xref: /openbmc/libbej/test/bej_encoder_test.cpp (revision a46f9850)
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