A fast, safe, and efficient CBOR serialization library for Swift on any platform.
swiftpackageindex.com/thecoolwinter/CBOR/1.1.1/documentation/cbor
atproto
swift
cbor
1//
2// DecodableTests.swift
3// CBOR
4//
5// Created by Khan Winter on 8/20/25.
6//
7
8import Testing
9#if canImport(FoundationEssentials)
10import FoundationEssentials
11#else
12import Foundation
13#endif
14@testable import CBOR
15
16@Suite
17struct DecodableTests {
18 @Test
19 func uint8() throws {
20 var value: UInt8 = try CBORDecoder().decode(UInt8.self, from: [0])
21 #expect(value == 0)
22 value = try CBORDecoder().decode(UInt8.self, from: [1])
23 #expect(value == 1)
24 // Just below max arg size
25 value = try CBORDecoder().decode(UInt8.self, from: [23])
26 #expect(value == 23)
27 // Just above max arg size
28// value = try CBORDecoder().decode(UInt8.self, from: [24, 24])
29// #expect(value == 24)
30 // Max Int
31 value = try CBORDecoder().decode(UInt8.self, from: [24, UInt8.max])
32 #expect(value == UInt8.max)
33
34 #expect(throws: DecodingError.self) { try CBORDecoder().decode(UInt8.self, from: [128]) }
35 }
36
37 @Test
38 func int8() throws {
39 #expect(throws: DecodingError.self) { try CBORDecoder().decode(Int8.self, from: [24, 255]) }
40 }
41
42 @Test
43 func uint16() throws {
44 var value = try CBORDecoder().decode(UInt16.self, from: [0])
45 #expect(value == 0)
46 value = try CBORDecoder().decode(UInt16.self, from: [1])
47 #expect(value == 1)
48 // Just below max arg size
49 value = try CBORDecoder().decode(UInt16.self, from: [23])
50 #expect(value == 23)
51 // Just above max arg size
52 #expect(throws: DecodingError.self) { try CBORDecoder().decode(UInt16.self, from: [24, 24]) }
53
54 #expect(throws: DecodingError.self) { try CBORDecoder().decode(UInt16.self, from: [128]) }
55
56 #expect(throws: DecodingError.self) { try CBORDecoder().decode(UInt16.self, from: [25, 0, 1]) }
57
58 value = try CBORDecoder().decode(UInt16.self, from: [25, 1, 1])
59 #expect(value == 257)
60 value = try CBORDecoder().decode(UInt16.self, from: [25, UInt8.max, UInt8.max])
61 #expect(value == UInt16.max)
62 // Missing bytes from end
63 #expect(throws: DecodingError.self) { try CBORDecoder().decode(UInt16.self, from: [25]) }
64 #expect(throws: DecodingError.self) { try CBORDecoder().decode(UInt16.self, from: [25, 0]) }
65 }
66
67 @Test
68 func uint32() throws {
69 var value: UInt32 = try CBORDecoder().decode(UInt32.self, from: [0])
70 #expect(value == 0)
71 value = try CBORDecoder().decode(UInt32.self, from: [1])
72 #expect(value == 1)
73 // Just below max arg size
74 value = try CBORDecoder().decode(UInt32.self, from: [23])
75 #expect(value == 23)
76
77 value = try CBORDecoder().decode(UInt32.self, from: [25, 1, 1])
78 #expect(value == 257)
79 value = try CBORDecoder().decode(UInt32.self, from: [25, UInt8.max, UInt8.max])
80 #expect(value == UInt16.max)
81 // Missing bytes from end
82 #expect(throws: DecodingError.self) { try CBORDecoder().decode(UInt32.self, from: [25]) }
83 #expect(throws: DecodingError.self) { try CBORDecoder().decode(UInt32.self, from: [25, 0]) }
84
85 #expect(throws: DecodingError.self) { try CBORDecoder().decode(UInt32.self, from: [26, 0, 0, 0, 0]) }
86 #expect(throws: DecodingError.self) { try CBORDecoder().decode(UInt32.self, from: [26, 0, 0, 0, 1]) }
87 #expect(throws: DecodingError.self) { try CBORDecoder().decode(UInt32.self, from: [26, 0, 0, 1, 1]) }
88
89 value = try CBORDecoder().decode(UInt32.self, from: [26, 0, 1, 1, 1])
90 #expect(value == 65793)
91
92 value = try CBORDecoder().decode(UInt32.self, from: [26, UInt8.max, UInt8.max, UInt8.max, UInt8.max])
93 #expect(value == UInt32.max)
94 // Missing bytes from end
95 #expect(throws: DecodingError.self) { try CBORDecoder().decode(UInt32.self, from: [26]) }
96 #expect(throws: DecodingError.self) { try CBORDecoder().decode(UInt32.self, from: [26, 0, 0, 0]) }
97 }
98
99 @Test
100 func uint64() throws {
101 var value: UInt64 = try CBORDecoder().decode(UInt64.self, from: [0])
102 #expect(value == 0)
103 value = try CBORDecoder().decode(UInt64.self, from: [1])
104 #expect(value == 1)
105 // Just below max arg size
106 value = try CBORDecoder().decode(UInt64.self, from: [23])
107 #expect(value == 23)
108
109 value = try CBORDecoder().decode(UInt64.self, from: [25, 1, 1])
110 #expect(value == 257)
111 value = try CBORDecoder().decode(UInt64.self, from: [25, UInt8.max, UInt8.max])
112 #expect(value == UInt16.max)
113 // Missing bytes from end
114 #expect(throws: DecodingError.self) { try CBORDecoder().decode(UInt64.self, from: [25]) }
115 #expect(throws: DecodingError.self) { try CBORDecoder().decode(UInt64.self, from: [25, 0]) }
116
117 #expect(throws: DecodingError.self) { try CBORDecoder().decode(UInt64.self, from: [26, 0, 0, 0, 0]) }
118 #expect(throws: DecodingError.self) { try CBORDecoder().decode(UInt64.self, from: [26, 0, 0, 0, 1]) }
119 #expect(throws: DecodingError.self) { try CBORDecoder().decode(UInt64.self, from: [26, 0, 0, 1, 1]) }
120
121 value = try CBORDecoder().decode(UInt64.self, from: [26, 0, 1, 1, 1])
122 #expect(value == 65793)
123 value = try CBORDecoder().decode(UInt64.self, from: [26, UInt8.max, UInt8.max, UInt8.max, UInt8.max])
124 #expect(value == UInt32.max)
125 // Missing bytes from end
126 #expect(throws: DecodingError.self) { try CBORDecoder().decode(UInt64.self, from: [26]) }
127 #expect(throws: DecodingError.self) { try CBORDecoder().decode(UInt64.self, from: [26, 0, 0, 0]) }
128
129 #expect(throws: DecodingError.self) {
130 try CBORDecoder().decode(UInt64.self, from: [27, 0, 0, 0, 0, 0, 0, 0, 0])
131 }
132 #expect(throws: DecodingError.self) {
133 try CBORDecoder().decode(UInt64.self, from: [27, 0, 0, 0, 0, 0, 0, 0, 1])
134 }
135 #expect(throws: DecodingError.self) {
136 try CBORDecoder().decode(UInt64.self, from: [27, 0, 0, 0, 0, 0, 0, 1, 1])
137 }
138
139 // Missing bytes from end
140 #expect(throws: DecodingError.self) { try CBORDecoder().decode(UInt64.self, from: [27]) }
141 #expect(throws: DecodingError.self) {
142 try CBORDecoder().decode(UInt64.self, from: [27, 0, 0, 0, 0, 0, 0, 0])
143 }
144 }
145
146 @available(macOS 15.0, *)
147 @Test
148 func minInt() throws {
149 let data = "3bffffffffffffffff".asHexData()
150 let decoded = try CBORDecoder().decode(Int128.self, from: data)
151 #expect(decoded == -18446744073709551616)
152 }
153
154 @Test
155 func float() throws {
156 let expected: Float = 100000.0
157 let data = "fa47c35000".asHexData()
158 let decoded = try CBORDecoder().decode(Float.self, from: data)
159 #expect(decoded == expected)
160 }
161
162 @Test
163 func double() throws {
164 let expected: Double = 0.10035
165 let data = "FB3FB9B089A0275254".asHexData()
166 let decoded = try CBORDecoder().decode(Double.self, from: data)
167 #expect(decoded == expected)
168 }
169
170 @Test(arguments: [
171 ("6b68656c6c6f20776f726c64", "hello world"),
172 ("60", ""),
173 ("66e29da4efb88f", "❤️"),
174 // swiftlint:disable:next line_length
175 ("782F68656C6C6F20776F726C642068656C6C6F20776F726C642068656C6C6F20776F726C642068656C6C6F20776F726C64", "hello world hello world hello world hello world")
176 ])
177 func string(data: String, expected: String) throws {
178 let data = data.asHexData()
179 let string = try CBORDecoder().decode(String.self, from: data)
180 #expect(string == expected)
181 }
182
183 @Test(arguments: [
184 ("7FFF", ""),
185 ("7F61416142FF", "AB")
186 ])
187 func indeterminateString(data: String, expected: String) throws {
188 let data = data.asHexData()
189 let string = try CBORDecoder(rejectIndeterminateLengths: false)
190 .decode(String.self, from: data)
191 #expect(string == expected)
192 }
193
194 @Test
195 func emptyMap() throws {
196 let data = "A0".asHexData()
197 let dictionary = try CBORDecoder().decode([String: Int].self, from: data)
198 #expect(dictionary.isEmpty)
199 }
200
201 @Test
202 func simpleMap() throws {
203 let data = "A262414201614102".asHexData()
204 let dictionary = try CBORDecoder().decode([String: Int].self, from: data)
205 #expect(dictionary == ["AB": 1, "A": 2])
206 }
207
208 @Test
209 func unkeyedContainerHasCountForIndeterminate() throws {
210 let data = "9F0203FF".asHexData()
211 try data.withUnsafeBytes {
212 let data = $0[...]
213 let reader = DataReader(data: data)
214 let options = DecodingOptions(rejectIndeterminateLengths: false)
215 let scanner = CBORScanner(data: reader, options: options)
216 let results = try scanner.scan()
217
218 let context = DecodingContext(options: options, results: results)
219 let container = SingleValueCBORDecodingContainer(
220 context: context,
221 data: results.load(at: 0)
222 )
223
224 var unkeyedContainer = try container.unkeyedContainer()
225 #expect(unkeyedContainer.isAtEnd == false)
226 #expect(unkeyedContainer.count == 2)
227 #expect(unkeyedContainer.currentIndex == 0)
228
229 let key1 = try unkeyedContainer.decode(Int.self)
230 let key2 = try unkeyedContainer.decode(Int.self)
231 #expect(key1 == 2)
232 #expect(key2 == 3)
233
234 #expect(unkeyedContainer.isAtEnd)
235 #expect(unkeyedContainer.currentIndex == 2)
236 }
237 }
238
239 @Test
240 func array() throws {
241 let twentyItems = "940101010101010101010101010101010101010101".asHexData()
242 #expect(try CBORDecoder().decode([Int].self, from: twentyItems) == Array(repeating: 1, count: 20))
243 }
244
245 @Test
246 func indeterminateArray() throws {
247 let array = "9F0203FF".asHexData()
248 #expect(try CBORDecoder(rejectIndeterminateLengths: false).decode([Int].self, from: array) == [2, 3])
249
250 let twodArray = "9F9F0203FF9F0405FFFF".asHexData()
251 let result = try CBORDecoder(rejectIndeterminateLengths: false).decode([[Int]].self, from: twodArray)
252 #expect(result == [[2, 3], [4, 5]])
253 }
254
255 @Test
256 func rejectsIndeterminateArrayWhenConfigured() throws {
257 let array = "9FFF".asHexData()
258 #expect(throws: DecodingError.self) {
259 try CBORDecoder(rejectIndeterminateLengths: true).decode([Int].self, from: array)
260 }
261
262 }
263
264 @Test
265 func emptyData() throws {
266 let data = Data()
267 #expect(throws: DecodingError.self) { try CBORDecoder().decode(Data.self, from: data) }
268 }
269
270 @Test
271 func date() throws {
272 let data = "C11A5C295C00".asHexData()
273 let expected = Date(timeIntervalSince1970: 1546214400.0)
274 let value = try CBORDecoder().decode(Date.self, from: data)
275 #expect(value == expected)
276 }
277
278 @Test(arguments: [
279 ("F90000", 0),
280 ("F93C00", 1.0),
281 ("F9BE00", -1.5),
282 ("F97C00", .infinity),
283 ("F93E32", 1.548828125),
284 ("F9F021", -8456)
285 ])
286 func float16(data: String, value: Float) throws {
287 let data = data.asHexData()
288 let decoded = try CBORDecoder().decode(Float.self, from: data)
289 #expect(decoded == value)
290 }
291
292 @Test
293 func recursionLimit() {
294 #expect(throws: DecodingError.self) {
295 // swiftlint:disable:next line_length
296 let data = "a260818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818180652474797065726170702e62736b792e666565642e70736f74".asHexData()
297 return try CBORDecoder().decode(AnyDecodable.self, from: data)
298 }
299 }
300}