Skip to content

Commit 887ba6f

Browse files
committed
test: improve on testing
1 parent 19343f6 commit 887ba6f

File tree

4 files changed

+138
-120
lines changed

4 files changed

+138
-120
lines changed

Sources/ComposableRequest/Providers/Lock/LockProviderType.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,12 @@ public extension LockProviderType {
1919
Self.generate(self, from: key)
2020
}
2121
}
22+
23+
public extension LockProviderType where Input == Void {
24+
/// Unlock.
25+
///
26+
/// - returns: Some `Content`.
27+
func unlock() -> Output {
28+
Self.generate(self, from: ())
29+
}
30+
}

Sources/ComposableRequest/Publishers/Pager/Pager.swift

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,20 @@ public extension Publishers {
4242
self.generator = generator
4343
}
4444

45+
/// Init.
46+
///
47+
/// - parameters:
48+
/// - count: A valid `Int`. Defaults to `.max`.
49+
/// - offset: A valid `Offset`.
50+
/// - delay: A valid `Delay`.
51+
/// - generator: A valid generator.
52+
public init(_ count: Int = .max,
53+
offset: Offset,
54+
delay: Delay,
55+
generator: @escaping (_ offset: Offset) -> Iteration) {
56+
self.init(count, offset: offset, delay: { _ in delay }, generator: generator)
57+
}
58+
4559
/// Init.
4660
///
4761
/// - parameters:
@@ -77,10 +91,19 @@ public extension Publishers {
7791
case .stop:
7892
return current.eraseToAnyPublisher()
7993
case .load(let next):
80-
return current
81-
.append(Deferred { Pager(count - 1, offset: next, delay: delay, generator: generator) }
82-
.delay(for: count - 1 > 0 ? delay(next) : .zero, scheduler: RunLoop.main))
83-
.eraseToAnyPublisher()
94+
switch count - 1 > 0 ? delay(next) : .zero {
95+
case ...0:
96+
return current
97+
.append(Deferred { Pager(count - 1, offset: next, delay: delay, generator: generator) })
98+
.eraseToAnyPublisher()
99+
case let delay:
100+
return current
101+
.append(
102+
Deferred { Pager(count - 1, offset: next, delay: self.delay, generator: generator) }
103+
.delay(for: delay, scheduler: RunLoop.main)
104+
)
105+
.eraseToAnyPublisher()
106+
}
84107
}
85108
}
86109
.subscribe(subscriber)

Tests/ComposableRequestTests/ObservableTests.swift

