// https://github.com/vinniefalco/LuaBridge
 
// Copyright 2019, Dmitry Tarakanov
 
// Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>
 
// Copyright 2007, Nathan Reed
 
// SPDX-License-Identifier: MIT
 
 
 
#pragma once
 
 
 
#include "Lua/LuaLibrary.h"
 
 
 
#include "LuaBridge/LuaBridge.h"
 
 
 
#include <gtest/gtest.h>
 
 
 
#include <stdexcept>
 
 
 
// traceback function, adapted from lua.c
 
// when a runtime error occurs, this will append the call stack to the error message
 
//
 
inline int traceback(lua_State* L)
 
{
 
    // look up Lua's 'debug.traceback' function
 
    lua_getglobal(L, "debug");
 
    if (!lua_istable(L, -1))
 
    {
 
        lua_pop(L, 1);
 
        return 1;
 
    }
 
    lua_getfield(L, -1, "traceback");
 
    if (!lua_isfunction(L, -1))
 
    {
 
        lua_pop(L, 2);
 
        return 1;
 
    }
 
    lua_pushvalue(L, 1); /* pass error message */
 
    lua_pushinteger(L, 2); /* skip this function and traceback */
 
    lua_call(L, 2, 1); /* call debug.traceback */
 
    return 1;
 
}
 
 
 
/// Base test class. Introduces the global 'result' variable,
 
/// used for checking of C++ - Lua interoperation.
 
///
 
struct TestBase : public ::testing::Test
 
{
 
    lua_State* L = nullptr;
 
 
 
    void SetUp() override
 
    {
 
        L = nullptr;
 
        L = luaL_newstate();
 
        luaL_openlibs(L);
 
        lua_pushcfunction(L, &traceback);
 
    }
 
 
 
    void TearDown() override
 
    {
 
        if (L != nullptr)
 
        {
 
            lua_close(L);
 
        }
 
    }
 
 
 
    void runLua(const std::string& script) const
 
    {
 
        if (luaL_loadstring(L, script.c_str()) != 0)
 
        {
 
            throw std::runtime_error(lua_tostring(L, -1));
 
        }
 
 
 
        if (lua_pcall(L, 0, 0, -2) != 0)
 
        {
 
            throw std::runtime_error(lua_tostring(L, -1));
 
        }
 
    }
 
 
 
    template<class T = luabridge::LuaRef>
 
    T result() const
 
    {
 
        return luabridge::getGlobal(L, "result").cast<T>();
 
    }
 
 
 
    void resetResult() const { luabridge::setGlobal(L, luabridge::LuaRef(L), "result"); }
 
 
 
    void printStack() const
 
    {
 
        std::cerr << "===== Stack =====\n";
 
        for (int i = 1; i <= lua_gettop(L); ++i)
 
        {
 
            std::cerr << "@" << i << " = " << luabridge::LuaRef::fromStack(L, i) << "\n";
 
        }
 
    }
 
};