• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

jordanvrtanoski / luacpp / d7c55702-4001-4c25-8c7e-0299d3a984d4

19 Feb 2026 12:11AM UTC coverage: 99.518% (-0.5%) from 100.0%
d7c55702-4001-4c25-8c7e-0299d3a984d4

Pull #34

circleci

jordanvrtanoski
[IMP] Add Lua state pooling with colored pools

Implement opt-in state pooling system for high-performance scenarios:
- StatePool class for managing reusable lua_State instances
- PoolManager with predefined pool colors (default, sandboxed, minimal, io)
- Selective library loading per pool configuration
- PooledState RAII wrapper for exception-safe state management
- Thread-safe pool mode for multi-threaded applications
- Pool warmup/drain/statistics methods
- PoolExhaustedException for handling pool exhaustion

Add LuaContext integration:
- RunPooled(), RunWithEnvironmentPooled() methods
- AcquirePooledState(), ReleasePooledState() methods
- AcquirePooledStateRAII() for automatic state return
- createPool(), hasPool(), getPool() management methods

Add comprehensive documentation and examples:
- docs/2-state-pooling.md: Complete pooling documentation
- docs/1-introduction.md: Added pooling overview section
- example_StatePool.cpp: Basic pooling usage
- example_StatePoolAdvanced.cpp: Advanced patterns with threads

All 126 tests passing including 56 new pooling tests.

Signed-off-by: Jordan Vrtanoski <jordan.vrtanoski@gmail.com>
Pull Request #34: [IMP] Add Lua state pooling with colored pools

975 of 987 new or added lines in 10 files covered. (98.78%)

1 existing line in 1 file now uncovered.

2682 of 2695 relevant lines covered (99.52%)

49.01 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.32
/Source/UnitTest/TestLuaContextNewState.cpp
1
#include "../LuaCpp.hpp"
2
#include "gtest/gtest.h"
3

