Skip to content

Commit 816db7e

Browse files
authored
Better tests (#36)
1 parent cac9f05 commit 816db7e

13 files changed

+369
-718
lines changed

Sources/PureSwiftJSONCoding/Decoding/JSONDecoder.swift

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -39,39 +39,6 @@ public struct JSONDecoder {
3939
@inlinable public func decode<T: Decodable>(_ type: T.Type) throws -> T {
4040
return try T.init(from: self)
4141
}
42-
43-
internal func decoderForKey<Key: CodingKey>(_ key: Key) throws -> JSONDecoderImpl {
44-
45-
switch self.json {
46-
case .array(let array):
47-
guard let key = key as? ArrayKey else {
48-
preconditionFailure(
49-
"For arrays we use the ArrayKey type as the key within this package.")
50-
}
51-
let json = array[key.intValue!]
52-
var newPath = self.codingPath
53-
newPath.append(key)
54-
55-
return JSONDecoderImpl(
56-
userInfo : userInfo,
57-
from : json,
58-
codingPath: newPath)
59-
60-
case .object(let dictionary):
61-
let json = dictionary[key.stringValue]!
62-
var newPath = self.codingPath
63-
newPath.append(key)
64-
return JSONDecoderImpl(
65-
userInfo : userInfo,
66-
from : json,
67-
codingPath: newPath)
68-
69-
default:
70-
71-
#warning("we need good error handling here")
72-
preconditionFailure()
73-
}
74-
}
7542
}
7643

7744
extension JSONDecoderImpl: Decoder {

Sources/PureSwiftJSONCoding/Decoding/JSONKeyedDecodingContainer.swift

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,18 +98,18 @@ struct JSONKeyedDecodingContainer<K: CodingKey>: KeyedDecodingContainerProtocol
9898
}
9999

100100
func decode<T>(_ type: T.Type, forKey key: K) throws -> T where T : Decodable {
101-
let decoder = try impl.decoderForKey(key)
101+
let decoder = try self.decoderForKey(key)
102102
return try T.init(from: decoder)
103103
}
104104

105105
func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type, forKey key: K) throws
106106
-> KeyedDecodingContainer<NestedKey> where NestedKey : CodingKey
107107
{
108-
return try impl.decoderForKey(key).container(keyedBy: type)
108+
return try decoderForKey(key).container(keyedBy: type)
109109
}
110110

111111
func nestedUnkeyedContainer(forKey key: K) throws -> UnkeyedDecodingContainer {
112-
return try impl.decoderForKey(key).unkeyedContainer()
112+
return try decoderForKey(key).unkeyedContainer()
113113
}
114114

