Skip to content

Commit

Permalink
Googletest export
Browse files Browse the repository at this point in the history
Use linear-time string globbing in UnitTestOptions::MatchesFilter.

Algorithm is based on https://research.swtch.com/glob.

Closes google#3227

PiperOrigin-RevId: 355222440
  • Loading branch information
dmcardle authored and dinord committed Feb 5, 2021
1 parent f4e7727 commit fd873f6
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 41 deletions.
7 changes: 0 additions & 7 deletions googletest/src/gtest-internal-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -394,13 +394,6 @@ class GTEST_API_ UnitTestOptions {

// Functions for processing the gtest_filter flag.

// Returns true if and only if the wildcard pattern matches the string.
// The first ':' or '\0' character in pattern marks the end of it.
//
// This recursive algorithm isn't very efficient, but is clear and
// works well enough for matching test names, which are short.
static bool PatternMatchesString(const char *pattern, const char *str);

// Returns true if and only if the user-specified filter matches the test
// suite name and the test name.
static bool FilterMatchesTest(const std::string& test_suite_name,
Expand Down
103 changes: 69 additions & 34 deletions googletest/src/gtest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit fd873f6

Please sign in to comment.