<?php
/**
 * PhpMyObject extensions to Simpletest HTMLReporter class.
 *
 * This file is part of the PhpMyObject project,
 * an Object-Relational Mapping (ORM) system.
 * 
 * For questions, help, comments, discussion, etc., please join our
 * forum at {@link http://www.developpez.net/forums/forumdisplay.php?f=770} 
 * or our mailing list at {@link http://groups.google.com/group/pmo-dev}.
 *
 * PhpMyObject is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @package    	PhpMyObject
 * @subpackage 	PMO_Tests
 * @author     	Louis Lapointe <laplix@gmail.com>
 * @link				http://pmo.developpez.com/
 * @since 			PhpMyObject v0.15
 * @version       $Revision$
 * @copyright  	Copyright (C) 2008 Louis Lapointe
 * @license    	GPLv3 {@link http://www.gnu.org/licenses/gpl}
 * @filesource
 *
 * @todo 			tidy up, get rid of the $html vars and use a templating system
 * @todo 			need to make this extension independant so it can be used 
 * 					by other projects. This could be part of a PMO sub project
 */

/**
 * EOL
 */
if (!defined('NL')) {
	define ('NL', "\n");
}

/**
 * requires needed libraries from simpletest.
 */
require_once(SIMPLETEST . DS . 'reporter.php');

/**
 * PMO_HTMLReporter extends the Simpletest HTMLReporter
 *
 * This class adds more details to the HTMLReporter. A new
 * parameter can be used in the URL, e=1 or e=2. This parameter
 * enables the user to have subtotals for each test case (e=1)
 * or have all tests displayed (e=2).
 *
 * @package       PHoMyObject
 * @subpackage    PMO_Tests
 */
class PMO_HTMLReporter extends HTMLReporter
{
	/**
	 * @var int
	 */
	private $pmo_progress;
	
	/**
	 * @var int
	 */
	private $pmo_passes;
	
	/**
	 * @var int
	 */
	private $pmo_failures;

	/**
	 * @var int
	 */
	private $pmo_exceptions;

   /**
	 * indicates level of results printing
	 *
	 * defaults to false. 1 will print cases subtotals and 2 will print
	 * the result of each test
	 *
	 * @var int
    */
   private $pmo_level;

	function __construct($level=0) {
		parent::HtmlReporter();
		$this->pmo_level = $level;
   }
		
	/**
	 * PHP4 constructor
	 * @param int $extended
	 */
   function PMO_HTMLReporter($level=0)
	{
		$this->__construct($level);
   }

	/**
	 * return a css class based on $errors
	 * @param int $errors
	 * @return string
	 */
   function getClass($errors) 
   {

      if ($errors) {
         $class = 'Fail';
      } else {
         $class = 'Pass';
      }
      return $class;      
   }

	/**
	 * resets our stats.
	 * @param int $firstime 	if true, reset to zero
	 * 								if false, stores the current stats so
	 * 								subtotals can be displayed
	 */ 
   function _resetStats($firstTime = false)
   {
      if ($firstTime) {
         $this->pmo_progress = 0;
         $this->pmo_passes = 0;
         $this->pmo_failures = 0;
         $this->pmo_exceptions = 0;
      } else {
         $this->pmo_progress =  $this->getTestCaseProgress();
         $this->pmo_passes =  $this->getPassCount();;
         $this->pmo_failures =  $this->getFailCount();;
         $this->pmo_exceptions =  $this->getExceptionCount();;
      }
   }

	/**
	 * includes the needed javascript
	 *
	 * the javascript has been has been transfered into its
	 * own file so that it's easier to add/modify
	 *
	 * @return string
	 */
   function getScript()
   {
		include(dirname(__FILE__).DS.'js.php');
      return $html;
   }

	/**
	 * extends the _getCss() method
	 *
	 * the css has been transfered into its own file to 
	 * make easier to modify.
	 *
	 * @return string
	 */
   function _getCss() 
   {
		include(dirname(__FILE__).DS.'style.php');
      return $style;
   }

