init
This commit is contained in:
commit
b6ff6e900c
|
@ -0,0 +1,23 @@
|
|||
# This workflow will build a Swift project
|
||||
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-swift
|
||||
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Build
|
||||
run: swift build -v
|
||||
- name: Run tests
|
||||
run: swift test -v
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
name: docc
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
concurrency:
|
||||
group: "pages"
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
pages:
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
runs-on: macos-12
|
||||
steps:
|
||||
- name: git checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: docbuild
|
||||
run: |
|
||||
xcodebuild docbuild -scheme Test \
|
||||
-derivedDataPath /tmp/docbuild \
|
||||
-destination 'generic/platform=iOS';
|
||||
$(xcrun --find docc) process-archive \
|
||||
transform-for-static-hosting /tmp/docbuild/Build/Products/Debug-iphoneos/Test.doccarchive \
|
||||
--hosting-base-path Test \
|
||||
--output-path docs;
|
||||
echo "<script>window.location.href += \"/documentation/test\"</script>" > docs/index.html;
|
||||
- name: artifacts
|
||||
uses: actions/upload-pages-artifact@v1
|
||||
with:
|
||||
path: 'docs'
|
||||
- name: deploy
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v1
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
.DS_Store
|
||||
/.build
|
||||
/Packages
|
||||
/*.xcodeproj
|
||||
xcuserdata/
|
||||
DerivedData/
|
||||
.swiftpm/config/registries.json
|
||||
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
|
||||
.netrc
|
|
@ -0,0 +1,59 @@
|
|||
{
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "fork",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/0xLeif/Fork",
|
||||
"state" : {
|
||||
"revision" : "7682617694979adcf1f8b08d1507c865cad81ae4",
|
||||
"version" : "1.2.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "o",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/0xOpenBytes/o",
|
||||
"state" : {
|
||||
"revision" : "3e83362434c82f318a8d72e0d3b0786ffb3ba640",
|
||||
"version" : "2.1.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "plugin",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/0xLeif/Plugin",
|
||||
"state" : {
|
||||
"revision" : "bd5449fc40720589881bacb4ed7373ddfcb2d214",
|
||||
"version" : "2.0.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "scribe",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/0xLeif/Scribe",
|
||||
"state" : {
|
||||
"revision" : "79881803f6942346941421de516fefe690ea4db3",
|
||||
"version" : "1.3.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-log",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-log",
|
||||
"state" : {
|
||||
"revision" : "32e8d724467f8fe623624570367e3d50c5638e46",
|
||||
"version" : "1.5.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "t",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/0xOpenBytes/t",
|
||||
"state" : {
|
||||
"revision" : "c111675ac4d84af23d2d9b65bffaf1829c376986",
|
||||
"version" : "1.0.4"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 2
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
// swift-tools-version: 5.7
|
||||
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "Test",
|
||||
platforms: [
|
||||
.iOS(.v14),
|
||||
.macOS(.v11),
|
||||
.watchOS(.v7),
|
||||
.tvOS(.v14)
|
||||
],
|
||||
products: [
|
||||
.library(
|
||||
name: "Test",
|
||||
targets: ["Test"]),
|
||||
],
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/0xOpenBytes/t", from: "1.0.0"),
|
||||
.package(url: "https://github.com/0xLeif/Scribe", from: "1.3.0"),
|
||||
.package(url: "https://github.com/0xLeif/Plugin", from: "2.0.0")
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
name: "Test",
|
||||
dependencies: [
|
||||
"t",
|
||||
"Scribe",
|
||||
"Plugin"
|
||||
]
|
||||
),
|
||||
.testTarget(
|
||||
name: "TestTests",
|
||||
dependencies: ["Test"]
|
||||
)
|
||||
]
|
||||
)
|
|
@ -0,0 +1,47 @@
|
|||
# Test
|
||||
|
||||
*Expect and assert*
|
||||
|
||||
## What is `Test`?
|
||||
|
||||
`Test` is a simple testing function that allows you to create test suites with multiple steps, expectations, and assertions. You can specify a name for the test suite, an instance of the `Tester` class to be used for running the tests, and a closure that contains the test steps.
|
||||
|
||||
## Where can `Test` be used?
|
||||
|
||||
`Test` can be used anywhere! `Test` can be used to test quickly inside a function to make sure something is working as expected. It is especially useful when you want to test a complex piece of code with multiple steps and assertions. `Test` can even be used for your unit tests!
|
||||
|
||||
## Examples
|
||||
|
||||
### Simple test with assertions
|
||||
|
||||
```swift
|
||||
try await Test(named: "Test someMethod()") { tester in
|
||||
try tester.assert(SomeClass.someMethod())
|
||||
try await tester.assertNoThrows(try await SomeClass.someOtherMethod())
|
||||
try tester.assert(SomeClass.someBooleanValue, isEqualTo: false)
|
||||
}
|
||||
```
|
||||
|
||||
### Test with expectations
|
||||
|
||||
```swift
|
||||
try Test(named: "Test someMethod() with expectations") { tester in
|
||||
try Expect("First step should succeed") {
|
||||
try SomeClass.someMethod()
|
||||
}
|
||||
|
||||
tester.logInfo("Just finished first step")
|
||||
|
||||
try Expect("Second step should succeed") {
|
||||
try SomeClass.someOtherMethod()
|
||||
}
|
||||
|
||||
tester.logWarning("Something unexpected happened during the second step")
|
||||
|
||||
try Expect("Final assertion should be true") {
|
||||
try tester.assert(SomeClass.someBooleanValue)
|
||||
}
|
||||
|
||||
tester.logSuccess("All steps and assertions passed!")
|
||||
}
|
||||
```
|
|
@ -0,0 +1,29 @@
|
|||
import t
|
||||
|
||||
/**
|
||||
The `Expect` function is used to define a test expectation within a `Test` function. The expectation closure contains the code that should succeed and produce the expected result.
|
||||
|
||||
- Parameters:
|
||||
- description: A string describing the expectation.
|
||||
- expectation: A closure that contains the code to test the expectation.
|
||||
*/
|
||||
public func Expect(
|
||||
_ description: String? = nil,
|
||||
expectation: @escaping () throws -> Void
|
||||
) throws {
|
||||
try t.expect(description, expectation: expectation)
|
||||
}
|
||||
|
||||
/**
|
||||
The `Expect` function is used to define a test expectation within a `Test` function. The expectation closure contains the code that should succeed and produce the expected result.
|
||||
|
||||
- Parameters:
|
||||
- description: A string describing the expectation.
|
||||
- expectation: A closure that contains the code to test the expectation.
|
||||
*/
|
||||
public func Expect(
|
||||
_ description: String? = nil,
|
||||
expectation: @escaping () async throws -> Void
|
||||
) async throws {
|
||||
try await t.expect(description, expectation: expectation)
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
import t
|
||||
import Scribe
|
||||
|
||||
/**
|
||||
The `TestConfiguration` enum provides a global configuration for tests.
|
||||
|
||||
Use this enum to customize the logger and scribe for the test framework.
|
||||
|
||||
Example usage:
|
||||
|
||||
TestConfiguration.logger = { print($0) }
|
||||
TestConfiguration.scribe = Scribe(label: "Custom Scribe")
|
||||
|
||||
*/
|
||||
public enum TestConfiguration {
|
||||
/**
|
||||
A closure that receives a string and logs it. This is used by the framework to log messages when tests run.
|
||||
|
||||
By default, the logger is set to `t.logger` from the `t` library.
|
||||
|
||||
- Note: To change the logger, set `TestConfiguration.logger`.
|
||||
|
||||
Example usage:
|
||||
|
||||
TestConfiguration.logger = { print($0) }
|
||||
*/
|
||||
public static var logger: (String) -> Void {
|
||||
get { t.logger }
|
||||
set { t.logger = newValue }
|
||||
}
|
||||
|
||||
/**
|
||||
The scribe used by the test framework.
|
||||
|
||||
By default, the scribe is set to a new instance of `Scribe` with a label of "Test.Scribe".
|
||||
|
||||
- Note: To change the scribe, set `TestConfiguration.scribe`.
|
||||
|
||||
Example usage:
|
||||
|
||||
TestConfiguration.scribe = Scribe(label: "Custom Scribe")
|
||||
|
||||
*/
|
||||
public static var scribe: Scribe = Scribe(label: "Test.Scribe")
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
import Plugin
|
||||
|
||||
/**
|
||||
The `TestPlugin` protocol defines a contract for plugins that can be used to extend the behavior of the `Tester` class.
|
||||
|
||||
`TestPlugin` extends the `ImmutablePlugin` protocol, which allows the plugin to be treated as an immutable object.
|
||||
|
||||
`TestPlugin` is used for conveince when creating a `Plugin` for a `Tester`.
|
||||
*/
|
||||
public protocol TestPlugin: ImmutablePlugin { }
|
|
@ -0,0 +1,105 @@
|
|||
import t
|
||||
|
||||
/**
|
||||
The `Test` function is used to create a test suite with multiple steps, expectations, and assertions.
|
||||
|
||||
- Parameters:
|
||||
- named: An optional name for the test suite.
|
||||
- tester: An instance of the `Tester` class to be used for running the test suite.
|
||||
- operation: A closure that contains the test steps, expectations, and assertions.
|
||||
- lineNumber: The line number where the `Test` function is called. Defaults to the line number where the function is called.
|
||||
- functionName: The name of the function where the `Test` function is called. Defaults to the name of the function where the function is called.
|
||||
- fileName: The name of the file where the `Test` function is called. Defaults to the name of the file where the function is called.
|
||||
|
||||
- Throws: A `TestError` if the test suite fails.
|
||||
|
||||
Example usage:
|
||||
|
||||
try Test(named: "My Test Suite") { tester in
|
||||
try Expect("First step should succeed") {
|
||||
try SomeClass.someMethod()
|
||||
}
|
||||
|
||||
try Expect("Second step should succeed") {
|
||||
try SomeClass.someOtherMethod()
|
||||
}
|
||||
|
||||
try Expect("Final assertion should be true") {
|
||||
try tester.assert(SomeClass.someBooleanValue)
|
||||
}
|
||||
}
|
||||
*/
|
||||
public func Test(
|
||||
named name: String? = nil,
|
||||
tester: Tester = Tester(),
|
||||
operation: (Tester) throws -> Void,
|
||||
lineNumber: Int = #line,
|
||||
functionName: String = #function,
|
||||
fileName: String = #file
|
||||
) throws {
|
||||
let result = t.suite(named: name) {
|
||||
try operation(tester)
|
||||
}
|
||||
|
||||
guard result == true else {
|
||||
let testName = name.map { "\($0) Test" } ?? "Test"
|
||||
throw TestError(
|
||||
description: "\(testName) failed.",
|
||||
lineNumber: lineNumber,
|
||||
functionName: functionName,
|
||||
fileName: fileName
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
The `Test` function is used to create a test suite with multiple steps, expectations, and assertions.
|
||||
|
||||
- Parameters:
|
||||
- named: An optional name for the test suite.
|
||||
- tester: An instance of the `Tester` class to be used for running the test suite.
|
||||
- operation: A closure that contains the test steps, expectations, and assertions.
|
||||
- lineNumber: The line number where the `Test` function is called. Defaults to the line number where the function is called.
|
||||
- functionName: The name of the function where the `Test` function is called. Defaults to the name of the function where the function is called.
|
||||
- fileName: The name of the file where the `Test` function is called. Defaults to the name of the file where the function is called.
|
||||
|
||||
- Throws: A `TestError` if the test suite fails.
|
||||
|
||||
Example usage:
|
||||
|
||||
try await Test(named: "My Test Suite") { tester in
|
||||
try Expect("First step should succeed") {
|
||||
try SomeClass.someMethod()
|
||||
}
|
||||
|
||||
try await Expect("Second step should succeed") {
|
||||
try await SomeClass.someOtherMethod()
|
||||
}
|
||||
|
||||
try Expect("Final assertion should be true") {
|
||||
try tester.assert(SomeClass.someBooleanValue)
|
||||
}
|
||||
}
|
||||
*/
|
||||
public func Test(
|
||||
named name: String? = nil,
|
||||
tester: Tester = Tester(),
|
||||
operation: (Tester) async throws -> Void,
|
||||
lineNumber: Int = #line,
|
||||
functionName: String = #function,
|
||||
fileName: String = #file
|
||||
) async throws {
|
||||
let result = await t.suite(named: name) {
|
||||
try await operation(tester)
|
||||
}
|
||||
|
||||
guard result == true else {
|
||||
let testName = name.map { "\($0) Test" } ?? "Test"
|
||||
throw TestError(
|
||||
description: "\(testName) failed.",
|
||||
lineNumber: lineNumber,
|
||||
functionName: functionName,
|
||||
fileName: fileName
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
import t
|
||||
|
||||
/**
|
||||
The `TestError` function creates an instance of a test error with a given description and location information.
|
||||
|
||||
- Parameters:
|
||||
- description: A string describing the error.
|
||||
- lineNumber: The line number where the error occurred. Defaults to the line number where the function is called.
|
||||
- functionName: The name of the function where the error occurred. Defaults to the name of the function where the function is called.
|
||||
- fileName: The name of the file where the error occurred. Defaults to the name of the file where the function is called.
|
||||
|
||||
- Returns: An instance of `Error` representing the test error.
|
||||
|
||||
Example usage:
|
||||
|
||||
throw TestError(description: "Test failed.")
|
||||
*/
|
||||
public func TestError(
|
||||
description: String,
|
||||
lineNumber: Int = #line,
|
||||
functionName: String = #function,
|
||||
fileName: String = #file
|
||||
) -> Error {
|
||||
t.error(
|
||||
description: description,
|
||||
lineNumber: lineNumber,
|
||||
functionName: functionName,
|
||||
fileName: fileName
|
||||
)
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
import t
|
||||
|
||||
/**
|
||||
The Tested function is used to test a single operation and return its output.
|
||||
|
||||
Parameters:
|
||||
description: A description of the operation being tested.
|
||||
operation: A closure that contains the operation to be tested.
|
||||
|
||||
Returns: The output of the tested operation.
|
||||
|
||||
Throws: Any errors that occur during the operation.
|
||||
|
||||
Example usage:
|
||||
|
||||
let result = try Tested("Tested operation") {
|
||||
return SomeClass.someMethod()
|
||||
}
|
||||
*/
|
||||
public func Tested<Output>(
|
||||
_ description: String,
|
||||
operation: @escaping () throws -> Output
|
||||
) throws -> Output {
|
||||
try t.tested(description, operation)
|
||||
}
|
||||
|
||||
/**
|
||||
The Tested function is used to test a single operation and return its output.
|
||||
|
||||
Parameters:
|
||||
operation: A closure that contains the operation to be tested.
|
||||
|
||||
Returns: The output of the tested operation.
|
||||
|
||||
Throws: Any errors that occur during the operation.
|
||||
|
||||
Example usage:
|
||||
|
||||
let result = try Tested("Tested operation") {
|
||||
return SomeClass.someMethod()
|
||||
}
|
||||
*/
|
||||
public func Tested<Output>(
|
||||
operation: @escaping () throws -> Output
|
||||
) throws -> Output {
|
||||
try t.tested(operation)
|
||||
}
|
||||
|
||||
/**
|
||||
The Tested function is used to test a single operation and return its output.
|
||||
|
||||
Parameters:
|
||||
description: A description of the operation being tested.
|
||||
operation: A closure that contains the operation to be tested.
|
||||
|
||||
Returns: The output of the tested operation.
|
||||
|
||||
Throws: Any errors that occur during the operation.
|
||||
|
||||
Example usage:
|
||||
|
||||
let result = try await Tested("Tested operation") {
|
||||
try await SomeClass.someMethod()
|
||||
}
|
||||
*/
|
||||
public func Tested<Output>(
|
||||
_ description: String,
|
||||
operation: @escaping () async throws -> Output
|
||||
) async throws -> Output {
|
||||
try await t.tested(description, operation)
|
||||
}
|
||||
|
||||
/**
|
||||
The Tested function is used to test a single operation and return its output.
|
||||
|
||||
Parameters:
|
||||
operation: A closure that contains the operation to be tested.
|
||||
|
||||
Returns: The output of the tested operation.
|
||||
|
||||
Throws: Any errors that occur during the operation.
|
||||
|
||||
Example usage:
|
||||
|
||||
let result = try await Tested("Tested operation") {
|
||||
try await SomeClass.someMethod()
|
||||
}
|
||||
*/
|
||||
public func Tested<Output>(
|
||||
operation: @escaping () async throws -> Output
|
||||
) async throws -> Output {
|
||||
try await t.tested(operation)
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
import Scribe
|
||||
|
||||
extension Tester {
|
||||
public typealias PluginPayload = Scribe.PluginPayload
|
||||
public typealias Message = Scribe.Message
|
||||
public typealias Level = Scribe.Level
|
||||
public typealias Metadata = Scribe.Metadata
|
||||
|
||||
/// Logs a message with the trace log level.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - message: The message to be logged.
|
||||
/// - metadata: Optional metadata to be included with the log message.
|
||||
/// - source: Optional source information to be included with the log message.
|
||||
/// - Returns: A task representing the log operation.
|
||||
@discardableResult
|
||||
public func logTrace(
|
||||
_ message: Message,
|
||||
metadata: Metadata? = nil,
|
||||
source: String? = nil
|
||||
) -> Task<Void, Error> {
|
||||
scribe.trace(
|
||||
message,
|
||||
metadata: metadata,
|
||||
source: source
|
||||
)
|
||||
}
|
||||
|
||||
/// Logs a message with the debug log level.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - message: The message to be logged.
|
||||
/// - metadata: Optional metadata to be included with the log message.
|
||||
/// - source: Optional source information to be included with the log message.
|
||||
/// - Returns: A task representing the log operation.
|
||||
@discardableResult
|
||||
public func logDebug(
|
||||
_ message: Message,
|
||||
metadata: Metadata? = nil,
|
||||
source: String? = nil
|
||||
) -> Task<Void, Error> {
|
||||
scribe.debug(
|
||||
message,
|
||||
metadata: metadata,
|
||||
source: source
|
||||
)
|
||||
}
|
||||
|
||||
/// Logs a message with the info log level.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - message: The message to be logged.
|
||||
/// - metadata: Optional metadata to be included with the log message.
|
||||
/// - source: Optional source information to be included with the log message.
|
||||
/// - Returns: A task representing the log operation.
|
||||
@discardableResult
|
||||
public func logInfo(
|
||||
_ message: Message,
|
||||
metadata: Metadata? = nil,
|
||||
source: String? = nil
|
||||
) -> Task<Void, Error> {
|
||||
scribe.info(
|
||||
message,
|
||||
metadata: metadata,
|
||||
source: source
|
||||
)
|
||||
}
|
||||
|
||||
/// Logs a message with the notice log level.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - message: The message to be logged.
|
||||
/// - metadata: Optional metadata to be included with the log message.
|
||||
/// - source: Optional source information to be included with the log message.
|
||||
/// - Returns: A task representing the log operation.
|
||||
@discardableResult
|
||||
public func logNotice(
|
||||
_ message: Message,
|
||||
metadata: Metadata? = nil,
|
||||
source: String? = nil
|
||||
) -> Task<Void, Error> {
|
||||
scribe.notice(
|
||||
message,
|
||||
metadata: metadata,
|
||||
source: source
|
||||
)
|
||||
}
|
||||
|
||||
/// Logs a message with the warning log level.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - message: The message to be logged.
|
||||
/// - metadata: Optional metadata to be included with the log message.
|
||||
/// - source: Optional source information to be included with the log message.
|
||||
/// - Returns: A task representing the log operation.
|
||||
@discardableResult
|
||||
public func logWarning(
|
||||
_ message: Message,
|
||||
metadata: Metadata? = nil,
|
||||
source: String? = nil
|
||||
) -> Task<Void, Error> {
|
||||
scribe.warning(
|
||||
message,
|
||||
metadata: metadata,
|
||||
source: source
|
||||
)
|
||||
}
|
||||
|
||||
/// Logs a message with the error log level.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - message: The message to be logged.
|
||||
/// - metadata: Optional metadata to be included with the log message.
|
||||
/// - source: Optional source information to be included with the log message.
|
||||
/// - Returns: A task representing the log operation.
|
||||
@discardableResult
|
||||
public func logError(
|
||||
_ message: Message,
|
||||
metadata: Metadata? = nil,
|
||||
source: String? = nil
|
||||
) -> Task<Void, Error> {
|
||||
scribe.error(
|
||||
message,
|
||||
metadata: metadata,
|
||||
source: source
|
||||
)
|
||||
}
|
||||
|
||||
/// Logs a message with the critical log level.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - message: The message to be logged.
|
||||
/// - metadata: Optional metadata to be included with the log message.
|
||||
/// - source: Optional source information to be included with the log message.
|
||||
/// - Returns: A task representing the log operation.
|
||||
@discardableResult
|
||||
public func logCritical(
|
||||
_ message: Message,
|
||||
metadata: Metadata? = nil,
|
||||
source: String? = nil
|
||||
) -> Task<Void, Error> {
|
||||
scribe.critical(
|
||||
message,
|
||||
metadata: metadata,
|
||||
source: source
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,199 @@
|
|||
import t
|
||||
|
||||
extension Tester {
|
||||
/// Assert that the condition is true.
|
||||
public func assert(
|
||||
_ condition: Bool,
|
||||
_ message: String? = nil,
|
||||
lineNumber: Int = #line,
|
||||
functionName: String = #function,
|
||||
fileName: String = #file
|
||||
) throws {
|
||||
try t.assert(
|
||||
condition,
|
||||
message,
|
||||
lineNumber: lineNumber,
|
||||
functionName: functionName,
|
||||
fileName: fileName
|
||||
)
|
||||
}
|
||||
|
||||
/// Assert that the condition is false.
|
||||
public func assert(
|
||||
notTrue condition: Bool,
|
||||
_ message: String? = nil,
|
||||
lineNumber: Int = #line,
|
||||
functionName: String = #function,
|
||||
fileName: String = #file
|
||||
) throws {
|
||||
try t.assert(
|
||||
notTrue: condition,
|
||||
message,
|
||||
lineNumber: lineNumber,
|
||||
functionName: functionName,
|
||||
fileName: fileName
|
||||
)
|
||||
}
|
||||
|
||||
/// Assert that the first value is equal to the second.
|
||||
public func assert<Value: Equatable>(
|
||||
_ firstValue: Value,
|
||||
isEqualTo secondValue: Value,
|
||||
_ message: String? = nil,
|
||||
lineNumber: Int = #line,
|
||||
functionName: String = #function,
|
||||
fileName: String = #file
|
||||
) throws {
|
||||
try t.assert(
|
||||
firstValue,
|
||||
isEqualTo: secondValue,
|
||||
message,
|
||||
lineNumber: lineNumber,
|
||||
functionName: functionName,
|
||||
fileName: fileName
|
||||
)
|
||||
}
|
||||
|
||||
/// Assert that the first value is not equal to the second.
|
||||
public func assert<Value: Equatable>(
|
||||
_ firstValue: Value,
|
||||
isNotEqualTo secondValue: Value,
|
||||
_ message: String? = nil,
|
||||
lineNumber: Int = #line,
|
||||
functionName: String = #function,
|
||||
fileName: String = #file
|
||||
) throws {
|
||||
try t.assert(
|
||||
firstValue,
|
||||
isNotEqualTo: secondValue,
|
||||
message,
|
||||
lineNumber: lineNumber,
|
||||
functionName: functionName,
|
||||
fileName: fileName
|
||||
)
|
||||
}
|
||||
|
||||
/// Assert that the value is not nil.
|
||||
public func assert<Value>(
|
||||
isNotNil value: Value?,
|
||||
_ message: String? = nil,
|
||||
lineNumber: Int = #line,
|
||||
functionName: String = #function,
|
||||
fileName: String = #file
|
||||
) throws {
|
||||
try t.assert(
|
||||
isNotNil: value,
|
||||
message,
|
||||
lineNumber: lineNumber,
|
||||
functionName: functionName,
|
||||
fileName: fileName
|
||||
)
|
||||
}
|
||||
|
||||
/// Assert that the value is nil.
|
||||
public func assert<Value>(
|
||||
isNil value: Value?,
|
||||
_ message: String? = nil,
|
||||
lineNumber: Int = #line,
|
||||
functionName: String = #function,
|
||||
fileName: String = #file
|
||||
) throws {
|
||||
try t.assert(
|
||||
isNil: value,
|
||||
message,
|
||||
lineNumber: lineNumber,
|
||||
functionName: functionName,
|
||||
fileName: fileName
|
||||
)
|
||||
}
|
||||
|
||||
/// Assert that the closure should throw
|
||||
public func assertThrows<Value>(
|
||||
_ message: String? = nil,
|
||||
operation: @escaping @autoclosure () throws -> Value,
|
||||
lineNumber: Int = #line,
|
||||
functionName: String = #function,
|
||||
fileName: String = #file
|
||||
) throws {
|
||||
try t.assertThrows(
|
||||
operation,
|
||||
message: message,
|
||||
lineNumber: lineNumber,
|
||||
functionName: functionName,
|
||||
fileName: fileName
|
||||
)
|
||||
}
|
||||
|
||||
/// Assert that the closure should not throw
|
||||
public func assertNoThrows<Value>(
|
||||
_ message: String? = nil,
|
||||
operation: @escaping @autoclosure () throws -> Value,
|
||||
lineNumber: Int = #line,
|
||||
functionName: String = #function,
|
||||
fileName: String = #file
|
||||
) throws {
|
||||
try t.assertNoThrows(
|
||||
operation,
|
||||
message: message,
|
||||
lineNumber: lineNumber,
|
||||
functionName: functionName,
|
||||
fileName: fileName
|
||||
)
|
||||
}
|
||||
|
||||
///
|
||||
@discardableResult
|
||||
public func unwrap<Value>(
|
||||
_ value: Value?,
|
||||
message: String? = nil,
|
||||
lineNumber: Int = #line,
|
||||
functionName: String = #function,
|
||||
fileName: String = #file
|
||||
) throws -> Value {
|
||||
try t.unwrap(
|
||||
value,
|
||||
message: message,
|
||||
lineNumber: lineNumber,
|
||||
functionName: functionName,
|
||||
fileName: fileName
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
|
||||
extension Tester {
|
||||
/// Assert that the closure should throw
|
||||
public func assertThrows<Value>(
|
||||
_ message: String? = nil,
|
||||
operation: @escaping @autoclosure () async throws -> Value,
|
||||
lineNumber: Int = #line,
|
||||
functionName: String = #function,
|
||||
fileName: String = #file
|
||||
) async throws {
|
||||
try await t.assertThrows(
|
||||
operation,
|
||||
message: message,
|
||||
lineNumber: lineNumber,
|
||||
functionName: functionName,
|
||||
fileName: fileName
|
||||
)
|
||||
}
|
||||
|
||||
/// Assert that the closure should not throw
|
||||
public func assertNoThrows<Value>(
|
||||
_ message: String? = nil,
|
||||
operation: @escaping @autoclosure () async throws -> Value,
|
||||
lineNumber: Int = #line,
|
||||
functionName: String = #function,
|
||||
fileName: String = #file
|
||||
) async throws {
|
||||
try await t.assertNoThrows(
|
||||
operation,
|
||||
message: message,
|
||||
lineNumber: lineNumber,
|
||||
functionName: functionName,
|
||||
fileName: fileName
|
||||
)
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,29 @@
|
|||
import t
|
||||
import Scribe
|
||||
import Plugin
|
||||
|
||||
/**
|
||||
The `Tester` class is responsible for managing the execution of a test suite.
|
||||
|
||||
A `Tester` instance contains a `Scribe` object, which is used to log test results and a collection of plugins that are used to perform custom operations during the test run.
|
||||
*/
|
||||
open class Tester: ImmutablePluginable {
|
||||
/// The `Scribe` object used to log test results.
|
||||
open var scribe: Scribe
|
||||
|
||||
/**
|
||||
Initializes a new `Tester` object.
|
||||
|
||||
- Parameters:
|
||||
- scribe: The `Scribe` object used to log test results. Defaults to `TestConfiguration.scribe`.
|
||||
- plugins: An array of plugins to use during the test run. Defaults to an empty array.
|
||||
*/
|
||||
public init(
|
||||
scribe: Scribe = TestConfiguration.scribe,
|
||||
plugins: [any Plugin] = []
|
||||
) {
|
||||
self.scribe = scribe
|
||||
|
||||
super.init(plugins: plugins)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
import XCTest
|
||||
@testable import Test
|
||||
|
||||
final class TestTests: XCTestCase {
|
||||
override class func setUp() {
|
||||
super.setUp()
|
||||
|
||||
TestConfiguration.logger = { _ in }
|
||||
}
|
||||
|
||||
func testExample() async throws {
|
||||
struct ExampleTestPlugin: TestPlugin {
|
||||
func handle(value: Bool) async throws {
|
||||
guard value else {
|
||||
throw TestError(description: "False value")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try await Test(
|
||||
named: "Development",
|
||||
tester: Tester(
|
||||
plugins: [
|
||||
ExampleTestPlugin()
|
||||
]
|
||||
),
|
||||
operation: { tester in
|
||||
try await Expect(description) {
|
||||
tester.logInfo("Info!")
|
||||
|
||||
try tester.assert(0, isEqualTo: 0)
|
||||
|
||||
try await tester.handle(value: true)
|
||||
|
||||
tester.logError("There should have been an error")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue