Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add assert and precondition #213

Merged
merged 9 commits into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 24 additions & 15 deletions Dependencies.xcworkspace/xcshareddata/swiftpm/Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-argument-parser",
"state" : {
"revision" : "fddd1c00396eed152c45a46bea9f47b98e59301d",
"version" : "1.2.0"
"revision" : "46989693916f56d1186bd59ac15124caef896560",
"version" : "1.3.1"
}
},
{
Expand All @@ -32,25 +32,34 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/swift-clocks",
"state" : {
"revision" : "d1fd837326aa719bee979bdde1f53cd5797443eb",
"version" : "1.0.0"
"revision" : "a8421d68068d8f45fbceb418fbf22c5dad4afd33",
"version" : "1.0.2"
}
},
{
"identity" : "swift-concurrency-extras",
"kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/swift-concurrency-extras",
"state" : {
"revision" : "ea631ce892687f5432a833312292b80db238186a",
"version" : "1.0.0"
"revision" : "bb5059bde9022d69ac516803f4f227d8ac967f71",
"version" : "1.1.0"
}
},
{
"identity" : "swift-docc-plugin",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-docc-plugin",
"state" : {
"revision" : "3303b164430d9a7055ba484c8ead67a52f7b74f6",
"revision" : "26ac5758409154cc448d7ab82389c520fa8a8247",
"version" : "1.3.0"
}
},
{
"identity" : "swift-docc-symbolkit",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-docc-symbolkit",
"state" : {
"revision" : "b45d1f2ed151d057b54504d653e0da5552844e34",
"version" : "1.0.0"
}
},
Expand All @@ -59,35 +68,35 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/swift-macro-testing",
"state" : {
"revision" : "35acd9468d40ae87e75991a18af6271e8124c261",
"version" : "0.2.1"
"revision" : "5c4a1b9d7c23cd5c08ea50677d8e89080365cb00",
"version" : "0.4.0"
}
},
{
"identity" : "swift-snapshot-testing",
"kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/swift-snapshot-testing",
"state" : {
"revision" : "bb0ea08db8e73324fe6c3727f755ca41a23ff2f4",
"version" : "1.14.2"
"revision" : "625ccca8570773dd84a34ee51a81aa2bc5a4f97a",
"version" : "1.16.0"
}
},
{
"identity" : "swift-syntax",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-syntax",
"state" : {
"revision" : "ffa3cd6fc2aa62adbedd31d3efaf7c0d86a9f029",
"version" : "509.0.1"
"revision" : "303e5c5c36d6a558407d364878df131c3546fad8",
"version" : "510.0.2"
}
},
{
"identity" : "xctest-dynamic-overlay",
"kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/xctest-dynamic-overlay",
"state" : {
"revision" : "b13b1d1a8e787a5ffc71ac19dcaf52183ab27ba2",
"version" : "1.1.1"
"revision" : "6f30bdba373bbd7fbfe241dddd732651f2fbd1e2",
"version" : "1.1.2"
}
}
],
Expand Down
20 changes: 10 additions & 10 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-argument-parser",
"state" : {
"revision" : "c8ed701b513cf5177118a175d85fbbbcd707ab41",
"version" : "1.3.0"
"revision" : "46989693916f56d1186bd59ac15124caef896560",
"version" : "1.3.1"
}
},
{
Expand Down Expand Up @@ -68,35 +68,35 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/swift-macro-testing",
"state" : {
"revision" : "90e38eec4bf661ec0da1bbfd3ec507d0f0c05310",
"version" : "0.3.0"
"revision" : "5c4a1b9d7c23cd5c08ea50677d8e89080365cb00",
"version" : "0.4.0"
}
},
{
"identity" : "swift-snapshot-testing",
"kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/swift-snapshot-testing",
"state" : {
"revision" : "5b0c434778f2c1a4c9b5ebdb8682b28e84dd69bd",
"version" : "1.15.4"
"revision" : "625ccca8570773dd84a34ee51a81aa2bc5a4f97a",
"version" : "1.16.0"
}
},
{
"identity" : "swift-syntax",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-syntax",
"state" : {
"revision" : "08a2f0a9a30e0f705f79c9cfaca1f68b71bdc775",
"version" : "510.0.0"
"revision" : "303e5c5c36d6a558407d364878df131c3546fad8",
"version" : "510.0.2"
}
},
{
"identity" : "xctest-dynamic-overlay",
"kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/xctest-dynamic-overlay",
"state" : {
"revision" : "b13b1d1a8e787a5ffc71ac19dcaf52183ab27ba2",
"version" : "1.1.1"
"revision" : "6f30bdba373bbd7fbfe241dddd732651f2fbd1e2",
"version" : "1.1.2"
}
}
],
Expand Down
196 changes: 196 additions & 0 deletions Sources/Dependencies/DependencyValues/Assert.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
extension DependencyValues {
/// A dependency for handling assertions.
///
/// Useful as a controllable and testable substitute for Swift's `assert` function that calls
/// `XCTFail` in tests instead of terminating the executable.
///
/// ```swift
/// func operate(_ n: Int) {
/// @Dependency(\.assert) var assert
/// assert(n > 0, "Number must be greater than zero")
/// // ...
/// }
/// ```
///
/// Tests can assert against this precondition using `XCTExpectFailure`:
///
/// ```swift
/// XCTExpectFailure {
/// operate(n)
/// } issueMatcher: {
/// $0.compactDescription = "Number must be greater than zero"
/// }
/// ```
public var assert: any AssertionEffect {
get { self[AssertKey.self] }
set { self[AssertKey.self] = newValue }
}

/// A dependency for failing an assertion.
///
/// Equivalent to passing a `false` condition to ``DependencyValues/assert``.
public var assertionFailure: any AssertionFailureEffect {
AssertionFailure(base: self.assert)
}

/// A dependency for handling preconditions.
///
/// Useful as a controllable and testable substitute for Swift's `precondition` function that
/// calls `XCTFail` in tests instead of terminating the executable.
///
/// ```swift
/// func operate(_ n: Int) {
/// @Dependency(\.precondition) var precondition
/// precondition(n > 0, "Number must be greater than zero")
/// // ...
/// }
/// ```
///
/// Tests can assert against this precondition using `XCTExpectFailure`:
///
/// ```swift
/// XCTExpectFailure {
/// operate(n)
/// } issueMatcher: {
/// $0.compactDescription = "Number must be greater than zero"
/// }
/// ```
public var precondition: any AssertionEffect {
get { self[PreconditionKey.self] }
set { self[PreconditionKey.self] = newValue }
}
}

