PageRenderTime 394ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/tools/tester.php

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