blob: 8c5f04dda248f2dd8e6fb6030e5e9916d992a4aa [file] [log] [blame]
/*
This source file is part of the Swift.org open source project
Copyright 2016 Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception
See http://swift.org/LICENSE.txt for license information
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
*/
import XCTest
import Basic
import TestSupport
private enum DummyError: Swift.Error {
case somethingWentWrong
}
private enum OtherDummyError: Swift.Error {
case somethingElseWentWrong
case andYetAnotherThingToGoWrong
}
class ResultTests: XCTestCase {
func testBasics() throws {
func doSomething(right: Bool) -> Result<String, DummyError> {
if right {
return Result("All OK.")
}
return Result(DummyError.somethingWentWrong)
}
// Success.
switch doSomething(right: true) {
case .success(let string):
XCTAssertEqual(string, "All OK.")
case .failure(let error):
XCTFail("Unexpected failure: \(error)")
}
// Error.
switch doSomething(right: false) {
case .success(let string):
XCTFail("Unexpected success: \(string)")
case .failure(let error):
XCTAssertEqual(error, DummyError.somethingWentWrong)
}
// Test dematerialize.
XCTAssertEqual(try doSomething(right: true).dematerialize(), "All OK.")
do {
_ = try doSomething(right: false).dematerialize()
XCTFail("Unexpected success")
} catch DummyError.somethingWentWrong {}
func should(`throw`: Bool) throws -> Int {
if `throw` {
throw DummyError.somethingWentWrong
}
return 1
}
// Test closure.
let result: Result<Int, DummyError> = try Result {
return try should(throw: false)
}
XCTAssertEqual(try result.dematerialize(), 1)
}
func testAnyError() {
func doSomething(right: Bool) -> Result<String, AnyError> {
if right {
return Result("All OK.")
}
return Result(DummyError.somethingWentWrong)
}
// Success.
switch doSomething(right: true) {
case .success(let string):
XCTAssertEqual(string, "All OK.")
case .failure(let error):
XCTFail("Unexpected failure: \(error)")
}
// Error.
switch doSomething(right: false) {
case .success(let string):
XCTFail("Unexpected success: \(string)")
case .failure(let error):
XCTAssertEqual(error.underlyingError as? DummyError, DummyError.somethingWentWrong)
}
do {
// Create an any error and check it doesn't nest.
let error = AnyError(DummyError.somethingWentWrong)
XCTAssertEqual(error.underlyingError as? DummyError, DummyError.somethingWentWrong)
let nested = AnyError(error)
XCTAssertEqual(nested.underlyingError as? DummyError, DummyError.somethingWentWrong)
// Check can create result directly from error.
let result: Result<String, AnyError> = Result(DummyError.somethingWentWrong)
if case let .failure(resultError) = result {
XCTAssertEqual(resultError.underlyingError as? DummyError, DummyError.somethingWentWrong)
} else {
XCTFail("Wrong result value \(result)")
}
}
do {
// Check any error closure initializer.
func throwing() throws -> String {
throw DummyError.somethingWentWrong
}
let result = Result(anyError: { try throwing() })
if case let .failure(resultError) = result {
XCTAssertEqual(resultError.underlyingError as? DummyError, DummyError.somethingWentWrong)
} else {
XCTFail("Wrong result value \(result)")
}
}
}
func testMap() throws {
XCTAssertEqual(try Result<String, DummyError>("Hello").map { $0 + " World" }.dematerialize(), "Hello World")
XCTAssertThrows(DummyError.somethingWentWrong) {
_ = try Result<String, DummyError>(.somethingWentWrong).map { $0 + " World" }.dematerialize()
}
}
func testMapAny() throws {
func throwing(_ shouldThrow: Bool) throws -> String {
if shouldThrow {
throw DummyError.somethingWentWrong
}
return " World"
}
// We should be able to map when we have value in result and our closure doesn't throw.
let success = Result<String, AnyError>("Hello").mapAny { value -> String in
let second = try throwing(false)
return value + second
}
XCTAssertEqual(try success.dematerialize(), "Hello World")
// We don't have a value, closure shouldn't matter.
let failure1 = Result<String, AnyError>(DummyError.somethingWentWrong).mapAny { value -> String in
let second = try throwing(false)
return value + second
}
XCTAssertThrowsAny(DummyError.somethingWentWrong) {
_ = try failure1.dematerialize()
}
// We have a value, but our closure throws.
let failure2 = Result<String, AnyError>("Hello").mapAny { value -> String in
let second = try throwing(true)
return value + second
}
XCTAssertThrowsAny(DummyError.somethingWentWrong) {
_ = try failure2.dematerialize()
}
}
func testFlatMap() throws {
XCTAssertEqual(try Result<String, DummyError>("Hello").flatMap { .success($0.utf8.count ) }.dematerialize(), 5)
XCTAssertThrows(DummyError.somethingWentWrong) {
_ = try Result<String, DummyError>.failure(.somethingWentWrong).flatMap { .success($0 + " World") }.dematerialize()
}
XCTAssertThrows(DummyError.somethingWentWrong) {
_ = try Result<String, DummyError>.failure(.somethingWentWrong).flatMap { (x: String) -> Result<String, DummyError> in
XCTFail("should not be executed")
return .success(x + " World")
}.dematerialize()
}
XCTAssertThrows(DummyError.somethingWentWrong) {
_ = try Result<String, DummyError>("Hello").flatMap { String -> Result<Int, DummyError> in .failure(.somethingWentWrong) }.dematerialize()
}
XCTAssertThrows(OtherDummyError.somethingElseWentWrong) {
_ = try Result<String, OtherDummyError>.failure(.somethingElseWentWrong)
.flatMap { String -> Result<String, OtherDummyError> in .failure(.andYetAnotherThingToGoWrong) }.dematerialize()
}
}
static var allTests = [
("testBasics", testBasics),
("testAnyError", testAnyError),
("testMap", testMap),
("testMapAny", testMapAny),
("testFlatMap", testFlatMap)
]
}
public func XCTAssertThrowsAny<T: Swift.Error>(_ expectedError: T, file: StaticString = #file, line: UInt = #line, _ body: () throws -> ()) where T: Equatable {
do {
try body()
XCTFail("body completed successfully", file: file, line: line)
} catch let error as AnyError {
XCTAssertEqual(error.underlyingError as? T, expectedError, file: file, line: line)
} catch {
XCTFail("unexpected error thrown", file: file, line: line)
}
}