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


class StringsBuilderTest : public CppUnit::TestFixture
{
	CPPUNIT_TEST_SUITE( StringsBuilderTest );
		CPPUNIT_TEST( testConsDefault );
		CPPUNIT_TEST( testConsFromAsciiBuffer );
		CPPUNIT_TEST( testConsFromUTF8Buffer );
		CPPUNIT_TEST( testConsFromAsciiNTS );
		CPPUNIT_TEST( testConsFromUTF8NTS );
		CPPUNIT_TEST( testConsFromString );

		CPPUNIT_TEST( testCopyCons );
		CPPUNIT_TEST( testCopyAss );

		CPPUNIT_TEST( testMoveCons );
		CPPUNIT_TEST( testMoveAss );

		CPPUNIT_TEST( testSwap );

		CPPUNIT_TEST( testEnsureCapacityFactor );
		CPPUNIT_TEST( testEnsureCapacityOther );
		CPPUNIT_TEST( testEnsureCapacitySmaller );

		CPPUNIT_TEST( testClear );
		CPPUNIT_TEST( testFree );

		CPPUNIT_TEST( testTruncate );
		CPPUNIT_TEST( testTruncateLonger );

		CPPUNIT_TEST( testAppendOther );
		CPPUNIT_TEST( testAppendSwap );
		CPPUNIT_TEST( testAppendStr );
		CPPUNIT_TEST( testAppendBuf );
		CPPUNIT_TEST( testAppendChar );
		CPPUNIT_TEST( testAppendUnicode );

		CPPUNIT_TEST( testAppendNumSignedZeroB10 );
		CPPUNIT_TEST( testAppendNumSignedPositiveB10 );
		CPPUNIT_TEST( testAppendNumSignedNegativeB10 );
		CPPUNIT_TEST( testAppendNumSignedMinB10 );
		CPPUNIT_TEST( testAppendNumSignedMaxB10 );
		CPPUNIT_TEST( testAppendNumSignedRest );

		CPPUNIT_TEST( testAppendNumUnsigned );
		CPPUNIT_TEST( testAppendNumFloat );

		CPPUNIT_TEST( testStringConsCopy );
		CPPUNIT_TEST( testStringAssCopy );

		CPPUNIT_TEST( testStringConsMove );
		CPPUNIT_TEST( testStringAssMove );

		CPPUNIT_TEST( testCmpOther );
		CPPUNIT_TEST( testCmpCString );
		CPPUNIT_TEST( testCmpTString );

		CPPUNIT_TEST( testToUnsignedBasic );
		CPPUNIT_TEST( testToUnsignedOkValue );
		CPPUNIT_TEST( testToUnsignedInvalid );
		CPPUNIT_TEST( testToUnsignedTooLarge );
		CPPUNIT_TEST( testToUnsignedBaseOk );
		CPPUNIT_TEST( testToUnsignedBaseInvalid );
		CPPUNIT_TEST( testToUnsignedBaseAutoBinary );
		CPPUNIT_TEST( testToUnsignedBaseAutoOctal );
		CPPUNIT_TEST( testToUnsignedBaseAutoDecimal );
		CPPUNIT_TEST( testToUnsignedBaseAutoHexa );
		CPPUNIT_TEST( testToUnsignedBaseAutoZero );
		CPPUNIT_TEST( testToUnsignedBaseAutoInvalid );
		CPPUNIT_TEST( testToUnsignedBaseAutoCut );
		CPPUNIT_TEST( testToUnsignedSeparator );
		CPPUNIT_TEST( testToUnsignedSeparatorCustom );
		CPPUNIT_TEST( testToUnsignedSeparatorCustomUTF8 );
		CPPUNIT_TEST( testToUnsignedLeadingWhitespace );
		CPPUNIT_TEST( testToUnsignedLeadingWhitespaceOnly );

		CPPUNIT_TEST( testToSignedBasic );
		CPPUNIT_TEST( testToSignedOkValue );
		CPPUNIT_TEST( testToSignedPlus );
		CPPUNIT_TEST( testToSignedPlusMultiple );
		CPPUNIT_TEST( testToSignedMinus );
		CPPUNIT_TEST( testToSignedMinusMultiple );
		CPPUNIT_TEST( testToSignedInvalid );
		CPPUNIT_TEST( testToSignedTooLarge );
		CPPUNIT_TEST( testToSignedTooSmall );
		CPPUNIT_TEST( testToSignedBaseOk );
		CPPUNIT_TEST( testToSignedBaseInvalid );
		CPPUNIT_TEST( testToSignedBaseAutoBinary );
		CPPUNIT_TEST( testToSignedBaseAutoOctal );
		CPPUNIT_TEST( testToSignedBaseAutoDecimal );
		CPPUNIT_TEST( testToSignedBaseAutoHexa );
		CPPUNIT_TEST( testToSignedBaseAutoZero );
		CPPUNIT_TEST( testToSignedBaseAutoInvalid );
		CPPUNIT_TEST( testToSignedBaseAutoCut );
		CPPUNIT_TEST( testToSignedBaseAutoPlus );
		CPPUNIT_TEST( testToSignedBaseAutoMinus );
		CPPUNIT_TEST( testToSignedSeparator );
		CPPUNIT_TEST( testToSignedSeparatorCustom );
		CPPUNIT_TEST( testToSignedSeparatorCustomUTF8 );
		CPPUNIT_TEST( testToSignedLeadingWhitespace );
		CPPUNIT_TEST( testToSignedLeadingWhitespaceOnly );

		CPPUNIT_TEST( testToDouble );
	CPPUNIT_TEST_SUITE_END( );

public:
	void testConsDefault( );
	void testConsFromAsciiBuffer( );
	void testConsFromUTF8Buffer( );
	void testConsFromAsciiNTS( );
	void testConsFromUTF8NTS( );
	void testConsFromString( );

	void testCopyCons( );
	void testCopyAss( );

	void testMoveCons( );
	void testMoveAss( );

	void testSwap( );

	void testEnsureCapacityFactor( );
	void testEnsureCapacityOther( );
	void testEnsureCapacitySmaller( );

	void testClear( );
	void testFree( );

	void testTruncate( );
	void testTruncateLonger( );

	void testAppendOther( );
	void testAppendSwap( );
	void testAppendStr( );
	void testAppendBuf( );
	void testAppendChar( );
	void testAppendUnicode( );

