From c0dbcfb6c20e59599b5cdc14f151430866322a7e Mon Sep 17 00:00:00 2001 From: Ross Butler Date: Sun, 20 Mar 2022 19:13:00 +0000 Subject: [PATCH] Refactored logic in order to add unit tests for new functionality --- .../project.pbxproj | 4 ++ Example/Pods/Pods.xcodeproj/project.pbxproj | 4 ++ ...gURLSessionConfigurationFactoryTests.swift | 39 +++++++++++++++++++ ...achingURLSessionConfigurationFactory.swift | 18 +++++++++ .../Classes/Core/Hyperconnectivity.swift | 9 ++--- 5 files changed, 68 insertions(+), 6 deletions(-) create mode 100644 Example/Tests/NonCachingURLSessionConfigurationFactoryTests.swift create mode 100644 Hyperconnectivity/Classes/Combine/NonCachingURLSessionConfigurationFactory.swift diff --git a/Example/Hyperconnectivity.xcodeproj/project.pbxproj b/Example/Hyperconnectivity.xcodeproj/project.pbxproj index 6ae7171..b48bd74 100644 --- a/Example/Hyperconnectivity.xcodeproj/project.pbxproj +++ b/Example/Hyperconnectivity.xcodeproj/project.pbxproj @@ -10,6 +10,7 @@ 0E0DB5B062AA610A7FB6B2F1 /* Pods_Hyperconnectivity_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5906C1669FAA2FD5D578F683 /* Pods_Hyperconnectivity_Tests.framework */; }; 134B21BE2466DD5500A8F332 /* ConnectivityStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 134B21BC2466DC7200A8F332 /* ConnectivityStateTests.swift */; }; 134B21C1246877F300A8F332 /* ConnectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 134B21BF2468779E00A8F332 /* ConnectionTests.swift */; }; + 134E807027E7AB2700C2A94A /* NonCachingURLSessionConfigurationFactoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 134E806F27E7AB2700C2A94A /* NonCachingURLSessionConfigurationFactoryTests.swift */; }; 13962F0A246F3F7300CCE8B9 /* ConnectivitySubscriptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13962F09246F3F7300CCE8B9 /* ConnectivitySubscriptionTests.swift */; }; 13962F12247191B000CCE8B9 /* MockPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13962F11247191B000CCE8B9 /* MockPath.swift */; }; 13962F142471929500CCE8B9 /* MockConnectivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13962F132471929500CCE8B9 /* MockConnectivity.swift */; }; @@ -46,6 +47,7 @@ /* Begin PBXFileReference section */ 134B21BC2466DC7200A8F332 /* ConnectivityStateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectivityStateTests.swift; sourceTree = ""; }; 134B21BF2468779E00A8F332 /* ConnectionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionTests.swift; sourceTree = ""; }; + 134E806F27E7AB2700C2A94A /* NonCachingURLSessionConfigurationFactoryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NonCachingURLSessionConfigurationFactoryTests.swift; sourceTree = ""; }; 13962F09246F3F7300CCE8B9 /* ConnectivitySubscriptionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectivitySubscriptionTests.swift; sourceTree = ""; }; 13962F11247191B000CCE8B9 /* MockPath.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockPath.swift; sourceTree = ""; }; 13962F132471929500CCE8B9 /* MockConnectivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockConnectivity.swift; sourceTree = ""; }; @@ -183,6 +185,7 @@ 13962F222471AA1600CCE8B9 /* ConnectivityPublisherTests.swift */, 13C3D88E2471C36800E0A879 /* ReachabilityPublisherTests.swift */, 607FACE91AFB9204008FA782 /* Supporting Files */, + 134E806F27E7AB2700C2A94A /* NonCachingURLSessionConfigurationFactoryTests.swift */, ); path = Tests; sourceTree = ""; @@ -428,6 +431,7 @@ 13962F12247191B000CCE8B9 /* MockPath.swift in Sources */, 13962F232471AA1600CCE8B9 /* ConnectivityPublisherTests.swift in Sources */, 13962F142471929500CCE8B9 /* MockConnectivity.swift in Sources */, + 134E807027E7AB2700C2A94A /* NonCachingURLSessionConfigurationFactoryTests.swift in Sources */, 13962F0A246F3F7300CCE8B9 /* ConnectivitySubscriptionTests.swift in Sources */, 13962F1A247196F800CCE8B9 /* ResponseContainsStringValidatorTests.swift in Sources */, 13962F18247196AE00CCE8B9 /* ResponseStringEqualityValidatorTests.swift in Sources */, diff --git a/Example/Pods/Pods.xcodeproj/project.pbxproj b/Example/Pods/Pods.xcodeproj/project.pbxproj index 3eb5d82..f5d5739 100644 --- a/Example/Pods/Pods.xcodeproj/project.pbxproj +++ b/Example/Pods/Pods.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 0FC42EFC33490211CA83E3740D4E51E6 /* Compatibility.h in Headers */ = {isa = PBXBuildFile; fileRef = AC17B72B64B618B416CCA97514209F19 /* Compatibility.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 134E806E27E7A9C700C2A94A /* NonCachingURLSessionConfigurationFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 134E806D27E7A9C600C2A94A /* NonCachingURLSessionConfigurationFactory.swift */; }; 1B67C6B5AA3631D44A1131E24B223E80 /* Pods-Hyperconnectivity_Tests-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 5302FE9DFF692B2E0E4DC36993AC06CD /* Pods-Hyperconnectivity_Tests-dummy.m */; }; 1F7678DA34C83E57229D405CF3C011CB /* HTTPStubsResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 8752ED48BF48F46C57381CD46FB0C493 /* HTTPStubsResponse.m */; }; 2442CE680DF66ACA774AFEB2ED40D3DB /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D1233D20896561397787188965775C36 /* Foundation.framework */; }; @@ -86,6 +87,7 @@ 0389F5918103E445BC3DBC43C114AEFC /* HTTPStubs.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = HTTPStubs.h; path = Sources/OHHTTPStubs/include/HTTPStubs.h; sourceTree = ""; }; 053D11B7D73CF7BBCDF43E9700F9B90E /* ConnectivitySubscription.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ConnectivitySubscription.swift; sourceTree = ""; }; 05795D80F7ADBF5F4A6F778B7729A2F6 /* HTTPStubsPathHelpers.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = HTTPStubsPathHelpers.m; path = Sources/OHHTTPStubs/HTTPStubsPathHelpers.m; sourceTree = ""; }; + 134E806D27E7A9C600C2A94A /* NonCachingURLSessionConfigurationFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NonCachingURLSessionConfigurationFactory.swift; sourceTree = ""; }; 179D4DB955D6AC3900B45340DC1B3C34 /* Hyperconnectivity-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Hyperconnectivity-prefix.pch"; sourceTree = ""; }; 17E33041B314FA837A3CAEB9DF3CDE9F /* OHHTTPStubs.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = OHHTTPStubs.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 1AC9AAE6418AF877A38D1FDCBC620BE5 /* NSURLRequest+HTTPBodyTesting.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSURLRequest+HTTPBodyTesting.h"; path = "Sources/OHHTTPStubs/include/NSURLRequest+HTTPBodyTesting.h"; sourceTree = ""; }; @@ -243,6 +245,7 @@ isa = PBXGroup; children = ( FA9463D8F662E3C593D138C0EF30368B /* ConnectivityPublisher.swift */, + 134E806D27E7A9C600C2A94A /* NonCachingURLSessionConfigurationFactory.swift */, 053D11B7D73CF7BBCDF43E9700F9B90E /* ConnectivitySubscription.swift */, 636DF3058B92AF46A526983B670D61A7 /* Publishers.swift */, D8EFC716F104B05931D57CDB03A4E800 /* ReachabilityPublisher.swift */, @@ -706,6 +709,7 @@ ED8B64E2B4A4B5062F89BAF796F8F82A /* ReachabilitySubscription.swift in Sources */, A24A6BF84B18AC56E507150BAD9E2A04 /* ResponseContainsStringValidator.swift in Sources */, 8F263F50193BCB276814CA4A9BB280E5 /* ResponseRegExValidator.swift in Sources */, + 134E806E27E7A9C700C2A94A /* NonCachingURLSessionConfigurationFactory.swift in Sources */, 938E4F20E814D8DD128E28D85704EAA9 /* ResponseStringEqualityValidator.swift in Sources */, F5B0B86C50A6C63A12F0E358D0048D7A /* ResponseStringValidator.swift in Sources */, 7175F8664E96DAD0709FC90DED4AB821 /* ResponseValidator.swift in Sources */, diff --git a/Example/Tests/NonCachingURLSessionConfigurationFactoryTests.swift b/Example/Tests/NonCachingURLSessionConfigurationFactoryTests.swift new file mode 100644 index 0000000..4d1cd18 --- /dev/null +++ b/Example/Tests/NonCachingURLSessionConfigurationFactoryTests.swift @@ -0,0 +1,39 @@ +// +// NonCachingURLSessionConfigurationFactoryTests.swift +// Hyperconnectivity_Tests +// +// Created by Ross Butler on 20/03/2022. +// Copyright © 2022 CocoaPods. All rights reserved. +// + +import Foundation +@testable import Hyperconnectivity +import XCTest + +class NonCachingURLSessionConfigurationFactoryTests: XCTestCase { + /// Tests that given a `URLSessionConfiguration` object with a response cache set, the factory will return a `URLSessionConfiguration` object which does not cache. + func testResponseCachingIsDisabled() { + let sut = NonCachingURLSessionConfigurationFactory() + let originalURLSessionConfiguration = URLSessionConfiguration.default + XCTAssertNotNil(originalURLSessionConfiguration.urlCache) + let newURLSessionConfiguration = sut.urlSessionConfiguration(from: originalURLSessionConfiguration) + XCTAssertNil(newURLSessionConfiguration.urlCache, "URL cache should be nil.") + } + + /// Tests that the `URLSessionConfiguration` passed as input to the factory is copied i.e. the output of the factory is a different object. + func testThatFactoryProducesACopy() { + let sut = NonCachingURLSessionConfigurationFactory() + let originalURLSessionConfiguration = URLSessionConfiguration.default + let newURLSessionConfiguration = sut.urlSessionConfiguration(from: originalURLSessionConfiguration) + XCTAssertFalse(originalURLSessionConfiguration === newURLSessionConfiguration) + } + + /// Tests that given a `URLSessionConfiguration` object passed as input to the factory, the request caching policy of the output ignores the local cache. + func testRequestCachePolicyIgnoresLocalCache() { + let sut = NonCachingURLSessionConfigurationFactory() + let originalURLSessionConfiguration = URLSessionConfiguration.default + XCTAssertEqual(originalURLSessionConfiguration.requestCachePolicy, .useProtocolCachePolicy) + let newURLSessionConfiguration = sut.urlSessionConfiguration(from: originalURLSessionConfiguration) + XCTAssertEqual(newURLSessionConfiguration.requestCachePolicy, .reloadIgnoringLocalCacheData) + } +} diff --git a/Hyperconnectivity/Classes/Combine/NonCachingURLSessionConfigurationFactory.swift b/Hyperconnectivity/Classes/Combine/NonCachingURLSessionConfigurationFactory.swift new file mode 100644 index 0000000..412af30 --- /dev/null +++ b/Hyperconnectivity/Classes/Combine/NonCachingURLSessionConfigurationFactory.swift @@ -0,0 +1,18 @@ +// +// NonCachingURLSessionConfigurationFactory.swift +// Hyperconnectivity +// +// Created by Ross Butler on 20/03/2022. +// + +import Foundation + +struct NonCachingURLSessionConfigurationFactory { + func urlSessionConfiguration(from input: URLSessionConfiguration) -> URLSessionConfiguration { + // Ensure that we never use cached results, including where using a custom `URLSessionConfiguration`. + let output = input.copy() as! URLSessionConfiguration + output.requestCachePolicy = .reloadIgnoringCacheData + output.urlCache = nil + return output + } +} diff --git a/Hyperconnectivity/Classes/Core/Hyperconnectivity.swift b/Hyperconnectivity/Classes/Core/Hyperconnectivity.swift index 4f40a57..9b8f261 100644 --- a/Hyperconnectivity/Classes/Core/Hyperconnectivity.swift +++ b/Hyperconnectivity/Classes/Core/Hyperconnectivity.swift @@ -51,18 +51,15 @@ public class Hyperconnectivity { private extension Hyperconnectivity { private func checkConnectivity(of path: NWPath, using configuration: Configuration) { - // Ensure that we never use cached results, including where using a custom `URLSessionConfiguration`. - let urlSessionConfiguration = configuration.urlSessionConfiguration.copy() as! URLSessionConfiguration - urlSessionConfiguration.requestCachePolicy = .reloadIgnoringCacheData - urlSessionConfiguration.urlCache = nil - + let factory = NonCachingURLSessionConfigurationFactory() + let urlSessionConfiguration = factory.urlSessionConfiguration(from: configuration.urlSessionConfiguration) let publishers = configuration.connectivityURLs.map { url in URLSession(configuration: urlSessionConfiguration).dataTaskPublisher(for: url) } let totalChecks = UInt(configuration.connectivityURLs.count) let result = ConnectivityResult(path: path, successThreshold: configuration.successThreshold, totalChecks: totalChecks) let combinedPublisher = Publishers.MergeMany(publishers) - cancellable = combinedPublisher.sink(receiveCompletion:{ [weak self] _ in + cancellable = combinedPublisher.sink(receiveCompletion:{ [weak self] _ in self?.connectivityChanged(result) }, receiveValue: { [weak self] response in result.connectivityCheck(successful: configuration.isResponseValid(response))