feat(test): replace custom test header with Catch2 and migrated lua
tests to new system
This commit is contained in:
parent
3297f6e3ff
commit
47ad44bb83
18 changed files with 325 additions and 304 deletions
|
|
@ -13,5 +13,16 @@
|
||||||
"program": "$ZED_WORKTREE_ROOT/build/bin/editor",
|
"program": "$ZED_WORKTREE_ROOT/build/bin/editor",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"adapter": "CodeLLDB"
|
"adapter": "CodeLLDB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Debug tests",
|
||||||
|
"build": {
|
||||||
|
"command": "cmake",
|
||||||
|
"args": ["--build", "build", "-j16"],
|
||||||
|
"cwd": "$ZED_WORKTREE_ROOT"
|
||||||
|
},
|
||||||
|
"program": "$ZED_WORKTREE_ROOT/build/bin/obtest",
|
||||||
|
"request": "launch",
|
||||||
|
"adapter": "CodeLLDB"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,4 @@ add_subdirectory(core)
|
||||||
add_subdirectory(client)
|
add_subdirectory(client)
|
||||||
add_subdirectory(editor)
|
add_subdirectory(editor)
|
||||||
|
|
||||||
|
|
||||||
set( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/tests )
|
|
||||||
enable_testing()
|
|
||||||
add_subdirectory(tests)
|
add_subdirectory(tests)
|
||||||
|
|
|
||||||
3
Testing/Temporary/LastTest.log
Normal file
3
Testing/Temporary/LastTest.log
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
Start testing: Dec 11 22:10 CET
|
||||||
|
----------------------------------------------------------
|
||||||
|
End testing: Dec 11 22:10 CET
|
||||||
|
|
@ -137,6 +137,7 @@ void ScriptContext::RunSleepingThreads() {
|
||||||
for (i = 0; i < sleepingThreads.size();) {
|
for (i = 0; i < sleepingThreads.size();) {
|
||||||
bool deleted = false;
|
bool deleted = false;
|
||||||
|
|
||||||
|
// TODO: Remove threads that belong to non-existent scripts
|
||||||
SleepingThread sleep = sleepingThreads[i];
|
SleepingThread sleep = sleepingThreads[i];
|
||||||
if (tu_clock_micros() >= sleep.targetTimeMicros) {
|
if (tu_clock_micros() >= sleep.targetTimeMicros) {
|
||||||
// Time args
|
// Time args
|
||||||
|
|
@ -167,6 +168,12 @@ void ScriptContext::RunSleepingThreads() {
|
||||||
schedTime = tu_clock_micros() - startTime;
|
schedTime = tu_clock_micros() - startTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Temporary stopgap until RunSleepingThreads can clear threads that belong to
|
||||||
|
// scripts no longer parented to the DataModel
|
||||||
|
void ScriptContext::DebugClearSleepingThreads() {
|
||||||
|
sleepingThreads.clear();
|
||||||
|
}
|
||||||
|
|
||||||
void ScriptContext::NewEnvironment(lua_State* L) {
|
void ScriptContext::NewEnvironment(lua_State* L) {
|
||||||
lua_newtable(L); // Env table
|
lua_newtable(L); // Env table
|
||||||
lua_newtable(L); // Metatable
|
lua_newtable(L); // Metatable
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,8 @@ public:
|
||||||
lua_State* state;
|
lua_State* state;
|
||||||
void PushThreadSleep(lua_State* thread, float delay);
|
void PushThreadSleep(lua_State* thread, float delay);
|
||||||
void RunSleepingThreads();
|
void RunSleepingThreads();
|
||||||
|
// TEMPORARY. USED ONLY FOR TESTING
|
||||||
|
void DebugClearSleepingThreads();
|
||||||
|
|
||||||
// Generates an environment with a metatable and pushes it both the env table and metatable in order onto the stack
|
// Generates an environment with a metatable and pushes it both the env table and metatable in order onto the stack
|
||||||
void NewEnvironment(lua_State* state);
|
void NewEnvironment(lua_State* state);
|
||||||
|
|
|
||||||
22
justfile
Normal file
22
justfile
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
help:
|
||||||
|
just -l
|
||||||
|
|
||||||
|
configure:
|
||||||
|
cmake -Bbuild -DCMAKE_BUILD_TYPE=Debug .
|
||||||
|
|
||||||
|
# Commented out configure because it takes unnecessarily long
|
||||||
|
# Just run configure manually if you've made any changes
|
||||||
|
build: #configure
|
||||||
|
cmake --build build -j$(nproc)
|
||||||
|
|
||||||
|
editor: build
|
||||||
|
./build/bin/editor
|
||||||
|
|
||||||
|
test: build
|
||||||
|
ctest --test-dir=build
|
||||||
|
|
||||||
|
test-v: build
|
||||||
|
ctest --test-dir=build --rerun-failed --output-on-failure
|
||||||
|
|
||||||
|
test-dbg: build
|
||||||
|
gdb -q ./build/bin/obtest
|
||||||
|
|
@ -1,14 +1,12 @@
|
||||||
function (create_test TEST_NAME)
|
|
||||||
set(TARGET_NAME test_${TEST_NAME})
|
|
||||||
add_executable(${TARGET_NAME} ${ARGN})
|
|
||||||
target_link_libraries(${TARGET_NAME} PRIVATE openblocks)
|
|
||||||
add_dependencies(${TARGET_NAME} openblocks)
|
|
||||||
add_test(NAME ${TARGET_NAME} COMMAND ${TARGET_NAME})
|
|
||||||
endfunction ()
|
|
||||||
|
|
||||||
create_test(lua src/luatest.cpp)
|
include(${CMAKE_CURRENT_SOURCE_DIR}/deps.cmake)
|
||||||
create_test(luasched src/luaschedtest.cpp)
|
|
||||||
create_test(luasignal src/luasignaltest.cpp)
|
|
||||||
|
|
||||||
# https://stackoverflow.com/a/36729074/16255372
|
add_executable(obtest
|
||||||
add_custom_target(check ${CMAKE_CTEST_COMMAND} --output-on-failure WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
src/common.cpp
|
||||||
|
src/lua/luasched.cpp
|
||||||
|
src/lua/luasignal.cpp
|
||||||
|
src/lua/luageneric.cpp
|
||||||
|
)
|
||||||
|
target_link_libraries(obtest PRIVATE openblocks Catch2::Catch2WithMain)
|
||||||
|
target_include_directories(obtest PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
||||||
|
catch_discover_tests(obtest)
|
||||||
8
tests/deps.cmake
Normal file
8
tests/deps.cmake
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
|
||||||
|
include(CPM)
|
||||||
|
|
||||||
|
CPMAddPackage("gh:catchorg/Catch2@3.8.1")
|
||||||
|
|
||||||
|
list(APPEND CMAKE_MODULE_PATH ${Catch2_SOURCE_DIR}/extras)
|
||||||
|
include(CTest)
|
||||||
|
include(Catch)
|
||||||
54
tests/src/common.cpp
Normal file
54
tests/src/common.cpp
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
#include <catch2/reporters/catch_reporter_event_listener.hpp>
|
||||||
|
#include <catch2/reporters/catch_reporter_registrars.hpp>
|
||||||
|
|
||||||
|
#include "logger.h"
|
||||||
|
#include "objects/base/instance.h"
|
||||||
|
#include "objects/service/script/scriptcontext.h"
|
||||||
|
#include "objects/service/script/serverscriptservice.h"
|
||||||
|
#include "objects/service/workspace.h"
|
||||||
|
#include "testcommon.h"
|
||||||
|
|
||||||
|
std::shared_ptr<DataModel> gTestModel;
|
||||||
|
std::stringstream testLogOutput;
|
||||||
|
|
||||||
|
class commonTestListener : public Catch::EventListenerBase {
|
||||||
|
public:
|
||||||
|
using Catch::EventListenerBase::EventListenerBase;
|
||||||
|
|
||||||
|
void testRunStarting(Catch::TestRunInfo const&) override {
|
||||||
|
// TODO: Make physicsInit optional in headless environments
|
||||||
|
physicsInit();
|
||||||
|
|
||||||
|
gTestModel = DataModel::New();
|
||||||
|
gTestModel->Init(true);
|
||||||
|
Logger::initTest(&testLogOutput);
|
||||||
|
}
|
||||||
|
|
||||||
|
void testRunEnded(Catch::TestRunStats const&) override {
|
||||||
|
gTestModel = nullptr;
|
||||||
|
physicsDeinit();
|
||||||
|
Logger::initTest(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void testCasePartialStarting(const Catch::TestCaseInfo &testInfo, uint64_t partNumber) override {
|
||||||
|
// Clear the log output prior to each test
|
||||||
|
testLogOutput.str("");
|
||||||
|
}
|
||||||
|
|
||||||
|
void testCasePartialEnded(const Catch::TestCaseStats &testCaseStats, uint64_t partNumber) override {
|
||||||
|
auto ctx = gTestModel->GetService<ScriptContext>();
|
||||||
|
ctx->DebugClearSleepingThreads();
|
||||||
|
|
||||||
|
// Clean up remaining scripts from ServerScriptService
|
||||||
|
for (auto& obj : gTestModel->GetService<ServerScriptService>()->GetChildren()) {
|
||||||
|
obj->Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also clear workspace
|
||||||
|
for (auto& obj : gTestModel->GetService<Workspace>()->GetChildren()) {
|
||||||
|
obj->Destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
CATCH_REGISTER_LISTENER(commonTestListener)
|
||||||
20
tests/src/lua/luageneric.cpp
Normal file
20
tests/src/lua/luageneric.cpp
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
|
||||||
|
#include "testcommon.h"
|
||||||
|
#include "testutil.h"
|
||||||
|
|
||||||
|
TEST_CASE("Generic lua test", "[luageneric]") {
|
||||||
|
auto m = gTestModel;
|
||||||
|
|
||||||
|
SECTION("Script output") {
|
||||||
|
REQUIRE(luaEvalOut(m, "print('Hello, world!')") == "INFO: Hello, world!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// SECTION("Script warning") {
|
||||||
|
// REQUIRE(luaEvalOut(m, "warn('Some warning here.')") == "WARN: Some warning here.\n");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// SECTION("Script error") {
|
||||||
|
// REQUIRE(luaEvalOut(m, "error('An error!')") == "ERROR: An error!.\n");
|
||||||
|
// }
|
||||||
|
}
|
||||||
81
tests/src/lua/luasched.cpp
Normal file
81
tests/src/lua/luasched.cpp
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
|
||||||
|
#include "objects/service/script/scriptcontext.h"
|
||||||
|
#include "testcommon.h"
|
||||||
|
#include "testutil.h"
|
||||||
|
#include "timeutil.h"
|
||||||
|
|
||||||
|
static auto& m = gTestModel;
|
||||||
|
static auto& out = testLogOutput;
|
||||||
|
|
||||||
|
TEST_CASE("Wait with delay") {
|
||||||
|
auto ctx = m->GetService<ScriptContext>();
|
||||||
|
|
||||||
|
tu_set_override(0);
|
||||||
|
luaEval(m, "wait(1) print('Wait')");
|
||||||
|
|
||||||
|
SECTION("Empty output at 0s") {
|
||||||
|
ctx->RunSleepingThreads();
|
||||||
|
REQUIRE(out.str() == "");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Empty output at 0.5s") {
|
||||||
|
TT_ADVANCETIME(0.5);
|
||||||
|
ctx->RunSleepingThreads();
|
||||||
|
REQUIRE(out.str() == "");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Print output at 1s") {
|
||||||
|
TT_ADVANCETIME(1);
|
||||||
|
ctx->RunSleepingThreads();
|
||||||
|
REQUIRE(out.str() == "INFO: Wait\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Wait with minimum delay") {
|
||||||
|
auto ctx = m->GetService<ScriptContext>();
|
||||||
|
|
||||||
|
tu_set_override(0);
|
||||||
|
luaEval(m, "wait(0) print('Wait')");
|
||||||
|
|
||||||
|
SECTION("Empty output at 0s") {
|
||||||
|
ctx->RunSleepingThreads();
|
||||||
|
REQUIRE(out.str() == "");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Empty output at 0.02s") {
|
||||||
|
TT_ADVANCETIME(0.02);
|
||||||
|
ctx->RunSleepingThreads();
|
||||||
|
REQUIRE(out.str() == "");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Print output at 0.03s") {
|
||||||
|
TT_ADVANCETIME(0.03);
|
||||||
|
ctx->RunSleepingThreads();
|
||||||
|
REQUIRE(out.str() == "INFO: Wait\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Run callback after delay") {
|
||||||
|
auto ctx = m->GetService<ScriptContext>();
|
||||||
|
|
||||||
|
tu_set_override(0);
|
||||||
|
luaEval(m, "delay(1, function() print('Delay') end)");
|
||||||
|
|
||||||
|
SECTION("Empty output at 0s") {
|
||||||
|
ctx->RunSleepingThreads();
|
||||||
|
REQUIRE(out.str() == "");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Empty output at 0.5s") {
|
||||||
|
TT_ADVANCETIME(0.5);
|
||||||
|
ctx->RunSleepingThreads();
|
||||||
|
REQUIRE(out.str() == "");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Print output at 1s") {
|
||||||
|
TT_ADVANCETIME(1);
|
||||||
|
ctx->RunSleepingThreads();
|
||||||
|
REQUIRE(out.str() == "INFO: Delay\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
79
tests/src/lua/luasignal.cpp
Normal file
79
tests/src/lua/luasignal.cpp
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
|
||||||
|
#include "objects/part/part.h"
|
||||||
|
#include "objects/service/script/scriptcontext.h"
|
||||||
|
#include "objects/service/workspace.h"
|
||||||
|
#include "testcommon.h"
|
||||||
|
#include "testutil.h"
|
||||||
|
#include "timeutil.h"
|
||||||
|
|
||||||
|
static auto& m = gTestModel;
|
||||||
|
static auto& out = testLogOutput;
|
||||||
|
|
||||||
|
TEST_CASE("Connect to event") {
|
||||||
|
auto ws = m->GetService<Workspace>();
|
||||||
|
auto part = Part::New();
|
||||||
|
ws->AddChild(part);
|
||||||
|
|
||||||
|
luaEval(m, "workspace.Part.Touched:Connect(function() print('Fired!') end)");
|
||||||
|
|
||||||
|
SECTION("Single fire") {
|
||||||
|
part->Touched->Fire();
|
||||||
|
REQUIRE(out.str() == "INFO: Fired!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Double fire") {
|
||||||
|
part->Touched->Fire();
|
||||||
|
part->Touched->Fire();
|
||||||
|
REQUIRE(out.str() == "INFO: Fired!\nINFO: Fired!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Wait within event listener") {
|
||||||
|
auto ctx = m->GetService<ScriptContext>();
|
||||||
|
auto ws = m->GetService<Workspace>();
|
||||||
|
auto part = Part::New();
|
||||||
|
ws->AddChild(part);
|
||||||
|
|
||||||
|
tu_set_override(0);
|
||||||
|
luaEval(m, "workspace.Part.Touched:Connect(function() print('Fired!') wait(1) print('Waited') end)");
|
||||||
|
|
||||||
|
SECTION("Single fire") {
|
||||||
|
part->Touched->Fire();
|
||||||
|
REQUIRE(out.str() == "INFO: Fired!\n");
|
||||||
|
TT_ADVANCETIME(0.5);
|
||||||
|
ctx->RunSleepingThreads();
|
||||||
|
REQUIRE(out.str() == "INFO: Fired!\n");
|
||||||
|
TT_ADVANCETIME(0.5);
|
||||||
|
ctx->RunSleepingThreads();
|
||||||
|
REQUIRE(out.str() == "INFO: Fired!\nINFO: Waited\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Nested double fire") {
|
||||||
|
part->Touched->Fire();
|
||||||
|
TT_ADVANCETIME(0.2);
|
||||||
|
part->Touched->Fire();
|
||||||
|
REQUIRE(out.str() == "INFO: Fired!\nINFO: Fired!\n");
|
||||||
|
TT_ADVANCETIME(1-0.2); // Small extra delay is necessary because floating point math
|
||||||
|
ctx->RunSleepingThreads();
|
||||||
|
REQUIRE(out.str() == "INFO: Fired!\nINFO: Fired!\nINFO: Waited\n");
|
||||||
|
TT_ADVANCETIME(0.2);
|
||||||
|
ctx->RunSleepingThreads();
|
||||||
|
REQUIRE(out.str() == "INFO: Fired!\nINFO: Fired!\nINFO: Waited\nINFO: Waited\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Wait for event") {
|
||||||
|
auto ctx = m->GetService<ScriptContext>();
|
||||||
|
auto ws = m->GetService<Workspace>();
|
||||||
|
auto part = Part::New();
|
||||||
|
ws->AddChild(part);
|
||||||
|
|
||||||
|
tu_set_override(0);
|
||||||
|
luaEval(m, "workspace.Part.Touched:Wait() print('Fired!')");
|
||||||
|
|
||||||
|
part->Touched->Fire();
|
||||||
|
REQUIRE(out.str() == "INFO: Fired!\n");
|
||||||
|
part->Touched->Fire(); // Firing again should not affect output
|
||||||
|
REQUIRE(out.str() == "INFO: Fired!\n");
|
||||||
|
}
|
||||||
|
|
@ -1,77 +0,0 @@
|
||||||
#include "testutillua.h"
|
|
||||||
|
|
||||||
#include "timeutil.h"
|
|
||||||
|
|
||||||
void test_wait1(DATAMODEL_REF m) {
|
|
||||||
auto ctx = m->GetService<ScriptContext>();
|
|
||||||
std::stringstream out;
|
|
||||||
Logger::initTest(&out);
|
|
||||||
|
|
||||||
tu_set_override(0);
|
|
||||||
luaEval(m, "wait(1) print('Wait')");
|
|
||||||
|
|
||||||
ctx->RunSleepingThreads();
|
|
||||||
ASSERT_EQ("", out.str());
|
|
||||||
|
|
||||||
TT_ADVANCETIME(0.5);
|
|
||||||
ctx->RunSleepingThreads();
|
|
||||||
ASSERT_EQ("", out.str());
|
|
||||||
|
|
||||||
TT_ADVANCETIME(0.5);
|
|
||||||
ctx->RunSleepingThreads();
|
|
||||||
ASSERT_EQ("INFO: Wait\n", out.str());
|
|
||||||
|
|
||||||
Logger::initTest(nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_wait0(DATAMODEL_REF m) {
|
|
||||||
auto ctx = m->GetService<ScriptContext>();
|
|
||||||
std::stringstream out;
|
|
||||||
Logger::initTest(&out);
|
|
||||||
|
|
||||||
tu_set_override(0);
|
|
||||||
luaEval(m, "wait(0) print('Wait')");
|
|
||||||
ASSERT_EQ("", out.str());
|
|
||||||
|
|
||||||
ctx->RunSleepingThreads();
|
|
||||||
ASSERT_EQ("", out.str());
|
|
||||||
|
|
||||||
TT_ADVANCETIME(0.03);
|
|
||||||
ctx->RunSleepingThreads();
|
|
||||||
ASSERT_EQ("INFO: Wait\n", out.str());
|
|
||||||
|
|
||||||
Logger::initTest(nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_delay(DATAMODEL_REF m) {
|
|
||||||
auto ctx = m->GetService<ScriptContext>();
|
|
||||||
std::stringstream out;
|
|
||||||
Logger::initTest(&out);
|
|
||||||
|
|
||||||
tu_set_override(0);
|
|
||||||
luaEval(m, "delay(1, function() print('Delay') end)");
|
|
||||||
|
|
||||||
ctx->RunSleepingThreads();
|
|
||||||
ASSERT_EQ("", out.str());
|
|
||||||
|
|
||||||
TT_ADVANCETIME(0.5);
|
|
||||||
ctx->RunSleepingThreads();
|
|
||||||
ASSERT_EQ("", out.str());
|
|
||||||
|
|
||||||
TT_ADVANCETIME(0.5);
|
|
||||||
ctx->RunSleepingThreads();
|
|
||||||
ASSERT_EQ("INFO: Delay\n", out.str());
|
|
||||||
|
|
||||||
Logger::initTest(nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
auto m = DataModel::New();
|
|
||||||
m->Init(true);
|
|
||||||
|
|
||||||
test_wait1(m);
|
|
||||||
test_wait0(m);
|
|
||||||
test_delay(m);
|
|
||||||
|
|
||||||
return TEST_STATUS;
|
|
||||||
}
|
|
||||||
|
|
@ -1,103 +0,0 @@
|
||||||
|
|
||||||
#include "testutil.h"
|
|
||||||
#include "testutillua.h"
|
|
||||||
|
|
||||||
#include "timeutil.h"
|
|
||||||
#include "objects/part/part.h"
|
|
||||||
#include "objects/service/workspace.h"
|
|
||||||
#include <memory>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
void test_connect(DATAMODEL_REF m) {
|
|
||||||
auto ctx = m->GetService<ScriptContext>();
|
|
||||||
auto part = Part::New();
|
|
||||||
m->GetService<Workspace>()->AddChild(part);
|
|
||||||
std::stringstream out;
|
|
||||||
Logger::initTest(&out);
|
|
||||||
|
|
||||||
luaEval(m, "workspace.Part.Touched:Connect(function() print('Fired!') end)");
|
|
||||||
ASSERT_EQ("", out.str());
|
|
||||||
|
|
||||||
part->Touched->Fire();
|
|
||||||
ASSERT_EQ("INFO: Fired!\n", out.str());
|
|
||||||
part->Touched->Fire();
|
|
||||||
ASSERT_EQ("INFO: Fired!\nINFO: Fired!\n", out.str());
|
|
||||||
|
|
||||||
Logger::initTest(nullptr);
|
|
||||||
part->Destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_waitwithin(DATAMODEL_REF m) {
|
|
||||||
auto ctx = m->GetService<ScriptContext>();
|
|
||||||
auto part = Part::New();
|
|
||||||
m->GetService<Workspace>()->AddChild(part);
|
|
||||||
std::stringstream out;
|
|
||||||
Logger::initTest(&out);
|
|
||||||
|
|
||||||
tu_set_override(0);
|
|
||||||
luaEval(m, "workspace.Part.Touched:Connect(function() print('Fired!') wait(1) print('Waited') end)");
|
|
||||||
ASSERT_EQ("", out.str());
|
|
||||||
|
|
||||||
// One shot
|
|
||||||
part->Touched->Fire();
|
|
||||||
ctx->RunSleepingThreads();
|
|
||||||
ASSERT_EQ("INFO: Fired!\n", out.str());
|
|
||||||
TT_ADVANCETIME(0.5);
|
|
||||||
ctx->RunSleepingThreads();
|
|
||||||
ASSERT_EQ("INFO: Fired!\n", out.str());
|
|
||||||
TT_ADVANCETIME(0.5);
|
|
||||||
ctx->RunSleepingThreads();
|
|
||||||
ASSERT_EQ("INFO: Fired!\nINFO: Waited\n", out.str());
|
|
||||||
|
|
||||||
// Clear
|
|
||||||
out = std::stringstream();
|
|
||||||
Logger::initTest(&out); // Shouldn't *theoretically* be necessary, but just in principle...
|
|
||||||
|
|
||||||
// Double fire
|
|
||||||
part->Touched->Fire();
|
|
||||||
TT_ADVANCETIME(0.2);
|
|
||||||
part->Touched->Fire();
|
|
||||||
ASSERT_EQ("INFO: Fired!\nINFO: Fired!\n", out.str());
|
|
||||||
TT_ADVANCETIME(1-0.2); // Small extra delay is necessary because floating point math
|
|
||||||
ctx->RunSleepingThreads();
|
|
||||||
ASSERT_EQ("INFO: Fired!\nINFO: Fired!\nINFO: Waited\n", out.str());
|
|
||||||
TT_ADVANCETIME(0.2);
|
|
||||||
ctx->RunSleepingThreads();
|
|
||||||
ASSERT_EQ("INFO: Fired!\nINFO: Fired!\nINFO: Waited\nINFO: Waited\n", out.str());
|
|
||||||
|
|
||||||
tu_set_override(-1UL);
|
|
||||||
Logger::initTest(nullptr);
|
|
||||||
part->Destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_await(DATAMODEL_REF m) {
|
|
||||||
auto ctx = m->GetService<ScriptContext>();
|
|
||||||
auto part = Part::New();
|
|
||||||
m->GetService<Workspace>()->AddChild(part);
|
|
||||||
std::stringstream out;
|
|
||||||
Logger::initTest(&out);
|
|
||||||
|
|
||||||
tu_set_override(0);
|
|
||||||
luaEval(m, "workspace.Part.Touched:Wait() print('Fired!')");
|
|
||||||
ASSERT_EQ("", out.str());
|
|
||||||
|
|
||||||
part->Touched->Fire();
|
|
||||||
ASSERT_EQ("INFO: Fired!\n", out.str());
|
|
||||||
part->Touched->Fire(); // Firing again should not affect output
|
|
||||||
ASSERT_EQ("INFO: Fired!\n", out.str());
|
|
||||||
|
|
||||||
tu_set_override(-1UL);
|
|
||||||
Logger::initTest(nullptr);
|
|
||||||
part->Destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
auto m = DataModel::New();
|
|
||||||
m->Init(true);
|
|
||||||
|
|
||||||
test_connect(m);
|
|
||||||
test_waitwithin(m);
|
|
||||||
test_await(m);
|
|
||||||
|
|
||||||
return TEST_STATUS;
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
#include "testutil.h"
|
|
||||||
#include "testutillua.h"
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
|
|
||||||
void test_output(DATAMODEL_REF m) {
|
|
||||||
ASSERT_EQ("INFO: Hello, world!\n", luaEvalOut(m, "print('Hello, world!')"));
|
|
||||||
// ASSERT_EQ("WARN: Some warning here.\n", luaEvalOut(m, "warn('Some warning here.')"));
|
|
||||||
// ASSERT_EQ("ERROR: An error!.\n", luaEvalOut(m, "error('An error!')"));
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
auto m = DataModel::New();
|
|
||||||
m->Init(true);
|
|
||||||
|
|
||||||
test_output(m);
|
|
||||||
|
|
||||||
return TEST_STATUS;
|
|
||||||
}
|
|
||||||
8
tests/src/testcommon.h
Normal file
8
tests/src/testcommon.h
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#include "objects/datamodel.h"
|
||||||
|
|
||||||
|
extern std::shared_ptr<DataModel> gTestModel;
|
||||||
|
extern std::stringstream testLogOutput;
|
||||||
|
|
@ -1,70 +1,31 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// https://bastian.rieck.me/blog/2017/simple_unit_tests/
|
#include "objects/datamodel.h"
|
||||||
|
#include "objects/script.h"
|
||||||
#include <algorithm>
|
#include "objects/service/script/serverscriptservice.h"
|
||||||
#include <cstddef>
|
#include "testcommon.h"
|
||||||
#include <iomanip>
|
|
||||||
#include <regex>
|
|
||||||
#include <sstream>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#ifdef __FUNCTION__
|
|
||||||
#define __FUNC_NAME __FUNCTION__
|
|
||||||
#else
|
|
||||||
#define __FUNC_NAME __func__
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define ASSERT(x, msg) __assert((x), __FILE__, __LINE__, __FUNC_NAME, msg)
|
|
||||||
#define ASSERT_EQ(x, y) __assert_eq((x) == (y), __FILE__, __LINE__, __FUNC_NAME, #x, (y))
|
|
||||||
// #define ASSERT_EQSTR(x, y) ASSERT(strcmp(x, y) == 0, #x " != " #y)
|
|
||||||
#define ASSERT_EQSTR(x, y) ASSERT_EQ(x, y)
|
|
||||||
|
|
||||||
#define DATAMODEL_REF std::shared_ptr<DataModel>
|
|
||||||
|
|
||||||
#define TU_TIME_EXPOSE_TEST
|
#define TU_TIME_EXPOSE_TEST
|
||||||
#define TT_ADVANCETIME(secs) tu_set_override(tu_clock_micros() + (secs) * 1'000'000);
|
#define TT_ADVANCETIME(secs) tu_set_override(tu_clock_micros() + (secs) * 1'000'000);
|
||||||
|
|
||||||
#include <cstdio>
|
inline std::string luaEvalOut(std::shared_ptr<DataModel> m, std::string source) {
|
||||||
#include <cstring>
|
testLogOutput.seekp(0, std::ios::end);
|
||||||
|
size_t offset = testLogOutput.tellp();
|
||||||
|
testLogOutput.seekp(0);
|
||||||
|
|
||||||
int TEST_STATUS = 0;
|
auto ss = m->GetService<ServerScriptService>();
|
||||||
|
auto s = Script::New();
|
||||||
|
m->AddChild(s);
|
||||||
|
s->source = source;
|
||||||
|
s->Run();
|
||||||
|
|
||||||
inline void __assert(bool cond, std::string file, int line, std::string func, std::string message) {
|
return testLogOutput.str().substr(offset);
|
||||||
if (cond) return;
|
|
||||||
fprintf(stderr, "ASSERT FAILED : %s:%d : %s : '%s'\n", file.c_str(), line, func.c_str(), message.c_str());
|
|
||||||
TEST_STATUS = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
inline void luaEval(std::shared_ptr<DataModel> m, std::string source) {
|
||||||
inline std::string quote(T value) {
|
auto ss = m->GetService<ServerScriptService>();
|
||||||
return std::to_string(value);
|
auto s = Script::New();
|
||||||
|
ss->AddChild(s);
|
||||||
|
s->source = source;
|
||||||
|
s->Run();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
|
||||||
std::string quote<std::string>(std::string value) {
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << std::quoted(value);
|
|
||||||
std::string newstr = ss.str();
|
|
||||||
|
|
||||||
newstr = std::regex_replace(newstr, std::regex("\n"), "\\n");
|
|
||||||
return newstr;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
inline std::string quote<const char*>(const char* value) {
|
|
||||||
return quote<std::string>(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
inline std::string quote<char*>(char* value) {
|
|
||||||
return quote<std::string>(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void __assert_eq(bool cond, std::string file, int line, std::string func, std::string model, T value) {
|
|
||||||
if (cond) return;
|
|
||||||
std::string message = model + " != " + quote(value);
|
|
||||||
fprintf(stderr, "ASSERT FAILED : %s:%d : %s : '%s'\n", file.c_str(), line, func.c_str(), message.c_str());
|
|
||||||
TEST_STATUS = 1;
|
|
||||||
}
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "testutil.h"
|
|
||||||
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
#include "logger.h"
|
|
||||||
#include "objects/datamodel.h"
|
|
||||||
#include "objects/script.h"
|
|
||||||
#include "objects/service/script/scriptcontext.h"
|
|
||||||
|
|
||||||
std::string luaEvalOut(DATAMODEL_REF m, std::string source) {
|
|
||||||
std::stringstream out;
|
|
||||||
Logger::initTest(&out);
|
|
||||||
|
|
||||||
auto s = Script::New();
|
|
||||||
m->AddChild(s);
|
|
||||||
s->source = source;
|
|
||||||
s->Run();
|
|
||||||
|
|
||||||
Logger::initTest(nullptr);
|
|
||||||
return out.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
void luaEval(DATAMODEL_REF m, std::string source) {
|
|
||||||
auto s = Script::New();
|
|
||||||
m->AddChild(s);
|
|
||||||
s->source = source;
|
|
||||||
s->Run();
|
|
||||||
}
|
|
||||||
Loading…
Add table
Reference in a new issue