Full Code of groue/CombineExpectations for AI

master 04d4e4b21c9e cached
27 files
181.0 KB
40.1k tokens
1 requests
Download .txt
Repository: groue/CombineExpectations
Branch: master
Commit: 04d4e4b21c9e
Files: 27
Total size: 181.0 KB

Directory structure:
gitextract_q_moxppx/

├── .github/
│   ├── FUNDING.yml
│   └── workflows/
│       └── ci.yml
├── .gitignore
├── CHANGELOG.md
├── CombineExpectations.podspec
├── LICENSE
├── Package.swift
├── README.md
├── Sources/
│   └── CombineExpectations/
│       ├── PublisherExpectation.swift
│       ├── PublisherExpectations/
│       │   ├── AvailableElements.swift
│       │   ├── Finished.swift
│       │   ├── Inverted.swift
│       │   ├── Map.swift
│       │   ├── Next.swift
│       │   ├── NextOne.swift
│       │   ├── Prefix.swift
│       │   └── Recording.swift
│       ├── Recorder.swift
│       └── RecordingError.swift
└── Tests/
    ├── CombineExpectationsTests/
    │   ├── DocumentationTests.swift
    │   ├── FailureTestCase.swift
    │   ├── LateSubscriptionTest.swift
    │   ├── RecorderTests.swift
    │   ├── Support.swift
    │   ├── WackySubscriberTests.swift
    │   └── XCTestManifests.swift
    └── LinuxMain.swift

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/FUNDING.yml
================================================
github: [groue]


================================================
FILE: .github/workflows/ci.yml
================================================
name: Continuous Integration
on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true
jobs:
  build-test:
    name: Build & Test (Swift ${{ matrix.swift }}, ${{ matrix.platform }})
    runs-on: macos-${{ matrix.macos || '11' }}
    strategy:
      fail-fast: false
      matrix:
        swift: ['5.1', '5.2', '5.3', '5.4', '5.5']
        platform: [macOS, iOS, tvOS, watchOS]
        exclude:
          # watchOS requires Swift 5.4 or later.
          - swift: '5.1'
            platform: watchOS
          - swift: '5.2'
            platform: watchOS
          - swift: '5.3'
            platform: watchOS
        include:
          # The macOS 11 runner no longer includes Swift 5.1.
          - swift: '5.1'
            macos: '10.15'
    steps:
    - name: Clone
      uses: actions/checkout@v2
    - name: Build & Test
      uses: mxcl/xcodebuild@v1
      with:
        swift: ~${{ matrix.swift }}
        platform: ${{ matrix.platform }}


================================================
FILE: .gitignore
================================================
.DS_Store
/.build
/Packages
/*.xcodeproj
xcuserdata/
/.swiftpm


================================================
FILE: CHANGELOG.md
================================================
Release Notes
=============

All notable changes to this project will be documented in this file.

#### 0.x Releases

- [0.10.0](#0100)
- [0.9.0](#090)
- [0.8.0](#080)
- [0.7.0](#070)
- [0.6.0](#060)
- [0.5.0](#050)
- [0.4.0](#040)
- [0.3.0](#030)
- [0.2.0](#020)
- [0.1.0](#010)

## 0.10.0

Released August 11, 2021 • [diff](https://github.com/groue/CombineExpectations/compare/v0.9.0...v0.10.0)

- **New**: [#17](https://github.com/groue/CombineExpectations/pull/17) by [@chris-araman](https://github.com/chris-araman): Improvements for Swift 5.1, Swift 5.3, Swift 5.4, watchOS 7.4, Xcode 12.5, Xcode 13 beta

## 0.9.0

Released June 7, 2021 • [diff](https://github.com/groue/CombineExpectations/compare/v0.8.0...v0.9.0)

- **New**: [#16](https://github.com/groue/CombineExpectations/pull/16) by [@chris-araman](https://github.com/chris-araman): Support watchOS 6 or later when building with Swift 5.4 or later

## 0.8.0

Released May 29, 2021 • [diff](https://github.com/groue/CombineExpectations/compare/v0.7.0...v0.8.0)

- **Fixed**: [#15](https://github.com/groue/CombineExpectations/pull/15) by [@chrisballinger](https://github.com/chrisballinger): Fix XCTFail not found issue with Xcode 12.5

## 0.7.0

Released January 9, 2021 • [diff](https://github.com/groue/CombineExpectations/compare/v0.6.0...v0.7.0)

- **Fixed**: [#13](https://github.com/groue/CombineExpectations/pull/13) by [@chrisballinger](https://github.com/chrisballinger): Remove module_name override in CocoaPods spec

## 0.6.0

Released December 23, 2020 • [diff](https://github.com/groue/CombineExpectations/compare/v0.5.0...v0.6.0)

- **New**: [#11](https://github.com/groue/CombineExpectations/pull/11): `availableElements` expectation (fixes [#8](https://github.com/groue/CombineExpectations/issues/8)).

## 0.5.0

Released June 25, 2020 • [diff](https://github.com/groue/CombineExpectations/compare/v0.4.0...v0.5.0)

- **Fixed**: `next().get()` no longer returns an optional.
- **New**: Support for Xcode 12

## 0.4.0

Released January 4, 2020 • [diff](https://github.com/groue/CombineExpectations/compare/v0.3.0...v0.4.0)

- [#6](https://github.com/groue/CombineExpectations/pull/6): Support for synchronous tests

**Documentation diff**:

The [Usage] section shows how to use the new `get()` method in order to perform synchronous tests that do not have to wait.


## 0.3.0

Released November 27, 2019 • [diff](https://github.com/groue/CombineExpectations/compare/v0.2.0...v0.3.0)

- [#2](https://github.com/groue/CombineExpectations/pull/2): RecordingError renaming
- [#3](https://github.com/groue/CombineExpectations/pull/3): Next Expectation
- [#4](https://github.com/groue/CombineExpectations/pull/4): Drop the "first" expectation


## 0.2.0

Released November 24, 2019 • [diff](https://github.com/groue/CombineExpectations/compare/v0.1.0...v0.2.0)

**Increased robustness**

## 0.1.0

Released November 23, 2019

**Initial release**

[Usage]: README.md#usage


================================================
FILE: CombineExpectations.podspec
================================================
Pod::Spec.new do |s|
  s.name     = 'CombineExpectations'
  s.version  = '0.10.0'
  
  s.license  = { :type => 'MIT', :file => 'LICENSE' }
  s.summary  = 'A set of extensions for SQLite, GRDB.swift, and Combine'
  s.homepage = 'https://github.com/groue/CombineExpectations'
  s.author   = { 'Gwendal Roué' => 'gr@pierlis.com' }
  s.source   = { :git => 'https://github.com/groue/CombineExpectations.git', :tag => "v#{s.version}" }
  
  s.swift_versions = ['5.1', '5.2', '5.3', '5.4']
  s.ios.deployment_target = '13.0'
  s.osx.deployment_target = '10.15'
  s.tvos.deployment_target = '13.0'
  s.watchos.deployment_target = '7.4'
  
  s.frameworks = ['Combine', 'XCTest']
  s.source_files = 'Sources/CombineExpectations/**/*.swift'
  s.pod_target_xcconfig = {
    "ENABLE_TESTING_SEARCH_PATHS" => "YES" # Required for Xcode 12.5
  }
end


================================================
FILE: LICENSE
================================================
Copyright (C) 2019 Gwendal Roué

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


================================================
FILE: Package.swift
================================================
// swift-tools-version:5.1
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "CombineExpectations",
    platforms: [
        .iOS(.v13),
        .macOS(.v10_15),
        .tvOS(.v13),
    ],
    products: [
        // Products define the executables and libraries produced by a package, and make them visible to other packages.
        .library(
            name: "CombineExpectations",
            targets: ["CombineExpectations"]),
    ],
    targets: [
        // Targets are the basic building blocks of a package. A target can define a module or a test suite.
        // Targets can depend on other targets in this package, and on products in packages which this package depends on.
        .target(
            name: "CombineExpectations",
            dependencies: [],
            linkerSettings: [.linkedFramework("XCTest")]),
        .testTarget(
            name: "CombineExpectationsTests",
            dependencies: ["CombineExpectations"]),
    ]
)

#if swift(>=5.4)
  // XCTest was introduced for watchOS with Swift 5.4, Xcode 12.5, and watchOS 7.4.
  package.platforms! += [
    .watchOS("7.4")
  ]
#endif


================================================
FILE: README.md
================================================
# Combine Expectations

### Utilities for tests that wait for Combine publishers.

---

**Latest release**: [version 0.10.0](https://github.com/groue/CombineExpectations/tree/v0.10.0) (August 11, 2021) • [Release Notes]

**Requirements**: iOS 13+, macOS 10.15+, and tvOS 13+ require Swift 5.1+ or Xcode 11+. watchOS 7.4+ requires Swift 5.4+ or Xcode 12.5+.

**Contact**: Report bugs and ask questions in [Github issues](https://github.com/groue/CombineExpectations/issues).

---

Testing Combine publishers with [XCTestExpectation](*https://developer.apple.com/documentation/xctest/xctestexpectation*) often requires setting up a lot of boilerplate code.

CombineExpectations aims at streamlining those tests. It defines an XCTestCase method which waits for *publisher expectations*.

- [Usage]
- [Installation]
- [Publisher Expectations]: [availableElements], [completion], [elements], [finished], [last], [next()], [next(count)], [prefix(maxLength)], [recording], [single]

---

## Usage

Waiting for [Publisher Expectations] allows your tests to look like this:

```swift
import XCTest
import CombineExpectations

class PublisherTests: XCTestCase {
    func testElements() throws {
        // 1. Create a publisher
        let publisher = ...
        
        // 2. Start recording the publisher
        let recorder = publisher.record()
        
        // 3. Wait for a publisher expectation
        let elements = try wait(for: recorder.elements, timeout: ..., description: "Elements")
        
        // 4. Test the result of the expectation
        XCTAssertEqual(elements, ["Hello", "World!"])
    }
}
```

**When you wait for a publisher expectation:**

- The test fails if the expectation is not fulfilled within the specified timeout.
- An error is thrown if the expected value can not be returned. For example, waiting for `recorder.elements` throws the publisher error if the publisher completes with a failure.
- The `wait` method returns immediately if the expectation has already reached the waited state.

You can wait multiple times for a publisher:

```swift
class PublisherTests: XCTestCase {
    func testPublisher() throws {
        let publisher = ...
        let recorder = publisher.record()
        
        // Wait for first element
        _ = try wait(for: recorder.next(), timeout: ...)
        
        // Wait for second element
        _ = try wait(for: recorder.next(), timeout: ...)
        
        // Wait for successful completion
        try wait(for: recorder.finished, timeout: ...)
    }
}
```

**Not all tests have to wait**, because some publishers expectations are fulfilled right away. In this case, prefer the synchronous `get()` method over `wait(for:timeout:)`, as below:

```swift
class PublisherTests: XCTestCase {
    func testSynchronousPublisher() throws {
        // 1. Create a publisher
        let publisher = ...
        
        // 2. Start recording the publisher
        let recorder = publisher.record()
        
        // 3. Grab the expected result
        let elements = try recorder.elements.get()
        
        // 4. Test the result of the expectation
        XCTAssertEqual(elements, ["Hello", "World!"])
    }
}
```

Just like `wait(for:timeout:)`, the `get()` method can be called multiple times:

```swift
class PublisherTests: XCTestCase {
    // SUCCESS: no error
    func testPassthroughSubjectSynchronouslyPublishesElements() throws {
        let publisher = PassthroughSubject<String, Never>()
        let recorder = publisher.record()
        
        publisher.send("foo")
        try XCTAssertEqual(recorder.next().get(), "foo")
        
        publisher.send("bar")
        try XCTAssertEqual(recorder.next().get(), "bar")
    }
}
```


## Installation

Add a dependency for CombineExpectations to your [Swift Package](https://swift.org/package-manager/) test targets:

```diff
 import PackageDescription
 
 let package = Package(
     dependencies: [
+        .package(url: "https://github.com/groue/CombineExpectations.git", ...)
     ],
     targets: [
         .testTarget(
             dependencies: [
+                "CombineExpectations"
             ])
     ]
 )
```


## Publisher Expectations

There are various publisher expectations. Each one waits for a specific publisher aspect:

- [availableElements]: all published elements until timeout expiration
- [completion]: the publisher completion
- [elements]: all published elements until successful completion
- [finished]: the publisher successful completion
- [last]: the last published element
- [next()]: the next published element
- [next(count)]: the next N published elements
- [prefix(maxLength)]: the first N published elements
- [recording]: the full recording of publisher events
- [single]: the one and only published element

---

### availableElements

:clock230: `recorder.availableElements` waits for the expectation to expire, or the recorded publisher to complete.

:x: When waiting for this expectation, the publisher error is thrown if the publisher fails before the expectation has expired.

:white_check_mark: Otherwise, an array of all elements published before the expectation has expired is returned.

:arrow_right: Related expectations: [elements], [prefix(maxLength)].

Unlike other expectations, `availableElements` does not make a test fail on timeout expiration. It just returns the elements published so far.

Example:

```swift
// SUCCESS: no timeout, no error
func testTimerPublishesIncreasingDates() throws {
    let publisher = Timer.publish(every: 0.01, on: .main, in: .common).autoconnect()
    let recorder = publisher.record()
    let dates = try wait(for: recorder.availableElements, timeout: ...)
    XCTAssertEqual(dates.sorted(), dates)
}
```

### completion

:clock230: `recorder.completion` waits for the recorded publisher to complete.

:x: When waiting for this expectation, a `RecordingError.notCompleted` is thrown if the publisher does not complete on time.

:white_check_mark: Otherwise, a [`Subscribers.Completion`](https://developer.apple.com/documentation/combine/subscribers/completion) is returned.

:arrow_right: Related expectations: [finished], [recording].

Example:

```swift
// SUCCESS: no timeout, no error
func testArrayPublisherCompletesWithSuccess() throws {
    let publisher = ["foo", "bar", "baz"].publisher
    let recorder = publisher.record()
    let completion = try wait(for: recorder.completion, timeout: ...)
    if case let .failure(error) = completion {
        XCTFail("Unexpected error \(error)")
    }
}

// SUCCESS: no error
func testArrayPublisherSynchronouslyCompletesWithSuccess() throws {
    let publisher = ["foo", "bar", "baz"].publisher
    let recorder = publisher.record()
    let completion = try recorder.completion.get()
    if case let .failure(error) = completion {
        XCTFail("Unexpected error \(error)")
    }
}
```

<details>
    <summary>Examples of failing tests</summary>

```swift
// FAIL: Asynchronous wait failed
// FAIL: Caught error RecordingError.notCompleted
func testCompletionTimeout() throws {
    let publisher = PassthroughSubject<String, Never>()
    let recorder = publisher.record()
    let completion = try wait(for: recorder.completion, timeout: ...)
}
```

</details>


---

### elements

:clock230: `recorder.elements` waits for the recorded publisher to complete.

:x: When waiting for this expectation, a `RecordingError.notCompleted` is thrown if the publisher does not complete on time, and the publisher error is thrown if the publisher fails.

:white_check_mark: Otherwise, an array of published elements is returned.

:arrow_right: Related expectations: [availableElements], [last], [prefix(maxLength)], [recording], [single].

Example:

```swift
// SUCCESS: no timeout, no error
func testArrayPublisherPublishesArrayElements() throws {
    let publisher = ["foo", "bar", "baz"].publisher
    let recorder = publisher.record()
    let elements = try wait(for: recorder.elements, timeout: ...)
    XCTAssertEqual(elements, ["foo", "bar", "baz"])
}

// SUCCESS: no error
func testArrayPublisherSynchronouslyPublishesArrayElements() throws {
    let publisher = ["foo", "bar", "baz"].publisher
    let recorder = publisher.record()
    let elements = try recorder.elements.get()
    XCTAssertEqual(elements, ["foo", "bar", "baz"])
}
```

<details>
    <summary>Examples of failing tests</summary>

```swift
// FAIL: Asynchronous wait failed
// FAIL: Caught error RecordingError.notCompleted
func testElementsTimeout() throws {
    let publisher = PassthroughSubject<String, Never>()
    let recorder = publisher.record()
    let elements = try wait(for: recorder.elements, timeout: ...)
}

// FAIL: Caught error MyError
func testElementsError() throws {
    let publisher = PassthroughSubject<String, MyError>()
    let recorder = publisher.record()
    publisher.send(completion: .failure(MyError()))
    let elements = try wait(for: recorder.elements, timeout: ...)
}
```

</details>


---

### finished

:clock230: `recorder.finished` waits for the recorded publisher to complete.

:x: When waiting for this expectation, the publisher error is thrown if the publisher fails.

:arrow_right: Related expectations: [completion], [recording].

Example:

```swift
// SUCCESS: no timeout, no error
func testArrayPublisherFinishesWithoutError() throws {
    let publisher = ["foo", "bar", "baz"].publisher
    let recorder = publisher.record()
    try wait(for: recorder.finished, timeout: ...)
}

// SUCCESS: no error
func testArrayPublisherSynchronouslyFinishesWithoutError() throws {
    let publisher = ["foo", "bar", "baz"].publisher
    let recorder = publisher.record()
    try recorder.finished.get()
}
```

<details>
    <summary>Examples of failing tests</summary>

```swift
// FAIL: Asynchronous wait failed
func testFinishedTimeout() throws {
    let publisher = PassthroughSubject<String, Never>()
    let recorder = publisher.record()
    try wait(for: recorder.finished, timeout: ...)
}

// FAIL: Caught error MyError
func testFinishedError() throws {
    let publisher = PassthroughSubject<String, MyError>()
    let recorder = publisher.record()
    publisher.send(completion: .failure(MyError()))
    try wait(for: recorder.finished, timeout: ...)
}
```

</details>

`recorder.finished` can be inverted:

```swift
// SUCCESS: no timeout, no error
func testPassthroughSubjectDoesNotFinish() throws {
    let publisher = PassthroughSubject<String, Never>()
    let recorder = publisher.record()
    try wait(for: recorder.finished.inverted, timeout: ...)
}
```

<details>
    <summary>Examples of failing tests</summary>

```swift
// FAIL: Fulfilled inverted expectation
// FAIL: Caught error MyError
func testInvertedFinishedError() throws {
    let publisher = PassthroughSubject<String, MyError>()
    let recorder = publisher.record()
    publisher.send(completion: .failure(MyError()))
    try wait(for: recorder.finished.inverted, timeout: ...)
}
```

</details>


---

### last

:clock230: `recorder.last` waits for the recorded publisher to complete.

:x: When waiting for this expectation, a `RecordingError.notCompleted` is thrown if the publisher does not complete on time, and the publisher error is thrown if the publisher fails.

:white_check_mark: Otherwise, the last published element is returned, or nil if the publisher completes before it publishes any element.

:arrow_right: Related expectations: [elements], [single].

Example:

```swift
// SUCCESS: no timeout, no error
func testArrayPublisherPublishesLastElementLast() throws {
    let publisher = ["foo", "bar", "baz"].publisher
    let recorder = publisher.record()
    if let element = try wait(for: recorder.last, timeout: ...) {
        XCTAssertEqual(element, "baz")
    } else {
        XCTFail("Expected one element")
    }
}

// SUCCESS: no error
func testArrayPublisherSynchronouslyPublishesLastElementLast() throws {
    let publisher = ["foo", "bar", "baz"].publisher
    let recorder = publisher.record()
    if let element = try recorder.last.get() {
        XCTAssertEqual(element, "baz")
    } else {
        XCTFail("Expected one element")
    }
}
```

<details>
    <summary>Examples of failing tests</summary>

```swift
// FAIL: Asynchronous wait failed
// FAIL: Caught error RecordingError.notCompleted
func testLastTimeout() throws {
    let publisher = PassthroughSubject<String, Never>()
    let recorder = publisher.record()
    let element = try wait(for: recorder.last, timeout: ...)
}

// FAIL: Caught error MyError
func testLastError() throws {
    let publisher = PassthroughSubject<String, MyError>()
    let recorder = publisher.record()
    publisher.send(completion: .failure(MyError()))
    let element = try wait(for: recorder.last, timeout: ...)
}
```

</details>


---

### next()

:clock230: `recorder.next()` waits for the recorded publisher to emit one element, or to complete.

:x: When waiting for this expectation, a `RecordingError.notEnoughElements` is thrown if the publisher does not publish one element after last waited expectation. The publisher error is thrown if the publisher fails before publishing the next element.

:white_check_mark: Otherwise, the next published element is returned.

:arrow_right: Related expectations: [next(count)], [single].

Example:

```swift
// SUCCESS: no timeout, no error
func testArrayOfTwoElementsPublishesElementsInOrder() throws {
    let publisher = ["foo", "bar"].publisher
    let recorder = publisher.record()
    
    var element = try wait(for: recorder.next(), timeout: ...)
    XCTAssertEqual(element, "foo")
    
    element = try wait(for: recorder.next(), timeout: ...)
    XCTAssertEqual(element, "bar")
}

// SUCCESS: no error
func testArrayOfTwoElementsSynchronouslyPublishesElementsInOrder() throws {
    let publisher = ["foo", "bar"].publisher
    let recorder = publisher.record()
    
    var element = try recorder.next().get()
    XCTAssertEqual(element, "foo")
    
    element = try recorder.next().get()
    XCTAssertEqual(element, "bar")
}
```

<details>
    <summary>Examples of failing tests</summary>

```swift
// FAIL: Asynchronous wait failed
// FAIL: Caught error RecordingError.notEnoughElements
func testNextTimeout() throws {
    let publisher = PassthroughSubject<String, Never>()
    let recorder = publisher.record()
    let element = try wait(for: recorder.next(), timeout: ...)
}

// FAIL: Caught error MyError
func testNextError() throws {
    let publisher = PassthroughSubject<String, MyError>()
    let recorder = publisher.record()
    publisher.send(completion: .failure(MyError()))
    let element = try wait(for: recorder.next(), timeout: ...)
}

// FAIL: Caught error RecordingError.notEnoughElements
func testNextNotEnoughElementsError() throws {
    let publisher = PassthroughSubject<String, Never>()
    let recorder = publisher.record()
    publisher.send(completion: .finished)
    let element = try wait(for: recorder.next(), timeout: ...)
}
```

</details>

`recorder.next()` can be inverted:

```swift
// SUCCESS: no timeout, no error
func testPassthroughSubjectDoesNotPublishAnyElement() throws {
    let publisher = PassthroughSubject<String, Never>()
    let recorder = publisher.record()
    try wait(for: recorder.next().inverted, timeout: ...)
}
```

<details>
    <summary>Examples of failing tests</summary>

```swift
// FAIL: Fulfilled inverted expectation
func testInvertedNextTooEarly() throws {
    let publisher = PassthroughSubject<String, Never>()
    let recorder = publisher.record()
    publisher.send("foo")
    try wait(for: recorder.next().inverted, timeout: ...)
}

// FAIL: Fulfilled inverted expectation
// FAIL: Caught error MyError
func testInvertedNextError() throws {
    let publisher = PassthroughSubject<String, MyError>()
    let recorder = publisher.record()
    publisher.send(completion: .failure(MyError()))
    try wait(for: recorder.next().inverted, timeout: ...)
}
```

</details>


---

### next(count)

:clock230: `recorder.next(count)` waits for the recorded publisher to emit `count` elements, or to complete.

:x: When waiting for this expectation, a `RecordingError.notEnoughElements` is thrown if the publisher does not publish `count` elements after last waited expectation. The publisher error is thrown if the publisher fails before publishing the next `count` elements.

:white_check_mark: Otherwise, an array of exactly `count` elements is returned.

:arrow_right: Related expectations: [next()], [prefix(maxLength)].

Example:

```swift
// SUCCESS: no timeout, no error
func testArrayOfThreeElementsPublishesTwoThenOneElement() throws {
    let publisher = ["foo", "bar", "baz"].publisher
    let recorder = publisher.record()
    
    var elements = try wait(for: recorder.next(2), timeout: ...)
    XCTAssertEqual(elements, ["foo", "bar"])
    
    elements = try wait(for: recorder.next(1), timeout: ...)
    XCTAssertEqual(elements, ["baz"])
}

// SUCCESS: no error
func testArrayOfThreeElementsSynchronouslyPublishesTwoThenOneElement() throws {
    let publisher = ["foo", "bar", "baz"].publisher
    let recorder = publisher.record()
    
    var elements = try recorder.next(2).get()
    XCTAssertEqual(elements, ["foo", "bar"])
    
    elements = try recorder.next(1).get()
    XCTAssertEqual(elements, ["baz"])
}
```

<details>
    <summary>Examples of failing tests</summary>

```swift
// FAIL: Asynchronous wait failed
// FAIL: Caught error RecordingError.notEnoughElements
func testNextCountTimeout() throws {
    let publisher = PassthroughSubject<String, Never>()
    let recorder = publisher.record()
    publisher.send("foo")
    let elements = try wait(for: recorder.next(2), timeout: ...)
}

// FAIL: Caught error MyError
func testNextCountError() throws {
    let publisher = PassthroughSubject<String, MyError>()
    let recorder = publisher.record()
    publisher.send("foo")
    publisher.send(completion: .failure(MyError()))
    let elements = try wait(for: recorder.next(2), timeout: ...)
}

// FAIL: Caught error RecordingError.notEnoughElements
func testNextCountNotEnoughElementsError() throws {
    let publisher = PassthroughSubject<String, Never>()
    let recorder = publisher.record()
    publisher.send("foo")
    publisher.send(completion: .finished)
    let elements = try wait(for: recorder.next(2), timeout: ...)
}
```

</details>


---

### prefix(maxLength)

:clock230: `recorder.prefix(maxLength)` waits for the recorded publisher to emit `maxLength` elements, or to complete.

:x: When waiting for this expectation, the publisher error is thrown if the publisher fails before `maxLength` elements are published.

:white_check_mark: Otherwise, an array of received elements is returned, containing at most `maxLength` elements, or less if the publisher completes early.

:arrow_right: Related expectations: [availableElements], [elements], [next(count)].

Example:

```swift
// SUCCESS: no timeout, no error
func testArrayOfThreeElementsPublishesTwoFirstElementsWithoutError() throws {
    let publisher = ["foo", "bar", "baz"].publisher
    let recorder = publisher.record()
    let elements = try wait(for: recorder.prefix(2), timeout: ...)
    XCTAssertEqual(elements, ["foo", "bar"])
}

// SUCCESS: no error
func testArrayOfThreeElementsSynchronouslyPublishesTwoFirstElementsWithoutError() throws {
    let publisher = ["foo", "bar", "baz"].publisher
    let recorder = publisher.record()
    let elements = try recorder.prefix(2).get()
    XCTAssertEqual(elements, ["foo", "bar"])
}
```

<details>
    <summary>Examples of failing tests</summary>

```swift
// FAIL: Asynchronous wait failed
func testPrefixTimeout() throws {
    let publisher = PassthroughSubject<String, Never>()
    let recorder = publisher.record()
    publisher.send("foo")
    let elements = try wait(for: recorder.prefix(2), timeout: ...)
}

// FAIL: Caught error MyError
func testPrefixError() throws {
    let publisher = PassthroughSubject<String, MyError>()
    let recorder = publisher.record()
    publisher.send("foo")
    publisher.send(completion: .failure(MyError()))
    let elements = try wait(for: recorder.prefix(2), timeout: ...)
}
```

</details>

`recorder.prefix(maxLength)` can be inverted:

```swift
// SUCCESS: no timeout, no error
func testPassthroughSubjectPublishesNoMoreThanSentValues() throws {
    let publisher = PassthroughSubject<String, Never>()
    let recorder = publisher.record()
    publisher.send("foo")
    publisher.send("bar")
    let elements = try wait(for: recorder.prefix(3).inverted, timeout: ...)
    XCTAssertEqual(elements, ["foo", "bar"])
}
```

<details>
    <summary>Examples of failing tests</summary>

```swift
// FAIL: Fulfilled inverted expectation
func testInvertedPrefixTooEarly() throws {
    let publisher = PassthroughSubject<String, Never>()
    let recorder = publisher.record()
    publisher.send("foo")
    publisher.send("bar")
    publisher.send("baz")
    let elements = try wait(for: recorder.prefix(3).inverted, timeout: ...)
}

// FAIL: Fulfilled inverted expectation
// FAIL: Caught error MyError
func testInvertedPrefixError() throws {
    let publisher = PassthroughSubject<String, MyError>()
    let recorder = publisher.record()
    publisher.send("foo")
    publisher.send(completion: .failure(MyError()))
    let elements = try wait(for: recorder.prefix(3).inverted, timeout: ...)
}
```

</details>


---

### recording

:clock230: `recorder.recording` waits for the recorded publisher to complete.

:x: When waiting for this expectation, a `RecordingError.notCompleted` is thrown if the publisher does not complete on time.

:white_check_mark: Otherwise, a [`Record.Recording`](https://developer.apple.com/documentation/combine/record/recording) is returned.

:arrow_right: Related expectations: [completion], [elements], [finished].

Example:

```swift
// SUCCESS: no timeout, no error
func testArrayPublisherRecording() throws {
    let publisher = ["foo", "bar", "baz"].publisher
    let recorder = publisher.record()
    let recording = try wait(for: recorder.recording, timeout: ...)
    XCTAssertEqual(recording.output, ["foo", "bar", "baz"])
    if case let .failure(error) = recording.completion {
        XCTFail("Unexpected error \(error)")
    }
}

// SUCCESS: no error
func testArrayPublisherSynchronousRecording() throws {
    let publisher = ["foo", "bar", "baz"].publisher
    let recorder = publisher.record()
    let recording = try recorder.recording.get()
    XCTAssertEqual(recording.output, ["foo", "bar", "baz"])
    if case let .failure(error) = recording.completion {
        XCTFail("Unexpected error \(error)")
    }
}
```

<details>
    <summary>Examples of failing tests</summary>

```swift
// FAIL: Asynchronous wait failed
// FAIL: Caught error RecordingError.notCompleted
func testRecordingTimeout() throws {
    let publisher = PassthroughSubject<String, Never>()
    let recorder = publisher.record()
    let recording = try wait(for: recorder.recording, timeout: ...)
}
```

</details>


---

### single

:clock230: `recorder.single` waits for the recorded publisher to complete.

:x: When waiting for this expectation, a `RecordingError` is thrown if the publisher does not complete on time, or does not publish exactly one element before it completes. The publisher error is thrown if the publisher fails.

:white_check_mark: Otherwise, the single published element is returned.

:arrow_right: Related expectations: [elements], [last], [next()].

Example:

```swift
// SUCCESS: no timeout, no error
func testJustPublishesExactlyOneElement() throws {
    let publisher = Just("foo")
    let recorder = publisher.record()
    let element = try wait(for: recorder.single, timeout: ...)
    XCTAssertEqual(element, "foo")
}

// SUCCESS: no error
func testJustSynchronouslyPublishesExactlyOneElement() throws {
    let publisher = Just("foo")
    let recorder = publisher.record()
    let element = try recorder.single.get()
    XCTAssertEqual(element, "foo")
}
```

<details>
    <summary>Examples of failing tests</summary>

```swift
// FAIL: Asynchronous wait failed
// FAIL: Caught error RecordingError.notCompleted
func testSingleTimeout() throws {
    let publisher = PassthroughSubject<String, Never>()
    let recorder = publisher.record()
    let element = try wait(for: recorder.single, timeout: ...)
}

// FAIL: Caught error MyError
func testSingleError() throws {
    let publisher = PassthroughSubject<String, MyError>()
    let recorder = publisher.record()
    publisher.send(completion: .failure(MyError()))
    let element = try wait(for: recorder.single, timeout: ...)
}

// FAIL: Caught error RecordingError.tooManyElements
func testSingleTooManyElementsError() throws {
    let publisher = PassthroughSubject<String, Never>()
    let recorder = publisher.record()
    publisher.send("foo")
    publisher.send("bar")
    publisher.send(completion: .finished)
    let element = try wait(for: recorder.single, timeout: ...)
}

// FAIL: Caught error RecordingError.notEnoughElements
func testSingleNotEnoughElementsError() throws {
    let publisher = PassthroughSubject<String, Never>()
    let recorder = publisher.record()
    publisher.send(completion: .finished)
    let element = try wait(for: recorder.single, timeout: ...)
}
```

</details>


[Release Notes]: CHANGELOG.md
[Usage]: #usage
[Installation]: #installation
[Publisher Expectations]: #publisher-expectations
[finished]: #finished
[prefix(maxLength)]: #prefixmaxlength
[next()]: #next
[next(count)]: #nextcount
[recording]: #recording
[completion]: #completion
[elements]: #elements
[last]: #last
[single]: #single
[availableElements]: #availableElements


================================================
FILE: Sources/CombineExpectations/PublisherExpectation.swift
================================================
import XCTest

/// A name space for publisher expectations
public enum PublisherExpectations { }

/// The base protocol for PublisherExpectation. It is an implementation detail
/// that you are not supposed to use, as shown by the underscore prefix.
///
/// :nodoc:
public protocol _PublisherExpectationBase {
    /// Sets up an XCTestExpectation. This method is an implementation detail
    /// that you are not supposed to use, as shown by the underscore prefix.
    func _setup(_ expectation: XCTestExpectation)
    
    /// Returns an object that waits for the expectation. If nil, expectation
    /// is waited by the XCTestCase.
    func _makeWaiter() -> XCTWaiter?
}

extension _PublisherExpectationBase {
    /// :nodoc:
    public func _makeWaiter() -> XCTWaiter? { nil }
}

/// The protocol for publisher expectations.
///
/// You can build publisher expectations from Recorder returned by the
/// `Publisher.record()` method.
///
/// For example:
///
///     // The expectation for all published elements until completion
///     let publisher = ["foo", "bar", "baz"].publisher
///     let recorder = publisher.record()
///     let expectation = recorder.elements
///
/// When a test grants some time for the expectation to fulfill, use the
/// XCTest `wait(for:timeout:description)` method:
///
///     // SUCCESS: no timeout, no error
///     func testArrayPublisherPublishesArrayElements() throws {
///         let publisher = ["foo", "bar", "baz"].publisher
///         let recorder = publisher.record()
///         let expectation = recorder.elements
///         let elements = try wait(for: expectation, timeout: 1)
///         XCTAssertEqual(elements, ["foo", "bar", "baz"])
///     }
///
/// On the other hand, when the expectation is supposed to be immediately
/// fulfilled, use the PublisherExpectation `get()` method in order to grab the
/// expected value:
///
///     // SUCCESS: no error
///     func testArrayPublisherSynchronouslyPublishesArrayElements() throws {
///         let publisher = ["foo", "bar", "baz"].publisher
///         let recorder = publisher.record()
///         let elements = try recorder.elements.get()
///         XCTAssertEqual(elements, ["foo", "bar", "baz"])
///     }
public protocol PublisherExpectation: _PublisherExpectationBase {
    /// The type of the expected value.
    associatedtype Output
    
    /// Returns the expected value, or throws an error if the
    /// expectation fails.
    ///
    /// For example:
    ///
    ///     // SUCCESS: no error
    ///     func testArrayPublisherSynchronouslyPublishesArrayElements() throws {
    ///         let publisher = ["foo", "bar", "baz"].publisher
    ///         let recorder = publisher.record()
    ///         let elements = try recorder.elements.get()
    ///         XCTAssertEqual(elements, ["foo", "bar", "baz"])
    ///     }
    func get() throws -> Output
}

extension XCTestCase {
    /// Waits for the publisher expectation to fulfill, and returns the
    /// expected value.
    ///
    /// For example:
    ///
    ///     // SUCCESS: no timeout, no error
    ///     func testArrayPublisherPublishesArrayElements() throws {
    ///         let publisher = ["foo", "bar", "baz"].publisher
    ///         let recorder = publisher.record()
    ///         let elements = try wait(for: recorder.elements, timeout: 1)
    ///         XCTAssertEqual(elements, ["foo", "bar", "baz"])
    ///     }
    ///
    /// - parameter publisherExpectation: The publisher expectation.
    /// - parameter timeout: The number of seconds within which the expectation
    ///   must be fulfilled.
    /// - parameter description: A string to display in the test log for the
    ///   expectation, to help diagnose failures.
    /// - throws: An error if the expectation fails.
    public func wait<R: PublisherExpectation>(
        for publisherExpectation: R,
        timeout: TimeInterval,
        description: String = "")
        throws -> R.Output
    {
        let expectation = self.expectation(description: description)
        publisherExpectation._setup(expectation)
        if let waiter = publisherExpectation._makeWaiter() {
            waiter.wait(for: [expectation], timeout: timeout)
        } else {
            wait(for: [expectation], timeout: timeout)
        }
        return try publisherExpectation.get()
    }
}


================================================
FILE: Sources/CombineExpectations/PublisherExpectations/AvailableElements.swift
================================================
import XCTest

extension PublisherExpectations {
    /// A publisher expectation which waits for the timeout to expire, or
    /// the recorded publisher to complete.
    ///
    /// When waiting for this expectation, the publisher error is thrown if
    /// the publisher fails before the expectation has expired.
    ///
    /// Otherwise, an array of all elements published before the expectation
    /// has expired is returned.
    ///
    /// Unlike other expectations, `AvailableElements` does not make a test fail
    /// on timeout expiration. It just returns the elements published so far.
    ///
    /// For example:
    ///
    ///     // SUCCESS: no timeout, no error
    ///     func testTimerPublishesIncreasingDates() throws {
    ///         let publisher = Timer.publish(every: 0.01, on: .main, in: .common).autoconnect()
    ///         let recorder = publisher.record()
    ///         let dates = try wait(for: recorder.availableElements, timeout: ...)
    ///         XCTAssertEqual(dates.sorted(), dates)
    ///     }
    public struct AvailableElements<Input, Failure: Error>: PublisherExpectation {
        let recorder: Recorder<Input, Failure>
        
        public func _makeWaiter() -> XCTWaiter? { Waiter() }
        
        public func _setup(_ expectation: XCTestExpectation) {
            recorder.fulfillOnCompletion(expectation)
        }
        
        /// Returns all elements published so far, or throws an error if the
        /// publisher has failed.
        public func get() throws -> [Input] {
            try recorder.value { (elements, completion, remainingElements, consume) in
                if case let .failure(error) = completion {
                    throw error
                }
                consume(remainingElements.count)
                return elements
            }
        }
        
        /// A waiter that waits but never fails
        private class Waiter: XCTWaiter, XCTWaiterDelegate {
            init() {
                super.init(delegate: nil)
                delegate = self
            }
            
            func waiter(_ waiter: XCTWaiter, didTimeoutWithUnfulfilledExpectations unfulfilledExpectations: [XCTestExpectation]) { }
            func waiter(_ waiter: XCTWaiter, fulfillmentDidViolateOrderingConstraintsFor expectation: XCTestExpectation, requiredExpectation: XCTestExpectation) { }
            func waiter(_ waiter: XCTWaiter, didFulfillInvertedExpectation expectation: XCTestExpectation) { }
            func nestedWaiter(_ waiter: XCTWaiter, wasInterruptedByTimedOutWaiter outerWaiter: XCTWaiter) { }
        }
    }
}


================================================
FILE: Sources/CombineExpectations/PublisherExpectations/Finished.swift
================================================
import XCTest

// The Finished expectation waits for the publisher to complete, and throws an
// error if and only if the publisher fails with an error.
//
// It is not derived from the Recording expectation, because Finished does not
// throw RecordingError.notCompleted if the publisher does not complete on time.
// It only triggers a timeout test failure.
//
// This allows to write tests for publishers that should not complete:
//
//      // SUCCESS: no timeout, no error
//      func testPassthroughSubjectDoesNotFinish() throws {
//          let publisher = PassthroughSubject<String, Never>()
//          let recorder = publisher.record()
//          try wait(for: recorder.finished.inverted, timeout: 1)
//      }

extension PublisherExpectations {
    /// A publisher expectation which waits for the recorded publisher
    /// to complete.
    ///
    /// When waiting for this expectation, the publisher error is thrown if the
    /// publisher fails.
    ///
    /// For example:
    ///
    ///     // SUCCESS: no timeout, no error
    ///     func testArrayPublisherFinishesWithoutError() throws {
    ///         let publisher = ["foo", "bar", "baz"].publisher
    ///         let recorder = publisher.record()
    ///         try wait(for: recorder.finished, timeout: 1)
    ///     }
    ///
    /// This publisher expectation can be inverted:
    ///
    ///     // SUCCESS: no timeout, no error
    ///     func testPassthroughSubjectDoesNotFinish() throws {
    ///         let publisher = PassthroughSubject<String, Never>()
    ///         let recorder = publisher.record()
    ///         try wait(for: recorder.finished.inverted, timeout: 1)
    ///     }
    public struct Finished<Input, Failure: Error>: PublisherExpectation {
        let recorder: Recorder<Input, Failure>
        
        public func _setup(_ expectation: XCTestExpectation) {
            recorder.fulfillOnCompletion(expectation)
        }
        
        /// Returns the expected output, or throws an error if the
        /// expectation fails.
        ///
        /// For example:
        ///
        ///     // SUCCESS: no error
        ///     func testArrayPublisherSynchronouslyFinishesWithoutError() throws {
        ///         let publisher = ["foo", "bar", "baz"].publisher
        ///         let recorder = publisher.record()
        ///         try recorder.finished.get()
        ///     }
        public func get() throws {
            try recorder.value { (_, completion, remainingElements, consume) in
                guard let completion = completion else {
                    consume(remainingElements.count)
                    return
                }
                if case let .failure(error) = completion {
                    throw error
                }
            }
        }
        
        /// Returns an inverted publisher expectation which waits for a
        /// publisher to complete successfully.
        ///
        /// When waiting for this expectation, an error is thrown if the
        /// publisher fails with an error.
        ///
        /// For example:
        ///
        ///     // SUCCESS: no timeout, no error
        ///     func testPassthroughSubjectDoesNotFinish() throws {
        ///         let publisher = PassthroughSubject<String, Never>()
        ///         let recorder = publisher.record()
        ///         try wait(for: recorder.finished.inverted, timeout: 1)
        ///     }
        public var inverted: Inverted<Self> {
            return Inverted(base: self)
        }
    }
}


================================================
FILE: Sources/CombineExpectations/PublisherExpectations/Inverted.swift
================================================
import XCTest

extension PublisherExpectations {
    /// A publisher expectation that fails if the base expectation is fulfilled.
    ///
    /// When waiting for this expectation, you receive the same result and
    /// eventual error as the base expectation.
    ///
    /// For example:
    ///
    ///     // SUCCESS: no timeout, no error
    ///     func testPassthroughSubjectDoesNotFinish() throws {
    ///         let publisher = PassthroughSubject<String, Never>()
    ///         let recorder = publisher.record()
    ///         try wait(for: recorder.finished.inverted, timeout: 1)
    ///     }
    public struct Inverted<Base: PublisherExpectation>: PublisherExpectation {
        let base: Base
        
        public func _setup(_ expectation: XCTestExpectation) {
            base._setup(expectation)
            expectation.isInverted.toggle()
        }
        
        public func get() throws -> Base.Output {
            try base.get()
        }
    }
}


================================================
FILE: Sources/CombineExpectations/PublisherExpectations/Map.swift
================================================
import XCTest

extension PublisherExpectations {
    /// A publisher expectation that transforms the value of a base expectation.
    ///
    /// This expectation has no public initializer.
    public struct Map<Base: PublisherExpectation, Output>: PublisherExpectation {
        let base: Base
        let transform: (Base.Output) throws -> Output
        
        public func _setup(_ expectation: XCTestExpectation) {
            base._setup(expectation)
        }
        
        public func get() throws -> Output {
            try transform(base.get())
        }
    }
}

extension PublisherExpectation {
    /// Returns a publisher expectation that transforms the value of the
    /// base expectation.
    func map<T>(_ transform: @escaping (Output) throws -> T) -> PublisherExpectations.Map<Self, T> {
        PublisherExpectations.Map(base: self, transform: transform)
    }
}


================================================
FILE: Sources/CombineExpectations/PublisherExpectations/Next.swift
================================================
import XCTest

extension PublisherExpectations {
    /// A publisher expectation which waits for the recorded publisher to emit
    /// `count` elements, or to complete.
    ///
    /// When waiting for this expectation, a `RecordingError.notEnoughElements`
    /// is thrown if the publisher does not publish `count` elements after last
    /// waited expectation. The publisher error is thrown if the publisher fails
    /// before publishing the next `count` elements.
    ///
    /// Otherwise, an array of exactly `count` elements is returned.
    ///
    /// For example:
    ///
    ///     // SUCCESS: no timeout, no error
    ///     func testArrayOfThreeElementsPublishesTwoThenOneElement() throws {
    ///         let publisher = ["foo", "bar", "baz"].publisher
    ///         let recorder = publisher.record()
    ///
    ///         var elements = try wait(for: recorder.next(2), timeout: 1)
    ///         XCTAssertEqual(elements, ["foo", "bar"])
    ///
    ///         elements = try wait(for: recorder.next(1), timeout: 1)
    ///         XCTAssertEqual(elements, ["baz"])
    ///     }
    public struct Next<Input, Failure: Error>: PublisherExpectation {
        let recorder: Recorder<Input, Failure>
        let count: Int
        
        init(recorder: Recorder<Input, Failure>, count: Int) {
            precondition(count >= 0, "Can't take a prefix of negative length")
            self.recorder = recorder
            self.count = count
        }
        
        public func _setup(_ expectation: XCTestExpectation) {
            if count == 0 {
                // Such an expectation is immediately fulfilled, by essence.
                expectation.expectedFulfillmentCount = 1
                expectation.fulfill()
            } else {
                expectation.expectedFulfillmentCount = count
                recorder.fulfillOnInput(expectation, includingConsumed: false)
            }
        }
        
        /// Returns the expected output, or throws an error if the
        /// expectation fails.
        ///
        /// For example:
        ///
        ///     // SUCCESS: no error
        ///     func testArrayOfThreeElementsSynchronouslyPublishesTwoThenOneElement() throws {
        ///         let publisher = ["foo", "bar", "baz"].publisher
        ///         let recorder = publisher.record()
        ///
        ///         var elements = try recorder.next(2).get()
        ///         XCTAssertEqual(elements, ["foo", "bar"])
        ///
        ///         elements = try recorder.next(1).get()
        ///         XCTAssertEqual(elements, ["baz"])
        ///     }
        public func get() throws -> [Input] {
            try recorder.value { (_, completion, remainingElements, consume) in
                if remainingElements.count >= count {
                    consume(count)
                    return Array(remainingElements.prefix(count))
                }
                if case let .failure(error) = completion {
                    throw error
                } else {
                    throw RecordingError.notEnoughElements
                }
            }
        }
    }
}


================================================
FILE: Sources/CombineExpectations/PublisherExpectations/NextOne.swift
================================================
import XCTest

extension PublisherExpectations {
    /// A publisher expectation which waits for the recorded publisher to emit
    /// one element, or to complete.
    ///
    /// When waiting for this expectation, a `RecordingError.notEnoughElements`
    /// is thrown if the publisher does not publish one element after last
    /// waited expectation. The publisher error is thrown if the publisher fails
    /// before publishing the next element.
    ///
    /// Otherwise, the next published element is returned.
    ///
    /// For example:
    ///
    ///     // SUCCESS: no timeout, no error
    ///     func testArrayOfTwoElementsPublishesElementsInOrder() throws {
    ///         let publisher = ["foo", "bar"].publisher
    ///         let recorder = publisher.record()
    ///
    ///         var element = try wait(for: recorder.next(), timeout: 1)
    ///         XCTAssertEqual(element, "foo")
    ///
    ///         element = try wait(for: recorder.next(), timeout: 1)
    ///         XCTAssertEqual(element, "bar")
    ///     }
    public struct NextOne<Input, Failure: Error>: PublisherExpectation {
        let recorder: Recorder<Input, Failure>
        
        public func _setup(_ expectation: XCTestExpectation) {
            recorder.fulfillOnInput(expectation, includingConsumed: false)
        }
        
        /// Returns the expected output, or throws an error if the
        /// expectation fails.
        ///
        /// For example:
        ///
        ///     // SUCCESS: no error
        ///     func testArrayOfTwoElementsSynchronouslyPublishesElementsInOrder() throws {
        ///         let publisher = ["foo", "bar"].publisher
        ///         let recorder = publisher.record()
        ///
        ///         var element = try recorder.next().get()
        ///         XCTAssertEqual(element, "foo")
        ///
        ///         element = try recorder.next().get()
        ///         XCTAssertEqual(element, "bar")
        ///     }
        public func get() throws -> Input {
            try recorder.value { (_, completion, remainingElements, consume) in
                if let next = remainingElements.first {
                    consume(1)
                    return next
                }
                if case let .failure(error) = completion {
                    throw error
                } else {
                    throw RecordingError.notEnoughElements
                }
            }
        }
        
        /// Returns an inverted publisher expectation which waits for the
        /// recorded publisher to emit one element, or to complete.
        ///
        /// When waiting for this expectation, a RecordingError is thrown if the
        /// publisher does not publish one element after last waited
        /// expectation. The publisher error is thrown if the publisher fails
        /// before publishing one element.
        ///
        /// For example:
        ///
        ///     // SUCCESS: no timeout, no error
        ///     func testPassthroughSubjectDoesNotPublishAnyElement() throws {
        ///         let publisher = PassthroughSubject<String, Never>()
        ///         let recorder = publisher.record()
        ///         try wait(for: recorder.next().inverted, timeout: 1)
        ///     }
        public var inverted: NextOneInverted<Input, Failure> {
            return NextOneInverted(recorder: recorder)
        }
    }
    
    /// An inverted publisher expectation which waits for the recorded publisher
    /// to emit one element, or to complete.
    ///
    /// When waiting for this expectation, a RecordingError is thrown if the
    /// publisher does not publish one element after last waited expectation.
    /// The publisher error is thrown if the publisher fails before
    /// publishing one element.
    ///
    /// For example:
    ///
    ///     // SUCCESS: no timeout, no error
    ///     func testPassthroughSubjectDoesNotPublishAnyElement() throws {
    ///         let publisher = PassthroughSubject<String, Never>()
    ///         let recorder = publisher.record()
    ///         try wait(for: recorder.next().inverted, timeout: 1)
    ///     }
    public struct NextOneInverted<Input, Failure: Error>: PublisherExpectation {
        let recorder: Recorder<Input, Failure>
        
        public func _setup(_ expectation: XCTestExpectation) {
            expectation.isInverted = true
            recorder.fulfillOnInput(expectation, includingConsumed: false)
        }
        
        public func get() throws {
            try recorder.value { (_, completion, remainingElements, consume) in
                if remainingElements.isEmpty == false {
                    return
                }
                if case let .failure(error) = completion {
                    throw error
                }
            }
        }
    }
}


================================================
FILE: Sources/CombineExpectations/PublisherExpectations/Prefix.swift
================================================
import XCTest

extension PublisherExpectations {
    /// A publisher expectation which waits for the recorded publisher to emit
    /// `maxLength` elements, or to complete.
    ///
    /// When waiting for this expectation, the publisher error is thrown if the
    /// publisher fails before `maxLength` elements are published.
    ///
    /// Otherwise, an array of received elements is returned, containing at
    /// most `maxLength` elements, or less if the publisher completes early.
    ///
    /// For example:
    ///
    ///     // SUCCESS: no timeout, no error
    ///     func testArrayOfThreeElementsPublishesTwoFirstElementsWithoutError() throws {
    ///         let publisher = ["foo", "bar", "baz"].publisher
    ///         let recorder = publisher.record()
    ///         let elements = try wait(for: recorder.prefix(2), timeout: 1)
    ///         XCTAssertEqual(elements, ["foo", "bar"])
    ///     }
    ///
    /// This publisher expectation can be inverted:
    ///
    ///     // SUCCESS: no timeout, no error
    ///     func testPassthroughSubjectPublishesNoMoreThanSentValues() throws {
    ///         let publisher = PassthroughSubject<String, Never>()
    ///         let recorder = publisher.record()
    ///         publisher.send("foo")
    ///         publisher.send("bar")
    ///         let elements = try wait(for: recorder.prefix(3).inverted, timeout: 1)
    ///         XCTAssertEqual(elements, ["foo", "bar"])
    ///     }
    public struct Prefix<Input, Failure: Error>: PublisherExpectation {
        let recorder: Recorder<Input, Failure>
        let maxLength: Int
        
        init(recorder: Recorder<Input, Failure>, maxLength: Int) {
            precondition(maxLength >= 0, "Can't take a prefix of negative length")
            self.recorder = recorder
            self.maxLength = maxLength
        }
        
        public func _setup(_ expectation: XCTestExpectation) {
            if maxLength == 0 {
                // Such an expectation is immediately fulfilled, by essence.
                expectation.expectedFulfillmentCount = 1
                expectation.fulfill()
            } else {
                expectation.expectedFulfillmentCount = maxLength
                recorder.fulfillOnInput(expectation, includingConsumed: true)
            }
        }
        
        /// Returns the expected output, or throws an error if the
        /// expectation fails.
        ///
        /// For example:
        ///
        ///     // SUCCESS: no error
        ///     func testArrayOfThreeElementsSynchronouslyPublishesTwoFirstElementsWithoutError() throws {
        ///         let publisher = ["foo", "bar", "baz"].publisher
        ///         let recorder = publisher.record()
        ///         let elements = try recorder.prefix(2).get()
        ///         XCTAssertEqual(elements, ["foo", "bar"])
        ///     }
        public func get() throws -> [Input] {
            try recorder.value { (elements, completion, remainingElements, consume) in
                if elements.count >= maxLength {
                    let extraCount = max(maxLength + remainingElements.count - elements.count, 0)
                    consume(extraCount)
                    return Array(elements.prefix(maxLength))
                }
                if case let .failure(error) = completion {
                    throw error
                }
                consume(remainingElements.count)
                return elements
            }
        }
        
        /// Returns an inverted publisher expectation which waits for a
        /// publisher to emit `maxLength` elements, or to complete.
        ///
        /// When waiting for this expectation, the publisher error is thrown
        /// if the publisher fails before `maxLength` elements are published.
        ///
        /// Otherwise, an array of received elements is returned, containing at
        /// most `maxLength` elements, or less if the publisher completes early.
        ///
        /// For example:
        ///
        ///     // SUCCESS: no timeout, no error
        ///     func testPassthroughSubjectPublishesNoMoreThanSentValues() throws {
        ///         let publisher = PassthroughSubject<String, Never>()
        ///         let recorder = publisher.record()
        ///         publisher.send("foo")
        ///         publisher.send("bar")
        ///         let elements = try wait(for: recorder.prefix(3).inverted, timeout: 1)
        ///         XCTAssertEqual(elements, ["foo", "bar"])
        ///     }
        public var inverted: Inverted<Self> {
            return Inverted(base: self)
        }
    }
}


================================================
FILE: Sources/CombineExpectations/PublisherExpectations/Recording.swift
================================================
import Combine
import XCTest

extension PublisherExpectations {
    /// A publisher expectation which waits for the recorded publisher
    /// to complete.
    ///
    /// When waiting for this expectation, a RecordingError.notCompleted is
    /// thrown if the publisher does not complete on time.
    ///
    /// Otherwise, a [Record.Recording](https://developer.apple.com/documentation/combine/record/recording)
    /// is returned.
    ///
    /// For example:
    ///
    ///     // SUCCESS: no timeout, no error
    ///     func testArrayPublisherRecording() throws {
    ///         let publisher = ["foo", "bar", "baz"].publisher
    ///         let recorder = publisher.record()
    ///         let recording = try wait(for: recorder.recording, timeout: 1)
    ///         XCTAssertEqual(recording.output, ["foo", "bar", "baz"])
    ///         if case let .failure(error) = recording.completion {
    ///             XCTFail("Unexpected error \(error)")
    ///         }
    ///     }
    public struct Recording<Input, Failure: Error>: PublisherExpectation {
        let recorder: Recorder<Input, Failure>
        
        public func _setup(_ expectation: XCTestExpectation) {
            recorder.fulfillOnCompletion(expectation)
        }
        
        /// Returns the expected output, or throws an error if the
        /// expectation fails.
        ///
        /// For example:
        ///
        ///     // SUCCESS: no error
        ///     func testArrayPublisherSynchronousRecording() throws {
        ///         let publisher = ["foo", "bar", "baz"].publisher
        ///         let recorder = publisher.record()
        ///         let recording = try recorder.recording.get()
        ///         XCTAssertEqual(recording.output, ["foo", "bar", "baz"])
        ///         if case let .failure(error) = recording.completion {
        ///             XCTFail("Unexpected error \(error)")
        ///         }
        ///     }
        public func get() throws -> Record<Input, Failure>.Recording {
            try recorder.value { (elements, completion, remainingElements, consume) in
                if let completion = completion {
                    consume(remainingElements.count)
                    return Record<Input, Failure>.Recording(output: elements, completion: completion)
                } else {
                    throw RecordingError.notCompleted
                }
            }
        }
    }
}


================================================
FILE: Sources/CombineExpectations/Recorder.swift
================================================
import Combine
import XCTest

/// A Combine subscriber which records all events published by a publisher.
///
/// You create a Recorder with the `Publisher.record()` method:
///
///     let publisher = ["foo", "bar", "baz"].publisher
///     let recorder = publisher.record()
///
/// You can build publisher expectations from the Recorder. For example:
///
///     let elements = try wait(for: recorder.elements, timeout: 1)
///     XCTAssertEqual(elements, ["foo", "bar", "baz"])
public class Recorder<Input, Failure: Error>: Subscriber {
    public typealias Input = Input
    public typealias Failure = Failure
    
    private enum RecorderExpectation {
        case onInput(XCTestExpectation, remainingCount: Int)
        case onCompletion(XCTestExpectation)
        
        var expectation: XCTestExpectation {
            switch self {
            case let .onCompletion(expectation):
                return expectation
            case let .onInput(expectation, remainingCount: _):
                return expectation
            }
        }
    }
    
    /// The recorder state
    private enum State {
        /// Publisher is not subscribed yet. The recorder may have an
        /// expectation to fulfill.
        case waitingForSubscription(RecorderExpectation?)
        
        /// Publisher is subscribed. The recorder may have an expectation to
        /// fulfill. It keeps track of all published elements.
        case subscribed(Subscription, RecorderExpectation?, [Input])
        
        /// Publisher is completed. The recorder keeps track of all published
        /// elements and completion.
        case completed([Input], Subscribers.Completion<Failure>)
        
        var elementsAndCompletion: (elements: [Input], completion: Subscribers.Completion<Failure>?) {
            switch self {
            case .waitingForSubscription:
                return (elements: [], completion: nil)
            case let .subscribed(_, _, elements):
                return (elements: elements, completion: nil)
            case let .completed(elements, completion):
                return (elements: elements, completion: completion)
            }
        }
        
        var recorderExpectation: RecorderExpectation? {
            switch self {
            case let .waitingForSubscription(exp), let .subscribed(_, exp, _):
                return exp
            case .completed:
                return nil
            }
        }
    }
    
    private let lock = NSLock()
    private var state = State.waitingForSubscription(nil)
    private var consumedCount = 0
    
    /// The elements and completion recorded so far.
    var elementsAndCompletion: (elements: [Input], completion: Subscribers.Completion<Failure>?) {
        synchronized {
            state.elementsAndCompletion
        }
    }
    
    /// Use Publisher.record()
    fileprivate init() { }
    
    deinit {
        if case let .subscribed(subscription, _, _) = state {
            subscription.cancel()
        }
    }
    
    private func synchronized<T>(_ execute: () throws -> T) rethrows -> T {
        lock.lock()
        defer { lock.unlock() }
        return try execute()
    }
    
    // MARK: - PublisherExpectation API
    
    /// Registers the expectation so that it gets fulfilled when publisher
    /// publishes elements or completes.
    ///
    /// - parameter expectation: An XCTestExpectation.
    /// - parameter includingConsumed: This flag controls how elements that were
    ///   already published at the time this method is called fulfill the
    ///   expectation. If true, all published elements fulfill the expectation.
    ///   If false, only published elements that are not consumed yet fulfill
    ///   the expectation. For example, the Prefix expectation uses true, but
    ///   the NextOne expectation uses false.
    func fulfillOnInput(_ expectation: XCTestExpectation, includingConsumed: Bool) {
        synchronized {
            preconditionCanFulfillExpectation()
            
            let expectedFulfillmentCount = expectation.expectedFulfillmentCount
            
            switch state {
            case .waitingForSubscription:
                let exp = RecorderExpectation.onInput(expectation, remainingCount: expectedFulfillmentCount)
                state = .waitingForSubscription(exp)
                
            case let .subscribed(subscription, _, elements):
                let maxFulfillmentCount = includingConsumed
                    ? elements.count
                    : elements.count - consumedCount
                let fulfillmentCount = min(expectedFulfillmentCount, maxFulfillmentCount)
                expectation.fulfill(count: fulfillmentCount)
                
                let remainingCount = expectedFulfillmentCount - fulfillmentCount
                if remainingCount > 0 {
                    let exp = RecorderExpectation.onInput(expectation, remainingCount: remainingCount)
                    state = .subscribed(subscription, exp, elements)
                }
                
            case .completed:
                expectation.fulfill(count: expectedFulfillmentCount)
            }
        }
    }
    
    /// Registers the expectation so that it gets fulfilled when
    /// publisher completes.
    func fulfillOnCompletion(_ expectation: XCTestExpectation) {
        synchronized {
            preconditionCanFulfillExpectation()
            
            switch state {
            case .waitingForSubscription:
                let exp = RecorderExpectation.onCompletion(expectation)
                state = .waitingForSubscription(exp)
                
            case let .subscribed(subscription, _, elements):
                let exp = RecorderExpectation.onCompletion(expectation)
                state = .subscribed(subscription, exp, elements)
                
            case .completed:
                expectation.fulfill()
            }
        }
    }
    
    /// Returns a value based on the recorded state of the publisher.
    ///
    /// - parameter value: A function which returns the value, given the
    ///   recorded state of the publisher.
    /// - parameter elements: All recorded elements.
    /// - parameter completion: The eventual publisher completion.
    /// - parameter remainingElements: The elements that were not consumed yet.
    /// - parameter consume: A function which consumes elements.
    /// - parameter count: The number of consumed elements.
    /// - returns: The value
    func value<T>(_ value: (
        _ elements: [Input],
        _ completion: Subscribers.Completion<Failure>?,
        _ remainingElements: ArraySlice<Input>,
        _ consume: (_ count: Int) -> ()) throws -> T)
        rethrows -> T
    {
        try synchronized {
            let (elements, completion) = state.elementsAndCompletion
            let remainingElements = elements[consumedCount...]
            return try value(elements, completion, remainingElements, { count in
                precondition(count >= 0)
                precondition(count <= remainingElements.count)
                consumedCount += count
            })
        }
    }
    
    /// Checks that recorder can fulfill an expectation.
    ///
    /// The reason this method exists is that a recorder can fulfill a single
    /// expectation at a given time. It is a programmer error to wait for two
    /// expectations concurrently.
    ///
    /// This method MUST be called within a synchronized block.
    private func preconditionCanFulfillExpectation() {
        if let exp = state.recorderExpectation {
            // We are already waiting for an expectation! Is it a programmer
            // error? Recorder drops references to non-inverted expectations
            // when they are fulfilled. But inverted expectations are not
            // fulfilled, and thus not dropped. We can't quite know if an
            // inverted expectations has expired yet, so just let it go.
            precondition(exp.expectation.isInverted, "Already waiting for an expectation")
        }
    }
    
    // MARK: - Subscriber
    
    public func receive(subscription: Subscription) {
        synchronized {
            switch state {
            case let .waitingForSubscription(exp):
                state = .subscribed(subscription, exp, [])
            default:
                XCTFail("Publisher recorder is already subscribed")
            }
        }
        subscription.request(.unlimited)
    }
    
    public func receive(_ input: Input) -> Subscribers.Demand {
        return synchronized {
            switch state {
            case let .subscribed(subscription, exp, elements):
                var elements = elements
                elements.append(input)
                
                if case let .onInput(expectation, remainingCount: remainingCount) = exp {
                    assert(remainingCount > 0)
                    expectation.fulfill()
                    if remainingCount > 1 {
                        let exp = RecorderExpectation.onInput(expectation, remainingCount: remainingCount - 1)
                        state = .subscribed(subscription, exp, elements)
                    } else {
                        state = .subscribed(subscription, nil, elements)
                    }
                } else {
                    state = .subscribed(subscription, exp, elements)
                }
                
                return .unlimited
                
            case .waitingForSubscription:
                XCTFail("Publisher recorder got unexpected input before subscription: \(String(reflecting: input))")
                return .none
                
            case .completed:
                XCTFail("Publisher recorder got unexpected input after completion: \(String(reflecting: input))")
                return .none
            }
        }
    }
    
    public func receive(completion: Subscribers.Completion<Failure>) {
        synchronized {
            switch state {
            case let .subscribed(_, exp, elements):
                if let exp = exp {
                    switch exp {
                    case let .onCompletion(expectation):
                        expectation.fulfill()
                    case let .onInput(expectation, remainingCount: remainingCount):
                        expectation.fulfill(count: remainingCount)
                    }
                }
                state = .completed(elements, completion)
                
            case .waitingForSubscription:
                XCTFail("Publisher recorder got unexpected completion before subscription: \(String(describing: completion))")
                
            case .completed:
                XCTFail("Publisher recorder got unexpected completion after completion: \(String(describing: completion))")
            }
        }
    }
}

// MARK: - Publisher Expectations

extension PublisherExpectations {
    /// The type of the publisher expectation returned by `Recorder.completion`.
    public typealias Completion<Input, Failure: Error> = Map<Recording<Input, Failure>, Subscribers.Completion<Failure>>
    
    /// The type of the publisher expectation returned by `Recorder.elements`.
    public typealias Elements<Input, Failure: Error> = Map<Recording<Input, Failure>, [Input]>
    
    /// The type of the publisher expectation returned by `Recorder.last`.
    public typealias Last<Input, Failure: Error> = Map<Elements<Input, Failure>, Input?>
    
    /// The type of the publisher expectation returned by `Recorder.single`.
    public typealias Single<Input, Failure: Error> = Map<Elements<Input, Failure>, Input>
}

extension Recorder {
    /// Returns a publisher expectation which waits for the timeout to expire,
    /// or the recorded publisher to complete.
    ///
    /// When waiting for this expectation, the publisher error is thrown if
    /// the publisher fails before the expectation has expired.
    ///
    /// Otherwise, an array of all elements published before the expectation
    /// has expired is returned.
    ///
    /// Unlike other expectations, `availableElements` does not make a test fail
    /// on timeout expiration. It just returns the elements published so far.
    ///
    /// For example:
    ///
    ///     // SUCCESS: no timeout, no error
    ///     func testTimerPublishesIncreasingDates() throws {
    ///         let publisher = Timer.publish(every: 0.01, on: .main, in: .common).autoconnect()
    ///         let recorder = publisher.record()
    ///         let dates = try wait(for: recorder.availableElements, timeout: ...)
    ///         XCTAssertEqual(dates.sorted(), dates)
    ///     }
    public var availableElements: PublisherExpectations.AvailableElements<Input, Failure> {
        PublisherExpectations.AvailableElements(recorder: self)
    }
    
    /// Returns a publisher expectation which waits for the recorded publisher
    /// to complete.
    ///
    /// When waiting for this expectation, a RecordingError.notCompleted is
    /// thrown if the publisher does not complete on time.
    ///
    /// Otherwise, a [Subscribers.Completion](https://developer.apple.com/documentation/combine/subscribers/completion)
    /// is returned.
    ///
    /// For example:
    ///
    ///     // SUCCESS: no timeout, no error
    ///     func testArrayPublisherCompletesWithSuccess() throws {
    ///         let publisher = ["foo", "bar", "baz"].publisher
    ///         let recorder = publisher.record()
    ///         let completion = try wait(for: recorder.completion, timeout: 1)
    ///         if case let .failure(error) = completion {
    ///             XCTFail("Unexpected error \(error)")
    ///         }
    ///     }
    public var completion: PublisherExpectations.Completion<Input, Failure> {
        recording.map { $0.completion }
    }
    
    /// Returns a publisher expectation which waits for the recorded publisher
    /// to complete.
    ///
    /// When waiting for this expectation, a RecordingError.notCompleted is
    /// thrown if the publisher does not complete on time, and the publisher
    /// error is thrown if the publisher fails.
    ///
    /// Otherwise, an array of published elements is returned.
    ///
    /// For example:
    ///
    ///     // SUCCESS: no timeout, no error
    ///     func testArrayPublisherPublishesArrayElements() throws {
    ///         let publisher = ["foo", "bar", "baz"].publisher
    ///         let recorder = publisher.record()
    ///         let elements = try wait(for: recorder.elements, timeout: 1)
    ///         XCTAssertEqual(elements, ["foo", "bar", "baz"])
    ///     }
    public var elements: PublisherExpectations.Elements<Input, Failure> {
        recording.map { recording in
            if case let .failure(error) = recording.completion {
                throw error
            }
            return recording.output
        }
    }
    
    /// Returns a publisher expectation which waits for the recorded publisher
    /// to complete.
    ///
    /// When waiting for this expectation, the publisher error is thrown if the
    /// publisher fails.
    ///
    /// For example:
    ///
    ///     // SUCCESS: no timeout, no error
    ///     func testArrayPublisherFinishesWithoutError() throws {
    ///         let publisher = ["foo", "bar", "baz"].publisher
    ///         let recorder = publisher.record()
    ///         try wait(for: recorder.finished, timeout: 1)
    ///     }
    ///
    /// This publisher expectation can be inverted:
    ///
    ///     // SUCCESS: no timeout, no error
    ///     func testPassthroughSubjectDoesNotFinish() throws {
    ///         let publisher = PassthroughSubject<String, Never>()
    ///         let recorder = publisher.record()
    ///         try wait(for: recorder.finished.inverted, timeout: 1)
    ///     }
    public var finished: PublisherExpectations.Finished<Input, Failure> {
        PublisherExpectations.Finished(recorder: self)
    }
    
    /// Returns a publisher expectation which waits for the recorded publisher
    /// to complete.
    ///
    /// When waiting for this expectation, a RecordingError.notCompleted is
    /// thrown if the publisher does not complete on time, and the publisher
    /// error is thrown if the publisher fails.
    ///
    /// Otherwise, the last published element is returned, or nil if the publisher
    /// completes before it publishes any element.
    ///
    /// For example:
    ///
    ///     // SUCCESS: no timeout, no error
    ///     func testArrayPublisherPublishesLastElementLast() throws {
    ///         let publisher = ["foo", "bar", "baz"].publisher
    ///         let recorder = publisher.record()
    ///         if let element = try wait(for: recorder.last, timeout: 1) {
    ///             XCTAssertEqual(element, "baz")
    ///         } else {
    ///             XCTFail("Expected one element")
    ///         }
    ///     }
    public var last: PublisherExpectations.Last<Input, Failure> {
        elements.map { $0.last }
    }
    
    /// Returns a publisher expectation which waits for the recorded publisher
    /// to emit one element, or to complete.
    ///
    /// When waiting for this expectation, a `RecordingError.notEnoughElements`
    /// is thrown if the publisher does not publish one element after last
    /// waited expectation. The publisher error is thrown if the publisher fails
    /// before publishing the next element.
    ///
    /// Otherwise, the next published element is returned.
    ///
    /// For example:
    ///
    ///     // SUCCESS: no timeout, no error
    ///     func testArrayOfTwoElementsPublishesElementsInOrder() throws {
    ///         let publisher = ["foo", "bar"].publisher
    ///         let recorder = publisher.record()
    ///
    ///         var element = try wait(for: recorder.next(), timeout: 1)
    ///         XCTAssertEqual(element, "foo")
    ///
    ///         element = try wait(for: recorder.next(), timeout: 1)
    ///         XCTAssertEqual(element, "bar")
    ///     }
    public func next() -> PublisherExpectations.NextOne<Input, Failure> {
        PublisherExpectations.NextOne(recorder: self)
    }
    
    /// Returns a publisher expectation which waits for the recorded publisher
    /// to emit `count` elements, or to complete.
    ///
    /// When waiting for this expectation, a `RecordingError.notEnoughElements`
    /// is thrown if the publisher does not publish `count` elements after last
    /// waited expectation. The publisher error is thrown if the publisher fails
    /// before publishing the next `count` elements.
    ///
    /// Otherwise, an array of exactly `count` elements is returned.
    ///
    /// For example:
    ///
    ///     // SUCCESS: no timeout, no error
    ///     func testArrayOfThreeElementsPublishesTwoThenOneElement() throws {
    ///         let publisher = ["foo", "bar", "baz"].publisher
    ///         let recorder = publisher.record()
    ///
    ///         var elements = try wait(for: recorder.next(2), timeout: 1)
    ///         XCTAssertEqual(elements, ["foo", "bar"])
    ///
    ///         elements = try wait(for: recorder.next(1), timeout: 1)
    ///         XCTAssertEqual(elements, ["baz"])
    ///     }
    ///
    /// - parameter count: The number of elements.
    public func next(_ count: Int) -> PublisherExpectations.Next<Input, Failure> {
        PublisherExpectations.Next(recorder: self, count: count)
    }
    
    /// Returns a publisher expectation which waits for the recorded publisher
    /// to emit `maxLength` elements, or to complete.
    ///
    /// When waiting for this expectation, the publisher error is thrown if the
    /// publisher fails before `maxLength` elements are published.
    ///
    /// Otherwise, an array of received elements is returned, containing at
    /// most `maxLength` elements, or less if the publisher completes early.
    ///
    /// For example:
    ///
    ///     // SUCCESS: no timeout, no error
    ///     func testArrayOfThreeElementsPublishesTwoFirstElementsWithoutError() throws {
    ///         let publisher = ["foo", "bar", "baz"].publisher
    ///         let recorder = publisher.record()
    ///         let elements = try wait(for: recorder.prefix(2), timeout: 1)
    ///         XCTAssertEqual(elements, ["foo", "bar"])
    ///     }
    ///
    /// This publisher expectation can be inverted:
    ///
    ///     // SUCCESS: no timeout, no error
    ///     func testPassthroughSubjectPublishesNoMoreThanSentValues() throws {
    ///         let publisher = PassthroughSubject<String, Never>()
    ///         let recorder = publisher.record()
    ///         publisher.send("foo")
    ///         publisher.send("bar")
    ///         let elements = try wait(for: recorder.prefix(3).inverted, timeout: 1)
    ///         XCTAssertEqual(elements, ["foo", "bar"])
    ///     }
    ///
    /// - parameter maxLength: The maximum number of elements.
    public func prefix(_ maxLength: Int) -> PublisherExpectations.Prefix<Input, Failure> {
        PublisherExpectations.Prefix(recorder: self, maxLength: maxLength)
    }
    
    /// Returns a publisher expectation which waits for the recorded publisher
    /// to complete.
    ///
    /// When waiting for this expectation, a RecordingError.notCompleted is
    /// thrown if the publisher does not complete on time.
    ///
    /// Otherwise, a [Record.Recording](https://developer.apple.com/documentation/combine/record/recording)
    /// is returned.
    ///
    /// For example:
    ///
    ///     // SUCCESS: no timeout, no error
    ///     func testArrayPublisherRecording() throws {
    ///         let publisher = ["foo", "bar", "baz"].publisher
    ///         let recorder = publisher.record()
    ///         let recording = try wait(for: recorder.recording, timeout: 1)
    ///         XCTAssertEqual(recording.output, ["foo", "bar", "baz"])
    ///         if case let .failure(error) = recording.completion {
    ///             XCTFail("Unexpected error \(error)")
    ///         }
    ///     }
    public var recording: PublisherExpectations.Recording<Input, Failure> {
        PublisherExpectations.Recording(recorder: self)
    }
    
    /// Returns a publisher expectation which waits for the recorded publisher
    /// to complete.
    ///
    /// When waiting for this expectation, a RecordingError is thrown if the
    /// publisher does not complete on time, or does not publish exactly one
    /// element before it completes. The publisher error is thrown if the
    /// publisher fails.
    ///
    /// Otherwise, the single published element is returned.
    ///
    /// For example:
    ///
    ///     // SUCCESS: no timeout, no error
    ///     func testJustPublishesExactlyOneElement() throws {
    ///         let publisher = Just("foo")
    ///         let recorder = publisher.record()
    ///         let element = try wait(for: recorder.single, timeout: 1)
    ///         XCTAssertEqual(element, "foo")
    ///     }
    public var single: PublisherExpectations.Single<Input, Failure> {
        elements.map { elements in
            guard let element = elements.first else {
                throw RecordingError.notEnoughElements
            }
            if elements.count > 1 {
                throw RecordingError.tooManyElements
            }
            return element
        }
    }
}

// MARK: - Publisher + Recorder

extension Publisher {
    /// Returns a subscribed Recorder.
    ///
    /// For example:
    ///
    ///     let publisher = ["foo", "bar", "baz"].publisher
    ///     let recorder = publisher.record()
    ///
    /// You can build publisher expectations from the Recorder. For example:
    ///
    ///     let elements = try wait(for: recorder.elements, timeout: 1)
    ///     XCTAssertEqual(elements, ["foo", "bar", "baz"])
    public func record() -> Recorder<Output, Failure> {
        let recorder = Recorder<Output, Failure>()
        subscribe(recorder)
        return recorder
    }
}

// MARK: - Convenience

extension XCTestExpectation {
    fileprivate func fulfill(count: Int) {
        for _ in 0..<count {
            fulfill()
        }
    }
}


================================================
FILE: Sources/CombineExpectations/RecordingError.swift
================================================
import Foundation

/// An error that may be thrown when waiting for publisher expectations.
public enum RecordingError: Error {
    /// The publisher did not complete.
    case notCompleted
    
    /// The publisher did not publish enough elements.
    /// For example, see `recorder.single`.
    case notEnoughElements
    
    /// The publisher did publish too many elements.
    /// For example, see `recorder.single`.
    case tooManyElements
}

extension RecordingError: LocalizedError {
    public var errorDescription: String? {
        switch self {
        case .notCompleted:
            return "RecordingError.notCompleted"
        case .notEnoughElements:
            return "RecordingError.notEnoughElements"
        case .tooManyElements:
            return "RecordingError.tooManyElements"
        }
    }
}


================================================
FILE: Tests/CombineExpectationsTests/DocumentationTests.swift
================================================
import XCTest
import Combine
import CombineExpectations

/// Tests for sample code in documentation
class DocumentationTests: FailureTestCase {
    private struct MyError: Error { }
    
    // MARK: - Completion
    
    // SUCCESS: no error
    func testArrayPublisherSynchronouslyCompletesWithSuccess() throws {
        let publisher = ["foo", "bar", "baz"].publisher
        let recorder = publisher.record()
        let completion = try recorder.completion.get()
        if case let .failure(error) = completion {
            XCTFail("Unexpected error \(error)")
        }
    }
    
    // SUCCESS: no timeout, no error
    func testArrayPublisherCompletesWithSuccess() throws {
        let publisher = ["foo", "bar", "baz"].publisher
        let recorder = publisher.record()
        let completion = try wait(for: recorder.completion, timeout: 0.1)
        if case let .failure(error) = completion {
            XCTFail("Unexpected error \(error)")
        }
    }
    
    // FAIL: Asynchronous wait failed
    // FAIL: Caught error RecordingError.notCompleted
    func testCompletionTimeout() throws {
        try assertFailure("Asynchronous wait failed") {
            do {
                let publisher = PassthroughSubject<String, Never>()
                let recorder = publisher.record()
                _ = try wait(for: recorder.completion, timeout: 0.1)
                XCTFail("Expected error")
            } catch RecordingError.notCompleted { }
        }
    }
    
    // MARK: - Elements
    
    func testArrayPublisherSynchronouslyPublishesArrayElements() throws {
        let publisher = ["foo", "bar", "baz"].publisher
        let recorder = publisher.record()
        let elements = try recorder.elements.get()
        XCTAssertEqual(elements, ["foo", "bar", "baz"])
    }
    
    // SUCCESS: no timeout, no error
    func testArrayPublisherPublishesArrayElements() throws {
        let publisher = ["foo", "bar", "baz"].publisher
        let recorder = publisher.record()
        let elements = try wait(for: recorder.elements, timeout: 0.1)
        XCTAssertEqual(elements, ["foo", "bar", "baz"])
    }

    // FAIL: Asynchronous wait failed
    // FAIL: Caught error RecordingError.notCompleted
    func testElementsTimeout() throws {
        try assertFailure("Asynchronous wait failed") {
            do {
                let publisher = PassthroughSubject<String, Never>()
                let recorder = publisher.record()
                _ = try wait(for: recorder.elements, timeout: 0.1)
                XCTFail("Expected error")
            } catch RecordingError.notCompleted { }
        }
    }
    
    // FAIL: Caught error MyError
    func testElementsSynchronousError() throws {
        do {
            let publisher = PassthroughSubject<String, MyError>()
            let recorder = publisher.record()
            publisher.send(completion: .failure(MyError()))
            _ = try recorder.elements.get()
            XCTFail("Expected error")
        } catch is MyError { }
    }

    // FAIL: Caught error MyError
    func testElementsError() throws {
        do {
            let publisher = PassthroughSubject<String, MyError>()
            let recorder = publisher.record()
            publisher.send(completion: .failure(MyError()))
            _ = try wait(for: recorder.elements, timeout: 0.1)
            XCTFail("Expected error")
        } catch is MyError { }
    }
    
    // MARK: - Finished
    
    // SUCCESS: no error
    func testArrayPublisherSynchronouslyFinishesWithoutError() throws {
        let publisher = ["foo", "bar", "baz"].publisher
        let recorder = publisher.record()
        try recorder.finished.get()
    }
    
    // SUCCESS: no timeout, no error
    func testArrayPublisherFinishesWithoutError() throws {
        let publisher = ["foo", "bar", "baz"].publisher
        let recorder = publisher.record()
        try wait(for: recorder.finished, timeout: 0.1)
    }
    
    // FAIL: Asynchronous wait failed
    func testFinishedTimeout() throws {
        try assertFailure("Asynchronous wait failed") {
            let publisher = PassthroughSubject<String, Never>()
            let recorder = publisher.record()
            try wait(for: recorder.finished, timeout: 0.1)
        }
    }
    
    // FAIL: Caught error MyError
    func testFinishedError() throws {
        do {
            let publisher = PassthroughSubject<String, MyError>()
            let recorder = publisher.record()
            publisher.send(completion: .failure(MyError()))
            try wait(for: recorder.finished, timeout: 0.1)
            XCTFail("Expected error")
        } catch is MyError { }
    }
    
    // MARK: - Finished.inverted
    
    // SUCCESS: no timeout, no error
    func testPassthroughSubjectDoesNotFinish() throws {
        let publisher = PassthroughSubject<String, Never>()
        let recorder = publisher.record()
        try wait(for: recorder.finished.inverted, timeout: 0.1)
    }
    
    // FAIL: Fulfilled inverted expectation
    // FAIL: Caught error MyError
    func testInvertedFinishedError() throws {
        try assertFailure("Fulfilled inverted expectation") {
            do {
                let publisher = PassthroughSubject<String, MyError>()
                let recorder = publisher.record()
                publisher.send(completion: .failure(MyError()))
                try wait(for: recorder.finished.inverted, timeout: 0.1)
                XCTFail("Expected error")
            } catch is MyError { }
        }
    }
    
    // MARK: - Last
    
    // SUCCESS: no error
    func testArrayPublisherSynchronouslyPublishesLastElementLast() throws {
        let publisher = ["foo", "bar", "baz"].publisher
        let recorder = publisher.record()
        if let element = try recorder.last.get() {
            XCTAssertEqual(element, "baz")
        } else {
            XCTFail("Expected one element")
        }
    }
    
    // SUCCESS: no timeout, no error
    func testArrayPublisherPublishesLastElementLast() throws {
        let publisher = ["foo", "bar", "baz"].publisher
        let recorder = publisher.record()
        if let element = try wait(for: recorder.last, timeout: 0.1) {
            XCTAssertEqual(element, "baz")
        } else {
            XCTFail("Expected one element")
        }
    }
    
    // FAIL: Asynchronous wait failed
    // FAIL: Caught error RecordingError.notCompleted
    func testLastTimeout() throws {
        try assertFailure("Asynchronous wait failed") {
            do {
                let publisher = PassthroughSubject<String, Never>()
                let recorder = publisher.record()
                _ = try wait(for: recorder.last, timeout: 0.1)
                XCTFail("Expected error")
            } catch RecordingError.notCompleted { }
        }
    }
    
    // FAIL: Caught error MyError
    func testLastError() throws {
        do {
            let publisher = PassthroughSubject<String, MyError>()
            let recorder = publisher.record()
            publisher.send(completion: .failure(MyError()))
            _ = try wait(for: recorder.last, timeout: 0.1)
            XCTFail("Expected error")
        } catch is MyError { }
    }
    
    // MARK: - next()
    
    // SUCCESS: no error
    func testPassthroughSubjectSynchronouslyPublishesElements() throws {
        let publisher = PassthroughSubject<String, Never>()
        let recorder = publisher.record()
        
        publisher.send("foo")
        try XCTAssertEqual(recorder.next().get(), "foo")
        
        publisher.send("bar")
        try XCTAssertEqual(recorder.next().get(), "bar")
    }
    
    // SUCCESS: no error
    func testArrayOfTwoElementsSynchronouslyPublishesElementsInOrder() throws {
        let publisher = ["foo", "bar"].publisher
        let recorder = publisher.record()
    
        var element = try recorder.next().get()
        XCTAssertEqual(element, "foo")
    
        element = try recorder.next().get()
        XCTAssertEqual(element, "bar")
    }
    
    // SUCCESS: no timeout, no error
    func testArrayOfTwoElementsPublishesElementsInOrder() throws {
        let publisher = ["foo", "bar"].publisher
        let recorder = publisher.record()
        
        var element = try wait(for: recorder.next(), timeout: 0.1)
        XCTAssertEqual(element, "foo")
        
        element = try wait(for: recorder.next(), timeout: 0.1)
        XCTAssertEqual(element, "bar")
    }
    
    // FAIL: Asynchronous wait failed
    // FAIL: Caught error RecordingError.notEnoughElements
    func testNextTimeout() throws {
        try assertFailure("Asynchronous wait failed") {
            do {
                let publisher = PassthroughSubject<String, Never>()
                let recorder = publisher.record()
                _ = try wait(for: recorder.next(), timeout: 0.1)
                XCTFail("Expected error")
            } catch RecordingError.notEnoughElements { }
        }
    }
    
    // FAIL: Caught error MyError
    func testNextError() throws {
        do {
            let publisher = PassthroughSubject<String, MyError>()
            let recorder = publisher.record()
            publisher.send(completion: .failure(MyError()))
            _ = try wait(for: recorder.next(), timeout: 0.1)
            XCTFail("Expected error")
        } catch is MyError { }
    }
    
    // FAIL: Caught error RecordingError.notEnoughElements
    func testNextNotEnoughElementsError() throws {
        do {
            let publisher = PassthroughSubject<String, Never>()
            let recorder = publisher.record()
            publisher.send(completion: .finished)
            _ = try wait(for: recorder.next(), timeout: 0.1)
            XCTFail("Expected error")
        } catch RecordingError.notEnoughElements { }
    }
    
    // MARK: - next().inverted
    
    // SUCCESS: no timeout, no error
    func testPassthroughSubjectDoesNotPublishAnyElement() throws {
        let publisher = PassthroughSubject<String, Never>()
        let recorder = publisher.record()
        try wait(for: recorder.next().inverted, timeout: 0.1)
    }
    
    // FAIL: Fulfilled inverted expectation
    func testInvertedNextTooEarly() throws {
        try assertFailure("Fulfilled inverted expectation") {
            let publisher = PassthroughSubject<String, Never>()
            let recorder = publisher.record()
            publisher.send("foo")
            try wait(for: recorder.next().inverted, timeout: 0.1)
        }
    }
    
    // FAIL: Fulfilled inverted expectation
    // FAIL: Caught error MyError
    func testInvertedNextError() throws {
        try assertFailure("Fulfilled inverted expectation") {
            do {
                let publisher = PassthroughSubject<String, MyError>()
                let recorder = publisher.record()
                publisher.send(completion: .failure(MyError()))
                try wait(for: recorder.next().inverted, timeout: 0.1)
                XCTFail("Expected error")
            } catch is MyError { }
        }
    }
    
    // MARK: - next(count)
    
    // SUCCESS: no error
    func testArrayOfThreeElementsSynchronouslyPublishesTwoThenOneElement() throws {
        let publisher = ["foo", "bar", "baz"].publisher
        let recorder = publisher.record()
    
        var elements = try recorder.next(2).get()
        XCTAssertEqual(elements, ["foo", "bar"])
    
        elements = try recorder.next(1).get()
        XCTAssertEqual(elements, ["baz"])
    }
    
    // SUCCESS: no timeout, no error
    func testArrayOfThreeElementsPublishesTwoThenOneElement() throws {
        let publisher = ["foo", "bar", "baz"].publisher
        let recorder = publisher.record()
        
        var elements = try wait(for: recorder.next(2), timeout: 0.1)
        XCTAssertEqual(elements, ["foo", "bar"])
        
        elements = try wait(for: recorder.next(1), timeout: 0.1)
        XCTAssertEqual(elements, ["baz"])
    }
    
    // FAIL: Asynchronous wait failed
    // FAIL: Caught error RecordingError.notEnoughElements
    func testNextCountTimeout() throws {
        try assertFailure("Asynchronous wait failed") {
            do {
                let publisher = PassthroughSubject<String, Never>()
                let recorder = publisher.record()
                publisher.send("foo")
                _ = try wait(for: recorder.next(2), timeout: 0.1)
                XCTFail("Expected error")
            } catch RecordingError.notEnoughElements { }
        }
    }
    
    // FAIL: Caught error MyError
    func testNextCountError() throws {
        do {
            let publisher = PassthroughSubject<String, MyError>()
            let recorder = publisher.record()
            publisher.send("foo")
            publisher.send(completion: .failure(MyError()))
            _ = try wait(for: recorder.next(2), timeout: 0.1)
            XCTFail("Expected error")
        } catch is MyError { }
    }
    
    // FAIL: Caught error RecordingError.notEnoughElements
    func testNextCountNotEnoughElementsError() throws {
        do {
            let publisher = PassthroughSubject<String, Never>()
            let recorder = publisher.record()
            publisher.send("foo")
            publisher.send(completion: .finished)
            _ = try wait(for: recorder.next(2), timeout: 0.1)
            XCTFail("Expected error")
        } catch RecordingError.notEnoughElements { }
    }
    
    // MARK: - Prefix
    
    // SUCCESS: no error
    func testArrayOfThreeElementsSynchronouslyPublishesTwoFirstElementsWithoutError() throws {
        let publisher = ["foo", "bar", "baz"].publisher
        let recorder = publisher.record()
        let elements = try recorder.prefix(2).get()
        XCTAssertEqual(elements, ["foo", "bar"])
    }
    
    // SUCCESS: no timeout, no error
    func testArrayOfThreeElementsPublishesTwoFirstElementsWithoutError() throws {
        let publisher = ["foo", "bar", "baz"].publisher
        let recorder = publisher.record()
        let elements = try wait(for: recorder.prefix(2), timeout: 0.1)
        XCTAssertEqual(elements, ["foo", "bar"])
    }
    
    // FAIL: Asynchronous wait failed
    func testPrefixTimeout() throws {
        try assertFailure("Asynchronous wait failed") {
            let publisher = PassthroughSubject<String, Never>()
            let recorder = publisher.record()
            publisher.send("foo")
            _ = try wait(for: recorder.prefix(2), timeout: 0.1)
        }
    }
    
    // FAIL: Caught error MyError
    func testPrefixError() throws {
        do {
            let publisher = PassthroughSubject<String, MyError>()
            let recorder = publisher.record()
            publisher.send("foo")
            publisher.send(completion: .failure(MyError()))
            _ = try wait(for: recorder.prefix(2), timeout: 0.1)
            XCTFail("Expected error")
        } catch is MyError { }
    }
    
    // MARK: - Prefix.inverted
    
    // SUCCESS: no timeout, no error
    func testPassthroughSubjectPublishesNoMoreThanSentValues() throws {
        let publisher = PassthroughSubject<String, Never>()
        let recorder = publisher.record()
        publisher.send("foo")
        publisher.send("bar")
        let elements = try wait(for: recorder.prefix(3).inverted, timeout: 0.1)
        XCTAssertEqual(elements, ["foo", "bar"])
    }
    
    // FAIL: Fulfilled inverted expectation
    func testInvertedPrefixTooEarly() throws {
        try assertFailure("Fulfilled inverted expectation") {
            let publisher = PassthroughSubject<String, Never>()
            let recorder = publisher.record()
            publisher.send("foo")
            publisher.send("bar")
            publisher.send("baz")
            _ = try wait(for: recorder.prefix(3).inverted, timeout: 0.1)
        }
    }
    
    // FAIL: Fulfilled inverted expectation
    // FAIL: Caught error MyError
    func testInvertedPrefixError() throws {
        try assertFailure("Fulfilled inverted expectation") {
            do {
                let publisher = PassthroughSubject<String, MyError>()
                let recorder = publisher.record()
                publisher.send("foo")
                publisher.send(completion: .failure(MyError()))
                _ = try wait(for: recorder.prefix(3).inverted, timeout: 0.1)
                XCTFail("Expected error")
            } catch is MyError { }
        }
    }
    
    // MARK: - Recording
    
    // SUCCESS: no error
    func testArrayPublisherSynchronousRecording() throws {
        let publisher = ["foo", "bar", "baz"].publisher
        let recorder = publisher.record()
        let recording = try recorder.recording.get()
        XCTAssertEqual(recording.output, ["foo", "bar", "baz"])
        if case let .failure(error) = recording.completion {
            XCTFail("Unexpected error \(error)")
        }
    }
    
    // SUCCESS: no timeout, no error
    func testArrayPublisherRecording() throws {
        let publisher = ["foo", "bar", "baz"].publisher
        let recorder = publisher.record()
        let recording = try wait(for: recorder.recording, timeout: 0.1)
        XCTAssertEqual(recording.output, ["foo", "bar", "baz"])
        if case let .failure(error) = recording.completion {
            XCTFail("Unexpected error \(error)")
        }
    }
    
    // FAIL: Asynchronous wait failed
    // FAIL: Caught error RecordingError.notCompleted
    func testRecordingTimeout() throws {
        try assertFailure("Asynchronous wait failed") {
            do {
                let publisher = PassthroughSubject<String, Never>()
                let recorder = publisher.record()
                _ = try wait(for: recorder.recording, timeout: 0.1)
                XCTFail("Expected error")
            } catch RecordingError.notCompleted { }
        }
    }
    
    // MARK: - Single
    
    // SUCCESS: no error
    func testJustSynchronouslyPublishesExactlyOneElement() throws {
        let publisher = Just("foo")
        let recorder = publisher.record()
        let element = try recorder.single.get()
        XCTAssertEqual(element, "foo")
    }
    
    // SUCCESS: no timeout, no error
    func testJustPublishesExactlyOneElement() throws {
        let publisher = Just("foo")
        let recorder = publisher.record()
        let element = try wait(for: recorder.single, timeout: 0.1)
        XCTAssertEqual(element, "foo")
    }
    
    // FAIL: Asynchronous wait failed
    // FAIL: Caught error RecordingError.notCompleted
    func testSingleTimeout() throws {
        try assertFailure("Asynchronous wait failed") {
            do {
                let publisher = PassthroughSubject<String, Never>()
                let recorder = publisher.record()
                _ = try wait(for: recorder.single, timeout: 0.1)
                XCTFail("Expected error")
            } catch RecordingError.notCompleted { }
        }
    }
    
    // FAIL: Caught error MyError
    func testSingleError() throws {
        do {
            let publisher = PassthroughSubject<String, MyError>()
            let recorder = publisher.record()
            publisher.send(completion: .failure(MyError()))
            _ = try wait(for: recorder.single, timeout: 0.1)
            XCTFail("Expected error")
        } catch is MyError { }
    }
    
    // FAIL: Caught error RecordingError.tooManyElements
    func testSingleTooManyElementsError() throws {
        do {
            let publisher = PassthroughSubject<String, Never>()
            let recorder = publisher.record()
            publisher.send("foo")
            publisher.send("bar")
            publisher.send(completion: .finished)
            _ = try wait(for: recorder.single, timeout: 0.1)
            XCTFail("Expected error")
        } catch RecordingError.tooManyElements { }
    }
    
    // FAIL: Caught error RecordingError.notEnoughElements
    func testSingleNotEnoughElementsError() throws {
        do {
            let publisher = PassthroughSubject<String, Never>()
            let recorder = publisher.record()
            publisher.send(completion: .finished)
            _ = try wait(for: recorder.single, timeout: 0.1)
            XCTFail("Expected error")
        } catch RecordingError.notEnoughElements { }
    }
}


================================================
FILE: Tests/CombineExpectationsTests/FailureTestCase.swift
================================================
import XCTest

/// A XCTestCase subclass that can test its own failures.
class FailureTestCase: XCTestCase {
    private struct Failure: Hashable {
        #if compiler(>=5.3)
        let issue: XCTIssue
        #else
        var description: String
        var file: String
        var line: Int
        var expected: Bool
        #endif
        
        #if compiler(>=5.3)
        func issue(prefix: String = "") -> XCTIssue {
            if prefix.isEmpty {
                return issue
            } else {
                return XCTIssue(
                    type: issue.type,
                    compactDescription: "\(prefix): \(issue.compactDescription)",
                    detailedDescription: issue.detailedDescription,
                    sourceCodeContext: issue.sourceCodeContext,
                    associatedError: issue.associatedError,
                    attachments: issue.attachments)
            }
        }
        #else
        func failure(prefix: String = "") -> (description: String, file: String, line: Int, expected: Bool) {
            let prefix = prefix.isEmpty ? "" : "\(prefix): "
            return (
                description: prefix + description,
                file: file,
                line: line,
                expected: expected)
        }
        #endif
        
        #if compiler(>=5.3)
        private var description: String {
            return issue.compactDescription
        }
        #endif
        
        func hash(into hasher: inout Hasher) {
            hasher.combine(0)
        }
        
        static func == (lhs: Failure, rhs: Failure) -> Bool {
            lhs.description.hasPrefix(rhs.description) || rhs.description.hasPrefix(lhs.description)
        }
    }
    
    private var recordedFailures: [Failure] = []
    private var isRecordingFailures = false
    
    func assertFailure(_ prefixes: String..., file: StaticString = #file, line: UInt = #line, _ execute: () throws -> Void) rethrows {
        let recordedFailures = try recordingFailures(execute)
        if prefixes.isEmpty {
            if recordedFailures.isEmpty {
                #if compiler(>=5.3)
                record(XCTIssue(
                        type: .assertionFailure,
                        compactDescription: "No failure did happen",
                        detailedDescription: nil,
                        sourceCodeContext: XCTSourceCodeContext(
                            location: XCTSourceCodeLocation(
                                filePath: String(describing: file),
                                lineNumber: Int(line))),
                        associatedError: nil,
                        attachments: []))
                #else
                recordFailure(
                    withDescription: "No failure did happen",
                    inFile: file.description,
                    atLine: Int(line),
                    expected: true)
                #endif
            }
        } else {
            let expectedFailures = prefixes.map { prefix -> Failure in
                #if compiler(>=5.3)
                return Failure(issue: XCTIssue(
                                type: .assertionFailure,
                                compactDescription: prefix,
                                detailedDescription: nil,
                                sourceCodeContext: XCTSourceCodeContext(
                                    location: XCTSourceCodeLocation(
                                        filePath: String(describing: file),
                                        lineNumber: Int(line))),
                                associatedError: nil,
                                attachments: []))
                #else
                return Failure(
                    description: prefix,
                    file: String(describing: file),
                    line: Int(line),
                    expected: true)
                #endif
            }
            assertMatch(
                recordedFailures: recordedFailures,
                expectedFailures: expectedFailures)
        }
    }
    
    override func setUp() {
        super.setUp()
        isRecordingFailures = false
        recordedFailures = []
    }
    
    #if compiler(>=5.3)
    override func record(_ issue: XCTIssue) {
        if isRecordingFailures {
            recordedFailures.append(Failure(issue: issue))
        } else {
            super.record(issue)
        }
    }
    #else
    override func recordFailure(withDescription description: String, inFile filePath: String, atLine lineNumber: Int, expected: Bool) {
        if isRecordingFailures {
            recordedFailures.append(Failure(
                                        description: description,
                                        file: filePath,
                                        line: lineNumber,
                                        expected: expected))
        } else {
            super.recordFailure(
                withDescription: description,
                inFile: filePath,
                atLine: lineNumber,
                expected: expected)
        }
    }
    #endif
    
    private func recordingFailures(_ execute: () throws -> Void) rethrows -> [Failure] {
        let oldRecordingFailures = isRecordingFailures
        let oldRecordedFailures = recordedFailures
        defer {
            isRecordingFailures = oldRecordingFailures
            recordedFailures = oldRecordedFailures
        }
        isRecordingFailures = true
        recordedFailures = []
        try execute()
        let result = recordedFailures
        return result
    }
    
    private func assertMatch(recordedFailures: [Failure], expectedFailures: [Failure]) {
        let diff = expectedFailures.difference(from: recordedFailures).inferringMoves()
        for change in diff {
            switch change {
            case let .insert(offset: _, element: failure, associatedWith: nil):
                #if compiler(>=5.3)
                record(failure.issue(prefix: "Failure did not happen"))
                #else
                let failure = failure.failure(prefix: "Failure did not happen")
                recordFailure(
                    withDescription: failure.description,
                    inFile: failure.file,
                    atLine: failure.line,
                    expected: failure.expected)
                #endif
            case let .remove(offset: _, element: failure, associatedWith: nil):
                #if compiler(>=5.3)
                record(failure.issue())
                #else
                let failure = failure.failure()
                recordFailure(
                    withDescription: failure.description,
                    inFile: failure.file,
                    atLine: failure.line,
                    expected: failure.expected)
                #endif
            default:
                break
            }
        }
    }
}

// MARK: - Tests

class FailureTestCaseTests: FailureTestCase {
    func testEmptyTest() {
    }
    
    func testExpectedAnyFailure() {
        assertFailure {
            XCTFail("foo")
        }
        assertFailure {
            XCTFail("foo")
            XCTFail("bar")
        }
    }
    
    func testMissingAnyFailure() {
        assertFailure("No failure did happen") {
            assertFailure {
            }
        }
    }
    
    func testExpectedFailure() {
        assertFailure("failed - foo") {
            XCTFail("foo")
        }
    }
    
    func testExpectedFailureMatchesOnPrefix() {
        assertFailure("failed - foo") {
            XCTFail("foobarbaz")
        }
    }
    
    func testOrderOfExpectedFailureIsIgnored() {
        assertFailure("failed - foo", "failed - bar") {
            XCTFail("foo")
            XCTFail("bar")
        }
        assertFailure("failed - bar", "failed - foo") {
            XCTFail("foo")
            XCTFail("bar")
        }
    }
    
    func testExpectedFailureCanBeRepeated() {
        assertFailure("failed - foo", "failed - foo", "failed - bar") {
            XCTFail("foo")
            XCTFail("bar")
            XCTFail("foo")
        }
    }
    
    func testExactNumberOfRepetitionIsRequired() {
        assertFailure("Failure did not happen: failed - foo") {
            assertFailure("failed - foo", "failed - foo") {
                XCTFail("foo")
            }
        }
        assertFailure("failed - foo") {
            assertFailure("failed - foo", "failed - foo") {
                XCTFail("foo")
                XCTFail("foo")
                XCTFail("foo")
            }
        }
    }
    
    func testUnexpectedFailure() {
        assertFailure("Failure did not happen: failed - foo") {
            assertFailure("failed - foo") {
            }
        }
    }
    
    func testMissedFailure() {
        assertFailure("failed - bar") {
            assertFailure("failed - foo") {
                XCTFail("foo")
                XCTFail("bar")
            }
        }
    }
}


================================================
FILE: Tests/CombineExpectationsTests/LateSubscriptionTest.swift
================================================
import XCTest
import Combine
import Foundation
@testable import CombineExpectations

/// Tests for subscribers that do not create subscriptions right when they
/// receive subscribers.
class LateSubscriptionTest: FailureTestCase {
    func testNoSubscriptionPublisher() throws {
        struct NoSubscriptionPublisher: Publisher {
            typealias Output = String
            typealias Failure = Never
            func receive<S>(subscriber: S) where S : Subscriber, Failure == S.Failure, Output == S.Input { }
        }

        do {
            let publisher = NoSubscriptionPublisher()
            let recorder = publisher.record()
            
            let (elements, completion) = recorder.elementsAndCompletion
            XCTAssertTrue(elements.isEmpty)
            XCTAssertNil(completion)
        }
        do {
            // a test with an expectation that is fulfilled on completion
            let publisher = NoSubscriptionPublisher()
            let recorder = publisher.record()
            
            try wait(for: recorder.finished.inverted, timeout: 0.1)
        }
        do {
            // a test with an expectation that is fulfilled on input
            let publisher = NoSubscriptionPublisher()
            let recorder = publisher.record()
            
            try wait(for: recorder.next().inverted, timeout: 0.1)
        }
    }
    
    func testAsynchronousSubscriptionPublisher() throws {
        struct AsynchronousSubscriptionPublisher<Base: Publisher>: Publisher {
            typealias Output = Base.Output
            typealias Failure = Base.Failure
            let base: Base
            func receive<S>(subscriber: S) where S : Subscriber, Failure == S.Failure, Output == S.Input {
                DispatchQueue.main.async {
                    self.base.receive(subscriber: subscriber)
                }
            }
        }
        
        do {
            let publisher = AsynchronousSubscriptionPublisher(base: Just("foo"))
            let recorder = publisher.record()
            
            let (elements, completion) = recorder.elementsAndCompletion
            XCTAssertTrue(elements.isEmpty)
            XCTAssertNil(completion)
        }
        do {
            // a test with an expectation that is fulfilled on completion
            let publisher = AsynchronousSubscriptionPublisher(base: Just("foo"))
            let recorder = publisher.record()
            
            try wait(for: recorder.finished, timeout: 0.1)
        }
        do {
            // a test with an expectation that is fulfilled on input
            let publisher = AsynchronousSubscriptionPublisher(base: Just("foo"))
            let recorder = publisher.record()
            
            let element = try wait(for: recorder.next(), timeout: 0.1)
            XCTAssertEqual(element, "foo")
            
            let (elements, completion) = recorder.elementsAndCompletion
            XCTAssertEqual(elements, ["foo"])
            if case let .failure(error) = try XCTUnwrap(completion) { throw error }
        }
    }
}


================================================
FILE: Tests/CombineExpectationsTests/RecorderTests.swift
================================================
import XCTest
import Combine
import Foundation
@testable import CombineExpectations

/// General tests for publisher expectations
class RecorderTests: XCTestCase {
    private struct TestError: Error { }
    
    // MARK: - Subscription
    
    func testRecorderSubscribes() throws {
        var subscribed = false
        let publisher = Empty<Void, Never>().handleEvents(receiveSubscription: { _ in subscribed = true })
        _ = publisher.record()
        XCTAssertTrue(subscribed)
    }
    
    // MARK: - availableElements
    
    func testAvailableElementsSync() throws {
        do {
            let publisher = [1, 2, 3].publisher
            let recorder = publisher.record()
            let availableElements = try recorder.availableElements.get()
            XCTAssertEqual(availableElements, [1, 2, 3])
        }
        do {
            let publisher = PassthroughSubject<Int, Never>()
            let recorder = publisher.record()
            publisher.send(1)
            publisher.send(2)
            publisher.send(3)
            let availableElements = try recorder.availableElements.get()
            XCTAssertEqual(availableElements, [1, 2, 3])
        }
        do {
            let publisher = PassthroughSubject<Int, TestError>()
            let recorder = publisher.record()
            publisher.send(1)
            publisher.send(completion: .failure(TestError()))
            _ = try recorder.availableElements.get()
            XCTFail("Expected TestError")
        } catch is TestError { }
    }
    
    func testAvailableElementsAsync() throws {
        do {
            let publisher = Timer.publish(every: 0.01, on: .main, in: .common).autoconnect()
            let recorder = publisher.record()
            let dates = try wait(for: recorder.availableElements, timeout: 1)
            XCTAssertTrue(dates.count > 2)
            XCTAssertEqual(dates.sorted(), dates)
        }
    }
    
    func testAvailableElementsStopsOnPublisherCompletion() throws {
        do {
            let publisher = PassthroughSubject<Int, TestError>()
            let recorder = publisher.record()
            
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
                publisher.send(completion: .finished)
            }
            let start = Date()
            _ = try wait(for: recorder.availableElements, timeout: 2)
            let duration = Date().timeIntervalSince(start)
            XCTAssertLessThan(duration, 1)
        }
    }
    
    // MARK: - elementsAndCompletion
    
    func testElementsAndCompletionSync() throws {
        do {
            let publisher = Empty<Int, Never>()
            let recorder = publisher.record()
            
            let (elements, completion) = recorder.elementsAndCompletion
            XCTAssertEqual(elements, [])
            if case let .failure(error) = try XCTUnwrap(completion) { throw error }
        }
        do {
            let publisher = (0..<1).publisher
            let recorder = publisher.record()
            
            let (elements, completion) = recorder.elementsAndCompletion
            XCTAssertEqual(elements, [0])
            if case let .failure(error) = try XCTUnwrap(completion) { throw error }
        }
        do {
            let publisher = (0..<2).publisher
            let recorder = publisher.record()
            
            let (elements, completion) = recorder.elementsAndCompletion
            XCTAssertEqual(elements, [0, 1])
            if case let .failure(error) = try XCTUnwrap(completion) { throw error }
        }
    }
    
    func testElementsAndCompletionAsync() throws {
        do {
            let publisher = Empty<Int, Never>().receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            
            var (elements, completion) = recorder.elementsAndCompletion
            XCTAssertEqual(elements, [])
            XCTAssertNil(completion)
            
            _ = try wait(for: recorder.completion, timeout: 1)
            (elements, completion) = recorder.elementsAndCompletion
            XCTAssertEqual(elements, [])
            if case let .failure(error) = try XCTUnwrap(completion) { throw error }
        }
        do {
            let publisher = (0..<1).publisher.receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            
            var (elements, completion) = recorder.elementsAndCompletion
            XCTAssertEqual(elements, [])
            XCTAssertNil(completion)
            
            _ = try wait(for: recorder.completion, timeout: 1)
            (elements, completion) = recorder.elementsAndCompletion
            XCTAssertEqual(elements, [0])
            if case let .failure(error) = try XCTUnwrap(completion) { throw error }
        }
        do {
            let publisher = (0..<2).publisher.receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            
            var (elements, completion) = recorder.elementsAndCompletion
            XCTAssertEqual(elements, [])
            XCTAssertNil(completion)
            
            _ = try wait(for: recorder.completion, timeout: 1)
            (elements, completion) = recorder.elementsAndCompletion
            XCTAssertEqual(elements, [0, 1])
            if case let .failure(error) = try XCTUnwrap(completion) { throw error }
        }
    }
    
    func testElementsAndCompletionFailure() throws {
        do {
            let publisher = Fail<Int, TestError>(error: TestError())
            let recorder = publisher.record()
            
            let (elements, completion) = recorder.elementsAndCompletion
            XCTAssertEqual(elements, [])
            if case .finished = try XCTUnwrap(completion) {
                XCTFail("Expected TestError")
            }
        }
        do {
            let publisher = (0..<1).publisher.append(error: TestError())
            let recorder = publisher.record()
            
            let (elements, completion) = recorder.elementsAndCompletion
            XCTAssertEqual(elements, [0])
            if case .finished = try XCTUnwrap(completion) {
                XCTFail("Expected TestError")
            }
        }
        do {
            let publisher = (0..<2).publisher.append(error: TestError())
            let recorder = publisher.record()
            
            let (elements, completion) = recorder.elementsAndCompletion
            XCTAssertEqual(elements, [0, 1])
            if case .finished = try XCTUnwrap(completion) {
                XCTFail("Expected TestError")
            }
        }
    }
    
    func testElementsAndCompletionFailureAsync() throws {
        do {
            let publisher = Fail<Int, TestError>(error: TestError()).receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            
            var (elements, completion) = recorder.elementsAndCompletion
            XCTAssertEqual(elements, [])
            XCTAssertNil(completion)
            
            _ = try wait(for: recorder.completion, timeout: 1)
            (elements, completion) = recorder.elementsAndCompletion
            XCTAssertEqual(elements, [])
            if case .finished = try XCTUnwrap(completion) {
                XCTFail("Expected TestError")
            }
        }
        do {
            let publisher = (0..<1).publisher.append(error: TestError()).receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            
            var (elements, completion) = recorder.elementsAndCompletion
            XCTAssertEqual(elements, [])
            XCTAssertNil(completion)
            
            _ = try wait(for: recorder.completion, timeout: 1)
            (elements, completion) = recorder.elementsAndCompletion
            XCTAssertEqual(elements, [0])
            if case .finished = try XCTUnwrap(completion) {
                XCTFail("Expected TestError")
            }
        }
        do {
            let publisher = (0..<2).publisher.append(error: TestError()).receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            
            var (elements, completion) = recorder.elementsAndCompletion
            XCTAssertEqual(elements, [])
            XCTAssertNil(completion)
            
            _ = try wait(for: recorder.completion, timeout: 1)
            (elements, completion) = recorder.elementsAndCompletion
            XCTAssertEqual(elements, [0, 1])
            if case .finished = try XCTUnwrap(completion) {
                XCTFail("Expected TestError")
            }
        }
    }
    
    // MARK: - wait(for: recorder.elements)
    
    func testWaitForElementsSync() throws {
        do {
            let publisher = Empty<Int, Never>()
            let recorder = publisher.record()
            let elements = try wait(for: recorder.elements, timeout: 1)
            XCTAssertEqual(elements, [])
        }
        do {
            let publisher = (0..<1).publisher
            let recorder = publisher.record()
            let elements = try wait(for: recorder.elements, timeout: 1)
            XCTAssertEqual(elements, [0])
        }
        do {
            let publisher = (0..<2).publisher
            let recorder = publisher.record()
            let elements = try wait(for: recorder.elements, timeout: 1)
            XCTAssertEqual(elements, [0, 1])
        }
    }
    
    func testWaitForElementsAsync() throws {
        do {
            let publisher = Empty<Int, Never>().receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let elements = try wait(for: recorder.elements, timeout: 1)
            XCTAssertEqual(elements, [])
        }
        do {
            let publisher = (0..<1).publisher.receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let elements = try wait(for: recorder.elements, timeout: 1)
            XCTAssertEqual(elements, [0])
        }
        do {
            let publisher = (0..<2).publisher.receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let elements = try wait(for: recorder.elements, timeout: 1)
            XCTAssertEqual(elements, [0, 1])
        }
    }
    
    func testWaitForElementsFailure() throws {
        do {
            let publisher = Fail<Int, TestError>(error: TestError())
            let recorder = publisher.record()
            _ = try wait(for: recorder.elements, timeout: 1)
            XCTFail("Expected TestError")
        } catch is TestError { }
        do {
            let publisher = (0..<1).publisher.append(error: TestError())
            let recorder = publisher.record()
            _ = try wait(for: recorder.elements, timeout: 1)
            XCTFail("Expected TestError")
        } catch is TestError { }
        do {
            let publisher = (0..<2).publisher.append(error: TestError())
            let recorder = publisher.record()
            _ = try wait(for: recorder.elements, timeout: 1)
            XCTFail("Expected TestError")
        } catch is TestError { }
    }
    
    func testWaitForElementsFailureAsync() throws {
        do {
            let publisher = Fail<Int, TestError>(error: TestError()).receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            _ = try wait(for: recorder.elements, timeout: 1)
            XCTFail("Expected TestError")
        } catch is TestError { }
        do {
            let publisher = (0..<1).publisher.append(error: TestError()).receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            _ = try wait(for: recorder.elements, timeout: 1)
            XCTFail("Expected TestError")
        } catch is TestError { }
        do {
            let publisher = (0..<2).publisher.append(error: TestError()).receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            _ = try wait(for: recorder.elements, timeout: 1)
            XCTFail("Expected TestError")
        } catch is TestError { }
    }
    
    func testWaitForElementsAndWaitAgain() throws {
        let publisher = (0..<2).publisher
        let recorder = publisher.record()
        let elements = try wait(for: recorder.elements, timeout: 1)
        XCTAssertEqual(elements, [0, 1])
        
        do {
            let elements = try wait(for: recorder.elements, timeout: 1)
            XCTAssertEqual(elements, [0, 1])
        }
        
        do {
            let elements = try wait(for: recorder.prefix(3), timeout: 1)
            XCTAssertEqual(elements, [0, 1])
        }
        
        do {
            let element = try wait(for: recorder.last, timeout: 1)
            XCTAssertEqual(element, 1)
        }
        
        do {
            _ = try wait(for: recorder.single, timeout: 1)
            XCTFail("Expected RecordingError")
        } catch RecordingError.tooManyElements { }
        
        do {
            try wait(for: recorder.finished, timeout: 1)
        }
        
        do {
            let completion = try wait(for: recorder.completion, timeout: 1)
            if case let .failure(error) = completion { throw error }
        }
    }
    
    // MARK: - wait(for: recorder.next())
    
    func testWaitForNextSync() throws {
        do {
            let publisher = Empty<Int, Never>()
            let recorder = publisher.record()
            _ = try wait(for: recorder.next(), timeout: 1)
            XCTFail("Expected RecordingError")
        } catch RecordingError.notEnoughElements { }
        do {
            let publisher = (0..<1).publisher
            let recorder = publisher.record()
            let element = try wait(for: recorder.next(), timeout: 1)
            XCTAssertEqual(element, 0)
        }
        do {
            let publisher = (0..<2).publisher
            let recorder = publisher.record()
            let element = try wait(for: recorder.next(), timeout: 1)
            XCTAssertEqual(element, 0)
        }
    }
    
    func testWaitForNextAsync() throws {
        do {
            let publisher = Empty<Int, Never>().receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            _ = try wait(for: recorder.next(), timeout: 1)
            XCTFail("Expected RecordingError")
        } catch RecordingError.notEnoughElements { }
        do {
            let publisher = (0..<1).publisher.receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let element = try wait(for: recorder.next(), timeout: 1)
            XCTAssertEqual(element, 0)
        }
        do {
            let publisher = (0..<2).publisher.receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let element = try wait(for: recorder.next(), timeout: 1)
            XCTAssertEqual(element, 0)
        }
    }
    
    func testWaitForNextFailure() throws {
        do {
            let publisher = Fail<Int, TestError>(error: TestError())
            let recorder = publisher.record()
            _ = try wait(for: recorder.next(), timeout: 1)
            XCTFail("Expected TestError")
        } catch is TestError { }
        do {
            let publisher = (0..<1).publisher.append(error: TestError())
            let recorder = publisher.record()
            let element = try wait(for: recorder.next(), timeout: 1)
            XCTAssertEqual(element, 0)
        }
        do {
            let publisher = (0..<2).publisher.append(error: TestError())
            let recorder = publisher.record()
            let element = try wait(for: recorder.next(), timeout: 1)
            XCTAssertEqual(element, 0)
        }
    }
    
    func testWaitForNextFailureAsync() throws {
        do {
            let publisher = Fail<Int, TestError>(error: TestError()).receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            _ = try wait(for: recorder.next(), timeout: 1)
            XCTFail("Expected TestError")
        } catch is TestError { }
        do {
            let publisher = (0..<1).publisher.append(error: TestError()).receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let element = try wait(for: recorder.next(), timeout: 1)
            XCTAssertEqual(element, 0)
        }
        do {
            let publisher = (0..<2).publisher.append(error: TestError()).receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let element = try wait(for: recorder.next(), timeout: 1)
            XCTAssertEqual(element, 0)
        }
    }
    
    func testWaitForNextInverted() throws {
        do {
            let publisher = Empty<Int, Never>().delay(for: 0.1, scheduler: DispatchQueue.main)
            let recorder = publisher.record()
            try wait(for: recorder.next().inverted, timeout: 0.01)
        }
        do {
            let publisher = Timer.publish(every: 0.2, on: .main, in: .default).autoconnect()
            let recorder = publisher.record()
            try wait(for: recorder.next().inverted, timeout: 0.1)
            _ = try wait(for: recorder.next(), timeout: 1)
        }
    }
    
    func testNextNext() throws {
        do {
            let publisher = PassthroughSubject<Int, Never>()
            let recorder = publisher.record()
            publisher.send(0)
            publisher.send(1)
            try XCTAssertEqual(wait(for: recorder.next(), timeout: 1), 0)
            try XCTAssertEqual(wait(for: recorder.next(), timeout: 1), 1)
        }
        do {
            let publisher = PassthroughSubject<Int, Never>()
            let recorder = publisher.record()
            publisher.send(0)
            try XCTAssertEqual(wait(for: recorder.next(), timeout: 1), 0)
            publisher.send(1)
            try XCTAssertEqual(wait(for: recorder.next(), timeout: 1), 1)
        }
        do {
            let publisher = Timer.publish(every: 0.1, on: .main, in: .default).autoconnect()
            let recorder = publisher.record()
            _ = try wait(for: recorder.next(), timeout: 1)
            _ = try wait(for: recorder.next(), timeout: 1)
        }
    }
    
    func testPrefixNext() throws {
        let publisher = PassthroughSubject<Int, Never>()
        let recorder = publisher.record()
        publisher.send(0)
        publisher.send(1)
        publisher.send(2)
        try XCTAssertEqual(wait(for: recorder.prefix(2), timeout: 1), [0, 1])
        try XCTAssertEqual(wait(for: recorder.next(), timeout: 1), 2)
        try XCTAssertEqual(wait(for: recorder.prefix(2), timeout: 1), [0, 1])
        publisher.send(3)
        publisher.send(4)
        publisher.send(5)
        try XCTAssertEqual(wait(for: recorder.prefix(4), timeout: 1), [0, 1, 2, 3])
        try XCTAssertEqual(wait(for: recorder.next(), timeout: 1), 4)
        try XCTAssertEqual(wait(for: recorder.next(), timeout: 1), 5)
    }
    
    // MARK: - wait(for: recorder.next(0))
    
    func testWaitForNext0Sync() throws {
        do {
            let publisher = Empty<Int, Never>()
            let recorder = publisher.record()
            let elements = try wait(for: recorder.next(0), timeout: 1)
            XCTAssertEqual(elements, [])
        }
        do {
            let publisher = (0..<1).publisher
            let recorder = publisher.record()
            let elements = try wait(for: recorder.next(0), timeout: 1)
            XCTAssertEqual(elements, [])
        }
        do {
            let publisher = (0..<2).publisher
            let recorder = publisher.record()
            let elements = try wait(for: recorder.next(0), timeout: 1)
            XCTAssertEqual(elements, [])
        }
    }
    
    func testWaitForNext0Async() throws {
        do {
            let publisher = Empty<Int, Never>().receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let elements = try wait(for: recorder.next(0), timeout: 1)
            XCTAssertEqual(elements, [])
        }
        do {
            let publisher = (0..<1).publisher.receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let elements = try wait(for: recorder.next(0), timeout: 1)
            XCTAssertEqual(elements, [])
        }
        do {
            let publisher = (0..<2).publisher.receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let elements = try wait(for: recorder.next(0), timeout: 1)
            XCTAssertEqual(elements, [])
        }
    }
    
    func testWaitForNext0Failure() throws {
        do {
            let publisher = Fail<Int, TestError>(error: TestError())
            let recorder = publisher.record()
            let elements = try wait(for: recorder.next(0), timeout: 1)
            XCTAssertEqual(elements, [])
        } catch is TestError { }
        do {
            let publisher = (0..<1).publisher.append(error: TestError())
            let recorder = publisher.record()
            let elements = try wait(for: recorder.next(0), timeout: 1)
            XCTAssertEqual(elements, [])
        } catch is TestError { }
        do {
            let publisher = (0..<2).publisher.append(error: TestError())
            let recorder = publisher.record()
            let elements = try wait(for: recorder.next(0), timeout: 1)
            XCTAssertEqual(elements, [])
        } catch is TestError { }
    }
    
    func testWaitForNext0FailureAsync() throws {
        do {
            let publisher = Fail<Int, TestError>(error: TestError()).receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let elements = try wait(for: recorder.next(0), timeout: 1)
            XCTAssertEqual(elements, [])
        } catch is TestError { }
        do {
            let publisher = (0..<1).publisher.append(error: TestError()).receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let elements = try wait(for: recorder.next(0), timeout: 1)
            XCTAssertEqual(elements, [])
        } catch is TestError { }
        do {
            let publisher = (0..<2).publisher.append(error: TestError()).receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let elements = try wait(for: recorder.next(0), timeout: 1)
            XCTAssertEqual(elements, [])
        } catch is TestError { }
    }
    
    // MARK: - wait(for: recorder.next(2))
    
    func testWaitForNext2Sync() throws {
        do {
            let publisher = Empty<Int, Never>()
            let recorder = publisher.record()
            _ = try wait(for: recorder.next(2), timeout: 1)
            XCTFail("Expected RecordingError")
        } catch RecordingError.notEnoughElements { }
        do {
            let publisher = (0..<1).publisher
            let recorder = publisher.record()
            _ = try wait(for: recorder.next(2), timeout: 1)
            XCTFail("Expected RecordingError")
        } catch RecordingError.notEnoughElements { }
        do {
            let publisher = (0..<2).publisher
            let recorder = publisher.record()
            let elements = try wait(for: recorder.next(2), timeout: 1)
            XCTAssertEqual(elements, [0, 1])
        }
        do {
            let publisher = (0..<3).publisher
            let recorder = publisher.record()
            let elements = try wait(for: recorder.next(2), timeout: 1)
            XCTAssertEqual(elements, [0, 1])
        }
    }
    
    func testWaitForNext2Async() throws {
        do {
            let publisher = Empty<Int, Never>().receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            _ = try wait(for: recorder.next(2), timeout: 1)
            XCTFail("Expected RecordingError")
        } catch RecordingError.notEnoughElements { }
        do {
            let publisher = (0..<1).publisher.receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            _ = try wait(for: recorder.next(2), timeout: 1)
            XCTFail("Expected RecordingError")
        } catch RecordingError.notEnoughElements { }
        do {
            let publisher = (0..<2).publisher.receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let elements = try wait(for: recorder.next(2), timeout: 1)
            XCTAssertEqual(elements, [0, 1])
        }
        do {
            let publisher = (0..<3).publisher.receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let elements = try wait(for: recorder.next(2), timeout: 1)
            XCTAssertEqual(elements, [0, 1])
        }
    }
    
    func testWaitForNext2Failure() throws {
        do {
            let publisher = Fail<Int, TestError>(error: TestError())
            let recorder = publisher.record()
            _ = try wait(for: recorder.next(2), timeout: 1)
            XCTFail("Expected TestError")
        } catch is TestError { }
        do {
            let publisher = (0..<1).publisher.append(error: TestError())
            let recorder = publisher.record()
            _ = try wait(for: recorder.next(2), timeout: 1)
            XCTFail("Expected TestError")
        } catch is TestError { }
        do {
            let publisher = (0..<2).publisher.append(error: TestError())
            let recorder = publisher.record()
            let elements = try wait(for: recorder.next(2), timeout: 1)
            XCTAssertEqual(elements, [0, 1])
        }
        do {
            let publisher = (0..<3).publisher.append(error: TestError())
            let recorder = publisher.record()
            let elements = try wait(for: recorder.next(2), timeout: 1)
            XCTAssertEqual(elements, [0, 1])
        }
    }
    
    func testWaitForNext2FailureAsync() throws {
        do {
            let publisher = Fail<Int, TestError>(error: TestError()).receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            _ = try wait(for: recorder.next(2), timeout: 1)
            XCTFail("Expected TestError")
        } catch is TestError { }
        do {
            let publisher = (0..<1).publisher.append(error: TestError()).receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            _ = try wait(for: recorder.next(2), timeout: 1)
            XCTFail("Expected TestError")
        } catch is TestError { }
        do {
            let publisher = (0..<2).publisher.append(error: TestError()).receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let elements = try wait(for: recorder.next(2), timeout: 1)
            XCTAssertEqual(elements, [0, 1])
        }
        do {
            let publisher = (0..<3).publisher.append(error: TestError()).receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let elements = try wait(for: recorder.next(2), timeout: 1)
            XCTAssertEqual(elements, [0, 1])
        }
    }
    
    func testNext2Next2() throws {
        do {
            let publisher = PassthroughSubject<Int, Never>()
            let recorder = publisher.record()
            publisher.send(0)
            publisher.send(1)
            publisher.send(2)
            publisher.send(3)
            try XCTAssertEqual(wait(for: recorder.next(2), timeout: 1), [0, 1])
            try XCTAssertEqual(wait(for: recorder.next(2), timeout: 1), [2, 3])
        }
        do {
            let publisher = PassthroughSubject<Int, Never>()
            let recorder = publisher.record()
            publisher.send(0)
            publisher.send(1)
            try XCTAssertEqual(wait(for: recorder.next(2), timeout: 1), [0, 1])
            publisher.send(2)
            publisher.send(3)
            try XCTAssertEqual(wait(for: recorder.next(2), timeout: 1), [2, 3])
        }
        do {
            let publisher = Timer.publish(every: 0.1, on: .main, in: .default).autoconnect()
            let recorder = publisher.record()
            _ = try wait(for: recorder.next(2), timeout: 1)
            _ = try wait(for: recorder.next(2), timeout: 1)
        }
    }
    
    func testPrefixNext2() throws {
        let publisher = PassthroughSubject<Int, Never>()
        let recorder = publisher.record()
        publisher.send(0)
        publisher.send(1)
        publisher.send(2)
        publisher.send(3)
        try XCTAssertEqual(wait(for: recorder.prefix(2), timeout: 1), [0, 1])
        try XCTAssertEqual(wait(for: recorder.next(2), timeout: 1), [2, 3])
        try XCTAssertEqual(wait(for: recorder.prefix(2), timeout: 1), [0, 1])
        publisher.send(4)
        publisher.send(5)
        try XCTAssertEqual(wait(for: recorder.prefix(4), timeout: 1), [0, 1, 2, 3])
        try XCTAssertEqual(wait(for: recorder.next(2), timeout: 1), [4, 5])
    }
    
    // MARK: - wait(for: recorder.prefix(0))
    
    func testWaitForPrefix0Sync() throws {
        do {
            let publisher = Empty<Int, Never>()
            let recorder = publisher.record()
            let elements = try wait(for: recorder.prefix(0), timeout: 1)
            XCTAssertEqual(elements, [])
        }
        do {
            let publisher = (0..<1).publisher
            let recorder = publisher.record()
            let elements = try wait(for: recorder.prefix(0), timeout: 1)
            XCTAssertEqual(elements, [])
        }
        do {
            let publisher = (0..<2).publisher
            let recorder = publisher.record()
            let elements = try wait(for: recorder.prefix(0), timeout: 1)
            XCTAssertEqual(elements, [])
        }
    }
    
    func testWaitForPrefix0Async() throws {
        do {
            let publisher = Empty<Int, Never>().receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let elements = try wait(for: recorder.prefix(0), timeout: 1)
            XCTAssertEqual(elements, [])
        }
        do {
            let publisher = (0..<1).publisher.receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let elements = try wait(for: recorder.prefix(0), timeout: 1)
            XCTAssertEqual(elements, [])
        }
        do {
            let publisher = (0..<2).publisher.receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let elements = try wait(for: recorder.prefix(0), timeout: 1)
            XCTAssertEqual(elements, [])
        }
    }
    
    func testWaitForPrefix0Failure() throws {
        do {
            let publisher = Fail<Int, TestError>(error: TestError())
            let recorder = publisher.record()
            let elements = try wait(for: recorder.prefix(0), timeout: 1)
            XCTAssertEqual(elements, [])
        } catch is TestError { }
        do {
            let publisher = (0..<1).publisher.append(error: TestError())
            let recorder = publisher.record()
            let elements = try wait(for: recorder.prefix(0), timeout: 1)
            XCTAssertEqual(elements, [])
        } catch is TestError { }
        do {
            let publisher = (0..<2).publisher.append(error: TestError())
            let recorder = publisher.record()
            let elements = try wait(for: recorder.prefix(0), timeout: 1)
            XCTAssertEqual(elements, [])
        } catch is TestError { }
    }
    
    func testWaitForPrefix0FailureAsync() throws {
        do {
            let publisher = Fail<Int, TestError>(error: TestError()).receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let elements = try wait(for: recorder.prefix(0), timeout: 1)
            XCTAssertEqual(elements, [])
        } catch is TestError { }
        do {
            let publisher = (0..<1).publisher.append(error: TestError()).receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let elements = try wait(for: recorder.prefix(0), timeout: 1)
            XCTAssertEqual(elements, [])
        } catch is TestError { }
        do {
            let publisher = (0..<2).publisher.append(error: TestError()).receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let elements = try wait(for: recorder.prefix(0), timeout: 1)
            XCTAssertEqual(elements, [])
        } catch is TestError { }
    }
    
    // MARK: - wait(for: recorder.prefix(1))
    
    func testWaitForPrefix1Sync() throws {
        do {
            let publisher = Empty<Int, Never>()
            let recorder = publisher.record()
            let elements = try wait(for: recorder.prefix(1), timeout: 1)
            XCTAssertEqual(elements, [])
        }
        do {
            let publisher = (0..<1).publisher
            let recorder = publisher.record()
            let elements = try wait(for: recorder.prefix(1), timeout: 1)
            XCTAssertEqual(elements, [0])
        }
        do {
            let publisher = (0..<2).publisher
            let recorder = publisher.record()
            let elements = try wait(for: recorder.prefix(1), timeout: 1)
            XCTAssertEqual(elements, [0])
        }
    }
    
    func testWaitForPrefix1Async() throws {
        do {
            let publisher = Empty<Int, Never>().receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let elements = try wait(for: recorder.prefix(1), timeout: 1)
            XCTAssertEqual(elements, [])
        }
        do {
            let publisher = (0..<1).publisher.receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let elements = try wait(for: recorder.prefix(1), timeout: 1)
            XCTAssertEqual(elements, [0])
        }
        do {
            let publisher = (0..<2).publisher.receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let elements = try wait(for: recorder.prefix(1), timeout: 1)
            XCTAssertEqual(elements, [0])
        }
    }
    
    func testWaitForPrefix1Failure() throws {
        do {
            let publisher = Fail<Int, TestError>(error: TestError())
            let recorder = publisher.record()
            _ = try wait(for: recorder.prefix(1), timeout: 1)
            XCTFail("Expected TestError")
        } catch is TestError { }
        do {
            let publisher = (0..<1).publisher.append(error: TestError())
            let recorder = publisher.record()
            let elements = try wait(for: recorder.prefix(1), timeout: 1)
            XCTAssertEqual(elements, [0])
        } catch is TestError { }
        do {
            let publisher = (0..<2).publisher.append(error: TestError())
            let recorder = publisher.record()
            let elements = try wait(for: recorder.prefix(1), timeout: 1)
            XCTAssertEqual(elements, [0])
        } catch is TestError { }
    }
    
    func testWaitForPrefix1FailureAsync() throws {
        do {
            let publisher = Fail<Int, TestError>(error: TestError()).receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            _ = try wait(for: recorder.prefix(1), timeout: 1)
            XCTFail("Expected TestError")
        } catch is TestError { }
        do {
            let publisher = (0..<1).publisher.append(error: TestError()).receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let elements = try wait(for: recorder.prefix(1), timeout: 1)
            XCTAssertEqual(elements, [0])
        } catch is TestError { }
        do {
            let publisher = (0..<2).publisher.append(error: TestError()).receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let elements = try wait(for: recorder.prefix(1), timeout: 1)
            XCTAssertEqual(elements, [0])
        } catch is TestError { }
    }
    
    func testWaitForPrefix1Inverted() throws {
        do {
            let publisher = Empty<Int, Never>().delay(for: 0.1, scheduler: DispatchQueue.main)
            let recorder = publisher.record()
            let elements = try wait(for: recorder.prefix(1).inverted, timeout: 0.01)
            XCTAssertEqual(elements, [])
        }
    }
    
    // MARK: - wait(for: recorder.prefix(2))
    
    func testWaitForPrefix2Sync() throws {
        do {
            let publisher = Empty<Int, Never>()
            let recorder = publisher.record()
            let elements = try wait(for: recorder.prefix(2), timeout: 1)
            XCTAssertEqual(elements, [])
        }
        do {
            let publisher = (0..<1).publisher
            let recorder = publisher.record()
            let elements = try wait(for: recorder.prefix(2), timeout: 1)
            XCTAssertEqual(elements, [0])
        }
        do {
            let publisher = (0..<2).publisher
            let recorder = publisher.record()
            let elements = try wait(for: recorder.prefix(2), timeout: 1)
            XCTAssertEqual(elements, [0, 1])
        }
        do {
            let publisher = (0..<3).publisher
            let recorder = publisher.record()
            let elements = try wait(for: recorder.prefix(2), timeout: 1)
            XCTAssertEqual(elements, [0, 1])
        }
    }
    
    func testWaitForPrefix2Async() throws {
        do {
            let publisher = Empty<Int, Never>().receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let elements = try wait(for: recorder.prefix(2), timeout: 1)
            XCTAssertEqual(elements, [])
        }
        do {
            let publisher = (0..<1).publisher.receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let elements = try wait(for: recorder.prefix(2), timeout: 1)
            XCTAssertEqual(elements, [0])
        }
        do {
            let publisher = (0..<2).publisher.receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let elements = try wait(for: recorder.prefix(2), timeout: 1)
            XCTAssertEqual(elements, [0, 1])
        }
        do {
            let publisher = (0..<3).publisher.receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let elements = try wait(for: recorder.prefix(2), timeout: 1)
            XCTAssertEqual(elements, [0, 1])
        }
    }
    
    func testWaitForPrefix2Failure() throws {
        do {
            let publisher = Fail<Int, TestError>(error: TestError())
            let recorder = publisher.record()
            _ = try wait(for: recorder.prefix(2), timeout: 1)
            XCTFail("Expected TestError")
        } catch is TestError { }
        do {
            let publisher = (0..<1).publisher.append(error: TestError())
            let recorder = publisher.record()
            _ = try wait(for: recorder.prefix(2), timeout: 1)
            XCTFail("Expected TestError")
        } catch is TestError { }
        do {
            let publisher = (0..<2).publisher.append(error: TestError())
            let recorder = publisher.record()
            let elements = try wait(for: recorder.prefix(2), timeout: 1)
            XCTAssertEqual(elements, [0, 1])
        } catch is TestError { }
        do {
            let publisher = (0..<3).publisher.append(error: TestError())
            let recorder = publisher.record()
            let elements = try wait(for: recorder.prefix(2), timeout: 1)
            XCTAssertEqual(elements, [0, 1])
        } catch is TestError { }
    }
    
    func testWaitForPrefix2FailureAsync() throws {
        do {
            let publisher = Fail<Int, TestError>(error: TestError()).receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            _ = try wait(for: recorder.prefix(2), timeout: 1)
            XCTFail("Expected TestError")
        } catch is TestError { }
        do {
            let publisher = (0..<1).publisher.append(error: TestError()).receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            _ = try wait(for: recorder.prefix(2), timeout: 1)
            XCTFail("Expected TestError")
        } catch is TestError { }
        do {
            let publisher = (0..<2).publisher.append(error: TestError()).receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let elements = try wait(for: recorder.prefix(2), timeout: 1)
            XCTAssertEqual(elements, [0, 1])
        } catch is TestError { }
        do {
            let publisher = (0..<3).publisher.append(error: TestError()).receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let elements = try wait(for: recorder.prefix(2), timeout: 1)
            XCTAssertEqual(elements, [0, 1])
        } catch is TestError { }
    }
    
    func testWaitForPrefix2Inverted() throws {
        do {
            let publisher = Empty<Int, Never>().delay(for: 0.1, scheduler: DispatchQueue.main)
            let recorder = publisher.record()
            let elements = try wait(for: recorder.prefix(2).inverted, timeout: 0.01)
            XCTAssertEqual(elements, [])
        }
        do {
            let publisher = (0..<1).publisher.append(Empty<Int, Never>().delay(for: 0.1, scheduler: DispatchQueue.main))
            let recorder = publisher.record()
            let elements = try wait(for: recorder.prefix(2).inverted, timeout: 0.01)
            XCTAssertEqual(elements, [0])
        }
    }
    
    // MARK: - wait(for: recorder.prefix(3))
    
    func testWaitForPrefix3Inverted() throws {
        do {
            let publisher = Empty<Int, Never>().delay(for: 0.1, scheduler: DispatchQueue.main)
            let recorder = publisher.record()
            let elements = try wait(for: recorder.prefix(3).inverted, timeout: 0.01)
            XCTAssertEqual(elements, [])
        }
        do {
            let publisher = (0..<1).publisher.append(Empty<Int, Never>().delay(for: 0.1, scheduler: DispatchQueue.main))
            let recorder = publisher.record()
            let elements = try wait(for: recorder.prefix(3).inverted, timeout: 0.01)
            XCTAssertEqual(elements, [0])
        }
        do {
            let publisher = (0..<2).publisher.append(Empty<Int, Never>().delay(for: 0.1, scheduler: DispatchQueue.main))
            let recorder = publisher.record()
            let elements = try wait(for: recorder.prefix(3).inverted, timeout: 0.0)
            XCTAssertEqual(elements, [0, 1])
        }
    }
    
    // MARK: - wait(for: recorder.prefix(N))
    
    func testWaitForPrefixAndWaitForPrefixAgain() throws {
        let publisher = PassthroughSubject<Int, Never>()
        let recorder = publisher.record()
        publisher.send(0)
        try XCTAssertEqual(wait(for: recorder.prefix(1), timeout: 1), [0])
        try XCTAssertEqual(wait(for: recorder.prefix(1), timeout: 1), [0])
        publisher.send(1)
        try XCTAssertEqual(wait(for: recorder.prefix(1), timeout: 1), [0])
        publisher.send(2)
        try XCTAssertEqual(wait(for: recorder.prefix(3), timeout: 1), [0, 1, 2])
    }
    
    func testWaitForPrefixAndWaitForPrefixAgainInverted() throws {
        let publisher = PassthroughSubject<Int, Never>()
        let recorder = publisher.record()
        publisher.send(0)
        try XCTAssertEqual(wait(for: recorder.prefix(1), timeout: 1), [0])
        try XCTAssertEqual(wait(for: recorder.prefix(1), timeout: 1), [0])
        try XCTAssertEqual(wait(for: recorder.prefix(2).inverted, timeout: 0.01), [0])
    }
    
    // MARK: - wait(for: recorder.last)
    
    func testWaitForLastSync() throws {
        do {
            let publisher = Empty<Int, Never>()
            let recorder = publisher.record()
            let element = try wait(for: recorder.last, timeout: 1)
            XCTAssertNil(element)
        }
        do {
            let publisher = (0..<1).publisher
            let recorder = publisher.record()
            let element = try wait(for: recorder.last, timeout: 1)
            XCTAssertEqual(element, 0)
        }
        do {
            let publisher = (0..<2).publisher
            let recorder = publisher.record()
            let element = try wait(for: recorder.last, timeout: 1)
            XCTAssertEqual(element, 1)
        }
    }
    
    func testWaitForLastAsync() throws {
        do {
            let publisher = Empty<Int, Never>().receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let element = try wait(for: recorder.last, timeout: 1)
            XCTAssertNil(element)
        }
        do {
            let publisher = (0..<1).publisher.receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let element = try wait(for: recorder.last, timeout: 1)
            XCTAssertEqual(element, 0)
        }
        do {
            let publisher = (0..<2).publisher.receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let element = try wait(for: recorder.last, timeout: 1)
            XCTAssertEqual(element, 1)
        }
    }
    
    func testWaitForLastFailure() throws {
        do {
            let publisher = Fail<Int, TestError>(error: TestError())
            let recorder = publisher.record()
            _ = try wait(for: recorder.last, timeout: 1)
            XCTFail("Expected TestError")
        } catch is TestError { }
        do {
            let publisher = (0..<1).publisher.append(error: TestError())
            let recorder = publisher.record()
            _ = try wait(for: recorder.last, timeout: 1)
            XCTFail("Expected TestError")
        } catch is TestError { }
        do {
            let publisher = (0..<2).publisher.append(error: TestError())
            let recorder = publisher.record()
            _ = try wait(for: recorder.last, timeout: 1)
            XCTFail("Expected TestError")
        } catch is TestError { }
    }
    
    func testWaitForLastFailureAsync() throws {
        do {
            let publisher = Fail<Int, TestError>(error: TestError()).receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            _ = try wait(for: recorder.last, timeout: 1)
            XCTFail("Expected TestError")
        } catch is TestError { }
        do {
            let publisher = (0..<1).publisher.append(error: TestError()).receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            _ = try wait(for: recorder.last, timeout: 1)
            XCTFail("Expected TestError")
        } catch is TestError { }
        do {
            let publisher = (0..<2).publisher.append(error: TestError()).receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            _ = try wait(for: recorder.last, timeout: 1)
            XCTFail("Expected TestError")
        } catch is TestError { }
    }
    
    // MARK: - wait(for: recorder.single)
    
    func testWaitForSingleSync() throws {
        do {
            let publisher = Empty<Int, Never>()
            let recorder = publisher.record()
            _ = try wait(for: recorder.single, timeout: 1)
            XCTFail("Expected RecordingError")
        } catch RecordingError.notEnoughElements { }
        do {
            let publisher = (0..<1).publisher
            let recorder = publisher.record()
            let element = try wait(for: recorder.single, timeout: 1)
            XCTAssertEqual(element, 0)
        }
        do {
            let publisher = (0..<2).publisher
            let recorder = publisher.record()
            _ = try wait(for: recorder.single, timeout: 1)
            XCTFail("Expected RecordingError")
        } catch RecordingError.tooManyElements { }
    }
    
    func testWaitForSingleAsync() throws {
        do {
            let publisher = Empty<Int, Never>().receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            _ = try wait(for: recorder.single, timeout: 1)
            XCTFail("Expected RecordingError")
        } catch RecordingError.notEnoughElements { }
        do {
            let publisher = (0..<1).publisher.receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let element = try wait(for: recorder.single, timeout: 1)
            XCTAssertEqual(element, 0)
        }
        do {
            let publisher = (0..<2).publisher.receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            _ = try wait(for: recorder.single, timeout: 1)
            XCTFail("Expected RecordingError")
        } catch RecordingError.tooManyElements { }
    }
    
    func testWaitForSingleFailure() throws {
        do {
            let publisher = Fail<Int, TestError>(error: TestError())
            let recorder = publisher.record()
            _ = try wait(for: recorder.single, timeout: 1)
            XCTFail("Expected TestError")
        } catch is TestError { }
        do {
            let publisher = (0..<1).publisher.append(error: TestError())
            let recorder = publisher.record()
            _ = try wait(for: recorder.single, timeout: 1)
            XCTFail("Expected TestError")
        } catch is TestError { }
        do {
            let publisher = (0..<2).publisher.append(error: TestError())
            let recorder = publisher.record()
            _ = try wait(for: recorder.single, timeout: 1)
            XCTFail("Expected TestError")
        } catch is TestError { }
    }
    
    func testWaitForSingleFailureAsync() throws {
        do {
            let publisher = Fail<Int, TestError>(error: TestError()).receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            _ = try wait(for: recorder.single, timeout: 1)
            XCTFail("Expected TestError")
        } catch is TestError { }
        do {
            let publisher = (0..<1).publisher.append(error: TestError()).receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            _ = try wait(for: recorder.single, timeout: 1)
            XCTFail("Expected TestError")
        } catch is TestError { }
        do {
            let publisher = (0..<2).publisher.append(error: TestError()).receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            _ = try wait(for: recorder.single, timeout: 1)
            XCTFail("Expected TestError")
        } catch is TestError { }
    }
    
    // MARK: - wait(for: recorder.finished)
    
    func testWaitForFinishedSync() throws {
        do {
            let publisher = Empty<Int, Never>()
            let recorder = publisher.record()
            try wait(for: recorder.finished, timeout: 1)
        }
        do {
            let publisher = (0..<1).publisher
            let recorder = publisher.record()
            try wait(for: recorder.finished, timeout: 1)
        }
        do {
            let publisher = (0..<2).publisher
            let recorder = publisher.record()
            try wait(for: recorder.finished, timeout: 1)
        }
    }
    
    func testWaitForFinishedAsync() throws {
        do {
            let publisher = Empty<Int, Never>().receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            try wait(for: recorder.finished, timeout: 1)
        }
        do {
            let publisher = (0..<1).publisher.receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            try wait(for: recorder.finished, timeout: 1)
        }
        do {
            let publisher = (0..<2).publisher.receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            try wait(for: recorder.finished, timeout: 1)
        }
    }
    
    func testWaitForFinishedFailure() throws {
        do {
            let publisher = Fail<Int, TestError>(error: TestError())
            let recorder = publisher.record()
            try wait(for: recorder.finished, timeout: 1)
            XCTFail("Expected TestError")
        } catch is TestError { }
        do {
            let publisher = (0..<1).publisher.append(error: TestError())
            let recorder = publisher.record()
            try wait(for: recorder.finished, timeout: 1)
            XCTFail("Expected TestError")
        } catch is TestError { }
        do {
            let publisher = (0..<2).publisher.append(error: TestError())
            let recorder = publisher.record()
            try wait(for: recorder.finished, timeout: 1)
            XCTFail("Expected TestError")
        } catch is TestError { }
    }
    
    func testWaitForFinishedFailureAsync() throws {
        do {
            let publisher = Fail<Int, TestError>(error: TestError()).receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            try wait(for: recorder.finished, timeout: 1)
            XCTFail("Expected TestError")
        } catch is TestError { }
        do {
            let publisher = (0..<1).publisher.append(error: TestError()).receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            try wait(for: recorder.finished, timeout: 1)
            XCTFail("Expected TestError")
        } catch is TestError { }
        do {
            let publisher = (0..<2).publisher.append(error: TestError()).receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            try wait(for: recorder.finished, timeout: 1)
            XCTFail("Expected TestError")
        } catch is TestError { }
    }
    
    func testWaitForFinishedInverted() throws {
        do {
            let publisher = Empty<Int, Never>().delay(for: 0.1, scheduler: DispatchQueue.main)
            let recorder = publisher.record()
            try wait(for: recorder.finished.inverted, timeout: 0.01)
        }
        do {
            let publisher = PassthroughSubject<Int, Never>()
            let recorder = publisher.record()
            try wait(for: recorder.finished.inverted, timeout: 0.01)
            publisher.send(completion: .finished)
            try wait(for: recorder.finished, timeout: 0.01)
        }
    }
    
    // MARK: - wait(for: recorder.completion)
    
    func testWaitForCompletionSync() throws {
        do {
            let publisher = Empty<Int, Never>()
            let recorder = publisher.record()
            let completion = try wait(for: recorder.completion, timeout: 1)
            if case let .failure(error) = completion { throw error }
        }
        do {
            let publisher = (0..<1).publisher
            let recorder = publisher.record()
            let completion = try wait(for: recorder.completion, timeout: 1)
            if case let .failure(error) = completion { throw error }
        }
        do {
            let publisher = (0..<2).publisher
            let recorder = publisher.record()
            let completion = try wait(for: recorder.completion, timeout: 1)
            if case let .failure(error) = completion { throw error }
        }
    }
    
    func testWaitForCompletionAsync() throws {
        do {
            let publisher = Empty<Int, Never>().receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let completion = try wait(for: recorder.completion, timeout: 1)
            if case let .failure(error) = completion { throw error }
        }
        do {
            let publisher = (0..<1).publisher.receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let completion = try wait(for: recorder.completion, timeout: 1)
            if case let .failure(error) = completion { throw error }
        }
        do {
            let publisher = (0..<2).publisher.receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let completion = try wait(for: recorder.completion, timeout: 1)
            if case let .failure(error) = completion { throw error }
        }
    }
    
    func testWaitForCompletionFailure() throws {
        do {
            let publisher = Fail<Int, TestError>(error: TestError())
            let recorder = publisher.record()
            let completion = try wait(for: recorder.completion, timeout: 1)
            if case .finished = completion {
                XCTFail("Expected TestError")
            }
        }
        do {
            let publisher = (0..<1).publisher.append(error: TestError())
            let recorder = publisher.record()
            let completion = try wait(for: recorder.completion, timeout: 1)
            if case .finished = completion {
                XCTFail("Expected TestError")
            }
        }
        do {
            let publisher = (0..<2).publisher.append(error: TestError())
            let recorder = publisher.record()
            let completion = try wait(for: recorder.completion, timeout: 1)
            if case .finished = completion {
                XCTFail("Expected TestError")
            }
        }
    }
    
    func testWaitForCompletionFailureAsync() throws {
        do {
            let publisher = Fail<Int, TestError>(error: TestError()).receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let completion = try wait(for: recorder.completion, timeout: 1)
            if case .finished = completion {
                XCTFail("Expected TestError")
            }
        }
        do {
            let publisher = (0..<1).publisher.append(error: TestError()).receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let completion = try wait(for: recorder.completion, timeout: 1)
            if case .finished = completion {
                XCTFail("Expected TestError")
            }
        }
        do {
            let publisher = (0..<2).publisher.append(error: TestError()).receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let completion = try wait(for: recorder.completion, timeout: 1)
            if case .finished = completion {
                XCTFail("Expected TestError")
            }
        }
    }
    
    // MARK: - wait(for: recorder.recording)
    
    func testWaitForRecordingSync() throws {
        do {
            let publisher = Empty<Int, Never>()
            let recorder = publisher.record()
            let recording = try wait(for: recorder.recording, timeout: 1)
            XCTAssertEqual(recording.output, [])
            if case let .failure(error) = recording.completion {
                XCTFail("Unexpected error \(error)")
            }
        }
        do {
            let publisher = (0..<1).publisher
            let recorder = publisher.record()
            let recording = try wait(for: recorder.recording, timeout: 1)
            XCTAssertEqual(recording.output, [0])
            if case let .failure(error) = recording.completion {
                XCTFail("Unexpected error \(error)")
            }
        }
        do {
            let publisher = (0..<2).publisher
            let recorder = publisher.record()
            let recording = try wait(for: recorder.recording, timeout: 1)
            XCTAssertEqual(recording.output, [0, 1])
            if case let .failure(error) = recording.completion {
                XCTFail("Unexpected error \(error)")
            }
        }
    }
    
    func testWaitForRecordingAsync() throws {
        do {
            let publisher = Empty<Int, Never>().receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let recording = try wait(for: recorder.recording, timeout: 1)
            XCTAssertEqual(recording.output, [])
            if case let .failure(error) = recording.completion {
                XCTFail("Unexpected error \(error)")
            }
        }
        do {
            let publisher = (0..<1).publisher.receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let recording = try wait(for: recorder.recording, timeout: 1)
            XCTAssertEqual(recording.output, [0])
            if case let .failure(error) = recording.completion {
                XCTFail("Unexpected error \(error)")
            }
        }
        do {
            let publisher = (0..<2).publisher.receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let recording = try wait(for: recorder.recording, timeout: 1)
            XCTAssertEqual(recording.output, [0, 1])
            if case let .failure(error) = recording.completion {
                XCTFail("Unexpected error \(error)")
            }
        }
    }
    
    func testWaitForRecordingFailure() throws {
        do {
            let publisher = Fail<Int, TestError>(error: TestError())
            let recorder = publisher.record()
            let recording = try wait(for: recorder.recording, timeout: 1)
            XCTAssertEqual(recording.output, [])
            if case .finished = recording.completion {
                XCTFail("Expected TestError")
            }
        }
        do {
            let publisher = (0..<1).publisher.append(error: TestError())
            let recorder = publisher.record()
            let recording = try wait(for: recorder.recording, timeout: 1)
            XCTAssertEqual(recording.output, [0])
            if case .finished = recording.completion {
                XCTFail("Expected TestError")
            }
        }
        do {
            let publisher = (0..<2).publisher.append(error: TestError())
            let recorder = publisher.record()
            let recording = try wait(for: recorder.recording, timeout: 1)
            XCTAssertEqual(recording.output, [0, 1])
            if case .finished = recording.completion {
                XCTFail("Expected TestError")
            }
        }
    }
    
    func testWaitForRecordingFailureAsync() throws {
        do {
            let publisher = Fail<Int, TestError>(error: TestError()).receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let recording = try wait(for: recorder.recording, timeout: 1)
            XCTAssertEqual(recording.output, [])
            if case .finished = recording.completion {
                XCTFail("Expected TestError")
            }
        }
        do {
            let publisher = (0..<1).publisher.append(error: TestError()).receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let recording = try wait(for: recorder.recording, timeout: 1)
            XCTAssertEqual(recording.output, [0])
            if case .finished = recording.completion {
                XCTFail("Expected TestError")
            }
        }
        do {
            let publisher = (0..<2).publisher.append(error: TestError()).receive(on: DispatchQueue.main)
            let recorder = publisher.record()
            let recording = try wait(for: recorder.recording, timeout: 1)
            XCTAssertEqual(recording.output, [0, 1])
            if case .finished = recording.completion {
                XCTFail("Expected TestError")
            }
        }
    }
}


================================================
FILE: Tests/CombineExpectationsTests/Support.swift
================================================
import Combine

extension Publisher where Failure == Never {
    /// Returns a publisher which completes with an error.
    func append<Failure: Error>(error: Failure) -> AnyPublisher<Output, Failure> {
        setFailureType(to: Failure.self)
            .append(Fail(error: error))
            .eraseToAnyPublisher()
    }
}


================================================
FILE: Tests/CombineExpectationsTests/WackySubscriberTests.swift
================================================
import XCTest
import Combine
import Foundation
import CombineExpectations

/// Tests that Recorder fail tests when they are fed with a subscriber that does
/// not behave correctly, and messes with the Recorder state machine.
///
/// Our goal is to make it clear that the problem with wacky publishers is
/// wacky publishers, not this library.
class WackySubscriberTests: FailureTestCase {
    func testDoubleSubscriptionPublisher() throws {
        struct DoubleSubscriptionPublisher<Base: Publisher>: Publisher {
            typealias Output = Base.Output
            typealias Failure = Base.Failure
            let base: Base
            func receive<S>(subscriber: S) where S : Subscriber, Failure == S.Failure, Output == S.Input {
                base.receive(subscriber: subscriber)
                base.receive(subscriber: subscriber)
            }
        }
        assertFailure("failed - Publisher recorder is already subscribed") {
            let publisher = DoubleSubscriptionPublisher(base: Just("foo").makeConnectable())
            _ = publisher.record()
        }
    }
    
    func testCompletionBeforeSubscriptionPublisher() throws {
        struct CompletionBeforeSubscriptionPublisher: Publisher {
            typealias Output = Never
            typealias Failure = Never
            func receive<S>(subscriber: S) where S : Subscriber, Failure == S.Failure, Output == S.Input {
                subscriber.receive(completion: .finished)
            }
        }
        assertFailure("failed - Publisher recorder got unexpected completion before subscription: finished") {
            let publisher = CompletionBeforeSubscriptionPublisher()
            _ = publisher.record()
        }
    }
    
    func testInputBeforeSubscriptionPublisher() throws {
        struct InputBeforeSubscriptionPublisher: Publisher {
            typealias Output = String
            typealias Failure = Never
            func receive<S>(subscriber: S) where S : Subscriber, Failure == S.Failure, Output == S.Input {
                _ = subscriber.receive("foo")
            }
        }
        assertFailure(#"failed - Publisher recorder got unexpected input before subscription: "foo""#) {
            let publisher = InputBeforeSubscriptionPublisher()
            _ = publisher.record()
        }
    }
    
    func testInputAfterCompletionPublisher() throws {
        struct InputAfterCompletionPublisher<Base: Publisher>: Publisher
            where Base.Output == String
        {
            typealias Output = Base.Output
            typealias Failure = Base.Failure
            let base: Base
            func receive<S>(subscriber: S) where S : Subscriber, Failure == S.Failure, Output == S.Input {
                base.receive(subscriber: subscriber)
                _ = subscriber.receive("bar")
            }
        }
        assertFailure(#"failed - Publisher recorder got unexpected input after completion: "bar""#) {
            let publisher = InputAfterCompletionPublisher(base: Just("foo"))
            _ = publisher.record()
        }
    }
    
    func testDoubleCompletionPublisher() throws {
        struct DoubleCompletionPublisher<Base: Publisher>: Publisher {
            typealias Output = Base.Output
            typealias Failure = Base.Failure
            let base: Base
            func receive<S>(subscriber: S) where S : Subscriber, Failure == S.Failure, Output == S.Input {
                base.receive(subscriber: subscriber)
                subscriber.receive(completion: .finished)
            }
        }
        assertFailure("failed - Publisher recorder got unexpected completion after completion") {
            let publisher = DoubleCompletionPublisher(base: Just("foo"))
            _ = publisher.record()
        }
    }
}


================================================
FILE: Tests/CombineExpectationsTests/XCTestManifests.swift
================================================
import XCTest

#if !canImport(ObjectiveC)
public func allTests() -> [XCTestCaseEntry] {
    return [
        testCase(CombineExpectationsTests.allTests),
    ]
}
#endif


================================================
FILE: Tests/LinuxMain.swift
================================================
import XCTest

import CombineExpectationsTests

var tests = [XCTestCaseEntry]()
tests += CombineExpectationsTests.allTests()
XCTMain(tests)
Download .txt
gitextract_q_moxppx/

├── .github/
│   ├── FUNDING.yml
│   └── workflows/
│       └── ci.yml
├── .gitignore
├── CHANGELOG.md
├── CombineExpectations.podspec
├── LICENSE
├── Package.swift
├── README.md
├── Sources/
│   └── CombineExpectations/
│       ├── PublisherExpectation.swift
│       ├── PublisherExpectations/
│       │   ├── AvailableElements.swift
│       │   ├── Finished.swift
│       │   ├── Inverted.swift
│       │   ├── Map.swift
│       │   ├── Next.swift
│       │   ├── NextOne.swift
│       │   ├── Prefix.swift
│       │   └── Recording.swift
│       ├── Recorder.swift
│       └── RecordingError.swift
└── Tests/
    ├── CombineExpectationsTests/
    │   ├── DocumentationTests.swift
    │   ├── FailureTestCase.swift
    │   ├── LateSubscriptionTest.swift
    │   ├── RecorderTests.swift
    │   ├── Support.swift
    │   ├── WackySubscriberTests.swift
    │   └── XCTestManifests.swift
    └── LinuxMain.swift
Condensed preview — 27 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (193K chars).
[
  {
    "path": ".github/FUNDING.yml",
    "chars": 16,
    "preview": "github: [groue]\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 1055,
    "preview": "name: Continuous Integration\non:\n  push:\n    branches: [ master ]\n  pull_request:\n    branches: [ master ]\nconcurrency:\n"
  },
  {
    "path": ".gitignore",
    "chars": 63,
    "preview": ".DS_Store\n/.build\n/Packages\n/*.xcodeproj\nxcuserdata/\n/.swiftpm\n"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 2991,
    "preview": "Release Notes\n=============\n\nAll notable changes to this project will be documented in this file.\n\n#### 0.x Releases\n\n- "
  },
  {
    "path": "CombineExpectations.podspec",
    "chars": 836,
    "preview": "Pod::Spec.new do |s|\n  s.name     = 'CombineExpectations'\n  s.version  = '0.10.0'\n  \n  s.license  = { :type => 'MIT', :f"
  },
  {
    "path": "LICENSE",
    "chars": 1056,
    "preview": "Copyright (C) 2019 Gwendal Roué\n\nPermission is hereby granted, free of charge, to any person obtaining a\ncopy of this so"
  },
  {
    "path": "Package.swift",
    "chars": 1228,
    "preview": "// swift-tools-version:5.1\n// The swift-tools-version declares the minimum version of Swift required to build this packa"
  },
  {
    "path": "README.md",
    "chars": 25634,
    "preview": "# Combine Expectations\n\n### Utilities for tests that wait for Combine publishers.\n\n---\n\n**Latest release**: [version 0.1"
  },
  {
    "path": "Sources/CombineExpectations/PublisherExpectation.swift",
    "chars": 4350,
    "preview": "import XCTest\n\n/// A name space for publisher expectations\npublic enum PublisherExpectations { }\n\n/// The base protocol "
  },
  {
    "path": "Sources/CombineExpectations/PublisherExpectations/AvailableElements.swift",
    "chars": 2623,
    "preview": "import XCTest\n\nextension PublisherExpectations {\n    /// A publisher expectation which waits for the timeout to expire, "
  },
  {
    "path": "Sources/CombineExpectations/PublisherExpectations/Finished.swift",
    "chars": 3551,
    "preview": "import XCTest\n\n// The Finished expectation waits for the publisher to complete, and throws an\n// error if and only if th"
  },
  {
    "path": "Sources/CombineExpectations/PublisherExpectations/Inverted.swift",
    "chars": 978,
    "preview": "import XCTest\n\nextension PublisherExpectations {\n    /// A publisher expectation that fails if the base expectation is f"
  },
  {
    "path": "Sources/CombineExpectations/PublisherExpectations/Map.swift",
    "chars": 888,
    "preview": "import XCTest\n\nextension PublisherExpectations {\n    /// A publisher expectation that transforms the value of a base exp"
  },
  {
    "path": "Sources/CombineExpectations/PublisherExpectations/Next.swift",
    "chars": 3145,
    "preview": "import XCTest\n\nextension PublisherExpectations {\n    /// A publisher expectation which waits for the recorded publisher "
  },
  {
    "path": "Sources/CombineExpectations/PublisherExpectations/NextOne.swift",
    "chars": 4870,
    "preview": "import XCTest\n\nextension PublisherExpectations {\n    /// A publisher expectation which waits for the recorded publisher "
  },
  {
    "path": "Sources/CombineExpectations/PublisherExpectations/Prefix.swift",
    "chars": 4655,
    "preview": "import XCTest\n\nextension PublisherExpectations {\n    /// A publisher expectation which waits for the recorded publisher "
  },
  {
    "path": "Sources/CombineExpectations/PublisherExpectations/Recording.swift",
    "chars": 2446,
    "preview": "import Combine\nimport XCTest\n\nextension PublisherExpectations {\n    /// A publisher expectation which waits for the reco"
  },
  {
    "path": "Sources/CombineExpectations/Recorder.swift",
    "chars": 24267,
    "preview": "import Combine\nimport XCTest\n\n/// A Combine subscriber which records all events published by a publisher.\n///\n/// You cr"
  },
  {
    "path": "Sources/CombineExpectations/RecordingError.swift",
    "chars": 824,
    "preview": "import Foundation\n\n/// An error that may be thrown when waiting for publisher expectations.\npublic enum RecordingError: "
  },
  {
    "path": "Tests/CombineExpectationsTests/DocumentationTests.swift",
    "chars": 20307,
    "preview": "import XCTest\nimport Combine\nimport CombineExpectations\n\n/// Tests for sample code in documentation\nclass DocumentationT"
  },
  {
    "path": "Tests/CombineExpectationsTests/FailureTestCase.swift",
    "chars": 9026,
    "preview": "import XCTest\n\n/// A XCTestCase subclass that can test its own failures.\nclass FailureTestCase: XCTestCase {\n    private"
  },
  {
    "path": "Tests/CombineExpectationsTests/LateSubscriptionTest.swift",
    "chars": 3072,
    "preview": "import XCTest\nimport Combine\nimport Foundation\n@testable import CombineExpectations\n\n/// Tests for subscribers that do n"
  },
  {
    "path": "Tests/CombineExpectationsTests/RecorderTests.swift",
    "chars": 63103,
    "preview": "import XCTest\nimport Combine\nimport Foundation\n@testable import CombineExpectations\n\n/// General tests for publisher exp"
  },
  {
    "path": "Tests/CombineExpectationsTests/Support.swift",
    "chars": 327,
    "preview": "import Combine\n\nextension Publisher where Failure == Never {\n    /// Returns a publisher which completes with an error.\n"
  },
  {
    "path": "Tests/CombineExpectationsTests/WackySubscriberTests.swift",
    "chars": 3775,
    "preview": "import XCTest\nimport Combine\nimport Foundation\nimport CombineExpectations\n\n/// Tests that Recorder fail tests when they "
  },
  {
    "path": "Tests/CombineExpectationsTests/XCTestManifests.swift",
    "chars": 169,
    "preview": "import XCTest\n\n#if !canImport(ObjectiveC)\npublic func allTests() -> [XCTestCaseEntry] {\n    return [\n        testCase(Co"
  },
  {
    "path": "Tests/LinuxMain.swift",
    "chars": 140,
    "preview": "import XCTest\n\nimport CombineExpectationsTests\n\nvar tests = [XCTestCaseEntry]()\ntests += CombineExpectationsTests.allTes"
  }
]

About this extraction

This page contains the full source code of the groue/CombineExpectations GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 27 files (181.0 KB), approximately 40.1k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!