summaryrefslogtreecommitdiff
path: root/Tests/CMakeLib/testUVRAII.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'Tests/CMakeLib/testUVRAII.cxx')
-rw-r--r--Tests/CMakeLib/testUVRAII.cxx181
1 files changed, 181 insertions, 0 deletions
diff --git a/Tests/CMakeLib/testUVRAII.cxx b/Tests/CMakeLib/testUVRAII.cxx
new file mode 100644
index 000000000..44def2527
--- /dev/null
+++ b/Tests/CMakeLib/testUVRAII.cxx
@@ -0,0 +1,181 @@
+#include "cmUVHandlePtr.h"
+
+#include <algorithm>
+#include <chrono>
+#include <iostream>
+#include <thread>
+
+#include "cm_uv.h"
+
+static void signal_reset_fn(uv_async_t* handle)
+{
+ auto ptr = static_cast<cm::uv_async_ptr*>(handle->data);
+ ptr->reset();
+}
+
+// A common pattern is to use an async signal to shutdown the server.
+static bool testAsyncShutdown()
+{
+ uv_loop_t Loop;
+ auto err = uv_loop_init(&Loop);
+ if (err != 0) {
+ std::cerr << "Could not init loop" << std::endl;
+ return false;
+ }
+
+ {
+ cm::uv_async_ptr signal;
+ signal.init(Loop, &signal_reset_fn, &signal);
+
+ std::thread([&] {
+ std::this_thread::sleep_for(std::chrono::seconds(2));
+ signal.send();
+ }).detach();
+
+ if (uv_run(&Loop, UV_RUN_DEFAULT) != 0) {
+ std::cerr << "Unclean exit state in testAsyncDtor" << std::endl;
+ return false;
+ }
+
+ if (signal.get()) {
+ std::cerr << "Loop exited with signal not being cleaned up" << std::endl;
+ return false;
+ }
+ }
+
+ uv_loop_close(&Loop);
+
+ return true;
+}
+
+static void signal_fn(uv_async_t*)
+{
+}
+
+// Async dtor is sort of a pain; since it locks a mutex we must be sure its
+// dtor always calls reset otherwise the mutex is deleted then locked.
+static bool testAsyncDtor()
+{
+ uv_loop_t Loop;
+ auto err = uv_loop_init(&Loop);
+ if (err != 0) {
+ std::cerr << "Could not init loop" << std::endl;
+ return false;
+ }
+
+ {
+ cm::uv_async_ptr signal;
+ signal.init(Loop, signal_fn);
+ }
+
+ if (uv_run(&Loop, UV_RUN_DEFAULT) != 0) {
+ std::cerr << "Unclean exit state in testAsyncDtor" << std::endl;
+ return false;
+ }
+
+ uv_loop_close(&Loop);
+
+ return true;
+}
+
+// Async needs a relatively stateful deleter; make sure that is properly
+// accounted for and doesn't try to hold on to invalid state when it is
+// moved
+static bool testAsyncMove()
+{
+ uv_loop_t Loop;
+ auto err = uv_loop_init(&Loop);
+ if (err != 0) {
+ std::cerr << "Could not init loop" << std::endl;
+ return false;
+ }
+
+ {
+ cm::uv_async_ptr signal;
+ {
+ cm::uv_async_ptr signalTmp;
+ signalTmp.init(Loop, signal_fn);
+ signal = std::move(signalTmp);
+ }
+ }
+
+ if (uv_run(&Loop, UV_RUN_DEFAULT) != 0) {
+ std::cerr << "Unclean exit state in testAsyncDtor" << std::endl;
+ return false;
+ }
+
+ uv_loop_close(&Loop);
+ return true;
+}
+
+// When a type is castable to another uv type (pipe -> stream) here,
+// and the deleter is convertible as well, we should allow moves from
+// one type to the other.
+static bool testCrossAssignment()
+{
+ uv_loop_t Loop;
+ auto err = uv_loop_init(&Loop);
+ if (err != 0) {
+ std::cerr << "Could not init loop" << std::endl;
+ return false;
+ }
+
+ {
+ cm::uv_pipe_ptr pipe;
+ pipe.init(Loop, 0);
+
+ cm::uv_stream_ptr stream = std::move(pipe);
+ if (pipe.get()) {
+ std::cerr << "Move should be sure to invalidate the previous ptr"
+ << std::endl;
+ return false;
+ }
+ cm::uv_handle_ptr handle = std::move(stream);
+ if (stream.get()) {
+ std::cerr << "Move should be sure to invalidate the previous ptr"
+ << std::endl;
+ return false;
+ }
+ }
+
+ if (uv_run(&Loop, UV_RUN_DEFAULT) != 0) {
+ std::cerr << "Unclean exit state in testCrossAssignment" << std::endl;
+ return false;
+ }
+
+ uv_loop_close(&Loop);
+ return true;
+}
+
+// This test can't fail at run time; but this makes sure we have all our move
+// ctors created correctly.
+static bool testAllMoves()
+{
+ using namespace cm;
+ struct allTypes
+ {
+ uv_stream_ptr _7;
+ uv_timer_ptr _8;
+ uv_tty_ptr _9;
+ uv_process_ptr _11;
+ uv_pipe_ptr _12;
+ uv_async_ptr _13;
+ uv_signal_ptr _14;
+ uv_handle_ptr _15;
+ };
+
+ allTypes a;
+ allTypes b(std::move(a));
+ allTypes c = std::move(b);
+ return true;
+};
+
+int testUVRAII(int, char** const)
+{
+ if ((testAsyncShutdown() &&
+ testAsyncDtor() & testAsyncMove() & testCrossAssignment() &
+ testAllMoves()) == 0) {
+ return -1;
+ }
+ return 0;
+}