summaryrefslogtreecommitdiffstats
path: root/build/android/test_package_executable.py
blob: 8df609afd140fdec50eeaad87085873e482fd591 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.


import logging
import os
import pexpect
import shutil
import sys
import tempfile

import cmd_helper
from test_package import TestPackage


class TestPackageExecutable(TestPackage):
  """A helper class for running stand-alone executables."""

  _TEST_RUNNER_RET_VAL_FILE = '/data/local/tmp/gtest_retval'

  def __init__(self, adb, device, test_suite, timeout, rebaseline,
               performance_test, cleanup_test_files, tool, dump_debug_info,
               symbols_dir=None):
    """
    Args:
      adb: ADB interface the tests are using.
      device: Device to run the tests.
      test_suite: A specific test suite to run, empty to run all.
      timeout: Timeout for each test.
      rebaseline: Whether or not to run tests in isolation and update the
          filter.
      performance_test: Whether or not performance test(s).
      cleanup_test_files: Whether or not to cleanup test files on device.
      tool: Name of the Valgrind tool.
      dump_debug_info: A debug_info object.
      symbols_dir: Directory to put the stripped binaries.
    """
    TestPackage.__init__(self, adb, device, test_suite, timeout,
                         rebaseline, performance_test, cleanup_test_files,
                         tool, dump_debug_info)
    self.symbols_dir = symbols_dir

  def _GetGTestReturnCode(self):
    ret = None
    ret_code = 1  # Assume failure if we can't find it
    ret_code_file = tempfile.NamedTemporaryFile()
    try:
      if not self.adb.Adb().Pull(
          TestPackageExecutable._TEST_RUNNER_RET_VAL_FILE, ret_code_file.name):
        logging.critical('Unable to pull gtest ret val file %s',
                         ret_code_file.name)
        raise ValueError
      ret_code = file(ret_code_file.name).read()
      ret = int(ret_code)
    except ValueError:
      logging.critical('Error reading gtest ret val file %s [%s]',
                       ret_code_file.name, ret_code)
      ret = 1
    return ret

  def _AddNativeCoverageExports(self):
    # export GCOV_PREFIX set the path for native coverage results
    # export GCOV_PREFIX_STRIP indicates how many initial directory
    #                          names to strip off the hardwired absolute paths.
    #                          This value is calculated in buildbot.sh and
    #                          depends on where the tree is built.
    # Ex: /usr/local/google/code/chrome will become
    #     /code/chrome if GCOV_PREFIX_STRIP=3
    try:
      depth = os.environ['NATIVE_COVERAGE_DEPTH_STRIP']
    except KeyError:
      logging.info('NATIVE_COVERAGE_DEPTH_STRIP is not defined: '
                   'No native coverage.')
      return ''
    export_string = 'export GCOV_PREFIX="/data/local/gcov"\n'
    export_string += 'export GCOV_PREFIX_STRIP=%s\n' % depth
    return export_string

  def GetAllTests(self):
    """Returns a list of all tests available in the test suite."""
    all_tests = self.adb.RunShellCommand(
        '/data/local/%s --gtest_list_tests' % self.test_suite_basename)
    return self._ParseGTestListTests(all_tests)

  def CreateTestRunnerScript(self, gtest_filter, test_arguments):
    """Creates a test runner script and pushes to the device.

    Args:
      gtest_filter: A gtest_filter flag.
      test_arguments: Additional arguments to pass to the test binary.
    """
    tool_wrapper = self.tool.GetTestWrapper()
    sh_script_file = tempfile.NamedTemporaryFile()
    # We need to capture the exit status from the script since adb shell won't
    # propagate to us.
    sh_script_file.write('cd /data/local\n'
                         '%s'
                         '%s /data/local/%s --gtest_filter=%s %s\n'
                         'echo $? > %s' %
                         (self._AddNativeCoverageExports(),
                          tool_wrapper, self.test_suite_basename,
                          gtest_filter, test_arguments,
                          TestPackageExecutable._TEST_RUNNER_RET_VAL_FILE))
    sh_script_file.flush()
    cmd_helper.RunCmd(['chmod', '+x', sh_script_file.name])
    self.adb.PushIfNeeded(sh_script_file.name,
                          '/data/local/chrome_test_runner.sh')
    logging.info('Conents of the test runner script: ')
    for line in open(sh_script_file.name).readlines():
      logging.info('  ' + line.rstrip())

  def RunTestsAndListResults(self):
    """Runs all the tests and checks for failures.

    Returns:
      A TestResults object.
    """
    args = ['adb', '-s', self.device, 'shell', 'sh',
            '/data/local/chrome_test_runner.sh']
    logging.info(args)
    p = pexpect.spawn(args[0], args[1:], logfile=sys.stdout)
    return self._WatchTestOutput(p)

  def StripAndCopyExecutable(self):
    """Strips and copies the executable to the device."""
    if self.tool.NeedsDebugInfo():
      target_name = self.test_suite
    else:
      target_name = self.test_suite + '_' + self.device + '_stripped'
      should_strip = True
      if os.path.isfile(target_name):
        logging.info('Found target file %s' % target_name)
        target_mtime = os.stat(target_name).st_mtime
        source_mtime = os.stat(self.test_suite).st_mtime
        if target_mtime > source_mtime:
          logging.info('Target mtime (%d) is newer than source (%d), assuming '
                       'no change.' % (target_mtime, source_mtime))
          should_strip = False

      if should_strip:
        logging.info('Did not find up-to-date stripped binary. Generating a '
                     'new one (%s).' % target_name)
        # Whenever we generate a stripped binary, copy to the symbols dir. If we
        # aren't stripping a new binary, assume it's there.
        if self.symbols_dir:
          if not os.path.exists(self.symbols_dir):
            os.makedirs(self.symbols_dir)
          shutil.copy(self.test_suite, self.symbols_dir)
        strip = os.environ['STRIP']
        cmd_helper.RunCmd([strip, self.test_suite, '-o', target_name])
    test_binary = '/data/local/' + self.test_suite_basename
    self.adb.PushIfNeeded(target_name, test_binary)

    if self.test_suite_basename == 'ui_unittests':
      self.adb.PushIfNeeded(self.test_suite_dirname + '/chrome.pak',
                            '/data/local/tmp/paks/chrome.pak')
      self.adb.PushIfNeeded(self.test_suite_dirname + '/locales/en-US.pak',
                            '/data/local/tmp/paks/en-US.pak')

  def _GetTestSuiteBaseName(self):
    """Returns the  base name of the test suite."""
    return os.path.basename(self.test_suite)