Moved component unit tests.

This commit is contained in:
Daniel Buckmaster 2014-07-15 12:31:56 +02:00
parent ad0899ae27
commit 21ecb6f50d
5 changed files with 251 additions and 220 deletions

View file

@ -21,7 +21,6 @@
//-----------------------------------------------------------------------------
#include "component/moreAdvancedComponent.h"
#include "unit/test.h"
// unitTest_runTests("Component/MoreAdvancedComponent");
@ -58,50 +57,4 @@ bool MoreAdvancedComponent::testDependentInterface()
return false;
return mSCInterface->isFortyTwo( 42 );
}
//////////////////////////////////////////////////////////////////////////
using namespace UnitTesting;
CreateUnitTest(MoreAdvancedComponentTest, "Component/MoreAdvancedComponent")
{
void run()
{
// Create component instances and compose them.
SimComponent *parentComponent = new SimComponent();
SimpleComponent *simpleComponent = new SimpleComponent();
MoreAdvancedComponent *moreAdvComponent = new MoreAdvancedComponent();
// CodeReview note that the interface pointer isn't initialized in a ctor
// on the components, so it's bad memory against which you might
// be checking in testDependentInterface [3/3/2007 justind]
parentComponent->addComponent( simpleComponent );
parentComponent->addComponent( moreAdvComponent );
simpleComponent->registerObject();
moreAdvComponent->registerObject();
// Put a break-point here, follow the onAdd call, and observe the order in
// which the SimComponent::onAdd function executes. You will see the interfaces
// get cached, and the dependent interface query being made.
parentComponent->registerObject();
// If the MoreAdvancedComponent found an interface, than the parentComponent
// should have returned true, from onAdd, and should therefore be registered
// properly with the Sim
test( parentComponent->isProperlyAdded(), "Parent component not properly added!" );
// Now lets test the interface. You can step through this, as well.
test( moreAdvComponent->testDependentInterface(), "Dependent interface test failed." );
// CodeReview is there a reason we can't just delete the parentComponent here? [3/3/2007 justind]
//
// Clean up
parentComponent->removeComponent( simpleComponent );
parentComponent->removeComponent( moreAdvComponent );
parentComponent->deleteObject();
moreAdvComponent->deleteObject();
simpleComponent->deleteObject();
}
};
}

View file

