Home | History | Annotate | Download | only in tests
      1 #
      2 # Copyright (C) 2015 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 import fnmatch
     17 
     18 
     19 class TestFilter(object):
     20     def __init__(self, patterns):
     21         self.early_filters = []
     22         self.late_filters = []
     23         for pattern in patterns:
     24             self.add_filter(pattern)
     25 
     26     def filter(self, test_name):
     27         filter_set = self.early_filters
     28         if '.' in test_name:
     29             filter_set = self.late_filters
     30         if len(filter_set) == 0:
     31             return True
     32         return any(f(test_name) for f in filter_set)
     33 
     34     def add_filter(self, pattern):
     35         """Adds a filter function based on the provided pattern.
     36 
     37         There are two types of filters we need to handle.
     38 
     39         1) Filters we can apply before running the test.
     40         2) Filters that cannot be run applied before running the test.
     41 
     42         We cannot apply all filters at a high level because we do not know all
     43         of the subtests until we begin running the test. The test cases for
     44         each device test are determined by searching for binaries in
     45         $OUT/libs/$ABI, and that list cannot be determined without building the
     46         test.
     47 
     48         Test filters that include a '.' will be split into two filters: one
     49         that filters on the whole test, and another that filters on the
     50         specific test case. This is done so foo will be built but bar will not
     51         if the case is 'foo.b*'. In this example, the resulting filters would
     52         be:
     53 
     54             Pass early filter if test name == 'foo'
     55             Pass late filter if case name matches 'foo.b*'
     56 
     57         This is still imperfect. While this does save us significant time in
     58         being able to skip building tests we don't care about, we have to
     59         specify them enough to do so. For example, *.foo will build everything.
     60         Also, if we have tests 'foo.bar_qux' and 'baz.bar_quux', we can't
     61         simply specify '*bar_*', but have to use '*.bar_*'.
     62         """
     63         # Note that the way we split the patterns does allow more than one '.'
     64         # to appear in the full test name. The early pattern will never contain
     65         # a '.', i.e. the early filter pattern for 'foo.bar.*' is 'foo'.
     66         early_pattern = pattern.split('.')[0]
     67         late_pattern = pattern
     68         if '.' not in pattern:
     69             late_pattern = pattern + '.*'
     70 
     71         self._add_early_filter(early_pattern)
     72         self._add_late_filter(late_pattern)
     73 
     74     def _make_filter_function(self, pattern):
     75         # pylint: disable=no-self-use
     76         def func(test_name):
     77             return fnmatch.fnmatch(test_name, pattern)
     78         return func
     79 
     80     def _add_early_filter(self, pattern):
     81         self.early_filters.append(self._make_filter_function(pattern))
     82 
     83     def _add_late_filter(self, pattern):
     84         self.late_filters.append(self._make_filter_function(pattern))
     85 
     86     @classmethod
     87     def from_string(cls, filter_string):
     88         return cls(filter_string.split(',') if filter_string else [])
     89