Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
| 1 | <?php |
||
| 14 | abstract class BaseJobManagerTest extends TestCase |
||
| 15 | { |
||
| 16 | const PERFORMANCE_TOTAL_JOBS = 100; |
||
| 17 | |||
| 18 | /** @var Worker */ |
||
| 19 | public static $worker; |
||
| 20 | |||
| 21 | /** @var string */ |
||
| 22 | public static $jobClass; |
||
| 23 | |||
| 24 | /** @var JobManagerInterface */ |
||
| 25 | public static $jobManager; |
||
| 26 | |||
| 27 | /** @var RunManager */ |
||
| 28 | public static $runManager; |
||
| 29 | |||
| 30 | /** @var JobTimingManager */ |
||
| 31 | public static $jobTimingManager; |
||
| 32 | |||
| 33 | public static function setUpBeforeClass() |
||
| 34 | { |
||
| 35 | self::$jobClass = self::$jobManager->getJobClass(); |
||
| 36 | self::$worker->setJobManager(self::$jobManager); |
||
| 37 | } |
||
| 38 | |||
| 39 | protected function drain() |
||
| 40 | { |
||
| 41 | $limit = 10000; |
||
| 42 | while ($limit && $job = self::$jobManager->getJob()) { |
||
| 43 | --$limit; |
||
| 44 | self::$jobManager->saveHistory($job); |
||
| 45 | } |
||
| 46 | self::assertGreaterThan(0, $limit); |
||
| 47 | } |
||
| 48 | |||
| 49 | public function testSaveJob() |
||
| 50 | { |
||
| 51 | $job = $this->getJob(); |
||
| 52 | $jobInQueue = self::$jobManager->getJob(); |
||
| 53 | self::assertNotNull($jobInQueue, 'There should be a job.'); |
||
| 54 | self::assertEquals( |
||
| 55 | $job->getId(), |
||
| 56 | $jobInQueue->getId(), |
||
| 57 | 'Job id returned by manager should be the same' |
||
| 58 | ); |
||
| 59 | self::$jobManager->deleteJob($job); |
||
| 60 | } |
||
| 61 | |||
| 62 | protected function getJob() |
||
| 63 | { |
||
| 64 | $job = new self::$jobClass(self::$worker, false, null); |
||
| 65 | $job->fibonacci(1); |
||
| 66 | self::assertNotNull($job->getId(), 'Job id should be generated'); |
||
| 67 | |||
| 68 | return $job; |
||
| 69 | } |
||
| 70 | |||
| 71 | public function testGetJobByWorker() |
||
| 72 | { |
||
| 73 | $job = $this->getJob(); |
||
| 74 | $jobInQueue = self::$jobManager->getJob(self::$worker->getName()); |
||
| 75 | self::assertEquals( |
||
| 76 | $job->getId(), |
||
| 77 | $jobInQueue->getId(), |
||
| 78 | 'Job id returned by manager should be the same' |
||
| 79 | ); |
||
| 80 | } |
||
| 81 | |||
| 82 | public function testDeleteJob() |
||
| 83 | { |
||
| 84 | $this->drain(); |
||
| 85 | |||
| 86 | $job = $this->getJob(); |
||
| 87 | self::$jobManager->deleteJob($job); |
||
| 88 | |||
| 89 | $nextJob = self::$jobManager->getJob(); |
||
| 90 | self::assertNull($nextJob, "Shouldn't be any jobs left in queue"); |
||
| 91 | |||
| 92 | return $job; |
||
| 93 | } |
||
| 94 | |||
| 95 | /** |
||
| 96 | * Not all managers will support job priority. |
||
| 97 | */ |
||
| 98 | public function testJobPriority() |
||
| 99 | { |
||
| 100 | $firstJob = new self::$jobClass(self::$worker, false, 12); |
||
| 101 | $firstJob->fibonacci(1); |
||
| 102 | self::assertNotNull($firstJob->getId(), 'Job id should be generated'); |
||
| 103 | |||
| 104 | $secondJob = new self::$jobClass(self::$worker, false, 1); |
||
| 105 | $secondJob->fibonacci(1); |
||
| 106 | self::assertNotNull($secondJob->getId(), 'Job id should be generated'); |
||
| 107 | |||
| 108 | $thirdJob = new self::$jobClass(self::$worker, false, 5); |
||
| 109 | $thirdJob->fibonacci(1); |
||
| 110 | self::assertNotNull($thirdJob->getId(), 'Job id should be generated'); |
||
| 111 | |||
| 112 | if (self::$jobManager instanceof PriorityJobManager && null !== self::$jobManager->getMaxPriority()) { |
||
| 113 | $jobInQueue = self::$jobManager->getJob(); |
||
| 114 | self::assertNotNull($jobInQueue, 'There should be a job.'); |
||
| 115 | self::assertEquals( |
||
| 116 | $secondJob->getId(), |
||
| 117 | $jobInQueue->getId(), |
||
| 118 | 'Second job id should be returned - lower number unload first' |
||
| 119 | ); |
||
| 120 | |||
| 121 | $jobInQueue = self::$jobManager->getJob(); |
||
| 122 | self::assertEquals( |
||
| 123 | $thirdJob->getId(), |
||
| 124 | $jobInQueue->getId(), |
||
| 125 | 'Third job id should be returned - lower number unload first' |
||
| 126 | ); |
||
| 127 | |||
| 128 | $jobInQueue = self::$jobManager->getJob(); |
||
| 129 | self::assertEquals( |
||
| 130 | $firstJob->getId(), |
||
| 131 | $jobInQueue->getId(), |
||
| 132 | 'First job id should be returned - lower number unload first' |
||
| 133 | ); |
||
| 134 | } |
||
| 135 | } |
||
| 136 | |||
| 137 | public function testResetExceptionJobs() |
||
| 138 | { |
||
| 139 | $this->expectingException('resetExceptionJobs'); |
||
| 140 | } |
||
| 141 | |||
| 142 | public function testPruneExceptionJobs() |
||
| 143 | { |
||
| 144 | $this->expectingException('pruneExceptionJobs'); |
||
| 145 | } |
||
| 146 | |||
| 147 | public function testPruneExpiredJobs() |
||
| 148 | { |
||
| 149 | $this->expectingException('pruneExpiredJobs'); |
||
| 150 | } |
||
| 151 | |||
| 152 | public function testGetStatus() |
||
| 153 | { |
||
| 154 | $count = self::$jobManager->getWaitingJobCount(); |
||
| 155 | $status = self::$jobManager->getStatus(); |
||
| 156 | |||
| 157 | self::assertEquals($count, $status['all'][BaseJob::STATUS_NEW]); |
||
| 158 | } |
||
| 159 | |||
| 160 | public function testPruneArchivedJobs() |
||
| 161 | { |
||
| 162 | $failed = false; |
||
| 163 | try { |
||
| 164 | $time = time() - 86400; |
||
| 165 | self::$jobManager->pruneArchivedJobs(new \DateTime("@$time")); |
||
| 166 | $failed = true; |
||
| 167 | } catch (\Exception $exception) { |
||
| 168 | self::assertTrue(true); |
||
| 169 | } |
||
| 170 | self::assertFalse($failed); |
||
| 171 | } |
||
| 172 | |||
| 173 | /** |
||
| 174 | * @param string $method |
||
| 175 | */ |
||
| 176 | protected function expectingException($method) |
||
| 177 | { |
||
| 178 | $failed = false; |
||
| 179 | try { |
||
| 180 | self::$jobManager->$method(); |
||
| 181 | $failed = true; |
||
| 182 | } catch (\Exception $exception) { |
||
| 183 | self::assertTrue(true); |
||
| 184 | } |
||
| 185 | self::assertFalse($failed); |
||
| 186 | } |
||
| 187 | |||
| 188 | public function performanceEnqueue() |
||
| 189 | { |
||
| 190 | $jobsTotal = static::PERFORMANCE_TOTAL_JOBS; |
||
| 191 | $start = microtime(true); |
||
| 192 | for ($i = 0; $i < $jobsTotal; ++$i) { |
||
| 193 | self::$worker->later()->fibonacci(1); |
||
| 194 | } |
||
| 195 | |||
| 196 | $total = microtime(true) - $start; |
||
| 197 | echo "\nTotal of {$jobsTotal} jobs enqueued in {$total} seconds\n"; |
||
| 198 | |||
| 199 | try { |
||
| 200 | // Have to add a sleep for RabbitMQ to catch up - |
||
| 201 | $count = self::getWaitingJobCount($jobsTotal); |
||
| 202 | self::assertEquals($jobsTotal, $count); |
||
| 203 | } catch (\Exception $e) { |
||
| 204 | if ('Unsupported' !== $e->getMessage()) { |
||
| 205 | throw $e; |
||
| 206 | } |
||
| 207 | } |
||
| 208 | } |
||
| 209 | |||
| 210 | protected static function fudgeRabbitMQCount($count, $expected) |
||
| 211 | { |
||
| 212 | if ($expected !== $count) { |
||
| 213 | sleep(1); |
||
| 214 | $count = self::$jobManager->getWaitingJobCount(); |
||
| 215 | if ($expected !== $count && $expected > 10) { |
||
| 216 | if ($expected >= $count - 10) { // 'fudge factor for RabbitMQ' |
||
| 217 | return $expected; |
||
| 218 | } |
||
| 219 | } |
||
| 220 | } |
||
| 221 | |||
| 222 | return $count; |
||
| 223 | } |
||
| 224 | |||
| 225 | protected static function getWaitingJobCount($expected) |
||
| 226 | { |
||
| 227 | $count = self::$jobManager->getWaitingJobCount(); |
||
| 228 | if (!self::$jobManager instanceof JobManager) { |
||
| 229 | return $count; |
||
| 230 | } |
||
| 231 | |||
| 232 | return self::fudgeRabbitMQCount($count, $expected); |
||
| 233 | } |
||
| 234 | |||
| 235 | /** |
||
| 236 | * @outputBuffering disabled |
||
| 237 | */ |
||
| 238 | public function testPerformance() |
||
| 239 | { |
||
| 240 | echo "\n".static::class.": Testing Performance\n"; |
||
| 241 | flush(); |
||
| 242 | $reflection = new \ReflectionObject(self::$jobManager); |
||
| 243 | if ($reflection->hasProperty('enableSorting')) { |
||
| 244 | $oldEnableSorting = self::$jobManager->enableSorting; |
||
| 245 | self::$jobManager->enableSorting = false; // Ignore priority |
||
| 246 | } |
||
| 247 | |||
| 248 | // Dequeue all outstanding jobs |
||
| 249 | $this->drain(); |
||
| 250 | |||
| 251 | $this->performanceEnqueue(); |
||
| 252 | $this->performanceDequeue(); |
||
| 253 | |||
| 254 | if ($reflection->hasProperty('enableSorting')) { |
||
| 255 | self::$jobManager->enableSorting = $oldEnableSorting; // Ignore priority |
||
| 256 | } |
||
| 257 | } |
||
| 258 | |||
| 259 | public function performanceDequeue() |
||
| 260 | { |
||
| 261 | $jobsTotal = static::PERFORMANCE_TOTAL_JOBS; |
||
| 262 | $start = microtime(true); |
||
| 263 | $job = null; |
||
| 264 | for ($i = 0; $i < $jobsTotal; ++$i) { |
||
| 265 | $job = self::$jobManager->getJob(); |
||
| 266 | } |
||
| 267 | $total = microtime(true) - $start; |
||
| 268 | echo "Total of {$jobsTotal} jobs dequeued in {$total} seconds\n"; |
||
| 269 | self::assertNotNull($job, 'The last job in queue is null'); |
||
| 270 | |||
| 271 | $nextJob = self::$jobManager->getJob(); |
||
| 272 | self::assertNull($nextJob, "Shouldn't be any jobs left in queue"); |
||
| 273 | } |
||
| 274 | } |
||
| 275 |