make unit tests faster (#241847)

This commit is contained in:
João Moreno
2025-02-25 15:18:20 +01:00
committed by GitHub
parent 649a74e7d8
commit ae43cc72b6
6 changed files with 340 additions and 248 deletions

View File

@@ -12,6 +12,7 @@ import { IAsyncDataSource, ITreeNode } from '../../../../browser/ui/tree/tree.js
import { timeout } from '../../../../common/async.js';
import { Iterable } from '../../../../common/iterator.js';
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../common/utils.js';
import { runWithFakedTimers } from '../../../common/timeTravelScheduler.js';
interface Element {
id: string;
@@ -465,45 +466,47 @@ suite('AsyncDataTree', function () {
});
test('issue #80098 - first expand should call getChildren', async () => {
const container = document.createElement('div');
return runWithFakedTimers({ useFakeTimers: true }, async () => {
const container = document.createElement('div');
const calls: Function[] = [];
const dataSource = new class implements IAsyncDataSource<Element, Element> {
hasChildren(element: Element): boolean {
return !!element.children && element.children.length > 0;
}
getChildren(element: Element): Promise<Element[]> {
return new Promise(c => calls.push(() => c(element.children || [])));
}
};
const calls: Function[] = [];
const dataSource = new class implements IAsyncDataSource<Element, Element> {
hasChildren(element: Element): boolean {
return !!element.children && element.children.length > 0;
}
getChildren(element: Element): Promise<Element[]> {
return new Promise(c => calls.push(() => c(element.children || [])));
}
};
const model = new Model({
id: 'root',
children: [{
id: 'a', children: [{
id: 'aa'
const model = new Model({
id: 'root',
children: [{
id: 'a', children: [{
id: 'aa'
}]
}]
}]
});
const tree = store.add(new AsyncDataTree<Element, Element>('test', container, new VirtualDelegate(), [new Renderer()], dataSource, { identityProvider: new IdentityProvider() }));
tree.layout(200);
const pSetInput = tree.setInput(model.root);
calls.pop()!(); // resolve getChildren(root)
await pSetInput;
const pExpandA = tree.expand(model.get('a'));
assert.strictEqual(calls.length, 1, 'expand(a) should\'ve called getChildren(a)');
let race = await Promise.race([pExpandA.then(() => 'expand'), timeout(1).then(() => 'timeout')]);
assert.strictEqual(race, 'timeout', 'expand(a) should not be yet done');
calls.pop()!();
assert.strictEqual(calls.length, 0, 'no pending getChildren calls');
race = await Promise.race([pExpandA.then(() => 'expand'), timeout(1).then(() => 'timeout')]);
assert.strictEqual(race, 'expand', 'expand(a) should now be done');
});
const tree = store.add(new AsyncDataTree<Element, Element>('test', container, new VirtualDelegate(), [new Renderer()], dataSource, { identityProvider: new IdentityProvider() }));
tree.layout(200);
const pSetInput = tree.setInput(model.root);
calls.pop()!(); // resolve getChildren(root)
await pSetInput;
const pExpandA = tree.expand(model.get('a'));
assert.strictEqual(calls.length, 1, 'expand(a) should\'ve called getChildren(a)');
let race = await Promise.race([pExpandA.then(() => 'expand'), timeout(1).then(() => 'timeout')]);
assert.strictEqual(race, 'timeout', 'expand(a) should not be yet done');
calls.pop()!();
assert.strictEqual(calls.length, 0, 'no pending getChildren calls');
race = await Promise.race([pExpandA.then(() => 'expand'), timeout(1).then(() => 'timeout')]);
assert.strictEqual(race, 'expand', 'expand(a) should now be done');
});
test('issue #78388 - tree should react to hasChildren toggles', async () => {

View File

@@ -56,6 +56,7 @@ import { CodeActionTriggerSource } from '../../../../editor/contrib/codeAction/c
import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js';
import { IExtHostTelemetry } from '../../common/extHostTelemetry.js';
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';
import { runWithFakedTimers } from '../../../../base/test/common/timeTravelScheduler.js';
suite('ExtHostLanguageFeatures', function () {
@@ -233,62 +234,65 @@ suite('ExtHostLanguageFeatures', function () {
// --- code lens
test('CodeLens, evil provider', async () => {
return runWithFakedTimers({ useFakeTimers: true }, async () => {
disposables.add(extHost.registerCodeLensProvider(defaultExtension, defaultSelector, new class implements vscode.CodeLensProvider {
provideCodeLenses(): any {
throw new Error('evil');
}
}));
disposables.add(extHost.registerCodeLensProvider(defaultExtension, defaultSelector, new class implements vscode.CodeLensProvider {
provideCodeLenses() {
return [new types.CodeLens(new types.Range(0, 0, 0, 0))];
}
}));
disposables.add(extHost.registerCodeLensProvider(defaultExtension, defaultSelector, new class implements vscode.CodeLensProvider {
provideCodeLenses(): any {
throw new Error('evil');
}
}));
disposables.add(extHost.registerCodeLensProvider(defaultExtension, defaultSelector, new class implements vscode.CodeLensProvider {
provideCodeLenses() {
return [new types.CodeLens(new types.Range(0, 0, 0, 0))];
}
}));
await rpcProtocol.sync();
const value = await getCodeLensModel(languageFeaturesService.codeLensProvider, model, CancellationToken.None);
assert.strictEqual(value.lenses.length, 1);
value.dispose();
await rpcProtocol.sync();
const value = await getCodeLensModel(languageFeaturesService.codeLensProvider, model, CancellationToken.None);
assert.strictEqual(value.lenses.length, 1);
value.dispose();
});
});
test('CodeLens, do not resolve a resolved lens', async () => {
return runWithFakedTimers({ useFakeTimers: true }, async () => {
disposables.add(extHost.registerCodeLensProvider(defaultExtension, defaultSelector, new class implements vscode.CodeLensProvider {
provideCodeLenses(): any {
return [new types.CodeLens(
new types.Range(0, 0, 0, 0),
{ command: 'id', title: 'Title' })];
}
resolveCodeLens(): any {
assert.ok(false, 'do not resolve');
}
}));
disposables.add(extHost.registerCodeLensProvider(defaultExtension, defaultSelector, new class implements vscode.CodeLensProvider {
provideCodeLenses(): any {
return [new types.CodeLens(
new types.Range(0, 0, 0, 0),
{ command: 'id', title: 'Title' })];
}
resolveCodeLens(): any {
assert.ok(false, 'do not resolve');
}
}));
await rpcProtocol.sync();
const value = await getCodeLensModel(languageFeaturesService.codeLensProvider, model, CancellationToken.None);
assert.strictEqual(value.lenses.length, 1);
const [data] = value.lenses;
const symbol = await Promise.resolve(data.provider.resolveCodeLens!(model, data.symbol, CancellationToken.None));
assert.strictEqual(symbol!.command!.id, 'id');
assert.strictEqual(symbol!.command!.title, 'Title');
value.dispose();
await rpcProtocol.sync();
const value = await getCodeLensModel(languageFeaturesService.codeLensProvider, model, CancellationToken.None);
assert.strictEqual(value.lenses.length, 1);
const [data] = value.lenses;
const symbol = await Promise.resolve(data.provider.resolveCodeLens!(model, data.symbol, CancellationToken.None));
assert.strictEqual(symbol!.command!.id, 'id');
assert.strictEqual(symbol!.command!.title, 'Title');
value.dispose();
});
});
test('CodeLens, missing command', async () => {
return runWithFakedTimers({ useFakeTimers: true }, async () => {
disposables.add(extHost.registerCodeLensProvider(defaultExtension, defaultSelector, new class implements vscode.CodeLensProvider {
provideCodeLenses() {
return [new types.CodeLens(new types.Range(0, 0, 0, 0))];
}
}));
disposables.add(extHost.registerCodeLensProvider(defaultExtension, defaultSelector, new class implements vscode.CodeLensProvider {
provideCodeLenses() {
return [new types.CodeLens(new types.Range(0, 0, 0, 0))];
}
}));
await rpcProtocol.sync();
const value = await getCodeLensModel(languageFeaturesService.codeLensProvider, model, CancellationToken.None);
assert.strictEqual(value.lenses.length, 1);
const [data] = value.lenses;
const symbol = await Promise.resolve(data.provider.resolveCodeLens!(model, data.symbol, CancellationToken.None));
assert.strictEqual(symbol, undefined);
value.dispose();
await rpcProtocol.sync();
const value = await getCodeLensModel(languageFeaturesService.codeLensProvider, model, CancellationToken.None);
assert.strictEqual(value.lenses.length, 1);
const [data] = value.lenses;
const symbol = await Promise.resolve(data.provider.resolveCodeLens!(model, data.symbol, CancellationToken.None));
assert.strictEqual(symbol, undefined);
value.dispose();
});
});
// --- definition
@@ -636,92 +640,96 @@ suite('ExtHostLanguageFeatures', function () {
// --- quick fix
test('Quick Fix, command data conversion', async () => {
return runWithFakedTimers({ useFakeTimers: true }, async () => {
disposables.add(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, {
provideCodeActions(): vscode.Command[] {
return [
{ command: 'test1', title: 'Testing1' },
{ command: 'test2', title: 'Testing2' }
];
}
}));
disposables.add(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, {
provideCodeActions(): vscode.Command[] {
return [
{ command: 'test1', title: 'Testing1' },
{ command: 'test2', title: 'Testing2' }
];
}
}));
await rpcProtocol.sync();
const value = await getCodeActions(languageFeaturesService.codeActionProvider, model, model.getFullModelRange(), { type: languages.CodeActionTriggerType.Invoke, triggerAction: CodeActionTriggerSource.QuickFix }, Progress.None, CancellationToken.None);
const { validActions: actions } = value;
assert.strictEqual(actions.length, 2);
const [first, second] = actions;
assert.strictEqual(first.action.title, 'Testing1');
assert.strictEqual(first.action.command!.id, 'test1');
assert.strictEqual(second.action.title, 'Testing2');
assert.strictEqual(second.action.command!.id, 'test2');
value.dispose();
await rpcProtocol.sync();
const value = await getCodeActions(languageFeaturesService.codeActionProvider, model, model.getFullModelRange(), { type: languages.CodeActionTriggerType.Invoke, triggerAction: CodeActionTriggerSource.QuickFix }, Progress.None, CancellationToken.None);
const { validActions: actions } = value;
assert.strictEqual(actions.length, 2);
const [first, second] = actions;
assert.strictEqual(first.action.title, 'Testing1');
assert.strictEqual(first.action.command!.id, 'test1');
assert.strictEqual(second.action.title, 'Testing2');
assert.strictEqual(second.action.command!.id, 'test2');
value.dispose();
});
});
test('Quick Fix, code action data conversion', async () => {
return runWithFakedTimers({ useFakeTimers: true }, async () => {
disposables.add(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, {
provideCodeActions(): vscode.CodeAction[] {
return [
{
title: 'Testing1',
command: { title: 'Testing1Command', command: 'test1' },
kind: types.CodeActionKind.Empty.append('test.scope')
}
];
}
}));
disposables.add(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, {
provideCodeActions(): vscode.CodeAction[] {
return [
{
title: 'Testing1',
command: { title: 'Testing1Command', command: 'test1' },
kind: types.CodeActionKind.Empty.append('test.scope')
}
];
}
}));
await rpcProtocol.sync();
const value = await getCodeActions(languageFeaturesService.codeActionProvider, model, model.getFullModelRange(), { type: languages.CodeActionTriggerType.Invoke, triggerAction: CodeActionTriggerSource.Default }, Progress.None, CancellationToken.None);
const { validActions: actions } = value;
assert.strictEqual(actions.length, 1);
const [first] = actions;
assert.strictEqual(first.action.title, 'Testing1');
assert.strictEqual(first.action.command!.title, 'Testing1Command');
assert.strictEqual(first.action.command!.id, 'test1');
assert.strictEqual(first.action.kind, 'test.scope');
value.dispose();
await rpcProtocol.sync();
const value = await getCodeActions(languageFeaturesService.codeActionProvider, model, model.getFullModelRange(), { type: languages.CodeActionTriggerType.Invoke, triggerAction: CodeActionTriggerSource.Default }, Progress.None, CancellationToken.None);
const { validActions: actions } = value;
assert.strictEqual(actions.length, 1);
const [first] = actions;
assert.strictEqual(first.action.title, 'Testing1');
assert.strictEqual(first.action.command!.title, 'Testing1Command');
assert.strictEqual(first.action.command!.id, 'test1');
assert.strictEqual(first.action.kind, 'test.scope');
value.dispose();
});
});
test('Cannot read property \'id\' of undefined, #29469', async () => {
return runWithFakedTimers({ useFakeTimers: true }, async () => {
disposables.add(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, new class implements vscode.CodeActionProvider {
provideCodeActions(): any {
return [
undefined,
null,
{ command: 'test', title: 'Testing' }
];
}
}));
disposables.add(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, new class implements vscode.CodeActionProvider {
provideCodeActions(): any {
return [
undefined,
null,
{ command: 'test', title: 'Testing' }
];
}
}));
await rpcProtocol.sync();
const value = await getCodeActions(languageFeaturesService.codeActionProvider, model, model.getFullModelRange(), { type: languages.CodeActionTriggerType.Invoke, triggerAction: CodeActionTriggerSource.Default }, Progress.None, CancellationToken.None);
const { validActions: actions } = value;
assert.strictEqual(actions.length, 1);
value.dispose();
await rpcProtocol.sync();
const value = await getCodeActions(languageFeaturesService.codeActionProvider, model, model.getFullModelRange(), { type: languages.CodeActionTriggerType.Invoke, triggerAction: CodeActionTriggerSource.Default }, Progress.None, CancellationToken.None);
const { validActions: actions } = value;
assert.strictEqual(actions.length, 1);
value.dispose();
});
});
test('Quick Fix, evil provider', async () => {
return runWithFakedTimers({ useFakeTimers: true }, async () => {
disposables.add(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, new class implements vscode.CodeActionProvider {
provideCodeActions(): any {
throw new Error('evil');
}
}));
disposables.add(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, new class implements vscode.CodeActionProvider {
provideCodeActions(): any {
return [{ command: 'test', title: 'Testing' }];
}
}));
disposables.add(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, new class implements vscode.CodeActionProvider {
provideCodeActions(): any {
throw new Error('evil');
}
}));
disposables.add(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, new class implements vscode.CodeActionProvider {
provideCodeActions(): any {
return [{ command: 'test', title: 'Testing' }];
}
}));
await rpcProtocol.sync();
const value = await getCodeActions(languageFeaturesService.codeActionProvider, model, model.getFullModelRange(), { type: languages.CodeActionTriggerType.Invoke, triggerAction: CodeActionTriggerSource.QuickFix }, Progress.None, CancellationToken.None);
const { validActions: actions } = value;
assert.strictEqual(actions.length, 1);
value.dispose();
await rpcProtocol.sync();
const value = await getCodeActions(languageFeaturesService.codeActionProvider, model, model.getFullModelRange(), { type: languages.CodeActionTriggerType.Invoke, triggerAction: CodeActionTriggerSource.QuickFix }, Progress.None, CancellationToken.None);
const { validActions: actions } = value;
assert.strictEqual(actions.length, 1);
value.dispose();
});
});
// --- navigate types
@@ -965,103 +973,107 @@ suite('ExtHostLanguageFeatures', function () {
// --- suggestions
test('Suggest, order 1/3', async () => {
return runWithFakedTimers({ useFakeTimers: true }, async () => {
disposables.add(extHost.registerCompletionItemProvider(defaultExtension, '*', new class implements vscode.CompletionItemProvider {
provideCompletionItems(): any {
return [new types.CompletionItem('testing1')];
}
}, []));
disposables.add(extHost.registerCompletionItemProvider(defaultExtension, '*', new class implements vscode.CompletionItemProvider {
provideCompletionItems(): any {
return [new types.CompletionItem('testing1')];
}
}, []));
disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider {
provideCompletionItems(): any {
return [new types.CompletionItem('testing2')];
}
}, []));
disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider {
provideCompletionItems(): any {
return [new types.CompletionItem('testing2')];
}
}, []));
await rpcProtocol.sync();
const value = await provideSuggestionItems(languageFeaturesService.completionProvider, model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set<languages.CompletionItemKind>().add(languages.CompletionItemKind.Snippet)));
assert.strictEqual(value.items.length, 1);
assert.strictEqual(value.items[0].completion.insertText, 'testing2');
value.disposable.dispose();
await rpcProtocol.sync();
const value = await provideSuggestionItems(languageFeaturesService.completionProvider, model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set<languages.CompletionItemKind>().add(languages.CompletionItemKind.Snippet)));
assert.strictEqual(value.items.length, 1);
assert.strictEqual(value.items[0].completion.insertText, 'testing2');
value.disposable.dispose();
});
});
test('Suggest, order 2/3', async () => {
return runWithFakedTimers({ useFakeTimers: true }, async () => {
disposables.add(extHost.registerCompletionItemProvider(defaultExtension, '*', new class implements vscode.CompletionItemProvider {
provideCompletionItems(): any {
return [new types.CompletionItem('weak-selector')]; // weaker selector but result
}
}, []));
disposables.add(extHost.registerCompletionItemProvider(defaultExtension, '*', new class implements vscode.CompletionItemProvider {
provideCompletionItems(): any {
return [new types.CompletionItem('weak-selector')]; // weaker selector but result
}
}, []));
disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider {
provideCompletionItems(): any {
return []; // stronger selector but not a good result;
}
}, []));
disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider {
provideCompletionItems(): any {
return []; // stronger selector but not a good result;
}
}, []));
await rpcProtocol.sync();
const value = await provideSuggestionItems(languageFeaturesService.completionProvider, model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set<languages.CompletionItemKind>().add(languages.CompletionItemKind.Snippet)));
assert.strictEqual(value.items.length, 1);
assert.strictEqual(value.items[0].completion.insertText, 'weak-selector');
value.disposable.dispose();
await rpcProtocol.sync();
const value = await provideSuggestionItems(languageFeaturesService.completionProvider, model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set<languages.CompletionItemKind>().add(languages.CompletionItemKind.Snippet)));
assert.strictEqual(value.items.length, 1);
assert.strictEqual(value.items[0].completion.insertText, 'weak-selector');
value.disposable.dispose();
});
});
test('Suggest, order 3/3', async () => {
return runWithFakedTimers({ useFakeTimers: true }, async () => {
disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider {
provideCompletionItems(): any {
return [new types.CompletionItem('strong-1')];
}
}, []));
disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider {
provideCompletionItems(): any {
return [new types.CompletionItem('strong-1')];
}
}, []));
disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider {
provideCompletionItems(): any {
return [new types.CompletionItem('strong-2')];
}
}, []));
disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider {
provideCompletionItems(): any {
return [new types.CompletionItem('strong-2')];
}
}, []));
await rpcProtocol.sync();
const value = await provideSuggestionItems(languageFeaturesService.completionProvider, model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set<languages.CompletionItemKind>().add(languages.CompletionItemKind.Snippet)));
assert.strictEqual(value.items.length, 2);
assert.strictEqual(value.items[0].completion.insertText, 'strong-1'); // sort by label
assert.strictEqual(value.items[1].completion.insertText, 'strong-2');
value.disposable.dispose();
await rpcProtocol.sync();
const value = await provideSuggestionItems(languageFeaturesService.completionProvider, model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set<languages.CompletionItemKind>().add(languages.CompletionItemKind.Snippet)));
assert.strictEqual(value.items.length, 2);
assert.strictEqual(value.items[0].completion.insertText, 'strong-1'); // sort by label
assert.strictEqual(value.items[1].completion.insertText, 'strong-2');
value.disposable.dispose();
});
});
test('Suggest, evil provider', async () => {
return runWithFakedTimers({ useFakeTimers: true }, async () => {
disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider {
provideCompletionItems(): any {
throw new Error('evil');
}
}, []));
disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider {
provideCompletionItems(): any {
throw new Error('evil');
}
}, []));
disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider {
provideCompletionItems(): any {
return [new types.CompletionItem('testing')];
}
}, []));
disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider {
provideCompletionItems(): any {
return [new types.CompletionItem('testing')];
}
}, []));
await rpcProtocol.sync();
const value = await provideSuggestionItems(languageFeaturesService.completionProvider, model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set<languages.CompletionItemKind>().add(languages.CompletionItemKind.Snippet)));
assert.strictEqual(value.items[0].container.incomplete, false);
value.disposable.dispose();
await rpcProtocol.sync();
const value = await provideSuggestionItems(languageFeaturesService.completionProvider, model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set<languages.CompletionItemKind>().add(languages.CompletionItemKind.Snippet)));
assert.strictEqual(value.items[0].container.incomplete, false);
value.disposable.dispose();
});
});
test('Suggest, CompletionList', async () => {
return runWithFakedTimers({ useFakeTimers: true }, async () => {
disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider {
provideCompletionItems(): any {
return new types.CompletionList([<any>new types.CompletionItem('hello')], true);
}
}, []));
disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider {
provideCompletionItems(): any {
return new types.CompletionList([<any>new types.CompletionItem('hello')], true);
}
}, []));
await rpcProtocol.sync();
await provideSuggestionItems(languageFeaturesService.completionProvider, model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set<languages.CompletionItemKind>().add(languages.CompletionItemKind.Snippet))).then(model => {
assert.strictEqual(model.items[0].container.incomplete, true);
model.disposable.dispose();
await rpcProtocol.sync();
await provideSuggestionItems(languageFeaturesService.completionProvider, model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set<languages.CompletionItemKind>().add(languages.CompletionItemKind.Snippet))).then(model => {
assert.strictEqual(model.items[0].container.incomplete, true);
model.disposable.dispose();
});
});
});

