Skip to content

Commit f887c0e

Browse files
authored
Enable swift PM tests on Linux and macOS (#118)
* Fix cycle in Mocker build steps * Fix `swift test` on Linux and macOS, introduce a simple Github Action
1 parent 068745f commit f887c0e

File tree

5 files changed

+102
-64
lines changed

5 files changed

+102
-64
lines changed

.github/workflows/ci.yml

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: "Mocker SPM CI"
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
pull_request:
8+
branches:
9+
- '*'
10+
11+
jobs:
12+
macos-run-tests:
13+
name: Unit Tests (macOS)
14+
runs-on: macos-latest
15+
steps:
16+
- uses: actions/checkout@v2
17+
- name: Run Tests
18+
run: swift test
19+
20+
linux-run-tests:
21+
name: Unit Tests (Linux)
22+
runs-on: ubuntu-latest
23+
steps:
24+
- uses: actions/checkout@v2
25+
- name: Run Tests
26+
run: swift test

Mocker.xcodeproj/project.pbxproj

+29-5
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
50D4606E20653F1F00A85D93 /* Mocker.h in Headers */ = {isa = PBXBuildFile; fileRef = 50D4606B20653F1F00A85D93 /* Mocker.h */; settings = {ATTRIBUTES = (Public, ); }; };
1616
50D4607020653F2500A85D93 /* MockedData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50D4605B20653EAF00A85D93 /* MockedData.swift */; };
1717
50D4607120653F2700A85D93 /* MockerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50D4605C20653EAF00A85D93 /* MockerTests.swift */; };
18-
50D4607320653F4500A85D93 /* Resources in Resources */ = {isa = PBXBuildFile; fileRef = 50D4607220653F4500A85D93 /* Resources */; };
18+
8ED91F36283AFDC300EA8E99 /* wetransfer_bot_avatar.png in Resources */ = {isa = PBXBuildFile; fileRef = 8ED91F32283AFDC300EA8E99 /* wetransfer_bot_avatar.png */; };
19+
8ED91F37283AFDC300EA8E99 /* example.json in Resources */ = {isa = PBXBuildFile; fileRef = 8ED91F34283AFDC300EA8E99 /* example.json */; };
20+
8ED91F38283AFDC300EA8E99 /* sample-redirect-get.data in Resources */ = {isa = PBXBuildFile; fileRef = 8ED91F35283AFDC300EA8E99 /* sample-redirect-get.data */; };
1921
/* End PBXBuildFile section */
2022

2123
/* Begin PBXContainerItemProxy section */
@@ -43,7 +45,9 @@
4345
50D4606320653EAF00A85D93 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
4446
50D4606B20653F1F00A85D93 /* Mocker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Mocker.h; sourceTree = "<group>"; };
4547
50D4606D20653F1F00A85D93 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
46-
50D4607220653F4500A85D93 /* Resources */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Resources; sourceTree = "<group>"; };
48+
8ED91F32283AFDC300EA8E99 /* wetransfer_bot_avatar.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = wetransfer_bot_avatar.png; sourceTree = "<group>"; };
49+
8ED91F34283AFDC300EA8E99 /* example.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = example.json; sourceTree = "<group>"; };
50+
8ED91F35283AFDC300EA8E99 /* sample-redirect-get.data */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "sample-redirect-get.data"; sourceTree = "<group>"; };
4751
/* End PBXFileReference section */
4852

4953
/* Begin PBXFrameworksBuildPhase section */
@@ -101,9 +105,9 @@
101105
50D4605A20653EAF00A85D93 /* MockerTests */ = {
102106
isa = PBXGroup;
103107
children = (
108+
8ED91F31283AFDC300EA8E99 /* Resources */,
104109
50D4605B20653EAF00A85D93 /* MockedData.swift */,
105110
50D4605C20653EAF00A85D93 /* MockerTests.swift */,
106-
50D4607220653F4500A85D93 /* Resources */,
107111
50D4606220653EAF00A85D93 /* Supporting Files */,
108112
);
109113
path = MockerTests;
@@ -134,6 +138,24 @@
134138
path = "Supporting Files";
135139
sourceTree = "<group>";
136140
};
141+
8ED91F31283AFDC300EA8E99 /* Resources */ = {
142+
isa = PBXGroup;
143+
children = (
144+
8ED91F32283AFDC300EA8E99 /* wetransfer_bot_avatar.png */,
145+
8ED91F33283AFDC300EA8E99 /* JSON Files */,
146+
8ED91F35283AFDC300EA8E99 /* sample-redirect-get.data */,
147+
);
148+
path = Resources;
149+
sourceTree = "<group>";
150+
};
151+
8ED91F33283AFDC300EA8E99 /* JSON Files */ = {
152+
isa = PBXGroup;
153+
children = (
154+
8ED91F34283AFDC300EA8E99 /* example.json */,
155+
);
156+
path = "JSON Files";
157+
sourceTree = "<group>";
158+
};
137159
/* End PBXGroup section */
138160

139161
/* Begin PBXHeadersBuildPhase section */
@@ -152,9 +174,9 @@
152174
isa = PBXNativeTarget;
153175
buildConfigurationList = 501E26A81F3DAE370048F39E /* Build configuration list for PBXNativeTarget "Mocker" */;
154176
buildPhases = (
177+
501E26911F3DAE370048F39E /* Headers */,
155178
501E268F1F3DAE370048F39E /* Sources */,
156179
501E26901F3DAE370048F39E /* Frameworks */,
157-
501E26911F3DAE370048F39E /* Headers */,
158180
501E26921F3DAE370048F39E /* Resources */,
159181
503446441F3DE70C0039D5E4 /* SwiftLint */,
160182
);
@@ -240,7 +262,9 @@
240262
isa = PBXResourcesBuildPhase;
241263
buildActionMask = 2147483647;
242264
files = (
243-
50D4607320653F4500A85D93 /* Resources in Resources */,
265+
8ED91F36283AFDC300EA8E99 /* wetransfer_bot_avatar.png in Resources */,
266+
8ED91F38283AFDC300EA8E99 /* sample-redirect-get.data in Resources */,
267+
8ED91F37283AFDC300EA8E99 /* example.json in Resources */,
244268
);
245269
runOnlyForDeploymentPostprocessing = 0;
246270
};

MockerTests/MockedData.swift

+9-4
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,18 @@
77
//
88

99
import Foundation
10-
import UIKit
1110

1211
/// Contains all available Mocked data.
1312
public final class MockedData {
14-
public static let botAvatarImageFileUrl: URL = Bundle(for: MockedData.self).url(forResource: "wetransfer_bot_avatar", withExtension: "png")!
15-
public static let exampleJSON: URL = Bundle(for: MockedData.self).url(forResource: "Resources/JSON Files/example", withExtension: "json")!
16-
public static let redirectGET: URL = Bundle(for: MockedData.self).url(forResource: "Resources/sample-redirect-get", withExtension: "data")!
13+
public static let botAvatarImageFileUrl: URL = Bundle.module.url(forResource: "wetransfer_bot_avatar", withExtension: "png")!
14+
public static let exampleJSON: URL = Bundle.module.url(forResource: "example", withExtension: "json")!
15+
public static let redirectGET: URL = Bundle.module.url(forResource: "sample-redirect-get", withExtension: "data")!
16+
}
17+
18+
extension Bundle {
19+
#if !SWIFT_PACKAGE
20+
static let module = Bundle(for: MockedData.self)
21+
#endif
1722
}
1823

