From 49a241cb7d44e6f5ff7083ca67cb00a96241e655 Mon Sep 17 00:00:00 2001 From: Stephen Celis Date: Mon, 10 Jul 2023 10:16:26 -0700 Subject: [PATCH] wip --- .../ReliablyTestingAsync/Countdown.swift | 4 - .../NumberFactModelTests.swift | 120 +----------------- .../ReliablyTestingAsyncTests.swift | 32 ++--- 0241-reliably-testing-async-pt4/README.md | 5 + .../project.pbxproj | 0 .../contents.xcworkspacedata | 0 .../xcshareddata/IDEWorkspaceChecks.plist | 0 .../xcschemes/ReliablyTestingAsync.xcscheme | 0 .../ReliablyTestingAsync/App.swift | 0 .../AccentColor.colorset/Contents.json | 0 .../AppIcon.appiconset/Contents.json | 0 .../Assets.xcassets/Contents.json | 0 .../ConcurrencyExtras.swift | 0 .../ReliablyTestingAsync/ContentView.swift | 0 .../ReliablyTestingAsync/Countdown.swift | 4 + .../ReliablyTestingAsync/Info.plist | 0 .../Preview Assets.xcassets/Contents.json | 0 .../LockIsolated.swift | 0 .../NumberFactModelTests.swift | 120 +++++++++++++++++- .../ReliablyTestingAsync.xctestplan | 0 .../ReliablyTestingAsyncTests.swift | 32 +++-- README.md | 1 + 22 files changed, 162 insertions(+), 156 deletions(-) create mode 100644 0241-reliably-testing-async-pt4/README.md rename {0240-reliably-testing-async-pt3/redo => 0241-reliably-testing-async-pt4}/ReliablyTestingAsync/ReliablyTestingAsync.xcodeproj/project.pbxproj (100%) rename {0240-reliably-testing-async-pt3/redo => 0241-reliably-testing-async-pt4}/ReliablyTestingAsync/ReliablyTestingAsync.xcodeproj/project.xcworkspace/contents.xcworkspacedata (100%) rename {0240-reliably-testing-async-pt3/redo => 0241-reliably-testing-async-pt4}/ReliablyTestingAsync/ReliablyTestingAsync.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (100%) rename {0240-reliably-testing-async-pt3/redo => 0241-reliably-testing-async-pt4}/ReliablyTestingAsync/ReliablyTestingAsync.xcodeproj/xcshareddata/xcschemes/ReliablyTestingAsync.xcscheme (100%) rename {0240-reliably-testing-async-pt3/redo => 0241-reliably-testing-async-pt4}/ReliablyTestingAsync/ReliablyTestingAsync/App.swift (100%) rename {0240-reliably-testing-async-pt3/redo => 0241-reliably-testing-async-pt4}/ReliablyTestingAsync/ReliablyTestingAsync/Assets.xcassets/AccentColor.colorset/Contents.json (100%) rename {0240-reliably-testing-async-pt3/redo => 0241-reliably-testing-async-pt4}/ReliablyTestingAsync/ReliablyTestingAsync/Assets.xcassets/AppIcon.appiconset/Contents.json (100%) rename {0240-reliably-testing-async-pt3/redo => 0241-reliably-testing-async-pt4}/ReliablyTestingAsync/ReliablyTestingAsync/Assets.xcassets/Contents.json (100%) rename {0240-reliably-testing-async-pt3/redo => 0241-reliably-testing-async-pt4}/ReliablyTestingAsync/ReliablyTestingAsync/ConcurrencyExtras.swift (100%) rename {0240-reliably-testing-async-pt3/redo => 0241-reliably-testing-async-pt4}/ReliablyTestingAsync/ReliablyTestingAsync/ContentView.swift (100%) rename {0240-reliably-testing-async-pt3/redo => 0241-reliably-testing-async-pt4}/ReliablyTestingAsync/ReliablyTestingAsync/Countdown.swift (97%) rename {0240-reliably-testing-async-pt3/redo => 0241-reliably-testing-async-pt4}/ReliablyTestingAsync/ReliablyTestingAsync/Info.plist (100%) rename {0240-reliably-testing-async-pt3/redo => 0241-reliably-testing-async-pt4}/ReliablyTestingAsync/ReliablyTestingAsync/Preview Content/Preview Assets.xcassets/Contents.json (100%) rename {0240-reliably-testing-async-pt3/redo => 0241-reliably-testing-async-pt4}/ReliablyTestingAsync/ReliablyTestingAsyncTests/LockIsolated.swift (100%) rename {0240-reliably-testing-async-pt3/redo => 0241-reliably-testing-async-pt4}/ReliablyTestingAsync/ReliablyTestingAsyncTests/NumberFactModelTests.swift (57%) rename {0240-reliably-testing-async-pt3/redo => 0241-reliably-testing-async-pt4}/ReliablyTestingAsync/ReliablyTestingAsyncTests/ReliablyTestingAsync.xctestplan (100%) rename {0240-reliably-testing-async-pt3/redo => 0241-reliably-testing-async-pt4}/ReliablyTestingAsync/ReliablyTestingAsyncTests/ReliablyTestingAsyncTests.swift (83%) diff --git a/0240-reliably-testing-async-pt3/ReliablyTestingAsync/ReliablyTestingAsync/Countdown.swift b/0240-reliably-testing-async-pt3/ReliablyTestingAsync/ReliablyTestingAsync/Countdown.swift index 2ca64e67..e62c1781 100644 --- a/0240-reliably-testing-async-pt3/ReliablyTestingAsync/ReliablyTestingAsync/Countdown.swift +++ b/0240-reliably-testing-async-pt3/ReliablyTestingAsync/ReliablyTestingAsync/Countdown.swift @@ -114,10 +114,6 @@ private let pointFreeColors = [ struct CountdownDemo_Previews: PreviewProvider { static var previews: some View { - let _ = swift_task_enqueueGlobal_hook = { job, _ in - MainActor.shared.enqueue(job) - } - CountdownDemo(clock: .immediate) } } diff --git a/0240-reliably-testing-async-pt3/ReliablyTestingAsync/ReliablyTestingAsyncTests/NumberFactModelTests.swift b/0240-reliably-testing-async-pt3/ReliablyTestingAsync/ReliablyTestingAsyncTests/NumberFactModelTests.swift index e3b32d15..876a94d5 100644 --- a/0240-reliably-testing-async-pt3/ReliablyTestingAsync/ReliablyTestingAsyncTests/NumberFactModelTests.swift +++ b/0240-reliably-testing-async-pt3/ReliablyTestingAsync/ReliablyTestingAsyncTests/NumberFactModelTests.swift @@ -48,25 +48,6 @@ final class NumberFactModelTests: XCTestCase { XCTAssertEqual(model.fact, "0 is a good number.") } - func testFactClearsOut_MainSerialExecutor() async { - swift_task_enqueueGlobal_hook = { job, _ in - MainActor.shared.enqueue(job) - } - - let model = withDependencies { - $0.numberFact.fact = { "\($0) is a good number." } - } operation: { - NumberFactModel() - } - model.fact = "An old fact about 0." - - let task = Task { await model.getFactButtonTapped() } - await Task.yield() - XCTAssertEqual(model.fact, nil) - await task.value - XCTAssertEqual(model.fact, "0 is a good number.") - } - func testFactIsLoading() async { let fact = AsyncStream.makeStream(of: String.self) @@ -88,26 +69,6 @@ final class NumberFactModelTests: XCTestCase { XCTAssertEqual(model.isLoading, false) } - func testFactIsLoading_MainSerialExecutor() async { - swift_task_enqueueGlobal_hook = { job, _ in - MainActor.shared.enqueue(job) - } - - let model = withDependencies { - $0.numberFact.fact = { "\($0) is a good number." } - } operation: { - NumberFactModel() - } - model.fact = "An old fact about 0." - - let task = Task { await model.getFactButtonTapped() } - await Task.yield() - XCTAssertEqual(model.isLoading, true) - await task.value - XCTAssertEqual(model.fact, "0 is a good number.") - XCTAssertEqual(model.isLoading, false) - } - func testBackToBackGetFact() async throws { let fact0 = AsyncStream.makeStream(of: String.self) let fact1 = AsyncStream.makeStream(of: String.self) @@ -138,38 +99,7 @@ final class NumberFactModelTests: XCTestCase { await task1.value XCTAssertEqual(model.fact, "0 is a great number.") } - - func testBackToBackGetFact_MainSerialExecutor() async throws { - swift_task_enqueueGlobal_hook = { job, _ in - MainActor.shared.enqueue(job) - } - - let callCount = LockIsolated(0) - - let model = withDependencies { - $0.numberFact.fact = { number in - callCount.withValue { $0 += 1 } - if callCount.value == 1 { - return "0 is a better number." - } else if callCount.value == 2 { - return "0 is a great number." - } else { - fatalError() - } - } - } operation: { - NumberFactModel() - } - - let task0 = Task { await model.getFactButtonTapped() } - let task1 = Task { await model.getFactButtonTapped() } - await Task.yield() - await task0.value - await task1.value - XCTAssertEqual(model.fact, "0 is a great number.") - } - - + func testCancel() async { let model = withDependencies { $0.numberFact.fact = { _ in try await Task.never() } @@ -182,65 +112,25 @@ final class NumberFactModelTests: XCTestCase { await task.value XCTAssertEqual(model.fact, nil) } - - func testCancel_MainSerialExecutor() async { - swift_task_enqueueGlobal_hook = { job, _ in - MainActor.shared.enqueue(job) - } - - let model = withDependencies { - $0.numberFact.fact = { - await Task.yield() - try Task.checkCancellation() - return "\($0) is a good number." - } - } operation: { - NumberFactModel() - } - let task = Task { await model.getFactButtonTapped() } - await Task.yield() - model.cancelButtonTapped() - await task.value - XCTAssertEqual(model.fact, nil) - } - - + func testScreenshots() async { let model = NumberFactModel() - + let task = Task { await model.onTask() } - + await Task.megaYield() NotificationCenter.default.post(name: UIApplication.userDidTakeScreenshotNotification, object: nil) while model.count != 1 { await Task.yield() } XCTAssertEqual(model.count, 1) - + NotificationCenter.default.post(name: UIApplication.userDidTakeScreenshotNotification, object: nil) while model.count != 2 { await Task.yield() } XCTAssertEqual(model.count, 2) } - - func testScreenshots_MainSerialExecutor() async { - swift_task_enqueueGlobal_hook = { job, _ in - MainActor.shared.enqueue(job) - } - - let model = NumberFactModel() - - let task = Task { await model.onTask() } - await Task.yield() - NotificationCenter.default.post(name: UIApplication.userDidTakeScreenshotNotification, object: nil) - await Task.yield() - XCTAssertEqual(model.count, 1) - - NotificationCenter.default.post(name: UIApplication.userDidTakeScreenshotNotification, object: nil) - await Task.yield() - XCTAssertEqual(model.count, 2) - } } extension Task where Success == Never, Failure == Never { diff --git a/0240-reliably-testing-async-pt3/ReliablyTestingAsync/ReliablyTestingAsyncTests/ReliablyTestingAsyncTests.swift b/0240-reliably-testing-async-pt3/ReliablyTestingAsync/ReliablyTestingAsyncTests/ReliablyTestingAsyncTests.swift index 44907965..9b1ef76e 100644 --- a/0240-reliably-testing-async-pt3/ReliablyTestingAsync/ReliablyTestingAsyncTests/ReliablyTestingAsyncTests.swift +++ b/0240-reliably-testing-async-pt3/ReliablyTestingAsync/ReliablyTestingAsyncTests/ReliablyTestingAsyncTests.swift @@ -14,10 +14,8 @@ final class ReliablyTestingAsyncTests: XCTestCase { let values = LockIsolated([Int]()) let task = Task { values.withValue { $0.append(1) } - print(#line, { Thread.current }()) } values.withValue { $0.append(2) } - print(#line, { Thread.current }()) await task.value XCTAssertEqual(values.value, [2, 1]) } @@ -40,11 +38,9 @@ final class ReliablyTestingAsyncTests: XCTestCase { let values = LockIsolated([Int]()) let task1 = Task { values.withValue { $0.append(1) } - print({ Thread.current }()) } let task2 = Task { values.withValue { $0.append(2) } - print({ Thread.current }()) } _ = await (task1.value, task2.value) XCTAssertEqual(values.value, [1, 2]) @@ -102,11 +98,7 @@ final class ReliablyTestingAsyncTests: XCTestCase { } for task in tasks { await task.value } - XCTAssertEqual( - values.value, - Array(0...count).map { $0 * 2 } // evens less than or equal to max - + Array(0...count).map { $0 * 2 + 1 } // odds less than or equal to max - ) + XCTAssertEqual(values.value, [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21]) } func testYieldScheduling_MainSerialExecutor() async { @@ -125,36 +117,28 @@ final class ReliablyTestingAsyncTests: XCTestCase { } for task in tasks { await task.value } - XCTAssertEqual( - values.value, - Array(0...count).map { $0 * 2 } // evens less than or equal to max - + Array(0...count).map { $0 * 2 + 1 } // odds less than or equal to max - ) + XCTAssertEqual(values.value, [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21]) } - /* - - [task0, task1, task2] + /* [{task0}, task1, task2] [0] - [{task1}, task2, task0] + [task2, task0, task1] [0, 2] - [{task2}, task0, task1] + [task0, task1, task2] [0, 2, 4] - [{task0}, task1, task2] + [task1, task2] [0, 2, 4, 1] - [{task1}, task2] + [task2] [0, 2, 4, 1, 3] - [{task2}] - [0, 2, 4, 1, 3, 5] - [] [0, 2, 4, 1, 3, 5] + */ @MainActor diff --git a/0241-reliably-testing-async-pt4/README.md b/0241-reliably-testing-async-pt4/README.md new file mode 100644 index 00000000..8e75846d --- /dev/null +++ b/0241-reliably-testing-async-pt4/README.md @@ -0,0 +1,5 @@ +## [Point-Free](https://www.pointfree.co) + +> #### This directory contains code from Point-Free Episode: [Reliable Async Tests: 🥹](https://www.pointfree.co/episodes/ep241-reliable-async-tests) +> +> We continue our deep dive into advanced Swift concurrency by exploring the concept of “executors” to better understand its global enqueue hook. We will then put this hook to work and finally have reliable, 100% deterministic async tests. diff --git a/0240-reliably-testing-async-pt3/redo/ReliablyTestingAsync/ReliablyTestingAsync.xcodeproj/project.pbxproj b/0241-reliably-testing-async-pt4/ReliablyTestingAsync/ReliablyTestingAsync.xcodeproj/project.pbxproj similarity index 100% rename from 0240-reliably-testing-async-pt3/redo/ReliablyTestingAsync/ReliablyTestingAsync.xcodeproj/project.pbxproj rename to 0241-reliably-testing-async-pt4/ReliablyTestingAsync/ReliablyTestingAsync.xcodeproj/project.pbxproj diff --git a/0240-reliably-testing-async-pt3/redo/ReliablyTestingAsync/ReliablyTestingAsync.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/0241-reliably-testing-async-pt4/ReliablyTestingAsync/ReliablyTestingAsync.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from 0240-reliably-testing-async-pt3/redo/ReliablyTestingAsync/ReliablyTestingAsync.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to 0241-reliably-testing-async-pt4/ReliablyTestingAsync/ReliablyTestingAsync.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/0240-reliably-testing-async-pt3/redo/ReliablyTestingAsync/ReliablyTestingAsync.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/0241-reliably-testing-async-pt4/ReliablyTestingAsync/ReliablyTestingAsync.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from 0240-reliably-testing-async-pt3/redo/ReliablyTestingAsync/ReliablyTestingAsync.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to 0241-reliably-testing-async-pt4/ReliablyTestingAsync/ReliablyTestingAsync.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/0240-reliably-testing-async-pt3/redo/ReliablyTestingAsync/ReliablyTestingAsync.xcodeproj/xcshareddata/xcschemes/ReliablyTestingAsync.xcscheme b/0241-reliably-testing-async-pt4/ReliablyTestingAsync/ReliablyTestingAsync.xcodeproj/xcshareddata/xcschemes/ReliablyTestingAsync.xcscheme similarity index 100% rename from 0240-reliably-testing-async-pt3/redo/ReliablyTestingAsync/ReliablyTestingAsync.xcodeproj/xcshareddata/xcschemes/ReliablyTestingAsync.xcscheme rename to 0241-reliably-testing-async-pt4/ReliablyTestingAsync/ReliablyTestingAsync.xcodeproj/xcshareddata/xcschemes/ReliablyTestingAsync.xcscheme diff --git a/0240-reliably-testing-async-pt3/redo/ReliablyTestingAsync/ReliablyTestingAsync/App.swift b/0241-reliably-testing-async-pt4/ReliablyTestingAsync/ReliablyTestingAsync/App.swift similarity index 100% rename from 0240-reliably-testing-async-pt3/redo/ReliablyTestingAsync/ReliablyTestingAsync/App.swift rename to 0241-reliably-testing-async-pt4/ReliablyTestingAsync/ReliablyTestingAsync/App.swift diff --git a/0240-reliably-testing-async-pt3/redo/ReliablyTestingAsync/ReliablyTestingAsync/Assets.xcassets/AccentColor.colorset/Contents.json b/0241-reliably-testing-async-pt4/ReliablyTestingAsync/ReliablyTestingAsync/Assets.xcassets/AccentColor.colorset/Contents.json similarity index 100% rename from 0240-reliably-testing-async-pt3/redo/ReliablyTestingAsync/ReliablyTestingAsync/Assets.xcassets/AccentColor.colorset/Contents.json rename to 0241-reliably-testing-async-pt4/ReliablyTestingAsync/ReliablyTestingAsync/Assets.xcassets/AccentColor.colorset/Contents.json diff --git a/0240-reliably-testing-async-pt3/redo/ReliablyTestingAsync/ReliablyTestingAsync/Assets.xcassets/AppIcon.appiconset/Contents.json b/0241-reliably-testing-async-pt4/ReliablyTestingAsync/ReliablyTestingAsync/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from 0240-reliably-testing-async-pt3/redo/ReliablyTestingAsync/ReliablyTestingAsync/Assets.xcassets/AppIcon.appiconset/Contents.json rename to 0241-reliably-testing-async-pt4/ReliablyTestingAsync/ReliablyTestingAsync/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/0240-reliably-testing-async-pt3/redo/ReliablyTestingAsync/ReliablyTestingAsync/Assets.xcassets/Contents.json b/0241-reliably-testing-async-pt4/ReliablyTestingAsync/ReliablyTestingAsync/Assets.xcassets/Contents.json similarity index 100% rename from 0240-reliably-testing-async-pt3/redo/ReliablyTestingAsync/ReliablyTestingAsync/Assets.xcassets/Contents.json rename to 0241-reliably-testing-async-pt4/ReliablyTestingAsync/ReliablyTestingAsync/Assets.xcassets/Contents.json diff --git a/0240-reliably-testing-async-pt3/redo/ReliablyTestingAsync/ReliablyTestingAsync/ConcurrencyExtras.swift b/0241-reliably-testing-async-pt4/ReliablyTestingAsync/ReliablyTestingAsync/ConcurrencyExtras.swift similarity index 100% rename from 0240-reliably-testing-async-pt3/redo/ReliablyTestingAsync/ReliablyTestingAsync/ConcurrencyExtras.swift rename to 0241-reliably-testing-async-pt4/ReliablyTestingAsync/ReliablyTestingAsync/ConcurrencyExtras.swift diff --git a/0240-reliably-testing-async-pt3/redo/ReliablyTestingAsync/ReliablyTestingAsync/ContentView.swift b/0241-reliably-testing-async-pt4/ReliablyTestingAsync/ReliablyTestingAsync/ContentView.swift similarity index 100% rename from 0240-reliably-testing-async-pt3/redo/ReliablyTestingAsync/ReliablyTestingAsync/ContentView.swift rename to 0241-reliably-testing-async-pt4/ReliablyTestingAsync/ReliablyTestingAsync/ContentView.swift diff --git a/0240-reliably-testing-async-pt3/redo/ReliablyTestingAsync/ReliablyTestingAsync/Countdown.swift b/0241-reliably-testing-async-pt4/ReliablyTestingAsync/ReliablyTestingAsync/Countdown.swift similarity index 97% rename from 0240-reliably-testing-async-pt3/redo/ReliablyTestingAsync/ReliablyTestingAsync/Countdown.swift rename to 0241-reliably-testing-async-pt4/ReliablyTestingAsync/ReliablyTestingAsync/Countdown.swift index e62c1781..2ca64e67 100644 --- a/0240-reliably-testing-async-pt3/redo/ReliablyTestingAsync/ReliablyTestingAsync/Countdown.swift +++ b/0241-reliably-testing-async-pt4/ReliablyTestingAsync/ReliablyTestingAsync/Countdown.swift @@ -114,6 +114,10 @@ private let pointFreeColors = [ struct CountdownDemo_Previews: PreviewProvider { static var previews: some View { + let _ = swift_task_enqueueGlobal_hook = { job, _ in + MainActor.shared.enqueue(job) + } + CountdownDemo(clock: .immediate) } } diff --git a/0240-reliably-testing-async-pt3/redo/ReliablyTestingAsync/ReliablyTestingAsync/Info.plist b/0241-reliably-testing-async-pt4/ReliablyTestingAsync/ReliablyTestingAsync/Info.plist similarity index 100% rename from 0240-reliably-testing-async-pt3/redo/ReliablyTestingAsync/ReliablyTestingAsync/Info.plist rename to 0241-reliably-testing-async-pt4/ReliablyTestingAsync/ReliablyTestingAsync/Info.plist diff --git a/0240-reliably-testing-async-pt3/redo/ReliablyTestingAsync/ReliablyTestingAsync/Preview Content/Preview Assets.xcassets/Contents.json b/0241-reliably-testing-async-pt4/ReliablyTestingAsync/ReliablyTestingAsync/Preview Content/Preview Assets.xcassets/Contents.json similarity index 100% rename from 0240-reliably-testing-async-pt3/redo/ReliablyTestingAsync/ReliablyTestingAsync/Preview Content/Preview Assets.xcassets/Contents.json rename to 0241-reliably-testing-async-pt4/ReliablyTestingAsync/ReliablyTestingAsync/Preview Content/Preview Assets.xcassets/Contents.json diff --git a/0240-reliably-testing-async-pt3/redo/ReliablyTestingAsync/ReliablyTestingAsyncTests/LockIsolated.swift b/0241-reliably-testing-async-pt4/ReliablyTestingAsync/ReliablyTestingAsyncTests/LockIsolated.swift similarity index 100% rename from 0240-reliably-testing-async-pt3/redo/ReliablyTestingAsync/ReliablyTestingAsyncTests/LockIsolated.swift rename to 0241-reliably-testing-async-pt4/ReliablyTestingAsync/ReliablyTestingAsyncTests/LockIsolated.swift diff --git a/0240-reliably-testing-async-pt3/redo/ReliablyTestingAsync/ReliablyTestingAsyncTests/NumberFactModelTests.swift b/0241-reliably-testing-async-pt4/ReliablyTestingAsync/ReliablyTestingAsyncTests/NumberFactModelTests.swift similarity index 57% rename from 0240-reliably-testing-async-pt3/redo/ReliablyTestingAsync/ReliablyTestingAsyncTests/NumberFactModelTests.swift rename to 0241-reliably-testing-async-pt4/ReliablyTestingAsync/ReliablyTestingAsyncTests/NumberFactModelTests.swift index 876a94d5..e3b32d15 100644 --- a/0240-reliably-testing-async-pt3/redo/ReliablyTestingAsync/ReliablyTestingAsyncTests/NumberFactModelTests.swift +++ b/0241-reliably-testing-async-pt4/ReliablyTestingAsync/ReliablyTestingAsyncTests/NumberFactModelTests.swift @@ -48,6 +48,25 @@ final class NumberFactModelTests: XCTestCase { XCTAssertEqual(model.fact, "0 is a good number.") } + func testFactClearsOut_MainSerialExecutor() async { + swift_task_enqueueGlobal_hook = { job, _ in + MainActor.shared.enqueue(job) + } + + let model = withDependencies { + $0.numberFact.fact = { "\($0) is a good number." } + } operation: { + NumberFactModel() + } + model.fact = "An old fact about 0." + + let task = Task { await model.getFactButtonTapped() } + await Task.yield() + XCTAssertEqual(model.fact, nil) + await task.value + XCTAssertEqual(model.fact, "0 is a good number.") + } + func testFactIsLoading() async { let fact = AsyncStream.makeStream(of: String.self) @@ -69,6 +88,26 @@ final class NumberFactModelTests: XCTestCase { XCTAssertEqual(model.isLoading, false) } + func testFactIsLoading_MainSerialExecutor() async { + swift_task_enqueueGlobal_hook = { job, _ in + MainActor.shared.enqueue(job) + } + + let model = withDependencies { + $0.numberFact.fact = { "\($0) is a good number." } + } operation: { + NumberFactModel() + } + model.fact = "An old fact about 0." + + let task = Task { await model.getFactButtonTapped() } + await Task.yield() + XCTAssertEqual(model.isLoading, true) + await task.value + XCTAssertEqual(model.fact, "0 is a good number.") + XCTAssertEqual(model.isLoading, false) + } + func testBackToBackGetFact() async throws { let fact0 = AsyncStream.makeStream(of: String.self) let fact1 = AsyncStream.makeStream(of: String.self) @@ -99,7 +138,38 @@ final class NumberFactModelTests: XCTestCase { await task1.value XCTAssertEqual(model.fact, "0 is a great number.") } - + + func testBackToBackGetFact_MainSerialExecutor() async throws { + swift_task_enqueueGlobal_hook = { job, _ in + MainActor.shared.enqueue(job) + } + + let callCount = LockIsolated(0) + + let model = withDependencies { + $0.numberFact.fact = { number in + callCount.withValue { $0 += 1 } + if callCount.value == 1 { + return "0 is a better number." + } else if callCount.value == 2 { + return "0 is a great number." + } else { + fatalError() + } + } + } operation: { + NumberFactModel() + } + + let task0 = Task { await model.getFactButtonTapped() } + let task1 = Task { await model.getFactButtonTapped() } + await Task.yield() + await task0.value + await task1.value + XCTAssertEqual(model.fact, "0 is a great number.") + } + + func testCancel() async { let model = withDependencies { $0.numberFact.fact = { _ in try await Task.never() } @@ -112,25 +182,65 @@ final class NumberFactModelTests: XCTestCase { await task.value XCTAssertEqual(model.fact, nil) } - + + func testCancel_MainSerialExecutor() async { + swift_task_enqueueGlobal_hook = { job, _ in + MainActor.shared.enqueue(job) + } + + let model = withDependencies { + $0.numberFact.fact = { + await Task.yield() + try Task.checkCancellation() + return "\($0) is a good number." + } + } operation: { + NumberFactModel() + } + let task = Task { await model.getFactButtonTapped() } + await Task.yield() + model.cancelButtonTapped() + await task.value + XCTAssertEqual(model.fact, nil) + } + + func testScreenshots() async { let model = NumberFactModel() - + let task = Task { await model.onTask() } - + await Task.megaYield() NotificationCenter.default.post(name: UIApplication.userDidTakeScreenshotNotification, object: nil) while model.count != 1 { await Task.yield() } XCTAssertEqual(model.count, 1) - + NotificationCenter.default.post(name: UIApplication.userDidTakeScreenshotNotification, object: nil) while model.count != 2 { await Task.yield() } XCTAssertEqual(model.count, 2) } + + func testScreenshots_MainSerialExecutor() async { + swift_task_enqueueGlobal_hook = { job, _ in + MainActor.shared.enqueue(job) + } + + let model = NumberFactModel() + + let task = Task { await model.onTask() } + await Task.yield() + NotificationCenter.default.post(name: UIApplication.userDidTakeScreenshotNotification, object: nil) + await Task.yield() + XCTAssertEqual(model.count, 1) + + NotificationCenter.default.post(name: UIApplication.userDidTakeScreenshotNotification, object: nil) + await Task.yield() + XCTAssertEqual(model.count, 2) + } } extension Task where Success == Never, Failure == Never { diff --git a/0240-reliably-testing-async-pt3/redo/ReliablyTestingAsync/ReliablyTestingAsyncTests/ReliablyTestingAsync.xctestplan b/0241-reliably-testing-async-pt4/ReliablyTestingAsync/ReliablyTestingAsyncTests/ReliablyTestingAsync.xctestplan similarity index 100% rename from 0240-reliably-testing-async-pt3/redo/ReliablyTestingAsync/ReliablyTestingAsyncTests/ReliablyTestingAsync.xctestplan rename to 0241-reliably-testing-async-pt4/ReliablyTestingAsync/ReliablyTestingAsyncTests/ReliablyTestingAsync.xctestplan diff --git a/0240-reliably-testing-async-pt3/redo/ReliablyTestingAsync/ReliablyTestingAsyncTests/ReliablyTestingAsyncTests.swift b/0241-reliably-testing-async-pt4/ReliablyTestingAsync/ReliablyTestingAsyncTests/ReliablyTestingAsyncTests.swift similarity index 83% rename from 0240-reliably-testing-async-pt3/redo/ReliablyTestingAsync/ReliablyTestingAsyncTests/ReliablyTestingAsyncTests.swift rename to 0241-reliably-testing-async-pt4/ReliablyTestingAsync/ReliablyTestingAsyncTests/ReliablyTestingAsyncTests.swift index 9b1ef76e..44907965 100644 --- a/0240-reliably-testing-async-pt3/redo/ReliablyTestingAsync/ReliablyTestingAsyncTests/ReliablyTestingAsyncTests.swift +++ b/0241-reliably-testing-async-pt4/ReliablyTestingAsync/ReliablyTestingAsyncTests/ReliablyTestingAsyncTests.swift @@ -14,8 +14,10 @@ final class ReliablyTestingAsyncTests: XCTestCase { let values = LockIsolated([Int]()) let task = Task { values.withValue { $0.append(1) } + print(#line, { Thread.current }()) } values.withValue { $0.append(2) } + print(#line, { Thread.current }()) await task.value XCTAssertEqual(values.value, [2, 1]) } @@ -38,9 +40,11 @@ final class ReliablyTestingAsyncTests: XCTestCase { let values = LockIsolated([Int]()) let task1 = Task { values.withValue { $0.append(1) } + print({ Thread.current }()) } let task2 = Task { values.withValue { $0.append(2) } + print({ Thread.current }()) } _ = await (task1.value, task2.value) XCTAssertEqual(values.value, [1, 2]) @@ -98,7 +102,11 @@ final class ReliablyTestingAsyncTests: XCTestCase { } for task in tasks { await task.value } - XCTAssertEqual(values.value, [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21]) + XCTAssertEqual( + values.value, + Array(0...count).map { $0 * 2 } // evens less than or equal to max + + Array(0...count).map { $0 * 2 + 1 } // odds less than or equal to max + ) } func testYieldScheduling_MainSerialExecutor() async { @@ -117,28 +125,36 @@ final class ReliablyTestingAsyncTests: XCTestCase { } for task in tasks { await task.value } - XCTAssertEqual(values.value, [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21]) + XCTAssertEqual( + values.value, + Array(0...count).map { $0 * 2 } // evens less than or equal to max + + Array(0...count).map { $0 * 2 + 1 } // odds less than or equal to max + ) } - /* + + [task0, task1, task2] + [{task0}, task1, task2] [0] - [task2, task0, task1] + [{task1}, task2, task0] [0, 2] - [task0, task1, task2] + [{task2}, task0, task1] [0, 2, 4] - [task1, task2] + [{task0}, task1, task2] [0, 2, 4, 1] - [task2] + [{task1}, task2] [0, 2, 4, 1, 3] - [] + [{task2}] [0, 2, 4, 1, 3, 5] + [] + [0, 2, 4, 1, 3, 5] */ @MainActor diff --git a/README.md b/README.md index 2f9ff56c..2a0a2a9f 100644 --- a/README.md +++ b/README.md @@ -242,3 +242,4 @@ This repository is the home of code written on episodes of [Point-Free](https:// 1. [Reliable Async Tests: The Problem](0238-reliably-testing-async-pt1) 1. [Reliable Async Tests: More Problems](0239-reliably-testing-async-pt2) 1. [Reliable Async Tests: 😳](0240-reliably-testing-async-pt3) +1. [Reliable Async Tests: 🥹](0241-reliably-testing-async-pt4)