1 #include "data_interface_mock.hpp" 2 #include "flags.hpp" 3 #include "status.hpp" 4 #include "tool_errors.hpp" 5 #include "updater.hpp" 6 #include "updater_mock.hpp" 7 #include "util.hpp" 8 9 #include <blobs-ipmid/blobs.hpp> 10 #include <ipmiblob/blob_errors.hpp> 11 #include <ipmiblob/test/blob_interface_mock.hpp> 12 13 #include <string> 14 #include <vector> 15 16 #include <gmock/gmock.h> 17 #include <gtest/gtest.h> 18 19 namespace host_tool 20 { 21 22 using ::testing::_; 23 using ::testing::Eq; 24 using ::testing::IsEmpty; 25 using ::testing::Return; 26 using ::testing::Throw; 27 using ::testing::TypedEq; 28 29 class UpdateHandlerTest : public ::testing::Test 30 { 31 protected: 32 const std::uint16_t session = 0xbeef; 33 34 DataInterfaceMock handlerMock; 35 ipmiblob::BlobInterfaceMock blobMock; 36 UpdateHandler updater{&blobMock, &handlerMock}; 37 }; 38 39 TEST_F(UpdateHandlerTest, CheckAvailableSuccess) 40 { 41 EXPECT_CALL(blobMock, getBlobList()) 42 .WillOnce( 43 Return(std::vector<std::string>({ipmi_flash::staticLayoutBlobId}))); 44 45 EXPECT_TRUE(updater.checkAvailable(ipmi_flash::staticLayoutBlobId)); 46 } 47 48 TEST_F(UpdateHandlerTest, CheckAvailableFailure) 49 { 50 EXPECT_CALL(blobMock, getBlobList()) 51 .WillOnce(Return(std::vector<std::string>())); 52 53 EXPECT_FALSE(updater.checkAvailable(ipmi_flash::staticLayoutBlobId)); 54 } 55 56 TEST_F(UpdateHandlerTest, SendFileSuccess) 57 { 58 /* Call sendFile to verify it does what we expect. */ 59 std::string firmwareImage = "image.bin"; 60 61 std::uint16_t supported = 62 static_cast<std::uint16_t>( 63 ipmi_flash::FirmwareFlags::UpdateFlags::lpc) | 64 static_cast<std::uint16_t>( 65 ipmi_flash::FirmwareFlags::UpdateFlags::openWrite); 66 67 EXPECT_CALL(handlerMock, supportedType()) 68 .WillOnce(Return(ipmi_flash::FirmwareFlags::UpdateFlags::lpc)); 69 70 EXPECT_CALL(blobMock, openBlob(ipmi_flash::staticLayoutBlobId, supported)) 71 .WillOnce(Return(session)); 72 73 EXPECT_CALL(handlerMock, sendContents(firmwareImage, session)) 74 .WillOnce(Return(true)); 75 76 EXPECT_CALL(blobMock, closeBlob(session)).Times(1); 77 78 updater.sendFile(ipmi_flash::staticLayoutBlobId, firmwareImage); 79 } 80 81 TEST_F(UpdateHandlerTest, SendFileExceptsOnBlobOpening) 82 { 83 std::string firmwareImage = "image.bin"; 84 85 std::uint16_t supported = 86 static_cast<std::uint16_t>( 87 ipmi_flash::FirmwareFlags::UpdateFlags::lpc) | 88 static_cast<std::uint16_t>( 89 ipmi_flash::FirmwareFlags::UpdateFlags::openWrite); 90 91 EXPECT_CALL(handlerMock, supportedType()) 92 .WillOnce(Return(ipmi_flash::FirmwareFlags::UpdateFlags::lpc)); 93 94 EXPECT_CALL(blobMock, openBlob(ipmi_flash::staticLayoutBlobId, supported)) 95 .WillOnce(Throw(ipmiblob::BlobException("asdf"))); 96 97 EXPECT_THROW( 98 updater.sendFile(ipmi_flash::staticLayoutBlobId, firmwareImage), 99 ToolException); 100 } 101 102 TEST_F(UpdateHandlerTest, SendFileHandlerFailureCausesException) 103 { 104 std::string firmwareImage = "image.bin"; 105 106 std::uint16_t supported = 107 static_cast<std::uint16_t>( 108 ipmi_flash::FirmwareFlags::UpdateFlags::lpc) | 109 static_cast<std::uint16_t>( 110 ipmi_flash::FirmwareFlags::UpdateFlags::openWrite); 111 112 EXPECT_CALL(handlerMock, supportedType()) 113 .WillOnce(Return(ipmi_flash::FirmwareFlags::UpdateFlags::lpc)); 114 115 EXPECT_CALL(blobMock, openBlob(ipmi_flash::staticLayoutBlobId, supported)) 116 .WillOnce(Return(session)); 117 118 EXPECT_CALL(handlerMock, sendContents(firmwareImage, session)) 119 .WillOnce(Return(false)); 120 121 EXPECT_CALL(blobMock, closeBlob(session)).Times(1); 122 123 EXPECT_THROW( 124 updater.sendFile(ipmi_flash::staticLayoutBlobId, firmwareImage), 125 ToolException); 126 } 127 128 TEST_F(UpdateHandlerTest, VerifyFileHandleReturnsTrueOnSuccess) 129 { 130 EXPECT_CALL(blobMock, openBlob(ipmi_flash::verifyBlobId, _)) 131 .WillOnce(Return(session)); 132 EXPECT_CALL(blobMock, commit(session, _)).WillOnce(Return()); 133 ipmiblob::StatResponse verificationResponse = {}; 134 /* the other details of the response are ignored, and should be. */ 135 verificationResponse.metadata.push_back( 136 static_cast<std::uint8_t>(ipmi_flash::ActionStatus::success)); 137 138 EXPECT_CALL(blobMock, getStat(TypedEq<std::uint16_t>(session))) 139 .WillOnce(Return(verificationResponse)); 140 EXPECT_CALL(blobMock, closeBlob(session)).WillOnce(Return()); 141 142 EXPECT_TRUE(updater.verifyFile(ipmi_flash::verifyBlobId, false)); 143 } 144 145 TEST_F(UpdateHandlerTest, VerifyFileHandleSkipsPollingIfIgnoreStatus) 146 { 147 /* if ignoreStatus, it'll skip polling for a verification result. */ 148 EXPECT_CALL(blobMock, openBlob(ipmi_flash::verifyBlobId, _)) 149 .WillOnce(Return(session)); 150 EXPECT_CALL(blobMock, commit(session, _)).WillOnce(Return()); 151 152 EXPECT_CALL(blobMock, closeBlob(session)).WillOnce(Return()); 153 154 EXPECT_TRUE(updater.verifyFile(ipmi_flash::verifyBlobId, true)); 155 } 156 157 TEST_F(UpdateHandlerTest, VerifyFileConvertsOpenBlobExceptionToToolException) 158 { 159 /* On open, it can except and this is converted to a ToolException. */ 160 EXPECT_CALL(blobMock, openBlob(ipmi_flash::verifyBlobId, _)) 161 .WillOnce(Throw(ipmiblob::BlobException("asdf"))); 162 EXPECT_THROW(updater.verifyFile(ipmi_flash::verifyBlobId, false), 163 ToolException); 164 } 165 166 TEST_F(UpdateHandlerTest, VerifyFileCommitExceptionForwards) 167 { 168 /* On commit, it can except. */ 169 EXPECT_CALL(blobMock, openBlob(ipmi_flash::verifyBlobId, _)) 170 .WillOnce(Return(session)); 171 EXPECT_CALL(blobMock, commit(session, _)) 172 .WillOnce(Throw(ipmiblob::BlobException("asdf"))); 173 EXPECT_THROW(updater.verifyFile(ipmi_flash::verifyBlobId, false), 174 ToolException); 175 } 176 177 TEST_F(UpdateHandlerTest, ReadVerisonReturnExpected) 178 { 179 /* It can return as expected, when polling and readBytes succeeds. */ 180 EXPECT_CALL(blobMock, openBlob(ipmi_flash::biosVersionBlobId, _)) 181 .WillOnce(Return(session)); 182 ipmiblob::StatResponse readVersionResponse = {}; 183 readVersionResponse.blob_state = 184 blobs::StateFlags::open_read | blobs::StateFlags::committed; 185 readVersionResponse.size = 10; 186 EXPECT_CALL(blobMock, getStat(TypedEq<std::uint16_t>(session))) 187 .WillOnce(Return(readVersionResponse)); 188 std::vector<uint8_t> resp = {0x2d, 0xfe}; 189 EXPECT_CALL(blobMock, readBytes(session, 0, _)).WillOnce(Return(resp)); 190 191 EXPECT_CALL(blobMock, closeBlob(session)).WillOnce(Return()); 192 EXPECT_EQ(resp, updater.readVersion(ipmi_flash::biosVersionBlobId)); 193 } 194 195 TEST_F(UpdateHandlerTest, ReadVersionExceptionWhenPollingSucceedsReadBytesFails) 196 { 197 /* On readBytes, it can except. */ 198 EXPECT_CALL(blobMock, openBlob(ipmi_flash::biosVersionBlobId, _)) 199 .WillOnce(Return(session)); 200 ipmiblob::StatResponse readVersionResponse = {}; 201 readVersionResponse.blob_state = 202 blobs::StateFlags::open_read | blobs::StateFlags::committed; 203 readVersionResponse.size = 10; 204 EXPECT_CALL(blobMock, getStat(TypedEq<std::uint16_t>(session))) 205 .WillOnce(Return(readVersionResponse)); 206 EXPECT_CALL(blobMock, readBytes(session, 0, _)) 207 .WillOnce(Throw(ipmiblob::BlobException("asdf"))); 208 EXPECT_CALL(blobMock, closeBlob(session)).WillOnce(Return()); 209 EXPECT_THROW(updater.readVersion(ipmi_flash::biosVersionBlobId), 210 ToolException); 211 } 212 213 TEST_F(UpdateHandlerTest, ReadVersionReturnsEmptyIfPollingFails) 214 { 215 /* It can return an empty result, when polling fails. */ 216 EXPECT_CALL(blobMock, openBlob(ipmi_flash::biosVersionBlobId, _)) 217 .WillOnce(Return(session)); 218 ipmiblob::StatResponse readVersionResponse = {}; 219 readVersionResponse.blob_state = blobs::StateFlags::commit_error; 220 EXPECT_CALL(blobMock, getStat(TypedEq<std::uint16_t>(session))) 221 .WillOnce(Return(readVersionResponse)); 222 EXPECT_CALL(blobMock, closeBlob(session)).WillOnce(Return()); 223 EXPECT_THAT(updater.readVersion(ipmi_flash::biosVersionBlobId), IsEmpty()); 224 } 225 226 TEST_F(UpdateHandlerTest, ReadVersionCovertsOpenBlobExceptionToToolException) 227 { 228 /* On open, it can except and this is converted to a ToolException. */ 229 EXPECT_CALL(blobMock, openBlob(ipmi_flash::biosVersionBlobId, _)) 230 .WillOnce(Throw(ipmiblob::BlobException("asdf"))); 231 EXPECT_THROW(updater.readVersion(ipmi_flash::biosVersionBlobId), 232 ToolException); 233 } 234 235 TEST_F(UpdateHandlerTest, CleanArtifactsSkipsCleanupIfUnableToOpen) 236 { 237 /* It only tries to commit if it's able to open the blob. However, if 238 * committing fails, this error is ignored. 239 */ 240 EXPECT_CALL(blobMock, openBlob(ipmi_flash::cleanupBlobId, _)) 241 .WillOnce(Throw(ipmiblob::BlobException("asdf"))); 242 EXPECT_CALL(blobMock, commit(_, _)).Times(0); 243 EXPECT_CALL(blobMock, closeBlob(_)).Times(0); 244 245 updater.cleanArtifacts(); 246 } 247 248 TEST_F(UpdateHandlerTest, CleanArtifactsIfOpenDoesClose) 249 { 250 /* The closeBlob call is called even if commit excepts. */ 251 std::uint16_t session = 0xa5eb; 252 EXPECT_CALL(blobMock, openBlob(ipmi_flash::cleanupBlobId, _)) 253 .WillOnce(Return(session)); 254 EXPECT_CALL(blobMock, commit(session, _)) 255 .WillOnce(Throw(ipmiblob::BlobException("asdf"))); 256 EXPECT_CALL(blobMock, closeBlob(session)); 257 258 updater.cleanArtifacts(); 259 } 260 261 TEST_F(UpdateHandlerTest, CleanArtifactsSuccessPath) 262 { 263 std::uint16_t session = 0xa5eb; 264 EXPECT_CALL(blobMock, openBlob(ipmi_flash::cleanupBlobId, _)) 265 .WillOnce(Return(session)); 266 EXPECT_CALL(blobMock, commit(session, _)); 267 EXPECT_CALL(blobMock, closeBlob(session)); 268 269 updater.cleanArtifacts(); 270 } 271 272 class UpdaterTest : public ::testing::Test 273 { 274 protected: 275 static constexpr char image[] = "image.bin"; 276 static constexpr char signature[] = "signature.bin"; 277 static constexpr char layout[] = "static"; 278 static constexpr char path[] = "/flash/static"; 279 280 ipmiblob::BlobInterfaceMock blobMock; 281 std::uint16_t session = 0xbeef; 282 bool defaultIgnore = false; 283 }; 284 285 TEST_F(UpdaterTest, UpdateMainReturnsSuccessIfAllSuccess) 286 { 287 UpdateHandlerMock handler; 288 289 EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true)); 290 EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return()); 291 EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature)) 292 .WillOnce(Return()); 293 EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore)) 294 .WillOnce(Return(true)); 295 EXPECT_CALL(handler, verifyFile(ipmi_flash::updateBlobId, defaultIgnore)) 296 .WillOnce(Return(true)); 297 EXPECT_CALL(blobMock, getBlobList()) 298 .WillOnce(Return(std::vector<std::string>({}))); 299 300 updaterMain(&handler, &blobMock, image, signature, layout, defaultIgnore); 301 } 302 303 TEST_F(UpdaterTest, UpdateMainReturnsSuccessIfAllSuccessWithDeleteActiveBlob) 304 { 305 UpdateHandlerMock handler; 306 307 EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true)); 308 EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return()); 309 EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature)) 310 .WillOnce(Return()); 311 EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore)) 312 .WillOnce(Return(true)); 313 EXPECT_CALL(handler, verifyFile(ipmi_flash::updateBlobId, defaultIgnore)) 314 .WillOnce(Return(true)); 315 EXPECT_CALL(handler, cleanArtifacts()).WillOnce(Return()); 316 EXPECT_CALL(blobMock, deleteBlob(ipmi_flash::activeImageBlobId)) 317 .WillOnce(Return()); 318 EXPECT_CALL(blobMock, getBlobList()) 319 .WillOnce(Return(std::vector<std::string>( 320 {ipmi_flash::staticLayoutBlobId, ipmi_flash::activeImageBlobId}))); 321 322 updaterMain(&handler, &blobMock, image, signature, layout, defaultIgnore); 323 } 324 325 TEST_F(UpdaterTest, UpdateMainReturnsSuccessWithIgnoreUpdate) 326 { 327 UpdateHandlerMock handler; 328 bool updateIgnore = true; 329 330 EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true)); 331 EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return()); 332 EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature)) 333 .WillOnce(Return()); 334 EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore)) 335 .WillOnce(Return(true)); 336 EXPECT_CALL(handler, verifyFile(ipmi_flash::updateBlobId, updateIgnore)) 337 .WillOnce(Return(true)); 338 EXPECT_CALL(blobMock, getBlobList()) 339 .WillOnce(Return(std::vector<std::string>({}))); 340 341 updaterMain(&handler, &blobMock, image, signature, layout, updateIgnore); 342 } 343 344 TEST_F(UpdaterTest, UpdateMainCleansUpOnFailure) 345 { 346 UpdateHandlerMock handler; 347 348 EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true)); 349 EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return()); 350 EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature)) 351 .WillOnce(Return()); 352 EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore)) 353 .WillOnce(Return(false)); 354 EXPECT_CALL(handler, cleanArtifacts()).WillOnce(Return()); 355 EXPECT_CALL(blobMock, getBlobList()) 356 .WillOnce(Return(std::vector<std::string>({}))); 357 358 EXPECT_THROW(updaterMain(&handler, &blobMock, image, signature, layout, 359 defaultIgnore), 360 ToolException); 361 } 362 363 TEST_F(UpdaterTest, UpdateMainExceptsOnUpdateBlobFailure) 364 { 365 UpdateHandlerMock handler; 366 367 EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true)); 368 EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return()); 369 EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature)) 370 .WillOnce(Return()); 371 EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore)) 372 .WillOnce(Return(true)); 373 EXPECT_CALL(handler, verifyFile(ipmi_flash::updateBlobId, defaultIgnore)) 374 .WillOnce(Return(false)); 375 EXPECT_CALL(handler, cleanArtifacts()).WillOnce(Return()); 376 EXPECT_CALL(blobMock, getBlobList()) 377 .WillOnce(Return(std::vector<std::string>({}))); 378 379 EXPECT_THROW(updaterMain(&handler, &blobMock, image, signature, layout, 380 defaultIgnore), 381 ToolException); 382 } 383 384 TEST_F(UpdaterTest, UpdateMainExceptsIfAvailableNotFound) 385 { 386 UpdateHandlerMock handler; 387 388 EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(false)); 389 390 EXPECT_THROW(updaterMain(&handler, &blobMock, image, signature, layout, 391 defaultIgnore), 392 ToolException); 393 } 394 395 } // namespace host_tool 396