Initial classes for callout resolutions

Signed-off-by: Zane Shelley <zshelle@us.ibm.com>
Change-Id: I5df0c1cc59facc5c36aac20ca52503d20ae0f76a
diff --git a/analyzer/meson.build b/analyzer/meson.build
index 1ef013c..461244c 100644
--- a/analyzer/meson.build
+++ b/analyzer/meson.build
@@ -4,6 +4,7 @@
     'create_pel.cpp',
     'hei_user_interface.cpp',
     'initialize_isolator.cpp',
+    'resolution.cpp',
 )
 
 # Library dependencies.
diff --git a/analyzer/resolution.cpp b/analyzer/resolution.cpp
new file mode 100644
index 0000000..9d994ce
--- /dev/null
+++ b/analyzer/resolution.cpp
@@ -0,0 +1,11 @@
+#include <analyzer/resolution.hpp>
+
+namespace analyzer
+{
+
+void HardwareCalloutResolution::resolve(ServiceData&) const
+{
+    // TODO: Add location code to callout list and entity path to gard list.
+}
+
+} // namespace analyzer
diff --git a/analyzer/resolution.hpp b/analyzer/resolution.hpp
new file mode 100644
index 0000000..485de2c
--- /dev/null
+++ b/analyzer/resolution.hpp
@@ -0,0 +1,124 @@
+#pragma once
+
+#include <analyzer/service_data.hpp>
+
+namespace analyzer
+{
+
+/** @brief An abstract class for service event resolutions. */
+class Resolution
+{
+  public:
+    /** @brief Pure virtual destructor. */
+    virtual ~Resolution() = 0;
+
+  public:
+    /**
+     * @brief Resolves the service actions required by this resolution.
+     * @param io_sd An object containing the service data collected during
+     *              hardware error analysis.
+     */
+    virtual void resolve(ServiceData& io_sd) const = 0;
+};
+
+// Pure virtual destructor must be defined.
+inline Resolution::~Resolution() {}
+
+/** @brief Resolves a hardware callout service event. */
+class HardwareCalloutResolution : public Resolution
+{
+  public:
+    /**
+     * @brief Constructor from components.
+     * @param i_path     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    The guard type for this callout.
+     */
+    HardwareCalloutResolution(const std::string& i_path,
+                              Callout::Priority i_priority,
+                              Guard::Type i_guard) :
+        iv_path(i_path),
+        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;
+
+    /** The callout priority. */
+    const Callout::Priority iv_priority;
+
+    /** The guard type for this callout. */
+    const Guard::Type iv_guard;
+
+  public:
+    void resolve(ServiceData& io_sd) const override;
+};
+
+/** @brief Resolves a procedure callout service event. */
+class ProcedureCalloutResolution : public Resolution
+{
+  public:
+    /**
+     * @brief Constructor from components.
+     * @param i_procedure The procedure callout type.
+     * @param i_priority  The callout priority.
+     */
+    ProcedureCalloutResolution(ProcedureCallout::Type i_procedure,
+                               Callout::Priority i_priority) :
+        iv_procedure(i_procedure),
+        iv_priority(i_priority)
+    {}
+
+  private:
+    /** The procedure callout type. */
+    const ProcedureCallout::Type iv_procedure;
+
+    /** The callout priority. */
+    const Callout::Priority iv_priority;
+
+  public:
+    void resolve(ServiceData& io_sd) const override
+    {
+        // Simply add the procedure to the callout list.
+        io_sd.addCallout(
+            std::make_shared<ProcedureCallout>(iv_procedure, iv_priority));
+    }
+};
+
+/**
+ * @brief Links two resolutions into a single resolution. The resolutions will
+ *        will be resolved in the order they are inputted into the constructor.
+ */
+class LinkResolution : public Resolution
+{
+  public:
+    /**
+     * @brief Constructor from components.
+     * @param i_first  The first resolution.
+     * @param i_second The second resolution.
+     */
+    LinkResolution(const std::shared_ptr<Resolution>& i_first,
+                   const std::shared_ptr<Resolution>& i_second) :
+        iv_first(i_first),
+        iv_second(i_second)
+    {}
+
+  private:
+    /** The first resolution. */
+    const std::shared_ptr<Resolution> iv_first;
+
+    /** The second resolution. */
+    const std::shared_ptr<Resolution> iv_second;
+
+  public:
+    void resolve(ServiceData& io_sd) const override
+    {
+        iv_first->resolve(io_sd);
+        iv_second->resolve(io_sd);
+    }
+};
+
+} // namespace analyzer
diff --git a/analyzer/service_data.hpp b/analyzer/service_data.hpp
index ffd0804..9f93bb9 100644
--- a/analyzer/service_data.hpp
+++ b/analyzer/service_data.hpp
@@ -114,7 +114,7 @@
 {
   public:
     /** Supported service procedures. */
-    enum Procedure
+    enum Type
     {
         NEXTLVL, ///< Contact next level support.
     };
@@ -125,19 +125,19 @@
      * @param i_procedure The location code of the hardware callout.
      * @param i_priority     The callout priority.
      */
-    ProcedureCallout(Procedure i_procedure, Priority i_priority) :
+    ProcedureCallout(Type i_procedure, Priority i_priority) :
         Callout(i_priority), iv_procedure(i_procedure)
     {}
 
   private:
     /** The callout priority. */
-    const Procedure iv_procedure;
+    const Type iv_procedure;
 
   public:
     void getJson(nlohmann::json& j) const override
     {
         // clang-format off
-        static const std::map<Procedure, std::string> m =
+        static const std::map<Type, std::string> m =
         {
             {NEXTLVL, "NEXTLVL"},
         };
diff --git a/test/meson.build b/test/meson.build
index 9486773..584a294 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -8,6 +8,7 @@
 tests = [
   'hello-world',
   'bin_stream_test',
+  'resolution_test',
   'service_data_test',
 ]
 
diff --git a/test/resolution_test.cpp b/test/resolution_test.cpp
new file mode 100644
index 0000000..61e0b35
--- /dev/null
+++ b/test/resolution_test.cpp
@@ -0,0 +1,152 @@
+#include <stdio.h>
+
+#include <analyzer/resolution.hpp>
+
+#include "gtest/gtest.h"
+
+// Chip string
+constexpr auto chip_str = "/proc0";
+
+// Unit paths
+constexpr auto proc_str = "";
+constexpr auto omi_str  = "pib/perv12/mc0/mi0/mcc0/omi0";
+constexpr auto core_str = "pib/perv39/eq7/fc1/core1";
+
+// Local implementation of this function.
+namespace analyzer
+{
+
+void HardwareCalloutResolution::resolve(ServiceData& io_sd) const
+{
+    auto sig = io_sd.getRootCause();
+
+    std::string fru{(const char*)sig.getChip().getChip()};
+    std::string guard{fru};
+    if (!iv_path.empty())
+    {
+        guard += "/" + iv_path;
+    }
+
+    io_sd.addCallout(std::make_shared<HardwareCallout>(fru, iv_priority));
+
+    io_sd.addGuard(std::make_shared<Guard>(guard, iv_guard));
+}
+
+} // namespace analyzer
+
+using namespace analyzer;
+
+TEST(Resolution, TestSet1)
+{
+    // Create a few resolutions
+    auto c1 = std::make_shared<HardwareCalloutResolution>(
+        proc_str, Callout::Priority::HIGH, Guard::NONE);
+
+    auto c2 = std::make_shared<HardwareCalloutResolution>(
+        omi_str, Callout::Priority::MED_A, Guard::FATAL);
+
+    auto c3 = std::make_shared<HardwareCalloutResolution>(
+        core_str, Callout::Priority::MED, Guard::NON_FATAL);
+
+    auto c4 = std::make_shared<ProcedureCalloutResolution>(
+        ProcedureCallout::NEXTLVL, Callout::Priority::LOW);
+
+    // l3 = (c1, c2, c3, c4)
+    auto l1 = std::make_shared<LinkResolution>(c1, c2);
+    auto l2 = std::make_shared<LinkResolution>(l1, c3);
+    auto l3 = std::make_shared<LinkResolution>(l2, c4);
+
+    // l5 = (c4, c3, c1, c2)
+    auto l4 = std::make_shared<LinkResolution>(c3, l1);
+    auto l5 = std::make_shared<LinkResolution>(c4, l4);
+
+    // Get some ServiceData objects
+    libhei::Chip chip{chip_str, 0xdeadbeef};
+    libhei::Signature sig{chip, 0xabcd, 0, 0, libhei::ATTN_TYPE_CHECKSTOP};
+    ServiceData sd1{sig};
+    ServiceData sd2{sig};
+
+    // Resolve
+    l3->resolve(sd1);
+    l5->resolve(sd2);
+
+    // Start verifying
+    nlohmann::json j{};
+    std::string s{};
+
+    sd1.getCalloutList(j);
+    s = R"([
+    {
+        "LocationCode": "/proc0",
+        "Priority": "H"
+    },
+    {
+        "LocationCode": "/proc0",
+        "Priority": "A"
+    },
+    {
+        "LocationCode": "/proc0",
+        "Priority": "M"
+    },
+    {
+        "Priority": "L",
+        "Procedure": "NEXTLVL"
+    }
+])";
+    ASSERT_EQ(s, j.dump(4));
+
+    sd1.getGuardList(j);
+    s = R"([
+    {
+        "Path": "/proc0",
+        "Type": "NONE"
+    },
+    {
+        "Path": "/proc0/pib/perv12/mc0/mi0/mcc0/omi0",
+        "Type": "FATAL"
+    },
+    {
+        "Path": "/proc0/pib/perv39/eq7/fc1/core1",
+        "Type": "NON_FATAL"
+    }
+])";
+    ASSERT_EQ(s, j.dump(4));
+
+    sd2.getCalloutList(j);
+    s = R"([
+    {
+        "Priority": "L",
+        "Procedure": "NEXTLVL"
+    },
+    {
+        "LocationCode": "/proc0",
+        "Priority": "M"
+    },
+    {
+        "LocationCode": "/proc0",
+        "Priority": "H"
+    },
+    {
+        "LocationCode": "/proc0",
+        "Priority": "A"
+    }
+])";
+    ASSERT_EQ(s, j.dump(4));
+
+    sd2.getGuardList(j);
+    s = R"([
+    {
+        "Path": "/proc0/pib/perv39/eq7/fc1/core1",
+        "Type": "NON_FATAL"
+    },
+    {
+        "Path": "/proc0",
+        "Type": "NONE"
+    },
+    {
+        "Path": "/proc0/pib/perv12/mc0/mi0/mcc0/omi0",
+        "Type": "FATAL"
+    }
+])";
+    ASSERT_EQ(s, j.dump(4));
+}