xref: /aosp_15_r20/external/pigweed/pw_presubmit/py/cpp_checks_test.py (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1#!/usr/bin/env python3
2# Copyright 2023 The Pigweed Authors
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may not
5# use this file except in compliance with the License. You may obtain a copy of
6# the License at
7#
8#     https://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, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations under
14# the License.
15"""Tests for cpp_checks."""
16
17import os
18from pathlib import Path
19import unittest
20from unittest.mock import MagicMock, mock_open, patch
21
22from pw_presubmit import cpp_checks
23
24# pylint: disable=attribute-defined-outside-init
25
26
27def _temproot():
28    root = Path(os.environ['_PW_ACTUAL_ENVIRONMENT_ROOT']) / 'temp'
29    root.mkdir(exist_ok=True)
30    return root
31
32
33class TestPragmaOnce(unittest.TestCase):
34    """Test pragma_once check."""
35
36    def _run(self, contents: str) -> None:
37        self.ctx = MagicMock()
38        self.ctx.fail = MagicMock()
39        self.ctx.format_options.filter_paths = lambda x: x
40        path = MagicMock(spec=Path('include/foo.h'))
41
42        def mocked_open_read(*args, **kwargs):
43            return mock_open(read_data=contents)(*args, **kwargs)
44
45        with patch.object(path, 'open', mocked_open_read):
46            self.ctx.paths = [path]
47            cpp_checks.pragma_once(self.ctx)
48
49    def test_empty(self) -> None:
50        self._run('')
51        self.ctx.fail.assert_called()
52
53    def test_simple(self) -> None:
54        self._run('#pragma once')
55        self.ctx.fail.assert_not_called()
56
57    def test_not_first(self) -> None:
58        self._run('1\n2\n3\n#pragma once')
59        self.ctx.fail.assert_not_called()
60
61    def test_different_pragma(self) -> None:
62        self._run('#pragma thrice')
63        self.ctx.fail.assert_called()
64
65
66def guard(path: Path) -> str:
67    return str(path.name).replace('.', '_').upper()
68
69
70class TestIncludeGuard(unittest.TestCase):
71    """Test pragma_once check."""
72
73    def _run(self, contents: str, **kwargs) -> None:
74        self.ctx = MagicMock()
75        self.ctx.format_options.filter_paths = lambda x: x
76        self.ctx.fail = MagicMock()
77        path = MagicMock(spec=Path('abc/def/foo.h'))
78        path.name = 'foo.h'
79
80        def mocked_open_read(*args, **kwargs):
81            return mock_open(read_data=contents)(*args, **kwargs)
82
83        with patch.object(path, 'open', mocked_open_read):
84            self.ctx.paths = [path]
85            check = cpp_checks.include_guard_check(**kwargs)
86            check(self.ctx)
87
88    def test_empty(self) -> None:
89        self._run('')
90        self.ctx.fail.assert_called()
91
92    def test_simple(self) -> None:
93        self._run('#ifndef FOO_H\n#define FOO_H', guard=guard)
94        self.ctx.fail.assert_not_called()
95
96    def test_simple_any(self) -> None:
97        self._run('#ifndef BAR_H\n#define BAR_H')
98        self.ctx.fail.assert_not_called()
99
100    def test_simple_comments(self) -> None:
101        self._run('// abc\n#ifndef BAR_H\n// def\n#define BAR_H\n// ghi')
102        self.ctx.fail.assert_not_called()
103
104    def test_simple_whitespace(self) -> None:
105        self._run(
106            '         // abc\n #ifndef           BAR_H\n// def\n'
107            '                  #define BAR_H\n// ghi\n #endif'
108        )
109        self.ctx.fail.assert_not_called()
110
111    def test_simple_not_expected(self) -> None:
112        self._run('#ifnef BAR_H\n#define BAR_H', guard=guard)
113        self.ctx.fail.assert_called()
114
115    def test_no_define(self) -> None:
116        self._run('#ifndef FOO_H')
117        self.ctx.fail.assert_called()
118
119    def test_non_matching_define(self) -> None:
120        self._run('#ifndef FOO_H\n#define BAR_H')
121        self.ctx.fail.assert_called()
122
123
124if __name__ == '__main__':
125    unittest.main()
126