1 #define SOL_CHECK_ARGUMENTS
2
3 #include <sol.hpp>
4 #include <catch.hpp>
5
6 #include <iterator>
7 #include <vector>
8 #include <list>
9 #include <map>
10 #include <unordered_map>
11 #include <set>
12 #include <unordered_set>
13
test_table_return_one()14 std::vector<int> test_table_return_one() {
15 return{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
16 }
17
test_table_return_two()18 std::vector<std::pair<std::string, int>> test_table_return_two() {
19 return{ { "one", 1 },{ "two", 2 },{ "three", 3 } };
20 }
21
test_table_return_three()22 std::map<std::string, std::string> test_table_return_three() {
23 return{ { "name", "Rapptz" },{ "friend", "ThePhD" },{ "project", "sol" } };
24 }
25
26 TEST_CASE("containers/returns", "make sure that even references to vectors are being serialized as tables") {
27 sol::state lua;
28 std::vector<int> v{ 1, 2, 3 };
__anona2d380c70102() 29 lua.set_function("f", [&]() -> std::vector<int>& {
30 return v;
31 });
32 lua.script("x = f()");
33 sol::object x = lua["x"];
34 sol::type xt = x.get_type();
35 REQUIRE(xt == sol::type::userdata);
36 sol::table t = x;
37 bool matching;
38 matching = t[1] == 1;
39 REQUIRE(matching);
40 matching = t[2] == 2;
41 REQUIRE(matching);
42 matching = t[3] == 3;
43 REQUIRE(matching);
44 }
45
46 TEST_CASE("containers/vector_roundtrip", "make sure vectors can be round-tripped") {
47 sol::state lua;
48 std::vector<int> v{ 1, 2, 3 };
__anona2d380c70202() 49 lua.set_function("f", [&]() -> std::vector<int>& {
50 return v;
51 });
52 lua.script("x = f()");
53 std::vector<int> x = lua["x"];
54 bool areequal = x == v;
55 REQUIRE(areequal);
56 }
57
58 TEST_CASE("containers/list_roundtrip", "make sure lists can be round-tripped") {
59 sol::state lua;
60 std::list<int> v{ 1, 2, 3 };
__anona2d380c70302() 61 lua.set_function("f", [&]() -> std::list<int>& {
62 return v;
63 });
64 lua.script("x = f()");
65 std::list <int> x = lua["x"];
66 bool areequal = x == v;
67 REQUIRE(areequal);
68 }
69
70 TEST_CASE("containers/map_roundtrip", "make sure maps can be round-tripped") {
71 sol::state lua;
72 std::map<std::string, int> v{ { "a", 1 },{ "b", 2 },{ "c", 3 } };
__anona2d380c70402() 73 lua.set_function("f", [&]() -> std::map<std::string, int>& {
74 return v;
75 });
76 lua.script("x = f()");
77 std::map<std::string, int> x = lua["x"];
78 bool areequal = x == v;
79 REQUIRE(areequal);
80 }
81
82 TEST_CASE("containers/unordered_map_roundtrip", "make sure unordered_maps can be round-tripped") {
83 sol::state lua;
84 std::unordered_map<std::string, int> v{ { "a", 1 },{ "b", 2 },{ "c", 3 } };
__anona2d380c70502() 85 lua.set_function("f", [&]() -> std::unordered_map<std::string, int>& {
86 return v;
87 });
88 lua.script("x = f()");
89 std::unordered_map<std::string, int> x = lua["x"];
90 bool areequal = x == v;
91 REQUIRE(areequal);
92 }
93
94 TEST_CASE("containers/unordered_set_roundtrip", "make sure unordered_sets can be round-tripped") {
95 sol::state lua;
96 std::unordered_set<int> v{ 1, 2, 3 };
__anona2d380c70602() 97 lua.set_function("f", [&]() -> std::unordered_set<int>& {
98 return v;
99 });
100 lua.script("x = f()");
101 std::unordered_set<int> x = lua["x"];
102 bool areequal = x == v;
103 REQUIRE(areequal);
104 }
105
106 TEST_CASE("containers/set_roundtrip", "make sure sets can be round-tripped") {
107 sol::state lua;
108 std::set<int> v{ 1, 2, 3 };
__anona2d380c70702() 109 lua.set_function("f", [&]() -> std::set<int>& {
110 return v;
111 });
112 lua.script("x = f()");
113 std::set<int> x = lua["x"];
114 bool areequal = x == v;
115 REQUIRE(areequal);
116 }
117
118 TEST_CASE("containers/custom-usertype", "make sure container usertype metatables can be overridden") {
119 typedef std::unordered_map<int, int> bark;
120
121 sol::state lua;
122 lua.open_libraries();
123 lua.new_usertype<bark>("bark",
__anona2d380c70802(const bark& b) 124 "something", [](const bark& b) {
125 INFO("It works: " << b.at(24));
126 },
127 "size", &bark::size,
128 "at", sol::resolve<const int&>(&bark::at),
129 "clear", &bark::clear
130 );
131 bark obj{ { 24, 50 } };
132 lua.set("a", &obj);
133 REQUIRE_NOTHROW(lua.script("assert(a:at(24) == 50)"));
134 REQUIRE_NOTHROW(lua.script("a:something()"));
135 lua.set("a", obj);
136 REQUIRE_NOTHROW(lua.script("assert(a:at(24) == 50)"));
137 REQUIRE_NOTHROW(lua.script("a:something()"));
138 }
139
140 TEST_CASE("containers/const-serialization-kvp", "make sure const keys / values are respected") {
141 typedef std::map<int, const int> bark;
142
143 sol::state lua;
144 lua.open_libraries();
145 bark obj{ { 24, 50 } };
146 lua.set("a", &obj);
147 REQUIRE_NOTHROW(lua.script("assert(a[24] == 50)"));
148 REQUIRE_THROWS(lua.script("a[24] = 51"));
149 REQUIRE_NOTHROW(lua.script("assert(a[24] == 50)"));
150 }
151
152 TEST_CASE("containers/basic-serialization", "make sure containers are turned into proper userdata and have basic hooks established") {
153 typedef std::vector<int> woof;
154 sol::state lua;
155 lua.open_libraries();
156 lua.set("b", woof{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 });
157 REQUIRE_NOTHROW(
158 lua.script("for k = 1, #b do assert(k == b[k]) end");
159 );
160 woof w{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 };
161 lua.set("b", w);
162 REQUIRE_NOTHROW(
163 lua.script("for k = 1, #b do assert(k == b[k]) end");
164 );
165 lua.set("b", &w);
166 REQUIRE_NOTHROW(
167 lua.script("for k = 1, #b do assert(k == b[k]) end");
168 );
169 lua.set("b", std::ref(w));
170 REQUIRE_NOTHROW(
171 lua.script("for k = 1, #b do assert(k == b[k]) end");
172 );
173 }
174
175 #if 0 // glibc is a fuccboi
176 TEST_CASE("containers/const-serialization", "make sure containers are turned into proper userdata and the basic hooks respect const-ness") {
177 typedef std::vector<const int> woof;
178 sol::state lua;
179 lua.open_libraries();
180 lua.set("b", woof{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 });
181 REQUIRE_NOTHROW(
182 lua.script("for k, v in pairs(b) do assert(k == v) end");
183 );
184 REQUIRE_THROWS(lua.script("b[1] = 20"));
185 }
186 #endif // Fuck you, glibc
187
188 TEST_CASE("containers/table-serialization", "ensure types can be serialized as tables still") {
189 typedef std::vector<int> woof;
190 sol::state lua;
191 lua.open_libraries();
192 lua.set("b", sol::as_table(woof{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 }));
193 REQUIRE_NOTHROW(
194 lua.script("for k, v in ipairs(b) do assert(k == v) end");
195 );
196 woof w{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 };
197 lua.set("b", sol::as_table(w));
198 REQUIRE_NOTHROW(
199 lua.script("for k, v in ipairs(b) do assert(k == v) end");
200 );
201 lua.set("b", sol::as_table(&w));
202 REQUIRE_NOTHROW(
203 lua.script("for k, v in ipairs(b) do assert(k == v) end");
204 );
205 lua.set("b", sol::as_table(std::ref(w)));
206 REQUIRE_NOTHROW(
207 lua.script("for k, v in ipairs(b) do assert(k == v) end");
208 );
209 }
210
211 TEST_CASE("containers/const-correctness", "usertype metatable names should reasonably ignore const attributes") {
212 struct Vec {
213 int x, y, z;
214 };
215
216 sol::state lua;
217 lua.open_libraries(sol::lib::base);
218 lua.new_usertype<Vec>("Vec", "x", &Vec::x, "y", &Vec::y, "z", &Vec::z);
219
220 Vec vec;
221 vec.x = 1;
222 vec.y = 2;
223 vec.z = -3;
224
225 std::vector<Vec> foo;
226 foo.push_back(vec);
227
228 std::vector<Vec const *> bar;
229 bar.push_back(&vec);
230
231 lua.script(R"(
232 func = function(vecs)
233 for i = 1, #vecs do
234 vec = vecs[i]
235 print(i, ":", vec.x, vec.y, vec.z)
236 end
237 end
238 )");
239
240 REQUIRE_NOTHROW({
241 lua["func"](foo);
242 lua["func"](bar);
243 });
244 }
245
246 TEST_CASE("containers/arbitrary-creation", "userdata and tables should be usable from standard containers") {
247 sol::state lua;
248 lua.open_libraries(sol::lib::base);
249 lua.set_function("test_one", test_table_return_one);
250 lua.set_function("test_two", test_table_return_two);
251 lua.set_function("test_three", test_table_return_three);
252
253 REQUIRE_NOTHROW(lua.script("a = test_one()"));
254 REQUIRE_NOTHROW(lua.script("b = test_two()"));
255 REQUIRE_NOTHROW(lua.script("c = test_three()"));
256
257 REQUIRE_NOTHROW(lua.script("assert(#a == 10, 'error')"));
258 REQUIRE_NOTHROW(lua.script("assert(a[3] == 3, 'error')"));
259 REQUIRE_NOTHROW(lua.script("assert(b.one == 1, 'error')"));
260 REQUIRE_NOTHROW(lua.script("assert(b.three == 3, 'error')"));
261 REQUIRE_NOTHROW(lua.script("assert(c.name == 'Rapptz', 'error')"));
262 REQUIRE_NOTHROW(lua.script("assert(c.project == 'sol', 'error')"));
263
264 sol::table a = lua.get<sol::table>("a");
265 sol::table b = lua.get<sol::table>("b");
266 sol::table c = lua.get<sol::table>("c");
267
268 REQUIRE(a.size() == 10ULL);
269 REQUIRE(a.get<int>(3) == 3);
270 REQUIRE(b.get<int>("one") == 1);
271 REQUIRE(b.get<int>("three") == 3);
272 REQUIRE(c.get<std::string>("name") == "Rapptz");
273 REQUIRE(c.get<std::string>("project") == "sol");
274 }
275
276 TEST_CASE("containers/extra-functions", "make sure the manipulation functions are present and usable and working across various container types") {
277 sol::state lua;
278 lua.open_libraries();
279
280 lua.script(R"(
281 function g (x)
282 x:add(20)
283 end
284
285 function h (x)
286 x:add(20, 40)
287 end
288
289 function i (x)
290 x:clear()
291 end
292 )");
293
294 // Have the function we
295 // just defined in Lua
296 sol::function g = lua["g"];
297 sol::function h = lua["h"];
298 sol::function i = lua["i"];
299
300 // Set a global variable called
301 // "arr" to be a vector of 5 lements
302 lua["arr"] = std::vector<int>{ 2, 4, 6, 8, 10 };
303 lua["map"] = std::map<int, int>{ { 1 , 2 },{ 2, 4 },{ 3, 6 },{ 4, 8 },{ 5, 10 } };
304 lua["set"] = std::set<int>{ 2, 4, 6, 8, 10 };
305 std::vector<int>& arr = lua["arr"];
306 std::map<int, int>& map = lua["map"];
307 std::set<int>& set = lua["set"];
308 REQUIRE(arr.size() == 5);
309 REQUIRE(map.size() == 5);
310 REQUIRE(set.size() == 5);
311
312 g(lua["set"]);
313 g(lua["arr"]);
314 h(lua["map"]);
315 REQUIRE(arr.size() == 6);
316 REQUIRE(map.size() == 6);
317 REQUIRE(set.size() == 6);
318
319 i(lua["arr"]);
320 i(lua["map"]);
321 i(lua["set"]);
322 REQUIRE(arr.empty());
323 REQUIRE(map.empty());
324 REQUIRE(set.empty());
325 }
326
327 TEST_CASE("containers/usertype-transparency", "Make sure containers pass their arguments through transparently and push the results as references, not new values") {
328 class A {
329 public:
330 int a;
A(int b=2)331 A(int b = 2) : a(b) {};
332
func()333 void func() { }
334 };
335
336 struct B {
337
BB338 B() {
339 for (std::size_t i = 0; i < 20; ++i) {
340 a_list.emplace_back(static_cast<int>(i));
341 }
342 }
343
344 std::vector<A> a_list;
345 };
346
347 sol::state lua;
348 lua.new_usertype<B>("B",
349 "a_list", &B::a_list
350 );
351
352 lua.script(R"(
353 b = B.new()
354 a_ref = b.a_list[2]
355 )");
356
357 B& b = lua["b"];
358 A& a_ref = lua["a_ref"];
359 REQUIRE(&b.a_list[1] == &a_ref);
360 REQUIRE(b.a_list[1].a == a_ref.a);
361 }
362
363 struct options {
364 static int livingcount;
365 static options* last;
optionsoptions366 options() {
367 ++livingcount;
368 last = this;
369 INFO("constructor: " << this);
370 }
371
output_helpoptions372 std::string output_help() {
373 last = this;
374 INFO("func: " << this);
375 return "";
376 }
377
beginoptions378 void begin() {}
endoptions379 void end() {}
380
~optionsoptions381 ~options() {
382 last = this;
383 --livingcount;
384 }
385 };
386
387 options* options::last = nullptr;
388 int options::livingcount = 0;
389
390 struct machine {
391 options opt;
392 };
393
394 namespace sol {
395 template <>
396 struct is_container<options> : std::false_type {};
397 }
398
399 TEST_CASE("containers/is-container", "make sure the is_container trait behaves properly") {
400 sol::state lua;
401 lua.open_libraries();
402
403 lua.new_usertype<options>("options_type",
404 "output_help", &options::output_help
405 );
406
407 lua.new_usertype<machine>("machine_type",
408 "new", sol::no_constructor,
__anona2d380c70902(machine& m) 409 "opt", [](machine& m) { return &m.opt; },
__anona2d380c70a02(machine& m) 410 "copy_opt", [](machine& m) { return m.opt; }
411 );
412
413 {
414 machine m;
415 lua["machine"] = &m;
416
417 lua.script(R"(
418 machine:opt():output_help()
419 )");
420
421 REQUIRE(options::last == &m.opt);
422 REQUIRE(options::livingcount == 1);
423 }
424 REQUIRE(options::livingcount == 0);
425 }
426
427 TEST_CASE("containers/readonly", "make sure readonly members are stored appropriately") {
428 sol::state lua;
429 lua.open_libraries();
430
431 struct bar {
432 int x = 24;
433 };
434
435 struct foo {
436 std::list<bar> seq;
437 };
438
439 lua.new_usertype<foo>(
440 "foo",
441 "seq", &foo::seq, // this one works
442 "readonly_seq", sol::readonly(&foo::seq) // this one does not work
443 );
444 lua["value"] = std::list<bar>{ {},{},{} };
445
446 lua.script(R"(
447 a = foo.new()
448 x = a.seq
449 a.seq = value
450 y = a.readonly_seq
451 )");
452 std::list<bar>& seqrefx = lua["x"];
453 std::list<bar>& seqrefy = lua["y"];
454 REQUIRE(&seqrefx == &seqrefy);
455 REQUIRE(seqrefx.size() == 3);
456 auto result = lua.do_string(R"(
457 a.readonly_seq = value;
458 )");
459 REQUIRE_FALSE(result.valid());
460 }
461
462 TEST_CASE("containers/to_args", "Test that the to_args abstractions works") {
463 sol::state lua;
464 lua.open_libraries();
465
466 lua.script("function f (a, b, c, d) print(a, b, c, d) return a, b, c, d end");
467
468 sol::function f = lua["f"];
469 int a, b, c, d;
470
471 std::vector<int> v2{ 3, 4 };
472 sol::tie(a, b, c, d) = f(1, 2, sol::as_args(v2));
473 REQUIRE(a == 1);
474 REQUIRE(b == 2);
475 REQUIRE(c == 3);
476 REQUIRE(d == 4);
477
478 std::set<int> v4{ 7, 6, 8, 5 };
479 sol::tie(a, b, c, d) = f(sol::as_args(v4));
480 REQUIRE(a == 5);
481 REQUIRE(b == 6);
482 REQUIRE(c == 7);
483 REQUIRE(d == 8);
484
485 int v3[] = { 10, 11, 12 };
486 sol::tie(a, b, c, d) = f(9, sol::as_args(v3));
487 REQUIRE(a == 9);
488 REQUIRE(b == 10);
489 REQUIRE(c == 11);
490 REQUIRE(d == 12);
491
492 }
493