mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
* Updating availability versions * Remove all remaining overlays in stdlib/public/Darwin/*: - ObjectiveC - Dispatch - CoreFoundation - CoreGraphics - Foundation
816 lines
24 KiB
Swift
816 lines
24 KiB
Swift
// RUN: %target-run-simple-swiftgyb
|
|
// REQUIRES: executable_test
|
|
|
|
import StdlibUnittest
|
|
import StdlibCollectionUnittest
|
|
|
|
extension Range {
|
|
static var _isHalfOpen: Bool { return true }
|
|
}
|
|
extension ClosedRange {
|
|
static var _isHalfOpen: Bool { return false }
|
|
}
|
|
|
|
protocol TestProtocol1 {}
|
|
|
|
struct ContainsTest {
|
|
let lowerBound: Int
|
|
let upperBound: Int
|
|
let value: Int
|
|
let loc: SourceLoc
|
|
|
|
var containedInHalfOpen: Bool {
|
|
return lowerBound <= value && value < upperBound
|
|
}
|
|
|
|
var containedInClosed: Bool {
|
|
return lowerBound <= value && value <= upperBound
|
|
}
|
|
|
|
init(
|
|
lowerBound: Int,
|
|
upperBound: Int,
|
|
value: Int,
|
|
file: String = #file, line: UInt = #line
|
|
) {
|
|
self.lowerBound = lowerBound
|
|
self.upperBound = upperBound
|
|
self.value = value
|
|
self.loc = SourceLoc(file, line, comment: "test data")
|
|
}
|
|
}
|
|
|
|
func generateContainsTests() -> [ContainsTest] {
|
|
let bounds = [ Int.min, -30, -10, 0, 10, 20, Int.max ]
|
|
var result: [ContainsTest] = []
|
|
for lowerBound in bounds {
|
|
for upperBound in bounds {
|
|
if lowerBound > upperBound { continue }
|
|
for value in bounds {
|
|
result.append(
|
|
ContainsTest(
|
|
lowerBound: lowerBound, upperBound: upperBound,
|
|
value: value))
|
|
}
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
let containsTests: [ContainsTest] = generateContainsTests()
|
|
|
|
infix operator ..<* : RangeFormationPrecedence
|
|
infix operator ...* : RangeFormationPrecedence
|
|
|
|
enum VariantRange {
|
|
case halfOpen(lowerBound: Int, upperBound: Int)
|
|
case closed(lowerBound: Int, upperBound: Int)
|
|
|
|
var isHalfOpen: Bool {
|
|
switch self {
|
|
case .halfOpen:
|
|
return true
|
|
case .closed:
|
|
return false
|
|
}
|
|
}
|
|
|
|
var lowerBound: Int {
|
|
switch self {
|
|
case .halfOpen(let result, _):
|
|
return result
|
|
case .closed(let result, _):
|
|
return result
|
|
}
|
|
}
|
|
|
|
var upperBound: Int {
|
|
switch self {
|
|
case .halfOpen(_, let result):
|
|
return result
|
|
case .closed(_, let result):
|
|
return result
|
|
}
|
|
}
|
|
}
|
|
|
|
func ..<* (lhs: Int, rhs: Int) -> VariantRange {
|
|
return .halfOpen(lowerBound: lhs, upperBound: rhs)
|
|
}
|
|
|
|
func ...* (lhs: Int, rhs: Int) -> VariantRange {
|
|
return .closed(lowerBound: lhs, upperBound: rhs)
|
|
}
|
|
|
|
struct OverlapsTest {
|
|
let expected: Bool
|
|
let lhs: VariantRange
|
|
let rhs: VariantRange
|
|
let loc: SourceLoc
|
|
|
|
init(
|
|
expected: Bool,
|
|
lhs: VariantRange,
|
|
rhs: VariantRange,
|
|
file: String = #file, line: UInt = #line
|
|
) {
|
|
self.expected = expected
|
|
self.lhs = lhs
|
|
self.rhs = rhs
|
|
self.loc = SourceLoc(file, line, comment: "test data")
|
|
}
|
|
}
|
|
|
|
let overlapsTests: [OverlapsTest] = [
|
|
// 0-4, 5-10
|
|
OverlapsTest(expected: false, lhs: 0..<*4, rhs: 5..<*10),
|
|
OverlapsTest(expected: false, lhs: 0..<*4, rhs: 5...*10),
|
|
OverlapsTest(expected: false, lhs: 0...*4, rhs: 5..<*10),
|
|
OverlapsTest(expected: false, lhs: 0...*4, rhs: 5...*10),
|
|
|
|
// 0-5, 5-10
|
|
OverlapsTest(expected: false, lhs: 0..<*5, rhs: 5..<*10),
|
|
OverlapsTest(expected: false, lhs: 0..<*5, rhs: 5...*10),
|
|
OverlapsTest(expected: true, lhs: 0...*5, rhs: 5..<*10),
|
|
OverlapsTest(expected: true, lhs: 0...*5, rhs: 5...*10),
|
|
|
|
// 0-6, 5-10
|
|
OverlapsTest(expected: true, lhs: 0..<*6, rhs: 5..<*10),
|
|
OverlapsTest(expected: true, lhs: 0..<*6, rhs: 5...*10),
|
|
OverlapsTest(expected: true, lhs: 0...*6, rhs: 5..<*10),
|
|
OverlapsTest(expected: true, lhs: 0...*6, rhs: 5...*10),
|
|
|
|
// 0-20, 5-10
|
|
OverlapsTest(expected: true, lhs: 0..<*20, rhs: 5..<*10),
|
|
OverlapsTest(expected: true, lhs: 0..<*20, rhs: 5...*10),
|
|
OverlapsTest(expected: true, lhs: 0...*20, rhs: 5..<*10),
|
|
OverlapsTest(expected: true, lhs: 0...*20, rhs: 5...*10),
|
|
|
|
// 0-0, 0-5
|
|
OverlapsTest(expected: false, lhs: 0..<*0, rhs: 0..<*5),
|
|
OverlapsTest(expected: false, lhs: 0..<*0, rhs: 0...*5),
|
|
]
|
|
|
|
struct ClampedTest {
|
|
let expected: Range<Int>
|
|
let subject: Range<Int>
|
|
let limits: Range<Int>
|
|
let loc: SourceLoc
|
|
|
|
init(
|
|
expected: Range<Int>,
|
|
subject: Range<Int>,
|
|
limits: Range<Int>,
|
|
file: String = #file, line: UInt = #line
|
|
) {
|
|
self.expected = expected
|
|
self.subject = subject
|
|
self.limits = limits
|
|
self.loc = SourceLoc(file, line, comment: "test data")
|
|
}
|
|
}
|
|
|
|
let clampedTests: [ClampedTest] = [
|
|
ClampedTest(expected: 5..<5, subject: 0..<3, limits: 5..<10),
|
|
ClampedTest(expected: 5..<9, subject: 0..<9, limits: 5..<10),
|
|
ClampedTest(expected: 5..<10, subject: 0..<13, limits: 5..<10),
|
|
ClampedTest(expected: 7..<9, subject: 7..<9, limits: 5..<10),
|
|
ClampedTest(expected: 7..<10, subject: 7..<13, limits: 5..<10),
|
|
ClampedTest(expected: 10..<10, subject: 13..<15, limits: 5..<10),
|
|
]
|
|
|
|
struct OffsetByTest {
|
|
static let start: Int = 10
|
|
static let end: Int = 20
|
|
let advanceBy: Int
|
|
|
|
/// Instead of advancing from `startIndex`, advance from `endIndex`.
|
|
let advanceFromEnd: Bool
|
|
|
|
// `nil` if the test should fail for open ranges.
|
|
let newOpenIndex: Int?
|
|
// `nil` if the test should fail for closed ranges.
|
|
let newClosedIndex: Int?
|
|
|
|
let loc: SourceLoc
|
|
|
|
init(
|
|
advanceBy: Int, newOpenIndex: Int? = nil, newClosedIndex: Int? = nil,
|
|
advanceFromEnd: Bool = false, file: String = #file, line: UInt = #line
|
|
) {
|
|
self.advanceBy = advanceBy
|
|
self.newOpenIndex = newOpenIndex
|
|
self.newClosedIndex = newClosedIndex
|
|
self.advanceFromEnd = advanceFromEnd
|
|
self.loc = SourceLoc(file, line, comment: "index(_:offsetBy:) tests for countable ranges")
|
|
}
|
|
}
|
|
|
|
let offsetByTests: [OffsetByTest] = [
|
|
// Move forward, valid.
|
|
OffsetByTest(advanceBy: 4, newOpenIndex: 14, newClosedIndex: 14),
|
|
OffsetByTest(advanceBy: 0, newOpenIndex: 10, newClosedIndex: 10),
|
|
OffsetByTest(advanceBy: 10, newOpenIndex: 20, newClosedIndex: 20),
|
|
OffsetByTest(advanceBy: 11, newClosedIndex: 21),
|
|
|
|
// Move forward, invalid.
|
|
OffsetByTest(advanceBy: 12),
|
|
OffsetByTest(advanceBy: 25600),
|
|
|
|
// Move backward, valid.
|
|
OffsetByTest(advanceBy: -9, newOpenIndex: 11, newClosedIndex: 12,
|
|
advanceFromEnd: true),
|
|
OffsetByTest(advanceBy: -10, newOpenIndex: 10, newClosedIndex: 11,
|
|
advanceFromEnd: true),
|
|
OffsetByTest(advanceBy: -11, newClosedIndex: 10,
|
|
advanceFromEnd: true),
|
|
OffsetByTest(advanceBy: 0, newOpenIndex: 20, newClosedIndex: 21,
|
|
advanceFromEnd: true),
|
|
|
|
// Move backward, invalid.
|
|
OffsetByTest(advanceBy: -12, advanceFromEnd: true),
|
|
OffsetByTest(advanceBy: -2885, advanceFromEnd: true),
|
|
]
|
|
|
|
struct IndexDistanceTest {
|
|
static let start: Int = 10
|
|
static let end: Int = 30
|
|
|
|
// Leave either of these `nil` to use the 'right edge index', which differs
|
|
// between open and closed ranges.
|
|
let leftIndex: Int?
|
|
let rightIndex: Int?
|
|
|
|
let loc: SourceLoc
|
|
let openDistance: Int
|
|
let closedDistance: Int
|
|
|
|
/// Return the index `leftIndex` steps away from `start`, or the collection's
|
|
/// `endIndex` if `leftIndex` is `nil`.
|
|
func leftIndex<C: RandomAccessCollection>(of c: C) -> C.Index {
|
|
guard let leftIndex = leftIndex else {
|
|
return c.endIndex
|
|
}
|
|
let iterations = leftIndex - IndexDistanceTest.start
|
|
var idx = c.startIndex
|
|
for _ in 0..<iterations {
|
|
idx = c.index(after: idx)
|
|
}
|
|
return idx
|
|
}
|
|
|
|
/// Return the index `rightIndex` steps away from `start`, or the
|
|
/// collection's `endIndex` if `rightIndex` is `nil`.
|
|
func rightIndex<C: RandomAccessCollection>(of c: C) -> C.Index {
|
|
guard let rightIndex = rightIndex else {
|
|
return c.endIndex
|
|
}
|
|
let iterations = rightIndex - IndexDistanceTest.start
|
|
precondition(iterations >= 0)
|
|
var idx = c.startIndex
|
|
for _ in 0..<iterations {
|
|
idx = c.index(after: idx)
|
|
}
|
|
return idx
|
|
}
|
|
|
|
init(
|
|
leftIndex: Int? = nil, rightIndex: Int? = nil,
|
|
distance: Int, closedDistance: Int? = nil,
|
|
file: String = #file, line: UInt = #line
|
|
) {
|
|
self.leftIndex = leftIndex
|
|
self.rightIndex = rightIndex
|
|
self.openDistance = distance
|
|
// Only set closedDistance if testing against someRange.endIndex.
|
|
if closedDistance != nil {
|
|
precondition(leftIndex == nil || rightIndex == nil)
|
|
}
|
|
self.closedDistance = closedDistance ?? distance
|
|
self.loc = SourceLoc(file, line, comment: "distance(from:to:) tests for countable ranges")
|
|
}
|
|
}
|
|
|
|
let indexDistanceTests: [IndexDistanceTest] = [
|
|
// Forward, inside bounds.
|
|
IndexDistanceTest(leftIndex: 10, rightIndex: 29, distance: 19),
|
|
IndexDistanceTest(leftIndex: 15, rightIndex: 15, distance: 0),
|
|
IndexDistanceTest(leftIndex: 16, rightIndex: 29, distance: 13),
|
|
// Forward, at edge.
|
|
IndexDistanceTest(leftIndex: 10, distance: 20, closedDistance: 21),
|
|
IndexDistanceTest(leftIndex: 21, distance: 9, closedDistance: 10),
|
|
IndexDistanceTest(leftIndex: IndexDistanceTest.end, distance: 0, closedDistance: 1),
|
|
// Backwards, inside bounds.
|
|
IndexDistanceTest(leftIndex: 27, rightIndex: 16, distance: -11),
|
|
IndexDistanceTest(leftIndex: 15, rightIndex: 14, distance: -1),
|
|
// Backwards, at edge.
|
|
IndexDistanceTest(rightIndex: 18, distance: -12, closedDistance: -13),
|
|
IndexDistanceTest(rightIndex: 10, distance: -20, closedDistance: -21),
|
|
IndexDistanceTest(rightIndex: IndexDistanceTest.end, distance: 0, closedDistance: -1),
|
|
// Completely at edge.
|
|
IndexDistanceTest(distance: 0),
|
|
]
|
|
|
|
%{
|
|
all_range_types = [
|
|
('Range', '..<', 'MinimalComparableValue', ''),
|
|
('Range', '..<', 'MinimalStrideableValue', 'Countable'),
|
|
('ClosedRange', '...', 'MinimalComparableValue', ''),
|
|
('ClosedRange', '...', 'MinimalStrideableValue', 'Countable'),
|
|
]
|
|
}%
|
|
|
|
% for (Self, op, Bound, Countable) in all_range_types:
|
|
% TestSuite = Countable + Self + 'TestSuite'
|
|
|
|
% if Countable == '':
|
|
// Check that the generic parameter is called 'Bound'.
|
|
extension ${Self} where Bound : TestProtocol1 {
|
|
var _elementIsTestProtocol1: Bool {
|
|
fatalError("not implemented")
|
|
}
|
|
}
|
|
% end
|
|
|
|
var ${TestSuite} = TestSuite("${Self}")
|
|
|
|
${TestSuite}.test("init(uncheckedBounds:)")
|
|
.forEach(in: [(1, 2), (1, 1)]) {
|
|
(lowerInt, upperInt) in
|
|
|
|
let r = ${Self}(
|
|
uncheckedBounds: (lower: ${Bound}(lowerInt), upper: ${Bound}(upperInt)))
|
|
expectEqual(lowerInt, r.lowerBound.value)
|
|
expectEqual(upperInt, r.upperBound.value)
|
|
}
|
|
|
|
if #available(macOS 11.3, iOS 14.5, tvOS 14.5, watchOS 7.4, *) {
|
|
// Debug check was introduced in https://github.com/apple/swift/pull/34961
|
|
${TestSuite}.test("init(uncheckedBounds:) with invalid values")
|
|
.xfail(
|
|
.custom(
|
|
{ _isDebugAssertConfiguration() },
|
|
reason: "unchecked initializer still checks its input in Debug mode"))
|
|
.code {
|
|
let low = 2
|
|
let up = 1
|
|
// Check that 'init(uncheckedBounds:)' does not perform precondition checks,
|
|
// allowing to create ranges that break invariants.
|
|
let r = ${Self}(
|
|
uncheckedBounds: (lower: ${Bound}(low), upper: ${Bound}(up)))
|
|
expectEqual(low, r.lowerBound.value)
|
|
expectEqual(up, r.upperBound.value)
|
|
}
|
|
}
|
|
|
|
% for (DestinationSelf, _, _, _) in all_range_types:
|
|
${TestSuite}.test("init(${DestinationSelf})/whereBoundIsStrideable")
|
|
.forEach(in: [(0, 0), (1, 2), (10, 20), (Int.min, Int.max)]) {
|
|
(lowerInt, upperInt) in
|
|
|
|
let lower = MinimalStrideableValue(lowerInt)
|
|
let upper = MinimalStrideableValue(upperInt)
|
|
let source: ${Self}<MinimalStrideableValue> = lower${op}upper
|
|
|
|
let isSourceHalfOpen = ${Self}<MinimalStrideableValue>._isHalfOpen
|
|
let isDestinationHalfOpen =
|
|
${DestinationSelf}<MinimalStrideableValue>._isHalfOpen
|
|
|
|
let shouldTrap =
|
|
(source.isEmpty && !isDestinationHalfOpen) ||
|
|
(upperInt == Int.max && !isSourceHalfOpen && isDestinationHalfOpen)
|
|
if shouldTrap {
|
|
expectCrashLater()
|
|
}
|
|
|
|
let converted = ${DestinationSelf}(source)
|
|
|
|
if !shouldTrap {
|
|
expectEqual(lower.value, converted.lowerBound.value)
|
|
expectEqual(
|
|
upper.value + ((isSourceHalfOpen ? 0 : 1) - (isDestinationHalfOpen ? 0 : 1)),
|
|
converted.upperBound.value)
|
|
}
|
|
}
|
|
% end
|
|
|
|
${TestSuite}.test("lowerBound, upperBound") {
|
|
let _1 = ${Bound}(1, identity: 1010)
|
|
let _2 = ${Bound}(2, identity: 2020)
|
|
let range: ${Self}<${Bound}> = _1${op}_2
|
|
expectEqual(1, range.lowerBound.value)
|
|
expectEqual(1010, range.lowerBound.identity)
|
|
expectEqual(2, range.upperBound.value)
|
|
expectEqual(2020, range.upperBound.identity)
|
|
}
|
|
|
|
${TestSuite}.test("Equatable") {
|
|
let _1 = ${Bound}(1)
|
|
let _2 = ${Bound}(2)
|
|
|
|
let instances: [${Self}<${Bound}>] = [
|
|
_1${op}_1,
|
|
_1${op}_2,
|
|
_2${op}_2,
|
|
]
|
|
checkEquatable(instances, oracle: { $0 == $1 })
|
|
}
|
|
|
|
${TestSuite}.test("'${op}' traps when upperBound < lowerBound")
|
|
.crashOutputMatches(_isDebugAssertConfiguration() ?
|
|
"Range requires lowerBound <= upperBound" : "")
|
|
.code {
|
|
let _1 = ${Bound}(1)
|
|
let _2 = ${Bound}(2)
|
|
expectCrashLater()
|
|
let range: ${Self}<${Bound}> = _2${op}_1
|
|
_blackHole(range)
|
|
}
|
|
|
|
${TestSuite}.test("contains(_:)/staticDispatch") {
|
|
let start = ${Bound}(10)
|
|
let end = ${Bound}(20)
|
|
let range: ${Self}<${Bound}> = start${op}end
|
|
expectEqual(1, ${Bound}.timesLessWasCalled.value)
|
|
for test in 0..<30 {
|
|
% if 'Closed' in Self:
|
|
let expected = test >= start.value && test <= end.value
|
|
% else:
|
|
let expected = test >= start.value && test < end.value
|
|
% end
|
|
expectEqual(
|
|
expected, range.contains(${Bound}(test)),
|
|
"test=\(test)")
|
|
}
|
|
expectEqual(51, ${Bound}.timesLessWasCalled.value)
|
|
}
|
|
|
|
${TestSuite}.test("~=/staticDispatch") {
|
|
let start = ${Bound}(10)
|
|
let end = ${Bound}(20)
|
|
let range: ${Self}<${Bound}> = start${op}end
|
|
expectEqual(1, ${Bound}.timesLessWasCalled.value)
|
|
for test in 0..<30 {
|
|
% if 'Closed' in Self:
|
|
let expected = test >= start.value && test <= end.value
|
|
% else:
|
|
let expected = test >= start.value && test < end.value
|
|
% end
|
|
expectEqual(
|
|
expected, range ~= ${Bound}(test),
|
|
"test=\(test)")
|
|
}
|
|
expectEqual(51, ${Bound}.timesLessWasCalled.value)
|
|
}
|
|
|
|
% if 'Strideable' in Bound:
|
|
${TestSuite}.test("contains(_:)/dynamicDispatch") {
|
|
let start = ${Bound}(10)
|
|
let end = ${Bound}(20)
|
|
let range: ${Self}<${Bound}> = start${op}end
|
|
let loggingRange = LoggingCollection(wrapping: range)
|
|
expectEqual(1, ${Bound}.timesLessWasCalled.value)
|
|
for test in 0..<30 {
|
|
% if 'Closed' in Self:
|
|
let expected = test >= start.value && test <= end.value
|
|
% else:
|
|
let expected = test >= start.value && test < end.value
|
|
% end
|
|
expectEqual(
|
|
expected, loggingRange.contains(${Bound}(test)),
|
|
"test=\(test)")
|
|
}
|
|
expectEqual(51, MinimalStrideableValue.timesLessWasCalled.value)
|
|
}
|
|
% end
|
|
|
|
${TestSuite}.test("contains(_:)/semantics, ~=/semantics")
|
|
.forEach(in: containsTests) {
|
|
(test) in
|
|
|
|
// Check both static and dynamic dispatch.
|
|
let range: ${Self}<${Bound}> = ${Bound}(test.lowerBound)${op}${Bound}(test.upperBound)
|
|
% if 'Strideable' in Bound:
|
|
let loggingRange = LoggingCollection(wrapping: range)
|
|
% else:
|
|
let loggingRange = range
|
|
% end
|
|
|
|
let value = ${Bound}(test.value)
|
|
let expected =
|
|
${Self}<${Bound}>._isHalfOpen
|
|
? test.containedInHalfOpen
|
|
: test.containedInClosed
|
|
expectEqual(expected, range.contains(value))
|
|
expectEqual(expected, loggingRange.contains(value))
|
|
expectEqual(expected, range ~= value)
|
|
}
|
|
|
|
% for (OtherSelf, other_op, OtherBound, __elementIsTestProtocol1) in all_range_types:
|
|
${TestSuite}.test("overlaps(${OtherSelf})/semantics")
|
|
.forEach(in: overlapsTests) {
|
|
(test) in
|
|
|
|
if test.lhs.isHalfOpen != ${Self}<${Bound}>._isHalfOpen ||
|
|
test.rhs.isHalfOpen != ${OtherSelf}<${OtherBound}>._isHalfOpen {
|
|
return
|
|
}
|
|
|
|
let lhs: ${Self}<${Bound}>
|
|
= ${Bound}(test.lhs.lowerBound)${op}${Bound}(test.lhs.upperBound)
|
|
let rhs: ${Self}<${Bound}>
|
|
= ${Bound}(test.rhs.lowerBound)${op}${Bound}(test.rhs.upperBound)
|
|
|
|
expectEqual(test.expected, lhs.overlaps(rhs))
|
|
expectEqual(test.expected, rhs.overlaps(lhs))
|
|
|
|
expectEqual(!lhs.isEmpty, lhs.overlaps(lhs))
|
|
expectEqual(!rhs.isEmpty, rhs.overlaps(rhs))
|
|
}
|
|
% end
|
|
|
|
${TestSuite}.test("clamped(to:)/semantics")
|
|
.forEach(in: clampedTests) {
|
|
(test) in
|
|
|
|
let subject: ${Self}<${Bound}>
|
|
= ${Bound}(test.subject.lowerBound)${op}${Bound}(test.subject.upperBound)
|
|
let limits: ${Self}<${Bound}>
|
|
= ${Bound}(test.limits.lowerBound)${op}${Bound}(test.limits.upperBound)
|
|
|
|
expectEqual(
|
|
${Bound}(test.expected.lowerBound)${op}${Bound}(test.expected.upperBound),
|
|
subject.clamped(to: limits))
|
|
}
|
|
|
|
${TestSuite}.test("count/whereBoundIsStrideable") {
|
|
typealias Bound = MinimalStrideableValue
|
|
let start = Bound(10)
|
|
let end = Bound(20)
|
|
let range1: ${Self}<Bound> = start${op}start
|
|
let range2: ${Self}<Bound> = start${op}end
|
|
expectEqual(0, Bound.timesEqualEqualWasCalled.value)
|
|
expectEqual(2, Bound.timesLessWasCalled.value)
|
|
expectEqual(0, Bound.timesDistanceWasCalled.value)
|
|
expectEqual(0, Bound.timesAdvancedWasCalled.value)
|
|
expectEqual( 0 + (${Self}<Bound>._isHalfOpen ? 0 : 1), range1.count)
|
|
expectEqual(10 + (${Self}<Bound>._isHalfOpen ? 0 : 1), range2.count)
|
|
expectEqual(0, Bound.timesEqualEqualWasCalled.value)
|
|
expectEqual(2, Bound.timesLessWasCalled.value)
|
|
expectEqual(2, Bound.timesDistanceWasCalled.value)
|
|
expectEqual(0, Bound.timesAdvancedWasCalled.value)
|
|
}
|
|
|
|
% if 'Strideable' in Bound:
|
|
${TestSuite}.test("index(after:)/semantics").forEach(in: [3, 5, 10, 12, 86]) {
|
|
(endValue) in
|
|
let start = ${Bound}(0)
|
|
let end = ${Bound}(endValue)
|
|
let range: ${Self}<${Bound}> = start${op}end
|
|
var idx = range.startIndex
|
|
var previousValue: Int?
|
|
|
|
for _ in 0${op}(endValue - 1) {
|
|
// Advance the index to the last in-range position.
|
|
idx = range.index(after: idx)
|
|
if let previousValue = previousValue {
|
|
expectEqual(range[idx].value, previousValue + 1)
|
|
}
|
|
previousValue = range[idx].value
|
|
}
|
|
// Iterate once more to get to the 'after end' position.
|
|
idx = range.index(after: idx)
|
|
expectEqual(idx, range.endIndex)
|
|
// Now, iterate once more and expect a crash.
|
|
expectCrashLater()
|
|
_ = range.index(after: idx)
|
|
}
|
|
|
|
${TestSuite}.test("index(after:)/semantics/smallestRange") {
|
|
let range: ${Self}<${Bound}> = ${Bound}(0)${op}${Bound}(0)
|
|
var idx = range.startIndex
|
|
|
|
% if 'Closed' in Self:
|
|
// Should be able to advance the index once.
|
|
idx = range.index(after: idx)
|
|
% end
|
|
|
|
expectCrashLater()
|
|
_ = range.index(after: idx)
|
|
}
|
|
|
|
${TestSuite}.test("index(before:)/semantics").forEach(in: [3, 5, 10, 12, 86]) {
|
|
(endValue) in
|
|
let start = ${Bound}(0)
|
|
let end = ${Bound}(endValue)
|
|
let range: ${Self}<${Bound}> = start${op}end
|
|
var idx = range.endIndex
|
|
var previousValue: Int?
|
|
|
|
for _ in 0${op}(endValue) {
|
|
// Advance the index backwards until we reach `startIndex`.
|
|
// Precondition: for a..<b, advance endIndex (b - a) times to reach startIndex.
|
|
// Precondition: for a...b, advance endIndex (b - a) + 1 times to reach startIndex.
|
|
idx = range.index(before: idx)
|
|
if let previousValue = previousValue {
|
|
expectEqual(range[idx].value, previousValue - 1)
|
|
}
|
|
previousValue = range[idx].value
|
|
}
|
|
// At this point, we should be at `startIndex`.
|
|
expectEqual(idx, range.startIndex)
|
|
// Now, iterate once more and expect a crash.
|
|
expectCrashLater()
|
|
_ = range.index(before: idx)
|
|
}
|
|
|
|
${TestSuite}.test("index(before:)/semantics/smallestRange") {
|
|
let range: ${Self}<${Bound}> = ${Bound}(0)${op}${Bound}(0)
|
|
var idx = range.endIndex
|
|
|
|
% if 'Closed' in Self:
|
|
// Should be able to advance the index once.
|
|
idx = range.index(before: idx)
|
|
% end
|
|
|
|
expectCrashLater()
|
|
_ = range.index(before: idx)
|
|
}
|
|
|
|
${TestSuite}.test("index(offsetBy:)/semantics").forEach(in: offsetByTests) {
|
|
(test) in
|
|
let start = ${Bound}(OffsetByTest.start)
|
|
let end = ${Bound}(OffsetByTest.end)
|
|
let range: ${Self}<${Bound}> = start${op}end
|
|
let initialIdx = test.advanceFromEnd ? range.endIndex : range.startIndex
|
|
|
|
% if 'Closed' in Self:
|
|
if let newIndex = test.newClosedIndex {
|
|
% else:
|
|
if let newIndex = test.newOpenIndex {
|
|
% end
|
|
let idx = range.index(initialIdx, offsetBy: test.advanceBy)
|
|
if idx != range.endIndex {
|
|
expectEqual(newIndex, range[idx].value, stackTrace: SourceLocStack().with(test.loc))
|
|
}
|
|
} else {
|
|
expectCrashLater()
|
|
_ = range.index(initialIdx, offsetBy: test.advanceBy)
|
|
}
|
|
}
|
|
|
|
${TestSuite}.test("index(offsetBy:)/semantics/smallestRange/forward") {
|
|
let range: ${Self}<${Bound}> = ${Bound}(0)${op}${Bound}(0)
|
|
var idx = range.startIndex
|
|
|
|
// This should work.
|
|
_ = range.index(idx, offsetBy: 0)
|
|
|
|
% if 'Closed' in Self:
|
|
// Can advance once more.
|
|
_ = range.index(idx, offsetBy: 1)
|
|
expectCrashLater()
|
|
_ = range.index(idx, offsetBy: 2)
|
|
% else:
|
|
expectCrashLater()
|
|
_ = range.index(idx, offsetBy: 1)
|
|
% end
|
|
}
|
|
|
|
${TestSuite}.test("index(offsetBy:)/semantics/smallestRange/backward") {
|
|
let range: ${Self}<${Bound}> = ${Bound}(0)${op}${Bound}(0)
|
|
var idx = range.endIndex
|
|
|
|
// This should work.
|
|
_ = range.index(idx, offsetBy: 0)
|
|
|
|
% if 'Closed' in Self:
|
|
// Can advance once more.
|
|
_ = range.index(idx, offsetBy: -1)
|
|
expectCrashLater()
|
|
_ = range.index(idx, offsetBy: -2)
|
|
% else:
|
|
expectCrashLater()
|
|
_ = range.index(idx, offsetBy: -1)
|
|
% end
|
|
}
|
|
|
|
${TestSuite}.test("distance(from:to:)/semantics") {
|
|
for test in indexDistanceTests {
|
|
let start = ${Bound}(IndexDistanceTest.start)
|
|
let end = ${Bound}(IndexDistanceTest.end)
|
|
let range: ${Self}<${Bound}> = start${op}end
|
|
let leftIdx = test.leftIndex(of: range)
|
|
let rightIdx = test.rightIndex(of: range)
|
|
|
|
let distance = range.distance(from: leftIdx, to: rightIdx)
|
|
% if 'Closed' in Self:
|
|
expectEqual(test.closedDistance, distance, stackTrace: SourceLocStack().with(test.loc))
|
|
% else:
|
|
expectEqual(test.openDistance, distance, stackTrace: SourceLocStack().with(test.loc))
|
|
% end
|
|
}
|
|
}
|
|
|
|
${TestSuite}.test("distance(from:to:)/semantics/smallestRange") {
|
|
let range: ${Self}<${Bound}> = ${Bound}(0)${op}${Bound}(0)
|
|
var idx = range.endIndex
|
|
|
|
% if 'Closed' in Self:
|
|
expectEqual(1, range.distance(from: range.startIndex, to: range.endIndex))
|
|
% else:
|
|
expectEqual(0, range.distance(from: range.startIndex, to: range.endIndex))
|
|
% end
|
|
}
|
|
|
|
% end
|
|
|
|
${TestSuite}.test("isEmpty") {
|
|
let start = ${Bound}(10)
|
|
let end = ${Bound}(20)
|
|
let range1: ${Self}<${Bound}> = start${op}start
|
|
let range2: ${Self}<${Bound}> = start${op}end
|
|
expectEqual(0, ${Bound}.timesEqualEqualWasCalled.value)
|
|
expectEqual(2, ${Bound}.timesLessWasCalled.value)
|
|
expectEqual(${Self}<${Bound}>._isHalfOpen, range1.isEmpty)
|
|
expectFalse(range2.isEmpty)
|
|
expectEqual(
|
|
${Self}<${Bound}>._isHalfOpen ? 2 : 0,
|
|
${Bound}.timesEqualEqualWasCalled.value)
|
|
expectEqual(2, ${Bound}.timesLessWasCalled.value)
|
|
% if Bound == 'MinimalStrideableValue':
|
|
expectEqual(0, ${Bound}.timesDistanceWasCalled.value)
|
|
expectEqual(0, ${Bound}.timesAdvancedWasCalled.value)
|
|
% end
|
|
}
|
|
|
|
${TestSuite}.test("CustomStringConvertible, CustomDebugStringConvertible, CustomReflectable") {
|
|
var r: ${Self}<CustomPrintableValue> =
|
|
CustomPrintableValue(1)${op}CustomPrintableValue(2)
|
|
expectPrinted("(value: 1).description${op}(value: 2).description", r)
|
|
expectDebugPrinted(
|
|
"${Self}(" +
|
|
"(value: 1).debugDescription${op}(value: 2).debugDescription" +
|
|
")",
|
|
r)
|
|
expectDumped(
|
|
"▿ ${Self}((value: 1).debugDescription${op}(value: 2).debugDescription)\n" +
|
|
" ▿ lowerBound: (value: 1).debugDescription\n" +
|
|
" - value: 1\n" +
|
|
" - identity: 0\n" +
|
|
" ▿ upperBound: (value: 2).debugDescription\n" +
|
|
" - value: 2\n" +
|
|
" - identity: 0\n",
|
|
r)
|
|
}
|
|
|
|
% end
|
|
|
|
CountableRangeTestSuite.test("AssociatedTypes") {
|
|
typealias Collection = Range<MinimalStrideableValue>
|
|
expectCollectionAssociatedTypes(
|
|
collectionType: Collection.self,
|
|
iteratorType: Collection.Iterator.self,
|
|
subSequenceType: Collection.self,
|
|
indexType: MinimalStrideableValue.self,
|
|
indicesType: Collection.self)
|
|
}
|
|
|
|
CountableClosedRangeTestSuite.test("AssociatedTypes") {
|
|
typealias Collection = ClosedRange<MinimalStrideableValue>
|
|
expectCollectionAssociatedTypes(
|
|
collectionType: Collection.self,
|
|
iteratorType: Collection.Iterator.self,
|
|
subSequenceType: Slice<Collection>.self,
|
|
indexType: Collection.Index.self,
|
|
indicesType: DefaultIndices<Collection>.self)
|
|
}
|
|
|
|
var MiscTestSuite = TestSuite("Misc")
|
|
|
|
MiscTestSuite.test("map()") {
|
|
// <rdar://problem/17054014> map method should exist on ranges
|
|
var result = (1..<4).map { $0*2 }
|
|
expectType(Array<Int>.self, &result)
|
|
expectEqualSequence([ 2, 4, 6 ], result)
|
|
}
|
|
|
|
MiscTestSuite.test("reversed()") {
|
|
var result = (0..<10).lazy.reversed()
|
|
typealias Expected = ReversedCollection<
|
|
LazyCollection<Range<Int>>>
|
|
expectType(Expected.self, &result)
|
|
expectEqualSequence(
|
|
[ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 ],
|
|
result)
|
|
}
|
|
|
|
MiscTestSuite.test("Compatibility typealiases") {
|
|
let _: CountablePartialRangeFrom = 1...
|
|
let _: CountableRange = MinimalStrideableValue(3)..<MinimalStrideableValue(3)
|
|
let _: CountableClosedRange = MinimalStrideableValue(3)...MinimalStrideableValue(3)
|
|
}
|
|
|
|
runAllTests()
|
|
|