Login   Register  
PHP Classes
elePHPant
Icontem

File: tools/tester.php

Recommend this page to a friend!
Stumble It! Stumble It! Bookmark in del.icio.us Bookmark in del.icio.us
  Classes of Kristo Vaher  >  Wave Framework  >  tools/tester.php  >  Download  
File: tools/tester.php
Role: Auxiliary script
Content type: text/plain
Description: Test Suite
Class: Wave Framework
MVC framework for building Web sites and APIs
Author: By
Last change: Made session starting configurations possible to be changed at runtime.
Updated documentation with indexes and anchors.
Added jQuery 2.0 as an alternative - to use the new version of jQuery that ignores Internet Explorer up to and including 8, then change jquery.js file to jquery2.js in the View Controller.
Developer tools Debugger and Log Reader for internal log messages are now browser IP and session-based, allowing to separate messages from one another based on client that makes the request.
Added a warning to developer tools when using the default password.
Fixed bug with tester script that did not properly autoload certain PHP scripts.
Implemented an improved getState() method that allows to return data better from nested arrays.
Implemented an improved setState() that allows to set nested array values in State more easily.
PNG images will also now get a background, if RGB setting is set in the dynamic image loading.
Date: 1 year ago
Size: 18,676 bytes
 

Contents

Class file image Download
<?php

/**
 * Wave Framework <http://www.waveframework.com>
 * Test Suite
 *
 * Wave Framework includes a full test suite for writing tests for API's and running said 
 * tests. Tests themselves are defined in '/tools/tests/' subfolder and they can be run 
 * based on settings from the Test Suite tool. This Test Suite tool then returns the results 
 * from said tests and whether the tests '.$command.' or not.
 *
 * @package    Tools
 * @author     Kristo Vaher <kristo@waher.net>
 * @copyright  Copyright (c) 2012, Kristo Vaher
 * @license    GNU Lesser General Public License Version 3
 * @tutorial   /doc/pages/guide_tools.htm
 * @since      1.0.0
 * @version    3.6.0
 */

// This initializes tools and authentication
require('.'.DIRECTORY_SEPARATOR.'tools_autoload.php');

// Log is printed out in plain text format
header('Content-Type: text/html;charset=utf-8');

// Information about tests
$tests=array();

