// RUN: %target-run-simple-swiftgyb // REQUIRES: executable_test import StdlibUnittest import StdlibCollectionUnittest // Tests protocol SubscriptTest { static var start: Int { get } static var end: Int { get } static var elementCount: Int { get } } extension SubscriptTest { static var elementCount: Int { get { return end - start } } static func allocateForRawBuffer(count: Int) -> UnsafeMutableRawPointer { return UnsafeMutableRawPointer.allocate(bytes: count, alignedTo: 1) } static func allocateForBuffer(count: Int ) -> UnsafeMutablePointer> { return UnsafeMutablePointer>.allocate(capacity: count) } static func deallocateForRawBuffer( _ memory: UnsafeMutableRawPointer, count: Int) { memory.deallocate(bytes: count, alignedTo: 1) } static func deallocateForBuffer( _ memory: UnsafeMutablePointer>, count: Int) { memory.deallocate(capacity: count) } % for SelfType in ['UnsafeRawBufferPointer', 'UnsafeMutableRawBufferPointer']: /// Create and populate an `${SelfType}` for use with unit tests. /// PRECONDITION: `memory` must be allocated with space for /// `SubscriptGetTest.elementCount` bytes. static func create${SelfType}(from memory: UnsafeMutableRawPointer) -> ${SelfType} { for i in Self.start..>) -> ${SelfType}> { for i in Self.start..] let replacementClosedValues: [OpaqueValue] /// The values that should be expected by slicing the UBP, or `nil` if the /// test is expected to crash. let expectedValues: [Int]? /// Same as `expectedValues`, but for closed ranges. `nil` if no difference. let expectedClosedValues: [Int]? let loc: SourceLoc init( rangeSelection: RangeSelection, replacementValues: [Int], replacementClosedValues: [Int]? = nil, expectedValues: [Int]? = nil, expectedClosedValues: [Int]? = nil, file: String = #file, line: UInt = #line ) { self.rangeSelection = rangeSelection self.expectedValues = expectedValues self.expectedClosedValues = expectedClosedValues ?? expectedValues self.replacementValues = replacementValues.map { OpaqueValue($0) } if let replacements = replacementClosedValues { self.replacementClosedValues = replacements.map { OpaqueValue($0) } } else { self.replacementClosedValues = self.replacementValues } self.loc = SourceLoc(file, line, comment: "test data") } /// Create and populate an UnsafeMutableRawBufferPointer slice for use with /// unit tests. /// PRECONDITION: `memory` must be allocated with space for /// `replacementValues.count` bytes. func replacementValuesSlice( from memory: UnsafeMutableRawPointer, replacementValues: [OpaqueValue] ) -> UnsafeMutableRawBufferPointer { for (i, value) in replacementValues.enumerated() { memory.initializeMemory( as: UInt8.self, at: i, to: numericCast(value.value)) } let buffer = UnsafeMutableRawBufferPointer( start: memory, count: replacementValues.count) let fullRange = RangeSelection.full.range(in: buffer) return buffer[fullRange] } /// Create and populate an UnsafeMutableBufferPointer slice for use with unit /// tests. /// PRECONDITION: `memory` must be allocated with space for /// `replacementValues.count` elements. func replacementValuesSlice( from memory: UnsafeMutablePointer>, replacementValues: [OpaqueValue] ) -> MutableRandomAccessSlice>> { for (i, value) in replacementValues.enumerated() { memory[i] = value } let buffer = UnsafeMutableBufferPointer( start: memory, count: replacementValues.count) let fullRange = RangeSelection.full.range(in: buffer) return buffer[fullRange] } } let subscriptSetTests : [SubscriptSetTest] = [ // Valid, empty. SubscriptSetTest( rangeSelection: .emptyRange, replacementValues: [], expectedValues: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]), // Valid, edges. SubscriptSetTest( rangeSelection: .leftEdge, replacementValues: [], replacementClosedValues: [91], expectedValues: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], expectedClosedValues: [91, 1, 2, 3, 4, 5, 6, 7, 8, 9]), SubscriptSetTest( rangeSelection: .rightEdge, replacementValues: [], replacementClosedValues: [91], expectedValues: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], expectedClosedValues: [0, 1, 2, 3, 4, 5, 6, 7, 8, 91]), // Valid, internal. SubscriptSetTest( rangeSelection: .leftHalf, replacementValues: [10, 11, 12, 13, 14], expectedValues: [10, 11, 12, 13, 14, 5, 6, 7, 8, 9]), SubscriptSetTest( rangeSelection: .rightHalf, replacementValues: [10, 11, 12, 13, 14], expectedValues: [0, 1, 2, 3, 4, 10, 11, 12, 13, 14]), SubscriptSetTest( rangeSelection: .middle, replacementValues: [10, 11, 12, 13, 14, 15], expectedValues: [0, 1, 10, 11, 12, 13, 14, 15, 8, 9]), SubscriptSetTest( rangeSelection: .full, replacementValues: [10, 11, 12, 13, 14, 15, 16, 17, 18, 19], expectedValues: [10, 11, 12, 13, 14, 15, 16, 17, 18, 19]), // Invalid, range and replacement count mismatch. SubscriptSetTest( rangeSelection: .leftEdge, replacementValues: [9, 9], replacementClosedValues: [9, 9, 9]), SubscriptSetTest( rangeSelection: .offsets(1, 2), replacementValues: [], replacementClosedValues: [9]), SubscriptSetTest( rangeSelection: .offsets(1, 2), replacementValues: [9, 9], replacementClosedValues: [9, 9, 9]), SubscriptSetTest( rangeSelection: .offsets(2, 5), replacementValues: [9, 9], replacementClosedValues: [9, 9, 9]), SubscriptSetTest( rangeSelection: .offsets(2, 5), replacementValues: [9, 9, 9, 9], replacementClosedValues: [9, 9, 9, 9, 9]), // Invalid, bottom out of bounds. SubscriptSetTest( rangeSelection: .offsets(-1, -1), replacementValues: []), SubscriptSetTest( rangeSelection: .offsets(-1, 0), replacementValues: [9]), SubscriptSetTest( rangeSelection: .offsets(-3, 5), replacementValues: [9, 9, 9, 9, 9, 9, 9, 9]), // Invalid, top out of bounds. SubscriptSetTest( rangeSelection: .offsets(10, 10), replacementValues: [], expectedValues: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], expectedClosedValues: []), // Only crash on a closed range. SubscriptSetTest( rangeSelection: .offsets(9, 10), replacementValues: [91], expectedValues: [0, 1, 2, 3, 4, 5, 6, 7, 8, 91], expectedClosedValues: []), // Only crash on a closed range. SubscriptSetTest( rangeSelection: .offsets(11, 11), replacementValues: []), SubscriptSetTest( rangeSelection: .offsets(8, 15), replacementValues: [9, 9, 9, 9, 9, 9, 9]), // Invalid, both out of bounds. SubscriptSetTest( rangeSelection: .offsets(-1, 10), replacementValues: [9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9]), SubscriptSetTest( rangeSelection: .offsets(-2, 11), replacementValues: [9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9]), ] // Test Suites var UnsafeBufferPointerTestSuite = TestSuite("UnsafeBufferPointer") var UnsafeMutableBufferPointerTestSuite = TestSuite("UnsafeMutableBufferPointer") var UnsafeRawBufferPointerTestSuite = TestSuite("UnsafeRawBufferPointer") var UnsafeMutableRawBufferPointerTestSuite = TestSuite("UnsafeMutableRawBufferPointer") % for (SelfName, IsMutable, IsRaw, SelfType, PointerType) in [ % ('UnsafeBufferPointer', False, False, 'UnsafeBufferPointer', 'UnsafePointer'), % ('UnsafeMutableBufferPointer', True, False, 'UnsafeMutableBufferPointer', 'UnsafeMutablePointer'), % ('UnsafeRawBufferPointer', False, True, 'UnsafeRawBufferPointer', 'UnsafeRawPointer'), % ('UnsafeMutableRawBufferPointer', True, True, 'UnsafeMutableRawBufferPointer', 'UnsafeMutableRawPointer') % ]: % if IsRaw: ${SelfName}TestSuite.test("AssociatedTypes") { expectRandomAccessCollectionAssociatedTypes( collectionType: ${SelfType}.self, iteratorType: ${SelfType}.Iterator.self, subSequenceType: ${SelfType}.self, indexType: Int.self, indexDistanceType: Int.self, indicesType: CountableRange.self) expect${'Mutable' if IsMutable else ''}CollectionType(${SelfType}.self) } % else: # !IsRaw ${SelfName}TestSuite.test("AssociatedTypes") { typealias Subject = ${SelfName}> expectRandomAccessCollectionAssociatedTypes( collectionType: Subject.self, iteratorType: IndexingIterator.self, subSequenceType: ${'Mutable' if IsMutable else ''}RandomAccessSlice.self, indexType: Int.self, indexDistanceType: Int.self, indicesType: CountableRange.self) expect${'Mutable' if IsMutable else ''}CollectionType(Subject.self) } % end # !IsRaw ${SelfName}TestSuite.test("nilBaseAddress") { let emptyBuffer = ${SelfType}(start: nil, count: 0) expectNil(emptyBuffer.baseAddress) expectEqual(0, emptyBuffer.count) expectTrue(emptyBuffer.startIndex == emptyBuffer.endIndex) var iter = emptyBuffer.makeIterator() expectNil(iter.next()) expectEqualSequence([], emptyBuffer) } ${SelfName}TestSuite.test("nonNilButEmpty") { let emptyAllocated = UnsafeMutablePointer.allocate(capacity: 0) defer { emptyAllocated.deallocate(capacity: 0) } let emptyBuffer = ${SelfType}(start: ${PointerType}(emptyAllocated), count: 0) expectEqual(emptyAllocated, emptyBuffer.baseAddress) expectEqual(0, emptyBuffer.count) expectTrue(emptyBuffer.startIndex == emptyBuffer.endIndex) var iter = emptyBuffer.makeIterator() expectNil(iter.next()) expectEqualSequence([], emptyBuffer) } % if IsRaw: ${SelfName}TestSuite.test("nonNilNonEmpty") { let count = 4 let allocated = UnsafeMutableRawPointer.allocate(bytes: count, alignedTo: 1) defer { allocated.deallocate(bytes: count, alignedTo: 1) } let uint8Ptr = allocated.initializeMemory(as: UInt8.self, count: count, to: 1) uint8Ptr[count - 1] = 2 let buffer = ${SelfType}(start: ${PointerType}(allocated), count: count - 1) expectEqual(allocated, buffer.baseAddress) expectEqual(count - 1, buffer.count) expectEqual(count - 1, buffer.endIndex - buffer.startIndex) uint8Ptr[1] = 0 expectEqual(1, buffer[0]) expectEqual(0, buffer[1]) expectEqual(1, buffer[2]) var iter = buffer.makeIterator() expectEqual(1, iter.next()) expectEqual(0, iter.next()) expectEqual(1, iter.next()) expectNil(iter.next()) expectEqualSequence([1, 0, 1], buffer.map { Int($0) }) expectEqual(2, uint8Ptr[count-1]) } % else: # !IsRaw ${SelfName}TestSuite.test("nonNilNonEmpty") { let count = 4 let allocated = UnsafeMutablePointer.allocate(capacity: count) defer { allocated.deallocate(capacity: count) } allocated.initialize(to: 1.0, count: count) allocated[count - 1] = 2.0 let buffer = ${SelfType}(start: ${PointerType}(allocated), count: count - 1) expectEqual(allocated, buffer.baseAddress) expectEqual(count - 1, buffer.count) expectEqual(count - 1, buffer.endIndex - buffer.startIndex) allocated[1] = 0.0 expectEqual(1.0, buffer[0]) expectEqual(0.0, buffer[1]) expectEqual(1.0, buffer[2]) var iter = buffer.makeIterator() expectEqual(1.0, iter.next()) expectEqual(0.0, iter.next()) expectEqual(1.0, iter.next()) expectNil(iter.next()) expectEqualSequence([1.0, 0.0, 1.0], buffer) expectEqual(2.0, allocated[count-1]) } % end # !IsRaw ${SelfName}TestSuite.test("badCount") .skip(.custom( { _isFastAssertConfiguration() }, reason: "this trap is not guaranteed to happen in -Ounchecked")) .code { expectCrashLater() let emptyAllocated = UnsafeMutablePointer.allocate(capacity: 0) defer { emptyAllocated.deallocate(capacity: 0) } let buffer = ${SelfType}(start: ${PointerType}(emptyAllocated), count: -1) _ = buffer } ${SelfName}TestSuite.test("badNilCount") .skip(.custom( { _isFastAssertConfiguration() }, reason: "this trap is not guaranteed to happen in -Ounchecked")) .code { expectCrashLater() let buffer = ${SelfType}(start: nil, count: 1) _ = buffer } ${SelfName}TestSuite.test("RandomAccessCollection") { let elementCount = SubscriptGetTest.elementCount var memory = SubscriptGetTest.allocateFor${'Raw' if IsRaw else ''}Buffer( count: elementCount) defer { SubscriptGetTest.deallocateFor${'Raw' if IsRaw else ''}Buffer( memory, count: elementCount) } var memoryCopy = SubscriptGetTest.allocateFor${'Raw' if IsRaw else ''}Buffer( count: elementCount) defer { SubscriptGetTest.deallocateFor${'Raw' if IsRaw else ''}Buffer( memoryCopy, count: elementCount) } var buffer = SubscriptGetTest.create${SelfName}(from: memory) var expected = UnsafeMutable${'Raw' if IsRaw else ''}BufferPointer( start: memoryCopy, count: buffer.count) % if IsRaw: expected.copyBytes(from: buffer) checkRandomAccessCollection(expected, buffer) { $0 == $1 } % else: expected.baseAddress!.initialize(from: buffer) checkRandomAccessCollection(expected, buffer) { $0.value == $1.value } % end } % for RangeName in ['range', 'countableRange', 'closedRange', 'countableClosedRange']: ${SelfName}TestSuite.test("subscript/${RangeName}/get").forEach(in: subscriptGetTests) { (test) in let expectedValues: [Int]? % if 'closed' in RangeName.lower(): if test.rangeSelection.isEmpty { return } // Treat an empty set as nil for closed ranges. if test.expectedClosedValues?.isEmpty ?? true { expectedValues = nil } else { expectedValues = test.expectedClosedValues } % else: expectedValues = test.expectedValues % end let elementCount = SubscriptGetTest.elementCount var memory = SubscriptGetTest.allocateFor${'Raw' if IsRaw else ''}Buffer( count: elementCount) defer { SubscriptGetTest.deallocateFor${'Raw' if IsRaw else ''}Buffer( memory, count: elementCount) } let buffer = SubscriptGetTest.create${SelfName}(from: memory) % if IsRaw: // A raw buffer pointer has debug bounds checks on indices, and // (currently) invokes Collection._failEarlyRangeCheck in nondebug mode. if expectedValues == nil { expectCrashLater() } % end let range = test.rangeSelection.${RangeName}(in: buffer) if _isDebugAssertConfiguration() { if expectedValues == nil { expectCrashLater() } } let slice = buffer[range] // If expectedValues is nil, we should have crashed above. Allowing the // following code to crash leads to false positives. if let expectedValues = expectedValues { expectEqual( expectedValues, % if IsRaw: slice.map { Int($0) }, % else: slice.map { $0.value }, % end stackTrace: SourceLocStack().with(test.loc) ) } } % end % end % for (SelfName, IsRaw) in [ % ('UnsafeMutableBufferPointer', False), % ('UnsafeMutableRawBufferPointer', True) % ]: % for RangeName in ['range', 'countableRange', 'closedRange', 'countableClosedRange']: UnsafeMutable${'Raw' if IsRaw else ''}BufferPointerTestSuite.test("subscript/${RangeName}/set") % if not IsRaw: // UnsafeRawBuffer (currently) invokes Collection._failEarlyRangeCheck // in nondebug mode. .skip(.custom( { !_isDebugAssertConfiguration() }, reason: "This tests a debug precondition.")) % end .forEach(in: subscriptSetTests) { (test) in let expectedValues: [Int]? let replacementValues: [OpaqueValue] let elementCount = SubscriptSetTest.elementCount % if 'closed' in RangeName.lower(): if test.rangeSelection.isEmpty { return } // Treat an empty set as nil for closed ranges. if test.expectedClosedValues?.isEmpty ?? true { expectedValues = nil } else { expectedValues = test.expectedClosedValues } replacementValues = test.replacementClosedValues let isOutOfBounds: () -> Bool = { switch test.rangeSelection { case let .offsets(lowerBound, upperBound): return lowerBound < 0 || upperBound >= elementCount default: return false } } % else: expectedValues = test.expectedValues replacementValues = test.replacementValues let isOutOfBounds: () -> Bool = { switch test.rangeSelection { case let .offsets(lowerBound, upperBound): return lowerBound < 0 || upperBound > elementCount default: return false } } % end var memory = SubscriptSetTest.allocateFor${'Raw' if IsRaw else ''}Buffer( count: elementCount) defer { SubscriptSetTest.deallocateFor${'Raw' if IsRaw else ''}Buffer( memory, count: elementCount) } var sliceMemory = SubscriptSetTest.allocateFor${'Raw' if IsRaw else ''}Buffer( count: replacementValues.count) defer { SubscriptSetTest.deallocateFor${'Raw' if IsRaw else ''}Buffer( sliceMemory, count: replacementValues.count) } var buffer = SubscriptSetTest.create${SelfName}(from: memory) % if IsRaw: // A raw buffer pointer has debug bounds checks on indices, and // (currently) invokes Collection._failEarlyRangeCheck in nondebug mode. if _isDebugAssertConfiguration() || isOutOfBounds() { if expectedValues == nil { expectCrashLater() } } % end let range = test.rangeSelection.${RangeName}(in: buffer) let replacementSlice = test.replacementValuesSlice( from: sliceMemory, replacementValues: replacementValues) if _isDebugAssertConfiguration() { if expectedValues == nil { expectCrashLater() } } buffer[range] = replacementSlice // If expectedValues is nil, we should have crashed above. Allowing the // following code to crash leads to false positives. if let expectedValues = expectedValues { expectEqual( expectedValues, % if IsRaw: buffer.map { Int($0) }, % else: buffer.map { $0.value }, % end stackTrace: SourceLocStack().with(test.loc) ) } } % end # RangeName % end # SelfName UnsafeMutableRawBufferPointerTestSuite.test("changeElementViaBuffer") { let count = 4 let allocated = UnsafeMutableRawPointer.allocate(bytes: count, alignedTo: 1) defer { allocated.deallocate(bytes: count, alignedTo: 1) } let uint8Ptr = allocated.initializeMemory(as: UInt8.self, count: count, to: 1) uint8Ptr[count-1] = UInt8.max var buffer = UnsafeMutableRawBufferPointer(start: allocated, count: count - 1) buffer[1] = 0 expectEqual(1, buffer[0]) expectEqual(0, buffer[1]) expectEqual(1, buffer[2]) expectEqual(1, uint8Ptr[0]) expectEqual(0, uint8Ptr[1]) expectEqual(1, uint8Ptr[2]) expectEqual(UInt8.max, uint8Ptr[count-1]) buffer.sort() expectEqual(0, buffer[0]) expectEqual(1, buffer[1]) expectEqual(1, buffer[2]) expectEqual(0, uint8Ptr[0]) expectEqual(1, uint8Ptr[1]) expectEqual(1, uint8Ptr[2]) expectEqual(UInt8.max, uint8Ptr[count-1]) } UnsafeMutableBufferPointerTestSuite.test("changeElementViaBuffer") { let count = 4 let allocated = UnsafeMutablePointer.allocate(capacity: count) defer { allocated.deallocate(capacity: count) } allocated.initialize(to: 1.0, count: count) allocated[count-1] = -1.0 var buffer = UnsafeMutableBufferPointer(start: allocated, count: count - 1) buffer[1] = 0.0 expectEqual(1.0, buffer[0]) expectEqual(0.0, buffer[1]) expectEqual(1.0, buffer[2]) expectEqual(1.0, allocated[0]) expectEqual(0.0, allocated[1]) expectEqual(1.0, allocated[2]) expectEqual(-1.0, allocated[count-1]) buffer.sort() expectEqual(0.0, buffer[0]) expectEqual(1.0, buffer[1]) expectEqual(1.0, buffer[2]) expectEqual(0.0, allocated[0]) expectEqual(1.0, allocated[1]) expectEqual(1.0, allocated[2]) expectEqual(-1.0, allocated[count-1]) } runAllTests()