MediaWiki
REL1_24
|
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 }