1//===- llvm/unittest/Support/ReplaceFileTest.cpp - unit tests -------------===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9 10#include "llvm/Support/Errc.h" 11#include "llvm/Support/ErrorHandling.h" 12#include "llvm/Support/FileSystem.h" 13#include "llvm/Support/MemoryBuffer.h" 14#include "llvm/Support/Path.h" 15#include "llvm/Support/Process.h" 16#include "gtest/gtest.h" 17 18using namespace llvm; 19using namespace llvm::sys; 20 21#define ASSERT_NO_ERROR(x) \ 22 do { \ 23 if (std::error_code ASSERT_NO_ERROR_ec = x) { \ 24 errs() << #x ": did not return errc::success.\n" \ 25 << "error number: " << ASSERT_NO_ERROR_ec.value() << "\n" \ 26 << "error message: " << ASSERT_NO_ERROR_ec.message() << "\n"; \ 27 } \ 28 } while (false) 29 30namespace { 31std::error_code CreateFileWithContent(const SmallString<128> &FilePath, 32 const StringRef &content) { 33 int FD = 0; 34 if (std::error_code ec = fs::openFileForWrite(FilePath, FD, fs::F_None)) 35 return ec; 36 37 const bool ShouldClose = true; 38 raw_fd_ostream OS(FD, ShouldClose); 39 OS << content; 40 41 return std::error_code(); 42} 43 44class ScopedFD { 45 int FD; 46 47 ScopedFD(const ScopedFD &) = delete; 48 ScopedFD &operator=(const ScopedFD &) = delete; 49 50 public: 51 explicit ScopedFD(int Descriptor) : FD(Descriptor) {} 52 ~ScopedFD() { Process::SafelyCloseFileDescriptor(FD); } 53}; 54 55TEST(rename, FileOpenedForReadingCanBeReplaced) { 56 // Create unique temporary directory for this test. 57 SmallString<128> TestDirectory; 58 ASSERT_NO_ERROR(fs::createUniqueDirectory( 59 "FileOpenedForReadingCanBeReplaced-test", TestDirectory)); 60 61 // Add a couple of files to the test directory. 62 SmallString<128> SourceFileName(TestDirectory); 63 path::append(SourceFileName, "source"); 64 65 SmallString<128> TargetFileName(TestDirectory); 66 path::append(TargetFileName, "target"); 67 68 ASSERT_NO_ERROR(CreateFileWithContent(SourceFileName, "!!source!!")); 69 ASSERT_NO_ERROR(CreateFileWithContent(TargetFileName, "!!target!!")); 70 71 { 72 // Open the target file for reading. 73 int ReadFD = 0; 74 ASSERT_NO_ERROR(fs::openFileForRead(TargetFileName, ReadFD)); 75 ScopedFD EventuallyCloseIt(ReadFD); 76 77 // Confirm we can replace the file while it is open. 78 EXPECT_TRUE(!fs::rename(SourceFileName, TargetFileName)); 79 80 // We should still be able to read the old data through the existing 81 // descriptor. 82 auto Buffer = MemoryBuffer::getOpenFile(ReadFD, TargetFileName, -1); 83 ASSERT_TRUE(static_cast<bool>(Buffer)); 84 EXPECT_EQ(Buffer.get()->getBuffer(), "!!target!!"); 85 86 // The source file should no longer exist 87 EXPECT_FALSE(fs::exists(SourceFileName)); 88 } 89 90 { 91 // If we obtain a new descriptor for the target file, we should find that it 92 // contains the content that was in the source file. 93 int ReadFD = 0; 94 ASSERT_NO_ERROR(fs::openFileForRead(TargetFileName, ReadFD)); 95 ScopedFD EventuallyCloseIt(ReadFD); 96 auto Buffer = MemoryBuffer::getOpenFile(ReadFD, TargetFileName, -1); 97 ASSERT_TRUE(static_cast<bool>(Buffer)); 98 99 EXPECT_EQ(Buffer.get()->getBuffer(), "!!source!!"); 100 } 101 102 // Rename the target file back to the source file name to confirm that rename 103 // still works if the destination does not already exist. 104 EXPECT_TRUE(!fs::rename(TargetFileName, SourceFileName)); 105 EXPECT_FALSE(fs::exists(TargetFileName)); 106 ASSERT_TRUE(fs::exists(SourceFileName)); 107 108 // Clean up. 109 ASSERT_NO_ERROR(fs::remove(SourceFileName)); 110 ASSERT_NO_ERROR(fs::remove(TestDirectory.str())); 111} 112 113} // anonymous namespace 114