fw update: tests for common code
Tests for the common code introduced in another commit.
These tests should check that the common code behaves as outlined in the
design [1]
References:
[1] https://github.com/openbmc/docs/blob/master/designs/code-update.md
Tested: unit tests pass
Change-Id: I8f12839afd47ef3403a80439af54fedcc00f10be
Signed-off-by: Alexander Hansen <alexander.hansen@9elements.com>
diff --git a/test/common/software/meson.build b/test/common/software/meson.build
new file mode 100644
index 0000000..76813c5
--- /dev/null
+++ b/test/common/software/meson.build
@@ -0,0 +1,33 @@
+
+testcases = [
+ 'test_software',
+ 'test_software_association',
+ 'test_software_version',
+ 'test_software_get_random_softwareid',
+]
+
+foreach t : testcases
+ test(
+ t,
+ executable(
+ t,
+ f'@t@.cpp',
+ include_directories: [
+ common_include,
+ '../device/',
+ ],
+ dependencies: [
+ libpldm_dep,
+ sdbusplus_dep,
+ phosphor_logging_dep,
+ gtest,
+ ],
+ link_with: [
+ libpldmutil,
+ software_common_lib,
+ libnopdevice,
+ ]
+ )
+ )
+endforeach
+
diff --git a/test/common/software/test_software.cpp b/test/common/software/test_software.cpp
new file mode 100644
index 0000000..5eae56d
--- /dev/null
+++ b/test/common/software/test_software.cpp
@@ -0,0 +1,79 @@
+#include "../nopdevice/nopdevice.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>
+
+// NOLINTBEGIN
+sdbusplus::async::task<> endContext(sdbusplus::async::context& ctx)
+// NOLINTEND
+{
+ ctx.request_stop();
+
+ co_return;
+}
+
+TEST(SoftwareTest, TestSoftwareConstructor)
+{
+ sdbusplus::async::context ctx;
+ NopCodeUpdater nopcu(ctx);
+ NopCodeUpdater* cu = &nopcu;
+
+ auto device = std::make_unique<NopDevice>(ctx, cu);
+
+ // the software version is currently unknown
+ assert(device->softwareCurrent == nullptr);
+
+ auto sw = std::make_unique<Software>(ctx, "swid_test_constructor", *device);
+
+ // since that software is not an update, there is no progress
+ assert(sw->optSoftwareActivationProgress == nullptr);
+
+ // test succeeds if we construct without any exception
+ // and can run the context
+
+ ctx.spawn(endContext(ctx));
+ ctx.run();
+}
+
+TEST(SoftwareTest, TestSoftwareId)
+{
+ sdbusplus::async::context ctx;
+ NopCodeUpdater nopcu(ctx);
+ NopCodeUpdater* cu = &nopcu;
+
+ auto device = std::make_unique<NopDevice>(ctx, cu);
+
+ // the software version is currently unknown
+ assert(device->softwareCurrent == nullptr);
+
+ auto sw = std::make_unique<Software>(ctx, "Nop_swid_test_swid", *device);
+
+ // design: Swid = <DeviceX>_<RandomId>
+ assert(sw->swid.starts_with("Nop"));
+}
+
+TEST(SoftwareTest, TestSoftwareObjectPath)
+{
+ sdbusplus::async::context ctx;
+ NopCodeUpdater nopcu(ctx);
+ NopCodeUpdater* cu = &nopcu;
+
+ auto device = std::make_unique<NopDevice>(ctx, cu);
+
+ auto sw = std::make_unique<Software>(ctx, "swid_test_obj_path", *device);
+
+ lg2::debug("{PATH}", "PATH", sw->getObjectPath());
+
+ // assert that the object path is as per the design
+ // design: /xyz/openbmc_project/Software/<SwId>
+ assert(std::string(sw->getObjectPath())
+ .starts_with("/xyz/openbmc_project/software/"));
+}
diff --git a/test/common/software/test_software_association.cpp b/test/common/software/test_software_association.cpp
new file mode 100644
index 0000000..f845af8
--- /dev/null
+++ b/test/common/software/test_software_association.cpp
@@ -0,0 +1,176 @@
+#include "../nopdevice/nopdevice.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 <xyz/openbmc_project/Association/Definitions/client.hpp>
+
+#include <memory>
+
+#include <gtest/gtest.h>
+
+// const std::string objPathInventory = "/xyz/openbmc_project/inventory/item9";
+
+// NOLINTBEGIN
+sdbusplus::async::task<>
+ testSoftwareAssociationMissing(sdbusplus::async::context& ctx)
+// NOLINTEND
+{
+ NopCodeUpdater nopcu(ctx);
+ NopCodeUpdater* cu = &nopcu;
+
+ std::string service = nopcu.setupBusName();
+
+ std::unique_ptr<NopDevice> device = std::make_unique<NopDevice>(ctx, cu);
+
+ device->softwareCurrent =
+ std::make_unique<Software>(ctx, "myswid", *device);
+
+ std::string objPathCurrentSoftware =
+ device->softwareCurrent->getObjectPath();
+
+ auto client =
+ sdbusplus::client::xyz::openbmc_project::association::Definitions<>(ctx)
+ .service(service)
+ .path(objPathCurrentSoftware);
+
+ // by default there is no association on the software
+ try
+ {
+ co_await client.associations();
+
+ assert(false);
+ }
+ catch (std::exception& e)
+ {
+ lg2::debug(e.what());
+ }
+
+ co_await device->softwareCurrent
+ ->setAssociationDefinitionsRunningActivating(false, false);
+
+ assert((co_await client.associations()).empty());
+
+ ctx.request_stop();
+
+ co_return;
+}
+
+TEST(SoftwareTest, TestSoftwareAssociationMissing)
+{
+ sdbusplus::async::context ctx;
+
+ ctx.spawn(testSoftwareAssociationMissing(ctx));
+
+ ctx.run();
+}
+
+// NOLINTBEGIN
+sdbusplus::async::task<>
+ testSoftwareAssociationRunning(sdbusplus::async::context& ctx)
+// NOLINTEND
+{
+ NopCodeUpdater nopcu(ctx);
+ NopCodeUpdater* cu = &nopcu;
+
+ std::string service = nopcu.setupBusName();
+
+ std::unique_ptr<NopDevice> device = std::make_unique<NopDevice>(ctx, cu);
+
+ device->softwareCurrent =
+ std::make_unique<Software>(ctx, "myotherswid", *device);
+
+ std::string objPathCurrentSoftware =
+ device->softwareCurrent->getObjectPath();
+
+ auto client =
+ sdbusplus::client::xyz::openbmc_project::association::Definitions<>(ctx)
+ .service(service)
+ .path(objPathCurrentSoftware);
+
+ co_await device->softwareCurrent
+ ->setAssociationDefinitionsRunningActivating(true, false);
+
+ auto res = co_await client.associations();
+
+ assert(res.size() == 1);
+
+ auto assoc = res[0];
+
+ std::string forward = std::get<0>(assoc);
+ std::string reverse = std::get<1>(assoc);
+ std::string endpoint = std::get<2>(assoc);
+
+ assert(forward == "running");
+ assert(reverse == "ran_on");
+ assert(endpoint == (co_await device->getInventoryItemObjectPath()));
+
+ ctx.request_stop();
+
+ co_return;
+}
+
+TEST(SoftwareTest, TestSoftwareAssociationRunning)
+{
+ sdbusplus::async::context ctx;
+
+ ctx.spawn(testSoftwareAssociationRunning(ctx));
+
+ ctx.run();
+}
+
+// NOLINTBEGIN
+sdbusplus::async::task<>
+ testSoftwareAssociationActivating(sdbusplus::async::context& ctx)
+// NOLINTEND
+{
+ NopCodeUpdater nopcu(ctx);
+ NopCodeUpdater* cu = &nopcu;
+
+ std::string service = nopcu.setupBusName();
+
+ std::unique_ptr<NopDevice> device = std::make_unique<NopDevice>(ctx, cu);
+
+ device->softwareCurrent =
+ std::make_unique<Software>(ctx, "newswid", *device);
+
+ std::string objPathCurrentSoftware =
+ device->softwareCurrent->getObjectPath();
+
+ auto client =
+ sdbusplus::client::xyz::openbmc_project::association::Definitions<>(ctx)
+ .service(service)
+ .path(objPathCurrentSoftware);
+
+ co_await device->softwareCurrent
+ ->setAssociationDefinitionsRunningActivating(false, true);
+
+ auto res = co_await client.associations();
+
+ assert(res.size() == 1);
+
+ auto assoc = res[0];
+
+ std::string forward = std::get<0>(assoc);
+ std::string reverse = std::get<1>(assoc);
+ std::string endpoint = std::get<2>(assoc);
+
+ assert(forward == "activating");
+ assert(reverse == "activated_on");
+ assert(endpoint == (co_await device->getInventoryItemObjectPath()));
+
+ ctx.request_stop();
+
+ co_return;
+}
+
+TEST(SoftwareTest, TestSoftwareAssociationActivating)
+{
+ sdbusplus::async::context ctx;
+
+ ctx.spawn(testSoftwareAssociationActivating(ctx));
+
+ ctx.run();
+}
diff --git a/test/common/software/test_software_get_random_softwareid.cpp b/test/common/software/test_software_get_random_softwareid.cpp
new file mode 100644
index 0000000..89b8efe
--- /dev/null
+++ b/test/common/software/test_software_get_random_softwareid.cpp
@@ -0,0 +1,43 @@
+#include "../nopdevice/nopdevice.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>
+
+class WrapSoftware : public Software
+{
+ public:
+ static std::string wrapGetRandomSoftwareId(Device& parent)
+ {
+ return Software::getRandomSoftwareId(parent);
+ };
+};
+
+TEST(SoftwareTest, TestSoftwareGetRandomSoftwareId)
+{
+ sdbusplus::async::context ctx;
+ NopCodeUpdater nopcu(ctx);
+ NopCodeUpdater* cu = &nopcu;
+
+ DeviceConfig config(0x1234, "my.example.compatible", "Nop",
+ "MB1NopComponent");
+
+ auto device = std::make_unique<NopDevice>(ctx, config, cu);
+
+ std::string swid = WrapSoftware::wrapGetRandomSoftwareId(*device);
+ lg2::debug("{SWID}", "SWID", swid);
+
+ std::regex re("[a-zA-Z0-9]+_[a-zA-Z0-9]+_[0-9]+");
+ std::cmatch m;
+
+ EXPECT_TRUE(std::regex_match(swid.c_str(), m, re));
+
+ EXPECT_TRUE(swid.starts_with("Nop_MB1NopComponent_"));
+}
diff --git a/test/common/software/test_software_version.cpp b/test/common/software/test_software_version.cpp
new file mode 100644
index 0000000..0fc216f
--- /dev/null
+++ b/test/common/software/test_software_version.cpp
@@ -0,0 +1,77 @@
+#include "../nopdevice/nopdevice.hpp"
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <unistd.h>
+
+#include <phosphor-logging/lg2.hpp>
+#include <sdbusplus/async/context.hpp>
+#include <xyz/openbmc_project/Software/Update/client.hpp>
+#include <xyz/openbmc_project/Software/Version/client.hpp>
+
+#include <cassert>
+#include <cstdlib>
+#include <cstring>
+
+#include <gtest/gtest.h>
+
+// NOLINTBEGIN
+sdbusplus::async::task<> testSoftwareVersion(sdbusplus::async::context& ctx)
+// NOLINTEND
+{
+ NopCodeUpdater nopcu(ctx);
+ NopCodeUpdater* cu = &nopcu;
+
+ const std::string service = nopcu.setupBusName();
+
+ auto device = std::make_unique<NopDevice>(ctx, cu);
+
+ device->softwareCurrent =
+ std::make_unique<Software>(ctx, "swid_sw_version", *device);
+
+ std::string objPathCurrentSoftware =
+ device->softwareCurrent->getObjectPath();
+
+ auto clientVersion =
+ sdbusplus::client::xyz::openbmc_project::software::Version<>(ctx)
+ .service(service)
+ .path(objPathCurrentSoftware);
+
+ // the version is unavailable at this point
+ try
+ {
+ co_await clientVersion.version();
+ assert(false);
+ }
+ catch (std::exception& e)
+ {
+ lg2::debug(e.what());
+ }
+
+ // now the version is available
+ {
+ device->softwareCurrent->setVersion("v12.6");
+
+ assert((co_await clientVersion.version()) == "v12.6");
+ }
+
+ // we cannot set the version twice
+ {
+ device->softwareCurrent->setVersion("v20");
+
+ assert((co_await clientVersion.version()) == "v12.6");
+ }
+
+ ctx.request_stop();
+
+ co_return;
+}
+
+TEST(SoftwareUpdate, TestSoftwareUpdate)
+{
+ sdbusplus::async::context ctx;
+
+ ctx.spawn(testSoftwareVersion(ctx));
+
+ ctx.run();
+}