sol2/tests/run_time/source/container_table.cpp
2022-06-25 04:00:53 -04:00

318 lines
11 KiB
C++

// sol2
// The MIT License (MIT)
// Copyright (c) 2013-2022 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 "sol_test.hpp"
#include "common_classes.hpp"
#include <catch2/catch_all.hpp>
#include <iterator>
#include <vector>
#include <list>
#include <forward_list>
#include <map>
#include <deque>
#include <array>
#include <unordered_map>
#include <set>
#include <unordered_set>
inline namespace sol2_test_container_table {
template <typename T>
struct as_table_callable {
T* ptr;
as_table_callable(T& ref_) : ptr(&ref_) {
}
auto operator()() const {
return sol::as_table(*ptr);
}
};
} // namespace sol2_test_container_table
TEST_CASE("containers/vector table roundtrip", "make sure vectors can be round-tripped") {
sol::state lua;
std::vector<int> v { 1, 2, 3 };
lua.set_function("f", as_table_callable<std::vector<int>>(v));
auto result1 = lua.safe_script("x = f()", sol::script_pass_on_error);
REQUIRE(result1.valid());
sol::as_table_t<std::vector<int>> x = lua["x"];
bool areequal = x.value() == v;
REQUIRE(areequal);
}
TEST_CASE("containers/deque table roundtrip", "make sure deques can be round-tripped") {
sol::state lua;
std::deque<int> v { 1, 2, 3 };
lua.set_function("f", as_table_callable<std::deque<int>>(v));
auto result1 = lua.safe_script("x = f()", sol::script_pass_on_error);
REQUIRE(result1.valid());
sol::as_table_t<std::deque<int>> x = lua["x"];
bool areequal = x.value() == v;
REQUIRE(areequal);
}
TEST_CASE("containers/array table roundtrip", "make sure arrays can be round-tripped") {
sol::state lua;
std::array<int, 3> v { { 1, 2, 3 } };
lua.set_function("f", as_table_callable<std::array<int, 3>>(v));
auto result1 = lua.safe_script("x = f()", sol::script_pass_on_error);
REQUIRE(result1.valid());
sol::as_table_t<std::array<int, 3>> x = lua["x"];
bool areequal = x.value() == v;
REQUIRE(areequal);
}
TEST_CASE("containers/list table roundtrip", "make sure lists can be round-tripped") {
sol::state lua;
std::list<int> v { 1, 2, 3 };
lua.set_function("f", as_table_callable<std::list<int>>(v));
auto result1 = lua.safe_script("x = f()", sol::script_pass_on_error);
REQUIRE(result1.valid());
sol::as_table_t<std::list<int>> x = lua["x"];
bool areequal = x.value() == v;
REQUIRE(areequal);
}
TEST_CASE("containers/forward_list table roundtrip", "make sure forward_lists can be round-tripped") {
sol::state lua;
std::forward_list<int> v { 1, 2, 3 };
lua.set_function("f", as_table_callable<std::forward_list<int>>(v));
auto result1 = lua.safe_script("x = f()", sol::script_pass_on_error);
REQUIRE(result1.valid());
sol::as_table_t<std::forward_list<int>> x = lua["x"];
bool areequal = x.value() == v;
REQUIRE(areequal);
}
TEST_CASE("containers/map table roundtrip", "make sure maps can be round-tripped") {
sol::state lua;
std::map<std::string, int> v { { "a", 1 }, { "b", 2 }, { "c", 3 } };
lua.set_function("f", as_table_callable<std::map<std::string, int>>(v));
auto result1 = lua.safe_script("x = f()", sol::script_pass_on_error);
REQUIRE(result1.valid());
sol::as_table_t<std::map<std::string, int>> x = lua["x"];
bool areequal = x.value() == v;
REQUIRE(areequal);
}
TEST_CASE("containers/unordered_map table roundtrip", "make sure unordered_maps can be round-tripped") {
sol::state lua;
std::unordered_map<std::string, int> v { { "a", 1 }, { "b", 2 }, { "c", 3 } };
lua.set_function("f", as_table_callable<std::unordered_map<std::string, int>>(v));
auto result1 = lua.safe_script("x = f()", sol::script_pass_on_error);
REQUIRE(result1.valid());
sol::as_table_t<std::unordered_map<std::string, int>> x = lua["x"];
bool areequal = x.value() == v;
REQUIRE(areequal);
}
TEST_CASE("containers/unordered_set table roundtrip", "make sure unordered_sets can be round-tripped") {
sol::state lua;
std::unordered_set<int> v { 1, 2, 3 };
lua.set_function("f", as_table_callable<std::unordered_set<int>>(v));
auto result1 = lua.safe_script("x = f()", sol::script_pass_on_error);
REQUIRE(result1.valid());
sol::as_table_t<std::unordered_set<int>> x = lua["x"];
bool areequal = x.value() == v;
REQUIRE(areequal);
}
TEST_CASE("containers/set table roundtrip", "make sure sets can be round-tripped") {
sol::state lua;
std::set<int> v { 1, 2, 3 };
lua.set_function("f", as_table_callable<std::set<int>>(v));
auto result1 = lua.safe_script("x = f()", sol::script_pass_on_error);
REQUIRE(result1.valid());
sol::as_table_t<std::set<int>> x = lua["x"];
bool areequal = x.value() == v;
REQUIRE(areequal);
}
TEST_CASE("containers/table conversions (lvalue)", "test table conversions with as_table and nested, when not directly serializing a temporary / new value") {
sol::state lua;
auto f = []() {
std::vector<std::string> response_words;
response_words.push_back("a");
response_words.push_back("b");
response_words.push_back("c");
return sol::as_table(response_words);
};
auto g = []() {
std::vector<std::string> response_words;
response_words.push_back("a");
response_words.push_back("b");
response_words.push_back("c");
return sol::as_nested(response_words);
};
lua["f"] = std::ref(f);
lua["g"] = std::ref(g);
sol::safe_function sff = lua["f"];
sol::safe_function sfg = lua["g"];
sol::table tf = sff();
sol::table tg = sfg();
std::string af = tf[1];
std::string bf = tf[2];
std::string cf = tf[3];
std::string ag = tf[1];
std::string bg = tf[2];
std::string cg = tf[3];
REQUIRE(tf.size() == 3);
REQUIRE(af == "a");
REQUIRE(bf == "b");
REQUIRE(cf == "c");
REQUIRE(tg.size() == 3);
REQUIRE(ag == "a");
REQUIRE(bg == "b");
REQUIRE(cg == "c");
}
TEST_CASE("containers/table conversions (std::ref)", "test table conversions with as_table and nested, when not directly serializing a temporary / new value") {
sol::state lua;
std::vector<std::string> response_words;
response_words.push_back("a");
response_words.push_back("b");
response_words.push_back("c");
auto f = [&response_words]() { return sol::as_table(std::ref(response_words)); };
auto g = [&response_words]() { return sol::as_nested(std::ref(response_words)); };
lua["f"] = std::ref(f);
lua["g"] = std::ref(g);
sol::safe_function sff = lua["f"];
sol::safe_function sfg = lua["g"];
sol::table tf = sff();
sol::table tg = sfg();
std::string af = tf[1];
std::string bf = tf[2];
std::string cf = tf[3];
std::string ag = tf[1];
std::string bg = tf[2];
std::string cg = tf[3];
REQUIRE(tf.size() == 3);
REQUIRE(af == "a");
REQUIRE(bf == "b");
REQUIRE(cf == "c");
REQUIRE(tg.size() == 3);
REQUIRE(ag == "a");
REQUIRE(bg == "b");
REQUIRE(cg == "c");
}
TEST_CASE("containers/table conversion", "test table conversions with as_table and nested") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.set_function("bark", []() { return sol::as_nested(std::vector<std::string> { "bark", "woof" }); });
lua.set_function("woof", []() { return sol::as_nested(std::vector<std::string> { "bark", "woof" }); });
auto result1 = lua.safe_script("v1 = bark()", sol::script_pass_on_error);
REQUIRE(result1.valid());
auto result2 = lua.safe_script("v2 = woof()", sol::script_pass_on_error);
REQUIRE(result2.valid());
sol::as_table_t<std::vector<std::string>> as_table_strings = lua["v1"];
sol::nested<std::vector<std::string>> nested_strings = lua["v2"];
std::vector<std::string> expected_values { "bark", "woof" };
REQUIRE(as_table_strings.value() == expected_values);
REQUIRE(nested_strings.value() == expected_values);
}
TEST_CASE("containers/from table argument conversions", "test table conversions without as_table and nested for function args") {
const std::vector<std::string> expected_values { "bark", "woof" };
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.set_function("f", [&](std::vector<std::string> t) { return t == expected_values; });
auto result0 = lua.safe_script("t = { \"bark\", \"woof\" }");
REQUIRE(result0.valid());
auto result1 = lua.safe_script("assert(f(t))", sol::script_pass_on_error);
REQUIRE(result1.valid());
sol::function f = lua["f"];
sol::table t = lua["t"];
bool passed = f(t);
REQUIRE(passed);
}
TEST_CASE("containers/deeply nested", "make sure nested works for deeply-nested C++ containers and works as advertised") {
typedef std::map<const char*, std::string> info_t;
typedef std::vector<info_t> info_vector;
class ModList {
public:
info_vector list;
ModList() {
list.push_back(info_t { { "a", "b" } });
}
sol::nested<info_vector&> getList() {
return sol::nested<info_vector&>(list);
}
};
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_usertype<ModList>("ModList", "getList", &ModList::getList);
sol::string_view code = R"(
mods = ModList.new()
local modlist = mods:getList()
print(modlist[1])
assert(type(modlist) == "table")
assert(type(modlist[1]) == "table")
)";
auto result1 = lua.safe_script(code, sol::script_pass_on_error);
REQUIRE(result1.valid());
}
TEST_CASE("containers/nested with optional", "optionals should not change the behavior of getting or setting types such as nested") {
const std::vector<int> color = { 1, 2, 3, 0 };
sol::state lua;
sol::optional<sol::nested<std::vector<int>>> maybe_color_no = lua["color"];
lua["color"] = color;
sol::optional<sol::nested<std::vector<int>>> maybe_color_yes = lua["color"];
REQUIRE_FALSE(maybe_color_no.has_value());
REQUIRE(maybe_color_yes.has_value());
std::vector<int>& color_yes = maybe_color_yes.value().value();
REQUIRE(color == color_yes);
}