blob: b632fa0609edf9080abd1b3a01e2becc4ed533dc [file] [log] [blame]
// 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 -Onone
// RUN: %line-directive %t/ArrayTraps.swift -- %target-build-swift %t/ArrayTraps.swift -o %t/a.out_Release -O
//
// RUN: %target-codesign %t/a.out_Debug
// RUN: %target-codesign %t/a.out_Release
// 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()