1#!/usr/bin/env python3 2# 3# Copyright 2024, The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17"""Unit tests for coverage.""" 18 19# pylint: disable=invalid-name 20 21from pathlib import PosixPath 22import unittest 23from unittest import mock 24from atest import atest_utils 25from atest import constants 26from atest import module_info 27from atest.coverage import coverage 28from atest.test_finders import test_info 29 30 31class DeduceCodeUnderTestUnittests(unittest.TestCase): 32 """Tests for _deduce_code_under_test.""" 33 34 def test_code_under_test_is_defined_return_modules_in_code_under_test(self): 35 mod_info = create_module_info([ 36 module( 37 name='test1', 38 dependencies=['dep1', 'dep2'], 39 code_under_test=['dep1'], 40 ), 41 module(name='dep1', dependencies=['dep1dep1', 'dep1dep2']), 42 module(name='dep1dep1'), 43 module(name='dep1dep2', dependencies=['dep1dep2dep']), 44 module(name='dep1dep2dep'), 45 module(name='dep2'), 46 ]) 47 48 self.assertEqual( 49 coverage._deduce_code_under_test([create_test_info('test1')], mod_info), 50 {'dep1'}, 51 ) 52 53 def test_code_under_test_not_defined_return_all_modules_from_one_test(self): 54 mod_info = create_module_info([ 55 module(name='test1', dependencies=['dep1', 'dep2']), 56 module(name='dep1', dependencies=['dep1dep1', 'dep1dep2']), 57 module(name='dep1dep1'), 58 module(name='dep1dep2', dependencies=['dep1dep2dep']), 59 module(name='dep1dep2dep'), 60 module(name='dep2'), 61 module(name='shouldnotappear'), 62 ]) 63 64 self.assertEqual( 65 coverage._deduce_code_under_test([create_test_info('test1')], mod_info), 66 { 67 'test1', 68 'dep1', 69 'dep2', 70 'dep1dep1', 71 'dep1dep2', 72 'dep1dep2dep', 73 }, 74 ) 75 76 def test_code_under_test_not_defined_return_all_modules_from_all_tests(self): 77 mod_info = create_module_info([ 78 module(name='test1', dependencies=['testlib', 'test1dep']), 79 module(name='test2', dependencies=['testlib', 'test2dep']), 80 module(name='testlib', dependencies=['testlibdep']), 81 module(name='testlibdep'), 82 module(name='test1dep'), 83 module(name='test2dep'), 84 module(name='shouldnotappear'), 85 ]) 86 87 self.assertEqual( 88 coverage._deduce_code_under_test( 89 [create_test_info('test1'), create_test_info('test2')], mod_info 90 ), 91 {'test1', 'test2', 'testlib', 'testlibdep', 'test1dep', 'test2dep'}, 92 ) 93 94 95class CollectJavaReportJarsUnittests(unittest.TestCase): 96 """Test cases for _collect_java_report_jars.""" 97 98 @mock.patch.object( 99 atest_utils, 100 'get_build_out_dir', 101 return_value=PosixPath('/out/soong/.intermediates'), 102 ) 103 @mock.patch.object( 104 PosixPath, 105 'rglob', 106 return_value=[ 107 '/out/soong/.intermediates/path/to/java_lib/variant-name/jacoco-report-classes/java_lib.jar' 108 ], 109 ) 110 def test_java_lib(self, _rglob, _get_build_out_dir): 111 code_under_test = {'java_lib'} 112 mod_info = create_module_info([ 113 module(name='java_lib', path='path/to'), 114 ]) 115 116 self.assertEqual( 117 coverage._collect_java_report_jars(code_under_test, mod_info, False), 118 { 119 'java_lib': [ 120 '/out/soong/.intermediates/path/to/java_lib/variant-name/jacoco-report-classes/java_lib.jar' 121 ] 122 }, 123 ) 124 125 def test_host_test_includes_installed(self): 126 code_under_test = {'java_host_test'} 127 mod_info = create_module_info([ 128 module( 129 name='java_host_test', 130 installed=[ 131 '/path/to/out/host/java_host_test.jar', 132 '/path/to/out/host/java_host_test.config', 133 ], 134 ), 135 ]) 136 137 self.assertEqual( 138 coverage._collect_java_report_jars(code_under_test, mod_info, True), 139 {'java_host_test': ['/path/to/out/host/java_host_test.jar']}, 140 ) 141 142 143class CollectNativeReportBinariesUnittests(unittest.TestCase): 144 """Test cases for _collect_native_report_binaries.""" 145 146 @mock.patch.object( 147 atest_utils, 148 'get_build_out_dir', 149 return_value=PosixPath('/out/soong/.intermediates'), 150 ) 151 @mock.patch.object(PosixPath, 'glob') 152 def test_native_binary(self, _glob, _get_build_out_dir): 153 _glob.return_value = [ 154 PosixPath( 155 '/out/soong/.intermediates/path/to/native_bin/variant-name-cov/unstripped/native_bin' 156 ) 157 ] 158 code_under_test = {'native_bin'} 159 mod_info = create_module_info([ 160 module(name='native_bin', path='path/to'), 161 ]) 162 163 self.assertEqual( 164 coverage._collect_native_report_binaries( 165 code_under_test, mod_info, False 166 ), 167 { 168 '/out/soong/.intermediates/path/to/native_bin/variant-name-cov/unstripped/native_bin' 169 }, 170 ) 171 172 @mock.patch.object( 173 atest_utils, 174 'get_build_out_dir', 175 return_value=PosixPath('/out/soong/.intermediates'), 176 ) 177 @mock.patch.object(PosixPath, 'glob') 178 def test_skip_rsp_and_d_files(self, _glob, _get_build_out_dir): 179 _glob.return_value = [ 180 PosixPath( 181 '/out/soong/.intermediates/path/to/native_bin/variant-name-cov/unstripped/native_bin' 182 ), 183 PosixPath( 184 '/out/soong/.intermediates/path/to/native_bin/variant-name-cov/unstripped/native_bin.rsp' 185 ), 186 PosixPath( 187 '/out/soong/.intermediates/path/to/native_bin/variant-name-cov/unstripped/native_bin.d' 188 ), 189 ] 190 code_under_test = {'native_bin'} 191 mod_info = create_module_info([ 192 module(name='native_bin', path='path/to'), 193 ]) 194 195 self.assertEqual( 196 coverage._collect_native_report_binaries( 197 code_under_test, mod_info, False 198 ), 199 { 200 '/out/soong/.intermediates/path/to/native_bin/variant-name-cov/unstripped/native_bin' 201 }, 202 ) 203 204 def test_host_test_includes_installed(self): 205 code_under_test = {'native_host_test'} 206 mod_info = create_module_info([ 207 module( 208 name='native_host_test', 209 installed=['/out/host/nativetests/native_host_test'], 210 classes=[constants.MODULE_CLASS_NATIVE_TESTS], 211 ), 212 ]) 213 214 self.assertEqual( 215 coverage._collect_native_report_binaries( 216 code_under_test, mod_info, True 217 ), 218 {'/out/host/nativetests/native_host_test'}, 219 ) 220 221 222class GenerateCoverageReportUnittests(unittest.TestCase): 223 """Tests for the code-under-test feature.""" 224 225 @mock.patch.object(coverage, '_collect_java_report_jars', return_value={}) 226 @mock.patch.object( 227 coverage, '_collect_native_report_binaries', return_value=set() 228 ) 229 def test_generate_report_for_code_under_test_passed_in_from_atest( 230 self, _collect_native, _collect_java 231 ): 232 test_infos = [create_test_info('test')] 233 mod_info = create_module_info([ 234 module(name='test', dependencies=['lib1', 'lib2']), 235 module(name='lib1'), 236 module(name='lib2', dependencies=['lib2dep']), 237 module(name='lib2dep'), 238 ]) 239 code_under_test = ['lib1', 'lib2'] 240 241 coverage.generate_coverage_report( 242 '/tmp/results_dir', test_infos, mod_info, True, code_under_test 243 ) 244 245 _collect_java.assert_called_with(code_under_test, mod_info, True) 246 _collect_native.assert_called_with(code_under_test, mod_info, True) 247 248 @mock.patch.object(coverage, '_collect_java_report_jars', return_value={}) 249 @mock.patch.object( 250 coverage, '_collect_native_report_binaries', return_value=set() 251 ) 252 def test_generate_report_for_modules_get_from_deduce_code_under_test( 253 self, _collect_native, _collect_java 254 ): 255 test_infos = [create_test_info('test')] 256 mod_info = create_module_info([ 257 module(name='test', dependencies=['lib1', 'lib2']), 258 module(name='lib1'), 259 module(name='lib2', dependencies=['lib2dep']), 260 module(name='lib2dep'), 261 module(name='not_a_dep'), 262 ]) 263 264 coverage.generate_coverage_report( 265 '/tmp/results_dir', test_infos, mod_info, False, [] 266 ) 267 268 expected_code_under_test = {'test', 'lib1', 'lib2', 'lib2dep'} 269 _collect_java.assert_called_with(expected_code_under_test, mod_info, False) 270 _collect_native.assert_called_with( 271 expected_code_under_test, mod_info, False 272 ) 273 274 275def create_module_info(modules=None): 276 """Wrapper function for creating module_info.ModuleInfo.""" 277 name_to_module_info = {} 278 modules = modules or [] 279 280 for m in modules: 281 name_to_module_info[m['module_name']] = m 282 283 return module_info.load_from_dict(name_to_module_info) 284 285 286# pylint: disable=too-many-arguments 287def module( 288 name=None, 289 path=None, 290 installed=None, 291 classes=None, 292 auto_test_config=None, 293 test_config=None, 294 shared_libs=None, 295 dependencies=None, 296 runtime_dependencies=None, 297 data=None, 298 data_dependencies=None, 299 compatibility_suites=None, 300 host_dependencies=None, 301 srcs=None, 302 supported_variants=None, 303 code_under_test=None, 304): 305 name = name or 'libhello' 306 307 m = {} 308 309 m['module_name'] = name 310 m['class'] = classes or [] 311 m['path'] = [path or ''] 312 m['installed'] = installed or [] 313 m['is_unit_test'] = 'false' 314 m['auto_test_config'] = auto_test_config or [] 315 m['test_config'] = test_config or [] 316 m['shared_libs'] = shared_libs or [] 317 m['runtime_dependencies'] = runtime_dependencies or [] 318 m['dependencies'] = dependencies or [] 319 m['data'] = data or [] 320 m['data_dependencies'] = data_dependencies or [] 321 m['compatibility_suites'] = compatibility_suites or [] 322 m['host_dependencies'] = host_dependencies or [] 323 m['srcs'] = srcs or [] 324 m['supported_variants'] = supported_variants or [] 325 m['code_under_test'] = code_under_test or [] 326 return m 327 328 329def create_test_info(name='HelloWorldTest'): 330 """Helper function for creating test_info.TestInfo.""" 331 return test_info.TestInfo(name, 'AtestTradefedRunner', set()) 332 333 334if __name__ == '__main__': 335 unittest.main() 336