2005/5/9

     
 

TestRunner.cpp

artefaktur
// -*- mode:C++; tab-width:2; c-basic-offset:2; indent-tabs-mode:nil -*- 
//
// Copyright (C) 2000-2005 by Roger Rene Kommer / artefaktur, Kassel, Germany.
// 
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public License (LGPL).
// 
// 
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the 
// License ACDK-FreeLicense document enclosed in the distribution
// for more for more details.
// This file is part of the Artefaktur Component Development Kit:
//                         ACDK
// 
// Please refer to
// - http://www.acdk.de
// - http://www.artefaktur.com
// - http://acdk.sourceforge.net
// for more information.
// 
// $Header: /cvsroot/acdk/acdk/acdk_core/src/acdk/tools/aunit/TestRunner.cpp,v 1.35 2005/04/21 13:30:40 kommer Exp $

#include "TestRunner.h"
#include "TestConsoleListener.h"
#include "TestDebuggerListener.h"
#include "TestHtmlReport.h"

#include <acdk/lang/System.h>
#include <acdk/lang/CmdLineParser.h>
#include <acdk/lang/CmdLineParseException.h>
#include <acdk/io/FileWriter.h>
#include <acdk/io/NullWriter.h>
#include <acdk/io/File.h>
#include <acdk/io/TeeCharWriter.h>

