CallbackSchedulingTest.php 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. <?php
  2. namespace Illuminate\Tests\Integration\Console;
  3. use Illuminate\Cache\ArrayStore;
  4. use Illuminate\Cache\Repository;
  5. use Illuminate\Console\Events\ScheduledTaskFailed;
  6. use Illuminate\Console\Scheduling\CacheEventMutex;
  7. use Illuminate\Console\Scheduling\CacheSchedulingMutex;
  8. use Illuminate\Console\Scheduling\EventMutex;
  9. use Illuminate\Console\Scheduling\Schedule;
  10. use Illuminate\Console\Scheduling\SchedulingMutex;
  11. use Illuminate\Container\Container;
  12. use Illuminate\Contracts\Cache\Factory;
  13. use Illuminate\Contracts\Events\Dispatcher;
  14. use Orchestra\Testbench\TestCase;
  15. use RuntimeException;
  16. class CallbackSchedulingTest extends TestCase
  17. {
  18. protected $log = [];
  19. protected function setUp(): void
  20. {
  21. parent::setUp();
  22. $cache = new class implements Factory
  23. {
  24. public $store;
  25. public function __construct()
  26. {
  27. $this->store = new Repository(new ArrayStore(true));
  28. }
  29. public function store($name = null)
  30. {
  31. return $this->store;
  32. }
  33. };
  34. $container = Container::getInstance();
  35. $container->instance(EventMutex::class, new CacheEventMutex($cache));
  36. $container->instance(SchedulingMutex::class, new CacheSchedulingMutex($cache));
  37. }
  38. protected function tearDown(): void
  39. {
  40. Container::setInstance(null);
  41. parent::tearDown();
  42. }
  43. /**
  44. * @dataProvider executionProvider
  45. */
  46. public function testExecutionOrder($background)
  47. {
  48. $event = $this->app->make(Schedule::class)
  49. ->call($this->logger('call'))
  50. ->after($this->logger('after 1'))
  51. ->before($this->logger('before 1'))
  52. ->after($this->logger('after 2'))
  53. ->before($this->logger('before 2'));
  54. if ($background) {
  55. $event->runInBackground();
  56. }
  57. $this->artisan('schedule:run');
  58. $this->assertLogged('before 1', 'before 2', 'call', 'after 1', 'after 2');
  59. }
  60. public function testExceptionHandlingInCallback()
  61. {
  62. $event = $this->app->make(Schedule::class)
  63. ->call($this->logger('call'))
  64. ->name('test-event')
  65. ->withoutOverlapping();
  66. // Set up "before" and "after" hooks to ensure they're called
  67. $event->before($this->logger('before'))->after($this->logger('after'));
  68. // Register a hook to validate that the mutex was initially created
  69. $mutexWasCreated = false;
  70. $event->before(function () use (&$mutexWasCreated, $event) {
  71. $mutexWasCreated = $event->mutex->exists($event);
  72. });
  73. // We'll trigger an exception in an "after" hook to test exception handling
  74. $event->after(function () {
  75. throw new RuntimeException;
  76. });
  77. // Because exceptions are caught by the ScheduleRunCommand, we need to listen for
  78. // the "failed" event to check whether our exception was actually thrown
  79. $failed = false;
  80. $this->app->make(Dispatcher::class)
  81. ->listen(ScheduledTaskFailed::class, function (ScheduledTaskFailed $failure) use (&$failed, $event) {
  82. if ($failure->task === $event) {
  83. $failed = true;
  84. }
  85. });
  86. $this->artisan('schedule:run');
  87. // Hooks and execution should happn in correct order
  88. $this->assertLogged('before', 'call', 'after');
  89. // Our exception should have resulted in a failure event
  90. $this->assertTrue($failed);
  91. // Validate that the mutex was originally created, but that it's since
  92. // been removed (even though an exception was thrown)
  93. $this->assertTrue($mutexWasCreated);
  94. $this->assertFalse($event->mutex->exists($event));
  95. }
  96. public function executionProvider()
  97. {
  98. return [
  99. 'Foreground' => [false],
  100. 'Background' => [true],
  101. ];
  102. }
  103. protected function logger($message)
  104. {
  105. return function () use ($message) {
  106. $this->log[] = $message;
  107. };
  108. }
  109. protected function assertLogged(...$message)
  110. {
  111. $this->assertEquals($message, $this->log);
  112. }
  113. }