diff options
author | Luis Hector Chavez | 2018-05-02 11:10:29 -0500 |
---|---|---|
committer | Luis Hector Chavez | 2018-05-16 17:20:48 -0500 |
commit | fbee0a9133ef9d94421f06888e6d147d3fbe2c5e (patch) | |
tree | 4eb21e825d4d2f825001be98484e25501fe865ce | |
parent | 454bc7c0be9757c583b6f9fd8912a1b158773f28 (diff) | |
download | platform-system-core-fbee0a9133ef9d94421f06888e6d147d3fbe2c5e.tar.gz platform-system-core-fbee0a9133ef9d94421f06888e6d147d3fbe2c5e.tar.xz platform-system-core-fbee0a9133ef9d94421f06888e6d147d3fbe2c5e.zip |
adb: Improve test_adb a bit more
This change:
* uses unittest.main(), which allows for a subset of the tests to be
selected.
* drops the requirement to have a device already connected since all the
tests that need a device now spin their own mock device.
* Splits the monolithic test class into more granular classes.
* Makes this file be pylint-compliant.
Bug: None
Test: python system/core/adb/test_adb.py
Test: pylint system/core/adb/test_adb.py
Change-Id: I91c7ced520c3c69f855d639e0dbf7e57bb690e97
-rw-r--r-- | adb/test_adb.py | 161 |
1 files changed, 91 insertions, 70 deletions
diff --git a/adb/test_adb.py b/adb/test_adb.py index ce4d4ecfe..8f31a5366 100644 --- a/adb/test_adb.py +++ b/adb/test_adb.py | |||
@@ -36,10 +36,11 @@ import adb | |||
36 | 36 | ||
37 | 37 | ||
38 | @contextlib.contextmanager | 38 | @contextlib.contextmanager |
39 | def fake_adb_server(protocol=socket.AF_INET, port=0): | 39 | def fake_adbd(protocol=socket.AF_INET, port=0): |
40 | """Creates a fake ADB server that just replies with a CNXN packet.""" | 40 | """Creates a fake ADB daemon that just replies with a CNXN packet.""" |
41 | 41 | ||
42 | serversock = socket.socket(protocol, socket.SOCK_STREAM) | 42 | serversock = socket.socket(protocol, socket.SOCK_STREAM) |
43 | serversock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | ||
43 | if protocol == socket.AF_INET: | 44 | if protocol == socket.AF_INET: |
44 | serversock.bind(('127.0.0.1', port)) | 45 | serversock.bind(('127.0.0.1', port)) |
45 | else: | 46 | else: |
@@ -60,33 +61,33 @@ def fake_adb_server(protocol=socket.AF_INET, port=0): | |||
60 | rlist = [readpipe, serversock] | 61 | rlist = [readpipe, serversock] |
61 | cnxn_sent = {} | 62 | cnxn_sent = {} |
62 | while True: | 63 | while True: |
63 | ready, _, _ = select.select(rlist, [], []) | 64 | read_ready, _, _ = select.select(rlist, [], []) |
64 | for r in ready: | 65 | for ready in read_ready: |
65 | if r == readpipe: | 66 | if ready == readpipe: |
66 | # Closure pipe | 67 | # Closure pipe |
67 | os.close(r) | 68 | os.close(ready) |
68 | serversock.shutdown(socket.SHUT_RDWR) | 69 | serversock.shutdown(socket.SHUT_RDWR) |
69 | serversock.close() | 70 | serversock.close() |
70 | return | 71 | return |
71 | elif r == serversock: | 72 | elif ready == serversock: |
72 | # Server socket | 73 | # Server socket |
73 | conn, _ = r.accept() | 74 | conn, _ = ready.accept() |
74 | rlist.append(conn) | 75 | rlist.append(conn) |
75 | else: | 76 | else: |
76 | # Client socket | 77 | # Client socket |
77 | data = r.recv(1024) | 78 | data = ready.recv(1024) |
78 | if not data or data.startswith('OPEN'): | 79 | if not data or data.startswith('OPEN'): |
79 | if r in cnxn_sent: | 80 | if ready in cnxn_sent: |
80 | del cnxn_sent[r] | 81 | del cnxn_sent[ready] |
81 | r.shutdown(socket.SHUT_RDWR) | 82 | ready.shutdown(socket.SHUT_RDWR) |
82 | r.close() | 83 | ready.close() |
83 | rlist.remove(r) | 84 | rlist.remove(ready) |
84 | continue | 85 | continue |
85 | if r in cnxn_sent: | 86 | if ready in cnxn_sent: |
86 | continue | 87 | continue |
87 | cnxn_sent[r] = True | 88 | cnxn_sent[ready] = True |
88 | r.sendall(_adb_packet('CNXN', 0x01000001, 1024 * 1024, | 89 | ready.sendall(_adb_packet('CNXN', 0x01000001, 1024 * 1024, |
89 | 'device::ro.product.name=fakeadb')) | 90 | 'device::ro.product.name=fakeadb')) |
90 | 91 | ||
91 | port = serversock.getsockname()[1] | 92 | port = serversock.getsockname()[1] |
92 | server_thread = threading.Thread(target=_handle) | 93 | server_thread = threading.Thread(target=_handle) |
@@ -113,13 +114,13 @@ def adb_connect(unittest, serial): | |||
113 | yield | 114 | yield |
114 | finally: | 115 | finally: |
115 | # Perform best-effort disconnection. Discard the output. | 116 | # Perform best-effort disconnection. Discard the output. |
116 | p = subprocess.Popen(['adb', 'disconnect', serial], | 117 | subprocess.Popen(['adb', 'disconnect', serial], |
117 | stdout=subprocess.PIPE, stderr=subprocess.PIPE) | 118 | stdout=subprocess.PIPE, |
118 | p.communicate() | 119 | stderr=subprocess.PIPE).communicate() |
119 | 120 | ||
120 | 121 | ||
121 | class NonApiTest(unittest.TestCase): | 122 | class CommandlineTest(unittest.TestCase): |
122 | """Tests for ADB that aren't a part of the AndroidDevice API.""" | 123 | """Tests for the ADB commandline.""" |
123 | 124 | ||
124 | def test_help(self): | 125 | def test_help(self): |
125 | """Make sure we get _something_ out of help.""" | 126 | """Make sure we get _something_ out of help.""" |
@@ -141,28 +142,37 @@ class NonApiTest(unittest.TestCase): | |||
141 | revision_line, r'^Revision [0-9a-f]{12}-android$') | 142 | revision_line, r'^Revision [0-9a-f]{12}-android$') |
142 | 143 | ||
143 | def test_tcpip_error_messages(self): | 144 | def test_tcpip_error_messages(self): |
144 | p = subprocess.Popen(['adb', 'tcpip'], stdout=subprocess.PIPE, | 145 | """Make sure 'adb tcpip' parsing is sane.""" |
145 | stderr=subprocess.STDOUT) | 146 | proc = subprocess.Popen(['adb', 'tcpip'], stdout=subprocess.PIPE, |
146 | out, _ = p.communicate() | 147 | stderr=subprocess.STDOUT) |
147 | self.assertEqual(1, p.returncode) | 148 | out, _ = proc.communicate() |
149 | self.assertEqual(1, proc.returncode) | ||
148 | self.assertIn('requires an argument', out) | 150 | self.assertIn('requires an argument', out) |
149 | 151 | ||
150 | p = subprocess.Popen(['adb', 'tcpip', 'foo'], stdout=subprocess.PIPE, | 152 | proc = subprocess.Popen(['adb', 'tcpip', 'foo'], stdout=subprocess.PIPE, |
151 | stderr=subprocess.STDOUT) | 153 | stderr=subprocess.STDOUT) |
152 | out, _ = p.communicate() | 154 | out, _ = proc.communicate() |
153 | self.assertEqual(1, p.returncode) | 155 | self.assertEqual(1, proc.returncode) |
154 | self.assertIn('invalid port', out) | 156 | self.assertIn('invalid port', out) |
155 | 157 | ||
156 | # Helper method that reads a pipe until it is closed, then sets the event. | 158 | |
157 | def _read_pipe_and_set_event(self, pipe, event): | 159 | class ServerTest(unittest.TestCase): |
158 | x = pipe.read() | 160 | """Tests for the ADB server.""" |
161 | |||
162 | @staticmethod | ||
163 | def _read_pipe_and_set_event(pipe, event): | ||
164 | """Reads a pipe until it is closed, then sets the event.""" | ||
165 | pipe.read() | ||
159 | event.set() | 166 | event.set() |
160 | 167 | ||
161 | # Test that launch_server() does not let the adb server inherit | ||
162 | # stdin/stdout/stderr handles which can cause callers of adb.exe to hang. | ||
163 | # This test also runs fine on unix even though the impetus is an issue | ||
164 | # unique to Windows. | ||
165 | def test_handle_inheritance(self): | 168 | def test_handle_inheritance(self): |
169 | """Test that launch_server() does not inherit handles. | ||
170 | |||
171 | launch_server() should not let the adb server inherit | ||
172 | stdin/stdout/stderr handles, which can cause callers of adb.exe to hang. | ||
173 | This test also runs fine on unix even though the impetus is an issue | ||
174 | unique to Windows. | ||
175 | """ | ||
166 | # This test takes 5 seconds to run on Windows: if there is no adb server | 176 | # This test takes 5 seconds to run on Windows: if there is no adb server |
167 | # running on the the port used below, adb kill-server tries to make a | 177 | # running on the the port used below, adb kill-server tries to make a |
168 | # TCP connection to a closed port and that takes 1 second on Windows; | 178 | # TCP connection to a closed port and that takes 1 second on Windows; |
@@ -184,29 +194,30 @@ class NonApiTest(unittest.TestCase): | |||
184 | 194 | ||
185 | try: | 195 | try: |
186 | # Run the adb client and have it start the adb server. | 196 | # Run the adb client and have it start the adb server. |
187 | p = subprocess.Popen(['adb', '-P', str(port), 'start-server'], | 197 | proc = subprocess.Popen(['adb', '-P', str(port), 'start-server'], |
188 | stdin=subprocess.PIPE, stdout=subprocess.PIPE, | 198 | stdin=subprocess.PIPE, |
189 | stderr=subprocess.PIPE) | 199 | stdout=subprocess.PIPE, |
200 | stderr=subprocess.PIPE) | ||
190 | 201 | ||
191 | # Start threads that set events when stdout/stderr are closed. | 202 | # Start threads that set events when stdout/stderr are closed. |
192 | stdout_event = threading.Event() | 203 | stdout_event = threading.Event() |
193 | stdout_thread = threading.Thread( | 204 | stdout_thread = threading.Thread( |
194 | target=self._read_pipe_and_set_event, | 205 | target=ServerTest._read_pipe_and_set_event, |
195 | args=(p.stdout, stdout_event)) | 206 | args=(proc.stdout, stdout_event)) |
196 | stdout_thread.daemon = True | 207 | stdout_thread.daemon = True |
197 | stdout_thread.start() | 208 | stdout_thread.start() |
198 | 209 | ||
199 | stderr_event = threading.Event() | 210 | stderr_event = threading.Event() |
200 | stderr_thread = threading.Thread( | 211 | stderr_thread = threading.Thread( |
201 | target=self._read_pipe_and_set_event, | 212 | target=ServerTest._read_pipe_and_set_event, |
202 | args=(p.stderr, stderr_event)) | 213 | args=(proc.stderr, stderr_event)) |
203 | stderr_thread.daemon = True | 214 | stderr_thread.daemon = True |
204 | stderr_thread.start() | 215 | stderr_thread.start() |
205 | 216 | ||
206 | # Wait for the adb client to finish. Once that has occurred, if | 217 | # Wait for the adb client to finish. Once that has occurred, if |
207 | # stdin/stderr/stdout are still open, it must be open in the adb | 218 | # stdin/stderr/stdout are still open, it must be open in the adb |
208 | # server. | 219 | # server. |
209 | p.wait() | 220 | proc.wait() |
210 | 221 | ||
211 | # Try to write to stdin which we expect is closed. If it isn't | 222 | # Try to write to stdin which we expect is closed. If it isn't |
212 | # closed, we should get an IOError. If we don't get an IOError, | 223 | # closed, we should get an IOError. If we don't get an IOError, |
@@ -214,7 +225,7 @@ class NonApiTest(unittest.TestCase): | |||
214 | # probably letting the adb server inherit stdin which would be | 225 | # probably letting the adb server inherit stdin which would be |
215 | # wrong. | 226 | # wrong. |
216 | with self.assertRaises(IOError): | 227 | with self.assertRaises(IOError): |
217 | p.stdin.write('x') | 228 | proc.stdin.write('x') |
218 | 229 | ||
219 | # Wait a few seconds for stdout/stderr to be closed (in the success | 230 | # Wait a few seconds for stdout/stderr to be closed (in the success |
220 | # case, this won't wait at all). If there is a timeout, that means | 231 | # case, this won't wait at all). If there is a timeout, that means |
@@ -228,8 +239,12 @@ class NonApiTest(unittest.TestCase): | |||
228 | subprocess.check_output(['adb', '-P', str(port), 'kill-server'], | 239 | subprocess.check_output(['adb', '-P', str(port), 'kill-server'], |
229 | stderr=subprocess.STDOUT) | 240 | stderr=subprocess.STDOUT) |
230 | 241 | ||
231 | # Use SO_LINGER to cause TCP RST segment to be sent on socket close. | 242 | |
243 | class EmulatorTest(unittest.TestCase): | ||
244 | """Tests for the emulator connection.""" | ||
245 | |||
232 | def _reset_socket_on_close(self, sock): | 246 | def _reset_socket_on_close(self, sock): |
247 | """Use SO_LINGER to cause TCP RST segment to be sent on socket close.""" | ||
233 | # The linger structure is two shorts on Windows, but two ints on Unix. | 248 | # The linger structure is two shorts on Windows, but two ints on Unix. |
234 | linger_format = 'hh' if os.name == 'nt' else 'ii' | 249 | linger_format = 'hh' if os.name == 'nt' else 'ii' |
235 | l_onoff = 1 | 250 | l_onoff = 1 |
@@ -248,7 +263,7 @@ class NonApiTest(unittest.TestCase): | |||
248 | Bug: https://code.google.com/p/android/issues/detail?id=21021 | 263 | Bug: https://code.google.com/p/android/issues/detail?id=21021 |
249 | """ | 264 | """ |
250 | with contextlib.closing( | 265 | with contextlib.closing( |
251 | socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as listener: | 266 | socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as listener: |
252 | # Use SO_REUSEADDR so subsequent runs of the test can grab the port | 267 | # Use SO_REUSEADDR so subsequent runs of the test can grab the port |
253 | # even if it is in TIME_WAIT. | 268 | # even if it is in TIME_WAIT. |
254 | listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | 269 | listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) |
@@ -258,7 +273,7 @@ class NonApiTest(unittest.TestCase): | |||
258 | 273 | ||
259 | # Now that listening has started, start adb emu kill, telling it to | 274 | # Now that listening has started, start adb emu kill, telling it to |
260 | # connect to our mock emulator. | 275 | # connect to our mock emulator. |
261 | p = subprocess.Popen( | 276 | proc = subprocess.Popen( |
262 | ['adb', '-s', 'emulator-' + str(port), 'emu', 'kill'], | 277 | ['adb', '-s', 'emulator-' + str(port), 'emu', 'kill'], |
263 | stderr=subprocess.STDOUT) | 278 | stderr=subprocess.STDOUT) |
264 | 279 | ||
@@ -267,12 +282,16 @@ class NonApiTest(unittest.TestCase): | |||
267 | # If WSAECONNABORTED (10053) is raised by any socket calls, | 282 | # If WSAECONNABORTED (10053) is raised by any socket calls, |
268 | # then adb probably isn't reading the data that we sent it. | 283 | # then adb probably isn't reading the data that we sent it. |
269 | conn.sendall('Android Console: type \'help\' for a list ' + | 284 | conn.sendall('Android Console: type \'help\' for a list ' + |
270 | 'of commands\r\n') | 285 | 'of commands\r\n') |
271 | conn.sendall('OK\r\n') | 286 | conn.sendall('OK\r\n') |
272 | 287 | ||
273 | with contextlib.closing(conn.makefile()) as f: | 288 | with contextlib.closing(conn.makefile()) as connf: |
274 | self.assertEqual('kill\n', f.readline()) | 289 | line = connf.readline() |
275 | self.assertEqual('quit\n', f.readline()) | 290 | if line.startswith('auth'): |
291 | # Ignore the first auth line. | ||
292 | line = connf.readline() | ||
293 | self.assertEqual('kill\n', line) | ||
294 | self.assertEqual('quit\n', connf.readline()) | ||
276 | 295 | ||
277 | conn.sendall('OK: killing emulator, bye bye\r\n') | 296 | conn.sendall('OK: killing emulator, bye bye\r\n') |
278 | 297 | ||
@@ -285,11 +304,15 @@ class NonApiTest(unittest.TestCase): | |||
285 | self._reset_socket_on_close(conn) | 304 | self._reset_socket_on_close(conn) |
286 | 305 | ||
287 | # Wait for adb to finish, so we can check return code. | 306 | # Wait for adb to finish, so we can check return code. |
288 | p.communicate() | 307 | proc.communicate() |
289 | 308 | ||
290 | # If this fails, adb probably isn't ignoring WSAECONNRESET when | 309 | # If this fails, adb probably isn't ignoring WSAECONNRESET when |
291 | # reading the response from the adb emu kill command (on Windows). | 310 | # reading the response from the adb emu kill command (on Windows). |
292 | self.assertEqual(0, p.returncode) | 311 | self.assertEqual(0, proc.returncode) |
312 | |||
313 | |||
314 | class ConnectionTest(unittest.TestCase): | ||
315 | """Tests for adb connect.""" | ||
293 | 316 | ||
294 | def test_connect_ipv4_ipv6(self): | 317 | def test_connect_ipv4_ipv6(self): |
295 | """Ensure that `adb connect localhost:1234` will try both IPv4 and IPv6. | 318 | """Ensure that `adb connect localhost:1234` will try both IPv4 and IPv6. |
@@ -298,7 +321,7 @@ class NonApiTest(unittest.TestCase): | |||
298 | """ | 321 | """ |
299 | for protocol in (socket.AF_INET, socket.AF_INET6): | 322 | for protocol in (socket.AF_INET, socket.AF_INET6): |
300 | try: | 323 | try: |
301 | with fake_adb_server(protocol=protocol) as port: | 324 | with fake_adbd(protocol=protocol) as port: |
302 | serial = 'localhost:{}'.format(port) | 325 | serial = 'localhost:{}'.format(port) |
303 | with adb_connect(self, serial): | 326 | with adb_connect(self, serial): |
304 | pass | 327 | pass |
@@ -309,7 +332,7 @@ class NonApiTest(unittest.TestCase): | |||
309 | def test_already_connected(self): | 332 | def test_already_connected(self): |
310 | """Ensure that an already-connected device stays connected.""" | 333 | """Ensure that an already-connected device stays connected.""" |
311 | 334 | ||
312 | with fake_adb_server() as port: | 335 | with fake_adbd() as port: |
313 | serial = 'localhost:{}'.format(port) | 336 | serial = 'localhost:{}'.format(port) |
314 | with adb_connect(self, serial): | 337 | with adb_connect(self, serial): |
315 | # b/31250450: this always returns 0 but probably shouldn't. | 338 | # b/31250450: this always returns 0 but probably shouldn't. |
@@ -320,7 +343,7 @@ class NonApiTest(unittest.TestCase): | |||
320 | def test_reconnect(self): | 343 | def test_reconnect(self): |
321 | """Ensure that a disconnected device reconnects.""" | 344 | """Ensure that a disconnected device reconnects.""" |
322 | 345 | ||
323 | with fake_adb_server() as port: | 346 | with fake_adbd() as port: |
324 | serial = 'localhost:{}'.format(port) | 347 | serial = 'localhost:{}'.format(port) |
325 | with adb_connect(self, serial): | 348 | with adb_connect(self, serial): |
326 | output = subprocess.check_output(['adb', '-s', serial, | 349 | output = subprocess.check_output(['adb', '-s', serial, |
@@ -328,10 +351,10 @@ class NonApiTest(unittest.TestCase): | |||
328 | self.assertEqual(output.strip(), 'device') | 351 | self.assertEqual(output.strip(), 'device') |
329 | 352 | ||
330 | # This will fail. | 353 | # This will fail. |
331 | p = subprocess.Popen(['adb', '-s', serial, 'shell', 'true'], | 354 | proc = subprocess.Popen(['adb', '-s', serial, 'shell', 'true'], |
332 | stdout=subprocess.PIPE, | 355 | stdout=subprocess.PIPE, |
333 | stderr=subprocess.STDOUT) | 356 | stderr=subprocess.STDOUT) |
334 | output, _ = p.communicate() | 357 | output, _ = proc.communicate() |
335 | self.assertEqual(output.strip(), 'error: closed') | 358 | self.assertEqual(output.strip(), 'error: closed') |
336 | 359 | ||
337 | subprocess.check_call(['adb', '-s', serial, 'wait-for-device']) | 360 | subprocess.check_call(['adb', '-s', serial, 'wait-for-device']) |
@@ -349,18 +372,16 @@ class NonApiTest(unittest.TestCase): | |||
349 | subprocess.check_output(['adb', '-s', serial, 'get-state'], | 372 | subprocess.check_output(['adb', '-s', serial, 'get-state'], |
350 | stderr=subprocess.STDOUT) | 373 | stderr=subprocess.STDOUT) |
351 | self.fail('Device should not be available') | 374 | self.fail('Device should not be available') |
352 | except subprocess.CalledProcessError as e: | 375 | except subprocess.CalledProcessError as err: |
353 | self.assertEqual( | 376 | self.assertEqual( |
354 | e.output.strip(), | 377 | err.output.strip(), |
355 | 'error: device \'{}\' not found'.format(serial)) | 378 | 'error: device \'{}\' not found'.format(serial)) |
356 | 379 | ||
380 | |||
357 | def main(): | 381 | def main(): |
382 | """Main entrypoint.""" | ||
358 | random.seed(0) | 383 | random.seed(0) |
359 | if len(adb.get_devices()) > 0: | 384 | unittest.main(verbosity=3) |
360 | suite = unittest.TestLoader().loadTestsFromName(__name__) | ||
361 | unittest.TextTestRunner(verbosity=3).run(suite) | ||
362 | else: | ||
363 | print('Test suite must be run with attached devices') | ||
364 | 385 | ||
365 | 386 | ||
366 | if __name__ == '__main__': | 387 | if __name__ == '__main__': |