]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
sbuff: Fix advancement functions to return position, and add tests
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Thu, 23 Jul 2020 22:52:38 +0000 (18:52 -0400)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Thu, 23 Jul 2020 22:52:42 +0000 (18:52 -0400)
src/lib/util/sbuff.c
src/lib/util/sbuff.h
src/lib/util/sbuff_tests.c

index a9f6f9121f4475f8ed5337f5af183c30bb5796da..59abf1dfcbbcf89b4a6752e3591e1bf7bddd1266 100644 (file)
@@ -863,10 +863,10 @@ bool fr_sbuff_adv_past_strcase(fr_sbuff_t *sbuff, char const *needle, size_t len
  *
  * @param[in] sbuff            sbuff to search in.
  * @return
- *     - 0, first character is not a whitespace character.
- *     - >0 how many whitespace characters we skipped.
+ *     - true and advance past the need if whitespace occurs next.
+ *     - false and don't advance if the whitespace does not occur next.
  */
-size_t fr_sbuff_adv_past_whitespace(fr_sbuff_t *sbuff)
+bool fr_sbuff_adv_past_whitespace(fr_sbuff_t *sbuff)
 {
 
        size_t          total = 0;
@@ -879,12 +879,12 @@ size_t fr_sbuff_adv_past_whitespace(fr_sbuff_t *sbuff)
 
                p = sbuff->p;
                while ((p < sbuff->end) && isspace(*p)) p++;
-               if (p == sbuff->p) return 0;
+               if (p == sbuff->p) break;
 
                total += fr_sbuff_advance(sbuff, p - sbuff->p);
        } while (p == sbuff->end);      /* Hit the end of the chunk, try again */
 
-       return total;
+       return (total > 0);
 }
 
 /** Wind position to first instance of specified multibyte utf8 char
@@ -895,13 +895,13 @@ size_t fr_sbuff_adv_past_whitespace(fr_sbuff_t *sbuff)
  * @param[in,out] sbuff                to search in.
  * @param[in] chr              to search for.
  * @return
- *     - 0, no instances found.
- *     - >0 the offset at which the first occurrence of the multi-byte chr was found.
+ *     - NULL, no instances found.
+ *     - The position of the first character.
  */
-size_t fr_sbuff_adv_to_chr_utf8(fr_sbuff_t *sbuff, char *chr)
+char *fr_sbuff_adv_to_chr_utf8(fr_sbuff_t *sbuff, char const *chr)
 {
-       size_t          total = 0;
        size_t          clen = strlen(chr);
+       fr_sbuff_t      our_sbuff = FR_SBUFF_NO_ADVANCE(sbuff);
 
        CHECK_SBUFF_INIT(sbuff);
 
@@ -912,18 +912,18 @@ size_t fr_sbuff_adv_to_chr_utf8(fr_sbuff_t *sbuff, char *chr)
                 *      Ensure we have enough chars to match
                 *      the needle.
                 */
-               if (FR_SBUFF_CANT_EXTEND_LOWAT(sbuff, clen)) break;
+               if (FR_SBUFF_CANT_EXTEND_LOWAT(&our_sbuff, clen)) break;
 
-               found = fr_utf8_strchr(NULL, fr_sbuff_current(sbuff), fr_sbuff_remaining(sbuff), chr);
+               found = fr_utf8_strchr(NULL, fr_sbuff_current(&our_sbuff), fr_sbuff_remaining(&our_sbuff), chr);
                if (found) {
-                       total += fr_sbuff_set(sbuff, found);
-                       break;
+                       (void)fr_sbuff_set(sbuff, found);
+                       return sbuff->p;
                }
 
-               total += fr_sbuff_advance(sbuff, 1);    /* Can't advance by clen, because we're searching for a sequence */
+               (void)fr_sbuff_advance(&our_sbuff, 1);  /* Can't advance by clen, because we're searching for a sequence */
        }
 
