Skip to content

Commit 8668fdf

Browse files
authored
More unit test for glz::async_map (#1466)
* More unit test for glz::async_map * Use GLZ_THROW_OR_ABORT
1 parent b192250 commit 8668fdf

File tree

2 files changed

+163
-6
lines changed

2 files changed

+163
-6
lines changed

include/glaze/thread/async_map.hpp

+10-4
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@
1010
#include <memory>
1111
#include <mutex>
1212
#include <shared_mutex>
13-
#include <stdexcept>
1413
#include <utility>
1514
#include <vector>
1615

16+
#include "glaze/util/expected.hpp"
17+
1718
// This async_map is intended to hold thread safe value types (V)
1819

1920
namespace glz
@@ -360,7 +361,7 @@ namespace glz
360361
std::tie(it, found) = binary_search_key(key);
361362

362363
if (!found) {
363-
throw std::out_of_range("Key was removed by another thread");
364+
GLZ_THROW_OR_ABORT(std::out_of_range("Key was removed by another thread"));
364365
}
365366

366367
auto shared_lock_ptr = std::make_shared<std::shared_lock<std::shared_mutex>>(std::move(shared_lock));
@@ -497,7 +498,7 @@ namespace glz
497498
return value_proxy((*it)->second, shared_lock_ptr);
498499
}
499500
else {
500-
throw std::out_of_range("Key not found");
501+
GLZ_THROW_OR_ABORT(std::out_of_range("Key not found"));
501502
}
502503
}
503504

@@ -513,7 +514,7 @@ namespace glz
513514
return const_value_proxy((*it)->second, shared_lock_ptr);
514515
}
515516
else {
516-
throw std::out_of_range("Key not found");
517+
GLZ_THROW_OR_ABORT(std::out_of_range("Key not found"));
517518
}
518519
}
519520

@@ -566,6 +567,11 @@ namespace glz
566567
auto [it, found] = binary_search_key(key);
567568
return found;
568569
}
570+
571+
bool empty() const {
572+
std::shared_lock lock(mutex);
573+
return items.size() == 0;
574+
}
569575
};
570576
}
571577

tests/exceptions_test/exceptions_test.cpp

+153-2
Original file line numberDiff line numberDiff line change
@@ -250,10 +250,10 @@ suite async_map_tests = [] {
250250
std::cout << it->second << '\n';
251251
}
252252
};
253+
254+
static_assert(glz::detail::readable_map_t<glz::async_map<std::string, std::atomic<int>>>);
253255

