text: //===--- FileSystemTest.cpp - for swift/Basic/FileSystem.h ----------------===//
  //
  // This source file is part of the Swift.org open source project
  //
  // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
  // Licensed under Apache License v2.0 with Runtime Library Exception
  //
  // See https://swift.org/LICENSE.txt for license information
  // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
  //
  //===----------------------------------------------------------------------===//
  
  #include "swift/Basic/FileSystem.h"
  #include "swift/Basic/LLVM.h"
  #include "llvm/Support/FileSystem.h"
  #include "llvm/Support/Path.h"
  #include "llvm/Support/raw_ostream.h"
  #include "gtest/gtest.h"
  
  #define ASSERT_NO_ERROR(x)                                                     \
    do if (std::error_code ASSERT_NO_ERROR_ec = x) {                             \
      llvm::errs() << #x ": did not return errc::success.\n"                     \
        << "error number: " << ASSERT_NO_ERROR_ec.value() << "\n"                \
        << "error message: " << ASSERT_NO_ERROR_ec.message() << "\n";            \
      FAIL();                                                                    \
    } while (0)
  
  using namespace llvm::sys;
  using namespace swift;
  
  namespace {
  TEST(FileSystem, MoveFileIfDifferentEmpty) {
    // Create unique temporary directory for these tests
    llvm::SmallString<128> dirPath;
    ASSERT_NO_ERROR(fs::createUniqueDirectory("FileSystem-test", dirPath));
  
    // Test 1: Move empty over nonexistent.
    llvm::SmallString<128> sourceFile = dirPath;
    path::append(sourceFile, "source.txt");
    {
      std::error_code error;
      llvm::raw_fd_ostream emptyOut(sourceFile, error, fs::OF_None);
      ASSERT_NO_ERROR(error);
    }
  
    fs::UniqueID origID;
    ASSERT_NO_ERROR(fs::getUniqueID(sourceFile, origID));
  
    llvm::SmallString<128> destFile = dirPath;
    path::append(destFile, "dest.txt");
    ASSERT_FALSE(fs::exists(destFile));
  
    ASSERT_NO_ERROR(moveFileIfDifferent(sourceFile, destFile));
    ASSERT_TRUE(fs::exists(destFile));
    ASSERT_FALSE(fs::exists(sourceFile));
  
    fs::UniqueID destID;
    ASSERT_NO_ERROR(fs::getUniqueID(destFile, destID));
    ASSERT_EQ(origID, destID);
  
    ASSERT_NO_ERROR(fs::rename(destFile, sourceFile));
  
    // Test 2: Move empty over empty.
    {
      std::error_code error;
      llvm::raw_fd_ostream emptyOut(destFile, error, fs::OF_None);
      ASSERT_NO_ERROR(error);
    }
  
    fs::UniqueID newID;
    ASSERT_NO_ERROR(fs::getUniqueID(destFile, newID));
    ASSERT_NE(origID, newID);
  
    ASSERT_NO_ERROR(moveFileIfDifferent(sourceFile, destFile));
    ASSERT_TRUE(fs::exists(destFile));
    ASSERT_FALSE(fs::exists(sourceFile));
  
    ASSERT_NO_ERROR(fs::getUniqueID(destFile, destID));
    ASSERT_EQ(newID, destID);
  
    ASSERT_NO_ERROR(fs::rename(destFile, sourceFile));
    origID = destID;
  
    // Test 3: Move empty over non-empty.
    {
      std::error_code error;
      llvm::raw_fd_ostream nonEmptyOut(destFile, error, fs::OF_None);
      ASSERT_NO_ERROR(error);
      nonEmptyOut << "a";
    }
  
    ASSERT_NO_ERROR(fs::getUniqueID(destFile, newID));
    ASSERT_NE(origID, newID);
  
    ASSERT_NO_ERROR(moveFileIfDifferent(sourceFile, destFile));
    ASSERT_TRUE(fs::exists(destFile));
    ASSERT_FALSE(fs::exists(sourceFile));
  
    ASSERT_NO_ERROR(fs::getUniqueID(destFile, destID));
    ASSERT_EQ(origID, destID);
  
    ASSERT_NO_ERROR(fs::rename(destFile, sourceFile));
  
    // Test 4: Move empty over self.
    ASSERT_NO_ERROR(moveFileIfDifferent(sourceFile, sourceFile));
    ASSERT_TRUE(fs::exists(sourceFile));
  
    ASSERT_NO_ERROR(fs::getUniqueID(sourceFile, newID));
    ASSERT_EQ(origID, newID);
  
    // Clean up.
    ASSERT_NO_ERROR(fs::remove(sourceFile, false));
    ASSERT_NO_ERROR(fs::remove(dirPath, false));
  }
  
  TEST(FileSystem, MoveFileIfDifferentNonEmpty) {
    // Create unique temporary directory for these tests
    llvm::SmallString<128> dirPath;
    ASSERT_NO_ERROR(fs::createUniqueDirectory("FileSystem-test", dirPath));
  
    // Test 1: Move source over nonexistent.
    llvm::SmallString<128> sourceFile = dirPath;
    path::append(sourceFile, "source.txt");
    {
      std::error_code error;
      llvm::raw_fd_ostream sourceOut(sourceFile, error, fs::OF_None);
      sourceOut << "original";
      ASSERT_NO_ERROR(error);
    }
  
    fs::UniqueID origID;
    ASSERT_NO_ERROR(fs::getUniqueID(sourceFile, origID));
  
    llvm::SmallString<128> destFile = dirPath;
    path::append(destFile, "dest.txt");
    ASSERT_FALSE(fs::exists(destFile));
  
    ASSERT_NO_ERROR(moveFileIfDifferent(sourceFile, destFile));
    ASSERT_TRUE(fs::exists(destFile));
    ASSERT_FALSE(fs::exists(sourceFile));
  
    fs::UniqueID destID;
    ASSERT_NO_ERROR(fs::getUniqueID(destFile, destID));
    ASSERT_EQ(origID, destID);
  
    ASSERT_NO_ERROR(fs::rename(destFile, sourceFile));
  
    // Test 2: Move source over empty.
    {
      std::error_code error;
      llvm::raw_fd_ostream emptyOut(destFile, error, fs::OF_None);
      ASSERT_NO_ERROR(error);
    }
  
    fs::UniqueID newID;
    ASSERT_NO_ERROR(fs::getUniqueID(destFile, newID));
    ASSERT_NE(origID, newID);
  
    ASSERT_NO_ERROR(moveFileIfDifferent(sourceFile, destFile));
    ASSERT_TRUE(fs::exists(destFile));
    ASSERT_FALSE(fs::exists(sourceFile));
  
    ASSERT_NO_ERROR(fs::getUniqueID(destFile, destID));
    ASSERT_EQ(origID, destID);
  
    ASSERT_NO_ERROR(fs::rename(destFile, sourceFile));
  
    // Test 3: Move source over non-empty-but-different.
    {
      std::error_code error;
      llvm::raw_fd_ostream nonEmptyOut(destFile, error, fs::OF_None);
      ASSERT_NO_ERROR(error);
      nonEmptyOut << "different";
    }
  
    ASSERT_NO_ERROR(fs::getUniqueID(destFile, newID));
    ASSERT_NE(origID, newID);
  
    ASSERT_NO_ERROR(moveFileIfDifferent(sourceFile, destFile));
    ASSERT_TRUE(fs::exists(destFile));
    ASSERT_FALSE(fs::exists(sourceFile));
  
    ASSERT_NO_ERROR(fs::getUniqueID(destFile, destID));
    ASSERT_EQ(origID, destID);
  
    ASSERT_NO_ERROR(fs::rename(destFile, sourceFile));
  
    // Test 4: Move source over identical.
    {
      std::error_code error;
      llvm::raw_fd_ostream nonEmptyOut(destFile, error, fs::OF_None);
      ASSERT_NO_ERROR(error);
      nonEmptyOut << "original";
    }
  
    ASSERT_NO_ERROR(fs::getUniqueID(destFile, newID));
    ASSERT_NE(origID, newID);
  
    ASSERT_NO_ERROR(moveFileIfDifferent(sourceFile, destFile));
    ASSERT_TRUE(fs::exists(destFile));
    ASSERT_FALSE(fs::exists(sourceFile));
  
    ASSERT_NO_ERROR(fs::getUniqueID(destFile, destID));
    ASSERT_EQ(newID, destID);
  
    ASSERT_NO_ERROR(fs::rename(destFile, sourceFile));
    origID = newID;
  
    // Test 5: Move source over self.
    ASSERT_NO_ERROR(moveFileIfDifferent(sourceFile, sourceFile));
    ASSERT_TRUE(fs::exists(sourceFile));
  
    ASSERT_NO_ERROR(fs::getUniqueID(sourceFile, newID));
    ASSERT_EQ(origID, newID);
  
    // Clean up.
    ASSERT_NO_ERROR(fs::remove(sourceFile, false));
    ASSERT_NO_ERROR(fs::remove(dirPath, false));
  }
  
  TEST(FileSystem, MoveFileIfDifferentNonExistent) {
    // Create unique temporary directory for these tests
    llvm::SmallString<128> dirPath;
    ASSERT_NO_ERROR(fs::createUniqueDirectory("FileSystem-test", dirPath));
  
    llvm::SmallString<128> sourceFile = dirPath;
    path::append(sourceFile, "source.txt");
    llvm::SmallString<128> destFile = dirPath;
    path::append(destFile, "dest.txt");
  
    // Test 1: Nonexistent -> nonexistent.
    ASSERT_TRUE((bool)moveFileIfDifferent(sourceFile, destFile));
  
    {
      std::error_code error;
      llvm::raw_fd_ostream emptyOut(destFile, error, fs::OF_None);
      ASSERT_NO_ERROR(error);
    }
  
    // Test 2: Nonexistent -> present.
    ASSERT_TRUE((bool)moveFileIfDifferent(sourceFile, destFile));
  
    // Clean up.
    ASSERT_NO_ERROR(fs::remove(destFile));
    ASSERT_NO_ERROR(fs::remove(dirPath));
  }
  
  TEST(FileSystem, MoveFileIfDifferentInvalid) {
    // Create unique temporary directory for these tests
    llvm::SmallString<128> dirPath;
    ASSERT_NO_ERROR(fs::createUniqueDirectory("FileSystem-test", dirPath));
  
    llvm::SmallString<128> sourceFile = dirPath;
    path::append(sourceFile, "source.txt");
    {
      std::error_code error;
      llvm::raw_fd_ostream emptyOut(sourceFile, error, fs::OF_None);
      ASSERT_NO_ERROR(error);
    }
  
    // Test 1: Move a file over its parent directory.
    ASSERT_TRUE((bool)moveFileIfDifferent(sourceFile, dirPath));
  
    // Test 2: Move a file into a nonexistent directory.
    llvm::SmallString<128> destFile = dirPath;
    path::append(destFile, "nonexistent", "dest.txt");
    ASSERT_TRUE((bool)moveFileIfDifferent(sourceFile, destFile));
  
    // Clean up.
    ASSERT_NO_ERROR(fs::remove(sourceFile, false));
    ASSERT_NO_ERROR(fs::remove(dirPath, false));
  }
  } // anonymous namespace
  