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.
5 """Base class for running tests on a single device."""
14 from pylib import android_commands
15 from pylib import constants
16 from pylib import ports
17 from pylib.chrome_test_server_spawner import SpawningServer
18 from pylib.forwarder import Forwarder
19 from pylib.valgrind_tools import CreateTool
20 # TODO(frankf): Move this to pylib/utils
21 import lighttpd_server
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
26 NET_TEST_SERVER_PORT_INFO_FILE = 'net-test-server-ports'
29 class BaseTestRunner(object):
30 """Base class for running tests on a single device."""
32 def __init__(self, device, tool, push_deps=True, cleanup_test_files=False):
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.
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
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))
63 def RunTest(self, test):
64 """Runs a test. Needs to be overridden.
71 (base_test_result.TestRunResults, tests to rerun or None)
73 raise NotImplementedError
75 def InstallTestPackage(self):
76 """Installs the test package once before all tests are run."""
79 def PushDataDeps(self):
80 """Push all data deps to device once before all tests are run."""
84 """Run once before all tests are run."""
85 self.InstallTestPackage()
86 push_size_before = self.adb.GetPushSizeInfo()
88 logging.warning('Pushing data files to device.')
90 push_size_after = self.adb.GetPushSizeInfo()
92 'Total data: %0.3fMB' %
93 ((push_size_after[0] - push_size_before[0]) / float(2 ** 20)))
95 'Total data transferred: %0.3fMB' %
96 ((push_size_after[1] - push_size_before[1]) / float(2 ** 20)))
98 logging.warning('Skipping pushing data to device.')
101 """Run once after all tests are run."""
102 self.ShutdownHelperToolsForTestSuite()
103 if self._cleanup_test_files:
104 self.adb.RemovePushedFiles()
106 def LaunchTestHttpServer(self, document_root, port=None,
107 extra_config_contents=None):
108 """Launches an HTTP server to serve HTTP tests.
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.
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)
121 logging.critical('Failed to start http server')
122 self._ForwardPortsForHttpServer()
123 return (self._forwarder_device_port, self._http_server.port)
125 def _ForwardPorts(self, port_pairs):
126 """Forwards a port."""
127 Forwarder.Map(port_pairs, self.adb, self.tool)
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)
134 # Deprecated: Use ForwardPorts instead.
135 def StartForwarder(self, port_pairs):
136 """Starts TCP traffic forwarding for the given |port_pairs|.
139 host_port_pairs: A list of (device_port, local_port) tuples to forward.
141 self._ForwardPorts(port_pairs)
143 def _ForwardPortsForHttpServer(self):
144 """Starts a forwarder for the HTTP server.
146 The forwarder forwards HTTP requests and responses between host and device.
148 self._ForwardPorts([(self._forwarder_device_port, self._http_server.port)])
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
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()
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()
167 def CleanupSpawningServerState(self):
168 """Tells the spawning server to clean up any state.
170 If the spawning server is reused for multiple tests, this should be called
171 after each test to prevent tests affecting each other.
173 if self._spawning_server:
174 self._spawning_server.CleanupState()
176 def LaunchChromeTestServerSpawner(self):
177 """Launches test server spawner."""
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()
187 [(self.test_server_spawner_port, self.test_server_spawner_port)])
188 self._spawning_server = SpawningServer(self.test_server_spawner_port,
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')
198 error_msgs.append(error_msg)
199 self._spawning_server.Stop()
200 # Wait for 2 seconds then restart.
203 logging.error(';'.join(error_msgs))
204 raise Exception('Can not start the test spawner server.')
205 self._PushTestServerPortInfoToDevice()