Lua tests (#158)

* Fix float comparison in tests

* Lua tests
This commit is contained in:
Gawaboumga 2018-01-31 15:27:23 +01:00 committed by Jérôme Leclercq
parent 7c1ffea19c
commit dfd28160a0
9 changed files with 595 additions and 5 deletions

View File

@ -0,0 +1,249 @@
#include <Nazara/Lua/LuaClass.hpp>
#include <Catch/catch.hpp>
#include <Nazara/Core/HandledObject.hpp>
#include <Nazara/Core/ObjectHandle.hpp>
class Test
{
public:
Test() = default;
Test(const Test& other) = default;
Test& operator=(const Test& other) = default;
virtual ~Test() = default;
Test(int i, bool j = false) :
m_i(i),
m_j(j)
{
}
virtual int GetI() const
{
return m_i;
}
bool GetJ() const
{
return m_j;
}
int GetDefault(int defaultValue = 0)
{
return defaultValue;
}
static int StaticMethodWithArguments(int a, int b)
{
return a + b;
}
private:
int m_i;
bool m_j;
};
class InheritTest : public Test
{
public:
InheritTest() :
Test(5, true)
{
}
int GetI() const override
{
return Test::GetI() + 3;
}
};
class TestWithHandle : public Nz::HandledObject<TestWithHandle>
{
public:
int GetI() const
{
return m_i;
}
int GetDefault(int defaultValue = 0)
{
return defaultValue;
}
private:
int m_i = 8;
};
inline unsigned int LuaImplQueryArg(const Nz::LuaState& instance, int index, Test* arg, Nz::TypeTag<Test>)
{
REQUIRE(instance.IsOfType(index, "Test"));
*arg = *static_cast<Test*>(instance.ToUserdata(index));
return 1;
}
inline unsigned int LuaImplQueryArg(const Nz::LuaState& instance, int index, Nz::ObjectHandle<TestWithHandle>* arg, Nz::TypeTag<Nz::ObjectHandle<TestWithHandle>>)
{
REQUIRE(instance.IsOfType(index, "TestWithHandle"));
*arg = *static_cast<Nz::ObjectHandle<TestWithHandle>*>(instance.ToUserdata(index));
return 1;
}
inline unsigned int LuaImplQueryArg(const Nz::LuaState& instance, int index, InheritTest* arg, Nz::TypeTag<InheritTest>)
{
REQUIRE(instance.IsOfType(index, "InheritTest"));
*arg = *static_cast<InheritTest*>(instance.ToUserdata(index));
return 1;
}
SCENARIO("LuaClass", "[LUA][LUACLASS]")
{
GIVEN("One lua class for our Test class")
{
Nz::LuaInstance luaInstance;
Nz::LuaClass<Test> test;
Nz::LuaClass<InheritTest> inheritTest;
using TestHandle = Nz::ObjectHandle<TestWithHandle>;
Nz::LuaClass<TestHandle> testHandle;
WHEN("We bind the methods")
{
test.Reset("Test");
test.BindDefaultConstructor();
test.SetConstructor([] (Nz::LuaState& lua, Test* instance, std::size_t argumentCount)
{
std::size_t argCount = std::min<std::size_t>(argumentCount, 2U);
int argIndex = 1;
switch (argCount)
{
case 1:
{
int iValue = lua.Check<int>(&argIndex, 0);
Nz::PlacementNew(instance, iValue);
return true;
}
case 2:
{
int iValue = lua.Check<int>(&argIndex, 0);
bool j = lua.Check<bool>(&argIndex, false);
Nz::PlacementNew(instance, iValue, j);
return true;
}
}
lua.Error("No matching overload for Test constructor");
return false;
});
test.BindMethod("GetI", &Test::GetI);
test.BindMethod("GetJ", &Test::GetJ);
test.BindMethod("GetDefault", &Test::GetDefault, 0);
test.BindStaticMethod("StaticMethodWithArguments", [] (Nz::LuaState& state) -> int
{
int argIndex = 1;
int result = Test::StaticMethodWithArguments(state.Check<int>(&argIndex), state.Check<int>(&argIndex));
state.Push(result);
return 1;
});
test.Register(luaInstance);
THEN("We should be able to call them")
{
int value = 1;
int staticResult = value + value;
luaInstance.PushFunction([=](Nz::LuaState& state) -> int
{
int argIndex = 1;
Test result = state.Check<Test>(&argIndex);
CHECK(result.GetI() == value);
CHECK_FALSE(result.GetJ());
return 1;
});
luaInstance.SetGlobal("CheckTest");
luaInstance.PushFunction([=](Nz::LuaState& state) -> int
{
int argIndex = 1;
int result = state.Check<int>(&argIndex);
CHECK(result == staticResult);
return 1;
});
luaInstance.SetGlobal("CheckStatic");
luaInstance.PushFunction([=](Nz::LuaState& state) -> int
{
int argIndex = 1;
Test result = state.Check<Test>(&argIndex);
CHECK(result.GetI() == staticResult);
CHECK(result.GetJ());
return 1;
});
luaInstance.SetGlobal("CheckFinalTest");
REQUIRE(luaInstance.ExecuteFromFile("resources/Engine/Lua/LuaClass.lua"));
REQUIRE(luaInstance.GetGlobal("test_Test") == Nz::LuaType_Function);
luaInstance.Call(0);
}
AND_THEN("With a subclass")
{
inheritTest.Reset("InheritTest");
inheritTest.Inherit(test);
inheritTest.BindDefaultConstructor();
inheritTest.Register(luaInstance);
luaInstance.PushFunction([=](Nz::LuaState& state) -> int
{
int argIndex = 1;
InheritTest result = state.Check<InheritTest>(&argIndex);
CHECK(result.GetI() == 8);
CHECK(result.GetJ());
return 1;
});
luaInstance.SetGlobal("CheckInheritTest");
REQUIRE(luaInstance.ExecuteFromFile("resources/Engine/Lua/LuaClass.lua"));
REQUIRE(luaInstance.GetGlobal("test_InheritTest") == Nz::LuaType_Function);
luaInstance.Call(0);
}
}
WHEN("We bind the object with Handle")
{
int defaultValue = 5;
testHandle.Reset("TestHandle");
testHandle.BindMethod("IsValid", &TestHandle::IsValid);
testHandle.BindMethod("GetI", &TestWithHandle::GetI);
testHandle.BindMethod("GetDefault", &TestWithHandle::GetDefault, defaultValue);
testHandle.Register(luaInstance);
THEN("We can ensure the following properties")
{
luaInstance.PushFunction([=](Nz::LuaState& state) -> int
{
int argIndex = 1;
TestHandle result = state.Check<TestHandle>(&argIndex);
CHECK(result->GetI() == 8);
CHECK(result->GetDefault() == defaultValue);
return 1;
});
luaInstance.SetGlobal("CheckTestHandle");
REQUIRE(luaInstance.ExecuteFromFile("resources/Engine/Lua/LuaClass.lua"));
REQUIRE(luaInstance.GetGlobal("test_TestHandle") == Nz::LuaType_Function);
luaInstance.Call(0);
}
}
}
}

View File

@ -0,0 +1,31 @@
#include <Nazara/Lua/LuaCoroutine.hpp>
#include <Catch/catch.hpp>
#include <Nazara/Lua/LuaInstance.hpp>
SCENARIO("LuaCoroutine", "[LUA][LUACOROUTINE]")
{
GIVEN("One lua with coroutine")
{
Nz::LuaInstance luaInstance;
luaInstance.LoadLibraries(Nz::LuaLib_Coroutine);
REQUIRE(luaInstance.ExecuteFromFile("resources/Engine/Lua/LuaCoroutine.lua"));
Nz::LuaCoroutine coroutine = luaInstance.NewCoroutine();
REQUIRE(coroutine.GetGlobal("infinite") == Nz::LuaType_Function);
coroutine.PushInteger(4);
CHECK(coroutine.Call(1));
coroutine.Resume();
CHECK(coroutine.CheckInteger(1) == 1);
coroutine.Resume();
CHECK(coroutine.CheckInteger(1) == -1);
coroutine.Resume();
CHECK(coroutine.CheckInteger(1) == 0);
coroutine.Resume();
CHECK(coroutine.CheckInteger(1) == 1);
coroutine.Resume();
CHECK_FALSE(coroutine.CanResume());
}
}

View File

@ -0,0 +1,84 @@
#include <Nazara/Lua/LuaInstance.hpp>
#include <Catch/catch.hpp>
#include <iostream>
SCENARIO("LuaInstance", "[LUA][LUAINSTANCE]")
{
GIVEN("One lua instance")
{
Nz::LuaInstance luaInstance;
WHEN("We set memory constraint")
{
luaInstance.SetMemoryLimit(1'000);
THEN("If we excess memory, it should crash")
{
REQUIRE_THROWS_WITH(luaInstance.Execute(R"(
global t = {}
for 1,10000000 do
t[i] = i
end
)"), Catch::Matchers::Contains("memory"));
}
}
WHEN("We set time constraint")
{
luaInstance.SetTimeLimit(10);
THEN("If we excess time, it should produce an error")
{
CHECK(!luaInstance.Execute(R"(
function ack(M,N)
if M == 0 then return N + 1 end
if N == 0 then return ack(M-1,1) end
return ack(M-1,ack(M, N-1))
end
ack(100, 100)
)"));
REQUIRE_THAT(luaInstance.GetLastError().ToStdString(), Catch::Matchers::Contains("time"));
}
}
}
GIVEN("Two instances")
{
Nz::LuaInstance luaInstance;
int memoryLimit = 10'000;
int timeLimit = 1'000;
luaInstance.SetMemoryLimit(memoryLimit);
luaInstance.SetTimeLimit(timeLimit);
int value = 5;
luaInstance.PushInteger(value);
WHEN("We use move constructor")
{
Nz::LuaInstance movedInstance(std::move(luaInstance));
THEN("We should be able to retrieve the value")
{
CHECK(movedInstance.CheckInteger(1) == value);
CHECK(movedInstance.GetMemoryLimit() == memoryLimit);
CHECK(movedInstance.GetTimeLimit() == timeLimit);
}
}
WHEN("We use move assignment")
{
Nz::LuaInstance movedInstance;
movedInstance.PushInteger(value + 1);
movedInstance = std::move(luaInstance);
THEN("We should be able to retrieve the value")
{
CHECK(movedInstance.CheckInteger(1) == value);
CHECK(movedInstance.GetMemoryLimit() == memoryLimit);
CHECK(movedInstance.GetTimeLimit() == timeLimit);
}
}
}
}

View File

@ -0,0 +1,187 @@
#include <Nazara/Lua/LuaInstance.hpp>
#include <Catch/catch.hpp>
static int counter(lua_State* L)
{
Nz::LuaInstance& luaInstance = Nz::LuaInstance::GetInstance(L);
double val = luaInstance.ToNumber(luaInstance.GetIndexOfUpValue(1));
luaInstance.PushNumber(++val);
luaInstance.PushValue(-1);
luaInstance.Replace(luaInstance.GetIndexOfUpValue(1));
return 1;
}
struct TestLuaState
{
int a = 3;
float b = 5.f;
};
struct TestMetaTable
{
float x = 1.f;
float y = 2.f;
};
inline int LuaImplReplyVal(const Nz::LuaState& state, TestLuaState&& val, Nz::TypeTag<TestLuaState>)
{
state.PushTable();
state.PushField("a", val.a);
state.PushField("b", val.b);
return 1;
}
inline unsigned int LuaImplQueryArg(const Nz::LuaState& state, int index, TestLuaState* arg, Nz::TypeTag<TestLuaState>)
{
state.CheckType(index, Nz::LuaType_Table);
arg->a = state.CheckField<int>("a", index);
arg->b = state.CheckField<float>("b", index);
return 1;
}
SCENARIO("LuaState", "[LUA][LUASTATE]")
{
GIVEN("One lua instance")
{
Nz::LuaInstance luaInstance;
luaInstance.LoadLibraries(Nz::LuaLib_Math);
WHEN("We push different primitive types")
{
bool booleanValue = true;
long long integerValue = 5LL;
double doubleValue = -55.0;
const char* stringValue = "test";
Nz::String nazaraValue = "Nazara";
luaInstance.PushBoolean(booleanValue);
luaInstance.PushInteger(integerValue);
luaInstance.PushNil();
luaInstance.PushNumber(doubleValue);
luaInstance.PushString(stringValue);
luaInstance.PushString(nazaraValue);
THEN("We should be able to retrieve them")
{
CHECK(luaInstance.CheckBoolean(1) == booleanValue);
CHECK(luaInstance.CheckInteger(2) == integerValue);
bool succeeded = false;
CHECK(luaInstance.ToInteger(2, &succeeded) == integerValue);
CHECK(succeeded);
CHECK(luaInstance.ToPointer(3) == nullptr);
CHECK(luaInstance.CheckNumber(4) == Approx(doubleValue));
succeeded = false;
CHECK(luaInstance.ToNumber(4, &succeeded) == Approx(doubleValue));
CHECK(succeeded);
CHECK(luaInstance.CheckString(5) == std::string(stringValue));
CHECK(luaInstance.CheckString(6) == nazaraValue);
std::size_t length = 0;
CHECK(luaInstance.ToString(6, &length) == nazaraValue);
CHECK(length == nazaraValue.GetSize());
}
}
WHEN("We use basic operations")
{
luaInstance.PushInteger(1);
luaInstance.PushInteger(2);
THEN("We should behave normally")
{
CHECK(luaInstance.Compare(1, 2, Nz::LuaComparison_Less));
luaInstance.Compute(Nz::LuaOperation_Substraction);
CHECK(luaInstance.ToInteger(1) == -1);
}
}
WHEN("We manipulate the stack")
{
Nz::String stringValue = "hello";
luaInstance.PushBoolean(true);
luaInstance.PushNumber(10.0);
luaInstance.PushNil();
luaInstance.PushString(stringValue);
/* true 10.0 nil hello */
THEN("These effects are expected")
{
luaInstance.PushValue(-4);
/* true 10.0 nil hello true */
CHECK(luaInstance.CheckBoolean(5));
luaInstance.Replace(3);
/* true 10.0 true hello */
CHECK(luaInstance.CheckBoolean(3));
luaInstance.Remove(-2);
/* true 10.0 hello */
CHECK(luaInstance.CheckString(3) == stringValue);
luaInstance.Pop(2);
/* true */
CHECK_FALSE(luaInstance.IsValid(2));
}
}
WHEN("We try the CFunction")
{
double counterValue = 55.0;
luaInstance.PushFunction([=](Nz::LuaState& s) -> int {
s.PushNumber(counterValue);
s.PushCFunction(&counter, 1);
return 1;
});
THEN("We can call them")
{
luaInstance.Call(0); // We call our counter creator
luaInstance.Call(0); // We call our counter, which increments the value
CHECK(luaInstance.ToNumber(0) == Approx(counterValue + 1.0));
}
}
WHEN("We push our user type locally")
{
luaInstance.Push(TestLuaState());
THEN("We can retrieve it")
{
int index = 1;
TestLuaState popped = luaInstance.Check<TestLuaState>(&index);
CHECK(popped.a == 3);
CHECK(popped.b == Approx(5.0));
}
}
WHEN("We push our user type globally")
{
luaInstance.PushGlobal("TestLuaState", TestLuaState());
THEN("We can retrieve it")
{
TestLuaState popped = luaInstance.CheckGlobal<TestLuaState>("TestLuaState");
CHECK(popped.a == 3);
CHECK(popped.b == Approx(5.0));
}
}
WHEN("We define a lua function")
{
luaInstance.Execute(R"(
function f (x, y)
return (x^2 * math.sin(y))/(1 - x)
end
)");
THEN("We can call it from the code")
{
REQUIRE(luaInstance.GetGlobal("f") == Nz::LuaType_Function);
luaInstance.Push(3.0, 2.0);
luaInstance.Call(2, 1);
CHECK(luaInstance.ToNumber(1) == Approx(-4.09).margin(0.1));
}
}
}
}

View File

@ -98,7 +98,7 @@ SCENARIO("Collider2D", "[PHYSICS2D][COLLIDER2D]")
THEN("We expect those to be true")
{
CHECK(segment.GetFirstPoint() == firstPoint);
CHECK(segment.GetLength() == firstPoint.Distance(secondPoint));
CHECK(segment.GetLength() == Approx(firstPoint.Distance(secondPoint)));
CHECK(segment.GetSecondPoint() == secondPoint);
CHECK(segment.GetType() == Nz::ColliderType2D_Segment);
}

View File

@ -306,13 +306,13 @@ Nz::RigidBody2D CreateBody(Nz::PhysWorld2D& world)
void EQUALITY(const Nz::RigidBody2D& left, const Nz::RigidBody2D& right)
{
CHECK(left.GetAABB() == right.GetAABB());
CHECK(left.GetAngularVelocity() == right.GetAngularVelocity());
CHECK(left.GetAngularVelocity() == Approx(right.GetAngularVelocity()));
CHECK(left.GetCenterOfGravity() == right.GetCenterOfGravity());
CHECK(left.GetGeom() == right.GetGeom());
CHECK(left.GetHandle() != right.GetHandle());
CHECK(left.GetMass() == right.GetMass());
CHECK(left.GetMass() == Approx(right.GetMass()));
CHECK(left.GetPosition() == right.GetPosition());
CHECK(left.GetRotation() == right.GetRotation());
CHECK(left.GetRotation() == Approx(right.GetRotation()));
CHECK(left.GetUserdata() == right.GetUserdata());
CHECK(left.GetVelocity() == right.GetVelocity());
}

View File

@ -89,7 +89,7 @@ SCENARIO("PhysicsSystem2D", "[NDK][PHYSICSSYSTEM2D]")
THEN("It should have been rotated")
{
CHECK(physicsComponent2D.GetAngularVelocity() == angularSpeed);
CHECK(physicsComponent2D.GetAngularVelocity() == Approx(angularSpeed));
CHECK(physicsComponent2D.GetAABB() == Nz::Rectf(-2.f, 0.f, 2.f, 1.f));
CHECK(physicsComponent2D.GetRotation() == Approx(Nz::FromDegrees(90.f)));
CHECK(nodeComponent.GetRotation().ToEulerAngles().roll == Approx(Nz::FromDegrees(90.f)));

View File

@ -0,0 +1,24 @@
function test_Test()
local test = Test(1)
local i = test:GetI()
local result = Test.StaticMethodWithArguments(i, i)
local finalTest = Test(result + test:GetDefault(), true)
CheckTest(test)
CheckStatic(result)
CheckFinalTest(finalTest)
end
function test_InheritTest()
local test = InheritTest()
CheckInheritTest(test)
end
function test_TestHandle()
local test = TestHandle()
CheckTestHandle()
end

View File

@ -0,0 +1,15 @@
function even (x)
coroutine.yield(1)
end
function odd (x)
coroutine.yield(0)
end
function infinite (x)
for i=1,x do
if i==3 then coroutine.yield(-1) end
if i % 2 == 0 then even(i) else odd(i) end
end
end