Files
swift-mirror/validation-test/stdlib/Range.swift.gyb
2016-04-26 11:15:49 -07:00

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()