aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/exess/test/test_time.c
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/exess/test/test_time.c')
-rw-r--r--subprojects/exess/test/test_time.c222
1 files changed, 222 insertions, 0 deletions
diff --git a/subprojects/exess/test/test_time.c b/subprojects/exess/test/test_time.c
new file mode 100644
index 00000000..6cf20f6d
--- /dev/null
+++ b/subprojects/exess/test/test_time.c
@@ -0,0 +1,222 @@
+/*
+ Copyright 2011-2021 David Robillard <d@drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#undef NDEBUG
+
+#include "float_test_data.h"
+#include "int_test_data.h"
+#include "time_test_utils.h"
+
+#include "exess/exess.h"
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+static const ExessTime nozone = {{EXESS_LOCAL}, 0, 0, 0, 0};
+static const ExessTime utc = {{0}, 12, 15, 1, 250000000};
+static const ExessTime zoned = {INIT_ZONE(11, 30), 23, 59, 59, 1000000};
+static const ExessTime high = {INIT_ZONE(11, 30), 24, 0, 0, 0};
+static const ExessTime garbage1 = {INIT_ZONE(11, 30), 0, 0, 0, 1000000000};
+static const ExessTime garbage2 = {INIT_ZONE(11, 30), 0, 0, 60, 0};
+static const ExessTime garbage3 = {INIT_ZONE(11, 30), 0, 60, 0, 0};
+static const ExessTime garbage4 = {INIT_ZONE(11, 30), 24, 0, 0, 1};
+static const ExessTime garbage5 = {INIT_ZONE(11, 30), 24, 0, 1, 0};
+static const ExessTime garbage6 = {INIT_ZONE(11, 30), 24, 1, 0, 0};
+static const ExessTime garbage7 = {INIT_ZONE(11, 30), 25, 0, 0, 0};
+
+static void
+check_read(const char* const string,
+ const ExessStatus expected_status,
+ const uint8_t expected_hour,
+ const uint8_t expected_minute,
+ const uint8_t expected_second,
+ const uint32_t expected_nanosecond,
+ const int8_t expected_tz_hour,
+ const int8_t expected_tz_minute,
+ const bool expected_tz_is_present,
+ const size_t expected_count)
+{
+ ExessTime value = {{0}, 0, 0, 0, 0};
+
+ const ExessResult r = exess_read_time(&value, string);
+ assert(r.status == expected_status);
+ assert(r.count == expected_count);
+ assert(value.hour == expected_hour);
+ assert(value.minute == expected_minute);
+ assert(value.second == expected_second);
+ assert(value.nanosecond == expected_nanosecond);
+ assert((!expected_tz_is_present && value.zone.quarter_hours == EXESS_LOCAL) ||
+ value.zone.quarter_hours ==
+ 4 * expected_tz_hour + expected_tz_minute / 15);
+}
+
+static void
+test_read_time(void)
+{
+ // No value
+ check_read("", EXESS_EXPECTED_DIGIT, 0, 0, 0, 0, 0, 0, false, 0);
+ check_read(" \f\n\r\t\v", EXESS_EXPECTED_DIGIT, 0, 0, 0, 0, 0, 0, false, 6);
+
+ // Good values
+ check_read("13:20:00", EXESS_SUCCESS, 13, 20, 0, 0, 0, 0, false, 8);
+ check_read(
+ "13:20:30.5555", EXESS_SUCCESS, 13, 20, 30, 555500000, 0, 0, false, 13);
+ check_read("13:20:00-05:00", EXESS_SUCCESS, 13, 20, 0, 0, -5, 0, true, 14);
+ check_read("13:20:00Z", EXESS_SUCCESS, 13, 20, 0, 0, 0, 0, true, 9);
+ check_read("00:00:00", EXESS_SUCCESS, 0, 0, 0, 0, 0, 0, false, 8);
+ check_read("24:00:00", EXESS_SUCCESS, 24, 0, 0, 0, 0, 0, false, 8);
+ check_read("21:32:52", EXESS_SUCCESS, 21, 32, 52, 0, 0, 0, false, 8);
+ check_read("21:32:52+02:00", EXESS_SUCCESS, 21, 32, 52, 0, 2, 0, true, 14);
+ check_read("19:32:52Z", EXESS_SUCCESS, 19, 32, 52, 0, 0, 0, true, 9);
+ check_read("19:32:52+00:00", EXESS_SUCCESS, 19, 32, 52, 0, 0, 0, true, 14);
+ check_read(
+ "21:32:52.12679", EXESS_SUCCESS, 21, 32, 52, 126790000, 0, 0, false, 14);
+
+ // Longest possible string
+ check_read("24:59:59.000000001-14:00",
+ EXESS_SUCCESS,
+ 24,
+ 59,
+ 59,
+ 1,
+ -14,
+ 0,
+ true,
+ EXESS_MAX_TIME_LENGTH);
+
+ // Non-canonical form
+ check_read(
+ " \f\n\r\t\v13:20:00 ", EXESS_SUCCESS, 13, 20, 0, 0, 0, 0, false, 14);
+
+ // Trailing garbage
+ check_read("13:20:00junk", EXESS_EXPECTED_SIGN, 13, 20, 0, 0, 0, 0, false, 8);
+ check_read("13:20:00Zjunk", EXESS_EXPECTED_END, 13, 20, 0, 0, 0, 0, true, 9);
+
+ // Garbage
+ check_read("13.20.00", EXESS_EXPECTED_COLON, 13, 0, 0, 0, 0, 0, false, 2);
+ check_read("13:20:", EXESS_EXPECTED_DIGIT, 13, 20, 0, 0, 0, 0, false, 6);
+ check_read("5:20:00", EXESS_EXPECTED_DIGIT, 5, 0, 0, 0, 0, 0, false, 1);
+ check_read("13:20", EXESS_EXPECTED_COLON, 13, 20, 0, 0, 0, 0, false, 5);
+ check_read("13:20.5:00", EXESS_EXPECTED_COLON, 13, 20, 0, 0, 0, 0, false, 5);
+ check_read("13:65:00", EXESS_OUT_OF_RANGE, 13, 65, 0, 0, 0, 0, false, 5);
+ check_read("21:32", EXESS_EXPECTED_COLON, 21, 32, 0, 0, 0, 0, false, 5);
+ check_read("25:25:10", EXESS_OUT_OF_RANGE, 25, 0, 0, 0, 0, 0, false, 2);
+ check_read("-10:00:00", EXESS_EXPECTED_DIGIT, 0, 0, 0, 0, 0, 0, false, 0);
+ check_read("1:20:10", EXESS_EXPECTED_DIGIT, 1, 0, 0, 0, 0, 0, false, 1);
+ check_read("13:20:00A", EXESS_EXPECTED_SIGN, 13, 20, 0, 0, 0, 0, false, 8);
+}
+
+static void
+check_write(const ExessTime value,
+ const ExessStatus expected_status,
+ const size_t buf_size,
+ const char* const expected_string)
+{
+ char buf[EXESS_MAX_TIME_LENGTH + 1] = {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};
+
+ assert(buf_size <= sizeof(buf));
+
+ const ExessResult r = exess_write_time(value, buf_size, buf);
+ assert(r.status == expected_status);
+ assert(r.count == strlen(buf));
+ assert(!strcmp(buf, expected_string));
+ assert(r.status || exess_write_time(value, 0, NULL).count == r.count);
+}
+
+static void
+test_write_time(void)
+{
+ check_write(nozone, EXESS_SUCCESS, 9, "00:00:00");
+ check_write(utc, EXESS_SUCCESS, 13, "12:15:01.25Z");
+ check_write(zoned, EXESS_SUCCESS, 19, "23:59:59.001+11:30");
+ check_write(high, EXESS_SUCCESS, 15, "24:00:00+11:30");
+
+ check_write(garbage1, EXESS_BAD_VALUE, 19, "");
+ check_write(garbage2, EXESS_BAD_VALUE, 19, "");
+ check_write(garbage3, EXESS_BAD_VALUE, 19, "");
+ check_write(garbage4, EXESS_BAD_VALUE, 19, "");
+ check_write(garbage5, EXESS_BAD_VALUE, 19, "");
+ check_write(garbage6, EXESS_BAD_VALUE, 19, "");
+ check_write(garbage7, EXESS_BAD_VALUE, 19, "");
+
+ check_write(nozone, EXESS_NO_SPACE, 8, "");
+ check_write(utc, EXESS_NO_SPACE, 12, "");
+ check_write(zoned, EXESS_NO_SPACE, 18, "");
+ check_write(zoned, EXESS_NO_SPACE, 12, "");
+ check_write(high, EXESS_NO_SPACE, 14, "");
+
+ // Check that nothing is written when there isn't enough space
+ char c = 42;
+ const ExessResult r = exess_write_time(nozone, 0, &c);
+ assert(c == 42);
+ assert(r.status == EXESS_NO_SPACE);
+ assert(r.count == 0);
+}
+
+static void
+check_round_trip(const ExessTime value)
+{
+ ExessTime parsed_value = {{0}, 0, 0, 0, 0};
+ char buf[EXESS_MAX_TIME_LENGTH + 1] = {0};
+
+ assert(!exess_write_time(value, sizeof(buf), buf).status);
+ assert(!exess_read_time(&parsed_value, buf).status);
+ assert(parsed_value.hour == value.hour);
+ assert(parsed_value.minute == value.minute);
+ assert(double_matches(parsed_value.second, value.second));
+ assert(parsed_value.zone.quarter_hours == value.zone.quarter_hours);
+}
+
+static void
+test_round_trip(void)
+{
+ uint32_t rng = 0;
+ for (uint8_t h = 0; h < 24; ++h) {
+ for (uint8_t m = 0; m < 60; ++m) {
+ rng = lcg32(rng);
+
+ const uint32_t ns = rng % 1000000000u;
+
+ rng = lcg32(rng);
+
+ const uint8_t s = (uint8_t)(rng % 60u);
+ const ExessTime no_zone = {{EXESS_LOCAL}, h, m, s, ns};
+ const ExessTime lowest_zone = {INIT_ZONE(-14, 0), h, m, s, ns};
+ const ExessTime highest_zone = {INIT_ZONE(14, 0), h, m, s, ns};
+
+ check_round_trip(no_zone);
+ check_round_trip(lowest_zone);
+ check_round_trip(highest_zone);
+
+ const ExessTime value = {random_timezone(&rng), h, m, s, ns};
+ check_round_trip(value);
+ }
+ }
+}
+
+int
+main(void)
+{
+ test_read_time();
+ test_write_time();
+ test_round_trip();
+
+ return 0;
+}