#include <stddef.h>
#include <stdint.h>
#include <array>
#include <map>
#include <optional>
#include <string>
#include <vector>
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "gtest/gtest.h"
#include "vpd/encoder.h"
extern "C" {
#include "vpd/include/lib/vpd.h"
#include "vpd/include/lib/vpd_decode.h"
#include "vpd/include/lib/vpd_tables.h"
}
namespace vpd {
class EncoderTest : public ::testing::Test {};
TEST_F(EncoderTest, Encode) {
std::string padded_string(
{'p', 'a', 'd', 'd', 'i', 'n', 'g', '\0', '\0', '\0'});
EXPECT_EQ(10, padded_string.size());
auto blob = Encoder::Encode(
Encoder::EncodingParams{
.partition_offset = 0,
},
{
{"foo", "bar"},
{"thi/s", ""},
{"padded", padded_string},
{"Newlines", "are\ndiscouraged"},
{"but", "they\nare\nsupported in the encoding"},
});
ASSERT_NE(std::nullopt, blob);
ASSERT_LT(0, blob->size());
Encoder::DecodedParams params;
auto dict = Encoder::Decode(*blob, ¶ms);
ASSERT_NE(std::nullopt, dict);
EXPECT_EQ(5, dict->size());
EXPECT_EQ("bar", (*dict)["foo"]);
EXPECT_EQ("", (*dict)["thi/s"]);
EXPECT_EQ(padded_string, (*dict)["padded"]);
EXPECT_EQ(10, (*dict)["padded"].size());
EXPECT_EQ("are\ndiscouraged", (*dict)["Newlines"]);
EXPECT_EQ("they\nare\nsupported in the encoding", (*dict)["but"]);
}
TEST_F(EncoderTest, EncodeEmpty) {
auto blob = Encoder::Encode(Encoder::EncodingParams{}, {});
ASSERT_NE(std::nullopt, blob);
Encoder::DecodedParams params;
auto dict = Encoder::Decode(*blob, ¶ms);
EXPECT_NE(std::nullopt, dict);
EXPECT_EQ(0, dict->size());
}
TEST_F(EncoderTest, EncodeParams) {
auto log_level = logging::GetMinLogLevel();
logging::SetMinLogLevel(logging::LOGGING_FATAL);
for (uint32_t eps_offset = 0; eps_offset < 1024; eps_offset++) {
auto blob = Encoder::Encode(
Encoder::EncodingParams{
.partition_offset = 0,
.eps_offset = eps_offset,
},
{
{"some", "key"},
{"another", "key"},
});
if (eps_offset % 16 == 0) {
EXPECT_NE(std::nullopt, blob);
} else {
EXPECT_EQ(std::nullopt, blob);
}
}
logging::SetMinLogLevel(log_level);
}
TEST_F(EncoderTest, Decode) {
auto blob =
base::ReadFileToBytes(base::FilePath("vpd-0.0.1/testdata/ro.bin"));
ASSERT_NE(std::nullopt, blob);
Encoder::DecodedParams params;
auto dict = Encoder::Decode(*blob, ¶ms);
ASSERT_NE(std::nullopt, dict);
EXPECT_EQ(dict->end(), dict->find("nonexistentkey"));
EXPECT_NE(dict->end(), dict->find("mlb_serial_number"));
EXPECT_EQ("ABC123DEF567", (*dict)["mlb_serial_number"]);
EXPECT_EQ(13, dict->size());
}
TEST_F(EncoderTest, DecodeRaw) {
auto blob =
base::ReadFileToBytes(base::FilePath("vpd-0.0.1/testdata/ro_raw"));
ASSERT_NE(std::nullopt, blob);
std::map<std::string, std::string> contents = {
{"serial_number", "NXH0BAA001122334455660"},
{"region", "us"},
{"mlb_serial_number", "01234AURBEEF00001"},
{"in_accel_y_lid_calibbias", "52"},
{"in_accel_x_lid_calibbias", "-22"},
{"in_accel_z_lid_calibbias", "-28"},
{"stable_device_secret_DO_NOT_SHARE",
"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"},
};
auto dict = Encoder::DecodeRaw(*blob);
ASSERT_NE(std::nullopt, dict);
EXPECT_EQ(dict->size(), contents.size());
for (const auto& pair : contents) {
EXPECT_EQ((*dict)[pair.first], pair.second);
}
}
TEST_F(EncoderTest, DecodeEmptyFlash) {
const std::vector<uint8_t> ff_flash(16 * 1024, 0xff);
Encoder::DecodedParams params;
auto dict = Encoder::Decode(ff_flash, ¶ms);
ASSERT_NE(std::nullopt, dict);
EXPECT_TRUE(dict->empty());
}
TEST_F(EncoderTest, DecodeCorruptFlash) {
std::vector<uint8_t> blob({0xd, 0xe, 0xa, 0xd, 0xb, 0xe, 0xe, 0xf});
blob.resize(16 * 1024, 0xff);
Encoder::DecodedParams params;
auto dict = Encoder::Decode(blob, ¶ms);
EXPECT_EQ(std::nullopt, dict);
}
TEST_F(EncoderTest, EncodeLength) {
const vpd::Encoder::EncodingParams params;
size_t preamble = sizeof(struct google_vpd_info) + GOOGLE_VPD_2_0_OFFSET;
{
std::map<std::string, std::string> contents = {{"", ""}};
auto blob = vpd::Encoder::Encode(params, contents);
ASSERT_NE(std::nullopt, blob);
ASSERT_LE(preamble, blob->size());
const std::vector<uint8_t> vpd(blob->begin() + preamble, blob->end());
const std::vector<uint8_t> expected({VPD_TYPE_STRING, 0x00 ,
0x00 ,
VPD_TYPE_TERMINATOR});
EXPECT_EQ(expected, vpd);
}
{
std::string key;
key.resize(0x7f);
std::map<std::string, std::string> contents = {{key, ""}};
auto blob = vpd::Encoder::Encode(params, contents);
ASSERT_NE(std::nullopt, blob);
ASSERT_LE(preamble, blob->size());
const std::vector<uint8_t> vpd(blob->begin() + preamble, blob->end());
std::vector<uint8_t> expected({VPD_TYPE_STRING});
expected.push_back(0x7f); expected.insert(expected.end(), key.begin(), key.end());
expected.push_back(0x00); expected.push_back(VPD_TYPE_TERMINATOR);
EXPECT_EQ(expected, vpd);
}
{
std::string key;
key.resize(0x80);
std::map<std::string, std::string> contents = {{key, ""}};
auto blob = vpd::Encoder::Encode(params, contents);
ASSERT_NE(std::nullopt, blob);
ASSERT_LE(preamble, blob->size());
const std::vector<uint8_t> vpd(blob->begin() + preamble, blob->end());
std::vector<uint8_t> expected({VPD_TYPE_STRING});
expected.push_back(0x81); expected.push_back(0x00); expected.insert(expected.end(), key.begin(), key.end());
expected.push_back(0x00); expected.push_back(VPD_TYPE_TERMINATOR);
EXPECT_EQ(expected, vpd);
}
{
std::string key;
key.resize(0x100040);
std::map<std::string, std::string> contents = {{key, ""}};
auto blob = vpd::Encoder::Encode(params, contents);
ASSERT_NE(std::nullopt, blob);
ASSERT_LE(preamble, blob->size());
const std::vector<uint8_t> vpd(blob->begin() + preamble, blob->end());
std::vector<uint8_t> expected({VPD_TYPE_STRING});
expected.push_back(0xc0); expected.push_back(0x80); expected.push_back(0x40); expected.insert(expected.end(), key.begin(), key.end());
expected.push_back(0x00); expected.push_back(VPD_TYPE_TERMINATOR);
EXPECT_EQ(expected, vpd);
}
}
TEST_F(EncoderTest, DecodeLength) {
{
std::vector<uint8_t> encoded{};
std::map<std::string, std::string> expected;
EXPECT_EQ(expected, vpd::Encoder::DecodeRaw(encoded));
}
{
std::vector<uint8_t> encoded({VPD_TYPE_STRING, 0x00});
EXPECT_EQ(std::nullopt, vpd::Encoder::DecodeRaw(encoded));
}
{
std::vector<uint8_t> encoded({VPD_TYPE_STRING, 0x00, 0x00});
std::map<std::string, std::string> expected({{"", ""}});
EXPECT_EQ(expected, vpd::Encoder::DecodeRaw(encoded));
}
{
std::vector<uint8_t> encoded(
{VPD_TYPE_STRING, 0x00, 0x00, VPD_TYPE_TERMINATOR});
std::map<std::string, std::string> expected({{"", ""}});
EXPECT_EQ(expected, vpd::Encoder::DecodeRaw(encoded));
}
{
std::vector<uint8_t> encoded({VPD_TYPE_STRING, 0x7F});
encoded.resize(encoded.size() + 0x7F);
encoded.push_back(0x00);
encoded.push_back(VPD_TYPE_TERMINATOR);
auto dict = vpd::Encoder::DecodeRaw(encoded);
EXPECT_NE(std::nullopt, dict);
if (dict) {
EXPECT_EQ(1, dict->size());
auto pair = *dict->begin();
EXPECT_EQ(0x7F, pair.first.size());
EXPECT_EQ("", pair.second);
}
}
{
std::vector<uint8_t> encoded({VPD_TYPE_STRING, 0x81, 0x02});
encoded.resize(encoded.size() + 0x82);
encoded.push_back(0x00);
encoded.push_back(VPD_TYPE_TERMINATOR);
auto dict = vpd::Encoder::DecodeRaw(encoded);
EXPECT_NE(std::nullopt, dict);
if (dict) {
EXPECT_EQ(1, dict->size());
auto pair = *dict->begin();
EXPECT_EQ(0x82, pair.first.size());
EXPECT_EQ("", pair.second);
}
}
{
std::vector<uint8_t> encoded({VPD_TYPE_STRING});
std::array<uint8_t, 5> encoded_length{0x82, 0xF8, 0x80, 0x80, 0x00};
encoded.insert(encoded.end(), encoded_length.begin(), encoded_length.end());
encoded.resize(encoded.size() + 0x2F000000);
encoded.push_back(0x00);
encoded.push_back(VPD_TYPE_TERMINATOR);
auto dict = vpd::Encoder::DecodeRaw(encoded);
EXPECT_NE(std::nullopt, dict);
if (dict) {
EXPECT_EQ(1, dict->size());
auto pair = *dict->begin();
EXPECT_EQ(0x2F000000, pair.first.size());
EXPECT_EQ("", pair.second);
}
}
}
TEST_F(EncoderTest, BidirectionlParams) {
std::array params{
Encoder::EncodingParams{
.partition_offset = 0,
},
Encoder::EncodingParams{
.partition_offset = 0x12300,
.eps_offset = 0x100,
},
};
for (const auto& param : params) {
auto blob = Encoder::Encode(param, {
{"key1", ""},
{"key", "largo"},
{"a",
"looooooooooooooooooooooooooooooooo"
"ooooooooooooooooooooooooong one"},
});
ASSERT_NE(std::nullopt, blob);
ASSERT_LT(0, blob->size());
Encoder::DecodedParams decoded_params;
auto dict = Encoder::Decode(*blob, &decoded_params);
ASSERT_NE(std::nullopt, dict);
EXPECT_EQ(3, dict->size());
EXPECT_EQ("", (*dict)["key1"]);
EXPECT_EQ("largo", (*dict)["key"]);
EXPECT_EQ(
"loooooooooooooooooooooooooooooooooooooooooooooooooooooooooong one",
(*dict)["a"]);
EXPECT_EQ(decoded_params.eps_offset, param.eps_offset);
}
}
}