(debug: boolean, includeCoverage: boolean, request: vs.TestRunRequest, _token: vs.CancellationToken)
| 136 | } |
| 137 | |
| 138 | public async runTests(debug: boolean, includeCoverage: boolean, request: vs.TestRunRequest, _token: vs.CancellationToken): Promise<void> { |
| 139 | const isRunningAll = !request.include; |
| 140 | |
| 141 | // If running all tests, ensure we have discovered them all first. |
| 142 | if (isRunningAll) |
| 143 | await this.discoverer?.ensureSuitesDiscovered(); |
| 144 | |
| 145 | const testsToRun = new Set<vs.TestItem>(); |
| 146 | const testsToExclude = new Set<vs.TestItem>(); |
| 147 | (request.include ?? this.controller.items).forEach((item) => testsToRun.add(item)); |
| 148 | request.exclude?.forEach((item) => { testsToRun.delete(item); testsToExclude.add(item); }); |
| 149 | |
| 150 | // For each item in the set, remove any of its descendants because they will be run by the parent. |
| 151 | this.removeRedundantChildNodes(testsToRun); |
| 152 | |
| 153 | // Similarly, remove any excluded tests that are already excluded by their suite, because that will allow the |
| 154 | // optimisation below (which requires only excluded suites) to work in more cases. |
| 155 | this.removeRedundantChildNodes(testsToExclude); |
| 156 | |
| 157 | const run = this.controller.createTestRun(request); |
| 158 | |
| 159 | // Mark each node we will run as enqueued. |
| 160 | this.markEnqueued(run, testsToRun, testsToExclude); |
| 161 | |
| 162 | try { |
| 163 | // As an optimisation, if we're no-debug and running complete files (eg. all included or excluded items are |
| 164 | // suites), we can run the "fast path" in a single `dart test` invocation. |
| 165 | const nodesToRun = this.extractRunnableNodes(testsToRun); |
| 166 | const nodesToExclude = this.extractRunnableNodes(testsToExclude); |
| 167 | if (!debug && nodesToRun.every((item) => item instanceof SuiteNode) && nodesToExclude.every((item) => item instanceof SuiteNode)) { |
| 168 | await vs.commands.executeCommand("_dart.runAllTestsWithoutDebugging", nodesToRun, nodesToExclude, includeCoverage, run, isRunningAll); |
| 169 | return; |
| 170 | } |
| 171 | |
| 172 | // Group into suites since we need to run each seperately (although we can run |
| 173 | // multiple tests witthin one suite together). |
| 174 | const testsBySuite = new Map<SuiteData, RunnableTreeNode[]>(); |
| 175 | |
| 176 | nodesToRun.forEach((node) => { |
| 177 | const testNodes = testsBySuite.get(node.suiteData) ?? []; |
| 178 | testsBySuite.set(node.suiteData, testNodes); |
| 179 | testNodes.push(node); |
| 180 | }); |
| 181 | |
| 182 | const command = debug |
| 183 | ? "_dart.startDebuggingTestsFromVsTestController" |
| 184 | : "_dart.startWithoutDebuggingTestsFromVsTestController"; |
| 185 | const totalItems = testsBySuite.size; |
| 186 | |
| 187 | if (totalItems === 0) |
| 188 | return; // Shouldn't get here |
| 189 | else if (totalItems === 1) { |
| 190 | const [[suite, nodes]] = testsBySuite.entries(); |
| 191 | await vs.commands.executeCommand(command, suite, nodes, false, includeCoverage, run, undefined); |
| 192 | } else { |
| 193 | const title = debug ? "Debugging" : "Running"; |
| 194 | await vs.window.withProgress({ |
| 195 | cancellable: true, |
no test coverage detected