	void testAppendNumSignedZeroB10( );
	void testAppendNumSignedPositiveB10( );
	void testAppendNumSignedNegativeB10( );
	void testAppendNumSignedMinB10( );
	void testAppendNumSignedMaxB10( );
	void testAppendNumSignedRest( );

	void testAppendNumUnsigned( );
	void testAppendNumFloat( );

	void testStringConsCopy( );
	void testStringAssCopy( );

	void testStringConsMove( );
	void testStringAssMove( );

	void testCmpOther( );
	void testCmpCString( );
	void testCmpTString( );

	void testToUnsignedBasic( );
	void testToUnsignedOkValue( );
	void testToUnsignedInvalid( );
	void testToUnsignedTooLarge( );
	void testToUnsignedBaseOk( );
	void testToUnsignedBaseInvalid( );
	void testToUnsignedBaseAutoBinary( );
	void testToUnsignedBaseAutoOctal( );
	void testToUnsignedBaseAutoDecimal( );
	void testToUnsignedBaseAutoHexa( );
	void testToUnsignedBaseAutoZero( );
	void testToUnsignedBaseAutoInvalid( );
	void testToUnsignedBaseAutoCut( );
	void testToUnsignedSeparator( );
	void testToUnsignedSeparatorCustom( );
	void testToUnsignedSeparatorCustomUTF8( );
	void testToUnsignedLeadingWhitespace( );
	void testToUnsignedLeadingWhitespaceOnly( );

	void testToSignedBasic( );
	void testToSignedOkValue( );
	void testToSignedPlus( );
	void testToSignedPlusMultiple( );
	void testToSignedMinus( );
	void testToSignedMinusMultiple( );
	void testToSignedInvalid( );
	void testToSignedTooLarge( );
	void testToSignedTooSmall( );
	void testToSignedBaseOk( );
	void testToSignedBaseInvalid( );
	void testToSignedBaseAutoBinary( );
	void testToSignedBaseAutoOctal( );
	void testToSignedBaseAutoDecimal( );
	void testToSignedBaseAutoHexa( );
	void testToSignedBaseAutoZero( );
	void testToSignedBaseAutoInvalid( );
	void testToSignedBaseAutoCut( );
	void testToSignedBaseAutoPlus( );
	void testToSignedBaseAutoMinus( );
	void testToSignedSeparator( );
	void testToSignedSeparatorCustom( );
	void testToSignedSeparatorCustomUTF8( );
	void testToSignedLeadingWhitespace( );
	void testToSignedLeadingWhitespaceOnly( );

	void testToDouble( );
};
CPPUNIT_TEST_SUITE_REGISTRATION( StringsBuilderTest );

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

void StringsBuilderTest::testConsDefault( )
{
	T_StringBuilder sb;
	CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , sb.size( ) );
	CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , sb.length( ) );
	CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , sb.capacity( ) );
	CPPUNIT_ASSERT( sb.data( ) == nullptr );
}

void StringsBuilderTest::testConsFromAsciiBuffer( )
{
	T_StringBuilder sb( "test" , 4 );
	CPPUNIT_ASSERT_EQUAL( uint32_t( 4 ) , sb.size( ) );
	CPPUNIT_ASSERT_EQUAL( uint32_t( 4 ) , sb.length( ) );
	CPPUNIT_ASSERT_EQUAL( uint32_t( T_StringBuilder::C_GROWTH ) ,
			sb.capacity( ) );
	CPPUNIT_ASSERT( sb.data( ) != nullptr );
	CPPUNIT_ASSERT( !memcmp( sb.data( ) , "test" , 4 ) );
}

void StringsBuilderTest::testConsFromUTF8Buffer( )
{
	const char test[] = "t\xe2\x82\xacst";
	const uint32_t sz = sizeof( test ) - 1;
	T_StringBuilder sb( test , sz );
	CPPUNIT_ASSERT_EQUAL( uint32_t( sz ) , sb.size( ) );
	CPPUNIT_ASSERT_EQUAL( uint32_t( 4 ) , sb.length( ) );
	CPPUNIT_ASSERT_EQUAL( uint32_t( T_StringBuilder::C_GROWTH ) ,
			sb.capacity( ) );
	CPPUNIT_ASSERT( sb.data( ) != nullptr );
	CPPUNIT_ASSERT( !memcmp( sb.data( ) , test , sz ) );
}

void StringsBuilderTest::testConsFromAsciiNTS( )
{
	const char test[] = "test";
	const uint32_t sz = sizeof( test ) - 1;
	T_StringBuilder sb( test );
	CPPUNIT_ASSERT_EQUAL( uint32_t( sz ) , sb.size( ) );
	CPPUNIT_ASSERT_EQUAL( uint32_t( 4 ) , sb.length( ) );
	CPPUNIT_ASSERT_EQUAL( uint32_t( T_StringBuilder::C_GROWTH ) ,
			sb.capacity( ) );
	CPPUNIT_ASSERT( sb.data( ) != nullptr );
	CPPUNIT_ASSERT( !memcmp( sb.data( ) , test , sz ) );
}

void StringsBuilderTest::testConsFromUTF8NTS( )
{
	const char test[] = "t\xe2\x82\xacst";
	const uint32_t sz = sizeof( test ) - 1;
	T_StringBuilder sb( test );
	CPPUNIT_ASSERT_EQUAL( uint32_t( sz ) , sb.size( ) );
	CPPUNIT_ASSERT_EQUAL( uint32_t( 4 ) , sb.length( ) );
	CPPUNIT_ASSERT_EQUAL( uint32_t( T_StringBuilder::C_GROWTH ) ,
			sb.capacity( ) );
	CPPUNIT_ASSERT( sb.data( ) != nullptr );
	CPPUNIT_ASSERT( !memcmp( sb.data( ) , test , sz ) );
}

void StringsBuilderTest::testConsFromString( )
{
	const T_String test( "t\xe2\x82\xacst" );
	T_StringBuilder sb( test );
	CPPUNIT_ASSERT_EQUAL( test.size( ) , sb.size( ) );
	CPPUNIT_ASSERT_EQUAL( test.length( ) , sb.length( ) );
	CPPUNIT_ASSERT_EQUAL( uint32_t( T_StringBuilder::C_GROWTH ) ,
			sb.capacity( ) );
	CPPUNIT_ASSERT( sb.data( ) != nullptr );
	CPPUNIT_ASSERT( !memcmp( sb.data( ) , test.data( ) , test.size( ) ) );
}

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

