telemetryService.test.ts 34.8 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
J
Johannes Rieken 已提交
6 7
import { Emitter } from 'vs/base/common/event';
import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService';
J
Joao Moreno 已提交
8
import ErrorTelemetry from 'vs/platform/telemetry/browser/errorTelemetry';
9
import { NullAppender, ITelemetryAppender } from 'vs/platform/telemetry/common/telemetryUtils';
10
import * as Errors from 'vs/base/common/errors';
E
Erich Gamma 已提交
11
import * as sinon from 'sinon';
J
Johannes Rieken 已提交
12
import { getConfigurationValue } from 'vs/platform/configuration/common/configuration';
13
import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
E
Erich Gamma 已提交
14

15
class TestTelemetryAppender implements ITelemetryAppender {
E
Erich Gamma 已提交
16 17 18 19

	public events: any[];
	public isDisposed: boolean;

B
Benjamin Pasero 已提交
20
	constructor() {
E
Erich Gamma 已提交
21 22 23 24 25
		this.events = [];
		this.isDisposed = false;
	}

	public log(eventName: string, data?: any): void {
26
		this.events.push({ eventName, data });
E
Erich Gamma 已提交
27 28
	}

B
Benjamin Pasero 已提交
29
	public getEventsCount() {
E
Erich Gamma 已提交
30 31 32
		return this.events.length;
	}

33
	public flush(): Promise<any> {
E
Erich Gamma 已提交
34
		this.isDisposed = true;
35
		return Promise.resolve(null);
E
Erich Gamma 已提交
36 37 38 39
	}
}

class ErrorTestingSettings {
40 41 42 43 44 45 46 47 48 49
	public personalInfo: string;
	public importantInfo: string;
	public filePrefix: string;
	public dangerousPathWithoutImportantInfo: string;
	public dangerousPathWithImportantInfo: string;
	public missingModelPrefix: string;
	public missingModelMessage: string;
	public noSuchFilePrefix: string;
	public noSuchFileMessage: string;
	public stack: string[];
50
	public randomUserFile: string = 'a/path/that/doe_snt/con-tain/code/names.js';
51
	public anonymizedRandomUserFile: string = '<REDACTED: user-file-path>';
52 53
	public nodeModulePathToRetain: string = 'node_modules/path/that/shouldbe/retained/names.js:14:15854';
	public nodeModuleAsarPathToRetain: string = 'node_modules.asar/path/that/shouldbe/retained/names.js:14:12354';
E
Erich Gamma 已提交
54 55 56 57 58 59 60 61 62 63 64 65 66 67

	constructor() {
		this.personalInfo = 'DANGEROUS/PATH';
		this.importantInfo = 'important/information';
		this.filePrefix = 'file:///';
		this.dangerousPathWithImportantInfo = this.filePrefix + this.personalInfo + '/resources/app/' + this.importantInfo;
		this.dangerousPathWithoutImportantInfo = this.filePrefix + this.personalInfo;

		this.missingModelPrefix = 'Received model events for missing model ';
		this.missingModelMessage = this.missingModelPrefix + ' ' + this.dangerousPathWithoutImportantInfo;

		this.noSuchFilePrefix = 'ENOENT: no such file or directory';
		this.noSuchFileMessage = this.noSuchFilePrefix + ' \'' + this.personalInfo + '\'';

68 69 70 71 72 73 74 75 76 77
		this.stack = [`at e._modelEvents (${this.randomUserFile}:11:7309)`,
		`    at t.AllWorkers (${this.randomUserFile}:6:8844)`,
		`    at e.(anonymous function) [as _modelEvents] (${this.randomUserFile}:5:29552)`,
		`    at Function.<anonymous> (${this.randomUserFile}:6:8272)`,
		`    at e.dispatch (${this.randomUserFile}:5:26931)`,
		`    at e.request (/${this.nodeModuleAsarPathToRetain})`,
		`    at t._handleMessage (${this.nodeModuleAsarPathToRetain})`,
		`    at t._onmessage (/${this.nodeModulePathToRetain})`,
		`    at t.onmessage (${this.nodeModulePathToRetain})`,
			`    at DedicatedWorkerGlobalScope.self.onmessage`,
78 79 80 81
		this.dangerousPathWithImportantInfo,
		this.dangerousPathWithoutImportantInfo,
		this.missingModelMessage,
		this.noSuchFileMessage];
E
Erich Gamma 已提交
82 83 84 85 86
	}
}

