More test cases for Resolution class

Signed-off-by: Zane Shelley <zshelle@us.ibm.com>
Change-Id: Ic887f97c4aae8d194657f9925cbd8336d3e0feef
diff --git a/analyzer/resolution.cpp b/analyzer/resolution.cpp
index 354ccbc..fb451aa 100644
--- a/analyzer/resolution.cpp
+++ b/analyzer/resolution.cpp
@@ -7,27 +7,51 @@
 
 //------------------------------------------------------------------------------
 
-void HardwareCalloutResolution::resolve(ServiceData& io_sd) const
+// Helper function to get the root cause chip target from the service data.
+pdbg_target* __getRootCauseChipTarget(const ServiceData& i_sd)
 {
-    // Get the chip target from the root cause signature.
-    auto trgt = util::pdbg::getTrgt(io_sd.getRootCause().getChip());
-    auto path = std::string{util::pdbg::getPath(trgt)};
+    auto target = util::pdbg::getTrgt(i_sd.getRootCause().getChip());
+    assert(nullptr != target); // This would be a really bad bug.
+    return target;
+}
 
-    // Get the unit target, if needed.
-    if (!iv_path.empty())
+//------------------------------------------------------------------------------
+
+// Helper function to get a unit target from the given unit path, which is a
+// devtree path relative the the containing chip. An empty string indicates the
+// chip target should be returned.
+pdbg_target* __getUnitTarget(pdbg_target* i_chipTarget,
+                             const std::string& i_unitPath)
+{
+    assert(nullptr != i_chipTarget);
+
+    auto target = i_chipTarget; // default, if i_unitPath is empty
+
+    if (!i_unitPath.empty())
     {
-        path += "/" + iv_path;
-        trgt = util::pdbg::getTrgt(path);
-        if (nullptr == trgt)
+        auto path = std::string{util::pdbg::getPath(target)} + "/" + i_unitPath;
+
+        target = util::pdbg::getTrgt(path);
+        if (nullptr == target)
         {
-            trace::err("Unable to find target for %s", path.c_str());
-            return; // can't continue
+            // Likely a bug the RAS data files.
+            throw std::logic_error("Unable to find target for " + path);
         }
     }
 
+    return target;
+}
+
+//------------------------------------------------------------------------------
+
+void HardwareCalloutResolution::resolve(ServiceData& io_sd) const
+{
+    // Get the target for the hardware callout.
+    auto target = __getUnitTarget(__getRootCauseChipTarget(io_sd), iv_unitPath);
+
     // Get the location code and entity path for this target.
-    auto locCode    = util::pdbg::getLocationCode(trgt);
-    auto entityPath = util::pdbg::getPhysDevPath(trgt);
+    auto locCode    = util::pdbg::getLocationCode(target);
+    auto entityPath = util::pdbg::getPhysDevPath(target);
 
     // Add the actual callout to the service data.
     nlohmann::json callout;
@@ -71,7 +95,7 @@
     // auto target = std::string{util::pdbg::getPath(m.at(iv_clockType))};
     // auto guardPath = util::pdbg::getPhysDevPath(target);
     // Guard guard = io_sd.addGuard(guardPath, iv_guard);
-    auto target    = util::pdbg::getTrgt(io_sd.getRootCause().getChip());
+    auto target    = __getRootCauseChipTarget(io_sd);
     auto guardPath = util::pdbg::getPhysDevPath(target);
 
     // Add the callout FFDC to the service data.
diff --git a/analyzer/resolution.hpp b/analyzer/resolution.hpp
index 1574d04..7c20647 100644
--- a/analyzer/resolution.hpp
+++ b/analyzer/resolution.hpp
@@ -30,21 +30,22 @@
   public:
     /**
      * @brief Constructor from components.
-     * @param i_path     The devtree path of a guardable unit relative to a
+     * @param i_unitPath The devtree path of a guardable unit relative to a
      *                   chip. An empty string refers to the chip itself.
      * @param i_priority The callout priority.
      * @param i_guard    True, if guard is required. False, otherwise.
      */
-    HardwareCalloutResolution(const std::string& i_path,
-                              callout::Priority i_priority, bool i_guard) :
-        iv_path(i_path),
+    HardwareCalloutResolution(const std::string& i_unitPath,
+                              const callout::Priority& i_priority,
+                              bool i_guard) :
+        iv_unitPath(i_unitPath),
         iv_priority(i_priority), iv_guard(i_guard)
     {}
 
   private:
     /** The devtree path of a guardable unit relative to a chip. An empty string
      *  refers to the chip itself. */
-    const std::string iv_path;
+    const std::string iv_unitPath;
 
     /** The callout priority. */
     const callout::Priority iv_priority;
diff --git a/test/resolution_test.cpp b/test/resolution_test.cpp
index 7cf95dd..52242f1 100644
--- a/test/resolution_test.cpp
+++ b/test/resolution_test.cpp
@@ -18,25 +18,54 @@
 
 //------------------------------------------------------------------------------
 
+// Helper function to get the root cause chip target path from the service data.
+std::string __getRootCauseChipPath(const ServiceData& i_sd)
+{
+    return std::string{(const char*)i_sd.getRootCause().getChip().getChip()};
+}
+
+//------------------------------------------------------------------------------
+
+// Helper function to get a unit target path from the given unit path, which is
+// a devtree path relative the the containing chip. An empty string indicates
+// the chip target path should be returned.
+std::string __getUnitPath(const std::string& i_chipPath,
+                          const std::string& i_unitPath)
+{
+    auto path = i_chipPath; // default, if i_unitPath is empty
+
+    if (!i_unitPath.empty())
+    {
+        path += "/" + i_unitPath;
+    }
+
+    return path;
+}
+
+//------------------------------------------------------------------------------
+
 void HardwareCalloutResolution::resolve(ServiceData& io_sd) const
 {
-    auto sig = io_sd.getRootCause();
-
-    std::string fru{(const char*)sig.getChip().getChip()};
-    std::string path{fru};
-    if (!iv_path.empty())
-    {
-        path += "/" + iv_path;
-    }
+    // Get the location code and entity path for this target.
+    auto locCode    = __getRootCauseChipPath(io_sd);
+    auto entityPath = __getUnitPath(locCode, iv_unitPath);
 
     // Add the actual callout to the service data.
     nlohmann::json callout;
-    callout["LocationCode"] = fru;
+    callout["LocationCode"] = locCode;
     callout["Priority"]     = iv_priority.getUserDataString();
     io_sd.addCallout(callout);
 
     // Add the guard info to the service data.
-    io_sd.addGuard(path, iv_guard);
+    Guard guard = io_sd.addGuard(entityPath, iv_guard);
+
+    // Add the callout FFDC to the service data.
+    nlohmann::json ffdc;
+    ffdc["Callout Type"] = "Hardware Callout";
+    ffdc["Target"]       = entityPath;
+    ffdc["Priority"]     = iv_priority.getRegistryString();
+    ffdc["Guard Type"]   = guard.getString();
+    io_sd.addCalloutFFDC(ffdc);
 }
 
 //------------------------------------------------------------------------------
@@ -48,25 +77,49 @@
     callout["Procedure"] = iv_procedure.getString();
     callout["Priority"]  = iv_priority.getUserDataString();
     io_sd.addCallout(callout);
+
+    // Add the callout FFDC to the service data.
+    nlohmann::json ffdc;
+    ffdc["Callout Type"] = "Procedure Callout";
+    ffdc["Procedure"]    = iv_procedure.getString();
+    ffdc["Priority"]     = iv_priority.getRegistryString();
+    io_sd.addCalloutFFDC(ffdc);
 }
 
 //------------------------------------------------------------------------------
 
 void ClockCalloutResolution::resolve(ServiceData& io_sd) const
 {
-    auto sig = io_sd.getRootCause();
-
-    std::string fru{"P0"};
-    std::string path{(const char*)sig.getChip().getChip()};
-
-    // Add the actual callout to the service data.
+    // Add the callout to the service data.
+    // TODO: For P10, the callout is simply the backplane. There isn't a devtree
+    //       object for this, yet. So will need to hardcode the location code
+    //       for now. In the future, we will need a mechanism to make this data
+    //       driven.
     nlohmann::json callout;
-    callout["LocationCode"] = fru;
+    callout["LocationCode"] = "P0";
     callout["Priority"]     = iv_priority.getUserDataString();
     io_sd.addCallout(callout);
 
     // Add the guard info to the service data.
-    io_sd.addGuard(path, iv_guard);
+    // TODO: Still waiting for clock targets to be defined in the device tree.
+    //       For get the processor path for the FFDC.
+    // static const std::map<callout::ClockType, std::string> m = {
+    //     {callout::ClockType::OSC_REF_CLOCK_0, ""},
+    //     {callout::ClockType::OSC_REF_CLOCK_1, ""},
+    // };
+    // auto target = std::string{util::pdbg::getPath(m.at(iv_clockType))};
+    // auto guardPath = util::pdbg::getPhysDevPath(target);
+    // Guard guard = io_sd.addGuard(guardPath, iv_guard);
+    auto guardPath = __getRootCauseChipPath(io_sd);
+
+    // Add the callout FFDC to the service data.
+    nlohmann::json ffdc;
+    ffdc["Callout Type"] = "Clock Callout";
+    ffdc["Clock Type"]   = iv_clockType.getString();
+    ffdc["Target"]       = guardPath;
+    ffdc["Priority"]     = iv_priority.getRegistryString();
+    ffdc["Guard Type"]   = ""; // TODO: guard.getString();
+    io_sd.addCalloutFFDC(ffdc);
 }
 
 //------------------------------------------------------------------------------
