@@ -22,109 +22,6 @@ fileprivate let backgroundIndexingOptions = SourceKitLSPServer.Options(
2222 indexOptions: IndexOptions ( enableBackgroundIndexing: true )
2323)
2424
25- fileprivate struct ExpectedPreparation {
26- let targetID : String
27- let runDestinationID : String
28-
29- /// A closure that will be executed when a preparation task starts.
30- /// This allows the artificial delay of a preparation task to force two preparation task to race.
31- let didStart : ( ( ) -> Void ) ?
32-
33- /// A closure that will be executed when a preparation task finishes.
34- /// This allows the artificial delay of a preparation task to force two preparation task to race.
35- let didFinish : ( ( ) -> Void ) ?
36-
37- internal init (
38- targetID: String ,
39- runDestinationID: String ,
40- didStart: ( ( ) -> Void ) ? = nil ,
41- didFinish: ( ( ) -> Void ) ? = nil
42- ) {
43- self . targetID = targetID
44- self . runDestinationID = runDestinationID
45- self . didStart = didStart
46- self . didFinish = didFinish
47- }
48-
49- var configuredTarget : ConfiguredTarget {
50- return ConfiguredTarget ( targetID: targetID, runDestinationID: runDestinationID)
51- }
52- }
53-
54- fileprivate actor ExpectedPreparationTracker {
55- /// The targets we expect to be prepared. For targets within the same set, we don't care about the exact order.
56- private var expectedPreparations : [ [ ExpectedPreparation ] ]
57-
58- /// Implicitly-unwrapped optional so we can reference `self` when creating `IndexTestHooks`.
59- /// `nonisolated(unsafe)` is fine because this is not modified after `testHooks` is created.
60- nonisolated ( unsafe) var testHooks: IndexTestHooks !
61-
62- init ( expectedPreparations: [ [ ExpectedPreparation ] ] ) {
63- self . expectedPreparations = expectedPreparations
64- self . testHooks = IndexTestHooks (
65- preparationTaskDidStart: { [ weak self] in
66- await self ? . preparationTaskDidStart ( taskDescription: $0)
67- } ,
68- preparationTaskDidFinish: { [ weak self] in
69- await self ? . preparationTaskDidFinish ( taskDescription: $0)
70- }
71- )
72- }
73-
74- func preparationTaskDidStart( taskDescription: PreparationTaskDescription ) -> Void {
75- if Task . isCancelled {
76- return
77- }
78- guard let expectedTargetsToPrepare = expectedPreparations. first else {
79- return
80- }
81- for expectedPreparation in expectedTargetsToPrepare {
82- if taskDescription. targetsToPrepare. contains ( expectedPreparation. configuredTarget) {
83- expectedPreparation. didStart ? ( )
84- }
85- }
86- }
87-
88- func preparationTaskDidFinish( taskDescription: PreparationTaskDescription ) -> Void {
89- if Task . isCancelled {
90- return
91- }
92- guard let expectedTargetsToPrepare = expectedPreparations. first else {
93- XCTFail ( " Didn't expect a preparation but received \( taskDescription. targetsToPrepare) " )
94- return
95- }
96- guard Set ( taskDescription. targetsToPrepare) . isSubset ( of: expectedTargetsToPrepare. map ( \. configuredTarget) ) else {
97- XCTFail ( " Received unexpected preparation of \( taskDescription. targetsToPrepare) " )
98- return
99- }
100- var remainingExpectedTargetsToPrepare : [ ExpectedPreparation ] = [ ]
101- for expectedPreparation in expectedTargetsToPrepare {
102- if taskDescription. targetsToPrepare. contains ( expectedPreparation. configuredTarget) {
103- expectedPreparation. didFinish ? ( )
104- } else {
105- remainingExpectedTargetsToPrepare. append ( expectedPreparation)
106- }
107- }
108- if remainingExpectedTargetsToPrepare. isEmpty {
109- expectedPreparations. remove ( at: 0 )
110- } else {
111- expectedPreparations [ 0 ] = remainingExpectedTargetsToPrepare
112- }
113- }
114-
115- nonisolated func keepAlive( ) {
116- withExtendedLifetime ( self ) { _ in }
117- }
118-
119- deinit {
120- let expectedPreparations = self . expectedPreparations
121- XCTAssert (
122- expectedPreparations. isEmpty,
123- " ExpectedPreparationTracker destroyed with unfulfilled expected preparations: \( expectedPreparations) . "
124- )
125- }
126- }
127-
12825final class BackgroundIndexingTests : XCTestCase {
12926 func testBackgroundIndexingOfSingleFile( ) async throws {
13027 let project = try await SwiftPMTestProject (
@@ -614,7 +511,7 @@ final class BackgroundIndexingTests: XCTestCase {
614511 func testPrepareTargetAfterEditToDependency( ) async throws {
615512 try await SkipUnless . swiftpmStoresModulesInSubdirectory ( )
616513 var serverOptions = backgroundIndexingOptions
617- let expectedPreparationTracker = ExpectedPreparationTracker ( expectedPreparations: [
514+ let expectedPreparationTracker = ExpectedIndexTaskTracker ( expectedPreparations: [
618515 [
619516 ExpectedPreparation ( targetID: " LibA " , runDestinationID: " dummy " ) ,
620517 ExpectedPreparation ( targetID: " LibB " , runDestinationID: " dummy " ) ,
@@ -706,7 +603,7 @@ final class BackgroundIndexingTests: XCTestCase {
706603
707604 try await SkipUnless . swiftpmStoresModulesInSubdirectory ( )
708605 var serverOptions = backgroundIndexingOptions
709- let expectedPreparationTracker = ExpectedPreparationTracker ( expectedPreparations: [
606+ let expectedPreparationTracker = ExpectedIndexTaskTracker ( expectedPreparations: [
710607 // Preparation of targets during the initial of the target
711608 [
712609 ExpectedPreparation ( targetID: " LibA " , runDestinationID: " dummy " ) ,
@@ -799,4 +696,47 @@ final class BackgroundIndexingTests: XCTestCase {
799696 " \( indexFileNotification. message) does not have the expected prefix "
800697 )
801698 }
699+
700+ func testPreparationHappensInParallel( ) async throws {
701+ try await SkipUnless . swiftpmStoresModulesInSubdirectory ( )
702+
703+ let fileAIndexingStarted = self . expectation ( description: " FileA indexing started " )
704+ let fileBIndexingStarted = self . expectation ( description: " FileB indexing started " )
705+
706+ var serverOptions = backgroundIndexingOptions
707+ let expectedIndexTaskTracker = ExpectedIndexTaskTracker (
708+ expectedIndexStoreUpdates: [
709+ [
710+ ExpectedIndexStoreUpdate (
711+ sourceFileName: " FileA.swift " ,
712+ didStart: {
713+ fileAIndexingStarted. fulfill ( )
714+ } ,
715+ didFinish: {
716+ self . wait ( for: [ fileBIndexingStarted] , timeout: defaultTimeout)
717+ }
718+ ) ,
719+ ExpectedIndexStoreUpdate (
720+ sourceFileName: " FileB.swift " ,
721+ didStart: {
722+ fileBIndexingStarted. fulfill ( )
723+ } ,
724+ didFinish: {
725+ self . wait ( for: [ fileAIndexingStarted] , timeout: defaultTimeout)
726+ }
727+ ) ,
728+ ]
729+ ]
730+ )
731+ serverOptions. indexTestHooks = expectedIndexTaskTracker. testHooks
732+
733+ _ = try await SwiftPMTestProject (
734+ files: [
735+ " FileA.swift " : " " ,
736+ " FileB.swift " : " " ,
737+ ] ,
738+ serverOptions: serverOptions,
739+ cleanUp: { expectedIndexTaskTracker. keepAlive ( ) }
740+ )
741+ }
802742}
0 commit comments