Files
swift-mirror/validation-test/stdlib/ErrorHandling.swift
Erik Eckstein 66e801b0e8 tests: fix lifetimes of objects in two tests
Those tests rely on lexical object lifetimes. But lifetimes are only guaranteed for "variables" but not for temporary objects.
Storing those objects in variables fixes the issue.

This fixes the tests when running them in optimize mode and when OSSA modules are enabled.

This is part of rdar://140229560.
2025-01-27 10:38:30 +01:00

448 lines
10 KiB
Swift

// RUN: %target-run-simple-swift
// REQUIRES: executable_test
//
// Tests for error handling in standard library APIs.
//
import StdlibUnittest
import StdlibCollectionUnittest
var NoisyCount = 0
class Noisy {
init() { NoisyCount += 1 }
deinit { NoisyCount -= 1 }
}
enum SillyError : Error { case JazzHands }
var ErrorHandlingTests = TestSuite("ErrorHandling")
ErrorHandlingTests.test("ErrorHandling/withUnsafeMutableBufferPointer restores array on throw") {
var x = [1, 2, 3]
do {
// Check that the array buffer is restored when an error is thrown
// inside withUnsafeMutableBufferPointer
try x.withUnsafeMutableBufferPointer { p in
p[0] = 4
p[1] = 5
p[2] = 6
// FIXME: Seems to have recently regressed
// Buffer should be swapped out of the original array.
// expectEqual(x, [])
throw SillyError.JazzHands
}
expectUnreachable()
} catch {}
// Mutated buffer should be restored to the array.
expectEqual(x, [4, 5, 6])
}
ErrorHandlingTests.test("ErrorHandling/withUnsafeBufferPointer extends lifetime") {
let initialCount = NoisyCount
do {
let x = [Noisy(), Noisy(), Noisy()]
let countBeforeWithUBP = NoisyCount
do {
// Don't use x anywhere in this test after this point.
try x.withUnsafeBufferPointer { p in
expectEqual(NoisyCount, countBeforeWithUBP)
throw SillyError.JazzHands
}
expectUnreachable()
} catch {}
}
expectEqual(NoisyCount, initialCount)
}
ErrorHandlingTests.test("ErrorHandling/Optional.map and .flatMap") {
let x: Int? = 222
do {
let _: String? = try x.map {(n: Int) -> String in
throw SillyError.JazzHands
return "\(n)"
}
expectUnreachable()
} catch {}
do {
let _: String? = try x.flatMap {(n: Int) -> String? in
throw SillyError.JazzHands
return .some("\(n)")
}
expectUnreachable()
} catch {}
}
ErrorHandlingTests.test("ErrorHandling/withCString extends lifetime") {
do {
let x = "ad astra per aspera"
do {
// Don't use x anywhere in this test after this point.
try x.withCString { p in
expectEqual(p[0], Int8(("a" as UnicodeScalar).value))
expectEqual(p[1], Int8(("d" as UnicodeScalar).value))
throw SillyError.JazzHands
}
expectUnreachable()
} catch {}
}
// TODO: Some way to check string was deallocated?
}
ErrorHandlingTests.test("ErrorHandling/firstIndex(where:)") {
do {
let _: Int? = try [1, 2, 3].firstIndex {
throw SillyError.JazzHands
return $0 == $0
}
expectUnreachable()
} catch {}
}
ErrorHandlingTests.test("ErrorHandling/split") {
do {
let _: [Substring] = try "foo".split { _ in
throw SillyError.JazzHands
return false
}
expectUnreachable()
} catch {}
do {
let _: [ArraySlice<Character>]
= try AnySequence("foo").split { _ in
throw SillyError.JazzHands
return false
}
expectUnreachable()
} catch {}
}
ErrorHandlingTests.test("ErrorHandling/forEach") {
var loopCount = 0
do {
try [1, 2, 3].forEach {
loopCount += 1
if $0 == 2 {
throw SillyError.JazzHands
}
}
expectUnreachable()
} catch {}
expectEqual(loopCount, 2)
}
ErrorHandlingTests.test("ErrorHandling/Optional flatMap") {
var loopCount = 0
do {
let _: [Int] = try [1, 2, 3].flatMap {
loopCount += 1
if $0 == 2 {
throw SillyError.JazzHands
}
return .some($0)
}
expectUnreachable()
} catch {}
expectEqual(loopCount, 2)
}
ErrorHandlingTests.test("ErrorHandling/Array flatMap") {
var loopCount = 0
do {
let _: [Int] = try [1, 2, 3].flatMap {(x) -> [Int] in
loopCount += 1
if x == 2 {
throw SillyError.JazzHands
}
return Array(repeating: x, count: x)
}
expectUnreachable()
} catch {}
expectEqual(loopCount, 2)
}
ErrorHandlingTests.test("ErrorHandling/min") {
do {
let _: Int? = try [1, 2, 3].min { _, _ in
throw SillyError.JazzHands
return false
}
expectUnreachable()
} catch {}
do {
let _: Int? = try [1, 2, 3].max { _, _ in
throw SillyError.JazzHands
return false
}
expectUnreachable()
} catch {}
}
ErrorHandlingTests.test("ErrorHandling/starts(with:)") {
do {
let _: Bool = try [1, 2, 3].starts(with: [1, 2]) { _, _ in
throw SillyError.JazzHands
return false
}
expectUnreachable()
} catch {}
}
ErrorHandlingTests.test("ErrorHandling/elementsEqual") {
do {
let _: Bool = try [1, 2, 3].elementsEqual([1, 2, 3]) { _, _ in
throw SillyError.JazzHands
return false
}
expectUnreachable()
} catch {}
}
ErrorHandlingTests.test("ErrorHandling/lexicographicallyPrecedes(_:)") {
do {
let _: Bool = try [1, 2, 3].lexicographicallyPrecedes([0, 2, 3]) { _, _ in
throw SillyError.JazzHands
return false
}
expectUnreachable()
} catch {}
}
ErrorHandlingTests.test("ErrorHandling/contains") {
do {
let _: Bool = try [1, 2, 3].contains { _ in
throw SillyError.JazzHands
return false
}
expectUnreachable()
} catch {}
}
ErrorHandlingTests.test("ErrorHandling/reduce") {
var loopCount = 0
do {
let _: Int = try [1, 2, 3, 4, 5].reduce(0) {
(x: Int, y: Int) -> Int
in
loopCount += 1
let total = x + y
if total > 5 {
throw SillyError.JazzHands
}
return total
}
expectUnreachable()
} catch {}
expectEqual(loopCount, 3)
}
func explosiveBoolean() throws -> Bool {
throw SillyError.JazzHands
}
func explosiveInt() throws -> Int {
throw SillyError.JazzHands
}
ErrorHandlingTests.test("ErrorHandling/operators") {
do {
if try true && explosiveBoolean() {
expectUnreachable()
}
expectUnreachable()
} catch {}
do {
if try false || explosiveBoolean() {
expectUnreachable()
}
expectUnreachable()
} catch {}
do {
if try nil ?? explosiveInt() == 0 {
expectUnreachable()
}
expectUnreachable()
} catch {}
}
ErrorHandlingTests.test("ErrorHandling/Sequence map") {
let initialCount = NoisyCount
for throwAtCount in 0...3 {
let sequence = MinimalSequence(elements: [1, 2, 3])
var loopCount = 0
do {
let result: [Noisy] = try sequence.map { _ in
if loopCount == throwAtCount {
throw SillyError.JazzHands
}
loopCount += 1
return Noisy()
}
expectEqual(NoisyCount, initialCount + 3)
expectEqual(result.count, 3)
} catch {}
expectEqual(NoisyCount, initialCount)
}
}
ErrorHandlingTests.test("ErrorHandling/Sequence filter") {
let initialCount = NoisyCount
for condition in [true, false] {
for throwAtCount in 0...3 {
let n1 = Noisy()
let n2 = Noisy()
let n3 = Noisy()
let sequence = [n1, n2, n3]
var loopCount = 0
do {
let result: [Noisy] = try sequence.filter { _ in
if loopCount == throwAtCount {
throw SillyError.JazzHands
}
loopCount += 1
return condition
}
expectEqual(NoisyCount, initialCount + sequence.count)
expectEqual(result.count, condition ? 3 : 0)
} catch {}
}
expectEqual(NoisyCount, initialCount)
}
}
ErrorHandlingTests.test("ErrorHandling/Collection map") {
let initialCount = NoisyCount
let collection = [1, 2, 3]
for throwAtCount in 0...3 {
var loopCount = 0
do {
let result: [Noisy] = try collection.map { _ in
if loopCount == throwAtCount {
throw SillyError.JazzHands
}
loopCount += 1
return Noisy()
}
expectEqual(NoisyCount, initialCount + 3)
expectEqual(result.count, 3)
} catch {}
expectEqual(NoisyCount, initialCount)
}
}
ErrorHandlingTests.test("ErrorHandling/sort") {
let collection = Array(1...5)
forAllPermutations(collection) { sequence in
for i in 0..<sequence.count {
var s = sequence
let throwElement = sequence[i]
do {
try s.sort { (a, b) throws -> Bool in
if b == throwElement {
throw SillyError.JazzHands
}
return a < b
}
} catch {}
//Check no element should lost and added
expectEqualsUnordered(collection, s)
}
}
}
ErrorHandlingTests.test("ErrorHandling/sorted") {
let collection = Array(1...5)
forAllPermutations(collection) { sequence in
for i in 0..<sequence.count {
let s = sequence
var thrown = false
let throwElement = sequence[i]
var result: [Int] = []
do {
result = try s.sorted { (a, b) throws -> Bool in
if b == throwElement {
thrown = true
throw SillyError.JazzHands
}
return a < b
}
} catch {}
//Check actual sequence should not mutate
expectEqualSequence(sequence, s)
if thrown {
//Check result should be empty when thrown
expectEqualSequence([], result)
} else {
//Check result should be sorted when not thrown
expectEqualSequence(collection, result)
}
}
}
}
ErrorHandlingTests.test("ErrorHandling/sort") {
let collection = Array(1...5)
forAllPermutations(collection) { sequence in
for i in 0..<sequence.count {
var s = sequence
let throwElement = sequence[i]
do {
try s.sort { (a, b) throws -> Bool in
if b == throwElement {
throw SillyError.JazzHands
}
return a < b
}
} catch {}
//Check no element should lost and added
expectEqualsUnordered(collection, s)
}
}
}
ErrorHandlingTests.test("ErrorHandling/sorted") {
let collection = Array(1...5)
forAllPermutations(collection) { sequence in
for i in 0..<sequence.count {
let s = sequence
var thrown = false
let throwElement = sequence[i]
var result: [Int] = []
do {
result = try s.sorted { (a, b) throws -> Bool in
if b == throwElement {
thrown = true
throw SillyError.JazzHands
}
return a < b
}
} catch {}
//Check actual sequence should not mutate
expectEqualSequence(sequence, s)
if thrown {
//Check result should be empty when thrown
expectEqualSequence([], result)
} else {
//Check result should be sorted when not thrown
expectEqualSequence(collection, result)
}
}
}
}
runAllTests()