// RUN: %empty-directory(%t) // // RUN: %gyb %s -o %t/main.swift // RUN: if [ %target-runtime == "objc" ]; then \ // RUN: %target-clang -fobjc-arc %S/Inputs/SlurpFastEnumeration/SlurpFastEnumeration.m -c -o %t/SlurpFastEnumeration.o; \ // RUN: %line-directive %t/main.swift -- %target-build-swift %t/main.swift -I %S/Inputs/SlurpFastEnumeration/ -Xlinker %t/SlurpFastEnumeration.o -o %t/Arrays -g -Onone -Xfrontend -disable-access-control; \ // RUN: else \ // RUN: %line-directive %t/main.swift -- %target-build-swift %t/main.swift -o %t/Arrays -Xfrontend -disable-access-control; \ // RUN: fi // // RUN: %line-directive %t/main.swift -- %target-run %t/Arrays // REQUIRES: executable_test import Swift import StdlibUnittest import StdlibCollectionUnittest #if _runtime(_ObjC) import Foundation import StdlibUnittestFoundationExtras #endif func isCocoaArray(_ a: Array) -> Bool { return !isNativeArray(a) } func isNativeArray(_ a: Array) -> Bool { return a._buffer._storage.isNative } func convertArrayToNSArray(_ a: [T]) -> NSArray { return a._bridgeToObjectiveC() } func convertNSArrayToArray(_ a: NSArray?) -> [T] { if _slowPath(a == nil) { return [] } var result: [T]? Array._forceBridgeFromObjectiveC(a!, result: &result) return result! } var _objcValueCount = _stdlib_AtomicInt(0) var _objcValueSerial = _stdlib_AtomicInt(0) class TestObjCValueTy : NSObject { class var objectCount: Int { get { return _objcValueCount.load() } set { _objcValueCount.store(newValue) } } init(_ value: Int) { _objcValueCount.fetchAndAdd(1) serial = _objcValueSerial.addAndFetch(1) self.value = value } deinit { assert(serial > 0, "double destruction") _objcValueCount.fetchAndAdd(-1) serial = -serial } override var description: String { assert(serial > 0, "dead TestObjCValueTy") return value.description } var value: Int var serial: Int } var _objcEquatableValueCount = _stdlib_AtomicInt(0) var _objcEquatableValueSerial = _stdlib_AtomicInt(0) class TestObjCEquatableValueTy : NSObject { class var objectCount: Int { get { return _objcEquatableValueCount.load() } set { _objcEquatableValueCount.store(newValue) } } init(_ value: Int) { _objcEquatableValueCount.fetchAndAdd(1) serial = _objcEquatableValueSerial.addAndFetch(1) self.value = value } deinit { assert(serial > 0, "double destruction") _objcEquatableValueCount.fetchAndAdd(-1) serial = -serial } override func isEqual(_ object: Any!) -> Bool { if let other = object { if let otherObjcKey = other as? TestObjCEquatableValueTy { return self.value == otherObjcKey.value } } return false } override var description: String { assert(serial > 0, "dead TestObjCValueTy") return value.description } var value: Int var serial: Int } func == (lhs: TestObjCEquatableValueTy, rhs: TestObjCEquatableValueTy) -> Bool { return lhs.value == rhs.value } var _bridgedValueSerial = _stdlib_AtomicInt(0) var _bridgedValueBridgeOperations = _stdlib_AtomicInt(0) struct TestBridgedValueTy : CustomStringConvertible, _ObjectiveCBridgeable { static var bridgeOperations: Int { get { return _bridgedValueBridgeOperations.load() } set { _bridgedValueBridgeOperations.store(newValue) } } init(_ value: Int) { serial = _bridgedValueSerial.fetchAndAdd(1) self.value = value } var description: String { assert(serial > 0, "dead TestBridgedValueTy") return value.description } func _bridgeToObjectiveC() -> TestObjCValueTy { TestBridgedValueTy.bridgeOperations += 1 return TestObjCValueTy(value) } static func _forceBridgeFromObjectiveC( _ x: TestObjCValueTy, result: inout TestBridgedValueTy? ) { TestBridgedValueTy.bridgeOperations += 1 result = TestBridgedValueTy(x.value) } static func _conditionallyBridgeFromObjectiveC( _ x: TestObjCValueTy, result: inout TestBridgedValueTy? ) -> Bool { self._forceBridgeFromObjectiveC(x, result: &result) return true } static func _unconditionallyBridgeFromObjectiveC(_ source: TestObjCValueTy?) -> TestBridgedValueTy { var result: TestBridgedValueTy? = nil _forceBridgeFromObjectiveC(source!, result: &result) return result! } var value: Int var serial: Int } var _bridgedEquatableValueSerial = _stdlib_AtomicInt(0) var _bridgedEquatableValueBridgeOperations = _stdlib_AtomicInt(0) struct TestBridgedEquatableValueTy : Equatable, CustomStringConvertible, _ObjectiveCBridgeable { static var bridgeOperations: Int { get { return _bridgedEquatableValueBridgeOperations.load() } set { _bridgedEquatableValueBridgeOperations.store(newValue) } } init(_ value: Int) { serial = _bridgedEquatableValueSerial.addAndFetch(1) self.value = value } var description: String { assert(serial > 0, "dead TestBridgedValueTy") return value.description } func _bridgeToObjectiveC() -> TestObjCEquatableValueTy { _bridgedEquatableValueBridgeOperations.fetchAndAdd(1) return TestObjCEquatableValueTy(value) } static func _forceBridgeFromObjectiveC( _ x: TestObjCEquatableValueTy, result: inout TestBridgedEquatableValueTy? ) { _bridgedEquatableValueBridgeOperations.fetchAndAdd(1) result = TestBridgedEquatableValueTy(x.value) } static func _conditionallyBridgeFromObjectiveC( _ x: TestObjCEquatableValueTy, result: inout TestBridgedEquatableValueTy? ) -> Bool { self._forceBridgeFromObjectiveC(x, result: &result) return true } static func _unconditionallyBridgeFromObjectiveC( _ source: TestObjCEquatableValueTy? ) -> TestBridgedEquatableValueTy { var result: TestBridgedEquatableValueTy? = nil _forceBridgeFromObjectiveC(source!, result: &result) return result! } var value: Int var serial: Int } func == (lhs: TestBridgedEquatableValueTy, rhs: TestBridgedEquatableValueTy) -> Bool { return lhs.value == rhs.value } let CopyToNativeArrayBufferTests = TestSuite("CopyToNativeArrayBufferTests") extension Array { func _rawIdentifier() -> Int { return unsafeBitCast(self, to: Int.self) } } CopyToNativeArrayBufferTests.test("Sequence._copyToContiguousArray()") { do { // Call from a static context. let s = MinimalSequence(elements: LifetimeTracked(10).. Array bridging tests. //===----------------------------------------------------------------------===// func getAsNSArray(_ a: Array) -> NSArray { let values = Array(a.map { TestObjCValueTy($0) }) // Return an `NSMutableArray` to make sure that it has a unique // pointer identity. let nsarray = NSMutableArray() nsarray.addObjects(from: values) return nsarray } func getAsEquatableNSArray(_ a: Array) -> NSArray { let values = Array(a.map { TestObjCEquatableValueTy($0) }) // Return an `NSMutableArray` to make sure that it has a unique // pointer identity. let nsarray = NSMutableArray() nsarray.addObjects(from: values) return nsarray } func getAsNSMutableArray(_ a: Array) -> NSMutableArray { let values = Array(a.map { TestObjCValueTy($0) }) let nsarray = NSMutableArray() nsarray.addObjects(from: values) return nsarray } func getBridgedVerbatimArray() -> Array { let nsa = getAsNSArray([1010, 1020, 1030]) return convertNSArrayToArray(nsa) } func getBridgedVerbatimArray(_ a: Array) -> Array { let nsa = getAsNSArray(a) return convertNSArrayToArray(nsa) } func getBridgedVerbatimArrayAndNSMutableArray() -> (Array, NSMutableArray) { let nsa = getAsNSMutableArray([1010, 1020, 1030]) return (convertNSArrayToArray(nsa), nsa) } func getBridgedNonverbatimArray() -> Array { let nsa = getAsNSArray([1010, 1020, 1030 ]) return Swift._forceBridgeFromObjectiveC(nsa, Array.self) } func getBridgedNonverbatimArray(_ a: Array) -> Array { let nsa = getAsNSArray(a) return Swift._forceBridgeFromObjectiveC(nsa, Array.self) } func getBridgedNonverbatimArrayAndNSMutableArray() -> (Array, NSMutableArray) { let nsa = getAsNSMutableArray([1010, 1020, 1030]) return (Swift._forceBridgeFromObjectiveC(nsa, Array.self), nsa) } func getBridgedVerbatimEquatableArray(_ a: Array) -> Array { let nsa = getAsEquatableNSArray(a) return convertNSArrayToArray(nsa) } func getBridgedNonverbatimEquatableArray(_ a: Array) -> Array { let nsa = getAsEquatableNSArray(a) return Swift._forceBridgeFromObjectiveC(nsa, Array.self) } func getHugeBridgedVerbatimArrayHelper() -> NSArray { let values = (1...32).map { TestObjCValueTy(1000 + $0) } let nsa = NSMutableArray() nsa.addObjects(from: values) return nsa } func getHugeBridgedVerbatimArray() -> Array { let nsa = getHugeBridgedVerbatimArrayHelper() return convertNSArrayToArray(nsa) } func getHugeBridgedNonverbatimArray() -> Array { let nsa = getHugeBridgedVerbatimArrayHelper() return Swift._forceBridgeFromObjectiveC(nsa, Array.self) } @objc class CustomImmutableNSArray : NSArray { init(_privateInit: ()) { super.init() } override init() { expectUnreachable() super.init() } override init(objects: UnsafePointer!, count cnt: Int) { expectUnreachable() super.init(objects: objects, count: cnt) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) not implemented by CustomImmutableNSArray") } @objc(copyWithZone:) override func copy(with zone: NSZone?) -> Any { CustomImmutableNSArray.timesCopyWithZoneWasCalled += 1 return self } override func object(at index: Int) -> Any { CustomImmutableNSArray.timesObjectAtIndexWasCalled += 1 return getAsNSArray([1010, 1020, 1030]).object(at: index) } override func objectEnumerator() -> NSEnumerator { CustomImmutableNSArray.timesObjectEnumeratorWasCalled += 1 return getAsNSArray([1010, 1020, 1030]).objectEnumerator() } override var count: Int { CustomImmutableNSArray.timesCountWasCalled += 1 return 3 } static var timesCopyWithZoneWasCalled = 0 static var timesObjectAtIndexWasCalled = 0 static var timesObjectEnumeratorWasCalled = 0 static var timesCountWasCalled = 0 } ArrayTestSuite.test("BridgedFromObjC.Verbatim.ArrayIsCopied") { var (a, nsa) = getBridgedVerbatimArrayAndNSMutableArray() var identity1 = a._rawIdentifier() expectTrue(isCocoaArray(a)) // Find an existing value. do { var v = a[0] expectEqual(1010, v.value) } // Remove the value from the NSMutableArray. nsa.removeObject(at: 0) // Find an existing value, again. do { var v = a[0] expectEqual(1010, v.value) } } ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.ArrayIsCopied") { var (a, nsa) = getBridgedNonverbatimArrayAndNSMutableArray() var identity1 = a._rawIdentifier() expectTrue(isNativeArray(a)) // Find an existing value. do { var v = a[0] expectEqual(1010, v.value) } // Remove the value from the NSMutableArray. nsa.removeObject(at: 0) // Find an existing value, again. do { var v = a[0] expectEqual(1010, v.value) } } ArrayTestSuite.test("BridgedFromObjC.Verbatim.NSArrayIsRetained") { var nsa: NSArray = autoreleasepool { NSArray(array: getAsNSArray([1010, 1020, 1030])) } var a: [AnyObject] = convertNSArrayToArray(nsa) var bridgedBack: NSArray = convertArrayToNSArray(a) expectEqual( unsafeBitCast(nsa, to: Int.self), unsafeBitCast(bridgedBack, to: Int.self)) _blackHole(nsa) _blackHole(a) _blackHole(bridgedBack) } ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.NSArrayIsCopied") { var nsa: NSArray = autoreleasepool { NSArray(array: getAsNSArray([1010, 1020, 1030])) } var a: [TestBridgedValueTy] = convertNSArrayToArray(nsa) var bridgedBack: NSArray = convertArrayToNSArray(a) expectNotEqual( unsafeBitCast(nsa, to: Int.self), unsafeBitCast(bridgedBack, to: Int.self)) _blackHole(nsa) _blackHole(a) _blackHole(bridgedBack) } ArrayTestSuite.test("BridgedFromObjC.Verbatim.ImmutableArrayIsRetained") { var nsa: NSArray = CustomImmutableNSArray(_privateInit: ()) CustomImmutableNSArray.timesCopyWithZoneWasCalled = 0 CustomImmutableNSArray.timesObjectAtIndexWasCalled = 0 CustomImmutableNSArray.timesObjectEnumeratorWasCalled = 0 CustomImmutableNSArray.timesCountWasCalled = 0 var a: [AnyObject] = convertNSArrayToArray(nsa) expectEqual(1, CustomImmutableNSArray.timesCopyWithZoneWasCalled) expectEqual(0, CustomImmutableNSArray.timesObjectAtIndexWasCalled) expectEqual(0, CustomImmutableNSArray.timesObjectEnumeratorWasCalled) expectEqual(0, CustomImmutableNSArray.timesCountWasCalled) var bridgedBack: NSArray = convertArrayToNSArray(a) expectEqual( unsafeBitCast(nsa, to: Int.self), unsafeBitCast(bridgedBack, to: Int.self)) _blackHole(nsa) _blackHole(a) _blackHole(bridgedBack) } ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.ImmutableArrayIsCopied") { var nsa: NSArray = CustomImmutableNSArray(_privateInit: ()) CustomImmutableNSArray.timesCopyWithZoneWasCalled = 0 CustomImmutableNSArray.timesObjectAtIndexWasCalled = 0 CustomImmutableNSArray.timesObjectEnumeratorWasCalled = 0 CustomImmutableNSArray.timesCountWasCalled = 0 TestBridgedValueTy.bridgeOperations = 0 var a: [TestBridgedValueTy] = convertNSArrayToArray(nsa) //FIXME: Why is this copied? expectEqual(1, CustomImmutableNSArray.timesCopyWithZoneWasCalled) expectEqual(3, CustomImmutableNSArray.timesObjectAtIndexWasCalled) expectNotEqual(0, CustomImmutableNSArray.timesCountWasCalled) expectEqual(3, TestBridgedValueTy.bridgeOperations) var bridgedBack: NSArray = convertArrayToNSArray(a) expectNotEqual( unsafeBitCast(nsa, to: Int.self), unsafeBitCast(bridgedBack, to: Int.self)) _fixLifetime(nsa) _fixLifetime(a) _fixLifetime(bridgedBack) } ArrayTestSuite.test("BridgedFromObjC.Verbatim.Subscript") { var a = getBridgedVerbatimArray() var identity1 = a._rawIdentifier() expectTrue(isCocoaArray(a)) // Find an existing value. do { var v = a[0] expectEqual(1010, (v as! TestObjCValueTy).value) v = a[1] expectEqual(1020, (v as! TestObjCValueTy).value) v = a[2] expectEqual(1030, (v as! TestObjCValueTy).value) } expectEqual(identity1, a._rawIdentifier()) } ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.Subscript") { var a = getBridgedNonverbatimArray() var identity1 = a._rawIdentifier() expectTrue(isNativeArray(a)) // Find an existing value. do { var v = a[0] expectEqual(1010, v.value) v = a[1] expectEqual(1020, v.value) v = a[2] expectEqual(1030, v.value) } expectEqual(identity1, a._rawIdentifier()) } ArrayTestSuite.test("BridgedFromObjC.Verbatim.RemoveAt") { var a = getBridgedVerbatimArray() var identity = a._rawIdentifier() expectTrue(isCocoaArray(a)) let index = 0 expectEqual(1010, (a[index] as! TestObjCValueTy).value) expectEqual(identity, a._rawIdentifier()) let removedElement = a.remove(at: index) expectNotEqual(identity, a._rawIdentifier()) expectTrue(isNativeArray(a)) expectEqual(1010, (removedElement as! TestObjCValueTy).value) expectEqual(2, a.count) } ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.RemoveAt") { var a = getBridgedNonverbatimArray() var identity = a._rawIdentifier() expectTrue(isNativeArray(a)) let index = 0 expectEqual(1010, a[index].value) expectEqual(identity, a._rawIdentifier()) let removedElement = a.remove(at: index) expectEqual(identity, a._rawIdentifier()) expectTrue(isNativeArray(a)) expectEqual(1010, removedElement.value) expectEqual(2, a.count) } ArrayTestSuite.test("BridgedFromObjC.Verbatim.RemoveAll") { do { var a = getBridgedVerbatimArray() var identity1 = a._rawIdentifier() expectTrue(isCocoaArray(a)) let originalCapacity = a.count expectEqual(3, a.count) expectEqual(1010, (a[0] as! TestObjCValueTy).value) a.removeAll() expectNotEqual(identity1, a._rawIdentifier()) expectLT(a._buffer.capacity, originalCapacity) expectEqual(0, a.count) } do { var a = getBridgedVerbatimArray() var identity1 = a._rawIdentifier() expectTrue(isCocoaArray(a)) let originalCapacity = a.count expectEqual(3, a.count) expectEqual(1010, (a[0] as! TestObjCValueTy).value) a.removeAll(keepingCapacity: true) expectNotEqual(identity1, a._rawIdentifier()) expectGE(a._buffer.capacity, originalCapacity) expectEqual(0, a.count) } do { var a1 = getBridgedVerbatimArray() var identity1 = a1._rawIdentifier() expectTrue(isCocoaArray(a1)) let originalCapacity = a1.count expectEqual(3, a1.count) expectEqual(1010, (a1[0] as! TestObjCValueTy).value) var a2 = a1 a2.removeAll() var identity2 = a2._rawIdentifier() expectEqual(identity1, a1._rawIdentifier()) expectNotEqual(identity2, identity1) expectEqual(3, a1.count) expectEqual(1010, (a1[0] as! TestObjCValueTy).value) assert(a2._buffer.capacity < originalCapacity) expectEqual(0, a2.count) } do { var a1 = getBridgedVerbatimArray() var identity1 = a1._rawIdentifier() expectTrue(isCocoaArray(a1)) let originalCapacity = a1.count expectEqual(3, a1.count) expectEqual(1010, (a1[0] as! TestObjCValueTy).value) var a2 = a1 a2.removeAll(keepingCapacity: true) var identity2 = a2._rawIdentifier() expectEqual(identity1, a1._rawIdentifier()) expectNotEqual(identity2, identity1) expectEqual(3, a1.count) expectEqual(1010, (a1[0] as! TestObjCValueTy).value) expectGE(a2._buffer.capacity, originalCapacity) expectEqual(0, a2.count) } } ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.RemoveAll") { do { var a = getBridgedNonverbatimArray() var identity1 = a._rawIdentifier() expectTrue(isNativeArray(a)) let originalCapacity = a.count expectEqual(3, a.count) expectEqual(1010, a[0].value) a.removeAll() expectNotEqual(identity1, a._rawIdentifier()) expectLT(a._buffer.capacity, originalCapacity) expectEqual(0, a.count) } do { var a = getBridgedNonverbatimArray() var identity1 = a._rawIdentifier() expectTrue(isNativeArray(a)) let originalCapacity = a.count expectEqual(3, a.count) expectEqual(1010, a[0].value) a.removeAll(keepingCapacity: true) expectEqual(identity1, a._rawIdentifier()) expectGE(a._buffer.capacity, originalCapacity) expectEqual(0, a.count) } do { var a1 = getBridgedNonverbatimArray() var identity1 = a1._rawIdentifier() expectTrue(isNativeArray(a1)) let originalCapacity = a1.count expectEqual(3, a1.count) expectEqual(1010, a1[0].value) var a2 = a1 a2.removeAll() var identity2 = a2._rawIdentifier() expectEqual(identity1, a1._rawIdentifier()) expectNotEqual(identity2, identity1) expectEqual(3, a1.count) expectEqual(1010, a1[0].value) assert(a2._buffer.capacity < originalCapacity) expectEqual(0, a2.count) } do { var a1 = getBridgedNonverbatimArray() var identity1 = a1._rawIdentifier() expectTrue(isNativeArray(a1)) let originalCapacity = a1.count expectEqual(3, a1.count) expectEqual(1010, a1[0].value) var a2 = a1 a2.removeAll(keepingCapacity: true) var identity2 = a2._rawIdentifier() expectEqual(identity1, a1._rawIdentifier()) expectNotEqual(identity2, identity1) expectEqual(3, a1.count) expectEqual(1010, a1[0].value) assert(a2._buffer.capacity >= originalCapacity) expectEqual(0, a2.count) } } ArrayTestSuite.test("BridgedFromObjC.Verbatim.Count") { var a = getBridgedVerbatimArray() var identity1 = a._rawIdentifier() expectTrue(isCocoaArray(a)) expectEqual(3, a.count) expectEqual(identity1, a._rawIdentifier()) } ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.Count") { var a = getBridgedNonverbatimArray() var identity1 = a._rawIdentifier() expectTrue(isNativeArray(a)) expectEqual(3, a.count) expectEqual(identity1, a._rawIdentifier()) } ArrayTestSuite.test("BridgedFromObjC.Verbatim.Generate") { var a = getBridgedVerbatimArray() var identity1 = a._rawIdentifier() expectTrue(isCocoaArray(a)) var iter = a.makeIterator() var values = Array() while let value = iter.next() { values.append((value as! TestObjCValueTy).value) } expectEqual(values, [1010, 1020, 1030]) // The following is not required by the IteratorProtocol protocol, but // it is a nice QoI. expectNil(iter.next()) expectNil(iter.next()) expectNil(iter.next()) expectEqual(identity1, a._rawIdentifier()) } ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.Generate") { var a = getBridgedNonverbatimArray() var identity1 = a._rawIdentifier() expectTrue(isNativeArray(a)) var iter = a.makeIterator() var values = Array() while let value = iter.next() { values.append(value.value) } expectEqual(values, [1010, 1020, 1030]) // The following is not required by the IteratorProtocol protocol, but // it is a nice QoI. expectNil(iter.next()) expectNil(iter.next()) expectNil(iter.next()) expectEqual(identity1, a._rawIdentifier()) } ArrayTestSuite.test("BridgedFromObjC.Verbatim.Generate_Empty") { var a = getBridgedVerbatimArray([]) var identity1 = a._rawIdentifier() expectTrue(isCocoaArray(a)) var iter = a.makeIterator() expectNil(iter.next()) // The following is not required by the IteratorProtocol protocol, but // it is a nice QoI. expectNil(iter.next()) expectNil(iter.next()) expectNil(iter.next()) expectEqual(identity1, a._rawIdentifier()) } ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.Generate_Empty") { var a = getBridgedNonverbatimArray([]) var identity1 = a._rawIdentifier() expectTrue(isNativeArray(a)) var iter = a.makeIterator() expectNil(iter.next()) // The following is not required by the IteratorProtocol protocol, but // it is a nice QoI. expectNil(iter.next()) expectNil(iter.next()) expectNil(iter.next()) expectEqual(identity1, a._rawIdentifier()) } ArrayTestSuite.test("BridgedFromObjC.Verbatim.Generate_Huge") { var a = getHugeBridgedVerbatimArray() var identity1 = a._rawIdentifier() expectTrue(isCocoaArray(a)) var iter = a.makeIterator() var values = Array() while let value = iter.next() { values.append((value as! TestObjCValueTy).value) } var expectedValues = Array() for i in 1...32 { expectedValues += [1000 + i] } expectEqual(values, expectedValues) // The following is not required by the IteratorProtocol protocol, but // it is a nice QoI. expectNil(iter.next()) expectNil(iter.next()) expectNil(iter.next()) expectEqual(identity1, a._rawIdentifier()) } ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.Generate_Huge") { var a = getHugeBridgedNonverbatimArray() var identity1 = a._rawIdentifier() expectTrue(isNativeArray(a)) var iter = a.makeIterator() var values = Array() while let value = iter.next() { values.append(value.value) } var expectedValues = Array() for i in 1...32 { expectedValues += [1000 + i] } expectEqual(values, expectedValues) // The following is not required by the IteratorProtocol protocol, but // it is a nice QoI. expectNil(iter.next()) expectNil(iter.next()) expectNil(iter.next()) expectEqual(identity1, a._rawIdentifier()) } ArrayTestSuite.test("BridgedFromObjC.Verbatim.EqualityTest_Empty") { var a1 = getBridgedVerbatimEquatableArray([]) var identity1 = a1._rawIdentifier() expectTrue(isCocoaArray(a1)) var a2 = getBridgedVerbatimEquatableArray([]) var identity2 = a2._rawIdentifier() expectTrue(isCocoaArray(a2)) // We can't check that `identity1 != identity2` because Foundation might be // returning the same singleton NSArray for empty arrays. expectEqual(a1, a2) expectEqual(identity1, a1._rawIdentifier()) expectEqual(identity2, a2._rawIdentifier()) } ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.EqualityTest_Empty") { var a1 = getBridgedNonverbatimEquatableArray([]) a1.append(TestBridgedEquatableValueTy(1)) a1.removeLast() var identity1 = a1._rawIdentifier() expectTrue(isNativeArray(a1)) var a2 = getBridgedNonverbatimEquatableArray([]) var identity2 = a2._rawIdentifier() expectTrue(isNativeArray(a2)) expectNotEqual(identity1, identity2) expectEqual(a1, a2) expectEqual(identity1, a1._rawIdentifier()) expectEqual(identity2, a2._rawIdentifier()) } ArrayTestSuite.test("BridgedFromObjC.Verbatim.EqualityTest_Small") { func helper(_ na1: Array, _ na2: Array, _ expectedEq: Bool) { let a1 = getBridgedVerbatimEquatableArray(na1) let identity1 = a1._rawIdentifier() expectTrue(isCocoaArray(a1)) var a2 = getBridgedVerbatimEquatableArray(na2) var identity2 = a2._rawIdentifier() expectTrue(isCocoaArray(a2)) do { let eq1 = (a1 == a2) expectEqual(eq1, expectedEq) let eq2 = (a2 == a1) expectEqual(eq2, expectedEq) let neq1 = (a1 != a2) expectNotEqual(neq1, expectedEq) let neq2 = (a2 != a1) expectNotEqual(neq2, expectedEq) } expectEqual(identity1, a1._rawIdentifier()) expectEqual(identity2, a2._rawIdentifier()) a2.append(TestObjCEquatableValueTy(1111)) a2.removeLast() expectTrue(isNativeArray(a2)) expectNotEqual(identity2, a2._rawIdentifier()) identity2 = a2._rawIdentifier() do { let eq1 = (a1 == a2) expectEqual(eq1, expectedEq) let eq2 = (a2 == a1) expectEqual(eq2, expectedEq) let neq1 = (a1 != a2) expectNotEqual(neq1, expectedEq) let neq2 = (a2 != a1) expectNotEqual(neq2, expectedEq) } expectEqual(identity1, a1._rawIdentifier()) expectEqual(identity2, a2._rawIdentifier()) } helper([], [], true) helper([1010], [1010], true) helper([1010, 1020], [1010, 1020], true) helper([1010, 1020, 1030], [1010, 1020, 1030], true) helper([1010, 1020, 1030], [1010, 1020, 1111], false) helper([1010, 1020, 1030], [1010, 1020], false) helper([1010, 1020, 1030], [1010], false) helper([1010, 1020, 1030], [], false) helper([1010, 1020, 1030], [1010, 1020, 1030, 1040], false) } //===--- // Array -> NSArray bridging tests. // // Values are bridged verbatim. //===--- func _expectAutoreleasedKeysAndValues( opt: (Int, Int) = (0, 0), unopt: (Int, Int) = (0, 0)) { var expectedKeys = 0 var expectedValues = 0 #if arch(i386) (expectedKeys, expectedValues) = unopt #else (expectedKeys, expectedValues) = opt #endif TestObjCValueTy.objectCount -= expectedValues } /// Expect some number of autoreleased value objects. /// /// - parameter opt: applies to platforms that have the return-autoreleased /// optimization. /// /// - parameter unopt: applies to platforms that don't. /// /// FIXME: Some non-zero `opt` might be cases of missed return-autorelease. func expectAutoreleasedValues( opt: Int = 0, unopt: Int = 0) { _expectAutoreleasedKeysAndValues(opt: (0, opt), unopt: (0, unopt)) } func isNativeNSArray(_ d: NSArray) -> Bool { let className: NSString = NSStringFromClass(type(of: d)) as NSString return ["_SwiftDeferredNSArray", "_ContiguousArray", "_EmptyArray"].contains { className.range(of: $0).length > 0 } } func getBridgedNSArrayOfRefTypesBridgedVerbatim() -> NSArray { expectTrue(_isBridgedVerbatimToObjectiveC(TestObjCValueTy.self)) var a = Array() a.reserveCapacity(32) a.append(TestObjCValueTy(1010)) a.append(TestObjCValueTy(1020)) a.append(TestObjCValueTy(1030)) let bridged = unsafeBitCast(convertArrayToNSArray(a), to: NSArray.self) assert(isNativeNSArray(bridged)) return bridged } func getBridgedEmptyNSArray() -> NSArray { let a = Array() let bridged = unsafeBitCast(convertArrayToNSArray(a), to: NSArray.self) assert(isNativeNSArray(bridged)) return bridged } func _makeExpectedArrayContents( _ expected: [Int] ) -> [ExpectedArrayElement] { var result = [ExpectedArrayElement]() for value in expected { result.append(ExpectedArrayElement(value: value)) } return result } func _equalsWithoutElementIdentity( _ lhs: [ExpectedArrayElement], _ rhs: [ExpectedArrayElement] ) -> Bool { func stripIdentity( _ list: [ExpectedArrayElement] ) -> [ExpectedArrayElement] { return list.map { ExpectedArrayElement(value: $0.value) } } return stripIdentity(lhs).elementsEqual(stripIdentity(rhs)) } struct ExpectedArrayElement : Comparable, CustomStringConvertible { var value: Int var valueIdentity: UInt init(value: Int, valueIdentity: UInt = 0) { self.value = value self.valueIdentity = valueIdentity } var description: String { return "(\(value), \(valueIdentity))" } } func == ( lhs: ExpectedArrayElement, rhs: ExpectedArrayElement ) -> Bool { return lhs.value == rhs.value && lhs.valueIdentity == rhs.valueIdentity } func < ( lhs: ExpectedArrayElement, rhs: ExpectedArrayElement ) -> Bool { let lhsElements = [ lhs.value, Int(bitPattern: lhs.valueIdentity) ] let rhsElements = [ rhs.value, Int(bitPattern: rhs.valueIdentity) ] return lhsElements.lexicographicallyPrecedes(rhsElements) } func _checkArrayFastEnumerationImpl( _ expected: [Int], _ a: NSArray, _ makeEnumerator: () -> NSFastEnumeration, _ useEnumerator: (NSArray, NSFastEnumeration, (AnyObject) -> ()) -> Void, _ convertValue: @escaping (AnyObject) -> Int ) { let expectedContentsWithoutIdentity = _makeExpectedArrayContents(expected) var expectedContents = [ExpectedArrayElement]() for i in 0..<3 { var actualContents = [ExpectedArrayElement]() let sink = { (value: AnyObject) in actualContents.append(ExpectedArrayElement( value: convertValue(value), valueIdentity: unsafeBitCast(value, to: UInt.self))) } useEnumerator(a, makeEnumerator(), sink) expectTrue( _equalsWithoutElementIdentity( expectedContentsWithoutIdentity, actualContents), "expected: \(expectedContentsWithoutIdentity)\n" + "actual: \(actualContents)\n") if i == 0 { expectedContents = actualContents } expectEqualSequence(expectedContents, actualContents) } } func slurpFastEnumerationFromObjC( _ a: NSArray, _ fe: NSFastEnumeration, _ sink: (AnyObject) -> Void ) { let objcValues = NSMutableArray() slurpFastEnumerationOfArrayFromObjCImpl(a, fe, objcValues) for value in objcValues { sink(value as AnyObject) } } import SlurpFastEnumeration func slurpFastEnumerationFromSwift( _ a: NSArray, _ fe: NSFastEnumeration, _ sink: (AnyObject) -> Void, maxItems: Int? = nil ) { var state = NSFastEnumerationState() let stackBufLength = 3 let stackBuf = _HeapBuffer<(), AnyObject?>( _HeapBufferStorage<(), AnyObject?>.self, (), stackBufLength) var itemsReturned = 0 while true { let returnedCount = fe.countByEnumerating( with: &state, objects: AutoreleasingUnsafeMutablePointer(stackBuf.baseAddress), count: stackBufLength) expectNotEqual(0, state.state) expectNotNil(state.mutationsPtr) if returnedCount == 0 { break } for i in 0..= maxItems! { return } } for _ in 0..<3 { let returnedCount = fe.countByEnumerating( with: &state, objects: AutoreleasingUnsafeMutablePointer(stackBuf.baseAddress), count: stackBufLength) expectNotEqual(0, state.state) expectNotNil(state.mutationsPtr) expectEqual(0, returnedCount) } } func checkArrayFastEnumerationFromSwift( _ expected: [Int], _ a: NSArray, _ makeEnumerator: () -> NSFastEnumeration, _ convertValue: @escaping (AnyObject) -> Int ) { _checkArrayFastEnumerationImpl( expected, a, makeEnumerator, { (a, fe, sink) in slurpFastEnumerationFromSwift(a, fe, sink) }, convertValue) } func checkArrayFastEnumerationFromObjC( _ expected: [Int], _ a: NSArray, _ makeEnumerator: () -> NSFastEnumeration, _ convertValue: @escaping (AnyObject) -> Int ) { _checkArrayFastEnumerationImpl( expected, a, makeEnumerator, { (a, fe, sink) in slurpFastEnumerationFromObjC(a, fe, sink) }, convertValue) } ArrayTestSuite.test("BridgedToObjC.Verbatim.Count") { let d = getBridgedNSArrayOfRefTypesBridgedVerbatim() assert(d.count == 3) } ArrayTestSuite.test("BridgedToObjC.Verbatim.ObjectForKey") { let a = getBridgedNSArrayOfRefTypesBridgedVerbatim() var v: AnyObject? = a.object(at: 0) as AnyObject expectEqual(1010, (v as! TestObjCValueTy).value) let idValue10 = unsafeBitCast(v, to: UInt.self) v = a.object(at: 1) as AnyObject expectEqual(1020, (v as! TestObjCValueTy).value) let idValue20 = unsafeBitCast(v, to: UInt.self) v = a.object(at: 2) as AnyObject expectEqual(1030, (v as! TestObjCValueTy).value) let idValue30 = unsafeBitCast(v, to: UInt.self) for i in 0..<3 { expectEqual(idValue10, unsafeBitCast( a.object(at: 0) as AnyObject, to: UInt.self)) expectEqual(idValue20, unsafeBitCast( a.object(at: 1) as AnyObject, to: UInt.self)) expectEqual(idValue30, unsafeBitCast( a.object(at: 2) as AnyObject, to: UInt.self)) } expectAutoreleasedValues(unopt: 3) } ArrayTestSuite.test("BridgedToObjC.Verbatim.KeyEnumerator.NextObject") { let a = getBridgedNSArrayOfRefTypesBridgedVerbatim() var capturedIdentities = Array() for _ in 0..<3 { let enumerator = a.objectEnumerator() var values = Array() var identities = Array() while let value = enumerator.nextObject() { let valueObj = (value as! TestObjCValueTy) values.append(valueObj.value) let identity = unsafeBitCast(valueObj, to: UInt.self) identities.append(identity) } expectEqual([ 1010, 1020, 1030 ], values) if capturedIdentities.isEmpty { capturedIdentities = identities } else { expectEqual(capturedIdentities, identities) } expectNil(enumerator.nextObject()) expectNil(enumerator.nextObject()) expectNil(enumerator.nextObject()) } expectAutoreleasedValues(unopt: 3) } ArrayTestSuite.test("BridgedToObjC.Verbatim.ObjectEnumerator.NextObject_Empty") { let a = getBridgedEmptyNSArray() let enumerator = a.objectEnumerator() expectNil(enumerator.nextObject()) expectNil(enumerator.nextObject()) expectNil(enumerator.nextObject()) } ArrayTestSuite.test("BridgedToObjC.Verbatim.ObjectEnumerator.FastEnumeration.UseFromSwift") { let a = getBridgedNSArrayOfRefTypesBridgedVerbatim() checkArrayFastEnumerationFromSwift( [ 1010, 1020, 1030 ], a, { a.objectEnumerator() }, { ($0 as! TestObjCValueTy).value }) expectAutoreleasedValues(unopt: 3) } ArrayTestSuite.test("BridgedToObjC.Verbatim.ObjectEnumerator.FastEnumeration.UseFromObjC") { let a = getBridgedNSArrayOfRefTypesBridgedVerbatim() checkArrayFastEnumerationFromObjC( [ 1010, 1020, 1030 ], a, { a.objectEnumerator() }, { ($0 as! TestObjCValueTy).value }) expectAutoreleasedValues(unopt: 3) } ArrayTestSuite.test("BridgedToObjC.Verbatim.ObjectEnumerator.FastEnumeration_Empty") { let a = getBridgedEmptyNSArray() checkArrayFastEnumerationFromSwift( [], a, { a.objectEnumerator() }, { ($0 as! TestObjCValueTy).value }) checkArrayFastEnumerationFromObjC( [], a, { a.objectEnumerator() }, { ($0 as! TestObjCValueTy).value }) } ArrayTestSuite.test("BridgedToObjC.Verbatim.FastEnumeration.UseFromSwift") { let a = getBridgedNSArrayOfRefTypesBridgedVerbatim() checkArrayFastEnumerationFromSwift( [ 1010, 1020, 1030 ], a, { a }, { ($0 as! TestObjCValueTy).value }) expectAutoreleasedValues(unopt: 3) } ArrayTestSuite.test("BridgedToObjC.Verbatim.FastEnumeration.UseFromObjC") { let a = getBridgedNSArrayOfRefTypesBridgedVerbatim() checkArrayFastEnumerationFromObjC( [ 1010, 1020, 1030 ], a, { a }, { ($0 as! TestObjCValueTy).value }) expectAutoreleasedValues(unopt: 3) } ArrayTestSuite.test("BridgedToObjC.Verbatim.FastEnumeration_Empty") { let a = getBridgedEmptyNSArray() checkArrayFastEnumerationFromSwift( [], a, { a }, { ($0 as! TestObjCValueTy).value }) checkArrayFastEnumerationFromObjC( [], a, { a }, { ($0 as! TestObjCValueTy).value }) } //===--- // Array -> NSArray bridging tests. // // Values are bridged non-verbatim. //===--- func getBridgedNSArrayOfObjValue_ValueTypesCustomBridged( numElements: Int = 3 ) -> NSArray { expectFalse(_isBridgedVerbatimToObjectiveC(TestBridgedValueTy.self)) var a = Array() for i in 1..<(numElements + 1) { a.append(TestBridgedValueTy(i * 10 + 1000)) } let bridged = convertArrayToNSArray(a) expectTrue(isNativeNSArray(bridged)) return bridged } func checkArrayEnumeratorPartialFastEnumerationFromSwift( _ expected: [Int], _ a: NSArray, maxFastEnumerationItems: Int, _ convertValue: @escaping (AnyObject) -> Int ) { _checkArrayFastEnumerationImpl( expected, a, { a.objectEnumerator() }, { (a, fe, sink) in slurpFastEnumerationOfNSEnumeratorFromSwift( a, fe as! NSEnumerator, sink, maxFastEnumerationItems: maxFastEnumerationItems) }, convertValue) } func slurpFastEnumerationOfNSEnumeratorFromSwift( _ a: NSArray, _ enumerator: NSEnumerator, _ sink: (AnyObject) -> Void, maxFastEnumerationItems: Int ) { slurpFastEnumerationFromSwift( a, enumerator, sink, maxItems: maxFastEnumerationItems) while let value = enumerator.nextObject() { sink(value as AnyObject) } } ArrayTestSuite.test("BridgedToObjC.KeyValue_ValueTypesCustomBridged") { let a = getBridgedNSArrayOfObjValue_ValueTypesCustomBridged() let enumerator = a.objectEnumerator() var values = Array() while let valueObj = enumerator.nextObject() { let value: AnyObject = valueObj as! AnyObject let v = (value as! TestObjCValueTy).value values.append(v) } expectEqual([ 1010, 1020, 1030 ], values) expectAutoreleasedValues(unopt: 3) } ArrayTestSuite.test("BridgedToObjC.Custom.ObjectEnumerator.FastEnumeration.UseFromSwift") { let a = getBridgedNSArrayOfObjValue_ValueTypesCustomBridged() checkArrayFastEnumerationFromSwift( [ 1010, 1020, 1030 ], a, { a.objectEnumerator() }, { ($0 as! TestObjCValueTy).value }) expectAutoreleasedValues(unopt: 3) } ArrayTestSuite.test("BridgedToObjC.Custom.ObjectEnumerator.FastEnumeration.UseFromSwift.Partial") { let a = getBridgedNSArrayOfObjValue_ValueTypesCustomBridged( numElements: 9) checkArrayEnumeratorPartialFastEnumerationFromSwift( [ 1010, 1020, 1030, 1040, 1050, 1060, 1070, 1080, 1090 ], a, maxFastEnumerationItems: 5, { ($0 as! TestObjCValueTy).value }) expectAutoreleasedValues(unopt: 9) } ArrayTestSuite.test("BridgedToObjC.Custom.ObjectEnumerator.FastEnumeration.UseFromObjC") { let a = getBridgedNSArrayOfObjValue_ValueTypesCustomBridged() checkArrayFastEnumerationFromObjC( [ 1010, 1020, 1030 ], a, { a.objectEnumerator() }, { ($0 as! TestObjCValueTy).value }) expectAutoreleasedValues(unopt: 3) } ArrayTestSuite.test("BridgedToObjC.Custom.FastEnumeration.UseFromSwift") { let a = getBridgedNSArrayOfObjValue_ValueTypesCustomBridged() checkArrayFastEnumerationFromSwift( [ 1010, 1020, 1030 ], a, { a }, { ($0 as! TestObjCValueTy).value }) expectAutoreleasedValues(unopt: 3) } ArrayTestSuite.test("BridgedToObjC.Custom.FastEnumeration.UseFromObjC") { let a = getBridgedNSArrayOfObjValue_ValueTypesCustomBridged() checkArrayFastEnumerationFromObjC( [ 1010, 1020, 1030 ], a, { a }, { ($0 as! TestObjCValueTy).value }) expectAutoreleasedValues(unopt: 3) } ArrayTestSuite.test("BridgedToObjC.Custom.FastEnumeration_Empty") { let a = getBridgedNSArrayOfObjValue_ValueTypesCustomBridged( numElements: 0) checkArrayFastEnumerationFromSwift( [], a, { a }, { ($0 as! TestObjCValueTy).value }) checkArrayFastEnumerationFromObjC( [], a, { a }, { ($0 as! TestObjCValueTy).value }) } func getBridgedNSArrayOfObj_ValueTypeCustomBridged() -> NSArray { expectTrue(_isBridgedVerbatimToObjectiveC(TestObjCValueTy.self)) var a = Array() a.append(TestObjCValueTy(1010)) a.append(TestObjCValueTy(1020)) a.append(TestObjCValueTy(1030)) let bridged = convertArrayToNSArray(a) expectTrue(isNativeNSArray(bridged)) return bridged } ArrayTestSuite.test("BridgedToObjC.Key_ValueTypeCustomBridged") { let a = getBridgedNSArrayOfObj_ValueTypeCustomBridged() let enumerator = a.objectEnumerator() var values = Array() while let valueObj = enumerator.nextObject() { let value: AnyObject = valueObj as! AnyObject let v = (value as! TestObjCValueTy).value values.append(v) } expectEqual([ 1010, 1020, 1030 ], values) expectAutoreleasedValues(unopt: 3) } func getBridgedNSArrayOfValue_ValueTypeCustomBridged() -> NSArray { expectFalse(_isBridgedVerbatimToObjectiveC(TestBridgedValueTy.self)) var a = Array() a.append(TestBridgedValueTy(1010)) a.append(TestBridgedValueTy(1020)) a.append(TestBridgedValueTy(1030)) let bridged = convertArrayToNSArray(a) expectTrue(isNativeNSArray(bridged)) return bridged } ArrayTestSuite.test("BridgedToObjC.Value_ValueTypeCustomBridged") { let a = getBridgedNSArrayOfValue_ValueTypeCustomBridged() let enumerator = a.objectEnumerator() var values = Array() while let valueObj = enumerator.nextObject() { let value: AnyObject = valueObj as! AnyObject let v = (value as! TestObjCValueTy).value values.append(v) } expectEqual([ 1010, 1020, 1030 ], values) expectAutoreleasedValues(unopt: 3) } //===--- // NSArray -> Array -> NSArray bridging tests. //===--- func isCocoaNSArray(_ a: NSArray) -> Bool { let className: NSString = NSStringFromClass(type(of: a)) as NSString return className.range(of: "NSArray").length > 0 || className.range(of: "NSCFArray").length > 0 } func getRoundtripBridgedNSArray() -> NSArray { let values = [ 1010, 1020, 1030 ].map { TestObjCValueTy($0) } let nsa = NSArray(array: values) let a: Array = convertNSArrayToArray(nsa) let bridgedBack = convertArrayToNSArray(a) expectTrue(isCocoaNSArray(bridgedBack)) expectEqual(unsafeBitCast(nsa, to: Int.self), unsafeBitCast(bridgedBack, to: Int.self)) return bridgedBack } ArrayTestSuite.test("BridgingRoundtrip") { let a = getRoundtripBridgedNSArray() let enumerator = a.objectEnumerator() var values = Array() while let valueObj = enumerator.nextObject() { let value: AnyObject = valueObj as! AnyObject let v = (value as! TestObjCValueTy).value values.append(v) } expectEqual([ 1010, 1020, 1030 ], values) } //===--- // NSArray -> Array implicit conversion. //===--- ArrayTestSuite.test("NSArrayToArrayConversion") { let values = [ 1010, 1020, 1030 ].map { TestObjCValueTy($0) } let nsa = NSArray(array: values) let a: Array = nsa as Array var bridgedValues = Array() for value in a { let v = (value as! TestObjCValueTy).value bridgedValues.append(v) } expectEqual([ 1010, 1020, 1030 ], bridgedValues) } ArrayTestSuite.test("ArrayToNSArrayConversion") { var a = Array() a.append(TestObjCValueTy(1010)) a.append(TestObjCValueTy(1020)) a.append(TestObjCValueTy(1030)) let nsa: NSArray = a as NSArray checkArrayFastEnumerationFromSwift( [ 1010, 1020, 1030 ], a as NSArray, { a as NSArray }, { ($0 as! TestObjCValueTy).value }) expectAutoreleasedValues(unopt: 3) } #endif //===----------------------------------------------------------------------===// // Native array tests // FIXME: incomplete. //===----------------------------------------------------------------------===// ArrayTestSuite.test("Native/count/empty") { let a = [LifetimeTracked]() expectEqual(0, a.count) } ArrayTestSuite.test("Native/count") { let a = [ LifetimeTracked(10), LifetimeTracked(20), LifetimeTracked(30) ] expectEqual(3, a.count) } ArrayTestSuite.test("Native/isEmpty/empty") { let a = [LifetimeTracked]() expectTrue(a.isEmpty) } ArrayTestSuite.test("Native/isEmpty") { let a = [ LifetimeTracked(10), LifetimeTracked(20), LifetimeTracked(30) ] expectFalse(a.isEmpty) } % for Kind in ['Array', 'ContiguousArray']: ArrayTestSuite.test("${Kind}/popLast") { // Empty do { var a = ${Kind}() let popped = a.popLast() expectNil(popped) expectTrue(a.isEmpty) } do { var popped = [Int]() var a: ${Kind} = [1010, 2020, 3030] while let element = a.popLast() { popped.append(element) } expectEqualSequence([1010, 2020, 3030], popped.reversed()) expectTrue(a.isEmpty) } } % end //===----------------------------------------------------------------------===// // COW(🐄) tests //===----------------------------------------------------------------------===// class COWBox< T: Equatable & CustomStringConvertible> : Equatable, CustomStringConvertible { var value: T init(_ value: T) { self.value = value } var description: String { return "Boxed: \(value.description)" } static func ==(lhs: COWBox, rhs: COWBox) -> Bool { return lhs.value == rhs.value } } ArrayTestSuite.test("COW.Smoke") { var a1 = Array>(repeating: COWBox(0), count: 10) var identity1 = a1._rawIdentifier() a1[0] = COWBox(1) a1[1] = COWBox(2) a1[2] = COWBox(3) var a2 = a1 expectEqual(identity1, a2._rawIdentifier()) a2[3] = COWBox(4) expectNotEqual(identity1, a2._rawIdentifier()) a1[4] = COWBox(5) expectEqual(identity1, a1._rawIdentifier()) _blackHole(a1) _blackHole(a2) } func getCOWFastArray() -> Array { var a = Array() a.reserveCapacity(10) a.append(1) a.append(2) a.append(3) return a } func getCOWSlowArray() -> Array> { var a = Array>() a.reserveCapacity(10) a.append(COWBox(1)) a.append(COWBox(2)) a.append(COWBox(3)) return a } ArrayTestSuite.test("COW.Fast.SubscriptWithIndexDoesNotReallocate") { var a = getCOWFastArray() var identity1 = a._rawIdentifier() var startIndex = a.startIndex expectNotEqual(0, a[startIndex]) expectEqual(identity1, a._rawIdentifier()) } ArrayTestSuite.test("COW.Slow.SubscriptWithIndexDoesNotReallocate") { var a = getCOWSlowArray() var identity1 = a._rawIdentifier() var startIndex = a.startIndex expectNotEqual(0, a[startIndex].value) expectEqual(identity1, a._rawIdentifier()) } ArrayTestSuite.test("COW.Fast.RemoveAtDoesNotReallocate") { do { var a = getCOWFastArray() var identity1 = a._rawIdentifier() let index1 = 1 expectEqual(identity1, a._rawIdentifier()) expectEqual(2, a[index1]) let removed = a.remove(at: index1) expectEqual(2, removed) expectEqual(identity1, a._rawIdentifier()) } do { var a1 = getCOWFastArray() var identity1 = a1._rawIdentifier() var a2 = a1 expectEqual(identity1, a1._rawIdentifier()) expectEqual(identity1, a2._rawIdentifier()) var index1 = 1 expectEqual(2, a2[index1]) expectEqual(identity1, a1._rawIdentifier()) expectEqual(identity1, a2._rawIdentifier()) let removed = a2.remove(at: index1) expectEqual(2, removed) expectEqual(identity1, a1._rawIdentifier()) expectNotEqual(identity1, a2._rawIdentifier()) } } ArrayTestSuite.test("COW.Slow.RemoveAtDoesNotReallocate") { do { var a = getCOWSlowArray() var identity1 = a._rawIdentifier() let index1 = 1 expectEqual(identity1, a._rawIdentifier()) expectEqual(2, a[index1].value) let removed = a.remove(at: index1) expectEqual(2, removed.value) expectEqual(identity1, a._rawIdentifier()) } do { var a1 = getCOWSlowArray() var identity1 = a1._rawIdentifier() var a2 = a1 expectEqual(identity1, a1._rawIdentifier()) expectEqual(identity1, a2._rawIdentifier()) var index1 = 1 expectEqual(2, a2[index1].value) expectEqual(identity1, a1._rawIdentifier()) expectEqual(identity1, a2._rawIdentifier()) let removed = a2.remove(at: index1) expectEqual(2, removed.value) expectEqual(identity1, a1._rawIdentifier()) expectNotEqual(identity1, a2._rawIdentifier()) } } ArrayTestSuite.test("COW.Fast.RemoveAllDoesNotReallocate") { do { var a = getCOWFastArray() let originalCapacity = a.capacity expectEqual(3, a.count) expectEqual(2, a[1]) a.removeAll() var identity1 = a._rawIdentifier() expectLT(a.capacity, originalCapacity) expectEqual(0, a.count) expectEqual(identity1, a._rawIdentifier()) } do { var a = getCOWFastArray() var identity1 = a._rawIdentifier() let originalCapacity = a.capacity expectEqual(3, a.count) expectEqual(2, a[1]) a.removeAll(keepingCapacity: true) expectEqual(identity1, a._rawIdentifier()) expectEqual(originalCapacity, a.capacity) expectEqual(0, a.count) } do { var a1 = getCOWFastArray() var identity1 = a1._rawIdentifier() expectEqual(3, a1.count) expectEqual(2, a1[1]) var a2 = a1 a2.removeAll() var identity2 = a2._rawIdentifier() expectEqual(identity1, a1._rawIdentifier()) expectNotEqual(identity2, identity1) expectEqual(3, a1.count) expectEqual(2, a1[1]) expectEqual(0, a2.count) // Keep variables alive. _blackHole(a1) _blackHole(a2) } do { var a1 = getCOWFastArray() var identity1 = a1._rawIdentifier() let originalCapacity = a1.capacity expectEqual(3, a1.count) expectEqual(2, a1[1]) var a2 = a1 a2.removeAll(keepingCapacity: true) var identity2 = a2._rawIdentifier() expectEqual(identity1, a1._rawIdentifier()) expectNotEqual(identity2, identity1) expectEqual(3, a1.count) expectEqual(2, a1[1]) expectEqual(originalCapacity, a2.capacity) expectEqual(0, a2.count) // Keep variables alive. _blackHole(a1) _blackHole(a2) } } ArrayTestSuite.test("COW.Slow.RemoveAllDoesNotReallocate") { do { var a = getCOWSlowArray() let originalCapacity = a.capacity expectEqual(3, a.count) expectEqual(2, a[1].value) a.removeAll() var identity1 = a._rawIdentifier() expectLT(a.capacity, originalCapacity) expectEqual(0, a.count) expectEqual(identity1, a._rawIdentifier()) } do { var a = getCOWSlowArray() var identity1 = a._rawIdentifier() let originalCapacity = a.capacity expectEqual(3, a.count) expectEqual(2, a[1].value) a.removeAll(keepingCapacity: true) expectEqual(identity1, a._rawIdentifier()) expectEqual(originalCapacity, a.capacity) expectEqual(0, a.count) } do { var a1 = getCOWSlowArray() var identity1 = a1._rawIdentifier() expectEqual(3, a1.count) expectEqual(2, a1[1].value) var a2 = a1 a2.removeAll() var identity2 = a2._rawIdentifier() expectEqual(identity1, a1._rawIdentifier()) expectNotEqual(identity2, identity1) expectEqual(3, a1.count) expectEqual(2, a1[1].value) expectEqual(0, a2.count) // Keep variables alive. _blackHole(a1) _blackHole(a2) } do { var a1 = getCOWSlowArray() var identity1 = a1._rawIdentifier() let originalCapacity = a1.capacity expectEqual(3, a1.count) expectEqual(2, a1[1].value) var a2 = a1 a2.removeAll(keepingCapacity: true) var identity2 = a2._rawIdentifier() expectEqual(identity1, a1._rawIdentifier()) expectNotEqual(identity2, identity1) expectEqual(3, a1.count) expectEqual(2, a1[1].value) expectEqual(originalCapacity, a2.capacity) expectEqual(0, a2.count) // Keep variables alive. _blackHole(a1) _blackHole(a2) } } ArrayTestSuite.test("COW.Fast.CountDoesNotReallocate") { var a = getCOWFastArray() var identity1 = a._rawIdentifier() expectEqual(3, a.count) expectEqual(identity1, a._rawIdentifier()) } ArrayTestSuite.test("COW.Slow.CountDoesNotReallocate") { var a = getCOWSlowArray() var identity1 = a._rawIdentifier() expectEqual(3, a.count) expectEqual(identity1, a._rawIdentifier()) } ArrayTestSuite.test("COW.Fast.GenerateDoesNotReallocate") { var a = getCOWFastArray() var identity1 = a._rawIdentifier() var iter = a.makeIterator() var copy = Array() while let value = iter.next() { copy.append(value) } expectEqual(copy, [ 1, 2, 3 ]) expectEqual(identity1, a._rawIdentifier()) } ArrayTestSuite.test("COW.Slow.GenerateDoesNotReallocate") { var a = getCOWSlowArray() var identity1 = a._rawIdentifier() var iter = a.makeIterator() var copy = Array() while let value = iter.next() { copy.append(value.value) } expectEqual(copy, [ 1, 2, 3 ]) expectEqual(identity1, a._rawIdentifier()) } ArrayTestSuite.test("COW.Fast.EqualityTestDoesNotReallocate") { var a1 = getCOWFastArray() var identity1 = a1._rawIdentifier() var a2 = getCOWFastArray() var identity2 = a2._rawIdentifier() expectEqual(a1, a2) expectEqual(identity1, a1._rawIdentifier()) expectEqual(identity2, a2._rawIdentifier()) a2[1] = 5 expectTrue(a1 != a2) expectEqual(identity1, a1._rawIdentifier()) expectEqual(identity2, a2._rawIdentifier()) } ArrayTestSuite.test("COW.Slow.EqualityTestDoesNotReallocate") { var a1 = getCOWSlowArray() var identity1 = a1._rawIdentifier() var a2 = getCOWSlowArray() var identity2 = a2._rawIdentifier() expectEqual(a1, a2) expectEqual(identity1, a1._rawIdentifier()) expectEqual(identity2, a2._rawIdentifier()) a2[2] = COWBox(5) expectTrue(a1 != a2) expectEqual(identity1, a1._rawIdentifier()) expectEqual(identity2, a2._rawIdentifier()) } //===----------------------------------------------------------------------===// // Index tests //===----------------------------------------------------------------------===// public struct ArrayIndexTest { public enum Operation { case append(Int) case insert(Int, at: Int) case partition(by: (OpaqueValue) throws -> Bool) case removeFirst case removeFirstN(Int) case removeLast case removeLastN(Int) case removeAt(Int) case removeAll(Bool) case removeClosedSubrange(ClosedRange) case removeHalfClosedSubrange(Range) } public let data: T public let expectedStart: Int public let expectedEnd: Int public let range: Range? public let operation: Operation public let loc: SourceLoc public init(data: T, expectedStart: Int, expectedEnd: Int, operation: Operation, range: Range? = nil, file: String = #file, line: UInt = #line) { self.data = data self.expectedStart = expectedStart self.expectedEnd = expectedEnd self.operation = operation self.range = range self.loc = SourceLoc(file, line, comment: "Array index test data") } } let indexTests: [ArrayIndexTest<[Int]>] = [ // Check how partition() affects indices. ArrayIndexTest( data: [ 99, 1010, 99 ], expectedStart: 1, expectedEnd: 2, operation: .partition(by: { $0.value > 100 } ), range: 1..<2 ), ArrayIndexTest( data: [ 99, 1010, 2020, 99 ], expectedStart: 1, expectedEnd: 3, operation: .partition(by: { $0.value > 0} ), range: 1..<3 ), ArrayIndexTest( data: [ 99, 1010, 2020, 99 ], expectedStart: 1, expectedEnd: 3, operation: .partition(by: { $0.value > 3000 }), range: 1..<3 ), // Check how partition(by:) affects indices. ArrayIndexTest( data: [ 10, 2, -33, 44, -5 ], expectedStart: 0, expectedEnd: 5, operation: .partition(by: { $0.value > 0 }) ), ArrayIndexTest( data: [ 10, 2, -33, 44, -5 ], expectedStart: 0, expectedEnd: 5, operation: .partition(by: { $0.value > 100 }) ), // Check how append affects indices. ArrayIndexTest( data: [ 2 ], expectedStart: 0, expectedEnd: 2, operation: .append(1) ), ArrayIndexTest( data: [], expectedStart: 0, expectedEnd: 1, operation: .append(1) ), // TODO: re-enable when rdar://problem/33358110 is addressed // ArrayIndexTest( // data: [ 42, 2525, 3535, 42 ], // expectedStart: 1, // expectedEnd: 3, // operation: .append(1), // range: 1..<2 // ), // Check how insert(_:at:) affects indices. ArrayIndexTest( data: [ 2 ], expectedStart: 0, expectedEnd: 2, operation: .insert(2, at: 0) ), ArrayIndexTest( data: [ 2 ], expectedStart: 0, expectedEnd: 2, operation: .insert(2, at: 1) ), ArrayIndexTest( data: [ 42, 2525, 3535, 42 ], expectedStart: 1, expectedEnd: 3, operation: .insert(2, at: 1), range: 1..<2 ), // Check how removeLast() affects indices. ArrayIndexTest( data: [ 1 ], expectedStart: 0, expectedEnd: 0, operation: .removeLast ), ArrayIndexTest( data: [ 1, 2 ], expectedStart: 0, expectedEnd: 0, operation: .removeLast ), ArrayIndexTest( data: [ 99, 1010, 99 ], expectedStart: 1, expectedEnd: 1, operation: .removeLast, range: 1..<2 ), ArrayIndexTest( data: [ 99, 1010, 2020, 99 ], expectedStart: 1, expectedEnd: 2, operation: .removeLast, range: 1..<3 ), // Check how remove(at:) affects indices. ArrayIndexTest( data: [ 1 ], expectedStart: 0, expectedEnd: 0, operation: .removeAt(0) ), ArrayIndexTest( data: [ 1, 2 ], expectedStart: 0, expectedEnd: 1, operation: .removeAt(1) ), ArrayIndexTest( data: [ 99, 1010, 99 ], expectedStart: 1, expectedEnd: 1, operation: .removeAt(1), range: 1..<2 ), ArrayIndexTest( data: [ 99, 1010, 2020, 99 ], expectedStart: 1, expectedEnd: 2, operation: .removeAt(1), range: 1..<3 ), // Check how removeAll(keepingCapacity:) affects indices. ArrayIndexTest( data: [ 1, 2, 3 ], expectedStart: 0, expectedEnd: 0, operation: .removeAll(true) ), ArrayIndexTest( data: [ 1, 2 ], expectedStart: 0, expectedEnd: 0, operation: .removeAll(false) ), ArrayIndexTest( data: [ 99, 1010, 99 ], expectedStart: 1, expectedEnd: 1, operation: .removeAll(true), range: 1..<2 ), ArrayIndexTest( data: [ 99, 1010, 2020, 99 ], expectedStart: 0, expectedEnd: 0, operation: .removeAll(false), range: 1..<2 ), // Check how removeFirst() affects indices. ArrayIndexTest( data: [ 1 ], expectedStart: 0, expectedEnd: 0, operation: .removeFirst ), ArrayIndexTest( data: [ 1, 2 ], expectedStart: 0, expectedEnd: 1, operation: .removeFirst ), ArrayIndexTest( data: [ 99, 1010, 99 ], expectedStart: 2, expectedEnd: 2, operation: .removeFirst, range: 1..<2 ), ArrayIndexTest( data: [ 99, 1010, 2020, 99 ], expectedStart: 2, expectedEnd: 3, operation: .removeFirst, range: 1..<3 ), // Check how removeFirst(_:) affects indices. ArrayIndexTest( data: [ 1, 2 ], expectedStart: 0, expectedEnd: 0, operation: .removeFirstN(2) ), ArrayIndexTest( data: [ 1, 2, 3, 4 ], expectedStart: 0, expectedEnd: 2, operation: .removeFirstN(2) ), ArrayIndexTest( data: [ 99, 1010, 99 ], expectedStart: 3, expectedEnd: 3, operation: .removeFirstN(2), range: 1..<3 ), ArrayIndexTest( data: [ 99, 1010, 2020, 99 ], expectedStart: 3, expectedEnd: 4, operation: .removeFirstN(2), range: 1..<4 ), // Check how removeLast() affects indices. ArrayIndexTest( data: [ 1 ], expectedStart: 0, expectedEnd: 0, operation: .removeLast ), ArrayIndexTest( data: [ 1, 2 ], expectedStart: 0, expectedEnd: 1, operation: .removeLast ), ArrayIndexTest( data: [ 99, 1010, 99 ], expectedStart: 1, expectedEnd: 1, operation: .removeLast, range: 1..<2 ), ArrayIndexTest( data: [ 99, 1010, 2020, 99 ], expectedStart: 1, expectedEnd: 1, operation: .removeLast, range: 1..<2 ), // Check how removeSubrange(_:ClosedRange) affects indices. ArrayIndexTest( data: [ 1 ], expectedStart: 0, expectedEnd: 0, operation: .removeHalfClosedSubrange(0..<1) ), ArrayIndexTest( data: [ 1, 2, 3 ], expectedStart: 0, expectedEnd: 1, operation: .removeHalfClosedSubrange(0..<2) ), ArrayIndexTest( data: [ 1, 2, 3 ], expectedStart: 0, expectedEnd: 1, operation: .removeHalfClosedSubrange(0..<3) ), ArrayIndexTest( data: [ 99, 1010, 99 ], expectedStart: 1, expectedEnd: 1, operation: .removeHalfClosedSubrange(1..<2), range: 1..<2 ), ArrayIndexTest( data: [ 99, 1010, 2020, 99 ], expectedStart: 1, expectedEnd: 2, operation: .removeHalfClosedSubrange(1..<2), range: 1..<3 ), ArrayIndexTest( data: [ 99, 1010, 2020, 99 ], expectedStart: 1, expectedEnd: 2, operation: .removeHalfClosedSubrange(2..<4), range: 1..<4 ), // Check how removeSubrange(_:Range) affects indices. ArrayIndexTest( data: [ 1, 2 ], expectedStart: 0, expectedEnd: 0, operation: .removeClosedSubrange(0...1) ), ArrayIndexTest( data: [ 1, 2, 3 ], expectedStart: 0, expectedEnd: 1, operation: .removeClosedSubrange(0...1) ), ArrayIndexTest( data: [ 1, 2, 3 ], expectedStart: 0, expectedEnd: 1, operation: .removeClosedSubrange(0...2) ), ArrayIndexTest( data: [ 99, 1010, 99 ], expectedStart: 1, expectedEnd: 1, operation: .removeClosedSubrange(1...2), range: 1..<3 ), ArrayIndexTest( data: [ 99, 1010, 2020, 99, 44 ], expectedStart: 1, expectedEnd: 2, operation: .removeClosedSubrange(2...3), range: 1..<4 ), ArrayIndexTest( data: [ 99, 1010, 2020, 99, 44 ], expectedStart: 1, expectedEnd: 2, operation: .removeClosedSubrange(1...2), range: 1..<4 ) ] % for Kind in ['Array', 'ContiguousArray', 'ArraySlice']: ArrayTestSuite.test("ArrayIndexTests") { for test in indexTests { let testData = test.data.map(OpaqueValue.init) % if Kind == 'ArraySlice': guard let range = test.range else { continue } var a = testData[range] % else: if test.range != nil { continue } var a = ${Kind}(testData) % end switch test.operation { case let .append(v): a.append(OpaqueValue(v)) case let .insert(v, index): a.insert(OpaqueValue(v), at: index) case let .partition(c): expectDoesNotThrow({ _ = try a.partition(by: c) }) case .removeFirst: a.removeFirst() case let .removeFirstN(n): a.removeFirst(n) case .removeLast: a.removeLast() case let .removeLastN(n): a.removeLast(n) case let .removeAt(index): a.remove(at: index) case let .removeAll(keepCapacity): a.removeAll(keepingCapacity: keepCapacity) case let .removeHalfClosedSubrange(range): a.removeSubrange(range) case let .removeClosedSubrange(range): a.removeSubrange(range) } expectEqual(test.expectedStart, a.startIndex, stackTrace: SourceLocStack().with(test.loc)) expectEqual(test.expectedEnd, a.endIndex, stackTrace: SourceLocStack().with(test.loc)) } } % end //===----------------------------------------------------------------------===// // Array and EvilCollection that changes its size while we are not looking //===----------------------------------------------------------------------===// let evilBoundsError = "EvilCollection: index out of range" final class EvilSequence : Sequence { init(_ growth: Int) { self.growth = growth } var growth: Int var _count: Int = 20 var underestimatedCount: Int { defer { _count += growth } return _count } func makeIterator() -> AnyIterator { var i = 0 return AnyIterator { if i >= self._count { return nil } let result = LifetimeTracked(i) i += 1 return result } } } final class EvilCollection : Collection { func index(after i: Int) -> Int { return i + 1 } init(_ growth: Int, boundsChecked: Bool) { self.growth = growth self.boundsChecked = boundsChecked } var growth: Int var _count: Int = 20 var boundsChecked: Bool var startIndex : Int { _count += growth return 0 } var endIndex : Int { return _count } subscript(i: Int) -> LifetimeTracked { if boundsChecked { precondition(i >= 0 && i < _count, evilBoundsError) } return LifetimeTracked(i) } // Default implementation will call _failEarlyRangeCheck, // passing in a startIndex that will grow _count faster than // necessary. func formIndex(after i: inout Int) { i += 1 } } for (step, evilBoundsCheck) in [ (1, true), (-1, false), (-1, true) ] { let message = step < 0 && evilBoundsCheck ? evilBoundsError : "invalid Collection: count differed in successive traversals" let constructionMessage = /*_isStdlibInternalChecksEnabled() && !evilBoundsCheck && step <= 0 ? "_UnsafePartiallyInitializedContiguousArrayBuffer has no more capacity" :*/ message // The invalid Collection error is a _debugPreconditon that will only fire // in a Debug assert configuration. let expectedToFail = (step < 0 && evilBoundsCheck) || _isDebugAssertConfiguration() let natureOfEvil = step > 0 ? "Growth" : "Shrinkage" let boundsChecked = evilBoundsCheck ? "BoundsChecked" : "NoBoundsCheck" let testPrefix = "MemorySafety/\(boundsChecked)/Evil\(natureOfEvil)" ArrayTestSuite.test("\(testPrefix)/Infrastructure/EvilSequence") { let evil = EvilSequence(step) let count0 = evil.underestimatedCount let count1 = evil.underestimatedCount expectNotEqual(count0, count1) if step > 0 { expectLE(count0, count1) } else { expectGE(count0, count1) } } let t1 = ArrayTestSuite.test("\(testPrefix)/Infrastructure/EvilCollection") (evilBoundsCheck && _isDebugAssertConfiguration() ? t1.crashOutputMatches(evilBoundsError) : t1) .code { let evil = EvilCollection(step, boundsChecked: evilBoundsCheck) let count0 = evil.count let count1 = evil.count expectNotEqual(count0, count1) if step > 0 { expectLE(count0, count1) } else { expectGE(count0, count1) } if evilBoundsCheck { expectCrashLater() } let x = evil[-1] _blackHole(x) } let t2 = ArrayTestSuite.test("\(testPrefix)/Construction") (_isDebugAssertConfiguration() && expectedToFail ? t2.crashOutputMatches(constructionMessage) : t2) .code { let evil = EvilCollection(step, boundsChecked: evilBoundsCheck) if expectedToFail { expectCrashLater() } let a = Array(evil) _blackHole(a) } for (op, rangeMax) in ["Grow":0, "Shrink":200] { let t3 = ArrayTestSuite.test("\(testPrefix)/replaceSubrange/\(op)Unique") (_isDebugAssertConfiguration() ? t3.crashOutputMatches(message) : t3) .code { let evil = EvilCollection(step, boundsChecked: evilBoundsCheck) var a = Array((0..<200).lazy.map { LifetimeTracked($0) }) if expectedToFail { expectCrashLater() } a.replaceSubrange(0..