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