diff options
Diffstat (limited to 'unit_tests/shared.cpp')
-rw-r--r-- | unit_tests/shared.cpp | 82 |
1 files changed, 56 insertions, 26 deletions
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 | } |