ILIAS  trunk Revision v11.0_alpha-2638-g80c1d007f79
CronJobScheduleTest.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
26 
28 {
29  private static DateTimeImmutable $now;
31 
32  private static function getJob(
33  bool $has_flexible_schedule,
34  JobScheduleType $default_schedule_type,
35  ?int $default_schedule_value,
36  JobScheduleType $schedule_type,
37  ?int $schedule_value
38  ): CronJob {
39  $job_instance = new class (
40  $has_flexible_schedule,
41  $default_schedule_type,
42  $default_schedule_value,
43  $schedule_type,
44  $schedule_value
45  ) extends
46  CronJob {
47  public function __construct(
48  private readonly bool $has_flexible_schedule,
49  private readonly JobScheduleType $default_schedule_type,
50  private readonly ?int $default_schedule_value,
51  JobScheduleType $schedule_type,
52  ?int $schedule_value
53  ) {
54  $this->schedule_type = $schedule_type;
55  $this->schedule_value = $schedule_value;
56  }
57 
58  public function getId(): string
59  {
60  return 'phpunit';
61  }
62 
63  public function getTitle(): string
64  {
65  return 'phpunit';
66  }
67 
68  public function getDescription(): string
69  {
70  return 'phpunit';
71  }
72 
73  public function hasAutoActivation(): bool
74  {
75  return false;
76  }
77 
78  public function hasFlexibleSchedule(): bool
79  {
80  return $this->has_flexible_schedule;
81  }
82 
83  public function getDefaultScheduleType(): JobScheduleType
84  {
85  return $this->default_schedule_type;
86  }
87 
88  public function getDefaultScheduleValue(): ?int
89  {
90  return $this->default_schedule_value;
91  }
92 
93  public function run(): JobResult
94  {
95  return new JobResult();
96  }
97  };
98 
99  $job_instance->setDateTimeProvider(fn(): DateTimeImmutable => self::$now);
100 
101  return $job_instance;
102  }
103 
107  public static function jobProvider(): array
108  {
109  return [
110  'Manual Run is Always Due' => [
111  self::getJob(
112  true,
113  JobScheduleType::DAILY,
114  null,
115  JobScheduleType::DAILY,
116  null
117  ),
118  true,
119  null,
120  JobScheduleType::DAILY,
121  null,
122  true
123  ],
124  'Job Without Any Run is Always Due' => [
125  self::getJob(
126  true,
127  JobScheduleType::DAILY,
128  null,
129  JobScheduleType::DAILY,
130  null
131  ),
132  false,
133  null,
134  JobScheduleType::DAILY,
135  null,
136  true
137  ],
138  'Daily Schedule / Did not run Today' => [
139  self::getJob(
140  true,
141  JobScheduleType::DAILY,
142  null,
143  JobScheduleType::DAILY,
144  null
145  ),
146  false,
147  function (): DateTimeImmutable {
148  self::$now = new DateTimeImmutable('@' . time());
149 
150  return self::$now->modify('-1 day');
151  },
152  JobScheduleType::DAILY,
153  null,
154  true
155  ],
156  'Daily Schedule / Did run Today' => [
157  self::getJob(
158  true,
159  JobScheduleType::DAILY,
160  null,
161  JobScheduleType::DAILY,
162  null
163  ),
164  false,
165  function (): DateTimeImmutable {
166  self::$now = new DateTimeImmutable('@' . time());
167 
168  return self::$now;
169  },
170  JobScheduleType::DAILY,
171  null,
172  false
173  ],
174  'Weekly Schedule / Did not run this Week' => [
175  self::getJob(
176  true,
177  JobScheduleType::WEEKLY,
178  null,
179  JobScheduleType::WEEKLY,
180  null
181  ),
182  false,
183  function (): DateTimeImmutable {
184  self::$now = new DateTimeImmutable('@' . time());
185 
186  return self::$now->modify('-1 week');
187  },
188  JobScheduleType::WEEKLY,
189  null,
190  true
191  ],
192  'Weekly Schedule / Did run this Week' => [
193  self::getJob(
194  true,
195  JobScheduleType::WEEKLY,
196  null,
197  JobScheduleType::WEEKLY,
198  null
199  ),
200  false,
201  function (): DateTimeImmutable {
202  self::$now = new DateTimeImmutable('@' . time());
203 
204  return self::$now->modify('monday this week');
205  },
206  JobScheduleType::WEEKLY,
207  null,
208  false
209  ],
210  'Monthly Schedule / Did not run this Month' => [
211  self::getJob(
212  true,
213  JobScheduleType::MONTHLY,
214  null,
215  JobScheduleType::MONTHLY,
216  null
217  ),
218  false,
219  function (): DateTimeImmutable {
220  self::$now = new DateTimeImmutable('@' . time());
221 
222  return self::$now->modify('last day of last month');
223  },
224  JobScheduleType::MONTHLY,
225  null,
226  true
227  ],
228  'Monthly Schedule / Did run this Month' => [
229  self::getJob(
230  true,
231  JobScheduleType::MONTHLY,
232  null,
233  JobScheduleType::MONTHLY,
234  null
235  ),
236  false,
237  function (): DateTimeImmutable {
238  self::$now = new DateTimeImmutable('@' . time());
239 
240  return self::$now->modify('first day of this month');
241  },
242  JobScheduleType::MONTHLY,
243  null,
244  false
245  ],
246  'Yearly Schedule / Did not run this Year' => [
247  self::getJob(
248  true,
250  null,
252  null
253  ),
254  false,
255  function (): DateTimeImmutable {
256  self::$now = new DateTimeImmutable('@' . time());
257 
258  return self::$now->modify('-1 year');
259  },
261  null,
262  true
263  ],
264  'Yearly Schedule / Did run this Year' => [
265  self::getJob(
266  true,
268  null,
270  null
271  ),
272  false,
273  function (): DateTimeImmutable {
274  self::$now = new DateTimeImmutable('@' . time());
275 
276  return self::$now->modify('first day of January this year');
277  },
279  null,
280  false
281  ],
282  'Quarterly Schedule / Did not run this Quarter' => [
283  self::getJob(
284  true,
285  JobScheduleType::QUARTERLY,
286  null,
287  JobScheduleType::QUARTERLY,
288  null
289  ),
290  false,
291  function (): DateTimeImmutable {
292  self::$now = new DateTimeImmutable('@' . time());
293 
294  $offset = (((int) self::$now->format('n')) - 1) % 3;
295  self::$this_quarter_start = self::$now->modify("first day of -$offset month midnight");
296 
297  return self::$this_quarter_start->modify('-1 seconds');
298  },
299  JobScheduleType::QUARTERLY,
300  null,
301  true
302  ],
303  'Quarterly Schedule / Did run this Quarter' => [
304  self::getJob(
305  true,
306  JobScheduleType::QUARTERLY,
307  null,
308  JobScheduleType::QUARTERLY,
309  null
310  ),
311  false,
312  function (): DateTimeImmutable {
313  self::$now = new DateTimeImmutable('@' . time());
314 
315  $offset = (((int) self::$now->format('n')) - 1) % 3;
316  self::$this_quarter_start = self::$now->modify("first day of -$offset month midnight");
317 
318  return self::$this_quarter_start->modify('+30 seconds');
319  },
320  JobScheduleType::QUARTERLY,
321  null,
322  false
323  ],
324  'Minutely Schedule / Did not run this Minute' => [
325  self::getJob(
326  true,
327  JobScheduleType::IN_MINUTES,
328  1,
329  JobScheduleType::IN_MINUTES,
330  1
331  ),
332  false,
333  function (): DateTimeImmutable {
334  self::$now = new DateTimeImmutable('@' . time());
335 
336  return self::$now->modify('-1 minute');
337  },
338  JobScheduleType::IN_MINUTES,
339  1,
340  true
341  ],
342  'Minutely Schedule / Did run this Minute' => [
343  self::getJob(
344  true,
345  JobScheduleType::IN_MINUTES,
346  1,
347  JobScheduleType::IN_MINUTES,
348  1
349  ),
350  false,
351  function (): DateTimeImmutable {
352  self::$now = new DateTimeImmutable('@' . time());
353 
354  return self::$now->modify('-30 seconds');
355  },
356  JobScheduleType::IN_MINUTES,
357  1,
358  false
359  ],
360  'Hourly Schedule / Did not run this Hour' => [
361  self::getJob(
362  true,
363  JobScheduleType::IN_HOURS,
364  7,
365  JobScheduleType::IN_HOURS,
366  7
367  ),
368  false,
369  function (): DateTimeImmutable {
370  self::$now = new DateTimeImmutable('@' . time());
371 
372  return self::$now->modify('-7 hours');
373  },
374  JobScheduleType::IN_HOURS,
375  7,
376  true
377  ],
378  'Hourly Schedule / Did run this Hour' => [
379  self::getJob(
380  true,
381  JobScheduleType::IN_HOURS,
382  7,
383  JobScheduleType::IN_HOURS,
384  7
385  ),
386  false,
387  function (): DateTimeImmutable {
388  self::$now = new DateTimeImmutable('@' . time());
389 
390  return self::$now->modify('-7 hours +30 seconds');
391  },
392  JobScheduleType::IN_HOURS,
393  7,
394  false
395  ],
396  'Every 5 Days Schedule / Did not run for 5 Days' => [
397  self::getJob(
398  true,
399  JobScheduleType::IN_DAYS,
400  5,
401  JobScheduleType::IN_DAYS,
402  5
403  ),
404  false,
405  function (): DateTimeImmutable {
406  self::$now = new DateTimeImmutable('@' . time());
407 
408  return self::$now->modify('-5 days');
409  },
410  JobScheduleType::IN_DAYS,
411  5,
412  true
413  ],
414  'Every 5 Days Schedule / Did run withing the last 5 Days' => [
415  self::getJob(
416  true,
417  JobScheduleType::IN_DAYS,
418  5,
419  JobScheduleType::IN_DAYS,
420  5
421  ),
422  false,
423  function (): DateTimeImmutable {
424  self::$now = new DateTimeImmutable('@' . time());
425 
426  return self::$now->modify('-4 days');
427  },
428  JobScheduleType::IN_DAYS,
429  5,
430  false
431  ]
432  ];
433  }
434 
438  #[DataProvider('jobProvider')]
439  public function testSchedule(
440  CronJob $job_instance,
441  bool $is_manual_run,
442  ?callable $last_run_datetime_callable,
443  JobScheduleType $schedule_type,
444  ?int $schedule_value,
445  bool $should_be_due
446  ): void {
447  $last_run_datetime = $last_run_datetime_callable ? $last_run_datetime_callable() : null;
448  self::assertEquals(
449  $should_be_due,
450  $job_instance->isDue($last_run_datetime, $schedule_type, $schedule_value, $is_manual_run),
451  'Last run: ' . ($last_run_datetime ? $last_run_datetime->format(DATE_ATOM) : 'never')
452  );
453  }
454 
455  public static function weeklyScheduleProvider(): Generator
456  {
457  yield 'Different Week' => [
458  self::getJob(
459  true,
460  JobScheduleType::WEEKLY,
461  null,
462  JobScheduleType::WEEKLY,
463  null
464  ),
465  function (): DateTimeImmutable {
466  self::$now = new DateTimeImmutable(
467  '@1672570104'
468  ); // Sun Jan 01 2023 10:48:24 GMT+0000 (year: 2023 / week: 52)
469 
470  return self::$now->modify('-1 week'); // Sun Dec 25 2022 10:48:24 GMT+0000 (year: 2022 / week: 51)
471  },
472  true
473  ];
474 
475  yield 'Same Week and Year, but different Month: December (now) and January (Last run)' => [
476  self::getJob(
477  true,
478  JobScheduleType::WEEKLY,
479  null,
480  JobScheduleType::WEEKLY,
481  null
482  ),
483  function (): DateTimeImmutable {
484  self::$now = new DateTimeImmutable(
485  '@1703669703'
486  ); // Wed Dec 27 2023 09:35:03 GMT+0000 (year: 2023 / week: 52 / month: 12)
487 
488  return new DateTimeImmutable(
489  '@1672570104'
490  ); // Sun Jan 01 2023 10:48:24 GMT+0000 (year: 2023 / week: 52 / month: 1)
491  },
492  true
493  ];
494 
495  yield 'Same Week and Year and same Month: January' => [
496  self::getJob(
497  true,
498  JobScheduleType::WEEKLY,
499  null,
500  JobScheduleType::WEEKLY,
501  null
502  ),
503  function (): DateTimeImmutable {
504  self::$now = new DateTimeImmutable(
505  '@1704188103'
506  ); // Tue Jan 02 2024 09:35:03 GMT+0000 (year: 2024 / week: 1 / month: 1)
507 
508  return self::$now->modify(
509  '-1 day'
510  ); // Mon Jan 01 2024 09:35:03 GMT+0000 (year: 2024 / week: 1 / month: 1)
511  },
512  false
513  ];
514 
515  yield 'Same Week (52nd), but Year Difference > 1' => [
516  self::getJob(
517  true,
518  JobScheduleType::WEEKLY,
519  null,
520  JobScheduleType::WEEKLY,
521  null
522  ),
523  function (): DateTimeImmutable {
524  self::$now = new DateTimeImmutable(
525  '@1672570104'
526  ); // Sun Jan 01 2023 10:48:24 GMT+0000 (year: 2023 / week: 52)
527 
528  return self::$now->modify('tuesday this week')->modify(
529  '-1 year'
530  ); // Mon Dec 27 2021 10:48:24 GMT+0000 (year: 2021 / week: 52)
531  },
532  true
533  ];
534 
535  yield 'Same Week (52nd) in different Years, but Turn of the Year' => [
536  self::getJob(
537  true,
538  JobScheduleType::WEEKLY,
539  null,
540  JobScheduleType::WEEKLY,
541  null
542  ),
543  function (): DateTimeImmutable {
544  self::$now = new DateTimeImmutable(
545  '@1672570104'
546  ); // Sun Jan 01 2023 10:48:24 GMT+0000 (year: 2023 / week: 52 / month: 1)
547 
548  return self::$now->modify(
549  'monday this week'
550  ); // Mon Dec 26 2022 10:48:24 GMT+0000 (year: 2022 / week: 52 / month: 12)
551  },
552  false
553  ];
554 
555  yield 'Same Week (52nd) in different Years, but not Turn of the Year' => [
556  self::getJob(
557  true,
558  JobScheduleType::WEEKLY,
559  null,
560  JobScheduleType::WEEKLY,
561  null
562  ),
563  function (): DateTimeImmutable {
564  self::$now = new DateTimeImmutable(
565  '@1703669703'
566  ); // Wed Dec 27 2023 09:35:03 GMT+0000 (year: 2023 / week: 52 / month: 12)
567 
568  return new DateTimeImmutable(
569  '@1672012800'
570  ); // Mon Dec 26 2022 00:00:00 GMT+0000 (year: 2022 / week: 52 / month: 12)
571  },
572  true
573  ];
574  }
575 
579  #[DataProvider('weeklyScheduleProvider')]
580  public function testWeeklySchedules(
581  CronJob $job_instance,
582  callable $last_run_datetime_provider,
583  bool $should_be_due
584  ): void {
585  $last_run_datetime = $last_run_datetime_provider();
586 
587  self::assertSame(
588  $should_be_due,
589  $job_instance->isDue(
590  $last_run_datetime,
591  $job_instance->getScheduleType(),
592  $job_instance->getScheduleValue(),
593  false
594  ),
595  'Last run: ' . $last_run_datetime->format(DATE_ATOM)
596  );
597  }
598 }
getScheduleType()
Get current schedule type (if flexible)
Definition: CronJob.php:156
testSchedule(CronJob $job_instance, bool $is_manual_run, ?callable $last_run_datetime_callable, JobScheduleType $schedule_type, ?int $schedule_value, bool $should_be_due)
isDue(?\DateTimeImmutable $last_run, ?JobScheduleType $schedule_type, ?int $schedule_value, bool $is_manually_executed=false)
Definition: CronJob.php:135
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
static DateTimeImmutable $this_quarter_start
getScheduleValue()
Get current schedule value (if flexible)
Definition: CronJob.php:168
static DateTimeImmutable $now
testWeeklySchedules(CronJob $job_instance, callable $last_run_datetime_provider, bool $should_be_due)
__construct(Container $dic, ilPlugin $plugin)
static getJob(bool $has_flexible_schedule, JobScheduleType $default_schedule_type, ?int $default_schedule_value, JobScheduleType $schedule_type, ?int $schedule_value)