`DependencyScanningFileSystemSharedCache` can currently report out-of-date negatively stat cached paths. This PR enhances the reporting with two modifications. 1. The reported path are now null terminated char arrays instead of `StringRef`s. This way the API's user can avoid copying `StringRef`s to other containers because the char arrays can be used directly. 2. The API now reports out-of-date cache entry due to file size changes. Specifically, we check each file's cached size against the size of the same file on the underlying FS. If the sizes are different, diagnostics will be reported. rdar://152247357
236 lines
7.8 KiB
C++
236 lines
7.8 KiB
C++
//===- 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/Tooling/DependencyScanning/DependencyScanningFilesystem.h"
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/Support/VirtualFileSystem.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
using namespace clang::tooling::dependencies;
|
|
|
|
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);
|
|
}
|