Skip to content

Commit

Permalink
test: improve on testing
Browse files Browse the repository at this point in the history
  • Loading branch information
sbertix committed May 26, 2021
1 parent 19343f6 commit 887ba6f
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 120 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,12 @@ public extension LockProviderType {
Self.generate(self, from: key)
}
}

public extension LockProviderType where Input == Void {
/// Unlock.
///
/// - returns: Some `Content`.
func unlock() -> Output {
Self.generate(self, from: ())
}
}
31 changes: 27 additions & 4 deletions Sources/ComposableRequest/Publishers/Pager/Pager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,20 @@ public extension Publishers {
self.generator = generator
}

/// Init.
///
/// - parameters:
/// - count: A valid `Int`. Defaults to `.max`.
/// - offset: A valid `Offset`.
/// - delay: A valid `Delay`.
/// - generator: A valid generator.
public init(_ count: Int = .max,
offset: Offset,
delay: Delay,
generator: @escaping (_ offset: Offset) -> Iteration) {
self.init(count, offset: offset, delay: { _ in delay }, generator: generator)
}

/// Init.
///
/// - parameters:
Expand Down Expand Up @@ -77,10 +91,19 @@ public extension Publishers {
case .stop:
return current.eraseToAnyPublisher()
case .load(let next):
return current
.append(Deferred { Pager(count - 1, offset: next, delay: delay, generator: generator) }
.delay(for: count - 1 > 0 ? delay(next) : .zero, scheduler: RunLoop.main))
.eraseToAnyPublisher()
switch count - 1 > 0 ? delay(next) : .zero {
case ...0:
return current
.append(Deferred { Pager(count - 1, offset: next, delay: delay, generator: generator) })
.eraseToAnyPublisher()
case let delay:
return current
.append(
Deferred { Pager(count - 1, offset: next, delay: self.delay, generator: generator) }
.delay(for: delay, scheduler: RunLoop.main)
)
.eraseToAnyPublisher()
}
}
}
.subscribe(subscriber)
Expand Down
152 changes: 36 additions & 116 deletions Tests/ComposableRequestTests/ObservableTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -95,59 +95,39 @@ internal final class ObservableTests: XCTestCase {

// MARK: Pagination

// swiftlint:disable line_length
/// Test a paginated fetch request.
func testPagination() {
let languages = ["en", "it", "de", "fr"]
let expectations = [languages.map(XCTestExpectation.init),
[XCTestExpectation(description: "delay"), XCTestExpectation(description: "completion")]]
.reduce(into: []) { $0.append(contentsOf: $1) }
let offset = Reference(0)
let delayed = Reference(false)
// Prepare the provider.
PagerProvider { pages in
// Actually paginate futures.
Pager(pages) { offset in
LockProvider<String, LockProvider<Int, LockProvider<Int, LockProvider<Int, LockProvider<Int, AnyPublisher<Int, Never>>>>>> { value, _, _, _, _ in
Just(value).map { _ in offset }.eraseToAnyPublisher()
}
.unlock(with: languages[offset])
.unlock(with: 0)
.unlock(with: 0)
.unlock(with: 0)
.unlock(with: 0)
.assertBackgroundThread()
.iterateFirst(stoppingAt: offset) {
XCTAssert(!Thread.isMainThread)
return .load($0 + 1)
// swiftlint:disable line_length
typealias Lock = LockProvider<Int, LockProvider<Void, LockProvider<Void, LockProvider<Void, LockProvider<Void, Just<Int>>>>>>
// swiftlint:enable line_length

let date = Date()
let expectations = ((0...4).map(String.init) + ["completion"]).map(XCTestExpectation.init)
PagerProvider {
Pager($0) { offset in
Lock { value, _, _, _, _ in
Just(value)
}
.unlock(with: offset)
.unlock()
.unlock()
.unlock()
.unlock()
.iterateFirst { .load($0 + 1) }
}
}
.pages(languages.count, offset: 0, delay: .seconds(2))
.subscribe(on: DispatchQueue.global(qos: .userInitiated))
.receive(on: RunLoop.main)
.assertMainThread()
.sink(
receiveCompletion: {
if case .failure(let error) = $0 { XCTFail(error.localizedDescription) }
XCTAssert(delayed.value, "Pagination did not wait.")
expectations.last?.fulfill()
},
receiveValue: {
XCTAssert(offset.value == $0)
offset.value = $0 + 1
expectations[$0].fulfill()
}
)
.store(in: &bin)
// Make sure you delay before it's completed.
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
delayed.value = true
expectations[4].fulfill()
.pages(5, offset: 0, delay: .seconds(1))
.sink {
if case .failure(let error) = $0 { XCTFail(error.localizedDescription) }
expectations.last?.fulfill()
} receiveValue: {
expectations[$0].fulfill()
}
wait(for: expectations, timeout: 30)
.store(in: &bin)

wait(for: expectations, timeout: 10)
XCTAssertLessThan(date.timeIntervalSinceNow, -4)
}
// swiftlint:enable line_length

/// Test a pagination request using a ranked offset.
func testRankedOffsetPagination() {
Expand All @@ -159,55 +139,19 @@ internal final class ObservableTests: XCTestCase {
Pager(pages) { Just(pages.rank[$0]).iterateFirst { .load($0 + 1) } }
}
.pages(values.count, offset: 0, rank: values)
.sink(
receiveCompletion: {
if case .failure(let error) = $0 { XCTFail(error.localizedDescription) }
expectations.last?.fulfill()
},
receiveValue: { value in
offset.sync {
XCTAssert(value == $0)
expectations[value].fulfill()
$0 = value + 1
}
}
)
.store(in: &bin)
wait(for: expectations, timeout: 30)
}

/// Test a remote paginated fetch request.
func testRemotePagination() {
let languages = ["en", "it", "de", "fr"]
let expectations = languages.map(XCTestExpectation.init) + [XCTestExpectation(description: "completion")]
let offset = Reference(0)
// Prepare the provider.
LockSessionPagerProvider { url, session, pages in // Additional tests.
// Actually paginate futures.
Pager(pages) { offset in
Request(url)
.query(appending: languages[offset], forKey: "l")
.publish(with: session)
.map { _ in offset }
.iterateLast { .load(($0 ?? -1) + 1) }
}
.sink {
if case .failure(let error) = $0 { XCTFail(error.localizedDescription) }
expectations.last?.fulfill()
}
.unlock(with: url)
.session(.shared)
.pages(languages.count, offset: 0)
.sink(
receiveCompletion: {
if case .failure(let error) = $0 { XCTFail(error.localizedDescription) }
expectations.last?.fulfill()
},
receiveValue: {
XCTAssert(offset.value == $0)
offset.value = $0 + 1
expectations[$0].fulfill()
receiveValue: { value in
offset.sync {
XCTAssert(value == $0)
expectations[value].fulfill()
$0 = value + 1
}
)
}
.store(in: &bin)
wait(for: expectations, timeout: 30 * TimeInterval(languages.count))
wait(for: expectations, timeout: 30)
}

// MARK: Cancellation
Expand All @@ -230,28 +174,4 @@ internal final class ObservableTests: XCTestCase {
}
wait(for: expectations, timeout: 5)
}

/// Test paginated cancellation.
func testPaginatedCancellation() {
let expectations = ["output", "completion"].map(XCTestExpectation.init)
expectations[0].assertForOverFulfill = true
expectations[0].expectedFulfillmentCount = 1
Pager(3) {
Request(url)
.publish(session: .shared)
.map(\.response)
}
.sink(
receiveCompletion: { _ in
XCTFail("This should not complete")
},
receiveValue: { _ in
expectations.first?.fulfill()
self.bin.removeAll()
}
)
.store(in: &bin)
DispatchQueue.main.asyncAfter(deadline: .now() + 25) { expectations.last?.fulfill() }
wait(for: expectations, timeout: 30)
}
}
66 changes: 66 additions & 0 deletions Tests/ComposableRequestTests/PagerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,70 @@ internal class PagerTests: XCTestCase {
}
XCTAssertEqual(provider.pages(1, delay: .seconds(2)), 3)
}

/// Test iterations.
func testIterations() {
/// Compare equality.
///
/// - parameters:
/// - instruction: A valid `Instruction`.
/// - value: Some value.
func compare<T: Equatable>(_ instruction: Instruction<T>, _ value: T) {
switch instruction {
case .stop:
XCTFail("Iteration stopped.")
case .load(let next):
XCTAssertEqual(next, value)
}
}

/// Compare to stop.
///
/// - parameter instruction: A valid `Instruction`.
func compare<T>(stop instruction: Instruction<T>) {
switch instruction {
case .stop:
break
case .load:
XCTFail("Iteration should stop.")
}
}

compare(Just(1).iterate { .load($0.reduce(into: 0) { $0 += $1 }+1) }.offset([0]), 1)
compare(stop: Just(1).iterate { $0 == 1 } with: { .load(($0.first ?? -1) + 1) }.offset([0]))
compare(stop: Just(1).iterate(stoppingAt: 1) { .load(($0.first ?? -1) + 1) }.offset([0]))
compare(Just(1).iterateFirst { .load(($0 ?? -1) + 1) }.offset([0]), 1)
compare(stop: Just(1).iterateFirst { $0 == 1 } with: { .load(($0 ?? -1) + 1) }.offset([0]))
compare(stop: Just(1).iterateFirst(stoppingAt: 1) { .load(($0 ?? -1) + 1) }.offset([0]))
compare(Just(1).iterateLast { .load(($0 ?? -1) + 1) }.offset([0]), 1)
compare(stop: Just(1).iterateLast { $0 == 1 } with: { .load(($0 ?? -1) + 1) }.offset([0]))
compare(stop: Just(1).iterateLast(stoppingAt: 1) { .load(($0 ?? -1) + 1) }.offset([0]))
compare(stop: Just(()).iterate { _ in false }.offset([()]))

compare(Just(1).iterateFirst { .load($0 + 1) }.offset([0]), 1)
compare(stop: Just(1).iterateFirst { $0 == 1 } with: { .load($0 + 1) }.offset([0]))
compare(stop: Just(1).iterateFirst(stoppingAt: 1) { .load($0 + 1) }.offset([0]))
compare(Just(1).iterateLast { .load($0 + 1) }.offset([0]), 1)
compare(stop: Just(1).iterateLast { $0 == 1 } with: { .load($0 + 1) }.offset([0]))
compare(stop: Just(1).iterateLast(stoppingAt: 1) { .load($0 + 1) }.offset([0]))

compare(Just(Page(offset: 1)).iterateFirst().offset([Page(offset: 0)]), 0)
compare(stop: Just(Page(offset: 1)).iterateFirst { $0 == 0 }.offset([Page(offset: 0)]))
compare(Just(Page(offset: 1)).iterateLast().offset([Page(offset: 0)]), 0)
compare(stop: Just(Page(offset: 1)).iterateFirst { $0 == 0 }.offset([Page(offset: 0)]))

compare(stop: Just(Page(offset: 1)).iterateFirst(stoppingAt: 0).offset([Page(offset: 0)]))
compare(stop: Just(Page(offset: 1)).iterateLast(stoppingAt: 0).offset([Page(offset: 0)]))
}
}

fileprivate extension PagerTests {
/// A `struct` defining a custom `Paginatable`.
struct Page: Paginatable {
let offset: Offset
}
}

fileprivate extension PagerTests.Page {
typealias Offset = Int?
}

0 comments on commit 887ba6f

Please sign in to comment.