#include <ebcl/Types.hh>
#include <cppunit/extensions/HelperMacros.h>
using namespace ebcl;


class VariantTest : public CppUnit::TestFixture
{
	CPPUNIT_TEST_SUITE( VariantTest );
		CPPUNIT_TEST( testEmpty );

		CPPUNIT_TEST( testValueCopyCons );
		CPPUNIT_TEST( testValueMoveCons );
		CPPUNIT_TEST( testValueCopyAss );
		CPPUNIT_TEST( testValueMoveAss );
		CPPUNIT_TEST( testValueNoMoveCons );
		CPPUNIT_TEST( testValueNoMoveAss );

		CPPUNIT_TEST( testLargeCopyCons );
		CPPUNIT_TEST( testLargeMoveCons );
		CPPUNIT_TEST( testLargeCopyAss );
		CPPUNIT_TEST( testLargeMoveAss );

		CPPUNIT_TEST( testLIPCopyCons );
		CPPUNIT_TEST( testLIPMoveCons );
		CPPUNIT_TEST( testLIPCopyAss );
		CPPUNIT_TEST( testLIPMoveAss );

		CPPUNIT_TEST( testValueRead );
		CPPUNIT_TEST( testLargeRead );
		CPPUNIT_TEST( testLIPRead );
		CPPUNIT_TEST( testValueReadBadType );
		CPPUNIT_TEST( testValueWrite );
		CPPUNIT_TEST( testLargeWrite );
		CPPUNIT_TEST( testLIPWrite );
		CPPUNIT_TEST( testValueWriteBadType );

		CPPUNIT_TEST( testCopyConsSmall );
		CPPUNIT_TEST( testCopyConsLarge );
		CPPUNIT_TEST( testCopyConsLIP );
		CPPUNIT_TEST( testCopyAssSmall );
		CPPUNIT_TEST( testCopyAssLarge );
		CPPUNIT_TEST( testCopyAssLIP );
		CPPUNIT_TEST( testMoveConsSmall );
		CPPUNIT_TEST( testMoveConsLarge );
		CPPUNIT_TEST( testMoveConsLIP );
		CPPUNIT_TEST( testMoveAssSmall );
		CPPUNIT_TEST( testMoveAssLarge );
		CPPUNIT_TEST( testMoveAssLIP );

		CPPUNIT_TEST( testSwapSS );
		CPPUNIT_TEST( testSwapLL );
		CPPUNIT_TEST( testSwapSL );
		CPPUNIT_TEST( testSwapES );
		CPPUNIT_TEST( testSwapEL );

		CPPUNIT_TEST( testSwapLIPSS );
		CPPUNIT_TEST( testSwapLIPLL );
		CPPUNIT_TEST( testSwapLIPSL );
		CPPUNIT_TEST( testSwapLIPES );
		CPPUNIT_TEST( testSwapLIPEL );

		CPPUNIT_TEST( testCopyConsLIPToSmall );
		CPPUNIT_TEST( testCopyConsLIPToLarge );
		CPPUNIT_TEST( testCopyConsSmallToLIP );
		CPPUNIT_TEST( testCopyConsLargeToLIP );
		CPPUNIT_TEST( testMoveConsLIPToSmall );
		CPPUNIT_TEST( testMoveConsLIPToLarge );
		CPPUNIT_TEST( testMoveConsSmallToLIP );
		CPPUNIT_TEST( testMoveConsLargeToLIP );
		CPPUNIT_TEST( testCopyAssLIPToSmall );
		CPPUNIT_TEST( testCopyAssLIPToLarge );
		CPPUNIT_TEST( testCopyAssSmallToLIP );
		CPPUNIT_TEST( testCopyAssLargeToLIP );
		CPPUNIT_TEST( testMoveAssLIPToSmall );
		CPPUNIT_TEST( testMoveAssLIPToLarge );
		CPPUNIT_TEST( testMoveAssSmallToLIP );
		CPPUNIT_TEST( testMoveAssLargeToLIP );
	CPPUNIT_TEST_SUITE_END( );

public:
	void tearDown( ) override;

	void testEmpty( );

	void testValueCopyCons( );
	void testValueMoveCons( );
	void testValueCopyAss( );
	void testValueMoveAss( );
	void testValueNoMoveCons( );
	void testValueNoMoveAss( );

	void testLargeCopyCons( );
	void testLargeMoveCons( );
	void testLargeCopyAss( );
	void testLargeMoveAss( );

	void testLIPCopyCons( );
	void testLIPMoveCons( );
	void testLIPCopyAss( );
	void testLIPMoveAss( );

	void testValueRead( );
	void testLargeRead( );
	void testLIPRead( );
	void testValueReadBadType( );
	void testValueWrite( );
	void testLargeWrite( );
	void testLIPWrite( );
	void testValueWriteBadType( );

	void testCopyConsSmall( );
	void testCopyConsLarge( );
	void testCopyConsLIP( );
	void testCopyAssSmall( );
	void testCopyAssLarge( );
	void testCopyAssLIP( );
	void testMoveConsSmall( );
	void testMoveConsLarge( );
	void testMoveConsLIP( );
	void testMoveAssSmall( );
	void testMoveAssLarge( );
	void testMoveAssLIP( );

	void testSwapSS( );
	void testSwapLL( );
	void testSwapSL( );
	void testSwapES( );
	void testSwapEL( );

	void testSwapLIPSS( );
	void testSwapLIPLL( );
	void testSwapLIPSL( );
	void testSwapLIPES( );
	void testSwapLIPEL( );

	void testCopyConsLIPToSmall( );
	void testCopyConsLIPToLarge( );
	void testCopyConsSmallToLIP( );
	void testCopyConsLargeToLIP( );
	void testMoveConsLIPToSmall( );
	void testMoveConsLIPToLarge( );
	void testMoveConsSmallToLIP( );
	void testMoveConsLargeToLIP( );
	void testCopyAssLIPToSmall( );
	void testCopyAssLIPToLarge( );
	void testCopyAssSmallToLIP( );
	void testCopyAssLargeToLIP( );
	void testMoveAssLIPToSmall( );
	void testMoveAssLIPToLarge( );
	void testMoveAssSmallToLIP( );
	void testMoveAssLargeToLIP( );
};
CPPUNIT_TEST_SUITE_REGISTRATION( VariantTest );

