xref: /aosp_15_r20/external/angle/scripts/angle_presubmit_utils_unittest.py (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1#!/usr/bin/env python3
2# Copyright 2020 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5"""
6angle_presubmit_utils_unittest.py: Top-level unittest script for ANGLE presubmit checks.
7"""
8
9import importlib.machinery
10import os
11import pathlib
12import sys
13import tempfile
14import unittest
15from angle_presubmit_utils import *
16
17def SetCWDToAngleFolder():
18    angle_folder = "angle"
19    cwd = os.path.dirname(os.path.abspath(__file__))
20    cwd = cwd.split(angle_folder)[0] + angle_folder
21    os.chdir(cwd)
22
23
24SetCWDToAngleFolder()
25
26loader = importlib.machinery.SourceFileLoader('PRESUBMIT', 'PRESUBMIT.py')
27PRESUBMIT = loader.load_module()
28
29
30class CommitMessageFormattingCheckTest(unittest.TestCase):
31
32    def __init__(self, *args, **kwargs):
33        super(CommitMessageFormattingCheckTest, self).__init__(*args, **kwargs)
34        self.output_api = OutputAPI_mock()
35
36    def run_check_commit_message_formatting(self, commit_msg):
37        input_api = InputAPI_mock(commit_msg)
38        return PRESUBMIT._CheckCommitMessageFormatting(input_api, self.output_api)
39
40    def test_correct_commit_message(self):
41        commit_msg = """a
42
43b
44
45Bug: angleproject:4662
46Change-Id: I966c79d96175da9eee92ef6da20db50d488137b2
47"""
48        errors = self.run_check_commit_message_formatting(commit_msg)
49        self.assertEqual(len(errors), 0)
50
51    def test_missing_description_body_and_description_summary(self):
52        commit_msg = """Change-Id: I966c79d96175da9eee92ef6da20db50d488137b2"""
53        errors = self.run_check_commit_message_formatting(commit_msg)
54        self.assertEqual(len(errors), 1)
55        self.assertEqual(
56            errors[0],
57            self.output_api.PresubmitError(
58                "Commit 1:Please ensure that your" +
59                " description summary and description body are not blank."))
60
61    def test_missing_description_body(self):
62        commit_msg = """
63        a
64
65b: d
66c: e
67"""
68        errors = self.run_check_commit_message_formatting(commit_msg)
69        self.assertEqual(len(errors), 0)
70
71    def test_missing_tag_paragraph(self):
72        commit_msg = """a
73
74bd
75efgh"""
76        errors = self.run_check_commit_message_formatting(commit_msg)
77        self.assertEqual(len(errors), 1)
78        self.assertEqual(
79            errors[0],
80            self.output_api.PresubmitError(
81                "Commit 1:Please ensure that there are tags (e.g., Bug:, Test:) in your description."
82            ))
83
84    def test_missing_tag_paragraph_and_description_body(self):
85        commit_msg = "a"
86        errors = self.run_check_commit_message_formatting(commit_msg)
87        self.assertEqual(len(errors), 1)
88        self.assertEqual(
89            errors[0],
90            self.output_api.PresubmitError(
91                "Commit 1:Please ensure that there are tags (e.g., Bug:, Test:) in your description."
92            ))
93
94    def test_missing_blank_line_between_description_summary_and_description_body(self):
95        commit_msg = """a
96b
97
98Change-Id: I925cdb45779a9cdebe4e14f9e81e4211ade37c12"""
99        errors = self.run_check_commit_message_formatting(commit_msg)
100        self.assertEqual(len(errors), 1)
101        self.assertEqual(errors[0], self.output_api.PresubmitError(
102          "Commit 1:Please ensure the summary is only 1 line and there is 1 blank line" + \
103          " between the summary and description body."))
104
105    def test_missing_blank_line_between_description_body_and_tags_paragraph(self):
106        commit_msg = """a
107
108b
109Change-Id: I925cdb45779a9cdebe4e14f9e81e4211ade37c12"""
110        errors = self.run_check_commit_message_formatting(commit_msg)
111        self.assertEqual(len(errors), 0)
112
113    def test_multiple_blank_lines_before_and_after_commit_message(self):
114        commit_msg = """
115
116
117                a
118
119                  b
120
121Change-Id: I925cdb45779a9cdebe4e14f9e81e4211ade37c12
122"""
123        errors = self.run_check_commit_message_formatting(commit_msg)
124        self.assertEqual(len(errors), 0)
125
126    def test_newlines_within_description_body(self):
127        commit_msg = """a
128
129b
130
131d
132
133e
134
135for
136
137Change-Id: I443c36aaa8956c20da1abddf7aea613659e2cd5b"""
138        errors = self.run_check_commit_message_formatting(commit_msg)
139        self.assertEqual(len(errors), 0)
140
141    # Summary description in warning threshold(at 65 characters)
142    def test_summmary_description_in_warning_thresholds(self):
143        commit_msg = """aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
144
145b
146
147Change-Id: I925cdb45779a9cdebe4e14f9e81e4211ade37c12
148"""
149        errors = self.run_check_commit_message_formatting(commit_msg)
150        self.assertEqual(len(errors), 1)
151        self.assertEqual(
152            errors[0],
153            self.output_api.PresubmitPromptWarning(
154                "Commit 1:Your description summary should be on one line of 64 or less characters."
155            ))
156
157    # Summary description in error threshold(at 71 characters)
158    def test_summary_description_in_error_threshold(self):
159        commit_msg = """aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
160
161b
162
163Change-Id: I925cdb45779a9cdebe4e14f9e81e4211ade37c12"""
164        errors = self.run_check_commit_message_formatting(commit_msg)
165        self.assertEqual(len(errors), 1)
166        self.assertEqual(
167            errors[0],
168            self.output_api.PresubmitError(
169                "Commit 1:Please ensure that your description summary is on one line of 64 or less characters."
170            ))
171
172    def test_description_body_exceeds_line_count_limit(self):
173        commit_msg = """a
174
175bbbbbbbb bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
176
177
178Change-Id: I443c36aaa8956c20da1abddf7aea613659e2cd5b"""
179        errors = self.run_check_commit_message_formatting(commit_msg)
180        self.assertEqual(len(errors), 2)
181        self.assertEqual(
182            errors[0],
183            self.output_api.PresubmitError(
184                "Commit 1:Please ensure that there exists only 1 blank line between tags and description body."
185            ))
186        self.assertEqual(
187            errors[1],
188            self.output_api.PresubmitError("""Commit 1:Line 3 is too long.
189"bbbbbbbb bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
190Please wrap it to 72 characters. Lines without spaces or lines starting with 4 spaces are exempt."""
191                                          ))
192
193    def test_description_body_exceeds_line_count_limit_but_with_4_spaces_prefix(self):
194        commit_msg = """a
195
196cc
197
198dddd
199
200    bbbbbbbbbbbbbbbbbbbbbbb bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
201
202Change-Id: I443c36aaa8956c20da1abddf7aea613659e2cd5b"""
203        errors = self.run_check_commit_message_formatting(commit_msg)
204        self.assertEqual(len(errors), 0)
205
206    def test_description_body_exceeds_line_count_limit_but_without_space(self):
207        commit_msg = """a
208
209cc
210
211dddd
212
213bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
214
215a: d"""
216        errors = self.run_check_commit_message_formatting(commit_msg)
217        self.assertEqual(len(errors), 0)
218
219    def test_description_body_exceeds_line_count_limit_but_in_quote(self):
220        commit_msg = """a
221
222cc
223
224dddd
225
226> bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
227
228a: d"""
229        errors = self.run_check_commit_message_formatting(commit_msg)
230        self.assertEqual(len(errors), 0)
231
232    def test_tabs_in_commit_message(self):
233        commit_msg = """																a
234
235bbbbbbbbbbbbbbbbbbbb
236
237Change-Id: I443c36aaa8956c20da1abddf7aea613659e2cd5b"""
238        errors = self.run_check_commit_message_formatting(commit_msg)
239        self.assertEqual(len(errors), 1)
240        self.assertEqual(
241            errors[0],
242            self.output_api.PresubmitError("Commit 1:Tabs are not allowed in commit message."))
243
244    def test_allowlist_revert(self):
245        commit_msg = """Revert "sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssa
246
247bbbbbbbbbbbbbbbbbbbb
248
249Change-Id: I443c36aaa8956c20da1abddf7aea613659e2cd5b"""
250        errors = self.run_check_commit_message_formatting(commit_msg)
251        self.assertEqual(len(errors), 0)
252
253    def test_allowlist_roll(self):
254        commit_msg = """Roll sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssadd
255
256bbbbbbbbbbbbbbbbbbbb
257
258Change-Id: I443c36aaa8956c20da1abddf7aea613659e2cd5b"""
259        errors = self.run_check_commit_message_formatting(commit_msg)
260        self.assertEqual(len(errors), 0)
261
262    def test_allowlist_reland(self):
263        commit_msg = """Reland sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssadd
264
265bbbbbbbbbbbbbbbbbbbb
266
267Change-Id: I443c36aaa8956c20da1abddf7aea613659e2cd5b"""
268        errors = self.run_check_commit_message_formatting(commit_msg)
269        self.assertEqual(len(errors), 0)
270
271    def test_allowlist_reland2(self):
272        commit_msg = """Reland: sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssadd
273
274bbbbbbbbbbbbbbbbbbbb
275
276Change-Id: I443c36aaa8956c20da1abddf7aea613659e2cd5b"""
277        errors = self.run_check_commit_message_formatting(commit_msg)
278        self.assertEqual(len(errors), 0)
279
280    def test_multiple_commits_with_errors_in_multiple_commits(self):
281        commit_msg = """a
282
283bbbbbbbb bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
284
285Change-Id: I443c36aaa8956c20da1abddf7aea613659e2cd5b
286
287a
288
289cccccccccccccccccccccccccccccc cccccccccccccccccccccccccccccccccccccccccccc
290
291Change-Id: I443c36aaa8956c20da1abddf7aea613659e2cd5b"""
292        errors = self.run_check_commit_message_formatting(commit_msg)
293        self.assertEqual(len(errors), 2)
294        self.assertEqual(
295            errors[0],
296            self.output_api.PresubmitError("""Commit 2:Line 3 is too long.
297"bbbbbbbb bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
298Please wrap it to 72 characters. Lines without spaces or lines starting with 4 spaces are exempt."""
299                                          ))
300        self.assertEqual(
301            errors[1],
302            self.output_api.PresubmitError("""Commit 1:Line 4 is too long.
303"cccccccccccccccccccccccccccccc cccccccccccccccccccccccccccccccccccccccccccc"
304Please wrap it to 72 characters. Lines without spaces or lines starting with 4 spaces are exempt."""
305                                          ))
306
307    def test_multiple_commits_with_error_in_one_commit(self):
308        commit_msg = """a
309
310bbbbbbbb bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
311
312Change-Id: I443c36aaa8956c20da1abddf7aea613659e2cd5b
313
314Roll sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssadd
315
316bbbbbbbbbbbbbbbbbbbb
317
318Change-Id: I443c36aaa8956c20da1abddf7aea613659e2cd5b"""
319        errors = self.run_check_commit_message_formatting(commit_msg)
320        self.assertEqual(len(errors), 1)
321        self.assertEqual(
322            errors[0],
323            self.output_api.PresubmitError("""Commit 2:Line 3 is too long.
324"bbbbbbbb bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
325Please wrap it to 72 characters. Lines without spaces or lines starting with 4 spaces are exempt."""
326                                          ))
327
328    def test_multiple_commits_with_no_error(self):
329        commit_msg = """Reland sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssadd
330
331bbbbbbbbbbbbbbbbbbbb
332
333Change-Id: I443c36aaa8956c20da1abddf7aea613659e2cd5b
334
335Roll sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssadd
336
337bbbbbbbbbbbbbbbbbbbb
338
339Change-Id: I443c36aaa8956c20da1abddf7aea613659e2cd5b"""
340        errors = self.run_check_commit_message_formatting(commit_msg)
341        self.assertEqual(len(errors), 0)
342
343
344class GClientFileExistenceCheck(unittest.TestCase):
345
346    def __init__(self, *args, **kwargs):
347        super(GClientFileExistenceCheck, self).__init__(*args, **kwargs)
348        self.output_api = OutputAPI_mock()
349
350    def run_gclient_presubmit(self, cwd, search_limit):
351        input_api = InputAPI_mock('')
352        input_api.cwd = cwd
353        return PRESUBMIT._CheckGClientExists(input_api, self.output_api,
354                                             pathlib.Path(search_limit))
355
356    def test_gclient_in_cwd(self):
357        with tempfile.TemporaryDirectory() as cwd:
358            pathlib.Path(cwd).joinpath('.gclient').touch()
359            errors = self.run_gclient_presubmit(cwd, cwd)
360            self.assertEqual(len(errors), 0)
361
362    def test_missing_gclient(self):
363        with tempfile.TemporaryDirectory() as cwd:
364            errors = self.run_gclient_presubmit(cwd, cwd)
365            self.assertEqual(len(errors), 1)
366            self.assertEqual(errors[0], self.output_api.PresubmitError('Missing .gclient file.'))
367
368    def test_gclient_in_parent(self):
369        with tempfile.TemporaryDirectory() as cwd:
370            cwd = pathlib.Path(cwd)
371            cwd.joinpath('.gclient').touch()
372            inner = cwd.joinpath('inner')
373            inner.mkdir()
374            errors = self.run_gclient_presubmit(str(inner), str(cwd))
375            self.assertEqual(len(errors), 0)
376
377
378class CheckShaderVersionInShaderLangHeaderTest(unittest.TestCase):
379
380    def __init__(self, *args, **kwargs):
381        super(CheckShaderVersionInShaderLangHeaderTest, self).__init__(*args, **kwargs)
382        self.output_api = OutputAPI_mock()
383
384    def run_shader_version_check_presubmit(self, commit_msg, diffs):
385        affected_files = [AffectedFile_mock(diff) for diff in diffs]
386        input_api = InputAPI_mock(commit_msg, affected_files)
387        return PRESUBMIT._CheckShaderVersionInShaderLangHeader(input_api, self.output_api)
388
389    def test_headers_not_changed(self):
390        errors = self.run_shader_version_check_presubmit('', [])
391        self.assertEqual(len(errors), 0)
392
393    def test_shader_lang_changed_with_version_change(self):
394        shader_lang_diff = """-#define ANGLE_SH_VERSION 100
395+#define ANGLE_SH_VERSION 101
396"""
397
398        errors = self.run_shader_version_check_presubmit('', [shader_lang_diff])
399        self.assertEqual(len(errors), 0)
400
401    def test_both_changed_with_version_change(self):
402        shader_lang_diff = """-#define ANGLE_SH_VERSION 100
403+#define ANGLE_SH_VERSION 101
404"""
405        shader_vars_diff = """-any change"""
406
407        errors = self.run_shader_version_check_presubmit('', [shader_lang_diff, shader_vars_diff])
408        self.assertEqual(len(errors), 0)
409
410    def test_shader_lang_changed_with_no_version_change(self):
411        shader_lang_diff = """+some change"""
412
413        errors = self.run_shader_version_check_presubmit('', [shader_lang_diff])
414        self.assertEqual(len(errors), 1)
415        self.assertEqual(
416            errors[0],
417            self.output_api.PresubmitError(
418                'ANGLE_SH_VERSION should be incremented when ShaderLang.h or ShaderVars.h change.')
419        )
420
421    def test_shader_lang_changed_with_no_version_change(self):
422        shader_lang_diff = """+some change
423 #define ANGLE_SH_VERSION 100
424-other changes"""
425
426        errors = self.run_shader_version_check_presubmit('', [shader_lang_diff])
427        self.assertEqual(len(errors), 1)
428        self.assertEqual(
429            errors[0],
430            self.output_api.PresubmitError(
431                'ANGLE_SH_VERSION should be incremented when ShaderLang.h or ShaderVars.h change.')
432        )
433
434    def test_shader_lang_changed_with_version_cosmetic_change(self):
435        shader_lang_diff = """-#define ANGLE_SH_VERSION 100
436+#define ANGLE_SH_VERSION 100 // cosmetic change
437"""
438
439        errors = self.run_shader_version_check_presubmit('', [shader_lang_diff])
440        self.assertEqual(len(errors), 1)
441        self.assertEqual(
442            errors[0],
443            self.output_api.PresubmitError(
444                'ANGLE_SH_VERSION should be incremented when ShaderLang.h or ShaderVars.h change.')
445        )
446
447    def test_shader_lang_changed_with_version_decrement(self):
448        shader_lang_diff = """-#define ANGLE_SH_VERSION 100
449+#define ANGLE_SH_VERSION 99
450"""
451
452        errors = self.run_shader_version_check_presubmit('', [shader_lang_diff])
453        self.assertEqual(len(errors), 1)
454        self.assertEqual(
455            errors[0],
456            self.output_api.PresubmitError(
457                'ANGLE_SH_VERSION should be incremented when ShaderLang.h or ShaderVars.h change.')
458        )
459
460    def test_shader_lang_changed_in_revert(self):
461        shader_lang_diff = """-#define ANGLE_SH_VERSION 100
462+#define ANGLE_SH_VERSION 99
463"""
464
465        errors = self.run_shader_version_check_presubmit('Revert some change', [shader_lang_diff])
466        self.assertEqual(len(errors), 0)
467
468
469if __name__ == '__main__':
470    unittest.main()
471