• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2019, The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""Cache Finder class."""
16
17import logging
18
19from atest import atest_utils
20from atest import constants
21from atest.test_finders import test_filter_utils
22from atest.test_finders import test_finder_base
23from atest.test_finders import test_info
24
25
26class CacheFinder(test_finder_base.TestFinderBase):
27  """Cache Finder class."""
28
29  NAME = 'CACHE'
30
31  def __init__(self, module_info=None):
32    super().__init__()
33    self.module_info = module_info
34
35  def _is_latest_testinfos(self, test_infos):
36    """Check whether test_infos are up-to-date.
37
38    Args:
39        test_infos: A list of TestInfo.
40
41    Returns:
42        True if all keys in test_infos and TestInfo object are equal.
43        Otherwise, False.
44    """
45    sorted_base_ti = sorted(vars(test_info.TestInfo(None, None, None)).keys())
46    for cached_test_info in test_infos:
47      sorted_cache_ti = sorted(vars(cached_test_info).keys())
48      if not sorted_cache_ti == sorted_base_ti:
49        logging.debug('test_info is not up-to-date.')
50        return False
51    return True
52
53  def find_test_by_cache(self, test_reference):
54    """Find the matched test_infos in saved caches.
55
56    Args:
57        test_reference: A string of the path to the test's file or dir.
58
59    Returns:
60        A list of TestInfo namedtuple if cache found and is in latest
61        TestInfo format, else None.
62    """
63    test_infos = atest_utils.load_test_info_cache(test_reference)
64    if test_infos and self._is_test_infos_valid(test_infos):
65      return test_infos
66    return None
67
68  def _is_test_infos_valid(self, test_infos):
69    """Check if the given test_infos are valid.
70
71    Args:
72        test_infos: A list of TestInfo.
73
74    Returns:
75        True if test_infos are all valid. Otherwise, False.
76    """
77    if not self._is_latest_testinfos(test_infos):
78      return False
79    for t_info in test_infos:
80      if t_info.test_runner == 'BazelTestRunner':
81        return False
82      if not self._is_test_path_valid(t_info):
83        return False
84      if not self._is_test_build_target_valid(t_info):
85        return False
86      if not self._is_test_filter_valid(t_info):
87        return False
88    return True
89
90  def _is_test_path_valid(self, t_info):
91    """Check if test path is valid.
92
93    Args:
94        t_info: TestInfo that has been filled out by a find method.
95
96    Returns:
97        True if test path is valid. Otherwise, False.
98    """
99    # For RoboTest it won't have 'MODULES-IN-' as build target. Treat test
100    # path is valid if cached_test_paths is None.
101    cached_test_paths = t_info.get_test_paths()
102    if cached_test_paths is None:
103      return True
104    current_test_paths = self.module_info.get_paths(t_info.test_name)
105    if not current_test_paths:
106      return False
107    formatted_paths = [p.replace('/', '-') for p in current_test_paths]
108    if sorted(cached_test_paths) != sorted(formatted_paths):
109      logging.debug('Not a valid test path.')
110      return False
111    return True
112
113  def _is_test_build_target_valid(self, t_info):
114    """Check if test build targets are valid.
115
116    Args:
117        t_info: TestInfo that has been filled out by a find method.
118
119    Returns:
120        True if test's build target is valid. Otherwise, False.
121    """
122    # If the cached build target can be found in current module-info, then
123    # it is a valid build targets of the test.
124    for build_target in t_info.build_targets:
125      if str(build_target).startswith(constants.MODULES_IN):
126        continue
127      if not self.module_info.is_module(build_target):
128        logging.debug('%s is not a valid build target.', build_target)
129        return False
130    return True
131
132  def _is_test_filter_valid(self, t_info):
133    """Check if test filter is valid.
134
135    Args:
136        t_info: TestInfo that has been filled out by a find method.
137
138    Returns:
139        True if test filter is valid. Otherwise, False.
140    """
141    test_filters = t_info.data.get(constants.TI_FILTER, [])
142    if not test_filters:
143      return True
144    for test_filter in test_filters:
145      # Check if the class filter is under current module.
146      # TODO: (b/172260100) The test_name may not be inevitably equal to
147      #  the module_name.
148      if not self._is_class_in_module(t_info.test_name, test_filter.class_name):
149        logging.debug('Not a valid test filter.')
150        return False
151    return True
152
153  def _is_class_in_module(self, module_name, filter_class):
154    """Check if input class is part of input module.
155
156    Args:
157        module_name: A string of the module name of the test.
158        filter_class: A string of the class name field of TI_FILTER.
159
160    Returns:
161        True if input filter_class is in the input module. Otherwise, False.
162    """
163    mod_info = self.module_info.get_module_info(module_name)
164    if not mod_info:
165      return False
166    module_srcs = mod_info.get(constants.MODULE_SRCS, [])
167    # If module didn't have src information treat the cached filter still
168    # valid. Remove this after all java srcs could be found in module-info.
169    if not module_srcs:
170      return True
171
172    for src_path in module_srcs:
173      abs_src_path = atest_utils.get_build_top(src_path)
174      if constants.CC_EXT_RE.match(src_path):
175        # TODO: (b/172260100) Also check for CC.
176        return True
177      else:
178        full_class_name = test_filter_utils.get_fully_qualified_class_name(
179            abs_src_path
180        )
181        package_name = test_filter_utils.get_package_name(abs_src_path)
182        if filter_class == full_class_name or filter_class == package_name:
183          return True
184    return False
185