suite('TelemetryService', () => {

87
	test('Disposing', sinon.test(function () {
B
Benjamin Pasero 已提交
88
		let testAppender = new TestTelemetryAppender();
89
		let service = new TelemetryService({ appender: testAppender }, undefined!);
E
Erich Gamma 已提交
90

91 92
		return service.publicLog('testPrivateEvent').then(() => {
			assert.equal(testAppender.getEventsCount(), 1);
E
Erich Gamma 已提交
93

94
			service.dispose();
95
			assert.equal(!testAppender.isDisposed, true);
96
		});
E
Erich Gamma 已提交
97 98 99
	}));

	// event reporting
100
	test('Simple event', sinon.test(function () {
B
Benjamin Pasero 已提交
101
		let testAppender = new TestTelemetryAppender();
102
		let service = new TelemetryService({ appender: testAppender }, undefined!);
E
Erich Gamma 已提交
103

104 105 106 107
		return service.publicLog('testEvent').then(_ => {
			assert.equal(testAppender.getEventsCount(), 1);
			assert.equal(testAppender.events[0].eventName, 'testEvent');
			assert.notEqual(testAppender.events[0].data, null);
E
Erich Gamma 已提交
108

109 110 111
			service.dispose();
		});
	}));
112

113 114
	test('Event with data', sinon.test(function () {
		let testAppender = new TestTelemetryAppender();
115
		let service = new TelemetryService({ appender: testAppender }, undefined!);
116 117 118 119 120 121 122 123 124

		return service.publicLog('testEvent', {
			'stringProp': 'property',
			'numberProp': 1,
			'booleanProp': true,
			'complexProp': {
				'value': 0
			}
		}).then(() => {
125 126 127
			assert.equal(testAppender.getEventsCount(), 1);
			assert.equal(testAppender.events[0].eventName, 'testEvent');
			assert.notEqual(testAppender.events[0].data, null);
128 129 130 131
			assert.equal(testAppender.events[0].data['stringProp'], 'property');
			assert.equal(testAppender.events[0].data['numberProp'], 1);
			assert.equal(testAppender.events[0].data['booleanProp'], true);
			assert.equal(testAppender.events[0].data['complexProp'].value, 0);
132 133 134

			service.dispose();
		});
135

E
Erich Gamma 已提交
136 137
	}));

138
	test('common properties added to *all* events, simple event', function () {
139
		let testAppender = new TestTelemetryAppender();
140
		let service = new TelemetryService({
141
			appender: testAppender,
142
			commonProperties: Promise.resolve({ foo: 'JA!', get bar() { return Math.random(); } })
143
		}, undefined!);
144

145
		return service.publicLog('testEvent').then(_ => {
146 147
			let [first] = testAppender.events;

148 149 150
			assert.equal(Object.keys(first.data).length, 2);
			assert.equal(typeof first.data['foo'], 'string');
			assert.equal(typeof first.data['bar'], 'number');
151 152 153 154 155

			service.dispose();
		});
	});

156
	test('common properties added to *all* events, event with data', function () {
B
Benjamin Pasero 已提交
157
		let testAppender = new TestTelemetryAppender();
158
		let service = new TelemetryService({
159
			appender: testAppender,
160
			commonProperties: Promise.resolve({ foo: 'JA!', get bar() { return Math.random(); } })
161
		}, undefined!);
E
Erich Gamma 已提交
162

163 164
		return service.publicLog('testEvent', { hightower: 'xl', price: 8000 }).then(_ => {
			let [first] = testAppender.events;
165

166 167 168 169 170
			assert.equal(Object.keys(first.data).length, 4);
			assert.equal(typeof first.data['foo'], 'string');
			assert.equal(typeof first.data['bar'], 'number');
			assert.equal(typeof first.data['hightower'], 'string');
			assert.equal(typeof first.data['price'], 'number');
171 172

			service.dispose();
E
Erich Gamma 已提交
173
		});
174
	});
E
Erich Gamma 已提交
175

176 177
	test('TelemetryInfo comes from properties', function () {
		let service = new TelemetryService({
178
			appender: NullAppender,
179
			commonProperties: Promise.resolve({
180 181 182 183
				sessionID: 'one',
				['common.instanceId']: 'two',
				['common.machineId']: 'three',
			})
184
		}, undefined!);
E
Erich Gamma 已提交
185

186 187 188 189
		return service.getTelemetryInfo().then(info => {
			assert.equal(info.sessionId, 'one');
			assert.equal(info.instanceId, 'two');
			assert.equal(info.machineId, 'three');
190 191

			service.dispose();
192 193 194 195
		});
	});

	test('enableTelemetry on by default', sinon.test(function () {
B
Benjamin Pasero 已提交
196
		let testAppender = new TestTelemetryAppender();
197
		let service = new TelemetryService({ appender: testAppender }, undefined!);
E
Erich Gamma 已提交
198

199 200 201
		return service.publicLog('testEvent').then(() => {
			assert.equal(testAppender.getEventsCount(), 1);
			assert.equal(testAppender.events[0].eventName, 'testEvent');
E
Erich Gamma 已提交
202

203 204
			service.dispose();
		});
E
Erich Gamma 已提交
205 206
	}));

207 208
	class JoinableTelemetryService extends TelemetryService {

J
Johannes Rieken 已提交
209
		private readonly promises: Promise<void>[] = [];
210 211 212 213 214

		join(): Promise<any> {
			return Promise.all(this.promises);
		}

J
Johannes Rieken 已提交
215
		publicLog(eventName: string, data?: ITelemetryData, anonymizeFilePaths?: boolean): Promise<void> {
216 217 218 219 220 221 222
			let p = super.publicLog(eventName, data, anonymizeFilePaths);
			this.promises.push(p);
			return p;
		}
	}

	test('Error events', sinon.test(async function (this: any) {
E
Erich Gamma 已提交
223

B
Benjamin Pasero 已提交
224 225
		let origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler();
		Errors.setUnexpectedErrorHandler(() => { });
E
Erich Gamma 已提交
226 227

		try {
B
Benjamin Pasero 已提交
228
			let testAppender = new TestTelemetryAppender();
229
			let service = new JoinableTelemetryService({ appender: testAppender }, undefined!);
J
Joao Moreno 已提交
230
			const errorTelemetry = new ErrorTelemetry(service);
E
Erich Gamma 已提交
231 232


B
Benjamin Pasero 已提交
233
			let e: any = new Error('This is a test.');
E
Erich Gamma 已提交
234
			// for Phantom
B
Benjamin Pasero 已提交
235
			if (!e.stack) {
E
Erich Gamma 已提交
236 237 238 239
				e.stack = 'blah';
			}

			Errors.onUnexpectedError(e);
J
Joao Moreno 已提交
240
			this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT);
241 242
			await service.join();

E
Erich Gamma 已提交
243 244
			assert.equal(testAppender.getEventsCount(), 1);
			assert.equal(testAppender.events[0].eventName, 'UnhandledError');
245
			assert.equal(testAppender.events[0].data.msg, 'This is a test.');
E
Erich Gamma 已提交
246

J
Joao Moreno 已提交
247
			errorTelemetry.dispose();
E
Erich Gamma 已提交
248 249 250 251 252 253
			service.dispose();
		} finally {
			Errors.setUnexpectedErrorHandler(origErrorHandler);
		}
	}));

B
Benjamin Pasero 已提交
254 255 256 257 258 259
	// 	test('Unhandled Promise Error events', sinon.test(function() {
	//
	// 		let origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler();
	// 		Errors.setUnexpectedErrorHandler(() => {});
	//
	// 		try {
260
	// 			let service = new MainTelemetryService();
B
Benjamin Pasero 已提交
261 262 263
	// 			let testAppender = new TestTelemetryAppender();
	// 			service.addTelemetryAppender(testAppender);
	//
264
	// 			winjs.Promise.wrapError(new Error('This should not get logged'));
B
Benjamin Pasero 已提交
265 266 267 268 269 270
	// 			winjs.TPromise.as(true).then(() => {
	// 				throw new Error('This should get logged');
	// 			});
	// 			// prevent console output from failing the test
	// 			this.stub(console, 'log');
	// 			// allow for the promise to finish
J
Joao Moreno 已提交
271
	// 			this.clock.tick(MainErrorTelemetry.ERROR_FLUSH_TIMEOUT);
B
Benjamin Pasero 已提交
272 273 274
	//
	// 			assert.equal(testAppender.getEventsCount(), 1);
	// 			assert.equal(testAppender.events[0].eventName, 'UnhandledError');
275
	// 			assert.equal(testAppender.events[0].data.msg,  'This should get logged');
B
Benjamin Pasero 已提交
276 277 278 279 280 281
	//
	// 			service.dispose();
	// 		} finally {
	// 			Errors.setUnexpectedErrorHandler(origErrorHandler);
	// 		}
	// 	}));
E
Erich Gamma 已提交
282

283
	test('Handle global errors', sinon.test(async function (this: any) {
S
Sandeep Somavarapu 已提交
284 285
		let errorStub = sinon.stub();
		window.onerror = errorStub;
E
Erich Gamma 已提交
286

B
Benjamin Pasero 已提交
287
		let testAppender = new TestTelemetryAppender();
288
		let service = new JoinableTelemetryService({ appender: testAppender }, undefined!);
J
Joao Moreno 已提交
289
		const errorTelemetry = new ErrorTelemetry(service);
E
Erich Gamma 已提交
290

B
Benjamin Pasero 已提交
291
		let testError = new Error('test');
E
Erich Gamma 已提交
292
		(<any>window.onerror)('Error Message', 'file.js', 2, 42, testError);
J
Joao Moreno 已提交
293
		this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT);
294
		await service.join();
E
Erich Gamma 已提交
295

A
Alex Dima 已提交
296
		assert.equal(errorStub.alwaysCalledWithExactly('Error Message', 'file.js', 2, 42, testError), true);
E
Erich Gamma 已提交
297 298 299 300
		assert.equal(errorStub.callCount, 1);

		assert.equal(testAppender.getEventsCount(), 1);
		assert.equal(testAppender.events[0].eventName, 'UnhandledError');
301 302
		assert.equal(testAppender.events[0].data.msg, 'Error Message');
		assert.equal(testAppender.events[0].data.file, 'file.js');
E
Erich Gamma 已提交
303 304
		assert.equal(testAppender.events[0].data.line, 2);
		assert.equal(testAppender.events[0].data.column, 42);
305
		assert.equal(testAppender.events[0].data.uncaught_error_msg, 'test');
E
Erich Gamma 已提交
306

J
Joao Moreno 已提交
307
		errorTelemetry.dispose();
E
Erich Gamma 已提交
308 309 310
		service.dispose();
	}));

311
	test('Error Telemetry removes PII from filename with spaces', sinon.test(async function (this: any) {
312 313 314 315
		let errorStub = sinon.stub();
		window.onerror = errorStub;
		let settings = new ErrorTestingSettings();
		let testAppender = new TestTelemetryAppender();
316
		let service = new JoinableTelemetryService({ appender: testAppender }, undefined!);
317 318 319 320 321 322 323
		const errorTelemetry = new ErrorTelemetry(service);

		let personInfoWithSpaces = settings.personalInfo.slice(0, 2) + ' ' + settings.personalInfo.slice(2);
		let dangerousFilenameError: any = new Error('dangerousFilename');
		dangerousFilenameError.stack = settings.stack;
		(<any>window.onerror)('dangerousFilename', settings.dangerousPathWithImportantInfo.replace(settings.personalInfo, personInfoWithSpaces) + '/test.js', 2, 42, dangerousFilenameError);
		this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT);
324
		await service.join();
325 326

		assert.equal(errorStub.callCount, 1);
327 328
		assert.equal(testAppender.events[0].data.file.indexOf(settings.dangerousPathWithImportantInfo.replace(settings.personalInfo, personInfoWithSpaces)), -1);
		assert.equal(testAppender.events[0].data.file, settings.importantInfo + '/test.js');
329 330 331 332 333

		errorTelemetry.dispose();
		service.dispose();
	}));

334
	test('Uncaught Error Telemetry removes PII from filename', sinon.test(function (this: any) {
335
		let clock = this.clock;
S
Sandeep Somavarapu 已提交
336 337
		let errorStub = sinon.stub();
		window.onerror = errorStub;
B
Benjamin Pasero 已提交
338 339
		let settings = new ErrorTestingSettings();
		let testAppender = new TestTelemetryAppender();
340
		let service = new JoinableTelemetryService({ appender: testAppender }, undefined!);
J
Joao Moreno 已提交
341
		const errorTelemetry = new ErrorTelemetry(service);
E
Erich Gamma 已提交
342

B
Benjamin Pasero 已提交
343
		let dangerousFilenameError: any = new Error('dangerousFilename');
E
Erich Gamma 已提交
344
		dangerousFilenameError.stack = settings.stack;
B
Benjamin Pasero 已提交
345
		(<any>window.onerror)('dangerousFilename', settings.dangerousPathWithImportantInfo + '/test.js', 2, 42, dangerousFilenameError);
346 347 348 349
		clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT);
		return service.join().then(() => {
			assert.equal(errorStub.callCount, 1);
			assert.equal(testAppender.events[0].data.file.indexOf(settings.dangerousPathWithImportantInfo), -1);
E
Erich Gamma 已提交
350

351 352 353 354 355 356 357 358 359
			dangerousFilenameError = new Error('dangerousFilename');
			dangerousFilenameError.stack = settings.stack;
			(<any>window.onerror)('dangerousFilename', settings.dangerousPathWithImportantInfo + '/test.js', 2, 42, dangerousFilenameError);
			clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT);
			return service.join();
		}).then(() => {
			assert.equal(errorStub.callCount, 2);
			assert.equal(testAppender.events[0].data.file.indexOf(settings.dangerousPathWithImportantInfo), -1);
			assert.equal(testAppender.events[0].data.file, settings.importantInfo + '/test.js');
E
Erich Gamma 已提交
360

361 362 363
			errorTelemetry.dispose();
			service.dispose();
		});
E
Erich Gamma 已提交
364 365
	}));

