├── .github ├── ISSUE_TEMPLATE ├── PULL_REQUEST_TEMPLATE ├── dependabot.yml ├── funding.yml └── workflows │ ├── carthage.yml │ ├── ci-swiftpm.yml │ ├── ci-xcode.yml │ ├── cocoapods.yml │ ├── documentation.yml │ ├── release.yml │ ├── swiftlint.yml │ └── wasm.yml ├── .gitignore ├── .swiftlint.yml ├── CONTRIBUTING.md ├── Cartfile.resolved ├── Dockerfile.test ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── Nimble.podspec ├── Nimble.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ ├── WorkspaceSettings.xcsettings │ │ └── swiftpm │ │ └── Package.resolved └── xcshareddata │ └── xcschemes │ └── Nimble.xcscheme ├── Package.resolved ├── Package.swift ├── Package@swift-5.9.swift ├── README.md ├── Sources ├── Nimble │ ├── Adapters │ │ ├── AdapterProtocols.swift │ │ ├── AssertionDispatcher.swift │ │ ├── AssertionRecorder+Async.swift │ │ ├── AssertionRecorder.swift │ │ ├── NMBExpectation.swift │ │ ├── NimbleEnvironment.swift │ │ ├── NimbleSwiftTestingHandler.swift │ │ ├── NimbleXCTestHandler.swift │ │ └── NonObjectiveC │ │ │ └── ExceptionCapture.swift │ ├── AsyncExpression.swift │ ├── DSL+AsyncAwait.swift │ ├── DSL+Require.swift │ ├── DSL+Wait.swift │ ├── DSL.swift │ ├── Expectation.swift │ ├── ExpectationMessage.swift │ ├── Expression.swift │ ├── FailureMessage.swift │ ├── Info.plist │ ├── Matchers │ │ ├── AllPass.swift │ │ ├── AsyncAllPass.swift │ │ ├── AsyncMatcher.swift │ │ ├── BeAKindOf.swift │ │ ├── BeAnInstanceOf.swift │ │ ├── BeCloseTo.swift │ │ ├── BeEmpty.swift │ │ ├── BeGreaterThan.swift │ │ ├── BeGreaterThanOrEqualTo.swift │ │ ├── BeIdenticalTo.swift │ │ ├── BeLessThan.swift │ │ ├── BeLessThanOrEqual.swift │ │ ├── BeLogical.swift │ │ ├── BeNil.swift │ │ ├── BeResult.swift │ │ ├── BeVoid.swift │ │ ├── BeWithin.swift │ │ ├── BeginWith.swift │ │ ├── BeginWithPrefix.swift │ │ ├── Contain.swift │ │ ├── ContainElementSatisfying.swift │ │ ├── ElementsEqual.swift │ │ ├── EndWith.swift │ │ ├── Equal+Tuple.swift │ │ ├── Equal+TupleArray.swift │ │ ├── Equal.swift │ │ ├── HaveCount.swift │ │ ├── Map.swift │ │ ├── Match.swift │ │ ├── MatchError.swift │ │ ├── Matcher.swift │ │ ├── MatcherProtocols.swift │ │ ├── Negation.swift │ │ ├── PostNotification.swift │ │ ├── RaisesException.swift │ │ ├── SatisfyAllOf.swift │ │ ├── SatisfyAnyOf.swift │ │ ├── ThrowAssertion.swift │ │ ├── ThrowError.swift │ │ └── ToSucceed.swift │ ├── Nimble.docc │ │ ├── Guides │ │ │ ├── Background.md │ │ │ ├── Concurrency.md │ │ │ ├── Expectations.md │ │ │ ├── ObjectiveC.md │ │ │ ├── PollingExpectations.md │ │ │ ├── Require.md │ │ │ └── WritingCustomMatchers.md │ │ ├── Matchers │ │ │ ├── Collections.md │ │ │ ├── Comparisons.md │ │ │ ├── CustomValidation.md │ │ │ ├── Equivalence.md │ │ │ ├── Exceptions.md │ │ │ ├── GroupsOfMatchers.md │ │ │ ├── Identity.md │ │ │ ├── Map.md │ │ │ ├── Notifications.md │ │ │ ├── Result.md │ │ │ ├── Strings.md │ │ │ ├── SwiftAssertions.md │ │ │ ├── SwiftErrors.md │ │ │ ├── Truthiness.md │ │ │ └── TypeChecking.md │ │ └── Nimble.md │ ├── Nimble.h │ ├── Polling+AsyncAwait.swift │ ├── Polling+Require.swift │ ├── Polling.swift │ ├── PrivacyInfo.xcprivacy │ ├── Requirement.swift │ └── Utils │ │ ├── AsyncAwait.swift │ │ ├── AsyncTimerSequence.swift │ │ ├── Errors.swift │ │ ├── NimbleTimeInterval.swift │ │ ├── PollAwait.swift │ │ ├── SourceLocation.swift │ │ └── Stringers.swift ├── NimbleObjectiveC │ ├── DSL.m │ ├── NMBExceptionCapture.m │ ├── NMBStringify.m │ ├── XCTestObservationCenter+Register.m │ └── include │ │ ├── DSL.h │ │ ├── NMBExceptionCapture.h │ │ └── NMBStringify.h └── NimbleSharedTestHelpers │ └── utils.swift ├── Tests ├── .swiftlint.yml ├── NimbleObjectiveCTests │ ├── NimbleSpecHelper.h │ ├── ObjCAllPassTest.m │ ├── ObjCAsyncTest.m │ ├── ObjCBeAnInstanceOfTest.m │ ├── ObjCBeCloseToTest.m │ ├── ObjCBeEmptyTest.m │ ├── ObjCBeFalseTest.m │ ├── ObjCBeFalsyTest.m │ ├── ObjCBeGreaterThanOrEqualToTest.m │ ├── ObjCBeGreaterThanTest.m │ ├── ObjCBeIdenticalToTest.m │ ├── ObjCBeKindOfTest.m │ ├── ObjCBeLessThanOrEqualToTest.m │ ├── ObjCBeLessThanTest.m │ ├── ObjCBeNilTest.m │ ├── ObjCBeTrueTest.m │ ├── ObjCBeTruthyTest.m │ ├── ObjCBeginWithTest.m │ ├── ObjCContainElementSatisfyingTest.m │ ├── ObjCContainTest.m │ ├── ObjCEndWithTest.m │ ├── ObjCEqualTest.m │ ├── ObjCHaveCountTest.m │ ├── ObjCMatchTest.m │ ├── ObjCRaiseExceptionTest.m │ ├── ObjCSatisfyAllOfTest.m │ ├── ObjCSatisfyAnyOfTest.m │ ├── ObjCSyncTest.m │ ├── ObjCUserDescriptionTest.m │ └── ObjcStringersTest.m └── NimbleTests │ ├── AsyncAwaitTest+Require.swift │ ├── AsyncAwaitTest.swift │ ├── AsyncPromiseTest.swift │ ├── AsyncTimerSequenceTest.swift │ ├── DSLTest.swift │ ├── Helpers │ ├── AsyncHelpers.swift │ ├── BackgroundThreadObject.swift │ └── ObjectWithLazyProperty.swift │ ├── Info.plist │ ├── LinuxSupport.swift │ ├── Matchers │ ├── AllPassTest.swift │ ├── AlwaysFailMatcher.swift │ ├── AsyncAllPassTest.swift │ ├── AsyncPredicateTest.swift │ ├── BeAKindOfTest.swift │ ├── BeAnInstanceOfTest.swift │ ├── BeCloseToTest.swift │ ├── BeEmptyTest.swift │ ├── BeGreaterThanOrEqualToTest.swift │ ├── BeGreaterThanTest.swift │ ├── BeIdenticalToObjectTest.swift │ ├── BeIdenticalToTest.swift │ ├── BeLessThanOrEqualToTest.swift │ ├── BeLessThanTest.swift │ ├── BeLogicalTest.swift │ ├── BeNilTest.swift │ ├── BeResultTest.swift │ ├── BeVoidTest.swift │ ├── BeWithinTest.swift │ ├── BeginWithPrefixTest.swift │ ├── BeginWithTest.swift │ ├── ContainElementSatisfyingTest.swift │ ├── ContainTest.swift │ ├── ElementsEqualTest.swift │ ├── EndWithTest.swift │ ├── EqualTest.swift │ ├── HaveCountTest.swift │ ├── MapTest.swift │ ├── MatchErrorTest.swift │ ├── MatchTest.swift │ ├── NegationTest.swift │ ├── PostNotificationTest.swift │ ├── RaisesExceptionTest.swift │ ├── SatisfyAllOfTest.swift │ ├── SatisfyAnyOfTest.swift │ ├── ThrowAssertionTest.swift │ ├── ThrowErrorTest.swift │ └── ToSucceedTest.swift │ ├── OnFailureThrowsTest.swift │ ├── PollingTest+Require.swift │ ├── PollingTest.swift │ ├── PredicateTest.swift │ ├── StatusTest.swift │ ├── SwiftTestingSupportTest.swift │ ├── SynchronousTest.swift │ └── UserDescriptionTest.swift ├── script ├── build_docs └── release └── test /.github/ISSUE_TEMPLATE: -------------------------------------------------------------------------------- 1 | - [ ] I have read [CONTRIBUTING](https://github.com/Quick/Nimble/blob/main/CONTRIBUTING.md) and have done my best to follow them. 2 | 3 | ### What did you do? 4 | 5 | Please replace this with what you did. 6 | 7 | ### What did you expect to happen? 8 | 9 | Please replace this with what you expected to happen. 10 | 11 | ### What actually happened instead? 12 | 13 | Please replace this with what happened instead. 14 | 15 | ### Environment 16 | 17 | List the software versions you're using: 18 | 19 | - Quick: *?.?.?* 20 | - Nimble: *?.?.?* 21 | - Xcode Version: *?.? (????)* (Open Xcode; In menubar: Xcode > About Xcode) 22 | - Swift Version: *?.?* (Open Xcode Preferences; Components > Toolchains. If none, use `Xcode Default`.) 23 | 24 | Please also mention which package manager you used and its version. Delete the 25 | other package managers in this list: 26 | 27 | - Cocoapods: *?.?.?* (Use `pod --version` in Terminal) 28 | - Carthage: *?.?* (Use `carthage version` in Terminal) 29 | - Swift Package Manager *?.?.? (swiftpm-???)* (Use `swift build --version` in Terminal) 30 | 31 | ### Project that demonstrates the issue 32 | 33 | Please link to a project we can download that reproduces the issue. Feel free 34 | to delete this section if it's not relevant to the issue (eg - feature request). 35 | 36 | The project should be [short, self-contained, and correct example](http://sscce.org/). 37 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE: -------------------------------------------------------------------------------- 1 | The PR should summarize what was changed and why. Here are some questions to 2 | help you if you're not sure: 3 | 4 | - What behavior was changed? 5 | - What code was refactored / updated to support this change? 6 | - What issues are related to this PR? Or why was this change introduced? 7 | 8 | Checklist - While not every PR needs it, new features should consider this list: 9 | 10 | - [ ] Does this have tests? 11 | - [ ] Does this have documentation? 12 | - [ ] Does this break the public API (Requires major version bump)? 13 | - [ ] Is this a new feature (Requires minor version bump)? 14 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "bundler" 9 | directory: "/" 10 | schedule: 11 | interval: "weekly" 12 | - package-ecosystem: "github-actions" 13 | directory: "/" 14 | schedule: 15 | interval: "weekly" 16 | -------------------------------------------------------------------------------- /.github/funding.yml: -------------------------------------------------------------------------------- 1 | github: [jessesquires, younata] 2 | -------------------------------------------------------------------------------- /.github/workflows/carthage.yml: -------------------------------------------------------------------------------- 1 | name: Carthage 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | tags: 8 | - "*" 9 | pull_request: 10 | branches: 11 | - "*" 12 | 13 | jobs: 14 | carthage: 15 | name: Carthage Build 16 | runs-on: macos-14 17 | steps: 18 | - uses: actions/checkout@v4 19 | - uses: ruby/setup-ruby@v1 20 | with: 21 | ruby-version: 3.2 22 | bundler-cache: true 23 | - run: ./test carthage 24 | -------------------------------------------------------------------------------- /.github/workflows/ci-swiftpm.yml: -------------------------------------------------------------------------------- 1 | name: CI (SwiftPM) 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | tags: 8 | - "*" 9 | pull_request: 10 | branches: 11 | - "*" 12 | 13 | jobs: 14 | swiftpm_darwin_ventura: 15 | name: SwiftPM, Darwin, Xcode ${{ matrix.xcode }} 16 | runs-on: macos-13 17 | strategy: 18 | matrix: 19 | xcode: ["14.3.1"] 20 | env: 21 | DEVELOPER_DIR: "/Applications/Xcode_${{ matrix.xcode }}.app" 22 | steps: 23 | - uses: actions/checkout@v4 24 | - run: ./test swiftpm 25 | 26 | swiftpm_darwin_sonoma: 27 | name: SwiftPM, Darwin, Xcode ${{ matrix.xcode }} 28 | runs-on: macos-14 29 | strategy: 30 | matrix: 31 | xcode: ["15.3", "16.1"] 32 | env: 33 | DEVELOPER_DIR: "/Applications/Xcode_${{ matrix.xcode }}.app" 34 | steps: 35 | - uses: actions/checkout@v4 36 | - run: ./test swiftpm 37 | 38 | swiftpm_linux: 39 | name: SwiftPM, Linux 40 | runs-on: ubuntu-latest 41 | strategy: 42 | matrix: 43 | container: 44 | - swift:5.7 45 | - swift:5.8 46 | - swift:5.9 47 | - swift:6.0 48 | # - swiftlang/swift:nightly 49 | fail-fast: false 50 | container: ${{ matrix.container }} 51 | steps: 52 | - uses: actions/checkout@v4 53 | - run: swift build -Xswiftc -suppress-warnings 54 | - run: swift test -Xswiftc -suppress-warnings --enable-test-discovery 55 | 56 | swiftpm_windows: 57 | name: SwiftPM, Windows 58 | runs-on: windows-latest 59 | steps: 60 | - uses: actions/checkout@v4 61 | - name: Install Swift 62 | uses: compnerd/gha-setup-swift@main 63 | with: 64 | branch: swift-5.9-release 65 | tag: 5.9-RELEASE 66 | - name: Test Windows 67 | run: swift test -Xswiftc -suppress-warnings 68 | -------------------------------------------------------------------------------- /.github/workflows/ci-xcode.yml: -------------------------------------------------------------------------------- 1 | name: CI (Xcode) 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | tags: 8 | - "*" 9 | pull_request: 10 | branches: 11 | - "*" 12 | 13 | jobs: 14 | xcode_ventura: 15 | name: Xcode ${{ matrix.xcode }} (Xcode Project) 16 | runs-on: macos-13 17 | strategy: 18 | matrix: 19 | xcode: ["14.3.1"] 20 | fail-fast: false 21 | env: 22 | DEVELOPER_DIR: "/Applications/Xcode_${{ matrix.xcode }}.app" 23 | steps: 24 | - uses: actions/checkout@v4 25 | - run: ./test macos 26 | - run: ./test ios 27 | - run: ./test tvos 28 | - run: ./test watchos 29 | 30 | xcode_sonoma: 31 | name: Xcode ${{ matrix.xcode }} (Xcode Project) 32 | runs-on: macos-14 33 | strategy: 34 | matrix: 35 | xcode: ["15.4", "16.1"] 36 | fail-fast: false 37 | env: 38 | DEVELOPER_DIR: "/Applications/Xcode_${{ matrix.xcode }}.app" 39 | steps: 40 | - uses: actions/checkout@v4 41 | - run: ./test macos 42 | - run: ./test ios 43 | - run: ./test tvos 44 | - run: ./test watchos 45 | 46 | xcode_spm: 47 | name: Xcode ${{ matrix.xcode }} (Swift Package) 48 | runs-on: macos-14 49 | strategy: 50 | matrix: 51 | xcode: ["16.1"] 52 | fail-fast: false 53 | env: 54 | DEVELOPER_DIR: "/Applications/Xcode_${{ matrix.xcode }}.app" 55 | steps: 56 | - uses: actions/checkout@v4 57 | - run: ./test macos_xcodespm 58 | - run: ./test ios_xcodespm 59 | - run: ./test tvos_xcodespm 60 | - run: ./test watchos_xcodespm 61 | -------------------------------------------------------------------------------- /.github/workflows/cocoapods.yml: -------------------------------------------------------------------------------- 1 | name: CocoaPods 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | tags: 8 | - "*" 9 | pull_request: 10 | branches: 11 | - "*" 12 | 13 | jobs: 14 | cocoapods: 15 | name: CocoaPods Lint 16 | runs-on: macos-14 17 | steps: 18 | - uses: actions/checkout@v4 19 | - uses: ruby/setup-ruby@v1 20 | with: 21 | ruby-version: 3.2 22 | bundler-cache: true 23 | - run: ./test podspec 24 | -------------------------------------------------------------------------------- /.github/workflows/documentation.yml: -------------------------------------------------------------------------------- 1 | name: Build Documentation 2 | on: 3 | push: 4 | branches: 5 | - main 6 | tags: 7 | - "*" 8 | pull_request: 9 | branches: 10 | - "*" 11 | 12 | permissions: 13 | contents: write 14 | 15 | jobs: 16 | build-documentation: 17 | runs-on: ubuntu-latest 18 | container: swift:latest 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@v4 22 | 23 | - name: Build Docs 24 | run: | 25 | ./script/build_docs 26 | 27 | - name: Deploy Docs 28 | if: github.ref == 'refs/heads/main' 29 | uses: JamesIves/github-pages-deploy-action@v4 30 | with: 31 | folder: docs 32 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: "Release Artifacts" 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | carthage_archive: 9 | name: Darwin, Xcode 14.0 10 | runs-on: macos-14 11 | strategy: 12 | matrix: 13 | xcode: ["16.1"] 14 | env: 15 | DEVELOPER_DIR: "/Applications/Xcode_${{ matrix.xcode }}.app" 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v4 19 | - name: Archive Nimble 20 | run: | 21 | ./test carthage 22 | zip -r Nimble.xcframework.zip Carthage/Build/Nimble.xcframework 23 | - name: Upload Nimble.xcframework.zip 24 | uses: softprops/action-gh-release@v2 25 | with: 26 | files: | 27 | Nimble.xcframework.zip 28 | -------------------------------------------------------------------------------- /.github/workflows/swiftlint.yml: -------------------------------------------------------------------------------- 1 | name: SwiftLint 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - '.github/workflows/swiftlint.yml' 7 | - '.swiftlint.yml' 8 | - '**/*.swift' 9 | 10 | jobs: 11 | lint: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | - uses: norio-nomura/action-swiftlint@3.2.1 16 | -------------------------------------------------------------------------------- /.github/workflows/wasm.yml: -------------------------------------------------------------------------------- 1 | name: SwiftWasm 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - .github/workflows/wasm.yml 7 | schedule: 8 | - cron: "0 0 * * 1" 9 | workflow_dispatch: 10 | 11 | jobs: 12 | test: 13 | name: Test SwiftWasm 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | - uses: swiftwasm/swiftwasm-action@v5.8 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | **/xcuserdata/* 3 | **/*.xccheckout 4 | **/*.xcscmblueprint 5 | build/ 6 | .idea 7 | DerivedData/ 8 | Nimble.framework.zip 9 | Nimble.xcframework.zip 10 | 11 | # Carthage 12 | # 13 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 14 | # Carthage/Checkouts 15 | Carthage 16 | 17 | # Swift Package Manager 18 | # 19 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 20 | # Packages/ 21 | .build/ 22 | -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | included: 2 | - Sources 3 | - Tests 4 | 5 | excluded: 6 | - Package.swift 7 | - Carthage/Checkouts 8 | - Tests/NimbleTests/XCTestManifests.swift 9 | - Tests/NimbleTests/Helpers/XCTestCaseProvider.swift 10 | 11 | disabled_rules: 12 | - 13 | 14 | opt_in_rules: 15 | - yoda_condition 16 | 17 | identifier_name: 18 | max_length: 50 19 | excluded: 20 | - x 21 | - l 22 | - r 23 | - e 24 | - n1 25 | - n2 26 | - to 27 | allowed_symbols: 28 | - _ 29 | 30 | line_length: 31 | warning: 160 32 | error: 240 33 | ignores_comments: true 34 | ignores_function_declarations: true 35 | 36 | trailing_comma: 37 | mandatory_comma: true 38 | -------------------------------------------------------------------------------- /Cartfile.resolved: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Quick/Nimble/28970fa1555daf50cae3a94968dc4601558310ab/Cartfile.resolved -------------------------------------------------------------------------------- /Dockerfile.test: -------------------------------------------------------------------------------- 1 | FROM swift:latest 2 | COPY . . 3 | CMD ./test swiftpm 4 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # A sample Gemfile 2 | source "https://rubygems.org" 3 | 4 | gem 'cocoapods', '~> 1.16' 5 | gem 'jazzy', '~> 0.15' 6 | -------------------------------------------------------------------------------- /Nimble.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "Nimble" 3 | s.version = "13.7.1" 4 | s.summary = "A Matcher Framework for Swift and Objective-C" 5 | s.description = <<-DESC 6 | Use Nimble to express the expected outcomes of Swift or Objective-C expressions. Inspired by Cedar. 7 | DESC 8 | s.homepage = "https://github.com/Quick/Nimble" 9 | s.license = { :type => "Apache 2.0", :file => "LICENSE" } 10 | s.author = "Quick Contributors" 11 | s.ios.deployment_target = "13.0" 12 | s.osx.deployment_target = "10.15" 13 | s.tvos.deployment_target = "13.0" 14 | s.watchos.deployment_target = "7.0" 15 | s.visionos.deployment_target = "1.0" 16 | s.source = { :git => "https://github.com/Quick/Nimble.git", 17 | :tag => "v#{s.version}" } 18 | 19 | s.source_files = [ 20 | "Sources/**/*.{swift,h,m,c}", 21 | ] 22 | 23 | s.header_dir = "Nimble" 24 | 25 | s.exclude_files = "Sources/Nimble/Adapters/NonObjectiveC/*.swift" 26 | s.weak_framework = "XCTest" 27 | s.requires_arc = true 28 | s.compiler_flags = '-DPRODUCT_NAME=Nimble/Nimble' 29 | s.pod_target_xcconfig = { 30 | 'APPLICATION_EXTENSION_API_ONLY' => 'YES', 31 | 'DEFINES_MODULE' => 'YES', 32 | 'ENABLE_BITCODE' => 'NO', 33 | 'ENABLE_TESTING_SEARCH_PATHS' => 'YES', 34 | 'OTHER_LDFLAGS' => '$(inherited) -weak-lXCTestSwiftSupport -Xlinker -no_application_extension', 35 | 'OTHER_SWIFT_FLAGS' => '$(inherited) -suppress-warnings', 36 | } 37 | 38 | [s.osx, s.ios, s.visionos].each do |platform| 39 | platform.dependency 'CwlPreconditionTesting', '~> 2.2.0' 40 | end 41 | 42 | s.cocoapods_version = '>= 1.4.0' 43 | if s.respond_to?(:swift_versions) then 44 | s.swift_versions = ['5.0'] 45 | else 46 | s.swift_version = '5.0' 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /Nimble.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Nimble.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Nimble.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildSystemType 6 | Latest 7 | 8 | 9 | -------------------------------------------------------------------------------- /Nimble.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "pins" : [ 3 | { 4 | "identity" : "cwlcatchexception", 5 | "kind" : "remoteSourceControl", 6 | "location" : "https://github.com/mattgallagher/CwlCatchException", 7 | "state" : { 8 | "revision" : "3b123999de19bf04905bc1dfdb76f817b0f2cc00", 9 | "version" : "2.1.2" 10 | } 11 | }, 12 | { 13 | "identity" : "cwlpreconditiontesting", 14 | "kind" : "remoteSourceControl", 15 | "location" : "https://github.com/mattgallagher/CwlPreconditionTesting", 16 | "state" : { 17 | "revision" : "dc9af4781f2afdd1e68e90f80b8603be73ea7abc", 18 | "version" : "2.2.0" 19 | } 20 | } 21 | ], 22 | "version" : 2 23 | } 24 | -------------------------------------------------------------------------------- /Nimble.xcodeproj/xcshareddata/xcschemes/Nimble.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 34 | 40 | 41 | 42 | 43 | 44 | 55 | 56 | 62 | 63 | 64 | 65 | 71 | 72 | 74 | 75 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "pins" : [ 3 | { 4 | "identity" : "cwlcatchexception", 5 | "kind" : "remoteSourceControl", 6 | "location" : "https://github.com/mattgallagher/CwlCatchException.git", 7 | "state" : { 8 | "revision" : "07b2ba21d361c223e25e3c1e924288742923f08c", 9 | "version" : "2.2.1" 10 | } 11 | }, 12 | { 13 | "identity" : "cwlpreconditiontesting", 14 | "kind" : "remoteSourceControl", 15 | "location" : "https://github.com/mattgallagher/CwlPreconditionTesting.git", 16 | "state" : { 17 | "revision" : "0139c665ebb45e6a9fbdb68aabfd7c39f3fe0071", 18 | "version" : "2.2.2" 19 | } 20 | }, 21 | { 22 | "identity" : "swift-docc-plugin", 23 | "kind" : "remoteSourceControl", 24 | "location" : "https://github.com/apple/swift-docc-plugin", 25 | "state" : { 26 | "revision" : "26ac5758409154cc448d7ab82389c520fa8a8247", 27 | "version" : "1.3.0" 28 | } 29 | }, 30 | { 31 | "identity" : "swift-docc-symbolkit", 32 | "kind" : "remoteSourceControl", 33 | "location" : "https://github.com/apple/swift-docc-symbolkit", 34 | "state" : { 35 | "revision" : "b45d1f2ed151d057b54504d653e0da5552844e34", 36 | "version" : "1.0.0" 37 | } 38 | } 39 | ], 40 | "version" : 2 41 | } 42 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.7 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "Nimble", 6 | platforms: [ 7 | .macOS(.v10_15), .iOS(.v13), .tvOS(.v13), .watchOS(.v6) 8 | ], 9 | products: [ 10 | .library( 11 | name: "Nimble", 12 | targets: { 13 | var targets: [String] = ["Nimble"] 14 | #if os(macOS) 15 | targets.append("NimbleObjectiveC") 16 | #endif 17 | return targets 18 | }() 19 | ), 20 | ], 21 | dependencies: [ 22 | .package(url: "https://github.com/mattgallagher/CwlPreconditionTesting.git", .upToNextMajor(from: "2.1.0")), 23 | .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"), 24 | ], 25 | targets: { 26 | var testHelperDependencies: [PackageDescription.Target.Dependency] = ["Nimble"] 27 | #if os(macOS) 28 | testHelperDependencies.append("NimbleObjectiveC") 29 | #endif 30 | var targets: [Target] = [ 31 | .target( 32 | name: "Nimble", 33 | dependencies: [ 34 | .product(name: "CwlPreconditionTesting", package: "CwlPreconditionTesting", 35 | condition: .when(platforms: [.macOS, .iOS, .macCatalyst])), 36 | .product(name: "CwlPosixPreconditionTesting", package: "CwlPreconditionTesting", 37 | condition: .when(platforms: [.tvOS, .watchOS])) 38 | ], 39 | exclude: ["Info.plist"], 40 | resources: [ 41 | .copy("PrivacyInfo.xcprivacy") 42 | ] 43 | ), 44 | .target( 45 | name: "NimbleSharedTestHelpers", 46 | dependencies: testHelperDependencies 47 | ), 48 | .testTarget( 49 | name: "NimbleTests", 50 | dependencies: ["Nimble", "NimbleSharedTestHelpers"], 51 | exclude: ["Info.plist"] 52 | ), 53 | ] 54 | #if os(macOS) 55 | targets.append(contentsOf: [ 56 | .target( 57 | name: "NimbleObjectiveC", 58 | dependencies: ["Nimble"] 59 | ), 60 | .testTarget( 61 | name: "NimbleObjectiveCTests", 62 | dependencies: ["NimbleObjectiveC", "Nimble", "NimbleSharedTestHelpers"] 63 | ) 64 | ]) 65 | #endif 66 | return targets 67 | }(), 68 | swiftLanguageVersions: [.v5] 69 | ) 70 | -------------------------------------------------------------------------------- /Package@swift-5.9.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.9 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "Nimble", 6 | platforms: [ 7 | .macOS(.v10_15), .iOS(.v13), .tvOS(.v13), .watchOS(.v6), .visionOS(.v1) 8 | ], 9 | products: [ 10 | .library( 11 | name: "Nimble", 12 | targets: { 13 | var targets: [String] = ["Nimble"] 14 | #if os(macOS) 15 | targets.append("NimbleObjectiveC") 16 | #endif 17 | return targets 18 | }() 19 | ), 20 | ], 21 | dependencies: [ 22 | .package(url: "https://github.com/mattgallagher/CwlPreconditionTesting.git", .upToNextMajor(from: "2.2.0")), 23 | .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"), 24 | ], 25 | targets: { 26 | var testHelperDependencies: [PackageDescription.Target.Dependency] = ["Nimble"] 27 | #if os(macOS) 28 | testHelperDependencies.append("NimbleObjectiveC") 29 | #endif 30 | var targets: [Target] = [ 31 | .target( 32 | name: "Nimble", 33 | dependencies: [ 34 | .product(name: "CwlPreconditionTesting", package: "CwlPreconditionTesting", 35 | condition: .when(platforms: [.macOS, .iOS, .macCatalyst, .visionOS])), 36 | .product(name: "CwlPosixPreconditionTesting", package: "CwlPreconditionTesting", 37 | condition: .when(platforms: [.tvOS, .watchOS])) 38 | ], 39 | exclude: ["Info.plist"] 40 | ), 41 | .target( 42 | name: "NimbleSharedTestHelpers", 43 | dependencies: testHelperDependencies 44 | ), 45 | .testTarget( 46 | name: "NimbleTests", 47 | dependencies: ["Nimble", "NimbleSharedTestHelpers"], 48 | exclude: ["Info.plist"] 49 | ), 50 | ] 51 | #if os(macOS) 52 | targets.append(contentsOf: [ 53 | .target( 54 | name: "NimbleObjectiveC", 55 | dependencies: ["Nimble"] 56 | ), 57 | .testTarget( 58 | name: "NimbleObjectiveCTests", 59 | dependencies: ["NimbleObjectiveC", "Nimble", "NimbleSharedTestHelpers"] 60 | ) 61 | ]) 62 | #endif 63 | return targets 64 | }(), 65 | swiftLanguageVersions: [.v5] 66 | ) 67 | -------------------------------------------------------------------------------- /Sources/Nimble/Adapters/AdapterProtocols.swift: -------------------------------------------------------------------------------- 1 | /// Protocol for the assertion handler that Nimble uses for all expectations. 2 | public protocol AssertionHandler { 3 | func assert(_ assertion: Bool, message: FailureMessage, location: SourceLocation) 4 | } 5 | 6 | /// Global backing interface for assertions that Nimble creates. 7 | /// Defaults to a private test handler that passes through to Swift Testing or XCTest. 8 | /// 9 | /// If neither Swift Testing or XCTest is available, you must assign your own assertion handler 10 | /// before using any matchers, otherwise Nimble will abort the program. 11 | /// 12 | /// @see AssertionHandler 13 | public var NimbleAssertionHandler: AssertionHandler = { () -> AssertionHandler in 14 | // swiftlint:disable:previous identifier_name 15 | if isSwiftTestingAvailable() || isXCTestAvailable() { 16 | return NimbleTestingHandler() 17 | } 18 | 19 | return NimbleTestingUnavailableHandler() 20 | }() 21 | -------------------------------------------------------------------------------- /Sources/Nimble/Adapters/AssertionDispatcher.swift: -------------------------------------------------------------------------------- 1 | /// AssertionDispatcher allows multiple AssertionHandlers to receive 2 | /// assertion messages. 3 | /// 4 | /// @warning Does not fully dispatch if one of the handlers raises an exception. 5 | /// This is possible with XCTest-based assertion handlers. 6 | /// 7 | public class AssertionDispatcher: AssertionHandler { 8 | let handlers: [AssertionHandler] 9 | 10 | public init(handlers: [AssertionHandler]) { 11 | self.handlers = handlers 12 | } 13 | 14 | public func assert(_ assertion: Bool, message: FailureMessage, location: SourceLocation) { 15 | for handler in handlers { 16 | handler.assert(assertion, message: message, location: location) 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Sources/Nimble/Adapters/NimbleEnvironment.swift: -------------------------------------------------------------------------------- 1 | #if !os(WASI) 2 | import Dispatch 3 | import class Foundation.Thread 4 | #endif 5 | import class Foundation.NSObject 6 | 7 | /// "Global" state of Nimble is stored here. Only DSL functions should access / be aware of this 8 | /// class' existence 9 | internal class NimbleEnvironment: NSObject { 10 | #if os(WASI) 11 | static var activeInstance: NimbleEnvironment = NimbleEnvironment() 12 | #else 13 | static var activeInstance: NimbleEnvironment { 14 | get { 15 | let env = Thread.current.threadDictionary["NimbleEnvironment"] 16 | if let env = env as? NimbleEnvironment { 17 | return env 18 | } else { 19 | let newEnv = NimbleEnvironment() 20 | self.activeInstance = newEnv 21 | return newEnv 22 | } 23 | } 24 | set { 25 | Thread.current.threadDictionary["NimbleEnvironment"] = newValue 26 | } 27 | } 28 | #endif 29 | 30 | // swiftlint:disable:next todo 31 | // TODO: eventually migrate the global to this environment value 32 | var assertionHandler: AssertionHandler { 33 | get { return NimbleAssertionHandler } 34 | set { NimbleAssertionHandler = newValue } 35 | } 36 | 37 | var suppressTVOSAssertionWarning: Bool = false 38 | var suppressWatchOSAssertionWarning: Bool = false 39 | #if !os(WASI) 40 | var awaiter: Awaiter 41 | #endif 42 | 43 | override init() { 44 | #if !os(WASI) 45 | let timeoutQueue = DispatchQueue.global(qos: .userInitiated) 46 | awaiter = Awaiter( 47 | waitLock: AssertionWaitLock(), 48 | asyncQueue: .main, 49 | timeoutQueue: timeoutQueue 50 | ) 51 | #endif 52 | 53 | super.init() 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Sources/Nimble/Adapters/NimbleSwiftTestingHandler.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | #if canImport(Testing) 3 | // See https://github.com/pointfreeco/swift-snapshot-testing/discussions/901#discussioncomment-10605497 4 | // tl;dr: Swift Testing is not available when using UI tests. 5 | // And apparently `private import` - the preferred way to do this - doesn't work. 6 | // So we use a deprecated approach that does work with this. 7 | @_implementationOnly import Testing 8 | #endif 9 | 10 | public class NimbleSwiftTestingHandler: AssertionHandler { 11 | public func assert(_ assertion: Bool, message: FailureMessage, location: SourceLocation) { 12 | if !assertion { 13 | recordTestingFailure("\(message.stringValue)\n", location: location) 14 | } 15 | } 16 | } 17 | 18 | func isSwiftTestingAvailable() -> Bool { 19 | #if canImport(Testing) 20 | true 21 | #else 22 | false 23 | #endif 24 | } 25 | 26 | func isRunningSwiftTest() -> Bool { 27 | #if canImport(Testing) 28 | Test.current != nil 29 | #else 30 | false 31 | #endif 32 | } 33 | 34 | public func recordTestingFailure(_ message: String, location: SourceLocation) { 35 | #if canImport(Testing) 36 | let testingLocation = Testing.SourceLocation( 37 | fileID: location.fileID, 38 | filePath: "\(location.filePath)", 39 | line: Int(location.line), 40 | column: Int(location.column) 41 | ) 42 | 43 | Testing.Issue.record( 44 | "\(message)", 45 | sourceLocation: testingLocation 46 | ) 47 | #endif 48 | } 49 | -------------------------------------------------------------------------------- /Sources/Nimble/Adapters/NonObjectiveC/ExceptionCapture.swift: -------------------------------------------------------------------------------- 1 | #if canImport(Darwin) 2 | import class Foundation.NSException 3 | #else 4 | // swift-corelibs-foundation doesn't provide NSException at all, so provide a dummy 5 | class NSException {} 6 | #endif 7 | 8 | // NOTE: This file is not intended to be included in the Xcode project. It 9 | // is picked up by the Swift Package Manager during its build process. 10 | 11 | /// A dummy reimplementation of the `NMBExceptionCapture` class to serve 12 | /// as a stand-in for build and runtime environments that don't support 13 | /// Objective C. 14 | internal class ExceptionCapture { 15 | let finally: (() -> Void)? 16 | 17 | init(handler: ((NSException) -> Void)?, finally: (() -> Void)?) { 18 | self.finally = finally 19 | } 20 | 21 | func tryBlock(_ unsafeBlock: (() -> Void)) { 22 | // We have no way of handling Objective C exceptions in Swift, 23 | // so we just go ahead and run the unsafeBlock as-is 24 | unsafeBlock() 25 | 26 | finally?() 27 | } 28 | } 29 | 30 | /// Compatibility with the actual Objective-C implementation 31 | typealias NMBExceptionCapture = ExceptionCapture 32 | -------------------------------------------------------------------------------- /Sources/Nimble/FailureMessage.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /// Encapsulates the failure message that matchers can report to the end user. 4 | /// 5 | /// This is shared state between Nimble and matchers that mutate this value. 6 | public class FailureMessage: NSObject { 7 | public var expected: String = "expected" 8 | public var actualValue: String? = "" // empty string -> use default; nil -> exclude 9 | public var to: String = "to" 10 | public var postfixMessage: String = "match" 11 | public var postfixActual: String = "" 12 | /// An optional message that will be appended as a new line and provides additional details 13 | /// about the failure. This message will only be visible in the issue navigator / in logs but 14 | /// not directly in the source editor since only a single line is presented there. 15 | public var extendedMessage: String? 16 | public var userDescription: String? 17 | 18 | public var stringValue: String { 19 | get { 20 | if let value = _stringValueOverride { 21 | return value 22 | } else { 23 | return computeStringValue() 24 | } 25 | } 26 | set { 27 | _stringValueOverride = newValue 28 | } 29 | } 30 | 31 | internal var _stringValueOverride: String? 32 | internal var hasOverriddenStringValue: Bool { 33 | return _stringValueOverride != nil 34 | } 35 | 36 | public override init() { 37 | } 38 | 39 | public init(stringValue: String) { 40 | _stringValueOverride = stringValue 41 | } 42 | 43 | internal func stripNewlines(_ str: String) -> String { 44 | let whitespaces = CharacterSet.whitespacesAndNewlines 45 | return str 46 | .components(separatedBy: "\n") 47 | .map { line in line.trimmingCharacters(in: whitespaces) } 48 | .joined(separator: "") 49 | } 50 | 51 | internal func computeStringValue() -> String { 52 | var value = "\(expected) \(to) \(postfixMessage)" 53 | if let actualValue = actualValue { 54 | value = "\(expected) \(to) \(postfixMessage), got \(actualValue)\(postfixActual)" 55 | } 56 | value = stripNewlines(value) 57 | 58 | if let extendedMessage = extendedMessage { 59 | value += "\n\(extendedMessage)" 60 | } 61 | 62 | if let userDescription = userDescription { 63 | return "\(userDescription)\n\(value)" 64 | } 65 | 66 | return value 67 | } 68 | 69 | internal func appendMessage(_ msg: String) { 70 | if hasOverriddenStringValue { 71 | stringValue += "\(msg)" 72 | } else if actualValue != nil { 73 | postfixActual += msg 74 | } else { 75 | postfixMessage += msg 76 | } 77 | } 78 | 79 | internal func appendDetails(_ msg: String) { 80 | if hasOverriddenStringValue { 81 | if let desc = userDescription { 82 | stringValue = "\(desc)\n\(stringValue)" 83 | } 84 | stringValue += "\n\(msg)" 85 | } else { 86 | if let desc = userDescription { 87 | userDescription = desc 88 | } 89 | extendedMessage = msg 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Sources/Nimble/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSHumanReadableCopyright 24 | Copyright © 2014 Jeff Hui. All rights reserved. 25 | NSPrincipalClass 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Sources/Nimble/Matchers/AsyncAllPass.swift: -------------------------------------------------------------------------------- 1 | public func allPass( 2 | _ passFunc: @escaping (S.Element) async throws -> Bool 3 | ) -> AsyncMatcher { 4 | let matcher = AsyncMatcher.define("pass a condition") { actualExpression, message in 5 | guard let actual = try await actualExpression.evaluate() else { 6 | return MatcherResult(status: .fail, message: message) 7 | } 8 | return MatcherResult(bool: try await passFunc(actual), message: message) 9 | } 10 | return createMatcher(matcher) 11 | } 12 | 13 | public func allPass( 14 | _ passName: String, 15 | _ passFunc: @escaping (S.Element) async throws -> Bool 16 | ) -> AsyncMatcher { 17 | let matcher = AsyncMatcher.define(passName) { actualExpression, message in 18 | guard let actual = try await actualExpression.evaluate() else { 19 | return MatcherResult(status: .fail, message: message) 20 | } 21 | return MatcherResult(bool: try await passFunc(actual), message: message) 22 | } 23 | return createMatcher(matcher) 24 | } 25 | 26 | public func allPass(_ elementMatcher: AsyncMatcher) -> AsyncMatcher { 27 | return createMatcher(elementMatcher) 28 | } 29 | 30 | private func createMatcher(_ elementMatcher: AsyncMatcher) -> AsyncMatcher { 31 | return AsyncMatcher { actualExpression in 32 | guard let actualValue = try await actualExpression.evaluate() else { 33 | return MatcherResult( 34 | status: .fail, 35 | message: .appends(.expectedTo("all pass"), " (use beNil() to match nils)") 36 | ) 37 | } 38 | 39 | var failure: ExpectationMessage = .expectedTo("all pass") 40 | for currentElement in actualValue { 41 | let exp = AsyncExpression( 42 | expression: { currentElement }, 43 | location: actualExpression.location 44 | ) 45 | let matcherResult = try await elementMatcher.satisfies(exp) 46 | if matcherResult.status == .matches { 47 | failure = matcherResult.message.prepended(expectation: "all ") 48 | } else { 49 | failure = matcherResult.message 50 | .replacedExpectation({ .expectedTo($0.expectedMessage) }) 51 | .wrappedExpectation( 52 | before: "all ", 53 | after: ", but failed first at element <\(stringify(currentElement))>" 54 | + " in <\(stringify(actualValue))>" 55 | ) 56 | return MatcherResult(status: .doesNotMatch, message: failure) 57 | } 58 | } 59 | failure = failure.replacedExpectation({ expectation in 60 | return .expectedTo(expectation.expectedMessage) 61 | }) 62 | return MatcherResult(status: .matches, message: failure) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Sources/Nimble/Matchers/BeAKindOf.swift: -------------------------------------------------------------------------------- 1 | private func matcherMessage(forType expectedType: T.Type) -> String { 2 | return "be a kind of \(String(describing: expectedType))" 3 | } 4 | private func matcherMessage(forClass expectedClass: AnyClass) -> String { 5 | return "be a kind of \(String(describing: expectedClass))" 6 | } 7 | 8 | /// A Nimble matcher that succeeds when the actual value is an instance of the given class. 9 | public func beAKindOf(_ expectedType: T.Type) -> Matcher { 10 | return Matcher.define { actualExpression in 11 | let message: ExpectationMessage 12 | 13 | let instance = try actualExpression.evaluate() 14 | guard let validInstance = instance else { 15 | message = .expectedCustomValueTo(matcherMessage(forType: expectedType), actual: "") 16 | return MatcherResult(status: .fail, message: message) 17 | } 18 | message = .expectedCustomValueTo( 19 | "be a kind of \(String(describing: expectedType))", 20 | actual: "<\(String(describing: type(of: validInstance))) instance>" 21 | ) 22 | 23 | return MatcherResult( 24 | bool: validInstance is T, 25 | message: message 26 | ) 27 | } 28 | } 29 | 30 | #if canImport(Darwin) 31 | import class Foundation.NSObject 32 | 33 | /// A Nimble matcher that succeeds when the actual value is an instance of the given class. 34 | /// @see beAnInstanceOf if you want to match against the exact class 35 | public func beAKindOf(_ expectedClass: AnyClass) -> Matcher { 36 | return Matcher.define { actualExpression in 37 | let message: ExpectationMessage 38 | let status: MatcherStatus 39 | 40 | let instance = try actualExpression.evaluate() 41 | if let validInstance = instance { 42 | status = MatcherStatus(bool: instance != nil && instance!.isKind(of: expectedClass)) 43 | message = .expectedCustomValueTo( 44 | matcherMessage(forClass: expectedClass), 45 | actual: "<\(String(describing: type(of: validInstance))) instance>" 46 | ) 47 | } else { 48 | status = .fail 49 | message = .expectedCustomValueTo( 50 | matcherMessage(forClass: expectedClass), 51 | actual: "" 52 | ) 53 | } 54 | 55 | return MatcherResult(status: status, message: message) 56 | } 57 | } 58 | 59 | extension NMBMatcher { 60 | @objc public class func beAKindOfMatcher(_ expected: AnyClass) -> NMBMatcher { 61 | return NMBMatcher { actualExpression in 62 | return try beAKindOf(expected).satisfies(actualExpression).toObjectiveC() 63 | } 64 | } 65 | } 66 | 67 | #endif 68 | -------------------------------------------------------------------------------- /Sources/Nimble/Matchers/BeAnInstanceOf.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /// A Nimble matcher that succeeds when the actual value is an _exact_ instance of the given class. 4 | public func beAnInstanceOf(_ expectedType: T.Type) -> Matcher { 5 | let errorMessage = "be an instance of \(String(describing: expectedType))" 6 | return Matcher.define { actualExpression in 7 | let instance = try actualExpression.evaluate() 8 | guard let validInstance: Any = instance else { 9 | return MatcherResult( 10 | status: .doesNotMatch, 11 | message: .expectedActualValueTo(errorMessage) 12 | ) 13 | } 14 | 15 | let actualString = "<\(String(describing: type(of: validInstance))) instance>" 16 | 17 | return MatcherResult( 18 | status: MatcherStatus(bool: type(of: validInstance) == expectedType), 19 | message: .expectedCustomValueTo(errorMessage, actual: actualString) 20 | ) 21 | } 22 | } 23 | 24 | /// A Nimble matcher that succeeds when the actual value is an instance of the given class. 25 | /// @see beAKindOf if you want to match against subclasses 26 | public func beAnInstanceOf(_ expectedClass: AnyClass) -> Matcher { 27 | let errorMessage = "be an instance of \(String(describing: expectedClass))" 28 | return Matcher.define { actualExpression in 29 | let instance = try actualExpression.evaluate() 30 | let actualString: String 31 | if let validInstance = instance { 32 | actualString = "<\(String(describing: type(of: validInstance))) instance>" 33 | } else { 34 | actualString = "" 35 | } 36 | #if canImport(Darwin) 37 | let matches = instance != nil && instance!.isMember(of: expectedClass) 38 | #else 39 | let matches = instance != nil && type(of: instance!) == expectedClass 40 | #endif 41 | return MatcherResult( 42 | status: MatcherStatus(bool: matches), 43 | message: .expectedCustomValueTo(errorMessage, actual: actualString) 44 | ) 45 | } 46 | } 47 | 48 | #if canImport(Darwin) 49 | extension NMBMatcher { 50 | @objc public class func beAnInstanceOfMatcher(_ expected: AnyClass) -> NMBMatcher { 51 | return NMBMatcher { actualExpression in 52 | return try beAnInstanceOf(expected).satisfies(actualExpression).toObjectiveC() 53 | } 54 | } 55 | } 56 | #endif 57 | -------------------------------------------------------------------------------- /Sources/Nimble/Matchers/BeGreaterThan.swift: -------------------------------------------------------------------------------- 1 | /// A Nimble matcher that succeeds when the actual value is greater than the expected value. 2 | public func beGreaterThan(_ expectedValue: T?) -> Matcher { 3 | let errorMessage = "be greater than <\(stringify(expectedValue))>" 4 | return Matcher.simple(errorMessage) { actualExpression in 5 | guard let actual = try actualExpression.evaluate(), let expected = expectedValue else { return .fail } 6 | 7 | return MatcherStatus(bool: actual > expected) 8 | } 9 | } 10 | 11 | public func > (lhs: SyncExpectation, rhs: T) { 12 | lhs.to(beGreaterThan(rhs)) 13 | } 14 | 15 | public func > (lhs: AsyncExpectation, rhs: T) async { 16 | await lhs.to(beGreaterThan(rhs)) 17 | } 18 | 19 | #if canImport(Darwin) 20 | import enum Foundation.ComparisonResult 21 | 22 | /// A Nimble matcher that succeeds when the actual value is greater than the expected value. 23 | public func beGreaterThan(_ expectedValue: T?) -> Matcher { 24 | let errorMessage = "be greater than <\(stringify(expectedValue))>" 25 | return Matcher.simple(errorMessage) { actualExpression in 26 | let actualValue = try actualExpression.evaluate() 27 | let matches = actualValue != nil 28 | && actualValue!.NMB_compare(expectedValue) == ComparisonResult.orderedDescending 29 | return MatcherStatus(bool: matches) 30 | } 31 | } 32 | 33 | public func > (lhs: SyncExpectation, rhs: T?) { 34 | lhs.to(beGreaterThan(rhs)) 35 | } 36 | 37 | public func > (lhs: AsyncExpectation, rhs: T?) async { 38 | await lhs.to(beGreaterThan(rhs)) 39 | } 40 | 41 | extension NMBMatcher { 42 | @objc public class func beGreaterThanMatcher(_ expected: NMBComparable?) -> NMBMatcher { 43 | return NMBMatcher { actualExpression in 44 | let expr = actualExpression.cast { $0 as? NMBComparable } 45 | return try beGreaterThan(expected).satisfies(expr).toObjectiveC() 46 | } 47 | } 48 | } 49 | #endif 50 | -------------------------------------------------------------------------------- /Sources/Nimble/Matchers/BeGreaterThanOrEqualTo.swift: -------------------------------------------------------------------------------- 1 | /// A Nimble matcher that succeeds when the actual value is greater than 2 | /// or equal to the expected value. 3 | public func beGreaterThanOrEqualTo(_ expectedValue: T?) -> Matcher { 4 | let message = "be greater than or equal to <\(stringify(expectedValue))>" 5 | return Matcher.simple(message) { actualExpression in 6 | guard let actual = try actualExpression.evaluate(), let expected = expectedValue else { return .fail } 7 | 8 | return MatcherStatus(bool: actual >= expected) 9 | } 10 | } 11 | 12 | public func >= (lhs: SyncExpectation, rhs: T) { 13 | lhs.to(beGreaterThanOrEqualTo(rhs)) 14 | } 15 | 16 | public func >= (lhs: AsyncExpectation, rhs: T) async { 17 | await lhs.to(beGreaterThanOrEqualTo(rhs)) 18 | } 19 | 20 | #if canImport(Darwin) 21 | import enum Foundation.ComparisonResult 22 | 23 | /// A Nimble matcher that succeeds when the actual value is greater than 24 | /// or equal to the expected value. 25 | public func beGreaterThanOrEqualTo(_ expectedValue: T?) -> Matcher { 26 | let message = "be greater than or equal to <\(stringify(expectedValue))>" 27 | return Matcher.simple(message) { actualExpression in 28 | let actualValue = try actualExpression.evaluate() 29 | let matches = actualValue != nil && actualValue!.NMB_compare(expectedValue) != ComparisonResult.orderedAscending 30 | return MatcherStatus(bool: matches) 31 | } 32 | } 33 | 34 | public func >= (lhs: SyncExpectation, rhs: T) { 35 | lhs.to(beGreaterThanOrEqualTo(rhs)) 36 | } 37 | 38 | public func >= (lhs: AsyncExpectation, rhs: T) async { 39 | await lhs.to(beGreaterThanOrEqualTo(rhs)) 40 | } 41 | 42 | extension NMBMatcher { 43 | @objc public class func beGreaterThanOrEqualToMatcher(_ expected: NMBComparable?) -> NMBMatcher { 44 | return NMBMatcher { actualExpression in 45 | let expr = actualExpression.cast { $0 as? NMBComparable } 46 | return try beGreaterThanOrEqualTo(expected).satisfies(expr).toObjectiveC() 47 | } 48 | } 49 | } 50 | #endif 51 | -------------------------------------------------------------------------------- /Sources/Nimble/Matchers/BeIdenticalTo.swift: -------------------------------------------------------------------------------- 1 | /// A Nimble matcher that succeeds when the actual value is the same instance 2 | /// as the expected instance. 3 | public func beIdenticalTo(_ expected: T?) -> Matcher { 4 | _beIdenticalTo(expected) 5 | } 6 | 7 | /// A Nimble matcher that succeeds when the actual value is the same instance 8 | /// as the expected instance. 9 | public func beIdenticalTo(_ expected: AnyObject?) -> Matcher { 10 | _beIdenticalTo(expected) 11 | } 12 | 13 | private func _beIdenticalTo(_ expected: T?) -> Matcher { 14 | return Matcher.define { actualExpression in 15 | let actual = try actualExpression.evaluate() 16 | 17 | let bool = actual === expected && actual !== nil 18 | return MatcherResult( 19 | bool: bool, 20 | message: .expectedCustomValueTo( 21 | "be identical to \(identityAsString(expected))", 22 | actual: "\(identityAsString(actual))" 23 | ) 24 | ) 25 | } 26 | } 27 | 28 | public func === (lhs: SyncExpectation, rhs: AnyObject?) { 29 | lhs.to(beIdenticalTo(rhs)) 30 | } 31 | 32 | public func === (lhs: AsyncExpectation, rhs: AnyObject?) async { 33 | await lhs.to(beIdenticalTo(rhs)) 34 | } 35 | 36 | public func !== (lhs: SyncExpectation, rhs: AnyObject?) { 37 | lhs.toNot(beIdenticalTo(rhs)) 38 | } 39 | 40 | public func !== (lhs: AsyncExpectation, rhs: AnyObject?) async { 41 | await lhs.toNot(beIdenticalTo(rhs)) 42 | } 43 | 44 | /// A Nimble matcher that succeeds when the actual value is the same instance 45 | /// as the expected instance. 46 | /// 47 | /// Alias for "beIdenticalTo". 48 | public func be(_ expected: T?) -> Matcher { 49 | return _beIdenticalTo(expected) 50 | } 51 | 52 | /// A Nimble matcher that succeeds when the actual value is the same instance 53 | /// as the expected instance. 54 | /// 55 | /// Alias for "beIdenticalTo". 56 | public func be(_ expected: AnyObject?) -> Matcher { 57 | return _beIdenticalTo(expected) 58 | } 59 | 60 | #if canImport(Darwin) 61 | import class Foundation.NSObject 62 | 63 | extension NMBMatcher { 64 | @objc public class func beIdenticalToMatcher(_ expected: NSObject?) -> NMBMatcher { 65 | return NMBMatcher { actualExpression in 66 | let aExpr = actualExpression.cast { $0 as AnyObject? } 67 | return try beIdenticalTo(expected).satisfies(aExpr).toObjectiveC() 68 | } 69 | } 70 | } 71 | #endif 72 | -------------------------------------------------------------------------------- /Sources/Nimble/Matchers/BeLessThan.swift: -------------------------------------------------------------------------------- 1 | /// A Nimble matcher that succeeds when the actual value is less than the expected value. 2 | public func beLessThan(_ expectedValue: T?) -> Matcher { 3 | let message = "be less than <\(stringify(expectedValue))>" 4 | return Matcher.simple(message) { actualExpression in 5 | guard let actual = try actualExpression.evaluate(), let expected = expectedValue else { return .fail } 6 | 7 | return MatcherStatus(bool: actual < expected) 8 | } 9 | } 10 | 11 | public func < (lhs: SyncExpectation, rhs: V) { 12 | lhs.to(beLessThan(rhs)) 13 | } 14 | 15 | public func < (lhs: AsyncExpectation, rhs: V) async { 16 | await lhs.to(beLessThan(rhs)) 17 | } 18 | 19 | #if canImport(Darwin) 20 | import enum Foundation.ComparisonResult 21 | 22 | /// A Nimble matcher that succeeds when the actual value is less than the expected value. 23 | public func beLessThan(_ expectedValue: T?) -> Matcher { 24 | let message = "be less than <\(stringify(expectedValue))>" 25 | return Matcher.simple(message) { actualExpression in 26 | let actualValue = try actualExpression.evaluate() 27 | let matches = actualValue != nil && actualValue!.NMB_compare(expectedValue) == ComparisonResult.orderedAscending 28 | return MatcherStatus(bool: matches) 29 | } 30 | } 31 | 32 | public func < (lhs: SyncExpectation, rhs: V?) { 33 | lhs.to(beLessThan(rhs)) 34 | } 35 | 36 | public func < (lhs: AsyncExpectation, rhs: V?) async { 37 | await lhs.to(beLessThan(rhs)) 38 | } 39 | 40 | extension NMBMatcher { 41 | @objc public class func beLessThanMatcher(_ expected: NMBComparable?) -> NMBMatcher { 42 | return NMBMatcher { actualExpression in 43 | let expr = actualExpression.cast { $0 as? NMBComparable } 44 | return try beLessThan(expected).satisfies(expr).toObjectiveC() 45 | } 46 | } 47 | } 48 | #endif 49 | -------------------------------------------------------------------------------- /Sources/Nimble/Matchers/BeLessThanOrEqual.swift: -------------------------------------------------------------------------------- 1 | /// A Nimble matcher that succeeds when the actual value is less than 2 | /// or equal to the expected value. 3 | public func beLessThanOrEqualTo(_ expectedValue: T?) -> Matcher { 4 | return Matcher.simple("be less than or equal to <\(stringify(expectedValue))>") { actualExpression in 5 | guard let actual = try actualExpression.evaluate(), let expected = expectedValue else { return .fail } 6 | 7 | return MatcherStatus(bool: actual <= expected) 8 | } 9 | } 10 | 11 | public func <= (lhs: SyncExpectation, rhs: T) { 12 | lhs.to(beLessThanOrEqualTo(rhs)) 13 | } 14 | 15 | public func <= (lhs: AsyncExpectation, rhs: T) async { 16 | await lhs.to(beLessThanOrEqualTo(rhs)) 17 | } 18 | 19 | #if canImport(Darwin) 20 | import enum Foundation.ComparisonResult 21 | 22 | /// A Nimble matcher that succeeds when the actual value is less than 23 | /// or equal to the expected value. 24 | public func beLessThanOrEqualTo(_ expectedValue: T?) -> Matcher { 25 | return Matcher.simple("be less than or equal to <\(stringify(expectedValue))>") { actualExpression in 26 | let actualValue = try actualExpression.evaluate() 27 | let matches = actualValue.map { $0.NMB_compare(expectedValue) != .orderedDescending } ?? false 28 | return MatcherStatus(bool: matches) 29 | } 30 | } 31 | 32 | public func <= (lhs: SyncExpectation, rhs: T) { 33 | lhs.to(beLessThanOrEqualTo(rhs)) 34 | } 35 | 36 | public func <= (lhs: AsyncExpectation, rhs: T) async { 37 | await lhs.to(beLessThanOrEqualTo(rhs)) 38 | } 39 | 40 | extension NMBMatcher { 41 | @objc public class func beLessThanOrEqualToMatcher(_ expected: NMBComparable?) -> NMBMatcher { 42 | return NMBMatcher { actualExpression in 43 | let expr = actualExpression.cast { $0 as? NMBComparable } 44 | return try beLessThanOrEqualTo(expected).satisfies(expr).toObjectiveC() 45 | } 46 | } 47 | } 48 | #endif 49 | -------------------------------------------------------------------------------- /Sources/Nimble/Matchers/BeNil.swift: -------------------------------------------------------------------------------- 1 | /// A protocol which represents whether the value is nil or not. 2 | private protocol _OptionalProtocol { 3 | var isNil: Bool { get } 4 | } 5 | 6 | extension Optional: _OptionalProtocol { 7 | var isNil: Bool { self == nil } 8 | } 9 | 10 | /// A Nimble matcher that succeeds when the actual value is nil. 11 | public func beNil() -> Matcher { 12 | return Matcher.simpleNilable("be nil") { actualExpression in 13 | let actualValue = try actualExpression.evaluate() 14 | if let actual = actualValue, let nestedOptionl = actual as? _OptionalProtocol { 15 | return MatcherStatus(bool: nestedOptionl.isNil) 16 | } 17 | return MatcherStatus(bool: actualValue == nil) 18 | } 19 | } 20 | 21 | /// Represents `nil` value to be used with the operator overloads for `beNil`. 22 | public struct ExpectationNil: ExpressibleByNilLiteral { 23 | public init(nilLiteral: ()) {} 24 | } 25 | 26 | extension SyncExpectation { 27 | public static func == (lhs: SyncExpectation, rhs: ExpectationNil) { 28 | lhs.to(beNil()) 29 | } 30 | 31 | public static func != (lhs: SyncExpectation, rhs: ExpectationNil) { 32 | lhs.toNot(beNil()) 33 | } 34 | } 35 | 36 | extension AsyncExpectation { 37 | public static func == (lhs: AsyncExpectation, rhs: ExpectationNil) async { 38 | await lhs.to(beNil()) 39 | } 40 | 41 | public static func != (lhs: AsyncExpectation, rhs: ExpectationNil) async { 42 | await lhs.toNot(beNil()) 43 | } 44 | } 45 | 46 | #if canImport(Darwin) 47 | import Foundation 48 | 49 | extension NMBMatcher { 50 | @objc public class func beNilMatcher() -> NMBMatcher { 51 | return NMBMatcher { actualExpression in 52 | return try beNil().satisfies(actualExpression).toObjectiveC() 53 | } 54 | } 55 | } 56 | #endif 57 | -------------------------------------------------------------------------------- /Sources/Nimble/Matchers/BeVoid.swift: -------------------------------------------------------------------------------- 1 | /// A Nimble matcher that succeeds when the actual value is Void. 2 | public func beVoid() -> Matcher<()> { 3 | return Matcher.simpleNilable("be void") { actualExpression in 4 | let actualValue: ()? = try actualExpression.evaluate() 5 | return MatcherStatus(bool: actualValue != nil) 6 | } 7 | } 8 | 9 | public func == (lhs: SyncExpectation<()>, rhs: ()) { 10 | lhs.to(beVoid()) 11 | } 12 | 13 | public func == (lhs: AsyncExpectation<()>, rhs: ()) async { 14 | await lhs.to(beVoid()) 15 | } 16 | 17 | public func != (lhs: SyncExpectation<()>, rhs: ()) { 18 | lhs.toNot(beVoid()) 19 | } 20 | 21 | public func != (lhs: AsyncExpectation<()>, rhs: ()) async { 22 | await lhs.toNot(beVoid()) 23 | } 24 | -------------------------------------------------------------------------------- /Sources/Nimble/Matchers/BeWithin.swift: -------------------------------------------------------------------------------- 1 | /// A Nimble matcher that succeeds when the actual value is within given range. 2 | public func beWithin(_ range: Range) -> Matcher { 3 | let errorMessage = "be within range <(\(range.lowerBound)..<\(range.upperBound))>" 4 | return Matcher.simple(errorMessage) { actualExpression in 5 | if let actual = try actualExpression.evaluate() { 6 | return MatcherStatus(bool: range.contains(actual)) 7 | } 8 | return .fail 9 | } 10 | } 11 | 12 | /// A Nimble matcher that succeeds when the actual value is within given range. 13 | public func beWithin(_ range: ClosedRange) -> Matcher { 14 | let errorMessage = "be within range <(\(range.lowerBound)...\(range.upperBound))>" 15 | return Matcher.simple(errorMessage) { actualExpression in 16 | if let actual = try actualExpression.evaluate() { 17 | return MatcherStatus(bool: range.contains(actual)) 18 | } 19 | return .fail 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Nimble/Matchers/BeginWith.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /// A Nimble matcher that succeeds when the actual sequence's first element 4 | /// is equal to the expected value. 5 | public func beginWith(_ startingElement: S.Element) -> Matcher where S.Element: Equatable { 6 | return Matcher.simple("begin with <\(startingElement)>") { actualExpression in 7 | guard let actualValue = try actualExpression.evaluate() else { return .fail } 8 | 9 | var actualGenerator = actualValue.makeIterator() 10 | return MatcherStatus(bool: actualGenerator.next() == startingElement) 11 | } 12 | } 13 | 14 | /// A Nimble matcher that succeeds when the actual collection's first element 15 | /// is equal to the expected object. 16 | public func beginWith(_ startingElement: Any) -> Matcher { 17 | return Matcher.simple("begin with <\(startingElement)>") { actualExpression in 18 | guard let collection = try actualExpression.evaluate() else { return .fail } 19 | guard collection.count > 0 else { return .doesNotMatch } 20 | #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) || os(visionOS) 21 | let collectionValue = collection.object(at: 0) as AnyObject 22 | #else 23 | guard let collectionValue = collection.object(at: 0) as? NSObject else { 24 | return .fail 25 | } 26 | #endif 27 | return MatcherStatus(bool: collectionValue.isEqual(startingElement)) 28 | } 29 | } 30 | 31 | /// A Nimble matcher that succeeds when the actual string contains expected substring 32 | /// where the expected substring's location is zero. 33 | public func beginWith(_ startingSubstring: String) -> Matcher { 34 | return Matcher.simple("begin with <\(startingSubstring)>") { actualExpression in 35 | guard let actual = try actualExpression.evaluate() else { return .fail } 36 | 37 | return MatcherStatus(bool: actual.hasPrefix(startingSubstring)) 38 | } 39 | } 40 | 41 | #if canImport(Darwin) 42 | extension NMBMatcher { 43 | @objc public class func beginWithMatcher(_ expected: Any) -> NMBMatcher { 44 | return NMBMatcher { actualExpression in 45 | let actual = try actualExpression.evaluate() 46 | if actual is String { 47 | let expr = actualExpression.cast { $0 as? String } 48 | // swiftlint:disable:next force_cast 49 | return try beginWith(expected as! String).satisfies(expr).toObjectiveC() 50 | } else { 51 | let expr = actualExpression.cast { $0 as? NMBOrderedCollection } 52 | return try beginWith(expected).satisfies(expr).toObjectiveC() 53 | } 54 | } 55 | } 56 | } 57 | #endif 58 | -------------------------------------------------------------------------------- /Sources/Nimble/Matchers/BeginWithPrefix.swift: -------------------------------------------------------------------------------- 1 | /// A Nimble matcher that succeeds when the exepected sequence is a prefix of the actual sequence. 2 | /// 3 | /// This is a matcher abstraction for https://developer.apple.com/documentation/swift/sequence/2854218-starts 4 | public func beginWith(prefix expectedPrefix: Seq2?) 5 | -> Matcher where Seq1.Element: Equatable, Seq1.Element == Seq2.Element { 6 | return Matcher.define("begin with <\(stringify(expectedPrefix))>") { (actualExpression, msg) in 7 | let actualPrefix = try actualExpression.evaluate() 8 | switch (expectedPrefix, actualPrefix) { 9 | case (nil, _?): 10 | return MatcherResult(status: .fail, message: msg.appendedBeNilHint()) 11 | case (nil, nil), (_, nil): 12 | return MatcherResult(status: .fail, message: msg) 13 | case (let expected?, let actual?): 14 | let matches = actual.starts(with: expected) 15 | return MatcherResult(bool: matches, message: msg) 16 | } 17 | } 18 | } 19 | 20 | /// A Nimble matcher that succeeds when the expected sequence is the prefix of the actual sequence, using the given matcher as the equivalence test. 21 | /// 22 | /// This is a matcher abstraction for https://developer.apple.com/documentation/swift/sequence/2996828-starts 23 | public func beginWith( 24 | prefix expectedPrefix: Seq2?, 25 | by areEquivalent: @escaping (Seq1.Element, Seq2.Element) -> Bool 26 | ) -> Matcher { 27 | return Matcher.define("begin with <\(stringify(expectedPrefix))>") { (actualExpression, msg) in 28 | let actualPrefix = try actualExpression.evaluate() 29 | switch (expectedPrefix, actualPrefix) { 30 | case (nil, _?): 31 | return MatcherResult(status: .fail, message: msg.appendedBeNilHint()) 32 | case (nil, nil), (_, nil): 33 | return MatcherResult(status: .fail, message: msg) 34 | case (let expected?, let actual?): 35 | let matches = actual.starts(with: expected, by: areEquivalent) 36 | return MatcherResult(bool: matches, message: msg) 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Sources/Nimble/Matchers/ContainElementSatisfying.swift: -------------------------------------------------------------------------------- 1 | public func containElementSatisfying( 2 | _ matcher: @escaping ((S.Element) -> Bool), _ matcherDescription: String = "" 3 | ) -> Matcher { 4 | return Matcher.define { actualExpression in 5 | let message: ExpectationMessage 6 | if matcherDescription == "" { 7 | message = .expectedTo("find object in collection that satisfies matcher") 8 | } else { 9 | message = .expectedTo("find object in collection \(matcherDescription)") 10 | } 11 | 12 | if let sequence = try actualExpression.evaluate() { 13 | for object in sequence where matcher(object) { 14 | return MatcherResult(bool: true, message: message) 15 | } 16 | 17 | return MatcherResult(bool: false, message: message) 18 | } 19 | 20 | return MatcherResult(status: .fail, message: message) 21 | } 22 | } 23 | 24 | public func containElementSatisfying( 25 | _ matcher: @escaping ((S.Element) async -> Bool), _ matcherDescription: String = "" 26 | ) -> AsyncMatcher { 27 | return AsyncMatcher.define { actualExpression in 28 | let message: ExpectationMessage 29 | if matcherDescription == "" { 30 | message = .expectedTo("find object in collection that satisfies matcher") 31 | } else { 32 | message = .expectedTo("find object in collection \(matcherDescription)") 33 | } 34 | 35 | if let sequence = try await actualExpression.evaluate() { 36 | for object in sequence where await matcher(object) { 37 | return MatcherResult(bool: true, message: message) 38 | } 39 | 40 | return MatcherResult(bool: false, message: message) 41 | } 42 | 43 | return MatcherResult(status: .fail, message: message) 44 | } 45 | } 46 | 47 | #if canImport(Darwin) 48 | import class Foundation.NSObject 49 | import struct Foundation.NSFastEnumerationIterator 50 | import protocol Foundation.NSFastEnumeration 51 | 52 | extension NMBMatcher { 53 | @objc public class func containElementSatisfyingMatcher(_ matcher: @escaping ((NSObject) -> Bool)) -> NMBMatcher { 54 | return NMBMatcher { actualExpression in 55 | let value = try actualExpression.evaluate() 56 | guard let enumeration = value as? NSFastEnumeration else { 57 | let message = ExpectationMessage.fail( 58 | "containElementSatisfying must be provided an NSFastEnumeration object" 59 | ) 60 | return NMBMatcherResult(status: .fail, message: message.toObjectiveC()) 61 | } 62 | 63 | let message = ExpectationMessage 64 | .expectedTo("find object in collection that satisfies matcher") 65 | .toObjectiveC() 66 | 67 | var iterator = NSFastEnumerationIterator(enumeration) 68 | while let item = iterator.next() { 69 | guard let object = item as? NSObject else { 70 | continue 71 | } 72 | 73 | if matcher(object) { 74 | return NMBMatcherResult(status: .matches, message: message) 75 | } 76 | } 77 | 78 | return NMBMatcherResult(status: .doesNotMatch, message: message) 79 | } 80 | } 81 | } 82 | #endif 83 | -------------------------------------------------------------------------------- /Sources/Nimble/Matchers/ElementsEqual.swift: -------------------------------------------------------------------------------- 1 | /// A Nimble matcher that succeeds when the actual sequence and the exepected sequence contain the same elements in 2 | /// the same order. 3 | /// 4 | /// This is a matcher abstraction for https://developer.apple.com/documentation/swift/sequence/2854213-elementsequal 5 | public func elementsEqual( 6 | _ expectedValue: Seq2? 7 | ) -> Matcher where Seq1.Element: Equatable, Seq1.Element == Seq2.Element { 8 | return Matcher.define("elementsEqual <\(stringify(expectedValue))>") { (actualExpression, msg) in 9 | let actualValue = try actualExpression.evaluate() 10 | switch (expectedValue, actualValue) { 11 | case (nil, _?): 12 | return MatcherResult(status: .fail, message: msg.appendedBeNilHint()) 13 | case (nil, nil), (_, nil): 14 | return MatcherResult(status: .fail, message: msg) 15 | case (let expected?, let actual?): 16 | let matches = expected.elementsEqual(actual) 17 | return MatcherResult(bool: matches, message: msg) 18 | } 19 | } 20 | } 21 | 22 | /// A Nimble matcher that succeeds when the actual sequence and the exepected sequence contain equivalent elements in 23 | /// the same order, using the given matcher as the equivalence test. 24 | /// 25 | /// This is a matcher abstraction for https://developer.apple.com/documentation/swift/sequence/2949668-elementsequal 26 | public func elementsEqual( 27 | _ expectedValue: Seq2?, 28 | by areEquivalent: @escaping (Seq1.Element, Seq2.Element) -> Bool 29 | ) -> Matcher { 30 | return Matcher.define("elementsEqual <\(stringify(expectedValue))>") { (actualExpression, msg) in 31 | let actualValue = try actualExpression.evaluate() 32 | switch (expectedValue, actualValue) { 33 | case (nil, _?): 34 | return MatcherResult(status: .fail, message: msg.appendedBeNilHint()) 35 | case (nil, nil), (_, nil): 36 | return MatcherResult(status: .fail, message: msg) 37 | case (let expected?, let actual?): 38 | let matches = actual.elementsEqual(expected, by: areEquivalent) 39 | return MatcherResult(bool: matches, message: msg) 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/Nimble/Matchers/EndWith.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /// A Nimble matcher that succeeds when the actual sequence's last element 4 | /// is equal to the expected value. 5 | public func endWith(_ endingElement: S.Element) -> Matcher where S.Element: Equatable { 6 | return Matcher.simple("end with <\(endingElement)>") { actualExpression in 7 | guard let actualValue = try actualExpression.evaluate() else { return .fail } 8 | 9 | var actualGenerator = actualValue.makeIterator() 10 | var lastItem: S.Element? 11 | var item: S.Element? 12 | repeat { 13 | lastItem = item 14 | item = actualGenerator.next() 15 | } while(item != nil) 16 | 17 | return MatcherStatus(bool: lastItem == endingElement) 18 | } 19 | } 20 | 21 | /// A Nimble matcher that succeeds when the actual collection's last element 22 | /// is equal to the expected object. 23 | public func endWith(_ endingElement: Any) -> Matcher { 24 | return Matcher.simple("end with <\(endingElement)>") { actualExpression in 25 | guard let collection = try actualExpression.evaluate() else { return .fail } 26 | 27 | guard collection.count > 0 else { return MatcherStatus(bool: false) } 28 | #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) || os(visionOS) 29 | let collectionValue = collection.object(at: collection.count - 1) as AnyObject 30 | #else 31 | guard let collectionValue = collection.object(at: collection.count - 1) as? NSObject else { 32 | return .fail 33 | } 34 | #endif 35 | 36 | return MatcherStatus(bool: collectionValue.isEqual(endingElement)) 37 | } 38 | } 39 | 40 | /// A Nimble matcher that succeeds when the actual string contains the expected substring 41 | /// where the expected substring's location is the actual string's length minus the 42 | /// expected substring's length. 43 | public func endWith(_ endingSubstring: String) -> Matcher { 44 | return Matcher.simple("end with <\(endingSubstring)>") { actualExpression in 45 | guard let collection = try actualExpression.evaluate() else { return .fail } 46 | 47 | return MatcherStatus(bool: collection.hasSuffix(endingSubstring)) 48 | } 49 | } 50 | 51 | #if canImport(Darwin) 52 | extension NMBMatcher { 53 | @objc public class func endWithMatcher(_ expected: Any) -> NMBMatcher { 54 | return NMBMatcher { actualExpression in 55 | let actual = try actualExpression.evaluate() 56 | if actual is String { 57 | let expr = actualExpression.cast { $0 as? String } 58 | // swiftlint:disable:next force_cast 59 | return try endWith(expected as! String).satisfies(expr).toObjectiveC() 60 | } else { 61 | let expr = actualExpression.cast { $0 as? NMBOrderedCollection } 62 | return try endWith(expected).satisfies(expr).toObjectiveC() 63 | } 64 | } 65 | } 66 | } 67 | #endif 68 | -------------------------------------------------------------------------------- /Sources/Nimble/Matchers/HaveCount.swift: -------------------------------------------------------------------------------- 1 | // The `haveCount` matchers do not print the full string representation of the collection value, 2 | // instead they only print the type name and the expected count. This makes it easier to understand 3 | // the reason for failed expectations. See: https://github.com/Quick/Nimble/issues/308. 4 | // The representation of the collection content is provided in a new line as an `extendedMessage`. 5 | 6 | /// A Nimble matcher that succeeds when the actual Collection's count equals 7 | /// the expected value 8 | public func haveCount(_ expectedValue: Int) -> Matcher { 9 | return Matcher.define { actualExpression in 10 | if let actualValue = try actualExpression.evaluate() { 11 | let message = ExpectationMessage 12 | .expectedCustomValueTo( 13 | "have \(prettyCollectionType(actualValue)) with count \(stringify(expectedValue))", 14 | actual: "\(actualValue.count)" 15 | ) 16 | .appended(details: "Actual Value: \(stringify(actualValue))") 17 | 18 | let result = expectedValue == actualValue.count 19 | return MatcherResult(bool: result, message: message) 20 | } else { 21 | return MatcherResult(status: .fail, message: .fail("")) 22 | } 23 | } 24 | } 25 | 26 | /// A Nimble matcher that succeeds when the actual collection's count equals 27 | /// the expected value 28 | public func haveCount(_ expectedValue: Int) -> Matcher { 29 | return Matcher { actualExpression in 30 | if let actualValue = try actualExpression.evaluate() { 31 | let message = ExpectationMessage 32 | .expectedCustomValueTo( 33 | "have \(prettyCollectionType(actualValue)) with count \(stringify(expectedValue))", 34 | actual: "\(actualValue.count). Actual Value: \(stringify(actualValue))" 35 | ) 36 | 37 | let result = expectedValue == actualValue.count 38 | return MatcherResult(bool: result, message: message) 39 | } else { 40 | return MatcherResult(status: .fail, message: .fail("")) 41 | } 42 | } 43 | } 44 | 45 | #if canImport(Darwin) 46 | import Foundation 47 | 48 | extension NMBMatcher { 49 | @objc public class func haveCountMatcher(_ expected: NSNumber) -> NMBMatcher { 50 | return NMBMatcher { actualExpression in 51 | let location = actualExpression.location 52 | let actualValue = try actualExpression.evaluate() 53 | if let value = actualValue as? NMBCollection { 54 | let expr = Expression(expression: ({ value as NMBCollection}), location: location) 55 | return try haveCount(expected.intValue).satisfies(expr).toObjectiveC() 56 | } 57 | 58 | let message: ExpectationMessage 59 | if let actualValue = actualValue { 60 | message = ExpectationMessage.expectedCustomValueTo( 61 | "get type of NSArray, NSSet, NSDictionary, or NSHashTable", 62 | actual: "\(String(describing: type(of: actualValue)))" 63 | ) 64 | } else { 65 | message = ExpectationMessage 66 | .expectedActualValueTo("have a collection with count \(stringify(expected.intValue))") 67 | .appendedBeNilHint() 68 | } 69 | return NMBMatcherResult(status: .fail, message: message.toObjectiveC()) 70 | } 71 | } 72 | } 73 | #endif 74 | -------------------------------------------------------------------------------- /Sources/Nimble/Matchers/Match.swift: -------------------------------------------------------------------------------- 1 | /// A Nimble matcher that succeeds when the actual string satisfies the regular expression 2 | /// described by the expected string. 3 | public func match(_ expectedValue: String?) -> Matcher { 4 | return Matcher.simple("match <\(stringify(expectedValue))>") { actualExpression in 5 | guard let actual = try actualExpression.evaluate(), let regexp = expectedValue else { return .fail } 6 | 7 | let bool = actual.range(of: regexp, options: .regularExpression) != nil 8 | return MatcherStatus(bool: bool) 9 | } 10 | } 11 | 12 | #if canImport(Darwin) 13 | import class Foundation.NSString 14 | 15 | extension NMBMatcher { 16 | @objc public class func matchMatcher(_ expected: NSString) -> NMBMatcher { 17 | return NMBMatcher { actualExpression in 18 | let actual = actualExpression.cast { $0 as? String } 19 | return try match(expected.description).satisfies(actual).toObjectiveC() 20 | } 21 | } 22 | } 23 | #endif 24 | -------------------------------------------------------------------------------- /Sources/Nimble/Matchers/MatchError.swift: -------------------------------------------------------------------------------- 1 | /// A Nimble matcher that succeeds when the actual expression evaluates to an 2 | /// error from the specified case. 3 | /// 4 | /// Errors are tried to be compared by their implementation of Equatable, 5 | /// otherwise they fallback to comparison by _domain and _code. 6 | public func matchError(_ error: T) -> Matcher { 7 | return Matcher.define { actualExpression in 8 | let actualError = try actualExpression.evaluate() 9 | 10 | let message = messageForError( 11 | postfixMessageVerb: "match", 12 | actualError: actualError, 13 | error: error 14 | ) 15 | 16 | var matches = false 17 | if let actualError = actualError, errorMatchesExpectedError(actualError, expectedError: error) { 18 | matches = true 19 | } 20 | 21 | return MatcherResult(bool: matches, message: message) 22 | } 23 | } 24 | 25 | /// A Nimble matcher that succeeds when the actual expression evaluates to an 26 | /// error from the specified case. 27 | /// 28 | /// Errors are tried to be compared by their implementation of Equatable, 29 | /// otherwise they fallback to comparision by _domain and _code. 30 | public func matchError(_ error: T) -> Matcher { 31 | return Matcher.define { actualExpression in 32 | let actualError = try actualExpression.evaluate() 33 | 34 | let message = messageForError( 35 | postfixMessageVerb: "match", 36 | actualError: actualError, 37 | error: error 38 | ) 39 | 40 | var matches = false 41 | if let actualError = actualError as? T, error == actualError { 42 | matches = true 43 | } 44 | 45 | return MatcherResult(bool: matches, message: message) 46 | } 47 | } 48 | 49 | /// A Nimble matcher that succeeds when the actual expression evaluates to an 50 | /// error of the specified type 51 | public func matchError(_ errorType: T.Type) -> Matcher { 52 | return Matcher.define { actualExpression in 53 | let actualError = try actualExpression.evaluate() 54 | 55 | let message = messageForError( 56 | postfixMessageVerb: "match", 57 | actualError: actualError, 58 | errorType: errorType 59 | ) 60 | 61 | var matches = false 62 | if actualError as? T != nil { 63 | matches = true 64 | } 65 | 66 | return MatcherResult(bool: matches, message: message) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Sources/Nimble/Matchers/Negation.swift: -------------------------------------------------------------------------------- 1 | /// A matcher that negates the passed in matcher 2 | /// 3 | /// - Note: If the passed-in matcher unconditionally fails, then `not` also unconditionally fails. 4 | public func not(_ matcher: Matcher) -> Matcher { 5 | Matcher { actualExpression in 6 | negateMatcherResult( 7 | try matcher.satisfies(actualExpression) 8 | ) 9 | } 10 | } 11 | 12 | /// A matcher that negates the passed in matcher 13 | /// 14 | /// - Note: If the passed-in matcher unconditionally fails, then `not` also unconditionally fails. 15 | public func not(_ matcher: AsyncMatcher) -> AsyncMatcher { 16 | AsyncMatcher { actualExpression in 17 | negateMatcherResult( 18 | try await matcher.satisfies(actualExpression) 19 | ) 20 | } 21 | } 22 | 23 | private func negateMatcherResult(_ matcherResult: MatcherResult) -> MatcherResult { 24 | let status: MatcherStatus 25 | switch matcherResult.status { 26 | case .matches: 27 | status = .doesNotMatch 28 | case .doesNotMatch: 29 | status = .matches 30 | case .fail: 31 | status = .fail 32 | } 33 | return MatcherResult( 34 | status: status, 35 | message: matcherResult.message.prepended(expectation: "not ") 36 | ) 37 | } 38 | -------------------------------------------------------------------------------- /Sources/Nimble/Matchers/ToSucceed.swift: -------------------------------------------------------------------------------- 1 | /** 2 | Used by the `succeed` matcher. 3 | 4 | This is the return type for the closure. 5 | */ 6 | public enum ToSucceedResult { 7 | case succeeded 8 | case failed(reason: String) 9 | } 10 | 11 | /** 12 | A Nimble matcher that takes in a closure for validation. 13 | 14 | Return `.succeeded` when the validation succeeds. 15 | Return `.failed` with a failure reason when the validation fails. 16 | */ 17 | public func succeed() -> Matcher { 18 | return Matcher.define { actualExpression in 19 | let optActual = try actualExpression.evaluate() 20 | guard let actual = optActual else { 21 | return MatcherResult(status: .fail, message: .fail("expected a ToSucceedResult, got ")) 22 | } 23 | 24 | switch actual { 25 | case .succeeded: 26 | return MatcherResult( 27 | bool: true, 28 | message: .expectedCustomValueTo("succeed", actual: "") 29 | ) 30 | case .failed(let reason): 31 | return MatcherResult( 32 | bool: false, 33 | message: .expectedCustomValueTo("succeed", actual: " because <\(reason)>") 34 | ) 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Sources/Nimble/Nimble.docc/Guides/Background.md: -------------------------------------------------------------------------------- 1 | # Some Background: Expressing Outcomes Using Assertions in XCTest 2 | 3 | Apple's Xcode includes the XCTest framework, which provides 4 | assertion macros to test whether code behaves properly. 5 | For example, to assert that `1 + 1 = 2`, XCTest has you write: 6 | 7 | ```swift 8 | // Swift 9 | 10 | XCTAssertEqual(1 + 1, 2, "expected one plus one to equal two") 11 | ``` 12 | 13 | Or, in Objective-C: 14 | 15 | ```objc 16 | // Objective-C 17 | 18 | XCTAssertEqual(1 + 1, 2, @"expected one plus one to equal two"); 19 | ``` 20 | 21 | XCTest assertions have a couple of drawbacks: 22 | 23 | 1. **Not enough macros.** There's no easy way to assert that a string 24 | contains a particular substring, or that a number is less than or 25 | equal to another. 26 | 2. **It's hard to check expressions that change over time.** XCTest forces you to write 27 | a lot of boilerplate code. 28 | 29 | Nimble addresses these concerns. 30 | -------------------------------------------------------------------------------- /Sources/Nimble/Nimble.docc/Guides/Concurrency.md: -------------------------------------------------------------------------------- 1 | # Swift Concurrency (Async/Await) Support 2 | 3 | Nimble makes it easy to await for an async function to complete. 4 | 5 | Simply pass the async function in to ``expecta(file:line:_:)-4z5rl``: 6 | 7 | ```swift 8 | // Swift 9 | await expect { await aFunctionReturning1() }.to(equal(1)) 10 | ``` 11 | 12 | The async function is awaited on first, before passing it to the matcher. This 13 | enables the matcher to run synchronous code like before, without caring about 14 | whether the value it's processing was abtained async or not. 15 | 16 | Async support is Swift-only, and it requires that you execute the test in an 17 | async context. For XCTest, this is as simple as marking your test function with 18 | `async`. If you use Quick, all tests in Quick 6 are executed in an async context. 19 | In Quick 7 and later, only tests that are in an `AsyncSpec` subclass will be 20 | executed in an async context. 21 | 22 | To avoid a compiler errors when using synchronous `expect` in asynchronous contexts, 23 | `expect` with async expressions does not support autoclosures. However, the `expecta` 24 | (expect async) function is provided as an alternative, which does support autoclosures. 25 | 26 | ```swift 27 | // Swift 28 | await expecta(await aFunctionReturning1()).to(equal(1))) 29 | ``` 30 | 31 | Similarly, if you're ever in a situation where you want to force the compiler to 32 | produce a ``SyncExpectation``, you can use the ``expects(file:line:_:)-1ojb4`` (expect sync) function to 33 | produce a `SyncExpectation`. Like so: 34 | 35 | ```swift 36 | // Swift 37 | expects(someNonAsyncFunction()).to(equal(1))) 38 | 39 | expects(await someAsyncFunction()).to(equal(1)) // Compiler error: 'async' call in an autoclosure that does not support concurrency 40 | ``` 41 | 42 | ### Async Matchers 43 | 44 | In addition to asserting on async functions prior to passing them to a 45 | synchronous matcher, you can also write matchers that directly take in an 46 | async value. These are called ``AsyncMatcher``s. This is most obviously useful 47 | when directly asserting against an actor. In addition to writing your own 48 | async matchers, Nimble currently ships with async versions of the following 49 | matchers: 50 | 51 | - ``allPass(_:)-5avdc`` 52 | - ``containElementSatisfying(_:_:)-8omf3`` 53 | - ``satisfyAllOf(_:)-99ble`` and the ``&&(left:right:)`` operator overload accept both ``AsyncMatcher`` and 54 | synchronous ``Matcher``s. 55 | - ``satisfyAnyOf(_:)-8groo`` and the ``||(left:right:)`` operator overload accept both ``AsyncMatcher`` and 56 | synchronous ``Matcher``s. 57 | 58 | Note: Swift Concurrency support is different than the 59 | ``AsyncExpectation/toEventually(_:timeout:pollInterval:description:)-38brw``/``AsyncExpectation/toEventuallyNot(_:timeout:pollInterval:description:)-4ez1r`` 60 | feature described in . 61 | Polling Expectations works by continuously polling 62 | the `Expectation` until it passes. As described here, Nimble's Swift 63 | Concurrency support is about waiting for an expression to finish. 64 | 65 | It is certainly possible to use Polling Expectations with async/await, as the 66 | result of a concurrent Expectation can certainly change with time. 67 | -------------------------------------------------------------------------------- /Sources/Nimble/Nimble.docc/Guides/ObjectiveC.md: -------------------------------------------------------------------------------- 1 | # Objective-C Support 2 | 3 | Nimble has full support for Objective-C. However, there are two things 4 | to keep in mind when using Nimble in Objective-C: 5 | 6 | 1. All parameters passed to the ``expect`` function, as well as matcher 7 | functions like ``equal``, must be Objective-C objects or can be converted into 8 | an `NSObject` equivalent: 9 | 10 | ```objc 11 | // Objective-C 12 | 13 | @import Nimble; 14 | 15 | expect(@(1 + 1)).to(equal(@2)); 16 | expect(@"Hello world").to(contain(@"world")); 17 | 18 | // Boxed as NSNumber * 19 | expect(2).to(equal(2)); 20 | expect(1.2).to(beLessThan(2.0)); 21 | expect(true).to(beTruthy()); 22 | 23 | // Boxed as NSString * 24 | expect("Hello world").to(equal("Hello world")); 25 | 26 | // Boxed as NSRange 27 | expect(NSMakeRange(1, 10)).to(equal(NSMakeRange(1, 10))); 28 | ``` 29 | 30 | 2. To make an expectation on an expression that does not return a value, 31 | such as `-[NSException raise]`, use ``expectAction`` instead of 32 | ``expect``: 33 | 34 | ```objc 35 | // Objective-C 36 | 37 | expectAction(^{ [exception raise]; }).to(raiseException()); 38 | ``` 39 | 40 | The following types are currently converted to an `NSObject` type: 41 | 42 | - **C Numeric types** are converted to `NSNumber *` 43 | - `NSRange` is converted to `NSValue *` 44 | - `char *` is converted to `NSString *` 45 | 46 | For the following matchers: 47 | 48 | - ``equal`` 49 | - ``beGreaterThan`` 50 | - ``beGreaterThanOrEqual`` 51 | - ``beLessThan`` 52 | - ``beLessThanOrEqual`` 53 | - ``beCloseTo`` 54 | - ``beTrue`` 55 | - ``beFalse`` 56 | - ``beTruthy`` 57 | - ``beFalsy`` 58 | - ``haveCount`` 59 | 60 | 61 | If you would like to see more, [file an issue](https://github.com/Quick/Nimble/issues). 62 | 63 | ## Disabling Objective-C Shorthand 64 | 65 | Nimble provides a shorthand for expressing expectations using the 66 | ``expect`` function. To disable this shorthand in Objective-C, define the 67 | ``NIMBLE_DISABLE_SHORT_SYNTAX`` macro somewhere in your code before 68 | importing Nimble: 69 | 70 | ```objc 71 | #define NIMBLE_DISABLE_SHORT_SYNTAX 1 72 | 73 | @import Nimble; 74 | 75 | NMB_expect(^{ return seagull.squawk; }, __FILE__, __LINE__).to(NMB_equal(@"Squee!")); 76 | ``` 77 | 78 | > Disabling the shorthand is useful if you're testing functions with 79 | names that conflict with Nimble functions, such as ``expect`` or 80 | ``equal``. If that's not the case, there's no point in disabling the 81 | shorthand. 82 | -------------------------------------------------------------------------------- /Sources/Nimble/Nimble.docc/Matchers/Comparisons.md: -------------------------------------------------------------------------------- 1 | # Comparisons 2 | 3 | Comparing the expression with other values. 4 | 5 | ```swift 6 | // Swift 7 | 8 | expect(actual).to(beLessThan(expected)) 9 | expect(actual) < expected 10 | 11 | expect(actual).to(beLessThanOrEqualTo(expected)) 12 | expect(actual) <= expected 13 | 14 | expect(actual).to(beGreaterThan(expected)) 15 | expect(actual) > expected 16 | 17 | expect(actual).to(beGreaterThanOrEqualTo(expected)) 18 | expect(actual) >= expected 19 | ``` 20 | 21 | ```objc 22 | // Objective-C 23 | 24 | expect(actual).to(beLessThan(expected)); 25 | expect(actual).to(beLessThanOrEqualTo(expected)); 26 | expect(actual).to(beGreaterThan(expected)); 27 | expect(actual).to(beGreaterThanOrEqualTo(expected)); 28 | ``` 29 | 30 | > Values given to the comparison matchers above must implement 31 | `Comparable`. 32 | 33 | Because of how computers represent floating point numbers, assertions 34 | that two floating point numbers be equal will sometimes fail. To express 35 | that two numbers should be close to one another within a certain margin 36 | of error, use `beCloseTo`: 37 | 38 | ```swift 39 | // Swift 40 | 41 | expect(actual).to(beCloseTo(expected, within: delta)) 42 | ``` 43 | 44 | ```objc 45 | // Objective-C 46 | 47 | expect(actual).to(beCloseTo(expected).within(delta)); 48 | ``` 49 | 50 | For example, to assert that `10.01` is close to `10`, you can write: 51 | 52 | ```swift 53 | // Swift 54 | 55 | expect(10.01).to(beCloseTo(10, within: 0.1)) 56 | ``` 57 | 58 | ```objc 59 | // Objective-C 60 | 61 | expect(@(10.01)).to(beCloseTo(@10).within(0.1)); 62 | ``` 63 | 64 | There is also an operator shortcut available in Swift: 65 | 66 | ```swift 67 | // Swift 68 | 69 | expect(actual) ≈ expected 70 | expect(actual) ≈ (expected, delta) 71 | 72 | ``` 73 | (Type option+x to get `≈` on a U.S. keyboard) 74 | 75 | The former version uses the default delta of 0.0001. Here is yet another way to do this: 76 | 77 | ```swift 78 | // Swift 79 | 80 | expect(actual) ≈ expected ± delta 81 | expect(actual) == expected ± delta 82 | 83 | ``` 84 | (Type option+shift+= to get `±` on a U.S. keyboard) 85 | 86 | If you are comparing arrays of floating point numbers, you'll find the following useful: 87 | 88 | ```swift 89 | // Swift 90 | 91 | expect([0.0, 2.0]) ≈ [0.0001, 2.0001] 92 | expect([0.0, 2.0]).to(beCloseTo([0.1, 2.1], within: 0.1)) 93 | 94 | ``` 95 | 96 | > Values given to the `beCloseTo` matcher must conform to `FloatingPoint`. 97 | -------------------------------------------------------------------------------- /Sources/Nimble/Nimble.docc/Matchers/CustomValidation.md: -------------------------------------------------------------------------------- 1 | # Custom Validation 2 | 3 | Nimble allows you to perform custom validation. 4 | 5 | ```swift 6 | // Swift 7 | 8 | // passes if .succeeded is returned from the closure 9 | expect { 10 | guard case .enumCaseWithAssociatedValueThatIDontCareAbout = actual else { 11 | return .failed(reason: "wrong enum case") 12 | } 13 | 14 | return .succeeded 15 | }.to(succeed()) 16 | 17 | // passes if .failed is returned from the closure 18 | expect { 19 | guard case .enumCaseWithAssociatedValueThatIDontCareAbout = actual else { 20 | return .failed(reason: "wrong enum case") 21 | } 22 | 23 | return .succeeded 24 | }.notTo(succeed()) 25 | ``` 26 | 27 | The `String` provided with `.failed()` is shown when the test fails. 28 | 29 | > Warning: When using Polling Expectations be careful not to make state changes 30 | or run process intensive code since this closure will be ran many times. 31 | -------------------------------------------------------------------------------- /Sources/Nimble/Nimble.docc/Matchers/Equivalence.md: -------------------------------------------------------------------------------- 1 | # Equivalence 2 | 3 | Checking if a value is equal to another. 4 | 5 | ```swift 6 | // Swift 7 | 8 | // Passes if 'actual' is equivalent to 'expected': 9 | expect(actual).to(equal(expected)) 10 | expect(actual) == expected 11 | 12 | // Passes if 'actual' is not equivalent to 'expected': 13 | expect(actual).toNot(equal(expected)) 14 | expect(actual) != expected 15 | ``` 16 | 17 | ```objc 18 | // Objective-C 19 | 20 | // Passes if 'actual' is equivalent to 'expected': 21 | expect(actual).to(equal(expected)) 22 | 23 | // Passes if 'actual' is not equivalent to 'expected': 24 | expect(actual).toNot(equal(expected)) 25 | ``` 26 | 27 | Values must be `Equatable`, `Comparable`, or subclasses of `NSObject`. 28 | `equal` will always fail when used to compare one or more `nil` values. 29 | -------------------------------------------------------------------------------- /Sources/Nimble/Nimble.docc/Matchers/Exceptions.md: -------------------------------------------------------------------------------- 1 | # Exceptions 2 | 3 | Check exceptions raised from Objective-C. 4 | 5 | ```swift 6 | // Swift 7 | 8 | // Passes if 'actual', when evaluated, raises an exception: 9 | expect(actual).to(raiseException()) 10 | 11 | // Passes if 'actual' raises an exception with the given name: 12 | expect(actual).to(raiseException(named: name)) 13 | 14 | // Passes if 'actual' raises an exception with the given name and reason: 15 | expect(actual).to(raiseException(named: name, reason: reason)) 16 | 17 | // Passes if 'actual' raises an exception which passes expectations defined in the given closure: 18 | // (in this case, if the exception's name begins with "a r") 19 | expect { exception.raise() }.to(raiseException { (exception: NSException) in 20 | expect(exception.name).to(beginWith("a r")) 21 | }) 22 | ``` 23 | 24 | ```objc 25 | // Objective-C 26 | 27 | // Passes if 'actual', when evaluated, raises an exception: 28 | expect(actual).to(raiseException()) 29 | 30 | // Passes if 'actual' raises an exception with the given name 31 | expect(actual).to(raiseException().named(name)) 32 | 33 | // Passes if 'actual' raises an exception with the given name and reason: 34 | expect(actual).to(raiseException().named(name).reason(reason)) 35 | 36 | // Passes if 'actual' raises an exception and it passes expectations defined in the given block: 37 | // (in this case, if name begins with "a r") 38 | expect(actual).to(raiseException().satisfyingBlock(^(NSException *exception) { 39 | expect(exception.name).to(beginWith(@"a r")); 40 | })); 41 | ``` 42 | 43 | > Note: Swift currently doesn't have exceptions (see [#220](https://github.com/Quick/Nimble/issues/220#issuecomment-172667064)). 44 | Only Objective-C code can raise exceptions that Nimble will catch. 45 | 46 | > Note: ``raiseException()`` is currently unavailable when Nimble is installed 47 | through Swift Package Manager. 48 | -------------------------------------------------------------------------------- /Sources/Nimble/Nimble.docc/Matchers/GroupsOfMatchers.md: -------------------------------------------------------------------------------- 1 | # Matching a value to any of a group of matchers 2 | 3 | Combining matchers into a single Expectation. 4 | 5 | ```swift 6 | // Swift 7 | 8 | // passes if actual is either less than 10 or greater than 20 9 | expect(actual).to(satisfyAnyOf(beLessThan(10), beGreaterThan(20))) 10 | 11 | // can include any number of matchers -- the following will pass 12 | // **be careful** -- too many matchers can be the sign of an unfocused test 13 | expect(6).to(satisfyAnyOf(equal(2), equal(3), equal(4), equal(5), equal(6), equal(7))) 14 | 15 | // in Swift you also have the option to use the || operator to achieve a similar function 16 | expect(82).to(beLessThan(50) || beGreaterThan(80)) 17 | ``` 18 | 19 | > Note: In swift, you can mix and match synchronous and asynchronous matchers using by `satisfyAnyOf`/`||`. 20 | 21 | ```objc 22 | // Objective-C 23 | 24 | // passes if actual is either less than 10 or greater than 20 25 | expect(actual).to(satisfyAnyOf(beLessThan(@10), beGreaterThan(@20))) 26 | 27 | // can include any number of matchers -- the following will pass 28 | // **be careful** -- too many matchers can be the sign of an unfocused test 29 | expect(@6).to(satisfyAnyOf(equal(@2), equal(@3), equal(@4), equal(@5), equal(@6), equal(@7))) 30 | ``` 31 | 32 | Note: This matcher allows you to chain any number of matchers together. This provides flexibility, 33 | but if you find yourself chaining many matchers together in one test, consider whether you 34 | could instead refactor that single test into multiple, more precisely focused tests for 35 | better coverage. 36 | -------------------------------------------------------------------------------- /Sources/Nimble/Nimble.docc/Matchers/Identity.md: -------------------------------------------------------------------------------- 1 | # Identity 2 | 3 | Checking if an object is the same address as another. 4 | 5 | ```swift 6 | // Swift 7 | 8 | // Passes if 'actual' has the same pointer address as 'expected': 9 | expect(actual).to(beIdenticalTo(expected)) 10 | expect(actual) === expected 11 | 12 | // Passes if 'actual' does not have the same pointer address as 'expected': 13 | expect(actual).toNot(beIdenticalTo(expected)) 14 | expect(actual) !== expected 15 | ``` 16 | 17 | It is important to remember that `beIdenticalTo` only makes sense when comparing 18 | types with reference semantics, which have a notion of identity. In Swift, 19 | that means types that are defined as a `class`. 20 | 21 | This matcher will not work when comparing types with value semantics such as 22 | those defined as a `struct` or `enum`. If you need to compare two value types, 23 | consider what it means for instances of your type to be identical. This may mean 24 | comparing individual properties or, if it makes sense to do so, conforming your type 25 | to `Equatable` and using Nimble's equivalence matchers instead. 26 | 27 | 28 | ```objc 29 | // Objective-C 30 | 31 | // Passes if 'actual' has the same pointer address as 'expected': 32 | expect(actual).to(beIdenticalTo(expected)); 33 | 34 | // Passes if 'actual' does not have the same pointer address as 'expected': 35 | expect(actual).toNot(beIdenticalTo(expected)); 36 | ``` 37 | 38 | > Warning: `beIdenticalTo` checks the pointer addresses. It does not use the 39 | `Identifiable` protocol to check for identity. 40 | -------------------------------------------------------------------------------- /Sources/Nimble/Nimble.docc/Matchers/Map.md: -------------------------------------------------------------------------------- 1 | # Mapping a Value to Another Value 2 | 3 | Mapping a value to another value in the matcher. 4 | 5 | Sometimes, you only want to match against a property or group of properties. 6 | For example, if you wanted to check that only one or a few properties of a value 7 | are equal to something else. For this, use the ``map`` matcher to convert a value 8 | to another value and check it with a matcher. 9 | 10 | ```swift 11 | // Swift 12 | 13 | expect(someValue).to(map(\.someProperty, equal(expectedProperty))) 14 | 15 | // or, for checking multiple different properties: 16 | 17 | expect(someValue).to(satisfyAllOf( 18 | map(\.firstProperty, equal(expectedFirstProperty)), 19 | map({ $0.secondProperty }, equal(expectedSecondProperty)) 20 | )) 21 | ``` 22 | 23 | The ``map`` matcher takes in either a closure or a keypath literal, and a matcher 24 | to compose with. It also works with async closures and async matchers. 25 | 26 | In most cases, it is simpler and easier to not use map (that is, prefer 27 | `expect(someValue.property).to(equal(1))` to 28 | `expect(someValue).to(map(\.property, equal(1)))`). But `map` is incredibly 29 | useful when combined with `satisfyAllOf`/`satisfyAnyOf`, especially for checking 30 | a value that cannot conform to `Equatable` (or you don't want to make it 31 | conform to `Equatable`). However, if you find yourself reusing `map` many times 32 | to do a fuzzy-equals of a given type, you will find writing a custom matcher to 33 | be much easier to use and maintain. 34 | 35 | > Warning: When using Polling Expectations be careful not run process intensive 36 | code since the map closure will be ran many times. 37 | -------------------------------------------------------------------------------- /Sources/Nimble/Nimble.docc/Matchers/Notifications.md: -------------------------------------------------------------------------------- 1 | # Notifications 2 | 3 | Checking Notifications posted to `NotificationCenter` or a 4 | `DistributedNotificationCenter`. 5 | 6 | ```swift 7 | // Swift 8 | let testNotification = Notification(name: Notification.Name("Foo"), object: nil) 9 | 10 | // Passes if the closure in expect { ... } posts a notification to the default 11 | // notification center. 12 | expect { 13 | NotificationCenter.default.post(testNotification) 14 | }.to(postNotifications(equal([testNotification]))) 15 | 16 | // Passes if the closure in expect { ... } posts a notification to a given 17 | // notification center 18 | let notificationCenter = NotificationCenter() 19 | expect { 20 | notificationCenter.post(testNotification) 21 | }.to(postNotifications(equal([testNotification]), from: notificationCenter)) 22 | 23 | // Passes if the closure in expect { ... } posts a notification with the provided names to a given 24 | // notification center. Make sure to use this when running tests on Catalina, 25 | // using DistributedNotificationCenter as there is currently no way 26 | // of observing notifications without providing specific names. 27 | let distributedNotificationCenter = DistributedNotificationCenter() 28 | expect { 29 | distributedNotificationCenter.post(testNotification) 30 | }.to(postDistributedNotifications(equal([testNotification]), 31 | from: distributedNotificationCenter, 32 | names: [testNotification.name])) 33 | ``` 34 | 35 | > This matcher is only available in Swift. 36 | -------------------------------------------------------------------------------- /Sources/Nimble/Nimble.docc/Matchers/Result.md: -------------------------------------------------------------------------------- 1 | # Result 2 | 3 | You can check the contents of a `Result` type using the `beSuccess` or 4 | `beFailure` matchers. 5 | 6 | ```swift 7 | // Swift 8 | let aResult: Result = .success("Hooray") 9 | 10 | // passes if result is .success 11 | expect(aResult).to(beSuccess()) 12 | 13 | // passes if result value is .success and validates Success value 14 | expect(aResult).to(beSuccess { value in 15 | expect(value).to(equal("Hooray")) 16 | }) 17 | 18 | // passes if the result value is .success and if the Success value matches 19 | // the passed-in matcher (in this case, `equal`) 20 | expect(aResult).to(beSuccess(equal("Hooray"))) 21 | 22 | // passes if the result value is .success and if the Success value equals 23 | // the passed-in value (only available when the Success value is Equatable) 24 | expect(aResult).to(beSuccess("Hooray")) 25 | 26 | 27 | enum AnError: Error { 28 | case somethingHappened 29 | } 30 | let otherResult: Result = .failure(.somethingHappened) 31 | 32 | // passes if result is .failure 33 | expect(otherResult).to(beFailure()) 34 | 35 | // passes if result value is .failure and validates error 36 | expect(otherResult).to(beFailure { error in 37 | expect(error).to(matchError(AnError.somethingHappened)) 38 | }) 39 | 40 | // passes if the result value is .failure and if the Failure value matches 41 | // the passed-in matcher (in this case, `matchError`) 42 | expect(otherResult).to(beFailure(matchError(AnError.somethingHappened))) 43 | ``` 44 | 45 | > Note: This matcher is only available in Swift. 46 | -------------------------------------------------------------------------------- /Sources/Nimble/Nimble.docc/Matchers/Strings.md: -------------------------------------------------------------------------------- 1 | # Strings 2 | 3 | You can check strings using the `contain`, `beginWith`, `endWith`, `beEmpty`, 4 | and `match` matchers. 5 | 6 | The `contain`, `beginWith`, `endWith`, and `beEmpty` operate using substrings, 7 | while the `match` matcher checks if the string matches the given regular 8 | expression. 9 | 10 | ```swift 11 | // Swift 12 | 13 | // Passes if 'actual' contains 'substring': 14 | expect(actual).to(contain(substring)) 15 | 16 | // Passes if 'actual' begins with 'prefix': 17 | expect(actual).to(beginWith(prefix)) 18 | 19 | // Passes if 'actual' ends with 'suffix': 20 | expect(actual).to(endWith(suffix)) 21 | 22 | // Passes if 'actual' represents the empty string, "": 23 | expect(actual).to(beEmpty()) 24 | 25 | // Passes if 'actual' matches the regular expression defined in 'expected': 26 | expect(actual).to(match(expected)) 27 | ``` 28 | 29 | ```objc 30 | // Objective-C 31 | 32 | // Passes if 'actual' contains 'substring': 33 | expect(actual).to(contain(expected)); 34 | 35 | // Passes if 'actual' begins with 'prefix': 36 | expect(actual).to(beginWith(prefix)); 37 | 38 | // Passes if 'actual' ends with 'suffix': 39 | expect(actual).to(endWith(suffix)); 40 | 41 | // Passes if 'actual' represents the empty string, "": 42 | expect(actual).to(beEmpty()); 43 | 44 | // Passes if 'actual' matches the regular expression defined in 'expected': 45 | expect(actual).to(match(expected)) 46 | ``` 47 | -------------------------------------------------------------------------------- /Sources/Nimble/Nimble.docc/Matchers/SwiftAssertions.md: -------------------------------------------------------------------------------- 1 | # Swift Assertions 2 | 3 | If you're using Swift, you can use the `throwAssertion` matcher to check if an 4 | assertion is thrown (e.g. `fatalError()`). 5 | 6 | This is made possible by [@mattgallagher](https://github.com/mattgallagher)'s 7 | [CwlPreconditionTesting](https://github.com/mattgallagher/CwlPreconditionTesting) library. 8 | 9 | ```swift 10 | // Swift 11 | 12 | // Passes if 'somethingThatThrows()' throws an assertion, 13 | // such as by calling 'fatalError()' or if a precondition fails: 14 | expect { try somethingThatThrows() }.to(throwAssertion()) 15 | expect { () -> Void in fatalError() }.to(throwAssertion()) 16 | expect { precondition(false) }.to(throwAssertion()) 17 | 18 | // Passes if throwing an NSError is not equal to throwing an assertion: 19 | expect { throw NSError(domain: "test", code: 0, userInfo: nil) }.toNot(throwAssertion()) 20 | 21 | // Passes if the code after the precondition check is not run: 22 | var reachedPoint1 = false 23 | var reachedPoint2 = false 24 | expect { 25 | reachedPoint1 = true 26 | precondition(false, "condition message") 27 | reachedPoint2 = true 28 | }.to(throwAssertion()) 29 | 30 | expect(reachedPoint1) == true 31 | expect(reachedPoint2) == false 32 | ``` 33 | 34 | > Note: This feature is only available in Swift. 35 | > Note: The tvOS simulator is supported, but using a different mechanism, 36 | requiring you to turn off the `Debug executable` scheme setting for your tvOS 37 | scheme's Test configuration. 38 | -------------------------------------------------------------------------------- /Sources/Nimble/Nimble.docc/Matchers/SwiftErrors.md: -------------------------------------------------------------------------------- 1 | # Swift Error Handling 2 | 3 | You can use the ``throwError()`` matcher to check if an error is thrown, and the 4 | ``matchError(_:)-8o974`` to check already-captured errors. 5 | 6 | `throwError` operates on errors that expressions throw using `try`. 7 | 8 | ```swift 9 | // Swift 10 | 11 | // Passes if 'somethingThatThrows()' throws an 'Error': 12 | expect { try somethingThatThrows() }.to(throwError()) 13 | 14 | // Passes if 'somethingThatThrows()' throws an error within a particular domain: 15 | expect { try somethingThatThrows() }.to(throwError { (error: Error) in 16 | expect(error._domain).to(equal(NSCocoaErrorDomain)) 17 | }) 18 | 19 | // Passes if 'somethingThatThrows()' throws a particular error enum case: 20 | expect { try somethingThatThrows() }.to(throwError(NSCocoaError.PropertyListReadCorruptError)) 21 | 22 | // Passes if 'somethingThatThrows()' throws an error of a particular type: 23 | expect { try somethingThatThrows() }.to(throwError(errorType: NimbleError.self)) 24 | ``` 25 | 26 | When working directly with `Error` values, using the `matchError` matcher 27 | allows you to perform certain checks on the error itself without having to 28 | explicitly cast the error. 29 | 30 | The `matchError` matcher allows you to check whether or not the error: 31 | 32 | - is the same _type_ of error you are expecting. 33 | - represents a particular error value that you are expecting. 34 | 35 | This can be useful when using `Result` or `Promise` types, for example. 36 | 37 | ```swift 38 | // Swift 39 | 40 | let actual: Error = ... 41 | 42 | // Passes if 'actual' represents any error value from the NimbleErrorEnum type: 43 | expect(actual).to(matchError(NimbleErrorEnum.self)) 44 | 45 | // Passes if 'actual' represents the case 'timeout' from the NimbleErrorEnum type: 46 | expect(actual).to(matchError(NimbleErrorEnum.timeout)) 47 | 48 | // Passes if 'actual' contains an NSError equal to the one provided: 49 | expect(actual).to(matchError(NSError(domain: "err", code: 123, userInfo: nil))) 50 | ``` 51 | 52 | > Note: This feature is only available in Swift. 53 | -------------------------------------------------------------------------------- /Sources/Nimble/Nimble.docc/Matchers/Truthiness.md: -------------------------------------------------------------------------------- 1 | # Truthiness 2 | 3 | Checking whether an expression is true, false, or nil. 4 | 5 | The ``beTrue()`` matcher matches only if the expression evaluates to `true`, while 6 | the ``beTruthy()`` matcher will also match if the expression also evaluates to a 7 | non-`nil` value, or an object with a boolean value of `true`. 8 | 9 | Similarly, the ``beFalse()`` matcher matches only if the expression evaluates to 10 | `false`, while the ``beFalsy()`` also accepts `nil`, or objects with a boolean 11 | value of `false`. 12 | 13 | Finally, the ``beNil())`` matcher matches only if the expression evaluates to 14 | `nil`. 15 | 16 | 17 | ```swift 18 | // Passes if 'actual' is not nil, true, or an object with a boolean value of true: 19 | expect(actual).to(beTruthy()) 20 | 21 | // Passes if 'actual' is only true (not nil or an object conforming to Boolean true): 22 | expect(actual).to(beTrue()) 23 | 24 | // Passes if 'actual' is nil, false, or an object with a boolean value of false: 25 | expect(actual).to(beFalsy()) 26 | 27 | // Passes if 'actual' is only false (not nil or an object conforming to Boolean false): 28 | expect(actual).to(beFalse()) 29 | 30 | // Passes if 'actual' is nil: 31 | expect(actual).to(beNil()) 32 | ``` 33 | 34 | ```objc 35 | // Objective-C 36 | 37 | // Passes if 'actual' is not nil, true, or an object with a boolean value of true: 38 | expect(actual).to(beTruthy()); 39 | 40 | // Passes if 'actual' is only true (not nil or an object conforming to Boolean true): 41 | expect(actual).to(beTrue()); 42 | 43 | // Passes if 'actual' is nil, false, or an object with a boolean value of false: 44 | expect(actual).to(beFalsy()); 45 | 46 | // Passes if 'actual' is only false (not nil or an object conforming to Boolean false): 47 | expect(actual).to(beFalse()); 48 | 49 | // Passes if 'actual' is nil: 50 | expect(actual).to(beNil()); 51 | ``` 52 | -------------------------------------------------------------------------------- /Sources/Nimble/Nimble.docc/Matchers/TypeChecking.md: -------------------------------------------------------------------------------- 1 | # Type Checking 2 | 3 | Nimble supports checking the type membership of any kind of object, whether 4 | Objective-C conformant or not. 5 | 6 | ```swift 7 | // Swift 8 | 9 | protocol SomeProtocol{} 10 | class SomeClassConformingToProtocol: SomeProtocol{} 11 | struct SomeStructConformingToProtocol: SomeProtocol{} 12 | 13 | // The following tests pass 14 | expect(1).to(beAKindOf(Int.self)) 15 | expect("turtle").to(beAKindOf(String.self)) 16 | 17 | let classObject = SomeClassConformingToProtocol() 18 | expect(classObject).to(beAKindOf(SomeProtocol.self)) 19 | expect(classObject).to(beAKindOf(SomeClassConformingToProtocol.self)) 20 | expect(classObject).toNot(beAKindOf(SomeStructConformingToProtocol.self)) 21 | 22 | let structObject = SomeStructConformingToProtocol() 23 | expect(structObject).to(beAKindOf(SomeProtocol.self)) 24 | expect(structObject).to(beAKindOf(SomeStructConformingToProtocol.self)) 25 | expect(structObject).toNot(beAKindOf(SomeClassConformingToProtocol.self)) 26 | ``` 27 | 28 | ```objc 29 | // Objective-C 30 | 31 | // The following tests pass 32 | NSMutableArray *array = [NSMutableArray array]; 33 | expect(array).to(beAKindOf([NSArray class])); 34 | expect(@1).toNot(beAKindOf([NSNull class])); 35 | ``` 36 | 37 | Objects can be tested for their exact types using the `beAnInstanceOf` matcher: 38 | 39 | ```swift 40 | // Swift 41 | 42 | protocol SomeProtocol{} 43 | class SomeClassConformingToProtocol: SomeProtocol{} 44 | struct SomeStructConformingToProtocol: SomeProtocol{} 45 | 46 | // Unlike the 'beKindOf' matcher, the 'beAnInstanceOf' matcher only 47 | // passes if the object is the EXACT type requested. The following 48 | // tests pass -- note its behavior when working in an inheritance hierarchy. 49 | expect(1).to(beAnInstanceOf(Int.self)) 50 | expect("turtle").to(beAnInstanceOf(String.self)) 51 | 52 | let classObject = SomeClassConformingToProtocol() 53 | expect(classObject).toNot(beAnInstanceOf(SomeProtocol.self)) 54 | expect(classObject).to(beAnInstanceOf(SomeClassConformingToProtocol.self)) 55 | expect(classObject).toNot(beAnInstanceOf(SomeStructConformingToProtocol.self)) 56 | 57 | let structObject = SomeStructConformingToProtocol() 58 | expect(structObject).toNot(beAnInstanceOf(SomeProtocol.self)) 59 | expect(structObject).to(beAnInstanceOf(SomeStructConformingToProtocol.self)) 60 | expect(structObject).toNot(beAnInstanceOf(SomeClassConformingToProtocol.self)) 61 | ``` 62 | -------------------------------------------------------------------------------- /Sources/Nimble/Nimble.docc/Nimble.md: -------------------------------------------------------------------------------- 1 | # ``Nimble`` 2 | 3 | **Nimble** is a testing framework for verifying the outcomes and Swift or Objective-C expressions. 4 | 5 | ## Overview 6 | 7 | Nimble provides 4 things: 8 | 9 | - A way to verify expressions using a natural, easily understood language. 10 | - **Matchers**, or functions to check the Behavior of an expression. 11 | - Means of requiring an **Expectation** - an expression-matcher combination - to pass before continuing. 12 | - A way to check expressions that change over time. 13 | 14 | ## Terms 15 | 16 | - term Expression: A Swift or Objective-C bit of code. For example `1 + 1`. 17 | - term Behavior: A result or side effect of an expression. For example 18 | `print("hello")` has a behavior of writing "hello\n" to standard output, while 19 | `1 + 1` has a behavior of returning 2. 20 | - term Matcher: A function from Nimble which checks an Expression's Behavior. 21 | - term Expectation: An Expression combined with an Expression. For example, 22 | `expect(1 + 1).to(equal(2))` is an Expectation. 23 | - term Polling Expectation: An expectation that is continuously polled until it 24 | finishes. 25 | - term Requirement: An Expectation that must pass before continuing. These are 26 | usually defined using `require` instead of `expect`, though there are shortcuts 27 | such as ``unwrap(file:line:customError:_:)-5q9f3`` and ``pollUnwrap(file:line:_:)-4ddnp``. 28 | 29 | ## Topics 30 | 31 | ### Guides 32 | 33 | - 34 | - 35 | - 36 | - 37 | - 38 | - 39 | - 40 | 41 | ### Matchers 42 | 43 | Nimble includes a wide variety of matcher functions. 44 | 45 | - 46 | - 47 | - 48 | - 49 | - 50 | - 51 | - 52 | - 53 | - 54 | - 55 | - 56 | - 57 | - 58 | - 59 | - 60 | -------------------------------------------------------------------------------- /Sources/Nimble/Nimble.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | // When running below Xcode 15, TARGET_OS_VISION is not defined. Since the project has TREAT_WARNINGS_AS_ERROS enabled 4 | // we need to workaround this warning. 5 | #ifndef TARGET_OS_VISION 6 | #define TARGET_OS_VISION 0 7 | #endif /* TARGET_OS_VISION */ 8 | 9 | #import 10 | #import 11 | #import 12 | 13 | #if TARGET_OS_OSX || TARGET_OS_IOS || TARGET_OS_VISION 14 | #if COCOAPODS 15 | #import 16 | #import 17 | #else 18 | #import "CwlMachBadInstructionHandler.h" 19 | #import "CwlCatchException.h" 20 | #endif 21 | #endif 22 | 23 | FOUNDATION_EXPORT double NimbleVersionNumber; 24 | FOUNDATION_EXPORT const unsigned char NimbleVersionString[]; 25 | -------------------------------------------------------------------------------- /Sources/Nimble/PrivacyInfo.xcprivacy: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 12 | NSPrivacyTracking 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Sources/Nimble/Utils/Errors.swift: -------------------------------------------------------------------------------- 1 | // Generic 2 | 3 | internal func messageForError( 4 | postfixMessageVerb: String = "throw", 5 | actualError: Error?, 6 | error: T? = nil, 7 | errorType: T.Type? = nil, 8 | closure: ((T) -> Void)? = nil 9 | ) -> ExpectationMessage { 10 | var rawMessage = "\(postfixMessageVerb) error" 11 | 12 | if let error = error { 13 | rawMessage += " <\(error)>" 14 | } else if errorType != nil || closure != nil { 15 | rawMessage += " from type <\(T.self)>" 16 | } 17 | if closure != nil { 18 | rawMessage += " that satisfies block" 19 | } 20 | if error == nil && errorType == nil && closure == nil { 21 | rawMessage = "\(postfixMessageVerb) any error" 22 | } 23 | 24 | let actual: String 25 | if let actualError = actualError { 26 | actual = "<\(actualError)>" 27 | } else { 28 | actual = "no error" 29 | } 30 | 31 | return .expectedCustomValueTo(rawMessage, actual: actual) 32 | } 33 | 34 | internal func errorMatchesExpectedError( 35 | _ actualError: Error, 36 | expectedError: T) -> Bool { 37 | return actualError._domain == expectedError._domain 38 | && actualError._code == expectedError._code 39 | } 40 | 41 | // Non-generic 42 | 43 | internal func messageForError( 44 | actualError: Error?, 45 | closure: ((Error) -> Void)? 46 | ) -> ExpectationMessage { 47 | var rawMessage = "throw error" 48 | 49 | if closure != nil { 50 | rawMessage += " that satisfies block" 51 | } else { 52 | rawMessage = "throw any error" 53 | } 54 | 55 | let actual: String 56 | if let actualError = actualError { 57 | actual = "<\(actualError)>" 58 | } else { 59 | actual = "no error" 60 | } 61 | 62 | return .expectedCustomValueTo(rawMessage, actual: actual) 63 | } 64 | -------------------------------------------------------------------------------- /Sources/Nimble/Utils/NimbleTimeInterval.swift: -------------------------------------------------------------------------------- 1 | #if !os(WASI) 2 | 3 | import Dispatch 4 | 5 | #if canImport(CDispatch) 6 | import CDispatch 7 | #endif 8 | 9 | /// A reimplementation of `DispatchTimeInterval` without the `never` case, and conforming to `Sendable`. 10 | public enum NimbleTimeInterval: Sendable, Equatable { 11 | case seconds(Int) 12 | case milliseconds(Int) 13 | case microseconds(Int) 14 | case nanoseconds(Int) 15 | } 16 | 17 | extension NimbleTimeInterval: CustomStringConvertible { 18 | public var dispatchTimeInterval: DispatchTimeInterval { 19 | switch self { 20 | case .seconds(let int): 21 | return .seconds(int) 22 | case .milliseconds(let int): 23 | return .milliseconds(int) 24 | case .microseconds(let int): 25 | return .microseconds(int) 26 | case .nanoseconds(let int): 27 | return .nanoseconds(int) 28 | } 29 | } 30 | 31 | // ** Note: We cannot simply divide the time interval because NimbleTimeInterval associated value type is Int 32 | internal var divided: NimbleTimeInterval { 33 | switch self { 34 | case let .seconds(val): return val < 2 ? .milliseconds(Int(Float(val)/2*1000)) : .seconds(val/2) 35 | case let .milliseconds(val): return .milliseconds(val/2) 36 | case let .microseconds(val): return .microseconds(val/2) 37 | case let .nanoseconds(val): return .nanoseconds(val/2) 38 | } 39 | } 40 | 41 | public var nanoseconds: UInt64 { 42 | switch self { 43 | case .seconds(let int): return UInt64(int) * 1_000_000_000 44 | case .milliseconds(let int): return UInt64(int) * 1_000_000 45 | case .microseconds(let int): return UInt64(int) * 1_000 46 | case .nanoseconds(let int): return UInt64(int) 47 | } 48 | } 49 | 50 | public var description: String { 51 | switch self { 52 | case let .seconds(val): return val == 1 ? "\(Float(val)) second" : "\(Float(val)) seconds" 53 | case let .milliseconds(val): return "\(Float(val)/1_000) seconds" 54 | case let .microseconds(val): return "\(Float(val)/1_000_000) seconds" 55 | case let .nanoseconds(val): return "\(Float(val)/1_000_000_000) seconds" 56 | } 57 | } 58 | } 59 | 60 | #if canImport(Foundation) 61 | import Foundation 62 | 63 | extension NimbleTimeInterval { 64 | public var timeInterval: TimeInterval { 65 | switch self { 66 | case .seconds(let int): return TimeInterval(int) 67 | case .milliseconds(let int): return TimeInterval(int) / 1_000 68 | case .microseconds(let int): return TimeInterval(int) / 1_000_000 69 | case .nanoseconds(let int): return TimeInterval(int) / 1_000_000_000 70 | } 71 | } 72 | } 73 | 74 | extension TimeInterval { 75 | public var nimbleInterval: NimbleTimeInterval { 76 | let microseconds = Int64(self * TimeInterval(USEC_PER_SEC)) 77 | // perhaps use nanoseconds, though would more often be > Int.max 78 | return microseconds < Int.max ? .microseconds(Int(microseconds)) : .seconds(Int(self)) 79 | } 80 | } 81 | 82 | extension Date { 83 | public func advanced(by nimbleTimeInterval: NimbleTimeInterval) -> Date { 84 | self.advanced(by: nimbleTimeInterval.timeInterval) 85 | } 86 | } 87 | #endif 88 | 89 | #endif // #if !os(WASI) 90 | -------------------------------------------------------------------------------- /Sources/Nimble/Utils/SourceLocation.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | // Ideally we would always use `StaticString` as the type for tracking the file name 4 | // that expectations originate from, for consistency with `assert` etc. from the 5 | // stdlib, and because recent versions of the XCTest overlay require `StaticString` 6 | // when calling `XCTFail`. Under the Objective-C runtime (i.e. building on Mac), we 7 | // have to use `String` instead because StaticString can't be generated from Objective-C 8 | #if !canImport(Darwin) 9 | public typealias FileString = StaticString 10 | #else 11 | public typealias FileString = String 12 | #endif 13 | 14 | public final class SourceLocation: NSObject, Sendable { 15 | public let fileID: String 16 | @available(*, deprecated, renamed: "filePath") 17 | public var file: FileString { filePath } 18 | public let filePath: FileString 19 | public let line: UInt 20 | public let column: UInt 21 | 22 | override init() { 23 | fileID = "Unknown/File" 24 | filePath = "Unknown File" 25 | line = 0 26 | column = 0 27 | } 28 | 29 | init(fileID: String, filePath: FileString, line: UInt, column: UInt) { 30 | self.fileID = fileID 31 | self.filePath = filePath 32 | self.line = line 33 | self.column = column 34 | } 35 | 36 | override public var description: String { 37 | return "\(filePath):\(line):\(column)" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Sources/NimbleObjectiveC/NMBExceptionCapture.m: -------------------------------------------------------------------------------- 1 | #import "NMBExceptionCapture.h" 2 | 3 | @interface NMBExceptionCapture () 4 | @property (nonatomic, copy) void(^ _Nullable handler)(NSException * _Nullable); 5 | @property (nonatomic, copy) void(^ _Nullable finally)(void); 6 | @end 7 | 8 | @implementation NMBExceptionCapture 9 | 10 | - (nonnull instancetype)initWithHandler:(void(^ _Nullable)(NSException * _Nonnull))handler finally:(void(^ _Nullable)(void))finally { 11 | self = [super init]; 12 | if (self) { 13 | self.handler = handler; 14 | self.finally = finally; 15 | } 16 | return self; 17 | } 18 | 19 | - (void)tryBlock:(__attribute__((noescape)) void(^ _Nonnull)(void))unsafeBlock { 20 | @try { 21 | unsafeBlock(); 22 | } 23 | @catch (NSException *exception) { 24 | if (self.handler) { 25 | self.handler(exception); 26 | } 27 | } 28 | @finally { 29 | if (self.finally) { 30 | self.finally(); 31 | } 32 | } 33 | } 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /Sources/NimbleObjectiveC/NMBStringify.m: -------------------------------------------------------------------------------- 1 | #import "NMBStringify.h" 2 | 3 | #if SWIFT_PACKAGE 4 | @import Nimble; 5 | #else 6 | #if __has_include("Nimble-Swift.h") 7 | #import "Nimble-Swift.h" 8 | #else 9 | #import 10 | #endif 11 | #endif 12 | 13 | NSString *_Nonnull NMBStringify(id _Nullable anyObject) { 14 | return [NMBStringer stringify:anyObject]; 15 | } 16 | -------------------------------------------------------------------------------- /Sources/NimbleObjectiveC/XCTestObservationCenter+Register.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #if SWIFT_PACKAGE 4 | @import Nimble; 5 | #else 6 | #if __has_include("Nimble-Swift.h") 7 | #import "Nimble-Swift.h" 8 | #else 9 | #import 10 | #endif 11 | #endif 12 | 13 | #pragma mark - Private 14 | 15 | @implementation XCTestObservationCenter (Register) 16 | 17 | + (void)load { 18 | [[XCTestObservationCenter sharedTestObservationCenter] addTestObserver:[CurrentTestCaseTracker sharedInstance]]; 19 | } 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /Sources/NimbleObjectiveC/include/NMBExceptionCapture.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface NMBExceptionCapture : NSObject 5 | 6 | - (nonnull instancetype)initWithHandler:(void(^ _Nullable)(NSException * _Nonnull))handler finally:(void(^ _Nullable)(void))finally; 7 | - (void)tryBlock:(__attribute__((noescape)) void(^ _Nonnull)(void))unsafeBlock NS_SWIFT_NAME(tryBlock(_:)); 8 | 9 | @end 10 | 11 | typedef void(^NMBSourceCallbackBlock)(BOOL successful); 12 | -------------------------------------------------------------------------------- /Sources/NimbleObjectiveC/include/NMBStringify.h: -------------------------------------------------------------------------------- 1 | @class NSString; 2 | 3 | /** 4 | * Returns a string appropriate for displaying in test output 5 | * from the provided value. 6 | * 7 | * @param anyObject A value that will show up in a test's output. 8 | * 9 | * @return The string that is returned can be 10 | * customized per type by conforming a type to the `TestOutputStringConvertible` 11 | * protocol. When stringifying a non-`TestOutputStringConvertible` type, this 12 | * function will return the value's debug description and then its 13 | * normal description if available and in that order. Otherwise it 14 | * will return the result of constructing a string from the value. 15 | * 16 | * @see `TestOutputStringConvertible` 17 | */ 18 | extern NSString *_Nonnull NMBStringify(id _Nullable anyObject) __attribute__((warn_unused_result)); 19 | -------------------------------------------------------------------------------- /Tests/.swiftlint.yml: -------------------------------------------------------------------------------- 1 | disabled_rules: 2 | - line_length 3 | -------------------------------------------------------------------------------- /Tests/NimbleObjectiveCTests/NimbleSpecHelper.h: -------------------------------------------------------------------------------- 1 | @import XCTest; 2 | @import Nimble; 3 | @import Foundation; 4 | #if SWIFT_PACKAGE 5 | @import NimbleSharedTestHelpers; 6 | @import NimbleObjectiveC; 7 | #else 8 | #import "NimbleTests-Swift.h" 9 | #endif 10 | 11 | // Use this when you want to verify the failure message for when an expectation fails 12 | #define expectFailureMessage(MSG, BLOCK) \ 13 | [NimbleHelper expectFailureMessage:(MSG) block:(BLOCK) file:@(__FILE__) line:__LINE__]; 14 | 15 | #define expectFailureMessages(MSGS, BLOCK) \ 16 | [NimbleHelper expectFailureMessages:(MSGS) block:(BLOCK) file:@(__FILE__) line:__LINE__]; 17 | 18 | #define expectFailureMessageRegex(REGEX, BLOCK) \ 19 | [NimbleHelper expectFailureMessageRegex:(REGEX) block:(BLOCK) file: @(__FILE__) line:__LINE__]; 20 | 21 | // Use this when you want to verify the failure message with the nil message postfixed 22 | // to it: " (use beNil() to match nils)" 23 | #define expectNilFailureMessage(MSG, BLOCK) \ 24 | [NimbleHelper expectFailureMessageForNil:(MSG) block:(BLOCK) file:@(__FILE__) line:__LINE__]; 25 | -------------------------------------------------------------------------------- /Tests/NimbleObjectiveCTests/ObjCAllPassTest.m: -------------------------------------------------------------------------------- 1 | #import "NimbleSpecHelper.h" 2 | 3 | @interface ObjCAllPassTest : XCTestCase 4 | 5 | @end 6 | 7 | @implementation ObjCAllPassTest 8 | 9 | - (void)testPositiveMatches { 10 | expect(@[@1, @2, @3,@4]).to(allPass(beLessThan(@5))); 11 | expect(@[@1, @2, @3,@4]).toNot(allPass(beGreaterThan(@5))); 12 | 13 | expect([NSSet setWithArray:@[@1, @2, @3,@4]]).to(allPass(beLessThan(@5))); 14 | expect([NSSet setWithArray:@[@1, @2, @3,@4]]).toNot(allPass(beGreaterThan(@5))); 15 | } 16 | 17 | - (void)testNegativeMatches { 18 | expectFailureMessage(@"expected to all be less than <3>, but failed first at element" 19 | " <3> in <[1, 2, 3, 4]>", ^{ 20 | expect(@[@1, @2, @3, @4]).to(allPass(beLessThan(@3))); 21 | }); 22 | expectFailureMessage(@"expected to not all be less than <5>", ^{ 23 | expect(@[@1, @2, @3, @4]).toNot(allPass(beLessThan(@5))); 24 | }); 25 | expectFailureMessage(@"expected to not all be less than <5>", ^{ 26 | expect([NSSet setWithArray:@[@1, @2, @3, @4]]).toNot(allPass(beLessThan(@5))); 27 | }); 28 | expectFailureMessage(@"allPass can only be used with types which implement NSFastEnumeration " 29 | "(NSArray, NSSet, ...), and whose elements subclass NSObject, got <3>", ^{ 30 | expect(@3).to(allPass(beLessThan(@5))); 31 | }); 32 | expectFailureMessage(@"allPass can only be used with types which implement NSFastEnumeration " 33 | "(NSArray, NSSet, ...), and whose elements subclass NSObject, got <3>", ^{ 34 | expect(@3).toNot(allPass(beLessThan(@5))); 35 | }); 36 | } 37 | @end 38 | -------------------------------------------------------------------------------- /Tests/NimbleObjectiveCTests/ObjCBeAnInstanceOfTest.m: -------------------------------------------------------------------------------- 1 | #import "NimbleSpecHelper.h" 2 | 3 | @interface ObjCBeAnInstanceOfTest : XCTestCase 4 | @end 5 | 6 | @implementation ObjCBeAnInstanceOfTest 7 | 8 | - (void)testPositiveMatches { 9 | NSNull *obj = [NSNull null]; 10 | expect(obj).to(beAnInstanceOf([NSNull class])); 11 | expect(@1).toNot(beAnInstanceOf([NSNull class])); 12 | } 13 | 14 | - (void)testNegativeMatches { 15 | expectFailureMessageRegex(@"expected to be an instance of NSNull, got <[_A-Za-z]+Number instance>", ^{ 16 | expect(@1).to(beAnInstanceOf([NSNull class])); 17 | }); 18 | expectFailureMessage(@"expected to not be an instance of NSNull, got ", ^{ 19 | expect([NSNull null]).toNot(beAnInstanceOf([NSNull class])); 20 | }); 21 | } 22 | 23 | - (void)testNilMatches { 24 | expectNilFailureMessage(@"expected to be an instance of NSNull, got ", ^{ 25 | expect(nil).to(beAnInstanceOf([NSNull class])); 26 | }); 27 | 28 | expectNilFailureMessage(@"expected to not be an instance of NSNull, got ", ^{ 29 | expect(nil).toNot(beAnInstanceOf([NSNull class])); 30 | }); 31 | } 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /Tests/NimbleObjectiveCTests/ObjCBeCloseToTest.m: -------------------------------------------------------------------------------- 1 | #import "NimbleSpecHelper.h" 2 | 3 | @interface ObjCBeCloseToTest : XCTestCase 4 | 5 | @end 6 | 7 | @implementation ObjCBeCloseToTest 8 | 9 | - (void)testPositiveMatches { 10 | expect(@1.2).to(beCloseTo(@1.2001)); 11 | expect(@1.2).to(beCloseTo(@2).within(10)); 12 | expect(@2).toNot(beCloseTo(@1)); 13 | expect(@1.00001).toNot(beCloseTo(@1).within(0.00000001)); 14 | 15 | expect(1.2).to(beCloseTo(1.2001)); 16 | expect(1.2).to(beCloseTo(2).within(10)); 17 | expect(2).toNot(beCloseTo(1)); 18 | expect(1.00001).toNot(beCloseTo(1).within(0.00000001)); 19 | } 20 | 21 | - (void)testNegativeMatches { 22 | expectFailureMessage(@"expected to be close to <0> (within 0.001), got <1>", ^{ 23 | expect(@1).to(beCloseTo(@0)); 24 | }); 25 | expectFailureMessage(@"expected to not be close to <0> (within 0.001), got <0.0001>", ^{ 26 | expect(@(0.0001)).toNot(beCloseTo(@0)); 27 | }); 28 | expectFailureMessage(@"expected to be close to <0> (within 0.001), got <1>", ^{ 29 | expect(1).to(beCloseTo(0)); 30 | }); 31 | expectFailureMessage(@"expected to not be close to <0> (within 0.001), got <0.0001>", ^{ 32 | expect(0.0001).toNot(beCloseTo(0)); 33 | }); 34 | } 35 | 36 | - (void)testNilMatches { 37 | expectNilFailureMessage(@"expected to be close to <0> (within 0.001), got ", ^{ 38 | expect(nil).to(beCloseTo(@0)); 39 | }); 40 | expectNilFailureMessage(@"expected to not be close to <0> (within 0.001), got ", ^{ 41 | expect(nil).toNot(beCloseTo(@0)); 42 | }); 43 | } 44 | 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /Tests/NimbleObjectiveCTests/ObjCBeFalseTest.m: -------------------------------------------------------------------------------- 1 | #import "NimbleSpecHelper.h" 2 | 3 | @interface ObjCBeFalseTest : XCTestCase 4 | 5 | @end 6 | 7 | @implementation ObjCBeFalseTest 8 | 9 | - (void)testPositiveMatches { 10 | expect(@NO).to(beFalse()); 11 | expect(@YES).toNot(beFalse()); 12 | 13 | expect(false).to(beFalse()); 14 | expect(true).toNot(beFalse()); 15 | 16 | expect(NO).to(beFalse()); 17 | expect(YES).toNot(beFalse()); 18 | 19 | expect(10).toNot(beFalse()); 20 | } 21 | 22 | - (void)testNegativeMatches { 23 | expectNilFailureMessage(@"expected to be false, got ", ^{ 24 | expect(nil).to(beFalse()); 25 | }); 26 | expectNilFailureMessage(@"expected to not be false, got ", ^{ 27 | expect(nil).toNot(beFalse()); 28 | }); 29 | 30 | expectFailureMessage(@"expected to be false, got <1>", ^{ 31 | expect(true).to(beFalse()); 32 | }); 33 | expectFailureMessage(@"expected to not be false, got <0>", ^{ 34 | expect(false).toNot(beFalse()); 35 | }); 36 | 37 | expectFailureMessage(@"expected to be false, got <1>", ^{ 38 | expect(YES).to(beFalse()); 39 | }); 40 | expectFailureMessage(@"expected to not be false, got <0>", ^{ 41 | expect(NO).toNot(beFalse()); 42 | }); 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /Tests/NimbleObjectiveCTests/ObjCBeFalsyTest.m: -------------------------------------------------------------------------------- 1 | #import "NimbleSpecHelper.h" 2 | 3 | @interface ObjCBeFalsyTest : XCTestCase 4 | 5 | @end 6 | 7 | @implementation ObjCBeFalsyTest 8 | 9 | - (void)testPositiveMatches { 10 | expect(@NO).to(beFalsy()); 11 | expect(@YES).toNot(beFalsy()); 12 | expect(nil).to(beFalsy()); 13 | 14 | expect(true).toNot(beFalsy()); 15 | expect(false).to(beFalsy()); 16 | 17 | expect(YES).toNot(beFalsy()); 18 | expect(NO).to(beFalsy()); 19 | 20 | expect(10).toNot(beFalsy()); 21 | expect(0).to(beFalsy()); 22 | } 23 | 24 | - (void)testNegativeMatches { 25 | expectFailureMessage(@"expected to not be falsy, got ", ^{ 26 | expect(nil).toNot(beFalsy()); 27 | }); 28 | expectFailureMessage(@"expected to be falsy, got <1>", ^{ 29 | expect(@1).to(beFalsy()); 30 | }); 31 | expectFailureMessage(@"expected to not be falsy, got <0>", ^{ 32 | expect(@NO).toNot(beFalsy()); 33 | }); 34 | 35 | expectFailureMessage(@"expected to be falsy, got <1>", ^{ 36 | expect(true).to(beFalsy()); 37 | }); 38 | expectFailureMessage(@"expected to not be falsy, got <0>", ^{ 39 | expect(false).toNot(beFalsy()); 40 | }); 41 | 42 | expectFailureMessage(@"expected to be falsy, got <1>", ^{ 43 | expect(YES).to(beFalsy()); 44 | }); 45 | expectFailureMessage(@"expected to not be falsy, got <0>", ^{ 46 | expect(NO).toNot(beFalsy()); 47 | }); 48 | 49 | expectFailureMessage(@"expected to be falsy, got <10>", ^{ 50 | expect(10).to(beFalsy()); 51 | }); 52 | expectFailureMessage(@"expected to not be falsy, got <0>", ^{ 53 | expect(0).toNot(beFalsy()); 54 | }); 55 | } 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /Tests/NimbleObjectiveCTests/ObjCBeGreaterThanOrEqualToTest.m: -------------------------------------------------------------------------------- 1 | #import "NimbleSpecHelper.h" 2 | 3 | @interface ObjCBeGreaterThanOrEqualToTest : XCTestCase 4 | 5 | @end 6 | 7 | @implementation ObjCBeGreaterThanOrEqualToTest 8 | 9 | - (void)testPositiveMatches { 10 | expect(@2).to(beGreaterThanOrEqualTo(@2)); 11 | expect(@2).toNot(beGreaterThanOrEqualTo(@3)); 12 | expect(2).to(beGreaterThanOrEqualTo(0)); 13 | expect(2).to(beGreaterThanOrEqualTo(2)); 14 | expect(2).toNot(beGreaterThanOrEqualTo(3)); 15 | expect(2.5).to(beGreaterThanOrEqualTo(2)); 16 | expect(2.5).to(beGreaterThanOrEqualTo(2.5)); 17 | } 18 | 19 | - (void)testNegativeMatches { 20 | expectFailureMessage(@"expected to be greater than or equal to <0>, got <-1>", ^{ 21 | expect(@(-1)).to(beGreaterThanOrEqualTo(@0)); 22 | }); 23 | expectFailureMessage(@"expected to not be greater than or equal to <1>, got <2>", ^{ 24 | expect(@2).toNot(beGreaterThanOrEqualTo(@(1))); 25 | }); 26 | expectFailureMessage(@"expected to be greater than or equal to <0>, got <-1>", ^{ 27 | expect(-1).to(beGreaterThanOrEqualTo(0)); 28 | }); 29 | expectFailureMessage(@"expected to not be greater than or equal to <1>, got <2>", ^{ 30 | expect(2).toNot(beGreaterThanOrEqualTo(1)); 31 | }); 32 | } 33 | 34 | - (void)testNilMatches { 35 | expectNilFailureMessage(@"expected to be greater than or equal to <-1>, got ", ^{ 36 | expect(nil).to(beGreaterThanOrEqualTo(@(-1))); 37 | }); 38 | expectNilFailureMessage(@"expected to not be greater than or equal to <1>, got ", ^{ 39 | expect(nil).toNot(beGreaterThanOrEqualTo(@(1))); 40 | }); 41 | } 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /Tests/NimbleObjectiveCTests/ObjCBeGreaterThanTest.m: -------------------------------------------------------------------------------- 1 | #import "NimbleSpecHelper.h" 2 | 3 | @interface ObjCBeGreaterThanTest : XCTestCase 4 | 5 | @end 6 | 7 | @implementation ObjCBeGreaterThanTest 8 | 9 | - (void)testPositiveMatches { 10 | expect(@2).to(beGreaterThan(@1)); 11 | expect(@2).toNot(beGreaterThan(@2)); 12 | expect(@2).to(beGreaterThan(0)); 13 | expect(@2).toNot(beGreaterThan(2)); 14 | expect(2.5).to(beGreaterThan(1.5)); 15 | } 16 | 17 | - (void)testNegativeMatches { 18 | expectFailureMessage(@"expected to be greater than <0>, got <-1>", ^{ 19 | expect(@(-1)).to(beGreaterThan(@(0))); 20 | }); 21 | expectFailureMessage(@"expected to not be greater than <1>, got <2>", ^{ 22 | expect(@2).toNot(beGreaterThan(@(1))); 23 | }); 24 | expectFailureMessage(@"expected to be greater than <0>, got <-1>", ^{ 25 | expect(-1).to(beGreaterThan(0)); 26 | }); 27 | expectFailureMessage(@"expected to not be greater than <1>, got <2>", ^{ 28 | expect(2).toNot(beGreaterThan(1)); 29 | }); 30 | } 31 | 32 | - (void)testNilMatches { 33 | expectNilFailureMessage(@"expected to be greater than <-1>, got ", ^{ 34 | expect(nil).to(beGreaterThan(@(-1))); 35 | }); 36 | expectNilFailureMessage(@"expected to not be greater than <1>, got ", ^{ 37 | expect(nil).toNot(beGreaterThan(@(1))); 38 | }); 39 | } 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /Tests/NimbleObjectiveCTests/ObjCBeIdenticalToTest.m: -------------------------------------------------------------------------------- 1 | #import "NimbleSpecHelper.h" 2 | 3 | @interface ObjCBeIdenticalToTest : XCTestCase 4 | 5 | @end 6 | 7 | @implementation ObjCBeIdenticalToTest 8 | 9 | - (void)testPositiveMatches { 10 | NSNull *obj = [NSNull null]; 11 | expect(obj).to(beIdenticalTo([NSNull null])); 12 | expect(@2).toNot(beIdenticalTo(@3)); 13 | } 14 | 15 | - (void)testNegativeMatches { 16 | NSNull *obj = [NSNull null]; 17 | expectFailureMessage(([NSString stringWithFormat:@"expected to be identical to <%p>, got <%p>", obj, @2]), ^{ 18 | expect(@2).to(beIdenticalTo(obj)); 19 | }); 20 | expectFailureMessage(([NSString stringWithFormat:@"expected to not be identical to <%p>, got <%p>", obj, obj]), ^{ 21 | expect(obj).toNot(beIdenticalTo(obj)); 22 | }); 23 | } 24 | 25 | - (void)testNilMatches { 26 | NSNull *obj = [NSNull null]; 27 | #pragma clang diagnostic push 28 | #pragma clang diagnostic ignored "-Wnonnull" 29 | expectNilFailureMessage(@"expected to be identical to nil, got nil", ^{ 30 | expect(nil).to(beIdenticalTo(nil)); 31 | }); 32 | #pragma clang diagnostic pop 33 | expectNilFailureMessage(([NSString stringWithFormat:@"expected to not be identical to <%p>, got nil", obj]), ^{ 34 | expect(nil).toNot(beIdenticalTo(obj)); 35 | }); 36 | } 37 | 38 | - (void)testAliasPositiveMatches { 39 | NSNull *obj = [NSNull null]; 40 | expect(obj).to(be([NSNull null])); 41 | expect(@2).toNot(be(@3)); 42 | } 43 | 44 | - (void)testAliasNegativeMatches { 45 | NSNull *obj = [NSNull null]; 46 | expectFailureMessage(([NSString stringWithFormat:@"expected to be identical to <%p>, got <%p>", obj, @2]), ^{ 47 | expect(@2).to(be(obj)); 48 | }); 49 | expectFailureMessage(([NSString stringWithFormat:@"expected to not be identical to <%p>, got <%p>", obj, obj]), ^{ 50 | expect(obj).toNot(be(obj)); 51 | }); 52 | } 53 | 54 | - (void)testAliasNilMatches { 55 | NSNull *obj = [NSNull null]; 56 | #pragma clang diagnostic push 57 | #pragma clang diagnostic ignored "-Wnonnull" 58 | expectNilFailureMessage(@"expected to be identical to nil, got nil", ^{ 59 | expect(nil).to(be(nil)); 60 | }); 61 | #pragma clang diagnostic pop 62 | expectNilFailureMessage(([NSString stringWithFormat:@"expected to not be identical to <%p>, got nil", obj]), ^{ 63 | expect(nil).toNot(be(obj)); 64 | }); 65 | } 66 | 67 | @end 68 | -------------------------------------------------------------------------------- /Tests/NimbleObjectiveCTests/ObjCBeKindOfTest.m: -------------------------------------------------------------------------------- 1 | #import "NimbleSpecHelper.h" 2 | 3 | @interface ObjCBeKindOfTest : XCTestCase 4 | 5 | @end 6 | 7 | @implementation ObjCBeKindOfTest 8 | 9 | - (void)testPositiveMatches { 10 | NSMutableArray *array = [NSMutableArray array]; 11 | expect(array).to(beAKindOf([NSArray class])); 12 | expect(@1).toNot(beAKindOf([NSNull class])); 13 | } 14 | 15 | - (void)testNegativeMatches { 16 | expectFailureMessageRegex(@"expected to be a kind of NSNull, got <[_a-zA-Z]+Number instance>", ^{ 17 | expect(@1).to(beAKindOf([NSNull class])); 18 | }); 19 | expectFailureMessage(@"expected to not be a kind of NSNull, got ", ^{ 20 | expect([NSNull null]).toNot(beAKindOf([NSNull class])); 21 | }); 22 | } 23 | 24 | - (void)testNilMatches { 25 | expectNilFailureMessage(@"expected to be a kind of NSNull, got ", ^{ 26 | expect(nil).to(beAKindOf([NSNull class])); 27 | }); 28 | expectNilFailureMessage(@"expected to not be a kind of NSNull, got ", ^{ 29 | expect(nil).toNot(beAKindOf([NSNull class])); 30 | }); 31 | } 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /Tests/NimbleObjectiveCTests/ObjCBeLessThanOrEqualToTest.m: -------------------------------------------------------------------------------- 1 | #import "NimbleSpecHelper.h" 2 | 3 | @interface ObjCBeLessThanOrEqualToTest : XCTestCase 4 | 5 | @end 6 | 7 | @implementation ObjCBeLessThanOrEqualToTest 8 | 9 | - (void)testPositiveMatches { 10 | expect(@2).to(beLessThanOrEqualTo(@2)); 11 | expect(@2).toNot(beLessThanOrEqualTo(@1)); 12 | expect(2).to(beLessThanOrEqualTo(2)); 13 | expect(2).toNot(beLessThanOrEqualTo(1)); 14 | expect(2).toNot(beLessThanOrEqualTo(0)); 15 | } 16 | 17 | - (void)testNegativeMatches { 18 | expectFailureMessage(@"expected to be less than or equal to <1>, got <2>", ^{ 19 | expect(@2).to(beLessThanOrEqualTo(@1)); 20 | }); 21 | expectFailureMessage(@"expected to not be less than or equal to <1>, got <1>", ^{ 22 | expect(@1).toNot(beLessThanOrEqualTo(@1)); 23 | }); 24 | 25 | expectFailureMessage(@"expected to be less than or equal to <1>, got <2>", ^{ 26 | expect(2).to(beLessThanOrEqualTo(1)); 27 | }); 28 | expectFailureMessage(@"expected to not be less than or equal to <1>, got <1>", ^{ 29 | expect(1).toNot(beLessThanOrEqualTo(1)); 30 | }); 31 | } 32 | 33 | - (void)testNilMatches { 34 | expectNilFailureMessage(@"expected to be less than or equal to <1>, got ", ^{ 35 | expect(nil).to(beLessThanOrEqualTo(@1)); 36 | }); 37 | expectNilFailureMessage(@"expected to not be less than or equal to <-1>, got ", ^{ 38 | expect(nil).toNot(beLessThanOrEqualTo(@(-1))); 39 | }); 40 | } 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /Tests/NimbleObjectiveCTests/ObjCBeLessThanTest.m: -------------------------------------------------------------------------------- 1 | #import "NimbleSpecHelper.h" 2 | 3 | @interface ObjCBeLessThanTest : XCTestCase 4 | 5 | @end 6 | 7 | @implementation ObjCBeLessThanTest 8 | 9 | - (void)testPositiveMatches { 10 | expect(@2).to(beLessThan(@3)); 11 | expect(@2).toNot(beLessThan(@2)); 12 | expect(2).to(beLessThan(3)); 13 | expect(2).toNot(beLessThan(2)); 14 | expect(2).toNot(beLessThan(0)); 15 | } 16 | 17 | - (void)testNegativeMatches { 18 | expectFailureMessage(@"expected to be less than <0>, got <1>", ^{ 19 | expect(@(1)).to(beLessThan(@0)); 20 | }); 21 | expectFailureMessage(@"expected to not be less than <1>, got <0>", ^{ 22 | expect(@0).toNot(beLessThan(@1)); 23 | }); 24 | expectFailureMessage(@"expected to be less than <0>, got <1>", ^{ 25 | expect(1).to(beLessThan(0)); 26 | }); 27 | expectFailureMessage(@"expected to not be less than <1>, got <0>", ^{ 28 | expect(0).toNot(beLessThan(1)); 29 | }); 30 | } 31 | 32 | - (void)testNilMatches { 33 | expectNilFailureMessage(@"expected to be less than <-1>, got ", ^{ 34 | expect(nil).to(beLessThan(@(-1))); 35 | }); 36 | expectNilFailureMessage(@"expected to not be less than <1>, got ", ^{ 37 | expect(nil).toNot(beLessThan(@1)); 38 | }); 39 | } 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /Tests/NimbleObjectiveCTests/ObjCBeNilTest.m: -------------------------------------------------------------------------------- 1 | #import "NimbleSpecHelper.h" 2 | 3 | @interface ObjCBeNilTest : XCTestCase 4 | 5 | @end 6 | 7 | @implementation ObjCBeNilTest 8 | 9 | - (void)testPositiveMatches { 10 | expect(nil).to(beNil()); 11 | expect(@NO).toNot(beNil()); 12 | } 13 | 14 | - (void)testNegativeMatches { 15 | expectFailureMessage(@"expected to be nil, got <1>", ^{ 16 | expect(@1).to(beNil()); 17 | }); 18 | expectFailureMessage(@"expected to not be nil, got ", ^{ 19 | expect(nil).toNot(beNil()); 20 | }); 21 | } 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /Tests/NimbleObjectiveCTests/ObjCBeTrueTest.m: -------------------------------------------------------------------------------- 1 | #import "NimbleSpecHelper.h" 2 | 3 | @interface ObjCBeTrueTest : XCTestCase 4 | 5 | @end 6 | 7 | @implementation ObjCBeTrueTest 8 | 9 | - (void)testPositiveMatches { 10 | expect(@YES).to(beTrue()); 11 | expect(@NO).toNot(beTrue()); 12 | expect(nil).toNot(beTrue()); 13 | 14 | expect(true).to(beTrue()); 15 | expect(false).toNot(beTrue()); 16 | 17 | expect(YES).to(beTrue()); 18 | expect(NO).toNot(beTrue()); 19 | } 20 | 21 | - (void)testNegativeMatches { 22 | expectFailureMessage(@"expected to be true, got <0>", ^{ 23 | expect(@NO).to(beTrue()); 24 | }); 25 | expectFailureMessage(@"expected to be true, got ", ^{ 26 | expect(nil).to(beTrue()); 27 | }); 28 | 29 | expectFailureMessage(@"expected to be true, got <0>", ^{ 30 | expect(false).to(beTrue()); 31 | }); 32 | 33 | expectFailureMessage(@"expected to not be true, got <1>", ^{ 34 | expect(true).toNot(beTrue()); 35 | }); 36 | 37 | expectFailureMessage(@"expected to be true, got <0>", ^{ 38 | expect(NO).to(beTrue()); 39 | }); 40 | 41 | expectFailureMessage(@"expected to not be true, got <1>", ^{ 42 | expect(YES).toNot(beTrue()); 43 | }); 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /Tests/NimbleObjectiveCTests/ObjCBeTruthyTest.m: -------------------------------------------------------------------------------- 1 | #import "NimbleSpecHelper.h" 2 | 3 | @interface ObjCBeTruthyTest : XCTestCase 4 | 5 | @end 6 | 7 | @implementation ObjCBeTruthyTest 8 | 9 | - (void)testPositiveMatches { 10 | expect(@YES).to(beTruthy()); 11 | expect(@NO).toNot(beTruthy()); 12 | expect(nil).toNot(beTruthy()); 13 | 14 | expect(true).to(beTruthy()); 15 | expect(false).toNot(beTruthy()); 16 | 17 | expect(YES).to(beTruthy()); 18 | expect(NO).toNot(beTruthy()); 19 | 20 | expect(10).to(beTruthy()); 21 | expect(0).toNot(beTruthy()); 22 | } 23 | 24 | - (void)testNegativeMatches { 25 | expectFailureMessage(@"expected to be truthy, got ", ^{ 26 | expect(nil).to(beTruthy()); 27 | }); 28 | expectFailureMessage(@"expected to not be truthy, got <1>", ^{ 29 | expect(@1).toNot(beTruthy()); 30 | }); 31 | expectFailureMessage(@"expected to be truthy, got <0>", ^{ 32 | expect(@NO).to(beTruthy()); 33 | }); 34 | expectFailureMessage(@"expected to be truthy, got <0>", ^{ 35 | expect(false).to(beTruthy()); 36 | }); 37 | expectFailureMessage(@"expected to not be truthy, got <1>", ^{ 38 | expect(true).toNot(beTruthy()); 39 | }); 40 | expectFailureMessage(@"expected to be truthy, got <0>", ^{ 41 | expect(NO).to(beTruthy()); 42 | }); 43 | expectFailureMessage(@"expected to not be truthy, got <1>", ^{ 44 | expect(YES).toNot(beTruthy()); 45 | }); 46 | expectFailureMessage(@"expected to not be truthy, got <10>", ^{ 47 | expect(10).toNot(beTruthy()); 48 | }); 49 | expectFailureMessage(@"expected to be truthy, got <0>", ^{ 50 | expect(0).to(beTruthy()); 51 | }); 52 | } 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /Tests/NimbleObjectiveCTests/ObjCBeginWithTest.m: -------------------------------------------------------------------------------- 1 | #import "NimbleSpecHelper.h" 2 | 3 | @interface ObjCBeginWithTest : XCTestCase 4 | 5 | @end 6 | 7 | @implementation ObjCBeginWithTest 8 | 9 | - (void)testPositiveMatches { 10 | expect(@"hello world!").to(beginWith(@"hello")); 11 | expect(@"hello world!").toNot(beginWith(@"world")); 12 | 13 | NSArray *array = @[@1, @2]; 14 | expect(array).to(beginWith(@1)); 15 | expect(array).toNot(beginWith(@2)); 16 | } 17 | 18 | - (void)testNegativeMatches { 19 | expectFailureMessage(@"expected to begin with , got ", ^{ 20 | expect(@"foo").to(beginWith(@"bar")); 21 | }); 22 | expectFailureMessage(@"expected to not begin with , got ", ^{ 23 | expect(@"foo").toNot(beginWith(@"foo")); 24 | }); 25 | } 26 | 27 | - (void)testNilMatches { 28 | expectNilFailureMessage(@"expected to begin with <1>, got ", ^{ 29 | expect(nil).to(beginWith(@1)); 30 | }); 31 | expectNilFailureMessage(@"expected to not begin with <1>, got ", ^{ 32 | expect(nil).toNot(beginWith(@1)); 33 | }); 34 | } 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /Tests/NimbleObjectiveCTests/ObjCContainElementSatisfyingTest.m: -------------------------------------------------------------------------------- 1 | #import "NimbleSpecHelper.h" 2 | 3 | @interface ObjCContainElementSatisfyingTest : XCTestCase 4 | 5 | @end 6 | 7 | @implementation ObjCContainElementSatisfyingTest 8 | 9 | - (void)testPassingMatches { 10 | NSArray *orderIndifferentArray = @[@1, @2, @3]; 11 | expect(orderIndifferentArray).to(containElementSatisfying(^BOOL(id object) { 12 | return [object isEqualToNumber:@1]; 13 | })); 14 | expect(orderIndifferentArray).to(containElementSatisfying(^BOOL(id object) { 15 | return [object isEqualToNumber:@2]; 16 | })); 17 | expect(orderIndifferentArray).to(containElementSatisfying(^BOOL(id object) { 18 | return [object isEqualToNumber:@3]; 19 | })); 20 | 21 | orderIndifferentArray = @[@3, @1, @2]; 22 | expect(orderIndifferentArray).to(containElementSatisfying(^BOOL(id object) { 23 | return [object isEqualToNumber:@1]; 24 | })); 25 | expect(orderIndifferentArray).to(containElementSatisfying(^BOOL(id object) { 26 | return [object isEqualToNumber:@2]; 27 | })); 28 | expect(orderIndifferentArray).to(containElementSatisfying(^BOOL(id object) { 29 | return [object isEqualToNumber:@3]; 30 | })); 31 | 32 | NSSet *orderIndifferentSet = [NSSet setWithObjects:@"turtle test", @"turtle assessment", nil]; 33 | expect(orderIndifferentSet).to(containElementSatisfying(^BOOL(id object) { 34 | return [object isEqualToString:@"turtle assessment"]; 35 | })); 36 | } 37 | 38 | - (void)testFailingMatches { 39 | expectFailureMessage(@"expected to find object in collection that satisfies matcher", ^{ 40 | expect(@[@1]).to(containElementSatisfying(^BOOL(id object) { 41 | return [object isEqualToNumber:@2]; 42 | })); 43 | }); 44 | expectFailureMessage(@"containElementSatisfying must be provided an NSFastEnumeration object", ^{ 45 | expect((nil)).to(containElementSatisfying(^BOOL(id object) { 46 | return [object isEqualToNumber:@3]; 47 | })); 48 | }); 49 | expectFailureMessage(@"containElementSatisfying must be provided an NSFastEnumeration object", ^{ 50 | expect((@3)).to(containElementSatisfying(^BOOL(id object) { 51 | return [object isEqualToNumber:@3]; 52 | })); 53 | }); 54 | } 55 | 56 | - (void)testNegativeCases { 57 | NSArray *orderIndifferentArray = @[@"puppies", @"kittens", @"turtles"]; 58 | expect(orderIndifferentArray).toNot(containElementSatisfying(^BOOL(id object) { 59 | return [object isEqualToString:@"armadillos"]; 60 | })); 61 | } 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /Tests/NimbleObjectiveCTests/ObjCContainTest.m: -------------------------------------------------------------------------------- 1 | #import "NimbleSpecHelper.h" 2 | 3 | @interface ObjCContainTest : XCTestCase 4 | 5 | @end 6 | 7 | @implementation ObjCContainTest 8 | 9 | - (void)testPositiveMatches { 10 | NSArray *array = @[@1, @2]; 11 | expect(array).to(contain(@1)); 12 | expect(array).toNot(contain(@"HI")); 13 | expect(@"String").to(contain(@"Str")); 14 | expect(@"Other").toNot(contain(@"Str")); 15 | } 16 | 17 | - (void)testNegativeMatches { 18 | expectFailureMessage(@"expected to contain <3>, got <(1, 2)>", ^{ 19 | expect((@[@1, @2])).to(contain(@3)); 20 | }); 21 | expectFailureMessage(@"expected to not contain <2>, got <(1, 2)>", ^{ 22 | expect((@[@1, @2])).toNot(contain(@2)); 23 | }); 24 | 25 | expectFailureMessage(@"expected to contain , got ", ^{ 26 | expect(@"la").to(contain(@"hi")); 27 | }); 28 | expectFailureMessage(@"expected to not contain , got ", ^{ 29 | expect(@"hihihi").toNot(contain(@"hi")); 30 | }); 31 | } 32 | 33 | - (void)testNilMatches { 34 | expectNilFailureMessage(@"expected to contain <3>, got ", ^{ 35 | expect(nil).to(contain(@3)); 36 | }); 37 | expectNilFailureMessage(@"expected to not contain <3>, got ", ^{ 38 | expect(nil).toNot(contain(@3)); 39 | }); 40 | 41 | expectNilFailureMessage(@"expected to contain , got ", ^{ 42 | expect(nil).to(contain(@"hi")); 43 | }); 44 | expectNilFailureMessage(@"expected to not contain , got ", ^{ 45 | expect(nil).toNot(contain(@"hi")); 46 | }); 47 | } 48 | 49 | - (void)testVariadicArguments { 50 | NSArray *array = @[@1, @2]; 51 | expect(array).to(contain(@1, @2)); 52 | expect(array).toNot(contain(@"HI", @"whale")); 53 | expect(@"String").to(contain(@"Str", @"ng")); 54 | expect(@"Other").toNot(contain(@"Str", @"Oth")); 55 | 56 | 57 | expectFailureMessage(@"expected to contain , got <(a, b, c)>", ^{ 58 | expect(@[@"a", @"b", @"c"]).to(contain(@"a", @"bar")); 59 | }); 60 | 61 | expectFailureMessage(@"expected to not contain , got <(a, b, c)>", ^{ 62 | expect(@[@"a", @"b", @"c"]).toNot(contain(@"a", @"b")); 63 | }); 64 | } 65 | 66 | - (void)testUnsupportedTypes { 67 | expectFailureMessage(@"expected to contain (only works for NSArrays, NSSets, NSHashTables, and NSStrings), got <1>", ^{ 68 | expect(@1).to(contain(@"foo")); 69 | }); 70 | expectFailureMessage(@"expected to not contain (only works for NSArrays, NSSets, NSHashTables, and NSStrings), got <1>", ^{ 71 | expect(@1).toNot(contain(@"foo")); 72 | }); 73 | } 74 | 75 | @end 76 | -------------------------------------------------------------------------------- /Tests/NimbleObjectiveCTests/ObjCEndWithTest.m: -------------------------------------------------------------------------------- 1 | #import "NimbleSpecHelper.h" 2 | 3 | @interface ObjCEndWithTest : XCTestCase 4 | 5 | @end 6 | 7 | @implementation ObjCEndWithTest 8 | 9 | - (void)testPositiveMatches { 10 | NSArray *array = @[@1, @2]; 11 | expect(@"hello world!").to(endWith(@"world!")); 12 | expect(@"hello world!").toNot(endWith(@"hello")); 13 | expect(array).to(endWith(@2)); 14 | expect(array).toNot(endWith(@1)); 15 | } 16 | 17 | - (void)testNegativeMatches { 18 | expectFailureMessage(@"expected to end with , got ", ^{ 19 | expect(@"hello world!").to(endWith(@"?")); 20 | }); 21 | expectFailureMessage(@"expected to not end with , got ", ^{ 22 | expect(@"hello world!").toNot(endWith(@"!")); 23 | }); 24 | } 25 | 26 | - (void)testNilMatches { 27 | expectNilFailureMessage(@"expected to end with <1>, got ", ^{ 28 | expect(nil).to(endWith(@1)); 29 | }); 30 | expectNilFailureMessage(@"expected to not end with <1>, got ", ^{ 31 | expect(nil).toNot(endWith(@1)); 32 | }); 33 | } 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /Tests/NimbleObjectiveCTests/ObjCMatchTest.m: -------------------------------------------------------------------------------- 1 | #import "NimbleSpecHelper.h" 2 | 3 | @interface ObjCMatchTest : XCTestCase 4 | 5 | @end 6 | 7 | @implementation ObjCMatchTest 8 | 9 | - (void)testPositiveMatches { 10 | expect(@"11:14").to(match(@"\\d{2}:\\d{2}")); 11 | expect(@"hello").toNot(match(@"\\d{2}:\\d{2}")); 12 | } 13 | 14 | - (void)testNegativeMatches { 15 | expectFailureMessage(@"expected to match <\\d{2}:\\d{2}>, got ", ^{ 16 | expect(@"hello").to(match(@"\\d{2}:\\d{2}")); 17 | }); 18 | expectFailureMessage(@"expected to not match <\\d{2}:\\d{2}>, got <11:22>", ^{ 19 | expect(@"11:22").toNot(match(@"\\d{2}:\\d{2}")); 20 | }); 21 | } 22 | 23 | - (void)testNilMatches { 24 | expectNilFailureMessage(@"expected to match <\\d{2}:\\d{2}>, got ", ^{ 25 | expect(nil).to(match(@"\\d{2}:\\d{2}")); 26 | }); 27 | expectNilFailureMessage(@"expected to not match <\\d{2}:\\d{2}>, got ", ^{ 28 | expect(nil).toNot(match(@"\\d{2}:\\d{2}")); 29 | }); 30 | } 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /Tests/NimbleObjectiveCTests/ObjCSatisfyAllOfTest.m: -------------------------------------------------------------------------------- 1 | #import "NimbleSpecHelper.h" 2 | 3 | @interface ObjCSatisfyAllOfTest : XCTestCase 4 | 5 | @end 6 | 7 | @implementation ObjCSatisfyAllOfTest 8 | 9 | - (void)testPositiveMatches { 10 | expect(@2).to(satisfyAllOf(equal(@2), beLessThan(@3))); 11 | expect(@2).toNot(satisfyAllOf(equal(@3), equal(@16))); 12 | expect(@[@1, @2, @3]).to(satisfyAllOf(equal(@[@1, @2, @3]), allPass(beLessThan(@4)))); 13 | expect(@NO).toNot(satisfyAllOf(beTrue(), beFalse())); 14 | expect(@YES).toNot(satisfyAllOf(beTrue(), beFalse())); 15 | } 16 | 17 | - (void)testNegativeMatches { 18 | expectFailureMessage(@"expected to match all of: {equal <3>}, and {equal <4>}, and {equal <5>}, got 2", ^{ 19 | expect(@2).to(satisfyAllOf(equal(@3), equal(@4), equal(@5))); 20 | }); 21 | 22 | expectFailureMessage(@"expected to match all of: {all be less than <4>, but failed first at element" 23 | " <5> in <[5, 6, 7]>}, and {equal <(1, 2, 3, 4)>}, got (5,6,7)", ^{ 24 | expect(@[@5, @6, @7]).to(satisfyAllOf(allPass(beLessThan(@4)), equal(@[@1, @2, @3, @4]))); 25 | }); 26 | 27 | expectFailureMessage(@"satisfyAllOf must be called with at least one matcher", ^{ 28 | expect(@"turtles").to(satisfyAllOf()); 29 | }); 30 | } 31 | @end 32 | -------------------------------------------------------------------------------- /Tests/NimbleObjectiveCTests/ObjCSatisfyAnyOfTest.m: -------------------------------------------------------------------------------- 1 | #import "NimbleSpecHelper.h" 2 | 3 | @interface ObjCSatisfyAnyOfTest : XCTestCase 4 | 5 | @end 6 | 7 | @implementation ObjCSatisfyAnyOfTest 8 | 9 | - (void)testPositiveMatches { 10 | expect(@2).to(satisfyAnyOf(equal(@2), equal(@3))); 11 | expect(@2).toNot(satisfyAnyOf(equal(@3), equal(@16))); 12 | expect(@[@1, @2, @3]).to(satisfyAnyOf(equal(@[@1, @2, @3]), allPass(beLessThan(@4)))); 13 | expect(@NO).to(satisfyAnyOf(beTrue(), beFalse())); 14 | expect(@YES).to(satisfyAnyOf(beTrue(), beFalse())); 15 | } 16 | 17 | - (void)testNegativeMatches { 18 | expectFailureMessage(@"expected to match one of: {equal <3>}, or {equal <4>}, or {equal <5>}, got 2", ^{ 19 | expect(@2).to(satisfyAnyOf(equal(@3), equal(@4), equal(@5))); 20 | }); 21 | 22 | expectFailureMessage(@"expected to match one of: {all be less than <4>, but failed first at element" 23 | " <5> in <[5, 6, 7]>}, or {equal <(1, 2, 3, 4)>}, got (5,6,7)", ^{ 24 | expect(@[@5, @6, @7]).to(satisfyAnyOf(allPass(beLessThan(@4)), equal(@[@1, @2, @3, @4]))); 25 | }); 26 | 27 | expectFailureMessage(@"satisfyAnyOf must be called with at least one matcher", ^{ 28 | expect(@"turtles").to(satisfyAnyOf()); 29 | }); 30 | } 31 | @end 32 | -------------------------------------------------------------------------------- /Tests/NimbleObjectiveCTests/ObjCSyncTest.m: -------------------------------------------------------------------------------- 1 | #import "NimbleSpecHelper.h" 2 | 3 | @interface ObjCSyncTest : XCTestCase 4 | 5 | @end 6 | 7 | @implementation ObjCSyncTest 8 | 9 | - (void)testFailureExpectation { 10 | expectFailureMessage(@"fail() always fails", ^{ 11 | fail(); 12 | }); 13 | 14 | expectFailureMessage(@"This always fails", ^{ 15 | failWithMessage(@"This always fails"); 16 | }); 17 | } 18 | 19 | #pragma mark - Assertion chaining 20 | 21 | - (void)testChain { 22 | expect(@2).toNot(equal(@1)).to(equal(@2)).notTo(equal(@3)); 23 | } 24 | 25 | - (void)testChainFail { 26 | expectFailureMessages((@[@"expected to not equal <2>, got <2>", @"expected to equal <3>, got <2>"]), ^{ 27 | expect(@2).toNot(equal(@1)).toNot(equal(@2)).to(equal(@3)); 28 | }); 29 | } 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /Tests/NimbleObjectiveCTests/ObjCUserDescriptionTest.m: -------------------------------------------------------------------------------- 1 | #import "NimbleSpecHelper.h" 2 | 3 | @interface ObjCUserDescriptionTest : XCTestCase 4 | 5 | @end 6 | 7 | @implementation ObjCUserDescriptionTest 8 | 9 | - (void)testToWithDescription { 10 | expectFailureMessage(@"These are equal!\n" 11 | "expected to equal <2>, got <1>", ^{ 12 | expect(@1).toWithDescription(equal(@2), @"These are equal!"); 13 | }); 14 | } 15 | 16 | - (void)testToNotWithDescription { 17 | expectFailureMessage(@"These aren't equal!\n" 18 | "expected to not equal <1>, got <1>", ^{ 19 | expect(@1).toNotWithDescription(equal(@1), @"These aren't equal!"); 20 | }); 21 | } 22 | 23 | - (void)testNotToWithDescription { 24 | expectFailureMessage(@"These aren't equal!\n" 25 | "expected to not equal <1>, got <1>", ^{ 26 | expect(@1).notToWithDescription(equal(@1), @"These aren't equal!"); 27 | }); 28 | } 29 | 30 | - (void)testToEventuallyWithDescription { 31 | expectFailureMessage(@"These are equal!\n" 32 | "expected to eventually equal <2>, got <1>", ^{ 33 | expect(@1).toEventuallyWithDescription(equal(@2), @"These are equal!"); 34 | }); 35 | } 36 | 37 | - (void)testToEventuallyNotWithDescription { 38 | expectFailureMessage(@"These aren't equal!\n" 39 | "expected to eventually not equal <1>, got <1>", ^{ 40 | expect(@1).toEventuallyNotWithDescription(equal(@1), @"These aren't equal!"); 41 | }); 42 | } 43 | 44 | - (void)testToNotEventuallyWithDescription { 45 | expectFailureMessage(@"These aren't equal!\n" 46 | "expected to eventually not equal <1>, got <1>", ^{ 47 | expect(@1).toNotEventuallyWithDescription(equal(@1), @"These aren't equal!"); 48 | }); 49 | } 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /Tests/NimbleObjectiveCTests/ObjcStringersTest.m: -------------------------------------------------------------------------------- 1 | #import "NimbleSpecHelper.h" 2 | 3 | @interface ObjcStringersTest : XCTestCase 4 | 5 | @end 6 | 7 | @implementation ObjcStringersTest 8 | 9 | - (void)testItCanStringifyArrays { 10 | NSArray *array = @[@1, @2, @3]; 11 | NSString *result = NMBStringify(array); 12 | 13 | expect(result).to(equal(@"(1, 2, 3)")); 14 | } 15 | 16 | - (void)testItCanStringifyIndexSets { 17 | NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(1, 3)]; 18 | NSString *result = NMBStringify(indexSet); 19 | 20 | expect(result).to(equal(@"(1, 2, 3)")); 21 | } 22 | 23 | - (void)testItRoundsLongDecimals { 24 | NSNumber *num = @291.123782163; 25 | NSString *result = NMBStringify(num); 26 | 27 | expect(result).to(equal(@"291.1238")); 28 | } 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /Tests/NimbleTests/AsyncPromiseTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Foundation 3 | @testable import Nimble 4 | 5 | final class AsyncPromiseTest: XCTestCase { 6 | func testSuspendsUntilValueIsSent() async { 7 | let promise = AsyncPromise() 8 | 9 | async let value = promise.value 10 | 11 | promise.send(3) 12 | 13 | let received = await value 14 | expect(received).to(equal(3)) 15 | } 16 | 17 | func testIgnoresFutureValuesSent() async { 18 | let promise = AsyncPromise() 19 | 20 | promise.send(3) 21 | promise.send(4) 22 | 23 | await expecta(await promise.value).to(equal(3)) 24 | } 25 | 26 | func testAllowsValueToBeBackpressured() async { 27 | let promise = AsyncPromise() 28 | 29 | promise.send(3) 30 | 31 | await expecta(await promise.value).to(equal(3)) 32 | } 33 | 34 | func testSupportsMultipleAwaiters() async { 35 | let promise = AsyncPromise() 36 | 37 | async let values = await withTaskGroup(of: Int.self, returning: [Int].self) { taskGroup in 38 | for _ in 0..<10 { 39 | taskGroup.addTask { 40 | await promise.value 41 | } 42 | } 43 | 44 | var values = [Int]() 45 | 46 | for await value in taskGroup { 47 | values.append(value) 48 | } 49 | 50 | return values 51 | } 52 | 53 | promise.send(4) 54 | 55 | let received = await values 56 | 57 | expect(received).to(equal(Array(repeating: 4, count: 10))) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Tests/NimbleTests/Helpers/AsyncHelpers.swift: -------------------------------------------------------------------------------- 1 | import Nimble 2 | import XCTest 3 | #if SWIFT_PACKAGE 4 | import NimbleSharedTestHelpers 5 | #endif 6 | 7 | func asyncEqual(_ expectedValue: T) -> AsyncMatcher { 8 | AsyncMatcher.define { expression in 9 | let message = ExpectationMessage.expectedActualValueTo("equal \(expectedValue)") 10 | if let value = try await expression.evaluate() { 11 | return MatcherResult(bool: value == expectedValue, message: message) 12 | } else { 13 | return MatcherResult(status: .fail, message: message.appendedBeNilHint()) 14 | } 15 | } 16 | } 17 | 18 | func asyncContain(_ items: S.Element...) -> AsyncMatcher where S.Element: Equatable { 19 | return asyncContain(items) 20 | } 21 | 22 | func asyncContain(_ items: [S.Element]) -> AsyncMatcher where S.Element: Equatable { 23 | return AsyncMatcher.simple("contain <\(String(describing: items))>") { actualExpression in 24 | guard let actual = try await actualExpression.evaluate() else { return .fail } 25 | 26 | let matches = items.allSatisfy { 27 | return actual.contains($0) 28 | } 29 | return MatcherStatus(bool: matches) 30 | } 31 | } 32 | 33 | func asyncBeCloseTo( 34 | _ expectedValue: Value 35 | ) -> AsyncMatcher { 36 | let delta: Value = 1/10000 37 | let errorMessage = "be close to <\(stringify(expectedValue))> (within \(stringify(delta)))" 38 | return AsyncMatcher.simple(errorMessage) { actualExpression in 39 | guard let actualValue = try await actualExpression.evaluate() else { 40 | return .doesNotMatch 41 | } 42 | 43 | return MatcherStatus(bool: abs(actualValue - expectedValue) < delta) 44 | } 45 | } 46 | 47 | func asyncEqualityCheck(_ received: T, _ expected: T) async -> Bool { 48 | received == expected 49 | } 50 | 51 | final class AsyncMatchersTest: XCTestCase { 52 | func testAsyncEqual() async { 53 | await expect(1).to(asyncEqual(1)) 54 | await expect(2).toNot(asyncEqual(1)) 55 | 56 | await failsWithErrorMessage("expected to equal 1, got <2>") { 57 | await expect(2).to(asyncEqual(1)) 58 | } 59 | } 60 | 61 | func testAsyncContain() async { 62 | await expect([1, 2, 3]).to(asyncContain(1)) 63 | 64 | await expect([1, 2, 3]).to(asyncContain(1, 2)) 65 | await expect([1, 2, 3]).to(asyncContain([1, 2])) 66 | 67 | await expect([1, 2, 3]).to(asyncContain(2, 1)) 68 | await expect([1, 2, 3]).to(asyncContain([2, 1])) 69 | 70 | await expect([1, 2, 3]).toNot(asyncContain(4)) 71 | 72 | await expect([1, 2, 3]).toNot(asyncContain(4, 2)) 73 | await expect([1, 2, 3]).toNot(asyncContain([4, 2])) 74 | 75 | await expect([1, 2, 3]).toNot(asyncContain(2, 4)) 76 | await expect([1, 2, 3]).toNot(asyncContain([2, 4])) 77 | } 78 | 79 | func testAsyncBeCloseTo() async { 80 | await expect(1.2).to(asyncBeCloseTo(1.2001)) 81 | await expect(1.2 as CDouble).to(asyncBeCloseTo(1.2001)) 82 | await expect(1.2 as Float).to(asyncBeCloseTo(1.2001)) 83 | 84 | await failsWithErrorMessage("expected to not be close to <1.2001> (within 0.0001), got <1.2>") { 85 | await expect(1.2).toNot(asyncBeCloseTo(1.2001)) 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Tests/NimbleTests/Helpers/BackgroundThreadObject.swift: -------------------------------------------------------------------------------- 1 | #if !os(WASI) 2 | 3 | import Foundation 4 | import Nimble 5 | 6 | // Simulates an object that *really* cares what thread it is run on. 7 | // Supposed to replicate what happens if a NSManagedObject ends up 8 | // in a notification and is picked up by Nimble 9 | class BackgroundThreadObject: CustomDebugStringConvertible { 10 | var debugDescription: String { 11 | if Thread.isMainThread { 12 | fail("This notification was accessed on the main thread when it should have been handled on the thread it was received on") 13 | } 14 | return "BackgroundThreadObject" 15 | } 16 | } 17 | 18 | #endif // #if !os(WASI) 19 | -------------------------------------------------------------------------------- /Tests/NimbleTests/Helpers/ObjectWithLazyProperty.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | class ObjectWithLazyProperty { 4 | init() {} 5 | lazy var value: String = "hello" 6 | lazy var anotherValue: String = { return "world" }() 7 | } 8 | -------------------------------------------------------------------------------- /Tests/NimbleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Tests/NimbleTests/LinuxSupport.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | #if os(Linux) 4 | extension NSNotification.Name { 5 | init(_ rawValue: String) { 6 | self.init(rawValue: rawValue) 7 | } 8 | } 9 | #endif 10 | -------------------------------------------------------------------------------- /Tests/NimbleTests/Matchers/AlwaysFailMatcher.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Nimble 3 | #if SWIFT_PACKAGE 4 | import NimbleSharedTestHelpers 5 | #endif 6 | 7 | func alwaysFail() -> Nimble.Matcher { 8 | return Matcher { _ throws -> MatcherResult in 9 | return MatcherResult(status: .fail, message: .fail("This matcher should always fail")) 10 | } 11 | } 12 | 13 | func asyncAlwaysFail() -> AsyncMatcher { 14 | return AsyncMatcher { _ throws -> MatcherResult in 15 | return MatcherResult(status: .fail, message: .fail("This matcher should always fail")) 16 | } 17 | } 18 | 19 | final class AlwaysFailTest: XCTestCase { 20 | func testAlwaysFail() { 21 | failsWithErrorMessage( 22 | "This matcher should always fail") { 23 | expect(true).toNot(alwaysFail()) 24 | } 25 | 26 | failsWithErrorMessage( 27 | "This matcher should always fail") { 28 | expect(true).to(alwaysFail()) 29 | } 30 | } 31 | 32 | func testAsyncAlwaysFail() async { 33 | await failsWithErrorMessage( 34 | "This matcher should always fail") { 35 | await expect(true).toNot(asyncAlwaysFail()) 36 | } 37 | 38 | await failsWithErrorMessage( 39 | "This matcher should always fail") { 40 | await expect(true).to(asyncAlwaysFail()) 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Tests/NimbleTests/Matchers/AsyncPredicateTest.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import XCTest 3 | import Nimble 4 | #if SWIFT_PACKAGE 5 | import NimbleSharedTestHelpers 6 | #endif 7 | 8 | private func beCalled(times: UInt) -> AsyncMatcher { 9 | AsyncMatcher.define { expression in 10 | let message = ExpectationMessage.expectedActualValueTo("be called \(times) times") 11 | if let value = try await expression.evaluate()?.callCount { 12 | return MatcherResult(bool: value == times, message: message) 13 | } else { 14 | return MatcherResult(status: .fail, message: message.appendedBeNilHint()) 15 | } 16 | } 17 | } 18 | 19 | private actor CallCounter { 20 | var callCount: UInt = 0 21 | 22 | func call() { 23 | callCount += 1 24 | } 25 | } 26 | 27 | private func asyncFunction(value: T) async -> T { return value } 28 | 29 | final class AsyncMatcherTest: XCTestCase { 30 | func testAsyncMatchersWithAsyncExpectations() async { 31 | await expecta(await asyncFunction(value: 1)).to(asyncEqual(1)) 32 | } 33 | 34 | func testAsyncMatchersWithSyncExpectations() async { 35 | let subject = CallCounter() 36 | await subject.call() 37 | await expects(subject).to(beCalled(times: 1)) 38 | } 39 | 40 | #if !os(WASI) 41 | func testAsyncPollingWithAsyncMatchers() async { 42 | let subject = CallCounter() 43 | 44 | await expect { 45 | await subject.call() 46 | return subject 47 | }.toEventually(beCalled(times: 3)) 48 | 49 | await expect { 50 | await asyncFunction(value: 1) 51 | }.toEventuallyNot(asyncEqual(0)) 52 | 53 | await expect { await asyncFunction(value: 1) }.toNever(asyncEqual(0)) 54 | await expect { await asyncFunction(value: 1) }.toAlways(asyncEqual(1)) 55 | } 56 | 57 | func testSyncPollingWithAsyncMatchers() async { 58 | await expects(1).toEventually(asyncEqual(1)) 59 | await expects(1).toAlways(asyncEqual(1)) 60 | await expects(1).toEventuallyNot(asyncEqual(0)) 61 | await expects(1).toNever(asyncEqual(0)) 62 | } 63 | #endif 64 | } 65 | -------------------------------------------------------------------------------- /Tests/NimbleTests/Matchers/BeGreaterThanOrEqualToTest.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import XCTest 3 | import Nimble 4 | #if SWIFT_PACKAGE 5 | import NimbleSharedTestHelpers 6 | #endif 7 | 8 | final class BeGreaterThanOrEqualToTest: XCTestCase { 9 | func testGreaterThanOrEqualTo() { 10 | expect(10).to(beGreaterThanOrEqualTo(10)) 11 | expect(10).to(beGreaterThanOrEqualTo(2)) 12 | expect(1).toNot(beGreaterThanOrEqualTo(2)) 13 | expect(1 as NSNumber).toNot(beGreaterThanOrEqualTo(2)) 14 | expect(2 as NSNumber).to(beGreaterThanOrEqualTo(2 as NSNumber)) 15 | expect(1).to(beGreaterThanOrEqualTo(0 as NSNumber)) 16 | 17 | failsWithErrorMessage("expected to be greater than or equal to <2>, got <0>") { 18 | expect(0).to(beGreaterThanOrEqualTo(2)) 19 | return 20 | } 21 | failsWithErrorMessage("expected to not be greater than or equal to <1>, got <1>") { 22 | expect(1).toNot(beGreaterThanOrEqualTo(1)) 23 | return 24 | } 25 | failsWithErrorMessageForNil("expected to be greater than or equal to <-2>, got ") { 26 | expect(nil as Int?).to(beGreaterThanOrEqualTo(-2)) 27 | } 28 | failsWithErrorMessageForNil("expected to not be greater than or equal to <1>, got ") { 29 | expect(nil as Int?).toNot(beGreaterThanOrEqualTo(1)) 30 | } 31 | } 32 | 33 | func testGreaterThanOrEqualToOperator() { 34 | expect(0) >= 0 35 | expect(1) >= 0 36 | expect(1 as NSNumber) >= 1 37 | expect(1 as NSNumber) >= 1 as NSNumber 38 | expect(2.5) >= 2.5 39 | expect(2.5) >= 2 40 | expect(Float(2.5)) >= Float(2.5) 41 | expect(Float(2.5)) >= 2 42 | 43 | failsWithErrorMessage("expected to be greater than or equal to <2>, got <1>") { 44 | expect(1) >= 2 45 | return 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Tests/NimbleTests/Matchers/BeGreaterThanTest.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import XCTest 3 | import Nimble 4 | #if SWIFT_PACKAGE 5 | import NimbleSharedTestHelpers 6 | #endif 7 | 8 | final class BeGreaterThanTest: XCTestCase { 9 | func testGreaterThan() { 10 | expect(10).to(beGreaterThan(2)) 11 | expect(1).toNot(beGreaterThan(2)) 12 | expect(3 as NSNumber).to(beGreaterThan(2 as NSNumber)) 13 | expect(1 as NSNumber).toNot(beGreaterThan(2 as NSNumber)) 14 | 15 | failsWithErrorMessage("expected to be greater than <2>, got <0>") { 16 | expect(0).to(beGreaterThan(2)) 17 | } 18 | failsWithErrorMessage("expected to not be greater than <0>, got <1>") { 19 | expect(1).toNot(beGreaterThan(0)) 20 | } 21 | failsWithErrorMessageForNil("expected to be greater than <-2>, got ") { 22 | expect(nil as Int?).to(beGreaterThan(-2)) 23 | } 24 | failsWithErrorMessageForNil("expected to not be greater than <0>, got ") { 25 | expect(nil as Int?).toNot(beGreaterThan(0)) 26 | } 27 | } 28 | 29 | func testGreaterThanOperator() { 30 | expect(1) > 0 31 | expect(1 as NSNumber) > 0 as NSNumber 32 | expect(1 as NSNumber) > 0 as NSNumber 33 | expect(2.5) > 1.5 34 | expect(Float(2.5)) > Float(1.5) 35 | 36 | failsWithErrorMessage("expected to be greater than <2>, got <1>") { 37 | expect(1) > 2 38 | return 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Tests/NimbleTests/Matchers/BeIdenticalToObjectTest.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import XCTest 3 | import Nimble 4 | #if SWIFT_PACKAGE 5 | import NimbleSharedTestHelpers 6 | #endif 7 | 8 | final class BeIdenticalToObjectTest: XCTestCase { 9 | private class BeIdenticalToObjectTester {} 10 | private let testObjectA = BeIdenticalToObjectTester() 11 | private let testObjectB = BeIdenticalToObjectTester() 12 | 13 | func testBeIdenticalToPositive() { 14 | expect(self.testObjectA).to(beIdenticalTo(testObjectA)) 15 | } 16 | 17 | func testbeIdenticalToAsSubmatcher() { 18 | // check that the typing works out when used as a submatcher. 19 | expect(self.testObjectA).to(map({ $0 }, be(testObjectA))) 20 | expect(self.testObjectA).to(map({ $0 }, beIdenticalTo(testObjectA))) 21 | } 22 | 23 | func testBeIdenticalToAnyObjectProtocol() { 24 | let object = AnObjectImplementation() 25 | 26 | expect(object as AnObjectProtocol).to(be(object)) 27 | expect(object as AnObjectProtocol).to(beIdenticalTo(object)) 28 | } 29 | 30 | func testBeIdenticalToNegative() { 31 | expect(self.testObjectA).toNot(beIdenticalTo(testObjectB)) 32 | } 33 | 34 | func testBeIdenticalToPositiveMessage() { 35 | let message = String(describing: NSString(format: "expected to be identical to <%p>, got <%p>", 36 | unsafeBitCast(testObjectB, to: Int.self), unsafeBitCast(testObjectA, to: Int.self))) 37 | failsWithErrorMessage(message) { 38 | expect(self.testObjectA).to(beIdenticalTo(self.testObjectB)) 39 | } 40 | } 41 | 42 | func testBeIdenticalToNegativeMessage() { 43 | let message = String(describing: NSString(format: "expected to not be identical to <%p>, got <%p>", 44 | unsafeBitCast(testObjectA, to: Int.self), unsafeBitCast(testObjectA, to: Int.self))) 45 | failsWithErrorMessage(message) { 46 | expect(self.testObjectA).toNot(beIdenticalTo(self.testObjectA)) 47 | } 48 | } 49 | 50 | func testFailsOnNils() { 51 | let message1 = String(describing: NSString(format: "expected to be identical to <%p>, got nil", 52 | unsafeBitCast(testObjectA, to: Int.self))) 53 | failsWithErrorMessageForNil(message1) { 54 | expect(nil as BeIdenticalToObjectTester?).to(beIdenticalTo(self.testObjectA)) 55 | } 56 | 57 | let message2 = String(describing: NSString(format: "expected to not be identical to <%p>, got nil", 58 | unsafeBitCast(testObjectA, to: Int.self))) 59 | failsWithErrorMessageForNil(message2) { 60 | expect(nil as BeIdenticalToObjectTester?).toNot(beIdenticalTo(self.testObjectA)) 61 | } 62 | } 63 | 64 | func testOperators() { 65 | expect(self.testObjectA) === testObjectA 66 | expect(self.testObjectA) !== testObjectB 67 | } 68 | } 69 | 70 | private protocol AnObjectProtocol: AnyObject {} 71 | private final class AnObjectImplementation: AnObjectProtocol { 72 | init() {} 73 | } 74 | -------------------------------------------------------------------------------- /Tests/NimbleTests/Matchers/BeIdenticalToTest.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import XCTest 3 | @testable import Nimble 4 | #if SWIFT_PACKAGE 5 | import NimbleSharedTestHelpers 6 | #endif 7 | 8 | final class BeIdenticalToTest: XCTestCase { 9 | func testBeIdenticalToPositive() { 10 | let value = NSDate() 11 | expect(value).to(beIdenticalTo(value)) 12 | } 13 | 14 | func testBeIdenticalToNegative() { 15 | expect(1 as NSNumber).toNot(beIdenticalTo("yo" as NSString)) 16 | expect([1 as NSNumber] as NSArray).toNot(beIdenticalTo([1 as NSNumber] as NSArray)) 17 | } 18 | 19 | func testBeIdenticalToPositiveMessage() { 20 | let num1 = 1 as NSNumber 21 | let num2 = 2 as NSNumber 22 | let message = "expected to be identical to \(identityAsString(num2)), got \(identityAsString(num1))" 23 | failsWithErrorMessage(message) { 24 | expect(num1).to(beIdenticalTo(num2)) 25 | } 26 | } 27 | 28 | func testBeIdenticalToNegativeMessage() { 29 | let value1 = NSArray() 30 | let value2 = value1 31 | let message = "expected to not be identical to \(identityAsString(value2)), got \(identityAsString(value1))" 32 | failsWithErrorMessage(message) { 33 | expect(value1).toNot(beIdenticalTo(value2)) 34 | } 35 | } 36 | 37 | func testOperators() { 38 | let value = NSDate() 39 | expect(value) === value 40 | expect(1 as NSNumber) !== 2 as NSNumber 41 | } 42 | 43 | func testBeAlias() { 44 | let value = NSDate() 45 | expect(value).to(be(value)) 46 | expect(1 as NSNumber).toNot(be("turtles" as NSString)) 47 | expect([1 as NSNumber] as NSArray).toNot(be([1 as NSNumber] as NSArray)) 48 | 49 | let value1 = NSArray() 50 | let value2 = value1 51 | let message = "expected to not be identical to \(identityAsString(value1)), got \(identityAsString(value2))" 52 | failsWithErrorMessage(message) { 53 | expect(value1).toNot(be(value2)) 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Tests/NimbleTests/Matchers/BeLessThanOrEqualToTest.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import XCTest 3 | import Nimble 4 | #if SWIFT_PACKAGE 5 | import NimbleSharedTestHelpers 6 | #endif 7 | 8 | final class BeLessThanOrEqualToTest: XCTestCase { 9 | func testLessThanOrEqualTo() { 10 | expect(10).to(beLessThanOrEqualTo(10)) 11 | expect(2).to(beLessThanOrEqualTo(10)) 12 | expect(2).toNot(beLessThanOrEqualTo(1)) 13 | 14 | expect(2 as NSNumber).to(beLessThanOrEqualTo(10)) 15 | expect(2 as NSNumber).toNot(beLessThanOrEqualTo(1)) 16 | expect(2).to(beLessThanOrEqualTo(10 as NSNumber)) 17 | expect(2).toNot(beLessThanOrEqualTo(1 as NSNumber)) 18 | 19 | failsWithErrorMessage("expected to be less than or equal to <0>, got <2>") { 20 | expect(2).to(beLessThanOrEqualTo(0)) 21 | return 22 | } 23 | failsWithErrorMessage("expected to not be less than or equal to <0>, got <0>") { 24 | expect(0).toNot(beLessThanOrEqualTo(0)) 25 | return 26 | } 27 | failsWithErrorMessageForNil("expected to be less than or equal to <2>, got ") { 28 | expect(nil as Int?).to(beLessThanOrEqualTo(2)) 29 | return 30 | } 31 | failsWithErrorMessageForNil("expected to not be less than or equal to <-2>, got ") { 32 | expect(nil as Int?).toNot(beLessThanOrEqualTo(-2)) 33 | return 34 | } 35 | } 36 | 37 | func testLessThanOrEqualToOperator() { 38 | expect(0) <= 1 39 | expect(1) <= 1 40 | 41 | failsWithErrorMessage("expected to be less than or equal to <1>, got <2>") { 42 | expect(2) <= 1 43 | return 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Tests/NimbleTests/Matchers/BeLessThanTest.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import XCTest 3 | import Nimble 4 | #if SWIFT_PACKAGE 5 | import NimbleSharedTestHelpers 6 | #endif 7 | 8 | final class BeLessThanTest: XCTestCase { 9 | func testLessThan() { 10 | expect(2).to(beLessThan(10)) 11 | expect(2).toNot(beLessThan(1)) 12 | expect(2 as NSNumber).to(beLessThan(10 as NSNumber)) 13 | expect(2 as NSNumber).toNot(beLessThan(1 as NSNumber)) 14 | 15 | failsWithErrorMessage("expected to be less than <0>, got <2>") { 16 | expect(2).to(beLessThan(0)) 17 | } 18 | failsWithErrorMessage("expected to not be less than <1>, got <0>") { 19 | expect(0).toNot(beLessThan(1)) 20 | } 21 | 22 | failsWithErrorMessageForNil("expected to be less than <2>, got ") { 23 | expect(nil as Int?).to(beLessThan(2)) 24 | } 25 | failsWithErrorMessageForNil("expected to not be less than <-1>, got ") { 26 | expect(nil as Int?).toNot(beLessThan(-1)) 27 | } 28 | } 29 | 30 | func testLessThanOperator() { 31 | expect(0) < 1 32 | expect(0 as NSNumber) < 1 as NSNumber 33 | failsWithErrorMessage("expected to be less than <1>, got <2>") { 34 | expect(2) < 1 35 | return 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Tests/NimbleTests/Matchers/BeNilTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Nimble 3 | #if SWIFT_PACKAGE 4 | import NimbleSharedTestHelpers 5 | #endif 6 | 7 | final class BeNilTest: XCTestCase { 8 | func producesNil() -> [Int]? { 9 | return nil 10 | } 11 | 12 | func testBeNil() { 13 | expect(nil as Int?).to(beNil()) 14 | expect(nil as Int?) == nil 15 | 16 | expect(1 as Int?).toNot(beNil()) 17 | expect(1 as Int?) != nil 18 | 19 | expect(self.producesNil()).to(beNil()) 20 | expect(self.producesNil()) == nil 21 | 22 | do { 23 | let message = "expected to not be nil, got " 24 | failsWithErrorMessage(message) { 25 | expect(nil as Int?).toNot(beNil()) 26 | } 27 | failsWithErrorMessage(message) { 28 | expect(nil as Int?) != nil 29 | } 30 | } 31 | 32 | do { 33 | let message = "expected to be nil, got <1>" 34 | failsWithErrorMessage(message) { 35 | expect(1 as Int?).to(beNil()) 36 | } 37 | failsWithErrorMessage(message) { 38 | expect(1 as Int?) == nil 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Tests/NimbleTests/Matchers/BeVoidTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Nimble 3 | #if SWIFT_PACKAGE 4 | import NimbleSharedTestHelpers 5 | #endif 6 | 7 | final class BeVoidTest: XCTestCase { 8 | func testBeVoid() { 9 | expect(()).to(beVoid()) 10 | expect(() as ()?).to(beVoid()) 11 | expect(nil as ()?).toNot(beVoid()) 12 | 13 | expect(()) == () 14 | expect(() as ()?) == () 15 | expect(nil as ()?) != () 16 | 17 | failsWithErrorMessage("expected to not be void, got <()>") { 18 | expect(()).toNot(beVoid()) 19 | } 20 | 21 | failsWithErrorMessage("expected to not be void, got <()>") { 22 | expect(() as ()?).toNot(beVoid()) 23 | } 24 | 25 | failsWithErrorMessage("expected to be void, got ") { 26 | expect(nil as ()?).to(beVoid()) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Tests/NimbleTests/Matchers/BeWithinTest.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import XCTest 3 | import Nimble 4 | #if SWIFT_PACKAGE 5 | import NimbleSharedTestHelpers 6 | #endif 7 | 8 | final class BeWithinTest: XCTestCase { 9 | func testBeWithinPositiveMatches() { 10 | // Range 11 | expect(0.1).to(beWithin(0.1..<1.1)) 12 | expect(4).to(beWithin(3..<5)) 13 | expect(-3).to(beWithin(-7..<5)) 14 | 15 | expect(0.3).toNot(beWithin(0.31..<0.99)) 16 | expect(2).toNot(beWithin(0..<2)) 17 | expect(-7.1).toNot(beWithin(-14.3..<(-7.2))) 18 | 19 | // ClosedRange 20 | expect(0.1).to(beWithin(0.1...1.1)) 21 | expect(5).to(beWithin(3...5)) 22 | expect(-3).to(beWithin(-7...5)) 23 | 24 | expect(0.3).toNot(beWithin(0.31...0.99)) 25 | expect(3).toNot(beWithin(0...2)) 26 | expect(-7.1).toNot(beWithin(-14.3...(-7.2))) 27 | } 28 | 29 | func testBeWithinNegativeMatches() { 30 | // Range 31 | failsWithErrorMessage("expected to be within range <(0.0..<2.1)>, got <2.1>") { 32 | expect(2.1).to(beWithin(0..<2.1)) 33 | } 34 | failsWithErrorMessage("expected to not be within range <(0.0..<2.2)>, got <2.1>") { 35 | expect(2.1).toNot(beWithin(0..<2.2)) 36 | } 37 | 38 | // ClosedRange 39 | failsWithErrorMessage("expected to be within range <(0.2...1.1)>, got <0.1>") { 40 | expect(0.1).to(beWithin(0.2...1.1)) 41 | } 42 | failsWithErrorMessage("expected to not be within range <(0.31...0.99)>, got <0.31>") { 43 | expect(0.31).toNot(beWithin(0.31...0.99)) 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Tests/NimbleTests/Matchers/BeginWithPrefixTest.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Nimble 3 | import XCTest 4 | #if SWIFT_PACKAGE 5 | import NimbleSharedTestHelpers 6 | #endif 7 | 8 | final class BeginWithPrefixTest: XCTestCase { 9 | 10 | func testBeginWithSequencePrefix() { 11 | failsWithErrorMessageForNil("expected to begin with , got ") { 12 | expect(nil as [Int]?).to(beginWith(prefix: nil as [Int]?)) 13 | } 14 | 15 | failsWithErrorMessageForNil("expected to begin with <[1, 2]>, got ") { 16 | expect(nil as [Int]?).to(beginWith(prefix: [1, 2])) 17 | } 18 | 19 | failsWithErrorMessageForNil("expected to begin with , got <[1, 2]>") { 20 | expect([1, 2]).to(beginWith(prefix: nil as [Int]?)) 21 | } 22 | 23 | let sequence = [1, 2, 3] 24 | expect(sequence).toNot(beginWith(prefix: [1, 2, 3, 4])) 25 | expect(sequence).toNot(beginWith(prefix: [2, 3])) 26 | 27 | expect(sequence).to(beginWith(prefix: [1, 2, 3])) 28 | expect(sequence).to(beginWith(prefix: [1, 2])) 29 | expect(sequence).to(beginWith(prefix: [])) 30 | 31 | expect([]).toNot(beginWith(prefix: [1])) 32 | expect([]).to(beginWith(prefix: [] as [Int])) 33 | } 34 | 35 | func testBeginWithSequencePrefixUsingMatcherClosure() { 36 | failsWithErrorMessageForNil("expected to begin with , got ") { 37 | expect(nil as [Int]?).to(beginWith(prefix: nil as [Int]?, by: { $0 == $1 })) 38 | } 39 | 40 | failsWithErrorMessageForNil("expected to begin with <[1, 2]>, got ") { 41 | expect(nil as [Int]?).to(beginWith(prefix: [1, 2], by: { $0 == $1 })) 42 | } 43 | 44 | failsWithErrorMessageForNil("expected to begin with , got <[1, 2]>") { 45 | expect([1, 2]).to(beginWith(prefix: nil as [Int]?, by: { $0 == $1 })) 46 | } 47 | 48 | let sequence = [1, 2, 3] 49 | expect(sequence).toNot(beginWith(prefix: [1, 2, 3, 4], by: { $0 == $1 })) 50 | expect(sequence).toNot(beginWith(prefix: [2, 3], by: { $0 == $1 })) 51 | 52 | expect(sequence).to(beginWith(prefix: [1, 2, 3], by: { $0 == $1 })) 53 | expect(sequence).to(beginWith(prefix: [1, 2], by: { $0 == $1 })) 54 | expect(sequence).to(beginWith(prefix: [], by: { $0 == $1 })) 55 | 56 | expect([]).toNot(beginWith(prefix: [1], by: { $0 == $1 })) 57 | expect([]).to(beginWith(prefix: [] as [Int], by: { $0 == $1 })) 58 | } 59 | 60 | func testBeginWithSequencePrefixWithDifferentSequenceTypes() { 61 | expect(1...3).to(beginWith(prefix: [1, 2, 3])) 62 | expect(1...3).toNot(beginWith(prefix: [1, 2, 3, 4, 5])) 63 | 64 | expect(1...3).to(beginWith(prefix: [1, 2, 3], by: { $0 == $1 })) 65 | expect(1...3).toNot(beginWith(prefix: [1, 2, 3, 4, 5], by: { $0 == $1 })) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Tests/NimbleTests/Matchers/BeginWithTest.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import XCTest 3 | import Nimble 4 | #if SWIFT_PACKAGE 5 | import NimbleSharedTestHelpers 6 | #endif 7 | 8 | final class BeginWithTest: XCTestCase { 9 | func testPositiveMatches() { 10 | expect([1, 2, 3]).to(beginWith(1)) 11 | expect([1, 2, 3]).toNot(beginWith(2)) 12 | 13 | expect("foobar").to(beginWith("foo")) 14 | expect("foobar").toNot(beginWith("oo")) 15 | 16 | expect("foobarfoo").to(beginWith("foo")) 17 | 18 | expect(("foobar" as NSString).description).to(beginWith("foo")) 19 | expect(("foobar" as NSString).description).toNot(beginWith("oo")) 20 | 21 | expect(["a", "b"] as NSArray).to(beginWith("a")) 22 | expect(["a", "b"] as NSArray).toNot(beginWith("b")) 23 | } 24 | 25 | func testNegativeMatches() { 26 | failsWithErrorMessageForNil("expected to begin with , got ") { 27 | expect(nil as NSArray?).to(beginWith("b" as NSString)) 28 | } 29 | failsWithErrorMessageForNil("expected to not begin with , got ") { 30 | expect(nil as NSArray?).toNot(beginWith("b" as NSString)) 31 | } 32 | 33 | failsWithErrorMessage("expected to begin with <2>, got <[1, 2, 3]>") { 34 | expect([1, 2, 3]).to(beginWith(2)) 35 | } 36 | failsWithErrorMessage("expected to not begin with <1>, got <[1, 2, 3]>") { 37 | expect([1, 2, 3]).toNot(beginWith(1)) 38 | } 39 | failsWithErrorMessage("expected to begin with , got ") { 40 | expect("batman").to(beginWith("atm")) 41 | } 42 | failsWithErrorMessage("expected to not begin with , got ") { 43 | expect("batman").toNot(beginWith("bat")) 44 | } 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /Tests/NimbleTests/Matchers/ElementsEqualTest.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import XCTest 3 | import Nimble 4 | #if SWIFT_PACKAGE 5 | import NimbleSharedTestHelpers 6 | #endif 7 | 8 | final class ElementsEqualTest: XCTestCase { 9 | 10 | func testSequenceElementsEquality() { 11 | failsWithErrorMessageForNil("expected to elementsEqual , got ") { 12 | expect(nil as [Int]?).to(elementsEqual(nil as [Int]?)) 13 | } 14 | let sequence = [1, 2] 15 | failsWithErrorMessageForNil("expected to elementsEqual <[1, 2]>, got ") { 16 | expect(nil as [Int]?).to(elementsEqual(sequence)) 17 | } 18 | 19 | failsWithErrorMessageForNil("expected to elementsEqual , got <[1, 2]>") { 20 | expect(sequence).to(elementsEqual(nil as [Int]?)) 21 | } 22 | 23 | let sequence1 = [1, 2, 3] 24 | let sequence2 = [1, 2, 3, 4, 5] 25 | expect(sequence1).toNot(elementsEqual(sequence2)) 26 | expect(sequence1).toNot(elementsEqual([3, 2, 1])) 27 | expect(sequence1).to(elementsEqual([1, 2, 3])) 28 | } 29 | 30 | func testSequenceElementsEqualityUsingMatcherClosure() { 31 | failsWithErrorMessageForNil("expected to elementsEqual , got ") { 32 | expect(nil as [Int]?).to(elementsEqual(nil as [Int]?, by: { $0 == $1 })) 33 | } 34 | let sequence = [1, 2] 35 | failsWithErrorMessageForNil("expected to elementsEqual <[1, 2]>, got ") { 36 | expect(nil as [Int]?).to(elementsEqual(sequence, by: { $0 == $1 })) 37 | } 38 | 39 | failsWithErrorMessageForNil("expected to elementsEqual , got <[1, 2]>") { 40 | expect(sequence).to(elementsEqual(nil as [Int]?, by: { $0 == $1 })) 41 | } 42 | 43 | let sequence1 = [1, 2, 3] 44 | let sequence2 = [1, 2, 3, 4, 5] 45 | expect(sequence1).toNot(elementsEqual(sequence2, by: { $0 == $1 })) 46 | expect(sequence1).toNot(elementsEqual([3, 2, 1], by: { $0 == $1 })) 47 | expect(sequence1).to(elementsEqual([1, 2, 3], by: { $0 == $1 })) 48 | } 49 | 50 | func testElementsEqualDifferentSequenceTypes() { 51 | expect(1...3).to(elementsEqual([1, 2, 3])) 52 | expect(1...3).toNot(elementsEqual([1, 2, 3, 4, 5])) 53 | expect(1...3).toNot(elementsEqual([3, 2, 1])) 54 | 55 | expect(1...3).to(elementsEqual([1, 2, 3], by: { $0 == $1 })) 56 | expect(1...3).toNot(elementsEqual([1, 2, 3, 4, 5], by: { $0 == $1 })) 57 | expect(1...3).toNot(elementsEqual([3, 2, 1], by: { $0 == $1 })) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Tests/NimbleTests/Matchers/EndWithTest.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import XCTest 3 | import Nimble 4 | #if SWIFT_PACKAGE 5 | import NimbleSharedTestHelpers 6 | #endif 7 | 8 | final class EndWithTest: XCTestCase { 9 | func testEndWithPositives() { 10 | expect([1, 2, 3]).to(endWith(3)) 11 | expect([1, 2, 3]).toNot(endWith(2)) 12 | expect([]).toNot(endWith(1)) 13 | expect(["a", "b", "a"]).to(endWith("a")) 14 | 15 | expect("foobar").to(endWith("bar")) 16 | expect("foobar").toNot(endWith("oo")) 17 | expect("foobarfoo").to(endWith("foo")) 18 | 19 | expect(("foobar" as NSString).description).to(endWith("bar")) 20 | expect(("foobar" as NSString).description).toNot(endWith("oo")) 21 | 22 | expect(["a", "b"] as NSArray).to(endWith("b")) 23 | expect(["a", "b"] as NSArray).toNot(endWith("a")) 24 | expect([] as NSArray).toNot(endWith("a")) 25 | expect(["a", "b", "a"] as NSArray).to(endWith("a")) 26 | } 27 | 28 | func testEndWithNegatives() { 29 | failsWithErrorMessageForNil("expected to end with <2>, got ") { 30 | expect(nil as [Int]?).to(endWith(2)) 31 | } 32 | failsWithErrorMessageForNil("expected to not end with <2>, got ") { 33 | expect(nil as [Int]?).toNot(endWith(2)) 34 | } 35 | 36 | failsWithErrorMessage("expected to end with <2>, got <[1, 2, 3]>") { 37 | expect([1, 2, 3]).to(endWith(2)) 38 | } 39 | failsWithErrorMessage("expected to not end with <3>, got <[1, 2, 3]>") { 40 | expect([1, 2, 3]).toNot(endWith(3)) 41 | } 42 | failsWithErrorMessage("expected to end with , got ") { 43 | expect("batman").to(endWith("atm")) 44 | } 45 | failsWithErrorMessage("expected to not end with , got ") { 46 | expect("batman").toNot(endWith("man")) 47 | } 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /Tests/NimbleTests/Matchers/HaveCountTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Nimble 3 | #if SWIFT_PACKAGE 4 | import NimbleSharedTestHelpers 5 | #endif 6 | 7 | final class HaveCountTest: XCTestCase { 8 | func testHaveCountForArray() { 9 | expect([1, 2, 3]).to(haveCount(3)) 10 | expect([1, 2, 3]).notTo(haveCount(1)) 11 | 12 | failsWithErrorMessage( 13 | """ 14 | expected to have Array with count 1, got 3 15 | Actual Value: [1, 2, 3] 16 | """ 17 | ) { 18 | expect([1, 2, 3]).to(haveCount(1)) 19 | } 20 | 21 | failsWithErrorMessage( 22 | """ 23 | expected to not have Array with count 3, got 3 24 | Actual Value: [1, 2, 3] 25 | """ 26 | ) { 27 | expect([1, 2, 3]).notTo(haveCount(3)) 28 | } 29 | } 30 | 31 | func testHaveCountForDictionary() { 32 | let dictionary = ["1": 1, "2": 2, "3": 3] 33 | expect(dictionary).to(haveCount(3)) 34 | expect(dictionary).notTo(haveCount(1)) 35 | 36 | failsWithErrorMessage( 37 | """ 38 | expected to have Dictionary with count 1, got 3 39 | Actual Value: \(stringify(dictionary)) 40 | """ 41 | ) { 42 | expect(dictionary).to(haveCount(1)) 43 | } 44 | 45 | failsWithErrorMessage( 46 | """ 47 | expected to not have Dictionary with count 3, got 3 48 | Actual Value: \(stringify(dictionary)) 49 | """ 50 | ) { 51 | expect(dictionary).notTo(haveCount(3)) 52 | } 53 | } 54 | 55 | func testHaveCountForSet() { 56 | let set = Set([1, 2, 3]) 57 | expect(set).to(haveCount(3)) 58 | expect(set).notTo(haveCount(1)) 59 | 60 | failsWithErrorMessage( 61 | """ 62 | expected to have Set with count 1, got 3 63 | Actual Value: \(stringify(set)) 64 | """ 65 | ) { 66 | expect(set).to(haveCount(1)) 67 | } 68 | 69 | failsWithErrorMessage( 70 | """ 71 | expected to not have Set with count 3, got 3 72 | Actual Value: \(stringify(set)) 73 | """ 74 | ) { 75 | expect(set).notTo(haveCount(3)) 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Tests/NimbleTests/Matchers/MatchErrorTest.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import XCTest 3 | import Nimble 4 | #if SWIFT_PACKAGE 5 | import NimbleSharedTestHelpers 6 | #endif 7 | 8 | final class MatchErrorTest: XCTestCase { 9 | func testMatchErrorPositive() { 10 | expect(NimbleError.laugh).to(matchError(NimbleError.laugh)) 11 | expect(NimbleError.laugh).to(matchError(NimbleError.self)) 12 | expect(EquatableError.parameterized(x: 1)).to(matchError(EquatableError.parameterized(x: 1))) 13 | 14 | expect(NimbleError.laugh as Error).to(matchError(NimbleError.laugh)) 15 | } 16 | 17 | func testMatchErrorNegative() { 18 | expect(NimbleError.laugh).toNot(matchError(NimbleError.cry)) 19 | expect(NimbleError.laugh as Error).toNot(matchError(NimbleError.cry)) 20 | expect(NimbleError.laugh).toNot(matchError(EquatableError.self)) 21 | expect(EquatableError.parameterized(x: 1)).toNot(matchError(EquatableError.parameterized(x: 2))) 22 | } 23 | 24 | func testMatchNSErrorPositive() { 25 | let error1 = NSError(domain: "err", code: 0, userInfo: nil) 26 | let error2 = NSError(domain: "err", code: 0, userInfo: nil) 27 | 28 | expect(error1).to(matchError(error2)) 29 | } 30 | 31 | func testMatchNSErrorNegative() { 32 | let error1 = NSError(domain: "err", code: 0, userInfo: nil) 33 | let error2 = NSError(domain: "err", code: 1, userInfo: nil) 34 | 35 | expect(error1).toNot(matchError(error2)) 36 | } 37 | 38 | func testMatchPositiveMessage() { 39 | failsWithErrorMessage("expected to match error , got ") { 40 | expect(EquatableError.parameterized(x: 1)).to(matchError(EquatableError.parameterized(x: 2))) 41 | } 42 | failsWithErrorMessage("expected to match error , got ") { 43 | expect(NimbleError.laugh).to(matchError(NimbleError.cry)) 44 | } 45 | failsWithErrorMessage("expected to match error , got ") { 46 | expect(CustomDebugStringConvertibleError.a).to(matchError(CustomDebugStringConvertibleError.b)) 47 | } 48 | 49 | failsWithErrorMessage("expected to match error , got ") { 50 | let error1 = NSError(domain: "err", code: 0, userInfo: nil) 51 | let error2 = NSError(domain: "err", code: 1, userInfo: nil) 52 | expect(error1).to(matchError(error2)) 53 | } 54 | } 55 | 56 | func testMatchNegativeMessage() { 57 | failsWithErrorMessage("expected to not match error , got ") { 58 | expect(NimbleError.laugh).toNot(matchError(NimbleError.laugh)) 59 | } 60 | 61 | failsWithErrorMessage("expected to match error from type , got ") { 62 | expect(NimbleError.laugh).to(matchError(EquatableError.self)) 63 | } 64 | } 65 | 66 | func testDoesNotMatchNils() { 67 | failsWithErrorMessageForNil("expected to match error , got no error") { 68 | expect(nil as Error?).to(matchError(NimbleError.laugh)) 69 | } 70 | 71 | failsWithErrorMessageForNil("expected to not match error , got no error") { 72 | expect(nil as Error?).toNot(matchError(NimbleError.laugh)) 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Tests/NimbleTests/Matchers/MatchTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Nimble 3 | #if SWIFT_PACKAGE 4 | import NimbleSharedTestHelpers 5 | #endif 6 | 7 | final class MatchTest: XCTestCase { 8 | func testMatchPositive() { 9 | expect("11:14").to(match("\\d{2}:\\d{2}")) 10 | } 11 | 12 | func testMatchNegative() { 13 | expect("hello").toNot(match("\\d{2}:\\d{2}")) 14 | } 15 | 16 | func testMatchPositiveMessage() { 17 | let message = "expected to match <\\d{2}:\\d{2}>, got " 18 | failsWithErrorMessage(message) { 19 | expect("hello").to(match("\\d{2}:\\d{2}")) 20 | } 21 | } 22 | 23 | func testMatchNegativeMessage() { 24 | let message = "expected to not match <\\d{2}:\\d{2}>, got <11:14>" 25 | failsWithErrorMessage(message) { 26 | expect("11:14").toNot(match("\\d{2}:\\d{2}")) 27 | } 28 | } 29 | 30 | func testMatchNils() { 31 | failsWithErrorMessageForNil("expected to match <\\d{2}:\\d{2}>, got ") { 32 | expect(nil as String?).to(match("\\d{2}:\\d{2}")) 33 | } 34 | 35 | failsWithErrorMessageForNil("expected to not match <\\d{2}:\\d{2}>, got ") { 36 | expect(nil as String?).toNot(match("\\d{2}:\\d{2}")) 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Tests/NimbleTests/Matchers/NegationTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Nimble 3 | #if SWIFT_PACKAGE 4 | import NimbleSharedTestHelpers 5 | #endif 6 | 7 | final class NegationTest: XCTestCase { 8 | func testSyncNil() { 9 | expect(nil as Int?).toNot(not(beNil())) 10 | 11 | failsWithErrorMessage("expected to not be nil, got ") { 12 | expect(nil as Int?).to(not(beNil())) 13 | } 14 | } 15 | 16 | func testSyncNonNil() { 17 | expect(1).to(not(equal(2))) 18 | 19 | failsWithErrorMessage("expected to not equal <2>, got <2>") { 20 | expect(2).to(not(equal(2))) 21 | } 22 | } 23 | 24 | func testAsyncNil() async { 25 | @Sendable func nilFunc() async -> Int? { 26 | nil 27 | } 28 | 29 | await expect(nilFunc).toNot(not(beNil())) 30 | 31 | await failsWithErrorMessage("expected to not be nil, got ") { 32 | await expect(nilFunc).to(not(beNil())) 33 | } 34 | } 35 | 36 | func testAsyncNonNil() async { 37 | await expect(1).to(not(asyncEqual(2))) 38 | 39 | await failsWithErrorMessage("expected to not equal 2, got <2>") { 40 | await expect(2).to(not(asyncEqual(2))) 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Tests/NimbleTests/Matchers/ThrowAssertionTest.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import XCTest 3 | import Nimble 4 | #if SWIFT_PACKAGE 5 | import NimbleSharedTestHelpers 6 | #endif 7 | 8 | private let error: Error = NSError(domain: "test", code: 0, userInfo: nil) 9 | 10 | final class ThrowAssertionTest: XCTestCase { 11 | func testPositiveMatch() { 12 | #if (arch(x86_64) || arch(arm64)) && !os(Windows) 13 | expect { () -> Void in fatalError() }.to(throwAssertion()) 14 | #endif 15 | } 16 | 17 | func testErrorThrown() { 18 | #if (arch(x86_64) || arch(arm64)) && !os(Windows) 19 | expect { throw error }.toNot(throwAssertion()) 20 | #endif 21 | } 22 | 23 | func testPostAssertionCodeNotRun() { 24 | #if (arch(x86_64) || arch(arm64)) && !os(Windows) 25 | var reachedPoint1 = false 26 | var reachedPoint2 = false 27 | 28 | expect { 29 | reachedPoint1 = true 30 | precondition(false, "condition message") 31 | reachedPoint2 = true 32 | }.to(throwAssertion()) 33 | 34 | expect(reachedPoint1) == true 35 | expect(reachedPoint2) == false 36 | #endif 37 | } 38 | 39 | func testNegativeMatch() { 40 | #if (arch(x86_64) || arch(arm64)) && !os(Windows) 41 | var reachedPoint1 = false 42 | 43 | expect { reachedPoint1 = true }.toNot(throwAssertion()) 44 | 45 | expect(reachedPoint1) == true 46 | #endif 47 | } 48 | 49 | func testPositiveMessage() { 50 | #if (arch(x86_64) || arch(arm64)) && !os(Windows) 51 | failsWithErrorMessage("expected to throw an assertion") { 52 | expect { () -> Void? in return }.to(throwAssertion()) 53 | } 54 | 55 | failsWithErrorMessage("expected to throw an assertion; threw error instead <\(error)>") { 56 | expect { throw error }.to(throwAssertion()) 57 | } 58 | #endif 59 | } 60 | 61 | func testNegativeMessage() { 62 | #if (arch(x86_64) || arch(arm64)) && !os(Windows) 63 | failsWithErrorMessage("expected to not throw an assertion") { 64 | expect { () -> Void in fatalError() }.toNot(throwAssertion()) 65 | } 66 | #endif 67 | } 68 | 69 | func testNonVoidClosure() { 70 | #if (arch(x86_64) || arch(arm64)) && !os(Windows) 71 | expect { () -> Int in fatalError() }.to(throwAssertion()) 72 | #endif 73 | } 74 | 75 | func testChainOnThrowAssertion() { 76 | #if (arch(x86_64) || arch(arm64)) && !os(Windows) 77 | expect { () -> Int in return 5 }.toNot(throwAssertion()).to(equal(5)) 78 | #endif 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Tests/NimbleTests/Matchers/ToSucceedTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Nimble 3 | #if SWIFT_PACKAGE 4 | import NimbleSharedTestHelpers 5 | #endif 6 | 7 | final class ToSucceedTest: XCTestCase { 8 | func testToSucceed() { 9 | expect { 10 | return .succeeded 11 | }.to(succeed()) 12 | 13 | expect { 14 | return .failed(reason: "") 15 | }.toNot(succeed()) 16 | 17 | expect { 18 | let result = ToSucceedResult.succeeded 19 | return result 20 | }.to(succeed()) 21 | 22 | failsWithErrorMessageForNil("expected a ToSucceedResult, got ") { 23 | expect(nil).to(succeed()) 24 | } 25 | 26 | failsWithErrorMessage("expected to succeed, got because ") { 27 | expect { 28 | .failed(reason: "something went wrong") 29 | }.to(succeed()) 30 | } 31 | 32 | failsWithErrorMessage("expected to not succeed, got ") { 33 | expect { 34 | return .succeeded 35 | }.toNot(succeed()) 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Tests/NimbleTests/OnFailureThrowsTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Nimble 3 | #if SWIFT_PACKAGE 4 | import NimbleSharedTestHelpers 5 | #endif 6 | 7 | final class OnFailureThrowsTest: XCTestCase { 8 | 9 | enum MyError: Error, Equatable { 10 | case error1 11 | } 12 | 13 | func testUnexecutedLogsAnError() { 14 | failsWithErrorMessage("Attempted to call `Expectation.onFailure(throw:) before a matcher has been applied.\nTry using `expect(...).to(...).onFailure(throw: ...`) instead.") { 15 | try expect(true).onFailure(throw: MyError.error1) 16 | } 17 | } 18 | 19 | func testPassedDoesNotThrow() { 20 | let expectation = expect(true).to(beTrue()) 21 | 22 | expect(try expectation.onFailure(throw: MyError.error1)).notTo(throwError()) 23 | } 24 | 25 | func testFailedThrowsAnError() { 26 | let expectation = suppressErrors { 27 | expect(true).to(beFalse()) 28 | } 29 | 30 | expect(try expectation.onFailure(throw: MyError.error1)).to(throwError(MyError.error1)) 31 | } 32 | 33 | func testMixedThrowsAnError() { 34 | let expectation = suppressErrors { 35 | expect(true).to(beTrue()).to(beFalse()) 36 | } 37 | 38 | expect(try expectation.onFailure(throw: MyError.error1)).to(throwError(MyError.error1)) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Tests/NimbleTests/PredicateTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Nimble 3 | #if SWIFT_PACKAGE 4 | import NimbleSharedTestHelpers 5 | #endif 6 | 7 | final class MatcherTest: XCTestCase { 8 | func testDefineDefaultMessage() { 9 | failsWithErrorMessage("expected to match, got <1>") { 10 | expect(1).to(Matcher.define { _, msg in MatcherResult(status: .fail, message: msg) }) 11 | } 12 | } 13 | 14 | func testDefineNilableDefaultMessage() { 15 | failsWithErrorMessage("expected to match, got <1>") { 16 | expect(1).to(Matcher.defineNilable { _, msg in MatcherResult(status: .fail, message: msg) }) 17 | } 18 | } 19 | 20 | func testSimpleDefaultMessage() { 21 | failsWithErrorMessage("expected to match, got <1>") { 22 | expect(1).to(Matcher.simple { _ in .fail }) 23 | } 24 | } 25 | 26 | func testSimpleNilableDefaultMessage() { 27 | failsWithErrorMessage("expected to match, got <1>") { 28 | expect(1).to(Matcher.simpleNilable { _ in .fail }) 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Tests/NimbleTests/StatusTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Nimble 3 | #if SWIFT_PACKAGE 4 | import NimbleSharedTestHelpers 5 | #endif 6 | 7 | final class StatusTest: XCTestCase { 8 | 9 | func testUnexecuted() { 10 | producesStatus(.pending) { 11 | expect(true) 12 | } 13 | } 14 | 15 | func testSingleExecution() { 16 | producesStatus(.passed) { 17 | expect(true).to(beTrue()) 18 | } 19 | 20 | producesStatus(.failed) { 21 | expect(true).to(beFalse()) 22 | } 23 | } 24 | 25 | func testChainedExecution() { 26 | producesStatus(.passed) { 27 | expect(true).to(beTrue()).to(beTrue()) 28 | } 29 | 30 | producesStatus(.failed) { 31 | expect(true).to(beFalse()).to(beFalse()) 32 | } 33 | 34 | producesStatus(.mixed) { 35 | expect(true).to(beTrue()).to(beFalse()) 36 | } 37 | 38 | producesStatus(.mixed) { 39 | expect(true).to(beFalse()).to(beTrue()) 40 | } 41 | } 42 | 43 | #if !os(WASI) 44 | func testAsync() { 45 | producesStatus(.passed) { 46 | expect(true).toEventually(beTrue()) 47 | } 48 | 49 | producesStatus(.failed) { 50 | expect(true).toEventually(beFalse()) 51 | } 52 | } 53 | #endif 54 | } 55 | -------------------------------------------------------------------------------- /Tests/NimbleTests/SwiftTestingSupportTest.swift: -------------------------------------------------------------------------------- 1 | #if canImport(Testing) 2 | import Nimble 3 | import Testing 4 | import XCTest 5 | 6 | @Suite struct SwiftTestingSupportSuite { 7 | @Test func reportsAssertionFailuresToSwiftTesting() { 8 | withKnownIssue { 9 | expect(1).to(equal(2)) 10 | } 11 | } 12 | 13 | @Test func reportsRequireErrorsToSwiftTesting() throws { 14 | withKnownIssue { 15 | try require(false).to(beTrue()) 16 | } 17 | } 18 | } 19 | 20 | #if canImport(Darwin) 21 | // the open source version of XCTest doesn't include `XCTExpectFailure`. 22 | class MixedSwiftTestingXCTestSupport: XCTestCase { 23 | func testAlsoRecordsErrorsToXCTest() { 24 | XCTExpectFailure("This should fail") 25 | fail() 26 | 27 | } 28 | 29 | func testAlsoRecordsRequireErrorsToXCTest() throws { 30 | XCTExpectFailure("This should fail") 31 | try require(false).to(beTrue()) 32 | } 33 | } 34 | #endif 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /Tests/NimbleTests/UserDescriptionTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Nimble 3 | #if SWIFT_PACKAGE 4 | import NimbleSharedTestHelpers 5 | #endif 6 | 7 | final class UserDescriptionTest: XCTestCase { 8 | func testToMatcher_CustomFailureMessage() { 9 | failsWithErrorMessage( 10 | """ 11 | These aren't equal! 12 | expected to match, got <1> 13 | """ 14 | ) { 15 | expect(1).to(Matcher.simple { _ in .doesNotMatch }, description: "These aren't equal!") 16 | } 17 | } 18 | 19 | func testNotToMatcher_CustomFailureMessage() { 20 | failsWithErrorMessage( 21 | """ 22 | These aren't equal! 23 | expected to not match, got <1> 24 | """ 25 | ) { 26 | expect(1).notTo(Matcher.simple { _ in .matches }, description: "These aren't equal!") 27 | } 28 | } 29 | 30 | func testToNotMatcher_CustomFailureMessage() { 31 | failsWithErrorMessage( 32 | """ 33 | These aren't equal! 34 | expected to not match, got <1> 35 | """ 36 | ) { 37 | expect(1).toNot(Matcher.simple { _ in .matches }, description: "These aren't equal!") 38 | } 39 | } 40 | 41 | #if !os(WASI) 42 | func testToEventuallyMatch_CustomFailureMessage() { 43 | failsWithErrorMessage( 44 | """ 45 | These aren't eventually equal! 46 | expected to eventually equal <1>, got <0> 47 | """ 48 | ) { 49 | expect { 0 }.toEventually(equal(1), description: "These aren't eventually equal!") 50 | } 51 | } 52 | 53 | func testToEventuallyNotMatch_CustomFailureMessage() { 54 | failsWithErrorMessage( 55 | """ 56 | These are eventually equal! 57 | expected to eventually not equal <1>, got <1> 58 | """ 59 | ) { 60 | expect { 1 }.toEventuallyNot(equal(1), description: "These are eventually equal!") 61 | } 62 | } 63 | 64 | func testToNotEventuallyMatch_CustomFailureMessage() { 65 | failsWithErrorMessage( 66 | """ 67 | These are eventually equal! 68 | expected to eventually not equal <1>, got <1> 69 | """ 70 | ) { 71 | expect { 1 }.toEventuallyNot(equal(1), description: "These are eventually equal!") 72 | } 73 | } 74 | #endif // #if !os(WASI) 75 | 76 | } 77 | -------------------------------------------------------------------------------- /script/build_docs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -e 4 | 5 | export DOCC_JSON_PRETTYPRINT="YES" 6 | 7 | mkdir -p docs 8 | 9 | swift package --allow-writing-to-directory docs \ 10 | generate-documentation --target Nimble \ 11 | --disable-indexing \ 12 | --transform-for-static-hosting \ 13 | --hosting-base-path 'Nimble' \ 14 | --output-path docs 15 | --------------------------------------------------------------------------------