Skip to content

Commit 68c5016

Browse files
committed
Move __ExpectationContext to its own file
1 parent 4e6ff0c commit 68c5016

File tree

3 files changed

+288
-280
lines changed

3 files changed

+288
-280
lines changed

Sources/Testing/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ add_library(Testing
3939
Expectations/Expectation.swift
4040
Expectations/Expectation+Macro.swift
4141
Expectations/ExpectationChecking+Macro.swift
42+
Expectations/ExpectationContext.swift
4243
Issues/Confirmation.swift
4344
Issues/ErrorSnapshot.swift
4445
Issues/Issue.swift

Sources/Testing/Expectations/ExpectationChecking+Macro.swift

Lines changed: 0 additions & 280 deletions
Original file line numberDiff line numberDiff line change
@@ -8,286 +8,6 @@
88
// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
99
//
1010

11-
private import _TestingInternals
12-
13-
/// A type representing the context within a call to the `#expect()` and
14-
/// `#require()` macros.
15-
///
16-
/// When the compiler expands a call to either of these macros, it creates a
17-
/// local instance of this type that is used to collect information about the
18-
/// various subexpressions of the macro's condition argument. The nature of the
19-
/// collected information is subject to change over time.
20-
///
21-
/// - Warning: This type is used to implement the `#expect()` and `#require()`
22-
/// macros. Do not use it directly.
23-
public struct __ExpectationContext {
24-
/// The source code of any captured expressions.
25-
var sourceCode: [__ExpressionID: String]
26-
27-
/// The runtime values of any captured expressions.
28-
///
29-
/// The values in this dictionary are generally gathered at runtime as
30-
/// subexpressions are evaluated. Not all expressions captured at compile time
31-
/// will have runtime values: notably, if an operand to a short-circuiting
32-
/// binary operator like `&&` is not evaluated, the corresponding expression
33-
/// will not be assigned a runtime value.
34-
var runtimeValues: [__ExpressionID: () -> Expression.Value?]
35-
36-
init(sourceCode: [__ExpressionID: String] = [:], runtimeValues: [__ExpressionID: () -> Expression.Value?] = [:]) {
37-
self.sourceCode = sourceCode
38-
self.runtimeValues = runtimeValues
39-
}
40-
41-
/// Collapse the given expression graph into one or more expressions with
42-
/// nested subexpressions.
43-
///
44-
/// - Parameters:
45-
/// - expressionGraph: The expression graph to collapse.
46-
/// - depth: How deep into the expression graph this call is. The first call
47-
/// has a depth of `0`.
48-
///
49-
/// - Returns: An array of expressions under the root node of
50-
/// `expressionGraph`. The expression at the root of the graph is not
51-
/// included in the result.
52-
private func _squashExpressionGraph(_ expressionGraph: Graph<UInt32, __Expression?>, depth: Int) -> [__Expression] {
53-
var result = [__Expression]()
54-
55-
let childGraphs = expressionGraph.children.sorted { $0.key < $1.key }
56-
for (_, childGraph) in childGraphs {
57-
let subexpressions = _squashExpressionGraph(childGraph, depth: depth + 1)
58-
if var subexpression = childGraph.value {
59-
subexpression.subexpressions += subexpressions
60-
result.append(subexpression)
61-
} else {
62-
// Hoist subexpressions of the child graph as there was no expression
63-
// recorded for it.
64-
result += subexpressions
65-
}
66-
}
67-
68-
return result
69-
}
70-
71-
/// Perform whatever final work is needed on this instance in order to produce
72-
/// an instance of `__Expression` corresponding to the condition expression
73-
/// being evaluated.
74-
///
75-
/// - Parameters:
76-
/// - successfully: Whether or not the expectation is "successful" (i.e. its
77-
/// condition expression evaluates to `true`). If the expectation failed,
78-
/// more diagnostic information is gathered including the runtime values
79-
/// of any subexpressions of the condition expression.
80-
///
81-
/// - Returns: An expression value representing the condition expression that
82-
/// was evaluated.
83-
consuming func finalize(successfully: Bool) -> __Expression {
84-
// Construct a graph containing the source code for all the subexpressions
85-
// we've captured during evaluation.
86-
var expressionGraph = Graph<UInt32, __Expression?>()
87-
for (id, sourceCode) in sourceCode {
88-
let keyPath = id.keyPath
89-
expressionGraph.insertValue(__Expression(sourceCode), at: keyPath)
90-
}
91-
92-
// If the expectation failed, insert any captured runtime values into the
93-
// graph alongside the source code.
94-
if !successfully {
95-
for (id, runtimeValue) in runtimeValues {
96-
let keyPath = id.keyPath
97-
if var expression = expressionGraph[keyPath], let runtimeValue = runtimeValue() {
98-
expression.runtimeValue = runtimeValue
99-
expressionGraph[keyPath] = expression
100-
}
101-
}
102-
}
103-
104-
// Flatten the expression graph.
105-
var subexpressions = _squashExpressionGraph(expressionGraph, depth: 0)
106-
var expression = if let rootExpression = expressionGraph.value {
107-
// We had a root expression and can add all reported subexpressions to it.
108-
// This should be the common case.
109-
rootExpression
110-
} else if subexpressions.count == 1 {
111-
// We had no root expression, but we did have a single reported
112-
// subexpression that can serve as our root.
113-
subexpressions.removeFirst()
114-
} else {
115-
// We could not distinguish which subexpression should serve as the root
116-
// expression. In practice this case should be treated as a bug.
117-
__Expression(kind: .generic("<expression unavailable>"))
118-
}
119-
expression.subexpressions += subexpressions
120-
121-
return expression
122-
}
123-
124-
#if !SWT_FIXED_122011759
125-
/// Storage for any locally-created C strings.
126-
private var _transformedCStrings: _TransformedCStrings?
127-
#endif
128-
}
129-
130-
@available(*, unavailable)
131-
extension __ExpectationContext: Sendable {}
132-
133-
// MARK: - Expression capturing
134-
135-
extension __ExpectationContext {
136-
/// Capture information about a value for use if the expectation currently
137-
/// being evaluated fails.
138-
///
139-
/// - Parameters:
140-
/// - value: The value to pass through.
141-
/// - id: A value that uniquely identifies the represented expression in the
142-
/// context of the expectation currently being evaluated.
143-
///
144-
/// - Returns: `value`, verbatim.
145-
///
146-
/// - Warning: This function is used to implement the `#expect()` and
147-
/// `#require()` macros. Do not call it directly.
148-
public mutating func callAsFunction<T>(_ value: T, _ id: __ExpressionID) -> T where T: Copyable {
149-
runtimeValues[id] = { Expression.Value(reflecting: value) }
150-
return value
151-
}
152-
153-
/// Capture information about a value for use if the expectation currently
154-
/// being evaluated fails.
155-
///
156-
/// - Parameters:
157-
/// - value: The value to pass through.
158-
/// - id: A value that uniquely identifies the represented expression in the
159-
/// context of the expectation currently being evaluated.
160-
///
161-
/// - Returns: `value`, verbatim.
162-
///
163-
/// - Warning: This function is used to implement the `#expect()` and
164-
/// `#require()` macros. Do not call it directly.
165-
@_disfavoredOverload
166-
public mutating func callAsFunction<T>(_ value: consuming T, _ id: __ExpressionID) -> T where T: ~Copyable {
167-
// TODO: add support for borrowing non-copyable expressions (need @lifetime)
168-
return value
169-
}
170-
171-
/// Perform a conditional cast (`as?`) on a value.
172-
///
173-
/// - Parameters:
174-
/// - value: The value to cast.
175-
/// - type: The type to cast `value` to.
176-
/// - typeID: The ID chain of the `type` expression as emitted during
177-
/// expansion of the `#expect()` or `#require()` macro.
178-
///
179-
/// - Returns: The result of the expression `value as? type`.
180-
///
181-
/// If `value` cannot be cast to `type`, the previously-recorded context for
182-
/// the expression `type` is assigned the runtime value `type(of: value)` so
183-
/// that the _actual_ type of `value` is recorded in any resulting issue.
184-
///
185-
/// - Warning: This function is used to implement the `#expect()` and
186-
/// `#require()` macros. Do not call it directly.
187-
public mutating func __as<T, U>(_ value: T, _ type: U.Type, _ typeID: __ExpressionID) -> U? {
188-
let result = value as? U
189-
190-
if result == nil {
191-
let correctType = Swift.type(of: value as Any)
192-
runtimeValues[typeID] = { Expression.Value(reflecting: correctType) }
193-
}
194-
195-
return result
196-
}
197-
198-
/// Check the type of a value using the `is` operator.
199-
///
200-
/// - Parameters:
201-
/// - value: The value to cast.
202-
/// - type: The type `value` is expected to be.
203-
/// - typeID: The ID chain of the `type` expression as emitted during
204-
/// expansion of the `#expect()` or `#require()` macro.
205-
///
206-
/// - Returns: The result of the expression `value as? type`.
207-
///
208-
/// If `value` is not an instance of `type`, the previously-recorded context
209-
/// for the expression `type` is assigned the runtime value `type(of: value)`
210-
/// so that the _actual_ type of `value` is recorded in any resulting issue.
211-
///
212-
/// - Warning: This function is used to implement the `#expect()` and
213-
/// `#require()` macros. Do not call it directly.
214-
public mutating func __is<T, U>(_ value: T, _ type: U.Type, _ typeID: __ExpressionID) -> Bool {
215-
let result = value is U
216-
217-
if !result {
218-
let correctType = Swift.type(of: value as Any)
219-
runtimeValues[typeID] = { Expression.Value(reflecting: correctType) }
220-
}
221-
222-
return true
223-
}
224-
}
225-
226-
#if !SWT_FIXED_122011759
227-
// MARK: - String-to-C-string handling
228-
229-
extension __ExpectationContext {
230-
/// A class that manages the lifetimes of any temporary C strings created in
231-
/// the context of an expectation.
232-
private final class _TransformedCStrings {
233-
/// The set of temporary C strings managed by this instance.
234-
var values = [UnsafeMutablePointer<CChar>]()
235-
236-
deinit {
237-
for cString in values {
238-
free(cString)
239-
}
240-
}
241-
}
242-
243-
/// Convert a string to a C string and capture information about it for use if
244-
/// the expectation currently being evaluated fails.
245-
///
246-
/// - Parameters:
247-
/// - value: The string value that should be transformed into a C string.
248-
/// - id: A value that uniquely identifies the represented expression in the
249-
/// context of the expectation currently being evaluated.
250-
///
251-
/// - Returns: `value`, transformed into a pointer to a C string. The caller
252-
/// should _not_ free this string; it will be freed when the expectation
253-
/// context is destroyed.
254-
///
255-
/// This overload of `callAsFunction(_:_:)` is necessary because Swift allows
256-
/// passing string literals directly to functions that take C strings. At
257-
/// compile time, the compiler generates code that makes a temporary UTF-8
258-
/// copy of the string, then frees that copy on return. That logic does not
259-
/// work correctly when strings are passed to intermediate functions such as
260-
/// this one, and the compiler will fail to extend the lifetime of the C
261-
/// strings to the appropriate point. ([122011759](rdar://122011759))
262-
///
263-
/// - Warning: This function is used to implement the `#expect()` and
264-
/// `#require()` macros. Do not call it directly.
265-
public mutating func callAsFunction<T, U>(_ value: T, _ id: __ExpressionID) -> U where T: StringProtocol, U: _Pointer {
266-
// Perform the normal value capture.
267-
let result = self(value, id)
268-
269-
// Create a C string copy of `value`.
270-
#if os(Windows)
271-
let resultCString = _strdup(String(result))!
272-
#else
273-
let resultCString = strdup(String(result))!
274-
#endif
275-
276-
// Store the C string pointer so we can free it later when this context is
277-
// torn down.
278-
if _transformedCStrings == nil {
279-
_transformedCStrings = _TransformedCStrings()
280-
}
281-
_transformedCStrings?.values.append(resultCString)
282-
283-
// Return the C string as whatever pointer type the caller wants.
284-
return U(bitPattern: Int(bitPattern: resultCString)).unsafelyUnwrapped
285-
}
286-
}
287-
#endif
288-
289-
// MARK: - Condition checking
290-
29111
/// Check that an expectation has passed after a condition has been evaluated
29212
/// and throw an error if it failed.
29313
///

0 commit comments

Comments
 (0)