Skip to content

Commit 9b83d91

Browse files
committed
[gardening] Migrate from unwrapped() to XCTUnwrap()
Instead of using an extension on Optional only available in Foundation, we should start using the recently introduced XCTUnwrap available in XCTest to perform the same job. The XCTest API would probably be more known in other code bases, so people will be more willing to use it and learn it. The commit removes all the usages of unwrapped and replaces them with XCTUnwrap. Additionally it marks unwrapped() as deprecated, so people receive a clear message about the disappearance with a helpful hint of what to use instead.
1 parent d602c57 commit 9b83d91

24 files changed

+177
-183
lines changed

Docs/Testing.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ swift-corelibs-foundation uses XCTest for its own test suite. This document expl
77
### In brief
88

99
* Tests should fail rather than crashing; swift-corelibs-xctest does not implement any crash recovery
10-
* You should avoid forced optional unwrapping (e.g.: `aValue!`). Use `try aValue.unwrapped()` instead
10+
* You should avoid forced optional unwrapping (e.g.: `aValue!`). Use `try XCTUnwrap(aValue)` instead
1111
* You can test code that is expected to crash; you must mark the whole body of the test method with `assertCrashes(within:)`
1212
* If a test or a portion of a test is giving the build trouble, use `testExpectedToFail` and write a bug
1313

@@ -19,7 +19,7 @@ Due to this, it is important to avoid crashing in test code, and to properly han
1919

2020
#### Avoiding Forced Unwrapping
2121

22-
Forced unwrapping is easily the easiest way to crash the test process, and should be avoided. We have an ergonomic replacement in the form of the `.unwrapped()` extension method on the `Optional` type.
22+
Forced unwrapping is easily the easiest way to crash the test process, and should be avoided. XCTest have an ergonomic replacement in the form of the `XCTUnwrap()` function.
2323

2424
The following code is a liability and code review should flag it:
2525

