Skip to content

Commit a5af2f2

Browse files
Pull out node classes to shared location
1 parent 3ebd13d commit a5af2f2

File tree

4 files changed

+135
-131
lines changed

4 files changed

+135
-131
lines changed
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import { basename, dirname, join } from "path";
2+
import { env } from "vscode";
3+
4+
/**
5+
* A node in the tree of files. This will be either a `FileTreeDirectory` or a `FileTreeLeaf`.
6+
*/
7+
export abstract class FileTreeNode {
8+
constructor(private _path: string, private _name: string) {}
9+
10+
public get path(): string {
11+
return this._path;
12+
}
13+
14+
public get name(): string {
15+
return this._name;
16+
}
17+
18+
public abstract get children(): readonly FileTreeNode[];
19+
20+
public abstract finish(): void;
21+
}
22+
23+
/**
24+
* A directory containing one or more files or other directories.
25+
*/
26+
export class FileTreeDirectory extends FileTreeNode {
27+
constructor(
28+
_path: string,
29+
_name: string,
30+
private _children: FileTreeNode[] = [],
31+
) {
32+
super(_path, _name);
33+
}
34+
35+
public get children(): readonly FileTreeNode[] {
36+
return this._children;
37+
}
38+
39+
public addChild(child: FileTreeNode): void {
40+
this._children.push(child);
41+
}
42+
43+
public createDirectory(relativePath: string): FileTreeDirectory {
44+
const dirName = dirname(relativePath);
45+
if (dirName === ".") {
46+
return this.createChildDirectory(relativePath);
47+
} else {
48+
const parent = this.createDirectory(dirName);
49+
return parent.createDirectory(basename(relativePath));
50+
}
51+
}
52+
53+
public finish(): void {
54+
// remove empty directories
55+
this._children.filter(
56+
(child) => child instanceof FileTreeLeaf || child.children.length > 0,
57+
);
58+
this._children.sort((a, b) => a.name.localeCompare(b.name, env.language));
59+
this._children.forEach((child, i) => {
60+
child.finish();
61+
if (
62+
child.children?.length === 1 &&
63+
child.children[0] instanceof FileTreeDirectory
64+
) {
65+
// collapse children
66+
const replacement = new FileTreeDirectory(
67+
child.children[0].path,
68+
`${child.name} / ${child.children[0].name}`,
69+
Array.from(child.children[0].children),
70+
);
71+
this._children[i] = replacement;
72+
}
73+
});
74+
}
75+
76+
private createChildDirectory(name: string): FileTreeDirectory {
77+
const existingChild = this._children.find((child) => child.name === name);
78+
if (existingChild !== undefined) {
79+
return existingChild as FileTreeDirectory;
80+
} else {
81+
const newChild = new FileTreeDirectory(join(this.path, name), name);
82+
this.addChild(newChild);
83+
return newChild;
84+
}
85+
}
86+
}
87+
88+
/**
89+
* A single file.
90+
*/
91+
export class FileTreeLeaf extends FileTreeNode {
92+
constructor(_path: string, _name: string) {
93+
super(_path, _name);
94+
}
95+
96+
public get children(): readonly FileTreeNode[] {
97+
return [];
98+
}
99+
100+
public finish(): void {
101+
/**/
102+
}
103+
}

extensions/ql-vscode/src/query-testing/qltest-discovery.ts

