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
at main 278 lines 11 kB view raw
1// 2// EncodableTests.swift 3// SwiftCBOR 4// 5// Created by Khan Winter on 8/17/25. 6// 7 8import Testing 9#if canImport(FoundationEssentials) 10import FoundationEssentials 11#else 12import Foundation 13#endif 14@testable import CBOR 15 16@Suite("EncodableTests") 17struct EncodableTests { 18 @Test 19 func encodeNull() throws { 20 let encoded = try CBOREncoder().encode(String?(nil)) 21 #expect(encoded == Data([0xf6])) 22 } 23 24 @Test 25 func encodeBools() throws { 26 let falseVal = try CBOREncoder().encode(false) 27 #expect(falseVal == Data([0xf4])) 28 29 let trueVal = try CBOREncoder().encode(true) 30 #expect(trueVal == Data([0xf5])) 31 } 32 33 @Test 34 func encodeInts() throws { 35 // Less than 24 36 let zero = try CBOREncoder().encode(0) 37 #expect(zero == Data([0x00])) 38 39 let eight = try CBOREncoder().encode(8) 40 #expect(eight == Data([0x08])) 41 42 let ten = try CBOREncoder().encode(10) 43 #expect(ten == Data([0x0a])) 44 45 let twentyThree = try CBOREncoder().encode(23) 46 #expect(twentyThree == Data([0x17])) 47 48 // Just bigger than 23 49 let twentyFour = try CBOREncoder().encode(24) 50 #expect(twentyFour == Data([0x18, 0x18])) 51 52 let twentyFive = try CBOREncoder().encode(25) 53 #expect(twentyFive == Data([0x18, 0x19])) 54 55 // Bigger 56 let hundred = try CBOREncoder().encode(100) 57 #expect(hundred == Data([0x18, 0x64])) 58 59 let thousand = try CBOREncoder().encode(1_000) 60 #expect(thousand == Data([0x19, 0x03, 0xe8])) 61 62 let million = try CBOREncoder().encode(1_000_000) 63 #expect(million == Data([0x1a, 0x00, 0x0f, 0x42, 0x40])) 64 65 let trillion = try CBOREncoder().encode(1_000_000_000_000) 66 #expect(trillion == Data([0x1b, 0x00, 0x00, 0x00, 0xe8, 0xd4, 0xa5, 0x10, 0x00])) 67 } 68 69 @Test 70 func encodeNegativeInts() throws { 71 // Less than 24 72 let minusOne = try CBOREncoder().encode(-1) 73 #expect(minusOne == Data([0x20])) 74 75 let minusTen = try CBOREncoder().encode(-10) 76 #expect(minusTen == Data([0x29])) 77 78 // Bigger 79 let minusHundred = try CBOREncoder().encode(-100) 80 #expect(minusHundred == Data([0x38, 0x63])) 81 82 let minusThousand = try CBOREncoder().encode(-1_000) 83 #expect(minusThousand == Data([0x39, 0x03, 0xe7])) 84 85 let minusMillion = try CBOREncoder().encode(-1_000_001) 86 #expect(minusMillion == Data([0x3A, 0x00, 0x0F, 0x42, 0x40])) 87 88 let minusTrillion = try CBOREncoder().encode(-1_000_000_001) 89 #expect(minusTrillion == Data([0x3A, 0x3B, 0x9A, 0xCA, 0x00])) 90 } 91 92 @Test 93 func encodeFloat() throws { 94 let value: Float = 100000.0 95 let data = "fa47c35000".asHexData() 96 let result = try CBOREncoder().encode(value) 97 #expect(data == result) 98 } 99 100 @Test 101 func encodeDouble() throws { 102 let value: Double = 0.10035 103 let data = "FB3FB9B089A0275254".asHexData() 104 let result = try CBOREncoder().encode(value) 105 #expect(data == result) 106 } 107 108 @Test 109 func encodeStrings() throws { 110 let empty = try CBOREncoder().encode("") 111 #expect(empty == Data([0x60])) 112 113 let a = try CBOREncoder().encode("a") 114 #expect(a == Data([0x61, 0x61])) 115 116 let IETF = try CBOREncoder().encode("IETF") 117 #expect(IETF == Data([0x64, 0x49, 0x45, 0x54, 0x46])) 118 119 let quoteSlash = try CBOREncoder().encode("\"\\") 120 #expect(quoteSlash == Data([0x62, 0x22, 0x5c])) 121 122 let littleUWithDiaeresis = try CBOREncoder().encode("\u{00FC}") 123 #expect(littleUWithDiaeresis == Data([0x62, 0xc3, 0xbc])) 124 } 125 126 @Test 127 func encodeByteStrings() throws { 128 let fourByteByteString = try CBOREncoder().encode(Data([0x01, 0x02, 0x03, 0x04])) 129 #expect(fourByteByteString == Data([0x44, 0x01, 0x02, 0x03, 0x04])) 130 } 131 132 @Test 133 func mixedByteArraysEncodeCorrectly() throws { 134 // TODO: Make the container swap to mixed mode if necessary. 135 136 /// See note in ``UnkeyeyedCBOREncodingContainer`` about mixed collections of ints 137 struct Mixed: Encodable { 138 func encode(to encoder: any Encoder) throws { 139 var container = encoder.unkeyedContainer() 140 try container.encode(UInt8.zero) 141 try container.encode(UInt8.max) 142 try container.encode("Hello World") 143 try container.encode(1000000) 144 } 145 } 146 147 let data = try CBOREncoder().encode(Mixed()) 148 // swiftlint:disable:next line_length 149 #expect(data == Data([0x84, 0x00, 0x18, 0xff, 0x6b, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x1a, 0x00, 0x0f, 0x42, 0x40])) 150 } 151 152 @Test 153 func encodeArrays() throws { 154 let empty = try CBOREncoder().encode([String]()) 155 #expect(empty == Data([0x80])) 156 157 let oneTwoThree = try CBOREncoder().encode([1, 2, 3]) 158 #expect(oneTwoThree == Data([0x83, 0x01, 0x02, 0x03])) 159 160 // swiftlint:disable:next line_length 161 let lotsOfInts = try CBOREncoder().encode([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]) 162 163 // swiftlint:disable:next line_length 164 #expect(lotsOfInts == Data([0x98, 0x19, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x18, 0x18, 0x19])) 165 166 let nestedSimple = try CBOREncoder().encode([[1], [2, 3], [4, 5]]) 167 #expect(nestedSimple == Data([0x83, 0x81, 0x01, 0x82, 0x02, 0x03, 0x82, 0x04, 0x05])) 168 } 169 170 @Test 171 func encodeMaps() throws { 172 let empty = try CBOREncoder().encode([String: String]()) 173 #expect(empty == Data([0xa0])) 174 175 let stringToString = try CBOREncoder().encode(["a": "A", "b": "B", "c": "C", "d": "D", "e": "E"]) 176 #expect(stringToString.first! == 0xa5) 177 178 let dataMinusFirstByte = stringToString[1...] 179 .map { $0 } 180 .chunked(into: 4) 181 .sorted(by: { $0.lexicographicallyPrecedes($1) }) 182 let dataForKeyValuePairs: [[UInt8]] = [ 183 [0x61, 0x61, 0x61, 0x41], 184 [0x61, 0x62, 0x61, 0x42], 185 [0x61, 0x63, 0x61, 0x43], 186 [0x61, 0x64, 0x61, 0x44], 187 [0x61, 0x65, 0x61, 0x45] 188 ] 189 #expect(dataMinusFirstByte == dataForKeyValuePairs) 190 191 let oneTwoThreeFour = try CBOREncoder().encode([1: 2, 3: 4]) 192 #expect( 193 oneTwoThreeFour == Data([0xa2, 0x01, 0x02, 0x03, 0x04]) || 194 oneTwoThreeFour == Data([0xa2, 0x03, 0x04, 0x01, 0x02]) 195 ) 196 197 let encoder = CBOREncoder(forceStringKeys: true) 198 let encodedWithStringKeys = try encoder.encode([1: 2, 3: 4]) 199 #expect( 200 encodedWithStringKeys == Data([0xa2, 0x61, 0x31, 0x02, 0x61, 0x33, 0x04]) 201 ) 202 } 203 204 @Test 205 func encodeSimpleStructs() throws { 206 struct MyStruct: Codable { 207 let age: Int 208 let name: String 209 } 210 211 let encoded = try CBOREncoder().encode(MyStruct(age: 27, name: "Ham")).map { $0 } 212 213 #expect( 214 encoded == [0xa2, 0x63, 0x61, 0x67, 0x65, 0x18, 0x1b, 0x64, 0x6e, 0x61, 0x6d, 0x65, 0x63, 0x48, 0x61, 0x6d] 215 ) 216 } 217 218 @Test 219 func encodeMoreComplexStructs() throws { 220 let encoder = CBOREncoder() 221 222 let data = try encoder.encode(Company.mock).hexString().uppercased() 223 // swiftlint:disable:next line_length 224 #expect(data == "A4646E616D656941636D6520436F727067666F756E6465641907CF686D65746164617461A268696E6475737472796474656368686C6F636174696F6E6672656D6F746569656D706C6F796565738AA563616765181E646E616D6565416C6963656474616773836573776966746463626F726962656E63686D61726B65656D61696C71616C696365406578616D706C652E636F6D686973416374697665F5A563616765181E646E616D6565416C6963656474616773836573776966746463626F726962656E63686D61726B65656D61696C71616C696365406578616D706C652E636F6D686973416374697665F5A563616765181E646E616D6565416C6963656474616773836573776966746463626F726962656E63686D61726B65656D61696C71616C696365406578616D706C652E636F6D686973416374697665F5A563616765181E646E616D6565416C6963656474616773836573776966746463626F726962656E63686D61726B65656D61696C71616C696365406578616D706C652E636F6D686973416374697665F5A563616765181E646E616D6565416C6963656474616773836573776966746463626F726962656E63686D61726B65656D61696C71616C696365406578616D706C652E636F6D686973416374697665F5A563616765181E646E616D6565416C6963656474616773836573776966746463626F726962656E63686D61726B65656D61696C71616C696365406578616D706C652E636F6D686973416374697665F5A563616765181E646E616D6565416C6963656474616773836573776966746463626F726962656E63686D61726B65656D61696C71616C696365406578616D706C652E636F6D686973416374697665F5A563616765181E646E616D6565416C6963656474616773836573776966746463626F726962656E63686D61726B65656D61696C71616C696365406578616D706C652E636F6D686973416374697665F5A563616765181E646E616D6565416C6963656474616773836573776966746463626F726962656E63686D61726B65656D61696C71616C696365406578616D706C652E636F6D686973416374697665F5A563616765181E646E616D6565416C6963656474616773836573776966746463626F726962656E63686D61726B65656D61696C71616C696365406578616D706C652E636F6D686973416374697665F5") 225 } 226 227 @Test 228 func uint16() throws { 229 let encoder = CBOREncoder() 230 let data = try encoder.encode(1999) 231 #expect(data == "1907CF".asHexData()) 232 } 233 234 @Test( 235 .disabled("Disabled until we can figure out how to make Float16 compile on all platforms."), 236 arguments: [ 237 ("F90000", 0), 238 ("F93C00", 1.0), 239 ("F9BE00", -1.5), 240 ("F97C00", .infinity), 241 ("F93E32", 1.548828125), 242 ("F9F021", -8456) 243 ] 244 ) 245 func halfFloat(expected: String, value: Float16) throws { 246 let expectedData = expected.asHexData() 247 let encoder = CBOREncoder() 248 let data = try encoder.encode(value) 249 #expect(data == expectedData) 250 } 251 252 @Test 253 func duplicateKeysAreDeduplicatedOnEncode() throws { 254 let encoder = CBOREncoder() 255 struct Mock: Encodable { 256 enum CodingKeys: String, CodingKey { 257 case one 258 } 259 260 func encode(to encoder: any Encoder) throws { 261 var container = encoder.container(keyedBy: CodingKeys.self) 262 try container.encode(true, forKey: .one) 263 try container.encode(false, forKey: .one) 264 } 265 } 266 267 let data = try encoder.encode(Mock()).hexString() 268 #expect(data == "a1636f6e65f4") 269 } 270 271 @Test 272 func uuid() throws { 273 let uuid = UUID(uuidString: "A0BDA068-60AD-4111-B4C9-04746791028B") 274 let data = try CBOREncoder().encode(uuid).hexString() 275 let expected = "d82550a0bda06860ad4111b4c904746791028b" 276 #expect(data == expected) 277 } 278}