MediaWiki  REL1_24
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 
00075     public function testGetWiki( $queue, $recycles, $desc ) {
00076         $queue = $this->$queue;
00077         if ( !$queue ) {
00078             $this->markTestSkipped( $desc );
00079         }
00080         $this->assertEquals( wfWikiID(), $queue->getWiki(), "Proper wiki ID ($desc)" );
00081     }
00082 
00087     public function testGetType( $queue, $recycles, $desc ) {
00088         $queue = $this->$queue;
00089         if ( !$queue ) {
00090             $this->markTestSkipped( $desc );
00091         }
00092         $this->assertEquals( 'null', $queue->getType(), "Proper job type ($desc)" );
00093     }
00094 
00099     public function testBasicOperations( $queue, $recycles, $desc ) {
00100         $queue = $this->$queue;
00101         if ( !$queue ) {
00102             $this->markTestSkipped( $desc );
00103         }
00104 
00105         $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
00106 
00107         $queue->flushCaches();
00108         $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
00109         $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
00110 
00111         $this->assertNull( $queue->push( $this->newJob() ), "Push worked ($desc)" );
00112         $this->assertNull( $queue->batchPush( array( $this->newJob() ) ), "Push worked ($desc)" );
00113 
00114         $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
00115 
00116         $queue->flushCaches();
00117         $this->assertEquals( 2, $queue->getSize(), "Queue size is correct ($desc)" );
00118         $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
00119         $jobs = iterator_to_array( $queue->getAllQueuedJobs() );
00120         $this->assertEquals( 2, count( $jobs ), "Queue iterator size is correct ($desc)" );
00121 
00122         $job1 = $queue->pop();
00123         $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
00124 
00125         $queue->flushCaches();
00126         $this->assertEquals( 1, $queue->getSize(), "Queue size is correct ($desc)" );
00127 
00128         $queue->flushCaches();
00129         if ( $recycles ) {
00130             $this->assertEquals( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
00131         } else {
00132             $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
00133         }
00134 
00135         $job2 = $queue->pop();
00136         $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
00137         $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
00138 
00139         $queue->flushCaches();
00140         if ( $recycles ) {
00141             $this->assertEquals( 2, $queue->getAcquiredCount(), "Active job count ($desc)" );
00142         } else {
00143             $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
00144         }
00145 
00146         $queue->ack( $job1 );
00147 
00148         $queue->flushCaches();
00149         if ( $recycles ) {
00150             $this->assertEquals( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
00151         } else {
00152             $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
00153         }
00154 
00155         $queue->ack( $job2 );
00156 
00157         $queue->flushCaches();
00158         $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
00159 
00160         $this->assertNull( $queue->batchPush( array( $this->newJob(), $this->newJob() ) ),
00161             "Push worked ($desc)" );
00162         $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
00163 
00164         $queue->delete();
00165         $queue->flushCaches();
00166         $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
00167         $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
00168     }
00169 
00174     public function testBasicDeduplication( $queue, $recycles, $desc ) {
00175         $queue = $this->$queue;
00176         if ( !$queue ) {
00177             $this->markTestSkipped( $desc );
00178         }
00179 
00180         $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
00181 
00182         $queue->flushCaches();
00183         $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
00184         $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
00185 
00186         $this->assertNull(
00187             $queue->batchPush(
00188                 array( $this->newDedupedJob(), $this->newDedupedJob(), $this->newDedupedJob() )
00189             ),
00190             "Push worked ($desc)" );
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         $this->assertNull(
00199             $queue->batchPush(
00200                 array( $this->newDedupedJob(), $this->newDedupedJob(), $this->newDedupedJob() )
00201             ),
00202             "Push worked ($desc)"
00203         );
00204 
00205         $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
00206 
00207         $queue->flushCaches();
00208         $this->assertEquals( 1, $queue->getSize(), "Queue size is correct ($desc)" );
00209         $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
00210 
00211         $job1 = $queue->pop();
00212         $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
00213 
00214         $queue->flushCaches();
00215         $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
00216         if ( $recycles ) {
00217             $this->assertEquals( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
00218         } else {
00219             $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
00220         }
00221 
00222         $queue->ack( $job1 );
00223 
00224         $queue->flushCaches();
00225         $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
00226     }
00227 
00232     public function testRootDeduplication( $queue, $recycles, $desc ) {
00233         $queue = $this->$queue;
00234         if ( !$queue ) {
00235             $this->markTestSkipped( $desc );
00236         }
00237 
00238         $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
00239 
00240         $queue->flushCaches();
00241         $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
00242         $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
00243 
00244         $id = wfRandomString( 32 );
00245         $root1 = Job::newRootJobParams( "nulljobspam:$id" ); // task ID/timestamp
00246         for ( $i = 0; $i < 5; ++$i ) {
00247             $this->assertNull( $queue->push( $this->newJob( 0, $root1 ) ), "Push worked ($desc)" );
00248         }
00249         $queue->deduplicateRootJob( $this->newJob( 0, $root1 ) );
00250         sleep( 1 ); // roo job timestamp will increase
00251         $root2 = Job::newRootJobParams( "nulljobspam:$id" ); // task ID/timestamp
00252         $this->assertNotEquals( $root1['rootJobTimestamp'], $root2['rootJobTimestamp'],
00253             "Root job signatures have different timestamps." );
00254         for ( $i = 0; $i < 5; ++$i ) {
00255             $this->assertNull( $queue->push( $this->newJob( 0, $root2 ) ), "Push worked ($desc)" );
00256         }
00257         $queue->deduplicateRootJob( $this->newJob( 0, $root2 ) );
00258 
00259         $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
00260 
00261         $queue->flushCaches();
00262         $this->assertEquals( 10, $queue->getSize(), "Queue size is correct ($desc)" );
00263         $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
00264 
00265         $dupcount = 0;
00266         $jobs = array();
00267         do {
00268             $job = $queue->pop();
00269             if ( $job ) {
00270                 $jobs[] = $job;
00271                 $queue->ack( $job );
00272             }
00273             if ( $job instanceof DuplicateJob ) {
00274                 ++$dupcount;
00275             }
00276         } while ( $job );
00277 
00278         $this->assertEquals( 10, count( $jobs ), "Correct number of jobs popped ($desc)" );
00279         $this->assertEquals( 5, $dupcount, "Correct number of duplicate jobs popped ($desc)" );
00280     }
00281 
00286     public function testJobOrder( $queue, $recycles, $desc ) {
00287         $queue = $this->$queue;
00288         if ( !$queue ) {
00289             $this->markTestSkipped( $desc );
00290         }
00291 
00292         $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
00293 
00294         $queue->flushCaches();
00295         $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
00296         $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
00297 
00298         for ( $i = 0; $i < 10; ++$i ) {
00299             $this->assertNull( $queue->push( $this->newJob( $i ) ), "Push worked ($desc)" );
00300         }
00301 
00302         for ( $i = 0; $i < 10; ++$i ) {
00303             $job = $queue->pop();
00304             $this->assertTrue( $job instanceof Job, "Jobs popped from queue ($desc)" );
00305             $params = $job->getParams();
00306             $this->assertEquals( $i, $params['i'], "Job popped from queue is FIFO ($desc)" );
00307             $queue->ack( $job );
00308         }
00309 
00310         $this->assertFalse( $queue->pop(), "Queue is not empty ($desc)" );
00311 
00312         $queue->flushCaches();
00313         $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
00314         $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
00315     }
00316 
00317     public static function provider_queueLists() {
00318         return array(
00319             array( 'queueRand', false, 'Random queue without ack()' ),
00320             array( 'queueRandTTL', true, 'Random queue with ack()' ),
00321             array( 'queueTimestamp', false, 'Time ordered queue without ack()' ),
00322             array( 'queueTimestampTTL', true, 'Time ordered queue with ack()' ),
00323             array( 'queueFifo', false, 'FIFO ordered queue without ack()' ),
00324             array( 'queueFifoTTL', true, 'FIFO ordered queue with ack()' )
00325         );
00326     }
00327 
00328     public static function provider_fifoQueueLists() {
00329         return array(
00330             array( 'queueFifo', false, 'Ordered queue without ack()' ),
00331             array( 'queueFifoTTL', true, 'Ordered queue with ack()' )
00332         );
00333     }
00334 
00335     function newJob( $i = 0, $rootJob = array() ) {
00336         return new NullJob( Title::newMainPage(),
00337             array( 'lives' => 0, 'usleep' => 0, 'removeDuplicates' => 0, 'i' => $i ) + $rootJob );
00338     }
00339 
00340     function newDedupedJob( $i = 0, $rootJob = array() ) {
00341         return new NullJob( Title::newMainPage(),
00342             array( 'lives' => 0, 'usleep' => 0, 'removeDuplicates' => 1, 'i' => $i ) + $rootJob );
00343     }
00344 }