diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /unittests/dmxtest | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'unittests/dmxtest')
| -rw-r--r-- | unittests/dmxtest/dmxtest.cpp | 45 | ||||
| -rw-r--r-- | unittests/dmxtest/dmxtest.vpc | 57 | ||||
| -rw-r--r-- | unittests/dmxtest/dmxtest_dmeloglayers.cpp | 908 | ||||
| -rw-r--r-- | unittests/dmxtest/dmxtest_vcdtodme.cpp | 110 | ||||
| -rw-r--r-- | unittests/dmxtest/dmxtestarray.cpp | 76 | ||||
| -rw-r--r-- | unittests/dmxtest/dmxtestdmelog.cpp | 393 | ||||
| -rw-r--r-- | unittests/dmxtest/dmxtestloader.cpp | 211 | ||||
| -rw-r--r-- | unittests/dmxtest/dmxtestnotify.cpp | 111 | ||||
| -rw-r--r-- | unittests/dmxtest/dmxtestserialization.cpp | 760 | ||||
| -rw-r--r-- | unittests/dmxtest/dmxtestundoredo.cpp | 1096 | ||||
| -rw-r--r-- | unittests/dmxtest/dmxtestvalue.cpp | 104 |
11 files changed, 3871 insertions, 0 deletions
diff --git a/unittests/dmxtest/dmxtest.cpp b/unittests/dmxtest/dmxtest.cpp new file mode 100644 index 0000000..55261d6 --- /dev/null +++ b/unittests/dmxtest/dmxtest.cpp @@ -0,0 +1,45 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Unit test program for DMX testing +// +// $NoKeywords: $ +//=============================================================================// + +#include "unitlib/unitlib.h" +#include "filesystem.h" +#include "datamodel/idatamodel.h" +#include "datamodel/dmelementfactoryhelper.h" +#include "tier3/tier3dm.h" + + +//----------------------------------------------------------------------------- +// Used to connect/disconnect the DLL +//----------------------------------------------------------------------------- +class CDmxTestAppSystem : public CTier3DmAppSystem< IAppSystem > +{ + typedef CTier3DmAppSystem< IAppSystem > BaseClass; + +public: + CDmxTestAppSystem() + { + } + + virtual bool Connect( CreateInterfaceFn factory ) + { + if ( !BaseClass::Connect( factory ) ) + return false; + + if ( !g_pFullFileSystem || !g_pDataModel || !g_pDmElementFramework ) + return false; + + return true; + } + + virtual InitReturnVal_t Init() + { + MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f ); + return BaseClass::Init(); + } +}; + +USE_UNITTEST_APPSYSTEM( CDmxTestAppSystem ) diff --git a/unittests/dmxtest/dmxtest.vpc b/unittests/dmxtest/dmxtest.vpc new file mode 100644 index 0000000..6f5069f --- /dev/null +++ b/unittests/dmxtest/dmxtest.vpc @@ -0,0 +1,57 @@ +//----------------------------------------------------------------------------- +// DMXTEST.VPC +// +// Project Script +//----------------------------------------------------------------------------- + +$Macro SRCDIR "..\.." +$Macro OUTBINDIR "$SRCDIR\..\game\bin\unittests" + +$Include "$SRCDIR\vpc_scripts\source_dll_base.vpc" + +$Configuration +{ + $Compiler + { + $AdditionalIncludeDirectories "$BASE,$SRCDIR\game\shared,$SRCDIR\utils\common" + $PreprocessorDefinitions "$BASE;DMXTEST_EXPORTS" + } +} + +$Project "Dmxtest" +{ + $Folder "Source Files" + { + $File "$SRCDIR\movieobjects\dmx_to_vcd.cpp" + $File "dmxtest.cpp" + $File "dmxtest_dmeloglayers.cpp" + $File "dmxtest_vcdtodme.cpp" + $File "dmxtestarray.cpp" + $File "dmxtestdmelog.cpp" + $File "dmxtestloader.cpp" + $File "dmxtestnotify.cpp" + $File "dmxtestserialization.cpp" + $File "dmxtestundoredo.cpp" + $File "$SRCDIR\public\interpolatortypes.cpp" + $File "$SRCDIR\public\movieobjects\movieobjects.cpp" + } + + $Folder "Header Files" + { + $File "$SRCDIR\public\movieobjects\dmx_to_vcd.h" + $File "$SRCDIR\public\interpolatortypes.h" + } + + $Folder "Link Libraries" + { + $Lib datamodel + $Lib choreoobjects + $Lib dmserializers + $Lib mathlib + $Lib dmxloader + $Lib movieobjects + $Lib tier2 + $Lib tier3 + $Lib unitlib + } +} diff --git a/unittests/dmxtest/dmxtest_dmeloglayers.cpp b/unittests/dmxtest/dmxtest_dmeloglayers.cpp new file mode 100644 index 0000000..77626b1 --- /dev/null +++ b/unittests/dmxtest/dmxtest_dmeloglayers.cpp @@ -0,0 +1,908 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "unitlib/unitlib.h" +#include "datamodel/dmelement.h" +#include "movieobjects/movieobjects.h" +#include "datamodel/idatamodel.h" + +#include "movieobjects/dmechannel.h" +#include "movieobjects/dmelog.h" + + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#define NUM_CHANNELS 1 +#define NUM_LOG_ENTRIES 10 + +enum +{ + SPEW_DIFFS = (1<<0), + SPEW_VALUES= (1<<1), + SPEW_KEYS= (1<<2), +}; + +static void ValidateDataSets( int spew, char const *testname, CUtlVector< CUtlVector< float > >& values, CUtlVector< CUtlVector< float > >& valuesbaked ) +{ + int i, j; + // Compare baked, unbaked values + Assert( values.Count() == valuesbaked.Count() ); + int c = values.Count(); + bool differs = false; + bool spewvalues = ( spew & SPEW_VALUES ) ? true : false; + + float tol = 0.0001f; + + for ( i = 0; i < c; ++i ) + { + CUtlVector< float >& v = values[ i ]; + CUtlVector< float >& vb = valuesbaked[ i ]; + + Assert( v.Count() == vb.Count() ); + + // Now get the values of the samples in the log + for ( j = 0; j < v.Count(); ++j ) + { + Assert( vb.IsValidIndex( j ) ); + if ( !vb.IsValidIndex( j ) ) + continue; + + float v1 = v[ j ]; + float v2 = vb[ j ]; + if ( fabs( v1 - v2 ) > tol ) + { + differs = true; + } + + if ( spewvalues ) + { + Msg( "%d %f %f\n", j, v[ j ], vb[ j ] ); + } + } + } + + Msg( " %s\n", differs ? "FAILED" : "OK" ); + + if ( !(spew & SPEW_DIFFS ) ) + return; + + for ( i = 0; i < c; ++i ) + { + CUtlVector< float >& v = values[ i ]; + CUtlVector< float >& vb = valuesbaked[ i ]; + + Assert( v.Count() == vb.Count() ); + + // Now get the values of the samples in the log + for ( j = 0; j < v.Count(); ++j ) + { + Assert( vb.IsValidIndex( j ) ); + if ( !vb.IsValidIndex( j ) ) + continue; + + if ( v[ j ] == vb[ j ] ) + { + if ( differs ) + { + Msg( "%d found %f to equal %f\n", j, v[ j ], vb[ j ] ); + } + continue; + } + + Msg( "%d expected %f to equal %f\n", j, v[ j ], vb[ j ] ); + } + } + + if ( differs ) + { + Msg( "End Test '%s'\n---------------\n", testname ); + } +} + +static void CreateChannels( int num, CUtlVector< CDmeChannel * >& channels, DmFileId_t fileid ) +{ + CDisableUndoScopeGuard guard; + + for ( int i = 0; i < num; ++i ) + { + CDmeChannel *channel = NULL; + + channel = CreateElement< CDmeChannel >( "channel1", fileid ); + channels.AddToTail( channel ); + channel->CreateLog( AT_FLOAT ); // only care about float logs for now + channel->SetMode( CM_PLAY );// Make sure it's in playback mode + } +} + +struct TestLayer_t +{ + enum + { + TYPE_SIMPLESLOPE = 0, // value == time + TYPE_SINE, // sinusoidal + TYPE_CONSTANT, + }; + + TestLayer_t() : + startTime( 0 ), + endTime( 0 ), + timeStep( 0 ), + usecurvetype( false ), + curvetype( CURVE_DEFAULT ), + type( TYPE_SIMPLESLOPE ), + constantvalue( 0.0f ) + { + } + + float ValueForTime( DmeTime_t time ) const + { + float t = (float)time.GetSeconds(); + switch ( type ) + { + default: + case TYPE_SIMPLESLOPE: + return t; + case TYPE_SINE: + return constantvalue * ( 1.0f + sin( ( t * 0.002f ) * 2 * M_PI ) ) * 0.5f; + case TYPE_CONSTANT: + return constantvalue; + } + + return t; + } + + int type; + DmeTime_t startTime; + DmeTime_t endTime; + DmeTime_t timeStep; + + bool usecurvetype; + int curvetype; + + float constantvalue; +}; + +struct TestParams_t +{ + TestParams_t() : + testundo( false ), + usecurves( false ), + purgevalues( true ), + testabort( false ), + spew( 0 ), + spewnontopmostlayers( false ), + defaultcurve( CURVE_DEFAULT ), + mintime( DmeTime_t( 0 ) ), + maxtime( DmeTime_t( 100 ) ) + { + } + int spew; + bool usecurves; + bool purgevalues; + bool testundo; + bool testabort; + bool spewnontopmostlayers; + int defaultcurve; + DmeTime_t mintime; + DmeTime_t maxtime; + CUtlVector< TestLayer_t > layers; + + void Reset() + { + purgevalues = true; + usecurves = false; + testundo = false; + testabort = false; + spewnontopmostlayers = false; + spew = 0; + mintime = DmeTime_t( 0 ); + maxtime = DmeTime_t( 100 ); + defaultcurve = CURVE_DEFAULT; + layers.RemoveAll(); + } + + void AddLayer( DmeTime_t start, DmeTime_t end, DmeTime_t step, int curvetype, int valuetype, float constantvalue = 0.0f ) + { + TestLayer_t tl; + tl.startTime = start; + tl.endTime = end; + tl.timeStep = step; + tl.curvetype = curvetype; + tl.type = valuetype; + tl.constantvalue = constantvalue; + + layers.AddToTail( tl ); + } +}; + +static void RunLayerTest( char const *testname, CUtlVector< CDmeChannel * >& channels, const TestParams_t& params ) +{ + if ( params.layers.Count() == 0 ) + { + Assert( 0 ); + return; + } + + Msg( "Test '%s'...\n", testname ); + + g_pDataModel->StartUndo( testname, testname ); + + int i; + int c = channels.Count(); + + { + CDisableUndoScopeGuard guard; + + for ( i = 0; i < NUM_CHANNELS; ++i ) + { + CDmeChannel *channel = channels[ i ]; + CDmeTypedLog< float > *pLog = CastElement< CDmeTypedLog< float > >( channel->GetLog() ); + Assert( pLog ); + pLog->ClearKeys(); // reset it + + CDmeCurveInfo *pCurveInfo = NULL; + if ( params.usecurves ) + { + pCurveInfo = pLog->GetOrCreateCurveInfo(); + pCurveInfo->SetDefaultCurveType( params.defaultcurve ); + pCurveInfo->SetMinValue( 0.0f ); + pCurveInfo->SetMaxValue( 1000.0f ); + } + else + { + if ( pLog->GetCurveInfo() ) + { + g_pDataModel->DestroyElement( pLog->GetCurveInfo()->GetHandle() ); + } + pLog->SetCurveInfo( NULL ); + } + + const TestLayer_t& tl = params.layers[ 0 ]; + // Now add entries + DmeTime_t logStep = tl.timeStep; + DmeTime_t logStart = tl.startTime; + + for ( DmeTime_t t = logStart; t <= tl.endTime + logStep - DmeTime_t( 1 ); t += logStep ) + { + DmeTime_t useTime = t; + if ( useTime > tl.endTime ) + { + useTime = tl.endTime; + } + float value = tl.ValueForTime( useTime ); + if ( tl.usecurvetype ) + { + pLog->SetKey( useTime, value, tl.curvetype ); + } + else + { + pLog->SetKey( useTime, value ); + } + } + } + } + + for ( int layer = 1; layer < params.layers.Count(); ++layer ) + { + const TestLayer_t& tl = params.layers[ layer ]; + + // Test creating a layer and collapsing it back down + g_pChannelRecordingMgr->StartLayerRecording( "layer operations" ); + for ( i = 0; i < c; ++i ) + { + g_pChannelRecordingMgr->AddChannelToRecordingLayer( channels[ i ] ); // calls log->CreateNewLayer() + } + + // Now add values to channel logs + for ( i = 0; i < c; ++i ) + { + CDmeChannel *channel = channels[ i ]; + CDmeTypedLog< float > *pLog = CastElement< CDmeTypedLog< float > >( channel->GetLog() ); + Assert( pLog ); + + // Now add entries + DmeTime_t logStep = tl.timeStep; + DmeTime_t logStart = tl.startTime; + + for ( DmeTime_t t = logStart; t <= tl.endTime + logStep - DmeTime_t( 1 ); t += logStep ) + { + DmeTime_t useTime = t; + if ( useTime > tl.endTime ) + { + useTime = tl.endTime; + } + float value = tl.ValueForTime( useTime ); + if ( tl.usecurvetype ) + { + pLog->SetKey( useTime, value, tl.curvetype ); + } + else + { + pLog->SetKey( useTime, value ); + } + } + } + + g_pChannelRecordingMgr->FinishLayerRecording( 0.0f, false ); // don't flatten layers here, we'll do it manually + } + + // Now sample values + CUtlVector< CUtlVector< float > > values; + CUtlVector< CUtlVector< float > > valuesbaked; + + for ( i = 0; i < c; ++i ) + { + CDmeChannel *channel = channels[ i ]; + CDmeTypedLog< float > *pLog = CastElement< CDmeTypedLog< float > >( channel->GetLog() ); + Assert( pLog ); + + int idx = values.AddToTail(); + + CUtlVector< float >& v = values[ idx ]; + + // Now get the values of the samples in the log + for ( DmeTime_t j = params.mintime; j <= params.maxtime; j += DmeTime_t( 1 ) ) + { + float fval = pLog->GetValue( j ); + v.AddToTail( fval ); + } + } + + if ( params.spewnontopmostlayers ) + { + for ( i = 0; i < c; ++i ) + { + CDmeChannel *channel = channels[ i ]; + CDmeTypedLog< float > *pLog = CastElement< CDmeTypedLog< float > >( channel->GetLog() ); + Assert( pLog ); + + // Now get the values of the samples in the log + for ( DmeTime_t j = params.mintime; j <= params.maxtime; j += DmeTime_t( 1 ) ) + { + float topValue = pLog->GetValue( j ); + float underlyingValue = pLog->GetValueSkippingTopmostLayer( j ); + + Msg( "t(%d) top [%f] rest [%f]\n", + j.GetTenthsOfMS(), topValue, underlyingValue ); + } + } + } + + // Now test creating a layer and "undo/redo" of the layer + if ( params.testundo ) + { + g_pDataModel->FinishUndo(); + g_pDataModel->Undo(); + g_pDataModel->Redo(); + g_pDataModel->StartUndo( testname, testname ); + } + + { + CUndoScopeGuard guard( "Bake Layers" ); + // Now bake down and resample values + for ( i = 0; i < c; ++i ) + { + CDmeChannel *channel = channels[ i ]; + CDmeTypedLog< float > *pLog = CastElement< CDmeTypedLog< float > >( channel->GetLog() ); + Assert( pLog ); + + pLog->FlattenLayers( 0.0f, params.spew & SPEW_DIFFS ); + + int idx = valuesbaked.AddToTail(); + + CUtlVector< float >& v = valuesbaked[ idx ]; + + // Now get the values of the samples in the log + for ( DmeTime_t j = params.mintime; j <= params.maxtime; j += DmeTime_t( 1 ) ) + { + float fval = pLog->GetValue( j ); + v.AddToTail( fval ); + } + } + } + + ValidateDataSets( params.spew, testname, values, valuesbaked ); + + // Now test creating a layer and "undo/redo" of the layer + if ( params.testundo ) + { + g_pDataModel->FinishUndo(); + g_pDataModel->Undo(); + g_pDataModel->Redo(); + g_pDataModel->StartUndo( testname, testname ); + } + + if ( params.testabort ) + { + g_pDataModel->AbortUndoableOperation(); + } + else + { + g_pDataModel->FinishUndo(); + } +} + +static void RunTimeSelectionTest( char const *testname, CUtlVector< CDmeChannel * >& channels, + const TestParams_t& params, DmeTime_t tHeadPosition, DmeLog_TimeSelection_t& ts, float value ) +{ + if ( params.layers.Count() == 0 ) + { + Assert( 0 ); + return; + } + + Msg( "Test '%s'...\n", testname ); + + int i, j; + int c = channels.Count(); + + if ( params.purgevalues ) + { + CDisableUndoScopeGuard guard; + + for ( i = 0; i < NUM_CHANNELS; ++i ) + { + CDmeChannel *channel = channels[ i ]; + CDmeTypedLog< float > *pLog = CastElement< CDmeTypedLog< float > >( channel->GetLog() ); + Assert( pLog ); + pLog->ClearKeys(); // reset it + + CDmeCurveInfo *pCurveInfo = params.usecurves ? pLog->GetOrCreateCurveInfo() : pLog->GetCurveInfo(); + if ( params.usecurves ) + { + pCurveInfo->SetDefaultCurveType( params.defaultcurve ); + pCurveInfo->SetMinValue( 0.0f ); + pCurveInfo->SetMaxValue( 1000.0f ); + } + else if ( !params.usecurves && pCurveInfo ) + { + g_pDataModel->DestroyElement( pCurveInfo->GetHandle() ); + pLog->SetCurveInfo( NULL ); + } + + const TestLayer_t& tl = params.layers[ 0 ]; + // Now add entries + DmeTime_t logStep = tl.timeStep; + DmeTime_t logStart = tl.startTime; + + for ( DmeTime_t t = logStart; t <= tl.endTime + logStep - DmeTime_t( 1 ); t += logStep ) + { + DmeTime_t useTime = t; + if ( useTime > tl.endTime ) + useTime = tl.endTime; + + float value = tl.ValueForTime( useTime ); + if ( tl.usecurvetype ) + { + pLog->SetKey( useTime, value, tl.curvetype ); + } + else + { + pLog->SetKey( useTime, value ); + } + } + } + } + + // Test creating a layer and collapsing it back down + g_pChannelRecordingMgr->StartLayerRecording( "layer operations", &ts ); + for ( i = 0; i < c; ++i ) + { + g_pChannelRecordingMgr->AddChannelToRecordingLayer( channels[ i ] ); // calls log->CreateNewLayer() + } + + // Now add values to channel logs + for ( i = 0; i < c; ++i ) + { + CDmeChannel *channel = channels[ i ]; + CDmeTypedLog< float > *pLog = CastElement< CDmeTypedLog< float > >( channel->GetLog() ); + Assert( pLog ); + + pLog->StampKeyAtHead( tHeadPosition, tHeadPosition, ts, value ); + } + + // Flattens the layers + g_pChannelRecordingMgr->FinishLayerRecording( 0.0f, true ); + + if ( params.spew & SPEW_VALUES ) + { + for ( i = 0; i < c; ++i ) + { + CDmeChannel *channel = channels[ i ]; + CDmeTypedLog< float > *pLog = CastElement< CDmeTypedLog< float > >( channel->GetLog() ); + Assert( pLog ); + Assert( pLog->GetNumLayers() == 1 ); + + for ( DmeTime_t j = params.mintime; j <= params.maxtime; j += DmeTime_t( 1 ) ) + { + float fval = pLog->GetValue( j ); + Msg( "%d %f\n", j.GetTenthsOfMS(), fval ); + } + } + } + + if ( params.spew & SPEW_KEYS ) + { + for ( i = 0; i < c; ++i ) + { + CDmeChannel *channel = channels[ i ]; + CDmeTypedLog< float > *pLog = CastElement< CDmeTypedLog< float > >( channel->GetLog() ); + Assert( pLog ); + Assert( pLog->GetNumLayers() == 1 ); + + int kc = pLog->GetKeyCount(); + for ( j = 0; j < kc; ++j ) + { + DmeTime_t time = pLog->GetKeyTime( j ); + + float fval = pLog->GetValue( time ); + Msg( "%d %f %f\n", j, time.GetSeconds(), fval ); + } + } + } + + // Now test creating a layer and "undo/redo" of the layer + if ( params.testundo ) + { + g_pDataModel->Undo(); + g_pDataModel->Redo(); + } +} + +DEFINE_TESTCASE_NOSUITE( DmxTestDmeLogLayers ) +{ + Msg( "Running CDmeTypedLog<float> layering tests...\n" ); + +#ifdef _DEBUG + int nStartingCount = g_pDataModel->GetAllocatedElementCount(); +#endif + + CUtlVector< CDmeChannel * > channels; + + DmFileId_t fileid = g_pDataModel->FindOrCreateFileId( "<DmxTestDmeLogLayers>" ); + + CreateChannels( NUM_CHANNELS, channels, fileid ); + + TestParams_t params; + { + params.testundo = false; + params.usecurves = false; + params.defaultcurve = CURVE_DEFAULT; + params.AddLayer( DmeTime_t( 0 ), DmeTime_t( 100 ), DmeTime_t( 10 ), CURVE_DEFAULT, TestLayer_t::TYPE_SIMPLESLOPE ); + params.AddLayer( DmeTime_t( 5 ), DmeTime_t( 95 ), DmeTime_t( 10 ), CURVE_DEFAULT, TestLayer_t::TYPE_SIMPLESLOPE ); + RunLayerTest( "One-Layer", channels, params ); + params.Reset(); + } + + // Single layer using lerp everywhere + // ----------------------- + { + params.testundo = false; + params.usecurves = true; + params.defaultcurve = CURVE_LINEAR_INTERP_TO_LINEAR_INTERP; + params.AddLayer( DmeTime_t( 0 ), DmeTime_t( 100 ), DmeTime_t( 10 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE ); + RunLayerTest( "One-Layer Lerp", channels, params ); + params.Reset(); + } + + // Two layers using lerp + // ---------------------------- + // ------------------------ + { + params.testundo = false; + params.usecurves = true; + params.defaultcurve = CURVE_LINEAR_INTERP_TO_LINEAR_INTERP; + params.AddLayer( DmeTime_t( 0 ), DmeTime_t( 100 ), DmeTime_t( 10 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE ); + params.AddLayer( DmeTime_t( 5 ), DmeTime_t( 95 ), DmeTime_t( 10 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE ); + RunLayerTest( "Two-Layer Lerp (contained)", channels, params ); + params.Reset(); + } + + // Two layers using CURVE_EASE_IN_TO_EASE_OUT, there should be some disparity + // ---------------------------- + // ------------------------ + { + params.testundo = false; + params.usecurves = true; + params.defaultcurve = CURVE_EASE_IN_TO_EASE_OUT; + params.AddLayer( DmeTime_t( 0 ), DmeTime_t( 100 ), DmeTime_t( 10 ), CURVE_EASE_IN_TO_EASE_OUT, TestLayer_t::TYPE_SIMPLESLOPE ); + params.AddLayer( DmeTime_t( 5 ), DmeTime_t( 95 ), DmeTime_t( 10 ), CURVE_EASE_IN_TO_EASE_OUT, TestLayer_t::TYPE_SIMPLESLOPE ); + RunLayerTest( "Two-Layer Ease In/Out (contained)", channels, params ); + params.Reset(); + } + + // Two layers using lerp + // ---------------------------- + // --------- + { + params.testundo = false; + params.usecurves = true; + params.mintime = DmeTime_t( -20 ); + params.defaultcurve = CURVE_LINEAR_INTERP_TO_LINEAR_INTERP; + params.AddLayer( DmeTime_t( 0 ), DmeTime_t( 100 ), DmeTime_t( 10 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE ); + params.AddLayer( DmeTime_t( -20 ), DmeTime_t( 20 ), DmeTime_t( 10 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE ); + RunLayerTest( "Two-Layer Lerp (overhang start)", channels, params ); + params.Reset(); + } + + // Two layers using lerp + // ---------------------------- + // ------------ + { + params.testundo = false; + params.usecurves = true; + params.maxtime = DmeTime_t( 120 ); + params.defaultcurve = CURVE_LINEAR_INTERP_TO_LINEAR_INTERP; + params.AddLayer( DmeTime_t( 0 ), DmeTime_t( 100 ), DmeTime_t( 10 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE ); + params.AddLayer( DmeTime_t( 80 ), DmeTime_t( 120 ), DmeTime_t( 10 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE ); + RunLayerTest( "Two-Layer Lerp (overhang end)", channels, params ); + params.Reset(); + } + // Three layers using lerp + // ------------- + // ----- ----- + { + params.testundo = false; + params.usecurves = true; + params.defaultcurve = CURVE_LINEAR_INTERP_TO_LINEAR_INTERP; + params.mintime = DmeTime_t( -12 ); + params.maxtime = DmeTime_t( 115 ); + params.AddLayer( DmeTime_t( 0 ), DmeTime_t( 100 ), DmeTime_t( 10 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE ); + params.AddLayer( DmeTime_t( -12 ), DmeTime_t( 12 ), DmeTime_t( 4 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE ); + params.AddLayer( DmeTime_t( 85 ), DmeTime_t( 115 ), DmeTime_t( 5 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE ); + RunLayerTest( "Three-Layer Lerp (overhang start + end)", channels, params ); + params.Reset(); + } + + // Three layers using lerp + // ------------- + // ----- + // -- + { + params.testundo = false; + params.usecurves = true; + params.defaultcurve = CURVE_LINEAR_INTERP_TO_LINEAR_INTERP; + params.AddLayer( DmeTime_t( 0 ), DmeTime_t( 100 ), DmeTime_t( 10 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE ); + params.AddLayer( DmeTime_t( 25 ), DmeTime_t( 75 ), DmeTime_t( 10 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE ); + params.AddLayer( DmeTime_t( 40 ), DmeTime_t( 60 ), DmeTime_t( 5 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE ); + RunLayerTest( "Three-Layer Lerp (layer inside layer)", channels, params ); + params.Reset(); + } + + // Three layers using lerp + // ------------- + // ----- + // -- + { + params.testundo = false; + params.usecurves = true; + params.defaultcurve = CURVE_LINEAR_INTERP_TO_LINEAR_INTERP; + params.AddLayer( DmeTime_t( 0 ), DmeTime_t( 100 ), DmeTime_t( 10 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE ); + params.AddLayer( DmeTime_t( 25 ), DmeTime_t( 75 ), DmeTime_t( 10 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE ); + params.AddLayer( DmeTime_t( 70 ), DmeTime_t( 80 ), DmeTime_t( 2 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE ); + RunLayerTest( "Three-Layer Lerp (first layer contained, second layer overlapping first at end)", channels, params ); + params.Reset(); + } + + // Three layers using lerp + // ------------- + // ----- + // -- + { + params.testundo = false; + params.usecurves = true; + params.defaultcurve = CURVE_LINEAR_INTERP_TO_LINEAR_INTERP; + params.AddLayer( DmeTime_t( 0 ), DmeTime_t( 100 ), DmeTime_t( 10 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE ); + params.AddLayer( DmeTime_t( 25 ), DmeTime_t( 75 ), DmeTime_t( 10 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE ); + params.AddLayer( DmeTime_t( 15 ), DmeTime_t( 35 ), DmeTime_t( 5 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE ); + RunLayerTest( "Three-Layer Lerp (first layer contained, second layer overlapping first at start)", channels, params ); + params.Reset(); + } + + // Four layers using lerp + // --------------- + // ----- + // ---- + // ------- + { + params.testundo = false; + params.usecurves = true; + // params.spewnontopmostlayers = true; + params.defaultcurve = CURVE_LINEAR_INTERP_TO_LINEAR_INTERP; + params.AddLayer( DmeTime_t( 0 ), DmeTime_t( 100 ), DmeTime_t( 10 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_CONSTANT, 20.0f ); + params.AddLayer( DmeTime_t( 15 ), DmeTime_t( 40 ), DmeTime_t( 10 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE ); + params.AddLayer( DmeTime_t( 60 ), DmeTime_t( 85 ), DmeTime_t( 5 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE ); + params.AddLayer( DmeTime_t( 35 ), DmeTime_t( 79 ), DmeTime_t( 5 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE ); + RunLayerTest( "Four-Layer Lerp (top overlapping end of 1st and start of 2nd layer)", channels, params ); + params.Reset(); + } + + // Single layer using lerp everywhere + // ----------------------- + { + params.testundo = false; + params.usecurves = true; + params.spew = 0; //SPEW_VALUES | SPEW_KEYS; + params.mintime = DmeTime_t( 0 ); + params.maxtime = DmeTime_t( 10000 ); + params.defaultcurve = CURVE_LINEAR_INTERP_TO_LINEAR_INTERP; + params.AddLayer( DmeTime_t( 0 ), DmeTime_t( 10000 ), DmeTime_t( 20 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SINE, 100.0f ); + + DmeTime_t tHeadPosition = DmeTime_t( 5000 ); + + DmeLog_TimeSelection_t ts; + ts.m_nTimes[ TS_LEFT_FALLOFF ] = tHeadPosition + DmeTime_t( -987 ); + ts.m_nTimes[ TS_LEFT_HOLD ] = ts.m_nTimes[ TS_RIGHT_HOLD ] = tHeadPosition; + ts.m_nTimes[ TS_RIGHT_FALLOFF ] = tHeadPosition + DmeTime_t( 1052 ); + ts.m_nFalloffInterpolatorTypes[ 0 ] = ts.m_nFalloffInterpolatorTypes[ 1 ] = INTERPOLATE_EASE_INOUT; + + // Resample at 50 msec intervals + ts.m_bResampleMode = true; + ts.m_nResampleInterval = DmeTime_t( 50 ); + + ///params.spew |= SPEW_KEYS | SPEW_VALUES; + + RunTimeSelectionTest( "One-Layer Time Selection at 50, falloff 25, EASE_INOUT interp", channels, params, tHeadPosition, ts, 250 ); + + params.purgevalues = false; + // params.spew |= SPEW_VALUES; + + // Shift the head and do it all again + tHeadPosition = DmeTime_t( 2000 ); + ts.m_nTimes[ TS_LEFT_FALLOFF ] = DmeTime_t( 1487 ); + ts.m_nTimes[ TS_LEFT_HOLD ] = ts.m_nTimes[ TS_RIGHT_HOLD ] = tHeadPosition; + ts.m_nTimes[ TS_RIGHT_FALLOFF ] = tHeadPosition + DmeTime_t( 631 ); + + RunTimeSelectionTest( "2nd layer", channels, params, tHeadPosition, ts, 500 ); + params.Reset(); + } + // Single layer using lerp everywhere + // ----------------------- + { + params.testundo = true; + params.usecurves = true; + params.spew = 0; //SPEW_VALUES | SPEW_KEYS; + params.mintime = DmeTime_t( 0 ); + params.maxtime = DmeTime_t( 1000 ); + params.defaultcurve = CURVE_LINEAR_INTERP_TO_LINEAR_INTERP; + params.AddLayer( DmeTime_t( 0 ), DmeTime_t( 1000 ), DmeTime_t( 20 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_CONSTANT, 100.0f ); + + DmeTime_t tHeadPosition = DmeTime_t( 500 ); + DmeLog_TimeSelection_t ts; + ts.m_nTimes[ TS_LEFT_FALLOFF ] = tHeadPosition + DmeTime_t( -100 ); + ts.m_nTimes[ TS_LEFT_HOLD ] = ts.m_nTimes[ TS_RIGHT_HOLD ] = tHeadPosition; + ts.m_nTimes[ TS_RIGHT_FALLOFF ] = tHeadPosition + DmeTime_t( 100 ); + ts.m_nFalloffInterpolatorTypes[ 0 ] = ts.m_nFalloffInterpolatorTypes[ 1 ] = INTERPOLATE_LINEAR_INTERP; + + // Resample at 50 msec intervals + ts.m_bResampleMode = true; + ts.m_nResampleInterval = DmeTime_t( 10 ); + +// params.spew |= SPEW_VALUES; + + RunTimeSelectionTest( "Resetting layer", channels, params, tHeadPosition, ts, 200 ); + + params.purgevalues = false; + //params.spew |= SPEW_VALUES; + + // Shift the head and do it all again + //ts.m_nRelativeFalloffTimes[ 0 ] = 1487 - 2000; + //ts.m_nRelativeHoldTimes[ 0 ] = ts.m_nRelativeHoldTimes[ 1 ] = 0; + //ts.m_nRelativeFalloffTimes[ 1 ] = 631; + //ts.SetHeadPosition( 2000 ); + + RunTimeSelectionTest( "2nd layer", channels, params, tHeadPosition, ts, 110 ); + params.Reset(); + } +// g_pDataModel->TraceUndo( true ); + + // Test abort undo stuff + for ( int i = 0; i < 2; ++i ) + // Four layers using lerp + // --------------- + // ----- + // ---- + // ------- + { + params.testundo = false; + params.testabort = i != 1 ? true : false; + params.usecurves = false; + // params.spewnontopmostlayers = true; + params.defaultcurve = CURVE_LINEAR_INTERP_TO_LINEAR_INTERP; + params.AddLayer( DmeTime_t( 0 ), DmeTime_t( 10 ), DmeTime_t( 10 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_CONSTANT, 20.0f ); + params.AddLayer( DmeTime_t( 5 ), DmeTime_t( 6 ), DmeTime_t( 1 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE ); + RunLayerTest( "Four-Layer Lerp (top overlapping end of 1st and start of 2nd layer)", channels, params ); + params.Reset(); + } + + // g_pDataModel->TraceUndo( false ); + + + //DestroyChannels( channels ); + + g_pDataModel->ClearUndo(); + + g_pDataModel->RemoveFileId( fileid ); + +#ifdef _DEBUG + int nEndingCount = g_pDataModel->GetAllocatedElementCount(); + AssertEquals( nEndingCount, nStartingCount ); + if ( nEndingCount != nStartingCount ) + { + for ( DmElementHandle_t hElement = g_pDataModel->FirstAllocatedElement() ; + hElement != DMELEMENT_HANDLE_INVALID; + hElement = g_pDataModel->NextAllocatedElement( hElement ) ) + { + CDmElement *pElement = g_pDataModel->GetElement( hElement ); + Assert( pElement ); + if ( !pElement ) + return; + + Msg( "[%s : %s] in memory\n", pElement->GetName(), pElement->GetTypeString() ); + } + } +#endif +} + +DEFINE_TESTCASE_NOSUITE( DmxTestDmeLogLayersUndo ) +{ + Msg( "Running CDmeTypedLog<float> layering UNDO tests...\n" ); + +#ifdef _DEBUG + int nStartingCount = g_pDataModel->GetAllocatedElementCount(); +#endif + + CUtlVector< CDmeChannel * > channels; + + DmFileId_t fileid = g_pDataModel->FindOrCreateFileId( "<DmxTestDmeLogLayersUndo>" ); + + CreateChannels( NUM_CHANNELS, channels, fileid ); + + TestParams_t params; + +// g_pDataModel->TraceUndo( true ); + + // Test abort undo stuff + for ( int i = 0; i < 2; ++i ) + { + params.testundo = false; + params.testabort = true; + params.usecurves = false; + // params.spewnontopmostlayers = true; + params.defaultcurve = CURVE_LINEAR_INTERP_TO_LINEAR_INTERP; + params.AddLayer( DmeTime_t( 0 ), DmeTime_t( 1000 ), DmeTime_t( 10 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_CONSTANT, 20.0f ); + params.AddLayer( DmeTime_t( 100 ), DmeTime_t( 900 ), DmeTime_t( 5 ), CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, TestLayer_t::TYPE_SIMPLESLOPE ); + RunLayerTest( "Abort undo", channels, params ); + params.Reset(); + } + +// g_pDataModel->TraceUndo( false ); + + g_pDataModel->ClearUndo(); + g_pDataModel->RemoveFileId( fileid ); + +#ifdef _DEBUG + int nEndingCount = g_pDataModel->GetAllocatedElementCount(); + AssertEquals( nEndingCount, nStartingCount ); + if ( nEndingCount != nStartingCount ) + { + for ( DmElementHandle_t hElement = g_pDataModel->FirstAllocatedElement() ; + hElement != DMELEMENT_HANDLE_INVALID; + hElement = g_pDataModel->NextAllocatedElement( hElement ) ) + { + CDmElement *pElement = g_pDataModel->GetElement( hElement ); + Assert( pElement ); + if ( !pElement ) + return; + + Msg( "[%s : %s] in memory\n", pElement->GetName(), pElement->GetTypeString() ); + } + } +#endif +} diff --git a/unittests/dmxtest/dmxtest_vcdtodme.cpp b/unittests/dmxtest/dmxtest_vcdtodme.cpp new file mode 100644 index 0000000..900b310 --- /dev/null +++ b/unittests/dmxtest/dmxtest_vcdtodme.cpp @@ -0,0 +1,110 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "unitlib/unitlib.h" +#include "datamodel/dmelement.h" +#include "movieobjects/movieobjects.h" +#include "datamodel/idatamodel.h" +#include "tier1/utlbuffer.h" +#include "filesystem.h" +#include "movieobjects/dmelog.h" +#include "choreoscene.h" +#include "choreoevent.h" +#include "iscenetokenprocessor.h" +#include "tier1/tokenreader.h" +#include "characterset.h" +#include "movieobjects/dmx_to_vcd.h" +#include "tier3/scenetokenprocessor.h" +#include "tier2/tier2.h" + +char const *vcdtestfile = "dmxtest.vcd"; + +void RunSceneToDmxTests( CChoreoScene *scene ) +{ + DmFileId_t fileid = g_pDataModel->FindOrCreateFileId( scene->GetFilename() ); + CDmeFilmClip *dmx = CreateElement< CDmeFilmClip >( scene->GetFilename(), fileid ); + Assert( dmx ); + + bool success = ConvertSceneToDmx( scene, dmx ); + Assert( success ); + + CChoreoScene *scene2 = new CChoreoScene( NULL ); + scene2->SetFileName( scene->GetFilename() ); + + success = ConvertDmxToScene( dmx, scene2 ); + Assert( success ); + + char sz[ 512 ]; + Q_StripExtension( scene->GetFilename(), sz, sizeof( sz ) ); + Q_strncat( sz, "_2.vcd", sizeof( sz ), COPY_ALL_CHARACTERS ); + scene2->SaveToFile( sz ); + + delete scene2; + + g_pDataModel->RemoveFileId( fileid ); +} + +DEFINE_TESTCASE_NOSUITE( DmxTestVcdToDme ) +{ + Msg( "Running .vcd (faceposer) to dmx tests\n" ); + +#ifdef _DEBUG + int nStartingCount = g_pDataModel->GetAllocatedElementCount(); +#endif + + CDisableUndoScopeGuard guard; + + g_pDmElementFramework->BeginEdit(); + + const char *pFileName = vcdtestfile; + char pFullPathName[ MAX_PATH ]; + char pDir[ MAX_PATH ]; + if ( g_pFullFileSystem->GetCurrentDirectory( pDir, sizeof( pDir ) ) ) + { + V_ComposeFileName( pDir, vcdtestfile, pFullPathName, sizeof( pFullPathName ) ); + V_RemoveDotSlashes( pFullPathName ); + pFileName = pFullPathName; + } + + CUtlBuffer buf; + if ( g_pFullFileSystem->ReadFile( pFileName, NULL, buf ) ) + { + SetTokenProcessorBuffer( (char *)buf.Base() ); + CChoreoScene *scene = ChoreoLoadScene( pFileName, NULL, GetTokenProcessor(), NULL ); + if ( scene ) + { + RunSceneToDmxTests( scene ); + delete scene; + } + } + else + { + Msg( "Unable to load test file '%s'\n", pFileName ); + } + + g_pDataModel->ClearUndo(); + +#ifdef _DEBUG + int nEndingCount = g_pDataModel->GetAllocatedElementCount(); + AssertEquals( nEndingCount, nStartingCount ); + if ( nEndingCount != nStartingCount ) + { + for ( DmElementHandle_t hElement = g_pDataModel->FirstAllocatedElement() ; + hElement != DMELEMENT_HANDLE_INVALID; + hElement = g_pDataModel->NextAllocatedElement( hElement ) ) + { + CDmElement *pElement = g_pDataModel->GetElement( hElement ); + Assert( pElement ); + if ( !pElement ) + return; + + Msg( "[%s : %s] in memory\n", pElement->GetName(), pElement->GetTypeString() ); + } + } +#endif +}
\ No newline at end of file diff --git a/unittests/dmxtest/dmxtestarray.cpp b/unittests/dmxtest/dmxtestarray.cpp new file mode 100644 index 0000000..e9c7468 --- /dev/null +++ b/unittests/dmxtest/dmxtestarray.cpp @@ -0,0 +1,76 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Unit test program for DMX testing (testing the Array operations) +// +// $NoKeywords: $ +//=============================================================================// + +#include "unitlib/unitlib.h" +#include "datamodel/dmelement.h" +#include "datamodel/idatamodel.h" +#include "tier1/utlbuffer.h" +#include "filesystem.h" +#include "datamodel/dmehandle.h" +#include "tier2/tier2.h" +#include "movieobjects/dmeshape.h" + + +DEFINE_TESTCASE_NOSUITE( DmxArrayTest ) +{ + Msg( "Running dmx array tests...\n" ); + + CDisableUndoScopeGuard sg; + DmFileId_t fileid = g_pDataModel->FindOrCreateFileId( "<RunArrayTests>" ); + + CDmElement *pElement = CreateElement< CDmElement >( "root", fileid ); + + CDmElement *pElement2 = CreateElement<CDmElement>( "element1", fileid ); + Assert( pElement2 ); + CDmElement *pElement3 = CreateElement<CDmElement>( "element2", fileid ); + Assert( pElement3 ); + CDmeShape *pElement4 = CreateElement<CDmeShape>( "shape", fileid ); + Assert( pElement4 ); + + CDmrStringArray stringVec( pElement, "string_array_test", true ); + stringVec.AddToTail( "string1" ); + stringVec.AddToTail( "string2" ); + stringVec.AddToTail( "string3" ); + + CDmrArray< float > floatVec( pElement, "float_array_test", true ); + floatVec.AddToTail( -1.0f ); + floatVec.AddToTail( 0.0f ); + floatVec.AddToTail( 1.0f ); + + CDmrElementArray< > elementVec( pElement, "element_array_test", true ); + elementVec.AddToTail( pElement2 ); + elementVec.AddToTail( pElement3 ); + elementVec.AddToTail( pElement4 ); + + CDmrStringArray stringVec2( pElement, "string_array_test2", true ); + stringVec2 = stringVec; + Shipping_Assert( stringVec2.Count() == 3 ); + + CDmrArray< float > floatVec2( pElement, "float_array_test2", true ); + floatVec2 = floatVec; + Shipping_Assert( floatVec2.Count() == 3 ); + + CDmrElementArray< > elementVec2( pElement, "element_array_test2", true ); + elementVec2 = elementVec; + Shipping_Assert( elementVec2.Count() == 3 ); + + CDmrElementArray< CDmeShape > elementVec3( pElement, "element_array_test3", true ); + elementVec3 = elementVec2; + Shipping_Assert( elementVec3.Count() == 1 ); + + CUtlVector<DmElementHandle_t> val; + val.AddToTail( pElement2->GetHandle() ); + val.AddToTail( pElement4->GetHandle() ); + + elementVec2 = val; + Shipping_Assert( elementVec2.Count() == 2 ); + + elementVec3 = val; + Shipping_Assert( elementVec3.Count() == 1 ); + + g_pDataModel->RemoveFileId( fileid ); +} diff --git a/unittests/dmxtest/dmxtestdmelog.cpp b/unittests/dmxtest/dmxtestdmelog.cpp new file mode 100644 index 0000000..3f98678 --- /dev/null +++ b/unittests/dmxtest/dmxtestdmelog.cpp @@ -0,0 +1,393 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "unitlib/unitlib.h" +#include "datamodel/dmelement.h" +#include "movieobjects/movieobjects.h" +#include "datamodel/idatamodel.h" +#include "tier1/utlbuffer.h" +#include "filesystem.h" +#include "movieobjects/dmelog.h" +#include "choreoscene.h" +#include "choreoevent.h" + +struct data_t +{ + int tms; // tenths of a millisecond + float value; + int curvetype; +}; + +struct tvpair_t +{ + int tms; + float expectedvalue; +}; + +data_t data[] = +{ + { 0, 0.0f, MAKE_CURVE_TYPE( INTERPOLATE_CATMULL_ROM_NORMALIZEX, INTERPOLATE_CATMULL_ROM_NORMALIZEX ) }, + { 10000, 0.5f, MAKE_CURVE_TYPE( INTERPOLATE_CATMULL_ROM_NORMALIZEX, INTERPOLATE_CATMULL_ROM_NORMALIZEX ) }, + { 20000, 0.5f, MAKE_CURVE_TYPE( INTERPOLATE_EASE_IN, INTERPOLATE_EASE_OUT ) }, + { 30000, 0.5f, MAKE_CURVE_TYPE( INTERPOLATE_EASE_OUT, INTERPOLATE_EASE_INOUT ) }, + { 40000, 0.5f, MAKE_CURVE_TYPE( INTERPOLATE_EASE_INOUT, INTERPOLATE_BSPLINE ) }, + { 50000, 0.5f, MAKE_CURVE_TYPE( INTERPOLATE_BSPLINE, INTERPOLATE_LINEAR_INTERP ) }, + { 60000, 1.0f, MAKE_CURVE_TYPE( INTERPOLATE_LINEAR_INTERP, INTERPOLATE_KOCHANEK_BARTELS ) }, + { 70000, 0.0f, MAKE_CURVE_TYPE( INTERPOLATE_KOCHANEK_BARTELS, INTERPOLATE_KOCHANEK_BARTELS_EARLY ) }, + { 80000, 0.5f, MAKE_CURVE_TYPE( INTERPOLATE_KOCHANEK_BARTELS_EARLY, INTERPOLATE_KOCHANEK_BARTELS_LATE ) }, + { 90000, 0.0f, MAKE_CURVE_TYPE( INTERPOLATE_KOCHANEK_BARTELS_LATE, INTERPOLATE_SIMPLE_CUBIC ) }, + { 100000, 0.25f, MAKE_CURVE_TYPE( INTERPOLATE_SIMPLE_CUBIC, INTERPOLATE_CATMULL_ROM ) }, + { 110000, 0.0f, MAKE_CURVE_TYPE( INTERPOLATE_CATMULL_ROM, INTERPOLATE_CATMULL_ROM_NORMALIZE ) }, + { 120000, 0.125f, MAKE_CURVE_TYPE( INTERPOLATE_CATMULL_ROM_NORMALIZE, INTERPOLATE_EXPONENTIAL_DECAY ) }, + { 130000, 0.0f, MAKE_CURVE_TYPE( INTERPOLATE_EXPONENTIAL_DECAY, INTERPOLATE_HOLD ) }, + { 140000, 0.0625f, MAKE_CURVE_TYPE( INTERPOLATE_CATMULL_ROM_NORMALIZEX, INTERPOLATE_EXPONENTIAL_DECAY ) }, + { 150000, 0.0f, MAKE_CURVE_TYPE( INTERPOLATE_CATMULL_ROM_NORMALIZEX, INTERPOLATE_CATMULL_ROM_NORMALIZEX ) }, +}; + +#define NUM_DEF_TESTS 3 + +static data_t values1[] = +{ + { -1, 0.0f, 0 }, +}; +static data_t values2[] = +{ + { 5000, 0.5f, CURVE_DEFAULT }, + { -1, 0.0f, 0 }, +}; +static data_t values3[] = +{ + { 2500, 0.25f, CURVE_DEFAULT }, + { 7500, 0.75f, CURVE_DEFAULT }, + { -1, 0.0f, 0 }, +}; + +static data_t *defaultvaluetest[ NUM_DEF_TESTS ] = +{ + values1, + values2, + values3 +}; + +#define NUM_TEST_VALUES 3 + +static tvpair_t expectedvalues1[NUM_TEST_VALUES] = +{ + { 0, 0.5f }, + { 5000, 0.5f }, + { 10000, 0.5f }, +}; + +static tvpair_t expectedvalues2[NUM_TEST_VALUES] = +{ + { 0, 0.5f }, + { 5000, 0.5f }, + { 10000, 0.5f }, +}; + +static tvpair_t expectedvalues3[NUM_TEST_VALUES] = +{ + { 0, 0.25f }, + { 5000, 0.5f }, + { 10000, 0.75f }, +}; + +static tvpair_t *expectedvalues[ NUM_DEF_TESTS ] = +{ + expectedvalues1, + expectedvalues2, + expectedvalues3 +}; + +void ResetLog( CDmeFloatLog *log, bool useCurveTypes, int startIndex = 0, int endIndex = -1 ) +{ + log->ClearKeys(); + + CDmeCurveInfo *pCurveInfo = useCurveTypes ? log->GetOrCreateCurveInfo() : log->GetCurveInfo(); + if ( useCurveTypes ) + { + pCurveInfo->SetDefaultCurveType( MAKE_CURVE_TYPE( INTERPOLATE_CATMULL_ROM_NORMALIZEX, INTERPOLATE_CATMULL_ROM_NORMALIZEX ) ); + } + else if ( !useCurveTypes && pCurveInfo ) + { + g_pDataModel->DestroyElement( pCurveInfo->GetHandle() ); + log->SetCurveInfo( NULL ); + } + + int i; + int c; + + c = ARRAYSIZE( data ); + for ( i = startIndex; i < c; ++i ) + { + log->SetKey( DmeTime_t( data[ i ].tms ), data[ i ].value, useCurveTypes ? data[ i ].curvetype : CURVE_DEFAULT ); + + if ( endIndex != -1 && i >= endIndex ) + break; + } +} + +void CompareFloats( float f1, float f2, float tol, char const *fmt, ... ) +{ + float diff = fabs( f1 - f2 ); + if ( diff < tol ) + return; + + char buf[ 256 ]; + va_list argptr; + va_start( argptr, fmt ); + _vsnprintf( buf, sizeof( buf ) - 1, fmt, argptr ); + va_end( argptr ); + + Msg( buf ); +} + +DEFINE_TESTCASE_NOSUITE( DmxRunDefaultValueLogTest ) +{ + Msg( "Running CDmeTypedLog<float> default value (stereo channel w/ value 0.5) tests...\n" ); + CDisableUndoScopeGuard sg; + + DmFileId_t fileid = g_pDataModel->FindOrCreateFileId( "<DmxTestDmeLog>" ); + + for ( int i = 0; i < NUM_DEF_TESTS; ++i ) + { + data_t *pdata = defaultvaluetest[ i ]; + tvpair_t *pexpected = expectedvalues[ i ]; + + // Run each test + + CDmeFloatLog *log = CreateElement<CDmeFloatLog>( "curve", fileid ); + if ( !log ) + { + Msg( "Unable to create CDmeFloatLog object!!!" ); + continue; + } + + log->SetDefaultValue( 0.5f ); + + if ( pdata ) + { + // Run the test + for ( int j = 0; ; ++j ) + { + if ( pdata[ j ].tms == -1 ) + break; + + log->SetKey( DmeTime_t( pdata[ j ].tms ), pdata[ j ].value ); + } + } + + // Now compare against expected values + for ( int j = 0; j < NUM_TEST_VALUES; ++j ) + { + DmeTime_t t = DmeTime_t( pexpected[ j ].tms ); + float v = pexpected[ j ].expectedvalue; + float logv = log->GetValue( t ); + Shipping_Assert( v == logv ); + } + + DestroyElement( log ); + } + + g_pDataModel->RemoveFileId( fileid ); +} + +void RunDmeFloatLogTests( CDmeFloatLog *log ) +{ + Msg( " Testing general log data...\n" ); + + ResetLog( log, false ); + + CompareFloats( 0.5f, log->GetValue( DmeTime_t( 2.0f ) ), 0.000001f, "log->GetValue( 2.0 ) expected to be 0.5f\n" ); + CompareFloats( 0.5f, log->GetValue( DmeTime_t( 2.5f ) ), 0.000001f, "log->GetValue( 2.5 ) expected to be 0.5f\n" ); + CompareFloats( 0.5f, log->GetValue( DmeTime_t( 2.5f ) ), 0.000001f, "log->GetValue( 2.5 ) expected to be 0.5f\n" ); + CompareFloats( 0.5f, log->GetValue( DmeTime_t( 6.5f ) ), 0.000001f, "log->GetValue( 6.5 ) expected to be 0.5f\n" ); + + CDmeCurveInfo *pCurveInfo = log->GetOrCreateCurveInfo(); + + int idx = log->FindKeyWithinTolerance( DmeTime_t( 6.0f ), DmeTime_t( 0 ) ); + Shipping_Assert( log->GetKeyTime( idx ) == DmeTime_t( 6.0f ) ); + log->SetKeyCurveType( idx, MAKE_CURVE_TYPE( INTERPOLATE_LINEAR_INTERP, INTERPOLATE_LINEAR_INTERP ) ); + log->SetKeyCurveType( idx + 1, MAKE_CURVE_TYPE( INTERPOLATE_LINEAR_INTERP, INTERPOLATE_LINEAR_INTERP ) ); + + float val = log->GetValue( DmeTime_t( 6.5f ) ); + float qval = log->GetValue( DmeTime_t( 6.25f ) ); + + CompareFloats( 0.5f, val, 0.000001f, "INTERPOLATE_LINEAR_INTERPlog->GetValue( 6500 ) expcted to be 0.5f\n" ); + CompareFloats( 0.75f, qval, 0.000001f, "INTERPOLATE_LINEAR_INTERPlog->GetValue( 6250 ) expcted to be 0.75f\n" ); + + log->SetKeyCurveType( idx, MAKE_CURVE_TYPE( INTERPOLATE_CATMULL_ROM_NORMALIZEX, INTERPOLATE_CATMULL_ROM_NORMALIZEX ) ); + log->SetKeyCurveType( idx + 1, MAKE_CURVE_TYPE( INTERPOLATE_CATMULL_ROM_NORMALIZEX, INTERPOLATE_CATMULL_ROM_NORMALIZEX ) ); + + float val2 = log->GetValue( DmeTime_t( 6.5f ) ); + float qval2 = log->GetValue( DmeTime_t( 6.25f ) ); + Shipping_Assert( val2 == val ); + Shipping_Assert( qval2 != val ); + + log->SetKeyCurveType( idx, MAKE_CURVE_TYPE( INTERPOLATE_EASE_INOUT, INTERPOLATE_EASE_INOUT ) ); + log->SetKeyCurveType( idx + 1, MAKE_CURVE_TYPE( INTERPOLATE_EASE_INOUT, INTERPOLATE_EASE_INOUT ) ); + + float val3 = log->GetValue( DmeTime_t( 6.5f ) ); + float qval3 = log->GetValue( DmeTime_t( 6.25f ) ); + Shipping_Assert( val3 == val ); + Shipping_Assert( qval3 != val ); + + log->SetKeyCurveType( idx, MAKE_CURVE_TYPE( INTERPOLATE_EXPONENTIAL_DECAY, INTERPOLATE_EXPONENTIAL_DECAY ) ); + log->SetKeyCurveType( idx + 1, MAKE_CURVE_TYPE( INTERPOLATE_EXPONENTIAL_DECAY, INTERPOLATE_EXPONENTIAL_DECAY ) ); + + float val4 = log->GetValue( DmeTime_t( 6.5f ) ); + float qval4 = log->GetValue( DmeTime_t( 6.25f ) ); + Shipping_Assert( val4 != val ); + Shipping_Assert( qval4 != val ); + + log->SetKeyCurveType( idx, MAKE_CURVE_TYPE( INTERPOLATE_KOCHANEK_BARTELS, INTERPOLATE_KOCHANEK_BARTELS ) ); + log->SetKeyCurveType( idx + 1, MAKE_CURVE_TYPE( INTERPOLATE_KOCHANEK_BARTELS, INTERPOLATE_KOCHANEK_BARTELS ) ); + + float val5 = log->GetValue( DmeTime_t( 6.5f ) ); + float qval5 = log->GetValue( DmeTime_t( 6.25f ) ); + Shipping_Assert( val5 == val ); + Shipping_Assert( qval5 != val ); + + pCurveInfo->SetDefaultCurveType( MAKE_CURVE_TYPE( INTERPOLATE_KOCHANEK_BARTELS, INTERPOLATE_KOCHANEK_BARTELS ) ); + log->SetKeyCurveType( idx, MAKE_CURVE_TYPE( INTERPOLATE_DEFAULT, INTERPOLATE_DEFAULT ) ); + log->SetKeyCurveType( idx + 1, MAKE_CURVE_TYPE( INTERPOLATE_DEFAULT, INTERPOLATE_DEFAULT ) ); + + float val6 = log->GetValue( DmeTime_t( 6.5f ) ); + float qval6 = log->GetValue( DmeTime_t( 6.25f ) ); + Shipping_Assert( val5 == val6 ); + Shipping_Assert( qval6 == qval5 ); + +} + +void CompareLogToChoreo( CFlexAnimationTrack *track, CDmeFloatLog *log ) +{ + // Now run tests + for ( DmeTime_t t( 0 ); t < DmeTime_t( 20.0f ); t += DmeTime_t( 0.1f ) ) + { + // Compare values + float dmevalue = log->GetValue( t ); + float choreovalue = track->GetIntensity( t.GetSeconds() ); + + CompareFloats( dmevalue, choreovalue, 0.001f, "Time(%f sec) , dme [%f] choreo[%f], diff[%f]\n", + t.GetSeconds(), + dmevalue, + choreovalue, + fabs( dmevalue - choreovalue ) ); + } +} + +void ResetChoreo( CFlexAnimationTrack *track, bool useCurveTypes, int startIndex = 0, int endIndex = -1 ) +{ + track->Clear(); + + int i; + int c; + + c = ARRAYSIZE( data ); + for ( i = startIndex; i < c; ++i ) + { + data_t *e = &data[ i ]; + + float t = (float)e->tms / 10000.0f; + + CExpressionSample *sample = track->AddSample( t, e->value ); + Shipping_Assert( sample ); + if ( useCurveTypes ) + { + sample->SetCurveType( e->curvetype ); + } + + if ( endIndex != -1 && i >= endIndex ) + break; + } +} + +void RunDmeChoreoComparisons( CDmeFloatLog *log ) +{ + Msg( " Testing choreo-style log data...\n" ); + + ResetLog( log, true ); + log->SetRightEdgeTime( DmeTime_t( 15.0f ) ); + + CChoreoScene *scene = new CChoreoScene( NULL ); + CChoreoEvent *event = new CChoreoEvent( scene, CChoreoEvent::FLEXANIMATION, "test" ); + event->SetStartTime( 0.0f ); + event->SetEndTime( 15.0f ); + CFlexAnimationTrack *track = new CFlexAnimationTrack( event ); + track->SetFlexControllerName( "flextest" ); + track->SetComboType( false ); + + ResetChoreo( track, true ); + + Msg( " Comparing default data...\n" ); + + CompareLogToChoreo( track, log ); + + ResetLog( log, true, 3, 14 ); + ResetChoreo( track, true, 3, 14 ); + + Msg( " Comparing subset of data...\n" ); + + CompareLogToChoreo( track, log ); + + Msg( " Comparing left/right edge settings...\n" ); + // Now test right and left edge stuff + // Enable left edge stuff + track->SetEdgeActive( true, true ); + track->SetEdgeInfo( true, MAKE_CURVE_TYPE( INTERPOLATE_LINEAR_INTERP, INTERPOLATE_LINEAR_INTERP ), 0.75f ); + track->SetEdgeActive( false, true ); + track->SetEdgeInfo( false, MAKE_CURVE_TYPE( INTERPOLATE_EASE_OUT, INTERPOLATE_EASE_OUT ), 0.25f ); + + // Same settings for log + log->SetUseEdgeInfo( true ); + log->SetDefaultEdgeZeroValue( 0.0f ); + log->SetEdgeInfo( 0, true, 0.75f, MAKE_CURVE_TYPE( INTERPOLATE_LINEAR_INTERP, INTERPOLATE_LINEAR_INTERP ) ); + log->SetEdgeInfo( 1, true, 0.25f, MAKE_CURVE_TYPE( INTERPOLATE_EASE_OUT, INTERPOLATE_EASE_OUT ) ); + + CompareLogToChoreo( track, log ); + + int i; + for ( i = 1; i < NUM_INTERPOLATE_TYPES; ++i ) + { + Msg( " Comparing left/right edge settings[ %s ]...\n", Interpolator_NameForInterpolator( i, true ) ); + + float val = (float)i / (float)( NUM_INTERPOLATE_TYPES - 1 ) ; + // Now test right and left edge stuff with different data + track->SetEdgeInfo( true, MAKE_CURVE_TYPE( i, i ), val ); + track->SetEdgeInfo( false, MAKE_CURVE_TYPE( i, i ), val ); + log->SetEdgeInfo( 0, true, val, MAKE_CURVE_TYPE( i, i ) ); + log->SetEdgeInfo( 1, true, val, MAKE_CURVE_TYPE( i, i ) ); + + CompareLogToChoreo( track, log ); + } + + delete event; + delete scene; +} + +DEFINE_TESTCASE_NOSUITE( DmxTestDmeLog ) +{ + Msg( "Running CDmeTypedLog<float> tests...\n" ); + CDisableUndoScopeGuard sg; + + DmFileId_t fileid = g_pDataModel->FindOrCreateFileId( "<DmxTestDmeLog>" ); + + CDmeFloatLog *pElement = CreateElement<CDmeFloatLog>( "curve", fileid ); + if ( !pElement ) + { + Msg( "Unable to create CDmeFloatLog object!!!" ); + return; + } + + // Run tests + RunDmeFloatLogTests( pElement ); + + RunDmeChoreoComparisons( pElement ); + + g_pDataModel->RemoveFileId( fileid ); +}
\ No newline at end of file diff --git a/unittests/dmxtest/dmxtestloader.cpp b/unittests/dmxtest/dmxtestloader.cpp new file mode 100644 index 0000000..ab3adbf --- /dev/null +++ b/unittests/dmxtest/dmxtestloader.cpp @@ -0,0 +1,211 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Unit test program for DMX testing +// +// $NoKeywords: $ +//=============================================================================// + +#include "unitlib/unitlib.h" +#include "dmxloader/dmxloader.h" +#include "dmxloader/dmxelement.h" + +struct TestStruct_t +{ + DmObjectId_t m_nId; + bool m_bBool; + int m_nInt; + float m_flFloat; + Color m_Color; + Vector2D m_Vector2D; + Vector m_Vector3D; + Vector4D m_Vector4D; + QAngle m_Angles; + Quaternion m_Quaternion; + VMatrix m_Matrix; + char m_pStringBuf[256]; +}; + +BEGIN_DMXELEMENT_UNPACK( TestStruct_t ) + DMXELEMENT_UNPACK_FIELD( "id_test", NULL, DmObjectId_t, m_nId ) + DMXELEMENT_UNPACK_FIELD( "bool_test", "1", bool, m_bBool ) + DMXELEMENT_UNPACK_FIELD( "int_test", "5", int, m_nInt ) + DMXELEMENT_UNPACK_FIELD( "float_test", "4.0", float, m_flFloat ) + DMXELEMENT_UNPACK_FIELD( "color_test", "200 200 200 200", Color, m_Color ) + DMXELEMENT_UNPACK_FIELD( "vector2d_test", "5.0 1.0", Vector2D, m_Vector2D ) + DMXELEMENT_UNPACK_FIELD( "vector3d_test", "5.0 1.0 -3.0", Vector, m_Vector3D ) + DMXELEMENT_UNPACK_FIELD( "vector4d_test", "5.0 1.0 -4.0 2.0", Vector4D, m_Vector4D ) + DMXELEMENT_UNPACK_FIELD( "qangle_test", "5.0 1.0 -3.0", QAngle, m_Angles ) + DMXELEMENT_UNPACK_FIELD( "quat_test", "5.0 1.0 -4.0 2.0", Quaternion, m_Quaternion ) + DMXELEMENT_UNPACK_FIELD( "vmatrix_test", NULL, VMatrix, m_Matrix ) + DMXELEMENT_UNPACK_FIELD_STRING( "string_test", "default", m_pStringBuf ) +END_DMXELEMENT_UNPACK( TestStruct_t, s_TestStructUnpack ) + +void TestReadFile( CDmxElement *pRoot ) +{ + VMatrix mattest, mat2test; + MatrixBuildRotateZ( mattest, 45 ); + MatrixBuildRotateZ( mat2test, 30 ); + + int i; + unsigned char buftest[256]; + unsigned char buf2test[256]; + for ( i = 0; i < 256; ++i ) + { + buftest[i] = i; + buf2test[i] = 255 - i; + } + + + // Make sure everything was read in ok. + AssertEquals( pRoot->GetValue<bool>( "bool_test" ), true ); + AssertEquals( pRoot->GetValue<int>( "int_test" ), 2 ); + AssertFloatEquals( pRoot->GetValue<float>( "float_test" ), 3.0f, 1e-3 ); + const Color& color = pRoot->GetValue<Color>( "color_test" ); + Shipping_Assert( color.r() == 0 && color.g() == 64 && color.b() == 128 && color.a() == 255 ); + const Vector2D& vec2D = pRoot->GetValue<Vector2D>( "vector2d_test" ); + Shipping_Assert( vec2D.x == 1.0f && vec2D.y == -1.0f ); + const Vector& vec3D = pRoot->GetValue<Vector>( "vector3d_test" ); + Shipping_Assert( vec3D.x == 1.0f && vec3D.y == -1.0f && vec3D.z == 0.0f ); + const Vector4D& vec4D = pRoot->GetValue<Vector4D>( "vector4d_test" ); + Shipping_Assert( vec4D.x == 1.0f && vec4D.y == -1.0f && vec4D.z == 0.0f && vec4D.w == 2.0f ); + const QAngle& ang = pRoot->GetValue<QAngle>( "qangle_test" ); + Shipping_Assert( ang.x == 0.0f && ang.y == 90.0f && ang.z == -90.0f ); + const Quaternion& quat = pRoot->GetValue<Quaternion>( "quat_test" ); + Shipping_Assert( quat.x == 1.0f && quat.y == -1.0f && quat.z == 0.0f && quat.w == 2.0f ); + + const VMatrix& mat = pRoot->GetValue<VMatrix>( "vmatrix_test" ); + Shipping_Assert( MatricesAreEqual( mat, mattest, 1e-3 ) ); + + Shipping_Assert( !Q_stricmp( pRoot->GetValueString( "string_test" ), "test" ) ); + const CUtlBinaryBlock& blob = pRoot->GetValue<CUtlBinaryBlock>( "binary_test" ); + Shipping_Assert( blob.Length() == 256 ); + Shipping_Assert( !memcmp( blob.Get(), buftest, 256 ) ); + + CDmxElement *pElement7 = pRoot->GetValue<CDmxElement*>( "element_test" ); + Shipping_Assert( pElement7 != NULL ); + CDmxElement *pElement6 = pRoot->GetValue<CDmxElement*>( "shared_element_test" ); + Shipping_Assert( pElement6 != NULL ); + const CUtlVector< CDmxElement* >& elementList = pRoot->GetArray<CDmxElement*>( "children" ); + Shipping_Assert( elementList.Count() == 2 ); + CDmxElement *pElement2 = elementList[0]; + CDmxElement *pElement3 = elementList[1]; + Shipping_Assert( pElement2 != NULL && pElement3 != NULL ); + Shipping_Assert( pElement7->GetValue<CDmxElement*>( "shared_element_test" ) == pElement6 ); + const CUtlVector< CDmxElement* >& elementList3 = pElement6->GetArray<CDmxElement*>( "element_array_test" ); + CDmxElement *pElement4 = elementList3[0]; + CDmxElement *pElement5 = elementList3[1]; + + const CUtlVector< bool > &boolVec = pElement2->GetArray<bool>( "bool_array_test" ); + Shipping_Assert( boolVec.Count() == 2 && boolVec[0] == false && boolVec[1] == true ); + + const CUtlVector< int > &intVec = pElement2->GetArray<int>( "int_array_test" ); + Shipping_Assert( intVec.Count() == 3 && intVec[0] == 0 && intVec[1] == 1 && intVec[2] == 2 ); + + const CUtlVector< float > &floatVec = pElement2->GetArray<float>( "float_array_test" ); + Shipping_Assert( floatVec.Count() == 3 && floatVec[0] == -1.0f && floatVec[1] == 0.0f && floatVec[2] == 1.0f ); + + const CUtlVector< Color > &colorVec = pElement3->GetArray<Color>( "color_array_test" ); + Shipping_Assert( colorVec.Count() == 3 && colorVec[0].r() == 0 && colorVec[1].r() == 64 && colorVec[2].r() == 128 ); + + const CUtlVector< Vector2D > &vec2DVec = pElement3->GetArray<Vector2D>( "vector2d_array_test" ); + Shipping_Assert( vec2DVec.Count() == 2 && vec2DVec[0].x == -1.0f && vec2DVec[1].x == 1.0f ); + + const CUtlVector< Vector > &vec3DVec = pElement3->GetArray<Vector>( "vector3d_array_test" ); + Shipping_Assert( vec3DVec.Count() == 2 && vec3DVec[0].x == 1.0f && vec3DVec[1].x == 2.0f ); + + const CUtlVector< Vector4D > &vec4DVec = pElement4->GetArray<Vector4D>( "vector4d_array_test" ); + Shipping_Assert( vec4DVec.Count() == 2 && vec4DVec[0].x == 1.0f && vec4DVec[1].x == 2.0f ); + + const CUtlVector< QAngle > &angVec = pElement4->GetArray<QAngle>( "qangle_array_test" ); + Shipping_Assert( angVec.Count() == 2 && angVec[0].x == 1.0f && angVec[1].x == 2.0f ); + + const CUtlVector< Quaternion > &quatVec = pElement4->GetArray<Quaternion>( "quat_array_test" ); + Shipping_Assert( quatVec.Count() == 2 && quatVec[0].x == 1.0f && quatVec[1].x == 2.0f ); + + const CUtlVector< VMatrix > &matVec = pElement5->GetArray<VMatrix>( "vmatrix_array_test" ); + Shipping_Assert( matVec.Count() == 2 ); + Shipping_Assert( MatricesAreEqual( matVec[0], mattest, 1e-3 ) ); + Shipping_Assert( MatricesAreEqual( matVec[1], mat2test, 1e-3 ) ); + + const CUtlVector< CUtlString > &stringVec = pElement5->GetArray<CUtlString>( "string_array_test" ); + Shipping_Assert( stringVec.Count() == 3 && !Q_stricmp( stringVec[2], "string3" ) ); + + const CUtlVector< CUtlBinaryBlock > &binaryVec = pElement5->GetArray<CUtlBinaryBlock>( "binary_array_test" ); + Shipping_Assert( binaryVec.Count() == 2 && !memcmp( binaryVec[1].Get(), buf2test, 256 ) ); + + const CUtlVector< DmObjectId_t > &idVec = pElement6->GetArray<DmObjectId_t>( "elementid_array_test" ); + Shipping_Assert( idVec.Count() == 3 ); + + TestStruct_t testStruct; + pRoot->UnpackIntoStructure( &testStruct, sizeof( testStruct ), s_TestStructUnpack ); + + Shipping_Assert( testStruct.m_bBool == true ); + Shipping_Assert( testStruct.m_nInt == 2 ); + AssertFloatEquals( testStruct.m_flFloat, 3.0f, 1e-3 ); + Shipping_Assert( testStruct.m_Color.r() == 0 && testStruct.m_Color.g() == 64 && testStruct.m_Color.b() == 128 && testStruct.m_Color.a() == 255 ); + Shipping_Assert( testStruct.m_Vector2D.x == 1.0f && testStruct.m_Vector2D.y == -1.0f ); + Shipping_Assert( testStruct.m_Vector3D.x == 1.0f && testStruct.m_Vector3D.y == -1.0f && testStruct.m_Vector3D.z == 0.0f ); + Shipping_Assert( testStruct.m_Vector4D.x == 1.0f && testStruct.m_Vector4D.y == -1.0f && testStruct.m_Vector4D.z == 0.0f && testStruct.m_Vector4D.w == 2.0f ); + Shipping_Assert( testStruct.m_Angles.x == 0.0f && testStruct.m_Angles.y == 90.0f && testStruct.m_Angles.z == -90.0f ); + Shipping_Assert( testStruct.m_Quaternion.x == 1.0f && testStruct.m_Quaternion.y == -1.0f && testStruct.m_Quaternion.z == 0.0f && testStruct.m_Quaternion.w == 2.0f ); + Shipping_Assert( MatricesAreEqual( testStruct.m_Matrix, mattest, 1e-3 ) ); + Shipping_Assert( !Q_stricmp( testStruct.m_pStringBuf, "test" ) ); + + pElement6->UnpackIntoStructure( &testStruct, sizeof( testStruct ), s_TestStructUnpack ); + + Shipping_Assert( testStruct.m_bBool == true ); + Shipping_Assert( testStruct.m_nInt == 5 ); + AssertFloatEquals( testStruct.m_flFloat, 4.0f, 1e-3 ); + Shipping_Assert( testStruct.m_Color.r() == 200 && testStruct.m_Color.g() == 200 && testStruct.m_Color.b() == 200 && testStruct.m_Color.a() == 200 ); + Shipping_Assert( testStruct.m_Vector2D.x == 5.0f && testStruct.m_Vector2D.y == 1.0f ); + Shipping_Assert( testStruct.m_Vector3D.x == 5.0f && testStruct.m_Vector3D.y == 1.0f && testStruct.m_Vector3D.z == -3.0f ); + Shipping_Assert( testStruct.m_Vector4D.x == 5.0f && testStruct.m_Vector4D.y == 1.0f && testStruct.m_Vector4D.z == -4.0f && testStruct.m_Vector4D.w == 2.0f ); + Shipping_Assert( testStruct.m_Angles.x == 5.0f && testStruct.m_Angles.y == 1.0f && testStruct.m_Angles.z == -3.0f ); + Shipping_Assert( testStruct.m_Quaternion.x == 5.0f && testStruct.m_Quaternion.y == 1.0f && testStruct.m_Quaternion.z == -4.0f && testStruct.m_Quaternion.w == 2.0f ); + Shipping_Assert( !Q_stricmp( testStruct.m_pStringBuf, "default" ) ); +} + +DEFINE_TESTCASE_NOSUITE( DmxLoaderTest ) +{ + Msg( "Running dmx loader tests...\n" ); + + CDmxElement *pRoot; + bool bOk = UnserializeDMX( "dmxtestloader.dmx", NULL, false, &pRoot ); + Shipping_Assert( bOk ); + Shipping_Assert( pRoot ); + if ( pRoot ) + { + TestReadFile( pRoot ); + CleanupDMX( pRoot ); + } + + bOk = UnserializeDMX( "dmxtestloadertext.dmx", NULL, true, &pRoot ); + Shipping_Assert( bOk ); + Shipping_Assert( pRoot ); + if ( pRoot ) + { + TestReadFile( pRoot ); + CleanupDMX( pRoot ); + } + + // Test serialization + bOk = UnserializeDMX( "dmxtestloader.dmx", NULL, false, &pRoot ); + Shipping_Assert( bOk ); + Shipping_Assert( pRoot ); + if ( pRoot ) + { + bOk = SerializeDMX( "dmxtestscratch.dmx", NULL, false, pRoot ); + Shipping_Assert( bOk ); + CleanupDMX( pRoot ); + } + CleanupDMX( pRoot ); + + bOk = UnserializeDMX( "dmxtestscratch.dmx", NULL, false, &pRoot ); + Shipping_Assert( bOk ); + Shipping_Assert( pRoot ); + if ( pRoot ) + { + TestReadFile( pRoot ); + CleanupDMX( pRoot ); + } +} diff --git a/unittests/dmxtest/dmxtestnotify.cpp b/unittests/dmxtest/dmxtestnotify.cpp new file mode 100644 index 0000000..e5a24be --- /dev/null +++ b/unittests/dmxtest/dmxtestnotify.cpp @@ -0,0 +1,111 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Unit test program for DMX testing (testing the Notify subsystem) +// +// $NoKeywords: $ +//=============================================================================// + +#include "unitlib/unitlib.h" +#include "datamodel/dmelement.h" +#include "datamodel/idatamodel.h" +#include "tier1/utlbuffer.h" +#include "filesystem.h" +#include "datamodel/dmehandle.h" +#include "tier2/tier2.h" + +class CNotifyTest : public IDmNotify +{ +public: + CNotifyTest() : m_nValueCount(0), m_nTopologyCount(0), m_nArrayCount(0) {} + + virtual void NotifyDataChanged( const char *pReason, int nNotifySource, int nNotifyFlags ) + { + if ( nNotifyFlags & NOTIFY_CHANGE_ATTRIBUTE_VALUE ) + { + m_nValueCount++; + } + if ( nNotifyFlags & NOTIFY_CHANGE_ATTRIBUTE_ARRAY_SIZE ) + { + m_nArrayCount++; + } + if ( nNotifyFlags & NOTIFY_CHANGE_TOPOLOGICAL ) + { + m_nTopologyCount++; + } + } + + int m_nTopologyCount; + int m_nArrayCount; + int m_nValueCount; +}; + + +DEFINE_TESTCASE_NOSUITE( DmxNotifyTest ) +{ + Msg( "Running dmx notify tests...\n" ); + + CNotifyTest test1, test2; + + DmFileId_t fileid = g_pDataModel->FindOrCreateFileId( "<RunNotifyTests>" ); + + g_pDataModel->InstallNotificationCallback( &test1 ); + + CDmElement *element = NULL; + + { + CUndoScopeGuard guard( NOTIFY_SOURCE_APPLICATION, 0, "create" ); + element = CreateElement< CDmElement >( "test", fileid ); + } + + Shipping_Assert( test1.m_nTopologyCount == 1 ); + Shipping_Assert( test1.m_nArrayCount == 0 ); + + g_pDataModel->Undo(); + + Shipping_Assert( test1.m_nTopologyCount == 2 ); + Shipping_Assert( test1.m_nArrayCount == 0 ); + + { + CNotifyScopeGuard notify( "test1", NOTIFY_SOURCE_APPLICATION, 0, &test2 ); + CDisableUndoScopeGuard guard; + element = CreateElement< CDmElement >( "test", fileid ); + } + + Shipping_Assert( test1.m_nTopologyCount == 3 ); + Shipping_Assert( test1.m_nArrayCount == 0 ); + Shipping_Assert( test2.m_nTopologyCount == 1 ); + Shipping_Assert( test2.m_nArrayCount == 0 ); + + { + CDisableUndoScopeGuard guard; + + // NOTE: Nested scope guards referring to the same callback shouldn't double call it + CNotifyScopeGuard notify( "test2", NOTIFY_SOURCE_APPLICATION, 0, &test2 ); + { + CNotifyScopeGuard notify( "test3", NOTIFY_SOURCE_APPLICATION, 0, &test2 ); + DestroyElement( element ); + } + } + + Shipping_Assert( test1.m_nTopologyCount == 4 ); + Shipping_Assert( test1.m_nArrayCount == 0 ); + Shipping_Assert( test2.m_nTopologyCount == 2 ); + Shipping_Assert( test2.m_nArrayCount == 0 ); + + { + CUndoScopeGuard guard( NOTIFY_SOURCE_APPLICATION, 0, "create" ); + { + element = CreateElement< CDmElement >( "test", fileid ); + element->SetValue( "test", 1.0f ); + } + guard.Abort(); + } + + Shipping_Assert( test1.m_nTopologyCount == 4 ); + Shipping_Assert( test1.m_nArrayCount == 0 ); + Shipping_Assert( test2.m_nTopologyCount == 2 ); + Shipping_Assert( test2.m_nArrayCount == 0 ); + + g_pDataModel->RemoveNotificationCallback( &test1 ); + g_pDataModel->RemoveFileId( fileid ); +} diff --git a/unittests/dmxtest/dmxtestserialization.cpp b/unittests/dmxtest/dmxtestserialization.cpp new file mode 100644 index 0000000..57bd323 --- /dev/null +++ b/unittests/dmxtest/dmxtestserialization.cpp @@ -0,0 +1,760 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Unit test program for DMX testing +// +// $NoKeywords: $ +//=============================================================================// + +#include "unitlib/unitlib.h" +#include "datamodel/dmelement.h" +#include "datamodel/idatamodel.h" +#include "tier1/utlbuffer.h" +#include "filesystem.h" +#include "datamodel/dmehandle.h" +#include "tier2/tier2.h" + +bool AssertEqualElementHierarchies( bool quiet, DmElementHandle_t src1, DmElementHandle_t src2 ); +bool AssertUnEqualElementHierarchies( DmElementHandle_t src1, DmElementHandle_t src2 ) +{ + bool equal = AssertEqualElementHierarchies( true, src1, src2 ); + if ( equal ) + { + AssertMsg( 0, "Hierarchies equal, expecting mismatch\n" ); + } + return !equal; +} + + +void CreateTestScene( CUtlVector< DmElementHandle_t >& handles, DmFileId_t fileid ) +{ + DmObjectId_t id; + CreateUniqueId( &id ); + + VMatrix mat, mat2; + MatrixBuildRotateZ( mat, 45 ); + MatrixBuildRotateZ( mat2, 30 ); + + int i; + unsigned char buf[256]; + unsigned char buf2[256]; + for ( i = 0; i < 256; ++i ) + { + buf[i] = i; + buf2[i] = 255 - i; + } + + CDmElement *pElement = CreateElement<CDmElement>( "root", fileid ); + Assert( pElement ); + CDmElement *pElement2 = CreateElement<CDmElement>( "shared_child", fileid ); + Assert( pElement2 ); + CDmElement *pElement3 = CreateElement<CDmElement>( "unique_child", fileid ); + Assert( pElement3 ); + CDmElement *pElement4 = CreateElement<CDmElement>( "shared_array_element", fileid ); + Assert( pElement4 ); + CDmElement *pElement5 = CreateElement<CDmElement>( "unique_array_element", fileid ); + Assert( pElement5 ); + CDmElement *pElement6 = CreateElement<CDmElement>( "shared_element", fileid ); + Assert( pElement6 ); + CDmElement *pElement7 = CreateElement<CDmElement>( "unique_element", fileid ); + Assert( pElement7 ); + + g_pDataModel->SetFileRoot( fileid, pElement->GetHandle() ); + + handles.AddToTail( pElement->GetHandle() ); + handles.AddToTail( pElement2->GetHandle() ); + handles.AddToTail( pElement3->GetHandle() ); + handles.AddToTail( pElement4->GetHandle() ); + handles.AddToTail( pElement5->GetHandle() ); + handles.AddToTail( pElement6->GetHandle() ); + handles.AddToTail( pElement7->GetHandle() ); + + pElement->SetValue( "id_test", id ); + pElement->SetValue( "bool_test", true ); + pElement->SetValue( "int_test", 2 ); + pElement->SetValue( "float_test", 3.0f ); + pElement->SetValue( "color_test", Color( 0, 64, 128, 255 ) ); + pElement->SetValue( "vector2d_test", Vector2D( 1.0f, -1.0f ) ); + pElement->SetValue( "vector3d_test", Vector( 1.0f, -1.0f, 0.0f ) ); + pElement->SetValue( "vector4d_test", Vector4D( 1.0f, -1.0f, 0.0f, 2.0f ) ); + pElement->SetValue( "qangle_test", QAngle( 0.0f, 90.0f, -90.0f ) ); + pElement->SetValue( "quat_test", Quaternion( 1.0f, -1.0f, 0.0f, 2.0f ) ); + pElement->SetValue( "vmatrix_test", mat ); + pElement->SetValue( "string_test", "test" ); + pElement->SetValue( "binary_test", buf, 256 ); + + // Test DONTSAVE +// pElement->SetValue( "dontsave", true ); +// CDmAttribute *pAttribute = pElement->GetAttribute( "dontsave" ); +// pAttribute->AddFlag( FATTRIB_DONTSAVE ); + + CDmrArray< bool > boolVec( pElement2, "bool_array_test", true ); + boolVec.AddToTail( false ); + boolVec.AddToTail( true ); + + CDmrArray< int > intVec( pElement2, "int_array_test", true ); + intVec.AddToTail( 0 ); + intVec.AddToTail( 1 ); + intVec.AddToTail( 2 ); + + CDmrArray< float > floatVec( pElement2, "float_array_test", true ); + floatVec.AddToTail( -1.0f ); + floatVec.AddToTail( 0.0f ); + floatVec.AddToTail( 1.0f ); + + CDmrArray< Color > colorVec( pElement3, "color_array_test", true ); + colorVec.AddToTail( Color( 0, 0, 0, 255 ) ); + colorVec.AddToTail( Color( 64, 64, 64, 255 ) ); + colorVec.AddToTail( Color( 128, 128, 128, 255 ) ); + + CDmrArray< Vector2D > vector2DVec( pElement3, "vector2d_array_test", true ); + vector2DVec.AddToTail( Vector2D( -1.0f, -1.0f ) ); + vector2DVec.AddToTail( Vector2D( 1.0f, 1.0f ) ); + + CDmrArray< Vector > vector3DVec( pElement3, "vector3d_array_test", true ); + vector3DVec.AddToTail( Vector( 1.0f, -1.0f, 0.0f ) ); + vector3DVec.AddToTail( Vector( 2.0f, -2.0f, 0.0f ) ); + + CDmrArray< Vector4D > vector4DVec( pElement4, "vector4d_array_test", true ); + vector4DVec.AddToTail( Vector4D( 1.0f, -1.0f, 0.0f, 2.0f ) ); + vector4DVec.AddToTail( Vector4D( 2.0f, -2.0f, 0.0f, 4.0f ) ); + + CDmrArray< QAngle > angleVec( pElement4, "qangle_array_test", true ); + angleVec.AddToTail( QAngle( 1.0f, -1.0f, 0.0f ) ); + angleVec.AddToTail( QAngle( 2.0f, -2.0f, 0.0f ) ); + + CDmrArray< Quaternion > quatVec( pElement4, "quat_array_test", true ); + quatVec.AddToTail( Quaternion( 1.0f, -1.0f, 0.0f, 2.0f ) ); + quatVec.AddToTail( Quaternion( 2.0f, -2.0f, 0.0f, 4.0f ) ); + + CDmrArray< VMatrix > matVec( pElement5, "vmatrix_array_test", true ); + matVec.AddToTail( mat ); + matVec.AddToTail( mat2 ); + + CDmrStringArray stringVec( pElement5, "string_array_test", true ); + stringVec.AddToTail( "string1" ); + stringVec.AddToTail( "string2" ); + stringVec.AddToTail( "string3" ); + + CDmrArray< CUtlBinaryBlock > binaryVec( pElement5, "binary_array_test", true ); + CUtlBinaryBlock block( (const void *)buf, 256 ); + i = binaryVec.AddToTail( block ); + CUtlBinaryBlock block2( (const void *)buf2, 256 ); + i = binaryVec.AddToTail( block2); + + CDmrArray< DmObjectId_t > idVec( pElement6, "elementid_array_test", true ); + i = idVec.AddToTail( pElement6->GetId() ); + i = idVec.AddToTail( pElement5->GetId() ); + i = idVec.AddToTail( pElement4->GetId() ); + + CDmrElementArray< > elementVec( pElement6, "element_array_test", true ); + elementVec.AddToTail( pElement4 ); + elementVec.AddToTail( pElement5 ); + + CDmrElementArray< > elementVec2( pElement7, "element_array_test", true ); + elementVec2.AddToTail( pElement2 ); + elementVec2.AddToTail( pElement4 ); + + pElement->SetValue( "element_test", pElement7 ); + pElement->SetValue( "shared_element_test", pElement6 ); + CDmrElementArray<> children( pElement, "children", true ); + children.InsertBefore( 0, pElement2 ); + children.InsertBefore( 1, pElement3 ); + + pElement7->SetValue( "shared_element_test", pElement6 ); + CDmrElementArray<> children2( pElement7, "children", true ); + children2.InsertBefore( 0, pElement2 ); +} + +DmElementHandle_t CreateTestScene( DmFileId_t fileid ) +{ + CUtlVector< DmElementHandle_t > handles; + CreateTestScene( handles, fileid ); + return handles[ 0 ]; +} + +DmElementHandle_t CreateKeyValuesTestScene( DmFileId_t fileid ) +{ + CDmElement *pElement = CreateElement<CDmElement>( "root", fileid ); + Assert( pElement ); + CDmElement *pElement2 = CreateElement<CDmElement>( "shared_child", fileid ); + Assert( pElement2 ); + CDmElement *pElement3 = CreateElement<CDmElement>( "unique_child", fileid ); + Assert( pElement3 ); + CDmElement *pElement4 = CreateElement<CDmElement>( "shared_array_element", fileid ); + Assert( pElement4 ); + CDmElement *pElement5 = CreateElement<CDmElement>( "unique_array_element", fileid ); + Assert( pElement5 ); + CDmElement *pElement6 = CreateElement<CDmElement>( "shared_element", fileid ); + Assert( pElement6 ); + CDmElement *pElement7 = CreateElement<CDmElement>( "unique_element", fileid ); + Assert( pElement7 ); + + g_pDataModel->SetFileRoot( fileid, pElement->GetHandle() ); + + pElement->SetValue( "int_test", 2 ); + pElement->SetValue( "float_test", 3.0f ); + pElement->SetValue( "string_test", "test" ); + + CDmrElementArray<> eVec( pElement6, "element_array_test", true ); + eVec.AddToTail( pElement4 ); + eVec.AddToTail( pElement5 ); + + CDmrElementArray<> eVec2( pElement7, "element_array_test", true ); + eVec2.AddToTail( pElement2 ); + eVec2.AddToTail( pElement4 ); + + pElement->SetValue( "element_test", pElement7 ); + pElement->SetValue( "shared_element_test", pElement6 ); + CDmrElementArray<> children( pElement, "children", true ); + children.InsertBefore( 0, pElement2 ); + children.InsertBefore( 1, pElement3 ); + + pElement7->SetValue( "shared_element_test", pElement6 ); + CDmrElementArray<> children2( pElement7, "children", true ); + children2.InsertBefore( 0, pElement2 ); + + return pElement->GetHandle(); +} + +template< class T > +bool AssertEqualsTest( bool quiet, const T& src1, const T& src2 ) +{ + if ( !( src1 == src2 )) + { + if ( !quiet ) + { + AssertMsg( 0, "Results not equal, expecting equal\n" ); + } + return false; + } + return true; +} + +template< class T > +bool AssertEqualsUtlVector( bool quiet, const CUtlVector<T> &src1, const CUtlVector<T> &src2 ) +{ + bool retval = true; + if ( src1.Count() != src2.Count() ) + { + if ( !quiet ) + { + AssertEqualsTest( quiet, src1.Count(), src2.Count() ); + } + retval = false; + } + + for ( int i = 0; i < src1.Count(); ++i ) + { + if ( !src2.IsValidIndex( i ) ) + continue; + + if ( !( src1[i] == src2[i] ) ) + { + if ( !quiet ) + { + AssertEqualsTest( quiet, src1[i], src2[i] ); + } + retval = false; + } + } + return retval; +} + +template< class T > +bool AssertEqualsUtlVector( bool quiet, CDmAttribute *pAttribute1, CDmAttribute *pAttribute2 ) +{ + CDmrArray<T> src1( pAttribute1 ); + CDmrArray<T> src2( pAttribute2 ); + return AssertEqualsUtlVector( quiet, src1.Get(), src2.Get() ); +} + +bool AssertEqualAttributes( bool quiet, CDmAttribute *pAttribute1, CDmAttribute *pAttribute2 ) +{ + // Always follow ptrs to elements... + if ( pAttribute1->GetType() != AT_ELEMENT_ARRAY && + pAttribute1->GetType() != AT_ELEMENT ) + { + // Dirty flag checking here is to avoid infinite recursive loops + if ( !pAttribute1->IsFlagSet( FATTRIB_DIRTY ) && !pAttribute2->IsFlagSet( FATTRIB_DIRTY ) ) + return true; + } + + if ( !pAttribute1 ) + { + if ( !quiet ) + { + AssertMsg( 0, "AssertEqualAttributes: pAttribute1 is NULL\n" ); + } + return false; + } + + + if ( !pAttribute2 ) + { + if ( !quiet ) + { + AssertMsg( 0, "AssertEqualAttributes: pAttribute2 is NULL\n" ); + } + return false; + } + + bool retval = true; + + pAttribute1->RemoveFlag( FATTRIB_DIRTY ); + pAttribute2->RemoveFlag( FATTRIB_DIRTY ); + + if ( pAttribute1->GetType() != pAttribute2->GetType() ) + { + if ( !quiet ) + { + AssertMsg( 0, "pAttribute1->GetType() == pAttribute2->GetType()" ); + } + retval = false; + } + + switch( pAttribute1->GetType() ) + { + case AT_INT: + return AssertEqualsTest( quiet, pAttribute1->GetValue<int>( ), pAttribute2->GetValue<int>( ) ); + + case AT_FLOAT: + return AssertEqualsTest( quiet, pAttribute1->GetValue<float>( ), pAttribute2->GetValue<float>( ) ); + + case AT_BOOL: + return AssertEqualsTest( quiet, pAttribute1->GetValue<bool>( ), pAttribute2->GetValue<bool>( ) ); + + case AT_STRING: + return AssertEqualsTest( quiet, pAttribute1->GetValue<CUtlString>( ), pAttribute2->GetValue<CUtlString>( ) ); + + case AT_VOID: + return AssertEqualsTest( quiet, pAttribute1->GetValue<CUtlBinaryBlock>( ), pAttribute2->GetValue<CUtlBinaryBlock>( ) ); + + case AT_OBJECTID: + return true; // skip this for now - two elements can't have the same id, and CreateTestScene currently creates random test_id's each time... +/* + { + if ( !g_pDataModel->IsEqual( pAttribute1->GetValue<DmObjectId_t>( ), pAttribute2->GetValue<DmObjectId_t>( ) ) ) + { + if ( !quiet ) + { + Assert( g_pDataModel->IsEqual( pAttribute1->GetValue<DmObjectId_t>( ), pAttribute2->GetValue<DmObjectId_t>( ) ) ); + } + return false; + } + return true; + } + break; +*/ + + case AT_COLOR: + return AssertEqualsTest( quiet, pAttribute1->GetValue<Color>( ), pAttribute2->GetValue<Color>( ) ); + + case AT_VECTOR2: + return AssertEqualsTest( quiet, pAttribute1->GetValue<Vector2D>( ), pAttribute2->GetValue<Vector2D>( ) ); + + case AT_VECTOR3: + return AssertEqualsTest( quiet, pAttribute1->GetValue<Vector>( ), pAttribute2->GetValue<Vector>( ) ); + + case AT_VECTOR4: + return AssertEqualsTest( quiet, pAttribute1->GetValue<Vector4D>( ), pAttribute2->GetValue<Vector4D>( ) ); + + case AT_QANGLE: + return AssertEqualsTest( quiet, pAttribute1->GetValue<QAngle>( ), pAttribute2->GetValue<QAngle>( ) ); + + case AT_QUATERNION: + return AssertEqualsTest( quiet, pAttribute1->GetValue<Quaternion>( ), pAttribute2->GetValue<Quaternion>( ) ); + + case AT_VMATRIX: + return AssertEqualsTest( quiet, pAttribute1->GetValue<VMatrix>( ), pAttribute2->GetValue<VMatrix>( ) ); + + case AT_ELEMENT: + return AssertEqualElementHierarchies( quiet, pAttribute1->GetValue<DmElementHandle_t>( ), pAttribute2->GetValue<DmElementHandle_t>( ) ); + + case AT_ELEMENT_ARRAY: + { + const CDmrElementArray< CDmElement > src1( pAttribute1 ); + const CDmrElementArray< CDmElement > src2( pAttribute2 ); + + bool differs = !AssertEqualsTest( quiet, src1.Count(), src2.Count() ); + bool differs2 = false; + for ( int i = 0; i < src1.Count(); ++i ) + { + differs2 |= !AssertEqualElementHierarchies( quiet, src1[ i ]->GetHandle(), src2[ i ]->GetHandle() ); + } + + return ( !differs && !differs2 ); + } + break; + + case AT_INT_ARRAY: + return AssertEqualsUtlVector<int>( quiet, pAttribute1, pAttribute2 ); + + case AT_FLOAT_ARRAY: + return AssertEqualsUtlVector<float>( quiet, pAttribute1, pAttribute2 ); + + case AT_BOOL_ARRAY: + return AssertEqualsUtlVector<bool>( quiet, pAttribute1, pAttribute2 ); + + case AT_STRING_ARRAY: + return AssertEqualsUtlVector<CUtlString>( quiet, pAttribute1, pAttribute2 ); + + case AT_VOID_ARRAY: + return AssertEqualsUtlVector<CUtlBinaryBlock>( quiet, pAttribute1, pAttribute2 ); + + case AT_OBJECTID_ARRAY: + { + const CDmrArray<DmObjectId_t> src1( pAttribute1 ); + const CDmrArray<DmObjectId_t> src2( pAttribute2 ); + + bool differs = AssertEqualsTest( quiet, src1.Count(), src2.Count() ); + return differs; // skip this for now - CreateTestScene currently creates random ids each time... +/* + bool differs2 = false; + for ( int i = 0; i < src1.Count(); ++i ) + { + if ( !g_pDataModel->IsEqual( src1[i], src2[i] ) ) + { + differs2 = true; + if ( !quiet ) + { + Assert( g_pDataModel->IsEqual( src1[i], src2[i] ) ); + } + } + } + + return ( !differs && !differs2 ); +*/ + } + break; + + case AT_COLOR_ARRAY: + return AssertEqualsUtlVector<Color>( quiet, pAttribute1, pAttribute2 ); + + case AT_VECTOR2_ARRAY: + return AssertEqualsUtlVector<Vector2D>( quiet, pAttribute1, pAttribute2 ); + + case AT_VECTOR3_ARRAY: + return AssertEqualsUtlVector<Vector>( quiet, pAttribute1, pAttribute2 ); + + case AT_VECTOR4_ARRAY: + return AssertEqualsUtlVector<Vector4D>( quiet, pAttribute1, pAttribute2 ); + + case AT_QANGLE_ARRAY: + return AssertEqualsUtlVector<QAngle>( quiet, pAttribute1, pAttribute2 ); + + case AT_QUATERNION_ARRAY: + return AssertEqualsUtlVector<Quaternion>( quiet, pAttribute1, pAttribute2 ); + + case AT_VMATRIX_ARRAY: + return AssertEqualsUtlVector<VMatrix>( quiet, pAttribute1, pAttribute2 ); + } + + return retval; +} + +bool AssertEqualElementHierarchies( bool quiet, DmElementHandle_t src1, DmElementHandle_t src2 ) +{ + CDmElement *pSrc1 = g_pDataModel->GetElement( src1 ); + CDmElement *pSrc2 = g_pDataModel->GetElement( src2 ); + + if ( !pSrc1 || !pSrc2 ) + return false; + + // Assume equality + bool retval = true; + + if ( pSrc1->GetType() != pSrc2->GetType() ) + { + if ( !quiet ) + { + AssertMsg( 0, "pSrc1->GetType() == pSrc2->GetType()" ); + } + retval = false; + } + + if ( Q_strcmp( pSrc1->GetName(), pSrc2->GetName() ) ) + { + if ( !quiet ) + { + AssertMsg2( 0, "Q_strcmp( %s, %s )", pSrc1->GetName(), pSrc2->GetName() ); + } + retval = false; + } + + if ( pSrc1->AttributeCount() != pSrc2->AttributeCount() ) + { + if ( !quiet ) + { + AssertMsg( 0, "pSrc1->NumAttributes() == pSrc2->NumAttributes()" ); + } + retval = false; + } + + for ( CDmAttribute *pAttribute1 = pSrc1->FirstAttribute(); pAttribute1; pAttribute1 = pAttribute1->NextAttribute() ) + { + const char *pName = pAttribute1->GetName(); + if ( !pSrc2->HasAttribute( pName ) ) + { + if ( !quiet ) + { + AssertMsg1( 0, "pSrc2->HasAttribute( %s ) failed\n", pName ); + } + retval = false; + } + else + { + CDmAttribute *pAttribute2 = pSrc2->GetAttribute( pName ); + + bool differs = !AssertEqualAttributes( quiet, pAttribute1, pAttribute2 ); + if ( differs ) + { + retval = false; + } + } + } + + return retval; +} + +void TestDeleteOldCR( const char *pSerializationType ) +{ + DmFileId_t testFileID = g_pDataModel->FindOrCreateFileId( "<TestDeleteOldCR>" ); + DmElementHandle_t hRoot = CreateTestScene( testFileID ); + + int nTestElements = g_pDataModel->NumElementsInFile( testFileID ); + + const char *pFileName = "DeleteOld.dmx"; + CDmElement *pRoot = static_cast< CDmElement* >( g_pDataModel->GetElement( hRoot ) ); + bool bOk = g_pDataModel->SaveToFile( pFileName, NULL, pSerializationType, "dmx", pRoot ); + Shipping_Assert( bOk ); + + CDmElement *pReadInRoot = NULL; + DmFileId_t readFileID = g_pDataModel->RestoreFromFile( pFileName, NULL, NULL, &pReadInRoot, CR_DELETE_OLD ); + Shipping_Assert( readFileID != DMFILEID_INVALID ); + + if ( pReadInRoot ) + { + Shipping_Assert( pReadInRoot->GetHandle() == hRoot ); + Shipping_Assert( g_pDataModel->GetElement( hRoot ) == pReadInRoot ); + Shipping_Assert( g_pDataModel->NumElementsInFile( testFileID ) == 0 ); + Shipping_Assert( g_pDataModel->NumElementsInFile( readFileID ) == nTestElements ); + + CDmeHandle< CDmElement > rootHandle( hRoot ); // keeps a reference to root around, even after the file is unloaded + g_pDataModel->UnloadFile( readFileID ); + + Shipping_Assert( g_pDataModel->NumElementsInFile( readFileID ) == 0 ); + Shipping_Assert( g_pDataModel->GetElement( hRoot ) == NULL ); + + DmFileId_t readFileID2 = g_pDataModel->RestoreFromFile( pFileName, NULL, NULL, &pReadInRoot, CR_DELETE_OLD ); + Shipping_Assert( readFileID2 == readFileID ); + + Shipping_Assert( pReadInRoot->GetHandle() == hRoot ); + Shipping_Assert( g_pDataModel->GetElement( hRoot ) == pReadInRoot ); + Shipping_Assert( g_pDataModel->NumElementsInFile( testFileID ) == 0 ); + Shipping_Assert( g_pDataModel->NumElementsInFile( readFileID ) == nTestElements ); + + g_pDataModel->RemoveFileId( readFileID ); + } + else + { + Msg( "Failed to load %s back from disk!!!", pFileName ); + } + + g_pDataModel->RemoveFileId( testFileID ); +} + +void TestDeleteNewCR( const char *pSerializationType ) +{ + DmFileId_t testFileID = g_pDataModel->FindOrCreateFileId( "<TestDeleteNewCR>" ); + DmElementHandle_t hRoot = CreateTestScene( testFileID ); + + int nTestElements = g_pDataModel->NumElementsInFile( testFileID ); + + const char *pFileName = "DeleteNew.dmx"; + CDmElement *pRoot = static_cast< CDmElement* >( g_pDataModel->GetElement( hRoot ) ); + bool bOk = g_pDataModel->SaveToFile( pFileName, NULL, pSerializationType, "dmx", pRoot ); + Shipping_Assert( bOk ); + + CDmElement *pReadInRoot = NULL; + DmFileId_t readFileID = g_pDataModel->RestoreFromFile( pFileName, NULL, NULL, &pReadInRoot, CR_DELETE_NEW ); + Shipping_Assert( readFileID != DMFILEID_INVALID ); + + Shipping_Assert( g_pDataModel->GetElement( hRoot ) == pRoot ); + Shipping_Assert( pRoot->GetHandle() == hRoot ); + Shipping_Assert( pReadInRoot == pRoot ); // RestoreFromFile now returns the old element when the new root is deleted + Shipping_Assert( g_pDataModel->NumElementsInFile( testFileID ) == nTestElements ); + Shipping_Assert( g_pDataModel->NumElementsInFile( readFileID ) == 0 ); + + g_pDataModel->UnloadFile( readFileID ); + + Shipping_Assert( g_pDataModel->NumElementsInFile( readFileID ) == 0 ); + + DmFileId_t readFileID2 = g_pDataModel->RestoreFromFile( pFileName, NULL, NULL, &pReadInRoot, CR_DELETE_NEW ); + Shipping_Assert( readFileID2 == readFileID ); + + Shipping_Assert( g_pDataModel->GetElement( hRoot ) == pRoot ); + Shipping_Assert( pRoot->GetHandle() == hRoot ); + Shipping_Assert( pReadInRoot == pRoot ); // RestoreFromFile now returns the old element when the new root is deleted + Shipping_Assert( g_pDataModel->NumElementsInFile( testFileID ) == nTestElements ); + Shipping_Assert( g_pDataModel->NumElementsInFile( readFileID ) == 0 ); + + g_pDataModel->RemoveFileId( readFileID ); + + g_pDataModel->RemoveFileId( testFileID ); +} + +void TestCopyNewCR( const char *pSerializationType ) +{ + DmFileId_t testFileID = g_pDataModel->FindOrCreateFileId( "<TestCopyNewCR>" ); + DmElementHandle_t hRoot = CreateTestScene( testFileID ); + + int nTestElements = g_pDataModel->NumElementsInFile( testFileID ); + + const char *pFileName = "CopyNew.dmx"; + CDmElement *pRoot = g_pDataModel->GetElement( hRoot ); + bool bOk = g_pDataModel->SaveToFile( pFileName, NULL, pSerializationType, "dmx", pRoot ); + Shipping_Assert( bOk ); + + CDmElement *pReadInRoot = NULL; + DmFileId_t readFileID = g_pDataModel->RestoreFromFile( pFileName, NULL, NULL, &pReadInRoot, CR_COPY_NEW ); + Shipping_Assert( readFileID != DMFILEID_INVALID ); + + if ( pReadInRoot ) + { + DmElementHandle_t hReadInRoot = pReadInRoot->GetHandle(); + + Shipping_Assert( g_pDataModel->GetElement( hRoot ) == pRoot ); + Shipping_Assert( pRoot->GetHandle() == hRoot ); + Shipping_Assert( pReadInRoot->GetHandle() != hRoot ); + Shipping_Assert( !IsUniqueIdEqual( pRoot->GetId(), pReadInRoot->GetId() ) ); + Shipping_Assert( g_pDataModel->NumElementsInFile( testFileID ) == nTestElements ); + Shipping_Assert( g_pDataModel->NumElementsInFile( readFileID ) == nTestElements ); + + g_pDataModel->UnloadFile( readFileID ); + + Shipping_Assert( g_pDataModel->NumElementsInFile( readFileID ) == 0 ); + Shipping_Assert( g_pDataModel->GetElement( hReadInRoot ) == NULL ); + + DmFileId_t readFileID2 = g_pDataModel->RestoreFromFile( pFileName, NULL, NULL, &pReadInRoot, CR_COPY_NEW ); + Shipping_Assert( readFileID2 == readFileID ); + + Shipping_Assert( g_pDataModel->GetElement( hRoot ) == pRoot ); + Shipping_Assert( pRoot->GetHandle() == hRoot ); + Shipping_Assert( pReadInRoot->GetHandle() != hRoot ); + Shipping_Assert( !IsUniqueIdEqual( pRoot->GetId(), pReadInRoot->GetId() ) ); + Shipping_Assert( g_pDataModel->NumElementsInFile( testFileID ) == nTestElements ); + Shipping_Assert( g_pDataModel->NumElementsInFile( readFileID ) == nTestElements ); + + g_pDataModel->RemoveFileId( readFileID ); + } + else + { + Msg( "Failed to load %s back from disk!!!", pFileName ); + } + + g_pDataModel->RemoveFileId( testFileID ); +} + +void TestForceCopyCR( const char *pSerializationType ) +{ + DmFileId_t testFileID = g_pDataModel->FindOrCreateFileId( "<TestForceCopyCR>" ); + DmElementHandle_t hRoot = CreateTestScene( testFileID ); + + int nTestElements = g_pDataModel->NumElementsInFile( testFileID ); + + const char *pFileName = "ForceCopy.dmx"; + CDmElement *pRoot = static_cast< CDmElement* >( g_pDataModel->GetElement( hRoot ) ); + bool bOk = g_pDataModel->SaveToFile( pFileName, NULL, pSerializationType, "dmx", pRoot ); + Shipping_Assert( bOk ); + + CDmElement *pReadInRoot = NULL; + DmFileId_t readFileID = g_pDataModel->RestoreFromFile( pFileName, NULL, NULL, &pReadInRoot, CR_FORCE_COPY ); + Shipping_Assert( readFileID != DMFILEID_INVALID ); + + if ( pReadInRoot ) + { + DmElementHandle_t hReadInRoot = pReadInRoot->GetHandle(); + + Shipping_Assert( g_pDataModel->GetElement( hRoot ) == pRoot ); + Shipping_Assert( pRoot->GetHandle() == hRoot ); + Shipping_Assert( pReadInRoot->GetHandle() != hRoot ); + Shipping_Assert( !IsUniqueIdEqual( pRoot->GetId(), pReadInRoot->GetId() ) ); + Shipping_Assert( g_pDataModel->NumElementsInFile( testFileID ) == nTestElements ); + Shipping_Assert( g_pDataModel->NumElementsInFile( readFileID ) == nTestElements ); + + g_pDataModel->UnloadFile( readFileID ); + + Shipping_Assert( g_pDataModel->NumElementsInFile( readFileID ) == 0 ); + Shipping_Assert( g_pDataModel->GetElement( hReadInRoot ) == NULL ); + + DmFileId_t readFileID2 = g_pDataModel->RestoreFromFile( pFileName, NULL, NULL, &pReadInRoot, CR_FORCE_COPY ); + Shipping_Assert( readFileID2 == readFileID ); + + Shipping_Assert( g_pDataModel->GetElement( hRoot ) == pRoot ); + Shipping_Assert( pRoot->GetHandle() == hRoot ); + Shipping_Assert( pReadInRoot->GetHandle() != hRoot ); + Shipping_Assert( !IsUniqueIdEqual( pRoot->GetId(), pReadInRoot->GetId() ) ); + Shipping_Assert( g_pDataModel->NumElementsInFile( testFileID ) == nTestElements ); + Shipping_Assert( g_pDataModel->NumElementsInFile( readFileID ) == nTestElements ); + + g_pDataModel->RemoveFileId( readFileID ); + } + else + { + Msg( "Failed to load %s back from disk!!!", pFileName ); + } + + g_pDataModel->RemoveFileId( testFileID ); +} + +void TestConflictResolution( const char *pSerializationType ) +{ + TestDeleteOldCR( pSerializationType ); + TestDeleteNewCR( pSerializationType ); + TestCopyNewCR( pSerializationType ); + TestForceCopyCR( pSerializationType ); +} + +void TestSerializationMethod( const char *pSerializationType ) +{ + DmFileId_t testFileID = g_pDataModel->FindOrCreateFileId( "<CreateTestScene>" ); + DmElementHandle_t hRoot = CreateTestScene( testFileID ); + + const char *pFileName = "dmxtest.dmx"; + CDmElement *pRoot = static_cast<CDmElement*>(g_pDataModel->GetElement(hRoot)); + bool bOk = g_pDataModel->SaveToFile( pFileName, NULL, pSerializationType, "dmx", pRoot ); + Shipping_Assert( bOk ); + + CDmElement *pReadInRoot = NULL; + DmFileId_t dmxFileID = g_pDataModel->RestoreFromFile( pFileName, NULL, NULL, &pReadInRoot, CR_FORCE_COPY ); + Shipping_Assert( dmxFileID != DMFILEID_INVALID ); + + if ( pReadInRoot ) + { + AssertEqualElementHierarchies( false, hRoot, pReadInRoot->GetHandle() ); + g_pDataModel->RemoveFileId( dmxFileID ); + } + else + { + Msg( "Failed to load dmxtest.dmx back from disk!!!" ); + } + + g_pDataModel->RemoveFileId( testFileID ); + + TestConflictResolution( pSerializationType ); +} + +DEFINE_TESTCASE_NOSUITE( DmxSerializationTest ) +{ + Msg( "Running dmx serialization tests...\n" ); + CDisableUndoScopeGuard sg; + + TestSerializationMethod( "keyvalues2" ); + TestSerializationMethod( "keyvalues2_flat" ); + TestSerializationMethod( "xml" ); + TestSerializationMethod( "xml_flat" ); + TestSerializationMethod( "binary" ); + + int nEndingCount = g_pDataModel->GetAllocatedElementCount(); + AssertEqualsTest( false, 0, nEndingCount ); +} diff --git a/unittests/dmxtest/dmxtestundoredo.cpp b/unittests/dmxtest/dmxtestundoredo.cpp new file mode 100644 index 0000000..991132c --- /dev/null +++ b/unittests/dmxtest/dmxtestundoredo.cpp @@ -0,0 +1,1096 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Unit test program for DMX testing +// +// $NoKeywords: $ +//=============================================================================// + +#include "unitlib/unitlib.h" +#include "datamodel/dmelement.h" +#include "datamodel/idatamodel.h" +#include "movieobjects/movieobjects.h" +#include "tier1/utlbuffer.h" +#include "filesystem.h" + + +// From dmxtestserialization.cpp + +bool AssertEqualElementHierarchies( bool quiet, DmElementHandle_t src1, DmElementHandle_t src2 ); +bool AssertUnEqualElementHierarchies( DmElementHandle_t src1, DmElementHandle_t src2 ); + +void CreateTestScene( CUtlVector< DmElementHandle_t >& handles, DmFileId_t fileid ); + +bool AssertEqualElementHierarchies( bool quiet, CDmElement *src1, CDmElement *src2 ) +{ + DmElementHandle_t h1 = src1->GetHandle(); + DmElementHandle_t h2 = src2->GetHandle(); + + return AssertEqualElementHierarchies( quiet, h1, h2 ); +} + +bool AssertUnEqualElementHierarchies( CDmElement *src1, CDmElement *src2 ) +{ + return AssertUnEqualElementHierarchies( src1->GetHandle(), src2->GetHandle() ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +//----------------------------------------------------------------------------- +void DescribeUndo() +{ + CUtlVector< UndoInfo_t > list; + g_pDataModel->GetUndoInfo( list ); + + for ( int i = list.Count() - 1; i >= 0; --i ) + { + UndoInfo_t& entry = list[ i ]; + if ( entry.terminator ) + { + Msg( "[ '%s' ] -- %i operations\n", entry.undo, entry.numoperations ); + } + + Msg( " +[ '%s' ]\n", entry.desc ); + } +} + +template< class T > +void RunSingleSetAttributeUndoTest( CDmElement *pTest, CDmElement *pOriginal, CDmElement *pElement, const char *attribute, const T& newVal ) +{ + { + CUndoScopeGuard guard( attribute, attribute ); + pElement->SetValue( attribute, newVal ); + } + g_pDataModel->Undo(); + + AssertEqualElementHierarchies( false, pTest, pOriginal ); + + g_pDataModel->Redo(); + g_pDataModel->Undo(); + + AssertEqualElementHierarchies( false, pTest, pOriginal ); +} + +void RunSingleSetAttributeUndoTestBinary( CDmElement *pTest, CDmElement *pOriginal, CDmElement *pElement, const char *attribute, const void *data, size_t size ) +{ + { + CUndoScopeGuard guard( attribute, attribute ); + pElement->SetValue( attribute, data, size ); + } + + AssertUnEqualElementHierarchies( pTest, pOriginal ); + + g_pDataModel->Undo(); + + AssertEqualElementHierarchies( false, pTest, pOriginal ); + + g_pDataModel->Redo(); + + AssertUnEqualElementHierarchies( pTest, pOriginal ); + + g_pDataModel->Undo(); + + AssertEqualElementHierarchies( false, pTest, pOriginal ); +} + +template< class T > +void RunSingleSetAttributeUndoTestArray( CDmElement *pTest, CDmElement *pOriginal, CDmElement *pElement, const char *attribute, int slot, const T& newVal ) +{ + { + CUndoScopeGuard guard( attribute, attribute ); + CDmrArray< T > array( pElement, attribute ); + array.Set( slot, newVal ); + } + + AssertUnEqualElementHierarchies( pTest, pOriginal ); + + g_pDataModel->Undo(); + + AssertEqualElementHierarchies( false, pTest, pOriginal ); + + g_pDataModel->Redo(); + + AssertUnEqualElementHierarchies( pTest, pOriginal ); + + g_pDataModel->Undo(); + + AssertEqualElementHierarchies( false, pTest, pOriginal ); +} + +template<> void RunSingleSetAttributeUndoTestArray<DmElementHandle_t>( CDmElement *pTest, CDmElement *pOriginal, CDmElement *pElement, const char *attribute, int slot, const DmElementHandle_t& newVal ) +{ + { + CUndoScopeGuard guard( attribute, attribute ); + CDmrElementArray<> array( pElement, attribute ); + array.SetHandle( slot, newVal ); + } + + AssertUnEqualElementHierarchies( pTest, pOriginal ); + + g_pDataModel->Undo(); + + AssertEqualElementHierarchies( false, pTest, pOriginal ); + + g_pDataModel->Redo(); + + AssertUnEqualElementHierarchies( pTest, pOriginal ); + + g_pDataModel->Undo(); + + AssertEqualElementHierarchies( false, pTest, pOriginal ); +} + +template< class T > +void RunSingleAttributeUndoTestArrayAddToHead( CDmElement *pTest, CDmElement *pOriginal, CDmElement *pElement, const char *attribute, const T& newVal ) +{ + { + CUndoScopeGuard guard( attribute, attribute ); + CDmrArray< T > array( pElement, attribute ); + array.InsertBefore( 0, newVal ); + } + + AssertUnEqualElementHierarchies( pTest, pOriginal ); + + g_pDataModel->Undo(); + + AssertEqualElementHierarchies( false, pTest, pOriginal ); + + g_pDataModel->Redo(); + + AssertUnEqualElementHierarchies( pTest, pOriginal ); + + g_pDataModel->Undo(); + + AssertEqualElementHierarchies( false, pTest, pOriginal ); +} + +template< class T > +void RunSingleAttributeUndoTestArrayAddToTail( CDmElement *pTest, CDmElement *pOriginal, CDmElement *pElement, const char *attribute, const T& newVal ) +{ + { + CUndoScopeGuard guard( attribute, attribute ); + CDmrArray< T > array( pElement, attribute ); + array.AddToTail( newVal ); + } + + AssertUnEqualElementHierarchies( pTest, pOriginal ); + + g_pDataModel->Undo(); + + AssertEqualElementHierarchies( false, pTest, pOriginal ); + + g_pDataModel->Redo(); + + AssertUnEqualElementHierarchies( pTest, pOriginal ); + + g_pDataModel->Undo(); + + AssertEqualElementHierarchies( false, pTest, pOriginal ); +} + +template< class T > +void RunSingleAttributeUndoTestArrayInsertBefore( CDmElement *pTest, CDmElement *pOriginal, CDmElement *pElement, const char *attribute, int slot, const T& newVal ) +{ + { + CUndoScopeGuard guard( attribute, attribute ); + CDmrArray< T > array( pElement, attribute ); + array.InsertBefore( slot, newVal ); + } + + AssertUnEqualElementHierarchies( pTest, pOriginal ); + + g_pDataModel->Undo(); + + AssertEqualElementHierarchies( false, pTest, pOriginal ); + + g_pDataModel->Redo(); + + AssertUnEqualElementHierarchies( pTest, pOriginal ); + + g_pDataModel->Undo(); + + AssertEqualElementHierarchies( false, pTest, pOriginal ); +} + + +template< class T > +void RunSingleAttributeUndoTestArrayFastRemove( CDmElement *pTest, CDmElement *pOriginal, CDmElement *pElement, const char *attribute, int slot, T typeDeducer ) +{ + { + CUndoScopeGuard guard( attribute, attribute ); + CDmrArray< T > array( pElement, attribute ); + array.FastRemove( slot ); + } + + AssertUnEqualElementHierarchies( pTest, pOriginal ); + + g_pDataModel->Undo(); + + AssertEqualElementHierarchies( false, pTest, pOriginal ); + + g_pDataModel->Redo(); + + AssertUnEqualElementHierarchies( pTest, pOriginal ); + + g_pDataModel->Undo(); + + AssertEqualElementHierarchies( false, pTest, pOriginal ); +} + + +void RunSingleAttributeUndoTestElementArrayRemove( CDmElement *pTest, CDmElement *pOriginal, CDmElement *pElement, const char *attribute, int slot ) +{ + { + CUndoScopeGuard guard( attribute, attribute ); + CDmrElementArray<> array( pElement, attribute ); + array.Remove( slot ); + } + + DescribeUndo(); + + AssertUnEqualElementHierarchies( pTest, pOriginal ); + + g_pDataModel->Undo(); + + AssertEqualElementHierarchies( false, pTest, pOriginal ); + + g_pDataModel->Redo(); + + AssertUnEqualElementHierarchies( pTest, pOriginal ); + + g_pDataModel->Undo(); + + AssertEqualElementHierarchies( false, pTest, pOriginal ); +} + +template< class T > +void RunSingleAttributeUndoTestArrayRemove( CDmElement *pTest, CDmElement *pOriginal, CDmElement *pElement, const char *attribute, int slot, T typeDeducer ) +{ + { + CUndoScopeGuard guard( attribute, attribute ); + CDmrArray<T> array( pElement, attribute ); + array.Remove( slot ); + } + + AssertUnEqualElementHierarchies( pTest, pOriginal ); + + g_pDataModel->Undo(); + + AssertEqualElementHierarchies( false, pTest, pOriginal ); + + g_pDataModel->Redo(); + + AssertUnEqualElementHierarchies( pTest, pOriginal ); + + g_pDataModel->Undo(); + + AssertEqualElementHierarchies( false, pTest, pOriginal ); +} + +template< class T > +void RunSingleAttributeUndoTestArrayRemoveMultiple( CDmElement *pTest, CDmElement *pOriginal, CDmElement *pElement, const char *attribute, int slot, int count, T typeDeducer ) +{ + { + CUndoScopeGuard guard( attribute, attribute ); + CDmrArray< T > array( pElement, attribute ); + array.RemoveMultiple( slot, count ); + } + + AssertUnEqualElementHierarchies( pTest, pOriginal ); + + g_pDataModel->Undo(); + + AssertEqualElementHierarchies( false, pTest, pOriginal ); + + g_pDataModel->Redo(); + + AssertUnEqualElementHierarchies( pTest, pOriginal ); + + g_pDataModel->Undo(); + + AssertEqualElementHierarchies( false, pTest, pOriginal ); +} + +template< class T > +void RunSingleAttributeUndoTestArrayRemoveAll( CDmElement *pTest, CDmElement *pOriginal, CDmElement *pElement, const char *attribute, bool purge ) +{ + { + CUndoScopeGuard guard( attribute, attribute ); + CDmrArray< T > array( pElement, attribute ); + if( purge ) + { + array.Purge(); + } + else + { + array.RemoveAll(); + } + } + + AssertUnEqualElementHierarchies( pTest, pOriginal ); + + g_pDataModel->Undo(); + + AssertEqualElementHierarchies( false, pTest, pOriginal ); + + g_pDataModel->Redo(); + + AssertUnEqualElementHierarchies( pTest, pOriginal ); + + g_pDataModel->Undo(); + + AssertEqualElementHierarchies( false, pTest, pOriginal ); +} + +template< class T > +void RunSingleAttributeUndoTestArrayCopyFrom( CDmElement *pTest, CDmElement *pOriginal, CDmElement *pElement, const char *attribute, const CUtlVector<T>& list ) +{ + { + CUndoScopeGuard guard( attribute, attribute ); + CDmrArray<T> array( pElement, attribute ); + array.CopyArray( list.Base(), list.Count() ); + } + + AssertUnEqualElementHierarchies( pTest, pOriginal ); + + g_pDataModel->Undo(); + + AssertEqualElementHierarchies( false, pTest, pOriginal ); + + g_pDataModel->Redo(); + + AssertUnEqualElementHierarchies( pTest, pOriginal ); + + g_pDataModel->Undo(); + + AssertEqualElementHierarchies( false, pTest, pOriginal ); +} + + +void RunSingleSetAttributeUndoTestArrayBinary( CDmElement *pTest, CDmElement *pOriginal, CDmElement *pElement, const char *attribute, int slot, const void *data, size_t size ) +{ + { + CUndoScopeGuard guard( attribute, attribute ); + CUtlBinaryBlock block( (const void *)data, size ); + CDmrArray< CUtlBinaryBlock > array( pElement, attribute ); + array.Set( slot, block ); + } + + AssertUnEqualElementHierarchies( pTest, pOriginal ); + + g_pDataModel->Undo(); + + AssertEqualElementHierarchies( false, pTest, pOriginal ); + + g_pDataModel->Redo(); + + AssertUnEqualElementHierarchies( pTest, pOriginal ); + + g_pDataModel->Undo(); + + AssertEqualElementHierarchies( false, pTest, pOriginal ); +} + +template< class T > +void RunSingleSetAttributeUndoTestArrayWhole( CDmElement *pTest, CDmElement *pOriginal, CDmElement *pElement, const char *attribute, CUtlVector< T >& newVal ) +{ + { + CUndoScopeGuard guard( attribute, attribute ); + IDmAttributeArray< T > *pArray = static_cast< IDmAttributeArray< T > * >( pElement->GetAttribute( "attribute" ) ); + pArray->SetValue( newVal ); + } + + AssertUnEqualElementHierarchies( pTest, pOriginal ); + + g_pDataModel->Undo(); + + AssertEqualElementHierarchies( false, pTest, pOriginal ); + + g_pDataModel->Redo(); + + AssertUnEqualElementHierarchies( pTest, pOriginal ); + + g_pDataModel->Undo(); + + AssertEqualElementHierarchies( false, pTest, pOriginal ); +} + +template< class T > +void RunSingleAddAttributeUndoTest( CDmElement *pTest, CDmElement *pOriginal, CDmElement *pElement, const char *attribute, const T& newVal ) +{ + { + CUndoScopeGuard guard( attribute, attribute ); + pElement->SetValue( attribute, newVal ); + } + + AssertUnEqualElementHierarchies( pTest, pOriginal ); + + g_pDataModel->Undo(); + + AssertEqualElementHierarchies( false, pTest, pOriginal ); + + g_pDataModel->Redo(); + + AssertUnEqualElementHierarchies( pTest, pOriginal ); + + g_pDataModel->Undo(); + + AssertEqualElementHierarchies( false, pTest, pOriginal ); +} + +void RunSingleRemoveAttributeUndoTest( CDmElement *pTest, CDmElement *pOriginal, CDmElement *pElement, const char *attribute ) +{ + AssertEqualElementHierarchies( false, pTest, pOriginal ); + + { + CUndoScopeGuard guard( attribute, attribute ); + pElement->RemoveAttribute( attribute ); + } + + AssertUnEqualElementHierarchies( pTest, pOriginal ); + + g_pDataModel->Undo(); + + AssertEqualElementHierarchies( false, pTest, pOriginal ); + + g_pDataModel->Redo(); + + AssertUnEqualElementHierarchies( pTest, pOriginal ); + + g_pDataModel->Undo(); + + AssertEqualElementHierarchies( false, pTest, pOriginal ); +} + + +void RunUndoTestsArray( CUtlVector< DmElementHandle_t >& handles, CDmElement *pTest, CDmElement *pOriginal ) +{ + DmObjectId_t id; + CreateUniqueId( &id ); + + VMatrix mat, mat2; + MatrixBuildRotateZ( mat, 55 ); + MatrixBuildRotateZ( mat2, -55 ); + int i; + unsigned char buf[256]; + unsigned char buf2[256]; + for ( i = 0; i < 256; ++i ) + { + buf[i] = i+55500; + buf2[i] = 55000 + 255 - i; + } + + Assert( handles.Count() == 7 ); + + //CDmElement *pElement = pTest; + CDmElement *pElement2 = GetElement< CDmElement >( handles[ 1 ] ); + CDmElement *pElement3 = GetElement< CDmElement >( handles[ 2 ] ); + CDmElement *pElement4 = GetElement< CDmElement >( handles[ 3 ] ); + CDmElement *pElement5 = GetElement< CDmElement >( handles[ 4 ] ); + CDmElement *pElement6 = GetElement< CDmElement >( handles[ 5 ] ); + CDmElement *pElement7 = GetElement< CDmElement >( handles[ 6 ] ); + + RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement2, "int_array_test", 0, 55 ); + RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement2, "float_array_test", 0, 55.0f ); + RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement2, "bool_array_test", 0, true ); + + RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement3, "color_array_test", 0, Color( 55, 55, 55, 55 ) ); + RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement3, "vector2d_array_test", 0, Vector2D( 55.0f, -55.0f ) ); + RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement3, "vector3d_array_test", 0, Vector( 55.0f, -55.0f, 55.0f ) ); + + RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement4, "vector4d_array_test", 0, Vector4D( 55.0f, -55.0f, 55.0f, 55.0f ) ); + RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement4, "qangle_array_test", 0, QAngle( 55.0f, 55.0f, -55.0f ) ); + RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement4, "quat_array_test", 0, Quaternion( 1.0f, -1.0f, 0.0f, 55.0f ) ); + + RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement5, "vmatrix_array_test", 0, mat ); + CUtlString newString( "55test" ); + RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement5, "string_array_test", 0, newString ); + RunSingleSetAttributeUndoTestArrayBinary( pTest, pOriginal, pElement5, "binary_array_test", 0, buf, 256 ); + +// RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement6, "elementid_array_test", 0, id ); + RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement6, "element_array_test", 0, pElement2->GetHandle() ); + + RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement7, "element_array_test", 0, pElement5->GetHandle() ); + + { + RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement2, "int_array_test", 0, 55 ); + RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement2, "float_array_test", 0, 55.0f ); + RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement2, "bool_array_test", 0, true ); + + RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement3, "color_array_test", 0, Color( 55, 55, 55, 55 ) ); + RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement3, "vector2d_array_test", 0, Vector2D( 55.0f, -55.0f ) ); + RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement3, "vector3d_array_test", 0, Vector( 55.0f, -55.0f, 55.0f ) ); + + RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement4, "vector4d_array_test", 0, Vector4D( 55.0f, -55.0f, 55.0f, 55.0f ) ); + RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement4, "qangle_array_test", 0, QAngle( 55.0f, 55.0f, -55.0f ) ); + RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement4, "quat_array_test", 0, Quaternion( 1.0f, -1.0f, 0.0f, 55.0f ) ); + + RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement5, "vmatrix_array_test", 0, mat ); + CUtlString newString( "55test" ); + RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement5, "string_array_test", 0, newString ); + RunSingleSetAttributeUndoTestArrayBinary( pTest, pOriginal, pElement5, "binary_array_test", 0, buf, 256 ); + +// RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement6, "elementid_array_test", 0, id ); + RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement6, "element_array_test", 0, pElement2->GetHandle() ); + + RunSingleSetAttributeUndoTestArray( pTest, pOriginal, pElement7, "element_array_test", 0, pElement5->GetHandle() ); + } + + RunSingleAttributeUndoTestArrayAddToHead( pTest, pOriginal, pElement2, "int_array_test", 55 ); + RunSingleAttributeUndoTestArrayAddToTail( pTest, pOriginal, pElement2, "int_array_test", 55 ); + RunSingleAttributeUndoTestArrayInsertBefore( pTest, pOriginal, pElement2, "int_array_test", 0, 55 ); + RunSingleAttributeUndoTestArrayFastRemove( pTest, pOriginal, pElement2, "int_array_test", 0, (int)0 ); + RunSingleAttributeUndoTestArrayRemove( pTest, pOriginal, pElement2, "int_array_test", 0, (int)0 ); + RunSingleAttributeUndoTestArrayRemoveMultiple( pTest, pOriginal, pElement2, "int_array_test", 0, 2, (int)0 ); + RunSingleAttributeUndoTestElementArrayRemove( pTest, pOriginal, pElement7, "children", 0 ); + + // RemoveAll + RunSingleAttributeUndoTestArrayRemoveAll<int>( pTest, pOriginal, pElement2, "int_array_test", false ); + // Purge + RunSingleAttributeUndoTestArrayRemoveAll<int>( pTest, pOriginal, pElement2, "int_array_test", true ); + + CUtlVector< int > foo; + foo.AddToTail( 55 ); + foo.AddToTail( 56 ); + foo.AddToTail( 57 ); + foo.AddToTail( 58 ); + foo.AddToTail( 59 ); + + RunSingleAttributeUndoTestArrayCopyFrom( pTest, pOriginal, pElement2, "int_array_test", foo ); +} + +void RunUndoTests( CUtlVector< DmElementHandle_t >& handles, CDmElement *pTest, CDmElement *pOriginal ) +{ + DmObjectId_t id; + CreateUniqueId( &id ); + + VMatrix mat, mat2; + MatrixBuildRotateZ( mat, 55 ); + MatrixBuildRotateZ( mat2, -55 ); + int i; + unsigned char buf[256]; + unsigned char buf2[256]; + for ( i = 0; i < 256; ++i ) + { + buf[i] = i+100; + buf2[i] = 127 + 255 - i; + } + + Assert( handles.Count() == 7 ); + CDmElement *pElement = pTest; + CDmElement *pElement2 = GetElement<CDmElement>( handles[ 1 ] ); + + RunSingleSetAttributeUndoTest( pTest, pOriginal, pElement, "element_test", (CDmElement *)NULL ); + RunSingleSetAttributeUndoTest( pTest, pOriginal, pElement, "int_test", 55 ); + RunSingleSetAttributeUndoTest( pTest, pOriginal, pElement, "float_test", 55.0f ); + RunSingleSetAttributeUndoTest( pTest, pOriginal, pElement, "bool_test", false ); +// RunSingleSetAttributeUndoTest( pTest, pOriginal, pElement, "id_test", id ); + RunSingleSetAttributeUndoTest( pTest, pOriginal, pElement, "string_test", "55test" ); + RunSingleSetAttributeUndoTestBinary( pTest, pOriginal, pElement, "binary_test", buf, 256 ); + RunSingleSetAttributeUndoTest( pTest, pOriginal, pElement, "color_test", Color( 55, 55, 55, 55 ) ); + RunSingleSetAttributeUndoTest( pTest, pOriginal, pElement, "vector2d_test", Vector2D( 55.0f, -55.0f ) ); + RunSingleSetAttributeUndoTest( pTest, pOriginal, pElement, "vector3d_test", Vector( 55.0f, -55.0f, 55.0f ) ); + RunSingleSetAttributeUndoTest( pTest, pOriginal, pElement, "vector4d_test", Vector4D( 55.0f, -55.0f, 55.0f, 55.0f ) ); + RunSingleSetAttributeUndoTest( pTest, pOriginal, pElement, "qangle_test", QAngle( 55.0f, 55.0f, -55.0f ) ); + RunSingleSetAttributeUndoTest( pTest, pOriginal, pElement, "quat_test", Quaternion( 1.0f, -1.0f, 0.0f, 55.0f ) ); + RunSingleSetAttributeUndoTest( pTest, pOriginal, pElement, "vmatrix_test", mat ); + + // Now run a single test with a bunch of operations occurring at the same time + { + { + CUndoScopeGuard guard( "biggish", "smallish" ); + pElement->SetValue( "bool_test", false ); + pElement->SetValue( "int_test", 55 ); + pElement->SetValue( "float_test", 55.0f ); + pElement->SetValue( "color_test", Color( 55, 55, 55, 55 ) ); + pElement->SetValue( "vector2d_test", Vector2D( 55.0f, -55.0f ) ); + pElement->SetValue( "vector3d_test", Vector( 55.0f, -55.0f, 55.0f ) ); + pElement->SetValue( "vector4d_test", Vector4D( 55.0f, -55.0f, 55.0f, 55.0f ) ); + pElement->SetValue( "qangle_test", QAngle( 55.0f, 55.0f, -55.0f ) ); + pElement->SetValue( "quat_test", Quaternion( 1.0f, -1.0f, 0.0f, 55.0f ) ); + pElement->SetValue( "vmatrix_test", mat ); + pElement->SetValue( "string_test", "55test" ); + pElement->SetValue( "binary_test", buf, 256 ); + } + + g_pDataModel->Undo(); + + AssertEqualElementHierarchies( false, pTest, pOriginal ); + + g_pDataModel->Redo(); + g_pDataModel->Undo(); + + AssertEqualElementHierarchies( false, pTest, pOriginal ); + } + + // Now run a test with multiple items in the stack + { + // Push 1 + { + CUndoScopeGuard guard( "biggish1", "smallish1" ); + pElement->SetValue( "bool_test", false ); + pElement->SetValue( "int_test", 55 ); + pElement->SetValue( "float_test", 55.0f ); + pElement->SetValue( "color_test", Color( 55, 55, 55, 55 ) ); + } + // Push 2 + { + CUndoScopeGuard guard( "biggish2", "smallish2" ); + pElement->SetValue( "vector2d_test", Vector2D( 55.0f, -55.0f ) ); + pElement->SetValue( "vector3d_test", Vector( 55.0f, -55.0f, 55.0f ) ); + pElement->SetValue( "vector4d_test", Vector4D( 55.0f, -55.0f, 55.0f, 55.0f ) ); + pElement->SetValue( "qangle_test", QAngle( 55.0f, 55.0f, -55.0f ) ); + pElement->SetValue( "quat_test", Quaternion( 1.0f, -1.0f, 0.0f, 55.0f ) ); + } + // Push 3 + { + CUndoScopeGuard guard( "biggish3", "smallish3" ); + pElement->SetValue( "vmatrix_test", mat ); + pElement->SetValue( "string_test", "55test" ); + pElement->SetValue( "binary_test", buf, 256 ); + } + // Tests + { + g_pDataModel->Undo(); + g_pDataModel->Undo(); + g_pDataModel->Undo(); + + AssertEqualElementHierarchies( false, pTest, pOriginal ); + + g_pDataModel->Redo(); + g_pDataModel->Redo(); + g_pDataModel->Redo(); + + g_pDataModel->Undo(); + g_pDataModel->Undo(); + g_pDataModel->Undo(); + + AssertEqualElementHierarchies( false, pTest, pOriginal ); + } + } + + // Now run "add" tests on a subset -- add adds an attribute where one didn't already exist + RunSingleAddAttributeUndoTest( pTest, pOriginal, pElement2, "int_test_added", 55 ); + + // Now run "remove" tests on a subset of attributes -- removes an existing attribute by name + RunSingleRemoveAttributeUndoTest( pTest, pOriginal, pTest, "int_test" ); +} + +#include "datamodel/dmehandle.h" + +void RunUndoTestCreateElement() +{ + DmFileId_t fileid = g_pDataModel->FindOrCreateFileId( "<RunUndoTestCreateElement>" ); + + CDmElement *element = NULL; + { + CUndoScopeGuard guard( "create" ); + element = CreateElement< CDmElement >( "test", fileid ); + } + + CDmeHandle< CDmElement > hChannel; + + hChannel = element; + + Assert( hChannel.Get() ); + + g_pDataModel->Undo(); + + Assert( !hChannel.Get() ); + + g_pDataModel->Redo(); + + Assert( hChannel.Get() ); + + g_pDataModel->Undo(); // It's on the redo stack, but marked as kill and all ptrs are NULL to it + + Assert( !hChannel.Get() ); + + g_pDataModel->ClearUndo(); + g_pDataModel->ClearRedo(); + + g_pDataModel->RemoveFileId( fileid ); +} + +DEFINE_TESTCASE_NOSUITE( DmxUndoRedoTest ) +{ + Msg( "Running undo tests...\n" ); + +#ifdef _DEBUG + int nStartingCount = g_pDataModel->GetAllocatedElementCount(); +#endif + + CUndoScopeGuard sg( "Create Test Scenes" ); + + DmFileId_t fileid = g_pDataModel->FindOrCreateFileId( "<DmxUndoRedoTest>" ); + CUtlVector< DmElementHandle_t > handles; + CreateTestScene( handles, fileid ); + DmElementHandle_t hOriginal = handles[ 0 ]; + handles.RemoveAll(); + CreateTestScene( handles, fileid ); + DmElementHandle_t hTest = handles[ 0 ]; + + sg.Release(); + + CDmElement *pOriginal = static_cast<CDmElement*>( g_pDataModel->GetElement( hOriginal ) ); + CDmElement *pTest = static_cast<CDmElement*>(g_pDataModel->GetElement( hTest ) ); + + AssertEqualElementHierarchies( false, pTest, pOriginal ); + + RunUndoTests( handles, pTest, pOriginal ); + RunUndoTestsArray( handles, pTest, pOriginal ); + RunUndoTestCreateElement(); + + g_pDataModel->ClearUndo(); + + g_pDataModel->RemoveFileId( fileid ); + +#ifdef _DEBUG + int nEndingCount = g_pDataModel->GetAllocatedElementCount(); + AssertEquals( nEndingCount, nStartingCount ); + if ( nEndingCount != nStartingCount ) + { + for ( DmElementHandle_t hElement = g_pDataModel->FirstAllocatedElement() ; + hElement != DMELEMENT_HANDLE_INVALID; + hElement = g_pDataModel->NextAllocatedElement( hElement ) ) + { + CDmElement *pElement = g_pDataModel->GetElement( hElement ); + Assert( pElement ); + if ( !pElement ) + return; + + Msg( "[%s : %s] in memory\n", pElement->GetName(), pElement->GetTypeString() ); + } + } +#endif +} + +#include "datamodel/dmelementfactoryhelper.h" + +//----------------------------------------------------------------------------- +// CDmeExternal - element class used for testing external attributes +//----------------------------------------------------------------------------- + +class CDmeExternal : public CDmElement +{ + DEFINE_ELEMENT( CDmeExternal, CDmElement ); + +public: + CDmaVar< float > m_External; +}; + +IMPLEMENT_ELEMENT_FACTORY( DmeExternal, CDmeExternal ); + +void CDmeExternal::OnConstruction() +{ + m_External .InitAndSet( this, "external", 99.9f ); +} + +void CDmeExternal::OnDestruction() +{ +} + +USING_ELEMENT_FACTORY( DmeExternal ); + + +DEFINE_TESTCASE_NOSUITE( DmxExternalAttributeTest ) +{ + Msg( "Running external attribute test...\n" ); + + DmFileId_t fileid = g_pDataModel->FindOrCreateFileId( "<DmxExternalAttributeTest>" ); + + CDmeExternal *pOriginal = 0; + +#ifdef _DEBUG + int nStartingCount = g_pDataModel->GetAllocatedElementCount(); +#endif + + { + CUndoScopeGuard guard( "External" ); + pOriginal = CreateElement<CDmeExternal>( "foo", fileid ); + + for ( int m = 0; m < 1; ++m ) + { + CDmeExternal *discard = CreateElement<CDmeExternal>( "foo", fileid ); + g_pDataModel->DestroyElement( discard->GetHandle() ); + } + } + + // Now mess with vars + + g_pDataModel->Undo(); + g_pDataModel->Redo(); + + { + CUndoScopeGuard guard( "External2" ); + pOriginal->m_External.Set( 100.0f ); + } + + g_pDataModel->ClearUndo(); + + g_pDataModel->RemoveFileId( fileid ); + +#ifdef _DEBUG + int nEndingCount = g_pDataModel->GetAllocatedElementCount(); + AssertEquals( nEndingCount, nStartingCount ); + if ( nEndingCount != nStartingCount ) + { + for ( DmElementHandle_t hElement = g_pDataModel->FirstAllocatedElement() ; + hElement != DMELEMENT_HANDLE_INVALID; + hElement = g_pDataModel->NextAllocatedElement( hElement ) ) + { + CDmElement *pElement = g_pDataModel->GetElement( hElement ); + Assert( pElement ); + if ( !pElement ) + return; + + Msg( "[%s : %s] in memory\n", pElement->GetName(), pElement->GetTypeString() ); + } + } +#endif +} + +void Assert_InvalidAndNULL( CDmeHandle< CDmElement > &hElement ) +{ + Shipping_Assert( !hElement.Get() ); + if ( !hElement.Get() ) + { + g_pDataModel->MarkHandleValid( hElement.GetHandle() ); + Shipping_Assert( !hElement.Get() ); + if ( hElement.Get() ) + { + g_pDataModel->MarkHandleInvalid( hElement.GetHandle() ); + } + } +} + +void Assert_InvalidButNonNULL( CDmeHandle< CDmElement > &hElement ) +{ + Shipping_Assert( !hElement.Get() ); + if ( !hElement.Get() ) + { + g_pDataModel->MarkHandleValid( hElement.GetHandle() ); + Shipping_Assert( hElement.Get() ); + if ( hElement.Get() ) + { + g_pDataModel->MarkHandleInvalid( hElement.GetHandle() ); + } + } +} + +void Assert_ValidAndNonNULL( CDmeHandle< CDmElement > &hElement ) +{ + Shipping_Assert( hElement.Get() ); +} + +DEFINE_TESTCASE_NOSUITE( DmxReferenceCountingTest ) +{ + Msg( "Running external attribute test...\n" ); + + DmFileId_t fileid = g_pDataModel->FindOrCreateFileId( "<DmxReferenceCountingTest>" ); + + CDmeHandle< CDmElement > hRoot; + CDmeHandle< CDmElement > hChild1, hChild2, hChild3, hChild4, hChild5; + CDmeHandle< CDmElement > hGrandChild1, hGrandChild2, hGrandChild3, hGrandChild4, hGrandChild5, hGrandChild6, hGrandChild7; + CDmeCountedHandle hStrong6, hStrong7; + + g_pDmElementFramework->BeginEdit(); + + int nStartingCount = g_pDataModel->GetAllocatedElementCount(); + Shipping_Assert( nStartingCount == 0 ); + + { + CUndoScopeGuard guard( "Create RefCountTest Elements" ); + + hRoot = CreateElement<CDmElement>( "root", fileid ); + CDmrElementArray<> arrayRoot( hRoot, "children", true ); + g_pDataModel->SetFileRoot( fileid, hRoot->GetHandle() ); + + hChild1 = CreateElement<CDmElement>( "child1", fileid ); + CDmrElementArray<> arrayChild1( hChild1, "children", true ); + arrayRoot.AddToTail( hChild1.Get() ); + + hChild2 = CreateElement<CDmElement>( "child2", fileid ); + hChild2->AddAttributeElement<CDmElement>( "child" ); + arrayRoot.AddToTail( hChild2.Get() ); + + hChild3 = CreateElement<CDmElement>( "child3", fileid ); + hChild3->AddAttributeElement<CDmElement>( "child" ); + arrayRoot.AddToTail( hChild3.Get() ); + + hChild4 = CreateElement<CDmElement>( "child4", fileid ); + hChild4->AddAttributeElement<CDmElement>( "child" ); + arrayRoot.AddToTail( hChild4.Get() ); + + hChild5 = CreateElement<CDmElement>( "child5", fileid ); + CDmrElementArray<> arrayChild5( hChild5, "children", true ); + arrayRoot.AddToTail( hChild5.Get() ); + + hGrandChild1 = CreateElement<CDmElement>( "grandchild1", fileid ); + arrayChild1.AddToTail( hGrandChild1.Get() ); + + hGrandChild2 = CreateElement<CDmElement>( "grandchild2", fileid ); + arrayChild1.AddToTail( hGrandChild2.Get() ); + hChild2->SetValue( "child", hGrandChild2.GetHandle() ); + + hGrandChild3 = CreateElement<CDmElement>( "grandchild3", fileid ); + arrayChild1.AddToTail( hGrandChild3.Get() ); + hChild3->SetValue( "child", hGrandChild3.GetHandle() ); + + hGrandChild4 = CreateElement<CDmElement>( "grandchild4", fileid ); + arrayChild1.AddToTail( hGrandChild4.Get() ); + hChild4->SetValue( "child", hGrandChild4.GetHandle() ); + + hGrandChild5 = CreateElement<CDmElement>( "grandchild5", fileid ); + arrayChild1.AddToTail( hGrandChild5.Get() ); + arrayChild5.AddToTail( hGrandChild5.Get() ); + + hGrandChild6 = CreateElement<CDmElement>( "grandchild6", fileid ); + arrayChild1.AddToTail( hGrandChild6.Get() ); + hStrong6 = hGrandChild6; + + hGrandChild7 = CreateElement<CDmElement>( "grandchild7", fileid ); + arrayChild1.AddToTail( hGrandChild7.Get() ); + hStrong7 = hGrandChild7; + } + + g_pDmElementFramework->BeginEdit(); + + int nCreateCount = g_pDataModel->GetAllocatedElementCount(); + Assert( nCreateCount == nStartingCount + 13 ); + + Assert_ValidAndNonNULL ( hRoot ); + Assert_ValidAndNonNULL ( hChild1 ); + Assert_ValidAndNonNULL ( hChild2 ); + Assert_ValidAndNonNULL ( hChild3 ); + Assert_ValidAndNonNULL ( hChild4 ); + Assert_ValidAndNonNULL ( hChild5 ); + Assert_ValidAndNonNULL ( hGrandChild1 ); + Assert_ValidAndNonNULL ( hGrandChild2 ); + Assert_ValidAndNonNULL ( hGrandChild3 ); + Assert_ValidAndNonNULL ( hGrandChild4 ); + Assert_ValidAndNonNULL ( hGrandChild5 ); + Assert_ValidAndNonNULL ( hGrandChild6 ); + Assert_ValidAndNonNULL ( hGrandChild7 ); + + g_pDataModel->ClearUndo(); + + g_pDmElementFramework->BeginEdit(); + + int nPostCreateCount = g_pDataModel->GetAllocatedElementCount(); + Shipping_Assert( nPostCreateCount == nCreateCount ); + + Assert_ValidAndNonNULL ( hRoot ); + Assert_ValidAndNonNULL ( hChild1 ); + Assert_ValidAndNonNULL ( hChild2 ); + Assert_ValidAndNonNULL ( hChild3 ); + Assert_ValidAndNonNULL ( hChild4 ); + Assert_ValidAndNonNULL ( hChild5 ); + Assert_ValidAndNonNULL ( hGrandChild1 ); + Assert_ValidAndNonNULL ( hGrandChild2 ); + Assert_ValidAndNonNULL ( hGrandChild3 ); + Assert_ValidAndNonNULL ( hGrandChild4 ); + Assert_ValidAndNonNULL ( hGrandChild5 ); + Assert_ValidAndNonNULL ( hGrandChild6 ); + Assert_ValidAndNonNULL ( hGrandChild7 ); + + { + CUndoScopeGuard guard( "Edit RefCountTest Elements" ); + + CDmrGenericArray children( hChild1, "children" ); + children.RemoveAll(); // grandchild1 unref'ed to 0, grandchild2..6 unref'ed to 1 + + hChild2->SetValue( "child", DMELEMENT_HANDLE_INVALID ); // grandchild2 unref'ed to 0 + + g_pDataModel->DestroyElement( hChild3 ); // grandchild3 is unref'ed to 0 + + CDmrElementArray<> children5( hChild5, "children" ); + children5.Set( 0, NULL ); // grandchild5 is unref'ed to 0 + + hStrong6 = DMELEMENT_HANDLE_INVALID; // grandchild6 is unref'ed to 0 + } + + g_pDmElementFramework->BeginEdit(); + + int nEditCount = g_pDataModel->GetAllocatedElementCount(); + Shipping_Assert( nEditCount == nCreateCount - 1 ); + + Assert_ValidAndNonNULL ( hRoot ); + Assert_ValidAndNonNULL ( hChild1 ); + Assert_ValidAndNonNULL ( hChild2 ); + Assert_InvalidButNonNULL( hChild3 ); + Assert_ValidAndNonNULL ( hChild4 ); + Assert_ValidAndNonNULL ( hChild5 ); + Assert_ValidAndNonNULL ( hGrandChild1 ); + Assert_ValidAndNonNULL ( hGrandChild2 ); + Assert_ValidAndNonNULL ( hGrandChild3 ); + Assert_ValidAndNonNULL ( hGrandChild4 ); + Assert_ValidAndNonNULL ( hGrandChild5 ); + Assert_ValidAndNonNULL ( hGrandChild6 ); + Assert_ValidAndNonNULL ( hGrandChild7 ); + + g_pDataModel->Undo(); + + g_pDmElementFramework->BeginEdit(); + + int nUndoCount = g_pDataModel->GetAllocatedElementCount(); + Shipping_Assert( nUndoCount == nCreateCount ); + + Assert_ValidAndNonNULL ( hRoot ); + Assert_ValidAndNonNULL ( hChild1 ); + Assert_ValidAndNonNULL ( hChild2 ); + Assert_ValidAndNonNULL ( hChild3 ); + Assert_ValidAndNonNULL ( hChild4 ); + Assert_ValidAndNonNULL ( hChild5 ); + Assert_ValidAndNonNULL ( hGrandChild1 ); + Assert_ValidAndNonNULL ( hGrandChild2 ); + Assert_ValidAndNonNULL ( hGrandChild3 ); + Assert_ValidAndNonNULL ( hGrandChild4 ); + Assert_ValidAndNonNULL ( hGrandChild5 ); + Assert_ValidAndNonNULL ( hGrandChild6 ); + Assert_ValidAndNonNULL ( hGrandChild7 ); + + g_pDataModel->Redo(); + + g_pDmElementFramework->BeginEdit(); + + int nRedoCount = g_pDataModel->GetAllocatedElementCount(); + Shipping_Assert( nRedoCount == nEditCount ); + + Assert_ValidAndNonNULL ( hRoot ); + Assert_ValidAndNonNULL ( hChild1 ); + Assert_ValidAndNonNULL ( hChild2 ); + Assert_InvalidButNonNULL( hChild3 ); + Assert_ValidAndNonNULL ( hChild4 ); + Assert_ValidAndNonNULL ( hChild5 ); + Assert_ValidAndNonNULL ( hGrandChild1 ); + Assert_ValidAndNonNULL ( hGrandChild2 ); + Assert_ValidAndNonNULL ( hGrandChild3 ); + Assert_ValidAndNonNULL ( hGrandChild4 ); + Assert_ValidAndNonNULL ( hGrandChild5 ); + Assert_ValidAndNonNULL ( hGrandChild6 ); + Assert_ValidAndNonNULL ( hGrandChild7 ); + + g_pDataModel->ClearUndo(); + + g_pDmElementFramework->BeginEdit(); + + int nClearUndoCount = g_pDataModel->GetAllocatedElementCount(); + Shipping_Assert( nClearUndoCount == nCreateCount - 6 ); + + Assert_ValidAndNonNULL ( hRoot ); + Assert_ValidAndNonNULL ( hChild1 ); + Assert_ValidAndNonNULL ( hChild2 ); + Assert_InvalidAndNULL ( hChild3 ); + Assert_ValidAndNonNULL ( hChild4 ); + Assert_ValidAndNonNULL ( hChild5 ); + Assert_InvalidAndNULL ( hGrandChild1 ); + Assert_InvalidAndNULL ( hGrandChild2 ); + Assert_InvalidAndNULL ( hGrandChild3 ); + Assert_ValidAndNonNULL ( hGrandChild4 ); + Assert_InvalidAndNULL ( hGrandChild5 ); + Assert_InvalidAndNULL ( hGrandChild6 ); + Assert_ValidAndNonNULL ( hGrandChild7 ); + + g_pDataModel->RemoveFileId( fileid ); +} diff --git a/unittests/dmxtest/dmxtestvalue.cpp b/unittests/dmxtest/dmxtestvalue.cpp new file mode 100644 index 0000000..df20e10 --- /dev/null +++ b/unittests/dmxtest/dmxtestvalue.cpp @@ -0,0 +1,104 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Unit test program for DMX testing (testing the single-value operations) +// +// $NoKeywords: $ +//=============================================================================// + +#include "unitlib/unitlib.h" +#include "datamodel/dmelement.h" +#include "datamodel/idatamodel.h" +#include "tier1/utlbuffer.h" +#include "filesystem.h" +#include "datamodel/dmehandle.h" +#include "tier2/tier2.h" +#include "movieobjects/dmeshape.h" + + +DEFINE_TESTCASE_NOSUITE( DmxValueTest ) +{ + Msg( "Running dmx single value tests...\n" ); + + CDisableUndoScopeGuard sg; + DmFileId_t fileid = g_pDataModel->FindOrCreateFileId( "<RunValueTests>" ); + + CDmElement *pElement = CreateElement< CDmElement >( "root", fileid ); + + CDmElement *pElement2 = CreateElement<CDmElement>( "element1", fileid ); + Assert( pElement2 ); + CDmElement *pElement3 = CreateElement<CDmElement>( "element2", fileid ); + Assert( pElement3 ); + CDmeShape *pElement4 = CreateElement<CDmeShape>( "shape", fileid ); + Assert( pElement4 ); + + CDmAttribute *pIntAttribute = pElement->SetValue( "int_test", 5 ); + CDmAttribute *pFloatAttribute = pElement->SetValue( "float_test", 4.5f ); + CDmAttribute *pBoolAttribute = pElement->SetValue( "bool_test", true ); + + CDmAttribute *pAttribute = pElement->AddAttribute( "int_convert_test", AT_INT ); + + // Type conversion set test + pAttribute->SetValue( 5 ); + Shipping_Assert( pAttribute->GetValue<int>() == 5 ); + pAttribute->SetValue( 4.5f ); + Shipping_Assert( pAttribute->GetValue<int>() == 4 ); + pAttribute->SetValue( true ); + Shipping_Assert( pAttribute->GetValue<int>() == 1 ); + pAttribute->SetValue( pIntAttribute ); + Shipping_Assert( pAttribute->GetValue<int>() == 5 ); + pAttribute->SetValue( pFloatAttribute ); + Shipping_Assert( pAttribute->GetValue<int>() == 4 ); + pAttribute->SetValue( pBoolAttribute ); + Shipping_Assert( pAttribute->GetValue<int>() == 1 ); + + pAttribute = pElement->AddAttribute( "bool_convert_test", AT_BOOL ); + + // Type conversion set test + pAttribute->SetValue( 5 ); + Shipping_Assert( pAttribute->GetValue<bool>() == true ); + pAttribute->SetValue( 4.5f ); + Shipping_Assert( pAttribute->GetValue<bool>() == true ); + pAttribute->SetValue( false ); + Shipping_Assert( pAttribute->GetValue<bool>() == false ); + pAttribute->SetValue( pIntAttribute ); + Shipping_Assert( pAttribute->GetValue<bool>() == true ); + pAttribute->SetValue( pFloatAttribute ); + Shipping_Assert( pAttribute->GetValue<bool>() == true ); + pAttribute->SetValue( pBoolAttribute ); + Shipping_Assert( pAttribute->GetValue<bool>() == true ); + + pAttribute = pElement->AddAttribute( "float_convert_test", AT_FLOAT ); + + // Type conversion set test + pAttribute->SetValue( 5 ); + Shipping_Assert( pAttribute->GetValue<float>() == 5.0f ); + pAttribute->SetValue( 4.5f ); + Shipping_Assert( pAttribute->GetValue<float>() == 4.5f ); + pAttribute->SetValue( true ); + Shipping_Assert( pAttribute->GetValue<float>() == 1.0f ); + pAttribute->SetValue( pIntAttribute ); + Shipping_Assert( pAttribute->GetValue<float>() == 5.0f ); + pAttribute->SetValue( pFloatAttribute ); + Shipping_Assert( pAttribute->GetValue<float>() == 4.5f ); + pAttribute->SetValue( pBoolAttribute ); + Shipping_Assert( pAttribute->GetValue<float>() == 1.0f ); + + // Type conversion set test + QAngle angles( 90, 0, 0 ); + Quaternion quat; + AngleQuaternion( angles, quat ); + + pAttribute = pElement->AddAttribute( "qangle_convert_test", AT_QANGLE ); + pAttribute->SetValue( angles ); + Shipping_Assert( pAttribute->GetValue<QAngle>() == angles ); + pAttribute->SetValue( quat ); + Shipping_Assert( pAttribute->GetValue<QAngle>() == angles ); + + pAttribute = pElement->AddAttribute( "quat_convert_test", AT_QUATERNION ); + pAttribute->SetValue( angles ); + Shipping_Assert( pAttribute->GetValue<Quaternion>() == quat ); + pAttribute->SetValue( quat ); + Shipping_Assert( pAttribute->GetValue<Quaternion>() == quat ); + + g_pDataModel->RemoveFileId( fileid ); +} |