Lines changed: 10 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -1,117 +1,16 @@
1-
import { dirname, basename, join, normalize, relative, extname } from "path";
1+
import { dirname, basename, normalize, relative, extname } from "path";
22
import { Discovery } from "../common/discovery";
33
import {
44
EventEmitter,
55
Event,
66
Uri,
77
RelativePattern,
88
WorkspaceFolder,
9-
env,
109
} from "vscode";
1110
import { MultiFileSystemWatcher } from "../common/vscode/multi-file-system-watcher";
1211
import { CodeQLCliServer } from "../codeql-cli/cli";
1312
import { pathExists } from "fs-extra";
14-
15-
/**
16-
* A node in the tree of tests. This will be either a `QLTestDirectory` or a `QLTestFile`.
17-
*/
18-
export abstract class QLTestNode {
19-
constructor(private _path: string, private _name: string) {}
20-
21-
public get path(): string {
22-
return this._path;
23-
}
24-
25-
public get name(): string {
26-
return this._name;
27-
}
28-
29-
public abstract get children(): readonly QLTestNode[];
30-
31-
public abstract finish(): void;
32-
}
33-
34-
/**
35-
* A directory containing one or more QL tests or other test directories.
36-
*/
37-
export class QLTestDirectory extends QLTestNode {
38-
constructor(
39-
_path: string,
40-
_name: string,
41-
private _children: QLTestNode[] = [],
42-
) {
43-
super(_path, _name);
44-
}
45-
46-
public get children(): readonly QLTestNode[] {
47-
return this._children;
48-
}
49-
50-
public addChild(child: QLTestNode): void {
51-
this._children.push(child);
52-
}
53-
54-
public createDirectory(relativePath: string): QLTestDirectory {
55-
const dirName = dirname(relativePath);
56-
if (dirName === ".") {
57-
return this.createChildDirectory(relativePath);
58-
} else {
59-
const parent = this.createDirectory(dirName);
60-
return parent.createDirectory(basename(relativePath));
61-
}
62-
}
63-
64-
public finish(): void {
65-
// remove empty directories
66-
this._children.filter(
67-
(child) => child instanceof QLTestFile || child.children.length > 0,
68-
);
69-
this._children.sort((a, b) => a.name.localeCompare(b.name, env.language));
70-
this._children.forEach((child, i) => {
71-
child.finish();
72-
if (
73-
child.children?.length === 1 &&
74-
child.children[0] instanceof QLTestDirectory
75-
) {
76-
// collapse children
77-
const replacement = new QLTestDirectory(
78-
child.children[0].path,
79-
`${child.name} / ${child.children[0].name}`,
80-
Array.from(child.children[0].children),
81-
);
82-
this._children[i] = replacement;
83-
}
84-
});
85-
}
86-
87-
private createChildDirectory(name: string): QLTestDirectory {
88-
const existingChild = this._children.find((child) => child.name === name);
89-
if (existingChild !== undefined) {
90-
return existingChild as QLTestDirectory;
91-
} else {
92-
const newChild = new QLTestDirectory(join(this.path, name), name);
93-
this.addChild(newChild);
94-
return newChild;
95-
}
96-
}
97-
}
98-
99-
/**
100-
* A single QL test. This will be either a `.ql` file or a `.qlref` file.
101-
*/
102-
export class QLTestFile extends QLTestNode {
103-
constructor(_path: string, _name: string) {
104-
super(_path, _name);
105-
}
106-
107-
public get children(): readonly QLTestNode[] {
108-
return [];
109-
}
110-
111-
public finish(): void {
112-
/**/
113-
}
114-
}
13+
import { FileTreeDirectory, FileTreeLeaf } from "../common/file-tree-nodes";
11514

11615
/**
11716
* The results of discovering QL tests.
@@ -120,7 +19,7 @@ interface QLTestDiscoveryResults {
12019
/**
12120
* A directory that contains one or more QL Tests, or other QLTestDirectories.
12221
*/
123-
testDirectory: QLTestDirectory | undefined;
22+
testDirectory: FileTreeDirectory | undefined;
12423

