From 6d908aa702ad59971cd25420858e22a680d9ef6e Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Mon, 29 Sep 2025 14:32:02 -0600 Subject: [PATCH 01/49] `mlib_check` supports an explanatory string with all assertions --- src/common/src/mlib/test.h | 142 +++++++++++++++++++++++++---------- src/common/tests/test-mlib.c | 3 + 2 files changed, 104 insertions(+), 41 deletions(-) diff --git a/src/common/src/mlib/test.h b/src/common/src/mlib/test.h index 4527b2f3e91..c0f937fade9 100644 --- a/src/common/src/mlib/test.h +++ b/src/common/src/mlib/test.h @@ -129,56 +129,98 @@ typedef struct mlib_source_location { #define mlib_check(...) MLIB_ARGC_PICK(_mlib_check, #__VA_ARGS__, __VA_ARGS__) // One arg: #define _mlib_check_argc_2(ArgString, Condition) \ - _mlibCheckConditionSimple(Condition, ArgString, mlib_this_source_location()) + _mlibCheckConditionSimple(Condition, ArgString, NULL, mlib_this_source_location()) // Three args: #define _mlib_check_argc_4(ArgString, A, Operator, B) \ - MLIB_NOTHING(#A, #B) MLIB_PASTE(_mlibCheckCondition_, Operator)(A, B) + MLIB_NOTHING(#A, #B) MLIB_PASTE(_mlibCheckCondition_, Operator)(A, B, NULL) +// Five args: +#define _mlib_check_argc_6(ArgString, A, Operator, B, Infix, Reason) \ + MLIB_NOTHING(#A, #B) MLIB_PASTE(_mlib_check_with_suffix_, Infix)(A, Operator, B, Reason) +#define _mlib_check_with_suffix_because(A, Operator, B, Reason) \ + MLIB_NOTHING(#A, #B) MLIB_PASTE(_mlibCheckCondition_, Operator)(A, B, Reason) // String-compare: -#define _mlibCheckCondition_str_eq(A, B) _mlibCheckStrEq(A, B, #A, #B, mlib_this_source_location()) +#define _mlibCheckCondition_str_eq(A, B, Reason) _mlibCheckStrEq(A, B, #A, #B, Reason, mlib_this_source_location()) // Pointer-compare: -#define _mlibCheckCondition_ptr_eq(A, B) _mlibCheckPtrEq(A, B, #A, #B, mlib_this_source_location()) +#define _mlibCheckCondition_ptr_eq(A, B, Reason) _mlibCheckPtrEq(A, B, #A, #B, Reason, mlib_this_source_location()) // Integer-equal: -#define _mlibCheckCondition_eq(A, B) \ - _mlibCheckIntCmp( \ - mlib_equal, true, "==", mlib_upsize_integer(A), mlib_upsize_integer(B), #A, #B, mlib_this_source_location()) +#define _mlibCheckCondition_eq(A, B, Reason) \ + _mlibCheckIntCmp(mlib_equal, \ + true, \ + "==", \ + mlib_upsize_integer(A), \ + mlib_upsize_integer(B), \ + #A, \ + #B, \ + Reason, \ + mlib_this_source_location()) // Integer not-equal: -#define _mlibCheckCondition_neq(A, B) \ - _mlibCheckIntCmp( \ - mlib_equal, false, "≠", mlib_upsize_integer(A), mlib_upsize_integer(B), #A, #B, mlib_this_source_location()) -// Simple assertion with an explanatory string -#define _mlibCheckCondition_because(Cond, Msg) _mlibCheckConditionBecause(Cond, #Cond, Msg, mlib_this_source_location()) +#define _mlibCheckCondition_neq(A, B, Reason) \ + _mlibCheckIntCmp(mlib_equal, \ + false, \ + "!=", \ + mlib_upsize_integer(A), \ + mlib_upsize_integer(B), \ + #A, \ + #B, \ + Reason, \ + mlib_this_source_location()) // Integer comparisons: -#define _mlibCheckCondition_lt(A, B) \ - _mlibCheckIntCmp( \ - mlib_less, true, "<", mlib_upsize_integer(A), mlib_upsize_integer(B), #A, #B, mlib_this_source_location()) -#define _mlibCheckCondition_lte(A, B) \ - _mlibCheckIntCmp( \ - mlib_greater, false, "≤", mlib_upsize_integer(A), mlib_upsize_integer(B), #A, #B, mlib_this_source_location()) -#define _mlibCheckCondition_gt(A, B) \ - _mlibCheckIntCmp( \ - mlib_greater, true, ">", mlib_upsize_integer(A), mlib_upsize_integer(B), #A, #B, mlib_this_source_location()) -#define _mlibCheckCondition_gte(A, B) \ - _mlibCheckIntCmp( \ - mlib_less, false, "≥", mlib_upsize_integer(A), mlib_upsize_integer(B), #A, #B, mlib_this_source_location()) +#define _mlibCheckCondition_lt(A, B, Reason) \ + _mlibCheckIntCmp(mlib_less, \ + true, \ + "<", \ + mlib_upsize_integer(A), \ + mlib_upsize_integer(B), \ + #A, \ + #B, \ + Reason, \ + mlib_this_source_location()) +#define _mlibCheckCondition_lte(A, B, Reason) \ + _mlibCheckIntCmp(mlib_greater, \ + false, \ + "≤", \ + mlib_upsize_integer(A), \ + mlib_upsize_integer(B), \ + #A, \ + #B, \ + Reason, \ + mlib_this_source_location()) +#define _mlibCheckCondition_gt(A, B, Reason) \ + _mlibCheckIntCmp(mlib_greater, \ + true, \ + ">", \ + mlib_upsize_integer(A), \ + mlib_upsize_integer(B), \ + #A, \ + #B, \ + Reason, \ + mlib_this_source_location()) +#define _mlibCheckCondition_gte(A, B, Reason) \ + _mlibCheckIntCmp(mlib_less, \ + false, \ + "≥", \ + mlib_upsize_integer(A), \ + mlib_upsize_integer(B), \ + #A, \ + #B, \ + Reason, \ + mlib_this_source_location()) + +// Simple assertion with an explanatory string +#define _mlibCheckCondition_because(Cond, Reason, _null) \ + _mlibCheckConditionSimple(Cond, #Cond, Reason, mlib_this_source_location()) /// Check evaluator when given a single boolean static inline void -_mlibCheckConditionSimple(bool c, const char *expr, struct mlib_source_location here) +_mlibCheckConditionSimple(bool c, const char *expr, const char *reason, struct mlib_source_location here) { if (!c) { - fprintf(stderr, "%s:%d: in [%s]: Check condition ⟨%s⟩ failed\n", here.file, here.lineno, here.func, expr); - fflush(stderr); - abort(); - } -} - -static inline void -_mlibCheckConditionBecause(bool cond, const char *expr, const char *reason, mlib_source_location here) -{ - if (!cond) { - fprintf( - stderr, "%s:%d: in [%s]: Check condition ⟨%s⟩ failed (%s)\n", here.file, here.lineno, here.func, expr, reason); + fprintf(stderr, "%s:%d: in [%s]: Check condition ⟨%s⟩ failed", here.file, here.lineno, here.func, expr); + if (reason) { + fprintf(stderr, " (%s)", reason); + } + fprintf(stderr, "\n"); fflush(stderr); abort(); } @@ -193,6 +235,7 @@ _mlibCheckIntCmp(enum mlib_cmp_result cres, // The cmp result to check struct mlib_upsized_integer right, const char *left_expr, const char *right_expr, + const char *reason, struct mlib_source_location here) { if (((mlib_cmp)(left, right, 0) == cres) != cond) { @@ -218,6 +261,9 @@ _mlibCheckIntCmp(enum mlib_cmp_result cres, // The cmp result to check fprintf(stderr, "%llu", (unsigned long long)right.bits.as_unsigned); } fprintf(stderr, " ⟨%s⟩\n", right_expr); + if (reason) { + fprintf(stderr, "Because: %s\n", reason); + } fflush(stderr); abort(); } @@ -225,8 +271,12 @@ _mlibCheckIntCmp(enum mlib_cmp_result cres, // The cmp result to check // Pointer-comparison static inline void -_mlibCheckPtrEq( - const void *left, const void *right, const char *left_expr, const char *right_expr, struct mlib_source_location here) +_mlibCheckPtrEq(const void *left, + const void *right, + const char *left_expr, + const char *right_expr, + const char *reason, + struct mlib_source_location here) { if (left != right) { fprintf(stderr, @@ -243,6 +293,9 @@ _mlibCheckPtrEq( left_expr, right, right_expr); + if (reason) { + fprintf(stderr, "Because: %s\n", reason); + } fflush(stderr); abort(); } @@ -250,8 +303,12 @@ _mlibCheckPtrEq( // String-comparison static inline void -_mlibCheckStrEq( - const char *left, const char *right, const char *left_expr, const char *right_expr, struct mlib_source_location here) +_mlibCheckStrEq(const char *left, + const char *right, + const char *left_expr, + const char *right_expr, + const char *reason, + struct mlib_source_location here) { if (strcmp(left, right)) { fprintf(stderr, @@ -268,6 +325,9 @@ _mlibCheckStrEq( left_expr, right, right_expr); + if (reason) { + fprintf(stderr, "Because: %s\n", reason); + } fflush(stderr); abort(); } diff --git a/src/common/tests/test-mlib.c b/src/common/tests/test-mlib.c index 12405c2dbb4..cd71cae3483 100644 --- a/src/common/tests/test-mlib.c +++ b/src/common/tests/test-mlib.c @@ -75,6 +75,9 @@ _test_checks(void) mlib_assert_aborts () { mlib_check(3, gte, 5); } + + // An infix with a reason string + mlib_check(1, eq, 1, because, "1 = 1"); } static void From d636e33d0e638c92c74c5da14bf054eeabf1109d Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Mon, 29 Sep 2025 14:38:59 -0600 Subject: [PATCH 02/49] Define a mutable string type --- src/common/src/mlib/str.h | 349 +++++++++++++++++++++++++++++++++++ src/common/tests/test-mlib.c | 85 +++++++++ 2 files changed, 434 insertions(+) diff --git a/src/common/src/mlib/str.h b/src/common/src/mlib/str.h index 8f77fffe94f..70af68c950e 100644 --- a/src/common/src/mlib/str.h +++ b/src/common/src/mlib/str.h @@ -565,4 +565,353 @@ mstr_contains_any_of(mstr_view str, mstr_view needle) } #define mstr_contains_any_of(Str, Needle) mstr_contains_any_of(mstr_view_from(Str), mstr_view_from(Needle)) + +/** + * @brief A simple mutable string type, with a guaranteed null terminator. + * + * This type is a trivially relocatable aggregate type that contains a pointer `data` + * and a size `len`. If not null, the pointer `data` points to an array of mutable + * `char` of length `len + 1`, where the character at `data[len]` is always zero, + * and must not be modified. + */ +typedef struct mstr { + /** + * @brief Pointer to the first char in the string, or NULL if + * the string is null. + * + * The pointed-to character array has a length of `len + 1`, where + * the character at `data[len]` is always null. + * + * @warning Attempting to overwrite the null character at `data[len]` + * will result in undefined behavior! + * + * @note An empty string is not equivalent to a null string! An empty string + * will still point to an array of length 1, where the only char is the null + * terminator. + */ + char *data; + /** + * @brief The number of characters in the array pointed-to by `data` + * that preceed the null terminator. + */ + size_t len; +} mstr; + + +/** + * @brief Resize an existing or null `mstr`, without initializing any of the + * added content other than the null terminator. This operation is potientially + * UNSAFE, because it gives uninitialized memory to the caller. + * + * @param str Pointer to a valid `mstr`, or a null `mstr`. + * @param new_len The new length of the string. + * @return true If the operation succeeds + * @return false Otherwise + * + * If `str` is a null string, this function will initialize a new `mstr` object + * on-the-fly. + * + * If the operation increases the length of the string (or initializes a new string), + * then the new `char` in `str.data[str.len : new_len] will contain uninitialized + * values. The char at `str.data[new_len]` WILL be set to zero, to ensure there + * is a null terminator. The caller should always initialize the new string + * content to ensure that the string has a specified value. + */ +static inline bool +mstr_resize_for_overwrite(mstr *const str, const size_t new_len) +{ + // We need to allocate one additional char to hold the null terminator + size_t alloc_size = new_len; + if (mlib_add(&alloc_size, 1) || alloc_size > SSIZE_MAX) { + // Allocation size is too large + return false; + } + // Try to (re)allocate the region + char *data = (char *)realloc(str->data, alloc_size); + if (!data) { + // Failed to (re)allocate + return false; + } + // Note: We do not initialize any of the data in the newly allocated region. + // We only set the null terminator. It is up to the caller to do the rest of + // the init. + data[new_len] = 0; + // Update the final object + str->data = data; + str->len = new_len; + // Success + return true; +} + +/** + * @brief Given an existing `mstr`, resize it to hold `new_len` chars + * + * @param str Pointer to a string object to update, or a null `mstr` + * @param new_len The new length of the string, not including the implicit null terminator + * @return true If the operation succeeds + * @return false Otherwise + * + * @note If the operation fails, then `*str` is not modified. + */ +static inline bool +mstr_resize(mstr *str, size_t new_len) +{ + const size_t old_len = str->len; + if (!mstr_resize_for_overwrite(str, new_len)) { + // Failed to allocate new storage for the string + return false; + } + // Check how many chars we added/removed + const ptrdiff_t len_diff = new_len - str->len; + if (len_diff > 0) { + // We added new chars. Zero-init all the new chars + memset(str->data + old_len, 0, (size_t)len_diff); + } + // Success + return true; +} + +/** + * @brief Create a new `mstr` of the given length + * + * @param new_len The length of the new string, in characters, not including the null terminator + * @return mstr A new string. The string's `data` member is NULL in case of failure + * + * The character array allocated for the string will always be `new_len + 1` `char` in length, + * where the char at the index `new_len` is a null terminator. This means that a string of + * length zero will allocate a single character to store the null terminator. + * + * All characters in the new string are initialize to zero. If you want uninitialized + * string content, use `mstr_resize_for_overwrite`. + */ +static inline mstr +mstr_new(size_t new_len) +{ + mstr ret = {NULL, 0}; + // We can rely on `resize` to handle the null state properly. + mstr_resize(&ret, new_len); + return ret; +} + +/** + * @brief Delete an `mstr` that was created with an allocating API, including + * the resize APIs + * + * @param s An `mstr` object. If the object is null, this function is a no-op. + * + * After this call, the value of the `s` object has been consumed and is invalid. + */ +static inline void +mstr_delete(mstr s) +{ + free(s.data); +} + +/** + * @brief Replace the content of the given string, attempting to reuse the buffer + * + * @param inout Pointer to a valid or null `mstr` to be replaced + * @param s The new string contents + * @return true If the operation succeeded + * @return false Otherwise + * + * If the operation fails, `*inout` is not modified + */ +static inline bool +mstr_assign(mstr *inout, mstr_view s) +{ + if (!mstr_resize_for_overwrite(inout, s.len)) { + return false; + } + memcpy(inout->data, s.data, s.len); + return true; +} + +#define mstr_assign(InOut, S) mstr_assign((InOut), mstr_view_from((S))) + +/** + * @brief Create a mutable copy of the given string. + * + * @param sv The string to be copied + * @return mstr A new valid string, or a null string in case of allocation failure. + */ +static inline mstr +mstr_copy(mstr_view sv) +{ + mstr ret = {NULL, 0}; + mstr_assign(&ret, sv); + return ret; +} + +#define mstr_copy(S) mstr_copy(mstr_view_from((S))) +#define mstr_copy_cstring(S) mstr_copy(mstr_cstring((S))) + +/** + * @brief Concatenate two strings into a new mutable string + * + * @param a The left-hand string to be concatenated + * @param b The right-hand string to be concatenated + * @return mstr A new valid string composed by concatenating `a` with `b`, or + * a null string in case of allocation failure. + */ +static inline mstr +mstr_concat(mstr_view a, mstr_view b) +{ + mstr ret = {NULL, 0}; + size_t cat_len; + if (mlib_add(&cat_len, a.len, b.len)) { + // Size would overflow. No go. + return ret; + } + // Prepare the new string + if (!mstr_resize_for_overwrite(&ret, cat_len)) { + // Failed to allocate. The ret string is still null, and we can just return it + return ret; + } + // Copy in the characters from `a` + char *out = ret.data; + memcpy(out, a.data, a.len); + // Copy in the characters from `b` + out += a.len; + memcpy(out, b.data, b.len); + // Success + return ret; +} + +#define mstr_concat(A, B) mstr_concat(mstr_view_from((A)), mstr_view_from((B))) + +/** + * @brief Delete and/or insert characters into a string + * + * @param str The string object to be updated + * @param splice_pos The position at which to do the splice + * @param n_delete The number of characters to delete at `splice_pos` + * @param insert A string to be inserted at `split_pos` after chars are deleted + * @return true If the operation succeeds + * @return false Otherwise + * + * If `n_delete` is zero, then no characters are deleted. If `insert` is empty + * or null, then no characters are inserted. + */ +static inline bool +mstr_splice(mstr *str, size_t splice_pos, size_t n_delete, mstr_view insert) +{ + mlib_check(splice_pos <= str->len); + // How many chars is it possible to delete from `splice_pos`? + size_t n_chars_avail_to_delete = str->len - splice_pos; + mlib_check(n_delete <= n_chars_avail_to_delete); + // Compute the new string length + size_t new_len = str->len; + // This should never fail, because we should try to delete more chars than we have + mlib_check(!mlib_sub(&new_len, n_delete)); + // Check if appending would make too big of a string + if (mlib_add(&new_len, insert.len)) { + // New string will be too long + return false; + } + char *mut = str->data; + // We either resize first or resize last, depending on where we are shifting chars + if (new_len > str->len) { + // Do the resize first + if (!mstr_resize_for_overwrite(str, new_len)) { + // Failed to allocate + return false; + } + mut = str->data; + } + // Move to the splice position + mut += splice_pos; + // Shift the existing string parts around for the deletion operation + const size_t tail_len = n_chars_avail_to_delete - n_delete; + // Adjust to the begining of the string part that we want to keep + char *copy_from = mut + n_delete; + char *copy_to = mut + insert.len; + memmove(copy_to, copy_from, tail_len); + if (new_len < str->len) { + // We didn't resize first, so resize now. We are shrinking the string, so this + // will never fail, and does not create any uninitialized memory: + mlib_check(mstr_resize_for_overwrite(str, new_len)); + mut = str->data + splice_pos; + } + // Insert the new data + memcpy(mut, insert.data, insert.len); + return true; +} + +/** + * @brief Append a string to the end of some other string. + * + * @param str The string to be modified + * @param suffix The suffix string to be appended onto `*str` + * @return true If the operation was successful + * @return false Otherwise + * + * If case of failure, `*str` is not modified. + */ +static inline bool +mstr_append(mstr *str, mstr_view suffix) +{ + return mstr_splice(str, str->len, 0, suffix); +} + +#define mstr_append(Into, Suffix) mstr_append((Into), mstr_view_from((Suffix))) + +/** + * @brief Append a single character to the given string object + * + * @param str The string object to be updated + * @param c The single character that will be inserted at the end + * @return true If the operation succeeded + * @return false Otherwise + * + * In case of failure, the string is not modified. + */ +static inline bool +mstr_pushchar(mstr *str, char c) +{ + mstr_view one = mstr_view_data(&c, 1); + return mstr_append(str, one); +} + +/** + * @brief Replace every occurrence of `needle` in `str` with `sub` + * + * @param str The string object to be updated + * @param needle The non-empty needle string to be searched for.s + * @param sub The string to be inserted in place of each `needle` + * @return true If the operation succeeds + * @return false Otherwise + * + * @warning If the `needle` string is empty, then the program will terminate! + * @note If the operation fails, the content of `str` is an unspecified but valid + * string. + */ +static inline bool +mstr_replace(mstr *str, mstr_view needle, mstr_view sub) +{ + mlib_check(needle.len, neq, 0, because, "Trying to replace an empty string will result in an infinite loop"); + // Scan forward, starting from the first position: + size_t off = 0; + while (1) { + // Find the next occurrence, starting from the scan offset + off = mstr_find(*str, needle, off); + if (off == SIZE_MAX) { + // No more occurrences. + return true; + } + // Replace the needle string with the new value + if (!mstr_splice(str, off, needle.len, sub)) { + return false; + } + // Advance over the length of the replacement string, so we don't try to + // infinitely replace content if the replacement itself contains the needle + // string + if (mlib_add(&off, sub.len)) { + // Integer overflow while advancing the offset. No good. + return false; + } + } +} + + #endif // MLIB_STR_H_INCLUDED diff --git a/src/common/tests/test-mlib.c b/src/common/tests/test-mlib.c index cd71cae3483..86822c8578d 100644 --- a/src/common/tests/test-mlib.c +++ b/src/common/tests/test-mlib.c @@ -922,6 +922,90 @@ _test_str_view(void) } } +static inline void +_test_str(void) +{ + // Simple empty + { + mstr s = mstr_new(0); + // Length is zero + mlib_check(s.len, eq, 0); + // Data is not null for empty strings, since we want a null terminator + mlib_check(s.data != NULL); + // The null terminator is present: + mlib_check(s.data[0], eq, 0); + mstr_delete(s); + } + + // Simple copy of a C string + { + mstr s = mstr_copy_cstring("foo bar"); + mlib_check(s.len, eq, 7); + mlib_check(mstr_cmp(s, ==, mstr_cstring("foo bar"))); + mstr_delete(s); + } + + // Concat two strings + { + mstr s = mstr_concat(mstr_cstring("foo"), mstr_cstring("bar")); + mlib_check(mstr_cmp(s, ==, mstr_cstring("foobar"))); + mstr_delete(s); + } + + // Append individual characters + { + mstr s = mstr_new(0); + mstr_pushchar(&s, 'f'); + mstr_pushchar(&s, 'o'); + mstr_pushchar(&s, 'o'); + mlib_check(mstr_cmp(s, ==, mstr_cstring("foo"))); + mstr_delete(s); + } + + // Splice deletion + { + mstr s = mstr_copy_cstring("foo bar baz"); + mlib_check(mstr_splice(&s, 4, 3, mstr_cstring(""))); + mlib_check(mstr_cmp(s, ==, mstr_cstring("foo baz"))); + + mstr_assign(&s, mstr_cstring("foo bar baz")); + mlib_check(mstr_splice(&s, 4, 3, mstr_cstring("quux"))); + mlib_check(mstr_cmp(s, ==, mstr_cstring("foo quux baz"))); + + mstr_assign(&s, mstr_cstring("foo bar baz")); + mlib_check(mstr_splice(&s, 4, 0, mstr_cstring("quux "))); + mlib_check(mstr_cmp(s, ==, mstr_cstring("foo quux bar baz"))); + + mstr_delete(s); + } + + // Replacing + { + mstr s = mstr_copy_cstring("abcd abcd"); + mlib_check(mstr_replace(&s, mstr_cstring("b"), mstr_cstring("foo"))); + mlib_check(mstr_cmp(s, ==, mstr_cstring("afoocd afoocd"))); + + // Try to replace where the replacement contains the needle + mstr_assign(&s, mstr_cstring("foo bar baz")); + mlib_check(mstr_replace(&s, mstr_cstring("bar"), mstr_cstring("foo bar baz"))); + // A naive impl would explode into an infinite string, but we don't try to replace + // within the already-replaced content: + mlib_check(s.data, str_eq, "foo foo bar baz baz"); + + // Try to replace, where the needle is an empty string. This just produces a repetition of the needle + mstr_assign(&s, mstr_cstring("foo")); + mlib_assert_aborts () { + // A naive replacement of an empty string will result in an infinite string + // as it keeps matching the empty string forever, so we terminate rather than + // allocate forever: + mlib_check(mstr_replace(&s, mstr_cstring(""), mstr_cstring("a"))); + } + + mstr_delete(s); + } +} + + static void _test_duration(void) { @@ -1129,6 +1213,7 @@ test_mlib_install(TestSuite *suite) TestSuite_Add(suite, "/mlib/check-cast", _test_cast); TestSuite_Add(suite, "/mlib/ckdint-partial", _test_ckdint_partial); TestSuite_Add(suite, "/mlib/str_view", _test_str_view); + TestSuite_Add(suite, "/mlib/str", _test_str); TestSuite_Add(suite, "/mlib/duration", _test_duration); TestSuite_Add(suite, "/mlib/time_point", _test_time_point); TestSuite_Add(suite, "/mlib/sleep", _test_sleep); From 9675ae5bdcc8baabd5fbdbb23f5d82c0f3ed549d Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Mon, 29 Sep 2025 15:52:03 -0600 Subject: [PATCH 03/49] Add a "vector" template header --- src/common/src/mlib/vec.t.h | 432 +++++++++++++++++++++++++++++++++++ src/common/tests/test-mlib.c | 84 +++++++ 2 files changed, 516 insertions(+) create mode 100644 src/common/src/mlib/vec.t.h diff --git a/src/common/src/mlib/vec.t.h b/src/common/src/mlib/vec.t.h new file mode 100644 index 00000000000..50b5774919b --- /dev/null +++ b/src/common/src/mlib/vec.t.h @@ -0,0 +1,432 @@ +/** + * @file vec.t.h + * @brief Declare a new vector container data type + * @date 2024-10-02 + * + * To use this file: + * + * - #define a type `T` immediately before including this file. + * - Optional: Define an identifier `VecName` to the name of the vector. If unset, declares `_vec` + * - Optional: Define a `VecDestroyElement(Ptr)` macro to specify how the vector + * should destroy the element at `*Ptr`. If unset, destroying is a no-op. + * - Optional: Define `VecInitElement(Ptr, ...)` which initializes a new element. + * The first macro argument is a pointer to the element and subsequent arguments + * are unspecified and reserved for future use. Elements are zero-initialized + * before being passed to this macro. + * - Optional: Define `VecCopyElement(DstPtr, SrcPtr)` to copy data from `*SrcPtr` + * to `*DstPtr`. The vector's copying function is only defined if this macro + * is defined. This macro MUST evaluate to a boolean to indicate if the copy + * operation succeeded. If a copy fails, then the partially copied elements + * will be destroyed and the overall copy will fail. + * + * To add a trival copying function, define `VecCopyElement` to + * `VecTrivialCopyElement`. + * + * - NOTE: All of the above macros will be automatically undef'd after this file + * is included. + * + * Types stored in the vector must be trivially relocatable. + * + * @copyright Copyright 2009-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include + +#include // assert +#include // bool +#include // size_t +#include // calloc, realloc, free +#include // memcpy, memset + +// Check that the caller provided a `T` macro to be the element type +#ifndef T +#if defined(__clangd__) || defined(__INTELLISENSE__) +#define T int // Define a type for IDE diagnostics +#define VecCopyElement VecTrivialCopyElement // For IDE highlighting +#else +#error A type `T` should be defined before including this file +#endif +#endif + +#ifndef VecName +#define VecName MLIB_PASTE(T, _vec) +#endif + +#ifndef VecDestroyElement +#define VecDestroyElement(Ptr) ((void)(Ptr)) +#endif + +#ifndef VecInitElement +#define VecInitElement(Ptr) ((void)(Ptr)) +#endif + +#ifndef VecTrivialCopyElement +#define VecTrivialCopyElement(DstPtr, SrcPtr) ((*(DstPtr) = *(SrcPtr)), true) +#endif + +#pragma push_macro("vec_inline_spec") +#if !defined(vec_inline_spec) +#define vec_inline_spec static inline +#endif + +// The "fn" macro just adds a qualified name to the front of a function identifier +#pragma push_macro("fn") +#undef fn +#define fn(M) MLIB_PASTE_3(VecName, _, M) + +typedef struct VecName { + /** + * @private + * @brief Pointer to the first vector element, or NULL if the vector is + * empty. + * + * @note DO NOT MODIFY + */ + T *data; + /** + * @brief The number of elements in the vector. + * + * @note DO NOT MODIFY + */ + size_t size; + /** + * @brief The number of allocated storage elements. + * + * @note DO NOT MODIFY + */ + size_t capacity; + +#if mlib_is_cxx() + T * + begin() noexcept + { + return data; + } + T * + end() noexcept + { + return data + size; + } +#endif +} VecName; + +mlib_extern_c_begin(); + +/** + * @brief Obtain a pointer-to-mutable to the first element in the given vector + */ +vec_inline_spec T * +fn(begin(VecName *v)) mlib_noexcept +{ + return v->data; +} + +/** + * @brief Obtain a pointer-to-mutable past the last element in the given vector + */ +vec_inline_spec T * +fn(end(VecName *v)) mlib_noexcept +{ + return v->data + v->size; +} + +/** + * @brief Obtain a pointer-to-const to the first element in the given vector + */ +vec_inline_spec T const * +fn(cbegin(VecName const *v)) mlib_noexcept +{ + return v->data; +} + +/** + * @brief Obtain a pointer-to-const past the last element in the given vector + */ +vec_inline_spec T const * +fn(cend(VecName const *v)) mlib_noexcept +{ + return v->data + v->size; +} + +/** + * @brief Get the maximum number of elements that can be held in the vector of + * a certain type. + */ +vec_inline_spec size_t +fn(max_size(void)) mlib_noexcept +{ + // We compare against (signed) SSIZE_MAX because want to support the difference + // between two pointers. If we use the unsigned size, then we could have vectors + // with size that is too large to represent the difference between two sizes. + return SSIZE_MAX / sizeof(T); +} + +/** + * @brief Set the capacity of the given vector. + * + * @param self The vector object to be modified + * @param count The new capacity. If this is less than the current size, then + * the capacity will be capped at the size instead + * + * @retval true If-and-only-if the reallocation was successful + * @retval false If there was an error in allocating the buffer + */ +vec_inline_spec bool +fn(reserve(VecName *const self, size_t count)) mlib_noexcept +{ + // Check if this value is beyond the possible capacity of the vector + if (count > fn(max_size())) { + // Too many elements. We cannot allocate a region this large. + return false; + } + // Check if we are already at the requested capacity. + if (count == self->capacity) { + // No reallocation needed. + return true; + } + // Check if the caller is requesting a lower capacity than our current size + if (count < self->size) { + // We cannot shrink the capacity below the current size, so just shrink-to-fit + count = self->size; + } + // Impossible: We will never shrink below `self.size`, and if + // `self.size == 0` and `count == 0`, then we early-return'd above. + assert(count != 0); + // The number of bytes we need to allocate. Note that this cannot overflow + // because we guard against it by checking against `max_size()` + const size_t new_buffer_size = count * sizeof(T); + // Attempt to reallocate the region + T *const new_buffer = (T *)realloc(self->data, new_buffer_size); + if (!new_buffer) { + // Failed to reallocate a new storage region + return false; + } + // Successfully reallocated the buffer. Update our storage pointer. + self->data = new_buffer; + // Note the new capacity. + self->capacity = count; + return true; +} + +/** + * @brief Destroy elements in the vector at the specified range positions + * + * @param self The vector to be updated + * @param first Pointer to the first element to be destroyed + * @param last Pointer to the first element to NOT be destroyed + * + * Elements are destroyed and removed starting at the end. If `first == last`, + * this is a no-op. The given pointers must refer to vector elements, and `last` + * must be reachable by advancing `first` zero or more times. + */ +vec_inline_spec void +fn(erase(VecName *const self, T *const first, T *const last)) +{ + for (T *r_iter = last; r_iter != first; --r_iter) { + VecDestroyElement((r_iter - 1)); + --self->size; + } +} + +/** + * @brief Destroy a single element at the given zero-based index position + */ +vec_inline_spec void +fn(erase_at(VecName *const self, size_t pos)) +{ + fn(erase(self, fn(begin(self)) + pos, fn(end(self)))); +} + +/** + * @brief Resize the vector to hold the given number of elements + * + * Newly added elements are zero-initialized, or initailized using VecInitElement + * + * @retval true If-and-only-if the resize was successful + * @retval false If the function failed to allocate the new storage region + * + * @note Don't forget to check the return value for success! + */ +// mlib_nodiscard ("Check the returned bool to detect allocation failure") +vec_inline_spec bool +fn(resize(VecName *const self, size_t const count)) mlib_noexcept +{ + // Check if we aren't actually growing the vector. + if (count <= self->size) { + // We need to destroy elements at the tail. If `count == size`, this is a no-op. + fn(erase(self, fn(begin(self)) + count, fn(end(self)))); + return true; + } + + // We need to increase the capacity of the vector to hold the new elements + // Try to auto-grow capacity. Increase capacity by ×1.5 + const size_t half_current_capacity = self->capacity / 2; + size_t new_capacity = 0; + if (mlib_add(&new_capacity, self->size, half_current_capacity)) { + // The auto growth amount would overflow, so just cap to the max size. + new_capacity = fn(max_size()); + } + // Check if our automatic growth is big enough to hold the requested number of elements + if (new_capacity < count) { + // The automatic growth factor is actually smaller than the number of new elements + // the caller wants, so we need to increase capacity to that level instead. + new_capacity = count; + } + // Try to reserve more storage + if (!fn(reserve(self, new_capacity))) { + // We failed to reserve the new storage region. The requested capacity may be too large, + // or we may have just run out of memory. + return false; + } + + // Pointer to where the new end will be + T *const new_end = fn(begin(self)) + count; + // Create a zero-initialized object to copy over the top of each new element. + T zero; + memset(&zero, 0, sizeof zero); + // Call init() on ever new element up until the new size + for (T *iter = fn(end(self)); iter != new_end; ++iter) { + *iter = zero; + (void)(VecInitElement((iter))); + } + + // Update the stored size + self->size = count; + return true; +} + +/** + * @brief Append another element, returning a pointer to that element. + * + * @return T* A pointer to the newly added element, or NULL in case of allocation failure. + */ +// mlib_nodiscard ("Check the returned pointer for failure") +vec_inline_spec T * +fn(push(VecName *self)) mlib_noexcept +{ + size_t count = self->size; + if (mlib_add(&count, 1)) { + // Adding another element would overflow size_t. This is extremely unlikely, + // but precautionary. + return NULL; + } + if (!fn(resize(self, count))) { + // Failed to push another item + return NULL; + } + return fn(begin(self)) + count - 1; +} + +/** + * @brief Create a new empty vector + */ +vec_inline_spec VecName fn(new(void)) mlib_noexcept +{ + VecName ret = {NULL, 0, 0}; + return ret; +} + +/** + * @brief Destroy the pointed-to vector, freeing the associated data buffer. + * + * The pointed-to vector becomes valid storage for a new vector object. + */ +vec_inline_spec void +fn(destroy(VecName *self)) mlib_noexcept +{ + // Resizing to zero will destroy all elements + (void)fn(resize(self, 0)); + // Resizing won't necessarily free the data buffer. Do that now. + free(self->data); + self->capacity = 0; + self->data = NULL; +} + +/** + * @brief Create a new vector with `n` initialized elements + */ +vec_inline_spec VecName +fn(new_n(size_t n, bool *okay)) mlib_noexcept +{ + VecName ret = fn(new()); + *okay = fn(resize)(&ret, n); + return ret; +} + +#ifdef VecCopyElement +/** + * @brief Copy the data from the vector `src` into storage for a new vector `dst` + * + * @param dst_vec Pointer-to-storage for a new vector object to be initialized. + * @param src_vec Pointer to a vector whose elements will be copied into a new vector + * @retval true If-and-only-if the copy was successful. + * @retval false Otherwise + */ +vec_inline_spec bool +fn(init_copy(VecName *dst_vec, VecName const *src_vec)) mlib_noexcept +{ + VecName tmp = fn(new()); + // Try to reseve capacity for all new elements. Don't resize(), because we want + // uninitialized storage for the new data. + if (!fn(reserve(&tmp, src_vec->size))) { + // We failed to reserve capacity in the new vector + fn(destroy(&tmp)); + return false; + } + // Copy everything into the destination element-by-element + { + // Input iterator + T const *in_iter = fn(cbegin(src_vec)); + // Input stop position + T const *const in_stop = fn(cend(src_vec)); + // Output iterator + T *out_iter = tmp.data; + // Copy from the first to the last + for (; in_iter != in_stop; ++in_iter, ++out_iter) { + // Try to copy into the new element + if (!VecCopyElement((out_iter), (in_iter))) { + // Failed copying here. Undo everything by destroying the temporary + fn(destroy(&tmp)); + return false; + } + // Update the size of the temporary vec to record that it is holding the new + // element. This allows us to call `destroy()` to undo our work. + tmp.size++; + } + } + // Everything went okay. Give the temporary to the caller as the final result + *dst_vec = tmp; + return true; +} +#endif // VecCopyElement + +mlib_extern_c_end(); + +#ifndef mlib_vec_foreach +#define mlib_vec_foreach(Type, VarName, Vector) \ + for (Type *VarName = (Vector).data; VarName != (Vector).data + (Vector).size; ++VarName) +#endif + +#undef T +#undef VecName +#undef VecDestroyElement +#undef VecInitElement +#undef VecTrivialCopyElement +#ifdef VecCopyElement +#undef VecCopyElement +#endif +// These ones we want to pop, not undefine: +#pragma pop_macro("fn") +#pragma pop_macro("vec_inline_spec") diff --git a/src/common/tests/test-mlib.c b/src/common/tests/test-mlib.c index 86822c8578d..13c96d3d577 100644 --- a/src/common/tests/test-mlib.c +++ b/src/common/tests/test-mlib.c @@ -1197,6 +1197,88 @@ _test_timer(void) mlib_check(mlib_timer_is_expired(tm)); } +// Tests for `int_vec` assert the behavior of the vector type when handling trivial +// elements. +#define T int +#include +static void +_test_int_vec(void) +{ + int_vec ints = int_vec_new(); + mlib_check(ints.size, eq, 0, because, "Initial vector is empty"); + + // Append an element + int *el; + mlib_check(el = int_vec_push(&ints)); + *el = 42; + mlib_check(int_vec_begin(&ints)[0], eq, 42); + mlib_check(ints.size, eq, 1); + + int_vec_erase_at(&ints, 0); + mlib_check(ints.size, eq, 0, because, "We are back to an empty vector"); + + *int_vec_push(&ints) = 42; + *int_vec_push(&ints) = 1729; + *int_vec_push(&ints) = 123456; + *int_vec_push(&ints) = -7; + mlib_check(ints.size, eq, 4, because, "We added four elements from empty"); + // Erase in the middle + int_vec_erase(&ints, ints.data + 1, ints.data + 3); + mlib_check(ints.size, eq, 2, because, "We erased two elements"); + + int_vec_destroy(&ints); +} + +#define T char * +#define VecName cstring_vec +#define VecDestroyElement(CStrPtr) ((free(*CStrPtr), *CStrPtr = NULL)) +#define VecCopyElement(Dst, Src) ((*Dst = strdup(*Src))) +#include +static void +_test_cstring_vec(void) +{ + // Simple new and destroy + { + cstring_vec v = cstring_vec_new(); + mlib_check(v.size, eq, 0); + mlib_check(v.capacity, eq, 0); + cstring_vec_destroy(&v); + } + // Simple new and push an element + { + cstring_vec v = cstring_vec_new(); + *cstring_vec_push(&v) = strdup("Hey"); + mlib_check(v.size, eq, 1); + mlib_check(v.capacity, eq, 1); + mlib_check(cstring_vec_begin(&v)[0], str_eq, "Hey"); + cstring_vec_destroy(&v); + } + // Copy an empty + { + cstring_vec v = cstring_vec_new(); + cstring_vec b; + cstring_vec_init_copy(&b, &v); + cstring_vec_destroy(&v); + cstring_vec_destroy(&b); + } + // Copy non-empty + { + cstring_vec a = cstring_vec_new(); + *cstring_vec_push(&a) = strdup("Hello"); + *cstring_vec_push(&a) = strdup("world!"); + mlib_check(a.size, eq, 2); + mlib_check(a.capacity, eq, 2); + cstring_vec b; + mlib_check(cstring_vec_init_copy(&b, &a)); + mlib_check(a.size, eq, 2); + mlib_check(a.capacity, eq, 2); + mlib_check(cstring_vec_begin(&a)[0], str_eq, "Hello"); + mlib_check(cstring_vec_begin(&b)[1], str_eq, "world!"); + cstring_vec_destroy(&b); + cstring_vec_destroy(&a); + } +} + void test_mlib_install(TestSuite *suite) { @@ -1218,6 +1300,8 @@ test_mlib_install(TestSuite *suite) TestSuite_Add(suite, "/mlib/time_point", _test_time_point); TestSuite_Add(suite, "/mlib/sleep", _test_sleep); TestSuite_Add(suite, "/mlib/timer", _test_timer); + TestSuite_Add(suite, "/mlib/int-vector", _test_int_vec); + TestSuite_Add(suite, "/mlib/string-vector", _test_cstring_vec); } mlib_diagnostic_pop(); From 8f4ec31f390503a313d585a93711bc6216a5e1bf Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Tue, 30 Sep 2025 12:08:29 -0600 Subject: [PATCH 04/49] Wrapping indexing for vec --- src/common/src/mlib/vec.t.h | 16 ++++++++++++++++ src/common/tests/test-mlib.c | 2 ++ 2 files changed, 18 insertions(+) diff --git a/src/common/src/mlib/vec.t.h b/src/common/src/mlib/vec.t.h index 50b5774919b..793724c1000 100644 --- a/src/common/src/mlib/vec.t.h +++ b/src/common/src/mlib/vec.t.h @@ -43,6 +43,7 @@ */ #include #include +#include #include // assert #include // bool @@ -419,6 +420,21 @@ mlib_extern_c_end(); for (Type *VarName = (Vector).data; VarName != (Vector).data + (Vector).size; ++VarName) #endif +#ifndef mlib_vec_at +#define mlib_vec_at(Vec, Pos) ((Vec).data[_mlib_vec_index_adjust((Vec).size, mlib_upsize_integer(Pos))]) + +static inline size_t +_mlib_vec_index_adjust(size_t size, mlib_upsized_integer pos) +{ + if (pos.is_signed && pos.bits.as_signed < 0) { + return mlib_assert_add(size_t, size, pos.bits.as_signed); + } + mlib_check(pos.bits.as_unsigned, lte, size, because, "the vector index must be in-bounds for mlib_vec_at()"); + return pos.bits.as_unsigned; +} + +#endif + #undef T #undef VecName #undef VecDestroyElement diff --git a/src/common/tests/test-mlib.c b/src/common/tests/test-mlib.c index 13c96d3d577..191b1f5fbab 100644 --- a/src/common/tests/test-mlib.c +++ b/src/common/tests/test-mlib.c @@ -1226,6 +1226,8 @@ _test_int_vec(void) int_vec_erase(&ints, ints.data + 1, ints.data + 3); mlib_check(ints.size, eq, 2, because, "We erased two elements"); + mlib_check(mlib_vec_at(ints, -1), eq, -7, because, "Negative index wraps"); + int_vec_destroy(&ints); } From f97883e0a571e4c8a44191930df9d0b17b62fd17 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Wed, 1 Oct 2025 12:03:43 -0600 Subject: [PATCH 05/49] String functions for trimming whitespace --- src/common/src/mlib/str.h | 75 ++++++++++++++++++++++++++++++++++++ src/common/tests/test-mlib.c | 10 ++++- 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/src/common/src/mlib/str.h b/src/common/src/mlib/str.h index 70af68c950e..1b53c3ad36f 100644 --- a/src/common/src/mlib/str.h +++ b/src/common/src/mlib/str.h @@ -288,6 +288,24 @@ _mstr_adjust_index(mstr_view s, mlib_upsized_integer pos, bool clamp_to_length) return pos.bits.as_unsigned; } +/** + * @brief Obtain the code unit at the given zero-based index, with negative index wrapping. + * + * This function asserts that the index is in-bounds for the given string. + * + * @param s The string to be inspected. + * @param pos The index to access. Zero is the first code unit, and -1 is the last. + * @return char The code unit at position `pos`. + */ +static inline char +mstr_at(mstr_view s, mlib_upsized_integer pos_) +{ + size_t pos = _mstr_adjust_index(s, pos_, false); + return s.data[pos]; +} + +#define mstr_at(S, Pos) (mstr_at)(mstr_view_from(S), mlib_upsize_integer(Pos)) + /** * @brief Create a new `mstr_view` that views a substring within another string * @@ -441,6 +459,63 @@ mstr_find_first_of(mstr_view hay, mstr_view const needles, mlib_upsized_integer #define _mstr_find_first_of_argc_3(Hay, Needle, Pos) _mstr_find_first_of_argc_4(Hay, Needle, Pos, SIZE_MAX) #define _mstr_find_first_of_argc_4(Hay, Needle, Pos, Len) mstr_find_first_of(Hay, Needle, mlib_upsize_integer(Pos), Len) +/** + * @brief Trim leading latin (ASCII) whitespace from the given string + * + * @param s The string to be inspected + * @return mstr_view A substring view of `s` that excludes any leading whitespace + */ +static inline mstr_view +mstr_trim_left(mstr_view s) +{ + while (s.len) { + char c = mstr_at(s, 0); + if (c == ' ' || c == '\n' || c == '\r' || c == '\t') { + s = mstr_substr(s, 1); + } else { + break; + } + } + return s; +} +#define mstr_trim_left(S) (mstr_trim_left)(mstr_view_from(S)) + +/** + * @brief Trim trailing latin (ASCII) whitespace from the given string + * + * @param s The string to be insepcted + * @return mstr_view A substring view of `s` that excludes any trailing whitespace. + */ +static inline mstr_view +mstr_trim_right(mstr_view s) +{ + while (s.len) { + char c = mstr_at(s, -1); + if (c == ' ' || c == '\n' || c == '\r' || c == '\t') { + s = mstr_slice(s, 0, -1); + } else { + break; + } + } + return s; +} +#define mstr_trim_right(S) (mstr_trim_right)(mstr_view_from(S)) + +/** + * @brief Trim leading and trailing latin (ASCII) whitespace from the string + * + * @param s The string to be inspected + * @return mstr_view A substring of `s` that excludes leading and trailing whitespace. + */ +static inline mstr_view +mstr_trim(mstr_view s) +{ + s = mstr_trim_left(s); + s = mstr_trim_right(s); + return s; +} +#define mstr_trim(S) (mstr_trim)(mstr_view_from(S)) + /** * @brief Split a single string view into two strings at the given position * diff --git a/src/common/tests/test-mlib.c b/src/common/tests/test-mlib.c index 191b1f5fbab..71526334e4d 100644 --- a/src/common/tests/test-mlib.c +++ b/src/common/tests/test-mlib.c @@ -920,6 +920,14 @@ _test_str_view(void) // But "Food" > "foo" when case-insensitive: mlib_check(mstr_latin_casecmp(mstr_cstring("Food"), >, mstr_cstring("foo"))); } + + // Trimming + { + mstr_view s = mstr_cstring(" foo bar \n"); + mlib_check(mstr_cmp(mstr_trim_left(s), ==, mstr_cstring("foo bar \n"))); + mlib_check(mstr_cmp(mstr_trim_right(s), ==, mstr_cstring(" foo bar"))); + mlib_check(mstr_cmp(mstr_trim(s), ==, mstr_cstring("foo bar"))); + } } static inline void @@ -1226,7 +1234,7 @@ _test_int_vec(void) int_vec_erase(&ints, ints.data + 1, ints.data + 3); mlib_check(ints.size, eq, 2, because, "We erased two elements"); - mlib_check(mlib_vec_at(ints, -1), eq, -7, because, "Negative index wraps"); + mlib_check(mlib_vec_at(ints, -1), eq, 1729, because, "Negative index wraps"); int_vec_destroy(&ints); } From 47629d7a79286a8b99bc1e632b2330b0d0ecfdbe Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Wed, 1 Oct 2025 12:15:19 -0600 Subject: [PATCH 06/49] Use mlib strings and vec types in TestSuite files --- .clang-format | 1 + src/common/src/mlib/str_vec.h | 31 +++ src/libmongoc/tests/TestSuite.c | 273 ++++++++------------------- src/libmongoc/tests/TestSuite.h | 109 ++++++++--- src/libmongoc/tests/json-test.c | 71 +++---- src/libmongoc/tests/test-libmongoc.h | 6 +- 6 files changed, 241 insertions(+), 250 deletions(-) create mode 100644 src/common/src/mlib/str_vec.h diff --git a/.clang-format b/.clang-format index a392e039ca1..bfd879b27f2 100644 --- a/.clang-format +++ b/.clang-format @@ -149,6 +149,7 @@ ForEachMacros: - mlib_foreach_urange - mlib_foreach - mlib_foreach_arr + - mlib_vec_foreach IfMacros: - mlib_assert_aborts - KJ_IF_MAYBE diff --git a/src/common/src/mlib/str_vec.h b/src/common/src/mlib/str_vec.h new file mode 100644 index 00000000000..bdfe723ed3e --- /dev/null +++ b/src/common/src/mlib/str_vec.h @@ -0,0 +1,31 @@ +/** + * @file str_vec.h + * @brief This file defines mstr_vec, a common "array of strings" type + * @date 2025-09-30 + * + * @copyright Copyright 2009-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MLIB_STR_VEC_H_INCLUDED +#define MLIB_STR_VEC_H_INCLUDED + +#include +#include + +#define T mstr +#define VecDestroyElement(Ptr) (mstr_delete(*Ptr), Ptr->data = NULL, Ptr->len = 0) +#define VecCopyElement(Dst, Src) (*Dst = mstr_copy(*Src), Dst->data != NULL) +#include + +#endif // MLIB_STR_VEC_H_INCLUDED diff --git a/src/libmongoc/tests/TestSuite.c b/src/libmongoc/tests/TestSuite.c index c82bc338361..baf9fc98748 100644 --- a/src/libmongoc/tests/TestSuite.c +++ b/src/libmongoc/tests/TestSuite.c @@ -21,6 +21,8 @@ #include +#include + #include @@ -131,9 +133,6 @@ TestSuite_Init(TestSuite *suite, const char *name, int argc, char **argv) suite->flags = 0; suite->prgname = bson_strdup(argv[0]); suite->silent = false; - suite->ctest_run = NULL; - _mongoc_array_init(&suite->match_patterns, sizeof(char *)); - _mongoc_array_init(&suite->failing_flaky_skips, sizeof(TestSkip *)); for (i = 1; i < argc; i++) { if (0 == strcmp("-d", argv[i])) { @@ -172,7 +171,7 @@ TestSuite_Init(TestSuite *suite, const char *name, int argc, char **argv) } else if ((0 == strcmp("-s", argv[i])) || (0 == strcmp("--silent", argv[i]))) { suite->silent = true; } else if ((0 == strcmp("--ctest-run", argv[i]))) { - if (suite->ctest_run) { + if (suite->ctest_run.data) { test_error("'--ctest-run' can only be specified once"); } if (argc - 1 == i) { @@ -180,14 +179,14 @@ TestSuite_Init(TestSuite *suite, const char *name, int argc, char **argv) } suite->flags |= TEST_NOFORK; suite->silent = true; - suite->ctest_run = bson_strdup(argv[++i]); + suite->ctest_run = mstr_copy_cstring(argv[i + 1]); + ++i; } else if ((0 == strcmp("-l", argv[i])) || (0 == strcmp("--match", argv[i]))) { - char *val; if (argc - 1 == i) { test_error("%s requires an argument.", argv[i]); } - val = bson_strdup(argv[++i]); - _mongoc_array_append_val(&suite->match_patterns, val); + ++i; + *mstr_vec_push(&suite->match_patterns) = mstr_copy_cstring(argv[i]); } else if (0 == strcmp("--skip-tests", argv[i])) { if (argc - 1 == i) { test_error("%s requires an argument.", argv[i]); @@ -201,7 +200,7 @@ TestSuite_Init(TestSuite *suite, const char *name, int argc, char **argv) } } - if (suite->match_patterns.len != 0 && suite->ctest_run != NULL) { + if (suite->match_patterns.size != 0 && suite->ctest_run.data) { test_error("'--ctest-run' cannot be specified with '-l' or '--match'"); } @@ -280,57 +279,28 @@ TestSuite_AddLive(TestSuite *suite, /* IN */ } -static void -_TestSuite_AddCheck(Test *test, CheckFunc check, const char *name) -{ - test->checks[test->num_checks] = check; - if (++test->num_checks > MAX_TEST_CHECK_FUNCS) { - MONGOC_STDERR_PRINTF("Too many check funcs for %s, increase MAX_TEST_CHECK_FUNCS " - "to more than %d\n", - name, - MAX_TEST_CHECK_FUNCS); - abort(); - } -} - - Test * _V_TestSuite_AddFull(TestSuite *suite, const char *name, TestFuncWC func, TestFuncDtor dtor, void *ctx, va_list ap) { - CheckFunc check; - Test *test; - Test *iter; - - if (suite->ctest_run && (0 != strcmp(suite->ctest_run, name))) { + if (suite->ctest_run.data && mstr_cmp(suite->ctest_run, !=, mstr_cstring(name))) { if (dtor) { dtor(ctx); } return NULL; } - test = (Test *)bson_malloc0(sizeof *test); - test->name = bson_strdup(name); + Test *test = TestVec_push(&suite->tests); + test->name = mstr_copy_cstring(name); test->func = func; - test->num_checks = 0; + CheckFunc check; while ((check = va_arg(ap, CheckFunc))) { - _TestSuite_AddCheck(test, check, name); + *CheckFuncVec_push(&test->checks) = check; } - test->next = NULL; test->dtor = dtor; test->ctx = ctx; TestSuite_SeedRand(suite, test); - - if (!suite->tests) { - suite->tests = test; - return test; - } - - for (iter = suite->tests; iter->next; iter = iter->next) { - } - - iter->next = test; return test; } @@ -348,7 +318,7 @@ _TestSuite_AddMockServerTest(TestSuite *suite, const char *name, TestFunc func, va_end(ap); if (test) { - _TestSuite_AddCheck(test, TestSuite_CheckMockServerAllowed, name); + *CheckFuncVec_push(&test->checks) = TestSuite_CheckMockServerAllowed; } } @@ -531,10 +501,9 @@ TestSuite_RunTest(TestSuite *suite, /* IN */ char name[MAX_TEST_NAME_LENGTH]; mcommon_string_append_t buf; mcommon_string_t *mock_server_log_buf; - size_t i; int status = 0; - bson_snprintf(name, sizeof name, "%s%s", suite->name, test->name); + bson_snprintf(name, sizeof name, "%s%.*s", suite->name, MSTR_FMT(test->name)); mcommon_string_new_as_append(&buf); @@ -542,19 +511,18 @@ TestSuite_RunTest(TestSuite *suite, /* IN */ test_msg("Begin %s, seed %u", name, test->seed); } - for (i = 0; i < suite->failing_flaky_skips.len; i++) { - TestSkip *skip = _mongoc_array_index(&suite->failing_flaky_skips, TestSkip *, i); - if (0 == strcmp(name, skip->test_name) && skip->subtest_desc == NULL) { - if (suite->ctest_run) { + mlib_vec_foreach (TestSkip, skip, suite->failing_flaky_skips) { + if (mstr_cmp(skip->test_name, ==, mstr_cstring(name)) && !skip->subtest_desc.data) { + if (suite->ctest_run.data) { /* Write a marker that tells CTest that we are skipping this test */ test_msg("@@ctest-skipped@@"); } if (!suite->silent) { mcommon_string_append_printf(&buf, - " { \"status\": \"skip\", \"test_file\": \"%s\"," - " \"reason\": \"%s\" }%s", - test->name, - skip->reason, + " { \"status\": \"skip\", \"test_file\": \"%.*s\"," + " \"reason\": \"%.*s\" }%s", + MSTR_FMT(test->name), + MSTR_FMT(skip->reason), ((*count) == 1) ? "" : ","); test_msg("%s", mcommon_str_from_append(&buf)); if (suite->outfile) { @@ -567,15 +535,17 @@ TestSuite_RunTest(TestSuite *suite, /* IN */ } } - for (i = 0; i < test->num_checks; i++) { - if (!test->checks[i]()) { - if (suite->ctest_run) { + mlib_vec_foreach (CheckFunc, check, test->checks) { + if (!(*check)()) { + if (suite->ctest_run.data) { /* Write a marker that tells CTest that we are skipping this test */ test_msg("@@ctest-skipped@@"); } if (!suite->silent) { - mcommon_string_append_printf( - &buf, " { \"status\": \"skip\", \"test_file\": \"%s\" }%s", test->name, ((*count) == 1) ? "" : ","); + mcommon_string_append_printf(&buf, + " { \"status\": \"skip\", \"test_file\": \"%.*s\" }%s", + MSTR_FMT(test->name), + ((*count) == 1) ? "" : ","); test_msg("%s", mcommon_str_from_append(&buf)); if (suite->outfile) { fprintf(suite->outfile, "%s", mcommon_str_from_append(&buf)); @@ -690,11 +660,9 @@ TestSuite_PrintHelp(TestSuite *suite) /* IN */ static void TestSuite_PrintTests(TestSuite *suite) /* IN */ { - Test *iter; - printf("\nTests:\n"); - for (iter = suite->tests; iter; iter = iter->next) { - printf("%s%s\n", suite->name, iter->name); + mlib_vec_foreach (Test, t, suite->tests) { + printf("%s%.*s\n", suite->name, MSTR_FMT(t->name)); } printf("\n"); @@ -870,7 +838,7 @@ TestSuite_TestMatchesName(const TestSuite *suite, const Test *test, const char * char name[128]; bool star = strlen(testname) && testname[strlen(testname) - 1] == '*'; - bson_snprintf(name, sizeof name, "%s%s", suite->name, test->name); + bson_snprintf(name, sizeof name, "%s%.*s", suite->name, MSTR_FMT(test->name)); if (star) { /* e.g. testname is "/Client*" and name is "/Client/authenticate" */ @@ -884,19 +852,18 @@ TestSuite_TestMatchesName(const TestSuite *suite, const Test *test, const char * bool test_matches(TestSuite *suite, Test *test) { - if (suite->ctest_run) { + if (suite->ctest_run.data) { /* We only want exactly the named test */ - return strcmp(test->name, suite->ctest_run) == 0; + return mstr_cmp(test->name, ==, suite->ctest_run); } /* If no match patterns were provided, then assume all match. */ - if (suite->match_patterns.len == 0) { + if (suite->match_patterns.size == 0) { return true; } - for (size_t i = 0u; i < suite->match_patterns.len; i++) { - char *pattern = _mongoc_array_index(&suite->match_patterns, char *, i); - if (TestSuite_TestMatchesName(suite, test, pattern)) { + mlib_vec_foreach (mstr, pat, suite->match_patterns) { + if (TestSuite_TestMatchesName(suite, test, pat->data)) { return true; } } @@ -905,23 +872,9 @@ test_matches(TestSuite *suite, Test *test) } void -_process_skip_file(const char *filename, mongoc_array_t *skips) +_process_skip_file(const char *filename, TestSkipVec *skips) { - const int max_lines = 1000; - int lines_read = 0; - char buffer[SKIP_LINE_BUFFER_SIZE]; - size_t buflen; FILE *skip_file; - char *fgets_ret; - TestSkip *skip; - char *test_name_end; - size_t comment_len; - char *comment_char; - char *comment_text; - size_t subtest_len; - size_t new_buflen; - char *subtest_start; - char *subtest_end; #ifdef _WIN32 if (0 != fopen_s(&skip_file, filename, "r")) { @@ -934,73 +887,47 @@ _process_skip_file(const char *filename, mongoc_array_t *skips) test_error("Failed to open skip file: %s: errno: %d", filename, errno); } - while (lines_read < max_lines) { - fgets_ret = fgets(buffer, sizeof(buffer), skip_file); - buflen = strlen(buffer); - - if (buflen == 0 || !fgets_ret) { - break; /* error or EOF */ + while (1) { + char buffer[SKIP_LINE_BUFFER_SIZE]; + if (!fgets(buffer, sizeof(buffer), skip_file)) { + break; /* error */ } - if (buffer[0] == '#' || buffer[0] == ' ' || buffer[0] == '\n') { - continue; /* Comment line or blank line */ + mstr_view line = mstr_cstring(buffer); + if (!line.len) { + // EOF + break; } - - skip = (TestSkip *)bson_malloc0(sizeof *skip); - if (buffer[buflen - 1] == '\n') - buflen--; - test_name_end = buffer + buflen; - - /* First get the comment, starting at '#' to EOL */ - comment_len = 0; - comment_char = strchr(buffer, '#'); - if (comment_char) { - test_name_end = comment_char; - comment_text = comment_char; - while (comment_text[0] == '#' || comment_text[0] == ' ' || comment_text[0] == '\t') { - if (++comment_text >= (buffer + buflen)) - break; - } - skip->reason = bson_strndup(comment_text, buflen - (comment_text - buffer)); - comment_len = buflen - (comment_char - buffer); - } else { - skip->reason = NULL; + // Remove whitespace + line = mstr_trim(line); + if (line.len == 0 || line.data[0] == '#') { + // Empty line or comment + continue; } - /* Next get the subtest name, from first '"' until last '"' */ - new_buflen = buflen - comment_len; - subtest_start = strstr(buffer, "/\""); - if (subtest_start && (!comment_char || (subtest_start < comment_char))) { - test_name_end = subtest_start; - subtest_start++; - /* find the second '"' that marks end of subtest name */ - subtest_end = subtest_start + 1; - while (subtest_end[0] != '\0' && subtest_end[0] != '"' && (subtest_end < buffer + new_buflen)) { - subtest_end++; - } - /* 'subtest_start + 1' to trim leading and trailing '"' */ - subtest_len = subtest_end - (subtest_start + 1); - skip->subtest_desc = bson_strndup(subtest_start + 1, subtest_len); - } else { - skip->subtest_desc = NULL; - } + TestSkip skip = {0}; + // If there is a trailing comment, drop that: + mstr_view comment = {0}; + mstr_split_around(line, mstr_cstring("#"), &line, &comment); + line = mstr_trim(line); + comment = mstr_trim(comment); - /* Next get the test name */ - while (test_name_end[-1] == ' ' && test_name_end > buffer) { - /* trailing space might be between test name and '#' */ - test_name_end--; + if (comment.len) { + skip.reason = mstr_copy(comment); } - skip->test_name = bson_strndup(buffer, test_name_end - buffer); - - _mongoc_array_append_val(skips, skip); - lines_read++; - } - if (lines_read == max_lines) { - test_error("Skip file: %s exceeded maximum lines: %d. Increase " - "max_lines in _process_skip_file", - filename, - max_lines); + // If it contains a '/"' substring, the quoted part is the subtest description, + // and everything before the '/' is the main test name. Split on that: + mstr_view test_name; + mstr_view subtest_desc; + if (mstr_split_around(line, mstr_cstring("/\""), &test_name, &subtest_desc)) { + // Drop trailing quote: + mlib_check(mstr_at(subtest_desc, -1), eq, '"', because, "Subtest description should end with a quote"); + subtest_desc = mstr_slice(subtest_desc, 0, -1); + skip.subtest_desc = mstr_copy(subtest_desc); + } + skip.test_name = mstr_copy(test_name); + *TestSkipVec_push(skips) = skip; } fclose(skip_file); } @@ -1008,30 +935,29 @@ _process_skip_file(const char *filename, mongoc_array_t *skips) static int TestSuite_RunAll(TestSuite *suite /* IN */) { - Test *test; int count = 0; int status = 0; ASSERT(suite); /* initialize "count" so we can omit comma after last test output */ - for (test = suite->tests; test; test = test->next) { - if (test_matches(suite, test)) { + mlib_vec_foreach (Test, t, suite->tests) { + if (test_matches(suite, t)) { count++; } } - if (suite->ctest_run) { + if (suite->ctest_run.data) { /* We should have matched *at most* one test */ ASSERT(count <= 1); if (count == 0) { - test_error("No such test '%s'", suite->ctest_run); + test_error("No such test '%.*s'", MSTR_FMT(suite->ctest_run)); } } - for (test = suite->tests; test; test = test->next) { - if (test_matches(suite, test)) { - status += TestSuite_RunTest(suite, test, &count); + mlib_vec_foreach (Test, t, suite->tests) { + if (test_matches(suite, t)) { + status += TestSuite_RunTest(suite, t, &count); count--; } } @@ -1075,39 +1001,20 @@ TestSuite_Run(TestSuite *suite) /* IN */ } start_us = bson_get_monotonic_time(); - if (suite->tests) { - failures += TestSuite_RunAll(suite); - } else if (!suite->silent) { - TestSuite_PrintJsonFooter(stdout); - if (suite->outfile) { - TestSuite_PrintJsonFooter(suite->outfile); - } - } + failures += TestSuite_RunAll(suite); MONGOC_DEBUG("Duration of all tests (s): %" PRId64, (bson_get_monotonic_time() - start_us) / (1000 * 1000)); return failures; } - void TestSuite_Destroy(TestSuite *suite) { - Test *test; - Test *tmp; - bson_mutex_lock(&gTestMutex); gTestSuite = NULL; bson_mutex_unlock(&gTestMutex); - for (test = suite->tests; test; test = tmp) { - tmp = test->next; - - if (test->dtor) { - test->dtor(test->ctx); - } - bson_free(test->name); - bson_free(test); - } + TestVec_destroy(&suite->tests); if (suite->outfile) { fclose(suite->outfile); @@ -1117,23 +1024,9 @@ TestSuite_Destroy(TestSuite *suite) bson_free(suite->name); bson_free(suite->prgname); - bson_free(suite->ctest_run); - for (size_t i = 0u; i < suite->match_patterns.len; i++) { - char *val = _mongoc_array_index(&suite->match_patterns, char *, i); - bson_free(val); - } - - _mongoc_array_destroy(&suite->match_patterns); - - for (size_t i = 0u; i < suite->failing_flaky_skips.len; i++) { - TestSkip *val = _mongoc_array_index(&suite->failing_flaky_skips, TestSkip *, i); - bson_free(val->test_name); - bson_free(val->subtest_desc); - bson_free(val->reason); - bson_free(val); - } - - _mongoc_array_destroy(&suite->failing_flaky_skips); + mstr_delete(suite->ctest_run); + mstr_vec_destroy(&suite->match_patterns); + TestSkipVec_destroy(&suite->failing_flaky_skips); } diff --git a/src/libmongoc/tests/TestSuite.h b/src/libmongoc/tests/TestSuite.h index c3627c71e40..9486010268a 100644 --- a/src/libmongoc/tests/TestSuite.h +++ b/src/libmongoc/tests/TestSuite.h @@ -25,6 +25,8 @@ #include +#include +#include #include #include @@ -655,37 +657,105 @@ typedef void (*TestFunc)(void); typedef void (*TestFuncWC)(void *); typedef void (*TestFuncDtor)(void *); typedef int (*CheckFunc)(void); -typedef struct _Test Test; -typedef struct _TestSuite TestSuite; +typedef struct Test Test; +typedef struct TestSuite TestSuite; typedef struct _TestFnCtx TestFnCtx; -typedef struct _TestSkip TestSkip; - - -struct _Test { - Test *next; - char *name; +typedef struct TestSkip TestSkip; + +#define T CheckFunc +#define VecName CheckFuncVec +#include + +struct Test { + /** + * @brief The C string that names the test case + */ + mstr name; + /** + * @brief The function that will be executed for the test case + */ TestFuncWC func; + /** + * @brief The function that destroys the context data associated with the test case + */ TestFuncDtor dtor; + /** + * @brief Pointer to arbitrary context data associated with the text case + */ void *ctx; + /** + * @brief The exit code that was received from the test function + */ int exit_code; + /** + * @brief Randomness seed for the test case + */ unsigned seed; - CheckFunc checks[MAX_TEST_CHECK_FUNCS]; - size_t num_checks; + /** + * @brief Array of check functions that determine whether this test case should be skipped + */ + CheckFuncVec checks; +}; + +static inline void +Test_Destroy(Test *t) +{ + if (t->dtor) { + t->dtor(t->ctx); + } + mstr_delete(t->name); + CheckFuncVec_destroy(&t->checks); +} + +#define T Test +#define VecName TestVec +#define VecDestroyElement Test_Destroy +#include + +/** + * @brief Information about a test that we plan to skip + */ +struct TestSkip { + /** + * @brief The name of the test that is being skipped + */ + mstr test_name; + /** + * @brief If not-null, the description of the sub-test that we are skipping. + */ + mstr subtest_desc; + /** + * @brief An explanatory string for why we are skipping the test + */ + mstr reason; }; +static inline void +TestSkip_Destroy(TestSkip *skip) +{ + mstr_delete(skip->test_name); + mstr_delete(skip->subtest_desc); + mstr_delete(skip->reason); +} + +#define T TestSkip +#define VecName TestSkipVec +#define VecDestroyElement(Skip) TestSkip_Destroy(Skip) +#include -struct _TestSuite { + +struct TestSuite { char *prgname; char *name; - mongoc_array_t match_patterns; - char *ctest_run; - Test *tests; + mstr_vec match_patterns; + mstr ctest_run; + TestVec tests; FILE *outfile; int flags; int silent; mcommon_string_t *mock_server_log_buf; FILE *mock_server_log; - mongoc_array_t failing_flaky_skips; + TestSkipVec failing_flaky_skips; }; @@ -695,13 +765,6 @@ struct _TestFnCtx { }; -struct _TestSkip { - char *test_name; - char *subtest_desc; - char *reason; -}; - - void TestSuite_Init(TestSuite *suite, const char *name, int argc, char **argv); void @@ -742,7 +805,7 @@ test_suite_debug_output(void); void test_suite_mock_server_log(const char *msg, ...); void -_process_skip_file(const char *, mongoc_array_t *); +_process_skip_file(const char *, TestSkipVec *); bool TestSuite_NoFork(TestSuite *suite); diff --git a/src/libmongoc/tests/json-test.c b/src/libmongoc/tests/json-test.c index 22e0953e891..61e37262f0b 100644 --- a/src/libmongoc/tests/json-test.c +++ b/src/libmongoc/tests/json-test.c @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -1884,9 +1885,11 @@ _install_json_test_suite_with_check(TestSuite *suite, const char *base, const ch bson_snprintf(joined, PATH_MAX, "%s/%s", base, subdir); ASSERT(realpath(joined, resolved)); - if (suite->ctest_run) { - const char *found = strstr(suite->ctest_run, subdir); - if (found != suite->ctest_run && found != suite->ctest_run + 1) { + if (suite->ctest_run.data) { + // If we're running a specific test, only register if the directory we are scanning + // is a prefix of the requested test pathname + size_t where = mstr_find(suite->ctest_run, mstr_cstring(subdir)); + if (where != 0 && where != 1) { return; } } @@ -1903,39 +1906,39 @@ _install_json_test_suite_with_check(TestSuite *suite, const char *base, const ch ext[0] = '\0'; test = _skip_if_unsupported(skip_json, test); - for (size_t j = 0u; j < suite->failing_flaky_skips.len; j++) { - TestSkip *skip = _mongoc_array_index(&suite->failing_flaky_skips, TestSkip *, j); - if (0 == strcmp(skip_json, skip->test_name)) { - /* Modify the test file to give applicable entries a skipReason */ - bson_t *modified = bson_new(); - bson_array_builder_t *modified_tests; - bson_iter_t iter; - - bson_copy_to_excluding_noinit(test, modified, "tests", NULL); - BSON_APPEND_ARRAY_BUILDER_BEGIN(modified, "tests", &modified_tests); - BSON_ASSERT(bson_iter_init_find(&iter, test, "tests")); - for (bson_iter_recurse(&iter, &iter); bson_iter_next(&iter);) { - bson_iter_t desc_iter; - uint32_t desc_len; - const char *desc; - bson_t original_test; - bson_t modified_test; - - bson_iter_bson(&iter, &original_test); - bson_iter_init_find(&desc_iter, &original_test, "description"); - desc = bson_iter_utf8(&desc_iter, &desc_len); - - bson_array_builder_append_document_begin(modified_tests, &modified_test); - bson_concat(&modified_test, &original_test); - if (!skip->subtest_desc || 0 == strcmp(skip->subtest_desc, desc)) { - BSON_APPEND_UTF8(&modified_test, "skipReason", skip->reason != NULL ? skip->reason : "(null)"); - } - bson_array_builder_append_document_end(modified_tests, &modified_test); + mlib_vec_foreach (TestSkip, skip, suite->failing_flaky_skips) { + if (mstr_cmp(mstr_cstring(skip_json), !=, skip->test_name)) { + continue; + } + /* Modify the test file to give applicable entries a skipReason */ + bson_t *modified = bson_new(); + bson_array_builder_t *modified_tests; + bson_iter_t iter; + + bson_copy_to_excluding_noinit(test, modified, "tests", NULL); + BSON_APPEND_ARRAY_BUILDER_BEGIN(modified, "tests", &modified_tests); + BSON_ASSERT(bson_iter_init_find(&iter, test, "tests")); + for (bson_iter_recurse(&iter, &iter); bson_iter_next(&iter);) { + bson_iter_t desc_iter; + uint32_t desc_len; + const char *desc; + bson_t original_test; + bson_t modified_test; + + bson_iter_bson(&iter, &original_test); + bson_iter_init_find(&desc_iter, &original_test, "description"); + desc = bson_iter_utf8(&desc_iter, &desc_len); + + bson_array_builder_append_document_begin(modified_tests, &modified_test); + bson_concat(&modified_test, &original_test); + if (!skip->subtest_desc.data || mstr_cmp(skip->subtest_desc, ==, mstr_cstring(desc))) { + BSON_APPEND_UTF8(&modified_test, "skipReason", skip->reason.data ? skip->reason.data : "(null)"); } - bson_append_array_builder_end(modified, modified_tests); - bson_destroy(test); - test = modified; + bson_array_builder_append_document_end(modified_tests, &modified_test); } + bson_append_array_builder_end(modified, modified_tests); + bson_destroy(test); + test = modified; } /* list of "check" functions that decide whether to skip the test */ va_start(ap, callback); diff --git a/src/libmongoc/tests/test-libmongoc.h b/src/libmongoc/tests/test-libmongoc.h index e3d05d2db06..e3b380455d3 100644 --- a/src/libmongoc/tests/test-libmongoc.h +++ b/src/libmongoc/tests/test-libmongoc.h @@ -21,14 +21,14 @@ #include -struct _TestSuite; +struct TestSuite; struct _bson_t; struct _server_version_t; void -test_libmongoc_init(struct _TestSuite *suite, int argc, char **argv); +test_libmongoc_init(struct TestSuite *suite, int argc, char **argv); void -test_libmongoc_destroy(struct _TestSuite *suite); +test_libmongoc_destroy(struct TestSuite *suite); mongoc_database_t * get_test_database(mongoc_client_t *client); From b39b7957860b86c9623d7874243a090457e41945 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Fri, 3 Oct 2025 10:10:16 -0600 Subject: [PATCH 07/49] Add support for test cases to declare tags (labels) This change allows for test cases to declare any number of associated "tags". The tags are specified after the test case name string as a list of bracketed tags, mimicking Catch2's syntax. The LoadTests.cmake script has been modified to apply test case's declared tags as CTest labels. This also gives us the ability to apply test fixture requirements granularly on only tests that declare their requirement (via a tag). --- build/cmake/LoadTests.cmake | 79 +++++++++++++---------- src/libmongoc/tests/TestSuite.c | 59 +++++++++++++---- src/libmongoc/tests/TestSuite.h | 30 +++++---- src/libmongoc/tests/test-mcd-azure-imds.c | 7 +- 4 files changed, 117 insertions(+), 58 deletions(-) diff --git a/build/cmake/LoadTests.cmake b/build/cmake/LoadTests.cmake index 262797b4779..8e6b9abde8a 100644 --- a/build/cmake/LoadTests.cmake +++ b/build/cmake/LoadTests.cmake @@ -3,46 +3,56 @@ # allowing CTest to control the execution, parallelization, and collection of # test results. -if (NOT EXISTS "${TEST_LIBMONGOC_EXE}") +if(NOT EXISTS "${TEST_LIBMONGOC_EXE}") # This will fail if 'test-libmongoc' is not compiled yet. - message (WARNING "The test executable ${TEST_LIBMONGOC_EXE} is not present. " + message(WARNING "The test executable ${TEST_LIBMONGOC_EXE} is not present. " "Its tests will not be registered") - add_test (mongoc/not-found NOT_FOUND) - return () -endif () + add_test(mongoc/not-found NOT_FOUND) + return() +endif() -# Get the list of tests -execute_process ( - COMMAND "${TEST_LIBMONGOC_EXE}" --list-tests --no-fork - OUTPUT_VARIABLE tests_out +# Get the list of tests. This command emits CMake code that defines variables for +# all test cases defined in the suite +execute_process( + COMMAND "${TEST_LIBMONGOC_EXE}" --tests-cmake --no-fork + OUTPUT_VARIABLE tests_cmake WORKING_DIRECTORY "${SRC_ROOT}" RESULT_VARIABLE retc ) -if (retc) +if(retc) # Failed to list the tests. That's bad. - message (FATAL_ERROR "Failed to run test-libmongoc to discover tests [${retc}]:\n${tests_out}") -endif () + message(FATAL_ERROR "Failed to run test-libmongoc to discover tests [${retc}]:\n${tests_out}") +endif() -# Split lines on newlines -string (REPLACE "\n" ";" lines "${tests_out}") +# Execute the code that defines the test case information +cmake_language(EVAL CODE "${tests_cmake}") -# TODO: Allow individual test cases to specify the fixtures they want. -set (all_fixtures "mongoc/fixtures/fake_kms_provider_server") -set (all_env +# Define environment variables that are common to all test cases +set(all_env TEST_KMS_PROVIDER_HOST=localhost:14987 # Refer: Fixtures.cmake ) -# Generate the test definitions -foreach (line IN LISTS lines) - if (NOT line MATCHES "^/") - # Only generate if the line begins with `/`, which all tests should. - continue () - endif () - # The new test name is prefixed with 'mongoc' - set (test "mongoc${line}") - # Define the test. Use `--ctest-run` to tell it that CTest is in control. - add_test ("${test}" "${TEST_LIBMONGOC_EXE}" --ctest-run "${line}") - set_tests_properties ("${test}" PROPERTIES +# The emitted code defines a list MONGOC_TESTS with the name of every test case +# in the suite. +foreach(casename IN LISTS MONGOC_TESTS) + set(name "mongoc${casename}") + # Run the program with --ctest-run to select only this one test case + add_test("${name}" "${TEST_LIBMONGOC_EXE}" --ctest-run "${casename}") + # The emitted code defines a TAGS list for every test case that it emits. We use + # these as the LABELS for the test case + set(labels "${MONGOC_TEST_${casename}_TAGS}") + + # Find what test fixtures the test wants by inspecting labels. Each "uses:" + # label defines the names of the test fixtures that a particular case requires + set(fixtures "${labels}") + list(FILTER fixtures INCLUDE REGEX "^uses:") + list(TRANSFORM fixtures REPLACE "^uses:(.*)$" "mongoc/fixtures/\\1") + + # Add a label for all test cases generated via this script so that they + # can be (de)selected separately: + list(APPEND labels test-libmongoc-generated) + # Set up the test: + set_tests_properties("${name}" PROPERTIES # test-libmongoc expects to execute in the root of the source directory WORKING_DIRECTORY "${SRC_ROOT}" # If a test emits '@@ctest-skipped@@', this tells us that the test is @@ -50,10 +60,11 @@ foreach (line IN LISTS lines) SKIP_REGULAR_EXPRESSION "@@ctest-skipped@@" # 45 seconds of timeout on each test. TIMEOUT 45 - FIXTURES_REQUIRED "${all_fixtures}" + # Common environment variables: ENVIRONMENT "${all_env}" - # Mark all tests generated from the executable, so they can be (de)selected - # for execution separately. - LABELS "test-libmongoc-generated" - ) -endforeach () + # Apply the labels + LABELS "${labels}" + # Fixture requirements: + FIXTURES_REQUIRED "${fixtures}" + ) +endforeach() diff --git a/src/libmongoc/tests/TestSuite.c b/src/libmongoc/tests/TestSuite.c index baf9fc98748..68d1876ca56 100644 --- a/src/libmongoc/tests/TestSuite.c +++ b/src/libmongoc/tests/TestSuite.c @@ -20,6 +20,7 @@ #include #include +#include #include @@ -168,6 +169,8 @@ TestSuite_Init(TestSuite *suite, const char *name, int argc, char **argv) suite->flags |= TEST_HELPTEXT; } else if (0 == strcmp("--list-tests", argv[i])) { suite->flags |= TEST_LISTTESTS; + } else if (0 == strcmp("--tests-cmake", argv[i])) { + suite->flags |= TEST_TESTS_CMAKE; } else if ((0 == strcmp("-s", argv[i])) || (0 == strcmp("--silent", argv[i]))) { suite->silent = true; } else if ((0 == strcmp("--ctest-run", argv[i]))) { @@ -280,9 +283,17 @@ TestSuite_AddLive(TestSuite *suite, /* IN */ Test * -_V_TestSuite_AddFull(TestSuite *suite, const char *name, TestFuncWC func, TestFuncDtor dtor, void *ctx, va_list ap) +_V_TestSuite_AddFull( + TestSuite *suite, const char *name_and_tags, TestFuncWC func, TestFuncDtor dtor, void *ctx, va_list ap) { - if (suite->ctest_run.data && mstr_cmp(suite->ctest_run, !=, mstr_cstring(name))) { + // Split the name and tags around the first whitespace: + mstr_view name, tags; + mstr_split_around(mstr_cstring(name_and_tags), mstr_cstring(" "), &name, &tags); + // Trim extras: + tags = mstr_trim(tags); + + if (suite->ctest_run.data && mstr_cmp(suite->ctest_run, !=, name)) { + // We are running CTest, and not running this particular test, so just skip registering it if (dtor) { dtor(ctx); } @@ -290,9 +301,19 @@ _V_TestSuite_AddFull(TestSuite *suite, const char *name, TestFuncWC func, TestFu } Test *test = TestVec_push(&suite->tests); - test->name = mstr_copy_cstring(name); + test->name = mstr_copy(name); test->func = func; + mstr_view tag, tail; + tail = tags; + while (tail.len) { + mlib_check(mstr_split_around(tail, mstr_cstring("["), NULL, &tag), + because, + "Expected an opening bracket for the next test tag"); + mlib_check(mstr_split_around(tag, mstr_cstring("]"), &tag, &tail), because, "Expected a closing bracket for tag"); + *mstr_vec_push(&test->tags) = mstr_copy(tag); + } + CheckFunc check; while ((check = va_arg(ap, CheckFunc))) { *CheckFuncVec_push(&test->checks) = check; @@ -334,13 +355,12 @@ TestSuite_AddWC(TestSuite *suite, /* IN */ } -void -_TestSuite_AddFull(TestSuite *suite, /* IN */ - const char *name, /* IN */ - TestFuncWC func, /* IN */ - TestFuncDtor dtor, /* IN */ - void *ctx, - ...) /* IN */ +void(TestSuite_AddFull)(TestSuite *suite, /* IN */ + const char *name, /* IN */ + TestFuncWC func, /* IN */ + TestFuncDtor dtor, /* IN */ + void *ctx, + ...) /* IN */ { va_list ap; @@ -640,6 +660,7 @@ TestSuite_PrintHelp(TestSuite *suite) /* IN */ "Options:\n" " -h, --help Show this help menu.\n" " --list-tests Print list of available tests.\n" + " --tests-cmake Print CMake code that defines test information.\n" " -f, --no-fork Do not spawn a process per test (abort on " "first error).\n" " -l, --match PATTERN Run test by name, e.g. \"/Client/command\" or " @@ -668,6 +689,18 @@ TestSuite_PrintTests(TestSuite *suite) /* IN */ printf("\n"); } +static void +TestSuite_PrintCMake(TestSuite *suite) +{ + printf("set(MONGOC_TESTS)\n"); + mlib_vec_foreach (Test, t, suite->tests) { + printf("list(APPEND MONGOC_TESTS [[%.*s]])\n", MSTR_FMT(t->name)); + printf("set(MONGOC_TEST_%.*s_TAGS)\n", MSTR_FMT(t->name)); + mlib_vec_foreach (mstr, tag, t->tags) { + printf("list(APPEND MONGOC_TEST_%.*s_TAGS [[%.*s]])\n", MSTR_FMT(t->name), MSTR_FMT(*tag)); + } + } +} static void TestSuite_PrintJsonSystemHeader(FILE *stream) @@ -989,7 +1022,11 @@ TestSuite_Run(TestSuite *suite) /* IN */ TestSuite_PrintTests(suite); } - if ((suite->flags & TEST_HELPTEXT) || (suite->flags & TEST_LISTTESTS)) { + if (suite->flags & TEST_TESTS_CMAKE) { + TestSuite_PrintCMake(suite); + } + + if ((suite->flags & TEST_HELPTEXT) || (suite->flags & TEST_LISTTESTS) || (suite->flags & TEST_TESTS_CMAKE)) { return 0; } diff --git a/src/libmongoc/tests/TestSuite.h b/src/libmongoc/tests/TestSuite.h index 9486010268a..432f4305fe2 100644 --- a/src/libmongoc/tests/TestSuite.h +++ b/src/libmongoc/tests/TestSuite.h @@ -96,6 +96,7 @@ bson_open(const char *filename, int flags, ...) #define TEST_DEBUGOUTPUT (1 << 3) #define TEST_TRACE (1 << 4) #define TEST_LISTTESTS (1 << 5) +#define TEST_TESTS_CMAKE (1 << 6) #define CERT_CA CERT_TEST_DIR "/ca.pem" @@ -671,6 +672,10 @@ struct Test { * @brief The C string that names the test case */ mstr name; + /** + * @brief Set of tags that are associated with this test case + */ + mstr_vec tags; /** * @brief The function that will be executed for the test case */ @@ -704,6 +709,7 @@ Test_Destroy(Test *t) t->dtor(t->ctx); } mstr_delete(t->name); + mstr_vec_destroy(&t->tags); CheckFuncVec_destroy(&t->checks); } @@ -764,11 +770,10 @@ struct _TestFnCtx { TestFuncDtor dtor; }; - void TestSuite_Init(TestSuite *suite, const char *name, int argc, char **argv); void -TestSuite_Add(TestSuite *suite, const char *name, TestFunc func); +TestSuite_Add(TestSuite *suite, const char *name_and_tags, TestFunc func); int TestSuite_CheckLive(void); void @@ -779,21 +784,22 @@ void _TestSuite_AddMockServerTest(TestSuite *suite, const char *name, TestFunc func, ...); #define TestSuite_AddMockServerTest(_suite, _name, ...) _TestSuite_AddMockServerTest(_suite, _name, __VA_ARGS__, NULL) void -TestSuite_AddWC(TestSuite *suite, const char *name, TestFuncWC func, TestFuncDtor dtor, void *ctx); +TestSuite_AddWC(TestSuite *suite, const char *name_and_tags, TestFuncWC func, TestFuncDtor dtor, void *ctx); Test * -_V_TestSuite_AddFull(TestSuite *suite, const char *name, TestFuncWC func, TestFuncDtor dtor, void *ctx, va_list ap); +_V_TestSuite_AddFull( + TestSuite *suite, const char *name_and_tags, TestFuncWC func, TestFuncDtor dtor, void *ctx, va_list ap); void -_TestSuite_AddFull(TestSuite *suite, const char *name, TestFuncWC func, TestFuncDtor dtor, void *ctx, ...); +TestSuite_AddFull(TestSuite *suite, const char *name_and_tags, TestFuncWC func, TestFuncDtor dtor, void *ctx, ...); void _TestSuite_TestFnCtxDtor(void *ctx); #define TestSuite_AddFull(_suite, _name, _func, _dtor, _ctx, ...) \ - _TestSuite_AddFull(_suite, _name, _func, _dtor, _ctx, __VA_ARGS__, NULL) -#define TestSuite_AddFullWithTestFn(_suite, _name, _func, _dtor, _test_fn, ...) \ - do { \ - TestFnCtx *ctx = bson_malloc(sizeof(TestFnCtx)); \ - ctx->test_fn = (TestFunc)(_test_fn); \ - ctx->dtor = _dtor; \ - _TestSuite_AddFull(_suite, _name, _func, _TestSuite_TestFnCtxDtor, ctx, __VA_ARGS__, NULL); \ + (TestSuite_AddFull)(_suite, _name, _func, _dtor, _ctx, __VA_ARGS__, NULL) +#define TestSuite_AddFullWithTestFn(_suite, _name, _func, _dtor, _test_fn, ...) \ + do { \ + TestFnCtx *ctx = bson_malloc(sizeof(TestFnCtx)); \ + ctx->test_fn = (TestFunc)(_test_fn); \ + ctx->dtor = _dtor; \ + TestSuite_AddFull(_suite, _name, _func, _TestSuite_TestFnCtxDtor, ctx, __VA_ARGS__, NULL); \ } while (0) int TestSuite_Run(TestSuite *suite); diff --git a/src/libmongoc/tests/test-mcd-azure-imds.c b/src/libmongoc/tests/test-mcd-azure-imds.c index e208c9638fa..785e22c9ca9 100644 --- a/src/libmongoc/tests/test-mcd-azure-imds.c +++ b/src/libmongoc/tests/test-mcd-azure-imds.c @@ -107,5 +107,10 @@ test_mcd_azure_imds_install(TestSuite *suite) { TestSuite_Add(suite, "/azure/imds/http/parse", _test_oauth_parse); TestSuite_Add(suite, "/azure/imds/http/request", _test_http_req); - TestSuite_AddFull(suite, "/azure/imds/http/talk", _test_with_mock_server, NULL, NULL, have_mock_server_env); + TestSuite_AddFull(suite, + "/azure/imds/http/talk [uses:fake_kms_provider_server]", + _test_with_mock_server, + NULL, + NULL, + have_mock_server_env); } From 4e844f6f612e406b6a4ebccee3dc0c7d8ef94626 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Fri, 3 Oct 2025 10:17:14 -0600 Subject: [PATCH 08/49] Add parens around assignment-as-condition --- src/common/tests/test-mlib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/tests/test-mlib.c b/src/common/tests/test-mlib.c index 71526334e4d..4e3e295bdf7 100644 --- a/src/common/tests/test-mlib.c +++ b/src/common/tests/test-mlib.c @@ -1217,7 +1217,7 @@ _test_int_vec(void) // Append an element int *el; - mlib_check(el = int_vec_push(&ints)); + mlib_check((el = int_vec_push(&ints))); *el = 42; mlib_check(int_vec_begin(&ints)[0], eq, 42); mlib_check(ints.size, eq, 1); From 6837d55d4678766a72bb695e930f421f228b30be Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Fri, 3 Oct 2025 10:20:38 -0600 Subject: [PATCH 09/49] Add Earthly alias for CTest with uvx --- Earthfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Earthfile b/Earthfile index ba6af57c399..6ebb576e250 100644 --- a/Earthfile +++ b/Earthfile @@ -137,6 +137,7 @@ PREP_CMAKE: FUNCTION # Run all CMake commands using uvx: RUN __alias cmake uvx cmake + RUN __alias ctest uvx --from=cmake ctest # Executing any CMake command will warm the cache: RUN cmake --version | head -n 1 From 319e351950f4bed3a39e576737559b55e536f118 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Fri, 3 Oct 2025 10:39:49 -0600 Subject: [PATCH 10/49] The GCP test also requires the fake KMS server --- src/libmongoc/tests/test-service-gcp.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/libmongoc/tests/test-service-gcp.c b/src/libmongoc/tests/test-service-gcp.c index 724c00eace7..36f9b2aed42 100644 --- a/src/libmongoc/tests/test-service-gcp.c +++ b/src/libmongoc/tests/test-service-gcp.c @@ -107,5 +107,10 @@ test_service_gcp_install(TestSuite *suite) { TestSuite_Add(suite, "/gcp/http/parse", _test_gcp_parse); TestSuite_Add(suite, "/gcp/http/request", _test_gcp_http_request); - TestSuite_AddFull(suite, "/gcp/http/talk", _test_with_mock_server, NULL, NULL, have_mock_server_env); + TestSuite_AddFull(suite, + "/gcp/http/talk [uses:fake_kms_provider_server]", + _test_with_mock_server, + NULL, + NULL, + have_mock_server_env); } From 9de54abeac7c6b512ea50a120b93db4d337c529f Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Fri, 3 Oct 2025 12:09:19 -0600 Subject: [PATCH 11/49] Windows-only compile error --- src/libmongoc/tests/TestSuite.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libmongoc/tests/TestSuite.c b/src/libmongoc/tests/TestSuite.c index 68d1876ca56..43b3ed4221d 100644 --- a/src/libmongoc/tests/TestSuite.c +++ b/src/libmongoc/tests/TestSuite.c @@ -406,7 +406,7 @@ TestSuite_RunFuncInChild(TestSuite *suite, /* IN */ si.cb = sizeof(si); ZeroMemory(&pi, sizeof(pi)); - cmdline = bson_strdup_printf("%s --silent --no-fork -l %s", suite->prgname, test->name); + cmdline = bson_strdup_printf("%s --silent --no-fork -l %.*s", suite->prgname, MSTR_FMT(test->name)); if (!CreateProcess(NULL, cmdline, From aed388da8cf406386d863e27f530a793d75453d4 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Fri, 3 Oct 2025 12:38:24 -0600 Subject: [PATCH 12/49] No SSIZE_MAX on Windows --- src/common/src/mlib/str.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/common/src/mlib/str.h b/src/common/src/mlib/str.h index 1b53c3ad36f..44a1a9c3914 100644 --- a/src/common/src/mlib/str.h +++ b/src/common/src/mlib/str.h @@ -33,6 +33,7 @@ #include #include +#include /** * @brief A simple non-owning string-view type. @@ -697,7 +698,7 @@ mstr_resize_for_overwrite(mstr *const str, const size_t new_len) { // We need to allocate one additional char to hold the null terminator size_t alloc_size = new_len; - if (mlib_add(&alloc_size, 1) || alloc_size > SSIZE_MAX) { + if (mlib_add(&alloc_size, 1) || alloc_size > PTRDIFF_MAX) { // Allocation size is too large return false; } From b4d93bbf530117cd754dd4adf5c2705338b93e8e Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Fri, 3 Oct 2025 13:54:28 -0600 Subject: [PATCH 13/49] uninit-var in MSVC --- src/common/src/mlib/str.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/src/mlib/str.h b/src/common/src/mlib/str.h index 44a1a9c3914..28c3fa2e01e 100644 --- a/src/common/src/mlib/str.h +++ b/src/common/src/mlib/str.h @@ -834,7 +834,7 @@ static inline mstr mstr_concat(mstr_view a, mstr_view b) { mstr ret = {NULL, 0}; - size_t cat_len; + size_t cat_len = 0; if (mlib_add(&cat_len, a.len, b.len)) { // Size would overflow. No go. return ret; From 11c9aa809eaf957daacc5b6000380029cfdd0ebd Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Fri, 3 Oct 2025 14:13:06 -0600 Subject: [PATCH 14/49] Fix UBSan issue for null pointer arithmetic --- src/common/src/mlib/vec.t.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/src/mlib/vec.t.h b/src/common/src/mlib/vec.t.h index 793724c1000..fd21d497ac0 100644 --- a/src/common/src/mlib/vec.t.h +++ b/src/common/src/mlib/vec.t.h @@ -417,7 +417,7 @@ mlib_extern_c_end(); #ifndef mlib_vec_foreach #define mlib_vec_foreach(Type, VarName, Vector) \ - for (Type *VarName = (Vector).data; VarName != (Vector).data + (Vector).size; ++VarName) + for (Type *VarName = (Vector).data; VarName && (VarName != (Vector).data + (Vector).size); ++VarName) #endif #ifndef mlib_vec_at From 91d517b02a3bdb585cc298557021c670b9271e3f Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Mon, 6 Oct 2025 12:20:48 -0600 Subject: [PATCH 15/49] Fix another nullptr-arith warning --- src/common/src/mlib/vec.t.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/common/src/mlib/vec.t.h b/src/common/src/mlib/vec.t.h index fd21d497ac0..921add05d2a 100644 --- a/src/common/src/mlib/vec.t.h +++ b/src/common/src/mlib/vec.t.h @@ -267,7 +267,9 @@ fn(resize(VecName *const self, size_t const count)) mlib_noexcept // Check if we aren't actually growing the vector. if (count <= self->size) { // We need to destroy elements at the tail. If `count == size`, this is a no-op. - fn(erase(self, fn(begin(self)) + count, fn(end(self)))); + if (self->data) { + fn(erase(self, fn(begin(self)) + count, fn(end(self)))); + } return true; } From 6a0c534e4db04e70eb552f51aad7c32c6006b7e3 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Mon, 6 Oct 2025 12:35:40 -0600 Subject: [PATCH 16/49] Tests can declare resource locks in tags --- build/cmake/LoadTests.cmake | 20 +++++++++++++++++--- src/libmongoc/tests/test-mcd-azure-imds.c | 2 +- src/libmongoc/tests/test-service-gcp.c | 2 +- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/build/cmake/LoadTests.cmake b/build/cmake/LoadTests.cmake index 8e6b9abde8a..288d819b29e 100644 --- a/build/cmake/LoadTests.cmake +++ b/build/cmake/LoadTests.cmake @@ -44,9 +44,21 @@ foreach(casename IN LISTS MONGOC_TESTS) # Find what test fixtures the test wants by inspecting labels. Each "uses:" # label defines the names of the test fixtures that a particular case requires - set(fixtures "${labels}") - list(FILTER fixtures INCLUDE REGEX "^uses:") - list(TRANSFORM fixtures REPLACE "^uses:(.*)$" "mongoc/fixtures/\\1") + list( + TRANSFORM labels + REPLACE "^uses:(.*)$" "mongoc/fixtures/\\1" + REGEX "^uses:" + OUTPUT_VARIABLE fixtures + ) + + list(TRANSFORM labels + REPLACE "^lock:(.*)" "\\1" + REGEX "^lock:" + OUTPUT_VARIABLE lock + ) + if("live" IN_LIST labels) + list(APPEND lock live-server) + endif() # Add a label for all test cases generated via this script so that they # can be (de)selected separately: @@ -66,5 +78,7 @@ foreach(casename IN LISTS MONGOC_TESTS) LABELS "${labels}" # Fixture requirements: FIXTURES_REQUIRED "${fixtures}" + # Resources that need exclusive access by this test: + RESOURCE_LOCK "${lock}" ) endforeach() diff --git a/src/libmongoc/tests/test-mcd-azure-imds.c b/src/libmongoc/tests/test-mcd-azure-imds.c index 785e22c9ca9..8cc871796eb 100644 --- a/src/libmongoc/tests/test-mcd-azure-imds.c +++ b/src/libmongoc/tests/test-mcd-azure-imds.c @@ -108,7 +108,7 @@ test_mcd_azure_imds_install(TestSuite *suite) TestSuite_Add(suite, "/azure/imds/http/parse", _test_oauth_parse); TestSuite_Add(suite, "/azure/imds/http/request", _test_http_req); TestSuite_AddFull(suite, - "/azure/imds/http/talk [uses:fake_kms_provider_server]", + "/azure/imds/http/talk [uses:fake_kms_provider_server][lock:fake-kms]", _test_with_mock_server, NULL, NULL, diff --git a/src/libmongoc/tests/test-service-gcp.c b/src/libmongoc/tests/test-service-gcp.c index 36f9b2aed42..db634b92f71 100644 --- a/src/libmongoc/tests/test-service-gcp.c +++ b/src/libmongoc/tests/test-service-gcp.c @@ -108,7 +108,7 @@ test_service_gcp_install(TestSuite *suite) TestSuite_Add(suite, "/gcp/http/parse", _test_gcp_parse); TestSuite_Add(suite, "/gcp/http/request", _test_gcp_http_request); TestSuite_AddFull(suite, - "/gcp/http/talk [uses:fake_kms_provider_server]", + "/gcp/http/talk [uses:fake_kms_provider_server][lock:fake-kms]", _test_with_mock_server, NULL, NULL, From 5ac0881d0994f02e1711d97bae5e7a81edc687a5 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Tue, 7 Oct 2025 12:21:28 -0600 Subject: [PATCH 17/49] Yet another nullptr-arith --- src/common/src/mlib/vec.t.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/common/src/mlib/vec.t.h b/src/common/src/mlib/vec.t.h index 921add05d2a..7715d130789 100644 --- a/src/common/src/mlib/vec.t.h +++ b/src/common/src/mlib/vec.t.h @@ -118,7 +118,7 @@ typedef struct VecName { T * end() noexcept { - return data + size; + return data ? data + size : data; } #endif } VecName; @@ -140,7 +140,7 @@ fn(begin(VecName *v)) mlib_noexcept vec_inline_spec T * fn(end(VecName *v)) mlib_noexcept { - return v->data + v->size; + return v->data ? v->data + v->size : v->data; } /** @@ -158,7 +158,7 @@ fn(cbegin(VecName const *v)) mlib_noexcept vec_inline_spec T const * fn(cend(VecName const *v)) mlib_noexcept { - return v->data + v->size; + return v->data ? v->data + v->size : v->data; } /** From 5329c7a55f08f402e13ca321590d2b00f1625c17 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Tue, 7 Oct 2025 13:43:49 -0600 Subject: [PATCH 18/49] Suppress an over-eager Clang warning --- build/cmake/MongoC-Warnings.cmake | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/build/cmake/MongoC-Warnings.cmake b/build/cmake/MongoC-Warnings.cmake index aad93436e29..26080beb01f 100644 --- a/build/cmake/MongoC-Warnings.cmake +++ b/build/cmake/MongoC-Warnings.cmake @@ -103,4 +103,7 @@ mongoc_add_warning_options ( # Aside: Disable CRT insecurity warnings msvc:/D_CRT_SECURE_NO_WARNINGS - ) + + # Old Clang has an over-aggressive missing-braces warning that warns on the `foo = {0}` idiom + clang:clang-lt10:-Wno-missing-braces +) From 24225c02f0cfc9883ee353b8d10941e2b44ebd92 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Tue, 7 Oct 2025 13:44:31 -0600 Subject: [PATCH 19/49] Clean up automatic test properties from tags --- build/cmake/LoadTests.cmake | 40 +++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/build/cmake/LoadTests.cmake b/build/cmake/LoadTests.cmake index 288d819b29e..d6df59789aa 100644 --- a/build/cmake/LoadTests.cmake +++ b/build/cmake/LoadTests.cmake @@ -6,7 +6,7 @@ if(NOT EXISTS "${TEST_LIBMONGOC_EXE}") # This will fail if 'test-libmongoc' is not compiled yet. message(WARNING "The test executable ${TEST_LIBMONGOC_EXE} is not present. " - "Its tests will not be registered") + "Its tests will not be registered") add_test(mongoc/not-found NOT_FOUND) return() endif() @@ -32,6 +32,14 @@ set(all_env TEST_KMS_PROVIDER_HOST=localhost:14987 # Refer: Fixtures.cmake ) +function(list_select list_var) + cmake_parse_arguments(PARSE_ARGV 1 arg "" "SELECT;REPLACE;OUT" "") + set(seq "${${list_var}}") + list(FILTER seq INCLUDE REGEX "${arg_SELECT}") + list(TRANSFORM seq REPLACE "${arg_SELECT}" "${arg_REPLACE}") + set("${arg_OUT}" "${seq}" PARENT_SCOPE) +endfunction() + # The emitted code defines a list MONGOC_TESTS with the name of every test case # in the suite. foreach(casename IN LISTS MONGOC_TESTS) @@ -40,22 +48,24 @@ foreach(casename IN LISTS MONGOC_TESTS) add_test("${name}" "${TEST_LIBMONGOC_EXE}" --ctest-run "${casename}") # The emitted code defines a TAGS list for every test case that it emits. We use # these as the LABELS for the test case + unset(labels) set(labels "${MONGOC_TEST_${casename}_TAGS}") # Find what test fixtures the test wants by inspecting labels. Each "uses:" # label defines the names of the test fixtures that a particular case requires - list( - TRANSFORM labels - REPLACE "^uses:(.*)$" "mongoc/fixtures/\\1" - REGEX "^uses:" - OUTPUT_VARIABLE fixtures - ) + list_select(labels SELECT "^uses:(.*)$" REPLACE "mongoc/fixtures/\\1" OUT fixtures) - list(TRANSFORM labels - REPLACE "^lock:(.*)" "\\1" - REGEX "^lock:" - OUTPUT_VARIABLE lock - ) + # For any "lock:..." labels, add a resource lock with the corresponding name + list_select(labels SELECT "^lock:(.*)$" REPLACE "\\1" OUT lock) + + # Tests can set a timeout with a tag: + list_select(labels SELECT "^timeout:(.*)$" REPLACE "\\1" OUT timeout) + if(NOT timeout) + # Default timeout of 5 seconds + set(timeout 5) + endif() + + # If a test declares that it is "live", lock exclusive access to the live server if("live" IN_LIST labels) list(APPEND lock live-server) endif() @@ -70,15 +80,15 @@ foreach(casename IN LISTS MONGOC_TESTS) # If a test emits '@@ctest-skipped@@', this tells us that the test is # skipped. SKIP_REGULAR_EXPRESSION "@@ctest-skipped@@" - # 45 seconds of timeout on each test. - TIMEOUT 45 + # Apply a timeout to each test, either the default or one from test tags + TIMEOUT "${timeout}" # Common environment variables: ENVIRONMENT "${all_env}" # Apply the labels LABELS "${labels}" # Fixture requirements: FIXTURES_REQUIRED "${fixtures}" - # Resources that need exclusive access by this test: + # Test may lock resources: RESOURCE_LOCK "${lock}" ) endforeach() From 535b88b30b31cfdb992365dc1c0f596169feaab6 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Tue, 7 Oct 2025 13:56:47 -0600 Subject: [PATCH 20/49] No -Wmissing-braces anywhere --- CMakeLists.txt | 1 + build/cmake/MongoC-Warnings.cmake | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4343a19442b..bb56c31e0ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -285,6 +285,7 @@ if(ENABLE_MAINTAINER_FLAGS) gnu-like:-Wuninitialized # Disabled, for now: gnu-like:-Wno-strict-aliasing + gnu-like:-Wno-missing-braces # Sign-comparison-mismatch: gnu-like:-Wsign-compare msvc:/we4018 diff --git a/build/cmake/MongoC-Warnings.cmake b/build/cmake/MongoC-Warnings.cmake index 26080beb01f..f7489137d16 100644 --- a/build/cmake/MongoC-Warnings.cmake +++ b/build/cmake/MongoC-Warnings.cmake @@ -103,7 +103,4 @@ mongoc_add_warning_options ( # Aside: Disable CRT insecurity warnings msvc:/D_CRT_SECURE_NO_WARNINGS - - # Old Clang has an over-aggressive missing-braces warning that warns on the `foo = {0}` idiom - clang:clang-lt10:-Wno-missing-braces ) From a8ea90faf69aefe41ec29410fc0f1a0a94384f4e Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Mon, 20 Oct 2025 11:46:03 -0600 Subject: [PATCH 21/49] Add an mstr formatting function, change to destroy mstr objects in-place --- src/common/src/mlib/str.h | 152 ++++++++++++++++++++++++++++++-- src/common/src/mlib/str_vec.h | 2 +- src/common/tests/test-mlib.c | 25 ++++-- src/libmongoc/tests/TestSuite.c | 2 +- src/libmongoc/tests/TestSuite.h | 8 +- 5 files changed, 169 insertions(+), 20 deletions(-) diff --git a/src/common/src/mlib/str.h b/src/common/src/mlib/str.h index 28c3fa2e01e..b3420f88f35 100644 --- a/src/common/src/mlib/str.h +++ b/src/common/src/mlib/str.h @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -649,6 +650,13 @@ mstr_contains_any_of(mstr_view str, mstr_view needle) * and a size `len`. If not null, the pointer `data` points to an array of mutable * `char` of length `len + 1`, where the character at `data[len]` is always zero, * and must not be modified. + * + * @note The string MAY contain nul (zero-value) characters, so using them with + * C string APIs could truncate unexpectedly. + * @note The string itself may be "null" if the `data` member of the string is + * a null pointer. A zero-initialized `mstr` is null. The null string is distinct + * from the empty string, which has a non-null `.data` that points to an empty + * C string. */ typedef struct mstr { /** @@ -668,7 +676,7 @@ typedef struct mstr { char *data; /** * @brief The number of characters in the array pointed-to by `data` - * that preceed the null terminator. + * that precede the null terminator. */ size_t len; } mstr; @@ -738,7 +746,7 @@ mstr_resize(mstr *str, size_t new_len) return false; } // Check how many chars we added/removed - const ptrdiff_t len_diff = new_len - str->len; + const ptrdiff_t len_diff = mlib_assert_sub(ptrdiff_t, new_len, str->len); if (len_diff > 0) { // We added new chars. Zero-init all the new chars memset(str->data + old_len, 0, (size_t)len_diff); @@ -770,17 +778,33 @@ mstr_new(size_t new_len) } /** - * @brief Delete an `mstr` that was created with an allocating API, including - * the resize APIs + * @brief Free the resources associated with an mstr object. * - * @param s An `mstr` object. If the object is null, this function is a no-op. + * @param s Pointer to an `mstr` object. If pointer or the pointed-to-object is null, + * this function is a no-op. * - * After this call, the value of the `s` object has been consumed and is invalid. + * After this call, the pointed-to `s` will be a null `mstr` */ static inline void -mstr_delete(mstr s) +mstr_destroy(mstr *s) { - free(s.data); + if (s) { + free(s->data); + s->len = 0; + s->data = NULL; + } +} + +/** + * @brief Obtain a null mstr string object. + * + * @return mstr A null string, with a null data pointer and zero size + */ +static inline mstr +mstr_null(void) +{ + const mstr ret = {0}; + return ret; } /** @@ -989,5 +1013,117 @@ mstr_replace(mstr *str, mstr_view needle, mstr_view sub) } } +/** + * @brief Like `mstr_sprintf_append`, but accepts the va_list directly. + */ +MLIB_IF_GNU_LIKE(__attribute__((format(printf, 2, 0)))) +static inline bool +mstr_vsprintf_append(mstr *string, const char *format, va_list args) +{ + mlib_check(string != NULL, because, "Output string parameter is required"); + // The size of the string before we appended any characters to it. This is also + // the zero-based offset where we will be inserting new characters. + const size_t start_size = string->len; + + // Try to guess the length of the added string + size_t added_len = strlen(format); + // Give use some wiggle room since we are inserting characters: + if (mlib_mul(&added_len, 2)) { + // Doubling the size overflowed. Not likely, but just use the original string + // size and grow as-needed. + added_len = strlen(format); + } + + while (1) { + // Calculate the new size of the string + size_t new_size = 0; + if (mlib_add(&new_size, start_size, added_len)) { + // Adding more space overflows + return false; + } + // Try to make room + if (!mstr_resize(string, new_size)) { + // Failed to allocate the new region. + return false; + } + // Calc the size with the null terminator + size_t len_with_null = added_len; + if (mlib_add(&len_with_null, 1)) { + // Unlikely: Overflow + return false; + } + + // Do the formatting + va_list dup_args; + va_copy(dup_args, args); + int n_chars = vsnprintf(string->data + start_size, len_with_null, format, dup_args); + va_end(dup_args); + + // On error, returns a negative value + if (n_chars < 0) { + return false; + } + + // Upon success, vsnprintf returns the number of chars that were written, at most + // as many chars as are available + if ((size_t)n_chars <= added_len) { + // Success. This add+resize never fails + mlib_check(!mlib_add(&new_size, start_size, (size_t)n_chars)); + mlib_check(mstr_resize(string, new_size)); + return true; + } + + // Otherwise, returns the number of chars that would have been written. Update + // the number of chars that we expect to insert, and then try again. + added_len = (size_t)n_chars; + } +} + +/** + * @brief Append content to a string using `printf()` style formatting. + * + * @param string Pointer to a valid or null string object which will be modified + * @param format A printf-style format string to append onto `string` + * @param args The interpolation arguments for `format` + * + * @retval true If-and-only-if the string is successfully modified + * @retval false If there was an error during formatting. The content of `string` + * is unspecified. + * + * This function maintains the existing content of `string` and only inserts + * additional characters at the end of the string. + */ +MLIB_IF_GNU_LIKE(__attribute__((format(printf, 2, 3)))) +static inline bool +mstr_sprintf_append(mstr *string, const char *format, ...) +{ + va_list args; + va_start(args, format); + const bool okay = mstr_vsprintf_append(string, format, args); + va_end(args); + return okay; +} + +/** + * @brief Format a string according to `printf` rules + * + * @param f The format string to be used. + * @param ... The formatting arguments to interpolate into the string + */ +MLIB_IF_GNU_LIKE(__attribute__((format(printf, 1, 2)))) +static inline mstr +mstr_sprintf(const char *f, ...) +{ + mstr ret = mstr_null(); + va_list args; + va_start(args, f); + const bool okay = mstr_vsprintf_append(&ret, f, args); + va_end(args); + if (!okay) { + mstr_destroy(&ret); + return mstr_null(); + } + return ret; +} #endif // MLIB_STR_H_INCLUDED diff --git a/src/common/src/mlib/str_vec.h b/src/common/src/mlib/str_vec.h index bdfe723ed3e..38b6fedb6b7 100644 --- a/src/common/src/mlib/str_vec.h +++ b/src/common/src/mlib/str_vec.h @@ -24,7 +24,7 @@ #include #define T mstr -#define VecDestroyElement(Ptr) (mstr_delete(*Ptr), Ptr->data = NULL, Ptr->len = 0) +#define VecDestroyElement(Ptr) (mstr_destroy(Ptr)) #define VecCopyElement(Dst, Src) (*Dst = mstr_copy(*Src), Dst->data != NULL) #include diff --git a/src/common/tests/test-mlib.c b/src/common/tests/test-mlib.c index 4e3e295bdf7..a6089a809cc 100644 --- a/src/common/tests/test-mlib.c +++ b/src/common/tests/test-mlib.c @@ -942,7 +942,7 @@ _test_str(void) mlib_check(s.data != NULL); // The null terminator is present: mlib_check(s.data[0], eq, 0); - mstr_delete(s); + mstr_destroy(&s); } // Simple copy of a C string @@ -950,14 +950,14 @@ _test_str(void) mstr s = mstr_copy_cstring("foo bar"); mlib_check(s.len, eq, 7); mlib_check(mstr_cmp(s, ==, mstr_cstring("foo bar"))); - mstr_delete(s); + mstr_destroy(&s); } // Concat two strings { mstr s = mstr_concat(mstr_cstring("foo"), mstr_cstring("bar")); mlib_check(mstr_cmp(s, ==, mstr_cstring("foobar"))); - mstr_delete(s); + mstr_destroy(&s); } // Append individual characters @@ -967,7 +967,7 @@ _test_str(void) mstr_pushchar(&s, 'o'); mstr_pushchar(&s, 'o'); mlib_check(mstr_cmp(s, ==, mstr_cstring("foo"))); - mstr_delete(s); + mstr_destroy(&s); } // Splice deletion @@ -984,7 +984,7 @@ _test_str(void) mlib_check(mstr_splice(&s, 4, 0, mstr_cstring("quux "))); mlib_check(mstr_cmp(s, ==, mstr_cstring("foo quux bar baz"))); - mstr_delete(s); + mstr_destroy(&s); } // Replacing @@ -1009,10 +1009,22 @@ _test_str(void) mlib_check(mstr_replace(&s, mstr_cstring(""), mstr_cstring("a"))); } - mstr_delete(s); + mstr_destroy(&s); } } +static void +_test_str_format(void) +{ + mstr s = mstr_sprintf("foo"); + mlib_check(s.len, eq, 3); + mlib_check(s.data, str_eq, "foo"); + mstr_sprintf_append(&s, " bar"); + mlib_check(s.data, str_eq, "foo bar"); + mstr_sprintf_append(&s, " %d", 123); + mlib_check(s.data, str_eq, "foo bar 123"); + mstr_destroy(&s); +} static void _test_duration(void) @@ -1306,6 +1318,7 @@ test_mlib_install(TestSuite *suite) TestSuite_Add(suite, "/mlib/ckdint-partial", _test_ckdint_partial); TestSuite_Add(suite, "/mlib/str_view", _test_str_view); TestSuite_Add(suite, "/mlib/str", _test_str); + TestSuite_Add(suite, "/mlib/str-format", _test_str_format); TestSuite_Add(suite, "/mlib/duration", _test_duration); TestSuite_Add(suite, "/mlib/time_point", _test_time_point); TestSuite_Add(suite, "/mlib/sleep", _test_sleep); diff --git a/src/libmongoc/tests/TestSuite.c b/src/libmongoc/tests/TestSuite.c index 43b3ed4221d..1c7797956d1 100644 --- a/src/libmongoc/tests/TestSuite.c +++ b/src/libmongoc/tests/TestSuite.c @@ -1061,7 +1061,7 @@ TestSuite_Destroy(TestSuite *suite) bson_free(suite->name); bson_free(suite->prgname); - mstr_delete(suite->ctest_run); + mstr_destroy(&suite->ctest_run); mstr_vec_destroy(&suite->match_patterns); TestSkipVec_destroy(&suite->failing_flaky_skips); } diff --git a/src/libmongoc/tests/TestSuite.h b/src/libmongoc/tests/TestSuite.h index 432f4305fe2..6f351f290a0 100644 --- a/src/libmongoc/tests/TestSuite.h +++ b/src/libmongoc/tests/TestSuite.h @@ -708,7 +708,7 @@ Test_Destroy(Test *t) if (t->dtor) { t->dtor(t->ctx); } - mstr_delete(t->name); + mstr_destroy(&t->name); mstr_vec_destroy(&t->tags); CheckFuncVec_destroy(&t->checks); } @@ -739,9 +739,9 @@ struct TestSkip { static inline void TestSkip_Destroy(TestSkip *skip) { - mstr_delete(skip->test_name); - mstr_delete(skip->subtest_desc); - mstr_delete(skip->reason); + mstr_destroy(&skip->test_name); + mstr_destroy(&skip->subtest_desc); + mstr_destroy(&skip->reason); } #define T TestSkip From d0231314475272d17ce517dcc5931d152e2771a5 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Mon, 20 Oct 2025 16:49:26 -0600 Subject: [PATCH 22/49] Define a CTest fixture for our simple HTTP server --- build/cmake/TestFixtures.cmake | 8 ++++++++ src/libmongoc/tests/test-mongoc-http.c | 16 ++++++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/build/cmake/TestFixtures.cmake b/build/cmake/TestFixtures.cmake index d79d15ae60d..854dfc21c9a 100644 --- a/build/cmake/TestFixtures.cmake +++ b/build/cmake/TestFixtures.cmake @@ -47,3 +47,11 @@ mongo_define_subprocess_fixture( "${_MONGOC_BUILD_SCRIPT_DIR}/bottle.py" fake_kms_provider_server:kms_provider --bind localhost:14987 # Port 14987 chosen arbitrarily ) + +# Run our very simple HTTP server in a fixture process +mongo_define_subprocess_fixture( + mongoc/fixtures/simple-http-server-18000 + SPAWN_WAIT 1 + COMMAND + $ -u "${mongo-c-driver_SOURCE_DIR}/.evergreen/scripts/simple_http_server.py" + ) diff --git a/src/libmongoc/tests/test-mongoc-http.c b/src/libmongoc/tests/test-mongoc-http.c index 30ca457db11..816b4e94d32 100644 --- a/src/libmongoc/tests/test-mongoc-http.c +++ b/src/libmongoc/tests/test-mongoc-http.c @@ -88,9 +88,17 @@ test_mongoc_http_post(void *unused) void test_http_install(TestSuite *suite) { - TestSuite_AddFull( - suite, "/http/get", test_mongoc_http_get, NULL /* dtor */, NULL /* ctx */, test_framework_skip_if_offline); + TestSuite_AddFull(suite, + "/http/get [uses:simple-http-server-18000]", + test_mongoc_http_get, + NULL /* dtor */, + NULL /* ctx */, + test_framework_skip_if_offline); - TestSuite_AddFull( - suite, "/http/post", test_mongoc_http_post, NULL /* dtor */, NULL /* ctx */, test_framework_skip_if_offline); + TestSuite_AddFull(suite, + "/http/post [uses:simple-http-server-18000]", + test_mongoc_http_post, + NULL /* dtor */, + NULL /* ctx */, + test_framework_skip_if_offline); } From a044a4c29676d730da65910c059244410a11a83c Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Mon, 20 Oct 2025 16:50:40 -0600 Subject: [PATCH 23/49] Tweak default timeout and locking behavior --- build/cmake/LoadTests.cmake | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/build/cmake/LoadTests.cmake b/build/cmake/LoadTests.cmake index d6df59789aa..9739e0fc49b 100644 --- a/build/cmake/LoadTests.cmake +++ b/build/cmake/LoadTests.cmake @@ -61,13 +61,9 @@ foreach(casename IN LISTS MONGOC_TESTS) # Tests can set a timeout with a tag: list_select(labels SELECT "^timeout:(.*)$" REPLACE "\\1" OUT timeout) if(NOT timeout) - # Default timeout of 5 seconds - set(timeout 5) - endif() - - # If a test declares that it is "live", lock exclusive access to the live server - if("live" IN_LIST labels) - list(APPEND lock live-server) + # Default timeout of 10 seconds. If a test takes longer than this, it either + # has a bug or it needs to declare a longer timeout. + set(timeout 10) endif() # Add a label for all test cases generated via this script so that they From 651d7db67eda34aa51cd137e6dd0b973ae77dab3 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Mon, 20 Oct 2025 16:52:48 -0600 Subject: [PATCH 24/49] Add test tags and properties throughout --- src/libmongoc/tests/TestSuite.c | 5 +- src/libmongoc/tests/json-test.c | 6 +- src/libmongoc/tests/test-mongoc-aggregate.c | 2 +- src/libmongoc/tests/test-mongoc-bulk.c | 50 +++-- src/libmongoc/tests/test-mongoc-bulkwrite.c | 30 +-- .../tests/test-mongoc-change-stream.c | 66 +++--- src/libmongoc/tests/test-mongoc-client-pool.c | 13 +- .../tests/test-mongoc-client-session.c | 210 +++++++++++------- .../test-mongoc-client-side-encryption.c | 103 +++++---- src/libmongoc/tests/test-mongoc-client.c | 84 ++++--- src/libmongoc/tests/test-mongoc-cluster.c | 37 +-- src/libmongoc/tests/test-mongoc-cmd.c | 2 +- src/libmongoc/tests/test-mongoc-collection.c | 104 ++++++--- src/libmongoc/tests/test-mongoc-counters.c | 17 +- src/libmongoc/tests/test-mongoc-crud.c | 26 +-- src/libmongoc/tests/test-mongoc-cursor.c | 57 ++--- src/libmongoc/tests/test-mongoc-cyrus.c | 2 +- src/libmongoc/tests/test-mongoc-database.c | 8 +- src/libmongoc/tests/test-mongoc-dns.c | 20 +- src/libmongoc/tests/test-mongoc-exhaust.c | 40 +++- .../tests/test-mongoc-find-and-modify.c | 2 +- .../tests/test-mongoc-gridfs-bucket.c | 7 +- src/libmongoc/tests/test-mongoc-gridfs.c | 16 +- .../tests/test-mongoc-loadbalanced.c | 16 +- .../tests/test-mongoc-long-namespace.c | 12 +- .../tests/test-mongoc-max-staleness.c | 14 +- .../tests/test-mongoc-mongos-pinning.c | 4 +- .../tests/test-mongoc-primary-stepdown.c | 4 +- .../tests/test-mongoc-retryable-reads.c | 8 +- .../tests/test-mongoc-retryable-writes.c | 26 ++- .../tests/test-mongoc-sample-commands.c | 3 +- src/libmongoc/tests/test-mongoc-scram.c | 8 +- src/libmongoc/tests/test-mongoc-sdam.c | 10 +- .../test-mongoc-server-selection-errors.c | 16 +- src/libmongoc/tests/test-mongoc-socket.c | 12 +- .../tests/test-mongoc-topology-reconcile.c | 26 ++- .../tests/test-mongoc-topology-scanner.c | 4 +- src/libmongoc/tests/test-mongoc-topology.c | 67 +++--- .../tests/test-mongoc-transactions.c | 30 ++- .../tests/test-mongoc-with-transaction.c | 2 +- .../tests/test-mongoc-write-commands.c | 9 +- .../tests/test-mongoc-write-concern.c | 11 +- src/libmongoc/tests/test-mongoc-x509.c | 4 +- 43 files changed, 731 insertions(+), 462 deletions(-) diff --git a/src/libmongoc/tests/TestSuite.c b/src/libmongoc/tests/TestSuite.c index 1c7797956d1..768f32f99b9 100644 --- a/src/libmongoc/tests/TestSuite.c +++ b/src/libmongoc/tests/TestSuite.c @@ -278,7 +278,10 @@ TestSuite_AddLive(TestSuite *suite, /* IN */ const char *name, /* IN */ TestFunc func) /* IN */ { - TestSuite_AddFullWithTestFn(suite, name, TestSuite_AddHelper, NULL, func, TestSuite_CheckLive); + // Add the `lock:live-server` tag to the test. + mstr with_tags = mstr_sprintf("%s [lock:live-server]", name); + TestSuite_AddFullWithTestFn(suite, with_tags.data, TestSuite_AddHelper, NULL, func, TestSuite_CheckLive); + mstr_destroy(&with_tags); } diff --git a/src/libmongoc/tests/json-test.c b/src/libmongoc/tests/json-test.c index 61e37262f0b..366f3d757b3 100644 --- a/src/libmongoc/tests/json-test.c +++ b/src/libmongoc/tests/json-test.c @@ -1940,11 +1940,13 @@ _install_json_test_suite_with_check(TestSuite *suite, const char *base, const ch bson_destroy(test); test = modified; } + mstr name = mstr_copy_cstring(skip_json); + mstr_append(&name, mstr_cstring(" [lock:live-server][json][slow]")); /* list of "check" functions that decide whether to skip the test */ va_start(ap, callback); - _V_TestSuite_AddFull(suite, skip_json, callback, &bson_destroy_vp, test, ap); - + _V_TestSuite_AddFull(suite, name.data, callback, &bson_destroy_vp, test, ap); va_end(ap); + mstr_destroy(&name); } } diff --git a/src/libmongoc/tests/test-mongoc-aggregate.c b/src/libmongoc/tests/test-mongoc-aggregate.c index cb6e85ae223..591c22dc049 100644 --- a/src/libmongoc/tests/test-mongoc-aggregate.c +++ b/src/libmongoc/tests/test-mongoc-aggregate.c @@ -175,7 +175,7 @@ test_aggregate_install(TestSuite *suite) { TestSuite_AddMockServerTest(suite, "/Aggregate/query_flags", test_query_flags); TestSuite_AddFull(suite, - "/Aggregate/write_respects_read_prefs", + "/Aggregate/write_respects_read_prefs [lock:live-server]", test_write_respects_read_prefs, NULL, NULL, diff --git a/src/libmongoc/tests/test-mongoc-bulk.c b/src/libmongoc/tests/test-mongoc-bulk.c index 15f79c5b07b..abecda3d8a1 100644 --- a/src/libmongoc/tests/test-mongoc-bulk.c +++ b/src/libmongoc/tests/test-mongoc-bulk.c @@ -4849,22 +4849,34 @@ test_bulk_install(TestSuite *suite) TestSuite_AddLive(suite, "/BulkOperation/upsert_ordered", test_upsert_ordered); TestSuite_AddLive(suite, "/BulkOperation/upsert_unordered", test_upsert_unordered); TestSuite_AddFull(suite, - "/BulkOperation/upsert_unordered_oversized", + "/BulkOperation/upsert_unordered_oversized [lock:live-server][timeout:30]", test_upsert_unordered_oversized, NULL, NULL, test_framework_skip_if_slow_or_live); - TestSuite_AddFull( - suite, "/BulkOperation/upsert_large", test_upsert_large, NULL, NULL, test_framework_skip_if_slow_or_live); - TestSuite_AddFull( - suite, "/BulkOperation/upsert_huge", test_upsert_huge, NULL, NULL, test_framework_skip_if_slow_or_live); + TestSuite_AddFull(suite, + "/BulkOperation/upsert_large [lock:live-server][timeout:30]", + test_upsert_large, + NULL, + NULL, + test_framework_skip_if_slow_or_live); + TestSuite_AddFull(suite, + "/BulkOperation/upsert_huge [lock:live-server][timeout:30]", + test_upsert_huge, + NULL, + NULL, + test_framework_skip_if_slow_or_live); TestSuite_AddLive(suite, "/BulkOperation/upserted_index_ordered", test_upserted_index_ordered); TestSuite_AddLive(suite, "/BulkOperation/upserted_index_unordered", test_upserted_index_unordered); TestSuite_AddLive(suite, "/BulkOperation/update_one_ordered", test_update_one_ordered); TestSuite_AddLive(suite, "/BulkOperation/update_one_unordered", test_update_one_unordered); TestSuite_AddLive(suite, "/BulkOperation/update_with_opts_validate", test_update_with_opts_validate); - TestSuite_AddFull( - suite, "/BulkOperation/update_arrayfilters", test_update_arrayfilters, NULL, NULL, TestSuite_CheckLive); + TestSuite_AddFull(suite, + "/BulkOperation/update_arrayfilters [lock:live-server]", + test_update_arrayfilters, + NULL, + NULL, + TestSuite_CheckLive); TestSuite_AddLive(suite, "/BulkOperation/update/hint/validate", test_update_hint_validate); TestSuite_AddLive(suite, "/BulkOperation/delete/hint/validate", test_delete_hint_validate); TestSuite_AddLive(suite, "/BulkOperation/replace_one_ordered", test_replace_one_ordered); @@ -4882,13 +4894,13 @@ test_bulk_install(TestSuite *suite) TestSuite_AddLive(suite, "/BulkOperation/single_unordered_bulk", test_single_unordered_bulk); TestSuite_AddLive(suite, "/BulkOperation/single_error_unordered_bulk", test_single_error_unordered_bulk); TestSuite_AddFull(suite, - "/BulkOperation/oversized/ordered", + "/BulkOperation/oversized/ordered [lock:live-server][timeout:30]", test_oversized_bulk_op_ordered, NULL, NULL, test_framework_skip_if_slow_or_live); TestSuite_AddFull(suite, - "/BulkOperation/oversized/unordered", + "/BulkOperation/oversized/unordered [lock:live-server][timeout:30]", test_oversized_bulk_op_unordered, NULL, NULL, @@ -4913,21 +4925,25 @@ test_bulk_install(TestSuite *suite) "/BulkOperation/wtimeout_duplicate_key/write_commands", test_wtimeout_plus_duplicate_key_err_write_commands); TestSuite_AddFull(suite, - "/BulkOperation/large_inserts_ordered", + "/BulkOperation/large_inserts_ordered [lock:live-server][timeout:30]", test_large_inserts_ordered, NULL, NULL, test_framework_skip_if_slow_or_live); TestSuite_AddFull(suite, - "/BulkOperation/large_inserts_unordered", + "/BulkOperation/large_inserts_unordered [lock:live-server][timeout:30]", test_large_inserts_unordered, NULL, NULL, test_framework_skip_if_slow_or_live); - TestSuite_AddFull( - suite, "/BulkOperation/numerous_ordered", test_numerous_ordered, NULL, NULL, test_framework_skip_if_slow_or_live); TestSuite_AddFull(suite, - "/BulkOperation/numerous_unordered", + "/BulkOperation/numerous_ordered [lock:live-server][timeout:30]", + test_numerous_ordered, + NULL, + NULL, + test_framework_skip_if_slow_or_live); + TestSuite_AddFull(suite, + "/BulkOperation/numerous_unordered [lock:live-server][timeout:30]", test_numerous_unordered, NULL, NULL, @@ -4938,13 +4954,13 @@ test_bulk_install(TestSuite *suite) TestSuite_AddLive(suite, "/BulkOperation/OP_MSG/max_batch_size", test_bulk_max_batch_size); TestSuite_AddLive(suite, "/BulkOperation/OP_MSG/max_msg_size", test_bulk_max_msg_size); TestSuite_AddFull(suite, - "/BulkOperation/split", + "/BulkOperation/split [lock:live-server]", test_bulk_split, NULL /* dtor */, NULL /* ctx */, test_framework_skip_if_no_sessions); TestSuite_AddFull(suite, - "/BulkOperation/write_concern/split", + "/BulkOperation/write_concern/split [lock:live-server]", test_bulk_write_concern_split, NULL /* dtor */, NULL /* ctx */, @@ -4968,7 +4984,7 @@ test_bulk_install(TestSuite *suite) TestSuite_AddMockServerTest(suite, "/BulkOperation/opts/let", test_bulk_let); TestSuite_AddMockServerTest(suite, "/BulkOperation/opts/let/multi", test_bulk_let_multi); TestSuite_AddFull(suite, - "/BulkOperation/multiple_errors", + "/BulkOperation/multiple_errors [lock:live-server]", test_bulk_write_multiple_errors, NULL, NULL, diff --git a/src/libmongoc/tests/test-mongoc-bulkwrite.c b/src/libmongoc/tests/test-mongoc-bulkwrite.c index 69cf623d7d4..5accd904200 100644 --- a/src/libmongoc/tests/test-mongoc-bulkwrite.c +++ b/src/libmongoc/tests/test-mongoc-bulkwrite.c @@ -904,7 +904,7 @@ void test_bulkwrite_install(TestSuite *suite) { TestSuite_AddFull(suite, - "/bulkwrite/insert", + "/bulkwrite/insert [lock:live-server]", test_bulkwrite_insert, NULL /* dtor */, NULL /* ctx */, @@ -912,7 +912,7 @@ test_bulkwrite_install(TestSuite *suite) ); TestSuite_AddFull(suite, - "/bulkwrite/upsert_with_null", + "/bulkwrite/upsert_with_null [lock:live-server]", test_bulkwrite_upsert_with_null, NULL /* dtor */, NULL /* ctx */, @@ -920,7 +920,7 @@ test_bulkwrite_install(TestSuite *suite) ); TestSuite_AddFull(suite, - "/bulkwrite/writeError", + "/bulkwrite/writeError [lock:live-server]", test_bulkwrite_writeError, NULL /* dtor */, NULL /* ctx */, @@ -928,7 +928,7 @@ test_bulkwrite_install(TestSuite *suite) ); TestSuite_AddFull(suite, - "/bulkwrite/session_with_unacknowledged", + "/bulkwrite/session_with_unacknowledged [lock:live-server]", test_bulkwrite_session_with_unacknowledged, NULL /* dtor */, NULL /* ctx */, @@ -936,7 +936,7 @@ test_bulkwrite_install(TestSuite *suite) test_framework_skip_if_no_sessions); TestSuite_AddFull(suite, - "/bulkwrite/double_execute", + "/bulkwrite/double_execute [lock:live-server]", test_bulkwrite_double_execute, NULL /* dtor */, NULL /* ctx */, @@ -944,7 +944,7 @@ test_bulkwrite_install(TestSuite *suite) ); TestSuite_AddFull(suite, - "/bulkwrite/server_id", + "/bulkwrite/server_id [lock:live-server]", test_bulkwrite_serverid, NULL /* dtor */, NULL /* ctx */, @@ -952,7 +952,7 @@ test_bulkwrite_install(TestSuite *suite) ); TestSuite_AddFull(suite, - "/bulkwrite/server_id/unacknowledged", + "/bulkwrite/server_id/unacknowledged [lock:live-server]", test_bulkwrite_serverid_unacknowledged, NULL /* dtor */, NULL /* ctx */, @@ -960,7 +960,7 @@ test_bulkwrite_install(TestSuite *suite) ); TestSuite_AddFull(suite, - "/bulkwrite/server_id/on_retry", + "/bulkwrite/server_id/on_retry [lock:live-server]", test_bulkwrite_serverid_on_retry, NULL /* dtor */, NULL /* ctx */, @@ -970,7 +970,7 @@ test_bulkwrite_install(TestSuite *suite) ); TestSuite_AddFull(suite, - "/bulkwrite/extra", + "/bulkwrite/extra [lock:live-server]", test_bulkwrite_extra, NULL /* dtor */, NULL /* ctx */, @@ -978,7 +978,7 @@ test_bulkwrite_install(TestSuite *suite) ); TestSuite_AddFull(suite, - "/bulkwrite/no_verbose_results", + "/bulkwrite/no_verbose_results [lock:live-server]", test_bulkwrite_no_verbose_results, NULL /* dtor */, NULL /* ctx */, @@ -986,7 +986,7 @@ test_bulkwrite_install(TestSuite *suite) ); TestSuite_AddFull(suite, - "/bulkwrite/many_namespaces", + "/bulkwrite/many_namespaces [lock:live-server]", test_bulkwrite_many_namespaces, NULL /* dtor */, NULL /* ctx */, @@ -995,7 +995,7 @@ test_bulkwrite_install(TestSuite *suite) ); TestSuite_AddFull(suite, - "/bulkwrite/execute_requires_client", + "/bulkwrite/execute_requires_client [lock:live-server]", test_bulkwrite_execute_requires_client, NULL /* dtor */, NULL /* ctx */, @@ -1003,7 +1003,7 @@ test_bulkwrite_install(TestSuite *suite) ); TestSuite_AddFull(suite, - "/bulkwrite/two_large_inserts", + "/bulkwrite/two_large_inserts [lock:live-server]", test_bulkwrite_two_large_inserts, NULL /* dtor */, NULL /* ctx */, @@ -1011,7 +1011,7 @@ test_bulkwrite_install(TestSuite *suite) ); TestSuite_AddFull(suite, - "/bulkwrite/client_error_no_result", + "/bulkwrite/client_error_no_result [lock:live-server]", test_bulkwrite_client_error_no_result, NULL /* dtor */, NULL /* ctx */, @@ -1019,7 +1019,7 @@ test_bulkwrite_install(TestSuite *suite) ); TestSuite_AddFull(suite, - "/bulkwrite/check_acknowledged", + "/bulkwrite/check_acknowledged [lock:live-server]", test_bulkwrite_check_acknowledged, NULL /* dtor */, NULL /* ctx */, diff --git a/src/libmongoc/tests/test-mongoc-change-stream.c b/src/libmongoc/tests/test-mongoc-change-stream.c index 5ef5daa86c1..e08456e9f91 100644 --- a/src/libmongoc/tests/test-mongoc-change-stream.c +++ b/src/libmongoc/tests/test-mongoc-change-stream.c @@ -2135,14 +2135,14 @@ test_change_stream_install(TestSuite *suite) TestSuite_AddMockServerTest(suite, "/change_stream/pipeline", test_change_stream_pipeline); TestSuite_AddFull(suite, - "/change_stream/live/single_server", + "/change_stream/live/single_server [lock:live-server]", test_change_stream_live_single_server, NULL, NULL, test_framework_skip_if_not_single); TestSuite_AddFull(suite, - "/change_stream/live/track_resume_token", + "/change_stream/live/track_resume_token [lock:live-server]", test_change_stream_live_track_resume_token, NULL, NULL, @@ -2150,21 +2150,21 @@ test_change_stream_install(TestSuite *suite) test_framework_skip_if_no_failpoint); TestSuite_AddFull(suite, - "/change_stream/live/batch_size", + "/change_stream/live/batch_size [lock:live-server]", test_change_stream_live_batch_size, NULL, NULL, test_framework_skip_if_not_replset); TestSuite_AddFull(suite, - "/change_stream/live/missing_resume_token", + "/change_stream/live/missing_resume_token [lock:live-server]", test_change_stream_live_missing_resume_token, NULL, NULL, test_framework_skip_if_not_replset); TestSuite_AddFull(suite, - "/change_stream/live/invalid_resume_token", + "/change_stream/live/invalid_resume_token [lock:live-server]", test_change_stream_live_invalid_resume_token, NULL, NULL, @@ -2175,14 +2175,14 @@ test_change_stream_install(TestSuite *suite) TestSuite_AddMockServerTest(suite, "/change_stream/options", test_change_stream_options); TestSuite_AddFull(suite, - "/change_stream/live/watch", + "/change_stream/live/watch [lock:live-server]", test_change_stream_live_watch, NULL, NULL, test_framework_skip_if_not_replset); TestSuite_AddFull(suite, - "/change_stream/live/read_prefs", + "/change_stream/live/read_prefs [lock:live-server]", test_change_stream_live_read_prefs, NULL, NULL, @@ -2192,28 +2192,28 @@ test_change_stream_install(TestSuite *suite) TestSuite_Add(suite, "/change_stream/server_selection_fails", test_change_stream_server_selection_fails); TestSuite_AddFull(suite, - "/change_stream/next_after_error", + "/change_stream/next_after_error [lock:live-server]", test_change_stream_next_after_error, NULL, NULL, test_framework_skip_if_not_replset); TestSuite_AddFull(suite, - "/change_stream/accepts_array", + "/change_stream/accepts_array [lock:live-server]", test_change_stream_accepts_array, NULL, NULL, test_framework_skip_if_not_replset); TestSuite_AddMockServerTest(suite, "/change_stream/getmore_errors", test_getmore_errors); TestSuite_AddFull(suite, - "/change_stream/start_at_operation_time", + "/change_stream/start_at_operation_time [lock:live-server]", test_change_stream_start_at_operation_time, NULL, NULL, test_framework_skip_if_not_replset, test_framework_skip_if_no_crypto); TestSuite_AddFull(suite, - "/change_stream/resume_with_post_batch_resume_token", + "/change_stream/resume_with_post_batch_resume_token [lock:live-server]", test_change_stream_resume_with_post_batch_resume_token, NULL, NULL, @@ -2221,26 +2221,42 @@ test_change_stream_install(TestSuite *suite) test_framework_skip_if_no_crypto, test_framework_skip_if_no_failpoint); TestSuite_AddFull(suite, - "/change_stream/database", + "/change_stream/database [lock:live-server]", test_change_stream_database_watch, NULL, NULL, test_framework_skip_if_not_replset); - TestSuite_AddFull( - suite, "/change_stream/client", test_change_stream_client_watch, NULL, NULL, test_framework_skip_if_not_replset); - TestSuite_AddMockServerTest(suite, "/change_stream/resume_with_first_doc", test_resume_cases); + TestSuite_AddFull(suite, + "/change_stream/client [lock:live-server]", + test_change_stream_client_watch, + NULL, + NULL, + test_framework_skip_if_not_replset); + TestSuite_AddMockServerTest(suite, "/change_stream/resume_with_first_doc [timeout:10]", test_resume_cases); TestSuite_AddMockServerTest(suite, - "/change_stream/resume_with_first_doc/post_batch_resume_token", + "/change_stream/resume_with_first_doc/post_batch_resume_token [timeout:10]", test_resume_cases_with_post_batch_resume_token); - TestSuite_AddFull( - suite, "/change_stream/error_null_doc", test_error_null_doc, NULL, NULL, test_framework_skip_if_not_replset); - TestSuite_AddFull( - suite, "/change_stream/live/prose_test_11", prose_test_11, NULL, NULL, test_framework_skip_if_not_replset); + TestSuite_AddFull(suite, + "/change_stream/error_null_doc [lock:live-server]", + test_error_null_doc, + NULL, + NULL, + test_framework_skip_if_not_replset); + TestSuite_AddFull(suite, + "/change_stream/live/prose_test_11 [lock:live-server]", + prose_test_11, + NULL, + NULL, + test_framework_skip_if_not_replset); // Prose test 12 is removed. C driver does not support server 4.0.7. - TestSuite_AddFull( - suite, "/change_stream/live/prose_test_13", prose_test_13, NULL, NULL, test_framework_skip_if_not_replset); TestSuite_AddFull(suite, - "/change_stream/live/prose_test_14", + "/change_stream/live/prose_test_13 [lock:live-server]", + prose_test_13, + NULL, + NULL, + test_framework_skip_if_not_replset); + TestSuite_AddFull(suite, + "/change_stream/live/prose_test_14 [lock:live-server]", prose_test_14, NULL, NULL, @@ -2249,13 +2265,13 @@ test_change_stream_install(TestSuite *suite) TestSuite_AddMockServerTest(suite, "/change_streams/prose_test_17", prose_test_17); TestSuite_AddMockServerTest(suite, "/change_streams/prose_test_18", prose_test_18); TestSuite_AddFull(suite, - "/change_streams/iterate_after_invalidate", + "/change_streams/iterate_after_invalidate [lock:live-server]", iterate_after_invalidate, NULL, NULL, test_framework_skip_if_not_replset); TestSuite_AddFull(suite, - "/change_stream/batchSize0", + "/change_stream/batchSize0 [lock:live-server]", test_change_stream_batchSize0, NULL, NULL, diff --git a/src/libmongoc/tests/test-mongoc-client-pool.c b/src/libmongoc/tests/test-mongoc-client-pool.c index 3878b215117..76274925561 100644 --- a/src/libmongoc/tests/test-mongoc-client-pool.c +++ b/src/libmongoc/tests/test-mongoc-client-pool.c @@ -613,7 +613,7 @@ test_client_pool_install(TestSuite *suite) TestSuite_Add(suite, "/ClientPool/handshake", test_mongoc_client_pool_handshake); TestSuite_AddFull(suite, - "/ClientPool/create_client_pool_unused_session", + "/ClientPool/create_client_pool_unused_session [lock:live-server]", test_client_pool_create_unused_session, NULL /* dtor */, NULL /* ctx */, @@ -623,11 +623,13 @@ test_client_pool_install(TestSuite *suite) #endif TestSuite_AddLive(suite, "/ClientPool/destroy_without_push", test_client_pool_destroy_without_pushing); TestSuite_AddLive(suite, "/ClientPool/max_pool_size_exceeded", test_client_pool_max_pool_size_exceeded); - TestSuite_Add(suite, "/ClientPool/can_override_sockettimeoutms", test_client_pool_can_override_sockettimeoutms); + TestSuite_Add(suite, + "/ClientPool/can_override_sockettimeoutms [lock:live-server]", + test_client_pool_can_override_sockettimeoutms); TestSuite_AddFull( suite, - "/client_pool/disconnects_removed_servers/on_push", + "/client_pool/disconnects_removed_servers/on_push [lock:live-server]", disconnects_removed_servers_on_push, NULL, NULL, @@ -636,7 +638,7 @@ test_client_pool_install(TestSuite *suite) TestSuite_AddFull( suite, - "/client_pool/disconnects_removed_servers/in_pool", + "/client_pool/disconnects_removed_servers/in_pool [lock:live-server]", disconnects_removed_servers_in_pool, NULL, NULL, @@ -644,7 +646,8 @@ test_client_pool_install(TestSuite *suite) test_framework_skip_if_max_wire_version_less_than_9 /* require server 4.4+ for streaming monitoring protocol */); #if defined(MONGOC_ENABLE_SSL_OPENSSL) - TestSuite_Add(suite, "/ClientPool/openssl/change_ssl_opts", test_mongoc_client_pool_change_openssl_ctx); + TestSuite_Add( + suite, "/ClientPool/openssl/change_ssl_opts [lock:live-server]", test_mongoc_client_pool_change_openssl_ctx); #endif #if defined(MONGOC_ENABLE_SSL) diff --git a/src/libmongoc/tests/test-mongoc-client-session.c b/src/libmongoc/tests/test-mongoc-client-session.c index f65fc3a230b..2835b7c3370 100644 --- a/src/libmongoc/tests/test-mongoc-client-session.c +++ b/src/libmongoc/tests/test-mongoc-client-session.c @@ -2598,7 +2598,7 @@ test_session_install(TestSuite *suite) TestSuite_Add( suite, "/Session/opts/causal_consistency_and_snapshot", test_session_opts_causal_consistency_and_snapshot); TestSuite_AddFull(suite, - "/Session/no_crypto", + "/Session/no_crypto [lock:live-server]", test_session_no_crypto, NULL, NULL, @@ -2606,21 +2606,21 @@ test_session_install(TestSuite *suite) test_framework_skip_if_no_sessions, test_framework_skip_if_crypto); TestSuite_AddFull(suite, - "/Session/lifo/single", + "/Session/lifo/single [lock:live-server]", test_session_pool_lifo_single, NULL, NULL, test_framework_skip_if_no_sessions, test_framework_skip_if_no_crypto); TestSuite_AddFull(suite, - "/Session/lifo/pooled", + "/Session/lifo/pooled [lock:live-server]", test_session_pool_lifo_pooled, NULL, NULL, test_framework_skip_if_no_sessions, test_framework_skip_if_no_crypto); TestSuite_AddFull(suite, - "/Session/timeout/single", + "/Session/timeout/single [lock:live-server][timeout:30]", test_session_pool_timeout_single, NULL, NULL, @@ -2628,7 +2628,7 @@ test_session_install(TestSuite *suite) test_framework_skip_if_no_crypto, test_framework_skip_if_slow); TestSuite_AddFull(suite, - "/Session/timeout/pooled", + "/Session/timeout/pooled [lock:live-server][timeout:30]", test_session_pool_timeout_pooled, NULL, NULL, @@ -2636,7 +2636,7 @@ test_session_install(TestSuite *suite) test_framework_skip_if_no_crypto, test_framework_skip_if_slow); TestSuite_AddFull(suite, - "/Session/reap/single", + "/Session/reap/single [lock:live-server][timeout:30]", test_session_pool_reap_single, NULL, NULL, @@ -2644,7 +2644,7 @@ test_session_install(TestSuite *suite) test_framework_skip_if_no_crypto, test_framework_skip_if_slow); TestSuite_AddFull(suite, - "/Session/reap/pooled", + "/Session/reap/pooled [lock:live-server][timeout:30]", test_session_pool_reap_pooled, NULL, NULL, @@ -2652,21 +2652,21 @@ test_session_install(TestSuite *suite) test_framework_skip_if_no_crypto, test_framework_skip_if_slow); TestSuite_AddFull(suite, - "/Session/id_bad", + "/Session/id_bad [lock:live-server]", test_session_id_bad, NULL, NULL, test_framework_skip_if_no_sessions, test_framework_skip_if_no_crypto); TestSuite_AddFull(suite, - "/Session/supported/single", + "/Session/supported/single [lock:live-server]", test_session_supported_single, NULL, NULL, TestSuite_CheckLive, test_framework_skip_if_no_crypto); TestSuite_AddFull(suite, - "/Session/supported/pooled", + "/Session/supported/pooled [lock:live-server]", test_session_supported_pooled, NULL, NULL, @@ -2677,25 +2677,25 @@ test_session_install(TestSuite *suite) TestSuite_AddMockServerTest( suite, "/Session/end/mock/pooled", test_mock_end_sessions_pooled, test_framework_skip_if_no_crypto); TestSuite_AddMockServerTest(suite, - "/Session/end/mock/disconnected", + "/Session/end/mock/disconnected [timeout:20]", test_mock_end_sessions_server_disconnect, test_framework_skip_if_no_crypto); TestSuite_AddFull(suite, - "/Session/end/single", + "/Session/end/single [lock:live-server]", test_end_sessions_single, NULL, NULL, test_framework_skip_if_no_crypto, TestSuite_CheckLive); TestSuite_AddFull(suite, - "/Session/end/pooled", + "/Session/end/pooled [lock:live-server]", test_end_sessions_pooled, NULL, NULL, test_framework_skip_if_no_crypto, TestSuite_CheckLive); TestSuite_AddFull(suite, - "/Session/end/many/single", + "/Session/end/many/single [lock:live-server][timeout:30]", test_end_sessions_many_single, NULL, NULL, @@ -2703,7 +2703,7 @@ test_session_install(TestSuite *suite) TestSuite_CheckLive, test_framework_skip_if_slow); TestSuite_AddFull(suite, - "/Session/end/many/pooled", + "/Session/end/many/pooled [lock:live-server][timeout:30]", test_end_sessions_many_pooled, NULL, NULL, @@ -2711,14 +2711,14 @@ test_session_install(TestSuite *suite) TestSuite_CheckLive, test_framework_skip_if_slow); TestSuite_AddFull(suite, - "/Session/advance_cluster_time", + "/Session/advance_cluster_time [lock:live-server]", test_session_advance_cluster_time, NULL, NULL, test_framework_skip_if_no_crypto, test_framework_skip_if_no_sessions); TestSuite_AddFull(suite, - "/Session/advance_operation_time", + "/Session/advance_operation_time [lock:live-server]", test_session_advance_operation_time, NULL, NULL, @@ -2727,41 +2727,42 @@ test_session_install(TestSuite *suite) /* "true" is for tests that expect readConcern: afterClusterTime for causally * consistent sessions, "false" is for tests that prohibit readConcern */ - add_session_test(suite, "/Session/cmd", test_cmd, false); - add_session_test(suite, "/Session/read_cmd", test_read_cmd, true); - add_session_test(suite, "/Session/write_cmd", test_write_cmd, false); - add_session_test(suite, "/Session/read_write_cmd", test_read_write_cmd, true); - add_session_test(suite, "/Session/db_cmd", test_db_cmd, false); - add_session_test(suite, "/Session/cursor", test_cursor, true); - add_session_test(suite, "/Session/drop", test_drop, false); - add_session_test(suite, "/Session/drop_index", test_drop_index, false); - add_session_test(suite, "/Session/create_index", test_create_index, false); - add_session_test(suite, "/Session/replace_one", test_replace_one, false); - add_session_test(suite, "/Session/update_one", test_update_one, false); - add_session_test(suite, "/Session/update_many", test_update_many, false); - add_session_test(suite, "/Session/insert_one", test_insert_one, false); - add_session_test(suite, "/Session/insert_many", test_insert_many, false); - add_session_test(suite, "/Session/delete_one", test_delete_one, false); - add_session_test(suite, "/Session/delete_many", test_delete_many, false); - add_session_test(suite, "/Session/rename", test_rename, false); - add_session_test(suite, "/Session/fam", test_fam, true); - add_session_test(suite, "/Session/db_drop", test_db_drop, false); - add_session_test(suite, "/Session/gridfs_find", test_gridfs_find, true); - add_session_test(suite, "/Session/gridfs_find_one", test_gridfs_find_one, true); - add_session_test_wc(suite, "/Session/watch", test_watch, true, test_framework_skip_if_not_replset); - add_session_test(suite, "/Session/aggregate", test_aggregate, true); - add_session_test(suite, "/Session/create", test_create, false); - add_session_test(suite, "/Session/database_names", test_database_names, true); - add_session_test(suite, "/Session/find_databases", test_find_databases, true); - add_session_test(suite, "/Session/find_collections", test_find_collections, true); - add_session_test(suite, "/Session/collection_names", test_collection_names, true); - add_session_test(suite, "/Session/bulk", test_bulk, false); - add_session_test(suite, "/Session/find_indexes", test_find_indexes, true); + add_session_test(suite, "/Session/cmd [lock:live-server]", test_cmd, false); + add_session_test(suite, "/Session/read_cmd [lock:live-server]", test_read_cmd, true); + add_session_test(suite, "/Session/write_cmd [lock:live-server]", test_write_cmd, false); + add_session_test(suite, "/Session/read_write_cmd [lock:live-server]", test_read_write_cmd, true); + add_session_test(suite, "/Session/db_cmd [lock:live-server]", test_db_cmd, false); + add_session_test(suite, "/Session/cursor [lock:live-server]", test_cursor, true); + add_session_test(suite, "/Session/drop [lock:live-server]", test_drop, false); + add_session_test(suite, "/Session/drop_index [lock:live-server]", test_drop_index, false); + add_session_test(suite, "/Session/create_index [lock:live-server]", test_create_index, false); + add_session_test(suite, "/Session/replace_one [lock:live-server]", test_replace_one, false); + add_session_test(suite, "/Session/update_one [lock:live-server]", test_update_one, false); + add_session_test(suite, "/Session/update_many [lock:live-server]", test_update_many, false); + add_session_test(suite, "/Session/insert_one [lock:live-server]", test_insert_one, false); + add_session_test(suite, "/Session/insert_many [lock:live-server]", test_insert_many, false); + add_session_test(suite, "/Session/delete_one [lock:live-server]", test_delete_one, false); + add_session_test(suite, "/Session/delete_many [lock:live-server]", test_delete_many, false); + add_session_test(suite, "/Session/rename [lock:live-server]", test_rename, false); + add_session_test(suite, "/Session/fam [lock:live-server]", test_fam, true); + add_session_test(suite, "/Session/db_drop [lock:live-server]", test_db_drop, false); + add_session_test(suite, "/Session/gridfs_find [lock:live-server]", test_gridfs_find, true); + add_session_test(suite, "/Session/gridfs_find_one [lock:live-server]", test_gridfs_find_one, true); + add_session_test_wc( + suite, "/Session/watch [lock:live-server]", test_watch, true, test_framework_skip_if_not_replset); + add_session_test(suite, "/Session/aggregate [lock:live-server]", test_aggregate, true); + add_session_test(suite, "/Session/create [lock:live-server]", test_create, false); + add_session_test(suite, "/Session/database_names [lock:live-server]", test_database_names, true); + add_session_test(suite, "/Session/find_databases [lock:live-server]", test_find_databases, true); + add_session_test(suite, "/Session/find_collections [lock:live-server]", test_find_collections, true); + add_session_test(suite, "/Session/collection_names [lock:live-server]", test_collection_names, true); + add_session_test(suite, "/Session/bulk [lock:live-server]", test_bulk, false); + add_session_test(suite, "/Session/find_indexes [lock:live-server]", test_find_indexes, true); { session_test_helper_t *const helper = bson_malloc(sizeof(*helper)); *helper = (session_test_helper_t){.test_fn = test_bulk_set_session}; TestSuite_AddFull(suite, - "/Session/bulk_set_session", + "/Session/bulk_set_session [lock:live-server]", run_session_test_bulk_operation, &bson_free, helper, @@ -2772,7 +2773,7 @@ test_session_install(TestSuite *suite) session_test_helper_t *const helper = bson_malloc(sizeof(*helper)); *helper = (session_test_helper_t){.test_fn = test_bulk_set_client}; TestSuite_AddFull(suite, - "/Session/bulk_set_client", + "/Session/bulk_set_client [lock:live-server]", run_session_test_bulk_operation, &bson_free, helper, @@ -2780,79 +2781,124 @@ test_session_install(TestSuite *suite) test_framework_skip_if_no_crypto); } TestSuite_AddFull(suite, - "/Session/cursor_implicit_session", + "/Session/cursor_implicit_session [lock:live-server]", test_cursor_implicit_session, NULL, NULL, test_framework_skip_if_no_cluster_time, test_framework_skip_if_no_crypto); TestSuite_AddFull(suite, - "/Session/change_stream_implicit_session", + "/Session/change_stream_implicit_session [lock:live-server]", test_change_stream_implicit_session, NULL, NULL, test_framework_skip_if_no_cluster_time, test_framework_skip_if_no_crypto); TestSuite_AddFull(suite, - "/Session/cmd_error", + "/Session/cmd_error [lock:live-server]", test_cmd_error, NULL, NULL, test_framework_skip_if_no_cluster_time, test_framework_skip_if_no_crypto); TestSuite_AddFull(suite, - "/Session/read_concern", + "/Session/read_concern [lock:live-server]", test_read_concern, NULL, NULL, test_framework_skip_if_no_cluster_time, test_framework_skip_if_no_crypto); + add_unacknowledged_test(suite, + "/Session/unacknowledged/insert_one/explicit_cs/inherit_wc [lock:live-server]", + test_insert_one, + true, + true); + add_unacknowledged_test(suite, + "/Session/unacknowledged/insert_one/explicit_cs/explicit_wc [lock:live-server]", + test_insert_one, + true, + false); + add_unacknowledged_test(suite, + "/Session/unacknowledged/insert_one/implicit_cs/inherit_wc [lock:live-server]", + test_insert_one, + false, + true); + add_unacknowledged_test(suite, + "/Session/unacknowledged/insert_one/implicit_cs/explicit_wc [lock:live-server]", + test_insert_one, + false, + false); add_unacknowledged_test( - suite, "/Session/unacknowledged/insert_one/explicit_cs/inherit_wc", test_insert_one, true, true); + suite, "/Session/unacknowledged/bulk/explicit_cs/inherit_wc [lock:live-server]", test_bulk, true, true); add_unacknowledged_test( - suite, "/Session/unacknowledged/insert_one/explicit_cs/explicit_wc", test_insert_one, true, false); + suite, "/Session/unacknowledged/bulk/explicit_cs/explicit_wc [lock:live-server]", test_bulk, true, false); add_unacknowledged_test( - suite, "/Session/unacknowledged/insert_one/implicit_cs/inherit_wc", test_insert_one, false, true); + suite, "/Session/unacknowledged/bulk/implicit_cs/inherit_wc [lock:live-server]", test_bulk, false, true); add_unacknowledged_test( - suite, "/Session/unacknowledged/insert_one/implicit_cs/explicit_wc", test_insert_one, false, false); - add_unacknowledged_test(suite, "/Session/unacknowledged/bulk/explicit_cs/inherit_wc", test_bulk, true, true); - add_unacknowledged_test(suite, "/Session/unacknowledged/bulk/explicit_cs/explicit_wc", test_bulk, true, false); - add_unacknowledged_test(suite, "/Session/unacknowledged/bulk/implicit_cs/inherit_wc", test_bulk, false, true); - add_unacknowledged_test(suite, "/Session/unacknowledged/bulk/implicit_cs/explicit_wc", test_bulk, false, false); + suite, "/Session/unacknowledged/bulk/implicit_cs/explicit_wc [lock:live-server]", test_bulk, false, false); /* find_and_modify_with_opts only inherits acknowledged write concerns, so * skip tests that inherit a write concern. Technically, an explicit * unacknowledged write concern doesn't make much sense with findAndModify, * but this is testing the common code path for command execution. */ - add_unacknowledged_test( - suite, "/Session/unacknowledged/find_and_modify/explicit_cs/explicit_wc", test_fam, true, false); - add_unacknowledged_test( - suite, "/Session/unacknowledged/find_and_modify/implicit_cs/explicit_wc", test_fam, false, false); + add_unacknowledged_test(suite, + "/Session/unacknowledged/find_and_modify/explicit_cs/explicit_wc [lock:live-server]", + test_fam, + true, + false); + add_unacknowledged_test(suite, + "/Session/unacknowledged/find_and_modify/implicit_cs/explicit_wc [lock:live-server]", + test_fam, + false, + false); /* command_with_opts also does not inherit write concerns, but we still want * to test the common code path for command execution. */ - add_unacknowledged_test(suite, "/Session/unacknowledged/db_cmd/explicit_cs/explicit_wc", test_db_cmd, true, false); - add_unacknowledged_test(suite, "/Session/unacknowledged/db_cmd/implicit_cs/explicit_wc", test_db_cmd, false, false); - add_unacknowledged_test( - suite, "/Session/unacknowledged/read_write_cmd/explicit_cs/inherit_wc", test_read_write_cmd, true, true); - add_unacknowledged_test( - suite, "/Session/unacknowledged/read_write_cmd/explicit_cs/explicit_wc", test_read_write_cmd, true, false); - add_unacknowledged_test( - suite, "/Session/unacknowledged/read_write_cmd/implicit_cs/inherit_wc", test_read_write_cmd, false, true); - add_unacknowledged_test( - suite, "/Session/unacknowledged/read_write_cmd/implicit_cs/explicit_wc", test_read_write_cmd, false, false); - add_unacknowledged_test( - suite, "/Session/unacknowledged/write_cmd/explicit_cs/inherit_wc", test_write_cmd, true, true); add_unacknowledged_test( - suite, "/Session/unacknowledged/write_cmd/explicit_cs/explicit_wc", test_write_cmd, true, false); + suite, "/Session/unacknowledged/db_cmd/explicit_cs/explicit_wc [lock:live-server]", test_db_cmd, true, false); add_unacknowledged_test( - suite, "/Session/unacknowledged/write_cmd/implicit_cs/inherit_wc", test_write_cmd, false, true); + suite, "/Session/unacknowledged/db_cmd/implicit_cs/explicit_wc [lock:live-server]", test_db_cmd, false, false); + add_unacknowledged_test(suite, + "/Session/unacknowledged/read_write_cmd/explicit_cs/inherit_wc [lock:live-server]", + test_read_write_cmd, + true, + true); + add_unacknowledged_test(suite, + "/Session/unacknowledged/read_write_cmd/explicit_cs/explicit_wc [lock:live-server]", + test_read_write_cmd, + true, + false); + add_unacknowledged_test(suite, + "/Session/unacknowledged/read_write_cmd/implicit_cs/inherit_wc [lock:live-server]", + test_read_write_cmd, + false, + true); + add_unacknowledged_test(suite, + "/Session/unacknowledged/read_write_cmd/implicit_cs/explicit_wc [lock:live-server]", + test_read_write_cmd, + false, + false); add_unacknowledged_test( - suite, "/Session/unacknowledged/write_cmd/implicit_cs/explicit_wc", test_write_cmd, false, false); + suite, "/Session/unacknowledged/write_cmd/explicit_cs/inherit_wc [lock:live-server]", test_write_cmd, true, true); + add_unacknowledged_test(suite, + "/Session/unacknowledged/write_cmd/explicit_cs/explicit_wc [lock:live-server]", + test_write_cmd, + true, + false); + add_unacknowledged_test(suite, + "/Session/unacknowledged/write_cmd/implicit_cs/inherit_wc [lock:live-server]", + test_write_cmd, + false, + true); + add_unacknowledged_test(suite, + "/Session/unacknowledged/write_cmd/implicit_cs/explicit_wc [lock:live-server]", + test_write_cmd, + false, + false); install_json_test_suite_with_check( suite, JSON_DIR, "sessions/legacy", test_sessions_spec_cb, test_framework_skip_if_no_sessions); TestSuite_AddFull(suite, - "/Session/dirty", + "/Session/dirty [lock:live-server]", test_session_dirty, NULL /* dtor */, NULL /* ctx */, @@ -2862,7 +2908,7 @@ test_session_install(TestSuite *suite) test_framework_skip_if_single); TestSuite_AddFull(suite, - "/Session/snapshot/prose_test_1", + "/Session/snapshot/prose_test_1 [lock:live-server]", test_sessions_snapshot_prose_test_1, NULL, NULL, diff --git a/src/libmongoc/tests/test-mongoc-client-side-encryption.c b/src/libmongoc/tests/test-mongoc-client-side-encryption.c index 72c051e0c9c..8641dec1ce2 100644 --- a/src/libmongoc/tests/test-mongoc-client-side-encryption.c +++ b/src/libmongoc/tests/test-mongoc-client-side-encryption.c @@ -19,6 +19,7 @@ #include +#include #include #include @@ -6957,7 +6958,7 @@ test_client_side_encryption_install(TestSuite *suite) test_framework_skip_if_no_client_side_encryption); /* Prose tests from the spec. */ TestSuite_AddFull(suite, - "/client_side_encryption/create_datakey_with_custom_key_material", + "/client_side_encryption/create_datakey_with_custom_key_material [lock:live-server]", test_create_datakey_with_custom_key_material, NULL, NULL, @@ -6965,7 +6966,7 @@ test_client_side_encryption_install(TestSuite *suite) TestSuite_CheckLive, test_framework_skip_if_offline /* requires AWS */); TestSuite_AddFull(suite, - "/client_side_encryption/datakey_and_double_encryption", + "/client_side_encryption/datakey_and_double_encryption [lock:live-server]", test_datakey_and_double_encryption, NULL, NULL, @@ -6973,7 +6974,7 @@ test_client_side_encryption_install(TestSuite *suite) TestSuite_CheckLive, test_framework_skip_if_offline /* requires AWS */); TestSuite_AddFull(suite, - "/client_side_encryption/external_key_vault", + "/client_side_encryption/external_key_vault [lock:live-server]", test_external_key_vault, NULL, NULL, @@ -6981,14 +6982,14 @@ test_client_side_encryption_install(TestSuite *suite) TestSuite_CheckLive, test_framework_skip_if_no_auth /* requires auth for error check */); TestSuite_AddFull(suite, - "/client_side_encryption/bson_size_limits_and_batch_splitting", + "/client_side_encryption/bson_size_limits_and_batch_splitting [lock:live-server]", test_bson_size_limits_and_batch_splitting_no_qe, NULL, NULL, test_framework_skip_if_no_client_side_encryption, TestSuite_CheckLive); TestSuite_AddFull(suite, - "/client_side_encryption/bson_size_limits_and_batch_splitting_qe", + "/client_side_encryption/bson_size_limits_and_batch_splitting_qe [lock:live-server]", test_bson_size_limits_and_batch_splitting_qe, NULL, NULL, @@ -6996,14 +6997,14 @@ test_client_side_encryption_install(TestSuite *suite) test_framework_skip_if_max_wire_version_less_than_25, test_framework_skip_if_single); TestSuite_AddFull(suite, - "/client_side_encryption/views_are_prohibited", + "/client_side_encryption/views_are_prohibited [lock:live-server]", test_views_are_prohibited, NULL, NULL, test_framework_skip_if_no_client_side_encryption, TestSuite_CheckLive); TestSuite_AddFull(suite, - "/client_side_encryption/corpus", + "/client_side_encryption/corpus [lock:live-server]", test_corpus, NULL, NULL, @@ -7011,7 +7012,7 @@ test_client_side_encryption_install(TestSuite *suite) TestSuite_CheckLive, test_framework_skip_if_offline /* requires AWS */); TestSuite_AddFull(suite, - "/client_side_encryption/custom_endpoint", + "/client_side_encryption/custom_endpoint [lock:live-server]", test_custom_endpoint, NULL, NULL, @@ -7020,7 +7021,7 @@ test_client_side_encryption_install(TestSuite *suite) test_framework_skip_if_offline /* requires AWS, Azure, and GCP */); TestSuite_AddFull(suite, "/client_side_encryption/bypass_spawning_mongocryptd/" - "mongocryptdBypassSpawn", + "mongocryptdBypassSpawn [lock:live-server]", test_bypass_spawning_via_mongocryptdBypassSpawn, NULL, NULL, @@ -7028,7 +7029,7 @@ test_client_side_encryption_install(TestSuite *suite) TestSuite_CheckLive); TestSuite_AddFull(suite, "/client_side_encryption/bypass_spawning_mongocryptd/" - "bypassAutoEncryption", + "bypassAutoEncryption [lock:live-server]", test_bypass_spawning_via_bypassAutoEncryption, NULL, NULL, @@ -7036,7 +7037,7 @@ test_client_side_encryption_install(TestSuite *suite) TestSuite_CheckLive); TestSuite_AddFull(suite, "/client_side_encryption/bypass_spawning_mongocryptd/" - "bypassQueryAnalysis", + "bypassQueryAnalysis [lock:live-server]", test_bypass_spawning_via_bypassQueryAnalysis, NULL, NULL, @@ -7044,7 +7045,7 @@ test_client_side_encryption_install(TestSuite *suite) TestSuite_CheckLive); TestSuite_AddFull(suite, "/client_side_encryption/bypass_spawning_mongocryptd/" - "cryptSharedLibLoaded", + "cryptSharedLibLoaded [lock:live-server]", test_bypass_spawning_via_cryptSharedLibLoaded, NULL, NULL, @@ -7052,35 +7053,35 @@ test_client_side_encryption_install(TestSuite *suite) TestSuite_CheckLive, _skip_if_no_crypt_shared); TestSuite_AddFull(suite, - "/client_side_encryption/kms_tls/valid", + "/client_side_encryption/kms_tls/valid [lock:live-server]", test_kms_tls_cert_valid, NULL, NULL, test_framework_skip_if_no_client_side_encryption, TestSuite_CheckLive); TestSuite_AddFull(suite, - "/client_side_encryption/kms_tls/expired", + "/client_side_encryption/kms_tls/expired [lock:live-server]", test_kms_tls_cert_expired, NULL, NULL, test_framework_skip_if_no_client_side_encryption, TestSuite_CheckLive); TestSuite_AddFull(suite, - "/client_side_encryption/kms_tls/wrong_host", + "/client_side_encryption/kms_tls/wrong_host [lock:live-server]", test_kms_tls_cert_wrong_host, NULL, NULL, test_framework_skip_if_no_client_side_encryption, TestSuite_CheckLive); TestSuite_AddFull(suite, - "/client_side_encryption/unique_index_on_keyaltnames", + "/client_side_encryption/unique_index_on_keyaltnames [lock:live-server]", test_unique_index_on_keyaltnames, NULL, NULL, test_framework_skip_if_no_client_side_encryption, TestSuite_CheckLive); TestSuite_AddFull(suite, - "/client_side_encryption/prose_test_16/case1", + "/client_side_encryption/prose_test_16/case1 [lock:live-server][timeout:30]", test_rewrap_with_separate_client_encryption, NULL, NULL, @@ -7088,7 +7089,7 @@ test_client_side_encryption_install(TestSuite *suite) TestSuite_CheckLive, test_framework_skip_if_slow); TestSuite_AddFull(suite, - "/client_side_encryption/prose_test_16/case2", + "/client_side_encryption/prose_test_16/case2 [lock:live-server]", test_rewrap_without_provider, NULL, NULL, @@ -7097,28 +7098,28 @@ test_client_side_encryption_install(TestSuite *suite) /* Other, C driver specific, tests. */ TestSuite_AddFull(suite, - "/client_side_encryption/single_and_pool_mismatches", + "/client_side_encryption/single_and_pool_mismatches [lock:live-server]", test_invalid_single_and_pool_mismatches, NULL, NULL, test_framework_skip_if_no_client_side_encryption, TestSuite_CheckLive); TestSuite_AddFull(suite, - "/client_side_encryption/multi_threaded", + "/client_side_encryption/multi_threaded [lock:live-server]", test_multi_threaded, NULL, NULL, test_framework_skip_if_no_client_side_encryption, TestSuite_CheckLive); TestSuite_AddFull(suite, - "/client_side_encryption/malformed_explicit", + "/client_side_encryption/malformed_explicit [lock:live-server]", test_malformed_explicit, NULL, NULL, test_framework_skip_if_no_client_side_encryption, TestSuite_CheckLive); TestSuite_AddFull(suite, - "/client_side_encryption/kms_tls_options", + "/client_side_encryption/kms_tls_options [lock:live-server]", test_kms_tls_options, NULL, NULL, @@ -7130,20 +7131,20 @@ test_client_side_encryption_install(TestSuite *suite) test_framework_skip_if_windows); TestSuite_AddFull(suite, - "/client_side_encryption/kms_tls_options/extra_rejected", + "/client_side_encryption/kms_tls_options/extra_rejected [lock:live-server]", test_kms_tls_options_extra_rejected, NULL, NULL, test_framework_skip_if_no_client_side_encryption); TestSuite_AddFull(suite, - "/client_side_encryption/kms_retry", + "/client_side_encryption/kms_retry [lock:live-server]", test_kms_retry, NULL, NULL, test_framework_skip_if_no_client_side_encryption); TestSuite_AddFull(suite, - "/client_side_encryption/explicit_encryption/case1", + "/client_side_encryption/explicit_encryption/case1 [lock:live-server]", test_explicit_encryption_case1, NULL /* dtor */, NULL /* ctx */, @@ -7152,7 +7153,7 @@ test_client_side_encryption_install(TestSuite *suite) test_framework_skip_if_single); TestSuite_AddFull(suite, - "/client_side_encryption/explicit_encryption/case2", + "/client_side_encryption/explicit_encryption/case2 [lock:live-server]", test_explicit_encryption_case2, NULL /* dtor */, NULL /* ctx */, @@ -7161,7 +7162,7 @@ test_client_side_encryption_install(TestSuite *suite) test_framework_skip_if_single); TestSuite_AddFull(suite, - "/client_side_encryption/explicit_encryption/case3", + "/client_side_encryption/explicit_encryption/case3 [lock:live-server]", test_explicit_encryption_case3, NULL /* dtor */, NULL /* ctx */, @@ -7170,7 +7171,7 @@ test_client_side_encryption_install(TestSuite *suite) test_framework_skip_if_single); TestSuite_AddFull(suite, - "/client_side_encryption/explicit_encryption/case4", + "/client_side_encryption/explicit_encryption/case4 [lock:live-server]", test_explicit_encryption_case4, NULL /* dtor */, NULL /* ctx */, @@ -7179,7 +7180,7 @@ test_client_side_encryption_install(TestSuite *suite) test_framework_skip_if_single); TestSuite_AddFull(suite, - "/client_side_encryption/explicit_encryption/case5", + "/client_side_encryption/explicit_encryption/case5 [lock:live-server]", test_explicit_encryption_case5, NULL /* dtor */, NULL /* ctx */, @@ -7188,7 +7189,7 @@ test_client_side_encryption_install(TestSuite *suite) test_framework_skip_if_single); TestSuite_AddFull(suite, - "/client_side_encryption/decryption_events/case1", + "/client_side_encryption/decryption_events/case1 [lock:live-server]", test_decryption_events_case1, NULL /* dtor */, NULL /* ctx */, @@ -7196,7 +7197,7 @@ test_client_side_encryption_install(TestSuite *suite) TestSuite_CheckLive); TestSuite_AddFull(suite, - "/client_side_encryption/decryption_events/case2", + "/client_side_encryption/decryption_events/case2 [lock:live-server]", test_decryption_events_case2, NULL /* dtor */, NULL /* ctx */, @@ -7204,7 +7205,7 @@ test_client_side_encryption_install(TestSuite *suite) TestSuite_CheckLive); TestSuite_AddFull(suite, - "/client_side_encryption/decryption_events/case3", + "/client_side_encryption/decryption_events/case3 [lock:live-server]", test_decryption_events_case3, NULL /* dtor */, NULL /* ctx */, @@ -7213,7 +7214,7 @@ test_client_side_encryption_install(TestSuite *suite) TestSuite_AddFull(suite, - "/client_side_encryption/decryption_events/case4", + "/client_side_encryption/decryption_events/case4 [lock:live-server]", test_decryption_events_case4, NULL /* dtor */, NULL /* ctx */, @@ -7221,7 +7222,7 @@ test_client_side_encryption_install(TestSuite *suite) TestSuite_CheckLive); TestSuite_AddFull(suite, - "/client_side_encryption/qe_docs_example", + "/client_side_encryption/qe_docs_example [lock:live-server]", test_qe_docs_example, NULL /* dtor */, NULL /* ctx */, @@ -7230,7 +7231,7 @@ test_client_side_encryption_install(TestSuite *suite) test_framework_skip_if_single); TestSuite_AddFull(suite, - "/client_side_encryption/kms/callback", + "/client_side_encryption/kms/callback [lock:live-server]", test_kms_callback, NULL, // dtor NULL, // ctx @@ -7238,7 +7239,7 @@ test_client_side_encryption_install(TestSuite *suite) TestSuite_CheckLive); TestSuite_AddFull(suite, - "/client_side_encryption/kms/auto-aws/fail", + "/client_side_encryption/kms/auto-aws/fail [lock:live-server]", test_auto_aws_fail, NULL, NULL, @@ -7247,7 +7248,7 @@ test_client_side_encryption_install(TestSuite *suite) _not_have_aws_creds_env); TestSuite_AddFull(suite, - "/client_side_encryption/kms/auto-aws/succeed", + "/client_side_encryption/kms/auto-aws/succeed [lock:live-server]", test_auto_aws_succeed, NULL, NULL, @@ -7256,17 +7257,18 @@ test_client_side_encryption_install(TestSuite *suite) _have_aws_creds_env); TestSuite_AddFull(suite, - "/client_side_encryption/drop_qe_null_error", + "/client_side_encryption/drop_qe_null_error [lock:live-server]", test_drop_qe_null_error, NULL, NULL, test_framework_skip_if_no_client_side_encryption, TestSuite_CheckLive); - TestSuite_AddFull(suite, "/client_side_encryption/auto_datakeys", test_auto_datakeys, NULL, NULL, NULL); + TestSuite_AddFull( + suite, "/client_side_encryption/auto_datakeys [lock:live-server]", test_auto_datakeys, NULL, NULL, NULL); TestSuite_AddFull(suite, - "/client_side_encryption/createEncryptedCollection/simple", + "/client_side_encryption/createEncryptedCollection/simple [lock:live-server]", test_create_encrypted_collection_simple, NULL, NULL, @@ -7276,7 +7278,7 @@ test_client_side_encryption_install(TestSuite *suite) TestSuite_AddFull(suite, "/client_side_encryption/createEncryptedCollection/" - "missing-encryptedFields", + "missing-encryptedFields [lock:live-server]", test_create_encrypted_collection_no_encryptedFields, NULL, NULL, @@ -7285,7 +7287,7 @@ test_client_side_encryption_install(TestSuite *suite) test_framework_skip_if_single); TestSuite_AddFull(suite, "/client_side_encryption/createEncryptedCollection/" - "bad-keyId", + "bad-keyId [lock:live-server]", test_create_encrypted_collection_bad_keyId, NULL, NULL, @@ -7293,7 +7295,7 @@ test_client_side_encryption_install(TestSuite *suite) test_framework_skip_if_max_wire_version_less_than_21, test_framework_skip_if_single); TestSuite_AddFull(suite, - "/client_side_encryption/createEncryptedCollection/insert", + "/client_side_encryption/createEncryptedCollection/insert [lock:live-server]", test_create_encrypted_collection_insert, NULL, NULL, @@ -7301,7 +7303,7 @@ test_client_side_encryption_install(TestSuite *suite) test_framework_skip_if_max_wire_version_less_than_21, test_framework_skip_if_single); TestSuite_AddFull(suite, - "/client_side_encryption/bypass_mongocryptd_shared_library", + "/client_side_encryption/bypass_mongocryptd_shared_library [lock:live-server]", test_bypass_mongocryptd_shared_library, NULL, NULL, @@ -7344,12 +7346,14 @@ test_client_side_encryption_install(TestSuite *suite) char *test_name = bson_strdup_printf("/client_side_encryption/range_explicit_encryption/%s/%s", rc.name, rangeType); + mstr name_with_tags = mstr_copy_cstring(test_name); + mstr_append(&name_with_tags, mstr_cstring(" [lock:live-server]")); // Skip DecimalNoPrecision if not a replica set. if (0 == strcmp(rangeType, "DecimalNoPrecision")) { TestSuite_AddFull( suite, - test_name, + name_with_tags.data, rc.fn, NULL /* dtor */, (void *)rangeTypes[i] /* ctx */, @@ -7359,7 +7363,7 @@ test_client_side_encryption_install(TestSuite *suite) } else { TestSuite_AddFull( suite, - test_name, + name_with_tags.data, rc.fn, NULL /* dtor */, (void *)rangeTypes[i] /* ctx */, @@ -7367,13 +7371,14 @@ test_client_side_encryption_install(TestSuite *suite) test_framework_skip_if_max_wire_version_less_than_25, /* range queries require MongoDB 8.0+ */ test_framework_skip_if_single); } + mstr_destroy(&name_with_tags); bson_free(test_name); } } TestSuite_AddFull(suite, - "/client_side_encryption/range_explicit_encryption/applies_defaults", + "/client_side_encryption/range_explicit_encryption/applies_defaults [lock:live-server]", test_range_explicit_encryption_applies_defaults, NULL, NULL, @@ -7381,7 +7386,7 @@ test_client_side_encryption_install(TestSuite *suite) test_framework_skip_if_no_client_side_encryption); TestSuite_AddFull(suite, - "/client_side_encryption/test_lookup", + "/client_side_encryption/test_lookup [lock:live-server]", test_lookup, NULL, NULL, @@ -7389,7 +7394,7 @@ test_client_side_encryption_install(TestSuite *suite) test_framework_skip_if_single, /* QE not supported on standalone */ test_framework_skip_if_no_client_side_encryption); TestSuite_AddFull(suite, - "/client_side_encryption/test_lookup/pre-8.1", + "/client_side_encryption/test_lookup/pre-8.1 [lock:live-server]", test_lookup_pre81, NULL, NULL, diff --git a/src/libmongoc/tests/test-mongoc-client.c b/src/libmongoc/tests/test-mongoc-client.c index 9df2a07db47..04ba91a023e 100644 --- a/src/libmongoc/tests/test-mongoc-client.c +++ b/src/libmongoc/tests/test-mongoc-client.c @@ -3839,33 +3839,37 @@ test_killCursors(void) void test_client_install(TestSuite *suite) { - TestSuite_AddLive(suite, "/Client/ipv6/single", test_mongoc_client_ipv6_single); - TestSuite_AddLive(suite, "/Client/ipv6/pooled", test_mongoc_client_ipv6_pooled); + TestSuite_AddLive(suite, "/Client/ipv6/single [ipv6]", test_mongoc_client_ipv6_single); + TestSuite_AddLive(suite, "/Client/ipv6/pooled [ipv6]", test_mongoc_client_ipv6_pooled); - TestSuite_AddFull( - suite, "/Client/authenticate", test_mongoc_client_authenticate, NULL, NULL, test_framework_skip_if_no_auth); TestSuite_AddFull(suite, - "/Client/speculative_auth_failure/single", + "/Client/authenticate [lock:live-server]", + test_mongoc_client_authenticate, + NULL, + NULL, + test_framework_skip_if_no_auth); + TestSuite_AddFull(suite, + "/Client/speculative_auth_failure/single [lock:live-server]", test_mongoc_client_single_speculative_auth_failure, NULL, NULL, test_framework_skip_if_no_auth, test_framework_skip_if_no_failpoint); TestSuite_AddFull(suite, - "/Client/speculative_auth_failure/pooled", + "/Client/speculative_auth_failure/pooled [lock:live-server]", test_mongoc_client_pooled_speculative_auth_failure, NULL, NULL, test_framework_skip_if_no_auth, test_framework_skip_if_no_failpoint); TestSuite_AddFull(suite, - "/Client/authenticate_cached/pool", + "/Client/authenticate_cached/pool [lock:live-server]", test_mongoc_client_authenticate_cached_pooled, NULL, NULL, test_framework_skip_if_no_auth); TestSuite_AddFull(suite, - "/Client/authenticate_cached/client", + "/Client/authenticate_cached/client [lock:live-server]", test_mongoc_client_authenticate_cached_single, NULL, NULL, @@ -3875,23 +3879,31 @@ test_client_install(TestSuite *suite) // was introduced in server 4.4 (maxWireVersion=9) test_framework_skip_if_max_wire_version_more_than_8); TestSuite_AddFull(suite, - "/Client/authenticate_failure", + "/Client/authenticate_failure [lock:live-server]", test_mongoc_client_authenticate_failure, NULL, NULL, test_framework_skip_if_no_auth); TestSuite_AddFull(suite, - "/Client/authenticate_timeout", + "/Client/authenticate_timeout [lock:live-server]", test_mongoc_client_authenticate_timeout, NULL, NULL, test_framework_skip_if_no_auth); TestSuite_AddMockServerTest(suite, "/Client/command_w_server_id", test_client_cmd_w_server_id); TestSuite_AddMockServerTest(suite, "/Client/command_w_server_id/sharded", test_client_cmd_w_server_id_sharded); - TestSuite_AddFull( - suite, "/Client/command_w_server_id/option", test_server_id_option, NULL, NULL, test_framework_skip_if_auth); - TestSuite_AddFull( - suite, "/Client/command_w_write_concern", test_client_cmd_w_write_concern, NULL, NULL, TestSuite_CheckLive); + TestSuite_AddFull(suite, + "/Client/command_w_server_id/option [lock:live-server]", + test_server_id_option, + NULL, + NULL, + test_framework_skip_if_auth); + TestSuite_AddFull(suite, + "/Client/command_w_write_concern [lock:live-server]", + test_client_cmd_w_write_concern, + NULL, + NULL, + TestSuite_CheckLive); TestSuite_AddMockServerTest(suite, "/Client/command/write_concern", test_client_cmd_write_concern); TestSuite_AddMockServerTest(suite, "/Client/command/write_concern_fam", test_client_cmd_write_concern_fam); TestSuite_AddMockServerTest( @@ -3921,14 +3933,18 @@ test_client_install(TestSuite *suite) TestSuite_AddMockServerTest(suite, "/Client/mongos_seeds_reconnect/pooled", test_mongos_seeds_reconnect_pooled); TestSuite_AddFull(suite, "/Client/recovering", test_recovering, NULL, NULL, test_framework_skip_if_slow); TestSuite_AddMockServerTest(suite, "/Client/database_names", test_get_database_names); - TestSuite_AddFull( - suite, "/Client/connect/uds", test_mongoc_client_unix_domain_socket, NULL, NULL, test_framework_skip_if_no_uds); + TestSuite_AddFull(suite, + "/Client/connect/uds [lock:live-server]", + test_mongoc_client_unix_domain_socket, + NULL, + NULL, + test_framework_skip_if_no_uds); TestSuite_AddMockServerTest(suite, "/Client/mismatched_me", test_mongoc_client_mismatched_me); TestSuite_AddMockServerTest(suite, "/Client/handshake/pool", test_mongoc_handshake_pool); TestSuite_Add(suite, "/Client/application_handshake", test_mongoc_client_application_handshake); TestSuite_AddFull(suite, - "/Client/sends_handshake_single", + "/Client/sends_handshake_single [lock:live-server][timeout:30]", test_client_sends_handshake_single, NULL, NULL, @@ -3968,8 +3984,12 @@ test_client_install(TestSuite *suite) TestSuite_AddLive(suite, "/Client/get_description/single", test_mongoc_client_get_description_single); TestSuite_AddLive(suite, "/Client/get_description/pooled", test_mongoc_client_get_description_pooled); TestSuite_AddLive(suite, "/Client/descriptions/single", test_mongoc_client_descriptions_single); - TestSuite_AddFull( - suite, "/Client/descriptions/pooled", test_mongoc_client_descriptions_pooled, NULL, NULL, TestSuite_CheckLive); + TestSuite_AddFull(suite, + "/Client/descriptions/pooled [lock:live-server]", + test_mongoc_client_descriptions_pooled, + NULL, + NULL, + TestSuite_CheckLive); TestSuite_AddLive(suite, "/Client/select_server/single", test_mongoc_client_select_server_single); TestSuite_AddLive(suite, "/Client/select_server/pooled", test_mongoc_client_select_server_pooled); TestSuite_AddLive(suite, "/Client/select_server/err/single", test_mongoc_client_select_server_error_single); @@ -3981,13 +4001,13 @@ test_client_install(TestSuite *suite) suite, "/Client/fetch_stream/retry/succeed", test_mongoc_client_fetch_stream_retry_succeed); TestSuite_AddMockServerTest(suite, "/Client/fetch_stream/retry/fail", test_mongoc_client_fetch_stream_retry_fail); TestSuite_AddFull(suite, - "/Client/null_error_pointer/single", + "/Client/null_error_pointer/single [timeout:30]", test_null_error_pointer_single, NULL, NULL, test_framework_skip_if_slow); TestSuite_AddFull(suite, - "/Client/null_error_pointer/pooled", + "/Client/null_error_pointer/pooled [timeout:30]", test_null_error_pointer_pooled, NULL, NULL, @@ -3995,10 +4015,12 @@ test_client_install(TestSuite *suite) TestSuite_Add(suite, "/Client/get_database", test_get_database); TestSuite_Add(suite, "/Client/invalid_server_id", test_invalid_server_id); TestSuite_AddMockServerTest(suite, "/Client/recv_network_error", test_mongoc_client_recv_network_error); - TestSuite_AddLive( - suite, "/Client/get_handshake_hello_response/single", test_mongoc_client_get_handshake_hello_response_single); - TestSuite_AddLive( - suite, "/Client/get_handshake_hello_response/pooled", test_mongoc_client_get_handshake_hello_response_pooled); + TestSuite_AddLive(suite, + "/Client/get_handshake_hello_response/single [timeout:30]", + test_mongoc_client_get_handshake_hello_response_single); + TestSuite_AddLive(suite, + "/Client/get_handshake_hello_response/pooled [timeout:30]", + test_mongoc_client_get_handshake_hello_response_pooled); TestSuite_AddLive(suite, "/Client/get_handshake_establishes_connection/single", test_mongoc_client_get_handshake_establishes_connection_single); @@ -4008,17 +4030,21 @@ test_client_install(TestSuite *suite) TestSuite_AddMockServerTest( suite, "/Client/resends_handshake_on_network_error", test_mongoc_client_resends_handshake_on_network_error); TestSuite_Add(suite, "/Client/failure_to_auth", test_failure_to_auth); - TestSuite_AddFull( - suite, "/Client/failure_to_auth_logs", test_failure_to_auth_logs, NULL, NULL, test_framework_skip_if_no_auth); + TestSuite_AddFull(suite, + "/Client/failure_to_auth_logs [lock:live-server]", + test_failure_to_auth_logs, + NULL, + NULL, + test_framework_skip_if_no_auth); #if defined(MONGOC_ENABLE_SSL_OPENSSL) TestSuite_AddFull(suite, - "/Client/openssl/change_ssl_opts_before_ops", + "/Client/openssl/change_ssl_opts_before_ops [lock:live-server]", test_mongoc_client_change_openssl_ctx_before_ops, NULL, NULL, test_framework_skip_if_no_server_ssl); TestSuite_AddFull(suite, - "/Client/openssl/change_ssl_opts_after_ops", + "/Client/openssl/change_ssl_opts_after_ops [lock:live-server]", test_mongoc_client_change_openssl_ctx_between_ops, NULL, NULL, diff --git a/src/libmongoc/tests/test-mongoc-cluster.c b/src/libmongoc/tests/test-mongoc-cluster.c index 8ff862b0e90..ab7e54361fa 100644 --- a/src/libmongoc/tests/test-mongoc-cluster.c +++ b/src/libmongoc/tests/test-mongoc-cluster.c @@ -1547,13 +1547,13 @@ test_cluster_install(TestSuite *suite) TestSuite_AddLive(suite, "/Cluster/test_get_max_bson_obj_size", test_get_max_bson_obj_size); TestSuite_AddLive(suite, "/Cluster/test_get_max_msg_size", test_get_max_msg_size); TestSuite_AddFull(suite, - "/Cluster/disconnect/single", + "/Cluster/disconnect/single [timeout:30]", test_cluster_node_disconnect_single, NULL, NULL, test_framework_skip_if_slow); TestSuite_AddFull(suite, - "/Cluster/disconnect/pooled", + "/Cluster/disconnect/pooled [timeout:30]", test_cluster_node_disconnect_pooled, NULL, NULL, @@ -1561,30 +1561,31 @@ test_cluster_install(TestSuite *suite) TestSuite_AddMockServerTest(suite, "/Cluster/command/timeout/single", test_cluster_command_timeout_single); TestSuite_AddMockServerTest(suite, "/Cluster/command/timeout/pooled", test_cluster_command_timeout_pooled); TestSuite_AddFull(suite, - "/Cluster/write_command/disconnect", + "/Cluster/write_command/disconnect [timeout:30]", test_write_command_disconnect, NULL, NULL, test_framework_skip_if_slow); TestSuite_AddLive(suite, "/Cluster/cluster_time/command_simple/single", test_cluster_time_command_simple_single); - TestSuite_AddLive(suite, "/Cluster/cluster_time/command_simple/pooled", test_cluster_time_command_simple_pooled); + TestSuite_AddLive( + suite, "/Cluster/cluster_time/command_simple/pooled [timeout:30]", test_cluster_time_command_simple_pooled); TestSuite_AddLive( suite, "/Cluster/cluster_time/command_with_opts/single", test_cluster_time_command_with_opts_single); TestSuite_AddLive( - suite, "/Cluster/cluster_time/command_with_opts/pooled", test_cluster_time_command_with_opts_pooled); + suite, "/Cluster/cluster_time/command_with_opts/pooled [timeout:30]", test_cluster_time_command_with_opts_pooled); TestSuite_AddLive(suite, "/Cluster/cluster_time/aggregate/single", test_cluster_time_aggregate_single); - TestSuite_AddLive(suite, "/Cluster/cluster_time/aggregate/pooled", test_cluster_time_aggregate_pooled); + TestSuite_AddLive(suite, "/Cluster/cluster_time/aggregate/pooled [timeout:30]", test_cluster_time_aggregate_pooled); TestSuite_AddLive(suite, "/Cluster/cluster_time/cursor/single", test_cluster_time_cursor_single); - TestSuite_AddLive(suite, "/Cluster/cluster_time/cursor/pooled", test_cluster_time_cursor_pooled); + TestSuite_AddLive(suite, "/Cluster/cluster_time/cursor/pooled [timeout:30]", test_cluster_time_cursor_pooled); TestSuite_AddLive(suite, "/Cluster/cluster_time/insert/single", test_cluster_time_insert_single); - TestSuite_AddLive(suite, "/Cluster/cluster_time/insert/pooled", test_cluster_time_insert_pooled); + TestSuite_AddLive(suite, "/Cluster/cluster_time/insert/pooled [timeout:30]", test_cluster_time_insert_pooled); TestSuite_AddLive(suite, "/Cluster/command/timeout/negative", test_cluster_command_timeout_negative); TestSuite_AddMockServerTest(suite, - "/Cluster/cluster_time/comparison/single", + "/Cluster/cluster_time/comparison/single [timeout:30]", test_cluster_time_comparison_single, test_framework_skip_if_slow); TestSuite_AddMockServerTest(suite, - "/Cluster/cluster_time/comparison/pooled", + "/Cluster/cluster_time/comparison/pooled [timeout:30]", test_cluster_time_comparison_pooled, test_framework_skip_if_slow); TestSuite_AddMockServerTest(suite, @@ -1592,13 +1593,17 @@ test_cluster_install(TestSuite *suite) test_advanced_cluster_time_not_sent_to_standalone, test_framework_skip_if_no_crypto); TestSuite_AddMockServerTest( - suite, "/Cluster/not_primary/single", test_not_primary_single, test_framework_skip_if_slow); - TestSuite_AddMockServerTest( - suite, "/Cluster/not_primary/pooled", test_not_primary_pooled, test_framework_skip_if_slow); + suite, "/Cluster/not_primary/single [timeout:30]", test_not_primary_single, test_framework_skip_if_slow); TestSuite_AddMockServerTest( - suite, "/Cluster/not_primary_auth/single", test_not_primary_auth_single, test_framework_skip_if_slow); - TestSuite_AddMockServerTest( - suite, "/Cluster/not_primary_auth/pooled", test_not_primary_auth_pooled, test_framework_skip_if_slow); + suite, "/Cluster/not_primary/pooled [timeout:30]", test_not_primary_pooled, test_framework_skip_if_slow); + TestSuite_AddMockServerTest(suite, + "/Cluster/not_primary_auth/single [timeout:30]", + test_not_primary_auth_single, + test_framework_skip_if_slow); + TestSuite_AddMockServerTest(suite, + "/Cluster/not_primary_auth/pooled [timeout:30]", + test_not_primary_auth_pooled, + test_framework_skip_if_slow); TestSuite_AddMockServerTest(suite, "/Cluster/hello_fails", test_cluster_hello_fails); TestSuite_AddMockServerTest(suite, "/Cluster/hello_hangup", test_cluster_hello_hangup); TestSuite_AddMockServerTest(suite, "/Cluster/command_error/op_msg", test_cluster_command_error); diff --git a/src/libmongoc/tests/test-mongoc-cmd.c b/src/libmongoc/tests/test-mongoc-cmd.c index 441cf5c8d0b..dc6c15a5b7c 100644 --- a/src/libmongoc/tests/test-mongoc-cmd.c +++ b/src/libmongoc/tests/test-mongoc-cmd.c @@ -145,7 +145,7 @@ test_client_cmd_install(TestSuite *suite) { TestSuite_AddMockServerTest(suite, "/Client/cmd/options", test_client_cmd_options); TestSuite_AddFull(suite, - "/cmd/with_two_payload1", + "/cmd/with_two_payload1 [lock:live-server]", test_cmd_with_two_payload1, NULL /* dtor */, NULL /* ctx */, diff --git a/src/libmongoc/tests/test-mongoc-collection.c b/src/libmongoc/tests/test-mongoc-collection.c index fa2828d46a6..8c3ae16f9e6 100644 --- a/src/libmongoc/tests/test-mongoc-collection.c +++ b/src/libmongoc/tests/test-mongoc-collection.c @@ -4959,16 +4959,28 @@ test_collection_install(TestSuite *suite) { test_aggregate_install(suite); - TestSuite_AddFull( - suite, "/Collection/aggregate/write_concern", test_aggregate_w_write_concern, NULL, NULL, TestSuite_CheckLive); - TestSuite_AddFull( - suite, "/Collection/read_prefs_is_valid", test_read_prefs_is_valid, NULL, NULL, test_framework_skip_if_mongos); + TestSuite_AddFull(suite, + "/Collection/aggregate/write_concern [lock:live-server]", + test_aggregate_w_write_concern, + NULL, + NULL, + TestSuite_CheckLive); + TestSuite_AddFull(suite, + "/Collection/read_prefs_is_valid [lock:live-server]", + test_read_prefs_is_valid, + NULL, + NULL, + test_framework_skip_if_mongos); TestSuite_AddLive(suite, "/Collection/insert_many", test_insert_many); TestSuite_AddLive(suite, "/Collection/copy", test_copy); TestSuite_AddLive(suite, "/Collection/insert", test_insert); TestSuite_AddLive(suite, "/Collection/insert/null_string", test_insert_null); - TestSuite_AddFull( - suite, "/Collection/insert/oversize", test_insert_oversize, NULL, NULL, test_framework_skip_if_slow_or_live); + TestSuite_AddFull(suite, + "/Collection/insert/oversize [lock:live-server][timeout:30]", + test_insert_oversize, + NULL, + NULL, + test_framework_skip_if_slow_or_live); TestSuite_AddMockServerTest(suite, "/Collection/insert/keys", test_insert_command_keys); TestSuite_AddLive(suite, "/Collection/insert/w0", test_insert_w0); TestSuite_AddLive(suite, "/Collection/update/w0", test_update_w0); @@ -4978,31 +4990,53 @@ test_collection_install(TestSuite *suite) TestSuite_AddLive(suite, "/Collection/index_w_write_concern", test_index_w_write_concern); TestSuite_AddMockServerTest(suite, "/Collection/index/collation", test_index_with_collation); TestSuite_AddLive(suite, "/Collection/index_compound", test_index_compound); - TestSuite_AddFull( - suite, "/Collection/index_geo", test_index_geo, NULL, NULL, test_framework_skip_if_max_wire_version_more_than_9); + TestSuite_AddFull(suite, + "/Collection/index_geo [lock:live-server]", + test_index_geo, + NULL, + NULL, + test_framework_skip_if_max_wire_version_more_than_9); TestSuite_AddLive(suite, "/Collection/index_storage", test_index_storage); TestSuite_AddLive(suite, "/Collection/regex", test_regex); - TestSuite_AddFull(suite, "/Collection/decimal128", test_decimal128, NULL, NULL, skip_unless_server_has_decimal128); + TestSuite_AddFull(suite, + "/Collection/decimal128 [lock:live-server]", + test_decimal128, + NULL, + NULL, + skip_unless_server_has_decimal128); TestSuite_AddLive(suite, "/Collection/update", test_update); - TestSuite_AddFull(suite, "/Collection/update_pipeline", test_update_pipeline, NULL, NULL, TestSuite_CheckLive); + TestSuite_AddFull( + suite, "/Collection/update_pipeline [lock:live-server]", test_update_pipeline, NULL, NULL, TestSuite_CheckLive); TestSuite_AddLive(suite, "/Collection/update/multi", test_update_multi); TestSuite_AddLive(suite, "/Collection/update/upsert", test_update_upsert); - TestSuite_AddFull( - suite, "/Collection/update/oversize", test_update_oversize, NULL, NULL, test_framework_skip_if_slow_or_live); + TestSuite_AddFull(suite, + "/Collection/update/oversize [lock:live-server][timeout:30]", + test_update_oversize, + NULL, + NULL, + test_framework_skip_if_slow_or_live); TestSuite_AddLive(suite, "/Collection/remove", test_remove); TestSuite_AddLive(suite, "/Collection/remove/multi", test_remove_multi); - TestSuite_AddFull( - suite, "/Collection/remove/oversize", test_remove_oversize, NULL, NULL, test_framework_skip_if_slow_or_live); + TestSuite_AddFull(suite, + "/Collection/remove/oversize [lock:live-server][timeout:30]", + test_remove_oversize, + NULL, + NULL, + test_framework_skip_if_slow_or_live); TestSuite_AddLive(suite, "/Collection/drop", test_drop); TestSuite_AddLive(suite, "/Collection/aggregate", test_aggregate); TestSuite_AddMockServerTest(suite, "/Collection/aggregate/inherit/collection", test_aggregate_inherit_collection); TestSuite_AddLive(suite, "/Collection/aggregate/large", test_aggregate_large); - TestSuite_AddFull( - suite, "/Collection/aggregate/secondary", test_aggregate_secondary, NULL, NULL, test_framework_skip_if_mongos); + TestSuite_AddFull(suite, + "/Collection/aggregate/secondary [lock:live-server]", + test_aggregate_secondary, + NULL, + NULL, + test_framework_skip_if_mongos); TestSuite_AddMockServerTest(suite, "/Collection/aggregate/secondary/sharded", test_aggregate_secondary_sharded); TestSuite_AddMockServerTest(suite, "/Collection/aggregate/read_concern", test_aggregate_read_concern); TestSuite_AddFull(suite, - "/Collection/aggregate/bypass_document_validation", + "/Collection/aggregate/bypass_document_validation [lock:live-server]", test_aggregate_bypass, NULL, NULL, @@ -5011,19 +5045,27 @@ test_collection_install(TestSuite *suite) TestSuite_AddMockServerTest(suite, "/Collection/aggregate_w_server_id", test_aggregate_w_server_id); TestSuite_AddMockServerTest(suite, "/Collection/aggregate_w_server_id/sharded", test_aggregate_w_server_id_sharded); TestSuite_AddFull(suite, - "/Collection/aggregate_w_server_id/option", + "/Collection/aggregate_w_server_id/option [lock:live-server]", test_aggregate_server_id_option, NULL, NULL, test_framework_skip_if_auth); TestSuite_AddLive(suite, "/Collection/rename", test_rename); TestSuite_AddMockServerTest(suite, "/Collection/find_read_concern", test_find_read_concern); - TestSuite_AddFull( - suite, "/Collection/getmore_read_concern_live", test_getmore_read_concern_live, NULL, NULL, TestSuite_CheckLive); + TestSuite_AddFull(suite, + "/Collection/getmore_read_concern_live [lock:live-server]", + test_getmore_read_concern_live, + NULL, + NULL, + TestSuite_CheckLive); TestSuite_AddLive(suite, "/Collection/find_and_modify", test_find_and_modify); TestSuite_AddMockServerTest(suite, "/Collection/find_and_modify/write_concern", test_find_and_modify_write_concern); - TestSuite_AddFull( - suite, "/Collection/large_return", test_large_return, NULL, NULL, test_framework_skip_if_slow_or_live); + TestSuite_AddFull(suite, + "/Collection/large_return [lock:live-server]", + test_large_return, + NULL, + NULL, + test_framework_skip_if_slow_or_live); TestSuite_AddLive(suite, "/Collection/many_return", test_many_return); TestSuite_AddLive(suite, "/Collection/insert_one_validate", test_insert_one_validate); TestSuite_AddLive(suite, "/Collection/insert_many_validate", test_insert_many_validate); @@ -5032,8 +5074,12 @@ test_collection_install(TestSuite *suite) TestSuite_AddLive(suite, "/Collection/get_index_info", test_get_index_info); TestSuite_AddMockServerTest(suite, "/Collection/find_indexes/error", test_find_indexes_err); TestSuite_AddLive(suite, "/Collection/insert/duplicate_key", test_insert_duplicate_key); - TestSuite_AddFull( - suite, "/Collection/create_index/fail", test_create_index_fail, NULL, NULL, test_framework_skip_if_offline); + TestSuite_AddFull(suite, + "/Collection/create_index/fail [lock:live-server]", + test_create_index_fail, + NULL, + NULL, + test_framework_skip_if_offline); TestSuite_AddLive(suite, "/Collection/insert_one", test_insert_one); TestSuite_AddLive(suite, "/Collection/update_and_replace", test_update_and_replace); TestSuite_AddLive(suite, "/Collection/array_filters_validate", test_array_filters_validate); @@ -5051,7 +5097,7 @@ test_collection_install(TestSuite *suite) TestSuite_AddLive(suite, "/Collection/estimated_document_count_live", test_estimated_document_count_live); TestSuite_AddMockServerTest(suite, "/Collection/aggregate_with_batch_size", test_aggregate_with_batch_size); TestSuite_AddFull(suite, - "/Collection/fam/no_error_on_retry", + "/Collection/fam/no_error_on_retry [lock:live-server]", test_fam_no_error_on_retry, NULL, NULL, @@ -5061,7 +5107,7 @@ test_collection_install(TestSuite *suite) TestSuite_AddLive(suite, "/Collection/hint_is_validated/countDocuments", test_hint_is_validated_countDocuments); TestSuite_AddLive(suite, "/Collection/create_indexes_with_opts", test_create_indexes_with_opts); TestSuite_AddFull(suite, - "/Collection/create_indexes_with_opts/commitQuorum/pre44", + "/Collection/create_indexes_with_opts/commitQuorum/pre44 [lock:live-server]", test_create_indexes_with_opts_commitQuorum_pre44, NULL /* _dtor */, NULL /* _ctx */, @@ -5070,7 +5116,7 @@ test_collection_install(TestSuite *suite) // Server Version 4.4 has Wire Version 9. test_framework_skip_if_max_wire_version_more_than_8); TestSuite_AddFull(suite, - "/Collection/create_indexes_with_opts/commitQuorum/post44", + "/Collection/create_indexes_with_opts/commitQuorum/post44 [lock:live-server]", test_create_indexes_with_opts_commitQuorum_post44, NULL /* _dtor */, NULL /* _ctx */, @@ -5079,7 +5125,7 @@ test_collection_install(TestSuite *suite) // Server Version 4.4 has Wire Version 9. test_framework_skip_if_max_wire_version_less_than_9); TestSuite_AddFull(suite, - "/Collection/create_indexes_with_opts/no_retry", + "/Collection/create_indexes_with_opts/no_retry [lock:live-server]", test_create_indexes_with_opts_no_retry, NULL /* _dtor */, NULL /* _ctx */, @@ -5088,7 +5134,7 @@ test_collection_install(TestSuite *suite) TestSuite_AddLive(suite, "/Collection/insert_one_reports_id", test_insert_one_reports_id); TestSuite_AddFull(suite, - "/Collection/test_create_indexes_acts_as_write_command", + "/Collection/test_create_indexes_acts_as_write_command [lock:live-server]", test_create_indexes_acts_as_write_command, NULL /* _dtor */, NULL /* _ctx */, diff --git a/src/libmongoc/tests/test-mongoc-counters.c b/src/libmongoc/tests/test-mongoc-counters.c index ee68861ad69..5ff35d15132 100644 --- a/src/libmongoc/tests/test-mongoc-counters.c +++ b/src/libmongoc/tests/test-mongoc-counters.c @@ -1455,14 +1455,14 @@ test_counters_install(TestSuite *suite) BSON_UNUSED(suite); #ifdef MONGOC_ENABLE_SHM_COUNTERS TestSuite_AddFull(suite, - "/counters/op_msg", + "/counters/op_msg [lock:live-server]", test_counters_op_msg, NULL, NULL, test_framework_skip_if_compressors, TestSuite_CheckLive); TestSuite_AddFull(suite, - "/counters/op_compressed", + "/counters/op_compressed [lock:live-server]", test_counters_op_compressed, NULL, NULL, @@ -1470,9 +1470,10 @@ test_counters_install(TestSuite *suite) TestSuite_CheckLive); TestSuite_AddLive(suite, "/counters/cursors", test_counters_cursors); TestSuite_AddLive(suite, "/counters/clients", test_counters_clients); - TestSuite_AddFull(suite, "/counters/streams", test_counters_streams, NULL, NULL, TestSuite_CheckLive); + TestSuite_AddFull( + suite, "/counters/streams [lock:live-server]", test_counters_streams, NULL, NULL, TestSuite_CheckLive); TestSuite_AddFull(suite, - "/counters/auth", + "/counters/auth [lock:live-server]", test_counters_auth, NULL, NULL, @@ -1510,14 +1511,14 @@ test_counters_install(TestSuite *suite) #if defined(MONGOC_ENABLE_SSL) TestSuite_AddFull(suite, - "/counters/rpc/op_egress/auth/single/op_query", + "/counters/rpc/op_egress/auth/single/op_query [lock:live-server]", test_counters_auth_single_op_query, NULL, NULL, test_framework_skip_if_no_auth, test_framework_skip_if_not_replset); TestSuite_AddFull(suite, - "/counters/rpc/op_egress/auth/single/op_msg", + "/counters/rpc/op_egress/auth/single/op_msg [lock:live-server]", test_counters_auth_single_op_msg, NULL, NULL, @@ -1525,14 +1526,14 @@ test_counters_install(TestSuite *suite) test_framework_skip_if_max_wire_version_less_than_13, test_framework_skip_if_not_replset); TestSuite_AddFull(suite, - "/counters/rpc/op_egress/auth/pooled/op_query", + "/counters/rpc/op_egress/auth/pooled/op_query [lock:live-server]", test_counters_auth_pooled_op_query, NULL, NULL, test_framework_skip_if_no_auth, test_framework_skip_if_not_replset); TestSuite_AddFull(suite, - "/counters/rpc/op_egress/auth/pooled/op_msg", + "/counters/rpc/op_egress/auth/pooled/op_msg [lock:live-server]", test_counters_auth_pooled_op_msg, NULL, NULL, diff --git a/src/libmongoc/tests/test-mongoc-crud.c b/src/libmongoc/tests/test-mongoc-crud.c index 0671a4930e4..f244ecfa1a5 100644 --- a/src/libmongoc/tests/test-mongoc-crud.c +++ b/src/libmongoc/tests/test-mongoc-crud.c @@ -1377,42 +1377,42 @@ test_crud_install(TestSuite *suite) test_all_spec_tests(suite); TestSuite_AddFull(suite, - "/crud/prose_test_1", + "/crud/prose_test_1 [lock:live-server]", prose_test_1, NULL, /* dtor */ NULL, /* ctx */ test_framework_skip_if_no_failpoint); TestSuite_AddFull(suite, - "/crud/prose_test_2", + "/crud/prose_test_2 [lock:live-server]", prose_test_2, NULL, /* dtor */ NULL, /* ctx */ test_framework_skip_if_max_wire_version_less_than_13); TestSuite_AddFull(suite, - "/crud/prose_test_3", + "/crud/prose_test_3 [lock:live-server]", prose_test_3, NULL, /* dtor */ NULL, /* ctx */ test_framework_skip_if_max_wire_version_less_than_25 /* require 8.0+ server */); TestSuite_AddFull(suite, - "/crud/prose_test_4", + "/crud/prose_test_4 [lock:live-server]", prose_test_4, NULL, /* dtor */ NULL, /* ctx */ test_framework_skip_if_max_wire_version_less_than_25 /* require 8.0+ server */); TestSuite_AddFull(suite, - "/crud/prose_test_5", + "/crud/prose_test_5 [lock:live-server]", prose_test_5, NULL, /* dtor */ NULL, /* ctx */ test_framework_skip_if_max_wire_version_less_than_25 /* require 8.0+ server */); TestSuite_AddFull(suite, - "/crud/prose_test_6", + "/crud/prose_test_6 [lock:live-server][timeout:30]", prose_test_6, NULL, /* dtor */ NULL, /* ctx */ @@ -1421,7 +1421,7 @@ test_crud_install(TestSuite *suite) TestSuite_AddFull(suite, - "/crud/prose_test_7", + "/crud/prose_test_7 [lock:live-server]", prose_test_7, NULL, /* dtor */ NULL, /* ctx */ @@ -1430,7 +1430,7 @@ test_crud_install(TestSuite *suite) TestSuite_AddFull(suite, - "/crud/prose_test_8", + "/crud/prose_test_8 [lock:live-server]", prose_test_8, NULL, /* dtor */ NULL, /* ctx */ @@ -1438,7 +1438,7 @@ test_crud_install(TestSuite *suite) test_framework_skip_if_no_txns); TestSuite_AddFull(suite, - "/crud/prose_test_9", + "/crud/prose_test_9 [lock:live-server]", prose_test_9, NULL, /* dtor */ NULL, /* ctx */ @@ -1446,14 +1446,14 @@ test_crud_install(TestSuite *suite) ); TestSuite_AddFull(suite, - "/crud/prose_test_10", + "/crud/prose_test_10 [lock:live-server]", prose_test_10, NULL, /* dtor */ NULL, /* ctx */ test_framework_skip_if_max_wire_version_less_than_25 /* require 8.0+ server */); TestSuite_AddFull(suite, - "/crud/prose_test_11", + "/crud/prose_test_11 [lock:live-server]", prose_test_11, NULL /* dtor */, NULL /* ctx */, @@ -1461,7 +1461,7 @@ test_crud_install(TestSuite *suite) ); TestSuite_AddFull(suite, - "/crud/prose_test_12", + "/crud/prose_test_12 [lock:live-server]", prose_test_12, NULL /* dtor */, NULL /* ctx */, @@ -1469,7 +1469,7 @@ test_crud_install(TestSuite *suite) ); TestSuite_AddFull(suite, - "/crud/prose_test_15", + "/crud/prose_test_15 [lock:live-server]", prose_test_15, NULL /* dtor */, NULL /* ctx */, diff --git a/src/libmongoc/tests/test-mongoc-cursor.c b/src/libmongoc/tests/test-mongoc-cursor.c index 076470e4a04..2f80bce1510 100644 --- a/src/libmongoc/tests/test-mongoc-cursor.c +++ b/src/libmongoc/tests/test-mongoc-cursor.c @@ -415,37 +415,37 @@ _make_array_cursor(mongoc_collection_t *coll) return mongoc_client_find_databases_with_opts(coll->client, NULL); } -#define TEST_CURSOR_FIND(prefix, fn) \ - if (1) { \ - make_cursor_helper_t *const helper = bson_malloc(sizeof(*helper)); \ - *helper = (make_cursor_helper_t){.ctor = _make_find_cursor}; \ - TestSuite_AddFull(suite, prefix "/find", fn, &bson_free, helper, TestSuite_CheckLive); \ - } else \ +#define TEST_CURSOR_FIND(prefix, fn) \ + if (1) { \ + make_cursor_helper_t *const helper = bson_malloc(sizeof(*helper)); \ + *helper = (make_cursor_helper_t){.ctor = _make_find_cursor}; \ + TestSuite_AddFull(suite, prefix "/find [lock:live-server]", fn, &bson_free, helper, TestSuite_CheckLive); \ + } else \ ((void)0) -#define TEST_CURSOR_CMD(prefix, fn) \ - if (1) { \ - make_cursor_helper_t *const helper = bson_malloc(sizeof(*helper)); \ - *helper = (make_cursor_helper_t){.ctor = _make_cmd_cursor}; \ - TestSuite_AddFull(suite, prefix "/cmd", fn, &bson_free, helper, TestSuite_CheckLive); \ - } else \ +#define TEST_CURSOR_CMD(prefix, fn) \ + if (1) { \ + make_cursor_helper_t *const helper = bson_malloc(sizeof(*helper)); \ + *helper = (make_cursor_helper_t){.ctor = _make_cmd_cursor}; \ + TestSuite_AddFull(suite, prefix "/cmd [lock:live-server]", fn, &bson_free, helper, TestSuite_CheckLive); \ + } else \ ((void)0) -#define TEST_CURSOR_ARRAY(prefix, fn) \ - if (1) { \ - make_cursor_helper_t *const helper = bson_malloc(sizeof(*helper)); \ - *helper = (make_cursor_helper_t){.ctor = _make_array_cursor}; \ - TestSuite_AddFull(suite, prefix "/array", fn, &bson_free, helper, TestSuite_CheckLive); \ - } else \ +#define TEST_CURSOR_ARRAY(prefix, fn) \ + if (1) { \ + make_cursor_helper_t *const helper = bson_malloc(sizeof(*helper)); \ + *helper = (make_cursor_helper_t){.ctor = _make_array_cursor}; \ + TestSuite_AddFull(suite, prefix "/array [lock:live-server]", fn, &bson_free, helper, TestSuite_CheckLive); \ + } else \ ((void)0) -#define TEST_CURSOR_AGG(prefix, fn) \ - if (1) { \ - make_cursor_helper_t *const helper = bson_malloc(sizeof(*helper)); \ - *helper = (make_cursor_helper_t){.ctor = _make_cmd_cursor_from_agg}; \ - TestSuite_AddFull(suite, prefix "/agg", fn, &bson_free, helper, TestSuite_CheckLive); \ - } else \ +#define TEST_CURSOR_AGG(prefix, fn) \ + if (1) { \ + make_cursor_helper_t *const helper = bson_malloc(sizeof(*helper)); \ + *helper = (make_cursor_helper_t){.ctor = _make_cmd_cursor_from_agg}; \ + TestSuite_AddFull(suite, prefix "/agg [lock:live-server]", fn, &bson_free, helper, TestSuite_CheckLive); \ + } else \ ((void)0) @@ -2394,9 +2394,14 @@ test_cursor_install(TestSuite *suite) TestSuite_AddLive(suite, "/Cursor/empty_collection", test_cursor_empty_collection); TestSuite_AddLive(suite, "/Cursor/new_from_agg", test_cursor_new_from_aggregate); TestSuite_AddLive(suite, "/Cursor/new_from_agg_no_initial", test_cursor_new_from_aggregate_no_initial); - TestSuite_AddFull(suite, "/Cursor/new_from_find", test_cursor_new_from_find, NULL, NULL, TestSuite_CheckLive); TestSuite_AddFull( - suite, "/Cursor/new_from_find_batches", test_cursor_new_from_find_batches, NULL, NULL, TestSuite_CheckLive); + suite, "/Cursor/new_from_find [lock:live-server]", test_cursor_new_from_find, NULL, NULL, TestSuite_CheckLive); + TestSuite_AddFull(suite, + "/Cursor/new_from_find_batches [lock:live-server]", + test_cursor_new_from_find_batches, + NULL, + NULL, + TestSuite_CheckLive); TestSuite_AddLive(suite, "/Cursor/new_invalid", test_cursor_new_invalid); TestSuite_AddMockServerTest(suite, "/Cursor/new_tailable_await", test_cursor_new_tailable_await); TestSuite_AddMockServerTest(suite, "/Cursor/int64_t_maxtimems", test_cursor_int64_t_maxtimems); diff --git a/src/libmongoc/tests/test-mongoc-cyrus.c b/src/libmongoc/tests/test-mongoc-cyrus.c index f3797ad68bc..8451a3d80c5 100644 --- a/src/libmongoc/tests/test-mongoc-cyrus.c +++ b/src/libmongoc/tests/test-mongoc-cyrus.c @@ -92,7 +92,7 @@ test_cyrus_install(TestSuite *suite) { TestSuite_Add(suite, "/SASL/properties", test_sasl_properties); TestSuite_AddFull(suite, - "/SASL/canonicalize", + "/SASL/canonicalize [lock:live-server]", test_sasl_canonicalize_hostname, NULL, NULL, diff --git a/src/libmongoc/tests/test-mongoc-database.c b/src/libmongoc/tests/test-mongoc-database.c index 94f1760c6e9..a3031b264e9 100644 --- a/src/libmongoc/tests/test-mongoc-database.c +++ b/src/libmongoc/tests/test-mongoc-database.c @@ -917,8 +917,12 @@ void test_database_install(TestSuite *suite) { TestSuite_AddMockServerTest(suite, "/Database/aggregate/inherit/database", test_aggregate_inherit_database); - TestSuite_AddFull( - suite, "/Database/create_with_write_concern", test_create_with_write_concern, NULL, NULL, TestSuite_CheckLive); + TestSuite_AddFull(suite, + "/Database/create_with_write_concern [lock:live-server]", + test_create_with_write_concern, + NULL, + NULL, + TestSuite_CheckLive); TestSuite_AddLive(suite, "/Database/copy", test_copy); TestSuite_AddLive(suite, "/Database/has_collection", test_has_collection); TestSuite_AddMockServerTest( diff --git a/src/libmongoc/tests/test-mongoc-dns.c b/src/libmongoc/tests/test-mongoc-dns.c index 4668d41d8cb..22678a83ae5 100644 --- a/src/libmongoc/tests/test-mongoc-dns.c +++ b/src/libmongoc/tests/test-mongoc-dns.c @@ -1377,7 +1377,7 @@ test_dns_install(TestSuite *suite) TestSuite_AddFull( suite, "/initial_dns_seedlist_discovery/srv_polling/mocked", test_srv_polling_mocked, NULL, NULL, NULL); TestSuite_AddFull(suite, - "/initial_dns_seedlist_discovery/small_initial_buffer", + "/initial_dns_seedlist_discovery/small_initial_buffer [lock:live-server]", test_small_initial_buffer, NULL, NULL, @@ -1388,56 +1388,56 @@ test_dns_install(TestSuite *suite) * Records for mongos Discovery" spec, not the "Initial DNS Seedlist * Discovery" spec. */ TestSuite_AddFull(suite, - "/initial_dns_seedlist_discovery/srv_polling/prose_test_9/single", + "/initial_dns_seedlist_discovery/srv_polling/prose_test_9/single [lock:live-server]", prose_test_9_single, NULL, NULL, test_dns_check_srv_polling); TestSuite_AddFull(suite, - "/initial_dns_seedlist_discovery/srv_polling/prose_test_9/pooled", + "/initial_dns_seedlist_discovery/srv_polling/prose_test_9/pooled [lock:live-server]", prose_test_9_pooled, NULL, NULL, test_dns_check_srv_polling); TestSuite_AddFull(suite, - "/initial_dns_seedlist_discovery/srv_polling/prose_test_10/single", + "/initial_dns_seedlist_discovery/srv_polling/prose_test_10/single [lock:live-server]", prose_test_10_single, NULL, NULL, test_dns_check_srv_polling); TestSuite_AddFull(suite, - "/initial_dns_seedlist_discovery/srv_polling/prose_test_10/pooled", + "/initial_dns_seedlist_discovery/srv_polling/prose_test_10/pooled [lock:live-server]", prose_test_10_pooled, NULL, NULL, test_dns_check_srv_polling); TestSuite_AddFull(suite, - "/initial_dns_seedlist_discovery/srv_polling/prose_test_11/single", + "/initial_dns_seedlist_discovery/srv_polling/prose_test_11/single [lock:live-server]", prose_test_11_single, NULL, NULL, test_dns_check_srv_polling); TestSuite_AddFull(suite, - "/initial_dns_seedlist_discovery/srv_polling/prose_test_11/pooled", + "/initial_dns_seedlist_discovery/srv_polling/prose_test_11/pooled [lock:live-server]", prose_test_11_pooled, NULL, NULL, test_dns_check_srv_polling); TestSuite_AddFull(suite, - "/initial_dns_seedlist_discovery/srv_polling/prose_test_12/single", + "/initial_dns_seedlist_discovery/srv_polling/prose_test_12/single [lock:live-server]", prose_test_12_single, NULL, NULL, test_dns_check_srv_polling); TestSuite_AddFull(suite, - "/initial_dns_seedlist_discovery/srv_polling/prose_test_12/pooled", + "/initial_dns_seedlist_discovery/srv_polling/prose_test_12/pooled [lock:live-server]", prose_test_12_pooled, NULL, NULL, @@ -1445,7 +1445,7 @@ test_dns_install(TestSuite *suite) TestSuite_AddFull( suite, - "/initial_dns_seedlist_discovery/srv_polling/removing_servers_closes_connections", + "/initial_dns_seedlist_discovery/srv_polling/removing_servers_closes_connections [lock:live-server]", test_removing_servers_closes_connections, NULL, NULL, diff --git a/src/libmongoc/tests/test-mongoc-exhaust.c b/src/libmongoc/tests/test-mongoc-exhaust.c index ffe6e5ee3c7..776e80a0c0f 100644 --- a/src/libmongoc/tests/test-mongoc-exhaust.c +++ b/src/libmongoc/tests/test-mongoc-exhaust.c @@ -768,17 +768,39 @@ test_exhaust_in_child(void) void test_exhaust_install(TestSuite *suite) { - TestSuite_AddFull(suite, "/Client/exhaust_cursor/works", test_exhaust_cursor_works, NULL, NULL, skip_if_no_exhaust); - TestSuite_AddFull( - suite, "/Client/exhaust_cursor/no_match", test_exhaust_cursor_no_match, NULL, NULL, skip_if_no_exhaust); - TestSuite_AddFull( - suite, "/Client/exhaust_cursor/single", test_exhaust_cursor_single, NULL, NULL, skip_if_no_exhaust); - TestSuite_AddFull(suite, "/Client/exhaust_cursor/pool", test_exhaust_cursor_pool, NULL, NULL, skip_if_no_exhaust); - TestSuite_AddFull( - suite, "/Client/exhaust_cursor/batches", test_exhaust_cursor_multi_batch, NULL, NULL, skip_if_no_exhaust); + TestSuite_AddFull(suite, + "/Client/exhaust_cursor/works [lock:live-server]", + test_exhaust_cursor_works, + NULL, + NULL, + skip_if_no_exhaust); + TestSuite_AddFull(suite, + "/Client/exhaust_cursor/no_match [lock:live-server]", + test_exhaust_cursor_no_match, + NULL, + NULL, + skip_if_no_exhaust); + TestSuite_AddFull(suite, + "/Client/exhaust_cursor/single [lock:live-server]", + test_exhaust_cursor_single, + NULL, + NULL, + skip_if_no_exhaust); + TestSuite_AddFull(suite, + "/Client/exhaust_cursor/pool [lock:live-server]", + test_exhaust_cursor_pool, + NULL, + NULL, + skip_if_no_exhaust); + TestSuite_AddFull(suite, + "/Client/exhaust_cursor/batches [lock:live-server]", + test_exhaust_cursor_multi_batch, + NULL, + NULL, + skip_if_no_exhaust); TestSuite_AddLive(suite, "/Client/set_max_await_time_ms", test_cursor_set_max_await_time_ms); TestSuite_AddFull(suite, - "/Client/exhaust_cursor/server_hint", + "/Client/exhaust_cursor/server_hint [lock:live-server]", test_cursor_server_hint_with_exhaust, NULL, NULL, diff --git a/src/libmongoc/tests/test-mongoc-find-and-modify.c b/src/libmongoc/tests/test-mongoc-find-and-modify.c index 115550c3243..80e8cc21c31 100644 --- a/src/libmongoc/tests/test-mongoc-find-and-modify.c +++ b/src/libmongoc/tests/test-mongoc-find-and-modify.c @@ -511,7 +511,7 @@ test_find_and_modify_install(TestSuite *suite) TestSuite_AddMockServerTest( suite, "/find_and_modify/find_and_modify/write_concern", test_find_and_modify_write_concern); TestSuite_AddFull(suite, - "/find_and_modify/find_and_modify/write_concern_failure", + "/find_and_modify/find_and_modify/write_concern_failure [lock:live-server]", test_find_and_modify_write_concern_failure, NULL, NULL, diff --git a/src/libmongoc/tests/test-mongoc-gridfs-bucket.c b/src/libmongoc/tests/test-mongoc-gridfs-bucket.c index c12f8d7b823..be4667e3ffc 100644 --- a/src/libmongoc/tests/test-mongoc-gridfs-bucket.c +++ b/src/libmongoc/tests/test-mongoc-gridfs-bucket.c @@ -1054,16 +1054,17 @@ test_gridfs_bucket_install(TestSuite *suite) test_all_spec_tests(suite); TestSuite_AddLive(suite, "/gridfs/create_bucket", test_create_bucket); TestSuite_AddLive(suite, "/gridfs/upload_and_download", test_upload_and_download); - TestSuite_AddFull(suite, "/gridfs/upload_error", test_upload_error, NULL, NULL, test_framework_skip_if_no_auth); + TestSuite_AddFull( + suite, "/gridfs/upload_error [lock:live-server]", test_upload_error, NULL, NULL, test_framework_skip_if_no_auth); TestSuite_AddFull(suite, - "/gridfs/find_w_session", + "/gridfs/find_w_session [lock:live-server]", test_find_w_session, NULL, NULL, test_framework_skip_if_no_sessions, test_framework_skip_if_no_crypto); TestSuite_AddFull(suite, - "/gridfs/find", + "/gridfs/find [lock:live-server]", test_find, NULL, NULL, diff --git a/src/libmongoc/tests/test-mongoc-gridfs.c b/src/libmongoc/tests/test-mongoc-gridfs.c index 9a68462ec55..033c0d8157d 100644 --- a/src/libmongoc/tests/test-mongoc-gridfs.c +++ b/src/libmongoc/tests/test-mongoc-gridfs.c @@ -1618,11 +1618,19 @@ test_gridfs_install(TestSuite *suite) TestSuite_AddLive(suite, "/gridfs_old/write", test_write); TestSuite_AddLive(suite, "/gridfs_old/write_at_boundary", test_write_at_boundary); TestSuite_AddLive(suite, "/gridfs_old/write_past_end", test_write_past_end); - TestSuite_AddFull( - suite, "/gridfs_old/test_long_seek", test_long_seek, NULL, NULL, test_framework_skip_if_slow_or_live); + TestSuite_AddFull(suite, + "/gridfs_old/test_long_seek [lock:live-server][timeout:30]", + test_long_seek, + NULL, + NULL, + test_framework_skip_if_slow_or_live); TestSuite_AddLive(suite, "/gridfs_old/remove_by_filename", test_remove_by_filename); - TestSuite_AddFull( - suite, "/gridfs_old/missing_chunk", test_missing_chunk, NULL, NULL, test_framework_skip_if_slow_or_live); + TestSuite_AddFull(suite, + "/gridfs_old/missing_chunk [lock:live-server][timeout:30]", + test_missing_chunk, + NULL, + NULL, + test_framework_skip_if_slow_or_live); TestSuite_AddLive(suite, "/gridfs_old/oversize_chunk", test_oversize); TestSuite_AddLive(suite, "/gridfs_old/missing_file", test_missing_file); TestSuite_AddLive(suite, "/gridfs_old/file_set_id", test_set_id); diff --git a/src/libmongoc/tests/test-mongoc-loadbalanced.c b/src/libmongoc/tests/test-mongoc-loadbalanced.c index f793bfc2477..5ebd33aa1d6 100644 --- a/src/libmongoc/tests/test-mongoc-loadbalanced.c +++ b/src/libmongoc/tests/test-mongoc-loadbalanced.c @@ -832,47 +832,47 @@ void test_loadbalanced_install(TestSuite *suite) { TestSuite_AddFull(suite, - "/loadbalanced/sessions/supported", + "/loadbalanced/sessions/supported [lock:live-server]", test_loadbalanced_sessions_supported, NULL /* ctx */, NULL /* dtor */, skip_if_not_loadbalanced); TestSuite_AddFull(suite, - "/loadbalanced/sessions/do_not_expire", + "/loadbalanced/sessions/do_not_expire [lock:live-server]", test_loadbalanced_sessions_do_not_expire, NULL /* ctx */, NULL /* dtor */, skip_if_not_loadbalanced); TestSuite_AddFull(suite, - "/loadbalanced/client_uri_validation", + "/loadbalanced/client_uri_validation [lock:live-server]", test_loadbalanced_client_uri_validation, NULL /* ctx */, NULL /* dtor */, NULL); TestSuite_AddFull(suite, - "/loadbalanced/connect/single", + "/loadbalanced/connect/single [lock:live-server]", test_loadbalanced_connect_single, NULL /* ctx */, NULL /* dtor */, skip_if_not_loadbalanced); TestSuite_AddFull(suite, - "/loadbalanced/connect/pooled", + "/loadbalanced/connect/pooled [lock:live-server]", test_loadbalanced_connect_pooled, NULL /* ctx */, NULL /* dtor */, skip_if_not_loadbalanced); TestSuite_AddFull(suite, - "/loadbalanced/server_selection_establishes_connection/single", + "/loadbalanced/server_selection_establishes_connection/single [lock:live-server]", test_loadbalanced_server_selection_establishes_connection_single, NULL /* ctx */, NULL /* dtor */, skip_if_not_loadbalanced); TestSuite_AddFull(suite, - "/loadbalanced/cooldown_is_bypassed/single", + "/loadbalanced/cooldown_is_bypassed/single [lock:live-server]", test_loadbalanced_cooldown_is_bypassed_single, NULL /* dtor */, NULL /* ctx */, @@ -892,7 +892,7 @@ test_loadbalanced_install(TestSuite *suite) suite, "/loadbalanced/post_handshake_error_clears_pool", test_post_handshake_error_clears_pool); TestSuite_AddFull(suite, - "/loadbalanced/sends_recoveryToken", + "/loadbalanced/sends_recoveryToken [lock:live-server]", test_loadbalanced_sends_recoveryToken, NULL /* ctx */, NULL /* dtor */, diff --git a/src/libmongoc/tests/test-mongoc-long-namespace.c b/src/libmongoc/tests/test-mongoc-long-namespace.c index 95b507134ba..19456850df4 100644 --- a/src/libmongoc/tests/test-mongoc-long-namespace.c +++ b/src/libmongoc/tests/test-mongoc-long-namespace.c @@ -459,22 +459,24 @@ test_long_namespace_install(TestSuite *suite) /* MongoDB 4.4 (wire version 9) introduced support for long namespaces in * SERVER-32959 */ - add_long_namespace_test("/long_namespace/crud", crud, test_framework_skip_if_max_wire_version_less_than_9); + add_long_namespace_test( + "/long_namespace/crud [lock:live-server]", crud, test_framework_skip_if_max_wire_version_less_than_9); - add_long_namespace_test("/long_namespace/getmore", getmore, test_framework_skip_if_max_wire_version_less_than_9); + add_long_namespace_test( + "/long_namespace/getmore [lock:live-server]", getmore, test_framework_skip_if_max_wire_version_less_than_9); - add_long_namespace_test("/long_namespace/change_stream", + add_long_namespace_test("/long_namespace/change_stream [lock:live-server]", change_stream, test_framework_skip_if_not_rs_version_9, test_framework_skip_if_no_sessions); - add_long_namespace_test("/long_namespace/collection_rename", + add_long_namespace_test("/long_namespace/collection_rename [lock:live-server]", collection_rename, test_framework_skip_if_max_wire_version_less_than_9, test_framework_skip_if_mongos); TestSuite_AddFull(suite, - "/long_namespace/unsupported_long_coll", + "/long_namespace/unsupported_long_coll [lock:live-server]", unsupported_long_coll, NULL /* dtor */, NULL /* ctx */, diff --git a/src/libmongoc/tests/test-mongoc-max-staleness.c b/src/libmongoc/tests/test-mongoc-max-staleness.c index 17d207b17ad..20750b73aaa 100644 --- a/src/libmongoc/tests/test-mongoc-max-staleness.c +++ b/src/libmongoc/tests/test-mongoc-max-staleness.c @@ -339,23 +339,27 @@ test_client_max_staleness_install(TestSuite *suite) TestSuite_Add(suite, "/Client/max_staleness", test_mongoc_client_max_staleness); TestSuite_AddMockServerTest(suite, "/Client/max_staleness/mongos", test_mongos_max_staleness_read_pref); TestSuite_AddFull(suite, - "/Client/last_write_date", + "/Client/last_write_date [lock:live-server][timeout:30]", test_last_write_date, NULL, NULL, test_framework_skip_if_not_replset, test_framework_skip_if_slow); TestSuite_AddFull(suite, - "/Client/last_write_date/pooled", + "/Client/last_write_date/pooled [lock:live-server][timeout:30]", test_last_write_date_pooled, NULL, NULL, test_framework_skip_if_not_replset, test_framework_skip_if_slow); - TestSuite_AddFull( - suite, "/Client/last_write_date_absent", test_last_write_date_absent, NULL, NULL, test_framework_skip_if_replset); TestSuite_AddFull(suite, - "/Client/last_write_date_absent/pooled", + "/Client/last_write_date_absent [lock:live-server]", + test_last_write_date_absent, + NULL, + NULL, + test_framework_skip_if_replset); + TestSuite_AddFull(suite, + "/Client/last_write_date_absent/pooled [lock:live-server]", test_last_write_date_absent_pooled, NULL, NULL, diff --git a/src/libmongoc/tests/test-mongoc-mongos-pinning.c b/src/libmongoc/tests/test-mongoc-mongos-pinning.c index 404e34569eb..19817d339c4 100644 --- a/src/libmongoc/tests/test-mongoc-mongos-pinning.c +++ b/src/libmongoc/tests/test-mongoc-mongos-pinning.c @@ -169,7 +169,7 @@ void test_mongos_pinning_install(TestSuite *suite) { TestSuite_AddFull(suite, - "/mongos_pinning/new_transaction_unpins", + "/mongos_pinning/new_transaction_unpins [lock:live-server]", test_new_transaction_unpins, NULL, NULL, @@ -178,7 +178,7 @@ test_mongos_pinning_install(TestSuite *suite) test_framework_skip_if_not_mongos); TestSuite_AddFull(suite, - "/mongos_pinning/non_transaction_unpins", + "/mongos_pinning/non_transaction_unpins [lock:live-server]", test_non_transaction_unpins, NULL, NULL, diff --git a/src/libmongoc/tests/test-mongoc-primary-stepdown.c b/src/libmongoc/tests/test-mongoc-primary-stepdown.c index da17735162c..ac98251ded9 100644 --- a/src/libmongoc/tests/test-mongoc-primary-stepdown.c +++ b/src/libmongoc/tests/test-mongoc-primary-stepdown.c @@ -422,14 +422,14 @@ test_primary_stepdown_install(TestSuite *suite) #define TestPooledAndSingle(name, fn) \ if (1) { \ TestSuite_AddFull(suite, \ - name "/single", \ + name "/single [lock:live-server]", \ fn, \ NULL, \ &single_ctx, \ test_framework_skip_if_auth, \ test_framework_skip_if_not_replset); \ TestSuite_AddFull(suite, \ - name "/pooled", \ + name "/pooled [lock:live-server]", \ fn, \ NULL, \ &pooled_ctx, \ diff --git a/src/libmongoc/tests/test-mongoc-retryable-reads.c b/src/libmongoc/tests/test-mongoc-retryable-reads.c index 597dfe9bc2f..74d277f768b 100644 --- a/src/libmongoc/tests/test-mongoc-retryable-reads.c +++ b/src/libmongoc/tests/test-mongoc-retryable-reads.c @@ -564,28 +564,28 @@ test_retryable_reads_install(TestSuite *suite) { test_all_spec_tests(suite); TestSuite_AddFull(suite, - "/retryable_reads/cmd_helpers", + "/retryable_reads/cmd_helpers [lock:live-server]", test_cmd_helpers, NULL, NULL, test_framework_skip_if_mongos, test_framework_skip_if_no_failpoint); TestSuite_AddFull(suite, - "/retryable_reads/retry_off", + "/retryable_reads/retry_off [lock:live-server]", test_retry_reads_off, NULL, NULL, test_framework_skip_if_mongos, test_framework_skip_if_no_failpoint); TestSuite_AddFull(suite, - "/retryable_reads/sharded/on_other_mongos", + "/retryable_reads/sharded/on_other_mongos [lock:live-server]", test_retry_reads_sharded_on_other_mongos, NULL, NULL, test_framework_skip_if_not_mongos, test_framework_skip_if_no_failpoint); TestSuite_AddFull(suite, - "/retryable_reads/sharded/on_same_mongos", + "/retryable_reads/sharded/on_same_mongos [lock:live-server]", test_retry_reads_sharded_on_same_mongos, NULL, NULL, diff --git a/src/libmongoc/tests/test-mongoc-retryable-writes.c b/src/libmongoc/tests/test-mongoc-retryable-writes.c index 3f14e89c705..a2a49badaab 100644 --- a/src/libmongoc/tests/test-mongoc-retryable-writes.c +++ b/src/libmongoc/tests/test-mongoc-retryable-writes.c @@ -1236,7 +1236,7 @@ test_retryable_writes_install(TestSuite *suite) test_all_spec_tests(suite); TestSuite_AddMockServerTest(suite, "/retryable_writes/failover", test_rs_failover, test_framework_skip_if_no_crypto); TestSuite_AddFull(suite, - "/retryable_writes/command_with_opts", + "/retryable_writes/command_with_opts [lock:live-server]", test_command_with_opts, NULL, NULL, @@ -1259,21 +1259,25 @@ test_retryable_writes_install(TestSuite *suite) "/retryable_writes/bulk_operation_execute_unacknowledged", test_bulk_operation_execute_unacknowledged, test_framework_skip_if_no_crypto); - TestSuite_AddFull( - suite, "/retryable_writes/no_crypto", test_retry_no_crypto, NULL, NULL, test_framework_skip_if_crypto); + TestSuite_AddFull(suite, + "/retryable_writes/no_crypto [lock:live-server]", + test_retry_no_crypto, + NULL, + NULL, + test_framework_skip_if_crypto); TestSuite_AddMockServerTest(suite, "/retryable_writes/unsupported_storage_engine_error", test_unsupported_storage_engine_error, test_framework_skip_if_no_crypto); TestSuite_AddFull(suite, - "/retryable_writes/bulk_tracks_new_server", + "/retryable_writes/bulk_tracks_new_server [lock:live-server]", test_bulk_retry_tracks_new_server, NULL /* dtor */, NULL /* ctx */, test_framework_skip_if_not_replset, test_framework_skip_if_no_crypto); TestSuite_AddFull(suite, - "/retryable_writes/prose_test_3", + "/retryable_writes/prose_test_3 [lock:live-server]", retryable_writes_prose_test_3, NULL, NULL, @@ -1281,7 +1285,7 @@ test_retryable_writes_install(TestSuite *suite) test_framework_skip_if_max_wire_version_less_than_17, test_framework_skip_if_no_crypto); TestSuite_AddFull(suite, - "/retryable_writes/prose_test_3/find_modify", + "/retryable_writes/prose_test_3/find_modify [lock:live-server]", retryable_writes_original_error_find_modify, NULL, NULL, @@ -1289,7 +1293,7 @@ test_retryable_writes_install(TestSuite *suite) test_framework_skip_if_max_wire_version_less_than_17, test_framework_skip_if_no_crypto); TestSuite_AddFull(suite, - "/retryable_writes/prose_test_3/general_command", + "/retryable_writes/prose_test_3/general_command [lock:live-server]", retryable_writes_original_error_general_command, NULL, NULL, @@ -1297,7 +1301,7 @@ test_retryable_writes_install(TestSuite *suite) test_framework_skip_if_max_wire_version_less_than_17, test_framework_skip_if_no_crypto); TestSuite_AddFull(suite, - "/retryable_writes/prose_test_3/bulkwrite", + "/retryable_writes/prose_test_3/bulkwrite [lock:live-server]", retryable_writes_original_error_bulkwrite, NULL, NULL, @@ -1305,7 +1309,7 @@ test_retryable_writes_install(TestSuite *suite) test_framework_skip_if_max_wire_version_less_than_25, // require server 8.0 test_framework_skip_if_no_crypto); TestSuite_AddFull(suite, - "/retryable_writes/prose_test_4/insert", + "/retryable_writes/prose_test_4/insert [lock:live-server]", retryable_writes_sharded_on_other_mongos_insert, NULL, NULL, @@ -1315,7 +1319,7 @@ test_retryable_writes_install(TestSuite *suite) test_framework_skip_if_max_wire_version_less_than_9, test_framework_skip_if_no_crypto); TestSuite_AddFull(suite, - "/retryable_writes/prose_test_4/bulkwrite", + "/retryable_writes/prose_test_4/bulkwrite [lock:live-server]", retryable_writes_sharded_on_other_mongos_bulkWrite, NULL, NULL, @@ -1324,7 +1328,7 @@ test_retryable_writes_install(TestSuite *suite) test_framework_skip_if_max_wire_version_less_than_25, // require server 8.0 test_framework_skip_if_no_crypto); TestSuite_AddFull(suite, - "/retryable_writes/prose_test_5", + "/retryable_writes/prose_test_5 [lock:live-server]", retryable_writes_sharded_on_same_mongos, NULL, NULL, diff --git a/src/libmongoc/tests/test-mongoc-sample-commands.c b/src/libmongoc/tests/test-mongoc-sample-commands.c index 7ab24e80834..11d6838865c 100644 --- a/src/libmongoc/tests/test-mongoc-sample-commands.c +++ b/src/libmongoc/tests/test-mongoc-sample-commands.c @@ -4463,5 +4463,6 @@ void test_samples_install(TestSuite *suite) { TestSuite_AddLive(suite, "/Samples", test_sample_commands); - TestSuite_AddFull(suite, "/Samples/with_txn", test_with_txn_example, NULL, NULL, test_framework_skip_if_no_txns); + TestSuite_AddFull( + suite, "/Samples/with_txn [lock:live-server]", test_with_txn_example, NULL, NULL, test_framework_skip_if_no_txns); } diff --git a/src/libmongoc/tests/test-mongoc-scram.c b/src/libmongoc/tests/test-mongoc-scram.c index 9553bec77b0..7eb25f0f962 100644 --- a/src/libmongoc/tests/test-mongoc-scram.c +++ b/src/libmongoc/tests/test-mongoc-scram.c @@ -749,14 +749,14 @@ test_scram_install(TestSuite *suite) TestSuite_Add(suite, "/scram/utf8_to_unicode", test_mongoc_utf8_to_unicode); #endif TestSuite_AddFull(suite, - "/scram/cache_invalidation", + "/scram/cache_invalidation [lock:live-server]", test_mongoc_scram_cache_invalidation, NULL, NULL, test_framework_skip_if_no_auth, test_framework_skip_if_macos); // CDRIVER-6079 TestSuite_AddFull(suite, - "/scram/auth_tests", + "/scram/auth_tests [lock:live-server]", test_mongoc_scram_auth, NULL /* dtor */, NULL /* ctx */, @@ -764,7 +764,7 @@ test_scram_install(TestSuite *suite) _skip_if_no_sha256, TestSuite_CheckLive); TestSuite_AddFull(suite, - "/scram/saslprep_auth", + "/scram/saslprep_auth [lock:live-server]", test_mongoc_saslprep_auth, NULL /* dtor */, NULL /* ctx */, @@ -772,7 +772,7 @@ test_scram_install(TestSuite *suite) _skip_if_no_sha256, TestSuite_CheckLive); TestSuite_AddFull(suite, - "/scram/empty_password", + "/scram/empty_password [lock:live-server]", test_mongoc_scram_empty_password, NULL /* dtor */, NULL /* ctx */, diff --git a/src/libmongoc/tests/test-mongoc-sdam.c b/src/libmongoc/tests/test-mongoc-sdam.c index 7751df58552..99191f11d0e 100644 --- a/src/libmongoc/tests/test-mongoc-sdam.c +++ b/src/libmongoc/tests/test-mongoc-sdam.c @@ -1001,28 +1001,28 @@ test_sdam_install(TestSuite *suite) { test_all_spec_tests(suite); TestSuite_AddFull(suite, - "/server_discovery_and_monitoring/topology/discovery", + "/server_discovery_and_monitoring/topology/discovery [lock:live-server]", test_topology_discovery, NULL /* dtor */, NULL /* ctx */, test_framework_skip_if_not_replset); TestSuite_AddFull(suite, - "/server_discovery_and_monitoring/directconnection", + "/server_discovery_and_monitoring/directconnection [lock:live-server]", test_direct_connection, NULL /* dtor */, NULL /* ctx */, test_framework_skip_if_not_replset); TestSuite_AddFull(suite, - "/server_discovery_and_monitoring/existing/behavior", + "/server_discovery_and_monitoring/existing/behavior [lock:live-server]", test_existing_behavior, NULL /* dtor */, NULL /* ctx */, test_framework_skip_if_not_replset); TestSuite_AddFull(suite, - "/server_discovery_and_monitoring/prose/rtt", + "/server_discovery_and_monitoring/prose/rtt [lock:live-server]", test_prose_rtt, NULL /* dtor */, NULL /* ctx */, test_framework_skip_if_max_wire_version_less_than_9); - TestSuite_Add(suite, "/server_discovery_and_monitoring/prose/heartbeat", test_prose_heartbeat); + TestSuite_Add(suite, "/server_discovery_and_monitoring/prose/heartbeat [lock:live-server]", test_prose_heartbeat); } diff --git a/src/libmongoc/tests/test-mongoc-server-selection-errors.c b/src/libmongoc/tests/test-mongoc-server-selection-errors.c index 2cdd26d3220..2b0163ecf8c 100644 --- a/src/libmongoc/tests/test-mongoc-server-selection-errors.c +++ b/src/libmongoc/tests/test-mongoc-server-selection-errors.c @@ -293,7 +293,7 @@ test_server_selection_errors_install(TestSuite *suite) { TestSuite_Add(suite, "/server_selection/errors/dns/direct/single", test_server_selection_error_dns_direct_single); TestSuite_AddFull(suite, - "/server_selection/errors/dns/direct/pooled", + "/server_selection/errors/dns/direct/pooled [timeout:30]", test_server_selection_error_dns_direct_pooled, NULL, NULL, @@ -301,43 +301,43 @@ test_server_selection_errors_install(TestSuite *suite) TestSuite_Add( suite, "/server_selection/errors/dns/multi/fail/single", test_server_selection_error_dns_multi_fail_single); TestSuite_AddFull(suite, - "/server_selection/errors/dns/multi/fail/pooled", + "/server_selection/errors/dns/multi/fail/pooled [lock:live-server][timeout:30]", test_server_selection_error_dns_multi_fail_pooled, NULL, NULL, test_framework_skip_if_slow); TestSuite_AddFull(suite, - "/server_selection/errors/dns/multi/success/single", + "/server_selection/errors/dns/multi/success/single [lock:live-server]", test_server_selection_error_dns_multi_success_single, NULL, NULL, test_framework_skip_if_single); TestSuite_AddFull(suite, - "/server_selection/errors/dns/multi/success/pooled", + "/server_selection/errors/dns/multi/success/pooled [lock:live-server]", test_server_selection_error_dns_multi_success_pooled, NULL, NULL, test_framework_skip_if_single); TestSuite_AddFull(suite, - "/server_selection/errors/uds/auth_failure/single", + "/server_selection/errors/uds/auth_failure/single [lock:live-server]", test_server_selection_uds_auth_failure_single, NULL, NULL, test_framework_skip_if_no_uds); TestSuite_AddFull(suite, - "/server_selection/errors/uds/auth_failure/pooled", + "/server_selection/errors/uds/auth_failure/pooled [lock:live-server]", test_server_selection_uds_auth_failure_pooled, NULL, NULL, test_framework_skip_if_no_uds); TestSuite_AddFull(suite, - "/server_selection/errors/uds/not_found/single", + "/server_selection/errors/uds/not_found/single [lock:live-server]", test_server_selection_uds_not_found_single, NULL, NULL, test_framework_skip_if_windows); TestSuite_AddFull(suite, - "/server_selection/errors/uds/not_found/pooled", + "/server_selection/errors/uds/not_found/pooled [lock:live-server]", test_server_selection_uds_not_found_pooled, NULL, NULL, diff --git a/src/libmongoc/tests/test-mongoc-socket.c b/src/libmongoc/tests/test-mongoc-socket.c index 037d67aaee4..ec48354ef29 100644 --- a/src/libmongoc/tests/test-mongoc-socket.c +++ b/src/libmongoc/tests/test-mongoc-socket.c @@ -459,8 +459,14 @@ void test_socket_install(TestSuite *suite) { TestSuite_Add(suite, "/Socket/check_closed", test_mongoc_socket_check_closed); - TestSuite_AddFull(suite, "/Socket/timed_out", test_mongoc_socket_timed_out, NULL, NULL, test_framework_skip_if_slow); - TestSuite_AddFull(suite, "/Socket/sendv", test_mongoc_socket_sendv, NULL, NULL, test_framework_skip_if_slow); TestSuite_AddFull( - suite, "/Socket/connect_refusal", test_mongoc_socket_poll_refusal, NULL, NULL, test_framework_skip_if_slow); + suite, "/Socket/timed_out [timeout:30]", test_mongoc_socket_timed_out, NULL, NULL, test_framework_skip_if_slow); + TestSuite_AddFull( + suite, "/Socket/sendv [timeout:30]", test_mongoc_socket_sendv, NULL, NULL, test_framework_skip_if_slow); + TestSuite_AddFull(suite, + "/Socket/connect_refusal [timeout:30]", + test_mongoc_socket_poll_refusal, + NULL, + NULL, + test_framework_skip_if_slow); } diff --git a/src/libmongoc/tests/test-mongoc-topology-reconcile.c b/src/libmongoc/tests/test-mongoc-topology-reconcile.c index ab33d8eb065..15391483fbb 100644 --- a/src/libmongoc/tests/test-mongoc-topology-reconcile.c +++ b/src/libmongoc/tests/test-mongoc-topology-reconcile.c @@ -638,20 +638,28 @@ test_topology_reconcile_add_single(void) void test_topology_reconcile_install(TestSuite *suite) { - TestSuite_AddMockServerTest( - suite, "/TOPOLOGY/reconcile/rs/pooled", test_topology_reconcile_rs_pooled, test_framework_skip_if_slow); - TestSuite_AddMockServerTest( - suite, "/TOPOLOGY/reconcile/rs/single", test_topology_reconcile_rs_single, test_framework_skip_if_slow); + TestSuite_AddMockServerTest(suite, + "/TOPOLOGY/reconcile/rs/pooled [timeout:30]", + test_topology_reconcile_rs_pooled, + test_framework_skip_if_slow); + TestSuite_AddMockServerTest(suite, + "/TOPOLOGY/reconcile/rs/single [timeout:30]", + test_topology_reconcile_rs_single, + test_framework_skip_if_slow); TestSuite_AddMockServerTest(suite, "/TOPOLOGY/reconcile/sharded/pooled", test_topology_reconcile_sharded_pooled); TestSuite_AddMockServerTest(suite, "/TOPOLOGY/reconcile/sharded/single", test_topology_reconcile_sharded_single); TestSuite_AddFull(suite, - "/TOPOLOGY/reconcile/from_handshake", + "/TOPOLOGY/reconcile/from_handshake [lock:live-server]", test_topology_reconcile_from_handshake, NULL, NULL, test_framework_skip_if_not_replset); - TestSuite_AddMockServerTest( - suite, "/TOPOLOGY/reconcile/retire/single", test_topology_reconcile_retire_single, test_framework_skip_if_slow); - TestSuite_AddMockServerTest( - suite, "/TOPOLOGY/reconcile/add/single", test_topology_reconcile_add_single, test_framework_skip_if_slow); + TestSuite_AddMockServerTest(suite, + "/TOPOLOGY/reconcile/retire/single [timeout:30]", + test_topology_reconcile_retire_single, + test_framework_skip_if_slow); + TestSuite_AddMockServerTest(suite, + "/TOPOLOGY/reconcile/add/single [timeout:30]", + test_topology_reconcile_add_single, + test_framework_skip_if_slow); } diff --git a/src/libmongoc/tests/test-mongoc-topology-scanner.c b/src/libmongoc/tests/test-mongoc-topology-scanner.c index 9a33801cb0d..69c4b97aace 100644 --- a/src/libmongoc/tests/test-mongoc-topology-scanner.c +++ b/src/libmongoc/tests/test-mongoc-topology-scanner.c @@ -710,13 +710,13 @@ test_topology_scanner_install(TestSuite *suite) suite, "/TOPOLOGY/dns", test_topology_scanner_dns, test_framework_skip_if_no_dual_ip_hostname); TestSuite_AddMockServerTest(suite, "/TOPOLOGY/retired_fails_to_initiate", test_topology_retired_fails_to_initiate); TestSuite_AddFull(suite, - "/TOPOLOGY/scanner/renegotiate/single", + "/TOPOLOGY/scanner/renegotiate/single [lock:live-server][timeout:30]", test_topology_scanner_does_not_renegotiate_single, NULL, NULL, test_framework_skip_if_slow_or_live); TestSuite_AddFull(suite, - "/TOPOLOGY/scanner/renegotiate/pooled", + "/TOPOLOGY/scanner/renegotiate/pooled [lock:live-server][timeout:30]", test_topology_scanner_does_not_renegotiate_pooled, NULL, NULL, diff --git a/src/libmongoc/tests/test-mongoc-topology.c b/src/libmongoc/tests/test-mongoc-topology.c index dd16d10e851..80da044dbef 100644 --- a/src/libmongoc/tests/test-mongoc-topology.c +++ b/src/libmongoc/tests/test-mongoc-topology.c @@ -2607,89 +2607,102 @@ test_topology_install(TestSuite *suite) TestSuite_AddLive(suite, "/Topology/client_pool_creation", test_topology_client_pool_creation); TestSuite_AddLive(suite, "/Topology/start_stop", test_topology_thread_start_stop); TestSuite_AddFull(suite, - "/Topology/server_selection_try_once_option", + "/Topology/server_selection_try_once_option [lock:live-server][timeout:30]", test_server_selection_try_once_option, NULL, NULL, test_framework_skip_if_slow); TestSuite_AddFull(suite, - "/Topology/server_selection_try_once", + "/Topology/server_selection_try_once [lock:live-server][timeout:30]", test_server_selection_try_once, NULL, NULL, test_framework_skip_if_slow); TestSuite_AddFull(suite, - "/Topology/server_selection_try_once_false", + "/Topology/server_selection_try_once_false [lock:live-server][timeout:30]", test_server_selection_try_once_false, NULL, NULL, test_framework_skip_if_slow); TestSuite_AddFull(suite, - "/Topology/invalidate_server/single", + "/Topology/invalidate_server/single [lock:live-server][timeout:30]", test_topology_invalidate_server_single, NULL, NULL, test_framework_skip_if_slow_or_live); TestSuite_AddFull(suite, - "/Topology/invalidate_server/pooled", + "/Topology/invalidate_server/pooled [lock:live-server][timeout:30]", test_topology_invalidate_server_pooled, NULL, NULL, test_framework_skip_if_slow_or_live); TestSuite_AddFull(suite, - "/Topology/invalid_cluster_node", + "/Topology/invalid_cluster_node [lock:live-server][timeout:30]", test_invalid_cluster_node, NULL, NULL, test_framework_skip_if_slow_or_live); TestSuite_AddFull(suite, - "/Topology/max_wire_version_race_condition", + "/Topology/max_wire_version_race_condition [lock:live-server]", test_max_wire_version_race_condition, NULL, NULL, test_framework_skip_if_no_auth); + TestSuite_AddMockServerTest(suite, + "/Topology/cooldown/standalone [timeout:30]", + test_cooldown_standalone, + NULL, + NULL, + test_framework_skip_if_slow); TestSuite_AddMockServerTest( - suite, "/Topology/cooldown/standalone", test_cooldown_standalone, NULL, NULL, test_framework_skip_if_slow); - TestSuite_AddMockServerTest( - suite, "/Topology/cooldown/rs", test_cooldown_rs, NULL, NULL, test_framework_skip_if_slow); + suite, "/Topology/cooldown/rs [timeout:30]", test_cooldown_rs, NULL, NULL, test_framework_skip_if_slow); TestSuite_AddMockServerTest( - suite, "/Topology/cooldown/retry", test_cooldown_retry, NULL, NULL, test_framework_skip_if_slow); + suite, "/Topology/cooldown/retry [timeout:30]", test_cooldown_retry, NULL, NULL, test_framework_skip_if_slow); TestSuite_Add(suite, "/Topology/multiple_selection_errors", test_multiple_selection_errors); TestSuite_AddMockServerTest(suite, "/Topology/connect_timeout/succeed", test_select_after_timeout); TestSuite_AddMockServerTest(suite, "/Topology/try_once/succeed", test_select_after_try_once); TestSuite_AddLive(suite, "/Topology/invalid_server_id", test_invalid_server_id); TestSuite_AddMockServerTest(suite, "/Topology/server_removed/single", test_server_removed_during_handshake_single); TestSuite_AddMockServerTest(suite, "/Topology/server_removed/pooled", test_server_removed_during_handshake_pooled); - TestSuite_AddFull(suite, "/Topology/rtt", test_rtt, NULL, NULL, test_framework_skip_if_slow); + TestSuite_AddFull( + suite, "/Topology/rtt [lock:live-server][timeout:30]", test_rtt, NULL, NULL, test_framework_skip_if_slow); TestSuite_AddMockServerTest(suite, "/Topology/add_and_scan_failure", test_add_and_scan_failure); - TestSuite_AddMockServerTest( - suite, "/Topology/hello_retry/single/hangup", test_hello_retry_single_hangup, test_framework_skip_if_slow); - TestSuite_AddMockServerTest( - suite, "/Topology/hello_retry/single/timeout", test_hello_retry_single_timeout, test_framework_skip_if_slow); TestSuite_AddMockServerTest(suite, - "/Topology/hello_retry/single/hangup/fail", + "/Topology/hello_retry/single/hangup [timeout:30]", + test_hello_retry_single_hangup, + test_framework_skip_if_slow); + TestSuite_AddMockServerTest(suite, + "/Topology/hello_retry/single/timeout [timeout:30]", + test_hello_retry_single_timeout, + test_framework_skip_if_slow); + TestSuite_AddMockServerTest(suite, + "/Topology/hello_retry/single/hangup/fail [timeout:30]", test_hello_retry_single_hangup_fail, test_framework_skip_if_slow); TestSuite_AddMockServerTest(suite, - "/Topology/hello_retry/single/timeout/fail", + "/Topology/hello_retry/single/timeout/fail [timeout:30]", test_hello_retry_single_timeout_fail, test_framework_skip_if_slow); - TestSuite_AddMockServerTest( - suite, "/Topology/hello_retry/pooled/hangup", test_hello_retry_pooled_hangup, test_framework_skip_if_slow); - TestSuite_AddMockServerTest( - suite, "/Topology/hello_retry/pooled/timeout", test_hello_retry_pooled_timeout, test_framework_skip_if_slow); TestSuite_AddMockServerTest(suite, - "/Topology/hello_retry/pooled/hangup/fail", + "/Topology/hello_retry/pooled/hangup [timeout:30]", + test_hello_retry_pooled_hangup, + test_framework_skip_if_slow); + TestSuite_AddMockServerTest(suite, + "/Topology/hello_retry/pooled/timeout [timeout:30]", + test_hello_retry_pooled_timeout, + test_framework_skip_if_slow); + TestSuite_AddMockServerTest(suite, + "/Topology/hello_retry/pooled/hangup/fail [timeout:30]", test_hello_retry_pooled_hangup_fail, test_framework_skip_if_slow); TestSuite_AddMockServerTest(suite, - "/Topology/hello_retry/pooled/timeout/fail", + "/Topology/hello_retry/pooled/timeout/fail [timeout:30]", test_hello_retry_pooled_timeout_fail, test_framework_skip_if_slow); TestSuite_AddMockServerTest( - suite, "/Topology/incompatible_error", test_incompatible_error, test_framework_skip_if_slow); + suite, "/Topology/incompatible_error [timeout:30]", test_incompatible_error, test_framework_skip_if_slow); TestSuite_AddMockServerTest(suite, - "/Topology/compatible_null_error_pointer", + "/Topology/compatible_null_error_pointer [timeout:30]", test_compatible_null_error_pointer, test_framework_skip_if_slow); TestSuite_AddMockServerTest( @@ -2704,5 +2717,5 @@ test_topology_install(TestSuite *suite) TestSuite_AddMockServerTest(suite, "/Topology/hello_ok/single", test_hello_ok_single); TestSuite_AddMockServerTest(suite, "/Topology/hello_ok/pooled", test_hello_ok_pooled); TestSuite_AddMockServerTest(suite, "/Topology/failure_to_setup_after_retry", test_failure_to_setup_after_retry); - TestSuite_Add(suite, "/Topology/detect_nongenuine_hosts", test_detect_nongenuine_hosts); + TestSuite_Add(suite, "/Topology/detect_nongenuine_hosts [lock:live-server]", test_detect_nongenuine_hosts); } diff --git a/src/libmongoc/tests/test-mongoc-transactions.c b/src/libmongoc/tests/test-mongoc-transactions.c index 6fe63d1ac37..3cc292955ed 100644 --- a/src/libmongoc/tests/test-mongoc-transactions.c +++ b/src/libmongoc/tests/test-mongoc-transactions.c @@ -1044,10 +1044,18 @@ test_transactions_install(TestSuite *suite) test_framework_skip_if_no_txns, test_framework_skip_if_slow); - TestSuite_AddFull( - suite, "/transactions/supported", test_transactions_supported, NULL, NULL, test_framework_skip_if_no_txns); - TestSuite_AddFull( - suite, "/transactions/in_transaction", test_in_transaction, NULL, NULL, test_framework_skip_if_no_txns); + TestSuite_AddFull(suite, + "/transactions/supported [lock:live-server]", + test_transactions_supported, + NULL, + NULL, + test_framework_skip_if_no_txns); + TestSuite_AddFull(suite, + "/transactions/in_transaction [lock:live-server]", + test_in_transaction, + NULL, + NULL, + test_framework_skip_if_no_txns); TestSuite_AddMockServerTest( suite, "/transactions/server_selection_err", test_server_selection_error, test_framework_skip_if_no_crypto); TestSuite_AddMockServerTest( @@ -1055,15 +1063,19 @@ test_transactions_install(TestSuite *suite) TestSuite_AddMockServerTest( suite, "/transactions/unknown_commit_result", test_unknown_commit_result, test_framework_skip_if_no_crypto); TestSuite_AddFull(suite, - "/transactions/cursor_primary_read_pref", + "/transactions/cursor_primary_read_pref [lock:live-server]", test_cursor_primary_read_pref, NULL, NULL, test_framework_skip_if_no_txns); - TestSuite_AddFull( - suite, "/transactions/inherit_from_client", test_inherit_from_client, NULL, NULL, test_framework_skip_if_no_txns); TestSuite_AddFull(suite, - "/transactions/recovery_token_cleared", + "/transactions/inherit_from_client [lock:live-server]", + test_inherit_from_client, + NULL, + NULL, + test_framework_skip_if_no_txns); + TestSuite_AddFull(suite, + "/transactions/recovery_token_cleared [lock:live-server]", test_transaction_recovery_token_cleared, NULL, NULL, @@ -1071,7 +1083,7 @@ test_transactions_install(TestSuite *suite) test_framework_skip_if_no_crypto, test_framework_skip_if_not_mongos); TestSuite_AddFull(suite, - "/transactions/selected_server_pinned_to_mongos", + "/transactions/selected_server_pinned_to_mongos [lock:live-server]", test_selected_server_is_pinned_to_mongos, NULL, NULL, diff --git a/src/libmongoc/tests/test-mongoc-with-transaction.c b/src/libmongoc/tests/test-mongoc-with-transaction.c index 6a3952f20d8..84a73cc3e13 100644 --- a/src/libmongoc/tests/test-mongoc-with-transaction.c +++ b/src/libmongoc/tests/test-mongoc-with-transaction.c @@ -84,7 +84,7 @@ void test_with_transaction_install(TestSuite *suite) { TestSuite_AddFull(suite, - "/with_transaction/timeout_tests", + "/with_transaction/timeout_tests [lock:live-server]", test_with_transaction_timeout, NULL, NULL, diff --git a/src/libmongoc/tests/test-mongoc-write-commands.c b/src/libmongoc/tests/test-mongoc-write-commands.c index ff1c5fa8945..e388f72cf88 100644 --- a/src/libmongoc/tests/test-mongoc-write-commands.c +++ b/src/libmongoc/tests/test-mongoc-write-commands.c @@ -455,10 +455,15 @@ test_write_command_install(TestSuite *suite) TestSuite_AddLive(suite, "/WriteCommand/split_insert", test_split_insert); TestSuite_AddLive(suite, "/WriteCommand/bypass_not_sent", test_bypass_not_sent); TestSuite_AddLive(suite, "/WriteCommand/invalid_write_concern", test_invalid_write_concern); - TestSuite_AddFull(suite, "/WriteCommand/bypass_validation", test_bypass_validation, NULL, NULL, TestSuite_CheckLive); + TestSuite_AddFull(suite, + "/WriteCommand/bypass_validation [lock:live-server]", + test_bypass_validation, + NULL, + NULL, + TestSuite_CheckLive); TestSuite_AddMockServerTest(suite, "/WriteCommand/insert_disconnect_mid_batch", test_disconnect_mid_batch); TestSuite_AddFull(suite, - "/WriteCommand/invalid_wc_server_error", + "/WriteCommand/invalid_wc_server_error [lock:live-server]", _test_invalid_wc_server_error, NULL, NULL, diff --git a/src/libmongoc/tests/test-mongoc-write-concern.c b/src/libmongoc/tests/test-mongoc-write-concern.c index ddd254bcb84..6eebed5a13c 100644 --- a/src/libmongoc/tests/test-mongoc-write-concern.c +++ b/src/libmongoc/tests/test-mongoc-write-concern.c @@ -613,16 +613,21 @@ test_write_concern_install(TestSuite *suite) TestSuite_Add(suite, "/WriteConcern/wtimeout_preserved", test_write_concern_wtimeout_preserved); TestSuite_AddMockServerTest(suite, "/WriteConcern", test_write_concern); TestSuite_AddLive(suite, "/WriteConcern/unacknowledged", test_write_concern_unacknowledged); - TestSuite_AddFull(suite, "/WriteConcern/inherited_fam", test_fam_no_session_no_txn, NULL, NULL, TestSuite_CheckLive); TestSuite_AddFull(suite, - "/WriteConcern/inherited_fam_session_no_txn", + "/WriteConcern/inherited_fam [lock:live-server]", + test_fam_no_session_no_txn, + NULL, + NULL, + TestSuite_CheckLive); + TestSuite_AddFull(suite, + "/WriteConcern/inherited_fam_session_no_txn [lock:live-server]", test_fam_session_no_txn, NULL, NULL, test_framework_skip_if_no_sessions, test_framework_skip_if_no_txns); TestSuite_AddFull(suite, - "/WriteConcern/inherited_fam_txn", + "/WriteConcern/inherited_fam_txn [lock:live-server]", test_fam_session_txn, NULL, NULL, diff --git a/src/libmongoc/tests/test-mongoc-x509.c b/src/libmongoc/tests/test-mongoc-x509.c index 0e73a844015..11135e667af 100644 --- a/src/libmongoc/tests/test-mongoc-x509.c +++ b/src/libmongoc/tests/test-mongoc-x509.c @@ -593,13 +593,13 @@ test_x509_install(TestSuite *suite) { #ifdef MONGOC_ENABLE_SSL TestSuite_AddFull(suite, - "/X509/auth", + "/X509/auth [lock:live-server]", test_x509_auth, NULL, NULL, test_framework_skip_if_no_auth, test_framework_skip_if_no_server_ssl); - TestSuite_AddFull(suite, "/X509/crl", test_crl, NULL, NULL, test_framework_skip_if_no_server_ssl); + TestSuite_AddFull(suite, "/X509/crl [lock:live-server]", test_crl, NULL, NULL, test_framework_skip_if_no_server_ssl); #endif #ifdef MONGOC_ENABLE_OCSP_OPENSSL From b9c15bb7b19fac3cc8efe59488d081a250c22e75 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Mon, 20 Oct 2025 16:53:02 -0600 Subject: [PATCH 25/49] Fix OpenSSL test that requires an SSL context --- src/libmongoc/tests/test-mongoc-topology-scanner.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libmongoc/tests/test-mongoc-topology-scanner.c b/src/libmongoc/tests/test-mongoc-topology-scanner.c index 69c4b97aace..e7305957c0b 100644 --- a/src/libmongoc/tests/test-mongoc-topology-scanner.c +++ b/src/libmongoc/tests/test-mongoc-topology-scanner.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -71,6 +72,7 @@ _test_topology_scanner(bool with_ssl) copt.weak_cert_validation = 1; mongoc_topology_scanner_set_ssl_opts(topology_scanner, &copt); + topology_scanner->openssl_ctx = _mongoc_openssl_ctx_new(&copt); } #endif From 4ec209fc60bdb9018572f8cb634f7943ff1b1041 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Mon, 20 Oct 2025 17:01:33 -0600 Subject: [PATCH 26/49] Add missing tags following merge --- src/libmongoc/tests/test-mongoc-bulk.c | 2 +- src/libmongoc/tests/test-mongoc-change-stream.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libmongoc/tests/test-mongoc-bulk.c b/src/libmongoc/tests/test-mongoc-bulk.c index 8a87220ec8a..6fa8e018ce8 100644 --- a/src/libmongoc/tests/test-mongoc-bulk.c +++ b/src/libmongoc/tests/test-mongoc-bulk.c @@ -5044,7 +5044,7 @@ test_bulk_install(TestSuite *suite) TestSuite_AddLive(suite, "/BulkOperation/multiple_execution", test_multiple_execution); TestSuite_AddFull( suite, - "/BulkOperation/big_let", + "/BulkOperation/big_let [lock:live-server]", test_bulk_big_let, NULL, NULL, diff --git a/src/libmongoc/tests/test-mongoc-change-stream.c b/src/libmongoc/tests/test-mongoc-change-stream.c index 5edfa930930..45f4a332104 100644 --- a/src/libmongoc/tests/test-mongoc-change-stream.c +++ b/src/libmongoc/tests/test-mongoc-change-stream.c @@ -2088,7 +2088,7 @@ test_change_stream_install(TestSuite *suite) TestSuite_AddMockServerTest(suite, "/change_stream/pipeline", test_change_stream_pipeline); TestSuite_AddFull(suite, - "/change_stream/live/track_resume_token", + "/change_stream/live/track_resume_token [lock:live-server]", test_change_stream_live_track_resume_token, NULL, NULL, From a3f1c7e55ccf65610eaf782e0853b46354a1ced7 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Mon, 20 Oct 2025 17:14:59 -0600 Subject: [PATCH 27/49] Conditional content in opeen-private.h --- src/libmongoc/src/mongoc/mongoc-openssl-private.h | 3 +++ src/libmongoc/tests/test-mongoc-topology-scanner.c | 2 ++ 2 files changed, 5 insertions(+) diff --git a/src/libmongoc/src/mongoc/mongoc-openssl-private.h b/src/libmongoc/src/mongoc/mongoc-openssl-private.h index 9560b187a52..dc29f486a55 100644 --- a/src/libmongoc/src/mongoc/mongoc-openssl-private.h +++ b/src/libmongoc/src/mongoc/mongoc-openssl-private.h @@ -25,6 +25,8 @@ #include +#if defined(MONGOC_ENABLE_SSL_OPENSSL) + #include #include #include @@ -55,5 +57,6 @@ _mongoc_tlsfeature_has_status_request(const uint8_t *data, int length); BSON_END_DECLS +#endif // OpenSSL enabled? #endif /* MONGOC_OPENSSL_PRIVATE_H */ diff --git a/src/libmongoc/tests/test-mongoc-topology-scanner.c b/src/libmongoc/tests/test-mongoc-topology-scanner.c index e7305957c0b..e4217d2a92b 100644 --- a/src/libmongoc/tests/test-mongoc-topology-scanner.c +++ b/src/libmongoc/tests/test-mongoc-topology-scanner.c @@ -72,7 +72,9 @@ _test_topology_scanner(bool with_ssl) copt.weak_cert_validation = 1; mongoc_topology_scanner_set_ssl_opts(topology_scanner, &copt); +#ifdef MONGOC_ENABLE_SSL_OPENSSL topology_scanner->openssl_ctx = _mongoc_openssl_ctx_new(&copt); +#endif } #endif From 0ee0a3e5ae68f71aca767833a016ffdd4b131628 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Tue, 21 Oct 2025 11:12:41 -0600 Subject: [PATCH 28/49] Fix conditional OpenSSL context in test case --- src/libmongoc/tests/test-mongoc-topology-scanner.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libmongoc/tests/test-mongoc-topology-scanner.c b/src/libmongoc/tests/test-mongoc-topology-scanner.c index e4217d2a92b..721e759492e 100644 --- a/src/libmongoc/tests/test-mongoc-topology-scanner.c +++ b/src/libmongoc/tests/test-mongoc-topology-scanner.c @@ -72,7 +72,7 @@ _test_topology_scanner(bool with_ssl) copt.weak_cert_validation = 1; mongoc_topology_scanner_set_ssl_opts(topology_scanner, &copt); -#ifdef MONGOC_ENABLE_SSL_OPENSSL +#if defined(MONGOC_ENABLE_SSL_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10100000L topology_scanner->openssl_ctx = _mongoc_openssl_ctx_new(&copt); #endif } From c530f9823f8bd3098225826234d031d17ffb565f Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Fri, 24 Oct 2025 09:17:48 -0600 Subject: [PATCH 29/49] Fix NRVO warning --- src/common/src/mlib/str.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/src/mlib/str.h b/src/common/src/mlib/str.h index b3420f88f35..4c3d4d08ef0 100644 --- a/src/common/src/mlib/str.h +++ b/src/common/src/mlib/str.h @@ -1121,7 +1121,7 @@ mstr_sprintf(const char *f, ...) va_end(args); if (!okay) { mstr_destroy(&ret); - return mstr_null(); + return ret; } return ret; } From 095c84567ed11b61f7d7222de41f2fe00456b287 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Fri, 24 Oct 2025 09:18:11 -0600 Subject: [PATCH 30/49] Shift conditional inclusion of openssl detail header --- src/libmongoc/src/mongoc/mongoc-openssl-private.h | 6 ------ src/libmongoc/tests/test-mongoc-topology-scanner.c | 5 ++++- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/libmongoc/src/mongoc/mongoc-openssl-private.h b/src/libmongoc/src/mongoc/mongoc-openssl-private.h index dc29f486a55..ccd1f3e230f 100644 --- a/src/libmongoc/src/mongoc/mongoc-openssl-private.h +++ b/src/libmongoc/src/mongoc/mongoc-openssl-private.h @@ -21,12 +21,8 @@ #include -#include - #include -#if defined(MONGOC_ENABLE_SSL_OPENSSL) - #include #include #include @@ -57,6 +53,4 @@ _mongoc_tlsfeature_has_status_request(const uint8_t *data, int length); BSON_END_DECLS -#endif // OpenSSL enabled? - #endif /* MONGOC_OPENSSL_PRIVATE_H */ diff --git a/src/libmongoc/tests/test-mongoc-topology-scanner.c b/src/libmongoc/tests/test-mongoc-topology-scanner.c index 721e759492e..b81e90ad3ee 100644 --- a/src/libmongoc/tests/test-mongoc-topology-scanner.c +++ b/src/libmongoc/tests/test-mongoc-topology-scanner.c @@ -1,11 +1,14 @@ #include #include #include -#include #include #include #include +#if defined(MONGOC_ENABLE_SSL_OPENSSL) +#include +#endif + #include #include From 09d04a66ab6f9e856e1344beec0794f936c069a1 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Fri, 24 Oct 2025 09:58:31 -0600 Subject: [PATCH 31/49] Tweak error message on invalid test specifier string --- src/libmongoc/tests/TestSuite.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libmongoc/tests/TestSuite.c b/src/libmongoc/tests/TestSuite.c index 768f32f99b9..f12c86b4e7e 100644 --- a/src/libmongoc/tests/TestSuite.c +++ b/src/libmongoc/tests/TestSuite.c @@ -312,8 +312,10 @@ _V_TestSuite_AddFull( while (tail.len) { mlib_check(mstr_split_around(tail, mstr_cstring("["), NULL, &tag), because, - "Expected an opening bracket for the next test tag"); - mlib_check(mstr_split_around(tag, mstr_cstring("]"), &tag, &tail), because, "Expected a closing bracket for tag"); + "Invalid test specifier: Expected an opening bracket (following whitespace or closing bracket) for a " + "test tag"); + mlib_check( + mstr_split_around(tag, mstr_cstring("]"), &tag, &tail), because, "Expected a closing bracket for test tag"); *mstr_vec_push(&test->tags) = mstr_copy(tag); } From b258f1649f75ca9994ca110bab369ced2a41c5b3 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Fri, 24 Oct 2025 09:58:52 -0600 Subject: [PATCH 32/49] Fix: Wrong argument when invoking exec() with mstr --- src/libmongoc/tests/TestSuite.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libmongoc/tests/TestSuite.c b/src/libmongoc/tests/TestSuite.c index f12c86b4e7e..82c2ed4b50d 100644 --- a/src/libmongoc/tests/TestSuite.c +++ b/src/libmongoc/tests/TestSuite.c @@ -485,14 +485,14 @@ TestSuite_RunFuncInChild(TestSuite *suite, /* IN */ dup2(pipefd[1], STDOUT_FILENO); close(pipefd[0]); close(pipefd[1]); - execle(suite->prgname, suite->prgname, "--no-fork", "--silent", "-l", test->name, (char *)0, envp); + execle(suite->prgname, suite->prgname, "--no-fork", "--silent", "-l", test->name.data, (char *)0, envp); } else { /* suppress child output */ fd = open("/dev/null", O_WRONLY); dup2(fd, STDOUT_FILENO); close(fd); - execl(suite->prgname, suite->prgname, "--no-fork", "-l", test->name, (char *)0); + execl(suite->prgname, suite->prgname, "--no-fork", "-l", test->name.data, (char *)0); } exit(-1); From 427ccd2de3e42731b5695da4b6643736eae3b1ce Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Fri, 24 Oct 2025 09:59:12 -0600 Subject: [PATCH 33/49] Clarify the purpose of JSON suite name matching --- src/libmongoc/tests/json-test.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libmongoc/tests/json-test.c b/src/libmongoc/tests/json-test.c index 366f3d757b3..72758c2f902 100644 --- a/src/libmongoc/tests/json-test.c +++ b/src/libmongoc/tests/json-test.c @@ -1889,7 +1889,9 @@ _install_json_test_suite_with_check(TestSuite *suite, const char *base, const ch // If we're running a specific test, only register if the directory we are scanning // is a prefix of the requested test pathname size_t where = mstr_find(suite->ctest_run, mstr_cstring(subdir)); - if (where != 0 && where != 1) { + // allow where == 0 or where == 1 to allow optional leading slash in the + // test specifier: + if (where > 1) { return; } } From d448c7ad1f3d1c7e1f93414840b74c0ae676ff2b Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Fri, 24 Oct 2025 10:04:25 -0600 Subject: [PATCH 34/49] Adjust position of lock:live-server for session tests --- .../tests/test-mongoc-client-session.c | 166 +++++++----------- 1 file changed, 60 insertions(+), 106 deletions(-) diff --git a/src/libmongoc/tests/test-mongoc-client-session.c b/src/libmongoc/tests/test-mongoc-client-session.c index 2835b7c3370..33efebeaba6 100644 --- a/src/libmongoc/tests/test-mongoc-client-session.c +++ b/src/libmongoc/tests/test-mongoc-client-session.c @@ -2394,7 +2394,7 @@ test_unacknowledged_explicit_cs_explicit_wc(void *ctx) session_test_helper_t *const helper = bson_malloc(sizeof(*helper)); \ *helper = (session_test_helper_t){.test_fn = (_test_fn)}; \ TestSuite_AddFull(_suite, \ - _name, \ + _name " [lock:live-server]", \ (_allow_read_concern) ? run_session_test : run_session_test_no_rc, \ &bson_free, \ helper, \ @@ -2408,7 +2408,7 @@ test_unacknowledged_explicit_cs_explicit_wc(void *ctx) session_test_helper_t *const helper = bson_malloc(sizeof(*helper)); \ *helper = (session_test_helper_t){.test_fn = (_test_fn)}; \ TestSuite_AddFull(_suite, \ - _name, \ + _name " [lock:live-server]", \ (_allow_read_concern) ? run_session_test : run_session_test_no_rc, \ &bson_free, \ helper, \ @@ -2423,7 +2423,7 @@ test_unacknowledged_explicit_cs_explicit_wc(void *ctx) session_test_helper_t *const helper = bson_malloc(sizeof(*helper)); \ *helper = (session_test_helper_t){.test_fn = (_test_fn)}; \ TestSuite_AddFull(_suite, \ - _name, \ + _name " [lock:live-server]", \ (_explicit_cs) ? (_inherit_wc ? test_unacknowledged_explicit_cs_inherit_wc \ : test_unacknowledged_implicit_cs_explicit_wc) \ : (_inherit_wc ? test_unacknowledged_implicit_cs_inherit_wc \ @@ -2727,37 +2727,36 @@ test_session_install(TestSuite *suite) /* "true" is for tests that expect readConcern: afterClusterTime for causally * consistent sessions, "false" is for tests that prohibit readConcern */ - add_session_test(suite, "/Session/cmd [lock:live-server]", test_cmd, false); - add_session_test(suite, "/Session/read_cmd [lock:live-server]", test_read_cmd, true); - add_session_test(suite, "/Session/write_cmd [lock:live-server]", test_write_cmd, false); - add_session_test(suite, "/Session/read_write_cmd [lock:live-server]", test_read_write_cmd, true); - add_session_test(suite, "/Session/db_cmd [lock:live-server]", test_db_cmd, false); - add_session_test(suite, "/Session/cursor [lock:live-server]", test_cursor, true); - add_session_test(suite, "/Session/drop [lock:live-server]", test_drop, false); - add_session_test(suite, "/Session/drop_index [lock:live-server]", test_drop_index, false); - add_session_test(suite, "/Session/create_index [lock:live-server]", test_create_index, false); - add_session_test(suite, "/Session/replace_one [lock:live-server]", test_replace_one, false); - add_session_test(suite, "/Session/update_one [lock:live-server]", test_update_one, false); - add_session_test(suite, "/Session/update_many [lock:live-server]", test_update_many, false); - add_session_test(suite, "/Session/insert_one [lock:live-server]", test_insert_one, false); - add_session_test(suite, "/Session/insert_many [lock:live-server]", test_insert_many, false); - add_session_test(suite, "/Session/delete_one [lock:live-server]", test_delete_one, false); - add_session_test(suite, "/Session/delete_many [lock:live-server]", test_delete_many, false); - add_session_test(suite, "/Session/rename [lock:live-server]", test_rename, false); - add_session_test(suite, "/Session/fam [lock:live-server]", test_fam, true); - add_session_test(suite, "/Session/db_drop [lock:live-server]", test_db_drop, false); - add_session_test(suite, "/Session/gridfs_find [lock:live-server]", test_gridfs_find, true); - add_session_test(suite, "/Session/gridfs_find_one [lock:live-server]", test_gridfs_find_one, true); - add_session_test_wc( - suite, "/Session/watch [lock:live-server]", test_watch, true, test_framework_skip_if_not_replset); - add_session_test(suite, "/Session/aggregate [lock:live-server]", test_aggregate, true); - add_session_test(suite, "/Session/create [lock:live-server]", test_create, false); - add_session_test(suite, "/Session/database_names [lock:live-server]", test_database_names, true); - add_session_test(suite, "/Session/find_databases [lock:live-server]", test_find_databases, true); - add_session_test(suite, "/Session/find_collections [lock:live-server]", test_find_collections, true); - add_session_test(suite, "/Session/collection_names [lock:live-server]", test_collection_names, true); - add_session_test(suite, "/Session/bulk [lock:live-server]", test_bulk, false); - add_session_test(suite, "/Session/find_indexes [lock:live-server]", test_find_indexes, true); + add_session_test(suite, "/Session/cmd", test_cmd, false); + add_session_test(suite, "/Session/read_cmd", test_read_cmd, true); + add_session_test(suite, "/Session/write_cmd", test_write_cmd, false); + add_session_test(suite, "/Session/read_write_cmd", test_read_write_cmd, true); + add_session_test(suite, "/Session/db_cmd", test_db_cmd, false); + add_session_test(suite, "/Session/cursor", test_cursor, true); + add_session_test(suite, "/Session/drop", test_drop, false); + add_session_test(suite, "/Session/drop_index", test_drop_index, false); + add_session_test(suite, "/Session/create_index", test_create_index, false); + add_session_test(suite, "/Session/replace_one", test_replace_one, false); + add_session_test(suite, "/Session/update_one", test_update_one, false); + add_session_test(suite, "/Session/update_many", test_update_many, false); + add_session_test(suite, "/Session/insert_one", test_insert_one, false); + add_session_test(suite, "/Session/insert_many", test_insert_many, false); + add_session_test(suite, "/Session/delete_one", test_delete_one, false); + add_session_test(suite, "/Session/delete_many", test_delete_many, false); + add_session_test(suite, "/Session/rename", test_rename, false); + add_session_test(suite, "/Session/fam", test_fam, true); + add_session_test(suite, "/Session/db_drop", test_db_drop, false); + add_session_test(suite, "/Session/gridfs_find", test_gridfs_find, true); + add_session_test(suite, "/Session/gridfs_find_one", test_gridfs_find_one, true); + add_session_test_wc(suite, "/Session/watch", test_watch, true, test_framework_skip_if_not_replset); + add_session_test(suite, "/Session/aggregate", test_aggregate, true); + add_session_test(suite, "/Session/create", test_create, false); + add_session_test(suite, "/Session/database_names", test_database_names, true); + add_session_test(suite, "/Session/find_databases", test_find_databases, true); + add_session_test(suite, "/Session/find_collections", test_find_collections, true); + add_session_test(suite, "/Session/collection_names", test_collection_names, true); + add_session_test(suite, "/Session/bulk", test_bulk, false); + add_session_test(suite, "/Session/find_indexes", test_find_indexes, true); { session_test_helper_t *const helper = bson_malloc(sizeof(*helper)); *helper = (session_test_helper_t){.test_fn = test_bulk_set_session}; @@ -2808,91 +2807,46 @@ test_session_install(TestSuite *suite) NULL, test_framework_skip_if_no_cluster_time, test_framework_skip_if_no_crypto); - add_unacknowledged_test(suite, - "/Session/unacknowledged/insert_one/explicit_cs/inherit_wc [lock:live-server]", - test_insert_one, - true, - true); - add_unacknowledged_test(suite, - "/Session/unacknowledged/insert_one/explicit_cs/explicit_wc [lock:live-server]", - test_insert_one, - true, - false); - add_unacknowledged_test(suite, - "/Session/unacknowledged/insert_one/implicit_cs/inherit_wc [lock:live-server]", - test_insert_one, - false, - true); - add_unacknowledged_test(suite, - "/Session/unacknowledged/insert_one/implicit_cs/explicit_wc [lock:live-server]", - test_insert_one, - false, - false); add_unacknowledged_test( - suite, "/Session/unacknowledged/bulk/explicit_cs/inherit_wc [lock:live-server]", test_bulk, true, true); + suite, "/Session/unacknowledged/insert_one/explicit_cs/inherit_wc", test_insert_one, true, true); add_unacknowledged_test( - suite, "/Session/unacknowledged/bulk/explicit_cs/explicit_wc [lock:live-server]", test_bulk, true, false); + suite, "/Session/unacknowledged/insert_one/explicit_cs/explicit_wc", test_insert_one, true, false); add_unacknowledged_test( - suite, "/Session/unacknowledged/bulk/implicit_cs/inherit_wc [lock:live-server]", test_bulk, false, true); + suite, "/Session/unacknowledged/insert_one/implicit_cs/inherit_wc", test_insert_one, false, true); add_unacknowledged_test( - suite, "/Session/unacknowledged/bulk/implicit_cs/explicit_wc [lock:live-server]", test_bulk, false, false); + suite, "/Session/unacknowledged/insert_one/implicit_cs/explicit_wc", test_insert_one, false, false); + add_unacknowledged_test(suite, "/Session/unacknowledged/bulk/explicit_cs/inherit_wc", test_bulk, true, true); + add_unacknowledged_test(suite, "/Session/unacknowledged/bulk/explicit_cs/explicit_wc", test_bulk, true, false); + add_unacknowledged_test(suite, "/Session/unacknowledged/bulk/implicit_cs/inherit_wc", test_bulk, false, true); + add_unacknowledged_test(suite, "/Session/unacknowledged/bulk/implicit_cs/explicit_wc", test_bulk, false, false); /* find_and_modify_with_opts only inherits acknowledged write concerns, so * skip tests that inherit a write concern. Technically, an explicit * unacknowledged write concern doesn't make much sense with findAndModify, * but this is testing the common code path for command execution. */ - add_unacknowledged_test(suite, - "/Session/unacknowledged/find_and_modify/explicit_cs/explicit_wc [lock:live-server]", - test_fam, - true, - false); - add_unacknowledged_test(suite, - "/Session/unacknowledged/find_and_modify/implicit_cs/explicit_wc [lock:live-server]", - test_fam, - false, - false); + add_unacknowledged_test( + suite, "/Session/unacknowledged/find_and_modify/explicit_cs/explicit_wc", test_fam, true, false); + add_unacknowledged_test( + suite, "/Session/unacknowledged/find_and_modify/implicit_cs/explicit_wc", test_fam, false, false); /* command_with_opts also does not inherit write concerns, but we still want * to test the common code path for command execution. */ + add_unacknowledged_test(suite, "/Session/unacknowledged/db_cmd/explicit_cs/explicit_wc", test_db_cmd, true, false); + add_unacknowledged_test(suite, "/Session/unacknowledged/db_cmd/implicit_cs/explicit_wc", test_db_cmd, false, false); + add_unacknowledged_test( + suite, "/Session/unacknowledged/read_write_cmd/explicit_cs/inherit_wc", test_read_write_cmd, true, true); + add_unacknowledged_test( + suite, "/Session/unacknowledged/read_write_cmd/explicit_cs/explicit_wc", test_read_write_cmd, true, false); + add_unacknowledged_test( + suite, "/Session/unacknowledged/read_write_cmd/implicit_cs/inherit_wc", test_read_write_cmd, false, true); + add_unacknowledged_test( + suite, "/Session/unacknowledged/read_write_cmd/implicit_cs/explicit_wc", test_read_write_cmd, false, false); + add_unacknowledged_test( + suite, "/Session/unacknowledged/write_cmd/explicit_cs/inherit_wc", test_write_cmd, true, true); add_unacknowledged_test( - suite, "/Session/unacknowledged/db_cmd/explicit_cs/explicit_wc [lock:live-server]", test_db_cmd, true, false); + suite, "/Session/unacknowledged/write_cmd/explicit_cs/explicit_wc", test_write_cmd, true, false); add_unacknowledged_test( - suite, "/Session/unacknowledged/db_cmd/implicit_cs/explicit_wc [lock:live-server]", test_db_cmd, false, false); - add_unacknowledged_test(suite, - "/Session/unacknowledged/read_write_cmd/explicit_cs/inherit_wc [lock:live-server]", - test_read_write_cmd, - true, - true); - add_unacknowledged_test(suite, - "/Session/unacknowledged/read_write_cmd/explicit_cs/explicit_wc [lock:live-server]", - test_read_write_cmd, - true, - false); - add_unacknowledged_test(suite, - "/Session/unacknowledged/read_write_cmd/implicit_cs/inherit_wc [lock:live-server]", - test_read_write_cmd, - false, - true); - add_unacknowledged_test(suite, - "/Session/unacknowledged/read_write_cmd/implicit_cs/explicit_wc [lock:live-server]", - test_read_write_cmd, - false, - false); + suite, "/Session/unacknowledged/write_cmd/implicit_cs/inherit_wc", test_write_cmd, false, true); add_unacknowledged_test( - suite, "/Session/unacknowledged/write_cmd/explicit_cs/inherit_wc [lock:live-server]", test_write_cmd, true, true); - add_unacknowledged_test(suite, - "/Session/unacknowledged/write_cmd/explicit_cs/explicit_wc [lock:live-server]", - test_write_cmd, - true, - false); - add_unacknowledged_test(suite, - "/Session/unacknowledged/write_cmd/implicit_cs/inherit_wc [lock:live-server]", - test_write_cmd, - false, - true); - add_unacknowledged_test(suite, - "/Session/unacknowledged/write_cmd/implicit_cs/explicit_wc [lock:live-server]", - test_write_cmd, - false, - false); + suite, "/Session/unacknowledged/write_cmd/implicit_cs/explicit_wc", test_write_cmd, false, false); install_json_test_suite_with_check( suite, JSON_DIR, "sessions/legacy", test_sessions_spec_cb, test_framework_skip_if_no_sessions); From 49189eebebbba6407f7a990fab5ce6454be091c5 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Fri, 24 Oct 2025 10:06:36 -0600 Subject: [PATCH 35/49] Remove extraneous [slow][json] test tags --- src/libmongoc/tests/json-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libmongoc/tests/json-test.c b/src/libmongoc/tests/json-test.c index 72758c2f902..46b41cd8b34 100644 --- a/src/libmongoc/tests/json-test.c +++ b/src/libmongoc/tests/json-test.c @@ -1943,7 +1943,7 @@ _install_json_test_suite_with_check(TestSuite *suite, const char *base, const ch test = modified; } mstr name = mstr_copy_cstring(skip_json); - mstr_append(&name, mstr_cstring(" [lock:live-server][json][slow]")); + mstr_append(&name, mstr_cstring(" [lock:live-server]")); /* list of "check" functions that decide whether to skip the test */ va_start(ap, callback); _V_TestSuite_AddFull(suite, name.data, callback, &bson_destroy_vp, test, ap); From bc96437fd5376bd95d2f5f3bfb11ca09fca98cd2 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Mon, 27 Oct 2025 09:23:45 -0600 Subject: [PATCH 36/49] Restore accidentally removed inclusion --- src/libmongoc/src/mongoc/mongoc-openssl-private.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libmongoc/src/mongoc/mongoc-openssl-private.h b/src/libmongoc/src/mongoc/mongoc-openssl-private.h index ccd1f3e230f..f4f41864aa4 100644 --- a/src/libmongoc/src/mongoc/mongoc-openssl-private.h +++ b/src/libmongoc/src/mongoc/mongoc-openssl-private.h @@ -21,6 +21,8 @@ #include +#include + #include #include From 6bdaa7a12d4625ef79db41aab65cff9b48c3ce23 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Mon, 27 Oct 2025 09:28:48 -0600 Subject: [PATCH 37/49] Switch extension: `.t.h` -> `.th` --- src/common/src/mlib/str_vec.h | 2 +- src/common/src/mlib/{vec.t.h => vec.th} | 4 +++- src/common/tests/test-mlib.c | 4 ++-- src/libmongoc/tests/TestSuite.h | 6 +++--- tools/format.py | 1 + 5 files changed, 10 insertions(+), 7 deletions(-) rename src/common/src/mlib/{vec.t.h => vec.th} (99%) diff --git a/src/common/src/mlib/str_vec.h b/src/common/src/mlib/str_vec.h index 38b6fedb6b7..8a69e6383e9 100644 --- a/src/common/src/mlib/str_vec.h +++ b/src/common/src/mlib/str_vec.h @@ -26,6 +26,6 @@ #define T mstr #define VecDestroyElement(Ptr) (mstr_destroy(Ptr)) #define VecCopyElement(Dst, Src) (*Dst = mstr_copy(*Src), Dst->data != NULL) -#include +#include #endif // MLIB_STR_VEC_H_INCLUDED diff --git a/src/common/src/mlib/vec.t.h b/src/common/src/mlib/vec.th similarity index 99% rename from src/common/src/mlib/vec.t.h rename to src/common/src/mlib/vec.th index 7715d130789..851c04f330a 100644 --- a/src/common/src/mlib/vec.t.h +++ b/src/common/src/mlib/vec.th @@ -1,5 +1,5 @@ /** - * @file vec.t.h + * @file vec.th * @brief Declare a new vector container data type * @date 2024-10-02 * @@ -448,3 +448,5 @@ _mlib_vec_index_adjust(size_t size, mlib_upsized_integer pos) // These ones we want to pop, not undefine: #pragma pop_macro("fn") #pragma pop_macro("vec_inline_spec") + +// vi: ft=c diff --git a/src/common/tests/test-mlib.c b/src/common/tests/test-mlib.c index 9b940fc84eb..f42cf329bd8 100644 --- a/src/common/tests/test-mlib.c +++ b/src/common/tests/test-mlib.c @@ -1229,7 +1229,7 @@ _test_timer(void) // Tests for `int_vec` assert the behavior of the vector type when handling trivial // elements. #define T int -#include +#include static void _test_int_vec(void) { @@ -1264,7 +1264,7 @@ _test_int_vec(void) #define VecName cstring_vec #define VecDestroyElement(CStrPtr) ((free(*CStrPtr), *CStrPtr = NULL)) #define VecCopyElement(Dst, Src) ((*Dst = strdup(*Src))) -#include +#include static void _test_cstring_vec(void) { diff --git a/src/libmongoc/tests/TestSuite.h b/src/libmongoc/tests/TestSuite.h index 6f351f290a0..ea2e1176c45 100644 --- a/src/libmongoc/tests/TestSuite.h +++ b/src/libmongoc/tests/TestSuite.h @@ -665,7 +665,7 @@ typedef struct TestSkip TestSkip; #define T CheckFunc #define VecName CheckFuncVec -#include +#include struct Test { /** @@ -716,7 +716,7 @@ Test_Destroy(Test *t) #define T Test #define VecName TestVec #define VecDestroyElement Test_Destroy -#include +#include /** * @brief Information about a test that we plan to skip @@ -747,7 +747,7 @@ TestSkip_Destroy(TestSkip *skip) #define T TestSkip #define VecName TestSkipVec #define VecDestroyElement(Skip) TestSkip_Destroy(Skip) -#include +#include struct TestSuite { diff --git a/tools/format.py b/tools/format.py index 4b2f75fff29..162697ce6e3 100644 --- a/tools/format.py +++ b/tools/format.py @@ -130,6 +130,7 @@ def main(argv: Sequence[str]) -> int: SOURCE_PATTERNS = [ '**/*.h', + '**/*.th', '**/*.hpp', '**/*.c', '**/*.cpp', From 3cbeb2967cbbc498a42c2b204df5b6d6d56abeac Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Mon, 27 Oct 2025 09:36:07 -0600 Subject: [PATCH 38/49] Fix missing extern_c around vec_index_adjust --- src/common/src/mlib/vec.th | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/common/src/mlib/vec.th b/src/common/src/mlib/vec.th index 851c04f330a..b6efdde11dd 100644 --- a/src/common/src/mlib/vec.th +++ b/src/common/src/mlib/vec.th @@ -415,16 +415,19 @@ fn(init_copy(VecName *dst_vec, VecName const *src_vec)) mlib_noexcept } #endif // VecCopyElement -mlib_extern_c_end(); - #ifndef mlib_vec_foreach #define mlib_vec_foreach(Type, VarName, Vector) \ for (Type *VarName = (Vector).data; VarName && (VarName != (Vector).data + (Vector).size); ++VarName) #endif #ifndef mlib_vec_at +/** + * @brief Obtain a vector element at some zero-based index offset, with negative index + * wrapping (-1 refers to the last element in the vector) + * + * @note The `Vec` argument will be evaluated at least twice! + */ #define mlib_vec_at(Vec, Pos) ((Vec).data[_mlib_vec_index_adjust((Vec).size, mlib_upsize_integer(Pos))]) - static inline size_t _mlib_vec_index_adjust(size_t size, mlib_upsized_integer pos) { @@ -434,9 +437,10 @@ _mlib_vec_index_adjust(size_t size, mlib_upsized_integer pos) mlib_check(pos.bits.as_unsigned, lte, size, because, "the vector index must be in-bounds for mlib_vec_at()"); return pos.bits.as_unsigned; } - #endif +mlib_extern_c_end(); + #undef T #undef VecName #undef VecDestroyElement From bdc88d48d43b909bbeaccfc2c00d4c6afab9182c Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Mon, 27 Oct 2025 11:04:53 -0600 Subject: [PATCH 39/49] PR feedback, change behavior of replace() on empty --- src/common/src/mlib/str.h | 69 +++++++++++++++++++++++------------- src/common/tests/test-mlib.c | 20 +++++------ 2 files changed, 54 insertions(+), 35 deletions(-) diff --git a/src/common/src/mlib/str.h b/src/common/src/mlib/str.h index 4c3d4d08ef0..63a3ec13743 100644 --- a/src/common/src/mlib/str.h +++ b/src/common/src/mlib/str.h @@ -31,10 +31,13 @@ #include #include -#include +#include // va_list #include #include #include +#include // vsnprintf +#include // malloc/free +#include // memcpy /** * @brief A simple non-owning string-view type. @@ -461,6 +464,21 @@ mstr_find_first_of(mstr_view hay, mstr_view const needles, mlib_upsized_integer #define _mstr_find_first_of_argc_3(Hay, Needle, Pos) _mstr_find_first_of_argc_4(Hay, Needle, Pos, SIZE_MAX) #define _mstr_find_first_of_argc_4(Hay, Needle, Pos, Len) mstr_find_first_of(Hay, Needle, mlib_upsize_integer(Pos), Len) +/** + * @brief Test whether the given codepoint is a Basic Latin whitespace character + * + * @param c The codepoint to be tested + */ +static inline bool +mlib_is_latin_whitespace(int32_t c) +{ + return c == 0x20 // space + || c == 0xa // line feed + || c == 0xd // carriage return + || c == 0x9 // horizontal tab + ; +} + /** * @brief Trim leading latin (ASCII) whitespace from the given string * @@ -470,13 +488,10 @@ mstr_find_first_of(mstr_view hay, mstr_view const needles, mlib_upsized_integer static inline mstr_view mstr_trim_left(mstr_view s) { - while (s.len) { - char c = mstr_at(s, 0); - if (c == ' ' || c == '\n' || c == '\r' || c == '\t') { - s = mstr_substr(s, 1); - } else { - break; - } + // Testing arbitrary code units for whitespace is safe as only 1-byte-encoded + // codepoints can land within the Basic Latin range: + while (s.len && mlib_is_latin_whitespace(mstr_at(s, 0))) { + s = mstr_substr(s, 1); } return s; } @@ -491,13 +506,8 @@ mstr_trim_left(mstr_view s) static inline mstr_view mstr_trim_right(mstr_view s) { - while (s.len) { - char c = mstr_at(s, -1); - if (c == ' ' || c == '\n' || c == '\r' || c == '\t') { - s = mstr_slice(s, 0, -1); - } else { - break; - } + while (s.len && mlib_is_latin_whitespace(mstr_at(s, -1))) { + s = mstr_slice(s, 0, -1); } return s; } @@ -719,7 +729,7 @@ mstr_resize_for_overwrite(mstr *const str, const size_t new_len) // Note: We do not initialize any of the data in the newly allocated region. // We only set the null terminator. It is up to the caller to do the rest of // the init. - data[new_len] = 0; + data[new_len] = '\0'; // Update the final object str->data = data; str->len = new_len; @@ -803,8 +813,7 @@ mstr_destroy(mstr *s) static inline mstr mstr_null(void) { - const mstr ret = {0}; - return ret; + return mlib_init(mstr){0}; } /** @@ -967,7 +976,7 @@ mstr_append(mstr *str, mstr_view suffix) * In case of failure, the string is not modified. */ static inline bool -mstr_pushchar(mstr *str, char c) +mstr_append_char(mstr *str, char c) { mstr_view one = mstr_view_data(&c, 1); return mstr_append(str, one); @@ -982,22 +991,22 @@ mstr_pushchar(mstr *str, char c) * @return true If the operation succeeds * @return false Otherwise * - * @warning If the `needle` string is empty, then the program will terminate! + * @warning If the `needle` string is empty, then the substitution string will be inserted + * around and between every existing character in the string. * @note If the operation fails, the content of `str` is an unspecified but valid * string. */ static inline bool mstr_replace(mstr *str, mstr_view needle, mstr_view sub) { - mlib_check(needle.len, neq, 0, because, "Trying to replace an empty string will result in an infinite loop"); // Scan forward, starting from the first position: size_t off = 0; - while (1) { + while (off <= str->len) { // Find the next occurrence, starting from the scan offset off = mstr_find(*str, needle, off); if (off == SIZE_MAX) { // No more occurrences. - return true; + break; } // Replace the needle string with the new value if (!mstr_splice(str, off, needle.len, sub)) { @@ -1010,7 +1019,16 @@ mstr_replace(mstr *str, mstr_view needle, mstr_view sub) // Integer overflow while advancing the offset. No good. return false; } + // Note: To support empty needles, advance one more space to avoid infinite + // repititions in-place. + // TODO: To do this properly, this should instead advance over a full UTF-8-encoded + // codepoint. For now, just do a single byte. + if (!needle.len && mlib_add(&off, 1)) { + // Advancing the extra distance failed + return false; + } } + return true; } /** @@ -1026,12 +1044,13 @@ mstr_vsprintf_append(mstr *string, const char *format, va_list args) const size_t start_size = string->len; // Try to guess the length of the added string - size_t added_len = strlen(format); + const size_t format_len = strlen(format); + size_t added_len = format_len; // Give use some wiggle room since we are inserting characters: if (mlib_mul(&added_len, 2)) { // Doubling the size overflowed. Not likely, but just use the original string // size and grow as-needed. - added_len = strlen(format); + added_len = format_len; } while (1) { diff --git a/src/common/tests/test-mlib.c b/src/common/tests/test-mlib.c index f42cf329bd8..0829c806394 100644 --- a/src/common/tests/test-mlib.c +++ b/src/common/tests/test-mlib.c @@ -963,9 +963,9 @@ _test_str(void) // Append individual characters { mstr s = mstr_new(0); - mstr_pushchar(&s, 'f'); - mstr_pushchar(&s, 'o'); - mstr_pushchar(&s, 'o'); + mstr_append_char(&s, 'f'); + mstr_append_char(&s, 'o'); + mstr_append_char(&s, 'o'); mlib_check(mstr_cmp(s, ==, mstr_cstring("foo"))); mstr_destroy(&s); } @@ -1000,14 +1000,14 @@ _test_str(void) // within the already-replaced content: mlib_check(s.data, str_eq, "foo foo bar baz baz"); - // Try to replace, where the needle is an empty string. This just produces a repetition of the needle + // Try to replace, where the needle is an empty string. mstr_assign(&s, mstr_cstring("foo")); - mlib_assert_aborts () { - // A naive replacement of an empty string will result in an infinite string - // as it keeps matching the empty string forever, so we terminate rather than - // allocate forever: - mlib_check(mstr_replace(&s, mstr_cstring(""), mstr_cstring("a"))); - } + mlib_check(mstr_replace(&s, mstr_cstring(""), mstr_cstring("bar"))); + mlib_check(s.data, str_eq, "barfbarobarobar"); + + mstr_assign(&s, mstr_cstring("foofoofoo")); + mlib_check(mstr_replace(&s, mstr_cstring("foo"), mstr_cstring("bar"))); + mlib_check(s.data, str_eq, "barbarbar"); mstr_destroy(&s); } From 6c58ddc982279125df415d3a22a144de18a8758c Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Mon, 27 Oct 2025 11:15:52 -0600 Subject: [PATCH 40/49] Add "unlikely" annotations around direct overflow checks --- src/common/src/mlib/config.h | 11 +++++++++++ src/common/src/mlib/str.h | 14 +++++++------- src/common/src/mlib/vec.th | 4 ++-- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/common/src/mlib/config.h b/src/common/src/mlib/config.h index 0cd2292d17a..74c67e2bd68 100644 --- a/src/common/src/mlib/config.h +++ b/src/common/src/mlib/config.h @@ -397,4 +397,15 @@ MLIB_IF_GNU_LIKE(mlib_gnu_warning_disable("-Wunused-parameter");) \ MLIB_IF_MSVC(mlib_msvc_warning(disable : 4100);) mlib_static_assert(1, "") +/** + * @brief Annotate a boolean expression as "likely to be true" to guide the optimizer. + * Use this very sparingly. + */ +#define mlib_likely(...) MLIB_IF_ELSE(mlib_is_gnu_like())(__builtin_expect(!!(__VA_ARGS__), 1))((__VA_ARGS__)) +/** + * @brief Annotate a boolean expression as "likely to be untrue" to guide the optimizer. + * Use this very sparingly. + */ +#define mlib_unlikely(...) MLIB_IF_ELSE(mlib_is_gnu_like())(__builtin_expect(!!(__VA_ARGS__), 0))((__VA_ARGS__)) + #endif // MLIB_CONFIG_H_INCLUDED diff --git a/src/common/src/mlib/str.h b/src/common/src/mlib/str.h index 63a3ec13743..9815983b596 100644 --- a/src/common/src/mlib/str.h +++ b/src/common/src/mlib/str.h @@ -716,7 +716,7 @@ mstr_resize_for_overwrite(mstr *const str, const size_t new_len) { // We need to allocate one additional char to hold the null terminator size_t alloc_size = new_len; - if (mlib_add(&alloc_size, 1) || alloc_size > PTRDIFF_MAX) { + if (mlib_unlikely(mlib_add(&alloc_size, 1) || alloc_size > PTRDIFF_MAX)) { // Allocation size is too large return false; } @@ -868,7 +868,7 @@ mstr_concat(mstr_view a, mstr_view b) { mstr ret = {NULL, 0}; size_t cat_len = 0; - if (mlib_add(&cat_len, a.len, b.len)) { + if (mlib_unlikely(mlib_add(&cat_len, a.len, b.len))) { // Size would overflow. No go. return ret; } @@ -914,7 +914,7 @@ mstr_splice(mstr *str, size_t splice_pos, size_t n_delete, mstr_view insert) // This should never fail, because we should try to delete more chars than we have mlib_check(!mlib_sub(&new_len, n_delete)); // Check if appending would make too big of a string - if (mlib_add(&new_len, insert.len)) { + if (mlib_unlikely(mlib_add(&new_len, insert.len))) { // New string will be too long return false; } @@ -1015,7 +1015,7 @@ mstr_replace(mstr *str, mstr_view needle, mstr_view sub) // Advance over the length of the replacement string, so we don't try to // infinitely replace content if the replacement itself contains the needle // string - if (mlib_add(&off, sub.len)) { + if (mlib_unlikely(mlib_add(&off, sub.len))) { // Integer overflow while advancing the offset. No good. return false; } @@ -1023,7 +1023,7 @@ mstr_replace(mstr *str, mstr_view needle, mstr_view sub) // repititions in-place. // TODO: To do this properly, this should instead advance over a full UTF-8-encoded // codepoint. For now, just do a single byte. - if (!needle.len && mlib_add(&off, 1)) { + if (!needle.len && mlib_unlikely(mlib_add(&off, 1))) { // Advancing the extra distance failed return false; } @@ -1056,7 +1056,7 @@ mstr_vsprintf_append(mstr *string, const char *format, va_list args) while (1) { // Calculate the new size of the string size_t new_size = 0; - if (mlib_add(&new_size, start_size, added_len)) { + if (mlib_unlikely(mlib_add(&new_size, start_size, added_len))) { // Adding more space overflows return false; } @@ -1067,7 +1067,7 @@ mstr_vsprintf_append(mstr *string, const char *format, va_list args) } // Calc the size with the null terminator size_t len_with_null = added_len; - if (mlib_add(&len_with_null, 1)) { + if (mlib_unlikely(mlib_add(&len_with_null, 1))) { // Unlikely: Overflow return false; } diff --git a/src/common/src/mlib/vec.th b/src/common/src/mlib/vec.th index b6efdde11dd..d957465f533 100644 --- a/src/common/src/mlib/vec.th +++ b/src/common/src/mlib/vec.th @@ -277,7 +277,7 @@ fn(resize(VecName *const self, size_t const count)) mlib_noexcept // Try to auto-grow capacity. Increase capacity by ×1.5 const size_t half_current_capacity = self->capacity / 2; size_t new_capacity = 0; - if (mlib_add(&new_capacity, self->size, half_current_capacity)) { + if (mlib_unlikely(mlib_add(&new_capacity, self->size, half_current_capacity))) { // The auto growth amount would overflow, so just cap to the max size. new_capacity = fn(max_size()); } @@ -320,7 +320,7 @@ vec_inline_spec T * fn(push(VecName *self)) mlib_noexcept { size_t count = self->size; - if (mlib_add(&count, 1)) { + if (mlib_unlikely(mlib_add(&count, 1))) { // Adding another element would overflow size_t. This is extremely unlikely, // but precautionary. return NULL; From ae5bd529959bcaa0dd0681ff6f847f6213ce20b5 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Mon, 27 Oct 2025 11:19:53 -0600 Subject: [PATCH 41/49] Conditionally select the appropriate printf() attribute --- src/common/src/mlib/config.h | 8 ++++++++ src/common/src/mlib/str.h | 12 +++--------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/common/src/mlib/config.h b/src/common/src/mlib/config.h index 74c67e2bd68..a95f917efb8 100644 --- a/src/common/src/mlib/config.h +++ b/src/common/src/mlib/config.h @@ -397,6 +397,14 @@ MLIB_IF_GNU_LIKE(mlib_gnu_warning_disable("-Wunused-parameter");) \ MLIB_IF_MSVC(mlib_msvc_warning(disable : 4100);) mlib_static_assert(1, "") +#if mlib_is_clang() +#define mlib_printf_attribute(f, v) __attribute__((format(printf, f, v))) +#elif mlib_is_gcc() +#define mlib_printf_attribute(f, v) __attribute__((format(gnu_printf, f, v))) +#else +#define mlib_printf_attribute(f, v) +#endif + /** * @brief Annotate a boolean expression as "likely to be true" to guide the optimizer. * Use this very sparingly. diff --git a/src/common/src/mlib/str.h b/src/common/src/mlib/str.h index 9815983b596..4a710a58e4f 100644 --- a/src/common/src/mlib/str.h +++ b/src/common/src/mlib/str.h @@ -1034,9 +1034,7 @@ mstr_replace(mstr *str, mstr_view needle, mstr_view sub) /** * @brief Like `mstr_sprintf_append`, but accepts the va_list directly. */ -MLIB_IF_GNU_LIKE(__attribute__((format(printf, 2, 0)))) -static inline bool -mstr_vsprintf_append(mstr *string, const char *format, va_list args) +mlib_printf_attribute(2, 0) static inline bool mstr_vsprintf_append(mstr *string, const char *format, va_list args) { mlib_check(string != NULL, because, "Output string parameter is required"); // The size of the string before we appended any characters to it. This is also @@ -1112,9 +1110,7 @@ mstr_vsprintf_append(mstr *string, const char *format, va_list args) * This function maintains the existing content of `string` and only inserts * additional characters at the end of the string. */ -MLIB_IF_GNU_LIKE(__attribute__((format(printf, 2, 3)))) -static inline bool -mstr_sprintf_append(mstr *string, const char *format, ...) +mlib_printf_attribute(2, 3) static inline bool mstr_sprintf_append(mstr *string, const char *format, ...) { va_list args; va_start(args, format); @@ -1129,9 +1125,7 @@ mstr_sprintf_append(mstr *string, const char *format, ...) * @param f The format string to be used. * @param ... The formatting arguments to interpolate into the string */ -MLIB_IF_GNU_LIKE(__attribute__((format(printf, 1, 2)))) -static inline mstr -mstr_sprintf(const char *f, ...) +mlib_printf_attribute(1, 2) static inline mstr mstr_sprintf(const char *f, ...) { mstr ret = mstr_null(); va_list args; From 6f417034d6de59c6ba066d27abae7e496f04bdca Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Mon, 27 Oct 2025 11:22:04 -0600 Subject: [PATCH 42/49] More PR tweaks --- src/common/src/mlib/vec.th | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/common/src/mlib/vec.th b/src/common/src/mlib/vec.th index d957465f533..bbe0a0928d8 100644 --- a/src/common/src/mlib/vec.th +++ b/src/common/src/mlib/vec.th @@ -93,19 +93,19 @@ typedef struct VecName { * @brief Pointer to the first vector element, or NULL if the vector is * empty. * - * @note DO NOT MODIFY + * @note DO NOT DIRECTLY MODIFY THIS VALUE */ T *data; /** * @brief The number of elements in the vector. * - * @note DO NOT MODIFY + * @note DO NOT DIRECTLY MODIFY THIS VALUE */ size_t size; /** * @brief The number of allocated storage elements. * - * @note DO NOT MODIFY + * @note DO NOT DIRECTLY MODIFY THIS VALUE */ size_t capacity; @@ -168,10 +168,10 @@ fn(cend(VecName const *v)) mlib_noexcept vec_inline_spec size_t fn(max_size(void)) mlib_noexcept { - // We compare against (signed) SSIZE_MAX because want to support the difference + // We compare against (signed) PTRDIFF_MAX because want to support the difference // between two pointers. If we use the unsigned size, then we could have vectors // with size that is too large to represent the difference between two sizes. - return SSIZE_MAX / sizeof(T); + return PTRDIFF_MAX / sizeof(T); } /** From efa5055557f72a2d8bec68ae867fbcfd6843bb63 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Mon, 27 Oct 2025 12:13:08 -0600 Subject: [PATCH 43/49] Fix: Not adjusting elements after erasure --- src/common/src/mlib/vec.th | 10 ++++++++++ src/common/tests/test-mlib.c | 6 ++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/common/src/mlib/vec.th b/src/common/src/mlib/vec.th index bbe0a0928d8..453b80c9fbe 100644 --- a/src/common/src/mlib/vec.th +++ b/src/common/src/mlib/vec.th @@ -235,10 +235,20 @@ fn(reserve(VecName *const self, size_t count)) mlib_noexcept vec_inline_spec void fn(erase(VecName *const self, T *const first, T *const last)) { + // Number of elements following the removed region + const size_t n_tail_elements = (size_t)(fn(end(self)) - last); + // Destroy elements in reverse order: for (T *r_iter = last; r_iter != first; --r_iter) { VecDestroyElement((r_iter - 1)); --self->size; } + // This mult cannot overflow because we can never contain enough elements to overflow PTRDIFF_MAX + const size_t n_bytes_to_shift = n_tail_elements * sizeof(T); + // If there are any elements to be shifted, shift them down over the removed region + if (n_bytes_to_shift) { + // Shift all tail elements down into their new position + memmove(first, last, n_bytes_to_shift); + } } /** diff --git a/src/common/tests/test-mlib.c b/src/common/tests/test-mlib.c index 0829c806394..d96085f7a75 100644 --- a/src/common/tests/test-mlib.c +++ b/src/common/tests/test-mlib.c @@ -1251,11 +1251,13 @@ _test_int_vec(void) *int_vec_push(&ints) = 123456; *int_vec_push(&ints) = -7; mlib_check(ints.size, eq, 4, because, "We added four elements from empty"); + mlib_check(mlib_vec_at(ints, -1), eq, -7, because, "Negative index wraps"); + // Erase in the middle int_vec_erase(&ints, ints.data + 1, ints.data + 3); mlib_check(ints.size, eq, 2, because, "We erased two elements"); - - mlib_check(mlib_vec_at(ints, -1), eq, 1729, because, "Negative index wraps"); + mlib_check(mlib_vec_at(ints, 1), eq, -7, because, "We erased, so the back elements shifted down"); + mlib_check(mlib_vec_at(ints, -1), eq, -7, because, "-7 is still the last element"); int_vec_destroy(&ints); } From ccb2521128f4398e60132c22242a22a10e5ad7b0 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Tue, 28 Oct 2025 16:01:23 -0600 Subject: [PATCH 44/49] Minor tweaks from PR feedback --- src/common/src/mlib/str.h | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/common/src/mlib/str.h b/src/common/src/mlib/str.h index 4a710a58e4f..f80c7d1c8b3 100644 --- a/src/common/src/mlib/str.h +++ b/src/common/src/mlib/str.h @@ -467,16 +467,23 @@ mstr_find_first_of(mstr_view hay, mstr_view const needles, mlib_upsized_integer /** * @brief Test whether the given codepoint is a Basic Latin whitespace character * + * This function does not depend on the locale and has no undefined behavior, unlike functions + * * @param c The codepoint to be tested */ static inline bool mlib_is_latin_whitespace(int32_t c) { - return c == 0x20 // space - || c == 0xa // line feed - || c == 0xd // carriage return - || c == 0x9 // horizontal tab - ; + switch (c) { + case 0x09: // horizontal tab + case 0x0a: // line feed + case 0x0d: // carriage return + case 0x20: // space + return true; + + default: + return false; + } } /** @@ -991,8 +998,11 @@ mstr_append_char(mstr *str, char c) * @return true If the operation succeeds * @return false Otherwise * - * @warning If the `needle` string is empty, then the substitution string will be inserted - * around and between every existing character in the string. + * @note If the `needle` string is empty, then the substitution string will + * be inserted around and between every byte in the string: + * + * replace("foo", "", "|") -> "|f|o|o|" + * * @note If the operation fails, the content of `str` is an unspecified but valid * string. */ @@ -1021,7 +1031,7 @@ mstr_replace(mstr *str, mstr_view needle, mstr_view sub) } // Note: To support empty needles, advance one more space to avoid infinite // repititions in-place. - // TODO: To do this properly, this should instead advance over a full UTF-8-encoded + // TODO: To do this "properly", this should instead advance over a full UTF-8-encoded // codepoint. For now, just do a single byte. if (!needle.len && mlib_unlikely(mlib_add(&off, 1))) { // Advancing the extra distance failed From 445b8c52f652ff2682fa2aefc6c534c3ecbe341c Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Thu, 30 Oct 2025 18:01:55 -0600 Subject: [PATCH 45/49] Fix mstr APIs to support self-assign/self-insert --- src/common/src/mlib/str.h | 203 +++++++++++++++++++++++------------ src/common/tests/test-mlib.c | 25 +++++ 2 files changed, 162 insertions(+), 66 deletions(-) diff --git a/src/common/src/mlib/str.h b/src/common/src/mlib/str.h index f80c7d1c8b3..ac69d8160a2 100644 --- a/src/common/src/mlib/str.h +++ b/src/common/src/mlib/str.h @@ -823,6 +823,22 @@ mstr_null(void) return mlib_init(mstr){0}; } +/** + * @internal + * @brief Test whether the given string-view is a view within the given owning string + */ +static inline bool +_mstr_overlaps(mstr const *str, mstr_view sv) +{ + // Note: Pointer-comparison between objects is unspecified, but is guaranteed + // to returns `true` if there is overlap. We're okay with false-positive overlaps. + // Additionally, POSIX and Win32 both offer stronger guarantees about pointer + // comparison, which we can rely on here. + return str->data // + && str->data <= sv.data // + && sv.data <= str->data + str->len; +} + /** * @brief Replace the content of the given string, attempting to reuse the buffer * @@ -836,6 +852,18 @@ mstr_null(void) static inline bool mstr_assign(mstr *inout, mstr_view s) { + // Check for self-assignment + if (_mstr_overlaps(inout, s)) { + // We are overwriting a string with a (sub)string of its own content. + // Move the substring to the front of the string (may be a no-op if `s` + // points to the beginning of the string) + memmove(inout->data, s.data, s.len); + // Resize to truncate. This will always shrink the string, because a valid + // string-view into `inout` cannot be longer than `inout` itself. Thus, it + // also cannot fail. + mstr_resize_for_overwrite(inout, s.len); + return true; + } if (!mstr_resize_for_overwrite(inout, s.len)) { return false; } @@ -912,6 +940,22 @@ mstr_concat(mstr_view a, mstr_view b) static inline bool mstr_splice(mstr *str, size_t splice_pos, size_t n_delete, mstr_view insert) { + // Guard against self-insertion: + if (insert.data && _mstr_overlaps(str, insert)) { + // The insertion string exists within the current string. We cannot modify it in-place. + // Duplicate the insertion string to remain pristine while we splice: + mstr insert_dup = mstr_copy(insert); + if (!insert_dup.data) { + // Failed to dup the insert string. Failure to splice + return false; + } + // Do the splice, now using the copy of the insertion string + const bool ok = mstr_splice(str, splice_pos, n_delete, mstr_view_from(insert_dup)); + // We're done with the dup + mstr_destroy(&insert_dup); + // Return the sub-result + return ok; + } mlib_check(splice_pos <= str->len); // How many chars is it possible to delete from `splice_pos`? size_t n_chars_avail_to_delete = str->len - splice_pos; @@ -949,8 +993,10 @@ mstr_splice(mstr *str, size_t splice_pos, size_t n_delete, mstr_view insert) mlib_check(mstr_resize_for_overwrite(str, new_len)); mut = str->data + splice_pos; } - // Insert the new data - memcpy(mut, insert.data, insert.len); + // Insert the new data if the insertion string is non-null + if (insert.data) { + memcpy(mut, insert.data, insert.len); + } return true; } @@ -1003,15 +1049,37 @@ mstr_append_char(mstr *str, char c) * * replace("foo", "", "|") -> "|f|o|o|" * + * @note The operation is guaranteed to never fail if the `sub` string is not + * longer than the `needle` string AND the needle and sub strings do not overlap + * * @note If the operation fails, the content of `str` is an unspecified but valid * string. */ static inline bool mstr_replace(mstr *str, mstr_view needle, mstr_view sub) { + bool okay = true; + // We may dup the needle/sub if they overlap the output string + mstr needle_dup = mstr_null(); + mstr sub_dup = mstr_null(); + // Check if the needle is a substring of the target: + if (_mstr_overlaps(str, needle)) { + // Copy the needle string + needle_dup = mstr_copy(needle); + // Detect allocation failure: + okay = !!needle_dup.data; + // Update the needle to point to the duplicate: + needle = mstr_view_from(needle_dup); + } + // Do the same with the sub string: + if (okay && _mstr_overlaps(str, sub)) { + sub_dup = mstr_copy(sub); + okay = !!needle_dup.data; + sub = mstr_view_from(sub_dup); + } // Scan forward, starting from the first position: size_t off = 0; - while (off <= str->len) { + while (okay && off <= str->len) { // Find the next occurrence, starting from the scan offset off = mstr_find(*str, needle, off); if (off == SIZE_MAX) { @@ -1020,14 +1088,14 @@ mstr_replace(mstr *str, mstr_view needle, mstr_view sub) } // Replace the needle string with the new value if (!mstr_splice(str, off, needle.len, sub)) { - return false; + okay = false; } // Advance over the length of the replacement string, so we don't try to // infinitely replace content if the replacement itself contains the needle // string if (mlib_unlikely(mlib_add(&off, sub.len))) { // Integer overflow while advancing the offset. No good. - return false; + okay = false; } // Note: To support empty needles, advance one more space to avoid infinite // repititions in-place. @@ -1035,75 +1103,97 @@ mstr_replace(mstr *str, mstr_view needle, mstr_view sub) // codepoint. For now, just do a single byte. if (!needle.len && mlib_unlikely(mlib_add(&off, 1))) { // Advancing the extra distance failed - return false; + okay = false; } } - return true; + // Destroy the needle/sub strings, which we may have duplicated if they overlapped + // the target. If not, then these are a no-op. + mstr_destroy(&needle_dup); + mstr_destroy(&sub_dup); + return okay; } /** - * @brief Like `mstr_sprintf_append`, but accepts the va_list directly. + * @brief Like `mstr_snprintf`, but accepts a `va_list` directly. */ -mlib_printf_attribute(2, 0) static inline bool mstr_vsprintf_append(mstr *string, const char *format, va_list args) +mlib_printf_attribute(1, 0) static inline mstr mstr_vsnprintf(const char *format, va_list args) { - mlib_check(string != NULL, because, "Output string parameter is required"); - // The size of the string before we appended any characters to it. This is also - // the zero-based offset where we will be inserting new characters. - const size_t start_size = string->len; - - // Try to guess the length of the added string - const size_t format_len = strlen(format); - size_t added_len = format_len; - // Give use some wiggle room since we are inserting characters: - if (mlib_mul(&added_len, 2)) { - // Doubling the size overflowed. Not likely, but just use the original string - // size and grow as-needed. - added_len = format_len; + size_t format_strlen = strlen(format); + size_t sz = format_strlen; + if (mlib_unlikely(mlib_mul(&sz, 2))) { + // Overflow on multiply. Oof + sz = format_strlen; } + mstr ret = mstr_null(); while (1) { - // Calculate the new size of the string - size_t new_size = 0; - if (mlib_unlikely(mlib_add(&new_size, start_size, added_len))) { - // Adding more space overflows - return false; - } - // Try to make room - if (!mstr_resize(string, new_size)) { - // Failed to allocate the new region. - return false; + // Resize to make room for the formatted text + if (!mstr_resize(&ret, sz)) { + // Allocation failure + break; } + // Calc the size with the null terminator - size_t len_with_null = added_len; + size_t len_with_null = ret.len; if (mlib_unlikely(mlib_add(&len_with_null, 1))) { // Unlikely: Overflow - return false; + break; } // Do the formatting va_list dup_args; va_copy(dup_args, args); - int n_chars = vsnprintf(string->data + start_size, len_with_null, format, dup_args); + int n_chars = vsnprintf(ret.data, len_with_null, format, dup_args); va_end(dup_args); // On error, returns a negative value if (n_chars < 0) { - return false; + break; } - // Upon success, vsnprintf returns the number of chars that were written, at most - // as many chars as are available - if ((size_t)n_chars <= added_len) { - // Success. This add+resize never fails - mlib_check(!mlib_add(&new_size, start_size, (size_t)n_chars)); - mlib_check(mstr_resize(string, new_size)); - return true; + if ((size_t)n_chars <= ret.len) { + // Success. Truncate to the number of chars actually written: + mstr_resize(&ret, (size_t)n_chars); + // Return the successfully formatted string: + return ret; } - // Otherwise, returns the number of chars that would have been written. Update - // the number of chars that we expect to insert, and then try again. - added_len = (size_t)n_chars; + // Need more room. Resize and try again: + sz = (size_t)n_chars; + continue; } + + // Only reached if the operation failed + mstr_destroy(&ret); + return ret; +} + +/** + * @brief Format a string according to `printf` rules + * + * @param f The format string to be used. + * @param ... The formatting arguments to interpolate into the string + * @return mstr A new mstr upon success, or a null mstr upon failure. + */ +mlib_printf_attribute(1, 2) static inline mstr mstr_sprintf(const char *f, ...) +{ + va_list args; + va_start(args, f); + mstr ret = mstr_vsnprintf(f, args); + va_end(args); + return ret; +} + +/** + * @brief Like `mstr_sprintf_append`, but accepts the va_list directly. + */ +mlib_printf_attribute(2, 0) static inline bool mstr_vsprintf_append(mstr *string, const char *format, va_list args) +{ + mlib_check(string != NULL, because, "Output string parameter is required"); + mstr suffix = mstr_vsnprintf(format, args); + bool ok = mstr_append(string, suffix); + mstr_destroy(&suffix); + return ok; } /** @@ -1111,7 +1201,7 @@ mlib_printf_attribute(2, 0) static inline bool mstr_vsprintf_append(mstr *string * * @param string Pointer to a valid or null string object which will be modified * @param format A printf-style format string to append onto `string` - * @param args The interpolation arguments for `format` + * @param ... The interpolation arguments for `format` * * @retval true If-and-only-if the string is successfully modified * @retval false If there was an error during formatting. The content of `string` @@ -1129,24 +1219,5 @@ mlib_printf_attribute(2, 3) static inline bool mstr_sprintf_append(mstr *string, return okay; } -/** - * @brief Format a string according to `printf` rules - * - * @param f The format string to be used. - * @param ... The formatting arguments to interpolate into the string - */ -mlib_printf_attribute(1, 2) static inline mstr mstr_sprintf(const char *f, ...) -{ - mstr ret = mstr_null(); - va_list args; - va_start(args, f); - const bool okay = mstr_vsprintf_append(&ret, f, args); - va_end(args); - if (!okay) { - mstr_destroy(&ret); - return ret; - } - return ret; -} #endif // MLIB_STR_H_INCLUDED diff --git a/src/common/tests/test-mlib.c b/src/common/tests/test-mlib.c index d96085f7a75..b0e8816125a 100644 --- a/src/common/tests/test-mlib.c +++ b/src/common/tests/test-mlib.c @@ -1011,6 +1011,26 @@ _test_str(void) mstr_destroy(&s); } + + // Self-assignment/insertion + { + // Use `assign()` over itself + mstr s = mstr_copy_cstring("foo bar baz"); + mstr_assign(&s, s); + mlib_check(s.data, str_eq, "foo bar baz"); + + // Use splice() where the insertion string overlaps the target + mstr_view bar = mstr_substr(s, 4, 3); + mlib_check(mstr_splice(&s, 0, 3, bar)); + mlib_check(mstr_splice(&s, 8, 3, bar)); + mlib_check(s.data, str_eq, "bar bar bar"); + + // Use replace() with a needle that points into the target + mstr_replace(&s, bar, mstr_cstring("foo")); + mlib_check(s.data, str_eq, "foo foo foo"); + + mstr_destroy(&s); + } } static void @@ -1023,6 +1043,11 @@ _test_str_format(void) mlib_check(s.data, str_eq, "foo bar"); mstr_sprintf_append(&s, " %d", 123); mlib_check(s.data, str_eq, "foo bar 123"); + + // Attempt to format into itself: + mstr_sprintf_append(&s, " hey %s", s.data); + mlib_check(s.data, str_eq, "foo bar 123 hey foo bar 123"); + mstr_destroy(&s); } From ec375240a6e7e0fe9cd20c6006174ce538fc9936 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Thu, 30 Oct 2025 18:04:40 -0600 Subject: [PATCH 46/49] mstr splice clamps deletion count --- src/common/src/mlib/str.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/common/src/mlib/str.h b/src/common/src/mlib/str.h index ac69d8160a2..18308098b21 100644 --- a/src/common/src/mlib/str.h +++ b/src/common/src/mlib/str.h @@ -959,10 +959,13 @@ mstr_splice(mstr *str, size_t splice_pos, size_t n_delete, mstr_view insert) mlib_check(splice_pos <= str->len); // How many chars is it possible to delete from `splice_pos`? size_t n_chars_avail_to_delete = str->len - splice_pos; - mlib_check(n_delete <= n_chars_avail_to_delete); + // Clamp to the number of chars available for deletion: + if (n_delete > n_chars_avail_to_delete) { + n_delete = n_chars_avail_to_delete; + } // Compute the new string length size_t new_len = str->len; - // This should never fail, because we should try to delete more chars than we have + // This should never fail, because we should never try to delete more chars than we have mlib_check(!mlib_sub(&new_len, n_delete)); // Check if appending would make too big of a string if (mlib_unlikely(mlib_add(&new_len, insert.len))) { From d561be5e0030ddc64a3538804b8e7fdbaa2a3081 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Thu, 30 Oct 2025 18:06:22 -0600 Subject: [PATCH 47/49] spelling --- src/common/src/mlib/str.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/src/mlib/str.h b/src/common/src/mlib/str.h index 18308098b21..153e408646f 100644 --- a/src/common/src/mlib/str.h +++ b/src/common/src/mlib/str.h @@ -701,7 +701,7 @@ typedef struct mstr { /** * @brief Resize an existing or null `mstr`, without initializing any of the - * added content other than the null terminator. This operation is potientially + * added content other than the null terminator. This operation is potentially * UNSAFE, because it gives uninitialized memory to the caller. * * @param str Pointer to a valid `mstr`, or a null `mstr`. From 0d9efd44c6a76dfedb7c29a222640c89dc2a35e4 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Thu, 30 Oct 2025 18:07:06 -0600 Subject: [PATCH 48/49] Plural "locks" --- build/cmake/LoadTests.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/cmake/LoadTests.cmake b/build/cmake/LoadTests.cmake index 9739e0fc49b..7aa9c4203bf 100644 --- a/build/cmake/LoadTests.cmake +++ b/build/cmake/LoadTests.cmake @@ -56,7 +56,7 @@ foreach(casename IN LISTS MONGOC_TESTS) list_select(labels SELECT "^uses:(.*)$" REPLACE "mongoc/fixtures/\\1" OUT fixtures) # For any "lock:..." labels, add a resource lock with the corresponding name - list_select(labels SELECT "^lock:(.*)$" REPLACE "\\1" OUT lock) + list_select(labels SELECT "^lock:(.*)$" REPLACE "\\1" OUT locks) # Tests can set a timeout with a tag: list_select(labels SELECT "^timeout:(.*)$" REPLACE "\\1" OUT timeout) @@ -85,6 +85,6 @@ foreach(casename IN LISTS MONGOC_TESTS) # Fixture requirements: FIXTURES_REQUIRED "${fixtures}" # Test may lock resources: - RESOURCE_LOCK "${lock}" + RESOURCE_LOCK "${locks}" ) endforeach() From 3ec45a220a98040b584b987aa9a52515dd7a5783 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Fri, 31 Oct 2025 10:47:06 -0600 Subject: [PATCH 49/49] Fix name mstr_vsnprintf -> mstr_vsprintf --- src/common/src/mlib/str.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/common/src/mlib/str.h b/src/common/src/mlib/str.h index 153e408646f..c3c21a3f5dd 100644 --- a/src/common/src/mlib/str.h +++ b/src/common/src/mlib/str.h @@ -1117,9 +1117,9 @@ mstr_replace(mstr *str, mstr_view needle, mstr_view sub) } /** - * @brief Like `mstr_snprintf`, but accepts a `va_list` directly. + * @brief Like `mstr_sprintf`, but accepts a `va_list` directly. */ -mlib_printf_attribute(1, 0) static inline mstr mstr_vsnprintf(const char *format, va_list args) +mlib_printf_attribute(1, 0) static inline mstr mstr_vsprintf(const char *format, va_list args) { size_t format_strlen = strlen(format); size_t sz = format_strlen; @@ -1182,7 +1182,7 @@ mlib_printf_attribute(1, 2) static inline mstr mstr_sprintf(const char *f, ...) { va_list args; va_start(args, f); - mstr ret = mstr_vsnprintf(f, args); + mstr ret = mstr_vsprintf(f, args); va_end(args); return ret; } @@ -1193,7 +1193,7 @@ mlib_printf_attribute(1, 2) static inline mstr mstr_sprintf(const char *f, ...) mlib_printf_attribute(2, 0) static inline bool mstr_vsprintf_append(mstr *string, const char *format, va_list args) { mlib_check(string != NULL, because, "Output string parameter is required"); - mstr suffix = mstr_vsnprintf(format, args); + mstr suffix = mstr_vsprintf(format, args); bool ok = mstr_append(string, suffix); mstr_destroy(&suffix); return ok;