123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489 |
- // Copyright 2017 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "components/cbor/writer.h"
- #include <limits>
- #include <string>
- #include "testing/gmock/include/gmock/gmock.h"
- #include "testing/gtest/include/gtest/gtest.h"
- /* Leveraging RFC 7049 examples from
- https://github.com/cbor/test-vectors/blob/master/appendix_a.json. */
- namespace cbor {
- TEST(CBORWriterTest, TestWriteUint) {
- struct UintTestCase {
- const int64_t value;
- const base::StringPiece cbor;
- };
- static const UintTestCase kUintTestCases[] = {
- // Reminder: must specify length when creating string pieces
- // with null bytes, else the string will truncate prematurely.
- {0, base::StringPiece("\x00", 1)},
- {1, base::StringPiece("\x01")},
- {10, base::StringPiece("\x0a")},
- {23, base::StringPiece("\x17")},
- {24, base::StringPiece("\x18\x18")},
- {25, base::StringPiece("\x18\x19")},
- {100, base::StringPiece("\x18\x64")},
- {1000, base::StringPiece("\x19\x03\xe8")},
- {1000000, base::StringPiece("\x1a\x00\x0f\x42\x40", 5)},
- {0xFFFFFFFF, base::StringPiece("\x1a\xff\xff\xff\xff")},
- {0x100000000,
- base::StringPiece("\x1b\x00\x00\x00\x01\x00\x00\x00\x00", 9)},
- {std::numeric_limits<int64_t>::max(),
- base::StringPiece("\x1b\x7f\xff\xff\xff\xff\xff\xff\xff")}};
- for (const UintTestCase& test_case : kUintTestCases) {
- auto cbor = Writer::Write(Value(test_case.value));
- ASSERT_TRUE(cbor.has_value());
- EXPECT_THAT(cbor.value(), testing::ElementsAreArray(test_case.cbor));
- }
- }
- TEST(CBORWriterTest, TestWriteNegativeInteger) {
- static const struct {
- const int64_t negative_int;
- const base::StringPiece cbor;
- } kNegativeIntTestCases[] = {
- {-1LL, base::StringPiece("\x20")},
- {-10LL, base::StringPiece("\x29")},
- {-23LL, base::StringPiece("\x36")},
- {-24LL, base::StringPiece("\x37")},
- {-25LL, base::StringPiece("\x38\x18")},
- {-100LL, base::StringPiece("\x38\x63")},
- {-1000LL, base::StringPiece("\x39\x03\xe7")},
- {-4294967296LL, base::StringPiece("\x3a\xff\xff\xff\xff")},
- {-4294967297LL,
- base::StringPiece("\x3b\x00\x00\x00\x01\x00\x00\x00\x00", 9)},
- {std::numeric_limits<int64_t>::min(),
- base::StringPiece("\x3b\x7f\xff\xff\xff\xff\xff\xff\xff")},
- };
- for (const auto& test_case : kNegativeIntTestCases) {
- SCOPED_TRACE(testing::Message() << "testing negative int at index: "
- << test_case.negative_int);
- auto cbor = Writer::Write(Value(test_case.negative_int));
- ASSERT_TRUE(cbor.has_value());
- EXPECT_THAT(cbor.value(), testing::ElementsAreArray(test_case.cbor));
- }
- }
- TEST(CBORWriterTest, TestWriteBytes) {
- struct BytesTestCase {
- const std::vector<uint8_t> bytes;
- const base::StringPiece cbor;
- };
- static const BytesTestCase kBytesTestCases[] = {
- {{}, base::StringPiece("\x40")},
- {{0x01, 0x02, 0x03, 0x04}, base::StringPiece("\x44\x01\x02\x03\x04")},
- };
- for (const BytesTestCase& test_case : kBytesTestCases) {
- auto cbor = Writer::Write(Value(test_case.bytes));
- ASSERT_TRUE(cbor.has_value());
- EXPECT_THAT(cbor.value(), testing::ElementsAreArray(test_case.cbor));
- }
- }
- TEST(CBORWriterTest, TestWriteString) {
- struct StringTestCase {
- const std::string string;
- const base::StringPiece cbor;
- };
- static const StringTestCase kStringTestCases[] = {
- {"", base::StringPiece("\x60")},
- {"a", base::StringPiece("\x61\x61")},
- {"IETF", base::StringPiece("\x64\x49\x45\x54\x46")},
- {"\"\\", base::StringPiece("\x62\x22\x5c")},
- {"\xc3\xbc", base::StringPiece("\x62\xc3\xbc")},
- {"\xe6\xb0\xb4", base::StringPiece("\x63\xe6\xb0\xb4")},
- {"\xf0\x90\x85\x91", base::StringPiece("\x64\xf0\x90\x85\x91")}};
- for (const StringTestCase& test_case : kStringTestCases) {
- SCOPED_TRACE(testing::Message()
- << "testing encoding string : " << test_case.string);
- auto cbor = Writer::Write(Value(test_case.string));
- ASSERT_TRUE(cbor.has_value());
- EXPECT_THAT(cbor.value(), testing::ElementsAreArray(test_case.cbor));
- }
- }
- TEST(CBORWriterTest, TestWriteArray) {
- static const uint8_t kArrayTestCaseCbor[] = {
- // clang-format off
- 0x98, 0x19, // array of 25 elements
- 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,
- // clang-format on
- };
- std::vector<Value> array;
- for (int64_t i = 1; i <= 25; i++) {
- array.push_back(Value(i));
- }
- auto cbor = Writer::Write(Value(array));
- ASSERT_TRUE(cbor.has_value());
- EXPECT_THAT(cbor.value(),
- testing::ElementsAreArray(kArrayTestCaseCbor,
- std::size(kArrayTestCaseCbor)));
- }
- TEST(CBORWriterTest, TestWriteMap) {
- static const uint8_t kMapTestCaseCbor[] = {
- // clang-format off
- 0xb8, 0x19, // map of 25 pairs:
- 0x00, // key 0
- 0x61, 0x61, // value "a"
- 0x17, // key 23
- 0x61, 0x62, // value "b"
- 0x18, 0x18, // key 24
- 0x61, 0x63, // value "c"
- 0x18, 0xFF, // key 255
- 0x61, 0x64, // value "d"
- 0x19, 0x01, 0x00, // key 256
- 0x61, 0x65, // value "e"
- 0x19, 0xFF, 0xFF, // key 65535
- 0x61, 0x66, // value "f"
- 0x1A, 0x00, 0x01, 0x00, 0x00, // key 65536
- 0x61, 0x67, // value "g"
- 0x1A, 0xFF, 0xFF, 0xFF, 0xFF, // key 4294967295
- 0x61, 0x68, // value "h"
- // key 4294967296
- 0x1B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
- 0x61, 0x69, // value "i"
- // key INT64_MAX
- 0x1b, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0x61, 0x6a, // value "j"
- 0x20, // key -1
- 0x61, 0x6b, // value "k"
- 0x37, // key -24
- 0x61, 0x6c, // value "l"
- 0x38, 0x18, // key -25
- 0x61, 0x6d, // value "m"
- 0x38, 0xFF, // key -256
- 0x61, 0x6e, // value "n"
- 0x39, 0x01, 0x00, // key -257
- 0x61, 0x6f, // value "o"
- 0x3A, 0x00, 0x01, 0x00, 0x00, // key -65537
- 0x61, 0x70, // value "p"
- 0x3A, 0xFF, 0xFF, 0xFF, 0xFF, // key -4294967296
- 0x61, 0x71, // value "q"
- // key -4294967297
- 0x3B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
- 0x61, 0x72, // value "r"
- // key INT64_MIN
- 0x3b, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0x61, 0x73, // value "s"
- 0x41, 'a', // byte string "a"
- 0x02,
- 0x43, 'b', 'a', 'r', // byte string "bar"
- 0x03,
- 0x43, 'f', 'o', 'o', // byte string "foo"
- 0x04,
- 0x60, // key ""
- 0x61, 0x2e, // value "."
- 0x61, 0x65, // key "e"
- 0x61, 0x45, // value "E"
- 0x62, 0x61, 0x61, // key "aa"
- 0x62, 0x41, 0x41, // value "AA"
- // clang-format on
- };
- Value::MapValue map;
- // Shorter strings sort first in CTAP, thus the “aa” value should be
- // serialised last in the map.
- map[Value("aa")] = Value("AA");
- map[Value("e")] = Value("E");
- // The empty string is shorter than all others, so should appear first among
- // the strings.
- map[Value("")] = Value(".");
- // Map keys are sorted by major type, by byte length, and then by
- // byte-wise lexical order. So all integer type keys should appear before
- // key "" and all positive integer keys should appear before negative integer
- // keys.
- map[Value(-1)] = Value("k");
- map[Value(-24)] = Value("l");
- map[Value(-25)] = Value("m");
- map[Value(-256)] = Value("n");
- map[Value(-257)] = Value("o");
- map[Value(-65537)] = Value("p");
- map[Value(int64_t(-4294967296))] = Value("q");
- map[Value(int64_t(-4294967297))] = Value("r");
- map[Value(std::numeric_limits<int64_t>::min())] = Value("s");
- map[Value(Value::BinaryValue{'a'})] = Value(2);
- map[Value(Value::BinaryValue{'b', 'a', 'r'})] = Value(3);
- map[Value(Value::BinaryValue{'f', 'o', 'o'})] = Value(4);
- map[Value(0)] = Value("a");
- map[Value(23)] = Value("b");
- map[Value(24)] = Value("c");
- map[Value(std::numeric_limits<uint8_t>::max())] = Value("d");
- map[Value(256)] = Value("e");
- map[Value(std::numeric_limits<uint16_t>::max())] = Value("f");
- map[Value(65536)] = Value("g");
- map[Value(int64_t(std::numeric_limits<uint32_t>::max()))] = Value("h");
- map[Value(int64_t(4294967296))] = Value("i");
- map[Value(std::numeric_limits<int64_t>::max())] = Value("j");
- auto cbor = Writer::Write(Value(map));
- ASSERT_TRUE(cbor.has_value());
- EXPECT_THAT(cbor.value(), testing::ElementsAreArray(
- kMapTestCaseCbor, std::size(kMapTestCaseCbor)));
- }
- TEST(CBORWriterTest, TestWriteMapWithArray) {
- static const uint8_t kMapArrayTestCaseCbor[] = {
- // clang-format off
- 0xa2, // map of 2 pairs
- 0x61, 0x61, // "a"
- 0x01,
- 0x61, 0x62, // "b"
- 0x82, // array with 2 elements
- 0x02,
- 0x03,
- // clang-format on
- };
- Value::MapValue map;
- map[Value("a")] = Value(1);
- Value::ArrayValue array;
- array.push_back(Value(2));
- array.push_back(Value(3));
- map[Value("b")] = Value(array);
- auto cbor = Writer::Write(Value(map));
- ASSERT_TRUE(cbor.has_value());
- EXPECT_THAT(cbor.value(),
- testing::ElementsAreArray(kMapArrayTestCaseCbor,
- std::size(kMapArrayTestCaseCbor)));
- }
- TEST(CBORWriterTest, TestWriteNestedMap) {
- static const uint8_t kNestedMapTestCase[] = {
- // clang-format off
- 0xa2, // map of 2 pairs
- 0x61, 0x61, // "a"
- 0x01,
- 0x61, 0x62, // "b"
- 0xa2, // map of 2 pairs
- 0x61, 0x63, // "c"
- 0x02,
- 0x61, 0x64, // "d"
- 0x03,
- // clang-format on
- };
- Value::MapValue map;
- map[Value("a")] = Value(1);
- Value::MapValue nested_map;
- nested_map[Value("c")] = Value(2);
- nested_map[Value("d")] = Value(3);
- map[Value("b")] = Value(nested_map);
- auto cbor = Writer::Write(Value(map));
- ASSERT_TRUE(cbor.has_value());
- EXPECT_THAT(cbor.value(),
- testing::ElementsAreArray(kNestedMapTestCase,
- std::size(kNestedMapTestCase)));
- }
- TEST(CBORWriterTest, TestSignedExchangeExample) {
- // Example adopted from:
- // https://wicg.github.io/webpackage/draft-yasskin-http-origin-signed-responses.html
- static const uint8_t kSignedExchangeExample[] = {
- // clang-format off
- 0xa5, // map of 5 pairs
- 0x0a, // 10
- 0x01,
- 0x18, 0x64, // 100
- 0x02,
- 0x20, // -1
- 0x03,
- 0x61, 'z', // text string "z"
- 0x04,
- 0x62, 'a', 'a', // text string "aa"
- 0x05,
- /*
- 0x81, 0x18, 0x64, // [100] (array as map key is not yet supported)
- 0x06,
- 0x81, 0x20, // [-1] (array as map key is not yet supported)
- 0x07,
- 0xf4, // false (boolean as map key is not yet supported)
- 0x08,
- */
- // clang-format on
- };
- Value::MapValue map;
- map[Value(10)] = Value(1);
- map[Value(100)] = Value(2);
- map[Value(-1)] = Value(3);
- map[Value("z")] = Value(4);
- map[Value("aa")] = Value(5);
- auto cbor = Writer::Write(Value(map));
- ASSERT_TRUE(cbor.has_value());
- EXPECT_THAT(cbor.value(),
- testing::ElementsAreArray(kSignedExchangeExample,
- std::size(kSignedExchangeExample)));
- }
- TEST(CBORWriterTest, TestWriteSimpleValue) {
- static const struct {
- Value::SimpleValue simple_value;
- const base::StringPiece cbor;
- } kSimpleTestCase[] = {
- {Value::SimpleValue::FALSE_VALUE, base::StringPiece("\xf4")},
- {Value::SimpleValue::TRUE_VALUE, base::StringPiece("\xf5")},
- {Value::SimpleValue::NULL_VALUE, base::StringPiece("\xf6")},
- {Value::SimpleValue::UNDEFINED, base::StringPiece("\xf7")}};
- for (const auto& test_case : kSimpleTestCase) {
- auto cbor = Writer::Write(Value(test_case.simple_value));
- ASSERT_TRUE(cbor.has_value());
- EXPECT_THAT(cbor.value(), testing::ElementsAreArray(test_case.cbor));
- }
- }
- // For major type 0, 2, 3, empty CBOR array, and empty CBOR map, the nesting
- // depth is expected to be 0 since the CBOR decoder does not need to parse
- // any nested CBOR value elements.
- TEST(CBORWriterTest, TestWriteSingleLayer) {
- const Value simple_uint = Value(1);
- const Value simple_string = Value("a");
- const std::vector<uint8_t> byte_data = {0x01, 0x02, 0x03, 0x04};
- const Value simple_bytestring = Value(byte_data);
- Value::ArrayValue empty_cbor_array;
- Value::MapValue empty_cbor_map;
- const Value empty_array_value = Value(empty_cbor_array);
- const Value empty_map_value = Value(empty_cbor_map);
- Value::ArrayValue simple_array;
- simple_array.push_back(Value(2));
- Value::MapValue simple_map;
- simple_map[Value("b")] = Value(3);
- const Value single_layer_cbor_map = Value(simple_map);
- const Value single_layer_cbor_array = Value(simple_array);
- EXPECT_TRUE(Writer::Write(simple_uint, 0).has_value());
- EXPECT_TRUE(Writer::Write(simple_string, 0).has_value());
- EXPECT_TRUE(Writer::Write(simple_bytestring, 0).has_value());
- EXPECT_TRUE(Writer::Write(empty_array_value, 0).has_value());
- EXPECT_TRUE(Writer::Write(empty_map_value, 0).has_value());
- EXPECT_FALSE(Writer::Write(single_layer_cbor_array, 0).has_value());
- EXPECT_TRUE(Writer::Write(single_layer_cbor_array, 1).has_value());
- EXPECT_FALSE(Writer::Write(single_layer_cbor_map, 0).has_value());
- EXPECT_TRUE(Writer::Write(single_layer_cbor_map, 1).has_value());
- }
- // Major type 5 nested CBOR map value with following structure.
- // {"a": 1,
- // "b": {"c": 2,
- // "d": 3}}
- TEST(CBORWriterTest, NestedMaps) {
- Value::MapValue cbor_map;
- cbor_map[Value("a")] = Value(1);
- Value::MapValue nested_map;
- nested_map[Value("c")] = Value(2);
- nested_map[Value("d")] = Value(3);
- cbor_map[Value("b")] = Value(nested_map);
- EXPECT_TRUE(Writer::Write(Value(cbor_map), 2).has_value());
- EXPECT_FALSE(Writer::Write(Value(cbor_map), 1).has_value());
- }
- // Testing Write() function for following CBOR structure with depth of 3.
- // [1,
- // 2,
- // 3,
- // {"a": 1,
- // "b": {"c": 2,
- // "d": 3}}]
- TEST(CBORWriterTest, UnbalancedNestedContainers) {
- Value::ArrayValue cbor_array;
- Value::MapValue cbor_map;
- Value::MapValue nested_map;
- cbor_map[Value("a")] = Value(1);
- nested_map[Value("c")] = Value(2);
- nested_map[Value("d")] = Value(3);
- cbor_map[Value("b")] = Value(nested_map);
- cbor_array.push_back(Value(1));
- cbor_array.push_back(Value(2));
- cbor_array.push_back(Value(3));
- cbor_array.push_back(Value(cbor_map));
- EXPECT_TRUE(Writer::Write(Value(cbor_array), 3).has_value());
- EXPECT_FALSE(Writer::Write(Value(cbor_array), 2).has_value());
- }
- // Testing Write() function for following CBOR structure.
- // {"a": 1,
- // "b": {"c": 2,
- // "d": 3
- // "h": { "e": 4,
- // "f": 5,
- // "g": [6, 7, [8]]}}}
- // Since above CBOR contains 5 nesting levels. Thus, Write() is expected to
- // return empty optional object when maximum nesting layer size is set to 4.
- TEST(CBORWriterTest, OverlyNestedCBOR) {
- Value::MapValue map;
- Value::MapValue nested_map;
- Value::MapValue inner_nested_map;
- Value::ArrayValue inner_array;
- Value::ArrayValue array;
- map[Value("a")] = Value(1);
- nested_map[Value("c")] = Value(2);
- nested_map[Value("d")] = Value(3);
- inner_nested_map[Value("e")] = Value(4);
- inner_nested_map[Value("f")] = Value(5);
- inner_array.push_back(Value(6));
- array.push_back(Value(6));
- array.push_back(Value(7));
- array.push_back(Value(inner_array));
- inner_nested_map[Value("g")] = Value(array);
- nested_map[Value("h")] = Value(inner_nested_map);
- map[Value("b")] = Value(nested_map);
- EXPECT_TRUE(Writer::Write(Value(map), 5).has_value());
- EXPECT_FALSE(Writer::Write(Value(map), 4).has_value());
- }
- } // namespace cbor
|