From c0347df537ba10c6b6b11eb48eece3a761107b80 Mon Sep 17 00:00:00 2001 From: Kore Nordmann Date: Thu, 13 May 2010 11:35:24 +0200 Subject: [PATCH] - Implemented: Shell logger, logging executor process to STDERR --- src/environment.php | 3 + src/executor.php | 26 ++++++ src/job_provider/shell.php | 12 ++- src/logger/cli.php | 119 ++++++++++++++++++++++++++ src/logger/dummy.php | 67 +++++++++++++++ src/logger/shell.php | 119 ++++++++++++++++++++++++++ tests/logger/shell_tests.php | 159 +++++++++++++++++++++++++++++++++++ tests/logger_suite.php | 60 +++++++++++++ tests/suite.php | 2 + 9 files changed, 566 insertions(+), 1 deletion(-) create mode 100644 src/logger/cli.php create mode 100644 src/logger/dummy.php create mode 100644 src/logger/shell.php create mode 100644 tests/logger/shell_tests.php create mode 100644 tests/logger_suite.php diff --git a/src/environment.php b/src/environment.php index a54b275..82251aa 100644 --- a/src/environment.php +++ b/src/environment.php @@ -26,6 +26,9 @@ /* * Includes all classes, which are required to use the Native PHP job queue. */ +require __DIR__ . '/logger.php'; +require __DIR__ . '/logger/dummy.php'; +require __DIR__ . '/logger/shell.php'; require __DIR__ . '/executor.php'; require __DIR__ . '/job_provider.php'; require __DIR__ . '/job_provider/shell.php'; diff --git a/src/executor.php b/src/executor.php index 05f024f..5d27587 100644 --- a/src/executor.php +++ b/src/executor.php @@ -35,6 +35,26 @@ */ class Executor { + /** + * Logger used to log the execution + * + * @var Logger + */ + protected $logger; + + /** + * Construct from logger + * + * Defaults to a dummy logger, which does nothing + * + * @param Logger $logger + * @return void + */ + public function __construct( Logger $logger = null ) + { + $this->logger = $logger === null ? new DummyLogger() : $logger; + } + /** * Run jobs * @@ -51,7 +71,10 @@ class Executor */ public function run( JobProvider $jobs, $parallel = 4 ) { + $this->logger->startExecutor( $this, $jobs ); + $forks = array(); + $jobNr = 0; while ( $jobs->hasJobs() || count( $forks ) ) { @@ -59,6 +82,7 @@ public function run( JobProvider $jobs, $parallel = 4 ) while ( ( count( $forks ) < $parallel ) && ( $job = $jobs->getNextJob() ) ) { + $this->logger->progressJob( ++$jobNr ); if ( ( $forks[] = pcntl_fork() ) === 0 ) { // We are the newly forked child, just execute the job @@ -77,6 +101,8 @@ public function run( JobProvider $jobs, $parallel = 4 ) } } while ( count( $forks ) >= $parallel ); } + + $this->logger->finishedExecutor(); } } diff --git a/src/job_provider/shell.php b/src/job_provider/shell.php index cd14b8c..98eba62 100644 --- a/src/job_provider/shell.php +++ b/src/job_provider/shell.php @@ -31,7 +31,7 @@ * Constructed from an array of shell commands, it returns those, to all be * executed. */ -class ShellJobProvider implements JobProvider +class ShellJobProvider implements JobProvider, \Countable { /** * Array of shell commands, which are provided by this job provider and @@ -89,5 +89,15 @@ public function getNextJob() return shell_exec( $command ); }; } + + /** + * Return numer of shell commands + * + * @return int + */ + public function count() + { + return count( $this->shellCmds ); + } } diff --git a/src/logger/cli.php b/src/logger/cli.php new file mode 100644 index 0000000..5a60e3a --- /dev/null +++ b/src/logger/cli.php @@ -0,0 +1,119 @@ +stream = $output; + } + + /** + * Method called, when the executor run is started + * + * @param Executor $executor + * @return void + */ + public function startExecutor( Executor $executoar, JobProvider $jobProvider ) + { + if ( $jobProvider instanceof \Countable ) + { + $this->count = count( $jobProvider ); + } + } + + /** + * Method called, when all jobs are executed + * + * @return void + */ + public function finishedExecutor() + { + fprint( $this->stream, "\n" ); + } + + /** + * Method called, when all jobs are executed + * + * @return void + */ + public function progressJob( $nr ) + { + if ( $this->count ) + { + fwrite( $this->stream, sprintf( " \r% 4d / %d (% 2.2F%%) %s ", + $nr + 1, + $this->count, + ( $nr + 1 ) / $this->count * 100, + $this->processIndicators[$nr % count( $this->processIndicators )] + ) ); + } + else + { + fwrite( $this->stream, sprintf( " \r% 4d %s ", + $nr + 1, + $this->processIndicators[$nr % count( $this->processIndicators )] + ) ); + } + } +} + diff --git a/src/logger/dummy.php b/src/logger/dummy.php new file mode 100644 index 0000000..970a472 --- /dev/null +++ b/src/logger/dummy.php @@ -0,0 +1,67 @@ +stream = $output; + } + + /** + * Method called, when the executor run is started + * + * @param Executor $executor + * @return void + */ + public function startExecutor( Executor $executoar, JobProvider $jobProvider ) + { + if ( $jobProvider instanceof \Countable ) + { + $this->count = count( $jobProvider ); + } + } + + /** + * Method called, when all jobs are executed + * + * @return void + */ + public function finishedExecutor() + { + fwrite( $this->stream, "\n" ); + } + + /** + * Method called, when all jobs are executed + * + * @return void + */ + public function progressJob( $nr ) + { + if ( $this->count ) + { + \fwrite( $this->stream, \sprintf( " \r% 4d / %d (% 2.2F%%) %s ", + $nr, + $this->count, + $nr / $this->count * 100, + $this->processIndicators[$nr % count( $this->processIndicators )] + ) ); + } + else + { + \fwrite( $this->stream, \sprintf( " \r% 4d %s ", + $nr, + $this->processIndicators[$nr % count( $this->processIndicators )] + ) ); + } + } +} + diff --git a/tests/logger/shell_tests.php b/tests/logger/shell_tests.php new file mode 100644 index 0000000..81fb3b7 --- /dev/null +++ b/tests/logger/shell_tests.php @@ -0,0 +1,159 @@ +jobs ); + } + + public function getNextJob() + { + $command = array_pop( $this->jobs ); + + if ( $command === null ) + { + return null; + } + + return function() + { + return null; + }; + } +} + +/** + * Tests for shell job executor + */ +class ShellTests extends \PHPUnit_Framework_TestCase +{ + /** + * Return test suite + * + * @return PHPUnit_Framework_TestSuite + */ + public static function suite() + { + return new \PHPUnit_Framework_TestSuite( __CLASS__ ); + } + + public function tearDown() + { + if ( is_file( 'tmp' ) ) + { + unlink( 'tmp' ); + } + } + + public function testNoJobs() + { + $fp = fopen( 'tmp', 'w' ); + + $executor = new \njq\Executor( + new \njq\ShellLogger( $fp ) + ); + $executor->run( + new \njq\ShellJobProvider( array() ) + ); + fclose( $fp ); + + $this->assertEquals( + "\n", + file_get_contents( 'tmp' ) + ); + } + + public function testExecuteSingleJob() + { + $fp = fopen( 'tmp', 'w' ); + + $executor = new \njq\Executor( + new \njq\ShellLogger( $fp ) + ); + $executor->run( + new \njq\ShellJobProvider( array( + 'echo "1"', + ) ) + ); + fclose( $fp ); + + $this->assertEquals( + " \r 1 / 1 (100.00%) / " . + "\n", + file_get_contents( 'tmp' ) + ); + } + + public function testExecuteMultipleJobs() + { + $fp = fopen( 'tmp', 'w' ); + + $executor = new \njq\Executor( + new \njq\ShellLogger( $fp ) + ); + $executor->run( + new \njq\ShellJobProvider( array( + 'echo "1"', + 'echo "2"', + 'echo "3"', + ) ) + ); + fclose( $fp ); + + $this->assertEquals( + " \r 1 / 3 (33.33%) / " . + " \r 2 / 3 (66.67%) - " . + " \r 3 / 3 (100.00%) \\ " . + "\n", + file_get_contents( 'tmp' ) + ); + } + + public function testExecuteNonCountableJobProvider() + { + $fp = fopen( 'tmp', 'w' ); + + $executor = new \njq\Executor( + new \njq\ShellLogger( $fp ) + ); + $executor->run( new DummyJobProvider() ); + fclose( $fp ); + + $this->assertEquals( + " \r 1 / " . + " \r 2 - " . + " \r 3 \\ " . + "\n", + file_get_contents( 'tmp' ) + ); + } +} + diff --git a/tests/logger_suite.php b/tests/logger_suite.php new file mode 100644 index 0000000..31efdd0 --- /dev/null +++ b/tests/logger_suite.php @@ -0,0 +1,60 @@ +setName( 'Shell job logger' ); + + $this->addTest( ShellTests::suite() ); + } + + /** + * Return test suite + * + * @return prpTestSuite + */ + public static function suite() + { + return new Suite( __CLASS__ ); + } +} diff --git a/tests/suite.php b/tests/suite.php index 58f4e9a..7dd9e56 100644 --- a/tests/suite.php +++ b/tests/suite.php @@ -32,6 +32,7 @@ */ require 'job_provider_suite.php'; require 'executor_suite.php'; +require 'logger_suite.php'; /** * Main test suite @@ -50,6 +51,7 @@ public function __construct() $this->addTestSuite( \njq\Tests\JobProvider\Suite::suite() ); $this->addTestSuite( \njq\Tests\Executor\Suite::suite() ); + $this->addTestSuite( \njq\Tests\Logger\Suite::suite() ); } /**