View File

@@ -34,6 +34,46 @@
const urlParams = new URLSearchParams(window.location.search);
const isCI = urlParams.get('ci');
const $globalThis = globalThis;
const setTimeout0IsFaster = (typeof $globalThis.postMessage === 'function' && !$globalThis.importScripts);
/**
* See https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#:~:text=than%204%2C%20then-,set%20timeout%20to%204,-.
*
* Works similarly to `setTimeout(0)` but doesn't suffer from the 4ms artificial delay
* that browsers set when the nesting level is > 5.
*/
const setTimeout0 = (() => {
if (setTimeout0IsFaster) {
const pending = [];
$globalThis.addEventListener('message', (e) => {
if (e.data && e.data.vscodeScheduleAsyncWork) {
for (let i = 0, len = pending.length; i < len; i++) {
const candidate = pending[i];
if (candidate.id === e.data.vscodeScheduleAsyncWork) {
pending.splice(i, 1);
candidate.callback();
return;
}
}
}
});
let lastId = 0;
return (callback) => {
const myId = ++lastId;
pending.push({
id: myId,
callback: callback
});
$globalThis.postMessage({ vscodeScheduleAsyncWork: myId }, '*');
};
}
return (callback) => setTimeout(callback);
})();
Mocha.Runner.immediately = setTimeout0;
mocha.setup({
ui: 'tdd',
timeout: isCI ? 30000 : 5000

View File

@@ -32,12 +32,6 @@
e.stopPropagation();
}
});
mocha.setup({
ui: 'tdd',
timeout: typeof process.env['BUILD_ARTIFACTSTAGINGDIRECTORY'] === 'string' ? 30000 : 5000,
forbidOnly: typeof process.env['BUILD_ARTIFACTSTAGINGDIRECTORY'] === 'string' // disallow .only() when running on build machine
});
</script>
<script>
const urlParams = new URLSearchParams(window.location.search);

