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


/* - FSPathTest ------------------------------------------------------------*/

class FSPathTest : public CppUnit::TestFixture
{
	CPPUNIT_TEST_SUITE( FSPathTest );
		CPPUNIT_TEST( testValidComponentOK );
		CPPUNIT_TEST( testValidComponentBad );

		CPPUNIT_TEST( testConstructDefault );
		CPPUNIT_TEST( testConstructValidStringRel );
		CPPUNIT_TEST( testConstructValidStringAbs );
		CPPUNIT_TEST( testConstructInvalidString );

		CPPUNIT_TEST( testCopyCons );
		CPPUNIT_TEST( testCopyAss );
		CPPUNIT_TEST( testMoveCons );
		CPPUNIT_TEST( testMoveAss );
		CPPUNIT_TEST( testSwap );

		CPPUNIT_TEST( testIsRelative );
		CPPUNIT_TEST( testIsAbsolute );
	CPPUNIT_TEST_SUITE_END( );

public:
	void testValidComponentOK( );
	void testValidComponentBad( );

	void testConstructDefault( );
	void testConstructValidStringRel( );
	void testConstructValidStringAbs( );
	void testConstructInvalidString( );

	void testCopyCons( );
	void testCopyAss( );
	void testMoveCons( );
	void testMoveAss( );
	void testSwap( );

	void testIsRelative( );
	void testIsAbsolute( );
};
CPPUNIT_TEST_SUITE_REGISTRATION( FSPathTest );

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

void FSPathTest::testValidComponentOK( )
{
	CPPUNIT_ASSERT( T_FSPath::IsValidComponent( "a" ) );
	CPPUNIT_ASSERT( T_FSPath::IsValidComponent( " " ) );
	CPPUNIT_ASSERT( T_FSPath::IsValidComponent( "a b" ) );
	CPPUNIT_ASSERT( T_FSPath::IsValidComponent( "123" ) );
	CPPUNIT_ASSERT( T_FSPath::IsValidComponent( ".test" ) );
	CPPUNIT_ASSERT( T_FSPath::IsValidComponent( "-_-" ) );
}

void FSPathTest::testValidComponentBad( )
{
	CPPUNIT_ASSERT( !T_FSPath::IsValidComponent( "a\t" ) );
	CPPUNIT_ASSERT( !T_FSPath::IsValidComponent( "a/" ) );
	CPPUNIT_ASSERT( !T_FSPath::IsValidComponent( "a\\" ) );
	CPPUNIT_ASSERT( !T_FSPath::IsValidComponent( "a*" ) );
	CPPUNIT_ASSERT( !T_FSPath::IsValidComponent( "a?" ) );
	CPPUNIT_ASSERT( !T_FSPath::IsValidComponent( "a<" ) );
	CPPUNIT_ASSERT( !T_FSPath::IsValidComponent( "a>" ) );
	CPPUNIT_ASSERT( !T_FSPath::IsValidComponent( "a|" ) );
	CPPUNIT_ASSERT( !T_FSPath::IsValidComponent( "a:" ) );
	CPPUNIT_ASSERT( !T_FSPath::IsValidComponent( "a\"" ) );
}

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

void FSPathTest::testConstructDefault( )
{
	const T_FSPath test{ };
	CPPUNIT_ASSERT( test.isValid( ) );
	CPPUNIT_ASSERT( !test.root( ) );
	CPPUNIT_ASSERT( test.components( ).empty( ) );
}

void FSPathTest::testConstructValidStringRel( )
{
	const T_FSPath test{ "this/is/a/test" };
	CPPUNIT_ASSERT( test.isValid( ) );
	CPPUNIT_ASSERT( !test.root( ) );
	CPPUNIT_ASSERT_EQUAL( 4u , test.components( ).size( ) );
	CPPUNIT_ASSERT( test.components( )[ 0 ].equals( "this" ) );
	CPPUNIT_ASSERT( test.components( )[ 1 ].equals( "is" ) );
	CPPUNIT_ASSERT( test.components( )[ 2 ].equals( "a" ) );
	CPPUNIT_ASSERT( test.components( )[ 3 ].equals( "test" ) );
}

