run_tests.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. #!/usr/bin/env python
  2. #
  3. # Copyright (c) 2012 The Chromium Authors. All rights reserved.
  4. # Use of this source code is governed by a BSD-style license that can be
  5. # found in the LICENSE file.
  6. """Runs all the native unit tests.
  7. 1. Copy over test binary to /data/local on device.
  8. 2. Resources: chrome/unit_tests requires resources (chrome.pak and en-US.pak)
  9. to be deployed to the device. We use the device's $EXTERNAL_STORAGE as the
  10. base dir (which maps to Context.getExternalFilesDir()).
  11. 3. Environment:
  12. 3.1. chrome/unit_tests requires (via chrome_paths.cc) a directory named:
  13. $EXTERNAL_STORAGE + /chrome/test/data
  14. 3.2. page_cycler_tests have following requirements,
  15. 3.2.1 the following data on host:
  16. <chrome_src_dir>/tools/page_cycler
  17. <chrome_src_dir>/data/page_cycler
  18. 3.2.2. two data directories to store above test data on device named:
  19. $EXTERNAL_STORAGE + /tools/ (for database perf test)
  20. $EXTERNAL_STORAGE + /data/ (for other perf tests)
  21. 3.2.3. a http server to serve http perf tests.
  22. The http root is host's <chrome_src_dir>/data/page_cycler/, port 8000.
  23. 3.2.4 a tool named forwarder is also required to run on device to
  24. forward the http request/response between host and device.
  25. 3.2.5 Chrome is installed on device.
  26. 4. Run the binary in the device and stream the log to the host.
  27. 4.1. Optionally, filter specific tests.
  28. 4.2. Optionally, rebaseline: run the available tests and update the
  29. suppressions file for failures.
  30. 4.3. If we're running a single test suite and we have multiple devices
  31. connected, we'll shard the tests.
  32. 5. Clean up the device.
  33. Suppressions:
  34. Individual tests in a test binary can be suppressed by listing it in
  35. the gtest_filter directory in a file of the same name as the test binary,
  36. one test per line. Here is an example:
  37. $ cat gtest_filter/base_unittests_disabled
  38. DataPackTest.Load
  39. ReadOnlyFileUtilTest.ContentsEqual
  40. This file is generated by the tests running on devices. If running on emulator,
  41. additonal filter file which lists the tests only failed in emulator will be
  42. loaded. We don't care about the rare testcases which succeeded on emuatlor, but
  43. failed on device.
  44. """
  45. import fnmatch
  46. import logging
  47. import optparse
  48. import os
  49. import signal
  50. import subprocess
  51. import sys
  52. import time
  53. from pylib import android_commands
  54. from pylib.base_test_sharder import BaseTestSharder
  55. from pylib import buildbot_report
  56. from pylib import constants
  57. from pylib import debug_info
  58. import emulator
  59. from pylib import ports
  60. from pylib import run_tests_helper
  61. from pylib import test_options_parser
  62. from pylib.single_test_runner import SingleTestRunner
  63. from pylib.test_result import BaseTestResult, TestResults
  64. _TEST_SUITES = ['base_unittests',
  65. 'content_unittests',
  66. 'gpu_unittests',
  67. 'ipc_tests',
  68. 'media_unittests',
  69. 'net_unittests',
  70. 'sql_unittests',
  71. 'sync_unit_tests',
  72. 'ui_unittests',
  73. 'unit_tests',
  74. ]
  75. def TestSuiteDir(build_type):
  76. """Return the base directory of test suites."""
  77. return os.path.abspath(os.path.join(constants.CHROME_DIR, 'out', build_type))
  78. def FullyQualifiedTestSuites(exe, option_test_suite, build_type):
  79. """Return a fully qualified list
  80. Args:
  81. exe: if True, use the executable-based test runner.
  82. option_test_suite: the test_suite specified as an option.
  83. build_type: 'Release' or 'Debug'.
  84. """
  85. test_suite_dir = TestSuiteDir(build_type)
  86. if option_test_suite:
  87. all_test_suites = [option_test_suite]
  88. else:
  89. all_test_suites = _TEST_SUITES
  90. if exe:
  91. qualified_test_suites = [os.path.join(test_suite_dir, t)
  92. for t in all_test_suites]
  93. else:
  94. # out/(Debug|Release)/$SUITE_apk/$SUITE-debug.apk
  95. qualified_test_suites = [os.path.join(test_suite_dir,
  96. t + '_apk',
  97. t + '-debug.apk')
  98. for t in all_test_suites]
  99. for t, q in zip(all_test_suites, qualified_test_suites):
  100. if not os.path.exists(q):
  101. logging.critical('Test suite %s not found in %s.\n'
  102. 'Supported test suites:\n %s\n'
  103. 'Ensure it has been built.\n',
  104. t, q, _TEST_SUITES)
  105. return []
  106. return qualified_test_suites
  107. class TimeProfile(object):
  108. """Class for simple profiling of action, with logging of cost."""
  109. def __init__(self, description):
  110. self._description = description
  111. self.Start()
  112. def Start(self):
  113. self._starttime = time.time()
  114. def Stop(self):
  115. """Stop profiling and dump a log."""
  116. if self._starttime:
  117. stoptime = time.time()
  118. logging.info('%fsec to perform %s',
  119. stoptime - self._starttime, self._description)
  120. self._starttime = None
  121. class Xvfb(object):
  122. """Class to start and stop Xvfb if relevant. Nop if not Linux."""
  123. def __init__(self):
  124. self._pid = 0
  125. def _IsLinux(self):
  126. """Return True if on Linux; else False."""
  127. return sys.platform.startswith('linux')
  128. def Start(self):
  129. """Start Xvfb and set an appropriate DISPLAY environment. Linux only.
  130. Copied from tools/code_coverage/coverage_posix.py
  131. """
  132. if not self._IsLinux():
  133. return
  134. proc = subprocess.Popen(['Xvfb', ':9', '-screen', '0', '1024x768x24',
  135. '-ac'],
  136. stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
  137. self._pid = proc.pid
  138. if not self._pid:
  139. raise Exception('Could not start Xvfb')
  140. os.environ['DISPLAY'] = ':9'
  141. # Now confirm, giving a chance for it to start if needed.
  142. for _ in range(10):
  143. proc = subprocess.Popen('xdpyinfo >/dev/null', shell=True)
  144. _, retcode = os.waitpid(proc.pid, 0)
  145. if retcode == 0:
  146. break
  147. time.sleep(0.25)
  148. if retcode != 0:
  149. raise Exception('Could not confirm Xvfb happiness')
  150. def Stop(self):
  151. """Stop Xvfb if needed. Linux only."""
  152. if self._pid:
  153. try:
  154. os.kill(self._pid, signal.SIGKILL)
  155. except:
  156. pass
  157. del os.environ['DISPLAY']
  158. self._pid = 0
  159. class TestSharder(BaseTestSharder):
  160. """Responsible for sharding the tests on the connected devices."""
  161. def __init__(self, attached_devices, test_suite, gtest_filter,
  162. test_arguments, timeout, rebaseline, performance_test,
  163. cleanup_test_files, tool, log_dump_name, fast_and_loose,
  164. build_type):
  165. BaseTestSharder.__init__(self, attached_devices)
  166. self.test_suite = test_suite
  167. self.test_suite_basename = os.path.basename(test_suite)
  168. self.gtest_filter = gtest_filter or ''
  169. self.test_arguments = test_arguments
  170. self.timeout = timeout
  171. self.rebaseline = rebaseline
  172. self.performance_test = performance_test
  173. self.cleanup_test_files = cleanup_test_files
  174. self.tool = tool
  175. self.log_dump_name = log_dump_name
  176. self.fast_and_loose = fast_and_loose
  177. self.build_type = build_type
  178. test = SingleTestRunner(self.attached_devices[0], test_suite, gtest_filter,
  179. test_arguments, timeout, rebaseline,
  180. performance_test, cleanup_test_files, tool, 0,
  181. not not self.log_dump_name, fast_and_loose,
  182. build_type)
  183. self.tests = []
  184. if not self.gtest_filter:
  185. # No filter has been specified, let's add all tests then.
  186. # The executable/apk needs to be copied before we can call GetAllTests.
  187. test.test_package.StripAndCopyExecutable()
  188. all_tests = test.test_package.GetAllTests()
  189. if not rebaseline:
  190. disabled_list = test.GetDisabledTests()
  191. # Only includes tests that do not have any match in the disabled list.
  192. all_tests = filter(lambda t:
  193. not any([fnmatch.fnmatch(t, disabled_pattern)
  194. for disabled_pattern in disabled_list]),
  195. all_tests)
  196. self.tests = all_tests
  197. def CreateShardedTestRunner(self, device, index):
  198. """Creates a suite-specific test runner.
  199. Args:
  200. device: Device serial where this shard will run.
  201. index: Index of this device in the pool.
  202. Returns:
  203. A SingleTestRunner object.
  204. """
  205. device_num = len(self.attached_devices)
  206. shard_size = (len(self.tests) + device_num - 1) / device_num
  207. shard_test_list = self.tests[index * shard_size : (index + 1) * shard_size]
  208. test_filter = ':'.join(shard_test_list) + self.gtest_filter
  209. return SingleTestRunner(device, self.test_suite,
  210. test_filter, self.test_arguments, self.timeout,
  211. self.rebaseline, self.performance_test,
  212. self.cleanup_test_files, self.tool, index,
  213. not not self.log_dump_name, self.fast_and_loose,
  214. self.build_type)
  215. def OnTestsCompleted(self, test_runners, test_results):
  216. """Notifies that we completed the tests."""
  217. test_results.LogFull('Unit test', os.path.basename(self.test_suite),
  218. self.build_type)
  219. test_results.PrintAnnotation()
  220. if test_results.failed and self.rebaseline:
  221. test_runners[0].UpdateFilter(test_results.failed)
  222. if self.log_dump_name:
  223. # Zip all debug info outputs into a file named by log_dump_name.
  224. debug_info.GTestDebugInfo.ZipAndCleanResults(
  225. os.path.join(TestSuiteDir(self.build_type), 'debug_info_dumps'),
  226. self.log_dump_name)
  227. def _RunATestSuite(options):
  228. """Run a single test suite.
  229. Helper for Dispatch() to allow stop/restart of the emulator across
  230. test bundles. If using the emulator, we start it on entry and stop
  231. it on exit.
  232. Args:
  233. options: options for running the tests.
  234. Returns:
  235. 0 if successful, number of failing tests otherwise.
  236. """
  237. step_name = os.path.basename(options.test_suite).replace('-debug.apk', '')
  238. buildbot_report.PrintNamedStep(step_name)
  239. attached_devices = []
  240. buildbot_emulators = []
  241. if options.use_emulator:
  242. for n in range(options.emulator_count):
  243. t = TimeProfile('Emulator launch %d' % n)
  244. avd_name = None
  245. if n > 0:
  246. # Creates a temporary AVD for the extra emulators.
  247. avd_name = 'run_tests_avd_%d' % n
  248. buildbot_emulator = emulator.Emulator(avd_name, options.fast_and_loose)
  249. buildbot_emulator.Launch(kill_all_emulators=n == 0)
  250. t.Stop()
  251. buildbot_emulators.append(buildbot_emulator)
  252. attached_devices.append(buildbot_emulator.device)
  253. # Wait for all emulators to boot completed.
  254. map(lambda buildbot_emulator: buildbot_emulator.ConfirmLaunch(True),
  255. buildbot_emulators)
  256. elif options.test_device:
  257. attached_devices = [options.test_device]
  258. else:
  259. attached_devices = android_commands.GetAttachedDevices()
  260. if not attached_devices:
  261. logging.critical('A device must be attached and online.')
  262. buildbot_report.PrintError()
  263. return 1
  264. # Reset the test port allocation. It's important to do it before starting
  265. # to dispatch any tests.
  266. if not ports.ResetTestServerPortAllocation():
  267. raise Exception('Failed to reset test server port.')
  268. if options.performance_test or options.gtest_filter:
  269. # These configuration can't be split in multiple devices.
  270. attached_devices = [attached_devices[0]]
  271. sharder = TestSharder(attached_devices, options.test_suite,
  272. options.gtest_filter, options.test_arguments,
  273. options.timeout, options.rebaseline,
  274. options.performance_test,
  275. options.cleanup_test_files, options.tool,
  276. options.log_dump, options.fast_and_loose,
  277. options.build_type)
  278. test_results = sharder.RunShardedTests()
  279. for buildbot_emulator in buildbot_emulators:
  280. buildbot_emulator.Shutdown()
  281. # Another chance if we timed out? At this point It is safe(r) to
  282. # run fast and loose since we just uploaded all the test data and
  283. # binary.
  284. if test_results.timed_out and options.repeat:
  285. logging.critical('Timed out; repeating in fast_and_loose mode.')
  286. options.fast_and_loose = True
  287. options.repeat -= 1
  288. logging.critical('Repeats left: ' + str(options.repeat))
  289. return _RunATestSuite(options)
  290. return len(test_results.failed)
  291. def Dispatch(options):
  292. """Dispatches the tests, sharding if possible.
  293. If options.use_emulator is True, all tests will be run in new emulator
  294. instance.
  295. Args:
  296. options: options for running the tests.
  297. Returns:
  298. 0 if successful, number of failing tests otherwise.
  299. """
  300. if options.test_suite == 'help':
  301. ListTestSuites()
  302. return 0
  303. if options.use_xvfb:
  304. xvfb = Xvfb()
  305. xvfb.Start()
  306. all_test_suites = FullyQualifiedTestSuites(options.exe, options.test_suite,
  307. options.build_type)
  308. failures = 0
  309. for suite in all_test_suites:
  310. options.test_suite = suite
  311. failures += _RunATestSuite(options)
  312. if options.use_xvfb:
  313. xvfb.Stop()
  314. return failures
  315. def ListTestSuites():
  316. """Display a list of available test suites."""
  317. print 'Available test suites are:'
  318. for test_suite in _TEST_SUITES:
  319. print test_suite
  320. def main(argv):
  321. option_parser = optparse.OptionParser()
  322. test_options_parser.AddTestRunnerOptions(option_parser, default_timeout=0)
  323. option_parser.add_option('-s', '--suite', dest='test_suite',
  324. help='Executable name of the test suite to run '
  325. '(use -s help to list them)')
  326. option_parser.add_option('-d', '--device', dest='test_device',
  327. help='Target device the test suite to run ')
  328. option_parser.add_option('-r', dest='rebaseline',
  329. help='Rebaseline and update *testsuite_disabled',
  330. action='store_true')
  331. option_parser.add_option('-f', '--gtest_filter', dest='gtest_filter',
  332. help='gtest filter')
  333. option_parser.add_option('-a', '--test_arguments', dest='test_arguments',
  334. help='Additional arguments to pass to the test')
  335. option_parser.add_option('-p', dest='performance_test',
  336. help='Indicator of performance test',
  337. action='store_true')
  338. option_parser.add_option('-L', dest='log_dump',
  339. help='file name of log dump, which will be put in '
  340. 'subfolder debug_info_dumps under the same '
  341. 'directory in where the test_suite exists.')
  342. option_parser.add_option('-e', '--emulator', dest='use_emulator',
  343. action='store_true',
  344. help='Run tests in a new instance of emulator')
  345. option_parser.add_option('-n', '--emulator_count',
  346. type='int', default=1,
  347. help='Number of emulators to launch for running the '
  348. 'tests.')
  349. option_parser.add_option('-x', '--xvfb', dest='use_xvfb',
  350. action='store_true',
  351. help='Use Xvfb around tests (ignored if not Linux)')
  352. option_parser.add_option('--fast', '--fast_and_loose', dest='fast_and_loose',
  353. action='store_true',
  354. help='Go faster (but be less stable), '
  355. 'for quick testing. Example: when tracking down '
  356. 'tests that hang to add to the disabled list, '
  357. 'there is no need to redeploy the test binary '
  358. 'or data to the device again. '
  359. 'Don\'t use on bots by default!')
  360. option_parser.add_option('--repeat', dest='repeat', type='int',
  361. default=2,
  362. help='Repeat count on test timeout')
  363. option_parser.add_option('--exit_code', action='store_true',
  364. help='If set, the exit code will be total number '
  365. 'of failures.')
  366. option_parser.add_option('--exe', action='store_true',
  367. help='If set, use the exe test runner instead of '
  368. 'the APK.')
  369. options, args = option_parser.parse_args(argv)
  370. if len(args) > 1:
  371. print 'Unknown argument:', args[1:]
  372. option_parser.print_usage()
  373. sys.exit(1)
  374. run_tests_helper.SetLogLevel(options.verbose_count)
  375. emulator.DeleteAllTempAVDs()
  376. failed_tests_count = Dispatch(options)
  377. # Failures of individual test suites are communicated by printing a
  378. # STEP_FAILURE message.
  379. # Returning a success exit status also prevents the buildbot from incorrectly
  380. # marking the last suite as failed if there were failures in other suites in
  381. # the batch (this happens because the exit status is a sum of all failures
  382. # from all suites, but the buildbot associates the exit status only with the
  383. # most recent step).
  384. if options.exit_code:
  385. return failed_tests_count
  386. return 0
  387. if __name__ == '__main__':
  388. sys.exit(main(sys.argv))