Commit 649794a9 authored by Caleb Rouleau's avatar Caleb Rouleau Committed by Commit Bot

[ChromeDriver] More robust adb server socket protocol implementation

This implementation separates the reading of the socket from the
parsing of the data from the socket to avoid edge cases.

The existing implementation makes assumptions about how the result
will be pushed to the socket from the other end. It had different
behavior depending on whether we read in an output like
"OKAYOKAY0005abcde"
in separate chucks like
"OKAY"
"OKAY"
"0005abcde"
versus as larger chunks like
"OKAYOKAY"
"0005abcde"
or one big chunk like
"OKAYOKAY0005abcde"

Given that the adb server has some interesting edge cases (documented
in this new code), it was more complex to handle them with the old
implementation.

Note also that these edge cases were being hit when I tried to pass
port=0 to the adb server (so that it would allocate a remote forwarding
port itself so there would be no race condition). So that work is
blocked on this work.

Bug: chromedriver:2161
Change-Id: Ia83f8c3e4e0261a9467ab2814502c37f3f7e0645
Reviewed-on: https://chromium-review.googlesource.com/1011131
Commit-Queue: Caleb Rouleau <crouleau@chromium.org>
Reviewed-by: default avatarJohn Chen <johnchen@chromium.org>
Cr-Commit-Position: refs/heads/master@{#554886}
parent d0689dce
...@@ -388,6 +388,7 @@ test("chromedriver_unittests") { ...@@ -388,6 +388,7 @@ test("chromedriver_unittests") {
"command_listener_proxy_unittest.cc", "command_listener_proxy_unittest.cc",
"commands_unittest.cc", "commands_unittest.cc",
"logging_unittest.cc", "logging_unittest.cc",
"net/adb_client_socket_unittest.cc",
"net/timeout_unittest.cc", "net/timeout_unittest.cc",
"performance_logger_unittest.cc", "performance_logger_unittest.cc",
"server/http_handler_unittest.cc", "server/http_handler_unittest.cc",
...@@ -409,6 +410,8 @@ test("chromedriver_unittests") { ...@@ -409,6 +410,8 @@ test("chromedriver_unittests") {
"//base/test:run_all_unittests", "//base/test:run_all_unittests",
"//net", "//net",
"//net:http_server", "//net:http_server",
"//net:test_support",
"//testing/gmock",
"//testing/gtest", "//testing/gtest",
"//ui/base", "//ui/base",
"//ui/gfx", "//ui/gfx",
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "base/time/time.h" #include "base/time/time.h"
#include "chrome/test/chromedriver/chrome/status.h" #include "chrome/test/chromedriver/chrome/status.h"
#include "chrome/test/chromedriver/net/adb_client_socket.h" #include "chrome/test/chromedriver/net/adb_client_socket.h"
#include "net/base/net_errors.h"
namespace { namespace {
...@@ -48,11 +49,20 @@ class ResponseBuffer : public base::RefCountedThreadSafe<ResponseBuffer> { ...@@ -48,11 +49,20 @@ class ResponseBuffer : public base::RefCountedThreadSafe<ResponseBuffer> {
static_cast<int>(timeout.InSeconds()))); static_cast<int>(timeout.InSeconds())));
ready_.TimedWait(timeout); ready_.TimedWait(timeout);
} }
if (result_ < 0) if (result_ < 0) {
return Status(kUnknownError,
"Failed to run adb command with networking error: " +
net::ErrorToString(result_) +
". Is the adb server running? Extra response: <" +
response_ + ">.");
}
if (result_ > 0) {
return Status( return Status(
// TODO(crouleau): Use an error code that can differentiate this from
// the above networking error.
kUnknownError, kUnknownError,
"Failed to run adb command, is the adb server running? Error: " + "The adb command failed. Extra response: <" + response_ + ">.");
response_ + "."); }
*response = response_; *response = response_;
return Status(kOk); return Status(kOk);
} }
......
...@@ -24,17 +24,22 @@ ...@@ -24,17 +24,22 @@
namespace { namespace {
const int kBufferSize = 16 * 1024; const int kBufferSize = 16 * 1024;
const int kBufferGrowthRate = 16 * 1024;
const size_t kAdbDataChunkSize = 32 * 1024; const size_t kAdbDataChunkSize = 32 * 1024;
const char kOkayResponse[] = "OKAY"; const char kOkayResponse[] = "OKAY";
const char kFailResponse[] = "FAIL";
const char kHostTransportCommand[] = "host:transport:%s"; const char kHostTransportCommand[] = "host:transport:%s";
const char kLocalAbstractCommand[] = "localabstract:%s"; const char kLocalAbstractCommand[] = "localabstract:%s";
const char kSyncCommand[] = "sync:"; const char kSyncCommand[] = "sync:";
const char kSendCommand[] = "SEND"; const char kSendCommand[] = "SEND";
const char kDataCommand[] = "DATA"; const char kDataCommand[] = "DATA";
const char kDoneCommand[] = "DONE"; const char kDoneCommand[] = "DONE";
const int kAdbFailure = 1;
const int kAdbSuccess = 0;
typedef base::Callback<void(int, const std::string&)> CommandCallback; typedef base::Callback<void(int, const std::string&)> CommandCallback;
typedef base::Callback<void(int, net::StreamSocket*)> SocketCallback; typedef base::Callback<void(int, net::StreamSocket*)> SocketCallback;
typedef base::Callback<void(const std::string&)> ParserCallback;
std::string EncodeMessage(const std::string& message) { std::string EncodeMessage(const std::string& message) {
static const char kHexChars[] = "0123456789ABCDEF"; static const char kHexChars[] = "0123456789ABCDEF";
...@@ -71,16 +76,18 @@ class AdbTransportSocket : public AdbClientSocket { ...@@ -71,16 +76,18 @@ class AdbTransportSocket : public AdbClientSocket {
if (!CheckNetResultOrDie(result)) if (!CheckNetResultOrDie(result))
return; return;
SendCommand(base::StringPrintf(kHostTransportCommand, serial_.c_str()), SendCommand(base::StringPrintf(kHostTransportCommand, serial_.c_str()),
true, true, base::Bind(&AdbTransportSocket::SendLocalAbstract, false, true,
base::Unretained(this))); base::Bind(&AdbTransportSocket::SendLocalAbstract,
base::Unretained(this)));
} }
void SendLocalAbstract(int result, const std::string& response) { void SendLocalAbstract(int result, const std::string& response) {
if (!CheckNetResultOrDie(result)) if (!CheckNetResultOrDie(result))
return; return;
SendCommand(base::StringPrintf(kLocalAbstractCommand, socket_name_.c_str()), SendCommand(base::StringPrintf(kLocalAbstractCommand, socket_name_.c_str()),
true, true, base::Bind(&AdbTransportSocket::OnSocketAvailable, false, true,
base::Unretained(this))); base::Bind(&AdbTransportSocket::OnSocketAvailable,
base::Unretained(this)));
} }
void OnSocketAvailable(int result, const std::string& response) { void OnSocketAvailable(int result, const std::string& response) {
...@@ -276,12 +283,13 @@ class AdbQuerySocket : AdbClientSocket { ...@@ -276,12 +283,13 @@ class AdbQuerySocket : AdbClientSocket {
CheckNetResultOrDie(net::ERR_MSG_TOO_BIG); CheckNetResultOrDie(net::ERR_MSG_TOO_BIG);
return; return;
} }
bool is_void = current_query_ < queries_.size() - 1; bool has_output = current_query_ == queries_.size() - 1;
// The |shell| command is a special case because it is the only command that // The |shell| command is a special case because it is the only command that
// doesn't include a length at the beginning of the data stream. // doesn't include a length at the beginning of the data stream.
bool has_length = bool has_length =
!base::StartsWith(query, "shell:", base::CompareCase::SENSITIVE); !base::StartsWith(query, "shell:", base::CompareCase::SENSITIVE);
SendCommand(query, is_void, has_length, SendCommand(
query, has_output, has_length,
base::Bind(&AdbQuerySocket::OnResponse, base::Unretained(this))); base::Bind(&AdbQuerySocket::OnResponse, base::Unretained(this)));
} }
...@@ -341,7 +349,7 @@ class AdbSendFileSocket : AdbClientSocket { ...@@ -341,7 +349,7 @@ class AdbSendFileSocket : AdbClientSocket {
if (!CheckNetResultOrDie(result)) if (!CheckNetResultOrDie(result))
return; return;
SendCommand( SendCommand(
base::StringPrintf(kHostTransportCommand, serial_.c_str()), true, true, base::StringPrintf(kHostTransportCommand, serial_.c_str()), false, true,
base::Bind(&AdbSendFileSocket::SendSync, base::Unretained(this))); base::Bind(&AdbSendFileSocket::SendSync, base::Unretained(this)));
} }
...@@ -349,7 +357,7 @@ class AdbSendFileSocket : AdbClientSocket { ...@@ -349,7 +357,7 @@ class AdbSendFileSocket : AdbClientSocket {
if (!CheckNetResultOrDie(result)) if (!CheckNetResultOrDie(result))
return; return;
SendCommand( SendCommand(
kSyncCommand, true, true, kSyncCommand, false, true,
base::Bind(&AdbSendFileSocket::SendSend, base::Unretained(this))); base::Bind(&AdbSendFileSocket::SendSend, base::Unretained(this)));
} }
...@@ -388,7 +396,7 @@ class AdbSendFileSocket : AdbClientSocket { ...@@ -388,7 +396,7 @@ class AdbSendFileSocket : AdbClientSocket {
} }
void ReadFinalResponse(int result) { void ReadFinalResponse(int result) {
ReadResponse(callback_, true, false, result); ReadResponse(callback_, false, false, result);
} }
// Send a special payload command ("SEND", "DATA", or "DONE"). // Send a special payload command ("SEND", "DATA", or "DONE").
...@@ -523,179 +531,135 @@ void AdbClientSocket::Connect(const net::CompletionCallback& callback) { ...@@ -523,179 +531,135 @@ void AdbClientSocket::Connect(const net::CompletionCallback& callback) {
} }
void AdbClientSocket::SendCommand(const std::string& command, void AdbClientSocket::SendCommand(const std::string& command,
bool is_void, bool has_output,
bool has_length, bool has_length,
const CommandCallback& callback) { const CommandCallback& response_callback) {
scoped_refptr<net::StringIOBuffer> request_buffer = scoped_refptr<net::StringIOBuffer> request_buffer =
new net::StringIOBuffer(EncodeMessage(command)); new net::StringIOBuffer(EncodeMessage(command));
int result = socket_->Write( int result = socket_->Write(
request_buffer.get(), request_buffer->size(), request_buffer.get(), request_buffer->size(),
base::Bind(&AdbClientSocket::ReadResponse, base::Unretained(this), base::Bind(&AdbClientSocket::ReadResponse, base::Unretained(this),
callback, is_void, has_length), response_callback, has_output, has_length),
TRAFFIC_ANNOTATION_FOR_TESTS); TRAFFIC_ANNOTATION_FOR_TESTS);
if (result != net::ERR_IO_PENDING) if (result != net::ERR_IO_PENDING)
ReadResponse(callback, is_void, has_length, result); ReadResponse(response_callback, has_output, has_length, result);
} }
void AdbClientSocket::ReadResponse(const CommandCallback& callback, void AdbClientSocket::ReadResponse(const CommandCallback& response_callback,
bool is_void, bool has_output,
bool has_length, bool has_length,
int result) { int result) {
if (result < 0) { if (result < 0) {
callback.Run(result, "IO error"); response_callback.Run(result, "IO error");
return;
}
scoped_refptr<net::IOBuffer> response_buffer =
new net::IOBuffer(kBufferSize);
result = socket_->Read(response_buffer.get(),
kBufferSize,
base::Bind(&AdbClientSocket::OnResponseStatus,
base::Unretained(this),
callback,
is_void,
has_length,
response_buffer));
if (result != net::ERR_IO_PENDING)
OnResponseStatus(callback, is_void, has_length, response_buffer, result);
}
void AdbClientSocket::OnResponseStatus(
const CommandCallback& callback,
bool is_void,
bool has_length,
scoped_refptr<net::IOBuffer> response_buffer,
int result) {
if (result <= 0) {
callback.Run(result == 0 ? net::ERR_CONNECTION_CLOSED : result,
"IO error");
return; return;
} }
scoped_refptr<net::GrowableIOBuffer> socket_buffer =
std::string data = std::string(response_buffer->data(), result); new net::GrowableIOBuffer;
if (result < 4) { socket_buffer->SetCapacity(kBufferSize);
callback.Run(net::ERR_FAILED, "Response is too short: " + data); if (has_output) {
return; const ParserCallback& parse_output_callback = base::Bind(
} &AdbClientSocket::ParseOutput, has_length, response_callback);
int socket_result = socket_->Read(
std::string status = data.substr(0, 4); socket_buffer.get(), kBufferSize,
if (status != kOkayResponse) { base::Bind(&AdbClientSocket::ReadUntilEOF, base::Unretained(this),
callback.Run(net::ERR_FAILED, data); parse_output_callback, response_callback, socket_buffer));
return; if (socket_result != net::ERR_IO_PENDING) {
} ReadUntilEOF(parse_output_callback, response_callback, socket_buffer,
socket_result);
data = data.substr(4);
if (!is_void) {
if (!has_length) {
// Payload doesn't include length, so skip straight to reading in data.
OnResponseData(callback, data, response_buffer, -1, 0);
} else if (data.length() >= 4) {
// We've already read the length out of the socket, so we don't need to
// read more yet.
OnResponseLength(callback, data, response_buffer, 0);
} else {
// Part or all of the length is still in the socket, so we need to read it
// out of the socket before parsing the length.
result = socket_->Read(response_buffer.get(),
kBufferSize,
base::Bind(&AdbClientSocket::OnResponseLength,
base::Unretained(this),
callback,
data,
response_buffer));
if (result != net::ERR_IO_PENDING)
OnResponseLength(callback, data, response_buffer, result);
} }
} else { } else {
callback.Run(net::OK, data); int socket_result =
socket_->Read(socket_buffer.get(), kBufferSize,
base::Bind(&AdbClientSocket::ReadStatusOutput,
response_callback, socket_buffer));
if (socket_result != net::ERR_IO_PENDING) {
ReadStatusOutput(response_callback, socket_buffer, socket_result);
}
} }
} }
void AdbClientSocket::OnResponseLength( void AdbClientSocket::ReadStatusOutput(
const CommandCallback& callback, const CommandCallback& response_callback,
const std::string& response, scoped_refptr<net::IOBuffer> socket_buffer,
scoped_refptr<net::IOBuffer> response_buffer, int socket_result) {
int result) { // Sometimes adb-server responds with the 8 char string "OKAY\0\0\0\0" instead
if (result < 0) { // of the expected "OKAY".
callback.Run(result, "IO error"); if (socket_result >= 4 &&
std::string(socket_buffer->data(), 4) == kOkayResponse) {
response_callback.Run(kAdbSuccess, kOkayResponse);
return; return;
} }
response_callback.Run(kAdbFailure, kFailResponse);
}
std::string new_response = void AdbClientSocket::ReadUntilEOF(
response + std::string(response_buffer->data(), result); const ParserCallback& parse_output_callback,
// Sometimes ADB server will respond like "OKAYOKAY<payload_length><payload>" const CommandCallback& response_callback,
// instead of the expected "OKAY<payload_length><payload>". scoped_refptr<net::GrowableIOBuffer> socket_buffer,
// For the former case, the first OKAY is cropped off in the OnResponseStatus int socket_result) {
// but we still need to crop the second OKAY. if (socket_result < 0) {
if (new_response.substr(0, 4) == "OKAY") { VLOG(3) << "IO error";
VLOG(3) << "cutting new_response down to size"; response_callback.Run(socket_result, "IO error");
new_response = new_response.substr(4); return;
} } else if (socket_result > 0) {
if (new_response.length() < 4) { // We read in data. Let's try to read in more.
if (new_response.length() == 0 && result == net::OK) { socket_buffer->set_offset(socket_buffer->offset() + socket_result);
// The socket is shut down and all data has been read. if (socket_buffer->RemainingCapacity() == 0) {
// Note that this is a hack because is_void is false, socket_buffer->SetCapacity(socket_buffer->capacity() + kBufferGrowthRate);
// but we are not returning any data. The upstream logic
// to determine is_void is not in a good state.
// However, this is a better solution than trusting is_void anyway
// because otherwise this can become an infinite loop.
callback.Run(net::OK, new_response);
return;
} }
result = socket_->Read(response_buffer.get(), int new_socket_result = socket_->Read(
kBufferSize, socket_buffer.get(), socket_buffer->RemainingCapacity(),
base::Bind(&AdbClientSocket::OnResponseLength, base::Bind(&AdbClientSocket::ReadUntilEOF, base::Unretained(this),
base::Unretained(this), parse_output_callback, response_callback, socket_buffer));
callback, if (new_socket_result != net::ERR_IO_PENDING) {
new_response, ReadUntilEOF(parse_output_callback, response_callback, socket_buffer,
response_buffer)); new_socket_result);
if (result != net::ERR_IO_PENDING)
OnResponseLength(callback, new_response, response_buffer, result);
} else {
int payload_length = 0;
if (!base::HexStringToInt(new_response.substr(0, 4), &payload_length)) {
VLOG(1) << "net error since payload length wasn't readable.";
callback.Run(net::ERR_FAILED, "response <" + new_response +
"> from adb server was unexpected");
return;
} }
} else if (socket_result == 0) {
new_response = new_response.substr(4); // We hit EOF. The socket is closed on the other side.
int bytes_left = payload_length - new_response.length(); std::string adb_output(socket_buffer->StartOfBuffer(),
OnResponseData(callback, new_response, response_buffer, bytes_left, 0); socket_buffer->offset());
parse_output_callback.Run(adb_output);
} }
} }
void AdbClientSocket::OnResponseData( void AdbClientSocket::ParseOutput(bool has_length,
const CommandCallback& callback, const CommandCallback& response_callback,
const std::string& response, const std::string& adb_output) {
scoped_refptr<net::IOBuffer> response_buffer, int result = kAdbFailure;
int bytes_left, // Expected data format is
int result) { // "OKAY<payload_length><payload>" if has_length
if (result < 0) { // or "OKAY<payload>" if not has_length.
callback.Run(result, "IO error"); // or "OKAY" if there is no output (regardless of has_length).
return; // FAIL is returned instead of OKAY if there was an error.
} std::string output(adb_output);
if (output.substr(0, 4) == kOkayResponse) {
bytes_left -= result; output = output.substr(4);
std::string new_response = result = kAdbSuccess;
response + std::string(response_buffer->data(), result); }
if (bytes_left == 0) { if (output.substr(0, 4) == kFailResponse) {
callback.Run(net::OK, new_response); output = output.substr(4);
return; result = kAdbFailure;
}
if (output.substr(0, 4) == kOkayResponse) {
VLOG(3) << "ADB server responded with \"OKAYOKAY\" instead of \"OKAY\".";
output = output.substr(4);
// Note: It's unclear whether we should set result = kAdbSuccess here or
// not. I've never seen "FAILOKAY" in the wild.
}
// Note that has_length=true implies that the payload length will be prepended
// to the payload in the case that there is a payload. If there is no payload,
// then there may not be a payload length.
if (has_length && output.size() != 0) {
if (output.size() >= 4) {
// Just skip the hex string length. It is unnecessary since
// EOF is sent after the message is complete.
output = output.substr(4);
} else {
VLOG(3) << "Error: ADB server responded without the expected hexstring"
<< " length";
result = kAdbFailure;
}
} }
response_callback.Run(result, output);
// Read tail
result = socket_->Read(response_buffer.get(),
kBufferSize,
base::Bind(&AdbClientSocket::OnResponseData,
base::Unretained(this),
callback,
new_response,
response_buffer,
bytes_left));
if (result > 0)
OnResponseData(callback, new_response, response_buffer, bytes_left, result);
else if (result != net::ERR_IO_PENDING)
callback.Run(net::OK, new_response);
} }
...@@ -16,6 +16,7 @@ class AdbClientSocket { ...@@ -16,6 +16,7 @@ class AdbClientSocket {
typedef base::Callback<void(int, const std::string&)> CommandCallback; typedef base::Callback<void(int, const std::string&)> CommandCallback;
typedef base::Callback<void(int result, typedef base::Callback<void(int result,
net::StreamSocket*)> SocketCallback; net::StreamSocket*)> SocketCallback;
typedef base::Callback<void(const std::string&)> ParserCallback;
static void AdbQuery(int port, static void AdbQuery(int port,
const std::string& query, const std::string& query,
...@@ -51,38 +52,34 @@ class AdbClientSocket { ...@@ -51,38 +52,34 @@ class AdbClientSocket {
void Connect(const net::CompletionCallback& callback); void Connect(const net::CompletionCallback& callback);
void SendCommand(const std::string& command, void SendCommand(const std::string& command,
bool is_void, bool has_output,
bool has_length, bool has_length,
const CommandCallback& callback); const CommandCallback& response_callback);
std::unique_ptr<net::StreamSocket> socket_; std::unique_ptr<net::StreamSocket> socket_;
void ReadResponse(const CommandCallback& callback, void ReadResponse(const CommandCallback& callback,
bool is_void, bool has_output,
bool has_length, bool has_length,
int result); int result);
private: private:
void OnResponseStatus(const CommandCallback& callback, static void ReadStatusOutput(const CommandCallback& response_callback,
bool is_void, scoped_refptr<net::IOBuffer> socket_buffer,
bool has_length, int socket_result);
scoped_refptr<net::IOBuffer> response_buffer,
int result); void ReadUntilEOF(const ParserCallback& parse_output_callback,
const CommandCallback& response_callback,
void OnResponseLength(const CommandCallback& callback, scoped_refptr<net::GrowableIOBuffer> socket_buffer,
const std::string& response, int socket_result);
scoped_refptr<net::IOBuffer> response_buffer,
int result); static void ParseOutput(bool has_length,
const CommandCallback& response_callback,
void OnResponseData(const CommandCallback& callback, const std::string& adb_output);
const std::string& response,
scoped_refptr<net::IOBuffer> response_buffer,
int bytes_left,
int result);
int port_; int port_;
friend class AdbClientSocketTest;
DISALLOW_COPY_AND_ASSIGN(AdbClientSocket); DISALLOW_COPY_AND_ASSIGN(AdbClientSocket);
}; };
#endif // CHROME_TEST_CHROMEDRIVER_NET_ADB_CLIENT_SOCKET_H_ #endif // CHROME_TEST_CHROMEDRIVER_NET_ADB_CLIENT_SOCKET_H_
// Copyright (c) 2016 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/net/adb_client_socket.h"
#include "base/logging.h"
#include "base/run_loop.h"
#include "base/test/gtest_util.h"
#include "base/test/mock_callback.h"
#include "net/socket/socket_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
class MockSocket : public net::MockClientSocket {
public:
int return_values_length;
std::string* return_values_array;
MockSocket(std::string* return_values_array, int return_values_length)
: MockClientSocket(net::NetLogWithSource()),
return_values_length(return_values_length),
return_values_array(return_values_array) {}
MockSocket() : MockClientSocket(net::NetLogWithSource()) {}
int Read(net::IOBuffer* buf,
int buf_len,
net::CompletionOnceCallback callback) override {
int result = ReadHelper(buf, buf_len);
if (result == net::ERR_IO_PENDING) {
// Note this really should be posted as a task, but that is a pain to
// figure out.
std::move(callback).Run(ReadLoop(buf, buf_len));
}
return result;
}
int ReadLoop(net::IOBuffer* buf, int buf_len) {
int result;
do {
result = ReadHelper(buf, buf_len);
} while (result == net::ERR_IO_PENDING);
return result;
}
int ReadHelper(net::IOBuffer* buf, int buf_len) {
if (return_values_length) {
int chunk_length = return_values_array[0].length();
if (chunk_length > buf_len) {
strncpy(buf->data(), return_values_array[0].data(), buf_len);
return_values_array[0] = return_values_array[0].substr(buf_len);
return buf_len;
}
strncpy(buf->data(), return_values_array[0].data(), chunk_length);
return_values_length--;
return_values_array++;
if (chunk_length == 0) {
return net::ERR_IO_PENDING;
}
return chunk_length;
}
return 0;
}
int Write(
net::IOBuffer* buf,
int buf_len,
const net::CompletionOnceCallback callback,
const net::NetworkTrafficAnnotationTag& traffic_annotation) override {
return 0;
}
// The following functions are not expected to be used.
int Connect(const net::CompletionOnceCallback callback) override {
return net::ERR_UNEXPECTED;
}
bool GetSSLInfo(net::SSLInfo* ssl_info) override { return false; }
bool WasEverUsed() const override { return false; }
};
class AdbClientSocketTest : public testing::Test {
public:
void TestParsing(const char* adb_output,
bool has_length,
int expected_result_code,
const char* expected_response) {
base::MockCallback<AdbClientSocket::CommandCallback> callback;
EXPECT_CALL(callback, Run(expected_result_code, expected_response));
AdbClientSocket::ParseOutput(has_length, callback.Get(),
std::string(adb_output));
}
void TestReadUntilEOF_EOF(const char* data_on_buffer) {
std::unique_ptr<MockSocket> socket = std::make_unique<MockSocket>();
AdbClientSocket adb_socket(3);
base::MockCallback<AdbClientSocket::ParserCallback> parse_callback;
EXPECT_CALL(parse_callback, Run(data_on_buffer));
base::MockCallback<AdbClientSocket::CommandCallback> response_callback;
// The following means "expect not to be called."
EXPECT_CALL(response_callback, Run(0, "")).Times(0);
scoped_refptr<net::GrowableIOBuffer> buffer = new net::GrowableIOBuffer;
buffer->SetCapacity(100);
strcpy(buffer->data(), data_on_buffer);
buffer->set_offset(strlen(data_on_buffer));
adb_socket.ReadUntilEOF(parse_callback.Get(), response_callback.Get(),
buffer, 0);
}
void TestReadUntilEOF_Error() {
int error_code = -1;
std::unique_ptr<MockSocket> socket = std::make_unique<MockSocket>();
// 3 is an arbitrary meaningless number in the following call.
AdbClientSocket adb_socket(3);
base::MockCallback<AdbClientSocket::ParserCallback> parse_callback;
// The following means "expect not to be called."
EXPECT_CALL(parse_callback, Run("")).Times(0);
base::MockCallback<AdbClientSocket::CommandCallback> response_callback;
EXPECT_CALL(response_callback, Run(error_code, "IO error")).Times(1);
scoped_refptr<net::GrowableIOBuffer> buffer = new net::GrowableIOBuffer;
buffer->SetCapacity(100);
adb_socket.ReadUntilEOF(parse_callback.Get(), response_callback.Get(),
buffer, error_code);
}
void TestReadUntilEOF_Recurse(std::string* chunks,
int number_chunks,
std::string expected_result) {
// 3 is an arbitrary meaningless number in the following call.
AdbClientSocket adb_socket(3);
adb_socket.socket_.reset(new MockSocket(chunks, number_chunks));
base::MockCallback<AdbClientSocket::ParserCallback> parse_callback;
EXPECT_CALL(parse_callback, Run(expected_result.c_str())).Times(1);
base::MockCallback<AdbClientSocket::CommandCallback> response_callback;
// The following means "expect not to be called."
EXPECT_CALL(response_callback, Run(0, "")).Times(0);
scoped_refptr<net::GrowableIOBuffer> buffer = new net::GrowableIOBuffer;
int initial_capacity = 4;
buffer->SetCapacity(initial_capacity);
int result = adb_socket.socket_->Read(
buffer.get(), initial_capacity,
base::Bind(&AdbClientSocket::ReadUntilEOF,
base::Unretained(&adb_socket), parse_callback.Get(),
response_callback.Get(), buffer));
if (result != net::ERR_IO_PENDING) {
adb_socket.ReadUntilEOF(parse_callback.Get(), response_callback.Get(),
buffer, result);
}
}
void TestReadStatus(const char* buffer_data,
int result,
const char* expected_result_string,
int expected_result_code) {
base::MockCallback<AdbClientSocket::ParserCallback> parse_callback;
// The following means "expect not to be called."
EXPECT_CALL(parse_callback, Run("")).Times(0);
base::MockCallback<AdbClientSocket::CommandCallback> response_callback;
EXPECT_CALL(response_callback,
Run(expected_result_code, expected_result_string))
.Times(1);
scoped_refptr<net::GrowableIOBuffer> buffer = new net::GrowableIOBuffer;
int initial_capacity = 100;
buffer->SetCapacity(initial_capacity);
if (result > 0) {
strncpy(buffer->data(), buffer_data, result);
}
AdbClientSocket::ReadStatusOutput(response_callback.Get(), buffer, result);
}
};
TEST_F(AdbClientSocketTest, ParseOutput) {
TestParsing("OKAY000512345", true, 0, "12345");
TestParsing("FAIL000512345", true, 1, "12345");
TestParsing("OKAY12345", false, 0, "12345");
TestParsing("FAIL12345", false, 1, "12345");
TestParsing("ABC", false, 1, "ABC");
TestParsing("ABC", true, 1, "ABC");
TestParsing("OKAYAB", false, 0, "AB");
TestParsing("OKAYAB", true, 1, "AB");
TestParsing("OKAYOKAYOKAY", false, 0, "OKAY");
TestParsing("", false, 1, "");
TestParsing("", true, 1, "");
TestParsing("OKAYOKAY", true, 0, "");
TestParsing("OKAYOKAY", false, 0, "");
}
TEST_F(AdbClientSocketTest, ReadUntilEOF_EOFEmptyString) {
TestReadUntilEOF_EOF("");
}
TEST_F(AdbClientSocketTest, ReadUntilEOF_EOFNonEmptyString1) {
TestReadUntilEOF_EOF("blah");
}
TEST_F(AdbClientSocketTest, ReadUntilEOF_EOFNonEmptyString2) {
TestReadUntilEOF_EOF("blahbla");
}
TEST_F(AdbClientSocketTest, ReadUntilEOF_Error) {
TestReadUntilEOF_Error();
}
TEST_F(AdbClientSocketTest, ReadUntilEOF_GrowBuffer) {
std::string chunks[7] = {"This", "", " data", " should",
"", " be ", "read in."};
TestReadUntilEOF_Recurse(chunks, 7, "This data should be read in.");
}
TEST_F(AdbClientSocketTest, ReadUntilEOF_EmptyChunks) {
std::string chunks[5] = {"", "", "", "", ""};
TestReadUntilEOF_Recurse(chunks, 5, "");
}
TEST_F(AdbClientSocketTest, ReadUntilEOF_Empty) {
std::string chunks[0] = {};
TestReadUntilEOF_Recurse(chunks, 0, "");
}
TEST_F(AdbClientSocketTest, ReadUntilEOF_EmptyEndingChunk) {
std::string chunks[2] = {"yeah", ""};
TestReadUntilEOF_Recurse(chunks, 2, "yeah");
}
TEST_F(AdbClientSocketTest, ReadStatusOutput_Okay) {
TestReadStatus("OKAY", 4, "OKAY", 0);
}
TEST_F(AdbClientSocketTest, ReadStatusOutput_OkayNulls) {
TestReadStatus("OKAY\0\0\0\0", 8, "OKAY", 0);
}
TEST_F(AdbClientSocketTest, ReadStatusOutput_Fail) {
TestReadStatus("FAIL", 4, "FAIL", 1);
}
TEST_F(AdbClientSocketTest, ReadStatusOutput_FailEmpty) {
TestReadStatus("", 0, "FAIL", 1);
}
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