@@ -134,7 +134,9 @@ class FixItApplierApplyEditsTests: XCTestCase {
134
134
. init( range: 3 ..< 7 , replacement: " cd " ) ,
135
135
] ,
136
136
// The second edit is skipped.
137
- possibleOutputs: [ " aboo = 1 " , " varcd = 1 " ]
137
+ outputs: [
138
+ . either( [ " aboo = 1 " , " varcd = 1 " ] )
139
+ ]
138
140
)
139
141
}
140
142
@@ -148,19 +150,36 @@ class FixItApplierApplyEditsTests: XCTestCase {
148
150
. init( range: 0 ..< 5 , replacement: " _ " ) ,
149
151
. init( range: 0 ..< 3 , replacement: " let " ) ,
150
152
] ,
151
- possibleOutputs: [ " _ = 11 " , " let x = 11 " ]
153
+ outputs: [
154
+ . either( [ " _ = 11 " , " let x = 11 " ] )
155
+ ]
152
156
)
153
157
}
154
158
155
159
func testMultipleOverlappingInsertions( ) {
160
+ assertAppliedEdits (
161
+ to: " x = 1 " ,
162
+ edits: [
163
+ . init( range: 1 ..< 1 , replacement: " y " ) ,
164
+ . init( range: 1 ..< 1 , replacement: " z " ) ,
165
+ ] ,
166
+ outputs: [
167
+ . either( [ " xyz = 1 " , " xzy = 1 " ] )
168
+ ]
169
+ )
170
+
156
171
assertAppliedEdits (
157
172
to: " x = 1 " ,
158
173
edits: [
159
174
. init( range: 0 ..< 0 , replacement: " var " ) ,
160
175
. init( range: 0 ..< 0 , replacement: " var " ) ,
176
+ . init( range: 4 ..< 5 , replacement: " 2 " ) ,
161
177
. init( range: 0 ..< 0 , replacement: " var " ) ,
162
178
] ,
163
- output: " var var var x = 1 "
179
+ outputs: [
180
+ . output( " var var var x = 2 " , allowDuplicateInsertions: true ) ,
181
+ . output( " var x = 2 " , allowDuplicateInsertions: false ) ,
182
+ ]
164
183
)
165
184
}
166
185
@@ -182,43 +201,110 @@ class FixItApplierApplyEditsTests: XCTestCase {
182
201
. init( range: 2 ..< 2 , replacement: " a " ) , // Insertion
183
202
] ,
184
203
// FIXME: This behavior where these edits are not considered overlapping doesn't feel desirable
185
- possibleOutputs: [ " _x = 1 " , " _ a= 1 " ]
204
+ outputs: [
205
+ . either( [ " _x = 1 " , " _ a= 1 " ] )
206
+ ]
186
207
)
187
208
}
188
209
}
189
210
190
- /// Asserts that at least one element in `possibleOutputs` matches the result
191
- /// of applying an array of edits to `input`, for all permutations of `edits`.
211
+ private enum Output {
212
+ case output( String , allowDuplicateInsertions: Bool ? = nil )
213
+ case either( [ String ] , allowDuplicateInsertions: Bool ? = nil )
214
+
215
+ var possibleOutputs : [ String ] {
216
+ switch self {
217
+ case . output( let output, _) :
218
+ return [ output]
219
+ case . either( let outputs, _) :
220
+ return outputs
221
+ }
222
+ }
223
+
224
+ var allowDuplicateInsertions : Bool ? {
225
+ switch self {
226
+ case . output( _, let allowDuplicateInsertions) ,
227
+ . either( _, let allowDuplicateInsertions) :
228
+ return allowDuplicateInsertions
229
+ }
230
+ }
231
+ }
232
+
233
+ /// Asserts that the given outputs match the result of applying an array of
234
+ /// edits to `input`, for all permutations of `edits`.
192
235
private func assertAppliedEdits(
193
236
to tree: SourceFileSyntax ,
194
237
edits: [ SourceEdit ] ,
195
- possibleOutputs : [ String ]
238
+ outputs : [ Output ]
196
239
) {
197
- precondition ( !possibleOutputs. isEmpty)
198
-
199
- var indices = Array ( edits. indices)
200
- while true {
201
- let result = FixItApplier . apply ( edits: indices. map { edits [ $0] } , to: tree)
202
- guard possibleOutputs. contains ( result) else {
203
- XCTFail ( " \" \( result) \" is not equal to either of \( possibleOutputs) " )
204
- return
205
- }
240
+ precondition ( !outputs. isEmpty)
241
+
242
+ NEXT_OUTPUT: for output in outputs {
243
+ let allowDuplicateInsertionsValues =
244
+ switch output. allowDuplicateInsertions {
245
+ case . none:
246
+ [ true , false ]
247
+ case . some( let value) :
248
+ [ value]
249
+ }
250
+
251
+ let possibleOutputs = output. possibleOutputs
252
+
253
+ // Check this output against all permutations of edits.
254
+ var indices = Array ( edits. indices)
255
+ while true {
256
+ let editsPermutation = indices. map { edits [ $0] }
257
+
258
+ for allowDuplicateInsertionsValue in allowDuplicateInsertionsValues {
259
+ let actualOutput = FixItApplier . apply (
260
+ edits: editsPermutation,
261
+ to: tree,
262
+ allowDuplicateInsertions: allowDuplicateInsertionsValue
263
+ )
206
264
207
- let keepGoing = indices. nextPermutation ( )
208
- guard keepGoing else {
209
- break
265
+ guard possibleOutputs. contains ( actualOutput) else {
266
+ XCTFail (
267
+ """
268
+
269
+ Actual output:
270
+ \" \( actualOutput) \"
271
+ Expected output:
272
+ either of \( possibleOutputs)
273
+ Edits:
274
+ \( editsPermutation)
275
+ allowDuplicateInsertions:
276
+ \( allowDuplicateInsertionsValue)
277
+ """
278
+ )
279
+
280
+ // Fail once for all permutations to avoid excessive logging.
281
+ continue NEXT_OUTPUT
282
+ }
283
+ }
284
+
285
+ let keepGoing = indices. nextPermutation ( )
286
+ guard keepGoing else {
287
+ break
288
+ }
210
289
}
211
290
}
212
291
}
213
292
214
293
/// Asserts that `output` matches the result of applying an array of edits to
215
- /// `input`, for all permutations of `edits`.
294
+ /// `input`, for all permutations of `edits` and for `allowDuplicateInsertions`
295
+ /// both `true` and `false`.
216
296
private func assertAppliedEdits(
217
297
to tree: SourceFileSyntax ,
218
298
edits: [ SourceEdit ] ,
219
299
output: String
220
300
) {
221
- assertAppliedEdits ( to: tree, edits: edits, possibleOutputs: [ output] )
301
+ assertAppliedEdits (
302
+ to: tree,
303
+ edits: edits,
304
+ outputs: [
305
+ . output( output, allowDuplicateInsertions: nil )
306
+ ]
307
+ )
222
308
}
223
309
224
310
// Grabbed from https://github.com/apple/swift-algorithms/blob/main/Sources/Algorithms/Permutations.swift
0 commit comments