Skip to content

Commit d4dd446

Browse files
committed
Capture minimal number of test arguments
1 parent 9ff6052 commit d4dd446

File tree

3 files changed

+181
-103
lines changed

3 files changed

+181
-103
lines changed

Diff for: src/TestExplorer/TestRunArguments.ts

+153
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the VSCode Swift open source project
4+
//
5+
// Copyright (c) 2021-2024 the VSCode 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 VSCode Swift project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
import * as vscode from "vscode";
16+
17+
type ProcessResult = {
18+
testItems: vscode.TestItem[];
19+
xcTestArgs: string[];
20+
swiftTestArgs: string[];
21+
};
22+
23+
/**
24+
* Given a `TestRunRequest`, produces the lists of
25+
* XCTests and swift-testing tests to run.
26+
*/
27+
export class TestRunArguments {
28+
public testItems: vscode.TestItem[];
29+
public xcTestArgs: string[];
30+
public swiftTestArgs: string[];
31+
32+
constructor(request: vscode.TestRunRequest) {
33+
const { testItems, xcTestArgs, swiftTestArgs } = this.createTestLists(request);
34+
this.testItems = testItems;
35+
this.xcTestArgs = xcTestArgs;
36+
this.swiftTestArgs = swiftTestArgs;
37+
}
38+
39+
public get hasXCTests(): boolean {
40+
return this.xcTestArgs.length > 0;
41+
}
42+
43+
public get hasSwiftTestingTests(): boolean {
44+
return this.swiftTestArgs.length > 0;
45+
}
46+
47+
/**
48+
* Construct test item list from TestRequest
49+
* @returns list of test items to run and list of test for XCTest arguments
50+
*/
51+
private createTestLists(request: vscode.TestRunRequest): ProcessResult {
52+
const includes = request.include ?? [];
53+
return includes.reduce(this.createTestItemReducer(request.include, request.exclude), {
54+
testItems: [],
55+
xcTestArgs: [],
56+
swiftTestArgs: [],
57+
});
58+
}
59+
60+
private createTestItemReducer(
61+
include: readonly vscode.TestItem[] | undefined,
62+
exclude: readonly vscode.TestItem[] | undefined
63+
): (previousValue: ProcessResult, testItem: vscode.TestItem) => ProcessResult {
64+
return (previousValue, testItem) => {
65+
const { testItems, swiftTestArgs, xcTestArgs } = this.processTestItem(
66+
testItem,
67+
include,
68+
exclude
69+
);
70+
71+
// If no children were added we can skip adding this parent.
72+
if (xcTestArgs.length + swiftTestArgs.length === 0) {
73+
return previousValue;
74+
} else if (xcTestArgs.length + swiftTestArgs.length === testItem.children.size) {
75+
// If we've added all the children to the list of arguments, just add
76+
// the parent instead of each individual child. This crafts a minimal set
77+
// of test/suites that run all the test cases requested with the smallest list
78+
// of arguments.
79+
const isXCTest = !!testItem.tags.find(tag => tag.id === "XCTest");
80+
return {
81+
testItems: [...previousValue.testItems, ...testItems],
82+
swiftTestArgs: [
83+
...previousValue.swiftTestArgs,
84+
...(!isXCTest ? [testItem.id] : []),
85+
],
86+
xcTestArgs: [...previousValue.xcTestArgs, ...(isXCTest ? [testItem.id] : [])],
87+
};
88+
} else {
89+
// If we've only added some of the children the append to our test list
90+
return {
91+
testItems: [...previousValue.testItems, ...testItems],
92+
swiftTestArgs: [...previousValue.swiftTestArgs, ...swiftTestArgs],
93+
xcTestArgs: [...previousValue.xcTestArgs, ...xcTestArgs],
94+
};
95+
}
96+
};
97+
}
98+
99+
private processTestItem(
100+
testItem: vscode.TestItem,
101+
include?: readonly vscode.TestItem[],
102+
exclude?: readonly vscode.TestItem[]
103+
): ProcessResult {
104+
// Skip tests the user asked to exclude
105+
if (exclude?.includes(testItem)) {
106+
return {
107+
testItems: [],
108+
xcTestArgs: [],
109+
swiftTestArgs: [],
110+
};
111+
}
112+
113+
const testItems: vscode.TestItem[] = [];
114+
const xcTestArgs: string[] = [];
115+
const swiftTestArgs: string[] = [];
116+
117+
// If this test item is included or we are including everything
118+
if (include?.includes(testItem) || !include) {
119+
testItems.push(testItem);
120+
121+
// Only add leaf items to testArgs
122+
if (testItem.children.size === 0) {
123+
if (testItem.tags.find(tag => tag.id === "XCTest")) {
124+
xcTestArgs.push(testItem.id);
125+
} else {
126+
swiftTestArgs.push(testItem.id);
127+
}
128+
}
129+
}
130+
131+
return this.reduceTestItemChildren(
132+
testItem.children,
133+
this.createTestItemReducer(undefined, exclude),
134+
{
135+
testItems,
136+
xcTestArgs,
137+
swiftTestArgs,
138+
}
139+
);
140+
}
141+
142+
private reduceTestItemChildren<U>(
143+
array: vscode.TestItemCollection,
144+
callback: (accumulator: U, currentValue: vscode.TestItem) => U,
145+
initialValue: U
146+
): U {
147+
let accumulator = initialValue;
148+
array.forEach(currentValue => {
149+
accumulator = callback(accumulator, currentValue);
150+
});
151+
return accumulator;
152+
}
153+
}

