1 #!/usr/bin/python2.4 2 # 3 # 4 # Copyright 2009, The Android Open Source Project 5 # 6 # Licensed under the Apache License, Version 2.0 (the "License"); 7 # you may not use this file except in compliance with the License. 8 # You may obtain a copy of the License at 9 # 10 # http://www.apache.org/licenses/LICENSE-2.0 11 # 12 # Unless required by applicable law or agreed to in writing, software 13 # distributed under the License is distributed on an "AS IS" BASIS, 14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 # See the License for the specific language governing permissions and 16 # limitations under the License. 17 18 """TestSuite for running native Android tests.""" 19 20 # python imports 21 import os 22 import re 23 24 # local imports 25 import android_build 26 import logger 27 import run_command 28 import test_suite 29 30 31 class NativeTestSuite(test_suite.AbstractTestSuite): 32 """A test suite for running native aka C/C++ tests on device.""" 33 34 def Run(self, options, adb): 35 """Run the provided *native* test suite. 36 37 The test_suite must contain a build path where the native test 38 files are. Subdirectories are automatically scanned as well. 39 40 Each test's name must have a .cc or .cpp extension and match one 41 of the following patterns: 42 - test_* 43 - *_test.[cc|cpp] 44 - *_unittest.[cc|cpp] 45 A successful test must return 0. Any other value will be considered 46 as an error. 47 48 Args: 49 options: command line options 50 adb: adb interface 51 """ 52 # find all test files, convert unicode names to ascii, take the basename 53 # and drop the .cc/.cpp extension. 54 source_list = [] 55 build_path = os.path.join(android_build.GetTop(), self.GetBuildPath()) 56 os.path.walk(build_path, self._CollectTestSources, source_list) 57 logger.SilentLog("Tests source %s" % source_list) 58 59 # Host tests are under out/host/<os>-<arch>/bin. 60 host_list = self._FilterOutMissing(android_build.GetHostBin(), source_list) 61 logger.SilentLog("Host tests %s" % host_list) 62 63 # Target tests are under $ANDROID_PRODUCT_OUT/system/bin. 64 target_list = self._FilterOutMissing(android_build.GetTargetSystemBin(), 65 source_list) 66 logger.SilentLog("Target tests %s" % target_list) 67 68 # Run on the host 69 logger.Log("\nRunning on host") 70 for f in host_list: 71 if run_command.RunHostCommand(f) != 0: 72 logger.Log("%s... failed" % f) 73 else: 74 if run_command.HasValgrind(): 75 if run_command.RunHostCommand(f, valgrind=True) == 0: 76 logger.Log("%s... ok\t\t[valgrind: ok]" % f) 77 else: 78 logger.Log("%s... ok\t\t[valgrind: failed]" % f) 79 else: 80 logger.Log("%s... ok\t\t[valgrind: missing]" % f) 81 82 # Run on the device 83 logger.Log("\nRunning on target") 84 for f in target_list: 85 full_path = os.path.join(os.sep, "system", "bin", f) 86 87 # Single quotes are needed to prevent the shell splitting it. 88 output = adb.SendShellCommand("'%s 2>&1;echo -n exit code:$?'" % 89 "(cd /sdcard;%s)" % full_path, 90 int(options.timeout)) 91 success = output.endswith("exit code:0") 92 logger.Log("%s... %s" % (f, success and "ok" or "failed")) 93 # Print the captured output when the test failed. 94 if not success or options.verbose: 95 pos = output.rfind("exit code") 96 output = output[0:pos] 97 logger.Log(output) 98 99 # Cleanup 100 adb.SendShellCommand("rm %s" % full_path) 101 102 def _CollectTestSources(self, test_list, dirname, files): 103 """For each directory, find tests source file and add them to the list. 104 105 Test files must match one of the following pattern: 106 - test_*.[cc|cpp] 107 - *_test.[cc|cpp] 108 - *_unittest.[cc|cpp] 109 110 This method is a callback for os.path.walk. 111 112 Args: 113 test_list: Where new tests should be inserted. 114 dirname: Current directory. 115 files: List of files in the current directory. 116 """ 117 for f in files: 118 (name, ext) = os.path.splitext(f) 119 if ext == ".cc" or ext == ".cpp" or ext == ".c": 120 if re.search("_test$|_test_$|_unittest$|_unittest_$|^test_", name): 121 logger.SilentLog("Found %s" % f) 122 test_list.append(str(os.path.join(dirname, f))) 123 124 def _FilterOutMissing(self, path, sources): 125 """Filter out from the sources list missing tests. 126 127 Sometimes some test source are not built for the target, i.e there 128 is no binary corresponding to the source file. We need to filter 129 these out. 130 131 Args: 132 path: Where the binaries should be. 133 sources: List of tests source path. 134 Returns: 135 A list of test binaries built from the sources. 136 """ 137 binaries = [] 138 for f in sources: 139 binary = os.path.basename(f) 140 binary = os.path.splitext(binary)[0] 141 full_path = os.path.join(path, binary) 142 if os.path.exists(full_path): 143 binaries.append(binary) 144 return binaries 145 146 def _RunHostCommand(self, binary, valgrind=False): 147 """Run a command on the host (opt using valgrind). 148 149 Runs the host binary and returns the exit code. 150 If successfull, the output (stdout and stderr) are discarded, 151 but printed in case of error. 152 The command can be run under valgrind in which case all the 153 output are always discarded. 154 155 Args: 156 binary: basename of the file to be run. It is expected to be under 157 out/host/<os>-<arch>/bin. 158 valgrind: If True the command will be run under valgrind. 159 160 Returns: 161 The command exit code (int) 162 """ 163 full_path = os.path.join(android_build.GetHostBin(), binary) 164 return run_command.RunHostCommand(full_path, valgrind=valgrind) 165