366
	test('Unexpected Error Telemetry removes PII', sinon.test(async function (this: any) {
B
Benjamin Pasero 已提交
367 368
		let origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler();
		Errors.setUnexpectedErrorHandler(() => { });
E
Erich Gamma 已提交
369
		try {
B
Benjamin Pasero 已提交
370 371
			let settings = new ErrorTestingSettings();
			let testAppender = new TestTelemetryAppender();
372
			let service = new JoinableTelemetryService({ appender: testAppender }, undefined!);
J
Joao Moreno 已提交
373
			const errorTelemetry = new ErrorTelemetry(service);
E
Erich Gamma 已提交
374

B
Benjamin Pasero 已提交
375
			let dangerousPathWithoutImportantInfoError: any = new Error(settings.dangerousPathWithoutImportantInfo);
E
Erich Gamma 已提交
376 377
			dangerousPathWithoutImportantInfoError.stack = settings.stack;
			Errors.onUnexpectedError(dangerousPathWithoutImportantInfoError);
J
Joao Moreno 已提交
378
			this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT);
379
			await service.join();
E
Erich Gamma 已提交
380

381 382
			assert.equal(testAppender.events[0].data.msg.indexOf(settings.personalInfo), -1);
			assert.equal(testAppender.events[0].data.msg.indexOf(settings.filePrefix), -1);
E
Erich Gamma 已提交
383

384 385 386 387
			assert.equal(testAppender.events[0].data.callstack.indexOf(settings.personalInfo), -1);
			assert.equal(testAppender.events[0].data.callstack.indexOf(settings.filePrefix), -1);
			assert.notEqual(testAppender.events[0].data.callstack.indexOf(settings.stack[4].replace(settings.randomUserFile, settings.anonymizedRandomUserFile)), -1);
			assert.equal(testAppender.events[0].data.callstack.split('\n').length, settings.stack.length);
E
Erich Gamma 已提交
388

J
Joao Moreno 已提交
389
			errorTelemetry.dispose();
E
Erich Gamma 已提交
390 391 392 393 394 395 396
			service.dispose();
		}
		finally {
			Errors.setUnexpectedErrorHandler(origErrorHandler);
		}
	}));

397
	test('Uncaught Error Telemetry removes PII', sinon.test(async function (this: any) {
S
Sandeep Somavarapu 已提交
398 399
		let errorStub = sinon.stub();
		window.onerror = errorStub;
B
Benjamin Pasero 已提交
400 401
		let settings = new ErrorTestingSettings();
		let testAppender = new TestTelemetryAppender();
402
		let service = new JoinableTelemetryService({ appender: testAppender }, undefined!);
J
Joao Moreno 已提交
403
		const errorTelemetry = new ErrorTelemetry(service);
E
Erich Gamma 已提交
404

B
Benjamin Pasero 已提交
405
		let dangerousPathWithoutImportantInfoError: any = new Error('dangerousPathWithoutImportantInfo');
E
Erich Gamma 已提交
406 407
		dangerousPathWithoutImportantInfoError.stack = settings.stack;
		(<any>window.onerror)(settings.dangerousPathWithoutImportantInfo, 'test.js', 2, 42, dangerousPathWithoutImportantInfoError);
J
Joao Moreno 已提交
408
		this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT);
409
		await service.join();
E
Erich Gamma 已提交
410 411 412

		assert.equal(errorStub.callCount, 1);
		// Test that no file information remains, esp. personal info
413 414 415 416 417 418
		assert.equal(testAppender.events[0].data.msg.indexOf(settings.personalInfo), -1);
		assert.equal(testAppender.events[0].data.msg.indexOf(settings.filePrefix), -1);
		assert.equal(testAppender.events[0].data.callstack.indexOf(settings.personalInfo), -1);
		assert.equal(testAppender.events[0].data.callstack.indexOf(settings.filePrefix), -1);
		assert.notEqual(testAppender.events[0].data.callstack.indexOf(settings.stack[4].replace(settings.randomUserFile, settings.anonymizedRandomUserFile)), -1);
		assert.equal(testAppender.events[0].data.callstack.split('\n').length, settings.stack.length);
E
Erich Gamma 已提交
419

J
Joao Moreno 已提交
420
		errorTelemetry.dispose();
E
Erich Gamma 已提交
421 422 423
		service.dispose();
	}));

