ordered_map.cpp (9003B)
1 #include "libshit/container/ordered_map.hpp" 2 3 #include "libshit/doctest.hpp" 4 #include "libshit/utils.hpp" 5 6 #include <ostream> 7 #include <string> 8 9 namespace Libshit::Test 10 { 11 TEST_SUITE_BEGIN("Libshit::OrderedMap"); 12 namespace 13 { 14 struct OMItemTest final : public OrderedMapItem, public Lua::DynamicObject 15 { 16 LIBSHIT_DYNAMIC_OBJECT; 17 public: 18 OMItemTest(std::string k, int v) : k{Move(k)}, v{v} { ++count; } 19 ~OMItemTest() { --count; } 20 OMItemTest(const OMItemTest&) = delete; 21 void operator=(const OMItemTest&) = delete; 22 23 std::string k; 24 int v; 25 static size_t count; 26 27 bool operator==(const OMItemTest& o) const noexcept 28 { return k == o.k && v == o.v; } 29 }; 30 std::ostream& operator<<(std::ostream& os, const OMItemTest& i) 31 { return os << "OMItemTest{" << i.k << ", " << i.v << '}'; } 32 size_t OMItemTest::count; 33 34 struct OMItemTestTraits 35 { 36 using type = std::string; 37 const std::string& operator()(const OMItemTest& x) { return x.k; } 38 }; 39 40 using X = OMItemTest; 41 using OM = OrderedMap<OMItemTest, OMItemTestTraits>; 42 } 43 44 TEST_CASE("basic test") 45 { 46 X::count = 0; 47 { 48 OM om; 49 CHECK(om.empty()); 50 CHECK(om.size() == 0); 51 om.emplace_back("foo",2); 52 om.emplace_back("bar",7); 53 CHECK(!om.empty()); 54 CHECK(om.size() == 2); 55 56 SUBCASE("at") 57 { 58 CHECK(om.at(0) == X("foo", 2)); 59 CHECK(om.at(1) == X("bar", 7)); 60 CHECK_THROWS(om.at(2)); 61 } 62 63 SUBCASE("operator[]") 64 { 65 CHECK(om[0] == X("foo", 2)); 66 CHECK(om[1] == X("bar", 7)); 67 } 68 69 SUBCASE("front/back") 70 { 71 CHECK(om.front() == X("foo", 2)); 72 CHECK(om.back() == X("bar", 7)); 73 } 74 75 SUBCASE("iterator") 76 { 77 auto it = om.begin(); 78 REQUIRE(it != om.end()); 79 CHECK(it->k == "foo"); 80 ++it; 81 REQUIRE(it != om.end()); 82 CHECK(*it == X("bar", 7)); 83 ++it; 84 CHECK(it == om.end()); 85 } 86 87 SUBCASE("reserve") 88 { 89 CHECK(om.capacity() >= 2); 90 om.reserve(10); 91 REQUIRE(om.size() == 2); 92 CHECK(om.capacity() >= 10); 93 94 CHECK(om[1] == X("bar", 7)); 95 } 96 97 SUBCASE("clear") 98 { 99 om.clear(); 100 CHECK(om.empty()); 101 } 102 103 SUBCASE("insert") 104 { 105 om.insert(om.begin()+1, MakeSmart<X>("def", 9)); 106 REQUIRE(om.size() == 3); 107 CHECK(om[0] == X("foo", 2)); 108 CHECK(om[1] == X("def", 9)); 109 CHECK(om[2] == X("bar", 7)); 110 } 111 112 SUBCASE("insert existing") 113 { 114 om.insert(om.begin(), MakeSmart<X>("bar", -1)); 115 REQUIRE(om.size() == 2); 116 CHECK(om[0] == X("foo", 2)); 117 CHECK(om[1] == X("bar", 7)); 118 } 119 120 SUBCASE("emplace") 121 { 122 om.emplace(om.begin(), "aa", 5); 123 REQUIRE(om.size() == 3); 124 CHECK(om[0] == X("aa", 5)); 125 CHECK(om[1] == X("foo", 2)); 126 CHECK(om[2] == X("bar", 7)); 127 } 128 129 SUBCASE("emplace existing") 130 { 131 om.emplace(om.begin(), "foo", -1); 132 REQUIRE(om.size() == 2); 133 CHECK(om[0] == X("foo", 2)); 134 CHECK(om[1] == X("bar", 7)); 135 } 136 137 SUBCASE("erase one") 138 { 139 om.erase(om.begin()); 140 REQUIRE(om.size() == 1); 141 CHECK(om[0] == X("bar", 7)); 142 } 143 144 SUBCASE("erase range") 145 { 146 om.erase(om.begin(), om.begin()+1); 147 REQUIRE(om.size() == 1); 148 CHECK(om[0] == X("bar", 7)); 149 } 150 151 SUBCASE("erase everything") 152 { 153 om.erase(om.begin(), om.end()); 154 CHECK(om.empty()); 155 } 156 157 SUBCASE("push_back") 158 { 159 om.push_back(MakeSmart<X>("zed",3)); 160 REQUIRE(om.size() == 3); 161 CHECK(om[0] == X("foo", 2)); 162 CHECK(om[1] == X("bar", 7)); 163 CHECK(om[2] == X("zed", 3)); 164 } 165 166 SUBCASE("push_back existing") 167 { 168 om.push_back(MakeSmart<X>("bar",77)); 169 REQUIRE(om.size() == 2); 170 CHECK(om[0] == X("foo", 2)); 171 CHECK(om[1] == X("bar", 7)); 172 } 173 174 SUBCASE("pop_back") 175 { 176 om.pop_back(); 177 REQUIRE(om.size() == 1); 178 CHECK(om[0] == X("foo", 2)); 179 om.pop_back(); 180 REQUIRE(om.empty()); 181 } 182 183 SUBCASE("nth") 184 { 185 CHECK(om.nth(0) == om.begin()); 186 CHECK(om.nth(0)->k == "foo"); 187 CHECK(*om.nth(1) == X("bar", 7)); 188 CHECK(om.nth(2) == om.end()); 189 CHECK(om.index_of(om.nth(1)) == 1); 190 } 191 192 SUBCASE("map find") 193 { 194 CHECK(om.count("foo") == 1); 195 CHECK(om.count("baz") == 0); 196 CHECK(*om.find("foo") == X("foo", 2)); 197 CHECK(om.find("baz") == om.end()); 198 } 199 200 SUBCASE("iterator_to") 201 { 202 X& x = om[1]; 203 CHECK(om.iterator_to(x) == om.nth(1)); 204 } 205 206 SUBCASE("key_change") 207 { 208 om[0].k = "abc"; 209 CHECK(om.count("abc") == 0); // wrong rb-tree 210 om.key_change(om.begin()); 211 CHECK(om.count("abc") == 1); // fixed 212 } 213 } 214 CHECK(X::count == 0); 215 } 216 217 #if LIBSHIT_WITH_LUA 218 TEST_CASE("lua binding") 219 { 220 Lua::State vm; 221 auto om = MakeSmart<OM>(); 222 vm.Push(om); 223 lua_setglobal(vm, "om"); 224 vm.DoString("it = libshit.test.om_item_test"); 225 226 SUBCASE("lua push_back") 227 { 228 vm.DoString("om:push_back(it('xy',2))"); 229 CHECK(om->size() == 1); 230 CHECK(om->at(0) == X("xy", 2)); 231 } 232 233 SUBCASE("lua insert") 234 { 235 vm.DoString("return om:insert(0, it('bar', 7))"); 236 REQUIRE(lua_gettop(vm) == 2); 237 CHECK(vm.Get<bool>(-2)); 238 CHECK(vm.Get<int>(-1) == 0); 239 lua_pop(vm, 2); 240 241 vm.DoString("return om:insert(0, it('foo', 2))"); 242 REQUIRE(lua_gettop(vm) == 2); 243 CHECK(vm.Get<bool>(-2)); 244 CHECK(vm.Get<int>(-1) == 0); 245 lua_pop(vm, 2); 246 247 vm.DoString("return om:insert(0, it('bar', 13))"); 248 REQUIRE(lua_gettop(vm) == 2); 249 CHECK(!vm.Get<bool>(-2)); 250 CHECK(vm.Get<int>(-1) == 1); 251 lua_pop(vm, 2); 252 253 CHECK(om->size() == 2); 254 CHECK(om->at(0) == X("foo", 2)); 255 CHECK(om->at(1) == X("bar", 7)); 256 } 257 258 SUBCASE("populate") 259 { 260 om->emplace_back("abc", 7); 261 om->emplace_back("xyz", -2); 262 om->emplace_back("foo", 5); 263 264 SUBCASE("get") 265 { 266 vm.DoString("return om[0]"); 267 REQUIRE(lua_gettop(vm) == 1); 268 CHECK(vm.Get<X>(-1) == X("abc", 7)); 269 lua_pop(vm, 1); 270 271 vm.DoString("return om[3]"); 272 REQUIRE(lua_gettop(vm) == 1); 273 CHECK(lua_isnil(vm, -1)); 274 lua_pop(vm, 1); 275 276 vm.DoString("return om.xyz"); 277 REQUIRE(lua_gettop(vm) == 1); 278 CHECK(vm.Get<X>(-1) == X("xyz", -2)); 279 lua_pop(vm, 1); 280 281 vm.DoString("return om.blahblah"); 282 REQUIRE(lua_gettop(vm) == 1); 283 CHECK(lua_isnil(vm, -1)); 284 lua_pop(vm, 1); 285 286 vm.DoString("return om[{}]"); 287 REQUIRE(lua_gettop(vm) == 1); 288 CHECK(lua_isnil(vm, -1)); 289 lua_pop(vm, 1); 290 } 291 292 SUBCASE("erase") 293 { 294 vm.DoString("return om:erase(1)"); 295 REQUIRE(lua_gettop(vm) == 1); 296 297 CHECK(vm.Get<size_t>(-1) == 1); 298 CHECK(om->size() == 2); 299 CHECK(om->at(0) == X("abc", 7)); 300 CHECK(om->at(1) == X("foo", 5)); 301 lua_pop(vm, 1); 302 } 303 304 SUBCASE("erase range") 305 { 306 vm.DoString("return om:erase(1, 3)"); 307 REQUIRE(lua_gettop(vm) == 1); 308 309 CHECK(vm.Get<size_t>(-1) == 1); 310 CHECK(om->size() == 1); 311 CHECK(om->at(0) == X("abc", 7)); 312 lua_pop(vm, 1); 313 } 314 315 SUBCASE("remove") 316 { 317 vm.DoString("return om:remove(1)"); 318 REQUIRE(lua_gettop(vm) == 1); 319 320 CHECK(vm.Get<X>(-1) == X("xyz", -2)); 321 CHECK(om->size() == 2); 322 CHECK(om->at(0) == X("abc", 7)); 323 CHECK(om->at(1) == X("foo", 5)); 324 lua_pop(vm, 1); 325 } 326 327 SUBCASE("find") 328 { 329 vm.DoString("return om:find('xyz')"); 330 REQUIRE(lua_gettop(vm) == 2); 331 CHECK(vm.Get<size_t>(-2) == 1); 332 CHECK(vm.Get<X>(-1) == X("xyz", -2)); 333 lua_pop(vm, 2); 334 335 vm.DoString("return om:find('not')"); 336 REQUIRE(lua_gettop(vm) == 1); 337 CHECK(lua_isnil(vm, -1)); 338 lua_pop(vm, 1); 339 } 340 341 SUBCASE("to_table") 342 { 343 vm.DoString(R"( 344 local t = om:to_table() 345 assert(type(t) == 'table') 346 assert(t[0].k == 'abc' and t[0].v == 7) 347 assert(t[1].k == 'xyz' and t[1].v == -2) 348 assert(t[2].k == 'foo' and t[2].v == 5) 349 assert(t[3] == nil) 350 )"); 351 } 352 353 SUBCASE("ipairs") 354 { 355 vm.DoString(R"( 356 local nexti = 0 357 local map = {[0] = {k='abc',v=7}, {k='xyz',v=-2}, {k='foo',v=5} } 358 for i,v in ipairs(om) do 359 assert(i == nexti) nexti = i+1 360 assert(v.k == map[i].k) 361 assert(v.v == map[i].v) 362 end 363 assert(nexti == 3) 364 )"); 365 } 366 } 367 } 368 #endif 369 TEST_SUITE_END(); 370 } 371 372 #include <libshit/container/ordered_map.lua.hpp> 373 LIBSHIT_ORDERED_MAP_LUAGEN( 374 om_item_test, Libshit::Test::OMItemTest, Libshit::Test::OMItemTestTraits); 375 #include "ordered_map.binding.hpp"