/** @file

    $Id$

    $Date$
    $Author$

    @copy © Marc Wäckerlin
    @license LGPL, see file <a href="license.html">COPYING</a>

    $Log$
    Revision 1.2  2005/04/07 20:42:38  marc
    renamed loggerhierarchy from mrw.gccfunctiontrace to mrw.fn

    Revision 1.1  2005/03/11 21:07:55  marc
    initial version


         1         2         3         4         5         6         7         8
    5678901234567890123456789012345678901234567890123456789012345678901234567890
*/

#include <log4cxx/propertyconfigurator.h>
#include <log4cxx/helpers/properties.h>
#include <cppunit/TestFixture.h>
#include <cppunit/ui/text/TestRunner.h>
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <mrw/file.hpp>
#include <mrw/regexp.hpp>
#include <mrw/string.hpp>
#include <mrw/stacktrace.hpp>
#include <map>

namespace HalloWelt {
  
  class A {
    public:
      void method() {
      }
  };

  void fn() {
    A().method();
    A().method();
  }

  void fn1() {
    fn();
    fn();
  }

}

void anotherFunction() {
  HalloWelt::fn1();
}

#ifdef _REENTRANT
#include <boost/thread/thread.hpp>
//#include <unistd.h> // sleep, the one from boost::thread does not work!
class Thread {
  public:
    void operator()() {
      anotherFunction();
    }
};
#endif

