import XCTest @testable import SwiftCBOR class CBOREncoderTests: XCTestCase { func assertEquivalent(_ input: T, _ cbor: [UInt8], options: CBOROptions = CBOROptions()) { XCTAssertEqual(input.encode(options: options), cbor) XCTAssertEqual(try! CBOR.decode(input.encode(options: options)), try! CBOR.decode(cbor)) } func testEncodeInts() { for i in 0..<24 { assertEquivalent(i, [UInt8(i)]) XCTAssertEqual((-i).encode().count, 1) } XCTAssertEqual(Int(-1).encode(), [0x20]) XCTAssertEqual(UInt(1).encode(), [0x1]) XCTAssertEqual(CBOR.encode(-10), [0x29]) XCTAssertEqual(Int(-24).encode(), [0x37]) XCTAssertEqual(Int(-25).encode(), [0x38, 24]) XCTAssertEqual(1000000.encode(), [0x1a, 0x00, 0x0f, 0x42, 0x40]) XCTAssertEqual(4294967295.encode(), [0x1a, 0xff, 0xff, 0xff, 0xff]) //UInt32.max XCTAssertEqual(1000000000000.encode(), [0x1b, 0x00, 0x00, 0x00, 0xe8, 0xd4, 0xa5, 0x10, 0x00]) XCTAssertEqual(Int(-1_000_000).encode(), [0x3a, 0x00, 0x0f, 0x42, 0x3f]) XCTAssertEqual(Int(-1_000_000_000_000).encode(), [0x3b, 0x00, 0x00, 0x00, 0xe8, 0xd4, 0xa5, 0x0f, 0xff]) } func testEncodeByteStrings() { XCTAssertEqual(CBOR.encode([UInt8](), asByteString: true), [0x40]) XCTAssertEqual(CBOR.encode([UInt8]([0xf0]), asByteString: true), [0x41, 0xf0]) XCTAssertEqual(CBOR.encode([UInt8]([0x01, 0x02, 0x03, 0x04]), asByteString: true), [0x44, 0x01, 0x02, 0x03, 0x04]) let bs23: [UInt8] = [0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xaa] assert(bs23.count == 23) XCTAssertEqual(CBOR.encode(bs23, asByteString: true), [0x57] + bs23) let bs24 = bs23 + [0xaa] XCTAssertEqual(CBOR.encode(bs24, asByteString: true), [0x58, 24] + bs24) // non-UInt8, raw bytes (reversed). XCTAssertEqual(CBOR.encode([UInt16]([240]), asByteString: true), [0x41, 0x00, 0xf0]) } func testEncodeData() { XCTAssertEqual(CBOR.encode(Data()), [0x40]) XCTAssertEqual(CBOR.encode(Data([0xf0])), [0x41, 0xf0]) XCTAssertEqual(CBOR.encode(Data([0x01, 0x02, 0x03, 0x04])), [0x44, 0x01, 0x02, 0x03, 0x04]) let bs23 = Data([0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xaa]) assert(bs23.count == 23) XCTAssertEqual(CBOR.encode(bs23), [0x57] + bs23) let bs24 = Data(bs23 + [0xaa]) XCTAssertEqual(CBOR.encode(bs24), [0x58, 24] + bs24) // non-UInt8, raw bytes (reversed). XCTAssertEqual(CBOR.encode([UInt16]([240]), asByteString: true), [0x41, 0x00, 0xf0]) } func testEncodeUtf8Strings() { XCTAssertEqual("".encode(), [0x60]) XCTAssertEqual("a".encode(), [0x61, 0x61]) XCTAssertEqual("B".encode(), [0x61, 0x42]) XCTAssertEqual(try! CBOR.decode("B".encode()), try! CBOR.decode([0x78, 1, 0x42])) XCTAssertEqual("ABC".encode(), [0x63, 0x41, 0x42, 0x43]) XCTAssertEqual(try! CBOR.decode("ABC".encode()), try! CBOR.decode([0x79, 0x00, 3, 0x41, 0x42, 0x43])) XCTAssertEqual("IETF".encode(), [0x64, 0x49, 0x45, 0x54, 0x46]) XCTAssertEqual("今日は".encode(), [0x69, 0xE4, 0xBB, 0x8A, 0xE6, 0x97, 0xA5, 0xE3, 0x81, 0xAF]) XCTAssertEqual("♨️français;日本語!Longer text\n with break?".encode(), [0x78, 0x34, 0xe2, 0x99, 0xa8, 0xef, 0xb8, 0x8f, 0x66, 0x72, 0x61, 0x6e, 0xc3, 0xa7, 0x61, 0x69, 0x73, 0x3b, 0xe6, 0x97, 0xa5, 0xe6, 0x9c, 0xac, 0xe8, 0xaa, 0x9e, 0xef, 0xbc, 0x81, 0x4c, 0x6f, 0x6e, 0x67, 0x65, 0x72, 0x20, 0x74, 0x65, 0x78, 0x74, 0x0a, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3f]) XCTAssertEqual("\"\\".encode(), [0x62, 0x22, 0x5c]) XCTAssertEqual("\u{6C34}".encode(), [0x63, 0xe6, 0xb0, 0xb4]) XCTAssertEqual("水".encode(), [0x63, 0xe6, 0xb0, 0xb4]) XCTAssertEqual("\u{00fc}".encode(), [0x62, 0xc3, 0xbc]) XCTAssertEqual("abc\n123".encode(), [0x67, 0x61, 0x62, 0x63, 0x0a, 0x31, 0x32, 0x33]) } func testEncodeArrays() { XCTAssertEqual(CBOR.encode(Array()), [0x80]) XCTAssertEqual(Array().encode(), [0x80]) XCTAssertEqual(CBOR.encode([1, 2, 3]), [0x83, 0x01, 0x02, 0x03]) XCTAssertEqual([1, 2, 3].encode(), [0x83, 0x01, 0x02, 0x03]) let arr: [[UInt64]] = [[1], [2, 3], [4, 5]] let wrapped = arr.map{ inner in return CBOR.array(inner.map{ return CBOR.unsignedInt($0) })} XCTAssertEqual(CBOR.encode(wrapped), [0x83, 0x81, 0x01, 0x82, 0x02, 0x03, 0x82, 0x04, 0x05]) let arr25Enc: [UInt8] = [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] XCTAssertEqual(CBOR.encode(Array(1...25)), arr25Enc) } func testEncodeMaps() { XCTAssertEqual(CBOR.encode(Dictionary()), [0xa0]) let encoded = CBOR.encode([1: 2, 3: 4]) XCTAssert(encoded == [0xa2, 0x01, 0x02, 0x03, 0x04] || encoded == [0xa2, 0x03, 0x04, 0x01, 0x02]) let arr1: CBOR = [1] let arr2: CBOR = [2,3] let nestedEnc: [UInt8] = CBOR.encode(["a": arr1, "b": arr2]) let encodedAFirst: [UInt8] = [0xa2, 0x61, 0x61, 0x81, 0x01, 0x61, 0x62, 0x82, 0x02, 0x03] let encodedBFirst: [UInt8] = [0xa2, 0x61, 0x62, 0x82, 0x02, 0x03, 0x61, 0x61, 0x81, 0x01] XCTAssert(nestedEnc == encodedAFirst || nestedEnc == encodedBFirst) let mapToAny: [String: Any] = [ "a": 1, "b": [2, 3] ] let encodedMapToAny = try! CBOR.encodeMap(mapToAny) XCTAssertEqual(encodedMapToAny, [0xa2, 0x61, 0x61, 0x01, 0x61, 0x62, 0x82, 0x02, 0x03]) let mapToAnyWithIntKeys: [Int: Any] = [ 12: 1, 51: "testing" ] XCTAssertThrowsError(try CBOR.encodeMap(mapToAnyWithIntKeys, options: CBOROptions(forbidNonStringMapKeys: true))) { err in XCTAssertEqual(err as! CBOREncoderError, CBOREncoderError.nonStringKeyInMap) } } func testEncodeTagged() { let bignum: [UInt8] = [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] // 2**64 let bignumCBOR = CBOR.byteString(bignum) XCTAssertEqual(CBOR.encodeTagged(tag: .positiveBignum, value: bignumCBOR), [0xc2, 0x49] + bignum) XCTAssertEqual(CBOR.encodeTagged(tag: .init(rawValue: UInt64.max), value: bignumCBOR), [0xdb, 255, 255, 255, 255, 255, 255, 255, 255, 0x49] + bignum) } func testEncodeSimple() { XCTAssertEqual(false.encode(), [0xf4]) XCTAssertEqual(true.encode(), [0xf5]) XCTAssertEqual(CBOR.null.encode(), [0xf6]) XCTAssertEqual(CBOR.undefined.encode(), [0xf7]) XCTAssertEqual(CBOR.simple(16).encode(), [0xf0]) XCTAssertEqual(CBOR.simple(24).encode(), [0xf8, 0x18]) XCTAssertEqual(CBOR.simple(255).encode(), [0xf8, 0xff]) XCTAssertEqual(CBOR.break.encode(), [0xff]) } func testEncodeFloats() { // The following tests are modifications of examples of Float16 in the RFC XCTAssertEqual(Float(0.0).encode(), [0xfa,0x00, 0x00, 0x00, 0x00]) XCTAssertEqual(Float(-0.0).encode(), [0xfa, 0x80, 0x00, 0x00, 0x00]) XCTAssertEqual(Float(1.0).encode(), [0xfa, 0x3f, 0x80, 0x00, 0x00]) XCTAssertEqual(Float(1.5).encode(), [0xfa,0x3f,0xc0, 0x00,0x00]) XCTAssertEqual(Float(65504.0).encode(), [0xfa, 0x47, 0x7f, 0xe0, 0x00]) // The following are seen as Float32s in the RFC XCTAssertEqual(Float(100000.0).encode(), [0xfa,0x47,0xc3,0x50,0x00]) XCTAssertEqual(Float(3.4028234663852886e+38).encode(), [0xfa, 0x7f, 0x7f, 0xff, 0xff]) // The following are seen as Doubles in the RFC XCTAssertEqual(Double(1.1).encode(), [0xfb,0x3f,0xf1,0x99,0x99,0x99,0x99,0x99,0x9a]) XCTAssertEqual(Double(-4.1).encode(), [0xfb, 0xc0, 0x10, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66]) XCTAssertEqual(CBOR.encode(1.0e+300), [0xfb, 0x7e, 0x37, 0xe4, 0x3c, 0x88, 0x00, 0x75, 0x9c]) XCTAssertEqual(Double(5.960464477539063e-8).encode(), [0xfb, 0x3e, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) // Special values XCTAssertEqual(Float.infinity.encode(), [0xfa, 0x7f, 0x80, 0x00, 0x00]) XCTAssertEqual(CBOR.encode(-Float.infinity), [0xfa, 0xff, 0x80, 0x00, 0x00]) XCTAssertEqual(Double.infinity.encode(), [0xfb, 0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) XCTAssertEqual(CBOR.encode(-Double.infinity), [0xfb, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) XCTAssertEqual(Float.nan.encode(), [0xfa,0x7f, 0xc0, 0x00, 0x00]) XCTAssertEqual(Double.nan.encode(), [0xfb, 0x7f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) // Swift's floating point literals are read as Doubles unless specifically specified. e.g. XCTAssertEqual(CBOR.encode(0.0), [0xfb,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) } func testEncodeIndefiniteArrays() { var indefArray = CBOR.encodeArrayStreamStart() XCTAssertEqual(indefArray, [0x9f]) let a: [Int] = [1, 2] indefArray.append(contentsOf: CBOR.encodeArrayChunk(a)) // this encode a definite array! // send current indef array (without close. the receiver would be programmed to anticipate this. indefArray.append(contentsOf: 3.encode()) indefArray.append(contentsOf: CBOR.encodeStreamEnd()) XCTAssertEqual(indefArray, [0x9f, 0x01, 0x02, 0x03, 0xff]) indefArray.removeLast() // remove stream end let innerArray = [UInt8]([1,2,3]) indefArray.append(contentsOf: CBOR.encodeArrayStreamStart() + CBOR.encodeArrayChunk(innerArray)) indefArray.append(contentsOf: CBOR.encodeStreamEnd()) // close inner array indefArray.append(contentsOf: CBOR.encodeStreamEnd()) // close outer XCTAssertEqual(indefArray, [0x9f, 0x01, 0x02, 0x03, 0x9f, 0x01, 0x02, 0x03, 0xff, 0xff]) } func testEncodeIndefiniteMaps() { XCTAssertEqual(CBOR.encodeMapStreamStart(), [0xbf]) let map: [String: Int] = ["a": 1] let map2 = ["B": 2] let a1_enc: [UInt8] = [0x61, 0x61, 0x01] let b2_enc: [UInt8] = [0x61, 0x42, 0x02] let final: [UInt8] = CBOR.encodeMapStreamStart() + CBOR.encodeMapChunk(map) + CBOR.encodeMapChunk(map2) + CBOR.encodeStreamEnd() XCTAssertEqual(final, [0xbf] + a1_enc + b2_enc + [0xff]) } func testEncodeIndefiniteStrings() { XCTAssertEqual(CBOR.encodeStringStreamStart(), [0x7f]) let s = "a" let t = "B" let st_enc: [UInt8] = [0x7f, 0x61, 0x61, 0x61, 0x42, 0xff] XCTAssertEqual(CBOR.encodeStringStreamStart() + CBOR.encode(s) + CBOR.encode(t) + CBOR.encodeStreamEnd(), st_enc) } func testEncodeIndefiniteByteStrings() { XCTAssertEqual(CBOR.encodeByteStringStreamStart(), [0x5f]) let bs: [UInt8] = [0xf0] let bs2: [UInt8] = [0xff] let cbor: [UInt8] = [0x5f, 0x41, 0xf0, 0x41, 0xff, 0xff] let encoded = CBOR.encodeByteStringStreamStart() + CBOR.encode(bs, asByteString: true) + CBOR.encode(bs2, asByteString: true) + CBOR.encodeStreamEnd() XCTAssertEqual(encoded, cbor) } func testEncodeDates() { let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ" dateFormatter.locale = Locale(identifier: "en_US_POSIX") dateFormatter.timeZone = TimeZone(secondsFromGMT: 0) let dateOne = dateFormatter.date(from: "2013-03-21T20:04:00+00:00")! XCTAssertEqual(CBOR.encodeDate(dateOne), [0xc1, 0x1a, 0x51, 0x4b, 0x67, 0xb0]) let dateTwo = Date(timeIntervalSince1970: 1363896240.5) XCTAssertEqual(CBOR.encodeDate(dateTwo), [0xc1, 0xfb, 0x41, 0xd4, 0x52, 0xd9, 0xec, 0x20, 0x00, 0x00]) let dateAsMap = Date(timeIntervalSince1970: 1363896240.5) XCTAssertEqual( CBOR.encodeDate(dateAsMap, options: CBOROptions(dateStrategy: .annotatedMap)), [0xa2, 0x66, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x74, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x67, 0x5f, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0xfb, 0x41, 0xd4, 0x52, 0xd9, 0xec, 0x20, 0x00, 0x00] ) let oldDateAsMap = Date(timeIntervalSince1970: -156896240.5) XCTAssertEqual( CBOR.encodeDate(oldDateAsMap, options: CBOROptions(dateStrategy: .annotatedMap)), [0xa2, 0x66, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x74, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x67, 0x5f, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0xfb, 0xc1, 0xa2, 0xb4, 0x17, 0xe1, 0x00, 0x00, 0x00] ) } func testReadmeExamples() { XCTAssertEqual(100.encode(), [0x18, 0x64]) XCTAssertEqual(CBOR.encode(100), [0x18, 0x64]) XCTAssertEqual("hello".encode(), [0x65, 0x68, 0x65, 0x6c, 0x6c, 0x6f]) XCTAssertEqual(CBOR.encode(["a", "b", "c"]), [0x83, 0x61, 0x61, 0x61, 0x62, 0x61, 0x63]) struct MyStruct: CBOREncodable { var x: Int var y: String public func encode(options: CBOROptions = CBOROptions()) -> [UInt8] { let cborWrapper: CBOR = [ "x": CBOR(integerLiteral: self.x), "y": .utf8String(self.y) ] return cborWrapper.encode(options: options) } public func toCBOR(options: CBOROptions = CBOROptions()) -> CBOR { return CBOR(dictionaryLiteral:("x", CBOR(integerLiteral: self.x)), ("y", .utf8String(self.y))) } } let encoded = MyStruct(x: 42, y: "words").encode() XCTAssert( encoded == [0xa2, 0x61, 0x79, 0x65, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x61, 0x78, 0x18, 0x2a] || encoded == [0xa2, 0x61, 0x78, 0x18, 0x2a, 0x61, 0x79, 0x65, 0x77, 0x6f, 0x72, 0x64, 0x73] ) } func testEncodeAny() { struct MyCodableThing: Codable, Equatable { var zipcode: String var locationId: Int static func random() -> MyCodableThing { let formatter = NumberFormatter() formatter.minimumIntegerDigits = 5 let randomInt = Int.random(in: 0..<99999) let zip = formatter.string(from: NSNumber(integerLiteral: randomInt))! return MyCodableThing(zipcode: zip, locationId: Int.random(in: 0..<1000)) } } let myCodableThing = MyCodableThing.random() let encoded = try! CBOR.encodeAny(myCodableThing) let decoded = try! CodableCBORDecoder().decode(MyCodableThing.self, from: Data(encoded)) XCTAssertEqual(decoded, myCodableThing) } }