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 26 using BejEncoderTest = testing::TestWithParam<BejEncoderTestParams>; 27 28 const 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 36 const 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 44 const 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 52 struct 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 94 const 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 124 struct 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 */ 233 struct 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 */ 247 struct RedfishLinkJson 248 { 249 struct RedfishPropertyParent set; 250 struct RedfishPropertyLeafString odataId; 251 }; 252 253 void 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 264 void 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 285 struct 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 335 TEST_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 379 TEST_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 431 INSTANTIATE_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 443