// treesort_test.cpp
// Glenn G. Chappell
// 21 Apr 2008
//
// For CS 311 Spring 2008
// Test program for function treesort
// Used in Assignment 7, Exercise A

// Includes for code to be tested
#include "treesort.h"   // For treesort
#include "treesort.h"   // Double inclusion test

// Includes for testing package & code common to all test programs
#include <iostream>     // for std::cout, std::endl, std::cin
#include <string>       // for std::string
#include <stdexcept>    // for std::runtime_error

// Additional includes for this test program
#include <vector>       // for std::vector
#include <list>         // for std::list
#include <deque>        // for std::deque
#include <algorithm>    // for std::stable_sort, std::equal


// ************************************************************************
// Testing Package:
//     Class Tester - For Tracking Tests
// ************************************************************************


// class Tester
// For extremely simple unit testing.
// Keeps track of number of tests and number of passes.
// Use test (with success/failure parameter) to do a test.
// Get results with numTests, numPassed, numFailed, allPassed.
// Restart testing with reset.
// Invariants:
//     countTests_ == number of tests (calls to test) since last reset.
//     countPasses_ == number of times function test called with true param
//      since last reset.
//     0 <= countPasses_ <= countTests_.
//     tolerance_ >= 0.
class Tester {

// ***** Tester: ctors, dctor, op= *****
public:

    // Default ctor
    // Sets countTests_, countPasses_ to zero, tolerance_ to given value
    // Pre: None.
    // Post:
    //     numTests == 0, countPasses == 0, tolerance_ == abs(theTolerance)
    // Does not throw (No-Throw Guarantee)
    Tester(double theTolerance = 0.0000001)
        :countTests_(0),
         countPasses_(0),
         tolerance_(theTolerance >= 0 ? theTolerance : -theTolerance)
    {}

    // Compiler-generated copy ctor, copy op=, dctor are used

// ***** Tester: general public functions *****
public:

    // test
    // Handles single test, param indicates pass/fail
    // Pre: None.
    // Post:
    //     countTests_ incremented
    //     countPasses_ incremented if (success)
    //     Message indicating test name (if given)
    //      and pass/fail printed to cout
    // Does not throw (No-Throw Guarantee)
    //  - Assuming exceptions have not been turned on for cout.
    void test(bool success,
              const std::string & testName = "")
    {
        ++countTests_;
        if (success) ++countPasses_;

        std::cout << "    ";
        if (testName != "")
        {
            std::cout << "Test: "
                      << testName
                      << " - ";
        }
        std::cout << (success ? "passed" : "********** FAILED **********")
                  << std::endl;
    }

    // ftest
    // Does single floating-point test.
    // Tests passes iff difference of first two values is <= tolerance.
    // Pre: None.
    // Post:
    //     countTests_ incremented
    //     countPasses_ incremented if (abs(val1-val2) <= tolerance_)
    //     Message indicating test name (if given)
    //      and pass/fail printed to cout
    // Does not throw (No-Throw Guarantee)
    void ftest(double val1,
               double val2,
               const std::string & testName = "")
    { test(val1-val2 <= tolerance_ && val2-val1 <= tolerance_, testName); }

    // reset
    // Resets *this to default constructed state
    // Pre: None.
    // Post:
    //     countTests_ == 0, countPasses_ == 0
    // Does not throw (No-Throw Guarantee)
    void reset()
    {
        countTests_ = 0;
        countPasses_ = 0;
    }

    // numTests
    // Returns the number of tests that have been done since last reset 
    // Pre: None.
    // Post:
    //     return == countTests_
    // Does not throw (No-Throw Guarantee)
    int numTests() const
    { return countTests_; }

    // numPassed
    // Returns the number of tests that have passed since last reset
    // Pre: None.
    // Post:
    //     return == countPasses_
    // Does not throw (No-Throw Guarantee)
    int numPassed() const
    { return countPasses_; }

    // numFailed
    // Returns the number of tests that have not passed since last reset
    // Pre: None.
    // Post:
    //     return + countPasses_ == numTests_
    // Does not throw (No-Throw Guarantee)
    int numFailed() const
    { return countTests_ - countPasses_; }

