Update to sol2 v2.20.6

This commit is contained in:
Desone Burns II
2020-11-18 17:01:43 -07:00
parent dfc1bfc27e
commit deacc8fb5c
409 changed files with 386663 additions and 14519 deletions
+88
View File
@@ -0,0 +1,88 @@
# # # # sol2
# The MIT License (MIT)
#
# Copyright (c) 2013-2018 Rapptz, ThePhD, and contributors
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# # # # sol2 tests
if (CMAKE_GENERATOR MATCHES "Visual Studio 14 2015")
find_package(Catch 1.12.1 REQUIRED)
else()
find_package(Catch 2.1.2 REQUIRED)
endif()
file(GLOB SOL2_TEST_SOURCES test*.cpp)
source_group(test_sources FILES ${SOL2_TEST_SOURCES})
function(CREATE_TEST test_target_name test_name is_single)
if (is_single)
set(header_files ${SOL2_SINGLE_HEADER_SOURCES})
else()
set(header_files ${SOL2_HEADER_SOURCES})
endif()
add_executable(${test_target_name} ${SOL2_TEST_SOURCES} ${header_files})
set_target_properties(${test_target_name}
PROPERTIES
OUTPUT_NAME ${test_name})
if (is_single)
target_link_libraries(${test_target_name} sol2_single)
target_compile_definitions(${test_target_name}
PRIVATE TEST_SINGLE)
else()
target_link_libraries(${test_target_name} sol2)
endif()
if (MSVC)
if (NOT CMAKE_COMPILER_ID MATCHES "Clang")
target_compile_options(${test_target_name}
PRIVATE /bigobj)
endif()
else()
target_compile_options(${test_target_name}
PRIVATE -Wno-noexcept-type -ftemplate-depth=1024 -pthread)
if (IS_X86)
if(MINGW)
set_target_properties(${test_target_name}
PROPERTIES
LINK_FLAGS -static-libstdc++)
endif()
endif()
endif()
if (CI)
target_compile_definitions(${test_target_name}
PRIVATE SOL2_CI)
endif()
if (CMAKE_DL_LIBS)
target_link_libraries(${test_target_name}
${CMAKE_DL_LIBS})
endif()
target_link_libraries(${test_target_name}
Threads::Threads ${LUA_LIBRARIES} ${CATCH_LIBRARIES})
add_test(NAME ${test_name} COMMAND ${test_target_name})
install(TARGETS ${test_target_name} RUNTIME DESTINATION bin)
endfunction(CREATE_TEST)
CREATE_TEST(tests "tests" FALSE)
if (TESTS_SINGLE AND SOL2_SINGLE_FOUND)
CREATE_TEST(tests_single "tests.single" TRUE)
endif()
+11
View File
@@ -0,0 +1,11 @@
#include <cstdlib>
struct pre_main {
pre_main() {
#ifdef SOL2_CI
#ifdef _MSC_VER
_set_abort_behavior(0, _WRITE_ABORT_MSG);
#endif
#endif
}
} pm;
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+584
View File
@@ -0,0 +1,584 @@
// sol2
// The MIT License (MIT)
// Copyright (c) 2013-2018 Rapptz, ThePhD and contributors
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "test_sol.hpp"
#include <catch.hpp>
TEST_CASE("coroutines/coroutine.yield", "ensure calling a coroutine works") {
const auto& script = R"(counter = 20
function loop()
while counter ~= 30
do
coroutine.yield(counter);
counter = counter + 1;
end
return counter
end
)";
sol::state lua;
lua.open_libraries(sol::lib::base, sol::lib::coroutine);
auto result1 = lua.safe_script(script);
REQUIRE(result1.valid());
sol::coroutine cr = lua["loop"];
int counter;
for (counter = 20; counter < 31 && cr; ++counter) {
int value = cr();
REQUIRE(counter == value);
}
counter -= 1;
REQUIRE(counter == 30);
}
TEST_CASE("coroutines/new thread coroutines", "ensure calling a coroutine works when the work is put on a different thread") {
const auto& code = R"(counter = 20
function loop()
while counter ~= 30
do
coroutine.yield(counter);
counter = counter + 1;
end
return counter
end
)";
sol::state lua;
lua.open_libraries(sol::lib::base, sol::lib::coroutine);
auto result = lua.safe_script(code, sol::script_pass_on_error);
REQUIRE(result.valid());
sol::thread runner = sol::thread::create(lua.lua_state());
sol::state_view runnerstate = runner.state();
sol::coroutine cr = runnerstate["loop"];
int counter;
for (counter = 20; counter < 31 && cr; ++counter) {
int value = cr();
REQUIRE(counter == value);
}
counter -= 1;
REQUIRE(counter == 30);
}
TEST_CASE("coroutines/transfer", "test that things created inside of a coroutine can have their state transferred using lua_xmove constructors") {
for (std::size_t tries = 0; tries < 200; ++tries) {
sol::state lua;
lua.open_libraries();
{
sol::function f2;
lua["f"] = [&lua, &f2](sol::object t) {
f2 = sol::function(lua, t);
};
{
auto code = R"(
i = 0
function INIT()
co = coroutine.create(
function()
local g = function() i = i + 1 end
f(g)
g = nil
collectgarbage()
end
)
coroutine.resume(co)
co = nil
collectgarbage()
end
)";
auto result = lua.safe_script(code, sol::script_pass_on_error);
REQUIRE(result.valid());
}
sol::function f3;
sol::function f1;
{
auto code = "INIT()";
auto result = lua.safe_script(code, sol::script_pass_on_error);
REQUIRE(result.valid());
}
f2();
auto updatecode = "return function() collectgarbage() end";
auto pfr = lua.safe_script(updatecode);
REQUIRE(pfr.valid());
sol::function update = pfr;
update();
f3 = f2;
f3();
update();
f1 = f2;
f1();
update();
int i = lua["i"];
REQUIRE(i == 3);
}
}
}
TEST_CASE("coroutines/explicit transfer", "check that the xmove constructors shift things around appropriately") {
const std::string code = R"(
-- main thread - L1
-- co - L2
-- co2 - L3
x = co_test.new("x")
local co = coroutine.wrap(
function()
local t = co_test.new("t")
local co2 = coroutine.wrap(
function()
local t2 = { "SOME_TABLE" }
t:copy_store(t2) -- t2 = [L3], t.obj = [L2]
end
)
co2()
co2 = nil
collectgarbage() -- t2 ref in t remains valid!
x:store(t:get()) -- t.obj = [L2], x.obj = [L1]
end
)
co()
collectgarbage()
collectgarbage()
co = nil
)";
struct co_test {
std::string identifier;
sol::reference obj;
co_test(sol::this_state L, std::string id)
: identifier(id), obj(L, sol::lua_nil) {
}
void store(sol::table ref) {
// must be explicit
obj = sol::reference(obj.lua_state(), ref);
}
void copy_store(sol::table ref) {
// must be explicit
obj = sol::reference(obj.lua_state(), ref);
}
sol::reference get() {
return obj;
}
~co_test() {
}
};
sol::state lua;
lua.open_libraries(sol::lib::coroutine, sol::lib::base);
lua.new_usertype<co_test>("co_test",
sol::constructors<co_test(sol::this_state, std::string)>(),
"store", &co_test::store,
"copy_store", &co_test::copy_store,
"get", &co_test::get);
auto r = lua.safe_script(code, sol::script_pass_on_error);
REQUIRE(r.valid());
co_test& ct = lua["x"];
lua_State* Lmain1 = lua.lua_state();
lua_State* Lmain2 = sol::main_thread(lua);
lua_State* Lmain3 = ct.get().lua_state();
REQUIRE(Lmain1 == Lmain2);
REQUIRE(Lmain1 == Lmain3);
sol::table t = ct.get();
REQUIRE(t.size() == 1);
std::string s = t[1];
REQUIRE(s == "SOME_TABLE");
}
TEST_CASE("coroutines/implicit transfer", "check that copy and move assignment constructors implicitly shift things around") {
const std::string code = R"(
-- main thread - L1
-- co - L2
-- co2 - L3
x = co_test.new("x")
local co = coroutine.wrap(
function()
local t = co_test.new("t")
local co2 = coroutine.wrap(
function()
local t2 = { "SOME_TABLE" }
t:copy_store(t2) -- t2 = [L3], t.obj = [L2]
end
)
co2()
co2 = nil
collectgarbage() -- t2 ref in t remains valid!
x:store(t:get()) -- t.obj = [L2], x.obj = [L1]
end
)
co()
collectgarbage()
collectgarbage()
co = nil
)";
struct co_test_implicit {
std::string identifier;
sol::reference obj;
co_test_implicit(sol::this_state L, std::string id)
: identifier(id), obj(L, sol::lua_nil) {
}
void store(sol::table ref) {
// must be explicit
obj = std::move(ref);
}
void copy_store(sol::table ref) {
// must be explicit
obj = ref;
}
sol::reference get() {
return obj;
}
~co_test_implicit() {
}
};
sol::state lua;
lua.open_libraries(sol::lib::coroutine, sol::lib::base);
lua.new_usertype<co_test_implicit>("co_test",
sol::constructors<co_test_implicit(sol::this_state, std::string)>(),
"store", &co_test_implicit::store,
"copy_store", &co_test_implicit::copy_store,
"get", &co_test_implicit::get);
auto r = lua.safe_script(code, sol::script_pass_on_error);
REQUIRE(r.valid());
co_test_implicit& ct = lua["x"];
lua_State* Lmain1 = lua.lua_state();
lua_State* Lmain2 = sol::main_thread(lua);
lua_State* Lmain3 = ct.get().lua_state();
REQUIRE(Lmain1 == Lmain2);
REQUIRE(Lmain1 == Lmain3);
sol::table t = ct.get();
REQUIRE(t.size() == 1);
std::string s = t[1];
REQUIRE(s == "SOME_TABLE");
}
TEST_CASE("coroutines/main transfer", "check that copy and move assignment constructors using main-forced types work") {
const std::string code = R"(
-- main thread - L1
-- co - L2
-- co2 - L3
x = co_test.new("x")
local co = coroutine.wrap(
function()
local t = co_test.new("t")
local co2 = coroutine.wrap(
function()
local t2 = { "SOME_TABLE" }
t:copy_store(t2) -- t2 = [L3], t.obj = [L2]
end
)
co2()
co2 = nil
collectgarbage() -- t2 ref in t remains valid!
x:store(t:get()) -- t.obj = [L2], x.obj = [L1]
end
)
co()
co = nil
collectgarbage()
)";
struct co_test_implicit {
std::string identifier;
sol::main_reference obj;
co_test_implicit(sol::this_state L, std::string id)
: identifier(id), obj(L, sol::lua_nil) {
}
void store(sol::table ref) {
// main_reference does the state shift implicitly
obj = std::move(ref);
lua_State* Lmain = sol::main_thread(ref.lua_state());
REQUIRE(obj.lua_state() == Lmain);
}
void copy_store(sol::table ref) {
// main_reference does the state shift implicitly
obj = ref;
lua_State* Lmain = sol::main_thread(ref.lua_state());
REQUIRE(obj.lua_state() == Lmain);
}
sol::reference get() {
return obj;
}
~co_test_implicit() {
}
};
sol::state lua;
lua.open_libraries(sol::lib::coroutine, sol::lib::base);
lua.new_usertype<co_test_implicit>("co_test",
sol::constructors<co_test_implicit(sol::this_state, std::string)>(),
"store", &co_test_implicit::store,
"copy_store", &co_test_implicit::copy_store,
"get", &co_test_implicit::get);
auto r = lua.safe_script(code, sol::script_pass_on_error);
REQUIRE(r.valid());
co_test_implicit& ct = lua["x"];
lua_State* Lmain1 = lua.lua_state();
lua_State* Lmain2 = sol::main_thread(lua);
lua_State* Lmain3 = ct.get().lua_state();
REQUIRE(Lmain1 == Lmain2);
REQUIRE(Lmain1 == Lmain3);
sol::table t = ct.get();
REQUIRE(t.size() == 1);
std::string s = t[1];
REQUIRE(s == "SOME_TABLE");
}
TEST_CASE("coroutines/coroutine.create protection", "ensure that a thread picked up from coroutine.create does not throw off the lua stack entirely when called from C++") {
sol::state lua;
lua.open_libraries(sol::lib::base, sol::lib::coroutine);
auto code = R"(
function loop()
local i = 0
while true do
print("pre-yield in loop")
coroutine.yield(i)
print("post-yield in loop")
i = i+1
end
end
loop_th = coroutine.create(loop)
)";
auto r = lua.safe_script(code, sol::script_pass_on_error);
REQUIRE(r.valid());
sol::thread runner_thread = lua["loop_th"];
auto test_resume = [&runner_thread]() {
sol::state_view th_state = runner_thread.state();
sol::coroutine cr = th_state["loop"];
int r = cr();
return r;
};
lua.set_function("test_resume", std::ref(test_resume));
int v0 = test_resume();
int v1 = test_resume();
int v2, v3;
{
auto r2 = lua.safe_script("return test_resume()", sol::script_pass_on_error);
REQUIRE(r2.valid());
auto r3 = lua.safe_script("return test_resume()", sol::script_pass_on_error);
REQUIRE(r3.valid());
v2 = r2;
v3 = r3;
}
REQUIRE(v0 == 0);
REQUIRE(v1 == 1);
REQUIRE(v2 == 2);
REQUIRE(v3 == 3);
}
TEST_CASE("coroutines/stack-check", "check that resumed functions consume the entire execution stack") {
sol::state lua;
lua.open_libraries(sol::lib::base, sol::lib::table, sol::lib::coroutine);
{
auto code = R"(
unpack = unpack or table.unpack
function loop()
local i = 0
while true do
print("pre-yield in loop")
coroutine.yield(i)
print("post-yield in loop")
i = i+1
end
end
loop_th = coroutine.create(loop)
loop_res = function(...)
returns = { coroutine.resume(loop_th, ...) }
return unpack(returns, 2)
end
)";
auto result = lua.safe_script(code, sol::script_pass_on_error);
REQUIRE(result.valid());
}
// Resume from lua via thread and coroutine
sol::thread runner_thread = lua["loop_th"];
sol::state_view runner_thread_state = runner_thread.state();
auto test_resume = [&runner_thread, &runner_thread_state]() {
sol::coroutine cr = runner_thread_state["loop"];
sol::stack::push(runner_thread_state, 50);
sol::stack::push(runner_thread_state, 25);
int r = cr();
return r;
};
lua.set_function("test_resume", std::ref(test_resume));
// Resume via getting a sol::function from the state
sol::function test_resume_lua = lua["loop_res"];
// Resume via passing a sol::function object
auto test_resume_func = [](sol::function f) {
int r = f();
return r;
};
lua.set_function("test_resume_func", std::ref(test_resume_func));
int v0 = test_resume();
int s0 = runner_thread_state.stack_top();
int v1 = test_resume();
int s1 = runner_thread_state.stack_top();
int v2;
{
auto result = lua.safe_script("return test_resume()", sol::script_pass_on_error);
REQUIRE(result.valid());
v2 = result;
}
int s2 = runner_thread_state.stack_top();
int v3;
{
auto result = lua.safe_script("return test_resume()", sol::script_pass_on_error);
REQUIRE(result.valid());
v3 = result;
}
int s3 = runner_thread_state.stack_top();
int v4 = test_resume_lua();
int s4 = runner_thread_state.stack_top();
int v5;
{
auto result = lua.safe_script("return test_resume_func(loop_res)", sol::script_pass_on_error);
REQUIRE(result.valid());
v5 = result;
}
int s5 = runner_thread_state.stack_top();
REQUIRE(v0 == 0);
REQUIRE(v1 == 1);
REQUIRE(v2 == 2);
REQUIRE(v3 == 3);
REQUIRE(v4 == 4);
REQUIRE(v5 == 5);
REQUIRE(s0 == 0);
REQUIRE(s1 == 0);
REQUIRE(s2 == 0);
REQUIRE(s3 == 0);
REQUIRE(s4 == 0);
REQUIRE(s5 == 0);
}
TEST_CASE("coroutines/yielding", "test that a sol2 bound function can yield when marked yieldable") {
sol::state lua;
lua.open_libraries(sol::lib::base, sol::lib::coroutine);
int i = 0;
auto func = [&i]() {
++i;
return i;
};
struct h {
int x = 500;
int func() const {
return x;
}
} hobj{};
lua["f"] = sol::yielding(func);
lua["g"] = sol::yielding([]() { return 300; });
lua["h"] = sol::yielding(&h::func);
lua["hobj"] = &hobj;
sol::string_view code = R"(
co1 = coroutine.create(function () return f() end)
success1, value1 = coroutine.resume(co1)
co2 = coroutine.create(function () return g() end)
success2, value2 = coroutine.resume(co2)
co3 = coroutine.create(function()
h(hobj)
end)
success3, value3 = coroutine.resume(co3)
)";
auto result = lua.safe_script(code);
REQUIRE(result.valid());
bool success1 = lua["success1"];
int value1 = lua["value1"];
REQUIRE(success1);
REQUIRE(value1 == 1);
bool success2 = lua["success2"];
int value2 = lua["value2"];
REQUIRE(success2);
REQUIRE(value2 == 300);
bool success3 = lua["success3"];
int value3 = lua["value3"];
REQUIRE(success3);
REQUIRE(value3 == 500);
}
+170
View File
@@ -0,0 +1,170 @@
// sol2
// The MIT License (MIT)
// Copyright (c) 2013-2018 Rapptz, ThePhD and contributors
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "test_sol.hpp"
#include <catch.hpp>
#include <unordered_map>
#include <vector>
struct two_things {
int a;
bool b;
};
struct number_shim {
double num = 0;
};
namespace sol {
// First, the expected size
// Specialization of a struct
template <>
struct lua_size<two_things> : std::integral_constant<int, 2> {};
// Then, the expected type
template <>
struct lua_type_of<two_things> : std::integral_constant<sol::type, sol::type::poly> {};
// do note specialize size for this because it is our type
template <>
struct lua_type_of<number_shim> : std::integral_constant<sol::type, sol::type::poly> {};
// Now, specialize various stack structures
namespace stack {
template <>
struct checker<two_things> {
template <typename Handler>
static bool check(lua_State* L, int index, Handler&& handler, record& tracking) {
// Check first and second second index for being the proper types
bool success = stack::check<int>(L, index, handler)
&& stack::check<bool>(L, index + 1, handler);
tracking.use(2);
return success;
}
};
template <>
struct getter<two_things> {
static two_things get(lua_State* L, int index, record& tracking) {
// Get the first element
int a = stack::get<int>(L, index);
// Get the second element,
// in the +1 position from the first
bool b = stack::get<bool>(L, index + 1);
// we use 2 slots, each of the previous takes 1
tracking.use(2);
return two_things{ a, b };
}
};
template <>
struct pusher<two_things> {
static int push(lua_State* L, const two_things& things) {
int amount = stack::push(L, things.a);
amount += stack::push(L, things.b);
// Return 2 things
return amount;
}
};
template <>
struct checker<number_shim> {
template <typename Handler>
static bool check(lua_State* L, int index, Handler&& handler, record& tracking) {
// check_usertype is a backdoor for directly checking sol2 usertypes
if (!check_usertype<number_shim>(L, index) && !stack::check<double>(L, index)) {
handler(L, index, type_of(L, index), type::userdata, "expected a number_shim or a number");
return false;
}
tracking.use(1);
return true;
}
};
template <>
struct getter<number_shim> {
static number_shim get(lua_State* L, int index, record& tracking) {
if (check_usertype<number_shim>(L, index)) {
number_shim& ns = get_usertype<number_shim>(L, index, tracking);
return ns;
}
number_shim ns{};
ns.num = stack::get<double>(L, index, tracking);
return ns;
}
};
} // namespace stack
} // namespace sol
TEST_CASE("customization/split struct", "using the newly documented customization points to handle different kinds of classes") {
sol::state lua;
// Create a pass-through style of function
auto result1 = lua.safe_script("function f ( a, b, c ) return a + c, b end");
REQUIRE(result1.valid());
lua.set_function("g", [](int a, bool b, int c, double d) {
return std::make_tuple(a + c, b, d + 2.5);
});
// get the function out of Lua
sol::function f = lua["f"];
sol::function g = lua["g"];
two_things thingsf = f(two_things{ 24, true }, 1);
two_things thingsg;
double d;
sol::tie(thingsg, d) = g(two_things{ 25, false }, 2, 34.0);
REQUIRE(thingsf.a == 25);
REQUIRE(thingsf.b);
REQUIRE(thingsg.a == 27);
REQUIRE_FALSE(thingsg.b);
REQUIRE(d == 36.5);
}
TEST_CASE("customization/get_ check_usertype", "using the newly documented customization points to handle different kinds of classes") {
sol::state lua;
// Create a pass-through style of function
auto result1 = lua.safe_script("function f ( a ) return a end");
REQUIRE(result1.valid());
lua.set_function("g", [](double a) {
number_shim ns;
ns.num = a;
return ns;
});
auto result2 = lua.safe_script("vf = f(25) vg = g(35)", sol::script_pass_on_error);
REQUIRE(result2.valid());
number_shim thingsf = lua["vf"];
number_shim thingsg = lua["vg"];
REQUIRE(thingsf.num == 25);
REQUIRE(thingsg.num == 35);
}
+268
View File
@@ -0,0 +1,268 @@
// sol2
// The MIT License (MIT)
// Copyright (c) 2013-2018 Rapptz, ThePhD and contributors
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "test_sol.hpp"
#include <catch.hpp>
#include <iostream>
TEST_CASE("environments/get", "Envronments can be taken out of things like Lua functions properly") {
sol::state lua;
sol::stack_guard luasg(lua);
lua.open_libraries(sol::lib::base);
auto result1 = lua.safe_script("f = function() return test end", sol::script_pass_on_error);
REQUIRE(result1.valid());
sol::function f = lua["f"];
sol::environment env_f(lua, sol::create);
env_f["test"] = 31;
sol::set_environment(env_f, f);
int result = f();
REQUIRE(result == 31);
auto result2 = lua.safe_script("g = function() test = 5 end", sol::script_pass_on_error);
REQUIRE(result2.valid());
sol::function g = lua["g"];
sol::environment env_g(lua, sol::create);
env_g.set_on(g);
g();
int test = env_g["test"];
REQUIRE(test == 5);
sol::object global_test = lua["test"];
REQUIRE(!global_test.valid());
auto result3 = lua.safe_script("h = function() end", sol::script_pass_on_error);
REQUIRE(result3.valid());
lua.set_function("check_f_env",
[&lua, &env_f](sol::object target) {
sol::stack_guard sg(lua);
sol::environment target_env(sol::env_key, target);
int test_env_f = env_f["test"];
int test_target_env = target_env["test"];
REQUIRE(test_env_f == test_target_env);
REQUIRE(test_env_f == 31);
REQUIRE(env_f == target_env);
});
lua.set_function("check_g_env",
[&lua, &env_g](sol::function target) {
sol::stack_guard sg(lua);
sol::environment target_env = sol::get_environment(target);
int test_env_g = env_g["test"];
int test_target_env = target_env["test"];
REQUIRE(test_env_g == test_target_env);
REQUIRE(test_env_g == 5);
REQUIRE(env_g == target_env);
});
lua.set_function("check_h_env",
[&lua](sol::function target) {
sol::stack_guard sg(lua);
sol::environment target_env = sol::get_environment(target);
});
auto checkf = lua.safe_script("check_f_env(f)");
REQUIRE(checkf.valid());
auto checkg = lua.safe_script("check_g_env(g)");
REQUIRE(checkg.valid());
auto checkh = lua.safe_script("check_h_env(h)");
REQUIRE(checkh.valid());
}
TEST_CASE("environments/shadowing", "Environments can properly shadow and fallback on variables") {
sol::state lua;
lua["b"] = 2142;
SECTION("no fallback") {
sol::environment plain_env(lua, sol::create);
auto result1 = lua.safe_script("a = 24", plain_env, sol::script_pass_on_error);
REQUIRE(result1.valid());
sol::optional<int> maybe_env_a = plain_env["a"];
sol::optional<int> maybe_global_a = lua["a"];
sol::optional<int> maybe_env_b = plain_env["b"];
sol::optional<int> maybe_global_b = lua["b"];
REQUIRE(maybe_env_a != sol::nullopt);
REQUIRE(maybe_env_a.value() == 24);
REQUIRE(maybe_env_b == sol::nullopt);
REQUIRE(maybe_global_a == sol::nullopt);
REQUIRE(maybe_global_b != sol::nullopt);
REQUIRE(maybe_global_b.value() == 2142);
}
SECTION("fallback") {
sol::environment env_with_fallback(lua, sol::create, lua.globals());
auto result1 = lua.safe_script("a = 56", env_with_fallback, sol::script_pass_on_error);
REQUIRE(result1.valid());
sol::optional<int> maybe_env_a = env_with_fallback["a"];
sol::optional<int> maybe_global_a = lua["a"];
sol::optional<int> maybe_env_b = env_with_fallback["b"];
sol::optional<int> maybe_global_b = lua["b"];
REQUIRE(maybe_env_a != sol::nullopt);
REQUIRE(maybe_env_a.value() == 56);
REQUIRE(maybe_env_b != sol::nullopt);
REQUIRE(maybe_env_b.value() == 2142);
REQUIRE(maybe_global_a == sol::nullopt);
REQUIRE(maybe_global_b != sol::nullopt);
REQUIRE(maybe_global_b.value() == 2142);
}
SECTION("from name") {
sol::environment env_with_fallback(lua, sol::create, lua.globals());
lua["env"] = env_with_fallback;
sol::environment env = lua["env"];
auto result1 = lua.safe_script("a = 56", env, sol::script_pass_on_error);
REQUIRE(result1.valid());
sol::optional<int> maybe_env_a = env["a"];
sol::optional<int> maybe_global_a = lua["a"];
sol::optional<int> maybe_env_b = env["b"];
sol::optional<int> maybe_global_b = lua["b"];
REQUIRE(maybe_env_a != sol::nullopt);
REQUIRE(maybe_env_a.value() == 56);
REQUIRE(maybe_env_b != sol::nullopt);
REQUIRE(maybe_env_b.value() == 2142);
REQUIRE(maybe_global_a == sol::nullopt);
REQUIRE(maybe_global_b != sol::nullopt);
REQUIRE(maybe_global_b.value() == 2142);
}
SECTION("name with newtable") {
lua["blank_env"] = sol::new_table(0, 1);
sol::environment plain_env = lua["blank_env"];
auto result1 = lua.safe_script("a = 24", plain_env, sol::script_pass_on_error);
REQUIRE(result1.valid());
sol::optional<int> maybe_env_a = plain_env["a"];
sol::optional<int> maybe_global_a = lua["a"];
sol::optional<int> maybe_env_b = plain_env["b"];
sol::optional<int> maybe_global_b = lua["b"];
REQUIRE(maybe_env_a != sol::nullopt);
REQUIRE(maybe_env_a.value() == 24);
REQUIRE(maybe_env_b == sol::nullopt);
REQUIRE(maybe_global_a == sol::nullopt);
REQUIRE(maybe_global_b != sol::nullopt);
REQUIRE(maybe_global_b.value() == 2142);
}
}
TEST_CASE("environments/functions", "see if environments on functions are working properly") {
SECTION("basic") {
sol::state lua;
auto result1 = lua.safe_script("a = function() return 5 end", sol::script_pass_on_error);
REQUIRE(result1.valid());
sol::function a = lua["a"];
int result0 = a();
REQUIRE(result0 == 5);
sol::environment env(lua, sol::create);
sol::set_environment(env, a);
int value = a();
REQUIRE(value == 5);
}
SECTION("return environment value") {
sol::state lua;
auto result1 = lua.safe_script("a = function() return test end", sol::script_pass_on_error);
REQUIRE(result1.valid());
sol::function a = lua["a"];
sol::environment env(lua, sol::create);
env["test"] = 5;
env.set_on(a);
// the function returns the value from the environment table
int result = a();
REQUIRE(result == 5);
}
SECTION("set environment value") {
sol::state lua;
auto result1 = lua.safe_script("a = function() test = 5 end", sol::script_pass_on_error);
REQUIRE(result1.valid());
sol::function a = lua["a"];
sol::environment env(lua, sol::create);
sol::set_environment(env, a);
a();
// the value can be retrieved from the env table
int result = env["test"];
REQUIRE(result == 5);
// the global environment is not polluted
auto gtest = lua["test"];
REQUIRE(!gtest.valid());
}
}
TEST_CASE("environments/this_environment", "test various situations of pulling out an environment") {
static std::string code = "return (f(10))";
sol::state lua;
lua["f"] = [](sol::this_environment te, int x, sol::this_state ts) {
if (te) {
sol::environment& env = te;
return x + static_cast<int>(env["x"]);
}
sol::state_view lua = ts;
return x + static_cast<int>(lua["x"]);
};
sol::environment e(lua, sol::create, lua.globals());
lua["x"] = 5;
e["x"] = 20;
SECTION("from Lua script") {
auto result1 = lua.safe_script(code, e, sol::script_pass_on_error);
REQUIRE(result1.valid());
int value = result1;
REQUIRE(value == 30);
}
SECTION("from C++") {
sol::function f = lua["f"];
e.set_on(f);
int value = f(10);
REQUIRE(value == 30);
}
SECTION("from C++, with no env") {
sol::function f = lua["f"];
int value = f(10);
REQUIRE(value == 15);
}
}
+265
View File
@@ -0,0 +1,265 @@
// sol2
// The MIT License (MIT)
// Copyright (c) 2013-2018 Rapptz, ThePhD and contributors
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "test_sol.hpp"
#include <catch.hpp>
#include <iostream>
#include <string>
TEST_CASE("filters/self", "ensure we return a direct reference to the lua userdata rather than creating a new one") {
struct vec2 {
float x = 20.f;
float y = 20.f;
vec2& normalize() {
float len2 = x * x + y * y;
if (len2 != 0) {
float len = sqrtf(len2);
x /= len;
y /= len;
}
return *this;
}
~vec2() {
x = std::numeric_limits<float>::lowest();
y = std::numeric_limits<float>::lowest();
}
};
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_usertype<vec2>("vec2",
"x", &vec2::x,
"y", &vec2::y,
"normalize", sol::filters(&vec2::normalize, sol::returns_self()));
auto result1 = lua.safe_script(R"(
v1 = vec2.new()
print('v1:', v1.x, v1.y)
v2 = v1:normalize()
print('v1:', v1.x, v1.y)
print('v2:', v2.x, v2.y)
print(v1, v2)
assert(rawequal(v1, v2))
v1 = nil
collectgarbage()
print(v2) -- v2 points to same, is not destroyed
)", sol::script_pass_on_error);
REQUIRE(result1.valid());
}
TEST_CASE("filters/self_dependency", "ensure we can keep a userdata instance alive by attaching it to the lifetime of another userdata") {
struct dep;
struct gc_test;
static std::vector<dep*> deps_destroyed;
static std::vector<gc_test*> gc_tests_destroyed;
struct dep {
int value = 20;
~dep() {
std::cout << "\t"
<< "[C++] ~dep" << std::endl;
value = std::numeric_limits<int>::max();
deps_destroyed.push_back(this);
}
};
struct gc_test {
dep d;
~gc_test() {
std::cout << "\t"
<< "[C++] ~gc_test" << std::endl;
gc_tests_destroyed.push_back(this);
}
};
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_usertype<dep>("dep",
"value", &dep::value,
sol::meta_function::to_string, [](dep& d) {
return "{ " + std::to_string(d.value) + " }";
});
lua.new_usertype<gc_test>("gc_test",
"d", sol::filters(&gc_test::d, sol::self_dependency()),
sol::meta_function::to_string, [](gc_test& g) {
return "{ d: { " + std::to_string(g.d.value) + " } }";
});
auto result1 = lua.safe_script(R"(
g = gc_test.new()
d = g.d
print("new gc_test, d = g.d")
print("", g)
)", sol::script_pass_on_error);
REQUIRE(result1.valid());
REQUIRE(deps_destroyed.empty());
REQUIRE(gc_tests_destroyed.empty());
gc_test* g = lua["g"];
dep* d = lua["d"];
auto result2 = lua.safe_script(R"(
print("g = nil, collectgarbage")
g = nil
collectgarbage()
print("", d)
)", sol::script_pass_on_error);
REQUIRE(result2.valid());
REQUIRE(deps_destroyed.empty());
REQUIRE(gc_tests_destroyed.empty());
auto result3 = lua.safe_script(R"(
print("d = nil, collectgarbage")
d = nil
collectgarbage()
)", sol::script_pass_on_error);
REQUIRE(result3.valid());
REQUIRE(deps_destroyed.size() == 1);
REQUIRE(gc_tests_destroyed.size() == 1);
REQUIRE(deps_destroyed[0] == d);
REQUIRE(gc_tests_destroyed[0] == g);
}
TEST_CASE("filters/stack_dependencies", "ensure we can take dependencies even to arguments pushed on the stack") {
struct holder;
struct depends_on_reference;
struct composition_related;
static std::vector<composition_related*> composition_relateds_destroyed;
static std::vector<holder*> holders_destroyed;
static std::vector<depends_on_reference*> depends_on_references_destroyed;
struct composition_related {
std::string text = "bark";
~composition_related() {
std::cout << "[C++] ~composition_related" << std::endl;
text = "";
composition_relateds_destroyed.push_back(this);
}
};
struct holder {
int value = 20;
~holder() {
std::cout << "[C++] ~holder" << std::endl;
value = std::numeric_limits<int>::max();
holders_destroyed.push_back(this);
}
};
struct depends_on_reference {
std::reference_wrapper<holder> href;
composition_related comp;
depends_on_reference(holder& h)
: href(h) {
}
~depends_on_reference() {
std::cout << "[C++] ~depends_on_reference" << std::endl;
depends_on_references_destroyed.push_back(this);
}
};
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_usertype<holder>("holder",
"value", &holder::value);
lua.new_usertype<depends_on_reference>("depends_on_reference",
"new", sol::filters(sol::constructors<depends_on_reference(holder&)>(), sol::stack_dependencies(-1, 1)),
"comp", &depends_on_reference::comp);
auto result1 = lua.safe_script(R"(
h = holder.new()
dor = depends_on_reference.new(h)
c = dor.comp
)", sol::script_pass_on_error);
REQUIRE(result1.valid());
REQUIRE(composition_relateds_destroyed.empty());
REQUIRE(holders_destroyed.empty());
REQUIRE(depends_on_references_destroyed.empty());
holder* h = lua["h"];
composition_related* c = lua["c"];
depends_on_reference* dor = lua["dor"];
REQUIRE(h == &dor->href.get());
REQUIRE(c == &dor->comp);
auto result2 = lua.safe_script(R"(
h = nil
collectgarbage()
)");
REQUIRE(result2.valid());
REQUIRE(composition_relateds_destroyed.empty());
REQUIRE(holders_destroyed.empty());
REQUIRE(depends_on_references_destroyed.empty());
auto result3 = lua.safe_script(R"(
c = nil
collectgarbage()
)", sol::script_pass_on_error);
REQUIRE(result3.valid());
REQUIRE(composition_relateds_destroyed.empty());
REQUIRE(holders_destroyed.empty());
REQUIRE(depends_on_references_destroyed.empty());
auto result4 = lua.safe_script(R"(
dor = nil
collectgarbage()
)", sol::script_pass_on_error);
REQUIRE(result4.valid());
REQUIRE(composition_relateds_destroyed.size() == 1);
REQUIRE(holders_destroyed.size() == 1);
REQUIRE(depends_on_references_destroyed.size() == 1);
REQUIRE(composition_relateds_destroyed[0] == c);
REQUIRE(holders_destroyed[0] == h);
REQUIRE(depends_on_references_destroyed[0] == dor);
}
int always_return_24(lua_State* L, int) {
return sol::stack::push(L, 24);
}
TEST_CASE("filters/custom", "ensure we can return dependencies on multiple things in the stack") {
sol::state lua;
lua.set_function("f", sol::filters([]() { return std::string("hi there"); }, always_return_24));
int value = lua["f"]();
REQUIRE(value == 24);
}
File diff suppressed because it is too large Load Diff
+709
View File
@@ -0,0 +1,709 @@
// sol2
// The MIT License (MIT)
// Copyright (c) 2013-2018 Rapptz, ThePhD and contributors
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "test_sol.hpp"
#include <catch.hpp>
#include <iostream>
#include <string>
#include <list>
#include <vector>
#include <memory>
#include <set>
TEST_CASE("gc/destructors", "test if destructors are fired properly through gc of unbound usertypes") {
struct test;
static std::vector<test*> tests_destroyed;
struct test {
int v = 10;
~test() {
tests_destroyed.push_back(this);
}
};
test t;
test* pt = nullptr;
{
sol::state lua;
lua["t"] = test{};
pt = lua["t"];
}
REQUIRE(tests_destroyed.size() == 2);
REQUIRE(tests_destroyed.back() == pt);
{
sol::state lua;
lua["t"] = &t;
pt = lua["t"];
}
REQUIRE(tests_destroyed.size() == 2);
REQUIRE(&t == pt);
{
sol::state lua;
lua["t"] = std::ref(t);
pt = lua["t"];
}
REQUIRE(tests_destroyed.size() == 2);
REQUIRE(&t == pt);
{
sol::state lua;
lua["t"] = t;
pt = lua["t"];
}
REQUIRE(tests_destroyed.size() == 3);
REQUIRE(&t != pt);
REQUIRE(nullptr != pt);
}
TEST_CASE("gc/virtual destructors", "ensure types with virtual destructions behave just fine") {
class B;
class A;
static std::vector<B*> bs;
static std::vector<A*> as;
class A {
public:
virtual ~A() {
as.push_back(this);
std::cout << "~A" << std::endl;
}
};
class B : public A {
public:
virtual ~B() {
bs.push_back(this);
std::cout << "~B" << std::endl;
}
};
{
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_usertype<A>("A");
lua.new_usertype<B>("B", sol::base_classes, sol::bases<A>());
B b1;
lua["b1"] = b1; // breaks here
}
REQUIRE(as.size() == 2);
REQUIRE(bs.size() == 2);
}
TEST_CASE("gc/function argument storage", "ensure functions take references on their types, not ownership, when specified") {
class gc_entity;
static std::vector<gc_entity*> entities;
class gc_entity {
public:
~gc_entity() {
entities.push_back(this);
}
};
SECTION("plain") {
entities.clear();
sol::state lua;
lua.open_libraries();
sol::function f = lua.safe_script(R"(
return function(e)
end
)");
gc_entity* target = nullptr;
{
gc_entity e;
target = &e;
{
f(e);
lua.collect_garbage();
}
{
f(&e);
lua.collect_garbage();
}
{
f(std::ref(e));
lua.collect_garbage();
}
}
REQUIRE(entities.size() == 1);
REQUIRE(entities.back() == target);
}
SECTION("regular") {
entities.clear();
sol::state lua;
lua.open_libraries();
lua.new_usertype<gc_entity>("entity");
sol::function f = lua.safe_script(R"(
return function(e)
end
)");
gc_entity* target = nullptr;
{
gc_entity e;
target = &e;
{
f(e); // same with std::ref(e)!
lua.collect_garbage(); // destroys e for some reason
}
{
f(&e); // same with std::ref(e)!
lua.collect_garbage(); // destroys e for some reason
}
{
f(std::ref(e)); // same with std::ref(e)!
lua.collect_garbage(); // destroys e for some reason
}
}
REQUIRE(entities.size() == 1);
REQUIRE(entities.back() == target);
}
SECTION("simple") {
entities.clear();
sol::state lua;
lua.open_libraries();
lua.new_simple_usertype<gc_entity>("entity");
sol::function f = lua.safe_script(R"(
return function(e)
end
)");
gc_entity* target = nullptr;
{
gc_entity e;
target = &e;
{
f(e); // same with std::ref(e)!
lua.collect_garbage(); // destroys e for some reason
}
{
f(&e); // same with std::ref(e)!
lua.collect_garbage(); // destroys e for some reason
}
{
f(std::ref(e)); // same with std::ref(e)!
lua.collect_garbage(); // destroys e for some reason
}
}
REQUIRE(entities.size() == 1);
REQUIRE(entities.back() == target);
}
}
TEST_CASE("gc/function storage", "show that proper copies / destruction happens for function storage (or not)") {
static int created = 0;
static int destroyed = 0;
static void* last_call = nullptr;
static void* static_call = reinterpret_cast<void*>(0x01);
typedef void (*fptr)();
struct x {
x() {
++created;
}
x(const x&) {
++created;
}
x(x&&) {
++created;
}
x& operator=(const x&) {
return *this;
}
x& operator=(x&&) {
return *this;
}
void func() {
last_call = static_cast<void*>(this);
};
~x() {
++destroyed;
}
};
struct y {
y() {
++created;
}
y(const x&) {
++created;
}
y(x&&) {
++created;
}
y& operator=(const x&) {
return *this;
}
y& operator=(x&&) {
return *this;
}
static void func() {
last_call = static_call;
};
void operator()() {
func();
}
operator fptr() {
return func;
}
~y() {
++destroyed;
}
};
// stateful functors/member functions should always copy unless specified
{
created = 0;
destroyed = 0;
last_call = nullptr;
{
sol::state lua;
x x1;
lua.set_function("x1copy", &x::func, x1);
auto result1 = lua.safe_script("x1copy()", sol::script_pass_on_error);
REQUIRE(result1.valid());
REQUIRE(created == 2);
REQUIRE(destroyed == 0);
REQUIRE_FALSE(last_call == &x1);
lua.set_function("x1ref", &x::func, std::ref(x1));
auto result2 = lua.safe_script("x1ref()", sol::script_pass_on_error);
REQUIRE(result2.valid());
REQUIRE(created == 2);
REQUIRE(destroyed == 0);
REQUIRE(last_call == &x1);
}
REQUIRE(created == 2);
REQUIRE(destroyed == 2);
}
// things convertible to a static function should _never_ be forced to make copies
// therefore, pass through untouched
{
created = 0;
destroyed = 0;
last_call = nullptr;
{
sol::state lua;
y y1;
lua.set_function("y1copy", y1);
auto result1 = lua.safe_script("y1copy()", sol::script_pass_on_error);
REQUIRE(result1.valid());
REQUIRE(created == 1);
REQUIRE(destroyed == 0);
REQUIRE(last_call == static_call);
last_call = nullptr;
lua.set_function("y1ref", std::ref(y1));
auto result2 = lua.safe_script("y1ref()", sol::script_pass_on_error);
REQUIRE(result2.valid());
REQUIRE(created == 1);
REQUIRE(destroyed == 0);
REQUIRE(last_call == static_call);
}
REQUIRE(created == 1);
REQUIRE(destroyed == 1);
}
}
TEST_CASE("gc/same type closures", "make sure destructions are per-object, not per-type, by destroying one type multiple times") {
static std::set<void*> last_my_closures;
static bool checking_closures = false;
static bool check_failed = false;
struct my_closure {
int& n;
my_closure(int& n)
: n(n) {
}
~my_closure() noexcept(false) {
if (!checking_closures)
return;
void* addr = static_cast<void*>(this);
auto f = last_my_closures.find(addr);
if (f != last_my_closures.cend()) {
check_failed = true;
}
last_my_closures.insert(f, addr);
}
int operator()() {
++n;
return n;
}
};
int n = 250;
my_closure a(n);
my_closure b(n);
{
sol::state lua;
lua.set_function("f", a);
lua.set_function("g", b);
checking_closures = true;
}
REQUIRE_FALSE(check_failed);
REQUIRE(last_my_closures.size() == 2);
}
TEST_CASE("gc/usertypes", "show that proper copies / destruction happens for usertypes") {
static int created = 0;
static int destroyed = 0;
struct x {
x() {
++created;
}
x(const x&) {
++created;
}
x(x&&) {
++created;
}
x& operator=(const x&) {
return *this;
}
x& operator=(x&&) {
return *this;
}
~x() {
++destroyed;
}
};
SECTION("plain") {
created = 0;
destroyed = 0;
{
sol::state lua;
x x1;
x x2;
lua.set("x1copy", x1, "x2copy", x2, "x1ref", std::ref(x1));
x& x1copyref = lua["x1copy"];
x& x2copyref = lua["x2copy"];
x& x1ref = lua["x1ref"];
REQUIRE(created == 4);
REQUIRE(destroyed == 0);
REQUIRE(std::addressof(x1) == std::addressof(x1ref));
REQUIRE(std::addressof(x1copyref) != std::addressof(x1));
REQUIRE(std::addressof(x2copyref) != std::addressof(x2));
}
REQUIRE(created == 4);
REQUIRE(destroyed == 4);
}
SECTION("regular") {
created = 0;
destroyed = 0;
{
sol::state lua;
lua.new_usertype<x>("x");
x x1;
x x2;
lua.set("x1copy", x1, "x2copy", x2, "x1ref", std::ref(x1));
x& x1copyref = lua["x1copy"];
x& x2copyref = lua["x2copy"];
x& x1ref = lua["x1ref"];
REQUIRE(created == 4);
REQUIRE(destroyed == 0);
REQUIRE(std::addressof(x1) == std::addressof(x1ref));
REQUIRE(std::addressof(x1copyref) != std::addressof(x1));
REQUIRE(std::addressof(x2copyref) != std::addressof(x2));
}
REQUIRE(created == 4);
REQUIRE(destroyed == 4);
}
SECTION("simple") {
created = 0;
destroyed = 0;
{
sol::state lua;
lua.new_simple_usertype<x>("x");
x x1;
x x2;
lua.set("x1copy", x1, "x2copy", x2, "x1ref", std::ref(x1));
x& x1copyref = lua["x1copy"];
x& x2copyref = lua["x2copy"];
x& x1ref = lua["x1ref"];
REQUIRE(created == 4);
REQUIRE(destroyed == 0);
REQUIRE(std::addressof(x1) == std::addressof(x1ref));
REQUIRE(std::addressof(x1copyref) != std::addressof(x1));
REQUIRE(std::addressof(x2copyref) != std::addressof(x2));
}
REQUIRE(created == 4);
REQUIRE(destroyed == 4);
}
}
TEST_CASE("gc/double-deletion tests", "make sure usertypes are properly destructed and don't double-delete memory or segfault") {
class crash_class {
public:
crash_class() {
}
~crash_class() {
a = 10;
}
private:
int a;
};
sol::state lua;
SECTION("regular") {
lua.new_usertype<crash_class>("CrashClass",
sol::call_constructor, sol::constructors<sol::types<>>());
auto result1 = lua.safe_script(R"(
function testCrash()
local x = CrashClass()
end
)", sol::script_pass_on_error);
REQUIRE(result1.valid());
for (int i = 0; i < 1000; ++i) {
lua["testCrash"]();
}
}
SECTION("simple") {
lua.new_simple_usertype<crash_class>("CrashClass",
sol::call_constructor, sol::constructors<sol::types<>>());
auto result1 = lua.safe_script(R"(
function testCrash()
local x = CrashClass()
end
)", sol::script_pass_on_error);
REQUIRE(result1.valid());
for (int i = 0; i < 1000; ++i) {
lua["testCrash"]();
}
}
}
TEST_CASE("gc/shared_ptr regression", "metatables should not screw over unique usertype metatables") {
static int created = 0;
static int destroyed = 0;
struct test {
test() {
++created;
}
~test() {
++destroyed;
}
};
SECTION("regular") {
created = 0;
destroyed = 0;
{
std::list<std::shared_ptr<test>> tests;
sol::state lua;
lua.open_libraries();
lua.new_usertype<test>("test",
"create", [&]() -> std::shared_ptr<test> {
tests.push_back(std::make_shared<test>());
return tests.back();
});
REQUIRE(created == 0);
REQUIRE(destroyed == 0);
auto result1 = lua.safe_script("x = test.create()", sol::script_pass_on_error);
REQUIRE(result1.valid());
REQUIRE(created == 1);
REQUIRE(destroyed == 0);
REQUIRE_FALSE(tests.empty());
std::shared_ptr<test>& x = lua["x"];
std::size_t xuse = x.use_count();
std::size_t tuse = tests.back().use_count();
REQUIRE(xuse == tuse);
}
REQUIRE(created == 1);
REQUIRE(destroyed == 1);
}
SECTION("simple") {
created = 0;
destroyed = 0;
{
std::list<std::shared_ptr<test>> tests;
sol::state lua;
lua.open_libraries();
lua.new_simple_usertype<test>("test",
"create", [&]() -> std::shared_ptr<test> {
tests.push_back(std::make_shared<test>());
return tests.back();
});
REQUIRE(created == 0);
REQUIRE(destroyed == 0);
auto result1 = lua.safe_script("x = test.create()", sol::script_pass_on_error);
REQUIRE(result1.valid());
REQUIRE(created == 1);
REQUIRE(destroyed == 0);
REQUIRE_FALSE(tests.empty());
std::shared_ptr<test>& x = lua["x"];
std::size_t xuse = x.use_count();
std::size_t tuse = tests.back().use_count();
REQUIRE(xuse == tuse);
}
REQUIRE(created == 1);
REQUIRE(destroyed == 1);
}
}
TEST_CASE("gc/double deleter guards", "usertype metatables internally must not rely on C++ state") {
SECTION("regular") {
struct c_a {
int xv;
};
struct c_b {
int yv;
};
auto routine = []() {
sol::state lua;
lua.new_usertype<c_a>("c_a", "x", &c_a::xv);
lua.new_usertype<c_b>("c_b", "y", &c_b::yv);
lua = sol::state();
lua.new_usertype<c_a>("c_a", "x", &c_a::xv);
lua.new_usertype<c_b>("c_b", "y", &c_b::yv);
lua = sol::state();
};
REQUIRE_NOTHROW(routine());
}
SECTION("simple") {
struct sc_a {
int xv;
};
struct sc_b {
int yv;
};
auto routine = []() {
sol::state lua;
lua.new_simple_usertype<sc_a>("c_a", "x", &sc_a::xv);
lua.new_simple_usertype<sc_b>("c_b", "y", &sc_b::yv);
lua = sol::state();
lua.new_simple_usertype<sc_a>("c_a", "x", &sc_a::xv);
lua.new_simple_usertype<sc_b>("c_b", "y", &sc_b::yv);
lua = sol::state();
};
REQUIRE_NOTHROW(routine());
}
}
TEST_CASE("gc/alignment", "test that allocation is always on aligned boundaries, no matter the wrapper / type") {
struct test {
std::function<void()> callback = []() { std::cout << "Hello world!" << std::endl; };
void check_alignment() {
std::uintptr_t p = reinterpret_cast<std::uintptr_t>(this);
std::uintptr_t offset = p % std::alignment_of<test>::value;
REQUIRE(offset == 0);
}
};
sol::state lua;
lua.new_usertype<test>("test",
"callback", &test::callback);
test obj{};
lua["obj"] = &obj;
INFO("obj");
{
auto r = lua.safe_script("obj.callback()", sol::script_pass_on_error);
REQUIRE(r.valid());
}
{
// Do not check for stack-created object
//test& lobj = lua["obj"];
//lobj.check_alignment();
}
lua["obj0"] = std::ref(obj);
INFO("obj0");
{
auto r = lua.safe_script("obj0.callback()", sol::script_pass_on_error);
REQUIRE(r.valid());
}
{
// Do not check for stack-created object
//test& lobj = lua["obj0"];
//lobj.check_alignment();
}
lua["obj1"] = obj;
INFO("obj1");
{
auto r = lua.safe_script("obj1.callback()", sol::script_pass_on_error);
REQUIRE(r.valid());
}
{
test& lobj = lua["obj1"];
lobj.check_alignment();
}
lua["obj2"] = test{};
INFO("obj2");
{
auto r = lua.safe_script("obj2.callback()", sol::script_pass_on_error);
REQUIRE(r.valid());
}
{
test& lobj = lua["obj2"];
lobj.check_alignment();
}
lua["obj3"] = std::make_unique<test>();
INFO("obj3");
{
auto r = lua.safe_script("obj3.callback()", sol::script_pass_on_error);
REQUIRE(r.valid());
}
{
test& lobj = lua["obj3"];
lobj.check_alignment();
}
lua["obj4"] = std::make_shared<test>();
INFO("obj4");
{
auto r = lua.safe_script("obj4.callback()", sol::script_pass_on_error);
REQUIRE(r.valid());
}
{
test& lobj = lua["obj4"];
lobj.check_alignment();
}
}
+287
View File
@@ -0,0 +1,287 @@
// sol2
// The MIT License (MIT)
// Copyright (c) 2013-2018 Rapptz, ThePhD and contributors
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "test_sol.hpp"
#include <catch.hpp>
#include <iostream>
TEST_CASE("inheritance/basic", "test that metatables are properly inherited") {
struct A {
int a = 5;
};
struct B {
int b() {
return 10;
}
};
struct C : B, A {
double c = 2.4;
};
struct D : C {
bool d() const {
return true;
}
};
sol::state lua;
lua.new_usertype<A>("A",
"a", &A::a);
lua.new_usertype<B>("B",
"b", &B::b);
lua.new_usertype<C>("C",
"c", &C::c,
sol::base_classes, sol::bases<B, A>());
lua.new_usertype<D>("D",
"d", &D::d,
sol::base_classes, sol::bases<C, B, A>());
auto result1 = lua.safe_script("obj = D.new()", sol::script_pass_on_error);
REQUIRE(result1.valid());
auto result2 = lua.safe_script("d = obj:d()", sol::script_pass_on_error);
REQUIRE(result2.valid());
bool d = lua["d"];
auto result3 = lua.safe_script("c = obj.c", sol::script_pass_on_error);
REQUIRE(result3.valid());
double c = lua["c"];
auto result4 = lua.safe_script("b = obj:b()", sol::script_pass_on_error);
REQUIRE(result4.valid());
int b = lua["b"];
auto result5 = lua.safe_script("a = obj.a", sol::script_pass_on_error);
REQUIRE(result5.valid());
int a = lua["a"];
REQUIRE(d);
REQUIRE(c == 2.4);
REQUIRE(b == 10);
REQUIRE(a == 5);
}
TEST_CASE("inheritance/multi base", "test that multiple bases all work and overloading for constructors works with them") {
class TestClass00 {
public:
int Thing() const {
return 123;
}
};
class TestClass01 : public TestClass00 {
public:
TestClass01()
: a(1) {
}
TestClass01(const TestClass00& other)
: a(other.Thing()) {
}
int a;
};
class TestClass02 : public TestClass01 {
public:
TestClass02()
: b(2) {
}
TestClass02(const TestClass01& other)
: b(other.a) {
}
TestClass02(const TestClass00& other)
: b(other.Thing()) {
}
int b;
};
class TestClass03 : public TestClass02 {
public:
TestClass03()
: c(2) {
}
TestClass03(const TestClass02& other)
: c(other.b) {
}
TestClass03(const TestClass01& other)
: c(other.a) {
}
TestClass03(const TestClass00& other)
: c(other.Thing()) {
}
int c;
};
sol::state lua;
sol::usertype<TestClass00> s_TestUsertype00(
sol::call_constructor, sol::constructors<sol::types<>>(),
"Thing", &TestClass00::Thing);
lua.set_usertype("TestClass00", s_TestUsertype00);
sol::usertype<TestClass01> s_TestUsertype01(
sol::call_constructor, sol::constructors<sol::types<>, sol::types<const TestClass00&>>(),
sol::base_classes, sol::bases<TestClass00>(),
"a", &TestClass01::a);
lua.set_usertype("TestClass01", s_TestUsertype01);
sol::usertype<TestClass02> s_TestUsertype02(
sol::call_constructor, sol::constructors<sol::types<>, sol::types<const TestClass01&>, sol::types<const TestClass00&>>(),
sol::base_classes, sol::bases<TestClass01, TestClass00>(),
"b", &TestClass02::b);
lua.set_usertype("TestClass02", s_TestUsertype02);
sol::usertype<TestClass03> s_TestUsertype03(
sol::call_constructor, sol::constructors<sol::types<>, sol::types<const TestClass02&>, sol::types<const TestClass01&>, sol::types<const TestClass00&>>(),
sol::base_classes, sol::bases<TestClass02, TestClass01, TestClass00>(),
"c", &TestClass03::c);
lua.set_usertype("TestClass03", s_TestUsertype03);
auto result1 = lua.safe_script(R"(
tc0 = TestClass00()
tc2 = TestClass02(tc0)
tc1 = TestClass01()
tc3 = TestClass03(tc1)
)", sol::script_pass_on_error);
REQUIRE(result1.valid());
TestClass00& tc0 = lua["tc0"];
TestClass01& tc1 = lua["tc1"];
TestClass02& tc2 = lua["tc2"];
TestClass03& tc3 = lua["tc3"];
REQUIRE(tc0.Thing() == 123);
REQUIRE(tc1.a == 1);
REQUIRE(tc2.a == 1);
REQUIRE(tc2.b == 123);
REQUIRE(tc3.a == 1);
REQUIRE(tc3.b == 2);
REQUIRE(tc3.c == 1);
}
TEST_CASE("inheritance/simple multi base", "test that multiple bases all work and overloading for constructors works with them") {
class TestClass00 {
public:
int Thing() const {
return 123;
}
};
class TestClass01 : public TestClass00 {
public:
TestClass01()
: a(1) {
}
TestClass01(const TestClass00& other)
: a(other.Thing()) {
}
int a;
};
class TestClass02 : public TestClass01 {
public:
TestClass02()
: b(2) {
}
TestClass02(const TestClass01& other)
: b(other.a) {
}
TestClass02(const TestClass00& other)
: b(other.Thing()) {
}
int b;
};
class TestClass03 : public TestClass02 {
public:
TestClass03()
: c(2) {
}
TestClass03(const TestClass02& other)
: c(other.b) {
}
TestClass03(const TestClass01& other)
: c(other.a) {
}
TestClass03(const TestClass00& other)
: c(other.Thing()) {
}
int c;
};
sol::state lua;
sol::simple_usertype<TestClass00> s_TestUsertype00(lua,
sol::call_constructor, sol::constructors<sol::types<>>(),
"Thing", &TestClass00::Thing);
lua.set_usertype("TestClass00", s_TestUsertype00);
sol::simple_usertype<TestClass01> s_TestUsertype01(lua,
sol::call_constructor, sol::constructors<sol::types<>, sol::types<const TestClass00&>>(),
sol::base_classes, sol::bases<TestClass00>(),
"a", &TestClass01::a);
lua.set_usertype("TestClass01", s_TestUsertype01);
sol::simple_usertype<TestClass02> s_TestUsertype02(lua,
sol::call_constructor, sol::constructors<sol::types<>, sol::types<const TestClass01&>, sol::types<const TestClass00&>>(),
sol::base_classes, sol::bases<TestClass01, TestClass00>(),
"b", &TestClass02::b);
lua.set_usertype("TestClass02", s_TestUsertype02);
sol::simple_usertype<TestClass03> s_TestUsertype03(lua,
sol::call_constructor, sol::constructors<sol::types<>, sol::types<const TestClass02&>, sol::types<const TestClass01&>, sol::types<const TestClass00&>>(),
sol::base_classes, sol::bases<TestClass02, TestClass01, TestClass00>(),
"c", &TestClass03::c);
lua.set_usertype("TestClass03", s_TestUsertype03);
auto result1 = lua.safe_script(R"(
tc0 = TestClass00()
tc2 = TestClass02(tc0)
tc1 = TestClass01()
tc3 = TestClass03(tc1)
)", sol::script_pass_on_error);
REQUIRE(result1.valid());
TestClass00& tc0 = lua["tc0"];
TestClass01& tc1 = lua["tc1"];
TestClass02& tc2 = lua["tc2"];
TestClass03& tc3 = lua["tc3"];
REQUIRE(tc0.Thing() == 123);
REQUIRE(tc1.a == 1);
REQUIRE(tc2.a == 1);
REQUIRE(tc2.b == 123);
REQUIRE(tc3.a == 1);
REQUIRE(tc3.b == 2);
REQUIRE(tc3.c == 1);
}
+156
View File
@@ -0,0 +1,156 @@
// sol2
// The MIT License (MIT)
// Copyright (c) 2013-2018 Rapptz, ThePhD and contributors
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "test_sol.hpp"
#include <catch.hpp>
#include <cstdint>
#include <limits>
TEST_CASE("large_integer/bool", "pass bool integral value to and from lua") {
sol::state lua;
lua.open_libraries();
lua.set_function("f", [&](bool num) {
REQUIRE(num == true);
return num;
});
auto result1 = lua.safe_script("x = f(true)\n"
"assert(x == true)", sol::script_pass_on_error);
REQUIRE(result1.valid());
sol::object x = lua["x"];
REQUIRE(x.is<bool>());
REQUIRE(x.as<bool>() == true);
REQUIRE_FALSE(x.is<std::int32_t>());
{
auto result = lua.safe_script("f(1)", sol::script_pass_on_error);
REQUIRE_FALSE(result.valid());
}
}
TEST_CASE("large_integers/unsigned32", "pass large unsigned 32bit values to and from lua") {
using T = std::uint32_t;
sol::state lua;
lua.open_libraries();
lua.set_function("f", [&](T num) -> T {
REQUIRE(num == 0xFFFFFFFF);
return num;
});
auto result1 = lua.safe_script("x = f(0xFFFFFFFF)\n"
"assert(x == 0xFFFFFFFF)", sol::script_pass_on_error);
REQUIRE(result1.valid());
sol::object x = lua["x"];
REQUIRE(x.is<T>());
REQUIRE(x.as<T>() == 0xFFFFFFFF);
}
TEST_CASE("large_integer/unsigned53", "pass large unsigned 53bit value to and from lua") {
using T = std::uint64_t;
sol::state lua;
lua.open_libraries();
lua.set_function("f", [&](T num) -> T {
REQUIRE(num == 0x1FFFFFFFFFFFFFull);
return num;
});
auto result1 = lua.safe_script("x = f(0x1FFFFFFFFFFFFF)\n"
"assert(x == 0x1FFFFFFFFFFFFF)");
REQUIRE(result1.valid());
sol::object x = lua["x"];
REQUIRE(x.is<T>());
REQUIRE(x.as<T>() == 0x1FFFFFFFFFFFFFull);
}
TEST_CASE("large_integer/unsigned64", "pass too large unsigned 64bit value to lua") {
using T = std::int64_t;
sol::state lua;
lua.set_function("f", [&](T num) -> T {
return num;
});
REQUIRE_THROWS([&lua]() {
sol::protected_function pf = lua["f"];
auto result = pf(0xFFFFFFFFFFFFFFFFull);
}());
}
TEST_CASE("large_integer/double", "pass negative and large positive values as signed and unsigned from and to lua") {
sol::state lua;
lua.open_libraries();
lua.set_function("s32", [&](std::int32_t num) {
return num;
});
lua.set_function("s64", [&](std::int64_t num) {
return num;
});
lua.set_function("u32", [&](std::uint32_t num) {
return num;
});
lua.set_function("u64", [&](std::uint64_t num) {
return num;
});
{
//signed 32bit
auto result1 = lua.safe_script("x = s32(-1)", sol::script_pass_on_error);
REQUIRE(result1.valid());
auto result2 = lua.safe_script("assert(x == -1)", sol::script_pass_on_error);
REQUIRE(result2.valid());
auto result3 = lua.safe_script("x = s32(0xFFFFFFFF)", sol::script_pass_on_error);
REQUIRE(result3.valid());
auto result4 = lua.safe_script("assert(x == -1)", sol::script_pass_on_error);
REQUIRE(result4.valid());
sol::object x = lua["x"];
REQUIRE(x.is<std::int32_t>());
REQUIRE(x.as<std::int32_t>() == -1);
REQUIRE(x.is<std::uint32_t>());
REQUIRE(x.as<std::uint32_t>() == 0xFFFFFFFF);
}
//unsigned 32bit
{
auto result1 = lua.safe_script("x = u32(0xFFFFFFFF)", sol::script_pass_on_error);
REQUIRE(result1.valid());
auto result2 = lua.safe_script("assert(x == 0xFFFFFFFF)", sol::script_pass_on_error);
REQUIRE(result2.valid());
auto result3 = lua.safe_script("x = u32(-1)", sol::script_pass_on_error);
REQUIRE(result3.valid());
auto result4 = lua.safe_script("assert(x == 0xFFFFFFFF)", sol::script_pass_on_error);
REQUIRE(result4.valid());
sol::object x = lua["x"];
REQUIRE(x.is<std::int32_t>());
REQUIRE(x.as<std::int32_t>() == -1);
REQUIRE(x.is<std::uint32_t>());
REQUIRE(x.as<std::uint32_t>() == 0xFFFFFFFF);
}
//signed 64bit
{
auto result1 = lua.safe_script("x = s64(-1)", sol::script_pass_on_error);
REQUIRE(result1.valid());
auto result2 = lua.safe_script("assert(x == -1)", sol::script_pass_on_error);
REQUIRE(result2.valid());
sol::object x = lua["x"];
REQUIRE(x.is<std::int64_t>());
REQUIRE(x.as<std::int64_t>() == -1);
REQUIRE(x.is<std::uint64_t>());
REQUIRE(x.as<std::uint64_t>() == 0xFFFFFFFFFFFFFFFFull);
}
}
+518
View File
@@ -0,0 +1,518 @@
// sol2
// The MIT License (MIT)
// Copyright (c) 2013-2018 Rapptz, ThePhD and contributors
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "test_sol.hpp"
#include <catch.hpp>
#include <algorithm>
#include <numeric>
#include <iostream>
TEST_CASE("operators/default", "test that generic equality operators and all sorts of equality tests can be used") {
struct T {};
struct U {
int a;
U(int x = 20)
: a(x) {
}
bool operator==(const U& r) {
return a == r.a;
}
};
struct V {
int a;
V(int x = 20)
: a(x) {
}
bool operator==(const V& r) const {
return a == r.a;
}
};
sol::state lua;
lua.open_libraries(sol::lib::base);
T t1;
T& t2 = t1;
T t3;
U u1;
U u2{ 30 };
U u3;
U v1;
U v2{ 30 };
U v3;
lua["t1"] = &t1;
lua["t2"] = &t2;
lua["t3"] = &t3;
lua["u1"] = &u1;
lua["u2"] = &u2;
lua["u3"] = &u3;
lua["v1"] = &v1;
lua["v2"] = &v2;
lua["v3"] = &v3;
SECTION("plain") {
// Can only compare identity here
{
auto result1 = lua.safe_script("assert(t1 == t1)"
"assert(t2 == t2)"
"assert(t3 == t3)", sol::script_pass_on_error);
REQUIRE(result1.valid());
}
{
auto result1 = lua.safe_script("assert(t1 == t2)"
"assert(not (t1 == t3))"
"assert(not (t2 == t3))", sol::script_pass_on_error);
REQUIRE(result1.valid());
}
// Object should compare equal to themselves
// (and not invoke operator==; pointer test should be sufficient)
{
auto result1 = lua.safe_script("assert(u1 == u1)"
"assert(u2 == u2)"
"assert(u3 == u3)", sol::script_pass_on_error);
REQUIRE(result1.valid());
}
{
auto result1 = lua.safe_script("assert(not (u1 == u2))"
"assert(u1 == u3)"
"assert(not (u2 == u3))", sol::script_pass_on_error);
REQUIRE(result1.valid());
}
// Object should compare equal to themselves
// (and not invoke operator==; pointer test should be sufficient)
{
auto result1 = lua.safe_script("assert(v1 == v1)"
"assert(v2 == v2)"
"assert(v3 == v3)", sol::script_pass_on_error);
REQUIRE(result1.valid());
}
{
auto result1 = lua.safe_script("assert(not (v1 == v2))"
"assert(v1 == v3)"
"assert(not (v2 == v3))", sol::script_pass_on_error);
REQUIRE(result1.valid());
}
}
SECTION("regular") {
lua.new_usertype<T>("T");
lua.new_usertype<U>("U");
lua.new_usertype<V>("V");
// Can only compare identity here
{
auto result1 = lua.safe_script("assert(t1 == t1)"
"assert(t2 == t2)"
"assert(t3 == t3)", sol::script_pass_on_error);
REQUIRE(result1.valid());
}
{
auto result1 = lua.safe_script("assert(t1 == t2)"
"assert(not (t1 == t3))"
"assert(not (t2 == t3))", sol::script_pass_on_error);
REQUIRE(result1.valid());
}
// Object should compare equal to themselves
// (and not invoke operator==; pointer test should be sufficient)
{
auto result1 = lua.safe_script("assert(u1 == u1)"
"assert(u2 == u2)"
"assert(u3 == u3)", sol::script_pass_on_error);
REQUIRE(result1.valid());
}
{
auto result1 = lua.safe_script("assert(not (u1 == u2))"
"assert(u1 == u3)"
"assert(not (u2 == u3))", sol::script_pass_on_error);
REQUIRE(result1.valid());
}
// Object should compare equal to themselves
// (and not invoke operator==; pointer test should be sufficient)
{
auto result1 = lua.safe_script("assert(v1 == v1)"
"assert(v2 == v2)"
"assert(v3 == v3)", sol::script_pass_on_error);
REQUIRE(result1.valid());
}
{
auto result1 = lua.safe_script("assert(not (v1 == v2))"
"assert(v1 == v3)"
"assert(not (v2 == v3))", sol::script_pass_on_error);
REQUIRE(result1.valid());
}
}
SECTION("simple") {
lua.new_simple_usertype<T>("T");
lua.new_simple_usertype<U>("U");
lua.new_simple_usertype<V>("V");
// Can only compare identity here
{
auto result1 = lua.safe_script("assert(t1 == t1)"
"assert(t2 == t2)"
"assert(t3 == t3)", sol::script_pass_on_error);
REQUIRE(result1.valid());
}
{
auto result1 = lua.safe_script("assert(t1 == t2)"
"assert(not (t1 == t3))"
"assert(not (t2 == t3))", sol::script_pass_on_error);
REQUIRE(result1.valid());
}
// Object should compare equal to themselves
// (and not invoke operator==; pointer test should be sufficient)
{
auto result1 = lua.safe_script("assert(u1 == u1)"
"assert(u2 == u2)"
"assert(u3 == u3)", sol::script_pass_on_error);
REQUIRE(result1.valid());
}
{
auto result1 = lua.safe_script("assert(not (u1 == u2))"
"assert(u1 == u3)"
"assert(not (u2 == u3))", sol::script_pass_on_error);
REQUIRE(result1.valid());
}
// Object should compare equal to themselves
// (and not invoke operator==; pointer test should be sufficient)
{
auto result1 = lua.safe_script("assert(v1 == v1)"
"assert(v2 == v2)"
"assert(v3 == v3)", sol::script_pass_on_error);
REQUIRE(result1.valid());
}
{
auto result1 = lua.safe_script("assert(not (v1 == v2))"
"assert(v1 == v3)"
"assert(not (v2 == v3))", sol::script_pass_on_error);
REQUIRE(result1.valid());
}
}
}
TEST_CASE("operators/call", "test call operator generation") {
struct callable {
int operator()(int a, std::string b) {
return a + static_cast<int>(b.length());
}
};
sol::state lua;
lua.open_libraries(sol::lib::base);
SECTION("plain") {
{
lua.set("obj", callable());
auto result1 = lua.safe_script("v = obj(2, 'bark woof')", sol::script_pass_on_error);
REQUIRE(result1.valid());
int v = lua["v"];
REQUIRE(v == 11);
}
}
SECTION("regular") {
lua.new_usertype<callable>("callable");
{
auto result1 = lua.safe_script("obj = callable.new()\n"
"v = obj(2, 'bark woof')", sol::script_pass_on_error);
REQUIRE(result1.valid());
int v = lua["v"];
REQUIRE(v == 11);
}
}
SECTION("simple") {
lua.new_simple_usertype<callable>("callable");
{
lua.safe_script("obj = callable.new()");
lua.safe_script("v = obj(2, 'bark woof')");
int v = lua["v"];
REQUIRE(v == 11);
}
}
}
struct stringable {
static const void* last_print_ptr;
};
const void* stringable::last_print_ptr = nullptr;
std::ostream& operator<<(std::ostream& ostr, const stringable& o) {
stringable::last_print_ptr = static_cast<const void*>(&o);
return ostr << "{ stringable, std::ostream! }";
}
struct adl_stringable {
static const void* last_print_ptr;
};
const void* adl_stringable::last_print_ptr = nullptr;
std::string to_string(const adl_stringable& o) {
adl_stringable::last_print_ptr = static_cast<const void*>(&o);
return "{ adl_stringable, to_string! }";
}
namespace inside {
struct adl_stringable2 {
static const void* last_print_ptr;
};
const void* adl_stringable2::last_print_ptr = nullptr;
std::string to_string(const adl_stringable2& o) {
adl_stringable2::last_print_ptr = static_cast<const void*>(&o);
return "{ inside::adl_stringable2, inside::to_string! }";
}
} // namespace inside
struct member_stringable {
static const void* last_print_ptr;
std::string to_string() const {
member_stringable::last_print_ptr = static_cast<const void*>(this);
return "{ member_stringable, to_string! }";
}
};
const void* member_stringable::last_print_ptr = nullptr;
TEST_CASE("operators/stringable", "test std::ostream stringability") {
sol::state lua;
lua.open_libraries(sol::lib::base);
SECTION("plain") {
{
lua["obj"] = stringable();
auto result1 = lua.safe_script("print(obj)", sol::script_pass_on_error);
REQUIRE(result1.valid());
stringable& obj = lua["obj"];
REQUIRE(stringable::last_print_ptr == &obj);
}
}
SECTION("regular") {
lua.new_usertype<stringable>("stringable");
{
auto result1 = lua.safe_script(R"(obj = stringable.new()
print(obj) )", sol::script_pass_on_error);
REQUIRE(result1.valid());
stringable& obj = lua["obj"];
REQUIRE(stringable::last_print_ptr == &obj);
}
}
SECTION("simple") {
lua.new_simple_usertype<stringable>("stringable");
{
auto result1 = lua.safe_script(R"(obj = stringable.new()
print(obj))", sol::script_pass_on_error);
REQUIRE(result1.valid());
stringable& obj = lua["obj"];
REQUIRE(stringable::last_print_ptr == &obj);
}
}
}
TEST_CASE("operators/adl_stringable", "test adl to_string stringability") {
sol::state lua;
lua.open_libraries(sol::lib::base);
SECTION("plain") {
{
lua["obj"] = adl_stringable();
lua.safe_script("print(obj)");
adl_stringable& obj = lua["obj"];
REQUIRE(adl_stringable::last_print_ptr == &obj);
}
}
SECTION("regular") {
lua.new_usertype<adl_stringable>("stringable");
{
lua["obj"] = adl_stringable();
lua.safe_script("print(obj)");
adl_stringable& obj = lua["obj"];
REQUIRE(adl_stringable::last_print_ptr == &obj);
}
}
SECTION("simple") {
lua.new_simple_usertype<adl_stringable>("stringable");
{
lua.safe_script("obj = stringable.new()");
lua.safe_script("print(obj)");
adl_stringable& obj = lua["obj"];
REQUIRE(adl_stringable::last_print_ptr == &obj);
}
}
}
TEST_CASE("operators/inside::adl_stringable2", "test adl to_string stringability from inside a namespace") {
sol::state lua;
lua.open_libraries(sol::lib::base);
SECTION("plain") {
{
lua["obj"] = inside::adl_stringable2();
lua.safe_script("print(obj)");
inside::adl_stringable2& obj = lua["obj"];
REQUIRE(inside::adl_stringable2::last_print_ptr == &obj);
}
}
SECTION("regular") {
lua.new_usertype<inside::adl_stringable2>("stringable");
{
lua.safe_script("obj = stringable.new()");
lua.safe_script("print(obj)");
inside::adl_stringable2& obj = lua["obj"];
REQUIRE(inside::adl_stringable2::last_print_ptr == &obj);
}
}
SECTION("simple") {
lua.new_simple_usertype<inside::adl_stringable2>("stringable");
{
lua.safe_script("obj = stringable.new()");
lua.safe_script("print(obj)");
inside::adl_stringable2& obj = lua["obj"];
REQUIRE(inside::adl_stringable2::last_print_ptr == &obj);
}
}
}
TEST_CASE("operators/member_stringable", "test member to_string stringability") {
sol::state lua;
lua.open_libraries(sol::lib::base);
SECTION("plain") {
{
lua["obj"] = member_stringable();
lua.safe_script("print(obj)");
member_stringable& obj = lua["obj"];
REQUIRE(member_stringable::last_print_ptr == &obj);
}
}
SECTION("regular") {
lua.new_usertype<member_stringable>("stringable");
{
lua.safe_script("obj = stringable.new()");
lua.safe_script("print(obj)");
member_stringable& obj = lua["obj"];
REQUIRE(member_stringable::last_print_ptr == &obj);
}
}
SECTION("simple") {
lua.new_simple_usertype<member_stringable>("stringable");
{
lua.safe_script("obj = stringable.new()");
lua.safe_script("print(obj)");
member_stringable& obj = lua["obj"];
REQUIRE(member_stringable::last_print_ptr == &obj);
}
}
}
TEST_CASE("operators/container-like", "test that generic begin/end and iterator are automatically bound") {
#if SOL_LUA_VERSION > 501
struct container {
typedef int* iterator;
typedef int value_type;
value_type values[10];
container() {
std::iota(begin(), end(), 1);
}
iterator begin() {
return &values[0];
}
iterator end() {
return &values[0] + 10;
}
};
sol::state lua;
lua.open_libraries(sol::lib::base);
SECTION("plain") {
{
lua["obj"] = container();
lua.safe_script("i = 0 for k, v in pairs(obj) do i = i + 1 assert(k == v) end");
std::size_t i = lua["i"];
REQUIRE(i == 10);
}
}
SECTION("regular") {
lua.new_usertype<container>("container");
{
lua.safe_script("obj = container.new()");
lua.safe_script("i = 0 for k, v in pairs(obj) do i = i + 1 assert(k == v) end");
std::size_t i = lua["i"];
REQUIRE(i == 10);
}
}
SECTION("simple") {
lua.new_simple_usertype<container>("container");
{
lua.safe_script("obj = container.new()");
lua.safe_script("i = 0 for k, v in pairs(obj) do i = i + 1 assert(k == v) end");
std::size_t i = lua["i"];
REQUIRE(i == 10);
}
}
#else
SUCCEED("");
#endif
}
TEST_CASE("operators/length", "test that size is automatically bound to the length operator") {
struct sizable {
std::size_t size() const {
return 6;
}
};
sol::state lua;
lua.open_libraries(sol::lib::base);
SECTION("plain") {
{
lua["obj"] = sizable();
lua.safe_script("s = #obj");
std::size_t s = lua["s"];
REQUIRE(s == 6);
}
}
SECTION("regular") {
lua.new_usertype<sizable>("sizable");
{
lua.safe_script("obj = sizable.new()");
lua.safe_script("s = #obj");
std::size_t s = lua["s"];
REQUIRE(s == 6);
}
}
SECTION("simple") {
lua.new_simple_usertype<sizable>("sizable");
{
lua.safe_script("obj = sizable.new()");
lua.safe_script("s = #obj");
std::size_t s = lua["s"];
REQUIRE(s == 6);
}
}
}
+68
View File
@@ -0,0 +1,68 @@
// sol2
// The MIT License (MIT)
// Copyright (c) 2013-2018 Rapptz, ThePhD and contributors
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "test_sol.hpp"
#include <catch.hpp>
TEST_CASE("issues/stack overflow", "make sure various operations repeated don't trigger stack overflow") {
sol::state lua;
lua.safe_script("t = {};t[0]=20");
lua.safe_script("lua_function=function(i)return i;end");
sol::function f = lua["lua_function"];
std::string teststring = "testtext";
REQUIRE_NOTHROW([&] {
for (int i = 0; i < 1000000; ++i) {
std::string result = f(teststring);
if (result != teststring)
throw std::logic_error("RIP");
}
}());
sol::table t = lua["t"];
int expected = 20;
REQUIRE_NOTHROW([&] {
for (int i = 0; i < 1000000; ++i) {
int result = t[0];
t.size();
if (result != expected)
throw std::logic_error("RIP");
}
}());
}
TEST_CASE("issues/stack overflow 2", "make sure basic iterators clean up properly when they're not iterated through (e.g., with empty())") {
sol::state lua;
sol::table t = lua.create_table_with(1, "wut");
int MAX = 50000;
auto fx = [&]() {
int a = 50;
for (int i = 0; i < MAX; ++i) {
if (t.empty()) {
a += 4;
}
a += 2;
}
};
REQUIRE_NOTHROW(fx());
}
+180
View File
@@ -0,0 +1,180 @@
// sol2
// The MIT License (MIT)
// Copyright (c) 2013-2018 Rapptz, ThePhD and contributors
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#define SOL_CHECK_ARGUMENTS 1
#define SOL_ENABLE_INTEROP 1
#include <sol.hpp>
#include <catch.hpp>
TEST_CASE("plain/alignment", "test that aligned classes in certain compilers don't trigger compiler errors") {
#ifdef _MSC_VER
__declspec(align(16)) struct aligned_class {
int var;
};
struct A {
aligned_class a;
};
sol::state lua;
lua.open_libraries(sol::lib::base, sol::lib::math);
lua.new_usertype<A>("A");
A a;
lua["a"] = &a;
A& la = lua["a"];
REQUIRE(&a == &la);
#else
REQUIRE(true);
#endif // VC++ stuff
}
TEST_CASE("plain/indestructible", "test that we error for types that are innately indestructible") {
struct indestructible {
public:
int v = 20;
struct insider {
void operator()(indestructible* i) const {
i->~indestructible();
}
};
private:
~indestructible() {
}
};
SECTION("doomed") {
sol::state lua;
lua.open_libraries(sol::lib::base);
std::unique_ptr<indestructible, indestructible::insider> i = sol::detail::make_unique_deleter<indestructible, indestructible::insider>();
lua["i"] = *i;
lua.safe_script("i = nil");
auto result = lua.safe_script("collectgarbage()", sol::script_pass_on_error);
REQUIRE_FALSE(result.valid());
}
SECTION("saved") {
sol::state lua;
lua.open_libraries(sol::lib::base);
std::unique_ptr<indestructible, indestructible::insider> i = sol::detail::make_unique_deleter<indestructible, indestructible::insider>();
lua["i"] = *i;
lua.new_usertype<indestructible>("indestructible",
sol::default_constructor,
sol::meta_function::garbage_collect, sol::destructor([](indestructible& i) {
indestructible::insider del;
del(&i);
}));
lua.safe_script("i = nil");
auto result = lua.safe_script("collectgarbage()", sol::script_pass_on_error);
REQUIRE(result.valid());
}
}
TEST_CASE("plain/constructors and destructors", "Make sure that constructors, destructors, deallocators and others work properly with the desired type") {
static int constructed = 0;
static int destructed = 0;
static int copied = 0;
static int moved = 0;
struct st {
int value = 10;
st() : value(10) {
++constructed;
}
st(const st& o) : value(o.value) {
++copied;
++constructed;
}
st(st&& o) : value(o.value) {
++moved;
++constructed;
}
~st() {
value = 0;
++destructed;
}
};
struct deallocate_only {
void operator()(st* p) const {
std::allocator<st> alloc;
alloc.deallocate(p, 1);
}
};
{
sol::state lua;
lua["f"] = sol::constructors<st()>();
lua["g"] = sol::initializers([](st* mem) { std::allocator<st> alloc; std::allocator_traits<std::allocator<st>>::construct(alloc, mem); });
lua["h"] = sol::factories([]() { return st(); });
lua["d"] = sol::destructor([](st& m) { m.~st(); });
sol::protected_function_result result1 = lua.safe_script("v = f()", &sol::script_pass_on_error);
REQUIRE(result1.valid());
st& v = lua["v"];
REQUIRE(v.value == 10);
REQUIRE(constructed == 1);
REQUIRE(destructed == 0);
{
std::unique_ptr<st, deallocate_only> unsafe(new st());
lua["unsafe"] = unsafe.get();
sol::protected_function_result result2 = lua.safe_script("d(unsafe)", &sol::script_pass_on_error);
REQUIRE(result2.valid());
REQUIRE(constructed == 2);
REQUIRE(destructed == 1);
}
REQUIRE(constructed == 2);
REQUIRE(destructed == 1);
{
sol::protected_function_result result3 = lua.safe_script("vg = g()", &sol::script_pass_on_error);
REQUIRE(result3.valid());
st& vg = lua["vg"];
REQUIRE(vg.value == 10);
REQUIRE(constructed == 3);
REQUIRE(destructed == 1);
}
{
sol::protected_function_result result4 = lua.safe_script("vh = h()", &sol::script_pass_on_error);
REQUIRE(result4.valid());
st& vh = lua["vh"];
REQUIRE(vh.value == 10);
}
}
int purely_constructed = constructed - moved - copied;
int purely_destructed = destructed - moved - copied;
REQUIRE(constructed == destructed);
REQUIRE(purely_constructed == purely_destructed);
REQUIRE(purely_constructed == 4);
REQUIRE(purely_destructed == 4);
}
+99
View File
@@ -0,0 +1,99 @@
// sol2
// The MIT License (MIT)
// Copyright (c) 2013-2018 Rapptz, ThePhD and contributors
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "test_sol.hpp"
#include <catch.hpp>
#include <iostream>
TEST_CASE("proxy/function results", "make sure that function results return proper proxies and can be indexed nicely") {
sol::state lua;
SECTION("unsafe_function_result") {
auto ufr = lua.script("return 1, 2, 3, 4");
int accum = 0;
for (const auto& r : ufr) {
int v = r;
accum += v;
}
REQUIRE(accum == 10);
}
SECTION("protected_function_result") {
auto pfr = lua.safe_script("return 1, 2, 3, 4");
int accum = 0;
for (const auto& r : pfr) {
int v = r;
accum += v;
}
REQUIRE(accum == 10);
}
}
TEST_CASE("proxy/optional conversion", "make sure optional conversions out of a table work properly") {
sol::state state{};
sol::table table = state.create_table_with("func", 42);
sol::optional<sol::function> func = table["func"];
REQUIRE(func == sol::nullopt);
}
TEST_CASE("proxy/proper-pushing", "allow proxies to reference other proxies and be serialized as the proxy itself and not a function or something") {
sol::state lua;
lua.open_libraries(sol::lib::base, sol::lib::io);
class T {};
lua.new_usertype<T>("T");
T t;
lua["t1"] = &t;
lua["t2"] = lua["t1"];
lua.safe_script("b = t1 == t2");
bool b = lua["b"];
REQUIRE(b);
}
TEST_CASE("proxy/equality", "check to make sure equality tests work") {
sol::state lua;
#ifndef __clang__
REQUIRE((lua["a"] == sol::lua_nil));
REQUIRE((lua["a"] == nullptr));
REQUIRE_FALSE((lua["a"] != sol::lua_nil));
REQUIRE_FALSE((lua["a"] != nullptr));
REQUIRE_FALSE((lua["a"] == 0));
REQUIRE_FALSE((lua["a"] == 2));
REQUIRE((lua["a"] != 0));
REQUIRE((lua["a"] != 2));
#endif // clang screws up by trying to access int128 types that it doesn't support, even when we don't ask for them
lua["a"] = 2;
#ifndef __clang__
REQUIRE_FALSE((lua["a"] == sol::lua_nil));
REQUIRE_FALSE((lua["a"] == nullptr));
REQUIRE((lua["a"] != sol::lua_nil));
REQUIRE((lua["a"] != nullptr));
REQUIRE_FALSE((lua["a"] == 0));
REQUIRE((lua["a"] == 2));
REQUIRE((lua["a"] != 0));
REQUIRE_FALSE((lua["a"] != 2));
#endif // clang screws up by trying to access int128 types that it doesn't support, even when we don't ask for them
}
+977
View File
@@ -0,0 +1,977 @@
// sol2
// The MIT License (MIT)
// Copyright (c) 2013-2018 Rapptz, ThePhD and contributors
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "test_sol.hpp"
#include <catch.hpp>
#include <iostream>
#include <list>
#include <memory>
#include <mutex>
TEST_CASE("simple_usertype/usertypes", "Ensure that simple usertypes properly work here") {
struct marker {
bool value = false;
};
struct bark {
int var = 50;
marker mark;
void fun() {
var = 51;
}
int get() const {
return var;
}
int set(int x) {
var = x;
return var;
}
std::string special() const {
return mark.value ? "woof" : "pantpant";
}
const marker& the_marker() const {
return mark;
}
};
sol::state lua;
lua.new_simple_usertype<bark>("bark",
"fun", &bark::fun,
"get", &bark::get,
"var", sol::as_function(&bark::var),
"the_marker", sol::as_function(&bark::the_marker),
"x", sol::overload(&bark::get),
"y", sol::overload(&bark::set),
"z", sol::overload(&bark::get, &bark::set));
lua.safe_script("b = bark.new()");
bark& b = lua["b"];
lua.safe_script("b:fun()");
int var = b.var;
REQUIRE(var == 51);
lua.safe_script("b:var(20)");
lua.safe_script("v = b:var()");
int v = lua["v"];
REQUIRE(v == 20);
REQUIRE(b.var == 20);
lua.safe_script("m = b:the_marker()");
marker& m = lua["m"];
REQUIRE_FALSE(b.mark.value);
REQUIRE_FALSE(m.value);
m.value = true;
REQUIRE(&b.mark == &m);
REQUIRE(b.mark.value);
sol::table barktable = lua["bark"];
barktable["special"] = &bark::special;
lua.safe_script("s = b:special()");
std::string s = lua["s"];
REQUIRE(s == "woof");
lua.safe_script("b:y(24)");
lua.safe_script("x = b:x()");
int x = lua["x"];
REQUIRE(x == 24);
lua.safe_script("z = b:z(b:z() + 5)");
int z = lua["z"];
REQUIRE(z == 29);
}
TEST_CASE("simple_usertype/usertype constructors", "Ensure that calls with specific arguments work") {
struct marker {
bool value = false;
};
struct bark {
int var = 50;
marker mark;
bark() {
}
bark(int v)
: var(v) {
}
void fun() {
var = 51;
}
int get() const {
return var;
}
int set(int x) {
var = x;
return var;
}
std::string special() const {
return mark.value ? "woof" : "pantpant";
}
const marker& the_marker() const {
return mark;
}
};
sol::state lua;
lua.new_simple_usertype<bark>("bark",
sol::constructors<sol::types<>, sol::types<int>>(),
"fun", sol::protect(&bark::fun),
"get", &bark::get,
"var", sol::as_function(&bark::var),
"the_marker", &bark::the_marker,
"x", sol::overload(&bark::get),
"y", sol::overload(&bark::set),
"z", sol::overload(&bark::get, &bark::set));
lua.safe_script("bx = bark.new(760)");
bark& bx = lua["bx"];
REQUIRE(bx.var == 760);
lua.safe_script("b = bark.new()");
bark& b = lua["b"];
lua.safe_script("b:fun()");
int var = b.var;
REQUIRE(var == 51);
lua.safe_script("b:var(20)");
lua.safe_script("v = b:var()");
int v = lua["v"];
REQUIRE(v == 20);
lua.safe_script("m = b:the_marker()");
marker& m = lua["m"];
REQUIRE_FALSE(b.mark.value);
REQUIRE_FALSE(m.value);
m.value = true;
REQUIRE(&b.mark == &m);
REQUIRE(b.mark.value);
sol::table barktable = lua["bark"];
barktable["special"] = &bark::special;
lua.safe_script("s = b:special()");
std::string s = lua["s"];
REQUIRE(s == "woof");
lua.safe_script("b:y(24)");
lua.safe_script("x = b:x()");
int x = lua["x"];
REQUIRE(x == 24);
lua.safe_script("z = b:z(b:z() + 5)");
int z = lua["z"];
REQUIRE(z == 29);
}
TEST_CASE("simple_usertype/vars", "simple usertype vars can bind various values (no ref)") {
int muh_variable = 10;
int through_variable = 25;
sol::state lua;
lua.open_libraries();
struct test {};
lua.new_simple_usertype<test>("test",
"straight", sol::var(2),
"global", sol::var(muh_variable),
"global2", sol::var(through_variable),
"global3", sol::var(std::ref(through_variable)));
through_variable = 20;
lua.safe_script(R"(
print(test.straight)
s = test.straight
print(test.global)
g = test.global
print(test.global2)
g2 = test.global2
print(test.global3)
g3 = test.global3
)");
int s = lua["s"];
int g = lua["g"];
int g2 = lua["g2"];
int g3 = lua["g3"];
REQUIRE(s == 2);
REQUIRE(g == 10);
REQUIRE(g2 == 25);
REQUIRE(g3 == 20);
}
TEST_CASE("simple_usertype/variable-control", "test to see if usertypes respond to inheritance and variable controls") {
class A {
public:
virtual void a() {
throw std::runtime_error("entered base pure virtual implementation");
};
};
class B : public A {
public:
virtual void a() override {
}
};
class sA {
public:
virtual void a() {
throw std::runtime_error("entered base pure virtual implementation");
};
};
class sB : public sA {
public:
virtual void a() override {
}
};
struct sV {
int a = 10;
int b = 20;
int get_b() const {
return b + 2;
}
void set_b(int value) {
b = value;
}
};
struct sW : sV {};
sol::state lua;
lua.open_libraries();
lua.new_usertype<A>("A", "a", &A::a);
lua.new_usertype<B>("B", sol::base_classes, sol::bases<A>());
lua.new_simple_usertype<sA>("sA", "a", &sA::a);
lua.new_simple_usertype<sB>("sB", sol::base_classes, sol::bases<sA>());
lua.new_simple_usertype<sV>("sV", "a", &sV::a, "b", &sV::b, "pb", sol::property(&sV::get_b, &sV::set_b));
lua.new_simple_usertype<sW>("sW", sol::base_classes, sol::bases<sV>());
B b;
lua.set("b", &b);
lua.safe_script("b:a()");
sB sb;
lua.set("sb", &sb);
lua.safe_script("sb:a()");
sV sv;
lua.set("sv", &sv);
lua.safe_script("print(sv.b)assert(sv.b == 20)");
sW sw;
lua.set("sw", &sw);
lua.safe_script("print(sw.a)assert(sw.a == 10)");
lua.safe_script("print(sw.b)assert(sw.b == 20)");
lua.safe_script("print(sw.pb)assert(sw.pb == 22)");
lua.safe_script("sw.a = 11");
lua.safe_script("sw.b = 21");
lua.safe_script("print(sw.a)assert(sw.a == 11)");
lua.safe_script("print(sw.b)assert(sw.b == 21)");
lua.safe_script("print(sw.pb)assert(sw.pb == 23)");
lua.safe_script("sw.pb = 25");
lua.safe_script("print(sw.b)assert(sw.b == 25)");
lua.safe_script("print(sw.pb)assert(sw.pb == 27)");
}
TEST_CASE("simple_usertype/factory constructor overloads", "simple usertypes should invoke the proper factories") {
class A {
public:
virtual void a() {
throw std::runtime_error("entered base pure virtual implementation");
};
};
class B : public A {
public:
int bvar = 24;
virtual void a() override {
}
void f() {
}
};
sol::state lua;
lua.open_libraries();
sol::constructors<sol::types<>, sol::types<const B&>> c;
lua.new_simple_usertype<B>("B",
sol::call_constructor, c,
"new", sol::factories([]() {
return B();
}),
"new2", sol::initializers([](B& mem) { new (&mem) B(); }, [](B& mem, int v) {
new(&mem)B(); mem.bvar = v; }),
"f", sol::as_function(&B::bvar),
"g", sol::overload([](B&) { return 2; }, [](B&, int v) { return v; }));
lua.safe_script("b = B()");
lua.safe_script("b2 = B.new()");
lua.safe_script("b3 = B.new2()");
lua.safe_script("b4 = B.new2(11)");
lua.safe_script("x = b:f()");
lua.safe_script("x2 = b2:f()");
lua.safe_script("x3 = b3:f()");
lua.safe_script("x4 = b4:f()");
int x = lua["x"];
int x2 = lua["x2"];
int x3 = lua["x3"];
int x4 = lua["x4"];
REQUIRE(x == 24);
REQUIRE(x2 == 24);
REQUIRE(x3 == 24);
REQUIRE(x4 == 11);
lua.safe_script("y = b:g()");
lua.safe_script("y2 = b2:g(3)");
lua.safe_script("y3 = b3:g()");
lua.safe_script("y4 = b4:g(3)");
int y = lua["y"];
int y2 = lua["y2"];
int y3 = lua["y3"];
int y4 = lua["y4"];
REQUIRE(y == 2);
REQUIRE(y2 == 3);
REQUIRE(y3 == 2);
REQUIRE(y4 == 3);
}
TEST_CASE("simple_usertype/runtime append", "allow extra functions to be appended at runtime directly to the metatable itself") {
class A {
};
class B : public A {
};
sol::state lua;
lua.new_simple_usertype<A>("A");
lua.new_simple_usertype<B>("B", sol::base_classes, sol::bases<A>());
lua.set("b", std::make_unique<B>());
lua["A"]["method"] = []() { return 200; };
lua["B"]["method2"] = [](B&) { return 100; };
lua.safe_script("x = b.method()");
lua.safe_script("y = b:method()");
int x = lua["x"];
int y = lua["y"];
REQUIRE(x == 200);
REQUIRE(y == 200);
lua.safe_script("z = b.method2(b)");
lua.safe_script("w = b:method2()");
int z = lua["z"];
int w = lua["w"];
REQUIRE(z == 100);
REQUIRE(w == 100);
}
TEST_CASE("simple_usertype/table append", "Ensure that appending to the meta table also affects the internal function table for pointers as well") {
struct A {
int func() {
return 5000;
}
};
sol::state lua;
lua.open_libraries();
lua.new_simple_usertype<A>("A");
sol::table table = lua["A"];
table["func"] = &A::func;
A a;
lua.set("a", &a);
lua.set("pa", &a);
lua.set("ua", std::make_unique<A>());
REQUIRE_NOTHROW([&] {
lua.safe_script("assert(a:func() == 5000)");
lua.safe_script("assert(pa:func() == 5000)");
lua.safe_script("assert(ua:func() == 5000)");
}());
}
TEST_CASE("simple_usertype/class call propogation", "make sure methods and variables from base classes work properly in SAFE_USERTYPE mode") {
class A {
public:
int var = 200;
int thing() const {
return 123;
}
};
class B : public A {
};
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_simple_usertype<B>("B",
sol::default_constructor,
"thing", &B::thing,
"var", &B::var);
lua.safe_script(R"(
b = B.new()
print(b.var)
b:thing()
)");
}
TEST_CASE("simple_usertype/call constructor", "ensure that all kinds of call-based constructors can be serialized") {
struct thing {};
struct v_test {
};
struct f_test {
int i;
f_test(int i)
: i(i) {
}
};
struct i_test {
int i;
i_test(int i)
: i(i) {
}
};
struct r_test {
int i;
r_test(int i)
: i(i) {
}
};
sol::state lua;
lua.open_libraries(sol::lib::base);
auto f = sol::factories([]() { return f_test(30); });
lua.new_simple_usertype<f_test>("f_test",
sol::call_constructor, sol::factories([]() {
return f_test(20);
}),
"new", f);
lua.new_simple_usertype<i_test>("i_test",
sol::call_constructor, sol::initializers([](i_test& obj) {
new (&obj) i_test(21);
}));
lua.new_simple_usertype<r_test>("r_test",
sol::call_constructor, [](sol::table) {
return r_test(22);
});
lua.safe_script("a = f_test()");
lua.safe_script("b = i_test()");
lua.safe_script("c = r_test()");
lua.safe_script("d = f_test.new()");
f_test& a = lua["a"];
f_test& d = lua["d"];
i_test& b = lua["b"];
r_test& c = lua["c"];
REQUIRE(a.i == 20);
REQUIRE(b.i == 21);
REQUIRE(c.i == 22);
REQUIRE(d.i == 30);
auto vfactories = sol::factories(
[](const sol::table& tbl) {
for (auto v : tbl) {
REQUIRE(v.second.valid());
REQUIRE(v.second.is<thing>());
}
return v_test();
});
lua.new_simple_usertype<v_test>("v_test",
sol::meta_function::construct, vfactories,
sol::call_constructor, vfactories);
lua.new_simple_usertype<thing>("thing");
lua.safe_script("things = {thing.new(), thing.new()}");
SECTION("new") {
{
auto result = lua.safe_script("a = v_test.new(things)", sol::script_pass_on_error);
REQUIRE(result.valid());
}
}
SECTION("call_constructor") {
{
auto result = lua.safe_script("b = v_test(things)", sol::script_pass_on_error);
REQUIRE(result.valid());
}
}
}
TEST_CASE("simple_usertype/no_constructor", "make sure simple usertype errors when no-constructor types are called") {
struct thing {};
SECTION("new no_constructor") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_simple_usertype<thing>("thing",
sol::meta_function::construct, sol::no_constructor);
auto result = lua.safe_script("a = thing.new()", sol::script_pass_on_error);
REQUIRE_FALSE(result.valid());
}
SECTION("call no_constructor") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_simple_usertype<thing>("thing",
sol::call_constructor, sol::no_constructor);
auto result = lua.safe_script("a = thing()", sol::script_pass_on_error);
REQUIRE_FALSE(result.valid());
}
}
TEST_CASE("simple_usertype/missing key", "make sure a missing key returns nil") {
struct thing {};
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_simple_usertype<thing>("thing");
{
auto result = lua.safe_script("print(thing.missingKey)", sol::script_pass_on_error);
REQUIRE(result.valid());
}
}
TEST_CASE("simple_usertype/runtime extensibility", "Check if usertypes are runtime extensible") {
struct thing {
int v = 20;
int func(int a) {
return a;
}
};
int val = 0;
SECTION("just functions") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_simple_usertype<thing>("thing",
"func", &thing::func);
lua.safe_script(R"(
t = thing.new()
)");
{
auto result = lua.safe_script(R"(
t.runtime_func = function (a)
return a + 50
end
)",
sol::script_pass_on_error);
REQUIRE_FALSE(result.valid());
}
{
auto result = lua.safe_script(R"(
function t:runtime_func(a)
return a + 52
end
)",
sol::script_pass_on_error);
REQUIRE_FALSE(result.valid());
}
lua.safe_script("val = t:func(2)");
val = lua["val"];
REQUIRE(val == 2);
REQUIRE_NOTHROW([&lua]() {
lua.safe_script(R"(
function thing:runtime_func(a)
return a + 1
end
)");
}());
lua.safe_script("val = t:runtime_func(2)");
val = lua["val"];
REQUIRE(val == 3);
}
SECTION("with variable") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_simple_usertype<thing>("thing",
"func", &thing::func,
"v", &thing::v);
lua.safe_script(R"(
t = thing.new()
)");
{
auto result = lua.safe_script(R"(
t.runtime_func = function (a)
return a + 50
end
)",
sol::script_pass_on_error);
REQUIRE_FALSE(result.valid());
}
{
auto result = lua.safe_script(R"(
function t:runtime_func(a)
return a + 52
end
)", sol::script_pass_on_error);
REQUIRE_FALSE(result.valid());
}
lua.safe_script("val = t:func(2)");
val = lua["val"];
REQUIRE(val == 2);
REQUIRE_NOTHROW([&lua]() {
lua.safe_script(R"(
function thing:runtime_func(a)
return a + 1
end
)");
}());
lua.safe_script("val = t:runtime_func(2)");
val = lua["val"];
REQUIRE(val == 3);
}
}
TEST_CASE("simple_usertype/runtime replacement", "ensure that functions can be properly replaced at runtime for non-indexed things") {
struct heart_base_t {};
struct heart_t : heart_base_t {
void func() {
}
};
SECTION("plain") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_simple_usertype<heart_t>("a");
REQUIRE_NOTHROW([&lua]() {
lua.safe_script("obj = a.new()");
lua.safe_script("function a:heartbeat () print('arf') return 1 end");
lua.safe_script("v1 = obj:heartbeat()");
lua.safe_script("function a:heartbeat () print('bark') return 2 end");
lua.safe_script("v2 = obj:heartbeat()");
lua.safe_script("a.heartbeat = function(self) print('woof') return 3 end");
lua.safe_script("v3 = obj:heartbeat()");
}());
int v1 = lua["v1"];
int v2 = lua["v2"];
int v3 = lua["v3"];
REQUIRE(v1 == 1);
REQUIRE(v2 == 2);
REQUIRE(v3 == 3);
}
SECTION("variables") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_simple_usertype<heart_t>("a",
sol::base_classes, sol::bases<heart_base_t>());
REQUIRE_NOTHROW([&lua]() {
lua.safe_script("obj = a.new()");
lua.safe_script("function a:heartbeat () print('arf') return 1 end");
lua.safe_script("v1 = obj:heartbeat()");
lua.safe_script("function a:heartbeat () print('bark') return 2 end");
lua.safe_script("v2 = obj:heartbeat()");
lua.safe_script("a.heartbeat = function(self) print('woof') return 3 end");
lua.safe_script("v3 = obj:heartbeat()");
}());
int v1 = lua["v1"];
int v2 = lua["v2"];
int v3 = lua["v3"];
REQUIRE(v1 == 1);
REQUIRE(v2 == 2);
REQUIRE(v3 == 3);
}
SECTION("methods") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_simple_usertype<heart_t>("a",
"func", &heart_t::func,
sol::base_classes, sol::bases<heart_base_t>());
REQUIRE_NOTHROW([&lua]() {
lua.safe_script("obj = a.new()");
lua.safe_script("function a:heartbeat () print('arf') return 1 end");
lua.safe_script("v1 = obj:heartbeat()");
lua.safe_script("function a:heartbeat () print('bark') return 2 end");
lua.safe_script("v2 = obj:heartbeat()");
lua.safe_script("a.heartbeat = function(self) print('woof') return 3 end");
lua.safe_script("v3 = obj:heartbeat()");
}());
int v1 = lua["v1"];
int v2 = lua["v2"];
int v3 = lua["v3"];
REQUIRE(v1 == 1);
REQUIRE(v2 == 2);
REQUIRE(v3 == 3);
}
}
TEST_CASE("simple_usertype/runtime additions with newindex", "ensure that additions when new_index is overriden don't hit the specified new_index function") {
class newindex_object {};
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_simple_usertype<newindex_object>("object",
sol::meta_function::new_index, [](newindex_object& o, sol::object key, sol::object value) {
return;
});
lua["object"]["test"] = [](newindex_object& o) {
std::cout << "test" << std::endl;
return 446;
};
auto result1 = lua.safe_script("o = object.new()", sol::script_pass_on_error);
REQUIRE(result1.valid());
auto result2 = lua.safe_script("assert(o:test() == 446)", sol::script_pass_on_error);
REQUIRE(result2.valid());
}
TEST_CASE("simple_usertype/meta key retrievals", "allow for special meta keys (__index, __newindex) to trigger methods even if overwritten directly") {
SECTION("dynamically") {
static int writes = 0;
static std::string keys[4] = {};
static int values[4] = {};
struct d_sample {
void foo(std::string k, int v) {
keys[writes] = k;
values[writes] = v;
++writes;
}
};
sol::state lua;
lua.new_simple_usertype<d_sample>("sample");
sol::table s = lua["sample"]["new"]();
s[sol::metatable_key][sol::meta_function::new_index] = &d_sample::foo;
lua["var"] = s;
lua.safe_script("var = sample.new()");
lua.safe_script("var.key = 2");
lua.safe_script("var.__newindex = 4");
lua.safe_script("var.__index = 3");
lua.safe_script("var.__call = 1");
REQUIRE(values[0] == 2);
REQUIRE(values[1] == 4);
REQUIRE(values[2] == 3);
REQUIRE(values[3] == 1);
REQUIRE(keys[0] == "key");
REQUIRE(keys[1] == "__newindex");
REQUIRE(keys[2] == "__index");
REQUIRE(keys[3] == "__call");
}
SECTION("statically") {
static int writes = 0;
static std::string keys[4] = {};
static int values[4] = {};
struct sample {
void foo(std::string k, int v) {
keys[writes] = k;
values[writes] = v;
++writes;
}
};
sol::state lua;
lua.new_simple_usertype<sample>("sample", sol::meta_function::new_index, &sample::foo);
lua.safe_script("var = sample.new()");
lua.safe_script("var.key = 2");
lua.safe_script("var.__newindex = 4");
lua.safe_script("var.__index = 3");
lua.safe_script("var.__call = 1");
REQUIRE(values[0] == 2);
REQUIRE(values[1] == 4);
REQUIRE(values[2] == 3);
REQUIRE(values[3] == 1);
REQUIRE(keys[0] == "key");
REQUIRE(keys[1] == "__newindex");
REQUIRE(keys[2] == "__index");
REQUIRE(keys[3] == "__call");
}
}
TEST_CASE("simple_usertype/static properties", "allow for static functions to get and set things as a property") {
static int b = 50;
struct test_t {
static double s_func() {
return b + 0.5;
}
static void g_func(int v) {
b = v;
}
std::size_t func() {
return 24;
}
};
test_t manager;
sol::state lua;
lua.new_simple_usertype<test_t>("test",
"f", std::function<std::size_t()>(std::bind(std::mem_fn(&test_t::func), &manager)),
"g", sol::property(&test_t::s_func, &test_t::g_func));
lua.safe_script("v1 = test.f()");
lua.safe_script("v2 = test.g");
lua.safe_script("test.g = 60");
lua.safe_script("v2a = test.g");
int v1 = lua["v1"];
REQUIRE(v1 == 24);
double v2 = lua["v2"];
REQUIRE(v2 == 50.5);
double v2a = lua["v2a"];
REQUIRE(v2a == 60.5);
}
TEST_CASE("simple_usertype/indexing", "make sure simple usertypes can be indexed/new_indexed properly without breaking") {
static int val = 0;
class indexing_test {
public:
indexing_test() {
val = 0;
}
sol::object getter(const std::string& name, sol::this_state _s) {
REQUIRE(name == "a");
return sol::make_object(_s, 2);
}
void setter(std::string name, sol::object n) {
REQUIRE(name == "unknown");
val = n.as<int>();
}
int hi() {
std::cout << "hi" << std::endl;
return 25;
}
};
SECTION("no runtime additions") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_simple_usertype<indexing_test>("test",
sol::meta_function::index, &indexing_test::getter,
sol::meta_function::new_index, &indexing_test::setter);
lua.safe_script(R"(
local t = test.new()
v = t.a
print(v)
t.unknown = 50
)");
int v = lua["v"];
REQUIRE(v == 2);
REQUIRE(val == 50);
}
SECTION("runtime additions") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_simple_usertype<indexing_test>("test",
sol::meta_function::index, &indexing_test::getter,
sol::meta_function::new_index, &indexing_test::setter);
lua["test"]["hi"] = [](indexing_test& _self) -> int { return _self.hi(); };
lua.safe_script(R"(
local t = test.new()
v = t.a;
print(v)
t.unknown = 50
u = t:hi()
)");
int v = lua["v"];
int u = lua["u"];
REQUIRE(v == 2);
REQUIRE(u == 25);
REQUIRE(val == 50);
}
}
TEST_CASE("simple_usertype/basic type information", "check that we can query some basic type information") {
struct my_thing {};
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_simple_usertype<my_thing>("my_thing");
lua.safe_script("obj = my_thing.new()");
lua.safe_script("assert(my_thing.__type.is(obj))");
lua.safe_script("assert(not my_thing.__type.is(1))");
lua.safe_script("assert(not my_thing.__type.is(\"not a thing\"))");
lua.safe_script("print(my_thing.__type.name)");
lua.safe_script("assert(obj.__type.is(obj))");
lua.safe_script("assert(not obj.__type.is(1))");
lua.safe_script("assert(not obj.__type.is(\"not a thing\"))");
lua.safe_script("print(obj.__type.name)");
lua.safe_script("assert(getmetatable(my_thing).__type.is(obj))");
lua.safe_script("assert(not getmetatable(my_thing).__type.is(1))");
lua.safe_script("assert(not getmetatable(my_thing).__type.is(\"not a thing\"))");
lua.safe_script("print(getmetatable(my_thing).__type.name)");
lua.safe_script("assert(getmetatable(obj).__type.is(obj))");
lua.safe_script("assert(not getmetatable(obj).__type.is(1))");
lua.safe_script("assert(not getmetatable(obj).__type.is(\"not a thing\"))");
lua.safe_script("print(getmetatable(obj).__type.name)");
}
+67
View File
@@ -0,0 +1,67 @@
// sol2
// The MIT License (MIT)
// Copyright (c) 2013-2018 Rapptz, ThePhD and contributors
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#ifndef SOL_TEST_SOL_HPP
#define SOL_TEST_SOL_HPP
#ifndef SOL_CHECK_ARGUMENTS
#define SOL_CHECK_ARGUMENTS 1
#endif // SOL_CHECK_ARGUMENTS
#ifndef SOL_ENABLE_INTEROP
#define SOL_ENABLE_INTEROP 1
#endif // SOL_ENABLE_INTEROP
// Can't activate until all script/safe_script calls are vetted...
/*#ifndef SOL_DEFAULT_PASS_ON_ERROR
#define SOL_DEFAULT_PASS_ON_ERROR 1
#endif // SOL_DEFAULT_PASS_ON_ERROR
*/
#include <iostream>
#include <cstdlib>
#ifdef TEST_SINGLE
#include <sol_forward.hpp>
#endif // Single
#include <sol.hpp>
struct test_stack_guard {
lua_State* L;
int& begintop;
int& endtop;
test_stack_guard(lua_State* L, int& begintop, int& endtop)
: L(L), begintop(begintop), endtop(endtop) {
begintop = lua_gettop(L);
}
void check() {
if (begintop != endtop) {
std::abort();
}
}
~test_stack_guard() {
endtop = lua_gettop(L);
}
};
#endif // SOL_TEST_SOL_HPP
+706
View File
@@ -0,0 +1,706 @@
// sol2
// The MIT License (MIT)
// Copyright (c) 2013-2018 Rapptz, ThePhD and contributors
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "test_sol.hpp"
#include <catch.hpp>
#include <iostream>
#include <fstream>
#include <chrono>
#include <thread>
#include <mutex>
#include <atomic>
template <typename Name, typename Data>
void write_file_attempt(Name&& filename, Data&& data) {
bool success = false;
for (std::size_t i = 0; i < 20; ++i) {
try {
std::ofstream file(std::forward<Name>(filename), std::ios::out);
file.exceptions(std::ios_base::badbit | std::ios_base::failbit);
file << std::forward<Data>(data) << std::endl;
file.close();
}
catch (const std::exception&) {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
continue;
}
success = true;
break;
}
if (!success) {
throw std::runtime_error("cannot open file or write out");
}
}
struct write_file_attempt_object {
template <typename... Args>
void operator()(Args&&... args) {
write_file_attempt(std::forward<Args>(args)...);
}
};
struct string_reader_load {
const std::string& str;
std::size_t reads;
string_reader_load(const std::string& s)
: str(s), reads(0) {
}
};
const char* string_reader(lua_State* L, void* vpstr, size_t* sz) {
(void)L;
string_reader_load& srl = *static_cast<string_reader_load*>(vpstr);
if (srl.reads++ > 0) {
*sz = 0;
return NULL;
}
*sz = static_cast<size_t>(srl.str.size());
return srl.str.c_str();
}
TEST_CASE("state/require_file", "opening files as 'requires'") {
static const char file_require_file[] = "./tmp_thingy.lua";
static const char file_require_file_user[] = "./tmp_thingy_user.lua";
static const char require_file_code[] = "return { modfunc = function () return 221 end }";
static const char require_file_user_code[] = "return { modfunc = function () return foo.new(221) end }";
static std::atomic<int> finished(0);
static std::once_flag flag_file = {}, flag_file_user = {};
std::call_once(flag_file, write_file_attempt_object(), file_require_file, require_file_code);
std::call_once(flag_file_user, write_file_attempt_object(), file_require_file_user, require_file_user_code);
auto clean_files = []() {
if (finished.fetch_add(1) < 1) {
return;
}
std::remove(file_require_file);
std::remove(file_require_file_user);
};
SECTION("with usertypes") {
struct foo {
foo(int bar)
: bar(bar) {
}
const int bar;
};
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_usertype<foo>("foo",
sol::constructors<sol::types<int>>{},
"bar", &foo::bar);
const sol::table thingy1 = lua.require_file("thingy", file_require_file_user);
REQUIRE(thingy1.valid());
const foo foo_v = thingy1["modfunc"]();
int val1 = foo_v.bar;
REQUIRE(val1 == 221);
clean_files();
}
SECTION("simple") {
sol::state lua;
lua.open_libraries(sol::lib::base);
const sol::table thingy1 = lua.require_file("thingy", file_require_file);
const sol::table thingy2 = lua.require_file("thingy", file_require_file);
REQUIRE(thingy1.valid());
REQUIRE(thingy2.valid());
int val1 = thingy1["modfunc"]();
int val2 = thingy2["modfunc"]();
REQUIRE(val1 == 221);
REQUIRE(val2 == 221);
// must have loaded the same table
REQUIRE((thingy1 == thingy2));
clean_files();
}
}
TEST_CASE("state/require_script", "opening strings as 'requires' clauses") {
std::string code = "return { modfunc = function () return 221 end }";
sol::state lua;
sol::stack_guard sg(lua);
sol::table thingy1 = lua.require_script("thingy", code);
sol::table thingy2 = lua.require_script("thingy", code);
int val1 = thingy1["modfunc"]();
int val2 = thingy2["modfunc"]();
REQUIRE(val1 == 221);
REQUIRE(val2 == 221);
// must have loaded the same table
REQUIRE((thingy1 == thingy2));
}
TEST_CASE("state/require", "require using a function") {
struct open {
static int open_func(lua_State* L) {
sol::state_view lua = L;
return sol::stack::push(L, lua.create_table_with("modfunc", sol::as_function([]() { return 221; })));
}
};
sol::state lua;
sol::stack_guard sg(lua);
sol::table thingy1 = lua.require("thingy", open::open_func);
sol::table thingy2 = lua.require("thingy", open::open_func);
int val1 = thingy1["modfunc"]();
int val2 = thingy2["modfunc"]();
REQUIRE(val1 == 221);
REQUIRE(val2 == 221);
// THIS IS ONLY REQUIRED IN LUA 5.3, FOR SOME REASON
// must have loaded the same table
// REQUIRE(thingy1 == thingy2);
}
TEST_CASE("state/multi require", "make sure that requires transfers across hand-rolled script implementation and standard requiref") {
struct open {
static int open_func(lua_State* L) {
sol::state_view lua = L;
return sol::stack::push(L, lua.create_table_with("modfunc", sol::as_function([]() { return 221; })));
}
};
std::string code = "return { modfunc = function () return 221 end }";
sol::state lua;
sol::table thingy1 = lua.require("thingy", open::open_func);
sol::table thingy2 = lua.require("thingy", open::open_func);
sol::table thingy3 = lua.require_script("thingy", code);
int val1 = thingy1["modfunc"]();
int val2 = thingy2["modfunc"]();
int val3 = thingy3["modfunc"]();
REQUIRE(val1 == 221);
REQUIRE(val2 == 221);
REQUIRE(val3 == 221);
// must have loaded the same table
// Lua is not obliged to give a shit. Thanks, Lua
//REQUIRE(thingy1 == thingy2);
// But we care, thankfully
//REQUIRE(thingy1 == thingy3);
REQUIRE((thingy2 == thingy3));
}
TEST_CASE("state/require-safety", "make sure unrelated modules aren't harmed in using requires") {
sol::state lua;
lua.open_libraries();
std::string t1 = lua.safe_script(R"(require 'io'
return 'test1')");
sol::object ot2 = lua.require_script("test2", R"(require 'io'
return 'test2')");
std::string t2 = ot2.as<std::string>();
std::string t3 = lua.safe_script(R"(require 'io'
return 'test3')");
REQUIRE(t1 == "test1");
REQUIRE(t2 == "test2");
REQUIRE(t3 == "test3");
}
TEST_CASE("state/leak check", "make sure there are no humongous memory leaks in iteration") {
#if 0
sol::state lua;
lua.safe_script(R"(
record = {}
for i=1,256 do
record[i] = i
end
function run()
for i=1,25000 do
fun(record)
end
end
function run2()
for i=1,50000 do
fun(record)
end
end
)");
lua["fun"] = [](const sol::table &t) {
//removing the for loop fixes the memory leak
auto b = t.begin();
auto e = t.end();
for (; b != e; ++b) {
}
};
size_t beforewarmup = lua.memory_used();
lua["run"]();
size_t beforerun = lua.memory_used();
lua["run"]();
size_t afterrun = lua.memory_used();
lua["run2"]();
size_t afterrun2 = lua.memory_used();
// Less memory used before the warmup
REQUIRE(beforewarmup <= beforerun);
// Iteration size and such does not bloat or affect memory
// (these are weak checks but they'll warn us nonetheless if something goes wrong)
REQUIRE(beforerun == afterrun);
REQUIRE(afterrun == afterrun2);
#else
REQUIRE(true);
#endif
}
TEST_CASE("state/script returns", "make sure script returns are done properly") {
std::string script =
R"(
local example =
{
str = "this is a string",
num = 1234,
func = function(self)
print(self.str)
return "fstr"
end
}
return example;
)";
auto bar = [&script](sol::this_state l) {
sol::state_view lua = l;
sol::table data = lua.safe_script(script);
std::string str = data["str"];
int num = data["num"];
std::string fstr = data["func"](data);
REQUIRE(str == "this is a string");
REQUIRE(fstr == "fstr");
REQUIRE(num == 1234);
};
auto foo = [&script](int, sol::this_state l) {
sol::state_view lua = l;
sol::table data = lua.safe_script(script);
std::string str = data["str"];
int num = data["num"];
std::string fstr = data["func"](data);
REQUIRE(str == "this is a string");
REQUIRE(fstr == "fstr");
REQUIRE(num == 1234);
};
auto bar2 = [&script](sol::this_state l) {
sol::state_view lua = l;
sol::table data = lua.do_string(script);
std::string str = data["str"];
int num = data["num"];
std::string fstr = data["func"](data);
REQUIRE(str == "this is a string");
REQUIRE(fstr == "fstr");
REQUIRE(num == 1234);
};
auto foo2 = [&script](int, sol::this_state l) {
sol::state_view lua = l;
sol::table data = lua.do_string(script);
std::string str = data["str"];
int num = data["num"];
std::string fstr = data["func"](data);
REQUIRE(str == "this is a string");
REQUIRE(fstr == "fstr");
REQUIRE(num == 1234);
};
sol::state lua;
sol::stack_guard sg(lua);
lua.open_libraries();
lua.set_function("foo", foo);
lua.set_function("foo2", foo2);
lua.set_function("bar", bar);
lua.set_function("bar2", bar2);
lua.safe_script("bar() bar2() foo(1) foo2(1)");
}
TEST_CASE("state/copy and move", "ensure state can be properly copied and moved") {
sol::state lua;
lua["a"] = 1;
sol::state lua2(std::move(lua));
int a2 = lua2["a"];
REQUIRE(a2 == 1);
lua = std::move(lua2);
int a = lua["a"];
REQUIRE(a == 1);
}
TEST_CASE("state/requires-reload", "ensure that reloading semantics do not cause a crash") {
sol::state lua;
sol::stack_guard sg(lua);
lua.open_libraries();
lua.safe_script("require 'io'\nreturn 'test1'");
lua.require_script("test2", "require 'io'\nreturn 'test2'");
lua.safe_script("require 'io'\nreturn 'test3'");
}
TEST_CASE("state/script, do, and load", "test success and failure cases for loading and running scripts") {
const static std::string bad_syntax = "weird\n%$@symb\nols";
static const char file_bad_syntax[] = "./temp.bad_syntax.lua";
const static std::string bad_runtime = "bad.code = 20";
static const char file_bad_runtime[] = "./temp.bad_runtime.lua";
const static std::string good = "a = 21\nreturn a";
static const char file_good[] = "./temp.good.lua";
static std::once_flag flag_file_bs = {}, flag_file_br = {}, flag_file_g = {};
static std::atomic<int> finished(0);
std::call_once(flag_file_bs, write_file_attempt_object(), file_bad_syntax, bad_syntax);
std::call_once(flag_file_br, write_file_attempt_object(), file_bad_runtime, bad_runtime);
std::call_once(flag_file_g, write_file_attempt_object(), file_good, good);
auto clean_files = []() {
if (finished.fetch_add(1) < 14) {
return;
}
std::remove(file_bad_syntax);
std::remove(file_bad_runtime);
std::remove(file_good);
};
SECTION("script") {
sol::state lua;
sol::stack_guard sg(lua);
int ar = lua.safe_script(good);
int a = lua["a"];
REQUIRE(a == 21);
REQUIRE(ar == 21);
clean_files();
}
SECTION("unsafe_script") {
sol::state lua;
sol::stack_guard sg(lua);
int ar = lua.unsafe_script(good);
int a = lua["a"];
REQUIRE(a == 21);
REQUIRE(ar == 21);
clean_files();
}
SECTION("script-handler") {
sol::state lua;
sol::stack_guard sg(lua);
auto errbs = lua.safe_script(bad_syntax, sol::script_pass_on_error);
REQUIRE(!errbs.valid());
auto errbr = lua.safe_script(bad_runtime, sol::script_pass_on_error);
REQUIRE(!errbr.valid());
auto result = lua.safe_script(good, sol::script_pass_on_error);
int a = lua["a"];
int ar = result;
REQUIRE(result.valid());
REQUIRE(a == 21);
REQUIRE(ar == 21);
clean_files();
}
SECTION("do_string") {
sol::state lua;
sol::stack_guard sg(lua);
auto errbs = lua.do_string(bad_syntax);
REQUIRE(!errbs.valid());
auto errbr = lua.do_string(bad_runtime);
REQUIRE(!errbr.valid());
auto result = lua.do_string(good);
int a = lua["a"];
int ar = result;
REQUIRE(result.valid());
REQUIRE(a == 21);
REQUIRE(ar == 21);
clean_files();
}
SECTION("load_string") {
sol::state lua;
sol::stack_guard sg(lua);
auto errbsload = lua.load(bad_syntax);
REQUIRE(!errbsload.valid());
sol::load_result errbrload = lua.load(bad_runtime);
REQUIRE(errbrload.valid());
sol::protected_function errbrpf = errbrload;
auto errbr = errbrpf();
REQUIRE(!errbr.valid());
sol::load_result resultload = lua.load(good);
REQUIRE(resultload.valid());
sol::protected_function resultpf = resultload;
auto result = resultpf();
int a = lua["a"];
int ar = result;
REQUIRE(result.valid());
REQUIRE(a == 21);
REQUIRE(ar == 21);
clean_files();
}
SECTION("load") {
sol::state lua;
sol::stack_guard sg(lua);
string_reader_load bssrl(bad_syntax);
void* vpbssrl = static_cast<void*>(&bssrl);
auto errbsload = lua.load(&string_reader, vpbssrl, bad_syntax);
REQUIRE(!errbsload.valid());
string_reader_load brsrl(bad_runtime);
void* vpbrsrl = static_cast<void*>(&brsrl);
sol::load_result errbrload = lua.load(&string_reader, vpbrsrl, bad_runtime);
REQUIRE(errbrload.valid());
sol::protected_function errbrpf = errbrload;
auto errbr = errbrpf();
REQUIRE(!errbr.valid());
string_reader_load gsrl(good);
void* vpgsrl = static_cast<void*>(&gsrl);
sol::load_result resultload = lua.load(&string_reader, vpgsrl, good);
REQUIRE(resultload.valid());
sol::protected_function resultpf = resultload;
auto result = resultpf();
int a = lua["a"];
int ar = result;
REQUIRE(result.valid());
REQUIRE(a == 21);
REQUIRE(ar == 21);
clean_files();
}
SECTION("load_string (text)") {
sol::state lua;
sol::stack_guard sg(lua);
auto errbsload = lua.load(bad_syntax, bad_syntax, sol::load_mode::text);
REQUIRE(!errbsload.valid());
sol::load_result errbrload = lua.load(bad_runtime, bad_runtime, sol::load_mode::text);
REQUIRE(errbrload.valid());
sol::protected_function errbrpf = errbrload;
auto errbr = errbrpf();
REQUIRE(!errbr.valid());
sol::load_result resultload = lua.load(good, good, sol::load_mode::text);
REQUIRE(resultload.valid());
sol::protected_function resultpf = resultload;
auto result = resultpf();
int a = lua["a"];
int ar = result;
REQUIRE(result.valid());
REQUIRE(a == 21);
REQUIRE(ar == 21);
clean_files();
}
SECTION("load (text)") {
sol::state lua;
sol::stack_guard sg(lua);
string_reader_load bssrl(bad_syntax);
void* vpbssrl = static_cast<void*>(&bssrl);
auto errbsload = lua.load(&string_reader, vpbssrl, bad_syntax, sol::load_mode::text);
REQUIRE(!errbsload.valid());
string_reader_load brsrl(bad_runtime);
void* vpbrsrl = static_cast<void*>(&brsrl);
sol::load_result errbrload = lua.load(&string_reader, vpbrsrl, bad_runtime, sol::load_mode::text);
REQUIRE(errbrload.valid());
sol::protected_function errbrpf = errbrload;
auto errbr = errbrpf();
REQUIRE(!errbr.valid());
string_reader_load gsrl(good);
void* vpgsrl = static_cast<void*>(&gsrl);
sol::load_result resultload = lua.load(&string_reader, vpgsrl, good, sol::load_mode::text);
REQUIRE(resultload.valid());
sol::protected_function resultpf = resultload;
auto result = resultpf();
int a = lua["a"];
int ar = result;
REQUIRE(result.valid());
REQUIRE(a == 21);
REQUIRE(ar == 21);
clean_files();
}
SECTION("script_file") {
sol::state lua;
sol::stack_guard sg(lua);
int ar = lua.safe_script_file(file_good);
int a = lua["a"];
REQUIRE(a == 21);
REQUIRE(ar == 21);
clean_files();
}
SECTION("unsafe_script_file") {
sol::state lua;
sol::stack_guard sg(lua);
int ar = lua.unsafe_script_file(file_good);
int a = lua["a"];
REQUIRE(a == 21);
REQUIRE(ar == 21);
clean_files();
}
SECTION("script_file-handler") {
sol::state lua;
sol::stack_guard sg(lua);
auto errbs = lua.safe_script_file(file_bad_syntax, sol::script_pass_on_error);
REQUIRE(!errbs.valid());
auto errbr = lua.safe_script_file(file_bad_runtime, sol::script_pass_on_error);
REQUIRE(!errbr.valid());
auto result = lua.safe_script_file(file_good, sol::script_pass_on_error);
int a = lua["a"];
int ar = result;
REQUIRE(result.valid());
REQUIRE(a == 21);
REQUIRE(ar == 21);
clean_files();
}
SECTION("safe_script_file-handler") {
sol::state lua;
sol::stack_guard sg(lua);
auto errbs = lua.safe_script_file(file_bad_syntax, sol::script_pass_on_error);
REQUIRE(!errbs.valid());
auto errbr = lua.safe_script_file(file_bad_runtime, sol::script_pass_on_error);
REQUIRE(!errbr.valid());
auto result = lua.safe_script_file(file_good, sol::script_pass_on_error);
int a = lua["a"];
int ar = result;
REQUIRE(result.valid());
REQUIRE(a == 21);
REQUIRE(ar == 21);
clean_files();
}
SECTION("do_file") {
sol::state lua;
sol::stack_guard sg(lua);
auto errbs = lua.do_file(file_bad_syntax);
REQUIRE(!errbs.valid());
auto errbr = lua.do_file(file_bad_runtime);
REQUIRE(!errbr.valid());
auto result = lua.do_file(file_good);
int a = lua["a"];
int ar = result;
REQUIRE(result.valid());
REQUIRE(a == 21);
REQUIRE(ar == 21);
clean_files();
}
SECTION("load_file") {
sol::state lua;
sol::stack_guard sg(lua);
auto errbsload = lua.load_file(file_bad_syntax);
REQUIRE(!errbsload.valid());
sol::load_result errbrload = lua.load_file(file_bad_runtime);
REQUIRE(errbrload.valid());
sol::protected_function errbrpf = errbrload;
auto errbr = errbrpf();
REQUIRE(!errbr.valid());
sol::load_result resultload = lua.load_file(file_good);
REQUIRE(resultload.valid());
sol::protected_function resultpf = resultload;
auto result = resultpf();
int a = lua["a"];
int ar = result;
REQUIRE(result.valid());
REQUIRE(a == 21);
REQUIRE(ar == 21);
clean_files();
}
SECTION("load_file (text)") {
sol::state lua;
sol::stack_guard sg(lua);
auto errbsload = lua.load_file(file_bad_syntax, sol::load_mode::text);
REQUIRE(!errbsload.valid());
sol::load_result errbrload = lua.load_file(file_bad_runtime, sol::load_mode::text);
REQUIRE(errbrload.valid());
sol::protected_function errbrpf = errbrload;
auto errbr = errbrpf();
REQUIRE(!errbr.valid());
sol::load_result resultload = lua.load_file(file_good, sol::load_mode::text);
REQUIRE(resultload.valid());
sol::protected_function resultpf = resultload;
auto result = resultpf();
int a = lua["a"];
int ar = result;
REQUIRE(result.valid());
REQUIRE(a == 21);
REQUIRE(ar == 21);
clean_files();
}
}
TEST_CASE("state/script return converts", "make sure that script return values are convertable from one to another") {
sol::state lua;
sol::protected_function_result r1 = lua.unsafe_script("return 2");
sol::unsafe_function_result r2 = lua.safe_script("return 3");
int v1 = r1;
int v2 = r2;
REQUIRE(v1 == 2);
REQUIRE(v2 == 3);
}
TEST_CASE("state/script function returns", "make sure that returned functions are converitble from a result to a function") {
SECTION("from result move constructor") {
sol::state lua;
sol::protected_function pf = lua.safe_script("return function () return 2 end", sol::script_pass_on_error);
REQUIRE(pf.valid());
int v1 = pf();
REQUIRE(v1 == 2);
}
SECTION("from result operator=") {
sol::state lua;
sol::protected_function_result r1 = lua.safe_script("return function () return 1 end", sol::script_pass_on_error);
REQUIRE(r1.valid());
sol::protected_function pf = r1;
int v1 = pf();
REQUIRE(v1 == 1);
}
}
+83
View File
@@ -0,0 +1,83 @@
// sol2
// The MIT License (MIT)
// Copyright (c) 2013-2018 Rapptz, ThePhD and contributors
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#define SOL_CHECK_ARGUMENTS 1
#define SOL_ENABLE_INTEROP 1
#include <sol.hpp>
#include <catch.hpp>
TEST_CASE("storage/registry construction", "ensure entries from the registry can be retrieved") {
const auto& code = R"(
function f()
return 2
end
)";
sol::state lua;
{
auto r = lua.safe_script(code, sol::script_pass_on_error);
REQUIRE(r.valid());
}
sol::function f = lua["f"];
sol::reference r = lua["f"];
sol::function regf(lua, f);
sol::reference regr(lua, sol::ref_index(f.registry_index()));
bool isequal = f == r;
REQUIRE(isequal);
isequal = f == regf;
REQUIRE(isequal);
isequal = f == regr;
REQUIRE(isequal);
}
TEST_CASE("storage/registry construction empty", "ensure entries from the registry can be retrieved") {
sol::state lua;
sol::function f = lua["f"];
sol::reference r = lua["f"];
sol::function regf(lua, f);
sol::reference regr(lua, sol::ref_index(f.registry_index()));
bool isequal = f == r;
REQUIRE(isequal);
isequal = f == regf;
REQUIRE(isequal);
isequal = f == regr;
REQUIRE(isequal);
}
TEST_CASE("storage/main thread", "ensure round-tripping and pulling out thread data even on 5.1 with a backup works") {
sol::state lua;
{
sol::stack_guard g(lua);
lua_State* orig = lua;
lua_State* ts = sol::main_thread(lua, lua);
REQUIRE(ts == orig);
}
{
sol::stack_guard g(lua);
lua_State* orig = lua;
lua_State* ts = sol::main_thread(lua);
REQUIRE(ts == orig);
}
}
+179
View File
@@ -0,0 +1,179 @@
// sol2
// The MIT License (MIT)
// Copyright (c) 2013-2018 Rapptz, ThePhD and contributors
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "test_sol.hpp"
#include <catch.hpp>
#include <iostream>
struct test {};
template <typename T>
struct test_t {};
namespace muh_namespace {
struct ns_test {};
namespace {
struct ns_anon_test {};
} // namespace
} // namespace muh_namespace
TEST_CASE("stack/strings", "test that strings can be roundtripped") {
sol::state lua;
static const char utf8str[] = "\xF0\x9F\x8D\x8C\x20\xE6\x99\xA5\x20\x46\x6F\x6F\x20\xC2\xA9\x20\x62\x61\x72\x20\xF0\x9D\x8C\x86\x20\x62\x61\x7A\x20\xE2\x98\x83\x20\x71\x75\x78";
static const char16_t utf16str[] = { 0xD83C, 0xDF4C, 0x20, 0x6665, 0x20, 0x46, 0x6F, 0x6F, 0x20, 0xA9, 0x20, 0x62, 0x61, 0x72, 0x20, 0xD834, 0xDF06, 0x20, 0x62, 0x61, 0x7A, 0x20, 0x2603, 0x20, 0x71, 0x75, 0x78, 0x00 };
static const char32_t utf32str[] = { 0x1F34C, 0x0020, 0x6665, 0x0020, 0x0046, 0x006F, 0x006F, 0x0020, 0x00A9, 0x0020, 0x0062, 0x0061, 0x0072, 0x0020, 0x1D306, 0x0020, 0x0062, 0x0061, 0x007A, 0x0020, 0x2603, 0x0020, 0x0071, 0x0075, 0x0078, 0x00 };
#ifdef _WIN32
INFO("win32 widestr");
static const wchar_t widestr[] = { 0xD83C, 0xDF4C, 0x20, 0x6665, 0x20, 0x46, 0x6F, 0x6F, 0x20, 0xA9, 0x20, 0x62, 0x61, 0x72, 0x20, 0xD834, 0xDF06, 0x20, 0x62, 0x61, 0x7A, 0x20, 0x2603, 0x20, 0x71, 0x75, 0x78, 0x00 };
#else
INFO("non-windows widestr");
static const wchar_t widestr[] = { 0x1F34C, 0x0020, 0x6665, 0x0020, 0x0046, 0x006F, 0x006F, 0x0020, 0x00A9, 0x0020, 0x0062, 0x0061, 0x0072, 0x0020, 0x1D306, 0x0020, 0x0062, 0x0061, 0x007A, 0x0020, 0x2603, 0x0020, 0x0071, 0x0075, 0x0078, 0x00 };
#endif
static const std::string utf8str_s = utf8str;
static const std::u16string utf16str_s = utf16str;
static const std::u32string utf32str_s = utf32str;
static const std::wstring widestr_s = widestr;
INFO("sizeof(wchar_t): " << sizeof(wchar_t));
INFO("sizeof(char16_t): " << sizeof(char16_t));
INFO("sizeof(char32_t): " << sizeof(char32_t));
INFO("utf8str: " << utf8str);
INFO("utf8str_s: " << utf8str_s);
lua["utf8"] = utf8str;
lua["utf16"] = utf16str;
lua["utf32"] = utf32str;
lua["wide"] = widestr;
std::string utf8_to_utf8 = lua["utf8"];
std::string utf16_to_utf8 = lua["utf16"];
std::string utf32_to_utf8 = lua["utf32"];
std::string wide_to_utf8 = lua["wide"];
REQUIRE(utf8_to_utf8 == utf8str_s);
REQUIRE(utf16_to_utf8 == utf8str_s);
REQUIRE(utf32_to_utf8 == utf8str_s);
REQUIRE(wide_to_utf8 == utf8str_s);
std::wstring utf8_to_wide = lua["utf8"];
std::wstring utf16_to_wide = lua["utf16"];
std::wstring utf32_to_wide = lua["utf32"];
std::wstring wide_to_wide = lua["wide"];
REQUIRE(utf8_to_wide == widestr_s);
REQUIRE(utf16_to_wide == widestr_s);
REQUIRE(utf32_to_wide == widestr_s);
REQUIRE(wide_to_wide == widestr_s);
std::u16string utf8_to_utf16 = lua["utf8"];
std::u16string utf16_to_utf16 = lua["utf16"];
std::u16string utf32_to_utf16 = lua["utf32"];
std::u16string wide_to_utf16 = lua["wide"];
REQUIRE(utf8_to_utf16 == utf16str_s);
REQUIRE(utf16_to_utf16 == utf16str_s);
REQUIRE(utf32_to_utf16 == utf16str_s);
REQUIRE(wide_to_utf16 == utf16str_s);
std::u32string utf8_to_utf32 = lua["utf8"];
std::u32string utf16_to_utf32 = lua["utf16"];
std::u32string utf32_to_utf32 = lua["utf32"];
std::u32string wide_to_utf32 = lua["wide"];
REQUIRE(utf8_to_utf32 == utf32str_s);
REQUIRE(utf16_to_utf32 == utf32str_s);
REQUIRE(utf32_to_utf32 == utf32str_s);
REQUIRE(wide_to_utf32 == utf32str_s);
char32_t utf8_to_char32 = lua["utf8"];
char32_t utf16_to_char32 = lua["utf16"];
char32_t utf32_to_char32 = lua["utf32"];
char32_t wide_to_char32 = lua["wide"];
REQUIRE(utf8_to_char32 == utf32str[0]);
REQUIRE(utf16_to_char32 == utf32str[0]);
REQUIRE(utf32_to_char32 == utf32str[0]);
REQUIRE(wide_to_char32 == utf32str[0]);
char16_t utf8_to_char16 = lua["utf8"];
char16_t utf16_to_char16 = lua["utf16"];
char16_t utf32_to_char16 = lua["utf32"];
char16_t wide_to_char16 = lua["wide"];
REQUIRE(utf8_to_char16 == utf16str[0]);
REQUIRE(utf16_to_char16 == utf16str[0]);
REQUIRE(utf32_to_char16 == utf16str[0]);
REQUIRE(wide_to_char16 == utf16str[0]);
}
TEST_CASE("detail/demangling", "test some basic demangling cases") {
std::string teststr = sol::detail::short_demangle<test>();
std::string nsteststr = sol::detail::short_demangle<muh_namespace::ns_test>();
std::string nsateststr = sol::detail::short_demangle<muh_namespace::ns_anon_test>();
REQUIRE(teststr == "test");
REQUIRE(nsteststr == "ns_test");
REQUIRE(nsateststr == "ns_anon_test");
}
TEST_CASE("object/string-pushers", "test some basic string pushers with in_place constructor") {
sol::state lua;
sol::object ocs(lua, sol::in_place, "bark\0bark", 9);
sol::object os(lua, sol::in_place_type<std::string>, std::string("bark\0bark", 9), 8);
sol::object osv(lua, sol::in_place_type<sol::string_view>, sol::string_view("woofwoof", 8), 8);
bool test1 = ocs.as<std::string>() == std::string("bark\0bark", 9);
bool test2 = os.as<std::string>() == std::string("bark\0bar", 8);
bool test3 = osv.as<std::string>() == std::string("woofwoof", 8);
REQUIRE(ocs.get_type() == sol::type::string);
REQUIRE(ocs.is<std::string>());
REQUIRE(ocs.is<sol::string_view>());
REQUIRE(os.get_type() == sol::type::string);
REQUIRE(os.is<std::string>());
REQUIRE(os.is<sol::string_view>());
REQUIRE(osv.get_type() == sol::type::string);
REQUIRE(osv.is<std::string>());
REQUIRE(osv.is<sol::string_view>());
REQUIRE(test1);
REQUIRE(test2);
REQUIRE(test3);
}
TEST_CASE("strings/non const c strings", "push non const qualified c strings as strings") {
sol::state lua;
char cbark[] = "bark";
char* bark = cbark;
lua["bark"] = bark;
sol::type t = lua["bark"].get_type();
std::string lbark = lua["bark"];
REQUIRE((t == sol::type::string));
REQUIRE((bark == std::string("bark")));
}
+638
View File
@@ -0,0 +1,638 @@
// sol2
// The MIT License (MIT)
// Copyright (c) 2013-2018 Rapptz, ThePhD and contributors
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "test_sol.hpp"
#include <catch.hpp>
#include <iostream>
#include <algorithm>
#include <numeric>
#include <vector>
std::string free_function() {
INFO("free_function()");
return "test";
}
struct object {
std::string operator()() {
INFO("member_test()");
return "test";
}
};
int plop_xyz(int x, int y, std::string z) {
INFO(x << " " << y << " " << z);
return 11;
}
TEST_CASE("tables/as enums", "Making sure enums can be put in and gotten out as values") {
enum direction {
up,
down,
left,
right
};
sol::state lua;
lua.open_libraries(sol::lib::base);
lua["direction"] = lua.create_table_with(
"up", direction::up,
"down", direction::down,
"left", direction::left,
"right", direction::right);
sol::object obj = lua["direction"]["up"];
bool isdir = obj.is<direction>();
REQUIRE(isdir);
auto dir = obj.as<direction>();
REQUIRE(dir == direction::up);
}
TEST_CASE("tables/as enum classes", "Making sure enums can be put in and gotten out as values") {
enum class direction {
up,
down,
left,
right
};
sol::state lua;
lua.open_libraries(sol::lib::base);
lua["direction"] = lua.create_table_with(
"up", direction::up,
"down", direction::down,
"left", direction::left,
"right", direction::right);
sol::object obj = lua["direction"]["up"];
bool isdir = obj.is<direction>();
REQUIRE(isdir);
auto dir = obj.as<direction>();
REQUIRE(dir == direction::up);
}
TEST_CASE("tables/cleanup", "make sure tables leave the stack balanced") {
sol::state lua;
lua.open_libraries();
auto f = [] { return 5; };
for (int i = 0; i < 30; i++) {
std::string name = std::string("init") + std::to_string(i);
int top = lua_gettop(lua);
lua[name] = f;
int aftertop = lua_gettop(lua);
REQUIRE(aftertop == top);
int val = lua[name]();
REQUIRE(val == 5);
}
}
TEST_CASE("tables/nested cleanup", "make sure tables leave the stack balanced") {
sol::state lua;
lua.open_libraries();
lua.safe_script("A={}");
auto f = [] { return 5; };
for (int i = 0; i < 30; i++) {
std::string name = std::string("init") + std::to_string(i);
int top = lua_gettop(lua);
auto A = lua["A"];
int beforetop = lua_gettop(lua);
REQUIRE(beforetop == top);
A[name] = f;
int aftertop = lua_gettop(lua);
REQUIRE(aftertop == top);
int val = A[name]();
REQUIRE(val == 5);
}
}
TEST_CASE("tables/new_enum", "Making sure enums can be put in and gotten out as values") {
enum class direction {
up,
down,
left,
right
};
SECTION("variadics") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_enum("direction",
"up", direction::up,
"down", direction::down,
"left", direction::left,
"right", direction::right);
direction d = lua["direction"]["left"];
REQUIRE(d == direction::left);
auto result = lua.safe_script("direction.left = 50", sol::script_pass_on_error);
REQUIRE_FALSE(result.valid());
d = lua["direction"]["left"];
REQUIRE(d == direction::left);
}
SECTION("initializer_list") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_enum<direction>("direction", { { "up", direction::up }, { "down", direction::down }, { "left", direction::left }, { "right", direction::right } });
direction d = lua["direction"]["left"];
REQUIRE(d == direction::left);
auto result = lua.safe_script("direction.left = 50", sol::script_pass_on_error);
REQUIRE_FALSE(result.valid());
d = lua["direction"]["left"];
REQUIRE(d == direction::left);
}
}
TEST_CASE("tables/for_each", "Testing the use of for_each to get values from a lua table") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.safe_script(
"arr = {\n"
"[0] = \"Hi\",\n"
"[1] = 123.45,\n"
"[2] = \"String value\",\n"
// Does nothing
//"[3] = nil,\n"
//"[nil] = 3,\n"
"[\"WOOF\"] = 123,\n"
"}");
sol::table tbl = lua["arr"];
std::size_t tablesize = 4;
std::size_t iterations = 0;
auto fx = [&iterations](sol::object key, sol::object value) {
++iterations;
sol::type keytype = key.get_type();
switch (keytype) {
case sol::type::number:
switch (key.as<int>()) {
case 0:
REQUIRE((value.as<std::string>() == "Hi"));
break;
case 1:
REQUIRE((value.as<double>() == 123.45));
break;
case 2:
REQUIRE((value.as<std::string>() == "String value"));
break;
case 3:
REQUIRE((value.is<sol::lua_nil_t>()));
break;
}
break;
case sol::type::string:
if (key.as<std::string>() == "WOOF") {
REQUIRE((value.as<double>() == 123));
}
break;
case sol::type::lua_nil:
REQUIRE((value.as<double>() == 3));
break;
default:
break;
}
};
auto fxpair = [&fx](std::pair<sol::object, sol::object> kvp) { fx(kvp.first, kvp.second); };
tbl.for_each(fx);
REQUIRE(iterations == tablesize);
iterations = 0;
tbl.for_each(fxpair);
REQUIRE(iterations == tablesize);
}
TEST_CASE("tables/for_each empty", "empty tables should not crash") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.safe_script("arr = {}");
sol::table tbl = lua["arr"];
REQUIRE(tbl.empty());
std::size_t tablesize = 0;
std::size_t iterations = 0;
auto fx = [&iterations](sol::object key, sol::object value) {
++iterations;
sol::type keytype = key.get_type();
switch (keytype) {
case sol::type::number:
switch (key.as<int>()) {
case 0:
REQUIRE((value.as<std::string>() == "Hi"));
break;
case 1:
REQUIRE((value.as<double>() == 123.45));
break;
case 2:
REQUIRE((value.as<std::string>() == "String value"));
break;
case 3:
REQUIRE((value.is<sol::lua_nil_t>()));
break;
}
break;
case sol::type::string:
if (key.as<std::string>() == "WOOF") {
REQUIRE((value.as<double>() == 123));
}
break;
case sol::type::lua_nil:
REQUIRE((value.as<double>() == 3));
break;
default:
break;
}
};
auto fxpair = [&fx](std::pair<sol::object, sol::object> kvp) { fx(kvp.first, kvp.second); };
tbl.for_each(fx);
REQUIRE(iterations == tablesize);
iterations = 0;
tbl.for_each(fxpair);
REQUIRE(iterations == tablesize);
iterations = 0;
for (const auto& kvp : tbl) {
fxpair(kvp);
++iterations;
}
REQUIRE(iterations == tablesize);
}
TEST_CASE("tables/iterators", "Testing the use of iteratrs to get values from a lua table") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.safe_script(
"arr = {\n"
"[0] = \"Hi\",\n"
"[1] = 123.45,\n"
"[2] = \"String value\",\n"
// Does nothing
//"[3] = nil,\n"
//"[nil] = 3,\n"
"[\"WOOF\"] = 123,\n"
"}");
sol::table tbl = lua["arr"];
std::size_t tablesize = 4;
std::size_t iterations = 0;
int begintop = 0;
int endtop = 0;
{
test_stack_guard s(lua.lua_state(), begintop, endtop);
for (auto& kvp : tbl) {
[&iterations](sol::object key, sol::object value) {
++iterations;
sol::type keytype = key.get_type();
switch (keytype) {
case sol::type::number:
switch (key.as<int>()) {
case 0:
REQUIRE((value.as<std::string>() == "Hi"));
break;
case 1:
REQUIRE((value.as<double>() == 123.45));
break;
case 2:
REQUIRE((value.as<std::string>() == "String value"));
break;
case 3:
REQUIRE((value.is<sol::lua_nil_t>()));
break;
}
break;
case sol::type::string:
if (key.as<std::string>() == "WOOF") {
REQUIRE((value.as<double>() == 123));
}
break;
case sol::type::lua_nil:
REQUIRE((value.as<double>() == 3));
break;
default:
break;
}
}(kvp.first, kvp.second);
}
}
REQUIRE(begintop == endtop);
REQUIRE(iterations == tablesize);
}
TEST_CASE("tables/variables", "Check if tables and variables work as intended") {
sol::state lua;
lua.open_libraries(sol::lib::base, sol::lib::os);
lua.get<sol::table>("os").set("name", "windows");
{
auto result = lua.safe_script("assert(os.name == \"windows\")", sol::script_pass_on_error);
REQUIRE(result.valid());
}
}
TEST_CASE("tables/create", "Check if creating a table is kosher") {
sol::state lua;
lua["testtable"] = sol::table::create(lua.lua_state(), 0, 0, "Woof", "Bark", 1, 2, 3, 4);
sol::object testobj = lua["testtable"];
REQUIRE(testobj.is<sol::table>());
sol::table testtable = testobj.as<sol::table>();
REQUIRE((testtable["Woof"] == std::string("Bark")));
REQUIRE((testtable[1] == 2));
REQUIRE((testtable[3] == 4));
}
TEST_CASE("tables/create local", "Check if creating a table is kosher") {
sol::state lua;
lua["testtable"] = lua.create_table(0, 0, "Woof", "Bark", 1, 2, 3, 4);
sol::object testobj = lua["testtable"];
REQUIRE(testobj.is<sol::table>());
sol::table testtable = testobj.as<sol::table>();
REQUIRE((testtable["Woof"] == std::string("Bark")));
REQUIRE((testtable[1] == 2));
REQUIRE((testtable[3] == 4));
}
TEST_CASE("tables/create local named", "Check if creating a table is kosher") {
sol::state lua;
sol::table testtable = lua.create_table("testtable", 0, 0, "Woof", "Bark", 1, 2, 3, 4);
sol::object testobj = lua["testtable"];
REQUIRE(testobj.is<sol::table>());
REQUIRE((testobj == testtable));
REQUIRE_FALSE((testobj != testtable));
REQUIRE((testtable["Woof"] == std::string("Bark")));
REQUIRE((testtable[1] == 2));
REQUIRE((testtable[3] == 4));
}
TEST_CASE("tables/create-with-local", "Check if creating a table is kosher") {
sol::state lua;
lua["testtable"] = lua.create_table_with("Woof", "Bark", 1, 2, 3, 4);
sol::object testobj = lua["testtable"];
REQUIRE(testobj.is<sol::table>());
sol::table testtable = testobj.as<sol::table>();
REQUIRE((testtable["Woof"] == std::string("Bark")));
REQUIRE((testtable[1] == 2));
REQUIRE((testtable[3] == 4));
}
TEST_CASE("tables/function variables", "Check if tables and function calls work as intended") {
sol::state lua;
lua.open_libraries(sol::lib::base, sol::lib::os);
auto run_script = [](sol::state& lua) -> void {
lua.safe_script("assert(os.fun() == \"test\")");
};
lua.get<sol::table>("os").set_function("fun",
[]() {
INFO("stateless lambda()");
return "test";
});
REQUIRE_NOTHROW(run_script(lua));
lua.get<sol::table>("os").set_function("fun", &free_function);
REQUIRE_NOTHROW(run_script(lua));
// l-value, canNOT optimise
// prefer value semantics unless wrapped with std::reference_wrapper
{
auto lval = object();
lua.get<sol::table>("os").set_function("fun", &object::operator(), lval);
}
REQUIRE_NOTHROW(run_script(lua));
auto reflval = object();
lua.get<sol::table>("os").set_function("fun", &object::operator(), std::ref(reflval));
REQUIRE_NOTHROW(run_script(lua));
// stateful lambda: non-convertible, cannot be optimised
int breakit = 50;
lua.get<sol::table>("os").set_function("fun",
[&breakit]() {
INFO("stateful lambda() with breakit:" << breakit);
return "test";
});
REQUIRE_NOTHROW(run_script(lua));
// r-value, cannot optimise
lua.get<sol::table>("os").set_function("fun", &object::operator(), object());
REQUIRE_NOTHROW(run_script(lua));
// r-value, cannot optimise
auto rval = object();
lua.get<sol::table>("os").set_function("fun", &object::operator(), std::move(rval));
REQUIRE_NOTHROW(run_script(lua));
}
TEST_CASE("tables/operator[]", "Check if operator[] retrieval and setting works properly") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.safe_script("foo = 20\nbar = \"hello world\"");
// basic retrieval
std::string bar = lua["bar"];
int foo = lua["foo"];
REQUIRE(bar == "hello world");
REQUIRE(foo == 20);
// test operator= for stringification
// errors due to ambiguous operators
bar = lua["bar"];
// basic setting
lua["bar"] = 20.4;
lua["foo"] = "goodbye";
// doesn't modify the actual values obviously.
REQUIRE(bar == "hello world");
REQUIRE(foo == 20);
// function setting
lua["test"] = plop_xyz;
{
auto result = lua.safe_script("assert(test(10, 11, \"hello\") == 11)", sol::script_pass_on_error);
REQUIRE(result.valid());
}
// function retrieval
sol::function test = lua["test"];
REQUIRE(test.call<int>(10, 11, "hello") == 11);
// setting a lambda
lua["lamb"] = [](int x) {
return x * 2;
};
{
auto result = lua.safe_script("assert(lamb(220) == 440)", sol::script_pass_on_error);
REQUIRE(result.valid());
}
// function retrieval of a lambda
sol::function lamb = lua["lamb"];
REQUIRE(lamb.call<int>(220) == 440);
// test const table retrieval
auto assert1 = [](const sol::table& t) {
std::string a = t["foo"];
double b = t["bar"];
REQUIRE(a == "goodbye");
REQUIRE(b == 20.4);
};
REQUIRE_NOTHROW(assert1(lua.globals()));
}
TEST_CASE("tables/operator[] valid", "Test if proxies on tables can lazily evaluate validity") {
sol::state lua;
bool isFullScreen = false;
auto fullscreennopers = lua["fullscreen"]["nopers"];
auto fullscreen = lua["fullscreen"];
REQUIRE_FALSE(fullscreennopers.valid());
REQUIRE_FALSE(fullscreen.valid());
lua["fullscreen"] = true;
REQUIRE_FALSE(fullscreennopers.valid());
REQUIRE(fullscreen.valid());
isFullScreen = lua["fullscreen"];
REQUIRE(isFullScreen);
lua["fullscreen"] = false;
REQUIRE_FALSE(fullscreennopers.valid());
REQUIRE(fullscreen.valid());
isFullScreen = lua["fullscreen"];
REQUIRE_FALSE(isFullScreen);
}
TEST_CASE("tables/operator[] optional", "Test if proxies on tables can lazily evaluate validity") {
sol::state lua;
sol::optional<int> test1 = lua["no_exist_yet"];
bool present = (bool)test1;
REQUIRE_FALSE(present);
lua["no_exist_yet"] = 262;
sol::optional<int> test2 = lua["no_exist_yet"];
present = (bool)test2;
REQUIRE(present);
REQUIRE(test2.value() == 262);
sol::optional<int> nope = lua["nope"]["kek"]["hah"];
auto nope2 = lua.get<sol::optional<int>>(std::make_tuple("nope", "kek", "hah"));
present = (bool)nope;
REQUIRE_FALSE(present);
present = (bool)nope2;
REQUIRE_FALSE(present);
lua.create_named_table("nope", "kek", lua.create_table_with("hah", 1));
sol::optional<int> non_nope = lua["nope"]["kek"]["hah"].get<sol::optional<int>>();
sol::optional<int> non_nope2 = lua.get<sol::optional<int>>(std::make_tuple("nope", "kek", "hah"));
present = (bool)non_nope;
REQUIRE(present);
present = (bool)non_nope2;
REQUIRE(present);
REQUIRE(non_nope.value() == 1);
REQUIRE(non_nope2.value() == 1);
INFO("Keys: nope, kek, hah");
lua.set(std::make_tuple("nope", "kek", "hah"), 35);
sol::optional<int> non_nope3 = lua["nope"]["kek"]["hah"].get<sol::optional<int>>();
sol::optional<int> non_nope4 = lua.get<sol::optional<int>>(std::make_tuple("nope", "kek", "hah"));
present = (bool)non_nope3;
REQUIRE(present);
present = (bool)non_nope4;
REQUIRE(present);
REQUIRE(non_nope3.value() == 35);
REQUIRE(non_nope4.value() == 35);
}
TEST_CASE("tables/add", "Basic test to make sure the 'add' feature works") {
static const int sz = 120;
sol::state lua;
sol::table t = lua.create_table(sz, 0);
std::vector<int> bigvec(sz);
std::iota(bigvec.begin(), bigvec.end(), 1);
for (std::size_t i = 0; i < bigvec.size(); ++i) {
t.add(bigvec[i]);
}
for (std::size_t i = 0; i < bigvec.size(); ++i) {
int val = t[i + 1];
REQUIRE(val == bigvec[i]);
}
}
TEST_CASE("tables/raw set and raw get", "ensure raw setting and getting works through metatables") {
sol::state lua;
sol::table t = lua.create_table();
t[sol::metatable_key] = lua.create_table_with(
sol::meta_function::new_index, [](lua_State* L) { return luaL_error(L, "nay"); },
sol::meta_function::index, [](lua_State* L) { return luaL_error(L, "nay"); });
t.raw_set("a", 2.5);
double la = t.raw_get<double>("a");
REQUIRE(la == 2.5);
}
TEST_CASE("tables/boolean keys", "make sure boolean keys don't get caught up in `is_integral` specializations") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.safe_script(R"(
tbl = {}
tbl[true] = 10
tbl[1] = 20
print(tbl[true])
print(tbl[1])
)");
sol::table tbl = lua["tbl"];
int v1 = tbl[true];
int v2 = tbl[1];
REQUIRE(v1 == 10);
REQUIRE(v2 == 20);
tbl[true] = 30;
tbl[1] = 40;
v1 = tbl[true];
v2 = tbl[1];
REQUIRE(v1 == 30);
REQUIRE(v2 == 40);
}
TEST_CASE("tables/optional move", "ensure pushing a sol::optional<T> rvalue correctly moves the contained object into tables") {
sol::state sol_state;
struct move_only {
int secret_code;
move_only(const move_only&) = delete;
move_only(move_only&&) = default;
};
sol_state["requires_move"] = sol::optional<move_only>{ move_only{ 0x4D } };
REQUIRE(sol_state["requires_move"].get<move_only>().secret_code == 0x4D);
}
File diff suppressed because it is too large Load Diff
+270
View File
@@ -0,0 +1,270 @@
// sol2
// The MIT License (MIT)
// Copyright (c) 2013-2018 Rapptz, ThePhD and contributors
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "test_sol.hpp"
#include <catch.hpp>
#include <mutex>
#include <thread>
#ifdef SOL_CXX17_FEATURES
#include <string_view>
#include <variant>
#endif
std::mutex basic_init_require_mutex;
void basic_initialization_and_lib_open() {
sol::state lua;
try {
lua.open_libraries();
lua["a"] = 24;
int a = lua["a"];
{
std::lock_guard<std::mutex> lg(basic_init_require_mutex);
REQUIRE(a == 24);
}
}
catch (const sol::error& e) {
std::lock_guard<std::mutex> lg(basic_init_require_mutex);
INFO(e.what());
REQUIRE(false);
}
catch (...) {
std::lock_guard<std::mutex> lg(basic_init_require_mutex);
REQUIRE(false);
}
}
TEST_CASE("utility/variant", "test that variant can be round-tripped") {
#ifdef SOL_CXX17_FEATURES
SECTION("okay") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.set_function("f", [](int v) {
return v == 2;
});
lua.set_function("g", [](std::variant<float, int, std::string> vv) {
int v = std::get<int>(vv);
return v == 2;
});
lua["v"] = std::variant<float, int, std::string>(2);
{
auto result = lua.safe_script("assert(f(v))", sol::script_pass_on_error);
REQUIRE(result.valid());
};
{
auto result = lua.safe_script("assert(g(v))", sol::script_pass_on_error);
REQUIRE(result.valid());
};
}
SECTION("throws") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.set_function("f", [](int v) {
return v == 2;
});
lua.set_function("g", [](std::variant<float, int, std::string> vv) {
int v = std::get<int>(vv);
return v == 2;
});
lua["v"] = std::variant<float, int, std::string>(std::string("bark"));
{
auto result = lua.safe_script("assert(f(v))", sol::script_pass_on_error);
REQUIRE_FALSE(result.valid());
};
{
auto result = lua.safe_script("assert(g(v))", sol::script_pass_on_error);
REQUIRE_FALSE(result.valid());
};
}
#else
REQUIRE(true);
#endif // C++17
}
TEST_CASE("utility/optional", "test that shit optional can be round-tripped") {
#ifdef SOL_CXX17_FEATURES
SECTION("okay") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.set_function("f", [](int v) {
return v == 2;
});
lua.set_function("g", [](std::optional<int> vv) {
return vv && *vv == 2;
});
lua["v"] = std::optional<int>(2);
{
auto result = lua.safe_script("assert(f(v))", sol::script_pass_on_error);
REQUIRE(result.valid());
}
{
auto result = lua.safe_script("assert(g(v))", sol::script_pass_on_error);
REQUIRE(result.valid());
}
}
SECTION("throws") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.set_function("f", [](int v) {
return v == 2;
});
lua.set_function("g", [](std::optional<int> vv) {
return vv && *vv == 2;
});
lua["v"] = std::optional<int>(std::nullopt);
{
auto result = lua.safe_script("assert(f(v))", sol::script_pass_on_error);
REQUIRE_FALSE(result.valid());
};
{
auto result = lua.safe_script("assert(g(v))", sol::script_pass_on_error);
REQUIRE_FALSE(result.valid());
};
}
SECTION("in classes") {
sol::state lua;
lua.open_libraries(sol::lib::base);
struct opt_c {
std::optional<int> member;
};
auto uto = lua.new_usertype<opt_c>("opt_c",
"value", &opt_c::member);
opt_c obj;
lua["obj"] = std::ref(obj);
lua.safe_script("print(obj.value) obj.value = 20 print(obj.value)");
REQUIRE(obj.member == 20);
lua.safe_script("print(obj.value) obj.value = nil print(obj.value)");
REQUIRE(obj.member == std::nullopt);
}
#else
REQUIRE(true);
#endif // C++17
}
TEST_CASE("utility/string_view", "test that string_view can be taken as an argument") {
#ifdef SOL_CXX17_FEATURES
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.set_function("f", [](std::string_view v) {
return v == "bark!";
});
lua["v"] = "bark!";
REQUIRE_NOTHROW([&]() {
lua.safe_script("assert(f(v))");
}());
#else
REQUIRE(true);
#endif // C++17
}
TEST_CASE("utility/thread", "fire up lots of threads at the same time to make sure the initialization changes do not cause horrible crashing data races") {
REQUIRE_NOTHROW([]() {
std::thread thrds[16];
for (int i = 0; i < 16; i++) {
thrds[i] = std::thread(&basic_initialization_and_lib_open);
}
for (int i = 0; i < 16; i++) {
thrds[i].join();
}
}());
}
TEST_CASE("utility/pointer", "check we can get pointer value from references") {
sol::state lua;
lua.set_function("f", [](bool aorb, sol::reference a, sol::stack_reference b) {
if (aorb) {
return a.pointer();
}
return b.pointer();
});
auto result0 = lua.safe_script("v0 = 'hi'", sol::script_pass_on_error);
REQUIRE(result0.valid());
auto result1 = lua.safe_script("v1 = f(true, v0)", sol::script_pass_on_error);
REQUIRE(result1.valid());
auto result2 = lua.safe_script("v2 = f(false, nil, v0)", sol::script_pass_on_error);
REQUIRE(result2.valid());
const void* ap = lua["v1"];
const void* bp = lua["v2"];
REQUIRE(ap == bp);
}
TEST_CASE("utility/this_state", "Ensure this_state argument can be gotten anywhere in the function.") {
struct bark {
int with_state(sol::this_state l, int a, int b) {
lua_State* L = l;
int c = lua_gettop(L);
return a + b + (c - c);
}
static int with_state_2(int a, sol::this_state l, int b) {
INFO("inside with_state_2");
lua_State* L = l;
INFO("L is" << (void*)L);
int c = lua_gettop(L);
return a * b + (c - c);
}
};
sol::state lua;
INFO("created lua state");
lua.open_libraries(sol::lib::base);
lua.new_usertype<bark>("bark",
"with_state", &bark::with_state);
INFO("setting b and with_state_2");
bark b;
lua.set("b", &b);
lua.set("with_state_2", bark::with_state_2);
INFO("finished setting");
INFO("getting fx");
sol::function fx = lua["with_state_2"];
INFO("calling fx");
int a = fx(25, 25);
INFO("finished setting fx");
INFO("calling a script");
lua.safe_script("a = with_state_2(25, 25)");
INFO("calling c script");
lua.safe_script("c = b:with_state(25, 25)");
INFO("getting a");
int la = lua["a"];
INFO("getting b");
int lc = lua["c"];
REQUIRE(lc == 50);
REQUIRE(a == 625);
REQUIRE(la == 625);
}
+255
View File
@@ -0,0 +1,255 @@
// sol2
// The MIT License (MIT)
// Copyright (c) 2013-2018 Rapptz, ThePhD and contributors
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "test_sol.hpp"
#include <catch.hpp>
#include <deque>
#include <set>
#include <functional>
#include <string>
TEST_CASE("variadics/variadic_args", "Check to see we can receive multiple arguments through a variadic") {
struct structure {
int x;
bool b;
};
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.set_function("v", [](sol::this_state, sol::variadic_args va) -> structure {
int r = 0;
for (auto v : va) {
int value = v;
r += value;
}
return { r, r > 200 };
});
lua.safe_script("x = v(25, 25)");
lua.safe_script("x2 = v(25, 25, 100, 50, 250, 150)");
lua.safe_script("x3 = v(1, 2, 3, 4, 5, 6)");
structure& lx = lua["x"];
structure& lx2 = lua["x2"];
structure& lx3 = lua["x3"];
REQUIRE(lx.x == 50);
REQUIRE(lx2.x == 600);
REQUIRE(lx3.x == 21);
REQUIRE_FALSE(lx.b);
REQUIRE(lx2.b);
REQUIRE_FALSE(lx3.b);
}
TEST_CASE("variadics/required with variadic_args", "Check if a certain number of arguments can still be required even when using variadic_args") {
sol::state lua;
lua.set_function("v",
[](sol::this_state, sol::variadic_args, int, int) {
});
{
auto result = lua.safe_script("v(20, 25, 30)", sol::script_pass_on_error);
REQUIRE(result.valid());
}
{
auto result = lua.safe_script("v(20, 25)", sol::script_pass_on_error);
REQUIRE(result.valid());
}
{
auto result = lua.safe_script("v(20)", sol::script_pass_on_error);
REQUIRE_FALSE(result.valid());
}
}
TEST_CASE("variadics/variadic_args get type", "Make sure we can inspect types proper from variadic_args") {
sol::state lua;
lua.set_function("f", [](sol::variadic_args va) {
sol::type types[] = {
sol::type::number,
sol::type::string,
sol::type::boolean
};
bool working = true;
auto b = va.begin();
for (std::size_t i = 0; i < va.size(); ++i, ++b) {
sol::type t1 = va.get_type(i);
sol::type t2 = b->get_type();
working &= types[i] == t1;
working &= types[i] == t2;
}
REQUIRE(working);
});
lua.safe_script("f(1, 'bark', true)");
lua.safe_script("f(2, 'wuf', false)");
}
TEST_CASE("variadics/variadic_results", "returning a variable amount of arguments from C++") {
SECTION("as_returns - containers") {
sol::state lua;
lua.set_function("f", []() {
std::set<std::string> results{ "arf", "bark", "woof" };
return sol::as_returns(std::move(results));
});
lua.set_function("g", []() {
static const std::deque<int> results{ 25, 82 };
return sol::as_returns(std::ref(results));
});
REQUIRE_NOTHROW([&]() {
lua.safe_script(R"(
v1, v2, v3 = f()
v4, v5 = g()
)");
}());
std::string v1 = lua["v1"];
std::string v2 = lua["v2"];
std::string v3 = lua["v3"];
int v4 = lua["v4"];
int v5 = lua["v5"];
REQUIRE(v1 == "arf");
REQUIRE(v2 == "bark");
REQUIRE(v3 == "woof");
REQUIRE(v4 == 25);
REQUIRE(v5 == 82);
}
SECTION("variadic_results - variadic_args") {
sol::state lua;
lua.set_function("f", [](sol::variadic_args args) {
return sol::variadic_results(args.cbegin(), args.cend());
});
REQUIRE_NOTHROW([&]() {
lua.safe_script(R"(
v1, v2, v3 = f(1, 'bark', true)
v4, v5 = f(25, 82)
)");
}());
int v1 = lua["v1"];
std::string v2 = lua["v2"];
bool v3 = lua["v3"];
int v4 = lua["v4"];
int v5 = lua["v5"];
REQUIRE(v1 == 1);
REQUIRE(v2 == "bark");
REQUIRE(v3);
REQUIRE(v4 == 25);
REQUIRE(v5 == 82);
}
SECTION("variadic_results") {
sol::state lua;
lua.set_function("f", [](sol::this_state ts, bool maybe) {
if (maybe) {
sol::variadic_results vr;
vr.push_back({ ts, sol::in_place, 1 });
vr.push_back({ ts, sol::in_place, 2 });
vr.insert(vr.cend(), { ts, sol::in_place, 3 });
return vr;
}
else {
sol::variadic_results vr;
vr.push_back({ ts, sol::in_place, "bark" });
vr.push_back({ ts, sol::in_place, "woof" });
vr.insert(vr.cend(), { ts, sol::in_place, "arf" });
vr.push_back({ ts, sol::in_place, "borf" });
return vr;
}
});
REQUIRE_NOTHROW([&]() {
lua.safe_script(R"(
v1, v2, v3 = f(true)
v4, v5, v6, v7 = f(false)
)");
}());
int v1 = lua["v1"];
int v2 = lua["v2"];
int v3 = lua["v3"];
std::string v4 = lua["v4"];
std::string v5 = lua["v5"];
std::string v6 = lua["v6"];
std::string v7 = lua["v7"];
REQUIRE(v1 == 1);
REQUIRE(v2 == 2);
REQUIRE(v3 == 3);
REQUIRE(v4 == "bark");
REQUIRE(v5 == "woof");
REQUIRE(v6 == "arf");
REQUIRE(v7 == "borf");
}
}
TEST_CASE("variadics/fallback_constructor", "ensure constructor matching behaves properly in the presence of variadic fallbacks") {
struct vec2 {
float x = 0, y = 0;
};
sol::state lua;
lua.new_simple_usertype<vec2>("vec2",
sol::call_constructor, sol::factories([]() { return vec2{}; }, [](vec2 const& v) -> vec2 { return v; }, [](sol::variadic_args va) {
vec2 res{};
if (va.size() == 1) {
res.x = va[0].get<float>();
res.y = va[0].get<float>();
}
else if (va.size() == 2) {
res.x = va[0].get<float>();
res.y = va[1].get<float>();
}
else {
throw sol::error("invalid args");
}
return res; }));
REQUIRE_NOTHROW([&]() {
lua.safe_script("v0 = vec2();");
lua.safe_script("v1 = vec2(1);");
lua.safe_script("v2 = vec2(1, 2);");
lua.safe_script("v3 = vec2(v2)");
}());
vec2& v0 = lua["v0"];
vec2& v1 = lua["v1"];
vec2& v2 = lua["v2"];
vec2& v3 = lua["v3"];
REQUIRE(v0.x == 0);
REQUIRE(v0.y == 0);
REQUIRE(v1.x == 1);
REQUIRE(v1.y == 1);
REQUIRE(v2.x == 1);
REQUIRE(v2.y == 2);
REQUIRE(v3.x == v2.x);
REQUIRE(v3.y == v2.y);
}
+654
View File
@@ -0,0 +1,654 @@
// sol2
// The MIT License (MIT)
// Copyright (c) 2013-2018 Rapptz, ThePhD and contributors
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "test_sol.hpp"
#include <catch.hpp>
#include <fstream>
#include <iostream>
#include <map>
#include <unordered_map>
#include <vector>
bool func_opt_ret_bool(sol::optional<int> i) {
if (i) {
INFO(i.value());
}
else {
INFO("optional isn't set");
}
return true;
}
TEST_CASE("table/traversal", "ensure that we can chain requests and tunnel down into a value if we desire") {
sol::state lua;
int begintop = 0, endtop = 0;
sol::function scriptload = lua.load("t1 = {t2 = {t3 = 24}};");
scriptload();
{
test_stack_guard g(lua.lua_state(), begintop, endtop);
int traversex24 = lua.traverse_get<int>("t1", "t2", "t3");
REQUIRE(traversex24 == 24);
}
REQUIRE(begintop == endtop);
{
test_stack_guard g(lua.lua_state(), begintop, endtop);
int x24 = lua["t1"]["t2"]["t3"];
REQUIRE(x24 == 24);
}
REQUIRE(begintop == endtop);
{
test_stack_guard g(lua.lua_state(), begintop, endtop);
lua["t1"]["t2"]["t3"] = 64;
int traversex64 = lua.traverse_get<int>("t1", "t2", "t3");
REQUIRE(traversex64 == 64);
}
REQUIRE(begintop == endtop);
{
test_stack_guard g(lua.lua_state(), begintop, endtop);
int x64 = lua["t1"]["t2"]["t3"];
REQUIRE(x64 == 64);
}
REQUIRE(begintop == endtop);
{
test_stack_guard g(lua.lua_state(), begintop, endtop);
lua.traverse_set("t1", "t2", "t3", 13);
int traversex13 = lua.traverse_get<int>("t1", "t2", "t3");
REQUIRE(traversex13 == 13);
}
REQUIRE(begintop == endtop);
{
test_stack_guard g(lua.lua_state(), begintop, endtop);
int x13 = lua["t1"]["t2"]["t3"];
REQUIRE(x13 == 13);
}
REQUIRE(begintop == endtop);
}
TEST_CASE("simple/set", "Check if the set works properly.") {
sol::state lua;
int begintop = 0, endtop = 0;
{
test_stack_guard g(lua.lua_state(), begintop, endtop);
lua.set("a", 9);
}
REQUIRE(begintop == endtop);
{
auto result = lua.safe_script("if a ~= 9 then error('wrong value') end", sol::script_pass_on_error);
REQUIRE(result.valid());
}
{
test_stack_guard g(lua.lua_state(), begintop, endtop);
lua.set("d", "hello");
}
REQUIRE(begintop == endtop);
{
auto result = lua.safe_script("if d ~= 'hello' then error('expected \\'hello\\', got '.. tostring(d)) end", sol::script_pass_on_error);
REQUIRE(result.valid());
}
{
test_stack_guard g(lua.lua_state(), begintop, endtop);
lua.set("e", std::string("hello"), "f", true);
}
REQUIRE(begintop == endtop);
{
auto result = lua.safe_script("if d ~= 'hello' then error('expected \\'hello\\', got '.. tostring(d)) end", sol::script_pass_on_error);
REQUIRE(result.valid());
}
{
auto result = lua.safe_script("if f ~= true then error('wrong value') end", sol::script_pass_on_error);
REQUIRE(result.valid());
}
}
TEST_CASE("simple/get", "Tests if the get function works properly.") {
sol::state lua;
int begintop = 0, endtop = 0;
lua.safe_script("a = 9");
{
test_stack_guard g(lua.lua_state(), begintop, endtop);
auto a = lua.get<int>("a");
REQUIRE(a == 9.0);
}
REQUIRE(begintop == endtop);
lua.safe_script("b = nil");
{
test_stack_guard g(lua.lua_state(), begintop, endtop);
REQUIRE_NOTHROW(lua.get<sol::lua_nil_t>("b"));
}
REQUIRE(begintop == endtop);
lua.safe_script("d = 'hello'");
lua.safe_script("e = true");
{
test_stack_guard g(lua.lua_state(), begintop, endtop);
std::string d;
bool e;
std::tie(d, e) = lua.get<std::string, bool>("d", "e");
REQUIRE(d == "hello");
REQUIRE(e == true);
}
REQUIRE(begintop == endtop);
}
TEST_CASE("simple/set and get global integer", "Tests if the get function works properly with global integers") {
sol::state lua;
lua[1] = 25.4;
lua.safe_script("b = 1");
double a = lua.get<double>(1);
double b = lua.get<double>("b");
REQUIRE(a == 25.4);
REQUIRE(b == 1);
}
TEST_CASE("simple/get_or", "check if table.get_or works correctly") {
sol::state lua;
auto bob_table = lua.create_table("bob");
bob_table.set("is_set", 42);
int is_set = bob_table.get_or("is_set", 3);
int is_not_set = bob_table.get_or("is_not_set", 22);
REQUIRE(is_set == 42);
REQUIRE(is_not_set == 22);
lua["joe"] = 55.6;
double bark = lua.get_or<double>("joe", 60);
REQUIRE(bark == 55.6);
}
TEST_CASE("simple/proxy get_or", "check if proxy.get_or works correctly") {
sol::state lua;
auto bob_table = lua.create_table("bob");
bob_table.set("is_set", 42);
int is_set = bob_table["is_set"].get_or(3);
int is_not_set = bob_table["is_not_set"].get_or(22);
REQUIRE(is_set == 42);
REQUIRE(is_not_set == 22);
lua["joe"] = 55.6;
double bark = lua["joe"].get_or<double>(60);
REQUIRE(bark == 55.6);
}
TEST_CASE("simple/addition", "check if addition works and can be gotten through lua.get and lua.set") {
sol::state lua;
lua.set("b", 0.2);
lua.safe_script("c = 9 + b");
auto c = lua.get<double>("c");
REQUIRE(c == 9.2);
}
TEST_CASE("simple/if", "check if if statements work through lua") {
sol::state lua;
std::string program = "if true then f = 0.1 else f = 'test' end";
lua.safe_script(program);
auto f = lua.get<double>("f");
REQUIRE(f == 0.1);
REQUIRE((f == lua["f"]));
}
TEST_CASE("negative/basic errors", "Check if error handling works correctly") {
sol::state lua;
auto result = lua.safe_script("nil[5]", sol::script_pass_on_error);
REQUIRE_FALSE(result.valid());
}
TEST_CASE("libraries", "Check if we can open libraries") {
sol::state lua;
REQUIRE_NOTHROW(lua.open_libraries(sol::lib::base, sol::lib::os));
}
TEST_CASE("libraries2", "Check if we can open ALL the libraries") {
sol::state lua;
REQUIRE_NOTHROW(lua.open_libraries(sol::lib::base,
sol::lib::bit32,
sol::lib::coroutine,
sol::lib::debug,
sol::lib::ffi,
sol::lib::jit,
sol::lib::math,
sol::lib::os,
sol::lib::package,
sol::lib::string,
sol::lib::table));
}
TEST_CASE("interop/null-to-nil-and-back", "nil should be the given type when a pointer from C++ is returned as nullptr, and nil should result in nullptr in connected C++ code") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.set_function("lol", []() -> int* {
return nullptr;
});
lua.set_function("rofl", [](int* x) {
INFO(x);
});
REQUIRE_NOTHROW(lua.safe_script(
"x = lol()\n"
"rofl(x)\n"
"assert(x == nil)"));
}
TEST_CASE("object/conversions", "make sure all basic reference types can be made into objects") {
sol::state lua;
lua.open_libraries(sol::lib::base);
struct d {};
lua.safe_script("function f () print('bark') end");
lua["d"] = d{};
lua["l"] = static_cast<void*>(nullptr);
sol::table t = lua.create_table();
sol::table t2(lua, sol::create);
sol::thread th = sol::thread::create(lua);
sol::function f = lua["f"];
sol::protected_function pf = lua["f"];
sol::userdata ud = lua["d"];
sol::lightuserdata lud = lua["l"];
sol::environment env(lua, sol::create);
sol::object ot(t);
sol::object ot2(t2);
sol::object oteq = ot;
sol::object oth(th);
sol::object of(f);
sol::object opf(pf);
sol::object od(ud);
sol::object ol(lud);
sol::object oenv(env);
auto oni = sol::make_object(lua, 50);
auto ond = sol::make_object(lua, 50.0);
std::string somestring = "look at this text isn't it nice";
auto osl = sol::make_object(lua, "Bark bark bark");
auto os = sol::make_object(lua, somestring);
auto omn = sol::make_object(lua, sol::lua_nil);
REQUIRE(ot.get_type() == sol::type::table);
REQUIRE(ot2.get_type() == sol::type::table);
REQUIRE(oteq.get_type() == sol::type::table);
REQUIRE(oth.get_type() == sol::type::thread);
REQUIRE(of.get_type() == sol::type::function);
REQUIRE(opf.get_type() == sol::type::function);
REQUIRE(od.get_type() == sol::type::userdata);
REQUIRE(ol.get_type() == sol::type::lightuserdata);
REQUIRE(oni.get_type() == sol::type::number);
REQUIRE(ond.get_type() == sol::type::number);
REQUIRE(osl.get_type() == sol::type::string);
REQUIRE(os.get_type() == sol::type::string);
REQUIRE(omn.get_type() == sol::type::lua_nil);
REQUIRE(oenv.get_type() == sol::type::table);
}
TEST_CASE("object/main_* conversions", "make sure all basic reference types can be made into objects") {
sol::state lua;
lua.open_libraries(sol::lib::base);
struct d {};
lua.safe_script("function f () print('bark') end");
lua["d"] = d{};
lua["l"] = static_cast<void*>(nullptr);
sol::main_table t = lua.create_table();
sol::main_table t2(lua, sol::create);
sol::thread th = sol::thread::create(lua);
sol::main_function f = lua["f"];
sol::main_protected_function pf = lua["f"];
sol::main_userdata ud = lua["d"];
sol::main_lightuserdata lud = lua["l"];
sol::main_environment env(lua, sol::create);
sol::main_object ot(t);
sol::main_object ot2(t2);
sol::main_object oteq = ot;
sol::main_object oth(th);
sol::main_object of(f);
sol::main_object opf(pf);
sol::main_object od(ud);
sol::main_object ol(lud);
sol::main_object oenv(env);
auto oni = sol::make_object(lua, 50);
auto ond = sol::make_object(lua, 50.0);
std::string somestring = "look at this text isn't it nice";
auto osl = sol::make_object(lua, "Bark bark bark");
auto os = sol::make_object(lua, somestring);
auto omn = sol::make_object(lua, sol::lua_nil);
REQUIRE(ot.get_type() == sol::type::table);
REQUIRE(ot2.get_type() == sol::type::table);
REQUIRE(oteq.get_type() == sol::type::table);
REQUIRE(oth.get_type() == sol::type::thread);
REQUIRE(of.get_type() == sol::type::function);
REQUIRE(opf.get_type() == sol::type::function);
REQUIRE(od.get_type() == sol::type::userdata);
REQUIRE(ol.get_type() == sol::type::lightuserdata);
REQUIRE(oni.get_type() == sol::type::number);
REQUIRE(ond.get_type() == sol::type::number);
REQUIRE(osl.get_type() == sol::type::string);
REQUIRE(os.get_type() == sol::type::string);
REQUIRE(omn.get_type() == sol::type::lua_nil);
REQUIRE(oenv.get_type() == sol::type::table);
}
TEST_CASE("feature/indexing overrides", "make sure index functions can be overridden on types") {
struct PropertySet {
sol::object get_property_lua(const char* name, sol::this_state s) {
auto& var = props[name];
return sol::make_object(s, var);
}
void set_property_lua(const char* name, sol::stack_object object) {
props[name] = object.as<std::string>();
}
std::unordered_map<std::string, std::string> props;
};
struct DynamicObject {
PropertySet& get_dynamic_props() {
return dynamic_props;
}
PropertySet dynamic_props;
};
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_usertype<PropertySet>("PropertySet", sol::meta_function::new_index, &PropertySet::set_property_lua, sol::meta_function::index, &PropertySet::get_property_lua);
lua.new_usertype<DynamicObject>("DynamicObject", "props", sol::property(&DynamicObject::get_dynamic_props));
lua.safe_script(R"__(
obj = DynamicObject:new()
obj.props.name = 'test name'
print('name = ' .. obj.props.name)
)__");
std::string name = lua["obj"]["props"]["name"];
REQUIRE(name == "test name");
}
TEST_CASE("features/indexing numbers", "make sure indexing functions can be override on usertypes") {
class vector {
public:
double data[3];
vector()
: data{ 0, 0, 0 } {
}
double& operator[](int i) {
return data[i];
}
static double my_index(vector& v, int i) {
return v[i];
}
static void my_new_index(vector& v, int i, double x) {
v[i] = x;
}
};
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_usertype<vector>("vector", sol::constructors<sol::types<>>(),
sol::meta_function::index, &vector::my_index,
sol::meta_function::new_index, &vector::my_new_index);
lua.safe_script(
"v = vector.new()\n"
"print(v[1])\n"
"v[2] = 3\n"
"print(v[2])\n");
vector& v = lua["v"];
REQUIRE(v[0] == 0.0);
REQUIRE(v[1] == 0.0);
REQUIRE(v[2] == 3.0);
}
TEST_CASE("features/multiple inheritance", "Ensure that multiple inheritance works as advertised") {
struct base1 {
int a1 = 250;
};
struct base2 {
int a2 = 500;
};
struct simple : base1 {
};
struct complex : base1, base2 {
};
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_usertype<base1>("base1",
"a1", &base1::a1);
lua.new_usertype<base2>("base2",
"a2", &base2::a2);
lua.new_usertype<simple>("simple",
"a1", &simple::a1,
sol::base_classes, sol::bases<base1>());
lua.new_usertype<complex>("complex",
"a1", &complex::a1,
"a2", &complex::a2,
sol::base_classes, sol::bases<base1, base2>());
lua.safe_script(
"c = complex.new()\n"
"s = simple.new()\n"
"b1 = base1.new()\n"
"b2 = base1.new()\n");
base1* sb1 = lua["s"];
REQUIRE(sb1 != nullptr);
REQUIRE(sb1->a1 == 250);
base1* cb1 = lua["c"];
base2* cb2 = lua["c"];
REQUIRE(cb1 != nullptr);
REQUIRE(cb2 != nullptr);
REQUIRE(cb1->a1 == 250);
REQUIRE(cb2->a2 == 500);
}
TEST_CASE("regressions/std::ref", "Ensure that std::reference_wrapper<> isn't considered as a function by using unwrap_unqualified_t trait") {
struct base1 {
int a1 = 250;
};
sol::state lua;
base1 v;
lua["vp"] = &v;
lua["vr"] = std::ref(v);
base1* vp = lua["vp"];
base1& vr = lua["vr"];
REQUIRE(vp != nullptr);
REQUIRE(vp == &v);
REQUIRE(vp->a1 == 250);
REQUIRE(vr.a1 == 250);
v.a1 = 568;
REQUIRE(vp->a1 == 568);
REQUIRE(vr.a1 == 568);
}
TEST_CASE("optional/left out args", "Make sure arguments can be left out of optional without tanking miserably") {
sol::state lua;
lua.open_libraries(sol::lib::base);
// sol::optional needs an argument no matter what?
lua.set_function("func_opt_ret_bool", func_opt_ret_bool);
REQUIRE_NOTHROW([&] {
lua.safe_script(R"(
func_opt_ret_bool(42)
func_opt_ret_bool()
print('ok')
)");
}());
}
TEST_CASE("optional/engaged versus unengaged", "solidify semantics for an engaged and unengaged optional") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.set_function("f", [](sol::optional<sol::object> optional_arg) {
if (optional_arg) {
return true;
}
return false;
});
auto valid0 = lua.safe_script("assert(not f())", sol::script_pass_on_error);
REQUIRE(valid0.valid());
auto valid1 = lua.safe_script("assert(not f(nil))", sol::script_pass_on_error);
REQUIRE(valid1.valid());
auto valid2 = lua.safe_script("assert(f(1))", sol::script_pass_on_error);
REQUIRE(valid2.valid());
auto valid3 = lua.safe_script("assert(f('hi'))", sol::script_pass_on_error);
REQUIRE(valid3.valid());
}
TEST_CASE("pusher/constness", "Make sure more types can handle being const and junk") {
struct Foo {
Foo(const sol::function& f)
: _f(f) {
}
const sol::function& _f;
const sol::function& f() const {
return _f;
}
};
sol::state lua;
lua.new_usertype<Foo>("Foo",
sol::call_constructor, sol::no_constructor,
"f", &Foo::f);
lua["func"] = []() { return 20; };
sol::function f = lua["func"];
lua["foo"] = Foo(f);
Foo& foo = lua["foo"];
int x = foo.f()();
REQUIRE(x == 20);
}
TEST_CASE("compilation/const regression", "make sure constness in tables is respected all the way down") {
struct State {
public:
State() {
this->state_.registry()["state"] = this;
}
sol::state state_;
};
State state;
State* s = state.state_.registry()["state"];
REQUIRE(s == &state);
}
TEST_CASE("numbers/integers", "make sure integers are detectable on most platforms") {
sol::state lua;
lua["a"] = 50; // int
lua["b"] = 50.5; // double
sol::object a = lua["a"];
sol::object b = lua["b"];
bool a_is_int = a.is<int>();
bool a_is_double = a.is<double>();
bool b_is_int = b.is<int>();
bool b_is_double = b.is<double>();
REQUIRE(a_is_int);
REQUIRE(a_is_double);
// TODO: will this fail on certain lower Lua versions?
REQUIRE_FALSE(b_is_int);
REQUIRE(b_is_double);
}
TEST_CASE("object/is", "test whether or not the is abstraction works properly for a user-defined type") {
struct thing {};
SECTION("stack_object") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.set_function("is_thing", [](sol::stack_object obj) { return obj.is<thing>(); });
lua["a"] = thing{};
{
auto result = lua.safe_script("assert(is_thing(a))", sol::script_pass_on_error);
REQUIRE(result.valid());
}
}
SECTION("object") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.set_function("is_thing", [](sol::object obj) { return obj.is<thing>(); });
lua["a"] = thing{};
{
auto result = lua.safe_script("assert(is_thing(a))", sol::script_pass_on_error);
REQUIRE(result.valid());
}
}
}
+7
View File
@@ -0,0 +1,7 @@
#define CATCH_CONFIG_RUNNER
#include "catch.hpp"
int main(int argc, char* argv[]) {
int result = Catch::Session().run(argc, argv);
return result;
}