//////////////////////////////////////////////////////////////////////
//
/// \file
/// \brief Testing routines for slogcxx (slog-icks)
///
/// Copyright (c) 2006 Kurt Schwehr
///     Data Visualization Research Lab,
/// 	Center for Coastal and Ocean Mapping
///	University of New Hampshire.
///	http://ccom.unh.edu
///
/// \todo convert asserts to tests that call FAILED_HERE and return false
//////////////////////////////////////////////////////////////////////

#include <iostream>
#include <string>
#include <cstdlib>

#include <slogcxx.h>

// For testing do NOT add "using namespace std;"!!  By not doing this, we can tell better what is going on

#ifndef UNUSED
#ifdef __GNUC__
#define UNUSED __attribute((__unused__))
#else
/*!
  \brief GNU CC attribute to denote unused paramters in function calls.
  The attribute remove compiler warning for unused arguments and variable.  Only works
  for GNU compilers such as gcc and g++.

  http://gcc.gnu.org/onlinedocs/gcc-3.4.2/gcc/Variable-Attributes.html
*/
#define UNUSED
#endif // ifdef __GNUC__
#endif // ifndef UNUSED



/*!
  \brief Use \a 'FAILED_HERE;' to emit a string that looks like a compiler warning
  Use this to allow emacs to jump to this source line with either emacs command: C-x ` or F2
*/
/*
#define FAILED_HERE std::cerr <<  __FILE__ << ":" << __LINE__		\
			 << " failed in function " << __FUNCTION__ << std::endl;
*/
//#define FAILED_HERE std::cerr << "Hello\n";
#define FAILED_HERE std::cerr << __FILE__ << ":" << __LINE__ << ": error: failed in function " << __FUNCTION__<< std::endl;

//////////////////////////////////////////////////////////////////////
// Test functions
//////////////////////////////////////////////////////////////////////

/// test writing to a file without and with xml
bool testFile() {
  // Logging to a file
  {
    Slog l("foo.log"," ",false,false);
    l.entry(ALWAYS,"Hello World");
    l.enableXml();
    l.entry(ALWAYS,"Hello World in XML");
  }
  return true;
}

/// Try out writing to a file in xml mode with one LogState
bool testFileXml() {
  {
    Slog l("fooXml.log"," ",true,true);
    l.entry(ALWAYS,"Hello World in XML");
    {
      LogState ls(&l,"lvl_1");
      l.entry(ALWAYS,"Should be at lvl one");
    }
  }
  return true;
}

/// Try out a couple scopes
bool testHeavyScope() {
  Slog l("foo-testScoping.log");
  //l.setStateIndent("\t");
  LogState ls1(&l,"one");
  {
    l << "inside 1" << endl;
    LogState ls2(&l,"two");
    {
      l << "inside 2" << endl;
      LogState ls3(&l,"three");
      {
	l << "inside 3" << endl;
      } // 3
    } // 2
  } // 1
  return true;
}

/// Try out many scopes without xml.  Should still indent
bool testHeavyScopeNoXml() {
  Slog l("foo-testScopingNoXml.log"," ",false,false,true);
  //l.setStateIndent("\t");
  LogState ls1(&l,"one");
  {
    l << "inside 1" << endl;
    LogState ls2(&l,"two");
    {
      l << "inside 2" << endl;
      LogState ls3(&l,"three");
      {
	l << "inside 3" << endl;
      } // 3
    } // 2
  } // 1
  return true;
}


/// Make a ton of scopes and leave them without closing.  Generates a runtime warning.
bool testScope() {
  Slog l("scopeTests.log");
  l.pushState("one");
  l.pushState("2");
  l.pushState("3");
  l.pushState("four");
  l.pushState("5");
  l.pushState("6");
  l.pushState("seven");
  l.writeState();
  l.writeState(false);
  // Unbalanced pops... this generates a warning
  return true;  
}

