| // 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 <stdarg.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include "base/command_line.h" | |
| #include "base/files/file_util.h" | |
| #include "base/files/scoped_temp_dir.h" | |
| #include "base/strings/stringprintf.h" | |
| #include "gn/exec_process.h" | |
| #include "gn/standard_out.h" | |
| #include "util/test/test.h" | |
| #if defined(OS_WIN) | |
| #include <windows.h> | |
| #else | |
| #include <unistd.h> | |
| #endif | |
| namespace testing { | |
| Test* g_current_test; | |
| std::string Pretty(bool value) { | |
| return value ? "true" : "false"; | |
| } | |
| std::string Pretty(const char* value) { | |
| if (value == nullptr) | |
| return "(const char*) NULL"; | |
| return Pretty(std::string(value)); | |
| } | |
| std::string Pretty(const Err& err) { | |
| if (!err.has_error()) { | |
| return "no error"; | |
| } | |
| return err.message(); | |
| } | |
| std::string Indent(std::string_view value) { | |
| std::stringstream ss; | |
| ss << " "; | |
| for (auto c : value) { | |
| switch (c) { | |
| case '\n': | |
| ss << c; | |
| ss << " "; | |
| break; | |
| default: | |
| ss << c; | |
| } | |
| } | |
| return ss.str(); | |
| } | |
| std::string DiffStrings(std::string_view expected, std::string_view actual) { | |
| if (expected == actual) { | |
| return "Different values with the same representation:\n" + | |
| std::string(expected); | |
| } | |
| base::ScopedTempDir temp_dir; | |
| auto fallback = [&]() { | |
| return base::StringPrintf( | |
| "Expected:\n%.*s\n\nActual:\n%.*s", static_cast<int>(expected.size()), | |
| expected.data(), static_cast<int>(actual.size()), actual.data()); | |
| }; | |
| if (!temp_dir.CreateUniqueTempDir()) { | |
| return fallback(); | |
| } | |
| base::FilePath expected_path = temp_dir.GetPath().AppendASCII("expected.txt"); | |
| base::FilePath actual_path = temp_dir.GetPath().AppendASCII("actual.txt"); | |
| if (base::WriteFile(expected_path, expected.data(), expected.size()) != | |
| static_cast<int>(expected.size())) { | |
| return fallback(); | |
| } | |
| if (base::WriteFile(actual_path, actual.data(), actual.size()) != | |
| static_cast<int>(actual.size())) { | |
| return fallback(); | |
| } | |
| base::CommandLine cmdline(base::CommandLine::NO_PROGRAM); | |
| cmdline.SetParseSwitches(false); | |
| cmdline.SetProgram(base::FilePath(FILE_PATH_LITERAL("git"))); | |
| cmdline.AppendArg("diff"); | |
| cmdline.AppendArg("--no-index"); | |
| if (::IsColorEnabled()) { | |
| cmdline.AppendArg("--color"); | |
| } | |
| cmdline.AppendArgPath(expected_path); | |
| cmdline.AppendArgPath(actual_path); | |
| std::string output; | |
| std::string stderr_output; | |
| int exit_code = 0; | |
| ::internal::ExecProcess(cmdline, base::FilePath(FILE_PATH_LITERAL(".")), | |
| &output, &stderr_output, &exit_code); | |
| if (output.empty()) { | |
| return fallback(); | |
| } | |
| return output; | |
| } | |
| } // 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 = "*"; | |
| bool quiet = false; | |
| 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)]; | |
| } else if (strcmp(argv[i], "--quiet") == 0) { | |
| quiet = true; | |
| } | |
| } | |
| 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(); | |
| if (!quiet) { | |
| 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; | |
| if (quiet) { | |
| printf("FAILED: %s\n", tests[i].name); | |
| } | |
| } | |
| delete test; | |
| } | |
| if (!quiet || !passed) { | |
| printf("\n%s\n", passed ? "PASSED" : "FAILED"); | |
| } | |
| fflush(stdout); | |
| return passed ? EXIT_SUCCESS : EXIT_FAILURE; | |
| } |