@@ -34,14 +34,14 @@ func testSomeInterestingAPI() {
3434
Instead:
3535

3636
1. Change the test method to throw errors by adding the `throws` clause. Tests that throw errors will fail and stop the first time an error is thrown, so plan accordingly, but a thrown error will not stop the test run, merely fail this test.
37-
2. Change the forced unwrapping to `try ….unwrapped()`.
37+
2. Change the forced unwrapping to `try XCTUnwrap(…)`.
3838

3939
For example, the code above can be fixed as follows:
4040

4141
```swift
4242
func testSomeInterestingAPI() throws { // Step 1: Add 'throws'
4343
// Step 2: Replace the unwrap.
44-
let x = try interestingAPI.someOptionalProperty.unwrapped()
44+
let x = try XCTUnwrap(interestingAPI.someOptionalProperty)
4545

4646
XCTAssertEqual(x, 42, "The correct answer is present")
4747
}

TestFoundation/FixtureValues.swift

+4-4
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ enum Fixtures {
4141
attrs3Maybe = nil
4242
}
4343

44-
let attrs3 = try attrs3Maybe.unwrapped()
44+
let attrs3 = try XCTUnwrap(attrs3Maybe)
4545

4646
string.setAttributes(attrs1, range: NSMakeRange(1, string.length - 2))
4747
string.setAttributes(attrs2, range: NSMakeRange(2, 2))
@@ -147,7 +147,7 @@ enum Fixtures {
147147
static let textCheckingResultSimpleRegex = TypedFixture<NSTextCheckingResult>("NSTextCheckingResult-SimpleRegex") {
148148
let string = "aaa"
149149
let regexp = try NSRegularExpression(pattern: "aaa", options: [])
150-
let result = try regexp.matches(in: string, range: NSRange(string.startIndex ..< string.endIndex, in: string)).first.unwrapped()
150+
let result = try XCTUnwrap(regexp.matches(in: string, range: NSRange(string.startIndex ..< string.endIndex, in: string)).first)
151151

152152
return result
153153
}
@@ -156,15 +156,15 @@ enum Fixtures {
156156
static let textCheckingResultExtendedRegex = TypedFixture<NSTextCheckingResult>("NSTextCheckingResult-ExtendedRegex") {
157157
let string = "aaaaaa"
158158
let regexp = try NSRegularExpression(pattern: "a(a(a(a(a(a)))))", options: [])
159-
let result = try regexp.matches(in: string, range: NSRange(string.startIndex ..< string.endIndex, in: string)).first.unwrapped()
159+
let result = try XCTUnwrap(regexp.matches(in: string, range: NSRange(string.startIndex ..< string.endIndex, in: string)).first)
160160

161161
return result
162162
}
163163

164164
static let textCheckingResultComplexRegex = TypedFixture<NSTextCheckingResult>("NSTextCheckingResult-ComplexRegex") {
165165
let string = "aaaaaaaaa"
166166
let regexp = try NSRegularExpression(pattern: "a(a(a(a(a(a(a(a(a))))))))", options: [])
167-
let result = try regexp.matches(in: string, range: NSRange(string.startIndex ..< string.endIndex, in: string)).first.unwrapped()
167+
let result = try XCTUnwrap(regexp.matches(in: string, range: NSRange(string.startIndex ..< string.endIndex, in: string)).first)
168168

169169
return result
170170
}

TestFoundation/TestCachedURLResponse.swift

+14-14
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
class TestCachedURLResponse : XCTestCase {
1111
func test_copy() throws {
12-
let url = try URL(string: "http://example.com/").unwrapped()
12+
let url = try XCTUnwrap(URL(string: "http://example.com/"))
1313
let response = URLResponse(url: url, mimeType: nil, expectedContentLength: -1, textEncodingName: nil)
1414
let bytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1515
let data = Data(bytes: bytes, count: bytes.count)
@@ -26,7 +26,7 @@ class TestCachedURLResponse : XCTestCase {
2626
}
2727

2828
func test_initDefaultUserInfoAndStoragePolicy() throws {
29-
let url = try URL(string: "http://example.com/").unwrapped()
29+
let url = try XCTUnwrap(URL(string: "http://example.com/"))
3030
let response = URLResponse(url: url, mimeType: nil, expectedContentLength: -1, textEncodingName: nil)
3131
let bytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
3232
let data = Data(bytes: bytes, count: bytes.count)
@@ -39,7 +39,7 @@ class TestCachedURLResponse : XCTestCase {
3939
}
4040

4141
func test_initDefaultUserInfo() throws {
42-
let url = try URL(string: "http://example.com/").unwrapped()
42+
let url = try XCTUnwrap(URL(string: "http://example.com/"))
4343
let response = URLResponse(url: url, mimeType: nil, expectedContentLength: -1, textEncodingName: nil)
4444
let bytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
4545
let data = Data(bytes: bytes, count: bytes.count)
@@ -53,7 +53,7 @@ class TestCachedURLResponse : XCTestCase {
5353
}
5454

5555
func test_initWithoutDefaults() throws {
56-
let url = try URL(string: "http://example.com/").unwrapped()
56+
let url = try XCTUnwrap(URL(string: "http://example.com/"))
5757
let response = URLResponse(url: url, mimeType: nil, expectedContentLength: -1, textEncodingName: nil)
5858
let bytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
5959
let data = Data(bytes: bytes, count: bytes.count)
@@ -68,7 +68,7 @@ class TestCachedURLResponse : XCTestCase {
6868
}
6969

7070
func test_equalWithTheSameInstance() throws {
71-
let url = try URL(string: "http://example.com/").unwrapped()
71+
let url = try XCTUnwrap(URL(string: "http://example.com/"))
7272
let response = URLResponse(url: url, mimeType: nil, expectedContentLength: -1, textEncodingName: nil)
7373
let bytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
7474
let data = Data(bytes: bytes, count: bytes.count)
@@ -80,7 +80,7 @@ class TestCachedURLResponse : XCTestCase {
8080
}
8181

8282
func test_equalWithUnrelatedObject() throws {
83-
let url = try URL(string: "http://example.com/").unwrapped()
83+
let url = try XCTUnwrap(URL(string: "http://example.com/"))
8484
let response = URLResponse(url: url, mimeType: nil, expectedContentLength: -1, textEncodingName: nil)
8585
let bytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
8686
let data = Data(bytes: bytes, count: bytes.count)
@@ -92,19 +92,19 @@ class TestCachedURLResponse : XCTestCase {
9292
}
9393

9494
func test_equalCheckingResponse() throws {
95-
let url1 = try URL(string: "http://example.com/").unwrapped()
95+
let url1 = try XCTUnwrap(URL(string: "http://example.com/"))
9696
let response1 = URLResponse(url: url1, mimeType: nil, expectedContentLength: -1, textEncodingName: nil)
9797
let bytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
9898
let data = Data(bytes: bytes, count: bytes.count)
9999
let userInfo: [AnyHashable: Any] = ["Key1": "Value1", "Key2": "Value2"]
100100
let storagePolicy = URLCache.StoragePolicy.allowedInMemoryOnly
101101
let cachedResponse1 = CachedURLResponse(response: response1, data: data, userInfo: userInfo, storagePolicy: storagePolicy)
102102

103-
let url2 = try URL(string: "http://example.com/second").unwrapped()
103+
let url2 = try XCTUnwrap(URL(string: "http://example.com/second"))
104104
let response2 = URLResponse(url: url2, mimeType: nil, expectedContentLength: -1, textEncodingName: nil)
105105
let cachedResponse2 = CachedURLResponse(response: response2, data: data, userInfo: userInfo, storagePolicy: storagePolicy)
106106

107-
let url3 = try URL(string: "http://example.com/").unwrapped()
107+
let url3 = try XCTUnwrap(URL(string: "http://example.com/"))
108108
let response3 = URLResponse(url: url3, mimeType: nil, expectedContentLength: -1, textEncodingName: nil)
109109
let cachedResponse3 = CachedURLResponse(response: response3, data: data, userInfo: userInfo, storagePolicy: storagePolicy)
110110

@@ -115,7 +115,7 @@ class TestCachedURLResponse : XCTestCase {
115115
}
116116

117117
func test_equalCheckingData() throws {
118-
let url = try URL(string: "http://example.com/").unwrapped()
118+
let url = try XCTUnwrap(URL(string: "http://example.com/"))
119119
let response = URLResponse(url: url, mimeType: nil, expectedContentLength: -1, textEncodingName: nil)
120120
let bytes1: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
121121
let data1 = Data(bytes: bytes1, count: bytes1.count)
@@ -138,7 +138,7 @@ class TestCachedURLResponse : XCTestCase {
138138
}
139139

140140
func test_equalCheckingStoragePolicy() throws {
141-
let url = try URL(string: "http://example.com/").unwrapped()
141+
let url = try XCTUnwrap(URL(string: "http://example.com/"))
142142
let response = URLResponse(url: url, mimeType: nil, expectedContentLength: -1, textEncodingName: nil)
143143
let bytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
144144
let data = Data(bytes: bytes, count: bytes.count)
@@ -159,15 +159,15 @@ class TestCachedURLResponse : XCTestCase {
159159
}
160160

161161
func test_hash() throws {
162-
let url1 = try URL(string: "http://example.com/").unwrapped()
162+
let url1 = try XCTUnwrap(URL(string: "http://example.com/"))
163163
let response1 = URLResponse(url: url1, mimeType: nil, expectedContentLength: -1, textEncodingName: nil)
164164
let bytes1: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
165165
let data1 = Data(bytes: bytes1, count: bytes1.count)
166166
let userInfo1: [AnyHashable: Any] = ["Key1": "Value1", "Key2": "Value2"]
167167
let storagePolicy1 = URLCache.StoragePolicy.allowedInMemoryOnly
168168
let cachedResponse1 = CachedURLResponse(response: response1, data: data1, userInfo: userInfo1, storagePolicy: storagePolicy1)
169169

170-
let url2 = try URL(string: "http://example.com/").unwrapped()
170+
let url2 = try XCTUnwrap(URL(string: "http://example.com/"))
171171
let response2 = URLResponse(url: url2, mimeType: nil, expectedContentLength: -1, textEncodingName: nil)
172172
let bytes2: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
173173
let data2 = Data(bytes: bytes2, count: bytes2.count)
@@ -176,7 +176,7 @@ class TestCachedURLResponse : XCTestCase {
176176
let cachedResponse2 = CachedURLResponse(response: response2, data: data2, userInfo: userInfo2, storagePolicy: storagePolicy2)
177177

178178
// Ideally, this cached response should have a different hash.
179-
let url3 = try URL(string: "http://example.com/second").unwrapped()
179+
let url3 = try XCTUnwrap(URL(string: "http://example.com/second"))
180180
let response3 = URLResponse(url: url3, mimeType: nil, expectedContentLength: -1, textEncodingName: nil)
181181
let bytes3: [UInt8] = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
182182
let data3 = Data(bytes: bytes3, count: bytes3.count)

TestFoundation/TestCalendar.swift

+5-5
Original file line numberDiff line numberDiff line change
@@ -234,21 +234,21 @@ class TestCalendar: XCTestCase {
234234
// Check that date(from:) does not change the timeZone of the calendar
235235
let df = DateFormatter()
236236
df.dateFormat = "yyyy-MM-dd"
237-
df.timeZone = try TimeZone(identifier: "UTC").unwrapped()
237+
df.timeZone = try XCTUnwrap(TimeZone(identifier: "UTC"))
238238

239239
var calendar = Calendar(identifier: .gregorian)
240240
calendar.locale = Locale(identifier: "en_US_POSIX")
241-
calendar.timeZone = try TimeZone(secondsFromGMT: 0).unwrapped()
241+
calendar.timeZone = try XCTUnwrap(TimeZone(secondsFromGMT: 0))
242242

243243
let calendarCopy = calendar
244244
XCTAssertEqual(calendarCopy.timeZone.identifier, "GMT")
245245
XCTAssertEqual(calendarCopy.timeZone.description, "GMT (fixed)")
246246

247-
let dc = try calendarCopy.dateComponents(in: TimeZone(identifier: "America/New_York").unwrapped(), from: df.date(from: "2019-01-01").unwrapped())
247+
let dc = try calendarCopy.dateComponents(in: XCTUnwrap(TimeZone(identifier: "America/New_York")), from: XCTUnwrap(df.date(from: "2019-01-01")))
248248
XCTAssertEqual(calendarCopy.timeZone.identifier, "GMT")
249249
XCTAssertEqual(calendarCopy.timeZone.description, "GMT (fixed)")
250250

251-
let dt = try calendarCopy.date(from: dc).unwrapped()
251+
let dt = try XCTUnwrap(calendarCopy.date(from: dc))
252252
XCTAssertEqual(dt.description, "2019-01-01 00:00:00 +0000")
253253
XCTAssertEqual(calendarCopy.timeZone.identifier, "GMT")
254254
XCTAssertEqual(calendarCopy.timeZone.description, "GMT (fixed)")
@@ -498,7 +498,7 @@ class TestNSDateComponents: XCTestCase {
498498
let date3 = Date(timeIntervalSince1970: 46570600.45678)
499499

500500
var calendar = Calendar.current
501-
calendar.timeZone = try TimeZone(abbreviation: "UTC").unwrapped()
501+
calendar.timeZone = try XCTUnwrap(TimeZone(abbreviation: "UTC"))
502502

503503
let diff1 = calendar.dateComponents([.nanosecond], from: date1, to: date2)
504504
XCTAssertEqual(diff1.nanosecond, 1230003)

TestFoundation/TestDateFormatter.swift

+3-3
Original file line numberDiff line numberDiff line change
@@ -419,12 +419,12 @@ class TestDateFormatter: XCTestCase {
419419
formatter.dateFormat = "yyyy-MM-dd"
420420

421421
XCTAssertNil(formatter.date(from: "2018-03-09T10:25:16+01:00"))
422-
let d1 = try formatter.date(from: "2018-03-09").unwrapped()
422+
let d1 = try XCTUnwrap(formatter.date(from: "2018-03-09"))
423423
XCTAssertEqual(d1.description, "2018-03-09 00:00:00 +0000")
424424

425425
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
426426
XCTAssertNil(formatter.date(from: "2018-03-09"))
427-
let d2 = try formatter.date(from: "2018-03-09T10:25:16+01:00").unwrapped()
427+
let d2 = try XCTUnwrap(formatter.date(from: "2018-03-09T10:25:16+01:00"))
428428
XCTAssertEqual(d2.description, "2018-03-09 09:25:16 +0000")
429429
}
430430

@@ -471,7 +471,7 @@ class TestDateFormatter: XCTestCase {
471471
let formatter = DateFormatter()
472472
formatter.timeZone = TimeZone(identifier: "CET")
473473
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
474-
let date = try formatter.date(from: "2019-05-05T12:52:10").unwrapped()
474+
let date = try XCTUnwrap(formatter.date(from: "2019-05-05T12:52:10"))
475475

476476
let applySettings: [(String, (DateFormatter) -> Void)] =
477477
[(".timeZone", {

TestFoundation/TestDateIntervalFormatter.swift

+6-6
Original file line numberDiff line numberDiff line change
@@ -107,14 +107,14 @@ class TestDateIntervalFormatter: XCTestCase {
107107
let result = formatter.string(from: date, to: date)
108108
result.assertContainsInOrder(requiresLastToBeAtEnd: true, "February 4", "2001", "5:20:00 PM", "Greenwich Mean Time")
109109

110-
let firstFebruary = try result.range(of: "February").unwrapped()
110+
let firstFebruary = try XCTUnwrap(result.range(of: "February"))
111111
XCTAssertNil(result[firstFebruary.upperBound...].range(of: "February")) // February appears only once.
112112
}
113113

114114
func testStringFromDateIntervalAcrossThreeMillionSeconds() throws {
115115
let interval = DateInterval(start: Date(timeIntervalSinceReferenceDate: 0), duration: 3e6)
116116

117-
let result = try formatter.string(from: interval).unwrapped()
117+
let result = try XCTUnwrap(formatter.string(from: interval))
118118
result.assertContainsInOrder("January 1", "2001", "12:00:00 AM", "Greenwich Mean Time",
119119
"February 4", "2001", "5:20:00 PM", "Greenwich Mean Time")
120120
}
@@ -195,7 +195,7 @@ class TestDateIntervalFormatter: XCTestCase {
195195
let result = formatter.string(from: older, to: newer)
196196
result.assertContainsInOrder(requiresLastToBeAtEnd: true, "January", "1", "2001", "12:00:00 AM", "5:00:00 AM", "GMT")
197197

198-
let firstJanuary = try result.range(of: "January").unwrapped()
198+
let firstJanuary = try XCTUnwrap(result.range(of: "January"))
199199
XCTAssertNil(result[firstJanuary.upperBound...].range(of: "January")) // January appears only once.
200200
}
201201

@@ -217,7 +217,7 @@ class TestDateIntervalFormatter: XCTestCase {
217217
let result = formatter.string(from: older, to: newer)
218218
result.assertContainsInOrder(requiresLastToBeAtEnd: true, "January", "1", "2001", "12:00:00 AM", "6:00:00 PM", "GMT")
219219

220-
let firstJanuary = try result.range(of: "January").unwrapped()
220+
let firstJanuary = try XCTUnwrap(result.range(of: "January"))
221221
XCTAssertNil(result[firstJanuary.upperBound...].range(of: "January")) // January appears only once.
222222
}
223223

@@ -229,8 +229,8 @@ class TestDateIntervalFormatter: XCTestCase {
229229
XCTAssertNotNil(lhs)
230230
XCTAssertNotNil(rhs)
231231

232-
let a = try lhs.unwrapped()
233-
let b = try rhs.unwrapped()
232+
let a = try XCTUnwrap(lhs)
233+
let b = try XCTUnwrap(rhs)
234234

235235
XCTAssertEqual(a.dateStyle, b.dateStyle, message())
236236
XCTAssertEqual(a.timeStyle, b.timeStyle, message())

TestFoundation/TestDecimal.swift

+4-4
Original file line numberDiff line numberDiff line change
@@ -602,10 +602,10 @@ class TestDecimal: XCTestCase {
602602
XCTAssertEqual(NSDecimalNumber(decimal: Decimal(UInt64.min)).description, UInt64.min.description)
603603
XCTAssertEqual(NSDecimalNumber(decimal: Decimal(UInt64.max)).description, UInt64.max.description)
604604

605-
XCTAssertEqual(try NSDecimalNumber(decimal: Decimal(string: "12.34").unwrapped()).description, "12.34")
606-
XCTAssertEqual(try NSDecimalNumber(decimal: Decimal(string: "0.0001").unwrapped()).description, "0.0001")
607-
XCTAssertEqual(try NSDecimalNumber(decimal: Decimal(string: "-1.0002").unwrapped()).description, "-1.0002")
608-
XCTAssertEqual(try NSDecimalNumber(decimal: Decimal(string: "0.0").unwrapped()).description, "0")
605+
XCTAssertEqual(NSDecimalNumber(decimal: try XCTUnwrap(Decimal(string: "12.34"))).description, "12.34")
606+
XCTAssertEqual(NSDecimalNumber(decimal: try XCTUnwrap(Decimal(string: "0.0001"))).description, "0.0001")
607+
XCTAssertEqual(NSDecimalNumber(decimal: try XCTUnwrap(Decimal(string: "-1.0002"))).description, "-1.0002")
608+
XCTAssertEqual(NSDecimalNumber(decimal: try XCTUnwrap(Decimal(string: "0.0"))).description, "0")
609609
}
610610

611611
func test_PositivePowers() {

0 commit comments

Comments
 (0)