[openssh-commits] [openssh] 02/05: upstream: flesh out match_pattern() tests, including a new
git+noreply at mindrot.org
git+noreply at mindrot.org
Sun May 31 22:29:11 AEST 2026
This is an automated email from the git hooks/post-receive script.
djm pushed a commit to branch master
in repository openssh.
commit 633a4c1a24605e48c46fe76afc0814d20593fc00
Author: djm at openbsd.org <djm at openbsd.org>
AuthorDate: Sun May 31 04:20:58 2026 +0000
upstream: flesh out match_pattern() tests, including a new
comparison test of the new NFA-based implementation against the original one
for all possible combinations of short inputs and patterns constructed from a
small dictionary of possibilities.
OpenBSD-Regress-ID: a18e86c95afb6243ac270468f5dd0ab4a78c8074
---
regress/unittests/match/tests.c | 202 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 200 insertions(+), 2 deletions(-)
diff --git a/regress/unittests/match/tests.c b/regress/unittests/match/tests.c
index 163a3a255..58d4ee7c8 100644
--- a/regress/unittests/match/tests.c
+++ b/regress/unittests/match/tests.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: tests.c,v 1.9 2025/04/15 04:00:42 djm Exp $ */
+/* $OpenBSD: tests.c,v 1.10 2026/05/31 04:20:58 djm Exp $ */
/*
* Regress test for matching functions
*
@@ -17,6 +17,142 @@
#include "match.h"
+/* Original match_pattern() implementation; has bad worst-case behaviour */
+static int
+match_pattern_original(const char *s, const char *pattern)
+{
+ for (;;) {
+ /* If at end of pattern, accept if also at end of string. */
+ if (!*pattern)
+ return !*s;
+
+ if (*pattern == '*') {
+ /* Skip this and any consecutive asterisks. */
+ while (*pattern == '*')
+ pattern++;
+
+ /* If at end of pattern, accept immediately. */
+ if (!*pattern)
+ return 1;
+
+ /* If next character in pattern is known, optimize. */
+ if (*pattern != '?' && *pattern != '*') {
+ /*
+ * Look instances of the next character in
+ * pattern, and try to match starting from
+ * those.
+ */
+ for (; *s; s++)
+ if (*s == *pattern &&
+ match_pattern_original(s + 1,
+ pattern + 1))
+ return 1;
+ /* Failed. */
+ return 0;
+ }
+ /*
+ * Move ahead one character at a time and try to
+ * match at each position.
+ */
+ for (; *s; s++)
+ if (match_pattern_original(s, pattern))
+ return 1;
+ /* Failed. */
+ return 0;
+ }
+ /*
+ * There must be at least one more character in the string.
+ * If we are at the end, fail.
+ */
+ if (!*s)
+ return 0;
+
+ /* Check if the next character of the string is acceptable. */
+ if (*pattern != '?' && *pattern != *s)
+ return 0;
+
+ /* Move to the next character, both in string and in pattern. */
+ s++;
+ pattern++;
+ }
+ /* NOTREACHED */
+}
+
+/* n^x for size_t */
+static size_t
+pow_size(size_t base, size_t exp)
+{
+ size_t r = 1;
+
+ ASSERT_SIZE_T_NE(base, 0);
+ while (exp-- != 0) {
+ ASSERT_SIZE_T_LE(r, SIZE_MAX / base);
+ r *= base;
+ }
+ return r;
+}
+
+static void
+make_word(size_t v, const char *alphabet, size_t alphabet_len,
+ char *word, size_t wordlen)
+{
+ size_t i;
+
+ for (i = 0; i < wordlen; i++) {
+ word[i] = alphabet[v % alphabet_len];
+ v /= alphabet_len;
+ }
+ word[wordlen] = '\0';
+}
+
+#define PATTERN_LEN 8
+#define INPUT_LEN 7
+
+static void
+match_pattern_check_one_input(const char *input, const char *pattern_alphabet)
+{
+ char pattern[PATTERN_LEN];
+ size_t len, i, npatterns;
+ int actual, expected;
+
+ for (len = 0; len < sizeof(pattern); len++) {
+ /* Check with all patterns of this size using alphabet */
+ npatterns = pow_size(strlen(pattern_alphabet), len);
+ for (i = 0; i < npatterns; i++) {
+ make_word(i, pattern_alphabet,
+ strlen(pattern_alphabet), pattern, len);
+ actual = match_pattern(input, pattern);
+ expected = match_pattern_original(input, pattern);
+ test_subtest_info("input=\"%s\" pattern=\"%s\"",
+ input, pattern);
+ ASSERT_INT_EQ(actual, expected);
+ }
+ }
+}
+
+/*
+ * Check current match_pattern against original one with an exhaustive
+ * combination of patterns and inputs.
+ */
+static void
+match_pattern_exhaustive_original(void)
+{
+ const char *pattern_alphabet = "abx?*";
+ const char *input_alphabet = "abc";
+ char input[INPUT_LEN];
+ size_t len, i, ninputs;
+
+ for (len = 0; len < sizeof(input); len++) {
+ /* Check every possible input from alphabet of this size */
+ ninputs = pow_size(strlen(input_alphabet), len);
+ for (i = 0; i < ninputs; i++) {
+ make_word(i, input_alphabet,
+ strlen(input_alphabet), input, len);
+ match_pattern_check_one_input(input, pattern_alphabet);
+ }
+ }
+}
+
void
tests(void)
{
@@ -26,22 +162,84 @@ tests(void)
ASSERT_INT_EQ(match_pattern("aaa", ""), 0);
ASSERT_INT_EQ(match_pattern("aaa", "aaaa"), 0);
ASSERT_INT_EQ(match_pattern("aaaa", "aaa"), 0);
+ ASSERT_INT_EQ(match_pattern("abc", "abc"), 1);
+ ASSERT_INT_EQ(match_pattern("abc", "abd"), 0);
+ ASSERT_INT_EQ(match_pattern("abc", "abcd"), 0);
+ ASSERT_INT_EQ(match_pattern("abcd", "abc"), 0);
TEST_DONE();
TEST_START("match_pattern wildcard");
ASSERT_INT_EQ(match_pattern("", "*"), 1);
+ ASSERT_INT_EQ(match_pattern("", "**"), 1);
+ ASSERT_INT_EQ(match_pattern("", "***"), 1);
+ ASSERT_INT_EQ(match_pattern("", "?"), 0);
+ ASSERT_INT_EQ(match_pattern("", "*?"), 0);
+ ASSERT_INT_EQ(match_pattern("", "?*"), 0);
+ ASSERT_INT_EQ(match_pattern("", "**a*"), 0);
ASSERT_INT_EQ(match_pattern("a", "?"), 1);
ASSERT_INT_EQ(match_pattern("aa", "a?"), 1);
ASSERT_INT_EQ(match_pattern("a", "*"), 1);
ASSERT_INT_EQ(match_pattern("aa", "a*"), 1);
ASSERT_INT_EQ(match_pattern("aa", "?*"), 1);
- ASSERT_INT_EQ(match_pattern("aa", "**"), 1);
ASSERT_INT_EQ(match_pattern("aa", "?a"), 1);
ASSERT_INT_EQ(match_pattern("aa", "*a"), 1);
ASSERT_INT_EQ(match_pattern("ba", "a?"), 0);
ASSERT_INT_EQ(match_pattern("ba", "a*"), 0);
ASSERT_INT_EQ(match_pattern("ab", "?a"), 0);
ASSERT_INT_EQ(match_pattern("ab", "*a"), 0);
+ ASSERT_INT_EQ(match_pattern("aa", "**"), 1);
+ ASSERT_INT_EQ(match_pattern("ab", "a***b"), 1);
+ ASSERT_INT_EQ(match_pattern("axb", "a***b"), 1);
+ ASSERT_INT_EQ(match_pattern("axxb", "a***b"), 1);
+ ASSERT_INT_EQ(match_pattern("ax", "a***b"), 0);
+ ASSERT_INT_EQ(match_pattern("abbb", "a*b*b"), 1);
+ ASSERT_INT_EQ(match_pattern("abbb", "a*b*c"), 0);
+ ASSERT_INT_EQ(match_pattern("aaaaaaaaac", "a*a*a*a*b"), 0);
+ ASSERT_INT_EQ(match_pattern("aaaaaaaaab", "a*a*a*a*b"), 1);
+ ASSERT_INT_EQ(match_pattern("ab", "*b"), 1);
+ ASSERT_INT_EQ(match_pattern("ab", "*a*"), 1);
+ ASSERT_INT_EQ(match_pattern("ab", "*a*b"), 1);
+ ASSERT_INT_EQ(match_pattern("ab", "*a*c"), 0);
+ ASSERT_INT_EQ(match_pattern("abc", "a?c"), 1);
+ ASSERT_INT_EQ(match_pattern("abc", "??c"), 1);
+ ASSERT_INT_EQ(match_pattern("abc", "???"), 1);
+ ASSERT_INT_EQ(match_pattern("abc", "a?d"), 0);
+ ASSERT_INT_EQ(match_pattern("ab", "???"), 0);
+ ASSERT_INT_EQ(match_pattern("abc", "ab*"), 1);
+ ASSERT_INT_EQ(match_pattern("ab", "ab*"), 1);
+ ASSERT_INT_EQ(match_pattern("a", "ab*"), 0);
+ ASSERT_INT_EQ(match_pattern("abc", "ab?"), 1);
+ ASSERT_INT_EQ(match_pattern("ab", "ab?"), 0);
+ ASSERT_INT_EQ(match_pattern("abcd", "ab?"), 0);
+ ASSERT_INT_EQ(match_pattern("abc", "?bc"), 1);
+ ASSERT_INT_EQ(match_pattern("abc", "?b*"), 1);
+ ASSERT_INT_EQ(match_pattern("abc", "?c"), 0);
+ ASSERT_INT_EQ(match_pattern("abc", "a*?c"), 1);
+ ASSERT_INT_EQ(match_pattern("ac", "a*?c"), 0);
+ ASSERT_INT_EQ(match_pattern("abbc", "a*?c"), 1);
+ ASSERT_INT_EQ(match_pattern("abc", "a?*c"), 1);
+ ASSERT_INT_EQ(match_pattern("ac", "a?*c"), 0);
+ ASSERT_INT_EQ(match_pattern("abbc", "a?*c"), 1);
+ ASSERT_INT_EQ(match_pattern("abc", "a*?*c"), 1);
+ ASSERT_INT_EQ(match_pattern("ac", "a*?*c"), 0);
+ ASSERT_INT_EQ(match_pattern("abc", "?*c"), 1);
+ ASSERT_INT_EQ(match_pattern("ac", "?*c"), 1);
+ ASSERT_INT_EQ(match_pattern("c", "?*c"), 0);
+ ASSERT_INT_EQ(match_pattern("abc", "*?c"), 1);
+ ASSERT_INT_EQ(match_pattern("ac", "*?c"), 1);
+ ASSERT_INT_EQ(match_pattern("c", "*?c"), 0);
+ ASSERT_INT_EQ(match_pattern("abc", "a?*"), 1);
+ ASSERT_INT_EQ(match_pattern("ab", "a?*"), 1);
+ ASSERT_INT_EQ(match_pattern("a", "a?*"), 0);
+ ASSERT_INT_EQ(match_pattern("abc", "a*?"), 1);
+ ASSERT_INT_EQ(match_pattern("ab", "a*?"), 1);
+ ASSERT_INT_EQ(match_pattern("a", "a*?"), 0);
+ ASSERT_INT_EQ(match_pattern("abb", "a*b"), 1);
+ ASSERT_INT_EQ(match_pattern("abbc", "a*b"), 0);
+ TEST_DONE();
+
+ TEST_START("match_pattern exhaustive original");
+ match_pattern_exhaustive_original();
TEST_DONE();
TEST_START("match_pattern_list");
--
To stop receiving notification emails like this one, please contact
djm at mindrot.org.
More information about the openssh-commits
mailing list