diff --git a/nano/core_test/CMakeLists.txt b/nano/core_test/CMakeLists.txt index 1d01f6e79..84b3ec16d 100644 --- a/nano/core_test/CMakeLists.txt +++ b/nano/core_test/CMakeLists.txt @@ -37,6 +37,7 @@ add_executable( node.cpp numbers.cpp object_stream.cpp + observer_set.cpp optimistic_scheduler.cpp processing_queue.cpp processor_service.cpp diff --git a/nano/core_test/observer_set.cpp b/nano/core_test/observer_set.cpp new file mode 100644 index 000000000..14c6d1301 --- /dev/null +++ b/nano/core_test/observer_set.cpp @@ -0,0 +1,129 @@ +#include +#include + +#include + +#include +#include + +using namespace std::chrono_literals; + +TEST (observer_set, notify_one) +{ + nano::observer_set set; + int value{ 0 }; + set.add ([&value] (int v) { + value = v; + }); + set.notify (1); + ASSERT_EQ (1, value); +} + +TEST (observer_set, notify_multiple) +{ + nano::observer_set set; + int value{ 0 }; + set.add ([&value] (int v) { + value = v; + }); + set.add ([&value] (int v) { + value += v; + }); + set.notify (1); + ASSERT_EQ (2, value); +} + +TEST (observer_set, notify_empty) +{ + nano::observer_set set; + set.notify (1); +} + +TEST (observer_set, notify_multiple_types) +{ + nano::observer_set set; + int value{ 0 }; + std::string str; + set.add ([&value, &str] (int v, std::string s) { + value = v; + str = s; + }); + set.notify (1, "test"); + ASSERT_EQ (1, value); + ASSERT_EQ ("test", str); +} + +TEST (observer_set, empty_params) +{ + nano::observer_set<> set; + set.notify (); +} + +// Make sure there are no TSAN warnings +TEST (observer_set, parallel_notify) +{ + nano::observer_set set; + std::atomic value{ 0 }; + set.add ([&value] (int v) { + std::this_thread::sleep_for (100ms); + value = v; + }); + nano::timer timer{ nano::timer_state::started }; + std::vector threads; + for (int i = 0; i < 10; ++i) + { + threads.emplace_back ([&set] { + set.notify (1); + }); + } + for (auto & thread : threads) + { + thread.join (); + } + ASSERT_EQ (1, value); + // Notification should be done in parallel + ASSERT_LT (timer.since_start (), 300ms); +} + +namespace +{ +struct move_only +{ + move_only () = default; + move_only (move_only &&) = default; + move_only & operator= (move_only &&) = default; + move_only (move_only const &) = delete; + move_only & operator= (move_only const &) = delete; +}; + +struct copy_throw +{ + copy_throw () = default; + copy_throw (copy_throw &&) = default; + copy_throw & operator= (copy_throw &&) = default; + copy_throw (copy_throw const &) + { + throw std::runtime_error ("copy_throw"); + } + copy_throw & operator= (copy_throw const &) = delete; +}; +} + +// Ensure that parameters are not unnecessarily copied, this should compile +TEST (observer_set, move_only) +{ + nano::observer_set set; + set.add ([] (move_only const &) { + }); + move_only value; + set.notify (value); +} + +TEST (observer_set, copy_throw) +{ + nano::observer_set set; + set.add ([] (copy_throw const &) { + }); + copy_throw value; + ASSERT_NO_THROW (set.notify (value)); +} \ No newline at end of file