blob: c1d6de6e62b43f73f326e4fb6c6ffe839ccd081b [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
Patrick Williamsbe27f2e2024-08-16 15:22:35 -0400264void redfishCreateArrayOfLinksJson(
265 struct RedfishPropertyParent* parent, const char* arrayName, int linkCount,
266 const char* const links[], struct RedfishArrayOfLinksJson* linksInfo,
267 struct RedfishLinkJson* linkJsonArray)
Nikhil Namjoshida6e5572023-03-13 10:52:53 -0700268{
269 bejTreeInitArray(&linksInfo->array, arrayName);
270 bejTreeLinkChildToParent(parent, &linksInfo->array);
271
272 bejTreeInitPropertyAnnotated(&linksInfo->annotatedProperty, arrayName);
273 bejTreeAddInteger(&linksInfo->annotatedProperty, &linksInfo->count,
274 "@odata.count", linkCount);
275 bejTreeLinkChildToParent(parent, &linksInfo->annotatedProperty);
276
277 for (int i = 0; i < linkCount; ++i)
278 {
279 addLinkToTree(&linksInfo->array, &linkJsonArray[i].set, NULL,
280 &linkJsonArray[i].odataId, links[i]);
281 }
282}
283
284struct RedfishPropertyParent* createChassisResource()
285{
286 constexpr int containsLinkCount = 2;
287 const char* contains[containsLinkCount] = {"/redfish/v1/Chassis/Disk_0",
288 "/redfish/v1/Chassis/Disk_1"};
289 const char* storage[1] = {"/redfish/v1/Systems/system/Storage/SATA"};
290 const char* drives[1] = {"/redfish/v1/Chassis/SomeChassis/Drives/SATA_0"};
291 static struct RedfishPropertyParent root;
292 static struct RedfishPropertyLeafString odataId;
293 static struct RedfishPropertyParent links;
294 static struct RedfishPropertyParent computerSystemsArray;
295 static struct RedfishPropertyParent computerSystemsLinkSet;
296 static struct RedfishPropertyLeafString computerSystemsLinkOdataId;
297 static struct RedfishPropertyParent containedBySet;
298 static struct RedfishPropertyLeafString containedByOdataId;
299 static struct RedfishArrayOfLinksJson containsArray;
300 static struct RedfishLinkJson containsLinks[containsLinkCount];
301 static struct RedfishArrayOfLinksJson storageArray;
302 static struct RedfishLinkJson storageLink;
303 static struct RedfishArrayOfLinksJson drives_array;
304 static struct RedfishLinkJson drive_link;
305
306 bejTreeInitSet(&root, "Chassis");
307 bejTreeAddString(&root, &odataId, "@odata.id",
308 "/redfish/v1/Chassis/SomeChassis");
309
310 bejTreeInitSet(&links, "Links");
311 bejTreeLinkChildToParent(&root, &links);
312
313 bejTreeInitArray(&computerSystemsArray, "ComputerSystems");
314 bejTreeLinkChildToParent(&links, &computerSystemsArray);
315
316 addLinkToTree(&computerSystemsArray, &computerSystemsLinkSet, "",
317 &computerSystemsLinkOdataId, "/redfish/v1/Systems/system");
318
319 addLinkToTree(&links, &containedBySet, "ContainedBy", &containedByOdataId,
320 "/redfish/v1/Chassis/SomeOtherChassis");
321
322 redfishCreateArrayOfLinksJson(&links, "Contains", containsLinkCount,
323 contains, &containsArray, containsLinks);
324
325 redfishCreateArrayOfLinksJson(&links, "Storage", /*linkCount=*/1, storage,
326 &storageArray, &storageLink);
327
328 redfishCreateArrayOfLinksJson(&links, "Drives", /*linkCount=*/1, drives,
329 &drives_array, &drive_link);
330
331 return &root;
332}
333
334TEST_P(BejEncoderTest, Encode)
335{
336 const BejEncoderTestParams& test_case = GetParam();
337 auto inputsOrErr = loadInputs(test_case.inputFiles);
338 EXPECT_TRUE(inputsOrErr);
339
340 BejDictionaries dictionaries = {
341 .schemaDictionary = inputsOrErr->schemaDictionary,
342 .annotationDictionary = inputsOrErr->annotationDictionary,
343 .errorDictionary = inputsOrErr->errorDictionary,
344 };
345
346 std::vector<uint8_t> outputBuffer;
347 struct BejEncoderOutputHandler output = {
348 .handlerContext = &outputBuffer,
349 .recvOutput = &getBejEncodedBuffer,
350 };
351
352 std::vector<void*> pointerStack;
353 struct BejPointerStackCallback stackCallbacks = {
354 .stackContext = &pointerStack,
355 .stackEmpty = stackEmpty,
356 .stackPeek = stackPeek,
357 .stackPop = stackPop,
358 .stackPush = stackPush,
359 .deleteStack = NULL,
360 };
361
362 bejEncode(&dictionaries, BEJ_DICTIONARY_START_AT_HEAD, bejMajorSchemaClass,
363 test_case.createResource(), &output, &stackCallbacks);
364
365 BejDecoderJson decoder;
366 EXPECT_THAT(decoder.decode(dictionaries, std::span(outputBuffer)), 0);
367 std::string decoded = decoder.getOutput();
368 nlohmann::json jsonDecoded = nlohmann::json::parse(decoded);
369
370 if (!test_case.expectedJson.empty())
371 {
372 inputsOrErr->expectedJson =
373 nlohmann::json::parse(test_case.expectedJson);
374 }
375 EXPECT_TRUE(jsonDecoded.dump() == inputsOrErr->expectedJson.dump());
376}
377
378TEST_P(BejEncoderTest, EncodeWrapper)
379{
380 const BejEncoderTestParams& test_case = GetParam();
381 auto inputsOrErr = loadInputs(test_case.inputFiles);
382 EXPECT_TRUE(inputsOrErr);
383
384 BejDictionaries dictionaries = {
385 .schemaDictionary = inputsOrErr->schemaDictionary,
386 .annotationDictionary = inputsOrErr->annotationDictionary,
387 .errorDictionary = inputsOrErr->errorDictionary,
388 };
389
390 libbej::BejEncoderJson encoder;
391 encoder.encode(&dictionaries, bejMajorSchemaClass,
392 test_case.createResource());
393
394 std::vector<uint8_t> outputBuffer = encoder.getOutput();
395 BejDecoderJson decoder;
396 EXPECT_THAT(decoder.decode(dictionaries, std::span(outputBuffer)), 0);
397 std::string decoded = decoder.getOutput();
398 nlohmann::json jsonDecoded = nlohmann::json::parse(decoded);
399
400 if (!test_case.expectedJson.empty())
401 {
402 inputsOrErr->expectedJson =
403 nlohmann::json::parse(test_case.expectedJson);
404 }
405 EXPECT_TRUE(jsonDecoded.dump() == inputsOrErr->expectedJson.dump());
406
407 // Try using the same encoder object again to ensure that the same object
408 // does the encoding correctly
409 encoder.encode(&dictionaries, bejMajorSchemaClass,
410 test_case.createResource());
411
412 outputBuffer = encoder.getOutput();
413
414 EXPECT_THAT(decoder.decode(dictionaries, std::span(outputBuffer)), 0);
415 decoded = decoder.getOutput();
416 jsonDecoded = nlohmann::json::parse(decoded);
417
418 if (!test_case.expectedJson.empty())
419 {
420 inputsOrErr->expectedJson =
421 nlohmann::json::parse(test_case.expectedJson);
422 }
423 EXPECT_TRUE(jsonDecoded.dump() == inputsOrErr->expectedJson.dump());
424}
425
426/**
427 * TODO: Add more test cases.
428 */
429
430INSTANTIATE_TEST_SUITE_P(
431 , BejEncoderTest,
432 testing::ValuesIn<BejEncoderTestParams>({
433 {"DriveOEM", driveOemTestFiles, driveOemJson, &createDriveOem},
434 {"DummySimple", dummySimpleTestFiles, "", &createDummyResource},
435 {"Chassis", chassisTestFiles, "", &createChassisResource},
436 }),
437 [](const testing::TestParamInfo<BejEncoderTest::ParamType>& info) {
Patrick Williamsbe27f2e2024-08-16 15:22:35 -0400438 return info.param.testName;
439 });
Nikhil Namjoshida6e5572023-03-13 10:52:53 -0700440
441} // namespace libbej