aboutsummaryrefslogtreecommitdiff
path: root/unit_tests/shared.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'unit_tests/shared.cpp')
-rw-r--r--unit_tests/shared.cpp82
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
449void FileRunner::performTest(FileRunnerParam const& testParam_) 461void 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}