[blobfs] Port 7 more integration tests to zxtest.

blobfs-integration-test --gtest_filter:

*.WriteSeekIgnored
*.RestartCreation
*.InvalidOperations
*.RootDirectory
*.PartialWrite
*.PartialWriteSleepyDisk
*.MultipleWrites

Bug: 33983

Change-Id: I915ef267a8cac75ed50a1c3389fdf926c97c1d91
diff --git a/zircon/system/ulib/blobfs/test/integration/blobfs_integration_test.cc b/zircon/system/ulib/blobfs/test/integration/blobfs_integration_test.cc
index 48fa3d2..41a0073 100644
--- a/zircon/system/ulib/blobfs/test/integration/blobfs_integration_test.cc
+++ b/zircon/system/ulib/blobfs/test/integration/blobfs_integration_test.cc
@@ -5,7 +5,13 @@
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <fuchsia/blobfs/c/fidl.h>
+#include <fuchsia/io/c/fidl.h>
+#include <lib/fzl/fdio.h>
+#include <lib/zx/vmo.h>
 #include <sys/mman.h>
+#include <utime.h>
+#include <zircon/device/vfs.h>
 
 #include <array>
 #include <atomic>
@@ -14,11 +20,6 @@
 
 #include <digest/digest.h>
 #include <fbl/auto_call.h>
-#include <fuchsia/blobfs/c/fidl.h>
-#include <fuchsia/io/c/fidl.h>
-#include <lib/fzl/fdio.h>
-#include <lib/zx/vmo.h>
-#include <zircon/device/vfs.h>
 #include <zxtest/zxtest.h>
 
 #include "blobfs_test.h"
@@ -970,220 +971,213 @@
 
 TEST_F(BlobfsTestWithFvm, WaitForRead) { RunWaitForReadTest(); }
 
