blob: 5e3fbe86da5928140282e06b751f84d4490702ca [file] [edit]
#!/usr/bin/env python3
# Copyright 2020 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""
angle_presubmit_utils_unittest.py: Top-level unittest script for ANGLE presubmit checks.
"""
import importlib.machinery
import os
import pathlib
import sys
import tempfile
import unittest
from angle_presubmit_utils import *
def SetCWDToAngleFolder():
angle_folder = pathlib.Path(__file__).resolve().parents[1]
os.chdir(angle_folder)
SetCWDToAngleFolder()
loader = importlib.machinery.SourceFileLoader('PRESUBMIT', 'PRESUBMIT.py')
PRESUBMIT = loader.load_module()
class CommitMessageFormattingCheckTest(unittest.TestCase):
def __init__(self, *args, **kwargs):
super(CommitMessageFormattingCheckTest, self).__init__(*args, **kwargs)
self.output_api = OutputAPI_mock()
def run_check_commit_message_formatting(self, commit_msg):
input_api = InputAPI_mock(commit_msg)
return PRESUBMIT._CheckCommitMessageFormatting(input_api, self.output_api)
def test_correct_commit_message(self):
commit_msg = """a
b
Bug: angleproject:42263262
Change-Id: I966c79d96175da9eee92ef6da20db50d488137b2
"""
errors = self.run_check_commit_message_formatting(commit_msg)
self.assertEqual(len(errors), 0)
def test_missing_description_body_and_description_summary(self):
commit_msg = """Change-Id: I966c79d96175da9eee92ef6da20db50d488137b2"""
errors = self.run_check_commit_message_formatting(commit_msg)
self.assertEqual(len(errors), 1)
self.assertEqual(
errors[0],
self.output_api.PresubmitError(
"Commit 1:Please ensure that your" +
" description summary and description body are not blank."))
def test_missing_description_body(self):
commit_msg = """
a
b: d
c: e
"""
errors = self.run_check_commit_message_formatting(commit_msg)
self.assertEqual(len(errors), 0)
def test_missing_tag_paragraph(self):
commit_msg = """a
bd
efgh"""
errors = self.run_check_commit_message_formatting(commit_msg)
self.assertEqual(len(errors), 1)
self.assertEqual(
errors[0],
self.output_api.PresubmitError(
"Commit 1:Please ensure that there are tags (e.g., Bug:, Test:) in your description."
))
def test_missing_tag_paragraph_and_description_body(self):
commit_msg = "a"
errors = self.run_check_commit_message_formatting(commit_msg)
self.assertEqual(len(errors), 1)
self.assertEqual(
errors[0],
self.output_api.PresubmitError(
"Commit 1:Please ensure that there are tags (e.g., Bug:, Test:) in your description."
))
def test_missing_blank_line_between_description_summary_and_description_body(self):
commit_msg = """a
b
Change-Id: I925cdb45779a9cdebe4e14f9e81e4211ade37c12"""
errors = self.run_check_commit_message_formatting(commit_msg)
self.assertEqual(len(errors), 1)
self.assertEqual(errors[0], self.output_api.PresubmitError(
"Commit 1:Please ensure the summary is only 1 line and there is 1 blank line" + \
" between the summary and description body."))
def test_missing_blank_line_between_description_body_and_tags_paragraph(self):
commit_msg = """a
b
Change-Id: I925cdb45779a9cdebe4e14f9e81e4211ade37c12"""
errors = self.run_check_commit_message_formatting(commit_msg)
self.assertEqual(len(errors), 0)
def test_multiple_blank_lines_before_and_after_commit_message(self):
commit_msg = """
a
b
Change-Id: I925cdb45779a9cdebe4e14f9e81e4211ade37c12
"""
errors = self.run_check_commit_message_formatting(commit_msg)
self.assertEqual(len(errors), 0)
def test_newlines_within_description_body(self):
commit_msg = """a
b
d
e
for
Change-Id: I443c36aaa8956c20da1abddf7aea613659e2cd5b"""
errors = self.run_check_commit_message_formatting(commit_msg)
self.assertEqual(len(errors), 0)
# Summary description in warning threshold(at 65 characters)
def test_summmary_description_in_warning_thresholds(self):
commit_msg = """aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
b
Change-Id: I925cdb45779a9cdebe4e14f9e81e4211ade37c12
"""
errors = self.run_check_commit_message_formatting(commit_msg)
self.assertEqual(len(errors), 1)
self.assertEqual(
errors[0],
self.output_api.PresubmitPromptWarning(
"Commit 1:Your description summary should be on one line of 64 or less characters."
))
# Summary description in error threshold(at 71 characters)
def test_summary_description_in_error_threshold(self):
commit_msg = """aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
b
Change-Id: I925cdb45779a9cdebe4e14f9e81e4211ade37c12"""
errors = self.run_check_commit_message_formatting(commit_msg)
self.assertEqual(len(errors), 1)
self.assertEqual(
errors[0],
self.output_api.PresubmitError(
"Commit 1:Please ensure that your description summary is on one line of 64 or less characters."
))
def test_description_body_exceeds_line_count_limit(self):
commit_msg = """a
bbbbbbbb bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
Change-Id: I443c36aaa8956c20da1abddf7aea613659e2cd5b"""
errors = self.run_check_commit_message_formatting(commit_msg)
self.assertEqual(len(errors), 2)
self.assertEqual(
errors[0],
self.output_api.PresubmitError(
"Commit 1:Please ensure that there exists only 1 blank line between tags and description body."
))
self.assertEqual(
errors[1],
self.output_api.PresubmitError("""Commit 1:Line 3 is too long.
"bbbbbbbb bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
Please wrap it to 72 characters. Lines without spaces or lines starting with 4 spaces are exempt."""
))
def test_description_body_exceeds_line_count_limit_but_with_4_spaces_prefix(self):
commit_msg = """a
cc
dddd
bbbbbbbbbbbbbbbbbbbbbbb bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
Change-Id: I443c36aaa8956c20da1abddf7aea613659e2cd5b"""
errors = self.run_check_commit_message_formatting(commit_msg)
self.assertEqual(len(errors), 0)
def test_description_body_exceeds_line_count_limit_but_without_space(self):
commit_msg = """a
cc
dddd
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
a: d"""
errors = self.run_check_commit_message_formatting(commit_msg)
self.assertEqual(len(errors), 0)
def test_description_body_exceeds_line_count_limit_but_in_quote(self):
commit_msg = """a
cc
dddd
> bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
a: d"""
errors = self.run_check_commit_message_formatting(commit_msg)
self.assertEqual(len(errors), 0)
def test_tabs_in_commit_message(self):
commit_msg = """ a
bbbbbbbbbbbbbbbbbbbb
Change-Id: I443c36aaa8956c20da1abddf7aea613659e2cd5b"""
errors = self.run_check_commit_message_formatting(commit_msg)
self.assertEqual(len(errors), 1)
self.assertEqual(
errors[0],
self.output_api.PresubmitError("Commit 1:Tabs are not allowed in commit message."))
def test_allowlist_revert(self):
commit_msg = """Revert "sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssa
bbbbbbbbbbbbbbbbbbbb
Change-Id: I443c36aaa8956c20da1abddf7aea613659e2cd5b"""
errors = self.run_check_commit_message_formatting(commit_msg)
self.assertEqual(len(errors), 0)
def test_allowlist_roll(self):
commit_msg = """Roll sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssadd
bbbbbbbbbbbbbbbbbbbb
Change-Id: I443c36aaa8956c20da1abddf7aea613659e2cd5b"""
errors = self.run_check_commit_message_formatting(commit_msg)
self.assertEqual(len(errors), 0)
def test_allowlist_reland(self):
commit_msg = """Reland sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssadd
bbbbbbbbbbbbbbbbbbbb
Change-Id: I443c36aaa8956c20da1abddf7aea613659e2cd5b"""
errors = self.run_check_commit_message_formatting(commit_msg)
self.assertEqual(len(errors), 0)
def test_allowlist_reland2(self):
commit_msg = """Reland: sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssadd
bbbbbbbbbbbbbbbbbbbb
Change-Id: I443c36aaa8956c20da1abddf7aea613659e2cd5b"""
errors = self.run_check_commit_message_formatting(commit_msg)
self.assertEqual(len(errors), 0)
def test_multiple_commits_with_errors_in_multiple_commits(self):
commit_msg = """a
bbbbbbbb bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
Change-Id: I443c36aaa8956c20da1abddf7aea613659e2cd5b
a
cccccccccccccccccccccccccccccc cccccccccccccccccccccccccccccccccccccccccccc
Change-Id: I443c36aaa8956c20da1abddf7aea613659e2cd5b"""
errors = self.run_check_commit_message_formatting(commit_msg)
self.assertEqual(len(errors), 2)
self.assertEqual(
errors[0],
self.output_api.PresubmitError("""Commit 2:Line 3 is too long.
"bbbbbbbb bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
Please wrap it to 72 characters. Lines without spaces or lines starting with 4 spaces are exempt."""
))
self.assertEqual(
errors[1],
self.output_api.PresubmitError("""Commit 1:Line 4 is too long.
"cccccccccccccccccccccccccccccc cccccccccccccccccccccccccccccccccccccccccccc"
Please wrap it to 72 characters. Lines without spaces or lines starting with 4 spaces are exempt."""
))
def test_multiple_commits_with_error_in_one_commit(self):
commit_msg = """a
bbbbbbbb bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
Change-Id: I443c36aaa8956c20da1abddf7aea613659e2cd5b
Roll sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssadd
bbbbbbbbbbbbbbbbbbbb
Change-Id: I443c36aaa8956c20da1abddf7aea613659e2cd5b"""
errors = self.run_check_commit_message_formatting(commit_msg)
self.assertEqual(len(errors), 1)
self.assertEqual(
errors[0],
self.output_api.PresubmitError("""Commit 2:Line 3 is too long.
"bbbbbbbb bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
Please wrap it to 72 characters. Lines without spaces or lines starting with 4 spaces are exempt."""
))
def test_multiple_commits_with_no_error(self):
commit_msg = """Reland sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssadd
bbbbbbbbbbbbbbbbbbbb
Change-Id: I443c36aaa8956c20da1abddf7aea613659e2cd5b
Roll sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssadd
bbbbbbbbbbbbbbbbbbbb
Change-Id: I443c36aaa8956c20da1abddf7aea613659e2cd5b"""
errors = self.run_check_commit_message_formatting(commit_msg)
self.assertEqual(len(errors), 0)
class GClientFileExistenceCheck(unittest.TestCase):
def __init__(self, *args, **kwargs):
super(GClientFileExistenceCheck, self).__init__(*args, **kwargs)
self.output_api = OutputAPI_mock()
def run_gclient_presubmit(self, cwd, search_limit):
input_api = InputAPI_mock('')
input_api.cwd = cwd
return PRESUBMIT._CheckGClientExists(input_api, self.output_api,
pathlib.Path(search_limit))
def test_gclient_in_cwd(self):
with tempfile.TemporaryDirectory() as cwd:
pathlib.Path(cwd).joinpath('.gclient').touch()
errors = self.run_gclient_presubmit(cwd, cwd)
self.assertEqual(len(errors), 0)
def test_missing_gclient(self):
with tempfile.TemporaryDirectory() as cwd:
errors = self.run_gclient_presubmit(cwd, cwd)
self.assertEqual(len(errors), 1)
self.assertEqual(errors[0], self.output_api.PresubmitError('Missing .gclient file.'))
def test_gclient_in_parent(self):
with tempfile.TemporaryDirectory() as cwd:
cwd = pathlib.Path(cwd)
cwd.joinpath('.gclient').touch()
inner = cwd.joinpath('inner')
inner.mkdir()
errors = self.run_gclient_presubmit(str(inner), str(cwd))
self.assertEqual(len(errors), 0)
class CheckShaderVersionInShaderLangHeaderTest(unittest.TestCase):
def __init__(self, *args, **kwargs):
super(CheckShaderVersionInShaderLangHeaderTest, self).__init__(*args, **kwargs)
self.output_api = OutputAPI_mock()
def run_shader_version_check_presubmit(self, commit_msg, diffs):
affected_files = [AffectedFile_mock(diff) for diff in diffs]
input_api = InputAPI_mock(commit_msg, affected_files)
return PRESUBMIT._CheckShaderVersionInShaderLangHeader(input_api, self.output_api)
def test_headers_not_changed(self):
errors = self.run_shader_version_check_presubmit('', [])
self.assertEqual(len(errors), 0)
def test_shader_lang_changed_with_version_change(self):
shader_lang_diff = """-#define ANGLE_SH_VERSION 100
+#define ANGLE_SH_VERSION 101
"""
errors = self.run_shader_version_check_presubmit('', [shader_lang_diff])
self.assertEqual(len(errors), 0)
def test_both_changed_with_version_change(self):
shader_lang_diff = """-#define ANGLE_SH_VERSION 100
+#define ANGLE_SH_VERSION 101
"""
shader_vars_diff = """-any change"""
errors = self.run_shader_version_check_presubmit('', [shader_lang_diff, shader_vars_diff])
self.assertEqual(len(errors), 0)
def test_shader_lang_changed_with_no_version_change(self):
shader_lang_diff = """+some change"""
errors = self.run_shader_version_check_presubmit('', [shader_lang_diff])
self.assertEqual(len(errors), 1)
self.assertEqual(
errors[0],
self.output_api.PresubmitError(
'ANGLE_SH_VERSION should be incremented when ShaderLang.h or ShaderVars.h change.')
)
def test_shader_lang_changed_with_no_version_change(self):
shader_lang_diff = """+some change
#define ANGLE_SH_VERSION 100
-other changes"""
errors = self.run_shader_version_check_presubmit('', [shader_lang_diff])
self.assertEqual(len(errors), 1)
self.assertEqual(
errors[0],
self.output_api.PresubmitError(
'ANGLE_SH_VERSION should be incremented when ShaderLang.h or ShaderVars.h change.')
)
def test_shader_lang_changed_with_version_cosmetic_change(self):
shader_lang_diff = """-#define ANGLE_SH_VERSION 100
+#define ANGLE_SH_VERSION 100 // cosmetic change
"""
errors = self.run_shader_version_check_presubmit('', [shader_lang_diff])
self.assertEqual(len(errors), 1)
self.assertEqual(
errors[0],
self.output_api.PresubmitError(
'ANGLE_SH_VERSION should be incremented when ShaderLang.h or ShaderVars.h change.')
)
def test_shader_lang_changed_with_version_decrement(self):
shader_lang_diff = """-#define ANGLE_SH_VERSION 100
+#define ANGLE_SH_VERSION 99
"""
errors = self.run_shader_version_check_presubmit('', [shader_lang_diff])
self.assertEqual(len(errors), 1)
self.assertEqual(
errors[0],
self.output_api.PresubmitError(
'ANGLE_SH_VERSION should be incremented when ShaderLang.h or ShaderVars.h change.')
)
def test_shader_lang_changed_in_revert(self):
shader_lang_diff = """-#define ANGLE_SH_VERSION 100
+#define ANGLE_SH_VERSION 99
"""
errors = self.run_shader_version_check_presubmit('Revert some change', [shader_lang_diff])
self.assertEqual(len(errors), 0)
class RestrictedTracesCheckTest(unittest.TestCase):
def __init__(self, *args, **kwargs):
super(RestrictedTracesCheckTest, self).__init__(*args, **kwargs)
self.output_api = OutputAPI_mock()
self.angle_root = os.getcwd()
def setUp(self):
json_path = os.path.join(self.angle_root,
'src/tests/restricted_traces/restricted_traces.json')
with open(json_path, 'r') as f:
self.original_content = f.read().splitlines()
def run_check(self, affected_files):
input_api = InputAPI_mock('', affected_files=affected_files)
input_api.cwd = self.angle_root
return PRESUBMIT._CheckRestrictedTraces(input_api, self.output_api)
def test_json_not_modified(self):
affected_files = [AffectedFile_mock('', 'some/other/file.cpp')]
errors = self.run_check(affected_files)
self.assertEqual(len(errors), 0)
def test_json_modified_limit_not_exceeded(self):
affected_files = [
AffectedFile_mock(
'',
'src/tests/restricted_traces/restricted_traces.json',
old_contents=self.original_content)
]
errors = self.run_check(affected_files)
self.assertEqual(len(errors), 0)
def test_json_modified_limit_exceeded(self):
json_path = os.path.join(self.angle_root,
'src/tests/restricted_traces/restricted_traces.json')
with open(json_path, 'r') as f:
original_data = json.load(f)
try:
temp_data = original_data.copy()
# Add 11 extra traces (no 'ci' and no 'representative')
temp_data['traces'] = original_data['traces'] + [
f"dummy_trace_{i} 1" for i in range(11)
]
with open(json_path, 'w') as f:
json.dump(temp_data, f)
affected_files = [
AffectedFile_mock(
'',
'src/tests/restricted_traces/restricted_traces.json',
old_contents=self.original_content)
]
errors = self.run_check(affected_files)
self.assertEqual(len(errors), 1)
self.assertIn("Too many CQ extra traces", errors[0]._message)
finally:
with open(json_path, 'w') as f:
json.dump(original_data, f, indent=2)
f.write('\n')
def test_missing_traces_key(self):
json_path = os.path.join(self.angle_root,
'src/tests/restricted_traces/restricted_traces.json')
with open(json_path, 'r') as f:
original_data = json.load(f)
try:
temp_data = original_data.copy()
if 'traces' in temp_data:
del temp_data['traces']
with open(json_path, 'w') as f:
json.dump(temp_data, f)
affected_files = [
AffectedFile_mock(
'',
'src/tests/restricted_traces/restricted_traces.json',
old_contents=self.original_content)
]
errors = self.run_check(affected_files)
self.assertEqual(len(errors), 1)
self.assertIn("missing the \"traces\" key", errors[0]._message)
finally:
with open(json_path, 'w') as f:
json.dump(original_data, f, indent=2)
f.write('\n')
def test_allowed_coexisting_tags(self):
json_path = os.path.join(self.angle_root,
'src/tests/restricted_traces/restricted_traces.json')
with open(json_path, 'r') as f:
original_data = json.load(f)
try:
temp_data = original_data.copy()
temp_data['traces'] = original_data['traces'] + [
"dummy_trace_1 1 ci representative smoke"
]
with open(json_path, 'w') as f:
json.dump(temp_data, f)
with open(json_path, 'r') as f:
mutated_content = f.read().splitlines()
affected_files = [
AffectedFile_mock(
'',
'src/tests/restricted_traces/restricted_traces.json',
old_contents=mutated_content)
]
errors = self.run_check(affected_files)
self.assertEqual(len(errors), 0)
finally:
with open(json_path, 'w') as f:
json.dump(original_data, f, indent=2)
f.write('\n')
def test_unsorted_tags(self):
json_path = os.path.join(self.angle_root,
'src/tests/restricted_traces/restricted_traces.json')
with open(json_path, 'r') as f:
original_data = json.load(f)
try:
temp_data = original_data.copy()
new_traces = []
modified = False
for trace in original_data['traces']:
if 'antutu_refinery' in trace and not modified:
new_traces.append("antutu_refinery 1 representative ci smoke")
modified = True
else:
new_traces.append(trace)
temp_data['traces'] = new_traces
with open(json_path, 'w') as f:
json.dump(temp_data, f)
affected_files = [
AffectedFile_mock(
'',
'src/tests/restricted_traces/restricted_traces.json',
old_contents=self.original_content)
]
errors = self.run_check(affected_files)
self.assertEqual(len(errors), 1)
self.assertIn("has unsorted tags", errors[0]._message)
finally:
with open(json_path, 'w') as f:
json.dump(original_data, f, indent=2)
f.write('\n')
def test_new_trace_with_tags_fails(self):
json_path = os.path.join(self.angle_root,
'src/tests/restricted_traces/restricted_traces.json')
with open(json_path, 'r') as f:
original_data = json.load(f)
try:
temp_data = original_data.copy()
temp_data['traces'] = original_data['traces'] + ["dummy_new_trace 1 representative"]
with open(json_path, 'w') as f:
json.dump(temp_data, f)
affected_files = [
AffectedFile_mock(
'',
'src/tests/restricted_traces/restricted_traces.json',
old_contents=self.original_content)
]
errors = self.run_check(affected_files)
self.assertEqual(len(errors), 1)
self.assertIn("must not have any tags initially", errors[0]._message)
finally:
with open(json_path, 'w') as f:
json.dump(original_data, f, indent=2)
f.write('\n')
def test_new_trace_without_tags_passes(self):
json_path = os.path.join(self.angle_root,
'src/tests/restricted_traces/restricted_traces.json')
with open(json_path, 'r') as f:
original_data = json.load(f)
try:
temp_data = original_data.copy()
temp_data['traces'] = original_data['traces'] + ["dummy_new_trace 1"]
with open(json_path, 'w') as f:
json.dump(temp_data, f)
affected_files = [
AffectedFile_mock(
'',
'src/tests/restricted_traces/restricted_traces.json',
old_contents=self.original_content)
]
errors = self.run_check(affected_files)
self.assertEqual(len(errors), 0)
finally:
with open(json_path, 'w') as f:
json.dump(original_data, f, indent=2)
f.write('\n')
class CheckUnsafeBuffersSafetyCommentsTest(unittest.TestCase):
def testNoUsage(self):
mock_input_api = InputAPI_mock('')
mock_input_api.source_files = [
AffectedFile_mock(
'', 'src/libANGLE/Foo.cpp', new_contents=[
'void Foo() {',
' int x = 0;',
'}',
])
]
errors = PRESUBMIT._CheckUnsafeBuffersSafetyComments(mock_input_api, OutputAPI_mock())
self.assertEqual(0, len(errors))
def testValidUsageSameLine(self):
mock_input_api = InputAPI_mock('')
mock_input_api.source_files = [
AffectedFile_mock(
'',
'src/libANGLE/Foo.cpp',
new_contents=[
'void Foo() {',
' ANGLE_UNSAFE_BUFFERS(ptr[0] = 0); // SAFETY: safe',
'}',
])
]
errors = PRESUBMIT._CheckUnsafeBuffersSafetyComments(mock_input_api, OutputAPI_mock())
self.assertEqual(0, len(errors))
def testValidUsagePrecedingLine(self):
mock_input_api = InputAPI_mock('')
mock_input_api.source_files = [
AffectedFile_mock(
'',
'src/libANGLE/Foo.cpp',
new_contents=[
'void Foo() {',
' // SAFETY: safe',
' ANGLE_UNSAFE_BUFFERS(ptr[0] = 0);',
'}',
])
]
errors = PRESUBMIT._CheckUnsafeBuffersSafetyComments(mock_input_api, OutputAPI_mock())
self.assertEqual(0, len(errors))
def testValidUsagePrecedingLineWithOtherComments(self):
mock_input_api = InputAPI_mock('')
mock_input_api.source_files = [
AffectedFile_mock(
'',
'src/libANGLE/Foo.cpp',
new_contents=[
'void Foo() {',
' // SAFETY: safe',
' // some other info',
' ANGLE_UNSAFE_BUFFERS(ptr[0] = 0);',
'}',
])
]
errors = PRESUBMIT._CheckUnsafeBuffersSafetyComments(mock_input_api, OutputAPI_mock())
self.assertEqual(0, len(errors))
def testValidUsageBrokenLine(self):
mock_input_api = InputAPI_mock('')
mock_input_api.source_files = [
AffectedFile_mock(
'',
'src/libANGLE/Foo.cpp',
new_contents=[
'void Foo() {',
' // SAFETY: safe',
' int very_long_line_due_lots_of_stuff = line_broken_by_clang_format +',
' ANGLE_UNSAFE_BUFFERS(ptr[0] = 0);',
'}',
])
]
errors = PRESUBMIT._CheckUnsafeBuffersSafetyComments(mock_input_api, OutputAPI_mock())
self.assertEqual(0, len(errors))
def testValidUsageSafetyCommentWithCodeSyntax(self):
mock_input_api = InputAPI_mock('')
mock_input_api.source_files = [
AffectedFile_mock(
'',
'src/libANGLE/Foo.cpp',
new_contents=[
'void Foo() {',
' // SAFETY: Explanation too large to desplay in one line of comment and use of c++ syntax',
' // to justify usage.... This is safe because',
' // dummy[1] = dummy[0]++; is safe',
' ANGLE_UNSAFE_BUFFERS(ptr[0] = 0);',
'}',
])
]
errors = PRESUBMIT._CheckUnsafeBuffersSafetyComments(mock_input_api, OutputAPI_mock())
self.assertEqual(0, len(errors))
def testInvalidUsageCommentSeparatedByForLoop(self):
mock_input_api = InputAPI_mock('')
mock_input_api.source_files = [
AffectedFile_mock(
'',
'src/libANGLE/Foo.cpp',
new_contents=[
'void Foo() {',
' // SAFETY: safe',
' for(int b = 0; b < buffer_size; b++){',
' ANGLE_UNSAFE_BUFFERS(dummy[b] += x);',
' }'
'}',
])
]
errors = PRESUBMIT._CheckUnsafeBuffersSafetyComments(mock_input_api, OutputAPI_mock())
self.assertEqual(1, len(errors))
def testInvalidUsageNoComment(self):
mock_input_api = InputAPI_mock('')
mock_input_api.source_files = [
AffectedFile_mock(
'',
'src/libANGLE/Foo.cpp',
new_contents=[
'void Foo() {',
' ANGLE_UNSAFE_BUFFERS(ptr[0] = 0);',
'}',
])
]
errors = PRESUBMIT._CheckUnsafeBuffersSafetyComments(mock_input_api, OutputAPI_mock())
self.assertEqual(1, len(errors))
self.assertIn('ANGLE_UNSAFE_BUFFERS usage must be accompanied', errors[0].items[0])
def testInvalidUsageCommentNotSafety(self):
mock_input_api = InputAPI_mock('')
mock_input_api.source_files = [
AffectedFile_mock(
'',
'src/libANGLE/Foo.cpp',
new_contents=[
'void Foo() {',
' // this is a comment but not safety',
' ANGLE_UNSAFE_BUFFERS(ptr[0] = 0);',
'}',
])
]
errors = PRESUBMIT._CheckUnsafeBuffersSafetyComments(mock_input_api, OutputAPI_mock())
self.assertEqual(1, len(errors))
def testInvalidUsageCommentSeparatedByCode(self):
mock_input_api = InputAPI_mock('')
mock_input_api.source_files = [
AffectedFile_mock(
'',
'src/libANGLE/Foo.cpp',
new_contents=[
'void Foo() {',
' // SAFETY: safe',
' int x = 0;',
' ANGLE_UNSAFE_BUFFERS(ptr[0] = 0);',
'}',
])
]
errors = PRESUBMIT._CheckUnsafeBuffersSafetyComments(mock_input_api, OutputAPI_mock())
self.assertEqual(1, len(errors))
def testIgnoreCommentedUsage(self):
mock_input_api = InputAPI_mock('')
mock_input_api.source_files = [
AffectedFile_mock(
'',
'src/libANGLE/Foo.cpp',
new_contents=[
'void Foo() {',
' // ANGLE_UNSAFE_BUFFERS(ptr[0] = 0);',
'}',
])
]
errors = PRESUBMIT._CheckUnsafeBuffersSafetyComments(mock_input_api, OutputAPI_mock())
self.assertEqual(0, len(errors))
class CheckBannedPatternsTest(unittest.TestCase):
def testNoUsage(self):
mock_input_api = InputAPI_mock('')
mock_input_api.source_files = [
AffectedFile_mock(
'', 'src/libANGLE/Foo.cpp', new_contents=[
'void Foo() {',
' int x = 0;',
'}',
])
]
errors = PRESUBMIT._CheckNoBannedPatterns(mock_input_api, OutputAPI_mock())
self.assertEqual(0, len(errors))
def testBannedUnsafeTodo(self):
mock_input_api = InputAPI_mock('')
mock_input_api.source_files = [
AffectedFile_mock(
'',
'src/libANGLE/Foo.cpp',
new_contents=[
'void Foo() {',
' ANGLE_UNSAFE_TODO(ptr[0] = 0);',
'}',
])
]
errors = PRESUBMIT._CheckNoBannedPatterns(mock_input_api, OutputAPI_mock())
self.assertEqual(1, len(errors))
self.assertIn('A banned pattern was used.', errors[0]._message)
self.assertIn('src/libANGLE/Foo.cpp:2:', errors[0]._message)
self.assertIn('Do not introduce new instances of ANGLE_UNSAFE_TODO', errors[0]._message)
self.assertEqual(1, len(errors[0].locations))
loc = errors[0].locations[0]
self.assertEqual('src/libANGLE/Foo.cpp', loc.file_path)
self.assertEqual(2, loc.start_line)
self.assertEqual(2, loc.end_line)
def testBannedPragma(self):
mock_input_api = InputAPI_mock('')
mock_input_api.source_files = [
AffectedFile_mock(
'', 'src/libANGLE/Foo.cpp', new_contents=[
'#pragma allow_unsafe_buffers',
])
]
errors = PRESUBMIT._CheckNoBannedPatterns(mock_input_api, OutputAPI_mock())
self.assertEqual(1, len(errors))
self.assertIn('A banned pattern was used.', errors[0]._message)
self.assertIn('src/libANGLE/Foo.cpp:1:', errors[0]._message)
self.assertIn('#pragma allow_unsafe_buffers is discouraged', errors[0]._message)
self.assertEqual(1, len(errors[0].locations))
loc = errors[0].locations[0]
self.assertEqual('src/libANGLE/Foo.cpp', loc.file_path)
self.assertEqual(1, loc.start_line)
self.assertEqual(1, loc.end_line)
def testCommentedUsageIgnored(self):
mock_input_api = InputAPI_mock('')
mock_input_api.source_files = [
AffectedFile_mock(
'',
'src/libANGLE/Foo.cpp',
new_contents=[
'// ANGLE_UNSAFE_TODO(ptr[0] = 0);',
'// #pragma allow_unsafe_buffers',
])
]
errors = PRESUBMIT._CheckNoBannedPatterns(mock_input_api, OutputAPI_mock())
self.assertEqual(0, len(errors))
if __name__ == '__main__':
unittest.main()