424
	test('Unexpected Error Telemetry removes PII but preserves Code file path', sinon.test(async function (this: any) {
E
Erich Gamma 已提交
425

B
Benjamin Pasero 已提交
426 427
		let origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler();
		Errors.setUnexpectedErrorHandler(() => { });
E
Erich Gamma 已提交
428 429

		try {
B
Benjamin Pasero 已提交
430 431
			let settings = new ErrorTestingSettings();
			let testAppender = new TestTelemetryAppender();
432
			let service = new JoinableTelemetryService({ appender: testAppender }, undefined!);
J
Joao Moreno 已提交
433
			const errorTelemetry = new ErrorTelemetry(service);
E
Erich Gamma 已提交
434

B
Benjamin Pasero 已提交
435
			let dangerousPathWithImportantInfoError: any = new Error(settings.dangerousPathWithImportantInfo);
E
Erich Gamma 已提交
436 437 438 439
			dangerousPathWithImportantInfoError.stack = settings.stack;

			// Test that important information remains but personal info does not
			Errors.onUnexpectedError(dangerousPathWithImportantInfoError);
J
Joao Moreno 已提交
440
			this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT);
441
			await service.join();
E
Erich Gamma 已提交
442

443 444 445 446 447 448 449 450
			assert.notEqual(testAppender.events[0].data.msg.indexOf(settings.importantInfo), -1);
			assert.equal(testAppender.events[0].data.msg.indexOf(settings.personalInfo), -1);
			assert.equal(testAppender.events[0].data.msg.indexOf(settings.filePrefix), -1);
			assert.notEqual(testAppender.events[0].data.callstack.indexOf(settings.importantInfo), -1);
			assert.equal(testAppender.events[0].data.callstack.indexOf(settings.personalInfo), -1);
			assert.equal(testAppender.events[0].data.callstack.indexOf(settings.filePrefix), -1);
			assert.notEqual(testAppender.events[0].data.callstack.indexOf(settings.stack[4].replace(settings.randomUserFile, settings.anonymizedRandomUserFile)), -1);
			assert.equal(testAppender.events[0].data.callstack.split('\n').length, settings.stack.length);
E
Erich Gamma 已提交
451

J
Joao Moreno 已提交
452
			errorTelemetry.dispose();
E
Erich Gamma 已提交
453 454 455 456 457 458 459
			service.dispose();
		}
		finally {
			Errors.setUnexpectedErrorHandler(origErrorHandler);
		}
	}));

460
	test('Uncaught Error Telemetry removes PII but preserves Code file path', sinon.test(async function (this: any) {
S
Sandeep Somavarapu 已提交
461 462
		let errorStub = sinon.stub();
		window.onerror = errorStub;
B
Benjamin Pasero 已提交
463 464
		let settings = new ErrorTestingSettings();
		let testAppender = new TestTelemetryAppender();
465
		let service = new JoinableTelemetryService({ appender: testAppender }, undefined!);
J
Joao Moreno 已提交
466
		const errorTelemetry = new ErrorTelemetry(service);
E
Erich Gamma 已提交
467

B
Benjamin Pasero 已提交
468
		let dangerousPathWithImportantInfoError: any = new Error('dangerousPathWithImportantInfo');
E
Erich Gamma 已提交
469
		dangerousPathWithImportantInfoError.stack = settings.stack;
B
Benjamin Pasero 已提交
470
		(<any>window.onerror)(settings.dangerousPathWithImportantInfo, 'test.js', 2, 42, dangerousPathWithImportantInfoError);
471
		this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT);
472
		await service.join();
473 474 475

		assert.equal(errorStub.callCount, 1);
		// Test that important information remains but personal info does not
476 477 478 479 480 481 482 483
		assert.notEqual(testAppender.events[0].data.msg.indexOf(settings.importantInfo), -1);
		assert.equal(testAppender.events[0].data.msg.indexOf(settings.personalInfo), -1);
		assert.equal(testAppender.events[0].data.msg.indexOf(settings.filePrefix), -1);
		assert.notEqual(testAppender.events[0].data.callstack.indexOf(settings.importantInfo), -1);
		assert.equal(testAppender.events[0].data.callstack.indexOf(settings.personalInfo), -1);
		assert.equal(testAppender.events[0].data.callstack.indexOf(settings.filePrefix), -1);
		assert.notEqual(testAppender.events[0].data.callstack.indexOf(settings.stack[4].replace(settings.randomUserFile, settings.anonymizedRandomUserFile)), -1);
		assert.equal(testAppender.events[0].data.callstack.split('\n').length, settings.stack.length);
484 485 486 487 488

		errorTelemetry.dispose();
		service.dispose();
	}));

489
	test('Unexpected Error Telemetry removes PII but preserves Code file path with node modules', sinon.test(async function (this: any) {
490 491 492 493 494 495 496

		let origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler();
		Errors.setUnexpectedErrorHandler(() => { });

		try {
			let settings = new ErrorTestingSettings();
			let testAppender = new TestTelemetryAppender();
497
			let service = new JoinableTelemetryService({ appender: testAppender }, undefined!);
498 499 500 501 502 503 504 505
			const errorTelemetry = new ErrorTelemetry(service);

			let dangerousPathWithImportantInfoError: any = new Error(settings.dangerousPathWithImportantInfo);
			dangerousPathWithImportantInfoError.stack = settings.stack;


			Errors.onUnexpectedError(dangerousPathWithImportantInfoError);
			this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT);
506
			await service.join();
507

508 509 510 511
			assert.notEqual(testAppender.events[0].data.callstack.indexOf('(' + settings.nodeModuleAsarPathToRetain), -1);
			assert.notEqual(testAppender.events[0].data.callstack.indexOf('(' + settings.nodeModulePathToRetain), -1);
			assert.notEqual(testAppender.events[0].data.callstack.indexOf('(/' + settings.nodeModuleAsarPathToRetain), -1);
			assert.notEqual(testAppender.events[0].data.callstack.indexOf('(/' + settings.nodeModulePathToRetain), -1);
512 513 514 515 516 517 518 519 520

			errorTelemetry.dispose();
			service.dispose();
		}
		finally {
			Errors.setUnexpectedErrorHandler(origErrorHandler);
		}
	}));

