@@ -15,6 +15,7 @@ import { WorkspaceContext } from "../../shared/workspace";
1515import { DasFileTracker } from "../analysis/file_tracker_das" ;
1616import { LspFileTracker } from "../analysis/file_tracker_lsp" ;
1717import { isDartDocument } from "../editors" ;
18+ import { VsCodeTestController } from "../test/vs_test_controller" ;
1819import { isInsideFlutterProject , isTestFile } from "../utils" ;
1920
2021const CURSOR_IS_IN_TEST = "dart-code:cursorIsInTest" ;
@@ -29,14 +30,14 @@ export type SuiteList = [SuiteNode, string[]];
2930abstract class TestCommands implements vs . Disposable {
3031 private disposables : vs . Disposable [ ] = [ ] ;
3132
32- constructor ( protected readonly logger : Logger , private readonly testModel : TestModel , protected readonly wsContext : WorkspaceContext , protected readonly flutterCapabilities : FlutterCapabilities ) {
33+ constructor ( protected readonly logger : Logger , private readonly testModel : TestModel , protected readonly wsContext : WorkspaceContext , private readonly vsCodeTestController : VsCodeTestController | undefined , protected readonly flutterCapabilities : FlutterCapabilities ) {
3334 this . disposables . push (
34- vs . commands . registerCommand ( "dart.startDebuggingTest" , ( treeNode : SuiteNode | GroupNode | TestNode , testRun ?: vs . TestRun ) => this . runTests ( treeNode , this . getTestNames ( treeNode ) , true , false , treeNode instanceof TestNode ) ) ,
35- vs . commands . registerCommand ( "dart.startWithoutDebuggingTest" , ( treeNode : SuiteNode | GroupNode | TestNode , testRun ?: vs . TestRun ) => this . runTests ( treeNode , this . getTestNames ( treeNode ) , false , false , treeNode instanceof TestNode ) ) ,
36- vs . commands . registerCommand ( "dart.startDebuggingSkippedTests" , ( treeNode : SuiteNode | GroupNode | TestNode ) => this . runTests ( treeNode , this . getTestNames ( treeNode , TestStatus . Skipped ) , true , false , true ) ) ,
37- vs . commands . registerCommand ( "dart.startWithoutDebuggingSkippedTests" , ( treeNode : SuiteNode | GroupNode | TestNode ) => this . runTests ( treeNode , this . getTestNames ( treeNode , TestStatus . Skipped ) , false , false , true ) ) ,
38- vs . commands . registerCommand ( "dart.startDebuggingFailedTests" , ( treeNode : SuiteNode | GroupNode | TestNode ) => this . runTests ( treeNode , this . getTestNames ( treeNode , TestStatus . Failed ) , true , false , false ) ) ,
39- vs . commands . registerCommand ( "dart.startWithoutDebuggingFailedTests" , ( treeNode : SuiteNode | GroupNode | TestNode ) => this . runTests ( treeNode , this . getTestNames ( treeNode , TestStatus . Failed ) , false , false , false ) ) ,
35+ vs . commands . registerCommand ( "dart.startDebuggingTest" , ( treeNode : SuiteNode | GroupNode | TestNode , testRun ?: vs . TestRun ) => this . runTestsForNode ( treeNode , this . getTestNames ( treeNode ) , true , false , treeNode instanceof TestNode , undefined , testRun ) ) ,
36+ vs . commands . registerCommand ( "dart.startWithoutDebuggingTest" , ( treeNode : SuiteNode | GroupNode | TestNode , testRun ?: vs . TestRun ) => this . runTestsForNode ( treeNode , this . getTestNames ( treeNode ) , false , false , treeNode instanceof TestNode , undefined , testRun ) ) ,
37+ vs . commands . registerCommand ( "dart.startDebuggingSkippedTests" , ( treeNode : SuiteNode | GroupNode | TestNode ) => this . runTestsForNode ( treeNode , this . getTestNames ( treeNode , TestStatus . Skipped ) , true , false , true ) ) ,
38+ vs . commands . registerCommand ( "dart.startWithoutDebuggingSkippedTests" , ( treeNode : SuiteNode | GroupNode | TestNode ) => this . runTestsForNode ( treeNode , this . getTestNames ( treeNode , TestStatus . Skipped ) , false , false , true ) ) ,
39+ vs . commands . registerCommand ( "dart.startDebuggingFailedTests" , ( treeNode : SuiteNode | GroupNode | TestNode ) => this . runTestsForNode ( treeNode , this . getTestNames ( treeNode , TestStatus . Failed ) , true , false , false ) ) ,
40+ vs . commands . registerCommand ( "dart.startWithoutDebuggingFailedTests" , ( treeNode : SuiteNode | GroupNode | TestNode ) => this . runTestsForNode ( treeNode , this . getTestNames ( treeNode , TestStatus . Failed ) , false , false , false ) ) ,
4041 vs . commands . registerCommand ( "dart.runAllSkippedTestsWithoutDebugging" , ( ) => this . runAllSkippedTests ( ) ) ,
4142 vs . commands . registerCommand ( "dart.runAllFailedTestsWithoutDebugging" , ( ) => this . runAllFailedTests ( ) ) ,
4243 vs . commands . registerCommand ( "dart.runTestAtCursor" , ( ) => this . runTestAtCursor ( false ) , this ) ,
@@ -89,21 +90,33 @@ abstract class TestCommands implements vs.Disposable {
8990 break ;
9091 const suiteName = path . basename ( node . suiteData . path ) ;
9192 progress . report ( { message : suiteName } ) ;
92- await this . runTests ( node , failedTestNames , false , true , onlyOfType === TestStatus . Skipped , token ) ;
93+ await this . runTestsForNode ( node , failedTestNames , false , true , onlyOfType === TestStatus . Skipped , token ) ;
9394 progress . report ( { message : suiteName , increment : failedTestNames . length * percentProgressPerTest } ) ;
9495 }
9596 } ,
9697 ) ;
9798 }
9899
99- private async runTests ( treeNode : GroupNode | SuiteNode | TestNode , testNames : string [ ] | undefined , debug : boolean , suppressPromptOnErrors : boolean , runSkippedTests : boolean , token ?: vs . CancellationToken , testRun ?: vs . TestRun ) {
100- // TODO: We should report these to a VS Code test run?
101- // https://code.visualstudio.com/api/extension-guides/testing#publishonly-controllers
100+ private async runTestsForNode ( treeNode : GroupNode | SuiteNode | TestNode , testNames : string [ ] | undefined , debug : boolean , suppressPromptOnErrors : boolean , runSkippedTests : boolean , token ?: vs . CancellationToken , testRun ?: vs . TestRun ) {
101+ const programPath = fsPath ( treeNode . suiteData . path ) ;
102+ const isGroup = treeNode instanceof GroupNode ;
103+ const canRunSkippedTest = this . flutterCapabilities . supportsRunSkippedTests || ! isInsideFlutterProject ( vs . Uri . file ( treeNode . suiteData . path ) ) ;
104+ const shouldRunSkippedTests = runSkippedTests && canRunSkippedTest ;
105+
106+ return this . runTests ( programPath , debug , testNames , isGroup , shouldRunSkippedTests , suppressPromptOnErrors , undefined , testRun , token ) ;
107+ }
108+
109+ private runTests ( programPath : string , debug : boolean , testNames : string [ ] | undefined , isGroup : boolean , shouldRunSkippedTests : boolean , suppressPromptOnErrors : boolean , launchTemplate : any | undefined , testRun : vs . TestRun | undefined , token : vs . CancellationToken | undefined ) {
110+ const vsTest = this . vsCodeTestController ?. controller ;
111+ if ( vsTest )
112+ testRun ??= vsTest . createTestRun ( new vs . TestRunRequest ( ) , undefined , true ) ;
102113
103114 const subs : vs . Disposable [ ] = [ ] ;
104115 return new Promise < void > ( async ( resolve , reject ) => {
105116 // Construct a unique ID for this session so we can track when it completes.
106117 const dartCodeDebugSessionID = `session-${ getRandomInt ( 0x1000 , 0x10000 ) . toString ( 16 ) } ` ;
118+ if ( testRun )
119+ this . vsCodeTestController ?. registerTestRun ( dartCodeDebugSessionID , testRun ) ;
107120 if ( token ) {
108121 subs . push ( vs . debug . onDidStartDebugSession ( ( e ) => {
109122 if ( e . configuration . dartCodeDebugSessionID === dartCodeDebugSessionID )
@@ -114,27 +127,28 @@ abstract class TestCommands implements vs.Disposable {
114127 if ( e . configuration . dartCodeDebugSessionID === dartCodeDebugSessionID )
115128 resolve ( ) ;
116129 } ) ) ;
117- const programPath = fsPath ( treeNode . suiteData . path ) ;
118- const canRunSkippedTest = this . flutterCapabilities . supportsRunSkippedTests || ! isInsideFlutterProject ( vs . Uri . file ( treeNode . suiteData . path ) ) ;
119- const shouldRunSkippedTests = runSkippedTests && canRunSkippedTest ;
120130 const didStart = await vs . debug . startDebugging (
121- vs . workspace . getWorkspaceFolder ( vs . Uri . file ( treeNode . suiteData . path ) ) ,
131+ vs . workspace . getWorkspaceFolder ( vs . Uri . file ( programPath ) ) ,
122132 {
123133 dartCodeDebugSessionID,
124134 suppressPromptOnErrors,
125135 ...getLaunchConfig (
126136 ! debug ,
127137 programPath ,
128138 testNames ,
129- treeNode instanceof GroupNode ,
139+ isGroup ,
130140 shouldRunSkippedTests ,
141+ launchTemplate ,
131142 ) ,
132143 name : `Tests ${ path . basename ( programPath ) } ` ,
133144 }
134145 ) ;
135146 if ( ! didStart )
136147 reject ( ) ;
137- } ) . finally ( ( ) => disposeAll ( subs ) ) ;
148+ } ) . finally ( ( ) => {
149+ testRun ?. end ( ) ;
150+ disposeAll ( subs ) ;
151+ } ) ;
138152 }
139153
140154 private getTestNames ( treeNode : TreeNode , onlyOfStatus ?: TestStatus ) : string [ ] | undefined {
@@ -164,10 +178,18 @@ abstract class TestCommands implements vs.Disposable {
164178
165179 private startTestFromOutline ( noDebug : boolean , test : TestOutlineInfo , launchTemplate : any | undefined ) {
166180 const canRunSkippedTest = ! test . isGroup && ( this . flutterCapabilities . supportsRunSkippedTests || ! isInsideFlutterProject ( vs . Uri . file ( test . file ) ) ) ;
167-
168- return vs . debug . startDebugging (
169- vs . workspace . getWorkspaceFolder ( vs . Uri . file ( test . file ) ) ,
170- getLaunchConfig ( noDebug , test . file , [ test . fullName ] , test . isGroup , canRunSkippedTest , launchTemplate ) ,
181+ const shouldRunSkippedTests = canRunSkippedTest ; // These are the same when running directly, since we always run skipped.
182+
183+ return this . runTests (
184+ test . file ,
185+ ! noDebug ,
186+ [ test . fullName ] ,
187+ test . isGroup ,
188+ shouldRunSkippedTests ,
189+ false ,
190+ launchTemplate ,
191+ undefined ,
192+ undefined ,
171193 ) ;
172194 }
173195
@@ -296,8 +318,8 @@ abstract class TestCommands implements vs.Disposable {
296318}
297319
298320export class DasTestCommands extends TestCommands {
299- constructor ( logger : Logger , testModel : TestModel , wsContext : WorkspaceContext , private readonly fileTracker : DasFileTracker , flutterCapabilities : FlutterCapabilities ) {
300- super ( logger , testModel , wsContext , flutterCapabilities ) ;
321+ constructor ( logger : Logger , testModel : TestModel , wsContext : WorkspaceContext , vsCodeTestController : VsCodeTestController | undefined , private readonly fileTracker : DasFileTracker , flutterCapabilities : FlutterCapabilities ) {
322+ super ( logger , testModel , wsContext , vsCodeTestController , flutterCapabilities ) ;
301323 }
302324
303325 protected testForCursor ( editor : vs . TextEditor ) : TestOutlineInfo | undefined {
@@ -327,8 +349,8 @@ export class DasTestCommands extends TestCommands {
327349}
328350
329351export class LspTestCommands extends TestCommands {
330- constructor ( logger : Logger , testModel : TestModel , wsContext : WorkspaceContext , private readonly fileTracker : LspFileTracker , flutterCapabilities : FlutterCapabilities ) {
331- super ( logger , testModel , wsContext , flutterCapabilities ) ;
352+ constructor ( logger : Logger , testModel : TestModel , wsContext : WorkspaceContext , vsCodeTestController : VsCodeTestController | undefined , private readonly fileTracker : LspFileTracker , flutterCapabilities : FlutterCapabilities ) {
353+ super ( logger , testModel , wsContext , vsCodeTestController , flutterCapabilities ) ;
332354 }
333355
334356 protected testForCursor ( editor : vs . TextEditor ) : LspTestOutlineInfo | undefined {
0 commit comments