diff options
author | dmcardle <dmcardle@google.com> | 2021-02-02 20:10:54 (GMT) |
---|---|---|
committer | Dino Radaković <dinor@google.com> | 2021-02-05 18:40:36 (GMT) |
commit | fd873f6d7d22e231a083a7fc570f95961c36e85c (patch) | |
tree | 4291e24dc8a41420af3e37406bf2ba5616eb1aa3 /googletest/src/gtest.cc | |
parent | f4e7727cf4b9fc4f4a821490b86047ac44540e7c (diff) | |
download | googletest-fd873f6d7d22e231a083a7fc570f95961c36e85c.zip googletest-fd873f6d7d22e231a083a7fc570f95961c36e85c.tar.gz googletest-fd873f6d7d22e231a083a7fc570f95961c36e85c.tar.bz2 |
Googletest export
Use linear-time string globbing in UnitTestOptions::MatchesFilter.
Algorithm is based on https://research.swtch.com/glob.
Closes #3227
PiperOrigin-RevId: 355222440
Diffstat (limited to 'googletest/src/gtest.cc')
-rw-r--r-- | googletest/src/gtest.cc | 103 |
1 files changed, 69 insertions, 34 deletions
diff --git a/googletest/src/gtest.cc b/googletest/src/gtest.cc index 3dedfc7..30e2071 100644 --- a/googletest/src/gtest.cc +++ b/googletest/src/gtest.cc @@ -646,47 +646,82 @@ std::string UnitTestOptions::GetAbsolutePathToOutputFile() { return result.string(); } -// Returns true if and only if the wildcard pattern matches the string. -// The first ':' or '\0' character in pattern marks the end of it. +// Returns true if and only if the wildcard pattern matches the string. Each +// pattern consists of regular characters, single-character wildcards (?), and +// multi-character wildcards (*). // -// This recursive algorithm isn't very efficient, but is clear and -// works well enough for matching test names, which are short. -bool UnitTestOptions::PatternMatchesString(const char *pattern, - const char *str) { - switch (*pattern) { - case '\0': - case ':': // Either ':' or '\0' marks the end of the pattern. - return *str == '\0'; - case '?': // Matches any single character. - return *str != '\0' && PatternMatchesString(pattern + 1, str + 1); - case '*': // Matches any string (possibly empty) of characters. - return (*str != '\0' && PatternMatchesString(pattern, str + 1)) || - PatternMatchesString(pattern + 1, str); - default: // Non-special character. Matches itself. - return *pattern == *str && - PatternMatchesString(pattern + 1, str + 1); - } -} - -bool UnitTestOptions::MatchesFilter( - const std::string& name, const char* filter) { - const char *cur_pattern = filter; - for (;;) { - if (PatternMatchesString(cur_pattern, name.c_str())) { - return true; +// This function implements a linear-time string globbing algorithm based on +// https://research.swtch.com/glob. +static bool PatternMatchesString(const std::string& name_str, + const char* pattern, const char* pattern_end) { + const char* name = name_str.c_str(); + const char* const name_begin = name; + const char* const name_end = name + name_str.size(); + + const char* pattern_next = pattern; + const char* name_next = name; + + while (pattern < pattern_end || name < name_end) { + if (pattern < pattern_end) { + switch (*pattern) { + default: // Match an ordinary character. + if (name < name_end && *name == *pattern) { + ++pattern; + ++name; + continue; + } + break; + case '?': // Match any single character. + if (name < name_end) { + ++pattern; + ++name; + continue; + } + break; + case '*': + // Match zero or more characters. Start by skipping over the wildcard + // and matching zero characters from name. If that fails, restart and + // match one more character than the last attempt. + pattern_next = pattern; + name_next = name + 1; + ++pattern; + continue; + } + } + // Failed to match a character. Restart if possible. + if (name_begin < name_next && name_next <= name_end) { + pattern = pattern_next; + name = name_next; + continue; } + return false; + } + return true; +} - // Finds the next pattern in the filter. - cur_pattern = strchr(cur_pattern, ':'); +bool UnitTestOptions::MatchesFilter(const std::string& name_str, + const char* filter) { + // The filter is a list of patterns separated by colons (:). + const char* pattern = filter; + while (true) { + // Find the bounds of this pattern. + const char* const next_sep = strchr(pattern, ':'); + const char* const pattern_end = + next_sep != nullptr ? next_sep : pattern + strlen(pattern); - // Returns if no more pattern can be found. - if (cur_pattern == nullptr) { - return false; + // Check if this pattern matches name_str. + if (PatternMatchesString(name_str, pattern, pattern_end)) { + return true; } - // Skips the pattern separater (the ':' character). - cur_pattern++; + // Give up on this pattern. However, if we found a pattern separator (:), + // advance to the next pattern (skipping over the separator) and restart. + if (next_sep == nullptr) { + return false; + } + pattern = next_sep + 1; } + return true; } // Returns true if and only if the user-specified filter matches the test |