521
	test('Uncaught Error Telemetry removes PII but preserves Code file path', sinon.test(async function (this: any) {
522 523 524 525
		let errorStub = sinon.stub();
		window.onerror = errorStub;
		let settings = new ErrorTestingSettings();
		let testAppender = new TestTelemetryAppender();
526
		let service = new JoinableTelemetryService({ appender: testAppender }, undefined!);
527 528 529 530 531 532
		const errorTelemetry = new ErrorTelemetry(service);

		let dangerousPathWithImportantInfoError: any = new Error('dangerousPathWithImportantInfo');
		dangerousPathWithImportantInfoError.stack = settings.stack;
		(<any>window.onerror)(settings.dangerousPathWithImportantInfo, 'test.js', 2, 42, dangerousPathWithImportantInfoError);
		this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT);
533
		await service.join();
534 535 536

		assert.equal(errorStub.callCount, 1);

537 538 539 540
		assert.notEqual(testAppender.events[0].data.callstack.indexOf('(' + settings.nodeModuleAsarPathToRetain), -1);
		assert.notEqual(testAppender.events[0].data.callstack.indexOf('(' + settings.nodeModulePathToRetain), -1);
		assert.notEqual(testAppender.events[0].data.callstack.indexOf('(/' + settings.nodeModuleAsarPathToRetain), -1);
		assert.notEqual(testAppender.events[0].data.callstack.indexOf('(/' + settings.nodeModulePathToRetain), -1);
541 542 543 544 545 546

		errorTelemetry.dispose();
		service.dispose();
	}));


547
	test('Unexpected Error Telemetry removes PII but preserves Code file path when PIIPath is configured', sinon.test(async function (this: any) {
548 549 550 551 552 553 554

		let origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler();
		Errors.setUnexpectedErrorHandler(() => { });

		try {
			let settings = new ErrorTestingSettings();
			let testAppender = new TestTelemetryAppender();
555
			let service = new JoinableTelemetryService({ appender: testAppender, piiPaths: [settings.personalInfo + '/resources/app/'] }, undefined!);
556 557 558 559 560 561 562 563
			const errorTelemetry = new ErrorTelemetry(service);

			let dangerousPathWithImportantInfoError: any = new Error(settings.dangerousPathWithImportantInfo);
			dangerousPathWithImportantInfoError.stack = settings.stack;

			// Test that important information remains but personal info does not
			Errors.onUnexpectedError(dangerousPathWithImportantInfoError);
			this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT);
564
			await service.join();
565

566 567 568 569 570 571 572 573
			assert.notEqual(testAppender.events[0].data.msg.indexOf(settings.importantInfo), -1);
			assert.equal(testAppender.events[0].data.msg.indexOf(settings.personalInfo), -1);
			assert.equal(testAppender.events[0].data.msg.indexOf(settings.filePrefix), -1);
			assert.notEqual(testAppender.events[0].data.callstack.indexOf(settings.importantInfo), -1);
			assert.equal(testAppender.events[0].data.callstack.indexOf(settings.personalInfo), -1);
			assert.equal(testAppender.events[0].data.callstack.indexOf(settings.filePrefix), -1);
			assert.notEqual(testAppender.events[0].data.callstack.indexOf(settings.stack[4].replace(settings.randomUserFile, settings.anonymizedRandomUserFile)), -1);
			assert.equal(testAppender.events[0].data.callstack.split('\n').length, settings.stack.length);
574 575 576 577 578 579 580 581 582

			errorTelemetry.dispose();
			service.dispose();
		}
		finally {
			Errors.setUnexpectedErrorHandler(origErrorHandler);
		}
	}));

583
	test('Uncaught Error Telemetry removes PII but preserves Code file path when PIIPath is configured', sinon.test(async function (this: any) {
S
Sandeep Somavarapu 已提交
584 585
		let errorStub = sinon.stub();
		window.onerror = errorStub;
586 587
		let settings = new ErrorTestingSettings();
		let testAppender = new TestTelemetryAppender();
588
		let service = new JoinableTelemetryService({ appender: testAppender, piiPaths: [settings.personalInfo + '/resources/app/'] }, undefined!);
589 590 591 592 593
		const errorTelemetry = new ErrorTelemetry(service);

		let dangerousPathWithImportantInfoError: any = new Error('dangerousPathWithImportantInfo');
		dangerousPathWithImportantInfoError.stack = settings.stack;
		(<any>window.onerror)(settings.dangerousPathWithImportantInfo, 'test.js', 2, 42, dangerousPathWithImportantInfoError);
J
Joao Moreno 已提交
594
		this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT);
595
		await service.join();
E
Erich Gamma 已提交
596 597 598

		assert.equal(errorStub.callCount, 1);
		// Test that important information remains but personal info does not
599 600 601 602 603 604 605 606
		assert.notEqual(testAppender.events[0].data.msg.indexOf(settings.importantInfo), -1);
		assert.equal(testAppender.events[0].data.msg.indexOf(settings.personalInfo), -1);
		assert.equal(testAppender.events[0].data.msg.indexOf(settings.filePrefix), -1);
		assert.notEqual(testAppender.events[0].data.callstack.indexOf(settings.importantInfo), -1);
		assert.equal(testAppender.events[0].data.callstack.indexOf(settings.personalInfo), -1);
		assert.equal(testAppender.events[0].data.callstack.indexOf(settings.filePrefix), -1);
		assert.notEqual(testAppender.events[0].data.callstack.indexOf(settings.stack[4].replace(settings.randomUserFile, settings.anonymizedRandomUserFile)), -1);
		assert.equal(testAppender.events[0].data.callstack.split('\n').length, settings.stack.length);
E
Erich Gamma 已提交
607

J
Joao Moreno 已提交
608
		errorTelemetry.dispose();
E
Erich Gamma 已提交
609 610 611
		service.dispose();
	}));