   /**
	 * This paints the HTML headers.
	 *
	 * This extends HTMLReporter by adding a container to encompass
	 * the whole page so we can style it.
	 *
    */
   function paintHeader($test_name) {
      $this->_resetStats(true);
      $my_header = '<a href="http://pmo.developpez.com">PhpMyObject</a> Test Suite';
      ob_start();
      parent::paintHeader($test_name);
      $header = ob_get_contents();
		ob_end_clean();

      $header = str_replace("<h1>PMO Test Suite</h1>", "<h1>$my_header</h1>", $header);
      $header = str_replace('</head>', $this->getScript().NL.'</head>', $header);
      //pr($header);exit;

      $html = $header;
      $html .= '<div class="container">'.NL;
      $html .= '<p id="more">This TestSuite is able to give more or less details about your tests. <a onclick="info()">Click to toggle details</a>.</p>'.NL;
      $html .= '<div id="info" class="info">'.NL;
		$html .= '<ul>'.NL;
      $html .= '<li><b><code><a href="http://'.$_SERVER['SERVER_NAME'].$_SERVER['SCRIPT_NAME'].'">http://'.$_SERVER['SERVER_NAME'].$_SERVER['SCRIPT_NAME'].'</a></code></b> will give you the results you get from the standard <a href="http://simpletest.sourceforge.net/">Simpletest</a> HTMLReporter class, e.g. fails and exceptions and the grand total from your tests.</li>'.NL;
      $html .= '<li><b><code><a href="http://'.$_SERVER['SERVER_NAME'].$_SERVER['SCRIPT_NAME'].'?level=1">http://'.$_SERVER['SERVER_NAME'].$_SERVER['SCRIPT_NAME'].'<span class="hilite">?level=1</span></a></code></b> will give you the results from each test case. You get the standard fails and exceptions, your test cases sub-totals and the grand total.</li>'.NL;
      $html .= '<li>Finally, <b><code><a href="http://'.$_SERVER['SERVER_NAME'].$_SERVER['SCRIPT_NAME'].'?level=2">http://'.$_SERVER['SERVER_NAME'].$_SERVER['SCRIPT_NAME'].'<span class="hilite">?level=2</span></a></code></b> will output each test result, your test cases sub-totals and the grand total.</li>'.NL;
      $html .= '</ul><hr /></div>'.NL;

      print $html;
   }

   /**
    * we need to close our container div
    * and use our own style and color scheme so we redo the footer
	 * @param string $test_name
    */
   function paintFooter($test_name) {

      // reset our stats
      $this->_resetStats();
      $errors = $this->getFailCount() + $this->getExceptionCount();
      $passes = $this->getPassCount();

      $class = $this->getClass($errors);

      // close the container div
      $html = '<div style="clear:left;"></div></div>'.NL;
      $html .= '<div class="footer footer'.$class.'">'.NL;
      $html .= $this->getTestCaseProgress() . "/" . $this->getTestCaseCount();
      $html .= ' test cases completed: '.NL;
      $html .= '<strong>' . $this->getPassCount() . '</strong> passes, ';
      $html .= '<strong>' . $this->getFailCount() . '</strong> fails and ';
      $html .= '<strong>' . $this->getExceptionCount() . '</strong> exceptions.'.NL;
      $html .= '</div>'.NL;
		$html .= '<div class="pmoFooter">PMO_HTMLReporter v0.1</div>';
      $html .= '</body>'.NL.'</html>'.NL;
      print $html;
   }

	/**
	 * paints the test case header
	 * if in extended mode, also paints the test header
	 *
	 * @param string $test_name
	 */
   function paintCaseStart($test_name) {
      parent::paintCaseStart($test_name);
      if ($this->pmo_level >= 1)
         print '<div class="caseName">'.$test_name.'</div>'.NL;
   }

