#if defined WIN32
#pragma warning(disable: 4786)
#endif

#include "Test.h"

#include "Own.h"
#include "Log.h"
#include <vector>    //fix
#include <list>      //fix

namespace MM
{

class Own_test : public Test
{
public:
    Own_test (const Text & suite_name) : Test (suite_name) { }

    void run()
    {
        test1_example();                count_tests();
        test2_release();                count_tests();
        test3_vector ();                count_tests();
        test4_list   ();                count_tests();
        test5_dangerous_assignments();  count_tests();
        test6_derived_class();          //count_tests();
    }

    void test1_example();
    void test2_release();
    void test3_vector ();
    void test4_list   ();
    void test5_dangerous_assignments();
    void test6_derived_class();

    class A {};
    class B : public A {};

    class Test_class
    {
        Test_class (const Test_class &);
        const Test_class & operator = (const Test_class &);

    public:
        Test_class()    {++counter_;}
       ~Test_class()    {--counter_;}

        static int counter_;
    };
};

Own_test test_own ("correctness");

void Own_test::
test1_example ()
{
    Own <int> i (new int(123));
    TEST ("i holds 123",    i() == 123);

    i() = 555;
    TEST ("i holds 555",    i() == 555);
}

void Own_test::
test2_release ()
{
    Own <int> i (new int(123));
    TEST ("i holds 123",    i() == 123);

    i();
    TEST ("i holds 123",    i() == 123);

    i();
    TEST ("i holds 123",    i() == 123);

    Own <int>  ii (i.orphan());

    #ifndef NDEBUG

    bool logger_state = log().is_on();
    log().set_on (false);

    bool success = false;
    try
    {
        i();
        FAIL ("This point should be unreachable"
              " so that i does not hold any value now");
    }
    catch (Flaw &)
    {
        success = true;
    }
    TEST ("i does not hold 123", success);

    success = false;
    try
    {
        i.orphan();
        FAIL ("This point should be unreachable"
              " so that i was released already");
    }
    catch (Flaw &)
    {
        success = true;
    }
    TEST ("i was released already", success);

    log().set_on (logger_state);
    TEST ("ii holds 123",    ii() == 123);

    #endif//*/
}

int Own_test::Test_class::counter_ = 0;

void Own_test::
test3_vector ()
{
    using std::vector;

    vector <Own <int> > v;
    int i, size = 10;

    for (i=0;  i<size;  ++i)
        v.push_back (Own <int> (new int(i)));

    for ( i=0;  i<size;  ++i)
        TEST ("Vector contents", v[i]() == i);

    ///////////////////////////////////////////////
    {
        vector< Own<Own_test::Test_class> > t; 

        for (i=0;  i<size;  ++i)
            t.push_back (Own <Own_test::Test_class> 
                (new Own_test::Test_class));

        TEST ("", Own_test::Test_class::counter_ == size);
    }
    TEST ("", Own_test::Test_class::counter_ == 0);
}

void Own_test::
test4_list ()
{
    using namespace std;

    list <Own <int> > l;
    int i, size = 10;

    for (i=0;  i<size;  ++i)
        l.push_back (Own <int> (new int(i)));

    i = 0;
    for (list <Own <int> >::iterator it=l.begin();  it!=l.end();  ++it)
        TEST ("List contents", (*it)() == i++);

    ///////////////////////////////////////////////
    {
        list <Own <Own_test::Test_class> > t;

        for (i=0;  i<size;  ++i)
            t.push_back (Own <Own_test::Test_class> (new Own_test::Test_class));

        TEST ("", Own_test::Test_class::counter_ == size);
    }
    TEST ("", Own_test::Test_class::counter_ == 0);
}

void Own_test::
test5_dangerous_assignments ()
{
    int *i = new int (123);
    Own <int> j1(i), j2(i);    // Error: two janitors held the same object.

    bool logger_state = log().is_on();
    log().set_on (false);

    bool success = false;
    try
    {
        j2 = j1;                        // Error should be detected here.

        FAIL ("This point should be unreachable");
    }
    catch (Flaw &)
    {
        success = true;
    }
    log().set_on (logger_state);
    TEST ("Assignment of the same object to two janitors was detected", success);
}

void Own_test::
test6_derived_class()
{
    Own <A> a (new A);
    Own <B> b (new B);

//    b = a;                  // Error
    a = b;
//    Janitor< B > b2( a );   // Error
//    Janitor< int > i( a );  // Error
//    Janitor< A > i( 123 );  // Error
//    a = 123;                // Error
}

}//MM


