dragonfly/core/interpreter_test.cc

170 lines
3.5 KiB
C++

// Copyright 2022, Roman Gershman. All rights reserved.
// See LICENSE for licensing terms.
//
#include "core/interpreter.h"
extern "C" {
#include <lauxlib.h>
#include <lua.h>
}
#include <absl/strings/str_cat.h>
#include <gmock/gmock.h>
#include "base/gtest.h"
#include "base/logging.h"
namespace dfly {
using namespace std;
class TestSerializer : public ObjectExplorer {
public:
string res;
void OnBool(bool b) final {
absl::StrAppend(&res, "bool(", b, ") ");
OnItem();
}
void OnString(std::string_view str) final {
absl::StrAppend(&res, "str(", str, ") ");
OnItem();
}
void OnDouble(double d) final {
absl::StrAppend(&res, "d(", d, ") ");
OnItem();
}
void OnInt(int64_t val) final {
absl::StrAppend(&res, "i(", val, ") ");
OnItem();
}
void OnArrayStart(unsigned len) final {
absl::StrAppend(&res, "[");
}
void OnArrayEnd() final {
if (res.back() == ' ')
res.pop_back();
absl::StrAppend(&res, "] ");
}
void OnNil() final {
absl::StrAppend(&res, "nil ");
}
void OnStatus(std::string_view str) {
absl::StrAppend(&res, "status(", str, ") ");
}
void OnError(std::string_view str) {
absl::StrAppend(&res, "err(", str, ") ");
}
private:
void OnItem() {
}
};
class InterpreterTest : public ::testing::Test {
protected:
InterpreterTest() {
}
lua_State* lua() {
return intptr_.lua();
}
void RunInline(string_view buf, const char* name) {
CHECK_EQ(0, luaL_loadbuffer(lua(), buf.data(), buf.size(), name));
CHECK_EQ(0, lua_pcall(lua(), 0, 0, 0));
}
bool Serialize(string* err) {
ser_.res.clear();
bool res = intptr_.Serialize(&ser_, err);
if (!ser_.res.empty())
ser_.res.pop_back();
return res;
}
bool Execute(string_view script);
Interpreter intptr_;
TestSerializer ser_;
string error_;
};
bool InterpreterTest::Execute(string_view script) {
char buf[48];
return intptr_.Execute(script, buf, &error_) && Serialize(&error_);
}
TEST_F(InterpreterTest, Basic) {
RunInline(R"(
function foo(n)
return n,n+1
end)",
"code1");
int type = lua_getglobal(lua(), "foo");
ASSERT_EQ(LUA_TFUNCTION, type);
lua_pushnumber(lua(), 42);
lua_pcall(lua(), 1, 2, 0);
int val1 = lua_tointeger(lua(), -1);
int val2 = lua_tointeger(lua(), -2);
lua_pop(lua(), 2);
EXPECT_EQ(43, val1);
EXPECT_EQ(42, val2);
EXPECT_EQ(0, lua_gettop(lua()));
}
TEST_F(InterpreterTest, Add) {
string res1, res2;
EXPECT_TRUE(intptr_.AddFunction("return 0", &res1));
EXPECT_EQ(0, lua_gettop(lua()));
EXPECT_FALSE(intptr_.AddFunction("foobar", &res2));
EXPECT_THAT(res2, testing::HasSubstr("syntax error"));
EXPECT_EQ(0, lua_gettop(lua()));
}
// Test cases taken from scripting.tcl
TEST_F(InterpreterTest, Execute) {
EXPECT_TRUE(Execute("return 42"));
EXPECT_EQ("i(42)", ser_.res);
EXPECT_TRUE(Execute("return 'hello'"));
EXPECT_EQ("str(hello)", ser_.res);
// Breaks compatibility.
EXPECT_TRUE(Execute("return 100.5"));
EXPECT_EQ("d(100.5)", ser_.res);
EXPECT_TRUE(Execute("return true"));
EXPECT_EQ("bool(1)", ser_.res);
EXPECT_TRUE(Execute("return false"));
EXPECT_EQ("bool(0)", ser_.res);
EXPECT_TRUE(Execute("return {ok='fine'}"));
EXPECT_EQ("status(fine)", ser_.res);
EXPECT_TRUE(Execute("return {err= 'bla'}"));
EXPECT_EQ("err(bla)", ser_.res);
EXPECT_TRUE(Execute("return {1, 2, nil, 3}"));
EXPECT_EQ("[i(1) i(2) nil i(3)]", ser_.res);
EXPECT_TRUE(Execute("return {1,2,3,'ciao',{1,2}}"));
EXPECT_EQ("[i(1) i(2) i(3) str(ciao) [i(1) i(2)]]", ser_.res);
}
} // namespace dfly