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