254256
"async_map write_json"_test = [] {
255-
static_assert(glz::detail::readable_map_t<glz::async_map<std::string, std::atomic<int>>>);
256-
257257
glz::async_map<std::string, std::atomic<int>> map;
258258
map["one"] = 1;
259259
map["two"] = 2;
@@ -267,6 +267,157 @@ suite async_map_tests = [] {
267267
expect(map.at("one").value() == 1);
268268
expect(map.at("two").value() == 2);
269269
};
270+
271+
// Test serialization and deserialization of an empty async_map
272+
"async_map empty"_test = [] {
273+
glz::async_map<std::string, std::atomic<int>> map;
274+
275+
// Serialize the empty map
276+
std::string buffer{};
277+
expect(not glz::write_json(map, buffer));
278+
expect(buffer == R"({})") << buffer;
279+
280+
// Clear and deserialize back
281+
map.clear();
282+
expect(not glz::read_json(map, buffer));
283+
expect(map.empty());
284+
};
285+
286+
// Test handling of keys and values with special characters
287+
"async_map special_characters"_test = [] {
288+
glz::async_map<std::string, std::atomic<int>> map;
289+
map["key with spaces"] = 42;
290+
map["key_with_\"quotes\""] = 84;
291+
map["ключ"] = 168; // "key" in Russian
292+
293+
std::string buffer{};
294+
expect(not glz::write_json(map, buffer));
295+
296+
// Expected JSON with properly escaped characters
297+
std::string expected = R"({"key with spaces":42,"key_with_\"quotes\"":84,"ключ":168})";
298+
expect(buffer == expected) << buffer;
299+
300+
// Deserialize and verify
301+
map.clear();
302+
expect(not glz::read_json(map, buffer));
303+
expect(map.at("key with spaces").value() == 42);
304+
expect(map.at("key_with_\"quotes\"").value() == 84);
305+
expect(map.at("ключ").value() == 168);
306+
};
307+
308+
// Test serialization and deserialization of a large async_map
309+
"async_map large_map"_test = [] {
310+
glz::async_map<int, std::atomic<int>> map;
311+
312+
// Populate the map with 1000 entries
313+
for(int i = 0; i < 1000; ++i) {
314+
map[i] = i * i;
315+
}
316+
317+
std::string buffer{};
318+
expect(not glz::write_json(map, buffer));
319+
320+
// Simple check to ensure buffer is not empty
321+
expect(!buffer.empty());
322+
323+
// Deserialize and verify a few entries
324+
map.clear();
325+
expect(not glz::read_json(map, buffer));
326+
expect(map.size() == 1000);
327+
expect(map.at(0).value() == 0);
328+
expect(map.at(999).value() == 999 * 999);
329+
};
330+
331+
// Test deserialization with invalid JSON
332+
"async_map invalid_json"_test = [] {
333+
glz::async_map<std::string, std::atomic<int>> map;
334+
std::string invalid_buffer = R"({"one":1, "two": "invalid_value"})"; // "two" should be an integer
335+
336+
expect(glz::read_json(map, invalid_buffer)); // Expecting an error (assuming 'read_json' returns true on failure)
337+
};
338+
339+
// Test updating existing keys and adding new keys
340+
"async_map update_and_add"_test = [] {
341+
glz::async_map<std::string, std::atomic<int>> map;
342+
map["alpha"] = 10;
343+
map["beta"] = 20;
344+
345+
std::string buffer{};
346+
expect(not glz::write_json(map, buffer));
347+
expect(buffer == R"({"alpha":10,"beta":20})") << buffer;
348+
349+
// Update existing key and add a new key
350+
map["alpha"] = 30;
351+
map["gamma"] = 40;
352+
353+
expect(not glz::write_json(map, buffer));
354+
expect(buffer == R"({"alpha":30,"beta":20,"gamma":40})") << buffer;
355+
356+
// Deserialize and verify
357+
map.clear();
358+
expect(not glz::read_json(map, buffer));
359+
expect(map.at("alpha").value() == 30);
360+
expect(map.at("beta").value() == 20);
361+
expect(map.at("gamma").value() == 40);
362+
};
363+
364+
// Test concurrent access to the async_map
365+
"async_map concurrent_access"_test = [] {
366+
glz::async_map<int, std::atomic<int>> map;
367+
const int num_threads = 10;
368+
const int increments_per_thread = 1000;
369+
370+
// Initialize map with keys
371+
for(int i = 0; i < num_threads; ++i) {
372+
map[i] = 0;
373+
}
374+
375+
// Launch multiple threads to increment values concurrently
376+
std::vector<std::thread> threads;
377+
for(int i = 0; i < num_threads; ++i) {
378+
threads.emplace_back([&, i]() {
379+
for(int j = 0; j < increments_per_thread; ++j) {
380+
++map[i].value();
381+
}
382+
});
383+
}
384+
385+
// Wait for all threads to finish
386+
for(auto& th : threads) {
387+
th.join();
388+
}
389+
390+
// Verify the results
391+
for(int i = 0; i < num_threads; ++i) {
392+
expect(map.at(i).value() == increments_per_thread) << "Key " << i;
393+
}
394+
};
395+
396+
// Test removal of keys from the async_map
397+
"async_map remove_keys"_test = [] {
398+
glz::async_map<std::string, std::atomic<int>> map;
399+
map["first"] = 100;
400+
map["second"] = 200;
401+
map["third"] = 300;
402+
403+
// Remove a key
404+
map.erase("second");
405+
expect(map.size() == 2);
406+
expect(map.find("second") == map.end());
407+
408+
// Serialize and verify
409+
std::string buffer{};
410+
expect(not glz::write_json(map, buffer));
411+
expect(buffer == R"({"first":100,"third":300})") << buffer;
412+
413+
// Deserialize and verify
414+
map.clear();
415+
expect(not glz::read_json(map, buffer));
416+
expect(map.size() == 2);
417+
expect(map.at("first").value() == 100);
418+
expect(map.at("third").value() == 300);
419+
};
420+
270421
};
271422

272423
int main() { return 0; }

0 commit comments

Comments
 (0)