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