diff options
Diffstat (limited to '')
-rw-r--r-- | unit_tests/lane_tests.cpp | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/unit_tests/lane_tests.cpp b/unit_tests/lane_tests.cpp new file mode 100644 index 0000000..14a327f --- /dev/null +++ b/unit_tests/lane_tests.cpp | |||
@@ -0,0 +1,209 @@ | |||
1 | #include "_pch.hpp" | ||
2 | #include "shared.h" | ||
3 | |||
4 | // ################################################################################################# | ||
5 | // ################################################################################################# | ||
6 | |||
7 | class LaneTests : public ::testing::Test | ||
8 | { | ||
9 | protected: | ||
10 | LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } }; | ||
11 | |||
12 | void SetUp() override | ||
13 | { | ||
14 | std::ignore = S.doString("lanes = require 'lanes'.configure()"); | ||
15 | } | ||
16 | }; | ||
17 | |||
18 | // ################################################################################################# | ||
19 | |||
20 | TEST_F(LaneTests, GeneratorCreation) | ||
21 | { | ||
22 | // no parameter is bad | ||
23 | EXPECT_NE(S.doString("lanes.gen()"), LuaError::OK); | ||
24 | |||
25 | // minimal generator needs a function | ||
26 | EXPECT_EQ(S.doString("lanes.gen(function() end)"), LuaError::OK) << S; | ||
27 | |||
28 | // acceptable parameters for the generator are strings, tables, nil, followed by the function body | ||
29 | EXPECT_EQ(S.doString("lanes.gen(nil, function() end)"), LuaError::OK) << S; | ||
30 | EXPECT_EQ(S.doString("lanes.gen('', function() end)"), LuaError::OK) << S; | ||
31 | EXPECT_EQ(S.doString("lanes.gen({}, function() end)"), LuaError::OK) << S; | ||
32 | EXPECT_EQ(S.doString("lanes.gen('', {}, function() end)"), LuaError::OK) << S; | ||
33 | EXPECT_EQ(S.doString("lanes.gen({}, '', function() end)"), LuaError::OK) << S; | ||
34 | EXPECT_EQ(S.doString("lanes.gen('', '', function() end)"), LuaError::OK) << S; | ||
35 | EXPECT_EQ(S.doString("lanes.gen({}, {}, function() end)"), LuaError::OK) << S; | ||
36 | |||
37 | // anything different should fail: booleans, numbers, any userdata | ||
38 | EXPECT_NE(S.doString("lanes.gen(false, function() end)"), LuaError::OK); | ||
39 | EXPECT_NE(S.doString("lanes.gen(true, function() end)"), LuaError::OK); | ||
40 | EXPECT_NE(S.doString("lanes.gen(42, function() end)"), LuaError::OK); | ||
41 | EXPECT_NE(S.doString("lanes.gen(io.stdin, function() end)"), LuaError::OK); | ||
42 | EXPECT_NE(S.doString("lanes.gen(lanes.linda(), function() end)"), LuaError::OK); | ||
43 | EXPECT_NE(S.doString("lanes.gen(lanes.linda():deep(), function() end)"), LuaError::OK); | ||
44 | |||
45 | // even if parameter types are correct, the function must come last | ||
46 | EXPECT_NE(S.doString("lanes.gen(function() end, '')"), LuaError::OK); | ||
47 | |||
48 | // the strings should only list "known base libraries", in any order, or "*" | ||
49 | // if the particular Lua flavor we build for doesn't support them, they raise an error unless postfixed by '?' | ||
50 | EXPECT_EQ(S.doString("lanes.gen('base', function() end)"), LuaError::OK) << S; | ||
51 | |||
52 | // bit, ffi, jit are LuaJIT-specific | ||
53 | #if LUAJIT_FLAVOR() == 0 | ||
54 | EXPECT_NE(S.doString("lanes.gen('bit,ffi,jit', function() end)"), LuaError::OK); | ||
55 | EXPECT_EQ(S.doString("lanes.gen('bit?,ffi?,jit?', function() end)"), LuaError::OK) << S; | ||
56 | #endif // LUAJIT_FLAVOR() | ||
57 | |||
58 | // bit32 library existed only in Lua 5.2, there is still a loader that will raise an error in Lua 5.3 | ||
59 | #if LUA_VERSION_NUM == 502 || LUA_VERSION_NUM == 503 | ||
60 | EXPECT_EQ(S.doString("lanes.gen('bit32', function() end)"), LuaError::OK) << S; | ||
61 | #else // LUA_VERSION_NUM == 502 || LUA_VERSION_NUM == 503 | ||
62 | EXPECT_NE(S.doString("lanes.gen('bit32', function() end)"), LuaError::OK); | ||
63 | EXPECT_EQ(S.doString("lanes.gen('bit32?', function() end)"), LuaError::OK) << S; | ||
64 | #endif // LUA_VERSION_NUM == 502 || LUA_VERSION_NUM == 503 | ||
65 | |||
66 | // coroutine library appeared with Lua 5.2 | ||
67 | #if LUA_VERSION_NUM == 501 | ||
68 | EXPECT_NE(S.doString("lanes.gen('coroutine', function() end)"), LuaError::OK); | ||
69 | EXPECT_EQ(S.doString("lanes.gen('coroutine?', function() end)"), LuaError::OK) << S; | ||
70 | #endif // LUA_VERSION_NUM == 501 | ||
71 | |||
72 | EXPECT_EQ(S.doString("lanes.gen('debug', function() end)"), LuaError::OK) << S; | ||
73 | EXPECT_EQ(S.doString("lanes.gen('io', function() end)"), LuaError::OK) << S; | ||
74 | EXPECT_EQ(S.doString("lanes.gen('math', function() end)"), LuaError::OK) << S; | ||
75 | EXPECT_EQ(S.doString("lanes.gen('os', function() end)"), LuaError::OK) << S; | ||
76 | EXPECT_EQ(S.doString("lanes.gen('package', function() end)"), LuaError::OK) << S; | ||
77 | EXPECT_EQ(S.doString("lanes.gen('string', function() end)"), LuaError::OK) << S; | ||
78 | EXPECT_EQ(S.doString("lanes.gen('table', function() end)"), LuaError::OK) << S; | ||
79 | |||
80 | // utf8 library appeared with Lua 5.3 | ||
81 | #if LUA_VERSION_NUM < 503 | ||
82 | EXPECT_NE(S.doString("lanes.gen('utf8', function() end)"), LuaError::OK); | ||
83 | EXPECT_EQ(S.doString("lanes.gen('utf8?', function() end)"), LuaError::OK) << S; | ||
84 | #endif // LUA_VERSION_NUM < 503 | ||
85 | |||
86 | EXPECT_EQ(S.doString("lanes.gen('lanes.core', function() end)"), LuaError::OK) << S; | ||
87 | // "*" repeated or combined with anything else is forbidden | ||
88 | EXPECT_NE(S.doString("lanes.gen('*', '*', function() end)"), LuaError::OK); | ||
89 | EXPECT_NE(S.doString("lanes.gen('base', '*', function() end)"), LuaError::OK); | ||
90 | // unknown names are forbidden | ||
91 | EXPECT_NE(S.doString("lanes.gen('Base', function() end)"), LuaError::OK); | ||
92 | // repeating the same library more than once is forbidden | ||
93 | EXPECT_NE(S.doString("lanes.gen('base,base', function() end)"), LuaError::OK); | ||
94 | } | ||
95 | |||
96 | // ################################################################################################# | ||
97 | |||
98 | TEST_F(LaneTests, UncooperativeShutdown) | ||
99 | { | ||
100 | // prepare a callback for lanes.finally() | ||
101 | static bool _wasCalled{}; | ||
102 | static bool _allLanesTerminated{}; | ||
103 | auto _finallyCB{ +[](lua_State* const L_) { _wasCalled = true; _allLanesTerminated = lua_toboolean(L_, 1); return 0; } }; | ||
104 | lua_pushcfunction(S, _finallyCB); | ||
105 | lua_setglobal(S, "finallyCB"); | ||
106 | // start a lane that lasts a long time | ||
107 | std::string_view const script{ | ||
108 | " lanes.finally(finallyCB)" | ||
109 | " print ('in Master')" | ||
110 | " f = lanes.gen('*'," | ||
111 | " {name = 'auto'}," | ||
112 | " function()" | ||
113 | " for i = 1,1e37 do end" // no cooperative cancellation checks here! | ||
114 | " end)" | ||
115 | " f()" | ||
116 | }; | ||
117 | ASSERT_EQ(S.doString(script), LuaError::OK) << S; | ||
118 | // close the state before the lane ends. | ||
119 | // since we don't wait at all, it is possible that the OS thread for the lane hasn't even started at that point | ||
120 | S.close(); | ||
121 | // the finally handler should have been called, and told all lanes are stopped | ||
122 | ASSERT_EQ(_wasCalled, true) << S; | ||
123 | ASSERT_EQ(_allLanesTerminated, true) << S; | ||
124 | } | ||
125 | |||
126 | // ################################################################################################# | ||
127 | // ################################################################################################# | ||
128 | |||
129 | class LaneCancel : public ::testing::Test | ||
130 | { | ||
131 | protected: | ||
132 | LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ true } }; | ||
133 | |||
134 | void SetUp() override | ||
135 | { | ||
136 | // need the timers so that there is a lane running on which we can operate | ||
137 | std::ignore = S.doString("timer_lane = require 'lanes'.configure{with_timers = true}.timer_lane"); | ||
138 | } | ||
139 | }; | ||
140 | |||
141 | // ################################################################################################# | ||
142 | |||
143 | TEST_F(LaneCancel, FailsOnBadArguments) | ||
144 | { | ||
145 | //FAIL() << "Make sure the failures fail the way we expect them"; | ||
146 | // make sure we have the timer lane and its cancel method handy | ||
147 | ASSERT_EQ(S.doString("assert(timer_lane and timer_lane.cancel)"), LuaError::OK); | ||
148 | // as well as the fixture module | ||
149 | ASSERT_EQ(S.doString("fixture = require 'fixture'"), LuaError::OK) << S; | ||
150 | |||
151 | // cancel operation must be a known string | ||
152 | ASSERT_NE(S.doString("timer_lane:cancel('gleh')"), LuaError::OK); | ||
153 | ASSERT_NE(S.doString("timer_lane:cancel(function() end)"), LuaError::OK); | ||
154 | ASSERT_NE(S.doString("timer_lane:cancel({})"), LuaError::OK); | ||
155 | ASSERT_NE(S.doString("timer_lane:cancel(fixture.newuserdata())"), LuaError::OK); | ||
156 | ASSERT_NE(S.doString("timer_lane:cancel(fixture.newlightuserdata())"), LuaError::OK); | ||
157 | |||
158 | // cancel doesn't expect additional non-number/bool arguments after the mode | ||
159 | ASSERT_NE(S.doString("timer_lane:cancel('soft', 'gleh')"), LuaError::OK); | ||
160 | ASSERT_NE(S.doString("timer_lane:cancel('soft', function() end)"), LuaError::OK); | ||
161 | ASSERT_NE(S.doString("timer_lane:cancel('soft', {})"), LuaError::OK); | ||
162 | ASSERT_NE(S.doString("timer_lane:cancel('soft', fixture.newuserdata())"), LuaError::OK); | ||
163 | ASSERT_NE(S.doString("timer_lane:cancel('soft', fixture.newlightuserdata())"), LuaError::OK); | ||
164 | |||
165 | // hook-based cancellation expects a number for the count. IOW, a bool is not valid | ||
166 | ASSERT_NE(S.doString("timer_lane:cancel('call', true)"), LuaError::OK); | ||
167 | ASSERT_NE(S.doString("timer_lane:cancel('ret', true)"), LuaError::OK); | ||
168 | ASSERT_NE(S.doString("timer_lane:cancel('line', true)"), LuaError::OK); | ||
169 | ASSERT_NE(S.doString("timer_lane:cancel('count', true)"), LuaError::OK); | ||
170 | ASSERT_NE(S.doString("timer_lane:cancel('all', true)"), LuaError::OK); | ||
171 | |||
172 | // non-hook should only have one number after the mode (the timeout), else it means we have a count | ||
173 | ASSERT_NE(S.doString("timer_lane:cancel('hard', 10, 10)"), LuaError::OK); | ||
174 | |||
175 | // extra arguments are not accepted either | ||
176 | ASSERT_NE(S.doString("timer_lane:cancel('hard', 10, true, 10)"), LuaError::OK); | ||
177 | ASSERT_NE(S.doString("timer_lane:cancel('call', 10, 10, 10)"), LuaError::OK); | ||
178 | ASSERT_NE(S.doString("timer_lane:cancel('line', 10, 10, true, 10)"), LuaError::OK); | ||
179 | |||
180 | // out-of-range hook count is not valid | ||
181 | ASSERT_NE(S.doString("timer_lane:cancel('call', -1)"), LuaError::OK); | ||
182 | ASSERT_NE(S.doString("timer_lane:cancel('call', 0)"), LuaError::OK); | ||
183 | |||
184 | // out-of-range duration is not valid | ||
185 | ASSERT_NE(S.doString("timer_lane:cancel('soft', -1)"), LuaError::OK); | ||
186 | } | ||
187 | |||
188 | // ################################################################################################# | ||
189 | // ################################################################################################# | ||
190 | |||
191 | INSTANTIATE_TEST_CASE_P( | ||
192 | LaneScriptedTests, | ||
193 | UnitTestRunner, | ||
194 | ::testing::Values( | ||
195 | FileRunnerParam{ PUC_LUA_ONLY("lane/cooperative_shutdown"), TestType::AssertNoLuaError }, // 0 | ||
196 | FileRunnerParam{ "lane/uncooperative_shutdown", TestType::AssertThrows }, | ||
197 | FileRunnerParam{ "lane/tasking_basic", TestType::AssertNoLuaError }, // 2 | ||
198 | FileRunnerParam{ "lane/tasking_cancelling", TestType::AssertNoLuaError }, // 3 | ||
199 | FileRunnerParam{ "lane/tasking_comms_criss_cross", TestType::AssertNoLuaError }, // 4 | ||
200 | FileRunnerParam{ "lane/tasking_communications", TestType::AssertNoLuaError }, | ||
201 | FileRunnerParam{ "lane/tasking_error", TestType::AssertNoLuaError }, // 6 | ||
202 | FileRunnerParam{ "lane/tasking_join_test", TestType::AssertNoLuaError }, // 7 | ||
203 | FileRunnerParam{ "lane/tasking_send_receive_code", TestType::AssertNoLuaError }, | ||
204 | FileRunnerParam{ "lane/stdlib_naming", TestType::AssertNoLuaError }, | ||
205 | FileRunnerParam{ "coro/basics", TestType::AssertNoLuaError }, // 10 | ||
206 | FileRunnerParam{ "coro/error_handling", TestType::AssertNoLuaError } | ||
207 | ) | ||
208 | //,[](::testing::TestParamInfo<FileRunnerParam> const& info_) { return std::string{ info_.param.script };} | ||
209 | ); | ||