OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "base/strings/string_number_conversions.h" | |
6 #include "chrome/browser/devtools/android_device.h" | |
7 #include "chrome/browser/ui/browser.h" | |
8 #include "chrome/test/base/in_process_browser_test.h" | |
9 #include "content/public/browser/browser_thread.h" | |
10 #include "content/public/test/browser_test.h" | |
11 #include "content/public/test/test_utils.h" | |
12 #include "net/base/host_port_pair.h" | |
13 #include "net/base/io_buffer.h" | |
14 #include "net/base/ip_endpoint.h" | |
15 #include "net/base/net_errors.h" | |
16 #include "net/base/net_log.h" | |
17 #include "net/socket/stream_socket.h" | |
18 #include "net/socket/tcp_server_socket.h" | |
19 | |
20 using namespace net; | |
21 | |
22 static const char kDeviceModelCommand[] = "shell:getprop ro.product.model"; | |
23 static const char kDeviceModel[] = "Nexus 8"; | |
24 | |
25 static const int kBufferSize = 16*1024; | |
26 static const int kAdbPort = 5037; | |
27 | |
28 static const int kAdbMessageHeaderSize = 4; | |
29 | |
30 class MockAdbServer { | |
31 public: | |
32 MockAdbServer() { | |
33 input_buffer_ = new net::GrowableIOBuffer(); | |
34 input_buffer_->SetCapacity(kBufferSize); | |
35 | |
36 output_buffer_ = new net::GrowableIOBuffer(); | |
37 } | |
38 | |
39 void Start(base::Callback<bool(void)> callback) { | |
40 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); | |
41 | |
42 IPAddressNumber address; | |
43 ParseIPLiteralToNumber("127.0.0.1", &address); | |
44 | |
45 IPEndPoint endpoint(address, kAdbPort); | |
46 | |
47 server_socket_.reset(new TCPServerSocket(NULL, NetLog::Source())); | |
48 server_socket_->Listen(endpoint, 2); | |
49 | |
50 callback.Run(); | |
51 | |
52 AcceptConnection(); | |
53 } | |
54 | |
55 void Shutdown(base::Callback<bool(void)> callback) { | |
56 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); | |
57 server_socket_.reset(); | |
58 | |
59 if (client_socket_) { | |
60 client_socket_->Disconnect(); | |
61 client_socket_.reset(); | |
62 } | |
63 | |
64 callback.Run(); | |
65 } | |
66 | |
67 private: | |
68 void AcceptConnection() { | |
69 if (client_socket_) { | |
70 client_socket_->Disconnect(); | |
71 client_socket_.reset(); | |
72 } | |
73 | |
74 int accept_result = server_socket_->Accept(&client_socket_, | |
75 base::Bind(&MockAdbServer::OnAccepted, base::Unretained(this))); | |
76 if (accept_result != ERR_IO_PENDING) | |
77 OnAccepted(accept_result); | |
78 } | |
79 | |
80 void OnAccepted(int result) { | |
81 ASSERT_EQ(result, 0); | |
82 ReadData(); | |
83 } | |
84 | |
85 void ReadData() { | |
86 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); | |
87 | |
88 int read_result = client_socket_->Read( | |
89 input_buffer_.get(), | |
90 input_buffer_->RemainingCapacity(), | |
91 base::Bind(&MockAdbServer::OnDataRead, base::Unretained(this))); | |
92 | |
93 if (read_result != ERR_IO_PENDING) | |
94 OnDataRead(read_result); | |
95 } | |
96 | |
97 void OnDataRead(int count) { | |
98 if (count <= 0) { | |
99 AcceptConnection(); | |
100 return; | |
101 } | |
102 | |
103 input_buffer_->set_offset(input_buffer_->offset() + count); | |
104 | |
105 if (!TryProcessCommand()) { | |
106 // Posting is needed not to enter deep recursion in case too synchronous | |
107 // replies. | |
108 content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, | |
109 base::Bind(&MockAdbServer::ReadData, base::Unretained(this))); | |
110 } | |
111 } | |
112 | |
113 void WriteData() { | |
114 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); | |
115 ASSERT_NE(output_buffer_->offset(), output_buffer_->capacity()); | |
116 int write_result = client_socket_->Write( | |
117 output_buffer_, | |
118 output_buffer_->capacity() - output_buffer_->offset(), | |
119 base::Bind(&MockAdbServer::OnDataWritten, | |
120 base::Unretained(this))); | |
121 if (write_result != ERR_IO_PENDING) | |
122 OnDataWritten(write_result); | |
123 } | |
124 | |
125 void OnDataWritten(int count) { | |
126 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); | |
127 if (count < 0) { | |
128 AcceptConnection(); | |
129 return; | |
130 } | |
131 | |
132 ASSERT_TRUE(count > 0); | |
133 ASSERT_TRUE(output_buffer_->offset() + count <= output_buffer_->capacity()); | |
134 | |
135 output_buffer_->set_offset(output_buffer_->offset() + count); | |
136 if (output_buffer_->offset() != output_buffer_->capacity()) | |
137 WriteData(); | |
138 else if (!TryProcessCommand()) | |
139 ReadData(); | |
140 } | |
141 | |
142 bool TryProcessCommand() { | |
143 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); | |
144 char* data = input_buffer_->StartOfBuffer(); | |
145 int data_size = input_buffer_->offset(); | |
146 | |
147 if (data_size >= kAdbMessageHeaderSize) { | |
148 std::string message_header(data, kAdbMessageHeaderSize); | |
149 int message_size; | |
150 | |
151 EXPECT_TRUE(base::HexStringToInt(message_header, &message_size)); | |
152 | |
153 if (data_size >= message_size + kAdbMessageHeaderSize) { | |
154 std::string message_body(data + kAdbMessageHeaderSize, message_size ); | |
155 | |
156 memmove(data, data + message_size, | |
157 data_size - message_size - kAdbMessageHeaderSize); | |
158 input_buffer_->set_offset( | |
159 data_size - message_size - kAdbMessageHeaderSize); | |
160 | |
161 ProcessCommand(message_body); | |
162 | |
163 return true; | |
164 } | |
165 } | |
166 | |
167 return false; | |
168 } | |
169 | |
170 void ProcessCommand(const std::string& command) { | |
171 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); | |
172 | |
173 if (command == "host:devices") { | |
174 SendResponse("01498B321301A00A\tdevice\n01498B2B0D01300E\toffline"); | |
175 } else if (command == "host:transport:01498B321301A00A") { | |
176 SendResponse(""); | |
177 } else if (command == kDeviceModelCommand) { | |
178 SendResponse(kDeviceModel); | |
179 } else { | |
180 NOTREACHED() << "Unknown command - " << command; | |
181 } | |
182 } | |
183 | |
184 void SendResponse(const std::string& response) { | |
185 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); | |
186 | |
187 std::stringstream response_stream; | |
188 response_stream << "OKAY"; | |
189 | |
190 int size = response.size(); | |
191 if (size > 0) { | |
192 static const char kHexChars[] = "0123456789ABCDEF"; | |
193 for (int i = 3; i >= 0; i--) | |
194 response_stream << kHexChars[ (size >> 4*i) & 0x0f ]; | |
195 response_stream << response; | |
196 } | |
197 | |
198 std::string response_data = response_stream.str(); | |
199 | |
200 output_buffer_->SetCapacity(response_data.size()); | |
201 output_buffer_->set_offset(0); | |
202 mempcpy(output_buffer_->StartOfBuffer(), response_data.c_str(), | |
203 response_data.size()); | |
204 WriteData(); | |
205 } | |
206 | |
207 private: | |
208 scoped_ptr<TCPServerSocket> server_socket_; | |
209 scoped_ptr<StreamSocket> client_socket_; | |
210 scoped_refptr<net::GrowableIOBuffer> input_buffer_; | |
211 scoped_refptr<net::GrowableIOBuffer> output_buffer_; | |
212 }; | |
213 | |
214 class AdbClientSocketTest : public InProcessBrowserTest { | |
215 public: | |
216 void StartTest() { | |
217 adb_thread_ = RefCountedAdbThread::GetInstance(); | |
218 adb_server_.reset(new MockAdbServer()); | |
219 | |
220 content::BrowserThread::PostTask( | |
Vladislav Kaznacheev
2013/10/28 14:12:37
This look terrifying.
Please consider using Brows
Dmitry Zvorygin
2013/10/29 13:14:01
Done.
| |
221 content::BrowserThread::IO, | |
222 FROM_HERE, | |
223 base::Bind( | |
224 &MockAdbServer::Start, | |
225 base::Unretained(adb_server_.get()), | |
226 base::Bind( | |
227 &content::BrowserThread::PostTask, | |
228 content::BrowserThread::UI, | |
229 FROM_HERE, | |
230 base::Bind( | |
231 &AdbClientSocketTest::QueryDevices, | |
232 base::Unretained(this))))); | |
233 } | |
234 | |
235 void EndTest() { | |
236 adb_provider_ = NULL; | |
237 adb_thread_ = NULL; | |
238 | |
239 content::BrowserThread::PostTask( | |
240 content::BrowserThread::IO, | |
241 FROM_HERE, | |
242 base::Bind( | |
243 &MockAdbServer::Shutdown, | |
244 base::Unretained(adb_server_.get()), | |
245 base::Bind( | |
246 &content::BrowserThread::PostTask, | |
247 content::BrowserThread::UI, | |
248 FROM_HERE, | |
249 base::Bind( | |
250 &content::MessageLoopRunner::Quit, | |
251 runner)))); | |
252 } | |
253 | |
254 private: | |
255 void QueryDevices() { | |
Vladislav Kaznacheev
2013/10/28 18:34:34
Is there a reason not to use DevToolsAdbBridge (li
Dmitry Zvorygin
2013/10/29 13:14:01
Done.
| |
256 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
257 | |
258 adb_provider_ = AndroidDeviceProvider::GetAdbDeviceProvider(); | |
259 adb_provider_->QueryDevices( | |
260 base::Bind(&AdbClientSocketTest::AdbDevicesReceived, | |
261 base::Unretained(this))); | |
262 } | |
263 | |
264 void AdbDevicesReceived( | |
265 const AndroidDeviceProvider::AndroidDevices& received_devices) { | |
266 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
267 #if defined(DEBUG_DEVTOOLS) | |
Vladislav Kaznacheev
2013/10/28 14:12:37
Just to make sure, did you try running this with d
Dmitry Zvorygin
2013/10/29 13:14:01
Sure, see gyp(i) file in changelist.
| |
268 // Mock device is added | |
269 ASSERT_EQ(3U, received_devices.size()); | |
270 #else | |
271 ASSERT_EQ(2U, received_devices.size()); | |
272 #endif | |
273 for (AndroidDeviceProvider::AndroidDevices::const_iterator it = | |
274 received_devices.begin(); it != received_devices.end(); ++it) { | |
275 if ((*it)->serial() == "01498B321301A00A") | |
276 online_device_ = *it; | |
277 else if ((*it)->serial() == "01498B2B0D01300E") | |
278 offline_device_ = *it; | |
279 } | |
280 | |
281 ASSERT_EQ(online_device_->serial(), "01498B321301A00A"); | |
282 ASSERT_TRUE(online_device_->is_connected()); | |
283 ASSERT_FALSE(offline_device_->is_connected()); | |
284 | |
285 adb_thread_->message_loop()->PostTask( | |
286 FROM_HERE, | |
287 base::Bind(&AndroidDevice::RunCommand, | |
288 online_device_, | |
289 kDeviceModelCommand, | |
290 base::Bind(&AdbClientSocketTest::AdbDeviceModelReceived, | |
291 base::Unretained(this)))); | |
292 } | |
293 | |
294 void AdbDeviceModelReceived(int result_code, const std::string& model) { | |
295 DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current()); | |
296 ASSERT_EQ(result_code, 0); | |
297 ASSERT_EQ(model, kDeviceModel); | |
298 EndTest(); | |
299 } | |
300 | |
301 public: | |
302 scoped_refptr<content::MessageLoopRunner> runner; | |
303 | |
304 private: | |
305 scoped_ptr<MockAdbServer> adb_server_; | |
306 | |
307 scoped_refptr<RefCountedAdbThread> adb_thread_; | |
Vladislav Kaznacheev
2013/10/28 14:12:37
I suspect that only adb_thread_ really needs to be
Dmitry Zvorygin
2013/10/29 13:14:01
Done.
| |
308 scoped_refptr<AndroidDeviceProvider> adb_provider_; | |
309 scoped_refptr<AndroidDevice> online_device_; | |
310 scoped_refptr<AndroidDevice> offline_device_; | |
311 }; | |
312 | |
313 IN_PROC_BROWSER_TEST_F(AdbClientSocketTest, TestAdbClientSocket) { | |
314 runner = new content::MessageLoopRunner; | |
315 | |
316 StartTest(); | |
317 | |
318 runner->Run(); | |
319 } | |
OLD | NEW |