DevTools: Unify ADB-related browser tests

Merged devtools_android_bridge_browsertest.cc logic into adb_client_socket_browser_test.cc.
Made inspect_ui_browsertest.cc use MockAdbServer instead of a mock DeviceProvider.

BUG=None

Review URL: https://codereview.chromium.org/257563003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@266302 0039d316-1c4b-4281-b951-d872f2087c98
parent 0eb5f08a
......@@ -2,421 +2,127 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/strings/string_number_conversions.h"
#include "chrome/browser/devtools/device/adb/mock_adb_server.h"
#include "chrome/browser/devtools/device/devtools_android_bridge.h"
#include "chrome/browser/devtools/devtools_target_impl.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/test_utils.h"
#include "net/base/host_port_pair.h"
#include "net/base/io_buffer.h"
#include "net/base/ip_endpoint.h"
#include "net/base/net_errors.h"
#include "net/base/net_log.h"
#include "net/socket/stream_socket.h"
#include "net/socket/tcp_server_socket.h"
const char kOpenedUnixSocketsCommand[] = "shell:cat /proc/net/unix";
const char kDeviceModelCommand[] = "shell:getprop ro.product.model";
const char kDumpsysCommand[] = "shell:dumpsys window policy";
const char kListProcessesCommand[] = "shell:ps";
const char kInstalledChromePackagesCommand[] = "shell:pm list packages";
const char kDeviceModel[] = "Nexus 8";
using content::BrowserThread;
const char kSampleOpenedUnixSocketsWithoutBrowsers[] =
"Num RefCount Protocol Flags Type St Inode Path\n"
"00000000: 00000004 00000000"
" 00000000 0002 01 3328 /dev/socket/wpa_wlan0\n"
"00000000: 00000002 00000000"
" 00010000 0001 01 5394 /dev/socket/vold\n";
const char kSampleDumpsys[] =
"WINDOW MANAGER POLICY STATE (dumpsys window policy)\r\n"
" mStable=(0,50)-(720,1184)\r\n";
const char kSampleListProcesses[] =
"USER PID PPID VSIZE RSS WCHAN PC NAME\n"
"root 1 0 688 508 ffffffff 00000000 S /init\n";
const char kSampleListPackages[] = "package:com.example.app";
static const int kBufferSize = 16*1024;
static const int kAdbPort = 5037;
static const int kAdbMessageHeaderSize = 4;
// This is single connection server which listens on specified port and
// simplifies asynchronous IO.
// To write custom server, extend this class and implement TryProcessData
// method which is invoked everytime data arrives. In case of successful data
// processing(e.g. enough data collected already to parse client reply/request)
// return amount of bytes processed to throw them away from buffer
// To send data, SendData method should be used. This method is non-blocking
// and appends data to be sent to internal buffer.
// Since all calls are non-blocking and no callbacks are given, internal
// overflows may occur in case too heavy traffic.
// In case of heavy traffic performance may suffer because of memcpy calls.
class SingleConnectionServer {
public:
SingleConnectionServer(net::IPEndPoint endpoint, int buffer_size);
virtual ~SingleConnectionServer();
protected:
virtual int TryProcessData(const char* data, int size) = 0;
void SendData(const char* data, int size);
private:
void AcceptConnection();
void OnAccepted(int result);
void ReadData();
void OnDataRead(int count);
void WriteData();
void OnDataWritten(int count);
private:
int bytes_to_write_;
scoped_ptr<net::TCPServerSocket> server_socket_;
scoped_ptr<net::StreamSocket> client_socket_;
scoped_refptr<net::GrowableIOBuffer> input_buffer_;
scoped_refptr<net::GrowableIOBuffer> output_buffer_;
DISALLOW_COPY_AND_ASSIGN(SingleConnectionServer);
};
SingleConnectionServer::SingleConnectionServer(net::IPEndPoint endpoint,
int buffer_size)
: bytes_to_write_(0) {
CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
input_buffer_ = new net::GrowableIOBuffer();
input_buffer_->SetCapacity(buffer_size);
output_buffer_ = new net::GrowableIOBuffer();
server_socket_.reset(new net::TCPServerSocket(NULL, net::NetLog::Source()));
server_socket_->Listen(endpoint, 1);
AcceptConnection();
}
SingleConnectionServer::~SingleConnectionServer() {
CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
server_socket_.reset();
if (client_socket_) {
client_socket_->Disconnect();
client_socket_.reset();
}
}
void SingleConnectionServer::SendData(const char* data, int size) {
CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
if ((output_buffer_->offset() + bytes_to_write_ + size) >
output_buffer_->capacity()) {
// If not enough space without relocation
if (output_buffer_->capacity() < (bytes_to_write_ + size)) {
// If even buffer is not enough
int new_size = std::max(output_buffer_->capacity() * 2, size * 2);
output_buffer_->SetCapacity(new_size);
}
memmove(output_buffer_->StartOfBuffer(),
output_buffer_->data(),
bytes_to_write_);
output_buffer_->set_offset(0);
}
memcpy(output_buffer_->data() + bytes_to_write_, data, size);
bytes_to_write_ += size;
if (bytes_to_write_ == size)
// If write loop wasn't yet started, then start it
WriteData();
}
void SingleConnectionServer::AcceptConnection() {
if (client_socket_) {
client_socket_->Disconnect();
client_socket_.reset();
}
int accept_result = server_socket_->Accept(&client_socket_,
base::Bind(&SingleConnectionServer::OnAccepted, base::Unretained(this)));
if (accept_result != net::ERR_IO_PENDING)
content::BrowserThread::PostTask(
content::BrowserThread::IO,
FROM_HERE,
base::Bind(&SingleConnectionServer::OnAccepted,
base::Unretained(this),
accept_result));
}
void SingleConnectionServer::OnAccepted(int result) {
ASSERT_EQ(result, 0); // Fails if the socket is already in use.
ReadData();
}
void SingleConnectionServer::ReadData() {
CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
if (input_buffer_->RemainingCapacity() == 0)
input_buffer_->SetCapacity(input_buffer_->capacity() * 2);
int read_result = client_socket_->Read(
input_buffer_.get(),
input_buffer_->RemainingCapacity(),
base::Bind(&SingleConnectionServer::OnDataRead, base::Unretained(this)));
if (read_result != net::ERR_IO_PENDING)
OnDataRead(read_result);
}
void SingleConnectionServer::OnDataRead(int count) {
if (count <= 0) {
AcceptConnection();
return;
}
input_buffer_->set_offset(input_buffer_->offset() + count);
int bytes_processed;
do {
char* data = input_buffer_->StartOfBuffer();
int data_size = input_buffer_->offset();
bytes_processed = TryProcessData(data, data_size);
if (bytes_processed) {
memmove(data, data + bytes_processed, data_size - bytes_processed);
input_buffer_->set_offset( data_size - bytes_processed);
}
} while (bytes_processed);
// Posting is needed not to enter deep recursion in case too synchronous IO
content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE,
base::Bind(&SingleConnectionServer::ReadData, base::Unretained(this)));
}
void SingleConnectionServer::WriteData() {
CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
CHECK_GE(output_buffer_->capacity(),
output_buffer_->offset() + bytes_to_write_) << "Overflow";
int write_result = client_socket_->Write(
output_buffer_,
bytes_to_write_,
base::Bind(&SingleConnectionServer::OnDataWritten,
base::Unretained(this)));
if (write_result != net::ERR_IO_PENDING)
OnDataWritten(write_result);
}
void SingleConnectionServer::OnDataWritten(int count) {
CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
if (count < 0) {
AcceptConnection();
return;
}
CHECK_GT(count, 0);
CHECK_GE(output_buffer_->capacity(),
output_buffer_->offset() + bytes_to_write_) << "Overflow";
bytes_to_write_ -= count;
output_buffer_->set_offset(output_buffer_->offset() + count);
if (bytes_to_write_ != 0)
// Posting is needed not to enter deep recursion in case too synchronous IO
content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE,
base::Bind(&SingleConnectionServer::WriteData, base::Unretained(this)));
static scoped_refptr<DevToolsAndroidBridge::RemoteBrowser>
FindBrowserByDisplayName(DevToolsAndroidBridge::RemoteBrowsers browsers,
const std::string& name) {
for (DevToolsAndroidBridge::RemoteBrowsers::iterator it = browsers.begin();
it != browsers.end(); ++it)
if ((*it)->display_name() == name)
return *it;
return NULL;
}
class MockAdbServer: public SingleConnectionServer {
public:
MockAdbServer(net::IPEndPoint endpoint, int buffer_size)
: SingleConnectionServer(endpoint, buffer_size)
{}
virtual ~MockAdbServer() {}
private:
virtual int TryProcessData(const char* data, int size) OVERRIDE {
CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
if (size >= kAdbMessageHeaderSize) {
std::string message_header(data, kAdbMessageHeaderSize);
int message_size;
EXPECT_TRUE(base::HexStringToInt(message_header, &message_size));
if (size >= message_size + kAdbMessageHeaderSize) {
std::string message_body(data + kAdbMessageHeaderSize, message_size );
ProcessCommand(message_body);
return kAdbMessageHeaderSize + message_size;
}
}
return 0;
}
void ProcessCommand(const std::string& command) {
CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
if (command == "host:devices") {
SendResponse("01498B321301A00A\tdevice\n01498B2B0D01300E\toffline");
} else if (command == "host:transport:01498B321301A00A") {
SendResponse("");
} else if (command == kDeviceModelCommand) {
SendResponse(kDeviceModel);
} else if (command == kOpenedUnixSocketsCommand) {
SendResponse(kSampleOpenedUnixSocketsWithoutBrowsers);
} else if (command == kDumpsysCommand) {
SendResponse(kSampleDumpsys);
} else if (command == kListProcessesCommand) {
SendResponse(kSampleListProcesses);
} else if (command == kInstalledChromePackagesCommand) {
SendResponse(kSampleListPackages);
} else {
NOTREACHED() << "Unknown command - " << command;
}
}
void SendResponse(const std::string& response) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
std::stringstream response_stream;
response_stream << "OKAY";
int size = response.size();
if (size > 0) {
static const char kHexChars[] = "0123456789ABCDEF";
for (int i = 3; i >= 0; i--)
response_stream << kHexChars[ (size >> 4*i) & 0x0f ];
response_stream << response;
}
std::string response_data = response_stream.str();
SendData(response_data.c_str(), response_data.size());
}
};
class AdbClientSocketTest : public InProcessBrowserTest,
public DevToolsAndroidBridge::DeviceListListener {
public:
public:
void StartTest() {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
content::BrowserThread::PostTaskAndReply(
content::BrowserThread::IO,
FROM_HERE,
base::Bind(&AdbClientSocketTest::StartMockAdbServer,
base::Unretained(this)),
base::Bind(&AdbClientSocketTest::AddListener,
base::Unretained(this)));
Profile* profile = browser()->profile();
android_bridge_ = DevToolsAndroidBridge::Factory::GetForProfile(profile);
AndroidDeviceManager::DeviceProviders device_providers;
device_providers.push_back(AndroidDeviceManager::GetAdbDeviceProvider());
android_bridge_->set_device_providers_for_test(device_providers);
android_bridge_->AddDeviceListListener(this);
}
virtual void DeviceListChanged(
const DevToolsAndroidBridge::RemoteDevices& devices) OVERRIDE {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
android_bridge_->RemoveDeviceListListener(this);
devices_ = devices;
EndTest();
android_bridge_->RemoveDeviceListListener(this);
base::MessageLoop::current()->Quit();
}
void CheckDevices() {
ASSERT_EQ(2U, devices_.size());
scoped_refptr<DevToolsAndroidBridge::RemoteDevice> online_device_;
scoped_refptr<DevToolsAndroidBridge::RemoteDevice> offline_device_;
scoped_refptr<DevToolsAndroidBridge::RemoteDevice> connected =
devices_[0]->is_connected() ? devices_[0] : devices_[1];
for (DevToolsAndroidBridge::RemoteDevices::const_iterator it =
devices_.begin(); it != devices_.end(); ++it) {
if ((*it)->serial() == "01498B321301A00A")
online_device_ = *it;
else if ((*it)->serial() == "01498B2B0D01300E")
offline_device_ = *it;
}
scoped_refptr<DevToolsAndroidBridge::RemoteDevice> not_connected =
devices_[0]->is_connected() ? devices_[1] : devices_[0];
ASSERT_EQ(online_device_->serial(), "01498B321301A00A");
ASSERT_TRUE(online_device_->is_connected());
ASSERT_FALSE(offline_device_->is_connected());
ASSERT_TRUE(connected->is_connected());
ASSERT_FALSE(not_connected->is_connected());
ASSERT_EQ(online_device_->model(), kDeviceModel);
ASSERT_EQ(online_device_->browsers().size(), 0U);
ASSERT_EQ(online_device_->screen_size(), gfx::Size(720, 1184));
}
ASSERT_EQ(720, connected->screen_size().width());
ASSERT_EQ(1184, connected->screen_size().height());
private:
void EndTest() {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
android_bridge_ = NULL;
ASSERT_EQ("01498B321301A00A", connected->serial());
ASSERT_EQ("Nexus 6", connected->model());
content::BrowserThread::PostTaskAndReply(
content::BrowserThread::IO,
FROM_HERE,
base::Bind(&AdbClientSocketTest::StopMockAdbServer,
base::Unretained(this)),
base::Bind(&AdbClientSocketTest::StopMessageLoop,
base::Unretained(this)));
}
ASSERT_EQ("01498B2B0D01300E", not_connected->serial());
ASSERT_EQ("Offline", not_connected->model());
void StartMockAdbServer() {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
net::IPAddressNumber address;
net::ParseIPLiteralToNumber("127.0.0.1", &address);
net::IPEndPoint endpoint(address, kAdbPort);
const DevToolsAndroidBridge::RemoteBrowsers& browsers =
connected->browsers();
ASSERT_EQ(4U, browsers.size());
adb_server_.reset(new MockAdbServer(endpoint, kBufferSize));
}
scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> chrome =
FindBrowserByDisplayName(browsers, "Chrome");
ASSERT_TRUE(chrome);
void StopMockAdbServer() {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
adb_server_.reset();
}
scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> chrome_beta =
FindBrowserByDisplayName(browsers, "Chrome Beta");
ASSERT_TRUE(chrome_beta);
void StopMessageLoop() {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
runner->Quit();
}
scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> chromium =
FindBrowserByDisplayName(browsers, "Chromium");
ASSERT_FALSE(chromium);
void AddListener() {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
android_bridge_ = DevToolsAndroidBridge::Factory::GetForProfile(
browser()->profile());
scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> webview =
FindBrowserByDisplayName(browsers, "WebView in com.sample.feed");
ASSERT_TRUE(webview);
AndroidDeviceManager::DeviceProviders device_providers;
device_providers.push_back(AndroidDeviceManager::GetAdbDeviceProvider());
scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> noprocess =
FindBrowserByDisplayName(browsers, "Noprocess");
ASSERT_TRUE(noprocess);
android_bridge_->set_device_providers_for_test(device_providers);
android_bridge_->AddDeviceListListener(this);
}
ASSERT_EQ("32.0.1679.0", chrome->version());
ASSERT_EQ("31.0.1599.0", chrome_beta->version());
ASSERT_EQ("4.0", webview->version());
std::vector<DevToolsTargetImpl*> chrome_pages =
chrome->CreatePageTargets();
std::vector<DevToolsTargetImpl*> chrome_beta_pages =
chrome_beta->CreatePageTargets();
std::vector<DevToolsTargetImpl*> webview_pages =
webview->CreatePageTargets();
ASSERT_EQ(1U, chrome_pages.size());
ASSERT_EQ(0U, chrome_beta_pages.size());
ASSERT_EQ(2U, webview_pages.size());
public:
scoped_refptr<content::MessageLoopRunner> runner;
// Check that we have non-empty description for webview pages.
ASSERT_EQ(0U, chrome_pages[0]->GetDescription().size());
ASSERT_NE(0U, webview_pages[0]->GetDescription().size());
ASSERT_NE(0U, webview_pages[1]->GetDescription().size());
private:
scoped_ptr<MockAdbServer> adb_server_;
ASSERT_EQ(GURL("http://www.chromium.org/"), chrome_pages[0]->GetUrl());
ASSERT_EQ("The Chromium Projects", chrome_pages[0]->GetTitle());
STLDeleteElements(&chrome_pages);
STLDeleteElements(&webview_pages);
}
private:
scoped_refptr<DevToolsAndroidBridge> android_bridge_;
DevToolsAndroidBridge::RemoteDevices devices_;
};
IN_PROC_BROWSER_TEST_F(AdbClientSocketTest, TestAdbClientSocket) {
CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
runner = new content::MessageLoopRunner;
StartMockAdbServer();
StartTest();
runner->Run();
content::RunMessageLoop();
CheckDevices();
StopMockAdbServer();
}
......@@ -2,21 +2,46 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/devtools/device/android_device_manager.h"
#include "chrome/browser/devtools/device/devtools_android_bridge.h"
#include "chrome/browser/devtools/devtools_target_impl.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/threading/non_thread_safe.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/test_utils.h"
#include "net/base/io_buffer.h"
#include "net/base/ip_endpoint.h"
#include "net/base/net_errors.h"
#include "net/socket/stream_socket.h"
#include "net/socket/tcp_server_socket.h"
using content::BrowserThread;
namespace {
const char kHostTransportPrefix[] = "host:transport:";
const char kLocalAbstractPrefix[] = "localabstract:";
const char kDeviceModelCommand[] = "shell:getprop ro.product.model";
const char kOpenedUnixSocketsCommand[] = "shell:cat /proc/net/unix";
const char kListProcessesCommand[] = "shell:ps";
const char kListPackagesCommand[] = "shell:pm list packages";
const char kDeviceModelCommand[] = "shell:getprop ro.product.model";
const char kDumpsysCommand[] = "shell:dumpsys window policy";
const char kListProcessesCommand[] = "shell:ps";
const char kInstalledChromePackagesCommand[] = "shell:pm list packages";
const char kPageListRequest[] = "GET /json HTTP/1.1\r\n\r\n";
const char kVersionRequest[] = "GET /json/version HTTP/1.1\r\n\r\n";
const char kSerialOnline[] = "01498B321301A00A";
const char kSerialOffline[] = "01498B2B0D01300E";
const char kDeviceModel[] = "Nexus 6";
const char kJsonVersionPath[] = "/json/version";
const char kJsonPath[] = "/json";
const char kJsonListPath[] = "/json/list";
const char kHttpRequestTerminator[] = "\r\n\r\n";
const char kHttpResponse[] =
"HTTP/1.1 200 OK\r\n"
"Content-Length:%d\r\n"
"Content-Type:application/json; charset=UTF-8\r\n\r\n%s";
const char kSampleOpenedUnixSockets[] =
"Num RefCount Protocol Flags Type St Inode Path\n"
......@@ -49,7 +74,7 @@ const char kSampleListPackages[] =
"package:com.chrome.beta\r\n"
"package:com.google.android.apps.chrome\r\n";
const char kSampleDumpsysCommand[] =
const char kSampleDumpsys[] =
"WINDOW MANAGER POLICY STATE (dumpsys window policy)\r\n"
" mSafeMode=false mSystemReady=true mSystemBooted=true\r\n"
" mStable=(0,50)-(720,1184)\r\n" // Only mStable parameter is parsed
......@@ -124,255 +149,409 @@ char kSampleWebViewPages[] = "[ {\n"
"44681551-ADFD-2411-076B-3AB14C1C60E2\"\n"
"}]";
class MockDeviceImpl : public AndroidDeviceManager::Device {
static const int kBufferSize = 16*1024;
static const int kAdbPort = 5037;
static const int kAdbMessageHeaderSize = 4;
class SingleConnectionServer : base::NonThreadSafe {
public:
MockDeviceImpl(const std::string& serial, int index,
bool connected, const char* device_model)
: Device(serial, connected),
device_model_(device_model)
{}
virtual void RunCommand(const std::string& command,
const CommandCallback& callback) OVERRIDE {
const char* response;
if (command == kDeviceModelCommand) {
response = device_model_;
} else if (command == kOpenedUnixSocketsCommand) {
response = kSampleOpenedUnixSockets;
} else if (command == kListProcessesCommand) {
response = kSampleListProcesses;
} else if (command == kListPackagesCommand) {
response = kSampleListPackages;
} else if (command == kDumpsysCommand) {
response = kSampleDumpsysCommand;
} else {
NOTREACHED();
return;
}
class Parser {
public:
virtual int Consume(const char* data, int size) = 0;
virtual void Reset() = 0;
base::MessageLoop::current()->PostTask( FROM_HERE,
base::Bind(&MockDeviceImpl::RunCommandCallback,
this, callback, 0, response));
}
protected:
virtual ~Parser() {}
};
void RunCommandCallback(const CommandCallback& callback, int result,
const std::string& response) {
callback.Run(result, response);
}
SingleConnectionServer(
Parser* parser, net::IPEndPoint endpoint, int buffer_size);
virtual void OpenSocket(const std::string& name,
const SocketCallback& callback) OVERRIDE {
NOTREACHED();
}
virtual ~SingleConnectionServer();
virtual void HttpQuery(const std::string& la_name,
const std::string& request,
const CommandCallback& callback) OVERRIDE {
const char* response;
void Send(const std::string& message);
if (la_name == "chrome_devtools_remote") {
if (request == kVersionRequest) {
response = kSampleChromeVersion;
} else if (request == kPageListRequest) {
response = kSampleChromePages;
} else {
NOTREACHED();
return;
}
} else if (la_name == "chrome_devtools_remote_1002") {
if (request == kVersionRequest) {
response = kSampleChromeBetaVersion;
} else if (request == kPageListRequest) {
response = kSampleChromeBetaPages;
} else {
NOTREACHED();
return;
}
} else if (la_name.find("noprocess_devtools_remote") == 0) {
if (request == kVersionRequest) {
response = "{}";
} else if (request == kPageListRequest) {
response = "[]";
} else {
NOTREACHED();
return;
}
} else if (la_name == "webview_devtools_remote_2425") {
if (request == kVersionRequest) {
response = kSampleWebViewVersion;
} else if (request == kPageListRequest) {
response = kSampleWebViewPages;
} else {
NOTREACHED();
return;
}
} else {
NOTREACHED();
return;
}
private:
void SendData(const char* data, int size);
base::MessageLoop::current()->PostTask( FROM_HERE,
base::Bind(&MockDeviceImpl::RunCommandCallback,
this, callback, 0, response));
}
void AcceptConnection();
void OnAccepted(int result);
void ReadData();
void OnDataRead(int count);
void WriteData();
void OnDataWritten(int count);
virtual void HttpUpgrade(const std::string& la_name,
const std::string& request,
const SocketCallback& callback) {
NOTREACHED();
Parser* parser_;
int bytes_to_write_;
scoped_ptr<net::TCPServerSocket> server_socket_;
scoped_ptr<net::StreamSocket> client_socket_;
scoped_refptr<net::GrowableIOBuffer> input_buffer_;
scoped_refptr<net::GrowableIOBuffer> output_buffer_;
DISALLOW_COPY_AND_ASSIGN(SingleConnectionServer);
};
SingleConnectionServer::SingleConnectionServer(Parser* parser,
net::IPEndPoint endpoint,
int buffer_size)
: parser_(parser),
bytes_to_write_(0) {
CHECK(CalledOnValidThread());
input_buffer_ = new net::GrowableIOBuffer();
input_buffer_->SetCapacity(buffer_size);
output_buffer_ = new net::GrowableIOBuffer();
server_socket_.reset(new net::TCPServerSocket(NULL, net::NetLog::Source()));
server_socket_->Listen(endpoint, 1);
AcceptConnection();
}
SingleConnectionServer::~SingleConnectionServer() {
CHECK(CalledOnValidThread());
server_socket_.reset();
if (client_socket_) {
client_socket_->Disconnect();
client_socket_.reset();
}
}
void SingleConnectionServer::Send(const std::string& message) {
SendData(message.c_str(), message.size());
}
virtual void HttpQueryCallback(const CommandCallback& next, int code,
const std::string& result) {
NOTREACHED();
void SingleConnectionServer::SendData(const char* data, int size) {
CHECK(CalledOnValidThread());
if ((output_buffer_->offset() + bytes_to_write_ + size) >
output_buffer_->capacity()) {
// If not enough space without relocation
if (output_buffer_->capacity() < (bytes_to_write_ + size)) {
// If even buffer is not enough
int new_size = std::max(output_buffer_->capacity() * 2, size * 2);
output_buffer_->SetCapacity(new_size);
}
memmove(output_buffer_->StartOfBuffer(),
output_buffer_->data(),
bytes_to_write_);
output_buffer_->set_offset(0);
}
private:
virtual ~MockDeviceImpl()
{}
memcpy(output_buffer_->data() + bytes_to_write_, data, size);
bytes_to_write_ += size;
const char* device_model_;
};
if (bytes_to_write_ == size)
// If write loop wasn't yet started, then start it
WriteData();
}
class MockDeviceProvider : public AndroidDeviceManager::DeviceProvider {
virtual ~MockDeviceProvider()
{}
void SingleConnectionServer::AcceptConnection() {
CHECK(CalledOnValidThread());
virtual void QueryDevices(const QueryDevicesCallback& callback) OVERRIDE {
AndroidDeviceManager::Devices devices;
devices.push_back(new MockDeviceImpl("FirstDevice", 0, true, "Nexus 6"));
devices.push_back(new MockDeviceImpl("SecondDevice", 1, false, "Nexus 8"));
callback.Run(devices);
if (client_socket_) {
client_socket_->Disconnect();
client_socket_.reset();
}
};
// static
scoped_refptr<AndroidDeviceManager::DeviceProvider>
AndroidDeviceManager::GetMockDeviceProviderForTest() {
return new MockDeviceProvider();
int accept_result = server_socket_->Accept(&client_socket_,
base::Bind(&SingleConnectionServer::OnAccepted, base::Unretained(this)));
if (accept_result != net::ERR_IO_PENDING)
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&SingleConnectionServer::OnAccepted,
base::Unretained(this),
accept_result));
}
static scoped_refptr<DevToolsAndroidBridge::RemoteBrowser>
FindBrowserByDisplayName(DevToolsAndroidBridge::RemoteBrowsers browsers,
const std::string& name) {
for (DevToolsAndroidBridge::RemoteBrowsers::iterator it = browsers.begin();
it != browsers.end(); ++it)
if ((*it)->display_name() == name)
return *it;
return NULL;
void SingleConnectionServer::OnAccepted(int result) {
CHECK(CalledOnValidThread());
ASSERT_EQ(result, 0); // Fails if the socket is already in use.
parser_->Reset();
ReadData();
}
class DevToolsAdbBridgeTest : public InProcessBrowserTest,
public DevToolsAndroidBridge::DeviceListListener {
typedef DevToolsAndroidBridge::RemoteDevices::const_iterator rdci;
typedef DevToolsAndroidBridge::RemoteBrowsers::const_iterator rbci;
public:
virtual void DeviceListChanged(
const DevToolsAndroidBridge::RemoteDevices& devices) OVERRIDE {
devices_ = devices;
runner_->Quit();
void SingleConnectionServer::ReadData() {
CHECK(CalledOnValidThread());
if (input_buffer_->RemainingCapacity() == 0)
input_buffer_->SetCapacity(input_buffer_->capacity() * 2);
int read_result = client_socket_->Read(
input_buffer_.get(),
input_buffer_->RemainingCapacity(),
base::Bind(&SingleConnectionServer::OnDataRead, base::Unretained(this)));
if (read_result != net::ERR_IO_PENDING)
OnDataRead(read_result);
}
void SingleConnectionServer::OnDataRead(int count) {
CHECK(CalledOnValidThread());
if (count <= 0) {
AcceptConnection();
return;
}
void CheckDevices() {
ASSERT_EQ(2U, devices_.size());
input_buffer_->set_offset(input_buffer_->offset() + count);
scoped_refptr<DevToolsAndroidBridge::RemoteDevice> connected =
devices_[0]->is_connected() ? devices_[0] : devices_[1];
int bytes_processed;
scoped_refptr<DevToolsAndroidBridge::RemoteDevice> not_connected =
devices_[0]->is_connected() ? devices_[1] : devices_[0];
do {
char* data = input_buffer_->StartOfBuffer();
int data_size = input_buffer_->offset();
ASSERT_TRUE(connected->is_connected());
ASSERT_FALSE(not_connected->is_connected());
bytes_processed = parser_->Consume(data, data_size);
ASSERT_EQ(720, connected->screen_size().width());
ASSERT_EQ(1184, connected->screen_size().height());
if (bytes_processed) {
memmove(data, data + bytes_processed, data_size - bytes_processed);
input_buffer_->set_offset(data_size - bytes_processed);
}
} while (bytes_processed);
ASSERT_EQ("FirstDevice", connected->serial());
ASSERT_EQ("Nexus 6", connected->model());
// Posting is needed not to enter deep recursion in case too synchronous IO
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&SingleConnectionServer::ReadData, base::Unretained(this)));
}
ASSERT_EQ("SecondDevice", not_connected->serial());
ASSERT_EQ("Offline", not_connected->model());
void SingleConnectionServer::WriteData() {
CHECK(CalledOnValidThread());
const DevToolsAndroidBridge::RemoteBrowsers& browsers =
connected->browsers();
ASSERT_EQ(4U, browsers.size());
CHECK_GE(output_buffer_->capacity(),
output_buffer_->offset() + bytes_to_write_) << "Overflow";
scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> chrome =
FindBrowserByDisplayName(browsers, "Chrome");
ASSERT_TRUE(chrome);
int write_result = client_socket_->Write(
output_buffer_,
bytes_to_write_,
base::Bind(&SingleConnectionServer::OnDataWritten,
base::Unretained(this)));
if (write_result != net::ERR_IO_PENDING)
OnDataWritten(write_result);
}
scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> chrome_beta =
FindBrowserByDisplayName(browsers, "Chrome Beta");
ASSERT_TRUE(chrome_beta);
void SingleConnectionServer::OnDataWritten(int count) {
CHECK(CalledOnValidThread());
if (count < 0) {
AcceptConnection();
return;
}
scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> chromium =
FindBrowserByDisplayName(browsers, "Chromium");
ASSERT_FALSE(chromium);
CHECK_GT(count, 0);
CHECK_GE(output_buffer_->capacity(),
output_buffer_->offset() + bytes_to_write_) << "Overflow";
scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> webview =
FindBrowserByDisplayName(browsers, "WebView in com.sample.feed");
ASSERT_TRUE(webview);
bytes_to_write_ -= count;
output_buffer_->set_offset(output_buffer_->offset() + count);
scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> noprocess =
FindBrowserByDisplayName(browsers, "Noprocess");
ASSERT_TRUE(noprocess);
if (bytes_to_write_ != 0)
// Posting is needed not to enter deep recursion in case too synchronous IO
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&SingleConnectionServer::WriteData, base::Unretained(this)));
}
ASSERT_EQ("32.0.1679.0", chrome->version());
ASSERT_EQ("31.0.1599.0", chrome_beta->version());
ASSERT_EQ("4.0", webview->version());
std::vector<DevToolsTargetImpl*> chrome_pages =
chrome->CreatePageTargets();
std::vector<DevToolsTargetImpl*> chrome_beta_pages =
chrome_beta->CreatePageTargets();
std::vector<DevToolsTargetImpl*> webview_pages =
webview->CreatePageTargets();
class MockAdbServer : SingleConnectionServer::Parser,
base::NonThreadSafe {
public:
MockAdbServer() {
CHECK(CalledOnValidThread());
net::IPAddressNumber address;
net::ParseIPLiteralToNumber("127.0.0.1", &address);
net::IPEndPoint endpoint(address, kAdbPort);
server_.reset(new SingleConnectionServer(this, endpoint, kBufferSize));
}
ASSERT_EQ(1U, chrome_pages.size());
ASSERT_EQ(0U, chrome_beta_pages.size());
ASSERT_EQ(2U, webview_pages.size());
virtual ~MockAdbServer() {
CHECK(CalledOnValidThread());
}
// Check that we have non-empty description for webview pages.
ASSERT_EQ(0U, chrome_pages[0]->GetDescription().size());
ASSERT_NE(0U, webview_pages[0]->GetDescription().size());
ASSERT_NE(0U, webview_pages[1]->GetDescription().size());
private:
virtual int Consume(const char* data, int size) OVERRIDE {
CHECK(CalledOnValidThread());
if (!selected_socket_.empty()) {
std::string message(data, size);
size_t request_end_pos = message.find(kHttpRequestTerminator);
if (request_end_pos != std::string::npos) {
ProcessHTTPRequest(message.substr(0, request_end_pos));
return request_end_pos + strlen(kHttpRequestTerminator);
}
return 0;
}
ASSERT_EQ(GURL("http://www.chromium.org/"), chrome_pages[0]->GetUrl());
ASSERT_EQ("The Chromium Projects", chrome_pages[0]->GetTitle());
if (size >= kAdbMessageHeaderSize) {
std::string message_header(data, kAdbMessageHeaderSize);
int message_size;
STLDeleteElements(&chrome_pages);
STLDeleteElements(&webview_pages);
EXPECT_TRUE(base::HexStringToInt(message_header, &message_size));
if (size >= message_size + kAdbMessageHeaderSize) {
std::string message_body(data + kAdbMessageHeaderSize, message_size );
ProcessCommand(message_body);
return kAdbMessageHeaderSize + message_size;
}
}
void init() {
runner_ = new content::MessageLoopRunner;
return 0;
}
protected:
scoped_refptr<content::MessageLoopRunner> runner_;
DevToolsAndroidBridge::RemoteDevices devices_;
};
virtual void Reset() OVERRIDE {
CHECK(CalledOnValidThread());
selected_device_ = std::string();
selected_socket_ = std::string();
}
IN_PROC_BROWSER_TEST_F(DevToolsAdbBridgeTest, DiscoverAndroidBrowsers) {
init();
void ProcessHTTPRequest(const std::string& request) {
CHECK(CalledOnValidThread());
std::vector<std::string> tokens;
Tokenize(request, " ", &tokens);
CHECK_EQ(3U, tokens.size());
CHECK_EQ("GET", tokens[0]);
CHECK_EQ("HTTP/1.1", tokens[2]);
std::string path(tokens[1]);
if (path == kJsonPath)
path = kJsonListPath;
if (selected_socket_ == "chrome_devtools_remote") {
if (path == kJsonVersionPath)
SendHTTPResponse(kSampleChromeVersion);
else if (path == kJsonListPath)
SendHTTPResponse(kSampleChromePages);
else
NOTREACHED() << "Unknown command " << request;
} else if (selected_socket_ == "chrome_devtools_remote_1002") {
if (path == kJsonVersionPath)
SendHTTPResponse(kSampleChromeBetaVersion);
else if (path == kJsonListPath)
SendHTTPResponse(kSampleChromeBetaPages);
else
NOTREACHED() << "Unknown command " << request;
} else if (selected_socket_.find("noprocess_devtools_remote") == 0) {
if (path == kJsonVersionPath)
SendHTTPResponse("{}");
else if (path == kJsonListPath)
SendHTTPResponse("[]");
else
NOTREACHED() << "Unknown command " << request;
} else if (selected_socket_ == "webview_devtools_remote_2425") {
if (path == kJsonVersionPath)
SendHTTPResponse(kSampleWebViewVersion);
else if (path == kJsonListPath)
SendHTTPResponse(kSampleWebViewPages);
else
NOTREACHED() << "Unknown command " << request;
} else {
NOTREACHED() << "Unknown socket " << selected_socket_;
}
}
scoped_refptr<DevToolsAndroidBridge> android_bridge =
DevToolsAndroidBridge::Factory::GetForProfile(browser()->profile());
void ProcessCommand(const std::string& command) {
CHECK(CalledOnValidThread());
if (command == "host:devices") {
SendResponse(base::StringPrintf("%s\tdevice\n%s\toffline",
kSerialOnline,
kSerialOffline));
} else if (command.find(kHostTransportPrefix) == 0) {
selected_device_ = command.substr(strlen(kHostTransportPrefix));
SendResponse("");
} else if (selected_device_ != kSerialOnline) {
NOTREACHED() << "Unknown device - " << selected_device_;
} else if (command == kDeviceModelCommand) {
SendResponse(kDeviceModel);
} else if (command == kOpenedUnixSocketsCommand) {
SendResponse(kSampleOpenedUnixSockets);
} else if (command == kDumpsysCommand) {
SendResponse(kSampleDumpsys);
} else if (command == kListProcessesCommand) {
SendResponse(kSampleListProcesses);
} else if (command == kInstalledChromePackagesCommand) {
SendResponse(kSampleListPackages);
} else if (command.find(kLocalAbstractPrefix) == 0) {
selected_socket_ = command.substr(strlen(kLocalAbstractPrefix));
SendResponse("");
} else {
NOTREACHED() << "Unknown command - " << command;
}
}
AndroidDeviceManager::DeviceProviders providers;
providers.push_back(AndroidDeviceManager::GetMockDeviceProviderForTest());
void SendResponse(const std::string& response) {
CHECK(CalledOnValidThread());
std::stringstream response_stream;
response_stream << "OKAY";
int size = response.size();
if (size > 0) {
static const char kHexChars[] = "0123456789ABCDEF";
for (int i = 3; i >= 0; i--)
response_stream << kHexChars[ (size >> 4*i) & 0x0f ];
response_stream << response;
}
android_bridge->set_device_providers_for_test(providers);
server_->Send(response_stream.str());
}
if (!android_bridge) {
FAIL() << "Failed to get DevToolsAndroidBridge.";
void SendHTTPResponse(const std::string& body) {
CHECK(CalledOnValidThread());
std::string response_data(base::StringPrintf(kHttpResponse,
static_cast<int>(body.size()),
body.c_str()));
server_->Send(response_data);
}
android_bridge->AddDeviceListListener(this);
std::string selected_device_;
std::string selected_socket_;
runner_->Run();
scoped_ptr<SingleConnectionServer> server_;
};
static MockAdbServer* mock_adb_server_ = NULL;
void StartMockAdbServerOnIOThread() {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
CHECK(mock_adb_server_ == NULL);
mock_adb_server_ = new MockAdbServer();
}
CheckDevices();
void StopMockAdbServerOnIOThread() {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
CHECK(mock_adb_server_ != NULL);
delete mock_adb_server_;
mock_adb_server_ = NULL;
}
} // namespace
void StartMockAdbServer() {
BrowserThread::PostTaskAndReply(
BrowserThread::IO,
FROM_HERE,
base::Bind(&StartMockAdbServerOnIOThread),
base::MessageLoop::QuitClosure());
content::RunMessageLoop();
}
void StopMockAdbServer() {
BrowserThread::PostTaskAndReply(
BrowserThread::IO,
FROM_HERE,
base::Bind(&StopMockAdbServerOnIOThread),
base::MessageLoop::QuitClosure());
content::RunMessageLoop();
}
// Copyright 2014 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_BROWSER_DEVTOOLS_DEVICE_ADB_MOCK_ADB_SERVER_H_
#define CHROME_BROWSER_DEVTOOLS_DEVICE_ADB_MOCK_ADB_SERVER_H_
// Single instance mock ADB server for use in browser tests. Runs on IO thread.
// These methods can be called from any thread.
void StartMockAdbServer();
void StopMockAdbServer();
#endif // CHROME_BROWSER_DEVTOOLS_DEVICE_ADB_MOCK_ADB_SERVER_H_
......@@ -35,7 +35,7 @@ class AndroidDeviceManager
const CommandCallback& callback) = 0;
virtual void OpenSocket(const std::string& socket_name,
const SocketCallback& callback) = 0;
virtual void HttpQuery(const std::string& la_name,
void HttpQuery(const std::string& la_name,
const std::string& request,
const CommandCallback& callback);
void HttpUpgrade(const std::string& la_name,
......@@ -91,8 +91,6 @@ class AndroidDeviceManager
static scoped_refptr<DeviceProvider> GetUsbDeviceProvider(Profile* profile);
// Use only in a test and/or when DEBUG_DEVTOOLS is defined.
static scoped_refptr<DeviceProvider> GetSelfAsDeviceProvider(int port);
// Implemented in browser_tests.
static scoped_refptr<DeviceProvider> GetMockDeviceProviderForTest();
static scoped_refptr<AndroidDeviceManager> Create();
......
......@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/devtools/device/adb/mock_adb_server.h"
#include "chrome/browser/devtools/device/devtools_android_bridge.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
......@@ -71,12 +72,16 @@ IN_PROC_BROWSER_TEST_F(InspectUITest, AndroidTargets) {
scoped_refptr<DevToolsAndroidBridge> android_bridge =
DevToolsAndroidBridge::Factory::GetForProfile(browser()->profile());
AndroidDeviceManager::DeviceProviders providers;
providers.push_back(AndroidDeviceManager::GetMockDeviceProviderForTest());
providers.push_back(AndroidDeviceManager::GetAdbDeviceProvider());
android_bridge->set_device_providers_for_test(providers);
StartMockAdbServer();
ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUIInspectURL));
ASSERT_TRUE(WebUIBrowserTest::RunJavascriptAsyncTest("testAdbTargetsListed"));
StopMockAdbServer();
}
IN_PROC_BROWSER_TEST_F(InspectUITest, ReloadCrash) {
......
......@@ -971,7 +971,8 @@
'browser/crash_recovery_browsertest.cc',
'browser/custom_handlers/protocol_handler_registry_browsertest.cc',
'browser/devtools/device/adb/adb_client_socket_browsertest.cc',
'browser/devtools/device/devtools_android_bridge_browsertest.cc',
'browser/devtools/device/adb/mock_adb_server.cc',
'browser/devtools/device/adb/mock_adb_server.h',
'browser/devtools/device/port_forwarding_browsertest.cc',
'browser/devtools/devtools_sanity_browsertest.cc',
'browser/dom_distiller/dom_distiller_viewer_source_browsertest.cc',
......
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