/*----------------------------------------------------------------------------*/

namespace {

struct T_Base
{
	static int copies;
	static int moves;
	static void reset( ) { copies = moves = 0; }
};

int T_Base::copies = 0;
int T_Base::moves = 0;

struct T_Obj : public T_Base
{
	uint32_t v;
	T_Obj( ) : T_Obj( 0 ) { }
	T_Obj( uint32_t v ) : v( v ) { }
	T_Obj( T_Obj const& o ) : T_Obj( o.v ) { copies ++; }
	T_Obj& operator =( T_Obj const& o ) { v = o.v; copies ++; return *this; }
	T_Obj( T_Obj&& o ) noexcept : T_Obj( o.v ) { moves ++; }
	T_Obj& operator =( T_Obj&& o ) noexcept { v = o.v; moves ++; return *this; }
};

struct T_ObjCopy : public T_Base
{
	uint32_t v;
	T_ObjCopy( ) : T_ObjCopy( 0 ) { }
	T_ObjCopy( uint32_t v ) : v( v ) { }
	T_ObjCopy( T_ObjCopy const& o ) : T_ObjCopy( o.v ) { copies ++; }
	T_ObjCopy& operator =( T_ObjCopy const& o ) { v = o.v; copies ++; return *this; }
	T_ObjCopy( T_ObjCopy&& o ) noexcept = delete;
	T_ObjCopy& operator =( T_ObjCopy&& o ) noexcept = delete;
};

struct T_ObjLarge : public T_Base
{
	uint32_t v[ 32 ];
	T_ObjLarge( ) : T_ObjLarge( 0 ) { }

	T_ObjLarge( uint32_t value )
	{
		for ( uint32_t i = 0 ; i < sizeof( v ) / sizeof( uint32_t ) ; i ++ ) {
			v[ i ] = value;
		}
	}

	T_ObjLarge( T_ObjLarge const& o ) : T_ObjLarge( o.v[ 0 ] ) { copies ++; }
	T_ObjLarge& operator =( T_ObjLarge const& o )
	{
		memcpy( v , o.v , sizeof( v ) );
		copies ++;
		return *this;
	}

	T_ObjLarge( T_ObjLarge&& o ) noexcept : T_ObjLarge( o.v[ 0 ] ) { moves ++; }
	T_ObjLarge& operator =( T_ObjLarge&& o ) noexcept
	{
		memcpy( v , o.v , sizeof( v ) );
		moves ++;
		return *this;
	}
};

static_assert( sizeof( T_Obj ) <= sizeof( void* ) && alignof( T_Obj ) <= alignof( void * ) ,
		"T_Obj would not be stored in-place" );
static_assert( sizeof( T_ObjCopy ) <= sizeof( void* ) && alignof( T_ObjCopy ) <= alignof( void * ) ,
		"T_ObjCopy would not be stored in-place" );
static_assert( sizeof( T_ObjLarge ) > sizeof( void* ) || alignof( T_ObjLarge ) > alignof( void * ) ,
		"T_ObjLarge would be stored in-place" );

using T_LIP_ = T_VariantExt< sizeof( T_ObjLarge ) >;

template< typename Type , size_t S >
bool IsInPlace( T_VariantExt< S > const* container ,
		Type const* contained ) noexcept
{
	char const* cStart( (char const*) container );
	char const* cEnd( cStart + sizeof( T_VariantExt< S > ) );
	char const* oStart( (char const*) contained );
	char const* oEnd( oStart + sizeof( Type ) );
	return oStart >= cStart && oEnd <= cEnd;
}

#define M_INPLACE( Container , Type ) \
	IsInPlace( &Container , &( Container.value< Type >( ) ) )

}

void VariantTest::tearDown( )
{
	T_Base::reset( );
}

/*----------------------------------------------------------------------------*/

void VariantTest::testEmpty( )
{
	T_Variant test;
	CPPUNIT_ASSERT( !bool( test ) );
	CPPUNIT_ASSERT( !test );
	CPPUNIT_ASSERT( test.typeInfo( ) == typeid( void ) );
}

/*----------------------------------------------------------------------------*/

void VariantTest::testValueCopyCons( )
{
	T_Obj init( 12 );
	T_Variant test( init );
	CPPUNIT_ASSERT( bool( test ) );
	CPPUNIT_ASSERT( !( !test ) );
	CPPUNIT_ASSERT( test.typeInfo( ) == typeid( T_Obj ) );
	CPPUNIT_ASSERT( M_INPLACE( test , T_Obj ) );
	CPPUNIT_ASSERT_EQUAL( 1 , T_Base::copies );
	CPPUNIT_ASSERT_EQUAL( 0 , T_Base::moves );
}

void VariantTest::testValueMoveCons( )
{
	T_Variant test( T_Obj( 12 ) );
	CPPUNIT_ASSERT( bool( test ) );
	CPPUNIT_ASSERT( !( !test ) );
	CPPUNIT_ASSERT( test.typeInfo( ) == typeid( T_Obj ) );
	CPPUNIT_ASSERT( M_INPLACE( test , T_Obj ) );
	CPPUNIT_ASSERT_EQUAL( 0 , T_Base::copies );
	CPPUNIT_ASSERT_EQUAL( 1 , T_Base::moves );
}

void VariantTest::testValueCopyAss( )
{
	T_Obj init( 12 );
	T_Variant test;
	test = init;
	CPPUNIT_ASSERT( bool( test ) );
	CPPUNIT_ASSERT( !( !test ) );
	CPPUNIT_ASSERT( test.typeInfo( ) == typeid( T_Obj ) );
	CPPUNIT_ASSERT( M_INPLACE( test , T_Obj ) );
	CPPUNIT_ASSERT_EQUAL( 1 , T_Base::copies );
	CPPUNIT_ASSERT_EQUAL( 0 , T_Base::moves );
}