@ -28,124 +28,4 @@ ConsoleDocClass( SimpleComponent,
"@brief The purpose of this component is to provide a minimalistic component that "
"exposes a simple, cached interface\n\n"
"Soon to be deprecated, internal only.\n\n "
"@internal");
//////////////////////////////////////////////////////////////////////////
// It may seem like some weak sauce to use a unit test for this, however
// it is very, very easy to set breakpoints in a unit test, and trace
// execution in the debugger, so I will use a unit test.
//
// Note I am not using much actual 'test' functionality, just providing
// an easy place to examine the functionality that was described and implemented
// in the header file.
//
// If you want to run this code, simply run Torque, pull down the console, and
// type:
// unitTest_runTests("Components/SimpleComponent");
#include "unit/test.h"
using namespace UnitTesting;
CreateUnitTest(TestSimpleComponent, "Components/SimpleComponent")
{
void run()
{
// When instantiating, and working with a SimObject in C++ code, such as
// a unit test, you *may not* allocate a SimObject off of the stack.
//
// For example:
// SimpleComponent sc;
// is a stack allocation. This memory is allocated off of the program stack
// when the function is called. SimObject deletion is done via SimObject::deleteObject()
// and the last command of this method is 'delete this;' That command will
// cause an assert if it is called on stack-allocated memory. Therefor, when
// instantiating SimObjects in C++ code, it is imperitive that you keep in
// mind that if any script calls 'delete()' on that SimObject, or any other
// C++ code calls 'deleteObject()' on that SimObject, it will crash.
SimpleComponent *sc = new SimpleComponent();
// SimObject::registerObject must be called on a SimObject before it is
// fully 'hooked in' to the engine.
//
// Tracing execution of this function will let you see onAdd get called on
// the component, and you will see it cache the interface we exposed.
sc->registerObject();
// It is *not* required that a component always be owned by a component (obviously)
// however I am using an owner so that you can trace execution of recursive
// calls to cache interfaces and such.
SimComponent *testOwner = new SimComponent();
// Add the test component to it's owner. This will set the 'mOwner' field
// of 'sc' to the address of 'testOwner'
testOwner->addComponent( sc );
// If you step-into this registerObject the same way as the previous one,
// you will be able to see the recursive caching of the exposed interface.
testOwner->registerObject();
// Now to prove that object composition is working properly, lets ask
// both of these components for their interface lists...
// The ComponentInterfaceList is a typedef for type 'VectorPtr<ComponentInterface *>'
// and it will be used by getInterfaces() to store the results of the interface
// query. This is the "complete" way to obtain an interface, and it is too
// heavy-weight for most cases. A simplified query will be performed next,
// to demonstrate the usage of both.
ComponentInterfaceList iLst;
// This query requests all interfaces, on all components, regardless of name
// or owner.
sc->getInterfaces( &iLst,
// This is the type field. I am passing NULL here to signify that the query
// should match all values of 'type' in the list.
NULL,
// The name field, let's pass NULL again just so when you trace execution
// you can see how queries work in the simple case, first.
NULL );
// Lets process the list that we've gotten back, and find the interface that
// we want.
SimpleComponentInterface *scQueriedInterface = NULL;
for( ComponentInterfaceListIterator i = iLst.begin(); i != iLst.end(); i++ )
{
scQueriedInterface = dynamic_cast<SimpleComponentInterface *>( *i );
if( scQueriedInterface != NULL )
break;
}
AssertFatal( scQueriedInterface != NULL, "No valid SimpleComponentInterface was found in query" );
// Lets do it again, only we will execute the query on the parent instead,
// in a simplified way. Remember the parent component doesn't expose any
// interfaces at all, so the success of this behavior is entirely dependent
// on the recursive registration that occurs in registerInterfaces()
SimpleComponentInterface *ownerQueriedInterface = testOwner->getInterface<SimpleComponentInterface>();
AssertFatal( ownerQueriedInterface != NULL, "No valid SimpleComponentInterface was found in query" );
// We should now have two pointers to the same interface obtained by querying
// different components.
test( ownerQueriedInterface == scQueriedInterface, "This really shouldn't be possible to fail given the setup of the test" );
// Lets call the method that was exposed on the component via the interface.
// Trace the execution of this function, if you wish.
test( ownerQueriedInterface->isFortyTwo( 42 ), "Don't panic, but it's a bad day in the component system." );
test( scQueriedInterface->isFortyTwo( 42 ), "Don't panic, but it's a bad day in the component system." );
// So there you have it. Writing a simple component that exposes a cached
// interface, and testing it. It's time to clean up.
testOwner->removeComponent( sc );
sc->deleteObject();
testOwner->deleteObject();
// Interfaces do not need to be freed. In Juggernaught, these will be ref-counted
// for more robust behavior. Right now, however, the values of our two interface
// pointers, scQueriedInterface and ownerQueriedInterface, reference invalid
// memory.
}
};
"@internal");

View file

@ -0,0 +1,68 @@
//-----------------------------------------------------------------------------
// 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 "component/moreAdvancedComponent.h"
TEST(MoreAdvancedComponent, MoreAdvancedComponent)
{
// Create component instances and compose them.
SimComponent *parentComponent = new SimComponent();
SimpleComponent *simpleComponent = new SimpleComponent();
MoreAdvancedComponent *moreAdvComponent = new MoreAdvancedComponent();
// CodeReview note that the interface pointer isn't initialized in a ctor
// on the components, so it's bad memory against which you might
// be checking in testDependentInterface [3/3/2007 justind]
parentComponent->addComponent( simpleComponent );
parentComponent->addComponent( moreAdvComponent );
simpleComponent->registerObject();
moreAdvComponent->registerObject();
// Put a break-point here, follow the onAdd call, and observe the order in
// which the SimComponent::onAdd function executes. You will see the interfaces
// get cached, and the dependent interface query being made.
parentComponent->registerObject();
// If the MoreAdvancedComponent found an interface, than the parentComponent
// should have returned true, from onAdd, and should therefore be registered
// properly with the Sim
EXPECT_TRUE( parentComponent->isProperlyAdded() )
<< "Parent component not properly added!";
// Now lets test the interface. You can step through this, as well.
EXPECT_TRUE( moreAdvComponent->testDependentInterface() )
<< "Dependent interface test failed.";
// CodeReview is there a reason we can't just delete the parentComponent here? [3/3/2007 justind]
//
// Clean up
parentComponent->removeComponent( simpleComponent );
parentComponent->removeComponent( moreAdvComponent );
parentComponent->deleteObject();
moreAdvComponent->deleteObject();
simpleComponent->deleteObject();
};
#endif

