MediaWiki  REL1_22
JobQueueTest.php
Go to the documentation of this file.
00001 <?php
00002 
00008 class JobQueueTest extends MediaWikiTestCase {
00009     protected $key;
00010     protected $queueRand, $queueRandTTL, $queueFifo, $queueFifoTTL;
00011 
00012     function __construct( $name = null, array $data = array(), $dataName = '' ) {
00013         parent::__construct( $name, $data, $dataName );
00014 
00015         $this->tablesUsed[] = 'job';
00016     }
00017 
00018     protected function setUp() {
00019         global $wgJobTypeConf;
00020         parent::setUp();
00021 
00022         $this->setMwGlobals( 'wgMemc', new HashBagOStuff() );
00023 
00024         if ( $this->getCliArg( 'use-jobqueue=' ) ) {
00025             $name = $this->getCliArg( 'use-jobqueue=' );
00026             if ( !isset( $wgJobTypeConf[$name] ) ) {
00027                 throw new MWException( "No \$wgJobTypeConf entry for '$name'." );
00028             }
00029             $baseConfig = $wgJobTypeConf[$name];
00030         } else {
00031             $baseConfig = array( 'class' => 'JobQueueDB' );
00032         }
00033         $baseConfig['type'] = 'null';
00034         $baseConfig['wiki'] = wfWikiID();
00035         $variants = array(
00036             'queueRand' => array( 'order' => 'random', 'claimTTL' => 0 ),
00037             'queueRandTTL' => array( 'order' => 'random', 'claimTTL' => 10 ),
00038             'queueTimestamp' => array( 'order' => 'timestamp', 'claimTTL' => 0 ),
00039             'queueTimestampTTL' => array( 'order' => 'timestamp', 'claimTTL' => 10 ),
00040             'queueFifo' => array( 'order' => 'fifo', 'claimTTL' => 0 ),
00041             'queueFifoTTL' => array( 'order' => 'fifo', 'claimTTL' => 10 ),
00042         );
00043         foreach ( $variants as $q => $settings ) {
00044             try {
00045                 $this->$q = JobQueue::factory( $settings + $baseConfig );
00046                 if ( !( $this->$q instanceof JobQueueDB ) ) {
00047                     $this->$q->setTestingPrefix( 'unittests-' . wfRandomString( 32 ) );
00048                 }
00049             } catch ( MWException $e ) {
00050                 // unsupported?
00051                 // @todo What if it was another error?
00052             };
00053         }
00054     }
00055 
00056     protected function tearDown() {
00057         parent::tearDown();
00058         foreach (
00059             array(
00060                 'queueRand', 'queueRandTTL', 'queueTimestamp', 'queueTimestampTTL',
00061                 'queueFifo', 'queueFifoTTL'
00062             ) as $q
00063         ) {
00064             if ( $this->$q ) {
00065                 $this->$q->delete();
00066             }
00067             $this->$q = null;
00068         }
00069     }
00070 
00074     public function testProperties( $queue, $recycles, $desc ) {
00075         $queue = $this->$queue;
00076         if ( !$queue ) {
00077             $this->markTestSkipped( $desc );
00078         }
00079 
00080         $this->assertEquals( wfWikiID(), $queue->getWiki(), "Proper wiki ID ($desc)" );
00081         $this->assertEquals( 'null', $queue->getType(), "Proper job type ($desc)" );
00082     }
00083 
00087     public function testBasicOperations( $queue, $recycles, $desc ) {
00088         $queue = $this->$queue;
00089         if ( !$queue ) {
00090             $this->markTestSkipped( $desc );
00091         }
00092 
00093         $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
00094 
00095         $queue->flushCaches();
00096         $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
00097         $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
00098 
00099         $this->assertTrue( $queue->push( $this->newJob() ), "Push worked ($desc)" );
00100         $this->assertTrue( $queue->batchPush( array( $this->newJob() ) ), "Push worked ($desc)" );
00101 
00102         $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
00103 
00104         $queue->flushCaches();
00105         $this->assertEquals( 2, $queue->getSize(), "Queue size is correct ($desc)" );
00106         $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
00107         $jobs = iterator_to_array( $queue->getAllQueuedJobs() );
00108         $this->assertEquals( 2, count( $jobs ), "Queue iterator size is correct ($desc)" );
00109 
00110         $job1 = $queue->pop();
00111         $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
00112 
00113         $queue->flushCaches();
00114         $this->assertEquals( 1, $queue->getSize(), "Queue size is correct ($desc)" );
00115 
00116         $queue->flushCaches();
00117         if ( $recycles ) {
00118             $this->assertEquals( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
00119         } else {
00120             $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
00121         }
00122 
00123         $job2 = $queue->pop();
00124         $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
00125         $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
00126 
00127         $queue->flushCaches();
00128         if ( $recycles ) {
00129             $this->assertEquals( 2, $queue->getAcquiredCount(), "Active job count ($desc)" );
00130         } else {
00131             $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
00132         }
00133 
00134         $queue->ack( $job1 );
00135 
00136         $queue->flushCaches();
00137         if ( $recycles ) {
00138             $this->assertEquals( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
00139         } else {
00140             $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
00141         }
00142 
00143         $queue->ack( $job2 );
00144 
00145         $queue->flushCaches();
00146         $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
00147 
00148         $this->assertTrue( $queue->batchPush( array( $this->newJob(), $this->newJob() ) ),
00149             "Push worked ($desc)" );
00150         $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
00151 
00152         $queue->delete();
00153         $queue->flushCaches();
00154         $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
00155         $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
00156     }
00157 
00161     public function testBasicDeduplication( $queue, $recycles, $desc ) {
00162         $queue = $this->$queue;
00163         if ( !$queue ) {
00164             $this->markTestSkipped( $desc );
00165         }
00166 
00167         $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
00168 
00169         $queue->flushCaches();
00170         $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
00171         $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
00172 
00173         $this->assertTrue(
00174             $queue->batchPush(
00175                 array( $this->newDedupedJob(), $this->newDedupedJob(), $this->newDedupedJob() )
00176             ),
00177             "Push worked ($desc)" );
00178 
00179         $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
00180 
00181         $queue->flushCaches();
00182         $this->assertEquals( 1, $queue->getSize(), "Queue size is correct ($desc)" );
00183         $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
00184 
00185         $this->assertTrue(
00186             $queue->batchPush(
00187                 array( $this->newDedupedJob(), $this->newDedupedJob(), $this->newDedupedJob() )
00188             ),
00189             "Push worked ($desc)"
00190         );
00191 
00192         $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
00193 
00194         $queue->flushCaches();
00195         $this->assertEquals( 1, $queue->getSize(), "Queue size is correct ($desc)" );
00196         $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
00197 
00198         $job1 = $queue->pop();
00199         $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
00200 
00201         $queue->flushCaches();
00202         $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
00203         if ( $recycles ) {
00204             $this->assertEquals( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
00205         } else {
00206             $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
00207         }
00208 
00209         $queue->ack( $job1 );
00210 
00211         $queue->flushCaches();
00212         $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
00213     }
00214 
00218     public function testRootDeduplication( $queue, $recycles, $desc ) {
00219         $queue = $this->$queue;
00220         if ( !$queue ) {
00221             $this->markTestSkipped( $desc );
00222         }
00223 
00224         $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
00225 
00226         $queue->flushCaches();
00227         $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
00228         $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
00229 
00230         $id = wfRandomString( 32 );
00231         $root1 = Job::newRootJobParams( "nulljobspam:$id" ); // task ID/timestamp
00232         for ( $i = 0; $i < 5; ++$i ) {
00233             $this->assertTrue( $queue->push( $this->newJob( 0, $root1 ) ), "Push worked ($desc)" );
00234         }
00235         $queue->deduplicateRootJob( $this->newJob( 0, $root1 ) );
00236         sleep( 1 ); // roo job timestamp will increase
00237         $root2 = Job::newRootJobParams( "nulljobspam:$id" ); // task ID/timestamp
00238         $this->assertNotEquals( $root1['rootJobTimestamp'], $root2['rootJobTimestamp'],
00239             "Root job signatures have different timestamps." );
00240         for ( $i = 0; $i < 5; ++$i ) {
00241             $this->assertTrue( $queue->push( $this->newJob( 0, $root2 ) ), "Push worked ($desc)" );
00242         }
00243         $queue->deduplicateRootJob( $this->newJob( 0, $root2 ) );
00244 
00245         $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
00246 
00247         $queue->flushCaches();
00248         $this->assertEquals( 10, $queue->getSize(), "Queue size is correct ($desc)" );
00249         $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
00250 
00251         $dupcount = 0;
00252         $jobs = array();
00253         do {
00254             $job = $queue->pop();
00255             if ( $job ) {
00256                 $jobs[] = $job;
00257                 $queue->ack( $job );
00258             }
00259             if ( $job instanceof DuplicateJob ) {
00260                 ++$dupcount;
00261             }
00262         } while ( $job );
00263 
00264         $this->assertEquals( 10, count( $jobs ), "Correct number of jobs popped ($desc)" );
00265         $this->assertEquals( 5, $dupcount, "Correct number of duplicate jobs popped ($desc)" );
00266     }
00267 
00271     public function testJobOrder( $queue, $recycles, $desc ) {
00272         $queue = $this->$queue;
00273         if ( !$queue ) {
00274             $this->markTestSkipped( $desc );
00275         }
00276 
00277         $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
00278 
00279         $queue->flushCaches();
00280         $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
00281         $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
00282 
00283         for ( $i = 0; $i < 10; ++$i ) {
00284             $this->assertTrue( $queue->push( $this->newJob( $i ) ), "Push worked ($desc)" );
00285         }
00286 
00287         for ( $i = 0; $i < 10; ++$i ) {
00288             $job = $queue->pop();
00289             $this->assertTrue( $job instanceof Job, "Jobs popped from queue ($desc)" );
00290             $params = $job->getParams();
00291             $this->assertEquals( $i, $params['i'], "Job popped from queue is FIFO ($desc)" );
00292             $queue->ack( $job );
00293         }
00294 
00295         $this->assertFalse( $queue->pop(), "Queue is not empty ($desc)" );
00296 
00297         $queue->flushCaches();
00298         $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
00299         $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
00300     }
00301 
00302     public static function provider_queueLists() {
00303         return array(
00304             array( 'queueRand', false, 'Random queue without ack()' ),
00305             array( 'queueRandTTL', true, 'Random queue with ack()' ),
00306             array( 'queueTimestamp', false, 'Time ordered queue without ack()' ),
00307             array( 'queueTimestampTTL', true, 'Time ordered queue with ack()' ),
00308             array( 'queueFifo', false, 'FIFO ordered queue without ack()' ),
00309             array( 'queueFifoTTL', true, 'FIFO ordered queue with ack()' )
00310         );
00311     }
00312 
00313     public static function provider_fifoQueueLists() {
00314         return array(
00315             array( 'queueFifo', false, 'Ordered queue without ack()' ),
00316             array( 'queueFifoTTL', true, 'Ordered queue with ack()' )
00317         );
00318     }
00319 
00320     function newJob( $i = 0, $rootJob = array() ) {
00321         return new NullJob( Title::newMainPage(),
00322             array( 'lives' => 0, 'usleep' => 0, 'removeDuplicates' => 0, 'i' => $i ) + $rootJob );
00323     }
00324 
00325     function newDedupedJob( $i = 0, $rootJob = array() ) {
00326         return new NullJob( Title::newMainPage(),
00327             array( 'lives' => 0, 'usleep' => 0, 'removeDuplicates' => 1, 'i' => $i ) + $rootJob );
00328     }
00329 }