115115
func superDecoder() throws -> Decoder {
@@ -123,6 +123,17 @@ struct JSONKeyedDecodingContainer<K: CodingKey>: KeyedDecodingContainerProtocol
123123

124124
extension JSONKeyedDecodingContainer {
125125

126+
private func decoderForKey(_ key: K) throws -> JSONDecoderImpl {
127+
let value = try getValue(forKey: key)
128+
var newPath = self.codingPath
129+
newPath.append(key)
130+
131+
return JSONDecoderImpl(
132+
userInfo : impl.userInfo,
133+
from : value,
134+
codingPath: newPath)
135+
}
136+
126137
@inline(__always) private func getValue(forKey key: K) throws -> JSONValue {
127138
guard let value = self.dictionary[key.stringValue] else {
128139
throw DecodingError.keyNotFound(key, .init(

Sources/PureSwiftJSONCoding/Decoding/JSONUnkeyedDecodingContainer.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,11 @@ struct JSONUnkeyedDecodingContainer: UnkeyedDecodingContainer {
119119
}
120120
}
121121

122-
let newKey = ArrayKey(index: currentIndex)
123-
let decoder = try impl.decoderForKey(newKey)
124-
122+
let json = array[currentIndex]
123+
var newPath = self.codingPath
124+
newPath.append(ArrayKey(index: currentIndex))
125+
let decoder = JSONDecoderImpl(userInfo: impl.userInfo, from: json, codingPath: newPath)
126+
125127
return try T.init(from: decoder)
126128
}
127129

Sources/PureSwiftJSONParsing/JSONValue.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

2-
public enum JSONError: Swift.Error {
2+
public enum JSONError: Swift.Error, Equatable {
33
case unexpectedCharacter(ascii: UInt8, characterIndex: Int)
44
case unexpectedEndOfFile
55
case tooManyNestedArraysOrDictionaries(characterIndex: Int)

Tests/JSONCodingTests/Decoding/JSONKeyedDecodingContainerTests.swift

Lines changed: 83 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,18 @@ class JSONKeyedDecodingContainerTests: XCTestCase {
2222
}
2323
}
2424

25-
func testContains() throws {
25+
func testContains() {
2626
let impl = JSONDecoderImpl(userInfo: [:], from: .object(["hello": .null, "world": .null]), codingPath: [])
2727

2828
enum CodingKeys: String, CodingKey {
2929
case hello
3030
case haha
3131
}
3232

33-
let container = try impl.container(keyedBy: CodingKeys.self)
34-
XCTAssertEqual(container.contains(.hello), true)
35-
XCTAssertEqual(container.contains(.haha), false)
33+
var container: KeyedDecodingContainer<CodingKeys>?
34+
XCTAssertNoThrow(container = try impl.container(keyedBy: CodingKeys.self))
35+
XCTAssertEqual(try XCTUnwrap(container).contains(.hello), true)
36+
XCTAssertEqual(try XCTUnwrap(container).contains(.haha), false)
3637
}
3738

3839
// MARK: - Null -
@@ -43,19 +44,15 @@ class JSONKeyedDecodingContainerTests: XCTestCase {
4344
case hello
4445
}
4546

46-
do {
47-
let container = try impl.container(keyedBy: CodingKeys.self)
48-
let result = try container.decodeNil(forKey: .hello)
49-
XCTFail("Did not expect to get a result: \(result)")
50-
}
51-
catch Swift.DecodingError.keyNotFound(let codingKey, let context) {
52-
// expected
47+
var container: KeyedDecodingContainer<CodingKeys>?
48+
XCTAssertNoThrow(container = try impl.container(keyedBy: CodingKeys.self))
49+
XCTAssertThrowsError(try XCTUnwrap(container).decodeNil(forKey: .hello)) { (error) in
50+
guard case Swift.DecodingError.keyNotFound(let codingKey, let context) = error else {
51+
XCTFail("Unexpected error: \(error)"); return
52+
}
5353
XCTAssertEqual(codingKey as? CodingKeys, .hello)
5454
XCTAssertEqual(context.debugDescription, "No value associated with key CodingKeys(stringValue: \"hello\", intValue: nil) (\"hello\").")
5555
}
56-
catch {
57-
XCTFail("Unexpected error: \(error)")
58-
}
5956
}
6057

6158
func testDecodeNull() {
@@ -64,14 +61,11 @@ class JSONKeyedDecodingContainerTests: XCTestCase {
6461
case hello
6562
}
6663

67-
do {
68-
let container = try impl.container(keyedBy: CodingKeys.self)
69-
let result = try container.decodeNil(forKey: .hello)
70-
XCTAssertEqual(result, true)
71-
}
72-
catch {
73-
XCTFail("Unexpected error: \(error)")
74-
}
64+
var container: KeyedDecodingContainer<CodingKeys>?
65+
XCTAssertNoThrow(container = try impl.container(keyedBy: CodingKeys.self))
66+
var result: Bool?
67+
XCTAssertNoThrow(result = try XCTUnwrap(container).decodeNil(forKey: .hello))
68+
XCTAssertEqual(result, true)
7569
}
7670

7771
func testDecodeNullFromArray() {
@@ -80,14 +74,11 @@ class JSONKeyedDecodingContainerTests: XCTestCase {
8074
case hello
8175
}
8276

83-
do {
84-
let container = try impl.container(keyedBy: CodingKeys.self)
85-
let result = try container.decodeNil(forKey: .hello)
86-
XCTAssertEqual(result, false)
87-
}
88-
catch {
89-
XCTFail("Unexpected error: \(error)")
90-
}
77+
var container: KeyedDecodingContainer<CodingKeys>?
78+
XCTAssertNoThrow(container = try impl.container(keyedBy: CodingKeys.self))
79+
var result: Bool?
80+
XCTAssertNoThrow(result = try XCTUnwrap(container).decodeNil(forKey: .hello))
81+
XCTAssertEqual(result, false)
9182
}
9283

9384
// MARK: - String -
@@ -98,21 +89,17 @@ class JSONKeyedDecodingContainerTests: XCTestCase {
9889
case hello
9990
}
10091

101-
do {
102-
let container = try impl.container(keyedBy: CodingKeys.self)
103-
let result = try container.decode(String.self, forKey: .hello)
104-
XCTFail("Did not expect to get a result: \(result)")
105-
}
106-
catch Swift.DecodingError.typeMismatch(let type, let context) {
107-
// expected
92+
var container: KeyedDecodingContainer<CodingKeys>?
93+
XCTAssertNoThrow(container = try impl.container(keyedBy: CodingKeys.self))
94+
XCTAssertThrowsError(try XCTUnwrap(container).decode(String.self, forKey: .hello)) { (error) in
95+
guard case Swift.DecodingError.typeMismatch(let type, let context) = error else {
96+
XCTFail("Unexpected error: \(error)"); return
97+
}
10898
XCTAssertTrue(type == String.self)
10999
XCTAssertEqual(context.codingPath.count, 1)
110100
XCTAssertEqual(context.codingPath.first as? CodingKeys, CodingKeys.hello)
111101
XCTAssertEqual(context.debugDescription, "Expected to decode String but found a number instead.")
112102
}
113-
catch {
114-
XCTFail("Unexpected error: \(error)")
115-
}
116103
}
117104

118105
func testDecodeStringFromKeyNotFound() {
@@ -121,19 +108,15 @@ class JSONKeyedDecodingContainerTests: XCTestCase {
121108
case hello
122109
}
123110

124-
do {
125-
let container = try impl.container(keyedBy: CodingKeys.self)
126-
let result = try container.decode(String.self, forKey: .hello)
127-
XCTFail("Did not expect to get a result: \(result)")
128-
}
129-
catch Swift.DecodingError.keyNotFound(let codingKey, let context) {
130-
// expected
111+
var container: KeyedDecodingContainer<CodingKeys>?
112+
XCTAssertNoThrow(container = try impl.container(keyedBy: CodingKeys.self))
113+
XCTAssertThrowsError(try XCTUnwrap(container).decode(String.self, forKey: .hello)) { (error) in
114+
guard case Swift.DecodingError.keyNotFound(let codingKey, let context) = error else {
115+
XCTFail("Unexpected error: \(error)"); return
116+
}
131117
XCTAssertEqual(codingKey as? CodingKeys, .hello)
132118
XCTAssertEqual(context.debugDescription, "No value associated with key CodingKeys(stringValue: \"hello\", intValue: nil) (\"hello\").")
133119
}
134-
catch {
135-
XCTFail("Unexpected error: \(error)")
136-
}
137120
}
138121

139122
// MARK: - Bool -
@@ -144,21 +127,17 @@ class JSONKeyedDecodingContainerTests: XCTestCase {
144127
case hello
145128
}
146129

147-
do {
148-
let container = try impl.container(keyedBy: CodingKeys.self)
149-
let result = try container.decode(Bool.self, forKey: .hello)
150-
XCTFail("Did not expect to get a result: \(result)")
151-
}
152-
catch Swift.DecodingError.typeMismatch(let type, let context) {
153-
// expected
130+
var container: KeyedDecodingContainer<CodingKeys>?
131+
XCTAssertNoThrow(container = try impl.container(keyedBy: CodingKeys.self))
132+
XCTAssertThrowsError(try XCTUnwrap(container).decode(Bool.self, forKey: .hello)) { (error) in
133+
guard case Swift.DecodingError.typeMismatch(let type, let context) = error else {
134+
XCTFail("Unexpected error: \(error)"); return
135+
}
154136
XCTAssertTrue(type == Bool.self)
155137
XCTAssertEqual(context.codingPath.count, 1)
156138
XCTAssertEqual(context.codingPath.first as? CodingKeys, CodingKeys.hello)
157139
XCTAssertEqual(context.debugDescription, "Expected to decode Bool but found a number instead.")
158140
}
159-
catch {
160-
XCTFail("Unexpected error: \(error)")
161-
}
162141
}
163142

164143
// MARK: - Integers -
@@ -170,46 +149,36 @@ class JSONKeyedDecodingContainerTests: XCTestCase {
170149
case hello
171150
}
172151

173-
do {
174-
let container = try impl.container(keyedBy: CodingKeys.self)
175-
let result = try container.decode(UInt8.self, forKey: .hello)
176-
XCTFail("Did not expect to get a result: \(result)")
177-
}
178-
catch Swift.DecodingError.dataCorrupted(let context) {
179-
// expected
152+
var container: KeyedDecodingContainer<CodingKeys>?
153+
XCTAssertNoThrow(container = try impl.container(keyedBy: CodingKeys.self))
154+
XCTAssertThrowsError(try XCTUnwrap(container).decode(UInt8.self, forKey: .hello)) { (error) in
155+
guard case Swift.DecodingError.dataCorrupted(let context) = error else {
156+
XCTFail("Unexpected error: \(error)"); return
157+
}
180158
XCTAssertEqual(context.codingPath.count, 1)
181159
XCTAssertEqual(context.codingPath.first as? CodingKeys, .hello)
182160
XCTAssertEqual(context.debugDescription, "Parsed JSON number <\(number)> does not fit in UInt8.")
183161
}
184-
catch {
185-
XCTFail("Unexpected error: \(error)")
186-
}
187162
}
188163

189164
func testGetUInt8FromFloat() {
190165
let number = -3.14
191166
let type = UInt8.self
192-
193167
enum CodingKeys: String, CodingKey {
194168
case hello
195169
}
196170

197171
let impl = JSONDecoderImpl(userInfo: [:], from: .object(["hello": .number("\(number)")]), codingPath: [])
198-
199-
do {
200-
let container = try impl.container(keyedBy: CodingKeys.self)
201-
let result = try container.decode(type.self, forKey: .hello)
202-
XCTFail("Did not expect to get a result: \(result)")
203-
}
204-
catch Swift.DecodingError.dataCorrupted(let context) {
205-
// expected
172+
var container: KeyedDecodingContainer<CodingKeys>?
173+
XCTAssertNoThrow(container = try impl.container(keyedBy: CodingKeys.self))
174+
XCTAssertThrowsError(try XCTUnwrap(container).decode(type, forKey: .hello)) { (error) in
175+
guard case Swift.DecodingError.dataCorrupted(let context) = error else {
176+
XCTFail("Unexpected error: \(error)"); return
177+
}
206178
XCTAssertEqual(context.codingPath.count, 1)
207179
XCTAssertEqual(context.codingPath.first as? CodingKeys, .hello)
208180
XCTAssertEqual(context.debugDescription, "Parsed JSON number <\(number)> does not fit in UInt8.")
209181
}
210-
catch {
211-
XCTFail("Unexpected error: \(error)")
212-
}
213182
}
214183

215184
func testGetUInt8TypeMismatch() {
@@ -220,22 +189,17 @@ class JSONKeyedDecodingContainerTests: XCTestCase {
220189
}
221190

222191
let impl = JSONDecoderImpl(userInfo: [:], from: .object(["hello": .bool(false)]), codingPath: [])
223-
224-
do {
225-
let container = try impl.container(keyedBy: CodingKeys.self)
226-
let result = try container.decode(type.self, forKey: .hello)
227-
XCTFail("Did not expect to get a result: \(result)")
228-
}
229-
catch Swift.DecodingError.typeMismatch(let type, let context) {
230-
// expected
192+
var container: KeyedDecodingContainer<CodingKeys>?
193+
XCTAssertNoThrow(container = try impl.container(keyedBy: CodingKeys.self))
194+
XCTAssertThrowsError(try XCTUnwrap(container).decode(type, forKey: .hello)) { (error) in
195+
guard case Swift.DecodingError.typeMismatch(let type, let context) = error else {
196+
XCTFail("Unexpected error: \(error)"); return
197+
}
231198
XCTAssertTrue(type == UInt8.self)
232199
XCTAssertEqual(context.codingPath.count, 1)
233200
XCTAssertEqual(context.codingPath.first as? CodingKeys, .hello)
234201
XCTAssertEqual(context.debugDescription, "Expected to decode UInt8 but found bool instead.")
235202
}
236-
catch {
237-
XCTFail("Unexpected error: \(error)")
238-
}
239203
}
240204

241205
func testGetUInt8Success() {
@@ -551,4 +515,30 @@ class JSONKeyedDecodingContainerTests: XCTestCase {
551515
XCTFail("Unexpected error: \(error)")
552516
}
553517
}
518+
519+
func testCantCreateSubDecoderForKey() {
520+
struct Test: Decodable {
521+
struct SubTest: Decodable {
522+
let hello: String
523+
}
524+
525+
let sub: SubTest
526+
527+
enum CodingKeys: String, CodingKey {
528+
case sub
529+
}
530+
}
531+
532+
let impl = JSONDecoderImpl(userInfo: [:], from: .object(["hello": .bool(false)]), codingPath: [])
533+
534+
XCTAssertThrowsError(_ = try Test(from: impl)) { (error) in
535+
guard case Swift.DecodingError.keyNotFound(let codingKey, let context) = error else {
536+
XCTFail("Unexpected error: \(error)"); return
537+
}
538+
XCTAssertEqual(codingKey as? Test.CodingKeys, .sub)
539+
XCTAssertEqual(context.debugDescription, "No value associated with key CodingKeys(stringValue: \"sub\", intValue: nil) (\"sub\").")
540+
}
541+
542+
}
543+
554544
}

0 commit comments

Comments
 (0)