From ef34210fefeda51d5c85301fce37c5b6a3f2d334 Mon Sep 17 00:00:00 2001 From: Jeff Hutchinson Date: Tue, 23 Mar 2021 21:30:54 -0400 Subject: [PATCH] Implement Unit Test Suite for TorqueScript. --- Engine/source/console/test/ScriptTest.cpp | 395 +++++++++++++++++++++ Engine/source/console/test/consoleTest.cpp | 34 +- Tools/CMake/torque3d.cmake | 1 + 3 files changed, 397 insertions(+), 33 deletions(-) create mode 100644 Engine/source/console/test/ScriptTest.cpp diff --git a/Engine/source/console/test/ScriptTest.cpp b/Engine/source/console/test/ScriptTest.cpp new file mode 100644 index 000000000..994606783 --- /dev/null +++ b/Engine/source/console/test/ScriptTest.cpp @@ -0,0 +1,395 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifdef TORQUE_TESTS_ENABLED +#include "testing/unitTesting.h" +#include "platform/platform.h" +#include "console/simBase.h" +#include "console/consoleTypes.h" +#include "console/simBase.h" +#include "console/engineAPI.h" +#include "math/mMath.h" +#include "console/stringStack.h" + +template +inline T Convert(ConsoleValueRef); + +template<> +inline U32 Convert(ConsoleValueRef val) +{ + return val.getIntValue(); +} + +template<> +inline S32 Convert(ConsoleValueRef val) +{ + return val.getSignedIntValue(); +} + +template<> +inline bool Convert(ConsoleValueRef val) +{ + return val.getBoolValue(); +} + +template<> +inline F32 Convert(ConsoleValueRef val) +{ + return val.getFloatValue(); +} + +template<> +inline const char* Convert(ConsoleValueRef val) +{ + return val.getStringValue(); +} + +template<> +inline SimObject* Convert(ConsoleValueRef val) +{ + return Sim::findObject(val); +} + +template +inline T RunScript(const char* str) +{ + return Convert(Con::evaluate(str, false, NULL)); +} + +TEST(Script, Basic_Arithmetic) +{ + S32 add = RunScript(R"( + return 1.0 + 1; + )"); + + EXPECT_EQ(add, 2); + + S32 sub = RunScript(R"( + return 10 - 1.0; + )"); + + EXPECT_EQ(sub, 9); + + S32 mult = RunScript(R"( + return 10 * 2.5; + )"); + + EXPECT_EQ(mult, 25); + + S32 div = RunScript(R"( + return 10.0 / 2; + )"); + + EXPECT_EQ(div, 5); +} + +TEST(Script, Complex_Arithmetic) +{ + S32 result = RunScript(R"( + return 1 * 2 - (0.5 * 2); + )"); + + EXPECT_EQ(result, 1); + + S32 result2 = RunScript(R"( + return 1 * 2 * 3 % 2; + )"); + + EXPECT_EQ(result2, 0); +} + +TEST(Script, Basic_Concatination) +{ + const char* result1 = RunScript(R"( + return "a" @ "b"; + )"); + + EXPECT_STREQ(result1, "ab"); + + const char* result2 = RunScript(R"( + return "a" SPC "b"; + )"); + + EXPECT_STREQ(result2, "a b"); + + const char* result3 = RunScript(R"( + return "a" TAB "b"; + )"); + + EXPECT_STREQ(result3, "a\tb"); + + const char* result4 = RunScript(R"( + return "a" NL "b"; + )"); + + EXPECT_STREQ(result4, "a\nb"); + + const char* complex = RunScript(R"( + return "a" @ "b" @ "c" @ "d"; + )"); + + EXPECT_STREQ(complex, "abcd"); +} + +TEST(Script, Basic_Global_Variable_Tests) +{ + S32 value = RunScript(R"( + $a = 1; + return $a; + )"); + + EXPECT_EQ(value, 1); +} + +TEST(Script, Variable_Chaining_And_Usage) +{ + S32 value = RunScript(R"( + function t() + { + %a = %b = 2; + return %a; + } + return t(); + )"); + + EXPECT_EQ(value, 2); + + S32 valueGlobal = RunScript(R"( + function t() + { + $a = %b = 2; + } + t(); + return $a; + )"); + + EXPECT_EQ(valueGlobal, 2); + + S32 value2 = RunScript(R"( + function t(%a) + { + if ((%b = 2 * %a) != 5) + return %b; + return 5; + } + + return t(2); + )"); + + EXPECT_EQ(value2, 4); +} + +TEST(Script, Basic_Function_Call_And_Local_Variable_Testing) +{ + S32 value = RunScript(R"( + function t() { %a = 2; return %a; } + return t(); + )"); + + EXPECT_EQ(value, 2); + + S32 value2 = RunScript(R"( + function add(%a, %b) { return %a + %b; } + return add(2, 4); + )"); + + EXPECT_EQ(value2, 6); + + S32 value3 = RunScript(R"( + function fib(%a) { + if (%a == 0) + return 0; + if (%a == 1) + return 1; + return fib(%a - 1) + fib(%a - 2); + } + return fib(15); + )"); + + EXPECT_EQ(value3, 610); + + S32 staticCall = RunScript(R"( + function SimObject::bar(%a, %b) { + return %a + %b; + } + return SimObject::bar(1, 2); + )"); + + EXPECT_EQ(staticCall, 3); +} + +TEST(Script, Basic_Conditional_Statements) +{ + S32 value = RunScript(R"( + $a = "hello"; + if ($a $= "hello") + return 1; + return 2; + )"); + + EXPECT_EQ(value, 1); + + const char* ternaryValue = RunScript(R"( + return $a $= "hello" ? "World" : "No U"; + )"); + + EXPECT_STREQ(ternaryValue, "World"); +} + +TEST(Script, Basic_Loop_Statements) +{ + S32 whileValue = RunScript(R"( + $count = 0; + while ($count < 5) + $count++; + return $count; + )"); + + EXPECT_EQ(whileValue, 5); + + const char* forValue = RunScript(R"( + function t(%times) + { + %result = ""; + for (%i = 0; %i < %times; %i++) + %result = %result @ "a"; + return %result; + } + + return t(3); + )"); + + EXPECT_STREQ(forValue, "aaa"); + + const char* forIfValue = RunScript(R"( + function t() { + %str = ""; + for (%i = 0; %i < 5; %i++) { + + %loopValue = %i; + + if (%str $= "") + %str = %loopValue; + else + %str = %str @ "," SPC %loopValue; + } + return %str; + } + + return t(); + )"); + + EXPECT_STREQ(forIfValue, "0, 1, 2, 3, 4"); +} + +TEST(Script, TorqueScript_Array_Testing) +{ + S32 value = RunScript(R"( + function t(%idx) { %a[idx] = 2; return %a[idx]; } + return t(5); + )"); + + EXPECT_EQ(value, 2); + + S32 value2 = RunScript(R"( + function t(%idx) { %a[idx, 0] = 2; return %a[idx, 0]; } + return t(5); + )"); + + EXPECT_EQ(value2, 2); +} + +TEST(Script, Basic_SimObject) +{ + SimObject* object = RunScript(R"( + return new SimObject(FudgeCollector) { + fudge = "Chocolate"; + }; + )"); + + EXPECT_NE(object, (SimObject*)NULL); + + const char* propertyValue = RunScript(R"( + return FudgeCollector.fudge; + )"); + + EXPECT_STREQ(propertyValue, "Chocolate"); + + const char* funcReturn = RunScript(R"( + function SimObject::fooFunc(%this) + { + return "Bar"; + } + + return FudgeCollector.fooFunc(); + )"); + + EXPECT_STREQ(funcReturn, "Bar"); + + const char* parentFn = RunScript(R"( + new SimObject(Hello); + + function SimObject::fooFunc2(%this) + { + return "Bar"; + } + + function Hello::fooFunc2(%this) + { + %bar = Parent::fooFunc2(%this); + return "Foo" @ %bar; + } + + return Hello.fooFunc2(); + )"); + + EXPECT_STREQ(parentFn, "FooBar"); +} + +TEST(Script, Basic_Package) +{ + S32 value = RunScript(R"( + function a() { return 3; } + package overrides { + function a() { return 5; } + }; + return a(); + )"); + + EXPECT_EQ(value, 3); + + S32 overrideValue = RunScript(R"( + activatePackage(overrides); + return a(); + )"); + + EXPECT_EQ(overrideValue, 5); + + S32 deactivatedValue = RunScript(R"( + deactivatePackage(overrides); + return a(); + )"); + + EXPECT_EQ(deactivatedValue, 3); +} + +#endif diff --git a/Engine/source/console/test/consoleTest.cpp b/Engine/source/console/test/consoleTest.cpp index aa0b3970e..adea797a2 100644 --- a/Engine/source/console/test/consoleTest.cpp +++ b/Engine/source/console/test/consoleTest.cpp @@ -252,36 +252,4 @@ TEST(Con, execute) STR.popFrame(); } -static U32 gConsoleStackFrame = 0; - -ConsoleFunction(testConsoleStackFrame, S32, 1, 1, "") -{ - gConsoleStackFrame = CSTK.mFrame; - return (U32)Con::executef("testScriptEvalFunction"); // execute a sub function which manipulates the stack -} - -TEST(Con, evaluate) -{ - U32 startStackPos = CSTK.mStackPos; - U32 startStringStackPos = STR.mStart; - S32 returnValue = Con::evaluate("function testScriptEvalFunction() {return \"1\"@\"2\"@\"3\";}\nreturn testConsoleStackFrame();", false, "testEvaluate"); - U32 frame = CSTK.mFrame; - - EXPECT_TRUE(returnValue == 123) << - "Evaluate should return 123"; - - EXPECT_TRUE(gConsoleStackFrame == (frame+2)) << - "Console stack frame inside function should be +2"; - - EXPECT_TRUE(CSTK.mFrame == frame) << - "Console stack frame outside function should be the same as before"; - - EXPECT_TRUE(STR.mStart == startStringStackPos) << - "Console string stack should not be changed"; - - EXPECT_TRUE(CSTK.mStackPos == startStackPos) << - "Console stack should not be changed"; - -} - -#endif \ No newline at end of file +#endif diff --git a/Tools/CMake/torque3d.cmake b/Tools/CMake/torque3d.cmake index 0c1791a5d..c64a18986 100644 --- a/Tools/CMake/torque3d.cmake +++ b/Tools/CMake/torque3d.cmake @@ -247,6 +247,7 @@ addPath("${srcDir}/sfx/media") addPath("${srcDir}/sfx/null") addPath("${srcDir}/sfx") addPath("${srcDir}/console") +addPath("${srcDir}/console/test") addPath("${srcDir}/core") addPath("${srcDir}/core/stream") addPath("${srcDir}/core/strings")