namespace mrw {
  class AutoFunctionTraceLog4CxxTest: public CppUnit::TestFixture {
    public:
      void testcase() {
        mrw::StackTrace::createSymtable();
#ifdef _REENTRANT
        try {mrw::File::remove("mrwautofunctiontracelog4cxx_test-mt.log");}
#else
        try {mrw::File::remove("mrwautofunctiontracelog4cxx_test.log");}
#endif
        catch (...) {}
        log4cxx::helpers::Properties properties;
        properties.setProperty("log4j.rootLogger",
                               "OFF, A1");
        properties.setProperty("log4j.logger.mrw.fn",
                               "DEBUG");
        properties.setProperty("log4j.logger.mrw.fn.log4cxx",
                               "OFF");
        properties.setProperty("log4j.logger.mrw.fn.boost",
                               "OFF");
        properties.setProperty("log4j.logger.mrw.fn.Thread",
                               "OFF");
        properties.setProperty("log4j.logger.mrw.fn.mrw",
                               "OFF");
        properties.setProperty("log4j.logger.mrw.fn.std",
                               "OFF");
        properties.setProperty("log4j.logger.mrw.fn.new",
                               "OFF");
        properties.setProperty("log4j.logger.mrw.fn.CppUnit",
                               "OFF");
        properties.setProperty("log4j.logger.mrw.fn.__gnu_cxx",
                               "OFF");
        properties.setProperty("log4j.appender.A1",
                               "org.apache.log4j.FileAppender");
        properties.setProperty("log4j.appender.A1.layout",
                               "org.apache.log4j.PatternLayout");
#ifdef _REENTRANT
        properties.setProperty("log4j.appender.A1.layout.ConversionPattern",
                               "%t-%-27c%m%n");
        properties.setProperty("log4j.appender.A1.filename",
                               "mrwautofunctiontracelog4cxx_test-mt.log");
#else
        properties.setProperty("log4j.appender.A1.layout.ConversionPattern",
                               "%-27c%m%n");
        properties.setProperty("log4j.appender.A1.filename",
                               "mrwautofunctiontracelog4cxx_test.log");
#endif
        log4cxx::PropertyConfigurator::configure(properties);
#ifdef _REENTRANT
        // sleep(4); // to be reproducable, wait for "main" flag
        Thread threadFunction;
        boost::thread::thread thread1(threadFunction);
        boost::thread::thread thread2(threadFunction);
        boost::thread::thread thread3(threadFunction);
#endif
        anotherFunction();
#ifdef _REENTRANT
        thread1.join();
        thread2.join();
        thread3.join();
#endif
      }
      void checkfile() {
        mrw::RegExp match
          ("^mrw\\.fn\\.anotherFunction     ( ? ? ?)\\\\ anotherFunction\\(\\)\n"
           "mrw\\.fn\\.HalloWelt\\.fn1       \\1 \\\\ HalloWelt::fn1\\(\\)\n"
           "mrw\\.fn\\.HalloWelt\\.fn        \\1  \\\\ HalloWelt::fn\\(\\)\n"
           "mrw\\.fn\\.HalloWelt\\.A\\.method  \\1   \\\\ HalloWelt::A::method\\(\\)\n"
           "mrw\\.fn\\.HalloWelt\\.A\\.method  \\1   / HalloWelt::A::method\\(\\)\n"
           "mrw\\.fn\\.HalloWelt\\.A\\.method  \\1   \\\\ HalloWelt::A::method\\(\\)\n"
           "mrw\\.fn\\.HalloWelt\\.A\\.method  \\1   / HalloWelt::A::method\\(\\)\n"
           "mrw\\.fn\\.HalloWelt\\.fn        \\1  / HalloWelt::fn\\(\\)\n"
           "mrw\\.fn\\.HalloWelt\\.fn        \\1  \\\\ HalloWelt::fn\\(\\)\n"
           "mrw\\.fn\\.HalloWelt\\.A\\.method  \\1   \\\\ HalloWelt::A::method\\(\\)\n"
           "mrw\\.fn\\.HalloWelt\\.A\\.method  \\1   / HalloWelt::A::method\\(\\)\n"
           "mrw\\.fn\\.HalloWelt\\.A\\.method  \\1   \\\\ HalloWelt::A::method\\(\\)\n"
           "mrw\\.fn\\.HalloWelt\\.A\\.method  \\1   / HalloWelt::A::method\\(\\)\n"
           "mrw\\.fn\\.HalloWelt\\.fn        \\1  / HalloWelt::fn\\(\\)\n"
           "mrw\\.fn\\.HalloWelt\\.fn1       \\1 / HalloWelt::fn1\\(\\)\n"
           "mrw\\.fn\\.anotherFunction     \\1/ anotherFunction\\(\\)\n$");
#ifdef _REENTRANT
        std::string log(mrw::File::read("mrwautofunctiontracelog4cxx_test-mt.log"));
        std::map<unsigned long, std::string> logs;
        for (std::string::size_type pos(0), last(0);
             (pos=log.find('\n', pos+1))!=std::string::npos; last=pos) {
          std::string::size_type dash(log.find('-', last));
          CPPUNIT_ASSERT(dash!=std::string::npos);
          logs[mrw::to<unsigned long>(log.substr(last, dash-last))] +=
            log.substr(dash+1, pos-dash);
        }
        CPPUNIT_ASSERT(logs.size()==4); // 4 threads
        for (std::map<unsigned long, std::string>::iterator it(logs.begin());
             it!=logs.end(); ++it)
          CPPUNIT_ASSERT(match(it->second));
        mrw::File::remove("mrwautofunctiontracelog4cxx_test-mt.log");
#else
        CPPUNIT_ASSERT(match(mrw::File::read("mrwautofunctiontracelog4cxx_test.log")));
        mrw::File::remove("mrwautofunctiontracelog4cxx_test.log");
#endif
      }
      CPPUNIT_TEST_SUITE(AutoFunctionTraceLog4CxxTest);
      CPPUNIT_TEST(testcase);
      CPPUNIT_TEST(checkfile);
      CPPUNIT_TEST_SUITE_END();
  };
  CPPUNIT_TEST_SUITE_REGISTRATION(AutoFunctionTraceLog4CxxTest);
}

int main() {
  CppUnit::TextUi::TestRunner runner;
  runner.addTest(CppUnit::TestFactoryRegistry::getRegistry().makeTest());
  return runner.run() ? 0 : 1;
}