Code: Select all
<?php
/**
* Based loosely upon the designs of autotest.rb and ruby auto test utility(s)
*
* Pass the path you want to recurisvely monitor as the only command line argument
* Override or change doDelay() and runTests() to suite your environment
*
*
* - override runTests() with the command you want to run
*
* - override doDelay() to check more/less often
*
* - pass the path to files in the contstructor
*
* - pass a log level to the constructor if you would like helpful output
* (although this may interfere with ability to read your test failures if your delay is a low # )
*
* Invoke like this:
* C:/wamp/bin/php/php5.3.0/php E:\dev\path\phpautotest.php E:\dev\path\to\monitor
*
*
* @license code is licensed persuant to Open Software License ("OSL") v. 3.0
*
* You may use it within your applications without providing credit
* Any modifications or derivative works must be licensed OSL v 3.0 unless express written consent provided
*
* @author Josh Ribakoff <josh.ribakoff@gmail.com> http://www.ne8.net
*/
if( isset( $argv[1] ) )
{
$path = trim( $argv[1] );
}
if( !$path )
{
exit();
}
$test = new PHPAutoTest( $path );
$test->main();
/**
* The main test controller
*/
class PHPAutoTest
{
protected $path;
/**
* No log messages
*/
const NONE = 0;
/**
* Notify when file checking starts/stops
*/
const CHANGE_LOOP = 1;
/**
* Notify when changes are detected
*/
const CHANGED_FILES = 2;
/**
* @var array of PHPAutoTest_File
*/
protected $files = array();
/**
* @var PHPAutoTest_Logger
*/
protected $logger;
/**
* @param mixed $path path of files to monitor
* @param mixed $logLevel set to NONE, CHANGE_LOOP, or CHANGED_FILES
*/
public function __construct( $path, $logLevel = 2)
{
$this->path = $path;
$this->logger = new PHPAutoTest_Logger( $logLevel );
}
public function main()
{
while( true )
{
$this->logger->log( "checking files, please wait", self::CHANGE_LOOP );
$run = false;
foreach( $this->filesMonitoring() as $file )
{
if( $file->hasDeleted() )
{
$run = true;
}
if( $file->hasModified( ) )
{
$run = true;
}
}
if( $run )
{
$this->logger->log( "changes detected", self::CHANGE_LOOP );
$this->runTests();
}
$this->logger->log( "done", self::CHANGE_LOOP );
$this->doDelay();
}
}
protected function doDelay()
{
sleep( 4 );
}
protected function runTests()
{
passthru( 'tests.bat' );
}
/**
* @return array of PHPAutoTest_File
*/
function filesMonitoring()
{
$files = $this->recursiveGlob( $this->path );
$return = array();
foreach( $files as $file )
{
if( isset( $this->files[ md5( $file ) ] ) )
{
$fileObj = $this->files[ md5( $file ) ];
array_push( $return, $fileObj );
if( $fileObj->hasDeleted() )
{
unset( $this->files[ $fileObj->getHash() ] );
}
continue;
}
$file = new PHPAutoTest_File( $file, $this->logger );
array_push( $return, $file );
$this->files[ $file->getHash() ] = $file;
}
return $return;
}
protected function recursiveGlob( $path )
{
$files = Array();
$file_tmp= glob( $path . '*',GLOB_MARK | GLOB_NOSORT);
foreach($file_tmp as $item)
{
if(substr($item,-1)!=DIRECTORY_SEPARATOR)
{
$files[] = $item;
}
else
{
$files = array_merge($files, $this->recursiveGlob($item));
}
}
return $files;
}
}
/**
* Each file is a PHPAutoTest_File object
*/
class PHPAutoTest_File
{
protected $file;
protected $modified;
protected $logger;
public function __construct( $file, PHPAutoTest_Logger $logger )
{
$this->file = $file;
$this->logger = $logger;
}
public function isFile()
{
return filetype( $this->file ) == 'file';
}
public function hasDeleted()
{
if( !file_exists( $this->file ) )
{
return true;
}
}
/**
* put your comment there...
*
* @return boolean true if need to re-run tests, otherwise false
*/
public function hasModified()
{
if( !$this->isFile() )
{
return false;
}
$modified = filemtime( $this->file );
$return = (bool)( $modified > $this->modified && $this->modified != 0 );
if( $return ) $this->logger->log( $this->file . " modified at " . $modified, PHPAutoTest::CHANGED_FILES );
$this->modified = $modified;
return $return;
}
public function getHash()
{
return md5( $this->file );
}
}
class PHPAutoTest_Logger
{
protected $logLevel;
public function __construct( $logLevel )
{
$this->logLevel = $logLevel;
}
public function log( $msg, $level )
{
if( $this->logLevel >= $level )
{
echo $msg . "\n";
}
}
}
November 19: updated again