blob: 82eadb59f3eb955cb5e738d4e997b66b48b96efc [file] [log] [blame]
// RUN: %empty-directory(%t)
// RUN: %target-build-swift %s -o %t/a.out -module-name main -swift-version 4
// RUN: %target-codesign %t/a.out
// RUN: %target-run %t/a.out
// REQUIRES: executable_test
// REQUIRES: objc_interop
// Requires explicit swift-version 4.
// UNSUPPORTED: swift_test_mode_optimize_none_with_implicit_dynamic
import Foundation
#if FOUNDATION_XCTEST
import XCTest
class TestSuper : XCTestCase { }
#else
import StdlibUnittest
class TestSuper { }
#endif
struct SInt: Codable, Equatable, Hashable {
var x: Int
}
struct SFloat {
var y: Float
}
extension SFloat: Equatable {}
extension SFloat: Hashable {}
extension SFloat: Codable {}
struct SGeneric<T, U> {
var t: T
var u: U
}
extension SGeneric: Equatable where T: Equatable, U: Equatable {}
extension SGeneric: Hashable where T: Hashable, U: Hashable {}
extension SGeneric: Codable where T: Codable, U: Codable {}
enum EGeneric<T> {
case a(T), b(Int), c
}
extension EGeneric: Equatable where T: Equatable {}
extension EGeneric: Hashable where T: Hashable {}
enum NoValues {
case a, b, c
}
extension NoValues: CaseIterable {}
// Cache some values, and make them all have the same width (within a type) for
// formatting niceness.
let SIOne = SInt(x: 1)
let SITwo = SInt(x: 2)
let SFOne = SFloat(y: 1.0)
let SFTwo = SFloat(y: 2.0)
let SFInf = SFloat(y: .infinity)
let SFNan = SFloat(y: .nan)
let SGOneOne = SGeneric(t: SIOne, u: SFOne)
let SGTwoOne = SGeneric(t: SITwo, u: SFOne)
let SGTwoTwo = SGeneric(t: SITwo, u: SFTwo)
let SGOneInf = SGeneric(t: SIOne, u: SFInf)
let SGOneNan = SGeneric(t: SIOne, u: SFNan)
let EGaOne: EGeneric<SInt> = .a(SIOne)
let EGaTwo: EGeneric<SInt> = .a(SITwo)
let EGbOne: EGeneric<SInt> = .b(1)
let EGbTwo: EGeneric<SInt> = .b(2)
let EGc___: EGeneric<SInt> = .c
func debugDescription<T>(_ value: T) -> String {
if let debugDescribable = value as? CustomDebugStringConvertible {
return debugDescribable.debugDescription
} else if let describable = value as? CustomStringConvertible {
return describable.description
} else {
return "\(value)"
}
}
func testEquatableHashable<T: Equatable & Hashable>(cases: [Int: (T, T, Bool, Bool)]) {
for (testLine, (lhs, rhs, equal, hashEqual)) in cases {
expectEqual(lhs == rhs, equal,
"\(#file):\(testLine) LHS <\(debugDescription(lhs))> == RHS <\(debugDescription(rhs))> doesn't match <\(equal)>")
let lhsHash = lhs.hashValue
let rhsHash = rhs.hashValue
expectEqual(lhsHash == rhsHash, hashEqual,
"\(#file):\(testLine) LHS <\(debugDescription(lhs)).hashValue> (\(lhsHash)) == RHS <\(debugDescription(rhs)).hashValue> (\(rhsHash)) doesn't match <\(hashEqual)>")
}
}
class TestEquatableHashable : TestSuper {
lazy var int: [Int: (SInt, SInt, Bool, Bool)] = [
#line : (SIOne, SIOne, true, true),
#line : (SIOne, SITwo, false, false),
#line : (SITwo, SIOne, false, false),
#line : (SITwo, SITwo, true, true),
]
func test_SInt() {
testEquatableHashable(cases: int)
}
lazy var float: [Int: (SFloat, SFloat, Bool, Bool)] = [
#line : (SFOne, SFOne, true, true),
#line : (SFOne, SFTwo, false, false),
#line : (SFTwo, SFOne, false, false),
#line : (SFTwo, SFTwo, true, true),
#line : (SFInf, SFInf, true, true),
#line : (SFInf, SFOne, false, false),
// A bit-based hasher is likely to hash these to the same thing.
#line : (SFNan, SFNan, false, true),
#line : (SFNan, SFOne, false, false),
]
func test_SFloat() {
testEquatableHashable(cases: float)
}
lazy var generic: [Int: (SGeneric<SInt, SFloat>, SGeneric<SInt, SFloat>, Bool, Bool)] = [
#line : (SGOneOne, SGOneOne, true, true),
#line : (SGOneOne, SGTwoOne, false, false),
#line : (SGOneOne, SGTwoTwo, false, false),
#line : (SGTwoOne, SGTwoOne, true, true),
#line : (SGTwoOne, SGTwoTwo, false, false),
#line : (SGTwoTwo, SGTwoTwo, true, true),
#line : (SGOneInf, SGOneInf, true, true),
#line : (SGOneInf, SGOneOne, false, false),
#line : (SGOneInf, SGTwoOne, false, false),
#line : (SGOneInf, SGTwoTwo, false, false),
// As above, a bit-based hasher is likely to hash these to the same thing
#line : (SGOneNan, SGOneNan, false, true),
#line : (SGOneNan, SGOneOne, false, false),
#line : (SGOneNan, SGTwoOne, false, false),
#line : (SGOneNan, SGTwoTwo, false, false),
#line : (SGOneNan, SGOneInf, false, false)
]
func test_SGeneric() {
testEquatableHashable(cases: generic)
}
lazy var egeneric: [Int: (EGeneric<SInt>, EGeneric<SInt>, Bool, Bool)] = [
#line : (EGaOne, EGaOne, true, true),
#line : (EGaOne, EGaTwo, false, false),
#line : (EGaOne, EGbOne, false, false),
#line : (EGaOne, EGbTwo, false, false),
#line : (EGaOne, EGc___, false, false),
#line : (EGbOne, EGaOne, false, false),
#line : (EGbOne, EGaTwo, false, false),
#line : (EGbOne, EGbOne, true, true),
#line : (EGbOne, EGbTwo, false, false),
#line : (EGbOne, EGc___, false, false),
#line : (EGc___, EGaOne, false, false),
#line : (EGc___, EGaTwo, false, false),
#line : (EGc___, EGbOne, false, false),
#line : (EGc___, EGbTwo, false, false),
#line : (EGc___, EGc___, true, true),
]
func test_EGeneric() {
testEquatableHashable(cases: egeneric)
}
}
func expectRoundTripEquality<T : Codable>(of value: T, lineNumber: Int) where T : Equatable {
let inf = "INF", negInf = "-INF", nan = "NaN"
let encoder = JSONEncoder()
encoder.nonConformingFloatEncodingStrategy = .convertToString(positiveInfinity: inf,
negativeInfinity: negInf,
nan: nan)
let data: Data
do {
data = try encoder.encode(value)
} catch {
fatalError("\(#file):\(lineNumber): Unable to encode \(T.self) <\(debugDescription(value))>: \(error)")
}
let decoder = JSONDecoder()
decoder.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: inf,
negativeInfinity: negInf,
nan: nan)
let decoded: T
do {
decoded = try decoder.decode(T.self, from: data)
} catch {
fatalError("\(#file):\(lineNumber): Unable to decode \(T.self) <\(debugDescription(value))>: \(error)")
}
expectEqual(value, decoded, "\(#file):\(lineNumber): Decoded \(T.self) <\(debugDescription(decoded))> not equal to original <\(debugDescription(value))>")
}
class TestCodable : TestSuper {
lazy var int: [Int: SInt] = [
#line : SIOne,
#line : SITwo,
]
func test_SInt() {
for (testLine, value) in int {
expectRoundTripEquality(of: value, lineNumber: testLine)
}
}
lazy var float: [Int : SFloat] = [
#line : SFOne,
#line : SFTwo,
#line : SFInf,
// This won't compare equal to itself
// #line : SFNan
]
func test_SFloat() {
for (testLine, value) in float {
expectRoundTripEquality(of: value, lineNumber: testLine)
}
}
lazy var generic : [Int : SGeneric<SInt, SFloat>] = [
#line : SGOneOne,
#line : SGTwoOne,
#line : SGTwoTwo,
#line : SGOneInf,
// As above, this won't compare equal to itself
// #line : SGOneNan,
]
func test_SGeneric() {
for (testLine, value) in generic {
expectRoundTripEquality(of: value, lineNumber: testLine)
}
}
}
class TestCaseIterable : TestSuper {
func test_allCases() {
expectEqual(NoValues.allCases, [.a, .b, .c])
}
}
#if !FOUNDATION_XCTEST
var equatableHashable = [
"TestEquatableHashable.test_SInt": TestEquatableHashable.test_SInt,
"TestEquatableHashable.test_SFloat": TestEquatableHashable.test_SFloat,
"TestEquatableHashable.test_SGeneric": TestEquatableHashable.test_SGeneric,
"TestEquatableHashable.test_EGeneric": TestEquatableHashable.test_EGeneric,
]
var EquatableHashableTests = TestSuite("TestEquatableHashable")
for (name, test) in equatableHashable {
EquatableHashableTests.test(name) { test(TestEquatableHashable())() }
}
var codable = [
"TestCodable.test_SInt": TestCodable.test_SInt,
"TestCodable.test_SFloat": TestCodable.test_SFloat,
"TestCodable.test_SGeneric": TestCodable.test_SGeneric,
]
var CodableTests = TestSuite("TestCodable")
for (name, test) in codable {
CodableTests.test(name) { test(TestCodable())() }
}
var caseIterable = [
"TestCaseIterable.test_allCases": TestCaseIterable.test_allCases,
]
var CaseIterableTests = TestSuite("TestCaseIterable")
for (name, test) in caseIterable {
CaseIterableTests.test(name) { test(TestCaseIterable())() }
}
runAllTests()
#endif