Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(5033)

Unified Diff: chrome/browser/devtools/device/adb/mock_adb_server.cc

Issue 257563003: DevTools: Unify ADB-related browser tests (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fixed clang compile Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/devtools/device/adb/mock_adb_server.cc
diff --git a/chrome/browser/devtools/device/adb/mock_adb_server.cc b/chrome/browser/devtools/device/adb/mock_adb_server.cc
new file mode 100644
index 0000000000000000000000000000000000000000..b116f84cd5e93595987965828e4869cef2e23912
--- /dev/null
+++ b/chrome/browser/devtools/device/adb/mock_adb_server.cc
@@ -0,0 +1,557 @@
+// 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.
+
+#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 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 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"
+ "00000000: 00000004 00000000"
+ " 00000000 0002 01 3328 /dev/socket/wpa_wlan0\n"
+ "00000000: 00000002 00000000"
+ " 00010000 0001 01 5394 /dev/socket/vold\n"
+ "00000000: 00000002 00000000"
+ " 00010000 0001 01 11810 @webview_devtools_remote_2425\n"
+ "00000000: 00000002 00000000"
+ " 00010000 0001 01 20893 @chrome_devtools_remote\n"
+ "00000000: 00000002 00000000"
+ " 00010000 0001 01 20894 @chrome_devtools_remote_1002\n"
+ "00000000: 00000002 00000000"
+ " 00010000 0001 01 20895 @noprocess_devtools_remote\n";
+
+const char kSampleListProcesses[] =
+ "USER PID PPID VSIZE RSS WCHAN PC NAME\n"
+ "root 1 0 688 508 ffffffff 00000000 S /init\r\n"
+ "u0_a75 2425 123 933736 193024 ffffffff 00000000 S com.sample.feed\r\n"
+ "nfc 741 123 706448 26316 ffffffff 00000000 S com.android.nfc\r\n"
+ "u0_a76 1001 124 111111 222222 ffffffff 00000000 S com.android.chrome\r\n"
+ "u0_a77 1002 125 111111 222222 ffffffff 00000000 S com.chrome.beta\r\n"
+ "u0_a78 1003 126 111111 222222 ffffffff 00000000 S com.noprocess.app\r\n";
+
+const char kSampleListPackages[] =
+ "package:com.sample.feed\r\n"
+ "package:com.android.nfc\r\n"
+ "package:com.android.chrome\r\n"
+ "package:com.chrome.beta\r\n"
+ "package:com.google.android.apps.chrome\r\n";
+
+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
+ " mForceStatusBar=false mForceStatusBarFromKeyguard=false\r\n";
+
+char kSampleChromeVersion[] = "{\n"
+ " \"Browser\": \"Chrome/32.0.1679.0\",\n"
+ " \"Protocol-Version\": \"1.0\",\n"
+ " \"User-Agent\": \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 "
+ "(KHTML, like Gecko) Chrome/32.0.1679.0 Safari/537.36\",\n"
+ " \"WebKit-Version\": \"537.36 (@160162)\"\n"
+ "}";
+
+char kSampleChromeBetaVersion[] = "{\n"
+ " \"Browser\": \"Chrome/31.0.1599.0\",\n"
+ " \"Protocol-Version\": \"1.0\",\n"
+ " \"User-Agent\": \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 "
+ "(KHTML, like Gecko) Chrome/32.0.1679.0 Safari/537.36\",\n"
+ " \"WebKit-Version\": \"537.36 (@160162)\"\n"
+ "}";
+
+char kSampleWebViewVersion[] = "{\n"
+ " \"Browser\": \"Version/4.0\",\n"
+ " \"Protocol-Version\": \"1.0\",\n"
+ " \"User-Agent\": \"Mozilla/5.0 (Linux; Android 4.3; Build/KRS74B) "
+ "AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Safari/537.36\",\n"
+ " \"WebKit-Version\": \"537.36 (@157588)\"\n"
+ "}";
+
+char kSampleChromePages[] = "[ {\n"
+ " \"description\": \"\",\n"
+ " \"devtoolsFrontendUrl\": \"/devtools/devtools.html?"
+ "ws=/devtools/page/755DE5C9-D49F-811D-0693-51B8E15C80D2\",\n"
+ " \"id\": \"755DE5C9-D49F-811D-0693-51B8E15C80D2\",\n"
+ " \"title\": \"The Chromium Projects\",\n"
+ " \"type\": \"page\",\n"
+ " \"url\": \"http://www.chromium.org/\",\n"
+ " \"webSocketDebuggerUrl\": \""
+ "ws:///devtools/page/755DE5C9-D49F-811D-0693-51B8E15C80D2\"\n"
+ "} ]";
+
+char kSampleChromeBetaPages[] = "[]";
+
+char kSampleWebViewPages[] = "[ {\n"
+ " \"description\": \"{\\\"attached\\\":false,\\\"empty\\\":false,"
+ "\\\"height\\\":1173,\\\"screenX\\\":0,\\\"screenY\\\":0,"
+ "\\\"visible\\\":true,\\\"width\\\":800}\",\n"
+ " \"devtoolsFrontendUrl\": \"http://chrome-devtools-frontend.appspot.com/"
+ "serve_rev/@157588/devtools.html?ws="
+ "/devtools/page/3E962D4D-B676-182D-3BE8-FAE7CE224DE7\",\n"
+ " \"faviconUrl\": \"http://chromium.org/favicon.ico\",\n"
+ " \"id\": \"3E962D4D-B676-182D-3BE8-FAE7CE224DE7\",\n"
+ " \"thumbnailUrl\": \"/thumb/3E962D4D-B676-182D-3BE8-FAE7CE224DE7\",\n"
+ " \"title\": \"Blink - The Chromium Projects\",\n"
+ " \"type\": \"page\",\n"
+ " \"url\": \"http://www.chromium.org/blink\",\n"
+ " \"webSocketDebuggerUrl\": \"ws:///devtools/"
+ "page/3E962D4D-B676-182D-3BE8-FAE7CE224DE7\"\n"
+ "}, {\n"
+ " \"description\": \"{\\\"attached\\\":true,\\\"empty\\\":true,"
+ "\\\"screenX\\\":0,\\\"screenY\\\":33,\\\"visible\\\":false}\",\n"
+ " \"devtoolsFrontendUrl\": \"http://chrome-devtools-frontend.appspot.com/"
+ "serve_rev/@157588/devtools.html?ws="
+ "/devtools/page/44681551-ADFD-2411-076B-3AB14C1C60E2\",\n"
+ " \"faviconUrl\": \"\",\n"
+ " \"id\": \"44681551-ADFD-2411-076B-3AB14C1C60E2\",\n"
+ " \"thumbnailUrl\": \"/thumb/44681551-ADFD-2411-076B-3AB14C1C60E2\",\n"
+ " \"title\": \"More Activity\",\n"
+ " \"type\": \"page\",\n"
+ " \"url\": \"about:blank\",\n"
+ " \"webSocketDebuggerUrl\": \"ws:///devtools/page/"
+ "44681551-ADFD-2411-076B-3AB14C1C60E2\"\n"
+ "}]";
+
+static const int kBufferSize = 16*1024;
+static const int kAdbPort = 5037;
+
+static const int kAdbMessageHeaderSize = 4;
+
+
+class SingleConnectionServer : base::NonThreadSafe {
+ public:
+ class Parser {
+ public:
+ virtual int Consume(const char* data, int size) = 0;
+ virtual void Reset() = 0;
+
+ protected:
+ virtual ~Parser() {}
+ };
+
+ SingleConnectionServer(
+ Parser* parser, net::IPEndPoint endpoint, int buffer_size);
+
+ virtual ~SingleConnectionServer();
+
+ void Send(const std::string& message);
+
+ private:
+ void SendData(const char* data, int size);
+
+ void AcceptConnection();
+ void OnAccepted(int result);
+
+ void ReadData();
+ void OnDataRead(int count);
+
+ void WriteData();
+ void OnDataWritten(int count);
+
+ 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());
+}
+
+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);
+ }
+
+ 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() {
+ CHECK(CalledOnValidThread());
+
+ 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)
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&SingleConnectionServer::OnAccepted,
+ base::Unretained(this),
+ accept_result));
+}
+
+void SingleConnectionServer::OnAccepted(int result) {
+ CHECK(CalledOnValidThread());
+
+ ASSERT_EQ(result, 0); // Fails if the socket is already in use.
+ parser_->Reset();
+ ReadData();
+}
+
+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;
+ }
+
+ 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 = parser_->Consume(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
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&SingleConnectionServer::ReadData, base::Unretained(this)));
+}
+
+void SingleConnectionServer::WriteData() {
+ CHECK(CalledOnValidThread());
+
+ 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(CalledOnValidThread());
+
+ 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
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&SingleConnectionServer::WriteData, base::Unretained(this)));
+}
+
+
+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));
+ }
+
+ virtual ~MockAdbServer() {
+ CHECK(CalledOnValidThread());
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ virtual void Reset() OVERRIDE {
+ CHECK(CalledOnValidThread());
+ selected_device_ = std::string();
+ selected_socket_ = std::string();
+ }
+
+ 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_;
+ }
+ }
+
+ 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;
+ }
+ }
+
+ 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;
+ }
+
+ server_->Send(response_stream.str());
+ }
+
+ 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);
+ }
+
+ std::string selected_device_;
+ std::string selected_socket_;
+
+ 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();
+}
+
+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();
+}
+
« no previous file with comments | « chrome/browser/devtools/device/adb/mock_adb_server.h ('k') | chrome/browser/devtools/device/android_device_manager.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698