2024-10-23 21:33:12 -07:00
|
|
|
// Copyright (c) FIRST and other WPILib contributors.
|
|
|
|
|
// Open Source Software; you can modify and/or share it under the terms of
|
|
|
|
|
// the WPILib BSD license file in the root directory of this project.
|
|
|
|
|
|
2022-05-20 18:59:53 -04:00
|
|
|
//===- llvm/unittest/ADT/StringMapMap.cpp - StringMap unit tests ----------===//
|
|
|
|
|
//
|
|
|
|
|
// 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
|
|
|
|
|
//
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
2024-10-23 21:33:12 -07:00
|
|
|
#include "wpi/StringMap.h" // NOLINT(build/include_order)
|
|
|
|
|
|
|
|
|
|
#include <string>
|
2022-05-20 18:59:53 -04:00
|
|
|
#include <tuple>
|
2024-10-23 21:33:12 -07:00
|
|
|
#include <utility>
|
|
|
|
|
|
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
|
|
2022-05-20 18:59:53 -04:00
|
|
|
using namespace wpi;
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
// Test fixture
|
|
|
|
|
class StringMapTest : public testing::Test {
|
2024-10-23 21:33:12 -07:00
|
|
|
protected:
|
2022-05-20 18:59:53 -04:00
|
|
|
StringMap<uint32_t> testMap;
|
|
|
|
|
|
|
|
|
|
static const char testKey[];
|
|
|
|
|
static const uint32_t testValue;
|
2024-10-23 21:33:12 -07:00
|
|
|
static const char* testKeyFirst;
|
2022-05-20 18:59:53 -04:00
|
|
|
static size_t testKeyLength;
|
|
|
|
|
static const std::string testKeyStr;
|
|
|
|
|
|
|
|
|
|
void assertEmptyMap() {
|
|
|
|
|
// Size tests
|
|
|
|
|
EXPECT_EQ(0u, testMap.size());
|
|
|
|
|
EXPECT_TRUE(testMap.empty());
|
|
|
|
|
|
|
|
|
|
// Iterator tests
|
|
|
|
|
EXPECT_TRUE(testMap.begin() == testMap.end());
|
|
|
|
|
|
|
|
|
|
// Lookup tests
|
2023-09-21 19:54:33 -07:00
|
|
|
EXPECT_FALSE(testMap.contains(testKey));
|
2022-05-20 18:59:53 -04:00
|
|
|
EXPECT_EQ(0u, testMap.count(testKey));
|
|
|
|
|
EXPECT_EQ(0u, testMap.count(std::string_view(testKeyFirst, testKeyLength)));
|
|
|
|
|
EXPECT_EQ(0u, testMap.count(testKeyStr));
|
|
|
|
|
EXPECT_TRUE(testMap.find(testKey) == testMap.end());
|
2023-07-12 22:50:13 -07:00
|
|
|
EXPECT_TRUE(testMap.find(std::string_view(testKeyFirst, testKeyLength)) ==
|
2022-05-20 18:59:53 -04:00
|
|
|
testMap.end());
|
|
|
|
|
EXPECT_TRUE(testMap.find(testKeyStr) == testMap.end());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void assertSingleItemMap() {
|
|
|
|
|
// Size tests
|
|
|
|
|
EXPECT_EQ(1u, testMap.size());
|
|
|
|
|
EXPECT_FALSE(testMap.begin() == testMap.end());
|
|
|
|
|
EXPECT_FALSE(testMap.empty());
|
|
|
|
|
|
|
|
|
|
// Iterator tests
|
|
|
|
|
StringMap<uint32_t>::iterator it = testMap.begin();
|
2024-10-23 21:33:12 -07:00
|
|
|
EXPECT_STREQ(testKey, it->first.data());
|
2022-05-20 18:59:53 -04:00
|
|
|
EXPECT_EQ(testValue, it->second);
|
|
|
|
|
++it;
|
|
|
|
|
EXPECT_TRUE(it == testMap.end());
|
|
|
|
|
|
|
|
|
|
// Lookup tests
|
2023-09-21 19:54:33 -07:00
|
|
|
EXPECT_TRUE(testMap.contains(testKey));
|
2022-05-20 18:59:53 -04:00
|
|
|
EXPECT_EQ(1u, testMap.count(testKey));
|
|
|
|
|
EXPECT_EQ(1u, testMap.count(std::string_view(testKeyFirst, testKeyLength)));
|
|
|
|
|
EXPECT_EQ(1u, testMap.count(testKeyStr));
|
|
|
|
|
EXPECT_TRUE(testMap.find(testKey) == testMap.begin());
|
2023-07-12 22:50:13 -07:00
|
|
|
EXPECT_TRUE(testMap.find(std::string_view(testKeyFirst, testKeyLength)) ==
|
2022-05-20 18:59:53 -04:00
|
|
|
testMap.begin());
|
|
|
|
|
EXPECT_TRUE(testMap.find(testKeyStr) == testMap.begin());
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const char StringMapTest::testKey[] = "key";
|
|
|
|
|
const uint32_t StringMapTest::testValue = 1u;
|
2024-10-23 21:33:12 -07:00
|
|
|
const char* StringMapTest::testKeyFirst = testKey;
|
2022-05-20 18:59:53 -04:00
|
|
|
size_t StringMapTest::testKeyLength = sizeof(testKey) - 1;
|
|
|
|
|
const std::string StringMapTest::testKeyStr(testKey);
|
|
|
|
|
|
|
|
|
|
struct CountCopyAndMove {
|
|
|
|
|
CountCopyAndMove() = default;
|
2024-10-23 21:33:12 -07:00
|
|
|
CountCopyAndMove(const CountCopyAndMove&) { copy = 1; }
|
|
|
|
|
CountCopyAndMove(CountCopyAndMove&&) { move = 1; }
|
|
|
|
|
void operator=(const CountCopyAndMove&) { ++copy; }
|
|
|
|
|
void operator=(CountCopyAndMove&&) { ++move; }
|
2022-05-20 18:59:53 -04:00
|
|
|
int copy = 0;
|
|
|
|
|
int move = 0;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Empty map tests.
|
2024-10-23 21:33:12 -07:00
|
|
|
TEST_F(StringMapTest, EmptyMap) {
|
|
|
|
|
assertEmptyMap();
|
|
|
|
|
}
|
2022-05-20 18:59:53 -04:00
|
|
|
|
|
|
|
|
// Constant map tests.
|
2024-10-23 21:33:12 -07:00
|
|
|
TEST_F(StringMapTest, ConstEmptyMap) {
|
|
|
|
|
const StringMap<uint32_t>& constTestMap = testMap;
|
2022-05-20 18:59:53 -04:00
|
|
|
|
|
|
|
|
// Size tests
|
|
|
|
|
EXPECT_EQ(0u, constTestMap.size());
|
|
|
|
|
EXPECT_TRUE(constTestMap.empty());
|
|
|
|
|
|
|
|
|
|
// Iterator tests
|
|
|
|
|
EXPECT_TRUE(constTestMap.begin() == constTestMap.end());
|
|
|
|
|
|
|
|
|
|
// Lookup tests
|
|
|
|
|
EXPECT_EQ(0u, constTestMap.count(testKey));
|
2024-10-23 21:33:12 -07:00
|
|
|
EXPECT_EQ(0u,
|
|
|
|
|
constTestMap.count(std::string_view(testKeyFirst, testKeyLength)));
|
2022-05-20 18:59:53 -04:00
|
|
|
EXPECT_EQ(0u, constTestMap.count(testKeyStr));
|
|
|
|
|
EXPECT_TRUE(constTestMap.find(testKey) == constTestMap.end());
|
2024-10-23 21:33:12 -07:00
|
|
|
EXPECT_TRUE(constTestMap.find(std::string_view(
|
|
|
|
|
testKeyFirst, testKeyLength)) == constTestMap.end());
|
2022-05-20 18:59:53 -04:00
|
|
|
EXPECT_TRUE(constTestMap.find(testKeyStr) == constTestMap.end());
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-15 05:38:15 -07:00
|
|
|
// initializer_list ctor test; also implicitly tests initializer_list and
|
|
|
|
|
// iterator overloads of insert().
|
|
|
|
|
TEST_F(StringMapTest, InitializerListCtor) {
|
|
|
|
|
testMap = StringMap<uint32_t>({{"key", 1}});
|
|
|
|
|
assertSingleItemMap();
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-20 18:59:53 -04:00
|
|
|
// A map with a single entry.
|
2024-10-23 21:33:12 -07:00
|
|
|
TEST_F(StringMapTest, SingleEntryMap) {
|
2022-05-20 18:59:53 -04:00
|
|
|
testMap[testKey] = testValue;
|
|
|
|
|
assertSingleItemMap();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Test clear() method.
|
2024-10-23 21:33:12 -07:00
|
|
|
TEST_F(StringMapTest, Clear) {
|
2022-05-20 18:59:53 -04:00
|
|
|
testMap[testKey] = testValue;
|
|
|
|
|
testMap.clear();
|
|
|
|
|
assertEmptyMap();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Test erase(iterator) method.
|
2024-10-23 21:33:12 -07:00
|
|
|
TEST_F(StringMapTest, EraseIterator) {
|
2022-05-20 18:59:53 -04:00
|
|
|
testMap[testKey] = testValue;
|
|
|
|
|
testMap.erase(testMap.begin());
|
|
|
|
|
assertEmptyMap();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Test erase(value) method.
|
2024-10-23 21:33:12 -07:00
|
|
|
TEST_F(StringMapTest, EraseValue) {
|
2022-05-20 18:59:53 -04:00
|
|
|
testMap[testKey] = testValue;
|
|
|
|
|
testMap.erase(testKey);
|
|
|
|
|
assertEmptyMap();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Test inserting two values and erasing one.
|
2024-10-23 21:33:12 -07:00
|
|
|
TEST_F(StringMapTest, InsertAndErase) {
|
2022-05-20 18:59:53 -04:00
|
|
|
testMap[testKey] = testValue;
|
|
|
|
|
testMap["otherKey"] = 2;
|
|
|
|
|
testMap.erase("otherKey");
|
|
|
|
|
assertSingleItemMap();
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-23 21:33:12 -07:00
|
|
|
TEST_F(StringMapTest, SmallFullMap) {
|
|
|
|
|
wpi::StringMap<int> Map;
|
2022-05-20 18:59:53 -04:00
|
|
|
|
|
|
|
|
Map["eins"] = 1;
|
|
|
|
|
Map["zwei"] = 2;
|
|
|
|
|
Map["drei"] = 3;
|
|
|
|
|
Map.erase("drei");
|
|
|
|
|
Map.erase("eins");
|
|
|
|
|
Map["veir"] = 4;
|
|
|
|
|
Map["funf"] = 5;
|
|
|
|
|
|
|
|
|
|
EXPECT_EQ(3u, Map.size());
|
2024-10-23 21:33:12 -07:00
|
|
|
EXPECT_FALSE(Map.contains("eins"));
|
|
|
|
|
EXPECT_EQ(2, Map["zwei"]);
|
|
|
|
|
EXPECT_FALSE(Map.contains("drei"));
|
|
|
|
|
EXPECT_EQ(4, Map["veir"]);
|
|
|
|
|
EXPECT_EQ(5, Map["funf"]);
|
2022-05-20 18:59:53 -04:00
|
|
|
}
|
|
|
|
|
|
2024-10-23 21:33:12 -07:00
|
|
|
TEST_F(StringMapTest, CopyCtor) {
|
2022-05-20 18:59:53 -04:00
|
|
|
wpi::StringMap<int> Map;
|
|
|
|
|
|
|
|
|
|
Map["eins"] = 1;
|
|
|
|
|
Map["zwei"] = 2;
|
|
|
|
|
Map["drei"] = 3;
|
|
|
|
|
Map.erase("drei");
|
|
|
|
|
Map.erase("eins");
|
|
|
|
|
Map["veir"] = 4;
|
|
|
|
|
Map["funf"] = 5;
|
|
|
|
|
|
|
|
|
|
EXPECT_EQ(3u, Map.size());
|
2024-10-23 21:33:12 -07:00
|
|
|
EXPECT_FALSE(Map.contains("eins"));
|
|
|
|
|
EXPECT_EQ(2, Map["zwei"]);
|
|
|
|
|
EXPECT_FALSE(Map.contains("drei"));
|
|
|
|
|
EXPECT_EQ(4, Map["veir"]);
|
|
|
|
|
EXPECT_EQ(5, Map["funf"]);
|
2022-05-20 18:59:53 -04:00
|
|
|
|
|
|
|
|
wpi::StringMap<int> Map2(Map);
|
|
|
|
|
EXPECT_EQ(3u, Map2.size());
|
2024-10-23 21:33:12 -07:00
|
|
|
EXPECT_FALSE(Map2.contains("eins"));
|
|
|
|
|
EXPECT_EQ(2, Map2["zwei"]);
|
|
|
|
|
EXPECT_FALSE(Map2.contains("drei"));
|
|
|
|
|
EXPECT_EQ(4, Map2["veir"]);
|
|
|
|
|
EXPECT_EQ(5, Map2["funf"]);
|
2022-05-20 18:59:53 -04:00
|
|
|
}
|
|
|
|
|
|
2024-10-23 21:33:12 -07:00
|
|
|
TEST_F(StringMapTest, At) {
|
2023-09-21 19:54:33 -07:00
|
|
|
wpi::StringMap<int> Map;
|
|
|
|
|
|
|
|
|
|
// keys both found and not found on non-empty map
|
|
|
|
|
Map["a"] = 1;
|
|
|
|
|
Map["b"] = 2;
|
|
|
|
|
Map["c"] = 3;
|
|
|
|
|
EXPECT_EQ(1, Map.at("a"));
|
|
|
|
|
EXPECT_EQ(2, Map.at("b"));
|
|
|
|
|
EXPECT_EQ(3, Map.at("c"));
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-20 18:59:53 -04:00
|
|
|
// A more complex iteration test.
|
2024-10-23 21:33:12 -07:00
|
|
|
TEST_F(StringMapTest, Iteration) {
|
2022-05-20 18:59:53 -04:00
|
|
|
bool visited[100];
|
|
|
|
|
|
|
|
|
|
// Insert 100 numbers into the map
|
|
|
|
|
for (int i = 0; i < 100; ++i) {
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
ss << "key_" << i;
|
|
|
|
|
testMap[ss.str()] = i;
|
|
|
|
|
visited[i] = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Iterate over all numbers and mark each one found.
|
2023-07-12 22:50:13 -07:00
|
|
|
for (StringMap<uint32_t>::iterator it = testMap.begin(); it != testMap.end();
|
|
|
|
|
++it) {
|
2022-05-20 18:59:53 -04:00
|
|
|
std::stringstream ss;
|
|
|
|
|
ss << "key_" << it->second;
|
2024-10-23 21:33:12 -07:00
|
|
|
ASSERT_STREQ(ss.str().c_str(), it->first.data());
|
2022-05-20 18:59:53 -04:00
|
|
|
visited[it->second] = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Ensure every number was visited.
|
|
|
|
|
for (int i = 0; i < 100; ++i) {
|
|
|
|
|
ASSERT_TRUE(visited[i]) << "Entry #" << i << " was never visited";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Test insert() method.
|
2024-10-23 21:33:12 -07:00
|
|
|
TEST_F(StringMapTest, Insert) {
|
2022-05-20 18:59:53 -04:00
|
|
|
SCOPED_TRACE("InsertTest");
|
2024-11-17 20:29:23 -08:00
|
|
|
testMap.insert(std::pair{std::string_view(testKeyFirst, testKeyLength), 1u});
|
2022-05-20 18:59:53 -04:00
|
|
|
assertSingleItemMap();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Test insert(pair<K, V>) method
|
2024-10-23 21:33:12 -07:00
|
|
|
TEST_F(StringMapTest, InsertPair) {
|
2022-05-20 18:59:53 -04:00
|
|
|
bool Inserted;
|
|
|
|
|
StringMap<uint32_t>::iterator NewIt;
|
|
|
|
|
std::tie(NewIt, Inserted) =
|
2024-11-17 20:29:23 -08:00
|
|
|
testMap.insert(std::pair{testKeyFirst, testValue});
|
2022-05-20 18:59:53 -04:00
|
|
|
EXPECT_EQ(1u, testMap.size());
|
|
|
|
|
EXPECT_EQ(testValue, testMap[testKeyFirst]);
|
2024-10-23 21:33:12 -07:00
|
|
|
EXPECT_EQ(testKeyFirst, NewIt->first);
|
2022-05-20 18:59:53 -04:00
|
|
|
EXPECT_EQ(testValue, NewIt->second);
|
|
|
|
|
EXPECT_TRUE(Inserted);
|
|
|
|
|
|
|
|
|
|
StringMap<uint32_t>::iterator ExistingIt;
|
|
|
|
|
std::tie(ExistingIt, Inserted) =
|
2024-11-17 20:29:23 -08:00
|
|
|
testMap.insert(std::pair{testKeyFirst, testValue + 1});
|
2022-05-20 18:59:53 -04:00
|
|
|
EXPECT_EQ(1u, testMap.size());
|
|
|
|
|
EXPECT_EQ(testValue, testMap[testKeyFirst]);
|
|
|
|
|
EXPECT_FALSE(Inserted);
|
|
|
|
|
EXPECT_EQ(NewIt, ExistingIt);
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-23 21:33:12 -07:00
|
|
|
TEST_F(StringMapTest, InsertOrAssign) {
|
2022-05-20 18:59:53 -04:00
|
|
|
struct A : CountCopyAndMove {
|
2024-10-23 21:33:12 -07:00
|
|
|
explicit A(int v) : v(v) {}
|
2022-05-20 18:59:53 -04:00
|
|
|
int v;
|
|
|
|
|
};
|
2024-10-23 21:33:12 -07:00
|
|
|
StringMap<A> t;
|
2022-05-20 18:59:53 -04:00
|
|
|
|
|
|
|
|
auto try1 = t.insert_or_assign("A", A(1));
|
|
|
|
|
EXPECT_TRUE(try1.second);
|
|
|
|
|
EXPECT_EQ(1, try1.first->second.v);
|
|
|
|
|
EXPECT_EQ(1, try1.first->second.move);
|
|
|
|
|
|
|
|
|
|
auto try2 = t.insert_or_assign("A", A(2));
|
|
|
|
|
EXPECT_FALSE(try2.second);
|
|
|
|
|
EXPECT_EQ(2, try2.first->second.v);
|
|
|
|
|
EXPECT_EQ(2, try1.first->second.move);
|
|
|
|
|
|
|
|
|
|
EXPECT_EQ(try1.first, try2.first);
|
|
|
|
|
EXPECT_EQ(0, try1.first->second.copy);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create a non-default constructable value
|
|
|
|
|
struct StringMapTestStruct {
|
2024-10-23 21:33:12 -07:00
|
|
|
explicit StringMapTestStruct(int i) : i(i) {}
|
2022-05-20 18:59:53 -04:00
|
|
|
StringMapTestStruct() = delete;
|
|
|
|
|
int i;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
TEST_F(StringMapTest, NonDefaultConstructable) {
|
|
|
|
|
StringMap<StringMapTestStruct> t;
|
2024-11-17 20:29:23 -08:00
|
|
|
t.insert(std::pair{"Test", StringMapTestStruct(123)});
|
2022-05-20 18:59:53 -04:00
|
|
|
StringMap<StringMapTestStruct>::iterator iter = t.find("Test");
|
|
|
|
|
ASSERT_NE(iter, t.end());
|
|
|
|
|
ASSERT_EQ(iter->second.i, 123);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct Immovable {
|
|
|
|
|
Immovable() {}
|
2024-10-23 21:33:12 -07:00
|
|
|
Immovable(Immovable&&) = delete; // will disable the other special members
|
2022-05-20 18:59:53 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct MoveOnly {
|
|
|
|
|
int i;
|
2024-10-23 21:33:12 -07:00
|
|
|
explicit MoveOnly(int i) : i(i) {}
|
|
|
|
|
explicit MoveOnly(const Immovable&) : i(0) {}
|
|
|
|
|
MoveOnly(MoveOnly&& RHS) : i(RHS.i) {}
|
|
|
|
|
MoveOnly& operator=(MoveOnly&& RHS) {
|
2022-05-20 18:59:53 -04:00
|
|
|
i = RHS.i;
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-23 21:33:12 -07:00
|
|
|
private:
|
|
|
|
|
MoveOnly(const MoveOnly&) = delete;
|
|
|
|
|
MoveOnly& operator=(const MoveOnly&) = delete;
|
2022-05-20 18:59:53 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
TEST_F(StringMapTest, MoveConstruct) {
|
|
|
|
|
StringMap<int> A;
|
|
|
|
|
A["x"] = 42;
|
|
|
|
|
StringMap<int> B = std::move(A);
|
|
|
|
|
ASSERT_EQ(A.size(), 0u);
|
|
|
|
|
ASSERT_EQ(B.size(), 1u);
|
|
|
|
|
ASSERT_EQ(B["x"], 42);
|
|
|
|
|
ASSERT_EQ(B.count("y"), 0u);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(StringMapTest, MoveAssignment) {
|
|
|
|
|
StringMap<int> A;
|
|
|
|
|
A["x"] = 42;
|
|
|
|
|
StringMap<int> B;
|
|
|
|
|
B["y"] = 117;
|
|
|
|
|
A = std::move(B);
|
|
|
|
|
ASSERT_EQ(A.size(), 1u);
|
|
|
|
|
ASSERT_EQ(B.size(), 0u);
|
|
|
|
|
ASSERT_EQ(A["y"], 117);
|
|
|
|
|
ASSERT_EQ(B.count("x"), 0u);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(StringMapTest, EqualEmpty) {
|
|
|
|
|
StringMap<int> A;
|
|
|
|
|
StringMap<int> B;
|
|
|
|
|
ASSERT_TRUE(A == B);
|
|
|
|
|
ASSERT_FALSE(A != B);
|
2024-10-23 21:33:12 -07:00
|
|
|
ASSERT_TRUE(A == A); // self check
|
2022-05-20 18:59:53 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(StringMapTest, EqualWithValues) {
|
|
|
|
|
StringMap<int> A;
|
|
|
|
|
A["A"] = 1;
|
|
|
|
|
A["B"] = 2;
|
|
|
|
|
A["C"] = 3;
|
|
|
|
|
A["D"] = 3;
|
|
|
|
|
|
|
|
|
|
StringMap<int> B;
|
|
|
|
|
B["A"] = 1;
|
|
|
|
|
B["B"] = 2;
|
|
|
|
|
B["C"] = 3;
|
|
|
|
|
B["D"] = 3;
|
|
|
|
|
|
|
|
|
|
ASSERT_TRUE(A == B);
|
|
|
|
|
ASSERT_TRUE(B == A);
|
|
|
|
|
ASSERT_FALSE(A != B);
|
|
|
|
|
ASSERT_FALSE(B != A);
|
2024-10-23 21:33:12 -07:00
|
|
|
ASSERT_TRUE(A == A); // self check
|
2022-05-20 18:59:53 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(StringMapTest, NotEqualMissingKeys) {
|
|
|
|
|
StringMap<int> A;
|
|
|
|
|
A["A"] = 1;
|
|
|
|
|
A["B"] = 2;
|
|
|
|
|
|
|
|
|
|
StringMap<int> B;
|
|
|
|
|
B["A"] = 1;
|
|
|
|
|
B["B"] = 2;
|
|
|
|
|
B["C"] = 3;
|
|
|
|
|
B["D"] = 3;
|
|
|
|
|
|
|
|
|
|
ASSERT_FALSE(A == B);
|
|
|
|
|
ASSERT_FALSE(B == A);
|
|
|
|
|
ASSERT_TRUE(A != B);
|
|
|
|
|
ASSERT_TRUE(B != A);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(StringMapTest, NotEqualWithDifferentValues) {
|
|
|
|
|
StringMap<int> A;
|
|
|
|
|
A["A"] = 1;
|
|
|
|
|
A["B"] = 2;
|
|
|
|
|
A["C"] = 100;
|
|
|
|
|
A["D"] = 3;
|
|
|
|
|
|
|
|
|
|
StringMap<int> B;
|
|
|
|
|
B["A"] = 1;
|
|
|
|
|
B["B"] = 2;
|
|
|
|
|
B["C"] = 3;
|
|
|
|
|
B["D"] = 3;
|
|
|
|
|
|
|
|
|
|
ASSERT_FALSE(A == B);
|
|
|
|
|
ASSERT_FALSE(B == A);
|
|
|
|
|
ASSERT_TRUE(A != B);
|
|
|
|
|
ASSERT_TRUE(B != A);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct Countable {
|
2024-10-23 21:33:12 -07:00
|
|
|
int& InstanceCount;
|
2022-05-20 18:59:53 -04:00
|
|
|
int Number;
|
2024-10-23 21:33:12 -07:00
|
|
|
Countable(int Number, int& InstanceCount)
|
2022-05-20 18:59:53 -04:00
|
|
|
: InstanceCount(InstanceCount), Number(Number) {
|
|
|
|
|
++InstanceCount;
|
|
|
|
|
}
|
2024-10-23 21:33:12 -07:00
|
|
|
Countable(Countable&& C) : InstanceCount(C.InstanceCount), Number(C.Number) {
|
2022-05-20 18:59:53 -04:00
|
|
|
++InstanceCount;
|
|
|
|
|
C.Number = -1;
|
|
|
|
|
}
|
2024-10-23 21:33:12 -07:00
|
|
|
Countable(const Countable& C)
|
2022-05-20 18:59:53 -04:00
|
|
|
: InstanceCount(C.InstanceCount), Number(C.Number) {
|
|
|
|
|
++InstanceCount;
|
|
|
|
|
}
|
2024-10-23 21:33:12 -07:00
|
|
|
Countable& operator=(Countable C) {
|
2022-05-20 18:59:53 -04:00
|
|
|
Number = C.Number;
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
~Countable() { --InstanceCount; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
TEST_F(StringMapTest, MoveDtor) {
|
|
|
|
|
int InstanceCount = 0;
|
|
|
|
|
StringMap<Countable> A;
|
2024-11-17 20:29:23 -08:00
|
|
|
A.insert(std::pair{"x", Countable(42, InstanceCount)});
|
2022-05-20 18:59:53 -04:00
|
|
|
ASSERT_EQ(InstanceCount, 1);
|
|
|
|
|
auto I = A.find("x");
|
|
|
|
|
ASSERT_NE(I, A.end());
|
|
|
|
|
ASSERT_EQ(I->second.Number, 42);
|
|
|
|
|
|
|
|
|
|
StringMap<Countable> B;
|
|
|
|
|
B = std::move(A);
|
|
|
|
|
ASSERT_EQ(InstanceCount, 1);
|
|
|
|
|
ASSERT_TRUE(A.empty());
|
|
|
|
|
I = B.find("x");
|
|
|
|
|
ASSERT_NE(I, B.end());
|
|
|
|
|
ASSERT_EQ(I->second.Number, 42);
|
|
|
|
|
|
|
|
|
|
B = StringMap<Countable>();
|
|
|
|
|
ASSERT_EQ(InstanceCount, 0);
|
|
|
|
|
ASSERT_TRUE(B.empty());
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-12 22:50:13 -07:00
|
|
|
TEST_F(StringMapTest, StructuredBindings) {
|
|
|
|
|
StringMap<int> A;
|
|
|
|
|
A["a"] = 42;
|
|
|
|
|
|
2024-10-23 21:33:12 -07:00
|
|
|
for (auto& [Key, Value] : A) {
|
2023-07-12 22:50:13 -07:00
|
|
|
EXPECT_EQ("a", Key);
|
|
|
|
|
EXPECT_EQ(42, Value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-25 01:11:41 -04:00
|
|
|
TEST_F(StringMapTest, StructuredBindingsMoveOnly) {
|
|
|
|
|
StringMap<MoveOnly> A;
|
2024-11-17 20:29:23 -08:00
|
|
|
A.insert(std::pair{"a", MoveOnly(42)});
|
2024-09-25 01:11:41 -04:00
|
|
|
|
2024-10-23 21:33:12 -07:00
|
|
|
for (auto&& [Key, Value] : A) {
|
2024-09-25 01:11:41 -04:00
|
|
|
EXPECT_EQ("a", Key);
|
|
|
|
|
EXPECT_EQ(42, Value.i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-20 18:59:53 -04:00
|
|
|
namespace {
|
|
|
|
|
// Simple class that counts how many moves and copy happens when growing a map
|
|
|
|
|
struct CountCtorCopyAndMove {
|
|
|
|
|
static unsigned Ctor;
|
|
|
|
|
static unsigned Move;
|
|
|
|
|
static unsigned Copy;
|
|
|
|
|
int Data = 0;
|
2024-10-23 21:33:12 -07:00
|
|
|
explicit CountCtorCopyAndMove(int Data) : Data(Data) { Ctor++; }
|
2022-05-20 18:59:53 -04:00
|
|
|
CountCtorCopyAndMove() { Ctor++; }
|
|
|
|
|
|
2024-10-23 21:33:12 -07:00
|
|
|
CountCtorCopyAndMove(const CountCtorCopyAndMove&) { Copy++; }
|
|
|
|
|
CountCtorCopyAndMove& operator=(const CountCtorCopyAndMove&) {
|
2022-05-20 18:59:53 -04:00
|
|
|
Copy++;
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
2024-10-23 21:33:12 -07:00
|
|
|
CountCtorCopyAndMove(CountCtorCopyAndMove&&) { Move++; }
|
|
|
|
|
CountCtorCopyAndMove& operator=(const CountCtorCopyAndMove&&) {
|
2022-05-20 18:59:53 -04:00
|
|
|
Move++;
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
unsigned CountCtorCopyAndMove::Copy = 0;
|
|
|
|
|
unsigned CountCtorCopyAndMove::Move = 0;
|
|
|
|
|
unsigned CountCtorCopyAndMove::Ctor = 0;
|
|
|
|
|
|
2024-10-23 21:33:12 -07:00
|
|
|
} // namespace
|
2022-05-20 18:59:53 -04:00
|
|
|
|
|
|
|
|
TEST(StringMapCustomTest, BracketOperatorCtor) {
|
|
|
|
|
StringMap<CountCtorCopyAndMove> Map;
|
|
|
|
|
CountCtorCopyAndMove::Ctor = 0;
|
|
|
|
|
Map["abcd"];
|
|
|
|
|
EXPECT_EQ(1u, CountCtorCopyAndMove::Ctor);
|
|
|
|
|
// Test that operator[] does not create a value when it is already in the map
|
|
|
|
|
CountCtorCopyAndMove::Ctor = 0;
|
|
|
|
|
Map["abcd"];
|
|
|
|
|
EXPECT_EQ(0u, CountCtorCopyAndMove::Ctor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
struct NonMoveableNonCopyableType {
|
|
|
|
|
int Data = 0;
|
|
|
|
|
NonMoveableNonCopyableType() = default;
|
2024-10-23 21:33:12 -07:00
|
|
|
explicit NonMoveableNonCopyableType(int Data) : Data(Data) {}
|
|
|
|
|
NonMoveableNonCopyableType(const NonMoveableNonCopyableType&) = delete;
|
|
|
|
|
NonMoveableNonCopyableType(NonMoveableNonCopyableType&&) = delete;
|
2022-05-20 18:59:53 -04:00
|
|
|
};
|
2024-10-23 21:33:12 -07:00
|
|
|
} // namespace
|
2022-05-20 18:59:53 -04:00
|
|
|
|
|
|
|
|
// Test that we can "emplace" an element in the map without involving map/move
|
2024-10-23 21:33:12 -07:00
|
|
|
TEST(StringMapCustomTest, Emplace) {
|
2022-05-20 18:59:53 -04:00
|
|
|
StringMap<NonMoveableNonCopyableType> Map;
|
|
|
|
|
Map.try_emplace("abcd", 42);
|
|
|
|
|
EXPECT_EQ(1u, Map.count("abcd"));
|
|
|
|
|
EXPECT_EQ(42, Map["abcd"].Data);
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-23 21:33:12 -07:00
|
|
|
} // namespace
|