Home | History | Annotate | Download | only in base
      1 # Copyright (c) 2012 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 """Base class for running tests on a single device."""
      6 
      7 import logging
      8 import time
      9 
     10 from pylib import ports
     11 from pylib.chrome_test_server_spawner import SpawningServer
     12 from pylib.device import device_utils
     13 from pylib.forwarder import Forwarder
     14 from pylib.valgrind_tools import CreateTool
     15 # TODO(frankf): Move this to pylib/utils
     16 import lighttpd_server
     17 
     18 
     19 # A file on device to store ports of net test server. The format of the file is
     20 # test-spawner-server-port:test-server-port
     21 NET_TEST_SERVER_PORT_INFO_FILE = 'net-test-server-ports'
     22 
     23 
     24 class BaseTestRunner(object):
     25   """Base class for running tests on a single device."""
     26 
     27   def __init__(self, device_serial, tool, push_deps=True,
     28                cleanup_test_files=False):
     29     """
     30       Args:
     31         device: Tests will run on the device of this ID.
     32         tool: Name of the Valgrind tool.
     33         push_deps: If True, push all dependencies to the device.
     34         cleanup_test_files: Whether or not to cleanup test files on device.
     35     """
     36     self.device_serial = device_serial
     37     self.device = device_utils.DeviceUtils(device_serial)
     38     self.tool = CreateTool(tool, self.device)
     39     self._http_server = None
     40     self._forwarder_device_port = 8000
     41     self.forwarder_base_url = ('http://localhost:%d' %
     42         self._forwarder_device_port)
     43     self._spawning_server = None
     44     # We will allocate port for test server spawner when calling method
     45     # LaunchChromeTestServerSpawner and allocate port for test server when
     46     # starting it in TestServerThread.
     47     self.test_server_spawner_port = 0
     48     self.test_server_port = 0
     49     self._push_deps = push_deps
     50     self._cleanup_test_files = cleanup_test_files
     51 
     52   def _PushTestServerPortInfoToDevice(self):
     53     """Pushes the latest port information to device."""
     54     self.device.WriteFile(
     55         self.device.GetExternalStoragePath() + '/' +
     56             NET_TEST_SERVER_PORT_INFO_FILE,
     57         '%d:%d' % (self.test_server_spawner_port, self.test_server_port))
     58 
     59   def RunTest(self, test):
     60     """Runs a test. Needs to be overridden.
     61 
     62     Args:
     63       test: A test to run.
     64 
     65     Returns:
     66       Tuple containing:
     67         (base_test_result.TestRunResults, tests to rerun or None)
     68     """
     69     raise NotImplementedError
     70 
     71   def InstallTestPackage(self):
     72     """Installs the test package once before all tests are run."""
     73     pass
     74 
     75   def PushDataDeps(self):
     76     """Push all data deps to device once before all tests are run."""
     77     pass
     78 
     79   def SetUp(self):
     80     """Run once before all tests are run."""
     81     self.InstallTestPackage()
     82     push_size_before = self.device.old_interface.GetPushSizeInfo()
     83     if self._push_deps:
     84       logging.warning('Pushing data files to device.')
     85       self.PushDataDeps()
     86       push_size_after = self.device.old_interface.GetPushSizeInfo()
     87       logging.warning(
     88           'Total data: %0.3fMB' %
     89           ((push_size_after[0] - push_size_before[0]) / float(2 ** 20)))
     90       logging.warning(
     91           'Total data transferred: %0.3fMB' %
     92           ((push_size_after[1] - push_size_before[1]) / float(2 ** 20)))
     93     else:
     94       logging.warning('Skipping pushing data to device.')
     95 
     96   def TearDown(self):
     97     """Run once after all tests are run."""
     98     self.ShutdownHelperToolsForTestSuite()
     99     if self._cleanup_test_files:
    100       self.device.old_interface.RemovePushedFiles()
    101 
    102   def LaunchTestHttpServer(self, document_root, port=None,
    103                            extra_config_contents=None):
    104     """Launches an HTTP server to serve HTTP tests.
    105 
    106     Args:
    107       document_root: Document root of the HTTP server.
    108       port: port on which we want to the http server bind.
    109       extra_config_contents: Extra config contents for the HTTP server.
    110     """
    111     self._http_server = lighttpd_server.LighttpdServer(
    112         document_root, port=port, extra_config_contents=extra_config_contents)
    113     if self._http_server.StartupHttpServer():
    114       logging.info('http server started: http://localhost:%s',
    115                    self._http_server.port)
    116     else:
    117       logging.critical('Failed to start http server')
    118     self._ForwardPortsForHttpServer()
    119     return (self._forwarder_device_port, self._http_server.port)
    120 
    121   def _ForwardPorts(self, port_pairs):
    122     """Forwards a port."""
    123     Forwarder.Map(port_pairs, self.device, self.tool)
    124 
    125   def _UnmapPorts(self, port_pairs):
    126     """Unmap previously forwarded ports."""
    127     for (device_port, _) in port_pairs:
    128       Forwarder.UnmapDevicePort(device_port, self.device)
    129 
    130   # Deprecated: Use ForwardPorts instead.
    131   def StartForwarder(self, port_pairs):
    132     """Starts TCP traffic forwarding for the given |port_pairs|.
    133 
    134     Args:
    135       host_port_pairs: A list of (device_port, local_port) tuples to forward.
    136     """
    137     self._ForwardPorts(port_pairs)
    138 
    139   def _ForwardPortsForHttpServer(self):
    140     """Starts a forwarder for the HTTP server.
    141 
    142     The forwarder forwards HTTP requests and responses between host and device.
    143     """
    144     self._ForwardPorts([(self._forwarder_device_port, self._http_server.port)])
    145 
    146   def _RestartHttpServerForwarderIfNecessary(self):
    147     """Restarts the forwarder if it's not open."""
    148     # Checks to see if the http server port is being used.  If not forwards the
    149     # request.
    150     # TODO(dtrainor): This is not always reliable because sometimes the port
    151     # will be left open even after the forwarder has been killed.
    152     if not ports.IsDevicePortUsed(self.device, self._forwarder_device_port):
    153       self._ForwardPortsForHttpServer()
    154 
    155   def ShutdownHelperToolsForTestSuite(self):
    156     """Shuts down the server and the forwarder."""
    157     if self._http_server:
    158       self._UnmapPorts([(self._forwarder_device_port, self._http_server.port)])
    159       self._http_server.ShutdownHttpServer()
    160     if self._spawning_server:
    161       self._spawning_server.Stop()
    162 
    163   def CleanupSpawningServerState(self):
    164     """Tells the spawning server to clean up any state.
    165 
    166     If the spawning server is reused for multiple tests, this should be called
    167     after each test to prevent tests affecting each other.
    168     """
    169     if self._spawning_server:
    170       self._spawning_server.CleanupState()
    171 
    172   def LaunchChromeTestServerSpawner(self):
    173     """Launches test server spawner."""
    174     server_ready = False
    175     error_msgs = []
    176     # TODO(pliard): deflake this function. The for loop should be removed as
    177     # well as IsHttpServerConnectable(). spawning_server.Start() should also
    178     # block until the server is ready.
    179     # Try 3 times to launch test spawner server.
    180     for _ in xrange(0, 3):
    181       self.test_server_spawner_port = ports.AllocateTestServerPort()
    182       self._ForwardPorts(
    183           [(self.test_server_spawner_port, self.test_server_spawner_port)])
    184       self._spawning_server = SpawningServer(self.test_server_spawner_port,
    185                                              self.device,
    186                                              self.tool)
    187       self._spawning_server.Start()
    188       server_ready, error_msg = ports.IsHttpServerConnectable(
    189           '127.0.0.1', self.test_server_spawner_port, path='/ping',
    190           expected_read='ready')
    191       if server_ready:
    192         break
    193       else:
    194         error_msgs.append(error_msg)
    195       self._spawning_server.Stop()
    196       # Wait for 2 seconds then restart.
    197       time.sleep(2)
    198     if not server_ready:
    199       logging.error(';'.join(error_msgs))
    200       raise Exception('Can not start the test spawner server.')
    201     self._PushTestServerPortInfoToDevice()
    202