4
namespace LuaCpp {
5

6
    // Global hook count
7
    int hook_count = 0;
8

9
    // Custom allocator function for testing
10
    void* customAllocator(void *ud, void *p, size_t osize, size_t nsize) {
1,274✔
11
        // Implement proper allocation logic here
12
        if (nsize == 0) {
1,274✔
13
            free(p);
656✔
14
            return nullptr;
656✔
15
        }
16
        if (p == nullptr) {
618✔
17
            return malloc(nsize);
606✔
18
        }
19
        if (osize == nsize) {
12✔
UNCOV
20
            return p; // No change in size, return the same pointer
×
21
        }
22
        // Resize the allocation
23
        void *newp = malloc(nsize);
12✔
24
        if (newp != nullptr) {
12✔
25
            if (p != nullptr) {
12✔
26
                memcpy(newp, p, osize < nsize ? osize : nsize);
12✔
27
            }
28
            free(p);
12✔
29
        }
30
        return newp;
12✔
31
    }
32

33
    class TestLuaContextNewState : public ::testing::Test {
34
      protected:
35
        virtual void SetUp() {}
20✔
36
    };
37

38
    TEST_F(TestLuaContextNewState, TestNewState) {
8✔
39
        /**
40
         * Test the newState() method of LuaContext
41
         */
42
        LuaContext ctx;
4✔
43

44
        // Test newState() without parameters
45
        std::unique_ptr<Engine::LuaState> L = ctx.newState();
4✔
46
        EXPECT_NE((Engine::LuaState *) NULL, L.get());
2✔
47

48
        // Test newState() with environment
49
        LuaEnvironment env;
4✔
50
        std::shared_ptr<Engine::LuaTString> str = std::make_shared<Engine::LuaTString>("test_value");
4✔
51
        env["test_str"] = str;
2✔
52

53
        std::unique_ptr<Engine::LuaState> L2 = ctx.newState(env);
4✔
54
        EXPECT_NE((Engine::LuaState *) NULL, L2.get());
2✔
55

56
        // Verify that the global variable is set in the new state
57
        lua_getglobal(*L2, "test_str");
2✔
58
        EXPECT_TRUE(lua_isstring(*L2, -1));
2✔
59
        EXPECT_STREQ("test_value", lua_tostring(*L2, -1));
2✔
60
        lua_pop(*L2, 1);
2✔
61
    }
2✔
62

63
    int SumFunction(lua_State* L) {
2✔
64
        int sum = 0;
2✔
65
        int n = lua_gettop(L); // Number of arguments
2✔
66
        for (int i = 1; i <= n; i++) {
8✔
67
            sum += lua_tonumber(L, i);
6✔
68
        }
69
        lua_pushnumber(L, sum);
2✔
70
        return 1;
2✔
71
    }
72

73
    TEST_F(TestLuaContextNewState, TestNewStateWithBuiltInFunctions) {
8✔
74
        /**
75
         * Test the newState() method with built-in functions
76
         */
77
        LuaContext ctx;
4✔
78

79
        // Add the built-in function to the context
80
        ctx.setBuiltInFnc("sum", SumFunction);
2✔
81

82
        // Create a new state
83
        std::unique_ptr<Engine::LuaState> L = ctx.newState();
4✔
84

85
        // Verify that the built-in function is available in the new state
86
        lua_getglobal(*L, "sum");
2✔
87
        EXPECT_TRUE(lua_isfunction(*L, -1));
2✔
88
        lua_pop(*L, 1);
2✔
89

90
        // Test the function with actual arguments
91
        lua_getglobal(*L, "sum");
2✔
92
        lua_pushnumber(*L, 1);
2✔
93
        lua_pushnumber(*L, 2);
2✔
94
        lua_pushnumber(*L, 3);
2✔
95
        lua_call(*L, 3, 1);
2✔
96
        EXPECT_DOUBLE_EQ(6.0, lua_tonumber(*L, -1));
2✔
97
        lua_pop(*L, 1);
2✔
98
    }
2✔
99

100
    int AddFunction(lua_State* L) {
2✔
101
        int a = lua_tonumber(L, 1);
2✔
102
        int b = lua_tonumber(L, 2);
2✔
103
        lua_pushnumber(L, a + b);
2✔
104
        return 1;
2✔
105
    }
106

107
    TEST_F(TestLuaContextNewState, TestSetBuiltInFnc) {
8✔
108
        /**
109
         * Test the setBuiltInFnc method with and without the replace flag
110
         */
111
        LuaContext ctx;
4✔
112

113
        // Add a built-in function without replace flag
114
        ctx.setBuiltInFnc("add", AddFunction);
2✔
115

116
        // Verify that the function is added
117
        std::unique_ptr<Engine::LuaState> L = ctx.newState();
4✔
118
        lua_getglobal(*L, "add");
2✔
119
        EXPECT_TRUE(lua_isfunction(*L, -1));
2✔
120
        lua_pop(*L, 1);
2✔
121

122
        // Add the same function with replace=true
123
        ctx.setBuiltInFnc("add", AddFunction, true);
2✔
124

125
        // Verify that the function is still there after replacement
126
        std::unique_ptr<Engine::LuaState> L2 = ctx.newState();
4✔
127
        lua_getglobal(*L2, "add");
2✔
128
        EXPECT_TRUE(lua_isfunction(*L2, -1));
2✔
129
        lua_pop(*L2, 1);
2✔
130

131
        // Test the function with actual arguments
132
        lua_getglobal(*L2, "add");
2✔
133
        lua_pushnumber(*L2, 1);
2✔
134
        lua_pushnumber(*L2, 2);
2✔
135
        lua_call(*L2, 2, 1);
2✔
136
        EXPECT_DOUBLE_EQ(3.0, lua_tonumber(*L2, -1));
2✔
137
        lua_pop(*L2, 1);
2✔
138
    }
2✔
139

140
    void CountHook(lua_State* L, lua_Debug* ar) {
2✔
141
        // This is a simple hook that just counts calls
142
        hook_count++;
2✔
143
    }
2✔
144

145
    TEST_F(TestLuaContextNewState, TestRegisterHooks) {
8✔
146
        /**
147
         * Test the registerHooks method with different hook types
148
         */
149
        LuaContext ctx;
4✔
150

151
        // Reset the hook count
152
        hook_count = 0;
2✔
153

154
        // Add hooks of different types
155
        ctx.addHook(lua_Hook(CountHook), "call", 0);
2✔
156
        ctx.addHook(lua_Hook(CountHook), "return", 0);
2✔
157
        ctx.addHook(lua_Hook(CountHook), "line", 1);
2✔
158
        ctx.addHook(lua_Hook(CountHook), "count", 10000);
2✔
159

160
        // Create a new state
161
        std::unique_ptr<Engine::LuaState> L = ctx.newState();
4✔
162

163
        // Call registerHooks to register all hooks
164
        ctx.registerHooks(*L);
2✔
165

166
        // Execute some Lua code to trigger the hooks
167
        luaL_dostring(*L, "for i = 1, 10000 do end"); // This will trigger the count hook
2✔
168

169
        // Verify that hooks were triggered
170
        EXPECT_GT(hook_count, 0);
2✔
171
    }
2✔
172

173
    int ExceptionFunction(lua_State* L) {
2✔
174
        throw std::runtime_error("Exception in Lua function");
2✔
175
        return 0; // Unreachable
176
    }
177

178
    TEST_F(TestLuaContextNewState, TestNewRuntimeException) {
8✔
179
        /**
180
         * Test that newState() properly handles exceptions
181
         */
182
        LuaContext ctx;
4✔
183

184
        // Add a built-in function that throws an exception
185
        ctx.setBuiltInFnc("exception", ExceptionFunction);
2✔
186

187
        // Create a new state
188
        std::unique_ptr<Engine::LuaState> L = ctx.newState();
4✔
189

190
        // Create a lambda to catch the exception
191
        auto testException = [&]() {
2✔
192
            lua_getglobal(*L, "exception");
2✔
193
            lua_call(*L, 0, 0);
2✔
194
        };
2✔
195

196
        // Verify that an exception is thrown
197
        EXPECT_THROW(testException(), std::runtime_error);
4✔
198
    }
2✔
199

200
    TEST_F(TestLuaContextNewState, TestSetBuiltInFncNullCFunction) {
8✔
201
         /**
202
          * Test that setBuiltInFnc throws an exception when a null C function is provided
203
          * and then newState is called.
204
          */
205
         LuaContext ctx;
4✔
206

207
         // Set a built-in function with a null C function
208
         ctx.setBuiltInFnc("null_func", nullptr);
2✔
209

210
         // Expect an exception when newState is called, as it tries to register the null function
211
         EXPECT_THROW(ctx.newState(), std::runtime_error);
4✔
212
    }
2✔
213

214
    TEST_F(TestLuaContextNewState, TestNewStateWithCustomParams) {
8✔
215
        /**
216
         * Test the newState() method with custom StateParams
217
         */
218
        LuaContext ctx;
4✔
219

220
        // Create StateParams with custom allocator
221
        Engine::StateParams params;
2✔
222
        params.allocator = customAllocator;
2✔
223

224
        // Create a new state with custom parameters
225
        std::unique_ptr<Engine::LuaState> L = ctx.newState(params);
4✔
226

227
        // Verify the state was created successfully
228
        EXPECT_NE((Engine::LuaState *) nullptr, L.get());
2✔
229

230
        // Execute a simple Lua command to verify the state works
231
        int result = luaL_dostring(*L, "result = 42");
2✔
232
        EXPECT_EQ(0, result); // 0 means success
2✔
233

234
        lua_getglobal(*L, "result");
2✔
235
        EXPECT_TRUE(lua_isnumber(*L, -1));
2✔
236
        EXPECT_DOUBLE_EQ(42.0, lua_tonumber(*L, -1));
2✔
237
        lua_pop(*L, 1);
2✔
238
    }
2✔
239

240
    // New test for CreateStateFor method
241
    TEST_F(TestLuaContextNewState, TestCreateStateFor) {
8✔
242
        /**
243
         * Test the CreateStateFor method of LuaContext
244
         */
245
        LuaContext ctx;
4✔
246

247
        // Compile a simple Lua code snippet
248
        const std::string code = "function test() return 42 end";
4✔
249
        ctx.CompileString("test_snippet", code);
2✔
250

251
        // Create a state for the snippet
252
        StateProxy proxy = ctx.CreateStateFor("test_snippet");
6✔
253

254
        // Create a LuaEnvironment to pass to RunWithEnvironment
255
        LuaEnvironment env;
4✔
256

257
        // The function should be available in the global environment
258
        EXPECT_NO_THROW(proxy.RunWithEnvironment(env));
2✔
259

260
    }
2✔
261

262
    // New test for StateProxy::RunWithEnvironment - normal execution
263
    TEST_F(TestLuaContextNewState, TestStateProxyRunWithEnvironment) {
8✔
264
        /**
265
         * Test the StateProxy::RunWithEnvironment method with normal execution
266
         */
267
        LuaContext ctx;
4✔
268
        
269
        // Compile a simple Lua code snippet
270
        const std::string code = "function test() return 42 end";
4✔
271
        ctx.CompileString("test_snippet", code);
2✔
272

273
        // Create a Lua state
274
        std::unique_ptr<Engine::LuaState> L = ctx.newStateFor("test_snippet");
6✔
275
        StateProxy proxy(std::move(L));
4✔
276

277
        // Create a LuaEnvironment with a string variable
278
        LuaEnvironment env;
4✔
279
        std::shared_ptr<Engine::LuaTString> str = std::make_shared<Engine::LuaTString>("test_value");
4✔
280
        env["test_str"] = str;
2✔
281

282
        // Run with environment - this should push and pop the variable correctly
283
        EXPECT_NO_THROW(proxy.RunWithEnvironment(env));
2✔
284

285
    }
2✔
286

287
    // New test for StateProxy::RunWithEnvironment - error handling
288
    TEST_F(TestLuaContextNewState, TestStateProxyRunWithEnvironmentError) {
8✔
289
        /**
290
         * Test the StateProxy::RunWithEnvironment method with error handling
291
         */
292
        LuaContext ctx;
4✔
293

294
        // Create a Lua state with an error in the code
295
        std::unique_ptr<Engine::LuaState> L = ctx.newState();
4✔
296
        int result = luaL_dostring(*L, "error('Test error')");
2✔
297
        EXPECT_NE(0, result); // Should already be in error state
2✔
298

299
        StateProxy proxy(std::move(L));
4✔
300

301
        // Create a LuaEnvironment
302
        LuaEnvironment env;
4✔
303

304
        // Run with environment - this should catch the error and throw an exception
305
        EXPECT_THROW(proxy.RunWithEnvironment(env), std::runtime_error);
4✔
306
    }
2✔
307

308
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc