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, ReadVersionReturnsErrorIfPollingFails) 214 { 215 /* It can throw an error, 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_THROW(updater.readVersion(ipmi_flash::biosVersionBlobId), 224 ToolException); 225 } 226 227 TEST_F(UpdateHandlerTest, ReadVersionCovertsOpenBlobExceptionToToolException) 228 { 229 /* On open, it can except and this is converted to a ToolException. */ 230 EXPECT_CALL(blobMock, openBlob(ipmi_flash::biosVersionBlobId, _)) 231 .WillOnce(Throw(ipmiblob::BlobException("asdf"))); 232 EXPECT_THROW(updater.readVersion(ipmi_flash::biosVersionBlobId), 233 ToolException); 234 } 235 236 TEST_F(UpdateHandlerTest, CleanArtifactsSkipsCleanupIfUnableToOpen) 237 { 238 /* It only tries to commit if it's able to open the blob. However, if 239 * committing fails, this error is ignored. 240 */ 241 EXPECT_CALL(blobMock, openBlob(ipmi_flash::cleanupBlobId, _)) 242 .WillOnce(Throw(ipmiblob::BlobException("asdf"))); 243 EXPECT_CALL(blobMock, commit(_, _)).Times(0); 244 EXPECT_CALL(blobMock, closeBlob(_)).Times(0); 245 246 updater.cleanArtifacts(); 247 } 248 249 TEST_F(UpdateHandlerTest, CleanArtifactsIfOpenDoesClose) 250 { 251 /* The closeBlob call is called even if commit excepts. */ 252 std::uint16_t session = 0xa5eb; 253 EXPECT_CALL(blobMock, openBlob(ipmi_flash::cleanupBlobId, _)) 254 .WillOnce(Return(session)); 255 EXPECT_CALL(blobMock, commit(session, _)) 256 .WillOnce(Throw(ipmiblob::BlobException("asdf"))); 257 EXPECT_CALL(blobMock, closeBlob(session)); 258 259 updater.cleanArtifacts(); 260 } 261 262 TEST_F(UpdateHandlerTest, CleanArtifactsSuccessPath) 263 { 264 std::uint16_t session = 0xa5eb; 265 EXPECT_CALL(blobMock, openBlob(ipmi_flash::cleanupBlobId, _)) 266 .WillOnce(Return(session)); 267 EXPECT_CALL(blobMock, commit(session, _)); 268 EXPECT_CALL(blobMock, closeBlob(session)); 269 270 updater.cleanArtifacts(); 271 } 272 273 class UpdaterTest : public ::testing::Test 274 { 275 protected: 276 static constexpr char image[] = "image.bin"; 277 static constexpr char signature[] = "signature.bin"; 278 static constexpr char layout[] = "static"; 279 static constexpr char path[] = "/flash/static"; 280 281 ipmiblob::BlobInterfaceMock blobMock; 282 std::uint16_t session = 0xbeef; 283 bool defaultIgnore = false; 284 }; 285 286 TEST_F(UpdaterTest, UpdateMainReturnsSuccessIfAllSuccess) 287 { 288 UpdateHandlerMock handler; 289 290 EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true)); 291 EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return()); 292 EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature)) 293 .WillOnce(Return()); 294 EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore)) 295 .WillOnce(Return(true)); 296 EXPECT_CALL(handler, verifyFile(ipmi_flash::updateBlobId, defaultIgnore)) 297 .WillOnce(Return(true)); 298 EXPECT_CALL(blobMock, getBlobList()) 299 .WillOnce(Return(std::vector<std::string>({}))); 300 301 updaterMain(&handler, &blobMock, image, signature, layout, defaultIgnore); 302 } 303 304 TEST_F(UpdaterTest, UpdateMainReturnsSuccessIfAllSuccessWithDeleteActiveBlob) 305 { 306 UpdateHandlerMock handler; 307 308 EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true)); 309 EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return()); 310 EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature)) 311 .WillOnce(Return()); 312 EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore)) 313 .WillOnce(Return(true)); 314 EXPECT_CALL(handler, verifyFile(ipmi_flash::updateBlobId, defaultIgnore)) 315 .WillOnce(Return(true)); 316 EXPECT_CALL(handler, cleanArtifacts()).WillOnce(Return()); 317 EXPECT_CALL(blobMock, deleteBlob(ipmi_flash::activeImageBlobId)) 318 .WillOnce(Return()); 319 EXPECT_CALL(blobMock, getBlobList()) 320 .WillOnce(Return(std::vector<std::string>( 321 {ipmi_flash::staticLayoutBlobId, ipmi_flash::activeImageBlobId}))); 322 323 updaterMain(&handler, &blobMock, image, signature, layout, defaultIgnore); 324 } 325 326 TEST_F(UpdaterTest, UpdateMainReturnsSuccessWithIgnoreUpdate) 327 { 328 UpdateHandlerMock handler; 329 bool updateIgnore = true; 330 331 EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true)); 332 EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return()); 333 EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature)) 334 .WillOnce(Return()); 335 EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore)) 336 .WillOnce(Return(true)); 337 EXPECT_CALL(handler, verifyFile(ipmi_flash::updateBlobId, updateIgnore)) 338 .WillOnce(Return(true)); 339 EXPECT_CALL(blobMock, getBlobList()) 340 .WillOnce(Return(std::vector<std::string>({}))); 341 342 updaterMain(&handler, &blobMock, image, signature, layout, updateIgnore); 343 } 344 345 TEST_F(UpdaterTest, UpdateMainCleansUpOnFailure) 346 { 347 UpdateHandlerMock handler; 348 349 EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true)); 350 EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return()); 351 EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature)) 352 .WillOnce(Return()); 353 EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore)) 354 .WillOnce(Return(false)); 355 EXPECT_CALL(handler, cleanArtifacts()).WillOnce(Return()); 356 EXPECT_CALL(blobMock, getBlobList()) 357 .WillOnce(Return(std::vector<std::string>({}))); 358 359 EXPECT_THROW(updaterMain(&handler, &blobMock, image, signature, layout, 360 defaultIgnore), 361 ToolException); 362 } 363 364 TEST_F(UpdaterTest, UpdateMainExceptsOnUpdateBlobFailure) 365 { 366 UpdateHandlerMock handler; 367 368 EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true)); 369 EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return()); 370 EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature)) 371 .WillOnce(Return()); 372 EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore)) 373 .WillOnce(Return(true)); 374 EXPECT_CALL(handler, verifyFile(ipmi_flash::updateBlobId, defaultIgnore)) 375 .WillOnce(Return(false)); 376 EXPECT_CALL(handler, cleanArtifacts()).WillOnce(Return()); 377 EXPECT_CALL(blobMock, getBlobList()) 378 .WillOnce(Return(std::vector<std::string>({}))); 379 380 EXPECT_THROW(updaterMain(&handler, &blobMock, image, signature, layout, 381 defaultIgnore), 382 ToolException); 383 } 384 385 TEST_F(UpdaterTest, UpdateMainExceptsIfAvailableNotFound) 386 { 387 UpdateHandlerMock handler; 388 389 EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(false)); 390 391 EXPECT_THROW(updaterMain(&handler, &blobMock, image, signature, layout, 392 defaultIgnore), 393 ToolException); 394 } 395 396 } // namespace host_tool 397