// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.

#pragma once

#include <iostream>
#include <iomanip>
#include <exception>
#include <sstream>
#include <utility>
#include <memory>
#include <cstddef>

struct empty_testcase{ void run(){} const char* name(){return 0;} };

template <std::size_t offset>
struct testcase : empty_testcase{};


template <std::size_t begin, std::size_t end>
struct testrange {
    void run(std::size_t& pass, std::size_t& fail) 
    {
        using namespace std;
        {   testcase<begin> a_case;
            if (a_case.name()) {
                std::size_t p=0, f=0;
                cout << "TEST: Running " << a_case.name() << endl;
                try {
                    a_case.run();
                    ++p;
                } catch (logic_error& e) {
                    cerr << "ERRORS:" << endl;
                    cerr << "  " << e.what() << endl;
                    ++f;
                }
                pass += p; fail += f;
            }
        }
        const std::size_t rem = (end-begin-1);
        testrange<begin+1, begin+1+rem/2>().run(pass, fail);
        testrange<begin+1+rem/2, end>().run(pass, fail);
    }
};

template <std::size_t begin>
struct testrange<begin,begin> {
    void run(std::size_t& pass, std::size_t& fail) {};
};

#define TEST(fun_name) \
void fun_name (); \
template <> \
struct testcase<__LINE__> { \
    const char* name() { return(#fun_name); } \
    void run() { fun_name(); } \
}; \
void fun_name()

#define Q_(e) #e
#define Q(e)  Q_(e)
#define TASSERT(expr) \
    { auto e = (expr); if (!e) { throw std::logic_error(__FILE__ "(" Q(__LINE__) "): TASSERT("#expr")"); }  }

struct errmsg 
{
    std::shared_ptr<std::stringstream> msg;
    errmsg() : msg(new std::stringstream)
    {}

    template <class T>
    errmsg& operator<<(T value)
    {
        (*msg) << value;
        return *this;
    }
    std::string str() { return msg->str(); }
};

#define TEST_WHERE  __FILE__ "(" Q(__LINE__) "): "
#define VERIFY(expr) \
    { auto e = (expr); if (!e) { throw std::logic_error(TEST_WHERE "VERIFY("#expr")"); }  }
#define VERIFY_EQ(expected, actual) \
    { auto e = (expected); auto a = (actual); \
      if (!(e == a)) { \
        throw std::logic_error( \
          (errmsg() << TEST_WHERE << "(" << e << ")!=(" << a << ") in VERIFY_EQ("#expected","#actual")").str() );}}