@@ -134,7 +187,7 @@
         "Priority": "L"
     }
 ])";
-    ASSERT_EQ(s, j.dump(4));
+    EXPECT_EQ(s, j.dump(4));
 
     j = sd2.getCalloutList();
     s = R"([
@@ -159,5 +212,116 @@
         "Priority": "L"
     }
 ])";
-    ASSERT_EQ(s, j.dump(4));
+    EXPECT_EQ(s, j.dump(4));
+}
+
+TEST(Resolution, HardwareCallout)
+{
+    auto c1 = std::make_shared<HardwareCalloutResolution>(
+        omi_str, callout::Priority::MED_A, true);
+
+    libhei::Chip chip{chip_str, 0xdeadbeef};
+    libhei::Signature sig{chip, 0xabcd, 0, 0, libhei::ATTN_TYPE_CHECKSTOP};
+    ServiceData sd{sig, true};
+
+    c1->resolve(sd);
+
+    nlohmann::json j{};
+    std::string s{};
+
+    // Callout list
+    j = sd.getCalloutList();
+    s = R"([
+    {
+        "LocationCode": "/proc0",
+        "Priority": "A"
+    }
+])";
+    EXPECT_EQ(s, j.dump(4));
+
+    // Callout FFDC
+    j = sd.getCalloutFFDC();
+    s = R"([
+    {
+        "Callout Type": "Hardware Callout",
+        "Guard Type": "FATAL",
+        "Priority": "medium_group_A",
+        "Target": "/proc0/pib/perv12/mc0/mi0/mcc0/omi0"
+    }
+])";
+    EXPECT_EQ(s, j.dump(4));
+}
+
+TEST(Resolution, ClockCallout)
+{
+    auto c1 = std::make_shared<ClockCalloutResolution>(
+        callout::ClockType::OSC_REF_CLOCK_1, callout::Priority::HIGH, false);
+
+    libhei::Chip chip{chip_str, 0xdeadbeef};
+    libhei::Signature sig{chip, 0xabcd, 0, 0, libhei::ATTN_TYPE_CHECKSTOP};
+    ServiceData sd{sig, true};
+
+    c1->resolve(sd);
+
+    nlohmann::json j{};
+    std::string s{};
+
+    // Callout list
+    j = sd.getCalloutList();
+    s = R"([
+    {
+        "LocationCode": "P0",
+        "Priority": "H"
+    }
+])";
+    EXPECT_EQ(s, j.dump(4));
+
+    // Callout FFDC
+    j = sd.getCalloutFFDC();
+    s = R"([
+    {
+        "Callout Type": "Clock Callout",
+        "Clock Type": "OSC_REF_CLOCK_1",
+        "Guard Type": "",
+        "Priority": "high",
+        "Target": "/proc0"
+    }
+])";
+    EXPECT_EQ(s, j.dump(4));
+}
+
+TEST(Resolution, ProcedureCallout)
+{
+    auto c1 = std::make_shared<ProcedureCalloutResolution>(
+        callout::Procedure::NEXTLVL, callout::Priority::LOW);
+
+    libhei::Chip chip{chip_str, 0xdeadbeef};
+    libhei::Signature sig{chip, 0xabcd, 0, 0, libhei::ATTN_TYPE_CHECKSTOP};
+    ServiceData sd{sig, true};
+
+    c1->resolve(sd);
+
+    nlohmann::json j{};
+    std::string s{};
+
+    // Callout list
+    j = sd.getCalloutList();
+    s = R"([
+    {
+        "Priority": "L",
+        "Procedure": "NEXTLVL"
+    }
+])";
+    EXPECT_EQ(s, j.dump(4));
+
+    // Callout FFDC
+    j = sd.getCalloutFFDC();
+    s = R"([
+    {
+        "Callout Type": "Procedure Callout",
+        "Priority": "low",
+        "Procedure": "NEXTLVL"
+    }
+])";
+    EXPECT_EQ(s, j.dump(4));
 }