blob: d258b4b0cee6bbc7660fd20f2478bca6e597c3f8 [file] [log] [blame]
Nikhil Namjoshida6e5572023-03-13 10:52:53 -07001#include "bej_dictionary.h"
2#include "bej_encoder_core.h"
3#include "bej_tree.h"
4
5#include "bej_common_test.hpp"
6#include "bej_decoder_json.hpp"
7#include "bej_encoder_json.hpp"
8
9#include <vector>
10
11#include <gmock/gmock-matchers.h>
12#include <gmock/gmock.h>
13#include <gtest/gtest.h>
14
15namespace libbej
16{
17
18struct BejEncoderTestParams
19{
20 const std::string testName;
21 const BejTestInputFiles inputFiles;
22 std::string expectedJson;
23 struct RedfishPropertyParent* (*createResource)();
24};
25
26using BejEncoderTest = testing::TestWithParam<BejEncoderTestParams>;
27
28const BejTestInputFiles dummySimpleTestFiles = {
29 .jsonFile = "../test/json/dummysimple.json",
30 .schemaDictionaryFile = "../test/dictionaries/dummy_simple_dict.bin",
31 .annotationDictionaryFile = "../test/dictionaries/annotation_dict.bin",
32 .errorDictionaryFile = "",
33 .encodedStreamFile = "../test/encoded/dummy_simple_enc.bin",
34};
35
36const BejTestInputFiles driveOemTestFiles = {
37 .jsonFile = "../test/json/drive_oem.json",
38 .schemaDictionaryFile = "../test/dictionaries/drive_oem_dict.bin",
39 .annotationDictionaryFile = "../test/dictionaries/annotation_dict.bin",
40 .errorDictionaryFile = "",
41 .encodedStreamFile = "../test/encoded/drive_oem_enc.bin",
42};
43
44const BejTestInputFiles chassisTestFiles = {
45 .jsonFile = "../test/json/chassis.json",
46 .schemaDictionaryFile = "../test/dictionaries/chassis_dict.bin",
47 .annotationDictionaryFile = "../test/dictionaries/annotation_dict.bin",
48 .errorDictionaryFile = "",
49 .encodedStreamFile = "../test/encoded/chassis_enc.bin",
50};
51
52struct RedfishPropertyParent* createDummyResource()
53{
54 static struct RedfishPropertyParent root;
55 bejTreeInitSet(&root, "DummySimple");
56
57 static struct RedfishPropertyLeafString id;
58 bejTreeAddString(&root, &id, "Id", "Dummy ID");
59
60 static struct RedfishPropertyLeafInt intProp;
61 bejTreeAddInteger(&root, &intProp, "SampleIntegerProperty", -5);
62
63 static struct RedfishPropertyLeafReal real;
64 bejTreeAddReal(&root, &real, "SampleRealProperty", -5576.90001);
65
66 static struct RedfishPropertyLeafNull enProp;
67 bejTreeAddNull(&root, &enProp, "SampleEnabledProperty");
68
69 static struct RedfishPropertyParent chArraySet1;
70 bejTreeInitSet(&chArraySet1, nullptr);
71
72 static struct RedfishPropertyLeafBool chArraySet1bool;
73 bejTreeAddBool(&chArraySet1, &chArraySet1bool, "AnotherBoolean", true);
74
75 static struct RedfishPropertyLeafEnum chArraySet1Ls;
76 bejTreeAddEnum(&chArraySet1, &chArraySet1Ls, "LinkStatus", "NoLink");
77
78 static struct RedfishPropertyParent chArraySet2;
79 bejTreeInitSet(&chArraySet2, nullptr);
80
81 static struct RedfishPropertyLeafEnum chArraySet2Ls;
82 bejTreeAddEnum(&chArraySet2, &chArraySet2Ls, "LinkStatus", "LinkDown");
83
84 static struct RedfishPropertyParent chArray;
85 bejTreeInitArray(&chArray, "ChildArrayProperty");
86
87 bejTreeLinkChildToParent(&chArray, &chArraySet1);
88 bejTreeLinkChildToParent(&chArray, &chArraySet2);
89
90 bejTreeLinkChildToParent(&root, &chArray);
91 return &root;
92}
93
94const std::string driveOemJson = R"(
95 {
96 "@odata.id": "/redfish/v1/drives/1",
97 "@odata.type": "#Drive.v1_5_0.Drive",
98 "Id": "Drive1",
99 "Actions": {
100 "#Drive.Reset": {
101 "target": "/redfish/v1/drives/1/Actions/Drive.Reset",
102 "title": "Reset a Drive",
103 "ResetType@Redfish.AllowableValues": [
104 "On",
105 "ForceOff",
106 "ForceRestart",
107 "Nmi",
108 "ForceOn",
109 "PushPowerButton"
110 ]
111 }
112 },
113 "Status@Message.ExtendedInfo": [
114 {
115 "MessageId": "PredictiveFailure",
116 "RelatedProperties": ["FailurePredicted", "MediaType"]
117 }
118 ],
119 "Identifiers": [],
120 "Links": {}
121 }
122)";
123
124struct RedfishPropertyParent* createDriveOem()
125{
126 static struct RedfishPropertyParent root;
127 bejTreeInitSet(&root, "Drive");
128
129 static struct RedfishPropertyLeafString odataId;
130 bejTreeAddString(&root, &odataId, "@odata.id", "/redfish/v1/drives/1");
131
132 static struct RedfishPropertyLeafString odataType;
133 bejTreeAddString(&root, &odataType, "@odata.type", "#Drive.v1_5_0.Drive");
134
135 static struct RedfishPropertyLeafString id;
136 bejTreeAddString(&root, &id, "Id", "Drive1");
137
138 static struct RedfishPropertyParent actions;
139 bejTreeInitSet(&actions, "Actions");
140
141 static struct RedfishPropertyParent drRst;
142 bejTreeInitSet(&drRst, "#Drive.Reset");
143
144 static struct RedfishPropertyLeafString drRstTarget;
145 bejTreeAddString(&drRst, &drRstTarget, "target",
146 "/redfish/v1/drives/1/Actions/Drive.Reset");
147
148 static struct RedfishPropertyLeafString drRstTitle;
149 bejTreeAddString(&drRst, &drRstTitle, "title", "Reset a Drive");
150
151 static struct RedfishPropertyParent drRstType;
152 bejTreeInitPropertyAnnotated(&drRstType, "ResetType");
153
154 static struct RedfishPropertyParent drRstTypeAllowable;
155 bejTreeInitArray(&drRstTypeAllowable, "@Redfish.AllowableValues");
156
157 static struct RedfishPropertyLeafString drRstTypeAllowableS1;
158 bejTreeAddString(&drRstTypeAllowable, &drRstTypeAllowableS1, "", "On");
159
160 static struct RedfishPropertyLeafString drRstTypeAllowableS2;
161 bejTreeAddString(&drRstTypeAllowable, &drRstTypeAllowableS2, "",
162 "ForceOff");
163
164 static struct RedfishPropertyLeafString drRstTypeAllowableS3;
165 bejTreeAddString(&drRstTypeAllowable, &drRstTypeAllowableS3, "",
166 "ForceRestart");
167
168 static struct RedfishPropertyLeafString drRstTypeAllowableS4;
169 bejTreeAddString(&drRstTypeAllowable, &drRstTypeAllowableS4, "", "Nmi");
170
171 static struct RedfishPropertyLeafString drRstTypeAllowableS5;
172 bejTreeAddString(&drRstTypeAllowable, &drRstTypeAllowableS5, "", "ForceOn");
173
174 static struct RedfishPropertyLeafString drRstTypeAllowableS6;
175 bejTreeAddString(&drRstTypeAllowable, &drRstTypeAllowableS6, "",
176 "PushPowerButton");
177
178 bejTreeLinkChildToParent(&drRstType, &drRstTypeAllowable);
179 bejTreeLinkChildToParent(&drRst, &drRstType);
180 bejTreeLinkChildToParent(&actions, &drRst);
181 bejTreeLinkChildToParent(&root, &actions);
182
183 static struct RedfishPropertyParent statusAnt;
184 bejTreeInitPropertyAnnotated(&statusAnt, "Status");
185
186 static struct RedfishPropertyParent statusAntMsgExtInfo;
187 bejTreeInitArray(&statusAntMsgExtInfo, "@Message.ExtendedInfo");
188
189 static struct RedfishPropertyParent statusAntMsgExtInfoSet1;
190 bejTreeInitSet(&statusAntMsgExtInfoSet1, nullptr);
191
192 static struct RedfishPropertyLeafString statusAntMsgExtInfoSet1P1;
193 bejTreeAddString(&statusAntMsgExtInfoSet1, &statusAntMsgExtInfoSet1P1,
194 "MessageId", "PredictiveFailure");
195
196 static struct RedfishPropertyParent statusAntMsgExtInfoSet1P2;
197 bejTreeInitArray(&statusAntMsgExtInfoSet1P2, "RelatedProperties");
198 bejTreeLinkChildToParent(&statusAntMsgExtInfoSet1,
199 &statusAntMsgExtInfoSet1P2);
200
201 static struct RedfishPropertyLeafString statusAntMsgExtInfoSet1P2Ele1;
202 bejTreeAddString(&statusAntMsgExtInfoSet1P2, &statusAntMsgExtInfoSet1P2Ele1,
203 "", "FailurePredicted");
204
205 static struct RedfishPropertyLeafString statusAntMsgExtInfoSet1P2Ele2;
206 bejTreeAddString(&statusAntMsgExtInfoSet1P2, &statusAntMsgExtInfoSet1P2Ele2,
207 "", "MediaType");
208
209 bejTreeLinkChildToParent(&statusAntMsgExtInfo, &statusAntMsgExtInfoSet1);
210 bejTreeLinkChildToParent(&statusAnt, &statusAntMsgExtInfo);
211 bejTreeLinkChildToParent(&root, &statusAnt);
212
213 static struct RedfishPropertyParent identifiers;
214 bejTreeInitArray(&identifiers, "Identifiers");
215 bejTreeLinkChildToParent(&root, &identifiers);
216
217 static struct RedfishPropertyParent links;
218 bejTreeInitSet(&links, "Links");
219 bejTreeLinkChildToParent(&root, &links);
220
221 return &root;
222}
223
224/**
225 * @brief Storage for an array of links with an annotated odata.count.
226 *
227 * This doesn't contain storage for the link itself.
228 *
229 * Eg:
230 * "Contains": [],
231 * "Contains@odata.count": 0,
232 */
233struct RedfishArrayOfLinksJson
234{
235 struct RedfishPropertyParent array;
236 struct RedfishPropertyParent annotatedProperty;
237 struct RedfishPropertyLeafInt count;
238};
239
240/**
241 * @brief Storage for a single odata.id link inside a JSON "Set" object.
242 *
243 * Eg: FieldName: {
244 * "@odata.id": "/redfish/v1/Chassis/Something"
245 * }
246 */
247struct RedfishLinkJson
248{
249 struct RedfishPropertyParent set;
250 struct RedfishPropertyLeafString odataId;
251};
252
253void addLinkToTree(struct RedfishPropertyParent* parent,
254 struct RedfishPropertyParent* linkSet,
255 const char* linkSetLabel,
256 struct RedfishPropertyLeafString* odataId,
257 const char* linkValue)
258{
259 bejTreeInitSet(linkSet, linkSetLabel);
260 bejTreeAddString(linkSet, odataId, "@odata.id", linkValue);
261 bejTreeLinkChildToParent(parent, linkSet);
262}
263
264void redfishCreateArrayOfLinksJson(struct RedfishPropertyParent* parent,
265 const char* arrayName, int linkCount,
266 const char* const links[],
267 struct RedfishArrayOfLinksJson* linksInfo,
268 struct RedfishLinkJson* linkJsonArray)
269{
270 bejTreeInitArray(&linksInfo->array, arrayName);
271 bejTreeLinkChildToParent(parent, &linksInfo->array);
272
273 bejTreeInitPropertyAnnotated(&linksInfo->annotatedProperty, arrayName);
274 bejTreeAddInteger(&linksInfo->annotatedProperty, &linksInfo->count,
275 "@odata.count", linkCount);
276 bejTreeLinkChildToParent(parent, &linksInfo->annotatedProperty);
277
278 for (int i = 0; i < linkCount; ++i)
279 {
280 addLinkToTree(&linksInfo->array, &linkJsonArray[i].set, NULL,
281 &linkJsonArray[i].odataId, links[i]);
282 }
283}
284
285struct RedfishPropertyParent* createChassisResource()
286{
287 constexpr int containsLinkCount = 2;
288 const char* contains[containsLinkCount] = {"/redfish/v1/Chassis/Disk_0",
289 "/redfish/v1/Chassis/Disk_1"};
290 const char* storage[1] = {"/redfish/v1/Systems/system/Storage/SATA"};
291 const char* drives[1] = {"/redfish/v1/Chassis/SomeChassis/Drives/SATA_0"};
292 static struct RedfishPropertyParent root;
293 static struct RedfishPropertyLeafString odataId;
294 static struct RedfishPropertyParent links;
295 static struct RedfishPropertyParent computerSystemsArray;
296 static struct RedfishPropertyParent computerSystemsLinkSet;
297 static struct RedfishPropertyLeafString computerSystemsLinkOdataId;
298 static struct RedfishPropertyParent containedBySet;
299 static struct RedfishPropertyLeafString containedByOdataId;
300 static struct RedfishArrayOfLinksJson containsArray;
301 static struct RedfishLinkJson containsLinks[containsLinkCount];
302 static struct RedfishArrayOfLinksJson storageArray;
303 static struct RedfishLinkJson storageLink;
304 static struct RedfishArrayOfLinksJson drives_array;
305 static struct RedfishLinkJson drive_link;
306
307 bejTreeInitSet(&root, "Chassis");
308 bejTreeAddString(&root, &odataId, "@odata.id",
309 "/redfish/v1/Chassis/SomeChassis");
310
311 bejTreeInitSet(&links, "Links");
312 bejTreeLinkChildToParent(&root, &links);
313
314 bejTreeInitArray(&computerSystemsArray, "ComputerSystems");
315 bejTreeLinkChildToParent(&links, &computerSystemsArray);
316
317 addLinkToTree(&computerSystemsArray, &computerSystemsLinkSet, "",
318 &computerSystemsLinkOdataId, "/redfish/v1/Systems/system");
319
320 addLinkToTree(&links, &containedBySet, "ContainedBy", &containedByOdataId,
321 "/redfish/v1/Chassis/SomeOtherChassis");
322
323 redfishCreateArrayOfLinksJson(&links, "Contains", containsLinkCount,
324 contains, &containsArray, containsLinks);
325
326 redfishCreateArrayOfLinksJson(&links, "Storage", /*linkCount=*/1, storage,
327 &storageArray, &storageLink);
328
329 redfishCreateArrayOfLinksJson(&links, "Drives", /*linkCount=*/1, drives,
330 &drives_array, &drive_link);
331
332 return &root;
333}
334
335TEST_P(BejEncoderTest, Encode)
336{
337 const BejEncoderTestParams& test_case = GetParam();
338 auto inputsOrErr = loadInputs(test_case.inputFiles);
339 EXPECT_TRUE(inputsOrErr);
340
341 BejDictionaries dictionaries = {
342 .schemaDictionary = inputsOrErr->schemaDictionary,
343 .annotationDictionary = inputsOrErr->annotationDictionary,
344 .errorDictionary = inputsOrErr->errorDictionary,
345 };
346
347 std::vector<uint8_t> outputBuffer;
348 struct BejEncoderOutputHandler output = {
349 .handlerContext = &outputBuffer,
350 .recvOutput = &getBejEncodedBuffer,
351 };
352
353 std::vector<void*> pointerStack;
354 struct BejPointerStackCallback stackCallbacks = {
355 .stackContext = &pointerStack,
356 .stackEmpty = stackEmpty,
357 .stackPeek = stackPeek,
358 .stackPop = stackPop,
359 .stackPush = stackPush,
360 .deleteStack = NULL,
361 };
362
363 bejEncode(&dictionaries, BEJ_DICTIONARY_START_AT_HEAD, bejMajorSchemaClass,
364 test_case.createResource(), &output, &stackCallbacks);
365
366 BejDecoderJson decoder;
367 EXPECT_THAT(decoder.decode(dictionaries, std::span(outputBuffer)), 0);
368 std::string decoded = decoder.getOutput();
369 nlohmann::json jsonDecoded = nlohmann::json::parse(decoded);
370
371 if (!test_case.expectedJson.empty())
372 {
373 inputsOrErr->expectedJson =
374 nlohmann::json::parse(test_case.expectedJson);
375 }
376 EXPECT_TRUE(jsonDecoded.dump() == inputsOrErr->expectedJson.dump());
377}
378
379TEST_P(BejEncoderTest, EncodeWrapper)
380{
381 const BejEncoderTestParams& test_case = GetParam();
382 auto inputsOrErr = loadInputs(test_case.inputFiles);
383 EXPECT_TRUE(inputsOrErr);
384
385 BejDictionaries dictionaries = {
386 .schemaDictionary = inputsOrErr->schemaDictionary,
387 .annotationDictionary = inputsOrErr->annotationDictionary,
388 .errorDictionary = inputsOrErr->errorDictionary,
389 };
390
391 libbej::BejEncoderJson encoder;
392 encoder.encode(&dictionaries, bejMajorSchemaClass,
393 test_case.createResource());
394
395 std::vector<uint8_t> outputBuffer = encoder.getOutput();
396 BejDecoderJson decoder;
397 EXPECT_THAT(decoder.decode(dictionaries, std::span(outputBuffer)), 0);
398 std::string decoded = decoder.getOutput();
399 nlohmann::json jsonDecoded = nlohmann::json::parse(decoded);
400
401 if (!test_case.expectedJson.empty())
402 {
403 inputsOrErr->expectedJson =
404 nlohmann::json::parse(test_case.expectedJson);
405 }
406 EXPECT_TRUE(jsonDecoded.dump() == inputsOrErr->expectedJson.dump());
407
408 // Try using the same encoder object again to ensure that the same object
409 // does the encoding correctly
410 encoder.encode(&dictionaries, bejMajorSchemaClass,
411 test_case.createResource());
412
413 outputBuffer = encoder.getOutput();
414
415 EXPECT_THAT(decoder.decode(dictionaries, std::span(outputBuffer)), 0);
416 decoded = decoder.getOutput();
417 jsonDecoded = nlohmann::json::parse(decoded);
418
419 if (!test_case.expectedJson.empty())
420 {
421 inputsOrErr->expectedJson =
422 nlohmann::json::parse(test_case.expectedJson);
423 }
424 EXPECT_TRUE(jsonDecoded.dump() == inputsOrErr->expectedJson.dump());
425}
426
427/**
428 * TODO: Add more test cases.
429 */
430
431INSTANTIATE_TEST_SUITE_P(
432 , BejEncoderTest,
433 testing::ValuesIn<BejEncoderTestParams>({
434 {"DriveOEM", driveOemTestFiles, driveOemJson, &createDriveOem},
435 {"DummySimple", dummySimpleTestFiles, "", &createDummyResource},
436 {"Chassis", chassisTestFiles, "", &createChassisResource},
437 }),
438 [](const testing::TestParamInfo<BejEncoderTest::ParamType>& info) {
439 return info.param.testName;
440});
441
442} // namespace libbej