From 39b52692029dc19af51dbe0a99e778ad52023d53 Mon Sep 17 00:00:00 2001 From: James Hall Date: Tue, 5 May 2020 10:15:37 -0500 Subject: [PATCH] Routable inputs include finalDestination and pathNode (#13) Signed-off-by: Quickthyme --- Sources/QRoute/Driving/QRouteDriving.swift | 1 - .../QRoute/Driving/QRouteWalkingDriver.swift | 26 ++++++--- .../Driving/QRouteWalkingDriverStep.swift | 13 ++++- Sources/QRoute/QRoutable.swift | 1 - Sources/QRoute/QRoute+ConvenienceInit.swift | 1 - Sources/QRoute/QRoute.swift | 1 - Sources/QRoute/QRoutePath.swift | 1 - Sources/QRoute/Resolving/QRouteResolver.swift | 1 - .../Resolving/QRouteResolverAction.swift | 1 - .../QRoute/Resolving/QRouteResolving.swift | 9 +++- .../Driving/QRouteWalkingDriverTests.swift | 53 +++++++++++++++---- .../Resolving/QRouteResolverActionTests.swift | 1 - .../QRouteResolverMergeInputTests.swift | 1 - .../Resolving/QRouteResolverTests.swift | 1 - .../QRouteTests/helpers/BDDTestHelpers.swift | 1 - .../helpers/QRouteResolverTestHelpers.swift | 1 - Tests/QRouteTests/mocks/MockQRoutePlan.swift | 1 - .../mocks/StubQResolverActions.swift | 1 - 18 files changed, 80 insertions(+), 35 deletions(-) diff --git a/Sources/QRoute/Driving/QRouteDriving.swift b/Sources/QRoute/Driving/QRouteDriving.swift index e76e32a..3a927e1 100644 --- a/Sources/QRoute/Driving/QRouteDriving.swift +++ b/Sources/QRoute/Driving/QRouteDriving.swift @@ -1,4 +1,3 @@ - public protocol QRouteDriving: class { func driveParent(from source: QRoutable, diff --git a/Sources/QRoute/Driving/QRouteWalkingDriver.swift b/Sources/QRoute/Driving/QRouteWalkingDriver.swift index 9f4ef38..d4c015b 100644 --- a/Sources/QRoute/Driving/QRouteWalkingDriver.swift +++ b/Sources/QRoute/Driving/QRouteWalkingDriver.swift @@ -8,12 +8,14 @@ public final class QRouteWalkingDriver: QRouteDriving { public func driveSub(_ targetId: QRouteId, from source: QRoutable, input: QRouteResolving.Input?, animated:Bool, completion: QRouteResolving.Completion?) { guard let clonePath = buildClonePath(to: targetId, from: source) else { completion?(nil); return } - type(of: self).nextStep(clonePath, source, input ?? [:], animated, completion) + let finalDestination = clonePath.last?.route.id + type(of: self).nextStep(clonePath, source, finalDestination, input ?? [:], animated, completion) } public func driveTo(_ targetId: QRouteId, from source: QRoutable, input: QRouteResolving.Input?, animated:Bool, completion: QRouteResolving.Completion?) { let path = source.routeResolver.route.findPath(to: targetId) - type(of: self).nextStep(path, source, input ?? [:], animated, completion) + let finalDestination = path.last?.route.id + type(of: self).nextStep(path, source, finalDestination, input ?? [:], animated, completion) } } @@ -25,20 +27,28 @@ fileprivate extension QRouteWalkingDriver { return [.DOWN( QRoute(deepClone: target).applyParent(sourceRoute) )] } - static func nextStep(_ path: QRoutePath, _ routable: QRoutable?, _ input: QRouteResolving.Input, + static func nextStep(_ path: QRoutePath, + _ routable: QRoutable?, + _ finalDestination: QRouteId!, + _ input: QRouteResolving.Input, _ animated:Bool, _ finalCompletion: QRouteResolving.Completion?) { - guard let nextRoutable = routable, (path.count > 0) + guard + let nextRoutable = routable, + (!path.isEmpty) else { finalCompletion?(routable); return } - let nextCompletion = nextStepCompletion(path, input, animated, finalCompletion) - QRouteWalkingDriverStep.perform(nextRoutable, path[0], input, animated, nextCompletion) + + let nextCompletion = nextStepCompletion(path, finalDestination, input, animated, finalCompletion) + QRouteWalkingDriverStep.perform(nextRoutable, path[0], finalDestination, input, animated, nextCompletion) } - static func nextStepCompletion(_ path: QRoutePath, _ input: QRouteResolving.Input, + static func nextStepCompletion(_ path: QRoutePath, + _ finalDestination: QRouteId, + _ input: QRouteResolving.Input, _ animated:Bool, _ finalCompletion: QRouteResolving.Completion?) -> QRouteResolving.Completion { return { - QRouteWalkingDriver.nextStep(QRoutePath( path.dropFirst() ), $0, input, animated, finalCompletion) + QRouteWalkingDriver.nextStep(QRoutePath(path.dropFirst()), $0, finalDestination, input, animated, finalCompletion) } } } diff --git a/Sources/QRoute/Driving/QRouteWalkingDriverStep.swift b/Sources/QRoute/Driving/QRouteWalkingDriverStep.swift index 616b85a..88c3036 100644 --- a/Sources/QRoute/Driving/QRouteWalkingDriverStep.swift +++ b/Sources/QRoute/Driving/QRouteWalkingDriverStep.swift @@ -1,12 +1,21 @@ - import Dispatch internal final class QRouteWalkingDriverStep { - static func perform(_ routable: QRoutable, _ pathNode: QRoutePathNode, _ input: QRouteResolving.Input, + static func perform(_ routable: QRoutable, + _ pathNode: QRoutePathNode, + _ finalDestination: QRouteId, + _ input: QRouteResolving.Input, _ animated: Bool, _ stepCompletion: @escaping QRouteResolving.Completion) { + let input = input.merging( + [ + QRouteResolvingInputKey.finalDestination: finalDestination, + QRouteResolvingInputKey.pathNode: pathNode + ], + uniquingKeysWith: { (_, new) in new }) + switch (pathNode) { case let .DOWN(nextRoute): diff --git a/Sources/QRoute/QRoutable.swift b/Sources/QRoute/QRoutable.swift index 5c90a52..28ba714 100644 --- a/Sources/QRoute/QRoutable.swift +++ b/Sources/QRoute/QRoutable.swift @@ -1,4 +1,3 @@ - public protocol QRoutable: class { var routeResolver: QRouteResolving! { get } } diff --git a/Sources/QRoute/QRoute+ConvenienceInit.swift b/Sources/QRoute/QRoute+ConvenienceInit.swift index 8905587..8284773 100644 --- a/Sources/QRoute/QRoute+ConvenienceInit.swift +++ b/Sources/QRoute/QRoute+ConvenienceInit.swift @@ -1,4 +1,3 @@ - public extension QRoute { convenience init(_ id:QRouteId, dependencies: [String], _ routes: QRoute...) { self.init(id: id, dependencies: dependencies, routes: routes) diff --git a/Sources/QRoute/QRoute.swift b/Sources/QRoute/QRoute.swift index f30024f..f807cb9 100644 --- a/Sources/QRoute/QRoute.swift +++ b/Sources/QRoute/QRoute.swift @@ -1,4 +1,3 @@ - public typealias QRouteId = String public class QRoute: Hashable, CustomDebugStringConvertible { diff --git a/Sources/QRoute/QRoutePath.swift b/Sources/QRoute/QRoutePath.swift index 47d7ff4..d4c07e8 100644 --- a/Sources/QRoute/QRoutePath.swift +++ b/Sources/QRoute/QRoutePath.swift @@ -1,4 +1,3 @@ - public typealias QRoutePath = Array public enum QRoutePathNode: Hashable { diff --git a/Sources/QRoute/Resolving/QRouteResolver.swift b/Sources/QRoute/Resolving/QRouteResolver.swift index 8b30074..3c47cf4 100644 --- a/Sources/QRoute/Resolving/QRouteResolver.swift +++ b/Sources/QRoute/Resolving/QRouteResolver.swift @@ -1,4 +1,3 @@ - public final class QRouteResolver: QRouteResolving { public init(_ route: QRoute, diff --git a/Sources/QRoute/Resolving/QRouteResolverAction.swift b/Sources/QRoute/Resolving/QRouteResolverAction.swift index 1f79f6a..84dcc0a 100644 --- a/Sources/QRoute/Resolving/QRouteResolverAction.swift +++ b/Sources/QRoute/Resolving/QRouteResolverAction.swift @@ -1,4 +1,3 @@ - public extension QRouteResolver { struct ActionType { diff --git a/Sources/QRoute/Resolving/QRouteResolving.swift b/Sources/QRoute/Resolving/QRouteResolving.swift index d9ce1da..213f1ad 100644 --- a/Sources/QRoute/Resolving/QRouteResolving.swift +++ b/Sources/QRoute/Resolving/QRouteResolving.swift @@ -1,4 +1,3 @@ - public protocol QRouteResolving: class { typealias Input = [String:Any] typealias Completion = (QRoutable?)->() @@ -16,6 +15,11 @@ public protocol QRouteResolving: class { static func mergeInputDependencies(resolver: QRouteResolving, input: Input) } +public struct QRouteResolvingInputKey { + public static let finalDestination = "QRouteResolvingInputKey.finalDestination" + public static let pathNode = "QRouteResolvingInputKey.pathNode" +} + public extension QRouteResolving { func resolveRouteToSelf(from: QRoutable, input: Input, animated: Bool, completion: @escaping Completion) { mergeInputDependencies(input: input) @@ -32,7 +36,8 @@ public extension QRouteResolving { } fileprivate func _mergeInputDependencies(_ resolver: QRouteResolving, _ newInput: QRouteResolving.Input) { - let dependencies = resolver.route.dependencies + let dependencies = [QRouteResolvingInputKey.finalDestination, + QRouteResolvingInputKey.pathNode] + resolver.route.dependencies let filteredInput = newInput.filter { key, val in dependencies.contains(key) } resolver.input = resolver.input.merging(filteredInput, uniquingKeysWith: { (_, new) in new }) } diff --git a/Tests/QRouteTests/Driving/QRouteWalkingDriverTests.swift b/Tests/QRouteTests/Driving/QRouteWalkingDriverTests.swift index 31c775c..46d0196 100644 --- a/Tests/QRouteTests/Driving/QRouteWalkingDriverTests.swift +++ b/Tests/QRouteTests/Driving/QRouteWalkingDriverTests.swift @@ -1,4 +1,3 @@ - import XCTest import QRoute @@ -60,12 +59,18 @@ class QRouteWalkingDriverTests: XCTestCase { finalResolver = $0?.routeResolver as? QRouteResolverMock expectComplete.fulfill() }) - wait(for: [expectComplete], timeout: 0.1) + wait(for: [expectComplete], timeout: 2.0) then("it should have routed to parent one times landing on 'first'") { XCTAssertEqual(finalResolver?.getTimesCalled("resolveRouteToChild()"), 0) XCTAssertEqual(finalResolver?.getTimesCalled("resolveRouteToSelf()"), 0) XCTAssertEqual(finalResolver?.getTimesCalled("resolveRouteToParent()"), 1) XCTAssertEqual(finalResolver?.routeTrail, [first]) + + with("finalDestination and pathNode inputs make it to the end") { + let finalInput = finalResolver?.getArgument("resolveRouteToParent()", "input") as? [String: Any] + XCTAssertEqual(finalInput?[QRouteResolvingInputKey.finalDestination] as? QRouteId, "first") + XCTAssertNotNil(finalInput?[QRouteResolvingInputKey.pathNode] as? QRoutePathNode) + } } } } @@ -87,12 +92,18 @@ class QRouteWalkingDriverTests: XCTestCase { finalResolver = $0?.routeResolver as? QRouteResolverMock expectComplete.fulfill() }) - wait(for: [expectComplete], timeout: 0.1) + wait(for: [expectComplete], timeout: 2.0) then("it should have routed to child one times landing on third") { XCTAssertEqual(finalResolver?.getTimesCalled("resolveRouteToChild()"), 1) XCTAssertEqual(finalResolver?.getTimesCalled("resolveRouteToSelf()"), 0) XCTAssertEqual(finalResolver?.getTimesCalled("resolveRouteToParent()"), 0) XCTAssertEqual(finalResolver?.routeTrail, [third]) + + with("finalDestination and pathNode inputs make it to the end") { + let finalInput = finalResolver?.getArgument("resolveRouteToChild()", "input") as? [String: Any] + XCTAssertEqual(finalInput?[QRouteResolvingInputKey.finalDestination] as? QRouteId, "third") + XCTAssertNotNil(finalInput?[QRouteResolvingInputKey.pathNode] as? QRoutePathNode) + } } } } @@ -115,12 +126,18 @@ class QRouteWalkingDriverTests: XCTestCase { finalResolver = $0?.routeResolver as? QRouteResolverMock expectComplete.fulfill() }) - wait(for: [expectComplete], timeout: 0.1) + wait(for: [expectComplete], timeout: 2.0) then("it should have routed to self one times") { XCTAssertEqual(finalResolver?.getTimesCalled("resolveRouteToChild()"), 0) XCTAssertEqual(finalResolver?.getTimesCalled("resolveRouteToSelf()"), 1) XCTAssertEqual(finalResolver?.getTimesCalled("resolveRouteToParent()"), 0) XCTAssertEqual(finalResolver?.routeTrail, [second]) + + with("finalDestination and pathNode inputs make it to the end") { + let finalInput = finalResolver?.getArgument("resolveRouteToSelf()", "input") as? [String: Any] + XCTAssertEqual(finalInput?[QRouteResolvingInputKey.finalDestination] as? QRouteId, "second") + XCTAssertNotNil(finalInput?[QRouteResolvingInputKey.pathNode] as? QRoutePathNode) + } } } } @@ -142,13 +159,19 @@ class QRouteWalkingDriverTests: XCTestCase { finalResolver = $0?.routeResolver as? QRouteResolverMock expectComplete.fulfill() }) - wait(for: [expectComplete], timeout: 0.1) + wait(for: [expectComplete], timeout: 2.0) then("it should have routed to child two times landing on 'third'") { XCTAssertEqual(finalResolver?.getTimesCalled("resolveRouteToChild()"), 2) XCTAssertEqual(finalResolver?.getTimesCalled("resolveRouteToSelf()"), 0) XCTAssertEqual(finalResolver?.getTimesCalled("resolveRouteToParent()"), 0) XCTAssertEqual(finalResolver?.routeTrail, [second, third]) XCTAssertEqual(finalResolver?.route, third) + + with("finalDestination and pathNode inputs make it to the end") { + let finalInput = finalResolver?.getArgument("resolveRouteToChild()", "input") as? [String: Any] + XCTAssertEqual(finalInput?[QRouteResolvingInputKey.finalDestination] as? QRouteId, "third") + XCTAssertNotNil(finalInput?[QRouteResolvingInputKey.pathNode] as? QRoutePathNode) + } } } } @@ -170,12 +193,18 @@ class QRouteWalkingDriverTests: XCTestCase { finalResolver = $0?.routeResolver as? QRouteResolverMock expectComplete.fulfill() }) - wait(for: [expectComplete], timeout: 0.1) + wait(for: [expectComplete], timeout: 2.0) then("it should have routed to parent two times landing on 'first'") { XCTAssertEqual(finalResolver?.getTimesCalled("resolveRouteToChild()"), 0) XCTAssertEqual(finalResolver?.getTimesCalled("resolveRouteToSelf()"), 0) XCTAssertEqual(finalResolver?.getTimesCalled("resolveRouteToParent()"), 2) XCTAssertEqual(finalResolver?.routeTrail, [second, first]) + + with("finalDestination and pathNode inputs make it to the end") { + let finalInput = finalResolver?.getArgument("resolveRouteToParent()", "input") as? [String: Any] + XCTAssertEqual(finalInput?[QRouteResolvingInputKey.finalDestination] as? QRouteId, "first") + XCTAssertNotNil(finalInput?[QRouteResolvingInputKey.pathNode] as? QRoutePathNode) + } } } } @@ -202,12 +231,18 @@ class QRouteWalkingDriverTests: XCTestCase { finalResolver = $0?.routeResolver as? QRouteResolverMock expectComplete.fulfill() }) - wait(for: [expectComplete], timeout: 0.1) + wait(for: [expectComplete], timeout: 2.0) then("it should have routed to parent 3 times and child 2 times landing at 'ZachTwo'") { XCTAssertEqual(finalResolver?.getTimesCalled("resolveRouteToChild()"), 2) XCTAssertEqual(finalResolver?.getTimesCalled("resolveRouteToSelf()"), 0) XCTAssertEqual(finalResolver?.getTimesCalled("resolveRouteToParent()"), 3) XCTAssertEqual(finalResolver?.routeTrail, [bravoOne, bravo, root, help, zachTwo]) + + with("finalDestination and pathNode inputs make it to the end") { + let finalInput = finalResolver?.getArgument("resolveRouteToChild()", "input") as? [String: Any] + XCTAssertEqual(finalInput?[QRouteResolvingInputKey.finalDestination] as? QRouteId, "ZachTwo") + XCTAssertNotNil(finalInput?[QRouteResolvingInputKey.pathNode] as? QRoutePathNode) + } } } } @@ -234,7 +269,7 @@ class QRouteWalkingDriverTests: XCTestCase { finalResolver = $0?.routeResolver as? QRouteResolverMock expectComplete.fulfill() }) - wait(for: [expectComplete], timeout: 0.1) + wait(for: [expectComplete], timeout: 2.0) then("it should have routed to child 1 times landing at 'ZachTwo' clone") { let zachTwoClone = finalResolver!.route XCTAssertEqual(finalResolver?.routeTrail, [zachTwoClone]) @@ -270,7 +305,7 @@ class QRouteWalkingDriverTests: XCTestCase { backResult = $0; expectComplete.fulfill(); } ) - wait(for: [expectComplete], timeout: 0.1) + wait(for: [expectComplete], timeout: 2.0) then("it should arrive back at 'BravoOne'") { XCTAssertEqual(backResult?.routeResolver.route, bravoOne) } diff --git a/Tests/QRouteTests/Resolving/QRouteResolverActionTests.swift b/Tests/QRouteTests/Resolving/QRouteResolverActionTests.swift index 365a603..086c095 100644 --- a/Tests/QRouteTests/Resolving/QRouteResolverActionTests.swift +++ b/Tests/QRouteTests/Resolving/QRouteResolverActionTests.swift @@ -1,4 +1,3 @@ - import XCTest import QRoute diff --git a/Tests/QRouteTests/Resolving/QRouteResolverMergeInputTests.swift b/Tests/QRouteTests/Resolving/QRouteResolverMergeInputTests.swift index 71047b6..e7bf275 100644 --- a/Tests/QRouteTests/Resolving/QRouteResolverMergeInputTests.swift +++ b/Tests/QRouteTests/Resolving/QRouteResolverMergeInputTests.swift @@ -1,4 +1,3 @@ - import XCTest import QRoute diff --git a/Tests/QRouteTests/Resolving/QRouteResolverTests.swift b/Tests/QRouteTests/Resolving/QRouteResolverTests.swift index 7b662d9..a027158 100644 --- a/Tests/QRouteTests/Resolving/QRouteResolverTests.swift +++ b/Tests/QRouteTests/Resolving/QRouteResolverTests.swift @@ -1,4 +1,3 @@ - import XCTest import QRoute diff --git a/Tests/QRouteTests/helpers/BDDTestHelpers.swift b/Tests/QRouteTests/helpers/BDDTestHelpers.swift index ca404ed..de097c7 100644 --- a/Tests/QRouteTests/helpers/BDDTestHelpers.swift +++ b/Tests/QRouteTests/helpers/BDDTestHelpers.swift @@ -1,4 +1,3 @@ - import XCTest extension XCTestCase { diff --git a/Tests/QRouteTests/helpers/QRouteResolverTestHelpers.swift b/Tests/QRouteTests/helpers/QRouteResolverTestHelpers.swift index 24c1583..7a03a88 100644 --- a/Tests/QRouteTests/helpers/QRouteResolverTestHelpers.swift +++ b/Tests/QRouteTests/helpers/QRouteResolverTestHelpers.swift @@ -1,4 +1,3 @@ - import XCTest import QRoute diff --git a/Tests/QRouteTests/mocks/MockQRoutePlan.swift b/Tests/QRouteTests/mocks/MockQRoutePlan.swift index e591b30..2827c40 100644 --- a/Tests/QRouteTests/mocks/MockQRoutePlan.swift +++ b/Tests/QRouteTests/mocks/MockQRoutePlan.swift @@ -1,4 +1,3 @@ - import QRoute public func MockQRoutePlan() -> QRoute { diff --git a/Tests/QRouteTests/mocks/StubQResolverActions.swift b/Tests/QRouteTests/mocks/StubQResolverActions.swift index 4e0a050..723a9d7 100644 --- a/Tests/QRouteTests/mocks/StubQResolverActions.swift +++ b/Tests/QRouteTests/mocks/StubQResolverActions.swift @@ -1,4 +1,3 @@ - import QRoute public class StubQResolverActions {