From 3fc744715cc58e74613ab797551ce1115de0071a Mon Sep 17 00:00:00 2001 From: Lucas Nelaupe Date: Wed, 29 Jun 2022 13:32:38 +0800 Subject: [PATCH] Add test for network listener (#411) --- Sources/SwiftQueue/Constraint+Network.swift | 74 +++++++++++++------ .../ConstraintTest+Network.swift | 42 +++++++++++ 2 files changed, 94 insertions(+), 22 deletions(-) diff --git a/Sources/SwiftQueue/Constraint+Network.swift b/Sources/SwiftQueue/Constraint+Network.swift index f58b3ef..00bf176 100644 --- a/Sources/SwiftQueue/Constraint+Network.swift +++ b/Sources/SwiftQueue/Constraint+Network.swift @@ -33,37 +33,28 @@ public enum NetworkType: Int, Codable { case wifi = 2 } -internal final class NetworkConstraint: SimpleConstraint, CodableConstraint { +internal protocol NetworkMonitor { - /// Require a certain connectivity type - internal let networkType: NetworkType + func hasCorrectNetworkType(require: NetworkType) -> Bool - private var monitor: NWPathMonitor? + func startMonitoring(networkType: NetworkType, operation: SqOperation) - required init(networkType: NetworkType) { - assert(networkType != .any) - self.networkType = networkType - } +} - convenience init?(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: NetworkConstraintKey.self) - if container.contains(.requireNetwork) { - try self.init(networkType: container.decode(NetworkType.self, forKey: .requireNetwork)) - } else { return nil } - } +internal class NWPathMonitorNetworkMonitor: NetworkMonitor { - override func willSchedule(queue: SqOperationQueue, operation: SqOperation) throws { - assert(operation.dispatchQueue != .main) - self.monitor = NWPathMonitor() - } - - override func run(operation: SqOperation) -> Bool { - guard let monitor = monitor else { return true } + private let monitor = NWPathMonitor() + func hasCorrectNetworkType(require: NetworkType) -> Bool { if monitor.currentPath.status == .satisfied { + monitor.pathUpdateHandler = nil return true + } else { + return false } + } + func startMonitoring(networkType: NetworkType, operation: SqOperation) { monitor.pathUpdateHandler = { [monitor, operation, networkType] path in guard path.status == .satisfied else { operation.logger.log(.verbose, jobId: operation.name, message: "Unsatisfied network requirement") @@ -81,8 +72,47 @@ internal final class NetworkConstraint: SimpleConstraint, CodableConstraint { monitor.pathUpdateHandler = nil operation.run() } - monitor.start(queue: operation.dispatchQueue) + } + + +} + + +internal final class NetworkConstraint: SimpleConstraint, CodableConstraint { + + /// Require a certain connectivity type + internal let networkType: NetworkType + + private let monitor: NetworkMonitor + + required init(networkType: NetworkType, monitor: NetworkMonitor) { + assert(networkType != .any) + self.networkType = networkType + self.monitor = monitor + } + + convenience init(networkType: NetworkType) { + self.init(networkType: networkType, monitor: NWPathMonitorNetworkMonitor()) + } + + convenience init?(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: NetworkConstraintKey.self) + if container.contains(.requireNetwork) { + try self.init(networkType: container.decode(NetworkType.self, forKey: .requireNetwork)) + } else { return nil } + } + + override func willSchedule(queue: SqOperationQueue, operation: SqOperation) throws { + assert(operation.dispatchQueue != .main) + } + + override func run(operation: SqOperation) -> Bool { + if monitor.hasCorrectNetworkType(require: networkType) { + return true + } + + monitor.startMonitoring(networkType: networkType, operation: operation) return false } diff --git a/Tests/SwiftQueueTests/ConstraintTest+Network.swift b/Tests/SwiftQueueTests/ConstraintTest+Network.swift index bfd2882..65da33b 100644 --- a/Tests/SwiftQueueTests/ConstraintTest+Network.swift +++ b/Tests/SwiftQueueTests/ConstraintTest+Network.swift @@ -54,4 +54,46 @@ class ConstraintTestNetwork: XCTestCase { job.assertSingleCompletion() } + func testNetworkWaitUntilAvailable() { + let (type, job) = (UUID().uuidString, TestJob()) + + let creator = TestCreator([type: job]) + let semaphore = DispatchSemaphore(value: 0) + + let manager = SwiftQueueManagerBuilder(creator: creator).set(persister: NoPersister.shared).build() + JobBuilder(type: type) + .add(constraint: NetworkConstraint(networkType: .wifi, monitor: TestNetworkMonitor(semaphore: semaphore))) + .schedule(manager: manager) + + job.assertNoRun() + + semaphore.signal() + + job.awaitForRemoval() + job.assertSingleCompletion() + } + +} + +internal class TestNetworkMonitor: NetworkMonitor { + + private let semaphore: DispatchSemaphore + + private var hasNetworkChanged = false + + required init(semaphore: DispatchSemaphore) { + self.semaphore = semaphore + } + + func hasCorrectNetworkType(require: NetworkType) -> Bool { + hasNetworkChanged + } + + func startMonitoring(networkType: NetworkType, operation: SqOperation) { + operation.dispatchQueue.async { [weak self] in + self?.semaphore.wait() + self?.hasNetworkChanged = true + operation.run() + } + } }