Skip to content

Commit 84f966f

Browse files
committed
More test exercising
1 parent cb9af5d commit 84f966f

1 file changed

Lines changed: 187 additions & 0 deletions

File tree

Tests/Source/Tests.cpp

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,26 @@ T identityCFunction(T value)
2525
{
2626
return value;
2727
}
28+
29+
int testGetter777(lua_State* L)
30+
{
31+
lua_pushinteger(L, 777);
32+
return 1;
33+
}
34+
35+
int testSetValueWithSelf(lua_State* L)
36+
{
37+
lua_pushvalue(L, 2);
38+
lua_setglobal(L, "captured");
39+
return 0;
40+
}
41+
42+
int testSetValueStatic(lua_State* L)
43+
{
44+
lua_pushvalue(L, 1);
45+
lua_setglobal(L, "captured");
46+
return 0;
47+
}
2848
} // namespace
2949

3050
struct LuaBridgeTest : TestBase
@@ -517,6 +537,173 @@ TEST_F(LuaBridgeTest, CallWithHandlerTypedReturnAndStackRestore)
517537
}
518538
}
519539

540+
TEST_F(LuaBridgeTest, IndexMetamethodSimple_ObjectFallbackBranches)
541+
{
542+
// Exercise the table-based fallback branch of index_metamethod_simple<true>.
543+
auto callIndex = [this](const char* key) -> bool
544+
{
545+
lua_newtable(L); // self
546+
547+
lua_newtable(L); // mt
548+
lua_newtable(L); // propget
549+
luabridge::lua_pushcfunction_x(L, &testGetter777, "testGetter777");
550+
lua_setfield(L, -2, "viaGetter");
551+
luabridge::lua_rawsetp_x(L, -2, luabridge::detail::getPropgetKey());
552+
lua_setmetatable(L, -2);
553+
554+
lua_newtable(L); // upvalue #1 (pg)
555+
lua_newtable(L); // upvalue #2 (mt)
556+
luabridge::lua_pushcclosure_x(L, &luabridge::detail::index_metamethod_simple<true>, "index_metamethod_simple_object", 2);
557+
558+
lua_pushvalue(L, -2); // self
559+
lua_pushstring(L, key);
560+
const int code = lua_pcall(L, 2, 1, 0);
561+
562+
lua_remove(L, -2); // remove self, keep result or error
563+
return code == LUABRIDGE_LUA_OK;
564+
};
565+
566+
// 1) Value found in self table branch.
567+
auto tmpSelf = luabridge::newTable(L);
568+
tmpSelf["selfField"] = 55;
569+
luabridge::setGlobal(L, tmpSelf, "tmpSelf");
570+
571+
lua_getglobal(L, "tmpSelf"); // self
572+
lua_newtable(L); // mt
573+
lua_newtable(L); // propget
574+
luabridge::lua_rawsetp_x(L, -2, luabridge::detail::getPropgetKey());
575+
lua_setmetatable(L, -2);
576+
lua_newtable(L);
577+
lua_newtable(L);
578+
luabridge::lua_pushcclosure_x(L, &luabridge::detail::index_metamethod_simple<true>, "index_metamethod_simple_object", 2);
579+
lua_pushvalue(L, -2);
580+
lua_pushstring(L, "selfField");
581+
ASSERT_EQ(LUABRIDGE_LUA_OK, lua_pcall(L, 2, 1, 0));
582+
ASSERT_TRUE(lua_isnumber(L, -1));
583+
ASSERT_EQ(55, static_cast<int>(lua_tointeger(L, -1)));
584+
lua_pop(L, 2);
585+
586+
// 2) Value found in metatable branch.
587+
lua_newtable(L); // self
588+
lua_newtable(L); // mt
589+
lua_pushinteger(L, 66);
590+
lua_setfield(L, -2, "metaField");
591+
lua_newtable(L);
592+
luabridge::lua_rawsetp_x(L, -2, luabridge::detail::getPropgetKey());
593+
lua_setmetatable(L, -2);
594+
lua_newtable(L);
595+
lua_newtable(L);
596+
luabridge::lua_pushcclosure_x(L, &luabridge::detail::index_metamethod_simple<true>, "index_metamethod_simple_object", 2);
597+
lua_pushvalue(L, -2);
598+
lua_pushstring(L, "metaField");
599+
ASSERT_EQ(LUABRIDGE_LUA_OK, lua_pcall(L, 2, 1, 0));
600+
ASSERT_TRUE(lua_isnumber(L, -1));
601+
ASSERT_EQ(66, static_cast<int>(lua_tointeger(L, -1)));
602+
lua_pop(L, 2);
603+
604+
// 3) Value resolved via propget callable branch.
605+
ASSERT_TRUE(callIndex("viaGetter"));
606+
ASSERT_TRUE(lua_isnumber(L, -1));
607+
ASSERT_EQ(777, static_cast<int>(lua_tointeger(L, -1)));
608+
lua_pop(L, 1);
609+
610+
// 4) Unknown key returns nil at final fallback.
611+
ASSERT_TRUE(callIndex("doesNotExist"));
612+
ASSERT_TRUE(lua_isnil(L, -1));
613+
lua_pop(L, 1);
614+
}
615+
616+
TEST_F(LuaBridgeTest, NewIndexMetamethodSimple_FallbackBranches)
617+
{
618+
// Object variant fallback path (self is table).
619+
{
620+
lua_newtable(L); // self
621+
lua_newtable(L); // mt
622+
lua_newtable(L); // propset
623+
luabridge::lua_pushcfunction_x(L, &testSetValueWithSelf, "testSetValueWithSelf");
624+
lua_setfield(L, -2, "x");
625+
luabridge::lua_rawsetp_x(L, -2, luabridge::detail::getPropsetKey());
626+
lua_setmetatable(L, -2);
627+
628+
lua_newtable(L); // upvalue #1
629+
luabridge::lua_pushcclosure_x(L, &luabridge::detail::newindex_metamethod_simple<true>, "newindex_metamethod_simple_object", 1);
630+
lua_pushvalue(L, -2);
631+
lua_pushstring(L, "x");
632+
lua_pushinteger(L, 123);
633+
ASSERT_EQ(LUABRIDGE_LUA_OK, lua_pcall(L, 3, 0, 0));
634+
lua_pop(L, 1);
635+
636+
auto captured = luabridge::getGlobal(L, "captured");
637+
ASSERT_TRUE(captured.isNumber());
638+
ASSERT_EQ(123, captured.unsafe_cast<int>());
639+
}
640+
641+
// Object variant fallback error when propset table exists but key is missing.
642+
{
643+
lua_newtable(L); // self
644+
lua_newtable(L); // mt
645+
lua_newtable(L); // propset
646+
luabridge::lua_rawsetp_x(L, -2, luabridge::detail::getPropsetKey());
647+
lua_setmetatable(L, -2);
648+
649+
lua_newtable(L); // upvalue #1
650+
luabridge::lua_pushcclosure_x(L, &luabridge::detail::newindex_metamethod_simple<true>, "newindex_metamethod_simple_object", 1);
651+
lua_pushvalue(L, -2);
652+
lua_pushstring(L, "missing");
653+
lua_pushinteger(L, 1);
654+
ASSERT_NE(LUABRIDGE_LUA_OK, lua_pcall(L, 3, 0, 0));
655+
auto err = lua_tostring(L, -1);
656+
ASSERT_NE(nullptr, err);
657+
EXPECT_TRUE(std::string(err).find("no writable member 'missing'") != std::string::npos);
658+
lua_pop(L, 2);
659+
}
660+
661+
// Static variant fallback path (self is userdata to bypass simple table fast path).
662+
{
663+
lua_newuserdata(L, 1); // self
664+
lua_newtable(L); // mt
665+
lua_newtable(L); // propset
666+
luabridge::lua_pushcfunction_x(L, &testSetValueStatic, "testSetValueStatic");
667+
lua_setfield(L, -2, "x");
668+
luabridge::lua_rawsetp_x(L, -2, luabridge::detail::getPropsetKey());
669+
lua_setmetatable(L, -2);
670+
671+
lua_newtable(L); // upvalue #1
672+
luabridge::lua_pushcclosure_x(L, &luabridge::detail::newindex_metamethod_simple<false>, "newindex_metamethod_simple_static", 1);
673+
lua_pushvalue(L, -2);
674+
lua_pushstring(L, "x");
675+
lua_pushinteger(L, 321);
676+
ASSERT_EQ(LUABRIDGE_LUA_OK, lua_pcall(L, 3, 0, 0));
677+
lua_pop(L, 1);
678+
679+
auto captured = luabridge::getGlobal(L, "captured");
680+
ASSERT_TRUE(captured.isNumber());
681+
ASSERT_EQ(321, captured.unsafe_cast<int>());
682+
}
683+
}
684+
685+
TEST_F(LuaBridgeTest, ReadOnlyErrorAndArgumentDecodeRaiseLuaError)
686+
{
687+
// Explicitly exercise read_only_error -> raise_lua_error path.
688+
lua_pushstring(L, "locked");
689+
luabridge::lua_pushcclosure_x(L, &luabridge::detail::read_only_error, "read_only_error", 1);
690+
ASSERT_NE(LUABRIDGE_LUA_OK, lua_pcall(L, 0, 0, 0));
691+
{
692+
auto err = lua_tostring(L, -1);
693+
ASSERT_NE(nullptr, err);
694+
EXPECT_TRUE(std::string(err).find("'locked' is read-only") != std::string::npos);
695+
}
696+
lua_pop(L, 1);
697+
698+
// Exercise argument decode error path in invocation wrappers (raise_lua_error at decode).
699+
luabridge::getGlobalNamespace(L)
700+
.addFunction("expectInt", +[](int) { return 0; });
701+
702+
auto [ok, err] = runLuaCaptureError("expectInt('not an int')");
703+
EXPECT_FALSE(ok);
704+
EXPECT_TRUE(err.find("Error decoding argument #1") != std::string::npos);
705+
}
706+
520707
TEST_F(LuaBridgeTest, InvokePassingUnregisteredClassShouldThrowAndRestoreStack)
521708
{
522709
class Unregistered {} unregistered;

0 commit comments

Comments
 (0)