-       return total;
+       return NULL;
 }
 
 /** Wind position to first instance of specified char
@@ -931,29 +931,30 @@ size_t fr_sbuff_adv_to_chr_utf8(fr_sbuff_t *sbuff, char *chr)
  * @param[in,out] sbuff                to search in.
  * @param[in] c                        to search for.
  * @return
- *     - 0, no instances found.
- *     - >0 the offset at which the first occurrence of the char was found.
+ *     - NULL, no instances found.
+ *     - The position of the first character.
  */
-size_t fr_sbuff_adv_to_chr(fr_sbuff_t *sbuff, char c)
+char *fr_sbuff_adv_to_chr(fr_sbuff_t *sbuff, char c)
 {
-       size_t          total = 0;
-       char const      *found;
+       fr_sbuff_t      our_sbuff = FR_SBUFF_NO_ADVANCE(sbuff);
 
        CHECK_SBUFF_INIT(sbuff);
 
        for (;;) {
-               if (FR_SBUFF_CANT_EXTEND(sbuff)) break;
+               char const      *found;
 
-               found = memchr(fr_sbuff_current(sbuff), c, fr_sbuff_remaining(sbuff));
+               if (FR_SBUFF_CANT_EXTEND(&our_sbuff)) break;
+
+               found = memchr(fr_sbuff_current(&our_sbuff), c, fr_sbuff_remaining(&our_sbuff));
                if (found) {
-                       total += fr_sbuff_set(sbuff, found);
-                       break;
+                       (void)fr_sbuff_set(sbuff, found);
+                       return sbuff->p;
                }
 
-               total += fr_sbuff_set(sbuff, sbuff->end);
+               (void)fr_sbuff_set(&our_sbuff, our_sbuff.end);
        }
 
-       return total;
+       return NULL;
 }
 
 /** Wind position to the first instance of the specified needle
@@ -962,12 +963,12 @@ size_t fr_sbuff_adv_to_chr(fr_sbuff_t *sbuff, char c)
  * @param[in] needle           to search for.
  * @param[in] len              Length of the needle.  -1 to use strlen.
  * @return
- *     - 0, no instances found.
- *     - >0 the offset at which the first occurrence of the needle was found.
+ *     - NULL, no instances found.
+ *     - The position of the first character.
  */
