tools/test: add updater tests to increase coverage

Add missing unit-tests for the updater.

Signed-off-by: Patrick Venture <venture@google.com>
Change-Id: Ieb668561e6cc7285549406d830f1295039dec4c0
diff --git a/tools/test/tools_bt_unittest.cpp b/tools/test/tools_bt_unittest.cpp
index c764f54..a5fab59 100644
--- a/tools/test/tools_bt_unittest.cpp
+++ b/tools/test/tools_bt_unittest.cpp
@@ -2,10 +2,15 @@
 #include "internal_sys_mock.hpp"
 #include "progress_mock.hpp"
 
+#include <ipmiblob/blob_errors.hpp>
 #include <ipmiblob/test/blob_interface_mock.hpp>
 
 #include <cstring>
+#include <memory>
+#include <string>
+#include <vector>
 
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
 namespace host_tool
@@ -19,25 +24,38 @@
 using ::testing::Invoke;
 using ::testing::NotNull;
 using ::testing::Return;
+using ::testing::Throw;
 
-TEST(BtHandlerTest, verifySendsFileContents)
+class BtHandlerTest : public ::testing::Test
+{
+  protected:
+    static constexpr std::uint16_t session = 0xbeef;
+
+    BtHandlerTest() :
+        handler(std::make_unique<BtDataHandler>(&blobMock, &progMock, &sysMock))
+    {}
+
+    internal::InternalSysMock sysMock;
+    ipmiblob::BlobInterfaceMock blobMock;
+    ProgressMock progMock;
+    std::unique_ptr<BtDataHandler> handler;
+    const std::string filePath = "/asdf";
+};
+
+TEST_F(BtHandlerTest, verifySendsFileContents)
 {
     /* In this very basic test, we'll feed the bt handler data from the internal
      * syscall mock and catch the writes via the blob mock.
      */
-    internal::InternalSysMock sysMock;
-    ipmiblob::BlobInterfaceMock blobMock;
-    ProgressMock progMock;
-
-    BtDataHandler handler(&blobMock, &progMock, &sysMock);
-    std::string filePath = "/asdf";
     int fd = 1;
-    std::uint16_t session = 0xbeef;
     std::vector<std::uint8_t> bytes = {'1', '2', '3', '4'};
     const int fakeFileSize = 100;
 
     EXPECT_CALL(sysMock, open(Eq(filePath), _)).WillOnce(Return(fd));
     EXPECT_CALL(sysMock, getSize(Eq(filePath))).WillOnce(Return(fakeFileSize));
+
+    EXPECT_CALL(progMock, start(fakeFileSize));
+
     EXPECT_CALL(sysMock, read(fd, NotNull(), _))
         .WillOnce(Invoke([&](int fd, void* buf, std::size_t count) {
             EXPECT_TRUE(count > bytes.size());
@@ -45,11 +63,65 @@
             return bytes.size();
         }))
         .WillOnce(Return(0));
+
+    EXPECT_CALL(progMock, updateProgress(bytes.size()));
+
     EXPECT_CALL(sysMock, close(fd)).WillOnce(Return(0));
 
     EXPECT_CALL(blobMock, writeBytes(session, 0, ContainerEq(bytes)));
 
-    EXPECT_TRUE(handler.sendContents(filePath, session));
+    EXPECT_TRUE(handler->sendContents(filePath, session));
+}
+
+TEST_F(BtHandlerTest, sendContentsFailsToOpenFile)
+{
+    /* If the file doesn't exist or the permissions are wrong, the sendContents
+     * will return failure.
+     */
+    EXPECT_CALL(sysMock, open(Eq(filePath), _)).WillOnce(Return(-1));
+
+    EXPECT_FALSE(handler->sendContents(filePath, session));
+}
+
+TEST_F(BtHandlerTest, sendContentsWithEmptyFile)
+{
+    /* An empty file should return failure from the sendContents. */
+    int fd = 1;
+
+    EXPECT_CALL(sysMock, open(Eq(filePath), _)).WillOnce(Return(fd));
+    EXPECT_CALL(sysMock, getSize(Eq(filePath))).WillOnce(Return(0));
+
+    EXPECT_FALSE(handler->sendContents(filePath, session));
+}
+
+TEST_F(BtHandlerTest, sendContentsFailsWhenBlobHandlerThrows)
+{
+    /* The blob handler throws an exception which is caught, the file is closed,
+     * and false is returned.
+     */
+
+    int fd = 1;
+    std::vector<std::uint8_t> bytes = {'1', '2', '3', '4'};
+    const int fakeFileSize = 100;
+
+    EXPECT_CALL(sysMock, open(Eq(filePath), _)).WillOnce(Return(fd));
+    EXPECT_CALL(sysMock, getSize(Eq(filePath))).WillOnce(Return(fakeFileSize));
+
+    EXPECT_CALL(progMock, start(fakeFileSize));
+
+    EXPECT_CALL(sysMock, read(fd, NotNull(), _))
+        .WillOnce(Invoke([&](int fd, void* buf, std::size_t count) {
+            EXPECT_TRUE(count > bytes.size());
+            std::memcpy(buf, bytes.data(), bytes.size());
+            return bytes.size();
+        }));
+
+    EXPECT_CALL(blobMock, writeBytes(session, 0, ContainerEq(bytes)))
+        .WillOnce(Throw(ipmiblob::BlobException("failure")));
+
+    EXPECT_CALL(sysMock, close(fd)).WillOnce(Return(0));
+
+    EXPECT_FALSE(handler->sendContents(filePath, session));
 }
 
 } // namespace
