/**
  * Adds a job to the JobRunner instance.
  *
  * @param JobDefinition  $definition Job definition (e.g. interval).
  * @throws InvalidArgumentException If the given class does not subclass Job.
  * @return void
  */
 public function addJob(JobDefinition $definition)
 {
     $class = $definition->getClassName();
     $reflection = new ReflectionClass($class);
     if (!$reflection->isSubclassOf(Job::class)) {
         $this->logger->error("{$reflection->getShortName()} does not subclass " . Job::class);
         throw new InvalidArgumentException("{$reflection->getShortName()} must subclass " . Job::class);
     }
     if (isset($this->jobs[$class])) {
         $this->logger->warning("{$reflection->getShortName()} is already registered, skipping");
         return;
     }
     // If we have a run_time and interval set, we will ignore the interval when checking if job can run.
     if (!is_null($definition->getInterval()) && !is_null($definition->getRunTime())) {
         $definition->setInterval(null);
         $this->logger->warning("Both run_time and interval are set for {$reflection->getShortName()} — " . "prioritizing run_time");
     }
     // Set internal definitions
     $definition->setLastRunTimeStart(null);
     $definition->setLastRunTimeFinish(null);
     $definition->setReflection($reflection);
     // Add to job list, using defaults where necessary
     $this->jobs[$class] = $definition;
     $this->createJobBuckets($class);
     $this->logger->info("Registered job {$reflection->getShortName()} -- " . ($definition->getEnabled() ? "enabled" : "disabled"));
 }
 /**
  * Tests adding a job class.
  * @return void
  */
 public function testAddJob()
 {
     $mock = m::mock('fork_daemon');
     $jr = new JobRunner($mock);
     // Make sure buckets are created correctly for the job
     $mock->shouldReceive('add_bucket')->with(JobStub::class)->once();
     $mock->shouldReceive('max_children_set')->with(1, JobStub::class)->once();
     $mock->shouldReceive('register_child_run')->with([$jr, 'processWork'], JobStub::class)->once();
     $mock->shouldReceive('register_parent_child_exit')->with([$jr, 'parentChildExit'], JobStub::class)->once();
     $mock->shouldReceive('child_max_run_time_set')->with(172800, JobStub::class)->once();
     // Check Job added correctly
     $jr->addJob(new JobDefinition(JobStub::class));
     $jobs = $jr->getJobs();
     $this->assertArrayHasKey(JobStub::class, $jobs);
     $this->assertCount(1, $jobs);
     // Check job default definitions
     $job = $jobs[JobStub::class];
     $this->assertInstanceOf(JobDefinition::class, $job);
     // Check enabled by default, and ReflectionClass saved
     $this->assertTrue($job->getEnabled());
     $this->assertInstanceOf(ReflectionClass::class, $job->getReflection());
     // Adding it twice should have no effect
     $jr->addJob(new JobDefinition(JobStub::class));
     $this->assertArrayHasKey(JobStub::class, $jobs);
     $this->assertCount(1, $jobs);
     // Try again, setting some definition values
     $jr = new JobRunner();
     $jd = new JobDefinition(JobStub::class, false, null, 3600);
     // should not be retained
     $jd->setLastRunTimeStart(time());
     $jd->setLastRunTimeFinish(time());
     $jr->addJob($jd);
     $job = $jr->getJobs()[JobStub::class];
     $this->assertInstanceOf(JobDefinition::class, $job);
     $this->assertFalse($job->getEnabled());
     $this->assertEquals(3600, $job->getInterval());
     // Should not retain given values
     $this->assertEmpty($job->getLastRunTimeStart());
     $this->assertEmpty($job->getLastRunTimeFinish());
     $this->assertInstanceOf(ReflectionClass::class, $job->getReflection());
     // Add a job with a run_time and interval set to see that interval is ignored and set back to null
     $definition = new JobDefinition(JobStub::class, true, '12:00', 5);
     $jr = new JobRunner();
     $jr->addJob($definition);
     $updated_jd = $jr->getJob($definition->getClassName());
     $this->assertNull($updated_jd->getInterval());
     // Test adding a class extending JobDefinition
     $jr = new JobRunner();
     $jr->addJob(new ExtendingJobDefinition(JobStub::class));
     $this->assertInstanceOf(JobDefinition::class, $jr->getJob(JobStub::class));
     // Adding a non-Job class should throw an exception
     $this->setExpectedException(Exception::class);
     $jr = new JobRunner();
     $jr->addJob(new JobDefinition(stdClass::class));
 }