@@ -22,109 +22,6 @@ fileprivate let backgroundIndexingOptions = SourceKitLSPServer.Options(
22
22
indexOptions: IndexOptions ( enableBackgroundIndexing: true )
23
23
)
24
24
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
-
128
25
final class BackgroundIndexingTests : XCTestCase {
129
26
func testBackgroundIndexingOfSingleFile( ) async throws {
130
27
let project = try await SwiftPMTestProject (
@@ -614,7 +511,7 @@ final class BackgroundIndexingTests: XCTestCase {
614
511
func testPrepareTargetAfterEditToDependency( ) async throws {
615
512
try await SkipUnless . swiftpmStoresModulesInSubdirectory ( )
616
513
var serverOptions = backgroundIndexingOptions
617
- let expectedPreparationTracker = ExpectedPreparationTracker ( expectedPreparations: [
514
+ let expectedPreparationTracker = ExpectedIndexTaskTracker ( expectedPreparations: [
618
515
[
619
516
ExpectedPreparation ( targetID: " LibA " , runDestinationID: " dummy " ) ,
620
517
ExpectedPreparation ( targetID: " LibB " , runDestinationID: " dummy " ) ,
@@ -706,7 +603,7 @@ final class BackgroundIndexingTests: XCTestCase {
706
603
707
604
try await SkipUnless . swiftpmStoresModulesInSubdirectory ( )
708
605
var serverOptions = backgroundIndexingOptions
709
- let expectedPreparationTracker = ExpectedPreparationTracker ( expectedPreparations: [
606
+ let expectedPreparationTracker = ExpectedIndexTaskTracker ( expectedPreparations: [
710
607
// Preparation of targets during the initial of the target
711
608
[
712
609
ExpectedPreparation ( targetID: " LibA " , runDestinationID: " dummy " ) ,
@@ -799,4 +696,47 @@ final class BackgroundIndexingTests: XCTestCase {
799
696
" \( indexFileNotification. message) does not have the expected prefix "
800
697
)
801
698
}
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
+ }
802
742
}
0 commit comments