#include "../exampledevice/example_device.hpp" #include "test/create_package/create_pldm_fw_package.hpp" #include #include #include #include #include #include #include #include #include #include PHOSPHOR_LOG2_USING; using namespace phosphor::software; using namespace phosphor::software::example_device; using SoftwareActivationProgress = sdbusplus::aserver::xyz::openbmc_project::software::ActivationProgress< Software>; class DeviceTest : public testing::Test { protected: DeviceTest() : exampleUpdater(ctx, true, "vUnknown"), device(exampleUpdater.getDevice()) {} ~DeviceTest() noexcept override {} sdbusplus::async::context ctx; ExampleCodeUpdater exampleUpdater; std::unique_ptr& device; public: DeviceTest(const DeviceTest&) = delete; DeviceTest(DeviceTest&&) = delete; DeviceTest& operator=(const DeviceTest&) = delete; DeviceTest& operator=(DeviceTest&&) = delete; // @returns memfd // @returns -1 on failure static int createTestPkgMemfd(); }; int DeviceTest::createTestPkgMemfd() { uint8_t component_image[] = {0x12, 0x34, 0x83, 0x21}; size_t sizeOut; std::unique_ptr buf = create_pldm_package_buffer( component_image, sizeof(component_image), std::optional(exampleVendorIANA), std::optional(exampleCompatibleHardware), sizeOut); const int fd = memfd_create("test_memfd", 0); EXPECT_TRUE(fd >= 0); debug("create fd {FD}", "FD", fd); if (write(fd, (void*)buf.get(), sizeOut) == -1) { std::cerr << "Failed to write to memfd: " << strerror(errno) << std::endl; close(fd); EXPECT_TRUE(false); return -1; } if (lseek(fd, 0, SEEK_SET) != 0) { error("could not seek to the beginning of the file"); close(fd); EXPECT_TRUE(false); return -1; } return fd; } TEST_F(DeviceTest, TestDeviceConstructor) { EXPECT_TRUE(device->getEMConfigType().starts_with("Nop")); // the software version is initialized EXPECT_NE(device->softwareCurrent, nullptr); // there is no pending update EXPECT_EQ(device->softwarePending, nullptr); } sdbusplus::async::task<> testDeviceStartUpdateCommon( sdbusplus::async::context& ctx, std::unique_ptr& device, RequestedApplyTimes applyTime) { const Software* oldSoftware = device->softwareCurrent.get(); const int fd = DeviceTest::createTestPkgMemfd(); EXPECT_TRUE(fd >= 0); if (fd < 0) { co_return; } std::unique_ptr softwareUpdate = std::make_unique(ctx, *device); const Software* newSoftware = softwareUpdate.get(); co_await device->startUpdateAsync(fd, applyTime, std::move(softwareUpdate)); EXPECT_TRUE(device->deviceSpecificUpdateFunctionCalled); if (applyTime == RequestedApplyTimes::Immediate) { EXPECT_NE(device->softwareCurrent.get(), oldSoftware); EXPECT_EQ(device->softwareCurrent.get(), newSoftware); EXPECT_FALSE(device->softwarePending); } if (applyTime == RequestedApplyTimes::OnReset) { // assert that the old software is still the running version, // since the apply time is 'OnReset' EXPECT_EQ(device->softwareCurrent.get(), oldSoftware); // assert that the updated software is present EXPECT_EQ(device->softwarePending.get(), newSoftware); } close(fd); ctx.request_stop(); co_return; } TEST_F(DeviceTest, TestDeviceStartUpdateImmediateSuccess) { ctx.spawn(testDeviceStartUpdateCommon(ctx, device, RequestedApplyTimes::Immediate)); ctx.run(); } TEST_F(DeviceTest, TestDeviceStartUpdateOnResetSuccess) { ctx.spawn( testDeviceStartUpdateCommon(ctx, device, RequestedApplyTimes::OnReset)); ctx.run(); } sdbusplus::async::task<> testDeviceStartUpdateInvalidFD( sdbusplus::async::context& ctx, std::unique_ptr& device) { std::unique_ptr activationProgress = std::make_unique(ctx, "/"); sdbusplus::message::unix_fd image; image.fd = -1; std::unique_ptr softwareUpdate = std::make_unique(ctx, *device); co_await device->startUpdateAsync(image, RequestedApplyTimes::Immediate, std::move(softwareUpdate)); // assert the bad file descriptor was caught and we did not proceed EXPECT_FALSE(device->deviceSpecificUpdateFunctionCalled); ctx.request_stop(); co_return; } TEST_F(DeviceTest, TestDeviceStartUpdateInvalidFD) { ctx.spawn(testDeviceStartUpdateInvalidFD(ctx, device)); ctx.run(); } sdbusplus::async::task<> testDeviceSpecificUpdateFunction( sdbusplus::async::context& ctx, std::unique_ptr& device) { uint8_t buffer[10]; size_t buffer_size = 10; auto previousVersion = device->softwareCurrent->swid; bool success = co_await device->updateDevice((const uint8_t*)buffer, buffer_size); EXPECT_TRUE(success); EXPECT_NE(device->softwareCurrent, nullptr); EXPECT_EQ(device->softwarePending, nullptr); ctx.request_stop(); co_return; } TEST_F(DeviceTest, TestDeviceSpecificUpdateFunction) { // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch) ctx.spawn(testDeviceSpecificUpdateFunction(ctx, device)); // NOLINTEND(clang-analyzer-core.uninitialized.Branch) ctx.run(); }