From 306782aa8ade8dcafa191fd89bcad43c8982fcdf Mon Sep 17 00:00:00 2001 From: Zachary Waldowski Date: Sat, 6 Oct 2018 17:07:08 -0400 Subject: [PATCH] Split out and improve testing for the basics of progress composition --- Deferred.xcodeproj/project.pbxproj | 8 ++ Tests/TaskTests/TaskProgressTests.swift | 164 ++++++++++++++++++++++++ Tests/TaskTests/TaskTests.swift | 115 +---------------- 3 files changed, 173 insertions(+), 114 deletions(-) create mode 100644 Tests/TaskTests/TaskProgressTests.swift diff --git a/Deferred.xcodeproj/project.pbxproj b/Deferred.xcodeproj/project.pbxproj index fa77fdd7..99c7a630 100644 --- a/Deferred.xcodeproj/project.pbxproj +++ b/Deferred.xcodeproj/project.pbxproj @@ -214,6 +214,9 @@ DBABD0BC203F2E3E00C50896 /* Atomics.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBABD0BA203F2E3E00C50896 /* Atomics.swift */; }; DBABD0BD203F2E3E00C50896 /* Atomics.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBABD0BA203F2E3E00C50896 /* Atomics.swift */; }; DBABD0BE203F2E3E00C50896 /* Atomics.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBABD0BA203F2E3E00C50896 /* Atomics.swift */; }; + DBEC962B216FF229004CF0FC /* TaskProgressTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBEC962A216FF229004CF0FC /* TaskProgressTests.swift */; }; + DBEC962C216FF229004CF0FC /* TaskProgressTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBEC962A216FF229004CF0FC /* TaskProgressTests.swift */; }; + DBEC962D216FF229004CF0FC /* TaskProgressTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBEC962A216FF229004CF0FC /* TaskProgressTests.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -311,6 +314,7 @@ DBA01B0C2071E6FF00083CD0 /* FuturePeek.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FuturePeek.swift; sourceTree = ""; }; DBABD0BA203F2E3E00C50896 /* Atomics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Atomics.swift; sourceTree = ""; }; DBC742631DC2F6D4002FB30D /* FutureEveryMap.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FutureEveryMap.swift; sourceTree = ""; }; + DBEC962A216FF229004CF0FC /* TaskProgressTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskProgressTests.swift; sourceTree = ""; }; EBEB828C1DC4A79A00B7E089 /* TaskComprehensiveTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TaskComprehensiveTests.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -483,6 +487,7 @@ DB55F1F71D96968E00FC1439 /* ResultRecoveryTests.swift */, DB55F1FD1D96968E00FC1439 /* TaskAsyncTests.swift */, EBEB828C1DC4A79A00B7E089 /* TaskComprehensiveTests.swift */, + DBEC962A216FF229004CF0FC /* TaskProgressTests.swift */, DB78F5EB215C4C5700D07CC6 /* TaskProtocolTests.swift */, DB55F1F81D96968E00FC1439 /* TaskResultTests.swift */, DB55F1FC1D96968E00FC1439 /* TaskTests.swift */, @@ -804,6 +809,7 @@ DB126D641E5368B900054E95 /* FutureTests.swift in Sources */, DB126D661E5368B900054E95 /* ProtectedTests.swift in Sources */, DB126D6E1E5368B900054E95 /* TaskAsyncTests.swift in Sources */, + DBEC962B216FF229004CF0FC /* TaskProgressTests.swift in Sources */, DB34FC942096DCE1005D5B82 /* FilledDeferredTests.swift in Sources */, DB126D6D1E5368B900054E95 /* TaskTests.swift in Sources */, DB126D621E5368B900054E95 /* FutureCustomExecutorTests.swift in Sources */, @@ -872,6 +878,7 @@ DB126D741E5368B900054E95 /* FutureTests.swift in Sources */, DB126D761E5368B900054E95 /* ProtectedTests.swift in Sources */, DB126D7E1E5368B900054E95 /* TaskAsyncTests.swift in Sources */, + DBEC962C216FF229004CF0FC /* TaskProgressTests.swift in Sources */, DB34FC952096DCE1005D5B82 /* FilledDeferredTests.swift in Sources */, DB126D7D1E5368B900054E95 /* TaskTests.swift in Sources */, DB126D721E5368B900054E95 /* FutureCustomExecutorTests.swift in Sources */, @@ -940,6 +947,7 @@ DB126D841E5368BA00054E95 /* FutureTests.swift in Sources */, DB126D861E5368BA00054E95 /* ProtectedTests.swift in Sources */, DB126D8E1E5368BA00054E95 /* TaskAsyncTests.swift in Sources */, + DBEC962D216FF229004CF0FC /* TaskProgressTests.swift in Sources */, DB34FC962096DCE1005D5B82 /* FilledDeferredTests.swift in Sources */, DB126D8D1E5368BA00054E95 /* TaskTests.swift in Sources */, DB126D821E5368BA00054E95 /* FutureCustomExecutorTests.swift in Sources */, diff --git a/Tests/TaskTests/TaskProgressTests.swift b/Tests/TaskTests/TaskProgressTests.swift new file mode 100644 index 00000000..8e298eca --- /dev/null +++ b/Tests/TaskTests/TaskProgressTests.swift @@ -0,0 +1,164 @@ +// +// TaskProgressTests.swift +// DeferredTests +// +// Created by Zachary Waldowski on 10/11/18. +// Copyright © 2018 Big Nerd Ranch. Licensed under MIT. +// + +import XCTest + +#if SWIFT_PACKAGE +import Atomics +import Task +#else +import Deferred +import Deferred.Atomics +#endif + +#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) +class TaskProgressTests: CustomExecutorTestCase { + static let allTests: [(String, (TaskProgressTests) -> () throws -> Void)] = [ + ("testThatCancellationIsAppliedImmediatelyWhenMapping", testThatCancellationIsAppliedImmediatelyWhenMapping), + ("testThatTaskCreatedWithProgressReflectsThatProgress", testThatTaskCreatedWithProgressReflectsThatProgress), + ("testTaskCreatedUnfilledIs0PercentCompleted", testTaskCreatedUnfilledIs0PercentCompleted), + ("testTaskCreatedFilledIs100PercentCompleted", testTaskCreatedFilledIs100PercentCompleted), + ("testThatTaskCreatedUnfilledIsIndeterminate", testThatTaskCreatedUnfilledIsIndeterminate), + ("testThatTaskWrappingUnfilledIsIndeterminate", testThatTaskWrappingUnfilledIsIndeterminate), + ("testThatTaskCreatedFilledIsDeterminate", testThatTaskCreatedFilledIsDeterminate), + ("testThatMapProgressFinishesAlongsideBaseProgress", testThatMapProgressFinishesAlongsideBaseProgress), + ("testThatAndThenProgressFinishesAlongsideBaseProgress", testThatAndThenProgressFinishesAlongsideBaseProgress), + ("testThanMappedProgressTakesUpMajorityOfDerivedProgress", testThanMappedProgressTakesUpMajorityOfDerivedProgress) + ] + + func testThatCancellationIsAppliedImmediatelyWhenMapping() { + let beforeExpect = expectation(description: "original task cancelled") + let beforeTask = Task(.never) { + beforeExpect.fulfill() + } + + beforeTask.cancel() + XCTAssert(beforeTask.progress.isCancelled) + + let afterExpect = expectation(description: "filled with same error") + afterExpect.isInverted = true + + let afterTask = beforeTask.map(upon: executor) { (value) -> String in + afterExpect.fulfill() + return String(describing: value) + } + + XCTAssert(afterTask.progress.isCancelled) + + shortWait(for: [ beforeExpect, afterExpect ]) + assertExecutorNeverCalled() + } + + func testThatTaskCreatedWithProgressReflectsThatProgress() { + let key = ProgressUserInfoKey(rawValue: "Test") + + let progress = Progress(parent: nil, userInfo: nil) + progress.totalUnitCount = 10 + progress.setUserInfoObject(true, forKey: key) + progress.isCancellable = false + + let task = Task(.never, progress: progress) + + XCTAssertEqual(task.progress.fractionCompleted, 0, accuracy: 0.001) + XCTAssertEqual(progress.userInfo[key] as? Bool, true) + XCTAssert(task.progress.isCancellable) + + progress.completedUnitCount = 5 + XCTAssertEqual(task.progress.fractionCompleted, 0.5, accuracy: 0.001) + } + + func testTaskCreatedUnfilledIs0PercentCompleted() { + let incompleteTask = Task.never + XCTAssertEqual(incompleteTask.progress.fractionCompleted, 0) + } + + func testTaskCreatedFilledIs100PercentCompleted() { + let completedTask = Task(success: 42) + XCTAssertEqual(completedTask.progress.fractionCompleted, 1) + } + + func testThatTaskCreatedUnfilledIsIndeterminate() { + let task = Task.never + XCTAssert(task.progress.isIndeterminate) + } + + func testThatTaskWrappingUnfilledIsIndeterminate() { + let deferred = Task.Promise() + let wrappedTask = Task(deferred) + XCTAssertFalse(wrappedTask.progress.isIndeterminate) + } + + func testThatTaskCreatedFilledIsDeterminate() { + let completedTask = Task(success: 42) + XCTAssertFalse(completedTask.progress.isIndeterminate) + } + + func testThatMapProgressFinishesAlongsideBaseProgress() { + let deferred = Task.Promise() + let task1 = Task(deferred) + let task2 = task1.map(upon: queue) { $0 * 2 } + + XCTAssertNotEqual(task1.progress.fractionCompleted, 1) + XCTAssertNotEqual(task2.progress.fractionCompleted, 1) + + deferred.succeed(with: 9000) + + shortWait(for: [ + expectation(for: NSPredicate(format: "fractionCompleted == 1"), evaluatedWith: task1.progress), + expectation(for: NSPredicate(format: "fractionCompleted == 1"), evaluatedWith: task2.progress), + expectQueueToBeEmpty() + ]) + } + + func testThatAndThenProgressFinishesAlongsideBaseProgress() { + let deferred = Task.Promise() + let task1 = Task(deferred) + let task2 = task1.andThen(upon: executor) { (result) -> Task.Promise in + let deferred2 = Task.Promise() + self.afterShortDelay { + deferred2.succeed(with: result * 2) + } + return deferred2 + } + + XCTAssertNotEqual(task1.progress.fractionCompleted, 1) + XCTAssertNotEqual(task2.progress.fractionCompleted, 1) + + deferred.succeed(with: 9000) + + shortWait(for: [ + expectation(for: NSPredicate(format: "fractionCompleted == 1"), evaluatedWith: task1.progress), + expectation(for: NSPredicate(format: "fractionCompleted == 1"), evaluatedWith: task2.progress) + ]) + + assertExecutorCalled(atLeast: 1) + } + + func testThanMappedProgressTakesUpMajorityOfDerivedProgress() { + let customProgress = Progress(totalUnitCount: 5) + let deferred = Task.Promise() + let task = Task(deferred, progress: customProgress) + .map(upon: .any(), transform: { $0 * 2 }) + .map(upon: .any(), transform: { "\($0)" }) + .map(upon: .any(), transform: { "\($0)\($0)" }) + + XCTAssertNotEqual(customProgress.fractionCompleted, 1) + XCTAssertNotEqual(task.progress.fractionCompleted, 1) + + customProgress.completedUnitCount = 5 + + XCTAssertGreaterThanOrEqual(task.progress.fractionCompleted, 0.75) + + deferred.succeed(with: 9000) + + shortWait(for: [ + expectation(for: NSPredicate(format: "fractionCompleted == 1"), evaluatedWith: task.progress) + ]) + } +} +#endif diff --git a/Tests/TaskTests/TaskTests.swift b/Tests/TaskTests/TaskTests.swift index 7e5b06b2..6cd03836 100755 --- a/Tests/TaskTests/TaskTests.swift +++ b/Tests/TaskTests/TaskTests.swift @@ -17,11 +17,8 @@ import Deferred import Deferred.Atomics #endif -// swiftlint:disable file_length -// swiftlint:disable type_body_length - class TaskTests: CustomExecutorTestCase { - static let universalTests: [(String, (TaskTests) -> () throws -> Void)] = [ + static let allTests: [(String, (TaskTests) -> () throws -> Void)] = [ ("testUponSuccess", testUponSuccess), ("testUponFailure", testUponFailure), ("testThatThrowingMapSubstitutesWithError", testThatThrowingMapSubstitutesWithError), @@ -39,24 +36,6 @@ class TaskTests: CustomExecutorTestCase { ("testSimpleFutureCanBeUpgradedToTask", testSimpleFutureCanBeUpgradedToTask) ] - static var allTests: [(String, (TaskTests) -> () throws -> Void)] { - #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) - return universalTests + [ - ("testThatCancellationIsAppliedImmediatelyWhenMapping", testThatCancellationIsAppliedImmediatelyWhenMapping), - ("testThatTaskCreatedWithProgressReflectsThatProgress", testThatTaskCreatedWithProgressReflectsThatProgress), - ("testTaskCreatedUnfilledIs100PercentCompleted", testTaskCreatedUnfilledIs100PercentCompleted), - ("testTaskCreatedFilledIs100PercentCompleted", testTaskCreatedFilledIs100PercentCompleted), - ("testThatTaskCreatedUnfilledIsIndeterminate", testThatTaskCreatedUnfilledIsIndeterminate), - ("testThatTaskWrappingUnfilledIsIndeterminate", testThatTaskWrappingUnfilledIsIndeterminate), - ("testThatTaskWrappingFilledIsDeterminate", testThatTaskWrappingFilledIsDeterminate), - ("testThatMapIncrementsParentProgressFraction", testThatMapIncrementsParentProgressFraction), - ("testThatAndThenIncrementsParentProgressFraction", testThatAndThenIncrementsParentProgressFraction) - ] - #else - return universalTests - #endif - } - private func expectation(that task: Task, succeedsWith makeExpected: @autoclosure @escaping() -> T, description: String? = nil) -> XCTestExpectation { let expect = expectation(description: description ?? "uponSuccess is called") task.uponSuccess(on: executor) { (value) in @@ -187,98 +166,6 @@ class TaskTests: CustomExecutorTestCase { assertExecutorCalled(atLeast: 1) } - #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) - func testThatCancellationIsAppliedImmediatelyWhenMapping() { - let beforeExpect = expectation(description: "original task cancelled") - let beforeTask = Task(Deferred.Result>()) { - beforeExpect.fulfill() - } - - beforeTask.cancel() - XCTAssert(beforeTask.progress.isCancelled) - - let afterExpect = expectation(description: "filled with same error") - afterExpect.isInverted = true - - let afterTask = beforeTask.map(upon: executor) { (value) -> String in - afterExpect.fulfill() - return String(describing: value) - } - - XCTAssert(afterTask.progress.isCancelled) - - shortWait(for: [ beforeExpect, afterExpect ]) - assertExecutorNeverCalled() - } - - func testThatTaskCreatedWithProgressReflectsThatProgress() { - let key = ProgressUserInfoKey(rawValue: "Test") - - let progress = Progress(parent: nil, userInfo: nil) - progress.totalUnitCount = 10 - progress.setUserInfoObject(true, forKey: key) - progress.isCancellable = false - - let task = Task(Deferred.Result>(), progress: progress) - - XCTAssertEqual(task.progress.fractionCompleted, 0, accuracy: 0.001) - XCTAssertEqual(progress.userInfo[key] as? Bool, true) - XCTAssert(task.progress.isCancellable) - - progress.completedUnitCount = 5 - XCTAssertEqual(task.progress.fractionCompleted, 0.5, accuracy: 0.001) - } - - func testTaskCreatedUnfilledIs100PercentCompleted() { - XCTAssertEqual(makeAnyUnfinishedTask().1.progress.fractionCompleted, 0) - } - - func testTaskCreatedFilledIs100PercentCompleted() { - XCTAssertEqual(makeAnyFinishedTask().progress.fractionCompleted, 1) - } - - func testThatTaskCreatedUnfilledIsIndeterminate() { - let task = Task.never - - XCTAssert(task.progress.isIndeterminate) - } - - func testThatTaskWrappingUnfilledIsIndeterminate() { - let deferred = Deferred.Result>() - let wrappedTask = Task(deferred) - - XCTAssertFalse(wrappedTask.progress.isIndeterminate) - } - - func testThatTaskWrappingFilledIsDeterminate() { - let deferred = Deferred.Result>(filledWith: .success(42)) - let wrappedTask = Task(deferred) - - XCTAssertFalse(wrappedTask.progress.isIndeterminate) - } - - func testThatMapIncrementsParentProgressFraction() { - let task = makeAnyFinishedTask().map(upon: executor) { $0 * 2 } - - shortWait(for: [ - expectation(for: NSPredicate(format: "fractionCompleted == 1"), evaluatedWith: task.progress) - ]) - - assertExecutorCalled(atLeast: 1) - } - - func testThatAndThenIncrementsParentProgressFraction() { - let task = makeAnyFinishedTask().andThen(upon: executor, start: makeContrivedNextTask) - XCTAssertNotEqual(task.progress.fractionCompleted, 1) - - shortWait(for: [ - expectation(for: NSPredicate(format: "fractionCompleted == 1"), evaluatedWith: task.progress) - ]) - - assertExecutorCalled(atLeast: 1) - } - #endif - func testThatFallbackProducesANewTask() { let task = makeAnyFailedTask().fallback(upon: queue) { _ -> Task in return self.makeAnyFinishedTask()