Skip to content

Commit fa6c028

Browse files
austinzhengmoiseev
authored andcommitted
Adding tests for replaceSubrange overloads for range types (#2774)
1 parent c4191de commit fa6c028

File tree

6 files changed

+170
-59
lines changed

6 files changed

+170
-59
lines changed

stdlib/private/StdlibCollectionUnittest/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ add_swift_library(swiftStdlibCollectionUnittest SHARED IS_STDLIB
2323
CheckSequenceType.swift
2424
LoggingWrappers.swift.gyb
2525
MinimalCollections.swift.gyb
26+
RangeSelection.swift
2627
../../public/core/WriteBackMutableSlice.swift
2728

2829
SWIFT_MODULE_DEPENDS StdlibUnittest

stdlib/private/StdlibCollectionUnittest/CheckRangeReplaceableCollectionType.swift

+56-57
Original file line numberDiff line numberDiff line change
@@ -12,40 +12,6 @@
1212

1313
import StdlibUnittest
1414

15-
internal enum RangeSelection {
16-
case emptyRange
17-
case leftEdge
18-
case rightEdge
19-
case middle
20-
case leftHalf
21-
case rightHalf
22-
case offsets(Range<Int>)
23-
24-
internal func range<C : Collection>(in c: C) -> Range<C.Index> {
25-
switch self {
26-
case .emptyRange: return c.endIndex..<c.endIndex
27-
case .leftEdge: return c.startIndex..<c.startIndex
28-
case .rightEdge: return c.endIndex..<c.endIndex
29-
case .middle:
30-
let start = c.index(c.startIndex, offsetBy: c.count / 4)
31-
let end = c.index(c.startIndex, offsetBy: 3 * c.count / 4 + 1)
32-
return start..<end
33-
case .leftHalf:
34-
let start = c.startIndex
35-
let end = c.index(start, offsetBy: c.count / 2)
36-
return start..<end
37-
case .rightHalf:
38-
let start = c.index(c.startIndex, offsetBy: c.count / 2)
39-
let end = c.endIndex
40-
return start..<end
41-
case .offsets(let offsets):
42-
let start = c.index(c.startIndex, offsetBy: numericCast(offsets.lowerBound))
43-
let end = c.index(c.startIndex, offsetBy: numericCast(offsets.upperBound))
44-
return start..<end
45-
}
46-
}
47-
}
48-
4915
internal enum IndexSelection {
5016
case start
5117
case middle
@@ -62,22 +28,24 @@ internal enum IndexSelection {
6228
}
6329
}
6430

65-
internal struct ReplaceSubrangeTest {
66-
let collection: [OpaqueValue<Int>]
67-
let newElements: [OpaqueValue<Int>]
68-
let rangeSelection: RangeSelection
69-
let expected: [Int]
70-
let loc: SourceLoc
31+
public struct ReplaceSubrangeTest {
32+
public let collection: [OpaqueValue<Int>]
33+
public let newElements: [OpaqueValue<Int>]
34+
public let rangeSelection: RangeSelection
35+
public let expected: [Int]
36+
public let closedExpected: [Int]? // Expected array for closed ranges
37+
public let loc: SourceLoc
7138

7239
internal init(
7340
collection: [Int], newElements: [Int],
74-
rangeSelection: RangeSelection, expected: [Int],
41+
rangeSelection: RangeSelection, expected: [Int], closedExpected: [Int]? = nil,
7542
file: String = #file, line: UInt = #line
7643
) {
7744
self.collection = collection.map(OpaqueValue.init)
7845
self.newElements = newElements.map(OpaqueValue.init)
7946
self.rangeSelection = rangeSelection
8047
self.expected = expected
48+
self.closedExpected = closedExpected
8149
self.loc = SourceLoc(file, line, comment: "replaceSubrange() test data")
8250
}
8351
}
@@ -335,7 +303,9 @@ let appendContentsOfTests: [AppendContentsOfTest] = [
335303
expected: [1010, 2020, 3030, 4040, 5050, 6060, 7070, 8080]),
336304
]
337305

338-
let replaceRangeTests: [ReplaceSubrangeTest] = [
306+
// Also used in RangeReplaceable.swift.gyb to test `replaceSubrange()`
307+
// overloads with the countable range types.
308+
public let replaceRangeTests: [ReplaceSubrangeTest] = [
339309
ReplaceSubrangeTest(
340310
collection: [],
341311
newElements: [],
@@ -358,55 +328,70 @@ let replaceRangeTests: [ReplaceSubrangeTest] = [
358328
collection: [4040],
359329
newElements: [1010, 2020, 3030],
360330
rangeSelection: .leftEdge,
361-
expected: [1010, 2020, 3030, 4040]),
331+
expected: [1010, 2020, 3030, 4040],
332+
closedExpected: [1010, 2020, 3030]),
333+
334+
ReplaceSubrangeTest(
335+
collection: [1010, 2020, 3030],
336+
newElements: [4040],
337+
rangeSelection: .leftEdge,
338+
expected: [4040, 1010, 2020, 3030],
339+
closedExpected: [4040, 2020, 3030]),
362340

363341
ReplaceSubrangeTest(
364342
collection: [1010],
365343
newElements: [2020, 3030, 4040],
366344
rangeSelection: .rightEdge,
367-
expected: [1010, 2020, 3030, 4040]),
345+
expected: [1010, 2020, 3030, 4040],
346+
closedExpected: [2020, 3030, 4040]),
368347

369348
ReplaceSubrangeTest(
370349
collection: [1010, 2020, 3030],
371350
newElements: [4040],
372351
rangeSelection: .rightEdge,
373-
expected: [1010, 2020, 3030, 4040]),
352+
expected: [1010, 2020, 3030, 4040],
353+
closedExpected: [1010, 2020, 4040]),
374354

375355
ReplaceSubrangeTest(
376356
collection: [1010, 2020, 3030, 4040, 5050],
377357
newElements: [9090],
378-
rangeSelection: .offsets(1..<1),
379-
expected: [1010, 9090, 2020, 3030, 4040, 5050]),
358+
rangeSelection: .offsets(1, 1),
359+
expected: [1010, 9090, 2020, 3030, 4040, 5050],
360+
closedExpected: [1010, 9090, 3030, 4040, 5050]),
380361

381362
ReplaceSubrangeTest(
382363
collection: [1010, 2020, 3030, 4040, 5050],
383364
newElements: [9090],
384-
rangeSelection: .offsets(1..<2),
385-
expected: [1010, 9090, 3030, 4040, 5050]),
365+
rangeSelection: .offsets(1, 2),
366+
expected: [1010, 9090, 3030, 4040, 5050],
367+
closedExpected: [1010, 9090, 4040, 5050]),
386368

