xref: /aosp_15_r20/external/skia/PRESUBMIT_test_mocks.py (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker# Copyright 2023 Google Inc.
2*c8dee2aaSAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be
3*c8dee2aaSAndroid Build Coastguard Worker# found in the LICENSE file.
4*c8dee2aaSAndroid Build Coastguard Worker
5*c8dee2aaSAndroid Build Coastguard Worker# This is a copy of PRESUBMIT_test_mocks.py from the Chromium project.
6*c8dee2aaSAndroid Build Coastguard Worker
7*c8dee2aaSAndroid Build Coastguard Workerfrom collections import defaultdict
8*c8dee2aaSAndroid Build Coastguard Workerimport fnmatch
9*c8dee2aaSAndroid Build Coastguard Workerimport json
10*c8dee2aaSAndroid Build Coastguard Workerimport os
11*c8dee2aaSAndroid Build Coastguard Workerimport re
12*c8dee2aaSAndroid Build Coastguard Workerimport subprocess
13*c8dee2aaSAndroid Build Coastguard Workerimport sys
14*c8dee2aaSAndroid Build Coastguard Worker
15*c8dee2aaSAndroid Build Coastguard Worker
16*c8dee2aaSAndroid Build Coastguard Workerdef _ReportErrorFileAndLine(filename, line_num, dummy_line):
17*c8dee2aaSAndroid Build Coastguard Worker    """Default error formatter for _FindNewViolationsOfRule."""
18*c8dee2aaSAndroid Build Coastguard Worker    return '%s:%s' % (filename, line_num)
19*c8dee2aaSAndroid Build Coastguard Worker
20*c8dee2aaSAndroid Build Coastguard Worker
21*c8dee2aaSAndroid Build Coastguard Workerclass MockCannedChecks(object):
22*c8dee2aaSAndroid Build Coastguard Worker    def _FindNewViolationsOfRule(self, callable_rule, input_api,
23*c8dee2aaSAndroid Build Coastguard Worker                                 source_file_filter=None,
24*c8dee2aaSAndroid Build Coastguard Worker                                 error_formatter=_ReportErrorFileAndLine):
25*c8dee2aaSAndroid Build Coastguard Worker        """Find all newly introduced violations of a per-line rule (a callable).
26*c8dee2aaSAndroid Build Coastguard Worker
27*c8dee2aaSAndroid Build Coastguard Worker        Arguments:
28*c8dee2aaSAndroid Build Coastguard Worker          callable_rule: a callable taking a file extension and line of input and
29*c8dee2aaSAndroid Build Coastguard Worker            returning True if the rule is satisfied and False if there was a
30*c8dee2aaSAndroid Build Coastguard Worker            problem.
31*c8dee2aaSAndroid Build Coastguard Worker          input_api: object to enumerate the affected files.
32*c8dee2aaSAndroid Build Coastguard Worker          source_file_filter: a filter to be passed to the input api.
33*c8dee2aaSAndroid Build Coastguard Worker          error_formatter: a callable taking (filename, line_number, line) and
34*c8dee2aaSAndroid Build Coastguard Worker            returning a formatted error string.
35*c8dee2aaSAndroid Build Coastguard Worker
36*c8dee2aaSAndroid Build Coastguard Worker        Returns:
37*c8dee2aaSAndroid Build Coastguard Worker          A list of the newly-introduced violations reported by the rule.
38*c8dee2aaSAndroid Build Coastguard Worker        """
39*c8dee2aaSAndroid Build Coastguard Worker        errors = []
40*c8dee2aaSAndroid Build Coastguard Worker        for f in input_api.AffectedFiles(include_deletes=False,
41*c8dee2aaSAndroid Build Coastguard Worker                                         file_filter=source_file_filter):
42*c8dee2aaSAndroid Build Coastguard Worker            # For speed, we do two passes, checking first the full file.  Shelling out
43*c8dee2aaSAndroid Build Coastguard Worker            # to the SCM to determine the changed region can be quite expensive on
44*c8dee2aaSAndroid Build Coastguard Worker            # Win32.  Assuming that most files will be kept problem-free, we can
45*c8dee2aaSAndroid Build Coastguard Worker            # skip the SCM operations most of the time.
46*c8dee2aaSAndroid Build Coastguard Worker            extension = str(f.LocalPath()).rsplit('.', 1)[-1]
47*c8dee2aaSAndroid Build Coastguard Worker            if all(callable_rule(extension, line) for line in f.NewContents()):
48*c8dee2aaSAndroid Build Coastguard Worker                # No violation found in full text: can skip considering diff.
49*c8dee2aaSAndroid Build Coastguard Worker                continue
50*c8dee2aaSAndroid Build Coastguard Worker
51*c8dee2aaSAndroid Build Coastguard Worker            for line_num, line in f.ChangedContents():
52*c8dee2aaSAndroid Build Coastguard Worker                if not callable_rule(extension, line):
53*c8dee2aaSAndroid Build Coastguard Worker                    errors.append(error_formatter(
54*c8dee2aaSAndroid Build Coastguard Worker                        f.LocalPath(), line_num, line))
55*c8dee2aaSAndroid Build Coastguard Worker
56*c8dee2aaSAndroid Build Coastguard Worker        return errors
57*c8dee2aaSAndroid Build Coastguard Worker
58*c8dee2aaSAndroid Build Coastguard Worker
59*c8dee2aaSAndroid Build Coastguard Workerclass MockInputApi(object):
60*c8dee2aaSAndroid Build Coastguard Worker    """Mock class for the InputApi class.
61*c8dee2aaSAndroid Build Coastguard Worker
62*c8dee2aaSAndroid Build Coastguard Worker    This class can be used for unittests for presubmit by initializing the files
63*c8dee2aaSAndroid Build Coastguard Worker    attribute as the list of changed files.
64*c8dee2aaSAndroid Build Coastguard Worker    """
65*c8dee2aaSAndroid Build Coastguard Worker
66*c8dee2aaSAndroid Build Coastguard Worker    DEFAULT_FILES_TO_SKIP = ()
67*c8dee2aaSAndroid Build Coastguard Worker
68*c8dee2aaSAndroid Build Coastguard Worker    def __init__(self):
69*c8dee2aaSAndroid Build Coastguard Worker        self.canned_checks = MockCannedChecks()
70*c8dee2aaSAndroid Build Coastguard Worker        self.fnmatch = fnmatch
71*c8dee2aaSAndroid Build Coastguard Worker        self.json = json
72*c8dee2aaSAndroid Build Coastguard Worker        self.re = re
73*c8dee2aaSAndroid Build Coastguard Worker        self.os_path = os.path
74*c8dee2aaSAndroid Build Coastguard Worker        self.platform = sys.platform
75*c8dee2aaSAndroid Build Coastguard Worker        self.python_executable = sys.executable
76*c8dee2aaSAndroid Build Coastguard Worker        self.python3_executable = sys.executable
77*c8dee2aaSAndroid Build Coastguard Worker        self.platform = sys.platform
78*c8dee2aaSAndroid Build Coastguard Worker        self.subprocess = subprocess
79*c8dee2aaSAndroid Build Coastguard Worker        self.sys = sys
80*c8dee2aaSAndroid Build Coastguard Worker        self.files = []
81*c8dee2aaSAndroid Build Coastguard Worker        self.is_committing = False
82*c8dee2aaSAndroid Build Coastguard Worker        self.change = MockChange([])
83*c8dee2aaSAndroid Build Coastguard Worker        self.presubmit_local_path = os.path.dirname(__file__)
84*c8dee2aaSAndroid Build Coastguard Worker        self.is_windows = sys.platform == 'win32'
85*c8dee2aaSAndroid Build Coastguard Worker        self.no_diffs = False
86*c8dee2aaSAndroid Build Coastguard Worker        # Although this makes assumptions about command line arguments used by test
87*c8dee2aaSAndroid Build Coastguard Worker        # scripts that create mocks, it is a convenient way to set up the verbosity
88*c8dee2aaSAndroid Build Coastguard Worker        # via the input api.
89*c8dee2aaSAndroid Build Coastguard Worker        self.verbose = '--verbose' in sys.argv
90*c8dee2aaSAndroid Build Coastguard Worker
91*c8dee2aaSAndroid Build Coastguard Worker    def CreateMockFileInPath(self, f_list):
92*c8dee2aaSAndroid Build Coastguard Worker        self.os_path.exists = lambda x: x in f_list
93*c8dee2aaSAndroid Build Coastguard Worker
94*c8dee2aaSAndroid Build Coastguard Worker    def AffectedFiles(self, file_filter=None, include_deletes=True):
95*c8dee2aaSAndroid Build Coastguard Worker        for file in self.files:
96*c8dee2aaSAndroid Build Coastguard Worker            if file_filter and not file_filter(file):
97*c8dee2aaSAndroid Build Coastguard Worker                continue
98*c8dee2aaSAndroid Build Coastguard Worker            if not include_deletes and file.Action() == 'D':
99*c8dee2aaSAndroid Build Coastguard Worker                continue
100*c8dee2aaSAndroid Build Coastguard Worker            yield file
101*c8dee2aaSAndroid Build Coastguard Worker
102*c8dee2aaSAndroid Build Coastguard Worker    def RightHandSideLines(self, source_file_filter=None):
103*c8dee2aaSAndroid Build Coastguard Worker        affected_files = self.AffectedSourceFiles(source_file_filter)
104*c8dee2aaSAndroid Build Coastguard Worker        for af in affected_files:
105*c8dee2aaSAndroid Build Coastguard Worker            lines = af.ChangedContents()
106*c8dee2aaSAndroid Build Coastguard Worker            for line in lines:
107*c8dee2aaSAndroid Build Coastguard Worker                yield (af, line[0], line[1])
108*c8dee2aaSAndroid Build Coastguard Worker
109*c8dee2aaSAndroid Build Coastguard Worker    def AffectedSourceFiles(self, file_filter=None):
110*c8dee2aaSAndroid Build Coastguard Worker        return self.AffectedFiles(file_filter=file_filter)
111*c8dee2aaSAndroid Build Coastguard Worker
112*c8dee2aaSAndroid Build Coastguard Worker    def FilterSourceFile(self, file,
113*c8dee2aaSAndroid Build Coastguard Worker                         files_to_check=(), files_to_skip=()):
114*c8dee2aaSAndroid Build Coastguard Worker        local_path = file.LocalPath()
115*c8dee2aaSAndroid Build Coastguard Worker        found_in_files_to_check = not files_to_check
116*c8dee2aaSAndroid Build Coastguard Worker        if files_to_check:
117*c8dee2aaSAndroid Build Coastguard Worker            if type(files_to_check) is str:
118*c8dee2aaSAndroid Build Coastguard Worker                raise TypeError(
119*c8dee2aaSAndroid Build Coastguard Worker                    'files_to_check should be an iterable of strings')
120*c8dee2aaSAndroid Build Coastguard Worker            for pattern in files_to_check:
121*c8dee2aaSAndroid Build Coastguard Worker                compiled_pattern = re.compile(pattern)
122*c8dee2aaSAndroid Build Coastguard Worker                if compiled_pattern.match(local_path):
123*c8dee2aaSAndroid Build Coastguard Worker                    found_in_files_to_check = True
124*c8dee2aaSAndroid Build Coastguard Worker                    break
125*c8dee2aaSAndroid Build Coastguard Worker        if files_to_skip:
126*c8dee2aaSAndroid Build Coastguard Worker            if type(files_to_skip) is str:
127*c8dee2aaSAndroid Build Coastguard Worker                raise TypeError(
128*c8dee2aaSAndroid Build Coastguard Worker                    'files_to_skip should be an iterable of strings')
129*c8dee2aaSAndroid Build Coastguard Worker            for pattern in files_to_skip:
130*c8dee2aaSAndroid Build Coastguard Worker                compiled_pattern = re.compile(pattern)
131*c8dee2aaSAndroid Build Coastguard Worker                if compiled_pattern.match(local_path):
132*c8dee2aaSAndroid Build Coastguard Worker                    return False
133*c8dee2aaSAndroid Build Coastguard Worker        return found_in_files_to_check
134*c8dee2aaSAndroid Build Coastguard Worker
135*c8dee2aaSAndroid Build Coastguard Worker    def LocalPaths(self):
136*c8dee2aaSAndroid Build Coastguard Worker        return [file.LocalPath() for file in self.files]
137*c8dee2aaSAndroid Build Coastguard Worker
138*c8dee2aaSAndroid Build Coastguard Worker    def PresubmitLocalPath(self):
139*c8dee2aaSAndroid Build Coastguard Worker        return self.presubmit_local_path
140*c8dee2aaSAndroid Build Coastguard Worker
141*c8dee2aaSAndroid Build Coastguard Worker    def ReadFile(self, filename, mode='r'):
142*c8dee2aaSAndroid Build Coastguard Worker        if hasattr(filename, 'AbsoluteLocalPath'):
143*c8dee2aaSAndroid Build Coastguard Worker            filename = filename.AbsoluteLocalPath()
144*c8dee2aaSAndroid Build Coastguard Worker        for file_ in self.files:
145*c8dee2aaSAndroid Build Coastguard Worker            if file_.LocalPath() == filename:
146*c8dee2aaSAndroid Build Coastguard Worker                return '\n'.join(file_.NewContents())
147*c8dee2aaSAndroid Build Coastguard Worker        # Otherwise, file is not in our mock API.
148*c8dee2aaSAndroid Build Coastguard Worker        raise IOError("No such file or directory: '%s'" % filename)
149*c8dee2aaSAndroid Build Coastguard Worker
150*c8dee2aaSAndroid Build Coastguard Worker
151*c8dee2aaSAndroid Build Coastguard Workerclass MockOutputApi(object):
152*c8dee2aaSAndroid Build Coastguard Worker    """Mock class for the OutputApi class.
153*c8dee2aaSAndroid Build Coastguard Worker
154*c8dee2aaSAndroid Build Coastguard Worker    An instance of this class can be passed to presubmit unittests for outputting
155*c8dee2aaSAndroid Build Coastguard Worker    various types of results.
156*c8dee2aaSAndroid Build Coastguard Worker    """
157*c8dee2aaSAndroid Build Coastguard Worker
158*c8dee2aaSAndroid Build Coastguard Worker    class PresubmitResult(object):
159*c8dee2aaSAndroid Build Coastguard Worker        def __init__(self, message, items=None, long_text=''):
160*c8dee2aaSAndroid Build Coastguard Worker            self.message = message
161*c8dee2aaSAndroid Build Coastguard Worker            self.items = items
162*c8dee2aaSAndroid Build Coastguard Worker            self.long_text = long_text
163*c8dee2aaSAndroid Build Coastguard Worker
164*c8dee2aaSAndroid Build Coastguard Worker        def __repr__(self):
165*c8dee2aaSAndroid Build Coastguard Worker            return self.message
166*c8dee2aaSAndroid Build Coastguard Worker
167*c8dee2aaSAndroid Build Coastguard Worker    class PresubmitError(PresubmitResult):
168*c8dee2aaSAndroid Build Coastguard Worker        def __init__(self, message, items=None, long_text=''):
169*c8dee2aaSAndroid Build Coastguard Worker            MockOutputApi.PresubmitResult.__init__(
170*c8dee2aaSAndroid Build Coastguard Worker                self, message, items, long_text)
171*c8dee2aaSAndroid Build Coastguard Worker            self.type = 'error'
172*c8dee2aaSAndroid Build Coastguard Worker
173*c8dee2aaSAndroid Build Coastguard Worker    class PresubmitPromptWarning(PresubmitResult):
174*c8dee2aaSAndroid Build Coastguard Worker        def __init__(self, message, items=None, long_text=''):
175*c8dee2aaSAndroid Build Coastguard Worker            MockOutputApi.PresubmitResult.__init__(
176*c8dee2aaSAndroid Build Coastguard Worker                self, message, items, long_text)
177*c8dee2aaSAndroid Build Coastguard Worker            self.type = 'warning'
178*c8dee2aaSAndroid Build Coastguard Worker
179*c8dee2aaSAndroid Build Coastguard Worker    class PresubmitNotifyResult(PresubmitResult):
180*c8dee2aaSAndroid Build Coastguard Worker        def __init__(self, message, items=None, long_text=''):
181*c8dee2aaSAndroid Build Coastguard Worker            MockOutputApi.PresubmitResult.__init__(
182*c8dee2aaSAndroid Build Coastguard Worker                self, message, items, long_text)
183*c8dee2aaSAndroid Build Coastguard Worker            self.type = 'notify'
184*c8dee2aaSAndroid Build Coastguard Worker
185*c8dee2aaSAndroid Build Coastguard Worker    class PresubmitPromptOrNotify(PresubmitResult):
186*c8dee2aaSAndroid Build Coastguard Worker        def __init__(self, message, items=None, long_text=''):
187*c8dee2aaSAndroid Build Coastguard Worker            MockOutputApi.PresubmitResult.__init__(
188*c8dee2aaSAndroid Build Coastguard Worker                self, message, items, long_text)
189*c8dee2aaSAndroid Build Coastguard Worker            self.type = 'promptOrNotify'
190*c8dee2aaSAndroid Build Coastguard Worker
191*c8dee2aaSAndroid Build Coastguard Worker    def __init__(self):
192*c8dee2aaSAndroid Build Coastguard Worker        self.more_cc = []
193*c8dee2aaSAndroid Build Coastguard Worker
194*c8dee2aaSAndroid Build Coastguard Worker    def AppendCC(self, more_cc):
195*c8dee2aaSAndroid Build Coastguard Worker        self.more_cc.append(more_cc)
196*c8dee2aaSAndroid Build Coastguard Worker
197*c8dee2aaSAndroid Build Coastguard Worker
198*c8dee2aaSAndroid Build Coastguard Workerclass MockFile(object):
199*c8dee2aaSAndroid Build Coastguard Worker    """Mock class for the File class.
200*c8dee2aaSAndroid Build Coastguard Worker
201*c8dee2aaSAndroid Build Coastguard Worker    This class can be used to form the mock list of changed files in
202*c8dee2aaSAndroid Build Coastguard Worker    MockInputApi for presubmit unittests.
203*c8dee2aaSAndroid Build Coastguard Worker    """
204*c8dee2aaSAndroid Build Coastguard Worker
205*c8dee2aaSAndroid Build Coastguard Worker    def __init__(self, local_path, new_contents, old_contents=None, action='A',
206*c8dee2aaSAndroid Build Coastguard Worker                 scm_diff=None):
207*c8dee2aaSAndroid Build Coastguard Worker        self._local_path = local_path
208*c8dee2aaSAndroid Build Coastguard Worker        self._new_contents = new_contents
209*c8dee2aaSAndroid Build Coastguard Worker        self._changed_contents = [(i + 1, l)
210*c8dee2aaSAndroid Build Coastguard Worker                                  for i, l in enumerate(new_contents)]
211*c8dee2aaSAndroid Build Coastguard Worker        self._action = action
212*c8dee2aaSAndroid Build Coastguard Worker        if scm_diff:
213*c8dee2aaSAndroid Build Coastguard Worker            self._scm_diff = scm_diff
214*c8dee2aaSAndroid Build Coastguard Worker        else:
215*c8dee2aaSAndroid Build Coastguard Worker            self._scm_diff = (
216*c8dee2aaSAndroid Build Coastguard Worker                "--- /dev/null\n+++ %s\n@@ -0,0 +1,%d @@\n" %
217*c8dee2aaSAndroid Build Coastguard Worker                (local_path, len(new_contents)))
218*c8dee2aaSAndroid Build Coastguard Worker            for l in new_contents:
219*c8dee2aaSAndroid Build Coastguard Worker                self._scm_diff += "+%s\n" % l
220*c8dee2aaSAndroid Build Coastguard Worker        self._old_contents = old_contents
221*c8dee2aaSAndroid Build Coastguard Worker
222*c8dee2aaSAndroid Build Coastguard Worker    def Action(self):
223*c8dee2aaSAndroid Build Coastguard Worker        return self._action
224*c8dee2aaSAndroid Build Coastguard Worker
225*c8dee2aaSAndroid Build Coastguard Worker    def ChangedContents(self):
226*c8dee2aaSAndroid Build Coastguard Worker        return self._changed_contents
227*c8dee2aaSAndroid Build Coastguard Worker
228*c8dee2aaSAndroid Build Coastguard Worker    def NewContents(self, flush_cache=False):
229*c8dee2aaSAndroid Build Coastguard Worker        return self._new_contents
230*c8dee2aaSAndroid Build Coastguard Worker
231*c8dee2aaSAndroid Build Coastguard Worker    def LocalPath(self):
232*c8dee2aaSAndroid Build Coastguard Worker        return self._local_path
233*c8dee2aaSAndroid Build Coastguard Worker
234*c8dee2aaSAndroid Build Coastguard Worker    def AbsoluteLocalPath(self):
235*c8dee2aaSAndroid Build Coastguard Worker        return self._local_path
236*c8dee2aaSAndroid Build Coastguard Worker
237*c8dee2aaSAndroid Build Coastguard Worker    def GenerateScmDiff(self):
238*c8dee2aaSAndroid Build Coastguard Worker        return self._scm_diff
239*c8dee2aaSAndroid Build Coastguard Worker
240*c8dee2aaSAndroid Build Coastguard Worker    def OldContents(self):
241*c8dee2aaSAndroid Build Coastguard Worker        return self._old_contents
242*c8dee2aaSAndroid Build Coastguard Worker
243*c8dee2aaSAndroid Build Coastguard Worker    def rfind(self, p):
244*c8dee2aaSAndroid Build Coastguard Worker        """os.path.basename is called on MockFile so we need an rfind method."""
245*c8dee2aaSAndroid Build Coastguard Worker        return self._local_path.rfind(p)
246*c8dee2aaSAndroid Build Coastguard Worker
247*c8dee2aaSAndroid Build Coastguard Worker    def __getitem__(self, i):
248*c8dee2aaSAndroid Build Coastguard Worker        """os.path.basename is called on MockFile so we need a get method."""
249*c8dee2aaSAndroid Build Coastguard Worker        return self._local_path[i]
250*c8dee2aaSAndroid Build Coastguard Worker
251*c8dee2aaSAndroid Build Coastguard Worker    def __len__(self):
252*c8dee2aaSAndroid Build Coastguard Worker        """os.path.basename is called on MockFile so we need a len method."""
253*c8dee2aaSAndroid Build Coastguard Worker        return len(self._local_path)
254*c8dee2aaSAndroid Build Coastguard Worker
255*c8dee2aaSAndroid Build Coastguard Worker    def replace(self, altsep, sep):
256*c8dee2aaSAndroid Build Coastguard Worker        """os.path.basename is called on MockFile so we need a replace method."""
257*c8dee2aaSAndroid Build Coastguard Worker        return self._local_path.replace(altsep, sep)
258*c8dee2aaSAndroid Build Coastguard Worker
259*c8dee2aaSAndroid Build Coastguard Worker
260*c8dee2aaSAndroid Build Coastguard Workerclass MockAffectedFile(MockFile):
261*c8dee2aaSAndroid Build Coastguard Worker    def AbsoluteLocalPath(self):
262*c8dee2aaSAndroid Build Coastguard Worker        return self._local_path
263*c8dee2aaSAndroid Build Coastguard Worker
264*c8dee2aaSAndroid Build Coastguard Worker
265*c8dee2aaSAndroid Build Coastguard Workerclass MockChange(object):
266*c8dee2aaSAndroid Build Coastguard Worker    """Mock class for Change class.
267*c8dee2aaSAndroid Build Coastguard Worker
268*c8dee2aaSAndroid Build Coastguard Worker    This class can be used in presubmit unittests to mock the query of the
269*c8dee2aaSAndroid Build Coastguard Worker    current change.
270*c8dee2aaSAndroid Build Coastguard Worker    """
271*c8dee2aaSAndroid Build Coastguard Worker
272*c8dee2aaSAndroid Build Coastguard Worker    def __init__(self, changed_files):
273*c8dee2aaSAndroid Build Coastguard Worker        self._changed_files = changed_files
274*c8dee2aaSAndroid Build Coastguard Worker        self.author_email = None
275*c8dee2aaSAndroid Build Coastguard Worker        self.footers = defaultdict(list)
276*c8dee2aaSAndroid Build Coastguard Worker
277*c8dee2aaSAndroid Build Coastguard Worker    def LocalPaths(self):
278*c8dee2aaSAndroid Build Coastguard Worker        return self._changed_files
279*c8dee2aaSAndroid Build Coastguard Worker
280*c8dee2aaSAndroid Build Coastguard Worker    def AffectedFiles(self, include_dirs=False, include_deletes=True,
281*c8dee2aaSAndroid Build Coastguard Worker                      file_filter=None):
282*c8dee2aaSAndroid Build Coastguard Worker        return self._changed_files
283*c8dee2aaSAndroid Build Coastguard Worker
284*c8dee2aaSAndroid Build Coastguard Worker    def GitFootersFromDescription(self):
285*c8dee2aaSAndroid Build Coastguard Worker        return self.footers
286