blob: 8f39f22c953e02734c926ca97eed992e462e54c6 [file] [edit]
#include "bej_dictionary.h"
#include "bej_encoder_core.h"
#include "bej_tree.h"
#include "bej_common_test.hpp"
#include "bej_decoder_json.hpp"
#include "bej_encoder_json.hpp"
#include <vector>
#include <gmock/gmock-matchers.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
namespace libbej
{
struct BejEncoderTestParams
{
const std::string testName;
const BejTestInputFiles inputFiles;
std::string expectedJson;
struct RedfishPropertyParent* (*createResource)();
};
void PrintTo(const BejEncoderTestParams& params, std::ostream* os)
{
*os << params.testName;
}
using BejEncoderTest = testing::TestWithParam<BejEncoderTestParams>;
const BejTestInputFiles dummySimpleTestFiles = {
.jsonFile = "../test/json/dummysimple.json",
.schemaDictionaryFile = "../test/dictionaries/dummy_simple_dict.bin",
.annotationDictionaryFile = "../test/dictionaries/annotation_dict.bin",
.errorDictionaryFile = "",
.encodedStreamFile = "../test/encoded/dummy_simple_enc.bin",
};
const BejTestInputFiles driveOemTestFiles = {
.jsonFile = "../test/json/drive_oem.json",
.schemaDictionaryFile = "../test/dictionaries/drive_oem_dict.bin",
.annotationDictionaryFile = "../test/dictionaries/annotation_dict.bin",
.errorDictionaryFile = "",
.encodedStreamFile = "../test/encoded/drive_oem_enc.bin",
};
const BejTestInputFiles chassisTestFiles = {
.jsonFile = "../test/json/chassis.json",
.schemaDictionaryFile = "../test/dictionaries/chassis_dict.bin",
.annotationDictionaryFile = "../test/dictionaries/annotation_dict.bin",
.errorDictionaryFile = "",
.encodedStreamFile = "../test/encoded/chassis_enc.bin",
};
const BejTestInputFiles chassisCollectionTestFiles = {
.jsonFile = "../test/json/chassis_collection.json",
.schemaDictionaryFile = "../test/dictionaries/chassis_collection_dict.bin",
.annotationDictionaryFile = "../test/dictionaries/annotation_dict.bin",
.errorDictionaryFile = "",
.encodedStreamFile = nullptr,
};
int recvOutput(const void* data, size_t data_size, void* handlerContext)
{
auto stack = reinterpret_cast<std::vector<uint8_t>*>(handlerContext);
const uint8_t* dataBuf = reinterpret_cast<const uint8_t*>(data);
stack->insert(stack->end(), dataBuf, dataBuf + data_size);
return 0;
}
bool stackEmpty(void* dataPtr)
{
auto stack = reinterpret_cast<std::vector<void*>*>(dataPtr);
return stack->empty();
}
void* stackPeek(void* dataPtr)
{
auto stack = reinterpret_cast<std::vector<void*>*>(dataPtr);
if (stack->empty())
{
return nullptr;
}
return stack->back();
}
void* stackPop(void* dataPtr)
{
auto stack = reinterpret_cast<std::vector<void*>*>(dataPtr);
if (stack->empty())
{
return nullptr;
}
void* value = stack->back();
stack->pop_back();
return value;
}
int stackPush(void* p, void* dataPtr)
{
auto stack = reinterpret_cast<std::vector<void*>*>(dataPtr);
stack->push_back(p);
return 0;
}
const std::string dummyJson = R"(
{
"Id": "Dummy ID",
"SampleIntegerProperty": -5,
"SampleRealProperty": -0.0294,
"SampleEnabledProperty": null,
"ChildArrayProperty": [
{
"AnotherBoolean": true,
"LinkStatus": "NoLink"
},
{
"LinkStatus": "LinkDown"
}
]
}
)";
struct RedfishPropertyParent* createDummyResource()
{
static struct RedfishPropertyParent root;
bejTreeInitSet(&root, "DummySimple");
static struct RedfishPropertyLeafString id;
bejTreeAddString(&root, &id, "Id", "Dummy ID");
static struct RedfishPropertyLeafInt intProp;
bejTreeAddInteger(&root, &intProp, "SampleIntegerProperty", -5);
static struct RedfishPropertyLeafReal real;
bejTreeAddReal(&root, &real, "SampleRealProperty", -0.0294);
static struct RedfishPropertyLeafNull enProp;
bejTreeAddNull(&root, &enProp, "SampleEnabledProperty");
static struct RedfishPropertyParent chArraySet1;
bejTreeInitSet(&chArraySet1, nullptr);
static struct RedfishPropertyLeafBool chArraySet1bool;
bejTreeAddBool(&chArraySet1, &chArraySet1bool, "AnotherBoolean", true);
static struct RedfishPropertyLeafEnum chArraySet1Ls;
bejTreeAddEnum(&chArraySet1, &chArraySet1Ls, "LinkStatus", "NoLink");
static struct RedfishPropertyParent chArraySet2;
bejTreeInitSet(&chArraySet2, nullptr);
static struct RedfishPropertyLeafEnum chArraySet2Ls;
bejTreeAddEnum(&chArraySet2, &chArraySet2Ls, "LinkStatus", "LinkDown");
static struct RedfishPropertyParent chArray;
bejTreeInitArray(&chArray, "ChildArrayProperty");
bejTreeLinkChildToParent(&chArray, &chArraySet1);
bejTreeLinkChildToParent(&chArray, &chArraySet2);
bejTreeLinkChildToParent(&root, &chArray);
return &root;
}
const std::string driveOemJson = R"(
{
"@odata.id": "/redfish/v1/drives/1",
"@odata.type": "#Drive.v1_5_0.Drive",
"Id": "Drive1",
"Actions": {
"#Drive.Reset": {
"target": "/redfish/v1/drives/1/Actions/Drive.Reset",
"title": "Reset a Drive",
"ResetType@Redfish.AllowableValues": [
"On",
"ForceOff",
"ForceRestart",
"Nmi",
"ForceOn",
"PushPowerButton"
]
}
},
"Status@Message.ExtendedInfo": [
{
"MessageId": "PredictiveFailure",
"RelatedProperties": ["FailurePredicted", "MediaType"]
}
],
"Identifiers": [],
"Links": {}
}
)";
struct RedfishPropertyParent* createDriveOem()
{
static struct RedfishPropertyParent root;
bejTreeInitSet(&root, "Drive");
static struct RedfishPropertyLeafString odataId;
bejTreeAddString(&root, &odataId, "@odata.id", "/redfish/v1/drives/1");
static struct RedfishPropertyLeafString odataType;
bejTreeAddString(&root, &odataType, "@odata.type", "#Drive.v1_5_0.Drive");
static struct RedfishPropertyLeafString id;
bejTreeAddString(&root, &id, "Id", "Drive1");
static struct RedfishPropertyParent actions;
bejTreeInitSet(&actions, "Actions");
static struct RedfishPropertyParent drRst;
bejTreeInitSet(&drRst, "#Drive.Reset");
static struct RedfishPropertyLeafString drRstTarget;
bejTreeAddString(&drRst, &drRstTarget, "target",
"/redfish/v1/drives/1/Actions/Drive.Reset");
static struct RedfishPropertyLeafString drRstTitle;
bejTreeAddString(&drRst, &drRstTitle, "title", "Reset a Drive");
static struct RedfishPropertyParent drRstType;
bejTreeInitPropertyAnnotated(&drRstType, "ResetType");
static struct RedfishPropertyParent drRstTypeAllowable;
bejTreeInitArray(&drRstTypeAllowable, "@Redfish.AllowableValues");
static struct RedfishPropertyLeafString drRstTypeAllowableS1;
bejTreeAddString(&drRstTypeAllowable, &drRstTypeAllowableS1, "", "On");
static struct RedfishPropertyLeafString drRstTypeAllowableS2;
bejTreeAddString(&drRstTypeAllowable, &drRstTypeAllowableS2, "",
"ForceOff");
static struct RedfishPropertyLeafString drRstTypeAllowableS3;
bejTreeAddString(&drRstTypeAllowable, &drRstTypeAllowableS3, "",
"ForceRestart");
static struct RedfishPropertyLeafString drRstTypeAllowableS4;
bejTreeAddString(&drRstTypeAllowable, &drRstTypeAllowableS4, "", "Nmi");
static struct RedfishPropertyLeafString drRstTypeAllowableS5;
bejTreeAddString(&drRstTypeAllowable, &drRstTypeAllowableS5, "", "ForceOn");
static struct RedfishPropertyLeafString drRstTypeAllowableS6;
bejTreeAddString(&drRstTypeAllowable, &drRstTypeAllowableS6, "",
"PushPowerButton");
bejTreeLinkChildToParent(&drRstType, &drRstTypeAllowable);
bejTreeLinkChildToParent(&drRst, &drRstType);
bejTreeLinkChildToParent(&actions, &drRst);
bejTreeLinkChildToParent(&root, &actions);
static struct RedfishPropertyParent statusAnt;
bejTreeInitPropertyAnnotated(&statusAnt, "Status");
static struct RedfishPropertyParent statusAntMsgExtInfo;
bejTreeInitArray(&statusAntMsgExtInfo, "@Message.ExtendedInfo");
static struct RedfishPropertyParent statusAntMsgExtInfoSet1;
bejTreeInitSet(&statusAntMsgExtInfoSet1, nullptr);
static struct RedfishPropertyLeafString statusAntMsgExtInfoSet1P1;
bejTreeAddString(&statusAntMsgExtInfoSet1, &statusAntMsgExtInfoSet1P1,
"MessageId", "PredictiveFailure");
static struct RedfishPropertyParent statusAntMsgExtInfoSet1P2;
bejTreeInitArray(&statusAntMsgExtInfoSet1P2, "RelatedProperties");
bejTreeLinkChildToParent(&statusAntMsgExtInfoSet1,
&statusAntMsgExtInfoSet1P2);
static struct RedfishPropertyLeafString statusAntMsgExtInfoSet1P2Ele1;
bejTreeAddString(&statusAntMsgExtInfoSet1P2, &statusAntMsgExtInfoSet1P2Ele1,
"", "FailurePredicted");
static struct RedfishPropertyLeafString statusAntMsgExtInfoSet1P2Ele2;
bejTreeAddString(&statusAntMsgExtInfoSet1P2, &statusAntMsgExtInfoSet1P2Ele2,
"", "MediaType");
bejTreeLinkChildToParent(&statusAntMsgExtInfo, &statusAntMsgExtInfoSet1);
bejTreeLinkChildToParent(&statusAnt, &statusAntMsgExtInfo);
bejTreeLinkChildToParent(&root, &statusAnt);
static struct RedfishPropertyParent identifiers;
bejTreeInitArray(&identifiers, "Identifiers");
bejTreeLinkChildToParent(&root, &identifiers);
static struct RedfishPropertyParent links;
bejTreeInitSet(&links, "Links");
bejTreeLinkChildToParent(&root, &links);
return &root;
}
/**
* @brief Storage for an array of links with an annotated odata.count.
*
* This doesn't contain storage for the link itself.
*
* Eg:
* "Contains": [],
* "Contains@odata.count": 0,
*/
struct RedfishArrayOfLinksJson
{
struct RedfishPropertyParent array;
struct RedfishPropertyParent annotatedProperty;
struct RedfishPropertyLeafInt count;
};
/**
* @brief Storage for a single odata.id link inside a JSON "Set" object.
*
* Eg: FieldName: {
* "@odata.id": "/redfish/v1/Chassis/Something"
* }
*/
struct RedfishLinkJson
{
struct RedfishPropertyParent set;
struct RedfishPropertyLeafString odataId;
};
/**
* @brief Storage for a single odata.id link inside a JSON "Set" object.
*
* Eg: FieldName: {
* "@odata.id": "/redfish/v1/Chassis/Something"
* }
*/
void addLinkToTree(struct RedfishPropertyParent* parent,
struct RedfishPropertyParent* linkSet,
const char* linkSetLabel,
struct RedfishPropertyLeafString* odataId,
const char* linkValue)
{
bejTreeInitSet(linkSet, linkSetLabel);
bejTreeAddString(linkSet, odataId, "@odata.id", linkValue);
bejTreeLinkChildToParent(parent, linkSet);
}
void redfishCreateArrayOfLinksJson(
struct RedfishPropertyParent* parent, const char* arrayName, int linkCount,
const char* const links[], struct RedfishArrayOfLinksJson* linksInfo,
struct RedfishLinkJson* linkJsonArray)
{
bejTreeInitArray(&linksInfo->array, arrayName);
bejTreeLinkChildToParent(parent, &linksInfo->array);
bejTreeInitPropertyAnnotated(&linksInfo->annotatedProperty, arrayName);
bejTreeAddInteger(&linksInfo->annotatedProperty, &linksInfo->count,
"@odata.count", linkCount);
bejTreeLinkChildToParent(parent, &linksInfo->annotatedProperty);
for (int i = 0; i < linkCount; ++i)
{
addLinkToTree(&linksInfo->array, &linkJsonArray[i].set, nullptr,
&linkJsonArray[i].odataId, links[i]);
}
}
struct RedfishPropertyParent* createChassisResource()
{
constexpr int containsLinkCount = 2;
const char* contains[containsLinkCount] = {"/redfish/v1/Chassis/Disk_0",
"/redfish/v1/Chassis/Disk_1"};
const char* storage[1] = {"/redfish/v1/Systems/system/Storage/SATA"};
const char* drives[1] = {"/redfish/v1/Chassis/SomeChassis/Drives/SATA_0"};
static struct RedfishPropertyParent root;
static struct RedfishPropertyLeafString odataId;
static struct RedfishPropertyParent links;
static struct RedfishPropertyParent computerSystemsArray;
static struct RedfishPropertyParent computerSystemsLinkSet;
static struct RedfishPropertyLeafString computerSystemsLinkOdataId;
static struct RedfishPropertyParent containedBySet;
static struct RedfishPropertyLeafString containedByOdataId;
static struct RedfishArrayOfLinksJson containsArray;
static struct RedfishLinkJson containsLinks[containsLinkCount];
static struct RedfishArrayOfLinksJson storageArray;
static struct RedfishLinkJson storageLink;
static struct RedfishArrayOfLinksJson drives_array;
static struct RedfishLinkJson drive_link;
bejTreeInitSet(&root, "Chassis");
bejTreeAddString(&root, &odataId, "@odata.id",
"/redfish/v1/Chassis/SomeChassis");
bejTreeInitSet(&links, "Links");
bejTreeLinkChildToParent(&root, &links);
bejTreeInitArray(&computerSystemsArray, "ComputerSystems");
bejTreeLinkChildToParent(&links, &computerSystemsArray);
addLinkToTree(&computerSystemsArray, &computerSystemsLinkSet, "",
&computerSystemsLinkOdataId, "/redfish/v1/Systems/system");
addLinkToTree(&links, &containedBySet, "ContainedBy", &containedByOdataId,
"/redfish/v1/Chassis/SomeOtherChassis");
redfishCreateArrayOfLinksJson(&links, "Contains", containsLinkCount,
contains, &containsArray, containsLinks);
redfishCreateArrayOfLinksJson(&links, "Storage", /*linkCount=*/1, storage,
&storageArray, &storageLink);
redfishCreateArrayOfLinksJson(&links, "Drives", /*linkCount=*/1, drives,
&drives_array, &drive_link);
return &root;
}
TEST_P(BejEncoderTest, Encode)
{
const BejEncoderTestParams& test_case = GetParam();
auto inputsOrErr = loadInputs(test_case.inputFiles);
EXPECT_TRUE(inputsOrErr);
BejDictionaries dictionaries = {
.schemaDictionary = inputsOrErr->schemaDictionary,
.schemaDictionarySize = inputsOrErr->schemaDictionarySize,
.annotationDictionary = inputsOrErr->annotationDictionary,
.annotationDictionarySize = inputsOrErr->annotationDictionarySize,
.errorDictionary = inputsOrErr->errorDictionary,
.errorDictionarySize = inputsOrErr->errorDictionarySize,
};
std::vector<uint8_t> outputBuffer;
struct BejEncoderOutputHandler output = {
.handlerContext = &outputBuffer,
.recvOutput = &getBejEncodedBuffer,
};
std::vector<void*> pointerStack;
struct BejPointerStackCallback stackCallbacks = {
.stackContext = &pointerStack,
.stackEmpty = stackEmpty,
.stackPeek = stackPeek,
.stackPop = stackPop,
.stackPush = stackPush,
.deleteStack = nullptr,
};
bejEncode(&dictionaries, BEJ_DICTIONARY_START_AT_HEAD, bejMajorSchemaClass,
test_case.createResource(), &output, &stackCallbacks);
BejDecoderJson decoder;
EXPECT_THAT(decoder.decode(dictionaries, std::span(outputBuffer)), 0);
std::string decoded = decoder.getOutput();
nlohmann::json jsonDecoded = nlohmann::json::parse(decoded);
if (!test_case.expectedJson.empty())
{
inputsOrErr->expectedJson =
nlohmann::json::parse(test_case.expectedJson);
}
EXPECT_TRUE(jsonDecoded.dump() == inputsOrErr->expectedJson.dump());
}
TEST_P(BejEncoderTest, EncodeWrapper)
{
const BejEncoderTestParams& test_case = GetParam();
auto inputsOrErr = loadInputs(test_case.inputFiles);
EXPECT_TRUE(inputsOrErr);
BejDictionaries dictionaries = {
.schemaDictionary = inputsOrErr->schemaDictionary,
.schemaDictionarySize = inputsOrErr->schemaDictionarySize,
.annotationDictionary = inputsOrErr->annotationDictionary,
.annotationDictionarySize = inputsOrErr->annotationDictionarySize,
.errorDictionary = inputsOrErr->errorDictionary,
.errorDictionarySize = inputsOrErr->errorDictionarySize,
};
libbej::BejEncoderJson encoder;
encoder.encode(&dictionaries, bejMajorSchemaClass,
test_case.createResource());
std::vector<uint8_t> outputBuffer = encoder.getOutput();
BejDecoderJson decoder;
EXPECT_THAT(decoder.decode(dictionaries, std::span(outputBuffer)), 0);
std::string decoded = decoder.getOutput();
nlohmann::json jsonDecoded = nlohmann::json::parse(decoded);
if (!test_case.expectedJson.empty())
{
inputsOrErr->expectedJson =
nlohmann::json::parse(test_case.expectedJson);
}
EXPECT_TRUE(jsonDecoded.dump() == inputsOrErr->expectedJson.dump());
// Try using the same encoder object again to ensure that the same object
// does the encoding correctly
encoder.encode(&dictionaries, bejMajorSchemaClass,
test_case.createResource());
outputBuffer = encoder.getOutput();
EXPECT_THAT(decoder.decode(dictionaries, std::span(outputBuffer)), 0);
decoded = decoder.getOutput();
jsonDecoded = nlohmann::json::parse(decoded);
if (!test_case.expectedJson.empty())
{
inputsOrErr->expectedJson =
nlohmann::json::parse(test_case.expectedJson);
}
EXPECT_TRUE(jsonDecoded.dump() == inputsOrErr->expectedJson.dump());
}
/**
* TODO: Add more test cases.
*/
INSTANTIATE_TEST_SUITE_P(
, BejEncoderTest,
testing::ValuesIn<BejEncoderTestParams>({
{"DriveOEM", driveOemTestFiles, driveOemJson, &createDriveOem},
{"DummySimple", dummySimpleTestFiles, dummyJson, &createDummyResource},
{"Chassis", chassisTestFiles, "", &createChassisResource},
}),
[](const testing::TestParamInfo<BejEncoderTest::ParamType>& info) {
return info.param.testName;
});
/**
* @brief Creates a Drive resource action payload
*
* {
* "ResetType": "On"
* }
*/
struct RedfishPropertyParent* createDriveAction()
{
static struct RedfishPropertyParent root;
static struct RedfishPropertyLeafEnum resetType;
bejTreeInitSet(&root, "#Drive.Reset");
bejTreeAddEnum(&root, &resetType, "ResetType", "On");
return &root;
}
constexpr std::array<uint8_t, 8> bejLocatorDriveReset{
{0x1, 0x6, 0x1, 0x0, 0x1, 0x0, 0x1, 0x4}};
const std::string driveActionJson = R"(
{
"ResetType": "On"
}
)";
TEST(BejEncoderDecoderTest, EncodeDecodeDriveAction)
{
auto inputsOrErr = loadInputs(driveOemTestFiles);
EXPECT_TRUE(inputsOrErr);
BejDictionaries dictionaries = {
.schemaDictionary = inputsOrErr->schemaDictionary,
.schemaDictionarySize = inputsOrErr->schemaDictionarySize,
.annotationDictionary = inputsOrErr->annotationDictionary,
.annotationDictionarySize = inputsOrErr->annotationDictionarySize,
.errorDictionary = inputsOrErr->errorDictionary,
.errorDictionarySize = inputsOrErr->errorDictionarySize,
};
std::vector<uint8_t> outputBuffer;
struct BejEncoderOutputHandler output = {
.handlerContext = &outputBuffer,
.recvOutput = &recvOutput,
};
std::vector<void*> pointerStack;
struct BejPointerStackCallback stackCallbacks = {
.stackContext = &pointerStack,
.stackEmpty = stackEmpty,
.stackPeek = stackPeek,
.stackPop = stackPop,
.stackPush = stackPush,
.deleteStack = NULL,
};
uint16_t property_offset;
EXPECT_THAT(bejDictEntryByBejLocator(
dictionaries.schemaDictionary, bejLocatorDriveReset.data(),
bejLocatorDriveReset.size(), nullptr, &property_offset),
0);
bejEncode(&dictionaries, property_offset, bejMajorSchemaClass,
createDriveAction(), &output, &stackCallbacks);
BejDecoderJson decoder;
EXPECT_THAT(
decoder.decode(dictionaries, std::span(outputBuffer), property_offset),
0);
std::string decoded = decoder.getOutput();
nlohmann::json jsonDecoded = nlohmann::json::parse(decoded);
nlohmann::json expectedJson = nlohmann::json::parse(driveActionJson);
EXPECT_TRUE(jsonDecoded.dump() == expectedJson.dump());
}
uint32_t gChassisResourceId = 0xABCDEF;
uint32_t gResourceUriContext = 1234;
const char* gResourceUri = "/redfish/v1/Chassis/SomeChassis";
struct redfishUriJson
{
struct RedfishPropertyParent set;
struct RedfishPropertyLeafUri odataId;
};
struct RedfishPropertyParent* createChassisCollection(uint32_t resourceId,
bool expand)
{
static struct RedfishPropertyParent root;
static struct RedfishPropertyLeafString odataId;
static struct RedfishPropertyLeafString odataType;
static struct RedfishPropertyLeafString name;
static struct RedfishArrayOfLinksJson membersArray;
static struct redfishUriJson chassisLink;
bejTreeInitSet(&root, NULL);
bejTreeAddString(&root, &odataId, "@odata.id", "/redfish/v1/Chassis");
bejTreeAddString(&root, &odataType, "@odata.type",
"#ChassisCollection.ChassisCollection");
bejTreeAddString(&root, &name, "Name", "Chassis Collection");
redfishCreateArrayOfLinksJson(&root, "Members", /*linkCount=*/0, NULL,
&membersArray, NULL);
bejTreeInitSet(&chassisLink.set, NULL);
bejTreeAddUri(&chassisLink.set, &chassisLink.odataId, "@odata.id",
resourceId, expand);
bejTreeLinkChildToParent(&membersArray.array, &chassisLink.set);
membersArray.count.value = 1;
return &root;
}
void bejGetUri(uint32_t resourceId, void* context, const char** uriStr)
{
if ((gChassisResourceId != resourceId) || (context != &gResourceUriContext))
{
*uriStr = NULL;
return;
}
*uriStr = gResourceUri;
}
class BejEncodeDecodeWithExpandTest : public ::testing::Test
{
protected:
BejEncodeDecodeWithExpandTest()
{
auto chassisColInputsOrErr =
loadInputs(chassisCollectionTestFiles);
EXPECT_TRUE(chassisColInputsOrErr);
expectedChassisColJson_ = chassisColInputsOrErr->expectedJson;
chassisColDictionaries_.schemaDictionary =
chassisColInputsOrErr->schemaDictionary;
chassisColDictionaries_.schemaDictionarySize =
chassisColInputsOrErr->schemaDictionarySize;
chassisColDictionaries_.annotationDictionary =
chassisColInputsOrErr->annotationDictionary;
chassisColDictionaries_.annotationDictionarySize =
chassisColInputsOrErr->annotationDictionarySize;
chassisColDictionaries_.errorDictionary =
chassisColInputsOrErr->errorDictionary;
chassisColDictionaries_.errorDictionarySize =
chassisColInputsOrErr->errorDictionarySize;
chassisColOutputHandler_.handlerContext = &chassisColOutputBuffer_;
chassisColOutputHandler_.recvOutput = &recvOutput;
stackCallbacks_.stackContext = &pointerStack_;
stackCallbacks_.stackEmpty = &stackEmpty;
stackCallbacks_.stackPeek = &stackPeek;
stackCallbacks_.stackPop = &stackPop;
stackCallbacks_.stackPush = &stackPush;
stackCallbacks_.deleteStack = NULL;
}
nlohmann::json expectedChassisColJson_;
BejDictionaries chassisColDictionaries_;
std::vector<uint8_t> chassisColOutputBuffer_;
struct BejEncoderOutputHandler chassisColOutputHandler_;
std::vector<void*> pointerStack_;
struct BejPointerStackCallback stackCallbacks_;
};
TEST_F(BejEncodeDecodeWithExpandTest, UriNoCallbackFail)
{
struct BejNodeInfoCallbacks nodeInfoCb = {};
EXPECT_THAT(
bejEncodeWithExpand(
&chassisColDictionaries_, BEJ_DICTIONARY_START_AT_HEAD,
bejMajorSchemaClass,
createChassisCollection(gChassisResourceId, /*expand=*/false),
&chassisColOutputHandler_, &stackCallbacks_, &nodeInfoCb),
bejErrorNullParameter);
}
TEST_F(BejEncodeDecodeWithExpandTest, UriInvalidResourceIdFail)
{
struct BejNodeInfoCallbacks nodeInfoCb = {};
nodeInfoCb.context = &gResourceUriContext;
nodeInfoCb.bejGetUri = &bejGetUri;
EXPECT_THAT(
bejEncodeWithExpand(
&chassisColDictionaries_, BEJ_DICTIONARY_START_AT_HEAD,
bejMajorSchemaClass,
createChassisCollection(gChassisResourceId + 1, /*expand=*/false),
&chassisColOutputHandler_, &stackCallbacks_, &nodeInfoCb),
bejErrorInvalidNodeInput);
}
TEST_F(BejEncodeDecodeWithExpandTest, UriEncodeDecodeSuccess)
{
struct BejNodeInfoCallbacks nodeInfoCb = {};
nodeInfoCb.context = &gResourceUriContext;
nodeInfoCb.bejGetUri = &bejGetUri;
EXPECT_THAT(
bejEncodeWithExpand(
&chassisColDictionaries_, BEJ_DICTIONARY_START_AT_HEAD,
bejMajorSchemaClass,
createChassisCollection(gChassisResourceId, /*expand=*/false),
&chassisColOutputHandler_, &stackCallbacks_, &nodeInfoCb),
0);
BejDecoderJson decoder;
EXPECT_THAT(decoder.decode(chassisColDictionaries_,
std::span(chassisColOutputBuffer_)),
0);
std::string decoded = decoder.getOutput();
nlohmann::json jsonDecoded = nlohmann::json::parse(decoded);
EXPECT_STREQ(jsonDecoded.dump().c_str(),
expectedChassisColJson_.dump().c_str());
}
constexpr int collectionMembers = 10;
struct RedfishPropertyParent* createChassisCollectionWithMultipleMembers()
{
static struct RedfishPropertyParent root;
static struct RedfishPropertyLeafString odataId;
static struct RedfishPropertyLeafString odataType;
static struct RedfishPropertyLeafString name;
static struct RedfishArrayOfLinksJson membersArray;
static struct redfishUriJson chassisLink[collectionMembers];
bejTreeInitSet(&root, NULL);
bejTreeAddString(&root, &odataId, "@odata.id", "/redfish/v1/Chassis");
bejTreeAddString(&root, &odataType, "@odata.type",
"#ChassisCollection.ChassisCollection");
bejTreeAddString(&root, &name, "Name", "Chassis Collection");
redfishCreateArrayOfLinksJson(&root, "Members", /*linkCount=*/0, NULL,
&membersArray, NULL);
int resourceId = 0;
for (int i = 0; i < collectionMembers; ++i)
{
bejTreeInitSet(&chassisLink[i].set, NULL);
bejTreeAddUri(&chassisLink[i].set, &chassisLink[i].odataId, "@odata.id",
resourceId, /*expand*/ true);
bejTreeLinkChildToParent(&membersArray.array, &chassisLink[i].set);
resourceId += 1;
}
membersArray.count.value = collectionMembers;
return &root;
}
int listPush(uint32_t resourceID, void* listContext)
{
auto stack = reinterpret_cast<std::vector<uint32_t>*>(listContext);
stack->push_back(resourceID);
return 0;
}
TEST(BejEncoderHelperTest, BejGetUriNodesToBeExpandedSuccess)
{
std::vector<uint32_t> list;
std::vector<void*> pointerStack;
struct BejPointerStackCallback stackCallbacks = {
.stackContext = &pointerStack,
.stackEmpty = stackEmpty,
.stackPeek = stackPeek,
.stackPop = stackPop,
.stackPush = stackPush,
.deleteStack = NULL,
};
EXPECT_THAT(
bejGetUriNodesToBeExpanded(createChassisCollectionWithMultipleMembers(),
&stackCallbacks, &list, listPush),
0);
int expResourceId = 0;
for (int i = 0; i < collectionMembers; ++i)
{
EXPECT_THAT(list[i], expResourceId);
expResourceId += 1;
}
}
} // namespace libbej