diff --git a/Package.swift b/Package.swift index 6d56232..58496cb 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.3 +// swift-tools-version:5.10 import PackageDescription @@ -25,4 +25,4 @@ let package = Package( path: "Tests/AllTests" ), ] -) \ No newline at end of file +) diff --git a/README.md b/README.md index b2ac155..b3f3ce5 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ [![GitHub License](https://img.shields.io/github/license/XCEssentials/Requirement.svg?longCache=true)](LICENSE) [![GitHub Tag](https://img.shields.io/github/tag/XCEssentials/Requirement.svg?longCache=true)](https://github.com/XCEssentials/Requirement/tags) [![Swift Package Manager Compatible](https://img.shields.io/badge/SPM-compatible-brightgreen.svg?longCache=true)](Package.swift) -[![Written in Swift](https://img.shields.io/badge/Swift-5.3-orange.svg?longCache=true)](https://swift.org) +[![Written in Swift](https://img.shields.io/badge/Swift-5.10%2B-orange.svg?longCache=true)](https://swift.org) [![Supported platforms](https://img.shields.io/badge/platforms-macOS%20%7C%20iOS%20%7C%20tvOS%20%7C%20watchOS%20%7C%20Linux-blue.svg?longCache=true)](Package.swift) [![CI](https://github.com/XCEssentials/Requirement/actions/workflows/ci.yml/badge.svg)](https://github.com/XCEssentials/Requirement/actions/workflows/ci.yml) @@ -35,7 +35,7 @@ Each requirement can be evaluated against a given data value (which can be an at ## How to install -The recommended way is to install using [SwiftPM](https://swift.org/package-manager/), but [Carthage](https://github.com/Carthage/Carthage) is also supported out of the box. +Install using [SwiftPM](https://swift.org/package-manager/). The package currently targets Swift tools version `5.10` and newer. ## How it works diff --git a/Tests/AllTests/AllTests.swift b/Tests/AllTests/AllTests.swift index 2a72884..5622c68 100644 --- a/Tests/AllTests/AllTests.swift +++ b/Tests/AllTests/AllTests.swift @@ -26,18 +26,34 @@ class AllTests: XCTestCase XCTFail("Unexpected failure") } } + + func test_requirement_validate_rethrowsConditionEvaluationError() + { + enum TestError: Error { case brokenCondition } + + let requirement = Requirement("Any value") { _ in + throw TestError.brokenCondition + } + + XCTAssertThrowsError(try requirement.validate(14)) { error in + guard + case TestError.brokenCondition = error + else + { + return XCTFail("Unexpected error") + } + } + } func test_requirement_unsatisfiedCondition() { let value = 0 - do - { + XCTAssertThrowsError( try Requirement("Non-zero value"){ $0 != value }.validate(value) - } - catch - { + ) + { error in guard let unsatisfiedRequirement = error as? UnsatisfiedRequirement else @@ -46,10 +62,61 @@ class AllTests: XCTestCase } XCTAssertEqual(unsatisfiedRequirement.description, "Non-zero value") - XCTAssert(unsatisfiedRequirement.input as! Int == value) + guard + let actualInput = unsatisfiedRequirement.input as? Int + else + { + return XCTFail("Unexpected input type") + } + XCTAssertEqual(actualInput, value) XCTAssertTrue(unsatisfiedRequirement.context.function.contains("test_requirement_unsatisfiedCondition")) } } + + func test_requirement_unsatisfiedCondition_customContext() + { + let requirement = Requirement("Non-zero value") { $0 != 0 } + + XCTAssertThrowsError( + try requirement.validate( + file: "CustomFile.swift", + line: 42, + function: "customFunction()", + 0 + ) + ) + { error in + guard + let unsatisfiedRequirement = error as? UnsatisfiedRequirement + else + { + return XCTFail("Unexpected validation error") + } + + XCTAssertEqual(unsatisfiedRequirement.context.file, "CustomFile.swift") + XCTAssertEqual(unsatisfiedRequirement.context.line, 42) + XCTAssertEqual(unsatisfiedRequirement.context.function, "customFunction()") + } + } + + func test_requirement_isValid() + { + let requirement = Requirement("Non-zero value") { $0 != 0 } + + XCTAssertTrue(requirement.isValid(1)) + XCTAssertFalse(requirement.isValid(0)) + } + + func test_requirement_isValid_returnsFalseOnThrownConditionError() + { + enum TestError: Error { case brokenCondition } + + let requirement = Requirement("Any value") { _ in + throw TestError.brokenCondition + } + + XCTAssertFalse(requirement.isValid(1)) + } func test_inlineCheck_success() { @@ -81,16 +148,13 @@ class AllTests: XCTestCase } } - do - { + XCTAssertThrowsError( try Check.that("Non-zero value") { try Container.failingProperty } - - } - catch - { + ) + { error in guard case let FailedCheck.errorDuringConditionCheck(desc, nestedError, context) = error else @@ -114,12 +178,10 @@ class AllTests: XCTestCase { let value = 0 - do - { + XCTAssertThrowsError( try Check.that("Non-zero value", value != 0 ) - } - catch - { + ) + { error in guard case let FailedCheck.unsatisfiedCondition(desc, context) = error else @@ -180,12 +242,7 @@ class AllTests: XCTestCase { let value: Int? = nil - do - { - try Check.nonEmpty { value } - } - catch - { + XCTAssertThrowsError(try Check.nonEmpty { value }) { error in switch error { case FailedCheck.unsatisfiedNonEmptyCondition(let desc, _): @@ -196,12 +253,10 @@ class AllTests: XCTestCase } } - do - { + XCTAssertThrowsError( try Check.nonEmpty("Custom check description") { value } - } - catch - { + ) + { error in switch error { case FailedCheck.unsatisfiedNonEmptyCondition(let desc, _): @@ -212,4 +267,68 @@ class AllTests: XCTestCase } } } + + func test_nonEmpty_errorDuringConditionCheck_whenInputThrows() + { + enum TestError: Error { case brokenCondition } + + XCTAssertThrowsError( + try Check.nonEmpty("Value is set") { () -> Int? in + throw TestError.brokenCondition + } + ) + { error in + guard + case let FailedCheck.errorDuringConditionCheck(desc, nestedError, context) = error + else + { + return XCTFail("Unexpected validation error") + } + + XCTAssertEqual(desc, "Value is set") + XCTAssertTrue(context.function.contains("test_nonEmpty_errorDuringConditionCheck_whenInputThrows")) + guard + case TestError.brokenCondition = nestedError + else + { + return XCTFail("Unexpected nested error") + } + } + } + + func test_nonEmpty_unsatisfiedCondition_customContext() + { + let value: Int? = nil + + XCTAssertThrowsError( + try Check.nonEmpty( + file: "CustomFile.swift", + line: 77, + function: "customFunction()", + "Value is set" + ) { value } + ) + { error in + guard + case let FailedCheck.unsatisfiedNonEmptyCondition(desc, context) = error + else + { + return XCTFail("Unexpected validation error") + } + + XCTAssertEqual(desc, "Value is set") + XCTAssertEqual(context.file, "CustomFile.swift") + XCTAssertEqual(context.line, 77) + XCTAssertEqual(context.function, "customFunction()") + } + } + + func test_aliases_requireAndCondition() + { + let require: Require = Require("Non-zero value") { $0 != 0 } + let condition: Condition = Condition("Positive value") { $0 > 0 } + + XCTAssertTrue(require.isValid(1)) + XCTAssertFalse(condition.isValid(0)) + } }