From b2fe56caaf0bed497ee480003f10486c18d8de9a Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Wed, 17 Sep 2014 19:48:26 -0700 Subject: Use a small, standalone testing framework instead of googletest. Ninja currently uses googletest for testing. That makes building ninja_test somewhat annoying since it requires that one passes --with-gtest PATH to configure. It turns out just implementing the bits of googletest that ninja uses needs about the same amount of code than making the --with-gtest flag in configure.py work and making googletest print test results in a way we want (!) In addition to making configuration simpler, this also makes compiling tests much faster: On my system, touching src/build_test.cc (the slowest file to build in ninja) and rebuilding ninja_tests is twice as fast than without this patch. Building all is noticeably faster too: 5.6s with this patch, 9.1s without this patch (38% faster). The most noticeable things missing: EXPECT_* and ASSERT_* don't support streaming notes to them with operator<<, and for failing tests the lhs and rhs are not printed. That's so that this header does not have to include sstream, which slows down building ninja_test almost 20%. If this turns out to be annoying, we can maybe add it. --- configure.py | 31 ++------------- src/build_test.cc | 4 +- src/depfile_parser_test.cc | 2 +- src/deps_log_test.cc | 5 +++ src/disk_interface_test.cc | 4 +- src/includes_normalize_test.cc | 2 - src/lexer_test.cc | 3 +- src/manifest_parser_test.cc | 5 +-- src/msvc_helper_test.cc | 2 - src/ninja_test.cc | 89 +++++++++++++++++++----------------------- src/state_test.cc | 3 +- src/subprocess_test.cc | 10 +++-- src/test.cc | 4 +- src/test.h | 88 ++++++++++++++++++++++++++++++++++++++++- 14 files changed, 153 insertions(+), 99 deletions(-) diff --git a/configure.py b/configure.py index 64123a0..a307043 100755 --- a/configure.py +++ b/configure.py @@ -44,8 +44,7 @@ parser.add_option('--debug', action='store_true', parser.add_option('--profile', metavar='TYPE', choices=profilers, help='enable profiling (' + '/'.join(profilers) + ')',) -parser.add_option('--with-gtest', metavar='PATH', - help='use gtest unpacked in directory PATH') +parser.add_option('--with-gtest', metavar='PATH', help='ignored') parser.add_option('--with-python', metavar='EXE', help='use EXE as the Python interpreter', default=os.path.basename(sys.executable)) @@ -319,34 +318,10 @@ all_targets += ninja n.comment('Tests all build into ninja_test executable.') variables = [] -test_cflags = cflags + ['-DGTEST_HAS_RTTI=0'] test_ldflags = None test_libs = libs objs = [] -if options.with_gtest: - path = options.with_gtest - gtest_all_incs = '-I%s -I%s' % (path, os.path.join(path, 'include')) - if platform.is_msvc(): - gtest_cflags = '/nologo /EHsc /Zi /D_VARIADIC_MAX=10 ' - if platform.msvc_needs_fs(): - gtest_cflags += '/FS ' - gtest_cflags += gtest_all_incs - else: - gtest_cflags = '-fvisibility=hidden ' + gtest_all_incs - objs += n.build(built('gtest-all' + objext), 'cxx', - os.path.join(path, 'src', 'gtest-all.cc'), - variables=[('cflags', gtest_cflags)]) - - test_cflags.append('-I%s' % os.path.join(path, 'include')) -else: - # Use gtest from system. - if platform.is_msvc(): - test_libs.extend(['gtest_main.lib', 'gtest.lib']) - else: - test_libs.extend(['-lgtest_main', '-lgtest']) - -n.variable('test_cflags', test_cflags) for name in ['build_log_test', 'build_test', 'clean_test', @@ -362,10 +337,10 @@ for name in ['build_log_test', 'subprocess_test', 'test', 'util_test']: - objs += cxx(name, variables=[('cflags', '$test_cflags')]) + objs += cxx(name) if platform.is_windows(): for name in ['includes_normalize_test', 'msvc_helper_test']: - objs += cxx(name, variables=[('cflags', '$test_cflags')]) + objs += cxx(name) if not platform.is_windows(): test_libs.append('-lpthread') diff --git a/src/build_test.cc b/src/build_test.cc index dad69dc..6336206 100644 --- a/src/build_test.cc +++ b/src/build_test.cc @@ -14,6 +14,8 @@ #include "build.h" +#include + #include "build_log.h" #include "deps_log.h" #include "graph.h" @@ -506,7 +508,7 @@ void BuildTest::RebuildTarget(const string& target, const char* manifest, builder.command_runner_.reset(&command_runner_); if (!builder.AlreadyUpToDate()) { bool build_res = builder.Build(&err); - EXPECT_TRUE(build_res) << "builder.Build(&err)"; + EXPECT_TRUE(build_res); } builder.command_runner_.release(); } diff --git a/src/depfile_parser_test.cc b/src/depfile_parser_test.cc index a5f3321..e67ef79 100644 --- a/src/depfile_parser_test.cc +++ b/src/depfile_parser_test.cc @@ -14,7 +14,7 @@ #include "depfile_parser.h" -#include +#include "test.h" struct DepfileParserTest : public testing::Test { bool Parse(const char* input, string* err); diff --git a/src/deps_log_test.cc b/src/deps_log_test.cc index e8e5138..4fa4008 100644 --- a/src/deps_log_test.cc +++ b/src/deps_log_test.cc @@ -14,6 +14,11 @@ #include "deps_log.h" +#ifndef _WIN32 +#include +#include +#endif + #include "graph.h" #include "util.h" #include "test.h" diff --git a/src/disk_interface_test.cc b/src/disk_interface_test.cc index b2e8cb5..05d509c 100644 --- a/src/disk_interface_test.cc +++ b/src/disk_interface_test.cc @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include - +#include +#include #ifdef _WIN32 #include #include diff --git a/src/includes_normalize_test.cc b/src/includes_normalize_test.cc index 419996f..cf4a4a3 100644 --- a/src/includes_normalize_test.cc +++ b/src/includes_normalize_test.cc @@ -14,8 +14,6 @@ #include "includes_normalize.h" -#include - #include "test.h" #include "util.h" diff --git a/src/lexer_test.cc b/src/lexer_test.cc index e8a1642..331d8e1 100644 --- a/src/lexer_test.cc +++ b/src/lexer_test.cc @@ -14,9 +14,8 @@ #include "lexer.h" -#include - #include "eval_env.h" +#include "test.h" TEST(Lexer, ReadVarValue) { Lexer lexer("plain text $var $VaR ${x}\n"); diff --git a/src/manifest_parser_test.cc b/src/manifest_parser_test.cc index 152b965..ee87c94 100644 --- a/src/manifest_parser_test.cc +++ b/src/manifest_parser_test.cc @@ -17,17 +17,16 @@ #include #include -#include - #include "graph.h" #include "state.h" +#include "test.h" struct ParserTest : public testing::Test, public ManifestParser::FileReader { void AssertParse(const char* input) { ManifestParser parser(&state, this); string err; - ASSERT_TRUE(parser.ParseTest(input, &err)) << err; + EXPECT_TRUE(parser.ParseTest(input, &err)); ASSERT_EQ("", err); } diff --git a/src/msvc_helper_test.cc b/src/msvc_helper_test.cc index 391c045..29db650 100644 --- a/src/msvc_helper_test.cc +++ b/src/msvc_helper_test.cc @@ -14,8 +14,6 @@ #include "msvc_helper.h" -#include - #include "test.h" #include "util.h" diff --git a/src/ninja_test.cc b/src/ninja_test.cc index 989ea5c..9499c8b 100644 --- a/src/ninja_test.cc +++ b/src/ninja_test.cc @@ -15,9 +15,22 @@ #include #include -#include "gtest/gtest.h" +#include "test.h" #include "line_printer.h" +// This can't be a vector because tests call RegisterTest from static +// initializers and the order static initializers run it isn't specified. So +// the vector constructor isn't guaranteed to run before all of the +// RegisterTest() calls. +static testing::Test* (*tests[10000])(); +testing::Test* g_current_test; +static int ntests; +static LinePrinter printer; + +void RegisterTest(testing::Test* (*factory)()) { + tests[ntests++] = factory; +} + string StringPrintf(const char* format, ...) { const int N = 1024; char buf[N]; @@ -30,59 +43,37 @@ string StringPrintf(const char* format, ...) { return buf; } -/// A test result printer that's less wordy than gtest's default. -struct LaconicPrinter : public testing::EmptyTestEventListener { - LaconicPrinter() : tests_started_(0), test_count_(0), iteration_(0) {} - virtual void OnTestProgramStart(const testing::UnitTest& unit_test) { - test_count_ = unit_test.test_to_run_count(); - } - - virtual void OnTestIterationStart(const testing::UnitTest& test_info, - int iteration) { - tests_started_ = 0; - iteration_ = iteration; - } - - virtual void OnTestStart(const testing::TestInfo& test_info) { - ++tests_started_; - printer_.Print( - StringPrintf("[%d/%d%s] %s.%s", - tests_started_, - test_count_, - iteration_ ? StringPrintf(" iter %d", iteration_).c_str() - : "", - test_info.test_case_name(), - test_info.name()), - LinePrinter::ELIDE); +bool testing::Test::Check(bool condition, const char* file, int line, + const char* error) { + if (!condition) { + printer.PrintOnNewLine( + StringPrintf("*** Failure in %s:%d\n%s\n", file, line, error)); + failed_ = true; } + return condition; +} - virtual void OnTestPartResult( - const testing::TestPartResult& test_part_result) { - if (!test_part_result.failed()) - return; - printer_.PrintOnNewLine(StringPrintf( - "*** Failure in %s:%d\n%s\n", test_part_result.file_name(), - test_part_result.line_number(), test_part_result.summary())); - } +int main(int argc, char **argv) { + int tests_started = 0; - virtual void OnTestProgramEnd(const testing::UnitTest& unit_test) { - printer_.PrintOnNewLine(unit_test.Passed() ? "passed\n" : "failed\n"); - } + bool passed = true; + for (int i = 0; i < ntests; i++) { + ++tests_started; - private: - LinePrinter printer_; - int tests_started_; - int test_count_; - int iteration_; -}; + testing::Test* test = tests[i](); -int main(int argc, char **argv) { - testing::InitGoogleTest(&argc, argv); + printer.Print( + StringPrintf("[%d/%d] %s", tests_started, ntests, test->Name()), + LinePrinter::ELIDE); - testing::TestEventListeners& listeners = - testing::UnitTest::GetInstance()->listeners(); - delete listeners.Release(listeners.default_result_printer()); - listeners.Append(new LaconicPrinter); + test->SetUp(); + test->Run(); + test->TearDown(); + if (test->Failed()) + passed = false; + delete test; + } - return RUN_ALL_TESTS(); + printer.PrintOnNewLine(passed ? "passed\n" : "failed\n"); + return passed ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/src/state_test.cc b/src/state_test.cc index af2bff1..a4fafa1 100644 --- a/src/state_test.cc +++ b/src/state_test.cc @@ -12,10 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include - #include "graph.h" #include "state.h" +#include "test.h" namespace { diff --git a/src/subprocess_test.cc b/src/subprocess_test.cc index 775a13a..fe53748 100644 --- a/src/subprocess_test.cc +++ b/src/subprocess_test.cc @@ -18,6 +18,7 @@ #ifndef _WIN32 // SetWithLots need setrlimit. +#include #include #include #include @@ -92,7 +93,7 @@ TEST_F(SubprocessTest, InterruptParent) { return; } - ADD_FAILURE() << "We should have been interrupted"; + ASSERT_FALSE("We should have been interrupted"); } TEST_F(SubprocessTest, Console) { @@ -176,9 +177,10 @@ TEST_F(SubprocessTest, SetWithLots) { // Make sure [ulimit -n] isn't going to stop us from working. rlimit rlim; ASSERT_EQ(0, getrlimit(RLIMIT_NOFILE, &rlim)); - ASSERT_GT(rlim.rlim_cur, kNumProcs) - << "Raise [ulimit -n] well above " << kNumProcs - << " to make this test go"; + if (!EXPECT_GT(rlim.rlim_cur, kNumProcs)) { + printf("Raise [ulimit -n] well above %u to make this test go\n", kNumProcs); + return; + } vector procs; for (size_t i = 0; i < kNumProcs; ++i) { diff --git a/src/test.cc b/src/test.cc index 21015ed..ed2b910 100644 --- a/src/test.cc +++ b/src/test.cc @@ -24,6 +24,8 @@ #ifdef _WIN32 #include +#else +#include #endif namespace { @@ -90,7 +92,7 @@ Node* StateTestWithBuiltinRules::GetNode(const string& path) { void AssertParse(State* state, const char* input) { ManifestParser parser(state, NULL); string err; - ASSERT_TRUE(parser.ParseTest(input, &err)) << err; + EXPECT_TRUE(parser.ParseTest(input, &err)); ASSERT_EQ("", err); } diff --git a/src/test.h b/src/test.h index f34b877..be5dcff 100644 --- a/src/test.h +++ b/src/test.h @@ -15,12 +15,96 @@ #ifndef NINJA_TEST_H_ #define NINJA_TEST_H_ -#include - #include "disk_interface.h" #include "state.h" #include "util.h" +// A tiny testing framework inspired by googletest, but much simpler and +// faster to compile. It supports most things commonly used from googltest. The +// most noticeable things missing: EXPECT_* and ASSERT_* don't support +// streaming notes to them with operator<<, and for failing tests the lhs and +// rhs are not printed. That's so that this header does not have to include +// sstream, which slows down building ninja_test almost 20%. +namespace testing { +class Test { + bool failed_; + int assertion_failures_; + public: + Test() : failed_(false), assertion_failures_(0) {} + virtual ~Test() {} + virtual void SetUp() {} + virtual void TearDown() {} + virtual void Run() = 0; + virtual const char* Name() const = 0; + + bool Failed() const { return failed_; } + int AssertionFailures() const { return assertion_failures_; } + void AddAssertionFailure() { assertion_failures_++; } + bool Check(bool condition, const char* file, int line, const char* error); +}; +} + +void RegisterTest(testing::Test* (*)()); + +extern testing::Test* g_current_test; +#define TEST_F_(x, y, name) \ + struct y : public x { \ + static testing::Test* Create() { return g_current_test = new y; } \ + virtual void Run(); \ + virtual const char* Name() const { return name; } \ + }; \ + struct Register##y { \ + Register##y() { RegisterTest(y::Create); } \ + }; \ + Register##y g_register_##y; \ + void y::Run() + +#define TEST_F(x, y) TEST_F_(x, x##y, #x "." #y) +#define TEST(x, y) TEST_F_(testing::Test, x##y, #x "." #y) + +#define EXPECT_EQ(a, b) \ + g_current_test->Check(a == b, __FILE__, __LINE__, #a " == " #b) +#define EXPECT_NE(a, b) \ + g_current_test->Check(a != b, __FILE__, __LINE__, #a " != " #b) +#define EXPECT_GT(a, b) \ + g_current_test->Check(a > b, __FILE__, __LINE__, #a " > " #b) +#define EXPECT_LT(a, b) \ + g_current_test->Check(a < b, __FILE__, __LINE__, #a " < " #b) +#define EXPECT_GE(a, b) \ + g_current_test->Check(a >= b, __FILE__, __LINE__, #a " >= " #b) +#define EXPECT_LE(a, b) \ + g_current_test->Check(a <= b, __FILE__, __LINE__, #a " <= " #b) +#define EXPECT_TRUE(a) \ + g_current_test->Check(static_cast(a), __FILE__, __LINE__, #a) +#define EXPECT_FALSE(a) \ + g_current_test->Check(!static_cast(a), __FILE__, __LINE__, #a) + +#define ASSERT_EQ(a, b) \ + if (!EXPECT_EQ(a, b)) { g_current_test->AddAssertionFailure(); return; } +#define ASSERT_NE(a, b) \ + if (!EXPECT_NE(a, b)) { g_current_test->AddAssertionFailure(); return; } +#define ASSERT_GT(a, b) \ + if (!EXPECT_GT(a, b)) { g_current_test->AddAssertionFailure(); return; } +#define ASSERT_LT(a, b) \ + if (!EXPECT_LT(a, b)) { g_current_test->AddAssertionFailure(); return; } +#define ASSERT_GE(a, b) \ + if (!EXPECT_GE(a, b)) { g_current_test->AddAssertionFailure(); return; } +#define ASSERT_LE(a, b) \ + if (!EXPECT_LE(a, b)) { g_current_test->AddAssertionFailure(); return; } +#define ASSERT_TRUE(a) \ + if (!EXPECT_TRUE(a)) { g_current_test->AddAssertionFailure(); return; } +#define ASSERT_FALSE(a) \ + if (!EXPECT_FALSE(a)) { g_current_test->AddAssertionFailure(); return; } +#define ASSERT_NO_FATAL_FAILURE(a) \ + { \ + int f = g_current_test->AssertionFailures(); \ + a; \ + if (f != g_current_test->AssertionFailures()) { \ + g_current_test->AddAssertionFailure(); \ + return; \ + } \ + } + // Support utilites for tests. struct Node; -- cgit v0.12 From d5bddeafc6bde19724f666617c1bc07f53d217c8 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Wed, 17 Sep 2014 20:35:07 -0700 Subject: Add support for --gtest_filter. The bots need it, and it is a useful flag. --- src/ninja_test.cc | 105 +++++++++++++++++++++++++++++++++++++++++++++++++----- src/test.h | 6 ++-- 2 files changed, 99 insertions(+), 12 deletions(-) diff --git a/src/ninja_test.cc b/src/ninja_test.cc index 9499c8b..31c2e1f 100644 --- a/src/ninja_test.cc +++ b/src/ninja_test.cc @@ -15,22 +15,35 @@ #include #include +#ifdef _WIN32 +#include "getopt.h" +#else +#include +#endif + #include "test.h" #include "line_printer.h" +struct RegisteredTest { + testing::Test* (*factory)(); + const char *name; + bool should_run; +}; // This can't be a vector because tests call RegisterTest from static // initializers and the order static initializers run it isn't specified. So // the vector constructor isn't guaranteed to run before all of the // RegisterTest() calls. -static testing::Test* (*tests[10000])(); +static RegisteredTest tests[10000]; testing::Test* g_current_test; static int ntests; static LinePrinter printer; -void RegisterTest(testing::Test* (*factory)()) { - tests[ntests++] = factory; +void RegisterTest(testing::Test* (*factory)(), const char* name) { + tests[ntests].factory = factory; + tests[ntests++].name = name; } +namespace { string StringPrintf(const char* format, ...) { const int N = 1024; char buf[N]; @@ -43,6 +56,74 @@ string StringPrintf(const char* format, ...) { return buf; } +void Usage() { + fprintf(stderr, +"usage: ninja_tests [options]\n" +"\n" +"options:\n" +" --gtest_filter=POSTIVE_PATTERNS[-NEGATIVE_PATTERNS]\n" +" Run only the tests whose name matches one of the positive patterns but\n" +" none of the negative patterns. '?' matches any single character; '*'\n" +" matches any substring; ':' separates two patterns.\n"); +} + +bool PatternMatchesString(const char* pattern, const char* str) { + switch (*pattern) { + case '\0': + case '-': + case ':': return *str == '\0'; + case '?': return *str != '\0' && PatternMatchesString(pattern + 1, str + 1); + case '*': return (*str != '\0' && PatternMatchesString(pattern, str + 1)) || + PatternMatchesString(pattern + 1, str); + default: return *pattern == *str && + PatternMatchesString(pattern + 1, str + 1); + } +} + +bool MatchesFilter(const char* name, const char* filter) { + for (const char* cur_pattern = filter - 1; cur_pattern != NULL; + cur_pattern = strchr(cur_pattern, ':')) { + cur_pattern++; // Skip the pattern separator (the ':' character). + if (PatternMatchesString(cur_pattern, name)) + return true; + } + return false; +} + +bool TestMatchesFilter(const char* test, const char* filter) { + // Split --gtest_filter at '-' into positive and negative filters. + const char* const dash = strchr(filter, '-'); + const char* positive = // Treat '-test1' as '*-test1': + dash == filter ? "*" : filter; + const char* negative = dash ? dash + 1 : ""; + return MatchesFilter(test, positive) && !MatchesFilter(test, negative); +} + +bool ReadFlags(int* argc, char*** argv, const char** test_filter) { + enum { OPT_GTEST_FILTER = 1 }; + const option kLongOptions[] = { + { "gtest_filter", required_argument, NULL, OPT_GTEST_FILTER }, + { NULL, 0, NULL, 0 } + }; + + int opt; + while ((opt = getopt_long(*argc, *argv, "h", kLongOptions, NULL)) != -1) { + switch (opt) { + case OPT_GTEST_FILTER: + *test_filter = optarg; + break; + default: + Usage(); + return false; + } + } + *argv += optind; + *argc -= optind; + return true; +} + +} // namespace + bool testing::Test::Check(bool condition, const char* file, int line, const char* error) { if (!condition) { @@ -56,16 +137,24 @@ bool testing::Test::Check(bool condition, const char* file, int line, int main(int argc, char **argv) { int tests_started = 0; + const char* test_filter = "*"; + if (!ReadFlags(&argc, &argv, &test_filter)) + return 1; + + int nactivetests = 0; + for (int i = 0; i < ntests; i++) + if ((tests[i].should_run = TestMatchesFilter(tests[i].name, test_filter))) + ++nactivetests; + bool passed = true; for (int i = 0; i < ntests; i++) { - ++tests_started; - - testing::Test* test = tests[i](); + if (!tests[i].should_run) continue; + ++tests_started; + testing::Test* test = tests[i].factory(); printer.Print( - StringPrintf("[%d/%d] %s", tests_started, ntests, test->Name()), + StringPrintf("[%d/%d] %s", tests_started, nactivetests, tests[i].name), LinePrinter::ELIDE); - test->SetUp(); test->Run(); test->TearDown(); diff --git a/src/test.h b/src/test.h index be5dcff..4c15203 100644 --- a/src/test.h +++ b/src/test.h @@ -35,7 +35,6 @@ class Test { virtual void SetUp() {} virtual void TearDown() {} virtual void Run() = 0; - virtual const char* Name() const = 0; bool Failed() const { return failed_; } int AssertionFailures() const { return assertion_failures_; } @@ -44,17 +43,16 @@ class Test { }; } -void RegisterTest(testing::Test* (*)()); +void RegisterTest(testing::Test* (*)(), const char*); extern testing::Test* g_current_test; #define TEST_F_(x, y, name) \ struct y : public x { \ static testing::Test* Create() { return g_current_test = new y; } \ virtual void Run(); \ - virtual const char* Name() const { return name; } \ }; \ struct Register##y { \ - Register##y() { RegisterTest(y::Create); } \ + Register##y() { RegisterTest(y::Create, name); } \ }; \ Register##y g_register_##y; \ void y::Run() -- cgit v0.12 From 954669e9c53301989af3a05bbd69564a3eb41bec Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Wed, 17 Sep 2014 21:22:49 -0700 Subject: Don't support ? and : for --gtest-filter, a bit simpler. Let me know if you use these! --- src/ninja_test.cc | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/src/ninja_test.cc b/src/ninja_test.cc index 31c2e1f..54d8784 100644 --- a/src/ninja_test.cc +++ b/src/ninja_test.cc @@ -61,18 +61,15 @@ void Usage() { "usage: ninja_tests [options]\n" "\n" "options:\n" -" --gtest_filter=POSTIVE_PATTERNS[-NEGATIVE_PATTERNS]\n" -" Run only the tests whose name matches one of the positive patterns but\n" -" none of the negative patterns. '?' matches any single character; '*'\n" -" matches any substring; ':' separates two patterns.\n"); +" --gtest_filter=POSTIVE_PATTERN[-NEGATIVE_PATTERN]\n" +" Run tests whose names match the positive but not the negative pattern.\n" +" '*' matches any substring. (gtest's ':', '?' are not implemented).\n"); } bool PatternMatchesString(const char* pattern, const char* str) { switch (*pattern) { case '\0': - case '-': - case ':': return *str == '\0'; - case '?': return *str != '\0' && PatternMatchesString(pattern + 1, str + 1); + case '-': return *str == '\0'; case '*': return (*str != '\0' && PatternMatchesString(pattern, str + 1)) || PatternMatchesString(pattern + 1, str); default: return *pattern == *str && @@ -80,23 +77,12 @@ bool PatternMatchesString(const char* pattern, const char* str) { } } -bool MatchesFilter(const char* name, const char* filter) { - for (const char* cur_pattern = filter - 1; cur_pattern != NULL; - cur_pattern = strchr(cur_pattern, ':')) { - cur_pattern++; // Skip the pattern separator (the ':' character). - if (PatternMatchesString(cur_pattern, name)) - return true; - } - return false; -} - bool TestMatchesFilter(const char* test, const char* filter) { // Split --gtest_filter at '-' into positive and negative filters. const char* const dash = strchr(filter, '-'); - const char* positive = // Treat '-test1' as '*-test1': - dash == filter ? "*" : filter; - const char* negative = dash ? dash + 1 : ""; - return MatchesFilter(test, positive) && !MatchesFilter(test, negative); + const char* pos = dash == filter ? "*" : filter; //Treat '-test1' as '*-test1' + const char* neg = dash ? dash + 1 : ""; + return PatternMatchesString(pos, test) && !PatternMatchesString(neg, test); } bool ReadFlags(int* argc, char*** argv, const char** test_filter) { @@ -110,8 +96,10 @@ bool ReadFlags(int* argc, char*** argv, const char** test_filter) { while ((opt = getopt_long(*argc, *argv, "h", kLongOptions, NULL)) != -1) { switch (opt) { case OPT_GTEST_FILTER: - *test_filter = optarg; - break; + if (strchr(optarg, '?') == NULL && strchr(optarg, ':') == NULL) { + *test_filter = optarg; + break; + } // else fall through. default: Usage(); return false; -- cgit v0.12