#include "bej_decoder_json.hpp" #include "nlohmann/json.hpp" #include #include #include #include #include #include #include #include #include namespace libbej { struct BejTestInputFiles { const char* jsonFile; const char* schemaDictionaryFile; const char* annotationDictionaryFile; const char* errorDictionaryFile; const char* encodedStreamFile; }; struct BejTestInputs { const nlohmann::json expectedJson; const uint8_t* schemaDictionary; const uint8_t* annotationDictionary; const uint8_t* errorDictionary; std::span encodedStream; }; struct BejDecoderTestParams { const std::string testName; const BejTestInputFiles inputFiles; }; using BejDecoderTest = testing::TestWithParam; const BejTestInputFiles driveOemTestFiles = { .jsonFile = "../test/json/drive_oem.json", .schemaDictionaryFile = "../test/dictionaries/drive_oem_dict.bin", .annotationDictionaryFile = "../test/dictionaries/annotation_dict.bin", .errorDictionaryFile = "", .encodedStreamFile = "../test/encoded/drive_oem_enc.bin", }; const BejTestInputFiles circuitTestFiles = { .jsonFile = "../test/json/circuit.json", .schemaDictionaryFile = "../test/dictionaries/circuit_dict.bin", .annotationDictionaryFile = "../test/dictionaries/annotation_dict.bin", .errorDictionaryFile = "", .encodedStreamFile = "../test/encoded/circuit_enc.bin", }; const BejTestInputFiles storageTestFiles = { .jsonFile = "../test/json/storage.json", .schemaDictionaryFile = "../test/dictionaries/storage_dict.bin", .annotationDictionaryFile = "../test/dictionaries/annotation_dict.bin", .errorDictionaryFile = "", .encodedStreamFile = "../test/encoded/storage_enc.bin", }; const BejTestInputFiles dummySimpleTestFiles = { .jsonFile = "../test/json/dummysimple.json", .schemaDictionaryFile = "../test/dictionaries/dummy_simple_dict.bin", .annotationDictionaryFile = "../test/dictionaries/annotation_dict.bin", .errorDictionaryFile = "", .encodedStreamFile = "../test/encoded/dummy_simple_enc.bin", }; // Buffer size for storing a single binary file data. constexpr uint32_t maxBufferSize = 16 * 1024; std::streamsize readBinaryFile(const char* fileName, std::span buffer) { std::ifstream inputStream(fileName, std::ios::binary); if (!inputStream.is_open()) { std::cerr << "Cannot open file: " << fileName << "\n"; return 0; } auto readLength = inputStream.readsome( reinterpret_cast(buffer.data()), buffer.size_bytes()); if (inputStream.peek() != EOF) { std::cerr << "Failed to read the complete file: " << fileName << " read length: " << readLength << "\n"; return 0; } return readLength; } std::optional loadInputs(const BejTestInputFiles& files, bool readErrorDictionary = false) { std::ifstream jsonInput(files.jsonFile); if (!jsonInput.is_open()) { std::cerr << "Cannot open file: " << files.jsonFile << "\n"; return std::nullopt; } nlohmann::json expJson; jsonInput >> expJson; static uint8_t schemaDictBuffer[maxBufferSize]; if (readBinaryFile(files.schemaDictionaryFile, std::span(schemaDictBuffer, maxBufferSize)) == 0) { return std::nullopt; } static uint8_t annoDictBuffer[maxBufferSize]; if (readBinaryFile(files.annotationDictionaryFile, std::span(annoDictBuffer, maxBufferSize)) == 0) { return std::nullopt; } static uint8_t encBuffer[maxBufferSize]; auto encLen = readBinaryFile(files.encodedStreamFile, std::span(encBuffer, maxBufferSize)); if (encLen == 0) { return std::nullopt; } static uint8_t errorDict[maxBufferSize]; if (readErrorDictionary) { if (readBinaryFile(files.errorDictionaryFile, std::span(errorDict, maxBufferSize)) == 0) { return std::nullopt; } } BejTestInputs inputs = { .expectedJson = expJson, .schemaDictionary = schemaDictBuffer, .annotationDictionary = annoDictBuffer, .errorDictionary = errorDict, .encodedStream = std::span(encBuffer, encLen), }; return inputs; } TEST_P(BejDecoderTest, Decode) { const BejDecoderTestParams& test_case = GetParam(); auto inputsOrErr = loadInputs(test_case.inputFiles); EXPECT_TRUE(inputsOrErr); BejDictionaries dictionaries = { .schemaDictionary = inputsOrErr->schemaDictionary, .annotationDictionary = inputsOrErr->annotationDictionary, .errorDictionary = inputsOrErr->errorDictionary, }; BejDecoderJson decoder; EXPECT_THAT(decoder.decode(dictionaries, inputsOrErr->encodedStream), 0); std::string decoded = decoder.getOutput(); nlohmann::json jsonDecoded = nlohmann::json::parse(decoded); // Just comparing nlohmann::json types could lead to errors. It compares the // byte values. So int64 and unit64 comparisons might be incorrect. Eg: // bytes values for -5 and 18446744073709551611 are the same. So compare the // string values. EXPECT_TRUE(jsonDecoded.dump() == inputsOrErr->expectedJson.dump()); } /** * TODO: Add more test cases. * - Test Enums inside array elemets * - Array inside an array: is this a valid case? * - Real numbers with exponent part * - Every type inside an array. */ INSTANTIATE_TEST_SUITE_P( , BejDecoderTest, testing::ValuesIn({ {"DriveOEM", driveOemTestFiles}, {"Circuit", circuitTestFiles}, {"Storage", storageTestFiles}, {"DummySimple", dummySimpleTestFiles}, }), [](const testing::TestParamInfo& info) { return info.param.testName; }); } // namespace libbej