-
Notifications
You must be signed in to change notification settings - Fork 644
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Properly acquire local port after calculating it. #208
base: master
Are you sure you want to change the base?
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
#!/usr/bin/env python | ||
# -*- coding: utf-8 -*- | ||
|
||
import unittest | ||
import logging | ||
import mock | ||
import os | ||
import subprocess | ||
import uiautomator | ||
import multiprocessing | ||
|
||
|
||
def _create_next_local_port_stub(ports): | ||
def _next_local_port_stub(_): | ||
max_used_port = max(x[1] for x in ports) if ports else 0 | ||
return max(max_used_port, uiautomator.LOCAL_PORT) + 1 | ||
return _next_local_port_stub | ||
|
||
|
||
def _create_adb_forward_stub(serial, ports): | ||
def _adb_forward_stub(local_port, device_port, rebind=True): | ||
ports.append([serial, local_port, device_port]) | ||
return _adb_forward_stub | ||
|
||
|
||
def _create_adb_forward_list_stub(ports): | ||
def _adb_forward_list_stub(): | ||
return [[x[0], "tcp:" + str(x[1]), "tcp:" + str(x[2])] for x in ports] | ||
return _adb_forward_list_stub | ||
|
||
|
||
class TestMultiProcess(unittest.TestCase): | ||
|
||
def setUp(self): | ||
self.ports = [] | ||
|
||
def create_device(self, serial): | ||
device = uiautomator.Device(serial=serial) | ||
device.server.adb = mock.MagicMock() | ||
device.server.adb.device_serial = lambda: serial | ||
device.server.adb.forward = _create_adb_forward_stub(serial, self.ports) | ||
device.server.adb.forward_list = _create_adb_forward_list_stub(self.ports) | ||
device.server.ping = mock.MagicMock(return_value="pong") | ||
return device | ||
|
||
def test_run_sequential(self): | ||
uiautomator.next_local_port = _create_next_local_port_stub(self.ports) | ||
|
||
device1 = self.create_device("1") | ||
device1.server.start() | ||
|
||
device2 = self.create_device("2") | ||
device2.server.start() | ||
|
||
self.assertNotEqual(device1.server.local_port, device2.server.local_port) | ||
|
||
def test_run_interleaving(self): | ||
uiautomator.next_local_port = _create_next_local_port_stub(self.ports) | ||
|
||
device1 = self.create_device("1") | ||
device2 = self.create_device("2") | ||
|
||
device1.server.start() | ||
device2.server.start() | ||
|
||
self.assertNotEqual(device1.server.local_port, device2.server.local_port) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -312,9 +312,13 @@ def devices(self): | |
raise EnvironmentError("adb is not working.") | ||
return dict([s.split("\t") for s in out[index + len(match):].strip().splitlines() if s.strip()]) | ||
|
||
def forward(self, local_port, device_port): | ||
def forward(self, local_port, device_port, rebind=True): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would be good to add some test coverage here. |
||
'''adb port forward. return 0 if success, else non-zero.''' | ||
return self.cmd("forward", "tcp:%d" % local_port, "tcp:%d" % device_port).wait() | ||
cmd = ["forward"] | ||
if not rebind: | ||
cmd.append("--no-rebind") | ||
cmd += ["tcp:%d" % local_port, "tcp:%d" % device_port] | ||
return self.cmd(*cmd).wait() | ||
|
||
def forward_list(self): | ||
'''adb forward --list''' | ||
|
@@ -380,17 +384,11 @@ def __init__(self, serial=None, local_port=None, device_port=None, adb_server_ho | |
self.adb = Adb(serial=serial, adb_server_host=adb_server_host, adb_server_port=adb_server_port) | ||
self.device_port = int(device_port) if device_port else DEVICE_PORT | ||
if local_port: | ||
self.local_port = local_port | ||
# Assume that the caller acquired the port correctly. | ||
self.__local_port = local_port | ||
else: | ||
try: # first we will try to use the local port already adb forwarded | ||
for s, lp, rp in self.adb.forward_list(): | ||
if s == self.adb.device_serial() and rp == 'tcp:%d' % self.device_port: | ||
self.local_port = int(lp[4:]) | ||
break | ||
else: | ||
self.local_port = next_local_port(adb_server_host) | ||
except: | ||
self.local_port = next_local_port(adb_server_host) | ||
# Port will be assigned later when communication actually starts. | ||
self.__local_port = None | ||
|
||
def push(self): | ||
base_dir = os.path.dirname(__file__) | ||
|
@@ -404,6 +402,31 @@ def install(self): | |
for apk in self.__apk_files: | ||
self.adb.cmd("install", "-r -t", os.path.join(base_dir, apk)).wait() | ||
|
||
def get_forwarded_port(self): | ||
for s, lp, rp in self.adb.forward_list(): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. s, lp, rp could be named better. Also, I know this module doesn't have the best documentation, but I think a docstring would be good. |
||
if s == self.adb.device_serial() and rp == 'tcp:%d' % self.device_port: | ||
return int(lp[4:]) | ||
return None | ||
|
||
@property | ||
def local_port(self): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here with the docstring. |
||
# If the port was already assigned, just return it. | ||
if self.__local_port: | ||
return self.__local_port | ||
|
||
# Otherwise, find and acquire an available port. | ||
while True: | ||
# First, check whether there is an already set up port. | ||
forwarded_port = self.get_forwarded_port() | ||
if forwarded_port: | ||
self.__local_port = forwarded_port | ||
return self.__local_port | ||
|
||
# If port is not set up yet, try to set it up. | ||
port = next_local_port(self.adb.adb_server_host) | ||
# Try to acquire the port, so that other processes don't take it. | ||
self.adb.forward(port, self.device_port, rebind=False) | ||
|
||
@property | ||
def jsonrpc(self): | ||
return self.jsonrpc_wrap(timeout=int(os.environ.get("jsonrpc_timeout", 90))) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thoughts on using a helper to create the AutomatorServer so each test doesn't have to specify local_port?