612
	test('Unexpected Error Telemetry removes PII but preserves Missing Model error message', sinon.test(async function (this: any) {
E
Erich Gamma 已提交
613

B
Benjamin Pasero 已提交
614 615
		let origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler();
		Errors.setUnexpectedErrorHandler(() => { });
E
Erich Gamma 已提交
616 617

		try {
B
Benjamin Pasero 已提交
618 619
			let settings = new ErrorTestingSettings();
			let testAppender = new TestTelemetryAppender();
620
			let service = new JoinableTelemetryService({ appender: testAppender }, undefined!);
J
Joao Moreno 已提交
621
			const errorTelemetry = new ErrorTelemetry(service);
E
Erich Gamma 已提交
622

B
Benjamin Pasero 已提交
623
			let missingModelError: any = new Error(settings.missingModelMessage);
E
Erich Gamma 已提交
624 625 626 627 628
			missingModelError.stack = settings.stack;

			// Test that no file information remains, but this particular
			// error message does (Received model events for missing model)
			Errors.onUnexpectedError(missingModelError);
J
Joao Moreno 已提交
629
			this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT);
630
			await service.join();
E
Erich Gamma 已提交
631

632 633 634 635 636 637 638 639
			assert.notEqual(testAppender.events[0].data.msg.indexOf(settings.missingModelPrefix), -1);
			assert.equal(testAppender.events[0].data.msg.indexOf(settings.personalInfo), -1);
			assert.equal(testAppender.events[0].data.msg.indexOf(settings.filePrefix), -1);
			assert.notEqual(testAppender.events[0].data.callstack.indexOf(settings.missingModelPrefix), -1);
			assert.equal(testAppender.events[0].data.callstack.indexOf(settings.personalInfo), -1);
			assert.equal(testAppender.events[0].data.callstack.indexOf(settings.filePrefix), -1);
			assert.notEqual(testAppender.events[0].data.callstack.indexOf(settings.stack[4].replace(settings.randomUserFile, settings.anonymizedRandomUserFile)), -1);
			assert.equal(testAppender.events[0].data.callstack.split('\n').length, settings.stack.length);
E
Erich Gamma 已提交
640

J
Joao Moreno 已提交
641
			errorTelemetry.dispose();
E
Erich Gamma 已提交
642 643 644 645 646 647
			service.dispose();
		} finally {
			Errors.setUnexpectedErrorHandler(origErrorHandler);
		}
	}));

648
	test('Uncaught Error Telemetry removes PII but preserves Missing Model error message', sinon.test(async function (this: any) {
S
Sandeep Somavarapu 已提交
649 650
		let errorStub = sinon.stub();
		window.onerror = errorStub;
B
Benjamin Pasero 已提交
651 652
		let settings = new ErrorTestingSettings();
		let testAppender = new TestTelemetryAppender();
653
		let service = new JoinableTelemetryService({ appender: testAppender }, undefined!);
J
Joao Moreno 已提交
654
		const errorTelemetry = new ErrorTelemetry(service);
E
Erich Gamma 已提交
655

B
Benjamin Pasero 已提交
656
		let missingModelError: any = new Error('missingModelMessage');
E
Erich Gamma 已提交
657 658
		missingModelError.stack = settings.stack;
		(<any>window.onerror)(settings.missingModelMessage, 'test.js', 2, 42, missingModelError);
J
Joao Moreno 已提交
659
		this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT);
660
		await service.join();
E
Erich Gamma 已提交
661 662 663 664

		assert.equal(errorStub.callCount, 1);
		// Test that no file information remains, but this particular
		// error message does (Received model events for missing model)
665 666 667 668 669 670 671 672
		assert.notEqual(testAppender.events[0].data.msg.indexOf(settings.missingModelPrefix), -1);
		assert.equal(testAppender.events[0].data.msg.indexOf(settings.personalInfo), -1);
		assert.equal(testAppender.events[0].data.msg.indexOf(settings.filePrefix), -1);
		assert.notEqual(testAppender.events[0].data.callstack.indexOf(settings.missingModelPrefix), -1);
		assert.equal(testAppender.events[0].data.callstack.indexOf(settings.personalInfo), -1);
		assert.equal(testAppender.events[0].data.callstack.indexOf(settings.filePrefix), -1);
		assert.notEqual(testAppender.events[0].data.callstack.indexOf(settings.stack[4].replace(settings.randomUserFile, settings.anonymizedRandomUserFile)), -1);
		assert.equal(testAppender.events[0].data.callstack.split('\n').length, settings.stack.length);
E
Erich Gamma 已提交
673

J
Joao Moreno 已提交
674
		errorTelemetry.dispose();
E
Erich Gamma 已提交
675 676 677
		service.dispose();
	}));

678
	test('Unexpected Error Telemetry removes PII but preserves No Such File error message', sinon.test(async function (this: any) {
E
Erich Gamma 已提交
679

B
Benjamin Pasero 已提交
680 681
		let origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler();
		Errors.setUnexpectedErrorHandler(() => { });
E
Erich Gamma 已提交
682 683

		try {
B
Benjamin Pasero 已提交
684 685
			let settings = new ErrorTestingSettings();
			let testAppender = new TestTelemetryAppender();
686
			let service = new JoinableTelemetryService({ appender: testAppender }, undefined!);
J
Joao Moreno 已提交
687
			const errorTelemetry = new ErrorTelemetry(service);
E
Erich Gamma 已提交
688

B
Benjamin Pasero 已提交
689
			let noSuchFileError: any = new Error(settings.noSuchFileMessage);
E
Erich Gamma 已提交
690 691 692 693 694
			noSuchFileError.stack = settings.stack;

			// Test that no file information remains, but this particular
			// error message does (ENOENT: no such file or directory)
			Errors.onUnexpectedError(noSuchFileError);
J
Joao Moreno 已提交
695
			this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT);
696
			await service.join();
E
Erich Gamma 已提交
697

698 699 700 701 702 703 704 705
			assert.notEqual(testAppender.events[0].data.msg.indexOf(settings.noSuchFilePrefix), -1);
			assert.equal(testAppender.events[0].data.msg.indexOf(settings.personalInfo), -1);
			assert.equal(testAppender.events[0].data.msg.indexOf(settings.filePrefix), -1);
			assert.notEqual(testAppender.events[0].data.callstack.indexOf(settings.noSuchFilePrefix), -1);
			assert.equal(testAppender.events[0].data.callstack.indexOf(settings.personalInfo), -1);
			assert.equal(testAppender.events[0].data.callstack.indexOf(settings.filePrefix), -1);
			assert.notEqual(testAppender.events[0].data.callstack.indexOf(settings.stack[4].replace(settings.randomUserFile, settings.anonymizedRandomUserFile)), -1);
			assert.equal(testAppender.events[0].data.callstack.split('\n').length, settings.stack.length);
E
Erich Gamma 已提交
706

J
Joao Moreno 已提交
707
			errorTelemetry.dispose();
E
Erich Gamma 已提交
708 709 710 711 712 713
			service.dispose();
		} finally {
			Errors.setUnexpectedErrorHandler(origErrorHandler);
		}
	}));