void StringsBuilderTest::testCopyCons( )
{
	const char str[] = "this is a test";
	const uint32_t sz = sizeof( str ) - 1;
	T_StringBuilder isb( str );
	T_StringBuilder sb( isb );

	CPPUNIT_ASSERT_EQUAL( sz , isb.length( ) );
	CPPUNIT_ASSERT_EQUAL( sz , isb.size( ) );
	CPPUNIT_ASSERT_EQUAL( uint32_t( T_StringBuilder::C_GROWTH ) , isb.capacity( ) );
	CPPUNIT_ASSERT( isb.data( ) != nullptr );
	CPPUNIT_ASSERT( !memcmp( isb.data( ) , str , sz ) );

	CPPUNIT_ASSERT_EQUAL( sz , sb.length( ) );
	CPPUNIT_ASSERT_EQUAL( sz , sb.size( ) );
	CPPUNIT_ASSERT_EQUAL( uint32_t( T_StringBuilder::C_GROWTH ) , sb.capacity( ) );
	CPPUNIT_ASSERT( sb.data( ) != nullptr );
	CPPUNIT_ASSERT( !memcmp( sb.data( ) , str , sz ) );

	CPPUNIT_ASSERT( sb.data( ) != isb.data( ) );
}

void StringsBuilderTest::testCopyAss( )
{
	const char str[] = "this is a test";
	const uint32_t sz = sizeof( str ) - 1;
	T_StringBuilder isb( str );
	T_StringBuilder sb;

	sb = isb;

	CPPUNIT_ASSERT_EQUAL( sz , isb.length( ) );
	CPPUNIT_ASSERT_EQUAL( sz , isb.size( ) );
	CPPUNIT_ASSERT_EQUAL( uint32_t( T_StringBuilder::C_GROWTH ) , isb.capacity( ) );
	CPPUNIT_ASSERT( isb.data( ) != nullptr );
	CPPUNIT_ASSERT( !memcmp( isb.data( ) , str , sz ) );

	CPPUNIT_ASSERT_EQUAL( sz , sb.length( ) );
	CPPUNIT_ASSERT_EQUAL( sz , sb.size( ) );
	CPPUNIT_ASSERT_EQUAL( uint32_t( T_StringBuilder::C_GROWTH ) , sb.capacity( ) );
	CPPUNIT_ASSERT( sb.data( ) != nullptr );
	CPPUNIT_ASSERT( !memcmp( sb.data( ) , str , sz ) );

	CPPUNIT_ASSERT( sb.data( ) != isb.data( ) );
}

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

void StringsBuilderTest::testMoveCons( )
{
	const char str[] = "this is a test";
	const uint32_t sz = sizeof( str ) - 1;
	T_StringBuilder isb( str );
	T_StringBuilder sb( std::move( isb ) );

	CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , isb.length( ) );
	CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , isb.size( ) );
	CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , isb.capacity( ) );
	CPPUNIT_ASSERT( isb.data( ) == nullptr );

	CPPUNIT_ASSERT_EQUAL( sz , sb.length( ) );
	CPPUNIT_ASSERT_EQUAL( sz , sb.size( ) );
	CPPUNIT_ASSERT_EQUAL( uint32_t( T_StringBuilder::C_GROWTH ) ,
			sb.capacity( ) );
	CPPUNIT_ASSERT( sb.data( ) != nullptr );
	CPPUNIT_ASSERT( !memcmp( sb.data( ) , str , sz ) );
}

void StringsBuilderTest::testMoveAss( )
{
	const char str[] = "this is a test";
	const uint32_t sz = sizeof( str ) - 1;
	T_StringBuilder isb( str );
	T_StringBuilder sb( "huh" );

	sb = std::move( isb );

	CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , isb.length( ) );
	CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , isb.size( ) );
	CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , isb.capacity( ) );
	CPPUNIT_ASSERT( isb.data( ) == nullptr );

	CPPUNIT_ASSERT_EQUAL( sz , sb.length( ) );
	CPPUNIT_ASSERT_EQUAL( sz , sb.size( ) );
	CPPUNIT_ASSERT_EQUAL( uint32_t( T_StringBuilder::C_GROWTH ) ,
			sb.capacity( ) );
	CPPUNIT_ASSERT( sb.data( ) != nullptr );
	CPPUNIT_ASSERT( !memcmp( sb.data( ) , str , sz ) );
}

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

void StringsBuilderTest::testSwap( )
{
	const char test[] = "test";
	const uint32_t sz = sizeof( test ) - 1;
	T_StringBuilder sb1( test , sz ) , sb2;
	swap( sb1 , sb2 );

	CPPUNIT_ASSERT_EQUAL( sz , sb2.size( ) );
	CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , sb1.size( ) );
	CPPUNIT_ASSERT_EQUAL( sz , sb2.length( ) );
	CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , sb1.length( ) );
	CPPUNIT_ASSERT_EQUAL( uint32_t( T_StringBuilder::C_GROWTH ) ,
			sb2.capacity( ) );
	CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , sb1.capacity( ) );
	CPPUNIT_ASSERT( sb1.data( ) == nullptr );
	CPPUNIT_ASSERT( sb2.data( ) != nullptr );
	CPPUNIT_ASSERT( !memcmp( sb2.data( ) , test , sz ) );
}

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

void StringsBuilderTest::testEnsureCapacityFactor( )
{
	T_StringBuilder sb;
	sb.ensureCapacity( 2 * sb.C_GROWTH );
	CPPUNIT_ASSERT_EQUAL( 2 * sb.C_GROWTH , sb.capacity( ) );
}

void StringsBuilderTest::testEnsureCapacityOther( )
{
	T_StringBuilder sb;
	sb.ensureCapacity( 3 * sb.C_GROWTH / 2 );
	CPPUNIT_ASSERT_EQUAL( 2 * sb.C_GROWTH , sb.capacity( ) );
}

void StringsBuilderTest::testEnsureCapacitySmaller( )
{
	T_StringBuilder sb;
	sb.ensureCapacity( 4 * sb.C_GROWTH );
	sb.ensureCapacity( 2 * sb.C_GROWTH );
	CPPUNIT_ASSERT_EQUAL( 4 * sb.C_GROWTH , sb.capacity( ) );
}

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

