Add the ability to run each test in isolation.
This adds a new gtest main that will run each test in its own forked
process. This can avoid problems where some tests do not clean up after
themselves, or a test modifies global state that can change the behavior
of another test.
This is only supported on device and linux.
Test: Passes extensive unit tests.
Change-Id: Iab29bf60b9f5cd9c1af67e85f502ada7efe24b18
diff --git a/IsolateMain.cpp b/IsolateMain.cpp
new file mode 100644
index 0000000..b25fc9c
--- /dev/null
+++ b/IsolateMain.cpp
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+#include <gtest_extras/IsolateMain.h>
+
+#include "Color.h"
+#include "Isolate.h"
+
+namespace android {
+namespace gtest_extras {
+
+static void PrintHelpInfo() {
+ printf("Unit Test Options:\n");
+ ColoredPrintf(COLOR_GREEN, " -j ");
+ ColoredPrintf(COLOR_YELLOW, "[JOB_COUNT]");
+ printf(" or ");
+ ColoredPrintf(COLOR_GREEN, "-j");
+ ColoredPrintf(COLOR_YELLOW, "[JOB_COUNT]\n");
+ printf(
+ " Run up to JOB_COUNT tests in parallel.\n"
+ " 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");
+ ColoredPrintf(COLOR_GREEN, " --deadline_threshold_ms=");
+ ColoredPrintf(COLOR_YELLOW, "[TIME_IN_MS]\n");
+ printf(" Run each test in no longer than ");
+ ColoredPrintf(COLOR_YELLOW, "[TIME_IN_MS]");
+ printf(
+ " time.\n"
+ " Only valid in isolation mode. Default deadline is 90000 ms.\n");
+ ColoredPrintf(COLOR_GREEN, " --slow_threshold_ms=");
+ ColoredPrintf(COLOR_YELLOW, "[TIME_IN_MS]\n");
+ printf(" Test running longer than ");
+ ColoredPrintf(COLOR_YELLOW, "[TIME_IN_MS]");
+ printf(
+ " will be called slow.\n"
+ " Only valid in isolation mode. Default slow threshold is 2000 ms.\n");
+ ColoredPrintf(COLOR_GREEN, " --gtest_format\n");
+ printf(
+ " Use the default gtest format, not the enhanced format.\n"
+ "\n"
+ "Default test option is ");
+ ColoredPrintf(COLOR_GREEN, "-j");
+ printf(
+ ".\n"
+ "In 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");
+}
+
+static int GtestRun(std::vector<const char*>* args) {
+ int argc = args->size();
+ args->push_back(nullptr);
+ ::testing::InitGoogleTest(&argc, const_cast<char**>(args->data()));
+ return RUN_ALL_TESTS();
+}
+
+static bool RunInIsolationMode(std::vector<const char*>& args) {
+ // Parse arguments that can't be used in isolation mode.
+ for (size_t i = 1; i < args.size(); ++i) {
+ if (strcmp(args[i], "--no_isolate") == 0) {
+ return false;
+ } else if (strcmp(args[i], "--gtest_list_tests") == 0) {
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace gtest_extras
+} // namespace android
+
+// Tests that override this weak function can add default arguments.
+extern "C" bool __attribute__((weak)) GetInitialArgs(const char***, size_t*) {
+ return false;
+}
+
+int IsolateMain(int argc, char** argv, char**) {
+ std::vector<const char*> args{argv[0]};
+
+ bool print_help = false;
+ size_t gtest_color_index = 0;
+ for (int i = 1; i < argc; ++i) {
+ args.push_back(argv[i]);
+ if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
+ print_help = true;
+ } else if (strncmp(argv[i], "--gtest_color=", 14) == 0) {
+ gtest_color_index = args.size() - 1;
+ }
+ }
+
+ if (print_help) {
+ std::vector<const char*> help_args{args[0], "--help"};
+ if (gtest_color_index != 0) {
+ // This is the only option that changes the way the help is printed.
+ help_args.push_back(args[gtest_color_index]);
+ ::testing::GTEST_FLAG(color) = args[gtest_color_index] + 14;
+ }
+ android::gtest_extras::PrintHelpInfo();
+ return android::gtest_extras::GtestRun(&help_args);
+ }
+
+ if (!android::gtest_extras::RunInIsolationMode(args)) {
+ return android::gtest_extras::GtestRun(&args);
+ }
+
+ const char** start_args;
+ size_t num_args;
+ if (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]);
+ }
+ args.insert(args.begin() + 1, initial_args.begin(), initial_args.end());
+ }
+
+ // To run a DeathTest in threadsafe mode, gtest requires that the user must
+ // invoke the test program directly, not by running it from the path.
+ // This is because gtest uses clone() + execve() to run a DeathTest() and
+ // execve() doesn't search the path to execute.
+ std::vector<const char*> child_args;
+ std::string exec_path; // Need to be scoped through the entire function.
+ if (strchr(args[0], '/') == nullptr) {
+ exec_path = android::base::GetExecutablePath();
+ child_args.push_back(exec_path.c_str());
+ } else {
+ child_args.push_back(args[0]);
+ }
+
+ android::gtest_extras::Options options;
+ if (!options.Process(args, &child_args)) {
+ return 1;
+ }
+
+ // Add the --no_isolate option to force child processes not to rerun
+ // in isolation mode.
+ child_args.push_back("--no_isolate");
+
+ // Set the flag values.
+ ::testing::GTEST_FLAG(color) = options.color();
+ ::testing::GTEST_FLAG(print_time) = options.print_time();
+
+ android::gtest_extras::Isolate isolate(options, child_args);
+ return isolate.Run();
+}