PageRenderTime 36ms CodeModel.GetById 13ms app.highlight 18ms RepoModel.GetById 1ms app.codeStats 0ms

/tools/tester.php

https://bitbucket.org/hanigamal/my-wave-framework
PHP | 475 lines | 294 code | 91 blank | 90 comment | 91 complexity | a249abf99f0e9bb5571aadfa52b1af40 MD5 | raw file
  1<?php
  2
  3/**
  4 * Wave Framework <http://www.waveframework.com>
  5 * Test Suite
  6 *
  7 * Wave Framework includes a full test suite for writing tests for API's and running said 
  8 * tests. Tests themselves are defined in '/tools/tests/' subfolder and they can be run 
  9 * based on settings from the Test Suite tool. This Test Suite tool then returns the results 
 10 * from said tests and whether the tests '.$command.' or not.
 11 *
 12 * @package    Tools
 13 * @author     Kristo Vaher <kristo@waher.net>
 14 * @copyright  Copyright (c) 2012, Kristo Vaher
 15 * @license    GNU Lesser General Public License Version 3
 16 * @tutorial   /doc/pages/guide_tools.htm
 17 * @since      1.0.0
 18 * @version    3.2.6
 19 */
 20
 21// This initializes tools and authentication
 22require('.'.DIRECTORY_SEPARATOR.'tools_autoload.php');
 23
 24// Log is printed out in plain text format
 25header('Content-Type: text/html;charset=utf-8');
 26
 27// Information about tests
 28$tests=array();
 29
 30// If any tests are assigned to be run
 31if(isset($_POST['tests']) && !empty($_POST['tests']) && isset($_POST['count']) && $_POST['count']>0){
 32
 33	// This holds the last detected error
 34	$lastError='';
 35	
 36	// This is an error handler itself
 37	function testErrorHandler($type=false,$message=false,$file=false,$line=false){
 38		global $lastError;
 39		$lastError.='TYPE: '.$type.'<br/>';
 40		$lastError.='MESSAGE: '.htmlspecialchars($message).'<br/>';
 41		$lastError.='FILE: '.$file.'<br/>';
 42		$lastError.='LINE: '.$line.'<br/>';
 43	}
 44
 45	// Setting the error handler, this helps suppress the actual error output
 46	set_error_handler('testErrorHandler',E_ALL);
 47
 48	// These arrays store the test results
 49	$testErrorLog=array();
 50	$testsRun=array();
 51	$testsPassed=array();
 52	$testsFailed=array();
 53	
 54	// If default execution time is changed
 55	if(isset($_POST['time']) && ini_get('max_execution_time')!=$_POST['time']){
 56		set_time_limit($_POST['time']);
 57	}
 58	
 59	// Loading State
 60	require(__ROOT__.'engine'.DIRECTORY_SEPARATOR.'class.www-state.php');
 61	$state=new WWW_State($config);
 62	
 63	// This holds link to database
 64	$databaseConnection=false;
 65	
 66	// Connecting to database, if configuration is set
 67	if(isset($config['test-database-name'],$config['test-database-type'],$config['test-database-host'],$config['test-database-username'],$config['test-database-password'])){
 68		// Including the required class and creating the object
 69		require(__ROOT__.'engine'.DIRECTORY_SEPARATOR.'class.www-database.php');
 70		$databaseConnection=new WWW_Database($config['test-database-type'],$config['test-database-host'],$config['test-database-name'],$config['test-database-username'],$config['test-database-password'],false,false);
 71		// Passing the database to State object
 72		$state->databaseConnection=$databaseConnection;
 73	}
 74	
 75	// Loading sessions class
 76	require(__ROOT__.'engine'.DIRECTORY_SEPARATOR.'class.www-sessions.php');
 77	// Loading sessions class with the session namespace
 78	$state->sessionHandler=new WWW_Sessions($state->data['session-name'],$state->data['session-lifetime'],$databaseConnection,true);
 79	
 80	// This functions file is not required, but can be used for system wide functions
 81	// If you want to include additional libraries, do so here
 82	if(file_exists(__ROOT__.'overrides'.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'autoload.php')){
 83		require(__ROOT__.'overrides'.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'autoload.php');
 84	} else {
 85		require(__ROOT__.'resources'.DIRECTORY_SEPARATOR.'autoload.php');
 86	}
 87	
 88	// API is used to process all requests and it handles caching and API validations
 89	require(__ROOT__.'engine'.DIRECTORY_SEPARATOR.'class.www-api.php');
 90	$api=new WWW_API($state);
 91	
 92	// Using error reporting in tests to catch potential errors
 93	error_reporting(E_ALL);
 94	
 95	// Looping over each test
 96	foreach($_POST['tests'] as $test){
 97	
 98		// Finding the test name based on filename
 99		$filename=explode('.',$test);
100		$tmp=array_pop($filename);
101		$testName=implode('.',$filename);
102		
103		// Loading test configuration
104		$configuration=parse_ini_file('tests'.DIRECTORY_SEPARATOR.$test,true,INI_SCANNER_RAW);
105		if($configuration){
106		
107			// Resetting the variables about the current test
108			$command=false;
109			$input=false;
110			$output=false;
111			
112			// Looping over configuration and tests
113			foreach($configuration as $group=>$settings){
114			
115				// Checking for INI groups for master/input/output related settings
116				if($group!='input' && $group!='output'){
117					// The same command can be tested multiple times in a single test script
118					$trueCommand=$group;
119					$command=explode('#',$trueCommand);
120					$command=$command[0];
121					// Resetting input values
122					$input=false;
123					$output=false;
124					// Assigning the group settings as input
125					$masterInput=$settings;
126				} elseif($group=='input'){
127					$input=$settings;
128				} elseif($group=='output'){
129					$output=$settings;
130				}
131				
132				// Running the test if all the settings are ok
133				if($command && $output){
134				
135					// Tests are run multiple times
136					for($x=1;$x<=$_POST['count'];$x++){
137					
138						// Defining test address which can be used later on to link between arrays
139						$testAddress=$testName.' '.$trueCommand.' #'.$x;
140					
141						// This is the input array sent to API
142						$thisInput=array();
143						
144						// Input is not required, but if it exists then generating input variables
145						if($input && !empty($input)){
146						
147							// Input variables can be both dynamic and fixed
148							foreach($input as $key=>$value){
149								if(preg_match('/^:numeric:/',$value)){
150								
151									// Numeric input is either a random integer or an integer within range of numbers
152									$bits=explode(':',$value,3);
153									if(isset($bits[2]) && $bits[2]!=''){
154										$bits=explode('-',$bits[2]);
155										$thisInput[$key]=rand($bits[0],$bits[1]);
156									} else {
157										$thisInput[$key]=rand(0,2147483647);
158									}
159									
160								} elseif(preg_match('/^:alpha:/',$value)){
161								
162									// Alpha input is random characters and spaces
163									$characters="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ";
164									$charactersLength=strlen($characters)-1;
165									$bits=explode(':',$value,3);
166									if(isset($bits[2]) && $bits[2]!=''){
167										$bits=explode('-',$bits[2]);
168										$length=rand($bits[0],$bits[1]);
169									} else {
170										$length=rand(1,32);
171									}
172									$string='';
173									for($i=1;$i<=$length;$i++){
174										$string.=$characters[rand(0,$charactersLength)];
175									}
176									// Assigning as input value
177									$thisInput[$key]=$string;
178									
179								} elseif(preg_match('/^:alphanumeric:/',$value)){
180								
181									// Alpha input is random characters and spaces
182									$characters="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ";
183									$charactersLength=strlen($characters)-1;
184									$bits=explode(':',$value,3);
185									if(isset($bits[2]) && $bits[2]!=''){
186										$bits=explode('-',$bits[2]);
187										$length=rand($bits[0],$bits[1]);
188									} else {
189										$length=rand(1,32);
190									}
191									$string='';
192									for($i=1;$i<=$length;$i++){
193										$string.=$characters[rand(0,$charactersLength)];
194									}
195									// Assigning as input value
196									$thisInput[$key]=$string;
197									
198								} elseif(preg_match('/^:fixed:/',$value)){
199								
200									// Fixed input is a random value from a fixed list
201									$bits=explode(':',$value,3);
202									if(isset($bits[2]) && $bits[2]!=''){
203										$bits=explode(',',$bits[2]);
204										$length=count($bits)-1;
205										$thisInput[$key]=$bits[rand(0,$length)];
206									}		
207									
208								} else {
209								
210									// If value was not dynamic, then it is sent as fixed
211									$thisInput[$key]=$value;
212									
213								}
214							}
215							
216						}
217						
218						// Final input
219						$thisInput=$thisInput+$masterInput;
220						
221						// Logging the tests run
222						$testsRun[$testAddress]=$thisInput;
223						
224						// Some of the values are enforced
225						$thisInput['www-command']=$command;
226						$thisInput['www-return-type']='php';
227						$thisInput['www-headers']=0;
228						$thisInput['www-disable-callbacks']=1;
229						
230						// Making the API request
231						$result=$apiResult=$api->command($thisInput,false,false,false);
232						
233						// It is considered a failed test if the output buffer contains contents
234						if(trim($lastError)!=''){
235						
236							// Error failed since output buffer included contents
237							$testErrorLog[$testAddress]=$testName.' test '.$trueCommand.' iteration #'.$x.' - encountered an error:<br/>'.$lastError;
238							// Resetting the last error container
239							$lastError='';
240						
241						} else {
242						
243							// Looping over the expected output variables and seeing if they were actually part of API output
244							foreach($output as $key=>$value){
245							
246								// Testing if the otput key exists
247								if(isset($result[$key])){
248								
249									if(preg_match('/^:numeric:/',$value)){
250									
251										// Testing if the return value is numeric and within accepted range
252										$bits=explode(':',$value,3);
253										if(isset($bits[2]) && $bits[2]!=''){
254											$variables=explode('-',$bits[2]);
255											if(!preg_match('/^[0-9]*\Z/i',$result[$key]) || (is_numeric($variables[0]) && $result[$key]<$variables[0]) || (is_numeric($variables[1]) && $result[$key]>$variables[1])){
256												$testErrorLog[$testAddress]=$testName.' test '.$trueCommand.' iteration #'.$x.' - output key "'.$key.'" with the value "'.$result[$key].'" is not numeric or is not within the expected range "'.$bits[2].'"';
257											}
258										} else {
259											if(!preg_match('/^[0-9]*\Z/i',$result[$key])){
260												$testErrorLog[$testAddress]=$testName.' test '.$trueCommand.' iteration #'.$x.' - output key "'.$key.'" with the value "'.$result[$key].'" is not numeric';
261											}
262										}
263										
264									} elseif(preg_match('/^:alpha:/',$value)){
265									
266										// Testing if the response consists of letters
267										$bits=explode(':',$value,3);
268										if(isset($bits[2]) && $bits[2]!=''){
269											$variables=explode('-',$bits[2]);
270											if(!preg_match('/^[[:alpha:] ]*\Z/ui',$result[$key]) || (is_numeric($variables[0]) && strlen($result[$key])<$variables[0]) || (is_numeric($variables[1]) && strlen($result[$key])>$variables[1])){
271												$testErrorLog[$testAddress]=$testName.' test '.$trueCommand.' iteration #'.$x.' - output key "'.$key.'" with the value "'.$result[$key].'" does not just have letters or is not within the expected length "'.$bits[2].'"';
272											}
273										} else {
274											if(!preg_match('/^[[:alpha:] ]*\Z/ui',$result[$key])){
275												$testErrorLog[$testAddress]=$testName.' test '.$trueCommand.' iteration #'.$x.' - output key "'.$key.'" with the value "'.$result[$key].'" does not just have letters';
276											}
277										}
278										
279									} elseif(preg_match('/^:alphanumeric:/',$value)){
280									
281										// Testing if the response consists of letters and numbers
282										$bits=explode(':',$value,3);
283										if(isset($bits[2]) && $bits[2]!=''){
284											$variables=explode('-',$bits[2]);
285											if(!preg_match('/^[[:alnum:] ]*\Z/ui',$result[$key]) || (is_numeric($variables[0]) && strlen($result[$key])<$variables[0]) || (is_numeric($variables[1]) && strlen($result[$key])>$variables[1])){
286												$testErrorLog[$testAddress]=$testName.' test '.$trueCommand.' iteration #'.$x.' - output key "'.$key.'" with the value "'.$result[$key].'" is not alphanumeric or within the expected length "'.$bits[2].'"';
287											}
288										} else {
289											if(!preg_match('/^[[:alnum:] ]*\Z/ui',$result[$key])){
290												$testErrorLog[$testAddress]=$testName.' test '.$trueCommand.' iteration #'.$x.' - output key "'.$key.'" with the value "'.$result[$key].'" is not alphanumeric';
291											}
292										}
293										
294									} elseif(preg_match('/^:fixed:/',$value)){
295									
296										// Testing if the response is an accepted fixed value
297										$bits=explode(':',$value,3);
298										if(isset($bits[2]) && $bits[2]!=''){
299											$variables=explode(',',$bits[2]);
300											if(!in_array($result[$key],$variables)){
301												$testErrorLog[$testAddress]=$testName.' test '.$trueCommand.' iteration #'.$x.' - output key "'.$key.'" with the value "'.$result[$key].'" did not match the expected value "'.$bits[2].'"';
302											}
303										} else {
304											$testErrorLog[$testAddress]=$testName.' test '.$trueCommand.' iteration #'.$x.' - output key "'.$key.'" with the value "'.$result[$key].'" could not be matched against empty fixed response';
305										}
306										
307									} elseif(preg_match('/^:any:/',$value)){
308									
309										// Testing if the response is an accepted fixed value
310										$bits=explode(':',$value,3);
311										if(isset($bits[2]) && $bits[2]!=''){
312											if(!preg_match('/^['.$bits[2].']*$/u',$result[$key])){
313												$testErrorLog[$testAddress]=$testName.' test '.$trueCommand.' iteration #'.$x.' - output key "'.$key.'" with the value "'.$result[$key].'" included illegal characters "'.$bits[2].'"';
314											}
315										}
316										
317									} else {
318									
319										// Testing if the value equals the expected value or not
320										if($value && $result[$key]!=$value){
321											$testErrorLog[$testAddress]=$testName.' test '.$trueCommand.' iteration #'.$x.' - output key "'.$key.'" with the value "'.$result[$key].'" did not match the expected value "'.$value.'"';
322										}
323										
324									}
325									
326								} else {
327								
328									// Expected value did not exists, so the test is considered as failed
329									$testErrorLog[$testAddress]=$testName.' test '.$trueCommand.' iteration #'.$x.' - output key "'.$key.'" was not returned';
330									
331								}				
332								
333							}
334							
335						}
336						
337						// Storing the result in counter
338						if(isset($testErrorLog[$testAddress])){
339							$testsFailed[$testAddress]=true;
340						} else {
341							$testsPassed[$testAddress]=true;
342						}
343					
344					}
345					
346				}
347				
348			}
349			
350		} else {
351		
352			// Test configuration failure is considered a '.$command.' test, but is not actual part of 'statistics'
353			$testErrorLog[$testName.'#all']=$testName.'#all - '.$command.' - test configuration incorrect';
354			
355		}
356		
357		// Reloading state
358		$state=new WWW_State($config);
359		$state->databaseConnection=$databaseConnection;
360		$state->sessionHandler=new WWW_Sessions($state->data['session-name'],$state->data['session-lifetime'],$databaseConnection,true);
361		
362		// Reloading API
363		$api=new WWW_API($state);
364		
365	}
366	
367}
368
369// Finding all INI configuration files for tests
370$testFiles=scandir('tests'.DIRECTORY_SEPARATOR);
371
372// Looping over each test file to check whether configuration is correct
373foreach($testFiles as $test){
374	// $state->setState('testing',true);
375	// Ignoring the placeholder file
376	if($test!='.empty' && is_file('tests'.DIRECTORY_SEPARATOR.$test)){
377		// Test configuration should be stored as a grouped INI configuration file
378		$configuration=parse_ini_file('tests'.DIRECTORY_SEPARATOR.$test,true,INI_SCANNER_RAW);
379		if($configuration){
380			$tests[$test]=$configuration;
381		}
382	}
383}
384	
385?>
386<!DOCTYPE html>
387<html lang="en">
388	<head>
389		<title>Test Suite</title>
390		<meta charset="utf-8">
391		<meta name="viewport" content="width=device-width"/> 
392		<link type="text/css" href="style.css" rel="stylesheet" media="all"/>
393		<link rel="icon" href="../favicon.ico" type="image/x-icon"/>
394		<link rel="icon" href="../favicon.ico" type="image/vnd.microsoft.icon"/>
395		<meta content="noindex,nocache,nofollow,noarchive,noimageindex,nosnippet" name="robots"/>
396		<meta http-equiv="cache-control" content="no-cache"/>
397		<meta http-equiv="pragma" content="no-cache"/>
398		<meta http-equiv="expires" content="0"/>
399	</head>
400	<body>
401		<?php
402		
403		// Header
404		echo '<h1>Test Suite</h1>';
405		echo '<h4 class="highlight">';
406		foreach($softwareVersions as $software=>$version){
407			// Adding version numbers
408			echo '<b>'.$software.'</b> ('.$version.') ';
409		}
410		echo '</h4>';
411		
412		// If error log is defined (if testing happened)
413		if(isset($testErrorLog)){
414		
415			// Printing out test results
416			echo '<h2>Test Results</h2>';
417			echo '<p class="bold">'.count($testsRun).' tests run, '.count($testsFailed).' failed</p>';
418			if(!empty($testErrorLog)){
419				echo '<h3>Logged errors</h3>';
420				foreach($testErrorLog as $address=>$log){
421					echo '<p class="bold red small" style="text-align:left;">';
422					echo $log;
423					echo '</p>';
424					if(isset($testsRun[$address]) && !empty($testsRun[$address])){
425						echo '<p class="bold small">Input data</p>';
426						echo '<pre style="padding-left:40px;">';
427							print_r($testsRun[$address]);
428						echo '</pre>';
429					}
430				}
431			}
432			
433		}
434		
435		echo '<h2>Available Tests</h2>';
436		
437		// Generating the form for all available tests
438		if(!empty($tests)){
439		
440			?>
441				<form method="post" action="" enctype="multipart/form-data">
442					<?php
443					foreach($tests as $file=>$test){
444						$filename=explode('.',$file);
445						$tmp=array_pop($filename);
446						$testName=implode('.',$filename);
447						?>
448						<p class="bold"><input type="checkbox" name="tests[]" value="<?=$file?>" <?=(isset($_POST['tests']) && in_array($file,$_POST['tests']))?'checked':''?>/> <?=$testName?></p>
449						<?php
450					}
451					?>
452					<p class="bold">
453						Set execution time to <input type="text" style="width:40px;" name="time" value="<?=(isset($_POST['time']))?$_POST['time']:ini_get('max_execution_time')?>"/> seconds and run each test 
454						<select name="count">
455							<option value="1">1</option>
456							<?php for($i=10;$i<=1000;$i=$i+10){?>
457								<option value="<?=$i?>" <?=(isset($_POST['count']) && $_POST['count']==$i)?'selected':''?>><?=$i?></option>
458							<?php } ?>
459						</select> times 
460						<input type="submit" value="GO!"/>
461					</p>
462					<p class="small">Please note that on some systems it is not possible to change the execution time by PHP itself, in which case you should ask for assistance from server administrators, if the default execution time is not enough and the script dies before it has finished.</p>
463				</form>
464			<?php
465		
466		} else {
467			echo '<p class="bold">There are no tests that can be run from /tools/tests/ subfolder</p>';
468		}
469		
470		// Footer
471		echo '<p class="footer small bold">Generated at '.date('d.m.Y h:i').' GMT '.date('P').' for '.$_SERVER['HTTP_HOST'].'</p>';
472	
473		?>
474	</body>
475</html>