@@ -18,18 +18,54 @@ import SwiftOptions
18
18
import IncrementalTestFramework
19
19
20
20
/// Add and remove function in imported struct and in imported extension of imported struct.
21
+ /// This is a very complicated test, so it is built programmatically.
21
22
class HideAndShowFuncInStructAndExtensionTests : XCTestCase {
22
23
func testHideAndShowFuncInStructAndExtension( ) throws {
24
+ try IncrementalTest . perform ( steps)
25
+ }
26
+ }
27
+
28
+ /// The changes to be tested
29
+ fileprivate let stepChanges : [ [ Change ] ] = [
30
+ [ ] ,
31
+ [ ] ,
32
+ [ . exposeFuncInStruct] ,
33
+ [ ] ,
34
+ [ . exposeFuncInExtension] ,
35
+ [ ] ,
36
+ Change . allCases,
37
+ [ ] ,
38
+ [ . exposeFuncInStruct] ,
39
+ [ . exposeFuncInExtension] ,
40
+ [ . exposeFuncInStruct] ,
41
+ Change . allCases,
42
+ [ . exposeFuncInExtension] ,
43
+ Change . allCases
44
+ ]
45
+
46
+
47
+ // MARK: - Reify the source changes
48
+
49
+ fileprivate enum Change : String , CustomStringConvertible , CaseIterable {
50
+ /// Make the imported specific method defined in a structure public
51
+ case exposeFuncInStruct
52
+
53
+ /// Make the imported specific method defined in an extension public
54
+ case exposeFuncInExtension
55
+
56
+ var name : String { rawValue}
57
+ var description : String { name}
58
+ }
23
59
24
- // MARK: - Define imported module
25
- let imported = Source ( named: " imported " , containing: """
60
+ // MARK: - Define imported module
61
+ fileprivate let imported = Source ( named: " imported " , containing: """
26
62
// Just for fun, a protocol, as well as the struc with the optional specific func.
27
63
public protocol PP {}
28
64
public struct S: PP {
29
65
public init() {}
30
66
// Optionally expose a specific function in the structure
31
- //# publicInStruct public
32
- static func inStruct(_ i: Int) {print( " 1: private " )}
67
+ //# \( Change . exposeFuncInStruct ) public
68
+ static func inStruct(_ i: Int) {print( " specific in struct " )}
33
69
func fo() {}
34
70
}
35
71
public struct T {
@@ -38,93 +74,125 @@ class HideAndShowFuncInStructAndExtensionTests: XCTestCase {
38
74
}
39
75
extension S {
40
76
// Optionally expose a specific function in the extension
41
- //# publicInExtension public
42
- static func inExtension(_ i: Int) {print( " 2: private " )}
77
+ //# \( Change . exposeFuncInExtension ) public
78
+ static func inExtension(_ i: Int) {print( " specific in extension " )}
43
79
}
44
80
""" )
45
81
46
- let importedModule = Module ( named: " importedModule " ,
47
- containing: [ imported] ,
48
- producing: . library)
82
+ fileprivate let importedModule = Module ( named: " importedModule " ,
83
+ containing: [ imported] ,
84
+ producing: . library)
49
85
50
- // MARK: - Define the main module
86
+ // MARK: - Define the main module
51
87
52
- let instantiatesS = Source ( named: " instantiatesS " , containing: """
88
+ fileprivate let instantiatesS = Source ( named: " instantiatesS " , containing: """
53
89
// Instantiate S
54
90
import \( importedModule. name)
55
91
func late() { _ = S() }
56
92
""" )
57
93
58
- let callFunctionInExtension = Source ( named: " callFunctionInExtension " , containing: """
94
+ fileprivate let callFunctionInExtension = Source ( named: " callFunctionInExtension " , containing: """
59
95
// Call the function defined in an extension
60
96
import \( importedModule. name)
61
97
func fred() { S.inExtension(3) }
62
98
""" )
63
99
64
- let noUseOfS = Source ( named: " noUseOfS " , containing: """
100
+ fileprivate let noUseOfS = Source ( named: " noUseOfS " , containing: """
65
101
/// Call a function in an unchanging struct
66
102
import \( importedModule. name)
67
103
func baz() { T.bar( " asdf " ) }
68
104
""" )
69
105
70
- let main = Source ( named: " main " , containing: """
106
+ fileprivate let main = Source ( named: " main " , containing: """
71
107
/// Extend S with general functions
72
108
import \( importedModule. name)
73
109
extension S {
74
110
static func inStruct<I: SignedInteger>(_ si: I) {
75
- print( " 1: not public " )
111
+ print( " general in struct " )
76
112
}
77
113
static func inExtension<I: SignedInteger>(_ si: I) {
78
- print( " 2: not public " )
114
+ print( " general in extension " )
79
115
}
80
116
}
81
117
S.inStruct(3)
118
+ S.inExtension(4)
82
119
""" )
83
120
84
- let mainModule = Module ( named: " mainModule " ,
85
- containing: [ instantiatesS,
86
- callFunctionInExtension,
87
- noUseOfS,
88
- main] ,
89
- importing: [ importedModule] ,
90
- producing: . executable)
91
-
92
- // MARK: - Define the test
93
-
94
- let modules = [ importedModule, mainModule]
95
-
96
- let addOrRmInStruct = ExpectedCompilations (
97
- always: [ callFunctionInExtension, imported, instantiatesS, main] ,
98
- andWhenDisabled: [ noUseOfS] )
99
-
100
- // Interestingly, changes to the imported extension do not change the
101
- /// structure's instantiation. (Compare to above.)
102
- let addOrRmInExt = ExpectedCompilations (
103
- always: [ callFunctionInExtension, imported, main] ,
104
- andWhenDisabled: [ instantiatesS, noUseOfS] )
105
-
106
- let addOrRmBoth = addOrRmInStruct
107
-
108
- let steps = [
109
- Step ( compiling: modules) ,
110
- Step ( adding: " publicInStruct " , compiling: modules, expecting: addOrRmInStruct) ,
111
- Step ( compiling: modules, expecting: addOrRmInStruct) ,
112
- Step ( adding: " publicInExtension " , compiling: modules, expecting: addOrRmInExt ) ,
113
- Step ( compiling: modules, expecting: addOrRmInExt ) ,
114
- Step ( adding: " publicInStruct " , " publicInExtension " , compiling: modules, expecting: addOrRmBoth ) ,
115
- Step ( compiling: modules, expecting: addOrRmBoth ) ,
116
- Step ( adding: " publicInStruct " , compiling: modules, expecting: addOrRmInStruct) ,
117
- Step ( adding: " publicInExtension " , compiling: modules, expecting: addOrRmInStruct) ,
118
- Step ( adding: " publicInStruct " , compiling: modules, expecting: addOrRmInStruct) ,
119
- Step ( adding: " publicInStruct " , " publicInExtension " , compiling: modules, expecting: addOrRmInExt ) ,
120
- Step ( adding: " publicInExtension " , compiling: modules, expecting: addOrRmInStruct) ,
121
- Step ( adding: " publicInStruct " , " publicInExtension " , compiling: modules, expecting: addOrRmInStruct) ,
122
- ]
121
+ fileprivate let mainModule = Module ( named: " mainModule " ,
122
+ containing: [ instantiatesS,
123
+ callFunctionInExtension,
124
+ noUseOfS,
125
+ main] ,
126
+ importing: [ importedModule] ,
127
+ producing: . executable)
128
+
129
+ // MARK: - Define the whole app
130
+ fileprivate let modules = [ importedModule, mainModule]
131
+
132
+ // MARK: - Compute the expectations
133
+ fileprivate extension Change {
134
+ var expectedCompilationsWithIncrementalImports : [ Source ] {
135
+ switch self {
136
+ case . exposeFuncInStruct: return [ callFunctionInExtension, imported, instantiatesS, main]
137
+ case . exposeFuncInExtension: return [ callFunctionInExtension, imported, main]
138
+ }
139
+ }
123
140
124
- try IncrementalTest . perform ( steps)
141
+ var locusOfExposure : String {
142
+ switch self {
143
+ case . exposeFuncInStruct: return " struct "
144
+ case . exposeFuncInExtension: return " extension "
145
+ }
146
+ }
147
+ static var allLociOfExposure : [ String ] {
148
+ allCases. map { $0. locusOfExposure}
125
149
}
126
150
}
127
151
152
+ // MARK: - Building a step from combinations of Changes
153
+ fileprivate extension Array where Element == Change {
154
+ var addOns : [ String ] { map { $0. name} }
155
+
156
+ func expectedCompilations( _ prevStep: Step ? ) -> ExpectedCompilations {
157
+ guard let prevStep = prevStep else {
158
+ return modules. allSourcesToCompile
159
+ }
160
+ let deltas = Set ( map { $0. name} ) . symmetricDifference ( prevStep. addOns. map { $0. name} )
161
+ . map { Change . init ( rawValue: $0) !}
128
162
163
+ let expectedCompilationsWithIncrementalImports : [ Source ] =
164
+ deltas. reduce ( into: Set < Source > ( ) ) { sources, change in
165
+ sources. formUnion ( change. expectedCompilationsWithIncrementalImports)
166
+ }
167
+ . sorted ( )
129
168
169
+ let andWhenDisabled = deltas. isEmpty
170
+ ? [ ]
171
+ : Set ( modules. allSources) . subtracting ( expectedCompilationsWithIncrementalImports) . sorted ( )
130
172
173
+ return ExpectedCompilations ( always: expectedCompilationsWithIncrementalImports, andWhenDisabled: andWhenDisabled)
174
+ }
175
+
176
+ var expectedOutput : ExpectedProcessResult {
177
+ let specOrGen = Change . allCases . map {
178
+ contains ( $0) ? " specific " : " general "
179
+ }
180
+ let output = zip ( specOrGen, Change . allLociOfExposure)
181
+ . map { " \( $0. 0 ) in \( $0. 1 ) " }
182
+ . joined ( separator: " \n " )
183
+ return ExpectedProcessResult ( output: output)
184
+ }
185
+
186
+ func step( prior: Step ? ) -> Step {
187
+ Step ( adding: addOns,
188
+ building: modules,
189
+ . expecting( expectedCompilations ( prior) , expectedOutput) )
190
+ }
191
+ }
192
+ // MARK: - All steps
193
+
194
+ var steps : [ Step ] {
195
+ stepChanges. reduce ( into: [ ] ) { steps, changes in
196
+ steps. append ( changes. step ( prior: steps. last) )
197
+ }
198
+ }
0 commit comments