Skip to content

Commit

Permalink
[gardening] Migrate from unwrapped() to XCTUnwrap()
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
drodriguez committed Aug 21, 2019
1 parent d602c57 commit 9b83d91
Show file tree
Hide file tree
Showing 24 changed files with 177 additions and 183 deletions.
8 changes: 4 additions & 4 deletions Docs/Testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ swift-corelibs-foundation uses XCTest for its own test suite. This document expl
### In brief

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

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

#### Avoiding Forced Unwrapping

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.
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.

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

Expand All @@ -34,14 +34,14 @@ func testSomeInterestingAPI() {
Instead:

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.
2. Change the forced unwrapping to `try ….unwrapped()`.
2. Change the forced unwrapping to `try XCTUnwrap(…)`.

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

```swift
func testSomeInterestingAPI() throws { // Step 1: Add 'throws'
// Step 2: Replace the unwrap.
let x = try interestingAPI.someOptionalProperty.unwrapped()
let x = try XCTUnwrap(interestingAPI.someOptionalProperty)

XCTAssertEqual(x, 42, "The correct answer is present")
}
Expand Down
8 changes: 4 additions & 4 deletions TestFoundation/FixtureValues.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ enum Fixtures {
attrs3Maybe = nil
}

let attrs3 = try attrs3Maybe.unwrapped()
let attrs3 = try XCTUnwrap(attrs3Maybe)

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

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

return result
}

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

return result
}
Expand Down
28 changes: 14 additions & 14 deletions TestFoundation/TestCachedURLResponse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

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

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

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

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

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

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

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

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

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

Expand All @@ -115,7 +115,7 @@ class TestCachedURLResponse : XCTestCase {
}

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

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

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

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

// Ideally, this cached response should have a different hash.
let url3 = try URL(string: "http://example.com/second").unwrapped()
let url3 = try XCTUnwrap(URL(string: "http://example.com/second"))
let response3 = URLResponse(url: url3, mimeType: nil, expectedContentLength: -1, textEncodingName: nil)
let bytes3: [UInt8] = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
let data3 = Data(bytes: bytes3, count: bytes3.count)
Expand Down
10 changes: 5 additions & 5 deletions TestFoundation/TestCalendar.swift
Original file line number Diff line number Diff line change
Expand Up @@ -234,21 +234,21 @@ class TestCalendar: XCTestCase {
// Check that date(from:) does not change the timeZone of the calendar
let df = DateFormatter()
df.dateFormat = "yyyy-MM-dd"
df.timeZone = try TimeZone(identifier: "UTC").unwrapped()
df.timeZone = try XCTUnwrap(TimeZone(identifier: "UTC"))

var calendar = Calendar(identifier: .gregorian)
calendar.locale = Locale(identifier: "en_US_POSIX")
calendar.timeZone = try TimeZone(secondsFromGMT: 0).unwrapped()
calendar.timeZone = try XCTUnwrap(TimeZone(secondsFromGMT: 0))

let calendarCopy = calendar
XCTAssertEqual(calendarCopy.timeZone.identifier, "GMT")
XCTAssertEqual(calendarCopy.timeZone.description, "GMT (fixed)")

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

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

var calendar = Calendar.current
calendar.timeZone = try TimeZone(abbreviation: "UTC").unwrapped()
calendar.timeZone = try XCTUnwrap(TimeZone(abbreviation: "UTC"))

let diff1 = calendar.dateComponents([.nanosecond], from: date1, to: date2)
XCTAssertEqual(diff1.nanosecond, 1230003)
Expand Down
6 changes: 3 additions & 3 deletions TestFoundation/TestDateFormatter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -419,12 +419,12 @@ class TestDateFormatter: XCTestCase {
formatter.dateFormat = "yyyy-MM-dd"

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

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

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

let applySettings: [(String, (DateFormatter) -> Void)] =
[(".timeZone", {
Expand Down
12 changes: 6 additions & 6 deletions TestFoundation/TestDateIntervalFormatter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -107,14 +107,14 @@ class TestDateIntervalFormatter: XCTestCase {
let result = formatter.string(from: date, to: date)
result.assertContainsInOrder(requiresLastToBeAtEnd: true, "February 4", "2001", "5:20:00 PM", "Greenwich Mean Time")

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

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

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

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

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

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

Expand All @@ -229,8 +229,8 @@ class TestDateIntervalFormatter: XCTestCase {
XCTAssertNotNil(lhs)
XCTAssertNotNil(rhs)

let a = try lhs.unwrapped()
let b = try rhs.unwrapped()
let a = try XCTUnwrap(lhs)
let b = try XCTUnwrap(rhs)

XCTAssertEqual(a.dateStyle, b.dateStyle, message())
XCTAssertEqual(a.timeStyle, b.timeStyle, message())
Expand Down
8 changes: 4 additions & 4 deletions TestFoundation/TestDecimal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -602,10 +602,10 @@ class TestDecimal: XCTestCase {
XCTAssertEqual(NSDecimalNumber(decimal: Decimal(UInt64.min)).description, UInt64.min.description)
XCTAssertEqual(NSDecimalNumber(decimal: Decimal(UInt64.max)).description, UInt64.max.description)

XCTAssertEqual(try NSDecimalNumber(decimal: Decimal(string: "12.34").unwrapped()).description, "12.34")
XCTAssertEqual(try NSDecimalNumber(decimal: Decimal(string: "0.0001").unwrapped()).description, "0.0001")
XCTAssertEqual(try NSDecimalNumber(decimal: Decimal(string: "-1.0002").unwrapped()).description, "-1.0002")
XCTAssertEqual(try NSDecimalNumber(decimal: Decimal(string: "0.0").unwrapped()).description, "0")
XCTAssertEqual(NSDecimalNumber(decimal: try XCTUnwrap(Decimal(string: "12.34"))).description, "12.34")
XCTAssertEqual(NSDecimalNumber(decimal: try XCTUnwrap(Decimal(string: "0.0001"))).description, "0.0001")
XCTAssertEqual(NSDecimalNumber(decimal: try XCTUnwrap(Decimal(string: "-1.0002"))).description, "-1.0002")
XCTAssertEqual(NSDecimalNumber(decimal: try XCTUnwrap(Decimal(string: "0.0"))).description, "0")
}

func test_PositivePowers() {
Expand Down
Loading

0 comments on commit 9b83d91

Please sign in to comment.