Skip to content

Commit 957a384

Browse files
committed
Move issue reporting for invalid fixedSeed usage to TestScope itself
1 parent e9c259f commit 957a384

File tree

5 files changed

+55
-30
lines changed

5 files changed

+55
-30
lines changed

Sources/PropertyBased/FixedSeedTrait.swift

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,16 @@ public struct FixedSeedTrait: TestTrait, TestScoping {
2121
let sourceLocation: SourceLocation
2222

2323
@TaskLocal
24-
static var fixedRandom: (rng: Xoshiro?, location: SourceLocation)?
24+
static var fixedRandom: (rng: Xoshiro, location: SourceLocation)?
2525

2626
public func provideScope(for test: Test, testCase: Test.Case?, performing function: () async throws -> Void) async throws {
2727
if let existing = Self.fixedRandom {
2828
Issue.record("Two different fixed seeds are used in the same test.", sourceLocation: existing.location)
29-
30-
try await function()
29+
return
30+
}
31+
32+
guard let rng else {
33+
Issue.record("An invalid seed was provided. Remove the fixedSeed Trait from the Test.", sourceLocation: sourceLocation)
3134
return
3235
}
3336

@@ -39,15 +42,15 @@ public struct FixedSeedTrait: TestTrait, TestScoping {
3942

4043
extension Trait where Self == FixedSeedTrait {
4144
/// Override the seed used by all property checks within this Test.
42-
///
45+
///
4346
/// If one of your property checks fails intermittently, apply this trait to reliably reproduce the issue.
44-
///
47+
///
4548
/// > Important: Do not commit usages of this trait into version control. Applying this trait will always report an issue regardless of the existence of any failures within the test.
46-
///
49+
///
4750
/// - Parameters:
4851
/// - seed: The seed to use.
4952
/// - sourceLocation: The source location of the trait.
50-
///
53+
///
5154
/// - Returns: An instance of ``FixedSeedTrait``.
5255
public static func fixedSeed(_ seed: StaticString, sourceLocation: SourceLocation = #_sourceLocation) -> Self {
5356
return Self(seed.description, sourceLocation)

Sources/PropertyBased/PropertyCheck.swift

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,6 @@ public func propertyCheck<each Value>(isolation: isolated (any Actor)? = #isolat
8282

8383
let fixedRng = FixedSeedTrait.fixedRandom
8484

85-
if let fixedRng, fixedRng.rng == nil {
86-
Issue.record("An invalid seed was provided. Remove the fixedSeed Trait from the Test.", sourceLocation: fixedRng.location)
87-
return
88-
}
89-
9085
let actualCount = fixedRng != nil ? 1 : count
9186

9287
for _ in 0..<actualCount {

Tests/PropertyBasedTests/FixedSeedTests.swift

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,50 @@ import PropertyBased
1010
import Gen
1111

1212
@Suite struct FixedSeedTests {
13-
@Test(.fixedSeed("qwert"))
14-
func testInvalidSeed() async {
13+
@Test func testInvalidSeed() async throws {
14+
let trait = FixedSeedTrait.fixedSeed("qwert")
15+
let scope = try #require(trait.scopeProvider(for: Test.current!, testCase: Test.Case.current))
16+
1517
let issues = await gatherIssues {
16-
await propertyCheck(input: .bool) { _ in }
18+
try await scope.provideScope(for: Test.current!, testCase: Test.Case.current) {
19+
try #require(Bool(false), "block must not be called")
20+
}
21+
}
22+
#expect(issues.count == 1)
23+
#expect(issues.contains(where: {
24+
$0.contains("invalid seed")
25+
}))
26+
}
27+
28+
@Test func testDuplicateSeeds() async throws {
29+
let trait1 = FixedSeedTrait.fixedSeed("INXoBVnaU+VA/a0Vu3WfVCmT1mBMK0ZrXJb24K4vLVY=")
30+
let trait2 = FixedSeedTrait.fixedSeed("I9kE/glCt1MIxbFsddPUSiKFAAJBGKPHSre93c+Wz9E=")
31+
32+
let issues = await gatherIssues {
33+
try await trait1.provideScope(for: Test.current!, testCase: Test.Case.current) {
34+
try await trait2.provideScope(for: Test.current!, testCase: Test.Case.current) {
35+
try #require(Bool(false), "block must not be called")
36+
}
37+
}
1738
}
1839
#expect(issues.count == 1)
1940
#expect(issues.contains(where: {
20-
$0.description.contains("invalid seed")
41+
$0.contains("different fixed seeds")
2142
}))
2243
}
2344

24-
@Test(.fixedSeed("4tPCyvymNncnc+napVCI0T4Jc6IYw1lXOQbXlIqyHck="))
25-
func testIdenticalInputs() async throws {
45+
@Test func testIdenticalInputs() async throws {
46+
let trait = FixedSeedTrait.fixedSeed("4tPCyvymNncnc+napVCI0T4Jc6IYw1lXOQbXlIqyHck=")
2647
let issues = await gatherIssues {
27-
await propertyCheck(input: .int(in: 0...1000000)) { n in
28-
#expect(n == 480813)
48+
try await trait.provideScope(for: Test.current!, testCase: Test.Case.current) {
49+
await propertyCheck(input: .int(in: 0...1000000)) { n in
50+
#expect(n == 480813)
51+
}
2952
}
3053
}
3154
#expect(issues.count == 1)
3255
#expect(issues.contains(where: {
33-
$0.description.contains("no expectation failure")
56+
$0.contains("no expectation failure")
3457
}))
3558
}
3659

Tests/PropertyBasedTests/PropertyCheckTests.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,14 @@ import Testing
5050
}
5151

5252
#expect(issues.contains(where: {
53-
$0.description.contains("> 20")
53+
$0.contains("> 20")
5454
}))
5555
#expect(issues.contains(where: {
56-
$0.description.contains("to reproduce this issue")
56+
$0.contains("to reproduce this issue")
5757
}))
5858

5959
#expect(!issues.contains(where: {
60-
$0.description.contains("> 50")
60+
$0.contains("> 50")
6161
}))
6262
}
6363
}

Tests/PropertyBasedTests/Utils.swift

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,18 @@
88
import Testing
99
@testable import PropertyBased
1010

11-
func gatherIssues(block: @Sendable () async -> Void) async -> [Issue] {
12-
let mutex = Mutex([] as [Issue])
13-
11+
func gatherIssues(block: @Sendable () async throws -> Void) async -> [String] {
12+
let mutex = Mutex([] as [String])
13+
1414
// No way to stop issues from still being recorded. Ignore those for now.
15-
await withKnownIssue {
16-
await block()
15+
await withKnownIssue(isIntermittent: true) {
16+
do {
17+
try await block()
18+
} catch {
19+
Issue.record(error)
20+
}
1721
} matching: { issue in
18-
mutex.withLock { $0.append(issue) }
22+
mutex.withLock { $0.append(issue.description) }
1923
return true
2024
}
2125

0 commit comments

Comments
 (0)