    // allPassed
    // Returns true if all tests since last reset have passed
    // Pre: None.
    // Post:
    //     return == (countPasses_ == countTests_)
    // Does not throw (No-Throw Guarantee)
    bool allPassed() const
    { return countPasses_ == countTests_; }

    // setTolerance
    // Sets tolerance_ to given value
    // Pre: None.
    // Post:
    //     tolerance_ = abs(theTolerance)
    // Does not throw (No-Throw Guarantee)
    void setTolerance(double theTolerance)
    { tolerance_ = (theTolerance >= 0 ? theTolerance : -theTolerance); }

// ***** Tester: data members *****
private:

    int countTests_;    // Number of tests done since last reset
    int countPasses_;   // Number of tests passed since last reset
    double tolerance_;  // Tolerance for floating-point near-equality tests

};  // end class Tester


// ************************************************************************
// Testing Package:
//     Class TypeCheck - Helper Class for Type Checking
// ************************************************************************


// class TypeCheck
// This class exists in order to have static member function check, which
// takes a parameter of a given type, by reference. Objects of type
// TypeCheck<T> cannot be created.
// Usage:
//     TypeCheck<MyType>::check(x)
//     returns true if the type of x is (MyType) or (const MyType),
//     otherwise false.
// Invariants: None.
// Requirements on Types: None.
template<typename T>
class TypeCheck {

private:

    // Uncopyable class. Do not define copy ctor, copy assn.
    TypeCheck(const TypeCheck &);
    TypeCheck<T> & operator=(const TypeCheck &);

    // Compiler-generated dctor is used (but irrelevant).

public:

    // check
    // The function and function template below simulate a single function
    // that takes a single parameter, and returns true iff the parameter has
    // type T or (const T).

    // check (reference-to-const T)
    // Pre: None.
    // Post:
    //     Return is true.
    // Does not throw (No-Throw Guarantee)
    static bool check(const T & param)
    { return true; }

    // check (reference-to-const non-T)
    // Pre: None.
    // Post:
    //     Return is false.
    // Requirements on types: None.
    // Does not throw (No-Throw Guarantee)
    template <typename OtherType>
    static bool check(const OtherType & param)
    { return false; }

};  // End class TypeCheck


// ************************************************************************
// Testing Package:
//     Class Counter - Helper Class for Counting Calls & Objects, Throwing
// ************************************************************************


// class Counter
// Item type for counting ctor, dctor, op= calls, counting existing
//  objects, and possibly throwing on copy. Has operator< (which always
//  returns false), allowing it to be the value type of a sorted container.
// If static member copyThrow_ is set, then copy ctor and copy assn throw
//  std::runtime_error.
// Increments static data member ctorCount_ on default construction and
//  successful copy construction. Increments static data member assnCount_
//  on successful copy assignment. Increments static data member
//  dctorCount_ on destruction.
// Increments static data member existing_ on construction, and decrements
//  it on destruction.
// Static data member maxExisting_ is highest value of existing_ since last
//  reset, or start of program if reset has never been called.
// Invariants:
//     Counter::existing_ is number of existing objects of this class.
//     Counter::ctorCount_ is number of successful ctor calls since
//      most recent call to reset, or start of program if reset has never
//      been called.
//     Counter::dctorCount_ is (similarly) number of dctor calls.
//     Counter::assnCount_ is (similarly) number of copy assn calls.
//     Counter::maxExisting_ is (similarly) highest value existing_ has
//      assumed.
class Counter {

// ***** Counter: Ctors, dctor, op= *****
public:

    // Default ctor
    // Pre: None.
    // Post:
    //     (ctorCount_ has been incremented.)
    //     (existing_ has been incremented.)
    Counter()
    {
        ++existing_;
        if (existing_ > maxExisting_)
            maxExisting_ = existing_;
        ++ctorCount_;
    }

    // Copy ctor
    // Pre: None.
    // Post:
    //     (ctorCount_ has been incremented.)
    //     (existing_ has been incremented.)
    Counter(const Counter & other)
    {
        if (copyThrow_)
            throw std::runtime_error("C");
        ++existing_;
        if (existing_ > maxExisting_)
            maxExisting_ = existing_;
        ++ctorCount_;
    }