View File

@@ -398,7 +398,57 @@ class IPCReporter {
}
}
const $globalThis = globalThis;
const setTimeout0IsFaster = (typeof $globalThis.postMessage === 'function' && !$globalThis.importScripts);
/**
* See https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#:~:text=than%204%2C%20then-,set%20timeout%20to%204,-.
*
* Works similarly to `setTimeout(0)` but doesn't suffer from the 4ms artificial delay
* that browsers set when the nesting level is > 5.
*/
const setTimeout0 = (() => {
if (setTimeout0IsFaster) {
const pending = [];
$globalThis.addEventListener('message', (e) => {
if (e.data && e.data.vscodeScheduleAsyncWork) {
for (let i = 0, len = pending.length; i < len; i++) {
const candidate = pending[i];
if (candidate.id === e.data.vscodeScheduleAsyncWork) {
pending.splice(i, 1);
candidate.callback();
return;
}
}
}
});
let lastId = 0;
return (callback) => {
const myId = ++lastId;
pending.push({
id: myId,
callback: callback
});
$globalThis.postMessage({ vscodeScheduleAsyncWork: myId }, '*');
};
}
return (callback) => setTimeout(callback);
})();
async function runTests(opts) {
// @ts-expect-error
Mocha.Runner.immediately = setTimeout0;
mocha.setup({
ui: 'tdd',
// @ts-expect-error
reporter: opts.dev ? 'html' : IPCReporter,
grep: opts.grep,
timeout: opts.timeout ?? (IS_CI ? 30000 : 5000),
forbidOnly: IS_CI // disallow .only() when running on build machine
});
// this *must* come before loadTests, or it doesn't work.
if (opts.timeout !== undefined) {
mocha.timeout(opts.timeout);
@@ -406,15 +456,6 @@ async function runTests(opts) {
await loadTests(opts);
if (opts.grep) {
mocha.grep(opts.grep);
}
if (!opts.dev) {
// @ts-expect-error
mocha.reporter(IPCReporter);
}
const runner = mocha.run(async () => {
await createCoverageReport(opts)
ipcRenderer.send('all done');

View File

@@ -17,6 +17,7 @@ import minimatch from 'minimatch';
import minimist from 'minimist';
import * as module from 'module';
import { fileURLToPath, pathToFileURL } from 'url';
import semver from 'semver';
/**
* @type {{ build: boolean; run: string; runGlob: string; coverage: boolean; help: boolean; coverageFormats: string | string[]; coveragePath: string; }}
@@ -68,10 +69,11 @@ const src = path.join(REPO_ROOT, out);
const baseUrl = pathToFileURL(src);
//@ts-ignore
const majorRequiredNodeVersion = `v${/^target="(.*)"$/m.exec(fs.readFileSync(path.join(REPO_ROOT, 'remote', '.npmrc'), 'utf8'))[1]}`.substring(0, 3);
const currentMajorNodeVersion = process.version.substring(0, 3);
if (majorRequiredNodeVersion !== currentMajorNodeVersion) {
console.error(`node.js unit tests require a major node.js version of ${majorRequiredNodeVersion} (your version is: ${currentMajorNodeVersion})`);
const requiredNodeVersion = semver.parse(/^target="(.*)"$/m.exec(fs.readFileSync(path.join(REPO_ROOT, 'remote', '.npmrc'), 'utf8'))[1]);
const currentNodeVersion = semver.parse(process.version);
//@ts-ignore
if (currentNodeVersion?.major < requiredNodeVersion?.major) {
console.error(`node.js unit tests require a major node.js version of ${requiredNodeVersion?.major} (your version is: ${currentNodeVersion?.major})`);
process.exit(1);
}