blob: 8372de23a6605629cfd80ab129b7367b7d99afec [file] [log] [blame]
/* 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,
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 {
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++;
}
parent: TestItem | undefined;
canResolveChildren: boolean;
busy: boolean;
description?: string;
range?: Range;
error?: string | MarkdownString;
runnable: boolean;
debuggable: boolean;
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
) {}
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) => void | Thenable<void>;
createTestRun(request: TestRunRequest, name?: string, persist?: boolean): TestRun {
return new MockTestRun();
}
createRunProfile(
label: string,
kind: TestRunProfileKind,
runHandler: TestRunHandler,
isDefault?: boolean
): 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> {
return Promise.resolve(this.fs.files.get(uri.toString()));
}
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.');
}
}