// If any tests are assigned to be run
if(isset($_POST['tests']) && !empty($_POST['tests']) && isset($_POST['count']) && $_POST['count']>0){

	// This holds the last detected error
	$lastError='';
	
	// This is an error handler itself
	function testErrorHandler($type=false,$message=false,$file=false,$line=false){
		global $lastError;
		$lastError.='TYPE: '.$type.'<br/>';
		$lastError.='MESSAGE: '.htmlspecialchars($message).'<br/>';
		$lastError.='FILE: '.$file.'<br/>';
		$lastError.='LINE: '.$line.'<br/>';
	}

	// Setting the error handler, this helps suppress the actual error output
	set_error_handler('testErrorHandler',E_ALL);

	// These arrays store the test results
	$testErrorLog=array();
	$testsRun=array();
	$testsPassed=array();
	$testsFailed=array();
	
	// If default execution time is changed
	if(isset($_POST['time']) && ini_get('max_execution_time')!=$_POST['time']){
		set_time_limit($_POST['time']);
	}
	
	// Loading State
	require(__ROOT__.'engine'.DIRECTORY_SEPARATOR.'class.www-state.php');
	$state=new WWW_State($config);
	
	// This holds link to database
	$databaseConnection=false;
	
	// Connecting to database, if configuration is set
	if(isset($config['test-database-name'],$config['test-database-type'],$config['test-database-host'],$config['test-database-username'],$config['test-database-password'])){
		// Including the required class and creating the object
		require(__ROOT__.'engine'.DIRECTORY_SEPARATOR.'class.www-database.php');
		$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);
		// Passing the database to State object
		$state->databaseConnection=$databaseConnection;
	}
	
	// Loading sessions class
	require(__ROOT__.'engine'.DIRECTORY_SEPARATOR.'class.www-sessions.php');
	// Loading sessions class with the session namespace
	$state->sessionHandler=new WWW_Sessions($state->data['session-name'],$state->data['session-lifetime'],$databaseConnection,true);
	
	// This functions file is not required, but can be used for system wide functions
	// If you want to include additional libraries, do so here
	if(file_exists(__ROOT__.'overrides'.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'scripts'.DIRECTORY_SEPARATOR.'script.php')){
		require(__ROOT__.'overrides'.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'scripts'.DIRECTORY_SEPARATOR.'script.php');
	} elseif(file_exists(__ROOT__.'overrides'.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'scripts'.DIRECTORY_SEPARATOR.'script.php')) {
		require(__ROOT__.'overrides'.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'scripts'.DIRECTORY_SEPARATOR.'script.php');
	}
	
	// API is used to process all requests and it handles caching and API validations
	require(__ROOT__.'engine'.DIRECTORY_SEPARATOR.'class.www-api.php');
	$api=new WWW_API($state);
	
	// Using error reporting in tests to catch potential errors
	error_reporting(E_ALL);
	
	// Looping over each test
	foreach($_POST['tests'] as $test){
	
		// Finding the test name based on filename
		$filename=explode('.',$test);
		$tmp=array_pop($filename);
		$testName=implode('.',$filename);
		
		// Loading test configuration
		$configuration=parse_ini_file('tests'.DIRECTORY_SEPARATOR.$test,true,INI_SCANNER_RAW);
		if($configuration){
		
			// Resetting the variables about the current test
			$command=false;
			$input=false;
			$output=false;
			
			// Looping over configuration and tests
			foreach($configuration as $group=>$settings){
			
				// Checking for INI groups for master/input/output related settings
				if($group!='input' && $group!='output'){
					// The same command can be tested multiple times in a single test script
					$trueCommand=$group;
					$command=explode('#',$trueCommand);
					$command=$command[0];
					// Resetting input values
					$input=false;
					$output=false;
					// Assigning the group settings as input
					$masterInput=$settings;
				} elseif($group=='input'){
					$input=$settings;
				} elseif($group=='output'){
					$output=$settings;
				}
				
				// Running the test if all the settings are ok
				if($command && $output){
				
					// Tests are run multiple times
					for($x=1;$x<=$_POST['count'];$x++){
					
						// Defining test address which can be used later on to link between arrays
						$testAddress=$testName.' '.$trueCommand.' #'.$x;
					
						// This is the input array sent to API
						$thisInput=array();
						
						// Input is not required, but if it exists then generating input variables
						if($input && !empty($input)){
						
							// Input variables can be both dynamic and fixed
							foreach($input as $key=>$value){
								if(preg_match('/^:numeric:/',$value)){
								
									// Numeric input is either a random integer or an integer within range of numbers
									$bits=explode(':',$value,3);
									if(isset($bits[2]) && $bits[2]!=''){
										$bits=explode('-',$bits[2]);
										$thisInput[$key]=rand($bits[0],$bits[1]);
									} else {
										$thisInput[$key]=rand(0,2147483647);
									}
									
								} elseif(preg_match('/^:alpha:/',$value)){
								
									// Alpha input is random characters and spaces
									$characters="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ";
									$charactersLength=strlen($characters)-1;
									$bits=explode(':',$value,3);
									if(isset($bits[2]) && $bits[2]!=''){
										$bits=explode('-',$bits[2]);
										$length=rand($bits[0],$bits[1]);
									} else {
										$length=rand(1,32);
									}
									$string='';
									for($i=1;$i<=$length;$i++){
										$string.=$characters[rand(0,$charactersLength)];
									}
									// Assigning as input value
									$thisInput[$key]=$string;
									
								} elseif(preg_match('/^:alphanumeric:/',$value)){
								
									// Alpha input is random characters and spaces
									$characters="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ";
									$charactersLength=strlen($characters)-1;
									$bits=explode(':',$value,3);
									if(isset($bits[2]) && $bits[2]!=''){
										$bits=explode('-',$bits[2]);
										$length=rand($bits[0],$bits[1]);
									} else {
										$length=rand(1,32);
									}
									$string='';
									for($i=1;$i<=$length;$i++){
										$string.=$characters[rand(0,$charactersLength)];
									}
									// Assigning as input value
									$thisInput[$key]=$string;
									
								} elseif(preg_match('/^:fixed:/',$value)){
								
									// Fixed input is a random value from a fixed list
									$bits=explode(':',$value,3);
									if(isset($bits[2]) && $bits[2]!=''){
										$bits=explode(',',$bits[2]);
										$length=count($bits)-1;
										$thisInput[$key]=$bits[rand(0,$length)];
									}		
									
								} else {
								
									// If value was not dynamic, then it is sent as fixed
									$thisInput[$key]=$value;
									
								}
							}
							
						}
						
						// Final input
						$thisInput=$thisInput+$masterInput;
						
						// Logging the tests run
						$testsRun[$testAddress]=$thisInput;
						
						// Some of the values are enforced
						$thisInput['www-command']=$command;
						$thisInput['www-return-type']='php';
						$thisInput['www-headers']=0;
						$thisInput['www-disable-callbacks']=1;
						
						// Making the API request
						$result=$apiResult=$api->command($thisInput,false,false,false);
						
						// It is considered a failed test if the output buffer contains contents
						if(trim($lastError)!=''){
						
							// Error failed since output buffer included contents
							$testErrorLog[$testAddress]=$testName.' test '.$trueCommand.' iteration #'.$x.' - encountered an error:<br/>'.$lastError;
							// Resetting the last error container
							$lastError='';
						
						} else {
						
							// Looping over the expected output variables and seeing if they were actually part of API output
							foreach($output as $key=>$value){
							
								// Testing if the otput key exists
								if(isset($result[$key])){
								
									if(preg_match('/^:numeric:/',$value)){
									
										// Testing if the return value is numeric and within accepted range
										$bits=explode(':',$value,3);
										if(isset($bits[2]) && $bits[2]!=''){
											$variables=explode('-',$bits[2]);
											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])){
												$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].'"';
											}
										} else {
											if(!preg_match('/^[0-9]*\Z/i',$result[$key])){
												$testErrorLog[$testAddress]=$testName.' test '.$trueCommand.' iteration #'.$x.' - output key "'.$key.'" with the value "'.$result[$key].'" is not numeric';
											}
										}
										
									} elseif(preg_match('/^:alpha:/',$value)){
									
										// Testing if the response consists of letters
										$bits=explode(':',$value,3);
										if(isset($bits[2]) && $bits[2]!=''){
											$variables=explode('-',$bits[2]);
											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])){
												$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].'"';
											}
										} else {
											if(!preg_match('/^[[:alpha:] ]*\Z/ui',$result[$key])){
												$testErrorLog[$testAddress]=$testName.' test '.$trueCommand.' iteration #'.$x.' - output key "'.$key.'" with the value "'.$result[$key].'" does not just have letters';
											}
										}
										
									} elseif(preg_match('/^:alphanumeric:/',$value)){
									
										// Testing if the response consists of letters and numbers
										$bits=explode(':',$value,3);
										if(isset($bits[2]) && $bits[2]!=''){
											$variables=explode('-',$bits[2]);
											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])){
												$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].'"';
											}
										} else {
											if(!preg_match('/^[[:alnum:] ]*\Z/ui',$result[$key])){
												$testErrorLog[$testAddress]=$testName.' test '.$trueCommand.' iteration #'.$x.' - output key "'.$key.'" with the value "'.$result[$key].'" is not alphanumeric';
											}
										}
										
									} elseif(preg_match('/^:fixed:/',$value)){
									
										// Testing if the response is an accepted fixed value
										$bits=explode(':',$value,3);
										if(isset($bits[2]) && $bits[2]!=''){
											$variables=explode(',',$bits[2]);
											if(!in_array($result[$key],$variables)){
												$testErrorLog[$testAddress]=$testName.' test '.$trueCommand.' iteration #'.$x.' - output key "'.$key.'" with the value "'.$result[$key].'" did not match the expected value "'.$bits[2].'"';
											}
										} else {
											$testErrorLog[$testAddress]=$testName.' test '.$trueCommand.' iteration #'.$x.' - output key "'.$key.'" with the value "'.$result[$key].'" could not be matched against empty fixed response';
										}
										
									} elseif(preg_match('/^:any:/',$value)){
									
										// Testing if the response is an accepted fixed value
										$bits=explode(':',$value,3);
										if(isset($bits[2]) && $bits[2]!=''){
											if(!preg_match('/^['.$bits[2].']*$/u',$result[$key])){
												$testErrorLog[$testAddress]=$testName.' test '.$trueCommand.' iteration #'.$x.' - output key "'.$key.'" with the value "'.$result[$key].'" included illegal characters "'.$bits[2].'"';
											}
										}
										
									} else {
									
										// Testing if the value equals the expected value or not
										if($value && $result[$key]!=$value){
											$testErrorLog[$testAddress]=$testName.' test '.$trueCommand.' iteration #'.$x.' - output key "'.$key.'" with the value "'.$result[$key].'" did not match the expected value "'.$value.'"';
										}
										
									}
									
								} else {
								
									// Expected value did not exists, so the test is considered as failed
									$testErrorLog[$testAddress]=$testName.' test '.$trueCommand.' iteration #'.$x.' - output key "'.$key.'" was not returned';
									
								}				
								
							}
							
						}
						
						// Storing the result in counter
						if(isset($testErrorLog[$testAddress])){
							$testsFailed[$testAddress]=true;
						} else {
							$testsPassed[$testAddress]=true;
						}
					
					}
					
				}
				
			}
			
		} else {
		
			// Test configuration failure is considered a '.$command.' test, but is not actual part of 'statistics'
			$testErrorLog[$testName.'#all']=$testName.'#all - '.$command.' - test configuration incorrect';
			
		}
		
		// Reloading state
		$state=new WWW_State($config);
		$state->databaseConnection=$databaseConnection;
		$state->sessionHandler=new WWW_Sessions($state->data['session-name'],$state->data['session-lifetime'],$databaseConnection,true);
		
		// Reloading API
		$api=new WWW_API($state);
		
	}
	
}