    // Copy assignment
    // Pre: None.
    // Post:
    //     Return value is *this.
    //     (assnCount_ has been incremented.)
    Counter & operator=(const Counter & rhs)
    {
        if (copyThrow_)
            throw std::runtime_error("A");
        ++assnCount_;
        return *this;
    }

    // Dctor
    // Pre: None.
    // Post:
    //     (dctorCount_ has been incremented.)
    //     (existing_ has been decremented.)
    ~Counter()
    {
        --existing_;
        ++dctorCount_;
    }

// ***** Counter: Functions dealing with count *****
public:

    // reset
    // Pre: None.
    // Post:
    //     maxExisting_ == existing_.
    //     ctorCount_ == 0.
    //     dctorCount_ == 0.
    //     assnCount_ == 0.
    //     copyThrow_ == shouldThrow.
    static void reset(bool shouldThrow = false)
    {
        maxExisting_ = existing_;
        ctorCount_ = 0;
        dctorCount_ = 0;
        assnCount_ = 0;
        copyThrow_ = shouldThrow;
    }

    // getExisting
    // Pre: None.
    // Post:
    //     return == existing_.
    static int getExisting()
    { return existing_; }

    // getMaxExisting
    // Pre: None.
    // Post:
    //     return == maxExisting_.
    static int getMaxExisting()
    { return maxExisting_; }

    // getCtorCount
    // Pre: None.
    // Post:
    //     return == ctorCount_.
    static int getCtorCount()
    { return ctorCount_; }

    // getDctorCount
    // Pre: None.
    // Post:
    //     return == dctorCount_.
    static int getDctorCount()
    { return dctorCount_; }

    // getAssnCount
    // Pre: None.
    // Post:
    //     return == assnCount_.
    static int getAssnCount()
    { return assnCount_; }

    // setCopyThrow
    // Pre: None.
    // Post:
    //     copyThrow_ == shouldThrow
    static void setCopyThrow(bool shouldThrow)
    { copyThrow_ = shouldThrow; }

// ***** Counter: Data Members *****
private:

    static int existing_;     // # of existing objects
    static int maxExisting_;  // Max # of existing objects
    static int ctorCount_;    // # of successful (non-throwing) ctor calls
    static int dctorCount_;   // # of dctor calls
    static int assnCount_;    // # of successful (non-throwing) copy = calls
    static bool copyThrow_;   // true if copy operations (ctor, =) throw

};  // End class Counter

// Definition of static data member of class Counter
int Counter::existing_ = 0;
int Counter::maxExisting_ = 0;
int Counter::ctorCount_ = 0;
int Counter::dctorCount_ = 0;
int Counter::assnCount_ = 0;
bool Counter::copyThrow_ = false;


// operator< (Counter)
// Fake operator< for Counter class
// Returns false (which is legal; all objects of type Counter are
//  equivalent).
// Pre: None.
// Post:
//     Return value == false.
// Does not throw (No-Throw Guarantee)
bool operator<(const Counter & a,
               const Counter & b)
{ return false; }


// ************************************************************************
// Utility Functions/Classes for This Testing Program
// ************************************************************************


// class OnlyLessThan
// object with limited operator availability.
// Has only default ctor, big 3, and operator< (always returns false).
// In particular, does not have operators ==, !=, >, >=.
// Invariants: None.
class OnlyLessThan {

    // Compiler-generated default ctor, copy ctor, copy =, dctor used

};  // End class OnlyLessThan

// operator< (OnlyLessThan)
// Fake operator< for OnlyLessThan class
// Returns false (which is legal; all objects of type OnlyLessThan are
//  equivalent).
// Pre: None.
// Post:
//     Return value == false.
// Does not throw (No-Throw Guarantee)
bool operator<(const OnlyLessThan & a,
               const OnlyLessThan & b)
{ return false; }


// class MyPair
// Holds pair of ints
// operator== is as usual, operator< only compairs first item in each pair
// Invariants: None.
class MyPair {

public:

    MyPair(int theX = 0,
           int theY = 0)
        :x_(theX),
         y_(theY)
    {}

    // Compiler-generated copy ctor, copy =, dctor used

    // getX
    // returns x_
    // Pre: None.
    // Post:
    //     Return value == x_.
    // Does not throw (No-Throw Guarantee)
    int getX() const
    { return x_; }

