1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/test/test_suite.h"
6
7#include "base/at_exit.h"
8#include "base/base_paths.h"
9#include "base/base_switches.h"
10#include "base/command_line.h"
11#include "base/debug/debug_on_start_win.h"
12#include "base/debug/debugger.h"
13#include "base/file_path.h"
14#include "base/i18n/icu_util.h"
15#include "base/logging.h"
16#include "base/mac/scoped_nsautorelease_pool.h"
17#include "base/memory/scoped_ptr.h"
18#include "base/path_service.h"
19#include "base/process_util.h"
20#include "base/test/multiprocess_test.h"
21#include "base/test/test_timeouts.h"
22#include "base/time.h"
23#include "testing/gtest/include/gtest/gtest.h"
24#include "testing/multiprocess_func_list.h"
25
26#if defined(OS_MACOSX)
27#include "base/test/mock_chrome_application_mac.h"
28#endif
29
30#if defined(TOOLKIT_USES_GTK)
31#include <gtk/gtk.h>
32#endif
33
34namespace {
35
36class MaybeTestDisabler : public testing::EmptyTestEventListener {
37 public:
38  virtual void OnTestStart(const testing::TestInfo& test_info) {
39    ASSERT_FALSE(TestSuite::IsMarkedMaybe(test_info))
40        << "Probably the OS #ifdefs don't include all of the necessary "
41           "platforms.\nPlease ensure that no tests have the MAYBE_ prefix "
42           "after the code is preprocessed.";
43  }
44};
45
46}  // namespace
47
48const char TestSuite::kStrictFailureHandling[] = "strict_failure_handling";
49
50TestSuite::TestSuite(int argc, char** argv) {
51#if defined(OS_WIN)
52  testing::GTEST_FLAG(catch_exceptions) = false;
53#endif
54  base::EnableTerminationOnHeapCorruption();
55  CommandLine::Init(argc, argv);
56  testing::InitGoogleTest(&argc, argv);
57#if defined(TOOLKIT_USES_GTK)
58  g_thread_init(NULL);
59  gtk_init_check(&argc, &argv);
60#endif  // defined(TOOLKIT_USES_GTK)
61  // Don't add additional code to this constructor.  Instead add it to
62  // Initialize().  See bug 6436.
63}
64
65TestSuite::~TestSuite() {
66  CommandLine::Reset();
67}
68
69// static
70bool TestSuite::IsMarkedFlaky(const testing::TestInfo& test) {
71  return strncmp(test.name(), "FLAKY_", 6) == 0;
72}
73
74// static
75bool TestSuite::IsMarkedFailing(const testing::TestInfo& test) {
76  return strncmp(test.name(), "FAILS_", 6) == 0;
77}
78
79// static
80bool TestSuite::IsMarkedMaybe(const testing::TestInfo& test) {
81  return strncmp(test.name(), "MAYBE_", 6) == 0;
82}
83
84// static
85bool TestSuite::ShouldIgnoreFailure(const testing::TestInfo& test) {
86  if (CommandLine::ForCurrentProcess()->HasSwitch(kStrictFailureHandling))
87    return false;
88  return IsMarkedFlaky(test) || IsMarkedFailing(test);
89}
90
91// static
92bool TestSuite::NonIgnoredFailures(const testing::TestInfo& test) {
93  return test.should_run() && test.result()->Failed() &&
94      !ShouldIgnoreFailure(test);
95}
96
97int TestSuite::GetTestCount(TestMatch test_match) {
98  testing::UnitTest* instance = testing::UnitTest::GetInstance();
99  int count = 0;
100
101  for (int i = 0; i < instance->total_test_case_count(); ++i) {
102    const testing::TestCase& test_case = *instance->GetTestCase(i);
103    for (int j = 0; j < test_case.total_test_count(); ++j) {
104      if (test_match(*test_case.GetTestInfo(j))) {
105        count++;
106      }
107    }
108  }
109
110  return count;
111}
112
113void TestSuite::CatchMaybeTests() {
114  testing::TestEventListeners& listeners =
115      testing::UnitTest::GetInstance()->listeners();
116  listeners.Append(new MaybeTestDisabler);
117}
118
119// Don't add additional code to this method.  Instead add it to
120// Initialize().  See bug 6436.
121int TestSuite::Run() {
122  base::mac::ScopedNSAutoreleasePool scoped_pool;
123
124  Initialize();
125  std::string client_func =
126      CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
127          switches::kTestChildProcess);
128  // Check to see if we are being run as a client process.
129  if (!client_func.empty())
130    return multi_process_function_list::InvokeChildProcessTest(client_func);
131  int result = RUN_ALL_TESTS();
132
133  // If there are failed tests, see if we should ignore the failures.
134  if (result != 0 && GetTestCount(&TestSuite::NonIgnoredFailures) == 0)
135    result = 0;
136
137  // Display the number of flaky tests.
138  int flaky_count = GetTestCount(&TestSuite::IsMarkedFlaky);
139  if (flaky_count) {
140    printf("  YOU HAVE %d FLAKY %s\n\n", flaky_count,
141           flaky_count == 1 ? "TEST" : "TESTS");
142  }
143
144  // Display the number of tests with ignored failures (FAILS).
145  int failing_count = GetTestCount(&TestSuite::IsMarkedFailing);
146  if (failing_count) {
147    printf("  YOU HAVE %d %s with ignored failures (FAILS prefix)\n\n",
148           failing_count, failing_count == 1 ? "test" : "tests");
149  }
150
151  // This MUST happen before Shutdown() since Shutdown() tears down
152  // objects (such as NotificationService::current()) that Cocoa
153  // objects use to remove themselves as observers.
154  scoped_pool.Recycle();
155
156  Shutdown();
157
158  return result;
159}
160
161// static
162void TestSuite::UnitTestAssertHandler(const std::string& str) {
163  RAW_LOG(FATAL, str.c_str());
164}
165
166void TestSuite::SuppressErrorDialogs() {
167#if defined(OS_WIN)
168  UINT new_flags = SEM_FAILCRITICALERRORS |
169                   SEM_NOGPFAULTERRORBOX |
170                   SEM_NOOPENFILEERRORBOX;
171
172  // Preserve existing error mode, as discussed at
173  // http://blogs.msdn.com/oldnewthing/archive/2004/07/27/198410.aspx
174  UINT existing_flags = SetErrorMode(new_flags);
175  SetErrorMode(existing_flags | new_flags);
176#endif  // defined(OS_WIN)
177}
178
179void TestSuite::Initialize() {
180#if defined(OS_MACOSX)
181  // Some of the app unit tests spin runloops.
182  mock_cr_app::RegisterMockCrApp();
183#endif
184
185  // Initialize logging.
186  FilePath exe;
187  PathService::Get(base::FILE_EXE, &exe);
188  FilePath log_filename = exe.ReplaceExtension(FILE_PATH_LITERAL("log"));
189  logging::InitLogging(
190      log_filename.value().c_str(),
191      logging::LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG,
192      logging::LOCK_LOG_FILE,
193      logging::DELETE_OLD_LOG_FILE,
194      logging::DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS);
195  // We want process and thread IDs because we may have multiple processes.
196  // Note: temporarily enabled timestamps in an effort to catch bug 6361.
197  logging::SetLogItems(true, true, true, true);
198
199  CHECK(base::EnableInProcessStackDumping());
200#if defined(OS_WIN)
201  // Make sure we run with high resolution timer to minimize differences
202  // between production code and test code.
203  base::Time::EnableHighResolutionTimer(true);
204#endif  // defined(OS_WIN)
205
206  // In some cases, we do not want to see standard error dialogs.
207  if (!base::debug::BeingDebugged() &&
208      !CommandLine::ForCurrentProcess()->HasSwitch("show-error-dialogs")) {
209    SuppressErrorDialogs();
210    base::debug::SetSuppressDebugUI(true);
211    logging::SetLogAssertHandler(UnitTestAssertHandler);
212  }
213
214  icu_util::Initialize();
215
216  CatchMaybeTests();
217
218  TestTimeouts::Initialize();
219}
220
221void TestSuite::Shutdown() {
222}
223