blob: 49758842b992bb228d4c28fc4ad1460f0639ea12 [file] [edit]
#!/usr/bin/env python3
# Copyright 2026 The Dawn & Tint Authors
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import os
import sys
import unittest
# Import PRESUBMIT first before depot_tools modifies sys.path
import PRESUBMIT
# Add depot_tools to path to import mocks
sys.path.append(
os.path.join(os.path.dirname(__file__), 'third_party', 'depot_tools'))
from testing_support.presubmit_canned_checks_test_mocks import (
MockAffectedFile, MockInputApi, MockOutputApi)
class CheckChangeTodoHasOwnerTest(unittest.TestCase):
def _create_mock_input_api(self):
mock_input_api = MockInputApi()
# Mock AffectedFiles to support file_filter
def mock_affected_files(include_dirs=False,
include_deletes=True,
file_filter=None):
if file_filter:
return [f for f in mock_input_api.files if file_filter(f)]
return mock_input_api.files
mock_input_api.change.AffectedFiles = mock_affected_files
return mock_input_api
def testNoTodo(self):
mock_input_api = self._create_mock_input_api()
mock_input_api.files = [
MockAffectedFile('src/dawn/Foo.cpp', [
'void Foo() {',
' int x = 0;',
'}',
])
]
errors = PRESUBMIT.CheckChangeTodoHasOwner(mock_input_api,
MockOutputApi())
self.assertEqual(0, len(errors))
def testValidTodo(self):
mock_input_api = self._create_mock_input_api()
mock_input_api.files = [
MockAffectedFile('src/dawn/Foo.cpp', [
'// TODO(crbug.com/123): fix this',
'// TODO: owner - fix this',
])
]
errors = PRESUBMIT.CheckChangeTodoHasOwner(mock_input_api,
MockOutputApi())
self.assertEqual(0, len(errors))
def testInvalidTodo(self):
mock_input_api = self._create_mock_input_api()
mock_input_api.files = [
MockAffectedFile('src/dawn/Foo.cpp', [
'// TODO: fix this',
])
]
errors = PRESUBMIT.CheckChangeTodoHasOwner(mock_input_api,
MockOutputApi())
self.assertEqual(1, len(errors))
self.assertIn('Found TODO with no issue number', errors[0].message)
def testDawnUnsafeTodoIgnored(self):
mock_input_api = self._create_mock_input_api()
mock_input_api.files = [
MockAffectedFile('src/dawn/Foo.cpp', [
'DAWN_UNSAFE_TODO(ptr[0] = 0);',
])
]
errors = PRESUBMIT.CheckChangeTodoHasOwner(mock_input_api,
MockOutputApi())
self.assertEqual(0, len(errors))
def testTodoAndDawnUnsafeTodo(self):
mock_input_api = self._create_mock_input_api()
mock_input_api.files = [
MockAffectedFile('src/dawn/Foo.cpp', [
'DAWN_UNSAFE_TODO(ptr[0] = 0); // TODO: fix this',
])
]
errors = PRESUBMIT.CheckChangeTodoHasOwner(mock_input_api,
MockOutputApi())
self.assertEqual(1, len(errors))
self.assertIn('Found TODO with no issue number', errors[0].message)
def testMixedTodos(self):
mock_input_api = self._create_mock_input_api()
mock_input_api.files = [
MockAffectedFile('src/dawn/Foo.cpp', [
'// TODO: fix this',
'DAWN_UNSAFE_TODO(ptr[0] = 0);',
])
]
errors = PRESUBMIT.CheckChangeTodoHasOwner(mock_input_api,
MockOutputApi())
self.assertEqual(1, len(errors))
self.assertIn('src/dawn/Foo.cpp:1', errors[0].message)
self.assertNotIn('src/dawn/Foo.cpp:2', errors[0].message)
def testPresubmitFilesIgnored(self):
mock_input_api = self._create_mock_input_api()
mock_input_api.files = [
MockAffectedFile('PRESUBMIT.py', [
'// TODO: fix this',
'DAWN_UNSAFE_TODO(ptr[0] = 0);',
]),
MockAffectedFile('PRESUBMIT_test.py', [
'// TODO: fix this',
'DAWN_UNSAFE_TODO(ptr[0] = 0);',
])
]
errors = PRESUBMIT.CheckChangeTodoHasOwner(mock_input_api,
MockOutputApi())
self.assertEqual(0, len(errors))
class CheckUnsafeBuffersSafetyCommentsTest(unittest.TestCase):
def testNoUsage(self):
mock_input_api = MockInputApi()
mock_input_api.files = [
MockAffectedFile('src/dawn/Foo.cpp', [
'void Foo() {',
' int x = 0;',
'}',
])
]
errors = PRESUBMIT.CheckUnsafeBuffersSafetyComments(
mock_input_api, MockOutputApi())
self.assertEqual(0, len(errors))
def testValidUsageSameLine(self):
mock_input_api = MockInputApi()
mock_input_api.files = [
MockAffectedFile('src/dawn/Foo.cpp', [
'void Foo() {',
' DAWN_UNSAFE_BUFFERS(ptr[0] = 0); // SAFETY: safe',
'}',
])
]
errors = PRESUBMIT.CheckUnsafeBuffersSafetyComments(
mock_input_api, MockOutputApi())
self.assertEqual(0, len(errors))
def testValidUsagePrecedingLine(self):
mock_input_api = MockInputApi()
mock_input_api.files = [
MockAffectedFile('src/dawn/Foo.cpp', [
'void Foo() {',
' // SAFETY: safe',
' DAWN_UNSAFE_BUFFERS(ptr[0] = 0);',
'}',
])
]
errors = PRESUBMIT.CheckUnsafeBuffersSafetyComments(
mock_input_api, MockOutputApi())
self.assertEqual(0, len(errors))
def testValidUsagePrecedingLineWithOtherComments(self):
mock_input_api = MockInputApi()
mock_input_api.files = [
MockAffectedFile('src/dawn/Foo.cpp', [
'void Foo() {',
' // SAFETY: safe',
' // some other info',
' DAWN_UNSAFE_BUFFERS(ptr[0] = 0);',
'}',
])
]
errors = PRESUBMIT.CheckUnsafeBuffersSafetyComments(
mock_input_api, MockOutputApi())
self.assertEqual(0, len(errors))
def testInvalidUsageNoComment(self):
mock_input_api = MockInputApi()
mock_input_api.files = [
MockAffectedFile('src/dawn/Foo.cpp', [
'void Foo() {',
' DAWN_UNSAFE_BUFFERS(ptr[0] = 0);',
'}',
])
]
errors = PRESUBMIT.CheckUnsafeBuffersSafetyComments(
mock_input_api, MockOutputApi())
self.assertEqual(1, len(errors))
self.assertIn('DAWN_UNSAFE_BUFFERS usage must be accompanied',
errors[0].items[0])
def testInvalidUsageCommentNotSafety(self):
mock_input_api = MockInputApi()
mock_input_api.files = [
MockAffectedFile('src/dawn/Foo.cpp', [
'void Foo() {',
' // this is a comment but not safety',
' DAWN_UNSAFE_BUFFERS(ptr[0] = 0);',
'}',
])
]
errors = PRESUBMIT.CheckUnsafeBuffersSafetyComments(
mock_input_api, MockOutputApi())
self.assertEqual(1, len(errors))
def testInvalidUsageCommentSeparatedByCode(self):
mock_input_api = MockInputApi()
mock_input_api.files = [
MockAffectedFile('src/dawn/Foo.cpp', [
'void Foo() {',
' // SAFETY: safe',
' int x = 0;',
' DAWN_UNSAFE_BUFFERS(ptr[0] = 0);',
'}',
])
]
errors = PRESUBMIT.CheckUnsafeBuffersSafetyComments(
mock_input_api, MockOutputApi())
self.assertEqual(1, len(errors))
def testIgnoreCommentedUsage(self):
mock_input_api = MockInputApi()
mock_input_api.files = [
MockAffectedFile('src/dawn/Foo.cpp', [
'void Foo() {',
' // DAWN_UNSAFE_BUFFERS(ptr[0] = 0);',
'}',
])
]
errors = PRESUBMIT.CheckUnsafeBuffersSafetyComments(
mock_input_api, MockOutputApi())
self.assertEqual(0, len(errors))
def testNonCppFilesIgnored(self):
mock_input_api = MockInputApi()
mock_input_api.files = [
MockAffectedFile('src/dawn/Foo.txt', [
'DAWN_UNSAFE_BUFFERS(ptr[0] = 0);',
])
]
errors = PRESUBMIT.CheckUnsafeBuffersSafetyComments(
mock_input_api, MockOutputApi())
self.assertEqual(0, len(errors))
if __name__ == '__main__':
unittest.main()