diff --git a/gulpfile.js b/gulpfile.js index 25bde9148fc7..4070af506d8c 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -287,7 +287,9 @@ function getAllowedWarningsForWebPack(buildConfig) { 'WARNING in ./node_modules/@jupyterlab/services/node_modules/ws/lib/validation.js', 'WARNING in ./node_modules/any-promise/register.js', 'WARNING in ./node_modules/log4js/lib/appenders/index.js', - 'WARNING in ./node_modules/log4js/lib/clustering.js' + 'WARNING in ./node_modules/log4js/lib/clustering.js', + 'WARNING in ./node_modules/diagnostic-channel-publishers/dist/src/azure-coretracing.pub.js', + 'WARNING in ./node_modules/applicationinsights/out/AutoCollection/NativePerformance.js' ]; case 'extension': return [ @@ -300,10 +302,16 @@ function getAllowedWarningsForWebPack(buildConfig) { 'remove-files-plugin@1.4.0:', 'WARNING in ./node_modules/@jupyterlab/services/node_modules/ws/lib/buffer-util.js', 'WARNING in ./node_modules/@jupyterlab/services/node_modules/ws/lib/validation.js', - 'WARNING in ./node_modules/@jupyterlab/services/node_modules/ws/lib/Validation.js' + 'WARNING in ./node_modules/@jupyterlab/services/node_modules/ws/lib/Validation.js', + 'WARNING in ./node_modules/diagnostic-channel-publishers/dist/src/azure-coretracing.pub.js', + 'WARNING in ./node_modules/applicationinsights/out/AutoCollection/NativePerformance.js' ]; case 'debugAdapter': - return ['WARNING in ./node_modules/vscode-uri/lib/index.js']; + return [ + 'WARNING in ./node_modules/vscode-uri/lib/index.js', + 'WARNING in ./node_modules/diagnostic-channel-publishers/dist/src/azure-coretracing.pub.js', + 'WARNING in ./node_modules/applicationinsights/out/AutoCollection/NativePerformance.js' + ]; default: throw new Error('Unknown WebPack Configuration'); } diff --git a/news/3 Code Health/11597.md b/news/3 Code Health/11597.md new file mode 100644 index 000000000000..4d5ba9f74640 --- /dev/null +++ b/news/3 Code Health/11597.md @@ -0,0 +1 @@ +Update telemetry on errors and exceptions to use [vscode-extension-telemetry](https://www.npmjs.com/package/vscode-extension-telemetry). diff --git a/package-lock.json b/package-lock.json index 27b70c2e1094..a2a8c36cb785 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4662,13 +4662,14 @@ } }, "applicationinsights": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/applicationinsights/-/applicationinsights-1.0.6.tgz", - "integrity": "sha512-VQT3kBpJVPw5fCO5n+WUeSx0VHjxFtD7znYbILBlVgOS9/cMDuGFmV2Br3ObzFyZUDGNbEfW36fD1y2/vAiCKw==", + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/applicationinsights/-/applicationinsights-1.7.4.tgz", + "integrity": "sha512-XFLsNlcanpjFhHNvVWEfcm6hr7lu9znnb6Le1Lk5RE03YUV9X2B2n2MfM4kJZRrUdV+C0hdHxvWyv+vWoLfY7A==", "requires": { + "cls-hooked": "^4.2.2", + "continuation-local-storage": "^3.2.1", "diagnostic-channel": "0.2.0", - "diagnostic-channel-publishers": "0.2.1", - "zone.js": "0.7.6" + "diagnostic-channel-publishers": "^0.3.3" } }, "aproba": { @@ -5235,11 +5236,28 @@ "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", "dev": true }, + "async-hook-jl": { + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/async-hook-jl/-/async-hook-jl-1.7.6.tgz", + "integrity": "sha512-gFaHkFfSxTjvoxDMYqDuGHlcRyUuamF8s+ZTtJdDzqjws4mCt7v0vuV79/E2Wr2/riMQgtG4/yUtXWs1gZ7JMg==", + "requires": { + "stack-chain": "^1.3.7" + } + }, "async-limiter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" }, + "async-listener": { + "version": "0.6.10", + "resolved": "https://registry.npmjs.org/async-listener/-/async-listener-0.6.10.tgz", + "integrity": "sha512-gpuo6xOyF4D5DE5WvyqZdPA3NGhiT6Qf07l7DCB0wwDEsLvDIbCr6j9S5aj5Ch96dLace5tXVzWBZkxU/c5ohw==", + "requires": { + "semver": "^5.3.0", + "shimmer": "^1.1.0" + } + }, "async-settle": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz", @@ -6812,6 +6830,16 @@ } } }, + "cls-hooked": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/cls-hooked/-/cls-hooked-4.2.2.tgz", + "integrity": "sha512-J4Xj5f5wq/4jAvcdgoGsL3G103BtWpZrMo8NEinRltN+xpTZdI+M38pyQqhuFU/P792xkMFvnKSf+Lm81U1bxw==", + "requires": { + "async-hook-jl": "^1.7.6", + "emitter-listener": "^1.0.1", + "semver": "^5.4.1" + } + }, "clsx": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.0.4.tgz", @@ -7153,6 +7181,15 @@ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" }, + "continuation-local-storage": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/continuation-local-storage/-/continuation-local-storage-3.2.1.tgz", + "integrity": "sha512-jx44cconVqkCEEyLSKWwkvUXwO561jXMa3LPjTPsm5QR22PA0/mhe33FT4Xb5y74JDvt/Cq+5lm8S8rskLv9ZA==", + "requires": { + "async-listener": "^0.6.0", + "emitter-listener": "^1.1.1" + } + }, "convert-source-map": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", @@ -8695,9 +8732,9 @@ } }, "diagnostic-channel-publishers": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.2.1.tgz", - "integrity": "sha1-ji1geottef6IC1SLxYzGvrKIxPM=" + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.3.4.tgz", + "integrity": "sha512-SZ1zMfFiEabf4Qx0Og9V1gMsRoqz3O+5ENkVcNOfI+SMJ3QhQsdEoKX99r0zvreagXot2parPxmrwwUM/ja8ug==" }, "diagnostics": { "version": "1.1.1", @@ -9064,6 +9101,14 @@ "minimalistic-crypto-utils": "^1.0.0" } }, + "emitter-listener": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/emitter-listener/-/emitter-listener-1.1.2.tgz", + "integrity": "sha512-Bt1sBAGFHY9DKY+4/2cV6izcKJUf5T7/gkdmkxzX/qv9CcGH8xSwVRW5mtX03SWJtRTWSOpzCuWN9rBFYZepZQ==", + "requires": { + "shimmer": "^1.2.0" + } + }, "emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", @@ -20687,6 +20732,11 @@ } } }, + "shimmer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", + "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==" + }, "shortid": { "version": "2.2.14", "resolved": "https://registry.npmjs.org/shortid/-/shortid-2.2.14.tgz", @@ -21279,6 +21329,11 @@ "figgy-pudding": "^3.5.1" } }, + "stack-chain": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/stack-chain/-/stack-chain-1.3.7.tgz", + "integrity": "sha1-0ZLJ/06moiyUxN1FkXHj8AzqEoU=" + }, "stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", @@ -24601,11 +24656,11 @@ "integrity": "sha512-+OMm11R1bGYbpIJ5eQIkwoDGFF4GvBz3Ztl6/VM+/RNNb2Gjk2c0Ku+oMmfhlTmTlPCpgHBsH4JqVCbUYhu5bA==" }, "vscode-extension-telemetry": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.0.tgz", - "integrity": "sha512-WVCnP+uLxlqB6UD98yQNV47mR5Rf79LFxpuZhSPhEf0Sb4tPZed3a63n003/dchhOwyCTCBuNN4n8XKJkLEI1Q==", + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.4.tgz", + "integrity": "sha512-9U2pUZ/YwZBfA8CkBrHwMxjnq9Ab+ng8daJWJzEQ6CAxlZyRhmck23bx2lqqpEwGWJCiuceQy4k0Me6llEB4zw==", "requires": { - "applicationinsights": "1.0.6" + "applicationinsights": "1.7.4" } }, "vscode-jsonrpc": { @@ -25719,11 +25774,6 @@ } } } - }, - "zone.js": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.7.6.tgz", - "integrity": "sha1-+7w50+AmHQmG8boGMG6zrrDSIAk=" } } } diff --git a/package.json b/package.json index caf24deab28c..8b1434ac40e7 100644 --- a/package.json +++ b/package.json @@ -2988,7 +2988,7 @@ "untildify": "^3.0.2", "vscode-debugadapter": "^1.28.0", "vscode-debugprotocol": "^1.28.0", - "vscode-extension-telemetry": "0.1.0", + "vscode-extension-telemetry": "0.1.4", "vscode-jsonrpc": "^5.0.1", "vscode-languageclient": "^6.2.0-next.2", "vscode-languageserver": "^6.2.0-next.2", diff --git a/src/client/activation/languageClientMiddleware.ts b/src/client/activation/languageClientMiddleware.ts index 287ce9c47ef8..07418180c674 100644 --- a/src/client/activation/languageClientMiddleware.ts +++ b/src/client/activation/languageClientMiddleware.ts @@ -444,9 +444,12 @@ function captureTelemetryForLSPMethod(method: string, debounceMilliseconds: numb this.lastCaptured.set(method, now); this.eventCount += 1; + // Replace all slashes in the method name so it doesn't get scrubbed by vscode-extension-telemetry. + const formattedMethod = method.replace(/\//g, '.'); + const properties = { lsVersion: this.serverVersion || 'unknown', - method: method + method: formattedMethod }; const stopWatch = new StopWatch(); diff --git a/src/client/activation/languageServer/languageServerProxy.ts b/src/client/activation/languageServer/languageServerProxy.ts index 29d2e318eb96..ce69f971abc7 100644 --- a/src/client/activation/languageServer/languageServerProxy.ts +++ b/src/client/activation/languageServer/languageServerProxy.ts @@ -73,7 +73,12 @@ export class DotNetLanguageServerProxy implements ILanguageServerProxy { if (settings.downloadLanguageServer) { this.languageClient.onTelemetry((telemetryEvent) => { const eventName = telemetryEvent.EventName || EventName.PYTHON_LANGUAGE_SERVER_TELEMETRY; - sendTelemetryEvent(eventName, telemetryEvent.Measurements, telemetryEvent.Properties); + const formattedProperties = { + ...telemetryEvent.Properties, + // Replace all slashes in the method name so it doesn't get scrubbed by vscode-extension-telemetry. + method: telemetryEvent.Properties.method?.replace(/\//g, '.') + }; + sendTelemetryEvent(eventName, telemetryEvent.Measurements, formattedProperties); }); } await this.registerTestServices(); diff --git a/src/client/activation/node/languageServerProxy.ts b/src/client/activation/node/languageServerProxy.ts index 17f25cbd97e7..ef1ecbb08310 100644 --- a/src/client/activation/node/languageServerProxy.ts +++ b/src/client/activation/node/languageServerProxy.ts @@ -99,7 +99,12 @@ export class NodeLanguageServerProxy implements ILanguageServerProxy { if (settings.downloadLanguageServer) { this.languageClient.onTelemetry((telemetryEvent) => { const eventName = telemetryEvent.EventName || EventName.LANGUAGE_SERVER_TELEMETRY; - sendTelemetryEvent(eventName, telemetryEvent.Measurements, telemetryEvent.Properties); + const formattedProperties = { + ...telemetryEvent.Properties, + // Replace all slashes in the method name so it doesn't get scrubbed by vscode-extension-telemetry. + method: telemetryEvent.Properties.method?.replace(/\//g, '.') + }; + sendTelemetryEvent(eventName, telemetryEvent.Measurements, formattedProperties); }); } await this.registerTestServices(); diff --git a/src/client/telemetry/index.ts b/src/client/telemetry/index.ts index 4583f26e9046..e1468985c57e 100644 --- a/src/client/telemetry/index.ts +++ b/src/client/telemetry/index.ts @@ -1,15 +1,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -// tslint:disable:no-reference no-any import-name no-any function-name -/// + import type { JSONObject } from '@phosphor/coreutils'; -import { basename as pathBasename, sep as pathSep } from 'path'; import * as stackTrace from 'stack-trace'; -import TelemetryReporter from 'vscode-extension-telemetry'; +// tslint:disable-next-line: import-name +import TelemetryReporter from 'vscode-extension-telemetry/lib/telemetryReporter'; import { DiagnosticCodes } from '../application/diagnostics/constants'; import { IWorkspaceService } from '../common/application/types'; -import { AppinsightsKey, EXTENSION_ROOT_DIR, isTestExecution, PVSC_EXTENSION_ID } from '../common/constants'; +import { AppinsightsKey, isTestExecution, PVSC_EXTENSION_ID } from '../common/constants'; import { traceError, traceInfo } from '../common/logger'; import { TerminalShellType } from '../common/terminal/types'; import { StopWatch } from '../common/utils/stopWatch'; @@ -28,10 +27,12 @@ import { TestProvider } from '../testing/common/types'; import { EventName, PlatformErrors } from './constants'; import { LinterTrigger, TestTool } from './types'; +// tslint:disable: no-any + /** * Checks whether telemetry is supported. * Its possible this function gets called within Debug Adapter, vscode isn't available in there. - * Withiin DA, there's a completely different way to send telemetry. + * Within DA, there's a completely different way to send telemetry. * @returns {boolean} */ function isTelemetrySupported(): boolean { @@ -63,14 +64,12 @@ function getTelemetryReporter() { const extensionId = PVSC_EXTENSION_ID; // tslint:disable-next-line:no-require-imports const extensions = (require('vscode') as typeof import('vscode')).extensions; - // tslint:disable-next-line:no-non-null-assertion const extension = extensions.getExtension(extensionId)!; - // tslint:disable-next-line:no-unsafe-any const extensionVersion = extension.packageJSON.version; // tslint:disable-next-line:no-require-imports const reporter = require('vscode-extension-telemetry').default as typeof TelemetryReporter; - return (telemetryReporter = new reporter(extensionId, extensionVersion, AppinsightsKey)); + return (telemetryReporter = new reporter(extensionId, extensionVersion, AppinsightsKey, true)); } export function clearTelemetryReporter() { @@ -88,45 +87,46 @@ export function sendTelemetryEvent

= {}; + let eventNameSent = eventName as string; - if (ex && (eventName as any) !== 'ERROR') { - // When sending `ERROR` telemetry event no need to send custom properties. + if (ex) { + // When sending telemetry events for exceptions no need to send custom properties. // Else we have to review all properties every time as part of GDPR. // Assume we have 10 events all with their own properties. // As we have errors for each event, those properties are treated as new data items. // Hence they need to be classified as part of the GDPR process, and thats unnecessary and onerous. - const props: Record = {}; - props.stackTrace = getStackTrace(ex); - props.originalEventName = (eventName as any) as string; - reporter.sendTelemetryEvent('ERROR', props, measures); - } - const customProperties: Record = {}; - if (properties) { - // tslint:disable-next-line:prefer-type-cast no-any - const data = properties as any; - Object.getOwnPropertyNames(data).forEach((prop) => { - if (data[prop] === undefined || data[prop] === null) { - return; - } - try { - // If there are any errors in serializing one property, ignore that and move on. - // Else nothign will be sent. - // tslint:disable-next-line:prefer-type-cast no-any no-unsafe-any - (customProperties as any)[prop] = - typeof data[prop] === 'string' - ? data[prop] - : typeof data[prop] === 'object' - ? 'object' - : data[prop].toString(); - } catch (ex) { - traceError(`Failed to serialize ${prop} for ${eventName}`, ex); - } - }); + eventNameSent = 'ERROR'; + customProperties = { originalEventName: eventName as string, stackTrace: serializeStackTrace(ex) }; + reporter.sendTelemetryErrorEvent(eventNameSent, customProperties, measures, []); + } else { + if (properties) { + const data = properties as any; + Object.getOwnPropertyNames(data).forEach((prop) => { + if (data[prop] === undefined || data[prop] === null) { + return; + } + try { + // If there are any errors in serializing one property, ignore that and move on. + // Else nothing will be sent. + customProperties[prop] = + typeof data[prop] === 'string' + ? data[prop] + : typeof data[prop] === 'object' + ? 'object' + : data[prop].toString(); + } catch (ex) { + traceError(`Failed to serialize ${prop} for ${eventName}`, ex); + } + }); + } + + reporter.sendTelemetryEvent(eventNameSent, customProperties, measures); } - reporter.sendTelemetryEvent((eventName as any) as string, customProperties, measures); + if (process.env && process.env.VSC_PYTHON_LOG_TELEMETRY) { traceInfo( - `Telemetry Event : ${eventName} Measures: ${JSON.stringify(measures)} Props: ${JSON.stringify( + `Telemetry Event : ${eventNameSent} Measures: ${JSON.stringify(measures)} Props: ${JSON.stringify( customProperties )} ` ); @@ -246,32 +246,12 @@ export function sendTelemetryWhenDone

${filename.substring(EXTENSION_ROOT_DIR.length)}`; - } else { - // We don't really care about files outside our extension. - filename = `${pathSep}${pathBasename(filename)}`; - } - return filename; -} - -function sanitizeName(name: string): string { - if (name.indexOf('/') === -1 && name.indexOf('\\') === -1) { - return name; - } else { - return ''; - } -} - -function getStackTrace(ex: Error): string { - // We aren't showing the error message (ex.message) since it might - // contain PII. +function serializeStackTrace(ex: Error): string { + // We aren't showing the error message (ex.message) since it might contain PII. let trace = ''; for (const frame of stackTrace.parse(ex)) { - let filename = frame.getFileName(); + const filename = frame.getFileName(); if (filename) { - filename = sanitizeFilename(filename); const lineno = frame.getLineNumber(); const colno = frame.getColumnNumber(); trace += `\n\tat ${getCallsite(frame)} ${filename}:${lineno}:${colno}`; @@ -297,7 +277,7 @@ function getCallsite(frame: stackTrace.StackFrame) { parts.push(frame.getFunctionName()); } } - return parts.map(sanitizeName).join('.'); + return parts.join('.'); } // Map all events to their properties diff --git a/src/client/telemetry/vscode-extension-telemetry.d.ts b/src/client/telemetry/vscode-extension-telemetry.d.ts deleted file mode 100644 index a5d3d6294739..000000000000 --- a/src/client/telemetry/vscode-extension-telemetry.d.ts +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -declare module 'vscode-extension-telemetry' { - export default class TelemetryReporter { - /** - * Constructs a new telemetry reporter - * @param {string} extensionId All events will be prefixed with this event name - * @param {string} extensionVersion Extension version to be reported with each event - * @param {string} key The application insights key - */ - // tslint:disable-next-line:no-empty - constructor(extensionId: string, extensionVersion: string, key: string); - - /** - * Sends a telemetry event - * @param {string} eventName The event name - * @param {object} properties An associative array of strings - * @param {object} measures An associative array of numbers - */ - // tslint:disable-next-line:member-access - public sendTelemetryEvent( - eventName: string, - properties?: { - [key: string]: string; - }, - measures?: { - [key: string]: number; - // tslint:disable-next-line:no-empty - } - ): void; - } -} diff --git a/src/test/telemetry/index.unit.test.ts b/src/test/telemetry/index.unit.test.ts index aad20386af82..0bc9d757649e 100644 --- a/src/test/telemetry/index.unit.test.ts +++ b/src/test/telemetry/index.unit.test.ts @@ -23,16 +23,22 @@ suite('Telemetry', () => { public static eventName: string[] = []; public static properties: Record[] = []; public static measures: {}[] = []; + public static errorProps: string[] | undefined; public static clear() { Reporter.eventName = []; Reporter.properties = []; Reporter.measures = []; + Reporter.errorProps = undefined; } public sendTelemetryEvent(eventName: string, properties?: {}, measures?: {}) { Reporter.eventName.push(eventName); Reporter.properties.push(properties!); Reporter.measures.push(measures!); } + public sendTelemetryErrorEvent(eventName: string, properties?: {}, measures?: {}, errorProps?: string[]) { + this.sendTelemetryEvent(eventName, properties, measures); + Reporter.errorProps = errorProps; + } } setup(() => { @@ -94,7 +100,7 @@ suite('Telemetry', () => { expect(Reporter.measures).to.deep.equal([measures]); expect(Reporter.properties).to.deep.equal([properties]); }); - test('Send Telemetry', () => { + test('Send Telemetry with properties', () => { rewiremock.enable(); rewiremock('vscode-extension-telemetry').with({ default: Reporter }); @@ -122,31 +128,33 @@ suite('Telemetry', () => { originalEventName: eventName }; - expect(Reporter.eventName).to.deep.equal(['ERROR', eventName]); - expect(Reporter.measures).to.deep.equal([measures, measures]); + expect(Reporter.eventName).to.deep.equal(['ERROR']); + expect(Reporter.measures).to.deep.equal([measures]); expect(Reporter.properties[0].stackTrace).to.be.length.greaterThan(1); delete Reporter.properties[0].stackTrace; - expect(Reporter.properties).to.deep.equal([expectedErrorProperties, properties]); + expect(Reporter.properties).to.deep.equal([expectedErrorProperties]); + expect(Reporter.errorProps).to.deep.equal([]); }); - test('Send Error Telemetry', () => { + test('Send Error Telemetry with stack trace', () => { rewiremock.enable(); const error = new Error('Boo'); + const root = EXTENSION_ROOT_DIR.replace(/\\/g, '/'); error.stack = [ 'Error: Boo', - `at Context.test (${EXTENSION_ROOT_DIR}/src/test/telemetry/index.unit.test.ts:50:23)`, - `at callFn (${EXTENSION_ROOT_DIR}/node_modules/mocha/lib/runnable.js:372:21)`, - `at Test.Runnable.run (${EXTENSION_ROOT_DIR}/node_modules/mocha/lib/runnable.js:364:7)`, - `at Runner.runTest (${EXTENSION_ROOT_DIR}/node_modules/mocha/lib/runner.js:455:10)`, - `at ${EXTENSION_ROOT_DIR}/node_modules/mocha/lib/runner.js:573:12`, - `at next (${EXTENSION_ROOT_DIR}/node_modules/mocha/lib/runner.js:369:14)`, - `at ${EXTENSION_ROOT_DIR}/node_modules/mocha/lib/runner.js:379:7`, - `at next (${EXTENSION_ROOT_DIR}/node_modules/mocha/lib/runner.js:303:14)`, - `at ${EXTENSION_ROOT_DIR}/node_modules/mocha/lib/runner.js:342:7`, - `at done (${EXTENSION_ROOT_DIR}/node_modules/mocha/lib/runnable.js:319:5)`, - `at callFn (${EXTENSION_ROOT_DIR}/node_modules/mocha/lib/runnable.js:395:7)`, - `at Hook.Runnable.run (${EXTENSION_ROOT_DIR}/node_modules/mocha/lib/runnable.js:364:7)`, - `at next (${EXTENSION_ROOT_DIR}/node_modules/mocha/lib/runner.js:317:10)`, - `at Immediate. (${EXTENSION_ROOT_DIR}/node_modules/mocha/lib/runner.js:347:5)`, + `at Context.test (${root}/src/test/telemetry/index.unit.test.ts:50:23)`, + `at callFn (${root}/node_modules/mocha/lib/runnable.js:372:21)`, + `at Test.Runnable.run (${root}/node_modules/mocha/lib/runnable.js:364:7)`, + `at Runner.runTest (${root}/node_modules/mocha/lib/runner.js:455:10)`, + `at ${root}/node_modules/mocha/lib/runner.js:573:12`, + `at next (${root}/node_modules/mocha/lib/runner.js:369:14)`, + `at ${root}/node_modules/mocha/lib/runner.js:379:7`, + `at next (${root}/node_modules/mocha/lib/runner.js:303:14)`, + `at ${root}/node_modules/mocha/lib/runner.js:342:7`, + `at done (${root}/node_modules/mocha/lib/runnable.js:319:5)`, + `at callFn (${root}/node_modules/mocha/lib/runnable.js:395:7)`, + `at Hook.Runnable.run (${root}/node_modules/mocha/lib/runnable.js:364:7)`, + `at next (${root}/node_modules/mocha/lib/runner.js:317:10)`, + `at Immediate. (${root}/node_modules/mocha/lib/runner.js:347:5)`, 'at runCallback (timers.js:789:20)', 'at tryOnImmediate (timers.js:751:5)', 'at processImmediate [as _immediateCallback] (timers.js:722:5)' @@ -167,112 +175,30 @@ suite('Telemetry', () => { const stackTrace = Reporter.properties[0].stackTrace; delete Reporter.properties[0].stackTrace; - expect(Reporter.eventName).to.deep.equal(['ERROR', eventName]); - expect(Reporter.measures).to.deep.equal([measures, measures]); - expect(Reporter.properties).to.deep.equal([expectedErrorProperties, properties]); - expect(stackTrace).to.be.length.greaterThan(1); - - const expectedStack = [ - 'at Context.test /src/test/telemetry/index.unit.test.ts:50:23\n\tat callFn /node_modules/mocha/lib/runnable.js:372:21', - 'at Test.Runnable.run /node_modules/mocha/lib/runnable.js:364:7', - 'at Runner.runTest /node_modules/mocha/lib/runner.js:455:10', - 'at /node_modules/mocha/lib/runner.js:573:12', - 'at next /node_modules/mocha/lib/runner.js:369:14', - 'at /node_modules/mocha/lib/runner.js:379:7', - 'at next /node_modules/mocha/lib/runner.js:303:14', - 'at /node_modules/mocha/lib/runner.js:342:7', - 'at done /node_modules/mocha/lib/runnable.js:319:5', - 'at callFn /node_modules/mocha/lib/runnable.js:395:7', - 'at Hook.Runnable.run /node_modules/mocha/lib/runnable.js:364:7', - 'at next /node_modules/mocha/lib/runner.js:317:10', - 'at Immediate /node_modules/mocha/lib/runner.js:347:5', - 'at runCallback /timers.js:789:20', - 'at tryOnImmediate /timers.js:751:5', - 'at processImmediate [as _immediateCallback] /timers.js:722:5' - ].join('\n\t'); - - expect(stackTrace).to.be.equal(expectedStack); - }); - test('Ensure non extension file paths are stripped from stack trace', () => { - rewiremock.enable(); - const error = new Error('Boo'); - error.stack = [ - 'Error: Boo', - `at Context.test (${EXTENSION_ROOT_DIR}/src/test/telemetry/index.unit.test.ts:50:23)`, - 'at callFn (c:/one/two/user/node_modules/mocha/lib/runnable.js:372:21)', - 'at Test.Runnable.run (/usr/Paul/Homer/desktop/node_modules/mocha/lib/runnable.js:364:7)', - 'at Runner.runTest (\\wowwee/node_modules/mocha/lib/runner.js:455:10)', - `at Immediate. (${EXTENSION_ROOT_DIR}/node_modules/mocha/lib/runner.js:347:5)` - ].join('\n\t'); - rewiremock('vscode-extension-telemetry').with({ default: Reporter }); - - const eventName = 'Testing'; - const properties = { hello: 'world', foo: 'bar' }; - const measures = { start: 123, end: 987 }; - - // tslint:disable-next-line:no-any - sendTelemetryEvent(eventName as any, measures, properties as any, error); - - const expectedErrorProperties = { - originalEventName: eventName - }; - - const stackTrace = Reporter.properties[0].stackTrace; - delete Reporter.properties[0].stackTrace; - - expect(Reporter.eventName).to.deep.equal(['ERROR', eventName]); - expect(Reporter.measures).to.deep.equal([measures, measures]); - expect(Reporter.properties).to.deep.equal([expectedErrorProperties, properties]); - expect(stackTrace).to.be.length.greaterThan(1); - - const expectedStack = [ - 'at Context.test /src/test/telemetry/index.unit.test.ts:50:23', - 'at callFn /runnable.js:372:21', - 'at Test.Runnable.run /runnable.js:364:7', - 'at Runner.runTest /runner.js:455:10', - 'at Immediate /node_modules/mocha/lib/runner.js:347:5' - ].join('\n\t'); - - expect(stackTrace).to.be.equal(expectedStack); - }); - test('Ensure non function names containing file names (unlikely, but for sake of completeness) are stripped from stack trace', () => { - rewiremock.enable(); - const error = new Error('Boo'); - error.stack = [ - 'Error: Boo', - `at Context.test (${EXTENSION_ROOT_DIR}/src/test/telemetry/index.unit.test.ts:50:23)`, - 'at callFn (c:/one/two/user/node_modules/mocha/lib/runnable.js:372:21)', - 'at Test./usr/Paul/Homer/desktop/node_modules/mocha/lib/runnable.run (/usr/Paul/Homer/desktop/node_modules/mocha/lib/runnable.js:364:7)', - 'at Runner.runTest (\\wowwee/node_modules/mocha/lib/runner.js:455:10)', - `at Immediate. (${EXTENSION_ROOT_DIR}/node_modules/mocha/lib/runner.js:347:5)` - ].join('\n\t'); - rewiremock('vscode-extension-telemetry').with({ default: Reporter }); - - const eventName = 'Testing'; - const properties = { hello: 'world', foo: 'bar' }; - const measures = { start: 123, end: 987 }; - - // tslint:disable-next-line:no-any - sendTelemetryEvent(eventName as any, measures, properties as any, error); - - const expectedErrorProperties = { - originalEventName: eventName - }; - - const stackTrace = Reporter.properties[0].stackTrace; - delete Reporter.properties[0].stackTrace; - - expect(Reporter.eventName).to.deep.equal(['ERROR', eventName]); - expect(Reporter.measures).to.deep.equal([measures, measures]); - expect(Reporter.properties).to.deep.equal([expectedErrorProperties, properties]); + expect(Reporter.eventName).to.deep.equal(['ERROR']); + expect(Reporter.measures).to.deep.equal([measures]); + expect(Reporter.properties).to.deep.equal([expectedErrorProperties]); expect(stackTrace).to.be.length.greaterThan(1); + expect(Reporter.errorProps).to.deep.equal([]); const expectedStack = [ - 'at Context.test /src/test/telemetry/index.unit.test.ts:50:23', - 'at callFn /runnable.js:372:21', - 'at .run /runnable.js:364:7', - 'at Runner.runTest /runner.js:455:10', - 'at Immediate /node_modules/mocha/lib/runner.js:347:5' + `at Context.test ${root}/src/test/telemetry/index.unit.test.ts:50:23`, + `at callFn ${root}/node_modules/mocha/lib/runnable.js:372:21`, + `at Test.Runnable.run ${root}/node_modules/mocha/lib/runnable.js:364:7`, + `at Runner.runTest ${root}/node_modules/mocha/lib/runner.js:455:10`, + `at ${root}/node_modules/mocha/lib/runner.js:573:12`, + `at next ${root}/node_modules/mocha/lib/runner.js:369:14`, + `at ${root}/node_modules/mocha/lib/runner.js:379:7`, + `at next ${root}/node_modules/mocha/lib/runner.js:303:14`, + `at ${root}/node_modules/mocha/lib/runner.js:342:7`, + `at done ${root}/node_modules/mocha/lib/runnable.js:319:5`, + `at callFn ${root}/node_modules/mocha/lib/runnable.js:395:7`, + `at Hook.Runnable.run ${root}/node_modules/mocha/lib/runnable.js:364:7`, + `at next ${root}/node_modules/mocha/lib/runner.js:317:10`, + `at Immediate ${root}/node_modules/mocha/lib/runner.js:347:5`, + 'at runCallback timers.js:789:20', + 'at tryOnImmediate timers.js:751:5', + 'at processImmediate [as _immediateCallback] timers.js:722:5' ].join('\n\t'); expect(stackTrace).to.be.equal(expectedStack); diff --git a/src/test/testing/helper.ts b/src/test/testing/helper.ts index d973f28d07d6..3f9ffbf44c89 100644 --- a/src/test/testing/helper.ts +++ b/src/test/testing/helper.ts @@ -32,7 +32,7 @@ export function lookForTestFile(tests: Tests, testFile: string) { // Only "/" (forward slash) in the given filename is affected. // // This helps with readability in test code. It allows us to use -// literals for filenames and dirnames instead of oath.join(). +// literals for filenames and dirnames instead of path.join(). export function fixPath(filename: string): string { return filename.replace(/\//, sep); }