• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2#
3# Copyright 2018, 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"""Unittests for module_info_utils."""
18
19import copy
20import logging
21import os.path
22import unittest
23from unittest import mock
24
25from aidegen import aidegen_main
26from aidegen import unittest_constants
27from aidegen.lib import common_util
28from aidegen.lib import errors
29from aidegen.lib import module_info_util
30from aidegen.lib import project_config
31
32from atest import atest_utils
33
34_TEST_CLASS_DICT = {'class': ['JAVA_LIBRARIES']}
35_TEST_SRCS_BAR_DICT = {'srcs': ['Bar']}
36_TEST_SRCS_BAZ_DICT = {'srcs': ['Baz']}
37_TEST_DEP_FOO_DIST = {'dependencies': ['Foo']}
38_TEST_DEP_SRC_DICT = {'dependencies': ['Foo'], 'srcs': ['Bar']}
39_TEST_DEP_SRC_MORE_DICT = {'dependencies': ['Foo'], 'srcs': ['Baz', 'Bar']}
40_TEST_MODULE_A_DICT = {
41    'module_a': {
42        'class': ['JAVA_LIBRARIES'],
43        'path': ['path_a'],
44        'installed': ['out/path_a/a.jar'],
45        'dependencies': ['Foo'],
46        'srcs': ['Bar'],
47        'compatibility_suites': ['null-suite'],
48        'module_name': ['ltp_fstat03_64']
49    }
50}
51_TEST_MODULE_A_DICT_HAS_NONEED_ITEMS = {
52    'module_a': {
53        'class': ['JAVA_LIBRARIES'],
54        'path': ['path_a'],
55        'installed': ['out/path_a/a.jar'],
56        'dependencies': ['Foo'],
57        'srcs': ['Bar'],
58        'compatibility_suites': ['null-suite'],
59        'module_name': ['ltp_fstat03_64']
60    }
61}
62_TEST_MODULE_A_JOIN_PATH_DICT = {
63    'module_a': {
64        'class': ['JAVA_LIBRARIES'],
65        'path': ['path_a'],
66        'installed': ['out/path_a/a.jar'],
67        'dependencies': ['Foo'],
68        'srcs': ['path_a/Bar']
69    }
70}
71
72
73# pylint: disable=invalid-name
74# pylint: disable=protected-access
75# ptlint: disable=too-many-format-args
76class AidegenModuleInfoUtilUnittests(unittest.TestCase):
77    """Unit tests for module_info_utils.py"""
78
79    @mock.patch.object(os.path, 'getmtime')
80    @mock.patch.object(logging, 'info')
81    @mock.patch.object(os.path, 'isfile')
82    @mock.patch.object(module_info_util, '_get_generated_json_files')
83    def test_of_build_bp_info_reuse_jsons(self, mock_json, mock_isfile,
84                                          mock_log, mock_time):
85        """Test of _build_bp_info to well reuse existing files."""
86        gen_files = ['file1', 'file_a', 'file_b']
87        mock_json.return_value = gen_files
88        # Test of the well reuse existing files.
89        mock_isfile.side_effect = (True, True, True)
90        mod_info = mock.MagicMock()
91        module_info_util._build_bp_info(mod_info, skip_build=True)
92        self.assertTrue(mock_json.called)
93        self.assertTrue(mock_log.called)
94        self.assertFalse(mock_time.called)
95
96    # pylint: disable=too-many-arguments
97    @mock.patch.object(module_info_util, '_generate_rust_project_link')
98    @mock.patch.object(module_info_util, '_show_build_failed_message')
99    @mock.patch.object(module_info_util, '_show_files_reuse_message')
100    @mock.patch.object(atest_utils, 'build')
101    @mock.patch.object(os.path, 'getmtime')
102    @mock.patch.object(logging, 'warning')
103    @mock.patch.object(os.path, 'isfile')
104    @mock.patch.object(module_info_util, '_get_generated_json_files')
105    def test_of_build_bp_info_rebuild_jsons(self, mock_json, mock_isfile,
106                                            mock_log, mock_time, mock_build,
107                                            mock_reuse, mock_fail,
108                                            mock_gen_rust):
109        """Test of _build_bp_info on rebuilding jsons."""
110        gen_files = ['file2', 'file_a', 'file_b']
111        mock_json.return_value = gen_files
112        mod_info = mock.MagicMock()
113        # Test of the existing files can't reuse.
114        mock_json.return_value = gen_files
115        mock_isfile.side_effect = (True, False, True)
116        mock_time.side_effect = (None, None, None)
117        mock_build.side_effect = (True, True)
118        module_info_util._build_bp_info(mod_info, skip_build=True)
119        self.assertTrue(mock_json.called)
120        self.assertTrue(mock_log.called)
121        self.assertTrue(mock_gen_rust.called)
122
123        # Test of the well rebuild case.
124        action_pass = '\nGenerate blueprint json successfully.'
125        self.assertTrue(mock_build.called)
126        self.assertTrue(mock_log.called_with(action_pass))
127        self.assertFalse(mock_reuse.called)
128        self.assertFalse(mock_fail.called)
129
130    @mock.patch.object(module_info_util, '_generate_rust_project_link')
131    @mock.patch.object(module_info_util, '_show_build_failed_message')
132    @mock.patch.object(module_info_util, '_show_files_reuse_message')
133    @mock.patch.object(module_info_util, '_is_new_json_file_generated')
134    @mock.patch.object(atest_utils, 'build')
135    @mock.patch.object(os.path, 'getmtime')
136    @mock.patch.object(logging, 'warning')
137    @mock.patch.object(os.path, 'isfile')
138    @mock.patch.object(module_info_util, '_get_generated_json_files')
139    def test_of_build_bp_info_show_build_fail(self, mock_json, mock_isfile,
140                                              mock_log, mock_time, mock_build,
141                                              mock_judge, mock_reuse,
142                                              mock_fail, mock_gen_rust):
143        """Test of _build_bp_info to show build failed message."""
144        gen_files = ['file3', 'file_a', 'file_b']
145        mock_json.return_value = gen_files
146        mod_info = mock.MagicMock()
147        # Test rebuild failed case - show build fail message.
148        mock_json.return_value = gen_files
149        test_prj = 'main'
150        mock_isfile.side_effect = (True, False, True)
151        mock_time.side_effect = (None, None, None)
152        mock_build.side_effect = (True, False)
153        mock_judge.side_effect = [True, False, False]
154        module_info_util._build_bp_info(mod_info, main_project=test_prj,
155                                        skip_build=False)
156        self.assertTrue(mock_json.called)
157        self.assertTrue(mock_log.called)
158        self.assertTrue(mock_build.called)
159        self.assertTrue(mock_gen_rust.called)
160        self.assertFalse(mock_reuse.called)
161        self.assertFalse(mock_fail.called)
162
163    @mock.patch.object(module_info_util, '_generate_rust_project_link')
164    @mock.patch.object(module_info_util, '_show_build_failed_message')
165    @mock.patch.object(module_info_util, '_show_files_reuse_message')
166    @mock.patch.object(module_info_util, '_is_new_json_file_generated')
167    @mock.patch.object(atest_utils, 'build')
168    @mock.patch.object(os.path, 'getmtime')
169    @mock.patch.object(logging, 'warning')
170    @mock.patch.object(os.path, 'isfile')
171    @mock.patch.object(module_info_util, '_get_generated_json_files')
172    def test_of_build_bp_info_rebuild_and_reuse(self, mock_json, mock_isfile,
173                                                mock_log, mock_time, mock_build,
174                                                mock_judge, mock_reuse,
175                                                mock_fail, mock_gen_rust):
176        """Test of _build_bp_info to reuse existing jsons."""
177        gen_files = ['file4', 'file_a', 'file_b']
178        mock_json.return_value = gen_files
179        mod_info = mock.MagicMock()
180        mock_json.return_value = gen_files
181        test_prj = 'main'
182        # Test rebuild failed case - show reuse message.
183        mock_isfile.side_effect = (True, True, True)
184        mock_time.side_effect = (None, None, None)
185        mock_build.side_effect = (True, False)
186        mock_judge.side_effect = [True, False, True]
187        module_info_util._build_bp_info(
188            mod_info, main_project=test_prj, skip_build=False)
189        self.assertTrue(mock_log.called)
190        self.assertTrue(mock_gen_rust.called)
191        self.assertFalse(mock_reuse.called)
192        self.assertFalse(mock_fail.called)
193
194    @mock.patch.object(module_info_util, '_generate_rust_project_link')
195    @mock.patch.object(module_info_util, '_show_build_failed_message')
196    @mock.patch.object(module_info_util, '_show_files_reuse_message')
197    @mock.patch.object(module_info_util, '_is_new_json_file_generated')
198    @mock.patch.object(atest_utils, 'build')
199    @mock.patch.object(os.path, 'getmtime')
200    @mock.patch.object(logging, 'warning')
201    @mock.patch.object(os.path, 'isfile')
202    @mock.patch.object(module_info_util, '_get_generated_json_files')
203    def test_of_build_bp_info_reuse_pass(self, mock_json, mock_isfile, mock_log,
204                                         mock_time, mock_build, mock_judge,
205                                         mock_reuse, mock_fail, mock_gen_rust):
206        """Test of _build_bp_info reuse pass."""
207        gen_files = ['file5', 'file_a', 'file_b']
208        mock_json.return_value = gen_files
209        test_prj = 'main'
210        mod_info = mock.MagicMock()
211        # Test rebuild failed case - show nothing.
212        mock_isfile.side_effect = (False, True, True)
213        mock_time.side_effect = (None, None, None)
214        mock_build.side_effect = (True, False)
215        mock_judge.side_effect = [True, True, True]
216        module_info_util._build_bp_info(mod_info, main_project=test_prj,
217                                        skip_build=False)
218        self.assertTrue(mock_log.called)
219        self.assertTrue(mock_gen_rust.called)
220        self.assertFalse(mock_reuse.called)
221        self.assertFalse(mock_fail.called)
222
223    # pylint: enable=too-many-arguments
224    def test_merge_module_keys_with_empty_dict(self):
225        """Test _merge_module_keys with an empty dictionary."""
226        test_b_dict = {}
227        test_m_dict = copy.deepcopy(_TEST_DEP_SRC_DICT)
228        module_info_util._merge_module_keys(test_m_dict, test_b_dict)
229        self.assertEqual(_TEST_DEP_SRC_DICT, test_m_dict)
230
231    def test_merge_module_keys_with_key_not_in_orginial_dict(self):
232        """Test _merge_module_keys with the key does not exist in the dictionary
233        to be merged into.
234        """
235        test_b_dict = _TEST_SRCS_BAR_DICT
236        test_m_dict = copy.deepcopy(_TEST_DEP_FOO_DIST)
237        module_info_util._merge_module_keys(test_m_dict, test_b_dict)
238        self.assertEqual(_TEST_DEP_SRC_DICT, test_m_dict)
239
240    def test_merge_module_keys_with_key_in_orginial_dict(self):
241        """Test _merge_module_keys with with the key exists in the dictionary
242        to be merged into.
243        """
244        test_b_dict = _TEST_SRCS_BAZ_DICT
245        test_m_dict = copy.deepcopy(_TEST_DEP_SRC_DICT)
246        module_info_util._merge_module_keys(test_m_dict, test_b_dict)
247        self.assertEqual(
248            set(_TEST_DEP_SRC_MORE_DICT['srcs']), set(test_m_dict['srcs']))
249        self.assertEqual(
250            set(_TEST_DEP_SRC_MORE_DICT['dependencies']),
251            set(test_m_dict['dependencies']))
252
253    def test_merge_module_keys_with_duplicated_item_dict(self):
254        """Test _merge_module_keys with with the key exists in the dictionary
255        to be merged into.
256        """
257        test_b_dict = _TEST_CLASS_DICT
258        test_m_dict = copy.deepcopy(_TEST_CLASS_DICT)
259        module_info_util._merge_module_keys(test_m_dict, test_b_dict)
260        self.assertEqual(_TEST_CLASS_DICT, test_m_dict)
261
262    def test_copy_needed_items_from_empty_dict(self):
263        """Test _copy_needed_items_from an empty dictionary."""
264        test_mk_dict = {}
265        want_dict = {}
266        self.assertEqual(want_dict,
267                         module_info_util._copy_needed_items_from(test_mk_dict))
268
269    def test_copy_needed_items_from_all_needed_items_dict(self):
270        """Test _copy_needed_items_from a dictionary with all needed items."""
271        self.assertEqual(
272            _TEST_MODULE_A_DICT,
273            module_info_util._copy_needed_items_from(_TEST_MODULE_A_DICT))
274
275    def test_copy_needed_items_from_some_needed_items_dict(self):
276        """Test _copy_needed_items_from a dictionary with some needed items."""
277        self.assertEqual(
278            _TEST_MODULE_A_DICT,
279            module_info_util._copy_needed_items_from(
280                _TEST_MODULE_A_DICT_HAS_NONEED_ITEMS))
281
282    @mock.patch.object(os.path, 'getmtime')
283    @mock.patch.object(atest_utils, 'build')
284    @mock.patch('os.path.isfile')
285    def test_build_bp_info_normal(self, mock_isfile, mock_build, mock_time):
286        """Test _build_target with verbose true and false."""
287        amodule_info = mock.MagicMock()
288        skip = True
289        mock_isfile.return_value = True
290        mock_build.return_value = True
291        module_info_util._build_bp_info(amodule_info, unittest_constants.
292                                        TEST_MODULE, False, skip)
293        self.assertFalse(mock_build.called)
294        skip = False
295        mock_time.return_value = float()
296        module_info_util._build_bp_info(amodule_info, unittest_constants.
297                                        TEST_MODULE, False, skip)
298        self.assertTrue(mock_time.called)
299        self.assertEqual(mock_build.call_count, 1)
300
301    @mock.patch('os.path.getmtime')
302    @mock.patch('os.path.isfile')
303    def test_is_new_json_file_generated(self, mock_isfile, mock_getmtime):
304        """Test _is_new_json_file_generated with different situations."""
305        jfile = 'path/test.json'
306        mock_isfile.return_value = False
307        self.assertFalse(
308            module_info_util._is_new_json_file_generated(jfile, None))
309        mock_isfile.return_value = True
310        self.assertTrue(
311            module_info_util._is_new_json_file_generated(jfile, None))
312        original_file_mtime = 1000
313        mock_getmtime.return_value = original_file_mtime
314        self.assertFalse(
315            module_info_util._is_new_json_file_generated(
316                jfile, original_file_mtime))
317        mock_getmtime.return_value = 1001
318        self.assertTrue(
319            module_info_util._is_new_json_file_generated(
320                jfile, original_file_mtime))
321
322    @mock.patch('builtins.input')
323    @mock.patch('glob.glob')
324    def test_build_failed_handle(self, mock_glob, mock_input):
325        """Test _build_failed_handle with different situations."""
326        mock_glob.return_value = ['project/file.iml']
327        mock_input.return_value = 'N'
328        with self.assertRaises(SystemExit) as cm:
329            module_info_util._build_failed_handle(
330                unittest_constants.TEST_MODULE)
331        self.assertEqual(cm.exception.code, 1)
332        mock_glob.return_value = []
333        with self.assertRaises(errors.BuildFailureError):
334            module_info_util._build_failed_handle(
335                unittest_constants.TEST_MODULE)
336
337    @mock.patch.object(project_config.ProjectConfig, 'get_instance')
338    @mock.patch.object(module_info_util, '_merge_dict')
339    @mock.patch.object(common_util, 'get_json_dict')
340    @mock.patch.object(module_info_util, '_build_bp_info')
341    def test_generate_merged_module_info(
342            self, mock_build, mock_get_soong, mock_merge_dict, mock_get_inst):
343        """Test generate_merged_module_info function."""
344        config = mock.MagicMock()
345        config.atest_module_info = mock.MagicMock()
346        main_project = 'tradefed'
347        args = aidegen_main._parse_args([main_project, '-n', '-v'])
348        config.targets = args.targets
349        config.verbose = args.verbose
350        config.is_skip_build = args.skip_build
351        module_info_util.generate_merged_module_info()
352        self.assertTrue(mock_get_inst.called)
353        self.assertTrue(mock_build.called)
354        self.assertTrue(mock_get_soong.called)
355        self.assertTrue(mock_merge_dict.called)
356
357    @mock.patch.object(common_util, 'get_blueprint_json_files_relative_dict')
358    def test_get_generated_json_files(self, mock_get_bp_dict):
359        """Test _get_generated_json_files function with condictions,"""
360        a_env = 'GEN_A'
361        b_env = 'GEN_B'
362        a_file_path = 'a/b/path/to/a_file'
363        b_file_path = 'a/b/path/to/b_file'
364        file_paths = [a_file_path, b_file_path]
365        env_on = {a_env: 'true', b_env: 'true'}
366        mock_get_bp_dict.return_value = {a_env: a_file_path, b_env: b_file_path}
367        result_paths = file_paths
368        self.assertEqual(
369            result_paths, module_info_util._get_generated_json_files(env_on))
370        result_paths = []
371        env_on = {a_env: 'false', b_env: 'false'}
372        self.assertEqual(
373            result_paths, module_info_util._get_generated_json_files(env_on))
374        c_env = 'GEN_C'
375        d_env = 'GEN_D'
376        c_file_path = 'a/b/path/to/d_file'
377        d_file_path = 'a/b/path/to/d_file'
378        env_on = {a_env: 'true', b_env: 'true'}
379        mock_get_bp_dict.return_value = {c_env: c_file_path, d_env: d_file_path}
380        result_paths = []
381        self.assertEqual(
382            result_paths, module_info_util._get_generated_json_files(env_on))
383
384    @mock.patch.object(common_util, 'get_related_paths')
385    @mock.patch.object(module_info_util, '_build_failed_handle')
386    def test_show_build_failed_message(
387            self, mock_handle, mock_relpath):
388        """Test _show_build_failed_message with different conditions."""
389        main_project = ''
390        mock_relpath.return_value = 'c/d', 'a/b/c/d'
391        module_info_util._show_build_failed_message({}, main_project)
392        self.assertFalse(mock_handle.called)
393        main_project = 'tradefed'
394        module_info_util._show_build_failed_message({}, main_project)
395        self.assertTrue(mock_handle.called)
396
397    # pylint: disable=too-many-arguments
398    @mock.patch.object(module_info_util, '_show_build_failed_message')
399    @mock.patch.object(module_info_util, '_show_files_reuse_message')
400    @mock.patch.object(logging, 'warning')
401    @mock.patch.object(module_info_util, '_is_new_json_file_generated')
402    @mock.patch.object(atest_utils, 'build')
403    @mock.patch.object(os.path, 'getmtime')
404    @mock.patch.object(logging, 'info')
405    @mock.patch.object(os.path, 'isfile')
406    @mock.patch.object(module_info_util, '_get_generated_json_files')
407    def test_build_bp_info_skip(self, mock_gen_jsons, mock_isfile,
408                                mock_log_info, mock_get_mtimes, mock_build,
409                                mock_news, mock_warn, mock_show_reuse,
410                                mock_build_fail):
411        """Test _build_bp_info function with skip build."""
412        mock_gen_jsons.return_value = [
413            'a/b/out/soong/module_bp_java_deps.json',
414            'a/b/out/soong/module_bp_cc_deps.json'
415        ]
416        mock_isfile.return_value = True
417        module_info_util._build_bp_info(
418            module_info=mock.Mock(), skip_build=True)
419        self.assertTrue(mock_log_info.called)
420        self.assertFalse(mock_get_mtimes.called)
421        self.assertFalse(mock_build.called)
422        self.assertFalse(mock_news.called)
423        self.assertFalse(mock_warn.called)
424        self.assertFalse(mock_show_reuse.called)
425        self.assertFalse(mock_build_fail.called)
426
427    @mock.patch.object(module_info_util, '_show_build_failed_message')
428    @mock.patch.object(module_info_util, '_show_files_reuse_message')
429    @mock.patch.object(logging, 'warning')
430    @mock.patch.object(module_info_util, '_is_new_json_file_generated')
431    @mock.patch.object(atest_utils, 'build')
432    @mock.patch.object(os.path, 'getmtime')
433    @mock.patch.object(logging, 'info')
434    @mock.patch.object(os.path, 'isfile')
435    @mock.patch.object(module_info_util, '_get_generated_json_files')
436    def test_build_bp_info_no_skip(self, mock_gen_jsons, mock_isfile,
437                                   mock_log_info, mock_get_mtimes, mock_build,
438                                   mock_news, mock_warn, mock_show_reuse,
439                                   mock_build_fail):
440        """Test _build_bp_info function without skip build."""
441        mock_gen_jsons.return_value = [
442            'a/b/out/soong/module_bp_java_deps.json',
443            'a/b/out/soong/module_bp_cc_deps.json'
444        ]
445        mock_isfile.return_value = True
446        mock_build.return_value = True
447        module_info_util._build_bp_info(
448            module_info=mock.Mock(), skip_build=False)
449        self.assertTrue(mock_log_info.called)
450        self.assertTrue(mock_get_mtimes.called)
451        self.assertTrue(mock_build.called)
452        self.assertFalse(mock_news.called)
453        self.assertTrue(mock_warn.called)
454        self.assertFalse(mock_show_reuse.called)
455        self.assertFalse(mock_build_fail.called)
456
457    @mock.patch.object(module_info_util, '_show_build_failed_message')
458    @mock.patch.object(module_info_util, '_show_files_reuse_message')
459    @mock.patch.object(logging, 'warning')
460    @mock.patch.object(module_info_util, '_is_new_json_file_generated')
461    @mock.patch.object(atest_utils, 'build')
462    @mock.patch.object(os.path, 'getmtime')
463    @mock.patch.object(logging, 'info')
464    @mock.patch.object(os.path, 'isfile')
465    @mock.patch.object(module_info_util, '_get_generated_json_files')
466    def test_build_bp_info_failed(self, mock_gen_jsons, mock_isfile,
467                                  mock_log_info, mock_get_mtimes, mock_build,
468                                  mock_news, mock_warn, mock_show_reuse,
469                                  mock_build_fail):
470        """Test _build_bp_info function with build failed."""
471        mock_gen_jsons.return_value = [
472            'a/b/out/soong/module_bp_java_deps.json',
473            'a/b/out/soong/module_bp_cc_deps.json'
474        ]
475        mock_isfile.return_value = True
476        mock_build.return_value = False
477        mock_news.return_value = False
478        module_info_util._build_bp_info(
479            module_info=mock.Mock(), skip_build=False)
480        self.assertFalse(mock_log_info.called)
481        self.assertTrue(mock_get_mtimes.called)
482        self.assertTrue(mock_build.called)
483        self.assertTrue(mock_news.called)
484        self.assertTrue(mock_warn.called)
485        self.assertTrue(mock_show_reuse.called)
486        self.assertFalse(mock_build_fail.called)
487
488    @mock.patch.object(module_info_util, '_show_build_failed_message')
489    @mock.patch.object(module_info_util, '_show_files_reuse_message')
490    @mock.patch.object(logging, 'warning')
491    @mock.patch.object(module_info_util, '_is_new_json_file_generated')
492    @mock.patch.object(atest_utils, 'build')
493    @mock.patch.object(os.path, 'getmtime')
494    @mock.patch.object(logging, 'info')
495    @mock.patch.object(os.path, 'isfile')
496    @mock.patch.object(module_info_util, '_get_generated_json_files')
497    def test_build_bp_info_failed_not_exist(
498            self, mock_gen_jsons, mock_isfile, mock_log_info,
499            mock_get_mtimes, mock_build, mock_news, mock_warn, mock_show_reuse,
500            mock_build_fail):
501        """Test _build_bp_info function with build failed files not exist."""
502        mock_gen_jsons.return_value = [
503            'a/b/out/soong/module_bp_java_deps.json',
504            'a/b/out/soong/module_bp_cc_deps.json'
505        ]
506        mock_isfile.return_value = False
507        mock_build.return_value = False
508        mock_news.return_value = False
509        module_info_util._build_bp_info(
510            module_info=mock.Mock(), skip_build=False)
511        self.assertFalse(mock_log_info.called)
512        self.assertFalse(mock_get_mtimes.called)
513        self.assertTrue(mock_build.called)
514        self.assertTrue(mock_news.called)
515        self.assertTrue(mock_warn.called)
516        self.assertFalse(mock_show_reuse.called)
517        self.assertTrue(mock_build_fail.called)
518
519    @mock.patch.object(module_info_util, '_show_build_failed_message')
520    @mock.patch.object(module_info_util, '_show_files_reuse_message')
521    @mock.patch.object(logging, 'warning')
522    @mock.patch.object(module_info_util, '_is_new_json_file_generated')
523    @mock.patch.object(atest_utils, 'build')
524    @mock.patch.object(os.path, 'getmtime')
525    @mock.patch.object(logging, 'info')
526    @mock.patch.object(os.path, 'isfile')
527    @mock.patch.object(module_info_util, '_get_generated_json_files')
528    def test_build_bp_info_failed_files_exist(
529            self, mock_gen_jsons, mock_isfile, mock_log_info,
530            mock_get_mtimes, mock_build, mock_news, mock_warn, mock_show_reuse,
531            mock_build_fail):
532        """Test _build_bp_info function with build failed files not exist."""
533        mock_gen_jsons.return_value = [
534            'a/b/out/soong/module_bp_java_deps.json',
535            'a/b/out/soong/module_bp_cc_deps.json'
536        ]
537        mock_isfile.return_value = False
538        mock_build.return_value = False
539        mock_news.return_value = True
540        module_info_util._build_bp_info(
541            module_info=mock.Mock(), skip_build=False)
542        self.assertFalse(mock_log_info.called)
543        self.assertFalse(mock_get_mtimes.called)
544        self.assertTrue(mock_build.called)
545        self.assertTrue(mock_news.called)
546        self.assertTrue(mock_warn.called)
547        self.assertFalse(mock_show_reuse.called)
548        self.assertFalse(mock_build_fail.called)
549
550    @mock.patch('builtins.print')
551    @mock.patch('os.symlink')
552    @mock.patch('os.remove')
553    @mock.patch('os.path.islink')
554    @mock.patch('os.path.isfile')
555    def test_generate_rust_project_link(self, mock_isfile, mock_islink,
556                                        mock_remove, mock_symlink, mock_print):
557        """Test _generate_rust_project_link function."""
558        mock_isfile.return_value = True
559        mock_islink.return_value = False
560        module_info_util._generate_rust_project_link()
561        self.assertFalse(mock_print.called)
562        self.assertFalse(mock_remove.called)
563        self.assertTrue(mock_symlink.called)
564
565        mock_symlink.mock_reset()
566        mock_remove.mock_reset()
567        mock_print.mock_reset()
568        mock_islink.return_value = True
569        module_info_util._generate_rust_project_link()
570        self.assertTrue(mock_remove.called)
571        self.assertFalse(mock_print.called)
572        self.assertTrue(mock_symlink.called)
573
574        mock_symlink.mock_reset()
575        mock_remove.mock_reset()
576        mock_print.mock_reset()
577        mock_isfile.return_value = False
578        module_info_util._generate_rust_project_link()
579        self.assertTrue(mock_print.called)
580
581        mock_symlink.mock_reset()
582        mock_remove.mock_reset()
583        mock_print.mock_reset()
584        mock_islink.return_value = True
585        module_info_util._generate_rust_project_link()
586        self.assertTrue(mock_print.called)
587
588if __name__ == '__main__':
589    unittest.main()
590