Lines changed: 36 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -95,59 +95,39 @@ internal final class ObservableTests: XCTestCase {
9595

9696
// MARK: Pagination
9797

98-
// swiftlint:disable line_length
9998
/// Test a paginated fetch request.
10099
func testPagination() {
101-
let languages = ["en", "it", "de", "fr"]
102-
let expectations = [languages.map(XCTestExpectation.init),
103-
[XCTestExpectation(description: "delay"), XCTestExpectation(description: "completion")]]
104-
.reduce(into: []) { $0.append(contentsOf: $1) }
105-
let offset = Reference(0)
106-
let delayed = Reference(false)
107-
// Prepare the provider.
108-
PagerProvider { pages in
109-
// Actually paginate futures.
110-
Pager(pages) { offset in
111-
LockProvider<String, LockProvider<Int, LockProvider<Int, LockProvider<Int, LockProvider<Int, AnyPublisher<Int, Never>>>>>> { value, _, _, _, _ in
112-
Just(value).map { _ in offset }.eraseToAnyPublisher()
113-
}
114-
.unlock(with: languages[offset])
115-
.unlock(with: 0)
116-
.unlock(with: 0)
117-
.unlock(with: 0)
118-
.unlock(with: 0)
119-
.assertBackgroundThread()
120-
.iterateFirst(stoppingAt: offset) {
121-
XCTAssert(!Thread.isMainThread)
122-
return .load($0 + 1)
100+
// swiftlint:disable line_length
101+
typealias Lock = LockProvider<Int, LockProvider<Void, LockProvider<Void, LockProvider<Void, LockProvider<Void, Just<Int>>>>>>
102+
// swiftlint:enable line_length
103+
104+
let date = Date()
105+
let expectations = ((0...4).map(String.init) + ["completion"]).map(XCTestExpectation.init)
106+
PagerProvider {
107+
Pager($0) { offset in
108+
Lock { value, _, _, _, _ in
109+
Just(value)
123110
}
111+
.unlock(with: offset)
112+
.unlock()
113+
.unlock()
114+
.unlock()
115+
.unlock()
116+
.iterateFirst { .load($0 + 1) }
124117
}
125118
}
126-
.pages(languages.count, offset: 0, delay: .seconds(2))
127-
.subscribe(on: DispatchQueue.global(qos: .userInitiated))
128-
.receive(on: RunLoop.main)
129-
.assertMainThread()
130-
.sink(
131-
receiveCompletion: {
132-
if case .failure(let error) = $0 { XCTFail(error.localizedDescription) }
133-
XCTAssert(delayed.value, "Pagination did not wait.")
134-
expectations.last?.fulfill()
135-
},
136-
receiveValue: {
137-
XCTAssert(offset.value == $0)
138-
offset.value = $0 + 1
139-
expectations[$0].fulfill()
140-
}
141-
)
142-
.store(in: &bin)
143-
// Make sure you delay before it's completed.
144-
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
145-
delayed.value = true
146-
expectations[4].fulfill()
119+
.pages(5, offset: 0, delay: .seconds(1))
120+
.sink {
121+
if case .failure(let error) = $0 { XCTFail(error.localizedDescription) }
122+
expectations.last?.fulfill()
123+
} receiveValue: {
124+
expectations[$0].fulfill()
147125
}
148-
wait(for: expectations, timeout: 30)
126+
.store(in: &bin)
127+
128+
wait(for: expectations, timeout: 10)
129+
XCTAssertLessThan(date.timeIntervalSinceNow, -4)
149130
}
150-
// swiftlint:enable line_length
151131

152132
/// Test a pagination request using a ranked offset.
153133
func testRankedOffsetPagination() {
@@ -159,55 +139,19 @@ internal final class ObservableTests: XCTestCase {
159139
Pager(pages) { Just(pages.rank[$0]).iterateFirst { .load($0 + 1) } }
160140
}
161141
.pages(values.count, offset: 0, rank: values)
162-
.sink(
163-
receiveCompletion: {
164-
if case .failure(let error) = $0 { XCTFail(error.localizedDescription) }
165-
expectations.last?.fulfill()
166-
},
167-
receiveValue: { value in
168-
offset.sync {
169-
XCTAssert(value == $0)
170-
expectations[value].fulfill()
171-
$0 = value + 1
172-
}
173-
}
174-
)
175-
.store(in: &bin)
176-
wait(for: expectations, timeout: 30)
177-
}
178-
179-
/// Test a remote paginated fetch request.
180-
func testRemotePagination() {
181-
let languages = ["en", "it", "de", "fr"]
182-
let expectations = languages.map(XCTestExpectation.init) + [XCTestExpectation(description: "completion")]
183-
let offset = Reference(0)
184-
// Prepare the provider.
185-
LockSessionPagerProvider { url, session, pages in // Additional tests.
186-
// Actually paginate futures.
187-
Pager(pages) { offset in
188-
Request(url)
189-
.query(appending: languages[offset], forKey: "l")
190-
.publish(with: session)
191-
.map { _ in offset }
192-
.iterateLast { .load(($0 ?? -1) + 1) }
193-
}
142+
.sink {
143+
if case .failure(let error) = $0 { XCTFail(error.localizedDescription) }
144+
expectations.last?.fulfill()
194145
}
195-
.unlock(with: url)
196-
.session(.shared)
197-
.pages(languages.count, offset: 0)
198-
.sink(
199-
receiveCompletion: {
200-
if case .failure(let error) = $0 { XCTFail(error.localizedDescription) }
201-
expectations.last?.fulfill()
202-
},
203-
receiveValue: {
204-
XCTAssert(offset.value == $0)
205-
offset.value = $0 + 1
206-
expectations[$0].fulfill()
146+
receiveValue: { value in
147+
offset.sync {
148+
XCTAssert(value == $0)
149+
expectations[value].fulfill()
150+
$0 = value + 1
207151
}
208-
)
152+
}
209153
.store(in: &bin)
210-
wait(for: expectations, timeout: 30 * TimeInterval(languages.count))
154+
wait(for: expectations, timeout: 30)
211155
}
212156

213157
// MARK: Cancellation
@@ -230,28 +174,4 @@ internal final class ObservableTests: XCTestCase {
230174
}
231175
wait(for: expectations, timeout: 5)
232176
}
233-
234-
/// Test paginated cancellation.
235-
func testPaginatedCancellation() {
236-
let expectations = ["output", "completion"].map(XCTestExpectation.init)
237-
expectations[0].assertForOverFulfill = true
238-
expectations[0].expectedFulfillmentCount = 1
239-
Pager(3) {
240-
Request(url)
241-
.publish(session: .shared)
242-
.map(\.response)
243-
}
244-
.sink(
245-
receiveCompletion: { _ in
246-
XCTFail("This should not complete")
247-
},
248-
receiveValue: { _ in
249-
expectations.first?.fulfill()
250-
self.bin.removeAll()
251-
}
252-
)
253-
.store(in: &bin)
254-
DispatchQueue.main.asyncAfter(deadline: .now() + 25) { expectations.last?.fulfill() }
255-
wait(for: expectations, timeout: 30)
256-
}
257177
}

Tests/ComposableRequestTests/PagerTests.swift

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,70 @@ internal class PagerTests: XCTestCase {
7575
}
7676
XCTAssertEqual(provider.pages(1, delay: .seconds(2)), 3)
7777
}
78+
79+
/// Test iterations.
80+
func testIterations() {
81+
/// Compare equality.
82+
///
83+
/// - parameters:
84+
/// - instruction: A valid `Instruction`.
85+
/// - value: Some value.
86+
func compare<T: Equatable>(_ instruction: Instruction<T>, _ value: T) {
87+
switch instruction {
88+
case .stop:
89+
XCTFail("Iteration stopped.")
90+
case .load(let next):
91+
XCTAssertEqual(next, value)
92+
}
93+
}
94+
95+
/// Compare to stop.
96+
///
97+
/// - parameter instruction: A valid `Instruction`.
98+
func compare<T>(stop instruction: Instruction<T>) {
99+
switch instruction {
100+
case .stop:
101+
break
102+
case .load:
103+
XCTFail("Iteration should stop.")
104+
}
105+
}
106+
107+
compare(Just(1).iterate { .load($0.reduce(into: 0) { $0 += $1 }+1) }.offset([0]), 1)
108+
compare(stop: Just(1).iterate { $0 == 1 } with: { .load(($0.first ?? -1) + 1) }.offset([0]))
109+
compare(stop: Just(1).iterate(stoppingAt: 1) { .load(($0.first ?? -1) + 1) }.offset([0]))
110+
compare(Just(1).iterateFirst { .load(($0 ?? -1) + 1) }.offset([0]), 1)
111+
compare(stop: Just(1).iterateFirst { $0 == 1 } with: { .load(($0 ?? -1) + 1) }.offset([0]))
112+
compare(stop: Just(1).iterateFirst(stoppingAt: 1) { .load(($0 ?? -1) + 1) }.offset([0]))
113+
compare(Just(1).iterateLast { .load(($0 ?? -1) + 1) }.offset([0]), 1)
114+
compare(stop: Just(1).iterateLast { $0 == 1 } with: { .load(($0 ?? -1) + 1) }.offset([0]))
115+
compare(stop: Just(1).iterateLast(stoppingAt: 1) { .load(($0 ?? -1) + 1) }.offset([0]))
116+
compare(stop: Just(()).iterate { _ in false }.offset([()]))
117+
118+
compare(Just(1).iterateFirst { .load($0 + 1) }.offset([0]), 1)
119+
compare(stop: Just(1).iterateFirst { $0 == 1 } with: { .load($0 + 1) }.offset([0]))
120+
compare(stop: Just(1).iterateFirst(stoppingAt: 1) { .load($0 + 1) }.offset([0]))
121+
compare(Just(1).iterateLast { .load($0 + 1) }.offset([0]), 1)
122+
compare(stop: Just(1).iterateLast { $0 == 1 } with: { .load($0 + 1) }.offset([0]))
123+
compare(stop: Just(1).iterateLast(stoppingAt: 1) { .load($0 + 1) }.offset([0]))
124+
125+
compare(Just(Page(offset: 1)).iterateFirst().offset([Page(offset: 0)]), 0)
126+
compare(stop: Just(Page(offset: 1)).iterateFirst { $0 == 0 }.offset([Page(offset: 0)]))
127+
compare(Just(Page(offset: 1)).iterateLast().offset([Page(offset: 0)]), 0)
128+
compare(stop: Just(Page(offset: 1)).iterateFirst { $0 == 0 }.offset([Page(offset: 0)]))
129+
130+
compare(stop: Just(Page(offset: 1)).iterateFirst(stoppingAt: 0).offset([Page(offset: 0)]))
131+
compare(stop: Just(Page(offset: 1)).iterateLast(stoppingAt: 0).offset([Page(offset: 0)]))
132+
}
133+
}
134+
135+
fileprivate extension PagerTests {
136+
/// A `struct` defining a custom `Paginatable`.
137+
struct Page: Paginatable {
138+
let offset: Offset
139+
}
140+
}
141+
142+
fileprivate extension PagerTests.Page {
143+
typealias Offset = Int?
78144
}

0 commit comments

Comments
 (0)