aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/unit/libmain/crash.cc56
-rw-r--r--tests/unit/libutil/async-collect.cc104
-rw-r--r--tests/unit/libutil/async-semaphore.cc74
-rw-r--r--tests/unit/meson.build10
4 files changed, 243 insertions, 1 deletions
diff --git a/tests/unit/libmain/crash.cc b/tests/unit/libmain/crash.cc
new file mode 100644
index 000000000..883dc39bd
--- /dev/null
+++ b/tests/unit/libmain/crash.cc
@@ -0,0 +1,56 @@
+#include <gtest/gtest.h>
+#include "crash-handler.hh"
+
+namespace nix {
+
+class OopsException : public std::exception
+{
+ const char * msg;
+
+public:
+ OopsException(const char * msg) : msg(msg) {}
+ const char * what() const noexcept override
+ {
+ return msg;
+ }
+};
+
+void causeCrashForTesting(std::function<void()> fixture)
+{
+ registerCrashHandler();
+ std::cerr << "time to crash\n";
+ try {
+ fixture();
+ } catch (...) {
+ std::terminate();
+ }
+}
+
+TEST(CrashHandler, exceptionName)
+{
+ ASSERT_DEATH(
+ causeCrashForTesting([]() { throw OopsException{"lol oops"}; }),
+ "time to crash\nLix crashed.*OopsException: lol oops"
+ );
+}
+
+TEST(CrashHandler, unknownTerminate)
+{
+ ASSERT_DEATH(
+ causeCrashForTesting([]() { std::terminate(); }),
+ "time to crash\nLix crashed.*std::terminate\\(\\) called without exception"
+ );
+}
+
+TEST(CrashHandler, nonStdException)
+{
+ ASSERT_DEATH(
+ causeCrashForTesting([]() {
+ // NOLINTNEXTLINE(hicpp-exception-baseclass): intentional
+ throw 4;
+ }),
+ "time to crash\nLix crashed.*Unknown exception! Spooky\\."
+ );
+}
+
+}
diff --git a/tests/unit/libutil/async-collect.cc b/tests/unit/libutil/async-collect.cc
new file mode 100644
index 000000000..770374d21
--- /dev/null
+++ b/tests/unit/libutil/async-collect.cc
@@ -0,0 +1,104 @@
+#include "async-collect.hh"
+
+#include <gtest/gtest.h>
+#include <kj/array.h>
+#include <kj/async.h>
+#include <kj/exception.h>
+#include <stdexcept>
+
+namespace nix {
+
+TEST(AsyncCollect, void)
+{
+ kj::EventLoop loop;
+ kj::WaitScope waitScope(loop);
+
+ auto a = kj::newPromiseAndFulfiller<void>();
+ auto b = kj::newPromiseAndFulfiller<void>();
+ auto c = kj::newPromiseAndFulfiller<void>();
+ auto d = kj::newPromiseAndFulfiller<void>();
+
+ auto collect = asyncCollect(kj::arr(
+ std::pair(1, std::move(a.promise)),
+ std::pair(2, std::move(b.promise)),
+ std::pair(3, std::move(c.promise)),
+ std::pair(4, std::move(d.promise))
+ ));
+
+ auto p = collect.next();
+ ASSERT_FALSE(p.poll(waitScope));
+
+ // collection is ordered
+ c.fulfiller->fulfill();
+ b.fulfiller->fulfill();
+
+ ASSERT_TRUE(p.poll(waitScope));
+ ASSERT_EQ(p.wait(waitScope), 3);
+
+ p = collect.next();
+ ASSERT_TRUE(p.poll(waitScope));
+ ASSERT_EQ(p.wait(waitScope), 2);
+
+ p = collect.next();
+ ASSERT_FALSE(p.poll(waitScope));
+
+ // exceptions propagate
+ a.fulfiller->rejectIfThrows([] { throw std::runtime_error("test"); });
+
+ p = collect.next();
+ ASSERT_TRUE(p.poll(waitScope));
+ ASSERT_THROW(p.wait(waitScope), kj::Exception);
+
+ // first exception aborts collection
+ p = collect.next();
+ ASSERT_TRUE(p.poll(waitScope));
+ ASSERT_THROW(p.wait(waitScope), kj::Exception);
+}
+
+TEST(AsyncCollect, nonVoid)
+{
+ kj::EventLoop loop;
+ kj::WaitScope waitScope(loop);
+
+ auto a = kj::newPromiseAndFulfiller<int>();
+ auto b = kj::newPromiseAndFulfiller<int>();
+ auto c = kj::newPromiseAndFulfiller<int>();
+ auto d = kj::newPromiseAndFulfiller<int>();
+
+ auto collect = asyncCollect(kj::arr(
+ std::pair(1, std::move(a.promise)),
+ std::pair(2, std::move(b.promise)),
+ std::pair(3, std::move(c.promise)),
+ std::pair(4, std::move(d.promise))
+ ));
+
+ auto p = collect.next();
+ ASSERT_FALSE(p.poll(waitScope));
+
+ // collection is ordered
+ c.fulfiller->fulfill(1);
+ b.fulfiller->fulfill(2);
+
+ ASSERT_TRUE(p.poll(waitScope));
+ ASSERT_EQ(p.wait(waitScope), std::pair(3, 1));
+
+ p = collect.next();
+ ASSERT_TRUE(p.poll(waitScope));
+ ASSERT_EQ(p.wait(waitScope), std::pair(2, 2));
+
+ p = collect.next();
+ ASSERT_FALSE(p.poll(waitScope));
+
+ // exceptions propagate
+ a.fulfiller->rejectIfThrows([] { throw std::runtime_error("test"); });
+
+ p = collect.next();
+ ASSERT_TRUE(p.poll(waitScope));
+ ASSERT_THROW(p.wait(waitScope), kj::Exception);
+
+ // first exception aborts collection
+ p = collect.next();
+ ASSERT_TRUE(p.poll(waitScope));
+ ASSERT_THROW(p.wait(waitScope), kj::Exception);
+}
+}
diff --git a/tests/unit/libutil/async-semaphore.cc b/tests/unit/libutil/async-semaphore.cc
new file mode 100644
index 000000000..12b52885d
--- /dev/null
+++ b/tests/unit/libutil/async-semaphore.cc
@@ -0,0 +1,74 @@
+#include "async-semaphore.hh"
+
+#include <gtest/gtest.h>
+#include <kj/async.h>
+
+namespace nix {
+
+TEST(AsyncSemaphore, counting)
+{
+ kj::EventLoop loop;
+ kj::WaitScope waitScope(loop);
+
+ AsyncSemaphore sem(2);
+
+ ASSERT_EQ(sem.available(), 2);
+ ASSERT_EQ(sem.used(), 0);
+
+ auto a = kj::evalNow([&] { return sem.acquire(); });
+ ASSERT_EQ(sem.available(), 1);
+ ASSERT_EQ(sem.used(), 1);
+ auto b = kj::evalNow([&] { return sem.acquire(); });
+ ASSERT_EQ(sem.available(), 0);
+ ASSERT_EQ(sem.used(), 2);
+
+ auto c = kj::evalNow([&] { return sem.acquire(); });
+ auto d = kj::evalNow([&] { return sem.acquire(); });
+
+ ASSERT_TRUE(a.poll(waitScope));
+ ASSERT_TRUE(b.poll(waitScope));
+ ASSERT_FALSE(c.poll(waitScope));
+ ASSERT_FALSE(d.poll(waitScope));
+
+ a = nullptr;
+ ASSERT_TRUE(c.poll(waitScope));
+ ASSERT_FALSE(d.poll(waitScope));
+
+ {
+ auto lock = b.wait(waitScope);
+ ASSERT_FALSE(d.poll(waitScope));
+ }
+
+ ASSERT_TRUE(d.poll(waitScope));
+
+ ASSERT_EQ(sem.available(), 0);
+ ASSERT_EQ(sem.used(), 2);
+ c = nullptr;
+ ASSERT_EQ(sem.available(), 1);
+ ASSERT_EQ(sem.used(), 1);
+ d = nullptr;
+ ASSERT_EQ(sem.available(), 2);
+ ASSERT_EQ(sem.used(), 0);
+}
+
+TEST(AsyncSemaphore, cancelledWaiter)
+{
+ kj::EventLoop loop;
+ kj::WaitScope waitScope(loop);
+
+ AsyncSemaphore sem(1);
+
+ auto a = kj::evalNow([&] { return sem.acquire(); });
+ auto b = kj::evalNow([&] { return sem.acquire(); });
+ auto c = kj::evalNow([&] { return sem.acquire(); });
+
+ ASSERT_TRUE(a.poll(waitScope));
+ ASSERT_FALSE(b.poll(waitScope));
+
+ b = nullptr;
+ a = nullptr;
+
+ ASSERT_TRUE(c.poll(waitScope));
+}
+
+}
diff --git a/tests/unit/meson.build b/tests/unit/meson.build
index 8ff0b5ec5..8b0c66dd8 100644
--- a/tests/unit/meson.build
+++ b/tests/unit/meson.build
@@ -39,6 +39,8 @@ liblixutil_test_support = declare_dependency(
)
libutil_tests_sources = files(
+ 'libutil/async-collect.cc',
+ 'libutil/async-semaphore.cc',
'libutil/canon-path.cc',
'libutil/checked-arithmetic.cc',
'libutil/chunked-vector.cc',
@@ -76,6 +78,7 @@ libutil_tester = executable(
liblixexpr_mstatic,
liblixutil_test_support,
nlohmann_json,
+ kj,
],
cpp_pch : cpp_pch,
)
@@ -262,9 +265,14 @@ test(
protocol : 'gtest',
)
+libmain_tests_sources = files(
+ 'libmain/crash.cc',
+ 'libmain/progress-bar.cc',
+)
+
libmain_tester = executable(
'liblixmain-tests',
- files('libmain/progress-bar.cc'),
+ libmain_tests_sources,
dependencies : [
liblixmain,
liblixexpr,