tutos-commits Mailing List for TUTOS
Projects / CRM / PLM / Calendar / Tasks / SCRUM / Test / Inventory
Brought to you by:
gokohnert
You can subscribe to this list here.
2001 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
(13) |
Aug
(214) |
Sep
(144) |
Oct
(22) |
Nov
(22) |
Dec
(93) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2002 |
Jan
(25) |
Feb
(31) |
Mar
(92) |
Apr
(70) |
May
(103) |
Jun
(130) |
Jul
(265) |
Aug
(325) |
Sep
(233) |
Oct
(244) |
Nov
(261) |
Dec
(157) |
2003 |
Jan
(101) |
Feb
(135) |
Mar
(148) |
Apr
(164) |
May
(53) |
Jun
(116) |
Jul
(149) |
Aug
(126) |
Sep
(45) |
Oct
(109) |
Nov
(36) |
Dec
(61) |
2004 |
Jan
(131) |
Feb
(236) |
Mar
(278) |
Apr
(259) |
May
(92) |
Jun
(110) |
Jul
(150) |
Aug
(64) |
Sep
(141) |
Oct
(141) |
Nov
(146) |
Dec
(65) |
2005 |
Jan
(70) |
Feb
(77) |
Mar
(129) |
Apr
(153) |
May
(161) |
Jun
(63) |
Jul
(42) |
Aug
(16) |
Sep
(30) |
Oct
(3) |
Nov
(8) |
Dec
(40) |
2006 |
Jan
(114) |
Feb
(16) |
Mar
(12) |
Apr
(15) |
May
(4) |
Jun
(9) |
Jul
(69) |
Aug
(27) |
Sep
(12) |
Oct
(80) |
Nov
(62) |
Dec
(41) |
2007 |
Jan
(34) |
Feb
(2) |
Mar
(38) |
Apr
(82) |
May
(61) |
Jun
(37) |
Jul
(16) |
Aug
(64) |
Sep
(7) |
Oct
(52) |
Nov
(18) |
Dec
(28) |
2008 |
Jan
(168) |
Feb
(26) |
Mar
(27) |
Apr
(19) |
May
(10) |
Jun
(58) |
Jul
(58) |
Aug
(91) |
Sep
(14) |
Oct
(23) |
Nov
(56) |
Dec
(38) |
2009 |
Jan
(58) |
Feb
(90) |
Mar
(204) |
Apr
(90) |
May
(27) |
Jun
(177) |
Jul
(116) |
Aug
(53) |
Sep
(42) |
Oct
(120) |
Nov
(51) |
Dec
(58) |
2010 |
Jan
(117) |
Feb
(231) |
Mar
(163) |
Apr
(90) |
May
(40) |
Jun
(139) |
Jul
(49) |
Aug
(118) |
Sep
(25) |
Oct
(80) |
Nov
(102) |
Dec
(99) |
2011 |
Jan
(176) |
Feb
(42) |
Mar
(60) |
Apr
(52) |
May
(30) |
Jun
(29) |
Jul
(27) |
Aug
(16) |
Sep
(51) |
Oct
(70) |
Nov
(63) |
Dec
(58) |
2012 |
Jan
(28) |
Feb
(26) |
Mar
(7) |
Apr
(12) |
May
(41) |
Jun
(61) |
Jul
(59) |
Aug
(38) |
Sep
(30) |
Oct
(28) |
Nov
(14) |
Dec
(31) |
2013 |
Jan
(24) |
Feb
(54) |
Mar
(45) |
Apr
(22) |
May
(35) |
Jun
(8) |
Jul
(18) |
Aug
(38) |
Sep
(11) |
Oct
(8) |
Nov
(19) |
Dec
(20) |
2014 |
Jan
(20) |
Feb
(22) |
Mar
(4) |
Apr
(6) |
May
(13) |
Jun
|
Jul
|
Aug
(1) |
Sep
|
Oct
|
Nov
|
Dec
(6) |
2015 |
Jan
|
Feb
(1) |
Mar
(4) |
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
(1) |
Dec
(1) |
2016 |
Jan
(4) |
Feb
|
Mar
|
Apr
(1) |
May
|
Jun
|
Jul
(2) |
Aug
(4) |
Sep
(1) |
Oct
(1) |
Nov
(1) |
Dec
|
2017 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
(3) |
Jul
(8) |
Aug
(13) |
Sep
(12) |
Oct
|
Nov
|
Dec
|
2018 |
Jan
|
Feb
|
Mar
(1) |
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
2019 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
(1) |
Dec
|
2020 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
(2) |
2021 |
Jan
|
Feb
(1) |
Mar
(1) |
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
S | M | T | W | T | F | S |
---|---|---|---|---|---|---|
|
|
|
|
|
1
(15) |
2
|
3
|
4
|
5
(18) |
6
|
7
|
8
|
9
|
10
|
11
|
12
|
13
(2) |
14
|
15
|
16
|
17
|
18
|
19
(1) |
20
|
21
|
22
|
23
|
24
|
25
|
26
|
27
|
28
|
29
(1) |
30
|
From: Gero K. <gok...@us...> - 2007-06-29 10:39:22
|
Update of /cvsroot/tutos/tutos/php In directory sc8-pr-cvs10.sourceforge.net:/tmp/cvs-serv13790 Modified Files: base.pinc Log Message: fix according to Support Request #1728202 Index: base.pinc =================================================================== RCS file: /cvsroot/tutos/tutos/php/base.pinc,v retrieving revision 1.95 retrieving revision 1.96 diff -u -d -r1.95 -r1.96 --- base.pinc 28 Dec 2006 20:23:43 -0000 1.95 +++ base.pinc 29 Jun 2007 10:39:18 -0000 1.96 @@ -704,8 +704,7 @@ global $current_user,$tutos,$g_hash; $msg = ''; - @reset($tutos[activemodules]); - while( list ($i,$f) = @each ($tutos[activemodules])) { + foreach ($tutos[activemodules] as $f) { $x = new $tutos[modules][$f][name]($this->dbconn); $msg .= $x->obj_save($current_user,$this); } |
From:
<sal...@ho...> - 2007-06-19 16:12:18
|
Dear Tutos user, We are Salvador Bueno and Dolores Gallego, Assistant Professors in Business Administration Department in Pablo de Olavide University at Seville, Spain. Our research involves an analysis about Open Source Software-Enterprise Resource Planning (OSS-ERP) and its impact at organization. We send you this mail to invite your participation in our study. Specifically, our analysis consists on development a Technological Acceptance Model of an OSS-ERP. We consider you are an OSS-ERP user and we appreciate the time necessary for answer our questionnaire. The name of the participants in this study will remain anonymous. I would be most grateful if you participate in this survey. You only have to answer the questionnaire in the following URL and answer online. You will consider for answer the distribution of OSS-ERP you use. http://aramis.upo.es/general/centros_depart/departamentos/invest/oss/ Please do not hesitate to contact us if you have any queries regarding this study. Best regards, Salvador Bueno, Ph D. _________________________________________________________________ Un amor, una aventura, compañía para un viaje. Regístrate gratis en MSN Amor & Amistad. http://match.msn.es/match/mt.cfm?pg=channel&tcid=162349 |
From: Gero K. <gok...@us...> - 2007-06-13 11:07:19
|
Update of /cvsroot/tutos/tutos/php/admin In directory sc8-pr-cvs10.sourceforge.net:/tmp/cvs-serv22877/php/admin Modified Files: customize_ins.php customize_show.php Log Message: added admin check Index: customize_show.php =================================================================== RCS file: /cvsroot/tutos/tutos/php/admin/customize_show.php,v retrieving revision 1.1 retrieving revision 1.2 diff -u -d -r1.1 -r1.2 --- customize_show.php 1 Jun 2007 16:31:41 -0000 1.1 +++ customize_show.php 13 Jun 2007 11:07:14 -0000 1.2 @@ -187,6 +187,15 @@ $this->name = $lang['AdminShow']; + if ( ! $this->user->isAdmin() ) { + echo $this->error("Only admins are allowed to see this"); + if ( $tutos[demo] == 1 ) { + echo $this->error("exceptionally enabled for this demo"); + } else { + return; + } + } + $this->lg1 = "en"; $this->mod = "php/localization"; if (isset($_GET['lg1'])) { @@ -204,4 +213,4 @@ <!-- CVS Info: $Id$ $Author$ ---> \ No newline at end of file +--> Index: customize_ins.php =================================================================== RCS file: /cvsroot/tutos/tutos/php/admin/customize_ins.php,v retrieving revision 1.2 retrieving revision 1.3 diff -u -d -r1.2 -r1.3 --- customize_ins.php 1 Jun 2007 16:31:41 -0000 1.2 +++ customize_ins.php 13 Jun 2007 11:07:14 -0000 1.3 @@ -18,6 +18,7 @@ include 'webelements.p3'; include 'permission.p3'; + $msg = ''; /* Check if user is allowed to use it */ check_user(); loadmodules('admin','customize'); @@ -31,73 +32,78 @@ $gotourl= addUrlParameter($gotourl,"lg1=". $_POST['lg1'],true); $gotourl= addUrlParameter($gotourl,"mod=". $_POST['mod'],true); - $use2 = false; - $use3 = false; + if ( ! $this->user->isAdmin() ) { + $msg .= "Only admins are allowed to use this"; + } - $fn2 = '../../'. $mod.'/'.$lg1. '_custom.p3'; - $tf2 = tempnam($tutos[sessionpath],'lg2'); - $f2 = fopen($tf2,"wb"); - fwrite($f2,"<?php\r\n"); - fwrite($f2,"#\r\n"); - fwrite($f2,"# Language Specific Custom File for ". $mod ." ". $lg1 ."\r\n"); - fwrite($f2,"# ".$fn2."\r\n"); - fwrite($f2,"#\r\n"); - fwrite($f2,"# generated by \t TUTOS admin - customize\r\n"); - fwrite($f2,"# DateTime: ". Date("d.M.Y H:i:s T") ."\r\n"); - fwrite($f2,"#\r\n"); - fwrite($f2,"#\r\n"); + if ($msg == "") { + $use2 = false; + $use3 = false; - $fn3 = '../../'.$mod .'/lang_custom.p3'; - $tf3 = tempnam($tutos[sessionpath],'lg3'); - $f3 = fopen($tf3,"wb"); - fwrite($f3,"<?php\r\n"); - fwrite($f3,"#\r\n"); - fwrite($f3,"# Global Custom File for ". $mod ." ". $lg1 ."\r\n"); - fwrite($f3,"# ".$fn3."\r\n"); - fwrite($f3,"#\r\n"); - fwrite($f3,"# generated by \t TUTOS admin - customize\r\n"); - fwrite($f3,"# DateTime: ". Date("d.M.Y H:i:s T") ."\r\n"); - fwrite($f3,"#\r\n"); - fwrite($f3,"#\r\n"); + $fn2 = '../../'. $mod.'/'.$lg1. '_custom.p3'; + $tf2 = tempnam($tutos[sessionpath],'lg2'); + $f2 = fopen($tf2,"wb"); + fwrite($f2,"<?php\r\n"); + fwrite($f2,"#\r\n"); + fwrite($f2,"# Language Specific Custom File for ". $mod ." ". $lg1 ."\r\n"); + fwrite($f2,"# ".$fn2."\r\n"); + fwrite($f2,"#\r\n"); + fwrite($f2,"# generated by \t TUTOS admin - customize\r\n"); + fwrite($f2,"# DateTime: ". Date("d.M.Y H:i:s T") ."\r\n"); + fwrite($f2,"#\r\n"); + fwrite($f2,"#\r\n"); - foreach ($_POST as $i => $f) { - if ( (strpos($i,'lg2') === 0) && ($f != '') ) { - $i1 = substr($i,4); - $p1 = strpos($i1,'__'); - if ($p1 > 0) { - $i2 = substr($i1,$p1+2); - $i1 = substr($i1,0,$p1); - if (! is_numeric($i2)) $i2 = "'".$i2."'"; - fwrite($f2,'$lang[\''. $i1 ."'][".$i2."] \t = \"". $f."\";\r\n"); - } else { - fwrite($f2,'$lang[\''. $i1 ."'] \t = \"". $f."\";\r\n"); - } - $use2 = true; - } - if ( (strpos($i,'lg3') === 0) && ($f != '') ) { - $i1 = substr($i,4); - $p1 = strpos($i1,'__'); - if ($p1 > 0) { - $i2 = substr($i1,$p1+2); - $i1 = substr($i1,0,$p1); - if (! is_numeric($i2)) $i2 = "'".$i2."'"; - fwrite($f3,'$lang[\''. $i1 ."'][".$i2."] \t = \"". $f."\";\r\n"); - } else { - fwrite($f3,'$lang[\''. $i1 ."'] \t = \"". $f."\";\r\n"); - } - $use3 = true; + $fn3 = '../../'.$mod .'/lang_custom.p3'; + $tf3 = tempnam($tutos[sessionpath],'lg3'); + $f3 = fopen($tf3,"wb"); + fwrite($f3,"<?php\r\n"); + fwrite($f3,"#\r\n"); + fwrite($f3,"# Global Custom File for ". $mod ." ". $lg1 ."\r\n"); + fwrite($f3,"# ".$fn3."\r\n"); + fwrite($f3,"#\r\n"); + fwrite($f3,"# generated by \t TUTOS admin - customize\r\n"); + fwrite($f3,"# DateTime: ". Date("d.M.Y H:i:s T") ."\r\n"); + fwrite($f3,"#\r\n"); + fwrite($f3,"#\r\n"); + + foreach ($_POST as $i => $f) { + if ( (strpos($i,'lg2') === 0) && ($f != '') ) { + $i1 = substr($i,4); + $p1 = strpos($i1,'__'); + if ($p1 > 0) { + $i2 = substr($i1,$p1+2); + $i1 = substr($i1,0,$p1); + if (! is_numeric($i2)) $i2 = "'".$i2."'"; + fwrite($f2,'$lang[\''. $i1 ."'][".$i2."] \t = \"". $f."\";\r\n"); + } else { + fwrite($f2,'$lang[\''. $i1 ."'] \t = \"". $f."\";\r\n"); + } + $use2 = true; + } + if ( (strpos($i,'lg3') === 0) && ($f != '') ) { + $i1 = substr($i,4); + $p1 = strpos($i1,'__'); + if ($p1 > 0) { + $i2 = substr($i1,$p1+2); + $i1 = substr($i1,0,$p1); + if (! is_numeric($i2)) $i2 = "'".$i2."'"; + fwrite($f3,'$lang[\''. $i1 ."'][".$i2."] \t = \"". $f."\";\r\n"); + } else { + fwrite($f3,'$lang[\''. $i1 ."'] \t = \"". $f."\";\r\n"); + } + $use3 = true; + } } - } - fwrite($f2,"#\r\n"); - fwrite($f2,"#\r\n"); - fwrite($f2,"?>\r\n"); - fclose($f2); + fwrite($f2,"#\r\n"); + fwrite($f2,"#\r\n"); + fwrite($f2,"?>\r\n"); + fclose($f2); - fwrite($f3,"#\r\n"); - fwrite($f3,"#\r\n"); - fwrite($f3,"?>\r\n"); - fclose($f3); + fwrite($f3,"#\r\n"); + fwrite($f3,"#\r\n"); + fwrite($f3,"?>\r\n"); + fclose($f3); # $f2 = fopen($tf2,"r"); # fpassthru ($f2); @@ -107,39 +113,40 @@ # fpassthru ($f3); # fclose($f3); - if ($use2) { - if (file_exists($fn2)) { - if (! is_writeable($fn2)) { - $msg .= $fn2 ." is not writeable !<br />"; - } else { - if (! copy ($tf2,$fn2)) $msg .= "copy ". $fn2 ." failed !<br />"; - } - } else { - if (! is_writeable(dirname($fn2))) { - $msg .= dirname($fn2) ." is not writeable !<br />"; - } else { - if (! copy ($tf2,$fn2)) $msg .= "copy ". $fn2 ." failed !<br />"; + if ($use2) { + if (file_exists($fn2)) { + if (! is_writeable($fn2)) { + $msg .= $fn2 ." is not writeable !<br />"; + } else { + if (! copy ($tf2,$fn2)) $msg .= "copy ". $fn2 ." failed !<br />"; + } + } else { + if (! is_writeable(dirname($fn2))) { + $msg .= dirname($fn2) ." is not writeable !<br />"; + } else { + if (! copy ($tf2,$fn2)) $msg .= "copy ". $fn2 ." failed !<br />"; + } } } - } - unlink($tf2); + unlink($tf2); - if ($use3) { - if (file_exists($fn3)) { - if (! is_writeable($fn3)) { - $msg .= $fn3 ." is not writeable !<br />"; - } else { - if (! copy ($tf3,$fn3)) $msg .= "copy ". $fn3 ." failed !<br />"; - } - } else { - if (! is_writeable(dirname($fn3))) { - $msg .= dirname($fn3) ." is not writeable !<br />"; - } else { - if (! copy ($tf3,$fn3)) $msg .= "copy ". $fn3 ." failed !<br />"; + if ($use3) { + if (file_exists($fn3)) { + if (! is_writeable($fn3)) { + $msg .= $fn3 ." is not writeable !<br />"; + } else { + if (! copy ($tf3,$fn3)) $msg .= "copy ". $fn3 ." failed !<br />"; + } + } else { + if (! is_writeable(dirname($fn3))) { + $msg .= dirname($fn3) ." is not writeable !<br />"; + } else { + if (! copy ($tf3,$fn3)) $msg .= "copy ". $fn3 ." failed !<br />"; + } } } + unlink($tf3); } - unlink($tf3); $gotourl = addMessage($gotourl,$msg,true); $gotourl = addSessionKey($gotourl,true); @@ -151,4 +158,4 @@ * CVS Info: $Id$ * $Author$ */ -?> \ No newline at end of file +?> |
From: Gero K. <gok...@us...> - 2007-06-13 10:44:03
|
Update of /cvsroot/tutos/tutos/php/resource In directory sc8-pr-cvs10.sourceforge.net:/tmp/cvs-serv11346/resource Modified Files: resource_overview.php Log Message: fix Index: resource_overview.php =================================================================== RCS file: /cvsroot/tutos/tutos/php/resource/resource_overview.php,v retrieving revision 1.22 retrieving revision 1.23 diff -u -d -r1.22 -r1.23 --- resource_overview.php 2 Jan 2007 19:59:59 -0000 1.22 +++ resource_overview.php 13 Jun 2007 10:44:01 -0000 1.23 @@ -106,6 +106,41 @@ echo $this->actionformEnd("resource_overview.php"); } /** + * action + */ + Function action() { + global $msg,$lang; + + if ( $_GET['action'] == -2 ) { + $this->dbconn->Begin("WORK"); + foreach ($_GET['mark'] as $key => $val) { + $p = new resource($this->dbconn); + $p = $p->read($val,$p); + if ( $p->id != $val ) { + continue; + } + if ( $p->del_ok() ) { + $msg .= sprintf($lang['ResDelI'], $p->getFullName()) ."<br />"; + $msg .= $p->delete(); + } else { + $msg .= $p->getLink() .": ". sprintf($lang['Err0023'],$lang[$p->getType()]); + } + unset($p); + } + $this->dbconn->Commit("WORK"); + return; + } + if ( $_GET['action'] == -4 ) { + $this->redirect = acl_action(); + return; + } + + if (class_exists('watchlist')) { + watchlist::parse_action($this->user); + } + + } + /** * navigate */ Function navigate() { |
From: Emmanuel D. <ede...@us...> - 2007-06-05 13:08:55
|
Update of /cvsroot/tutos/tutos/include/simpletest/extensions In directory sc8-pr-cvs10.sourceforge.net:/tmp/cvs-serv13989/include/simpletest/extensions Added Files: Tag: BRANCH-2-0 phpunit_test_case.php pear_test_case.php Log Message: TRY including TDD --- NEW FILE: phpunit_test_case.php --- <?php /** * adapter for SimpleTest to use PHPUnit test cases * @package SimpleTest * @subpackage Extensions * @version $Id: phpunit_test_case.php,v 1.1.2.1 2007/06/05 13:08:52 edeweerdt Exp $ */ /**#@+ * include SimpleTest files */ require_once(dirname(__FILE__) . '/../unit_tester.php'); require_once(dirname(__FILE__) . '/../expectation.php'); /**#@-*/ /** * Adapter for sourceforge PHPUnit test case to allow * legacy test cases to be used with SimpleTest. * @package SimpleTest * @subpackage Extensions */ class TestCase extends SimpleTestCase { /** * Constructor. Sets the test name. * @param $label Test name to display. * @public */ function TestCase($label) { $this->SimpleTestCase($label); } /** * Sends pass if the test condition resolves true, * a fail otherwise. * @param $condition Condition to test true. * @param $message Message to display. * @public */ function assert($condition, $message = false) { parent::assert(new TrueExpectation(), $condition, $message); } /** * Will test straight equality if set to loose * typing, or identity if not. * @param $first First value. * @param $second Comparison value. * @param $message Message to display. * @public */ function assertEquals($first, $second, $message = false) { parent::assert(new EqualExpectation($first), $second, $message); } /** * Simple string equality. * @param $first First value. * @param $second Comparison value. * @param $message Message to display. * @public */ function assertEqualsMultilineStrings($first, $second, $message = false) { parent::assert(new EqualExpectation($first), $second, $message); } /** * Tests a regex match. * @param $pattern Regex to match. * @param $subject String to search in. * @param $message Message to display. * @public */ function assertRegexp($pattern, $subject, $message = false) { parent::assert(new PatternExpectation($pattern), $subject, $message); } /** * Sends an error which we interpret as a fail * with a different message for compatibility. * @param $message Message to display. * @public */ function error($message) { parent::fail("Error triggered [$message]"); } /** * Accessor for name. * @public */ function name() { return $this->getLabel(); } } ?> --- NEW FILE: pear_test_case.php --- <?php /** * adapter for SimpleTest to use PEAR PHPUnit test cases * @package SimpleTest * @subpackage Extensions * @version $Id: pear_test_case.php,v 1.1.2.1 2007/06/05 13:08:52 edeweerdt Exp $ */ /**#@+ * include SimpleTest files */ require_once(dirname(__FILE__) . '/../dumper.php'); require_once(dirname(__FILE__) . '/../compatibility.php'); require_once(dirname(__FILE__) . '/../test_case.php'); require_once(dirname(__FILE__) . '/../expectation.php'); /**#@-*/ /** * Adapter for PEAR PHPUnit test case to allow * legacy PEAR test cases to be used with SimpleTest. * @package SimpleTest * @subpackage Extensions */ class PHPUnit_TestCase extends SimpleTestCase { var $_loosely_typed; /** * Constructor. Sets the test name. * @param $label Test name to display. * @public */ function PHPUnit_TestCase($label = false) { $this->SimpleTestCase($label); $this->_loosely_typed = false; } /** * Will test straight equality if set to loose * typing, or identity if not. * @param $first First value. * @param $second Comparison value. * @param $message Message to display. * @public */ function assertEquals($first, $second, $message = "%s", $delta = 0) { if ($this->_loosely_typed) { $expectation = &new EqualExpectation($first); } else { $expectation = &new IdenticalExpectation($first); } $this->assert($expectation, $second, $message); } /** * Passes if the value tested is not null. * @param $value Value to test against. * @param $message Message to display. * @public */ function assertNotNull($value, $message = "%s") { parent::assert(new TrueExpectation(), isset($value), $message); } /** * Passes if the value tested is null. * @param $value Value to test against. * @param $message Message to display. * @public */ function assertNull($value, $message = "%s") { parent::assert(new TrueExpectation(), !isset($value), $message); } /** * In PHP5 the identity test tests for the same * object. This is a reference test in PHP4. * @param $first First object handle. * @param $second Hopefully the same handle. * @param $message Message to display. * @public */ function assertSame(&$first, &$second, $message = "%s") { $dumper = &new SimpleDumper(); $message = sprintf( $message, "[" . $dumper->describeValue($first) . "] and [" . $dumper->describeValue($second) . "] should reference the same object"); return $this->assert( new TrueExpectation(), SimpleTestCompatibility::isReference($first, $second), $message); } /** * In PHP5 the identity test tests for the same * object. This is a reference test in PHP4. * @param $first First object handle. * @param $second Hopefully a different handle. * @param $message Message to display. * @public */ function assertNotSame(&$first, &$second, $message = "%s") { $dumper = &new SimpleDumper(); $message = sprintf( $message, "[" . $dumper->describeValue($first) . "] and [" . $dumper->describeValue($second) . "] should not be the same object"); return $this->assert( new falseExpectation(), SimpleTestCompatibility::isReference($first, $second), $message); } /** * Sends pass if the test condition resolves true, * a fail otherwise. * @param $condition Condition to test true. * @param $message Message to display. * @public */ function assertTrue($condition, $message = "%s") { parent::assert(new TrueExpectation(), $condition, $message); } /** * Sends pass if the test condition resolves false, * a fail otherwise. * @param $condition Condition to test false. * @param $message Message to display. * @public */ function assertFalse($condition, $message = "%s") { parent::assert(new FalseExpectation(), $condition, $message); } /** * Tests a regex match. Needs refactoring. * @param $pattern Regex to match. * @param $subject String to search in. * @param $message Message to display. * @public */ function assertRegExp($pattern, $subject, $message = "%s") { $this->assert(new PatternExpectation($pattern), $subject, $message); } /** * Tests the type of a value. * @param $value Value to take type of. * @param $type Hoped for type. * @param $message Message to display. * @public */ function assertType($value, $type, $message = "%s") { parent::assert(new TrueExpectation(), gettype($value) == strtolower($type), $message); } /** * Sets equality operation to act as a simple equal * comparison only, allowing a broader range of * matches. * @param $loosely_typed True for broader comparison. * @public */ function setLooselyTyped($loosely_typed) { $this->_loosely_typed = $loosely_typed; } /** * For progress indication during * a test amongst other things. * @return Usually one. * @public */ function countTestCases() { return $this->getSize(); } /** * Accessor for name, normally just the class * name. * @public */ function getName() { return $this->getLabel(); } /** * Does nothing. For compatibility only. * @param $name Dummy * @public */ function setName($name) { } } ?> |
From: Emmanuel D. <ede...@us...> - 2007-06-05 13:08:55
|
Update of /cvsroot/tutos/tutos/php/projectreport In directory sc8-pr-cvs10.sourceforge.net:/tmp/cvs-serv13989/php/projectreport Added Files: Tag: BRANCH-2-0 projectreport_test.php Log Message: TRY including TDD --- NEW FILE: projectreport_test.php --- <?php //ini_set("include_path","..:/usr2/web/tutos2/include:../bugtracking"); $tutos['base'] = "../.."; ini_set("include_path",".."); include_once 'webelements.p3'; include_once 'permission.p3'; require_once('projectreport/projectreport.pinc'); require_once('../../include/simpletest/unit_tester.php'); require_once('../../include/simpletest/reporter.php'); class TestOfProjectReport extends UnitTestCase { function testGetLast2PeriodBefore() { $today = new TutosDateTime(-1); $periods = projectreport::getLast2periodBefore($today); $this->dump($today,"Today"); $this->dump($periods,"Periods"); $this->assertTrue(1); } } $test = &new TestOfProjectReport(); $test->run(new HtmlReporter()); ?> |
From: Emmanuel D. <ede...@us...> - 2007-06-05 13:08:55
|
Update of /cvsroot/tutos/tutos/include/simpletest/test/support/collector In directory sc8-pr-cvs10.sourceforge.net:/tmp/cvs-serv13989/include/simpletest/test/support/collector Added Files: Tag: BRANCH-2-0 collectable.1 collectable.2 Log Message: TRY including TDD --- NEW FILE: collectable.1 --- --- NEW FILE: collectable.2 --- |
From: Emmanuel D. <ede...@us...> - 2007-06-05 13:08:55
|
Update of /cvsroot/tutos/tutos/include/simpletest/test/support In directory sc8-pr-cvs10.sourceforge.net:/tmp/cvs-serv13989/include/simpletest/test/support Added Files: Tag: BRANCH-2-0 latin1_sample upload_sample.txt supplementary_upload_sample.txt spl_examples.php Log Message: TRY including TDD --- NEW FILE: latin1_sample --- (This appears to be a binary file; contents omitted.) --- NEW FILE: upload_sample.txt --- Sample for testing file upload --- NEW FILE: supplementary_upload_sample.txt --- Some more text content --- NEW FILE: spl_examples.php --- <?php // $Id: spl_examples.php,v 1.1.2.1 2007/06/05 13:08:52 edeweerdt Exp $ class IteratorImplementation implements Iterator { function current() { } function next() { } function key() { } function valid() { } function rewind() { } } class IteratorAggregateImplementation implements IteratorAggregate { function getIterator() { } } ?> |
Update of /cvsroot/tutos/tutos/include/simpletest/test In directory sc8-pr-cvs10.sourceforge.net:/tmp/cvs-serv13989/include/simpletest/test Added Files: Tag: BRANCH-2-0 tag_test.php detached_test.php page_test.php shell_tester_test.php remote_test.php live_test.php expectation_test.php reflection_php4_test.php interfaces_test.php form_test.php user_agent_test.php acceptance_test.php adapter_test.php dumper_test.php compatibility_test.php mock_objects_test.php collector_test.php exceptions_test.php parse_error_test.php visual_test.php test_groups.php socket_test.php parser_test.php reflection_php5_test.php test_with_parse_error.php errors_test.php unit_tester_test.php shell_test.php xml_test.php cookies_test.php frames_test.php simpletest_test.php browser_test.php http_test.php unit_tests.php encoding_test.php url_test.php web_tester_test.php all_tests.php authentication_test.php Log Message: TRY including TDD --- NEW FILE: exceptions_test.php --- <?php require_once(dirname(__FILE__) . '/../exceptions.php'); require_once(dirname(__FILE__) . '/../expectation.php'); require_once(dirname(__FILE__) . '/../test_case.php'); Mock::generate('SimpleTestCase'); Mock::generate('SimpleExpectation'); class MyTestException extends Exception {} class HigherTestException extends MyTestException {} class OtherTestException extends Exception {} class TestOfExceptionExpectation extends UnitTestCase { function testExceptionClassAsStringWillMatchExceptionsRootedOnThatClass() { $expectation = new ExceptionExpectation('MyTestException'); $this->assertTrue($expectation->test(new MyTestException())); $this->assertTrue($expectation->test(new HigherTestException())); $this->assertFalse($expectation->test(new OtherTestException())); } function testMatchesClassAndMessageWhenExceptionExpected() { $expectation = new ExceptionExpectation(new MyTestException('Hello')); $this->assertTrue($expectation->test(new MyTestException('Hello'))); $this->assertFalse($expectation->test(new HigherTestException('Hello'))); $this->assertFalse($expectation->test(new OtherTestException('Hello'))); $this->assertFalse($expectation->test(new MyTestException('Goodbye'))); $this->assertFalse($expectation->test(new MyTestException())); } function testMessagelessExceptionMatchesOnlyOnClass() { $expectation = new ExceptionExpectation(new MyTestException()); $this->assertTrue($expectation->test(new MyTestException())); $this->assertFalse($expectation->test(new HigherTestException())); } } class TestOfExceptionTrap extends UnitTestCase { function testNoExceptionsInQueueMeansNoTestMessages() { $test = new MockSimpleTestCase(); $test->expectNever('assert'); $queue = new SimpleExceptionTrap(); $this->assertFalse($queue->isExpected($test, new Exception())); } function testMatchingExceptionGivesTrue() { $expectation = new MockSimpleExpectation(); $expectation->setReturnValue('test', true); $test = new MockSimpleTestCase(); $test->setReturnValue('assert', true); $queue = new SimpleExceptionTrap(); $queue->expectException($expectation, 'message'); $this->assertTrue($queue->isExpected($test, new Exception())); } function testMatchingExceptionTriggersAssertion() { $test = new MockSimpleTestCase(); $test->expectOnce('assert', array( '*', new ExceptionExpectation(new Exception()), 'message')); $queue = new SimpleExceptionTrap(); $queue->expectException(new ExceptionExpectation(new Exception()), 'message'); $queue->isExpected($test, new Exception()); } } class TestOfCatchingExceptions extends UnitTestCase { function testCanCatchAnyExpectedException() { $this->expectException(); throw new Exception(); } function testCanMatchExceptionByClass() { $this->expectException('MyTestException'); throw new HigherTestException(); } function testCanMatchExceptionExactly() { $this->expectException(new Exception('Ouch')); throw new Exception('Ouch'); } function testLastListedExceptionIsTheOneThatCounts() { $this->expectException('OtherTestException'); $this->expectException('MyTestException'); throw new HigherTestException(); } } ?> --- NEW FILE: browser_test.php --- <?php // $Id: browser_test.php,v 1.1.2.1 2007/06/05 13:08:50 edeweerdt Exp $ require_once(dirname(__FILE__) . '/../browser.php'); require_once(dirname(__FILE__) . '/../user_agent.php'); require_once(dirname(__FILE__) . '/../http.php'); require_once(dirname(__FILE__) . '/../page.php'); require_once(dirname(__FILE__) . '/../encoding.php'); Mock::generate('SimpleHttpResponse'); Mock::generate('SimplePage'); Mock::generate('SimpleForm'); Mock::generate('SimpleUserAgent'); Mock::generatePartial( 'SimpleBrowser', 'MockParseSimpleBrowser', array('_createUserAgent', '_parse')); Mock::generatePartial( 'SimpleBrowser', 'MockUserAgentSimpleBrowser', array('_createUserAgent')); class TestOfHistory extends UnitTestCase { function testEmptyHistoryHasFalseContents() { $history = &new SimpleBrowserHistory(); $this->assertIdentical($history->getUrl(), false); $this->assertIdentical($history->getParameters(), false); } function testCannotMoveInEmptyHistory() { $history = &new SimpleBrowserHistory(); $this->assertFalse($history->back()); $this->assertFalse($history->forward()); } function testCurrentTargetAccessors() { $history = &new SimpleBrowserHistory(); $history->recordEntry( new SimpleUrl('http://www.here.com/'), new SimpleGetEncoding()); $this->assertIdentical($history->getUrl(), new SimpleUrl('http://www.here.com/')); $this->assertIdentical($history->getParameters(), new SimpleGetEncoding()); } function testSecondEntryAccessors() { $history = &new SimpleBrowserHistory(); $history->recordEntry( new SimpleUrl('http://www.first.com/'), new SimpleGetEncoding()); $history->recordEntry( new SimpleUrl('http://www.second.com/'), new SimplePostEncoding(array('a' => 1))); $this->assertIdentical($history->getUrl(), new SimpleUrl('http://www.second.com/')); $this->assertIdentical( $history->getParameters(), new SimplePostEncoding(array('a' => 1))); } function testGoingBackwards() { $history = &new SimpleBrowserHistory(); $history->recordEntry( new SimpleUrl('http://www.first.com/'), new SimpleGetEncoding()); $history->recordEntry( new SimpleUrl('http://www.second.com/'), new SimplePostEncoding(array('a' => 1))); $this->assertTrue($history->back()); $this->assertIdentical($history->getUrl(), new SimpleUrl('http://www.first.com/')); $this->assertIdentical($history->getParameters(), new SimpleGetEncoding()); } function testGoingBackwardsOffBeginning() { $history = &new SimpleBrowserHistory(); $history->recordEntry( new SimpleUrl('http://www.first.com/'), new SimpleGetEncoding()); $this->assertFalse($history->back()); $this->assertIdentical($history->getUrl(), new SimpleUrl('http://www.first.com/')); $this->assertIdentical($history->getParameters(), new SimpleGetEncoding()); } function testGoingForwardsOffEnd() { $history = &new SimpleBrowserHistory(); $history->recordEntry( new SimpleUrl('http://www.first.com/'), new SimpleGetEncoding()); $this->assertFalse($history->forward()); $this->assertIdentical($history->getUrl(), new SimpleUrl('http://www.first.com/')); $this->assertIdentical($history->getParameters(), new SimpleGetEncoding()); } function testGoingBackwardsAndForwards() { $history = &new SimpleBrowserHistory(); $history->recordEntry( new SimpleUrl('http://www.first.com/'), new SimpleGetEncoding()); $history->recordEntry( new SimpleUrl('http://www.second.com/'), new SimplePostEncoding(array('a' => 1))); $this->assertTrue($history->back()); $this->assertTrue($history->forward()); $this->assertIdentical($history->getUrl(), new SimpleUrl('http://www.second.com/')); $this->assertIdentical( $history->getParameters(), new SimplePostEncoding(array('a' => 1))); } function testNewEntryReplacesNextOne() { $history = &new SimpleBrowserHistory(); $history->recordEntry( new SimpleUrl('http://www.first.com/'), new SimpleGetEncoding()); $history->recordEntry( new SimpleUrl('http://www.second.com/'), new SimplePostEncoding(array('a' => 1))); $history->back(); $history->recordEntry( new SimpleUrl('http://www.third.com/'), new SimpleGetEncoding()); $this->assertIdentical($history->getUrl(), new SimpleUrl('http://www.third.com/')); $this->assertIdentical($history->getParameters(), new SimpleGetEncoding()); } function testNewEntryDropsFutureEntries() { $history = &new SimpleBrowserHistory(); $history->recordEntry( new SimpleUrl('http://www.first.com/'), new SimpleGetEncoding()); $history->recordEntry( new SimpleUrl('http://www.second.com/'), new SimpleGetEncoding()); $history->recordEntry( new SimpleUrl('http://www.third.com/'), new SimpleGetEncoding()); $history->back(); $history->back(); $history->recordEntry( new SimpleUrl('http://www.fourth.com/'), new SimpleGetEncoding()); $this->assertIdentical($history->getUrl(), new SimpleUrl('http://www.fourth.com/')); $this->assertFalse($history->forward()); $history->back(); $this->assertIdentical($history->getUrl(), new SimpleUrl('http://www.first.com/')); $this->assertFalse($history->back()); } } class TestOfParsedPageAccess extends UnitTestCase { function &loadPage(&$page) { $response = &new MockSimpleHttpResponse($this); $agent = &new MockSimpleUserAgent($this); $agent->setReturnReference('fetchResponse', $response); $browser = &new MockParseSimpleBrowser($this); $browser->setReturnReference('_createUserAgent', $agent); $browser->setReturnReference('_parse', $page); $browser->SimpleBrowser(); $browser->get('http://this.com/page.html'); return $browser; } function testAccessorsWhenNoPage() { $agent = &new MockSimpleUserAgent($this); $browser = &new MockParseSimpleBrowser($this); $browser->setReturnReference('_createUserAgent', $agent); $browser->SimpleBrowser(); $this->assertEqual($browser->getContent(), ''); } function testParse() { $page = &new MockSimplePage(); $page->setReturnValue('getRequest', "GET here.html\r\n\r\n"); $page->setReturnValue('getRaw', 'Raw HTML'); $page->setReturnValue('getTitle', 'Here'); $page->setReturnValue('getFrameFocus', 'Frame'); $page->setReturnValue('getMimeType', 'text/html'); $page->setReturnValue('getResponseCode', 200); $page->setReturnValue('getAuthentication', 'Basic'); $page->setReturnValue('getRealm', 'Somewhere'); $page->setReturnValue('getTransportError', 'Ouch!'); $browser = &$this->loadPage($page); $this->assertEqual($browser->getRequest(), "GET here.html\r\n\r\n"); $this->assertEqual($browser->getContent(), 'Raw HTML'); $this->assertEqual($browser->getTitle(), 'Here'); $this->assertEqual($browser->getFrameFocus(), 'Frame'); $this->assertIdentical($browser->getResponseCode(), 200); $this->assertEqual($browser->getMimeType(), 'text/html'); $this->assertEqual($browser->getAuthentication(), 'Basic'); $this->assertEqual($browser->getRealm(), 'Somewhere'); $this->assertEqual($browser->getTransportError(), 'Ouch!'); } function testLinkAffirmationWhenPresent() { $page = &new MockSimplePage(); $page->setReturnValue('getUrlsByLabel', array('http://www.nowhere.com')); $page->expectOnce('getUrlsByLabel', array('a link label')); $browser = &$this->loadPage($page); $this->assertIdentical($browser->getLink('a link label'), 'http://www.nowhere.com'); } function testLinkAffirmationByIdWhenPresent() { $page = &new MockSimplePage(); $page->setReturnValue('getUrlById', 'a_page.com', array(99)); $page->setReturnValue('getUrlById', false, array('*')); $browser = &$this->loadPage($page); $this->assertIdentical($browser->getLinkById(99), 'a_page.com'); $this->assertFalse($browser->getLinkById(98)); } function testSettingFieldIsPassedToPage() { $page = &new MockSimplePage(); $page->expectOnce('setField', array(new SimpleByLabelOrName('key'), 'Value')); $page->setReturnValue('getField', 'Value'); $browser = &$this->loadPage($page); $this->assertEqual($browser->getField('key'), 'Value'); $browser->setField('key', 'Value'); } } class TestOfBrowserNavigation extends UnitTestCase { function &createBrowser(&$agent, &$page) { $browser = &new MockParseSimpleBrowser(); $browser->setReturnReference('_createUserAgent', $agent); $browser->setReturnReference('_parse', $page); $browser->SimpleBrowser(); return $browser; } function testClickLinkRequestsPage() { $agent = &new MockSimpleUserAgent(); $agent->setReturnReference('fetchResponse', new MockSimpleHttpResponse()); $agent->expectArgumentsAt( 0, 'fetchResponse', array(new SimpleUrl('http://this.com/page.html'), new SimpleGetEncoding())); $agent->expectArgumentsAt( 1, 'fetchResponse', array(new SimpleUrl('http://this.com/new.html'), new SimpleGetEncoding())); $agent->expectCallCount('fetchResponse', 2); $page = &new MockSimplePage(); $page->setReturnValue('getUrlsByLabel', array(new SimpleUrl('http://this.com/new.html'))); $page->expectOnce('getUrlsByLabel', array('New')); $page->setReturnValue('getRaw', 'A page'); $browser = &$this->createBrowser($agent, $page); $browser->get('http://this.com/page.html'); $this->assertTrue($browser->clickLink('New')); } function testClickLinkWithUnknownFrameStillRequestsWholePage() { $agent = &new MockSimpleUserAgent(); $agent->setReturnReference('fetchResponse', new MockSimpleHttpResponse()); $agent->expectArgumentsAt( 0, 'fetchResponse', array(new SimpleUrl('http://this.com/page.html'), new SimpleGetEncoding())); $target = new SimpleUrl('http://this.com/new.html'); $target->setTarget('missing'); $agent->expectArgumentsAt( 1, 'fetchResponse', array($target, new SimpleGetEncoding())); $agent->expectCallCount('fetchResponse', 2); $parsed_url = new SimpleUrl('http://this.com/new.html'); $parsed_url->setTarget('missing'); $page = &new MockSimplePage(); $page->setReturnValue('getUrlsByLabel', array($parsed_url)); $page->setReturnValue('hasFrames', false); $page->expectOnce('getUrlsByLabel', array('New')); $page->setReturnValue('getRaw', 'A page'); $browser = &$this->createBrowser($agent, $page); $browser->get('http://this.com/page.html'); $this->assertTrue($browser->clickLink('New')); } function testClickingMissingLinkFails() { $agent = &new MockSimpleUserAgent($this); $agent->setReturnReference('fetchResponse', new MockSimpleHttpResponse()); $page = &new MockSimplePage(); $page->setReturnValue('getUrlsByLabel', array()); $page->setReturnValue('getRaw', 'stuff'); $browser = &$this->createBrowser($agent, $page); $this->assertTrue($browser->get('http://this.com/page.html')); $this->assertFalse($browser->clickLink('New')); } function testClickIndexedLink() { $agent = &new MockSimpleUserAgent(); $agent->setReturnReference('fetchResponse', new MockSimpleHttpResponse()); $agent->expectArgumentsAt( 1, 'fetchResponse', array(new SimpleUrl('1.html'), new SimpleGetEncoding())); $agent->expectCallCount('fetchResponse', 2); $page = &new MockSimplePage(); $page->setReturnValue( 'getUrlsByLabel', array(new SimpleUrl('0.html'), new SimpleUrl('1.html'))); $page->setReturnValue('getRaw', 'A page'); $browser = &$this->createBrowser($agent, $page); $browser->get('http://this.com/page.html'); $this->assertTrue($browser->clickLink('New', 1)); } function testClinkLinkById() { $agent = &new MockSimpleUserAgent(); $agent->setReturnReference('fetchResponse', new MockSimpleHttpResponse()); $agent->expectArgumentsAt(1, 'fetchResponse', array( new SimpleUrl('http://this.com/link.html'), new SimpleGetEncoding())); $agent->expectCallCount('fetchResponse', 2); $page = &new MockSimplePage(); $page->setReturnValue('getUrlById', new SimpleUrl('http://this.com/link.html')); $page->expectOnce('getUrlById', array(2)); $page->setReturnValue('getRaw', 'A page'); $browser = &$this->createBrowser($agent, $page); $browser->get('http://this.com/page.html'); $this->assertTrue($browser->clickLinkById(2)); } function testClickingMissingLinkIdFails() { $agent = &new MockSimpleUserAgent(); $agent->setReturnReference('fetchResponse', new MockSimpleHttpResponse()); $page = &new MockSimplePage(); $page->setReturnValue('getUrlById', false); $browser = &$this->createBrowser($agent, $page); $browser->get('http://this.com/page.html'); $this->assertFalse($browser->clickLink(0)); } function testSubmitFormByLabel() { $agent = &new MockSimpleUserAgent(); $agent->setReturnReference('fetchResponse', new MockSimpleHttpResponse()); $agent->expectArgumentsAt(1, 'fetchResponse', array( new SimpleUrl('http://this.com/handler.html'), new SimplePostEncoding(array('a' => 'A')))); $agent->expectCallCount('fetchResponse', 2); $form = &new MockSimpleForm(); $form->setReturnValue('getAction', new SimpleUrl('http://this.com/handler.html')); $form->setReturnValue('getMethod', 'post'); $form->setReturnValue('submitButton', new SimplePostEncoding(array('a' => 'A'))); $form->expectOnce('submitButton', array(new SimpleByLabel('Go'), false)); $page = &new MockSimplePage(); $page->setReturnReference('getFormBySubmit', $form); $page->expectOnce('getFormBySubmit', array(new SimpleByLabel('Go'))); $page->setReturnValue('getRaw', 'stuff'); $browser = &$this->createBrowser($agent, $page); $browser->get('http://this.com/page.html'); $this->assertTrue($browser->clickSubmit('Go')); } function testDefaultSubmitFormByLabel() { $agent = &new MockSimpleUserAgent(); $agent->setReturnReference('fetchResponse', new MockSimpleHttpResponse()); $agent->expectArgumentsAt(1, 'fetchResponse', array( new SimpleUrl('http://this.com/page.html'), new SimpleGetEncoding(array('a' => 'A')))); $agent->expectCallCount('fetchResponse', 2); $form = &new MockSimpleForm(); $form->setReturnValue('getAction', new SimpleUrl('http://this.com/page.html')); $form->setReturnValue('getMethod', 'get'); $form->setReturnValue('submitButton', new SimpleGetEncoding(array('a' => 'A'))); $page = &new MockSimplePage(); $page->setReturnReference('getFormBySubmit', $form); $page->expectOnce('getFormBySubmit', array(new SimpleByLabel('Submit'))); $page->setReturnValue('getRaw', 'stuff'); $page->setReturnValue('getUrl', new SimpleUrl('http://this.com/page.html')); $browser = &$this->createBrowser($agent, $page); $browser->get('http://this.com/page.html'); $this->assertTrue($browser->clickSubmit()); } function testSubmitFormByName() { $agent = &new MockSimpleUserAgent(); $agent->setReturnReference('fetchResponse', new MockSimpleHttpResponse()); $form = &new MockSimpleForm(); $form->setReturnValue('getAction', new SimpleUrl('http://this.com/handler.html')); $form->setReturnValue('getMethod', 'post'); $form->setReturnValue('submitButton', new SimplePostEncoding(array('a' => 'A'))); $page = &new MockSimplePage(); $page->setReturnReference('getFormBySubmit', $form); $page->expectOnce('getFormBySubmit', array(new SimpleByName('me'))); $page->setReturnValue('getRaw', 'stuff'); $browser = &$this->createBrowser($agent, $page); $browser->get('http://this.com/page.html'); $this->assertTrue($browser->clickSubmitByName('me')); } function testSubmitFormById() { $agent = &new MockSimpleUserAgent(); $agent->setReturnReference('fetchResponse', new MockSimpleHttpResponse()); $form = &new MockSimpleForm(); $form->setReturnValue('getAction', new SimpleUrl('http://this.com/handler.html')); $form->setReturnValue('getMethod', 'post'); $form->setReturnValue('submitButton', new SimplePostEncoding(array('a' => 'A'))); $form->expectOnce('submitButton', array(new SimpleById(99), false)); $page = &new MockSimplePage(); $page->setReturnReference('getFormBySubmit', $form); $page->expectOnce('getFormBySubmit', array(new SimpleById(99))); $page->setReturnValue('getRaw', 'stuff'); $browser = &$this->createBrowser($agent, $page); $browser->get('http://this.com/page.html'); $this->assertTrue($browser->clickSubmitById(99)); } function testSubmitFormByImageLabel() { $agent = &new MockSimpleUserAgent(); $agent->setReturnReference('fetchResponse', new MockSimpleHttpResponse()); $form = &new MockSimpleForm(); $form->setReturnValue('getAction', new SimpleUrl('http://this.com/handler.html')); $form->setReturnValue('getMethod', 'post'); $form->setReturnValue('submitImage', new SimplePostEncoding(array('a' => 'A'))); $form->expectOnce('submitImage', array(new SimpleByLabel('Go!'), 10, 11, false)); $page = &new MockSimplePage(); $page->setReturnReference('getFormByImage', $form); $page->expectOnce('getFormByImage', array(new SimpleByLabel('Go!'))); $page->setReturnValue('getRaw', 'stuff'); $browser = &$this->createBrowser($agent, $page); $browser->get('http://this.com/page.html'); $this->assertTrue($browser->clickImage('Go!', 10, 11)); } function testSubmitFormByImageName() { $agent = &new MockSimpleUserAgent(); $agent->setReturnReference('fetchResponse', new MockSimpleHttpResponse()); $form = &new MockSimpleForm(); $form->setReturnValue('getAction', new SimpleUrl('http://this.com/handler.html')); $form->setReturnValue('getMethod', 'post'); $form->setReturnValue('submitImage', new SimplePostEncoding(array('a' => 'A'))); $form->expectOnce('submitImage', array(new SimpleByName('a'), 10, 11, false)); $page = &new MockSimplePage(); $page->setReturnReference('getFormByImage', $form); $page->expectOnce('getFormByImage', array(new SimpleByName('a'))); $page->setReturnValue('getRaw', 'stuff'); $browser = &$this->createBrowser($agent, $page); $browser->get('http://this.com/page.html'); $this->assertTrue($browser->clickImageByName('a', 10, 11)); } function testSubmitFormByImageId() { $agent = &new MockSimpleUserAgent(); $agent->setReturnReference('fetchResponse', new MockSimpleHttpResponse()); $form = &new MockSimpleForm(); $form->setReturnValue('getAction', new SimpleUrl('http://this.com/handler.html')); $form->setReturnValue('getMethod', 'post'); $form->setReturnValue('submitImage', new SimplePostEncoding(array('a' => 'A'))); $form->expectOnce('submitImage', array(new SimpleById(99), 10, 11, false)); $page = &new MockSimplePage(); $page->setReturnReference('getFormByImage', $form); $page->expectOnce('getFormByImage', array(new SimpleById(99))); $page->setReturnValue('getRaw', 'stuff'); $browser = &$this->createBrowser($agent, $page); $browser->get('http://this.com/page.html'); $this->assertTrue($browser->clickImageById(99, 10, 11)); } function testSubmitFormByFormId() { $agent = &new MockSimpleUserAgent(); $agent->setReturnReference('fetchResponse', new MockSimpleHttpResponse()); $agent->expectArgumentsAt(1, 'fetchResponse', array( new SimpleUrl('http://this.com/handler.html'), new SimplePostEncoding(array('a' => 'A')))); $agent->expectCallCount('fetchResponse', 2); $form = &new MockSimpleForm(); $form->setReturnValue('getAction', new SimpleUrl('http://this.com/handler.html')); $form->setReturnValue('getMethod', 'post'); $form->setReturnValue('submit', new SimplePostEncoding(array('a' => 'A'))); $page = &new MockSimplePage(); $page->setReturnReference('getFormById', $form); $page->expectOnce('getFormById', array(33)); $page->setReturnValue('getRaw', 'stuff'); $browser = &$this->createBrowser($agent, $page); $browser->get('http://this.com/page.html'); $this->assertTrue($browser->submitFormById(33)); } } class TestOfBrowserFrames extends UnitTestCase { function &createBrowser(&$agent) { $browser = &new MockUserAgentSimpleBrowser(); $browser->setReturnReference('_createUserAgent', $agent); $browser->SimpleBrowser(); return $browser; } function &createUserAgent($pages) { $agent = &new MockSimpleUserAgent(); foreach ($pages as $url => $raw) { $url = new SimpleUrl($url); $response = &new MockSimpleHttpResponse(); $response->setReturnValue('getUrl', $url); $response->setReturnValue('getContent', $raw); $agent->setReturnReference('fetchResponse', $response, array($url, '*')); } return $agent; } function testSimplePageHasNoFrames() { $browser = &$this->createBrowser($this->createUserAgent( array('http://site.with.no.frames/' => 'A non-framed page'))); $this->assertEqual( $browser->get('http://site.with.no.frames/'), 'A non-framed page'); $this->assertIdentical($browser->getFrames(), 'http://site.with.no.frames/'); } function testFramesetWithNoFrames() { $browser = &$this->createBrowser($this->createUserAgent( array('http://site.with.no.frames/' => '<frameset></frameset>'))); $this->assertEqual( $browser->get('http://site.with.no.frames/'), ''); $this->assertIdentical($browser->getFrames(), array()); } function testFramesetWithSingleFrame() { $frameset = '<frameset><frame name="a" src="frame.html"></frameset>'; $browser = &$this->createBrowser($this->createUserAgent(array( 'http://site.with.one.frame/' => $frameset, 'http://site.with.one.frame/frame.html' => 'A frame'))); $this->assertEqual( $browser->get('http://site.with.one.frame/'), 'A frame'); $this->assertIdentical( $browser->getFrames(), array('a' => 'http://site.with.one.frame/frame.html')); } function testTitleTakenFromFramesetPage() { $frameset = '<title>Frameset title</title>' . '<frameset><frame name="a" src="frame.html"></frameset>'; $browser = &$this->createBrowser($this->createUserAgent(array( 'http://site.with.one.frame/' => $frameset, 'http://site.with.one.frame/frame.html' => '<title>Page title</title>'))); $browser->get('http://site.with.one.frame/'); $this->assertEqual($browser->getTitle(), 'Frameset title'); } function testFramesetWithSingleUnnamedFrame() { $frameset = '<frameset><frame src="frame.html"></frameset>'; $browser = &$this->createBrowser($this->createUserAgent(array( 'http://site.with.one.frame/' => $frameset, 'http://site.with.one.frame/frame.html' => 'One frame'))); $this->assertEqual( $browser->get('http://site.with.one.frame/'), 'One frame'); $this->assertIdentical( $browser->getFrames(), array(1 => 'http://site.with.one.frame/frame.html')); } function testFramesetWithMultipleFrames() { $frameset = '<frameset>' . '<frame name="a" src="frame_a.html">' . '<frame name="b" src="frame_b.html">' . '<frame name="c" src="frame_c.html">' . '</frameset>'; $browser = &$this->createBrowser($this->createUserAgent(array( 'http://site.with.frames/' => $frameset, 'http://site.with.frames/frame_a.html' => 'A frame', 'http://site.with.frames/frame_b.html' => 'B frame', 'http://site.with.frames/frame_c.html' => 'C frame'))); $this->assertEqual( $browser->get('http://site.with.frames/'), 'A frameB frameC frame'); $this->assertIdentical($browser->getFrames(), array( 'a' => 'http://site.with.frames/frame_a.html', 'b' => 'http://site.with.frames/frame_b.html', 'c' => 'http://site.with.frames/frame_c.html')); } function testFrameFocusByName() { $frameset = '<frameset>' . '<frame name="a" src="frame_a.html">' . '<frame name="b" src="frame_b.html">' . '<frame name="c" src="frame_c.html">' . '</frameset>'; $browser = &$this->createBrowser($this->createUserAgent(array( 'http://site.with.frames/' => $frameset, 'http://site.with.frames/frame_a.html' => 'A frame', 'http://site.with.frames/frame_b.html' => 'B frame', 'http://site.with.frames/frame_c.html' => 'C frame'))); $browser->get('http://site.with.frames/'); $browser->setFrameFocus('a'); $this->assertEqual($browser->getContent(), 'A frame'); $browser->setFrameFocus('b'); $this->assertEqual($browser->getContent(), 'B frame'); $browser->setFrameFocus('c'); $this->assertEqual($browser->getContent(), 'C frame'); } function testFramesetWithSomeNamedFrames() { $frameset = '<frameset>' . '<frame name="a" src="frame_a.html">' . '<frame src="frame_b.html">' . '<frame name="c" src="frame_c.html">' . '<frame src="frame_d.html">' . '</frameset>'; $browser = &$this->createBrowser($this->createUserAgent(array( 'http://site.with.frames/' => $frameset, 'http://site.with.frames/frame_a.html' => 'A frame', 'http://site.with.frames/frame_b.html' => 'B frame', 'http://site.with.frames/frame_c.html' => 'C frame', 'http://site.with.frames/frame_d.html' => 'D frame'))); $this->assertEqual( $browser->get('http://site.with.frames/'), 'A frameB frameC frameD frame'); $this->assertIdentical($browser->getFrames(), array( 'a' => 'http://site.with.frames/frame_a.html', 2 => 'http://site.with.frames/frame_b.html', 'c' => 'http://site.with.frames/frame_c.html', 4 => 'http://site.with.frames/frame_d.html')); } function testFrameFocusWithMixedNamesAndIndexes() { $frameset = '<frameset>' . '<frame name="a" src="frame_a.html">' . '<frame src="frame_b.html">' . '<frame name="c" src="frame_c.html">' . '<frame src="frame_d.html">' . '</frameset>'; $browser = &$this->createBrowser($this->createUserAgent(array( 'http://site.with.frames/' => $frameset, 'http://site.with.frames/frame_a.html' => 'A frame', 'http://site.with.frames/frame_b.html' => 'B frame', 'http://site.with.frames/frame_c.html' => 'C frame', 'http://site.with.frames/frame_d.html' => 'D frame'))); $browser->get('http://site.with.frames/'); $browser->setFrameFocus('a'); $this->assertEqual($browser->getContent(), 'A frame'); $browser->setFrameFocus(2); $this->assertEqual($browser->getContent(), 'B frame'); $browser->setFrameFocus('c'); $this->assertEqual($browser->getContent(), 'C frame'); $browser->setFrameFocus(4); $this->assertEqual($browser->getContent(), 'D frame'); $browser->clearFrameFocus(); $this->assertEqual($browser->getContent(), 'A frameB frameC frameD frame'); } function testNestedFrameset() { $inner = '<frameset>' . '<frame name="page" src="page.html">' . '</frameset>'; $outer = '<frameset>' . '<frame name="inner" src="inner.html">' . '</frameset>'; $browser = &$this->createBrowser($this->createUserAgent(array( 'http://site.with.nested.frame/' => $outer, 'http://site.with.nested.frame/inner.html' => $inner, 'http://site.with.nested.frame/page.html' => 'The page'))); $this->assertEqual( $browser->get('http://site.with.nested.frame/'), 'The page'); $this->assertIdentical($browser->getFrames(), array( 'inner' => array( 'page' => 'http://site.with.nested.frame/page.html'))); } function testCanNavigateToNestedFrame() { $inner = '<frameset>' . '<frame name="one" src="one.html">' . '<frame name="two" src="two.html">' . '</frameset>'; $outer = '<frameset>' . '<frame name="inner" src="inner.html">' . '<frame name="three" src="three.html">' . '</frameset>'; $browser = &$this->createBrowser($this->createUserAgent(array( 'http://site.with.nested.frames/' => $outer, 'http://site.with.nested.frames/inner.html' => $inner, 'http://site.with.nested.frames/one.html' => 'Page one', 'http://site.with.nested.frames/two.html' => 'Page two', 'http://site.with.nested.frames/three.html' => 'Page three'))); $browser->get('http://site.with.nested.frames/'); $this->assertEqual($browser->getContent(), 'Page onePage twoPage three'); $this->assertTrue($browser->setFrameFocus('inner')); $this->assertEqual($browser->getFrameFocus(), array('inner')); $this->assertTrue($browser->setFrameFocus('one')); $this->assertEqual($browser->getFrameFocus(), array('inner', 'one')); $this->assertEqual($browser->getContent(), 'Page one'); $this->assertTrue($browser->setFrameFocus('two')); $this->assertEqual($browser->getFrameFocus(), array('inner', 'two')); $this->assertEqual($browser->getContent(), 'Page two'); $browser->clearFrameFocus(); $this->assertTrue($browser->setFrameFocus('three')); $this->assertEqual($browser->getFrameFocus(), array('three')); $this->assertEqual($browser->getContent(), 'Page three'); $this->assertTrue($browser->setFrameFocus('inner')); $this->assertEqual($browser->getContent(), 'Page onePage two'); } function testCanNavigateToNestedFrameByIndex() { $inner = '<frameset>' . '<frame src="one.html">' . '<frame src="two.html">' . '</frameset>'; $outer = '<frameset>' . '<frame src="inner.html">' . '<frame src="three.html">' . '</frameset>'; $browser = &$this->createBrowser($this->createUserAgent(array( 'http://site.with.nested.frames/' => $outer, 'http://site.with.nested.frames/inner.html' => $inner, 'http://site.with.nested.frames/one.html' => 'Page one', 'http://site.with.nested.frames/two.html' => 'Page two', 'http://site.with.nested.frames/three.html' => 'Page three'))); $browser->get('http://site.with.nested.frames/'); $this->assertEqual($browser->getContent(), 'Page onePage twoPage three'); $this->assertTrue($browser->setFrameFocusByIndex(1)); $this->assertEqual($browser->getFrameFocus(), array(1)); $this->assertTrue($browser->setFrameFocusByIndex(1)); $this->assertEqual($browser->getFrameFocus(), array(1, 1)); $this->assertEqual($browser->getContent(), 'Page one'); $this->assertTrue($browser->setFrameFocusByIndex(2)); $this->assertEqual($browser->getFrameFocus(), array(1, 2)); $this->assertEqual($browser->getContent(), 'Page two'); $browser->clearFrameFocus(); $this->assertTrue($browser->setFrameFocusByIndex(2)); $this->assertEqual($browser->getFrameFocus(), array(2)); $this->assertEqual($browser->getContent(), 'Page three'); $this->assertTrue($browser->setFrameFocusByIndex(1)); $this->assertEqual($browser->getContent(), 'Page onePage two'); } } ?> --- NEW FILE: detached_test.php --- <?php // $Id: detached_test.php,v 1.1.2.1 2007/06/05 13:08:45 edeweerdt Exp $ require_once('../detached.php'); require_once('../reporter.php'); // The following URL will depend on your own installation. $command = 'php ' . dirname(__FILE__) . '/visual_test.php xml'; $test = &new TestSuite('Remote tests'); $test->addTestCase(new DetachedTestCase($command)); if (SimpleReporter::inCli()) { exit ($test->run(new TextReporter()) ? 0 : 1); } $test->run(new HtmlReporter()); ?> --- NEW FILE: expectation_test.php --- <?php // $Id: expectation_test.php,v 1.1.2.1 2007/06/05 13:08:45 edeweerdt Exp $ require_once(dirname(__FILE__) . '/../expectation.php'); class TestOfEquality extends UnitTestCase { function testBoolean() { $is_true = &new EqualExpectation(true); $this->assertTrue($is_true->test(true)); $this->assertFalse($is_true->test(false)); } function testStringMatch() { $hello = &new EqualExpectation("Hello"); $this->assertTrue($hello->test("Hello")); $this->assertFalse($hello->test("Goodbye")); } function testInteger() { $fifteen = &new EqualExpectation(15); $this->assertTrue($fifteen->test(15)); $this->assertFalse($fifteen->test(14)); } function testFloat() { $pi = &new EqualExpectation(3.14); $this->assertTrue($pi->test(3.14)); $this->assertFalse($pi->test(3.15)); } function testArray() { $colours = &new EqualExpectation(array("r", "g", "b")); $this->assertTrue($colours->test(array("r", "g", "b"))); $this->assertFalse($colours->test(array("g", "b", "r"))); } function testHash() { $is_blue = &new EqualExpectation(array("r" => 0, "g" => 0, "b" => 255)); $this->assertTrue($is_blue->test(array("r" => 0, "g" => 0, "b" => 255))); $this->assertFalse($is_blue->test(array("r" => 0, "g" => 255, "b" => 0))); } function testHashWithOutOfOrderKeysShouldStillMatch() { $any_order = &new EqualExpectation(array('a' => 1, 'b' => 2)); $this->assertTrue($any_order->test(array('b' => 2, 'a' => 1))); } } class TestOfWithin extends UnitTestCase { function testWithinFloatingPointMargin() { $within = new WithinMarginExpectation(1.0, 0.2); $this->assertFalse($within->test(0.7)); $this->assertTrue($within->test(0.8)); $this->assertTrue($within->test(0.9)); $this->assertTrue($within->test(1.1)); $this->assertTrue($within->test(1.2)); $this->assertFalse($within->test(1.3)); } function testOutsideFloatingPointMargin() { $within = new OutsideMarginExpectation(1.0, 0.2); $this->assertTrue($within->test(0.7)); $this->assertFalse($within->test(0.8)); $this->assertFalse($within->test(1.2)); $this->assertTrue($within->test(1.3)); } } class TestOfInequality extends UnitTestCase { function testStringMismatch() { $not_hello = &new NotEqualExpectation("Hello"); $this->assertTrue($not_hello->test("Goodbye")); $this->assertFalse($not_hello->test("Hello")); } } class RecursiveNasty { var $_me; function RecursiveNasty() { $this->_me = $this; } } class TestOfIdentity extends UnitTestCase { function testType() { $string = &new IdenticalExpectation("37"); $this->assertTrue($string->test("37")); $this->assertFalse($string->test(37)); $this->assertFalse($string->test("38")); } function _testNastyPhp5Bug() { $this->assertFalse(new RecursiveNasty() != new RecursiveNasty()); } function _testReallyHorribleRecursiveStructure() { $hopeful = &new IdenticalExpectation(new RecursiveNasty()); $this->assertTrue($hopeful->test(new RecursiveNasty())); } } class TestOfNonIdentity extends UnitTestCase { function testType() { $string = &new NotIdenticalExpectation("37"); $this->assertTrue($string->test("38")); $this->assertTrue($string->test(37)); $this->assertFalse($string->test("37")); } } class TestOfPatterns extends UnitTestCase { function testWanted() { $pattern = &new PatternExpectation('/hello/i'); $this->assertTrue($pattern->test("Hello world")); $this->assertFalse($pattern->test("Goodbye world")); } function testUnwanted() { $pattern = &new NoPatternExpectation('/hello/i'); $this->assertFalse($pattern->test("Hello world")); $this->assertTrue($pattern->test("Goodbye world")); } } class ExpectedMethodTarget { function hasThisMethod() {} } class TestOfMethodExistence extends UnitTestCase { function testHasMethod() { $instance = &new ExpectedMethodTarget(); $expectation = &new MethodExistsExpectation('hasThisMethod'); $this->assertTrue($expectation->test($instance)); $expectation = &new MethodExistsExpectation('doesNotHaveThisMethod'); $this->assertFalse($expectation->test($instance)); } } class TestOfIsA extends UnitTestCase { function testString() { $expectation = &new IsAExpectation('string'); $this->assertTrue($expectation->test('Hello')); $this->assertFalse($expectation->test(5)); } function testBoolean() { $expectation = &new IsAExpectation('boolean'); $this->assertTrue($expectation->test(true)); $this->assertFalse($expectation->test(1)); } function testBool() { $expectation = &new IsAExpectation('bool'); $this->assertTrue($expectation->test(true)); $this->assertFalse($expectation->test(1)); } function testDouble() { $expectation = &new IsAExpectation('double'); $this->assertTrue($expectation->test(5.0)); $this->assertFalse($expectation->test(5)); } function testFloat() { $expectation = &new IsAExpectation('float'); $this->assertTrue($expectation->test(5.0)); $this->assertFalse($expectation->test(5)); } function testReal() { $expectation = &new IsAExpectation('real'); $this->assertTrue($expectation->test(5.0)); $this->assertFalse($expectation->test(5)); } function testInteger() { $expectation = &new IsAExpectation('integer'); $this->assertTrue($expectation->test(5)); $this->assertFalse($expectation->test(5.0)); } function testInt() { $expectation = &new IsAExpectation('int'); $this->assertTrue($expectation->test(5)); $this->assertFalse($expectation->test(5.0)); } } class TestOfNotA extends UnitTestCase { function testString() { $expectation = &new NotAExpectation('string'); $this->assertFalse($expectation->test('Hello')); $this->assertTrue($expectation->test(5)); } } ?> --- NEW FILE: remote_test.php --- <?php // $Id: remote_test.php,v 1.1.2.1 2007/06/05 13:08:45 edeweerdt Exp $ require_once('../remote.php'); require_once('../reporter.php'); // The following URL will depend on your own installation. if (isset($_SERVER['SCRIPT_URI'])) { $base_uri = $_SERVER['SCRIPT_URI']; } elseif (isset($_SERVER['HTTP_HOST']) && isset($_SERVER['PHP_SELF'])) { $base_uri = 'http://'. $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF']; }; $test_url = str_replace('remote_test.php', 'visual_test.php', $base_uri); $test = &new TestSuite('Remote tests'); $test->addTestCase(new RemoteTestCase($test_url . '?xml=yes', $test_url . '?xml=yes&dry=yes')); if (SimpleReporter::inCli()) { exit ($test->run(new TextReporter()) ? 0 : 1); } $test->run(new HtmlReporter()); ?> --- NEW FILE: parser_test.php --- <?php // $Id: parser_test.php,v 1.1.2.1 2007/06/05 13:08:48 edeweerdt Exp $ require_once(dirname(__FILE__) . '/../parser.php'); Mock::generate('SimpleHtmlSaxParser'); Mock::generate('SimpleSaxListener'); class TestOfParallelRegex extends UnitTestCase { function testNoPatterns() { $regex = &new ParallelRegex(false); $this->assertFalse($regex->match("Hello", $match)); $this->assertEqual($match, ""); } function testNoSubject() { $regex = &new ParallelRegex(false); $regex->addPattern(".*"); $this->assertTrue($regex->match("", $match)); $this->assertEqual($match, ""); } function testMatchAll() { $regex = &new ParallelRegex(false); $regex->addPattern(".*"); $this->assertTrue($regex->match("Hello", $match)); $this->assertEqual($match, "Hello"); } function testCaseSensitive() { $regex = &new ParallelRegex(true); $regex->addPattern("abc"); $this->assertTrue($regex->match("abcdef", $match)); $this->assertEqual($match, "abc"); $this->assertTrue($regex->match("AAABCabcdef", $match)); $this->assertEqual($match, "abc"); } function testCaseInsensitive() { $regex = &new ParallelRegex(false); $regex->addPattern("abc"); $this->assertTrue($regex->match("abcdef", $match)); $this->assertEqual($match, "abc"); $this->assertTrue($regex->match("AAABCabcdef", $match)); $this->assertEqual($match, "ABC"); } function testMatchMultiple() { $regex = &new ParallelRegex(true); $regex->addPattern("abc"); $regex->addPattern("ABC"); $this->assertTrue($regex->match("abcdef", $match)); $this->assertEqual($match, "abc"); $this->assertTrue($regex->match("AAABCabcdef", $match)); $this->assertEqual($match, "ABC"); $this->assertFalse($regex->match("Hello", $match)); } function testPatternLabels() { $regex = &new ParallelRegex(false); $regex->addPattern("abc", "letter"); $regex->addPattern("123", "number"); $this->assertIdentical($regex->match("abcdef", $match), "letter"); $this->assertEqual($match, "abc"); $this->assertIdentical($regex->match("0123456789", $match), "number"); $this->assertEqual($match, "123"); } } class TestOfStateStack extends UnitTestCase { function testStartState() { $stack = &new SimpleStateStack("one"); $this->assertEqual($stack->getCurrent(), "one"); } function testExhaustion() { $stack = &new SimpleStateStack("one"); $this->assertFalse($stack->leave()); } function testStateMoves() { $stack = &new SimpleStateStack("one"); $stack->enter("two"); $this->assertEqual($stack->getCurrent(), "two"); $stack->enter("three"); $this->assertEqual($stack->getCurrent(), "three"); $this->assertTrue($stack->leave()); $this->assertEqual($stack->getCurrent(), "two"); $stack->enter("third"); $this->assertEqual($stack->getCurrent(), "third"); $this->assertTrue($stack->leave()); $this->assertTrue($stack->leave()); $this->assertEqual($stack->getCurrent(), "one"); } } class TestParser { function accept() { } function a() { } function b() { } } Mock::generate('TestParser'); class TestOfLexer extends UnitTestCase { function testEmptyPage() { $handler = &new MockTestParser(); $handler->expectNever("accept"); $handler->setReturnValue("accept", true); $handler->expectNever("accept"); $handler->setReturnValue("accept", true); $lexer = &new SimpleLexer($handler); $lexer->addPattern("a+"); $this->assertTrue($lexer->parse("")); } function testSinglePattern() { $handler = &new MockTestParser(); $handler->expectArgumentsAt(0, "accept", array("aaa", LEXER_MATCHED)); $handler->expectArgumentsAt(1, "accept", array("x", LEXER_UNMATCHED)); $handler->expectArgumentsAt(2, "accept", array("a", LEXER_MATCHED)); $handler->expectArgumentsAt(3, "accept", array("yyy", LEXER_UNMATCHED)); $handler->expectArgumentsAt(4, "accept", array("a", LEXER_MATCHED)); $handler->expectArgumentsAt(5, "accept", array("x", LEXER_UNMATCHED)); $handler->expectArgumentsAt(6, "accept", array("aaa", LEXER_MATCHED)); $handler->expectArgumentsAt(7, "accept", array("z", LEXER_UNMATCHED)); $handler->expectCallCount("accept", 8); $handler->setReturnValue("accept", true); $lexer = &new SimpleLexer($handler); $lexer->addPattern("a+"); $this->assertTrue($lexer->parse("aaaxayyyaxaaaz")); } function testMultiplePattern() { $handler = &new MockTestParser(); $target = array("a", "b", "a", "bb", "x", "b", "a", "xxxxxx", "a", "x"); for ($i = 0; $i < count($target); $i++) { $handler->expectArgumentsAt($i, "accept", array($target[$i], '*')); } $handler->expectCallCount("accept", count($target)); $handler->setReturnValue("accept", true); $lexer = &new SimpleLexer($handler); $lexer->addPattern("a+"); $lexer->addPattern("b+"); $this->assertTrue($lexer->parse("ababbxbaxxxxxxax")); } } class TestOfLexerModes extends UnitTestCase { function testIsolatedPattern() { $handler = &new MockTestParser(); $handler->expectArgumentsAt(0, "a", array("a", LEXER_MATCHED)); $handler->expectArgumentsAt(1, "a", array("b", LEXER_UNMATCHED)); $handler->expectArgumentsAt(2, "a", array("aa", LEXER_MATCHED)); $handler->expectArgumentsAt(3, "a", array("bxb", LEXER_UNMATCHED)); $handler->expectArgumentsAt(4, "a", array("aaa", LEXER_MATCHED)); $handler->expectArgumentsAt(5, "a", array("x", LEXER_UNMATCHED)); $handler->expectArgumentsAt(6, "a", array("aaaa", LEXER_MATCHED)); $handler->expectArgumentsAt(7, "a", array("x", LEXER_UNMATCHED)); $handler->expectCallCount("a", 8); $handler->setReturnValue("a", true); $lexer = &new SimpleLexer($handler, "a"); $lexer->addPattern("a+", "a"); $lexer->addPattern("b+", "b"); $this->assertTrue($lexer->parse("abaabxbaaaxaaaax")); } function testModeChange() { $handler = &new MockTestParser(); $handler->expectArgumentsAt(0, "a", array("a", LEXER_MATCHED)); $handler->expectArgumentsAt(1, "a", array("b", LEXER_UNMATCHED)); $handler->expectArgumentsAt(2, "a", array("aa", LEXER_MATCHED)); $handler->expectArgumentsAt(3, "a", array("b", LEXER_UNMATCHED)); $handler->expectArgumentsAt(4, "a", array("aaa", LEXER_MATCHED)); $handler->expectArgumentsAt(0, "b", array(":", LEXER_ENTER)); $handler->expectArgumentsAt(1, "b", array("a", LEXER_UNMATCHED)); $handler->expectArgumentsAt(2, "b", array("b", LEXER_MATCHED)); $handler->expectArgumentsAt(3, "b", array("a", LEXER_UNMATCHED)); $handler->expectArgumentsAt(4, "b", array("bb", LEXER_MATCHED)); $handler->expectArgumentsAt(5, "b", array("a", LEXER_UNMATCHED)); $handler->expectArgumentsAt(6, "b", array("bbb", LEXER_MATCHED)); $handler->expectArgumentsAt(7, "b", array("a", LEXER_UNMATCHED)); $handler->expectCallCount("a", 5); $handler->expectCallCount("b", 8); $handler->setReturnValue("a", true); $handler->setReturnValue("b", true); $lexer = &new SimpleLexer($handler, "a"); $lexer->addPattern("a+", "a"); $lexer->addEntryPattern(":", "a", "b"); $lexer->addPattern("b+", "b"); $this->assertTrue($lexer->parse("abaabaaa:ababbabbba")); } function testNesting() { $handler = &new MockTestParser(); $handler->setReturnValue("a", true); $handler->setReturnValue("b", true); $handler->expectArgumentsAt(0, "a", array("aa", LEXER_MATCHED)); $handler->expectArgumentsAt(1, "a", array("b", LEXER_UNMATCHED)); $handler->expectArgumentsAt(2, "a", array("aa", LEXER_MATCHED)); $handler->expectArgumentsAt(3, "a", array("b", LEXER_UNMATCHED)); $handler->expectArgumentsAt(0, "b", array("(", LEXER_ENTER)); $handler->expectArgumentsAt(1, "b", array("bb", LEXER_MATCHED)); $handler->expectArgumentsAt(2, "b", array("a", LEXER_UNMATCHED)); $handler->expectArgumentsAt(3, "b", array("bb", LEXER_MATCHED)); $handler->expectArgumentsAt(4, "b", array(")", LEXER_EXIT)); $handler->expectArgumentsAt(4, "a", array("aa", LEXER_MATCHED)); $handler->expectArgumentsAt(5, "a", array("b", LEXER_UNMATCHED)); $handler... [truncated message content] |
Update of /cvsroot/tutos/tutos/include/simpletest/docs/fr In directory sc8-pr-cvs10.sourceforge.net:/tmp/cvs-serv13989/include/simpletest/docs/fr Added Files: Tag: BRANCH-2-0 authentication_documentation.html overview.html browser_documentation.html expectation_documentation.html partial_mocks_documentation.html unit_test_documentation.html mock_objects_documentation.html index.html docs.css group_test_documentation.html web_tester_documentation.html form_testing_documentation.html reporter_documentation.html server_stubs_documentation.html Log Message: TRY including TDD --- NEW FILE: expectation_documentation.html --- <html> <head> <META http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Documentation SimpleTest : étendre le testeur unitaire avec des classes d'attentes supplémentaires</title> <link rel="stylesheet" type="text/css" href="docs.css" title="Styles"> </head> <body> <div class="menu_back"> <div class="menu"> <a href="index.html">SimpleTest</a> | <a href="overview.html">Overview</a> | <a href="unit_test_documentation.html">Unit tester</a> | <a href="group_test_documentation.html">Group tests</a> | <a href="mock_objects_documentation.html">Mock objects</a> | <a href="partial_mocks_documentation.html">Partial mocks</a> | <a href="reporter_documentation.html">Reporting</a> | <a href="expectation_documentation.html">Expectations</a> | <a href="web_tester_documentation.html">Web tester</a> | <a href="form_testing_documentation.html">Testing forms</a> | <a href="authentication_documentation.html">Authentication</a> | <a href="browser_documentation.html">Scriptable browser</a> </div> </div> <h1>Documentation sur les attentes</h1> This page... <ul> <li> Utiliser les attentes <a href="#fantaisie">pour des tests plus précis avec des objets fantaisie</a> </li> <li> <a href="#comportement">Changer le comportement d'un objet fantaisie</a> avec des attentes </li> <li> <a href="#etendre">Créer des attentes</a> </li> <li> Par dessous SimpleTest <a href="#unitaire">utilise des classes d'attente</a> </li> </ul> <div class="content"> <p> <a class="target" name="fantaisie"> <h2>Plus de contrôle sur les objets fantaisie</h2> </a> </p> <p> Le comportement par défaut des <a href="mock_objects_documentation.html">objets fantaisie</a> dans <a href="SimpleTest" rel="nofollow">http://sourceforge.net/projects/simpletest/">SimpleTest</a> est soit une correspondance identique sur l'argument, soit l'acceptation de n'importe quel argument. Pour la plupart des tests, c'est suffisant. Cependant il est parfois nécessaire de ramollir un scénario de test. </p> <p> Un des endroits où un test peut être trop serré est la reconnaissance textuelle. Prenons l'exemple d'un composant qui produirait un message d'erreur utile lorsque quelque chose plante. Il serait utile de tester que l'erreur correcte est renvoyée, mais le texte proprement dit risque d'être plutôt long. Si vous testez le texte dans son ensemble alors à chaque modification de ce même message -- même un point ou une virgule -- vous aurez à revenir sur la suite de test pour la modifier. </p> <p> Voici un cas concret, nous avons un service d'actualités qui a échoué dans sa tentative de connexion à sa source distante. <pre> <strong>class NewsService { ... function publish(&$writer) { if (! $this->isConnected()) { $writer->write('Cannot connect to news service "' . $this->_name . '" at this time. ' . 'Please try again later.'); } ... } }</strong> </pre> Là il envoie son contenu vers un classe <span class="new_code">Writer</span>. Nous pourrions tester ce comportement avec un <span class="new_code">MockWriter</span>... <pre> class TestOfNewsService extends UnitTestCase { ... function testConnectionFailure() {<strong> $writer = &new MockWriter($this); $writer->expectOnce('write', array( 'Cannot connect to news service ' . '"BBC News" at this time. ' . 'Please try again later.')); $service = &new NewsService('BBC News'); $service->publish($writer); $writer->tally();</strong> } } </pre> C'est un bon exemple d'un test fragile. Si nous décidons d'ajouter des instructions complémentaires, par exemple proposer une source d'actualités alternative, nous casserons nos tests par la même occasion sans pourtant avoir modifié une seule fonctionnalité. </p> <p> Pour contourner ce problème, nous voudrions utiliser un test avec une expression rationnelle plutôt qu'une correspondance exacte. Nous pouvons y parvenir avec... <pre> class TestOfNewsService extends UnitTestCase { ... function testConnectionFailure() { $writer = &new MockWriter($this);<strong> $writer->expectOnce( 'write', array(new WantedPatternExpectation('/cannot connect/i')));</strong> $service = &new NewsService('BBC News'); $service->publish($writer); $writer->tally(); } } </pre> Plutôt que de transmettre le paramètre attendu au <span class="new_code">MockWriter</span>, nous envoyons une classe d'attente appelée <span class="new_code">WantedPatternExpectation</span>. L'objet fantaisie est suffisamment élégant pour reconnaître qu'il s'agit d'un truc spécial et pour le traiter différemment. Plutôt que de comparer l'argument entrant à cet objet, il utilise l'objet attente lui-même pour exécuter le test. </p> <p> <span class="new_code">WantedPatternExpectation</span> utilise l'expression rationnelle pour la comparaison avec son constructeur. A chaque fois qu'une comparaison est fait à travers <span class="new_code">MockWriter</span> par rapport à cette classe attente, elle fera un <span class="new_code">preg_match()</span> avec ce motif. Dans notre scénario de test ci-dessus, aussi longtemps que la chaîne "cannot connect" apparaît dans le texte, la fantaisie transmettra un succès au testeur unitaire. Peu importe le reste du texte. </p> <p> Les classes attente possibles sont... <table> <tbody> <tr> <td><span class="new_code">EqualExpectation</span></td><td>Une égalité, plutôt que la plus forte comparaison à l'identique</td> </tr> <tr> <td><span class="new_code">NotEqualExpectation</span></td><td>Une comparaison sur la non-égalité</td> </tr> <tr> <td><span class="new_code">IndenticalExpectation</span></td><td>La vérification par défaut de l'objet fantaisie qui doit correspondre exactement</td> </tr> <tr> <td><span class="new_code">NotIndenticalExpectation</span></td><td>Inverse la logique de l'objet fantaisie</td> </tr> <tr> <td><span class="new_code">WantedPatternExpectation</span></td><td>Utilise une expression rationnelle Perl pour comparer une chaîne</td> </tr> <tr> <td><span class="new_code">NoUnwantedPatternExpectation</span></td><td>Passe seulement si l'expression rationnelle Perl échoue</td> </tr> <tr> <td><span class="new_code">IsAExpectation</span></td><td>Vérifie le type ou le nom de la classe uniquement</td> </tr> <tr> <td><span class="new_code">NotAExpectation</span></td><td>L'opposé de <span class="new_code">IsAExpectation</span></td> </tr> <tr> <td><span class="new_code">MethodExistsExpectation</span></td><td>Vérifie si la méthode est disponible sur un objet</td> </tr> </tbody> </table> La plupart utilisent la valeur attendue dans le constructeur. Les exceptions sont les vérifications sur motif, qui utilisent une expression rationnelle, ainsi que <span class="new_code">IsAExpectation</span> et <span class="new_code">NotAExpectation</span>, qui prennent un type ou un nom de classe comme chaîne. </p> <p> <a class="target" name="comportement"> <h2>Utiliser les attentes pour contrôler les bouchons serveur</h2> </a> </p> <p> Les classes attente peuvent servir à autre chose que l'envoi d'assertions depuis les objets fantaisie, afin de choisir le comportement d'un <a href="mock_objects_documentation.html">objet fantaisie</a> ou celui d'un <a href="server_stubs_documentation.html">bouchon serveur</a>. A chaque fois qu'une liste d'arguments est donnée, une liste d'objets d'attente peut être insérée à la place. </p> <p> Mettons que nous voulons qu'un bouchon serveur d'autorisation simule une connexion réussie seulement si il reçoit un objet de session valide. Nous pouvons y arriver avec ce qui suit... <pre> Stub::generate('Authorisation'); <strong> $authorisation = new StubAuthorisation(); $authorisation->setReturnValue( 'isAllowed', true, array(new IsAExpectation('Session', 'Must be a session'))); $authorisation->setReturnValue('isAllowed', false);</strong> </pre> Le comportement par défaut du bouchon serveur est défini pour renvoyer <span class="new_code">false</span> quand <span class="new_code">isAllowed</span> est appelé. Lorsque nous appelons cette méthode avec un unique paramètre qui est un objet <span class="new_code">Session</span>, il renverra <span class="new_code">true</span>. Nous avons aussi ajouté un deuxième paramètre comme message. Il sera affiché dans le message d'erreur de l'objet fantaisie si l'attente est la cause de l'échec. </p> <p> Ce niveau de sophistication est rarement utile : il n'est inclut que pour être complet. </p> <p> <a class="target" name="etendre"> <h2>Créer vos propres attentes</h2> </a> </p> <p> Les classes d'attentes ont une structure très simple. Tellement simple qu'il devient très simple de créer vos propres version de logique pour des tests utilisés couramment. </p> <p> Par exemple voici la création d'une classe pour tester la validité d'adresses IP. Pour fonctionner correctement avec les bouchons serveurs et les objets fantaisie, cette nouvelle classe d'attente devrait étendre <span class="new_code">SimpleExpectation</span>... <pre> <strong>class ValidIp extends SimpleExpectation { function test($ip) { return (ip2long($ip) != -1); } function testMessage($ip) { return "Address [$ip] should be a valid IP address"; } }</strong> </pre> Il n'y a véritablement que deux méthodes à mettre en place. La méthode <span class="new_code">test()</span> devrait renvoyer un <span class="new_code">true</span> si l'attente doit passer, et une erreur <span class="new_code">false</span> dans le cas contraire. La méthode <span class="new_code">testMessage()</span> ne devrait renvoyer que du texte utile à la compréhension du test en lui-même. </p> <p> Cette classe peut désormais être employée à la place des classes d'attente précédentes. </p> <p> <a class="target" name="unitaire"> <h2>Sous le capot du testeur unitaire</h2> </a> </p> <p> Le <a href="framework" rel="nofollow">http://sourceforge.net/projects/simpletest/">framework de test unitaire SimpleTest</a> utilise aussi dans son coeur des classes d'attente pour la <a href="unit_test_documentation.html">classe UnitTestCase</a>. Nous pouvons aussi tirer parti de ces mécanismes pour réutiliser nos propres classes attente à l'intérieur même des suites de test. </p> <p> La méthode la plus directe est d'utiliser la méthode <span class="new_code">SimpleTest::assertExpectation()</span> pour effectuer le test... <pre> <strong>class TestOfNetworking extends UnitTestCase { ... function testGetValidIp() { $server = &new Server(); $this->assertExpectation( new ValidIp(), $server->getIp(), 'Server IP address->%s'); } }</strong> </pre> C'est plutôt sale par rapport à notre syntaxe habituelle du type <span class="new_code">assert...()</span>. </p> <p> Pour un cas aussi simple, nous créons d'ordinaire une méthode d'assertion distincte en utilisant la classe d'attente. Supposons un instant que notre attente soit un peu plus compliquée et que par conséquent nous souhaitions la réutiliser, nous obtenons... <pre> class TestOfNetworking extends UnitTestCase { ...<strong> function assertValidIp($ip, $message = '%s') { $this->assertExpectation(new ValidIp(), $ip, $message); }</strong> function testGetValidIp() { $server = &new Server();<strong> $this->assertValidIp( $server->getIp(), 'Server IP address->%s');</strong> } } </pre> Il est peu probable que nous ayons besoin de ce niveau de contrôle sur la machinerie de test. Il est assez rare que le besoin d'une attente dépasse le stade de la reconnaissance d'un motif. De plus, les classes d'attente complexes peuvent rendre les tests difficiles à lire et à déboguer. Ces mécanismes sont véritablement là pour les auteurs de système qui étendront le framework de test pour leurs propres outils de test. </p> </div> References and related information... <ul> <li> La page du projet SimpleTest sur <a href="SourceForge." rel="nofollow">http://sourceforge.net/projects/simpletest/">SourceForge</a>. </li> <li> La page de téléchargement de SimpleTest sur <a href="LastCraft." rel="nofollow">http://www.lastcraft.com/simple_test.php">LastCraft</a>. </li> <li> Les attentes imitent les contraintes dans <a href="JMock." rel="nofollow">http://www.jmock.org/">JMock</a>. </li> <li> <a href="L'API" rel="nofollow">http://simpletest.sourceforge.net/">L'API complète pour SimpleTest</a> réalisé avec PHPDoc. </li> </ul> <div class="menu_back"> <div class="menu"> <a href="index.html">SimpleTest</a> | <a href="overview.html">Overview</a> | <a href="unit_test_documentation.html">Unit tester</a> | <a href="group_test_documentation.html">Group tests</a> | <a href="mock_objects_documentation.html">Mock objects</a> | <a href="partial_mocks_documentation.html">Partial mocks</a> | <a href="reporter_documentation.html">Reporting</a> | <a href="expectation_documentation.html">Expectations</a> | <a href="web_tester_documentation.html">Web tester</a> | <a href="form_testing_documentation.html">Testing forms</a> | <a href="authentication_documentation.html">Authentication</a> | <a href="browser_documentation.html">Scriptable browser</a> </div> </div> <div class="copyright"> Copyright<br>Marcus Baker 2006 </div> </body> </html> --- NEW FILE: index.html --- <html> <head> <META http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title> Prise en main rapide de SimpleTest pour PHP - Tests unitaire et objets fantaisie pour PHP </title> <link rel="stylesheet" type="text/css" href="docs.css" title="Styles"> </head> <body> <div class="menu_back"> <div class="menu"> <a href="index.html">SimpleTest</a> | <a href="overview.html">Overview</a> | <a href="unit_test_documentation.html">Unit tester</a> | <a href="group_test_documentation.html">Group tests</a> | <a href="mock_objects_documentation.html">Mock objects</a> | <a href="partial_mocks_documentation.html">Partial mocks</a> | <a href="reporter_documentation.html">Reporting</a> | <a href="expectation_documentation.html">Expectations</a> | <a href="web_tester_documentation.html">Web tester</a> | <a href="form_testing_documentation.html">Testing forms</a> | <a href="authentication_documentation.html">Authentication</a> | <a href="browser_documentation.html">Scriptable browser</a> </div> </div> <h1>Prise en main rapide de SimpleTest</h1> This page... <ul> <li> <a href="#unit">Utiliser le testeur rapidement</a> avec un exemple. </li> <li> <a href="#group">Groupes de tests</a> pour tester en un seul clic. </li> <li> <a href="#mock">Utiliser les objets fantaisie</a> pour faciliter les tests et gagner en contrôle. </li> <li> <a href="#web">Tester des pages web</a> au niveau de l'HTML. </li> </ul> <div class="content"> <p> Le présent article présuppose que vous soyez familier avec le concept de tests unitaires ainsi que celui de développement web avec le langage PHP. Il s'agit d'un guide pour le nouvel et impatient utilisateur de <a href="SimpleTest." rel="nofollow">https://sourceforge.net/project/showfiles.php?group_id=76550">SimpleTest</a>. Pour une documentation plus complète, particulièrement si vous découvrez les tests unitaires, consultez la <a href="documentation" rel="nofollow">http://www.lastcraft.com/unit_test_documentation.php">documentation en cours</a>, et pour des exemples de scénarios de test, consultez le <a href="tutorial" rel="nofollow">http://www.lastcraft.com/first_test_tutorial.php">tutorial sur les tests unitaires</a>. </p> <p> <a class="target" name="unit"> <h2>Utiliser le testeur rapidement</h2> </a> </p> <p> Parmi les outils de test pour logiciel, le testeur unitaire est le plus proche du développeur. Dans un contexte de développement agile, le code de test se place juste à côté du code source étant donné que tous les deux sont écrits simultanément. Dans ce contexte, SimpleTest aspire à être une solution complète de test pour un développeur PHP et s'appelle "Simple" parce qu'elle devrait être simple à utiliser et à étendre. Ce nom n'était pas vraiment un bon choix. Non seulement cette solution inclut toutes les fonctions classiques qu'on est en droit d'attendre de la part des portages de <a href="JUnit" rel="nofollow">http://www.junit.org/">JUnit</a> et des <a href="PHPUnit," rel="nofollow">http://sourceforge.net/projects/phpunit/">PHPUnit</a>, mais elle inclut aussi les <a href="objets" rel="nofollow">http://www.mockobjects.com/">objets fantaisie ou "mock objects"</a>. Sans compter quelques fonctionnalités de <a href="JWebUnit" rel="nofollow">http://sourceforge.net/projects/jwebunit/">JWebUnit</a> : parmi celles-ci la navigation sur des pages web, les tests sur les cookies et l'envoi de formulaire. </p> <p> La démonstration la plus rapide : l'exemple </p> <p> Supposons que nous sommes en train de tester une simple classe de log dans un fichier : elle s'appelle <span class="new_code">Log</span> dans <em>classes/Log.php</em>. Commençons par créer un script de test, appelé <em>tests/log_test.php</em>. Son contenu est le suivant... <pre> <strong><?php require_once('simpletest/unit_tester.php'); require_once('simpletest/reporter.php'); require_once('../classes/log.php'); ?></strong> </pre> Ici le répertoire <em>simpletest</em> est soit dans le dossier courant, soit dans les dossiers pour fichiers inclus. Vous auriez à éditer ces arborescences suivant l'endroit où vous avez installé SimpleTest. Ensuite créons un scénario de test... <pre> <?php require_once('simpletest/unit_tester.php'); require_once('simpletest/reporter.php'); require_once('../classes/log.php'); <strong> class TestOfLogging extends UnitTestCase { }</strong> ?> </pre> A présent il y a 5 lignes de code d'échafaudage et toujours pas de test. Cependant à partir de cet instant le retour sur investissement arrive très rapidement. Supposons que la classe <span class="new_code">Log</span> prenne le nom du fichier à écrire dans le constructeur et que nous ayons un répertoire temporaire dans lequel placer ce fichier... <pre> <?php require_once('simpletest/unit_tester.php'); require_once('simpletest/reporter.php'); require_once('../classes/log.php'); class TestOfLogging extends UnitTestCase { <strong> function testCreatingNewFile() { @unlink('/temp/test.log'); $log = new Log('/temp/test.log'); $this->assertFalse(file_exists('/temp/test.log')); $log->message('Should write this to a file'); $this->assertTrue(file_exists('/temp/test.log')); }</strong> } ?> </pre> Au lancement du scénario de test, toutes les méthodes qui commencent avec la chaîne <span class="new_code">test</span> sont identifiées puis exécutées. D'ordinaire nous avons bien plusieurs méthodes de tests. Les assertions dans les méthodes de test envoient des messages vers le framework de test qui affiche immédiatement le résultat. Cette réponse immédiate est importante, non seulement lors d'un crash causé par le code, mais aussi de manière à rapprocher l'affichage de l'erreur au plus près du scénario de test concerné. </p> <p> Pour voir ces résultats lançons effectivement les tests. S'il s'agit de l'unique scénario de test à lancer, on peut y arriver avec... <pre> <?php require_once('simpletest/unit_tester.php'); require_once('simpletest/reporter.php'); require_once('../classes/log.php'); class TestOfLogging extends UnitTestCase { function testCreatingNewFile() { @unlink('/temp/test.log'); $log = new Log('/temp/test.log'); $this->assertFalse(file_exists('/temp/test.log')); $log->message('Should write this to a file'); $this->assertTrue(file_exists('/temp/test.log')); } } <strong> $test = &new TestOfLogging(); $test->run(new HtmlReporter());</strong> ?> </pre> </p> <p> En cas échec, l'affichage ressemble à... <div class="demo"> <h1>testoflogging</h1> <span class="fail">Fail</span>: testcreatingnewfile->True assertion failed.<br> <div style="padding: 8px; margin-top: 1em; background-color: red; color: white;">1/1 test cases complete. <strong>1</strong> passes and <strong>1</strong> fails.</div> </div> ...et si ça passe, on obtient... <div class="demo"> <h1>testoflogging</h1> <div style="padding: 8px; margin-top: 1em; background-color: green; color: white;">1/1 test cases complete. <strong>2</strong> passes and <strong>0</strong> fails.</div> </div> Et si vous obtenez ça... <div class="demo"> <b>Fatal error</b>: Failed opening required '../classes/log.php' (include_path='') in <b>/home/marcus/projects/lastcraft/tutorial_tests/Log/tests/log_test.php</b> on line <b>7</b> </div> c'est qu'il vous manque le fichier <em>classes/Log.php</em> qui pourrait ressembler à : <pre> <?php class Log { function Log($file_path) { } } ?>; </pre> </p> <p> <a class="target" name="group"> <h2>Construire des groupes de tests</h2> </a> </p> <p> Il est peu probable que dans une véritable application on ait uniquement besoin de passer un seul scénario de test. Cela veut dire que nous avons besoin de grouper les scénarios dans un script de test qui peut, si nécessaire, lancer tous les tests de l'application. </p> <p> Notre première étape est de supprimer les includes et de défaire notre hack précédent... <pre> <?php<strong> require_once('../classes/log.php');</strong> class TestOfLogging extends UnitTestCase { function testCreatingNewFile() { @unlink('/temp/test.log'); $log = new Log('/temp/test.log'); $this->assertFalse(file_exists('/temp/test.log')); $log->message('Should write this to a file'); $this->assertTrue(file_exists('/temp/test.log'));<strong> } } ?></strong> </pre> Ensuite nous créons un nouveau fichier appelé <em>tests/all_tests.php</em>. On y insère le code suivant... <pre> <strong><?php require_once('simpletest/unit_tester.php'); require_once('simpletest/reporter.php'); $test = &new GroupTest('All tests'); $test->addTestFile('log_test.php'); $test->run(new HtmlReporter()); ?></strong> </pre> Cette méthode <span class="new_code">GroupTest::addTestFile()</span> va inclure le fichier de scénarios de test et lire parmi toutes les nouvelles classes créées celles qui sont issues de <span class="new_code">TestCase</span>. Dans un premier temps, seuls les noms sont stockés, de la sorte le lanceur de test peut instancier la classe au fur et à mesure qu'il exécute votre suite de tests. </p> <p> Pour que ça puisse marcher proprement le fichier de suite de tests ne devrait pas inclure aveuglement d'autres extensions de scénarios de test qui n'exécuteraient pas effectivement de test. Le résultat pourrait être que des tests supplémentaires soient alors comptabilisés pendant l'exécution des tests. Ce n'est pas un problème grave mais pour éviter ce désagrément, il suffit d'ajouter la commande <span class="new_code">SimpleTestOptions::ignore()</span> quelque part dans le fichier de scénario de test. Par ailleurs le scénario de test ne devrait pas avoir été inclus ailleurs ou alors aucun scénario ne sera ajouté aux groupes de test. Il s'agirait là d'une erreur autrement sérieuse : si toutes les classes de scénario de test sont chargées par PHP, alors la méthode <span class="new_code">GroupTest::addTestFile()</span> ne pourra pas les détecter. </p> <p> Pour afficher les résultats, il est seulement nécessaire d'invoquer <em>tests/all_tests.php</em> à partir du serveur web. </p> <p> <a class="target" name="mock"> <h2>Utiliser les objets fantaisie</h2> </a> </p> <p> Avançons un peu plus dans le futur. </p> <p> Supposons que notre class logging soit testée et terminée. Supposons aussi que nous testons une autre classe qui ait besoin d'écrire des messages de log, disons <span class="new_code">SessionPool</span>. Nous voulons tester une méthode qui ressemblera probablement à quelque chose comme... <pre> <strong> class SessionPool { ... function logIn($username) { ... $this->_log->message('User $username logged in.'); ... } ... } </strong> </pre> Avec le concept de "réutilisation de code" comme fil conducteur, nous utilisons notre class <span class="new_code">Log</span>. Un scénario de test classique ressemblera peut-être à... <pre> <strong> <?php require_once('../classes/log.php'); require_once('../classes/session_pool.php'); class TestOfSessionLogging extends UnitTestCase { function setUp() { @unlink('/temp/test.log'); } function tearDown() { @unlink('/temp/test.log'); } function testLogInIsLogged() { $log = new Log('/temp/test.log'); $session_pool = &new SessionPool($log); $session_pool->logIn('fred'); $messages = file('/temp/test.log'); $this->assertEqual($messages[0], "User fred logged in.\n"); } } ?></strong> </pre> Le design de ce scénario de test n'est pas complètement mauvais, mais on peut l'améliorer. Nous passons du temps à tripoter les fichiers de log qui ne font pas partie de notre test. Pire, nous avons créé des liens de proximité entre la classe <span class="new_code">Log</span> et ce test. Que se passerait-il si nous n'utilisions plus de fichiers, mais la bibliothèque <em>syslog</em> à la place ? Avez-vous remarqué le retour chariot supplémentaire à la fin du message ? A-t-il été ajouté par le loggueur ? Et si il ajoutait aussi un timestamp ou d'autres données ? </p> <p> L'unique partie à tester réellement est l'envoi d'un message précis au loggueur. Nous réduisons le couplage en créant une fausse classe de logging : elle ne fait qu'enregistrer le message pour le test, mais ne produit aucun résultat. Sauf qu'elle doit ressembler exactement à l'original. </p> <p> Si l'objet fantaisie n'écrit pas dans un fichier alors nous nous épargnons la suppression du fichier avant et après le test. Nous pourrions même nous épargner quelques lignes de code supplémentaires si l'objet fantaisie pouvait exécuter l'assertion. <p> </p> Trop beau pour être vrai ? Par chance on peut créer un tel objet très facilement... <pre> <?php require_once('../classes/log.php'); require_once('../classes/session_pool.php');<strong> Mock::generate('Log');</strong> class TestOfSessionLogging extends UnitTestCase { function testLogInIsLogged() {<strong> $log = &new MockLog($this); $log->expectOnce('message', array('User fred logged in.'));</strong> $session_pool = &new SessionPool($log); $session_pool->logIn('fred');<strong> $log->tally();</strong> } } ?> </pre> L'appel <span class="new_code">tally()</span> est nécessaire pour annoncer à l'objet fantaisie qu'il n'y aura plus d'appels ultérieurs. Sans ça l'objet fantaisie pourrait attendre pendant une éternité l'appel de la méthode sans jamais prévenir le scénario de test. Les autres tests sont déclenchés automatiquement quand l'appel à <span class="new_code">message()</span> est invoqué sur l'objet <span class="new_code">MockLog</span>. L'appel <span class="new_code">mock</span> va déclencher une comparaison des paramètres et ensuite envoyer le message "pass" ou "fail" au test pour l'affichage. Des jokers peuvent être inclus ici aussi afin d'empêcher que les tests ne deviennent trop spécifiques. </p> <p> Les objets fantaisie dans la suite SimpleTest peuvent avoir un ensemble de valeurs de sortie arbitraires, des séquences de sorties, des valeurs de sortie sélectionnées à partir des arguments d'entrée, des séquences de paramètres attendus et des limites sur le nombre de fois qu'une méthode peut être invoquée. </p> <p> Pour que ce test fonctionne la librairie avec les objets fantaisie doit être incluse dans la suite de tests, par exemple dans <em>all_tests.php</em>. </p> <p> <a class="target" name="web"> <h2>Tester une page web</h2> </a> </p> <p> Une des exigences des sites web, c'est qu'ils produisent des pages web. Si vous construisez un projet de A à Z et que vous voulez intégrer des tests au fur et à mesure alors vous voulez un outil qui puisse effectuer une navigation automatique et en examiner le résultat. C'est le boulot d'un testeur web. </p> <p> Effectuer un test web via SimpleTest reste assez primitif : il n'y a pas de javascript par exemple. Pour vous donner une idée, voici un exemple assez trivial : aller chercher une page web, à partir de là naviguer vers la page "about" et finalement tester un contenu déterminé par le client. <pre> <?php<strong> require_once('simpletest/web_tester.php');</strong> require_once('simpletest/reporter.php'); <strong> class TestOfAbout extends WebTestCase { function setUp() { $this->get('http://test-server/index.php'); $this->clickLink('About'); } function testSearchEngineOptimisations() { $this->assertTitle('A long title about us for search engines'); $this->assertWantedPattern('/a popular keyphrase/i'); } }</strong> $test = &new TestOfAbout(); $test->run(new HtmlReporter()); ?> </pre> Avec ce code comme test de recette, vous pouvez vous assurer que le contenu corresponde toujours aux spécifications à la fois des développeurs et des autres parties prenantes au projet. </p> <p> <a href=" |
Update of /cvsroot/tutos/tutos/include/simpletest In directory sc8-pr-cvs10.sourceforge.net:/tmp/cvs-serv13989/include/simpletest Added Files: Tag: BRANCH-2-0 url.php exceptions.php VERSION http.php scorer.php HELP_MY_TESTS_DONT_WORK_ANYMORE shell_tester.php selector.php README collector.php simpletest.php user_agent.php remote.php web_tester.php reflection_php5.php LICENSE compatibility.php page.php authentication.php xml.php encoding.php unit_tester.php errors.php browser.php dumper.php expectation.php reflection_php4.php mock_objects.php reporter.php frames.php test_case.php cookies.php parser.php invoker.php socket.php form.php tag.php detached.php Log Message: TRY including TDD --- NEW FILE: parser.php --- <?php /** * base include file for SimpleTest * @package SimpleTest * @subpackage MockObjects * @version $Id: parser.php,v 1.1.2.1 2007/06/05 13:08:41 edeweerdt Exp $ */ /**#@+ * Lexer mode stack constants */ if (! defined('LEXER_ENTER')) { define('LEXER_ENTER', 1); } if (! defined('LEXER_MATCHED')) { define('LEXER_MATCHED', 2); } if (! defined('LEXER_UNMATCHED')) { define('LEXER_UNMATCHED', 3); } if (! defined('LEXER_EXIT')) { define('LEXER_EXIT', 4); } if (! defined('LEXER_SPECIAL')) { define('LEXER_SPECIAL', 5); } /**#@-*/ /** * Compounded regular expression. Any of * the contained patterns could match and * when one does, it's label is returned. * @package SimpleTest * @subpackage WebTester */ class ParallelRegex { var $_patterns; var $_labels; var $_regex; var $_case; /** * Constructor. Starts with no patterns. * @param boolean $case True for case sensitive, false * for insensitive. * @access public */ function ParallelRegex($case) { $this->_case = $case; $this->_patterns = array(); $this->_labels = array(); $this->_regex = null; } /** * Adds a pattern with an optional label. * @param string $pattern Perl style regex, but ( and ) * lose the usual meaning. * @param string $label Label of regex to be returned * on a match. * @access public */ function addPattern($pattern, $label = true) { $count = count($this->_patterns); $this->_patterns[$count] = $pattern; $this->_labels[$count] = $label; $this->_regex = null; } /** * Attempts to match all patterns at once against * a string. * @param string $subject String to match against. * @param string $match First matched portion of * subject. * @return boolean True on success. * @access public */ function match($subject, &$match) { if (count($this->_patterns) == 0) { return false; } if (! preg_match($this->_getCompoundedRegex(), $subject, $matches)) { $match = ''; return false; } $match = $matches[0]; for ($i = 1; $i < count($matches); $i++) { if ($matches[$i]) { return $this->_labels[$i - 1]; } } return true; } /** * Compounds the patterns into a single * regular expression separated with the * "or" operator. Caches the regex. * Will automatically escape (, ) and / tokens. * @param array $patterns List of patterns in order. * @access private */ function _getCompoundedRegex() { if ($this->_regex == null) { for ($i = 0, $count = count($this->_patterns); $i < $count; $i++) { $this->_patterns[$i] = '(' . str_replace( array('/', '(', ')'), array('\/', '\(', '\)'), $this->_patterns[$i]) . ')'; } $this->_regex = "/" . implode("|", $this->_patterns) . "/" . $this->_getPerlMatchingFlags(); } return $this->_regex; } /** * Accessor for perl regex mode flags to use. * @return string Perl regex flags. * @access private */ function _getPerlMatchingFlags() { return ($this->_case ? "msS" : "msSi"); } } /** * States for a stack machine. * @package SimpleTest * @subpackage WebTester */ class SimpleStateStack { var $_stack; /** * Constructor. Starts in named state. * @param string $start Starting state name. * @access public */ function SimpleStateStack($start) { $this->_stack = array($start); } /** * Accessor for current state. * @return string State. * @access public */ function getCurrent() { return $this->_stack[count($this->_stack) - 1]; } /** * Adds a state to the stack and sets it * to be the current state. * @param string $state New state. * @access public */ function enter($state) { array_push($this->_stack, $state); } /** * Leaves the current state and reverts * to the previous one. * @return boolean False if we drop off * the bottom of the list. * @access public */ function leave() { if (count($this->_stack) == 1) { return false; } array_pop($this->_stack); return true; } } /** * Accepts text and breaks it into tokens. * Some optimisation to make the sure the * content is only scanned by the PHP regex * parser once. Lexer modes must not start * with leading underscores. * @package SimpleTest * @subpackage WebTester */ class SimpleLexer { var $_regexes; var $_parser; var $_mode; var $_mode_handlers; var $_case; /** * Sets up the lexer in case insensitive matching * by default. * @param SimpleSaxParser $parser Handling strategy by * reference. * @param string $start Starting handler. * @param boolean $case True for case sensitive. * @access public */ function SimpleLexer(&$parser, $start = "accept", $case = false) { $this->_case = $case; $this->_regexes = array(); $this->_parser = &$parser; $this->_mode = &new SimpleStateStack($start); $this->_mode_handlers = array($start => $start); } /** * Adds a token search pattern for a particular * parsing mode. The pattern does not change the * current mode. * @param string $pattern Perl style regex, but ( and ) * lose the usual meaning. * @param string $mode Should only apply this * pattern when dealing with * this type of input. * @access public */ function addPattern($pattern, $mode = "accept") { if (! isset($this->_regexes[$mode])) { $this->_regexes[$mode] = new ParallelRegex($this->_case); } $this->_regexes[$mode]->addPattern($pattern); if (! isset($this->_mode_handlers[$mode])) { $this->_mode_handlers[$mode] = $mode; } } /** * Adds a pattern that will enter a new parsing * mode. Useful for entering parenthesis, strings, * tags, etc. * @param string $pattern Perl style regex, but ( and ) * lose the usual meaning. * @param string $mode Should only apply this * pattern when dealing with * this type of input. * @param string $new_mode Change parsing to this new * nested mode. * @access public */ function addEntryPattern($pattern, $mode, $new_mode) { if (! isset($this->_regexes[$mode])) { $this->_regexes[$mode] = new ParallelRegex($this->_case); } $this->_regexes[$mode]->addPattern($pattern, $new_mode); if (! isset($this->_mode_handlers[$new_mode])) { $this->_mode_handlers[$new_mode] = $new_mode; } } /** * Adds a pattern that will exit the current mode * and re-enter the previous one. * @param string $pattern Perl style regex, but ( and ) * lose the usual meaning. * @param string $mode Mode to leave. * @access public */ function addExitPattern($pattern, $mode) { if (! isset($this->_regexes[$mode])) { $this->_regexes[$mode] = new ParallelRegex($this->_case); } $this->_regexes[$mode]->addPattern($pattern, "__exit"); if (! isset($this->_mode_handlers[$mode])) { $this->_mode_handlers[$mode] = $mode; } } /** * Adds a pattern that has a special mode. Acts as an entry * and exit pattern in one go, effectively calling a special * parser handler for this token only. * @param string $pattern Perl style regex, but ( and ) * lose the usual meaning. * @param string $mode Should only apply this * pattern when dealing with * this type of input. * @param string $special Use this mode for this one token. * @access public */ function addSpecialPattern($pattern, $mode, $special) { if (! isset($this->_regexes[$mode])) { $this->_regexes[$mode] = new ParallelRegex($this->_case); } $this->_regexes[$mode]->addPattern($pattern, "_$special"); if (! isset($this->_mode_handlers[$special])) { $this->_mode_handlers[$special] = $special; } } /** * Adds a mapping from a mode to another handler. * @param string $mode Mode to be remapped. * @param string $handler New target handler. * @access public */ function mapHandler($mode, $handler) { $this->_mode_handlers[$mode] = $handler; } /** * Splits the page text into tokens. Will fail * if the handlers report an error or if no * content is consumed. If successful then each * unparsed and parsed token invokes a call to the * held listener. * @param string $raw Raw HTML text. * @return boolean True on success, else false. * @access public */ function parse($raw) { if (! isset($this->_parser)) { return false; } $length = strlen($raw); while (is_array($parsed = $this->_reduce($raw))) { list($raw, $unmatched, $matched, $mode) = $parsed; if (! $this->_dispatchTokens($unmatched, $matched, $mode)) { return false; } if ($raw === '') { return true; } if (strlen($raw) == $length) { return false; } $length = strlen($raw); } if (! $parsed) { return false; } return $this->_invokeParser($raw, LEXER_UNMATCHED); } /** * Sends the matched token and any leading unmatched * text to the parser changing the lexer to a new * mode if one is listed. * @param string $unmatched Unmatched leading portion. * @param string $matched Actual token match. * @param string $mode Mode after match. A boolean * false mode causes no change. * @return boolean False if there was any error * from the parser. * @access private */ function _dispatchTokens($unmatched, $matched, $mode = false) { if (! $this->_invokeParser($unmatched, LEXER_UNMATCHED)) { return false; } if (is_bool($mode)) { return $this->_invokeParser($matched, LEXER_MATCHED); } if ($this->_isModeEnd($mode)) { if (! $this->_invokeParser($matched, LEXER_EXIT)) { return false; } return $this->_mode->leave(); } if ($this->_isSpecialMode($mode)) { $this->_mode->enter($this->_decodeSpecial($mode)); if (! $this->_invokeParser($matched, LEXER_SPECIAL)) { return false; } return $this->_mode->leave(); } $this->_mode->enter($mode); return $this->_invokeParser($matched, LEXER_ENTER); } /** * Tests to see if the new mode is actually to leave * the current mode and pop an item from the matching * mode stack. * @param string $mode Mode to test. * @return boolean True if this is the exit mode. * @access private */ function _isModeEnd($mode) { return ($mode === "__exit"); } /** * Test to see if the mode is one where this mode * is entered for this token only and automatically * leaves immediately afterwoods. * @param string $mode Mode to test. * @return boolean True if this is the exit mode. * @access private */ function _isSpecialMode($mode) { return (strncmp($mode, "_", 1) == 0); } /** * Strips the magic underscore marking single token * modes. * @param string $mode Mode to decode. * @return string Underlying mode name. * @access private */ function _decodeSpecial($mode) { return substr($mode, 1); } /** * Calls the parser method named after the current * mode. Empty content will be ignored. The lexer * has a parser handler for each mode in the lexer. * @param string $content Text parsed. * @param boolean $is_match Token is recognised rather * than unparsed data. * @access private */ function _invokeParser($content, $is_match) { if (($content === '') || ($content === false)) { return true; } $handler = $this->_mode_handlers[$this->_mode->getCurrent()]; return $this->_parser->$handler($content, $is_match); } /** * Tries to match a chunk of text and if successful * removes the recognised chunk and any leading * unparsed data. Empty strings will not be matched. * @param string $raw The subject to parse. This is the * content that will be eaten. * @return array/boolean Three item list of unparsed * content followed by the * recognised token and finally the * action the parser is to take. * True if no match, false if there * is a parsing error. * @access private */ function _reduce($raw) { if ($action = $this->_regexes[$this->_mode->getCurrent()]->match($raw, $match)) { $unparsed_character_count = strpos($raw, $match); $unparsed = substr($raw, 0, $unparsed_character_count); $raw = substr($raw, $unparsed_character_count + strlen($match)); return array($raw, $unparsed, $match, $action); } return true; } } /** * Breas HTML into SAX events. * @package SimpleTest * @subpackage WebTester */ class SimpleHtmlLexer extends SimpleLexer { /** * Sets up the lexer with case insensitive matching * and adds the HTML handlers. * @param SimpleSaxParser $parser Handling strategy by * reference. * @access public */ function SimpleHtmlLexer(&$parser) { $this->SimpleLexer($parser, 'text'); $this->mapHandler('text', 'acceptTextToken'); $this->_addSkipping(); foreach ($this->_getParsedTags() as $tag) { $this->_addTag($tag); } $this->_addInTagTokens(); } /** * List of parsed tags. Others are ignored. * @return array List of searched for tags. * @access private */ function _getParsedTags() { return array('a', 'title', 'form', 'input', 'button', 'textarea', 'select', 'option', 'frameset', 'frame', 'label'); } /** * The lexer has to skip certain sections such * as server code, client code and styles. * @access private */ function _addSkipping() { $this->mapHandler('css', 'ignore'); $this->addEntryPattern('<style', 'text', 'css'); $this->addExitPattern('</style>', 'css'); $this->mapHandler('js', 'ignore'); $this->addEntryPattern('<script', 'text', 'js'); $this->addExitPattern('</script>', 'js'); $this->mapHandler('comment', 'ignore'); $this->addEntryPattern('<!--', 'text', 'comment'); $this->addExitPattern('-->', 'comment'); } /** * Pattern matches to start and end a tag. * @param string $tag Name of tag to scan for. * @access private */ function _addTag($tag) { $this->addSpecialPattern("</$tag>", 'text', 'acceptEndToken'); $this->addEntryPattern("<$tag", 'text', 'tag'); } /** * Pattern matches to parse the inside of a tag * including the attributes and their quoting. * @access private */ function _addInTagTokens() { $this->mapHandler('tag', 'acceptStartToken'); $this->addSpecialPattern('\s+', 'tag', 'ignore'); $this->_addAttributeTokens(); $this->addExitPattern('/>', 'tag'); $this->addExitPattern('>', 'tag'); } /** * Matches attributes that are either single quoted, * double quoted or unquoted. * @access private */ function _addAttributeTokens() { $this->mapHandler('dq_attribute', 'acceptAttributeToken'); $this->addEntryPattern('=\s*"', 'tag', 'dq_attribute'); $this->addPattern("\\\\\"", 'dq_attribute'); $this->addExitPattern('"', 'dq_attribute'); $this->mapHandler('sq_attribute', 'acceptAttributeToken'); $this->addEntryPattern("=\s*'", 'tag', 'sq_attribute'); $this->addPattern("\\\\'", 'sq_attribute'); $this->addExitPattern("'", 'sq_attribute'); $this->mapHandler('uq_attribute', 'acceptAttributeToken'); $this->addSpecialPattern('=\s*[^>\s]*', 'tag', 'uq_attribute'); } } /** * Converts HTML tokens into selected SAX events. * @package SimpleTest * @subpackage WebTester */ class SimpleHtmlSaxParser { var $_lexer; var $_listener; var $_tag; var $_attributes; var $_current_attribute; /** * Sets the listener. * @param SimpleSaxListener $listener SAX event handler. * @access public */ function SimpleHtmlSaxParser(&$listener) { $this->_listener = &$listener; $this->_lexer = &$this->createLexer($this); $this->_tag = ''; $this->_attributes = array(); $this->_current_attribute = ''; } /** * Runs the content through the lexer which * should call back to the acceptors. * @param string $raw Page text to parse. * @return boolean False if parse error. * @access public */ function parse($raw) { return $this->_lexer->parse($raw); } /** * Sets up the matching lexer. Starts in 'text' mode. * @param SimpleSaxParser $parser Event generator, usually $self. * @return SimpleLexer Lexer suitable for this parser. * @access public * @static */ function &createLexer(&$parser) { $lexer = &new SimpleHtmlLexer($parser); return $lexer; } /** * Accepts a token from the tag mode. If the * starting element completes then the element * is dispatched and the current attributes * set back to empty. The element or attribute * name is converted to lower case. * @param string $token Incoming characters. * @param integer $event Lexer event type. * @return boolean False if parse error. * @access public */ function acceptStartToken($token, $event) { if ($event == LEXER_ENTER) { $this->_tag = strtolower(substr($token, 1)); return true; } if ($event == LEXER_EXIT) { $success = $this->_listener->startElement( $this->_tag, $this->_attributes); $this->_tag = ''; $this->_attributes = array(); return $success; } if ($token != '=') { $this->_current_attribute = strtolower(SimpleHtmlSaxParser::decodeHtml($token)); $this->_attributes[$this->_current_attribute] = ''; } return true; } /** * Accepts a token from the end tag mode. * The element name is converted to lower case. * @param string $token Incoming characters. * @param integer $event Lexer event type. * @return boolean False if parse error. * @access public */ function acceptEndToken($token, $event) { if (! preg_match('/<\/(.*)>/', $token, $matches)) { return false; } return $this->_listener->endElement(strtolower($matches[1])); } /** * Part of the tag data. * @param string $token Incoming characters. * @param integer $event Lexer event type. * @return boolean False if parse error. * @access public */ function acceptAttributeToken($token, $event) { if ($this->_current_attribute) { if ($event == LEXER_UNMATCHED) { $this->_attributes[$this->_current_attribute] .= SimpleHtmlSaxParser::decodeHtml($token); } if ($event == LEXER_SPECIAL) { $this->_attributes[$this->_current_attribute] .= preg_replace('/^=\s*/' , '', SimpleHtmlSaxParser::decodeHtml($token)); } } return true; } /** * A character entity. * @param string $token Incoming characters. * @param integer $event Lexer event type. * @return boolean False if parse error. * @access public */ function acceptEntityToken($token, $event) { } /** * Character data between tags regarded as * important. * @param string $token Incoming characters. * @param integer $event Lexer event type. * @return boolean False if parse error. * @access public */ function acceptTextToken($token, $event) { return $this->_listener->addContent($token); } /** * Incoming data to be ignored. * @param string $token Incoming characters. * @param integer $event Lexer event type. * @return boolean False if parse error. * @access public */ function ignore($token, $event) { return true; } /** * Decodes any HTML entities. * @param string $html Incoming HTML. * @return string Outgoing plain text. * @access public * @static */ function decodeHtml($html) { static $translations; if (! isset($translations)) { $translations = array_flip(get_html_translation_table(HTML_ENTITIES)); } return strtr($html, $translations); } /** * Turns HTML into text browser visible text. Images * are converted to their alt text and tags are supressed. * Entities are converted to their visible representation. * @param string $html HTML to convert. * @return string Plain text. * @access public * @static */ function normalise($html) { $text = preg_replace('|<!--.*?-->|', '', $html); $text = preg_replace('|<img.*?alt\s*=\s*"(.*?)".*?>|', ' \1 ', $text); $text = preg_replace('|<img.*?alt\s*=\s*\'(.*?)\'.*?>|', ' \1 ', $text); $text = preg_replace('|<img.*?alt\s*=\s*([a-zA-Z_]+).*?>|', ' \1 ', $text); $text = preg_replace('|<.*?>|', '', $text); $text = SimpleHtmlSaxParser::decodeHtml($text); $text = preg_replace('|\s+|', ' ', $text); return trim($text); } } /** * SAX event handler. * @package SimpleTest * @subpackage WebTester * @abstract */ class SimpleSaxListener { /** * Sets the document to write to. * @access public */ function SimpleSaxListener() { } /** * Start of element event. * @param string $name Element name. * @param hash $attributes Name value pairs. * Attributes without content * are marked as true. * @return boolean False on parse error. * @access public */ function startElement($name, $attributes) { } /** * End of element event. * @param string $name Element name. * @return boolean False on parse error. * @access public */ function endElement($name) { } /** * Unparsed, but relevant data. * @param string $text May include unparsed tags. * @return boolean False on parse error. * @access public */ function addContent($text) { } } ?> --- NEW FILE: LICENSE --- GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the library's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. 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 GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. <signature of Ty Coon>, 1 April 1990 Ty Coon, President of Vice That's all there is to it! --- NEW FILE: page.php --- <?php /** * Base include file for SimpleTest * @package SimpleTest * @subpackage WebTester * @version $Id: page.php,v 1.1.2.1 2007/06/05 13:08:38 edeweerdt Exp $ */ /**#@+ * include other SimpleTest class files */ require_once(dirname(__FILE__) . '/http.php'); require_once(dirname(__FILE__) . '/parser.php'); require_once(dirname(__FILE__) . '/tag.php'); require_once(dirname(__FILE__) . '/form.php'); require_once(dirname(__FILE__) . '/selector.php'); /**#@-*/ /** * Creates tags and widgets given HTML tag * attributes. * @package SimpleTest * @subpackage WebTester */ class SimpleTagBuilder { /** * Factory for the tag objects. Creates the * appropriate tag object for the incoming tag name * and attributes. * @param string $name HTML tag name. * @param hash $attributes Element attributes. * @return SimpleTag Tag object. * @access public */ function createTag($name, $attributes) { static $map = array( 'a' => 'SimpleAnchorTag', 'title' => 'SimpleTitleTag', 'button' => 'SimpleButtonTag', 'textarea' => 'SimpleTextAreaTag', 'option' => 'SimpleOptionTag', 'label' => 'SimpleLabelTag', 'form' => 'SimpleFormTag', 'frame' => 'SimpleFrameTag'); $attributes = $this->_keysToLowerCase($attributes); if (array_key_exists($name, $map)) { $tag_class = $map[$name]; return new $tag_class($attributes); } elseif ($name == 'select') { return $this->_createSelectionTag($attributes); } elseif ($name == 'input') { return $this->_createInputTag($attributes); } return new SimpleTag($name, $attributes); } /** * Factory for selection fields. * @param hash $attributes Element attributes. * @return SimpleTag Tag object. * @access protected */ function _createSelectionTag($attributes) { if (isset($attributes['multiple'])) { return new MultipleSelectionTag($attributes); } return new SimpleSelectionTag($attributes); } /** * Factory for input tags. * @param hash $attributes Element attributes. * @return SimpleTag Tag object. * @access protected */ function _createInputTag($attributes) { if (! isset($attributes['type'])) { return new SimpleTextTag($attributes); } $type = strtolower(trim($attributes['type'])); $map = array( 'submit' => 'SimpleSubmitTag', 'image' => 'SimpleImageSubmitTag', 'checkbox' => 'SimpleCheckboxTag', 'radio' => 'SimpleRadioButtonTag', 'text' => 'SimpleTextTag', 'hidden' => 'SimpleTextTag', 'password' => 'SimpleTextTag', 'file' => 'SimpleUploadTag'); if (array_key_exists($type, $map)) { $tag_class = $map[$type]; return new $tag_class($attributes); } return false; } /** * Make the keys lower case for case insensitive look-ups. * @param hash $map Hash to convert. * @return hash Unchanged values, but keys lower case. * @access private */ function _keysToLowerCase($map) { $lower = array(); foreach ($map as $key => $value) { $lower[strtolower($key)] = $value; } return $lower; } } /** * SAX event handler. Maintains a list of * open tags and dispatches them as they close. * @package SimpleTest * @subpackage WebTester */ class SimplePageBuilder extends SimpleSaxListener { var $_tags; var $_page; var $_private_content_tag; /** * Sets the builder up empty. * @access public */ function SimplePageBuilder() { $this->SimpleSaxListener(); } /** * Frees up any references so as to allow the PHP garbage... [truncated message content] |
Update of /cvsroot/tutos/tutos/include/simpletest/docs/en In directory sc8-pr-cvs10.sourceforge.net:/tmp/cvs-serv13989/include/simpletest/docs/en Added Files: Tag: BRANCH-2-0 web_tester_documentation.html reporter_documentation.html expectation_documentation.html browser_documentation.html unit_test_documentation.html group_test_documentation.html mock_objects_documentation.html docs.css overview.html authentication_documentation.html partial_mocks_documentation.html index.html form_testing_documentation.html Log Message: TRY including TDD --- NEW FILE: expectation_documentation.html --- <html> <head> <META http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title> Extending the SimpleTest unit tester with additional expectation classes </title> <link rel="stylesheet" type="text/css" href="docs.css" title="Styles"> </head> <body> <div class="menu_back"> <div class="menu"> <a href="index.html">SimpleTest</a> | <a href="overview.html">Overview</a> | <a href="unit_test_documentation.html">Unit tester</a> | <a href="group_test_documentation.html">Group tests</a> | <a href="mock_objects_documentation.html">Mock objects</a> | <a href="partial_mocks_documentation.html">Partial mocks</a> | <a href="reporter_documentation.html">Reporting</a> | <span class="chosen">Expectations</span> | <a href="web_tester_documentation.html">Web tester</a> | <a href="form_testing_documentation.html">Testing forms</a> | <a href="authentication_documentation.html">Authentication</a> | <a href="browser_documentation.html">Scriptable browser</a> </div> </div> <h1>Expectation documentation</h1> This page... <ul> <li> Using expectations for <a href="#mock">more precise testing with mock objects</a> </li> <li> <a href="#behaviour">Changing mock object behaviour</a> with expectations </li> <li> <a href="#extending">Extending the expectations</a> </li> <li> Underneath SimpleTest <a href="#unit">uses expectation classes</a> </li> </ul> <div class="content"> <p> <a class="target" name="mock"> <h2>More control over mock objects</h2> </a> </p> <p> The default behaviour of the <a href="mock_objects_documentation.html">mock objects</a> in <a href="SimpleTest" rel="nofollow">http://sourceforge.net/projects/simpletest/">SimpleTest</a> is either an identical match on the argument or to allow any argument at all. For almost all tests this is sufficient. Sometimes, though, you want to weaken a test case. </p> <p> One place where a test can be too tightly coupled is with text matching. Suppose we have a component that outputs a helpful error message when something goes wrong. You want to test that the correct error was sent, but the actual text may be rather long. If you test for the text exactly, then every time the exact wording of the message changes, you will have to go back and edit the test suite. </p> <p> For example, suppose we have a news service that has failed to connect to its remote source. <pre> <strong>class NewsService { ... function publish(&$writer) { if (! $this->isConnected()) { $writer->write('Cannot connect to news service "' . $this->_name . '" at this time. ' . 'Please try again later.'); } ... } }</strong> </pre> Here it is sending its content to a <span class="new_code">Writer</span> class. We could test this behaviour with a <span class="new_code">MockWriter</span> like so... <pre> class TestOfNewsService extends UnitTestCase { ... function testConnectionFailure() {<strong> $writer = &new MockWriter(); $writer->expectOnce('write', array( 'Cannot connect to news service ' . '"BBC News" at this time. ' . 'Please try again later.')); $service = &new NewsService('BBC News'); $service->publish($writer); } } </pre> This is a good example of a brittle test. If we decide to add additional instructions, such as suggesting an alternative news source, we will break our tests even though no underlying functionality has been altered. </p> <p> To get around this, we would like to do a regular expression test rather than an exact match. We can actually do this with... <pre> class TestOfNewsService extends UnitTestCase { ... function testConnectionFailure() { $writer = &new MockWriter();<strong> $writer->expectOnce( 'write', array(new PatternExpectation('/cannot connect/i')));</strong> $service = &new NewsService('BBC News'); $service->publish($writer); } } </pre> Instead of passing in the expected parameter to the <span class="new_code">MockWriter</span> we pass an expectation class called <span class="new_code">WantedPatternExpectation</span>. The mock object is smart enough to recognise this as special and to treat it differently. Rather than simply comparing the incoming argument to this object, it uses the expectation object itself to perform the test. </p> <p> The <span class="new_code">WantedPatternExpectation</span> takes the regular expression to match in its constructor. Whenever a comparison is made by the <span class="new_code">MockWriter</span> against this expectation class, it will do a <span class="new_code">preg_match()</span> with this pattern. With our test case above, as long as "cannot connect" appears in the text of the string, the mock will issue a pass to the unit tester. The rest of the text does not matter. </p> <p> The possible expectation classes are... <table> <tbody> <tr> <td><span class="new_code">AnythingExpectation</span></td><td>Will always match</td> </tr> <tr> <td><span class="new_code">EqualExpectation</span></td><td>An equality, rather than the stronger identity comparison</td> </tr> <tr> <td><span class="new_code">NotEqualExpectation</span></td><td>An inequality comparison</td> </tr> <tr> <td><span class="new_code">IndenticalExpectation</span></td><td>The default mock object check which must match exactly</td> </tr> <tr> <td><span class="new_code">NotIndenticalExpectation</span></td><td>Inverts the mock object logic</td> </tr> <tr> <td><span class="new_code">WithinMarginExpectation</span></td><td>Compares a value to within a margin</td> </tr> <tr> <td><span class="new_code">OutsideMarginExpectation</span></td><td>Checks that a value is out side the margin</td> </tr> <tr> <td><span class="new_code">PatternExpectation</span></td><td>Uses a Perl Regex to match a string</td> </tr> <tr> <td><span class="new_code">NoPatternExpectation</span></td><td>Passes only if failing a Perl Regex</td> </tr> <tr> <td><span class="new_code">IsAExpectation</span></td><td>Checks the type or class name only</td> </tr> <tr> <td><span class="new_code">NotAExpectation</span></td><td>Opposite of the <span class="new_code">IsAExpectation</span></td> </tr> <tr> <td><span class="new_code">MethodExistsExpectation</span></td><td>Checks a method is available on an object</td> </tr> </tbody> </table> Most take the expected value in the constructor. The exceptions are the pattern matchers, which take a regular expression, and the <span class="new_code">IsAExpectation</span> and <span class="new_code">NotAExpectation</span> which takes a type or class name as a string. </p> <p> Some examples... </p> <p> <pre> $mock->expectOnce('method', array(new IdenticalExpectation(14))); </pre> This is the same as <span class="new_code">$mock->expectOnce('method', array(14))</span>. <pre> $mock->expectOnce('method', array(new EqualExpectation(14))); </pre> This is different from the previous version in that the string <span class="new_code">"14"</span> as a parameter will also pass. Sometimes the additional type checks of SimpleTest are too restrictive. <pre> $mock->expectOnce('method', array(new AnythingExpectation(14))); </pre> This is the same as <span class="new_code">$mock->expectOnce('method', array('*'))</span>. <pre> $mock->expectOnce('method', array(new IdenticalExpectation('*'))); </pre> This is handy if you want to assert a literal <span class="new_code">"*"</span>. <pre> new NotIdenticalExpectation(14) </pre> This matches on anything other than integer 14. Even the string <span class="new_code">"14"</span> would pass. <pre> new WithinMarginExpectation(14.0, 0.001) </pre> This will accept any value from 13.999 to 14.001 inclusive. </p> <p> <a class="target" name="behaviour"> <h2>Using expectations to control stubs</h2> </a> </p> <p> The expectation classes can be used not just for sending assertions from mock objects, but also for selecting behaviour for the <a href="mock_objects_documentation.html">mock objects</a>. Anywhere a list of arguments is given, a list of expectation objects can be inserted instead. </p> <p> Suppose we want a mock authorisation server to simulate a successful login, but only if it receives a valid session object. We can do this as follows... <pre> Mock::generate('Authorisation'); <strong> $authorisation = new MockAuthorisation(); $authorisation->setReturnValue( 'isAllowed', true, array(new IsAExpectation('Session', 'Must be a session'))); $authorisation->setReturnValue('isAllowed', false);</strong> </pre> We have set the default mock behaviour to return false when <span class="new_code">isAllowed</span> is called. When we call the method with a single parameter that is a <span class="new_code">Session</span> object, it will return true. We have also added a second parameter as a message. This will be displayed as part of the mock object failure message if this expectation is the cause of a failure. </p> <p> This kind of sophistication is rarely useful, but is included for completeness. </p> <p> <a class="target" name="extending"> <h2>Creating your own expectations</h2> </a> </p> <p> The expectation classes have a very simple structure. So simple that it is easy to create your own versions for commonly used test logic. </p> <p> As an example here is the creation of a class to test for valid IP addresses. In order to work correctly with the stubs and mocks the new expectation class should extend <span class="new_code">SimpleExpectation</span>... <pre> <strong>class ValidIp extends SimpleExpectation { function test($ip) { return (ip2long($ip) != -1); } function testMessage($ip) { return "Address [$ip] should be a valid IP address"; } }</strong> </pre> There are only two methods to implement. The <span class="new_code">test()</span> method should evaluate to true if the expectation is to pass, and false otherwise. The <span class="new_code">testMessage()</span> method should simply return some helpful text explaining the test that was carried out. </p> <p> This class can now be used in place of the earlier expectation classes. </p> <p> <a class="target" name="unit"> <h2>Under the bonnet of the unit tester</h2> </a> </p> <p> The <a href="SimpleTest" rel="nofollow">http://sourceforge.net/projects/simpletest/">SimpleTest unit testing framework</a> also uses the expectation classes internally for the <a href="unit_test_documentation.html">UnitTestCase class</a>. We can also take advantage of these mechanisms to reuse our homebrew expectation classes within the test suites directly. </p> <p> The most crude way of doing this is to use the <span class="new_code">SimpleTest::assert()</span> method to test against it directly... <pre> <strong>class TestOfNetworking extends UnitTestCase { ... function testGetValidIp() { $server = &new Server(); $this->assert( new ValidIp(), $server->getIp(), 'Server IP address->%s'); } }</strong> </pre> This is a little untidy compared with our usual <span class="new_code">assert...()</span> syntax. </p> <p> For such a simple case we would normally create a separate assertion method on our test case rather than bother using the expectation class. If we pretend that our expectation is a little more complicated for a moment, so that we want to reuse it, we get... <pre> class TestOfNetworking extends UnitTestCase { ...<strong> function assertValidIp($ip, $message = '%s') { $this->assert(new ValidIp(), $ip, $message); }</strong> function testGetValidIp() { $server = &new Server();<strong> $this->assertValidIp( $server->getIp(), 'Server IP address->%s');</strong> } } </pre> It is unlikely we would ever need this degree of control over the testing machinery. It is rare to need the expectations for more than pattern matching. Also, complex expectation classes could make the tests harder to read and debug. These mechanisms are really of most use to authors of systems that will extend the test framework to create their own tool set. </p> </div> References and related information... <ul> <li> SimpleTest project page on <a href="SourceForge." rel="nofollow">http://sourceforge.net/projects/simpletest/">SourceForge</a>. </li> <li> SimpleTest download page on <a href="LastCraft." rel="nofollow">http://www.lastcraft.com/simple_test.php">LastCraft</a>. </li> <li> The expectations mimic the constraints in <a href="JMock." rel="nofollow">http://www.jmock.org/">JMock</a>. </li> <li> <a href="Full" rel="nofollow">http://simpletest.sourceforge.net/">Full API for SimpleTest</a> from the PHPDoc. </li> </ul> <div class="menu_back"> <div class="menu"> <a href="index.html">SimpleTest</a> | <a href="overview.html">Overview</a> | <a href="unit_test_documentation.html">Unit tester</a> | <a href="group_test_documentation.html">Group tests</a> | <a href="mock_objects_documentation.html">Mock objects</a> | <a href="partial_mocks_documentation.html">Partial mocks</a> | <a href="reporter_documentation.html">Reporting</a> | <span class="chosen">Expectations</span> | <a href="web_tester_documentation.html">Web tester</a> | <a href="form_testing_documentation.html">Testing forms</a> | <a href="authentication_documentation.html">Authentication</a> | <a href="browser_documentation.html">Scriptable browser</a> </div> </div> <div class="copyright"> Copyright<br>Marcus Baker 2006 </div> </body> </html> --- NEW FILE: index.html --- <html> <head> <META http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title> Download the Simple Test testing framework - Unit tests and mock objects for PHP </title> <link rel="stylesheet" type="text/css" href="docs.css" title="Styles"> </head> <body> <div class="menu_back"> <div class="menu"> <span class="chosen">SimpleTest</span> | <a href="overview.html">Overview</a> | <a href="unit_test_documentation.html">Unit tester</a> | <a href="group_test_documentation.html">Group tests</a> | <a href="mock_objects_documentation.html">Mock objects</a> | <a href="partial_mocks_documentation.html">Partial mocks</a> | <a href="reporter_documentation.html">Reporting</a> | <a href="expectation_documentation.html">Expectations</a> | <a href="web_tester_documentation.html">Web tester</a> | <a href="form_testing_documentation.html">Testing forms</a> | <a href="authentication_documentation.html">Authentication</a> | <a href="browser_documentation.html">Scriptable browser</a> </div> </div> <h1>Simple Test for PHP</h1> This page... <ul> <li> <a href="#unit">Using unit tester</a> with an example. </li> <li> <a href="#group">Grouping tests</a> for testing with one click. </li> <li> <a href="#mock">Using mock objects</a> to ease testing and gain tighter control. </li> <li> <a href="#web">Testing web pages</a> at the browser level. </li> </ul> <div class="content"> <p> The following assumes that you are familiar with the concept of unit testing as well as the PHP web development language. It is a guide for the impatient new user of <a href="SimpleTest." rel="nofollow">https://sourceforge.net/project/showfiles.php?group_id=76550">SimpleTest</a>. For fuller documentation, especially if you are new to unit testing see the ongoing <a href="unit_test_documentation.html">documentation</a>, and for example test cases see the <a href="unit" rel="nofollow">http://www.lastcraft.com/first_test_tutorial.php">unit testing tutorial</a>. </p> <p> <a class="target" name="unit"> <h2>Using the tester quickly</h2> </a> </p> <p> Amongst software testing tools, a unit tester is the one closest to the developer. In the context of agile development the test code sits right next to the source code as both are written simultaneously. In this context SimpleTest aims to be a complete PHP developer test solution and is called "Simple" because it should be easy to use and extend. It wasn't a good choice of name really. It includes all of the typical functions you would expect from <a href="JUnit" rel="nofollow">http://www.junit.org/">JUnit</a> and the <a href="PHPUnit" rel="nofollow">http://sourceforge.net/projects/phpunit/">PHPUnit</a> ports, but also adds <a href="http://www.mockobjects.com">mock objects</a>. It has some <a href="JWebUnit" rel="nofollow">http://sourceforge.net/projects/jwebunit/">JWebUnit</a> functionality as well. This includes web page navigation, cookie testing and form submission. </p> <p> The quickest way to demonstrate is with an example. </p> <p> Let us suppose we are testing a simple file logging class called <span class="new_code">Log</span> in <em>classes/log.php</em>. We start by creating a test script which we will call <em>tests/log_test.php</em> and populate it as follows... <pre> <?php<strong> require_once('simpletest/unit_tester.php'); require_once('simpletest/reporter.php'); require_once('../classes/log.php'); class TestOfLogging extends UnitTestCase { }</strong> ?> </pre> Here the <em>simpletest</em> folder is either local or in the path. You would have to edit these locations depending on where you placed the toolset. The <span class="new_code">TestOfLogging</span> is our frst test case and it's currently empty. </p> <p> Now we have five lines of scaffolding code and still no tests. However from this part on we get return on our investment very quickly. We'll assume that the <span class="new_code">Log</span> class takes the file name to write to in the constructor and we have a temporary folder in which to place this file... <pre> <?php require_once('simpletest/unit_tester.php'); require_once('simpletest/reporter.php'); require_once('../classes/log.php'); class TestOfLogging extends UnitTestCase { <strong> function testCreatingNewFile() { @unlink('/temp/test.log'); $log = new Log('/temp/test.log'); $this->assertFalse(file_exists('/temp/test.log')); $log->message('Should write this to a file'); $this->assertTrue(file_exists('/temp/test.log')); }</strong> } ?> </pre> When a test case runs it will search for any method that starts with the string <span class="new_code">test</span> and execute that method. We would normally have more than one test method of course. Assertions within the test methods trigger messages to the test framework which displays the result immediately. This immediate response is important, not just in the event of the code causing a crash, but also so that <span class="new_code">print</span> statements can display their content right next to the test case concerned. </p> <p> To see these results we have to actually run the tests. If this is the only test case we wish to run we can achieve it with... <pre> <?php require_once('simpletest/unit_tester.php'); require_once('simpletest/reporter.php'); require_once('../classes/log.php'); class TestOfLogging extends UnitTestCase { function testCreatingNewFile() { @unlink('/temp/test.log'); $log = new Log('/temp/test.log'); $this->assertFalse(file_exists('/temp/test.log')); $log->message('Should write this to a file'); $this->assertTrue(file_exists('/temp/test.log')); } } <strong> $test = &new TestOfLogging(); $test->run(new HtmlReporter());</strong> ?> </pre> </p> <p> On failure the display looks like this... <div class="demo"> <h1>testoflogging</h1> <span class="fail">Fail</span>: testcreatingnewfile->True assertion failed.<br> <div style="padding: 8px; margin-top: 1em; background-color: red; color: white;">1/1 test cases complete. <strong>1</strong> passes and <strong>1</strong> fails.</div> </div> ...and if it passes like this... <div class="demo"> <h1>testoflogging</h1> <div style="padding: 8px; margin-top: 1em; background-color: green; color: white;">1/1 test cases complete. <strong>2</strong> passes and <strong>0</strong> fails.</div> </div> And if you get this... <div class="demo"> <b>Fatal error</b>: Failed opening required '../classes/log.php' (include_path='') in <b>/home/marcus/projects/lastcraft/tutorial_tests/Log/tests/log_test.php</b> on line <b>7</b> </div> it means you're missing the <em>classes/Log.php</em> file that could look like... <pre> <?php class Log { function Log($file_path) { } function message() { } } ?>; </pre> </p> <p> <a class="target" name="group"> <h2>Building group tests</h2> </a> </p> <p> It is unlikely in a real application that we will only ever run one test case. This means that we need a way of grouping cases into a test script that can, if need be, run every test in the application. </p> <p> Our first step is to strip the includes and to undo our previous hack... <pre> <?php<strong> require_once('../classes/log.php');</strong> class TestOfLogging extends UnitTestCase { function testCreatingNewFile() { @unlink('/temp/test.log'); $log = new Log('/temp/test.log'); $this->assertFalse(file_exists('/temp/test.log')); $log->message('Should write this to a file'); $this->assertTrue(file_exists('/temp/test.log'));<strong> } } ?></strong> </pre> Next we create a new file called <em>tests/all_tests.php</em> and insert the following code... <pre> <strong><?php require_once('simpletest/unit_tester.php'); require_once('simpletest/reporter.php'); $test = &new TestSuite('All tests'); $test->addTestFile('log_test.php'); $test->run(new HtmlReporter()); ?></strong> </pre> The method <span class="new_code">TestSuite::addTestFile()</span> will include the test case file and read any new classes created that are descended from <span class="new_code">SimpleTestCase</span>, of which <span class="new_code">UnitTestCase</span> is one example. Just the class names are stored for now, so that the test runner can instantiate the class when it works its way through your test suite. </p> <p> For this to work properly the test case file should not blindly include any other test case extensions that do not actually run tests. This could result in extra test cases being counted during the test run. Hardly a major problem, but to avoid this inconvenience simply add a <span class="new_code">SimpleTestOptions::ignore()</span> directive somewhere in the test case file. Also the test case file should not have been included elsewhere or no cases will be added to this group test. This would be a more serious error as if the test case classes are already loaded by PHP the <span class="new_code">TestSuite::addTestFile()</span> method will not detect them. </p> <p> To display the results it is necessary only to invoke <em>tests/all_tests.php</em> from the web server. </p> <p> <a class="target" name="mock"> <h2>Using mock objects</h2> </a> </p> <p> Let's move further into the future. </p> <p> Assume that our logging class is tested and completed. Assume also that we are testing another class that is required to write log messages, say a <span class="new_code">SessionPool</span>. We want to test a method that will probably end up looking like this... <pre> <strong> class SessionPool { ... function logIn($username) { ... $this->_log->message("User $username logged in."); ... } ... } </strong> </pre> In the spirit of reuse we are using our <span class="new_code">Log</span> class. A conventional test case might look like this... <pre> <strong> <?php require_once('../classes/log.php'); require_once('../classes/session_pool.php'); class TestOfSessionLogging extends UnitTestCase { function setUp() { @unlink('/temp/test.log'); } function tearDown() { @unlink('/temp/test.log'); } function testLogInIsLogged() { $log = new Log('/temp/test.log'); $session_pool = &new SessionPool($log); $session_pool->logIn('fred'); $messages = file('/temp/test.log'); $this->assertEqual($messages[0], "User fred logged in.\n"); } } ?></strong> </pre> This test case design is not all bad, but it could be improved. We are spending time fiddling with log files which are not part of our test. Worse, we have created close ties with the <span class="new_code">Log</span> class and this test. What if we don't use files any more, but use ths <em>syslog</em> library instead? Did you notice the extra carriage return in the message? Was that added by the logger? What if it also added a time stamp or other data? </p> <p> The only part that we really want to test is that a particular message was sent to the logger. We reduce coupling if we can pass in a fake logging class that simply records the message calls for testing, but takes no action. It would have to look exactly like our original though. </p> <p> If the fake object doesn't write to a file then we save on deleting the file before and after each test. We could save even more test code if the fake object would kindly run the assertion for us. <p> </p> Too good to be true? Luckily we can create such an object easily... <pre> <?php require_once('../classes/log.php'); require_once('../classes/session_pool.php');<strong> Mock::generate('Log');</strong> class TestOfSessionLogging extends UnitTestCase { function testLogInIsLogged() {<strong> $log = &new MockLog(); $log->expectOnce('message', array('User fred logged in.'));</strong> $session_pool = &new SessionPool($log); $session_pool->logIn('fred'); } } ?> </pre> The test will be triggered when the call to <span class="new_code">message()</span> is invoked on the <span class="new_code">MockLog</span> object. The mock call will trigger a parameter comparison and then send the resulting pass or fail event to the test display. Wildcards can be included here too so as to prevent tests becoming too specific. </p> <p> If the mock reaches the end of the test case without the method being called, the <span class="new_code">expectOnce()</span> expectation will trigger a test failure. In other words the mocks can detect the absence of behaviour as well as the presence. </p> <p> The mock objects in the SimpleTest suite can have arbitrary return values set, sequences of returns, return values selected according to the incoming arguments, sequences of parameter expectations and limits on the number of times a method is to be invoked. </p> <p> For this test to run the mock objects library must have been included in the test suite, say in <em>all_tests.php</em>. </p> <p> <a class="target" name="web"> <h2>Web page testing</h2> </a> </p> <p> One of the requirements of web sites is that they produce web pages. If you are building a project top-down and you want to fully integrate testing along the way then you will want a way of automatically navigating a site and examining output for correctness. This is the job of a web tester. </p> <p> The web testing in SimpleTest is fairly primitive, there is no JavaScript for example. To give an idea here is a trivial example where a home page is fetched, from which we navigate to an "about" page and then test some client determined content. <pre> <?php<strong> require_once('simpletest/web_tester.php');</strong> require_once('simpletest/reporter.php'); <strong> class TestOfAbout extends WebTestCase { function setUp() { $this->get('http://test-server/index.php'); $this->click('About'); } function testSearchEngineOptimisations() { $this->assertTitle('A long title about us for search engines'); $this->assertPattern('/a popular keyphrase/i'); } }</strong> $test = &new TestOfAbout(); $test->run(new HtmlReporter()); ?> </pre> With this code as an acceptance test you can ensure that the content always meets the specifications of both the developers and the other project stakeholders. </p> <p> <a href=" |
From: Emmanuel D. <ede...@us...> - 2007-06-05 13:08:08
|
Update of /cvsroot/tutos/tutos/include/simpletest/test/support/collector In directory sc8-pr-cvs10.sourceforge.net:/tmp/cvs-serv13644/include/simpletest/test/support/collector Log Message: Directory /cvsroot/tutos/tutos/include/simpletest/test/support/collector added to the repository |
From: Emmanuel D. <ede...@us...> - 2007-06-05 13:08:08
|
Update of /cvsroot/tutos/tutos/include/simpletest/test In directory sc8-pr-cvs10.sourceforge.net:/tmp/cvs-serv13644/include/simpletest/test Log Message: Directory /cvsroot/tutos/tutos/include/simpletest/test added to the repository |
From: Emmanuel D. <ede...@us...> - 2007-06-05 13:08:08
|
Update of /cvsroot/tutos/tutos/include/simpletest/docs In directory sc8-pr-cvs10.sourceforge.net:/tmp/cvs-serv13644/include/simpletest/docs Log Message: Directory /cvsroot/tutos/tutos/include/simpletest/docs added to the repository |
From: Emmanuel D. <ede...@us...> - 2007-06-05 13:08:08
|
Update of /cvsroot/tutos/tutos/include/simpletest/docs/en In directory sc8-pr-cvs10.sourceforge.net:/tmp/cvs-serv13644/include/simpletest/docs/en Log Message: Directory /cvsroot/tutos/tutos/include/simpletest/docs/en added to the repository |
From: Emmanuel D. <ede...@us...> - 2007-06-05 13:08:08
|
Update of /cvsroot/tutos/tutos/include/simpletest/docs/fr In directory sc8-pr-cvs10.sourceforge.net:/tmp/cvs-serv13644/include/simpletest/docs/fr Log Message: Directory /cvsroot/tutos/tutos/include/simpletest/docs/fr added to the repository |
From: Emmanuel D. <ede...@us...> - 2007-06-05 13:08:07
|
Update of /cvsroot/tutos/tutos/include/simpletest/extensions In directory sc8-pr-cvs10.sourceforge.net:/tmp/cvs-serv13644/include/simpletest/extensions Log Message: Directory /cvsroot/tutos/tutos/include/simpletest/extensions added to the repository |
From: Emmanuel D. <ede...@us...> - 2007-06-05 13:08:07
|
Update of /cvsroot/tutos/tutos/include/simpletest/test/support In directory sc8-pr-cvs10.sourceforge.net:/tmp/cvs-serv13644/include/simpletest/test/support Log Message: Directory /cvsroot/tutos/tutos/include/simpletest/test/support added to the repository |
From: Emmanuel D. <ede...@us...> - 2007-06-05 13:08:07
|
Update of /cvsroot/tutos/tutos/include/simpletest In directory sc8-pr-cvs10.sourceforge.net:/tmp/cvs-serv13644/include/simpletest Log Message: Directory /cvsroot/tutos/tutos/include/simpletest added to the repository --> Using per-directory sticky tag `BRANCH-2-0' |
From: Emmanuel D. <ede...@us...> - 2007-06-05 13:07:00
|
Update of /cvsroot/tutos/tutos/php In directory sc8-pr-cvs10.sourceforge.net:/tmp/cvs-serv13092/php Modified Files: Tag: BRANCH-2-0 timetrack_new.php Log Message: Change the timetrack_new page for speed Index: timetrack_new.php =================================================================== RCS file: /cvsroot/tutos/tutos/php/timetrack_new.php,v retrieving revision 1.50.2.14 retrieving revision 1.50.2.15 diff -u -d -r1.50.2.14 -r1.50.2.15 --- timetrack_new.php 23 May 2007 10:05:20 -0000 1.50.2.14 +++ timetrack_new.php 5 Jun 2007 13:06:56 -0000 1.50.2.15 @@ -6,336 +6,320 @@ * @module timetrack_new * @package timetrack */ - include_once 'webelements.p3'; - include_once 'permission.p3'; - include_once 'timetrack.pinc'; - include_once 'task.pinc'; - - include_once 'layout/layout.pinc'; - - /* Check if user is allowed to use it */ - check_user(); - loadmodules("timetrack","new"); +include_once 'webelements.p3'; +include_once 'permission.p3'; +include_once 'timetrack.pinc'; +include_once 'task.pinc'; - /** - * change/modify a timetrack entry - */ - class timetrack_new extends layout { - /** - * display the form - */ - Function info() { - global $lang, $tutos,$table; +include_once 'layout/layout.pinc'; - if ( $this->mode == 1 ) { - # if we are in time record mode we first give a list of all OPEN records - $q = "SELECT * from ". $this->obj->tablename ." WHERE t_start is not null AND t_end is NULL and adr_id = ". $this->obj->worker->id; - $r = $this->dbconn->Exec($q); - $n = $r->numrows(); - $a = 0; - if ( $n > 0 ) { - echo $this->DataTableStart(); - echo "<tr>\n"; - echo "<th>". $lang['Timetrack'] ."</th>\n"; - echo "<th>". $lang['AppStart'] ."</th>\n"; - echo "<th>". $lang['TimetrackRef'] ."</th>\n"; - echo "<th>". $lang['TimetrackModify'] ."</th>\n"; - echo "<th>". $lang['TTfinish'] ."</th>\n"; - echo "</tr>\n"; - } - $line = 0 ; - while ( $a < $n ) { - $f = new timetrack($this->dbconn); - $f->read_result($r,$a); - $a++; - echo $this->OverviewRowStart($line); - echo "<td>". $f->getLink() ."</td>"; - echo "<td>". $f->t_start->getDateTime() ."</td>"; - echo "<td>". $f->ref->getLink() ."</td>"; - echo "<td>". makelink("timetrack_new.php?mode=2&id=". $f->id,$lang['Modify'],$lang['TimetrackModify']) ."</td>"; - echo "<td>". makelink("timetrack_finish.php?id=". $f->id,$lang['TTfinish'],$lang['TTfinish']) ."</td>"; - echo $this->OverviewRowEnd($line++); - } - $r->free(); - if ( $n > 0 ) { - echo $this->DataTableEnd(); - return; - } - } +/* Check if user is allowed to use it */ +check_user(); +loadmodules("timetrack","new"); - /** - * Here begins the template stuff - * - * Note for the moment just one mode is implemented, other ways - * of displaying a timetrack are to be reviewed before to be - * re-implemented as is in the v2. - */ +/** + * change/modify a timetrack entry + */ +class timetrack_new extends layout { + /** + * display the form + */ + Function info() { + global $lang, $tutos,$table; - $this->assign('action', 'timetrack_ins.php'); - $this->assign('mode', $mode); - $this->assign('id', $this->obj->id); - $this->assign('lid', $this->obj->ref->id); + if ( $this->mode == 1 ) { + # if we are in time record mode we first give a list of all OPEN records + $q = "SELECT * from ". $this->obj->tablename ." WHERE t_start is not null AND t_end is NULL and adr_id = ". $this->obj->worker->id; + $r = $this->dbconn->Exec($q); + $n = $r->numrows(); + $a = 0; + if ( $n > 0 ) { + echo $this->DataTableStart(); + echo "<tr>\n"; + echo "<th>". $lang['Timetrack'] ."</th>\n"; + echo "<th>". $lang['AppStart'] ."</th>\n"; + echo "<th>". $lang['TimetrackRef'] ."</th>\n"; + echo "<th>". $lang['TimetrackModify'] ."</th>\n"; + echo "<th>". $lang['TTfinish'] ."</th>\n"; + echo "</tr>\n"; + } + $line = 0 ; + while ( $a < $n ) { + $f = new timetrack($this->dbconn); + $f->read_result($r,$a); + $a++; + echo $this->OverviewRowStart($line); + echo "<td>". $f->getLink() ."</td>"; + echo "<td>". $f->t_start->getDateTime() ."</td>"; + echo "<td>". $f->ref->getLink() ."</td>"; + echo "<td>". makelink("timetrack_new.php?mode=2&id=". $f->id,$lang['Modify'],$lang['TimetrackModify']) ."</td>"; + echo "<td>". makelink("timetrack_finish.php?id=". $f->id,$lang['TTfinish'],$lang['TTfinish']) ."</td>"; + echo $this->OverviewRowEnd($line++); + } + $r->free(); + if ( $n > 0 ) { + echo $this->DataTableEnd(); + return; + } + } - $this->assign('hours', $lang['hours']); + /** + * Here begins the template stuff + * + * Note for the moment just one mode is implemented, other ways + * of displaying a timetrack are to be reviewed before to be + * re-implemented as is in the v2. + */ - if( ($this->obj->ref != -1) - && ($this->obj->ref->gettype() != "address") ) { + $this->assign('action', 'timetrack_ins.php'); + $this->assign('mode', $mode); + $this->assign('id', $this->obj->id); + $this->assign('lid', $this->obj->ref->id); - $parents = array(); - $x = $this->obj->ref; - while ( isset ( $x ) ) { - // echo $pre . $lang[$x->gettype()] . " " . $x->getLink(); - $parents[] = array('label' => $x->getFullName(), - 'title' => $x->getFullName(), - 'link' => $x->getUrl(), - 'type' => $lang[$x->gettype()]); + $this->assign('hours', $lang['hours']); - if( isset($x->parent) && $x->parent != -1 ) - $x = $x->parent; - else - unset($x); - } - // $this->debug($parents); - $this->assign('parents', - array('label' => $lang['TaskSubTask'], - 'parents' => $parents)); - } + if( ($this->obj->ref != -1) && ($this->obj->ref->gettype() != "address") ) { - $this->assign('ref', - array('id' => $this->obj->ref->id, - 'label' => $lang['TimetrackRef'], - 'value' => $this->obj->ref->getFullName(), - 'title' => $this->obj->ref->getFullName(), - 'link' => $this->obj->ref->getUrl())); + $parents = array(); + $x = $this->obj->ref; + $limit = 2; + while ( isset ( $x ) && $limit) { + // echo $pre . $lang[$x->gettype()] . " " . $x->getLink(); + $parents[] = array('label' => $x->getFullName(), + 'title' => $x->getFullName(), + 'link' => $x->getUrl(), + 'type' => $lang[$x->gettype()]); - $this->assign('worker', - array('id' => 'w', - 'name' => "wfn", - 'label' => $lang['TimetrackWorker'], - 'value' => $this->obj->worker->getFullName(), - 'title' => $this->obj->worker->getFullName(), - 'link' => $this->obj->worker->getUrl())); - + if( isset($x->parent) && $x->parent != -1 ) + $x = $x->parent; + else + unset($x); + $limit--; + } + // $this->debug($parents); + $this->assign('parents', array('label' => $lang['TaskSubTask'], 'parents' => $parents)); + } - $this->assign('volume_done', - array('name' => 'volume', - 'label' => $lang['TaskVolumeDone'], - 'value' => number_format($this->obj->volume, 2, - $lang['DecPoint'], - $lang['ThousandPoint']))); + $this->assign('ref', array('id' => $this->obj->ref->id, + 'label' => $lang['TimetrackRef'], + 'value' => $this->obj->ref->getFullName(), + 'title' => $this->obj->ref->getFullName(), + 'link' => $this->obj->ref->getUrl())); - $this->assign_date($lang['atDateTime'], "wd", $this->obj->wday); + $this->assign('worker', array('id' => 'w', + 'name' => "wfn", + 'label' => $lang['TimetrackWorker'], + 'value' => $this->obj->worker->getFullName(), + 'title' => $this->obj->worker->getFullName(), + 'link' => $this->obj->worker->getUrl())); + - $this->assign('ref_type', $this->obj->ref->getType()); + $this->assign('volume_done', array('name' => 'volume', + 'label' => $lang['TaskVolumeDone'], + 'value' => number_format($this->obj->volume, 2, $lang['DecPoint'], $lang['ThousandPoint']))); - if( $this->obj->ref->getType() == "task" ) { - if( $this->obj->ref->volume_todo == -1 ) - $volume_todo = $this->obj->ref->volume - $this->obj->ref->volume_done; - else - $volume_todo = $this->obj->ref->volume_todo; + $this->assign_date($lang['atDateTime'], "wd", $this->obj->wday); + $this->assign('ref_type', $this->obj->ref->getType()); + if( $this->obj->ref->getType() == "task" ) { + if( $this->obj->ref->volume_todo == -1 ) + $volume_todo = $this->obj->ref->volume - $this->obj->ref->volume_done; + else + $volume_todo = $this->obj->ref->volume_todo; - // give a check box for the user to choose to indicate volume - // done or to ask the system to calculate it - $this->assign('calc_volume_todo', - array('id' => 'calc_volume_todo', - 'label' => $lang['TaskVolumeTodoC'], - 'value' => 0)); - $this->assign('volume_todo', - array('name' => 'volume_todo', - 'label' => $lang['TaskVolumeTodo'], - 'value' => $volume_todo)); - } + // give a check box for the user to choose to indicate volume + // done or to ask the system to calculate it + $this->assign('calc_volume_todo', array('id' => 'calc_volume_todo', + 'label' => $lang['TaskVolumeTodoC'], + 'value' => 0)); + $this->assign('volume_todo', array('name' => 'volume_todo', + 'label' => $lang['TaskVolumeTodo'], + 'value' => $volume_todo)); + } - $this->assign('useinvoice', $tutos[useinvoice]); - if( $tutos[useinvoice] == 1 ) { - $this->assign('currency', - array('id' => 'cph', - 'name' => 'currency', - 'label' => $lang['TTcph'], - 'value' => number_format($this->obj->cph, 2, - $lang['DecPoint'], - $lang['ThousandPoint']), - 'options' => $tutos[currencies], - 'currency' => $this->obj->currency)); - - $this->assign('state', - array('id' => 'id', - 'name' => 'state', - 'label' => $lang['TTState'], - 'value' => $this->obj->state, - 'options' => $lang['TTStates'])); - } + $this->assign('useinvoice', $tutos[useinvoice]); + if( $tutos[useinvoice] == 1 ) { + $this->assign('currency', + array('id' => 'cph', + 'name' => 'currency', + 'label' => $lang['TTcph'], + 'value' => number_format($this->obj->cph, 2,$lang['DecPoint'],$lang['ThousandPoint']), + 'options' => $tutos[currencies], + 'currency' => $this->obj->currency)); + + $this->assign('state', + array('id' => 'id', + 'name' => 'state', + 'label' => $lang['TTState'], + 'value' => $this->obj->state, + 'options' => $lang['TTStates'])); + } - $this->assign('desc', - array('name' => 'desc', - 'label' => $lang['Description'], - 'value' => $this->obj->desc, - 'size' => $table['timetrack']['description'][size])); + $this->assign('desc', + array('name' => 'desc', + 'label' => $lang['Description'], + 'value' => $this->obj->desc, + 'size' => $table['timetrack']['description'][size])); - $this->assign('InputOk', $lang['InputOk']); - $this->assign('ChangesOk', $lang['ChangesOk']); - $this->assign('Enter', $lang['Enter']); - $this->assign('Reset', $lang['Reset']); + $this->assign('InputOk', $lang['InputOk']); + $this->assign('ChangesOk', $lang['ChangesOk']); + $this->assign('Enter', $lang['Enter']); + $this->assign('Reset', $lang['Reset']); - $this->assign('focus', "ttadd.name"); + $this->assign('focus', "ttadd.name"); - $this->assign('FldsRequired', $lang['FldsRequired']); + $this->assign('FldsRequired', $lang['FldsRequired']); - $this->template('timetrack_new'); - } + $this->template('timetrack_new'); + } - /** - * navigate - */ - Function navigate() { - global $lang; + /** + * navigate + */ + Function navigate() { + global $lang; - $this->menu_categories['obj'] = - array('label' => $lang['Timetrack'], - 'icon' => 'timetrack'); - } + $this->menu_categories['obj'] = + array('label' => $lang['Timetrack'], + 'icon' => 'timetrack'); + } - /** - * prepare - */ - Function prepare() { - global $msg,$tutos,$lang; - # - # Mode - # 0 = classical Timetrack - # 1 = timerecord with a list of open entries - # 2 = timerecord editable - # - if ( isset($_GET['mode']) ) { - $this->mode = $_GET['mode']; - } else { - $this->mode = 0; - } - $this->obj = new timetrack($this->dbconn); - if ( isset($_GET['id']) && ($_GET['id'] > 0) ) { - # existing timetrack - $this->obj->read($_GET['id']); - if ( ($this->obj->t_start->notime == 0) && ( $this->mode != 2) ) { - $this->mode = 1; - } - if ( ($this->obj->id < 0) ) { - $msg .= sprintf($lang['Err0040'],$lang[$this->obj->getType()]); - $this->stop = true; - } - } else { - # new stuff - if ( $this->mode == 1 ) { - $this->obj->t_start = new TutosDateTime(); - } - $this->obj->ref = $this->user; - } - $this->obj->read_relations(); + /** + * prepare + */ + Function prepare() { + global $msg,$tutos,$lang; + # + # Mode + # 0 = classical Timetrack + # 1 = timerecord with a list of open entries + # 2 = timerecord editable + # + if ( isset($_GET['mode']) ) { + $this->mode = $_GET['mode']; + } else { + $this->mode = 0; + } + $this->obj = new timetrack($this->dbconn); + if ( isset($_GET['id']) && ($_GET['id'] > 0) ) { + # existing timetrack + $this->obj->read($_GET['id']); + if ( ($this->obj->t_start->notime == 0) && ( $this->mode != 2) ) { + $this->mode = 1; + } + if ( ($this->obj->id < 0) ) { + $msg .= sprintf($lang['Err0040'],$lang[$this->obj->getType()]); + $this->stop = true; + } + } else { + # new stuff + if ( $this->mode == 1 ) { + $this->obj->t_start = new TutosDateTime(); + } + $this->obj->ref = $this->user; + } + $this->obj->read_relations(); - if ( ($this->obj->id < 0) && !$this->user->feature_ok(usetimetrack,PERM_NEW) ) { - $msg .= sprintf($lang['Err0054'],$lang[$this->obj->getType()]); - $this->stop = true; - } - if ( $this->mode != 0 ) { - $this->name = $lang['TTRecord']; - } else if ( $this->obj->id > 0 ) { - $this->name = $lang['TimetrackModify']; - } else { - $this->name = $lang['TimetrackNew']; - } + if ( ($this->obj->id < 0) && !$this->user->feature_ok(usetimetrack,PERM_NEW) ) { + $msg .= sprintf($lang['Err0054'],$lang[$this->obj->getType()]); + $this->stop = true; + } + if ( $this->mode != 0 ) { + $this->name = $lang['TTRecord']; + } else if ( $this->obj->id > 0 ) { + $this->name = $lang['TimetrackModify']; + } else { + $this->name = $lang['TimetrackNew']; + } - if ( isset($_GET['reftype']) ) { - $this->obj->reftype = StripSlashes($_GET['reftype']); - } else { - $this->obj->reftype = ""; - } - if ( isset($_GET['desc']) ) { - $this->obj->desc = StripSlashes($_GET['desc']); - } - if ( isset($_GET['volume']) ) { - $this->obj->volume = $_GET['volume']; - } - if ( isset($_GET['state']) ) { - $this->obj->state = $_GET['state']; - } - if ( isset($_GET['cph']) ) { - $this->obj->cph = $_GET['cph']; - } - if ( isset($_GET['currency']) ) { - $this->obj->currncy = $_GET['currency']; - } - if ( isset($_GET['wd']) ) { - $this->obj->wday->setDateTime($_GET['wd']); - } - if ( isset($_GET['start']) ) { - $this->obj->t_start->setDateTime($_GET['start']); - } - if ( isset($_GET['end']) ) { - $this->obj->t_end->setDateTime($_GET['end']); - } + if ( isset($_GET['reftype']) ) { + $this->obj->reftype = StripSlashes($_GET['reftype']); + } else { + $this->obj->reftype = ""; + } + if ( isset($_GET['desc']) ) { + $this->obj->desc = StripSlashes($_GET['desc']); + } + if ( isset($_GET['volume']) ) { + $this->obj->volume = $_GET['volume']; + } + if ( isset($_GET['state']) ) { + $this->obj->state = $_GET['state']; + } + if ( isset($_GET['cph']) ) { + $this->obj->cph = $_GET['cph']; + } + if ( isset($_GET['currency']) ) { + $this->obj->currncy = $_GET['currency']; + } + if ( isset($_GET['wd']) ) { + $this->obj->wday->setDateTime($_GET['wd']); + } + if ( isset($_GET['start']) ) { + $this->obj->t_start->setDateTime($_GET['start']); + } + if ( isset($_GET['end']) ) { + $this->obj->t_end->setDateTime($_GET['end']); + } - preset_from_array_or_input($this->obj,'worker',"w"); - preset_from_array_or_input($this->obj,'ref',"l"); + preset_from_array_or_input($this->obj,'worker',"w"); + preset_from_array_or_input($this->obj,'ref',"l"); - if ($this->obj->ref == -1) { - $this->obj->link_id = -1; - } else { - $this->obj->link_id = $this->obj->ref->id; - } - if ( $this->obj->link_id == -1 ) { - $msg .= "Unknown reference"; - $this->stop = true; - } + if ($this->obj->ref == -1) { + $this->obj->link_id = -1; + } else { + $this->obj->link_id = $this->obj->ref->id; + } + if ( $this->obj->link_id == -1 ) { + $msg .= "Unknown reference"; + $this->stop = true; + } - # menu - $x = timetrack::getSelectLink($this->user); - $x['category'][] = "obj"; - $x['image'] = "search"; - $this->addmenu($x); + # menu + $x = timetrack::getSelectLink($this->user); + $x['category'][] = "obj"; + $x['image'] = "search"; + $this->addmenu($x); - $x = timetrack::getaddlink($this->user,$this->obj->ref); - $this->addMenu($x); + $x = timetrack::getaddlink($this->user,$this->obj->ref); + $this->addMenu($x); - if ( ($this->obj->id > 0) - && $this->obj->del_ok() && ($this->obj->ref >0 ) ) { - $x = array('url' => $this->obj->getDelURL(), - 'text' => $lang['Delete'], - 'info' => sprintf($lang['TimetrackDelI'], - $this->obj->ref->getFullname()), - 'image' => 'delete', - 'confirm' => true, - 'category' => array("timetrack","del","obj") - ); - $this->addMenu($x); - } + if ( ($this->obj->id > 0) + && $this->obj->del_ok() && ($this->obj->ref >0 ) ) { + $x = array('url' => $this->obj->getDelURL(), + 'text' => $lang['Delete'], + 'info' => sprintf($lang['TimetrackDelI'], + $this->obj->ref->getFullname()), + 'image' => 'delete', + 'confirm' => true, + 'category' => array("timetrack","del","obj") + ); + $this->addMenu($x); + } - if ( ($this->obj->id > 0) && $this->obj->mod_ok() ) { - $x = array( 'url' => "acl_show.php?id=".$this->obj->id, - 'text' => $lang['AclDetail'], - 'image' => 'acls', - 'info' => sprintf($lang['AclDetailI'], - $this->obj->getFullname()), - 'category' => array("timetrack", "acl","obj") - ); - $this->addMenu($x); - } - add_module_newlinks($this,$this->obj); - - // read the rates possible for this new timetrack - if ( class_exists ("rate") ) { - rate::obj_read($this->obj); - } + if ( ($this->obj->id > 0) && $this->obj->mod_ok() ) { + $x = array( 'url' => "acl_show.php?id=".$this->obj->id, + 'text' => $lang['AclDetail'], + 'image' => 'acls', + 'info' => sprintf($lang['AclDetailI'], + $this->obj->getFullname()), + 'category' => array("timetrack", "acl","obj") + ); + $this->addMenu($x); + } + add_module_newlinks($this,$this->obj); + + // read the rates possible for this new timetrack + if ( class_exists ("rate") ) { + rate::obj_read($this->obj); + } - web_StackStartLayout($this,"timetrack_new.php","timetrack_new.php?".$_SERVER['QUERY_STRING']); - } - } + web_StackStartLayout($this,"timetrack_new.php","timetrack_new.php?".$_SERVER['QUERY_STRING']); + } +} - $l = new timetrack_new($current_user); - $l->display(); - $dbconn->Close(); -?> -<!-- - CVS Info: $Id$ - $Author$ ---> \ No newline at end of file +$l = new timetrack_new($current_user); +$l->display(); +$dbconn->Close(); +?> \ No newline at end of file |
From: Emmanuel D. <ede...@us...> - 2007-06-05 13:06:58
|
Update of /cvsroot/tutos/tutos/templates/classic In directory sc8-pr-cvs10.sourceforge.net:/tmp/cvs-serv13092/templates/classic Modified Files: Tag: BRANCH-2-0 timetrack_new.tpl Log Message: Change the timetrack_new page for speed Index: timetrack_new.tpl =================================================================== RCS file: /cvsroot/tutos/tutos/templates/classic/Attic/timetrack_new.tpl,v retrieving revision 1.1.2.7 retrieving revision 1.1.2.8 diff -u -d -r1.1.2.7 -r1.1.2.8 --- timetrack_new.tpl 26 May 2005 15:12:51 -0000 1.1.2.7 +++ timetrack_new.tpl 5 Jun 2007 13:06:57 -0000 1.1.2.8 @@ -19,7 +19,7 @@ {if $parents != ""} {foreach name=parents from=$parents.parents item=p} - {if not $smarty.foreach.parents.first}»{/if} + {if not $smarty.foreach.parents.first} >{/if} {$p.type} |
From: Gero K. <gok...@us...> - 2007-06-01 19:08:36
|
Update of /cvsroot/tutos/tutos/php/layout In directory sc8-pr-cvs10.sourceforge.net:/tmp/cvs-serv21176/layout Modified Files: classic_base.pinc classic_layout.pinc layout_base.pinc new_layout.pinc test_layout.pinc Log Message: nothing important Index: new_layout.pinc =================================================================== RCS file: /cvsroot/tutos/tutos/php/layout/new_layout.pinc,v retrieving revision 1.70 retrieving revision 1.71 diff -u -d -r1.70 -r1.71 --- new_layout.pinc 4 Apr 2007 19:14:35 -0000 1.70 +++ new_layout.pinc 1 Jun 2007 19:08:36 -0000 1.71 @@ -500,6 +500,11 @@ echo "<!-- all rights reserved -->\n"; echo " <head>\n"; echo " <meta http-equiv=\"Content-Type\" content=\"text/html; charset=". $lang['content_encoding'] ."\"".$c; + echo ' <title>TUTOS -- '. myentities($this->name); + if (isset($this->dbconn)) { + echo " -- ". $this->dbconn->db->alias .""; + } + echo "</title>\n"; echo " <meta name=\"GENERATOR\" content=\"TUTOS ". $tutos[version] ."\"".$c; if ( isset($tutos[lang]) && $tutos[lang] != "" ) { echo ' <meta name="language" content="'. $tutos[lang] .'"'.$c; @@ -542,11 +547,6 @@ # TUTOS javascript # echo ' <script type="text/javascript" language="JavaScript" src="'. getBaseUrl().$tutos['base'] .'/html/tutos.js"></script>'."\n"; - echo ' <title>TUTOS -- '. myentities($this->name); - if (isset($this->dbconn)) { - echo " -- ". $this->dbconn->db->alias .""; - } - echo "</title>\n"; echo " </head>\n"; echo " <body>\n"; } Index: classic_layout.pinc =================================================================== RCS file: /cvsroot/tutos/tutos/php/layout/classic_layout.pinc,v retrieving revision 1.50 retrieving revision 1.51 diff -u -d -r1.50 -r1.51 --- classic_layout.pinc 19 Nov 2006 09:26:45 -0000 1.50 +++ classic_layout.pinc 1 Jun 2007 19:08:36 -0000 1.51 @@ -1,6 +1,6 @@ <?php /** - * Copyright 2002 - 2006 by Gero Kohnert + * Copyright 2002 - 2007 by Gero Kohnert * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -23,6 +23,12 @@ /** * initialize */ + function layout (&$user) { + $this->init($user); + } + /** + * initialize + */ function init (&$user) { $this->stylename = "classic_layout"; parent::init($user); @@ -332,4 +338,4 @@ return; } } -?> +?> \ No newline at end of file Index: classic_base.pinc =================================================================== RCS file: /cvsroot/tutos/tutos/php/layout/classic_base.pinc,v retrieving revision 1.53 retrieving revision 1.54 diff -u -d -r1.53 -r1.54 --- classic_base.pinc 2 Jan 2007 19:59:58 -0000 1.53 +++ classic_base.pinc 1 Jun 2007 19:08:36 -0000 1.54 @@ -1,6 +1,6 @@ <?php /** - * Copyright 2002 - 2006 by Gero Kohnert + * Copyright 2002 - 2007 by Gero Kohnert * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Index: layout_base.pinc =================================================================== RCS file: /cvsroot/tutos/tutos/php/layout/layout_base.pinc,v retrieving revision 1.43 retrieving revision 1.44 diff -u -d -r1.43 -r1.44 --- layout_base.pinc 11 Apr 2007 07:47:55 -0000 1.43 +++ layout_base.pinc 1 Jun 2007 19:08:36 -0000 1.44 @@ -1,6 +1,6 @@ <?php /** - * Copyright 2002 - 2006 by Gero Kohnert + * Copyright 2002 - 2007 by Gero Kohnert * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -495,4 +495,4 @@ return $r; } } -?> +?> \ No newline at end of file Index: test_layout.pinc =================================================================== RCS file: /cvsroot/tutos/tutos/php/layout/test_layout.pinc,v retrieving revision 1.43 retrieving revision 1.44 diff -u -d -r1.43 -r1.44 --- test_layout.pinc 3 Apr 2005 18:32:15 -0000 1.43 +++ test_layout.pinc 1 Jun 2007 19:08:36 -0000 1.44 @@ -1,6 +1,6 @@ <?php /** - * Copyright 2002 - 2004 by Gero Kohnert + * Copyright 2002 - 2007 by Gero Kohnert * * CVS Info: $Id$ * $Author$ @@ -19,6 +19,12 @@ /** * initialize */ + function layout (&$user) { + $this->init($user); + } + /** + * initialize + */ function init (&$user) { $this->stylename = "test_layout"; parent::init($user); |
From: Gero K. <gok...@us...> - 2007-06-01 19:06:53
|
Update of /cvsroot/tutos/tutos/php In directory sc8-pr-cvs10.sourceforge.net:/tmp/cvs-serv20346 Modified Files: history_show.php product_overview.php user.pinc Log Message: fix Index: product_overview.php =================================================================== RCS file: /cvsroot/tutos/tutos/php/product_overview.php,v retrieving revision 1.117 retrieving revision 1.118 diff -u -d -r1.117 -r1.118 --- product_overview.php 11 Apr 2007 07:47:55 -0000 1.117 +++ product_overview.php 1 Jun 2007 19:06:50 -0000 1.118 @@ -882,7 +882,6 @@ } else { $from->setDateTimeF("f"); # remember this -# session_register('prodsearchfrom'); $_SESSION['prodsearchfrom'] = $from->getYYYYMMDD(); } if ( $from->notime != 1 ) { @@ -899,7 +898,6 @@ } else { $to->setDateTimeF("t"); # remember this -# session_register('prodsearchto'); $_SESSION['prodsearchto'] = $to->getYYYYMMDD(); } if ( $to->notime != 1 ) { Index: user.pinc =================================================================== RCS file: /cvsroot/tutos/tutos/php/user.pinc,v retrieving revision 1.154 retrieving revision 1.155 diff -u -d -r1.154 -r1.155 --- user.pinc 5 Apr 2007 16:14:56 -0000 1.154 +++ user.pinc 1 Jun 2007 19:06:50 -0000 1.155 @@ -164,6 +164,9 @@ $this->tz = StripSlashes($r->get($pos, "tz")); $this->theme = StripSlashes($r->get($pos, "theme")); $this->ly = StripSlashes($r->get($pos, "layout")); + if ($this->ly == "") { + $this->ly = $tutos[layouts][0]; + } $holidays = StripSlashes($r->get($pos, "holidays")); foreach($tutos[holiday] as $i => $f) { $this->holiday[strtolower($i)] = 0; Index: history_show.php =================================================================== RCS file: /cvsroot/tutos/tutos/php/history_show.php,v retrieving revision 1.74 retrieving revision 1.75 diff -u -d -r1.74 -r1.75 --- history_show.php 5 Apr 2007 16:14:55 -0000 1.74 +++ history_show.php 1 Jun 2007 19:06:50 -0000 1.75 @@ -103,7 +103,7 @@ if ( $this->id == -1 ) { $x = getObject($this->dbconn,$f["obj_id"],1); - if ( $x == -1 ) { + if (!is_object($x) && ($x == -1) ) { echo " <td colspan=\"2\"><b> ". $f["obj_id"] ."</b></td>\n"; } else { echo " <td> ". $lang[$x->getType()] ."</td><td> <b>". $x->getLink() ."</b></td>\n"; |
From: Gero K. <gok...@us...> - 2007-06-01 16:49:22
|
Update of /cvsroot/tutos/tutos/php/watchlist In directory sc8-pr-cvs10.sourceforge.net:/tmp/cvs-serv28529/watchlist Added Files: .cvsignore Log Message: added custom ignore --- NEW FILE: .cvsignore --- # # $Id: .cvsignore,v 1.1 2007/06/01 16:49:23 gokohnert Exp $ # *~ *_custom.p3 |