blob: 119af8fcdd5a20139804b6d08021ecdf58b56a32 [file] [log] [blame]
Alexander Hansen3a31f0a2025-11-03 12:52:37 +01001#include "../exampledevice/example_device.hpp"
2#include "test/create_package/create_pldm_fw_package.hpp"
3
4#include <sys/mman.h>
5
6#include <phosphor-logging/lg2.hpp>
7#include <sdbusplus/asio/connection.hpp>
8#include <sdbusplus/asio/object_server.hpp>
9#include <sdbusplus/async.hpp>
10#include <sdbusplus/server.hpp>
11#include <xyz/openbmc_project/Association/Definitions/server.hpp>
12#include <xyz/openbmc_project/Software/Update/server.hpp>
13
14#include <memory>
15
16#include <gtest/gtest.h>
17
18PHOSPHOR_LOG2_USING;
19
20using namespace phosphor::software;
21using namespace phosphor::software::example_device;
22using SoftwareActivationProgress =
23 sdbusplus::aserver::xyz::openbmc_project::software::ActivationProgress<
24 Software>;
25
26class DeviceTest : public testing::Test
27{
28 protected:
29 DeviceTest() :
30 exampleUpdater(ctx, true, "vUnknown"),
31 device(exampleUpdater.getDevice())
32 {}
33 ~DeviceTest() noexcept override {}
34
35 sdbusplus::async::context ctx;
36 ExampleCodeUpdater exampleUpdater;
37 std::unique_ptr<ExampleDevice>& device;
38
39 public:
40 DeviceTest(const DeviceTest&) = delete;
41 DeviceTest(DeviceTest&&) = delete;
42 DeviceTest& operator=(const DeviceTest&) = delete;
43 DeviceTest& operator=(DeviceTest&&) = delete;
44
45 // @returns memfd
46 // @returns -1 on failure
47 static int createTestPkgMemfd();
48};
49
50int DeviceTest::createTestPkgMemfd()
51{
52 uint8_t component_image[] = {0x12, 0x34, 0x83, 0x21};
53
54 size_t sizeOut;
55 std::unique_ptr<uint8_t[]> buf = create_pldm_package_buffer(
56 component_image, sizeof(component_image),
57 std::optional<uint32_t>(exampleVendorIANA),
58 std::optional<std::string>(exampleCompatibleHardware), sizeOut);
59
60 const int fd = memfd_create("test_memfd", 0);
61
62 EXPECT_TRUE(fd >= 0);
63
64 debug("create fd {FD}", "FD", fd);
65
66 if (write(fd, (void*)buf.get(), sizeOut) == -1)
67 {
68 std::cerr << "Failed to write to memfd: " << strerror(errno)
69 << std::endl;
70 close(fd);
71 EXPECT_TRUE(false);
72 return -1;
73 }
74
75 if (lseek(fd, 0, SEEK_SET) != 0)
76 {
77 error("could not seek to the beginning of the file");
78 close(fd);
79 EXPECT_TRUE(false);
80 return -1;
81 }
82
83 return fd;
84}
85
86TEST_F(DeviceTest, TestDeviceConstructor)
87{
88 EXPECT_TRUE(device->getEMConfigType().starts_with("Nop"));
89
90 // the software version is initialized
91 EXPECT_NE(device->softwareCurrent, nullptr);
92
93 // there is no pending update
94 EXPECT_EQ(device->softwarePending, nullptr);
95}
96
97sdbusplus::async::task<> testDeviceStartUpdateCommon(
98 sdbusplus::async::context& ctx, std::unique_ptr<ExampleDevice>& device,
99 RequestedApplyTimes applyTime)
100{
101 const Software* oldSoftware = device->softwareCurrent.get();
102
103 const int fd = DeviceTest::createTestPkgMemfd();
104
105 EXPECT_TRUE(fd >= 0);
106
107 if (fd < 0)
108 {
109 co_return;
110 }
111
112 std::unique_ptr<Software> softwareUpdate =
113 std::make_unique<Software>(ctx, *device);
114
115 const Software* newSoftware = softwareUpdate.get();
116
117 co_await device->startUpdateAsync(fd, applyTime, std::move(softwareUpdate));
118
119 EXPECT_TRUE(device->deviceSpecificUpdateFunctionCalled);
120
121 if (applyTime == RequestedApplyTimes::Immediate)
122 {
123 EXPECT_NE(device->softwareCurrent.get(), oldSoftware);
124 EXPECT_EQ(device->softwareCurrent.get(), newSoftware);
125
126 EXPECT_FALSE(device->softwarePending);
127 }
128
129 if (applyTime == RequestedApplyTimes::OnReset)
130 {
131 // assert that the old software is still the running version,
132 // since the apply time is 'OnReset'
133 EXPECT_EQ(device->softwareCurrent.get(), oldSoftware);
134
135 // assert that the updated software is present
136 EXPECT_EQ(device->softwarePending.get(), newSoftware);
137 }
138
139 close(fd);
140
141 ctx.request_stop();
142
143 co_return;
144}
145
146TEST_F(DeviceTest, TestDeviceStartUpdateImmediateSuccess)
147{
148 ctx.spawn(testDeviceStartUpdateCommon(ctx, device,
149 RequestedApplyTimes::Immediate));
150 ctx.run();
151}
152
153TEST_F(DeviceTest, TestDeviceStartUpdateOnResetSuccess)
154{
155 ctx.spawn(
156 testDeviceStartUpdateCommon(ctx, device, RequestedApplyTimes::OnReset));
157 ctx.run();
158}
159
160sdbusplus::async::task<> testDeviceStartUpdateInvalidFD(
161 sdbusplus::async::context& ctx, std::unique_ptr<ExampleDevice>& device)
162{
163 std::unique_ptr<SoftwareActivationProgress> activationProgress =
164 std::make_unique<SoftwareActivationProgress>(ctx, "/");
165
166 sdbusplus::message::unix_fd image;
167 image.fd = -1;
168
169 std::unique_ptr<Software> softwareUpdate =
170 std::make_unique<Software>(ctx, *device);
171
172 co_await device->startUpdateAsync(image, RequestedApplyTimes::Immediate,
173 std::move(softwareUpdate));
174
175 // assert the bad file descriptor was caught and we did not proceed
176 EXPECT_FALSE(device->deviceSpecificUpdateFunctionCalled);
177
178 ctx.request_stop();
179
180 co_return;
181}
182
183TEST_F(DeviceTest, TestDeviceStartUpdateInvalidFD)
184{
185 ctx.spawn(testDeviceStartUpdateInvalidFD(ctx, device));
186 ctx.run();
187}
188
189sdbusplus::async::task<> testDeviceSpecificUpdateFunction(
190 sdbusplus::async::context& ctx, std::unique_ptr<ExampleDevice>& device)
191{
192 uint8_t buffer[10];
193 size_t buffer_size = 10;
194
195 auto previousVersion = device->softwareCurrent->swid;
196
197 bool success =
198 co_await device->updateDevice((const uint8_t*)buffer, buffer_size);
199
200 EXPECT_TRUE(success);
201
202 EXPECT_NE(device->softwareCurrent, nullptr);
203 EXPECT_EQ(device->softwarePending, nullptr);
204
205 ctx.request_stop();
206
207 co_return;
208}
209
210TEST_F(DeviceTest, TestDeviceSpecificUpdateFunction)
211{
212 // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
213 ctx.spawn(testDeviceSpecificUpdateFunction(ctx, device));
214 // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
215 ctx.run();
216}