summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/ninja_test.cc105
-rw-r--r--src/test.h6
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 <stdarg.h>
#include <stdio.h>
+#ifdef _WIN32
+#include "getopt.h"
+#else
+#include <getopt.h>
+#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()