@@ -35,7 +35,7 @@ export function registerWatcherForWorkspaces() {
35
35
}
36
36
37
37
console . log ( "onDidChangeTextDocument callback" ) ;
38
- debounceUpToDownUpdateTestItems ( e . document . uri ) ;
38
+ debounceHandleFileChangeCore ( e . document . uri ) ;
39
39
40
40
function isRustFile ( document : vscode . TextDocument ) : boolean {
41
41
return document . fileName . toLowerCase ( ) . endsWith ( '.rs' ) ;
@@ -62,17 +62,21 @@ function watchWorkspace(workspaceFolder: vscode.WorkspaceFolder) {
62
62
function debounce ( fn : Function , ms : number ) {
63
63
let timeout : NodeJS . Timeout | undefined = undefined ;
64
64
return ( ...params : any [ ] ) => {
65
- console . log ( performance . now ( ) ) ;
65
+ console . log ( "debounce debug: " + performance . now ( ) ) ;
66
66
clearTimeout ( timeout ) ;
67
67
timeout = setTimeout ( ( ) => {
68
68
fn ( ...params ) ;
69
69
} , ms ) ;
70
70
} ;
71
71
}
72
72
73
- const deboundeRefresh = debounce ( refreshAllThings , 500 ) ;
73
+ // Why choose 2s:
74
+ // when auto save is enabled, there seems to be 2 events for workspace.onDidChangeTextDocument
75
+ // the first one is for the change of the file, the second one is for the save of the file
76
+ // And usually it takes about 1s between on my machine between the two events
77
+ const deboundeRefresh = debounce ( refreshAllThings , 2000 ) ;
74
78
// FIXME: if there is changes in two files, we will lost the first chagne
75
- const debounceUpToDownUpdateTestItems = debounce ( updateModelByChangeOfFile , 500 ) ;
79
+ const debounceHandleFileChangeCore = debounce ( handleFileChangeCore , 2000 ) ;
76
80
77
81
export async function refreshAllThings ( ) {
78
82
if ( ! isInitilized ) return ;
@@ -133,10 +137,14 @@ async function handleFileCreate(uri: vscode.Uri) {
133
137
updateTestItemsByModel ( ) ;
134
138
}
135
139
140
+ async function handleFileChangeCore ( uri : vscode . Uri ) {
141
+ await updateModelByChangeOfFile ( uri ) ;
142
+ updateTestItemsByModel ( ) ;
143
+ }
144
+
136
145
async function handleFileChange ( uri : vscode . Uri ) {
137
146
console . log ( `handleFileChange triggered for ${ uri } ` ) ;
138
- await debounceUpToDownUpdateTestItems ( uri ) ;
139
- updateTestItemsByModel ( ) ;
147
+ await debounceHandleFileChangeCore ( uri ) ;
140
148
}
141
149
142
150
async function handleFileDelete ( uri : vscode . Uri ) {
@@ -148,24 +156,43 @@ async function handleFileDelete(uri: vscode.Uri) {
148
156
function updateTestItemsByModel ( ) {
149
157
testController ! . items . replace ( [ ] ) ;
150
158
const rootTestItems = VscodeTestTreeBuilder . build ( ) ;
151
- // const mockItem = testController!.createTestItem('123', '456');
152
- // testController!.items.add(mockItem);
153
159
testController ! . items . replace ( rootTestItems ) ;
154
160
}
155
161
162
+ async function getNormalizedTestRunnablesInFile ( uri : vscode . Uri ) {
163
+ const rawRunables = await RaApiHelper . getTestRunnablesInFile ( uri ) ;
164
+
165
+ assert ( ! ! rawRunables ) ;
166
+
167
+ const runnables = rawRunables . map ( it => new RunnableFacde ( it ) ) ;
168
+
169
+ // User might copy and past test, and then there might be same name test or test module
170
+ // Although it's wrong, we need to tollerate it.
171
+ // We only choose the first one.
172
+ return uniqueRunnables ( runnables ) ;
173
+
174
+ function uniqueRunnables ( runnables : RunnableFacde [ ] ) {
175
+ const map = new Map < string , RunnableFacde > ( ) ;
176
+ runnables . forEach ( runnable => {
177
+ const key = `${ runnable . workspaceRoot } |${ runnable . packageName } |${ runnable . targetKind } |${ runnable . targetName } |${ runnable . origin . label } ` ;
178
+ if ( ! map . has ( key ) ) {
179
+ map . set ( key , runnable ) ;
180
+ }
181
+ } ) ;
182
+ return Array . from ( map . values ( ) ) ;
183
+ }
184
+ }
185
+
156
186
async function updateModelByChangeOfFile ( uri : vscode . Uri ) {
157
- const testInfos = await RaApiHelper . getTestRunnablesInFile ( uri ) ;
187
+ const runnables = await getNormalizedTestRunnablesInFile ( uri ) ;
158
188
159
189
// Maybe from some to none
160
190
// need to recursively clean the parent, until there is at least one test cases.
161
- if ( testInfos === null || testInfos . length === 0 ) {
191
+ if ( runnables . length === 0 ) {
162
192
testModelTree . removeTestItemsRecusivelyByUri ( uri ) ;
163
193
return ;
164
194
}
165
195
166
- // Ensure parent is created in the test-model-tree
167
- const runnables = testInfos . map ( it => new RunnableFacde ( it ) ) ;
168
-
169
196
const testModuelRunnables = runnables . filter ( it =>
170
197
it . testKind === NodeKind . TestModule )
171
198
. sort ( RunnableFacde . sortByLabel ) ;
@@ -175,6 +202,7 @@ async function updateModelByChangeOfFile(uri: vscode.Uri) {
175
202
176
203
assert ( testModuelRunnables . length + testItemRunnables . length === runnables . length ) ;
177
204
205
+ // FIXME: should be file test modules
178
206
const rootTestModuleRunnbale = testModuelRunnables [ 0 ] ;
179
207
180
208
// Now, we know the root test module
@@ -231,13 +259,13 @@ async function updateModelByChangeOfFile(uri: vscode.Uri) {
231
259
// but this is overkill, we could reuse the test modules
232
260
// if definition are same(and not in the same file)
233
261
if ( nearestNode . kind === NodeKind . TestModule
234
- && nearestNode . declarationInfo . uri . toString ( ) === rootTestModuleRunnbale . uri . toString ( )
262
+ && nearestNode . definitionUri . toString ( ) === rootTestModuleRunnbale . uri . toString ( )
235
263
&& nearestNode . name === rootTestModuleRunnbale . testOrSuiteName ) {
236
264
nearestNode . testChildren . clear ( ) ;
237
265
}
238
266
239
267
// collect nodes which children need to be fetched
240
- const nodeNeededFetched = FalsyLeavesCollector . collect ( ) ;
268
+ const nodeNeededFetched = FalsyLeavesCollector . collect ( nearestNode ) ;
241
269
242
270
// fetch the children for nodes
243
271
await fetchChildrenForFalsyLeaves ( nodeNeededFetched ) ;
@@ -258,11 +286,8 @@ async function fetchChildrenForTestModuleNode(testModuleNode: TestModuleNode) {
258
286
, "if the test module is not a declaration module, it must be the root module of some target node" ) ;
259
287
260
288
const definitionUri = testModuleNode . definitionUri ;
261
- const rawRunnables = await RaApiHelper . getTestRunnablesInFile ( definitionUri ) ;
262
-
263
- assert ( ! ! rawRunnables ) ;
264
289
265
- const runnables = rawRunnables . map ( it => new RunnableFacde ( it ) ) ;
290
+ const runnables = await getNormalizedTestRunnablesInFile ( definitionUri ) ;
266
291
267
292
await updateModelByRunnables ( testModuleNode , runnables ) ;
268
293
}
@@ -287,7 +312,8 @@ async function updateModelByRunnables(parentNode: TestModuleNode, runnables: Run
287
312
// which means, when find the test item in test explorer, will refirect to declaration rather than the file
288
313
289
314
// Handle testRunnables and test modules which have items, which are in the same test file
290
- addTestModuleWithItemsRunnablesToTestModule ( parentNode , [ ...withItemsModuleRunnables , ...testRunnables ] ) ;
315
+ addTestModuleWithItemsRunnablesToTestModule ( parentNode , withItemsModuleRunnables ) ;
316
+ addTestModuleWithItemsRunnablesToTestModule ( parentNode , testRunnables ) ;
291
317
292
318
// Handle declarationModules
293
319
// TODO: maybe concurrent?
@@ -305,7 +331,7 @@ async function addAndFetchDeclarationModuleRunnableToTestModule(parentNode: Test
305
331
parentNode ,
306
332
declarationModuleRunnable . testOrSuiteName ,
307
333
declarationModuleRunnable . toTestLocation ( ) ,
308
- vscode . Uri . file ( definition . targetUri ) ) ;
334
+ vscode . Uri . parse ( definition . targetUri ) ) ;
309
335
parentNode . testChildren . add ( testModule ) ;
310
336
311
337
// Fetch and update their definitions
@@ -320,7 +346,7 @@ function addTestModuleWithItemsRunnablesToTestModule(parentNode: TestModuleNode,
320
346
const parentNode = testModelTree . findNearestNodeByRunnable ( runnable ) ;
321
347
assert ( parentNode . kind === NodeKind . TestModule , "Runable should be inserted into TestModule/Test, we create mock runnable for target/workspace node" ) ;
322
348
if ( ! parentNode . isRootTestModule ( ) ) {
323
- assert ( parentNode . name === runnable . testPaths [ runnable . testPaths . length - 1 ] ) ;
349
+ assert ( parentNode . name === runnable . testPaths [ runnable . testPaths . length - 2 ] ) ;
324
350
}
325
351
326
352
switch ( runnable . testKind ) {
0 commit comments