| /** | 
 |  * Copyright © 2019 IBM Corporation | 
 |  * | 
 |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
 |  * you may not use this file except in compliance with the License. | 
 |  * You may obtain a copy of the License at | 
 |  * | 
 |  *     http://www.apache.org/licenses/LICENSE-2.0 | 
 |  * | 
 |  * Unless required by applicable law or agreed to in writing, software | 
 |  * distributed under the License is distributed on an "AS IS" BASIS, | 
 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 |  * See the License for the specific language governing permissions and | 
 |  * limitations under the License. | 
 |  */ | 
 | #include "extensions/openpower-pels/registry.hpp" | 
 |  | 
 | #include <filesystem> | 
 | #include <fstream> | 
 | #include <nlohmann/json.hpp> | 
 |  | 
 | #include <gtest/gtest.h> | 
 |  | 
 | using namespace openpower::pels::message; | 
 | namespace fs = std::filesystem; | 
 |  | 
 | const auto registryData = R"( | 
 | { | 
 |     "PELs": | 
 |     [ | 
 |         { | 
 |             "Name": "xyz.openbmc_project.Power.Fault", | 
 |             "Subsystem": "power_supply", | 
 |  | 
 |             "SRC": | 
 |             { | 
 |                 "ReasonCode": "0x2030" | 
 |             }, | 
 |  | 
 |             "Documentation": | 
 |             { | 
 |                 "Description": "A PGOOD Fault", | 
 |                 "Message": "PS had a PGOOD Fault" | 
 |             } | 
 |         }, | 
 |  | 
 |         { | 
 |             "Name": "xyz.openbmc_project.Power.OverVoltage", | 
 |             "Subsystem": "power_control_hw", | 
 |             "Severity": "unrecoverable", | 
 |             "MfgSeverity": "non_error", | 
 |             "ActionFlags": ["service_action", "report", "call_home"], | 
 |             "MfgActionFlags": ["hidden"], | 
 |  | 
 |             "SRC": | 
 |             { | 
 |                 "ReasonCode": "0x2333", | 
 |                 "Type": "BD", | 
 |                 "SymptomIDFields": ["SRCWord5", "SRCWord6", "SRCWord7"], | 
 |                 "PowerFault": true, | 
 |                 "Words6To9": | 
 |                 { | 
 |                     "6": | 
 |                     { | 
 |                         "description": "Failing unit number", | 
 |                         "AdditionalDataPropSource": "PS_NUM" | 
 |                     }, | 
 |  | 
 |                     "7": | 
 |                     { | 
 |                         "description": "bad voltage", | 
 |                         "AdditionalDataPropSource": "VOLTAGE" | 
 |                     } | 
 |                 } | 
 |             }, | 
 |  | 
 |             "Documentation": | 
 |             { | 
 |                 "Description": "A PGOOD Fault", | 
 |                 "Message": "PS %1 had a PGOOD Fault", | 
 |                 "MessageArgSources": | 
 |                 [ | 
 |                     "SRCWord6" | 
 |                 ], | 
 |                 "Notes": [ | 
 |                     "In the UserData section there is a JSON", | 
 |                     "dump that provides debug information." | 
 |                 ] | 
 |             } | 
 |         } | 
 |     ] | 
 | } | 
 | )"; | 
 |  | 
 | class RegistryTest : public ::testing::Test | 
 | { | 
 |   protected: | 
 |     static void SetUpTestCase() | 
 |     { | 
 |         char path[] = "/tmp/regtestXXXXXX"; | 
 |         regDir = mkdtemp(path); | 
 |     } | 
 |  | 
 |     static void TearDownTestCase() | 
 |     { | 
 |         fs::remove_all(regDir); | 
 |     } | 
 |  | 
 |     static std::string writeData(const char* data) | 
 |     { | 
 |         fs::path path = regDir / "registry.json"; | 
 |         std::ofstream stream{path}; | 
 |         stream << data; | 
 |         return path; | 
 |     } | 
 |  | 
 |     static fs::path regDir; | 
 | }; | 
 |  | 
 | fs::path RegistryTest::regDir{}; | 
 |  | 
 | TEST_F(RegistryTest, TestNoEntry) | 
 | { | 
 |     auto path = RegistryTest::writeData(registryData); | 
 |     Registry registry{path}; | 
 |  | 
 |     auto entry = registry.lookup("foo", LookupType::name); | 
 |     EXPECT_FALSE(entry); | 
 | } | 
 |  | 
 | TEST_F(RegistryTest, TestFindEntry) | 
 | { | 
 |     auto path = RegistryTest::writeData(registryData); | 
 |     Registry registry{path}; | 
 |  | 
 |     auto entry = registry.lookup("xyz.openbmc_project.Power.OverVoltage", | 
 |                                  LookupType::name); | 
 |     ASSERT_TRUE(entry); | 
 |     EXPECT_EQ(entry->name, "xyz.openbmc_project.Power.OverVoltage"); | 
 |     EXPECT_EQ(entry->subsystem, 0x62); | 
 |     EXPECT_EQ(*(entry->severity), 0x40); | 
 |     EXPECT_EQ(*(entry->mfgSeverity), 0x00); | 
 |     EXPECT_EQ(*(entry->actionFlags), 0xA800); | 
 |     EXPECT_EQ(*(entry->mfgActionFlags), 0x4000); | 
 |     EXPECT_EQ(entry->componentID, 0x2300); | 
 |     EXPECT_FALSE(entry->eventType); | 
 |     EXPECT_FALSE(entry->eventScope); | 
 |  | 
 |     EXPECT_EQ(entry->src.type, 0xBD); | 
 |     EXPECT_EQ(entry->src.reasonCode, 0x2333); | 
 |     EXPECT_EQ(*(entry->src.powerFault), true); | 
 |  | 
 |     auto& hexwords = entry->src.hexwordADFields; | 
 |     EXPECT_TRUE(hexwords); | 
 |     EXPECT_EQ((*hexwords).size(), 2); | 
 |  | 
 |     auto word = (*hexwords).find(6); | 
 |     EXPECT_NE(word, (*hexwords).end()); | 
 |     EXPECT_EQ(word->second, "PS_NUM"); | 
 |  | 
 |     word = (*hexwords).find(7); | 
 |     EXPECT_NE(word, (*hexwords).end()); | 
 |     EXPECT_EQ(word->second, "VOLTAGE"); | 
 |  | 
 |     auto& sid = entry->src.symptomID; | 
 |     EXPECT_TRUE(sid); | 
 |     EXPECT_EQ((*sid).size(), 3); | 
 |     EXPECT_NE(std::find((*sid).begin(), (*sid).end(), 5), (*sid).end()); | 
 |     EXPECT_NE(std::find((*sid).begin(), (*sid).end(), 6), (*sid).end()); | 
 |     EXPECT_NE(std::find((*sid).begin(), (*sid).end(), 7), (*sid).end()); | 
 |  | 
 |     EXPECT_EQ(entry->doc.description, "A PGOOD Fault"); | 
 |     EXPECT_EQ(entry->doc.message, "PS %1 had a PGOOD Fault"); | 
 |     auto& hexwordSource = entry->doc.messageArgSources; | 
 |     EXPECT_TRUE(hexwordSource); | 
 |     EXPECT_EQ((*hexwordSource).size(), 1); | 
 |     EXPECT_EQ((*hexwordSource).front(), "SRCWord6"); | 
 |  | 
 |     entry = registry.lookup("0x2333", LookupType::reasonCode); | 
 |     ASSERT_TRUE(entry); | 
 |     EXPECT_EQ(entry->name, "xyz.openbmc_project.Power.OverVoltage"); | 
 | } | 
 |  | 
 | // Check the entry that mostly uses defaults | 
 | TEST_F(RegistryTest, TestFindEntryMinimal) | 
 | { | 
 |     auto path = RegistryTest::writeData(registryData); | 
 |     Registry registry{path}; | 
 |  | 
 |     auto entry = | 
 |         registry.lookup("xyz.openbmc_project.Power.Fault", LookupType::name); | 
 |     ASSERT_TRUE(entry); | 
 |     EXPECT_EQ(entry->name, "xyz.openbmc_project.Power.Fault"); | 
 |     EXPECT_EQ(entry->subsystem, 0x61); | 
 |     EXPECT_FALSE(entry->severity); | 
 |     EXPECT_FALSE(entry->mfgSeverity); | 
 |     EXPECT_FALSE(entry->mfgActionFlags); | 
 |     EXPECT_FALSE(entry->actionFlags); | 
 |     EXPECT_EQ(entry->componentID, 0x2000); | 
 |     EXPECT_FALSE(entry->eventType); | 
 |     EXPECT_FALSE(entry->eventScope); | 
 |  | 
 |     EXPECT_EQ(entry->src.reasonCode, 0x2030); | 
 |     EXPECT_EQ(entry->src.type, 0xBD); | 
 |     EXPECT_FALSE(entry->src.powerFault); | 
 |     EXPECT_FALSE(entry->src.hexwordADFields); | 
 |     EXPECT_FALSE(entry->src.symptomID); | 
 | } | 
 |  | 
 | TEST_F(RegistryTest, TestBadJSON) | 
 | { | 
 |     auto path = RegistryTest::writeData("bad {} json"); | 
 |  | 
 |     Registry registry{path}; | 
 |  | 
 |     EXPECT_FALSE(registry.lookup("foo", LookupType::name)); | 
 | } | 
 |  | 
 | // Test the helper functions the use the pel_values data. | 
 | TEST_F(RegistryTest, TestHelperFunctions) | 
 | { | 
 |     using namespace openpower::pels::message::helper; | 
 |     EXPECT_EQ(getSubsystem("input_power_source"), 0xA1); | 
 |     EXPECT_THROW(getSubsystem("foo"), std::runtime_error); | 
 |  | 
 |     EXPECT_EQ(getSeverity("symptom_recovered"), 0x71); | 
 |     EXPECT_THROW(getSeverity("foo"), std::runtime_error); | 
 |  | 
 |     EXPECT_EQ(getEventType("dump_notification"), 0x08); | 
 |     EXPECT_THROW(getEventType("foo"), std::runtime_error); | 
 |  | 
 |     EXPECT_EQ(getEventScope("possibly_multiple_platforms"), 0x04); | 
 |     EXPECT_THROW(getEventScope("foo"), std::runtime_error); | 
 |  | 
 |     std::vector<std::string> flags{"service_action", "dont_report", | 
 |                                    "termination"}; | 
 |     EXPECT_EQ(getActionFlags(flags), 0x9100); | 
 |  | 
 |     flags.clear(); | 
 |     flags.push_back("foo"); | 
 |     EXPECT_THROW(getActionFlags(flags), std::runtime_error); | 
 | } | 
 |  | 
 | TEST_F(RegistryTest, TestGetSRCReasonCode) | 
 | { | 
 |     using namespace openpower::pels::message::helper; | 
 |     EXPECT_EQ(getSRCReasonCode(R"({"ReasonCode": "0x5555"})"_json, "foo"), | 
 |               0x5555); | 
 |  | 
 |     EXPECT_THROW(getSRCReasonCode(R"({"ReasonCode": "ZZZZ"})"_json, "foo"), | 
 |                  std::runtime_error); | 
 | } | 
 |  | 
 | TEST_F(RegistryTest, TestGetSRCType) | 
 | { | 
 |     using namespace openpower::pels::message::helper; | 
 |     EXPECT_EQ(getSRCType(R"({"Type": "11"})"_json, "foo"), 0x11); | 
 |     EXPECT_EQ(getSRCType(R"({"Type": "BF"})"_json, "foo"), 0xBF); | 
 |  | 
 |     EXPECT_THROW(getSRCType(R"({"Type": "1"})"_json, "foo"), | 
 |                  std::runtime_error); | 
 |  | 
 |     EXPECT_THROW(getSRCType(R"({"Type": "111"})"_json, "foo"), | 
 |                  std::runtime_error); | 
 | } | 
 |  | 
 | TEST_F(RegistryTest, TestGetSRCHexwordFields) | 
 | { | 
 |     using namespace openpower::pels::message::helper; | 
 |     const auto hexwords = R"( | 
 |     {"Words6To9": | 
 |       { | 
 |         "8": | 
 |         { | 
 |             "AdditionalDataPropSource": "TEST" | 
 |         } | 
 |       } | 
 |     })"_json; | 
 |  | 
 |     auto fields = getSRCHexwordFields(hexwords, "foo"); | 
 |     EXPECT_TRUE(fields); | 
 |     auto word = fields->find(8); | 
 |     EXPECT_NE(word, fields->end()); | 
 |  | 
 |     const auto theInvalidRWord = R"( | 
 |     {"Words6To9": | 
 |       { | 
 |         "R": | 
 |         { | 
 |             "AdditionalDataPropSource": "TEST" | 
 |         } | 
 |       } | 
 |     })"_json; | 
 |  | 
 |     EXPECT_THROW(getSRCHexwordFields(theInvalidRWord, "foo"), | 
 |                  std::runtime_error); | 
 | } | 
 |  | 
 | TEST_F(RegistryTest, TestGetSRCSymptomIDFields) | 
 | { | 
 |     using namespace openpower::pels::message::helper; | 
 |     const auto sID = R"( | 
 |     { | 
 |         "SymptomIDFields": ["SRCWord3", "SRCWord4", "SRCWord5"] | 
 |     })"_json; | 
 |  | 
 |     auto fields = getSRCSymptomIDFields(sID, "foo"); | 
 |     EXPECT_NE(std::find(fields->begin(), fields->end(), 3), fields->end()); | 
 |     EXPECT_NE(std::find(fields->begin(), fields->end(), 4), fields->end()); | 
 |     EXPECT_NE(std::find(fields->begin(), fields->end(), 5), fields->end()); | 
 |  | 
 |     const auto badField = R"( | 
 |     { | 
 |         "SymptomIDFields": ["SRCWord3", "SRCWord4", "SRCWord"] | 
 |     })"_json; | 
 |  | 
 |     EXPECT_THROW(getSRCSymptomIDFields(badField, "foo"), std::runtime_error); | 
 | } | 
 |  | 
 | TEST_F(RegistryTest, TestGetComponentID) | 
 | { | 
 |     using namespace openpower::pels::message::helper; | 
 |  | 
 |     // Get it from the JSON | 
 |     auto id = | 
 |         getComponentID(0xBD, 0x4200, R"({"ComponentID":"0x4200"})"_json, "foo"); | 
 |     EXPECT_EQ(id, 0x4200); | 
 |  | 
 |     // Get it from the reason code on a 0xBD SRC | 
 |     id = getComponentID(0xBD, 0x6700, R"({})"_json, "foo"); | 
 |     EXPECT_EQ(id, 0x6700); | 
 |  | 
 |     // Not present on a 0x11 SRC | 
 |     EXPECT_THROW(getComponentID(0x11, 0x8800, R"({})"_json, "foo"), | 
 |                  std::runtime_error); | 
 | } |