Skip to content

Commit

Permalink
Add unit test to test preventOverlapping
Browse files Browse the repository at this point in the history
  • Loading branch information
digilist committed Oct 16, 2018
1 parent 438c1ca commit 1b91043
Showing 1 changed file with 111 additions and 0 deletions.
111 changes: 111 additions & 0 deletions tests/Functional/PreventOverlappingTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
<?php

declare(strict_types=1);

namespace Crunz\Tests\Functional;

use Crunz\Configuration\Configuration;
use Crunz\Event;
use Crunz\EventRunner;
use Crunz\HttpClient\HttpClientInterface;
use Crunz\Invoker;
use Crunz\Logger\ConsoleLoggerInterface;
use Crunz\Logger\LoggerFactory;
use Crunz\Mailer;
use Crunz\Schedule;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Output\NullOutput;

final class PreventOverlappingTest extends TestCase
{
public function testPreventOverlapping()
{
$command = PHP_BINARY . ' -r "usleep(250000);"';

$schedule1 = new Schedule();
$event1 = $schedule1->run($command)
->description('Show PHP version')
->preventOverlapping()
->everyMinute();

// Exact same event, might come from another schedule:run
$schedule2 = new Schedule();
$event2 = $schedule2->run($command)
->description('Show PHP version')
->preventOverlapping()
->everyMinute();

$eventRunner = new MyEventRunner(
new Invoker(),
$this->createMock(Configuration::class),
$this->createMock(Mailer::class),
$this->createMock(LoggerFactory::class),
$this->createMock(HttpClientInterface::class),
$this->createMock(ConsoleLoggerInterface::class)
);

// Both events are due, none is locked yet
$this->assertEquals([$event1], $schedule1->dueEvents(new \DateTimeZone(date_default_timezone_get())));
$this->assertEquals([$event2], $schedule2->dueEvents(new \DateTimeZone(date_default_timezone_get())));

// Start schedule, so that event1 will be locked
$eventRunner->handle(new NullOutput(), [$schedule1]);

// Event is locked and therefore not due (even over the boundaries of multiple independent events and schedules)
$this->assertEquals([], $schedule1->dueEvents(new \DateTimeZone(date_default_timezone_get())));
$this->assertEquals([], $schedule2->dueEvents(new \DateTimeZone(date_default_timezone_get())));

// Assert only one process is running
$this->assertTrue($event1->getProcess()->isRunning());
$this->assertNull($event2->getProcess(), 'Event 2 should not be running');

// Assert lock on both events
$this->assertTrue($event1->isLocked());
$this->assertTrue($event2->isLocked());

// Wait until the process finished
while ($event1->isLocked()) {
$eventRunner->manageStartedEvents();
usleep(10000);
}

// Assert both locks were removed
$this->assertFalse($event1->isLocked());
$this->assertFalse($event2->isLocked());
}
}

class MyEventRunner extends EventRunner {

/**
* Manage the running processes.
*
* This is a simplified version that checks whether the processes are still running and returns afterwards.
*
* This adoption is non-blocking / does not wait for all processes to finish, because otherwise we could not test
* the locking in a single threaded manner.
*/
public function manageStartedEvents()
{
foreach ($this->schedules as $scheduleKey => $schedule) {
$events = $schedule->events();

/** @var Event $event */
foreach ($events as $eventKey => $event) {
$proc = $event->getProcess();
if ($proc->isRunning()) {
continue;
}

// Dismiss the event if it's finished
$schedule->dismissEvent($eventKey);
}


if (!\count($schedule->events())) {
$this->invoke($schedule->afterCallbacks());
unset($this->schedules[$scheduleKey]);
}
}
}
}

0 comments on commit 1b91043

Please sign in to comment.