void FSPathTest::testConstructValidStringAbs( )
{
	const T_FSPath test{ "/this/is/a/test" , T_FSPathStyle::Unix( ) };
	CPPUNIT_ASSERT( test.isValid( ) );
	CPPUNIT_ASSERT( test.root( ).equals( "/" ) );
	CPPUNIT_ASSERT_EQUAL( 4u , test.components( ).size( ) );
	CPPUNIT_ASSERT( test.components( )[ 0 ].equals( "this" ) );
	CPPUNIT_ASSERT( test.components( )[ 1 ].equals( "is" ) );
	CPPUNIT_ASSERT( test.components( )[ 2 ].equals( "a" ) );
	CPPUNIT_ASSERT( test.components( )[ 3 ].equals( "test" ) );
}

void FSPathTest::testConstructInvalidString( )
{
	const T_FSPath test{ "/<this!>/<is!>/<SPARTA!>\n" , T_FSPathStyle::Unix( ) };
	CPPUNIT_ASSERT( !test.isValid( ) );
	CPPUNIT_ASSERT( test.root( ).equals( "/" ) );
	CPPUNIT_ASSERT_EQUAL( 3u , test.components( ).size( ) );
	CPPUNIT_ASSERT( test.components( )[ 0 ].equals( "<this!>" ) );
	CPPUNIT_ASSERT( test.components( )[ 1 ].equals( "<is!>" ) );
	CPPUNIT_ASSERT( test.components( )[ 2 ].equals( "<SPARTA!>\n" ) );
}

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

void FSPathTest::testCopyCons( )
{
	char const* const tests[] = {
		"/this/is/a/test" , "this/is/a/test" , "/<this!>/<is!>/<SPARTA!>\n"
	};
	for ( char const* test : tests ) {
		const T_FSPath src{ test , T_FSPathStyle::Unix( ) };
		const T_FSPath dest{ src };
		CPPUNIT_ASSERT_EQUAL( src.isValid( ) , dest.isValid( ) );
		CPPUNIT_ASSERT( src.root( ).equals( dest.root( ) ) );

		CPPUNIT_ASSERT_EQUAL( src.components( ).size( ) , dest.components( ).size( ) );
		for ( auto i = 0u ; i < src.components( ).size( ) ; i ++ ) {
			CPPUNIT_ASSERT( src.components( )[ i ].equals( dest.components( )[ i ] ) );
		}
	}
}

void FSPathTest::testCopyAss( )
{
	char const* const tests[] = {
		"/this/is/a/test" , "this/is/a/test" , "/<this!>/<is!>/<SPARTA!>\n"
	};
	for ( char const* test : tests ) {
		const T_FSPath src{ test , T_FSPathStyle::Unix( ) };
		T_FSPath dest{ "/nope" };
		dest = src;
		CPPUNIT_ASSERT_EQUAL( src.isValid( ) , dest.isValid( ) );
		CPPUNIT_ASSERT( src.root( ).equals( dest.root( ) ) );

		CPPUNIT_ASSERT_EQUAL( src.components( ).size( ) , dest.components( ).size( ) );
		for ( auto i = 0u ; i < src.components( ).size( ) ; i ++ ) {
			CPPUNIT_ASSERT( src.components( )[ i ].equals( dest.components( )[ i ] ) );
		}
	}
}

void FSPathTest::testMoveCons( )
{
	char const* const tests[] = {
		"/this/is/a/test" , "this/is/a/test" , "/<this!>/<is!>/<SPARTA!>\n"
	};
	for ( char const* test : tests ) {
		const T_FSPath src{ test , T_FSPathStyle::Unix( ) };
		T_FSPath moved{ test };
		const T_FSPath dest{ std::move( moved ) };

		CPPUNIT_ASSERT( moved.isValid( ) );
		CPPUNIT_ASSERT( !moved.root( ) );
		CPPUNIT_ASSERT( moved.components( ).empty( ) );

		CPPUNIT_ASSERT_EQUAL( src.isValid( ) , dest.isValid( ) );
		CPPUNIT_ASSERT( src.root( ).equals( dest.root( ) ) );

		CPPUNIT_ASSERT_EQUAL( src.components( ).size( ) , dest.components( ).size( ) );
		for ( auto i = 0u ; i < src.components( ).size( ) ; i ++ ) {
			CPPUNIT_ASSERT( src.components( )[ i ].equals( dest.components( )[ i ] ) );
		}
	}
}

