diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2021-07-15 00:08:06 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2021-07-15 00:08:06 +0000 |
commit | 6a73ec6b43d1bb4a8465b3b2019c19d00be55bf1 (patch) | |
tree | 5b82b63e08e6b34cb0895628cf81022e67a965f8 | |
parent | 8e8f1e9c82c946997959642073317784627919a5 (diff) | |
parent | c1bcf9b13e4f065ddf008fd21f959114888383de (diff) | |
download | gtest_extras-6a73ec6b43d1bb4a8465b3b2019c19d00be55bf1.tar.gz |
Snap for 7550640 from c1bcf9b13e4f065ddf008fd21f959114888383de to mainline-wifi-releaseandroid-mainline-12.0.0_r96android-mainline-12.0.0_r83android-mainline-12.0.0_r67android-mainline-12.0.0_r57android-mainline-12.0.0_r40android-mainline-12.0.0_r20android-mainline-12.0.0_r126android-mainline-12.0.0_r114aml_wif_311811030android12-mainline-wifi-release
Change-Id: Ibe85a05d6e4069ed8c7002838f1c9d1f4e9ba489
-rw-r--r-- | Android.bp | 8 | ||||
-rw-r--r-- | Isolate.cpp | 94 | ||||
-rw-r--r-- | IsolateMain.cpp | 54 | ||||
-rw-r--r-- | Log.h | 34 | ||||
-rw-r--r-- | Options.cpp | 62 | ||||
-rw-r--r-- | Test.cpp | 10 | ||||
-rw-r--r-- | Test.h | 5 |
7 files changed, 196 insertions, 71 deletions
@@ -12,6 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + cc_library_static { name: "libgtest_isolated", host_supported: true, @@ -27,11 +31,7 @@ cc_library_static { "Test.cpp", ], - // NOTE: libbase is re-exported by including them below. - // When Soong supports transitive static dependency includes, this - // library can be removed. whole_static_libs: [ - "libbase", "libgtest", ], diff --git a/Isolate.cpp b/Isolate.cpp index 889b183..8f76f54 100644 --- a/Isolate.cpp +++ b/Isolate.cpp @@ -20,21 +20,21 @@ #include <poll.h> #include <signal.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> #include <unistd.h> #include <atomic> +#include <memory> #include <string> #include <tuple> #include <vector> -#include <android-base/logging.h> -#include <android-base/strings.h> -#include <android-base/unique_fd.h> #include <gtest/gtest.h> #include "Color.h" #include "Isolate.h" +#include "Log.h" #include "NanoTime.h" #include "Test.h" @@ -50,22 +50,22 @@ static void SignalHandler(int sig) { static void RegisterSignalHandler() { auto ret = signal(SIGINT, SignalHandler); if (ret == SIG_ERR) { - PLOG(FATAL) << "Setting up SIGINT handler failed"; + FATAL_PLOG("Setting up SIGINT handler failed"); } ret = signal(SIGQUIT, SignalHandler); if (ret == SIG_ERR) { - PLOG(FATAL) << "Setting up SIGQUIT handler failed"; + FATAL_PLOG("Setting up SIGQUIT handler failed"); } } static void UnregisterSignalHandler() { auto ret = signal(SIGINT, SIG_DFL); if (ret == SIG_ERR) { - PLOG(FATAL) << "Disabling SIGINT handler failed"; + FATAL_PLOG("Disabling SIGINT handler failed"); } ret = signal(SIGQUIT, SIG_DFL); if (ret == SIG_ERR) { - PLOG(FATAL) << "Disabling SIGQUIT handler failed"; + FATAL_PLOG("Disabling SIGQUIT handler failed"); } } @@ -81,6 +81,12 @@ static std::string PluralizeString(size_t value, const char* name, bool uppercas return string; } +inline static bool StartsWithDisabled(const std::string& str) { + static constexpr char kDisabledStr[] = "DISABLED_"; + static constexpr size_t kDisabledStrLen = sizeof(kDisabledStr) - 1; + return str.compare(0, kDisabledStrLen, kDisabledStr) == 0; +} + void Isolate::EnumerateTests() { // Only apply --gtest_filter if present. This is the only option that changes // what tests are listed. @@ -89,13 +95,14 @@ void Isolate::EnumerateTests() { command += " --gtest_filter=" + options_.filter(); } command += " --gtest_list_tests"; -#if defined(__APPLE__) - FILE* fp = popen(command.c_str(), "r"); -#else +#if defined(__BIONIC__) + // Only bionic is guaranteed to support the 'e' option. FILE* fp = popen(command.c_str(), "re"); +#else + FILE* fp = popen(command.c_str(), "r"); #endif if (fp == nullptr) { - PLOG(FATAL) << "Unexpected failure from popen"; + FATAL_PLOG("Unexpected failure from popen"); } size_t total_shards = options_.total_shards(); @@ -122,7 +129,7 @@ void Isolate::EnumerateTests() { suite_name.resize(suite_name.size() - 1); } - if (!options_.allow_disabled_tests() && android::base::StartsWith(suite_name, "DISABLED_")) { + if (!options_.allow_disabled_tests() && StartsWithDisabled(suite_name)) { // This whole set of tests have been disabled, skip them all. skip_until_next_suite = true; } else { @@ -139,7 +146,7 @@ void Isolate::EnumerateTests() { if (test_name.back() == '\n') { test_name.resize(test_name.size() - 1); } - if (options_.allow_disabled_tests() || !android::base::StartsWith(test_name, "DISABLED_")) { + if (options_.allow_disabled_tests() || !StartsWithDisabled(test_name)) { if (!sharded || --test_count == 0) { tests_.push_back(std::make_tuple(suite_name, test_name)); total_tests_++; @@ -167,7 +174,7 @@ void Isolate::EnumerateTests() { } free(buffer); if (pclose(fp) == -1) { - PLOG(FATAL) << "Unexpected failure from pclose"; + FATAL_PLOG("Unexpected failure from pclose"); } } @@ -178,7 +185,7 @@ int Isolate::ChildProcessFn(const std::tuple<std::string, std::string>& test) { // Add the filter argument. std::vector<char*> args(child_args_); std::string filter("--gtest_filter=" + GetTestName(test)); - args.push_back(strdup(filter.c_str())); + args.push_back(filter.data()); int argc = args.size(); // Add the null terminator. @@ -187,22 +194,45 @@ int Isolate::ChildProcessFn(const std::tuple<std::string, std::string>& test) { return RUN_ALL_TESTS(); } +static bool Pipe(int* read_fd, int* write_fd) { + int pipefd[2]; + +#if defined(__linux__) + if (pipe2(pipefd, O_CLOEXEC) != 0) { + return false; + } +#else // defined(__APPLE__) + if (pipe(pipefd) != 0) { + return false; + } + if (fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) != 0 || fcntl(pipefd[1], F_SETFD, FD_CLOEXEC)) { + close(pipefd[0]); + close(pipefd[1]); + return false; + } +#endif + + *read_fd = pipefd[0]; + *write_fd = pipefd[1]; + return true; +} + void Isolate::LaunchTests() { while (!running_indices_.empty() && cur_test_index_ < tests_.size()) { - android::base::unique_fd read_fd, write_fd; + int read_fd, write_fd; if (!Pipe(&read_fd, &write_fd)) { - PLOG(FATAL) << "Unexpected failure from pipe"; + FATAL_PLOG("Unexpected failure from pipe"); } - if (fcntl(read_fd.get(), F_SETFL, O_NONBLOCK) == -1) { - PLOG(FATAL) << "Unexpected failure from fcntl"; + if (fcntl(read_fd, F_SETFL, O_NONBLOCK) == -1) { + FATAL_PLOG("Unexpected failure from fcntl"); } pid_t pid = fork(); if (pid == -1) { - PLOG(FATAL) << "Unexpected failure from fork"; + FATAL_PLOG("Unexpected failure from fork"); } if (pid == 0) { - read_fd.reset(); + close(read_fd); close(STDOUT_FILENO); close(STDERR_FILENO); if (dup2(write_fd, STDOUT_FILENO) == -1) { @@ -211,13 +241,14 @@ void Isolate::LaunchTests() { if (dup2(write_fd, STDERR_FILENO) == -1) { exit(1); } + close(write_fd); UnregisterSignalHandler(); exit(ChildProcessFn(tests_[cur_test_index_])); } size_t run_index = running_indices_.back(); running_indices_.pop_back(); - Test* test = new Test(tests_[cur_test_index_], cur_test_index_, run_index, read_fd.release()); + Test* test = new Test(tests_[cur_test_index_], cur_test_index_, run_index, read_fd); running_by_pid_.emplace(pid, test); running_[run_index] = test; running_by_test_index_[cur_test_index_] = test; @@ -226,6 +257,7 @@ void Isolate::LaunchTests() { pollfd->fd = test->fd(); pollfd->events = POLLIN; cur_test_index_++; + close(write_fd); } } @@ -254,9 +286,12 @@ size_t Isolate::CheckTestsFinished() { int status; pid_t pid; while ((pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG))) > 0) { + if (pid == -1) { + FATAL_PLOG("Unexpected failure from waitpid"); + } auto entry = running_by_pid_.find(pid); if (entry == running_by_pid_.end()) { - LOG(FATAL) << "Pid " << pid << " was not spawned by the isolation framework."; + FATAL_LOG("Found process not spawned by the isolation framework"); } std::unique_ptr<Test>& test_ptr = entry->second; @@ -326,7 +361,7 @@ size_t Isolate::CheckTestsFinished() { total_skipped_tests_++; break; case TEST_NONE: - LOG(FATAL) << "Test result is TEST_NONE, this should not be possible."; + FATAL_LOG("Test result is TEST_NONE, this should not be possible"); } finished_tests++; size_t test_index = test->test_index(); @@ -348,7 +383,7 @@ size_t Isolate::CheckTestsFinished() { // The only valid error case is if ECHILD is returned because there are // no more processes left running. if (pid == -1 && errno != ECHILD) { - PLOG(FATAL) << "Unexpected failure from waitpid"; + FATAL_PLOG("Unexpected failure from waitpid"); } return finished_tests; } @@ -618,7 +653,10 @@ void TestResultPrinter::OnTestPartResult(const ::testing::TestPartResult& result } if (result.type() == ::testing::TestPartResult::kSkip) { - printf("%s:(%d) Skipped%s\n", result.file_name(), result.line_number(), result.message()); + printf("%s:(%d) Skipped\n", result.file_name(), result.line_number()); + if (*result.message()) { + printf("%s\n", result.message()); + } } else { // Print failure message from the assertion (e.g. expected this and got that). printf("%s:(%d) Failure in test %s.%s\n%s\n", result.file_name(), result.line_number(), @@ -640,7 +678,7 @@ void Isolate::WriteXmlResults(uint64_t elapsed_time_ns, time_t start_time) { const tm* time_struct = localtime(&start_time); if (time_struct == nullptr) { - PLOG(FATAL) << "Unexpected failure from localtime"; + FATAL_PLOG("Unexpected failure from localtime"); } char timestamp[40]; snprintf(timestamp, sizeof(timestamp), "%4d-%02d-%02dT%02d:%02d:%02d", @@ -739,7 +777,7 @@ int Isolate::Run() { EnumerateTests(); // Stop default result printer to avoid environment setup/teardown information for each test. - ::testing::UnitTest::GetInstance()->listeners().Release( + delete ::testing::UnitTest::GetInstance()->listeners().Release( ::testing::UnitTest::GetInstance()->listeners().default_result_printer()); ::testing::UnitTest::GetInstance()->listeners().Append(new TestResultPrinter); RegisterSignalHandler(); diff --git a/IsolateMain.cpp b/IsolateMain.cpp index 30b871b..5b34ef5 100644 --- a/IsolateMain.cpp +++ b/IsolateMain.cpp @@ -15,14 +15,16 @@ */ #include <errno.h> +#include <limits.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <cstring> +#include <string_view> #include <vector> -#include <android-base/file.h> #include <gtest/gtest.h> #include <gtest_extras/IsolateMain.h> @@ -44,7 +46,15 @@ static void PrintHelpInfo() { " Use isolation mode, Run each test in a separate process.\n" " If JOB_COUNT is not given, it is set to the count of available processors.\n"); ColoredPrintf(COLOR_GREEN, " --no_isolate\n"); - printf(" Don't use isolation mode, run all tests in a single process.\n"); + printf( + " Don't use isolation mode, run all tests in a single process.\n" + " If the test seems to be running in a debugger (based on the parent's name) this will\n" + " be automatically set. If this behavior is not desired use the '--force_isolate' flag\n" + " below.\n"); + ColoredPrintf(COLOR_GREEN, " --force_isolate\n"); + printf( + " Force the use of isolation mode, even if it looks like we are running in a\n" + " debugger.\n"); ColoredPrintf(COLOR_GREEN, " --deadline_threshold_ms="); ColoredPrintf(COLOR_YELLOW, "[TIME_IN_MS]\n"); printf(" Run each test in no longer than "); @@ -59,10 +69,8 @@ static void PrintHelpInfo() { printf( " will be called slow.\n" " Only valid in isolation mode. Default slow threshold is 2000 ms.\n"); - ColoredPrintf(COLOR_GREEN, "-j"); printf( - ".\n" - "In isolation mode, you can send SIGQUIT to the parent process to show the\n" + "\nIn isolation mode, you can send SIGQUIT to the parent process to show the\n" "current running tests, or send SIGINT to the parent process to stop all\n" "running tests.\n" "\n"); @@ -77,13 +85,37 @@ static int GtestRun(std::vector<const char*>* args) { static bool RunInIsolationMode(std::vector<const char*>& args) { // Parse arguments that can't be used in isolation mode. + bool isolation_forced = false; for (size_t i = 1; i < args.size(); ++i) { if (strcmp(args[i], "--no_isolate") == 0) { return false; + } else if (strcmp(args[i], "--force_isolate") == 0) { + // We want to make sure we prioritize --no_isolate and --gtest_list_tests. + isolation_forced = true; } else if (strcmp(args[i], "--gtest_list_tests") == 0) { return false; } } + if (!isolation_forced) { + // Check if we are running gdb/gdbserver/lldb/lldb-server. No need to be sneaky we are assuming + // no one is hiding. + pid_t ppid = getppid(); + std::string exe_path = std::string("/proc/") + std::to_string(ppid) + "/exe"; + char buf[PATH_MAX + 1]; + size_t len; + // NB We can't use things like android::base::* or std::filesystem::* due to linking + // issues. + // Since PATH_MAX is the longest a symlink can be in posix we don't need to + // deal with truncation. Anyway this isn't critical for correctness and is + // just a QOL thing so it's fine if we are wrong. + if ((len = TEMP_FAILURE_RETRY(readlink(exe_path.c_str(), buf, sizeof(buf) - 1))) > 0) { + buf[len] = '\0'; + std::string_view file(basename(buf)); + return file != "gdb" && file != "gdbserver" && file != "gdbserver64" && + file != "gdbserver32" && file != "lldb" && file != "lldb-server"; + } + // If we can't figure out what our parent was just assume we are fine to isolate. + } return true; } @@ -91,9 +123,7 @@ static bool RunInIsolationMode(std::vector<const char*>& args) { } // namespace android // Tests that override this weak function can add default arguments. -extern "C" bool __attribute__((weak)) GetInitialArgs(const char***, size_t*) { - return false; -} +extern "C" bool __attribute__((weak)) GetInitialArgs(const char***, size_t*); int IsolateMain(int argc, char** argv, char**) { std::vector<const char*> args{argv[0]}; @@ -126,7 +156,7 @@ int IsolateMain(int argc, char** argv, char**) { const char** start_args; size_t num_args; - if (GetInitialArgs(&start_args, &num_args)) { + if (GetInitialArgs != nullptr && GetInitialArgs(&start_args, &num_args)) { std::vector<const char*> initial_args; for (size_t i = 0; i < num_args; i++) { initial_args.push_back(start_args[i]); @@ -149,5 +179,9 @@ int IsolateMain(int argc, char** argv, char**) { ::testing::GTEST_FLAG(print_time) = options.print_time(); android::gtest_extras::Isolate isolate(options, child_args); - return isolate.Run(); + int return_val = isolate.Run(); + for (auto child_arg : child_args) { + free(child_arg); + } + return return_val; } @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * 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. + */ + +#pragma once + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include <android/log.h> + +#define GTEST_EXTRAS_TAG "gtest_extras" + +#define FATAL_PLOG(msg) \ + __android_log_print(ANDROID_LOG_FATAL, GTEST_EXTRAS_TAG, "%s:%d] " msg ": %s", __FILE__, \ + __LINE__, strerror(errno)); \ + abort(); + +#define FATAL_LOG(msg) \ + __android_log_print(ANDROID_LOG_FATAL, GTEST_EXTRAS_TAG, "%s:%d] " msg, __FILE__, __LINE__); \ + abort(); diff --git a/Options.cpp b/Options.cpp index bcc4b06..96842a1 100644 --- a/Options.cpp +++ b/Options.cpp @@ -15,20 +15,22 @@ */ #include <errno.h> +#include <fcntl.h> #include <stdint.h> #include <stdlib.h> #include <string.h> +#include <sys/stat.h> +#include <sys/types.h> #include <unistd.h> #include <algorithm> #include <cctype> +#include <charconv> +#include <regex> #include <string> #include <unordered_map> #include <vector> -#include <android-base/file.h> -#include <android-base/parseint.h> -#include <android-base/strings.h> #include <gtest/gtest.h> #include "Options.h" @@ -90,20 +92,16 @@ static void PrintError(const std::string& arg, std::string msg, bool from_env) { } template <typename IntType> -static bool GetNumeric(const char* arg, const char* value, IntType* numeric_value, bool from_env) { - bool result = false; - if constexpr (std::is_unsigned<IntType>::value) { - result = android::base::ParseUint<IntType>(value, numeric_value); - } else { - result = android::base::ParseInt<IntType>(value, numeric_value); - } - if (!result) { - if (errno == ERANGE) { - PrintError(arg, std::string("value overflows (") + value + ")", from_env); - } else { - PrintError(arg, std::string("value is not formatted as a numeric value (") + value + ")", - from_env); - } +static bool GetNumeric(const std::string& arg, const std::string& value, IntType* numeric_value, + bool from_env) { + auto result = std::from_chars(value.c_str(), value.c_str() + value.size(), *numeric_value, 10); + if (result.ec == std::errc::result_out_of_range) { + PrintError(arg, std::string("value overflows (") + value + ")", from_env); + return false; + } else if (result.ec == std::errc::invalid_argument || result.ptr == nullptr || + *result.ptr != '\0') { + PrintError(arg, std::string("value is not formatted as a numeric value (") + value + ")", + from_env); return false; } return true; @@ -118,7 +116,7 @@ bool Options::SetPrintTime(const std::string&, const std::string& value, bool) { bool Options::SetNumeric(const std::string& arg, const std::string& value, bool from_env) { uint64_t* numeric = &numerics_.find(arg)->second; - if (!GetNumeric<uint64_t>(arg.c_str(), value.c_str(), numeric, from_env)) { + if (!GetNumeric<uint64_t>(arg, value, numeric, from_env)) { return false; } if (*numeric == 0) { @@ -134,7 +132,7 @@ bool Options::SetNumericEnvOnly(const std::string& arg, const std::string& value return false; } uint64_t* numeric = &numerics_.find(arg)->second; - if (!GetNumeric<uint64_t>(arg.c_str(), value.c_str(), numeric, from_env)) { + if (!GetNumeric<uint64_t>(arg, value, numeric, from_env)) { return false; } return true; @@ -146,7 +144,7 @@ bool Options::SetBool(const std::string& arg, const std::string&, bool) { } bool Options::SetIterations(const std::string& arg, const std::string& value, bool from_env) { - if (!GetNumeric<int>(arg.c_str(), value.c_str(), &num_iterations_, from_env)) { + if (!GetNumeric<int>(arg, value, &num_iterations_, from_env)) { return false; } return true; @@ -214,13 +212,29 @@ bool Options::HandleArg(const std::string& arg, const std::string& value, const return true; } +static bool ReadFileToString(const std::string& file, std::string* contents) { + int fd = TEMP_FAILURE_RETRY(open(file.c_str(), O_RDONLY | O_CLOEXEC)); + if (fd == -1) { + return false; + } + char buf[4096]; + ssize_t bytes_read; + while ((bytes_read = TEMP_FAILURE_RETRY(read(fd, &buf, sizeof(buf)))) > 0) { + contents->append(buf, bytes_read); + } + close(fd); + return true; +} + bool Options::ProcessFlagfile(const std::string& file, std::vector<char*>* child_args) { std::string contents; - if (!android::base::ReadFileToString(file, &contents)) { + if (!ReadFileToString(file, &contents)) { printf("Unable to read data from file %s\n", file.c_str()); return false; } + std::regex flag_regex("^\\s*(\\S.*\\S)\\s*$"); + std::regex empty_line_regex("^\\s*$"); size_t idx = 0; while (idx < contents.size()) { size_t newline_idx = contents.find('\n', idx); @@ -229,8 +243,10 @@ bool Options::ProcessFlagfile(const std::string& file, std::vector<char*>* child } std::string line(&contents[idx], newline_idx - idx); idx = newline_idx + 1; - line = android::base::Trim(line); - if (line.empty()) { + std::smatch match; + if (std::regex_match(line, match, flag_regex)) { + line = match[1]; + } else if (std::regex_match(line, match, empty_line_regex)) { // Skip lines with only whitespace. continue; } @@ -23,10 +23,11 @@ #include <tuple> #include <vector> -#include <android-base/logging.h> +#include <android/log.h> #include <gtest/gtest.h> #include "Color.h" +#include "Log.h" #include "NanoTime.h" #include "Test.h" @@ -49,7 +50,10 @@ void Test::Stop() { } void Test::CloseFd() { - fd_.reset(); + if (fd_ != -1) { + close(fd_); + fd_ = -1; + } } void Test::Print() { @@ -85,7 +89,7 @@ bool Test::Read() { // Reading would block. Since this is not an error keep going. return true; } - PLOG(FATAL) << "Unexpected failure from read"; + FATAL_PLOG("Unexpected failure from read"); return false; } @@ -20,8 +20,6 @@ #include <string> #include <tuple> -#include <android-base/unique_fd.h> - namespace android { namespace gtest_extras { @@ -38,6 +36,7 @@ enum TestResult : uint8_t { class Test { public: Test(std::tuple<std::string, std::string>& test, size_t test_index, size_t run_index, int fd); + ~Test() { CloseFd(); } void Print(); @@ -87,7 +86,7 @@ class Test { std::string name_; size_t test_index_; // Index into test list. size_t run_index_; // Index into running list. - android::base::unique_fd fd_; + int fd_ = -1; uint64_t start_ns_; uint64_t end_ns_ = 0; |