Skip to content

Commit 488c434

Browse files
committed
[JSON] Added some parse failure test
1 parent 278aea7 commit 488c434

File tree

2 files changed

+83
-7
lines changed

2 files changed

+83
-7
lines changed

Sources/SwiftCompilerPluginMessageHandling/JSON/JSONDecoding.swift

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,18 @@ import ucrt
2929
#endif
3030

3131
func decodeFromJSON<T: Decodable>(json: UnsafeBufferPointer<UInt8>) throws -> T {
32-
let map = try JSONScanner.scan(buffer: json)
32+
let map: JSONMap
33+
do {
34+
map = try JSONScanner.scan(buffer: json)
35+
} catch let err as JSONError {
36+
throw DecodingError.dataCorrupted(
37+
DecodingError.Context(
38+
codingPath: [],
39+
debugDescription: "Corrupted JSON",
40+
underlyingError: err
41+
)
42+
)
43+
}
3344
return try map.withValue { value in
3445
let decoder = JSONDecoding(value: value, codingPathNode: .root)
3546
return try T.init(from: decoder)
@@ -144,9 +155,19 @@ private struct JSONMapBuilder {
144155
}
145156
}
146157

147-
enum JSONError: Error {
158+
enum JSONError: Error, CustomDebugStringConvertible {
148159
case unexpectedEndOfFile
149160
case unexpectedCharacter(UInt8, context: String)
161+
162+
var debugDescription: String {
163+
switch self {
164+
case .unexpectedEndOfFile:
165+
return "unexpected end of file"
166+
case .unexpectedCharacter(let c, let ctxt):
167+
let char = c < 0x80 ? String(UnicodeScalar(c)) : "0x" + String(c, radix: 16, uppercase: true)
168+
return "unexpected character '\(char)'; \(ctxt)"
169+
}
170+
}
150171
}
151172

152173
private struct JSONScanner {
@@ -212,7 +233,7 @@ private struct JSONScanner {
212233
throw JSONError.unexpectedEndOfFile
213234
}
214235
guard ptr.pointee == UInt8(ascii: char) else {
215-
throw JSONError.unexpectedCharacter(ptr.pointee, context: "expected \(char)")
236+
throw JSONError.unexpectedCharacter(ptr.pointee, context: "expected '\(char)'")
216237
}
217238
ptr += 1
218239
}
@@ -764,7 +785,6 @@ extension JSONDecoding {
764785
codingPathNode: _CodingPathNode,
765786
_ additionalKey: (some CodingKey)?
766787
) throws -> T {
767-
try _checkNotNull(value, expectedType: type, for: codingPathNode, additionalKey)
768788
let decoder = Self(value: value, codingPathNode: codingPathNode.appending(additionalKey))
769789
return try T.init(from: decoder)
770790
}

Tests/SwiftCompilerPluginTest/JSONTests.swift

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,17 @@ import XCTest
1515

1616
final class JSONTests: XCTestCase {
1717

18+
func testPrimitive() {
19+
_testRoundTrip(of: true, expectedJSON: "true")
20+
_testRoundTrip(of: false, expectedJSON: "false")
21+
_testRoundTrip(of: Bool?.none, expectedJSON: "null")
22+
_testRoundTrip(of: "", expectedJSON: "\"\"")
23+
_testRoundTrip(of: 0, expectedJSON: "0")
24+
_testRoundTrip(of: 0 as Int8, expectedJSON: "0")
25+
_testRoundTrip(of: 0.0 as Float, expectedJSON: "0.0")
26+
_testRoundTrip(of: 0.0 as Double, expectedJSON: "0.0")
27+
}
28+
1829
func testEmptyStruct() {
1930
let value = EmptyStruct()
2031
_testRoundTrip(of: value, expectedJSON: "{}")
@@ -73,12 +84,12 @@ final class JSONTests: XCTestCase {
7384
data: [nil, 42]
7485
)
7586
],
76-
elapsed: 42.3
87+
elapsed: 42.3e32
7788
)
7889
_testRoundTrip(
7990
of: value,
8091
expectedJSON: #"""
81-
{"diagnostics":[{"animal":"cat","data":[null,42],"message":"error 🛑"}],"elapsed":42.3,"result":"\tresult\nfoo"}
92+
{"diagnostics":[{"animal":"cat","data":[null,42],"message":"error 🛑"}],"elapsed":4.23e+33,"result":"\tresult\nfoo"}
8293
"""#
8394
)
8495
}
@@ -92,6 +103,33 @@ final class JSONTests: XCTestCase {
92103
)
93104
}
94105

106+
func testParseError() {
107+
_assertParseError(
108+
#"{"foo": 1"#,
109+
message: "unexpected end of file"
110+
)
111+
_assertParseError(
112+
#""foo"#,
113+
message: "unexpected end of file"
114+
)
115+
_assertParseError(
116+
"\n",
117+
message: "unexpected end of file"
118+
)
119+
_assertParseError(
120+
"trua",
121+
message: "unexpected character 'a'; expected 'e'"
122+
)
123+
_assertParseError(
124+
"[true, #foo]",
125+
message: "unexpected character '#'; value start"
126+
)
127+
_assertParseError(
128+
"{}true",
129+
message: "unexpected character 't'; after top-level value"
130+
)
131+
}
132+
95133
func testTypeCoercion() {
96134
_testRoundTripTypeCoercionFailure(of: [false, true], as: [Int].self)
97135
_testRoundTripTypeCoercionFailure(of: [false, true], as: [Int8].self)
@@ -150,7 +188,25 @@ final class JSONTests: XCTestCase {
150188
let data = try JSONEncoder().encode(value)
151189
let _ = try JSONDecoder().decode(U.self, from: data)
152190
XCTFail("Coercion from \(T.self) to \(U.self) was expected to fail.")
153-
} catch {}
191+
} catch DecodingError.typeMismatch(_, _) {
192+
// Success
193+
} catch {
194+
XCTFail("unexpected error")
195+
}
196+
}
197+
198+
private func _assertParseError(_ json: String, message: String) {
199+
do {
200+
var json = json
201+
_ = try json.withUTF8 { try JSON.decode(Bool.self, from: $0) }
202+
} catch DecodingError.dataCorrupted(let context) {
203+
XCTAssertEqual(
204+
String(describing: try XCTUnwrap(context.underlyingError)),
205+
message
206+
)
207+
} catch {
208+
XCTFail("unexpected error")
209+
}
154210
}
155211
}
156212

0 commit comments

Comments
 (0)