/// Go crazy with scoping and use incl/decl and incMsg;
bool testScopeWithMsgLvl() {
  Slog l("scopeTestsWithMsgLvl.log");

  l.setLevel(TERSE);

  l.setMsgLevel(TRACE);
  l << "No" << endl;
  assert(TRACE == l.getMsgLevel());

    l.pushState("1",TERSE);
    l << "Yes" << endl;
    assert(TERSE == l.getMsgLevel());
    l.popState();
  l << "No" << endl;
  assert(TRACE == l.getMsgLevel());

  l.incMsg();
  assert(VERBOSE == l.getMsgLevel());

    l.pushState("1",BOMBASTIC);
    l << "No" << endl;
    assert(BOMBASTIC == l.getMsgLevel());

      l.pushState("2");
      l << "No" << endl;
      assert(BOMBASTIC == l.getMsgLevel());
      l << decl << "No" << decl << "No" << endl;
      assert(TRACE == l.getMsgLevel());
      l.popState();
    l << "No" << endl;
    assert(TRACE == l.getMsgLevel());

      l.pushState("2",TERSE);
      l << "Yes" << endl;
      assert(TERSE == l.getMsgLevel());
      l.popState();
    l << "No" << endl;
    assert(TRACE == l.getMsgLevel());

    l.popState();
    l << "No" << endl;
  assert(VERBOSE == l.getMsgLevel());
  return true;
}


/// One large test that does logs of stuff with dec/inc, etc
bool testBig() {
  // FIX: change asserts to tests!!!

  Slog log("foo2.log"," ",false,false,false);

  log << 1;
  log << endl;
  log << "a string";
  log << endl;
  log << 2 << " " << 3 << endl;

  log.setLevel(1);
  if (1!=log.getLevel()) {FAILED_HERE; return false;}

  log.dec();
  assert(0==log.getLevel());
  log.dec();
  assert(0==log.getLevel());

  log.setLevel(999);
  assert(999==log.getLevel());
  log.dec();
  assert(998==log.getLevel());
  log.inc();
  assert(999==log.getLevel());
  

  log.setLevel(TRACE);
  assert(log.entry(TRACE,"trace"));
  assert(!log.entry(VERBOSE,"verbose")); // Not seen
  log.inc();
  assert(log.entry(VERBOSE,"verbose after log")); // Seen

  assert(log.partial(TRACE,"tracePartial"));
  assert(log.complete());

  log.dec();
  assert(log.partial(TRACE,"a "));
  assert(!log.partial(VERBOSE,"b "));
  assert(log.partial(TRACE,"c "));
  assert(log.complete());


  log.setLevel(VERBOSE);
  log.setMsgLevel(TRACE);
  log << "Should see this"<<endl;
  log.setMsgLevel(BOMBASTIC);
  log << "Should NOT see this"<<endl;

  //
  // THIS IS THE TRUE WAY TO USE IT!!!
  //

  log.setLevel(TRACE);
  log.setMsgLevel(TRACE);
  log << "Yes " << incl << "No " << decl << "Yes!" << endl;

  log.setLevel(VERBOSE);
  log << "Yes " << incl << "YES " << decl << "Yes!" << endl;

  {
    assert(0==log.getStateDepth());
    LogState logstate1(&log,"one");
    assert(1==log.getStateDepth());
    {
      LogState logstate2(&log,"two");
      assert(2==log.getStateDepth());
      log.writeState();
      log << 2 << endl;
      // Try out an early explicit pop
      LogState logstate3(&log,"three");
      assert(3==log.getStateDepth());
      log << 3 << endl;
      log.writeState();
      logstate3.pop(); // Here is the pop
      assert(2==log.getStateDepth());
    }
    assert(1==log.getStateDepth());
  }
  assert(0==log.getStateDepth());
  return true;
}

/// This is about as simple a test as can be made
bool testSimple() {
  Slog l("fooSimple.log");
  l << "Hello World" << endl;
  return true;
}

/// Test out all the types available.
bool testTypes() {
  Slog l("types.log");
  l << "int: " << int(1) << endl;
  l << "size_t: " << size_t(9876541) << endl;
  char *cstr="c style string";
  l << cstr << endl;
  l << std::string("C++ STL string") << endl;
  l << "char: " << 'c' << endl;
  l << "short: " << short(2) << endl;
  l << "long: " << long(3) << endl;
  l << "float: " << float(4.1) << endl;
  l << "double: " << double(5.2) << endl;
  return true;
}

