File: TestScopingTraitTests.swift

package info (click to toggle)
swiftlang 6.1.3-4
  • links: PTS, VCS
  • area: main
  • in suites: forky
  • size: 2,791,644 kB
  • sloc: cpp: 9,901,738; ansic: 2,201,433; asm: 1,091,827; python: 308,252; objc: 82,166; f90: 80,126; lisp: 38,358; pascal: 25,559; sh: 20,429; ml: 5,058; perl: 4,745; makefile: 4,484; awk: 3,535; javascript: 3,018; xml: 918; fortran: 664; cs: 573; ruby: 396
file content (184 lines) | stat: -rw-r--r-- 6,197 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2024 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
//

@testable @_spi(Experimental) @_spi(ForToolsIntegrationOnly) import Testing

@Suite("TestScoping-conforming Trait Tests")
struct TestScopingTraitTests {
  @Test("Execute code before and after a non-parameterized test.")
  func executeCodeBeforeAndAfterNonParameterizedTest() async {
    await confirmation("Code was run before the test") { before in
      await confirmation("Code was run after the test") { after in
        await Test(CustomTrait(before: before, after: after)) {
          // do nothing
        }.run()
      }
    }
  }

  @Test("Execute code before and after a parameterized test.")
  func executeCodeBeforeAndAfterParameterizedTest() async {
    // `expectedCount` is 2 because we run it for each test case
    await confirmation("Code was run before the test", expectedCount: 2) { before in
      await confirmation("Code was run after the test", expectedCount: 2) { after in
        await Test(CustomTrait(before: before, after: after), arguments: ["Hello", "World"]) { _ in
          // do nothing
        }.run()
      }
    }
  }

  @Test("Custom execution trait throws an error")
  func customExecutionTraitThrowsAnError() async throws {
    var configuration = Configuration()
    await confirmation("Error thrown", expectedCount: 1) { errorThrownConfirmation in
      configuration.eventHandler = { event, _ in
        guard case let .issueRecorded(issue) = event.kind,
              case let .errorCaught(error) = issue.kind else {
          return
        }

        #expect(error is CustomThrowingErrorTrait.CustomTraitError)
        errorThrownConfirmation()
      }

      await Test(CustomThrowingErrorTrait()) {
        // Make sure this does not get reached
        Issue.record("Expected trait to fail the test. Should not have reached test body.")
      }.run(configuration: configuration)
    }
  }

  @Test("Teardown occurs after child tests run")
  func teardownOccursAtEnd() async throws {
    await runTest(for: TestsWithCustomTraitWithStrongOrdering.self, configuration: .init())
  }

  struct ExecutionControl {
    @Test("Trait applied directly to function is executed once")
    func traitAppliedToFunction() async {
      let counter = Locked(rawValue: 0)
      await DefaultExecutionTrait.$counter.withValue(counter) {
        await Test(DefaultExecutionTrait()) {}.run()
      }
      #expect(counter.rawValue == 1)
    }

    @Test("Non-recursive suite trait with default scope provider implementation")
    func nonRecursiveSuiteTrait() async {
      let counter = Locked(rawValue: 0)
      await DefaultExecutionTrait.$counter.withValue(counter) {
        await runTest(for: SuiteWithNonRecursiveDefaultExecutionTrait.self)
      }
      #expect(counter.rawValue == 1)
    }

    @Test("Recursive suite trait with default scope provider implementation")
    func recursiveSuiteTrait() async {
      let counter = Locked(rawValue: 0)
      await DefaultExecutionTrait.$counter.withValue(counter) {
        await runTest(for: SuiteWithRecursiveDefaultExecutionTrait.self)
      }
      #expect(counter.rawValue == 1)
    }

    @Test("Recursive, all-inclusive suite trait")
    func recursiveAllInclusiveSuiteTrait() async {
      let counter = Locked(rawValue: 0)
      await AllInclusiveExecutionTrait.$counter.withValue(counter) {
        await runTest(for: SuiteWithAllInclusiveExecutionTrait.self)
      }
      #expect(counter.rawValue == 3)
    }
  }
}

// MARK: - Fixtures

private struct CustomTrait: TestTrait, TestScoping {
  var before: Confirmation
  var after: Confirmation
  func provideScope(for test: Test, testCase: Test.Case?, performing function: @Sendable () async throws -> Void) async throws {
    before()
    defer {
      after()
    }
    try await function()
  }
}

private struct CustomThrowingErrorTrait: TestTrait, TestScoping {
  fileprivate struct CustomTraitError: Error {}

  func provideScope(for test: Test, testCase: Test.Case?, performing function: @Sendable () async throws -> Void) async throws {
    throw CustomTraitError()
  }
}

struct DoSomethingBeforeAndAfterTrait: SuiteTrait, TestTrait, TestScoping {
  static let state = Locked(rawValue: 0)

  func provideScope(for test: Test, testCase: Test.Case?, performing function: @Sendable () async throws -> Void) async throws {
    #expect(Self.state.increment() == 1)

    try await function()
    #expect(Self.state.increment() == 3)
  }
}

@Suite(.hidden, DoSomethingBeforeAndAfterTrait())
struct TestsWithCustomTraitWithStrongOrdering {
  @Test(.hidden) func f() async {
    #expect(DoSomethingBeforeAndAfterTrait.state.increment() == 2)
  }
}

private struct DefaultExecutionTrait: SuiteTrait, TestTrait, TestScoping {
  @TaskLocal static var counter: Locked<Int>?
  var isRecursive: Bool = false

  func provideScope(for test: Test, testCase: Test.Case?, performing function: @Sendable () async throws -> Void) async throws {
    Self.counter!.increment()
    try await function()
  }
}

@Suite(.hidden, DefaultExecutionTrait())
private struct SuiteWithNonRecursiveDefaultExecutionTrait {
  @Test func f() {}
}

@Suite(.hidden, DefaultExecutionTrait(isRecursive: true))
private struct SuiteWithRecursiveDefaultExecutionTrait {
  @Test func f() {}
}

private struct AllInclusiveExecutionTrait: SuiteTrait, TestTrait, TestScoping {
  @TaskLocal static var counter: Locked<Int>?

  var isRecursive: Bool {
    true
  }

  func scopeProvider(for test: Test, testCase: Test.Case?) -> AllInclusiveExecutionTrait? {
    // Unconditionally returning self makes this trait "all inclusive".
    self
  }

  func provideScope(for test: Test, testCase: Test.Case?, performing function: @Sendable () async throws -> Void) async throws {
    Self.counter!.increment()
    try await function()
  }
}

@Suite(.hidden, AllInclusiveExecutionTrait())
private struct SuiteWithAllInclusiveExecutionTrait {
  @Test func f() {}
}