	/**
	 * paints the testcase footer and totals
	 *
	 * if in extended mode, will also paints tests subtotals
	 *
	 * @param string $test_name
	 */
   function paintCaseEnd($test_name) {
      parent::paintCaseEnd($test_name);

      if ($this->pmo_level >= 1) {
         // what we do here is provide a subtotal
         
         // take note of last stats
         $passes = $this->pmo_passes;
         $failures = $this->pmo_failures;
         $exceptions = $this->pmo_exceptions;

         // reset our stats to get the new values
         $this->_resetStats();

         $passes = $this->pmo_passes - $passes;
         $failures = $this->pmo_failures - $failures;
         $exceptions = $this->pmo_exceptions - $exceptions;
      
         $class = $this->getClass($failures+$exceptions);

         // output it
         $html = '<div class="caseFooter caseFooter'.$class.'">'.NL;
         $html .= $this->getTestCaseProgress() . "/" . $this->getTestCaseCount();
         $html .= ' test cases completed: '.NL;
         $html .= '<strong>' . $passes . '</strong> passes, ';
         $html .= '<strong>' . $failures . '</strong> fails and ';
         $html .= '<strong>' . $exceptions . '</strong> exceptions.'.NL;
         $html .= '</div>'.NL;
         print $html;
      }
   }

	/**
	 * in extended mode 2, paints each tests
	 * @var string $message 		the test result
	 */
   function paintPass($message) {
      parent::paintPass($message);

      if ($this->pmo_level >= 2) {
         $list = $this->getTestList();
         $test = array_pop($list);
         if (strpos($message, 'at [')) {
            $msg = explode('at [', $message);
         } else {
            $msg[] = $message;
         }
         $html = '<div class="status pass">Pass</div>'.NL;
         $html .= '<div class="testName">'.$test.'</div>'.NL;
         $html .= '<div class="message">'.$this->_htmlEntities($msg[0]).'</div>'.NL;
         print $html;
      }
   }

	/**
	 * paints anm error
	 * @param string message
	 */
   function paintFail($message) {
      // trap the parent message since we want to output our own.
      ob_start();
      parent::paintFail($message);
      ob_end_clean();

      $list = $this->getTestList();
      $test = array_pop($list);

      if ($this->pmo_level > 0)
         $message = explode('at [', $message);
      else
         $message = array($message);

      $html = '<div class="status fail">Fail</div>'.NL;
      $html .= '<div class="testName">'.$test.'</div>'.NL;
      $html .= '<div class="message">'.$this->_htmlEntities($message[0]).'</div>'.NL;
      print $html;
   }

   /**
    *    Paints a PHP error.
    *    @param string $message        Message is ignored.
    *    @access public
    */
   function paintError($message) {
      // trap the parent message since we want to output our own.
      ob_start();
      parent::paintError($message);
      ob_end_clean();

      $list = $this->getTestList();
      $test = array_pop($list);
      $message = explode('at [', $message);
      $html = '<div class="status fail">Error</div>'.NL;
      $html .= '<div class="testName">'.$test.'</div>'.NL;
      $html .= '<div class="message">'.$this->_htmlEntities($message[0]).'</div>'.NL;
      print $html;
   }

   /**
    *    Paints a PHP exception.
    *    @param Exception $exception        Exception to display.
    *    @access public
    */
   function paintException($exception) {
      // trap the parent message since we want to output our own.
      ob_start();
      parent::paintException($exception);
      ob_end_clean();

      $message = 'Unexpected exception of type [' . get_class($exception) .
               '] with message ['. $exception->getMessage() .
               '] in ['. $exception->getFile() .
               ' line ' . $exception->getLine() . ']';

      $list = $this->getTestList();
      $test = array_pop($list);
      $message = explode('at [', $message);
      $html = '<div class="status fail">Exception</div>'.NL;
      $html .= '<div class="testName">'.$test.'</div>'.NL;
      $html .= '<div class="message">'.$this->_htmlEntities($message[0]).'</div>'.NL;
      print $html;
   }

   /**
    *    Prints the message for skipping tests.
    *    @param string $message    Text of skip condition.
    *    @access public
    */
   function paintSkip($message) {
      ob_start();
      parent::paintSkip($message);
      ob_end_clean();

      $list = $this->getTestList();
      $test = array_pop($list);
      $message = explode('at [', $message);
      $html = '<div class="status skip">Pass</div>'.NL;
      $html .= '<div class="testName skip">'.$test.'</div>'.NL;
      $html .= '<div class="message skip">'.$this->_htmlEntities($message[0]).'</div>'.NL;
      print $html;
   }
}

