separate testing environment

-Separate main for running unit tests
-Move unit tests into testing folder
This commit is contained in:
marauder2k7 2023-07-24 12:38:36 +01:00
parent 2e8f5795fa
commit c09f79d199
265 changed files with 84537 additions and 334 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,200 @@
#include "testing/unitTesting.h"
#include "platform/platform.h"
#include "console/simBase.h"
#include "console/consoleTypes.h"
#include "console/scriptObjects.h"
#include "console/simBase.h"
#include "console/engineAPI.h"
#include "math/mMath.h"
#include "console/stringStack.h"
#include "console/consoleInternal.h"
// Stupid globals not declared in a header
extern ExprEvalState gEvalState;
using ::testing::Matcher;
using ::testing::TypedEq;
class ConsoleTest : public ::testing::Test
{
protected:
ConsoleTest()
{
}
void SetUp() override
{
}
};
TEST_F(ConsoleTest, executef)
{
char buffer[128];
Con::evaluate("function testExecutef(%a,%b,%c,%d,%e,%f,%g,%h,%i,%j,%k){return %a SPC %b SPC %c SPC %d SPC %e SPC %f SPC %g SPC %h SPC %i SPC %j SPC %k;}\r\nfunction testThisFunction(%a,%b,%c,%d,%e,%f,%g,%h,%i,%j, %this){ return %a SPC %b SPC %c SPC %d SPC %e SPC %f SPC %g SPC %h SPC %i SPC %j;}\r\n", false, "test");
// Check basic calls with SimObject. We'll do this for every single possible call just to make sure.
ConsoleValue returnValue;
returnValue = Con::executef("testThisFunction");
EXPECT_STREQ(returnValue, " ") <<
"All values should be printed in the correct order";
returnValue = Con::executef("testThisFunction", "a");
EXPECT_STREQ(returnValue, "a ") <<
"All values should be printed in the correct order";
returnValue = Con::executef("testThisFunction", "a", "b");
EXPECT_STREQ(returnValue, "a b ") <<
"All values should be printed in the correct order";
returnValue = Con::executef("testThisFunction", "a", "b", "c");
EXPECT_STREQ(returnValue, "a b c ") <<
"All values should be printed in the correct order";
returnValue = Con::executef("testThisFunction", "a", "b", "c", "d");
EXPECT_STREQ(returnValue, "a b c d ") <<
"All values should be printed in the correct order";
returnValue = Con::executef("testThisFunction", "a", "b", "c", "d", "e");
EXPECT_STREQ(returnValue, "a b c d e ") <<
"All values should be printed in the correct order";
returnValue = Con::executef("testThisFunction", "a", "b", "c", "d", "e", "f");
EXPECT_STREQ(returnValue, "a b c d e f ") <<
"All values should be printed in the correct order";
returnValue = Con::executef("testThisFunction", "a", "b", "c", "d", "e", "f", "g");
EXPECT_STREQ(returnValue, "a b c d e f g ") <<
"All values should be printed in the correct order";
returnValue = Con::executef("testThisFunction", "a", "b", "c", "d", "e", "f", "g", "h");
EXPECT_STREQ(returnValue, "a b c d e f g h ") <<
"All values should be printed in the correct order";
returnValue = Con::executef("testThisFunction", "a", "b", "c", "d", "e", "f", "g", "h", "i");
EXPECT_STREQ(returnValue, "a b c d e f g h i ") <<
"All values should be printed in the correct order";
// Now test without the object
returnValue = Con::executef("testExecutef");
EXPECT_STREQ(returnValue, " ") <<
"All values should be printed in the correct order";
returnValue = Con::executef("testExecutef", "a");
EXPECT_STREQ(returnValue, "a ") <<
"All values should be printed in the correct order";
returnValue = Con::executef("testExecutef", "a", "b");
EXPECT_STREQ(returnValue, "a b ") <<
"All values should be printed in the correct order";
returnValue = Con::executef("testExecutef", "a", "b", "c");
EXPECT_STREQ(returnValue, "a b c ") <<
"All values should be printed in the correct order";
returnValue = Con::executef("testExecutef", "a", "b", "c", "d");
EXPECT_STREQ(returnValue, "a b c d ") <<
"All values should be printed in the correct order";
returnValue = Con::executef("testExecutef", "a", "b", "c", "d", "e");
EXPECT_STREQ(returnValue, "a b c d e ") <<
"All values should be printed in the correct order";
returnValue = Con::executef("testExecutef", "a", "b", "c", "d", "e", "f");
EXPECT_STREQ(returnValue, "a b c d e f ") <<
"All values should be printed in the correct order";
returnValue = Con::executef("testExecutef", "a", "b", "c", "d", "e", "f", "g");
EXPECT_STREQ(returnValue, "a b c d e f g ") <<
"All values should be printed in the correct order";
returnValue = Con::executef("testExecutef", "a", "b", "c", "d", "e", "f", "g", "h");
EXPECT_STREQ(returnValue, "a b c d e f g h ") <<
"All values should be printed in the correct order";
returnValue = Con::executef("testExecutef", "a", "b", "c", "d", "e", "f", "g", "h", "i");
EXPECT_STREQ(returnValue, "a b c d e f g h i ") <<
"All values should be printed in the correct order";
returnValue = Con::executef("testExecutef", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j");
EXPECT_STREQ(returnValue, "a b c d e f g h i j ") <<
"All values should be printed in the correct order";
// Test type conversions with and without SimObject...
// Integer
returnValue = Con::executef("testThisFunction", 123);
EXPECT_STREQ(returnValue, "123 ") <<
"Integer should be converted";
returnValue = Con::executef("testExecutef", 123);
EXPECT_STREQ(returnValue, "123 ") <<
"Integer should be converted";
// Float
returnValue = Con::executef("testThisFunction", (F32)123.0);
EXPECT_STREQ(returnValue, "123 ") <<
"Float should be converted";
returnValue = Con::executef("testExecutef", (F32)123.0);
EXPECT_STREQ(returnValue, "123 ") <<
"Float should be converted";
// Point3F
Point3F point(1,2,3);
returnValue = Con::executef("testThisFunction", point);
EXPECT_STREQ(returnValue, "1 2 3 ") <<
"Point3F should be converted";
returnValue = Con::executef("testExecutef", point);
EXPECT_STREQ(returnValue, "1 2 3 ") <<
"Point3F should be converted";
// Finally test the function arg offset. This should be consistently 0 after each call
EXPECT_EQ(STR.mFunctionOffset, 0) <<
"Function offset should be 0";
const char *floatArg = Con::getFloatArg(1.23);
EXPECT_GT(STR.mFunctionOffset, 0) <<
"Function offset should not be 0";
Con::executef("testExecutef", floatArg);
EXPECT_EQ(STR.mFunctionOffset, 0) <<
"Function offset should be 0";
floatArg = Con::getFloatArg(1.23);
EXPECT_GT(STR.mFunctionOffset, 0) <<
"Function offset should not be 0";
}
TEST_F(ConsoleTest, execute)
{
Con::evaluate("function testScriptExecuteFunction(%a,%b) {return %a SPC %b;}\nfunction testScriptExecuteFunction(%a,%b,%this) {return %a SPC %b;}\r\n", false, "testExecute");
U32 startStackPos = gEvalState.getStackDepth();
U32 startStringStackPos = STR.mStart;
// const char* versions of execute should maintain stack
const char *argv[] = {"testScriptExecuteFunction", "1", "2"};
const char *argvObject[] = {"testScriptExecuteFunction", "1", "2", ""};
ConsoleValue returnValue = Con::execute(3, argv);
EXPECT_STREQ(returnValue, "1 2") <<
"execute should return 1 2";
EXPECT_EQ(gEvalState.getStackDepth(), startStackPos) <<
"execute should restore stack";
returnValue = Con::execute(4, argvObject);
EXPECT_STREQ(returnValue, "1 2") <<
"execute should return 1 2";
EXPECT_EQ(gEvalState.getStackDepth(), startStackPos) <<
"execute should restore stack";
}

View file

@ -0,0 +1,176 @@
#include "testing/unitTesting.h"
#include "platform/platform.h"
#include "console/simBase.h"
#include "console/consoleTypes.h"
#include "console/scriptObjects.h"
#include "console/simBase.h"
#include "console/engineAPI.h"
#include "math/mMath.h"
using ::testing::Matcher;
using ::testing::TypedEq;
class EngineAPITest : public ::testing::Test
{
protected:
EngineAPITest()
{
}
void SetUp() override
{
ScriptObject* test = new ScriptObject();
test->assignName("TestConExec");
}
};
TEST_F(EngineAPITest, EngineMarshallData)
{
// Reserve some values
ConsoleValue values[16];
// Basic string casting...
SimObject *foo = new SimObject();
foo->registerObject();
const char *value = EngineMarshallData(foo);
EXPECT_STREQ(value, foo->getIdString())
<< "SimObject should be casted to its ID";
U32 unsignedNumber = 123;
S32 signedNumber = -123;
value = EngineMarshallData(unsignedNumber);
EXPECT_STREQ(value, "123")
<< "Integer should be converted to 123";
value = EngineMarshallData(signedNumber);
EXPECT_STREQ(value, "-123")
<< "Integer should be converted to -123";
bool boolValue = true;
value = EngineMarshallData(boolValue);
EXPECT_STREQ(value, "1")
<< "Bool should be converted to 1";
Point3F point(1,2,3);
value = EngineMarshallData(point);
EXPECT_STREQ(value, "1 2 3")
<< "Point3F should be converted to 1 2 3";
F32 floatValue = 1.23f;
value = EngineMarshallData(floatValue);
EXPECT_STREQ(value, "1.23")
<< "F32 should be converted to 1.23";
// Argv based casting
S32 argc = 0;
EngineMarshallData(foo, argc, values);
EngineMarshallData((const SimObject*)foo, argc, values);
EngineMarshallData(point, argc, values);
EngineMarshallData(unsignedNumber, argc, values);
EngineMarshallData(signedNumber, argc, values);
EngineMarshallData(boolValue, argc, values);
EngineMarshallData(floatValue, argc, values);
EXPECT_EQ(argc, 7)
<< "7 args should have been set";
EXPECT_EQ(values[0].getType(), ConsoleValueType::cvInteger)
<< "1st arg should be an int";
EXPECT_EQ(values[0].getInt(), foo->getId())
<< "1st arg should be foo's id";
EXPECT_EQ(values[1].getType(), ConsoleValueType::cvInteger)
<< "2nd arg should be an int";
EXPECT_EQ(values[1].getInt(), foo->getId())
<< "2nd arg should be foo's id";
EXPECT_EQ(values[2].getType(), ConsoleValueType::cvString)
<< "3rd arg should be a string";
EXPECT_STREQ(values[2].getString(), "1 2 3")
<< "3rd arg should be 1 2 3";
EXPECT_EQ(values[3].getType(), ConsoleValueType::cvInteger)
<< "4th arg should be a float";
EXPECT_EQ(values[3].getInt(), 123)
<< "4th arg should be 123";
EXPECT_EQ(values[4].getType(), ConsoleValueType::cvInteger)
<< "5th arg should be a float";
EXPECT_EQ(values[4].getInt(), -123)
<< "5th arg should be -123";
EXPECT_EQ(values[5].getType(), ConsoleValueType::cvInteger)
<< "6th arg should be a float";
EXPECT_TRUE(values[5].getBool())
<< "6th arg should be true";
EXPECT_EQ(values[6].getType(), ConsoleValueType::cvFloat)
<< "7th arg should be a float";
EXPECT_FLOAT_EQ(values[6].getFloat(), 1.23)
<< "7th arg should be 1.23";
foo->deleteObject();
}
TEST_F(EngineAPITest, EngineUnMarshallData)
{
SimObject *foo = new SimObject();
foo->registerObject();
SimObject *testFoo = EngineUnmarshallData<SimObject*>()(foo->getIdString());
EXPECT_EQ(foo, testFoo)
<< "Unmarshalling foo's id should return foo";
testFoo = EngineUnmarshallData<SimObject*>()("ShouldNotExist_Really123");
EXPECT_TRUE(testFoo == NULL)
<< "Unmarshalling a bad object should return NULL";
foo->deleteObject();
}
TEST_F(EngineAPITest, _EngineConsoleCallbackHelper)
{
Con::evaluate("if (isObject(TestConExec)) {\r\nTestConExec.delete();\r\n}\r\nfunction testExecutef(%a,%b,%c,%d,%e,%f,%g,%h,%i,%j,%k){return %a SPC %b SPC %c SPC %d SPC %e SPC %f SPC %g SPC %h SPC %i SPC %j SPC %k;}\r\nfunction TestConExec::testThisFunction(%this,%a,%b,%c,%d,%e,%f,%g,%h,%i,%j){ return %a SPC %b SPC %c SPC %d SPC %e SPC %f SPC %g SPC %h SPC %i SPC %j;}\r\nnew ScriptObject(TestConExec);\r\n", false, "test");
SimObject *testObject = NULL;
Sim::findObject("TestConExec", testObject);
_EngineConsoleCallbackHelper helper("testExecutef", NULL);
ConsoleValue returnValue = helper.call<ConsoleValue>("a", "b", "c");
EXPECT_STREQ(returnValue, "a b c ") <<
"All values should be printed in the correct order";
_EngineConsoleCallbackHelper objectHelper("testThisFunction", testObject);
returnValue = helper.call<ConsoleValue>("a", "b", "c");
EXPECT_STREQ(returnValue, "a b c ") <<
"All values should be printed in the correct order";
}
// NOTE: this is also indirectly tested by the executef tests
TEST_F(EngineAPITest, _EngineConsoleExecCallbackHelper)
{
Con::evaluate("if (isObject(TestConExec)) {\r\nTestConExec.delete();\r\n}\r\nfunction testExecutef(%a,%b,%c,%d,%e,%f,%g,%h,%i,%j,%k){return %a SPC %b SPC %c SPC %d SPC %e SPC %f SPC %g SPC %h SPC %i SPC %j SPC %k;}\r\nfunction TestConExec::testThisFunction(%this,%a,%b,%c,%d,%e,%f,%g,%h,%i,%j){ return %a SPC %b SPC %c SPC %d SPC %e SPC %f SPC %g SPC %h SPC %i SPC %j;}\r\nnew ScriptObject(TestConExec);\r\n", false, "test");
SimObject *testObject = NULL;
Sim::findObject("TestConExec", testObject);
_EngineConsoleExecCallbackHelper<const char*> helper("testExecutef");
ConsoleValue returnValue = helper.call<ConsoleValue>("a", "b", "c");
EXPECT_STREQ(returnValue, "a b c ") <<
"All values should be printed in the correct order";
_EngineConsoleExecCallbackHelper<SimObject*> objectHelper(testObject);
returnValue = objectHelper.call<ConsoleValue>("testThisFunction", "a", "b", "c");
EXPECT_STREQ(returnValue, "a b c ") <<
"All values should be printed in the correct order";
}

View file

@ -0,0 +1,70 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 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/threads/mutex.h"
#include "platform/threads/thread.h"
TEST(Mutex, BasicSynchronization)
{
// We test various scenarios wrt to locking and unlocking, in a single
// thread, just to make sure our basic primitives are working in the
// most basic case.
void *mutex1 = Mutex::createMutex();
EXPECT_TRUE(mutex1 != NULL)
<< "First Mutex::createMutex call failed - that's pretty bad!";
// This mutex is intentionally unused.
void *mutex2 = Mutex::createMutex();
EXPECT_TRUE(mutex2 != NULL)
<< "Second Mutex::createMutex call failed - that's pretty bad, too!";
EXPECT_TRUE(Mutex::lockMutex(mutex1, false))
<< "Nonblocking call to brand new mutex failed - should not be.";
EXPECT_TRUE(Mutex::lockMutex(mutex1, true))
<< "Failed relocking a mutex from the same thread - should be able to do this.";
// Try to acquire the mutex from another thread.
struct thread
{
static void body(void* mutex)
{
// We should not be able to lock the mutex from a separate thread, but
// we don't want to block either.
EXPECT_FALSE(Mutex::lockMutex(mutex, false));
}
};
Thread thread(&thread::body, mutex1);
thread.start();
thread.join();
// Unlock & kill mutex 1
Mutex::unlockMutex(mutex1);
Mutex::unlockMutex(mutex1);
Mutex::destroyMutex(mutex1);
// Kill mutex2, which was never touched.
Mutex::destroyMutex(mutex2);
}
#endif

View file

@ -0,0 +1,178 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 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/platformNet.h"
#include "core/util/journal/process.h"
struct TcpHandle
{
NetSocket mSocket;
S32 mDataReceived;
void notify(NetSocket sock, U32 state)
{
// Only consider our own socket.
if(mSocket != sock)
return;
// Ok - what's the state? We do some dumb responses to given states
// in order to fulfill the request.
if(state == Net::Connected)
{
U8 reqBuffer[] = {
"GET / HTTP/1.0\nUser-Agent: Torque/1.0\n\n"
};
Net::Error e = Net::sendtoSocket(mSocket, reqBuffer, sizeof(reqBuffer));
ASSERT_EQ(Net::NoError, e)
<< "Got an error sending our HTTP request!";
}
else
{
Process::requestShutdown();
mSocket = NetSocket::INVALID;
ASSERT_EQ(Net::Disconnected, state)
<< "Ended with a network error!";
}
}
void receive(NetSocket sock, RawData incomingData)
{
// Only consider our own socket.
if(mSocket != sock)
return;
mDataReceived += incomingData.size;
}
};
TEST(Net, TCPRequest)
{
TcpHandle handler;
handler.mSocket = NetSocket::INVALID;
handler.mDataReceived = 0;
// Hook into the signals.
Net::smConnectionNotify ->notify(&handler, &TcpHandle::notify);
Net::smConnectionReceive->notify(&handler, &TcpHandle::receive);
// Open a TCP connection to torque3d.org
handler.mSocket = Net::openConnectTo("108.61.193.195:80");
const U32 limit = Platform::getRealMilliseconds() + (5*1000);
while(Process::processEvents() && (Platform::getRealMilliseconds() < limit) ) {}
// Unhook from the signals.
Net::smConnectionNotify ->remove(&handler, &TcpHandle::notify);
Net::smConnectionReceive->remove(&handler, &TcpHandle::receive);
EXPECT_GT(handler.mDataReceived, 0)
<< "Didn't get any data back!";
}
struct JournalHandle
{
NetSocket mSocket;
S32 mDataReceived;
void notify(NetSocket sock, U32 state)
{
// Only consider our own socket.
if(mSocket != sock)
return;
// Ok - what's the state? We do some dumb responses to given states
// in order to fulfill the request.
if(state == Net::Connected)
{
U8 reqBuffer[] = {
"GET / HTTP/1.0\nUser-Agent: Torque/1.0\n\n"
};
Net::Error e = Net::sendtoSocket(mSocket, reqBuffer, sizeof(reqBuffer));
ASSERT_EQ(Net::NoError, e)
<< "Got an error sending our HTTP request!";
}
else
{
Process::requestShutdown();
mSocket = NetSocket::INVALID;
ASSERT_EQ(Net::Disconnected, state)
<< "Ended with a network error!";
}
}
void receive(NetSocket sock, RawData incomingData)
{
// Only consider our own socket.
if(mSocket != sock)
return;
mDataReceived += incomingData.size;
}
void makeRequest()
{
mSocket = NetSocket::INVALID;
mDataReceived = 0;
// Hook into the signals.
Net::smConnectionNotify ->notify(this, &JournalHandle::notify);
Net::smConnectionReceive->notify(this, &JournalHandle::receive);
// Open a TCP connection to torque3d.org
mSocket = Net::openConnectTo("108.61.193.195:80");
// Let the callbacks enable things to process.
while(Process::processEvents()) {}
// Unhook from the signals.
Net::smConnectionNotify ->remove(this, &JournalHandle::notify);
Net::smConnectionReceive->remove(this, &JournalHandle::receive);
EXPECT_GT(mDataReceived, 0)
<< "Didn't get any data back!";
}
};
TEST(Net, JournalTCPRequest)
{
JournalHandle handler;
Journal::Record("journalTCP.jrn");
ASSERT_TRUE(Journal::IsRecording());
handler.makeRequest();
S32 bytesRead = handler.mDataReceived;
Journal::Stop();
Journal::Play("journalTCP.jrn");
handler.makeRequest();
Journal::Stop();
EXPECT_EQ(bytesRead, handler.mDataReceived)
<< "Didn't get same data back from journal playback.";
}
#endif

View file

@ -0,0 +1,145 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
#include "testing/unitTesting.h"
#include "platform/platform.h"
#include "core/fileio.h"
#include "core/util/tVector.h"
#include "console/console.h"
class PlatformFileIOTest : public ::testing::Test
{
protected:
PlatformFileIOTest()
{
// create a file for tests.
f.open("testTouch.file", File::WriteAppend);
f.close();
Platform::getFileTimes("testTouch.file", &initCreate, NULL);
}
void SetUp() override
{
}
File f;
FileTime initCreate;
void TearDown() override
{
// Clean up..
dFileDelete("testTouch.file");
}
};
TEST_F(PlatformFileIOTest, ExcludedDirectories)
{
// Just dump everything under the current directory. We should
// find at least one file.
// Exclude .svn and CVS
Platform::clearExcludedDirectories();
Platform::addExcludedDirectory(".svn");
Platform::addExcludedDirectory("CVS");
EXPECT_TRUE(Platform::isExcludedDirectory(".svn"))
<< "On list, should be excluded.";
EXPECT_TRUE(Platform::isExcludedDirectory("CVS"))
<< "On list, should be excluded.";
EXPECT_FALSE(Platform::isExcludedDirectory("foo"))
<< "Doesn't match list, shouldn't be excluded.";
EXPECT_FALSE(Platform::isExcludedDirectory(".svnCVS"))
<< "Looks like a duck, but it shouldn't be excluded cuz it's distinct from all entries on the exclusion list.";
// Ok, now our exclusion list is setup, so let's dump some paths.
Vector<Platform::FileInfo> pathInfo;
Platform::dumpPath(Platform::getCurrentDirectory(), pathInfo, 2);
EXPECT_GT(pathInfo.size(), 0)
<< "Should find at least SOMETHING in the current directory!";
// This'll nuke info if we run it in a live situation... so don't run unit
// tests in a live situation. ;)
Platform::clearExcludedDirectories();
};
TEST_F(PlatformFileIOTest, CreateTest)
{
EXPECT_TRUE(Platform::isFile("testTouch.file"))
<< "Class should have created this file.";
}
TEST_F(PlatformFileIOTest, CreateTimeTest)
{
FileTime testCreate;
Platform::getFileTimes("testTouch.file", &testCreate, NULL);
EXPECT_EQ(Platform::compareFileTimes(testCreate, initCreate), 0)
<< "Create timestamps should match.";
}
TEST_F(PlatformFileIOTest, ModifyTimesTest)
{
FileTime modify[2];
// Touch a file and note its last-modified.
dFileTouch("testTouch.file");
Platform::getFileTimes("testTouch.file", NULL, &modify[0]);
// Sleep for a second
Platform::sleep(1000);
dFileTouch("testTouch.file");
Platform::getFileTimes("testTouch.file", NULL, &modify[1]);
// Now compare the times...
EXPECT_LT(Platform::compareFileTimes(modify[0], modify[1]), 0)
<< "Timestamps are wrong - modify[0] should be before modify[1]!";
};
TEST_F(PlatformFileIOTest, DeleteFileTest)
{
// Clean up..
dFileDelete("testTouch.file");
EXPECT_FALSE(Platform::isFile("testTouch.file"))
<< "Somehow failed to delete our test file.";
}
// Mac/Linux have no implementations for these functions, so we 'def it out for now.
#ifdef WIN32
TEST_F(PlatformFileIOTest, Volumes)
{
Vector<const char*> names;
Platform::getVolumeNamesList(names);
EXPECT_GT(names.size(), 0)
<< "We should have at least one volume...";
Vector<Platform::VolumeInformation> info;
Platform::getVolumeInformationList(info);
EXPECT_EQ(names.size(), info.size())
<< "Got inconsistent number of volumes back from info vs. name list functions!";
};
#endif

View file

@ -0,0 +1,91 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 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.
//-----------------------------------------------------------------------------
#include "testing/unitTesting.h"
#include "platform/platformTimer.h"
#include "core/util/journal/process.h"
#include "math/mMath.h"
TEST(PlatformTimerTest, AdvanceTime)
{
U32 time = Platform::getVirtualMilliseconds();
Platform::advanceTime(10);
U32 newTime = Platform::getVirtualMilliseconds();
EXPECT_EQ(10, newTime - time)
<< "We advanced 10ms but didn't get a 10ms delta!";
}
TEST(PlatformTimerTest, Sleep)
{
U32 start = Platform::getRealMilliseconds();
Platform::sleep(500);
U32 end = Platform::getRealMilliseconds();
EXPECT_GE(end - start, 500-10) // account for clock resolution
<< "We didn't sleep at least as long as we requested!";
};
struct handle
{
S32 mElapsedTime;
S32 mNumberCalls;
handle() : mElapsedTime(0), mNumberCalls(0) {}
void timeEvent(S32 timeDelta)
{
mElapsedTime += timeDelta;
mNumberCalls++;
if(mElapsedTime >= 1000)
Process::requestShutdown();
}
};
TEST(PlatformTimerTest, BasicAPI)
{
handle handler;
// Initialize the time manager...
TimeManager time;
time.timeEvent.notify(&handler, &handle::timeEvent);
// Event loop till at least one second has passed.
const U32 start = Platform::getRealMilliseconds();
while(Process::processEvents())
{
// If we go too long, kill it off...
if(Platform::getRealMilliseconds() - start > 30*1000)
{
EXPECT_TRUE(false)
<< "Terminated process loop due to watchdog, not due to time manager event, after 30 seconds.";
Process::requestShutdown();
}
}
const U32 end = Platform::getRealMilliseconds();
// Now, confirm we have approximately similar elapsed times.
S32 elapsedRealTime = end - start;
EXPECT_LT(mAbs(elapsedRealTime - handler.mElapsedTime), 50)
<< "Failed to elapse time to within the desired tolerance.";
EXPECT_GT(handler.mNumberCalls, 0)
<< "Somehow got no event callbacks from TimeManager?";
};

View file

@ -0,0 +1,114 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 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.
//-----------------------------------------------------------------------------
#include "unitTesting.h"
#include "platform/platform.h"
#include "core/util/endian.h"
TEST(PlatformTypesTest, Sizes)
{
// Run through all the types and ensure they're the right size.
#define CheckType(typeName, expectedSize) \
EXPECT_EQ( sizeof(typeName), expectedSize) \
<< "Wrong size for a " #typeName ", expected " #expectedSize;
// One byte types.
CheckType(bool, 1);
CheckType(U8, 1);
CheckType(S8, 1);
CheckType(UTF8, 1);
// Two byte types.
CheckType(U16, 2);
CheckType(S16, 2);
CheckType(UTF16, 2);
// Four byte types.
CheckType(U32, 4);
CheckType(S32, 4);
CheckType(F32, 4);
CheckType(UTF32, 4);
// Eight byte types.
CheckType(U64, 8);
CheckType(S64, 8);
CheckType(F64, 8);
// 16 byte (128bit) types will go here, when we get some.
#undef CheckType
};
TEST(PlatformTypesTest, EndianConversion)
{
// Convenient and non-palindrome byte patterns to test with.
const U16 U16Test = 0xA1B2;
const S16 S16Test = 0x52A1;
const U32 U32Test = 0xA1B2C3D4;
const S32 S32Test = 0xD4C3B2A1;
const F32 F32Test = 1234.5678f;
//const U64 U64Test = 0xA1B2C3D4E3F2E10A;
//const S64 S64Test = 0x1A2B3C4D3E2F1EA0;
const F64 F64Test = 12345678.9101112131415;
// Run through all the conversions - bump stuff from host to little or big
// endian and back again.
#define CheckEndianRoundTrip(type, b_or_l) \
EXPECT_EQ( type##Test, convert##b_or_l##EndianToHost(convertHostTo##b_or_l##Endian(type##Test))) \
<< "Failed to convert the " #type " test value to " #b_or_l " endian and back to host endian order.";
#define CheckTypeBothWays(type) \
CheckEndianRoundTrip(type, B); \
CheckEndianRoundTrip(type, L);
#define CheckIntsForBitSize(bits) \
CheckTypeBothWays( U##bits ); \
CheckTypeBothWays( S##bits );
// Don't check 8-bit types - they aren't affected by endian issues.
// Check the >1 byte int types, though.
CheckIntsForBitSize(16);
CheckIntsForBitSize(32);
// CheckIntsForBitSize(64); // don't have convertHostToLEndian(const U64/S64) so this doesn't work
// And check the float types.
CheckTypeBothWays(F32);
CheckTypeBothWays(F64);
// We'd check 128bit types here, if we had any.
#undef CheckIntsForBitSize
#undef CheckTypeBothWays
#undef CheckEndianRoundTrip
};
TEST(PlatformTypesTest, EndianSwap)
{
U32 swap32 = 0xABCDEF12;
U16 swap16 = 0xABCD;
EXPECT_EQ(endianSwap(swap32), 0x12EFCDAB)
<< "32 bit endianSwap should reverse byte order, but didn't.";
EXPECT_EQ(endianSwap(swap16), 0xCDAB)
<< "16 bit endianSwap should reverse byte order, but didn't.";
};

View file

@ -0,0 +1,47 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 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.
//-----------------------------------------------------------------------------
#include "platform/platform.h" // Allows us to see TORQUE_ENABLE_PROFILER
#ifdef TORQUE_ENABLE_PROFILER
#include "testing/unitTesting.h"
#include "platform/profiler.h"
TEST(Profiler, ProfileStartEnd)
{
PROFILE_START(ProfileStartEndTest);
// Do work.
if(true)
{
PROFILE_END();
return;
}
PROFILE_END();
}
TEST(Profiler, ProfileScope)
{
PROFILE_SCOPE(ScopedProfilerTest);
// Do work and return whenever you want.
}
#endif

View file

@ -0,0 +1,105 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 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.
//-----------------------------------------------------------------------------
#include "testing/unitTesting.h"
#include "platform/platform.h"
#include "console/simBase.h"
#include "console/consoleTypes.h"
#include "console/runtimeClassRep.h"
class RuntimeRegisteredSimObject : public SimObject
{
typedef SimObject Parent;
protected:
bool mFoo;
public:
RuntimeRegisteredSimObject() : mFoo(false) {};
DECLARE_RUNTIME_CONOBJECT(RuntimeRegisteredSimObject);
static void initPersistFields();
};
IMPLEMENT_RUNTIME_CONOBJECT(RuntimeRegisteredSimObject);
void RuntimeRegisteredSimObject::initPersistFields()
{
addField("fooField", TypeBool, Offset(mFoo, RuntimeRegisteredSimObject));
}
TEST(RunTimeClassRepTest, RuntimeClassRep)
{
// First test to make sure that the test class is not registered (don't
// know how it could be, but that's programming for you). Stop the test if
// it is.
ASSERT_TRUE(!RuntimeRegisteredSimObject::dynRTClassRep.isRegistered())
<< "RuntimeRegisteredSimObject class was already registered with the console";
// This should not be able to find the class, and return null (this may
// AssertWarn as well).
ConsoleObject *conobj = ConsoleObject::create("RuntimeRegisteredSimObject");
EXPECT_TRUE(conobj == NULL)
<< "Unregistered AbstractClassRep returned non-NULL value! That is really bad!";
// Register with console system.
RuntimeRegisteredSimObject::dynRTClassRep.consoleRegister();
// Make sure that the object knows it's registered.
EXPECT_TRUE(RuntimeRegisteredSimObject::dynRTClassRep.isRegistered())
<< "RuntimeRegisteredSimObject class failed console registration";
// Now try again to create the instance.
conobj = ConsoleObject::create("RuntimeRegisteredSimObject");
EXPECT_TRUE(conobj != NULL)
<< "AbstractClassRep::create method failed!";
// Cast the instance, and test it.
RuntimeRegisteredSimObject *rtinst =
dynamic_cast<RuntimeRegisteredSimObject *>(conobj);
EXPECT_TRUE(rtinst != NULL)
<< "Casting failed for some reason";
// Register it with a name.
rtinst->registerObject("_utRRTestObject");
EXPECT_TRUE(rtinst->isProperlyAdded())
<< "registerObject failed on test object";
// Now execute some script on it.
Con::evaluate("_utRRTestObject.fooField = true;");
// Test to make sure field worked.
EXPECT_TRUE(dAtob(rtinst->getDataField(StringTable->insert("fooField"), NULL)))
<< "Set property failed on instance.";
// BALETED
rtinst->deleteObject();
// Unregister the type.
RuntimeRegisteredSimObject::dynRTClassRep.consoleUnRegister();
// And make sure we can't create another one.
conobj = ConsoleObject::create("RuntimeRegisteredSimObject");
EXPECT_TRUE(conobj == NULL)
<< "Unregistration of type failed";
}

View file

@ -0,0 +1,87 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 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.
//-----------------------------------------------------------------------------
#include "testing/unitTesting.h"
#include "platform/threads/semaphore.h"
#include "platform/threads/thread.h"
TEST(Semaphore, BasicSynchronization)
{
Semaphore *sem1 = new Semaphore(1);
Semaphore *sem2 = new Semaphore(1);
// Test that we can do non-blocking acquires that succeed.
EXPECT_TRUE(sem1->acquire(false))
<< "Should succeed at acquiring a new semaphore with count 1.";
EXPECT_TRUE(sem2->acquire(false))
<< "This one should succeed too, see previous test.";
// Test that we can do non-blocking acquires that fail.
EXPECT_FALSE(sem1->acquire(false))
<< "Should failed, as we've already got the sem.";
sem1->release();
EXPECT_FALSE(sem2->acquire(false))
<< "Should also fail.";
sem2->release();
// Test that we can do blocking acquires that succeed.
EXPECT_TRUE(sem1->acquire(true))
<< "Should succeed as we just released.";
EXPECT_TRUE(sem2->acquire(true))
<< "Should succeed as we just released.";
// Clean up.
delete sem1;
delete sem2;
}
TEST(Semaphore, MultiThreadSynchronization)
{
Semaphore semaphore(1);
struct thread
{
// Try to acquire the semaphore from another thread.
static void body1(void* sem)
{
Semaphore *semaphore = reinterpret_cast<Semaphore*>(sem);
EXPECT_TRUE(semaphore->acquire(true));
// Note that this semaphore is never released. Bad programmer!
}
// One more acquisition should fail!
static void body2(void* sem)
{
Semaphore *semaphore = reinterpret_cast<Semaphore*>(sem);
EXPECT_FALSE(semaphore->acquire(false));
}
};
Thread thread1(&thread::body1, &semaphore);
EXPECT_TRUE(semaphore.acquire(true));
thread1.start();
semaphore.release();
thread1.join();
Thread thread2(&thread::body2, &semaphore);
thread2.start();
thread2.join();
}

View file

@ -0,0 +1,118 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 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.
//-----------------------------------------------------------------------------
#include "testing/unitTesting.h"
#include "platform/threads/threadPool.h"
#include "console/console.h"
#include "core/util/tVector.h"
FIXTURE(ThreadPool)
{
public:
// Represents a single unit of work. In this test we just set an element in
// a result vector.
struct TestItem : public ThreadPool::WorkItem
{
U32 mIndex;
Vector<U32>& mResults;
TestItem(U32 index, Vector<U32>& results)
: mIndex(index), mResults(results) {}
protected:
virtual void execute()
{
mResults[mIndex] = mIndex;
}
};
// A worker that delays for some time. We'll use this to test the ThreadPool's
// synchronous and asynchronous operations.
struct DelayItem : public ThreadPool::WorkItem
{
U32 ms;
DelayItem(U32 _ms) : ms(_ms) {}
protected:
virtual void execute()
{
Platform::sleep(ms);
}
};
};
TEST_FIX(ThreadPool, BasicAPI)
{
// Construct the vector of results from the work items.
const U32 numItems = 100;
Vector<U32> results(__FILE__, __LINE__);
results.setSize(numItems);
for (U32 i = 0; i < numItems; i++)
results[i] = U32(-1);
// Launch the work items.
ThreadPool* pool = &ThreadPool::GLOBAL();
for (U32 i = 0; i < numItems; i++)
{
ThreadSafeRef<TestItem> item(new TestItem(i, results));
pool->queueWorkItem(item);
}
pool->waitForAllItems();
// Verify.
for (U32 i = 0; i < numItems; i++)
EXPECT_EQ(results[i], i) << "result mismatch";
results.clear();
}
TEST_FIX(ThreadPool, Asynchronous)
{
const U32 delay = 500; //ms
// Launch a single delaying work item.
ThreadPool* pool = &ThreadPool::GLOBAL();
ThreadSafeRef<DelayItem> item(new DelayItem(delay));
pool->queueWorkItem(item);
// The thread should not yet be finished.
EXPECT_EQ(false, item->hasExecuted());
// Wait til the item should have completed.
Platform::sleep(delay * 2);
EXPECT_EQ(true, item->hasExecuted());
}
TEST_FIX(ThreadPool, Synchronous)
{
const U32 delay = 500; //ms
// Launch a single delaying work item.
ThreadPool* pool = &ThreadPool::GLOBAL();
ThreadSafeRef<DelayItem> item(new DelayItem(delay));
pool->queueWorkItem(item);
// Wait for the item to complete.
pool->waitForAllItems();
EXPECT_EQ(true, item->hasExecuted());
}

View file

@ -0,0 +1,199 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 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.
//-----------------------------------------------------------------------------
#include "testing/unitTesting.h"
#include "platform/threads/threadSafeDeque.h"
#include "platform/threads/thread.h"
#include "core/util/tVector.h"
#include "console/console.h"
FIXTURE(ThreadSafeDeque)
{
public:
// Used by the concurrent test.
struct Value : public ThreadSafeRefCount<Value>
{
U32 mIndex;
U32 mTick;
Value() {}
Value(U32 index, U32 tick)
: mIndex(index), mTick(tick) {}
};
typedef ThreadSafeRef<Value> ValueRef;
struct Deque : public ThreadSafeDeque<ValueRef>
{
typedef ThreadSafeDeque<ValueRef> Parent;
U32 mPushIndex;
U32 mPopIndex;
Deque()
: mPushIndex(0), mPopIndex(0) {}
void pushBack(const ValueRef& value)
{
EXPECT_EQ(value->mIndex, mPushIndex) << "index out of line";
mPushIndex++;
Parent::pushBack(value);
}
bool tryPopFront(ValueRef& outValue)
{
if(Parent::tryPopFront(outValue))
{
EXPECT_EQ(outValue->mIndex, mPopIndex) << "index out of line";
mPopIndex++;
return true;
}
else
return false;
}
};
struct ProducerThread : public Thread
{
Vector<U32>& mValues;
Deque& mDeque;
ProducerThread(Vector<U32>& values, Deque& deque)
: mValues(values), mDeque(deque) {}
virtual void run(void*)
{
for(U32 i = 0; i < mValues.size(); i++)
{
U32 tick = Platform::getRealMilliseconds();
mValues[i] = tick;
ValueRef val = new Value(i, tick);
mDeque.pushBack(val);
}
}
};
struct ConsumerThread : public Thread
{
Vector<U32>& mValues;
Deque& mDeque;
ConsumerThread(Vector<U32>& values, Deque& deque)
: mValues(values), mDeque(deque) {}
virtual void run(void*)
{
S32 timeOut = mValues.size() * 32;
U32 endTime = Platform::getRealMilliseconds() + timeOut;
for (U32 i = 0; i < mValues.size(); i++)
{
ValueRef value;
bool timedOut = false;
while (!mDeque.tryPopFront(value))
{
if (timeOut && Platform::getRealMilliseconds() >= endTime)
{
timedOut = true;
break;
}
};
ASSERT_FALSE(timedOut)
<< "consumer thread timed out!";
if (timedOut) return;
EXPECT_EQ(i, value->mIndex);
EXPECT_EQ(value->mTick, mValues[i]);
}
}
};
};
// Test deque without concurrency.
TEST_FIX(ThreadSafeDeque, PopFront)
{
ThreadSafeDeque<char> deque;
String str = "teststring";
for(U32 i = 0; i < str.length(); i++)
deque.pushBack(str[i]);
EXPECT_FALSE(deque.isEmpty());
char ch;
for(U32 i = 0; i < str.length(); i++)
{
EXPECT_TRUE(deque.tryPopFront(ch));
EXPECT_EQ(str[i], ch);
}
ASSERT_TRUE(deque.isEmpty());
}
TEST_FIX(ThreadSafeDeque, PopBack)
{
ThreadSafeDeque<char> deque;
String str = "teststring";
const char* p1 = str.c_str() + 4;
const char* p2 = p1 + 1;
while(*p2)
{
deque.pushFront(*p1);
deque.pushBack(*p2);
--p1;
++p2;
}
char ch;
for(S32 i = str.length()-1; i >= 0; i--)
{
EXPECT_TRUE(deque.tryPopBack(ch));
EXPECT_EQ(str[i], ch);
}
ASSERT_TRUE(deque.isEmpty());
}
// Test many items in a row
TEST_FIX(ThreadSafeDeque, Concurrent)
{
const U32 NumValues = 100;
Deque mDeque;
Vector<U32> mValues;
mValues.setSize(NumValues);
ProducerThread pThread(mValues, mDeque);
ConsumerThread cThread(mValues, mDeque);
pThread.start();
cThread.start();
pThread.join();
cThread.join();
mValues.clear();
};

View file

@ -0,0 +1,143 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 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.
//-----------------------------------------------------------------------------
#include "testing/unitTesting.h"
#include "platform/threads/threadSafePriorityQueue.h"
#include "platform/threads/thread.h"
#include "core/util/tVector.h"
#include "console/console.h"
// Test queue without concurrency.
TEST(ThreadSafePriorityQueue, Serial)
{
const U32 min = 0;
const U32 max = 9;
const U32 len = 11;
U32 indices[len] = { 2, 7, 4, 6, 1, 5, 3, 8, 6, 9, 0};
F32 priorities[len] = {0.2f, 0.7f, 0.4f, 0.6f, 0.1f, 0.5f, 0.3f, 0.8f, 0.6f, 0.9f, 0.0f};
ThreadSafePriorityQueue<U32, F32, true> minQueue;
ThreadSafePriorityQueue<U32, F32, false> maxQueue;
for(U32 i = 0; i < len; i++)
{
minQueue.insert(priorities[i], indices[i]);
maxQueue.insert(priorities[i], indices[i]);
}
EXPECT_FALSE(minQueue.isEmpty());
EXPECT_FALSE(maxQueue.isEmpty());
U32 index = min;
for(U32 i = 0; i < len; i++)
{
U32 popped;
EXPECT_TRUE(minQueue.takeNext(popped))
<< "Failed to pop element from minQueue";
EXPECT_LE(index, popped)
<< "Element from minQueue was not in sort order";
index = popped;
}
index = max;
for(U32 i = 0; i < len; i++)
{
U32 popped;
EXPECT_TRUE(maxQueue.takeNext(popped))
<< "Failed to pop element from maxQueue";
EXPECT_GE(index, popped)
<< "Element from maxQueue was not in sort order";
index = popped;
}
}
// Test queue with concurrency.
TEST(ThreadSafePriorityQueue, Concurrent)
{
#define MIN 0
#define MAX 9
#define LEN 11
typedef ThreadSafePriorityQueue<U32, F32, true> MinQueue;
typedef ThreadSafePriorityQueue<U32, F32, false> MaxQueue;
struct ProducerThread : public Thread
{
MinQueue& minQueue;
MaxQueue& maxQueue;
ProducerThread(MinQueue& min, MaxQueue& max)
: minQueue(min), maxQueue(max) {}
virtual void run(void*)
{
U32 indices[LEN] = { 2, 7, 4, 6, 1, 5, 3, 8, 6, 9, 0};
F32 priorities[LEN] = {0.2f, 0.7f, 0.4f, 0.6f, 0.1f, 0.5f, 0.3f, 0.8f, 0.6f, 0.9f, 0.0f};
for(U32 i = 0; i < LEN; i++)
{
minQueue.insert(priorities[i], indices[i]);
maxQueue.insert(priorities[i], indices[i]);
}
}
};
MinQueue minQueue;
MaxQueue maxQueue;
ProducerThread producers[] = {
ProducerThread(minQueue, maxQueue),
ProducerThread(minQueue, maxQueue),
ProducerThread(minQueue, maxQueue)
};
const U32 len = sizeof(producers) / sizeof(ProducerThread);
for(U32 i = 0; i < len; i++)
producers[i].start();
for(U32 i = 0; i < len; i++)
producers[i].join();
U32 index = MIN;
for(U32 i = 0; i < LEN * len; i++)
{
U32 popped;
EXPECT_TRUE(minQueue.takeNext(popped))
<< "Failed to pop element from minQueue";
EXPECT_LE(index, popped)
<< "Element from minQueue was not in sort order";
index = popped;
}
index = MAX;
for(U32 i = 0; i < LEN * len; i++)
{
U32 popped;
EXPECT_TRUE(maxQueue.takeNext(popped))
<< "Failed to pop element from maxQueue";
EXPECT_GE(index, popped)
<< "Element from maxQueue was not in sort order";
index = popped;
}
#undef MIN
#undef MAX
#undef LEN
}

View file

@ -0,0 +1,205 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 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/threads/threadSafeRefCount.h"
#include "platform/threads/thread.h"
#include "core/util/tVector.h"
#include "console/console.h"
FIXTURE(ThreadSafeRefCount)
{
public:
struct TestObjectDtor : public ThreadSafeRefCount<TestObjectDtor>
{
bool &flag;
TestObjectDtor(bool &f) : flag(f)
{
flag = false;
}
~TestObjectDtor()
{
flag = true;
}
};
typedef ThreadSafeRef<TestObjectDtor> TestObjectDtorRef;
enum
{
NUM_ADD_REFS_PER_THREAD = 10,
NUM_EXTRA_REFS_PER_THREAD = 10,
NUM_THREADS = 10
};
class TestObject : public ThreadSafeRefCount<TestObject> {};
typedef ThreadSafeRef<TestObject> TestObjectRef;
class TestThread : public Thread
{
public:
TestObjectRef mRef;
Vector<TestObjectRef> mExtraRefs;
TestThread(TestObjectRef ref) : mRef(ref) {}
void run(void* arg)
{
if (!arg)
{
// Create references.
for (U32 i = 0; i < NUM_ADD_REFS_PER_THREAD; i++)
mRef->addRef();
mExtraRefs.setSize(NUM_EXTRA_REFS_PER_THREAD);
for (U32 i = 0; i < NUM_EXTRA_REFS_PER_THREAD; i++)
mExtraRefs[i] = mRef;
}
else
{
// Clear references.
mExtraRefs.clear();
for (U32 i = 0; i < NUM_ADD_REFS_PER_THREAD; i++)
mRef->release();
}
}
};
};
TEST_FIX(ThreadSafeRefCount, Serial)
{
bool deleted = false;
TestObjectDtorRef ref1 = new TestObjectDtor(deleted);
ASSERT_FALSE(deleted);
EXPECT_FALSE(ref1->isShared());
EXPECT_TRUE(ref1 != NULL);
TestObjectDtorRef ref2 = ref1;
EXPECT_TRUE(ref1->isShared());
EXPECT_TRUE(ref2->isShared());
EXPECT_EQ(ref1, ref2);
ref1 = NULL;
EXPECT_FALSE(ref2->isShared());
ref2 = NULL;
ASSERT_TRUE(deleted);
}
TEST_FIX(ThreadSafeRefCount, Concurrent)
{
TestObjectRef mRef = new TestObject;
EXPECT_EQ(2, mRef->getRefCount()); // increments of 2
Vector<TestThread*> threads;
threads.setSize(NUM_THREADS);
// Create threads.
for (U32 i = 0; i < NUM_THREADS; i++)
threads[i] = new TestThread(mRef);
// Run phase 1: create references.
for (U32 i = 0; i < NUM_THREADS; i++)
threads[i]->start(NULL);
// Wait for completion.
for (U32 i = 0; i < NUM_THREADS; i++)
threads[i]->join();
EXPECT_EQ(2 + ((1 + NUM_ADD_REFS_PER_THREAD + NUM_EXTRA_REFS_PER_THREAD) * NUM_THREADS * 2),
mRef->getRefCount());
// Run phase 2: release references.
for (U32 i = 0; i < NUM_THREADS; i++)
threads[i]->start((void*) 1);
// Wait for completion.
for (U32 i = 0; i < NUM_THREADS; i++)
{
threads[i]->join();
delete threads[i];
}
EXPECT_EQ(2, mRef->getRefCount()); // increments of two
mRef = NULL;
}
TEST_FIX(ThreadSafeRefCount, Tagging)
{
TestObjectRef ref;
EXPECT_FALSE(ref.isTagged());
EXPECT_FALSE(bool(ref));
EXPECT_FALSE(bool(ref.ptr()));
EXPECT_TRUE(ref.trySetFromTo(ref, NULL));
EXPECT_FALSE(ref.isTagged());
EXPECT_TRUE(ref.trySetFromTo(ref, NULL, TestObjectRef::TAG_Set));
EXPECT_TRUE(ref.isTagged());
EXPECT_TRUE(ref.trySetFromTo(ref, NULL, TestObjectRef::TAG_Set));
EXPECT_TRUE(ref.isTagged());
EXPECT_TRUE(ref.trySetFromTo(ref, NULL, TestObjectRef::TAG_Unset));
EXPECT_FALSE(ref.isTagged());
EXPECT_TRUE(ref.trySetFromTo(ref, NULL, TestObjectRef::TAG_Unset));
EXPECT_FALSE(ref.isTagged());
EXPECT_TRUE(ref.trySetFromTo(ref, NULL, TestObjectRef::TAG_SetOrFail));
EXPECT_TRUE(ref.isTagged());
EXPECT_FALSE(ref.trySetFromTo(ref, NULL, TestObjectRef::TAG_SetOrFail));
EXPECT_TRUE(ref.isTagged());
EXPECT_FALSE(ref.trySetFromTo(ref, NULL, TestObjectRef::TAG_FailIfSet));
EXPECT_TRUE(ref.trySetFromTo(ref, NULL, TestObjectRef::TAG_UnsetOrFail));
EXPECT_FALSE(ref.isTagged());
EXPECT_FALSE(ref.trySetFromTo(ref, NULL, TestObjectRef::TAG_UnsetOrFail));
EXPECT_FALSE(ref.isTagged());
EXPECT_FALSE(ref.trySetFromTo(ref, NULL, TestObjectRef::TAG_FailIfUnset));
TestObjectRef objectA = new TestObject;
TestObjectRef objectB = new TestObject;
EXPECT_FALSE(objectA->isShared());
EXPECT_FALSE(objectB->isShared());
ref = objectA;
EXPECT_FALSE(ref.isTagged());
EXPECT_TRUE(ref == objectA);
EXPECT_TRUE(ref == objectA.ptr());
EXPECT_TRUE(objectA->isShared());
EXPECT_TRUE(ref.trySetFromTo(objectA, objectB, TestObjectRef::TAG_Set));
EXPECT_TRUE(ref.isTagged());
EXPECT_EQ(ref, objectB);
EXPECT_EQ(ref, objectB.ptr());
EXPECT_TRUE(objectB->isShared());
EXPECT_FALSE(objectA->isShared());
EXPECT_TRUE(ref.trySetFromTo(ref, objectA));
EXPECT_TRUE(ref.isTagged());
EXPECT_EQ(ref, objectA);
EXPECT_EQ(ref, objectA.ptr());
}
#endif

View file

@ -0,0 +1,137 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 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.
//-----------------------------------------------------------------------------
#include "testing/unitTesting.h"
// This unit test will blow up without thread static support
#include "core/threadStatic.h"
#ifdef TORQUE_ENABLE_THREAD_STATICS
// Declare a test thread static
DITTS(U32, gUnitTestFoo, 42);
DITTS(F32, gUnitTestF32, 1.0);
TEST(ThreadStaticTest, BasicAPI)
{
// ThreadStatic list should be initialized right now, so lets see if it has
// any entries.
EXPECT_FALSE(_TorqueThreadStaticReg::getStaticList().empty())
<< "Self-registration has failed, or no statics declared";
// Spawn a new copy.
TorqueThreadStaticListHandle testInstance = _TorqueThreadStaticReg::spawnThreadStaticsInstance();
// Test the copy
ASSERT_EQ(_TorqueThreadStaticReg::getStaticList(0).size(), testInstance->size())
<< "Spawned static list has a different size from master copy.";
// Traverse the list and compare it to the initial value copy (index 0)
for(S32 i = 0; i < _TorqueThreadStaticReg::getStaticList().size(); i++)
{
_TorqueThreadStatic *master = _TorqueThreadStaticReg::getStaticList()[i];
_TorqueThreadStatic *cpy = (*testInstance)[i];
// Make sure it is not the same memory
EXPECT_NE(master, cpy)
<< "Copy not spawned properly.";
// Make sure the sizes are the same
ASSERT_EQ(master->getMemInstSize(), cpy->getMemInstSize())
<< "Size mismatch between master and copy";
// Make sure the initialization occurred properly
EXPECT_EQ(0, dMemcmp(master->getMemInstPtr(), cpy->getMemInstPtr(), master->getMemInstSize()))
<< "Initial value for spawned list is not correct";
}
// Test metrics if enabled
#ifdef TORQUE_ENABLE_THREAD_STATIC_METRICS
U32 fooHitCount = (*testInstance)[_gUnitTestFooTorqueThreadStatic::getListIndex()]->getHitCount();
#endif
// Set/get some values (If we test static metrics, this is hit 1)
ATTS_(gUnitTestFoo, 1) = 55;
// Test to see that the master copy and the spawned copy differ
// (If we test metrics, this is hit 2, for the instance, and hit 1 for the master)
EXPECT_NE(ATTS_(gUnitTestFoo, 0), ATTS_(gUnitTestFoo, 1))
<< "Assignment for spawned instanced memory failed";
#ifdef TORQUE_ENABLE_THREAD_STATIC_METRICS
U32 fooHitCount2 = (*testInstance)[_gUnitTestFooTorqueThreadStatic::getListIndex()]->getHitCount();
EXPECT_EQ(fooHitCount2, (fooHitCount + 2))
<< "Thread static metric hit count failed";
#endif
// Destroy instances
_TorqueThreadStaticReg::destroyInstance(testInstance);
}
#ifdef TORQUE_ENABLE_PROFILER
#include "math/mRandom.h"
#include "platform/profiler.h"
// Declare a test thread static
DITTS(U32, gInstancedStaticFoo, 42);
static U32 gTrueStaticFoo = 42;
TEST(ThreadStaticTest, StressThreadStatic)
{
ASSERT_FALSE(gProfiler->isEnabled())
<< "Profiler is currently enabled, test cannot continue";
// Spawn an instance
TorqueThreadStaticListHandle testInstance = _TorqueThreadStaticReg::spawnThreadStaticsInstance();
static const dsize_t TEST_SIZE = 100000;
// What we are going to do in this test is to test some U32 static
// performance. The test will be run TEST_SIZE times, and so first create
// an array of values to standardize the tests on.
U32 testValue[TEST_SIZE];
for(S32 i = 0; i < TEST_SIZE; i++)
testValue[i] = gRandGen.randI();
// Reset the profiler, tell it to dump to console when done
gProfiler->dumpToConsole();
gProfiler->enable(true);
// Value array is initialized, so lets put the foo's through the paces
PROFILE_START(ThreadStaticPerf_TrueStaticAssign);
for(int i = 0; i < TEST_SIZE; i++)
gTrueStaticFoo = testValue[i];
PROFILE_END();
PROFILE_START(ThreadStaticPerf_InstanceStaticAssign);
for(S32 i = 0; i < TEST_SIZE; i++)
ATTS_(gInstancedStaticFoo, 1) = testValue[i];
PROFILE_END();
gProfiler->enable(false);
// Clean up instance
_TorqueThreadStaticReg::destroyInstance(testInstance);
}
#endif
#endif

View file

@ -0,0 +1,84 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 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.
//-----------------------------------------------------------------------------
#include "testing/unitTesting.h"
#include "platform/threads/thread.h"
TEST(ThreadTest, CallbackAPI)
{
#define VALUE_TO_SET 10
// This struct exists just so we can define run as a local function.
struct thread
{
// Do some work we can observe.
static void body(void* arg)
{
U32* value = reinterpret_cast<U32*>(arg);
*value = VALUE_TO_SET;
}
};
// Test most basic Thread API functions.
U32 value = ~VALUE_TO_SET;
Thread thread(&thread::body, reinterpret_cast<void*>(&value));
thread.start();
EXPECT_TRUE(thread.isAlive());
thread.join();
EXPECT_FALSE(thread.isAlive());
EXPECT_EQ(value, VALUE_TO_SET)
<< "Thread did not set expected value!";
#undef VALUE_TO_SET
}
TEST(ThreadTest, InheritanceAPI)
{
#define VALUE_TO_SET 10
// This struct exists just so we can define run as a local function.
struct thread : public Thread
{
U32* mPtr;
thread(U32* ptr): mPtr(ptr) {}
// Do some work we can observe.
virtual void run(void*)
{
*mPtr = VALUE_TO_SET;
}
};
// Test most basic Thread API functions.
U32 value = ~VALUE_TO_SET;
thread thread(&value);
thread.start();
EXPECT_TRUE(thread.isAlive());
thread.join();
EXPECT_FALSE(thread.isAlive());
EXPECT_EQ(value, VALUE_TO_SET)
<< "Thread did not set expected value!";
#undef VALUE_TO_SET
}

View file

@ -19,63 +19,75 @@
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "unitTesting.h"
#ifdef TORQUE_TESTS_ENABLED
#include "app/mainLoop.h"
#include "console/console.h"
#include "console/codeBlock.h"
#include "console/engineAPI.h"
#include "console/consoleInternal.h"
#include "unitTesting.h"
#include "memoryTester.h"
#include <gtest/src/gtest-all.cc>
//-----------------------------------------------------------------------------
class TorqueUnitTestListener : public ::testing::EmptyTestEventListener
{
// Called before a test starts.
virtual void OnTestStart( const ::testing::TestInfo& testInfo )
virtual void OnTestStart(const ::testing::TestInfo& testInfo)
{
if( mVerbose )
if (mVerbose)
Con::printf("> Starting Test '%s.%s'",
testInfo.test_case_name(), testInfo.name());
}
// Called after a failed assertion or a SUCCEED() invocation.
virtual void OnTestPartResult( const ::testing::TestPartResult& testPartResult )
virtual void OnTestPartResult(const ::testing::TestPartResult& testPartResult)
{
if ( testPartResult.failed() )
if (testPartResult.failed())
{
Con::warnf(">> Failed with '%s' in '%s' at (line:%d)\n",
testPartResult.summary(),
testPartResult.file_name(),
testPartResult.line_number()
);
);
}
else if( mVerbose )
else if (mVerbose)
{
Con::printf(">> Passed with '%s' in '%s' at (line:%d)",
testPartResult.summary(),
testPartResult.file_name(),
testPartResult.line_number()
);
);
}
}
// Called after a test ends.
virtual void OnTestEnd( const ::testing::TestInfo& testInfo )
virtual void OnTestEnd(const ::testing::TestInfo& testInfo)
{
if( mVerbose )
Con::printf("> Ending Test '%s.%s'\n",
if (testInfo.result()->Failed())
{
Con::printf("TestClass:%s Test:%s Failed!",
testInfo.test_case_name(), testInfo.name());
}
else if(testInfo.result()->Passed())
{
Con::printf("TestClass:%s Test:%s Succeeded!",
testInfo.test_case_name(), testInfo.name());
}
else
{
Con::printf("TestClass:%s Test:%s Skipped!",
testInfo.test_case_name(), testInfo.name());
}
Con::printf("> Ending Test\n");
}
bool mVerbose;
public:
TorqueUnitTestListener( bool verbose ) : mVerbose( verbose ) {}
TorqueUnitTestListener(bool verbose) : mVerbose(verbose) {}
};
class TorqueScriptFixture : public testing::Test {};
@ -92,7 +104,33 @@ private:
const char* mFunctionName;
};
DefineEngineFunction( addUnitTest, void, (const char* function),,
// uncomment to debug tests and use the test explorer.
//#define TEST_EXPLORER
#if !defined(TEST_EXPLORER)
int main(int argc, char** argv)
{
StandardMainLoop::init();
StandardMainLoop::handleCommandLine(argc, (const char**)argv);
StandardMainLoop::shutdown();
return StandardMainLoop::getReturnStatus();
}
#else
int main(int argc, char** argv)
{
StandardMainLoop::init();
printf("Running main() from %s\n", __FILE__);
// setup simular to runTests
Con::evaluate("GFXInit::createNullDevice();");
Con::evaluate("if (!isObject(GuiDefaultProfile)) new GuiControlProfile(GuiDefaultProfile){}; if (!isObject(GuiTooltipProfile)) new GuiControlProfile(GuiTooltipProfile){};");
testing::InitGoogleTest(&argc, argv);
int res = RUN_ALL_TESTS();
StandardMainLoop::shutdown();
return res;
}
#endif
DefineEngineFunction(addUnitTest, void, (const char* function), ,
"Add a TorqueScript function as a GTest unit test.\n"
"@note This is only implemented rudimentarily to open the door for future development in unit-testing the engine.\n"
"@tsexample\n"
@ -129,7 +167,7 @@ String scriptFileMessage(const char* message)
return String::ToString("at %s: %s", scriptLine, message);
}
DefineEngineFunction( expectTrue, void, (bool test, const char* message),(""),
DefineEngineFunction(expectTrue, void, (bool test, const char* message), (""),
"TorqueScript wrapper around the EXPECT_TRUE assertion in GTest.\n"
"@tsexample\n"
"expectTrue(2+2 == 4, \"basic math should work\");\n"
@ -138,7 +176,7 @@ DefineEngineFunction( expectTrue, void, (bool test, const char* message),(""),
EXPECT_TRUE(test) << scriptFileMessage(message).c_str();
}
DefineEngineFunction( runAllUnitTests, int, (const char* testSpecs, const char* reportFormat), (""),
DefineEngineFunction(runAllUnitTests, int, (const char* testSpecs, const char* reportFormat), (""),
"Runs engine unit tests. Some tests are marked as 'stress' tests which do not "
"necessarily check correctness, just performance or possible nondeterministic "
"glitches. There may also be interactive or networking tests which may be "
@ -154,7 +192,7 @@ DefineEngineFunction( runAllUnitTests, int, (const char* testSpecs, const char*
Vector<char*> args;
args.push_back(NULL); // Program name is unused by googletest.
String specsArg;
if ( dStrlen( testSpecs ) > 0 )
if (dStrlen(testSpecs) > 0)
{
specsArg = testSpecs;
specsArg.replace(' ', ':');
@ -163,16 +201,16 @@ DefineEngineFunction( runAllUnitTests, int, (const char* testSpecs, const char*
}
String reportFormatArg;
if ( dStrlen(reportFormat) > 0 )
if (dStrlen(reportFormat) > 0)
{
reportFormatArg = String::ToString("--gtest_output=%s", reportFormat);
args.push_back(const_cast<char*>(reportFormatArg.c_str()));
}
S32 argc = args.size();
// Initialize Google Test.
testing::InitGoogleTest( &argc, args.address() );
testing::InitGoogleTest(&argc, args.address());
// Fetch the unit test instance.
testing::UnitTest& unitTest = *testing::UnitTest::GetInstance();
@ -180,22 +218,20 @@ DefineEngineFunction( runAllUnitTests, int, (const char* testSpecs, const char*
testing::TestEventListeners& listeners = unitTest.listeners();
// Release the default listener.
delete listeners.Release( listeners.default_result_printer() );
delete listeners.Release(listeners.default_result_printer());
if ( Con::getBoolVariable( "$Testing::CheckMemoryLeaks", false ) ) {
if (Con::getBoolVariable("$Testing::CheckMemoryLeaks", false)) {
// Add the memory leak tester.
listeners.Append( new testing::MemoryLeakDetector );
listeners.Append(new testing::MemoryLeakDetector);
}
// Add the Torque unit test listener.
listeners.Append( new TorqueUnitTestListener(false) );
listeners.Append(new TorqueUnitTestListener(true));
// Perform googletest run.
Con::printf( "\nUnit Tests Starting...\n" );
Con::printf("\nUnit Tests Starting...\n");
const S32 result = RUN_ALL_TESTS();
Con::printf( "... Unit Tests Ended.\n" );
Con::printf("... Unit Tests Ended.\n");
return result;
}
#endif // TORQUE_TESTS_ENABLED

View file

@ -23,9 +23,12 @@
#ifndef _UNIT_TESTING_H_
#define _UNIT_TESTING_H_
#ifdef TORQUE_TESTS_ENABLED
#if defined(SDL_MAIN_AVAILABLE)
#undef SDL_MAIN_AVAILABLE
#endif
#include <gtest/gtest.h>
#include <gmock/gmock.h>
/// Convenience to define a test fixture with a Fixture suffix for use with
/// TEST_FIX.
@ -38,6 +41,4 @@
GTEST_TEST_(test_fixture, test_name, test_fixture##Fixture, \
::testing::internal::GetTypeId<test_fixture##Fixture>())
#endif // TORQUE_TESTS_ENABLED
#endif // _UNIT_TESTING_H_

View file

@ -0,0 +1,109 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 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.
//-----------------------------------------------------------------------------
#include "unitTesting.h"
#include "windowManager/platformWindow.h"
#include "windowManager/platformWindowMgr.h"
#include "platform/platformInput.h"
#include "platform/input/IProcessInput.h"
#include "windowManager/sdl/sdlWindowMgr.h"
#include "core/util/tVector.h"
using ::testing::Matcher;
using ::testing::TypedEq;
class PlatformWindowManagerSDLTest : public ::testing::Test
{
protected:
PlatformWindowManagerSDLTest()
{
putenv("SDL_VIDEODRIVER=dummy");
putenv("SDL_AUDIODRIVER=dummy");
// for tests in this class we probably only need the init_video an nothing else.
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_GAMECONTROLLER | SDL_INIT_EVENTS | SDL_INIT_NOPARACHUTE);
}
void SetUp() override
{
}
};
TEST_F(PlatformWindowManagerSDLTest, Constructor)
{
PlatformWindowManagerSDL* pwm = static_cast<PlatformWindowManagerSDL*>(CreatePlatformWindowManager());
ASSERT_TRUE(pwm) << "no monitor to test against!";
}
TEST_F(PlatformWindowManagerSDLTest, PrimaryRectTest)
{
PlatformWindowManagerSDL* pwm = static_cast<PlatformWindowManagerSDL*>(CreatePlatformWindowManager());
// Check out the primary desktop area...
RectI primary = pwm->getPrimaryDesktopArea();
EXPECT_TRUE(primary.isValidRect())
<< "Got some sort of invalid rect from the window manager!";
}
TEST_F(PlatformWindowManagerSDLTest, MonitorRectsValid)
{
PlatformWindowManagerSDL* pwm = static_cast<PlatformWindowManagerSDL*>(CreatePlatformWindowManager());
// Now try to get info about all the monitors.
Vector<RectI> monitorRects;
pwm->getMonitorRegions(monitorRects);
// should override the function above to test this with multiple setups.
for (S32 i = 0; i < monitorRects.size(); i++)
{
EXPECT_TRUE(monitorRects[i].isValidRect())
<< "Got an invalid rect for this monitor - no good.";
}
}
TEST_F(PlatformWindowManagerSDLTest, MonitorRectsAtLeastOne)
{
PlatformWindowManagerSDL* pwm = static_cast<PlatformWindowManagerSDL*>(CreatePlatformWindowManager());
// Now try to get info about all the monitors.
Vector<RectI> monitorRects;
pwm->getMonitorRegions(monitorRects);
EXPECT_GT(monitorRects.size(), 0)
<< "Should get at least one monitor rect back from getMonitorRegions!";
}
TEST_F(PlatformWindowManagerSDLTest, MonitorRectsOverflow)
{
PlatformWindowManagerSDL* pwm = static_cast<PlatformWindowManagerSDL*>(CreatePlatformWindowManager());
// Now try to get info about all the monitors.
Vector<RectI> monitorRects;
pwm->getMonitorRegions(monitorRects);
// This test is here just to detect overflow/runaway situations. -- BJG
EXPECT_LT(monitorRects.size(), 64)
<< "Either something's wrong, or you have a lot of monitors...";
}