void VariantTest::testValueMoveAss( )
{
	T_Variant test;
	test = T_Obj( 12 );
	CPPUNIT_ASSERT( bool( test ) );
	CPPUNIT_ASSERT( !( !test ) );
	CPPUNIT_ASSERT( test.typeInfo( ) == typeid( T_Obj ) );
	CPPUNIT_ASSERT( M_INPLACE( test , T_Obj ) );
	CPPUNIT_ASSERT_EQUAL( 0 , T_Base::copies );
	CPPUNIT_ASSERT_EQUAL( 1 , T_Base::moves );
}

void VariantTest::testValueNoMoveCons( )
{
	T_Variant test( T_ObjCopy( 12 ) );
	CPPUNIT_ASSERT( bool( test ) );
	CPPUNIT_ASSERT( !( !test ) );
	CPPUNIT_ASSERT( test.typeInfo( ) == typeid( T_ObjCopy ) );
	CPPUNIT_ASSERT( !M_INPLACE( test , T_ObjCopy ) );
	CPPUNIT_ASSERT_EQUAL( 1 , T_Base::copies );
	CPPUNIT_ASSERT_EQUAL( 0 , T_Base::moves );
}

void VariantTest::testValueNoMoveAss( )
{
	T_Variant test;
	test = T_ObjCopy( 12 );
	CPPUNIT_ASSERT( bool( test ) );
	CPPUNIT_ASSERT( !( !test ) );
	CPPUNIT_ASSERT( test.typeInfo( ) == typeid( T_ObjCopy ) );
	CPPUNIT_ASSERT( !M_INPLACE( test , T_ObjCopy ) );
	CPPUNIT_ASSERT_EQUAL( 1 , T_Base::copies );
	CPPUNIT_ASSERT_EQUAL( 0 , T_Base::moves );
}

/*----------------------------------------------------------------------------*/

void VariantTest::testLargeCopyCons( )
{
	T_ObjLarge init( 12 );
	T_Variant test( init );
	CPPUNIT_ASSERT( bool( test ) );
	CPPUNIT_ASSERT( !( !test ) );
	CPPUNIT_ASSERT( test.typeInfo( ) == typeid( T_ObjLarge ) );
	CPPUNIT_ASSERT( !M_INPLACE( test , T_ObjLarge ) );
	CPPUNIT_ASSERT_EQUAL( 1 , T_Base::copies );
	CPPUNIT_ASSERT_EQUAL( 0 , T_Base::moves );
}

void VariantTest::testLargeMoveCons( )
{
	T_Variant test( T_ObjLarge( 12 ) );
	CPPUNIT_ASSERT( bool( test ) );
	CPPUNIT_ASSERT( !( !test ) );
	CPPUNIT_ASSERT( test.typeInfo( ) == typeid( T_ObjLarge ) );
	CPPUNIT_ASSERT( !M_INPLACE( test , T_ObjLarge ) );
	CPPUNIT_ASSERT_EQUAL( 0 , T_Base::copies );
	CPPUNIT_ASSERT_EQUAL( 1 , T_Base::moves );
}

void VariantTest::testLargeCopyAss( )
{
	T_ObjLarge init( 12 );
	T_Variant test;
	test = init;
	CPPUNIT_ASSERT( bool( test ) );
	CPPUNIT_ASSERT( !( !test ) );
	CPPUNIT_ASSERT( test.typeInfo( ) == typeid( T_ObjLarge ) );
	CPPUNIT_ASSERT( !M_INPLACE( test , T_ObjLarge ) );
	CPPUNIT_ASSERT_EQUAL( 1 , T_Base::copies );
	CPPUNIT_ASSERT_EQUAL( 0 , T_Base::moves );
}

void VariantTest::testLargeMoveAss( )
{
	T_Variant test;
	test = T_ObjLarge( 12 );
	CPPUNIT_ASSERT( bool( test ) );
	CPPUNIT_ASSERT( !( !test ) );
	CPPUNIT_ASSERT( test.typeInfo( ) == typeid( T_ObjLarge ) );
	CPPUNIT_ASSERT( !M_INPLACE( test , T_ObjLarge ) );
	CPPUNIT_ASSERT_EQUAL( 0 , T_Base::copies );
	CPPUNIT_ASSERT_EQUAL( 1 , T_Base::moves );
}

/*----------------------------------------------------------------------------*/

void VariantTest::testLIPCopyCons( )
{
	T_ObjLarge init( 12 );
	T_LIP_ test( init );
	CPPUNIT_ASSERT( bool( test ) );
	CPPUNIT_ASSERT( !( !test ) );
	CPPUNIT_ASSERT( test.typeInfo( ) == typeid( T_ObjLarge ) );
	CPPUNIT_ASSERT( M_INPLACE( test , T_ObjLarge ) );
	CPPUNIT_ASSERT_EQUAL( 1 , T_Base::copies );
	CPPUNIT_ASSERT_EQUAL( 0 , T_Base::moves );
}

void VariantTest::testLIPMoveCons( )
{
	T_LIP_ test( T_ObjLarge( 12 ) );
	CPPUNIT_ASSERT( bool( test ) );
	CPPUNIT_ASSERT( !( !test ) );
	CPPUNIT_ASSERT( test.typeInfo( ) == typeid( T_ObjLarge ) );
	CPPUNIT_ASSERT( M_INPLACE( test , T_ObjLarge ) );
	CPPUNIT_ASSERT_EQUAL( 0 , T_Base::copies );
	CPPUNIT_ASSERT_EQUAL( 1 , T_Base::moves );
}

