#include "cbor.h"
#include <array>
#include <clocale>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include "json.h"
#include "parser_handler.h"
#include "span.h"
#include "status.h"
#include "status_test_support.h"
#include "test_platform.h"
using testing::ElementsAreArray;
namespace crdtp {
namespace cbor {
TEST(IsCBORMessage, SomeSmokeTests) {
std::vector<uint8_t> empty;
EXPECT_FALSE(IsCBORMessage(SpanFrom(empty)));
std::vector<uint8_t> hello = {'H', 'e', 'l', 'o', ' ', 't',
'h', 'e', 'r', 'e', '!'};
EXPECT_FALSE(IsCBORMessage(SpanFrom(hello)));
std::vector<uint8_t> example = {0xd8, 0x5a, 0, 0, 0, 0};
EXPECT_TRUE(IsCBORMessage(SpanFrom(example)));
std::vector<uint8_t> one = {0xd8, 0x5a, 0, 0, 0, 1, 1};
EXPECT_TRUE(IsCBORMessage(SpanFrom(one)));
}
TEST(CheckCBORMessage, SmallestValidExample) {
std::vector<uint8_t> empty_dict = {
0xd8, 0x5a, 0, 0, 0, 2, EncodeIndefiniteLengthMapStart(), EncodeStop()};
Status status = CheckCBORMessage(SpanFrom(empty_dict));
EXPECT_THAT(status, StatusIsOk());
}
TEST(CheckCBORMessage, ValidCBORButNotValidMessage) {
std::vector<uint8_t> not_a_message = {7};
std::string json;
Status status;
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&json, &status);
ParseCBOR(SpanFrom(not_a_message), json_writer.get());
EXPECT_THAT(status, StatusIsOk());
EXPECT_EQ("7", json);
EXPECT_THAT(CheckCBORMessage(SpanFrom(not_a_message)),
StatusIs(Error::CBOR_INVALID_START_BYTE, 0));
}
TEST(CheckCBORMessage, EmptyMessage) {
std::vector<uint8_t> empty;
Status status = CheckCBORMessage(SpanFrom(empty));
EXPECT_THAT(status, StatusIs(Error::CBOR_NO_INPUT, 0));
}
TEST(CheckCBORMessage, InvalidStartByte) {
Status status = CheckCBORMessage(SpanFrom("{\"msg\": \"Hello, world.\"}"));
EXPECT_THAT(status, StatusIs(Error::CBOR_INVALID_START_BYTE, 0));
}
TEST(CheckCBORMessage, InvalidEnvelopes) {
std::vector<uint8_t> bytes = {0xd8, 0x5a};
EXPECT_THAT(CheckCBORMessage(SpanFrom(bytes)),
StatusIs(Error::CBOR_INVALID_ENVELOPE, 1));
bytes = {0xd8, 0x5a, 0};
EXPECT_THAT(CheckCBORMessage(SpanFrom(bytes)),
StatusIs(Error::CBOR_INVALID_ENVELOPE, 1));
bytes = {0xd8, 0x5a, 0, 0};
EXPECT_THAT(CheckCBORMessage(SpanFrom(bytes)),
StatusIs(Error::CBOR_INVALID_ENVELOPE, 1));
bytes = {0xd8, 0x5a, 0, 0, 0};
EXPECT_THAT(CheckCBORMessage(SpanFrom(bytes)),
StatusIs(Error::CBOR_INVALID_ENVELOPE, 1));
bytes = {0xd8, 0x5a, 0, 0, 0, 0};
EXPECT_THAT(CheckCBORMessage(SpanFrom(bytes)),
StatusIs(Error::CBOR_INVALID_ENVELOPE, 1));
}
TEST(CheckCBORMessage, MapStartExpected) {
std::vector<uint8_t> bytes = {0xd8, 0x5a, 0, 0, 0, 1};
EXPECT_THAT(CheckCBORMessage(SpanFrom(bytes)),
StatusIs(Error::CBOR_MAP_START_EXPECTED, 6));
}
TEST(EncodeDecodeInt32Test, Roundtrips23) {
std::vector<uint8_t> encoded;
EncodeInt32(23, &encoded);
EXPECT_THAT(encoded, ElementsAreArray(std::array<uint8_t, 1>{{23}}));
CBORTokenizer tokenizer(SpanFrom(encoded));
EXPECT_EQ(CBORTokenTag::INT32, tokenizer.TokenTag());
EXPECT_EQ(23, tokenizer.GetInt32());
tokenizer.Next();
EXPECT_EQ(CBORTokenTag::DONE, tokenizer.TokenTag());
}
TEST(EncodeDecodeInt32Test, RoundtripsUint8) {
std::vector<uint8_t> encoded;
EncodeInt32(42, &encoded);
EXPECT_THAT(encoded, ElementsAreArray(std::array<uint8_t, 2>{{24, 42}}));
CBORTokenizer tokenizer(SpanFrom(encoded));
EXPECT_EQ(CBORTokenTag::INT32, tokenizer.TokenTag());
EXPECT_EQ(42, tokenizer.GetInt32());
tokenizer.Next();
EXPECT_EQ(CBORTokenTag::DONE, tokenizer.TokenTag());
}
TEST(EncodeDecodeInt32Test, RoundtripsUint16) {
std::vector<uint8_t> encoded;
EncodeInt32(500, &encoded);
EXPECT_EQ(3u, encoded.size());
EXPECT_EQ(25, encoded[0]);
EXPECT_EQ(0x01, encoded[1]);
EXPECT_EQ(0xf4, encoded[2]);
CBORTokenizer tokenizer(SpanFrom(encoded));
EXPECT_EQ(CBORTokenTag::INT32, tokenizer.TokenTag());
EXPECT_EQ(500, tokenizer.GetInt32());
tokenizer.Next();
EXPECT_EQ(CBORTokenTag::DONE, tokenizer.TokenTag());
}
TEST(EncodeDecodeInt32Test, RoundtripsInt32Max) {
std::vector<uint8_t> encoded;
EncodeInt32(std::numeric_limits<int32_t>::max(), &encoded);
EXPECT_THAT(
encoded,
ElementsAreArray(std::array<uint8_t, 5>{{26, 0x7f, 0xff, 0xff, 0xff}}));
CBORTokenizer tokenizer(SpanFrom(encoded));
EXPECT_EQ(CBORTokenTag::INT32, tokenizer.TokenTag());
EXPECT_EQ(std::numeric_limits<int32_t>::max(), tokenizer.GetInt32());
tokenizer.Next();
EXPECT_EQ(CBORTokenTag::DONE, tokenizer.TokenTag());
}
TEST(EncodeDecodeInt32Test, RoundtripsInt32Min) {
std::vector<uint8_t> encoded;
EncodeInt32(std::numeric_limits<int32_t>::min(), &encoded);
EXPECT_THAT(encoded, ElementsAreArray(std::array<uint8_t, 5>{
{1 << 5 | 26, 0x7f, 0xff, 0xff, 0xff}}));
CBORTokenizer tokenizer(SpanFrom(encoded));
EXPECT_EQ(CBORTokenTag::INT32, tokenizer.TokenTag());
EXPECT_EQ(std::numeric_limits<int32_t>::min(), tokenizer.GetInt32());
int32_t expected = -1 - 0x7fffffff;
EXPECT_EQ(expected, tokenizer.GetInt32());
tokenizer.Next();
EXPECT_EQ(CBORTokenTag::DONE, tokenizer.TokenTag());
}
TEST(EncodeDecodeInt32Test, CantRoundtripUint32) {
std::vector<uint8_t> encoded;
internals::WriteTokenStart(MajorType::UNSIGNED, 0xdeadbeef, &encoded);
EXPECT_THAT(
encoded,
ElementsAreArray(std::array<uint8_t, 5>{{26, 0xde, 0xad, 0xbe, 0xef}}));
CBORTokenizer tokenizer(SpanFrom(encoded));
EXPECT_EQ(CBORTokenTag::ERROR_VALUE, tokenizer.TokenTag());
EXPECT_THAT(tokenizer.Status(), StatusIs(Error::CBOR_INVALID_INT32, 0u));
}
TEST(EncodeDecodeInt32Test, DecodeErrorCases) {
struct TestCase {
std::vector<uint8_t> data;
std::string msg;
};
std::vector<TestCase> tests{{
TestCase{
{24},
"additional info = 24 would require 1 byte of payload (but it's 0)"},
TestCase{{27, 0xaa, 0xbb, 0xcc},
"additional info = 27 would require 8 bytes of payload (but "
"it's 3)"},
TestCase{{29}, "additional info = 29 isn't recognized"},
TestCase{{1 << 5 | 27, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
"Max UINT64 payload is outside the allowed range"},
TestCase{{1 << 5 | 26, 0xff, 0xff, 0xff, 0xff},
"Max UINT32 payload is outside the allowed range"},
TestCase{{1 << 5 | 26, 0x80, 0x00, 0x00, 0x00},
"UINT32 payload w/ high bit set is outside the allowed range"},
}};
for (const TestCase& test : tests) {
SCOPED_TRACE(test.msg);
CBORTokenizer tokenizer(SpanFrom(test.data));
EXPECT_EQ(CBORTokenTag::ERROR_VALUE, tokenizer.TokenTag());
EXPECT_THAT(tokenizer.Status(), StatusIs(Error::CBOR_INVALID_INT32, 0u));
}
}
TEST(EncodeDecodeInt32Test, RoundtripsMinus24) {
std::vector<uint8_t> encoded;
EncodeInt32(-24, &encoded);
EXPECT_THAT(encoded, ElementsAreArray(std::array<uint8_t, 1>{{1 << 5 | 23}}));
CBORTokenizer tokenizer(SpanFrom(encoded));
EXPECT_EQ(CBORTokenTag::INT32, tokenizer.TokenTag());
EXPECT_EQ(-24, tokenizer.GetInt32());
tokenizer.Next();
EXPECT_EQ(CBORTokenTag::DONE, tokenizer.TokenTag());
}
TEST(EncodeDecodeInt32Test, RoundtripsAdditionalNegativeExamples) {
std::vector<int32_t> examples = {-1,
-10,
-24,
-25,
-300,
-30000,
-300 * 1000,
-1000 * 1000,
-1000 * 1000 * 1000,
std::numeric_limits<int32_t>::min()};
for (int32_t example : examples) {
SCOPED_TRACE(std::string("example ") + std::to_string(example));
std::vector<uint8_t> encoded;
EncodeInt32(example, &encoded);
CBORTokenizer tokenizer(SpanFrom(encoded));
EXPECT_EQ(CBORTokenTag::INT32, tokenizer.TokenTag());
EXPECT_EQ(example, tokenizer.GetInt32());
tokenizer.Next();
EXPECT_EQ(CBORTokenTag::DONE, tokenizer.TokenTag());
}
}
TEST(EncodeDecodeString16Test, RoundtripsEmpty) {
std::vector<uint8_t> encoded;
EncodeString16(span<uint16_t>(), &encoded);
EXPECT_EQ(1u, encoded.size());
EXPECT_EQ(2 << 5, encoded[0]);
CBORTokenizer tokenizer(SpanFrom(encoded));
EXPECT_EQ(CBORTokenTag::STRING16, tokenizer.TokenTag());
span<uint8_t> decoded_string16_wirerep = tokenizer.GetString16WireRep();
EXPECT_TRUE(decoded_string16_wirerep.empty());
tokenizer.Next();
EXPECT_EQ(CBORTokenTag::DONE, tokenizer.TokenTag());
}
std::vector<uint16_t> String16WireRepToHost(span<uint8_t> in) {
CHECK_EQ(in.size() & 1, 0u);
std::vector<uint16_t> host_out;
for (size_t ii = 0; ii < in.size(); ii += 2)
host_out.push_back(in[ii + 1] << 8 | in[ii]);
return host_out;
}
TEST(EncodeDecodeString16Test, RoundtripsHelloWorld) {
std::array<uint16_t, 10> msg{
{'H', 'e', 'l', 'l', 'o', ',', ' ', 0xd83c, 0xdf0e, '.'}};
std::vector<uint8_t> encoded;
EncodeString16(span<uint16_t>(msg.data(), msg.size()), &encoded);
uint8_t initial_byte = 2 << 5 | 20;
std::array<uint8_t, 21> encoded_expected = {
{initial_byte, 'H', 0, 'e', 0, 'l', 0, 'l', 0, 'o', 0,
',', 0, ' ', 0, 0x3c, 0xd8, 0x0e, 0xdf, '.', 0}};
EXPECT_THAT(encoded, ElementsAreArray(encoded_expected));
CBORTokenizer tokenizer(SpanFrom(encoded));
EXPECT_EQ(CBORTokenTag::STRING16, tokenizer.TokenTag());
std::vector<uint16_t> decoded =
String16WireRepToHost(tokenizer.GetString16WireRep());
EXPECT_THAT(decoded, ElementsAreArray(msg));
tokenizer.Next();
EXPECT_EQ(CBORTokenTag::DONE, tokenizer.TokenTag());
std::string utf8_decoded = UTF16ToUTF8(SpanFrom(decoded));
EXPECT_EQ("Hello, 🌎.", utf8_decoded);
}
TEST(EncodeDecodeString16Test, Roundtrips500) {
std::vector<uint16_t> two_fifty;
for (uint16_t ii = 0; ii < 250; ++ii)
two_fifty.push_back(ii);
std::vector<uint8_t> encoded;
EncodeString16(span<uint16_t>(two_fifty.data(), two_fifty.size()), &encoded);
EXPECT_EQ(3u + 250u * 2, encoded.size());
EXPECT_EQ(2 << 5 | 25, encoded[0]);
EXPECT_EQ(0x01, encoded[1]);
EXPECT_EQ(0xf4, encoded[2]);
CBORTokenizer tokenizer(SpanFrom(encoded));
EXPECT_EQ(CBORTokenTag::STRING16, tokenizer.TokenTag());
std::vector<uint16_t> decoded =
String16WireRepToHost(tokenizer.GetString16WireRep());
EXPECT_THAT(decoded, ElementsAreArray(two_fifty));
tokenizer.Next();
EXPECT_EQ(CBORTokenTag::DONE, tokenizer.TokenTag());
}
TEST(EncodeDecodeString16Test, ErrorCases) {
struct TestCase {
std::vector<uint8_t> data;
std::string msg;
};
std::vector<TestCase> tests{
{TestCase{{2 << 5 | 1, 'a'},
"length must be divisible by 2 (but it's 1)"},
TestCase{{2 << 5 | 29}, "additional info = 29 isn't recognized"},
TestCase{{2 << 5 | 9, 1, 2, 3, 4, 5, 6, 7, 8},
"length (9) points just past the end of the test case"},
TestCase{{2 << 5 | 27, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
'a', 'b', 'c'},
"large length pointing past the end of the test case"}}};
for (const TestCase& test : tests) {
SCOPED_TRACE(test.msg);
CBORTokenizer tokenizer(SpanFrom(test.data));
EXPECT_EQ(CBORTokenTag::ERROR_VALUE, tokenizer.TokenTag());
EXPECT_THAT(tokenizer.Status(), StatusIs(Error::CBOR_INVALID_STRING16, 0u));
}
}
TEST(EncodeDecodeString8Test, RoundtripsHelloWorld) {
std::string utf8_msg = "Hello, 🌎.";
std::vector<uint8_t> msg(utf8_msg.begin(), utf8_msg.end());
std::vector<uint8_t> encoded;
EncodeString8(SpanFrom(utf8_msg), &encoded);
uint8_t initial_byte = 3 << 5 | 12;
std::array<uint8_t, 13> encoded_expected = {{initial_byte, 'H', 'e', 'l', 'l',
'o', ',', ' ', 0xF0, 0x9f, 0x8c,
0x8e, '.'}};
EXPECT_THAT(encoded, ElementsAreArray(encoded_expected));
CBORTokenizer tokenizer(SpanFrom(encoded));
EXPECT_EQ(CBORTokenTag::STRING8, tokenizer.TokenTag());
std::vector<uint8_t> decoded(tokenizer.GetString8().begin(),
tokenizer.GetString8().end());
EXPECT_THAT(decoded, ElementsAreArray(msg));
tokenizer.Next();
EXPECT_EQ(CBORTokenTag::DONE, tokenizer.TokenTag());
}
TEST(EncodeDecodeString8Test, ErrorCases) {
struct TestCase {
std::vector<uint8_t> data;
std::string msg;
};
std::vector<TestCase> tests{
{TestCase{{3 << 5 | 29}, "additional info = 29 isn't recognized"},
TestCase{{3 << 5 | 9, 1, 2, 3, 4, 5, 6, 7, 8},
"length (9) points just past the end of the test case"},
TestCase{{3 << 5 | 27, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
'a', 'b', 'c'},
"large length pointing past the end of the test case"}}};
for (const TestCase& test : tests) {
SCOPED_TRACE(test.msg);
CBORTokenizer tokenizer(SpanFrom(test.data));
EXPECT_EQ(CBORTokenTag::ERROR_VALUE, tokenizer.TokenTag());
EXPECT_THAT(tokenizer.Status(), StatusIs(Error::CBOR_INVALID_STRING8, 0u));
}
}
TEST(EncodeFromLatin1Test, ConvertsToUTF8IfNeeded) {
std::vector<std::pair<std::string, std::string>> examples = {
{"Hello, world.", "Hello, world."},
{"Above: \xDC"
"ber",
"Above: Über"},
{"\xA5 500 are about \xA3 3.50; a y with umlaut is \xFF",
"¥ 500 are about £ 3.50; a y with umlaut is ÿ"}};
for (const auto& example : examples) {
const std::string& latin1 = example.first;
const std::string& expected_utf8 = example.second;
std::vector<uint8_t> encoded;
EncodeFromLatin1(SpanFrom(latin1), &encoded);
CBORTokenizer tokenizer(SpanFrom(encoded));
EXPECT_EQ(CBORTokenTag::STRING8, tokenizer.TokenTag());
std::vector<uint8_t> decoded(tokenizer.GetString8().begin(),
tokenizer.GetString8().end());
std::string decoded_str(decoded.begin(), decoded.end());
EXPECT_THAT(decoded_str, testing::Eq(expected_utf8));
}
}
TEST(EncodeFromUTF16Test, ConvertsToUTF8IfEasy) {
std::vector<uint16_t> ascii = {'e', 'a', 's', 'y'};
std::vector<uint8_t> encoded;
EncodeFromUTF16(span<uint16_t>(ascii.data(), ascii.size()), &encoded);
CBORTokenizer tokenizer(SpanFrom(encoded));
EXPECT_EQ(CBORTokenTag::STRING8, tokenizer.TokenTag());
std::vector<uint8_t> decoded(tokenizer.GetString8().begin(),
tokenizer.GetString8().end());
std::string decoded_str(decoded.begin(), decoded.end());
EXPECT_THAT(decoded_str, testing::Eq("easy"));
}
TEST(EncodeFromUTF16Test, EncodesAsString16IfNeeded) {
std::vector<uint16_t> msg = {'H', 'e', 'l', 'l', 'o',
',', ' ', 0xd83c, 0xdf0e, '.'};
std::vector<uint8_t> encoded;
EncodeFromUTF16(span<uint16_t>(msg.data(), msg.size()), &encoded);
CBORTokenizer tokenizer(SpanFrom(encoded));
EXPECT_EQ(CBORTokenTag::STRING16, tokenizer.TokenTag());
std::vector<uint16_t> decoded =
String16WireRepToHost(tokenizer.GetString16WireRep());
std::string utf8_decoded = UTF16ToUTF8(SpanFrom(decoded));
EXPECT_EQ("Hello, 🌎.", utf8_decoded);
}
TEST(EncodeDecodeBinaryTest, RoundtripsHelloWorld) {
std::vector<uint8_t> binary = {'H', 'e', 'l', 'l', 'o', ',', ' ',
'w', 'o', 'r', 'l', 'd', '.'};
std::vector<uint8_t> encoded;
EncodeBinary(span<uint8_t>(binary.data(), binary.size()), &encoded);
EXPECT_THAT(
encoded,
ElementsAreArray(std::array<uint8_t, 15>{
{(6 << 5 | 22),
(2 << 5 | 13),
'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '.'}}));
std::vector<uint8_t> decoded;
CBORTokenizer tokenizer(SpanFrom(encoded));
EXPECT_EQ(CBORTokenTag::BINARY, tokenizer.TokenTag());
EXPECT_THAT(tokenizer.Status(), StatusIsOk());
decoded = std::vector<uint8_t>(tokenizer.GetBinary().begin(),
tokenizer.GetBinary().end());
EXPECT_THAT(decoded, ElementsAreArray(binary));
tokenizer.Next();
EXPECT_EQ(CBORTokenTag::DONE, tokenizer.TokenTag());
}
TEST(EncodeDecodeBinaryTest, ErrorCases) {
struct TestCase {
std::vector<uint8_t> data;
std::string msg;
};
std::vector<TestCase> tests{{TestCase{
{6 << 5 | 22,
2 << 5 | 27,
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
"large length pointing past the end of the test case"}}};
for (const TestCase& test : tests) {
SCOPED_TRACE(test.msg);
CBORTokenizer tokenizer(SpanFrom(test.data));
EXPECT_EQ(CBORTokenTag::ERROR_VALUE, tokenizer.TokenTag());
EXPECT_THAT(tokenizer.Status(), StatusIs(Error::CBOR_INVALID_BINARY, 0u));
}
}
TEST(EncodeDecodeDoubleTest, RoundtripsWikipediaExample) {
const double kOriginalValue = 1.0 / 3;
std::vector<uint8_t> encoded;
EncodeDouble(kOriginalValue, &encoded);
EXPECT_THAT(
encoded,
ElementsAreArray(std::array<uint8_t, 9>{
{7 << 5 | 27, 0x3f, 0xd5, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55}}));
CBORTokenizer tokenizer(SpanFrom(encoded));
EXPECT_EQ(CBORTokenTag::DOUBLE, tokenizer.TokenTag());
EXPECT_THAT(tokenizer.GetDouble(), testing::DoubleEq(kOriginalValue));
tokenizer.Next();
EXPECT_EQ(CBORTokenTag::DONE, tokenizer.TokenTag());
}
TEST(EncodeDecodeDoubleTest, RoundtripsAdditionalExamples) {
std::vector<double> examples = {0.0,
1.0,
-1.0,
3.1415,
std::numeric_limits<double>::min(),
std::numeric_limits<double>::max(),
std::numeric_limits<double>::infinity(),
std::numeric_limits<double>::quiet_NaN()};
for (double example : examples) {
SCOPED_TRACE(std::string("example ") + std::to_string(example));
std::vector<uint8_t> encoded;
EncodeDouble(example, &encoded);
CBORTokenizer tokenizer(SpanFrom(encoded));
EXPECT_EQ(CBORTokenTag::DOUBLE, tokenizer.TokenTag());
if (std::isnan(example))
EXPECT_TRUE(std::isnan(tokenizer.GetDouble()));
else
EXPECT_THAT(tokenizer.GetDouble(), testing::DoubleEq(example));
tokenizer.Next();
EXPECT_EQ(CBORTokenTag::DONE, tokenizer.TokenTag());
}
}
TEST(EncodeDecodeEnvelopesTest, MessageWithNestingAndEnvelopeContentsAccess) {
std::vector<uint8_t> message;
EnvelopeEncoder envelope;
envelope.EncodeStart(&message);
size_t pos_after_header = message.size();
message.push_back(EncodeIndefiniteLengthMapStart());
EncodeString8(SpanFrom("inner"), &message);
size_t pos_inside_inner = message.size();
EnvelopeEncoder inner_envelope;
inner_envelope.EncodeStart(&message);
size_t pos_inside_inner_contents = message.size();
message.push_back(EncodeIndefiniteLengthMapStart());
EncodeString8(SpanFrom("foo"), &message);
EncodeString8(SpanFrom("bar"), &message);
message.push_back(EncodeStop());
size_t pos_after_inner = message.size();
inner_envelope.EncodeStop(&message);
message.push_back(EncodeStop());
envelope.EncodeStop(&message);
CBORTokenizer tokenizer(SpanFrom(message));
ASSERT_EQ(CBORTokenTag::ENVELOPE, tokenizer.TokenTag());
EXPECT_EQ(message.size(), tokenizer.GetEnvelope().size());
EXPECT_EQ(message.data(), tokenizer.GetEnvelope().data());
EXPECT_EQ(message.data() + pos_after_header,
tokenizer.GetEnvelopeContents().data());
EXPECT_EQ(message.size() - pos_after_header,
tokenizer.GetEnvelopeContents().size());
tokenizer.EnterEnvelope();
ASSERT_EQ(CBORTokenTag::MAP_START, tokenizer.TokenTag());
tokenizer.Next();
ASSERT_EQ(CBORTokenTag::STRING8, tokenizer.TokenTag());
EXPECT_EQ("inner", std::string(tokenizer.GetString8().begin(),
tokenizer.GetString8().end()));
tokenizer.Next();
ASSERT_EQ(CBORTokenTag::ENVELOPE, tokenizer.TokenTag());
EXPECT_EQ(message.data() + pos_inside_inner, tokenizer.GetEnvelope().data());
EXPECT_EQ(pos_after_inner - pos_inside_inner, tokenizer.GetEnvelope().size());
EXPECT_EQ(message.data() + pos_inside_inner_contents,
tokenizer.GetEnvelopeContents().data());
EXPECT_EQ(pos_after_inner - pos_inside_inner_contents,
tokenizer.GetEnvelopeContents().size());
tokenizer.EnterEnvelope();
ASSERT_EQ(CBORTokenTag::MAP_START, tokenizer.TokenTag());
tokenizer.Next();
ASSERT_EQ(CBORTokenTag::STRING8, tokenizer.TokenTag());
EXPECT_EQ("foo", std::string(tokenizer.GetString8().begin(),
tokenizer.GetString8().end()));
tokenizer.Next();
ASSERT_EQ(CBORTokenTag::STRING8, tokenizer.TokenTag());
EXPECT_EQ("bar", std::string(tokenizer.GetString8().begin(),
tokenizer.GetString8().end()));
tokenizer.Next();
ASSERT_EQ(CBORTokenTag::STOP, tokenizer.TokenTag());
tokenizer.Next();
ASSERT_EQ(CBORTokenTag::STOP, tokenizer.TokenTag());
tokenizer.Next();
ASSERT_EQ(CBORTokenTag::DONE, tokenizer.TokenTag());
}
TEST(JSONToCBOREncoderTest, SevenBitStrings) {
std::vector<uint8_t> encoded;
Status status;
std::unique_ptr<ParserHandler> encoder = NewCBOREncoder(&encoded, &status);
std::vector<uint16_t> utf16 = {'f', 'o', 'o'};
encoder->HandleString16(span<uint16_t>(utf16.data(), utf16.size()));
EXPECT_THAT(status, StatusIsOk());
EXPECT_THAT(encoded,
ElementsAreArray(std::array<uint8_t, 4>{
{ 3 << 5 | 3, 'f', 'o', 'o'}}));
}
TEST(JsonCborRoundtrip, EncodingDecoding) {
std::string json =
"{"
"\"string\":\"Hello, \\ud83c\\udf0e.\","
"\"double\":3.1415,"
"\"int\":1,"
"\"negative int\":-1,"
"\"bool\":true,"
"\"null\":null,"
"\"array\":[1,2,3]"
"}";
std::vector<uint8_t> encoded;
Status status;
std::unique_ptr<ParserHandler> encoder = NewCBOREncoder(&encoded, &status);
span<uint8_t> ascii_in = SpanFrom(json);
json::ParseJSON(ascii_in, encoder.get());
std::vector<uint8_t> expected = {
0xd8,
0x5a,
0, 0, 0, 94,
};
expected.push_back(0xbf);
EncodeString8(SpanFrom("string"), &expected);
expected.push_back(2 << 5 | 20);
for (uint8_t ch : std::array<uint8_t, 20>{
{'H', 0, 'e', 0, 'l', 0, 'l', 0, 'o', 0,
',', 0, ' ', 0, 0x3c, 0xd8, 0x0e, 0xdf, '.', 0}})
expected.push_back(ch);
EncodeString8(SpanFrom("double"), &expected);
EncodeDouble(3.1415, &expected);
EncodeString8(SpanFrom("int"), &expected);
EncodeInt32(1, &expected);
EncodeString8(SpanFrom("negative int"), &expected);
EncodeInt32(-1, &expected);
EncodeString8(SpanFrom("bool"), &expected);
expected.push_back(7 << 5 | 21);
EncodeString8(SpanFrom("null"), &expected);
expected.push_back(7 << 5 | 22);
EncodeString8(SpanFrom("array"), &expected);
expected.push_back(0xd8);
expected.push_back(0x5a);
for (uint8_t ch : std::array<uint8_t, 4>{{0, 0, 0, 5}})
expected.push_back(ch);
expected.push_back(0x9f);
expected.push_back(1);
expected.push_back(2);
expected.push_back(3);
expected.push_back(0xff);
expected.push_back(0xff);
EXPECT_TRUE(status.ok());
EXPECT_THAT(encoded, ElementsAreArray(expected));
std::string decoded;
std::unique_ptr<ParserHandler> json_encoder =
json::NewJSONEncoder(&decoded, &status);
ParseCBOR(span<uint8_t>(encoded.data(), encoded.size()), json_encoder.get());
EXPECT_THAT(status, StatusIsOk());
EXPECT_EQ(json, decoded);
}
TEST(JsonCborRoundtrip, MoreRoundtripExamples) {
std::vector<std::string> examples = {
"{\"foo\":{\"bar\":1},\"baz\":2}", "{\"foo\":[1,2,3],\"baz\":2}"};
for (const std::string& json : examples) {
SCOPED_TRACE(std::string("example: ") + json);
std::vector<uint8_t> encoded;
Status status;
std::unique_ptr<ParserHandler> encoder = NewCBOREncoder(&encoded, &status);
span<uint8_t> ascii_in = SpanFrom(json);
json::ParseJSON(ascii_in, encoder.get());
std::string decoded;
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&decoded, &status);
ParseCBOR(span<uint8_t>(encoded.data(), encoded.size()), json_writer.get());
EXPECT_THAT(status, StatusIsOk());
EXPECT_EQ(json, decoded);
}
}
TEST(JSONToCBOREncoderTest, HelloWorldBinary_WithTripToJson) {
std::vector<uint8_t> encoded;
Status status;
std::unique_ptr<ParserHandler> encoder = NewCBOREncoder(&encoded, &status);
encoder->HandleMapBegin();
std::vector<uint16_t> key = {'f', 'o', 'o'};
encoder->HandleString16(SpanFrom(key));
encoder->HandleBinary(SpanFrom(std::vector<uint8_t>{
'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '.'}));
encoder->HandleMapEnd();
EXPECT_THAT(status, StatusIsOk());
std::string decoded;
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&decoded, &status);
ParseCBOR(SpanFrom(encoded), json_writer.get());
EXPECT_THAT(status, StatusIsOk());
EXPECT_EQ("{\"foo\":\"SGVsbG8sIHdvcmxkLg==\"}", decoded);
}
TEST(ParseCBORTest, ParseEmptyCBORMessage) {
std::vector<uint8_t> in = {0xd8, 0x5a, 0, 0, 0, 2, 0xbf, 0xff};
std::string out;
Status status;
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(in.data(), in.size()), json_writer.get());
EXPECT_THAT(status, StatusIsOk());
EXPECT_EQ("{}", out);
}
TEST(ParseCBORTest, ParseCBORHelloWorld) {
const uint8_t kPayloadLen = 27;
std::vector<uint8_t> bytes = {0xd8, 0x5a, 0, 0, 0, kPayloadLen};
bytes.push_back(0xbf);
EncodeString8(SpanFrom("msg"), &bytes);
bytes.push_back(2 << 5 | 20);
for (uint8_t ch : std::array<uint8_t, 20>{
{'H', 0, 'e', 0, 'l', 0, 'l', 0, 'o', 0,
',', 0, ' ', 0, 0x3c, 0xd8, 0x0e, 0xdf, '.', 0}})
bytes.push_back(ch);
bytes.push_back(0xff);
EXPECT_EQ(kPayloadLen, bytes.size() - 6);
std::string out;
Status status;
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
EXPECT_THAT(status, StatusIsOk());
EXPECT_EQ("{\"msg\":\"Hello, \\ud83c\\udf0e.\"}", out);
}
TEST(ParseCBORTest, UTF8IsSupportedInKeys) {
const uint8_t kPayloadLen = 11;
std::vector<uint8_t> bytes = {cbor::InitialByteForEnvelope(),
cbor::InitialByteFor32BitLengthByteString(),
0,
0,
0,
kPayloadLen};
bytes.push_back(cbor::EncodeIndefiniteLengthMapStart());
EncodeString8(SpanFrom("🌎"), &bytes);
EncodeString8(SpanFrom("☾"), &bytes);
bytes.push_back(cbor::EncodeStop());
EXPECT_EQ(kPayloadLen, bytes.size() - 6);
std::string out;
Status status;
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
EXPECT_THAT(status, StatusIsOk());
EXPECT_EQ("{\"\\ud83c\\udf0e\":\"\\u263e\"}", out);
}
TEST(ParseCBORTest, NoInputError) {
std::vector<uint8_t> in = {};
std::string out;
Status status;
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(in.data(), in.size()), json_writer.get());
EXPECT_THAT(status, StatusIs(Error::CBOR_NO_INPUT, 0u));
EXPECT_EQ("", out);
}
TEST(ParseCBORTest, UnexpectedEofExpectedValueError) {
constexpr uint8_t kPayloadLen = 5;
std::vector<uint8_t> bytes = {0xd8, 0x5a, 0, 0, 0, kPayloadLen,
0xbf};
EncodeString8(SpanFrom("key"), &bytes);
EXPECT_EQ(kPayloadLen, bytes.size() - 6);
std::string out;
Status status;
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
EXPECT_THAT(status, StatusIs(Error::CBOR_UNEXPECTED_EOF_EXPECTED_VALUE,
bytes.size()));
EXPECT_EQ("", out);
}
TEST(ParseCBORTest, UnexpectedEofInArrayError) {
constexpr uint8_t kPayloadLen = 8;
std::vector<uint8_t> bytes = {0xd8, 0x5a, 0, 0, 0, kPayloadLen,
0xbf};
EncodeString8(SpanFrom("array"), &bytes);
bytes.push_back(0x9f);
EXPECT_EQ(kPayloadLen, bytes.size() - 6);
std::string out;
Status status;
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
EXPECT_THAT(status,
StatusIs(Error::CBOR_UNEXPECTED_EOF_IN_ARRAY, bytes.size()));
EXPECT_EQ("", out);
}
TEST(ParseCBORTest, UnexpectedEofInMapError) {
constexpr uint8_t kPayloadLen = 1;
std::vector<uint8_t> bytes = {0xd8, 0x5a, 0, 0, 0, kPayloadLen,
0xbf};
EXPECT_EQ(kPayloadLen, bytes.size() - 6);
std::string out;
Status status;
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
EXPECT_THAT(status, StatusIs(Error::CBOR_UNEXPECTED_EOF_IN_MAP, 7u));
EXPECT_EQ("", out);
}
TEST(ParseCBORTest, NoEmptyEnvelopesAllowed) {
std::vector<uint8_t> bytes = {0xd8, 0x5a, 0, 0, 0, 0};
std::string out;
Status status;
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
EXPECT_THAT(status, StatusIs(Error::CBOR_MAP_OR_ARRAY_EXPECTED_IN_ENVELOPE,
bytes.size()));
EXPECT_EQ("", out);
}
TEST(ParseCBORTest, OnlyMapsAndArraysSupportedInsideEnvelopes) {
constexpr uint8_t kPayloadLen = 1;
std::vector<uint8_t> bytes = {0xd8,
0x5a,
0,
0,
0,
kPayloadLen,
EncodeIndefiniteLengthMapStart()};
EncodeString8(SpanFrom("foo"), &bytes);
for (uint8_t byte : {0xd8, 0x5a, 0, 0, 0, 1})
bytes.emplace_back(byte);
size_t error_pos = bytes.size();
bytes.push_back(1);
bytes.emplace_back(EncodeStop());
std::string out;
Status status;
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
EXPECT_THAT(status, StatusIs(Error::CBOR_MAP_OR_ARRAY_EXPECTED_IN_ENVELOPE,
error_pos));
EXPECT_EQ("", out);
}
TEST(ParseCBORTest, InvalidMapKeyError) {
constexpr uint8_t kPayloadLen = 2;
std::vector<uint8_t> bytes = {0xd8, 0x5a, 0,
0, 0, kPayloadLen,
0xbf,
7 << 5 | 22};
EXPECT_EQ(kPayloadLen, bytes.size() - 6);
std::string out;
Status status;
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
EXPECT_THAT(status, StatusIs(Error::CBOR_INVALID_MAP_KEY, 7u));
EXPECT_EQ("", out);
}
std::vector<uint8_t> MakeNestedCBOR(int depth) {
std::vector<uint8_t> bytes;
std::vector<EnvelopeEncoder> envelopes;
for (int ii = 0; ii < depth; ++ii) {
envelopes.emplace_back();
envelopes.back().EncodeStart(&bytes);
bytes.push_back(0xbf);
EncodeString8(SpanFrom("key"), &bytes);
}
EncodeString8(SpanFrom("innermost_value"), &bytes);
for (int ii = 0; ii < depth; ++ii) {
bytes.push_back(0xff);
envelopes.back().EncodeStop(&bytes);
envelopes.pop_back();
}
return bytes;
}
TEST(ParseCBORTest, StackLimitExceededError) {
{
std::vector<uint8_t> bytes = MakeNestedCBOR(3);
std::string out;
Status status;
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
EXPECT_THAT(status, StatusIsOk());
EXPECT_EQ("{\"key\":{\"key\":{\"key\":\"innermost_value\"}}}", out);
}
{
std::vector<uint8_t> bytes = MakeNestedCBOR(300);
std::string out;
Status status;
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
EXPECT_THAT(status, StatusIsOk());
}
std::vector<uint8_t> small_example = MakeNestedCBOR(3);
size_t opening_segment_size = 1;
while (opening_segment_size < small_example.size() &&
small_example[opening_segment_size] != 0xd8)
opening_segment_size++;
{
std::vector<uint8_t> bytes = MakeNestedCBOR(301);
std::string out;
Status status;
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
EXPECT_THAT(status, StatusIs(Error::CBOR_STACK_LIMIT_EXCEEDED,
opening_segment_size * 301));
}
{
std::vector<uint8_t> bytes = MakeNestedCBOR(320);
std::string out;
Status status;
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
EXPECT_THAT(status, StatusIs(Error::CBOR_STACK_LIMIT_EXCEEDED,
opening_segment_size * 301));
}
}
TEST(ParseCBORTest, UnsupportedValueError) {
constexpr uint8_t kPayloadLen = 6;
std::vector<uint8_t> bytes = {0xd8, 0x5a, 0, 0, 0, kPayloadLen,
0xbf};
EncodeString8(SpanFrom("key"), &bytes);
size_t error_pos = bytes.size();
bytes.push_back(6 << 5 | 5);
EXPECT_EQ(kPayloadLen, bytes.size() - 6);
std::string out;
Status status;
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
EXPECT_THAT(status, StatusIs(Error::CBOR_UNSUPPORTED_VALUE, error_pos));
EXPECT_EQ("", out);
}
TEST(ParseCBORTest, InvalidString16Error) {
constexpr uint8_t kPayloadLen = 11;
std::vector<uint8_t> bytes = {0xd8, 0x5a, 0, 0, 0, kPayloadLen,
0xbf};
EncodeString8(SpanFrom("key"), &bytes);
size_t error_pos = bytes.size();
bytes.push_back(2 << 5 | 5);
for (int ii = 0; ii < 5; ++ii)
bytes.push_back(' ');
EXPECT_EQ(kPayloadLen, bytes.size() - 6);
std::string out;
Status status;
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
EXPECT_THAT(status, StatusIs(Error::CBOR_INVALID_STRING16, error_pos));
EXPECT_EQ("", out);
}
TEST(ParseCBORTest, InvalidString8Error) {
constexpr uint8_t kPayloadLen = 6;
std::vector<uint8_t> bytes = {0xd8, 0x5a, 0, 0, 0, kPayloadLen,
0xbf};
EncodeString8(SpanFrom("key"), &bytes);
size_t error_pos = bytes.size();
bytes.push_back(3 << 5 | 5);
EXPECT_EQ(kPayloadLen, bytes.size() - 6);
std::string out;
Status status;
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
EXPECT_THAT(status, StatusIs(Error::CBOR_INVALID_STRING8, error_pos));
EXPECT_EQ("", out);
}
TEST(ParseCBORTest, InvalidBinaryError) {
constexpr uint8_t kPayloadLen = 9;
std::vector<uint8_t> bytes = {0xd8, 0x5a, 0, 0, 0, kPayloadLen,
0xbf};
EncodeString8(SpanFrom("key"), &bytes);
size_t error_pos = bytes.size();
bytes.push_back(6 << 5 | 22);
bytes.push_back(2 << 5 | 10);
bytes.push_back(0x31);
bytes.push_back(0x23);
EXPECT_EQ(kPayloadLen, bytes.size() - 6);
std::string out;
Status status;
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
EXPECT_THAT(status, StatusIs(Error::CBOR_INVALID_BINARY, error_pos));
EXPECT_EQ("", out);
}
TEST(ParseCBORTest, InvalidDoubleError) {
constexpr uint8_t kPayloadLen = 8;
std::vector<uint8_t> bytes = {0xd8, 0x5a, 0, 0, 0, kPayloadLen,
0xbf};
EncodeString8(SpanFrom("key"), &bytes);
size_t error_pos = bytes.size();
bytes.push_back(7 << 5 | 27);
bytes.push_back(0x31);
bytes.push_back(0x23);
EXPECT_EQ(kPayloadLen, bytes.size() - 6);
std::string out;
Status status;
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
EXPECT_THAT(status, StatusIs(Error::CBOR_INVALID_DOUBLE, error_pos));
EXPECT_EQ("", out);
}
TEST(ParseCBORTest, InvalidSignedError) {
constexpr uint8_t kPayloadLen = 14;
std::vector<uint8_t> bytes = {0xd8, 0x5a, 0, 0, 0, kPayloadLen,
0xbf};
EncodeString8(SpanFrom("key"), &bytes);
size_t error_pos = bytes.size();
internals::WriteTokenStart(MajorType::UNSIGNED,
std::numeric_limits<uint64_t>::max(), &bytes);
EXPECT_EQ(kPayloadLen, bytes.size() - 6);
std::string out;
Status status;
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
EXPECT_THAT(status, StatusIs(Error::CBOR_INVALID_INT32, error_pos));
EXPECT_EQ("", out);
}
TEST(ParseCBORTest, TrailingJunk) {
constexpr uint8_t kPayloadLen = 12;
std::vector<uint8_t> bytes = {0xd8, 0x5a, 0, 0, 0, kPayloadLen,
0xbf};
EncodeString8(SpanFrom("key"), &bytes);
EncodeString8(SpanFrom("value"), &bytes);
bytes.push_back(0xff);
ASSERT_EQ(kPayloadLen, bytes.size() - 6);
size_t error_pos = bytes.size();
EncodeString8(SpanFrom("trailing junk"), &bytes);
internals::WriteTokenStart(MajorType::UNSIGNED,
std::numeric_limits<uint64_t>::max(), &bytes);
std::string out;
Status status;
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
EXPECT_THAT(status, StatusIs(Error::CBOR_TRAILING_JUNK, error_pos));
EXPECT_EQ("", out);
}
TEST(ParseCBORTest, EnvelopeContentsLengthMismatch) {
constexpr uint8_t kPartialPayloadLen = 5;
std::vector<uint8_t> bytes = {0xd8, 0x5a, 0,
0, 0, kPartialPayloadLen,
0xbf};
EncodeString8(SpanFrom("key"), &bytes);
EXPECT_EQ(bytes.size() - 6, kPartialPayloadLen);
EncodeString8(SpanFrom("value"), &bytes);
bytes.push_back(0xff);
std::string out;
Status status;
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
EXPECT_THAT(status, StatusIs(Error::CBOR_ENVELOPE_CONTENTS_LENGTH_MISMATCH,
bytes.size()));
EXPECT_EQ("", out);
}
template <typename T>
class AppendString8EntryToMapTest : public ::testing::Test {};
using ContainerTestTypes = ::testing::Types<std::vector<uint8_t>, std::string>;
TYPED_TEST_SUITE(AppendString8EntryToMapTest, ContainerTestTypes);
TEST(AppendString8EntryToMapTest, AppendsEntrySuccessfully) {
constexpr uint8_t kPayloadLen = 12;
std::vector<uint8_t> bytes = {0xd8, 0x5a, 0, 0, 0, kPayloadLen,
0xbf};
size_t pos_before_payload = bytes.size() - 1;
EncodeString8(SpanFrom("key"), &bytes);
EncodeString8(SpanFrom("value"), &bytes);
bytes.push_back(0xff);
EXPECT_EQ(kPayloadLen, bytes.size() - pos_before_payload);
std::vector<uint8_t> msg(bytes.begin(), bytes.end());
Status status =
AppendString8EntryToCBORMap(SpanFrom("foo"), SpanFrom("bar"), &msg);
EXPECT_THAT(status, StatusIsOk());
std::string out;
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(SpanFrom(msg), json_writer.get());
EXPECT_EQ("{\"key\":\"value\",\"foo\":\"bar\"}", out);
EXPECT_THAT(status, StatusIsOk());
}
TYPED_TEST(AppendString8EntryToMapTest, AppendThreeEntries) {
std::vector<uint8_t> encoded = {
0xd8, 0x5a, 0, 0, 0, 2, EncodeIndefiniteLengthMapStart(), EncodeStop()};
EXPECT_THAT(
AppendString8EntryToCBORMap(SpanFrom("key"), SpanFrom("value"), &encoded),
StatusIsOk());
EXPECT_THAT(AppendString8EntryToCBORMap(SpanFrom("key1"), SpanFrom("value1"),
&encoded),
StatusIsOk());
EXPECT_THAT(AppendString8EntryToCBORMap(SpanFrom("key2"), SpanFrom("value2"),
&encoded),
StatusIsOk());
TypeParam msg(encoded.begin(), encoded.end());
std::string out;
Status status;
std::unique_ptr<ParserHandler> json_writer =
json::NewJSONEncoder(&out, &status);
ParseCBOR(SpanFrom(msg), json_writer.get());
EXPECT_EQ("{\"key\":\"value\",\"key1\":\"value1\",\"key2\":\"value2\"}", out);
EXPECT_THAT(status, StatusIsOk());
}
TEST(AppendString8EntryToMapTest, MapStartExpected_Error) {
std::vector<uint8_t> msg = {
0xd8, 0x5a, 0, 0, 0, 1, EncodeIndefiniteLengthArrayStart()};
Status status =
AppendString8EntryToCBORMap(SpanFrom("key"), SpanFrom("value"), &msg);
EXPECT_THAT(status, StatusIs(Error::CBOR_MAP_START_EXPECTED, 6u));
}
TEST(AppendString8EntryToMapTest, MapStopExpected_Error) {
std::vector<uint8_t> msg = {
0xd8, 0x5a, 0, 0, 0, 2, EncodeIndefiniteLengthMapStart(), 42};
Status status =
AppendString8EntryToCBORMap(SpanFrom("key"), SpanFrom("value"), &msg);
EXPECT_THAT(status, StatusIs(Error::CBOR_MAP_STOP_EXPECTED, 7u));
}
TEST(AppendString8EntryToMapTest, InvalidEnvelope_Error) {
{
std::vector<uint8_t> msg = {
0x5a, 0, 0, 0, 2, EncodeIndefiniteLengthMapStart(), EncodeStop(), 0};
Status status =
AppendString8EntryToCBORMap(SpanFrom("key"), SpanFrom("value"), &msg);
EXPECT_THAT(status, StatusIs(Error::CBOR_INVALID_ENVELOPE, 0u));
}
{
std::vector<uint8_t> msg = {
0xd8, 0x7a, 0, 0, 0, 2, EncodeIndefiniteLengthMapStart(), EncodeStop()};
Status status =
AppendString8EntryToCBORMap(SpanFrom("key"), SpanFrom("value"), &msg);
EXPECT_THAT(status, StatusIs(Error::CBOR_INVALID_ENVELOPE, 0u));
}
{
std::vector<uint8_t> msg = {
0xd8, 0x5a, 0, 0, 0, 3, EncodeIndefiniteLengthMapStart(), EncodeStop(),
};
Status status =
AppendString8EntryToCBORMap(SpanFrom("key"), SpanFrom("value"), &msg);
EXPECT_THAT(status, StatusIs(Error::CBOR_INVALID_ENVELOPE, 0u));
}
{
std::vector<uint8_t> msg = {
0xd8, 0x5a, 0, 0, 0, 1, EncodeIndefiniteLengthMapStart(), EncodeStop(),
};
Status status =
AppendString8EntryToCBORMap(SpanFrom("key"), SpanFrom("value"), &msg);
EXPECT_THAT(status, StatusIs(Error::CBOR_INVALID_ENVELOPE, 0u));
}
}
}
}