/// A type for creating an assertion or precondition.
///
/// See ``DependencyValues/assert`` or ``DependencyValues/precondition`` for more information.
public protocol AssertionEffect: Sendable {
func callAsFunction(
_ condition: @autoclosure () -> Bool,
_ message: @autoclosure () -> String,
file: StaticString,
line: UInt
)
}

extension AssertionEffect {
@_disfavoredOverload
@_transparent
public func callAsFunction(
_ condition: @autoclosure () -> Bool,
_ message: @autoclosure () -> String = "",
file: StaticString = #file,
line: UInt = #line
) {
self.callAsFunction(condition(), message(), file: file, line: line)
}
}

private struct LiveAssertionEffect: AssertionEffect {
@_transparent
func callAsFunction(
_ condition: @autoclosure () -> Bool,
_ message: @autoclosure () -> String,
file: StaticString,
line: UInt
) {
Swift.assert(condition(), message(), file: file, line: line)
}
}

private struct LivePreconditionEffect: AssertionEffect {
@_transparent
func callAsFunction(
_ condition: @autoclosure () -> Bool,
_ message: @autoclosure () -> String,
file: StaticString,
line: UInt
) {
Swift.precondition(condition(), message(), file: file, line: line)
}
}

private struct TestAssertionEffect: AssertionEffect {
@_transparent
func callAsFunction(
_ condition: @autoclosure () -> Bool,
_ message: @autoclosure () -> String,
file: StaticString,
line: UInt
) {
guard condition() else { return XCTFail(message(), file: file, line: line) }
}
}

public protocol AssertionFailureEffect: Sendable {
func callAsFunction(
_ message: @autoclosure () -> String,
file: StaticString,
line: UInt
)
}

extension AssertionFailureEffect {
@_disfavoredOverload
@_transparent
public func callAsFunction(
_ message: @autoclosure () -> String = "",
file: StaticString = #file,
line: UInt = #line
) {
self.callAsFunction(message(), file: file, line: line)
}
}

private struct AssertionFailure: AssertionFailureEffect {
let base: any AssertionEffect

@_transparent
func callAsFunction(
_ message: @autoclosure () -> String,
file: StaticString,
line: UInt
) {
self.base(false, message(), file: file, line: line)
}
}

private enum AssertKey: DependencyKey {
public static let liveValue: any AssertionEffect = LiveAssertionEffect()
public static let testValue: any AssertionEffect = TestAssertionEffect()
}

private enum PreconditionKey: DependencyKey {
public static let liveValue: any AssertionEffect = LivePreconditionEffect()
public static let testValue: any AssertionEffect = TestAssertionEffect()
}

/// An ``AssertionEffect`` that invokes the given closure.
public struct AnyAssertionEffect: AssertionEffect {
private let assert: @Sendable (
_ condition: @autoclosure () -> Bool,
_ message: @autoclosure () -> String,
_ file: StaticString,
_ line: UInt
) -> Void

public init(
_ assert: @escaping @Sendable (
_ condition: @autoclosure () -> Bool,
_ message: @autoclosure () -> String,
_ file: StaticString,
_ line: UInt
) -> Void
) {
self.assert = assert
}

public func callAsFunction(
_ condition: @autoclosure () -> Bool,
_ message: @autoclosure () -> String,
file: StaticString,
line: UInt
) {
self.assert(condition(), message(), file, line)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
### Creating and accessing values

- ``init()``
- ``subscript(_:_:_:_:)``
- ``subscript(key:file:function:line:)

### Overriding values

Expand All @@ -18,6 +18,8 @@

### Dependency values

- ``assert``
- ``assertionFailure``
- ``calendar``
- ``context``
- ``continuousClock``
Expand All @@ -27,6 +29,7 @@
- ``mainQueue``
- ``mainRunLoop``
- ``openURL``
- ``precondition``
- ``suspendingClock``
- ``timeZone``
- ``urlSession``
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# ``Dependencies/DependencyValues/assert``

## Topics

### Dependency values

- ``AssertionEffect``
- ``AssertionFailureEffect``

### Custom assertions

- ``AnyAssertionEffect``
Loading