// -*- mode:C++; tab-width:2; c-basic-offset:2; indent-tabs-mode:nil -*-
//
// Copyright (C) 2000 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_wx/src/acdk/tools/aunit/guitestrunner/TestRunnerFrame.h,v 1.13 2005/04/10 12:52:40 kommer Exp $
#ifndef acdk_tools_aunit_guitestrunner_TestRunnerFrame_h
#define acdk_tools_aunit_guitestrunner_TestRunnerFrame_h
#include "TestTreeCtrl.h"
#include "GuiTestListener.h"
#include <acdk/io/TeeCharWriter.h>
#include <acdk/cfgscript/Script.h>
#include <acdk/wx/TextCtrlCharWriter.h>
#include <acdk/wx/Gauge.h>
#include <acdk/wx/Timer.h>
#include <acdk/wx/Frame.h>
#include <acdk/wx/TextCtrl.h>
#include <acdk/wx/StaticText.h>
#include <acdk/wx/Button.h>
#include <acdk/wx/ToolBar.h>
#include <acdk/wx/Notebook.h>
#include <acdk/wx/HtmlWindow.h>
#include <acdk/wx/SplitterWindow.h>
namespace acdk {
namespace tools {
namespace aunit {
/**
A GUI application for running ACDK Unit Tests
*/
namespace guitestrunner {
//using namespace acdk::wx;
//using namespace acdk::tools::aunit;
#include "help.xpm"
#include "run.xpm"
enum Ids
{
ClearLogId = 1001,
MenuQuitId,
MenuAboutId,
TbHelpId,
TestTreeId,
TestRepId,
TestLogId,
RunTestId,
CurrentTestId,
StResultId,
RunTimerId,
RunBtnId,
TbRunId
};
ACDK_DECL_CLASS(BufferedCharWriter);
class BufferedCharWriter
: extends acdk::lang::Object
, implements acdk::io::CharWriter
{
public:
StringBuffer _buffer;
BufferedCharWriter()
{
}
void writeChar(char c)
{
SYNCTHIS();
_buffer.append(c);
}
void writeChar(ucchar c)
{
SYNCTHIS();
_buffer.append(c);
}
void writeString(IN(RString) str)
{
SYNCTHIS();
_buffer.append(str);
}
RString fetchBuffer()
{
SYNCTHIS();
RString ret = _buffer.toString();
_buffer.set("");
return ret;
}
void flush() {}
void close() {}
};
class TestRunnerFrame
: extends Frame
{
RTestTreeCtrl _treectrl;
RTextCtrl _console;
RMenu _treeMenu;
RTimer _testOutputTimer;
RBufferedCharWriter _consoleWriter;
RTestRunThread _testRunThread;
RStaticText _stSelectedTest;
RStaticText _stResult;
RButton _runButton;
RTestResult _testResult;
RGuiTestListener _guiTestListener;
//RHtmlWindow _htmlLogWindow;
RTreeItemId _runnedTreeItemId;
RGauge _progress;
bool _runNextSibling;// @todo = false doesn't working!
int _curErrsAndFailures;
RTestResultEntryArray _allTestResults;
bool _stopTests;
bool _abortTest;
RTextCtrl _testRep;
RTextCtrl _testLog;
acdk::cfgscript::RProps testCfg;
acdk::util::RProperties _testEnviron;
RTextCtrl _cfgTextCtrl;
RNotebook _notebook;
public:
TestRunnerFrame(IN(RString) title)
: Frame(Nil, -1, title, new Point(100, 100), new Size(800, 600))
{
_testEnviron = (acdk::util::RProperties)System::getEnvironment()->clone();
testCfg = new acdk::cfgscript::Props();
loadCfg();
_runNextSibling = false;
_stopTests = false;
_abortTest = false;
_curErrsAndFailures = 0;
RMenuBar menuBar = new MenuBar();
RMenu menuFile = new Menu();
menuFile->append(ClearLogId, "Clear Log", "Clear Log Window");
menuFile->appendSeparator();
menuFile->append(MenuQuitId, "E&xit\tAlt-X", "Quit this program");
menuBar->append(menuFile, "&File");
RMenu helpMenu = new Menu();
helpMenu->append(MenuAboutId, "&About...\tCtrl-A", "Show about dialog");
menuBar->append(helpMenu, "&Help");
setMenuBar(menuBar);
connect(CommandEvent::EvtCommandMenuSelected, MenuQuitId, (ObjectEventFunction)&TestRunnerFrame::onMenuQuit);
connect(CommandEvent::EvtCommandMenuSelected, MenuAboutId, (ObjectEventFunction)&TestRunnerFrame::onMenuAbout);
connect(CommandEvent::EvtCommandMenuSelected, ClearLogId, (ObjectEventFunction)&TestRunnerFrame::onMenuClear);
connect(CommandEvent::EvtCommandMenuSelected, RunTestId, (ObjectEventFunction)&TestRunnerFrame::onTestRun);
RToolBar tb = createToolBar();
tb->setWindowStyle(TbText | Tb3dbuttons);
RBitmap helpbm = new Bitmap(help_xpm);
RToolBarToolBase tbb = tb->addTool(TbHelpId, "Help", helpbm, "Display short help");
RBitmap runpbm = new Bitmap(run_xpm);
tbb = tb->addTool(TbRunId, "Run", runpbm, "Run selected Test");
connect(CommandEvent::EvtCommandMenuSelected, TbHelpId, (ObjectEventFunction)&TestRunnerFrame::onMenuAbout);
connect(CommandEvent::EvtCommandMenuSelected, TbRunId, (ObjectEventFunction)&TestRunnerFrame::onTestRun);
tb->realize();
RSplitterWindow splitter = new SplitterWindow(this);
_treectrl = new TestTreeCtrl(&splitter, TestTreeId, new Point(0, 0), new Size(200, 200), testCfg);
_notebook = new Notebook(&splitter, -1);
RSplitterWindow rsplt = new SplitterWindow(&_notebook);
_notebook->addPage(&rsplt, "Execute");
RSplitterWindow testlogsplitter = new SplitterWindow(&_notebook);
_testRep = new TextCtrl(&testlogsplitter, TestRepId, "", Point::defaultPosition(), Size::defaultSize(),
TeMultiline | TeAutoScroll | TeDontwrap);
_testLog = new TextCtrl(&testlogsplitter, TestLogId, "", Point::defaultPosition(), Size::defaultSize(),
TeMultiline | TeAutoScroll | TeDontwrap);
_notebook->addPage(&testlogsplitter, "Test Logs");
testlogsplitter->splitHorizontally(&_testRep, &_testLog, 130);
//_htmlLogWindow = new HtmlWindow(&_notebook, -1);
//_notebook->addPage(&_htmlLogWindow, "HTML Log");
RPanel testResultPanel = new Panel(&rsplt, 0, 0, 400, 100);
_console = new TextCtrl(&rsplt, -1, "", Point::defaultPosition(), Size::defaultSize(),
TeMultiline | TeAutoScroll | TeDontwrap | TeRich2);
_consoleWriter = new BufferedCharWriter();
splitter->splitVertically(&_treectrl, &_notebook, 300);
rsplt->splitHorizontally(&testResultPanel, &_console, 130);
_runButton = new Button(&testResultPanel, RunBtnId, "Run Test", new Point(10, 10), new Size(100, 40));
connect(CommandEvent::EvtCommandButtonClicked, RunBtnId, (ObjectEventFunction)&TestRunnerFrame::onTestRun);
_progress = new Gauge(&testResultPanel, -1, 100, new Point(10,60), new Size(400, 40), GaSmooth | BorderNone );
_stSelectedTest = new StaticText(&testResultPanel, CurrentTestId, "<selected Test>", new Point(120, 25));
_stResult = new StaticText(&testResultPanel, StResultId, "", new Point(10, 100));
_treeMenu = new Menu();
_treeMenu->append(RunTestId, "Run Test");
_testOutputTimer = new Timer(this, RunTimerId);
connect(TimerEvent::EvtTimer, RunTimerId, (ObjectEventFunction)&TestRunnerFrame::onReadTestBuffer);
createStatusBar(2);
_stResult->setBackgroundColour(new Colour(0, 0, 255));
_stResult->setLabel("asdfasdf");
connect(TreeEvent::EvtCommandTreeSelChanged, TestTreeId, (ObjectEventFunction)&TestRunnerFrame::onTestSelChanged);
connect(TreeEvent::EvtCommandTreeItemActivated, TestTreeId, (ObjectEventFunction)&TestRunnerFrame::onItemActivated);
_cfgTextCtrl = new TextCtrl(&_notebook, -1, "", Point::defaultPosition(), Size::defaultSize(),
TeMultiline | TeAutoScroll | TeDontwrap);
acdk::io::File f(getCfgFile());
if (f.exists() == true)
{
_cfgTextCtrl->setValue(f.getReader()->getCharReader()->readString());
}
_notebook->addPage(&_cfgTextCtrl, "Configuration");
}
void onMenuQuit(IN(REvent) event)
{
close(true);
}
void onMenuAbout(IN(REvent) event)
{
Window::messageBox("ACDK Unit Testrunner written with acdk_wx\n By Roger Rene Kommer (http://acdk->sourceforge.net)");
}
void onMenuClear(IN(REvent) event)
{
_console->clear();
}
void onTestRun(IN(REvent) event)
{
if (_runButton->getLabel()->equals("Stop") == true)
{
_stopTests = true;
_runButton->setLabel("Abort");
_guiTestListener->doStop();
return;
}
if (_runButton->getLabel()->equals("Abort") == true)
{
_abortTest = true;
return;
}
RTreeItemId tid = _treectrl->getSelection();
setCursor(Cursor::getHourglassCursor());
setStatusText("Prepare Test Execution", 0);
_runButton->enable(false);
_treectrl->expandAllTests(tid);
/*
int allTestCount = setIconsToUnknown(tid);
_progress.setRange(allTestCount);
_progress.setValue(0);
*/
_runButton->enable(true);
setCursor(Cursor::getNullCursor());
_curErrsAndFailures = 0;
_stopTests = false;
_abortTest = false;
_allTestResults = new TestResultEntryArray(0);
RTestResultEntry tre = (RTestResultEntry)_treectrl->getItemDataObject(tid);
if (tre == Nil)
{
if (_treectrl->getItemText(tid)->equals("Test Executables") == true)
{
jlong cookie = 0;
tid = _treectrl->getFirstChild(tid, cookie);
if (tid->isOk() == false)
return;
_runNextSibling = true;
tre = (RTestResultEntry)_treectrl->getItemDataObject(tid);
}
}
if (tre == Nil)
{
messageBox("No Test defined");
return;
}
_runButton->setLabel("Stop");
runTest(tre->test, tid);
}
void runTest(IN(RTest) t, IN(RTreeItemId) tid, bool nextRun = false)
{
_runnedTreeItemId = tid;
acdk::io::RStringWriter sout = new acdk::io::StringWriter();
acdk::io::RStringWriter serr = new acdk::io::StringWriter();
if (instanceof(t, ProcessTestCase) == true)
{
RProcessTestCase ptc(t);
ptc->setOutWriter(new acdk::io::TeeCharWriter(&_consoleWriter, &sout));
ptc->setErrWriter(new acdk::io::TeeCharWriter(&_consoleWriter, &serr));
}
else if (instanceof(t, ProcessTestSuite) == true)
{
RProcessTestSuite pts(t);
pts->setOutWriter(new acdk::io::TeeCharWriter(&_consoleWriter, &sout));
pts->setErrWriter(new acdk::io::TeeCharWriter(&_consoleWriter, &serr));
}
_testResult = new TestResult(sout, serr);
_guiTestListener = new GuiTestListener();
int allTestCount = _treectrl->setIconsToUnknown(tid);
_progress->setRange(allTestCount);
_progress->setValue(0);
_testResult->addTestListener(&_guiTestListener);
_testRunThread = new TestRunThread(t, _testResult);
_testRunThread->start();
_testOutputTimer->start(500, false);
//_runButton.enable(false);
Thread::sleep(200);
onReadTestBuffer(new TimerEvent());
}
void onTestSelChanged(IN(RTreeEvent) event)
{
RTreeItemId id = _treectrl->getSelection();
RString t = _treectrl->getItemText(id);
_stSelectedTest->setLabel(t);
setStatusText(t, 1);
RTestResultEntry tre = (RTestResultEntry)_treectrl->getItemDataObject(id);
if (tre == Nil)
{
_testLog->setValue("");
_testRep->setValue("not runned");
return;
}
StringBuffer sb;
sb << tre->toString() << ":\n";
if (tre->errors != Nil && tre->errors->length() > 0)
{
sb << tre->errors->length() << " ERRORS:\n";
for (int i = 0; i < tre->errors->length(); ++i)
{
RThrowable ex = tre->errors[i];
if (ex != Nil)
sb << ex->getMessage() << "\n";
}
}
else
sb << "0 Errors\n";
if (tre->failures != Nil && tre->failures->length() > 0)
{
sb << tre->failures->length() << " ERRORS:\n";
for (int i = 0; i < tre->failures->length(); ++i)
{
RThrowable ex = tre->failures[i];
if (ex != Nil)
sb << ex->getMessage() << "\n";
}
}
else
sb << "0 Failures\n";
_testRep->setValue(sb.toString());
_testLog->setValue("STDOUT:\n" + tre->output + "\n==========================================\nSTDERR:\n" + tre->errput + "\n");
RTest test = tre->test;
RString tn = test->getName();
if (tn->endsWith("_Test") == true || tn->endsWith("_Test.exe") == true || tn->endsWith("_Test_d.exe") == true || tn->endsWith("_Test_r.exe") == true)
{
RString fn;
if (t->endsWith(".exe") == true)
fn = System::getAcdkHome() + "/testreports/" + t->substr(0, t->length() - 4) + ".html";
else
fn = System::getAcdkHome() + "/testreports/" + t + ".html";
/*if ((new acdk::io::File(fn))->exists() == true)
_htmlLogWindow->loadFile(fn);
else
setStatusText("File doesn't exists: " + fn, 0);
*/
}
}
void onItemActivated(IN(RTreeEvent) event)
{
//messageBox("Item Activated");
_notebook->setSelection(1);
}
void onReadTestBuffer(IN(REvent) event)
{
RString s = _consoleWriter->fetchBuffer();
//out.println("Runned: " + _guiTestListener.runnedTest);
_progress->setValue(_guiTestListener->runnedTest);
if (s->length() > 0)
{
_console->appendText(s);
}
if (_testRunThread->isAlive() == false)
{
_testOutputTimer->stop();
_testRunThread = Nil;
if (_stopTests == true)
_runNextSibling = false;
if (_runNextSibling == true)
{
RTreeItemId nid = _treectrl->getNextSibling(_runnedTreeItemId);
if (nid->isOk() == true)
{
RTestResultEntry tre = (RTestResultEntry)_treectrl->getItemDataObject(nid);
if (tre != Nil)
{
writeTestResult();
runTest(tre->test, nid, true);
return;
}
}
else
{
_runNextSibling = false;
}
}
_runButton->setLabel("Run");
_console->appendText("Test Finished\r\n");
_runButton->enable(true);
setStatusText("Postprocess tests results");
setCursor(Cursor::getHourglassCursor());
writeTestResult();
setCursor(Cursor::getNullCursor());
}
else
{
setStatusText(_guiTestListener->getLastMessage(), 0);
}
}
void setTestResult(IN(RTreeItemId) id, IN(RTestResultEntry) tre, bool derived = false)
{
// set parents on fail and icon
_treectrl->setItemDataObject(id, &tre);
if ((tre->errors->length() > 0) || (tre->failures->length() > 0))
{
_treectrl->setTestFailed(id);
}
else
{
_treectrl->setTestOk(id);
/*
if (derived == false || _treectrl.getItemImage(id) == TestUnknown)
{
_treectrl.setItemImage(id, TestOk);
_treectrl.setItemImage(id, TestOk, TreeitemiconSelected);
}
*/
}
}
void writeTestResult()
{
RTestResultEntryArray results = _testResult->getResults();
StringBuffer sb;
sb << "Runned Test: " << results->length() << "\n";
int allErrors = 0;
int allFailures = 0;
for (int i = 0; i < results->length(); ++i)
{
RTestResultEntry tre = results[i];
allErrors = allErrors + tre->errors->length();
allFailures = allFailures + tre->failures->length();
//if (findSetTestResult(_treectrl.getRootItem(), tre) == false)
if (findSetTestResult(_runnedTreeItemId, &tre) == false)
{
//out.println("Test was not found: " + tre.test.toString());
}
}
if ((allErrors + allFailures) != 0)
{
sb << " TEST FAILED\n"
<< "Errors: " << allErrors << "\n"
<< "Failures: " << allFailures << "\n";
_stResult->setBackgroundColour(new Colour(255, 0, 0));
//setTestFailed();
}
else
{
sb << " Test ok\n";
_stResult->setBackgroundColour(new Colour(0, 255, 0));
_treectrl->setTestOk(_runnedTreeItemId);
}
//out.println(sb.toString());
_stResult->setLabel(sb.toString());
}
bool findSetTestResult(IN(RTreeItemId) id, IN(RTestResultEntry) tre)
{
RTreeItemId fid = (RTreeItemId)_treectrl->_testResultToTreeItemIdMap->get(&tre);
if (fid != Nil)
{
setTestResult(fid, tre);
return true;
}
//out.println("TEST not found: " + tre.test.toString() + " in:\n" + testResultToTreeItemIdMap.toString());
RTestResultEntry itre = (RTestResultEntry)_treectrl->getItemDataObject(id);
if (itre != Nil)
{
//out.println(itre.test.toString() + " == " + tre.test.toString());
if (tre->test->getName()->equals(itre->test->getName()) == true)
{
setTestResult(id, tre);
return true;
}
}
jlong cookie = 0;
RTreeItemId c = _treectrl->getFirstChild(id, cookie);
while (c->isOk() == true)
{
if (findSetTestResult(c, tre) == true)
{
setTestResult(id, tre, true);
return true;
}
c = _treectrl->getNextChild(id, cookie);
}
return false;
}
RString getCfgFile()
{
return SBSTR(System::getAcdkToolsHome() << acdk::io::File::separator() << "cfg"
<< acdk::io::File::separator() << "guitestrunner.csf");
}
bool loadCfg()
{
RString cfgfile;
try {
cfgfile = getCfgFile();
acdk::io::File f(cfgfile);
if (f.exists() == false)
{
messageBox("Configuration file missing: " + cfgfile);
return false;
}
acdk::cfgscript::RScript script = new acdk::cfgscript::Script(cfgfile);
acdk::cfgscript::RProps globals = new acdk::cfgscript::Props();
script->initAsEnvProps(globals);
globals->setProps("testCfg", testCfg);
script->readEval(globals, acdk::cfgscript::PropsParentRead | acdk::cfgscript::PropsParentWrite);
initCfg();
} catch (RThrowable ex) {
messageBox("Error while reading configuration file " + cfgfile + ":\n" + ex->getMessage());
}
return true;
}
void initCfg()
{
RStringArray pathlist = testCfg->getStringArrayVal("path", Nil);
if (pathlist != Nil)
{
RString path = _testEnviron->getProperty("PATH");
if (path == Nil)
path = _testEnviron->getProperty("Path");
StringBuffer np(path);
for (int i = 0; i < pathlist->length(); ++i)
{
RString pe = pathlist[i];
//out.println(pe);
np << acdk::io::File::pathSeparator() << pe;
}
_testEnviron->setProperty("PATH", np.toString());
_testEnviron->setProperty("Path", np.toString());
//out.println("New Path: " + np.toString());
}
acdk::cfgscript::RProps envprops = testCfg->getProps("environ", Nil);
if (envprops != Nil)
{
RStringArray keys = envprops->getKeys();
for (int i = 0; i < keys->length(); ++i)
{
RString k = keys[i];
_testEnviron->setProperty(k, envprops->getStringVal(k));
}
}
testCfg->setObjectVal("testEnviron", &_testEnviron);
}
};
} // namespace guitestrunner
} // namespace aunit
} // namespace tools
} // namespace acdk
#endif //acdk_tools_aunit_guitestrunner_TestRunnerFrame_h |