void VariantTest::testLIPCopyAss( )
{
	T_ObjLarge init( 12 );
	T_LIP_ test;
	test = init;
	CPPUNIT_ASSERT( bool( test ) );
	CPPUNIT_ASSERT( !( !test ) );
	CPPUNIT_ASSERT( test.typeInfo( ) == typeid( T_ObjLarge ) );
	CPPUNIT_ASSERT( M_INPLACE( test , T_ObjLarge ) );
	CPPUNIT_ASSERT_EQUAL( 1 , T_Base::copies );
	CPPUNIT_ASSERT_EQUAL( 0 , T_Base::moves );
}

void VariantTest::testLIPMoveAss( )
{
	T_LIP_ test;
	test = T_ObjLarge( 12 );
	CPPUNIT_ASSERT( bool( test ) );
	CPPUNIT_ASSERT( !( !test ) );
	CPPUNIT_ASSERT( test.typeInfo( ) == typeid( T_ObjLarge ) );
	CPPUNIT_ASSERT( M_INPLACE( test , T_ObjLarge ) );
	CPPUNIT_ASSERT_EQUAL( 0 , T_Base::copies );
	CPPUNIT_ASSERT_EQUAL( 1 , T_Base::moves );
}

/*----------------------------------------------------------------------------*/

void VariantTest::testValueRead( )
{
	T_Variant test( T_Obj( 12 ) );
	CPPUNIT_ASSERT( test.value< T_Obj >( ).v == 12 );
}

void VariantTest::testLargeRead( )
{
	T_Variant test( T_ObjLarge( 12 ) );
	CPPUNIT_ASSERT( test.value< T_ObjLarge >( ).v[ 0 ] == 12 );
}

void VariantTest::testLIPRead( )
{
	T_LIP_ test( T_ObjLarge( 12 ) );
	CPPUNIT_ASSERT( test.value< T_ObjLarge >( ).v[ 0 ] == 12 );
}

void VariantTest::testValueReadBadType( )
{
	T_Variant test( T_ObjCopy( 12 ) );
	try {
		test.value< T_Obj >( );
		CPPUNIT_FAIL( "exception not thrown" );
	} catch ( std::bad_cast const& ) { }
}

void VariantTest::testValueWrite( )
{
	T_Variant test( T_Obj( 1 ) );
	test.value< T_Obj >( ).v = 2;
	CPPUNIT_ASSERT( test.value< T_Obj >( ).v == 2 );
}

void VariantTest::testLargeWrite( )
{
	T_Variant test( T_ObjLarge( 1 ) );
	test.value< T_ObjLarge >( ).v[ 5 ] = 2;
	CPPUNIT_ASSERT( test.value< T_ObjLarge >( ).v[ 5 ] == 2 );
}

void VariantTest::testLIPWrite( )
{
	T_Variant test( T_ObjLarge( 1 ) );
	test.value< T_ObjLarge >( ).v[ 5 ] = 2;
	CPPUNIT_ASSERT( test.value< T_ObjLarge >( ).v[ 5 ] == 2 );
}

void VariantTest::testValueWriteBadType( )
{
	T_Variant test( T_ObjCopy( 12 ) );
	try {
		test.value< T_Obj >( ).v = 1;
		CPPUNIT_FAIL( "exception not thrown" );
	} catch ( std::bad_cast const& ) { }
}

/*----------------------------------------------------------------------------*/

void VariantTest::testCopyConsSmall( )
{
	T_Variant test( T_Obj( 1 ) );
	T_Base::reset( );

	T_Variant copy( test );
	CPPUNIT_ASSERT( bool( test ) );
	CPPUNIT_ASSERT( bool( copy ) );
	CPPUNIT_ASSERT( !( !copy ) );
	CPPUNIT_ASSERT( copy.typeInfo( ) == typeid( T_Obj ) );
	CPPUNIT_ASSERT( copy.value< T_Obj >( ).v == 1 );
	CPPUNIT_ASSERT( M_INPLACE( copy , T_Obj ) );
	CPPUNIT_ASSERT_EQUAL( 1 , T_Base::copies );
	CPPUNIT_ASSERT_EQUAL( 0 , T_Base::moves );
}

void VariantTest::testCopyConsLarge( )
{
	T_Variant test( T_ObjLarge( 1 ) );
	T_Base::reset( );

	T_Variant copy( test );
	CPPUNIT_ASSERT( bool( test ) );
	CPPUNIT_ASSERT( bool( copy ) );
	CPPUNIT_ASSERT( !( !copy ) );
	CPPUNIT_ASSERT( copy.typeInfo( ) == typeid( T_ObjLarge ) );
	CPPUNIT_ASSERT( copy.value< T_ObjLarge >( ).v[ 0 ] == 1 );
	CPPUNIT_ASSERT( !M_INPLACE( copy , T_ObjLarge ) );
	CPPUNIT_ASSERT_EQUAL( 1 , T_Base::copies );
	CPPUNIT_ASSERT_EQUAL( 0 , T_Base::moves );
}

void VariantTest::testCopyConsLIP( )
{
	T_LIP_ test( T_ObjLarge( 1 ) );
	T_Base::reset( );

	T_LIP_ copy( test );
	CPPUNIT_ASSERT( bool( test ) );
	CPPUNIT_ASSERT( bool( copy ) );
	CPPUNIT_ASSERT( !( !copy ) );
	CPPUNIT_ASSERT( copy.typeInfo( ) == typeid( T_ObjLarge ) );
	CPPUNIT_ASSERT( copy.value< T_ObjLarge >( ).v[ 0 ] == 1 );
	CPPUNIT_ASSERT( M_INPLACE( copy , T_ObjLarge ) );
	CPPUNIT_ASSERT_EQUAL( 1 , T_Base::copies );
	CPPUNIT_ASSERT_EQUAL( 0 , T_Base::moves );
}

