format: fix render_bytes with CLIFormatter
[pve-common.git] / test / calendar_event_test.pl
1 #!/usr/bin/perl
2
3 use lib '../src';
4 use strict;
5 use warnings;
6 use POSIX ();
7 use Data::Dumper;
8 use Time::Local;
9 use Test::More;
10
11 use PVE::CalendarEvent;
12
13 # Time tests should run in a controlled setting
14 $ENV{TZ} = 'UTC';
15 POSIX::tzset();
16
17 my $alldays = [0,1,2,3,4,5,6];
18 my $tests = [
19 [
20 '*',
21 { h => '*', m => '*', dow => $alldays },
22 [
23 [0, 60],
24 [30, 60],
25 [59, 60],
26 [60, 120],
27 ]
28 ],
29 [
30 '*/10',
31 { h => '*', m => [0, 10, 20, 30, 40, 50], dow => $alldays },
32 [
33 [0, 600],
34 [599, 600],
35 [600, 1200],
36 [50*60, 60*60]
37 ]
38 ],
39 [
40 '*/12:0' ,
41 { h => [0, 12], m => [0], dow => $alldays },
42 [
43 [ 10, 43200],
44 [ 13*3600, 24*3600],
45 ]
46 ],
47 [
48 '1/12:0/15' ,
49 { h => [1, 13], m => [0, 15, 30, 45], dow => $alldays },
50 [
51 [0, 3600],
52 [3600, 3600+15*60],
53 [3600+16*60, 3600+30*60 ],
54 [3600+30*60, 3600+45*60 ],
55 [3600+45*60, 3600+12*3600],
56 [13*3600 + 1, 13*3600+15*60],
57 [13*3600 + 15*60, 13*3600+30*60],
58 [13*3600 + 30*60, 13*3600+45*60],
59 [13*3600 + 45*60, 25*3600],
60 ],
61 ],
62 [
63 '1,4,6',
64 { h => '*', m => [1, 4, 6], dow => $alldays},
65 [
66 [0, 60],
67 [60, 4*60],
68 [4*60+60, 6*60],
69 [6*60, 3600+60],
70 ]
71 ],
72 [
73 '0..3',
74 { h => '*', m => [ 0, 1, 2, 3 ], dow => $alldays },
75 ],
76 [
77 '23..23:0..3',
78 { h => [ 23 ], m => [ 0, 1, 2, 3 ], dow => $alldays },
79 ],
80 [
81 'Mon',
82 { h => [0], m => [0], dow => [1] },
83 [
84 [0, 4*86400], # Note: Epoch 0 is Thursday, 1. January 1970
85 [4*86400, 11*86400],
86 [11*86400, 18*86400],
87 ],
88 ],
89 [
90 'sat..sun',
91 { h => [0], m => [0], dow => [0, 6] },
92 [
93 [0, 2*86400],
94 [2*86400, 3*86400],
95 [3*86400, 9*86400],
96 ]
97 ],
98 [
99 'sun..sat',
100 { h => [0], m => [0], dow => $alldays },
101 ],
102 [
103 'Fri..Mon',
104 { error => "wrong order in range 'Fri..Mon'" },
105 ],
106 [
107 'wed,mon..tue,fri',
108 { h => [0], m => [0], dow => [ 1, 2, 3, 5] },
109 ],
110 [
111 'mon */15',
112 { h => '*', m => [0, 15, 30, 45], dow => [1]},
113 ],
114 [
115 '22/1:0',
116 { h => [22, 23], m => [0], dow => $alldays },
117 [
118 [0, 22*60*60],
119 [22*60*60, 23*60*60],
120 [22*60*60 + 59*60, 23*60*60]
121 ],
122 ],
123 [
124 '*/2:*',
125 { h => [0,2,4,6,8,10,12,14,16,18,20,22], m => '*', dow => $alldays },
126 [
127 [0, 60],
128 [60*60, 2*60*60],
129 [2*60*60, 2*60*60 + 60]
130 ]
131 ],
132 [
133 '20..22:*/30',
134 { h => [20,21,22], m => [0,30], dow => $alldays },
135 [
136 [0, 20*60*60],
137 [20*60*60, 20*60*60 + 30*60],
138 [22*60*60 + 30*60, 44*60*60]
139 ]
140 ],
141 [
142 '61',
143 { error => "value '61' out of range" },
144 ],
145 [
146 '*/61',
147 { error => "repetition '61' out of range" },
148 ],
149 [
150 '0..80',
151 { error => "range end '80' out of range" },
152 ],
153 [
154 ' mon 0 0 0',
155 { error => "unable to parse calendar event - unused parts" },
156 ],
157 [
158 '',
159 { error => "unable to parse calendar event - event is empty" },
160 ],
161 [
162 ' mon 0 0',
163 { error => "unable to parse calendar event - unused parts" },
164 ],
165 [
166 '0,1,3..5',
167 { h => '*', m => [0,1,3,4,5], dow => $alldays },
168 [
169 [0, 60],
170 [60, 3*60],
171 [5*60, 60*60]
172 ]
173 ],
174 [
175 '2,4:0,1,3..5',
176 { h => [2,4], m => [0,1,3,4,5], dow => $alldays },
177 [
178 [0, 2*60*60],
179 [2*60*60 + 60, 2*60*60 + 3*60],
180 [2*60*60 + 5*60, 4*60*60]
181 ]
182 ],
183 ];
184
185 foreach my $test (@$tests) {
186 my ($t, $expect, $nextsync) = @$test;
187
188 my $timespec;
189 eval { $timespec = PVE::CalendarEvent::parse_calendar_event($t); };
190 my $err = $@;
191 delete $timespec->{utc};
192
193 if ($expect->{error}) {
194 chomp $err if $err;
195 $timespec = { error => $err } if $err;
196 is_deeply($timespec, $expect, "expect parse error on '$t' - $expect->{error}");
197 die "unable to execute nextsync tests" if $nextsync;
198 } else {
199 is_deeply($timespec, $expect, "parse '$t'");
200 }
201
202 next if !$nextsync;
203
204 foreach my $nt (@$nextsync) {
205 my ($last, $expect_next) = @$nt;
206 my $msg = "next event '$t' $last => ${expect_next}";
207 $timespec->{utc} = 1;
208 my $next = PVE::CalendarEvent::compute_next_event($timespec, $last);
209 is($next, $expect_next, $msg);
210 }
211 };
212
213 sub tztest {
214 my ($calspec, $last) = @_;
215 my $spec = PVE::CalendarEvent::parse_calendar_event($calspec);
216 return PVE::CalendarEvent::compute_next_event($spec, $last);
217 }
218
219 # Test loop termination at CEST/CET switch (cannot happen here in UTC)
220 is(tztest('mon..fri', timelocal(0, 0, 0, 28, 9, 2018)),
221 timelocal(0, 0, 0, 29, 9, 2018));
222 is(tztest('mon..fri UTC', timelocal(0, 0, 0, 28, 9, 2018)),
223 timelocal(0, 0, 0, 29, 9, 2018));
224
225 # Now in the affected time zone
226 $ENV{TZ} = ':Europe/Vienna';
227 POSIX::tzset();
228 is(tztest('mon..fri', timelocal(0, 0, 0, 28, 9, 2018)),
229 timelocal(0, 0, 0, 29, 9, 2018));
230 # Specifically requesting UTC in the calendar spec means the resulting output
231 # time as seen locally (timelocal() as opposed to timegm()) is shifted by 1
232 # hour.
233 is(tztest('mon..fri UTC', timelocal(0, 0, 0, 28, 9, 2018)),
234 timelocal(0, 0, 1, 29, 9, 2018));
235 $ENV{TZ} = 'UTC';
236 POSIX::tzset();
237
238 done_testing();