diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 69f25ef..47af85b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -7,6 +7,8 @@ function (create_test TEST_NAME) endfunction () create_test(lua src/luatest.cpp) +create_test(luasched src/luaschedtest.cpp) +create_test(luasignal src/luasignaltest.cpp) # https://stackoverflow.com/a/36729074/16255372 add_custom_target(check ${CMAKE_CTEST_COMMAND} --output-on-failure WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) \ No newline at end of file diff --git a/tests/src/luaschedtest.cpp b/tests/src/luaschedtest.cpp new file mode 100644 index 0000000..2c1825c --- /dev/null +++ b/tests/src/luaschedtest.cpp @@ -0,0 +1,77 @@ +#include "testutillua.h" + +#include "timeutil.h" + +void test_wait1(DATAMODEL_REF m) { + auto ctx = m->GetService(); + 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(); + 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(); + 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; +} \ No newline at end of file diff --git a/tests/src/luasignaltest.cpp b/tests/src/luasignaltest.cpp new file mode 100644 index 0000000..00342f7 --- /dev/null +++ b/tests/src/luasignaltest.cpp @@ -0,0 +1,103 @@ + +#include "testutil.h" +#include "testutillua.h" + +#include "timeutil.h" +#include "objects/part/part.h" +#include "objects/service/workspace.h" +#include +#include + +void test_connect(DATAMODEL_REF m) { + auto ctx = m->GetService(); + auto part = Part::New(); + m->GetService()->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(); + auto part = Part::New(); + m->GetService()->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(); + auto part = Part::New(); + m->GetService()->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; +} \ No newline at end of file diff --git a/tests/src/luatest.cpp b/tests/src/luatest.cpp index 935ad2b..29b6252 100644 --- a/tests/src/luatest.cpp +++ b/tests/src/luatest.cpp @@ -1,32 +1,8 @@ -#include "objects/service/script/scriptcontext.h" #include "testutil.h" +#include "testutillua.h" -#include "logger.h" -#include "objects/datamodel.h" -#include "objects/script.h" -#include "timeutil.h" #include -#include -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(); -} void test_output(DATAMODEL_REF m) { ASSERT_EQ("INFO: Hello, world!\n", luaEvalOut(m, "print('Hello, world!')")); @@ -34,77 +10,11 @@ void test_output(DATAMODEL_REF m) { // ASSERT_EQ("ERROR: An error!.\n", luaEvalOut(m, "error('An error!')")); } -void test_wait1(DATAMODEL_REF m) { - auto ctx = m->GetService(); - 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(); - 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(); - 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_output(m); - test_wait1(m); - test_wait0(m); - test_delay(m); return TEST_STATUS; } \ No newline at end of file diff --git a/tests/src/testutil.h b/tests/src/testutil.h index c7396eb..64c8a43 100644 --- a/tests/src/testutil.h +++ b/tests/src/testutil.h @@ -2,21 +2,69 @@ // https://bastian.rieck.me/blog/2017/simple_unit_tests/ +#include +#include +#include +#include +#include +#include + #ifdef __FUNCTION__ -#define ASSERT(x, msg) if (!(x)) { fprintf(stderr, "ASSERT FAILED : %s:%d : %s : '%s'\n", __FILE__, __LINE__, __FUNCTION__, msg); exit(1); TEST_STATUS = 1; } +#define __FUNC_NAME __FUNCTION__ #else -#define ASSERT(x, msg) if (!(x)) { fprintf(stderr, "ASSERT FAILED : %s:%d : ?? : '%s'\n", __FILE__, __LINE__, msg); TEST_STATUS = 1; } +#define __FUNC_NAME __func__ #endif -#define ASSERT_EQ(x, y) ASSERT(x == y, #x " != " #y) -#define ASSERT_EQSTR(x, y) ASSERT(strcmp(x, y) == 0, #x " != " #y) +#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 #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 #include -int TEST_STATUS = 0; \ No newline at end of file +int TEST_STATUS = 0; + +inline void __assert(bool cond, std::string file, int line, std::string func, std::string message) { + 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 +inline std::string quote(T value) { + return std::to_string(value); +} + +template <> +std::string quote(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* value) { + return quote(value); +} + +template <> +inline std::string quote(char* value) { + return quote(value); +} + +template +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; +} \ No newline at end of file diff --git a/tests/src/testutillua.h b/tests/src/testutillua.h new file mode 100644 index 0000000..a61c657 --- /dev/null +++ b/tests/src/testutillua.h @@ -0,0 +1,30 @@ +#pragma once + +#include "testutil.h" + +#include + +#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(); +} \ No newline at end of file