-size_t fr_sbuff_adv_to_str(fr_sbuff_t *sbuff, char const *needle, size_t len)
+char *fr_sbuff_adv_to_str(fr_sbuff_t *sbuff, char const *needle, size_t len)
 {
-       size_t          total = 0;
+       fr_sbuff_t      our_sbuff = FR_SBUFF_NO_ADVANCE(sbuff);
        char const      *found;
 
        CHECK_SBUFF_INIT(sbuff);
@@ -975,13 +976,11 @@ size_t fr_sbuff_adv_to_str(fr_sbuff_t *sbuff, char const *needle, size_t len)
        if (len == SIZE_MAX) len = strlen(needle);
        if (!len) return 0;
 
-       for (;;) {
-               if (FR_SBUFF_CANT_EXTEND_LOWAT(sbuff, len)) break;
-
-               found = memmem(fr_sbuff_current(sbuff), fr_sbuff_remaining(sbuff), needle, len);
+       while (!FR_SBUFF_CANT_EXTEND_LOWAT(&our_sbuff, len)) {
+               found = memmem(fr_sbuff_current(&our_sbuff), fr_sbuff_remaining(&our_sbuff), needle, len);
                if (found) {
-                       total += fr_sbuff_set(sbuff, found);
-                       break;
+                       (void)fr_sbuff_set(sbuff, found);
+                       return sbuff->p;
                }
 
                /*
@@ -989,10 +988,10 @@ size_t fr_sbuff_adv_to_str(fr_sbuff_t *sbuff, char const *needle, size_t len)
                 *      the end of the buffer so
                 *      don't advance too far.
                 */
-               total += fr_sbuff_advance(sbuff, (fr_sbuff_remaining(sbuff) - len) - 1);
+               (void)fr_sbuff_advance(&our_sbuff, (fr_sbuff_remaining(&our_sbuff) - len) - 1);
        }
 
-       return total;
+       return NULL;
 }
 
 /** Wind position to the first instance of the specified needle
@@ -1001,37 +1000,34 @@ size_t fr_sbuff_adv_to_str(fr_sbuff_t *sbuff, char const *needle, size_t len)
  * @param[in] needle           to search for.
  * @param[in] len              Length of the needle.  -1 to use strlen.
  * @return
- *     - 0, no instances found.
- *     - >0 the offset at which the first occurrence of the needle was found.
+ *     - NULL, no instances found.
+ *     - The position of the first character.
  */
-size_t fr_sbuff_adv_to_strcase(fr_sbuff_t *sbuff, char const *needle, size_t len)
+char *fr_sbuff_adv_to_strcase(fr_sbuff_t *sbuff, char const *needle, size_t len)
 {
-       size_t          total = 0;
+       fr_sbuff_t      our_sbuff = FR_SBUFF_NO_ADVANCE(sbuff);
 
        CHECK_SBUFF_INIT(sbuff);
 
        if (len == SIZE_MAX) len = strlen(needle);
        if (!len) return 0;
 
-       for (;;) {
+       while (!FR_SBUFF_CANT_EXTEND_LOWAT(&our_sbuff, len)) {
                char *p, *end;
                char const *n_p;
 
-               if (FR_SBUFF_CANT_EXTEND_LOWAT(sbuff, len)) break;
-
-               p = sbuff->p;
-               end = p + len;
-
-               for (p = sbuff->p, n_p = needle; (p < end) && (tolower(*p) != tolower(*n_p)); p++, n_p++);
+               for (p = our_sbuff.p, n_p = needle, end = our_sbuff.p + len;
+                    (p < end) && (tolower(*p) == tolower(*n_p));
+                    p++, n_p++);
                if (p == end) {
-                       total += fr_sbuff_set(sbuff, end);
-                       break;
+                       (void)fr_sbuff_set(sbuff, our_sbuff.p);
+                       return sbuff->p;
                }
 
-               total += fr_sbuff_advance(sbuff, 1);
+               (void)fr_sbuff_advance(&our_sbuff, 1);
        }
 
-       return total;
+       return NULL;
 }
 
 /** Return true if the current char matches, and if it does, advance
@@ -1046,7 +1042,7 @@ bool fr_sbuff_next_if_char(fr_sbuff_t *sbuff, char c)
 {
        CHECK_SBUFF_INIT(sbuff);
 
-       if (!FR_SBUFF_CANT_EXTEND(sbuff)) return false;
+       if (FR_SBUFF_CANT_EXTEND(sbuff)) return false;
 
        if (*sbuff->p != c) return false;
 
@@ -1067,7 +1063,7 @@ bool fr_sbuff_next_unless_char(fr_sbuff_t *sbuff, char c)
 {
        CHECK_SBUFF_INIT(sbuff);
 
-       if (!FR_SBUFF_CANT_EXTEND(sbuff)) return false;
+       if (FR_SBUFF_CANT_EXTEND(sbuff)) return false;
 
        if (*sbuff->p == c) return false;
 
index 4c6ce516d8e3920c14b3135da8485b9cada42bc1..92075604921b458d7c6b0735c5cf8425e1e4662b 100644 (file)
@@ -715,17 +715,17 @@ bool      fr_sbuff_adv_past_strcase(fr_sbuff_t *sbuff, char const *needle, size_t len
 
 #define fr_sbuff_adv_past_strcase_literal(_sbuff, _needle) fr_sbuff_adv_past_strcase(_sbuff, _needle, sizeof(_needle) - 1)
 
-size_t fr_sbuff_adv_past_whitespace(fr_sbuff_t *sbuff);
+bool   fr_sbuff_adv_past_whitespace(fr_sbuff_t *sbuff);
 
-size_t fr_sbuff_adv_to_chr_utf8(fr_sbuff_t *in, char *chr);
+char   *fr_sbuff_adv_to_chr_utf8(fr_sbuff_t *in, char const *chr);
 
-size_t fr_sbuff_adv_to_chr(fr_sbuff_t *in, char c);
+char   *fr_sbuff_adv_to_chr(fr_sbuff_t *in, char c);
 
-size_t fr_sbuff_adv_to_str(fr_sbuff_t *sbuff, char const *needle, size_t len);
+char   *fr_sbuff_adv_to_str(fr_sbuff_t *sbuff, char const *needle, size_t len);
 
 #define fr_sbuff_adv_to_str_literal(_sbuff, _needle) fr_sbuff_adv_to_str(_sbuff, _needle, sizeof(_needle) - 1)
 
-size_t fr_sbuff_adv_to_strcase(fr_sbuff_t *sbuff, char const *needle, size_t len);
+char   *fr_sbuff_adv_to_strcase(fr_sbuff_t *sbuff, char const *needle, size_t len);
 
 #define fr_sbuff_adv_to_strcase_literal(_sbuff, _needle) fr_sbuff_adv_to_strcase(_sbuff, _needle, sizeof(_needle) - 1)
 
index 75bc9e4f89939a09d3bfb631f7e201fb7dc6fd89..fa8302468d0ed665a6af6848a125bd6afd1881c4 100644 (file)
@@ -22,6 +22,13 @@ do { \
        TEST_MSG("Got length      : %zu", _len); \
 } while(0)
 
+#define TEST_CHECK_LEN(_exp, _got) \
+do { \
+       TEST_CHECK(_exp == _got); \
+       TEST_MSG("Expected length : %zu", (ssize_t)_exp); \
+       TEST_MSG("Got length      : %zu", (ssize_t)_got); \
+} while(0)
+
 #define TEST_CHECK_SLEN(_exp, _got) \
 do { \
        TEST_CHECK(_exp == _got); \
@@ -619,21 +626,237 @@ static void test_adv_past_whitespace(void)
 
        TEST_CASE("Check for token at beginning of string");
        fr_sbuff_init(&sbuff, in, sizeof(in));
-       TEST_CHECK(fr_sbuff_adv_past_whitespace(&sbuff) == 5);
+       TEST_CHECK(fr_sbuff_adv_past_whitespace(&sbuff) == true);
        TEST_CHECK_STRCMP("i am a         test string", sbuff.p);
 
        TEST_CASE("Check for token not at beginning of string");
        fr_sbuff_init(&sbuff, in_ns, sizeof(in_ns));
-       TEST_CHECK(fr_sbuff_adv_past_whitespace(&sbuff) == 0);
+       TEST_CHECK(fr_sbuff_adv_past_whitespace(&sbuff) == false);
        TEST_CHECK_STRCMP("i am a test string", sbuff.p);
 
        TEST_CASE("Check for token with zero length string");
        fr_sbuff_init(&sbuff, in, 0 + 1);
-       TEST_CHECK(fr_sbuff_adv_past_whitespace(&sbuff) == 0);
+       TEST_CHECK(fr_sbuff_adv_past_whitespace(&sbuff) == false);
 
        TEST_CASE("Check for token that is the string");
        fr_sbuff_init(&sbuff, in_ws, sizeof(in_ws));
-       TEST_CHECK(fr_sbuff_adv_past_whitespace(&sbuff) == 5);
+       TEST_CHECK(fr_sbuff_adv_past_whitespace(&sbuff) == true);
+}
+
+static void test_adv_to_utf8(void)
+{
+       fr_sbuff_t      sbuff;
+       char const      in[] = "🥺🥺🥺🥺🍪😀";
+       char            *p;
+
+       TEST_CASE("Check for token at beginning of string");
+       fr_sbuff_init(&sbuff, in, sizeof(in));
+       p = fr_sbuff_adv_to_chr_utf8(&sbuff, "🥺");
+       TEST_CHECK(p == sbuff.p);
+       TEST_CHECK_STRCMP("🥺🥺🥺🥺🍪😀", sbuff.p);
+
+       TEST_CASE("Check for token not at beginning of string");
+       fr_sbuff_init(&sbuff, in, sizeof(in));
+       p = fr_sbuff_adv_to_chr_utf8(&sbuff, "🍪");
+       TEST_CHECK(p == (sbuff.start + (sizeof("🥺🥺🥺🥺") - 1)));
+       TEST_CHECK_STRCMP("🍪😀", p);
+
+       TEST_CASE("Check for token with zero length string");
+       fr_sbuff_init(&sbuff, in, 0 + 1);
+       p = fr_sbuff_adv_to_chr_utf8(&sbuff, "🍪");
+       TEST_CHECK(p == NULL);
+       TEST_CHECK(sbuff.start == sbuff.p);
+
+       TEST_CASE("Check for token at the end of the string");
+       fr_sbuff_init(&sbuff, in, sizeof(in));
+       p = fr_sbuff_adv_to_chr_utf8(&sbuff, "😀");
+       TEST_CHECK(p == sbuff.start + (sizeof("🥺🥺🥺🥺🍪") - 1));
+
+       TEST_CASE("Check for token not in the string");
+       fr_sbuff_init(&sbuff, in, sizeof(in));
+       p = fr_sbuff_adv_to_chr_utf8(&sbuff, "🍆 ");
+       TEST_CHECK(p == NULL);
+}
+
+static void test_adv_to_chr(void)
+{
+       fr_sbuff_t      sbuff;
+       char const      in[] = "AAAAbC";
+       char            *p;
+
+       TEST_CASE("Check for token at beginning of string");
+       fr_sbuff_init(&sbuff, in, sizeof(in));
+       p = fr_sbuff_adv_to_chr(&sbuff, 'A');
+       TEST_CHECK(p == sbuff.p);
+       TEST_CHECK_STRCMP("AAAAbC", sbuff.p);
+
+       TEST_CASE("Check for token not at beginning of string");
+       fr_sbuff_init(&sbuff, in, sizeof(in));
+       p = fr_sbuff_adv_to_chr(&sbuff, 'b');
+       TEST_CHECK(p == (sbuff.start + (sizeof("AAAA") - 1)));
+       TEST_CHECK_STRCMP("bC", p);
+
+       TEST_CASE("Check for token with zero length string");
+       fr_sbuff_init(&sbuff, in, 0 + 1);
+       p = fr_sbuff_adv_to_chr(&sbuff, 'b');
+       TEST_CHECK(p == NULL);
+       TEST_CHECK(sbuff.start == sbuff.p);
+
+       TEST_CASE("Check for token at the end of the string");
+       fr_sbuff_init(&sbuff, in, sizeof(in));
+       p = fr_sbuff_adv_to_chr(&sbuff, 'C');
+       TEST_CHECK(p == sbuff.start + (sizeof("AAAAb") - 1));
+
+       TEST_CASE("Check for token not in the string");
+       fr_sbuff_init(&sbuff, in, sizeof(in));
+       p = fr_sbuff_adv_to_chr(&sbuff, 'D');
+       TEST_CHECK(p == NULL);
+}
+
+static void test_adv_to_str(void)
+{
+       fr_sbuff_t      sbuff;
+       char const      in[] = "i am a test string";
+       char            *p;
+
+       TEST_CASE("Check for token at beginning of string");
+       fr_sbuff_init(&sbuff, in, sizeof(in));
+       p = fr_sbuff_adv_to_str(&sbuff, "i am a test", SIZE_MAX);
+       TEST_CHECK(sbuff.p == p);
+       TEST_CHECK_STRCMP("i am a test string", sbuff.p);
+
+       TEST_CASE("Check for token not at beginning of string");
+       fr_sbuff_init(&sbuff, in, sizeof(in));
+       p = fr_sbuff_adv_to_str(&sbuff, "test", SIZE_MAX);
+       TEST_CHECK(sbuff.p == p);
+       TEST_CHECK_STRCMP("test string", sbuff.p);
+
+       TEST_CASE("Check for token at the end of string");
+       fr_sbuff_init(&sbuff, in, sizeof(in));
+       p = fr_sbuff_adv_to_str(&sbuff, "ing", SIZE_MAX);
+       TEST_CHECK(sbuff.p == p);
+       TEST_CHECK_STRCMP("ing", sbuff.p);
+
+       TEST_CASE("Check for token larger than the string");
+       fr_sbuff_init(&sbuff, in, sizeof(in));
+       p = fr_sbuff_adv_to_str(&sbuff, "i am a test string ", SIZE_MAX);
+       TEST_CHECK(sbuff.p == sbuff.start);
+       TEST_CHECK(p == NULL);
+
+       TEST_CASE("Check for token shorter than string, not in the string");
+       fr_sbuff_init(&sbuff, in, sizeof(in));
+       p = fr_sbuff_adv_to_strcase(&sbuff, "ng ", SIZE_MAX);
+       TEST_CHECK(sbuff.p == sbuff.start);
+       TEST_CHECK(p == NULL);
+
+       TEST_CASE("Check for token with zero length string");
+       fr_sbuff_init(&sbuff, in, 0 + 1);
+       p = fr_sbuff_adv_to_str(&sbuff, "i am a", SIZE_MAX);
+       TEST_CHECK(sbuff.p == sbuff.start);
+       TEST_CHECK(p == NULL);
+
+       TEST_CASE("Check for token that is the string");
+       fr_sbuff_init(&sbuff, in, sizeof(in));
+       p = fr_sbuff_adv_to_str(&sbuff, "i am a test string", SIZE_MAX);
+       TEST_CHECK(sbuff.p == sbuff.start);
+       TEST_CHECK(sbuff.p == p);
+       TEST_CHECK_STRCMP("i am a test string", p);
+}
+
+static void test_adv_to_strcase(void)
+{
+       fr_sbuff_t      sbuff;
+       char const      in[] = "i am a test string";
+       char            *p;
+
+       TEST_CASE("Check for token at beginning of string");
+       fr_sbuff_init(&sbuff, in, sizeof(in));
+       p = fr_sbuff_adv_to_strcase(&sbuff, "i AM a TEST", SIZE_MAX);
+       TEST_CHECK(sbuff.p == p);
+       TEST_CHECK_STRCMP("i am a test string", sbuff.p);
+
+       TEST_CASE("Check for token not at beginning of string");
+       fr_sbuff_init(&sbuff, in, sizeof(in));
+       p = fr_sbuff_adv_to_strcase(&sbuff, "tEst", SIZE_MAX);
+       TEST_CHECK(sbuff.p == p);
+       TEST_CHECK_STRCMP("test string", sbuff.p);
+
+       TEST_CASE("Check for token at the end of string");
+       fr_sbuff_init(&sbuff, in, sizeof(in));
+       p = fr_sbuff_adv_to_strcase(&sbuff, "Ing", SIZE_MAX);
+       TEST_CHECK(sbuff.p == p);
+       TEST_CHECK_STRCMP("ing", sbuff.p);
+
+       TEST_CASE("Check for token larger than the string");
+       fr_sbuff_init(&sbuff, in, sizeof(in));
+       p = fr_sbuff_adv_to_strcase(&sbuff, "i aM a tEst stRIng ", SIZE_MAX);
+       TEST_CHECK(sbuff.p == sbuff.start);
+       TEST_CHECK(p == NULL);
+
+       TEST_CASE("Check for token shorter than string, not in the string");
+       fr_sbuff_init(&sbuff, in, sizeof(in));
+       p = fr_sbuff_adv_to_strcase(&sbuff, "nG ", SIZE_MAX);
+       TEST_CHECK(sbuff.p == sbuff.start);
+       TEST_CHECK(p == NULL);
+
+       TEST_CASE("Check for token with zero length string");
+       fr_sbuff_init(&sbuff, in, 0 + 1);
+       p = fr_sbuff_adv_to_strcase(&sbuff, "i AM a", SIZE_MAX);
+       TEST_CHECK(sbuff.p == sbuff.start);
+       TEST_CHECK(p == NULL);
+
+       TEST_CASE("Check for token that is the string");
+       fr_sbuff_init(&sbuff, in, sizeof(in));
+       p = fr_sbuff_adv_to_strcase(&sbuff, "i AM a teST stRIng", SIZE_MAX);
+       TEST_CHECK(sbuff.p == sbuff.start);
+       TEST_CHECK(sbuff.p == p);
+       TEST_CHECK_STRCMP("i am a test string", p);
+}
+
+static void test_next_if_char(void)
+{
+       fr_sbuff_t      sbuff;
+       char const      in[] = "i ";
+
+       TEST_CASE("Check for advancement on match");
+       fr_sbuff_init(&sbuff, in, sizeof(in));
+       TEST_CHECK(fr_sbuff_next_if_char(&sbuff, 'i') == true);
+       TEST_CHECK_STRCMP(" ", sbuff.p);
+
+       TEST_CASE("Check for non-advancement on non-match");
+       TEST_CHECK(fr_sbuff_next_if_char(&sbuff, 'i') == false);
+       TEST_CHECK_STRCMP(" ", sbuff.p);
+
+       TEST_CASE("Check for advancement at end");
+       TEST_CHECK(fr_sbuff_next_if_char(&sbuff, ' ') == true);
+       TEST_CHECK_STRCMP("", sbuff.p);
+
+       TEST_CASE("Check we can't advance off the end of the buffer");
+       TEST_CHECK(fr_sbuff_next_if_char(&sbuff, ' ') == false);
+       TEST_CHECK_STRCMP("", sbuff.p);
+}
+
+static void test_next_unless_char(void)
+{
+       fr_sbuff_t      sbuff;
+       char const      in[] = "i ";
+
+       TEST_CASE("Check for advancement on non-match");
+       fr_sbuff_init(&sbuff, in, sizeof(in));
+       TEST_CHECK(fr_sbuff_next_unless_char(&sbuff, ' ') == true);
+       TEST_CHECK_STRCMP(" ", sbuff.p);
+
+       TEST_CASE("Check for non-advancement on match");
+       TEST_CHECK(fr_sbuff_next_unless_char(&sbuff, ' ') == false);
+       TEST_CHECK_STRCMP(" ", sbuff.p);
+
+       TEST_CASE("Check for advancement at end");
+       TEST_CHECK(fr_sbuff_next_unless_char(&sbuff, '_') == true);
+       TEST_CHECK_STRCMP("", sbuff.p);
+
+       TEST_CASE("Check we can't advance off the end of the buffer");
+       TEST_CHECK(fr_sbuff_next_unless_char(&sbuff, '_') == false);
+       TEST_CHECK_STRCMP("", sbuff.p);
 }
 
 TEST_LIST = {
@@ -663,5 +886,19 @@ TEST_LIST = {
        { "fr_sbuff_adv_past_strcase",          test_adv_past_strcase },
        { "fr_sbuff_adv_past_whitespace",       test_adv_past_whitespace },
 
+       /*
+        *      Token searching
+        */
+       { "fr_sbuff_adv_to_utf8",               test_adv_to_utf8 },
+       { "fr_sbuff_adv_to_chr",                test_adv_to_chr },
+       { "fr_sbuff_adv_to_str",                test_adv_to_str },
+       { "fr_sbuff_adv_to_strcase",            test_adv_to_strcase },
+
+       /*
+        *      Advancement
+        */
+       { "fr_sbuff_next_if_char",              test_next_if_char },
+       { "fr_sbuff_next_unless_char",          test_next_unless_char },
+
        { NULL }
 };