| // RUN: %empty-directory(%t) |
| // RUN: %gyb %s -o %t/ArrayTraps.swift |
| // RUN: %line-directive %t/ArrayTraps.swift -- %target-build-swift %t/ArrayTraps.swift -o %t/a.out_Debug |
| // RUN: %line-directive %t/ArrayTraps.swift -- %target-build-swift %t/ArrayTraps.swift -o %t/a.out_Release -O |
| // |
| // RUN: %line-directive %t/ArrayTraps.swift -- %target-run %t/a.out_Debug |
| // RUN: %line-directive %t/ArrayTraps.swift -- %target-run %t/a.out_Release |
| // REQUIRES: executable_test |
| |
| import StdlibUnittest |
| |
| let testSuiteSuffix = _isDebugAssertConfiguration() ? "_debug" : "_release" |
| |
| %{ |
| # We test for bounds-checking traps for both reading and writing |
| # both single elements and slices of all three different array |
| # types. |
| array_types = ('Array', 'ContiguousArray', 'ArraySlice') |
| |
| def bounds_trap(index, expr_to_write): |
| global io |
| return 'expectCrashLater()\n' + ( |
| 'let x = a[%s]' % index |
| if io == 'read' else |
| 'a[%s] = %s' % (index, expr_to_write)) |
| }% |
| |
| % for ArrayTy in array_types: |
| |
| var ${ArrayTy}Traps = TestSuite("${ArrayTy}Traps" + testSuiteSuffix) |
| var ${ArrayTy}LowerBoundOutOfRangeErrorMsg = ( |
| % if ArrayTy in ['Array', 'ContiguousArray']: |
| "Negative ${ArrayTy} index is out of range" |
| % else: |
| "${ArrayTy} index is out of range (before startIndex)" |
| % end |
| ) |
| |
| var ${ArrayTy}BoundsErrorMsg = ( |
| % if ArrayTy in ['Array', 'ContiguousArray']: |
| "Index out of range" |
| % else: |
| "Index out of bounds" |
| % end |
| ) |
| |
| % for io in ['read', 'write']: |
| |
| ${ArrayTy}Traps.test("bounds1/${io}") |
| .skip(.custom( |
| { _isFastAssertConfiguration() }, |
| reason: "this trap is not guaranteed to happen in -Ounchecked")) |
| .crashOutputMatches(_isDebugAssertConfiguration() ? |
| ${ArrayTy}BoundsErrorMsg : "") |
| .code { |
| var a: ${ArrayTy}<Int> = [] |
| ${bounds_trap(index='0', expr_to_write='1')} |
| } |
| |
| ${ArrayTy}Traps.test("bounds2/${io}") |
| .skip(.custom( |
| { _isFastAssertConfiguration() }, |
| reason: "this trap is not guaranteed to happen in -Ounchecked")) |
| .crashOutputMatches(_isDebugAssertConfiguration() ? |
| ${ArrayTy}BoundsErrorMsg : "") |
| .code { |
| var a: ${ArrayTy}<Int> = [] |
| ${bounds_trap(index='100', expr_to_write='1')} |
| } |
| |
| ${ArrayTy}Traps.test("bounds3/${io}") |
| .skip(.custom( |
| { _isFastAssertConfiguration() }, |
| reason: "this trap is not guaranteed to happen in -Ounchecked")) |
| .crashOutputMatches(_isDebugAssertConfiguration() ? |
| ${ArrayTy}BoundsErrorMsg : "") |
| .code { |
| var a: ${ArrayTy} = [ 10, 20, 30 ] |
| ${bounds_trap(index='3', expr_to_write='1')} |
| } |
| |
| ${ArrayTy}Traps.test("sliceBounds0/${io}") |
| .skip(.custom( |
| { _isFastAssertConfiguration() }, |
| reason: "this trap is not guaranteed to happen in -Ounchecked")) |
| .crashOutputMatches(_isDebugAssertConfiguration() ? |
| ${ArrayTy}LowerBoundOutOfRangeErrorMsg : "") |
| .code { |
| var a: ${ArrayTy}<Int> = [] |
| ${bounds_trap(index='-1..<1', expr_to_write='ArraySlice()')} |
| } |
| |
| ${ArrayTy}Traps.test("sliceBounds1/${io}") |
| .skip(.custom( |
| { _isFastAssertConfiguration() }, |
| reason: "this trap is not guaranteed to happen in -Ounchecked")) |
| .crashOutputMatches(_isDebugAssertConfiguration() ? |
| ${ArrayTy}LowerBoundOutOfRangeErrorMsg : "") |
| .code { |
| var a: ${ArrayTy} = [ 1 ] |
| ${bounds_trap(index='-1..<1', expr_to_write='ArraySlice()')} |
| } |
| |
| ${ArrayTy}Traps.test("sliceBounds2/${io}") |
| .skip(.custom( |
| { _isFastAssertConfiguration() }, |
| reason: "this trap is not guaranteed to happen in -Ounchecked")) |
| .crashOutputMatches(_isDebugAssertConfiguration() ? |
| "${ArrayTy} index is out of range" : "") |
| .code { |
| var a: ${ArrayTy} = [ 1 ] |
| ${bounds_trap(index='0..<2', expr_to_write='ArraySlice()')} |
| } |
| |
| ${ArrayTy}Traps.test("sliceBounds3/${io}") |
| .skip(.custom( |
| { _isFastAssertConfiguration() }, |
| reason: "this trap is not guaranteed to happen in -Ounchecked")) |
| .crashOutputMatches(_isDebugAssertConfiguration() ? |
| "${ArrayTy} index is out of range" : "") |
| .code { |
| var a: ${ArrayTy} = [ 1 ] |
| ${bounds_trap(index='1..<2', expr_to_write='ArraySlice()')} |
| } |
| |
| % end |
| |
| ${ArrayTy}Traps.test("PopFromEmpty") |
| .crashOutputMatches(_isDebugAssertConfiguration() ? |
| "can't remove last element from an empty collection" : "") |
| .code { |
| var a: ${ArrayTy}<Int> = [] |
| expectCrashLater() |
| a.removeLast() |
| } |
| |
| % for index in -1, 2: |
| ${ArrayTy}Traps.test("${'insert(_:at:)/%s' % index}") |
| .skip(.custom( |
| { _isFastAssertConfiguration() }, |
| reason: "this trap is not guaranteed to happen in -Ounchecked")) |
| .crashOutputMatches(_isDebugAssertConfiguration() ? |
| % if index < 0: |
| ${ArrayTy}LowerBoundOutOfRangeErrorMsg |
| % else: |
| "${ArrayTy} index is out of range" |
| %end |
| : "") |
| .code { |
| var a: ${ArrayTy}<Int> = [42] |
| expectCrashLater() |
| a.insert(3, at: ${index}) |
| } |
| % end |
| |
| % for index in -1, 1, 2: |
| ${ArrayTy}Traps.test("${'remove(at:)/%s' % index}") |
| .skip(.custom( |
| { _isFastAssertConfiguration() }, |
| reason: "this trap is not guaranteed to happen in -Ounchecked")) |
| .crashOutputMatches(_isDebugAssertConfiguration() ? |
| ${ArrayTy}BoundsErrorMsg : "") |
| .code { |
| var a: ${ArrayTy}<Int> = [42] |
| expectCrashLater() |
| a.remove(at: ${index}) |
| } |
| % end |
| % end |
| |
| ArrayTraps.test("unsafeLength") |
| .skip(.custom( |
| { _isFastAssertConfiguration() }, |
| reason: "this trap is not guaranteed to happen in -Ounchecked")) |
| .crashOutputMatches(_isDebugAssertConfiguration() ? |
| "UnsafeBufferPointer with negative count" : "") |
| .code { |
| var a = [ 42, 77, 88 ] |
| |
| expectEqual(42, a.withUnsafeBufferPointer({ $0[0] })) |
| |
| expectCrashLater() |
| |
| _ = a.withUnsafeBufferPointer { |
| UnsafeBufferPointer(start: $0.baseAddress, count: -1) |
| } |
| } |
| |
| runAllTests() |
| |