Skip to content

Commit a549af0

Browse files
committed
Run failed tests when using unit tests (#4533)
1 parent 42f27be commit a549af0

File tree

3 files changed

+208
-9
lines changed

3 files changed

+208
-9
lines changed

src/client/unittests/common/managers/baseTestManager.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export abstract class BaseTestManager implements ITestManager {
6060
private _testResultsService: ITestResultsService;
6161
private workspaceService: IWorkspaceService;
6262
private _outputChannel: OutputChannel;
63-
private tests?: Tests;
63+
protected tests?: Tests;
6464
private _status: TestStatus = TestStatus.Unknown;
6565
private testDiscoveryCancellationTokenSource?: CancellationTokenSource;
6666
private testRunnerCancellationTokenSource?: CancellationTokenSource;

src/client/unittests/unittest/main.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Uri } from 'vscode';
22
import { Product } from '../../common/types';
33
import { noop } from '../../common/utils/misc';
44
import { IServiceContainer } from '../../ioc/types';
5-
import { UNITTEST_PROVIDER } from '../common/constants';
5+
import { CommandSource, UNITTEST_PROVIDER } from '../common/constants';
66
import { BaseTestManager } from '../common/managers/baseTestManager';
77
import { ITestsHelper, TestDiscoveryOptions, TestRunOptions, Tests, TestStatus, TestsToRun } from '../common/types';
88
import { IArgumentsService, ITestManagerRunner, TestFilter } from '../types';
@@ -32,7 +32,16 @@ export class TestManager extends BaseTestManager {
3232
outChannel: this.outputChannel
3333
};
3434
}
35-
public async runTestImpl(tests: Tests, testsToRun?: TestsToRun, runFailedTests?: boolean, debug?: boolean): Promise<Tests> {
35+
public async runTest(cmdSource: CommandSource, testsToRun?: TestsToRun, runFailedTests?: boolean, debug?: boolean): Promise<Tests> {
36+
if (runFailedTests === true && this.tests) {
37+
testsToRun = { testFile: [], testFolder: [], testSuite: [], testFunction: [] };
38+
testsToRun.testFunction = this.tests.testFunctions.filter(fn => {
39+
return fn.testFunction.status === TestStatus.Error || fn.testFunction.status === TestStatus.Fail;
40+
}).map(fn => fn.testFunction);
41+
}
42+
return super.runTest(cmdSource, testsToRun, runFailedTests, debug);
43+
}
44+
public async runTestImpl(tests: Tests, testsToRun?: TestsToRun, _runFailedTests?: boolean, debug?: boolean): Promise<Tests> {
3645
let args: string[];
3746

3847
const runAllTests = this.helper.shouldRunAllTests(testsToRun);
@@ -42,12 +51,6 @@ export class TestManager extends BaseTestManager {
4251
args = this.argsService.filterArguments(this.settings.unitTest.unittestArgs, runAllTests ? TestFilter.runAll : TestFilter.runSpecific);
4352
}
4453

45-
if (runFailedTests === true) {
46-
testsToRun = { testFile: [], testFolder: [], testSuite: [], testFunction: [] };
47-
testsToRun.testFunction = tests.testFunctions.filter(fn => {
48-
return fn.testFunction.status === TestStatus.Error || fn.testFunction.status === TestStatus.Fail;
49-
}).map(fn => fn.testFunction);
50-
}
5154
const options: TestRunOptions = {
5255
workspaceFolder: this.workspaceFolder,
5356
cwd: this.rootDirectory,
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
'use strict';
5+
6+
import * as assert from 'assert';
7+
import { anything, capture, instance, mock, when } from 'ts-mockito';
8+
import { Uri } from 'vscode';
9+
import { IWorkspaceService } from '../../../client/common/application/types';
10+
import { WorkspaceService } from '../../../client/common/application/workspace';
11+
import { ConfigurationService } from '../../../client/common/configuration/service';
12+
import { IConfigurationService, IDisposableRegistry, IOutputChannel, IPythonSettings } from '../../../client/common/types';
13+
import { ServiceContainer } from '../../../client/ioc/container';
14+
import { IServiceContainer } from '../../../client/ioc/types';
15+
import { ArgumentsHelper } from '../../../client/unittests/common/argumentsHelper';
16+
import { CommandSource } from '../../../client/unittests/common/constants';
17+
import { TestCollectionStorageService } from '../../../client/unittests/common/services/storageService';
18+
import { TestResultsService } from '../../../client/unittests/common/services/testResultsService';
19+
import { TestsStatusUpdaterService } from '../../../client/unittests/common/services/testsStatusService';
20+
import { TestsHelper } from '../../../client/unittests/common/testUtils';
21+
import { TestResultResetVisitor } from '../../../client/unittests/common/testVisitors/resultResetVisitor';
22+
import { FlattenedTestFunction, FlattenedTestSuite, ITestResultsService, ITestsHelper, ITestsStatusUpdaterService, TestFile, TestFolder, TestFunction, Tests, TestStatus, TestSuite, TestType } from '../../../client/unittests/common/types';
23+
import { IArgumentsHelper, IArgumentsService, ITestManagerRunner } from '../../../client/unittests/types';
24+
import { TestManager } from '../../../client/unittests/unittest/main';
25+
import { TestManagerRunner } from '../../../client/unittests/unittest/runner';
26+
import { ArgumentsService } from '../../../client/unittests/unittest/services/argsService';
27+
import { MockOutputChannel } from '../../mockClasses';
28+
import { createMockTestDataItem } from '../common/testUtils.unit.test';
29+
30+
// tslint:disable:max-func-body-length no-any
31+
suite('xUnit Tests - unittest - run failed tests', () => {
32+
let testManager: TestManager;
33+
const workspaceFolder = Uri.file(__dirname);
34+
let serviceContainer: IServiceContainer;
35+
let argsHelper: IArgumentsHelper;
36+
let testsHelper: ITestsHelper;
37+
let testManagerRunner: ITestManagerRunner;
38+
let tests: Tests;
39+
function createTestData() {
40+
41+
const folder1 = createMockTestDataItem<TestFolder>(TestType.testFolder);
42+
const folder2 = createMockTestDataItem<TestFolder>(TestType.testFolder);
43+
const folder3 = createMockTestDataItem<TestFolder>(TestType.testFolder);
44+
const folder4 = createMockTestDataItem<TestFolder>(TestType.testFolder);
45+
const folder5 = createMockTestDataItem<TestFolder>(TestType.testFolder);
46+
folder1.folders.push(folder2);
47+
folder1.folders.push(folder3);
48+
folder2.folders.push(folder4);
49+
folder3.folders.push(folder5);
50+
51+
const file1 = createMockTestDataItem<TestFile>(TestType.testFile);
52+
const file2 = createMockTestDataItem<TestFile>(TestType.testFile);
53+
const file3 = createMockTestDataItem<TestFile>(TestType.testFile);
54+
const file4 = createMockTestDataItem<TestFile>(TestType.testFile);
55+
folder1.testFiles.push(file1);
56+
folder3.testFiles.push(file2);
57+
folder3.testFiles.push(file3);
58+
folder5.testFiles.push(file4);
59+
60+
const suite1 = createMockTestDataItem<TestSuite>(TestType.testSuite);
61+
const suite2 = createMockTestDataItem<TestSuite>(TestType.testSuite);
62+
const suite3 = createMockTestDataItem<TestSuite>(TestType.testSuite);
63+
const suite4 = createMockTestDataItem<TestSuite>(TestType.testSuite);
64+
const suite5 = createMockTestDataItem<TestSuite>(TestType.testSuite);
65+
const fn1 = createMockTestDataItem<TestFunction>(TestType.testFunction);
66+
const fn2 = createMockTestDataItem<TestFunction>(TestType.testFunction);
67+
const fn3 = createMockTestDataItem<TestFunction>(TestType.testFunction);
68+
const fn4 = createMockTestDataItem<TestFunction>(TestType.testFunction);
69+
const fn5 = createMockTestDataItem<TestFunction>(TestType.testFunction);
70+
file1.suites.push(suite1);
71+
file1.suites.push(suite2);
72+
file3.suites.push(suite3);
73+
suite3.suites.push(suite4);
74+
suite4.suites.push(suite5);
75+
file1.functions.push(fn1);
76+
file1.functions.push(fn2);
77+
suite1.functions.push(fn3);
78+
suite1.functions.push(fn4);
79+
suite3.functions.push(fn5);
80+
const flattendSuite1: FlattenedTestSuite = {
81+
testSuite: suite1,
82+
xmlClassName: suite1.xmlName
83+
} as any;
84+
const flattendSuite2: FlattenedTestSuite = {
85+
testSuite: suite2,
86+
xmlClassName: suite2.xmlName
87+
} as any;
88+
const flattendSuite3: FlattenedTestSuite = {
89+
testSuite: suite3,
90+
xmlClassName: suite3.xmlName
91+
} as any;
92+
const flattendSuite4: FlattenedTestSuite = {
93+
testSuite: suite4,
94+
xmlClassName: suite4.xmlName
95+
} as any;
96+
const flattendSuite5: FlattenedTestSuite = {
97+
testSuite: suite5,
98+
xmlClassName: suite5.xmlName
99+
} as any;
100+
const flattendFn1: FlattenedTestFunction = {
101+
testFunction: fn1,
102+
xmlClassName: fn1.name
103+
} as any;
104+
const flattendFn2: FlattenedTestFunction = {
105+
testFunction: fn2,
106+
xmlClassName: fn2.name
107+
} as any;
108+
const flattendFn3: FlattenedTestFunction = {
109+
testFunction: fn3,
110+
xmlClassName: fn3.name
111+
} as any;
112+
const flattendFn4: FlattenedTestFunction = {
113+
testFunction: fn4,
114+
xmlClassName: fn4.name
115+
} as any;
116+
const flattendFn5: FlattenedTestFunction = {
117+
testFunction: fn5,
118+
xmlClassName: fn5.name
119+
} as any;
120+
tests = {
121+
rootTestFolders: [folder1],
122+
summary: { errors: 0, skipped: 0, passed: 0, failures: 0 },
123+
testFiles: [file1, file2, file3, file4],
124+
testFolders: [folder1, folder2, folder3, folder4, folder5],
125+
testFunctions: [flattendFn1, flattendFn2, flattendFn3, flattendFn4, flattendFn5],
126+
testSuites: [flattendSuite1, flattendSuite2, flattendSuite3, flattendSuite4, flattendSuite5]
127+
};
128+
}
129+
setup(() => {
130+
createTestData();
131+
serviceContainer = mock(ServiceContainer);
132+
testsHelper = mock(TestsHelper);
133+
argsHelper = mock(ArgumentsHelper);
134+
testManagerRunner = mock(TestManagerRunner);
135+
const testStorage = mock(TestCollectionStorageService);
136+
const workspaceService = mock(WorkspaceService);
137+
const svcInstance = instance(serviceContainer);
138+
when(testStorage.getTests(anything())).thenReturn(tests);
139+
when(workspaceService.getWorkspaceFolder(anything())).thenReturn({ name: '', index: 0, uri: workspaceFolder });
140+
when(serviceContainer.get<IWorkspaceService>(IWorkspaceService)).thenReturn(instance(workspaceService));
141+
when(serviceContainer.get<IArgumentsHelper>(IArgumentsHelper)).thenReturn(new ArgumentsHelper(svcInstance));
142+
when(serviceContainer.get<IArgumentsService>(IArgumentsService, anything())).thenReturn(new ArgumentsService(svcInstance));
143+
when(serviceContainer.get<ITestsHelper>(ITestsHelper)).thenReturn(instance(testsHelper));
144+
when(serviceContainer.get<ITestManagerRunner>(ITestManagerRunner, anything())).thenReturn(instance(testManagerRunner));
145+
when(serviceContainer.get<ITestsStatusUpdaterService>(ITestsStatusUpdaterService)).thenReturn(new TestsStatusUpdaterService(instance(testStorage)));
146+
when(serviceContainer.get<ITestResultsService>(ITestResultsService)).thenReturn(new TestResultsService(new TestResultResetVisitor()));
147+
when(serviceContainer.get<IOutputChannel>(IOutputChannel)).thenReturn(instance(mock(MockOutputChannel)));
148+
when(serviceContainer.get<IOutputChannel>(IOutputChannel)).thenReturn(instance(mock(MockOutputChannel)));
149+
when(serviceContainer.get<IDisposableRegistry>(IDisposableRegistry)).thenReturn([]);
150+
const settingsService = mock(ConfigurationService);
151+
const settings: IPythonSettings = {
152+
unitTest: {
153+
unittestArgs: []
154+
}
155+
} as any;
156+
when(settingsService.getSettings(anything())).thenReturn(settings);
157+
when(serviceContainer.get<IConfigurationService>(IConfigurationService)).thenReturn(instance(settingsService));
158+
159+
testManager = new TestManager(workspaceFolder, workspaceFolder.fsPath, svcInstance);
160+
});
161+
162+
test('Run Failed tests', async () => {
163+
testManager.discoverTests = () => Promise.resolve(tests);
164+
when(testsHelper.shouldRunAllTests(anything())).thenReturn(false);
165+
when(testManagerRunner.runTest(anything(), anything(), anything())).thenResolve(undefined as any);
166+
(testManager as any).tests = tests;
167+
tests.testFunctions[0].testFunction.status = TestStatus.Fail;
168+
tests.testFunctions[2].testFunction.status = TestStatus.Fail;
169+
170+
await testManager.runTest(CommandSource.testExplorer, undefined, true);
171+
172+
const options = capture(testManagerRunner.runTest).last()[1];
173+
assert.deepEqual(options.tests, tests);
174+
assert.equal(options.testsToRun!.testFile!.length, 0);
175+
assert.equal(options.testsToRun!.testFolder!.length, 0);
176+
assert.equal(options.testsToRun!.testSuite!.length, 0);
177+
assert.equal(options.testsToRun!.testFunction!.length, 2);
178+
assert.deepEqual(options.testsToRun!.testFunction![0], tests.testFunctions[0].testFunction);
179+
assert.deepEqual(options.testsToRun!.testFunction![1], tests.testFunctions[2].testFunction);
180+
});
181+
test('Run All tests', async () => {
182+
testManager.discoverTests = () => Promise.resolve(tests);
183+
when(testsHelper.shouldRunAllTests(anything())).thenReturn(false);
184+
when(testManagerRunner.runTest(anything(), anything(), anything())).thenResolve(undefined as any);
185+
(testManager as any).tests = tests;
186+
187+
await testManager.runTest(CommandSource.testExplorer, undefined, true);
188+
189+
const options = capture(testManagerRunner.runTest).last()[1];
190+
assert.deepEqual(options.tests, tests);
191+
assert.equal(options.testsToRun!.testFile!.length, 0);
192+
assert.equal(options.testsToRun!.testFolder!.length, 0);
193+
assert.equal(options.testsToRun!.testSuite!.length, 0);
194+
assert.equal(options.testsToRun!.testFunction!.length, 0);
195+
});
196+
});

0 commit comments

Comments
 (0)