[zircon/blobfs] Port FailedWrite integration test.

Adds sleep functionality to the ramdisk wrapper.

ZX-4203 #comment Port FailedWriteTest

Change-Id: I93cfec22e887ad82a4d6c93274aa595ad77f020d
diff --git a/zircon/system/ulib/blobfs/test/integration/blobfs_integration_test.cpp b/zircon/system/ulib/blobfs/test/integration/blobfs_integration_test.cpp
index 6bc0ad8..a928003 100644
--- a/zircon/system/ulib/blobfs/test/integration/blobfs_integration_test.cpp
+++ b/zircon/system/ulib/blobfs/test/integration/blobfs_integration_test.cpp
@@ -1927,23 +1927,22 @@
     END_TEST;
 }
 
-static bool TestFailedWrite(BlobfsTest* blobfsTest) {
-    BEGIN_TEST;
+*/
 
-    if (gUseRealDisk) {
-        fprintf(stderr, "Ramdisk required; skipping test\n");
-        return true;
+void RunFailedWriteTest(const RamDisk* disk) {
+    if (!disk) {
+        return;
     }
 
-    uint64_t block_size = blobfsTest->GetBlockSize();
-    ASSERT_EQ(blobfs::kBlobfsBlockSize % block_size, 0);
-    const uint64_t kDiskBlocksPerBlobfsBlock = blobfs::kBlobfsBlockSize / block_size;
+    uint32_t page_size = disk->page_size();
+    const uint32_t pages_per_block = blobfs::kBlobfsBlockSize / page_size;
 
-    fbl::unique_ptr<fs_test_utils::BlobInfo> info;
-    ASSERT_TRUE(fs_test_utils::GenerateRandomBlob(MOUNT_PATH, blobfs::kBlobfsBlockSize, &info));
+    std::unique_ptr<fs_test_utils::BlobInfo> info;
+    ASSERT_TRUE(fs_test_utils::GenerateRandomBlob(kMountPath, blobfs::kBlobfsBlockSize, &info));
 
     fbl::unique_fd fd(open(info->path, O_CREAT | O_RDWR));
     ASSERT_TRUE(fd, "Failed to create blob");
+
     // Truncate before sleeping the ramdisk. This is so potential FVM updates
     // do not interfere with the ramdisk block count.
     ASSERT_EQ(ftruncate(fd.get(), info->size_data), 0);
@@ -1956,50 +1955,47 @@
     // Non-journal:
     // - One Inode table block
     // - One Data block
-    constexpr uint64_t kBlockCountToWrite = 5;
+    constexpr int kBlockCountToWrite = 5;
+
     // Sleep after |kBlockCountToWrite - 1| blocks. This is 1 less than will be
     // needed to write out the entire blob. This ensures that writing the blob
     // will ultimately fail, but the write operation will return a successful
     // response.
-    ASSERT_TRUE(blobfsTest->ToggleSleep(kDiskBlocksPerBlobfsBlock * (kBlockCountToWrite - 1)));
+    ASSERT_OK(disk->SleepAfter(pages_per_block * (kBlockCountToWrite - 1)));
     ASSERT_EQ(write(fd.get(), info->data.get(), info->size_data),
               static_cast<ssize_t>(info->size_data));
 
     // Since the write operation ultimately failed when going out to disk,
     // syncfs will return a failed response.
     ASSERT_LT(syncfs(fd.get()), 0);
-    ASSERT_EQ(errno, EPIPE);
 
-    ASSERT_TRUE(fs_test_utils::GenerateRandomBlob(MOUNT_PATH, blobfs::kBlobfsBlockSize, &info));
+    ASSERT_TRUE(fs_test_utils::GenerateRandomBlob(kMountPath, blobfs::kBlobfsBlockSize, &info));
     fd.reset(open(info->path, O_CREAT | O_RDWR));
     ASSERT_TRUE(fd, "Failed to create blob");
 
-    if (blobfsTest->GetType() == FsTestType::kFvm) {
-        // On an FVM, truncate may either succeed or fail. If an FVM extend call is necessary,
-        // it will fail since the ramdisk is asleep; otherwise, it will pass.
-        ftruncate(fd.get(), info->size_data);
-    } else {
-        // Since truncate is done entirely in memory, a non-FVM truncate should always pass.
-        ASSERT_EQ(ftruncate(fd.get(), info->size_data), 0);
-    }
+    // On an FVM, truncate may either succeed or fail. If an FVM extend call is necessary,
+    // it will fail since the ramdisk is asleep; otherwise, it will pass.
+    ftruncate(fd.get(), info->size_data);
 
     // Since the ramdisk is asleep and our blobfs is aware of it due to the sync, write should fail.
     ASSERT_LT(write(fd.get(), info->data.get(), blobfs::kBlobfsBlockSize), 0);
-    ASSERT_EQ(errno, EPIPE);
 
-    // Wake the ramdisk and forcibly remount the BlobfsTest, forcing the journal to replay
-    // and restore blobfs to a consistent state.
-    ASSERT_TRUE(blobfsTest->ToggleSleep());
-    ASSERT_TRUE(blobfsTest->ForceRemount());
-
-    // Reset the ramdisk counts so we don't attempt to run ramdisk failure tests,
-    // since we are already failing the ramdisk within this test.
-    ASSERT_TRUE(blobfsTest->ToggleSleep());
-    ASSERT_TRUE(blobfsTest->ToggleSleep());
-    END_TEST;
+    ASSERT_OK(disk->WakeUp());
 }
 
-*/
+TEST_F(BlobfsTest, FailedWrite) {
+    ASSERT_NO_FAILURES(RunFailedWriteTest(environment_->ramdisk()));
+
+    // Force journal replay.
+    Remount();
+}
+
+TEST_F(BlobfsTestWithFvm, FailedWrite) {
+    ASSERT_NO_FAILURES(RunFailedWriteTest(environment_->ramdisk()));
+
+    // Force journal replay.
+    Remount();
+}
 
 class LargeBlobTest : public BlobfsTest {
   public:
@@ -2084,7 +2080,6 @@
 RUN_TESTS(LARGE, CreateWriteReopen)
 RUN_TEST_MEDIUM(TestCreateFailure)
 RUN_TEST_MEDIUM(TestExtendFailure)
-RUN_TESTS(SMALL, TestFailedWrite)
 END_TEST_CASE(blobfs_tests)
 
 */
diff --git a/zircon/system/ulib/blobfs/test/integration/blobfs_test.cpp b/zircon/system/ulib/blobfs/test/integration/blobfs_test.cpp
index e0d904f..d7cb07a 100644
--- a/zircon/system/ulib/blobfs/test/integration/blobfs_test.cpp
+++ b/zircon/system/ulib/blobfs/test/integration/blobfs_test.cpp
@@ -56,6 +56,9 @@
 }
 
 void BlobfsTest::TearDown() {
+    if (environment_->ramdisk()) {
+        environment_->ramdisk()->WakeUp();
+    }
     CheckInfo();  // Failures here should not prevent unmount.
     ASSERT_NO_FAILURES(Unmount());
     ASSERT_OK(CheckFs());
diff --git a/zircon/system/ulib/blobfs/test/integration/environment.cpp b/zircon/system/ulib/blobfs/test/integration/environment.cpp
index f7aa51d..3e68a3c 100644
--- a/zircon/system/ulib/blobfs/test/integration/environment.cpp
+++ b/zircon/system/ulib/blobfs/test/integration/environment.cpp
@@ -63,6 +63,18 @@
     }
 }
 
+zx_status_t RamDisk::SleepAfter(uint32_t block_count) const {
+    return ramdisk_sleep_after(ramdisk_, block_count);
+}
+
+zx_status_t RamDisk::WakeUp() const {
+    return ramdisk_wake(ramdisk_);
+}
+
+zx_status_t RamDisk::GetBlockCounts(ramdisk_block_write_counts_t* counts) const {
+    return ramdisk_get_block_counts(ramdisk_, counts);
+}
+
 void Environment::SetUp() {
     if (config_.path) {
         ASSERT_TRUE(OpenDevice(config_.path));
diff --git a/zircon/system/ulib/blobfs/test/integration/environment.h b/zircon/system/ulib/blobfs/test/integration/environment.h
index 1ae57d3..287fbe1 100644
--- a/zircon/system/ulib/blobfs/test/integration/environment.h
+++ b/zircon/system/ulib/blobfs/test/integration/environment.h
@@ -18,6 +18,12 @@
     ~RamDisk();
 
     const char* path() const { return path_.c_str(); }
+    uint32_t page_size() const { return page_size_; }
+
+    // Expose the ramdisk client functionality.
+    zx_status_t SleepAfter(uint32_t block_count) const;
+    zx_status_t WakeUp() const;
+    zx_status_t GetBlockCounts(ramdisk_block_write_counts_t* counts) const;
 
     DISALLOW_COPY_ASSIGN_AND_MOVE(RamDisk);
 
@@ -51,6 +57,8 @@
 
     const char* device_path() const { return path_.c_str(); }
 
+    const RamDisk* ramdisk() const { return ramdisk_.get(); }
+
     DISALLOW_COPY_ASSIGN_AND_MOVE(Environment);
 
   private: