diff options
| author | Benoit Germain <benoit.germain@ubisoft.com> | 2025-03-17 12:34:08 +0100 |
|---|---|---|
| committer | Benoit Germain <benoit.germain@ubisoft.com> | 2025-03-17 12:34:08 +0100 |
| commit | a57690123ae3ce5bdd7e970690f1380e88e4eaf6 (patch) | |
| tree | d526e8f545cef2b1c23978cb9ee5c94dbc9cda2c /unit_tests | |
| parent | d93de7ca51edea911eeecb7c8edcffe77298ed07 (diff) | |
| download | lanes-a57690123ae3ce5bdd7e970690f1380e88e4eaf6.tar.gz lanes-a57690123ae3ce5bdd7e970690f1380e88e4eaf6.tar.bz2 lanes-a57690123ae3ce5bdd7e970690f1380e88e4eaf6.zip | |
Raise a regular Lua error instead of throwing a C++ std::logic_error exception in Universe::UniverseGC
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 |
