From 502cba6fc040c91eef80b1385bd5fed8da24d90b Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 9 Feb 2021 15:31:51 -0800 Subject: [PATCH] adopt new test api --- test-provider-sample/sample/test.md | 2 + test-provider-sample/src/stateRegistry.ts | 70 ------------- test-provider-sample/src/testProvider.ts | 120 ++++++---------------- 3 files changed, 31 insertions(+), 161 deletions(-) delete mode 100644 test-provider-sample/src/stateRegistry.ts diff --git a/test-provider-sample/sample/test.md b/test-provider-sample/sample/test.md index 400569e2..ca8a2235 100644 --- a/test-provider-sample/sample/test.md +++ b/test-provider-sample/sample/test.md @@ -6,3 +6,5 @@ # Harder Math 230230 + 5819123 = 6049353 + +3 - 1 = 2 diff --git a/test-provider-sample/src/stateRegistry.ts b/test-provider-sample/src/stateRegistry.ts deleted file mode 100644 index f245257b..00000000 --- a/test-provider-sample/src/stateRegistry.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { TestRunState, TestState } from 'vscode'; - -/** - * Utility class used in the state registry. - */ -class StateValue { - private readonly listeners = new Set<(v: TestState) => void>(); - private _value = StateRegistry.unsetState; - - public get value() { - return this._value; - } - - public set value(value: TestState) { - this._value = value; - for (const listener of this.listeners) { - listener(value); - } - } - - public addListener(l: (v: TestState) => void) { - this.listeners.add(l); - return this.listeners.size; - } - - public removeListener(l: (v: TestState) => void) { - this.listeners.delete(l); - return this.listeners.size; - } -} - -/** - * Singleton used to share state internally between tests in text files and - * those in the workspace. - */ -class StateRegistry { - public static unsetState = new TestState(TestRunState.Unset); - - private readonly values = new Map(); - - public current(id: string) { - return this.values.get(id)?.value ?? StateRegistry.unsetState; - } - - public update(id: string, value: TestState) { - const record = this.values.get(id); - if (record && record.value !== value) { - record.value = value; - } - } - - public listen(id: string, l: (v: TestState) => void) { - let value = this.values.get(id); - if (!value) { - value = new StateValue(); - this.values.set(id, value); - } - - value.addListener(l); - return { - dispose: () => { - if (value!.removeListener(l) === 0) { - this.values.delete(id); - } - }, - }; - } -} - -export const states = new StateRegistry(); diff --git a/test-provider-sample/src/testProvider.ts b/test-provider-sample/src/testProvider.ts index 498c3b8a..9edb4bf5 100644 --- a/test-provider-sample/src/testProvider.ts +++ b/test-provider-sample/src/testProvider.ts @@ -1,6 +1,5 @@ import { TextDecoder } from 'util'; import * as vscode from 'vscode'; -import { states } from './stateRegistry'; const textDecoder = new TextDecoder('utf-8'); @@ -29,10 +28,7 @@ export class MathTestProvider implements vscode.TestProvider { root, onDidChangeTest: changeTestEmitter.event, discoveredInitialTests, - dispose: () => { - watcher.dispose(); - root.dispose(); - }, + dispose: () => watcher.dispose(), }; } @@ -45,11 +41,11 @@ export class MathTestProvider implements vscode.TestProvider { root.children.push(file); const changeTestEmitter = new vscode.EventEmitter(); - file.updateTestsFromText(document.getText(), changeTestEmitter); + file.updateTestsFromText(document.getText()); const listener = vscode.workspace.onDidChangeTextDocument(evt => { if (evt.document === document) { - file.updateTestsFromText(document.getText(), changeTestEmitter); + file.updateTestsFromText(document.getText()); changeTestEmitter.fire(file); } }); @@ -58,41 +54,32 @@ export class MathTestProvider implements vscode.TestProvider { root, onDidChangeTest: changeTestEmitter.event, discoveredInitialTests: Promise.resolve(), - dispose: () => { - listener.dispose(); - root.dispose(); - }, + dispose: () => listener.dispose(), }; } /** * @inheritdoc */ - public async runTests(options: vscode.TestRunOptions, cancellation: vscode.CancellationToken) { - const queue = await this.gatherTestTree(options.tests); + public async runTests(run: vscode.TestRun, cancellation: vscode.CancellationToken) { + const runTests = async (tests: Iterable) => { + for (const test of tests) { + if (test instanceof TestCase) { + if (cancellation.isCancellationRequested) { + run.setState(test, { state: vscode.TestRunState.Skipped }); + } else { + run.setState(test, { state: vscode.TestRunState.Running }); + run.setState(test, await test.run()); + } + } - while (queue.length && !cancellation.isCancellationRequested) { - await queue.shift()!.run(); - } - - while (queue.length) { - queue.shift()!.cancel(); - } - } - - private async gatherTestTree(tests: vscode.TestItem[], queue: { run(): Promise; cancel(): void }[] = []) { - for (const test of tests) { - if (test instanceof TestCase) { - test.markQueued(); - queue.push({ run: () => test.run(), cancel: () => test.markCancelled() }); + if (test.children) { + await runTests(test.children); + } } + }; - if (test.children) { - this.gatherTestTree(test.children, queue); - } - } - - return queue; + await runTests(run.tests); } } @@ -108,7 +95,7 @@ const updateTestsInFile = async (root: TestRoot, uri: vscode.Uri, emitter: vscod root.children.push(testFile); } - if ((await testFile.updateTestsFromFs(emitter)) === 0) { + if ((await testFile.updateTestsFromFs()) === 0) { removeTestsForFile(root, uri); emitter.fire(root); } else { @@ -123,25 +110,15 @@ const headingRe = /^(#+)\s*(.+)$/; class TestRoot implements vscode.TestItem { public readonly label = 'Markdown Tests'; - public readonly state = new vscode.TestState(vscode.TestRunState.Unset); public children = [] as TestFile[]; - - public dispose() { - for (const child of this.children) { - child.dispose(); - } - } } class TestFile implements vscode.TestItem { public readonly label = this.uri.path.split('/').pop()!; public children: (TestHeading | TestCase)[] = []; - - public state = new vscode.TestState(vscode.TestRunState.Unset); - constructor(public readonly uri: vscode.Uri) {} - public async updateTestsFromFs(updateEmitter: vscode.EventEmitter) { + public async updateTestsFromFs() { let text: string; try { const rawContent = await vscode.workspace.fs.readFile(this.uri); @@ -151,10 +128,10 @@ class TestFile implements vscode.TestItem { return; } - return this.updateTestsFromText(text, updateEmitter); + return this.updateTestsFromText(text); } - public updateTestsFromText(text: string, updateEmitter: vscode.EventEmitter) { + public updateTestsFromText(text: string) { const lines = text.split('\n'); const ancestors: (TestFile | TestHeading)[] = [this]; let discovered = 0; @@ -168,7 +145,7 @@ class TestFile implements vscode.TestItem { if (test) { const [, a, operator, b, expected] = test; const range = new vscode.Range(new vscode.Position(lineNo, 0), new vscode.Position(lineNo, test[0].length)); - const tcase = new TestCase(Number(a), operator as Operator, Number(b), Number(expected), new vscode.Location(this.uri, range), updateEmitter); + const tcase = new TestCase(Number(a), operator as Operator, Number(b), Number(expected), new vscode.Location(this.uri, range)); ancestors[ancestors.length - 1].children.push(tcase); discovered++; continue; @@ -190,30 +167,16 @@ class TestFile implements vscode.TestItem { return discovered; } - - public dispose() { - for (const child of this.children) { - child.dispose(); - } - } } class TestHeading implements vscode.TestItem { public readonly children: (TestHeading | TestCase)[] = []; - public state = new vscode.TestState(vscode.TestRunState.Unset); - constructor( public readonly level: number, public readonly label: string, public readonly location: vscode.Location ) {} - - public dispose() { - for (const child of this.children) { - child.dispose(); - } - } } class TestCase implements vscode.TestItem { @@ -225,49 +188,28 @@ class TestCase implements vscode.TestItem { return `${this.location.uri.toString()}: ${this.label}`; } - public state = states.current(this.id); - - private stateListener = states.listen(this.id, state => { - this.state = state; - this.updateEmitter.fire(this); - }); - constructor( private readonly a: number, private readonly operator: Operator, private readonly b: number, private readonly expected: number, public readonly location: vscode.Location, - private readonly updateEmitter: vscode.EventEmitter ) {} - markQueued() { - states.update(this.id, new vscode.TestState(vscode.TestRunState.Queued)); - } - - markCancelled() { - states.update(this.id, new vscode.TestState(vscode.TestRunState.Skipped)); - } - - async run() { - states.update(this.id, new vscode.TestState(vscode.TestRunState.Running)); - + async run(): Promise { await new Promise(resolve => setTimeout(resolve, 200 + Math.random() * 3000)); const actual = this.evaluate(); if (actual === this.expected) { - states.update(this.id, new vscode.TestState(vscode.TestRunState.Passed)); + return { state: vscode.TestRunState.Passed}; } else { - states.update( - this.id, - new vscode.TestState(vscode.TestRunState.Failed, [ + return { state: vscode.TestRunState.Failed, messages: [ { message: `Expected ${this.label}`, expectedOutput: String(this.expected), actualOutput: String(actual), location: this.location, }, - ]) - ); + ]}; } } @@ -283,8 +225,4 @@ class TestCase implements vscode.TestItem { return this.a * this.b; } } - - public dispose() { - this.stateListener.dispose(); - } }