    // getY
    // returns y_
    // Pre: None.
    // Post:
    //     Return value == y_.
    // Does not throw (No-Throw Guarantee)
    int getY() const
    { return y_; }

private:

    int x_;  // First item in pair
    int y_;  // Second item in pair

};  // End class MyPair


// operator== (MyPair)
// The usual == op for a pair: compares both items.
// Pre: None.
// Post:
//     Return value == (a.x_ == b.x_ && a.y_ == b.y_).
bool operator==(const MyPair & a,
                const MyPair & b)
{ return a.getX() == b.getX() && a.getY() == b.getY(); }

// operator< (MyPair)
// Calls op< on x_ only.
// Pre: None.
// Post:
//     Return value == (a.x_ == b.x_).
bool operator<(const MyPair & a,
               const MyPair & b)
{ return a.getX() < b.getX(); }


// ************************************************************************
// Test Suite Functions
// ************************************************************************


// test_treesort_type_requirements
// Test suite for function treesort, type requirements
// Pre: None.
// Post:
//     Pass/fail status of tests have been registered with t.
//     Appropriate have been messages printed to cout.
// Does not throw (No-Throw Guarantee)
void test_treesort_type_requirements(Tester & t)
{
    std::cout << "Test Suite: function treesort, type requirements" << std::endl;

    // Works with minimal-functionality type (passes if it compiles)
    std::vector<OnlyLessThan> v(100);
    treesort(v.begin(), v.end());
    t.test(true, "Compiles with value type having minimal functionality");
    // NOTE: We could have used class Counter as the value type above.
    //  However, since Counter is officially part of a separate package
    //  (our testing package), which might have additional functionality
    //  added without considering the needs of this code, it seemed wiser
    //  to write a separate low-functionality item class).
}


// test_treesort_basic_int
// Test suite for function treesort, basic functionality, sorting ints
// Pre: None.
// Post:
//     Pass/fail status of tests have been registered with t.
//     Appropriate have been messages printed to cout.
// Does not throw (No-Throw Guarantee)
void test_treesort_basic_int(Tester & t)
{
    std::cout << "Test Suite: function treesort, basic functionality - sorting ints" << std::endl;

    // Value type for this test suite
    typedef int val;

    const int DATASIZE = 20;
    val data[DATASIZE] = { 3, 6, 19, -2, 8, 6, 7, 1, 141, -2, -200, 4, 6, 6, 11, -5, 32, 2, 7, 0 };
    // Make copies of data
    const std::vector<val> dataCheck(data, data+DATASIZE);
    std::vector<val> data_v(data, data+DATASIZE);
    std::deque<val> data_d(data, data+DATASIZE);
    std::list<val> data_l(data, data+DATASIZE);

    // Sorted data (all but first, last)
    std::vector<val> dataSorted1(data, data+DATASIZE);
    std::stable_sort(dataSorted1.begin()+1, dataSorted1.end()-1);

    // Sorted data (all)
    std::vector<val> dataSorted2(data, data+DATASIZE);
    std::stable_sort(dataSorted2.begin(), dataSorted2.end());

    // Test: vector empty range
    treesort(data_v.begin()+7, data_v.begin()+7);
    t.test(std::equal(data_v.begin(), data_v.end(), dataCheck.begin()), "vector: empty range");

    // Test: vector one item
    treesort(data_v.begin()+7, data_v.begin()+8);
    t.test(std::equal(data_v.begin(), data_v.end(), dataCheck.begin()), "vector: one item");

    // Test: vector nearly all
    treesort(data_v.begin()+1, data_v.end()-1);
    t.test(std::equal(data_v.begin(), data_v.end(), dataSorted1.begin()), "vector: nearly all");

    // Test: vector all
    treesort(data_v.begin(), data_v.end());
    t.test(std::equal(data_v.begin(), data_v.end(), dataSorted2.begin()), "vector: all");

    // Test: deque empty range
    treesort(data_d.begin()+7, data_d.begin()+7);
    t.test(std::equal(data_d.begin(), data_d.end(), dataCheck.begin()), "deque: empty range");

    // Test: deque one item
    treesort(data_d.begin()+7, data_d.begin()+8);
    t.test(std::equal(data_d.begin(), data_d.end(), dataCheck.begin()), "deque: one item");

    // Test: deque nearly all
    treesort(data_d.begin()+1, data_d.end()-1);
    t.test(std::equal(data_d.begin(), data_d.end(), dataSorted1.begin()), "deque: nearly all");

    // Test: deque all
    treesort(data_d.begin(), data_d.end());
    t.test(std::equal(data_d.begin(), data_d.end(), dataSorted2.begin()), "deque: all");

    std::list<val>::iterator it1, it2;

    // Test: list empty range
    it1 = data_l.begin();
    std::advance(it1, 3);
    it2 = data_l.begin();
    std::advance(it2, 3);
    treesort(it1, it2);
    t.test(std::equal(data_l.begin(), data_l.end(), dataCheck.begin()), "list: empty range");

    // Test: list one item
    ++it2;
    treesort(it1, it2);
    t.test(std::equal(data_l.begin(), data_l.end(), dataCheck.begin()), "list: one item");

    // Test: list nearly all
    it1 = data_l.begin();
    ++it1;
    it2 = data_l.end();
    --it2;
    treesort(it1, it2);
    t.test(std::equal(data_l.begin(), data_l.end(), dataSorted1.begin()), "list: nearly all");

    // Test: list all
    treesort(data_l.begin(), data_l.end());
    t.test(std::equal(data_l.begin(), data_l.end(), dataSorted2.begin()), "list: all");
}


