2005/5/9

     
 

AcdkTestRunner.csf

artefaktur

Props testCfg = new Props();

#include "atr_cfg.csf"
#include "ProcessTestRunner.csf"

using acdk.wx;
using acdk.tools.aunit;

enum TestState
{
  TestUnknown = 0,
  TestFailed = 1,
  TestOk = 2
};

class AcdkTestRunner
extends Frame
{
  TreeCtrl _treectrl;
  TextCtrl _console;
  Menu _treeMenu;
  TextConsoleCharWriter _consoleWriter;
  Timer _testOutputTimer;
  TestRunThread _testRunThread;
  StaticText _stSelectedTest;
  StaticText _stResult;
  Button _runButton;
  TestResult _testResult;
  GuiListener _guiTestListener;
  HtmlWindow _htmlLogWindow;
  TreeItemId _runnedTreeItemId;
  Gauge _progress;
  bool _runNextSibling;// @todo = false doesn't working!
  int _curErrsAndFailures = 0;
  TestResultEntryArray _allTestResults;
  bool _stopTests;
  bool _abortTest;
  TextCtrl _testRep;
  TextCtrl _testLog;
  //acdk.util.HashMap testResultToTreeItemIdMap = new acdk.util.HashMap();
  acdk.util.TreeMap testResultToTreeItemIdMap = new acdk.util.TreeMap();
  AcdkTestRunner(String title) 
  {
    super(Nil, -1, title, new Point(100, 100), new Size(800, 600));
    _runNextSibling = false;
    _stopTests = false;
    _abortTest = false;
  
   MenuBar menuBar = new MenuBar();
    
    Menu menuFile = new Menu();
    /*
    menuFile.append(Event_FileNew, "New", "Creates a new file"); 
    menuFile.append(Event_FileOpen, "Open", "Open a file"); 
    menuFile.append(Event_FileSave, "Save", "Open a file"); 
    */
    menuFile.append(Event_ClearLog, "Clear Log", "Clear Log Window"); 
    menuFile.appendSeparator();
    menuFile.append(Event_MenuQuit, "E&xit\tAlt-X", "Quit this program");
    menuBar.append(menuFile, "&File");
    Menu helpMenu = new Menu();
    helpMenu.append(Event_MenuAbout, "&About...\tCtrl-A", "Show about dialog");
    menuBar.append(helpMenu, "&Help");
    setMenuBar(menuBar);
    connect(CommandEvent.EvtCommandMenuSelected, Event_MenuQuit, 
            lambda[this]  { close(true);  } 
            );
            
    connect(CommandEvent.EvtCommandMenuSelected, Event_MenuAbout, delegate onMenuAbout);
    
    ToolBar tb = createToolBar();
    tb.setWindowStyle(TbText | Tb3dbuttons);
    String helpbmp = __script.getScriptPath() + "/" + "help.bmp";
    Bitmap helpbm = new Bitmap(helpbmp, BitmapTypeBmp);
    ToolBarToolBase tbb = tb.addTool(TbHelpId, "Help", helpbm, "Display short help");
    connect(CommandEvent.EvtCommandMenuSelected, TbHelpId, delegate onMenuAbout);
    tb.realize();
    SplitterWindow splitter = new SplitterWindow(this);
      
    _treectrl = new TreeCtrl(splitter, TestTreeId, new Point(0, 0), new Size(200, 200));
    ImageList imageList = new ImageList(16, 16);
    imageList.add(new Bitmap(__script.getScriptPath() + "/" + "folder_untested.bmp", BitmapTypeBmp));
    imageList.add(new Bitmap(__script.getScriptPath() + "/" + "folder_fail.bmp", BitmapTypeBmp));
    imageList.add(new Bitmap(__script.getScriptPath() + "/" + "folder_ok.bmp", BitmapTypeBmp));
    _treectrl.assignImageList(imageList);
    
    TreeItemId root = _treectrl.addRoot("Test Executables");
    Notebook  notebook = new Notebook(splitter, -1);
    SplitterWindow rsplt = new SplitterWindow(notebook);
    notebook.addPage(rsplt,  "Execute");
    
    SplitterWindow 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");
    
    
    
    
    Panel testResultPanel = new Panel(rsplt, 0, 0, 400, 100);
    
    _console = new TextCtrl(rsplt, -1, "",
                            Point.defaultPosition(), Size.defaultSize(), 
                            TeMultiline | TeAutoScroll | TeDontwrap | TeRich2);
    //_console.setWindowStyle(TeMultiline + TeAutoScroll + TeDontwrap);
    
    connect(CommandEvent.EvtCommandMenuSelected, Event_ClearLog, 
            lambda[console: _console]  { console.clear(); }
           );
            
    _consoleWriter = new TextConsoleCharWriter();
    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, delegate onTestRun);
    
    _progress = new Gauge(testResultPanel, -1, 100, new Point(10,60), new Size(400, 40), GaSmooth | BorderNone );
    
    _stSelectedTest = new StaticText(testResultPanel, CurrentTestST, "<selected Test>", new Point(120, 25));
    _stResult = new StaticText(testResultPanel, StResult, "", new Point(10, 100));
    _treeMenu = new Menu();
    _treeMenu.append(RunTestId, "Run Test");
    connect(CommandEvent.EvtCommandMenuSelected, RunTestId, delegate onTestRun);
    _testOutputTimer = new Timer(this, RunTimerId);
    connect(TimerEvent.EvtTimer, RunTimerId, delegate onReadTestBuffer);
    createStatusBar(2);
    _stResult.setBackgroundColour(new Colour(0, 0, 255));
    _stResult.setLabel("asdfasdf");
    
    connect(Event.EvtNull, Event_TestListenerPing, 
            lambda [this] { setStatusText("X: " + _guiTestListener.getLastMessage(), 0); }
            );
    
    fillWithTests();
  }
  
  void fillWithTests()
  {
    String dir = testCfg.testDir; //acdk.lang.System.getAcdkHome() + "/bin";
    String filter = testCfg.testPattern;
    /*
    String filter = "*_Test";
    if (System.getPlatformFlags() & acdk.lang.PfWin32)
       filter = "*Test_d.exe";
       */
    StringArray testexec = new File(dir).list(new GlobFilenameFilter(filter));
    TreeItemId root = _treectrl.getRootItem();
    _treectrl.setItemImage(root, TestUnknown);
    _treectrl.setItemImage(root, TestUnknown, TreeitemiconSelected);
    
    foreach (String execn in testexec)
    {
      bool ignoreTest = false;
      foreach (String t in testCfg.tests.getKeys())
      {
        if (execn.indexOf(t) != -1)
        {
          if (testCfg.tests.getProps(t).getBoolVal("ignore") == true)
            ignoreTest = true;
          break;
        }
      }
      if (ignoreTest == true)
        continue;
      TreeItemId tid = _treectrl.appendItem(root, execn);
      TestResultEntry tre = new TestResultEntry(new ProcessTestSuite(dir + "/" + execn, Nil, ConfigTools.getTestEnv()));
      _treectrl.setItemDataObject(tid, tre);
      testResultToTreeItemIdMap.put(tre, tid);
      _treectrl.setItemHasChildren(tid, true);
    }
    connect(TreeEvent.EvtCommandTreeItemExpanding, TestTreeId, delegate onListBoxExpanded);
    out.println("TreeEvent.EvtCommandTreeItemExpanding: " + TreeEvent.EvtCommandTreeItemExpanding);
    connect(TreeEvent.EvtCommandTreeItemRightClick, TestTreeId, 
            lambda [this]  void (Event event)
            { 
              popupMenu(_treeMenu, event.getPoint()); 
            } 
           );
    connect(TreeEvent.EvtCommandTreeSelChanged, TestTreeId, delegate onTestSelChanged);
    //out.println("TreeEvent.EvtCommandTreeSelChanged: " + TreeEvent.EvtCommandTreeSelChanged);
  }
  void onListBoxExpanded(TreeEvent event)
  {
    TreeItemId tid = event.getItem();
    if (_treectrl.getChildrenCount(tid, false) != 0)
      return;
    TestResultEntry tre = _treectrl.getItemDataObject(tid);
    if (tre == Nil)
        messageBox("No Suit available");
    ProcessTestSuite psuit = tre.test;
    TestArray ta = psuit.tests();
    foreach (Test t in ta)
    {
      String tn = t.getName();
      int idx;
      if ((idx = tn.indexOf(";")) != -1)
        tn = tn.substr(idx + 1);
      TreeItemId ctid = _treectrl.appendItem(tid, tn);
      TestResultEntry tre = new TestResultEntry(t);
      _treectrl.setItemDataObject(ctid, tre);
      testResultToTreeItemIdMap.put(tre, ctid);
      //out.println("Test Class: " + t.GetClass().toString());
      if (t instanceof ProcessTestSuite)
        _treectrl.setItemHasChildren(ctid, true);
      _treectrl.setItemImage(ctid, _treectrl.getItemImage(tid));
      _treectrl.setItemImage(ctid, _treectrl.getItemImage(tid, TreeitemiconSelected), TreeitemiconSelected);
    }
  }
  int setIconsToUnknown(TreeItemId id)
  {
    _treectrl.setItemImage(id, TestUnknown);
    _treectrl.setItemImage(id, TestUnknown, TreeitemiconSelected);
    int cookie = 0;
    RTreeItemId c = _treectrl.getFirstChild(id, cookie);
    int count = 0;
    if (c.isOk() == false)
      count = 1;
    while (c.isOk() == true)
    {
      count = count + setIconsToUnknown(c);
      c = _treectrl.getNextChild(id, cookie);
    }
    return count;
  }
  void expandAllTests(TreeItemId tid)
  {
    if (_treectrl.getChildrenCount(tid, false) != 0)
    {
      int cookie = 0;
      RTreeItemId c = _treectrl.getFirstChild(tid, cookie);
      while (c.isOk() == true)
      {
        expandAllTests(c);
        c = _treectrl.getNextChild(tid, cookie);
      }      
      return;
    }
    Test test = _treectrl.getItemDataObject(tid).test;
    if ((test instanceof ProcessTestSuite) == false)
      return;
    ProcessTestSuite psuit = test;
    if (psuit == Nil)
      return;
    TestArray ta = psuit.tests();
    String tests = psuit.getName();
    foreach (Test t in ta)
    {
      String tn = t.getName();
      int idx;
      if ((idx = tn.indexOf(";")) != -1)
        tn = tn.substr(idx + 1);
      TreeItemId ctid = _treectrl.appendItem(tid, tn);
      TestResultEntry tre = new TestResultEntry(t);
      _treectrl.setItemDataObject(ctid, tre);
      //out.println("HashCode of tre: " + tre.hashCode());
      
      testResultToTreeItemIdMap.put(tre, ctid);
      //out.println("Test Class: " + t.GetClass().toString());
      _treectrl.setItemImage(ctid, _treectrl.getItemImage(tid));
      _treectrl.setItemImage(ctid, _treectrl.getItemImage(tid, TreeitemiconSelected), TreeitemiconSelected);
      if (t instanceof ProcessTestSuite)
      {
        _treectrl.setItemHasChildren(ctid, true);
        expandAllTests(ctid);
      }
    }
  }
  void onTestRun(Event event)
  {
    if (_runButton.getLabel().equals("Stop") == true)
    {
      _stopTests = true;  
      _runButton.setLabel("Abort");
      _guiTestListener.doStop();
      return;
    }
    if (_runButton.getLabel().equals("Abort") == true)
    {
      _abortTest = true;
      return;
    }
  
    TreeItemId tid = _treectrl.getSelection();
    setCursor(Cursor.getHourglassCursor());
    setStatusText("Prepare Test Execution", 0);    
    _runButton.enable(false);
    
    expandAllTests(tid);
    /*
    int allTestCount = setIconsToUnknown(tid);
    _progress.setRange(allTestCount);
    _progress.setValue(0);
    */
    _runButton.enable(true);
    setCursor(Cursor.getNullCursor());
    _curErrsAndFailures = 0;
    _allTestResults = new TestResultEntryArray(0);
    TestResultEntry tre = _treectrl.getItemDataObject(tid);
    if (tre == Nil)
    {
      if (_treectrl.getItemText(tid).equals("Test Executables") == true)
      {
        int cookie = 0;
        tid = _treectrl.getFirstChild(tid, cookie);
        if (tid.isOk() == false)
          return;
        _runNextSibling = true;
        tre = _treectrl.getItemDataObject(tid);
      }
    }
    if (tre == Nil)
    {
      messageBox("No Test defined");
      return;
    }
    _runButton.setLabel("Stop");
    runTest(tre.test, tid);
  }
  void runTest(Test t, TreeItemId tid, bool nextRun = false)
  {
    _runnedTreeItemId = tid;
    StringWriter sout = new StringWriter();
    StringWriter serr = new StringWriter();
    t.setOutWriter(new TeeCharWriter(_consoleWriter, sout));
    t.setErrWriter(new TeeCharWriter(_consoleWriter, serr));
    _testResult = new TestResult(sout, serr);
    _guiTestListener = new GuiListener(this);
    
    int allTestCount = 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);
    onReadTestBuffer(new TimerEvent());
    
  }
  void onReadTestBuffer(Event event)
  {
    String 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)
      {
        TreeItemId nid = _treectrl.getNextSibling(_runnedTreeItemId);
        if (nid.isOk() == true)
        {
          TestResultEntry tre = _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(TreeItemId id, TestResultEntry tre, bool derived = false)
  {
    // set parents on fail and icon
    _treectrl.setItemDataObject(id, tre);
    if ((tre.errors.length() > 0) || (tre.failures.length() > 0))
    {
      setTestFailed(id);
    }
    else
    {
      setTestOk(id);
      /*
      if (derived == false || _treectrl.getItemImage(id) == TestUnknown)
      {
        _treectrl.setItemImage(id, TestOk);
        _treectrl.setItemImage(id, TestOk, TreeitemiconSelected);
      }
      */
    }
  }
  bool findSetTestResult(TreeItemId id, TestResultEntry tre)
  {
    TreeItemId fid = testResultToTreeItemIdMap.get(tre);
    if (fid != Nil)
    {
      setTestResult(fid, tre);
      return true;
    }
    out.println("TEST not found: " + tre.test.getName() + " in:\n" + testResultToTreeItemIdMap.toString());
    TestResultEntry itre = _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;
      }  
    }

    int 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;
  }
  void writeTestResult()
  {
    TestResultEntryArray results = _testResult.getResults();
    StringBuffer sb = new StringBuffer();
    sb << "Runned Test: " << results.length() << "\n";
    int allErrors = 0;
    int allFailures = 0;
    
    foreach (TestResultEntry tre in results)
    {
      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.getName());
      }
    }
    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));
      setTestOk(_runnedTreeItemId);
    }
    out.println(sb.toString());
    _stResult.setLabel(sb.toString());
    
  }
  void setTestFailed()
  {
    setTestFailed(_runnedTreeItemId);
  }
  void setTestFailed(TreeItemId p)
  {
    _treectrl.setItemImage(p, TestFailed);
    _treectrl.setItemImage(p, TestFailed, TreeitemiconSelected);
    
    while (p.isOk() == true)
    {
      p = _treectrl.getItemParent(p);
      if (p.isOk() == false)
        break;
      _treectrl.setItemImage(p, TestFailed);
      _treectrl.setItemImage(p, TestFailed, TreeitemiconSelected);
    }
  }
  void setTestOk(TreeItemId p = Nil)
  {
    if (p == Nil)
      p = _runnedTreeItemId;
    //out.println("SetOK: " + _treectrl.getItemText(p));
    _treectrl.setItemImage(p, TestOk);
    _treectrl.setItemImage(p, TestOk, TreeitemiconSelected);
    setTestOkInChilds(p);
    while (p.isOk() == true)
    {
      p = _treectrl.getItemParent(p);
      if (p.isOk() == false)
        break;
      if (_treectrl.getItemImage(p) == TestUnknown)
      {
        _treectrl.setItemImage(p, TestOk);
        _treectrl.setItemImage(p, TestOk, TreeitemiconSelected);
      }
    }
  }
  void setTestOkInChilds(TreeItemId p)
  {
    int cookie = 0;
    RTreeItemId c = _treectrl.getFirstChild(p, cookie);
    
    while (c.isOk() == true)
    {
      _treectrl.setItemImage(c, TestOk);
      _treectrl.setItemImage(c, TestOk, TreeitemiconSelected);
      setTestOkInChilds(c);
      c = _treectrl.getNextChild(p, cookie);
    }
  }
  void onTestSelChanged(TreeEvent event)
  {
    TreeItemId id = _treectrl.getSelection();
    String t = _treectrl.getItemText(id);
    _stSelectedTest.setLabel(t);
    setStatusText(t, 1);
    TestResultEntry tre = _treectrl.getItemDataObject(id);
    if (tre == Nil)
    {
      _testLog.setValue("");
      _testRep.setValue("not runned");
      return;
    }
    StringBuffer sb = new StringBuffer();
    sb << tre.toString() << ":\n";
    if (tre.errors != Nil && tre.errors.length() > 0)
    {
      sb << tre.errors.length() << " ERRORS:\n";
      foreach (Throwable ex in tre.errors)
      {
        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";
      foreach (Throwable ex in tre.failures)
      {
        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");
    Test test = tre.test;
    String tn = test.getName();
    if (tn.endsWith("_Test") == true || tn.endsWith("_Test.exe") == true)
    {
      String 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 File(fn)).exists() == true)
        _htmlLogWindow.loadFile(fn);
      else
        setStatusText("File doesn't exists: " + fn, 0);
    }
    
  }
  void onMenuAbout(Event event)
  {
    Window.messageBox("ACDK Unit Testrunner written with ACDK CfgScript\n By Roger Rene Kommer (http://acdk.sourceforge.net)");
  }
}

class MyApp
extends acdk.wx.App
{
  MyApp() {}
  bool onInit()
  {
    (new AcdkTestRunner("ACDK Test Runner")).show(true);
    return true; 
  }
}

StringArray args = new StringArray(0);
acdk.wx.App.createGui("MyApp", args);