1# 2# Copyright (C) 2017 The Android Open Source Project 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15# 16 17import importlib 18import json 19import logging 20import os 21import subprocess 22import sys 23import time 24 25from vts.runners.host import asserts 26from vts.runners.host import base_test 27from vts.runners.host import config_parser 28from vts.runners.host import records 29from vts.runners.host import test_runner 30from vts.utils.python.io import capture_printout 31from vts.utils.python.io import file_util 32 33ACTS_TEST_MODULE = 'ACTS_TEST_MODULE' 34LIST_TEST_OUTPUT_START = '==========> ' 35LIST_TEST_OUTPUT_END = ' <==========' 36# Temp directory inside python log path. The name is required to be 37# 'temp' for the Java framework to skip reading contents as regular test logs. 38TEMP_DIR_NAME = 'temp' 39CONFIG_FILE_NAME = 'acts_config.txt' 40RESULT_FILE_NAME = 'test_run_summary.json' 41 42CONFIG_TEXT = '''{{ 43 "_description": "VTS acts tests", 44 "testbed": 45 [ 46 {{ 47 "_description": "ACTS test bed", 48 "name": "{module_name}", 49 "AndroidDevice": 50 [ 51 {serials} 52 ] 53 }} 54 ], 55 "logpath": "{log_path}", 56 "testpaths": 57 [ 58 "{src_path}" 59 ] 60}} 61''' 62 63 64class ActsAdapter(base_test.BaseTestClass): 65 '''Template class for running acts test cases. 66 67 Attributes: 68 test_type: string, name of test type this adapter is for 69 result_path: string, test result directory for the adaptor 70 config_path: string, test config file path 71 module_name: string, ACTS module name 72 test_path: string, ACTS module source directory 73 ''' 74 test_type = 'ACTS' 75 76 def setUpClass(self): 77 '''Set up result directory, generate configuration file, and list tests.''' 78 self.result_path = os.path.join(logging.log_path, TEMP_DIR_NAME, 79 self.test_type, str(time.time())) 80 file_util.Makedirs(self.result_path) 81 logging.debug('Result path for %s: %s' % (self.test_type, 82 self.result_path)) 83 self.test_path, self.module_name = self.getUserParam( 84 ACTS_TEST_MODULE).rsplit('/', 1) 85 86 self.config_path = os.path.join(self.result_path, CONFIG_FILE_NAME) 87 self.GenerateConfigFile() 88 89 testcases = self.ListTestCases() 90 logging.debug('ACTS Test cases: %s', testcases) 91 92 def tearDownClass(self): 93 '''Clear the result path.''' 94 file_util.Rmdirs(self.result_path, ignore_errors=True) 95 96 def GenerateConfigFile(self): 97 '''Generate test configuration file.''' 98 serials = [] 99 for ad in self.android_devices: 100 serials.append('{"serial":"%s"}' % ad.serial) 101 102 config_text = CONFIG_TEXT.format( 103 module_name=self.module_name, 104 serials=','.join(serials), 105 log_path=self.result_path, 106 src_path=self.test_path) 107 108 with open(self.config_path, 'w') as f: 109 f.write(config_text) 110 111 def ListTestCases(self): 112 '''List test cases. 113 114 Returns: 115 List of string, test names. 116 ''' 117 # TODO use ACTS runner to list test cases and add requested record. 118 # This step is optional but desired. To be implemented later 119 120 def Run(self): 121 '''Execute test cases.''' 122 # acts.py is installed to user bin by ACTS setup script. 123 # In the future, it is preferred to use the source code 124 # from repo directory. 125 bin = 'acts/bin/act.py' 126 127 cmd = '{bin} -c {config} -tb {module_name} -tc {module_name}'.format( 128 bin=bin, config=self.config_path, module_name=self.module_name) 129 logging.debug('cmd is: %s', cmd) 130 131 # Calling through subprocess is required because ACTS requires python3 132 # while VTS is currently using python2. In the future, ACTS runner 133 # can be invoked through importing when VTS upgrades to python3. 134 135 # A "hack" to call python3 outside of python2 virtualenv created by 136 # VTS framework 137 environ = { 138 key: val 139 for key, val in os.environ.iteritems() if 'virtualenv' not in val 140 } 141 142 # TODO(yuexima): disable buffer 143 p = subprocess.Popen( 144 cmd, 145 shell=True, 146 stdout=subprocess.PIPE, 147 stderr=subprocess.STDOUT, 148 env=environ) 149 150 for line in iter(p.stdout.readline, b''): 151 print line.rstrip() 152 153 p.communicate() 154 if p.returncode: 155 asserts.fail('Subprocess of ACTS command failed. Return code: %s' % 156 p.returncode) 157 158 def ParseResults(self): 159 '''Get module run results and put in vts results.''' 160 file_path = file_util.FindFile(self.result_path, RESULT_FILE_NAME) 161 162 if file_path: 163 logging.debug('ACTS test result path: %s', file_path) 164 self.ParseJsonResults(file_path) 165 else: 166 logging.error('Cannot find result file name %s in %s', 167 RESULT_FILE_NAME, self.result_path) 168 169 def generateAllTests(self): 170 '''Run the test module and parse results.''' 171 self.Run() 172 self.ParseResults() 173 174 def ParseJsonResults(self, result_path): 175 '''Parse test json result. 176 177 Args: 178 result_path: string, result json file path. 179 ''' 180 with open(result_path, 'r') as f: 181 summary = json.load(f) 182 183 results = summary['Results'] 184 for result in results: 185 logging.debug('Adding result for %s' % 186 result[records.TestResultEnums.RECORD_NAME]) 187 record = records.TestResultRecord( 188 result[records.TestResultEnums.RECORD_NAME]) 189 record.test_class = result[records.TestResultEnums.RECORD_CLASS] 190 record.begin_time = result[ 191 records.TestResultEnums.RECORD_BEGIN_TIME] 192 record.end_time = result[records.TestResultEnums.RECORD_END_TIME] 193 record.result = result[records.TestResultEnums.RECORD_RESULT] 194 record.uid = result[records.TestResultEnums.RECORD_UID] 195 record.extras = result[records.TestResultEnums.RECORD_EXTRAS] 196 record.details = result[records.TestResultEnums.RECORD_DETAILS] 197 record.extra_errors = result[ 198 records.TestResultEnums.RECORD_EXTRA_ERRORS] 199 200 self.results.addRecord(record) 201 202 # TODO(yuexima): parse new result types 203 204if __name__ == "__main__": 205 test_runner.main() 206