You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
libshit/test/container/ordered_map.cpp

376 lines
8.8 KiB
C++

#include "libshit/container/ordered_map.hpp"
#include "libshit/doctest.hpp"
#include "libshit/utils.hpp"
#include <ostream>
#include <string>
namespace Libshit::Test
{
TEST_SUITE_BEGIN("Libshit::OrderedMap");
namespace
{
struct OMItemTest final : public OrderedMapItem, public Lua::DynamicObject
{
LIBSHIT_DYNAMIC_OBJECT;
public:
OMItemTest(std::string k, int v) : k{Move(k)}, v{v} { ++count; }
~OMItemTest() { --count; }
OMItemTest(const OMItemTest&) = delete;
void operator=(const OMItemTest&) = delete;
std::string k;
int v;
static size_t count;
bool operator==(const OMItemTest& o) const noexcept
{ return k == o.k && v == o.v; }
};
std::ostream& operator<<(std::ostream& os, const OMItemTest& i)
{ return os << "OMItemTest{" << i.k << ", " << i.v << '}'; }
size_t OMItemTest::count;
struct OMItemTestTraits
{
using type = std::string;
const std::string& operator()(const OMItemTest& x) { return x.k; }
};
using X = OMItemTest;
using OM = OrderedMap<OMItemTest, OMItemTestTraits>;
}
TEST_CASE("basic test")
{
X::count = 0;
{
OM om;
CHECK(om.empty());
CHECK(om.size() == 0);
om.emplace_back("foo",2);
om.emplace_back("bar",7);
CHECK(!om.empty());
CHECK(om.size() == 2);
SUBCASE("at")
{
CHECK(om.at(0) == X("foo", 2));
CHECK(om.at(1) == X("bar", 7));
CHECK_THROWS(om.at(2));
}
SUBCASE("operator[]")
{
CHECK(om[0] == X("foo", 2));
CHECK(om[1] == X("bar", 7));
}
SUBCASE("front/back")
{
CHECK(om.front() == X("foo", 2));
CHECK(om.back() == X("bar", 7));
}
SUBCASE("iterator")
{
auto it = om.begin();
REQUIRE(it != om.end());
CHECK(it->k == "foo");
++it;
REQUIRE(it != om.end());
CHECK(*it == X("bar", 7));
++it;
CHECK(it == om.end());
}
SUBCASE("reserve")
{
CHECK(om.capacity() >= 2);
om.reserve(10);
REQUIRE(om.size() == 2);
CHECK(om.capacity() >= 10);
CHECK(om[1] == X("bar", 7));
}
SUBCASE("clear")
{
om.clear();
CHECK(om.empty());
}
SUBCASE("insert")
{
om.insert(om.begin()+1, MakeSmart<X>("def", 9));
REQUIRE(om.size() == 3);
CHECK(om[0] == X("foo", 2));
CHECK(om[1] == X("def", 9));
CHECK(om[2] == X("bar", 7));
}
SUBCASE("insert existing")
{
om.insert(om.begin(), MakeSmart<X>("bar", -1));
REQUIRE(om.size() == 2);
CHECK(om[0] == X("foo", 2));
CHECK(om[1] == X("bar", 7));
}
SUBCASE("emplace")
{
om.emplace(om.begin(), "aa", 5);
REQUIRE(om.size() == 3);
CHECK(om[0] == X("aa", 5));
CHECK(om[1] == X("foo", 2));
CHECK(om[2] == X("bar", 7));
}
SUBCASE("emplace existing")
{
om.emplace(om.begin(), "foo", -1);
REQUIRE(om.size() == 2);
CHECK(om[0] == X("foo", 2));
CHECK(om[1] == X("bar", 7));
}
SUBCASE("erase one")
{
om.erase(om.begin());
REQUIRE(om.size() == 1);
CHECK(om[0] == X("bar", 7));
}
SUBCASE("erase range")
{
om.erase(om.begin(), om.begin()+1);
REQUIRE(om.size() == 1);
CHECK(om[0] == X("bar", 7));
}
SUBCASE("erase everything")
{
om.erase(om.begin(), om.end());
CHECK(om.empty());
}
SUBCASE("push_back")
{
om.push_back(MakeSmart<X>("zed",3));
REQUIRE(om.size() == 3);
CHECK(om[0] == X("foo", 2));
CHECK(om[1] == X("bar", 7));
CHECK(om[2] == X("zed", 3));
}
SUBCASE("push_back existing")
{
om.push_back(MakeSmart<X>("bar",77));
REQUIRE(om.size() == 2);
CHECK(om[0] == X("foo", 2));
CHECK(om[1] == X("bar", 7));
}
SUBCASE("pop_back")
{
om.pop_back();
REQUIRE(om.size() == 1);
CHECK(om[0] == X("foo", 2));
om.pop_back();
REQUIRE(om.empty());
}
SUBCASE("nth")
{
CHECK(om.nth(0) == om.begin());
CHECK(om.nth(0)->k == "foo");
CHECK(*om.nth(1) == X("bar", 7));
CHECK(om.nth(2) == om.end());
CHECK(om.index_of(om.nth(1)) == 1);
}
SUBCASE("map find")
{
CHECK(om.count("foo") == 1);
CHECK(om.count("baz") == 0);
CHECK(*om.find("foo") == X("foo", 2));
CHECK(om.find("baz") == om.end());
}
SUBCASE("iterator_to")
{
X& x = om[1];
CHECK(om.iterator_to(x) == om.nth(1));
}
SUBCASE("key_change")
{
om[0].k = "abc";
CHECK(om.count("abc") == 0); // wrong rb-tree
om.key_change(om.begin());
CHECK(om.count("abc") == 1); // fixed
}
}
CHECK(X::count == 0);
}
#if LIBSHIT_WITH_LUA
TEST_CASE("lua binding")
{
Lua::State vm;
auto om = MakeSmart<OM>();
vm.Push(om);
lua_setglobal(vm, "om");
vm.DoString("it = libshit.test.om_item_test");
SUBCASE("lua push_back")
{
vm.DoString("om:push_back(it('xy',2))");
CHECK(om->size() == 1);
CHECK(om->at(0) == X("xy", 2));
}
SUBCASE("lua insert")
{
vm.DoString("return om:insert(0, it('bar', 7))");
REQUIRE(lua_gettop(vm) == 2);
CHECK(vm.Get<bool>(-2));
CHECK(vm.Get<int>(-1) == 0);
lua_pop(vm, 2);
vm.DoString("return om:insert(0, it('foo', 2))");
REQUIRE(lua_gettop(vm) == 2);
CHECK(vm.Get<bool>(-2));
CHECK(vm.Get<int>(-1) == 0);
lua_pop(vm, 2);
vm.DoString("return om:insert(0, it('bar', 13))");
REQUIRE(lua_gettop(vm) == 2);
CHECK(!vm.Get<bool>(-2));
CHECK(vm.Get<int>(-1) == 1);
lua_pop(vm, 2);
CHECK(om->size() == 2);
CHECK(om->at(0) == X("foo", 2));
CHECK(om->at(1) == X("bar", 7));
}
SUBCASE("populate")
{
om->emplace_back("abc", 7);
om->emplace_back("xyz", -2);
om->emplace_back("foo", 5);
SUBCASE("get")
{
vm.DoString("return om[0]");
REQUIRE(lua_gettop(vm) == 1);
CHECK(vm.Get<X>(-1) == X("abc", 7));
lua_pop(vm, 1);
vm.DoString("return om[3]");
REQUIRE(lua_gettop(vm) == 1);
CHECK(lua_isnil(vm, -1));
lua_pop(vm, 1);
vm.DoString("return om.xyz");
REQUIRE(lua_gettop(vm) == 1);
CHECK(vm.Get<X>(-1) == X("xyz", -2));
lua_pop(vm, 1);
vm.DoString("return om.blahblah");
REQUIRE(lua_gettop(vm) == 1);
CHECK(lua_isnil(vm, -1));
lua_pop(vm, 1);
vm.DoString("return om[{}]");
REQUIRE(lua_gettop(vm) == 1);
CHECK(lua_isnil(vm, -1));
lua_pop(vm, 1);
}
SUBCASE("erase")
{
vm.DoString("return om:erase(1)");
REQUIRE(lua_gettop(vm) == 1);
CHECK(vm.Get<size_t>(-1) == 1);
CHECK(om->size() == 2);
CHECK(om->at(0) == X("abc", 7));
CHECK(om->at(1) == X("foo", 5));
lua_pop(vm, 1);
}
SUBCASE("erase range")
{
vm.DoString("return om:erase(1, 3)");
REQUIRE(lua_gettop(vm) == 1);
CHECK(vm.Get<size_t>(-1) == 1);
CHECK(om->size() == 1);
CHECK(om->at(0) == X("abc", 7));
lua_pop(vm, 1);
}
SUBCASE("remove")
{
vm.DoString("return om:remove(1)");
REQUIRE(lua_gettop(vm) == 1);
CHECK(vm.Get<X>(-1) == X("xyz", -2));
CHECK(om->size() == 2);
CHECK(om->at(0) == X("abc", 7));
CHECK(om->at(1) == X("foo", 5));
lua_pop(vm, 1);
}
SUBCASE("find")
{
vm.DoString("return om:find('xyz')");
REQUIRE(lua_gettop(vm) == 2);
CHECK(vm.Get<size_t>(-2) == 1);
CHECK(vm.Get<X>(-1) == X("xyz", -2));
lua_pop(vm, 2);
vm.DoString("return om:find('not')");
REQUIRE(lua_gettop(vm) == 1);
CHECK(lua_isnil(vm, -1));
lua_pop(vm, 1);
}
SUBCASE("to_table")
{
vm.DoString(R"(
local t = om:to_table()
assert(type(t) == 'table')
assert(t[0].k == 'abc' and t[0].v == 7)
assert(t[1].k == 'xyz' and t[1].v == -2)
assert(t[2].k == 'foo' and t[2].v == 5)
assert(t[3] == nil)
)");
}
SUBCASE("ipairs")
{
vm.DoString(R"(
local nexti = 0
local map = {[0] = {k='abc',v=7}, {k='xyz',v=-2}, {k='foo',v=5} }
for i,v in ipairs(om) do
assert(i == nexti) nexti = i+1
assert(v.k == map[i].k)
assert(v.v == map[i].v)
end
assert(nexti == 3)
)");
}
}
}
#endif
TEST_SUITE_END();
}
#include <libshit/container/ordered_map.lua.hpp>
LIBSHIT_ORDERED_MAP_LUAGEN(
om_item_test, Libshit::Test::OMItemTest, Libshit::Test::OMItemTestTraits);
#include "ordered_map.binding.hpp"