From 7a8f2d9a9a99e1e4eddfa869cde64c777c26c3d9 Mon Sep 17 00:00:00 2001 From: Arthur Semenyutin Date: Mon, 23 May 2022 09:22:44 +1000 Subject: [PATCH 1/2] Fix cycle in Mocker build steps --- Mocker.xcodeproj/project.pbxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mocker.xcodeproj/project.pbxproj b/Mocker.xcodeproj/project.pbxproj index b32515a..85353fa 100644 --- a/Mocker.xcodeproj/project.pbxproj +++ b/Mocker.xcodeproj/project.pbxproj @@ -152,9 +152,9 @@ isa = PBXNativeTarget; buildConfigurationList = 501E26A81F3DAE370048F39E /* Build configuration list for PBXNativeTarget "Mocker" */; buildPhases = ( + 501E26911F3DAE370048F39E /* Headers */, 501E268F1F3DAE370048F39E /* Sources */, 501E26901F3DAE370048F39E /* Frameworks */, - 501E26911F3DAE370048F39E /* Headers */, 501E26921F3DAE370048F39E /* Resources */, 503446441F3DE70C0039D5E4 /* SwiftLint */, ); From 5295a258aafc6eedb59ba50be1b6d65298b3db17 Mon Sep 17 00:00:00 2001 From: Arthur Semenyutin Date: Mon, 23 May 2022 09:39:14 +1000 Subject: [PATCH 2/2] Fix `swift test` on Linux and macOS, introduce a simple Github Action --- .github/workflows/ci.yml | 26 +++++++++ Mocker.xcodeproj/project.pbxproj | 32 +++++++++-- MockerTests/MockedData.swift | 13 +++-- MockerTests/MockerTests.swift | 91 +++++++++++++------------------- Package.swift | 2 +- 5 files changed, 101 insertions(+), 63 deletions(-) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..616c1ce --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,26 @@ +name: "Mocker SPM CI" + +on: + push: + branches: + - master + pull_request: + branches: + - '*' + +jobs: + macos-run-tests: + name: Unit Tests (macOS) + runs-on: macos-latest + steps: + - uses: actions/checkout@v2 + - name: Run Tests + run: swift test + + linux-run-tests: + name: Unit Tests (Linux) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Run Tests + run: swift test diff --git a/Mocker.xcodeproj/project.pbxproj b/Mocker.xcodeproj/project.pbxproj index 85353fa..ebf6fe2 100644 --- a/Mocker.xcodeproj/project.pbxproj +++ b/Mocker.xcodeproj/project.pbxproj @@ -15,7 +15,9 @@ 50D4606E20653F1F00A85D93 /* Mocker.h in Headers */ = {isa = PBXBuildFile; fileRef = 50D4606B20653F1F00A85D93 /* Mocker.h */; settings = {ATTRIBUTES = (Public, ); }; }; 50D4607020653F2500A85D93 /* MockedData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50D4605B20653EAF00A85D93 /* MockedData.swift */; }; 50D4607120653F2700A85D93 /* MockerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50D4605C20653EAF00A85D93 /* MockerTests.swift */; }; - 50D4607320653F4500A85D93 /* Resources in Resources */ = {isa = PBXBuildFile; fileRef = 50D4607220653F4500A85D93 /* Resources */; }; + 8ED91F36283AFDC300EA8E99 /* wetransfer_bot_avatar.png in Resources */ = {isa = PBXBuildFile; fileRef = 8ED91F32283AFDC300EA8E99 /* wetransfer_bot_avatar.png */; }; + 8ED91F37283AFDC300EA8E99 /* example.json in Resources */ = {isa = PBXBuildFile; fileRef = 8ED91F34283AFDC300EA8E99 /* example.json */; }; + 8ED91F38283AFDC300EA8E99 /* sample-redirect-get.data in Resources */ = {isa = PBXBuildFile; fileRef = 8ED91F35283AFDC300EA8E99 /* sample-redirect-get.data */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -43,7 +45,9 @@ 50D4606320653EAF00A85D93 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50D4606B20653F1F00A85D93 /* Mocker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Mocker.h; sourceTree = ""; }; 50D4606D20653F1F00A85D93 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 50D4607220653F4500A85D93 /* Resources */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Resources; sourceTree = ""; }; + 8ED91F32283AFDC300EA8E99 /* wetransfer_bot_avatar.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = wetransfer_bot_avatar.png; sourceTree = ""; }; + 8ED91F34283AFDC300EA8E99 /* example.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = example.json; sourceTree = ""; }; + 8ED91F35283AFDC300EA8E99 /* sample-redirect-get.data */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "sample-redirect-get.data"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -101,9 +105,9 @@ 50D4605A20653EAF00A85D93 /* MockerTests */ = { isa = PBXGroup; children = ( + 8ED91F31283AFDC300EA8E99 /* Resources */, 50D4605B20653EAF00A85D93 /* MockedData.swift */, 50D4605C20653EAF00A85D93 /* MockerTests.swift */, - 50D4607220653F4500A85D93 /* Resources */, 50D4606220653EAF00A85D93 /* Supporting Files */, ); path = MockerTests; @@ -134,6 +138,24 @@ path = "Supporting Files"; sourceTree = ""; }; + 8ED91F31283AFDC300EA8E99 /* Resources */ = { + isa = PBXGroup; + children = ( + 8ED91F32283AFDC300EA8E99 /* wetransfer_bot_avatar.png */, + 8ED91F33283AFDC300EA8E99 /* JSON Files */, + 8ED91F35283AFDC300EA8E99 /* sample-redirect-get.data */, + ); + path = Resources; + sourceTree = ""; + }; + 8ED91F33283AFDC300EA8E99 /* JSON Files */ = { + isa = PBXGroup; + children = ( + 8ED91F34283AFDC300EA8E99 /* example.json */, + ); + path = "JSON Files"; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -240,7 +262,9 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 50D4607320653F4500A85D93 /* Resources in Resources */, + 8ED91F36283AFDC300EA8E99 /* wetransfer_bot_avatar.png in Resources */, + 8ED91F38283AFDC300EA8E99 /* sample-redirect-get.data in Resources */, + 8ED91F37283AFDC300EA8E99 /* example.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/MockerTests/MockedData.swift b/MockerTests/MockedData.swift index 26fd9d4..33f24d1 100644 --- a/MockerTests/MockedData.swift +++ b/MockerTests/MockedData.swift @@ -7,13 +7,18 @@ // import Foundation -import UIKit /// Contains all available Mocked data. public final class MockedData { - public static let botAvatarImageFileUrl: URL = Bundle(for: MockedData.self).url(forResource: "wetransfer_bot_avatar", withExtension: "png")! - public static let exampleJSON: URL = Bundle(for: MockedData.self).url(forResource: "Resources/JSON Files/example", withExtension: "json")! - public static let redirectGET: URL = Bundle(for: MockedData.self).url(forResource: "Resources/sample-redirect-get", withExtension: "data")! + public static let botAvatarImageFileUrl: URL = Bundle.module.url(forResource: "wetransfer_bot_avatar", withExtension: "png")! + public static let exampleJSON: URL = Bundle.module.url(forResource: "example", withExtension: "json")! + public static let redirectGET: URL = Bundle.module.url(forResource: "sample-redirect-get", withExtension: "data")! +} + +extension Bundle { +#if !SWIFT_PACKAGE + static let module = Bundle(for: MockedData.self) +#endif } internal extension URL { diff --git a/MockerTests/MockerTests.swift b/MockerTests/MockerTests.swift index 5099212..2e38ad7 100644 --- a/MockerTests/MockerTests.swift +++ b/MockerTests/MockerTests.swift @@ -7,6 +7,9 @@ // import XCTest +#if canImport(FoundationNetworking) +import FoundationNetworking +#endif @testable import Mocker final class MockerTests: XCTestCase { @@ -36,17 +39,15 @@ final class MockerTests: XCTestCase { let expectation = self.expectation(description: "Data request should succeed") let originalURL = URL(string: "https://avatars3.githubusercontent.com/u/26250426?v=4&s=400")! + let mockedData = MockedData.botAvatarImageFileUrl.data let mock = Mock(url: originalURL, dataType: .imagePNG, statusCode: 200, data: [ - .get: MockedData.botAvatarImageFileUrl.data + .get: mockedData ]) mock.register() URLSession.shared.dataTask(with: originalURL) { (data, _, error) in XCTAssertNil(error) - let image: UIImage = UIImage(data: data!)! - let sampleImage: UIImage = UIImage(contentsOfFile: MockedData.botAvatarImageFileUrl.path)! - - XCTAssertEqual(image.size, sampleImage.size, "Image should be returned mocked") + XCTAssertEqual(data, mockedData, "Image should be returned mocked") expectation.fulfill() }.resume() @@ -58,16 +59,14 @@ final class MockerTests: XCTestCase { let expectation = self.expectation(description: "Data request should succeed") let originalURL = URL(string: "https://www.wetransfer.com/sample-image.png") + let mockedData = MockedData.botAvatarImageFileUrl.data Mock(fileExtensions: "png", dataType: .imagePNG, statusCode: 200, data: [ - .get: MockedData.botAvatarImageFileUrl.data + .get: mockedData ]).register() URLSession.shared.dataTask(with: originalURL!) { (data, _, error) in XCTAssertNil(error) - let image: UIImage = UIImage(data: data!)! - let sampleImage: UIImage = UIImage(contentsOfFile: MockedData.botAvatarImageFileUrl.path)! - - XCTAssertEqual(image.size, sampleImage.size, "Image should be returned mocked") + XCTAssertEqual(data, mockedData, "Image should be returned mocked") expectation.fulfill() }.resume() @@ -82,23 +81,15 @@ final class MockerTests: XCTestCase { Mock(fileExtensions: "png", dataType: .imagePNG, statusCode: 400, data: [ .get: Data() ]).register() + + let mockedData = MockedData.botAvatarImageFileUrl.data Mock(url: originalURL, ignoreQuery: true, dataType: .imagePNG, statusCode: 200, data: [ - .get: MockedData.botAvatarImageFileUrl.data + .get: mockedData ]).register() URLSession.shared.dataTask(with: originalURL) { (data, _, error) in XCTAssertNil(error) - guard let data = data else { - XCTFail("Data is nil") - return - } - guard let image: UIImage = UIImage(data: data) else { - XCTFail("Invalid data \(String(describing: data))") - return - } - let sampleImage: UIImage = UIImage(contentsOfFile: MockedData.botAvatarImageFileUrl.path)! - - XCTAssertEqual(image.size, sampleImage.size, "Image should be returned mocked") + XCTAssertEqual(data, mockedData, "Image should be returned mocked") expectation.fulfill() }.resume() @@ -110,8 +101,9 @@ final class MockerTests: XCTestCase { let expectation = self.expectation(description: "Data request should succeed") let originalURL = URL(string: "https://www.wetransfer.com/sample-image.png?width=200&height=200")! + let mockedData = MockedData.botAvatarImageFileUrl.data Mock(url: originalURL, ignoreQuery: true, dataType: .imagePNG, statusCode: 200, data: [ - .get: MockedData.botAvatarImageFileUrl.data + .get: mockedData ]).register() /// Make it different compared to the mocked URL. @@ -119,18 +111,7 @@ final class MockerTests: XCTestCase { URLSession.shared.dataTask(with: customURL) { (data, _, error) in XCTAssertNil(error) - guard let data = data else { - XCTFail("Data is nil") - return - } - - guard let image: UIImage = UIImage(data: data) else { - XCTFail("Invalid data \(String(describing: data))") - return - } - let sampleImage: UIImage = UIImage(contentsOfFile: MockedData.botAvatarImageFileUrl.path)! - - XCTAssertEqual(image.size, sampleImage.size, "Image should be returned mocked") + XCTAssertEqual(data, mockedData, "Image should be returned mocked") expectation.fulfill() }.resume() @@ -173,13 +154,13 @@ final class MockerTests: XCTestCase { /// It should return the additional headers. func testAdditionalHeaders() { let expectation = self.expectation(description: "Data request should succeed") - let headers = ["testkey": "testvalue"] + let headers = ["Testkey": "testvalue"] let mock = Mock(dataType: .json, statusCode: 200, data: [.get: Data()], additionalHeaders: headers) mock.register() URLSession.shared.dataTask(with: mock.request) { (_, response, error) in XCTAssertNil(error) - XCTAssertEqual(((response as? HTTPURLResponse)?.allHeaderFields["testkey"] as? String), "testvalue", "Additional headers should be added.") + XCTAssertEqual(((response as? HTTPURLResponse)?.allHeaderFields["Testkey"] as? String), "testvalue", "Additional headers should be added.") expectation.fulfill() }.resume() @@ -192,12 +173,12 @@ final class MockerTests: XCTestCase { let mock = Mock(dataType: .json, statusCode: 200, data: [.get: Data()], additionalHeaders: ["testkey": "testvalue"]) mock.register() - let newMock = Mock(dataType: .json, statusCode: 200, data: [.get: Data()], additionalHeaders: ["newkey": "newvalue"]) + let newMock = Mock(dataType: .json, statusCode: 200, data: [.get: Data()], additionalHeaders: ["Newkey": "newvalue"]) newMock.register() URLSession.shared.dataTask(with: mock.request) { (_, response, error) in XCTAssertNil(error) - XCTAssertEqual(((response as? HTTPURLResponse)?.allHeaderFields["newkey"] as? String), "newvalue", "Additional headers should be added.") + XCTAssertEqual(((response as? HTTPURLResponse)?.allHeaderFields["Newkey"] as? String), "newvalue", "Additional headers should be added.") expectation.fulfill() }.resume() @@ -209,8 +190,9 @@ final class MockerTests: XCTestCase { let expectation = self.expectation(description: "Data request should succeed") let originalURL = URL(string: "https://www.wetransfer.com/sample-image.png") + let mockedData = MockedData.botAvatarImageFileUrl.data Mock(fileExtensions: "png", dataType: .imagePNG, statusCode: 200, data: [ - .get: MockedData.botAvatarImageFileUrl.data + .get: mockedData ]).register() let configuration = URLSessionConfiguration.default @@ -219,18 +201,7 @@ final class MockerTests: XCTestCase { urlSession.dataTask(with: originalURL!) { (data, _, error) in XCTAssertNil(error) - guard let data = data else { - XCTFail("Data is nil") - return - } - - guard let image: UIImage = UIImage(data: data) else { - XCTFail("Invalid data \(String(describing: data))") - return - } - let sampleImage: UIImage = UIImage(contentsOfFile: MockedData.botAvatarImageFileUrl.path)! - - XCTAssertEqual(image.size, sampleImage.size, "Image should be returned mocked") + XCTAssertEqual(data, mockedData, "Image should be returned mocked") expectation.fulfill() }.resume() @@ -258,7 +229,10 @@ final class MockerTests: XCTestCase { } /// It should correctly handle redirect responses. - func testRedirectResponse() { + func testRedirectResponse() throws { + #if os(Linux) + throw XCTSkip("The URLSession swift-corelibs-foundation implementation doesn't currently handle redirects directly") + #endif let expectation = self.expectation(description: "Data request should be cancelled") let urlWhichRedirects: URL = URL(string: "https://we.tl/redirect")! Mock(url: urlWhichRedirects, dataType: .html, statusCode: 200, data: [.get: MockedData.redirectGET.data]).register() @@ -447,10 +421,14 @@ final class MockerTests: XCTestCase { XCTAssertNil(urlresponse) XCTAssertNotNil(error) if let error = error { + #if os(Linux) + XCTAssertEqual(error as? TestExampleError, .example) + #else // there's not a particularly elegant way to verify an instance // of an error, but this is a convenient workaround for testing // purposes XCTAssertTrue(String(describing: error).contains("TestExampleError")) + #endif } expectation.fulfill() @@ -460,7 +438,10 @@ final class MockerTests: XCTestCase { } /// It should cache response - func testMockCachePolicy() { + func testMockCachePolicy() throws { + #if os(Linux) + throw XCTSkip("URLSessionTask in swift-corelibs-foundation doesn't cache response for custom protocols") + #endif let expectation = self.expectation(description: "Data request should succeed") let originalURL = URL(string: "https://www.wetransfer.com/example.json")! @@ -471,7 +452,9 @@ final class MockerTests: XCTestCase { ).register() let configuration = URLSessionConfiguration.default + #if !os(Linux) configuration.urlCache = URLCache() + #endif configuration.protocolClasses = [MockingURLProtocol.self] let urlSession = URLSession(configuration: configuration) diff --git a/Package.swift b/Package.swift index bf96561..add4609 100644 --- a/Package.swift +++ b/Package.swift @@ -24,6 +24,6 @@ let package = Package(name: "Mocker", // dev .product(name: "WeTransferPRLinter", package: "WeTransferPRLinter") // dev ], path: "Submodules/WeTransfer-iOS-CI/DangerFakeSources", sources: ["DangerFakeSource.swift"]), .target(name: "Mocker", path: "Sources"), - .testTarget(name: "MockerTests", dependencies: ["Mocker"], path: "MockerTests") + .testTarget(name: "MockerTests", dependencies: ["Mocker"], path: "MockerTests", resources: [.process("Resources")]) ], swiftLanguageVersions: [.v5])