diff options
Diffstat (limited to 'unit_tests')
-rw-r--r-- | unit_tests/_pch.cpp | 14 | ||||
-rw-r--r-- | unit_tests/lane_tests.cpp | 10 | ||||
-rw-r--r-- | unit_tests/scripts/lane/uncooperative_shutdown.lua | 4 | ||||
-rw-r--r-- | unit_tests/shared.cpp | 82 | ||||
-rw-r--r-- | unit_tests/shared.h | 5 |
5 files changed, 66 insertions, 49 deletions
diff --git a/unit_tests/_pch.cpp b/unit_tests/_pch.cpp index 189089a..0d37ba4 100644 --- a/unit_tests/_pch.cpp +++ b/unit_tests/_pch.cpp | |||
@@ -1,15 +1 @@ | |||
1 | #include "_pch.hpp" | #include "_pch.hpp" | |
2 | |||
3 | // IMPORTANT INFORMATION: some relative paths coded in the test implementations suppose that the cwd when debugging is $(SolutionDir)Lanes | ||
4 | // Therefore that's what needs to be set in Google Test Adapter 'Working Directory' global setting... | ||
5 | |||
6 | //int main(int argc, char* argv[]) | ||
7 | //{ | ||
8 | // // your setup ... | ||
9 | // | ||
10 | // int result = Catch::Session().run(argc, argv); | ||
11 | // | ||
12 | // // your clean-up... | ||
13 | // | ||
14 | // return result; | ||
15 | //} \ No newline at end of file | ||
diff --git a/unit_tests/lane_tests.cpp b/unit_tests/lane_tests.cpp index 0c4feba..d6ef2e0 100644 --- a/unit_tests/lane_tests.cpp +++ b/unit_tests/lane_tests.cpp | |||
@@ -327,10 +327,10 @@ TEST_CASE("scripted tests." #DIR "." #FILE) \ | |||
327 | } | 327 | } |
328 | 328 | ||
329 | MAKE_TEST_CASE(lane, cooperative_shutdown, AssertNoLuaError) | 329 | MAKE_TEST_CASE(lane, cooperative_shutdown, AssertNoLuaError) |
330 | #if LUAJIT_FLAVOR() == 0 | 330 | #if LUA_VERSION_NUM >= 504 // // warnings are a Lua 5.4 feature |
331 | // TODO: for some reason, even though we throw as expected, the test fails with LuaJIT. To be investigated | 331 | // NOTE: when this test ends, there are resource leaks and a dangling thread |
332 | MAKE_TEST_CASE(lane, uncooperative_shutdown, AssertThrows) | 332 | MAKE_TEST_CASE(lane, uncooperative_shutdown, AssertWarns) |
333 | #endif // LUAJIT_FLAVOR() | 333 | #endif // LUA_VERSION_NUM |
334 | MAKE_TEST_CASE(lane, tasking_basic, AssertNoLuaError) | 334 | MAKE_TEST_CASE(lane, tasking_basic, AssertNoLuaError) |
335 | MAKE_TEST_CASE(lane, tasking_cancelling, AssertNoLuaError) | 335 | MAKE_TEST_CASE(lane, tasking_cancelling, AssertNoLuaError) |
336 | MAKE_TEST_CASE(lane, tasking_comms_criss_cross, AssertNoLuaError) | 336 | MAKE_TEST_CASE(lane, tasking_comms_criss_cross, AssertNoLuaError) |
@@ -350,7 +350,7 @@ TEST_CASE("lanes.scripted tests") | |||
350 | { | 350 | { |
351 | auto const& _testParam = GENERATE( | 351 | auto const& _testParam = GENERATE( |
352 | FileRunnerParam{ PUC_LUA_ONLY("lane/cooperative_shutdown"), TestType::AssertNoLuaError }, // 0 | 352 | FileRunnerParam{ PUC_LUA_ONLY("lane/cooperative_shutdown"), TestType::AssertNoLuaError }, // 0 |
353 | FileRunnerParam{ "lane/uncooperative_shutdown", TestType::AssertThrows }, | 353 | FileRunnerParam{ "lane/uncooperative_shutdown", TestType::AssertWarns }, |
354 | FileRunnerParam{ "lane/tasking_basic", TestType::AssertNoLuaError }, // 2 | 354 | FileRunnerParam{ "lane/tasking_basic", TestType::AssertNoLuaError }, // 2 |
355 | FileRunnerParam{ "lane/tasking_cancelling", TestType::AssertNoLuaError }, // 3 | 355 | FileRunnerParam{ "lane/tasking_cancelling", TestType::AssertNoLuaError }, // 3 |
356 | FileRunnerParam{ "lane/tasking_comms_criss_cross", TestType::AssertNoLuaError }, // 4 | 356 | FileRunnerParam{ "lane/tasking_comms_criss_cross", TestType::AssertNoLuaError }, // 4 |
diff --git a/unit_tests/scripts/lane/uncooperative_shutdown.lua b/unit_tests/scripts/lane/uncooperative_shutdown.lua index 51e5762..89e1ff8 100644 --- a/unit_tests/scripts/lane/uncooperative_shutdown.lua +++ b/unit_tests/scripts/lane/uncooperative_shutdown.lua | |||
@@ -8,7 +8,7 @@ local lanes = require "lanes".configure{shutdown_timeout = 0.001, on_state_creat | |||
8 | -- launch lanes that blocks forever | 8 | -- launch lanes that blocks forever |
9 | local lane = function() | 9 | local lane = function() |
10 | local fixture = require "fixture" | 10 | local fixture = require "fixture" |
11 | fixture.forever() | 11 | fixture.sleep_for() |
12 | end | 12 | end |
13 | 13 | ||
14 | -- the generator | 14 | -- the generator |
@@ -20,5 +20,5 @@ local h1 = g1() | |||
20 | -- wait until the lane is running | 20 | -- wait until the lane is running |
21 | repeat until h1.status == "running" | 21 | repeat until h1.status == "running" |
22 | 22 | ||
23 | -- let the script end, Lanes should throw std::logic_error because the lane did not gracefully terminate | 23 | -- this finalizer returns an error string that telling Universe::__gc will use to raise an error when it detects the uncooperative lane |
24 | lanes.finally(fixture.throwing_finalizer) | 24 | lanes.finally(fixture.throwing_finalizer) |
diff --git a/unit_tests/shared.cpp b/unit_tests/shared.cpp index d139579..2e2af73 100644 --- a/unit_tests/shared.cpp +++ b/unit_tests/shared.cpp | |||
@@ -25,35 +25,19 @@ namespace | |||
25 | STACK_CHECK(L_, 0); | 25 | STACK_CHECK(L_, 0); |
26 | } | 26 | } |
27 | 27 | ||
28 | |||
29 | static std::map<lua_State*, std::atomic_flag> sFinalizerHits; | 28 | static std::map<lua_State*, std::atomic_flag> sFinalizerHits; |
30 | static std::mutex sCallCountsLock; | 29 | static std::mutex sCallCountsLock; |
31 | 30 | ||
32 | // a finalizer that we can detect even after closing the state | 31 | // a finalizer that we can detect even after closing the state |
33 | lua_CFunction sThrowingFinalizer = +[](lua_State* L_) { | 32 | lua_CFunction sFreezingFinalizer = +[](lua_State* const L_) { |
34 | std::lock_guard _guard{ sCallCountsLock }; | 33 | std::lock_guard _guard{ sCallCountsLock }; |
35 | sFinalizerHits[L_].test_and_set(); | 34 | sFinalizerHits[L_].test_and_set(); |
36 | luaG_pushstring(L_, "throw"); | 35 | luaG_pushstring(L_, "freeze"); // just freeze the thread in place so that it can be debugged |
37 | return 1; | 36 | return 1; |
38 | }; | 37 | }; |
39 | 38 | ||
40 | // a finalizer that we can detect even after closing the state | ||
41 | lua_CFunction sYieldingFinalizer = +[](lua_State* L_) { | ||
42 | std::lock_guard _guard{ sCallCountsLock }; | ||
43 | sFinalizerHits[L_].test_and_set(); | ||
44 | return 0; | ||
45 | }; | ||
46 | |||
47 | // a function that runs forever | ||
48 | lua_CFunction sForever = +[](lua_State* L_) { | ||
49 | while (true) { | ||
50 | std::this_thread::yield(); | ||
51 | } | ||
52 | return 0; | ||
53 | }; | ||
54 | |||
55 | // a function that returns immediately (so that LuaJIT issues a function call for it) | 39 | // a function that returns immediately (so that LuaJIT issues a function call for it) |
56 | lua_CFunction sGiveMeBack = +[](lua_State* L_) { | 40 | lua_CFunction sGiveMeBack = +[](lua_State* const L_) { |
57 | return lua_gettop(L_); | 41 | return lua_gettop(L_); |
58 | }; | 42 | }; |
59 | 43 | ||
@@ -74,14 +58,42 @@ namespace | |||
74 | return 0; | 58 | return 0; |
75 | }; | 59 | }; |
76 | 60 | ||
61 | // a function that sleeps for the specified duration (in seconds) | ||
62 | lua_CFunction sSleepFor = +[](lua_State* const L_) { | ||
63 | std::chrono::time_point<std::chrono::steady_clock> _until{ std::chrono::time_point<std::chrono::steady_clock>::max() }; | ||
64 | lua_settop(L_, 1); | ||
65 | if (luaG_type(L_, kIdxTop) == LuaType::NUMBER) { // we don't want to use lua_isnumber() because of autocoercion | ||
66 | lua_Duration const _duration{ lua_tonumber(L_, kIdxTop) }; | ||
67 | if (_duration.count() >= 0.0) { | ||
68 | _until = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(_duration); | ||
69 | } else { | ||
70 | raise_luaL_argerror(L_, kIdxTop, "duration cannot be < 0"); | ||
71 | } | ||
72 | |||
73 | } else if (!lua_isnoneornil(L_, 2)) { | ||
74 | raise_luaL_argerror(L_, StackIndex{ 2 }, "incorrect duration type"); | ||
75 | } | ||
76 | std::this_thread::sleep_until(_until); | ||
77 | return 0; | ||
78 | }; | ||
79 | |||
80 | // a finalizer that we can detect even after closing the state | ||
81 | lua_CFunction sThrowingFinalizer = +[](lua_State* const L_) { | ||
82 | std::lock_guard _guard{ sCallCountsLock }; | ||
83 | sFinalizerHits[L_].test_and_set(); | ||
84 | bool const _allLanesTerminated = lua_toboolean(L_, kIdxTop); | ||
85 | luaG_pushstring(L_, "Finalizer%s", _allLanesTerminated ? "" : ": Uncooperative lanes detected"); | ||
86 | return 1; | ||
87 | }; | ||
88 | |||
77 | static luaL_Reg const sFixture[] = { | 89 | static luaL_Reg const sFixture[] = { |
78 | { "forever", sForever }, | 90 | { "freezing_finalizer", sFreezingFinalizer }, |
79 | { "give_me_back()", sGiveMeBack }, | 91 | { "give_me_back()", sGiveMeBack }, |
80 | { "newlightuserdata", sNewLightUserData }, | 92 | { "newlightuserdata", sNewLightUserData }, |
81 | { "newuserdata", sNewUserData }, | 93 | { "newuserdata", sNewUserData }, |
82 | { "on_state_create", sOnStateCreate }, | 94 | { "on_state_create", sOnStateCreate }, |
95 | { "sleep_for", sSleepFor }, | ||
83 | { "throwing_finalizer", sThrowingFinalizer }, | 96 | { "throwing_finalizer", sThrowingFinalizer }, |
84 | { "yielding_finalizer", sYieldingFinalizer }, | ||
85 | { nullptr, nullptr } | 97 | { nullptr, nullptr } |
86 | }; | 98 | }; |
87 | } // namespace local | 99 | } // namespace local |
@@ -448,16 +460,34 @@ FileRunner::FileRunner(std::string_view const& where_) | |||
448 | 460 | ||
449 | void FileRunner::performTest(FileRunnerParam const& testParam_) | 461 | void FileRunner::performTest(FileRunnerParam const& testParam_) |
450 | { | 462 | { |
463 | static constexpr auto _atPanic = [](lua_State* const L_) { | ||
464 | throw std::logic_error("panic!"); | ||
465 | return 0; | ||
466 | }; | ||
467 | |||
468 | #if LUA_VERSION_NUM >= 504 // // warnings are a Lua 5.4 feature | ||
469 | std::string _warnMessage; | ||
470 | static constexpr auto _onWarn = [](void* const opaque_, char const* const msg_, int const tocont_) { | ||
471 | std::string& _warnMessage = *static_cast<std::string*>(opaque_); | ||
472 | _warnMessage += msg_; | ||
473 | }; | ||
474 | #endif // LUA_VERSION_NUM | ||
475 | |||
451 | INFO(testParam_.script); | 476 | INFO(testParam_.script); |
452 | switch (testParam_.test) { | 477 | switch (testParam_.test) { |
453 | case TestType::AssertNoLuaError: | 478 | case TestType::AssertNoLuaError: |
454 | requireSuccess(root, testParam_.script); | 479 | requireSuccess(root, testParam_.script); |
455 | break; | 480 | break; |
456 | case TestType::AssertNoThrow: | 481 | |
457 | REQUIRE_NOTHROW((std::ignore = doFile(root, testParam_.script), close())); | 482 | #if LUA_VERSION_NUM >= 504 // // warnings are a Lua 5.4 feature |
458 | break; | 483 | case TestType::AssertWarns: |
459 | case TestType::AssertThrows: | 484 | lua_atpanic(L, _atPanic); |
460 | REQUIRE_THROWS_AS((std::ignore = doFile(root, testParam_.script), close()), std::logic_error); | 485 | lua_setwarnf(L, _onWarn, &_warnMessage); |
486 | std::ignore = doFile(root, testParam_.script); | ||
487 | close(); | ||
488 | WARN(_warnMessage); | ||
489 | REQUIRE(_warnMessage != std::string_view{}); | ||
461 | break; | 490 | break; |
491 | #endif // LUA_VERSION_NUM | ||
462 | } | 492 | } |
463 | } | 493 | } |
diff --git a/unit_tests/shared.h b/unit_tests/shared.h index 8a84a94..b884df0 100644 --- a/unit_tests/shared.h +++ b/unit_tests/shared.h | |||
@@ -66,8 +66,9 @@ class LuaState | |||
66 | enum class [[nodiscard]] TestType | 66 | enum class [[nodiscard]] TestType |
67 | { | 67 | { |
68 | AssertNoLuaError, | 68 | AssertNoLuaError, |
69 | AssertNoThrow, | 69 | #if LUA_VERSION_NUM >= 504 // warnings are a Lua 5.4 feature |
70 | AssertThrows, | 70 | AssertWarns, |
71 | #endif // LUA_VERSION_NUM | ||
71 | }; | 72 | }; |
72 | 73 | ||
73 | struct FileRunnerParam | 74 | struct FileRunnerParam |