void StringsBuilderTest::testClear( )
{
	T_StringBuilder sb( "this is a test" );
	sb.clear( );
	CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , sb.size( ) );
	CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , sb.length( ) );
	CPPUNIT_ASSERT_EQUAL( uint32_t( sb.C_GROWTH ) , sb.capacity( ) );
	CPPUNIT_ASSERT( sb.data( ) != nullptr );
}

void StringsBuilderTest::testFree( )
{
	T_StringBuilder sb( "this is a test" );
	sb.free( );
	CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , sb.size( ) );
	CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , sb.length( ) );
	CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , sb.capacity( ) );
	CPPUNIT_ASSERT( sb.data( ) == nullptr );
}

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

void StringsBuilderTest::testTruncate( )
{
	T_StringBuilder sb( "this is\uf1bb a test" );
	sb.truncate( 8 );
	CPPUNIT_ASSERT_EQUAL( uint32_t( 8 ) , sb.length( ) );
	CPPUNIT_ASSERT_EQUAL( uint32_t( 10 ) , sb.size( ) );
}

void StringsBuilderTest::testTruncateLonger( )
{
	T_StringBuilder sb( "this is\uf1bb a test" );
	sb.truncate( 20 );
	CPPUNIT_ASSERT_EQUAL( uint32_t( 15 ) , sb.length( ) );
	CPPUNIT_ASSERT_EQUAL( uint32_t( 17 ) , sb.size( ) );
}

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

void StringsBuilderTest::testAppendOther( )
{
	T_StringBuilder sb1( "this is " ) , sb2( "a test!" );
	T_StringBuilder sb3( sb1 );
	sb3.append( sb2 );

	CPPUNIT_ASSERT_EQUAL( sb1.size( ) + sb2.size( ) , sb3.size( ) );
	CPPUNIT_ASSERT_EQUAL( sb1.length( ) + sb2.length( ) , sb3.length( ) );
	CPPUNIT_ASSERT( !memcmp( sb3.data( ) , sb1.data( ) , sb1.size( ) ) );
	CPPUNIT_ASSERT( !memcmp( sb3.data( ) + sb1.size( ) , sb2.data( ) ,
				sb2.size( ) ) );
}

void StringsBuilderTest::testAppendSwap( )
{
	const char test[] = "test";
	const uint32_t sz = sizeof( test ) - 1;
	T_StringBuilder sbi( test , sz ) , sbo;
	sbo.append( std::move( sbi ) );

	CPPUNIT_ASSERT_EQUAL( sz , sbo.size( ) );
	CPPUNIT_ASSERT_EQUAL( sz , sbo.length( ) );
	CPPUNIT_ASSERT( sbo.data( ) != nullptr );
	CPPUNIT_ASSERT( !memcmp( sbo.data( ) , test , sz ) );

	CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , sbi.size( ) );
	CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , sbi.length( ) );
	CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , sbi.capacity( ) );
	CPPUNIT_ASSERT( sbi.data( ) == nullptr );
}

void StringsBuilderTest::testAppendStr( )
{
	T_String str( "test\u20ac" );
	T_StringBuilder sbo;
	sbo.append( str ).append( str );

	CPPUNIT_ASSERT_EQUAL( str.size( ) * 2 , sbo.size( ) );
	CPPUNIT_ASSERT_EQUAL( str.length( ) * 2 , sbo.length( ) );
	CPPUNIT_ASSERT( !memcmp( sbo.data( ) , str.data( ) , str.size( ) ) );
	CPPUNIT_ASSERT( !memcmp( sbo.data( ) + str.size( ) , str.data( ) ,
				str.size( ) ) );
}

void StringsBuilderTest::testAppendBuf( )
{
	T_String str( "test\u20ac" );
	T_StringBuilder sbo;
	sbo.append( str.data( ) , str.size( ) );

	CPPUNIT_ASSERT_EQUAL( str.size( ) , sbo.size( ) );
	CPPUNIT_ASSERT_EQUAL( str.length( ) , sbo.length( ) );
	CPPUNIT_ASSERT( !memcmp( sbo.data( ) , str.data( ) , str.size( ) ) );
}

void StringsBuilderTest::testAppendChar( )
{
	T_StringBuilder sbo;
	sbo.append( 'a' ).append( 'b' );

	CPPUNIT_ASSERT_EQUAL( uint32_t( 2 ) , sbo.size( ) );
	CPPUNIT_ASSERT_EQUAL( uint32_t( 2 ) , sbo.length( ) );
	CPPUNIT_ASSERT( !memcmp( sbo.data( ) , "ab" , 2 ) );
}

void StringsBuilderTest::testAppendUnicode( )
{
	const char expected[] = "a\u00e9\u20ac\U00010102";
	const uint32_t sz = sizeof( expected ) - 1;

	T_StringBuilder sbo;
	sbo.append( T_Character( 'a' ) )
		.append( T_Character( 0xe9u ) )
		.append( T_Character( 0x20acu ) )
		.append( T_Character( 0x10102u ) );

	CPPUNIT_ASSERT_EQUAL( sz , sbo.size( ) );
	CPPUNIT_ASSERT_EQUAL( uint32_t( 4 ) , sbo.length( ) );
	CPPUNIT_ASSERT( !memcmp( sbo.data( ) , expected , sz ) );
}

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

#define M_CHECK_( STR ) \
	{ \
		const char t[] = STR; \
		const uint32_t sz = sizeof( t ) - 1; \
		if ( sz != sbo.size( ) || memcmp( sbo.data( ) , t , sz ) ) { \
			char x[ sbo.size( ) + 1 ]; \
			x[ sizeof( x ) - 1 ] = 0; \
			memcpy( x , sbo.data( ) , sizeof( x ) - 1 ); \
			printf( "'%s' | expected '%s'\n" , x , STR ); \
		} \
		CPPUNIT_ASSERT_EQUAL( sz , sbo.size( ) ); \
		CPPUNIT_ASSERT( !memcmp( sbo.data( ) , t , sz ) ); \
	}

void StringsBuilderTest::testAppendNumSignedZeroB10( )
{
	T_StringBuilder sbo;
	sbo.clear( ).appendNumeric( int64_t( 0 ) ); M_CHECK_( "0" );
}