void FSPathTest::testMoveAss( )
{
	char const* const tests[] = {
		"/this/is/a/test" , "this/is/a/test" , "/<this!>/<is!>/<SPARTA!>\n"
	};
	for ( char const* test : tests ) {
		const T_FSPath src{ test , T_FSPathStyle::Unix( ) };
		T_FSPath moved{ test };
		T_FSPath dest{ "/nope" };
		dest = std::move( moved );

		CPPUNIT_ASSERT( moved.isValid( ) );
		CPPUNIT_ASSERT( !moved.root( ) );
		CPPUNIT_ASSERT( moved.components( ).empty( ) );

		CPPUNIT_ASSERT_EQUAL( src.isValid( ) , dest.isValid( ) );
		CPPUNIT_ASSERT( src.root( ).equals( dest.root( ) ) );

		CPPUNIT_ASSERT_EQUAL( src.components( ).size( ) , dest.components( ).size( ) );
		for ( auto i = 0u ; i < src.components( ).size( ) ; i ++ ) {
			CPPUNIT_ASSERT( src.components( )[ i ].equals( dest.components( )[ i ] ) );
		}
	}
}

void FSPathTest::testSwap( )
{
	T_FSPath a{ "\n/x" } , b{ "/x" };
	swap( a , b );

	CPPUNIT_ASSERT( a.isValid( ) );
	CPPUNIT_ASSERT( a.root( ).equals( "/" ) );
	CPPUNIT_ASSERT_EQUAL( 1u , a.components( ).size( ) );
	CPPUNIT_ASSERT( a.components( )[ 0 ].equals( "x" ) );

	CPPUNIT_ASSERT( !b.isValid( ) );
	CPPUNIT_ASSERT( !b.root( ) );
	CPPUNIT_ASSERT_EQUAL( 2u , b.components( ).size( ) );
	CPPUNIT_ASSERT( b.components( )[ 0 ].equals( "\n" ) );
	CPPUNIT_ASSERT( b.components( )[ 1 ].equals( "x" ) );
}

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

void FSPathTest::testIsRelative( )
{
	T_FSPath a{ "/test" , T_FSPathStyle::Unix( ) } ,
		 b{ "test" , T_FSPathStyle::Unix( ) } ,
		 c{ "./test" , T_FSPathStyle::Unix( ) } ,
		 d{ "/../test" , T_FSPathStyle::Unix( ) } ,
		 e{ T_FSPathStyle::Unix( ) };
	CPPUNIT_ASSERT( !a.isRelative( ) );
	CPPUNIT_ASSERT( b.isRelative( ) );
	CPPUNIT_ASSERT( c.isRelative( ) );
	CPPUNIT_ASSERT( !d.isRelative( ) );
	CPPUNIT_ASSERT( e.isRelative( ) );
}

void FSPathTest::testIsAbsolute( )
{
	T_FSPath a{ "/test" , T_FSPathStyle::Unix( ) } ,
		 b{ "test" , T_FSPathStyle::Unix( ) } ,
		 c{ "./test" , T_FSPathStyle::Unix( ) } ,
		 d{ "/../test" , T_FSPathStyle::Unix( ) } ,
		 e{ T_FSPathStyle::Unix( ) };
	CPPUNIT_ASSERT( a.isAbsolute( ) );
	CPPUNIT_ASSERT( !b.isAbsolute( ) );
	CPPUNIT_ASSERT( !c.isAbsolute( ) );
	CPPUNIT_ASSERT( d.isAbsolute( ) );
	CPPUNIT_ASSERT( !e.isAbsolute( ) );
}