aboutsummaryrefslogtreecommitdiff
path: root/llvm/unittests/CAS
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/unittests/CAS')
-rw-r--r--llvm/unittests/CAS/ActionCacheTest.cpp6
-rw-r--r--llvm/unittests/CAS/BuiltinUnifiedCASDatabasesTest.cpp67
-rw-r--r--llvm/unittests/CAS/CASTestConfig.cpp17
-rw-r--r--llvm/unittests/CAS/CASTestConfig.h41
-rw-r--r--llvm/unittests/CAS/CMakeLists.txt2
-rw-r--r--llvm/unittests/CAS/ObjectStoreTest.cpp134
-rw-r--r--llvm/unittests/CAS/OnDiskCommonUtils.h21
-rw-r--r--llvm/unittests/CAS/OnDiskGraphDBTest.cpp12
-rw-r--r--llvm/unittests/CAS/UnifiedOnDiskCacheTest.cpp198
9 files changed, 470 insertions, 28 deletions
diff --git a/llvm/unittests/CAS/ActionCacheTest.cpp b/llvm/unittests/CAS/ActionCacheTest.cpp
index db67e30..692da23 100644
--- a/llvm/unittests/CAS/ActionCacheTest.cpp
+++ b/llvm/unittests/CAS/ActionCacheTest.cpp
@@ -21,7 +21,7 @@ using namespace llvm;
using namespace llvm::cas;
TEST_P(CASTest, ActionCacheHit) {
- std::shared_ptr<ObjectStore> CAS = createObjectStore();
+ std::unique_ptr<ObjectStore> CAS = createObjectStore();
std::unique_ptr<ActionCache> Cache = createActionCache();
std::optional<ObjectProxy> ID;
@@ -36,7 +36,7 @@ TEST_P(CASTest, ActionCacheHit) {
}
TEST_P(CASTest, ActionCacheMiss) {
- std::shared_ptr<ObjectStore> CAS = createObjectStore();
+ std::unique_ptr<ObjectStore> CAS = createObjectStore();
std::unique_ptr<ActionCache> Cache = createActionCache();
std::optional<ObjectProxy> ID1, ID2;
@@ -59,7 +59,7 @@ TEST_P(CASTest, ActionCacheMiss) {
}
TEST_P(CASTest, ActionCacheRewrite) {
- std::shared_ptr<ObjectStore> CAS = createObjectStore();
+ std::unique_ptr<ObjectStore> CAS = createObjectStore();
std::unique_ptr<ActionCache> Cache = createActionCache();
std::optional<ObjectProxy> ID1, ID2;
diff --git a/llvm/unittests/CAS/BuiltinUnifiedCASDatabasesTest.cpp b/llvm/unittests/CAS/BuiltinUnifiedCASDatabasesTest.cpp
new file mode 100644
index 0000000..19522e9
--- /dev/null
+++ b/llvm/unittests/CAS/BuiltinUnifiedCASDatabasesTest.cpp
@@ -0,0 +1,67 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "llvm/CAS/BuiltinUnifiedCASDatabases.h"
+#include "CASTestConfig.h"
+#include "llvm/CAS/ActionCache.h"
+#include "llvm/CAS/ObjectStore.h"
+#include "llvm/Testing/Support/Error.h"
+#include "llvm/Testing/Support/SupportHelpers.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace llvm::cas;
+
+TEST_F(OnDiskCASTest, UnifiedCASMaterializationCheckPreventsGarbageCollection) {
+ unittest::TempDir Temp("on-disk-unified-cas", /*Unique=*/true);
+
+ auto WithCAS = [&](llvm::function_ref<void(ObjectStore &)> Action) {
+ std::pair<std::unique_ptr<ObjectStore>, std::unique_ptr<ActionCache>> DBs;
+ ASSERT_THAT_ERROR(
+ createOnDiskUnifiedCASDatabases(Temp.path()).moveInto(DBs),
+ Succeeded());
+ ObjectStore &CAS = *DBs.first;
+ ASSERT_THAT_ERROR(CAS.setSizeLimit(1), Succeeded());
+ Action(CAS);
+ };
+
+ std::optional<CASID> ID;
+
+ // Create an object in the CAS.
+ WithCAS([&ID](ObjectStore &CAS) {
+ std::optional<ObjectRef> Ref;
+ ASSERT_THAT_ERROR(CAS.store({}, "blah").moveInto(Ref), Succeeded());
+ ASSERT_TRUE(Ref.has_value());
+
+ ID = CAS.getID(*Ref);
+ });
+
+ // Check materialization and prune the storage.
+ WithCAS([&ID](ObjectStore &CAS) {
+ std::optional<ObjectRef> Ref = CAS.getReference(*ID);
+ ASSERT_TRUE(Ref.has_value());
+
+ std::optional<bool> IsMaterialized;
+ ASSERT_THAT_ERROR(CAS.isMaterialized(*Ref).moveInto(IsMaterialized),
+ Succeeded());
+ ASSERT_TRUE(IsMaterialized);
+
+ ASSERT_THAT_ERROR(CAS.pruneStorageData(), Succeeded());
+ });
+
+ // Verify that the previous materialization check kept the object in the CAS.
+ WithCAS([&ID](ObjectStore &CAS) {
+ std::optional<ObjectRef> Ref = CAS.getReference(*ID);
+ ASSERT_TRUE(Ref.has_value());
+
+ std::optional<bool> IsMaterialized;
+ ASSERT_THAT_ERROR(CAS.isMaterialized(*Ref).moveInto(IsMaterialized),
+ Succeeded());
+ ASSERT_TRUE(IsMaterialized);
+ });
+}
diff --git a/llvm/unittests/CAS/CASTestConfig.cpp b/llvm/unittests/CAS/CASTestConfig.cpp
index 10e4b68..08cbf1d 100644
--- a/llvm/unittests/CAS/CASTestConfig.cpp
+++ b/llvm/unittests/CAS/CASTestConfig.cpp
@@ -8,6 +8,7 @@
#include "CASTestConfig.h"
#include "llvm/CAS/ObjectStore.h"
+#include "llvm/Testing/Support/Error.h"
#include "gtest/gtest.h"
#include <mutex>
@@ -15,7 +16,8 @@ using namespace llvm;
using namespace llvm::cas;
static CASTestingEnv createInMemory(int I) {
- return CASTestingEnv{createInMemoryCAS(), createInMemoryActionCache()};
+ return CASTestingEnv{createInMemoryCAS(), createInMemoryActionCache(),
+ std::nullopt};
}
INSTANTIATE_TEST_SUITE_P(InMemoryCAS, CASTest,
@@ -23,7 +25,7 @@ INSTANTIATE_TEST_SUITE_P(InMemoryCAS, CASTest,
#if LLVM_ENABLE_ONDISK_CAS
namespace llvm::cas::ondisk {
-extern void setMaxMappingSize(uint64_t Size);
+void setMaxMappingSize(uint64_t Size);
} // namespace llvm::cas::ondisk
void setMaxOnDiskCASMappingSize() {
@@ -31,6 +33,17 @@ void setMaxOnDiskCASMappingSize() {
std::call_once(
Flag, [] { llvm::cas::ondisk::setMaxMappingSize(100 * 1024 * 1024); });
}
+
+static CASTestingEnv createOnDisk(int I) {
+ unittest::TempDir Temp("on-disk-cas", /*Unique=*/true);
+ std::unique_ptr<ObjectStore> CAS;
+ EXPECT_THAT_ERROR(createOnDiskCAS(Temp.path()).moveInto(CAS), Succeeded());
+ std::unique_ptr<ActionCache> Cache;
+ EXPECT_THAT_ERROR(createOnDiskActionCache(Temp.path()).moveInto(Cache),
+ Succeeded());
+ return CASTestingEnv{std::move(CAS), std::move(Cache), std::move(Temp)};
+}
+INSTANTIATE_TEST_SUITE_P(OnDiskCAS, CASTest, ::testing::Values(createOnDisk));
#else
void setMaxOnDiskCASMappingSize() {}
#endif /* LLVM_ENABLE_ONDISK_CAS */
diff --git a/llvm/unittests/CAS/CASTestConfig.h b/llvm/unittests/CAS/CASTestConfig.h
index 8d3c553..b1c0e59 100644
--- a/llvm/unittests/CAS/CASTestConfig.h
+++ b/llvm/unittests/CAS/CASTestConfig.h
@@ -6,16 +6,28 @@
//
//===----------------------------------------------------------------------===//
+#ifndef LLVM_UNITTESTS_CASTESTCONFIG_H
+#define LLVM_UNITTESTS_CASTESTCONFIG_H
+
#include "llvm/CAS/ActionCache.h"
#include "llvm/CAS/ObjectStore.h"
+#include "llvm/Testing/Support/SupportHelpers.h"
#include "gtest/gtest.h"
+#include <memory>
-#ifndef LLVM_UNITTESTS_CASTESTCONFIG_H
-#define LLVM_UNITTESTS_CASTESTCONFIG_H
+namespace llvm::unittest::cas {
+class MockEnv {
+ void anchor();
+
+public:
+ virtual ~MockEnv();
+};
+} // namespace llvm::unittest::cas
struct CASTestingEnv {
std::unique_ptr<llvm::cas::ObjectStore> CAS;
std::unique_ptr<llvm::cas::ActionCache> Cache;
+ std::optional<llvm::unittest::TempDir> Temp;
};
void setMaxOnDiskCASMappingSize();
@@ -24,26 +36,47 @@ void setMaxOnDiskCASMappingSize();
class OnDiskCASTest : public ::testing::Test {
protected:
void SetUp() override {
+#if !LLVM_ENABLE_ONDISK_CAS
+ GTEST_SKIP() << "OnDiskCAS is not enabled";
+#endif
// Use a smaller database size for testing to conserve disk space.
setMaxOnDiskCASMappingSize();
}
};
+// Parametered test fixture for ObjectStore and ActionCache tests.
class CASTest
: public testing::TestWithParam<std::function<CASTestingEnv(int)>> {
protected:
std::optional<int> NextCASIndex;
+ llvm::SmallVector<llvm::unittest::TempDir> Dirs;
+
+ llvm::SmallVector<std::unique_ptr<llvm::unittest::cas::MockEnv>> Envs;
+
std::unique_ptr<llvm::cas::ObjectStore> createObjectStore() {
auto TD = GetParam()(++(*NextCASIndex));
+ if (TD.Temp)
+ Dirs.push_back(std::move(*TD.Temp));
return std::move(TD.CAS);
}
std::unique_ptr<llvm::cas::ActionCache> createActionCache() {
auto TD = GetParam()(++(*NextCASIndex));
+ if (TD.Temp)
+ Dirs.push_back(std::move(*TD.Temp));
return std::move(TD.Cache);
}
- void SetUp() override { NextCASIndex = 0; }
- void TearDown() override { NextCASIndex = std::nullopt; }
+
+ void SetUp() override {
+ NextCASIndex = 0;
+ setMaxOnDiskCASMappingSize();
+ }
+
+ void TearDown() override {
+ NextCASIndex = std::nullopt;
+ Dirs.clear();
+ Envs.clear();
+ }
};
#endif
diff --git a/llvm/unittests/CAS/CMakeLists.txt b/llvm/unittests/CAS/CMakeLists.txt
index da469f7..91e49be 100644
--- a/llvm/unittests/CAS/CMakeLists.txt
+++ b/llvm/unittests/CAS/CMakeLists.txt
@@ -1,9 +1,11 @@
set(ONDISK_CAS_TEST_SOURCES
+ BuiltinUnifiedCASDatabasesTest.cpp
OnDiskGraphDBTest.cpp
OnDiskDataAllocatorTest.cpp
OnDiskKeyValueDBTest.cpp
OnDiskTrieRawHashMapTest.cpp
ProgramTest.cpp
+ UnifiedOnDiskCacheTest.cpp
)
set(LLVM_OPTIONAL_SOURCES
diff --git a/llvm/unittests/CAS/ObjectStoreTest.cpp b/llvm/unittests/CAS/ObjectStoreTest.cpp
index 54083fd..b43ae33 100644
--- a/llvm/unittests/CAS/ObjectStoreTest.cpp
+++ b/llvm/unittests/CAS/ObjectStoreTest.cpp
@@ -1,4 +1,4 @@
-//===- ObjectStoreTest.cpp ------------------------------------------------===//
+//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -76,7 +76,7 @@ multiline text multiline text multiline text multiline text multiline text)",
// Run validation on all CASIDs.
for (int I = 0, E = IDs.size(); I != E; ++I)
- ASSERT_THAT_ERROR(CAS1->validate(IDs[I]), Succeeded());
+ ASSERT_THAT_ERROR(CAS1->validateObject(IDs[I]), Succeeded());
// Check that the blobs can be retrieved multiple times.
for (int I = 0, E = IDs.size(); I != E; ++I) {
@@ -120,15 +120,15 @@ TEST_P(CASTest, BlobsBig) {
std::optional<CASID> ID2;
ASSERT_THAT_ERROR(CAS->createProxy({}, String1).moveInto(ID1), Succeeded());
ASSERT_THAT_ERROR(CAS->createProxy({}, String1).moveInto(ID2), Succeeded());
- ASSERT_THAT_ERROR(CAS->validate(*ID1), Succeeded());
- ASSERT_THAT_ERROR(CAS->validate(*ID2), Succeeded());
+ ASSERT_THAT_ERROR(CAS->validateObject(*ID1), Succeeded());
+ ASSERT_THAT_ERROR(CAS->validateObject(*ID2), Succeeded());
ASSERT_EQ(ID1, ID2);
String1.append(String2);
ASSERT_THAT_ERROR(CAS->createProxy({}, String2).moveInto(ID1), Succeeded());
ASSERT_THAT_ERROR(CAS->createProxy({}, String2).moveInto(ID2), Succeeded());
- ASSERT_THAT_ERROR(CAS->validate(*ID1), Succeeded());
- ASSERT_THAT_ERROR(CAS->validate(*ID2), Succeeded());
+ ASSERT_THAT_ERROR(CAS->validateObject(*ID1), Succeeded());
+ ASSERT_THAT_ERROR(CAS->validateObject(*ID2), Succeeded());
ASSERT_EQ(ID1, ID2);
String2.append(String1);
}
@@ -176,10 +176,11 @@ multiline text multiline text multiline text multiline text multiline text)",
// Check basic printing of IDs.
IDs.push_back(CAS1->getID(*Node));
- auto ID = CAS1->getID(Nodes.back());
- EXPECT_EQ(ID.toString(), IDs.back().toString());
- EXPECT_EQ(*Node, Nodes.back());
- EXPECT_EQ(ID, IDs.back());
+ EXPECT_EQ(IDs.back().toString(), IDs.back().toString());
+ EXPECT_EQ(Nodes.front(), Nodes.front());
+ EXPECT_EQ(Nodes.back(), Nodes.back());
+ EXPECT_EQ(IDs.front(), IDs.front());
+ EXPECT_EQ(IDs.back(), IDs.back());
if (Nodes.size() <= 1)
continue;
EXPECT_NE(Nodes.front(), Nodes.back());
@@ -266,7 +267,7 @@ TEST_P(CASTest, NodesBig) {
}
for (auto ID : CreatedNodes)
- ASSERT_THAT_ERROR(CAS->validate(CAS->getID(ID)), Succeeded());
+ ASSERT_THAT_ERROR(CAS->validateObject(CAS->getID(ID)), Succeeded());
}
#if LLVM_ENABLE_THREADS
@@ -332,17 +333,124 @@ static void testBlobsParallel1(ObjectStore &CAS, uint64_t BlobSize) {
}
TEST_P(CASTest, BlobsParallel) {
- std::shared_ptr<ObjectStore> CAS = createObjectStore();
+ std::unique_ptr<ObjectStore> CAS = createObjectStore();
uint64_t Size = 1ULL * 1024;
ASSERT_NO_FATAL_FAILURE(testBlobsParallel1(*CAS, Size));
}
#ifdef EXPENSIVE_CHECKS
TEST_P(CASTest, BlobsBigParallel) {
- std::shared_ptr<ObjectStore> CAS = createObjectStore();
+ std::unique_ptr<ObjectStore> CAS = createObjectStore();
// 100k is large enough to be standalone files in our on-disk cas.
uint64_t Size = 100ULL * 1024;
ASSERT_NO_FATAL_FAILURE(testBlobsParallel1(*CAS, Size));
}
#endif // EXPENSIVE_CHECKS
+
+#ifndef _WIN32 // create_link won't work for directories on Windows
+TEST_F(OnDiskCASTest, OnDiskCASBlobsParallelMultiCAS) {
+ // This test intentionally uses symlinked paths to the same CAS to subvert the
+ // shared memory mappings that would normally be created within a single
+ // process. This breaks the lock file guarantees, so we must be careful not
+ // to create or destroy the CAS objects concurrently, which is when the locks
+ // are normally important.
+ unittest::TempDir Temp("on-disk-cas", /*Unique=*/true);
+ ASSERT_EQ(sys::fs::create_directory(Temp.path("real_cas")),
+ std::error_code());
+ ASSERT_EQ(sys::fs::create_link("real_cas", Temp.path("sym_cas1")),
+ std::error_code());
+ ASSERT_EQ(sys::fs::create_link("real_cas", Temp.path("sym_cas2")),
+ std::error_code());
+ ASSERT_EQ(sys::fs::create_link("real_cas", Temp.path("sym_cas3")),
+ std::error_code());
+
+ std::unique_ptr<ObjectStore> CAS1, CAS2, CAS3, CAS4;
+ ASSERT_THAT_ERROR(createOnDiskCAS(Temp.path("real_cas")).moveInto(CAS1),
+ Succeeded());
+ ASSERT_THAT_ERROR(createOnDiskCAS(Temp.path("sym_cas1")).moveInto(CAS2),
+ Succeeded());
+ ASSERT_THAT_ERROR(createOnDiskCAS(Temp.path("sym_cas2")).moveInto(CAS3),
+ Succeeded());
+ ASSERT_THAT_ERROR(createOnDiskCAS(Temp.path("sym_cas3")).moveInto(CAS4),
+ Succeeded());
+
+ uint64_t Size = 1ULL * 1024;
+ ASSERT_NO_FATAL_FAILURE(testBlobsParallel(*CAS1, *CAS2, *CAS3, *CAS4, Size));
+}
+
+TEST_F(OnDiskCASTest, OnDiskCASBlobsBigParallelMultiCAS) {
+ // See comment in BlobsParallelMultiCAS.
+ unittest::TempDir Temp("on-disk-cas", /*Unique=*/true);
+ ASSERT_EQ(sys::fs::create_directory(Temp.path("real_cas")),
+ std::error_code());
+ ASSERT_EQ(sys::fs::create_link("real_cas", Temp.path("sym_cas1")),
+ std::error_code());
+ ASSERT_EQ(sys::fs::create_link("real_cas", Temp.path("sym_cas2")),
+ std::error_code());
+ ASSERT_EQ(sys::fs::create_link("real_cas", Temp.path("sym_cas3")),
+ std::error_code());
+
+ std::unique_ptr<ObjectStore> CAS1, CAS2, CAS3, CAS4;
+ ASSERT_THAT_ERROR(createOnDiskCAS(Temp.path("real_cas")).moveInto(CAS1),
+ Succeeded());
+ ASSERT_THAT_ERROR(createOnDiskCAS(Temp.path("sym_cas1")).moveInto(CAS2),
+ Succeeded());
+ ASSERT_THAT_ERROR(createOnDiskCAS(Temp.path("sym_cas2")).moveInto(CAS3),
+ Succeeded());
+ ASSERT_THAT_ERROR(createOnDiskCAS(Temp.path("sym_cas3")).moveInto(CAS4),
+ Succeeded());
+
+ // 100k is large enough to be standalone files in our on-disk cas.
+ uint64_t Size = 100ULL * 1024;
+ ASSERT_NO_FATAL_FAILURE(testBlobsParallel(*CAS1, *CAS2, *CAS3, *CAS4, Size));
+}
+#endif // _WIN32
#endif // LLVM_ENABLE_THREADS
+
+TEST_F(OnDiskCASTest, OnDiskCASDiskSize) {
+ unittest::TempDir Temp("on-disk-cas", /*Unique=*/true);
+ std::unique_ptr<ObjectStore> CAS;
+ ASSERT_THAT_ERROR(createOnDiskCAS(Temp.path()).moveInto(CAS), Succeeded());
+
+ uint64_t MaxSize = 100 * 1024 * 1024;
+
+ // Check that we map the files to the correct size.
+ auto CheckFileSizes = [&](bool Mapped) {
+ bool FoundIndex = false, FoundData = false;
+ std::error_code EC;
+ for (sys::fs::directory_iterator I(Temp.path(), EC), E; I != E && !EC;
+ I.increment(EC)) {
+ StringRef Filename = sys::path::filename(I->path());
+ if (Filename.starts_with("index.") && !Filename.ends_with(".shared")) {
+ FoundIndex = true;
+ ASSERT_TRUE(I->status());
+ if (Mapped)
+ EXPECT_EQ(I->status()->getSize(), MaxSize);
+ else
+ EXPECT_LT(I->status()->getSize(), MaxSize);
+ }
+ if (Filename.starts_with("data.") && !Filename.ends_with(".shared")) {
+ FoundData = true;
+ ASSERT_TRUE(I->status());
+ if (Mapped)
+ EXPECT_EQ(I->status()->getSize(), MaxSize);
+ else
+ EXPECT_LT(I->status()->getSize(), MaxSize);
+ }
+ }
+ ASSERT_TRUE(FoundIndex);
+ ASSERT_TRUE(FoundData);
+ };
+
+ // Check that we have the full mapping size when the CAS is open.
+ CheckFileSizes(/*Mapped=*/true);
+ CAS.reset();
+ // Check that the CAS is shrunk to a smaller size.
+ CheckFileSizes(/*Mapped=*/false);
+
+ // Repeat the checks when starting from an existing CAS.
+ ASSERT_THAT_ERROR(createOnDiskCAS(Temp.path()).moveInto(CAS), Succeeded());
+ CheckFileSizes(/*Mapped=*/true);
+ CAS.reset();
+ CheckFileSizes(/*Mapped=*/false);
+}
diff --git a/llvm/unittests/CAS/OnDiskCommonUtils.h b/llvm/unittests/CAS/OnDiskCommonUtils.h
index 89f93e0..48a1830 100644
--- a/llvm/unittests/CAS/OnDiskCommonUtils.h
+++ b/llvm/unittests/CAS/OnDiskCommonUtils.h
@@ -12,6 +12,8 @@
#include "llvm/CAS/BuiltinObjectHasher.h"
#include "llvm/CAS/OnDiskGraphDB.h"
+#include "llvm/CAS/OnDiskKeyValueDB.h"
+#include "llvm/CAS/UnifiedOnDiskCache.h"
#include "llvm/Support/BLAKE3.h"
#include "llvm/Testing/Support/Error.h"
@@ -58,6 +60,25 @@ inline Expected<ObjectID> store(OnDiskGraphDB &DB, StringRef Data,
return ID;
}
+inline Expected<ObjectID> cachePut(OnDiskKeyValueDB &DB, ArrayRef<uint8_t> Key,
+ ObjectID ID) {
+ auto Value = UnifiedOnDiskCache::getValueFromObjectID(ID);
+ auto Result = DB.put(Key, Value);
+ if (!Result)
+ return Result.takeError();
+ return UnifiedOnDiskCache::getObjectIDFromValue(*Result);
+}
+
+inline Expected<std::optional<ObjectID>> cacheGet(OnDiskKeyValueDB &DB,
+ ArrayRef<uint8_t> Key) {
+ auto Result = DB.get(Key);
+ if (!Result)
+ return Result.takeError();
+ if (!*Result)
+ return std::nullopt;
+ return UnifiedOnDiskCache::getObjectIDFromValue(**Result);
+}
+
inline Error printTree(OnDiskGraphDB &DB, ObjectID ID, raw_ostream &OS,
unsigned Indent = 0) {
std::optional<ondisk::ObjectHandle> Obj;
diff --git a/llvm/unittests/CAS/OnDiskGraphDBTest.cpp b/llvm/unittests/CAS/OnDiskGraphDBTest.cpp
index 3c2e963..e9c73bf 100644
--- a/llvm/unittests/CAS/OnDiskGraphDBTest.cpp
+++ b/llvm/unittests/CAS/OnDiskGraphDBTest.cpp
@@ -102,7 +102,7 @@ TEST_F(OnDiskCASTest, OnDiskGraphDBFaultInSingleNode) {
std::unique_ptr<OnDiskGraphDB> DB;
ASSERT_THAT_ERROR(
OnDiskGraphDB::open(Temp.path(), "blake3", sizeof(HashType),
- std::move(UpstreamDB),
+ UpstreamDB.get(),
OnDiskGraphDB::FaultInPolicy::SingleNode)
.moveInto(DB),
Succeeded());
@@ -208,7 +208,7 @@ TEST_F(OnDiskCASTest, OnDiskGraphDBFaultInFullTree) {
unittest::TempDir Temp("ondiskcas", /*Unique=*/true);
std::unique_ptr<OnDiskGraphDB> DB;
ASSERT_THAT_ERROR(OnDiskGraphDB::open(Temp.path(), "blake3", sizeof(HashType),
- std::move(UpstreamDB),
+ UpstreamDB.get(),
OnDiskGraphDB::FaultInPolicy::FullTree)
.moveInto(DB),
Succeeded());
@@ -264,14 +264,14 @@ TEST_F(OnDiskCASTest, OnDiskGraphDBFaultInPolicyConflict) {
unittest::TempDir Temp("ondiskcas", /*Unique=*/true);
std::unique_ptr<OnDiskGraphDB> DB;
ASSERT_THAT_ERROR(OnDiskGraphDB::open(Temp.path(), "blake3",
- sizeof(HashType),
- std::move(UpstreamDB), Policy1)
+ sizeof(HashType), UpstreamDB.get(),
+ Policy1)
.moveInto(DB),
Succeeded());
DB.reset();
ASSERT_THAT_ERROR(OnDiskGraphDB::open(Temp.path(), "blake3",
- sizeof(HashType),
- std::move(UpstreamDB), Policy2)
+ sizeof(HashType), UpstreamDB.get(),
+ Policy2)
.moveInto(DB),
Failed());
};
diff --git a/llvm/unittests/CAS/UnifiedOnDiskCacheTest.cpp b/llvm/unittests/CAS/UnifiedOnDiskCacheTest.cpp
new file mode 100644
index 0000000..09aebc2
--- /dev/null
+++ b/llvm/unittests/CAS/UnifiedOnDiskCacheTest.cpp
@@ -0,0 +1,198 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "llvm/CAS/UnifiedOnDiskCache.h"
+#include "CASTestConfig.h"
+#include "OnDiskCommonUtils.h"
+#include "llvm/Testing/Support/Error.h"
+#include "llvm/Testing/Support/SupportHelpers.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace llvm::cas;
+using namespace llvm::cas::ondisk;
+using namespace llvm::unittest::cas;
+
+/// Visits all the files of a directory recursively and returns the sum of their
+/// sizes.
+static Expected<size_t> countFileSizes(StringRef Path) {
+ size_t TotalSize = 0;
+ std::error_code EC;
+ for (sys::fs::directory_iterator DirI(Path, EC), DirE; !EC && DirI != DirE;
+ DirI.increment(EC)) {
+ if (DirI->type() == sys::fs::file_type::directory_file) {
+ Expected<size_t> Subsize = countFileSizes(DirI->path());
+ if (!Subsize)
+ return Subsize.takeError();
+ TotalSize += *Subsize;
+ continue;
+ }
+ ErrorOr<sys::fs::basic_file_status> Stat = DirI->status();
+ if (!Stat)
+ return createFileError(DirI->path(), Stat.getError());
+ TotalSize += Stat->getSize();
+ }
+ if (EC)
+ return createFileError(Path, EC);
+ return TotalSize;
+}
+
+TEST_F(OnDiskCASTest, UnifiedOnDiskCacheTest) {
+ unittest::TempDir Temp("ondisk-unified", /*Unique=*/true);
+ std::unique_ptr<UnifiedOnDiskCache> UniDB;
+
+ const uint64_t SizeLimit = 1024ull * 64;
+ auto reopenDB = [&]() {
+ UniDB.reset();
+ ASSERT_THAT_ERROR(UnifiedOnDiskCache::open(Temp.path(), SizeLimit, "blake3",
+ sizeof(HashType))
+ .moveInto(UniDB),
+ Succeeded());
+ };
+
+ reopenDB();
+
+ HashType RootHash;
+ HashType OtherHash;
+ HashType Key1Hash;
+ HashType Key2Hash;
+ {
+ OnDiskGraphDB &DB = UniDB->getGraphDB();
+ std::optional<ObjectID> ID1;
+ ASSERT_THAT_ERROR(store(DB, "1", {}).moveInto(ID1), Succeeded());
+ std::optional<ObjectID> ID2;
+ ASSERT_THAT_ERROR(store(DB, "2", {}).moveInto(ID2), Succeeded());
+ std::optional<ObjectID> IDRoot;
+ ASSERT_THAT_ERROR(store(DB, "root", {*ID1, *ID2}).moveInto(IDRoot),
+ Succeeded());
+ ArrayRef<uint8_t> Digest = DB.getDigest(*IDRoot);
+ ASSERT_EQ(Digest.size(), RootHash.size());
+ llvm::copy(Digest, RootHash.data());
+
+ std::optional<ObjectID> IDOther;
+ ASSERT_THAT_ERROR(store(DB, "other", {}).moveInto(IDOther), Succeeded());
+ Digest = DB.getDigest(*IDOther);
+ ASSERT_EQ(Digest.size(), OtherHash.size());
+ llvm::copy(Digest, OtherHash.data());
+
+ Key1Hash = digest("key1");
+ std::optional<ObjectID> Val;
+ ASSERT_THAT_ERROR(
+ cachePut(UniDB->getKeyValueDB(), Key1Hash, *IDRoot).moveInto(Val),
+ Succeeded());
+ EXPECT_EQ(IDRoot, Val);
+
+ Key2Hash = digest("key2");
+ std::optional<ObjectID> KeyID;
+ ASSERT_THAT_ERROR(DB.getReference(Key2Hash).moveInto(KeyID), Succeeded());
+ ASSERT_THAT_ERROR(cachePut(UniDB->getKeyValueDB(),
+ UniDB->getGraphDB().getDigest(*KeyID), *ID1)
+ .moveInto(Val),
+ Succeeded());
+ }
+
+ auto checkTree = [&](const HashType &Digest, StringRef ExpectedTree) {
+ OnDiskGraphDB &DB = UniDB->getGraphDB();
+ std::optional<ObjectID> ID;
+ ASSERT_THAT_ERROR(DB.getReference(Digest).moveInto(ID), Succeeded());
+ std::string PrintedTree;
+ raw_string_ostream OS(PrintedTree);
+ ASSERT_THAT_ERROR(printTree(DB, *ID, OS), Succeeded());
+ EXPECT_EQ(PrintedTree, ExpectedTree);
+ };
+ auto checkRootTree = [&]() {
+ return checkTree(RootHash, "root\n 1\n 2\n");
+ };
+
+ auto checkKey = [&](const HashType &Key, StringRef ExpectedData) {
+ OnDiskGraphDB &DB = UniDB->getGraphDB();
+ std::optional<ObjectID> Val;
+ ASSERT_THAT_ERROR(cacheGet(UniDB->getKeyValueDB(), Key).moveInto(Val),
+ Succeeded());
+
+ ASSERT_TRUE(Val.has_value());
+ std::optional<ondisk::ObjectHandle> Obj;
+ ASSERT_THAT_ERROR(DB.load(*Val).moveInto(Obj), Succeeded());
+ EXPECT_EQ(toStringRef(DB.getObjectData(*Obj)), ExpectedData);
+ };
+
+ checkRootTree();
+ checkTree(OtherHash, "other\n");
+ checkKey(Key1Hash, "root");
+ checkKey(Key2Hash, "1");
+
+ auto storeBigObject = [&](unsigned Index) {
+ SmallString<1000> Buf;
+ Buf.append(970, 'a');
+ raw_svector_ostream(Buf) << Index;
+ std::optional<ObjectID> ID;
+ ASSERT_THAT_ERROR(store(UniDB->getGraphDB(), Buf, {}).moveInto(ID),
+ Succeeded());
+ };
+
+ uint64_t PrevStoreSize = UniDB->getStorageSize();
+ unsigned Index = 0;
+ while (!UniDB->hasExceededSizeLimit()) {
+ storeBigObject(Index++);
+ }
+ EXPECT_GT(UniDB->getStorageSize(), PrevStoreSize);
+ UniDB->setSizeLimit(SizeLimit * 2);
+ EXPECT_FALSE(UniDB->hasExceededSizeLimit());
+ UniDB->setSizeLimit(SizeLimit);
+ EXPECT_TRUE(UniDB->hasExceededSizeLimit());
+
+ reopenDB();
+
+ EXPECT_FALSE(UniDB->hasExceededSizeLimit());
+ EXPECT_FALSE(UniDB->needsGarbageCollection());
+
+ checkRootTree();
+ checkKey(Key1Hash, "root");
+
+ while (!UniDB->hasExceededSizeLimit()) {
+ storeBigObject(Index++);
+ }
+ PrevStoreSize = UniDB->getStorageSize();
+ ASSERT_THAT_ERROR(UniDB->close(), Succeeded());
+ EXPECT_TRUE(UniDB->needsGarbageCollection());
+
+ reopenDB();
+ EXPECT_TRUE(UniDB->needsGarbageCollection());
+
+ std::optional<size_t> DirSizeBefore;
+ ASSERT_THAT_ERROR(countFileSizes(Temp.path()).moveInto(DirSizeBefore),
+ Succeeded());
+
+ ASSERT_THAT_ERROR(UnifiedOnDiskCache::collectGarbage(Temp.path()),
+ Succeeded());
+
+ std::optional<size_t> DirSizeAfter;
+ ASSERT_THAT_ERROR(countFileSizes(Temp.path()).moveInto(DirSizeAfter),
+ Succeeded());
+ EXPECT_LT(*DirSizeAfter, *DirSizeBefore);
+
+ reopenDB();
+ EXPECT_FALSE(UniDB->needsGarbageCollection());
+
+ checkRootTree();
+ checkKey(Key1Hash, "root");
+
+ EXPECT_LT(UniDB->getStorageSize(), PrevStoreSize);
+
+ // 'Other' tree and 'Key2' got garbage-collected.
+ {
+ OnDiskGraphDB &DB = UniDB->getGraphDB();
+ std::optional<ObjectID> ID;
+ ASSERT_THAT_ERROR(DB.getReference(OtherHash).moveInto(ID), Succeeded());
+ EXPECT_FALSE(DB.containsObject(*ID));
+ std::optional<ObjectID> Val;
+ ASSERT_THAT_ERROR(cacheGet(UniDB->getKeyValueDB(), Key2Hash).moveInto(Val),
+ Succeeded());
+ EXPECT_FALSE(Val.has_value());
+ }
+}