mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
523 lines
15 KiB
Plaintext
523 lines
15 KiB
Plaintext
// RUN: rm -rf %t && mkdir -p %t && %S/../../utils/gyb %s -o %t/out.swift
|
|
// RUN: %S/../../utils/line-directive %t/out.swift -- %target-build-swift %t/out.swift -o %t/a.out
|
|
// RUN: %S/../../utils/line-directive %t/out.swift -- %target-run %t/a.out
|
|
// REQUIRES: executable_test
|
|
|
|
import StdlibUnittest
|
|
import StdlibCollectionUnittest
|
|
|
|
extension Range {
|
|
static var _isHalfOpen: Bool { return true }
|
|
}
|
|
extension CountableRange {
|
|
static var _isHalfOpen: Bool { return true }
|
|
}
|
|
extension ClosedRange {
|
|
static var _isHalfOpen: Bool { return false }
|
|
}
|
|
extension CountableClosedRange {
|
|
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 ..<* { associativity none precedence 135 }
|
|
infix operator ...* { associativity none precedence 135 }
|
|
|
|
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),
|
|
]
|
|
|
|
%{
|
|
all_range_types = [
|
|
('Range', '..<', 'MinimalComparableValue'),
|
|
('CountableRange', '..<', 'MinimalStrideableValue'),
|
|
('ClosedRange', '...', 'MinimalComparableValue'),
|
|
('CountableClosedRange', '...', 'MinimalStrideableValue'),
|
|
]
|
|
}%
|
|
|
|
% for (Self, op, Bound) in all_range_types:
|
|
% TestSuite = Self + 'TestSuite'
|
|
|
|
// Check that the generic parameter is called 'Bound'.
|
|
extension ${Self} where Bound : TestProtocol1 {
|
|
var _elementIsTestProtocol1: Bool {
|
|
fatalError("not implemented")
|
|
}
|
|
}
|
|
|
|
var ${TestSuite} = TestSuite("${Self}")
|
|
|
|
${TestSuite}.test("init(uncheckedBounds:)")
|
|
.forEach(in: [(1, 2), (1, 1), (2, 1)]) {
|
|
(lowerInt, upperInt) in
|
|
|
|
// Check that 'init(uncheckedBounds:)' does not perform precondition checks,
|
|
// allowing to create ranges that break invariants.
|
|
let r = ${Self}(
|
|
uncheckedBounds: (lower: ${Bound}(lowerInt), upper: ${Bound}(upperInt)))
|
|
expectEqual(lowerInt, r.lowerBound.value)
|
|
expectEqual(upperInt, 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() ?
|
|
"Can't form Range with upperBound < lowerBound" : "")
|
|
.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 'Countable' in Self:
|
|
${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 'Countable' in Self:
|
|
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) 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)
|
|
% if Self == 'CountableClosedRange':
|
|
// FIXME: swift-3-indexing-model: implement an O(1) `distance()` on
|
|
// CountableClosedRange.
|
|
expectEqual(12, Bound.timesEqualEqualWasCalled.value)
|
|
expectEqual(2, Bound.timesLessWasCalled.value)
|
|
expectEqual(0, Bound.timesDistanceWasCalled.value)
|
|
expectEqual(10, Bound.timesAdvancedWasCalled.value)
|
|
% else:
|
|
expectEqual(0, Bound.timesEqualEqualWasCalled.value)
|
|
expectEqual(2, Bound.timesLessWasCalled.value)
|
|
expectEqual(2, Bound.timesDistanceWasCalled.value)
|
|
expectEqual(0, Bound.timesAdvancedWasCalled.value)
|
|
% 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 = CountableRange<MinimalStrideableValue>
|
|
expectCollectionAssociatedTypes(
|
|
collectionType: Collection.self,
|
|
iteratorType: IndexingIterator<Collection>.self,
|
|
subSequenceType: Collection.self,
|
|
indexType: MinimalStrideableValue.self,
|
|
indexDistanceType: MinimalStrideableValue.Stride.self,
|
|
indicesType: Collection.self)
|
|
}
|
|
|
|
CountableClosedRangeTestSuite.test("AssociatedTypes") {
|
|
typealias Collection = CountableClosedRange<MinimalStrideableValue>
|
|
expectCollectionAssociatedTypes(
|
|
collectionType: Collection.self,
|
|
iteratorType: ClosedRangeIterator<MinimalStrideableValue>.self,
|
|
subSequenceType: RandomAccessSlice<Collection>.self,
|
|
indexType: ClosedRangeIndex<MinimalStrideableValue>.self,
|
|
indexDistanceType: MinimalStrideableValue.Stride.self,
|
|
indicesType: DefaultRandomAccessIndices<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 = LazyRandomAccessCollection<
|
|
ReversedRandomAccessCollection<CountableRange<Int>>>
|
|
expectType(Expected.self, &result)
|
|
expectEqualSequence(
|
|
[ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 ],
|
|
result)
|
|
}
|
|
|
|
// FIXME: swift-3-indexing-model: this test does not belong in this file.
|
|
MiscTestSuite.test("stride") {
|
|
var result = [Double]()
|
|
for i in stride(from: 1.4, through: 3.4, by: 1) {
|
|
result.append(i)
|
|
}
|
|
expectEqual([ 1.4, 2.4, 3.4 ], result)
|
|
}
|
|
|
|
runAllTests()
|
|
|