Files
SwiftCBOR/Tests/CBOREncoderTests.swift
2024-10-28 17:41:03 +00:00

288 lines
15 KiB
Swift

import XCTest
@testable import SwiftCBOR
class CBOREncoderTests: XCTestCase {
func assertEquivalent<T: CBOREncodable>(_ 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<Int>()), [0x80])
XCTAssertEqual(Array<Int>().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<Int>(1...25)), arr25Enc)
}
func testEncodeMaps() {
XCTAssertEqual(CBOR.encode(Dictionary<Int, Int>()), [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)
}
}