Fix Short read/write issue

It was possible for "short" reads and writes to cause the pattern and
zero steps to not work correctly. This change adds logic to deal with
the short reads.

Tested: unit test and machine test
root@bmc# time busctl call xyz.openbmc_project.eStoraged.mmcblk0 \
> /xyz/openbmc_project/inventory/storage/mmcblk0 \
> xyz.openbmc_project.Inventory.Item.Volume Erase s \
> xyz.openbmc_project.Inventory.Item.Volume.EraseMethod.ZeroOverWrite \
> --timeout=1200
real    6m0.815s
user    0m0.010s
sys     0m0.010s

Change-Id: If8df9bdba159a3bcfa77104a4c17b8d352794db2
Signed-off-by: John Edward Broadbent <jebr@google.com>
diff --git a/include/pattern.hpp b/include/pattern.hpp
index 691cb16..e8c61ee 100644
--- a/include/pattern.hpp
+++ b/include/pattern.hpp
@@ -6,12 +6,13 @@
 #include <stdplus/fd/create.hpp>
 #include <stdplus/fd/managed.hpp>
 
+#include <chrono>
 #include <span>
 #include <string>
 
 namespace estoraged
 {
-using stdplus::fd::ManagedFd;
+using stdplus::fd::Fd;
 
 class Pattern : public Erase
 {
@@ -23,29 +24,48 @@
     Pattern(std::string_view inDevPath) : Erase(inDevPath)
     {}
 
+    /** @brief writes an uncompressible random pattern to the drive, using
+     * default parameters. It also throws errors accordingly.
+     */
+    void writePattern()
+    {
+        stdplus::fd::Fd&& fd =
+            stdplus::fd::open(devPath, stdplus::fd::OpenAccess::WriteOnly);
+        writePattern(util::findSizeOfBlockDevice(devPath), fd);
+    }
+
     /** @brief writes an uncompressible random pattern to the drive
      * and throws errors accordingly.
      *
      *  @param[in] bytes - Size of the block device
+     *  @param[in] fd - the stdplus file descriptor
      */
-    void writePattern()
-    {
-        writePattern(util::findSizeOfBlockDevice(devPath));
-    }
+    void writePattern(uint64_t driveSize, Fd& fd);
 
-    void writePattern(uint64_t driveSize);
+    /** @brief verifies the uncompressible random pattern is on the drive, using
+     * default parameters. It also throws errors accordingly.
+     */
+    void verifyPattern()
+    {
+        stdplus::fd::Fd&& fd =
+            stdplus::fd::open(devPath, stdplus::fd::OpenAccess::ReadOnly);
+        verifyPattern(util::findSizeOfBlockDevice(devPath), fd);
+    }
 
     /** @brief verifies the uncompressible random pattern is on the drive
      * and throws errors accordingly.
      *
      *  @param[in] bytes - Size of the block device
+     *  @param[in] fd - the stdplus file descriptor
      */
+    void verifyPattern(uint64_t driveSize, Fd& fd);
 
-    void verifyPattern()
-    {
-        verifyPattern(util::findSizeOfBlockDevice(devPath));
-    }
-    void verifyPattern(uint64_t driveSize);
+  private:
+    static constexpr uint32_t seed = 0x6a656272;
+    static constexpr size_t blockSize = 4096;
+    static constexpr size_t blockSizeUsing32 = blockSize / sizeof(uint32_t);
+    static constexpr size_t maxRetry = 32;
+    static constexpr std::chrono::duration delay = std::chrono::milliseconds(1);
 };
 
 } // namespace estoraged
diff --git a/include/zero.hpp b/include/zero.hpp
index 562b8ce..bfa240d 100644
--- a/include/zero.hpp
+++ b/include/zero.hpp
@@ -6,10 +6,12 @@
 #include <stdplus/fd/create.hpp>
 #include <stdplus/fd/managed.hpp>
 