+// Tests that seeks during writing are ignored.
+void RunWriteSeekIgnoredTest() {
+  srand(zxtest::Runner::GetInstance()->random_seed());
+  std::unique_ptr<fs_test_utils::BlobInfo> info;
+  ASSERT_TRUE(fs_test_utils::GenerateRandomBlob(kMountPath, 1 << 17, &info));
+  fbl::unique_fd fd(open(info->path, O_CREAT | O_RDWR));
+  ASSERT_TRUE(fd, "Failed to create blob");
+  ASSERT_EQ(0, ftruncate(fd.get(), info->size_data));
+
+  off_t seek_pos = (rand() % info->size_data);
+  ASSERT_EQ(seek_pos, lseek(fd.get(), seek_pos, SEEK_SET));
+  ASSERT_EQ(info->size_data, write(fd.get(), info->data.get(), info->size_data));
+
+  // Double check that attempting to seek early didn't cause problems...
+  ASSERT_TRUE(fs_test_utils::VerifyContents(fd.get(), info->data.get(), info->size_data));
+}
+
+TEST_F(BlobfsTest, WriteSeekIgnored) { RunWriteSeekIgnoredTest(); }
+
+TEST_F(BlobfsTestWithFvm, WriteSeekIgnored) { RunWriteSeekIgnoredTest(); }
+
+void UnlinkAndRecreate(const char* path, fbl::unique_fd* fd) {
+  ASSERT_EQ(0, unlink(path));
+  fd->reset();  // Make sure the file is gone.
+  fd->reset(open(path, O_CREAT | O_RDWR | O_EXCL));
+  ASSERT_TRUE(*fd, "Failed to recreate blob");
+}
+
+// Try unlinking while creating a blob.
+void RunRestartCreationTest() {
+  std::unique_ptr<fs_test_utils::BlobInfo> info;
+  ASSERT_TRUE(fs_test_utils::GenerateRandomBlob(kMountPath, 1 << 17, &info));
+
+  fbl::unique_fd fd(open(info->path, O_CREAT | O_RDWR));
+  ASSERT_TRUE(fd, "Failed to create blob");
+
+  // Unlink after first open.
+  ASSERT_NO_FAILURES(UnlinkAndRecreate(info->path, &fd));
+
+  // Unlink after init.
+  ASSERT_EQ(0, ftruncate(fd.get(), info->size_data));
+  ASSERT_NO_FAILURES(UnlinkAndRecreate(info->path, &fd));
+
+  // Unlink after first write.
+  ASSERT_EQ(0, ftruncate(fd.get(), info->size_data));
+  ASSERT_EQ(0, fs_test_utils::StreamAll(write, fd.get(), info->data.get(), info->size_data),
+            "Failed to write Data");
+  ASSERT_NO_FAILURES(UnlinkAndRecreate(info->path, &fd));
+}
+
+TEST_F(BlobfsTest, RestartCreation) { RunRestartCreationTest(); }
+
+TEST_F(BlobfsTestWithFvm, RestartCreation) { RunRestartCreationTest(); }
+
+// Attempt using invalid operations.
+void RunInvalidOperationsTest() {
+  // First off, make a valid blob.
+  std::unique_ptr<fs_test_utils::BlobInfo> info;
+  ASSERT_TRUE(fs_test_utils::GenerateRandomBlob(kMountPath, 1 << 12, &info));
+  fbl::unique_fd fd;
+  ASSERT_NO_FAILURES(MakeBlob(info.get(), &fd));
+  ASSERT_TRUE(fs_test_utils::VerifyContents(fd.get(), info->data.get(), info->size_data));
+
+  // Try some unsupported operations.
+  ASSERT_LT(rename(info->path, info->path), 0);
+  ASSERT_LT(truncate(info->path, 0), 0);
+  ASSERT_LT(utime(info->path, nullptr), 0);
+
+  // Test that a file cannot unmount the entire blobfs.
+  zx_status_t status;
+  fzl::FdioCaller caller(std::move(fd));
+  ASSERT_OK(fuchsia_io_DirectoryAdminUnmount(caller.borrow_channel(), &status));
+  ASSERT_EQ(ZX_ERR_ACCESS_DENIED, status);
+  fd = caller.release();
+
+  // Access the file once more, after these operations.
+  ASSERT_TRUE(fs_test_utils::VerifyContents(fd.get(), info->data.get(), info->size_data));
+}
+
+TEST_F(BlobfsTest, InvalidOperations) { RunInvalidOperationsTest(); }
+
+TEST_F(BlobfsTestWithFvm, InvalidOperations) { RunInvalidOperationsTest(); }
+
+// Attempt operations on the root directory.
+void RunRootDirectoryTest() {
+  std::string name(kMountPath);
+  name.append("/.");
+  fbl::unique_fd dirfd(open(name.c_str(), O_RDONLY));
+  ASSERT_TRUE(dirfd, "Cannot open root directory");
+
+  std::unique_ptr<fs_test_utils::BlobInfo> info;
+  ASSERT_TRUE(fs_test_utils::GenerateRandomBlob(kMountPath, 1 << 12, &info));
+
+  // Test operations which should ONLY operate on Blobs.
+  ASSERT_LT(ftruncate(dirfd.get(), info->size_data), 0);
+
+  char buf[8];
+  ASSERT_LT(write(dirfd.get(), buf, 8), 0, "Should not write to directory");
+  ASSERT_LT(read(dirfd.get(), buf, 8), 0, "Should not read from directory");
+
+  // Should NOT be able to unlink root dir.
+  ASSERT_LT(unlink(info->path), 0);
+}
+
+TEST_F(BlobfsTest, RootDirectory) { RunRootDirectoryTest(); }
+
+TEST_F(BlobfsTestWithFvm, RootDirectory) { RunRootDirectoryTest(); }
+
+void RunPartialWriteTest() {
+  std::unique_ptr<fs_test_utils::BlobInfo> info_complete;
+  std::unique_ptr<fs_test_utils::BlobInfo> info_partial;
+  size_t size = 1 << 20;
+  ASSERT_TRUE(fs_test_utils::GenerateRandomBlob(kMountPath, size, &info_complete));
+  ASSERT_TRUE(fs_test_utils::GenerateRandomBlob(kMountPath, size, &info_partial));
+
+  // Partially write out first blob.
+  fbl::unique_fd fd_partial(open(info_partial->path, O_CREAT | O_RDWR));
+  ASSERT_TRUE(fd_partial, "Failed to create blob");
+  ASSERT_EQ(0, ftruncate(fd_partial.get(), size));
+  ASSERT_EQ(0,
+            fs_test_utils::StreamAll(write, fd_partial.get(), info_partial->data.get(), size / 2),
+            "Failed to write Data");
+
+  // Completely write out second blob.
+  fbl::unique_fd fd_complete;
+  ASSERT_NO_FAILURES(MakeBlob(info_complete.get(), &fd_complete));
+}
+
+TEST_F(BlobfsTest, PartialWrite) { RunPartialWriteTest(); }
+
+TEST_F(BlobfsTestWithFvm, PartialWrite) { RunPartialWriteTest(); }
+
+void RunPartialWriteSleepyDiskTest(const RamDisk* disk) {
+  if (!disk) {
+    return;
+  }
+
+  std::unique_ptr<fs_test_utils::BlobInfo> info_complete;
+  std::unique_ptr<fs_test_utils::BlobInfo> info_partial;
+  size_t size = 1 << 20;
+  ASSERT_TRUE(fs_test_utils::GenerateRandomBlob(kMountPath, size, &info_complete));
+  ASSERT_TRUE(fs_test_utils::GenerateRandomBlob(kMountPath, size, &info_partial));
+
+  // Partially write out first blob.
+  fbl::unique_fd fd_partial(open(info_partial->path, O_CREAT | O_RDWR));
+  ASSERT_TRUE(fd_partial, "Failed to create blob");
+  ASSERT_EQ(0, ftruncate(fd_partial.get(), size));
+  ASSERT_EQ(0,
+            fs_test_utils::StreamAll(write, fd_partial.get(), info_partial->data.get(), size / 2),
+            "Failed to write Data");
+
+  // Completely write out second blob.
+  fbl::unique_fd fd_complete;
+  ASSERT_NO_FAILURES(MakeBlob(info_complete.get(), &fd_complete));
+
+  ASSERT_EQ(0, syncfs(fd_complete.get()));
+  ASSERT_OK(disk->SleepAfter(0));
+
+  fd_complete.reset(open(info_complete->path, O_RDONLY));
+  ASSERT_TRUE(fd_complete, "Failed to re-open blob");
+
+  ASSERT_EQ(0, syncfs(fd_complete.get()));
+  ASSERT_OK(disk->WakeUp());
+
+  ASSERT_TRUE(fs_test_utils::VerifyContents(fd_complete.get(), info_complete->data.get(), size));
+
+  fd_partial.reset();
+  fd_partial.reset(open(info_partial->path, O_RDONLY));
+  ASSERT_FALSE(fd_partial, "Should not be able to open invalid blob");
+}
+
+TEST_F(BlobfsTest, PartialWriteSleepyDisk) {
+  RunPartialWriteSleepyDiskTest(environment_->ramdisk());
+}
+
+TEST_F(BlobfsTestWithFvm, PartialWriteSleepyDisk) {
+  RunPartialWriteSleepyDiskTest(environment_->ramdisk());
+}
+
+void RunMultipleWritesTest() {
+  std::unique_ptr<fs_test_utils::BlobInfo> info;
+  ASSERT_TRUE(fs_test_utils::GenerateRandomBlob(kMountPath, 1 << 16, &info));
+
+  fbl::unique_fd fd(open(info->path, O_CREAT | O_RDWR));
+  ASSERT_TRUE(fd);
+
+  ASSERT_EQ(0, ftruncate(fd.get(), info->size_data));
+
+  const int kNumWrites = 128;
+  size_t write_size = info->size_data / kNumWrites;
+  for (size_t written = 0; written < info->size_data; written += write_size) {
+    ASSERT_EQ(0, fs_test_utils::StreamAll(write, fd.get(), info->data.get() + written, write_size),
+              "iteration %lu", written / write_size);
+  }
+
+  fd.reset();
+  fd.reset(open(info->path, O_RDONLY));
+  ASSERT_TRUE(fd);
+  ASSERT_TRUE(fs_test_utils::VerifyContents(fd.get(), info->data.get(), info->size_data));
+}
+
+TEST_F(BlobfsTest, MultipleWrites) { RunMultipleWritesTest(); }
+
+TEST_F(BlobfsTestWithFvm, MultipleWrites) { RunMultipleWritesTest(); }
+
 /*
 
-// Check that seeks during writing are ignored
-static bool WriteSeekIgnored(BlobfsTest* blobfsTest) {
-    BEGIN_HELPER;
-    fbl::unique_ptr<fs_test_utils::BlobInfo> info;
-    ASSERT_TRUE(fs_test_utils::GenerateRandomBlob(MOUNT_PATH, 1 << 17, &info));
-    fbl::unique_fd fd(open(info->path, O_CREAT | O_RDWR));
-    ASSERT_TRUE(fd, "Failed to create blob");
-    ASSERT_EQ(ftruncate(fd.get(), info->size_data), 0);
-
-    size_t n = 0;
-    while (n != info->size_data) {
-        off_t seek_pos = (rand() % info->size_data);
-        ASSERT_EQ(lseek(fd.get(), seek_pos, SEEK_SET), seek_pos);
-        ssize_t d = write(fd.get(), info->data.get(), info->size_data - n);
-        ASSERT_GT(d, 0, "Data Write error");
-        n += d;
-    }
-
-    // Double check that attempting to seek early didn't cause problems...
-    ASSERT_TRUE(fs_test_utils::VerifyContents(fd.get(), info->data.get(), info->size_data));
-    ASSERT_EQ(close(fd.release()), 0);
-    ASSERT_EQ(unlink(info->path), 0);
-    END_HELPER;
-}
-
-// Try unlinking at a variety of times
-static bool UnlinkTiming(BlobfsTest* blobfsTest) {
-    BEGIN_HELPER;
-    // Unlink, close fd, re-open fd as new file
-    auto full_unlink_reopen = [](fbl::unique_fd& fd, const char* path) {
-        BEGIN_HELPER;
-        ASSERT_EQ(unlink(path), 0);
-        ASSERT_EQ(close(fd.release()), 0);
-        fd.reset(open(path, O_CREAT | O_RDWR | O_EXCL));
-        ASSERT_TRUE(fd, "Failed to recreate blob");
-        END_HELPER;
-    };
-
-    fbl::unique_ptr<fs_test_utils::BlobInfo> info;
-    ASSERT_TRUE(fs_test_utils::GenerateRandomBlob(MOUNT_PATH, 1 << 17, &info));
-
-    fbl::unique_fd fd(open(info->path, O_CREAT | O_RDWR));
-    ASSERT_TRUE(fd, "Failed to create blob");
-
-    // Unlink after first open
-    ASSERT_TRUE(full_unlink_reopen(fd, info->path));
-
-    // Unlink after init
-    ASSERT_EQ(ftruncate(fd.get(), info->size_data), 0);
-    ASSERT_TRUE(full_unlink_reopen(fd, info->path));
-
-    // Unlink after first write
-    ASSERT_EQ(ftruncate(fd.get(), info->size_data), 0);
-    ASSERT_EQ(fs_test_utils::StreamAll(write, fd.get(), info->data.get(), info->size_data), 0,
-              "Failed to write Data");
-    ASSERT_TRUE(full_unlink_reopen(fd, info->path));
-    ASSERT_EQ(unlink(info->path), 0);
-    ASSERT_EQ(close(fd.release()), 0);
-    END_HELPER;
-}
-
-// Attempt using invalid operations
-static bool InvalidOps(BlobfsTest* blobfsTest) {
-    BEGIN_HELPER;
-    // First off, make a valid blob
-    fbl::unique_ptr<fs_test_utils::BlobInfo> info;
-    ASSERT_TRUE(fs_test_utils::GenerateRandomBlob(MOUNT_PATH, 1 << 12, &info));
-    fbl::unique_fd fd;
-    ASSERT_TRUE(MakeBlob(info.get(), &fd));
-    ASSERT_TRUE(fs_test_utils::VerifyContents(fd.get(), info->data.get(), info->size_data));
-
-    // Neat. Now, let's try some unsupported operations
-    ASSERT_LT(rename(info->path, info->path), 0);
-    ASSERT_LT(truncate(info->path, 0), 0);
-    ASSERT_LT(utime(info->path, nullptr), 0);
-
-    // Test that a blob fd cannot unmount the entire blobfs.
-    zx_status_t status;
-    fzl::FdioCaller caller(std::move(fd));
-    ASSERT_OK(fuchsia_io_DirectoryAdminUnmount(caller.borrow_channel(), &status));
-    ASSERT_EQ(status, ZX_ERR_ACCESS_DENIED);
-    fd.reset(caller.release().release());
-
-    // Access the file once more, after these operations
-    ASSERT_TRUE(fs_test_utils::VerifyContents(fd.get(), info->data.get(), info->size_data));
-    ASSERT_EQ(unlink(info->path), 0);
-    ASSERT_EQ(close(fd.release()), 0);
-    END_HELPER;
-}
-
-// Attempt operations on the root directory
-static bool RootDirectory(BlobfsTest* blobfsTest) {
-    BEGIN_HELPER;
-    fbl::unique_fd dirfd(open(MOUNT_PATH "/.", O_RDONLY));
-    ASSERT_TRUE(dirfd, "Cannot open root directory");
-
-    fbl::unique_ptr<fs_test_utils::BlobInfo> info;
-    ASSERT_TRUE(fs_test_utils::GenerateRandomBlob(MOUNT_PATH, 1 << 12, &info));
-
-    // Test operations which should ONLY operate on Blobs
-    ASSERT_LT(ftruncate(dirfd.get(), info->size_data), 0);
-
-    char buf[8];
-    ASSERT_LT(write(dirfd.get(), buf, 8), 0, "Should not write to directory");
-    ASSERT_LT(read(dirfd.get(), buf, 8), 0, "Should not read from directory");
-
-    // Should NOT be able to unlink root dir
-    ASSERT_EQ(close(dirfd.release()), 0);
-    ASSERT_LT(unlink(info->path), 0);
-    END_HELPER;
-}
-
-bool TestPartialWrite(BlobfsTest* blobfsTest) {
-    BEGIN_HELPER;
-    fbl::unique_ptr<fs_test_utils::BlobInfo> info_complete;
-    fbl::unique_ptr<fs_test_utils::BlobInfo> info_partial;
-    size_t size = 1 << 20;
-    ASSERT_TRUE(fs_test_utils::GenerateRandomBlob(MOUNT_PATH, size, &info_complete));
-    ASSERT_TRUE(fs_test_utils::GenerateRandomBlob(MOUNT_PATH, size, &info_partial));
-
-    // Partially write out first blob.
-    fbl::unique_fd fd_partial(open(info_partial->path, O_CREAT | O_RDWR));
-    ASSERT_TRUE(fd_partial, "Failed to create blob");
-    ASSERT_EQ(ftruncate(fd_partial.get(), size), 0);
-    ASSERT_EQ(fs_test_utils::StreamAll(write, fd_partial.get(), info_partial->data.get(), size / 2),
-0, "Failed to write Data");
-
-    // Completely write out second blob.
-    fbl::unique_fd fd_complete;
-    ASSERT_TRUE(MakeBlob(info_complete.get(), &fd_complete));
-
-    ASSERT_EQ(close(fd_complete.release()), 0);
-    ASSERT_EQ(close(fd_partial.release()), 0);
-    END_HELPER;
-}
-
-bool TestPartialWriteSleepRamdisk(BlobfsTest* blobfsTest) {
-    BEGIN_HELPER;
-    if (gUseRealDisk) {
-        fprintf(stderr, "Ramdisk required; skipping test\n");
-        return true;
-    }
-
-    fbl::unique_ptr<fs_test_utils::BlobInfo> info_complete;
-    fbl::unique_ptr<fs_test_utils::BlobInfo> info_partial;
-    size_t size = 1 << 20;
-    ASSERT_TRUE(fs_test_utils::GenerateRandomBlob(MOUNT_PATH, size, &info_complete));
-    ASSERT_TRUE(fs_test_utils::GenerateRandomBlob(MOUNT_PATH, size, &info_partial));
-
-    // Partially write out first blob.
-    fbl::unique_fd fd_partial(open(info_partial->path, O_CREAT | O_RDWR));
-    ASSERT_TRUE(fd_partial, "Failed to create blob");
-    ASSERT_EQ(ftruncate(fd_partial.get(), size), 0);
-    ASSERT_EQ(fs_test_utils::StreamAll(write, fd_partial.get(), info_partial->data.get(), size / 2),
-0, "Failed to write Data");
-
-    // Completely write out second blob.
-    fbl::unique_fd fd_complete;
-    ASSERT_TRUE(MakeBlob(info_complete.get(), &fd_complete));
-
-    ASSERT_EQ(syncfs(fd_complete.get()), 0);
-    ASSERT_TRUE(blobfsTest->ToggleSleep());
-
-    ASSERT_EQ(close(fd_complete.release()), 0);
-    ASSERT_EQ(close(fd_partial.release()), 0);
-
-    fd_complete.reset(open(info_complete->path, O_RDONLY));
-    ASSERT_TRUE(fd_complete, "Failed to re-open blob");
-
-    ASSERT_EQ(syncfs(fd_complete.get()), 0);
-    ASSERT_TRUE(blobfsTest->ToggleSleep());
-
-    ASSERT_TRUE(fs_test_utils::VerifyContents(fd_complete.get(), info_complete->data.get(), size));
-
-    fd_partial.reset(open(info_partial->path, O_RDONLY));
-    ASSERT_FALSE(fd_partial, "Should not be able to open invalid blob");
-    ASSERT_EQ(close(fd_complete.release()), 0);
-    END_HELPER;
-}
-
-bool TestAlternateWrite(BlobfsTest* blobfsTest) {
-    BEGIN_HELPER;
-    size_t num_blobs = 1;
-    size_t num_writes = 100;
-    unsigned int seed = static_cast<unsigned int>(zx_ticks_get());
-    fs_test_utils::BlobList bl(MOUNT_PATH);
-
-    for (size_t i = 0; i < num_blobs; i++) {
-        ASSERT_TRUE(bl.CreateBlob(&seed, num_writes));
-    }
-
-    for (size_t i = 0; i < num_blobs; i++) {
-        ASSERT_TRUE(bl.ConfigBlob());
-    }
-
-    for (size_t i = 0; i < num_writes; i++) {
-        for (size_t j = 0; j < num_blobs; j++) {
-            ASSERT_TRUE(bl.WriteData());
-        }
-    }
-
-    for (size_t i = 0; i < num_blobs; i++) {
-        ASSERT_TRUE(bl.ReopenBlob());
-    }
-
-    bl.VerifyAll();
-
-    bl.CloseAll();
-
-    END_HELPER;
-}
-
 static bool TestHugeBlobRandom(BlobfsTest* blobfsTest) {
     BEGIN_HELPER;
     fbl::unique_ptr<fs_test_utils::BlobInfo> info;
@@ -2049,13 +2043,6 @@
 RUN_TESTS(MEDIUM, TestDiskTooSmall)
 RUN_TESTS_SILENT(MEDIUM, CorruptedBlob)
 RUN_TESTS_SILENT(MEDIUM, CorruptedDigest)
-RUN_TESTS(MEDIUM, WriteSeekIgnored)
-RUN_TESTS(MEDIUM, UnlinkTiming)
-RUN_TESTS(MEDIUM, InvalidOps)
-RUN_TESTS(MEDIUM, RootDirectory)
-RUN_TESTS(MEDIUM, TestPartialWrite)
-RUN_TESTS(MEDIUM, TestPartialWriteSleepRamdisk)
-RUN_TESTS(MEDIUM, TestAlternateWrite)
 RUN_TESTS(LARGE, TestHugeBlobRandom)
 RUN_TESTS(LARGE, TestHugeBlobCompressible)
 RUN_TESTS(LARGE, CreateUmountRemountLarge)