-
Notifications
You must be signed in to change notification settings - Fork 146
Expand file tree
/
Copy pathCustomTestStringConvertible.swift
More file actions
139 lines (124 loc) · 4.5 KB
/
CustomTestStringConvertible.swift
File metadata and controls
139 lines (124 loc) · 4.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2023 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
//
/// A protocol describing types with a custom string representation when
/// presented as part of a test's output.
///
/// ## See Also
///
/// - ``Swift/String/init(describingForTest:)``
public protocol CustomTestStringConvertible {
/// A description of this instance to use when presenting it in a test's
/// output.
///
/// Do not use this property directly. To get the test description of a value,
/// use ``Swift/String/init(describingForTest:)``.
var testDescription: String { get }
}
extension String {
/// Initialize this instance so that it can be presented in a test's output.
///
/// - Parameters:
/// - value: The value to describe.
///
/// ## See Also
///
/// - ``CustomTestStringConvertible``
public init(describingForTest value: some Any) {
// The mangled type name SPI doesn't handle generic types very well, so we
// ask for the dynamic type of `value` (type(of:)) instead of just T.self.
lazy var valueTypeInfo = TypeInfo(describingTypeOf: value)
if let value = value as? any CustomTestStringConvertible {
self = value.testDescription
} else if let value = value as? any CustomStringConvertible {
self.init(describing: value)
} else if let value = value as? any TextOutputStreamable {
self.init(describing: value)
} else if let value = value as? any CustomDebugStringConvertible {
self.init(reflecting: value)
} else if let value = value as? any Any.Type {
self = _testDescription(of: value)
} else if let value = value as? any RawRepresentable, let type = valueTypeInfo.type, valueTypeInfo.isImportedFromC {
// Present raw-representable C types, which we assume to be imported
// enumerations, in a consistent fashion. The case names of C enumerations
// are not statically visible, so instead present the enumeration type's
// name along with the raw value of `value`.
let typeName = String(describingForTest: type)
self = "\(typeName)(rawValue: \(String(describingForTest: value.rawValue)))"
} else if valueTypeInfo.isSwiftEnumeration {
// Add a leading period to enumeration cases to more closely match their
// source representation. SEE: _adHocPrint_unlocked() in the stdlib.
self = ".\(value)"
} else {
// Use the generic description of the value.
self.init(describing: value)
}
}
}
// MARK: - Built-in implementations
/// The _de facto_ implementation of ``CustomTestStringConvertible`` for a
/// metatype value.
///
/// - Parameters:
/// - type: The type to describe.
///
/// - Returns: The description of `type`, as produced by
/// ``Swift/String/init(describingForTest:)``.
private func _testDescription(of type: any Any.Type) -> String {
TypeInfo(describing: type).unqualifiedName
}
extension Optional: CustomTestStringConvertible {
public var testDescription: String {
switch self {
case let .some(unwrappedValue):
String(describingForTest: unwrappedValue)
case nil:
"nil"
}
}
}
extension _OptionalNilComparisonType: CustomTestStringConvertible {
public var testDescription: String {
"nil"
}
}
// MARK: - Strings
extension CustomTestStringConvertible where Self: StringProtocol {
public var testDescription: String {
"\"\(self)\""
}
}
extension String: CustomTestStringConvertible {}
extension Substring: CustomTestStringConvertible {}
// MARK: - Ranges
extension ClosedRange: CustomTestStringConvertible {
public var testDescription: String {
"\(String(describingForTest: lowerBound)) ... \(String(describingForTest: upperBound))"
}
}
extension PartialRangeFrom: CustomTestStringConvertible {
public var testDescription: String {
"\(String(describingForTest: lowerBound))..."
}
}
extension PartialRangeThrough: CustomTestStringConvertible {
public var testDescription: String {
"...\(String(describingForTest: upperBound))"
}
}
extension PartialRangeUpTo: CustomTestStringConvertible {
public var testDescription: String {
"..<\(String(describingForTest: upperBound))"
}
}
extension Range: CustomTestStringConvertible {
public var testDescription: String {
"\(String(describingForTest: lowerBound)) ..< \(String(describingForTest: upperBound))"
}
}