1 # Copyright 2013 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 """Setup for instrumentation host-driven tests.""" 6 7 import logging 8 import os 9 import sys 10 import types 11 12 from pylib.host_driven import test_case 13 from pylib.host_driven import test_info_collection 14 from pylib.host_driven import test_runner 15 16 17 def _GetPythonFiles(root, files): 18 """Returns all files from |files| that end in 'Test.py'. 19 20 Args: 21 root: A directory name with python files. 22 files: A list of file names. 23 24 Returns: 25 A list with all python files that match the testing naming scheme. 26 """ 27 return [os.path.join(root, f) for f in files if f.endswith('Test.py')] 28 29 30 def _InferImportNameFromFile(python_file): 31 """Given a file, infer the import name for that file. 32 33 Example: /usr/foo/bar/baz.py -> baz. 34 35 Args: 36 python_file: Path to the Python file, ostensibly to import later. 37 38 Returns: 39 The module name for the given file. 40 """ 41 return os.path.splitext(os.path.basename(python_file))[0] 42 43 44 def _GetTestModules(host_driven_test_root, is_official_build): 45 """Retrieve a list of python modules that match the testing naming scheme. 46 47 Walks the location of host-driven tests, imports them, and provides the list 48 of imported modules to the caller. 49 50 Args: 51 host_driven_test_root: The path to walk, looking for the 52 pythonDrivenTests or host_driven_tests directory 53 is_official_build: Whether to run only those tests marked 'official' 54 55 Returns: 56 A list of python modules under |host_driven_test_root| which match the 57 testing naming scheme. Each module should define one or more classes that 58 derive from HostDrivenTestCase. 59 """ 60 # By default run all host-driven tests under pythonDrivenTests or 61 # host_driven_tests. 62 host_driven_test_file_list = [] 63 for root, _, files in os.walk(host_driven_test_root): 64 if (root.endswith('host_driven_tests') or 65 root.endswith('pythonDrivenTests') or 66 (is_official_build and (root.endswith('pythonDrivenTests/official') or 67 root.endswith('host_driven_tests/official')))): 68 host_driven_test_file_list += _GetPythonFiles(root, files) 69 host_driven_test_file_list.sort() 70 71 test_module_list = [_GetModuleFromFile(test_file) 72 for test_file in host_driven_test_file_list] 73 return test_module_list 74 75 76 def _GetModuleFromFile(python_file): 77 """Gets the python module associated with a file by importing it. 78 79 Args: 80 python_file: File to import. 81 82 Returns: 83 The module object. 84 """ 85 sys.path.append(os.path.dirname(python_file)) 86 import_name = _InferImportNameFromFile(python_file) 87 return __import__(import_name) 88 89 90 def _GetTestsFromClass(test_case_class, **kwargs): 91 """Returns one test object for each test method in |test_case_class|. 92 93 Test methods are methods on the class which begin with 'test'. 94 95 Args: 96 test_case_class: Class derived from HostDrivenTestCase which contains zero 97 or more test methods. 98 kwargs: Keyword args to pass into the constructor of test cases. 99 100 Returns: 101 A list of test case objects, each initialized for a particular test method. 102 """ 103 test_names = [m for m in dir(test_case_class) 104 if _IsTestMethod(m, test_case_class)] 105 return [test_case_class(name, **kwargs) for name in test_names] 106 107 108 def _GetTestsFromModule(test_module, **kwargs): 109 """Gets a list of test objects from |test_module|. 110 111 Args: 112 test_module: Module from which to get the set of test methods. 113 kwargs: Keyword args to pass into the constructor of test cases. 114 115 Returns: 116 A list of test case objects each initialized for a particular test method 117 defined in |test_module|. 118 """ 119 120 tests = [] 121 for name in dir(test_module): 122 attr = getattr(test_module, name) 123 if _IsTestCaseClass(attr): 124 tests.extend(_GetTestsFromClass(attr, **kwargs)) 125 return tests 126 127 128 def _IsTestCaseClass(test_class): 129 return (type(test_class) is types.TypeType and 130 issubclass(test_class, test_case.HostDrivenTestCase) and 131 test_class is not test_case.HostDrivenTestCase) 132 133 134 def _IsTestMethod(attrname, test_case_class): 135 """Checks whether this is a valid test method. 136 137 Args: 138 attrname: The method name. 139 test_case_class: The test case class. 140 141 Returns: 142 True if test_case_class.'attrname' is callable and it starts with 'test'; 143 False otherwise. 144 """ 145 attr = getattr(test_case_class, attrname) 146 return callable(attr) and attrname.startswith('test') 147 148 149 def _GetAllTests(test_root, is_official_build, **kwargs): 150 """Retrieve a list of host-driven tests defined under |test_root|. 151 152 Args: 153 test_root: Path which contains host-driven test files. 154 is_official_build: Whether this is an official build. 155 kwargs: Keyword args to pass into the constructor of test cases. 156 157 Returns: 158 List of test case objects, one for each available test method. 159 """ 160 if not test_root: 161 return [] 162 all_tests = [] 163 test_module_list = _GetTestModules(test_root, is_official_build) 164 for module in test_module_list: 165 all_tests.extend(_GetTestsFromModule(module, **kwargs)) 166 return all_tests 167 168 169 def InstrumentationSetup(host_driven_test_root, official_build, 170 instrumentation_options): 171 """Creates a list of host-driven instrumentation tests and a runner factory. 172 173 Args: 174 host_driven_test_root: Directory where the host-driven tests are. 175 official_build: True if this is an official build. 176 instrumentation_options: An InstrumentationOptions object. 177 178 Returns: 179 A tuple of (TestRunnerFactory, tests). 180 """ 181 182 test_collection = test_info_collection.TestInfoCollection() 183 all_tests = _GetAllTests( 184 host_driven_test_root, official_build, 185 instrumentation_options=instrumentation_options) 186 test_collection.AddTests(all_tests) 187 188 available_tests = test_collection.GetAvailableTests( 189 instrumentation_options.annotations, 190 instrumentation_options.exclude_annotations, 191 instrumentation_options.test_filter) 192 logging.debug('All available tests: ' + str( 193 [t.tagged_name for t in available_tests])) 194 195 def TestRunnerFactory(device, shard_index): 196 return test_runner.HostDrivenTestRunner( 197 device, shard_index, 198 instrumentation_options.tool, 199 instrumentation_options.push_deps, 200 instrumentation_options.cleanup_test_files) 201 202 return (TestRunnerFactory, available_tests) 203