714
	test('Uncaught Error Telemetry removes PII but preserves No Such File error message', sinon.test(async function (this: any) {
B
Benjamin Pasero 已提交
715 716
		let origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler();
		Errors.setUnexpectedErrorHandler(() => { });
E
Erich Gamma 已提交
717

718
		try {
S
Sandeep Somavarapu 已提交
719 720
			let errorStub = sinon.stub();
			window.onerror = errorStub;
B
Benjamin Pasero 已提交
721 722
			let settings = new ErrorTestingSettings();
			let testAppender = new TestTelemetryAppender();
723
			let service = new JoinableTelemetryService({ appender: testAppender }, undefined!);
J
Joao Moreno 已提交
724
			const errorTelemetry = new ErrorTelemetry(service);
E
Erich Gamma 已提交
725

B
Benjamin Pasero 已提交
726
			let noSuchFileError: any = new Error('noSuchFileMessage');
727 728
			noSuchFileError.stack = settings.stack;
			(<any>window.onerror)(settings.noSuchFileMessage, 'test.js', 2, 42, noSuchFileError);
J
Joao Moreno 已提交
729
			this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT);
730
			await service.join();
E
Erich Gamma 已提交
731

732 733 734 735
			assert.equal(errorStub.callCount, 1);
			// Test that no file information remains, but this particular
			// error message does (ENOENT: no such file or directory)
			Errors.onUnexpectedError(noSuchFileError);
736 737 738 739 740 741 742 743
			assert.notEqual(testAppender.events[0].data.msg.indexOf(settings.noSuchFilePrefix), -1);
			assert.equal(testAppender.events[0].data.msg.indexOf(settings.personalInfo), -1);
			assert.equal(testAppender.events[0].data.msg.indexOf(settings.filePrefix), -1);
			assert.notEqual(testAppender.events[0].data.callstack.indexOf(settings.noSuchFilePrefix), -1);
			assert.equal(testAppender.events[0].data.callstack.indexOf(settings.personalInfo), -1);
			assert.equal(testAppender.events[0].data.callstack.indexOf(settings.filePrefix), -1);
			assert.notEqual(testAppender.events[0].data.callstack.indexOf(settings.stack[4].replace(settings.randomUserFile, settings.anonymizedRandomUserFile)), -1);
			assert.equal(testAppender.events[0].data.callstack.split('\n').length, settings.stack.length);
744

J
Joao Moreno 已提交
745
			errorTelemetry.dispose();
746 747 748 749
			service.dispose();
		} finally {
			Errors.setUnexpectedErrorHandler(origErrorHandler);
		}
E
Erich Gamma 已提交
750 751
	}));

R
Ramya Achutha Rao 已提交
752
	test('Telemetry Service sends events when enableTelemetry is on', sinon.test(function () {
753
		let testAppender = new TestTelemetryAppender();
754
		let service = new TelemetryService({ appender: testAppender }, undefined!);
755 756 757 758 759

		return service.publicLog('testEvent').then(() => {
			assert.equal(testAppender.getEventsCount(), 1);
			service.dispose();
		});
760
	}));
S
Sofian Hnaide 已提交
761

762 763 764 765 766
	test('Telemetry Service checks with config service', function () {

		let enableTelemetry = false;
		let emitter = new Emitter<any>();

S
Sofian Hnaide 已提交
767
		let testAppender = new TestTelemetryAppender();
768
		let service = new TelemetryService({
769
			appender: testAppender
770
		}, {
771
				_serviceBrand: undefined,
772 773 774 775
				getValue() {
					return {
						enableTelemetry: enableTelemetry
					} as any;
776
				},
S
Sandeep Somavarapu 已提交
777
				updateValue(): Promise<void> {
778
					return null!;
779
				},
780
				inspect(key: string) {
B
Benjamin Pasero 已提交
781
					return {
782 783 784
						value: getConfigurationValue(this.getValue(), key),
						default: getConfigurationValue(this.getValue(), key),
						user: getConfigurationValue(this.getValue(), key),
785 786
						workspace: null!,
						workspaceFolder: null!
B
Benjamin Pasero 已提交
787 788
					};
				},
789
				keys() { return { default: [], user: [], workspace: [], workspaceFolder: [] }; },
790
				onDidChangeConfiguration: emitter.event,
791
				reloadConfiguration(): Promise<void> { return null!; },
S
Sandeep Somavarapu 已提交
792
				getConfigurationData() { return null; }
J
Johannes Rieken 已提交
793
			});
S
Sofian Hnaide 已提交
794

795
		assert.equal(service.isOptedIn, false);
S
Sofian Hnaide 已提交
796

797 798 799 800 801 802 803
		enableTelemetry = true;
		emitter.fire({});
		assert.equal(service.isOptedIn, true);

		enableTelemetry = false;
		emitter.fire({});
		assert.equal(service.isOptedIn, false);
804 805

		service.dispose();
806
	});
807
});