mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
3904e525b0
This introduces `emplace` and `emplace_or_assign` members allowing population of the container without copying values in. It also reworks the batch insertion. On top of adding the ability to move the elements in (without making a copy) and constructing them in place, it also makes the non-assign version of the inserter a lot safer by explicitly asserting the fact that while it was in use no duplicates were added to the vector. Before now, the duplicates were just silently removed from the container. It also optimizes the batch insertion somewhat by using non-stable `std::sort` for unique case as well as replacing `std::unique` invocation with `std::find_adjacent`.
334 lines
9.0 KiB
C++
334 lines
9.0 KiB
C++
/// \file KeyedObjectsContainers.cpp
|
|
/// \brief Tests for MutableSet and SortedVector
|
|
|
|
//
|
|
// This file is distributed under the MIT License. See LICENSE.md for details.
|
|
//
|
|
|
|
#define BOOST_TEST_MODULE KeyedObjectsContainers
|
|
bool init_unit_test();
|
|
#include "boost/test/unit_test.hpp"
|
|
|
|
#include "revng/ADT/MutableSet.h"
|
|
#include "revng/ADT/SortedVector.h"
|
|
#include "revng/ADT/TrackingContainer.h"
|
|
#include "revng/Support/Assert.h"
|
|
|
|
#include "TestKeyedObject.h"
|
|
|
|
template<typename T>
|
|
static void assertInsert(T &Set, uint64_t Key, uint64_t Value) {
|
|
auto [It, Success] = Set.insert({ Key, Value });
|
|
revng_check(It->key() == Key and It->value() == Value);
|
|
revng_check(Success);
|
|
}
|
|
|
|
template<typename T>
|
|
void testSet() {
|
|
T Set;
|
|
revng_check(Set.empty());
|
|
|
|
// Test copy/move constructor/assign
|
|
{
|
|
T Original;
|
|
T Copy(Original);
|
|
Copy = Original;
|
|
Copy = std::move(Original);
|
|
|
|
T Moved(std::move(Copy));
|
|
}
|
|
|
|
// Test insertion
|
|
assertInsert(Set, 0x2000, 0xDEAD);
|
|
assertInsert(Set, 0x1000, 0xDEADDEAD);
|
|
assertInsert(Set, 0x3000, 0xDEADDEADDEAD);
|
|
|
|
revng_check(not Set.empty());
|
|
revng_check(Set.size() == 3);
|
|
|
|
using IterationResultType = std::vector<std::pair<uint64_t, uint64_t>>;
|
|
IterationResultType ExpectedResult{ { 0x1000, 0xDEADDEAD },
|
|
{ 0x2000, 0xDEAD },
|
|
{ 0x3000, 0xDEADDEADDEAD } };
|
|
|
|
// Test forward iteration
|
|
{
|
|
IterationResultType IterationResult;
|
|
for (const Element &SE : Set)
|
|
IterationResult.emplace_back(SE.key(), SE.value());
|
|
|
|
revng_check(IterationResult == ExpectedResult);
|
|
}
|
|
|
|
// Test backward iteration
|
|
{
|
|
IterationResultType IterationResult;
|
|
for (const Element &SE : llvm::make_range(Set.rbegin(), Set.rend()))
|
|
IterationResult.emplace_back(SE.key(), SE.value());
|
|
std::reverse(ExpectedResult.begin(), ExpectedResult.end());
|
|
revng_check(IterationResult == ExpectedResult);
|
|
std::reverse(ExpectedResult.begin(), ExpectedResult.end());
|
|
}
|
|
|
|
// Test erase
|
|
{
|
|
revng_check(Set.find(0x1500) == Set.end());
|
|
|
|
auto [It, Success] = Set.insert({ 0x1500, 0xEEEE });
|
|
revng_check(Success);
|
|
auto Next = Set.erase(It);
|
|
revng_check(Next == Set.find(0x2000));
|
|
|
|
revng_check(Set.find(0x1500) == Set.end());
|
|
|
|
IterationResultType IterationResult;
|
|
for (const Element &SE : Set)
|
|
IterationResult.emplace_back(SE.key(), SE.value());
|
|
revng_check(IterationResult == ExpectedResult);
|
|
}
|
|
|
|
// Test find
|
|
{
|
|
auto It = Set.find(0x1000);
|
|
revng_check(It != Set.end());
|
|
revng_check(It->key() == 0x1000);
|
|
revng_check(It->value() == 0xDEADDEAD);
|
|
}
|
|
|
|
// insert vs insert_or_assign
|
|
{
|
|
auto [It, Success] = Set.insert({ 0x1000, 0x5555 });
|
|
revng_check(Set.find(0x1000) == It);
|
|
revng_check(not Success);
|
|
revng_check(Set[0x1000].value() == 0xDEADDEAD);
|
|
}
|
|
|
|
{
|
|
auto [It, Success] = Set.insert_or_assign({ 0x1000, 0x6666 });
|
|
revng_check(not Success);
|
|
revng_check(Set[0x1000].value() == 0x6666);
|
|
Set.insert_or_assign({ 0x1000, 0xDEADDEAD });
|
|
}
|
|
|
|
// Test operator[] on existing element
|
|
revng_check(Set[0x1000].key() == 0x1000);
|
|
revng_check(Set[0x1000].value() == 0xDEADDEAD);
|
|
|
|
// Test operator[] on new element
|
|
revng_check(Set[0x2500].key() == 0x2500);
|
|
revng_check(Set.size() == 4);
|
|
revng_check(Set.find(0x2500) != Set.end());
|
|
revng_check(Set[0x2500].value() == 0);
|
|
|
|
// Test elements are mutable
|
|
Set[0x2500].setValue(0x90);
|
|
revng_check(Set[0x2500].value() == 0x90);
|
|
|
|
// Test batch_insert
|
|
{
|
|
auto Inserter = Set.batch_insert();
|
|
Inserter.insert({ 0x1100, 0x2222 });
|
|
Inserter.insert({ 0x900, 0x1111 });
|
|
}
|
|
revng_check(Set[0x1000].value() == 0xDEADDEAD);
|
|
revng_check(Set[0x900].value() == 0x1111);
|
|
|
|
// Test batch_insert_or_assign
|
|
{
|
|
auto Inserter = Set.batch_insert_or_assign();
|
|
Inserter.insert_or_assign({ 0x1000, 0x2222 });
|
|
Inserter.insert_or_assign({ 0x900, 0x3333 });
|
|
}
|
|
revng_check(Set[0x1000].value() == 0x2222);
|
|
revng_check(Set[0x1100].value() == 0x2222);
|
|
revng_check(Set[0x900].value() == 0x3333);
|
|
|
|
// Test clear
|
|
Set.clear();
|
|
|
|
revng_check(Set.empty());
|
|
revng_check(Set.size() == 0);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(TestMutableSet) {
|
|
testSet<MutableSet<Element>>();
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(TestSortedVector) {
|
|
testSet<SortedVector<Element>>();
|
|
}
|
|
|
|
template<typename T>
|
|
bool isSerializationStable(T &&Original) {
|
|
std::string Buffer;
|
|
|
|
{
|
|
llvm::raw_string_ostream Stream(Buffer);
|
|
llvm::yaml::Output YAMLOutput(Stream);
|
|
YAMLOutput << Original;
|
|
}
|
|
|
|
T Deserialized;
|
|
llvm::yaml::Input YAMLInput(Buffer);
|
|
YAMLInput >> Deserialized;
|
|
|
|
return Original == Deserialized;
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(TestYAMLSerializationStability) {
|
|
revng_check(isSerializationStable(SortedVector<int>{ 4, 19, 7 }));
|
|
revng_check(isSerializationStable(MutableSet<int>{ 4, 19, 7 }));
|
|
|
|
SortedVector<Element> TestSV{ { 1, 2 }, { 2, 3 } };
|
|
revng_check(isSerializationStable(std::move(TestSV)));
|
|
|
|
MutableSet<Element> TestMS{ { 1, 2 }, { 2, 3 } };
|
|
revng_check(isSerializationStable(std::move(TestMS)));
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(TestUniqueLast) {
|
|
using IntPair = std::pair<int, int>;
|
|
using Vector = std::vector<IntPair>;
|
|
Vector TheVector = {
|
|
{ 1, 1 }, { 1, 2 }, { 1, 3 }, { 2, 1 }, { 2, 2 }, { 2, 3 },
|
|
};
|
|
|
|
auto Comparator = [](const IntPair &LHS, const IntPair &RHS) {
|
|
return LHS.first == RHS.first;
|
|
};
|
|
|
|
TheVector.erase(unique_last(TheVector.begin(), TheVector.end(), Comparator),
|
|
TheVector.end());
|
|
|
|
Vector Expected = { { 1, 3 }, { 2, 3 } };
|
|
revng_check(TheVector == Expected);
|
|
}
|
|
|
|
template<template<typename...> class T>
|
|
static void testAt() {
|
|
revng::TrackingContainer<T<int>> Vector;
|
|
int One(1);
|
|
Vector.insert(One);
|
|
Vector.resetTracking();
|
|
const auto &Reference = Vector;
|
|
Reference.at(One);
|
|
|
|
auto TrackingResult = Reference.getTrackingResult();
|
|
revng_check(TrackingResult.InspectedKeys.size() == 1);
|
|
revng_check(TrackingResult.InspectedKeys.contains(1));
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(TrackingContainerVectorAt) {
|
|
testAt<SortedVector>();
|
|
testAt<MutableSet>();
|
|
}
|
|
|
|
template<template<typename...> class T>
|
|
static void testVectorCount() {
|
|
revng::TrackingContainer<T<int>> Vector;
|
|
Vector.insert(1);
|
|
Vector.resetTracking();
|
|
const auto &Reference = Vector;
|
|
Reference.count(2);
|
|
|
|
auto TrackingResult = Reference.getTrackingResult();
|
|
revng_check(TrackingResult.InspectedKeys.size() == 1);
|
|
revng_check(not TrackingResult.Exact);
|
|
revng_check(TrackingResult.InspectedKeys.contains(2));
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(TrackingContainerVectorCount) {
|
|
testVectorCount<SortedVector>();
|
|
testVectorCount<MutableSet>();
|
|
}
|
|
|
|
template<template<typename...> class T>
|
|
static void testTryGet() {
|
|
revng::TrackingContainer<T<int>> Vector;
|
|
Vector.insert(1);
|
|
Vector.resetTracking();
|
|
const auto &Reference = Vector;
|
|
const int *Result = nullptr;
|
|
Result = Reference.tryGet(2);
|
|
revng_check(Result == nullptr);
|
|
Result = Reference.tryGet(1);
|
|
revng_check(Result != nullptr && *Result == 1);
|
|
|
|
auto TrackingResult = Reference.getTrackingResult();
|
|
revng_check(TrackingResult.InspectedKeys == std::set<int>({ 1, 2 }));
|
|
revng_check(not TrackingResult.Exact);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(TrackingContainerVectorTryGet) {
|
|
testTryGet<SortedVector>();
|
|
testTryGet<MutableSet>();
|
|
}
|
|
|
|
template<template<typename...> class T>
|
|
static void testBeginEnd() {
|
|
revng::TrackingContainer<SortedVector<int>> Vector;
|
|
Vector.insert(1);
|
|
Vector.resetTracking();
|
|
const auto &Reference = Vector;
|
|
llvm::find(Reference, 1);
|
|
|
|
auto TrackingResult = Reference.getTrackingResult();
|
|
revng_check(TrackingResult.InspectedKeys.empty());
|
|
revng_check(TrackingResult.Exact);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(TrackingContainerVectorBeginEnd) {
|
|
testBeginEnd<SortedVector>();
|
|
testBeginEnd<MutableSet>();
|
|
}
|
|
|
|
template<template<typename...> class T>
|
|
static void testSetCount() {
|
|
revng::TrackingContainer<SortedVector<int>> Vector;
|
|
Vector.insert(1);
|
|
Vector.resetTracking();
|
|
const auto &Reference = Vector;
|
|
Reference.count(1);
|
|
|
|
auto TrackingResult = Reference.getTrackingResult();
|
|
revng_check(TrackingResult.InspectedKeys == std::set<int>({ 1 }));
|
|
revng_check(not TrackingResult.Exact);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(TrackingContainerSetCount) {
|
|
testSetCount<SortedVector>();
|
|
testSetCount<MutableSet>();
|
|
}
|
|
|
|
template<template<typename...> class T>
|
|
static void testPush() {
|
|
revng::TrackingContainer<SortedVector<int>> Vector;
|
|
Vector.insert(1);
|
|
Vector.resetTracking();
|
|
const auto &Reference = Vector;
|
|
Reference.count(1);
|
|
|
|
Reference.trackingPush();
|
|
|
|
auto TrackingResult = Reference.getTrackingResult();
|
|
revng_check(TrackingResult.InspectedKeys == std::set<int>({ 1 }));
|
|
revng_check(not TrackingResult.Exact);
|
|
|
|
Reference.trackingPop();
|
|
|
|
TrackingResult = Reference.getTrackingResult();
|
|
revng_check(TrackingResult.InspectedKeys == std::set<int>({ 1 }));
|
|
revng_check(not TrackingResult.Exact);
|
|
|
|
Reference.trackingPop();
|
|
|
|
TrackingResult = Reference.getTrackingResult();
|
|
revng_check(TrackingResult.InspectedKeys.empty());
|
|
revng_check(not TrackingResult.Exact);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(TrackingContainerPush) {
|
|
testPush<SortedVector>();
|
|
testPush<MutableSet>();
|
|
}
|