// Finding all INI configuration files for tests
$testFiles=scandir('tests'.DIRECTORY_SEPARATOR);

// Looping over each test file to check whether configuration is correct
foreach($testFiles as $test){
	// $state->setState('testing',true);
	// Ignoring the placeholder file
	if($test!='.empty' && is_file('tests'.DIRECTORY_SEPARATOR.$test)){
		// Test configuration should be stored as a grouped INI configuration file
		$configuration=parse_ini_file('tests'.DIRECTORY_SEPARATOR.$test,true,INI_SCANNER_RAW);
		if($configuration){
			$tests[$test]=$configuration;
		}
	}
}
	
?>
<!DOCTYPE html>
<html lang="en">
	<head>
		<title>Test Suite</title>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width"/> 
		<link type="text/css" href="style.css" rel="stylesheet" media="all"/>
		<link rel="icon" href="../favicon.ico" type="image/x-icon"/>
		<link rel="icon" href="../favicon.ico" type="image/vnd.microsoft.icon"/>
		<meta content="noindex,nocache,nofollow,noarchive,noimageindex,nosnippet" name="robots"/>
		<meta http-equiv="cache-control" content="no-cache"/>
		<meta http-equiv="pragma" content="no-cache"/>
		<meta http-equiv="expires" content="0"/>
	</head>
	<body>
		<?php
		
		// Pops up an alert about default password
		passwordNotification($config['http-authentication-password']);
		
		// Header
		echo '<h1>Test Suite</h1>';
		echo '<h4 class="highlight">';
		foreach($softwareVersions as $software=>$version){
			// Adding version numbers
			echo '<b>'.$software.'</b> ('.$version.') ';
		}
		echo '</h4>';
		
		// If error log is defined (if testing happened)
		if(isset($testErrorLog)){
		
			// Printing out test results
			echo '<h2>Test Results</h2>';
			echo '<p class="bold">'.count($testsRun).' tests run, '.count($testsFailed).' failed</p>';
			if(!empty($testErrorLog)){
				echo '<h3>Logged errors</h3>';
				foreach($testErrorLog as $address=>$log){
					echo '<p class="bold red small" style="text-align:left;">';
					echo $log;
					echo '</p>';
					if(isset($testsRun[$address]) && !empty($testsRun[$address])){
						echo '<p class="bold small">Input data</p>';
						echo '<pre style="padding-left:40px;">';
							print_r($testsRun[$address]);
						echo '</pre>';
					}
				}
			}
			
		}
		
		echo '<h2>Available Tests</h2>';
		
		// Generating the form for all available tests
		if(!empty($tests)){
		
			?>
				<form method="post" action="" enctype="multipart/form-data">
					<?php
					foreach($tests as $file=>$test){
						$filename=explode('.',$file);
						$tmp=array_pop($filename);
						$testName=implode('.',$filename);
						?>
						<p class="bold"><input type="checkbox" name="tests[]" value="<?=$file?>" <?=(isset($_POST['tests']) && in_array($file,$_POST['tests']))?'checked':''?>/> <?=$testName?></p>
						<?php
					}
					?>
					<p class="bold">
						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 
						<select name="count">
							<option value="1">1</option>
							<?php for($i=10;$i<=1000;$i=$i+10){?>
								<option value="<?=$i?>" <?=(isset($_POST['count']) && $_POST['count']==$i)?'selected':''?>><?=$i?></option>
							<?php } ?>
						</select> times 
						<input type="submit" value="GO!"/>
					</p>
					<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>
				</form>
			<?php
		
		} else {
			echo '<p class="bold">There are no tests that can be run from /tools/tests/ subfolder</p>';
		}
		
		// Footer
		echo '<p class="footer small bold">Generated at '.date('d.m.Y h:i').' GMT '.date('P').' for '.$_SERVER['HTTP_HOST'].'</p>';
	
		?>
	</body>
</html>