PHP Classes

File: tools/tester.php

Recommend this page to a friend!
  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: removed references to waveframework.com
Date: 10 days ago
Size: 18,692 bytes
 

Contents

Class file image Download
<?php /** * Wave Framework <http://github.com/kristovaher/Wave-Framework> * 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>