diff --git a/tools/test/tools_updater_unittest.cpp b/tools/test/tools_updater_unittest.cpp
index 0e9a217..bb28ecb 100644
--- a/tools/test/tools_updater_unittest.cpp
+++ b/tools/test/tools_updater_unittest.cpp
@@ -6,10 +6,13 @@
 #include "updater_mock.hpp"
 #include "util.hpp"
 
+#include <ipmiblob/blob_errors.hpp>
 #include <ipmiblob/test/blob_interface_mock.hpp>
 
 #include <string>
+#include <vector>
 
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
 namespace host_tool
@@ -18,6 +21,7 @@
 using ::testing::_;
 using ::testing::Eq;
 using ::testing::Return;
+using ::testing::Throw;
 using ::testing::TypedEq;
 
 class UpdateHandlerTest : public ::testing::Test
@@ -39,6 +43,14 @@
     EXPECT_TRUE(updater.checkAvailable(ipmi_flash::staticLayoutBlobId));
 }
 
+TEST_F(UpdateHandlerTest, CheckAvailableFailure)
+{
+    EXPECT_CALL(blobMock, getBlobList())
+        .WillOnce(Return(std::vector<std::string>()));
+
+    EXPECT_FALSE(updater.checkAvailable(ipmi_flash::staticLayoutBlobId));
+}
+
 TEST_F(UpdateHandlerTest, SendFileSuccess)
 {
     /* Call sendFile to verify it does what we expect. */
@@ -64,6 +76,53 @@
     updater.sendFile(ipmi_flash::staticLayoutBlobId, firmwareImage);
 }
 
+TEST_F(UpdateHandlerTest, SendFileExceptsOnBlobOpening)
+{
+    std::string firmwareImage = "image.bin";
+
+    std::uint16_t supported =
+        static_cast<std::uint16_t>(
+            ipmi_flash::FirmwareFlags::UpdateFlags::lpc) |
+        static_cast<std::uint16_t>(
+            ipmi_flash::FirmwareFlags::UpdateFlags::openWrite);
+
+    EXPECT_CALL(handlerMock, supportedType())
+        .WillOnce(Return(ipmi_flash::FirmwareFlags::UpdateFlags::lpc));
+
+    EXPECT_CALL(blobMock, openBlob(ipmi_flash::staticLayoutBlobId, supported))
+        .WillOnce(Throw(ipmiblob::BlobException("asdf")));
+
+    EXPECT_THROW(
+        updater.sendFile(ipmi_flash::staticLayoutBlobId, firmwareImage),
+        ToolException);
+}
+
+TEST_F(UpdateHandlerTest, SendFileHandlerFailureCausesException)
+{
+    std::string firmwareImage = "image.bin";
+
+    std::uint16_t supported =
+        static_cast<std::uint16_t>(
+            ipmi_flash::FirmwareFlags::UpdateFlags::lpc) |
+        static_cast<std::uint16_t>(
+            ipmi_flash::FirmwareFlags::UpdateFlags::openWrite);
+
+    EXPECT_CALL(handlerMock, supportedType())
+        .WillOnce(Return(ipmi_flash::FirmwareFlags::UpdateFlags::lpc));
+
+    EXPECT_CALL(blobMock, openBlob(ipmi_flash::staticLayoutBlobId, supported))
+        .WillOnce(Return(session));
+
+    EXPECT_CALL(handlerMock, sendContents(firmwareImage, session))
+        .WillOnce(Return(false));
+
+    EXPECT_CALL(blobMock, closeBlob(session)).Times(1);
+
+    EXPECT_THROW(
+        updater.sendFile(ipmi_flash::staticLayoutBlobId, firmwareImage),
+        ToolException);
+}
+
 TEST_F(UpdateHandlerTest, VerifyFileHandleReturnsTrueOnSuccess)
 {
     EXPECT_CALL(blobMock, openBlob(ipmi_flash::verifyBlobId, _))
@@ -81,9 +140,83 @@
     EXPECT_TRUE(updater.verifyFile(ipmi_flash::verifyBlobId, false));
 }
 