+#include <chrono>
+
 namespace estoraged
 {
 
-using stdplus::fd::ManagedFd;
+using stdplus::fd::Fd;
 
 class Zero : public Erase
 {
@@ -23,29 +25,35 @@
     /** @brief writes zero to the drive
      * and throws errors accordingly.
      *  @param[in] driveSize - the size of the block device in bytes
+     *  @param[in] fd - the stdplus file descriptor
      */
-    void writeZero(uint64_t driveSize);
+    void writeZero(uint64_t driveSize, Fd& fd);
 
-    /** @brief writes zero to the drive
+    /** @brief writes zero to the drive using default parameters,
      * and throws errors accordingly.
      */
     void writeZero()
     {
-        writeZero(util::findSizeOfBlockDevice(devPath));
+        stdplus::fd::Fd&& fd =
+            stdplus::fd::open(devPath, stdplus::fd::OpenAccess::WriteOnly);
+        writeZero(util::findSizeOfBlockDevice(devPath), fd);
     }
 
-    /** @brief verifies the  uncompressible random pattern is on the drive
+    /** @brief verifies the drive has only zeros on it,
      * and throws errors accordingly.
      *  @param[in] driveSize - the size of the block device in bytes
+     *  @param[in] fd - the stdplus file descriptor
      */
-    void verifyZero(uint64_t driveSize);
+    void verifyZero(uint64_t driveSize, Fd& fd);
 
-    /** @brief verifies the  uncompressible random pattern is on the drive
-     * and throws errors accordingly.
+    /** @brief verifies the drive has only zeros on it,
+     * using the default parameters. It also throws errors accordingly.
      */
     void verifyZero()
     {
-        verifyZero(util::findSizeOfBlockDevice(devPath));
+        stdplus::fd::Fd&& fd =
+            stdplus::fd::open(devPath, stdplus::fd::OpenAccess::ReadOnly);
+        verifyZero(util::findSizeOfBlockDevice(devPath), fd);
     }
 
   private:
@@ -53,6 +61,8 @@
      * 32768 was also tested. It had almost identical performance.
      */
     static constexpr size_t blockSize = 4096;
+    static constexpr size_t maxRetry = 32;
+    static constexpr std::chrono::duration delay = std::chrono::milliseconds(1);
 };
 
 } // namespace estoraged
diff --git a/src/erase/pattern.cpp b/src/erase/pattern.cpp
index 5563661..7d7990b 100644
--- a/src/erase/pattern.cpp
+++ b/src/erase/pattern.cpp
@@ -10,22 +10,20 @@
 #include <xyz/openbmc_project/Common/error.hpp>
 
 #include <array>
+#include <chrono>
 #include <iostream>
 #include <random>
 #include <span>
 #include <string>