void StringsBuilderTest::testAppendNumSignedNegativeB10( )
{
	T_StringBuilder sbo;
	sbo.clear( ).appendNumeric( int64_t( -1234 ) ); M_CHECK_( "-1234" );
}

void StringsBuilderTest::testAppendNumSignedPositiveB10( )
{
	T_StringBuilder sbo;
	sbo.clear( ).appendNumeric( int64_t( 1234 ) ); M_CHECK_( "1234" );
}

void StringsBuilderTest::testAppendNumSignedMinB10( )
{
	T_StringBuilder sbo;
	sbo.clear( ).appendNumeric( int64_t( INT64_MIN ) ); M_CHECK_( "-9223372036854775808" );
}

void StringsBuilderTest::testAppendNumSignedMaxB10( )
{
	T_StringBuilder sbo;
	sbo.clear( ).appendNumeric( int64_t( INT64_MAX ) ); M_CHECK_( "9223372036854775807" );
}

void StringsBuilderTest::testAppendNumSignedRest( )
{
	T_StringBuilder sbo;
	sbo.clear( ).appendNumeric( int64_t( 129 ) , 2 );
	M_CHECK_( "10000001" );
	sbo.clear( ).appendNumeric( int64_t( -129 ) , 2 );
	M_CHECK_( "-10000001" );

	sbo.clear( ).appendNumeric( int64_t( 129 ) , 16 ); M_CHECK_( "81" );
	sbo.clear( ).appendNumeric( int64_t( -129 ) , 16 ); M_CHECK_( "-81" );

	sbo.clear( ).appendNumeric( int64_t( 1234 ) , 10 , true );
	M_CHECK_( "1 234" );
	sbo.clear( ).appendNumeric( int64_t( -1234 ) , 10 , true );
	M_CHECK_( "-1 234" );

	sbo.clear( ).appendNumeric( int64_t( 1234 ) , 10 , true , ',' );
	M_CHECK_( "1,234" );
	sbo.clear( ).appendNumeric( int64_t( -1234 ) , 10 , true , ',' );
	M_CHECK_( "-1,234" );

	sbo.clear( ).appendNumeric( int64_t( 1234 ) , 10 , true ,
			T_Character( 0x20acu ) );
	M_CHECK_( "1\u20ac234" );
	sbo.clear( ).appendNumeric( int64_t( -1234 ) , 10 , true , 0x20acu );
	M_CHECK_( "-1\u20ac234" );

	sbo.clear( ).appendNumeric( int64_t( 65535 ) , 2 , true , ' ' , 8 );
	M_CHECK_( "11111111 11111111" );
	sbo.clear( ).appendNumeric( int64_t( -65535 ) , 2 , true , ' ' , 8 );
	M_CHECK_( "-11111111 11111111" );

	sbo.clear( ).appendNumeric( int64_t( 15 ) , 2 , true , ' ' , 1 );
	M_CHECK_( "1 1 1 1" );

}

void StringsBuilderTest::testAppendNumUnsigned( )
{
	T_StringBuilder sbo;
	sbo.clear( ).appendNumeric( uint64_t( 1 ) ); M_CHECK_( "1" );
	sbo.clear( ).appendNumeric( uint64_t( 0 ) ); M_CHECK_( "0" );
	sbo.clear( ).appendNumeric( uint64_t( 1234 ) ); M_CHECK_( "1234" );

	sbo.clear( ).appendNumeric( uint64_t( 129 ) , 2 );
	M_CHECK_( "10000001" );

	sbo.clear( ).appendNumeric( uint64_t( 129 ) , 16 ); M_CHECK_( "81" );

	sbo.clear( ).appendNumeric( uint64_t( 1234 ) , 10 , true );
	M_CHECK_( "1 234" );

	sbo.clear( ).appendNumeric( uint64_t( 1234 ) , 10 , true , ',' );
	M_CHECK_( "1,234" );

	sbo.clear( ).appendNumeric( uint64_t( 1234 ) , 10 , true ,
			T_Character( 0x20acu ) );
	M_CHECK_( "1\u20ac234" );

	sbo.clear( ).appendNumeric( uint64_t( 65535 ) , 2 , true , ' ' , 8 );
	M_CHECK_( "11111111 11111111" );

	sbo.clear( ).appendNumeric( uint64_t( 15 ) , 2 , true , ' ' , 1 );
	M_CHECK_( "1 1 1 1" );
}

void StringsBuilderTest::testAppendNumFloat( )
{
	T_StringBuilder sbo;
	sbo.clear( ).appendDouble( -1 ); M_CHECK_( "-1" );
	sbo.clear( ).appendDouble( 0 ); M_CHECK_( "0" );
	sbo.clear( ).appendDouble( 1234 ); M_CHECK_( "1234" );
	sbo.clear( ).appendDouble( 1.234 ); M_CHECK_( "1.234" );
	sbo.clear( ).appendDouble( 1.23456789 ); M_CHECK_( "1.23457" );
	sbo.clear( ).appendDouble( 10.23456789 ); M_CHECK_( "10.2346" );
	sbo.clear( ).appendDouble( -1e10 ); M_CHECK_( "-1e+10" );

	sbo.clear( ).appendDouble( 1 , 2 ); M_CHECK_( "1" );
	sbo.clear( ).appendDouble( 1.5 , 2 ); M_CHECK_( "1.5" );
	sbo.clear( ).appendDouble( 1.5432 , 2 ); M_CHECK_( "1.5" );
	sbo.clear( ).appendDouble( 1e20 , 2 ); M_CHECK_( "1e+20" );

	sbo.clear( ).appendDouble( 1.234 , 6 , true ); M_CHECK_( "1.234000" );
	sbo.clear( ).appendDouble( 1.23456789 , 6 , true ); M_CHECK_( "1.234568" );
	sbo.clear( ).appendDouble( 1 , 6 , true ); M_CHECK_( "1.000000" );
	sbo.clear( ).appendDouble( -1 , 6 , true ); M_CHECK_( "-1.000000" );
	sbo.clear( ).appendDouble( 1e10 , 6 , true ); M_CHECK_( "10000000000.000000" );
}
#undef M_CHECK_

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

