1// Copyright (c) 2012 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/base_paths.h"
6#include "base/json/json_writer.h"
7#include "base/logging.h"
8#include "base/memory/scoped_ptr.h"
9#include "base/path_service.h"
10#include "base/strings/string_number_conversions.h"
11#include "base/strings/string_util.h"
12#include "base/strings/utf_string_conversions.h"
13#include "base/time/time.h"
14#include "base/values.h"
15#include "chrome/common/automation_messages.h"
16#include "chrome/common/chrome_switches.h"
17#include "chrome/test/automation/automation_proxy.h"
18#include "chrome/test/automation/tab_proxy.h"
19#include "chrome/test/pyautolib/pyautolib.h"
20#include "url/gurl.h"
21
22// PyUITestSuiteBase
23PyUITestSuiteBase::PyUITestSuiteBase(int argc, char** argv)
24    : UITestSuite(argc, argv) {
25}
26
27PyUITestSuiteBase::~PyUITestSuiteBase() {
28#if defined(OS_MACOSX)
29  pool_.Recycle();
30#endif
31  Shutdown();
32}
33
34void PyUITestSuiteBase::InitializeWithPath(const base::FilePath& browser_dir) {
35  SetBrowserDirectory(browser_dir);
36  UITestSuite::Initialize();
37}
38
39void PyUITestSuiteBase::SetCrSourceRoot(const base::FilePath& path) {
40  PathService::Override(base::DIR_SOURCE_ROOT, path);
41}
42
43// PyUITestBase
44PyUITestBase::PyUITestBase(bool clear_profile, std::wstring homepage)
45    : UITestBase() {
46  set_clear_profile(clear_profile);
47  set_homepage(WideToASCII(homepage));
48  // We add this so that pyauto can execute javascript in the renderer and
49  // read values back.
50  dom_automation_enabled_ = true;
51  message_loop_ = GetSharedMessageLoop(base::MessageLoop::TYPE_DEFAULT);
52}
53
54PyUITestBase::~PyUITestBase() {
55}
56
57// static, refer .h for why it needs to be static
58base::MessageLoop* PyUITestBase::message_loop_ = NULL;
59
60// static
61base::MessageLoop* PyUITestBase::GetSharedMessageLoop(
62    base::MessageLoop::Type msg_loop_type) {
63  if (!message_loop_)  // Create a shared instance of MessageLoop
64    message_loop_ = new base::MessageLoop(msg_loop_type);
65  return message_loop_;
66}
67
68void PyUITestBase::Initialize(const base::FilePath& browser_dir) {
69  UITestBase::SetBrowserDirectory(browser_dir);
70}
71
72ProxyLauncher* PyUITestBase::CreateProxyLauncher() {
73  if (named_channel_id_.empty())
74    return new AnonymousProxyLauncher(false);
75  return new NamedProxyLauncher(named_channel_id_, false, false);
76}
77
78void PyUITestBase::SetUp() {
79  UITestBase::SetUp();
80}
81
82void PyUITestBase::TearDown() {
83  UITestBase::TearDown();
84}
85
86void PyUITestBase::SetLaunchSwitches() {
87  // Clear the homepage because some of the pyauto tests don't work correctly
88  // if a URL argument is passed.
89  std::string homepage_original;
90  std::swap(homepage_original, homepage_);
91
92  UITestBase::SetLaunchSwitches();
93
94  // However, we *do* want the --homepage switch.
95  std::swap(homepage_original, homepage_);
96  launch_arguments_.AppendSwitchASCII(switches::kHomePage, homepage_);
97}
98
99AutomationProxy* PyUITestBase::automation() const {
100  AutomationProxy* automation_proxy = UITestBase::automation();
101  if (!automation_proxy) {
102    LOG(FATAL) << "The automation proxy is NULL.";
103  }
104  return automation_proxy;
105}
106
107std::string PyUITestBase::_SendJSONRequest(int window_index,
108                                           const std::string& request,
109                                           int timeout) {
110  std::string response;
111  bool success;
112  AutomationProxy* automation_sender = automation();
113  base::TimeTicks time = base::TimeTicks::Now();
114
115  if (!automation_sender) {
116    ErrorResponse("Automation proxy does not exist", request, false, &response);
117  } else if (!automation_sender->channel()) {
118    ErrorResponse("Chrome automation IPC channel was found already broken",
119                  request, false, &response);
120  } else if (!automation_sender->Send(
121      new AutomationMsg_SendJSONRequest(window_index, request, &response,
122                                        &success),
123      timeout)) {
124    RequestFailureResponse(request, base::TimeTicks::Now() - time,
125                           base::TimeDelta::FromMilliseconds(timeout),
126                           &response);
127  }
128  return response;
129}
130
131void PyUITestBase::ErrorResponse(
132    const std::string& error_string,
133    const std::string& request,
134    bool is_timeout,
135    std::string* response) {
136  base::DictionaryValue error_dict;
137  std::string error_msg = base::StringPrintf("%s for %s", error_string.c_str(),
138                                             request.c_str());
139  LOG(ERROR) << "Error during automation: " << error_msg;
140  error_dict.SetString("error", error_msg);
141  error_dict.SetBoolean("is_interface_error", true);
142  error_dict.SetBoolean("is_interface_timeout", is_timeout);
143  base::JSONWriter::Write(&error_dict, response);
144}
145
146void PyUITestBase::RequestFailureResponse(
147    const std::string& request,
148    const base::TimeDelta& duration,
149    const base::TimeDelta& timeout,
150    std::string* response) {
151  // TODO(craigdh): Determine timeout directly from IPC's Send().
152  if (duration >= timeout) {
153    ErrorResponse(
154        base::StringPrintf("Chrome automation timed out after %d seconds",
155                           static_cast<int>(duration.InSeconds())),
156        request, true, response);
157  } else {
158    // TODO(craigdh): Determine specific cause.
159    ErrorResponse(
160        "Chrome automation failed prior to timing out, did chrome crash?",
161        request, false, response);
162  }
163}
164