Files
swift-mirror/validation-test/StdlibUnittest/RaceTest.swift
David Zarzycki 283713d61d [Testing] Formalize stress tests
Stress tests are, by definition, stressful. They intentionally burn a
lot of resources by using randomness to hopefully surface state machine
bugs. Additionally, many stress tests are multi-threaded these days and
they may attempt to use all of the available CPUs to better uncover
bugs. In isolation, this is not a problem, but the test suite as a whole
assumes that individual tests are single threaded and therefore running
multiple stress tests at once can quickly spiral out of control.

This change formalizes stress tests and then treats them like long
tests, i.e. tested via 'check-swift-all' and otherwise opt-in.

Finally, with this change, the CI build bots might need to change if
they are still only testing 'validation' instead of all of the tests.
I see three options:

1) Run all of the tests. -- There are very few long tests left these
   days, and the additional costs seems small relative to the cost of
   the whole validation test suite before this change.
2) Continue checking 'validation', now sans stress tests.
3) Check 'validation', *then* the stress tests. If the former doesn't
   pass, then there is no point in the latter, and by running the stress
   tests separately, they stand a better chance of uncovering bugs and
   not overwhelming build bot resources.
2018-03-20 21:45:28 -04:00

186 lines
5.0 KiB
Swift

// RUN: %target-build-swift -Xfrontend -disable-access-control -module-name a %s -o %t.out
// RUN: %target-run %t.out | %FileCheck %s
// REQUIRES: stress_test
// UNSUPPORTED: nonatomic_rc
import StdlibUnittest
#if os(OSX) || os(iOS) || os(watchOS) || os(tvOS)
import Darwin
#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android)
import Glibc
#endif
_setTestSuiteFailedCallback() { print("abort()") }
struct RaceTest1 : RaceTestWithPerTrialData {
static var shouldPass: Bool = true
static var iterationCountdown: _stdlib_AtomicInt = _stdlib_AtomicInt(8)
class RaceData {
init() {}
}
typealias ThreadLocalData = Void
typealias Observation = Observation1UInt
func makeRaceData() -> RaceData {
return RaceData()
}
func makeThreadLocalData() -> Void {
return Void()
}
func thread1(
_ raceData: RaceData, _ threadLocalData: inout ThreadLocalData
) -> Observation {
switch RaceTest1.iterationCountdown.fetchAndAdd(-1) {
case 0:
return Observation(0x1)
case 1:
return Observation(RaceTest1.shouldPass ? 0x1 : 0xffff)
case 1, 2:
return Observation(0x2)
case 3, 4, 5:
return Observation(RaceTest1.shouldPass ? 0x2 : 0xfffe)
default:
return Observation(0x1)
}
}
func evaluateObservations(
_ observations: [Observation],
_ sink: (RaceTestObservationEvaluation) -> Void
) {
for observation in observations {
switch observation {
case Observation(0x1):
sink(.pass)
case Observation(0x2):
sink(.passInteresting(String(describing: observation)))
case Observation(0xffff):
sink(.failure)
case Observation(0xfffe):
sink(.failureInteresting(String(describing: observation)))
default:
fatalError("should not happen")
}
}
}
}
var RaceTestSuite = TestSuite("Race")
RaceTestSuite.test("passes") {
RaceTest1.shouldPass = true
RaceTest1.iterationCountdown = _stdlib_AtomicInt(8)
runRaceTest(RaceTest1.self, trials: 10)
}
// CHECK: [ RUN ] Race.passes
// CHECK: stdout>>> Pass: {{.*}} times
// CHECK: stdout>>> Pass (2): 4 times
// CHECK: stdout>>> Failure: 0 times
// CHECK: [ OK ] Race.passes
RaceTestSuite.test("fails") {
RaceTest1.shouldPass = false
RaceTest1.iterationCountdown = _stdlib_AtomicInt(8)
runRaceTest(RaceTest1.self, trials: 10)
}
// CHECK: [ RUN ] Race.fails
// REQUIRES: executable_test
// CHECK: stdout>>> Pass: {{.*}} times
// CHECK: stdout>>> Pass (2): 1 times
// CHECK: stdout>>> Failure: 1 times
// CHECK: stdout>>> Failure (65534): 3 times
// CHECK: [ FAIL ] Race.fails
RaceTestSuite.test("closure") {
let count = _stdlib_AtomicInt(0)
runRaceTest(trials: 10) {
_ = count.fetchAndAdd(1)
}
expectNotEqual(0, count.load())
}
// CHECK: [ RUN ] Race.closure
// CHECK: [ OK ] Race.closure
RaceTestSuite.test("timeout-zero") {
// Zero timeout is still expected to run at least one trial.
let count = _stdlib_AtomicInt(0)
runRaceTest(trials: 2000000000, timeoutInSeconds: 0) {
_ = count.fetchAndAdd(1)
}
expectGT(count.load(), 0)
}
// CHECK: [ RUN ] Race.timeout-zero
// CHECK: [ OK ] Race.timeout-zero
func -(_ lhs: timeval, _ rhs: timeval) -> timeval {
var result = timeval(tv_sec: 0, tv_usec: 0)
result.tv_sec = lhs.tv_sec - rhs.tv_sec
result.tv_usec = lhs.tv_usec - rhs.tv_usec
if result.tv_usec < 0 {
result.tv_usec += 1000000
result.tv_sec -= 1
}
return result
}
func gettimeofday() -> timeval {
var result = timeval(tv_sec: 0, tv_usec: 0)
gettimeofday(&result, nil)
return result
}
RaceTestSuite.test("timeout-small") {
// Verify that the timeout fires after the correct number of seconds.
// If the timeout fails to fire then this test will run for a very long time.
var startTime: timeval
var endTime: timeval
let timeout = 5
let count = _stdlib_AtomicInt(0)
startTime = gettimeofday()
runRaceTest(trials: 2000000000, timeoutInSeconds: timeout) {
_ = count.fetchAndAdd(1)
}
endTime = gettimeofday()
expectGT(count.load(), 0)
// Test should have run to the timeout.
// Test should not have run too long after the timeout.
let duration = endTime - startTime
expectGE(duration.tv_sec, timeout)
expectLT(duration.tv_sec, timeout*100) // large to avoid spurious failures
}
// CHECK: [ RUN ] Race.timeout-small
// CHECK: [ OK ] Race.timeout-small
RaceTestSuite.test("timeout-big") {
// Verify that a short test with a long timeout completes before the timeout.
var startTime: timeval
var endTime: timeval
let timeout = 10000
let count = _stdlib_AtomicInt(0)
startTime = gettimeofday()
runRaceTest(trials: 10, timeoutInSeconds: timeout) {
_ = count.fetchAndAdd(1)
}
endTime = gettimeofday()
expectGT(count.load(), 0)
// Test should have stopped long before the timeout.
let duration = endTime - startTime
expectLT(duration.tv_sec, timeout / 2)
}
// CHECK: [ RUN ] Race.timeout-big
// CHECK: [ OK ] Race.timeout-big
runAllTests()
// CHECK: Race: Some tests failed, aborting