+TEST_F(UpdateHandlerTest, VerifyFileHandleSkipsPollingIfIgnoreStatus)
+{
+    /* if ignoreStatus, it'll skip polling for a verification result. */
+    EXPECT_CALL(blobMock, openBlob(ipmi_flash::verifyBlobId, _))
+        .WillOnce(Return(session));
+    EXPECT_CALL(blobMock, commit(session, _)).WillOnce(Return());
+
+    EXPECT_CALL(blobMock, closeBlob(session)).WillOnce(Return());
+
+    EXPECT_TRUE(updater.verifyFile(ipmi_flash::verifyBlobId, true));
+}
+
+TEST_F(UpdateHandlerTest, VerifyFileConvertsOpenBlobExceptionToToolException)
+{
+    /* On open, it can except and this is converted to a ToolException. */
+    EXPECT_CALL(blobMock, openBlob(ipmi_flash::verifyBlobId, _))
+        .WillOnce(Throw(ipmiblob::BlobException("asdf")));
+    EXPECT_THROW(updater.verifyFile(ipmi_flash::verifyBlobId, false),
+                 ToolException);
+}
+
+TEST_F(UpdateHandlerTest, VerifyFileCommitExceptionForwards)
+{
+    /* On commit, it can except. */
+    EXPECT_CALL(blobMock, openBlob(ipmi_flash::verifyBlobId, _))
+        .WillOnce(Return(session));
+    EXPECT_CALL(blobMock, commit(session, _))
+        .WillOnce(Throw(ipmiblob::BlobException("asdf")));
+    EXPECT_THROW(updater.verifyFile(ipmi_flash::verifyBlobId, false),
+                 ToolException);
+}
+
+TEST_F(UpdateHandlerTest, CleanArtifactsSkipsCleanupIfUnableToOpen)
+{
+    /* It only tries to commit if it's able to open the blob.  However, if
+     * committing fails, this error is ignored.
+     */
+    EXPECT_CALL(blobMock, openBlob(ipmi_flash::cleanupBlobId, _))
+        .WillOnce(Throw(ipmiblob::BlobException("asdf")));
+    EXPECT_CALL(blobMock, commit(_, _)).Times(0);
+    EXPECT_CALL(blobMock, closeBlob(_)).Times(0);
+
+    updater.cleanArtifacts();
+}
+
+TEST_F(UpdateHandlerTest, CleanArtifactsIfOpenDoesClose)
+{
+    /* The closeBlob call is called even if commit excepts. */
+    std::uint16_t session = 0xa5eb;
+    EXPECT_CALL(blobMock, openBlob(ipmi_flash::cleanupBlobId, _))
+        .WillOnce(Return(session));
+    EXPECT_CALL(blobMock, commit(session, _))
+        .WillOnce(Throw(ipmiblob::BlobException("asdf")));
+    EXPECT_CALL(blobMock, closeBlob(session));
+
+    updater.cleanArtifacts();
+}
+
+TEST_F(UpdateHandlerTest, CleanArtifactsSuccessPath)
+{
+    std::uint16_t session = 0xa5eb;
+    EXPECT_CALL(blobMock, openBlob(ipmi_flash::cleanupBlobId, _))
+        .WillOnce(Return(session));
+    EXPECT_CALL(blobMock, commit(session, _));
+    EXPECT_CALL(blobMock, closeBlob(session));
+
+    updater.cleanArtifacts();
+}
+
 class UpdaterTest : public ::testing::Test
 {
   protected:
+    static constexpr char image[] = "image.bin";
+    static constexpr char signature[] = "signature.bin";
+    static constexpr char layout[] = "static";
+    static constexpr char path[] = "/flash/static";
+
     ipmiblob::BlobInterfaceMock blobMock;
     std::uint16_t session = 0xbeef;
     bool defaultIgnore = false;
@@ -91,55 +224,79 @@
 
 TEST_F(UpdaterTest, UpdateMainReturnsSuccessIfAllSuccess)
 {
-    const std::string image = "image.bin";
-    const std::string signature = "signature.bin";
     UpdateHandlerMock handler;
 
-    EXPECT_CALL(handler, checkAvailable(_)).WillOnce(Return(true));
-    EXPECT_CALL(handler, sendFile(_, image)).WillOnce(Return());
-    EXPECT_CALL(handler, sendFile(_, signature)).WillOnce(Return());
+    EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true));
+    EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return());
+    EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature))
+        .WillOnce(Return());
     EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore))
         .WillOnce(Return(true));
     EXPECT_CALL(handler, verifyFile(ipmi_flash::updateBlobId, defaultIgnore))
         .WillOnce(Return(true));
 
