Skip to content

Commit 02faff0

Browse files
authored
Add tests for Test Discovery (#921)
Add tests that ensure TestClasses returned from the LSP (or elsewhere) are properly added to the vscode TestController.
1 parent 3843201 commit 02faff0

File tree

3 files changed

+194
-13
lines changed

3 files changed

+194
-13
lines changed

src/TestExplorer/TestDiscovery.ts

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@
1313
//===----------------------------------------------------------------------===//
1414

1515
import * as vscode from "vscode";
16-
import { FolderContext } from "../FolderContext";
17-
import { TargetType } from "../SwiftPackage";
16+
import { SwiftPackage, TargetType } from "../SwiftPackage";
1817
import { LSPTestItem } from "../sourcekit-lsp/lspExtensions";
1918
import { reduceTestItemChildren } from "./TestUtils";
2019

@@ -24,26 +23,30 @@ export interface TestClass extends Omit<Omit<LSPTestItem, "location">, "children
2423
children: TestClass[];
2524
}
2625

26+
/**
27+
* Tag that denotes TestItems should be runnable in the VS Code UI.
28+
* Test items that do not have this tag will not have the green "run test" triangle button.
29+
*/
2730
export const runnableTag = new vscode.TestTag("runnable");
2831

2932
/**
3033
* Update Test Controller TestItems based off array of TestClasses.
3134
*
3235
* The function creates the TestTargets based off the test targets in the Swift
3336
* Package
34-
* @param folderContext Folder test classes came
37+
* @param testController Test controller
38+
* @param swiftPackage A swift package containing test targets
3539
* @param testClasses Array of test classes
3640
*/
37-
export function updateTestsFromClasses(folderContext: FolderContext, testItems: TestClass[]) {
38-
const testExplorer = folderContext.testExplorer;
39-
if (!testExplorer) {
40-
return;
41-
}
42-
const targets = folderContext.swiftPackage.getTargets(TargetType.test).map(target => {
41+
export function updateTestsFromClasses(
42+
testController: vscode.TestController,
43+
swiftPackage: SwiftPackage,
44+
testItems: TestClass[]
45+
) {
46+
const targets = swiftPackage.getTargets(TargetType.test).map(target => {
4347
const filteredItems = testItems.filter(
4448
testItem =>
45-
testItem.location &&
46-
folderContext.swiftPackage.getTarget(testItem.location.uri.fsPath) === target
49+
testItem.location && swiftPackage.getTarget(testItem.location.uri.fsPath) === target
4750
);
4851
return {
4952
id: target.c99name,
@@ -55,7 +58,7 @@ export function updateTestsFromClasses(folderContext: FolderContext, testItems:
5558
tags: [],
5659
} as TestClass;
5760
});
58-
updateTests(testExplorer.controller, targets);
61+
updateTests(testController, targets);
5962
}
6063

6164
/**

src/TestExplorer/TestExplorer.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,11 @@ export class TestExplorer {
326326
const tests = await this.lspTestDiscovery.getWorkspaceTests(
327327
this.folderContext.swiftPackage
328328
);
329-
TestDiscovery.updateTestsFromClasses(this.folderContext, tests);
329+
TestDiscovery.updateTestsFromClasses(
330+
this.controller,
331+
this.folderContext.swiftPackage,
332+
tests
333+
);
330334
this.onTestItemsDidChangeEmitter.fire(this.controller);
331335
}
332336

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the VS Code Swift open source project
4+
//
5+
// Copyright (c) 2021-2024 the VS Code Swift project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of VS Code Swift project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
import * as assert from "assert";
16+
import * as vscode from "vscode";
17+
import { beforeEach } from "mocha";
18+
import {
19+
TestClass,
20+
updateTests,
21+
updateTestsFromClasses,
22+
} from "../../../src/TestExplorer/TestDiscovery";
23+
import { reduceTestItemChildren } from "../../../src/TestExplorer/TestUtils";
24+
import { SwiftPackage, Target, TargetType } from "../../../src/SwiftPackage";
25+
import { SwiftToolchain } from "../../../src/toolchain/toolchain";
26+
27+
suite("TestDiscovery Suite", () => {
28+
let testController: vscode.TestController;
29+
let testRunCtr = 0;
30+
31+
interface SimplifiedTestItem {
32+
id: string;
33+
children: SimplifiedTestItem[];
34+
}
35+
36+
function testControllerChildren(collection: vscode.TestItemCollection): SimplifiedTestItem[] {
37+
return reduceTestItemChildren(
38+
collection,
39+
(acc, item) => [
40+
...acc,
41+
{ id: item.id, children: testControllerChildren(item.children) },
42+
],
43+
[] as SimplifiedTestItem[]
44+
);
45+
}
46+
47+
function testItem(id: string): TestClass {
48+
return {
49+
id,
50+
label: id,
51+
disabled: false,
52+
style: "XCTest",
53+
location: undefined,
54+
tags: [],
55+
children: [],
56+
};
57+
}
58+
59+
beforeEach(() => {
60+
const id = `TestDiscovery Suite Test Controller ${testRunCtr}`;
61+
testController = vscode.tests.createTestController(id, id);
62+
testRunCtr += 1;
63+
});
64+
65+
test("updates tests with empty collection", () => {
66+
updateTests(testController, []);
67+
assert.equal(testController.items.size, 0);
68+
});
69+
70+
test("removes test item not included in the new list", () => {
71+
const foo = testController.createTestItem("foo", "foo");
72+
testController.items.add(foo);
73+
const bar = testController.createTestItem("bar", "bar");
74+
testController.items.add(bar);
75+
76+
// `foo` is no longer in the list of new children
77+
updateTests(testController, [testItem("bar")]);
78+
79+
assert.deepStrictEqual(testControllerChildren(testController.items), [
80+
{ id: "bar", children: [] },
81+
]);
82+
});
83+
84+
test("removes parameterized test result children", () => {
85+
const fileUri = vscode.Uri.file("file:///some/file.swift");
86+
const parent = testController.createTestItem("parent", "parent", fileUri);
87+
testController.items.add(parent);
88+
89+
// Simulates a parameterized test result child as its a child with no URI.
90+
const child = testController.createTestItem("child", "child");
91+
parent.children.add(child);
92+
93+
updateTests(testController, [], fileUri);
94+
95+
assert.deepStrictEqual(testControllerChildren(testController.items), []);
96+
});
97+
98+
test("merges test item children", () => {
99+
const foo = testController.createTestItem("foo", "foo");
100+
const baz = testController.createTestItem("baz", "baz");
101+
foo.children.add(baz);
102+
testController.items.add(foo);
103+
104+
const newFoo = testItem("foo");
105+
newFoo.children = [testItem("baz"), testItem("bar")];
106+
107+
updateTests(testController, [newFoo]);
108+
109+
assert.deepStrictEqual(testControllerChildren(testController.items), [
110+
{
111+
id: "foo",
112+
children: [
113+
{ id: "baz", children: [] },
114+
{ id: "bar", children: [] },
115+
],
116+
},
117+
]);
118+
});
119+
120+
test("handles moving a test from one file to another", () => {
121+
const startUri = vscode.Uri.file("file:///some/file.swift");
122+
const test = testController.createTestItem("foo", "foo", startUri);
123+
const bar = testController.createTestItem("bar", "bar");
124+
test.children.add(bar);
125+
testController.items.add(test);
126+
127+
const newLocation = new vscode.Location(
128+
vscode.Uri.file("file:///another/file.swift"),
129+
new vscode.Range(new vscode.Position(1, 0), new vscode.Position(2, 0))
130+
);
131+
132+
const newBar = testItem("bar");
133+
newBar.location = newLocation;
134+
135+
const newFoo = testItem("foo");
136+
newFoo.label = "New Label";
137+
newFoo.location = newLocation;
138+
newFoo.children = [newBar];
139+
140+
updateTests(testController, [newFoo]);
141+
142+
assert.deepStrictEqual(testControllerChildren(testController.items), [
143+
{ id: "foo", children: [{ id: "bar", children: [] }] },
144+
]);
145+
assert.deepStrictEqual(testController.items.get("foo")?.uri, newLocation.uri);
146+
assert.deepStrictEqual(testController.items.get("foo")?.label, "New Label");
147+
});
148+
149+
test("updates tests from classes within a swift package", async () => {
150+
const file = vscode.Uri.file("file:///some/file.swift");
151+
const swiftPackage = await SwiftPackage.create(file, await SwiftToolchain.create());
152+
const testTargetName = "TestTarget";
153+
const target: Target = {
154+
c99name: testTargetName,
155+
name: testTargetName,
156+
path: file.fsPath,
157+
type: TargetType.test,
158+
sources: [],
159+
};
160+
swiftPackage.getTargets = () => [target];
161+
swiftPackage.getTarget = () => target;
162+
163+
const item = testItem("bar");
164+
item.location = new vscode.Location(
165+
vscode.Uri.file("file:///another/file.swift"),
166+
new vscode.Range(new vscode.Position(1, 0), new vscode.Position(2, 0))
167+
);
168+
updateTestsFromClasses(testController, swiftPackage, [item]);
169+
170+
assert.deepStrictEqual(testControllerChildren(testController.items), [
171+
{ id: "TestTarget", children: [{ id: "bar", children: [] }] },
172+
]);
173+
});
174+
});

0 commit comments

Comments
 (0)