void VariantTest::testCopyAssSmall( )
{
	T_Variant test( T_Obj( 1 ) );
	T_Base::reset( );

	T_Variant copy;
	copy = test;
	CPPUNIT_ASSERT( bool( test ) );
	CPPUNIT_ASSERT( bool( copy ) );
	CPPUNIT_ASSERT( !( !copy ) );
	CPPUNIT_ASSERT( copy.typeInfo( ) == typeid( T_Obj ) );
	CPPUNIT_ASSERT( copy.value< T_Obj >( ).v == 1 );
	CPPUNIT_ASSERT( M_INPLACE( copy , T_Obj ) );
	CPPUNIT_ASSERT_EQUAL( 1 , T_Base::copies );
	CPPUNIT_ASSERT_EQUAL( 0 , T_Base::moves );
}

void VariantTest::testCopyAssLarge( )
{
	T_Variant test( T_ObjLarge( 1 ) );
	T_Base::reset( );

	T_Variant copy;
	copy = test;
	CPPUNIT_ASSERT( bool( test ) );
	CPPUNIT_ASSERT( bool( copy ) );
	CPPUNIT_ASSERT( !( !copy ) );
	CPPUNIT_ASSERT( copy.typeInfo( ) == typeid( T_ObjLarge ) );
	CPPUNIT_ASSERT( copy.value< T_ObjLarge >( ).v[ 0 ] == 1 );
	CPPUNIT_ASSERT( !M_INPLACE( copy , T_ObjLarge ) );
	CPPUNIT_ASSERT_EQUAL( 1 , T_Base::copies );
	CPPUNIT_ASSERT_EQUAL( 0 , T_Base::moves );
}

void VariantTest::testCopyAssLIP( )
{
	T_LIP_ test( T_ObjLarge( 1 ) );
	T_Base::reset( );

	T_LIP_ copy;
	copy = test;
	CPPUNIT_ASSERT( bool( test ) );
	CPPUNIT_ASSERT( bool( copy ) );
	CPPUNIT_ASSERT( !( !copy ) );
	CPPUNIT_ASSERT( copy.typeInfo( ) == typeid( T_ObjLarge ) );
	CPPUNIT_ASSERT( copy.value< T_ObjLarge >( ).v[ 0 ] == 1 );
	CPPUNIT_ASSERT( M_INPLACE( copy , T_ObjLarge ) );
	CPPUNIT_ASSERT_EQUAL( 1 , T_Base::copies );
	CPPUNIT_ASSERT_EQUAL( 0 , T_Base::moves );
}

void VariantTest::testMoveConsSmall( )
{
	T_Variant test( T_Obj( 1 ) );
	T_Base::reset( );

	T_Variant copy( std::move( test ) );
	CPPUNIT_ASSERT( !test );
	CPPUNIT_ASSERT( bool( copy ) );
	CPPUNIT_ASSERT( !( !copy ) );
	CPPUNIT_ASSERT( copy.typeInfo( ) == typeid( T_Obj ) );
	CPPUNIT_ASSERT( copy.value< T_Obj >( ).v == 1 );
	CPPUNIT_ASSERT( M_INPLACE( copy , T_Obj ) );
	CPPUNIT_ASSERT_EQUAL( 0 , T_Base::copies );
	CPPUNIT_ASSERT_EQUAL( 1 , T_Base::moves );
}

void VariantTest::testMoveConsLarge( )
{
	T_Variant test( T_ObjLarge( 1 ) );
	T_Base::reset( );

	T_Variant copy( std::move( test ) );
	CPPUNIT_ASSERT( !test );
	CPPUNIT_ASSERT( bool( copy ) );
	CPPUNIT_ASSERT( !( !copy ) );
	CPPUNIT_ASSERT( copy.typeInfo( ) == typeid( T_ObjLarge ) );
	CPPUNIT_ASSERT( copy.value< T_ObjLarge >( ).v[ 0 ] == 1 );
	CPPUNIT_ASSERT( !M_INPLACE( copy , T_ObjLarge ) );
	CPPUNIT_ASSERT_EQUAL( 0 , T_Base::copies );
	CPPUNIT_ASSERT_EQUAL( 0 , T_Base::moves );
}

void VariantTest::testMoveConsLIP( )
{
	T_LIP_ test( T_ObjLarge( 1 ) );
	T_Base::reset( );

	T_LIP_ copy( std::move( test ) );
	CPPUNIT_ASSERT( !test );
	CPPUNIT_ASSERT( bool( copy ) );
	CPPUNIT_ASSERT( !( !copy ) );
	CPPUNIT_ASSERT( copy.typeInfo( ) == typeid( T_ObjLarge ) );
	CPPUNIT_ASSERT( copy.value< T_ObjLarge >( ).v[ 0 ] == 1 );
	CPPUNIT_ASSERT( M_INPLACE( copy , T_ObjLarge ) );
	CPPUNIT_ASSERT_EQUAL( 0 , T_Base::copies );
	CPPUNIT_ASSERT_EQUAL( 1 , T_Base::moves );
}

void VariantTest::testMoveAssSmall( )
{
	T_Variant test( T_Obj( 1 ) );
	T_Base::reset( );

	T_Variant copy;
	copy = std::move( test );
	CPPUNIT_ASSERT( !test );
	CPPUNIT_ASSERT( bool( copy ) );
	CPPUNIT_ASSERT( !( !copy ) );
	CPPUNIT_ASSERT( copy.typeInfo( ) == typeid( T_Obj ) );
	CPPUNIT_ASSERT( copy.value< T_Obj >( ).v == 1 );
	CPPUNIT_ASSERT( M_INPLACE( copy , T_Obj ) );
	CPPUNIT_ASSERT_EQUAL( 0 , T_Base::copies );
	CPPUNIT_ASSERT_EQUAL( 1 , T_Base::moves );
}

void VariantTest::testMoveAssLarge( )
{
	T_Variant test( T_ObjLarge( 1 ) );
	T_Base::reset( );

	T_Variant copy;
	copy = std::move( test );
	CPPUNIT_ASSERT( !test );
	CPPUNIT_ASSERT( bool( copy ) );
	CPPUNIT_ASSERT( !( !copy ) );
	CPPUNIT_ASSERT( copy.typeInfo( ) == typeid( T_ObjLarge ) );
	CPPUNIT_ASSERT( copy.value< T_ObjLarge >( ).v[ 0 ] == 1 );
	CPPUNIT_ASSERT( !M_INPLACE( copy , T_ObjLarge ) );
	CPPUNIT_ASSERT_EQUAL( 0 , T_Base::copies );
	CPPUNIT_ASSERT_EQUAL( 0 , T_Base::moves );
}

void VariantTest::testMoveAssLIP( )
{
	T_LIP_ test( T_ObjLarge( 1 ) );
	T_Base::reset( );

	T_LIP_ copy;
	copy = std::move( test );
	CPPUNIT_ASSERT( !test );
	CPPUNIT_ASSERT( bool( copy ) );
	CPPUNIT_ASSERT( !( !copy ) );
	CPPUNIT_ASSERT( copy.typeInfo( ) == typeid( T_ObjLarge ) );
	CPPUNIT_ASSERT( copy.value< T_ObjLarge >( ).v[ 0 ] == 1 );
	CPPUNIT_ASSERT( M_INPLACE( copy , T_ObjLarge ) );
	CPPUNIT_ASSERT_EQUAL( 0 , T_Base::copies );
	CPPUNIT_ASSERT_EQUAL( 1 , T_Base::moves );
}

/*----------------------------------------------------------------------------*/

void VariantTest::testSwapSS( )
{
	T_Variant test1( T_ObjCopy( 1 ) ) , test2( T_Obj( 2 ) );
	T_Base::reset( );
	swap( test1 , test2 );

	CPPUNIT_ASSERT( test1.typeInfo( ) == typeid( T_Obj ) );
	CPPUNIT_ASSERT( test1.value< T_Obj >( ).v == 2 );
	CPPUNIT_ASSERT( test2.typeInfo( ) == typeid( T_ObjCopy ) );
	CPPUNIT_ASSERT( test2.value< T_ObjCopy >( ).v == 1 );
	CPPUNIT_ASSERT_EQUAL( 0 , T_Base::copies );
	CPPUNIT_ASSERT_EQUAL( 2 , T_Base::moves );
}

void VariantTest::testSwapLL( )
{
	T_Variant test1( T_ObjLarge( 1 ) ) , test2( T_ObjLarge( 2 ) );
	T_Base::reset( );
	swap( test1 , test2 );

	CPPUNIT_ASSERT( test1.typeInfo( ) == typeid( T_ObjLarge ) );
	CPPUNIT_ASSERT( test1.value< T_ObjLarge >( ).v[ 0 ] == 2 );
	CPPUNIT_ASSERT( test2.typeInfo( ) == typeid( T_ObjLarge ) );
	CPPUNIT_ASSERT( test2.value< T_ObjLarge >( ).v[ 0 ] == 1 );
	CPPUNIT_ASSERT_EQUAL( 0 , T_Base::copies );
	CPPUNIT_ASSERT_EQUAL( 0 , T_Base::moves );
}

void VariantTest::testSwapSL( )
{
	T_Variant test1( T_Obj( 1 ) ) , test2( T_ObjLarge( 2 ) );
	T_Base::reset( );
	swap( test1 , test2 );

	CPPUNIT_ASSERT( test1.typeInfo( ) == typeid( T_ObjLarge ) );
	CPPUNIT_ASSERT( test1.value< T_ObjLarge >( ).v[ 0 ] == 2 );
	CPPUNIT_ASSERT( test2.typeInfo( ) == typeid( T_Obj ) );
	CPPUNIT_ASSERT( test2.value< T_Obj >( ).v == 1 );
	CPPUNIT_ASSERT_EQUAL( 0 , T_Base::copies );
	CPPUNIT_ASSERT_EQUAL( 1 , T_Base::moves );
}

void VariantTest::testSwapES( )
{
	T_Variant test1( T_Obj( 1 ) ) , test2;
	T_Base::reset( );
	swap( test1 , test2 );

	CPPUNIT_ASSERT( !test1 );
	CPPUNIT_ASSERT( test2.typeInfo( ) == typeid( T_Obj ) );
	CPPUNIT_ASSERT( test2.value< T_Obj >( ).v == 1 );
	CPPUNIT_ASSERT_EQUAL( 0 , T_Base::copies );
	CPPUNIT_ASSERT_EQUAL( 1 , T_Base::moves );
}

void VariantTest::testSwapEL( )
{
	T_Variant test1( T_ObjLarge( 1 ) ) , test2;
	T_Base::reset( );
	swap( test1 , test2 );

	CPPUNIT_ASSERT( !test1 );
	CPPUNIT_ASSERT( test2.typeInfo( ) == typeid( T_ObjLarge ) );
	CPPUNIT_ASSERT( test2.value< T_ObjLarge >( ).v[ 0 ] == 1 );
	CPPUNIT_ASSERT_EQUAL( 0 , T_Base::copies );
	CPPUNIT_ASSERT_EQUAL( 0 , T_Base::moves );
}

/*----------------------------------------------------------------------------*/

void VariantTest::testSwapLIPSS( )
{
	T_LIP_ test1( T_ObjCopy( 1 ) ) , test2( T_Obj( 2 ) );
	T_Base::reset( );
	swap( test1 , test2 );

	CPPUNIT_ASSERT( test1.typeInfo( ) == typeid( T_Obj ) );
	CPPUNIT_ASSERT( test1.value< T_Obj >( ).v == 2 );
	CPPUNIT_ASSERT( test2.typeInfo( ) == typeid( T_ObjCopy ) );
	CPPUNIT_ASSERT( test2.value< T_ObjCopy >( ).v == 1 );
	CPPUNIT_ASSERT_EQUAL( 0 , T_Base::copies );
	CPPUNIT_ASSERT_EQUAL( 2 , T_Base::moves );
}

