#include "Test.h"
#include "Test_suite.h"

#include "Log.h"
#include "Text_output.h"
#include "Timer.h"

#include <typeinfo>       //BCB

namespace MM
{

Test::
Test() 
: 
    out_(0), passed_(0), failed_(0), 
    counter_(0), no_testing_(false), duration_(0.) 
{
}

Test::
Test (const Text & suite_name)
    : out_(0), passed_(0), failed_(0), 
    counter_(0), no_testing_(false), duration_(0.)
{
    Test_suite &suite = Test_suite::singleton( suite_name );
    suite.add_test( *this );
}

Test::
~Test () {out_ = 0; passed_ = 0; failed_ = 0; counter_ = 0; duration_ = 0.;}

void Test::
run_test ()
{
    passed_ = failed_ = counter_ = 0;
    no_testing_ = false;

    try
    {
        Timer timer;
        run();
        duration_ = timer.elapsed ();
    }
    catch (DbC::Contract_violation &)
    {
		log() << "\n";
        crash();   
    }
    catch (Flaw & f)
    {
		log() << "\n\n" << "Test crash:\n" 
            << "flaw - " 
            << f.message() << "\n\n";
        crash();   
    }
    catch (char * e)
    {
		log() << "\n\n" << "Test crash:\n" << e << "\n\n";
        crash();   
    }
    catch (std::exception & e)
    {
		log() << "\n\n" << "Test crash:\n" 
            << "standart exception - " << typeid(e).name() << "\n"
            << e.what() << "\n\n";
        crash();   
    }
    catch (...)
    {
		log() << "\n\n" << "Test crash:\nUnknown exception\n\n";
        crash();   
    }//*/

    report();
}

void Test::
count_tests ()
{
    if (passed_ + failed_ == counter_)
        no_testing_ = true;

    counter_ = passed_ + failed_;
}

void Test::
test (const Text & assertion, 
      bool         condition, 
      const Text & condition_str,
      const char*  file_name, 
      int          line_number)
{
    if( condition ) 
        ++passed_;
    else            
        fail( assertion + "\n("+condition_str+")", file_name, line_number);
}

void Test::
double_equal (double expected, double actual, double threshold, 
			  const char* file_name, int line_number)
{
    if ((expected-actual)*(expected-actual) < (threshold*threshold)) 
        ++passed_;
    else            
        fail (Text() + "Two double values must be equal\n("
		      + Text(expected) + " == " + Text(actual) +")", 
			  file_name, line_number);
}

void Test::
fail (const Text & condition_str,
      const char*  file_name, 
      int          line_number)
{
    REQUIRE ("Output was initialized", out_ != 0);

    ++failed_;

    Text message = Text() 
        + typeid(*this).name() + " failure: \n" 
        + condition_str + ",\n"
        + file_name 
        + " (line " + line_number + ")";

    log() << "\n" << message << "\n\n";
    out_->line( message );
}

void Test::
crash ()
{
    REQUIRE ("Output was initialized", out_ != 0);

    ++failed_;
    Text message = Text() + typeid(*this).name() + " crash! \n";
    log() << "\n" << message << "\n\n";
    out_->line( message );
}

void Test::
report () const
{
    REQUIRE ("Output was initialized", out_ != 0);

    /*log() << Text()
        + "Failed: "  + failed_ + "\t"
        + "Passed: "  + passed_ + "\t"
        + typeid(*this).name()
        + "\t\t- " + Text (duration_, 2) + " sec";

    if (no_testing_)
        log() << "   NO TESTING!";

    log() << "\n";//*/

    Text text = Text()
        + "Failed "  + failed_ + "  "
        + "Passed "  + passed_ + " ";

    while (text.length() < 22)
        text += " ";

    text += typeid(*this).name();
    text += " ";

    while (text.length() < 59)
        text += " ";

    if (duration_ > 10e-3)
        text += Text (duration_, 2) + " sec";
    else
        text += "0.    sec";

    if (no_testing_)
        text += " NO TESTING!";

    log() << text << "\n";
}

}//MM

