CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
271 lines
9.8 KiB
C++
271 lines
9.8 KiB
C++
//===----------------------------------------------------------------------===//
|
|
//
|
|
// 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/NamedValuesSchema.h"
|
|
#include "llvm/CAS/ObjectStore.h"
|
|
#include "llvm/Testing/Support/Error.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
using namespace llvm;
|
|
using namespace llvm::cas;
|
|
|
|
TEST(NamedValuesSchemaTest, Basic) {
|
|
std::unique_ptr<ObjectStore> CAS1 = createInMemoryCAS();
|
|
std::unique_ptr<ObjectStore> CAS2 = createInMemoryCAS();
|
|
|
|
auto createBlobInBoth = [&](StringRef Content) {
|
|
std::optional<ObjectRef> H1, H2;
|
|
EXPECT_THAT_ERROR(CAS1->storeFromString({}, Content).moveInto(H1),
|
|
Succeeded());
|
|
EXPECT_THAT_ERROR(CAS2->storeFromString({}, Content).moveInto(H2),
|
|
Succeeded());
|
|
EXPECT_EQ(CAS1->getID(*H1), CAS2->getID(*H2));
|
|
return *H1;
|
|
};
|
|
|
|
ObjectRef Blob1 = createBlobInBoth("blob1");
|
|
ObjectRef Blob2 = createBlobInBoth("blob2");
|
|
ObjectRef Blob3 = createBlobInBoth("blob3");
|
|
|
|
SmallVector<SmallVector<NamedValuesEntry, 0>, 0> FlatEntries = {
|
|
{},
|
|
{NamedValuesEntry("regular", Blob1)},
|
|
{NamedValuesEntry("executable", Blob2)},
|
|
{NamedValuesEntry("symlink", Blob3)},
|
|
{
|
|
NamedValuesEntry("various", Blob1),
|
|
NamedValuesEntry("names", Blob1),
|
|
NamedValuesEntry("that", Blob1),
|
|
NamedValuesEntry("do", Blob1),
|
|
NamedValuesEntry("not", Blob1),
|
|
NamedValuesEntry("conflict", Blob1),
|
|
NamedValuesEntry("but have spaces and...", Blob1),
|
|
NamedValuesEntry("`~,!@#$%^&*()-+=[]{}\\<>'\"", Blob1),
|
|
},
|
|
};
|
|
|
|
SmallVector<ObjectRef> FlatRefs;
|
|
SmallVector<CASID> FlatIDs;
|
|
NamedValuesSchema Schema1 = cantFail(NamedValuesSchema::create(*CAS1));
|
|
|
|
for (ArrayRef<NamedValuesEntry> Entries : FlatEntries) {
|
|
std::optional<NamedValuesProxy> H;
|
|
ASSERT_THAT_ERROR(Schema1.construct(Entries).moveInto(H), Succeeded());
|
|
FlatIDs.push_back(H->getID());
|
|
FlatRefs.push_back(H->getRef());
|
|
}
|
|
|
|
// Confirm we get the same IDs the second time and that the trees can be
|
|
// visited (the entries themselves will be checked later).
|
|
for (int I = 0, E = FlatIDs.size(); I != E; ++I) {
|
|
std::optional<NamedValuesProxy> H;
|
|
ASSERT_THAT_ERROR(Schema1.construct(FlatEntries[I]).moveInto(H),
|
|
Succeeded());
|
|
EXPECT_EQ(FlatRefs[I], CAS1->getReference(*H));
|
|
std::optional<NamedValuesProxy> Value;
|
|
ASSERT_THAT_ERROR(Schema1.load(*H).moveInto(Value), Succeeded());
|
|
EXPECT_EQ(FlatEntries[I].size(), Value->size());
|
|
|
|
size_t NumCalls = 0;
|
|
EXPECT_THAT_ERROR(
|
|
Value->forEachEntry([&NumCalls](const NamedValuesEntry &E) {
|
|
++NumCalls;
|
|
return Error::success();
|
|
}),
|
|
Succeeded());
|
|
EXPECT_EQ(FlatEntries[I].size(), NumCalls);
|
|
}
|
|
|
|
// Run validation.
|
|
for (int I = 1, E = FlatIDs.size(); I != E; ++I)
|
|
ASSERT_THAT_ERROR(CAS1->validateObject(FlatIDs[I]), Succeeded());
|
|
|
|
// Confirm these trees don't exist in a fresh CAS instance. Skip the first
|
|
// tree, which is empty and could be implicitly in some CAS.
|
|
for (int I = 1, E = FlatIDs.size(); I != E; ++I)
|
|
EXPECT_FALSE(CAS2->getReference(FlatIDs[I]));
|
|
|
|
// Insert into the other CAS and confirm the IDs are stable.
|
|
for (int I = FlatIDs.size(), E = 0; I != E; --I) {
|
|
for (ObjectStore *CAS : {&*CAS1, &*CAS2}) {
|
|
NamedValuesSchema Schema = cantFail(NamedValuesSchema::create(*CAS));
|
|
auto &ID = FlatIDs[I - 1];
|
|
// Make a copy of the original entries and sort them.
|
|
SmallVector<NamedValuesEntry> NewEntries;
|
|
for (const NamedValuesEntry &Entry : FlatEntries[I - 1]) {
|
|
std::optional<ObjectRef> NewRef =
|
|
CAS->getReference(CAS1->getID(Entry.Ref));
|
|
ASSERT_TRUE(NewRef);
|
|
NewEntries.emplace_back(Entry.Name, *NewRef);
|
|
}
|
|
llvm::sort(NewEntries);
|
|
|
|
// Confirm we get the same tree out of CAS2.
|
|
{
|
|
std::optional<NamedValuesProxy> Value;
|
|
ASSERT_THAT_ERROR(Schema.construct(NewEntries).moveInto(Value),
|
|
Succeeded());
|
|
EXPECT_EQ(ID, Value->getID());
|
|
}
|
|
|
|
// Check that the correct entries come back.
|
|
std::optional<ObjectRef> Ref = CAS->getReference(ID);
|
|
ASSERT_TRUE(Ref);
|
|
std::optional<NamedValuesProxy> Value;
|
|
ASSERT_THAT_ERROR(Schema.load(*Ref).moveInto(Value), Succeeded());
|
|
for (int I = 0, E = NewEntries.size(); I != E; ++I)
|
|
EXPECT_EQ(NewEntries[I], Value->get(I));
|
|
}
|
|
}
|
|
|
|
// Create some nested trees.
|
|
SmallVector<ObjectRef> Nested = FlatRefs;
|
|
for (int I = 0, E = FlatEntries.size() * 3; I != E; ++I) {
|
|
// Copy one of the flat entries and add some trees.
|
|
auto OriginalEntries = ArrayRef(FlatEntries[I % FlatEntries.size()]);
|
|
SmallVector<NamedValuesEntry> Entries(OriginalEntries.begin(),
|
|
OriginalEntries.end());
|
|
std::string Name = ("tree" + Twine(I)).str();
|
|
Entries.emplace_back(
|
|
Name, *CAS1->getReference(FlatIDs[(I + 4) % FlatIDs.size()]));
|
|
|
|
std::optional<std::string> Name1, Name2;
|
|
if (Nested.size() >= 2) {
|
|
int Nested1 = I % Nested.size();
|
|
int Nested2 = (I * 3 + 2) % Nested.size();
|
|
if (Nested2 == Nested1)
|
|
Nested2 = (Nested1 + 1) % Nested.size();
|
|
ASSERT_NE(Nested1, Nested2);
|
|
Name1.emplace(("tree" + Twine(I) + "-" + Twine(Nested1)).str());
|
|
Name2.emplace(("tree" + Twine(I) + "-" + Twine(Nested2)).str());
|
|
|
|
Entries.emplace_back(*Name1, Nested[I % Nested.size()]);
|
|
Entries.emplace_back(*Name2, Nested[(I * 3 + 2) % Nested.size()]);
|
|
}
|
|
std::optional<CASID> ID;
|
|
{
|
|
std::optional<NamedValuesProxy> Value;
|
|
ASSERT_THAT_ERROR(Schema1.construct(Entries).moveInto(Value),
|
|
Succeeded());
|
|
ID = Value->getID();
|
|
}
|
|
|
|
llvm::sort(Entries);
|
|
for (ObjectStore *CAS : {&*CAS1, &*CAS2}) {
|
|
NamedValuesSchema Schema = cantFail(NamedValuesSchema::create(*CAS));
|
|
|
|
// Make a copy of the original entries and sort them.
|
|
SmallVector<NamedValuesEntry> NewEntries;
|
|
for (const NamedValuesEntry &Entry : Entries) {
|
|
std::optional<ObjectRef> NewRef =
|
|
CAS->getReference(CAS1->getID(Entry.Ref));
|
|
ASSERT_TRUE(NewRef);
|
|
NewEntries.emplace_back(Entry.Name, *NewRef);
|
|
}
|
|
llvm::sort(NewEntries);
|
|
|
|
std::optional<NamedValuesProxy> Value;
|
|
ASSERT_THAT_ERROR(Schema.construct(NewEntries).moveInto(Value),
|
|
Succeeded());
|
|
ASSERT_EQ(*ID, Value->getID());
|
|
ASSERT_THAT_ERROR(CAS->validateObject(*ID), Succeeded());
|
|
Value.reset();
|
|
std::optional<ObjectRef> Ref = CAS->getReference(*ID);
|
|
ASSERT_TRUE(Ref);
|
|
ASSERT_THAT_ERROR(Schema.load(*Ref).moveInto(Value), Succeeded());
|
|
for (int I = 0, E = NewEntries.size(); I != E; ++I)
|
|
EXPECT_EQ(NewEntries[I], Value->get(I));
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(NamedValuesSchemaTest, Lookup) {
|
|
std::unique_ptr<ObjectStore> CAS = createInMemoryCAS();
|
|
std::optional<ObjectRef> Node;
|
|
EXPECT_THAT_ERROR(CAS->storeFromString({}, "blob").moveInto(Node),
|
|
Succeeded());
|
|
ObjectRef Blob = *Node;
|
|
SmallVector<NamedValuesEntry> FlatEntries = {
|
|
NamedValuesEntry("e", Blob), NamedValuesEntry("b", Blob),
|
|
NamedValuesEntry("f", Blob), NamedValuesEntry("a", Blob),
|
|
NamedValuesEntry("c", Blob), NamedValuesEntry("d", Blob),
|
|
};
|
|
std::optional<NamedValuesProxy> Value;
|
|
NamedValuesSchema Schema = cantFail(NamedValuesSchema::create(*CAS));
|
|
|
|
ASSERT_THAT_ERROR(Schema.construct(FlatEntries).moveInto(Value), Succeeded());
|
|
|
|
ASSERT_EQ(Value->size(), (size_t)6);
|
|
auto CheckEntry = [&](StringRef Name) {
|
|
auto MaybeEntry = Value->lookup(Name);
|
|
ASSERT_TRUE(MaybeEntry);
|
|
ASSERT_EQ(MaybeEntry->Name, Name);
|
|
};
|
|
CheckEntry("a");
|
|
CheckEntry("b");
|
|
CheckEntry("c");
|
|
CheckEntry("d");
|
|
CheckEntry("e");
|
|
CheckEntry("f");
|
|
ASSERT_FALSE(Value->lookup("h"));
|
|
}
|
|
|
|
TEST(NamedValuesSchemaTest, Builder) {
|
|
std::unique_ptr<ObjectStore> CAS = createInMemoryCAS();
|
|
ObjectRef Blob = cantFail(CAS->storeFromString({}, ""));
|
|
|
|
NamedValuesSchema::Builder Builder(*CAS);
|
|
Builder.add("a", Blob);
|
|
Builder.add("a", Blob);
|
|
ASSERT_THAT_EXPECTED(Builder.build(), Failed());
|
|
|
|
NamedValuesSchema::Builder Builder2(*CAS);
|
|
Builder2.add("a", Blob);
|
|
Builder2.add("b", Blob);
|
|
ASSERT_THAT_EXPECTED(Builder2.build(), Succeeded());
|
|
}
|
|
|
|
TEST(NamedValuesSchemaTest, forEachEntry) {
|
|
std::unique_ptr<ObjectStore> CAS = createInMemoryCAS();
|
|
|
|
auto make = [&](StringRef Content) {
|
|
return cantFail(CAS->storeFromString({}, Content));
|
|
};
|
|
|
|
std::pair<std::string, std::string> ExpectedEntries[] = {
|
|
{"i", "blob2"},
|
|
{"l", "blob1"},
|
|
{"s", "blob3"},
|
|
{"t", "blob4"},
|
|
};
|
|
|
|
NamedValuesSchema::Builder Builder(*CAS);
|
|
for (auto &E : ExpectedEntries)
|
|
Builder.add(E.first, make(E.second));
|
|
|
|
std::optional<ObjectProxy> Root;
|
|
ASSERT_THAT_ERROR(Builder.build().moveInto(Root), Succeeded());
|
|
|
|
auto RemainingEntries = ArrayRef(ExpectedEntries);
|
|
|
|
NamedValuesSchema Schema = cantFail(NamedValuesSchema::create(*CAS));
|
|
std::optional<NamedValuesProxy> Loaded;
|
|
ASSERT_THAT_ERROR(Schema.load(*Root).moveInto(Loaded), Succeeded());
|
|
Error E = Loaded->forEachEntry([&](const NamedValuesEntry &Entry) -> Error {
|
|
if (RemainingEntries.empty())
|
|
return createStringError(inconvertibleErrorCode(),
|
|
"unexpected entry: '" + Entry.Name + "'");
|
|
auto ExpectedEntry = RemainingEntries.front();
|
|
RemainingEntries = RemainingEntries.drop_front();
|
|
EXPECT_EQ(ExpectedEntry.first, Entry.Name);
|
|
EXPECT_EQ(make(ExpectedEntry.second), Entry.Ref);
|
|
return Error::success();
|
|
});
|
|
EXPECT_THAT_ERROR(std::move(E), Succeeded());
|
|
}
|