// Copyright 2013 Google Inc. All Rights Reserved. // // 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 #include #include "base/command_line.h" #include "util/build_config.h" #include "util/test/test.h" #if defined(OS_WIN) #include #else #include #endif namespace testing { Test* g_current_test; } // namespace testing 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 RegisteredTest tests[10000]; static int ntests; void RegisterTest(testing::Test* (*factory)(), const char* name) { tests[ntests].factory = factory; tests[ntests++].name = name; } namespace { bool PatternMatchesString(const char* pattern, const char* str) { switch (*pattern) { case '\0': case '-': case ':': return *str == '\0'; case '*': return (*str != '\0' && PatternMatchesString(pattern, str + 1)) || PatternMatchesString(pattern + 1, str); default: return *pattern == *str && PatternMatchesString(pattern + 1, str + 1); } } bool PatternListMatchString(const char* pattern, const char* str) { const char* const colon = strchr(pattern, ':'); if (PatternMatchesString(pattern, str)) return true; if (!colon) return false; return PatternListMatchString(colon + 1, str); } 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* pos = dash == filter ? "*" : filter; // Treat '-test1' as '*-test1' const char* neg = dash ? dash + 1 : ""; return PatternListMatchString(pos, test) && !PatternListMatchString(neg, test); } #if defined(OS_WIN) struct ScopedEnableVTEscapeProcessing { ScopedEnableVTEscapeProcessing() { console_ = GetStdHandle(STD_OUTPUT_HANDLE); CONSOLE_SCREEN_BUFFER_INFO csbi; if (GetConsoleScreenBufferInfo(console_, &csbi) && GetConsoleMode(console_, &original_mode_)) { SetConsoleMode(console_, original_mode_ | ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN); } else { console_ = INVALID_HANDLE_VALUE; } } ~ScopedEnableVTEscapeProcessing() { if (is_valid()) SetConsoleMode(console_, original_mode_); } bool is_valid() const { return console_ != INVALID_HANDLE_VALUE; } HANDLE console_; DWORD original_mode_; }; #endif } // namespace int main(int argc, char** argv) { base::CommandLine::Init(argc, argv); #if defined(OS_WIN) ScopedEnableVTEscapeProcessing enable_vt_processing; #endif setvbuf(stdout, NULL, _IOLBF, BUFSIZ); int tests_started = 0; const char* test_filter = "*"; for (int i = 1; i < argc; ++i) { const char kTestFilterPrefix[] = "--gtest_filter="; if (strncmp(argv[i], kTestFilterPrefix, strlen(kTestFilterPrefix)) == 0) { test_filter = &argv[i][strlen(kTestFilterPrefix)]; } } int num_active_tests = 0; for (int i = 0; i < ntests; i++) { tests[i].should_run = TestMatchesFilter(tests[i].name, test_filter); if (tests[i].should_run) { ++num_active_tests; } } const char* prefix = ""; const char* suffix = "\n"; #if defined(OS_WIN) if (enable_vt_processing.is_valid()) #else // When run from Xcode, the console returns "true" to isatty(1) but it // does not interprets ANSI escape sequence resulting in difficult to // read output. There is no portable way to detect if the console is // Xcode's console (term is set to xterm or xterm-256colors) but Xcode // sets the __XCODE_BUILT_PRODUCTS_DIR_PATHS environment variable. Use // this as a proxy to detect that the console does not interpret the // ANSI sequences correctly. if (isatty(1) && getenv("__XCODE_BUILT_PRODUCTS_DIR_PATHS") == NULL) #endif { prefix = "\r"; suffix = "\x1B[K"; } bool passed = true; for (int i = 0; i < ntests; i++) { if (!tests[i].should_run) continue; ++tests_started; testing::Test* test = tests[i].factory(); printf("%s[%d/%d] %s%s", prefix, tests_started, num_active_tests, tests[i].name, suffix); test->SetUp(); test->Run(); test->TearDown(); if (test->Failed()) passed = false; delete test; } printf("\n%s\n", passed ? "PASSED" : "FAILED"); fflush(stdout); return passed ? EXIT_SUCCESS : EXIT_FAILURE; }