void StringsBuilderTest::testStringConsCopy( )
{
	const char str[] = "this is a test";
	const uint32_t sz = sizeof( str ) - 1;
	T_StringBuilder sb( str );
	T_String tstr( sb );

	CPPUNIT_ASSERT_EQUAL( sz , sb.length( ) );
	CPPUNIT_ASSERT_EQUAL( sz , sb.size( ) );
	CPPUNIT_ASSERT_EQUAL( uint32_t( T_StringBuilder::C_GROWTH ) , sb.capacity( ) );
	CPPUNIT_ASSERT( sb.data( ) != nullptr );
	CPPUNIT_ASSERT( !memcmp( sb.data( ) , str , sz ) );

	CPPUNIT_ASSERT( tstr == str );
	CPPUNIT_ASSERT( tstr.data( ) != sb.data( ) );
}

void StringsBuilderTest::testStringAssCopy( )
{
	const char str[] = "this is a test";
	const uint32_t sz = sizeof( str ) - 1;
	T_StringBuilder sb( str );
	T_String tstr;

	tstr = sb;

	CPPUNIT_ASSERT_EQUAL( sz , sb.length( ) );
	CPPUNIT_ASSERT_EQUAL( sz , sb.size( ) );
	CPPUNIT_ASSERT_EQUAL( uint32_t( T_StringBuilder::C_GROWTH ) , sb.capacity( ) );
	CPPUNIT_ASSERT( sb.data( ) != nullptr );
	CPPUNIT_ASSERT( !memcmp( sb.data( ) , str , sz ) );

	CPPUNIT_ASSERT( tstr == str );
	CPPUNIT_ASSERT( tstr.data( ) != sb.data( ) );
}

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

void StringsBuilderTest::testStringConsMove( )
{
	const char str[] = "this is a test";
	T_StringBuilder sb( str );
	T_String tstr( std::move( sb ) );

	CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , sb.length( ) );
	CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , sb.size( ) );
	CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , sb.capacity( ) );
	CPPUNIT_ASSERT( sb.data( ) == nullptr );

	CPPUNIT_ASSERT( tstr == str );
}

void StringsBuilderTest::testStringAssMove( )
{
	const char str[] = "this is a test";
	T_StringBuilder sb( str );
	T_String tstr;
	
	tstr = std::move( sb );

	CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , sb.length( ) );
	CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , sb.size( ) );
	CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , sb.capacity( ) );
	CPPUNIT_ASSERT( sb.data( ) == nullptr );

	CPPUNIT_ASSERT( tstr == str );
}

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

void StringsBuilderTest::testCmpOther( )
{
	T_StringBuilder sb1( "this" ) , sb2( "that" ) , sb3( "this" );
	CPPUNIT_ASSERT( sb1 == sb3 );
	CPPUNIT_ASSERT( sb1 != sb2 );
}

void StringsBuilderTest::testCmpCString( )
{
	T_StringBuilder sb( "this" );
	CPPUNIT_ASSERT( sb == "this" );
	CPPUNIT_ASSERT( sb != "that" );
}

void StringsBuilderTest::testCmpTString( )
{
	T_StringBuilder sb( "this" );
	T_String s1( "this" ) , s2( "that" );
	CPPUNIT_ASSERT( sb == s1 );
	CPPUNIT_ASSERT( sb != s2 );
}

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

void StringsBuilderTest::testToUnsignedBasic( )
{
	T_StringBuilder sb( "123" );
	CPPUNIT_ASSERT_EQUAL( uint64_t( 123 ) , sb.toUnsignedInteger( ) );
}

void StringsBuilderTest::testToUnsignedOkValue( )
{
	bool ok;
	T_StringBuilder sb( "123" );
	CPPUNIT_ASSERT_EQUAL( uint64_t( 123 ) , sb.toUnsignedInteger( &ok ) );
	CPPUNIT_ASSERT( ok );
}

void StringsBuilderTest::testToUnsignedInvalid( )
{
	bool ok;
	T_StringBuilder sb( "123lol" );
	CPPUNIT_ASSERT_EQUAL( uint64_t( 123 ) , sb.toUnsignedInteger( &ok ) );
	CPPUNIT_ASSERT( !ok );
}

void StringsBuilderTest::testToUnsignedTooLarge( )
{
	bool ok;
	T_StringBuilder sb( "18446744073709551616" );
	CPPUNIT_ASSERT_EQUAL( uint64_t( UINT64_MAX ) , sb.toUnsignedInteger( &ok ) );
	CPPUNIT_ASSERT( !ok );
}

void StringsBuilderTest::testToUnsignedBaseOk( )
{
	bool ok;
	T_StringBuilder sb( "1a23" );
	CPPUNIT_ASSERT_EQUAL( uint64_t( 6691 ) , sb.toUnsignedInteger( &ok , 16 ) );
	CPPUNIT_ASSERT( ok );
}

void StringsBuilderTest::testToUnsignedBaseInvalid( )
{
	bool ok;
	T_StringBuilder sb( "12g3" );
	CPPUNIT_ASSERT_EQUAL( uint64_t( 18 ) , sb.toUnsignedInteger( &ok , 16 ) );
	CPPUNIT_ASSERT( !ok );
}

void StringsBuilderTest::testToUnsignedBaseAutoBinary( )
{
	bool ok;
	T_StringBuilder sb( "0b1111" );
	CPPUNIT_ASSERT_EQUAL( uint64_t( 15 ) , sb.toUnsignedInteger( &ok , 0 ) );
	CPPUNIT_ASSERT( ok );
}

void StringsBuilderTest::testToUnsignedBaseAutoOctal( )
{
	bool ok;
	T_StringBuilder sb( "0123" );
	CPPUNIT_ASSERT_EQUAL( uint64_t( 83 ) , sb.toUnsignedInteger( &ok , 0 ) );
	CPPUNIT_ASSERT( ok );
}

void StringsBuilderTest::testToUnsignedBaseAutoDecimal( )
{
	bool ok;
	T_StringBuilder sb( "123" );
	CPPUNIT_ASSERT_EQUAL( uint64_t( 123 ) , sb.toUnsignedInteger( &ok , 0 ) );
	CPPUNIT_ASSERT( ok );
}

void StringsBuilderTest::testToUnsignedBaseAutoHexa( )
{
	bool ok;
	T_StringBuilder sb( "0x123" );
	CPPUNIT_ASSERT_EQUAL( uint64_t( 291 ) , sb.toUnsignedInteger( &ok , 0 ) );
	CPPUNIT_ASSERT( ok );
}

