1#!/usr/bin/env python3 2# Copyright 2022 The Android Open Source Project 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://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, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16"""Unittests for clang-format.""" 17 18import contextlib 19from pathlib import Path 20import sys 21import tempfile 22import unittest 23 24 25DIR = Path(__file__).resolve().parent 26sys.path.insert(0, str(DIR.parent)) 27 28# We have to import our local modules after the sys.path tweak. We can't use 29# relative imports because this is an executable program, not a module. 30# pylint: disable=wrong-import-position,import-error 31import rh.utils 32 33 34CLANG_FORMAT = DIR / 'clang-format.py' 35 36 37@contextlib.contextmanager 38def git_clang_format(data: str): 39 """Create a fake git-clang-format script.""" 40 with tempfile.TemporaryDirectory(prefix='repohooks-tests') as tempdir: 41 tempdir = Path(tempdir) 42 script = tempdir / 'git-clang-format-fake.sh' 43 script.write_text(f'#!/bin/sh\n{data}', encoding='utf-8') 44 script.chmod(0o755) 45 yield script 46 47 48def run_clang_format(script, args, **kwargs): 49 """Helper to run clang-format.py with fake git-clang-format script.""" 50 kwargs.setdefault('capture_output', True) 51 return rh.utils.run( 52 [CLANG_FORMAT, '--git-clang-format', script] + args, **kwargs) 53 54 55class GitClangFormatExit(unittest.TestCase): 56 """Test git-clang-format parsing.""" 57 58 def test_diff_exit_0_no_output(self): 59 """Test exit 0 w/no output.""" 60 with git_clang_format('exit 0') as script: 61 result = run_clang_format(script, ['--working-tree']) 62 self.assertEqual(result.stdout, '') 63 64 def test_diff_exit_0_stderr(self): 65 """Test exit 0 w/stderr output.""" 66 with git_clang_format('echo bad >&2; exit 0') as script: 67 with self.assertRaises(rh.utils.CalledProcessError) as e: 68 run_clang_format(script, ['--working-tree']) 69 self.assertIn('clang-format failed', e.exception.stderr) 70 71 def test_diff_exit_1_no_output(self): 72 """Test exit 1 w/no output.""" 73 with git_clang_format('exit 1') as script: 74 result = run_clang_format(script, ['--working-tree']) 75 self.assertEqual(result.stdout, '') 76 77 def test_diff_exit_1_output(self): 78 """Test exit 1 with output.""" 79 with git_clang_format('echo bad; exit 1') as script: 80 with self.assertRaises(rh.utils.CalledProcessError) as e: 81 run_clang_format(script, ['--working-tree']) 82 self.assertIn('clang-format failed', e.exception.stderr) 83 84 def test_diff_exit_1_stderr(self): 85 """Test exit 1 w/stderr.""" 86 with git_clang_format('echo bad >&2; exit 1') as script: 87 with self.assertRaises(rh.utils.CalledProcessError) as e: 88 run_clang_format(script, ['--working-tree']) 89 self.assertIn('clang-format failed', e.exception.stderr) 90 91 def test_diff_exit_2(self): 92 """Test exit 2.""" 93 with git_clang_format('exit 2') as script: 94 with self.assertRaises(rh.utils.CalledProcessError) as e: 95 run_clang_format(script, ['--working-tree']) 96 self.assertIn('clang-format failed', e.exception.stderr) 97 98 def test_fix_exit_1_output(self): 99 """Test fix with incorrect patch syntax.""" 100 with git_clang_format('echo bad patch; exit 1') as script: 101 with self.assertRaises(rh.utils.CalledProcessError) as e: 102 run_clang_format(script, ['--working-tree', '--fix']) 103 self.assertIn('Error: Unable to automatically fix things', 104 e.exception.stderr) 105 106 107if __name__ == '__main__': 108 unittest.main() 109