+#include <thread>
 
 namespace estoraged
 {
 
-constexpr uint32_t seed = 0x6a656272;
-constexpr size_t blockSize = 4096;
-constexpr size_t blockSizeUsing32 = blockSize / sizeof(uint32_t);
-
 using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
-using stdplus::fd::ManagedFd;
+using stdplus::fd::Fd;
 
-void Pattern::writePattern(const uint64_t driveSize)
+void Pattern::writePattern(const uint64_t driveSize, Fd& fd)
 {
     // static seed defines a fixed prng sequnce so it can be verified later,
     // and validated for entropy
@@ -36,9 +34,6 @@
     std::minstd_rand0 generator(seed);
     std::array<std::byte, blockSize> randArr{};
 
-    ManagedFd fd =
-        stdplus::fd::open(devPath, stdplus::fd::OpenAccess::WriteOnly);
-
     while (currentIndex < driveSize)
     {
         // generate a 4k block of prng
@@ -52,19 +47,34 @@
         size_t writeSize = currentIndex + blockSize < driveSize
                                ? blockSize
                                : driveSize - currentIndex;
-        if (fd.write({randArr.data(), writeSize}).size() != writeSize)
+        size_t written = 0;
+        size_t retry = 0;
+        while (written < writeSize)
         {
-            lg2::error("Estoraged erase pattern unable to write sizeof(long)",
-                       "REDFISH_MESSAGE_ID",
-                       std::string("eStorageD.1.0.EraseFailure"),
-                       "REDFISH_MESSAGE_ARGS", std::to_string(fd.get()));
-            throw InternalFailure();
+            written += fd.write({randArr.data() + written, writeSize - written})
+                           .size();
+            if (written == writeSize)
+            {
+                break;
+            }
+            if (written > writeSize)
+            {
+                throw InternalFailure();
+            }
+            retry++;
+            if (retry > maxRetry)
+            {
+                lg2::error("Unable to do full write", "REDFISH_MESSAGE_ID",
+                           std::string("eStorageD.1.0.EraseFailure"));
+                throw InternalFailure();
+            }
+            std::this_thread::sleep_for(delay);
         }
         currentIndex = currentIndex + writeSize;
     }
 }
 
-void Pattern::verifyPattern(const uint64_t driveSize)
+void Pattern::verifyPattern(const uint64_t driveSize, Fd& fd)
 {
 
     uint64_t currentIndex = 0;
@@ -74,9 +84,6 @@
     std::array<std::byte, blockSize> randArr{};
     std::array<std::byte, blockSize> readArr{};
 
-    ManagedFd fd =
-        stdplus::fd::open(devPath, stdplus::fd::OpenAccess::ReadOnly);
-
     while (currentIndex < driveSize)
     {
         size_t readSize = currentIndex + blockSize < driveSize
@@ -91,14 +98,35 @@
             {
                 (*randArrFill)[i] = generator();
             }
-            fd.read({readArr.data(), readSize});
+            size_t read = 0;
+            size_t retry = 0;
+            while (read < readSize)
+            {
+                read +=
+                    fd.read({readArr.data() + read, readSize - read}).size();
+                if (read == readSize)
+                {
+                    break;
+                }
+                if (read > readSize)
+                {
+                    throw InternalFailure();
+                }
+                retry++;
+                if (retry > maxRetry)
+                {
+                    lg2::error("Unable to do full read", "REDFISH_MESSAGE_ID",
+                               std::string("eStorageD.1.0.EraseFailure"));
+                    throw InternalFailure();
+                }
+                std::this_thread::sleep_for(delay);
+            }
         }
         catch (...)
         {
             lg2::error("Estoraged erase pattern unable to read",
                        "REDFISH_MESSAGE_ID",
-                       std::string("eStorageD.1.0.EraseFailure"),
-                       "REDFISH_MESSAGE_ARGS", std::to_string(fd.get()));
+                       std::string("eStorageD.1.0.EraseFailure"));
             throw InternalFailure();
         }
 
diff --git a/src/erase/zero.cpp b/src/erase/zero.cpp
index 2c5a6f5..d26e1b9 100644
--- a/src/erase/zero.cpp
+++ b/src/erase/zero.cpp
@@ -2,26 +2,26 @@
 
 #include "erase.hpp"
 
+#include <unistd.h>
+
 #include <phosphor-logging/lg2.hpp>
 #include <stdplus/fd/create.hpp>
 #include <stdplus/fd/managed.hpp>
 #include <xyz/openbmc_project/Common/error.hpp>
 
 #include <array>
+#include <chrono>
 #include <span>
+#include <thread>
 
 namespace estoraged
 {
 
 using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
-using stdplus::fd::ManagedFd;
+using stdplus::fd::Fd;
 
-void Zero::writeZero(const uint64_t driveSize)
+void Zero::writeZero(const uint64_t driveSize, Fd& fd)
 {
-
-    ManagedFd fd =
-        stdplus::fd::open(devPath, stdplus::fd::OpenAccess::WriteOnly);
-
     uint64_t currentIndex = 0;
     const std::array<const std::byte, blockSize> blockOfZeros{};
 
@@ -32,7 +32,31 @@
                                  : driveSize - currentIndex;
         try
         {
-            fd.write({blockOfZeros.data(), writeSize});
+            size_t written = 0;
+            size_t retry = 0;
+            while (written < writeSize)
+            {
+                written += fd.write({blockOfZeros.data() + written,
+                                     writeSize - written})
+                               .size();
+                if (written == writeSize)
+                {
+                    break;
+                }
+                if (written > writeSize)
+                {
+                    throw InternalFailure();
+                }
+                retry++;
+                if (retry > maxRetry)
+                {
+                    lg2::error("Unable to make full write",
+                               "REDFISH_MESSAGE_ID",
+                               std::string("eStorageD.1.0.EraseFailure"));
+                    throw InternalFailure();
+                }
+                std::this_thread::sleep_for(delay);
+            }
         }
         catch (...)
         {
@@ -45,11 +69,8 @@
     }
 }
 
-void Zero::verifyZero(uint64_t driveSize)
+void Zero::verifyZero(uint64_t driveSize, Fd& fd)
 {
-    ManagedFd fd =
-        stdplus::fd::open(devPath, stdplus::fd::OpenAccess::ReadOnly);
-
     uint64_t currentIndex = 0;
     std::array<std::byte, blockSize> readArr{};
     const std::array<const std::byte, blockSize> blockOfZeros{};
@@ -61,7 +82,29 @@
                                 : driveSize - currentIndex;
         try
         {
-            fd.read({readArr.data(), readSize});
+            size_t read = 0;
+            size_t retry = 0;
+            while (read < readSize)
+            {
+                read +=
+                    fd.read({readArr.data() + read, readSize - read}).size();
+                if (read == readSize)
+                {
+                    break;
+                }
+                if (read > readSize)
+                {
+                    throw InternalFailure();
+                }
+                retry++;
+                if (retry > maxRetry)
+                {
+                    lg2::error("Unable to make full read", "REDFISH_MESSAGE_ID",
+                               std::string("eStorageD.1.0.EraseFailure"));
+                    throw InternalFailure();
+                }
+                std::this_thread::sleep_for(delay);
+            }
         }
         catch (...)
         {
diff --git a/src/test/erase/pattern_test.cpp b/src/test/erase/pattern_test.cpp
index e0192a0..2e2ac81 100644
--- a/src/test/erase/pattern_test.cpp
+++ b/src/test/erase/pattern_test.cpp
@@ -5,6 +5,7 @@
 #include <unistd.h>
 
 #include <stdplus/fd/create.hpp>
+#include <stdplus/fd/gmock.hpp>
 #include <stdplus/fd/managed.hpp>
 #include <xyz/openbmc_project/Common/error.hpp>
 
@@ -20,6 +21,9 @@
 
 using estoraged::Pattern;
 using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
+using testing::_;
+using testing::Invoke;
+using testing::Return;
 
 TEST(pattern, patternPass)
 {
@@ -30,14 +34,22 @@
                   std::ios::out | std::ios::binary | std::ios::trunc);
     testFile.close();
 
+    stdplus::fd::Fd&& writeFd =
+        stdplus::fd::open(testFileName, stdplus::fd::OpenAccess::WriteOnly);
+
     Pattern pass(testFileName);
-    EXPECT_NO_THROW(pass.writePattern(size));
-    EXPECT_NO_THROW(pass.verifyPattern(size));
+    EXPECT_NO_THROW(pass.writePattern(size, writeFd));
+
+    stdplus::fd::Fd&& readFd =
+        stdplus::fd::open(testFileName, stdplus::fd::OpenAccess::ReadOnly);
+
+    EXPECT_NO_THROW(pass.verifyPattern(size, readFd));
 }
 
 /* This test that pattern writes the correct number of bytes even if
  * size of the drive is not divisable by the block size
  */
+
 TEST(pattern, patternNotDivisible)
 {
     std::string testFileName = "notDivisible";
@@ -48,9 +60,14 @@
                   std::ios::out | std::ios::binary | std::ios::trunc);
     testFile.close();
 
+    stdplus::fd::Fd&& writeFd =
+        stdplus::fd::open(testFileName, stdplus::fd::OpenAccess::WriteOnly);
     Pattern pass(testFileName);
-    EXPECT_NO_THROW(pass.writePattern(size));
-    EXPECT_NO_THROW(pass.verifyPattern(size));
+    EXPECT_NO_THROW(pass.writePattern(size, writeFd));
+
+    stdplus::fd::Fd&& readFd =
+        stdplus::fd::open(testFileName, stdplus::fd::OpenAccess::ReadOnly);
+    EXPECT_NO_THROW(pass.verifyPattern(size, readFd));
 }
 
 TEST(pattern, patternsDontMatch)
@@ -67,8 +84,140 @@
                    sizeof(dummyValue));
     testFile.close();
 
-    EXPECT_NO_THROW(pass.writePattern(size - sizeof(dummyValue)));
-    EXPECT_THROW(pass.verifyPattern(size), InternalFailure);
+    stdplus::fd::Fd&& writeFd =
+        stdplus::fd::open(testFileName, stdplus::fd::OpenAccess::WriteOnly);
+
+    EXPECT_NO_THROW(pass.writePattern(size - sizeof(dummyValue), writeFd));
+
+    stdplus::fd::Fd&& readFd =
+        stdplus::fd::open(testFileName, stdplus::fd::OpenAccess::ReadOnly);
+
+    EXPECT_THROW(pass.verifyPattern(size, readFd), InternalFailure);
+}
+
+uint64_t size = 4096;
+size_t shortSize = 128;
+static auto shortData1 = std::vector<std::byte>(shortSize);
+static auto shortData2 = std::vector<std::byte>(shortSize);
+static auto restOfData = std::vector<std::byte>(size - shortSize * 2);
+
+TEST(pattern, shortReadWritePass)
+{
+    std::string testFileName = "testfile_shortRead";
+
+    uint64_t size = 4096;
+    Pattern pass(testFileName);
+    stdplus::fd::FdMock mock;
+
+    testing::Sequence s;
+
+    // test write pattern with short blocks
+    EXPECT_CALL(mock, write(_))
+        .WillOnce(Invoke([](std::span<const std::byte> x) {
+            std::copy_n(x.begin(), shortData1.size(), shortData1.begin());
+            return shortData1;
+        }))
+        .RetiresOnSaturation();
+    EXPECT_CALL(mock, write(_))
+        .WillOnce(Invoke([](std::span<const std::byte> x) {
+            std::copy_n(x.begin(), restOfData.size(), restOfData.begin());
+            return restOfData;
+        }))
+        .RetiresOnSaturation();
+    EXPECT_CALL(mock, write(_))
+        .WillOnce(Invoke([](std::span<const std::byte> x) {
+            std::copy_n(x.begin(), shortData2.size(), shortData2.begin());
+            return shortData2;
+        }))
+        .RetiresOnSaturation();
+
+    // test read pattern
+    EXPECT_CALL(mock, read(_))
+        .WillOnce(Invoke([](nonstd::span<std::byte> x) {
+            std::copy_n(shortData1.begin(), shortData1.size(), x.data());
+            nonstd::span ret(shortData1);
+            return ret;
+        }))
+        .RetiresOnSaturation();
+
+    EXPECT_CALL(mock, read(_))
+        .WillOnce(Invoke([](nonstd::span<std::byte> x) {
+            std::copy_n(restOfData.begin(), restOfData.size(), x.data());
+            nonstd::span ret(restOfData);
+            return ret;
+        }))
+        .RetiresOnSaturation();
+
+    EXPECT_CALL(mock, read(_))
+        .WillOnce(Invoke([](nonstd::span<std::byte> x) {
+            std::copy_n(shortData2.begin(), shortData2.size(), x.data());
+            nonstd::span ret(shortData2);
+            return ret;
+        }))
+        .RetiresOnSaturation();
+
+    EXPECT_NO_THROW(pass.writePattern(size, mock));
+    EXPECT_NO_THROW(pass.verifyPattern(size, mock));
+}
+
+TEST(Zeros, shortReadWriteFail)
+{
+    std::string testFileName = "testfile_shortRead";
+
+    uint64_t size = 4096;
+    size_t shortSize = 128;
+    Pattern tryPattern(testFileName);
+    auto shortData = std::vector<std::byte>(shortSize, std::byte{0});
+    auto restOfData =
+        std::vector<std::byte>(size - shortSize * 3, std::byte{0});
+    // open the file and write none to it
+
+    stdplus::fd::FdMock mock;
+
+    // test writes
+    EXPECT_CALL(mock, write(_))
+        .WillOnce(Return(shortData))
+        .WillOnce(Return(shortData))
+        .WillOnce(Return(restOfData))
+        .WillOnce(Return(restOfData)); // return too much data!
+
+    EXPECT_THROW(tryPattern.writePattern(size, mock), InternalFailure);
+
+    // test reads
+    EXPECT_CALL(mock, read(_))
+        .WillOnce(Return(shortData))
+        .WillOnce(Return(shortData))
+        .WillOnce(Return(restOfData))
+        .WillOnce(Return(restOfData)); // return too much data!
+
+    EXPECT_THROW(tryPattern.verifyPattern(size, mock), InternalFailure);
+}
+
+TEST(pattern, driveIsSmaller)
+{
+    std::string testFileName = "testfile_driveIsSmaller";
+
+    uint64_t size = 4096;
+    Pattern tryPattern(testFileName);
+
+    stdplus::fd::FdMock mocks;
+    testing::InSequence s;
+
+    // test writes
+    EXPECT_CALL(mocks, write(_))
+        .Times(33)
+        .WillRepeatedly(Return(std::vector<std::byte>{}))
+        .RetiresOnSaturation();
+
+    EXPECT_THROW(tryPattern.writePattern(size, mocks), InternalFailure);
+
+    // test reads
+    EXPECT_CALL(mocks, read(_))
+        .Times(33)
+        .WillRepeatedly(Return(std::vector<std::byte>{}))
+        .RetiresOnSaturation();
+
+    EXPECT_THROW(tryPattern.verifyPattern(size, mocks), InternalFailure);
 }
 
 } // namespace estoraged_test
