summaryrefslogtreecommitdiffstats
path: root/library/cpp/containers/concurrent_hash/concurrent_hash_ut.cpp
diff options
context:
space:
mode:
authorswarmer <[email protected]>2025-08-11 03:16:07 +0300
committerswarmer <[email protected]>2025-08-11 03:27:35 +0300
commit28d6d6036edcc7c5d70eb5d24743c860f4deef2b (patch)
tree794abad70abb1f522252abc4d8fe929b2dd552de /library/cpp/containers/concurrent_hash/concurrent_hash_ut.cpp
parent328ac2061a2d3924497fdcd3290df17c2cf5eb73 (diff)
Add missing PEERDIR to the library/cpp/containers/concurrent_hash
KIKIMR-23824 commit_hash:dbb554ea903a90c990b23ad016d4690437d57e4b
Diffstat (limited to 'library/cpp/containers/concurrent_hash/concurrent_hash_ut.cpp')
-rw-r--r--library/cpp/containers/concurrent_hash/concurrent_hash_ut.cpp198
1 files changed, 198 insertions, 0 deletions
diff --git a/library/cpp/containers/concurrent_hash/concurrent_hash_ut.cpp b/library/cpp/containers/concurrent_hash/concurrent_hash_ut.cpp
new file mode 100644
index 00000000000..6427adf92c6
--- /dev/null
+++ b/library/cpp/containers/concurrent_hash/concurrent_hash_ut.cpp
@@ -0,0 +1,198 @@
+#include "concurrent_hash.h"
+
+#include <library/cpp/testing/unittest/gtest.h>
+
+#include <util/generic/noncopyable.h>
+#include <util/generic/ptr.h>
+#include <util/generic/string.h>
+#include <util/string/cast.h>
+
+TEST(TConcurrentHashTest, TEmptyGetTest) {
+ TConcurrentHashMap<TString, ui32> h;
+
+ EXPECT_FALSE(h.Has("key"));
+
+ ui32 res = 100;
+ EXPECT_FALSE(h.Get("key", res));
+ EXPECT_EQ(res, 100);
+
+ // Can't check h.Get("key") here because it has Y_ABORT_UNLESS inside o_O
+}
+
+TEST(TConcurrentHashTest, TInsertTest) {
+ TConcurrentHashMap<TString, ui32> h;
+
+ h.Insert("key1", 1);
+ h.Insert("key2", 2);
+
+ EXPECT_EQ(h.Get("key1"), 1);
+ EXPECT_EQ(h.Get("key2"), 2);
+
+ ui32 res = 100;
+ EXPECT_TRUE(h.Has("key1"));
+ EXPECT_TRUE(h.Get("key1", res));
+ EXPECT_EQ(res, 1);
+
+ EXPECT_TRUE(h.Has("key2"));
+ EXPECT_TRUE(h.Get("key2", res));
+ EXPECT_EQ(res, 2);
+
+ EXPECT_FALSE(h.Has("key3"));
+ EXPECT_FALSE(h.Get("key3", res));
+ EXPECT_EQ(res, 2);
+}
+
+TEST(TConcurrentHashTest, TInsertIfAbsentTest) {
+ TConcurrentHashMap<TString, ui32> h;
+
+ ui32 res;
+ EXPECT_FALSE(h.Has("key"));
+ EXPECT_FALSE(h.Get("key", res));
+
+ EXPECT_EQ(h.InsertIfAbsent("key", 1), static_cast<ui32>(1));
+ EXPECT_EQ(h.Get("key"), 1);
+
+ EXPECT_EQ(h.InsertIfAbsent("key", 2), static_cast<ui32>(1));
+ EXPECT_EQ(h.Get("key"), 1);
+}
+
+TEST(TConcurrentHashTest, TInsertIfAbsentTestFunc) {
+ TConcurrentHashMap<TString, ui32> h;
+
+ bool initialized = false;
+ auto f = [&initialized]() {
+ initialized = true;
+ return static_cast<ui32>(1);
+ };
+
+ ui32 res = 0;
+ EXPECT_FALSE(h.Get("key", res));
+ EXPECT_EQ(res, 0);
+
+ EXPECT_EQ(h.InsertIfAbsentWithInit("key", f), 1);
+ EXPECT_EQ(h.Get("key"), 1);
+ EXPECT_TRUE(initialized);
+
+ initialized = false;
+ EXPECT_EQ(h.InsertIfAbsentWithInit("key", f), 1);
+ EXPECT_EQ(h.Get("key"), 1);
+ EXPECT_FALSE(initialized);
+
+ h.Insert("key", 2);
+ EXPECT_EQ(h.InsertIfAbsentWithInit("key", f), 2);
+ EXPECT_EQ(h.Get("key"), 2);
+ EXPECT_FALSE(initialized);
+}
+
+
+TEST(TConcurrentHashTest, TEmplaceIfAbsentTest) {
+ struct TBadConstructor{};
+
+ // InsertIfAbsent cannot be uses for noncopyable and nonmovable types (e.g. atomics or structs with atomic members)
+ struct TFoo : public TNonCopyable {
+ explicit TFoo(int value)
+ : Value(value)
+ {}
+
+ explicit TFoo(TBadConstructor) {
+ ythrow yexception{} << "THis constructor must not be called";
+ }
+
+ int Value;
+ };
+
+ TConcurrentHashMap<TString, TFoo> h;
+
+ EXPECT_FALSE(h.Has("key"));
+
+ EXPECT_EQ(h.EmplaceIfAbsent("key", 123).Value, 123);
+ EXPECT_TRUE(h.Has("key"));
+
+ // If the key already exists, the value must not be constructed
+ EXPECT_EQ(h.EmplaceIfAbsent("key", TBadConstructor{}).Value, 123);
+}
+
+TEST(TConcurrentHashTest, TRemoveTest) {
+ TConcurrentHashMap<TString, ui32> h;
+
+ EXPECT_FALSE(h.Has("key1"));
+ EXPECT_FALSE(h.Has("key2"));
+ EXPECT_FALSE(h.Has("key3"));
+
+ h.Insert("key1", 1);
+ EXPECT_TRUE(h.Has("key1"));
+ EXPECT_FALSE(h.Has("key2"));
+ EXPECT_FALSE(h.Has("key3"));
+ EXPECT_EQ(h.Get("key1"), 1);
+
+ h.Remove("key1");
+ EXPECT_FALSE(h.Has("key1"));
+ EXPECT_FALSE(h.Has("key2"));
+ EXPECT_FALSE(h.Has("key3"));
+
+ h.Insert("key1", 1);
+ h.Insert("key2", 2);
+
+ EXPECT_TRUE(h.Has("key1"));
+ EXPECT_TRUE(h.Has("key2"));
+ EXPECT_FALSE(h.Has("key3"));
+ EXPECT_EQ(h.Get("key1"), 1);
+ EXPECT_EQ(h.Get("key2"), 2);
+
+ h.Remove("key2");
+ EXPECT_TRUE(h.Has("key1"));
+ EXPECT_FALSE(h.Has("key2"));
+ EXPECT_FALSE(h.Has("key3"));
+ EXPECT_EQ(h.Get("key1"), 1);
+}
+
+TEST(TConcurrentHashTest, TTryRemoveTest) {
+ TConcurrentHashMap<TString, ui32> h;
+
+ EXPECT_FALSE(h.Has("key"));
+
+ ui32 res;
+ EXPECT_FALSE(h.TryRemove("key", res));
+
+ h.Insert("key", 1);
+ EXPECT_TRUE(h.Has("key"));
+ EXPECT_TRUE(h.TryRemove("key", res));
+ EXPECT_EQ(res, 1);
+ EXPECT_FALSE(h.TryRemove("key", res));
+}
+
+TEST(TConcurrentHashTest, TExchangeTest) {
+ struct TValue: TThrRefBase {
+ TValue(int v)
+ : Value(v)
+ {
+ }
+
+ int Value;
+ };
+
+ using TValuePtr = TIntrusivePtr<TValue>;
+
+ TConcurrentHashMap<int, TValuePtr> h;
+
+ TValuePtr v = MakeIntrusive<TValue>(123);
+ h.Exchange(1, v);
+ EXPECT_EQ(v, nullptr);
+
+ v = MakeIntrusive<TValue>(456);
+ h.Exchange(1, v);
+ EXPECT_EQ(v->RefCount(), 1);
+ EXPECT_EQ(v->Value, 123);
+ EXPECT_EQ(h.Get(1)->Value, 456);
+}
+
+TEST(TConcurrentHashTest, TGetBucketTest) {
+ TConcurrentHashMap<TString, ui32> h;
+
+ for (int i = 0; i < 100; ++i) {
+ TString key = ToString(i);
+ auto& bucket1 = h.GetBucketForKey(key);
+ auto& bucket2 = h.GetBucketForKey(TStringBuf(key));
+ EXPECT_EQ(&bucket1, &bucket2);
+ }
+}