• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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