View file

@ -1,5 +1,5 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
// 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
@ -20,14 +20,10 @@
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "unit/test.h"
#include "unit/memoryTester.h"
#ifdef TORQUE_TESTS_ENABLED
#include "testing/unitTesting.h"
#include "component/simComponent.h"
using namespace UnitTesting;
//////////////////////////////////////////////////////////////////////////
class CachedInterfaceExampleComponent : public SimComponent
{
typedef SimComponent Parent;
@ -89,18 +85,16 @@ public:
// CodeReview [patw, 2, 17, 2007] I'm going to make another lightweight interface
// for this functionality later
void unit_test( UnitTest *test )
void unit_test()
{
AssertFatal(test, "CachedInterfaceExampleComponent::unit_test - NULL UnitTest");
if( !test )
return;
test->test( mpU32 != NULL, "Pointer to dependent interface is NULL" );
EXPECT_TRUE( mpU32 != NULL )
<< "Pointer to dependent interface is NULL";
if( mpU32 )
{
test->test( *(*mpU32) & ( 1 << 24 ), "Pointer to interface data is bogus." );
test->test( *(*mpU32) != *mMyId, "Two of me have the same ID, bad!" );
EXPECT_TRUE( *(*mpU32) & ( 1 << 24 ))
<< "Pointer to interface data is bogus.";
EXPECT_TRUE( *(*mpU32) != *mMyId)
<< "Two of me have the same ID, bad!";
}
}
};
@ -113,42 +107,47 @@ ConsoleDocClass( CachedInterfaceExampleComponent,
"Not intended for game development, for editors or internal use only.\n\n "
"@internal");
//////////////////////////////////////////////////////////////////////////
CreateUnitTest(TestComponentInterfacing, "Components/Composition")
TEST(SimComponent, Composition)
{
void run()
SimComponent *testComponent = new SimComponent();
CachedInterfaceExampleComponent *componentA = new CachedInterfaceExampleComponent();
CachedInterfaceExampleComponent *componentB = new CachedInterfaceExampleComponent();
// Register sub-components
EXPECT_TRUE( componentA->registerObject())
<< "Failed to register componentA";
EXPECT_TRUE( componentB->registerObject())
<< "Failed to register componentB";
// Add the components
EXPECT_TRUE( testComponent->addComponent( componentA ))
<< "Failed to add component a to testComponent";
EXPECT_TRUE( testComponent->addComponent( componentB ))
<< "Failed to add component b to testComponent";
EXPECT_EQ( componentA->getOwner(), testComponent)
<< "testComponent did not properly set the mOwner field of componentA to NULL.";
EXPECT_EQ( componentB->getOwner(), testComponent)
<< "testComponent did not properly set the mOwner field of componentB to NULL.";
// Register the object with the simulation, kicking off the interface registration
const bool registered = testComponent->registerObject();
EXPECT_TRUE( registered )
<< "Failed to register testComponent";
// Interface tests
if( registered )
{
MemoryTester m;
m.mark();
SimComponent *testComponent = new SimComponent();
CachedInterfaceExampleComponent *componentA = new CachedInterfaceExampleComponent();
CachedInterfaceExampleComponent *componentB = new CachedInterfaceExampleComponent();
// Register sub-components
test( componentA->registerObject(), "Failed to register componentA" );
test( componentB->registerObject(), "Failed to register componentB" );
// Add the components
test( testComponent->addComponent( componentA ), "Failed to add component a to testComponent" );
test( testComponent->addComponent( componentB ), "Failed to add component b to testComponent" );
test( componentA->getOwner() == testComponent, "testComponent did not properly set the mOwner field of componentA to NULL." );
test( componentB->getOwner() == testComponent, "testComponent did not properly set the mOwner field of componentB to NULL." );
// Register the object with the simulation, kicking off the interface registration
const bool registered = testComponent->registerObject();
test( registered, "Failed to register testComponent" );
// Interface tests
if( registered )
{
componentA->unit_test( this );
componentB->unit_test( this );
testComponent->deleteObject();
}
test( m.check(), "Component composition test leaked memory." );
SCOPED_TRACE("componentA");
componentA->unit_test();
}
{
SCOPED_TRACE("componentB");
componentB->unit_test();
}
testComponent->deleteObject();
}
};
};
#endif