387369
ReplaceSubrangeTest(
388370
collection: [1010, 2020, 3030, 4040, 5050],
389371
newElements: [9090],
390-
rangeSelection: .offsets(1..<3),
391-
expected: [1010, 9090, 4040, 5050]),
372+
rangeSelection: .offsets(1, 3),
373+
expected: [1010, 9090, 4040, 5050],
374+
closedExpected: [1010, 9090, 5050]),
392375

393376
ReplaceSubrangeTest(
394377
collection: [1010, 2020, 3030, 4040, 5050],
395378
newElements: [9090],
396-
rangeSelection: .offsets(1..<4),
397-
expected: [1010, 9090, 5050]),
379+
rangeSelection: .offsets(1, 4),
380+
expected: [1010, 9090, 5050],
381+
closedExpected: [1010, 9090]),
398382

399383
ReplaceSubrangeTest(
400384
collection: [1010, 2020, 3030, 4040, 5050],
401385
newElements: [9090],
402-
rangeSelection: .offsets(1..<5),
386+
rangeSelection: .offsets(1, 5),
403387
expected: [1010, 9090]),
404388

405389
ReplaceSubrangeTest(
406390
collection: [1010, 2020, 3030],
407391
newElements: [8080, 9090],
408-
rangeSelection: .offsets(1..<2),
409-
expected: [1010, 8080, 9090, 3030]),
392+
rangeSelection: .offsets(1, 2),
393+
expected: [1010, 8080, 9090, 3030],
394+
closedExpected: [1010, 8080, 9090]),
410395
]
411396

