Index: build/android/pylib/base_test_runner.py |
diff --git a/build/android/pylib/base_test_runner.py b/build/android/pylib/base_test_runner.py |
index 33e074103672090dd39e5165d46bd33e4b0619b0..9163e32a858083390db1067b02c92c246b651db1 100644 |
--- a/build/android/pylib/base_test_runner.py |
+++ b/build/android/pylib/base_test_runner.py |
@@ -2,20 +2,26 @@ |
# Use of this source code is governed by a BSD-style license that can be |
# found in the LICENSE file. |
+import contextlib |
+import httplib |
import logging |
import os |
+import tempfile |
+import time |
import android_commands |
from chrome_test_server_spawner import SpawningServer |
+import constants |
from flag_changer import FlagChanger |
+from forwarder import Forwarder |
import lighttpd_server |
-import run_tests_helper |
+import ports |
+from valgrind_tools import CreateTool |
-FORWARDER_PATH = '/data/local/tmp/forwarder' |
-# These ports must match up with the constants in net/test/test_server.cc |
-TEST_SERVER_SPAWNER_PORT = 8001 |
-TEST_SERVER_PORT = 8002 |
-TEST_SYNC_SERVER_PORT = 8003 |
+ |
+# A file on device to store ports of net test server. The format of the file is |
+# test-spawner-server-port:test-server-port |
+NET_TEST_SERVER_PORT_INFO_FILE = '/data/local/tmp/net-test-server-ports' |
class BaseTestRunner(object): |
@@ -25,7 +31,7 @@ class BaseTestRunner(object): |
the Run() method will set up tests, run them and tear them down. |
""" |
- def __init__(self, device, shard_index): |
+ def __init__(self, device, tool, shard_index): |
""" |
Args: |
device: Tests will run on the device of this ID. |
@@ -33,6 +39,7 @@ class BaseTestRunner(object): |
""" |
self.device = device |
self.adb = android_commands.AndroidCommands(device=device) |
+ self.tool = CreateTool(tool, self.adb) |
# Synchronize date/time between host and device. Otherwise same file on |
# host and device may have different timestamp which may cause |
# AndroidCommands.PushIfNeeded failed, or a test which may compare timestamp |
@@ -40,13 +47,25 @@ class BaseTestRunner(object): |
self.adb.SynchronizeDateTime() |
self._http_server = None |
self._forwarder = None |
- self._spawning_server = None |
- self._spawner_forwarder = None |
self._forwarder_device_port = 8000 |
self.forwarder_base_url = ('http://localhost:%d' % |
self._forwarder_device_port) |
self.flags = FlagChanger(self.adb) |
self.shard_index = shard_index |
+ self.flags.AddFlags(['--disable-fre']) |
+ self._spawning_server = None |
+ self._spawner_forwarder = None |
+ # We will allocate port for test server spawner when calling method |
+ # LaunchChromeTestServerSpawner and allocate port for test server when |
+ # starting it in TestServerThread. |
+ self.test_server_spawner_port = 0 |
+ self.test_server_port = 0 |
+ |
+ def _PushTestServerPortInfoToDevice(self): |
+ """Pushes the latest port information to device.""" |
+ self.adb.SetFileContents(NET_TEST_SERVER_PORT_INFO_FILE, |
+ '%d:%d' % (self.test_server_spawner_port, |
+ self.test_server_port)) |
def Run(self): |
"""Calls subclass functions to set up tests, run them and tear them down. |
@@ -54,6 +73,8 @@ class BaseTestRunner(object): |
Returns: |
Test results returned from RunTests(). |
""" |
+ if not self.HasTests(): |
+ return True |
self.SetUp() |
try: |
return self.RunTests() |
@@ -64,6 +85,10 @@ class BaseTestRunner(object): |
"""Called before tests run.""" |
pass |
+ def HasTests(self): |
+ """Whether the test suite has tests to run.""" |
+ return True |
+ |
def RunTests(self): |
"""Runs the tests. Need to be overridden.""" |
raise NotImplementedError |
@@ -83,42 +108,71 @@ class BaseTestRunner(object): |
""" |
for p in test_data_paths: |
self.adb.PushIfNeeded( |
- os.path.join(run_tests_helper.CHROME_DIR, p), |
+ os.path.join(constants.CHROME_DIR, p), |
os.path.join(dest_dir, p)) |
- def LaunchTestHttpServer(self, document_root, extra_config_contents=None): |
+ def LinkSdCardPathsToTempDir(self, paths): |
+ """Link |paths| which are under sdcard to /data/local/tmp. |
+ |
+ For example, the test data '/sdcard/my_data' will be linked to |
+ '/data/local/tmp/my_data'. |
+ |
+ Args: |
+ paths: A list of files and directories relative to /sdcard. |
+ """ |
+ links = set() |
+ for path in paths: |
+ link_name = os.path.dirname(path) |
+ assert link_name, 'Linked paths must be in a subdir of /sdcard/.' |
+ link_name = link_name.split('/')[0] |
+ if link_name not in links: |
+ mapped_device_path = '/data/local/tmp/' + link_name |
+ # Unlink the mapped_device_path at first in case it was mapped to |
+ # a wrong path. Add option '-r' becuase the old path could be a dir. |
+ self.adb.RunShellCommand('rm -r %s' % mapped_device_path) |
+ self.adb.RunShellCommand( |
+ 'ln -s /sdcard/%s %s' % (link_name, mapped_device_path)) |
+ links.add(link_name) |
+ |
+ def LaunchTestHttpServer(self, document_root, port=None, |
+ extra_config_contents=None): |
"""Launches an HTTP server to serve HTTP tests. |
Args: |
document_root: Document root of the HTTP server. |
+ port: port on which we want to the http server bind. |
extra_config_contents: Extra config contents for the HTTP server. |
""" |
self._http_server = lighttpd_server.LighttpdServer( |
- document_root, extra_config_contents=extra_config_contents) |
+ document_root, port=port, extra_config_contents=extra_config_contents) |
if self._http_server.StartupHttpServer(): |
logging.info('http server started: http://localhost:%s', |
self._http_server.port) |
else: |
logging.critical('Failed to start http server') |
- # Root access needed to make the forwarder executable work. |
- self.adb.EnableAdbRoot() |
self.StartForwarderForHttpServer() |
- def StartForwarderForHttpServer(self): |
- """Starts a forwarder for the HTTP server. |
+ def StartForwarder(self, port_pairs): |
+ """Starts TCP traffic forwarding for the given |port_pairs|. |
- The forwarder forwards HTTP requests and responses between host and device. |
+ Args: |
+ host_port_pairs: A list of (device_port, local_port) tuples to forward. |
""" |
# Sometimes the forwarder device port may be already used. We have to kill |
# all forwarder processes to ensure that the forwarder can be started since |
# currently we can not associate the specified port to related pid. |
- # TODO(yfriedman/wangxianzhu): This doesn't work as most of the time the |
- # port is in use but the forwarder is already dead. Killing all forwarders |
- # is overly destructive and breaks other tests which make use of forwarders. |
- # if IsDevicePortUsed(self.adb, self._forwarder_device_port): |
- # self.adb.KillAll('forwarder') |
- self._forwarder = run_tests_helper.ForwardDevicePorts( |
- self.adb, [(self._forwarder_device_port, self._http_server.port)]) |
+ self.adb.KillAll('forwarder') |
+ if self._forwarder: |
+ self._forwarder.Close() |
+ self._forwarder = Forwarder( |
+ self.adb, port_pairs, self.tool, '127.0.0.1') |
+ |
+ def StartForwarderForHttpServer(self): |
+ """Starts a forwarder for the HTTP server. |
+ |
+ The forwarder forwards HTTP requests and responses between host and device. |
+ """ |
+ self.StartForwarder([(self._forwarder_device_port, self._http_server.port)]) |
def RestartHttpServerForwarderIfNecessary(self): |
"""Restarts the forwarder if it's not open.""" |
@@ -126,7 +180,7 @@ class BaseTestRunner(object): |
# request. |
# TODO(dtrainor): This is not always reliable because sometimes the port |
# will be left open even after the forwarder has been killed. |
- if not run_tests_helper.IsDevicePortUsed(self.adb, |
+ if not ports.IsDevicePortUsed(self.adb, |
self._forwarder_device_port): |
self.StartForwarderForHttpServer() |
@@ -140,9 +194,9 @@ class BaseTestRunner(object): |
# (if it exists) |
self.adb.KillAll('forwarder') |
if self._forwarder: |
- self._forwarder.kill() |
+ self._forwarder.Close() |
if self._spawner_forwarder: |
- self._spawner_forwarder.kill() |
+ self._spawner_forwarder.Close() |
if self._http_server: |
self._http_server.ShutdownHttpServer() |
if self._spawning_server: |
@@ -151,12 +205,32 @@ class BaseTestRunner(object): |
def LaunchChromeTestServerSpawner(self): |
"""Launches test server spawner.""" |
- self._spawning_server = SpawningServer(TEST_SERVER_SPAWNER_PORT, |
- TEST_SERVER_PORT) |
- self._spawning_server.Start() |
- # TODO(yfriedman): Ideally we'll only try to start up a port forwarder if |
- # there isn't one already running but for now we just get an error message |
- # and the existing forwarder still works. |
- self._spawner_forwarder = run_tests_helper.ForwardDevicePorts( |
- self.adb, [(TEST_SERVER_SPAWNER_PORT, TEST_SERVER_SPAWNER_PORT), |
- (TEST_SERVER_PORT, TEST_SERVER_PORT)]) |
+ server_ready = False |
+ error_msgs = [] |
+ # Try 3 times to launch test spawner server. |
+ for i in xrange(0, 3): |
+ # Do not allocate port for test server here. We will allocate |
+ # different port for individual test in TestServerThread. |
+ self.test_server_spawner_port = ports.AllocateTestServerPort() |
+ self._spawning_server = SpawningServer(self.test_server_spawner_port, |
+ self.adb, |
+ self.tool) |
+ self._spawning_server.Start() |
+ server_ready, error_msg = ports.IsHttpServerConnectable( |
+ '127.0.0.1', self.test_server_spawner_port, path='/ping', |
+ expected_read='ready') |
+ if server_ready: |
+ break |
+ else: |
+ error_msgs.append(error_msg) |
+ self._spawning_server.Stop() |
+ # Wait for 2 seconds then restart. |
+ time.sleep(2) |
+ if not server_ready: |
+ logging.error(';'.join(error_msgs)) |
+ raise Exception('Can not start the test spawner server.') |
+ self._PushTestServerPortInfoToDevice() |
+ self._spawner_forwarder = Forwarder( |
+ self.adb, |
+ [(self.test_server_spawner_port, self.test_server_spawner_port)], |
+ self.tool, '127.0.0.1') |