300 lines · cpp
1//===- DependencyScanningFilesystemTest.cpp -------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h"10#include "llvm/ADT/SmallString.h"11#include "llvm/Support/VirtualFileSystem.h"12#include "gtest/gtest.h"13 14using namespace clang::tooling::dependencies;15 16TEST(DependencyScanningFilesystem, OpenFileAndGetBufferRepeatedly) {17 auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();18 InMemoryFS->setCurrentWorkingDirectory("/");19 InMemoryFS->addFile("/foo", 0, llvm::MemoryBuffer::getMemBuffer("content"));20 21 DependencyScanningFilesystemSharedCache SharedCache;22 DependencyScanningWorkerFilesystem DepFS(SharedCache, InMemoryFS);23 24 auto FileOrErr1 = DepFS.openFileForRead("foo");25 auto FileOrErr2 = DepFS.openFileForRead("foo");26 ASSERT_EQ(FileOrErr1.getError(), std::error_code{});27 ASSERT_EQ(FileOrErr1.getError(), std::error_code{});28 std::unique_ptr<llvm::vfs::File> File1 = std::move(*FileOrErr1);29 std::unique_ptr<llvm::vfs::File> File2 = std::move(*FileOrErr2);30 ASSERT_NE(File1, nullptr);31 ASSERT_NE(File2, nullptr);32 auto BufOrErr11 = File1->getBuffer("buf11");33 auto BufOrErr12 = File1->getBuffer("buf12");34 auto BufOrErr21 = File1->getBuffer("buf21");35 ASSERT_EQ(BufOrErr11.getError(), std::error_code{});36 ASSERT_EQ(BufOrErr12.getError(), std::error_code{});37 ASSERT_EQ(BufOrErr21.getError(), std::error_code{});38 std::unique_ptr<llvm::MemoryBuffer> Buf11 = std::move(*BufOrErr11);39 std::unique_ptr<llvm::MemoryBuffer> Buf12 = std::move(*BufOrErr12);40 std::unique_ptr<llvm::MemoryBuffer> Buf21 = std::move(*BufOrErr21);41 ASSERT_NE(Buf11, nullptr);42 ASSERT_NE(Buf12, nullptr);43 ASSERT_NE(Buf21, nullptr);44 ASSERT_EQ(Buf11->getBuffer().data(), Buf12->getBuffer().data());45 ASSERT_EQ(Buf11->getBuffer().data(), Buf21->getBuffer().data());46 EXPECT_EQ(Buf11->getBuffer(), "content");47}48 49TEST(DependencyScanningWorkerFilesystem, CacheStatusFailures) {50 auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();51 52 auto InstrumentingFS =53 llvm::makeIntrusiveRefCnt<llvm::vfs::TracingFileSystem>(InMemoryFS);54 55 DependencyScanningFilesystemSharedCache SharedCache;56 DependencyScanningWorkerFilesystem DepFS(SharedCache, InstrumentingFS);57 DependencyScanningWorkerFilesystem DepFS2(SharedCache, InstrumentingFS);58 59 DepFS.status("/foo.c");60 EXPECT_EQ(InstrumentingFS->NumStatusCalls, 1u);61 62 DepFS.status("/foo.c");63 EXPECT_EQ(InstrumentingFS->NumStatusCalls, 1u); // Cached, no increase.64 65 DepFS.status("/bar.c");66 EXPECT_EQ(InstrumentingFS->NumStatusCalls, 2u);67 68 DepFS2.status("/foo.c");69 EXPECT_EQ(InstrumentingFS->NumStatusCalls, 2u); // Shared cache.70}71 72TEST(DependencyScanningFilesystem, CacheGetRealPath) {73 auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();74 InMemoryFS->setCurrentWorkingDirectory("/");75 InMemoryFS->addFile("/foo", 0, llvm::MemoryBuffer::getMemBuffer(""));76 InMemoryFS->addFile("/bar", 0, llvm::MemoryBuffer::getMemBuffer(""));77 78 auto InstrumentingFS =79 llvm::makeIntrusiveRefCnt<llvm::vfs::TracingFileSystem>(InMemoryFS);80 81 DependencyScanningFilesystemSharedCache SharedCache;82 DependencyScanningWorkerFilesystem DepFS(SharedCache, InstrumentingFS);83 DependencyScanningWorkerFilesystem DepFS2(SharedCache, InstrumentingFS);84 85 {86 llvm::SmallString<128> Result;87 DepFS.getRealPath("/foo", Result);88 EXPECT_EQ(InstrumentingFS->NumGetRealPathCalls, 1u);89 }90 91 {92 llvm::SmallString<128> Result;93 DepFS.getRealPath("/foo", Result);94 EXPECT_EQ(InstrumentingFS->NumGetRealPathCalls, 1u); // Cached, no increase.95 }96 97 {98 llvm::SmallString<128> Result;99 DepFS.getRealPath("/bar", Result);100 EXPECT_EQ(InstrumentingFS->NumGetRealPathCalls, 2u);101 }102 103 {104 llvm::SmallString<128> Result;105 DepFS2.getRealPath("/foo", Result);106 EXPECT_EQ(InstrumentingFS->NumGetRealPathCalls, 2u); // Shared cache.107 }108}109 110TEST(DependencyScanningFilesystem, RealPathAndStatusInvariants) {111 auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();112 InMemoryFS->setCurrentWorkingDirectory("/");113 InMemoryFS->addFile("/foo.c", 0, llvm::MemoryBuffer::getMemBuffer(""));114 InMemoryFS->addFile("/bar.c", 0, llvm::MemoryBuffer::getMemBuffer(""));115 116 DependencyScanningFilesystemSharedCache SharedCache;117 DependencyScanningWorkerFilesystem DepFS(SharedCache, InMemoryFS);118 119 // Success.120 {121 DepFS.status("/foo.c");122 123 llvm::SmallString<128> Result;124 DepFS.getRealPath("/foo.c", Result);125 }126 {127 llvm::SmallString<128> Result;128 DepFS.getRealPath("/bar.c", Result);129 130 DepFS.status("/bar.c");131 }132 133 // Failure.134 {135 DepFS.status("/foo.m");136 137 llvm::SmallString<128> Result;138 DepFS.getRealPath("/foo.m", Result);139 }140 {141 llvm::SmallString<128> Result;142 DepFS.getRealPath("/bar.m", Result);143 144 DepFS.status("/bar.m");145 }146 147 // Failure without caching.148 {149 DepFS.status("/foo");150 151 llvm::SmallString<128> Result;152 DepFS.getRealPath("/foo", Result);153 }154 {155 llvm::SmallString<128> Result;156 DepFS.getRealPath("/bar", Result);157 158 DepFS.status("/bar");159 }160}161 162TEST(DependencyScanningFilesystem, CacheStatOnExists) {163 auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();164 auto InstrumentingFS =165 llvm::makeIntrusiveRefCnt<llvm::vfs::TracingFileSystem>(InMemoryFS);166 InMemoryFS->setCurrentWorkingDirectory("/");167 InMemoryFS->addFile("/foo", 0, llvm::MemoryBuffer::getMemBuffer(""));168 InMemoryFS->addFile("/bar", 0, llvm::MemoryBuffer::getMemBuffer(""));169 DependencyScanningFilesystemSharedCache SharedCache;170 DependencyScanningWorkerFilesystem DepFS(SharedCache, InstrumentingFS);171 172 DepFS.status("/foo");173 DepFS.status("/foo");174 DepFS.status("/bar");175 EXPECT_EQ(InstrumentingFS->NumStatusCalls, 2u);176 177 DepFS.exists("/foo");178 DepFS.exists("/bar");179 EXPECT_EQ(InstrumentingFS->NumStatusCalls, 2u);180 EXPECT_EQ(InstrumentingFS->NumExistsCalls, 0u);181}182 183TEST(DependencyScanningFilesystem, CacheStatFailures) {184 auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();185 InMemoryFS->setCurrentWorkingDirectory("/");186 InMemoryFS->addFile("/dir/vector", 0, llvm::MemoryBuffer::getMemBuffer(""));187 InMemoryFS->addFile("/cache/a.pcm", 0, llvm::MemoryBuffer::getMemBuffer(""));188 189 auto InstrumentingFS =190 llvm::makeIntrusiveRefCnt<llvm::vfs::TracingFileSystem>(InMemoryFS);191 192 DependencyScanningFilesystemSharedCache SharedCache;193 DependencyScanningWorkerFilesystem DepFS(SharedCache, InstrumentingFS);194 195 DepFS.status("/dir");196 DepFS.status("/dir");197 EXPECT_EQ(InstrumentingFS->NumStatusCalls, 1u);198 199 DepFS.status("/dir/vector");200 DepFS.status("/dir/vector");201 EXPECT_EQ(InstrumentingFS->NumStatusCalls, 2u);202 203 DepFS.setBypassedPathPrefix("/cache");204 DepFS.exists("/cache/a.pcm");205 EXPECT_EQ(InstrumentingFS->NumStatusCalls, 3u);206 DepFS.exists("/cache/a.pcm");207 EXPECT_EQ(InstrumentingFS->NumStatusCalls, 4u);208 209 DepFS.resetBypassedPathPrefix();210 DepFS.exists("/cache/a.pcm");211 DepFS.exists("/cache/a.pcm");212 EXPECT_EQ(InstrumentingFS->NumStatusCalls, 5u);213}214 215TEST(DependencyScanningFilesystem, DiagnoseStaleStatFailures) {216 auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();217 InMemoryFS->setCurrentWorkingDirectory("/");218 219 DependencyScanningFilesystemSharedCache SharedCache;220 DependencyScanningWorkerFilesystem DepFS(SharedCache, InMemoryFS);221 222 bool Path1Exists = DepFS.exists("/path1.suffix");223 ASSERT_EQ(Path1Exists, false);224 225 // Adding a file that has been stat-ed,226 InMemoryFS->addFile("/path1.suffix", 0, llvm::MemoryBuffer::getMemBuffer(""));227 Path1Exists = DepFS.exists("/path1.suffix");228 // Due to caching in SharedCache, path1 should not exist in229 // DepFS's eyes.230 ASSERT_EQ(Path1Exists, false);231 232 auto InvalidEntries = SharedCache.getOutOfDateEntries(*InMemoryFS);233 234 EXPECT_EQ(InvalidEntries.size(), 1u);235 ASSERT_STREQ("/path1.suffix", InvalidEntries[0].Path);236}237 238TEST(DependencyScanningFilesystem, DiagnoseCachedFileSizeChange) {239 auto InMemoryFS1 = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();240 auto InMemoryFS2 = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();241 InMemoryFS1->setCurrentWorkingDirectory("/");242 InMemoryFS2->setCurrentWorkingDirectory("/");243 244 DependencyScanningFilesystemSharedCache SharedCache;245 DependencyScanningWorkerFilesystem DepFS(SharedCache, InMemoryFS1);246 247 InMemoryFS1->addFile("/path1.suffix", 0,248 llvm::MemoryBuffer::getMemBuffer(""));249 bool Path1Exists = DepFS.exists("/path1.suffix");250 ASSERT_EQ(Path1Exists, true);251 252 // Add a file to a new FS that has the same path but different content.253 InMemoryFS2->addFile("/path1.suffix", 1,254 llvm::MemoryBuffer::getMemBuffer(" "));255 256 // Check against the new file system. InMemoryFS2 could be the underlying257 // physical system in the real world.258 auto InvalidEntries = SharedCache.getOutOfDateEntries(*InMemoryFS2);259 260 ASSERT_EQ(InvalidEntries.size(), 1u);261 ASSERT_STREQ("/path1.suffix", InvalidEntries[0].Path);262 auto SizeInfo = std::get_if<263 DependencyScanningFilesystemSharedCache::OutOfDateEntry::SizeChangedInfo>(264 &InvalidEntries[0].Info);265 ASSERT_TRUE(SizeInfo);266 ASSERT_EQ(SizeInfo->CachedSize, 0u);267 ASSERT_EQ(SizeInfo->ActualSize, 8u);268}269 270TEST(DependencyScanningFilesystem, DoNotDiagnoseDirSizeChange) {271 llvm::SmallString<128> Dir;272 ASSERT_FALSE(llvm::sys::fs::createUniqueDirectory("tmp", Dir));273 274 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =275 llvm::vfs::createPhysicalFileSystem();276 277 DependencyScanningFilesystemSharedCache SharedCache;278 DependencyScanningWorkerFilesystem DepFS(SharedCache, FS);279 280 // Trigger the file system cache.281 ASSERT_EQ(DepFS.exists(Dir), true);282 283 // Add a file to the FS to change its size.284 // It seems that directory sizes reported are not meaningful,285 // and should not be used to check for size changes.286 // This test is setup only to trigger a size change so that we287 // know we are excluding directories from reporting.288 llvm::SmallString<128> FilePath = Dir;289 llvm::sys::path::append(FilePath, "file.h");290 {291 std::error_code EC;292 llvm::raw_fd_ostream TempFile(FilePath, EC);293 ASSERT_FALSE(EC);294 }295 296 // We do not report directory size changes.297 auto InvalidEntries = SharedCache.getOutOfDateEntries(*FS);298 EXPECT_EQ(InvalidEntries.size(), 0u);299}300