gpio-presence: add test cases for GPIO comparisons

Add test cases for the DevicePresence class, where GPIO state
is cached in a map and then decisions about device presence are
made.  Add coverage of many GPIO + cache states for the determination.

Signed-off-by: Patrick Williams <patrick@stwcx.xyz>
Change-Id: I3a11c174d6a0a8b92c446dde3e0345b195a67a9c
diff --git a/test/meson.build b/test/meson.build
index 5282b07..20b413b 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -75,3 +75,15 @@
         dependencies: [gtest, phosphor_logging_dep, sdbusplus],
     ),
 )
+
+test(
+    'test_gpio_presence_state',
+    executable(
+        'test_gpio_presence_state',
+        'test_gpio_presence_state.cpp',
+        cpp_args: test_boost_args,
+        include_directories: test_include_dir,
+        dependencies: [boost, gtest, gmock, phosphor_logging_dep, libgpio_dep],
+        link_with: gpio_presence_lib,
+    ),
+)
diff --git a/test/test_gpio_presence_state.cpp b/test/test_gpio_presence_state.cpp
new file mode 100644
index 0000000..47e81fc
--- /dev/null
+++ b/test/test_gpio_presence_state.cpp
@@ -0,0 +1,194 @@
+#include "gpio-presence/device_presence.hpp"
+#include "gpio-presence/gpio_presence_manager.hpp"
+
+#include <gpiod.hpp>
+#include <phosphor-logging/lg2.hpp>
+
+#include <gtest/gtest.h>
+
+using namespace gpio_presence;
+
+class DevicePresenceDetailedTest : public ::testing::Test
+{
+  protected:
+    DevicePresenceDetailedTest() = default;
+    ~DevicePresenceDetailedTest() noexcept override = default;
+
+    sdbusplus::async::context ctx;
+    std::unordered_map<std::string, bool> gpioState;
+};
+
+// Test DevicePresence constructor with single GPIO, active low
+TEST_F(DevicePresenceDetailedTest, ConstructorSingleGpioActiveLow)
+{
+    std::vector<std::string> gpioNames = {"GPIO1"};
+    std::vector<uint64_t> gpioValues = {0}; // Active low
+    std::string deviceName = "device1";
+
+    DevicePresence device(ctx, gpioNames, gpioValues, deviceName, gpioState);
+
+    EXPECT_EQ(device.deviceName, deviceName);
+    EXPECT_EQ(device.gpioPolarity.size(), 1);
+    EXPECT_EQ(device.gpioPolarity["GPIO1"], ACTIVE_LOW);
+}
+
+// Test DevicePresence constructor with single GPIO, active high
+TEST_F(DevicePresenceDetailedTest, ConstructorSingleGpioActiveHigh)
+{
+    std::vector<std::string> gpioNames = {"GPIO2"};
+    std::vector<uint64_t> gpioValues = {1}; // Active high
+    std::string deviceName = "device2";
+
+    DevicePresence device(ctx, gpioNames, gpioValues, deviceName, gpioState);
+
+    EXPECT_EQ(device.deviceName, deviceName);
+    EXPECT_EQ(device.gpioPolarity.size(), 1);
+    EXPECT_EQ(device.gpioPolarity["GPIO2"], ACTIVE_HIGH);
+}
+
+// Test DevicePresence constructor with multiple GPIOs with mixed polarities
+TEST_F(DevicePresenceDetailedTest, ConstructorMultipleGpiosMixedPolarities)
+{
+    std::vector<std::string> gpioNames = {"GPIO1", "GPIO2", "GPIO3"};
+    std::vector<uint64_t> gpioValues = {0, 1, 0}; // Active low, high, low
+    std::string deviceName = "device3";
+
+    DevicePresence device(ctx, gpioNames, gpioValues, deviceName, gpioState);
+
+    EXPECT_EQ(device.deviceName, deviceName);
+    EXPECT_EQ(device.gpioPolarity.size(), 3);
+    EXPECT_EQ(device.gpioPolarity["GPIO1"], ACTIVE_LOW);
+    EXPECT_EQ(device.gpioPolarity["GPIO2"], ACTIVE_HIGH);
+    EXPECT_EQ(device.gpioPolarity["GPIO3"], ACTIVE_LOW);
+}
+
+// Test DevicePresence isPresent method with active low GPIO is low (device
+// present)
+TEST_F(DevicePresenceDetailedTest, IsPresentActiveLowGpioLow)
+{
+    std::unordered_map<std::string, bool> localGpioState;
+    std::vector<std::string> gpioNames = {"GPIO1"};
+    std::vector<uint64_t> gpioValues = {0}; // Active low
+    std::string deviceName = "device1";
+
+    localGpioState["GPIO1"] = false; // GPIO is low
+
+    DevicePresence device(ctx, gpioNames, gpioValues, deviceName,
+                          localGpioState);
+    EXPECT_TRUE(device.isPresent());
+}
+
+// Test DevicePresence isPresent method with active low GPIO is high (device
+// absent)
+TEST_F(DevicePresenceDetailedTest, IsPresentActiveLowGpioHigh)
+{
+    std::unordered_map<std::string, bool> localGpioState;
+    std::vector<std::string> gpioNames = {"GPIO1"};
+    std::vector<uint64_t> gpioValues = {0}; // Active low
+    std::string deviceName = "device1";
+
+    localGpioState["GPIO1"] = true; // GPIO is high
+
+    DevicePresence device(ctx, gpioNames, gpioValues, deviceName,
+                          localGpioState);
+    EXPECT_FALSE(device.isPresent());
+}
+
+// Test DevicePresence isPresent method with active high GPIO is high (device
+// present)
+TEST_F(DevicePresenceDetailedTest, IsPresentActiveHighGpioHigh)
+{
+    std::unordered_map<std::string, bool> localGpioState;
+    std::vector<std::string> gpioNames = {"GPIO1"};
+    std::vector<uint64_t> gpioValues = {1}; // Active high
+    std::string deviceName = "device1";
+
+    localGpioState["GPIO1"] = true; // GPIO is high
+
+    DevicePresence device(ctx, gpioNames, gpioValues, deviceName,
+                          localGpioState);
+    EXPECT_TRUE(device.isPresent());
+}
+
+// Test DevicePresence isPresent method with active high GPIO is low (device
+// absent)
+TEST_F(DevicePresenceDetailedTest, IsPresentActiveHighGpioLow)
+{
+    std::unordered_map<std::string, bool> localGpioState;
+    std::vector<std::string> gpioNames = {"GPIO1"};
+    std::vector<uint64_t> gpioValues = {1}; // Active high
+    std::string deviceName = "device1";
+
+    localGpioState["GPIO1"] = false; // GPIO is low
+
+    DevicePresence device(ctx, gpioNames, gpioValues, deviceName,
+                          localGpioState);
+    EXPECT_FALSE(device.isPresent());
+}
+
+// Test DevicePresence isPresent method with multiple GPIOs all correct (device
+// present)
+TEST_F(DevicePresenceDetailedTest, IsPresentMultipleGpiosAllCorrect)
+{
+    std::unordered_map<std::string, bool> localGpioState;
+    std::vector<std::string> gpioNames = {"GPIO1", "GPIO2", "GPIO3"};
+    std::vector<uint64_t> gpioValues = {0, 1, 0}; // Active low, high, low
+    std::string deviceName = "device1";
+
+    localGpioState["GPIO1"] = false; // Active low, should be low
+    localGpioState["GPIO2"] = true;  // Active high, should be high
+    localGpioState["GPIO3"] = false; // Active low, should be low
+
+    DevicePresence device(ctx, gpioNames, gpioValues, deviceName,
+                          localGpioState);
+    EXPECT_TRUE(device.isPresent());
+}
+
+// Test DevicePresence isPresent method with multiple GPIOs one incorrect
+// (device absent)
+TEST_F(DevicePresenceDetailedTest, IsPresentMultipleGpiosOneIncorrect)
+{
+    std::unordered_map<std::string, bool> localGpioState;
+    std::vector<std::string> gpioNames = {"GPIO1", "GPIO2", "GPIO3"};
+    std::vector<uint64_t> gpioValues = {0, 1, 0}; // Active low, high, low
+    std::string deviceName = "device1";
+
+    localGpioState["GPIO1"] = false; // Active low, should be low - correct
+    localGpioState["GPIO2"] = false; // Active high, should be high - incorrect
+    localGpioState["GPIO3"] = false; // Active low, should be low - correct
+
+    DevicePresence device(ctx, gpioNames, gpioValues, deviceName,
+                          localGpioState);
+    EXPECT_FALSE(device.isPresent());
+}
+
+// Test DevicePresence isPresent method with missing GPIO state (device absent)
+TEST_F(DevicePresenceDetailedTest, IsPresentMissingGpioState)
+{
+    std::unordered_map<std::string, bool> localGpioState;
+    std::vector<std::string> gpioNames = {"GPIO1"};
+    std::vector<uint64_t> gpioValues = {0}; // Active low
+    std::string deviceName = "device1";
+
+    // localGpioState["GPIO1"] is not set - simulating missing GPIO
+
+    DevicePresence device(ctx, gpioNames, gpioValues, deviceName,
+                          localGpioState);
+    EXPECT_FALSE(device.isPresent());
+}
+
+// Test DevicePresence getObjPath method
+TEST_F(DevicePresenceDetailedTest, GetObjPathTest)
+{
+    std::vector<std::string> gpioNames = {"GPIO1"};
+    std::vector<uint64_t> gpioValues = {0};
+    std::string deviceName = "test_device";
+
+    DevicePresence device(ctx, gpioNames, gpioValues, deviceName, gpioState);
+
+    sdbusplus::message::object_path objPath = device.getObjPath();
+    std::string expectedPath =
+        "/xyz/openbmc_project/GPIODeviceDetected/" + deviceName;
+
+    EXPECT_EQ(objPath.str, expectedPath);
+}