245 lines
7 KiB
C++
245 lines
7 KiB
C++
#include <lw/lib/SRDPreproc.hh>
|
|
#include <lw/lib/SRDText.hh>
|
|
#include <lw/lib/SRDIO.hh>
|
|
#include <cppunit/extensions/HelperMacros.h>
|
|
using namespace lw;
|
|
|
|
|
|
class SRDPreprocCoreTest : public CppUnit::TestFixture
|
|
{
|
|
CPPUNIT_TEST_SUITE( SRDPreprocCoreTest );
|
|
CPPUNIT_TEST( testEmptyInput );
|
|
CPPUNIT_TEST( testBasicInput );
|
|
CPPUNIT_TEST( testCommentsSkipped );
|
|
CPPUNIT_TEST( testCommandWordInInput );
|
|
CPPUNIT_TEST( testBadCommand );
|
|
CPPUNIT_TEST( testBadVariable );
|
|
CPPUNIT_TEST( testUnterminatedLists );
|
|
CPPUNIT_TEST( testTooManyErrorsInside );
|
|
CPPUNIT_TEST( testTooManyErrorsAtEnd );
|
|
CPPUNIT_TEST_SUITE_END( );
|
|
|
|
public:
|
|
void testEmptyInput( );
|
|
void testBasicInput( );
|
|
void testCommentsSkipped( );
|
|
void testCommandWordInInput( );
|
|
void testBadCommand( );
|
|
void testBadVariable( );
|
|
void testUnterminatedLists( );
|
|
void testTooManyErrorsInside( );
|
|
void testTooManyErrorsAtEnd( );
|
|
};
|
|
CPPUNIT_TEST_SUITE_REGISTRATION( SRDPreprocCoreTest );
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
// M_PRINTERR_( index ) - Print an error message
|
|
#define M_PRINTERR_( IDX ) \
|
|
do { \
|
|
auto const& _e( errors[ IDX ] ); \
|
|
char err[ _e.error( ).size( ) + 1 ]; \
|
|
err[ sizeof( err ) - 1 ] = 0; \
|
|
memcpy( err , _e.error( ).data( ) , \
|
|
sizeof( err ) - 1 ); \
|
|
printf( "ERR %s l. %u c. %lu\n" , err , \
|
|
_e.location( ).line( ) , \
|
|
_e.location( ).character( ) ); \
|
|
} while ( 0 )
|
|
|
|
// M_CKERR_( index , string , line , character ) - Check an error
|
|
#define M_CKERR_( IDX , STR , L , C ) \
|
|
do { \
|
|
auto const& _e( errors[ IDX ] ); \
|
|
CPPUNIT_ASSERT( T_String( STR ) == _e.error( ) ); \
|
|
CPPUNIT_ASSERT_EQUAL( uint32_t( L ) , \
|
|
_e.location( ).line( ) ); \
|
|
CPPUNIT_ASSERT_EQUAL( size_t( C ) , \
|
|
_e.location( ).character( ) ); \
|
|
} while ( 0 )
|
|
|
|
namespace {
|
|
|
|
T_SRDList process(
|
|
char const* const input ,
|
|
T_SRDErrors& errors ,
|
|
const bool clearFlushToken = true )
|
|
{
|
|
T_SRDMemoryTarget mt( false );
|
|
mt.clearFlushToken( clearFlushToken );
|
|
mt.start( errors );
|
|
|
|
T_SRDPreprocessorConfig cmd;
|
|
cmd.addBuiltinCommands( );
|
|
T_SRDPreprocessor pp( cmd , mt );
|
|
T_SRDLexer lexer( T_String( "test" ) , errors , pp );
|
|
pp.start( errors );
|
|
char const* ptr = input;
|
|
while ( *ptr != 0 ) {
|
|
lexer.processCharacter( *ptr ++ );
|
|
}
|
|
lexer.processEnd( );
|
|
mt.end( errors );
|
|
|
|
return mt.list( );
|
|
}
|
|
|
|
bool checkMatch( T_SRDList const& expected , T_SRDList const& actual )
|
|
{
|
|
const uint32_t nExpected( expected.size( ) );
|
|
const uint32_t nActual( actual.size( ) );
|
|
|
|
bool ok( nExpected == nActual );
|
|
const uint32_t nCheck( std::min( nExpected , nActual ) );
|
|
for ( uint32_t i = 0 ; i < nCheck ; i ++ ) {
|
|
T_SRDToken const& tExpected( expected[ i ] );
|
|
T_SRDToken const& tActual( actual[ i ] );
|
|
if ( tExpected.type( ) != tActual.type( ) ) {
|
|
std::cerr << "Expected token at "
|
|
<< tExpected.location( ).line( ) << ":"
|
|
<< tExpected.location( ).character( )
|
|
<< " has type " << int( tExpected.type( ) ) << "; actual token has type "
|
|
<< int( tActual.type( ) ) << "\n";
|
|
ok = false;
|
|
} else if ( tExpected.stringValue( ) != tActual.stringValue( ) ) {
|
|
std::cerr << "Expected token at "
|
|
<< tExpected.location( ).line( ) << ":"
|
|
<< tExpected.location( ).character( )
|
|
<< " has different text\n";
|
|
ok = false;
|
|
}
|
|
}
|
|
|
|
if ( nExpected != nActual ) {
|
|
std::cerr << "List size mismatch (" << nExpected << " expected, "
|
|
<< nActual << " found)\n";
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
bool check( char const* expected , T_SRDList const& actual )
|
|
{
|
|
T_SRDMemoryTarget mt( false );
|
|
T_SRDErrors errors;
|
|
T_SRDLexer lexer( T_String( "expected" ) , errors , mt );
|
|
mt.start( errors );
|
|
char const* ptr = expected;
|
|
while ( *ptr != 0 ) {
|
|
lexer.processCharacter( *ptr ++ );
|
|
}
|
|
lexer.processEnd( );
|
|
|
|
return checkMatch( mt.list( ) , actual );
|
|
}
|
|
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
void SRDPreprocCoreTest::testEmptyInput( )
|
|
{
|
|
T_SRDErrors errors;
|
|
T_SRDList output( process( "" , errors ) );
|
|
CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , errors.size( ) );
|
|
CPPUNIT_ASSERT( check( "" , output ) );
|
|
}
|
|
|
|
void SRDPreprocCoreTest::testBasicInput( )
|
|
{
|
|
T_SRDErrors errors;
|
|
T_SRDList output( process( "this is a simple test 123 \"yes\" ( a test )" , errors ) );
|
|
CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , errors.size( ) );
|
|
CPPUNIT_ASSERT( check( "this is a simple test 123 \"yes\" ( a test )" , output ) );
|
|
}
|
|
|
|
void SRDPreprocCoreTest::testCommentsSkipped( )
|
|
{
|
|
T_SRDErrors errors;
|
|
T_SRDList output( process( "a {comment!} ( is #skipped!\n)" , errors ) );
|
|
CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , errors.size( ) );
|
|
CPPUNIT_ASSERT( check( "a ( is )" , output ) );
|
|
}
|
|
|
|
void SRDPreprocCoreTest::testCommandWordInInput( )
|
|
{
|
|
T_SRDErrors errors;
|
|
T_SRDList output( process( "-blah" , errors ) );
|
|
CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , errors.size( ) );
|
|
CPPUNIT_ASSERT( check( "-blah" , output ) );
|
|
}
|
|
|
|
void SRDPreprocCoreTest::testBadCommand( )
|
|
{
|
|
T_SRDErrors errors;
|
|
T_SRDList output( process( "( -blah )" , errors ) );
|
|
CPPUNIT_ASSERT_EQUAL( uint32_t( 1 ) , errors.size( ) );
|
|
M_CKERR_( 0 , "unknown command" , 1 , 3 );
|
|
CPPUNIT_ASSERT( check( "( -blah )" , output ) );
|
|
}
|
|
|
|
void SRDPreprocCoreTest::testBadVariable( )
|
|
{
|
|
T_SRDErrors errors;
|
|
T_SRDList output( process( "$blah does not exist" , errors ) );
|
|
CPPUNIT_ASSERT_EQUAL( uint32_t( 1 ) , errors.size( ) );
|
|
M_CKERR_( 0 , "unknown variable" , 1 , 1 );
|
|
CPPUNIT_ASSERT( check( "does not exist" , output ) );
|
|
}
|
|
|
|
void SRDPreprocCoreTest::testUnterminatedLists( )
|
|
{
|
|
T_SRDErrors errors;
|
|
T_SRDList output( process( "( ( ) ( (" , errors ) );
|
|
CPPUNIT_ASSERT_EQUAL( uint32_t( 3 ) , errors.size( ) );
|
|
M_CKERR_( 0 , "unterminated list" , 1 , 1 );
|
|
M_CKERR_( 1 , "unterminated list" , 1 , 7 );
|
|
M_CKERR_( 2 , "unterminated list" , 1 , 9 );
|
|
CPPUNIT_ASSERT( check( "( ( ) ( ( ) ) )" , output ) );
|
|
}
|
|
|
|
namespace {
|
|
|
|
T_StringBuilder RepeatError_( char const* string )
|
|
{
|
|
T_StringBuilder sb;
|
|
for ( uint32_t i = 0 ; i < T_SRDErrors::MAX_ERRORS * 2 ; i ++ ) {
|
|
if ( i > 0 ) {
|
|
sb << ' ';
|
|
}
|
|
sb << string;
|
|
}
|
|
sb << " x" << '\0';
|
|
return sb;
|
|
}
|
|
|
|
}
|
|
|
|
void SRDPreprocCoreTest::testTooManyErrorsInside( )
|
|
{
|
|
T_StringBuilder input( RepeatError_( "$x" ) );
|
|
T_SRDErrors errors;
|
|
T_SRDList output( process( input.data( ) , errors ) );
|
|
CPPUNIT_ASSERT_EQUAL( uint32_t( T_SRDErrors::MAX_ERRORS + 1 ) , errors.size( ) );
|
|
M_CKERR_( T_SRDErrors::MAX_ERRORS , "too many errors" , 1 , T_SRDErrors::MAX_ERRORS * 3 - 2 );
|
|
CPPUNIT_ASSERT( check( "" , output ) );
|
|
}
|
|
|
|
void SRDPreprocCoreTest::testTooManyErrorsAtEnd( )
|
|
{
|
|
T_StringBuilder input( RepeatError_( "(" ) );
|
|
T_StringBuilder expectedOutput;
|
|
for ( uint32_t i = 0 ; i < T_SRDErrors::MAX_ERRORS * 2 ; i ++ ) {
|
|
expectedOutput << '(';
|
|
}
|
|
expectedOutput << 'x';
|
|
for ( uint32_t i = 0 ; i < T_SRDErrors::MAX_ERRORS * 2 ; i ++ ) {
|
|
expectedOutput << ')';
|
|
}
|
|
expectedOutput << '\0';
|
|
|
|
T_SRDErrors errors;
|
|
T_SRDList output( process( input.data( ) , errors ) );
|
|
CPPUNIT_ASSERT_EQUAL( uint32_t( T_SRDErrors::MAX_ERRORS + 1 ) , errors.size( ) );
|
|
M_CKERR_( T_SRDErrors::MAX_ERRORS , "too many errors" , 1 , T_SRDErrors::MAX_ERRORS * 2 - 1 );
|
|
CPPUNIT_ASSERT( check( expectedOutput.data( ) , output ) );
|
|
}
|