blob: 8e9da65a1079a646d8a41a473958037eb5ddc06c [file] [log] [blame]
kasunathe8946af2022-05-23 12:32:09 -07001#include "bej_decoder_json.hpp"
2#include "nlohmann/json.hpp"
3
4#include <fstream>
5#include <iostream>
6#include <memory>
7#include <optional>
8#include <span>
9#include <string_view>
10
11#include <gmock/gmock-matchers.h>
12#include <gmock/gmock.h>
13#include <gtest/gtest.h>
14
15namespace libbej
16{
17
18struct BejTestInputFiles
19{
20 const char* jsonFile;
21 const char* schemaDictionaryFile;
22 const char* annotationDictionaryFile;
23 const char* errorDictionaryFile;
24 const char* encodedStreamFile;
25};
26
27struct BejTestInputs
28{
29 const nlohmann::json expectedJson;
30 const uint8_t* schemaDictionary;
31 const uint8_t* annotationDictionary;
32 const uint8_t* errorDictionary;
33 std::span<const uint8_t> encodedStream;
34};
35
36struct BejDecoderTestParams
37{
38 const std::string testName;
39 const BejTestInputFiles inputFiles;
40};
41
42using BejDecoderTest = testing::TestWithParam<BejDecoderTestParams>;
43
44const BejTestInputFiles driveOemTestFiles = {
45 .jsonFile = "../test/json/drive_oem.json",
46 .schemaDictionaryFile = "../test/dictionaries/drive_oem_dict.bin",
47 .annotationDictionaryFile = "../test/dictionaries/annotation_dict.bin",
48 .errorDictionaryFile = "",
49 .encodedStreamFile = "../test/encoded/drive_oem_enc.bin",
50};
51
52const BejTestInputFiles circuitTestFiles = {
53 .jsonFile = "../test/json/circuit.json",
54 .schemaDictionaryFile = "../test/dictionaries/circuit_dict.bin",
55 .annotationDictionaryFile = "../test/dictionaries/annotation_dict.bin",
56 .errorDictionaryFile = "",
57 .encodedStreamFile = "../test/encoded/circuit_enc.bin",
58};
59
60const BejTestInputFiles storageTestFiles = {
61 .jsonFile = "../test/json/storage.json",
62 .schemaDictionaryFile = "../test/dictionaries/storage_dict.bin",
63 .annotationDictionaryFile = "../test/dictionaries/annotation_dict.bin",
64 .errorDictionaryFile = "",
65 .encodedStreamFile = "../test/encoded/storage_enc.bin",
66};
67
68const BejTestInputFiles dummySimpleTestFiles = {
69 .jsonFile = "../test/json/dummysimple.json",
70 .schemaDictionaryFile = "../test/dictionaries/dummy_simple_dict.bin",
71 .annotationDictionaryFile = "../test/dictionaries/annotation_dict.bin",
72 .errorDictionaryFile = "",
73 .encodedStreamFile = "../test/encoded/dummy_simple_enc.bin",
74};
75
76// Buffer size for storing a single binary file data.
77constexpr uint32_t maxBufferSize = 16 * 1024;
78
79std::streamsize readBinaryFile(const char* fileName, std::span<uint8_t> buffer)
80{
81 std::ifstream inputStream(fileName, std::ios::binary);
82 if (!inputStream.is_open())
83 {
84 std::cerr << "Cannot open file: " << fileName << "\n";
85 return 0;
86 }
87 auto readLength = inputStream.readsome(
88 reinterpret_cast<char*>(buffer.data()), buffer.size_bytes());
89 if (inputStream.peek() != EOF)
90 {
91 std::cerr << "Failed to read the complete file: " << fileName
92 << " read length: " << readLength << "\n";
93 return 0;
94 }
95 return readLength;
96}
97
98std::optional<BejTestInputs> loadInputs(const BejTestInputFiles& files,
99 bool readErrorDictionary = false)
100{
101 std::ifstream jsonInput(files.jsonFile);
102 if (!jsonInput.is_open())
103 {
104 std::cerr << "Cannot open file: " << files.jsonFile << "\n";
105 return std::nullopt;
106 }
107 nlohmann::json expJson;
108 jsonInput >> expJson;
109
110 static uint8_t schemaDictBuffer[maxBufferSize];
111 if (readBinaryFile(files.schemaDictionaryFile,
112 std::span(schemaDictBuffer, maxBufferSize)) == 0)
113 {
114 return std::nullopt;
115 }
116
117 static uint8_t annoDictBuffer[maxBufferSize];
118 if (readBinaryFile(files.annotationDictionaryFile,
119 std::span(annoDictBuffer, maxBufferSize)) == 0)
120 {
121 return std::nullopt;
122 }
123
124 static uint8_t encBuffer[maxBufferSize];
125 auto encLen = readBinaryFile(files.encodedStreamFile,
126 std::span(encBuffer, maxBufferSize));
127 if (encLen == 0)
128 {
129 return std::nullopt;
130 }
131
132 static uint8_t errorDict[maxBufferSize];
133 if (readErrorDictionary)
134 {
135 if (readBinaryFile(files.errorDictionaryFile,
136 std::span(errorDict, maxBufferSize)) == 0)
137 {
138 return std::nullopt;
139 }
140 }
141
142 BejTestInputs inputs = {
143 .expectedJson = expJson,
144 .schemaDictionary = schemaDictBuffer,
145 .annotationDictionary = annoDictBuffer,
146 .errorDictionary = errorDict,
147 .encodedStream = std::span(encBuffer, encLen),
148 };
149 return inputs;
150}
151
152TEST_P(BejDecoderTest, Decode)
153{
154 const BejDecoderTestParams& test_case = GetParam();
155 auto inputsOrErr = loadInputs(test_case.inputFiles);
156 EXPECT_TRUE(inputsOrErr);
157
158 BejDictionaries dictionaries = {
159 .schemaDictionary = inputsOrErr->schemaDictionary,
160 .annotationDictionary = inputsOrErr->annotationDictionary,
161 .errorDictionary = inputsOrErr->errorDictionary,
162 };
163
164 BejDecoderJson decoder;
165 EXPECT_THAT(decoder.decode(dictionaries, inputsOrErr->encodedStream), 0);
166 std::string decoded = decoder.getOutput();
167 nlohmann::json jsonDecoded = nlohmann::json::parse(decoded);
168
169 // Just comparing nlohmann::json types could lead to errors. It compares the
170 // byte values. So int64 and unit64 comparisons might be incorrect. Eg:
171 // bytes values for -5 and 18446744073709551611 are the same. So compare the
172 // string values.
173 EXPECT_TRUE(jsonDecoded.dump() == inputsOrErr->expectedJson.dump());
174}
175
176/**
177 * TODO: Add more test cases.
178 * - Test Enums inside array elemets
179 * - Array inside an array: is this a valid case?
180 * - Real numbers with exponent part
181 * - Every type inside an array.
182 */
183INSTANTIATE_TEST_SUITE_P(
184 , BejDecoderTest,
185 testing::ValuesIn<BejDecoderTestParams>({
186 {"DriveOEM", driveOemTestFiles},
187 {"Circuit", circuitTestFiles},
188 {"Storage", storageTestFiles},
189 {"DummySimple", dummySimpleTestFiles},
190 }),
191 [](const testing::TestParamInfo<BejDecoderTest::ParamType>& info) {
192 return info.param.testName;
193 });
194
195} // namespace libbej