// ----------------------------------------------------------------------------------------
//
//  common/test/assertions.cpp
//    
//  Bob Cook Development, Robotics Library
//  http://www.bobcookdev.com/rl/
//    
//  This file provides unit testing assertion macros.
//
//  Copyright (C) 2008 Bob Cook
//
//      This program is free software; you can redistribute it and/or modify it
//      under the terms of the GNU General Public License as published by the
//      Free Software Foundation; either version 2 of the License, or (at your
//      option) any later version.
//    
//      This program is distributed in the hope that it will be useful, but 
//      WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
//      or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License 
//      for more details.
//    
//      You should have received a copy of the GNU General Public License along
//      with this program; if not, write to the Free Software Foundation, Inc., 
//      51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//
// ----------------------------------------------------------------------------------------


#include "assertions.h"

#include <assert.h>
#include <stdio.h>

#include "project_defs.h"


// ----------------------------------------------------------------------------------------

static int s_test_failure_counter = 0;

// ----------------------------------------------------------------------------------------

static void test_report_print_as_octal( uint8_t value )
{
	char   s[ 3 ] = { '0', '0', '0' };
	int8_t i = 2;

    while ( value != 0 )
	{
		assert( i >= 0 );
		s[ i ] = static_cast< char >( static_cast< uint8_t >( '0' ) + ( value % 8 ) );
		value /= 8;
		i--;
	}

	putchar( s[ 0 ] );
	putchar( s[ 1 ] );
	putchar( s[ 2 ] );
}

// ----------------------------------------------------------------------------------------

static void test_report_print_string( const char* s )
{
	if ( s == 0 )
	{
		printf( "(null string)" );
		return;
	}

	putchar( '\"' );

	while ( *s != '\0' )
	{
		if ( *s >= 0x20 && *s < 0x7f && *s != '\\' )
		{
			putchar( *s );
		}
		else
		{
			putchar( '\\' );
			switch( *s )
			{
				case '\0': putchar( '0' ); break;
				case '\f': putchar( 'f' ); break;
				case '\n': putchar( 'n' ); break;
				case '\r': putchar( 'r' ); break;
				case '\t': putchar( 't' ); break;
				case '\\': putchar( '\\' ); break;
				default:
					test_report_print_as_octal( static_cast< uint8_t >( *s ) );
					break;
			};
		}
		s++;
	}

	putchar( '\"' );
}

// ----------------------------------------------------------------------------------------

static void test_report_print_failed_file( const char* file )
{
	static const char* last_failed_file = "";
	if ( ! test_strings_equal( last_failed_file, file ) )
	{
		printf( "! test failures [file: %s]\n", file );
		last_failed_file = file;
	}
}

// ----------------------------------------------------------------------------------------

bool test_strings_equal( const char* a, const char* b )
{
	if ( a == 0 && b == 0 ) { return true; }
	if ( a == 0 || b == 0 ) { return false; }
	while ( *a != '\0' && *b != '\0' )
	{
		if ( *a++ != *b++ ) { return false; }
	}
	if ( *a == '\0' && *b == '\0' ) { return true; }
	return false;
}

// ----------------------------------------------------------------------------------------

void test_report_failed( const char* test, const char* file, int line )
{
	s_test_failure_counter++;
	test_report_print_failed_file( file );
	printf( "! ** failed [line: %d]: %s\n", line, test );
}

// ----------------------------------------------------------------------------------------

void test_report_failed_str( const char* a, const char* b, const char* file, int line )
{
	s_test_failure_counter++;
	test_report_print_failed_file( file );
	printf( "! ** failed [line: %d]: ", line );
	test_report_print_string( a );
	printf( " != " );
	test_report_print_string( b );
	putchar( '\n' );
}

// ----------------------------------------------------------------------------------------

void test_report_failed_num( int a, int b, const char* op, const char* file, int line )
{
	s_test_failure_counter++;
	test_report_print_failed_file( file );
	printf( "! ** failed [line: %d]: %d %s %d\n", line, a, op, b );
}

// ----------------------------------------------------------------------------------------

#if defined( TEST_INC_SUPPORT_DOUBLES )

void test_report_failed_num( double a, int b, const char* op, const char* file, int line )
{
	s_test_failure_counter++;
	test_report_print_failed_file( file );
	printf( "! ** failed [line: %d]: %lf %s %d\n", line, a, op, b );
}

#endif

// ----------------------------------------------------------------------------------------

#if defined( TEST_INC_SUPPORT_DOUBLES )

void test_report_failed_num( int a, double b, const char* op, const char* file, int line )
{
	s_test_failure_counter++;
	test_report_print_failed_file( file );
	printf( "! ** failed [line: %d]: %d %s %lf\n", line, a, op, b );
}

#endif

// ----------------------------------------------------------------------------------------

#if defined( TEST_INC_SUPPORT_DOUBLES )

void test_report_failed_num( double a, double b, const char* op, const char* file, int line )
{
	s_test_failure_counter++;
	test_report_print_failed_file( file );
	printf( "! ** failed [line: %d]: %lf %s %lf\n", line, a, op, b );
}

#endif

// ----------------------------------------------------------------------------------------

void test_reset_failure_count()
{
	s_test_failure_counter = 0;
}

// ----------------------------------------------------------------------------------------

int test_get_failure_count()
{
	return s_test_failure_counter;
}

// ----------------------------------------------------------------------------------------
//  end of assertions.cpp
// ----------------------------------------------------------------------------------------

