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( 265 struct RedfishPropertyParent* parent, const char* arrayName, int linkCount, 266 const char* const links[], struct RedfishArrayOfLinksJson* linksInfo, 267 struct RedfishLinkJson* linkJsonArray) 268 { 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 284 struct 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 334 TEST_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 378 TEST_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 430 INSTANTIATE_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) { 438 return info.param.testName; 439 }); 440 441 } // namespace libbej 442