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 "content/public/test/browser_test_base.h"
6
7#include "base/bind.h"
8#include "base/command_line.h"
9#include "base/debug/stack_trace.h"
10#include "base/i18n/icu_util.h"
11#include "base/message_loop/message_loop.h"
12#include "base/strings/string_number_conversions.h"
13#include "base/sys_info.h"
14#include "base/test/test_timeouts.h"
15#include "content/public/app/content_main.h"
16#include "content/browser/renderer_host/render_process_host_impl.h"
17#include "content/browser/tracing/tracing_controller_impl.h"
18#include "content/public/browser/browser_thread.h"
19#include "content/public/common/content_switches.h"
20#include "content/public/common/main_function_params.h"
21#include "content/public/test/test_launcher.h"
22#include "content/public/test/test_utils.h"
23#include "net/base/net_errors.h"
24#include "net/dns/mock_host_resolver.h"
25#include "net/test/embedded_test_server/embedded_test_server.h"
26#include "ui/compositor/compositor_switches.h"
27#include "ui/gl/gl_implementation.h"
28#include "ui/gl/gl_switches.h"
29
30#if defined(OS_POSIX)
31#include "base/process/process_handle.h"
32#endif
33
34#if defined(OS_MACOSX)
35#include "base/mac/mac_util.h"
36#endif
37
38#if defined(OS_ANDROID)
39#include "base/threading/thread_restrictions.h"
40#include "content/public/browser/browser_main_runner.h"
41#include "content/public/browser/browser_thread.h"
42#endif
43
44#if defined(USE_AURA)
45#include "content/browser/compositor/image_transport_factory.h"
46#include "ui/aura/test/event_generator_delegate_aura.h"
47#if defined(USE_X11)
48#include "ui/aura/window_tree_host_x11.h"
49#endif
50#endif
51
52namespace content {
53namespace {
54
55#if defined(OS_POSIX)
56// On SIGTERM (sent by the runner on timeouts), dump a stack trace (to make
57// debugging easier) and also exit with a known error code (so that the test
58// framework considers this a failure -- http://crbug.com/57578).
59// Note: We only want to do this in the browser process, and not forked
60// processes. That might lead to hangs because of locks inside tcmalloc or the
61// OS. See http://crbug.com/141302.
62static int g_browser_process_pid;
63static void DumpStackTraceSignalHandler(int signal) {
64  if (g_browser_process_pid == base::GetCurrentProcId()) {
65    logging::RawLog(logging::LOG_ERROR,
66                    "BrowserTestBase signal handler received SIGTERM. "
67                    "Backtrace:\n");
68    base::debug::StackTrace().Print();
69  }
70  _exit(128 + signal);
71}
72#endif  // defined(OS_POSIX)
73
74void RunTaskOnRendererThread(const base::Closure& task,
75                             const base::Closure& quit_task) {
76  task.Run();
77  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, quit_task);
78}
79
80// In many cases it may be not obvious that a test makes a real DNS lookup.
81// We generally don't want to rely on external DNS servers for our tests,
82// so this host resolver procedure catches external queries and returns a failed
83// lookup result.
84class LocalHostResolverProc : public net::HostResolverProc {
85 public:
86  LocalHostResolverProc() : HostResolverProc(NULL) {}
87
88  virtual int Resolve(const std::string& host,
89                      net::AddressFamily address_family,
90                      net::HostResolverFlags host_resolver_flags,
91                      net::AddressList* addrlist,
92                      int* os_error) OVERRIDE {
93    const char* kLocalHostNames[] = {"localhost", "127.0.0.1", "::1"};
94    bool local = false;
95
96    if (host == net::GetHostName()) {
97      local = true;
98    } else {
99      for (size_t i = 0; i < arraysize(kLocalHostNames); i++)
100        if (host == kLocalHostNames[i]) {
101          local = true;
102          break;
103        }
104    }
105
106    // To avoid depending on external resources and to reduce (if not preclude)
107    // network interactions from tests, we simulate failure for non-local DNS
108    // queries, rather than perform them.
109    // If you really need to make an external DNS query, use
110    // net::RuleBasedHostResolverProc and its AllowDirectLookup method.
111    if (!local) {
112      DVLOG(1) << "To avoid external dependencies, simulating failure for "
113          "external DNS lookup of " << host;
114      return net::ERR_NOT_IMPLEMENTED;
115    }
116
117    return ResolveUsingPrevious(host, address_family, host_resolver_flags,
118                                addrlist, os_error);
119  }
120
121 private:
122  virtual ~LocalHostResolverProc() {}
123};
124
125void TraceDisableRecordingComplete(const base::Closure& quit,
126                                   const base::FilePath& file_path) {
127  LOG(ERROR) << "Tracing written to: " << file_path.value();
128  quit.Run();
129}
130
131}  // namespace
132
133extern int BrowserMain(const MainFunctionParams&);
134
135BrowserTestBase::BrowserTestBase()
136    : expected_exit_code_(0),
137      enable_pixel_output_(false),
138      use_software_compositing_(false) {
139#if defined(OS_MACOSX)
140  base::mac::SetOverrideAmIBundled(true);
141#endif
142
143#if defined(USE_AURA)
144#if defined(USE_X11)
145  aura::test::SetUseOverrideRedirectWindowByDefault(true);
146#endif
147  aura::test::InitializeAuraEventGeneratorDelegate();
148#endif
149
150#if defined(OS_POSIX)
151  handle_sigterm_ = true;
152#endif
153
154  // This is called through base::TestSuite initially. It'll also be called
155  // inside BrowserMain, so tell the code to ignore the check that it's being
156  // called more than once
157  base::i18n::AllowMultipleInitializeCallsForTesting();
158
159  embedded_test_server_.reset(new net::test_server::EmbeddedTestServer);
160}
161
162BrowserTestBase::~BrowserTestBase() {
163#if defined(OS_ANDROID)
164  // RemoteTestServer can cause wait on the UI thread.
165  base::ThreadRestrictions::ScopedAllowWait allow_wait;
166  test_server_.reset(NULL);
167#endif
168}
169
170void BrowserTestBase::SetUp() {
171  CommandLine* command_line = CommandLine::ForCurrentProcess();
172
173  // Override the child process connection timeout since tests can exceed that
174  // when sharded.
175  command_line->AppendSwitchASCII(
176      switches::kIPCConnectionTimeout,
177      base::IntToString(TestTimeouts::action_max_timeout().InSeconds()));
178
179  // The tests assume that file:// URIs can freely access other file:// URIs.
180  command_line->AppendSwitch(switches::kAllowFileAccessFromFiles);
181
182  command_line->AppendSwitch(switches::kDomAutomationController);
183
184  // It is sometimes useful when looking at browser test failures to know which
185  // GPU blacklisting decisions were made.
186  command_line->AppendSwitch(switches::kLogGpuControlListDecisions);
187
188  if (use_software_compositing_) {
189    command_line->AppendSwitch(switches::kDisableGpu);
190#if defined(USE_AURA)
191    command_line->AppendSwitch(switches::kUIDisableThreadedCompositing);
192#endif
193  }
194
195#if defined(USE_AURA)
196  // Most tests do not need pixel output, so we don't produce any. The command
197  // line can override this behaviour to allow for visual debugging.
198  if (command_line->HasSwitch(switches::kEnablePixelOutputInTests))
199    enable_pixel_output_ = true;
200
201  if (command_line->HasSwitch(switches::kDisableGLDrawingForTests)) {
202    NOTREACHED() << "kDisableGLDrawingForTests should not be used as it"
203                    "is chosen by tests. Use kEnablePixelOutputInTests "
204                    "to enable pixel output.";
205  }
206
207  // Don't enable pixel output for browser tests unless they override and force
208  // us to, or it's requested on the command line.
209  if (!enable_pixel_output_ && !use_software_compositing_)
210    command_line->AppendSwitch(switches::kDisableGLDrawingForTests);
211#endif
212
213  bool use_osmesa = true;
214
215  // We usually use OSMesa as this works on all bots. The command line can
216  // override this behaviour to use hardware GL.
217  if (command_line->HasSwitch(switches::kUseGpuInTests))
218    use_osmesa = false;
219
220  // Some bots pass this flag when they want to use hardware GL.
221  if (command_line->HasSwitch("enable-gpu"))
222    use_osmesa = false;
223
224#if defined(OS_MACOSX)
225  // On Mac we always use hardware GL.
226  use_osmesa = false;
227#endif
228
229#if defined(OS_ANDROID)
230  // On Android we always use hardware GL.
231  use_osmesa = false;
232#endif
233
234#if defined(OS_CHROMEOS)
235  // If the test is running on the chromeos envrionment (such as
236  // device or vm bots), we use hardware GL.
237  if (base::SysInfo::IsRunningOnChromeOS())
238    use_osmesa = false;
239#endif
240
241  if (use_osmesa && !use_software_compositing_)
242    command_line->AppendSwitch(switches::kOverrideUseGLWithOSMesaForTests);
243
244  scoped_refptr<net::HostResolverProc> local_resolver =
245      new LocalHostResolverProc();
246  rule_based_resolver_ =
247      new net::RuleBasedHostResolverProc(local_resolver.get());
248  rule_based_resolver_->AddSimulatedFailure("wpad");
249  net::ScopedDefaultHostResolverProc scoped_local_host_resolver_proc(
250      rule_based_resolver_.get());
251  SetUpInProcessBrowserTestFixture();
252
253  base::Closure* ui_task =
254      new base::Closure(
255          base::Bind(&BrowserTestBase::ProxyRunTestOnMainThreadLoop, this));
256
257#if defined(OS_ANDROID)
258  MainFunctionParams params(*command_line);
259  params.ui_task = ui_task;
260  // TODO(phajdan.jr): Check return code, http://crbug.com/374738 .
261  BrowserMain(params);
262#else
263  GetContentMainParams()->ui_task = ui_task;
264  EXPECT_EQ(expected_exit_code_, ContentMain(*GetContentMainParams()));
265#endif
266  TearDownInProcessBrowserTestFixture();
267}
268
269void BrowserTestBase::TearDown() {
270}
271
272void BrowserTestBase::ProxyRunTestOnMainThreadLoop() {
273#if defined(OS_POSIX)
274  if (handle_sigterm_) {
275    g_browser_process_pid = base::GetCurrentProcId();
276    signal(SIGTERM, DumpStackTraceSignalHandler);
277  }
278#endif  // defined(OS_POSIX)
279
280  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableTracing)) {
281    base::debug::CategoryFilter category_filter(
282        CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
283            switches::kEnableTracing));
284    TracingController::GetInstance()->EnableRecording(
285        category_filter,
286        base::debug::TraceOptions(base::debug::RECORD_CONTINUOUSLY),
287        TracingController::EnableRecordingDoneCallback());
288  }
289
290  RunTestOnMainThreadLoop();
291
292  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableTracing)) {
293    base::FilePath trace_file =
294        CommandLine::ForCurrentProcess()->GetSwitchValuePath(
295            switches::kEnableTracingOutput);
296    // If there was no file specified, put a hardcoded one in the current
297    // working directory.
298    if (trace_file.empty())
299      trace_file = base::FilePath().AppendASCII("trace.json");
300
301    // Wait for tracing to collect results from the renderers.
302    base::RunLoop run_loop;
303    TracingController::GetInstance()->DisableRecording(
304        TracingControllerImpl::CreateFileSink(
305            trace_file,
306            base::Bind(&TraceDisableRecordingComplete,
307                       run_loop.QuitClosure(),
308                       trace_file)));
309    run_loop.Run();
310  }
311}
312
313void BrowserTestBase::CreateTestServer(const base::FilePath& test_server_base) {
314  CHECK(!test_server_.get());
315  test_server_.reset(new net::SpawnedTestServer(
316      net::SpawnedTestServer::TYPE_HTTP,
317      net::SpawnedTestServer::kLocalhost,
318      test_server_base));
319}
320
321void BrowserTestBase::PostTaskToInProcessRendererAndWait(
322    const base::Closure& task) {
323  CHECK(CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess));
324
325  scoped_refptr<MessageLoopRunner> runner = new MessageLoopRunner;
326
327  base::MessageLoop* renderer_loop =
328      RenderProcessHostImpl::GetInProcessRendererThreadForTesting();
329  CHECK(renderer_loop);
330
331  renderer_loop->PostTask(
332      FROM_HERE,
333      base::Bind(&RunTaskOnRendererThread, task, runner->QuitClosure()));
334  runner->Run();
335}
336
337void BrowserTestBase::EnablePixelOutput() { enable_pixel_output_ = true; }
338
339void BrowserTestBase::UseSoftwareCompositing() {
340  use_software_compositing_ = true;
341}
342
343bool BrowserTestBase::UsingOSMesa() const {
344  CommandLine* cmd = CommandLine::ForCurrentProcess();
345  return cmd->GetSwitchValueASCII(switches::kUseGL) ==
346         gfx::kGLImplementationOSMesaName;
347}
348
349}  // namespace content
350