View file

@ -0,0 +1,131 @@
//-----------------------------------------------------------------------------
// 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 "component/simpleComponent.h"
TEST(SimpleComponent, SimpleComponent)
{
// When instantiating, and working with a SimObject in C++ code, such as
// a unit test, you *may not* allocate a SimObject off of the stack.
//
// For example:
// SimpleComponent sc;
// is a stack allocation. This memory is allocated off of the program stack
// when the function is called. SimObject deletion is done via SimObject::deleteObject()
// and the last command of this method is 'delete this;' That command will
// cause an assert if it is called on stack-allocated memory. Therefor, when
// instantiating SimObjects in C++ code, it is imperitive that you keep in
// mind that if any script calls 'delete()' on that SimObject, or any other
// C++ code calls 'deleteObject()' on that SimObject, it will crash.
SimpleComponent *sc = new SimpleComponent();
// SimObject::registerObject must be called on a SimObject before it is
// fully 'hooked in' to the engine.
//
// Tracing execution of this function will let you see onAdd get called on
// the component, and you will see it cache the interface we exposed.
sc->registerObject();
// It is *not* required that a component always be owned by a component (obviously)
// however I am using an owner so that you can trace execution of recursive
// calls to cache interfaces and such.
SimComponent *testOwner = new SimComponent();
// Add the test component to it's owner. This will set the 'mOwner' field
// of 'sc' to the address of 'testOwner'
testOwner->addComponent( sc );
// If you step-into this registerObject the same way as the previous one,
// you will be able to see the recursive caching of the exposed interface.
testOwner->registerObject();
// Now to prove that object composition is working properly, lets ask
// both of these components for their interface lists...
// The ComponentInterfaceList is a typedef for type 'VectorPtr<ComponentInterface *>'
// and it will be used by getInterfaces() to store the results of the interface
// query. This is the "complete" way to obtain an interface, and it is too
// heavy-weight for most cases. A simplified query will be performed next,
// to demonstrate the usage of both.
ComponentInterfaceList iLst;
// This query requests all interfaces, on all components, regardless of name
// or owner.
sc->getInterfaces( &iLst,
// This is the type field. I am passing NULL here to signify that the query
// should match all values of 'type' in the list.
NULL,
// The name field, let's pass NULL again just so when you trace execution
// you can see how queries work in the simple case, first.
NULL );
// Lets process the list that we've gotten back, and find the interface that
// we want.
SimpleComponentInterface *scQueriedInterface = NULL;
for( ComponentInterfaceListIterator i = iLst.begin(); i != iLst.end(); i++ )
{
scQueriedInterface = dynamic_cast<SimpleComponentInterface *>( *i );
if( scQueriedInterface != NULL )
break;
}
AssertFatal( scQueriedInterface != NULL, "No valid SimpleComponentInterface was found in query" );
// Lets do it again, only we will execute the query on the parent instead,
// in a simplified way. Remember the parent component doesn't expose any
// interfaces at all, so the success of this behavior is entirely dependent
// on the recursive registration that occurs in registerInterfaces()
SimpleComponentInterface *ownerQueriedInterface = testOwner->getInterface<SimpleComponentInterface>();
AssertFatal( ownerQueriedInterface != NULL, "No valid SimpleComponentInterface was found in query" );
// We should now have two pointers to the same interface obtained by querying
// different components.
EXPECT_EQ( ownerQueriedInterface, scQueriedInterface )
<< "This really shouldn't be possible to fail given the setup of the test";
// Lets call the method that was exposed on the component via the interface.
// Trace the execution of this function, if you wish.
EXPECT_TRUE( ownerQueriedInterface->isFortyTwo( 42 ) )
<< "Don't panic, but it's a bad day in the component system.";
EXPECT_TRUE( scQueriedInterface->isFortyTwo( 42 ) )
<< "Don't panic, but it's a bad day in the component system.";
// So there you have it. Writing a simple component that exposes a cached
// interface, and testing it. It's time to clean up.
testOwner->removeComponent( sc );
sc->deleteObject();
testOwner->deleteObject();
// Interfaces do not need to be freed. In Juggernaught, these will be ref-counted
// for more robust behavior. Right now, however, the values of our two interface
// pointers, scQueriedInterface and ownerQueriedInterface, reference invalid
// memory.
};
#endif