//===--- Index.swift.gyb - tests for Index types and operations -----------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // -*- swift -*- // RUN: rm -rf %t ; mkdir -p %t // RUN: %S/../../utils/gyb %s -o %t/Index.swift // RUN: %S/../../utils/line-directive %t/Index.swift -- %target-build-swift %t/Index.swift -o %t/a.out // RUN: %S/../../utils/line-directive %t/Index.swift -- %target-run %t/a.out // REQUIRES: executable_test import StdlibUnittest import StdlibCollectionUnittest // Also import modules which are used by StdlibUnittest internally. This // workaround is needed to link all required libraries in case we compile // StdlibUnittest with -sil-serialize-all. import SwiftPrivate #if _runtime(_ObjC) import ObjectiveC #endif extension Collection { func indexAt(offset offset: Int) -> Index { return self.index(numericCast(offset), stepsFrom: self.startIndex) } } struct DistanceToTest { let startIndex: Int let endIndex: Int let expectedDistance: Int let loc: SourceLoc init( startIndex: Int, endIndex: Int, expectedDistance: Int, file: String = #file, line: UInt = #line ) { self.startIndex = startIndex self.endIndex = endIndex self.expectedDistance = expectedDistance self.loc = SourceLoc(file, line, comment: "distance(from:to:) test data") } } struct AdvanceByTest { let startIndex: Int let distance: Int let limit: Int? let expectedIndex: Int let loc: SourceLoc init( startIndex: Int, distance: Int, expectedIndex: Int, limit: Int? = nil, file: String = #file, line: UInt = #line ) { self.startIndex = startIndex self.distance = distance self.expectedIndex = expectedIndex self.limit = limit self.loc = SourceLoc(file, line, comment: "advance(_:by:) test data") } } let distanceToTests = [ DistanceToTest( startIndex: 0, endIndex: 0, expectedDistance: 0 ), DistanceToTest( startIndex: 10, endIndex: 10, expectedDistance: 0 ), DistanceToTest( startIndex: 10, endIndex: 13, expectedDistance: 3 ), DistanceToTest( startIndex: 7, endIndex: 10, expectedDistance: 3 ), ] let advancedByTests = [ AdvanceByTest( startIndex: 0, distance: 0, expectedIndex: 0 ), AdvanceByTest( startIndex: 0, distance: -1, expectedIndex: -1 ), AdvanceByTest( startIndex: 0, distance: 0, expectedIndex: 0, limitedBy: 0 ), AdvanceByTest( startIndex: 0, distance: 0, expectedIndex: 0, limitedBy: 10 ), AdvanceByTest( startIndex: 0, distance: 10, expectedIndex: 0, limitedBy: 0 ), AdvanceByTest( startIndex: 0, distance: -10, expectedIndex: 0, limitedBy: 0 ), AdvanceByTest( startIndex: 0, distance: 10, expectedIndex: 10, limitedBy: 10 ), AdvanceByTest( startIndex: 0, distance: 20, expectedIndex: 10, limitedBy: 10 ), AdvanceByTest( startIndex: 10, distance: -20, expectedIndex: 0, limitedBy: 0 ), ] var Index = TestSuite("Index") % from gyb_stdlib_support import collectionForTraversal % # RandomAccess does not add any new behaviors to Bidirectional % # so we are left with just 2 traversals for the following tests % for Traversal in ['Forward', 'Bidirectional']: % TraversalCollection = collectionForTraversal(Traversal) Index.test("${TraversalCollection}/distance(to:)/dispatch") { let c = ${TraversalCollection}Log.dispatchTester(Array(0..<10)) _ = c.distance(from: 0, to: 10) expectCustomizable(c, c.log.distance) } Index.test("${TraversalCollection}/advance(_:by:)/dispatch") { let c = ${TraversalCollection}Log.dispatchTester(Array(0..<10)) _ = c.index(10, stepsFrom: c.startIndex) expectCustomizable(c, c.log.advance) } Index.test("${TraversalCollection}/advance(_:by:limit:)/dispatch") { let c = ${TraversalCollection}Log.dispatchTester(Array(0..<10)) _ = c.index(10, stepsFrom: c.startIndex, limitedBy: 5) expectCustomizable(c, c.log.advanceLimit) } % end % for Traversal in ['Forward', 'Bidirectional', 'RandomAccess']: % CollectionPrefix = collectionForTraversal(Traversal).replace('Collection', '') % for Base in ['Minimal', 'Defaulted']: % Kind = '{}{}'.format(Base, CollectionPrefix) Index.test("${Kind}Collection/distance(from:to:)/semantics") { let c = ${Kind}Collection(elements: Array(0..<20)) for test in distanceToTests { let d = c.distance( from: c.indexAt(offset: test.startIndex), to: c.indexAt(offset: test.endIndex)) expectEqual(test.expectedDistance, d, stackTrace: SourceLocStack().with(test.loc)) } } Index.test("${Kind}Collection/advance(_:by: n)/semantics") { for test in advancedByTests.filter({$0.limit == nil && $0.distance >= 0}) { let c = ${Kind}Collection(elements: Array(0..<10)) let new = c.advance(c.indexAt(offset: test.startIndex), by: test.distance) // Since the `indexAt(offset:)` method performs the same operation // (i.e. adavances `c.startIndex` by `test.distance`, it would be // silly to compare index values. Luckily the underlying collection // contains exactly index offsets. expectEqual(test.expectedIndex, c[new], stackTrace: SourceLocStack().with(test.loc)) } } % if Traversal == 'Forward': Index.test("${Kind}Collection/advance(_:by: -n)/semantics") { for test in advancedByTests.filter({$0.limit == nil && $0.distance < 0}) { let c = ${Kind}Collection(elements: Array(0..<10)) expectCrashLater() _ = c.advance(c.indexAt(offset: test.startIndex), by: test.distance) } } % end % if Traversal == 'Forward': Index.test("${Kind}Collection/advance(by: -n, limit:)/semantics") { for test in advancedByTests.filter({$0.limit != nil && $0.distance < 0}) { let c = ${Kind}Collection(elements: Array(0..<10)) let limit = c.indexAt(offset: test.limit.unsafelyUnwrapped) expectCrashLater() _ = c.advance(c.indexAt(offset: test.startIndex), by: test.distance, limitedBy: limit) } } % end Index.test("${Kind}Collection/advance(by: n, limit:)/semantics") { for test in advancedByTests.filter({$0.limit != nil && $0.distance >= 0}) { let c = ${Kind}Collection(elements: Array(0..<10)) let limit = c.indexAt(offset: test.limit.unsafelyUnwrapped) % if Traversal == 'Forward': if test.distance < 0 { expectCrashLater() } % end let new = c.advance(c.indexAt(offset: test.startIndex), by: test.distance, limitedBy: limit) expectEqual(c.indexAt(offset: test.expectedIndex), new, stackTrace: SourceLocStack().with(test.loc)) } } // Check that a random access collection doesn't call into O(n) predecessor // calls when it has a more efficient implementation. % if Traversal == 'RandomAccess' and Base == 'Defaulted': Index.test( "${Kind}Collection/advance(_:by:)/avoidsSuccessorAndPredecessor/dispatch" ) { for test in advancedByTests.filter({$0.limit == nil && $0.distance >= 0}) { let c = ${Kind}Collection(Array(0..<10)) let i = c.indexAt(offset: test.startIndex) let result = c.index(test.distance, stepsFrom: i) expectEqual(0, c.timesSuccessorCalled.value) expectEqual(0, c.timesPredecessorCalled.value) } } Index.test( "${Kind}Index/advance(_:by:limit:)/avoidsSuccessorAndPredecessor/dispatch" ).xfail(.custom({ true }, reason: "[swift-3-indexing-model] Implementation missing")) .code { for test in advancedByTests.filter({$0.limit != nil && $0.distance >= 0}) { let c = ${Kind}Collection(Array(0..<10)) let i = c.indexAt(offset: test.startIndex) let limit = c.indexAt(offset: test.limit.unsafelyUnwrapped) let result = c.index(test.distance, stepsFrom: i, limitedBy: limit) expectEqual(0, c.timesSuccessorCalled.value) expectEqual(0, c.timesPredecessorCalled.value) } } % end % end % end runAllTests()