void StringsBuilderTest::testToUnsignedBaseAutoZero( )
{
	bool ok;
	T_StringBuilder sb( "0" );
	CPPUNIT_ASSERT_EQUAL( uint64_t( 0 ) , sb.toUnsignedInteger( &ok , 0 ) );
	CPPUNIT_ASSERT( ok );
}

void StringsBuilderTest::testToUnsignedBaseAutoInvalid( )
{
	bool ok;
	T_StringBuilder sb( "0z123" );
	CPPUNIT_ASSERT_EQUAL( uint64_t( 0 ) , sb.toUnsignedInteger( &ok , 0 ) );
	CPPUNIT_ASSERT( !ok );
}

void StringsBuilderTest::testToUnsignedBaseAutoCut( )
{
	bool ok;
	T_StringBuilder sb( "0x" );
	CPPUNIT_ASSERT_EQUAL( uint64_t( 0 ) , sb.toUnsignedInteger( &ok , 0 ) );
	CPPUNIT_ASSERT( !ok );
}

void StringsBuilderTest::testToUnsignedSeparator( )
{
	bool ok;
	T_StringBuilder sb( "1 2 3" );
	CPPUNIT_ASSERT_EQUAL( uint64_t( 123 ) , sb.toUnsignedInteger( &ok , 10 , true ) );
	CPPUNIT_ASSERT( ok );
}

void StringsBuilderTest::testToUnsignedSeparatorCustom( )
{
	bool ok;
	T_StringBuilder sb( "1.2.3" );
	CPPUNIT_ASSERT_EQUAL( uint64_t( 123 ) , sb.toUnsignedInteger( &ok , 10 , true , '.' ) );
	CPPUNIT_ASSERT( ok );
}

void StringsBuilderTest::testToUnsignedSeparatorCustomUTF8( )
{
	bool ok;
	T_StringBuilder sb( "1""\xe2\x82\xac""23" );
	CPPUNIT_ASSERT_EQUAL( uint64_t( 123 ) , sb.toUnsignedInteger( &ok , 10 , true , T_Character( 0x20ac ) ) );
	CPPUNIT_ASSERT( ok );
}

void StringsBuilderTest::testToUnsignedLeadingWhitespace( )
{
	bool ok;
	T_StringBuilder sb( "    123" );
	CPPUNIT_ASSERT_EQUAL( uint64_t( 123 ) , sb.toUnsignedInteger( &ok ) );
	CPPUNIT_ASSERT( ok );
}

void StringsBuilderTest::testToUnsignedLeadingWhitespaceOnly( )
{
	bool ok;
	T_StringBuilder sb( "       " );
	CPPUNIT_ASSERT_EQUAL( uint64_t( 0 ) , sb.toUnsignedInteger( &ok ) );
	CPPUNIT_ASSERT( !ok );
}

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

void StringsBuilderTest::testToSignedBasic( )
{
	T_StringBuilder sb( "123" );
	CPPUNIT_ASSERT_EQUAL( int64_t( 123 ) , sb.toInteger( ) );
}

void StringsBuilderTest::testToSignedOkValue( )
{
	bool ok;
	T_StringBuilder sb( "123" );
	CPPUNIT_ASSERT_EQUAL( int64_t( 123 ) , sb.toInteger( &ok ) );
	CPPUNIT_ASSERT( ok );
}

void StringsBuilderTest::testToSignedPlus( )
{
	bool ok;
	T_StringBuilder sb( "+123" );
	CPPUNIT_ASSERT_EQUAL( int64_t( 123 ) , sb.toInteger( &ok ) );
	CPPUNIT_ASSERT( ok );
}

void StringsBuilderTest::testToSignedPlusMultiple( )
{
	bool ok;
	T_StringBuilder sb( "++123" );
	CPPUNIT_ASSERT_EQUAL( int64_t( 0 ) , sb.toInteger( &ok ) );
	CPPUNIT_ASSERT( !ok );
}

void StringsBuilderTest::testToSignedMinus( )
{
	bool ok;
	T_StringBuilder sb( "-123" );
	CPPUNIT_ASSERT_EQUAL( int64_t( -123 ) , sb.toInteger( &ok ) );
	CPPUNIT_ASSERT( ok );
}

void StringsBuilderTest::testToSignedMinusMultiple( )
{
	bool ok;
	T_StringBuilder sb( "--123" );
	CPPUNIT_ASSERT_EQUAL( int64_t( 0 ) , sb.toInteger( &ok ) );
	CPPUNIT_ASSERT( !ok );
}

void StringsBuilderTest::testToSignedInvalid( )
{
	bool ok;
	T_StringBuilder sb( "1lol23wut" );
	CPPUNIT_ASSERT_EQUAL( int64_t( 1 ) , sb.toInteger( &ok ) );
	CPPUNIT_ASSERT( !ok );
}

void StringsBuilderTest::testToSignedTooLarge( )
{
	bool ok;
	T_StringBuilder sb( "18446744073709551616" );
	CPPUNIT_ASSERT_EQUAL( INT64_MAX , sb.toInteger( &ok ) );
	CPPUNIT_ASSERT( !ok );
}

void StringsBuilderTest::testToSignedTooSmall( )
{
	bool ok;
	T_StringBuilder sb( "-18446744073709551616" );
	CPPUNIT_ASSERT_EQUAL( INT64_MIN , sb.toInteger( &ok ) );
	CPPUNIT_ASSERT( !ok );
}

void StringsBuilderTest::testToSignedBaseOk( )
{
	bool ok;
	T_StringBuilder sb( "1a2" );
	CPPUNIT_ASSERT_EQUAL( int64_t( 418 ) , sb.toInteger( &ok , 16 ) );
	CPPUNIT_ASSERT( ok );
}

void StringsBuilderTest::testToSignedBaseInvalid( )
{
	bool ok;
	T_StringBuilder sb( "g12" );
	CPPUNIT_ASSERT_EQUAL( int64_t( 0 ) , sb.toInteger( &ok , 16 ) );
	CPPUNIT_ASSERT( !ok );
}

void StringsBuilderTest::testToSignedBaseAutoBinary( )
{
	bool ok;
	T_StringBuilder sb( "0b11" );
	CPPUNIT_ASSERT_EQUAL( int64_t( 3 ) , sb.toInteger( &ok , 0 ) );
	CPPUNIT_ASSERT( ok );
}