1924
internal extension URL {

MockerTests/MockerTests.swift

+37-54
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
//
88

99
import XCTest
10+
#if canImport(FoundationNetworking)
11+
import FoundationNetworking
12+
#endif
1013
@testable import Mocker
1114

1215
final class MockerTests: XCTestCase {
@@ -36,17 +39,15 @@ final class MockerTests: XCTestCase {
3639
let expectation = self.expectation(description: "Data request should succeed")
3740
let originalURL = URL(string: "https://avatars3.githubusercontent.com/u/26250426?v=4&s=400")!
3841

42+
let mockedData = MockedData.botAvatarImageFileUrl.data
3943
let mock = Mock(url: originalURL, dataType: .imagePNG, statusCode: 200, data: [
40-
.get: MockedData.botAvatarImageFileUrl.data
44+
.get: mockedData
4145
])
4246

4347
mock.register()
4448
URLSession.shared.dataTask(with: originalURL) { (data, _, error) in
4549
XCTAssertNil(error)
46-
let image: UIImage = UIImage(data: data!)!
47-
let sampleImage: UIImage = UIImage(contentsOfFile: MockedData.botAvatarImageFileUrl.path)!
48-
49-
XCTAssertEqual(image.size, sampleImage.size, "Image should be returned mocked")
50+
XCTAssertEqual(data, mockedData, "Image should be returned mocked")
5051
expectation.fulfill()
5152
}.resume()
5253

@@ -58,16 +59,14 @@ final class MockerTests: XCTestCase {
5859
let expectation = self.expectation(description: "Data request should succeed")
5960
let originalURL = URL(string: "https://www.wetransfer.com/sample-image.png")
6061

62+
let mockedData = MockedData.botAvatarImageFileUrl.data
6163
Mock(fileExtensions: "png", dataType: .imagePNG, statusCode: 200, data: [
62-
.get: MockedData.botAvatarImageFileUrl.data
64+
.get: mockedData
6365
]).register()
6466

6567
URLSession.shared.dataTask(with: originalURL!) { (data, _, error) in
6668
XCTAssertNil(error)
67-
let image: UIImage = UIImage(data: data!)!
68-
let sampleImage: UIImage = UIImage(contentsOfFile: MockedData.botAvatarImageFileUrl.path)!
69-
70-
XCTAssertEqual(image.size, sampleImage.size, "Image should be returned mocked")
69+
XCTAssertEqual(data, mockedData, "Image should be returned mocked")
7170
expectation.fulfill()
7271
}.resume()
7372

@@ -82,23 +81,15 @@ final class MockerTests: XCTestCase {
8281
Mock(fileExtensions: "png", dataType: .imagePNG, statusCode: 400, data: [
8382
.get: Data()
8483
]).register()
84+
85+
let mockedData = MockedData.botAvatarImageFileUrl.data
8586
Mock(url: originalURL, ignoreQuery: true, dataType: .imagePNG, statusCode: 200, data: [
86-
.get: MockedData.botAvatarImageFileUrl.data
87+
.get: mockedData
8788
]).register()
8889

8990
URLSession.shared.dataTask(with: originalURL) { (data, _, error) in
9091
XCTAssertNil(error)
91-
guard let data = data else {
92-
XCTFail("Data is nil")
93-
return
94-
}
95-
guard let image: UIImage = UIImage(data: data) else {
96-
XCTFail("Invalid data \(String(describing: data))")
97-
return
98-
}
99-
let sampleImage: UIImage = UIImage(contentsOfFile: MockedData.botAvatarImageFileUrl.path)!
100-
101-
XCTAssertEqual(image.size, sampleImage.size, "Image should be returned mocked")
92+
XCTAssertEqual(data, mockedData, "Image should be returned mocked")
10293
expectation.fulfill()
10394
}.resume()
10495

@@ -110,27 +101,17 @@ final class MockerTests: XCTestCase {
110101
let expectation = self.expectation(description: "Data request should succeed")
111102
let originalURL = URL(string: "https://www.wetransfer.com/sample-image.png?width=200&height=200")!
112103

104+
let mockedData = MockedData.botAvatarImageFileUrl.data
113105
Mock(url: originalURL, ignoreQuery: true, dataType: .imagePNG, statusCode: 200, data: [
114-
.get: MockedData.botAvatarImageFileUrl.data
106+
.get: mockedData
115107
]).register()
116108

117109
/// Make it different compared to the mocked URL.
118110
let customURL = URL(string: originalURL.absoluteString + "&" + UUID().uuidString)!
119111

120112
URLSession.shared.dataTask(with: customURL) { (data, _, error) in
121113
XCTAssertNil(error)
122-
guard let data = data else {
123-
XCTFail("Data is nil")
124-
return
125-
}
126-
127-
guard let image: UIImage = UIImage(data: data) else {
128-
XCTFail("Invalid data \(String(describing: data))")
129-
return
130-
}
131-
let sampleImage: UIImage = UIImage(contentsOfFile: MockedData.botAvatarImageFileUrl.path)!
132-
133-
XCTAssertEqual(image.size, sampleImage.size, "Image should be returned mocked")
114+
XCTAssertEqual(data, mockedData, "Image should be returned mocked")
134115
expectation.fulfill()
135116
}.resume()
136117

@@ -173,13 +154,13 @@ final class MockerTests: XCTestCase {
173154
/// It should return the additional headers.
174155
func testAdditionalHeaders() {
175156
let expectation = self.expectation(description: "Data request should succeed")
176-
let headers = ["testkey": "testvalue"]
157+
let headers = ["Testkey": "testvalue"]
177158
let mock = Mock(dataType: .json, statusCode: 200, data: [.get: Data()], additionalHeaders: headers)
178159
mock.register()
179160

180161
URLSession.shared.dataTask(with: mock.request) { (_, response, error) in
181162
XCTAssertNil(error)
182-
XCTAssertEqual(((response as? HTTPURLResponse)?.allHeaderFields["testkey"] as? String), "testvalue", "Additional headers should be added.")
163+
XCTAssertEqual(((response as? HTTPURLResponse)?.allHeaderFields["Testkey"] as? String), "testvalue", "Additional headers should be added.")
183164
expectation.fulfill()
184165
}.resume()
185166

@@ -192,12 +173,12 @@ final class MockerTests: XCTestCase {
192173
let mock = Mock(dataType: .json, statusCode: 200, data: [.get: Data()], additionalHeaders: ["testkey": "testvalue"])
193174
mock.register()
194175

195-
let newMock = Mock(dataType: .json, statusCode: 200, data: [.get: Data()], additionalHeaders: ["newkey": "newvalue"])
176+
let newMock = Mock(dataType: .json, statusCode: 200, data: [.get: Data()], additionalHeaders: ["Newkey": "newvalue"])
196177
newMock.register()
197178

198179
URLSession.shared.dataTask(with: mock.request) { (_, response, error) in
199180
XCTAssertNil(error)
200-
XCTAssertEqual(((response as? HTTPURLResponse)?.allHeaderFields["newkey"] as? String), "newvalue", "Additional headers should be added.")
181+
XCTAssertEqual(((response as? HTTPURLResponse)?.allHeaderFields["Newkey"] as? String), "newvalue", "Additional headers should be added.")
201182
expectation.fulfill()
202183
}.resume()
203184

@@ -209,8 +190,9 @@ final class MockerTests: XCTestCase {
209190
let expectation = self.expectation(description: "Data request should succeed")
210191
let originalURL = URL(string: "https://www.wetransfer.com/sample-image.png")
211192

193+
let mockedData = MockedData.botAvatarImageFileUrl.data
212194
Mock(fileExtensions: "png", dataType: .imagePNG, statusCode: 200, data: [
213-
.get: MockedData.botAvatarImageFileUrl.data
195+
.get: mockedData
214196
]).register()
215197

216198
let configuration = URLSessionConfiguration.default
@@ -219,18 +201,7 @@ final class MockerTests: XCTestCase {
219201

220202
urlSession.dataTask(with: originalURL!) { (data, _, error) in
221203
XCTAssertNil(error)
222-
guard let data = data else {
223-
XCTFail("Data is nil")
224-
return
225-
}
226-
227-
guard let image: UIImage = UIImage(data: data) else {
228-
XCTFail("Invalid data \(String(describing: data))")
229-
return
230-
}
231-
let sampleImage: UIImage = UIImage(contentsOfFile: MockedData.botAvatarImageFileUrl.path)!
232-
233-
XCTAssertEqual(image.size, sampleImage.size, "Image should be returned mocked")
204+
XCTAssertEqual(data, mockedData, "Image should be returned mocked")
234205
expectation.fulfill()
235206
}.resume()
236207

@@ -258,7 +229,10 @@ final class MockerTests: XCTestCase {
258229
}
259230

260231
/// It should correctly handle redirect responses.
261-
func testRedirectResponse() {
232+
func testRedirectResponse() throws {
233+
#if os(Linux)
234+
throw XCTSkip("The URLSession swift-corelibs-foundation implementation doesn't currently handle redirects directly")
235+
#endif
262236
let expectation = self.expectation(description: "Data request should be cancelled")
263237
let urlWhichRedirects: URL = URL(string: "https://we.tl/redirect")!
264238
Mock(url: urlWhichRedirects, dataType: .html, statusCode: 200, data: [.get: MockedData.redirectGET.data]).register()
@@ -447,10 +421,14 @@ final class MockerTests: XCTestCase {
447421
XCTAssertNil(urlresponse)
448422
XCTAssertNotNil(error)
449423
if let error = error {
424+
#if os(Linux)
425+
XCTAssertEqual(error as? TestExampleError, .example)
426+
#else
450427
// there's not a particularly elegant way to verify an instance
451428
// of an error, but this is a convenient workaround for testing
452429
// purposes
453430
XCTAssertTrue(String(describing: error).contains("TestExampleError"))
431+
#endif
454432
}
455433

456434
expectation.fulfill()
@@ -460,7 +438,10 @@ final class MockerTests: XCTestCase {
460438
}
461439

462440
/// It should cache response
463-
func testMockCachePolicy() {
441+
func testMockCachePolicy() throws {
442+
#if os(Linux)
443+
throw XCTSkip("URLSessionTask in swift-corelibs-foundation doesn't cache response for custom protocols")
444+
#endif
464445
let expectation = self.expectation(description: "Data request should succeed")
465446
let originalURL = URL(string: "https://www.wetransfer.com/example.json")!
466447

@@ -471,7 +452,9 @@ final class MockerTests: XCTestCase {
471452
).register()
472453

473454
let configuration = URLSessionConfiguration.default
455+
#if !os(Linux)
474456
configuration.urlCache = URLCache()
457+
#endif
475458
configuration.protocolClasses = [MockingURLProtocol.self]
476459
let urlSession = URLSession(configuration: configuration)
477460

Package.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,6 @@ let package = Package(name: "Mocker",
2424
// dev .product(name: "WeTransferPRLinter", package: "WeTransferPRLinter")
2525
// dev ], path: "Submodules/WeTransfer-iOS-CI/DangerFakeSources", sources: ["DangerFakeSource.swift"]),
2626
.target(name: "Mocker", path: "Sources"),
27-
.testTarget(name: "MockerTests", dependencies: ["Mocker"], path: "MockerTests")
27+
.testTarget(name: "MockerTests", dependencies: ["Mocker"], path: "MockerTests", resources: [.process("Resources")])
2828
],
2929
swiftLanguageVersions: [.v5])

0 commit comments

Comments
 (0)