// test_treesort_basic_double
// Test suite for function treesort, basic functionality, sorting doubles
// Pre: None.
// Post:
//     Pass/fail status of tests have been registered with t.
//     Appropriate have been messages printed to cout.
// Does not throw (No-Throw Guarantee)
void test_treesort_basic_double(Tester & t)
{
    std::cout << "Test Suite: function treesort, basic functionality - sorting doubles" << std::endl;

    // Value type for this test suite
    typedef double val;

    const int DATASIZE = 20;
    val data[DATASIZE] = { 3.1, 6.2, 19.3, -2.4, 8.5, 6.6, 7.7, 1.8, 141.9, -2.0, -200.1, 4.2, 6.3, 6.4, 11.5, -5.6, 32.7, 2.8, 7.9, 0.0 };
    // Make copies of data
    const std::vector<val> dataCheck(data, data+DATASIZE);
    std::vector<val> data_v(data, data+DATASIZE);
    std::deque<val> data_d(data, data+DATASIZE);
    std::list<val> data_l(data, data+DATASIZE);

    // Sorted data (all but first, last)
    std::vector<val> dataSorted1(data, data+DATASIZE);
    std::stable_sort(dataSorted1.begin()+1, dataSorted1.end()-1);

    // Sorted data (all)
    std::vector<val> dataSorted2(data, data+DATASIZE);
    std::stable_sort(dataSorted2.begin(), dataSorted2.end());

    // Test: vector empty range
    treesort(data_v.begin()+7, data_v.begin()+7);
    t.test(std::equal(data_v.begin(), data_v.end(), dataCheck.begin()), "vector: empty range");

    // Test: vector one item
    treesort(data_v.begin()+7, data_v.begin()+8);
    t.test(std::equal(data_v.begin(), data_v.end(), dataCheck.begin()), "vector: one item");

    // Test: vector nearly all
    treesort(data_v.begin()+1, data_v.end()-1);
    t.test(std::equal(data_v.begin(), data_v.end(), dataSorted1.begin()), "vector: nearly all");

    // Test: vector all
    treesort(data_v.begin(), data_v.end());
    t.test(std::equal(data_v.begin(), data_v.end(), dataSorted2.begin()), "vector: all");

    // Test: deque empty range
    treesort(data_d.begin()+7, data_d.begin()+7);
    t.test(std::equal(data_d.begin(), data_d.end(), dataCheck.begin()), "deque: empty range");

    // Test: deque one item
    treesort(data_d.begin()+7, data_d.begin()+8);
    t.test(std::equal(data_d.begin(), data_d.end(), dataCheck.begin()), "deque: one item");

    // Test: deque nearly all
    treesort(data_d.begin()+1, data_d.end()-1);
    t.test(std::equal(data_d.begin(), data_d.end(), dataSorted1.begin()), "deque: nearly all");

    // Test: deque all
    treesort(data_d.begin(), data_d.end());
    t.test(std::equal(data_d.begin(), data_d.end(), dataSorted2.begin()), "deque: all");

    std::list<val>::iterator it1, it2;

    // Test: list empty range
    it1 = data_l.begin();
    std::advance(it1, 3);
    it2 = data_l.begin();
    std::advance(it2, 3);
    treesort(it1, it2);
    t.test(std::equal(data_l.begin(), data_l.end(), dataCheck.begin()), "list: empty range");

    // Test: list one item
    ++it2;
    treesort(it1, it2);
    t.test(std::equal(data_l.begin(), data_l.end(), dataCheck.begin()), "list: one item");

    // Test: list nearly all
    it1 = data_l.begin();
    ++it1;
    it2 = data_l.end();
    --it2;
    treesort(it1, it2);
    t.test(std::equal(data_l.begin(), data_l.end(), dataSorted1.begin()), "list: nearly all");

    // Test: list all
    treesort(data_l.begin(), data_l.end());
    t.test(std::equal(data_l.begin(), data_l.end(), dataSorted2.begin()), "list: all");
}


