| /* eslint-disable @typescript-eslint/no-explicit-any */ |
| /* eslint-disable @typescript-eslint/no-unused-vars */ |
| /*--------------------------------------------------------- |
| * Copyright 2021 The Go Authors. All rights reserved. |
| * Licensed under the MIT License. See LICENSE in the project root for license information. |
| *--------------------------------------------------------*/ |
| import path = require('path'); |
| import { |
| CancellationToken, |
| EndOfLine, |
| FileType, |
| MarkdownString, |
| Position, |
| Range, |
| TestController, |
| TestItem, |
| TestItemCollection, |
| TestMessage, |
| TestRun, |
| TestRunProfile, |
| TestRunProfileKind, |
| TestRunRequest, |
| TestTag, |
| TextDocument, |
| TextLine, |
| Uri, |
| WorkspaceFolder |
| } from 'vscode'; |
| import { FileSystem, Workspace } from '../../src/goTest/utils'; |
| |
| type TestRunHandler = (request: TestRunRequest, token: CancellationToken) => Thenable<void> | void; |
| |
| class MockTestCollection implements TestItemCollection { |
| constructor(private item: MockTestItem | MockTestController) {} |
| |
| private readonly m = new Map<string, MockTestItem>(); |
| |
| get size() { |
| return this.m.size; |
| } |
| |
| forEach(fn: (item: TestItem, coll: TestItemCollection) => unknown) { |
| for (const item of this.m.values()) fn(item, this); |
| } |
| |
| add(item: TestItem): void { |
| if (this.m.has(item.id)) { |
| throw new Error(`Test item ${item.id} already exists`); |
| } |
| |
| if (!(item instanceof MockTestItem)) { |
| throw new Error('not a mock'); |
| } else if (this.item instanceof MockTestItem) { |
| item.parent = this.item; |
| } |
| |
| this.m.set(item.id, item); |
| } |
| |
| delete(id: string): void { |
| this.m.delete(id); |
| } |
| |
| get(id: string): TestItem | undefined { |
| return this.m.get(id); |
| } |
| |
| replace(items: readonly TestItem[]): void { |
| throw new Error('not impelemented'); |
| } |
| } |
| |
| class MockTestItem implements TestItem { |
| private static idNum = 0; |
| private idNum: number; |
| |
| constructor(public id: string, public label: string, public uri: Uri | undefined, public ctrl: MockTestController) { |
| this.idNum = MockTestItem.idNum; |
| MockTestItem.idNum++; |
| } |
| tags: readonly TestTag[] = []; |
| sortText?: string | undefined; |
| |
| parent: TestItem | undefined; |
| canResolveChildren = false; |
| busy = false; |
| description?: string; |
| range: Range | undefined; |
| error: string | MarkdownString | undefined; |
| runnable = false; |
| debuggable = false; |
| |
| children: MockTestCollection = new MockTestCollection(this); |
| |
| invalidateResults(): void {} |
| |
| dispose(): void { |
| if (this.parent instanceof MockTestItem) { |
| this.parent.children.delete(this.id); |
| } |
| } |
| } |
| |
| class MockTestRunProfile implements TestRunProfile { |
| constructor( |
| public label: string, |
| public kind: TestRunProfileKind, |
| public runHandler: TestRunHandler, |
| public isDefault: boolean |
| ) {} |
| tag: TestTag | undefined; |
| |
| configureHandler(): void {} |
| dispose(): void {} |
| } |
| |
| class MockTestRun implements TestRun { |
| name = 'test run'; |
| isPersisted = false; |
| |
| get token(): CancellationToken { |
| throw new Error('Method not implemented.'); |
| } |
| |
| enqueued(test: TestItem): void {} |
| started(test: TestItem): void {} |
| skipped(test: TestItem): void {} |
| failed(test: TestItem, message: TestMessage | readonly TestMessage[], duration?: number): void {} |
| errored(test: TestItem, message: TestMessage | readonly TestMessage[], duration?: number): void {} |
| passed(test: TestItem, duration?: number): void {} |
| appendOutput(output: string): void {} |
| end(): void {} |
| } |
| |
| export class MockTestController implements TestController { |
| id = 'go'; |
| label = 'Go'; |
| items = new MockTestCollection(this); |
| |
| resolveHandler?: (item: TestItem | undefined) => void | Thenable<void>; |
| refreshHandler: ((token: CancellationToken) => void | Thenable<void>) | undefined; |
| |
| createTestRun(request: TestRunRequest, name?: string, persist?: boolean): TestRun { |
| return new MockTestRun(); |
| } |
| |
| createRunProfile( |
| label: string, |
| kind: TestRunProfileKind, |
| runHandler: TestRunHandler, |
| isDefault = false |
| ): TestRunProfile { |
| return new MockTestRunProfile(label, kind, runHandler, isDefault); |
| } |
| |
| createTestItem(id: string, label: string, uri?: Uri): TestItem { |
| return new MockTestItem(id, label, uri, this); |
| } |
| |
| dispose(): void {} |
| } |
| |
| type DirEntry = [string, FileType]; |
| |
| class MockTestFileSystem implements FileSystem { |
| constructor(public dirs: Map<string, DirEntry[]>, public files: Map<string, MockTestDocument>) {} |
| |
| readDirectory(uri: Uri): Thenable<[string, FileType][]> { |
| const k = uri.with({ query: '', fragment: '' }).toString(); |
| return Promise.resolve(this.dirs.get(k) || []); |
| } |
| |
| readFile(uri: Uri): Thenable<Uint8Array> { |
| const k = uri.with({ query: '', fragment: '' }).toString(); |
| const s = this.files.get(k)?.getText(); |
| return Promise.resolve(Buffer.from(s || '')); |
| } |
| } |
| |
| function unindent(s: string): string { |
| let lines = s.split('\n'); |
| if (/^\s*$/.test(lines[0])) lines = lines.slice(1); |
| |
| const m = lines[0].match(/^\s+/); |
| if (!m) return s; |
| if (!lines.every((l) => /^\s*$/.test(l) || l.startsWith(m[0]))) return s; |
| |
| for (const i in lines) { |
| lines[i] = lines[i].substring(m[0].length); |
| } |
| return lines.join('\n'); |
| } |
| |
| export class MockTestWorkspace implements Workspace { |
| static from(folders: string[], contents: Record<string, string | { contents: string; language: string }>) { |
| const wsdirs: WorkspaceFolder[] = []; |
| const dirs = new Map<string, DirEntry[]>(); |
| const files = new Map<string, MockTestDocument>(); |
| |
| for (const i in folders) { |
| const uri = Uri.parse(folders[i]); |
| wsdirs.push({ uri, index: Number(i), name: path.basename(uri.path) }); |
| } |
| |
| function push(uri: Uri, child: FileType) { |
| const entry: DirEntry = [path.basename(uri.path), child]; |
| const dir = uri.with({ path: path.dirname(uri.path) }); |
| if (dirs.has(dir.toString())) { |
| dirs.get(dir.toString())?.push(entry); |
| return; |
| } |
| |
| if (path.dirname(dir.path) !== dir.path) { |
| push(dir, FileType.Directory); |
| } |
| dirs.set(dir.toString(), [entry]); |
| } |
| |
| for (const k in contents) { |
| const uri = Uri.parse(k); |
| const entry = contents[k]; |
| |
| let doc: MockTestDocument; |
| if (typeof entry === 'object') { |
| doc = new MockTestDocument(uri, unindent(entry.contents), entry.language); |
| } else if (path.basename(uri.path) === 'go.mod') { |
| doc = new MockTestDocument(uri, unindent(entry), 'go.mod'); |
| } else { |
| doc = new MockTestDocument(uri, unindent(entry)); |
| } |
| |
| files.set(uri.toString(), doc); |
| push(uri, FileType.File); |
| } |
| |
| return new this(wsdirs, new MockTestFileSystem(dirs, files)); |
| } |
| |
| constructor(public workspaceFolders: WorkspaceFolder[], public fs: MockTestFileSystem) {} |
| |
| openTextDocument(uri: Uri): Thenable<TextDocument> { |
| const doc = this.fs.files.get(uri.toString()); |
| if (!doc) throw Error('doc not found'); |
| return Promise.resolve(doc); |
| } |
| |
| getWorkspaceFolder(uri: Uri): WorkspaceFolder { |
| return this.workspaceFolders.filter((x) => x.uri === uri)[0]; |
| } |
| |
| textDocuments: TextDocument[] = []; |
| } |
| |
| class MockTestDocument implements TextDocument { |
| constructor( |
| public uri: Uri, |
| private _contents: string, |
| public languageId: string = 'go', |
| public isUntitled: boolean = false, |
| public isDirty: boolean = false |
| ) {} |
| |
| set contents(s: string) { |
| this._contents = s; |
| } |
| |
| readonly version: number = 1; |
| readonly eol: EndOfLine = EndOfLine.LF; |
| |
| get lineCount() { |
| return this._contents.split('\n').length; |
| } |
| |
| get fileName() { |
| return path.basename(this.uri.path); |
| } |
| |
| save(): Thenable<boolean> { |
| if (!this.isDirty) { |
| return Promise.resolve(false); |
| } |
| |
| this.isDirty = false; |
| return Promise.resolve(true); |
| } |
| |
| get isClosed(): boolean { |
| throw new Error('Method not implemented.'); |
| } |
| |
| lineAt(line: number): TextLine; |
| lineAt(position: Position): TextLine; |
| lineAt(position: any): TextLine { |
| throw new Error('Method not implemented.'); |
| } |
| |
| offsetAt(position: Position): number { |
| throw new Error('Method not implemented.'); |
| } |
| |
| positionAt(offset: number): Position { |
| throw new Error('Method not implemented.'); |
| } |
| |
| getText(range?: Range): string { |
| if (range) { |
| throw new Error('Method not implemented.'); |
| } |
| return this._contents; |
| } |
| |
| getWordRangeAtPosition(position: Position, regex?: RegExp): Range { |
| throw new Error('Method not implemented.'); |
| } |
| |
| validateRange(range: Range): Range { |
| throw new Error('Method not implemented.'); |
| } |
| |
| validatePosition(position: Position): Position { |
| throw new Error('Method not implemented.'); |
| } |
| } |