@@ -28,7 +28,8 @@ namespace CppUtils::UnitTest
2828 struct Test final
2929 {
3030 std::string name;
31- std::function<void()> function;
31+ std::function<void(std::stop_token)> function;
32+ std::chrono::seconds timeout = std::chrono::seconds{10};
3233 };
3334
3435 class TestException: public std::runtime_error
@@ -72,7 +73,30 @@ namespace CppUtils::UnitTest
7273 try
7374 {
7475 auto chronoLogger = Log::ChronoLogger{"Test", settings.verbose and settings.chrono};
75- test.function();
76+
77+ auto promise = std::promise<void>{};
78+ auto future = promise.get_future();
79+
80+ auto worker = std::jthread{[promise = std::move(promise), function = test.function](std::stop_token stopToken) mutable {
81+ try
82+ {
83+ function(stopToken);
84+ promise.set_value();
85+ }
86+ catch (...)
87+ {
88+ promise.set_exception(std::current_exception());
89+ }
90+ }};
91+
92+ if (future.wait_for(test.timeout) == std::future_status::timeout)
93+ {
94+ worker.request_stop();
95+ worker.detach();
96+ throw TestException{std::format("Test timed out after {}s", test.timeout.count())};
97+ }
98+
99+ future.get();
76100 }
77101 catch (const TestException&)
78102 {
@@ -176,9 +200,14 @@ namespace CppUtils::UnitTest
176200 testRunner.addTestSuite(name, std::move(tests));
177201 }
178202
179- inline auto addTest(std::string name, std::function<void()> function) -> void
203+ inline auto addTest(std::string name, std::function<void(std::stop_token )> function, std::chrono::seconds timeout = std::chrono::seconds{10} ) -> void
180204 {
181- tests.emplace_back(std::move(name), function);
205+ tests.emplace_back(std::move(name), std::move(function), timeout);
206+ }
207+
208+ inline auto addTest(std::string name, std::function<void()> function, std::chrono::seconds timeout = std::chrono::seconds{10}) -> void
209+ {
210+ tests.emplace_back(std::move(name), [function = std::move(function)](std::stop_token) { function(); }, timeout);
182211 }
183212
184213 // Todo C++23: std::stacktrace stacktrace = std::current_stacktrace()
0 commit comments