summaryrefslogtreecommitdiff
path: root/unittests/dmxtest
diff options
context:
space:
mode:
Diffstat (limited to 'unittests/dmxtest')
-rw-r--r--unittests/dmxtest/dmxtest.cpp45
-rw-r--r--unittests/dmxtest/dmxtest.vpc57
-rw-r--r--unittests/dmxtest/dmxtest_dmeloglayers.cpp908
-rw-r--r--unittests/dmxtest/dmxtest_vcdtodme.cpp110
-rw-r--r--unittests/dmxtest/dmxtestarray.cpp76
-rw-r--r--unittests/dmxtest/dmxtestdmelog.cpp393
-rw-r--r--unittests/dmxtest/dmxtestloader.cpp211
-rw-r--r--unittests/dmxtest/dmxtestnotify.cpp111
-rw-r--r--unittests/dmxtest/dmxtestserialization.cpp760
-rw-r--r--unittests/dmxtest/dmxtestundoredo.cpp1096
-rw-r--r--unittests/dmxtest/dmxtestvalue.cpp104
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 );
+}