aboutsummaryrefslogtreecommitdiff
path: root/unit_tests
diff options
context:
space:
mode:
authorBenoit Germain <benoit.germain@ubisoft.com>2025-03-11 11:05:22 +0100
committerBenoit Germain <benoit.germain@ubisoft.com>2025-03-11 11:05:22 +0100
commitb1822d8d07f8ee34cef2e7e74391695dd4e1ea81 (patch)
tree961692802cfc90f7217baf2744712820b3d00939 /unit_tests
parentcd6169cf3ecaa41a0aec1cfddd98284fa831285c (diff)
downloadlanes-b1822d8d07f8ee34cef2e7e74391695dd4e1ea81.tar.gz
lanes-b1822d8d07f8ee34cef2e7e74391695dd4e1ea81.tar.bz2
lanes-b1822d8d07f8ee34cef2e7e74391695dd4e1ea81.zip
More unit tests
Diffstat (limited to 'unit_tests')
-rw-r--r--unit_tests/embedded_tests.cpp231
-rw-r--r--unit_tests/shared.h10
2 files changed, 194 insertions, 47 deletions
diff --git a/unit_tests/embedded_tests.cpp b/unit_tests/embedded_tests.cpp
index 1a63721..470deab 100644
--- a/unit_tests/embedded_tests.cpp
+++ b/unit_tests/embedded_tests.cpp
@@ -18,51 +18,129 @@ namespace
18 return 0; 18 return 0;
19 } 19 }
20 } 20 }
21
22 // #########################################################################################
23
24 // count the number of live allocations (not the actual byte count, because we can't be sure osize is correct
25 std::mutex sAllocStatsLock;
26 size_t sAllocCount = 0;
27 size_t sAllocBytes = 0;
28 std::map<void*, size_t> sAllocs;
29
30 // #########################################################################################
31
32 static void* allocf([[maybe_unused]] void* ud, void* ptr, size_t osize, size_t nsize)
33 {
34 std::lock_guard guard{ sAllocStatsLock };
35
36 if (!nsize) // free
37 {
38 if (ptr) {
39 --sAllocCount;
40 sAllocBytes -= osize;
41 free(ptr);
42 sAllocs.erase(ptr);
43 }
44 return nullptr;
45 } else if (!ptr) // malloc
46 {
47 ++sAllocCount;
48 sAllocBytes += nsize;
49 void* new_ptr = realloc(ptr, nsize);
50 if (new_ptr)
51 sAllocs[new_ptr] = nsize;
52 return new_ptr;
53 } else // realloc
54 {
55 int64_t delta = static_cast<int64_t>(nsize) - static_cast<int64_t>(osize);
56 sAllocBytes += static_cast<size_t>(delta);
57 if (!delta) {
58 return ptr;
59 } else {
60 sAllocs.erase(ptr);
61 void* new_ptr = realloc(ptr, nsize);
62 sAllocs[new_ptr] = nsize;
63 return new_ptr;
64 }
65 }
66
67 // same as lauxlib l_alloc
68 //(void)osize;
69 // if (nsize == 0)
70 //{
71 // free(ptr);
72 // return nullptr;
73 //}
74 // else
75 // return realloc(ptr, nsize);
76 }
77
78 // #########################################################################################
79
80 class EmbeddedLuaState : public LuaState
81 {
82 private:
83 HMODULE hCore{ LoadLibraryW(L"lanes\\core") };
84 lua_CFunction lanes_register{};
85
86 public:
87 ~EmbeddedLuaState() {
88 close();
89 FreeLibrary(hCore);
90 }
91 EmbeddedLuaState()
92 : LuaState { LuaState::WithBaseLibs{ false }, LuaState::WithFixture{ false } }
93 {
94
95 if (!hCore) {
96 throw std::logic_error("Could not load lanes.core");
97 }
98 luaopen_lanes_embedded_t const _p_luaopen_lanes_embedded{ reinterpret_cast<luaopen_lanes_embedded_t>(GetProcAddress(hCore, "luaopen_lanes_embedded")) };
99 if (!_p_luaopen_lanes_embedded) {
100 throw std::logic_error("Could not bind luaopen_lanes_embedded");
101 }
102
103 lanes_register = reinterpret_cast<lua_CFunction>(GetProcAddress(hCore, "lanes_register"));
104 if (!lanes_register) {
105 throw std::logic_error("Could not bind lanes_register");
106 }
107 // need base to make lanes happy
108 luaL_requiref(L, LUA_GNAME, luaopen_base, 1);
109 lua_pop(L, 1);
110 stackCheck(0);
111
112 // need package to be able to require lanes
113 luaL_requiref(L, LUA_LOADLIBNAME, luaopen_package, 1);
114 lua_pop(L, 1);
115 stackCheck(0);
116
117 // need table to make lanes happy
118 luaL_requiref(L, LUA_TABLIBNAME, luaopen_table, 1);
119 lua_pop(L, 1);
120 stackCheck(0);
121
122 // need string to make lanes happy
123 luaL_requiref(L, LUA_STRLIBNAME, luaopen_string, 1);
124 lua_pop(L, 1);
125 stackCheck(0);
126
127 _p_luaopen_lanes_embedded(L, local::load_lanes_lua); // S: lanes
128 lua_pop(L, 1);
129 stackCheck(0);
130 }
131
132 [[nodiscard]]
133 auto get_lanes_register() const noexcept { return lanes_register; }
134 };
135
21 } // namespace local 136 } // namespace local
22} 137}
23 138
24// ################################################################################################# 139// #################################################################################################
25 140
26TEST_CASE("lanes.embedding") 141TEST_CASE("lanes.embedding.with default allocator")
27{ 142{
28 LuaState S{ LuaState::WithBaseLibs{ false }, LuaState::WithFixture{ false } }; 143 local::EmbeddedLuaState S;
29
30 HMODULE hCore = LoadLibraryW(L"lanes\\core");
31 if (!hCore) {
32 throw std::logic_error("Could not load lanes.core");
33 }
34 luaopen_lanes_embedded_t const _p_luaopen_lanes_embedded{ reinterpret_cast<luaopen_lanes_embedded_t>(GetProcAddress(hCore, "luaopen_lanes_embedded")) };
35 if (!_p_luaopen_lanes_embedded) {
36 throw std::logic_error("Could not bind luaopen_lanes_embedded");
37 }
38
39 lua_CFunction lanes_register = reinterpret_cast<lua_CFunction>(GetProcAddress(hCore, "lanes_register"));
40 if (!lanes_register) {
41 throw std::logic_error("Could not bind lanes_register");
42 }
43 // need base to make lanes happy
44 luaL_requiref(S, LUA_GNAME, luaopen_base, 1);
45 lua_pop(S, 1);
46 S.stackCheck(0);
47
48 // need package to be able to require lanes
49 luaL_requiref(S, LUA_LOADLIBNAME, luaopen_package, 1);
50 lua_pop(S, 1);
51 S.stackCheck(0);
52
53 // need table to make lanes happy
54 luaL_requiref(S, LUA_TABLIBNAME, luaopen_table, 1);
55 lua_pop(S, 1);
56 S.stackCheck(0);
57
58 // need string to make lanes happy
59 luaL_requiref(S, LUA_STRLIBNAME, luaopen_string, 1);
60 lua_pop(S, 1);
61 S.stackCheck(0);
62
63 _p_luaopen_lanes_embedded(S, local::load_lanes_lua); // S: lanes
64 lua_pop(S, 1);
65 S.stackCheck(0);
66 144
67 // --------------------------------------------------------------------------------------------- 145 // ---------------------------------------------------------------------------------------------
68 146
@@ -102,9 +180,9 @@ TEST_CASE("lanes.embedding")
102 lua_pop(S, 1); 180 lua_pop(S, 1);
103 S.stackCheck(0); 181 S.stackCheck(0);
104 182
105 // try to send io.open into a linda 183 // try to send io.open into a linda, which fails if io base library is not loaded
106 std::string_view const _script{ 184 std::string_view const _script{
107 " local lanes = require 'lanes'.configure{with_timers = false}" 185 " local lanes = require 'lanes'"
108 " local l = lanes.linda'gleh'" 186 " local l = lanes.linda'gleh'"
109 " l:set('yo', io.open)" 187 " l:set('yo', io.open)"
110 " return 'SUCCESS'" 188 " return 'SUCCESS'"
@@ -112,17 +190,78 @@ TEST_CASE("lanes.embedding")
112 S.requireNotReturnedString(_script, "SUCCESS"); 190 S.requireNotReturnedString(_script, "SUCCESS");
113 191
114 // try again after manual registration 192 // try again after manual registration
115 lua_pushcfunction(S, lanes_register); // S: lanes_register 193 lua_pushcfunction(S, S.get_lanes_register()); // S: lanes_register
116 luaG_pushstring(S, LUA_IOLIBNAME); // S: lanes_register "io" 194 luaG_pushstring(S, LUA_IOLIBNAME); // S: lanes_register "io"
117 luaL_requiref(S, LUA_IOLIBNAME, luaopen_io, 1); // S: lanes_register "io" io 195 luaL_requiref(S, LUA_IOLIBNAME, luaopen_io, 1); // S: lanes_register "io" io
118 lua_call(S, 2, 0); // S: 196 lua_call(S, 2, 0); // S:
119 S.stackCheck(0); 197 S.stackCheck(0);
120 S.requireReturnedString(_script, "SUCCESS"); 198 S.requireReturnedString(_script, "SUCCESS");
121 } 199 }
200}
201
202// #################################################################################################
203
204// this is not really a test yet, just something sitting here until it is converted properly
205TEST_CASE("lanes.embedding.with custom allocator")
206{
207 static constexpr auto logPrint = +[](lua_State* L) {
208 lua_getglobal(L, "ID"); // ID
209 printf("[L%d] %s\n", (int) lua_tointeger(L, 2), lua_tostring(L, 1));
210 return 0;
211 };
212
213 static constexpr auto on_state_create = +[](lua_State* L) {
214 lua_pushcfunction(L, logPrint); // logPrint
215 lua_setglobal(L, "logPrint"); //
216 return 0;
217 };
218
219 static constexpr auto launch_lane = +[](lua_CFunction on_state_create_, int id_, int n_) {
220 char script[500];
221 lua_State* L = lua_newstate(local::allocf, nullptr);
222 // _G.ID = id_
223 luaL_openlibs(L);
224 luaL_dostring(L, "lanes = require 'lanes'");
225 lua_getglobal(L, "lanes"); // lanes
226 lua_getfield(L, -1, "configure"); // lanes configure
227 lua_replace(L, 1); // configure
228 lua_newtable(L); // configure {}
229 lua_pushcclosure(L, on_state_create_, 0); // configure {} on_state_create_
230 lua_setfield(L, -2, "on_state_create"); // configure {on_state_create = on_state_create_}
231 lua_pushboolean(L, 0); // configure {on_state_create = on_state_create_} false
232 lua_setfield(L, -2, "with_timers"); // configure {on_state_create = on_state_create_, with_timers = false}
233 // lua_pushliteral(L, "protected"); // configure {on_state_create = on_state_create_} "protected"
234 // lua_setfield(L, -2, "allocator"); // configure {on_state_create = on_state_create_, allocater = "protected"}
235 lua_pcall(L, 1, 0, 0);
236 sprintf_s(script,
237 "g = lanes.gen('*', {globals = {ID = %d}}, function(id_) lane_threadname('Lane %d.'..id_) logPrint('This is L%d.'..id_) end)" // lane generator
238 "for i = 1,%d do _G['a'..i] = g(i) end" // launch a few lanes, handle stored in global a<i>
239 ,
240 id_,
241 id_,
242 id_,
243 n_);
244 luaL_dostring(L, script); // when script ends, globals are collected, lanes are terminated gracefully
245 return L;
246 };
247
248 local::EmbeddedLuaState S;
122 249
123 // --------------------------------------------------------------------------------------------- 250 // ---------------------------------------------------------------------------------------------
124 251
125 // close state manually before we unload lanes.core 252 // L1: require 'lanes'.configure{on_state_create = L1_on_state_create, with_timers = false}
126 S.close(); 253 lua_State* const L1 = launch_lane(on_state_create, 1, 5);
127 FreeLibrary(hCore); 254
128} 255 // L2: require 'lanes'.configure{on_state_create = L2_on_state_create, with_timers = false}
256 lua_State* const L2 = launch_lane(on_state_create, 2, 5);
257
258 // L3: require 'lanes'.configure{on_state_create = L3_on_state_create, with_timers = false}
259 lua_State* const L3 = launch_lane(on_state_create, 3, 5);
260
261 // give some time to the lanes to execute
262 std::this_thread::sleep_for(1000ms);
263
264 lua_close(L3);
265 lua_close(L2);
266 lua_close(L1);
267} \ No newline at end of file
diff --git a/unit_tests/shared.h b/unit_tests/shared.h
index de7938e..c7cdba5 100644
--- a/unit_tests/shared.h
+++ b/unit_tests/shared.h
@@ -18,7 +18,15 @@ class LuaState
18 } 18 }
19 LuaState(WithBaseLibs withBaseLibs_, WithFixture withFixture_); 19 LuaState(WithBaseLibs withBaseLibs_, WithFixture withFixture_);
20 20
21 LuaState(LuaState&& rhs_) = default; 21 LuaState(LuaState const&) = delete;
22 LuaState(LuaState&& rhs_) noexcept
23 : L{ std::exchange(rhs_.L, nullptr) }
24 {
25 }
26 LuaState& operator=(LuaState const&) = delete;
27 LuaState& operator=(LuaState&& rhs_) noexcept {
28 L = std::exchange(rhs_.L, nullptr);
29 }
22 30
23 public: 31 public:
24 32