412397
extension TestSuite {
@@ -499,7 +484,7 @@ self.test("\(testNamePrefix).init(Sequence)/semantics") {
499484
// replaceSubrange()
500485
//===----------------------------------------------------------------------===//
501486

502-
self.test("\(testNamePrefix).replaceSubrange()/semantics") {
487+
self.test("\(testNamePrefix).replaceSubrange()/range/semantics") {
503488
for test in replaceRangeTests {
504489
var c = makeWrappedCollection(test.collection)
505490
let rangeToReplace = test.rangeSelection.range(in: c)
@@ -513,6 +498,20 @@ self.test("\(testNamePrefix).replaceSubrange()/semantics") {
513498
}
514499
}
515500

501+
self.test("\(testNamePrefix).replaceSubrange()/closedRange/semantics") {
502+
for test in replaceRangeTests {
503+
guard let closedExpected = test.closedExpected else { continue }
504+
var c = makeWrappedCollection(test.collection)
505+
let rangeToReplace = test.rangeSelection.closedRange(in: c)
506+
let newElements = makeWrappedCollection(test.newElements)
507+
c.replaceSubrange(rangeToReplace, with: newElements)
508+
expectEqualSequence(
509+
closedExpected,
510+
c.map { extractValue($0).value },
511+
stackTrace: SourceLocStack().with(test.loc))
512+
}
513+
}
514+
516515
//===----------------------------------------------------------------------===//
517516
// append()
518517
//===----------------------------------------------------------------------===//

stdlib/private/StdlibCollectionUnittest/MinimalCollections.swift.gyb

+1-1
Original file line numberDiff line numberDiff line change
@@ -516,7 +516,7 @@ Int64 distances.
516516
% # Only generating MinimalRandomAccessCollectionWithStrideableIndex
517517
% if not StrideableIndex or (
518518
% StrideableIndex and Traversal == 'RandomAccess' and
519-
% not Mutable and not RangeReplaceable):
519+
% not Mutable):
520520

521521
/// A minimal implementation of `Collection` with extra checks.
522522
public struct ${Self}<T> : ${SelfProtocols} {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
//===--- RangeSelection.swift.gyb ----------------------------*- swift -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2016 - 2016 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See http://swift.org/LICENSE.txt for license information
9+
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import StdlibUnittest
14+
15+
public enum RangeSelection {
16+
case emptyRange
17+
case leftEdge
18+
case rightEdge
19+
case middle
20+
case leftHalf
21+
case rightHalf
22+
case offsets(Int, Int)
23+
24+
public func range<C : Collection>(in c: C) -> Range<C.Index> {
25+
switch self {
26+
case .emptyRange: return c.endIndex..<c.endIndex
27+
case .leftEdge: return c.startIndex..<c.startIndex
28+
case .rightEdge: return c.endIndex..<c.endIndex
29+
case .middle:
30+
let start = c.index(c.startIndex, offsetBy: c.count / 4)
31+
let end = c.index(c.startIndex, offsetBy: 3 * c.count / 4 + 1)
32+
return start..<end
33+
case .leftHalf:
34+
let start = c.startIndex
35+
let end = c.index(start, offsetBy: c.count / 2)
36+
return start..<end
37+
case .rightHalf:
38+
let start = c.index(c.startIndex, offsetBy: c.count / 2)
39+
let end = c.endIndex
40+
return start..<end
41+
case let .offsets(lowerBound, upperBound):
42+
let start = c.index(c.startIndex, offsetBy: numericCast(lowerBound))
43+
let end = c.index(c.startIndex, offsetBy: numericCast(upperBound))
44+
return start..<end
45+
}
46+
}
47+
48+
public func countableRange<C : Collection>(in c: C) -> CountableRange<C.Index> {
49+
return CountableRange(range(in: c))
50+
}
51+
52+
public func closedRange<C : Collection>(in c: C) -> ClosedRange<C.Index> {
53+
switch self {
54+
case .emptyRange: fatalError("Closed range cannot be empty")
55+
case .leftEdge: return c.startIndex...c.startIndex
56+
case .rightEdge:
57+
let beforeEnd = c.index(c.startIndex, offsetBy: c.count - 1)
58+
return beforeEnd...beforeEnd
59+
case .middle:
60+
let start = c.index(c.startIndex, offsetBy: c.count / 4)
61+
let end = c.index(c.startIndex, offsetBy: 3 * c.count / 4)
62+
return start...end
63+
case .leftHalf:
64+
let start = c.startIndex
65+
let end = c.index(start, offsetBy: c.count / 2 - 1)
66+
return start...end
67+
case .rightHalf:
68+
let start = c.index(c.startIndex, offsetBy: c.count / 2)
69+
let beforeEnd = c.index(c.startIndex, offsetBy: c.count - 1)
70+
return start...beforeEnd
71+
case let .offsets(lowerBound, upperBound):
72+
let start = c.index(c.startIndex, offsetBy: numericCast(lowerBound))
73+
let end = c.index(c.startIndex, offsetBy: numericCast(upperBound))
74+
return start...end
75+
}
76+
}
77+
78+
public func countableClosedRange<C : Collection>(in c: C) -> CountableClosedRange<C.Index> {
79+
return CountableClosedRange(closedRange(in: c))
80+
}
81+
}

stdlib/public/core/RangeReplaceableCollection.swift.gyb

-1
Original file line numberDiff line numberDiff line change
@@ -903,7 +903,6 @@ extension RangeReplaceableCollection
903903
_ subrange: ${Range}<Index>,
904904
with newElements: C
905905
) where C : Collection, C.Iterator.Element == Iterator.Element {
906-
// FIXME: swift-3-indexing-model: tests.
907906
self.replaceSubrange(_makeHalfOpen(subrange), with: newElements)
908907
}
909908

validation-test/stdlib/RangeReplaceable.swift.gyb

+31
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,37 @@ RangeReplaceableTestSuite.test("removeAll/dispatch") {
8181
expectCustomizable(tester, tester.log.removeAll)
8282
}
8383

84+
RangeReplaceableTestSuite.test("replaceSubrange/countableRange") {
85+
for test in replaceRangeTests {
86+
var c =
87+
MinimalRangeReplaceableRandomAccessCollectionWithStrideableIndex(elements: test.collection)
88+
let rangeToReplace = test.rangeSelection.countableRange(in: c)
89+
let newElements =
90+
MinimalRangeReplaceableRandomAccessCollectionWithStrideableIndex(elements: test.newElements)
91+
c.replaceSubrange(rangeToReplace, with: newElements)
92+
expectEqualSequence(
93+
test.expected,
94+
c.map { $0.value },
95+
stackTrace: SourceLocStack().with(test.loc))
96+
}
97+
}
98+
99+
RangeReplaceableTestSuite.test("replaceSubrange/countableClosedRange") {
100+
for test in replaceRangeTests {
101+
guard let closedExpected = test.closedExpected else { continue }
102+
var c =
103+
MinimalRangeReplaceableRandomAccessCollectionWithStrideableIndex(elements: test.collection)
104+
let rangeToReplace = test.rangeSelection.countableClosedRange(in: c)
105+
let newElements =
106+
MinimalRangeReplaceableRandomAccessCollectionWithStrideableIndex(elements: test.newElements)
107+
c.replaceSubrange(rangeToReplace, with: newElements)
108+
expectEqualSequence(
109+
closedExpected,
110+
c.map { $0.value },
111+
stackTrace: SourceLocStack().with(test.loc))
112+
}
113+
}
114+
84115
RangeReplaceableTestSuite.test("reserveCapacity/dispatch") {
85116
var tester = RangeReplaceableCollectionLog.dispatchTester(Array<Int>())
86117
tester.reserveCapacity(10)

0 commit comments

Comments
 (0)