// test_treesort_object_count
// Test suite for function treesort, object counts
// Pre: None.
// Post:
//     Pass/fail status of tests have been registered with t.
//     Appropriate have been messages printed to cout.
// Does not throw (No-Throw Guarantee)
void test_treesort_object_count(Tester & t)
{
    std::cout << "Test Suite: function treesort, object counts" << std::endl;

    const int SIZE = 100;
    std::vector<Counter> vc(SIZE);
    Counter::reset();

    treesort(vc.begin(), vc.end());
    t.test(Counter::getMaxExisting() >= 2*SIZE, "at least n additional objects used in sorting");
    t.test(Counter::getExisting() == SIZE, "all additional objects destroyed");

}


// test_treesort_stability
// Test suite for function treesort, stability
// Pre: None.
// Post:
//     Pass/fail status of tests have been registered with t.
//     Appropriate have been messages printed to cout.
// Does not throw (No-Throw Guarantee)
void test_treesort_stability(Tester & t)
{
    std::cout << "Test Suite: function treesort, stability" << std::endl;

    const int SIZE = 16;

    int dataX[SIZE] = { 8, 7, 7, 6, 2, 3, 2, 4, 8, 3, 1, 1, 4, 5, 6, 5 };
    int dataY[SIZE] = { 1, 1, 2, 1, 1, 1, 2, 1, 2, 2, 1, 2, 2, 1, 2, 2 };

    std::vector<MyPair> vp1(SIZE);
    for (int i = 0; i < SIZE; ++i)
        vp1[i] = MyPair(dataX[i], dataY[i]);

    std::vector<MyPair> vp2(vp1);

    treesort(vp1.begin(), vp1.end());
    std::stable_sort(vp2.begin(), vp2.end());
    t.test(std::equal(vp1.begin(), vp1.end(), vp2.begin()), "treesort is stable");
}


// test_treesort
// Test suite for function treesort
// Uses other test-suite functions
// Pre: None.
// Post:
//     Pass/fail status of tests have been registered with t.
//     Appropriate have been messages printed to cout.
// Does not throw (No-Throw Guarantee)
void test_treesort(Tester & t)
{
    // Do all the test suites
    std::cout << "TEST SUITES FOR FUNCTION treesort" << std::endl;
    test_treesort_type_requirements(t);
    test_treesort_basic_int(t);
    test_treesort_basic_double(t);
    test_treesort_object_count(t);
    test_treesort_stability(t);
}


// ************************************************************************
// Main program
// ************************************************************************


// main
// Runs function treesort test suite, prints results to cout.
int main()
{
    Tester t;
    test_treesort(t);

    std::cout << std::endl;
    if (t.allPassed())
    {
        std::cout << "All tests successful" 
                  << std::endl;
    }
    else
    {
        std::cout << "Tests ********** UNSUCCESSFUL **********"
                  << std::endl;
    }
    std::cout << std::endl;

    std::cout << "Press ENTER to quit ";
    while (std::cin.get() != '\n') ;

    return 0;
}

