test: common: software

Test API of `class Software`, focus on initial state on construction.

Change-Id: I20350748d02f62cfb8371fee68d43ebbdb91609c
Signed-off-by: Alexander Hansen <alexander.hansen@9elements.com>
diff --git a/test/common/exampledevice/example_device.hpp b/test/common/exampledevice/example_device.hpp
index 67269d5..4bfbb1e 100644
--- a/test/common/exampledevice/example_device.hpp
+++ b/test/common/exampledevice/example_device.hpp
@@ -51,6 +51,7 @@
 {
   public:
     using Software::createInventoryAssociation;
+    using Software::getPurpose;
     using Software::objectPath;
     ExampleSoftware(sdbusplus::async::context& ctx, ExampleDevice& parent);
 };
diff --git a/test/common/software/meson.build b/test/common/software/meson.build
index a5ed0bf..e56441b 100644
--- a/test/common/software/meson.build
+++ b/test/common/software/meson.build
@@ -4,6 +4,7 @@
     'software_config',
     'software_association',
     'software_version',
+    'software',
 ]
 
 foreach t : testcases
diff --git a/test/common/software/software.cpp b/test/common/software/software.cpp
new file mode 100644
index 0000000..9741b3c
--- /dev/null
+++ b/test/common/software/software.cpp
@@ -0,0 +1,80 @@
+#include "../exampledevice/example_device.hpp"
+
+#include <phosphor-logging/lg2.hpp>
+#include <sdbusplus/asio/connection.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+#include <sdbusplus/async.hpp>
+#include <sdbusplus/server.hpp>
+
+#include <memory>
+#include <regex>
+
+#include <gtest/gtest.h>
+
+PHOSPHOR_LOG2_USING;
+
+using namespace phosphor::software;
+using namespace phosphor::software::example_device;
+
+class SoftwareTest : public testing::Test
+{
+  protected:
+    SoftwareTest() :
+        exampleUpdater(ctx, true, nullptr), device(exampleUpdater.getDevice())
+    {}
+    ~SoftwareTest() noexcept override {}
+
+    sdbusplus::async::context ctx;
+    ExampleCodeUpdater exampleUpdater;
+    std::unique_ptr<ExampleDevice>& device;
+
+  public:
+    SoftwareTest(const SoftwareTest&) = delete;
+    SoftwareTest(SoftwareTest&&) = delete;
+    SoftwareTest& operator=(const SoftwareTest&) = delete;
+    SoftwareTest& operator=(SoftwareTest&&) = delete;
+};
+
+TEST_F(SoftwareTest, TestSoftwareConstructor)
+{
+    // the software version is currently unknown
+    EXPECT_EQ(device->softwareCurrent, nullptr);
+
+    auto sw = std::make_unique<Software>(ctx, *device);
+
+    // since that software is not an update, there is no progress
+    EXPECT_EQ(sw->softwareActivationProgress, nullptr);
+}
+
+TEST_F(SoftwareTest, TestVersionPurpose)
+{
+    auto sw = std::make_unique<ExampleSoftware>(ctx, *device);
+
+    EXPECT_EQ(sw->getPurpose(), std::nullopt);
+
+    sw->setVersion("swVersion");
+    EXPECT_EQ(sw->getPurpose(), SoftwareVersion::VersionPurpose::Unknown);
+}
+
+TEST_F(SoftwareTest, TestSoftwareId)
+{
+    auto sw = std::make_unique<Software>(ctx, *device);
+
+    std::regex re("ExampleSoftware_[0-9]+");
+    std::cmatch m;
+
+    // design: Swid = <DeviceX>_<RandomId>
+    EXPECT_TRUE(std::regex_match(sw->swid.c_str(), m, re));
+}
+
+TEST_F(SoftwareTest, TestSoftwareObjectPath)
+{
+    auto sw = std::make_unique<ExampleSoftware>(ctx, *device);
+
+    debug("{PATH}", "PATH", sw->objectPath);
+
+    // assert that the object path is as per the design
+    // design: /xyz/openbmc_project/Software/<SwId>
+    EXPECT_TRUE(std::string(sw->objectPath)
+                    .starts_with("/xyz/openbmc_project/software/"));
+}