-
-
Notifications
You must be signed in to change notification settings - Fork 70
/
ProcessKillRequestsTest.php
145 lines (126 loc) · 4.73 KB
/
ProcessKillRequestsTest.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
<?php
declare(strict_types=1);
namespace EthanYehuda\CronjobManager\Test\Integration;
use EthanYehuda\CronjobManager\Api\Data\ScheduleInterface;
use EthanYehuda\CronjobManager\Api\ScheduleManagementInterface;
use EthanYehuda\CronjobManager\Model\Clock;
use EthanYehuda\CronjobManager\Model\ProcessManagement;
use EthanYehuda\CronjobManager\Test\Util\FakeClock;
use Magento\Cron\Model\Schedule;
use Magento\Framework\Event;
use Magento\Framework\ObjectManager\ObjectManager;
use Magento\TestFramework\Helper\Bootstrap;
use PHPUnit\Framework\TestCase;
/**
* @magentoAppArea crontab
* @magentoAppIsolation enabled
*/
class ProcessKillRequestsTest extends TestCase
{
const NOW = '2019-02-09 18:33:00';
/**
* @var int
*/
private $childPid = 0;
/**
* @var ObjectManager
*/
private $objectManager;
/**
* @var Event\ManagerInterface
*/
private $eventManager;
/**
* @var ScheduleManagementInterface
*/
private $scheduleManagement;
/**
* @var ProcessManagement
*/
private $processManagement;
/**
* @var FakeClock
*/
private $clock;
protected function setUp(): void
{
$this->objectManager = Bootstrap::getObjectManager();
$this->objectManager->configure(['preferences' => [Clock::class => FakeClock::class]]);
$this->clock = $this->objectManager->get(Clock::class);
$this->clock->setTimestamp(strtotime(self::NOW));
$this->eventManager = $this->objectManager->get(Event\ManagerInterface::class);
$this->scheduleManagement = $this->objectManager->get(ScheduleManagementInterface::class);
$this->processManagement = $this->objectManager->get(ProcessManagement::class);
}
protected function tearDown(): void
{
/*
* Take care of children that we failed to kill
*/
if ($this->childPid) {
\posix_kill($this->childPid, SIGKILL);
}
}
public function testDeadRunningJobsAreCleaned()
{
$this->givenRunningScheduleWithKillRequest($schedule, $this->timeStampInThePast());
$this->whenEventIsDispatched('process_cron_queue_before');
$this->thenScheduleHasStatus($schedule, ScheduleInterface::STATUS_KILLED);
$this->andScheduleHasMessage($schedule, 'Process was killed at ' . self::NOW);
$this->andProcessIsKilled($schedule);
}
private function givenRunningScheduleWithKillRequest(&$schedule, int $timestamp)
{
/** @var Schedule $schedule */
$schedule = $this->objectManager->create(Schedule::class);
$schedule->setStatus(Schedule::STATUS_RUNNING);
$schedule->setData('pid', $this->createProcessToKill());
$schedule->save();
$this->scheduleManagement->kill((int)$schedule->getId(), $timestamp);
}
private function whenEventIsDispatched($eventName)
{
$this->eventManager->dispatch($eventName);
}
private function thenScheduleHasStatus(Schedule $schedule, $expectedStatus)
{
/** @var \Magento\Cron\Model\ResourceModel\Schedule $scheduleResource */
$scheduleResource = $this->objectManager->get(\Magento\Cron\Model\ResourceModel\Schedule::class);
$scheduleResource->load($schedule, $schedule->getId());
$this->assertEquals($expectedStatus, $schedule->getStatus(), 'Schedule should have expected status');
}
private function andScheduleHasMessage(Schedule $schedule, $expectedMessage)
{
/** @var \Magento\Cron\Model\ResourceModel\Schedule $scheduleResource */
$scheduleResource = $this->objectManager->get(\Magento\Cron\Model\ResourceModel\Schedule::class);
$scheduleResource->load($schedule, $schedule->getId());
$this->assertEquals($expectedMessage, $schedule->getMessages(), 'Schedule should have expected message');
}
private function timeStampInThePast(): int
{
return $this->clock->now() - 1;
}
private function createProcessToKill(): int
{
$pid = \pcntl_fork();
if ($pid === -1) {
$this->fail('Could not fork process to test killing');
} elseif ($pid) {
$this->assertTrue($this->processManagement->isPidAlive($pid), 'Precondition: child is alive');
$this->childPid = $pid;
return $pid;
} else {
// we are the child, waiting to be killed
while (true) {
sleep(1);
}
}
return 0;
}
private function andProcessIsKilled(Schedule $schedule)
{
\pcntl_wait($status); // killed children are zombies until we wait for them
$pid = (int)$schedule->getData('pid');
$this->assertFalse($this->processManagement->isPidAlive($pid), "Child with PID {$pid} should be killed");
}
}