Diff for: src/TestExplorer/TestRunner.ts

+1-97
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import { LoggingDebugAdapterTracker } from "../debugger/logTracker";
4545
import { TaskOperation } from "../TaskQueue";
4646
import { TestXUnitParser, iXUnitTestState } from "./TestXUnitParser";
4747
import { ITestRunState } from "./TestParsers/TestRunState";
48+
import { TestRunArguments } from "./TestRunArguments";
4849

4950
/** Workspace Folder events */
5051
export enum TestKind {
@@ -56,103 +57,6 @@ export enum TestKind {
5657
coverage = "coverage",
5758
}
5859

59-
/**
60-
* Given a `TestRunRequest`, produces the lists of
61-
* XCTests and swift-testing tests to run.
62-
*/
63-
export class TestRunArguments {
64-
public testItems: vscode.TestItem[];
65-
public xcTestArgs: string[];
66-
public swiftTestArgs: string[];
67-
68-
constructor(request: vscode.TestRunRequest) {
69-
const { testItems, xcTestArgs, swiftTestArgs } = this.createTestLists(request);
70-
this.testItems = testItems;
71-
this.xcTestArgs = xcTestArgs;
72-
this.swiftTestArgs = swiftTestArgs;
73-
}
74-
75-
get hasXCTests(): boolean {
76-
return this.xcTestArgs.length > 0;
77-
}
78-
79-
get hasSwiftTestingTests(): boolean {
80-
return this.swiftTestArgs.length > 0;
81-
}
82-
83-
/**
84-
* Construct test item list from TestRequest
85-
* @returns list of test items to run and list of test for XCTest arguments
86-
*/
87-
private createTestLists(request: vscode.TestRunRequest): {
88-
testItems: vscode.TestItem[];
89-
xcTestArgs: string[];
90-
swiftTestArgs: string[];
91-
} {
92-
const testItems: vscode.TestItem[] = [];
93-
const xcTestArgs: string[] = [];
94-
const swiftTestArgs: string[] = [];
95-
96-
// Start processing from root items
97-
request.include?.forEach(item =>
98-
this.processTestItem(
99-
item,
100-
testItems,
101-
xcTestArgs,
102-
swiftTestArgs,
103-
request.include,
104-
request.exclude
105-
)
106-
);
107-
108-
return { testItems, xcTestArgs, swiftTestArgs };
109-
}
110-
111-
private processTestItem(
112-
testItem: vscode.TestItem,
113-
collection: vscode.TestItem[],
114-
xcTestArgs: string[],
115-
swiftTestArgs: string[],
116-
include?: readonly vscode.TestItem[],
117-
exclude?: readonly vscode.TestItem[]
118-
) {
119-
// Skip tests the user asked to exclude
120-
if (exclude?.includes(testItem)) {
121-
return;
122-
}
123-
124-
// If this test item is included or we are including everything
125-
if (include?.includes(testItem) || !include) {
126-
collection.push(testItem);
127-
128-
// Only add leaf items to testArgs
129-
if (testItem.children.size === 0) {
130-
if (testItem.tags.find(tag => tag.id === "XCTest")) {
131-
xcTestArgs.push(testItem.id);
132-
} else {
133-
swiftTestArgs.push(testItem.id);
134-
}
135-
}
136-
}
137-
138-
const lengthBefore = xcTestArgs.length + swiftTestArgs.length;
139-
140-
// Recursively process children
141-
testItem.children.forEach(child =>
142-
this.processTestItem(child, collection, xcTestArgs, swiftTestArgs, undefined, exclude)
143-
);
144-
145-
// If all of a parent's children were excluded then don't add
146-
// the parent to the testItem collection.
147-
if (
148-
testItem.children.size > 0 &&
149-
lengthBefore === xcTestArgs.length + swiftTestArgs.length
150-
) {
151-
collection.splice(collection.indexOf(testItem), 1);
152-
}
153-
}
154-
}
155-
15660
/** Class used to run tests */
15761
export class TestRunner {
15862
private testRun: vscode.TestRun;

Diff for: test/suite/testexplorer/TestRunArguments.test.ts

+27-6
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515
import * as vscode from "vscode";
1616
import * as assert from "assert";
1717
import { beforeEach } from "mocha";
18-
import { TestRunArguments } from "../../../src/TestExplorer/TestRunner";
18+
import { TestRunArguments } from "../../../src/TestExplorer/TestRunArguments";
1919

20-
suite("TestRunArguments Suite", () => {
20+
suite.only("TestRunArguments Suite", () => {
2121
let controller: vscode.TestController;
2222
let xcSuite: vscode.TestItem;
2323
let xcTest: vscode.TestItem;
@@ -58,8 +58,8 @@ suite("TestRunArguments Suite", () => {
5858
);
5959
assert.equal(testArgs.hasXCTests, true);
6060
assert.equal(testArgs.hasSwiftTestingTests, true);
61-
assert.deepEqual(testArgs.xcTestArgs, [xcTest.id]);
62-
assert.deepEqual(testArgs.swiftTestArgs, [swiftTest.id]);
61+
assert.deepEqual(testArgs.xcTestArgs, [xcSuite.id]);
62+
assert.deepEqual(testArgs.swiftTestArgs, [swiftTestSuite.id]);
6363
assert.deepEqual(
6464
testArgs.testItems.map(item => item.id),
6565
[xcSuite.id, xcTest.id, swiftTestSuite.id, swiftTest.id]
@@ -73,7 +73,7 @@ suite("TestRunArguments Suite", () => {
7373
assert.equal(testArgs.hasXCTests, false);
7474
assert.equal(testArgs.hasSwiftTestingTests, true);
7575
assert.deepEqual(testArgs.xcTestArgs, []);
76-
assert.deepEqual(testArgs.swiftTestArgs, [swiftTest.id]);
76+
assert.deepEqual(testArgs.swiftTestArgs, [swiftTestSuite.id]);
7777
assert.deepEqual(
7878
testArgs.testItems.map(item => item.id),
7979
[swiftTestSuite.id, swiftTest.id]
@@ -87,10 +87,31 @@ suite("TestRunArguments Suite", () => {
8787
assert.equal(testArgs.hasXCTests, false);
8888
assert.equal(testArgs.hasSwiftTestingTests, true);
8989
assert.deepEqual(testArgs.xcTestArgs, []);
90-
assert.deepEqual(testArgs.swiftTestArgs, [swiftTest.id]);
90+
assert.deepEqual(testArgs.swiftTestArgs, [swiftTestSuite.id]);
9191
assert.deepEqual(
9292
testArgs.testItems.map(item => item.id),
9393
[swiftTestSuite.id, swiftTest.id]
9494
);
9595
});
96+
97+
test("Single Test in Suite With Multiple", () => {
98+
const anotherSwiftTest = controller.createTestItem(
99+
"Another Swift Test Item",
100+
"Another Swift Test Item"
101+
);
102+
anotherSwiftTest.tags = [{ id: "swift-testing" }];
103+
swiftTestSuite.children.add(anotherSwiftTest);
104+
105+
const testArgs = new TestRunArguments(
106+
new vscode.TestRunRequest([anotherSwiftTest], [], undefined)
107+
);
108+
assert.equal(testArgs.hasXCTests, false);
109+
assert.equal(testArgs.hasSwiftTestingTests, true);
110+
assert.deepEqual(testArgs.xcTestArgs, []);
111+
assert.deepEqual(testArgs.swiftTestArgs, [anotherSwiftTest.id]);
112+
assert.deepEqual(
113+
testArgs.testItems.map(item => item.id),
114+
[anotherSwiftTest.id]
115+
);
116+
});
96117
});

0 commit comments

Comments
 (0)