[!!!][TASK] Extract testing framework for TYPO3
[Packages/TYPO3.CMS.git] / typo3 / sysext / scheduler / Tests / Unit / CronCommand / CronCommandTest.php
1 <?php
2 namespace TYPO3\CMS\Scheduler\Tests\Unit\CronCommand;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Scheduler\CronCommand\CronCommand;
18
19 /**
20 * Test case
21 */
22 class CronCommandTest extends \TYPO3\CMS\Components\TestingFramework\Core\UnitTestCase
23 {
24 /**
25 * @const integer timestamp of 1.1.2010 0:00 (Friday), timezone UTC/GMT
26 */
27 const TIMESTAMP = 1262304000;
28
29 /**
30 * @var string Selected timezone backup
31 */
32 protected $timezoneBackup = '';
33
34 /**
35 * We're fiddling with hard timestamps in the tests, but time methods in
36 * the system under test do use timezone settings. Therefore we backup the
37 * current timezone setting, set it to UTC explicitly and reconstitute it
38 * again in tearDown()
39 */
40 protected function setUp()
41 {
42 $this->timezoneBackup = date_default_timezone_get();
43 date_default_timezone_set('UTC');
44 }
45
46 protected function tearDown()
47 {
48 date_default_timezone_set($this->timezoneBackup);
49 parent::tearDown();
50 }
51
52 /**
53 * @test
54 */
55 public function constructorSetsNormalizedCronCommandSections()
56 {
57 $instance = new CronCommand('2-3 * * * *');
58 $this->assertSame(['2,3', '*', '*', '*', '*'], $instance->getCronCommandSections());
59 }
60
61 /**
62 * @test
63 */
64 public function constructorThrowsExceptionForInvalidCronCommand()
65 {
66 $this->expectException(\InvalidArgumentException::class);
67 $this->expectExceptionCode(1291470170);
68 new CronCommand('61 * * * *');
69 }
70
71 /**
72 * @test
73 */
74 public function constructorSetsTimestampToNowPlusOneMinuteRoundedDownToSixtySeconds()
75 {
76 $instance = new CronCommand('* * * * *');
77 $currentTime = time();
78 $expectedTime = $currentTime - ($currentTime % 60) + 60;
79 $this->assertSame($expectedTime, $instance->getTimestamp());
80 }
81
82 /**
83 * @test
84 */
85 public function constructorSetsTimestampToGivenTimestampPlusSixtySeconds()
86 {
87 $instance = new CronCommand('* * * * *', self::TIMESTAMP);
88 $this->assertSame(self::TIMESTAMP + 60, $instance->getTimestamp());
89 }
90
91 /**
92 * @test
93 */
94 public function constructorSetsTimestampToGiveTimestampRoundedDownToSixtySeconds()
95 {
96 $instance = new CronCommand('* * * * *', self::TIMESTAMP + 1);
97 $this->assertSame(self::TIMESTAMP + 60, $instance->getTimestamp());
98 }
99
100 /**
101 * @return array
102 */
103 public static function expectedTimestampDataProvider()
104 {
105 return [
106 'every minute' => [
107 '* * * * *',
108 self::TIMESTAMP,
109 self::TIMESTAMP + 60,
110 self::TIMESTAMP + 120
111 ],
112 'once an hour at 1' => [
113 '1 * * * *',
114 self::TIMESTAMP,
115 self::TIMESTAMP + 60,
116 self::TIMESTAMP + 60 + 60 * 60
117 ],
118 'once an hour at 0' => [
119 '0 * * * *',
120 self::TIMESTAMP,
121 self::TIMESTAMP + 60 * 60,
122 self::TIMESTAMP + 60 * 60 + 60 * 60
123 ],
124 'once a day at 1:00' => [
125 '0 1 * * *',
126 self::TIMESTAMP,
127 self::TIMESTAMP + 60 * 60,
128 self::TIMESTAMP + 60 * 60 + 60 * 60 * 24
129 ],
130 'once a day at 0:00' => [
131 '0 0 * * *',
132 self::TIMESTAMP,
133 self::TIMESTAMP + 60 * 60 * 24,
134 self::TIMESTAMP + 60 * 60 * 24 * 2
135 ],
136 'once a month' => [
137 '0 0 4 * *',
138 self::TIMESTAMP,
139 self::TIMESTAMP + 60 * 60 * 24 * 3,
140 self::TIMESTAMP + 60 * 60 * 24 * 3 + 60 * 60 * 24 * 31
141 ],
142 'once every Saturday' => [
143 '0 0 * * sat',
144 self::TIMESTAMP,
145 self::TIMESTAMP + 60 * 60 * 24,
146 self::TIMESTAMP + 60 * 60 * 24 + 60 * 60 * 24 * 7
147 ],
148 'once every day in February' => [
149 '0 0 * feb *',
150 self::TIMESTAMP,
151 self::TIMESTAMP + 60 * 60 * 24 * 31,
152 self::TIMESTAMP + 60 * 60 * 24 * 31 + 60 * 60 * 24
153 ],
154 'day of week and day of month restricted, next match in day of month field' => [
155 '0 0 2 * sun',
156 self::TIMESTAMP,
157 self::TIMESTAMP + 60 * 60 * 24,
158 self::TIMESTAMP + 60 * 60 * 24 + 60 * 60 * 24
159 ],
160 'day of week and day of month restricted, next match in day of week field' => [
161 '0 0 3 * sat',
162 self::TIMESTAMP,
163 self::TIMESTAMP + 60 * 60 * 24,
164 self::TIMESTAMP + 60 * 60 * 24 + 60 * 60 * 24
165 ],
166 'list of minutes' => [
167 '2,4 * * * *',
168 self::TIMESTAMP,
169 self::TIMESTAMP + 120,
170 self::TIMESTAMP + 240
171 ],
172 'list of hours' => [
173 '0 2,4 * * *',
174 self::TIMESTAMP,
175 self::TIMESTAMP + 60 * 60 * 2,
176 self::TIMESTAMP + 60 * 60 * 4
177 ],
178 ];
179 }
180
181 /**
182 * @return array
183 */
184 public static function expectedCalculatedTimestampDataProvider()
185 {
186 return [
187 'every first day of month' => [
188 '0 0 1 * *',
189 self::TIMESTAMP,
190 '01-02-2010',
191 '01-03-2010',
192 ],
193 'once every February' => [
194 '0 0 1 feb *',
195 self::TIMESTAMP,
196 '01-02-2010',
197 '01-02-2011',
198 ],
199 'once every Friday February' => [
200 '0 0 * feb fri',
201 self::TIMESTAMP,
202 '05-02-2010',
203 '12-02-2010',
204 ],
205 'first day in February and every Friday' => [
206 '0 0 1 feb fri',
207 self::TIMESTAMP,
208 '01-02-2010',
209 '05-02-2010',
210 ],
211 '29th February leap year' => [
212 '0 0 29 feb *',
213 self::TIMESTAMP,
214 '29-02-2012',
215 '29-02-2016',
216 ],
217 'list of days in month' => [
218 '0 0 2,4 * *',
219 self::TIMESTAMP,
220 '02-01-2010',
221 '04-01-2010',
222 ],
223 'list of month' => [
224 '0 0 1 2,3 *',
225 self::TIMESTAMP,
226 '01-02-2010',
227 '01-03-2010',
228 ],
229 'list of days of weeks' => [
230 '0 0 * * 2,4',
231 self::TIMESTAMP,
232 '05-01-2010',
233 '07-01-2010',
234 ]
235 ];
236 }
237
238 /**
239 * @test
240 * @dataProvider expectedTimestampDataProvider
241 * @param string $cronCommand Cron command
242 * @param int $startTimestamp Timestamp for start of calculation
243 * @param int $expectedTimestamp Expected result (next time of execution)
244 */
245 public function calculateNextValueDeterminesCorrectNextTimestamp($cronCommand, $startTimestamp, $expectedTimestamp)
246 {
247 $instance = new CronCommand($cronCommand, $startTimestamp);
248 $instance->calculateNextValue();
249 $this->assertSame($expectedTimestamp, $instance->getTimestamp());
250 }
251
252 /**
253 * @test
254 * @dataProvider expectedCalculatedTimestampDataProvider
255 * @param string $cronCommand Cron command
256 * @param int $startTimestamp Timestamp for start of calculation
257 * @param string $expectedTimestamp Expected result (next time of execution), to be feeded to strtotime
258 */
259 public function calculateNextValueDeterminesCorrectNextCalculatedTimestamp($cronCommand, $startTimestamp, $expectedTimestamp)
260 {
261 $instance = new CronCommand($cronCommand, $startTimestamp);
262 $instance->calculateNextValue();
263 $this->assertSame(strtotime($expectedTimestamp), $instance->getTimestamp());
264 }
265
266 /**
267 * @test
268 * @dataProvider expectedTimestampDataProvider
269 * @param string $cronCommand Cron command
270 * @param int $startTimestamp [unused] Timestamp for start of calculation
271 * @param int $firstTimestamp Timestamp of the next execution
272 * @param int $secondTimestamp Timestamp of the further execution
273 */
274 public function calculateNextValueDeterminesCorrectNextTimestampOnConsecutiveCall($cronCommand, $startTimestamp, $firstTimestamp, $secondTimestamp)
275 {
276 $instance = new CronCommand($cronCommand, $firstTimestamp);
277 $instance->calculateNextValue();
278 $this->assertSame($secondTimestamp, $instance->getTimestamp());
279 }
280
281 /**
282 * @test
283 * @dataProvider expectedCalculatedTimestampDataProvider
284 * @param string $cronCommand Cron command
285 * @param int $startTimestamp [unused] Timestamp for start of calculation
286 * @param string $firstTimestamp Timestamp of the next execution, to be fed to strtotime
287 * @param string $secondTimestamp Timestamp of the further execution, to be fed to strtotime
288 */
289 public function calculateNextValueDeterminesCorrectNextCalculatedTimestampOnConsecutiveCall($cronCommand, $startTimestamp, $firstTimestamp, $secondTimestamp)
290 {
291 $instance = new CronCommand($cronCommand, strtotime($firstTimestamp));
292 $instance->calculateNextValue();
293 $this->assertSame(strtotime($secondTimestamp), $instance->getTimestamp());
294 }
295
296 /**
297 * @test
298 */
299 public function calculateNextValueDeterminesCorrectNextTimestampOnChangeToSummertime()
300 {
301 $backupTimezone = date_default_timezone_get();
302 date_default_timezone_set('Europe/Berlin');
303 $instance = new CronCommand('* 3 28 mar *', self::TIMESTAMP);
304 $instance->calculateNextValue();
305 date_default_timezone_set($backupTimezone);
306 $this->assertSame(1269741600, $instance->getTimestamp());
307 }
308
309 /**
310 * @test
311 */
312 public function calculateNextValueThrowsExceptionWithImpossibleCronCommand()
313 {
314 $this->expectException(\RuntimeException::class);
315 $this->expectExceptionCode(1291501280);
316 $instance = new CronCommand('* * 31 apr *', self::TIMESTAMP);
317 $instance->calculateNextValue();
318 }
319
320 /**
321 * @test
322 */
323 public function getTimestampReturnsInteger()
324 {
325 $instance = new CronCommand('* * * * *');
326 $this->assertInternalType(\PHPUnit_Framework_Constraint_IsType::TYPE_INT, $instance->getTimestamp());
327 }
328
329 /**
330 * @test
331 */
332 public function getCronCommandSectionsReturnsArray()
333 {
334 $instance = new CronCommand('* * * * *');
335 $this->assertInternalType(\PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $instance->getCronCommandSections());
336 }
337 }