regulators: Complete compare_vpd action

Complete implementation of the compare_vpd action in the JSON
configuration file.  For more information about this action see
https://github.com/openbmc/phosphor-power/blob/master/phosphor-regulators/docs/config_file/compare_vpd.md

Implement the execute() method of the CompareVPDAction class.  Obtain
the actual VPD keyword value from the VPD service and compare it with
the expected value.

Also improve doxygen for the execute() method of the
ComparePresenceAction class.

Signed-off-by: Shawn McCarney <shawnmm@us.ibm.com>
Change-Id: Ieb93806245babe6782fef95209bed8eed5b32578
diff --git a/phosphor-regulators/test/actions/compare_vpd_action_tests.cpp b/phosphor-regulators/test/actions/compare_vpd_action_tests.cpp
index d44e2e8..1cc7782 100644
--- a/phosphor-regulators/test/actions/compare_vpd_action_tests.cpp
+++ b/phosphor-regulators/test/actions/compare_vpd_action_tests.cpp
@@ -14,16 +14,24 @@
  * limitations under the License.
  */
 #include "action_environment.hpp"
+#include "action_error.hpp"
 #include "compare_vpd_action.hpp"
 #include "id_map.hpp"
+#include "mock_services.hpp"
+#include "mock_vpd.hpp"
 
 #include <exception>
 #include <memory>
 #include <stdexcept>
+#include <string>
 #include <utility>
 
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
+using ::testing::Return;
+using ::testing::Throw;
+
 using namespace phosphor::power::regulators;
 
 TEST(CompareVPDActionTests, Constructor)
@@ -39,7 +47,123 @@
 
 TEST(CompareVPDActionTests, Execute)
 {
-    // TODO: Not implemented yet
+    // Test where works: Actual VPD value is not an empty string
+    {
+        std::string fru{"/xyz/openbmc_project/inventory/system"};
+        std::string keyword{"Model"};
+
+        // Create MockServices object.  VPD service will return "ABCD" as VPD
+        // value 4 times.
+        MockServices services{};
+        MockVPD& vpd = services.getMockVPD();
+        EXPECT_CALL(vpd, getValue(fru, keyword))
+            .Times(4)
+            .WillRepeatedly(Return("ABCD"));
+
+        IDMap idMap{};
+        ActionEnvironment environment{idMap, "", services};
+
+        // Test where returns true: actual value == expected value
+        {
+            CompareVPDAction action{fru, keyword, "ABCD"};
+            EXPECT_TRUE(action.execute(environment));
+        }
+
+        // Test where returns false: actual value != expected value
+        {
+            CompareVPDAction action{fru, keyword, "BEEF"};
+            EXPECT_FALSE(action.execute(environment));
+        }
+
+        // Test where returns false: expected value differs by case
+        {
+            CompareVPDAction action{fru, keyword, "abcd"};
+            EXPECT_FALSE(action.execute(environment));
+        }
+
+        // Test where returns false: expected value is an empty string
+        {
+            CompareVPDAction action{fru, keyword, ""};
+            EXPECT_FALSE(action.execute(environment));
+        }
+    }
+
+    // Test where works: Actual VPD value is an empty string
+    {
+        std::string fru{"/xyz/openbmc_project/inventory/system"};
+        std::string keyword{"Model"};
+
+        // Create MockServices object.  VPD service will return "" as VPD value
+        // 2 times.
+        MockServices services{};
+        MockVPD& vpd = services.getMockVPD();
+        EXPECT_CALL(vpd, getValue(fru, keyword))
+            .Times(2)
+            .WillRepeatedly(Return(""));
+
+        IDMap idMap{};
+        ActionEnvironment environment{idMap, "", services};
+
+        // Test where returns true: actual value == expected value
+        {
+            CompareVPDAction action{fru, keyword, ""};
+            EXPECT_TRUE(action.execute(environment));
+        }
+
+        // Test where returns false: actual value != expected value
+        {
+            CompareVPDAction action{fru, keyword, "ABCD"};
+            EXPECT_FALSE(action.execute(environment));
+        }
+    }
+
+    // Test where fails: Exception thrown when trying to get actual VPD value
+    {
+        std::string fru{"/xyz/openbmc_project/inventory/system"};
+        std::string keyword{"Model"};
+
+        // Create MockServices object.  VPD service will throw an exception.
+        MockServices services{};
+        MockVPD& vpd = services.getMockVPD();
+        EXPECT_CALL(vpd, getValue(fru, keyword))
+            .Times(1)
+            .WillOnce(
+                Throw(std::runtime_error{"D-Bus error: Invalid object path"}));
+
+        IDMap idMap{};
+        ActionEnvironment environment{idMap, "", services};
+
+        try
+        {
+            CompareVPDAction action{fru, keyword, "ABCD"};
+            action.execute(environment);
+            ADD_FAILURE() << "Should not have reached this line.";
+        }
+        catch (const ActionError& e)
+        {
+            EXPECT_STREQ(e.what(), "ActionError: compare_vpd: { fru: "
+                                   "/xyz/openbmc_project/inventory/system, "
+                                   "keyword: Model, value: ABCD }");
+            try
+            {
+                // Re-throw inner exception
+                std::rethrow_if_nested(e);
+                ADD_FAILURE() << "Should not have reached this line.";
+            }
+            catch (const std::runtime_error& re)
+            {
+                EXPECT_STREQ(re.what(), "D-Bus error: Invalid object path");
+            }
+            catch (...)
+            {
+                ADD_FAILURE() << "Should not have caught exception.";
+            }
+        }
+        catch (...)
+        {
+            ADD_FAILURE() << "Should not have caught exception.";
+        }
+    }
 }
 
 TEST(CompareVPDActionTests, GetFRU)