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