diff options
author | David Robillard <d@drobilla.net> | 2021-02-25 10:27:59 -0500 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2021-03-08 23:23:05 -0500 |
commit | c4821c8e6bf1f81c6ea31e11ebc0fc1666e9337b (patch) | |
tree | a62995534f5f606ac2f8bae22d525532b824cb5e /subprojects/exess/test/test_duration.c | |
parent | 6bcd18ae60482790b645a345f718e7099250f261 (diff) | |
download | serd-c4821c8e6bf1f81c6ea31e11ebc0fc1666e9337b.tar.gz serd-c4821c8e6bf1f81c6ea31e11ebc0fc1666e9337b.tar.bz2 serd-c4821c8e6bf1f81c6ea31e11ebc0fc1666e9337b.zip |
Add exess from git@gitlab.com:drobilla/exess.git 4638b1f
Diffstat (limited to 'subprojects/exess/test/test_duration.c')
-rw-r--r-- | subprojects/exess/test/test_duration.c | 310 |
1 files changed, 310 insertions, 0 deletions
diff --git a/subprojects/exess/test/test_duration.c b/subprojects/exess/test/test_duration.c new file mode 100644 index 00000000..3d964e80 --- /dev/null +++ b/subprojects/exess/test/test_duration.c @@ -0,0 +1,310 @@ +/* + 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 "int_test_data.h" +#include "macros.h" +#include "num_test_utils.h" + +#include "exess/exess.h" + +#include <assert.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +static const ExessDuration zero = {0, 0, 0}; +static const ExessDuration lowest = {-INT32_MAX, -INT32_MAX, -999999999}; +static const ExessDuration highest = {INT32_MAX, INT32_MAX, 999999999}; +static const ExessDuration year = {12, 0, 0}; +static const ExessDuration month = {1, 0, 0}; +static const ExessDuration day = {0, 24 * 60 * 60, 0}; +static const ExessDuration hour = {0, 60 * 60, 0}; +static const ExessDuration minute = {0, 60, 0}; +static const ExessDuration second = {0, 1, 0}; +static const ExessDuration nanosecond = {0, 0, 1}; + +static const ExessDuration n_year = {-12, 0, 0}; +static const ExessDuration n_month = {-1, 0, 0}; +static const ExessDuration n_day = {0, -24 * 60 * 60, 0}; +static const ExessDuration n_hour = {0, -60 * 60, 0}; +static const ExessDuration n_minute = {0, -60, 0}; +static const ExessDuration n_second = {0, -1, 0}; +static const ExessDuration n_nanosecond = {0, 0, -1}; + +static const ExessDuration garbage1 = {1, 1, -1}; +static const ExessDuration garbage2 = {1, -1, 1}; +static const ExessDuration garbage3 = {1, -1, -1}; +static const ExessDuration garbage4 = {-1, 1, 1}; +static const ExessDuration garbage5 = {-1, 1, -1}; +static const ExessDuration garbage6 = {-1, -1, 1}; +static const ExessDuration garbage7 = {INT32_MIN, 0, -999999999}; +static const ExessDuration garbage8 = {0, INT32_MIN, -999999999}; +static const ExessDuration garbage9 = {INT32_MIN, INT32_MIN, -999999999}; + +static void +check_read(const char* const string, + const ExessStatus expected_status, + const size_t expected_count, + const int32_t expected_years, + const int32_t expected_months, + const int32_t expected_days, + const int32_t expected_hours, + const int32_t expected_minutes, + const int32_t expected_seconds, + const int32_t expected_nanoseconds, + const bool expected_is_negative) +{ + ExessDuration value = {0, 0, 0.0f}; + const ExessResult r = exess_read_duration(&value, string); + + assert(r.status == expected_status); + assert(r.count == expected_count); + + assert(value.months == (expected_is_negative ? -1 : 1) * 12 * expected_years + + expected_months); + + assert(value.seconds == + (expected_is_negative ? -1 : 1) * + ((expected_seconds + (60 * expected_minutes) + + (60 * 60 * expected_hours) + (24 * 60 * 60 * expected_days)))); + + assert(value.nanoseconds == expected_nanoseconds); +} + +static void +test_read_duration(void) +{ + // No input + check_read("", EXESS_EXPECTED_DURATION, 0, 0, 0, 0, 0, 0, 0, 0, false); + check_read( + " \f\n\r\t\v", EXESS_EXPECTED_DURATION, 6, 0, 0, 0, 0, 0, 0, 0, false); + + // Good values + + check_read( + "P2Y6M5DT12H35M30S", EXESS_SUCCESS, 17, 2, 6, 5, 12, 35, 30, 0, false); + + check_read("P1DT2H", EXESS_SUCCESS, 6, 0, 0, 1, 2, 0, 0, 0, false); + check_read("P20M", EXESS_SUCCESS, 4, 0, 20, 0, 0, 0, 0, 0, false); + check_read("PT20M", EXESS_SUCCESS, 5, 0, 0, 0, 0, 20, 0, 0, false); + check_read("P0Y20M0D", EXESS_SUCCESS, 8, 0, 20, 0, 0, 0, 0, 0, false); + check_read("P0Y", EXESS_SUCCESS, 3, 0, 0, 0, 0, 0, 0, 0, false); + check_read("-P60D", EXESS_SUCCESS, 5, 0, 0, 60, 0, 0, 0, 0, true); + check_read( + "PT1M30.5S", EXESS_SUCCESS, 9, 0, 0, 0, 0, 1, 30, 500000000, false); + + // Leading and trailing whitespace + check_read(" \f\n\r\t\vP1Y", EXESS_SUCCESS, 9, 1, 0, 0, 0, 0, 0, 0, false); + check_read("P1MT2H \f\n\r\t\v", EXESS_SUCCESS, 6, 0, 1, 0, 2, 0, 0, 0, false); + check_read(" \f\n\r\t\vP1Y", EXESS_SUCCESS, 9, 1, 0, 0, 0, 0, 0, 0, false); + check_read("P1YT2H \f\n\r\t\v", EXESS_SUCCESS, 6, 1, 0, 0, 2, 0, 0, 0, false); + + // Non-canonical form + check_read("P06D", EXESS_SUCCESS, 4, 0, 0, 6, 0, 0, 0, 0, false); + check_read("PT7.0S", EXESS_SUCCESS, 6, 0, 0, 0, 0, 0, 7, 0, false); + check_read( + "P0Y0M01DT06H00M00S", EXESS_SUCCESS, 18, 0, 0, 1, 6, 0, 0, 0, false); + + // Out of range fields + check_read( + "P2147483647Y", EXESS_OUT_OF_RANGE, 11, 0, 0, 0, 0, 0, 0, 0, false); + check_read( + "P2147483647M", EXESS_OUT_OF_RANGE, 11, 0, 0, 0, 0, 0, 0, 0, false); + check_read( + "P2147483647D", EXESS_OUT_OF_RANGE, 11, 0, 0, 0, 0, 0, 0, 0, false); + check_read( + "PT2147483647H", EXESS_OUT_OF_RANGE, 12, 0, 0, 0, 0, 0, 0, 0, false); + check_read( + "PT2147483647M", EXESS_OUT_OF_RANGE, 12, 0, 0, 0, 0, 0, 0, 0, false); + check_read( + "PT2147483647S", EXESS_OUT_OF_RANGE, 12, 0, 0, 0, 0, 0, 0, 0, false); + + // Garbage + check_read("P-20M", EXESS_EXPECTED_DIGIT, 1, 0, 0, 0, 0, 0, 0, 0, false); + check_read("P20MT", EXESS_EXPECTED_DIGIT, 5, 0, 20, 0, 0, 0, 0, 0, false); + check_read("P1YM5D", EXESS_EXPECTED_DIGIT, 3, 1, 0, 0, 0, 0, 0, 0, false); + check_read("P15.5Y", EXESS_EXPECTED_DATE_TAG, 3, 0, 0, 0, 0, 0, 0, 0, false); + check_read("P1D2H", EXESS_EXPECTED_TIME_SEP, 3, 0, 0, 1, 0, 0, 0, 0, false); + check_read("1Y2M", EXESS_EXPECTED_DURATION, 0, 0, 0, 0, 0, 0, 0, 0, false); + check_read("P2M1Y", EXESS_BAD_ORDER, 4, 0, 2, 0, 0, 0, 0, 0, false); + check_read("P2D1Y", EXESS_EXPECTED_TIME_SEP, 3, 0, 0, 2, 0, 0, 0, 0, false); + check_read("P2D1M", EXESS_EXPECTED_TIME_SEP, 3, 0, 0, 2, 0, 0, 0, 0, false); + check_read("P", EXESS_EXPECTED_DIGIT, 1, 0, 0, 0, 0, 0, 0, 0, false); + check_read("PT15.5H", EXESS_EXPECTED_TIME_TAG, 6, 0, 0, 0, 0, 0, 0, 0, false); + check_read("PT2M1H", EXESS_BAD_ORDER, 5, 0, 0, 0, 0, 2, 0, 0, false); + check_read("PT2S1H", EXESS_EXPECTED_END, 4, 0, 0, 0, 0, 0, 2, 0, false); + check_read("PT2S1M", EXESS_EXPECTED_END, 4, 0, 0, 0, 0, 0, 2, 0, false); + check_read("PT15.S", EXESS_EXPECTED_DIGIT, 5, 0, 0, 0, 0, 0, 0, 0, false); + check_read("P1Q", EXESS_EXPECTED_DATE_TAG, 2, 0, 0, 0, 0, 0, 0, 0, false); + check_read("PT1Q", EXESS_EXPECTED_TIME_TAG, 3, 0, 0, 0, 0, 0, 0, 0, false); + check_read("P-1Y", EXESS_EXPECTED_DIGIT, 1, 0, 0, 0, 0, 0, 0, 0, false); + check_read("P-1M", EXESS_EXPECTED_DIGIT, 1, 0, 0, 0, 0, 0, 0, 0, false); + check_read("P-1D", EXESS_EXPECTED_DIGIT, 1, 0, 0, 0, 0, 0, 0, 0, false); + check_read("PT-1H", EXESS_EXPECTED_DIGIT, 2, 0, 0, 0, 0, 0, 0, 0, false); + check_read("PT-1M", EXESS_EXPECTED_DIGIT, 2, 0, 0, 0, 0, 0, 0, 0, false); + check_read("PT-1S", EXESS_EXPECTED_DIGIT, 2, 0, 0, 0, 0, 0, 0, 0, false); + check_read( + "P4294967296Y", EXESS_OUT_OF_RANGE, 11, 0, 0, 0, 0, 0, 0, 0, false); + check_read( + "P4294967296M", EXESS_OUT_OF_RANGE, 11, 0, 0, 0, 0, 0, 0, 0, false); + check_read( + "P4294967296D", EXESS_OUT_OF_RANGE, 11, 0, 0, 0, 0, 0, 0, 0, false); + check_read( + "PT4294967296H", EXESS_OUT_OF_RANGE, 12, 0, 0, 0, 0, 0, 0, 0, false); + check_read( + "PT4294967296M", EXESS_OUT_OF_RANGE, 12, 0, 0, 0, 0, 0, 0, 0, false); + check_read("", EXESS_EXPECTED_DURATION, 0, 0, 0, 0, 0, 0, 0, 0, false); +} + +static void +check_write(const ExessDuration value, + const ExessStatus expected_status, + const size_t buf_size, + const char* const expected_string) +{ + char buf[EXESS_MAX_DURATION_LENGTH + 1] = {42}; + + assert(buf_size <= sizeof(buf)); + + const ExessResult r = exess_write_duration(value, buf_size, buf); + assert(r.status == expected_status); + assert(r.count == strlen(buf)); + assert(!strcmp(buf, expected_string)); + assert(r.status || exess_write_duration(value, 0, NULL).count == r.count); +} + +static void +test_write_duration(void) +{ + check_write(zero, EXESS_SUCCESS, 4, "P0Y"); + + check_write( + lowest, EXESS_SUCCESS, 39, "-P178956970Y7M24855DT3H14M7.999999999S"); + + check_write( + highest, EXESS_SUCCESS, 38, "P178956970Y7M24855DT3H14M7.999999999S"); + + check_write(year, EXESS_SUCCESS, 4, "P1Y"); + check_write(month, EXESS_SUCCESS, 4, "P1M"); + check_write(day, EXESS_SUCCESS, 4, "P1D"); + check_write(hour, EXESS_SUCCESS, 5, "PT1H"); + check_write(minute, EXESS_SUCCESS, 5, "PT1M"); + check_write(second, EXESS_SUCCESS, 5, "PT1S"); + check_write(nanosecond, EXESS_SUCCESS, 15, "PT0.000000001S"); + + check_write(n_year, EXESS_SUCCESS, 5, "-P1Y"); + check_write(n_month, EXESS_SUCCESS, 5, "-P1M"); + check_write(n_day, EXESS_SUCCESS, 5, "-P1D"); + check_write(n_hour, EXESS_SUCCESS, 6, "-PT1H"); + check_write(n_minute, EXESS_SUCCESS, 6, "-PT1M"); + check_write(n_second, EXESS_SUCCESS, 6, "-PT1S"); + check_write(n_nanosecond, EXESS_SUCCESS, 16, "-PT0.000000001S"); + + check_write(garbage1, EXESS_BAD_VALUE, 41, ""); + check_write(garbage2, EXESS_BAD_VALUE, 41, ""); + check_write(garbage3, EXESS_BAD_VALUE, 41, ""); + check_write(garbage4, EXESS_BAD_VALUE, 41, ""); + check_write(garbage5, EXESS_BAD_VALUE, 41, ""); + check_write(garbage6, EXESS_BAD_VALUE, 41, ""); + check_write(garbage7, EXESS_OUT_OF_RANGE, 41, ""); + check_write(garbage8, EXESS_OUT_OF_RANGE, 41, ""); + check_write(garbage9, EXESS_OUT_OF_RANGE, 41, ""); + + check_write(zero, EXESS_NO_SPACE, 3, ""); + check_write(lowest, EXESS_NO_SPACE, 24, ""); + check_write(highest, EXESS_NO_SPACE, 4, ""); + check_write(highest, EXESS_NO_SPACE, 10, ""); + check_write(highest, EXESS_NO_SPACE, 13, ""); + check_write(highest, EXESS_NO_SPACE, 16, ""); + check_write(highest, EXESS_NO_SPACE, 20, ""); + check_write(highest, EXESS_NO_SPACE, 23, ""); + check_write(year, EXESS_NO_SPACE, 3, ""); + check_write(month, EXESS_NO_SPACE, 3, ""); + check_write(day, EXESS_NO_SPACE, 3, ""); + check_write(hour, EXESS_NO_SPACE, 4, ""); + check_write(minute, EXESS_NO_SPACE, 4, ""); + check_write(second, EXESS_NO_SPACE, 4, ""); + + // Check that nothing is written when there isn't enough space + char c = 42; + const ExessResult r = exess_write_duration(zero, 0, &c); + assert(c == 42); + assert(r.status == EXESS_NO_SPACE); + assert(r.count == 0); +} + +static void +check_round_trip(const ExessDuration value) +{ + ExessDuration parsed_value = {0, 0, 0}; + char buf[EXESS_MAX_DURATION_LENGTH + 1] = {0}; + + assert(exess_write_duration(value, 0, NULL).count <= + EXESS_MAX_DURATION_LENGTH); + + assert(!exess_write_duration(value, sizeof(buf), buf).status); + assert(!exess_read_duration(&parsed_value, buf).status); + assert(parsed_value.months == value.months); + assert(parsed_value.seconds == value.seconds); + assert(parsed_value.nanoseconds == value.nanoseconds); +} + +static void +test_round_trip(const ExessNumTestOptions opts) +{ + fprintf(stderr, "Testing xsd:duration randomly with seed %u\n", opts.seed); + + const uint64_t n_tests = MAX(256, opts.n_tests / 16); + + uint32_t rng = opts.seed; + for (size_t i = 0; i < n_tests; ++i) { + rng = lcg32(rng); + + const int32_t months = (int32_t)rng; + + rng = lcg32(rng); + + const int32_t seconds = (months < 0 ? -1 : 1) * (int32_t)(rng % INT32_MAX); + + rng = lcg32(rng); + + const int32_t nanoseconds = + (months < 0 ? -1 : 1) * (int32_t)(rng % 1000000000); + + const ExessDuration value = {months, seconds, nanoseconds}; + check_round_trip(value); + + print_num_test_progress(i, n_tests); + } +} + +int +main(int argc, char** argv) +{ + const ExessNumTestOptions opts = parse_num_test_options(argc, argv); + if (opts.error) { + return 1; + } + + test_read_duration(); + test_write_duration(); + test_round_trip(opts); + + return 0; +} |