/// Simple test to see what happens
class whereClassTest {
public:
  /// Some method that does logging
  void doWhere(Slog &s) { s << "Call where in a class method " << WHERE << endl;}
};

/// Try out the where and WHERE calls
bool testWhere() {
  {
    Slog l("test-where-noxml.log","\t",false,false);
    l.where("a file",123456,"some function");
    l.complete();

    l.where(__FILE__,__LINE__,__FUNCTION__);
    l.partial(l.getMsgLevel()," test of the __FILE__ etc macros");
    l.complete();
  }
  {
    Slog l("test-where-xml.log","\t");
    l.where("a file",123456,"some function");
    l.complete();

    l.where(__FILE__,__LINE__,__FUNCTION__);
    l.partial(l.getMsgLevel()," test of the __FILE__ etc macros");
    l.complete();

    // This is really how it should be used
    l << "This has an embedded where "<< WHERE <<" xml tag" << endl;
    l << "Probably better practice to put the WHERE at the end " << WHERE << endl;

    whereClassTest wct;
    wct.doWhere(l);
  }
  return true;
}

bool testPointer() {
    Slog *l = new Slog("test-pointer.log");
    assert(l);
    *l << "string" << endl;
    *l << 6 << endl;
    *l << 9.99 << endl;
    delete l;
    return true;
}

//////////////////////////////////////////////////////////////////////
// main
//////////////////////////////////////////////////////////////////////

/// This is bone head unit testing
int main(UNUSED int argc, UNUSED char *argv[]) {
  bool ok = true;

  //EXIT_DEBUG("because I am too lazy to go any farther");
  // FAILED_HERE; // Uncomment to see what an error looks like


  // This will not be pretty since the loggers send stuff to cout
  if (!testSimple())	 	{FAILED_HERE; ok=false; std::cout << "testSimple ... ERROR\n";} 	else std::cout << "testSimple ... ok\n";
  if (!testWhere())	 	{FAILED_HERE; ok=false; std::cout << "testWhere ... ERROR\n";} 	else std::cout << "testWhere ... ok\n";
  if (!testTypes())	 	{FAILED_HERE; ok=false; std::cout << "testTypes ... ERROR\n";} 		else std::cout << "testTypes ... ok\n";
  if (!testHeavyScope()) 	{FAILED_HERE; ok=false; std::cout << "testHeavyScope ... ERROR\n";} 	else std::cout << "testHeaveScope ... ok\n";
  if (!testHeavyScopeNoXml()) 	{FAILED_HERE; ok=false; std::cout << "testHeavyScopeNoXML ... ERROR\n";} else std::cout << "testHeaveScopeNoXML ... ok\n";
  if (!testFileXml()) 		{FAILED_HERE; ok=false; std::cout << "testFileXml ... ERROR\n";}	else std::cout << "testHeavyXml ... ok\n";
  if (!testFile()) 		{FAILED_HERE; ok=false; std::cout << "testFile ... ERROR\n";}	 	else std::cout << "testFile ... ok\n";
  if (!testBig()) 		{FAILED_HERE; ok=false; std::cout << "testBig ... ERROR\n";}	 	else std::cout << "testBig ... ok\n";

  if (!testScopeWithMsgLvl()) {FAILED_HERE; ok=false; std::cout << "testScopeWithMsgLvl ... ERROR\n";}	 	else std::cout << "testScopeWithMsgLvl ... ok\n";

  if (!testPointer())           {FAILED_HERE; ok=false; std::cout << "testPointer ... ERROR\n";}	else std::cout << "testPointer ... ok\n";

  // std::cout << "early"<< endl;exit(EXIT_FAILURE); // Use this line to run a subset of tests

  std::cout << "  " << argv[0] << " test:  " << (ok?"ok":"failed")<<std::endl;
  return (ok?EXIT_SUCCESS:EXIT_FAILURE);
}


syntax highlighted by Code2HTML, v. 0.9.1