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