void StringsBuilderTest::testToSignedBaseAutoOctal( )
{
	bool ok;
	T_StringBuilder sb( "011" );
	CPPUNIT_ASSERT_EQUAL( int64_t( 9 ) , sb.toInteger( &ok , 0 ) );
	CPPUNIT_ASSERT( ok );
}

void StringsBuilderTest::testToSignedBaseAutoDecimal( )
{
	bool ok;
	T_StringBuilder sb( "11" );
	CPPUNIT_ASSERT_EQUAL( int64_t( 11 ) , sb.toInteger( &ok , 0 ) );
	CPPUNIT_ASSERT( ok );
}

void StringsBuilderTest::testToSignedBaseAutoHexa( )
{
	bool ok;
	T_StringBuilder sb( "0x12" );
	CPPUNIT_ASSERT_EQUAL( int64_t( 18 ) , sb.toInteger( &ok , 0 ) );
	CPPUNIT_ASSERT( ok );
}

void StringsBuilderTest::testToSignedBaseAutoZero( )
{
	bool ok;
	T_StringBuilder sb( "0" );
	CPPUNIT_ASSERT_EQUAL( int64_t( 0 ) , sb.toInteger( &ok , 0 ) );
	CPPUNIT_ASSERT( ok );
}

void StringsBuilderTest::testToSignedBaseAutoInvalid( )
{
	bool ok;
	T_StringBuilder sb( "0z12" );
	CPPUNIT_ASSERT_EQUAL( int64_t( 0 ) , sb.toInteger( &ok , 0 ) );
	CPPUNIT_ASSERT( !ok );
}

void StringsBuilderTest::testToSignedBaseAutoCut( )
{
	bool ok;
	T_StringBuilder sb( "0x" );
	CPPUNIT_ASSERT_EQUAL( int64_t( 0 ) , sb.toInteger( &ok , 0 ) );
	CPPUNIT_ASSERT( !ok );
}

void StringsBuilderTest::testToSignedBaseAutoPlus( )
{
	bool ok;
	T_StringBuilder sb( "+0b11" );
	CPPUNIT_ASSERT_EQUAL( int64_t( 3 ) , sb.toInteger( &ok , 0 ) );
	CPPUNIT_ASSERT( ok );
}

void StringsBuilderTest::testToSignedBaseAutoMinus( )
{
	bool ok;
	T_StringBuilder sb( "-0b11" );
	CPPUNIT_ASSERT_EQUAL( int64_t( -3 ) , sb.toInteger( &ok , 0 ) );
	CPPUNIT_ASSERT( ok );
}

void StringsBuilderTest::testToSignedSeparator( )
{
	bool ok;
	T_StringBuilder sb( "-1 1" );
	CPPUNIT_ASSERT_EQUAL( int64_t( -11 ) , sb.toInteger( &ok , 10 , true ) );
	CPPUNIT_ASSERT( ok );
}

void StringsBuilderTest::testToSignedSeparatorCustom( )
{
	bool ok;
	T_StringBuilder sb( "-1x1" );
	CPPUNIT_ASSERT_EQUAL( int64_t( -11 ) , sb.toInteger( &ok , 10 , true , 'x' ) );
	CPPUNIT_ASSERT( ok );
}

void StringsBuilderTest::testToSignedSeparatorCustomUTF8( )
{
	bool ok;
	T_StringBuilder sb( "1""\xe2\x82\xac""23" );
	CPPUNIT_ASSERT_EQUAL( int64_t( 123 ) , sb.toInteger( &ok , 10 , true , T_Character( 0x20ac ) ) );
	CPPUNIT_ASSERT( ok );
}

void StringsBuilderTest::testToSignedLeadingWhitespace( )
{
	bool ok;
	T_StringBuilder sb( "    -   123" );
	CPPUNIT_ASSERT_EQUAL( int64_t( -123 ) , sb.toInteger( &ok ) );
	CPPUNIT_ASSERT( ok );
}

void StringsBuilderTest::testToSignedLeadingWhitespaceOnly( )
{
	bool ok;
	T_StringBuilder sb( "       " );
	CPPUNIT_ASSERT_EQUAL( int64_t( 0 ) , sb.toInteger( &ok ) );
	CPPUNIT_ASSERT( !ok );
}

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

#define M_CHECK_( STR , VAL , OK ) \
	do { \
		bool ok; \
		double v( T_StringBuilder( STR ).toDouble( &ok , '.' , true , ',' ) ); \
		CPPUNIT_ASSERT_EQUAL( double( VAL ) , v ); \
		CPPUNIT_ASSERT_EQUAL( bool( OK ) , ok ); \
	} while ( 0 )

void StringsBuilderTest::testToDouble( )
{
	M_CHECK_( "    " , 0 , false );
	M_CHECK_( "   0" , 0 , true );
	M_CHECK_( " ,,0" , 0 , false );
	M_CHECK_( "+" , 0 , false );
	M_CHECK_( "-" , 0 , false );
	M_CHECK_( "123" , 123 , true );
	M_CHECK_( ",123" , 0 , false );
	M_CHECK_( "1,2,3" , 123 , true );
	M_CHECK_( "1,2," , 0 , false );
	M_CHECK_( ",-123" , 0 , false );
	M_CHECK_( "-123" , -123 , true );
	M_CHECK_( "+123" , +123 , true );
	M_CHECK_( "+,123" , 0 , false );
	M_CHECK_( ".12" , .12 , true );
	M_CHECK_( "-.12" , -.12 , true );
	M_CHECK_( "+.12" , .12 , true );
	M_CHECK_( "." , 0 , true );
	M_CHECK_( "12." , 12 , true );
	M_CHECK_( "12e" , 0 , false );
	M_CHECK_( "12e2" , 12e2 , true );
	M_CHECK_( "12e+" , 0 , false );
	M_CHECK_( "12e-" , 0 , false );
	M_CHECK_( "12e+2" , 12e2 , true );
	M_CHECK_( "12e-2" , 12e-2 , true );
	M_CHECK_( "1.2e2" , 120 , true );
	M_CHECK_( "1.2e+2" , 120 , true );
	M_CHECK_( "1.2e-2" , 1.2e-2 , true );
	M_CHECK_( "1e3000" , std::numeric_limits< double >::infinity( ) , false );
	M_CHECK_( "-1e3000" , -std::numeric_limits< double >::infinity( ) , false );
	M_CHECK_( "1e-3000" , 0 , false );
}

#undef M_CHECK_