void VariantTest::testSwapLIPLL( )
{
	T_LIP_ test1( T_ObjLarge( 1 ) ) , test2( T_ObjLarge( 2 ) );
	T_Base::reset( );
	swap( test1 , test2 );

	CPPUNIT_ASSERT( test1.typeInfo( ) == typeid( T_ObjLarge ) );
	CPPUNIT_ASSERT( test1.value< T_ObjLarge >( ).v[ 0 ] == 2 );
	CPPUNIT_ASSERT( test2.typeInfo( ) == typeid( T_ObjLarge ) );
	CPPUNIT_ASSERT( test2.value< T_ObjLarge >( ).v[ 0 ] == 1 );
	CPPUNIT_ASSERT_EQUAL( 0 , T_Base::copies );
	CPPUNIT_ASSERT_EQUAL( 3 , T_Base::moves );
}

void VariantTest::testSwapLIPSL( )
{
	T_LIP_ test1( T_Obj( 1 ) ) , test2( T_ObjLarge( 2 ) );
	T_Base::reset( );
	swap( test1 , test2 );

	CPPUNIT_ASSERT( test1.typeInfo( ) == typeid( T_ObjLarge ) );
	CPPUNIT_ASSERT( test1.value< T_ObjLarge >( ).v[ 0 ] == 2 );
	CPPUNIT_ASSERT( test2.typeInfo( ) == typeid( T_Obj ) );
	CPPUNIT_ASSERT( test2.value< T_Obj >( ).v == 1 );
	CPPUNIT_ASSERT_EQUAL( 0 , T_Base::copies );
	CPPUNIT_ASSERT_EQUAL( 3 , T_Base::moves );
}

void VariantTest::testSwapLIPES( )
{
	T_LIP_ test1( T_Obj( 1 ) ) , test2;
	T_Base::reset( );
	swap( test1 , test2 );

	CPPUNIT_ASSERT( !test1 );
	CPPUNIT_ASSERT( test2.typeInfo( ) == typeid( T_Obj ) );
	CPPUNIT_ASSERT( test2.value< T_Obj >( ).v == 1 );
	CPPUNIT_ASSERT_EQUAL( 0 , T_Base::copies );
	CPPUNIT_ASSERT_EQUAL( 1 , T_Base::moves );
}

void VariantTest::testSwapLIPEL( )
{
	T_LIP_ test1( T_ObjLarge( 1 ) ) , test2;
	T_Base::reset( );
	swap( test1 , test2 );

	CPPUNIT_ASSERT( !test1 );
	CPPUNIT_ASSERT( test2.typeInfo( ) == typeid( T_ObjLarge ) );
	CPPUNIT_ASSERT( test2.value< T_ObjLarge >( ).v[ 0 ] == 1 );
	CPPUNIT_ASSERT_EQUAL( 0 , T_Base::copies );
	CPPUNIT_ASSERT_EQUAL( 1 , T_Base::moves );
}

/*----------------------------------------------------------------------------*/

void VariantTest::testCopyConsLIPToSmall( )
{
	T_LIP_ src( T_Obj( 12 ) );
	T_Variant dest( src );
	CPPUNIT_ASSERT( bool( src ) );
	CPPUNIT_ASSERT( bool( dest ) );
	CPPUNIT_ASSERT( dest.typeInfo( ) == typeid( T_Obj ) );
	CPPUNIT_ASSERT( dest.value< T_Obj >( ).v == 12 );
	CPPUNIT_ASSERT( M_INPLACE( dest , T_Obj ) );
}

void VariantTest::testCopyConsLIPToLarge( )
{
	T_LIP_ src( T_ObjLarge( 12 ) );
	T_Variant dest( src );
	CPPUNIT_ASSERT( bool( src ) );
	CPPUNIT_ASSERT( bool( dest ) );
	CPPUNIT_ASSERT( dest.typeInfo( ) == typeid( T_ObjLarge ) );
	CPPUNIT_ASSERT( dest.value< T_ObjLarge >( ).v[ 0 ] == 12 );
	CPPUNIT_ASSERT( !M_INPLACE( dest , T_ObjLarge ) );
}

void VariantTest::testCopyConsSmallToLIP( )
{
	T_Variant src( T_Obj( 2 ) );
	T_LIP_ dest( src );
	CPPUNIT_ASSERT( bool( src ) );
	CPPUNIT_ASSERT( bool( dest ) );
	CPPUNIT_ASSERT( dest.typeInfo( ) == typeid( T_Obj ) );
	CPPUNIT_ASSERT( dest.value< T_Obj >( ).v == 2 );
	CPPUNIT_ASSERT( M_INPLACE( dest , T_Obj ) );
}

void VariantTest::testCopyConsLargeToLIP( )
{
	T_Variant src( T_ObjLarge( 12 ) );
	T_LIP_ dest( src );
	CPPUNIT_ASSERT( bool( src ) );
	CPPUNIT_ASSERT( bool( dest ) );
	CPPUNIT_ASSERT( dest.typeInfo( ) == typeid( T_ObjLarge ) );
	CPPUNIT_ASSERT( dest.value< T_ObjLarge >( ).v[ 0 ] == 12 );
	CPPUNIT_ASSERT( M_INPLACE( dest , T_ObjLarge ) );
}

void VariantTest::testMoveConsLIPToSmall( )
{
	T_LIP_ src( T_Obj( 12 ) );
	T_Variant dest( std::move( src ) );
	CPPUNIT_ASSERT( !src );
	CPPUNIT_ASSERT( bool( dest ) );
	CPPUNIT_ASSERT( dest.typeInfo( ) == typeid( T_Obj ) );
	CPPUNIT_ASSERT( dest.value< T_Obj >( ).v == 12 );
	CPPUNIT_ASSERT( M_INPLACE( dest , T_Obj ) );
}

void VariantTest::testMoveConsLIPToLarge( )
{
	T_LIP_ src( T_ObjLarge( 12 ) );
	T_Variant dest( std::move( src ) );
	CPPUNIT_ASSERT( !src );
	CPPUNIT_ASSERT( bool( dest ) );
	CPPUNIT_ASSERT( dest.typeInfo( ) == typeid( T_ObjLarge ) );
	CPPUNIT_ASSERT( dest.value< T_ObjLarge >( ).v[ 0 ] == 12 );
	CPPUNIT_ASSERT( !M_INPLACE( dest , T_ObjLarge ) );
}