namespace acdk {
namespace tools {
namespace aunit {

//static 
//core_vector<RTest> TestRunner::_tests(0);
using namespace acdk::io;

//static 
RTestArray 
TestRunner::_getTests()
{
  static RTestArray _stests = new TestArray(0);
  return _stests;
}


bool 
TestRunner::executeTest(IN(RTest) test, IN(RTestResult) result)
{
  test->run(result);
  /*
  @todo replace with TestReport
  _countTest += test->testCount();
  _countRunTest += result.runTests();
  _countFailure += result.testFailures();
  _countError += result.testErrors();
  if (_print_summary == true)
  {
    ::acdk::lang::System::out->print(
      "=========================================\n"
      " Test " + test->getName() + " result:\n");
    ::acdk::lang::System::out->println(
      "Tests=[" + String::valueOf(test->testCount()) + "]  Run=[" + result.runTests() 
      + "]  Failed=[" + result.testFailures() + "]  Error=[" + result.testErrors() + "]\n");
  }
  */
  return result->wasSuccessful();
}

bool TestRunner::executeTests(IN(RTestResult) result)
{
  
  bool erg = true;
  for (int i = 0; i < _getTests().length(); i++) 
  {
    RTest testToRun = _getTests()[i];
    
    if (instanceof(testToRun, TestSuite) == true) 
      testToRun = RTestSuite(testToRun)->suite();

    
    erg &= executeTest(testToRun, result);
  }
  return erg;
}

void TestRunner::listTests(IN(RTest) test)
{
  if (instanceof(test, TestSuite) == true) 
  {
    RTest sl = RTestSuite(test)->suite();
    if (sl.impl() != test.impl()) 
    {
      //System::out->println("TestSuite: " + test->getName());
      listTests(sl);
    } 
    else 
    {
      System::out->println("TestSuite: " + test->getName());
      RTestArray ta = RTestSuite(test)->tests();
      for (int i = 0; i < ta->length(); i++) 
      {
        listTests(ta[i]);
      }
    }
  } else if (instanceof(test, TestCase) == true) {
    System::out->println("TestCase:  " + test->getName());
  } else {
    System::out->println("Test:      " + test->getName());
  }
}

void TestRunner::listTests()
{
  RTestArray tests = _getTests();
  for (int i = 0; i < tests.length(); i++) 
  {
    listTests(tests[i]);
  }
}

bool TestRunner::executeTest(IN(RString) instrtest, IN(RTestResult) result)
{
  RString strtest = instrtest->replace("::", ".");
  RTestArray _tests = _getTests();
  for (int i = 0; i < _tests.length(); i++) 
  {
    if (_tests[i]->getName()->equals(strtest) == true) {
      if (instanceof(_tests[i], TestSuite) == true) 
        return executeTest(RTestSuite(_tests[i])->suite(), result);
      return executeTest(_tests[i], result);
    }
    if (instanceof(_tests[i], TestSuite) == true) {
      RTest ts = RTestSuite(_tests[i])->suite();
      RTestArray ta = RTestSuite(ts)->tests();
      for (int j = 0; j < ta->length(); j++) 
      {
        if (ta[j]->getName()->equals(strtest) == true) 
          return executeTest(ta[j], result);
      }
    }
  }
  System::out->println("Test not found: " + strtest);
  return false;
}

int TestRunner::executeTests(IN(RStringArray) args)
{
  CmdLineParser cmdparser;
  int printFlags = ReportAllSummary | ReportTestDetails | ReportStartEndTest;
  int reportFlags = ReportAllSummary | ReportTestDetails | ReportTestSuccess | ReportStartEndTest;
  bool printHtmlReport = false;
  try {
    cmdparser.addOption("-test-verbose", "-verbose", false, "prints status information for each test (default)");
    cmdparser.addOption("-test-print-summary", "-print-summary", false, "prints status information for each test (default)");
    cmdparser.addOption("-test-quiet", "-quite", false, "prints no status information for each test");
    cmdparser.addOption("-test-out", "-out", true, "Where to redirect out. 'nul' discarges the output. <filenename> append to file. By default output to console");
    cmdparser.addOption("-test-err", "-err", true, "Where to redirect err. 'nul' discarges the output. <filenename> append to file. By default output to console");
    cmdparser.addOption("-test-list", "-list", false, "List available tests.");
    cmdparser.addOption("-test-abortonfail", "-abortonfail", false, "Abort all Test if one test fails");
    cmdparser.addOption("-test-htmlreport", "-htmlreport", false, "Abort all Test if one test fails");
    cmdparser.addOption("-test-interactive", "-interactive", false, "Allow to run user interactive tests");
    
    ::acdk::util::RProperties props = cmdparser.parse(System::getProperties(), args, false, true);
    if (props->getProperty("-list") != Nil) 
    {
      listTests();
      return 0;
    }
    if (props->getProperty("-verbose") != Nil) 
    {
      printFlags |= ReportAllSummary | ReportTestDetails | ReportTestSuccess | ReportStartEndTest;
    } 
    else if (props->getProperty("-quite") != Nil) 
    {
      printFlags = 0;
    } 
    else if (props->getProperty("-print-summary") != Nil)
    {
      printFlags |= ReportAllSummary;
    }
    
    if (props->getProperty("-out") != Nil) 
    {
      RString of = props->getProperty("-out");
      if (of->equals("nul") == true)
        System::setOut(new ::acdk::io::PrintWriter((acdk::io::RWriter)new ::acdk::io::NullWriter()));
      else {
        System::setOut(new ::acdk::io::PrintWriter((acdk::io::RWriter)new ::acdk::io::FileWriter(of, true, true)));
      }
    }
    if (props->getProperty("-err") != Nil) 
    {
      RString of = props->getProperty("-err");
      if (of->equals("nul") == true)
        System::setErr(new ::acdk::io::PrintWriter((acdk::io::RWriter)new ::acdk::io::NullWriter()));
      else {
        System::setErr(new ::acdk::io::PrintWriter((acdk::io::RWriter)new ::acdk::io::FileWriter(of, true, true)));
      }
    }
    if (props->getProperty("-htmlreport") != Nil) 
    {
      printHtmlReport = true;
      
    }
    if (props->getProperty("-interactive") != Nil) 
    {
      TestCase::TestInBatchMode = false;
    }
  
    if (props->getProperty("-abortonfail") != Nil) 
    {
        _abortonfail = true;
    }
  } catch (RCmdLineParseException ex) {
    System::out->println(ex->getMessage());
    return 1;
  }
  RCharWriter errwriter = System::err->getOut();
  RCharWriter outwriter = System::out->getOut();
  RStringWriter buferr = new StringWriter();
  RStringWriter bufout = new StringWriter();
  System::err->setOut(new TeeCharWriter(&outwriter, &bufout));
  System::out->setOut(new TeeCharWriter(&errwriter, &buferr));
  RTestResult result = new TestResult(bufout, buferr);
  result->addTestListener(new TestConsoleListener(System::out, printFlags));
  result->addTestListener(new TestDebuggerListener(printFlags | ReportTestSuccess));
  bool erg = true;
  try {
    if (args->length() <= 1)
    {
      erg = executeTests(result);
    }
    else
    {
      for (int i = 1; i < args->length(); i++) 
      {
        erg &= executeTest(args[i], result);
        if (erg == false && _abortonfail == true)
          break;
      }
    }  
    if (printHtmlReport == true)
    {
      RString fileName = SBSTR(System::getAcdkHome() << acdk::io::File::separatorChar() << "testreports" <<  acdk::io::File::separatorChar() << System::getModuleName() << ".html");
      TestHtmlReport(reportFlags, fileName).print(result->getResults());
    }
    return (erg == true ? 0 : 1);
  } catch (RThrowable ex) {
    ex->printStackTrace();
    System::out->println("Unhandled exception " + ex->getClass()->getName() + " in TestRunner.executeTests: " + ex->getMessage());
  /* must not handle otherwise NullPointer doesn't work
  } catch (...) {
    System::out->println("Unhandled unknown exception in TestRunner.executeTests");
  */
  }
  
  return 1;
}



//static 
int TestRunner::testmain(RStringArray args)
{
   TestRunner trunner;
   return trunner.executeTests(args);
}


} // namespace aunit
} // namespace tools
} // acdk