| //===- DependencyScanningFilesystemTest.cpp -------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "clang/DependencyScanning/DependencyScanningFilesystem.h" |
| #include "llvm/ADT/SmallString.h" |
| #include "llvm/Support/VirtualFileSystem.h" |
| #include "gtest/gtest.h" |
| |
| using namespace clang::dependencies; |
| |
| TEST(DependencyScanningFilesystem, OpenFileAndGetBufferRepeatedly) { |
| auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>(); |
| InMemoryFS->setCurrentWorkingDirectory("/"); |
| InMemoryFS->addFile("/foo", 0, llvm::MemoryBuffer::getMemBuffer("content")); |
| |
| DependencyScanningFilesystemSharedCache SharedCache; |
| DependencyScanningWorkerFilesystem DepFS(SharedCache, InMemoryFS); |
| |
| auto FileOrErr1 = DepFS.openFileForRead("foo"); |
| auto FileOrErr2 = DepFS.openFileForRead("foo"); |
| ASSERT_EQ(FileOrErr1.getError(), std::error_code{}); |
| ASSERT_EQ(FileOrErr1.getError(), std::error_code{}); |
| std::unique_ptr<llvm::vfs::File> File1 = std::move(*FileOrErr1); |
| std::unique_ptr<llvm::vfs::File> File2 = std::move(*FileOrErr2); |
| ASSERT_NE(File1, nullptr); |
| ASSERT_NE(File2, nullptr); |
| auto BufOrErr11 = File1->getBuffer("buf11"); |
| auto BufOrErr12 = File1->getBuffer("buf12"); |
| auto BufOrErr21 = File1->getBuffer("buf21"); |
| ASSERT_EQ(BufOrErr11.getError(), std::error_code{}); |
| ASSERT_EQ(BufOrErr12.getError(), std::error_code{}); |
| ASSERT_EQ(BufOrErr21.getError(), std::error_code{}); |
| std::unique_ptr<llvm::MemoryBuffer> Buf11 = std::move(*BufOrErr11); |
| std::unique_ptr<llvm::MemoryBuffer> Buf12 = std::move(*BufOrErr12); |
| std::unique_ptr<llvm::MemoryBuffer> Buf21 = std::move(*BufOrErr21); |
| ASSERT_NE(Buf11, nullptr); |
| ASSERT_NE(Buf12, nullptr); |
| ASSERT_NE(Buf21, nullptr); |
| ASSERT_EQ(Buf11->getBuffer().data(), Buf12->getBuffer().data()); |
| ASSERT_EQ(Buf11->getBuffer().data(), Buf21->getBuffer().data()); |
| EXPECT_EQ(Buf11->getBuffer(), "content"); |
| } |
| |
| TEST(DependencyScanningWorkerFilesystem, CacheStatusFailures) { |
| auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>(); |
| |
| auto InstrumentingFS = |
| llvm::makeIntrusiveRefCnt<llvm::vfs::TracingFileSystem>(InMemoryFS); |
| |
| DependencyScanningFilesystemSharedCache SharedCache; |
| DependencyScanningWorkerFilesystem DepFS(SharedCache, InstrumentingFS); |
| DependencyScanningWorkerFilesystem DepFS2(SharedCache, InstrumentingFS); |
| |
| DepFS.status("/foo.c"); |
| EXPECT_EQ(InstrumentingFS->NumStatusCalls, 1u); |
| |
| DepFS.status("/foo.c"); |
| EXPECT_EQ(InstrumentingFS->NumStatusCalls, 1u); // Cached, no increase. |
| |
| DepFS.status("/bar.c"); |
| EXPECT_EQ(InstrumentingFS->NumStatusCalls, 2u); |
| |
| DepFS2.status("/foo.c"); |
| EXPECT_EQ(InstrumentingFS->NumStatusCalls, 2u); // Shared cache. |
| } |
| |
| TEST(DependencyScanningFilesystem, CacheGetRealPath) { |
| auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>(); |
| InMemoryFS->setCurrentWorkingDirectory("/"); |
| InMemoryFS->addFile("/foo", 0, llvm::MemoryBuffer::getMemBuffer("")); |
| InMemoryFS->addFile("/bar", 0, llvm::MemoryBuffer::getMemBuffer("")); |
| |
| auto InstrumentingFS = |
| llvm::makeIntrusiveRefCnt<llvm::vfs::TracingFileSystem>(InMemoryFS); |
| |
| DependencyScanningFilesystemSharedCache SharedCache; |
| DependencyScanningWorkerFilesystem DepFS(SharedCache, InstrumentingFS); |
| DependencyScanningWorkerFilesystem DepFS2(SharedCache, InstrumentingFS); |
| |
| { |
| llvm::SmallString<128> Result; |
| DepFS.getRealPath("/foo", Result); |
| EXPECT_EQ(InstrumentingFS->NumGetRealPathCalls, 1u); |
| } |
| |
| { |
| llvm::SmallString<128> Result; |
| DepFS.getRealPath("/foo", Result); |
| EXPECT_EQ(InstrumentingFS->NumGetRealPathCalls, 1u); // Cached, no increase. |
| } |
| |
| { |
| llvm::SmallString<128> Result; |
| DepFS.getRealPath("/bar", Result); |
| EXPECT_EQ(InstrumentingFS->NumGetRealPathCalls, 2u); |
| } |
| |
| { |
| llvm::SmallString<128> Result; |
| DepFS2.getRealPath("/foo", Result); |
| EXPECT_EQ(InstrumentingFS->NumGetRealPathCalls, 2u); // Shared cache. |
| } |
| } |
| |
| TEST(DependencyScanningFilesystem, RealPathAndStatusInvariants) { |
| auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>(); |
| InMemoryFS->setCurrentWorkingDirectory("/"); |
| InMemoryFS->addFile("/foo.c", 0, llvm::MemoryBuffer::getMemBuffer("")); |
| InMemoryFS->addFile("/bar.c", 0, llvm::MemoryBuffer::getMemBuffer("")); |
| |
| DependencyScanningFilesystemSharedCache SharedCache; |
| DependencyScanningWorkerFilesystem DepFS(SharedCache, InMemoryFS); |
| |
| // Success. |
| { |
| DepFS.status("/foo.c"); |
| |
| llvm::SmallString<128> Result; |
| DepFS.getRealPath("/foo.c", Result); |
| } |
| { |
| llvm::SmallString<128> Result; |
| DepFS.getRealPath("/bar.c", Result); |
| |
| DepFS.status("/bar.c"); |
| } |
| |
| // Failure. |
| { |
| DepFS.status("/foo.m"); |
| |
| llvm::SmallString<128> Result; |
| DepFS.getRealPath("/foo.m", Result); |
| } |
| { |
| llvm::SmallString<128> Result; |
| DepFS.getRealPath("/bar.m", Result); |
| |
| DepFS.status("/bar.m"); |
| } |
| |
| // Failure without caching. |
| { |
| DepFS.status("/foo"); |
| |
| llvm::SmallString<128> Result; |
| DepFS.getRealPath("/foo", Result); |
| } |
| { |
| llvm::SmallString<128> Result; |
| DepFS.getRealPath("/bar", Result); |
| |
| DepFS.status("/bar"); |
| } |
| } |
| |
| TEST(DependencyScanningFilesystem, CacheStatOnExists) { |
| auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>(); |
| auto InstrumentingFS = |
| llvm::makeIntrusiveRefCnt<llvm::vfs::TracingFileSystem>(InMemoryFS); |
| InMemoryFS->setCurrentWorkingDirectory("/"); |
| InMemoryFS->addFile("/foo", 0, llvm::MemoryBuffer::getMemBuffer("")); |
| InMemoryFS->addFile("/bar", 0, llvm::MemoryBuffer::getMemBuffer("")); |
| DependencyScanningFilesystemSharedCache SharedCache; |
| DependencyScanningWorkerFilesystem DepFS(SharedCache, InstrumentingFS); |
| |
| DepFS.status("/foo"); |
| DepFS.status("/foo"); |
| DepFS.status("/bar"); |
| EXPECT_EQ(InstrumentingFS->NumStatusCalls, 2u); |
| |
| DepFS.exists("/foo"); |
| DepFS.exists("/bar"); |
| EXPECT_EQ(InstrumentingFS->NumStatusCalls, 2u); |
| EXPECT_EQ(InstrumentingFS->NumExistsCalls, 0u); |
| } |
| |
| TEST(DependencyScanningFilesystem, CacheStatFailures) { |
| auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>(); |
| InMemoryFS->setCurrentWorkingDirectory("/"); |
| InMemoryFS->addFile("/dir/vector", 0, llvm::MemoryBuffer::getMemBuffer("")); |
| InMemoryFS->addFile("/cache/a.pcm", 0, llvm::MemoryBuffer::getMemBuffer("")); |
| |
| auto InstrumentingFS = |
| llvm::makeIntrusiveRefCnt<llvm::vfs::TracingFileSystem>(InMemoryFS); |
| |
| DependencyScanningFilesystemSharedCache SharedCache; |
| DependencyScanningWorkerFilesystem DepFS(SharedCache, InstrumentingFS); |
| |
| DepFS.status("/dir"); |
| DepFS.status("/dir"); |
| EXPECT_EQ(InstrumentingFS->NumStatusCalls, 1u); |
| |
| DepFS.status("/dir/vector"); |
| DepFS.status("/dir/vector"); |
| EXPECT_EQ(InstrumentingFS->NumStatusCalls, 2u); |
| |
| DepFS.setBypassedPathPrefix("/cache"); |
| DepFS.exists("/cache/a.pcm"); |
| EXPECT_EQ(InstrumentingFS->NumStatusCalls, 3u); |
| DepFS.exists("/cache/a.pcm"); |
| EXPECT_EQ(InstrumentingFS->NumStatusCalls, 4u); |
| |
| DepFS.resetBypassedPathPrefix(); |
| DepFS.exists("/cache/a.pcm"); |
| DepFS.exists("/cache/a.pcm"); |
| EXPECT_EQ(InstrumentingFS->NumStatusCalls, 5u); |
| } |
| |
| TEST(DependencyScanningFilesystem, DiagnoseStaleStatFailures) { |
| auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>(); |
| InMemoryFS->setCurrentWorkingDirectory("/"); |
| |
| DependencyScanningFilesystemSharedCache SharedCache; |
| DependencyScanningWorkerFilesystem DepFS(SharedCache, InMemoryFS); |
| |
| bool Path1Exists = DepFS.exists("/path1.suffix"); |
| ASSERT_EQ(Path1Exists, false); |
| |
| // Adding a file that has been stat-ed, |
| InMemoryFS->addFile("/path1.suffix", 0, llvm::MemoryBuffer::getMemBuffer("")); |
| Path1Exists = DepFS.exists("/path1.suffix"); |
| // Due to caching in SharedCache, path1 should not exist in |
| // DepFS's eyes. |
| ASSERT_EQ(Path1Exists, false); |
| |
| auto InvalidEntries = SharedCache.getOutOfDateEntries(*InMemoryFS); |
| |
| EXPECT_EQ(InvalidEntries.size(), 1u); |
| ASSERT_STREQ("/path1.suffix", InvalidEntries[0].Path); |
| } |
| |
| TEST(DependencyScanningFilesystem, DiagnoseCachedFileSizeChange) { |
| auto InMemoryFS1 = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>(); |
| auto InMemoryFS2 = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>(); |
| InMemoryFS1->setCurrentWorkingDirectory("/"); |
| InMemoryFS2->setCurrentWorkingDirectory("/"); |
| |
| DependencyScanningFilesystemSharedCache SharedCache; |
| DependencyScanningWorkerFilesystem DepFS(SharedCache, InMemoryFS1); |
| |
| InMemoryFS1->addFile("/path1.suffix", 0, |
| llvm::MemoryBuffer::getMemBuffer("")); |
| bool Path1Exists = DepFS.exists("/path1.suffix"); |
| ASSERT_EQ(Path1Exists, true); |
| |
| // Add a file to a new FS that has the same path but different content. |
| InMemoryFS2->addFile("/path1.suffix", 1, |
| llvm::MemoryBuffer::getMemBuffer(" ")); |
| |
| // Check against the new file system. InMemoryFS2 could be the underlying |
| // physical system in the real world. |
| auto InvalidEntries = SharedCache.getOutOfDateEntries(*InMemoryFS2); |
| |
| ASSERT_EQ(InvalidEntries.size(), 1u); |
| ASSERT_STREQ("/path1.suffix", InvalidEntries[0].Path); |
| auto SizeInfo = std::get_if< |
| DependencyScanningFilesystemSharedCache::OutOfDateEntry::SizeChangedInfo>( |
| &InvalidEntries[0].Info); |
| ASSERT_TRUE(SizeInfo); |
| ASSERT_EQ(SizeInfo->CachedSize, 0u); |
| ASSERT_EQ(SizeInfo->ActualSize, 8u); |
| } |
| |
| TEST(DependencyScanningFilesystem, DoNotDiagnoseDirSizeChange) { |
| llvm::SmallString<128> Dir; |
| ASSERT_FALSE(llvm::sys::fs::createUniqueDirectory("tmp", Dir)); |
| |
| llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS = |
| llvm::vfs::createPhysicalFileSystem(); |
| |
| DependencyScanningFilesystemSharedCache SharedCache; |
| DependencyScanningWorkerFilesystem DepFS(SharedCache, FS); |
| |
| // Trigger the file system cache. |
| ASSERT_EQ(DepFS.exists(Dir), true); |
| |
| // Add a file to the FS to change its size. |
| // It seems that directory sizes reported are not meaningful, |
| // and should not be used to check for size changes. |
| // This test is setup only to trigger a size change so that we |
| // know we are excluding directories from reporting. |
| llvm::SmallString<128> FilePath = Dir; |
| llvm::sys::path::append(FilePath, "file.h"); |
| { |
| std::error_code EC; |
| llvm::raw_fd_ostream TempFile(FilePath, EC); |
| ASSERT_FALSE(EC); |
| } |
| |
| // We do not report directory size changes. |
| auto InvalidEntries = SharedCache.getOutOfDateEntries(*FS); |
| EXPECT_EQ(InvalidEntries.size(), 0u); |
| } |