void VariantTest::testMoveConsSmallToLIP( )
{
	T_Variant src( T_Obj( 12 ) );
	T_LIP_ dest( std::move( src ) );
	CPPUNIT_ASSERT( !src );
	CPPUNIT_ASSERT( bool( dest ) );
	CPPUNIT_ASSERT( dest.typeInfo( ) == typeid( T_Obj ) );
	CPPUNIT_ASSERT( dest.value< T_Obj >( ).v == 12 );
	CPPUNIT_ASSERT( M_INPLACE( dest , T_Obj ) );
}

void VariantTest::testMoveConsLargeToLIP( )
{
	T_Variant src( T_ObjLarge( 12 ) );
	T_LIP_ dest( std::move( src ) );
	CPPUNIT_ASSERT( !src );
	CPPUNIT_ASSERT( bool( dest ) );
	CPPUNIT_ASSERT( dest.typeInfo( ) == typeid( T_ObjLarge ) );
	CPPUNIT_ASSERT( dest.value< T_ObjLarge >( ).v[ 0 ] == 12 );
	CPPUNIT_ASSERT( !M_INPLACE( dest , T_ObjLarge ) );
}

void VariantTest::testCopyAssLIPToSmall( )
{
	T_LIP_ src( T_Obj( 12 ) );
	T_Variant dest;
	dest = src;
	CPPUNIT_ASSERT( bool( src ) );
	CPPUNIT_ASSERT( bool( dest ) );
	CPPUNIT_ASSERT( dest.typeInfo( ) == typeid( T_Obj ) );
	CPPUNIT_ASSERT( dest.value< T_Obj >( ).v == 12 );
	CPPUNIT_ASSERT( M_INPLACE( dest , T_Obj ) );
}

void VariantTest::testCopyAssLIPToLarge( )
{
	T_LIP_ src( T_ObjLarge( 12 ) );
	T_Variant dest;
	dest = src;
	CPPUNIT_ASSERT( bool( src ) );
	CPPUNIT_ASSERT( bool( dest ) );
	CPPUNIT_ASSERT( dest.typeInfo( ) == typeid( T_ObjLarge ) );
	CPPUNIT_ASSERT( dest.value< T_ObjLarge >( ).v[ 0 ] == 12 );
	CPPUNIT_ASSERT( !M_INPLACE( dest , T_ObjLarge ) );
}

void VariantTest::testCopyAssSmallToLIP( )
{
	T_Variant src( T_Obj( 2 ) );
	T_LIP_ dest;
	dest = src;
	CPPUNIT_ASSERT( bool( src ) );
	CPPUNIT_ASSERT( bool( dest ) );
	CPPUNIT_ASSERT( dest.typeInfo( ) == typeid( T_Obj ) );
	CPPUNIT_ASSERT( dest.value< T_Obj >( ).v == 2 );
	CPPUNIT_ASSERT( M_INPLACE( dest , T_Obj ) );
}

void VariantTest::testCopyAssLargeToLIP( )
{
	T_Variant src( T_ObjLarge( 12 ) );
	T_LIP_ dest;
	dest = src;
	CPPUNIT_ASSERT( bool( src ) );
	CPPUNIT_ASSERT( bool( dest ) );
	CPPUNIT_ASSERT( dest.typeInfo( ) == typeid( T_ObjLarge ) );
	CPPUNIT_ASSERT( dest.value< T_ObjLarge >( ).v[ 0 ] == 12 );
	CPPUNIT_ASSERT( M_INPLACE( dest , T_ObjLarge ) );
}

void VariantTest::testMoveAssLIPToSmall( )
{
	T_LIP_ src( T_Obj( 12 ) );
	T_Variant dest;
	dest = std::move( src );
	CPPUNIT_ASSERT( !src );
	CPPUNIT_ASSERT( bool( dest ) );
	CPPUNIT_ASSERT( dest.typeInfo( ) == typeid( T_Obj ) );
	CPPUNIT_ASSERT( dest.value< T_Obj >( ).v == 12 );
	CPPUNIT_ASSERT( M_INPLACE( dest , T_Obj ) );
}

void VariantTest::testMoveAssLIPToLarge( )
{
	T_LIP_ src( T_ObjLarge( 12 ) );
	T_Variant dest;
	dest = std::move( src );
	CPPUNIT_ASSERT( !src );
	CPPUNIT_ASSERT( bool( dest ) );
	CPPUNIT_ASSERT( dest.typeInfo( ) == typeid( T_ObjLarge ) );
	CPPUNIT_ASSERT( dest.value< T_ObjLarge >( ).v[ 0 ] == 12 );
	CPPUNIT_ASSERT( !M_INPLACE( dest , T_ObjLarge ) );
}

void VariantTest::testMoveAssSmallToLIP( )
{
	T_Variant src( T_Obj( 2 ) );
	T_LIP_ dest;
	dest = std::move( src );
	CPPUNIT_ASSERT( !src );
	CPPUNIT_ASSERT( bool( dest ) );
	CPPUNIT_ASSERT( dest.typeInfo( ) == typeid( T_Obj ) );
	CPPUNIT_ASSERT( dest.value< T_Obj >( ).v == 2 );
	CPPUNIT_ASSERT( M_INPLACE( dest , T_Obj ) );
}

void VariantTest::testMoveAssLargeToLIP( )
{
	T_Variant src( T_ObjLarge( 12 ) );
	T_LIP_ dest;
	dest = std::move( src );
	CPPUNIT_ASSERT( !src );
	CPPUNIT_ASSERT( bool( dest ) );
	CPPUNIT_ASSERT( dest.typeInfo( ) == typeid( T_ObjLarge ) );
	CPPUNIT_ASSERT( dest.value< T_ObjLarge >( ).v[ 0 ] == 12 );
	CPPUNIT_ASSERT( !M_INPLACE( dest , T_ObjLarge ) );
}