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 <nlohmann/json.hpp>
19 
20 #include <filesystem>
21 #include <fstream>
22 
23 #include <gtest/gtest.h>
24 
25 using namespace openpower::pels::message;
26 using namespace openpower::pels;
27 namespace fs = std::filesystem;
28 
29 const auto registryData = R"(
30 {
31     "PELs":
32     [
33         {
34             "Name": "xyz.openbmc_project.Power.Fault",
35             "Subsystem": "power_supply",
36 
37             "SRC":
38             {
39                 "ReasonCode": "0x2030"
40             },
41 
42             "Documentation":
43             {
44                 "Description": "A PGOOD Fault",
45                 "Message": "PS had a PGOOD Fault"
46             }
47         },
48 
49         {
50             "Name": "xyz.openbmc_project.Power.OverVoltage",
51             "Subsystem": "power_control_hw",
52             "Severity":
53             [
54                 {
55                     "System": "systemA",
56                     "SevValue": "unrecoverable"
57                 },
58                 {
59                     "System": "systemB",
60                     "SevValue": "recovered"
61                 },
62                 {
63                     "SevValue": "predictive"
64                 }
65             ],
66             "MfgSeverity": "non_error",
67             "ActionFlags": ["service_action", "report", "call_home"],
68             "MfgActionFlags": ["hidden"],
69 
70             "SRC":
71             {
72                 "ReasonCode": "0x2333",
73                 "Type": "BD",
74                 "SymptomIDFields": ["SRCWord5", "SRCWord6", "SRCWord7"],
75                 "Words6To9":
76                 {
77                     "6":
78                     {
79                         "Description": "Failing unit number",
80                         "AdditionalDataPropSource": "PS_NUM"
81                     },
82 
83                     "7":
84                     {
85                         "Description": "bad voltage",
86                         "AdditionalDataPropSource": "VOLTAGE"
87                     }
88                 },
89                 "DeconfigFlag": true,
90                 "CheckstopFlag": true
91             },
92 
93             "Documentation":
94             {
95                 "Description": "A PGOOD Fault",
96                 "Message": "PS %1 had a PGOOD Fault",
97                 "MessageArgSources":
98                 [
99                     "SRCWord6"
100                 ],
101                 "Notes": [
102                     "In the UserData section there is a JSON",
103                     "dump that provides debug information."
104                 ]
105             },
106 
107             "JournalCapture":
108             {
109                 "NumLines": 7
110             }
111         },
112 
113         {
114             "Name": "xyz.openbmc_project.Common.Error.Timeout",
115             "PossibleSubsystems": ["processor", "memory"],
116 
117             "SRC":
118             {
119                 "ReasonCode": "0x2030"
120             },
121             "Documentation":
122             {
123                 "Description": "A PGOOD Fault",
124                 "Message": "PS had a PGOOD Fault"
125             }
126         },
127 
128         {
129             "Name": "xyz.openbmc_project.Journal.Capture",
130             "Subsystem": "power_supply",
131 
132             "SRC":
133             {
134                 "ReasonCode": "0x2030"
135             },
136 
137             "Documentation":
138             {
139                 "Description": "journal capture test",
140                 "Message": "journal capture test"
141             },
142 
143             "JournalCapture":
144             {
145                 "Sections": [
146                     {
147                         "NumLines": 5,
148                         "SyslogID": "test1"
149                     },
150                     {
151                         "NumLines": 6,
152                         "SyslogID": "test2"
153                     }
154                 ]
155             }
156         }
157     ]
158 }
159 )";
160 
161 class RegistryTest : public ::testing::Test
162 {
163   protected:
164     static void SetUpTestCase()
165     {
166         char path[] = "/tmp/regtestXXXXXX";
167         regDir = mkdtemp(path);
168     }
169 
170     static void TearDownTestCase()
171     {
172         fs::remove_all(regDir);
173     }
174 
175     static std::string writeData(const char* data)
176     {
177         fs::path path = regDir / "registry.json";
178         std::ofstream stream{path};
179         stream << data;
180         return path;
181     }
182 
183     static fs::path regDir;
184 };
185 
186 fs::path RegistryTest::regDir{};
187 
188 TEST_F(RegistryTest, TestNoEntry)
189 {
190     auto path = RegistryTest::writeData(registryData);
191     Registry registry{path};
192 
193     auto entry = registry.lookup("foo", LookupType::name);
194     EXPECT_FALSE(entry);
195 }
196 
197 TEST_F(RegistryTest, TestFindEntry)
198 {
199     auto path = RegistryTest::writeData(registryData);
200     Registry registry{path};
201 
202     auto entry = registry.lookup("xyz.openbmc_project.Power.OverVoltage",
203                                  LookupType::name);
204     ASSERT_TRUE(entry);
205     EXPECT_EQ(entry->name, "xyz.openbmc_project.Power.OverVoltage");
206     EXPECT_EQ(entry->subsystem, 0x62);
207 
208     ASSERT_EQ(entry->severity->size(), 3);
209     EXPECT_EQ((*entry->severity)[0].severity, 0x40);
210     EXPECT_EQ((*entry->severity)[0].system, "systemA");
211     EXPECT_EQ((*entry->severity)[1].severity, 0x10);
212     EXPECT_EQ((*entry->severity)[1].system, "systemB");
213     EXPECT_EQ((*entry->severity)[2].severity, 0x20);
214     EXPECT_EQ((*entry->severity)[2].system, "");
215 
216     EXPECT_EQ(entry->mfgSeverity->size(), 1);
217     EXPECT_EQ((*entry->mfgSeverity)[0].severity, 0x00);
218 
219     EXPECT_EQ(*(entry->actionFlags), 0xA800);
220     EXPECT_EQ(*(entry->mfgActionFlags), 0x4000);
221     EXPECT_EQ(entry->componentID, 0x2300);
222     EXPECT_FALSE(entry->eventType);
223     EXPECT_FALSE(entry->eventScope);
224 
225     EXPECT_EQ(entry->src.type, 0xBD);
226     EXPECT_EQ(entry->src.reasonCode, 0x2333);
227     EXPECT_TRUE(entry->src.deconfigFlag);
228     EXPECT_TRUE(entry->src.checkstopFlag);
229 
230     auto& hexwords = entry->src.hexwordADFields;
231     EXPECT_TRUE(hexwords);
232     EXPECT_EQ((*hexwords).size(), 2);
233 
234     auto word = (*hexwords).find(6);
235     EXPECT_NE(word, (*hexwords).end());
236     EXPECT_EQ(std::get<0>(word->second), "PS_NUM");
237 
238     word = (*hexwords).find(7);
239     EXPECT_NE(word, (*hexwords).end());
240     EXPECT_EQ(std::get<0>(word->second), "VOLTAGE");
241 
242     auto& sid = entry->src.symptomID;
243     EXPECT_TRUE(sid);
244     EXPECT_EQ((*sid).size(), 3);
245     EXPECT_NE(std::find((*sid).begin(), (*sid).end(), 5), (*sid).end());
246     EXPECT_NE(std::find((*sid).begin(), (*sid).end(), 6), (*sid).end());
247     EXPECT_NE(std::find((*sid).begin(), (*sid).end(), 7), (*sid).end());
248 
249     EXPECT_EQ(entry->doc.description, "A PGOOD Fault");
250     EXPECT_EQ(entry->doc.message, "PS %1 had a PGOOD Fault");
251     auto& hexwordSource = entry->doc.messageArgSources;
252     EXPECT_TRUE(hexwordSource);
253     EXPECT_EQ((*hexwordSource).size(), 1);
254     EXPECT_EQ((*hexwordSource).front(), "SRCWord6");
255 
256     const auto& jc = entry->journalCapture;
257     ASSERT_TRUE(jc);
258     ASSERT_TRUE(std::holds_alternative<size_t>(*jc));
259     EXPECT_EQ(std::get<size_t>(*jc), 7);
260 
261     entry = registry.lookup("0x2333", LookupType::reasonCode);
262     ASSERT_TRUE(entry);
263     EXPECT_EQ(entry->name, "xyz.openbmc_project.Power.OverVoltage");
264 }
265 
266 // Check the entry that mostly uses defaults
267 TEST_F(RegistryTest, TestFindEntryMinimal)
268 {
269     auto path = RegistryTest::writeData(registryData);
270     Registry registry{path};
271 
272     auto entry = registry.lookup("xyz.openbmc_project.Power.Fault",
273                                  LookupType::name);
274     ASSERT_TRUE(entry);
275     EXPECT_EQ(entry->name, "xyz.openbmc_project.Power.Fault");
276     EXPECT_EQ(entry->subsystem, 0x61);
277     EXPECT_FALSE(entry->severity);
278     EXPECT_FALSE(entry->mfgSeverity);
279     EXPECT_FALSE(entry->mfgActionFlags);
280     EXPECT_FALSE(entry->actionFlags);
281     EXPECT_EQ(entry->componentID, 0x2000);
282     EXPECT_FALSE(entry->eventType);
283     EXPECT_FALSE(entry->eventScope);
284 
285     EXPECT_EQ(entry->src.reasonCode, 0x2030);
286     EXPECT_EQ(entry->src.type, 0xBD);
287     EXPECT_FALSE(entry->src.hexwordADFields);
288     EXPECT_FALSE(entry->src.symptomID);
289     EXPECT_FALSE(entry->src.deconfigFlag);
290     EXPECT_FALSE(entry->src.checkstopFlag);
291 }
292 
293 TEST_F(RegistryTest, TestBadJSON)
294 {
295     auto path = RegistryTest::writeData("bad {} json");
296 
297     Registry registry{path};
298 
299     EXPECT_FALSE(registry.lookup("foo", LookupType::name));
300 }
301 
302 // Test the helper functions the use the pel_values data.
303 TEST_F(RegistryTest, TestHelperFunctions)
304 {
305     using namespace openpower::pels::message::helper;
306     EXPECT_EQ(getSubsystem("input_power_source"), 0xA1);
307     EXPECT_THROW(getSubsystem("foo"), std::runtime_error);
308 
309     EXPECT_EQ(getSeverity("symptom_recovered"), 0x71);
310     EXPECT_THROW(getSeverity("foo"), std::runtime_error);
311 
312     EXPECT_EQ(getEventType("dump_notification"), 0x08);
313     EXPECT_THROW(getEventType("foo"), std::runtime_error);
314 
315     EXPECT_EQ(getEventScope("possibly_multiple_platforms"), 0x04);
316     EXPECT_THROW(getEventScope("foo"), std::runtime_error);
317 
318     std::vector<std::string> flags{"service_action", "dont_report",
319                                    "termination"};
320     EXPECT_EQ(getActionFlags(flags), 0x9100);
321 
322     flags.clear();
323     flags.push_back("foo");
324     EXPECT_THROW(getActionFlags(flags), std::runtime_error);
325 }
326 
327 TEST_F(RegistryTest, TestGetSRCReasonCode)
328 {
329     using namespace openpower::pels::message::helper;
330     EXPECT_EQ(getSRCReasonCode(R"({"ReasonCode": "0x5555"})"_json, "foo"),
331               0x5555);
332 
333     EXPECT_THROW(getSRCReasonCode(R"({"ReasonCode": "ZZZZ"})"_json, "foo"),
334                  std::runtime_error);
335 }
336 
337 TEST_F(RegistryTest, TestGetSRCType)
338 {
339     using namespace openpower::pels::message::helper;
340     EXPECT_EQ(getSRCType(R"({"Type": "11"})"_json, "foo"), 0x11);
341     EXPECT_EQ(getSRCType(R"({"Type": "BF"})"_json, "foo"), 0xBF);
342 
343     EXPECT_THROW(getSRCType(R"({"Type": "1"})"_json, "foo"),
344                  std::runtime_error);
345 
346     EXPECT_THROW(getSRCType(R"({"Type": "111"})"_json, "foo"),
347                  std::runtime_error);
348 }
349 
350 TEST_F(RegistryTest, TestGetSRCHexwordFields)
351 {
352     using namespace openpower::pels::message::helper;
353     const auto hexwords = R"(
354     {"Words6To9":
355       {
356         "8":
357         {
358             "Description": "TEST",
359             "AdditionalDataPropSource": "TEST"
360         }
361       }
362     })"_json;
363 
364     auto fields = getSRCHexwordFields(hexwords, "foo");
365     EXPECT_TRUE(fields);
366     auto word = fields->find(8);
367     EXPECT_NE(word, fields->end());
368 
369     const auto theInvalidRWord = R"(
370     {"Words6To9":
371       {
372         "R":
373         {
374             "Description": "TEST",
375             "AdditionalDataPropSource": "TEST"
376         }
377       }
378     })"_json;
379 
380     EXPECT_THROW(getSRCHexwordFields(theInvalidRWord, "foo"),
381                  std::runtime_error);
382 }
383 
384 TEST_F(RegistryTest, TestGetSRCSymptomIDFields)
385 {
386     using namespace openpower::pels::message::helper;
387     const auto sID = R"(
388     {
389         "SymptomIDFields": ["SRCWord3", "SRCWord4", "SRCWord5"]
390     })"_json;
391 
392     auto fields = getSRCSymptomIDFields(sID, "foo");
393     EXPECT_NE(std::find(fields->begin(), fields->end(), 3), fields->end());
394     EXPECT_NE(std::find(fields->begin(), fields->end(), 4), fields->end());
395     EXPECT_NE(std::find(fields->begin(), fields->end(), 5), fields->end());
396 
397     const auto badField = R"(
398     {
399         "SymptomIDFields": ["SRCWord3", "SRCWord4", "SRCWord"]
400     })"_json;
401 
402     EXPECT_THROW(getSRCSymptomIDFields(badField, "foo"), std::runtime_error);
403 }
404 
405 TEST_F(RegistryTest, TestGetComponentID)
406 {
407     using namespace openpower::pels::message::helper;
408 
409     // Get it from the JSON
410     auto id = getComponentID(0xBD, 0x4200, R"({"ComponentID":"0x4200"})"_json,
411                              "foo");
412     EXPECT_EQ(id, 0x4200);
413 
414     // Get it from the reason code on a 0xBD SRC
415     id = getComponentID(0xBD, 0x6700, R"({})"_json, "foo");
416     EXPECT_EQ(id, 0x6700);
417 
418     // Not present on a 0x11 SRC
419     EXPECT_THROW(getComponentID(0x11, 0x8800, R"({})"_json, "foo"),
420                  std::runtime_error);
421 }
422 
423 // Test when callouts are in the JSON.
424 TEST_F(RegistryTest, TestGetCallouts)
425 {
426     std::vector<std::string> systemNames;
427 
428     {
429         // Callouts without AD, that depend on system type,
430         // where there is a default entry without a system type.
431         auto json = R"(
432         [
433         {
434             "System": "system1",
435             "CalloutList":
436             [
437                 {
438                     "Priority": "high",
439                     "LocCode": "P1-C1"
440                 },
441                 {
442                     "Priority": "low",
443                     "LocCode": "P1"
444                 },
445                 {
446                     "Priority": "low",
447                     "SymbolicFRU": "service_docs"
448                 },
449                 {
450                     "Priority": "low",
451                     "SymbolicFRUTrusted": "air_mover",
452                     "UseInventoryLocCode": true
453                 }
454             ]
455         },
456         {
457             "CalloutList":
458             [
459                 {
460                     "Priority": "medium",
461                     "Procedure": "BMC0001"
462                 },
463                 {
464                     "Priority": "low",
465                     "LocCode": "P3-C8",
466                     "SymbolicFRUTrusted": "service_docs"
467                 }
468             ]
469 
470         }
471         ])"_json;
472 
473         AdditionalData ad;
474         systemNames.push_back("system1");
475 
476         auto callouts = Registry::getCallouts(json, systemNames, ad);
477         EXPECT_EQ(callouts.size(), 4);
478         EXPECT_EQ(callouts[0].priority, "high");
479         EXPECT_EQ(callouts[0].locCode, "P1-C1");
480         EXPECT_EQ(callouts[0].procedure, "");
481         EXPECT_EQ(callouts[0].symbolicFRU, "");
482         EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
483         EXPECT_EQ(callouts[1].priority, "low");
484         EXPECT_EQ(callouts[1].locCode, "P1");
485         EXPECT_EQ(callouts[1].procedure, "");
486         EXPECT_EQ(callouts[1].symbolicFRU, "");
487         EXPECT_EQ(callouts[1].symbolicFRUTrusted, "");
488         EXPECT_EQ(callouts[2].priority, "low");
489         EXPECT_EQ(callouts[2].locCode, "");
490         EXPECT_EQ(callouts[2].procedure, "");
491         EXPECT_EQ(callouts[2].symbolicFRU, "service_docs");
492         EXPECT_EQ(callouts[2].symbolicFRUTrusted, "");
493         EXPECT_EQ(callouts[3].priority, "low");
494         EXPECT_EQ(callouts[3].locCode, "");
495         EXPECT_EQ(callouts[3].procedure, "");
496         EXPECT_EQ(callouts[3].symbolicFRU, "");
497         EXPECT_EQ(callouts[3].symbolicFRUTrusted, "air_mover");
498         EXPECT_EQ(callouts[3].useInventoryLocCode, true);
499 
500         // system2 isn't in the JSON, so it will pick the default one
501         systemNames[0] = "system2";
502         callouts = Registry::getCallouts(json, systemNames, ad);
503         EXPECT_EQ(callouts.size(), 2);
504         EXPECT_EQ(callouts[0].priority, "medium");
505         EXPECT_EQ(callouts[0].locCode, "");
506         EXPECT_EQ(callouts[0].procedure, "BMC0001");
507         EXPECT_EQ(callouts[0].symbolicFRU, "");
508         EXPECT_EQ(callouts[1].priority, "low");
509         EXPECT_EQ(callouts[1].locCode, "P3-C8");
510         EXPECT_EQ(callouts[1].procedure, "");
511         EXPECT_EQ(callouts[1].symbolicFRU, "");
512         EXPECT_EQ(callouts[1].symbolicFRUTrusted, "service_docs");
513         EXPECT_EQ(callouts[1].useInventoryLocCode, false);
514     }
515 
516     // Empty JSON array (treated as an error)
517     {
518         auto json = R"([])"_json;
519         AdditionalData ad;
520         systemNames[0] = "system1";
521         EXPECT_THROW(Registry::getCallouts(json, systemNames, ad),
522                      std::runtime_error);
523     }
524 
525     {
526         // Callouts without AD, that depend on system type,
527         // where there isn't a default entry without a system type.
528         auto json = R"(
529         [
530         {
531             "System": "system1",
532             "CalloutList":
533             [
534                 {
535                     "Priority": "high",
536                     "LocCode": "P1-C1"
537                 },
538                 {
539                     "Priority": "low",
540                     "LocCode": "P1",
541                     "SymbolicFRU": "1234567"
542                 }
543             ]
544         },
545         {
546             "System": "system2",
547             "CalloutList":
548             [
549                 {
550                     "Priority": "medium",
551                     "LocCode": "P7",
552                     "CalloutType": "tool_fru"
553                 }
554             ]
555 
556         }
557         ])"_json;
558 
559         AdditionalData ad;
560         systemNames[0] = "system1";
561 
562         auto callouts = Registry::getCallouts(json, systemNames, ad);
563         EXPECT_EQ(callouts.size(), 2);
564         EXPECT_EQ(callouts[0].priority, "high");
565         EXPECT_EQ(callouts[0].locCode, "P1-C1");
566         EXPECT_EQ(callouts[0].procedure, "");
567         EXPECT_EQ(callouts[0].symbolicFRU, "");
568         EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
569         EXPECT_EQ(callouts[1].priority, "low");
570         EXPECT_EQ(callouts[1].locCode, "P1");
571         EXPECT_EQ(callouts[1].procedure, "");
572         EXPECT_EQ(callouts[1].symbolicFRU, "1234567");
573         EXPECT_EQ(callouts[1].symbolicFRUTrusted, "");
574 
575         systemNames[0] = "system2";
576         callouts = Registry::getCallouts(json, systemNames, ad);
577         EXPECT_EQ(callouts.size(), 1);
578         EXPECT_EQ(callouts[0].priority, "medium");
579         EXPECT_EQ(callouts[0].locCode, "P7");
580         EXPECT_EQ(callouts[0].procedure, "");
581         EXPECT_EQ(callouts[0].symbolicFRU, "");
582         EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
583 
584         // There is no entry for system3 or a default system,
585         // so this should fail.
586         systemNames[0] = "system3";
587         EXPECT_THROW(Registry::getCallouts(json, systemNames, ad),
588                      std::runtime_error);
589     }
590 
591     {
592         // Callouts that use the AdditionalData key PROC_NUM
593         // as an index into them, along with a system type.
594         // It supports PROC_NUMs 0 and 1.
595         auto json = R"(
596         {
597             "ADName": "PROC_NUM",
598             "CalloutsWithTheirADValues":
599             [
600                 {
601                     "ADValue": "0",
602                     "Callouts":
603                     [
604                         {
605                             "System": "system3",
606                             "CalloutList":
607                             [
608                                 {
609                                     "Priority": "high",
610                                     "LocCode": "P1-C5"
611                                 },
612                                 {
613                                     "Priority": "medium",
614                                     "LocCode": "P1-C6",
615                                     "SymbolicFRU": "1234567"
616                                 },
617                                 {
618                                     "Priority": "low",
619                                     "Procedure": "BMC0001",
620                                     "CalloutType": "config_procedure"
621                                 }
622                             ]
623                         },
624                         {
625                             "CalloutList":
626                             [
627                                 {
628                                     "Priority": "low",
629                                     "LocCode": "P55"
630                                 }
631                             ]
632                         }
633                     ]
634                 },
635                 {
636                     "ADValue": "1",
637                     "Callouts":
638                     [
639                         {
640                             "CalloutList":
641                             [
642                                 {
643                                     "Priority": "high",
644                                     "LocCode": "P1-C6",
645                                     "CalloutType": "external_fru"
646                                 }
647                             ]
648                         }
649                     ]
650                 }
651             ]
652         })"_json;
653 
654         {
655             // Find callouts for PROC_NUM 0 on system3
656             std::vector<std::string> adData{"PROC_NUM=0"};
657             AdditionalData ad{adData};
658             systemNames[0] = "system3";
659 
660             auto callouts = Registry::getCallouts(json, systemNames, ad);
661             EXPECT_EQ(callouts.size(), 3);
662             EXPECT_EQ(callouts[0].priority, "high");
663             EXPECT_EQ(callouts[0].locCode, "P1-C5");
664             EXPECT_EQ(callouts[0].procedure, "");
665             EXPECT_EQ(callouts[0].symbolicFRU, "");
666             EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
667             EXPECT_EQ(callouts[1].priority, "medium");
668             EXPECT_EQ(callouts[1].locCode, "P1-C6");
669             EXPECT_EQ(callouts[1].procedure, "");
670             EXPECT_EQ(callouts[1].symbolicFRU, "1234567");
671             EXPECT_EQ(callouts[1].symbolicFRUTrusted, "");
672             EXPECT_EQ(callouts[2].priority, "low");
673             EXPECT_EQ(callouts[2].locCode, "");
674             EXPECT_EQ(callouts[2].procedure, "BMC0001");
675             EXPECT_EQ(callouts[2].symbolicFRU, "");
676             EXPECT_EQ(callouts[2].symbolicFRUTrusted, "");
677 
678             // Find callouts for PROC_NUM 0 that uses the default system entry.
679             systemNames[0] = "system99";
680 
681             callouts = Registry::getCallouts(json, systemNames, ad);
682             EXPECT_EQ(callouts.size(), 1);
683             EXPECT_EQ(callouts[0].priority, "low");
684             EXPECT_EQ(callouts[0].locCode, "P55");
685             EXPECT_EQ(callouts[0].procedure, "");
686             EXPECT_EQ(callouts[0].symbolicFRU, "");
687             EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
688         }
689         {
690             // Find callouts for PROC_NUM 1 that uses a default system entry.
691             std::vector<std::string> adData{"PROC_NUM=1"};
692             AdditionalData ad{adData};
693             systemNames[0] = "system1";
694 
695             auto callouts = Registry::getCallouts(json, systemNames, ad);
696             EXPECT_EQ(callouts.size(), 1);
697             EXPECT_EQ(callouts[0].priority, "high");
698             EXPECT_EQ(callouts[0].locCode, "P1-C6");
699             EXPECT_EQ(callouts[0].procedure, "");
700             EXPECT_EQ(callouts[0].symbolicFRU, "");
701             EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
702         }
703         {
704             // There is no entry for PROC_NUM 2, so no callouts
705             std::vector<std::string> adData{"PROC_NUM=2"};
706             AdditionalData ad{adData};
707 
708             auto callouts = Registry::getCallouts(json, systemNames, ad);
709             EXPECT_TRUE(callouts.empty());
710         }
711     }
712 
713     {
714         // Callouts with a 'CalloutsWhenNoADMatch' section that will
715         // be used when the AdditionalData value doesn't match.
716         auto json = R"(
717         {
718             "ADName": "PROC_NUM",
719             "CalloutsWithTheirADValues":
720             [
721                 {
722                     "ADValue": "0",
723                     "Callouts":
724                     [
725                         {
726                             "CalloutList":
727                             [
728                                 {
729                                     "Priority": "high",
730                                     "LocCode": "P0-C0"
731                                 }
732                             ]
733                         }
734                     ]
735                 }
736             ],
737             "CalloutsWhenNoADMatch": [
738                 {
739                     "CalloutList": [
740                         {
741                             "Priority": "medium",
742                             "LocCode": "P1-C1"
743                         }
744                     ]
745                 }
746             ]
747         })"_json;
748 
749         // There isn't an entry in the JSON for a PROC_NUM of 8
750         // so it should choose the P1-C1 callout.
751         std::vector<std::string> adData{"PROC_NUM=8"};
752         AdditionalData ad{adData};
753         systemNames.clear();
754 
755         auto callouts = Registry::getCallouts(json, systemNames, ad);
756         EXPECT_EQ(callouts.size(), 1);
757         EXPECT_EQ(callouts[0].priority, "medium");
758         EXPECT_EQ(callouts[0].locCode, "P1-C1");
759     }
760 }
761 
762 TEST_F(RegistryTest, TestGetCalloutsWithSystems)
763 {
764     std::vector<std::string> systemNames;
765 
766     auto json = R"(
767         [
768         {
769             "Systems": ["system1", "system2"],
770             "CalloutList":
771             [
772                 {
773                     "Priority": "high",
774                     "LocCode": "P1-C1"
775                 },
776                 {
777                     "Priority": "low",
778                     "LocCode": "P1"
779                 },
780                 {
781                     "Priority": "low",
782                     "SymbolicFRU": "service_docs"
783                 },
784                 {
785                     "Priority": "low",
786                     "SymbolicFRUTrusted": "air_mover",
787                     "UseInventoryLocCode": true
788                 }
789             ]
790         },
791         {
792             "CalloutList":
793             [
794                 {
795                     "Priority": "medium",
796                     "Procedure": "BMC0001"
797                 },
798                 {
799                     "Priority": "low",
800                     "LocCode": "P3-C8",
801                     "SymbolicFRUTrusted": "service_docs"
802                 }
803             ]
804 
805         }
806         ])"_json;
807 
808     AdditionalData ad;
809     systemNames.push_back("system1");
810 
811     auto callouts = Registry::getCallouts(json, systemNames, ad);
812     EXPECT_EQ(callouts.size(), 4);
813     EXPECT_EQ(callouts[0].priority, "high");
814     EXPECT_EQ(callouts[0].locCode, "P1-C1");
815     EXPECT_EQ(callouts[0].procedure, "");
816     EXPECT_EQ(callouts[0].symbolicFRU, "");
817     EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
818     EXPECT_EQ(callouts[1].priority, "low");
819     EXPECT_EQ(callouts[1].locCode, "P1");
820     EXPECT_EQ(callouts[1].procedure, "");
821     EXPECT_EQ(callouts[1].symbolicFRU, "");
822     EXPECT_EQ(callouts[1].symbolicFRUTrusted, "");
823     EXPECT_EQ(callouts[2].priority, "low");
824     EXPECT_EQ(callouts[2].locCode, "");
825     EXPECT_EQ(callouts[2].procedure, "");
826     EXPECT_EQ(callouts[2].symbolicFRU, "service_docs");
827     EXPECT_EQ(callouts[2].symbolicFRUTrusted, "");
828     EXPECT_EQ(callouts[3].priority, "low");
829     EXPECT_EQ(callouts[3].locCode, "");
830     EXPECT_EQ(callouts[3].procedure, "");
831     EXPECT_EQ(callouts[3].symbolicFRU, "");
832     EXPECT_EQ(callouts[3].symbolicFRUTrusted, "air_mover");
833     EXPECT_EQ(callouts[3].useInventoryLocCode, true);
834 
835     // System3 isn't in the JSON, so it will pick the default one
836     systemNames[0] = "system3";
837 
838     callouts = Registry::getCallouts(json, systemNames, ad);
839     EXPECT_EQ(callouts.size(), 2);
840     EXPECT_EQ(callouts[0].priority, "medium");
841     EXPECT_EQ(callouts[0].locCode, "");
842     EXPECT_EQ(callouts[0].procedure, "BMC0001");
843     EXPECT_EQ(callouts[0].symbolicFRU, "");
844     EXPECT_EQ(callouts[1].priority, "low");
845     EXPECT_EQ(callouts[1].locCode, "P3-C8");
846     EXPECT_EQ(callouts[1].procedure, "");
847     EXPECT_EQ(callouts[1].symbolicFRU, "");
848     EXPECT_EQ(callouts[1].symbolicFRUTrusted, "service_docs");
849     EXPECT_EQ(callouts[1].useInventoryLocCode, false);
850 }
851 
852 TEST_F(RegistryTest, TestGetCalloutsWithSystemAndSystems)
853 {
854     std::vector<std::string> systemNames;
855 
856     auto json = R"(
857         [
858         {
859             "Systems": ["system1", "system2"],
860             "CalloutList":
861             [
862                 {
863                     "Priority": "high",
864                     "LocCode": "P1-C1"
865                 },
866                 {
867                     "Priority": "low",
868                     "LocCode": "P1"
869                 }
870             ]
871         },
872         {
873             "System": "system1",
874             "CalloutList":
875             [
876                 {
877                     "Priority": "low",
878                     "SymbolicFRU": "service_docs"
879                 },
880                 {
881                     "Priority": "low",
882                     "SymbolicFRUTrusted": "air_mover",
883                     "UseInventoryLocCode": true
884                 }
885             ]
886         },
887         {
888             "CalloutList":
889             [
890                 {
891                     "Priority": "medium",
892                     "Procedure": "BMC0001"
893                 },
894                 {
895                     "Priority": "low",
896                     "LocCode": "P3-C8",
897                     "SymbolicFRUTrusted": "service_docs"
898                 }
899             ]
900         }
901         ])"_json;
902 
903     AdditionalData ad;
904     systemNames.push_back("system1");
905 
906     auto callouts = Registry::getCallouts(json, systemNames, ad);
907     EXPECT_EQ(callouts.size(), 4);
908     EXPECT_EQ(callouts[0].priority, "high");
909     EXPECT_EQ(callouts[0].locCode, "P1-C1");
910     EXPECT_EQ(callouts[0].procedure, "");
911     EXPECT_EQ(callouts[0].symbolicFRU, "");
912     EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
913     EXPECT_EQ(callouts[1].priority, "low");
914     EXPECT_EQ(callouts[1].locCode, "P1");
915     EXPECT_EQ(callouts[1].procedure, "");
916     EXPECT_EQ(callouts[1].symbolicFRU, "");
917     EXPECT_EQ(callouts[1].symbolicFRUTrusted, "");
918     EXPECT_EQ(callouts[2].priority, "low");
919     EXPECT_EQ(callouts[2].locCode, "");
920     EXPECT_EQ(callouts[2].procedure, "");
921     EXPECT_EQ(callouts[2].symbolicFRU, "service_docs");
922     EXPECT_EQ(callouts[2].symbolicFRUTrusted, "");
923     EXPECT_EQ(callouts[3].priority, "low");
924     EXPECT_EQ(callouts[3].locCode, "");
925     EXPECT_EQ(callouts[3].procedure, "");
926     EXPECT_EQ(callouts[3].symbolicFRU, "");
927     EXPECT_EQ(callouts[3].symbolicFRUTrusted, "air_mover");
928     EXPECT_EQ(callouts[3].useInventoryLocCode, true);
929 
930     // if system name is "System2"
931     systemNames[0] = "system2";
932 
933     callouts = Registry::getCallouts(json, systemNames, ad);
934     EXPECT_EQ(callouts.size(), 2);
935     EXPECT_EQ(callouts[0].priority, "high");
936     EXPECT_EQ(callouts[0].locCode, "P1-C1");
937     EXPECT_EQ(callouts[0].procedure, "");
938     EXPECT_EQ(callouts[0].symbolicFRU, "");
939     EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
940     EXPECT_EQ(callouts[1].priority, "low");
941     EXPECT_EQ(callouts[1].locCode, "P1");
942     EXPECT_EQ(callouts[1].procedure, "");
943     EXPECT_EQ(callouts[1].symbolicFRU, "");
944     EXPECT_EQ(callouts[1].symbolicFRUTrusted, "");
945 
946     // system name is System3 which is not in json thereby will take default
947     systemNames[0] = "system3";
948 
949     callouts = Registry::getCallouts(json, systemNames, ad);
950     EXPECT_EQ(callouts.size(), 2);
951     EXPECT_EQ(callouts[0].priority, "medium");
952     EXPECT_EQ(callouts[0].locCode, "");
953     EXPECT_EQ(callouts[0].procedure, "BMC0001");
954     EXPECT_EQ(callouts[0].symbolicFRU, "");
955     EXPECT_EQ(callouts[1].priority, "low");
956     EXPECT_EQ(callouts[1].locCode, "P3-C8");
957     EXPECT_EQ(callouts[1].procedure, "");
958     EXPECT_EQ(callouts[1].symbolicFRU, "");
959     EXPECT_EQ(callouts[1].symbolicFRUTrusted, "service_docs");
960     EXPECT_EQ(callouts[1].useInventoryLocCode, false);
961 }
962 
963 TEST_F(RegistryTest, TestGetCalloutsWithOnlySystemAndSystems)
964 {
965     std::vector<std::string> systemNames;
966 
967     auto json = R"(
968         [
969         {
970             "Systems": ["system1", "system2"],
971             "CalloutList":
972             [
973                 {
974                     "Priority": "high",
975                     "LocCode": "P1-C1"
976                 },
977                 {
978                     "Priority": "low",
979                     "LocCode": "P1"
980                 }
981             ]
982         },
983         {
984             "System": "system1",
985             "CalloutList":
986             [
987                 {
988                     "Priority": "low",
989                     "SymbolicFRU": "service_docs"
990                 },
991                 {
992                     "Priority": "low",
993                     "SymbolicFRUTrusted": "air_mover",
994                     "UseInventoryLocCode": true
995                 }
996             ]
997         }
998         ])"_json;
999 
1000     AdditionalData ad;
1001     systemNames.push_back("system1");
1002 
1003     auto callouts = Registry::getCallouts(json, systemNames, ad);
1004     EXPECT_EQ(callouts.size(), 4);
1005     EXPECT_EQ(callouts[0].priority, "high");
1006     EXPECT_EQ(callouts[0].locCode, "P1-C1");
1007     EXPECT_EQ(callouts[0].procedure, "");
1008     EXPECT_EQ(callouts[0].symbolicFRU, "");
1009     EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
1010     EXPECT_EQ(callouts[1].priority, "low");
1011     EXPECT_EQ(callouts[1].locCode, "P1");
1012     EXPECT_EQ(callouts[1].procedure, "");
1013     EXPECT_EQ(callouts[1].symbolicFRU, "");
1014     EXPECT_EQ(callouts[1].symbolicFRUTrusted, "");
1015     EXPECT_EQ(callouts[2].priority, "low");
1016     EXPECT_EQ(callouts[2].locCode, "");
1017     EXPECT_EQ(callouts[2].procedure, "");
1018     EXPECT_EQ(callouts[2].symbolicFRU, "service_docs");
1019     EXPECT_EQ(callouts[2].symbolicFRUTrusted, "");
1020     EXPECT_EQ(callouts[3].priority, "low");
1021     EXPECT_EQ(callouts[3].locCode, "");
1022     EXPECT_EQ(callouts[3].procedure, "");
1023     EXPECT_EQ(callouts[3].symbolicFRU, "");
1024     EXPECT_EQ(callouts[3].symbolicFRUTrusted, "air_mover");
1025     EXPECT_EQ(callouts[3].useInventoryLocCode, true);
1026 
1027     // if system name is "System2"
1028     systemNames[0] = "system2";
1029 
1030     callouts = Registry::getCallouts(json, systemNames, ad);
1031     EXPECT_EQ(callouts.size(), 2);
1032     EXPECT_EQ(callouts[0].priority, "high");
1033     EXPECT_EQ(callouts[0].locCode, "P1-C1");
1034     EXPECT_EQ(callouts[0].procedure, "");
1035     EXPECT_EQ(callouts[0].symbolicFRU, "");
1036     EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
1037     EXPECT_EQ(callouts[1].priority, "low");
1038     EXPECT_EQ(callouts[1].locCode, "P1");
1039     EXPECT_EQ(callouts[1].procedure, "");
1040     EXPECT_EQ(callouts[1].symbolicFRU, "");
1041     EXPECT_EQ(callouts[1].symbolicFRUTrusted, "");
1042 
1043     // There is no entry for system3 or a default system,
1044     // so this should fail.
1045     systemNames[0] = "system3";
1046     EXPECT_THROW(Registry::getCallouts(json, systemNames, ad),
1047                  std::runtime_error);
1048 }
1049 
1050 TEST_F(RegistryTest, TestGetCalloutsWithOnlySystems)
1051 {
1052     std::vector<std::string> systemNames;
1053 
1054     auto json = R"(
1055         [
1056         {
1057             "Systems": ["system1", "system2"],
1058             "CalloutList":
1059             [
1060                 {
1061                     "Priority": "high",
1062                     "LocCode": "P1-C1"
1063                 },
1064                 {
1065                     "Priority": "low",
1066                     "LocCode": "P1"
1067                 }
1068             ]
1069         }
1070         ])"_json;
1071 
1072     AdditionalData ad;
1073     systemNames.push_back("system1");
1074 
1075     // system1 is available in JSON array
1076     auto callouts = Registry::getCallouts(json, systemNames, ad);
1077     EXPECT_EQ(callouts.size(), 2);
1078     EXPECT_EQ(callouts[0].priority, "high");
1079     EXPECT_EQ(callouts[0].locCode, "P1-C1");
1080     EXPECT_EQ(callouts[0].procedure, "");
1081     EXPECT_EQ(callouts[0].symbolicFRU, "");
1082     EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
1083     EXPECT_EQ(callouts[1].priority, "low");
1084     EXPECT_EQ(callouts[1].locCode, "P1");
1085     EXPECT_EQ(callouts[1].procedure, "");
1086     EXPECT_EQ(callouts[1].symbolicFRU, "");
1087     EXPECT_EQ(callouts[1].symbolicFRUTrusted, "");
1088 
1089     // There is no entry for system3 or a default system,
1090     // so this should fail.
1091     systemNames[0] = "system3";
1092     EXPECT_THROW(Registry::getCallouts(json, systemNames, ad),
1093                  std::runtime_error);
1094 }
1095 
1096 TEST_F(RegistryTest, TestGetCalloutsWithOnlyDefaults)
1097 {
1098     std::vector<std::string> systemNames;
1099 
1100     auto json = R"(
1101         [
1102         {
1103             "CalloutList":
1104             [
1105                 {
1106                     "Priority": "high",
1107                     "LocCode": "P1-C1"
1108                 },
1109                 {
1110                     "Priority": "low",
1111                     "LocCode": "P1"
1112                 }
1113             ]
1114         }
1115         ])"_json;
1116 
1117     AdditionalData ad;
1118     systemNames.push_back("system1");
1119 
1120     // Since neither System or Systems available, it will pick the default one
1121     // only
1122     auto callouts = Registry::getCallouts(json, systemNames, ad);
1123     EXPECT_EQ(callouts.size(), 2);
1124     EXPECT_EQ(callouts[0].priority, "high");
1125     EXPECT_EQ(callouts[0].locCode, "P1-C1");
1126     EXPECT_EQ(callouts[0].procedure, "");
1127     EXPECT_EQ(callouts[0].symbolicFRU, "");
1128     EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
1129     EXPECT_EQ(callouts[1].priority, "low");
1130     EXPECT_EQ(callouts[1].locCode, "P1");
1131     EXPECT_EQ(callouts[1].procedure, "");
1132     EXPECT_EQ(callouts[1].symbolicFRU, "");
1133     EXPECT_EQ(callouts[1].symbolicFRUTrusted, "");
1134 }
1135 
1136 TEST_F(RegistryTest, TestNoSubsystem)
1137 {
1138     auto path = RegistryTest::writeData(registryData);
1139     Registry registry{path};
1140 
1141     auto entry = registry.lookup("xyz.openbmc_project.Common.Error.Timeout",
1142                                  LookupType::name);
1143     ASSERT_TRUE(entry);
1144     EXPECT_FALSE(entry->subsystem);
1145 }
1146 
1147 TEST_F(RegistryTest, TestJournalSectionCapture)
1148 {
1149     auto path = RegistryTest::writeData(registryData);
1150     Registry registry{path};
1151 
1152     auto entry = registry.lookup("xyz.openbmc_project.Journal.Capture",
1153                                  LookupType::name);
1154     ASSERT_TRUE(entry);
1155 
1156     const auto& jc = entry->journalCapture;
1157     ASSERT_TRUE(jc);
1158     ASSERT_TRUE(std::holds_alternative<AppCaptureList>(*jc));
1159     const auto& acl = std::get<AppCaptureList>(*jc);
1160 
1161     ASSERT_EQ(acl.size(), 2);
1162 
1163     EXPECT_EQ(acl[0].syslogID, "test1");
1164     EXPECT_EQ(acl[0].numLines, 5);
1165 
1166     EXPECT_EQ(acl[1].syslogID, "test2");
1167     EXPECT_EQ(acl[1].numLines, 6);
1168 }
1169