aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/rerex/test
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/rerex/test')
-rw-r--r--subprojects/rerex/test/test_match.c238
-rw-r--r--subprojects/rerex/test/test_syntax.c99
-rw-r--r--subprojects/rerex/test/test_xsd.c522
3 files changed, 859 insertions, 0 deletions
diff --git a/subprojects/rerex/test/test_match.c b/subprojects/rerex/test/test_match.c
new file mode 100644
index 00000000..fa270e83
--- /dev/null
+++ b/subprojects/rerex/test/test_match.c
@@ -0,0 +1,238 @@
+/*
+ Copyright 2020 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 "rerex/rerex.h"
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+typedef struct {
+ uintptr_t match; ///< Boolean, true if text should match
+ const char* pattern; ///< Regular expression
+ const char* text; ///< Text to match against pattern
+} MatchTestCase;
+
+static const MatchTestCase match_tests[] = {
+ {1, "\\(", "("},
+ {1, "\\)", ")"},
+ {1, "\\*", "*"},
+ {1, "\\+", "+"},
+ {1, "\\-", "-"},
+ {1, "\\.", "."},
+ {1, "\\?", "?"},
+ {1, "\\[", "["},
+ {1, "\\]", "]"},
+ {1, "\\^", "^"},
+ {1, "\\|", "|"},
+ {0, ".", ""},
+ {1, ".", "a"},
+ {0, ".", "aa"},
+ {0, "..", ""},
+ {0, "..", "a"},
+ {1, "..", "aa"},
+ {1, ".*", ""},
+ {1, ".*", "a"},
+ {1, ".*", "aa"},
+ {0, ".+", ""},
+ {1, ".+", "a"},
+ {1, ".+", "aa"},
+ {1, ".?", ""},
+ {1, ".?", "a"},
+ {0, ".?", "aa"},
+ {1, "a*", ""},
+ {1, "a*", "a"},
+ {1, "a*", "aa"},
+ {0, "a*", "b"},
+ {0, "a+", ""},
+ {1, "a+", "a"},
+ {1, "a+", "aa"},
+ {0, "a+", "b"},
+ {1, "a?", ""},
+ {1, "a?", "a"},
+ {0, "a?", "aa"},
+ {0, "a?", "b"},
+ {0, "[.]", "a"},
+ {1, "[.]", "."},
+ {0, "[\\]]", "a"},
+ {1, "[\\]]", "]"},
+ {0, "[b]", "a"},
+ {1, "[b]", "b"},
+ {0, "[b]", "c"},
+ {0, "[bc]", "a"},
+ {1, "[bc]", "b"},
+ {1, "[bc]", "c"},
+ {0, "[bc]", "d"},
+ {0, "[bcd]", "a"},
+ {1, "[bcd]", "b"},
+ {1, "[bcd]", "c"},
+ {1, "[bcd]", "d"},
+ {0, "[bcd]", "e"},
+ {0, "[b-d]", "a"},
+ {1, "[b-d]", "b"},
+ {1, "[b-d]", "d"},
+ {0, "[b-d]", "e"},
+ {1, "[^b-d]", "a"},
+ {0, "[^b-d]", "b"},
+ {0, "[^b-d]", "d"},
+ {1, "[^b-d]", "e"},
+ {0, "[^ -/]", "\t"},
+ {1, "[^ -/]", "0"},
+ {1, "[^{-~]", "z"},
+ {0, "[^{-~]", "~"},
+ {0, "[A-Za-z]", "5"},
+ {1, "[A-Za-z]", "m"},
+ {1, "[A-Za-z]", "M"},
+ {0, "[A-Za-z]", "~"},
+ {0, "[+-]", "*"},
+ {1, "[+-]", "+"},
+ {0, "[+-]", ","},
+ {1, "[+-]", "-"},
+ {0, "[+-]", "."},
+ {1, "[b-d]*", ""},
+ {0, "[b-d]*", "a"},
+ {1, "[b-d]*", "b"},
+ {1, "[b-d]*", "c"},
+ {1, "[b-d]*", "cc"},
+ {1, "[b-d]*", "d"},
+ {0, "[b-d]*", "e"},
+ {0, "[b-d]+", ""},
+ {0, "[b-d]+", "a"},
+ {1, "[b-d]+", "b"},
+ {1, "[b-d]+", "c"},
+ {1, "[b-d]+", "cc"},
+ {1, "[b-d]+", "d"},
+ {0, "[b-d]+", "e"},
+ {1, "[b-d]?", ""},
+ {0, "[b-d]?", "a"},
+ {1, "[b-d]?", "b"},
+ {1, "[b-d]?", "c"},
+ {0, "[b-d]?", "cc"},
+ {1, "[b-d]?", "d"},
+ {0, "[b-d]?", "e"},
+ {1, "h(e|a)llo", "hello"},
+ {1, "h(e|a)llo", "hallo"},
+ {1, "h(e|a)+llo", "haello"},
+ {1, "h(e|a)*llo", "hllo"},
+ {1, "h(e|a)?llo", "hllo"},
+ {1, "h(e|a)?llo", "hello"},
+ {1, "h(e|a)*llo*", "haeeeallooo"},
+ {1, "(ab|a)(bc|c)", "abc"},
+ {0, "(ab|a)(bc|c)", "acb"},
+ {1, "(ab)c|abc", "abc"},
+ {0, "(ab)c|abc", "ab"},
+ {1, "(a*)(b?)(b+)", "aaabbbb"},
+ {0, "(a*)(b?)(b+)", "aaaa"},
+ {1, "((a|a)|a)", "a"},
+ {0, "((a|a)|a)", "aa"},
+ {1, "(a*)(a|aa)", "aaaa"},
+ {0, "(a*)(a|aa)", "b"},
+ {1, "a(b)|c(d)|a(e)f", "aef"},
+ {0, "a(b)|c(d)|a(e)f", "adf"},
+ {1, "(a|b)c|a(b|c)", "ac"},
+ {0, "(a|b)c|a(b|c)", "acc"},
+ {1, "(a|b)c|a(b|c)", "ab"},
+ {0, "(a|b)c|a(b|c)", "acb"},
+ {1, "(a|b)*c|(a|ab)*c", "abc"},
+ {0, "(a|b)*c|(a|ab)*c", "bbbcabbbc"},
+ {1, "a?(ab|ba)ab", "abab"},
+ {0, "a?(ab|ba)ab", "aaabab"},
+ {1, "(aa|aaa)*|(a|aaaaa)", "aa"},
+ {1, "(a)(b)(c)", "abc"},
+ {1, "((((((((((x))))))))))", "x"},
+ {1, "((((((((((x))))))))))*", "xx"},
+ {1, "a?(ab|ba)*", "ababababababababababababababababa"},
+ {1, "a*a*a*a*a*b", "aaaaaaaab"},
+ {1, "abc", "abc"},
+ {1, "ab*c", "abc"},
+ {1, "ab*bc", "abbc"},
+ {1, "ab*bc", "abbbbc"},
+ {1, "ab+bc", "abbc"},
+ {1, "ab+bc", "abbbbc"},
+ {1, "ab?bc", "abbc"},
+ {1, "ab?bc", "abc"},
+ {1, "ab|cd", "ab"},
+ {1, "(a)b(c)", "abc"},
+ {1, "a*", "aaa"},
+ {1, "(a+|b)*", "ab"},
+ {1, "(a+|b)+", "ab"},
+ {1, "a|b|c|d|e", "e"},
+ {1, "(a|b|c|d|e)f", "ef"},
+ {1, "abcd*efg", "abcdefg"},
+ {1, "(ab|ab*)bc", "abc"},
+ {1, "(ab|a)b*c", "abc"},
+ {1, "((a)(b)c)(d)", "abcd"},
+ {1, "(a|ab)(c|bcd)", "abcd"},
+ {1, "(a|ab)(bcd|c)", "abcd"},
+ {1, "(ab|a)(c|bcd)", "abcd"},
+ {1, "(ab|a)(bcd|c)", "abcd"},
+ {1, "((a|ab)(c|bcd))(d*)", "abcd"},
+ {1, "((a|ab)(bcd|c))(d*)", "abcd"},
+ {1, "((ab|a)(c|bcd))(d*)", "abcd"},
+ {1, "((ab|a)(bcd|c))(d*)", "abcd"},
+ {1, "(a|ab)((c|bcd)(d*))", "abcd"},
+ {1, "(a|ab)((bcd|c)(d*))", "abcd"},
+ {1, "(ab|a)((c|bcd)(d*))", "abcd"},
+ {1, "(ab|a)((bcd|c)(d*))", "abcd"},
+ {1, "(a*)(b|abc)", "abc"},
+ {1, "(a*)(abc|b)", "abc"},
+ {1, "((a*)(b|abc))(c*)", "abc"},
+ {1, "((a*)(abc|b))(c*)", "abc"},
+ {1, "(a*)((b|abc))(c*)", "abc"},
+ {1, "(a*)((abc|b)(c*))", "abc"},
+ {1, "(a*)(b|abc)", "abc"},
+ {1, "(a*)(abc|b)", "abc"},
+ {1, "((a*)(b|abc))(c*)", "abc"},
+ {1, "((a*)(abc|b))(c*)", "abc"},
+ {1, "(a*)((b|abc)(c*))", "abc"},
+ {1, "(a*)((abc|b)(c*))", "abc"},
+ {1, "(a|ab)", "ab"},
+ {1, "(ab|a)", "ab"},
+ {1, "(a|ab)(b*)", "ab"},
+ {1, "(ab|a)(b*)", "ab"},
+ {1, "(a|b)*c|(a|ab)*c", "abc"},
+};
+
+int
+main(void)
+{
+ const size_t n_tests = sizeof(match_tests) / sizeof(*match_tests);
+
+ for (size_t i = 0; i < n_tests; ++i) {
+ const char* const regexp = match_tests[i].pattern;
+ const char* const text = match_tests[i].text;
+ const bool should_match = match_tests[i].match;
+
+ RerexPattern* pattern = NULL;
+ size_t end = 0;
+ const RerexStatus st = rerex_compile(regexp, &end, &pattern);
+
+ assert(!st);
+
+ RerexMatcher* const matcher = rerex_new_matcher(pattern);
+ const bool matches = rerex_match(matcher, text);
+
+ assert(matches == should_match);
+
+ rerex_free_matcher(matcher);
+ rerex_free_pattern(pattern);
+ }
+
+ return 0;
+}
diff --git a/subprojects/rerex/test/test_syntax.c b/subprojects/rerex/test/test_syntax.c
new file mode 100644
index 00000000..9cff8855
--- /dev/null
+++ b/subprojects/rerex/test/test_syntax.c
@@ -0,0 +1,99 @@
+/*
+ Copyright 2020 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.
+*/
+
+// Tests that regex syntax errors are reported correctly
+
+#undef NDEBUG
+
+#include "rerex/rerex.h"
+
+#include <assert.h>
+#include <stdint.h>
+#include <string.h>
+
+typedef struct {
+ RerexStatus status;
+ unsigned offset;
+ const char* pattern;
+} BadSyntaxTestCase;
+
+static const BadSyntaxTestCase syntax_tests[] = {
+ {REREX_EXPECTED_CHAR, 1, "a\b"},
+ {REREX_EXPECTED_CHAR, 1, "a\x7F"},
+ {REREX_EXPECTED_ELEMENT, 1, "[\b]"},
+ {REREX_EXPECTED_ELEMENT, 1, "[\x7F]"},
+ {REREX_EXPECTED_ELEMENT, 2, "[a\b]"},
+ {REREX_EXPECTED_ELEMENT, 2, "[a\x7F]"},
+ {REREX_EXPECTED_ELEMENT, 3, "[a-\b]"},
+ {REREX_EXPECTED_ELEMENT, 3, "[a-\x7F]"},
+ {REREX_EXPECTED_RBRACKET, 2, "[\\n]"},
+ {REREX_EXPECTED_RPAREN, 2, "(a"},
+ {REREX_EXPECTED_SPECIAL, 1, "\\n"},
+ {REREX_UNEXPECTED_END, 1, "("},
+ {REREX_UNEXPECTED_END, 1, "["},
+ {REREX_UNEXPECTED_END, 2, "[a"},
+ {REREX_UNEXPECTED_END, 3, "(a|"},
+ {REREX_UNEXPECTED_END, 3, "[a-"},
+ {REREX_UNEXPECTED_END, 4, "[a-z"},
+ {REREX_UNEXPECTED_END, 4, "[a-z"},
+ {REREX_UNEXPECTED_SPECIAL, 0, "{"},
+ {REREX_UNEXPECTED_SPECIAL, 0, "}"},
+ {REREX_UNEXPECTED_SPECIAL, 0, "?"},
+ {REREX_UNEXPECTED_SPECIAL, 1, "[]]"},
+ {REREX_UNEXPECTED_SPECIAL, 2, "a|?"},
+ {REREX_UNEXPECTED_SPECIAL, 3, "(a|?)"},
+ {REREX_UNEXPECTED_SPECIAL, 3, "[[]]"},
+ {REREX_UNEXPECTED_SPECIAL, 3, "[a]]"},
+ {REREX_UNEXPECTED_SPECIAL, 4, "[A-]]"},
+ {REREX_UNEXPECTED_SPECIAL, 4, "[a[]]"},
+ {REREX_UNEXPECTED_SPECIAL, 5, "[A-[]]"},
+ {REREX_UNORDERED_RANGE, 4, "[z-a]"},
+};
+
+static void
+test_status(void)
+{
+ assert(!strcmp(rerex_strerror((RerexStatus)INT32_MAX), "Unknown error"));
+}
+
+static void
+test_syntax(void)
+{
+ const size_t n_tests = sizeof(syntax_tests) / sizeof(*syntax_tests);
+
+ for (size_t i = 0; i < n_tests; ++i) {
+ const char* const regexp = syntax_tests[i].pattern;
+ const size_t offset = syntax_tests[i].offset;
+ const RerexStatus status = syntax_tests[i].status;
+
+ RerexPattern* pattern = NULL;
+ size_t end = 0;
+ const RerexStatus st = rerex_compile(regexp, &end, &pattern);
+
+ assert(st == status);
+ assert(!pattern);
+ assert(strcmp(rerex_strerror(st), rerex_strerror(REREX_SUCCESS)));
+ assert(end == offset);
+ }
+}
+
+int
+main(void)
+{
+ test_status();
+ test_syntax();
+ return 0;
+}
diff --git a/subprojects/rerex/test/test_xsd.c b/subprojects/rerex/test/test_xsd.c
new file mode 100644
index 00000000..81104a99
--- /dev/null
+++ b/subprojects/rerex/test/test_xsd.c
@@ -0,0 +1,522 @@
+/*
+ Copyright 2020 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.
+*/
+
+/*
+ Tests more realistic patterns and using a matcher multiple times.
+
+ The patterns here were written by myself and may have bugs. XSD datatypes
+ unfortunately don't have canonical patterns, though sometimes they
+ infuriatingly say that they are constrained by a pattern... which is not
+ given. XML is terrible, don't use it, but the XSD datatypes are more
+ generally useful. Some patterns here slightly fuzzy (for example, with
+ dates), so matching does not necessary mean the value itself is valid.
+
+ These don't have much to do with Rerex itself, but it's something I needed it
+ for, and having some more realistic tests is nice. These also test reusing a
+ matcher, unlike the basic match tests.
+*/
+
+#undef NDEBUG
+
+#include "rerex/rerex.h"
+
+#include <assert.h>
+#include <stddef.h>
+
+static void
+test_pattern(const char* const regexp,
+ const char* const matching[],
+ const char* const nonmatching[])
+{
+ RerexPattern* pattern = NULL;
+ size_t end = 0;
+ const RerexStatus st = rerex_compile(regexp, &end, &pattern);
+
+ assert(!st);
+
+ RerexMatcher* const matcher = rerex_new_matcher(pattern);
+
+ for (const char* const* m = matching; *m; ++m) {
+ assert(rerex_match(matcher, *m));
+ }
+
+ for (const char* const* n = nonmatching; *n; ++n) {
+ assert(!rerex_match(matcher, *n));
+ }
+
+ rerex_free_matcher(matcher);
+ rerex_free_pattern(pattern);
+}
+
+static void
+test_base64Binary(void)
+{
+ static const char* const regexp =
+ "(([A-Za-z0-9+/] *[A-Za-z0-9+/] *[A-Za-z0-9+/] *[A-Za-z0-9+/] *)*" //
+ "(([A-Za-z0-9+/] *[A-Za-z0-9+/] *[A-Za-z0-9+/] *[A-Za-z0-9+/])|" //
+ "([A-Za-z0-9+/] *[A-Za-z0-9+/] *[AEIMQUYcgkosw048] *=)|" //
+ "([A-Za-z0-9+/] *[AQgw] *= *=)))?";
+
+ static const char* const good[] = {
+ "0FB8",
+ "0fb8",
+ "0 FB8 0F+9",
+ "0F+40A8=",
+ "0F+40A==",
+ "",
+ NULL,
+ };
+
+ static const char* const bad[] = {
+ " 0FB8",
+ "0FB8 ",
+ " 0FB8 ",
+ "FB8",
+ "==0F",
+ "0F+40A9=",
+ "0F+40B==",
+ NULL,
+ };
+
+ test_pattern(regexp, good, bad);
+}
+
+static void
+test_boolean(void)
+{
+ static const char* const regexp = "(true|false|0|1)";
+ static const char* const good[] = {"true", "false", "0", "1", NULL};
+ static const char* const bad[] = {"TRUE", "T", "", NULL};
+
+ test_pattern(regexp, good, bad);
+}
+
+static void
+test_date(void)
+{
+ static const char* const regexp = //
+ "-?[0-9][0-9][0-9][0-9][0-9]*" //
+ "-(0[1-9]|1[0-2])" //
+ "-(0[1-9]|[12][0-9]|3[01])" //
+ "(Z|[-+][0-2][0-9]:[0-5][0-9])?";
+
+ static const char* const good[] = {
+ "2004-04-12",
+ "-0045-01-01",
+ "12004-04-12",
+ "2004-04-12-05:00",
+ "2004-04-12Z",
+ "2001-10-26",
+ "2001-10-26+02:00",
+ "2001-10-26Z",
+ "2001-10-26+00:00",
+ "-2001-10-26",
+ "-20000-04-01",
+ NULL,
+ };
+
+ static const char* const bad[] = {
+ "99-04-12",
+ "2004-4-2",
+ "2004/04/02",
+ "04-12-2004",
+ // "2004-04-31", // Not quite that clever...
+ "2001-10",
+ "2001-10-32",
+ "2001-13-26+02:00",
+ "01-10-26",
+ "",
+ NULL,
+ };
+
+ test_pattern(regexp, good, bad);
+}
+
+static void
+test_decimal(void)
+{
+ static const char* const regexp =
+ "[+-]?(([0-9]+[.]?[0-9]*)|([0-9]*[.]?[0-9]+))";
+
+ static const char* const good[] = {
+ "3.0",
+ "-3.0",
+ "+3.5",
+ "3",
+ ".3",
+ "3.",
+ "0",
+ "-.3",
+ "0003.0",
+ "3.0000",
+ "-456",
+ NULL,
+ };
+
+ static const char* const bad[] = {"3,5", ".", "", NULL};
+
+ test_pattern(regexp, good, bad);
+}
+
+// Tests both xsd:float and xsd:double, which are lexically identical
+static void
+test_float(void)
+{
+ static const char* const regexp = "-?INF|NaN|[+-]?(([0-9]+[.]?[0-9]*)|([0-"
+ "9]*[.]?[0-9]+))([eE][-+]?[0-9]+)?";
+
+ static const char* const good[] = {
+ "-3E2",
+ "4268.22752E11",
+ "+24.3e-3",
+ "12",
+ "+3.5",
+ "INF",
+ "-INF",
+ "-0",
+ "NaN",
+ NULL,
+ };
+
+ static const char* const bad[] = {
+ "-3E2.4",
+ "12E",
+ "+INF",
+ "NAN",
+ "",
+ NULL,
+ };
+
+ test_pattern(regexp, good, bad);
+}
+
+static void
+test_gDay(void)
+{
+ static const char* const regexp =
+ "---(0[1-9]|[12][0-9]|3[01])(Z|[-+][0-2][0-9]:[0-5][0-9])?";
+
+ static const char* const good[] = {
+ "---02",
+ "---01",
+ "---01Z",
+ "---01+02:00",
+ "---01-04:00",
+ "---15",
+ "---31",
+ NULL,
+ };
+
+ static const char* const bad[] = {
+ "02",
+ "---2",
+ "---32",
+ "--30-",
+ "---35",
+ "---5",
+ "15",
+ "",
+ NULL,
+ };
+
+ test_pattern(regexp, good, bad);
+}
+
+static void
+test_gMonth(void)
+{
+ static const char* const regexp =
+ "--(0[1-9]|1[0-2])(Z|[-+][0-2][0-9]:[0-5][0-9])?";
+
+ static const char* const good[] = {
+ "--04",
+ "--04-05:00",
+ "--05",
+ "--11Z",
+ "--11+02:00",
+ "--11-04:00",
+ "--02",
+ NULL,
+ };
+
+ static const char* const bad[] = {
+ "2004-04",
+ "04",
+ "--4",
+ "--13",
+ "-01-",
+ "--13",
+ "--1",
+ "01",
+ "",
+ NULL,
+ };
+
+ test_pattern(regexp, good, bad);
+}
+
+static void
+test_gMonthDay(void)
+{
+ static const char* const regexp = //
+ "--(0[1-9]|1[0-2])" //
+ "-(0[1-9]|[12][0-9]|3[01])" //
+ "(Z|[-+][0-2][0-9]:[0-5][0-9])?";
+
+ static const char* const good[] = {
+ "--04-12",
+ "--04-12Z",
+ "--05-01",
+ "--11-01Z",
+ "--11-01+02:00",
+ "--11-01-04:00",
+ "--11-15",
+ "--02-29",
+ NULL,
+ };
+
+ static const char* const bad[] = {
+ "04-12",
+ // "--04-31", Not quite that clever...
+ "--4-6",
+ "-01-30-",
+ "--01-35",
+ "--1-5",
+ "01-15",
+ "",
+ NULL,
+ };
+
+ test_pattern(regexp, good, bad);
+}
+
+static void
+test_gYear(void)
+{
+ static const char* const regexp =
+ "-?[0-9][0-9][0-9][0-9][0-9]*(Z|[-+][0-2][0-9]:[0-5][0-9])?";
+
+ static const char* const good[] = {
+ "2004",
+ "2004-05:00",
+ "12004",
+ "0922",
+ "-0045",
+ "2001+02:00",
+ "2001Z",
+ "2001+00:00",
+ "-2001",
+ "-20000",
+ NULL,
+ };
+
+ static const char* const bad[] = {
+ "99",
+ "922",
+ "01",
+ "2001-12",
+ "",
+ NULL,
+ };
+
+ test_pattern(regexp, good, bad);
+}
+
+static void
+test_gYearMonth(void)
+{
+ static const char* const regexp = //
+ "-?[0-9][0-9][0-9][0-9][0-9]*" //
+ "-(0[1-9]|1[0-2])" //
+ "(Z|[-+][0-2][0-9]:[0-5][0-9])?";
+
+ static const char* const good[] = {
+ "2001-10",
+ "2001-10+02:00",
+ "2001-10Z",
+ "2001-10+00:00",
+ "-2001-10",
+ "-20000-04",
+ "2004-04-05:00",
+ NULL,
+ };
+
+ static const char* const bad[] = {
+ "2001",
+ "2001-13",
+ "2001-13-26+02:00",
+ "01-10",
+ "99-04",
+ "2004",
+ "2004-4",
+ "2004-13",
+ "",
+ NULL,
+ };
+
+ test_pattern(regexp, good, bad);
+}
+
+static void
+test_hexBinary(void)
+{
+ static const char* const regexp = "([0-9A-Fa-f][0-9A-Fa-f])*";
+ static const char* const good[] = {"0FB8", "0fb8", "", NULL};
+ static const char* const bad[] = {"F", "FB8", NULL};
+
+ test_pattern(regexp, good, bad);
+}
+
+static void
+test_integer(void)
+{
+ static const char* const regexp = "[-+]?[0-9]+";
+ static const char* const good[] = {"122", "00122", "0", "-3", "+3", NULL};
+ static const char* const bad[] = {"3.", "3.0", "A", "", NULL};
+
+ test_pattern(regexp, good, bad);
+}
+
+static void
+test_language(void)
+{
+ static const char* const regexp = //
+ "[a-zA-Z][a-zA-Z]?[a-zA-Z]?[a-zA-Z]?" //
+ "[a-zA-Z]?[a-zA-Z]?[a-zA-Z]?[a-zA-Z]?" //
+ "(-[a-zA-Z0-9][a-zA-Z0-9]?[a-zA-Z0-9]?[a-zA-Z0-9]?" //
+ "[a-zA-Z0-9]?[a-zA-Z0-9]?[a-zA-Z0-9]?[a-zA-Z0-9]?)*";
+
+ static const char* const good[] = {
+ "en",
+ "en-GB",
+ "en-US",
+ "fr",
+ "fr-FR",
+ "fr-CA",
+ "de",
+ "zh",
+ "ja",
+ "ko",
+ "i-navajo",
+ "x-Newspeak",
+ "any-value-with-short-parts",
+ NULL,
+ };
+
+ static const char* const bad[] = {
+ "longerThan8",
+ "even-longerThan8",
+ "longererThan8-first",
+ "last-longererThan8",
+ "middle-longererThan8-CA",
+ "",
+ NULL,
+ };
+
+ test_pattern(regexp, good, bad);
+}
+
+static void
+test_nonNegativeInteger(void)
+{
+ static const char* const regexp = "[+]?[0-9]+";
+ static const char* const good[] = {"+3", "122", "0", "0012", "+123", NULL};
+ static const char* const bad[] = {"-3", "3.0", "", NULL};
+
+ test_pattern(regexp, good, bad);
+}
+
+static void
+test_nonPositiveInteger(void)
+{
+ static const char* const regexp = "(0|-[0-9]+)";
+ static const char* const good[] = {"-3", "-0", "-00122", NULL};
+ static const char* const bad[] = {"122", "+3", "3.", "3.0", "", NULL};
+
+ test_pattern(regexp, good, bad);
+}
+
+static void
+test_positiveInteger(void)
+{
+ static const char* const regexp = "[+]?[0-9]*[1-9]+[0-9]*";
+ static const char* const good[] = {"122", "+3", "00122", NULL};
+ static const char* const bad[] = {"0", "-3", "3.0", "", NULL};
+
+ test_pattern(regexp, good, bad);
+}
+
+static void
+test_time(void)
+{
+ static const char* const regexp =
+ "(([0-1][0-9])|(2[0-4])):[0-5][0-9]:[0-5][0-9](.[0-9]+)?(Z|[-+][0-2][0-"
+ "9]:[0-5][0-9])?";
+
+ static const char* const good[] = {
+ "13:20:00",
+ "13:20:30.5555",
+ "13:20:00-05:00",
+ "13:20:00Z",
+ "00:00:00",
+ "24:00:00",
+ "21:32:52",
+ "21:32:52+02:00",
+ "19:32:52Z",
+ "19:32:52+00:00",
+ "21:32:52.12679",
+ NULL,
+ };
+
+ static const char* const bad[] = {
+ "5:20:00",
+ "13:20",
+ "13:20.5:00",
+ "13:65:00",
+ "21:32",
+ "25:25:10",
+ "-10:00:00",
+ "1:20:10",
+ "",
+ NULL,
+ };
+
+ test_pattern(regexp, good, bad);
+}
+
+int
+main(void)
+{
+ test_base64Binary();
+ test_boolean();
+ test_date();
+ test_decimal();
+ test_float();
+ test_gDay();
+ test_gMonth();
+ test_gMonthDay();
+ test_gYear();
+ test_gYearMonth();
+ test_hexBinary();
+ test_integer();
+ test_language();
+ test_nonNegativeInteger();
+ test_nonPositiveInteger();
+ test_positiveInteger();
+ test_time();
+
+ return 0;
+}