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