@@ -12,7 +12,7 @@ import { IJavaTestItem, ProjectType } from '../types';
1212import { executeJavaLanguageServerCommand } from '../utils/commandUtils' ;
1313import { getRequestDelay , lruCache , MovingAverage } from './debouncing' ;
1414import { runnableTag , testController } from './testController' ;
15- import { dataCache } from './testItemDataCache' ;
15+ import { dataCache , invalidateResolutionVersion } from './testItemDataCache' ;
1616import { TestKind , TestLevel } from '../java-test-runner.api' ;
1717
1818/**
@@ -90,30 +90,43 @@ export async function getProjectType(item: IJavaTestItem): Promise<ProjectType>
9090 * - If an existing child is not contained in the childrenData parameter, it will be deleted
9191 * - If a child does not exist, create it, otherwise, update it as well as its metadata.
9292 */
93- export function synchronizeItemsRecursively ( parent : TestItem , childrenData : IJavaTestItem [ ] | undefined ) : void {
94- if ( childrenData ) {
95- // remove the out-of-date children
96- parent . children . forEach ( ( child : TestItem ) => {
97- if ( dataCache . get ( child ) ?. testLevel === TestLevel . Invocation ) {
98- // only remove the invocation items before a new test session starts
99- return ;
100- }
101- const existingItem : IJavaTestItem | undefined = childrenData . find ( ( data : IJavaTestItem ) => data . id === child . id ) ;
102- if ( ! existingItem ) {
103- parent . children . delete ( child . id ) ;
104- }
105- } ) ;
106- // update/create children
107- for ( const child of childrenData ) {
108- const childItem : TestItem = updateOrCreateTestItem ( parent , child ) ;
109- if ( child . testLevel <= TestLevel . Class ) {
110- childItem . canResolveChildren = true ;
111- }
112- synchronizeItemsRecursively ( childItem , child . children ) ;
93+ export function synchronizeItemsRecursively ( parent : TestItem , childrenData : IJavaTestItem [ ] | undefined ,
94+ childrenAreComplete : boolean = false ) : void {
95+ if ( ! childrenData && ! childrenAreComplete ) {
96+ return ;
97+ }
98+
99+ const children : IJavaTestItem [ ] = childrenData ?? [ ] ;
100+ // remove the out-of-date children
101+ parent . children . forEach ( ( child : TestItem ) => {
102+ if ( dataCache . get ( child ) ?. testLevel === TestLevel . Invocation ) {
103+ // only remove the invocation items before a new test session starts
104+ return ;
105+ }
106+ const existingItem : IJavaTestItem | undefined = children . find ( ( data : IJavaTestItem ) => data . id === child . id ) ;
107+ if ( ! existingItem ) {
108+ parent . children . delete ( child . id ) ;
109+ }
110+ } ) ;
111+ // update/create children
112+ for ( const child of children ) {
113+ const childItem : TestItem = updateOrCreateTestItem ( parent , child ) ;
114+ if ( child . testLevel <= TestLevel . Class ) {
115+ childItem . canResolveChildren = true ;
113116 }
117+ synchronizeItemsRecursively ( childItem , child . children , childrenAreComplete ) ;
114118 }
115119}
116120
121+ export function markTestClassesResolvedRecursively ( item : TestItem ) : void {
122+ if ( dataCache . get ( item ) ?. testLevel === TestLevel . Class ) {
123+ item . canResolveChildren = false ;
124+ }
125+ item . children . forEach ( ( child : TestItem ) => {
126+ markTestClassesResolvedRecursively ( child ) ;
127+ } ) ;
128+ }
129+
117130export function updateOrCreateTestItem ( parent : TestItem , childData : IJavaTestItem ) : TestItem {
118131 let childItem : TestItem | undefined = parent . children . get ( childData . id ) ;
119132 if ( childItem ) {
@@ -125,6 +138,7 @@ export function updateOrCreateTestItem(parent: TestItem, childData: IJavaTestIte
125138}
126139
127140function updateTestItem ( testItem : TestItem , metaInfo : IJavaTestItem ) : void {
141+ const previousJdtHandler : string | undefined = dataCache . get ( testItem ) ?. jdtHandler ;
128142 testItem . range = asRange ( metaInfo . range ) ;
129143 testItem . label = metaInfo . label ;
130144 dataCache . set ( testItem , {
@@ -134,6 +148,10 @@ function updateTestItem(testItem: TestItem, metaInfo: IJavaTestItem): void {
134148 testLevel : metaInfo . testLevel ,
135149 testKind : metaInfo . testKind ,
136150 } ) ;
151+ if ( previousJdtHandler !== undefined && previousJdtHandler !== metaInfo . jdtHandler &&
152+ ( metaInfo . testLevel === TestLevel . Project || metaInfo . testLevel === TestLevel . Class ) ) {
153+ invalidateResolutionVersion ( testItem ) ;
154+ }
137155}
138156
139157/**
@@ -217,15 +235,11 @@ export async function updateItemForDocument(uri: Uri, testTypes?: IJavaTestItem[
217235 return [ ] ;
218236 }
219237
238+ const expectedTypeIds : Set < string > = new Set ( testTypes . map ( ( testType : IJavaTestItem ) => testType . id ) ) ;
239+ removeOutdatedTestItemsForDocument ( belongingPackage , uri , expectedTypeIds ) ;
240+
220241 const tests : TestItem [ ] = [ ] ;
221- if ( testTypes . length === 0 ) {
222- // Remove the children with the same uri when no test items is found
223- belongingPackage . children . forEach ( ( typeItem : TestItem ) => {
224- if ( path . relative ( typeItem . uri ?. fsPath || '' , uri . fsPath ) === '' ) {
225- belongingPackage ! . children . delete ( typeItem . id ) ;
226- }
227- } ) ;
228- } else {
242+ if ( testTypes . length > 0 ) {
229243 for ( const testType of testTypes ) {
230244 // here we do not directly call synchronizeItemsRecursively() because testTypes here are just part of the
231245 // children of the belonging package, we don't want to delete other children unexpectedly.
@@ -237,7 +251,8 @@ export async function updateItemForDocument(uri: Uri, testTypes?: IJavaTestItem[
237251 updateTestItem ( testTypeItem , testType ) ;
238252 }
239253 tests . push ( testTypeItem ) ;
240- synchronizeItemsRecursively ( testTypeItem , testType . children ) ;
254+ synchronizeItemsRecursively ( testTypeItem , testType . children , true ) ;
255+ markTestClassesResolvedRecursively ( testTypeItem ) ;
241256 }
242257 }
243258
@@ -248,6 +263,41 @@ export async function updateItemForDocument(uri: Uri, testTypes?: IJavaTestItem[
248263 return tests ;
249264}
250265
266+ export function removeOutdatedTestItemsForDocument ( belongingPackage : TestItem , uri : Uri ,
267+ expectedTypeIds : Set < string > ) : void {
268+ const belongingProject : TestItem | undefined = belongingPackage . parent ;
269+ if ( ! belongingProject ) {
270+ return ;
271+ }
272+
273+ invalidateResolutionVersion ( belongingProject ) ;
274+ belongingProject . children . forEach ( ( packageItem : TestItem ) => {
275+ packageItem . children . forEach ( ( typeItem : TestItem ) => {
276+ if ( path . relative ( typeItem . uri ?. fsPath || '' , uri . fsPath ) !== '' ) {
277+ return ;
278+ }
279+
280+ invalidateResolutionRecursively ( typeItem ) ;
281+ if ( ! expectedTypeIds . has ( typeItem . id ) ) {
282+ packageItem . children . delete ( typeItem . id ) ;
283+ }
284+ } ) ;
285+
286+ if ( packageItem !== belongingPackage && packageItem . children . size === 0 ) {
287+ belongingProject . children . delete ( packageItem . id ) ;
288+ }
289+ } ) ;
290+ }
291+
292+ function invalidateResolutionRecursively ( item : TestItem ) : void {
293+ if ( dataCache . get ( item ) ?. testLevel === TestLevel . Class ) {
294+ invalidateResolutionVersion ( item ) ;
295+ }
296+ item . children . forEach ( ( child : TestItem ) => {
297+ invalidateResolutionRecursively ( child ) ;
298+ } ) ;
299+ }
300+
251301/**
252302 * Give a test item for a type, find its belonging package item according to its id.
253303 */
0 commit comments