├── .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