1 /** 2 * Copyright © 2019 IBM Corporation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 #include "extensions/openpower-pels/registry.hpp" 17 18 #include <filesystem> 19 #include <fstream> 20 #include <nlohmann/json.hpp> 21 22 #include <gtest/gtest.h> 23 24 using namespace openpower::pels::message; 25 namespace fs = std::filesystem; 26 27 const auto registryData = R"( 28 { 29 "PELs": 30 [ 31 { 32 "Name": "xyz.openbmc_project.Power.Fault", 33 "Subsystem": "power_supply", 34 35 "SRC": 36 { 37 "ReasonCode": "0x2030" 38 }, 39 40 "Documentation": 41 { 42 "Description": "A PGOOD Fault", 43 "Message": "PS had a PGOOD Fault" 44 } 45 }, 46 47 { 48 "Name": "xyz.openbmc_project.Power.OverVoltage", 49 "Subsystem": "power_control_hw", 50 "Severity": "unrecoverable", 51 "MfgSeverity": "non_error", 52 "ActionFlags": ["service_action", "report", "call_home"], 53 "MfgActionFlags": ["hidden"], 54 55 "SRC": 56 { 57 "ReasonCode": "0x2333", 58 "Type": "BD", 59 "SymptomIDFields": ["SRCWord5", "SRCWord6", "SRCWord7"], 60 "PowerFault": true, 61 "Words6To9": 62 { 63 "6": 64 { 65 "description": "Failing unit number", 66 "AdditionalDataPropSource": "PS_NUM" 67 }, 68 69 "7": 70 { 71 "description": "bad voltage", 72 "AdditionalDataPropSource": "VOLTAGE" 73 } 74 } 75 }, 76 77 "Documentation": 78 { 79 "Description": "A PGOOD Fault", 80 "Message": "PS %1 had a PGOOD Fault", 81 "MessageArgSources": 82 [ 83 "SRCWord6" 84 ], 85 "Notes": [ 86 "In the UserData section there is a JSON", 87 "dump that provides debug information." 88 ] 89 } 90 } 91 ] 92 } 93 )"; 94 95 class RegistryTest : public ::testing::Test 96 { 97 protected: 98 static void SetUpTestCase() 99 { 100 char path[] = "/tmp/regtestXXXXXX"; 101 regDir = mkdtemp(path); 102 } 103 104 static void TearDownTestCase() 105 { 106 fs::remove_all(regDir); 107 } 108 109 static std::string writeData(const char* data) 110 { 111 fs::path path = regDir / "registry.json"; 112 std::ofstream stream{path}; 113 stream << data; 114 return path; 115 } 116 117 static fs::path regDir; 118 }; 119 120 fs::path RegistryTest::regDir{}; 121 122 TEST_F(RegistryTest, TestNoEntry) 123 { 124 auto path = RegistryTest::writeData(registryData); 125 Registry registry{path}; 126 127 auto entry = registry.lookup("foo", LookupType::name); 128 EXPECT_FALSE(entry); 129 } 130 131 TEST_F(RegistryTest, TestFindEntry) 132 { 133 auto path = RegistryTest::writeData(registryData); 134 Registry registry{path}; 135 136 auto entry = registry.lookup("xyz.openbmc_project.Power.OverVoltage", 137 LookupType::name); 138 ASSERT_TRUE(entry); 139 EXPECT_EQ(entry->name, "xyz.openbmc_project.Power.OverVoltage"); 140 EXPECT_EQ(entry->subsystem, 0x62); 141 EXPECT_EQ(*(entry->severity), 0x40); 142 EXPECT_EQ(*(entry->mfgSeverity), 0x00); 143 EXPECT_EQ(*(entry->actionFlags), 0xA800); 144 EXPECT_EQ(*(entry->mfgActionFlags), 0x4000); 145 EXPECT_EQ(entry->componentID, 0x2300); 146 EXPECT_FALSE(entry->eventType); 147 EXPECT_FALSE(entry->eventScope); 148 149 EXPECT_EQ(entry->src.type, 0xBD); 150 EXPECT_EQ(entry->src.reasonCode, 0x2333); 151 EXPECT_EQ(*(entry->src.powerFault), true); 152 153 auto& hexwords = entry->src.hexwordADFields; 154 EXPECT_TRUE(hexwords); 155 EXPECT_EQ((*hexwords).size(), 2); 156 157 auto word = (*hexwords).find(6); 158 EXPECT_NE(word, (*hexwords).end()); 159 EXPECT_EQ(word->second, "PS_NUM"); 160 161 word = (*hexwords).find(7); 162 EXPECT_NE(word, (*hexwords).end()); 163 EXPECT_EQ(word->second, "VOLTAGE"); 164 165 auto& sid = entry->src.symptomID; 166 EXPECT_TRUE(sid); 167 EXPECT_EQ((*sid).size(), 3); 168 EXPECT_NE(std::find((*sid).begin(), (*sid).end(), 5), (*sid).end()); 169 EXPECT_NE(std::find((*sid).begin(), (*sid).end(), 6), (*sid).end()); 170 EXPECT_NE(std::find((*sid).begin(), (*sid).end(), 7), (*sid).end()); 171 172 EXPECT_EQ(entry->doc.description, "A PGOOD Fault"); 173 EXPECT_EQ(entry->doc.message, "PS %1 had a PGOOD Fault"); 174 auto& hexwordSource = entry->doc.messageArgSources; 175 EXPECT_TRUE(hexwordSource); 176 EXPECT_EQ((*hexwordSource).size(), 1); 177 EXPECT_EQ((*hexwordSource).front(), "SRCWord6"); 178 179 entry = registry.lookup("0x2333", LookupType::reasonCode); 180 ASSERT_TRUE(entry); 181 EXPECT_EQ(entry->name, "xyz.openbmc_project.Power.OverVoltage"); 182 } 183 184 // Check the entry that mostly uses defaults 185 TEST_F(RegistryTest, TestFindEntryMinimal) 186 { 187 auto path = RegistryTest::writeData(registryData); 188 Registry registry{path}; 189 190 auto entry = 191 registry.lookup("xyz.openbmc_project.Power.Fault", LookupType::name); 192 ASSERT_TRUE(entry); 193 EXPECT_EQ(entry->name, "xyz.openbmc_project.Power.Fault"); 194 EXPECT_EQ(entry->subsystem, 0x61); 195 EXPECT_FALSE(entry->severity); 196 EXPECT_FALSE(entry->mfgSeverity); 197 EXPECT_FALSE(entry->mfgActionFlags); 198 EXPECT_FALSE(entry->actionFlags); 199 EXPECT_EQ(entry->componentID, 0x2000); 200 EXPECT_FALSE(entry->eventType); 201 EXPECT_FALSE(entry->eventScope); 202 203 EXPECT_EQ(entry->src.reasonCode, 0x2030); 204 EXPECT_EQ(entry->src.type, 0xBD); 205 EXPECT_FALSE(entry->src.powerFault); 206 EXPECT_FALSE(entry->src.hexwordADFields); 207 EXPECT_FALSE(entry->src.symptomID); 208 } 209 210 TEST_F(RegistryTest, TestBadJSON) 211 { 212 auto path = RegistryTest::writeData("bad {} json"); 213 214 Registry registry{path}; 215 216 EXPECT_FALSE(registry.lookup("foo", LookupType::name)); 217 } 218 219 // Test the helper functions the use the pel_values data. 220 TEST_F(RegistryTest, TestHelperFunctions) 221 { 222 using namespace openpower::pels::message::helper; 223 EXPECT_EQ(getSubsystem("input_power_source"), 0xA1); 224 EXPECT_THROW(getSubsystem("foo"), std::runtime_error); 225 226 EXPECT_EQ(getSeverity("symptom_recovered"), 0x71); 227 EXPECT_THROW(getSeverity("foo"), std::runtime_error); 228 229 EXPECT_EQ(getEventType("dump_notification"), 0x08); 230 EXPECT_THROW(getEventType("foo"), std::runtime_error); 231 232 EXPECT_EQ(getEventScope("possibly_multiple_platforms"), 0x04); 233 EXPECT_THROW(getEventScope("foo"), std::runtime_error); 234 235 std::vector<std::string> flags{"service_action", "dont_report", 236 "termination"}; 237 EXPECT_EQ(getActionFlags(flags), 0x9100); 238 239 flags.clear(); 240 flags.push_back("foo"); 241 EXPECT_THROW(getActionFlags(flags), std::runtime_error); 242 } 243 244 TEST_F(RegistryTest, TestGetSRCReasonCode) 245 { 246 using namespace openpower::pels::message::helper; 247 EXPECT_EQ(getSRCReasonCode(R"({"ReasonCode": "0x5555"})"_json, "foo"), 248 0x5555); 249 250 EXPECT_THROW(getSRCReasonCode(R"({"ReasonCode": "ZZZZ"})"_json, "foo"), 251 std::runtime_error); 252 } 253 254 TEST_F(RegistryTest, TestGetSRCType) 255 { 256 using namespace openpower::pels::message::helper; 257 EXPECT_EQ(getSRCType(R"({"Type": "11"})"_json, "foo"), 0x11); 258 EXPECT_EQ(getSRCType(R"({"Type": "BF"})"_json, "foo"), 0xBF); 259 260 EXPECT_THROW(getSRCType(R"({"Type": "1"})"_json, "foo"), 261 std::runtime_error); 262 263 EXPECT_THROW(getSRCType(R"({"Type": "111"})"_json, "foo"), 264 std::runtime_error); 265 } 266 267 TEST_F(RegistryTest, TestGetSRCHexwordFields) 268 { 269 using namespace openpower::pels::message::helper; 270 const auto hexwords = R"( 271 {"Words6To9": 272 { 273 "8": 274 { 275 "AdditionalDataPropSource": "TEST" 276 } 277 } 278 })"_json; 279 280 auto fields = getSRCHexwordFields(hexwords, "foo"); 281 EXPECT_TRUE(fields); 282 auto word = fields->find(8); 283 EXPECT_NE(word, fields->end()); 284 285 const auto theInvalidRWord = R"( 286 {"Words6To9": 287 { 288 "R": 289 { 290 "AdditionalDataPropSource": "TEST" 291 } 292 } 293 })"_json; 294 295 EXPECT_THROW(getSRCHexwordFields(theInvalidRWord, "foo"), 296 std::runtime_error); 297 } 298 299 TEST_F(RegistryTest, TestGetSRCSymptomIDFields) 300 { 301 using namespace openpower::pels::message::helper; 302 const auto sID = R"( 303 { 304 "SymptomIDFields": ["SRCWord3", "SRCWord4", "SRCWord5"] 305 })"_json; 306 307 auto fields = getSRCSymptomIDFields(sID, "foo"); 308 EXPECT_NE(std::find(fields->begin(), fields->end(), 3), fields->end()); 309 EXPECT_NE(std::find(fields->begin(), fields->end(), 4), fields->end()); 310 EXPECT_NE(std::find(fields->begin(), fields->end(), 5), fields->end()); 311 312 const auto badField = R"( 313 { 314 "SymptomIDFields": ["SRCWord3", "SRCWord4", "SRCWord"] 315 })"_json; 316 317 EXPECT_THROW(getSRCSymptomIDFields(badField, "foo"), std::runtime_error); 318 } 319 320 TEST_F(RegistryTest, TestGetComponentID) 321 { 322 using namespace openpower::pels::message::helper; 323 324 // Get it from the JSON 325 auto id = 326 getComponentID(0xBD, 0x4200, R"({"ComponentID":"0x4200"})"_json, "foo"); 327 EXPECT_EQ(id, 0x4200); 328 329 // Get it from the reason code on a 0xBD SRC 330 id = getComponentID(0xBD, 0x6700, R"({})"_json, "foo"); 331 EXPECT_EQ(id, 0x6700); 332 333 // Not present on a 0x11 SRC 334 EXPECT_THROW(getComponentID(0x11, 0x8800, R"({})"_json, "foo"), 335 std::runtime_error); 336 } 337