-    updaterMain(&handler, image, signature, "static", defaultIgnore);
+    updaterMain(&handler, image, signature, layout, defaultIgnore);
 }
 
 TEST_F(UpdaterTest, UpdateMainReturnsSuccessWithIgnoreUpdate)
 {
-    const std::string image = "image.bin";
-    const std::string signature = "signature.bin";
     UpdateHandlerMock handler;
     bool updateIgnore = true;
 
-    EXPECT_CALL(handler, checkAvailable(_)).WillOnce(Return(true));
-    EXPECT_CALL(handler, sendFile(_, image)).WillOnce(Return());
-    EXPECT_CALL(handler, sendFile(_, signature)).WillOnce(Return());
+    EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true));
+    EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return());
+    EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature))
+        .WillOnce(Return());
     EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore))
         .WillOnce(Return(true));
     EXPECT_CALL(handler, verifyFile(ipmi_flash::updateBlobId, updateIgnore))
         .WillOnce(Return(true));
 
-    updaterMain(&handler, image, signature, "static", updateIgnore);
+    updaterMain(&handler, image, signature, layout, updateIgnore);
 }
 
 TEST_F(UpdaterTest, UpdateMainCleansUpOnFailure)
 {
-    const std::string image = "image.bin";
-    const std::string signature = "signature.bin";
     UpdateHandlerMock handler;
 
-    EXPECT_CALL(handler, checkAvailable(_)).WillOnce(Return(true));
-    EXPECT_CALL(handler, sendFile(_, image)).WillOnce(Return());
-    EXPECT_CALL(handler, sendFile(_, signature)).WillOnce(Return());
+    EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true));
+    EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return());
+    EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature))
+        .WillOnce(Return());
     EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore))
         .WillOnce(Return(false));
     EXPECT_CALL(handler, cleanArtifacts()).WillOnce(Return());
 
-    EXPECT_THROW(
-        updaterMain(&handler, image, signature, "static", defaultIgnore),
-        ToolException);
+    EXPECT_THROW(updaterMain(&handler, image, signature, layout, defaultIgnore),
+                 ToolException);
+}
+
+TEST_F(UpdaterTest, UpdateMainExceptsOnUpdateBlobFailure)
+{
+    UpdateHandlerMock handler;
+
+    EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true));
+    EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return());
+    EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature))
+        .WillOnce(Return());
+    EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore))
+        .WillOnce(Return(true));
+    EXPECT_CALL(handler, verifyFile(ipmi_flash::updateBlobId, defaultIgnore))
+        .WillOnce(Return(false));
+    EXPECT_CALL(handler, cleanArtifacts()).WillOnce(Return());
+
+    EXPECT_THROW(updaterMain(&handler, image, signature, layout, defaultIgnore),
+                 ToolException);
+}
+
+TEST_F(UpdaterTest, UpdateMainExceptsIfAvailableNotFound)
+{
+    UpdateHandlerMock handler;
+
+    EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(false));
+
+    EXPECT_THROW(updaterMain(&handler, image, signature, layout, defaultIgnore),
+                 ToolException);
 }
 
 } // namespace host_tool