diff --git a/src/test/erase/zero_test.cpp b/src/test/erase/zero_test.cpp
index 846059d..b14d546 100644
--- a/src/test/erase/zero_test.cpp
+++ b/src/test/erase/zero_test.cpp
@@ -5,6 +5,7 @@
 #include <unistd.h>
 
 #include <stdplus/fd/create.hpp>
+#include <stdplus/fd/gmock.hpp>
 #include <stdplus/fd/managed.hpp>
 #include <xyz/openbmc_project/Common/error.hpp>
 
@@ -15,12 +16,14 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
-using estoraged::Zero;
-using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
-
 namespace estoraged_test
 {
 
+using estoraged::Zero;
+using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
+using testing::_;
+using testing::Return;
+
 TEST(Zeros, zeroPass)
 {
     std::string testFileName = "testfile_pass";
@@ -31,8 +34,12 @@
     testFile.close();
     uint64_t size = 4096;
     Zero pass(testFileName);
-    EXPECT_NO_THROW(pass.writeZero(size));
-    EXPECT_NO_THROW(pass.verifyZero(size));
+    stdplus::fd::Fd&& write =
+        stdplus::fd::open(testFileName, stdplus::fd::OpenAccess::ReadWrite);
+    EXPECT_NO_THROW(pass.writeZero(size, write));
+    stdplus::fd::Fd&& read =
+        stdplus::fd::open(testFileName, stdplus::fd::OpenAccess::ReadWrite);
+    EXPECT_NO_THROW(pass.verifyZero(size, read));
 }
 
 /* This test that pattern writes the correct number of bytes even if
@@ -49,9 +56,14 @@
 
     uint64_t size = 4097;
     // 4097 is not divisible by the block size, and we expect no errors
+
+    stdplus::fd::Fd&& write =
+        stdplus::fd::open(testFileName, stdplus::fd::OpenAccess::ReadWrite);
     Zero pass(testFileName);
-    EXPECT_NO_THROW(pass.writeZero(size));
-    EXPECT_NO_THROW(pass.verifyZero(size));
+    EXPECT_NO_THROW(pass.writeZero(size, write));
+    stdplus::fd::Fd&& read =
+        stdplus::fd::open(testFileName, stdplus::fd::OpenAccess::ReadWrite);
+    EXPECT_NO_THROW(pass.verifyZero(size, read));
 }
 
 TEST(Zeros, notZeroStart)
@@ -66,15 +78,18 @@
                    sizeof(dummyValue));
     testFile.close();
     uint64_t size = 4096;
+    stdplus::fd::Fd&& readWrite =
+        stdplus::fd::open(testFileName, stdplus::fd::OpenAccess::ReadWrite);
+
     Zero pass(testFileName);
-    EXPECT_NO_THROW(pass.writeZero(size - sizeof(dummyValue)));
+    EXPECT_NO_THROW(pass.writeZero(size - sizeof(dummyValue), readWrite));
 
     // force flush, needed for unit testing
     std::ofstream file;
     file.open(testFileName);
     file.close();
 
-    EXPECT_THROW(pass.verifyZero(size), InternalFailure);
+    EXPECT_THROW(pass.verifyZero(size, readWrite), InternalFailure);
 }
 
 TEST(Zeros, notZeroEnd)
@@ -89,14 +104,106 @@
     uint64_t size = 4096;
     Zero pass(testFileName);
     uint32_t dummyValue = 88;
-    EXPECT_NO_THROW(pass.writeZero(size - sizeof(dummyValue)));
+    stdplus::fd::Fd&& write =
+        stdplus::fd::open(testFileName, stdplus::fd::OpenAccess::ReadWrite);
+    EXPECT_NO_THROW(pass.writeZero(size - sizeof(dummyValue), write));
 
     // open the file and write none zero to it
     testFile.open(testFileName, std::ios::out);
     testFile << dummyValue;
     testFile.close();
 
-    EXPECT_THROW(pass.verifyZero(size), InternalFailure);
+    stdplus::fd::Fd&& read =
+        stdplus::fd::open(testFileName, stdplus::fd::OpenAccess::ReadWrite);
+    EXPECT_THROW(pass.verifyZero(size, read), InternalFailure);
+}
+
+TEST(Zeros, shortReadWritePass)
+{
+    std::string testFileName = "testfile_shortRead";
+
+    uint64_t size = 4096;
+    size_t shortSize = 128;
+    Zero pass(testFileName);
+    auto shortData = std::vector<std::byte>(shortSize, std::byte{0});
+    auto restOfData =
+        std::vector<std::byte>(size - shortSize * 3, std::byte{0});
+    stdplus::fd::FdMock mock;
+
+    // test write zeros with short blocks
+    EXPECT_CALL(mock, write(_))
+        .WillOnce(Return(shortData))
+        .WillOnce(Return(shortData))
+        .WillOnce(Return(restOfData))
+        .WillOnce(Return(shortData));
+
+    EXPECT_NO_THROW(pass.writeZero(size, mock));
+
+    // test read zeros with short blocks
+    EXPECT_CALL(mock, read(_))
+        .WillOnce(Return(shortData))
+        .WillOnce(Return(shortData))
+        .WillOnce(Return(restOfData))
+        .WillOnce(Return(shortData));
+
+    EXPECT_NO_THROW(pass.verifyZero(size, mock));
+}
+
+TEST(Zeros, shortReadWriteFail)
+{
+    std::string testFileName = "testfile_shortRead";
+
+    uint64_t size = 4096;
+    size_t shortSize = 128;
+    Zero tryZero(testFileName);
+    auto shortData = std::vector<std::byte>(shortSize, std::byte{0});
+    auto restOfData =
+        std::vector<std::byte>(size - shortSize * 3, std::byte{0});
+    // open the file and write none zero to it
+
+    stdplus::fd::FdMock mock;
+
+    // test writes
+    EXPECT_CALL(mock, write(_))
+        .WillOnce(Return(shortData))
+        .WillOnce(Return(shortData))
+        .WillOnce(Return(restOfData))
+        .WillOnce(Return(restOfData)); // return too much data!
+
+    EXPECT_THROW(tryZero.writeZero(size, mock), InternalFailure);
+
+    // test reads
+    EXPECT_CALL(mock, read(_))
+        .WillOnce(Return(shortData))
+        .WillOnce(Return(shortData))
+        .WillOnce(Return(restOfData))
+        .WillOnce(Return(restOfData)); // return too much data!
+
+    EXPECT_THROW(tryZero.verifyZero(size, mock), InternalFailure);
+}
+
+TEST(Zeros, driveIsSmaller)
+{
+    std::string testFileName = "testfile_driveIsSmaller";
+
+    uint64_t size = 4096;
+    Zero tryZero(testFileName);
+
+    stdplus::fd::FdMock mocks;
+    testing::InSequence s;
+
+    // test writes
+    EXPECT_CALL(mocks, write(_))
+        .Times(33)
+        .WillRepeatedly(Return(std::vector<std::byte>{}))
+        .RetiresOnSaturation();
+    EXPECT_THROW(tryZero.writeZero(size, mocks), InternalFailure);
+    // test reads
+    EXPECT_CALL(mocks, read(_))
+        .Times(33)
+        .WillRepeatedly(Return(std::vector<std::byte>{}))
+        .RetiresOnSaturation();
+    EXPECT_THROW(tryZero.verifyZero(size, mocks), InternalFailure);
 }
 
 } // namespace estoraged_test