12524
/**
12625
* The file system path to a directory to watch. If any ql or qlref file changes in
@@ -137,7 +36,7 @@ export class QLTestDiscovery extends Discovery<QLTestDiscoveryResults> {
13736
private readonly watcher: MultiFileSystemWatcher = this.push(
13837
new MultiFileSystemWatcher(),
13938
);
140-
private _testDirectory: QLTestDirectory | undefined;
39+
private _testDirectory: FileTreeDirectory | undefined;
14140

14241
constructor(
14342
private readonly workspaceFolder: WorkspaceFolder,
@@ -159,7 +58,7 @@ export class QLTestDiscovery extends Discovery<QLTestDiscoveryResults> {
15958
* The root directory. There is at least one test in this directory, or
16059
* in a subdirectory of this.
16160
*/
162-
public get testDirectory(): QLTestDirectory | undefined {
61+
public get testDirectory(): FileTreeDirectory | undefined {
16362
return this._testDirectory;
16463
}
16564

@@ -194,10 +93,10 @@ export class QLTestDiscovery extends Discovery<QLTestDiscoveryResults> {
19493
* @returns A `QLTestDirectory` object describing the contents of the directory, or `undefined` if
19594
* no tests were found.
19695
*/
197-
private async discoverTests(): Promise<QLTestDirectory> {
96+
private async discoverTests(): Promise<FileTreeDirectory> {
19897
const fullPath = this.workspaceFolder.uri.fsPath;
19998
const name = this.workspaceFolder.name;
200-
const rootDirectory = new QLTestDirectory(fullPath, name);
99+
const rootDirectory = new FileTreeDirectory(fullPath, name);
201100

202101
// Don't try discovery on workspace folders that don't exist on the filesystem
203102
if (await pathExists(fullPath)) {
@@ -208,7 +107,9 @@ export class QLTestDiscovery extends Discovery<QLTestDiscoveryResults> {
208107
const relativePath = normalize(relative(fullPath, testPath));
209108
const dirName = dirname(relativePath);
210109
const parentDirectory = rootDirectory.createDirectory(dirName);
211-
parentDirectory.addChild(new QLTestFile(testPath, basename(testPath)));
110+
parentDirectory.addChild(
111+
new FileTreeLeaf(testPath, basename(testPath)),
112+
);
212113
}
213114

214115
rootDirectory.finish();

extensions/ql-vscode/src/query-testing/test-adapter.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,17 @@ import {
1313
TestHub,
1414
} from "vscode-test-adapter-api";
1515
import { TestAdapterRegistrar } from "vscode-test-adapter-util";
16-
import {
17-
QLTestFile,
18-
QLTestNode,
19-
QLTestDirectory,
20-
QLTestDiscovery,
21-
} from "./qltest-discovery";
16+
import { QLTestDiscovery } from "./qltest-discovery";
2217
import { Event, EventEmitter, CancellationTokenSource } from "vscode";
2318
import { DisposableObject } from "../pure/disposable-object";
2419
import { CodeQLCliServer, TestCompleted } from "../codeql-cli/cli";
2520
import { testLogger } from "../common";
2621
import { TestRunner } from "./test-runner";
22+
import {
23+
FileTreeDirectory,
24+
FileTreeLeaf,
25+
FileTreeNode,
26+
} from "../common/file-tree-nodes";
2727

2828
/**
2929
* Get the full path of the `.expected` file for the specified QL test.
@@ -135,26 +135,26 @@ export class QLTestAdapter extends DisposableObject implements TestAdapter {
135135
}
136136

137137
private static createTestOrSuiteInfos(
138-
testNodes: readonly QLTestNode[],
138+
testNodes: readonly FileTreeNode[],
139139
): Array<TestSuiteInfo | TestInfo> {
140140
return testNodes.map((childNode) => {
141141
return QLTestAdapter.createTestOrSuiteInfo(childNode);
142142
});
143143
}
144144

145145
private static createTestOrSuiteInfo(
146-
testNode: QLTestNode,
146+
testNode: FileTreeNode,
147147
): TestSuiteInfo | TestInfo {
148-
if (testNode instanceof QLTestFile) {
148+
if (testNode instanceof FileTreeLeaf) {
149149
return QLTestAdapter.createTestInfo(testNode);
150-
} else if (testNode instanceof QLTestDirectory) {
150+
} else if (testNode instanceof FileTreeDirectory) {
151151
return QLTestAdapter.createTestSuiteInfo(testNode, testNode.name);
152152
} else {
153153
throw new Error("Unexpected test type.");
154154
}
155155
}
156156

157-
private static createTestInfo(testFile: QLTestFile): TestInfo {
157+
private static createTestInfo(testFile: FileTreeLeaf): TestInfo {
158158
return {
159159
type: "test",
160160
id: testFile.path,
@@ -165,7 +165,7 @@ export class QLTestAdapter extends DisposableObject implements TestAdapter {
165165
}
166166

167167
private static createTestSuiteInfo(
168-
testDirectory: QLTestDirectory,
168+
testDirectory: FileTreeDirectory,
169169
label: string,
170170
): TestSuiteInfo {
171171
return {

extensions/ql-vscode/src/query-testing/test-manager.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,19 @@ import {
1616
workspace,
1717
} from "vscode";
1818
import { DisposableObject } from "../pure/disposable-object";
19-
import {
20-
QLTestDirectory,
21-
QLTestDiscovery,
22-
QLTestFile,
23-
QLTestNode,
24-
} from "./qltest-discovery";
19+
import { QLTestDiscovery } from "./qltest-discovery";
2520
import { CodeQLCliServer } from "../codeql-cli/cli";
2621
import { getErrorMessage } from "../pure/helpers-pure";
2722
import { BaseLogger, LogOptions } from "../common";
2823
import { TestRunner } from "./test-runner";
2924
import { TestManagerBase } from "./test-manager-base";
3025
import { App } from "../common/app";
3126
import { isWorkspaceFolderOnDisk } from "../helpers";
27+
import {
28+
FileTreeDirectory,
29+
FileTreeLeaf,
30+
FileTreeNode,
31+
} from "../common/file-tree-nodes";
3232

3333
/**
3434
* Returns the complete text content of the specified file. If there is an error reading the file,
@@ -209,7 +209,7 @@ export class TestManager extends TestManagerBase {
209209
*/
210210
public updateTestsForWorkspaceFolder(
211211
workspaceFolder: WorkspaceFolder,
212-
testDirectory: QLTestDirectory | undefined,
212+
testDirectory: FileTreeDirectory | undefined,
213213
): void {
214214
if (testDirectory !== undefined) {
215215
// Adding an item with the same ID as an existing item will replace it, which is exactly what
@@ -229,9 +229,9 @@ export class TestManager extends TestManagerBase {
229229
/**
230230
* Creates a tree of `TestItem`s from the root `QlTestNode` provided by test discovery.
231231
*/
232-
private createTestItemTree(node: QLTestNode, isRoot: boolean): TestItem {
232+
private createTestItemTree(node: FileTreeNode, isRoot: boolean): TestItem {
233233
// Prefix the ID to identify it as a directory or a test
234-
const itemType = node instanceof QLTestDirectory ? "dir" : "test";
234+
const itemType = node instanceof FileTreeDirectory ? "dir" : "test";
235235
const testItem = this.testController.createTestItem(
236236
// For the root of a workspace folder, use the full path as the ID. Otherwise, use the node's
237237
// name as the ID, since it's shorter but still unique.
@@ -242,7 +242,7 @@ export class TestManager extends TestManagerBase {
242242

243243
for (const childNode of node.children) {
244244
const childItem = this.createTestItemTree(childNode, false);
245-
if (childNode instanceof QLTestFile) {
245+
if (childNode instanceof FileTreeLeaf) {
246246
childItem.range = new Range(0, 0, 0, 0);
247247
}
248248
testItem.children.add(childItem);

0 commit comments

Comments
 (0)