1+ #include " ConcurrentHashMap.h"
2+ #include < gtest/gtest.h>
3+ #include < thread>
4+ #include < vector>
5+ #include < atomic>
6+ #include < string>
7+
8+ class ConcurrentHashMapTest : public ::testing::Test
9+ {
10+ protected:
11+ void SetUp () override
12+ {
13+ map_ = std::make_unique<ConcurrentHashMap<std::string, int >>();
14+ }
15+
16+ std::unique_ptr<ConcurrentHashMap<std::string, int >> map_;
17+ };
18+
19+ TEST_F (ConcurrentHashMapTest, BasicInsertAndRetrieve)
20+ {
21+ map_->insert (" apple" , 10 );
22+ auto result = map_->get (" apple" );
23+ ASSERT_TRUE (result.has_value ());
24+ EXPECT_EQ (10 , *result);
25+ }
26+
27+ TEST_F (ConcurrentHashMapTest, UpdateExistingKey)
28+ {
29+ map_->insert (" apple" , 10 );
30+ map_->insert (" apple" , 20 );
31+ auto result = map_->get (" apple" );
32+ ASSERT_TRUE (result.has_value ());
33+ EXPECT_EQ (20 , *result);
34+ }
35+
36+ TEST_F (ConcurrentHashMapTest, RemoveKey)
37+ {
38+ map_->insert (" banana" , 30 );
39+ map_->remove (" banana" );
40+ auto result = map_->get (" banana" );
41+ EXPECT_FALSE (result.has_value ());
42+ }
43+
44+ TEST_F (ConcurrentHashMapTest, NonExistentKey)
45+ {
46+ auto result = map_->get (" mango" );
47+ EXPECT_FALSE (result.has_value ());
48+ }
49+
50+ TEST_F (ConcurrentHashMapTest, ConcurrentInserts)
51+ {
52+ constexpr int NUM_THREADS = 8 ;
53+ constexpr int ITEMS_PER_THREAD = 100 ;
54+ std::vector<std::thread> threads;
55+
56+ for (int i = 0 ; i < NUM_THREADS; ++i)
57+ {
58+ threads.emplace_back ([this , i]
59+ {
60+ for (int j = 0 ; j < ITEMS_PER_THREAD; ++j)
61+ {
62+ std::string key = " thread" + std::to_string (i) + " -" + std::to_string (j);
63+ map_->insert (key, j);
64+ }
65+ });
66+ }
67+
68+ for (auto &t : threads)
69+ {
70+ t.join ();
71+ }
72+
73+ // Verify all items were inserted
74+ for (int i = 0 ; i < NUM_THREADS; ++i)
75+ {
76+ for (int j = 0 ; j < ITEMS_PER_THREAD; ++j)
77+ {
78+ std::string key = " thread" + std::to_string (i) + " -" + std::to_string (j);
79+ auto result = map_->get (key);
80+ ASSERT_TRUE (result.has_value ()) << " Missing key: " << key;
81+ EXPECT_EQ (j, *result);
82+ }
83+ }
84+ }
85+
86+ TEST_F (ConcurrentHashMapTest, ConcurrentUpdates)
87+ {
88+ constexpr int NUM_THREADS = 8 ;
89+ std::vector<std::thread> threads;
90+ std::atomic<bool > start{false };
91+ // Initial value
92+ map_->insert (" contended" , 0 );
93+
94+ for (int i = 0 ; i < NUM_THREADS; ++i)
95+ {
96+ threads.emplace_back ([this , &start, i]
97+ {
98+ while (!start) { /* spin */ } // Wait for start signal
99+
100+ for (int j = 0 ; j < 100 ; ++j)
101+ {
102+ map_->insert (" contended" , i * 100 + j);
103+ }
104+ });
105+ }
106+
107+ start = true ;
108+
109+ for (auto &t : threads)
110+ {
111+ t.join ();
112+ }
113+
114+ // Verify the final value is from the last writer
115+ auto result = map_->get (" contended" );
116+ ASSERT_TRUE (result.has_value ());
117+ // The exact value depends on thread scheduling, but it should be
118+ // from one of the threads (between 0*100+99 and 7*100+99)
119+ EXPECT_GE (*result, 99 );
120+ EXPECT_LE (*result, 799 );
121+ }
122+
123+ TEST_F (ConcurrentHashMapTest, ConcurrentReadWrite)
124+ {
125+ constexpr int NUM_WRITERS = 4 ;
126+ constexpr int NUM_READERS = 4 ;
127+ std::atomic<bool > running{true };
128+ std::vector<std::thread> writers;
129+ std::vector<std::thread> readers;
130+
131+ // Writers constantly update values
132+ for (int i = 0 ; i < NUM_WRITERS; ++i)
133+ {
134+ writers.emplace_back ([this , i, &running]
135+ {
136+ while (running)
137+ {
138+ map_->insert (" key" + std::to_string (i), i);
139+ }
140+ });
141+ }
142+
143+ // Readers constantly read values
144+ for (int i = 0 ; i < NUM_READERS; ++i)
145+ {
146+ readers.emplace_back ([this , &running]
147+ {
148+ while (running)
149+ {
150+ for (int j = 0 ; j < NUM_WRITERS; ++j)
151+ {
152+ auto result = map_->get (" key" + std::to_string (j));
153+ }
154+ }
155+ });
156+ }
157+
158+ // Let them run for 500ms
159+ std::this_thread::sleep_for (std::chrono::milliseconds (500 ));
160+ running = false ;
161+
162+ for (auto &t : writers)
163+ {
164+ t.join ();
165+ }
166+
167+ for (auto &t : readers)
168+ {
169+ t.join ();
170+ }
171+
172+ // Verify final values are from writers
173+ for (int i = 0 ; i < NUM_WRITERS; ++i)
174+ {
175+ auto result = map_->get (" key" + std::to_string (i));
176+ ASSERT_TRUE (result.has_value ());
177+ EXPECT_EQ (i, *result);
178+ }
179+ }
180+
181+ TEST_F (ConcurrentHashMapTest, HighContentionSingleBucket)
182+ {
183+ // Create map with only 1 bucket to maximize contention
184+ ConcurrentHashMap<int , int > singleBucketMap (1 );
185+ constexpr int NUM_OPERATIONS = 1000 ;
186+ std::vector<std::thread> threads;
187+ std::atomic<int > counter{0 };
188+
189+ for (int i = 0 ; i < 8 ; ++i)
190+ {
191+ threads.emplace_back ([&]
192+ {
193+ for (int j = 0 ; j < NUM_OPERATIONS; ++j)
194+ {
195+ int key = j % 10 ; // Only 10 different keys
196+ singleBucketMap.insert (key, ++counter);
197+ singleBucketMap.get (key);
198+
199+ if (j % 10 == 0 )
200+ {
201+ singleBucketMap.remove (key);
202+ }
203+ }
204+ });
205+ }
206+
207+ for (auto &t : threads)
208+ {
209+ t.join ();
210+ }
211+
212+ // Verify final state
213+ for (int i = 0 ; i < 10 ; ++i)
214+ {
215+ auto result = singleBucketMap.get (i);
216+
217+ if (result)
218+ {
219+ EXPECT_GT (*result, 0 );
220+ EXPECT_LE (*result, counter.load ());
221+ }
222+ }
223+ }
224+
225+ int main (int argc, char **argv)
226+ {
227+ ::testing::InitGoogleTest (&argc, argv);
228+ return RUN_ALL_TESTS ();
229+ }
0 commit comments