Commit f887493a authored by klm@google.com's avatar klm@google.com

Logging API in chromedriver2.

Review URL: https://chromiumcodereview.appspot.com/14263024

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@195198 0039d316-1c4b-4281-b951-d872f2087c98
parent caab5725
...@@ -783,6 +783,8 @@ ...@@ -783,6 +783,8 @@
'test/chromedriver/chrome/devtools_client_impl.cc', 'test/chromedriver/chrome/devtools_client_impl.cc',
'test/chromedriver/chrome/devtools_client_impl.h', 'test/chromedriver/chrome/devtools_client_impl.h',
'test/chromedriver/chrome/devtools_event_listener.h', 'test/chromedriver/chrome/devtools_event_listener.h',
'test/chromedriver/chrome/devtools_event_logger.h',
'test/chromedriver/chrome/devtools_event_logger.cc',
'test/chromedriver/chrome/devtools_http_client.cc', 'test/chromedriver/chrome/devtools_http_client.cc',
'test/chromedriver/chrome/devtools_http_client.h', 'test/chromedriver/chrome/devtools_http_client.h',
'test/chromedriver/chrome/dom_tracker.cc', 'test/chromedriver/chrome/dom_tracker.cc',
...@@ -941,6 +943,8 @@ ...@@ -941,6 +943,8 @@
'test/chromedriver/keycode_text_conversion_mac.mm', 'test/chromedriver/keycode_text_conversion_mac.mm',
'test/chromedriver/keycode_text_conversion_win.cc', 'test/chromedriver/keycode_text_conversion_win.cc',
'test/chromedriver/keycode_text_conversion_x.cc', 'test/chromedriver/keycode_text_conversion_x.cc',
'test/chromedriver/logging.cc',
'test/chromedriver/logging.h',
'test/chromedriver/session.cc', 'test/chromedriver/session.cc',
'test/chromedriver/session.h', 'test/chromedriver/session.h',
'test/chromedriver/session_commands.cc', 'test/chromedriver/session_commands.cc',
...@@ -1024,6 +1028,7 @@ ...@@ -1024,6 +1028,7 @@
'test/chromedriver/capabilities_unittest.cc', 'test/chromedriver/capabilities_unittest.cc',
'test/chromedriver/chrome/chrome_finder_unittest.cc', 'test/chromedriver/chrome/chrome_finder_unittest.cc',
'test/chromedriver/chrome/devtools_client_impl_unittest.cc', 'test/chromedriver/chrome/devtools_client_impl_unittest.cc',
'test/chromedriver/chrome/devtools_event_logger_unittest.cc',
'test/chromedriver/chrome/devtools_http_client_unittest.cc', 'test/chromedriver/chrome/devtools_http_client_unittest.cc',
'test/chromedriver/chrome/dom_tracker_unittest.cc', 'test/chromedriver/chrome/dom_tracker_unittest.cc',
'test/chromedriver/chrome/frame_tracker_unittest.cc', 'test/chromedriver/chrome/frame_tracker_unittest.cc',
...@@ -1044,6 +1049,7 @@ ...@@ -1044,6 +1049,7 @@
'test/chromedriver/commands_unittest.cc', 'test/chromedriver/commands_unittest.cc',
'test/chromedriver/fake_session_accessor.cc', 'test/chromedriver/fake_session_accessor.cc',
'test/chromedriver/fake_session_accessor.h', 'test/chromedriver/fake_session_accessor.h',
'test/chromedriver/logging_unittest.cc',
'test/chromedriver/server/http_handler_unittest.cc', 'test/chromedriver/server/http_handler_unittest.cc',
'test/chromedriver/server/http_response_unittest.cc', 'test/chromedriver/server/http_response_unittest.cc',
'test/chromedriver/session_commands_unittest.cc', 'test/chromedriver/session_commands_unittest.cc',
......
...@@ -201,6 +201,21 @@ Status ParseAndroidChromeCapabilities(const base::DictionaryValue& desired_caps, ...@@ -201,6 +201,21 @@ Status ParseAndroidChromeCapabilities(const base::DictionaryValue& desired_caps,
return Status(kOk); return Status(kOk);
} }
Status ParseLoggingPrefs(const base::DictionaryValue& desired_caps,
Capabilities* capabilities) {
const base::Value* logging_prefs = NULL;
if (desired_caps.Get("loggingPrefs", &logging_prefs)) {
const base::DictionaryValue* logging_prefs_dict = NULL;
if (!logging_prefs->GetAsDictionary(&logging_prefs_dict)) {
return Status(kUnknownError, "'loggingPrefs' must be a dictionary");
}
// TODO(klm): verify log types.
// TODO(klm): verify log levels.
capabilities->logging_prefs.reset(logging_prefs_dict->DeepCopy());
}
return Status(kOk);
}
} // namespace } // namespace
Capabilities::Capabilities() : command(CommandLine::NO_PROGRAM) {} Capabilities::Capabilities() : command(CommandLine::NO_PROGRAM) {}
...@@ -212,7 +227,10 @@ bool Capabilities::IsAndroid() const { ...@@ -212,7 +227,10 @@ bool Capabilities::IsAndroid() const {
} }
Status Capabilities::Parse(const base::DictionaryValue& desired_caps) { Status Capabilities::Parse(const base::DictionaryValue& desired_caps) {
Status status = ParseAndroidChromeCapabilities(desired_caps, this); Status status = ParseLoggingPrefs(desired_caps, this);
if (status.IsError())
return status;
status = ParseAndroidChromeCapabilities(desired_caps, this);
if (status.IsError()) if (status.IsError())
return status; return status;
if (IsAndroid()) if (IsAndroid())
......
...@@ -34,6 +34,7 @@ struct Capabilities { ...@@ -34,6 +34,7 @@ struct Capabilities {
scoped_ptr<base::DictionaryValue> prefs; scoped_ptr<base::DictionaryValue> prefs;
scoped_ptr<base::DictionaryValue> local_state; scoped_ptr<base::DictionaryValue> local_state;
std::vector<std::string> extensions; std::vector<std::string> extensions;
scoped_ptr<base::DictionaryValue> logging_prefs;
}; };
#endif // CHROME_TEST_CHROMEDRIVER_CAPABILITIES_H_ #endif // CHROME_TEST_CHROMEDRIVER_CAPABILITIES_H_
...@@ -252,3 +252,27 @@ TEST(ParseCapabilities, MissingSettingForManualProxy) { ...@@ -252,3 +252,27 @@ TEST(ParseCapabilities, MissingSettingForManualProxy) {
Status status = capabilities.Parse(caps); Status status = capabilities.Parse(caps);
ASSERT_FALSE(status.IsOk()); ASSERT_FALSE(status.IsOk());
} }
TEST(ParseCapabilities, LoggingPrefsOk) {
Capabilities capabilities;
base::DictionaryValue logging_prefs;
logging_prefs.SetString("Network", "INFO");
base::DictionaryValue caps;
caps.Set("loggingPrefs", logging_prefs.DeepCopy());
Status status = capabilities.Parse(caps);
ASSERT_TRUE(status.IsOk());
ASSERT_TRUE(capabilities.logging_prefs.get());
ASSERT_EQ(1u, capabilities.logging_prefs->size());
std::string log_level;
ASSERT_TRUE(capabilities.logging_prefs->GetString("Network", &log_level));
ASSERT_STREQ("INFO", log_level.c_str());
}
TEST(ParseCapabilities, LoggingPrefsNotDict) {
Capabilities capabilities;
base::DictionaryValue caps;
caps.SetString("loggingPrefs", "INFO");
Status status = capabilities.Parse(caps);
ASSERT_FALSE(status.IsOk());
}
...@@ -10,8 +10,9 @@ ...@@ -10,8 +10,9 @@
ChromeAndroidImpl::ChromeAndroidImpl( ChromeAndroidImpl::ChromeAndroidImpl(
scoped_ptr<DevToolsHttpClient> client, scoped_ptr<DevToolsHttpClient> client,
const std::string& version, const std::string& version,
int build_no) int build_no,
: ChromeImpl(client.Pass(), version, build_no) {} const std::list<DevToolsEventLogger*>& devtools_event_loggers)
: ChromeImpl(client.Pass(), version, build_no, devtools_event_loggers) {}
ChromeAndroidImpl::~ChromeAndroidImpl() {} ChromeAndroidImpl::~ChromeAndroidImpl() {}
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#ifndef CHROME_TEST_CHROMEDRIVER_CHROME_CHROME_ANDROID_IMPL_H_ #ifndef CHROME_TEST_CHROMEDRIVER_CHROME_CHROME_ANDROID_IMPL_H_
#define CHROME_TEST_CHROMEDRIVER_CHROME_CHROME_ANDROID_IMPL_H_ #define CHROME_TEST_CHROMEDRIVER_CHROME_CHROME_ANDROID_IMPL_H_
#include <list>
#include <string> #include <string>
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
...@@ -15,9 +16,11 @@ class DevToolsHttpClient; ...@@ -15,9 +16,11 @@ class DevToolsHttpClient;
class ChromeAndroidImpl : public ChromeImpl { class ChromeAndroidImpl : public ChromeImpl {
public: public:
ChromeAndroidImpl(scoped_ptr<DevToolsHttpClient> client, ChromeAndroidImpl(
const std::string& version, scoped_ptr<DevToolsHttpClient> client,
int build_no); const std::string& version,
int build_no,
const std::list<DevToolsEventLogger*>& devtools_event_loggers);
virtual ~ChromeAndroidImpl(); virtual ~ChromeAndroidImpl();
// Overridden from Chrome: // Overridden from Chrome:
......
...@@ -20,10 +20,11 @@ ChromeDesktopImpl::ChromeDesktopImpl( ...@@ -20,10 +20,11 @@ ChromeDesktopImpl::ChromeDesktopImpl(
scoped_ptr<DevToolsHttpClient> client, scoped_ptr<DevToolsHttpClient> client,
const std::string& version, const std::string& version,
int build_no, int build_no,
const std::list<DevToolsEventLogger*>& devtools_event_loggers,
base::ProcessHandle process, base::ProcessHandle process,
base::ScopedTempDir* user_data_dir, base::ScopedTempDir* user_data_dir,
base::ScopedTempDir* extension_dir) base::ScopedTempDir* extension_dir)
: ChromeImpl(client.Pass(), version, build_no), : ChromeImpl(client.Pass(), version, build_no, devtools_event_loggers),
process_(process) { process_(process) {
if (user_data_dir->IsValid()) if (user_data_dir->IsValid())
CHECK(user_data_dir_.Set(user_data_dir->Take())); CHECK(user_data_dir_.Set(user_data_dir->Take()));
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#ifndef CHROME_TEST_CHROMEDRIVER_CHROME_CHROME_DESKTOP_IMPL_H_ #ifndef CHROME_TEST_CHROMEDRIVER_CHROME_CHROME_DESKTOP_IMPL_H_
#define CHROME_TEST_CHROMEDRIVER_CHROME_CHROME_DESKTOP_IMPL_H_ #define CHROME_TEST_CHROMEDRIVER_CHROME_CHROME_DESKTOP_IMPL_H_
#include <list>
#include <string> #include <string>
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
...@@ -22,6 +23,7 @@ class ChromeDesktopImpl : public ChromeImpl { ...@@ -22,6 +23,7 @@ class ChromeDesktopImpl : public ChromeImpl {
scoped_ptr<DevToolsHttpClient> client, scoped_ptr<DevToolsHttpClient> client,
const std::string& version, const std::string& version,
int build_no, int build_no,
const std::list<DevToolsEventLogger*>& devtools_event_loggers,
base::ProcessHandle process, base::ProcessHandle process,
base::ScopedTempDir* user_data_dir, base::ScopedTempDir* user_data_dir,
base::ScopedTempDir* extension_dir); base::ScopedTempDir* extension_dir);
......
...@@ -4,7 +4,9 @@ ...@@ -4,7 +4,9 @@
#include "chrome/test/chromedriver/chrome/chrome_impl.h" #include "chrome/test/chromedriver/chrome/chrome_impl.h"
#include "base/logging.h"
#include "chrome/test/chromedriver/chrome/devtools_client.h" #include "chrome/test/chromedriver/chrome/devtools_client.h"
#include "chrome/test/chromedriver/chrome/devtools_event_logger.h"
#include "chrome/test/chromedriver/chrome/devtools_http_client.h" #include "chrome/test/chromedriver/chrome/devtools_http_client.h"
#include "chrome/test/chromedriver/chrome/javascript_dialog_manager.h" #include "chrome/test/chromedriver/chrome/javascript_dialog_manager.h"
#include "chrome/test/chromedriver/chrome/status.h" #include "chrome/test/chromedriver/chrome/status.h"
...@@ -53,8 +55,16 @@ Status ChromeImpl::GetWebViewIds(std::list<std::string>* web_view_ids) { ...@@ -53,8 +55,16 @@ Status ChromeImpl::GetWebViewIds(std::list<std::string>* web_view_ids) {
} }
} }
if (!found) { if (!found) {
scoped_ptr<DevToolsClient> client(
devtools_http_client_->CreateClient(view.id));
for (std::list<DevToolsEventLogger*>::const_iterator logger =
devtools_event_loggers_.begin();
logger != devtools_event_loggers_.end(); ++logger) {
client->AddListener(*logger);
// Logger's OnConnected will fire when DevToolsClient connects later.
}
web_views_.push_back(make_linked_ptr(new WebViewImpl( web_views_.push_back(make_linked_ptr(new WebViewImpl(
view.id, devtools_http_client_->CreateClient(view.id)))); view.id, client.Pass())));
} }
} }
...@@ -131,10 +141,12 @@ Status ChromeImpl::GetAutomationExtension(AutomationExtension** extension) { ...@@ -131,10 +141,12 @@ Status ChromeImpl::GetAutomationExtension(AutomationExtension** extension) {
ChromeImpl::ChromeImpl( ChromeImpl::ChromeImpl(
scoped_ptr<DevToolsHttpClient> client, scoped_ptr<DevToolsHttpClient> client,
const std::string& version, const std::string& version,
int build_no) int build_no,
const std::list<DevToolsEventLogger*>& devtools_event_loggers)
: devtools_http_client_(client.Pass()), : devtools_http_client_(client.Pass()),
version_(version), version_(version),
build_no_(build_no) {} build_no_(build_no),
devtools_event_loggers_(devtools_event_loggers) {}
Status ChromeImpl::GetDialogManagerForOpenDialog( Status ChromeImpl::GetDialogManagerForOpenDialog(
JavaScriptDialogManager** manager) { JavaScriptDialogManager** manager) {
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "chrome/test/chromedriver/chrome/chrome.h" #include "chrome/test/chromedriver/chrome/chrome.h"
class AutomationExtension; class AutomationExtension;
class DevToolsEventLogger;
class DevToolsHttpClient; class DevToolsHttpClient;
class JavaScriptDialogManager; class JavaScriptDialogManager;
class Status; class Status;
...@@ -42,7 +43,8 @@ class ChromeImpl : public Chrome { ...@@ -42,7 +43,8 @@ class ChromeImpl : public Chrome {
protected: protected:
ChromeImpl(scoped_ptr<DevToolsHttpClient> client, ChromeImpl(scoped_ptr<DevToolsHttpClient> client,
const std::string& version, const std::string& version,
int build_no); int build_no,
const std::list<DevToolsEventLogger*>& devtools_event_loggers);
scoped_ptr<DevToolsHttpClient> devtools_http_client_; scoped_ptr<DevToolsHttpClient> devtools_http_client_;
...@@ -56,6 +58,7 @@ class ChromeImpl : public Chrome { ...@@ -56,6 +58,7 @@ class ChromeImpl : public Chrome {
// Web views in this list are in the same order as they are opened. // Web views in this list are in the same order as they are opened.
WebViewList web_views_; WebViewList web_views_;
std::list<DevToolsEventLogger*> devtools_event_loggers_;
}; };
#endif // CHROME_TEST_CHROMEDRIVER_CHROME_CHROME_IMPL_H_ #endif // CHROME_TEST_CHROMEDRIVER_CHROME_CHROME_IMPL_H_
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stdint.h>
#include "base/basictypes.h"
#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/string_util.h"
#include "base/time.h"
#include "base/values.h"
#include "chrome/test/chromedriver/chrome/devtools_client.h"
#include "chrome/test/chromedriver/chrome/devtools_event_logger.h"
#include "chrome/test/chromedriver/chrome/status.h"
DevToolsEventLogger::DevToolsEventLogger(
const std::string& log_type,
const std::vector<std::string>& domains,
const std::string& logging_level)
: log_type_(log_type),
domains_(domains),
logging_level_(logging_level),
log_entries_(new base::ListValue()) {
VLOG(1) << "DevToolsEventLogger(" <<
log_type_ << ", " << logging_level_ << ")";
}
DevToolsEventLogger::~DevToolsEventLogger() {
VLOG(1) << "Log type '" << log_type_ <<
"' lost " << log_entries_->GetSize() << " entries on destruction";
}
const std::string& DevToolsEventLogger::GetLogType() {
return log_type_;
}
static const std::string GetDomainEnableCommand(const std::string& domain) {
if ("Timeline" == domain) {
return "Timeline.start";
} else {
return domain + ".enable";
}
}
Status DevToolsEventLogger::OnConnected(DevToolsClient* client) {
base::DictionaryValue params; // All our enable commands have empty params.
scoped_ptr<base::DictionaryValue> result;
for (std::vector<std::string>::const_iterator domain = domains_.begin();
domain != domains_.end(); ++domain) {
const std::string command = GetDomainEnableCommand(*domain);
VLOG(1) << "Log type '" << log_type_ << "' sending command: " << command;
Status status = client->SendCommandAndGetResult(command, params, &result);
// The client may or may not be connected, e.g. from AddDevToolsClient().
if (status.IsError() && status.code() != kDisconnected) {
return status;
}
}
return Status(kOk);
}
scoped_ptr<base::ListValue> DevToolsEventLogger::GetAndClearLogEntries() {
scoped_ptr<base::ListValue> ret(log_entries_.release());
log_entries_.reset(new base::ListValue());
return ret.Pass();
}
bool DevToolsEventLogger::ShouldLogEvent(const std::string& method) {
for (std::vector<std::string>::const_iterator domain = domains_.begin();
domain != domains_.end(); ++domain) {
size_t prefix_len = domain->length();
if (method.length() > prefix_len && method[prefix_len] == '.' &&
StartsWithASCII(method, *domain, true)) {
return true;
}
}
return false;
}
scoped_ptr<DictionaryValue> DevToolsEventLogger::GetLogEntry(
DevToolsClient* client,
const std::string& method,
const base::DictionaryValue& params) {
// Get the log event timestamp ASAP. TODO(klm): extract from params?
double timestamp_epoch_ms = base::Time::Now().ToJsTime();
// Form JSON with a writer, verified same performance as concatenation.
base::DictionaryValue log_message_dict;
log_message_dict.SetString("webview", client->GetId());
log_message_dict.SetString("message.method", method);
log_message_dict.Set("message.params", params.DeepCopy());
std::string log_message_json;
base::JSONWriter::Write(&log_message_dict, &log_message_json);
scoped_ptr<base::DictionaryValue> log_entry_dict(new base::DictionaryValue());
log_entry_dict->SetDouble("timestamp",
static_cast<int64>(timestamp_epoch_ms));
log_entry_dict->SetString("level", logging_level_);
log_entry_dict->SetString("message", log_message_json);
return log_entry_dict.Pass();
}
void DevToolsEventLogger::OnEvent(DevToolsClient* client,
const std::string& method,
const base::DictionaryValue& params) {
if (!ShouldLogEvent(method)) {
return;
}
scoped_ptr<DictionaryValue> entry = GetLogEntry(client, method, params);
log_entries_->Append(entry.release());
}
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_TEST_CHROMEDRIVER_CHROME_DEVTOOLS_EVENT_LOGGER_H_
#define CHROME_TEST_CHROMEDRIVER_CHROME_DEVTOOLS_EVENT_LOGGER_H_
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "base/values.h"
#include "chrome/test/chromedriver/chrome/devtools_event_listener.h"
#include "chrome/test/chromedriver/chrome/status.h"
// Accumulates DevTools events of a given type for use in the Logging API.
// Tracks all WebViews of a given Chrome, via their respective DevToolsClients.
//
// A log message has the following JSON structure:
// {
// "level": logging_level,
// "timestamp": <milliseconds since epoch>,
// "message": <JSON string described below>
// }
// The message attribute is a JSON string of the following structure:
// {
// "webview": <originating WebView ID>,
// "message": { "method": "...", "params": { ... }} // DevTools message.
// }
class DevToolsEventLogger : public DevToolsEventListener {
public:
explicit DevToolsEventLogger(const std::string& log_type,
const std::vector<std::string>& domains,
const std::string& logging_level);
virtual ~DevToolsEventLogger();
const std::string& GetLogType();
scoped_ptr<base::ListValue> GetAndClearLogEntries();
virtual Status OnConnected(DevToolsClient* client) OVERRIDE;
virtual void OnEvent(DevToolsClient* client,
const std::string& method,
const base::DictionaryValue& params) OVERRIDE;
private:
virtual bool ShouldLogEvent(const std::string& method);
virtual scoped_ptr<DictionaryValue> GetLogEntry(
DevToolsClient* client,
const std::string& method,
const base::DictionaryValue& params);
const std::string log_type_;
const std::vector<std::string> domains_;
const std::string logging_level_;
scoped_ptr<ListValue> log_entries_;
DISALLOW_COPY_AND_ASSIGN(DevToolsEventLogger);
};
#endif // CHROME_TEST_CHROMEDRIVER_CHROME_DEVTOOLS_EVENT_LOGGER_H_
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/format_macros.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/json/string_escape.h"
#include "base/stringprintf.h"
#include "base/time.h"
#include "base/values.h"
#include "chrome/test/chromedriver/chrome/devtools_event_logger.h"
#include "chrome/test/chromedriver/chrome/stub_devtools_client.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
class FakeDevToolsClient : public StubDevToolsClient {
public:
explicit FakeDevToolsClient(const std::string& id) : id_(id) {}
virtual ~FakeDevToolsClient() {}
std::string PopSentCommand() {
std::string command;
if (!sent_command_queue_.empty()) {
command = sent_command_queue_.front();
sent_command_queue_.pop_front();
}
return command;
}
void TriggerEvent(const std::string& method) {
base::DictionaryValue empty_params;
listener_->OnEvent(this, method, empty_params);
}
// Overridden from DevToolsClient:
virtual Status ConnectIfNecessary() OVERRIDE {
return listener_->OnConnected(this);
}
virtual Status SendCommandAndGetResult(
const std::string& method,
const base::DictionaryValue& params,
scoped_ptr<base::DictionaryValue>* result) OVERRIDE {
sent_command_queue_.push_back(method);
return Status(kOk);
}
virtual void AddListener(DevToolsEventListener* listener) OVERRIDE {
listener_ = listener;
}
const std::string& GetId() OVERRIDE {
return id_;
}
private:
const std::string id_;
std::list<std::string> sent_command_queue_;
DevToolsEventListener* listener_;
};
scoped_ptr<DictionaryValue> ParseDictionary(const std::string& json) {
std::string error;
scoped_ptr<Value> value(base::JSONReader::ReadAndReturnError(
json, base::JSON_PARSE_RFC, NULL, &error));
if (NULL == value) {
SCOPED_TRACE(json.c_str());
SCOPED_TRACE(error.c_str());
ADD_FAILURE();
return scoped_ptr<DictionaryValue>(NULL);
}
DictionaryValue* dict = 0;
if (!value->GetAsDictionary(&dict)) {
SCOPED_TRACE("JSON object is not a dictionary");
ADD_FAILURE();
return scoped_ptr<DictionaryValue>(NULL);
}
return scoped_ptr<DictionaryValue>(dict->DeepCopy());
}
void ValidateLogEntry(base::ListValue *entries,
int index,
const char* expect_webview,
const char* expect_method,
const char* expect_level) {
const base::DictionaryValue *entry;
ASSERT_TRUE(entries->GetDictionary(index, &entry));
std::string message_json;
ASSERT_TRUE(entry->GetString("message", &message_json));
scoped_ptr<base::DictionaryValue> message(ParseDictionary(message_json));
std::string level;
EXPECT_TRUE(entry->GetString("level", &level));
EXPECT_STREQ(expect_level, level.c_str());
double timestamp = 0;
EXPECT_TRUE(entry->GetDouble("timestamp", &timestamp));
EXPECT_LT(0, timestamp);
std::string webview;
EXPECT_TRUE(message->GetString("webview", &webview));
EXPECT_STREQ(expect_webview, webview.c_str());
std::string method;
EXPECT_TRUE(message->GetString("message.method", &method));
EXPECT_STREQ(expect_method, method.c_str());
DictionaryValue* params;
EXPECT_TRUE(message->GetDictionary("message.params", &params));
EXPECT_EQ(0u, params->size());
}
} // namespace
TEST(DevToolsEventLogger, OneClientMultiDomains) {
FakeDevToolsClient client("webview-1");
std::vector<std::string> domains;
domains.push_back("Page");
domains.push_back("Network");
domains.push_back("Timeline");
DevToolsEventLogger logger("mylog", domains, "INFO");
client.AddListener(&logger);
logger.OnConnected(&client);
EXPECT_STREQ("Page.enable", client.PopSentCommand().c_str());
EXPECT_STREQ("Network.enable", client.PopSentCommand().c_str());
EXPECT_STREQ("Timeline.start", client.PopSentCommand().c_str());
EXPECT_STREQ("", client.PopSentCommand().c_str());
client.TriggerEvent("Network.gaga");
client.TriggerEvent("Page.ulala");
client.TriggerEvent("Console.bad"); // Ignore -- different domain.
scoped_ptr<base::ListValue> entries(logger.GetAndClearLogEntries());
ASSERT_EQ(2u, entries->GetSize());
ValidateLogEntry(entries.get(), 0, "webview-1", "Network.gaga", "INFO");
ValidateLogEntry(entries.get(), 1, "webview-1", "Page.ulala", "INFO");
// Repeat get returns nothing.
scoped_ptr<base::ListValue> no_entries(logger.GetAndClearLogEntries());
EXPECT_EQ(0u, no_entries->GetSize());
EXPECT_STREQ("", client.PopSentCommand().c_str()); // No more commands sent.
}
TEST(DevToolsEventLogger, MultiClientsOneDomain) {
FakeDevToolsClient client1("webview-1");
FakeDevToolsClient client2("webview-2");
std::vector<std::string> domains;
domains.push_back("Console");
DevToolsEventLogger logger("mylog", domains, "INFO");
client1.AddListener(&logger);
client2.AddListener(&logger);
logger.OnConnected(&client1);
logger.OnConnected(&client2);
EXPECT_STREQ("Console.enable", client1.PopSentCommand().c_str());
EXPECT_STREQ("", client1.PopSentCommand().c_str());
EXPECT_STREQ("Console.enable", client2.PopSentCommand().c_str());
EXPECT_STREQ("", client2.PopSentCommand().c_str());
// OnConnected sends the enable command only to that client, not others.
client1.ConnectIfNecessary();
EXPECT_STREQ("Console.enable", client1.PopSentCommand().c_str());
EXPECT_STREQ("", client1.PopSentCommand().c_str());
EXPECT_STREQ("", client2.PopSentCommand().c_str());
client1.TriggerEvent("Console.gaga1");
client2.TriggerEvent("Console.gaga2");
scoped_ptr<base::ListValue> entries(logger.GetAndClearLogEntries());
ASSERT_EQ(2u, entries->GetSize());
ValidateLogEntry(entries.get(), 0, "webview-1", "Console.gaga1", "INFO");
ValidateLogEntry(entries.get(), 1, "webview-2", "Console.gaga2", "INFO");
}
...@@ -177,11 +177,13 @@ Status WaitForDevToolsAndCheckVersion( ...@@ -177,11 +177,13 @@ Status WaitForDevToolsAndCheckVersion(
return Status(kUnknownError, "unable to discover open pages"); return Status(kUnknownError, "unable to discover open pages");
} }
Status LaunchDesktopChrome(URLRequestContextGetter* context_getter, Status LaunchDesktopChrome(
int port, URLRequestContextGetter* context_getter,
const SyncWebSocketFactory& socket_factory, int port,
const Capabilities& capabilities, const SyncWebSocketFactory& socket_factory,
scoped_ptr<Chrome>* chrome) { const Capabilities& capabilities,
const std::list<DevToolsEventLogger*>& devtools_event_loggers,
scoped_ptr<Chrome>* chrome) {
CommandLine command(CommandLine::NO_PROGRAM); CommandLine command(CommandLine::NO_PROGRAM);
base::ScopedTempDir user_data_dir; base::ScopedTempDir user_data_dir;
base::ScopedTempDir extension_dir; base::ScopedTempDir extension_dir;
...@@ -245,16 +247,18 @@ Status LaunchDesktopChrome(URLRequestContextGetter* context_getter, ...@@ -245,16 +247,18 @@ Status LaunchDesktopChrome(URLRequestContextGetter* context_getter,
return status; return status;
} }
chrome->reset(new ChromeDesktopImpl( chrome->reset(new ChromeDesktopImpl(
devtools_client.Pass(), version, build_no, process, &user_data_dir, devtools_client.Pass(), version, build_no, devtools_event_loggers,
&extension_dir)); process, &user_data_dir, &extension_dir));
return Status(kOk); return Status(kOk);
} }
Status LaunchAndroidChrome(URLRequestContextGetter* context_getter, Status LaunchAndroidChrome(
int port, URLRequestContextGetter* context_getter,
const SyncWebSocketFactory& socket_factory, int port,
const Capabilities& capabilities, const SyncWebSocketFactory& socket_factory,
scoped_ptr<Chrome>* chrome) { const Capabilities& capabilities,
const std::list<DevToolsEventLogger*>& devtools_event_loggers,
scoped_ptr<Chrome>* chrome) {
// TODO(frankf): Figure out how this should be installed to // TODO(frankf): Figure out how this should be installed to
// make this work for all platforms. // make this work for all platforms.
base::FilePath adb_commands(FILE_PATH_LITERAL("adb_commands.py")); base::FilePath adb_commands(FILE_PATH_LITERAL("adb_commands.py"));
...@@ -283,23 +287,27 @@ Status LaunchAndroidChrome(URLRequestContextGetter* context_getter, ...@@ -283,23 +287,27 @@ Status LaunchAndroidChrome(URLRequestContextGetter* context_getter,
return status; return status;
chrome->reset(new ChromeAndroidImpl( chrome->reset(new ChromeAndroidImpl(
devtools_client.Pass(), version, build_no)); devtools_client.Pass(), version, build_no, devtools_event_loggers));
return Status(kOk); return Status(kOk);
} }
} // namespace } // namespace
Status LaunchChrome(URLRequestContextGetter* context_getter, Status LaunchChrome(
int port, URLRequestContextGetter* context_getter,
const SyncWebSocketFactory& socket_factory, int port,
const Capabilities& capabilities, const SyncWebSocketFactory& socket_factory,
scoped_ptr<Chrome>* chrome) { const Capabilities& capabilities,
const std::list<DevToolsEventLogger*>& devtools_event_loggers,
scoped_ptr<Chrome>* chrome) {
if (capabilities.IsAndroid()) { if (capabilities.IsAndroid()) {
return LaunchAndroidChrome( return LaunchAndroidChrome(
context_getter, port, socket_factory, capabilities, chrome); context_getter, port, socket_factory, capabilities,
devtools_event_loggers, chrome);
} else { } else {
return LaunchDesktopChrome( return LaunchDesktopChrome(
context_getter, port, socket_factory, capabilities, chrome); context_getter, port, socket_factory, capabilities,
devtools_event_loggers, chrome);
} }
} }
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#ifndef CHROME_TEST_CHROMEDRIVER_CHROME_LAUNCHER_H_ #ifndef CHROME_TEST_CHROMEDRIVER_CHROME_LAUNCHER_H_
#define CHROME_TEST_CHROMEDRIVER_CHROME_LAUNCHER_H_ #define CHROME_TEST_CHROMEDRIVER_CHROME_LAUNCHER_H_
#include <list>
#include <string> #include <string>
#include <vector> #include <vector>
...@@ -14,6 +15,7 @@ ...@@ -14,6 +15,7 @@
#include "chrome/test/chromedriver/net/sync_websocket_factory.h" #include "chrome/test/chromedriver/net/sync_websocket_factory.h"
class CommandLine; class CommandLine;
class DevToolsEventLogger;
namespace base { namespace base {
class DictionaryValue; class DictionaryValue;
...@@ -24,11 +26,13 @@ class Chrome; ...@@ -24,11 +26,13 @@ class Chrome;
class Status; class Status;
class URLRequestContextGetter; class URLRequestContextGetter;
Status LaunchChrome(URLRequestContextGetter* context_getter, Status LaunchChrome(
int port, URLRequestContextGetter* context_getter,
const SyncWebSocketFactory& socket_factory, int port,
const Capabilities& capabilities, const SyncWebSocketFactory& socket_factory,
scoped_ptr<Chrome>* chrome); const Capabilities& capabilities,
const std::list<DevToolsEventLogger*>& devtools_event_loggers,
scoped_ptr<Chrome>* chrome);
namespace internal { namespace internal {
Status ProcessExtensions(const std::vector<std::string>& extensions, Status ProcessExtensions(const std::vector<std::string>& extensions,
......
...@@ -228,6 +228,10 @@ void CommandExecutorImpl::Init() { ...@@ -228,6 +228,10 @@ void CommandExecutorImpl::Init() {
base::Bind(&ExecuteSetWindowSize); base::Bind(&ExecuteSetWindowSize);
session_command_map[CommandNames::kMaximizeWindow] = session_command_map[CommandNames::kMaximizeWindow] =
base::Bind(&ExecuteMaximizeWindow); base::Bind(&ExecuteMaximizeWindow);
session_command_map[CommandNames::kGetAvailableLogTypes] =
base::Bind(&ExecuteGetAvailableLogTypes);
session_command_map[CommandNames::kGetLog] =
base::Bind(&ExecuteGetLog);
// Wrap SessionCommand into non-session Command. // Wrap SessionCommand into non-session Command.
base::Callback<Status( base::Callback<Status(
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
#include "chrome/test/chromedriver/commands.h" #include "chrome/test/chromedriver/commands.h"
#include <list>
#include "base/stringprintf.h" #include "base/stringprintf.h"
#include "base/sys_info.h" #include "base/sys_info.h"
#include "base/values.h" #include "base/values.h"
...@@ -11,10 +13,12 @@ ...@@ -11,10 +13,12 @@
#include "chrome/test/chromedriver/chrome/chrome.h" #include "chrome/test/chromedriver/chrome/chrome.h"
#include "chrome/test/chromedriver/chrome/chrome_android_impl.h" #include "chrome/test/chromedriver/chrome/chrome_android_impl.h"
#include "chrome/test/chromedriver/chrome/chrome_desktop_impl.h" #include "chrome/test/chromedriver/chrome/chrome_desktop_impl.h"
#include "chrome/test/chromedriver/chrome/devtools_event_logger.h"
#include "chrome/test/chromedriver/chrome/status.h" #include "chrome/test/chromedriver/chrome/status.h"
#include "chrome/test/chromedriver/chrome/version.h" #include "chrome/test/chromedriver/chrome/version.h"
#include "chrome/test/chromedriver/chrome/web_view.h" #include "chrome/test/chromedriver/chrome/web_view.h"
#include "chrome/test/chromedriver/chrome_launcher.h" #include "chrome/test/chromedriver/chrome_launcher.h"
#include "chrome/test/chromedriver/logging.h"
#include "chrome/test/chromedriver/net/net_util.h" #include "chrome/test/chromedriver/net/net_util.h"
#include "chrome/test/chromedriver/net/url_request_context_getter.h" #include "chrome/test/chromedriver/net/url_request_context_getter.h"
#include "chrome/test/chromedriver/session.h" #include "chrome/test/chromedriver/session.h"
...@@ -62,9 +66,17 @@ Status ExecuteNewSession( ...@@ -62,9 +66,17 @@ Status ExecuteNewSession(
if (status.IsError()) if (status.IsError())
return status; return status;
// Create DevToolsEventLoggers, fail if log levels are invalid.
ScopedVector<DevToolsEventLogger> devtools_event_loggers;
status = CreateLoggers(capabilities, &devtools_event_loggers);
if (status.IsError())
return status;
scoped_ptr<Chrome> chrome; scoped_ptr<Chrome> chrome;
std::list<DevToolsEventLogger*> devtools_event_logger_list(
devtools_event_loggers.begin(), devtools_event_loggers.end());
status = LaunchChrome(context_getter, port, socket_factory, status = LaunchChrome(context_getter, port, socket_factory,
capabilities, &chrome); capabilities, devtools_event_logger_list, &chrome);
if (status.IsError()) if (status.IsError())
return status; return status;
...@@ -80,6 +92,7 @@ Status ExecuteNewSession( ...@@ -80,6 +92,7 @@ Status ExecuteNewSession(
if (new_id.empty()) if (new_id.empty())
new_id = GenerateId(); new_id = GenerateId();
scoped_ptr<Session> session(new Session(new_id, chrome.Pass())); scoped_ptr<Session> session(new Session(new_id, chrome.Pass()));
session->devtools_event_loggers.swap(devtools_event_loggers);
if (!session->thread.Start()) { if (!session->thread.Start()) {
chrome->Quit(); chrome->Quit();
return Status(kUnknownError, return Status(kUnknownError,
......
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/test/chromedriver/logging.h"
#include "chrome/test/chromedriver/capabilities.h"
#include "chrome/test/chromedriver/chrome/devtools_event_logger.h"
#include "chrome/test/chromedriver/chrome/status.h"
Status CreateLoggers(const Capabilities& capabilities,
ScopedVector<DevToolsEventLogger>* out_loggers) {
if (capabilities.logging_prefs) {
ScopedVector<DevToolsEventLogger> loggers;
for (DictionaryValue::Iterator pref(*capabilities.logging_prefs);
!pref.IsAtEnd(); pref.Advance()) {
const std::string type = pref.key();
std::string level;
if (!pref.value().GetAsString(&level)) {
return Status(kUnknownError,
"logging level must be a string for log type: " + type);
}
if ("profiler" == type) {
std::vector<std::string> domains;
domains.push_back("Network");
domains.push_back("Page");
domains.push_back("Timeline");
loggers.push_back(new DevToolsEventLogger(type, domains, level));
} else {
return Status(kUnknownError, "unsupported log type: " + type);
}
// TODO(klm): Implement and add here the console logger.
}
out_loggers->swap(loggers);
}
return Status(kOk);
}
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_TEST_CHROMEDRIVER_LOGGING_H_
#define CHROME_TEST_CHROMEDRIVER_LOGGING_H_
#include "base/memory/scoped_vector.h"
struct Capabilities;
class DevToolsEventLogger;
class Status;
Status CreateLoggers(const Capabilities& capabilities,
ScopedVector<DevToolsEventLogger>* out_loggers);
#endif // CHROME_TEST_CHROMEDRIVER_LOGGING_H_
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/test/chromedriver/logging.h"
#include "base/values.h"
#include "chrome/test/chromedriver/capabilities.h"
#include "chrome/test/chromedriver/chrome/devtools_event_logger.h"
#include "chrome/test/chromedriver/chrome/status.h"
#include "testing/gtest/include/gtest/gtest.h"
TEST(Logging, CreatePerformanceLogger) {
Capabilities capabilities;
capabilities.logging_prefs.reset(new base::DictionaryValue());
capabilities.logging_prefs->SetString("profiler", "INFO");
ScopedVector<DevToolsEventLogger> loggers;
Status status = CreateLoggers(capabilities, &loggers);
ASSERT_TRUE(status.IsOk());
ASSERT_EQ(1u, loggers.size());
ASSERT_STREQ("profiler", loggers[0]->GetLogType().c_str());
}
TEST(Logging, InvalidLogType) {
Capabilities capabilities;
capabilities.logging_prefs.reset(new base::DictionaryValue());
capabilities.logging_prefs->SetString("gaga", "INFO");
ScopedVector<DevToolsEventLogger> loggers;
Status status = CreateLoggers(capabilities, &loggers);
EXPECT_FALSE(status.IsOk());
ASSERT_EQ(0u, loggers.size());
}
...@@ -159,6 +159,9 @@ int main(int argc, char *argv[]) { ...@@ -159,6 +159,9 @@ int main(int argc, char *argv[]) {
false, // enable_thread_id false, // enable_thread_id
true, // enable_timestamp true, // enable_timestamp
false); // enable_tickcount false); // enable_tickcount
if (cmd_line->HasSwitch("verbose")) {
logging::SetMinLogLevel(logging::LOG_VERBOSE);
}
scoped_ptr<CommandExecutor> executor(new CommandExecutorImpl()); scoped_ptr<CommandExecutor> executor(new CommandExecutorImpl());
HttpHandler handler(executor.Pass(), HttpHandler::CreateCommandMap(), HttpHandler handler(executor.Pass(), HttpHandler::CreateCommandMap(),
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "base/values.h" #include "base/values.h"
#include "chrome/test/chromedriver/chrome/chrome.h" #include "chrome/test/chromedriver/chrome/chrome.h"
#include "chrome/test/chromedriver/chrome/devtools_event_logger.h"
#include "chrome/test/chromedriver/chrome/status.h" #include "chrome/test/chromedriver/chrome/status.h"
#include "chrome/test/chromedriver/chrome/version.h" #include "chrome/test/chromedriver/chrome/version.h"
#include "chrome/test/chromedriver/chrome/web_view.h" #include "chrome/test/chromedriver/chrome/web_view.h"
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
#include "base/synchronization/lock.h" #include "base/synchronization/lock.h"
#include "base/threading/thread.h" #include "base/threading/thread.h"
#include "chrome/test/chromedriver/basic_types.h" #include "chrome/test/chromedriver/basic_types.h"
...@@ -21,6 +22,7 @@ class DictionaryValue; ...@@ -21,6 +22,7 @@ class DictionaryValue;
} }
class Chrome; class Chrome;
class DevToolsEventLogger;
class Status; class Status;
class WebView; class WebView;
...@@ -61,6 +63,7 @@ struct Session { ...@@ -61,6 +63,7 @@ struct Session {
int script_timeout; int script_timeout;
std::string prompt_text; std::string prompt_text;
scoped_ptr<Geoposition> overridden_geoposition; scoped_ptr<Geoposition> overridden_geoposition;
ScopedVector<DevToolsEventLogger> devtools_event_loggers;
const scoped_ptr<base::DictionaryValue> capabilities; const scoped_ptr<base::DictionaryValue> capabilities;
private: private:
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "chrome/test/chromedriver/basic_types.h" #include "chrome/test/chromedriver/basic_types.h"
#include "chrome/test/chromedriver/chrome/automation_extension.h" #include "chrome/test/chromedriver/chrome/automation_extension.h"
#include "chrome/test/chromedriver/chrome/chrome.h" #include "chrome/test/chromedriver/chrome/chrome.h"
#include "chrome/test/chromedriver/chrome/devtools_event_logger.h"
#include "chrome/test/chromedriver/chrome/geoposition.h" #include "chrome/test/chromedriver/chrome/geoposition.h"
#include "chrome/test/chromedriver/chrome/status.h" #include "chrome/test/chromedriver/chrome/status.h"
#include "chrome/test/chromedriver/chrome/web_view.h" #include "chrome/test/chromedriver/chrome/web_view.h"
...@@ -472,3 +473,37 @@ Status ExecuteMaximizeWindow( ...@@ -472,3 +473,37 @@ Status ExecuteMaximizeWindow(
return extension->MaximizeWindow(); return extension->MaximizeWindow();
} }
Status ExecuteGetAvailableLogTypes(
Session* session,
const base::DictionaryValue& params,
scoped_ptr<base::Value>* value) {
scoped_ptr<ListValue> types(new base::ListValue());
for (ScopedVector<DevToolsEventLogger>::const_iterator logger =
session->devtools_event_loggers.begin();
logger != session->devtools_event_loggers.end(); ++logger) {
types->AppendString((*logger)->GetLogType());
}
value->reset(types.release());
return Status(kOk);
}
Status ExecuteGetLog(
Session* session,
const base::DictionaryValue& params,
scoped_ptr<base::Value>* value) {
std::string log_type;
if (!params.GetString("type", &log_type)) {
return Status(kUnknownError, "missing or invalid 'type'");
}
for (ScopedVector<DevToolsEventLogger>::const_iterator logger =
session->devtools_event_loggers.begin();
logger != session->devtools_event_loggers.end(); ++logger) {
if (log_type == (*logger)->GetLogType()) {
scoped_ptr<ListValue> log_entries = (*logger)->GetAndClearLogEntries();
value->reset(log_entries.release());
return Status(kOk);
}
}
return Status(kUnknownError, "log type '" + log_type + "' not found");
}
...@@ -159,4 +159,14 @@ Status ExecuteMaximizeWindow( ...@@ -159,4 +159,14 @@ Status ExecuteMaximizeWindow(
const base::DictionaryValue& params, const base::DictionaryValue& params,
scoped_ptr<base::Value>* value); scoped_ptr<base::Value>* value);
Status ExecuteGetAvailableLogTypes(
Session* session,
const base::DictionaryValue& params,
scoped_ptr<base::Value>* value);
Status ExecuteGetLog(
Session* session,
const base::DictionaryValue& params,
scoped_ptr<base::Value>* value);
#endif // CHROME_TEST_CHROMEDRIVER_SESSION_COMMANDS_H_ #endif // CHROME_TEST_CHROMEDRIVER_SESSION_COMMANDS_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment