Skip to content

Commit df16fe6

Browse files
authored
Support more generated placeholder types (#39)
* Move _generatePlaceholder * Create DefaultInitializable.swift * Update GeneratePlaceholder.swift * Create GeneratePlaceholderTests.swift
1 parent e600841 commit df16fe6

File tree

4 files changed

+267
-21
lines changed

4 files changed

+267
-21
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
protocol _DefaultInitializable {
2+
init()
3+
}
4+
5+
extension Array: _DefaultInitializable {}
6+
extension Bool: _DefaultInitializable {}
7+
extension Character: _DefaultInitializable { init() { self.init(" ") } }
8+
extension Dictionary: _DefaultInitializable {}
9+
extension Double: _DefaultInitializable {}
10+
extension Float: _DefaultInitializable {}
11+
extension Int: _DefaultInitializable {}
12+
extension Int8: _DefaultInitializable {}
13+
extension Int16: _DefaultInitializable {}
14+
extension Int32: _DefaultInitializable {}
15+
extension Int64: _DefaultInitializable {}
16+
extension Set: _DefaultInitializable {}
17+
extension String: _DefaultInitializable {}
18+
extension Substring: _DefaultInitializable {}
19+
extension UInt: _DefaultInitializable {}
20+
extension UInt8: _DefaultInitializable {}
21+
extension UInt16: _DefaultInitializable {}
22+
extension UInt32: _DefaultInitializable {}
23+
extension UInt64: _DefaultInitializable {}
24+
25+
extension AsyncStream: _DefaultInitializable {
26+
init() { self.init { $0.finish() } }
27+
}
28+
29+
extension AsyncThrowingStream: _DefaultInitializable where Failure == Error {
30+
init() { self.init { $0.finish(throwing: CancellationError()) } }
31+
}
32+
33+
#if canImport(Foundation)
34+
import Foundation
35+
#if canImport(FoundationNetworking)
36+
import FoundationNetworking
37+
#endif
38+
39+
extension Data: _DefaultInitializable {}
40+
extension Date: _DefaultInitializable {}
41+
extension Decimal: _DefaultInitializable {}
42+
extension UUID: _DefaultInitializable {}
43+
extension URL: _DefaultInitializable { init() { self.init(string: "/")! } }
44+
#endif
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
extension _DefaultInitializable { fileprivate static var placeholder: Self { Self() } }
2+
extension AdditiveArithmetic { fileprivate static var placeholder: Self { .zero } }
3+
extension ExpressibleByArrayLiteral { fileprivate static var placeholder: Self { [] } }
4+
extension ExpressibleByBooleanLiteral { fileprivate static var placeholder: Self { false } }
5+
extension ExpressibleByDictionaryLiteral { fileprivate static var placeholder: Self { [:] } }
6+
extension ExpressibleByFloatLiteral { fileprivate static var placeholder: Self { 0.0 } }
7+
extension ExpressibleByIntegerLiteral { fileprivate static var placeholder: Self { 0 } }
8+
extension ExpressibleByUnicodeScalarLiteral { fileprivate static var placeholder: Self { " " } }
9+
extension RangeReplaceableCollection { fileprivate static var placeholder: Self { Self() } }
10+
11+
#if swift(>=5.7)
12+
13+
private func _placeholder<Result>() -> Result? {
14+
switch Result.self {
15+
case let type as _DefaultInitializable.Type: return type.placeholder as? Result
16+
case is Void.Type: return () as? Result
17+
case let type as any RangeReplaceableCollection.Type: return type.placeholder as? Result
18+
case let type as any AdditiveArithmetic.Type: return type.placeholder as? Result
19+
case let type as any ExpressibleByArrayLiteral.Type: return type.placeholder as? Result
20+
case let type as any ExpressibleByBooleanLiteral.Type: return type.placeholder as? Result
21+
case let type as any ExpressibleByDictionaryLiteral.Type: return type.placeholder as? Result
22+
case let type as any ExpressibleByFloatLiteral.Type: return type.placeholder as? Result
23+
case let type as any ExpressibleByIntegerLiteral.Type: return type.placeholder as? Result
24+
case let type as any ExpressibleByUnicodeScalarLiteral.Type: return type.placeholder as? Result
25+
default: return nil
26+
}
27+
}
28+
29+
private func _rawRepresentable<Result>() -> Result? {
30+
func posiblePlaceholder<T: RawRepresentable>(for type: T.Type) -> T? {
31+
(_placeholder() as T.RawValue?).flatMap(T.init(rawValue:))
32+
}
33+
34+
return (Result.self as? any RawRepresentable.Type).flatMap {
35+
posiblePlaceholder(for: $0) as? Result
36+
}
37+
}
38+
39+
private func _caseIterable<Result>() -> Result? {
40+
func firstCase<T: CaseIterable>(for type: T.Type) -> Result? {
41+
T.allCases.first as? Result
42+
}
43+
44+
return (Result.self as? any CaseIterable.Type).flatMap {
45+
firstCase(for: $0)
46+
}
47+
}
48+
49+
#else
50+
51+
private func _placeholder<Result>() -> Result? {
52+
if let result = (Result.self as? _DefaultInitializable.Type)?.placeholder {
53+
return result as? Result
54+
}
55+
56+
if Result.self == Void.self {
57+
return () as? Result
58+
}
59+
60+
switch Witness<Result>.self {
61+
case let type as AnyRangeReplaceableCollection.Type: return type.placeholder as? Result
62+
case let type as AnyAdditiveArithmetic.Type: return type.placeholder as? Result
63+
case let type as AnyExpressibleByArrayLiteral.Type: return type.placeholder as? Result
64+
case let type as AnyExpressibleByBooleanLiteral.Type: return type.placeholder as? Result
65+
case let type as AnyExpressibleByDictionaryLiteral.Type: return type.placeholder as? Result
66+
case let type as AnyExpressibleByFloatLiteral.Type: return type.placeholder as? Result
67+
case let type as AnyExpressibleByIntegerLiteral.Type: return type.placeholder as? Result
68+
case let type as AnyExpressibleByUnicodeScalarLiteral.Type: return type.placeholder as? Result
69+
default: return nil
70+
}
71+
}
72+
73+
private func _rawRepresentable<Result>() -> Result? {
74+
(Witness<Result>.self as? AnyRawRepresentable.Type).flatMap {
75+
$0.possiblePlaceholder as? Result
76+
}
77+
}
78+
79+
private func _caseIterable<Result>() -> Result? {
80+
(Witness<Result>.self as? AnyCaseIterable.Type).flatMap {
81+
$0.firstCase as? Result
82+
}
83+
}
84+
85+
private enum Witness<Value> {}
86+
private protocol AnyAdditiveArithmetic { static var placeholder: Any { get } }
87+
extension Witness: AnyAdditiveArithmetic where Value: AdditiveArithmetic {
88+
fileprivate static var placeholder: Any { Value.placeholder }
89+
}
90+
91+
private protocol AnyExpressibleByArrayLiteral { static var placeholder: Any { get } }
92+
extension Witness: AnyExpressibleByArrayLiteral where Value: ExpressibleByArrayLiteral {
93+
fileprivate static var placeholder: Any { Value.placeholder }
94+
}
95+
96+
private protocol AnyExpressibleByBooleanLiteral { static var placeholder: Any { get } }
97+
extension Witness: AnyExpressibleByBooleanLiteral where Value: ExpressibleByBooleanLiteral {
98+
fileprivate static var placeholder: Any { Value.placeholder }
99+
}
100+
101+
private protocol AnyExpressibleByDictionaryLiteral { static var placeholder: Any { get } }
102+
extension Witness: AnyExpressibleByDictionaryLiteral where Value: ExpressibleByDictionaryLiteral {
103+
fileprivate static var placeholder: Any { Value.placeholder }
104+
}
105+
106+
private protocol AnyExpressibleByFloatLiteral { static var placeholder: Any { get } }
107+
extension Witness: AnyExpressibleByFloatLiteral where Value: ExpressibleByFloatLiteral {
108+
fileprivate static var placeholder: Any { Value.placeholder }
109+
}
110+
111+
private protocol AnyExpressibleByIntegerLiteral { static var placeholder: Any { get } }
112+
extension Witness: AnyExpressibleByIntegerLiteral where Value: ExpressibleByIntegerLiteral {
113+
fileprivate static var placeholder: Any { Value.placeholder }
114+
}
115+
116+
private protocol AnyExpressibleByUnicodeScalarLiteral { static var placeholder: Any { get } }
117+
extension Witness: AnyExpressibleByUnicodeScalarLiteral
118+
where Value: ExpressibleByUnicodeScalarLiteral {
119+
fileprivate static var placeholder: Any { Value.placeholder }
120+
}
121+
122+
private protocol AnyRangeReplaceableCollection { static var placeholder: Any { get } }
123+
extension Witness: AnyRangeReplaceableCollection where Value: RangeReplaceableCollection {
124+
fileprivate static var placeholder: Any { Value.placeholder }
125+
}
126+
127+
private protocol AnyRawRepresentable { static var possiblePlaceholder: Any? { get } }
128+
extension Witness: AnyRawRepresentable where Value: RawRepresentable {
129+
fileprivate static var possiblePlaceholder: Any? {
130+
(_placeholder() as Value.RawValue?).flatMap(Value.init(rawValue:))
131+
}
132+
}
133+
134+
private protocol AnyCaseIterable { static var firstCase: Any? { get } }
135+
extension Witness: AnyCaseIterable where Value: CaseIterable {
136+
fileprivate static var firstCase: Any? {
137+
Value.allCases.first
138+
}
139+
}
140+
141+
#endif
142+
143+
func _generatePlaceholder<Result>() -> Result? {
144+
if let result = _placeholder() as Result? {
145+
return result
146+
}
147+
148+
if let result = _rawRepresentable() as Result? {
149+
return result
150+
}
151+
152+
if let result = _caseIterable() as Result? {
153+
return result
154+
}
155+
156+
return nil
157+
}

Sources/XCTestDynamicOverlay/Unimplemented.swift

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -543,17 +543,6 @@ func _fail(_ description: String, _ parameters: Any?, fileID: StaticString, line
543543
)
544544
}
545545

546-
func _generatePlaceholder<Result>() -> Result? {
547-
if Result.self == Void.self {
548-
return () as? Result
549-
}
550-
if let result = (Witness<Result>.self as? AnyRangeReplaceableCollection.Type)?.empty() as? Result
551-
{
552-
return result
553-
}
554-
return nil
555-
}
556-
557546
func _unimplementedFatalError(_ message: String, file: StaticString, line: UInt) -> Never {
558547
fatalError(
559548
"""
@@ -565,13 +554,3 @@ func _unimplementedFatalError(_ message: String, file: StaticString, line: UInt)
565554
line: line
566555
)
567556
}
568-
569-
protocol AnyRangeReplaceableCollection {
570-
static func empty() -> Any
571-
}
572-
enum Witness<Value> {}
573-
extension Witness: AnyRangeReplaceableCollection where Value: RangeReplaceableCollection {
574-
static func empty() -> Any {
575-
Value()
576-
}
577-
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#if !os(Linux)
2+
import Foundation
3+
import XCTest
4+
import XCTestDynamicOverlay
5+
6+
#if canImport(FoundationNetworking)
7+
import FoundationNetworking
8+
#endif
9+
10+
final class GeneratePlaceholderTests: XCTestCase {
11+
func testShouldGeneratePlaceholder() async throws {
12+
let bool: () -> Bool = unimplemented("bool")
13+
XCTAssertEqual(XCTExpectFailure(failingBlock: bool), false)
14+
let double: () -> Double = unimplemented("double")
15+
XCTAssertEqual(XCTExpectFailure(failingBlock: double), 0.0)
16+
let int: () -> Int = unimplemented("int")
17+
XCTAssertEqual(XCTExpectFailure(failingBlock: int), 0)
18+
let string: () -> String = unimplemented("string")
19+
XCTAssertEqual(XCTExpectFailure(failingBlock: string), "")
20+
21+
let array: () -> [Int] = unimplemented("array")
22+
XCTAssertEqual(XCTExpectFailure(failingBlock: array), [Int]())
23+
let dictionary: () -> [String: Int] = unimplemented("dictionary")
24+
XCTAssertEqual(XCTExpectFailure(failingBlock: dictionary), [String: Int]())
25+
let set: () -> Set<Int> = unimplemented("set")
26+
XCTAssertEqual(XCTExpectFailure(failingBlock: set), Set<Int>())
27+
28+
let stream: () -> AsyncStream<Int> = unimplemented("stream")
29+
for await _ in XCTExpectFailure(failingBlock: stream) {
30+
XCTFail("Stream should be finished")
31+
}
32+
33+
let throwingStream: () -> AsyncThrowingStream<Int, Error> = unimplemented("throwingStream")
34+
let result = await Task {
35+
try await XCTExpectFailure(failingBlock: throwingStream).first(where: { _ in true })
36+
}.result
37+
XCTAssertThrowsError(try result.get()) { XCTAssertTrue($0 is CancellationError) }
38+
39+
let date: () -> Date = unimplemented("date")
40+
XCTAssertNotNil(XCTExpectFailure(failingBlock: date))
41+
let url: () -> URL = unimplemented("url")
42+
XCTAssertNotNil(XCTExpectFailure(failingBlock: url))
43+
let uuid: () -> UUID = unimplemented("uuid")
44+
XCTAssertNotNil(XCTExpectFailure(failingBlock: uuid))
45+
46+
let enumCaseIterable: () -> EnumCaseIterable = unimplemented("enumCaseIterable")
47+
XCTAssertEqual(XCTExpectFailure(failingBlock: enumCaseIterable), .first)
48+
let enumInt: () -> EnumInt = unimplemented("enumInt")
49+
XCTAssertEqual(XCTExpectFailure(failingBlock: enumInt), .zero)
50+
let taggedInt: () -> Tagged<Self, Int> = unimplemented("taggedInt")
51+
XCTAssertEqual(XCTExpectFailure(failingBlock: taggedInt), 0)
52+
}
53+
}
54+
55+
enum EnumInt: Int { case zero, one, two }
56+
57+
enum EnumCaseIterable: CaseIterable { case first, second, third }
58+
59+
struct Tagged<Tag, RawValue: Equatable>: Equatable { var rawValue: RawValue }
60+
extension Tagged: ExpressibleByIntegerLiteral where RawValue: ExpressibleByIntegerLiteral {
61+
typealias IntegerLiteralType = RawValue.IntegerLiteralType
62+
init(integerLiteral value: IntegerLiteralType) {
63+
self.init(rawValue: RawValue(integerLiteral: value))
64+
}
65+
}
66+
#endif

0 commit comments

Comments
 (0)