Line data Source code
1 : /*
2 : Zipios -- a small C++ library that provides easy access to .zip files.
3 :
4 : Copyright (C) 2019 Made to Order Software Corporation
5 :
6 : This library is free software; you can redistribute it and/or
7 : modify it under the terms of the GNU Lesser General Public
8 : License as published by the Free Software Foundation; either
9 : version 2.1 of the License, or (at your option) any later version.
10 :
11 : This library is distributed in the hope that it will be useful,
12 : but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 : Lesser General Public License for more details.
15 :
16 : You should have received a copy of the GNU Lesser General Public
17 : License along with this library; if not, write to the Free Software
18 : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 : */
20 :
21 : /** \file
22 : *
23 : * Zipios unit tests verify the dosdatetime.cpp/hpp implementation.
24 : *
25 : * Keep in mind that the date is saved as a local time date in a zip file.
26 : * It's crap. But that comes from the old days of DOS which chose that format
27 : * at the time. Sharing between people in the whole world was not viewed in
28 : * a way similar to today's world...
29 : *
30 : * \warning
31 : * This test will fail if run around a standard time to a saving time
32 : * or a saving time to a standard time changing period (i.e. during the
33 : * 1h or so when time changes in spring and autumn.)
34 : */
35 :
36 : #include "tests.hpp"
37 :
38 : #include "zipios/dosdatetime.hpp"
39 : #include "zipios/zipiosexceptions.hpp"
40 :
41 : #include <fstream>
42 :
43 : #include <sys/stat.h>
44 : #include <unistd.h>
45 :
46 : #include <cstring>
47 : #include <iostream>
48 :
49 :
50 :
51 :
52 : // min = Jan 1, 1980 at 00:00:00
53 : std::time_t g_minimum_unix = -1;
54 :
55 : // max = Dec 31, 2107 at 23:59:59
56 : std::time_t g_maximum_unix = -1;
57 :
58 :
59 5 : void init_min_max()
60 : {
61 5 : if(g_minimum_unix == -1)
62 : {
63 : struct tm t;
64 :
65 1 : memset(&t, 0, sizeof(t));
66 1 : t.tm_sec = 0;
67 1 : t.tm_min = 0;
68 1 : t.tm_hour = 0;
69 1 : t.tm_mday = 1;
70 1 : t.tm_mon = 0;
71 1 : t.tm_year = 1980 - 1900;
72 1 : t.tm_isdst = -1;
73 1 : g_minimum_unix = mktime(&t);
74 :
75 : struct tm c;
76 1 : localtime_r(&g_minimum_unix, &c);
77 : }
78 :
79 5 : if(g_maximum_unix == -1)
80 : {
81 : struct tm t;
82 :
83 1 : memset(&t, 0, sizeof(t));
84 1 : t.tm_sec = 58;
85 1 : t.tm_min = 59;
86 1 : t.tm_hour = 23;
87 1 : t.tm_mday = 31;
88 1 : t.tm_mon = 11;
89 1 : t.tm_year = 2107 - 1900;
90 1 : t.tm_isdst = -1;
91 1 : g_maximum_unix = mktime(&t);
92 : }
93 5 : }
94 :
95 :
96 :
97 3 : TEST_CASE("DOS Date & Time Min/Max", "[dosdatetime]")
98 : {
99 4 : SECTION("DOS time minimum")
100 : {
101 1 : zipios::DOSDateTime t;
102 1 : t.setYear(1980);
103 1 : t.setMonth(1);
104 1 : t.setMDay(1);
105 1 : t.setHour(0);
106 1 : t.setMinute(0);
107 1 : t.setSecond(0);
108 1 : REQUIRE(zipios::DOSDateTime::g_min_dosdatetime == t.getDOSDateTime());
109 : }
110 :
111 4 : SECTION("DOS time maximum")
112 : {
113 1 : zipios::DOSDateTime t;
114 1 : t.setYear(2107);
115 1 : t.setMonth(12);
116 1 : t.setMDay(31);
117 1 : t.setHour(23);
118 1 : t.setMinute(59);
119 1 : t.setSecond(59);
120 1 : REQUIRE(zipios::DOSDateTime::g_max_dosdatetime == t.getDOSDateTime());
121 : }
122 2 : }
123 :
124 :
125 8 : TEST_CASE("Invalid DOS Date & Time", "[dosdatetime]")
126 : {
127 14 : SECTION("daysInMonth() when month is invalid")
128 : {
129 : // by default the date is 0 which means the month is 0 and
130 : // trying to call daysInMonth() fails with -1
131 : //
132 1 : zipios::DOSDateTime d;
133 1 : REQUIRE(d.daysInMonth() == -1);
134 : }
135 :
136 14 : SECTION("get/set seconds")
137 : {
138 61 : for(int i = 0; i < 60; ++i)
139 : {
140 60 : zipios::DOSDateTime t;
141 60 : t.setSecond(i);
142 60 : REQUIRE(t.getSecond() == (i & -2));
143 : }
144 21 : for(int i = -20; i < 0; ++i)
145 : {
146 20 : zipios::DOSDateTime t;
147 20 : int const r(rand() % 60);
148 20 : t.setSecond(r);
149 20 : REQUIRE(t.getSecond() == (r & -2));
150 20 : REQUIRE_THROWS_AS(t.setSecond(i), zipios::InvalidException);
151 20 : REQUIRE(t.getSecond() == (r & -2));
152 : }
153 22 : for(int i = 60; i <= 80; ++i)
154 : {
155 21 : zipios::DOSDateTime t;
156 21 : int const r(rand() % 60);
157 21 : t.setSecond(r);
158 21 : REQUIRE(t.getSecond() == (r & -2));
159 21 : REQUIRE_THROWS_AS(t.setSecond(i), zipios::InvalidException);
160 21 : REQUIRE(t.getSecond() == (r & -2));
161 : }
162 : }
163 :
164 14 : SECTION("get/set minutes")
165 : {
166 61 : for(int i = 0; i < 60; ++i)
167 : {
168 60 : zipios::DOSDateTime t;
169 60 : t.setMinute(i);
170 60 : REQUIRE(t.getMinute() == i);
171 : }
172 21 : for(int i = -20; i < 0; ++i)
173 : {
174 20 : zipios::DOSDateTime t;
175 20 : int const r(rand() % 60);
176 20 : t.setMinute(r);
177 20 : REQUIRE(t.getMinute() == r);
178 20 : REQUIRE_THROWS_AS(t.setMinute(i), zipios::InvalidException);
179 20 : REQUIRE(t.getMinute() == r);
180 : }
181 22 : for(int i = 60; i <= 80; ++i)
182 : {
183 21 : zipios::DOSDateTime t;
184 21 : int const r(rand() % 60);
185 21 : t.setMinute(r);
186 21 : REQUIRE(t.getMinute() == r);
187 21 : REQUIRE_THROWS_AS(t.setMinute(i), zipios::InvalidException);
188 21 : REQUIRE(t.getMinute() == r);
189 : }
190 : }
191 :
192 14 : SECTION("get/set hours")
193 : {
194 25 : for(int i = 0; i < 24; ++i)
195 : {
196 24 : zipios::DOSDateTime t;
197 24 : t.setHour(i);
198 24 : REQUIRE(t.getHour() == i);
199 : }
200 21 : for(int i = -20; i < 0; ++i)
201 : {
202 20 : zipios::DOSDateTime t;
203 20 : int const r(rand() % 24);
204 20 : t.setHour(r);
205 20 : REQUIRE(t.getHour() == r);
206 20 : REQUIRE_THROWS_AS(t.setHour(i), zipios::InvalidException);
207 20 : REQUIRE(t.getHour() == r);
208 : }
209 22 : for(int i = 24; i <= 44; ++i)
210 : {
211 21 : zipios::DOSDateTime t;
212 21 : int const r(rand() % 24);
213 21 : t.setHour(r);
214 21 : REQUIRE(t.getHour() == r);
215 21 : REQUIRE_THROWS_AS(t.setHour(i), zipios::InvalidException);
216 21 : REQUIRE(t.getHour() == r);
217 : }
218 : }
219 :
220 : // day is limited between 1 and 31 on a setMDay()
221 : // use the isValid() to know whether it is valid for the current month
222 : // and year
223 : //
224 14 : SECTION("get/set day of the month")
225 : {
226 32 : for(int i = 1; i < 32; ++i)
227 : {
228 31 : zipios::DOSDateTime t;
229 31 : t.setMDay(i);
230 31 : REQUIRE(t.getMDay() == i);
231 : }
232 22 : for(int i = -20; i <= 0; ++i)
233 : {
234 21 : zipios::DOSDateTime t;
235 21 : int const r(rand() % 31 + 1);
236 21 : t.setMDay(r);
237 21 : REQUIRE(t.getMDay() == r);
238 21 : REQUIRE_THROWS_AS(t.setMDay(i), zipios::InvalidException);
239 21 : REQUIRE(t.getMDay() == r);
240 : }
241 22 : for(int i = 32; i <= 52; ++i)
242 : {
243 21 : zipios::DOSDateTime t;
244 21 : int const r(rand() % 31 + 1);
245 21 : t.setMDay(r);
246 21 : REQUIRE(t.getMDay() == r);
247 21 : REQUIRE_THROWS_AS(t.setMDay(i), zipios::InvalidException);
248 21 : REQUIRE(t.getMDay() == r);
249 : }
250 : }
251 :
252 14 : SECTION("get/set month")
253 : {
254 12 : for(int i = 1; i < 12; ++i)
255 : {
256 11 : zipios::DOSDateTime t;
257 11 : t.setMonth(i);
258 11 : REQUIRE(t.getMonth() == i);
259 : }
260 22 : for(int i = -20; i <= 0; ++i)
261 : {
262 21 : zipios::DOSDateTime t;
263 21 : int const r(rand() % 12 + 1);
264 21 : t.setMonth(r);
265 21 : REQUIRE(t.getMonth() == r);
266 21 : REQUIRE_THROWS_AS(t.setMonth(i), zipios::InvalidException);
267 21 : REQUIRE(t.getMonth() == r);
268 : }
269 22 : for(int i = 13; i <= 33; ++i)
270 : {
271 21 : zipios::DOSDateTime t;
272 21 : int const r(rand() % 12 + 1);
273 21 : t.setMonth(r);
274 21 : REQUIRE(t.getMonth() == r);
275 21 : REQUIRE_THROWS_AS(t.setMonth(i), zipios::InvalidException);
276 21 : REQUIRE(t.getMonth() == r);
277 : }
278 : }
279 :
280 14 : SECTION("get/set year")
281 : {
282 129 : for(int i = 1980; i <= 2107; ++i)
283 : {
284 128 : zipios::DOSDateTime t;
285 128 : t.setYear(i);
286 128 : REQUIRE(t.getYear() == i);
287 : }
288 981 : for(int i = 1000; i < 1980; ++i)
289 : {
290 980 : zipios::DOSDateTime t;
291 980 : int const r(rand() % (2107 - 1980 + 1) + 1980);
292 980 : t.setYear(r);
293 980 : REQUIRE(t.getYear() == r);
294 980 : REQUIRE_THROWS_AS(t.setYear(i), zipios::InvalidException);
295 980 : REQUIRE(t.getYear() == r);
296 : }
297 94 : for(int i = 2108; i <= 2200; ++i)
298 : {
299 93 : zipios::DOSDateTime t;
300 93 : int const r(rand() % (2107 - 1980 + 1) + 1980);
301 93 : t.setYear(r);
302 93 : REQUIRE(t.getYear() == r);
303 93 : REQUIRE_THROWS_AS(t.setYear(i), zipios::InvalidException);
304 93 : REQUIRE(t.getYear() == r);
305 : }
306 : }
307 7 : }
308 :
309 :
310 : #if 0
311 : // this test is too long, which is why it is commented out by default
312 : // still, it is great if you want to verify that all possible DOS Time & Date
313 : //
314 : TEST_CASE("All Valid DOS Date & Time", "[dosdatetime]")
315 : {
316 : if(sizeof(std::time_t) < sizeof(uint64_t))
317 : {
318 : std::cerr << "warning: Unix to DOS time conversion is ignored on platform with a 32 bit time_t definition." << std::endl;
319 : return;
320 : }
321 :
322 : init_min_max();
323 :
324 : SECTION("all valid DOS Date Time values")
325 : {
326 : // make sure the maximum limit is checked properly
327 : //
328 : for(std::time_t t(g_minimum_unix); t <= g_maximum_unix; ++t)
329 : {
330 : std::time_t et((t + 1) & ~1);
331 :
332 : zipios::DOSDateTime td;
333 : REQUIRE_FALSE(td.isValid());
334 : td.setUnixTimestamp(t);
335 : REQUIRE(td.isValid());
336 :
337 : zipios::DOSDateTime::dosdatetime_t const d(td.getDOSDateTime());
338 :
339 : zipios::DOSDateTime tu;
340 : REQUIRE_FALSE(tu.isValid());
341 : tu.setDOSDateTime(d);
342 : REQUIRE(tu.isValid());
343 :
344 : std::time_t const u(tu.getUnixTimestamp());
345 : REQUIRE(u == et);
346 : }
347 : }
348 : }
349 : #endif
350 :
351 :
352 3 : TEST_CASE("Small DOS Date & Time", "[dosdatetime]")
353 : {
354 : if(sizeof(std::time_t) < sizeof(uint64_t))
355 : {
356 : std::cerr << "warning: Unix to DOS time conversion is ignored on platform with a 32 bit time_t definition." << std::endl;
357 : return;
358 : }
359 :
360 2 : init_min_max();
361 :
362 4 : SECTION("just under the minimum")
363 : {
364 : // make sure the minimum limit is checked properly
365 : //
366 1 : for(std::time_t t(g_minimum_unix - 20); t < 0; ++t)
367 : {
368 0 : zipios::DOSDateTime td;
369 0 : REQUIRE_FALSE(td.isValid());
370 0 : REQUIRE_THROWS_AS(td.setUnixTimestamp(t), zipios::InvalidException);
371 0 : REQUIRE_FALSE(td.isValid());
372 : }
373 : }
374 :
375 4 : SECTION("just around minimum, but valid")
376 : {
377 : // the "g_minimum_unix - 1" case is peculiar, the Unix date is not
378 : // usable as is because it is odd and we're going to use the next
379 : // second which means we're getting `minimum_unix` as the date,
380 : // which is valid!
381 : //
382 22 : for(std::time_t t(g_minimum_unix); t <= g_minimum_unix + 20; ++t)
383 : {
384 21 : std::time_t et((t + 1) & ~1);
385 :
386 21 : zipios::DOSDateTime td;
387 21 : REQUIRE_FALSE(td.isValid());
388 21 : td.setUnixTimestamp(t);
389 21 : REQUIRE(td.isValid());
390 :
391 21 : zipios::DOSDateTime::dosdatetime_t const d(td.getDOSDateTime());
392 :
393 21 : zipios::DOSDateTime tu;
394 21 : REQUIRE_FALSE(tu.isValid());
395 21 : tu.setDOSDateTime(d);
396 21 : REQUIRE(tu.isValid());
397 :
398 21 : std::time_t const u(tu.getUnixTimestamp());
399 21 : REQUIRE(u == et);
400 : }
401 : }
402 : }
403 :
404 :
405 3 : TEST_CASE("Large DOS Date & Time", "[dosdatetime]")
406 : {
407 : if(sizeof(std::time_t) < sizeof(uint64_t))
408 : {
409 : std::cerr << "warning: Unix to DOS time conversion is ignored on platform with a 32 bit time_t definition." << std::endl;
410 : return;
411 : }
412 :
413 2 : init_min_max();
414 :
415 4 : SECTION("just around maximum, but valid")
416 : {
417 : // make sure the maximum limit is checked properly
418 : //
419 22 : for(std::time_t t(g_maximum_unix - 20); t <= g_maximum_unix; ++t)
420 : {
421 21 : std::time_t et((t + 1) & ~1);
422 :
423 21 : zipios::DOSDateTime td;
424 21 : REQUIRE_FALSE(td.isValid());
425 21 : td.setUnixTimestamp(t);
426 21 : REQUIRE(td.isValid());
427 :
428 21 : zipios::DOSDateTime::dosdatetime_t const d(td.getDOSDateTime());
429 :
430 21 : zipios::DOSDateTime tu;
431 21 : REQUIRE_FALSE(tu.isValid());
432 21 : tu.setDOSDateTime(d);
433 21 : REQUIRE(tu.isValid());
434 :
435 21 : std::time_t const u(tu.getUnixTimestamp());
436 21 : REQUIRE(u == et);
437 : }
438 : }
439 :
440 4 : SECTION("just a bit too large")
441 : {
442 : // make sure the maximum limit is checked properly
443 : //
444 21 : for(std::time_t t(g_maximum_unix + 1); t <= g_maximum_unix + 20; ++t)
445 : {
446 20 : std::time_t et((t + 1) & ~1);
447 :
448 20 : zipios::DOSDateTime td;
449 20 : REQUIRE(td.getDOSDateTime() == 0);
450 20 : REQUIRE_FALSE(td.isValid());
451 20 : REQUIRE_THROWS_AS(td.setUnixTimestamp(t), zipios::InvalidException);
452 20 : REQUIRE_FALSE(td.isValid());
453 20 : REQUIRE(td.getDOSDateTime() == 0);
454 20 : REQUIRE(td.getUnixTimestamp() == 0);
455 : }
456 : }
457 : }
458 :
459 :
460 2 : TEST_CASE("Random DOS Date & Time", "[dosdatetime]")
461 : {
462 1 : init_min_max();
463 :
464 2 : SECTION("more random tests")
465 : {
466 : std::time_t const max(sizeof(std::time_t) < sizeof(uint64_t)
467 : ? 0x7FFFFFFF // max. in 32 bits is less in Unix time_t than what the DOS Date Time supports
468 1 : : 0x104000000LL); // Dec 31, 2107 23:59:59 is 0x10391447F, so use something a little higher
469 :
470 266643 : for(std::time_t t(0); t <= max; t += rand() & 0x7FFF)
471 : {
472 266642 : if(t < g_minimum_unix
473 247332 : || t > g_maximum_unix)
474 : {
475 19747 : zipios::DOSDateTime td;
476 19747 : REQUIRE_THROWS_AS(td.setUnixTimestamp(t), zipios::InvalidException);
477 : }
478 : else
479 : {
480 246895 : std::time_t et(t);
481 246895 : if(t == g_maximum_unix)
482 : {
483 0 : et += 1;
484 : }
485 246895 : et &= ~1;
486 :
487 246895 : zipios::DOSDateTime td;
488 246895 : td.setUnixTimestamp(t);
489 :
490 246895 : zipios::DOSDateTime::dosdatetime_t const d(td.getDOSDateTime());
491 :
492 246895 : zipios::DOSDateTime tu;
493 246895 : tu.setDOSDateTime(d);
494 :
495 246895 : std::time_t const u(tu.getUnixTimestamp());
496 : }
497 : }
498 : }
499 4 : }
500 :
501 :
502 :
503 : // Local Variables:
504 : // mode: cpp
505 : // indent-tabs-mode: nil
506 : // c-basic-offset: 4
507 : // tab-width: 4
508 : // End:
509 :
510 : // vim: ts=4 sw=4 et
|