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