aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-05-24 23:30:47 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-05-24 23:30:47 +0000
commit62080fb8f070f9594b388cba8b036291266ecfc1 (patch)
tree1bb0dd828c050251888c7fb65bfad048521b6348
parent7acbe93f5941c41441b2040b755a4b4d99d6234e (diff)
parent78a09afc68a6c21b994029b8aac5a7e173026fac (diff)
downloadrepohooks-android-security-13.0.0_r17.tar.gz
Change-Id: I20cb438e04c4cfbae2631054a1263056a80c447a
-rw-r--r--PREUPLOAD.cfg1
-rw-r--r--rh/shell.py6
-rwxr-xr-xrh/shell_unittest.py13
-rwxr-xr-xrh/utils_unittest.py8
-rwxr-xr-xtools/clang-format.py6
-rwxr-xr-xtools/clang-format_unittest.py94
6 files changed, 126 insertions, 2 deletions
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 81f505d..631915d 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -5,6 +5,7 @@ hooks_unittest = ./rh/hooks_unittest.py
shell_unittest = ./rh/shell_unittest.py
utils_unittest = ./rh/utils_unittest.py
android_test_mapping_format_unittest = ./tools/android_test_mapping_format_unittest.py
+clang-format unittest = ./tools/clang-format_unittest.py
config_test = ./rh/config_test.py --check-env --commit-id ${PREUPLOAD_COMMIT} --commit-msg ${PREUPLOAD_COMMIT_MESSAGE} --repo-root ${REPO_ROOT} -- ${PREUPLOAD_FILES}
[Builtin Hooks]
diff --git a/rh/shell.py b/rh/shell.py
index 0321e44..bece0b2 100644
--- a/rh/shell.py
+++ b/rh/shell.py
@@ -15,6 +15,7 @@
"""Functions for working with shell code."""
import os
+import pathlib
import sys
_path = os.path.realpath(__file__ + '/../..')
@@ -65,8 +66,13 @@ def shell_quote(s):
Returns:
A safely (possibly quoted) string.
"""
+ # If callers pass down bad types, don't blow up.
if isinstance(s, bytes):
s = s.encode('utf-8')
+ elif isinstance(s, pathlib.PurePath):
+ return str(s)
+ elif not isinstance(s, str):
+ return repr(s)
# See if no quoting is needed so we can return the string as-is.
for c in s:
diff --git a/rh/shell_unittest.py b/rh/shell_unittest.py
index 56b2321..f7d2bba 100755
--- a/rh/shell_unittest.py
+++ b/rh/shell_unittest.py
@@ -17,6 +17,7 @@
import difflib
import os
+from pathlib import Path
import sys
import unittest
@@ -94,6 +95,18 @@ class ShellQuoteTest(DiffTestCase):
self._testData(aux, {k: k for k in tests_quote.values()}, False)
self._testData(aux, {k: k for k in tests_quote}, False)
+ def testPathlib(self):
+ """Verify pathlib is handled."""
+ self.assertEqual(rh.shell.shell_quote(Path('/')), '/')
+
+ def testBadInputs(self):
+ """Verify bad inputs do not crash."""
+ for arg, exp in (
+ (1234, '1234'),
+ (Exception('hi'), "Exception('hi')"),
+ ):
+ self.assertEqual(rh.shell.shell_quote(arg), exp)
+
class CmdToStrTest(DiffTestCase):
"""Test the cmd_to_str function."""
diff --git a/rh/utils_unittest.py b/rh/utils_unittest.py
index 586dfcc..7928dd5 100755
--- a/rh/utils_unittest.py
+++ b/rh/utils_unittest.py
@@ -17,6 +17,7 @@
import datetime
import os
+from pathlib import Path
import sys
import unittest
@@ -215,6 +216,13 @@ class RunCommandTests(unittest.TestCase):
self.assertNotEqual(0, err.returncode)
self.assertIn('a/b/c/d', str(err))
+ def test_pathlib(self):
+ """Verify pathlib arguments work."""
+ result = rh.utils.run(['true', Path('/')])
+ # Verify stringify behavior.
+ str(result)
+ self.assertEqual(result.cmdstr, 'true /')
+
if __name__ == '__main__':
unittest.main()
diff --git a/tools/clang-format.py b/tools/clang-format.py
index ebb6abe..24ef711 100755
--- a/tools/clang-format.py
+++ b/tools/clang-format.py
@@ -82,8 +82,10 @@ def main(argv):
result = rh.utils.run(cmd, capture_output=True, check=False)
# Newer versions of git-clang-format will exit 1 when it worked. Assume a
# real failure is any exit code above 1, or any time stderr is used, or if
- # it exited 0/1 but didn't produce anything useful to stdout.
- if result.returncode > 1 or result.stderr or not result.stdout:
+ # it exited 1 but didn't produce anything useful to stdout. If it exited 0,
+ # then assume all is well and we'll attempt to parse its output below.
+ if (result.returncode > 1 or result.stderr or
+ (not result.stdout and result.returncode)):
print(f'clang-format failed:\ncmd: {result.cmdstr}\n'
f'stdout:\n{result.stdout}\nstderr:\n{result.stderr}',
file=sys.stderr)
diff --git a/tools/clang-format_unittest.py b/tools/clang-format_unittest.py
new file mode 100755
index 0000000..1128f65
--- /dev/null
+++ b/tools/clang-format_unittest.py
@@ -0,0 +1,94 @@
+#!/usr/bin/env python3
+# Copyright 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Unittests for clang-format."""
+
+import contextlib
+from pathlib import Path
+import sys
+import tempfile
+import unittest
+
+
+DIR = Path(__file__).resolve().parent
+sys.path.insert(0, str(DIR.parent))
+
+# We have to import our local modules after the sys.path tweak. We can't use
+# relative imports because this is an executable program, not a module.
+# pylint: disable=wrong-import-position
+import rh.utils
+
+
+CLANG_FORMAT = DIR / 'clang-format.py'
+
+
+@contextlib.contextmanager
+def git_clang_format(data: str):
+ """Create a fake git-clang-format script."""
+ with tempfile.TemporaryDirectory(prefix='repohooks-tests') as tempdir:
+ tempdir = Path(tempdir)
+ script = tempdir / 'git-clang-format-fake.sh'
+ script.write_text(f'#!/bin/sh\n{data}', encoding='utf-8')
+ script.chmod(0o755)
+ yield script
+
+
+def run_clang_format(script, args, **kwargs):
+ """Helper to run clang-format.py with fake git-clang-format script."""
+ kwargs.setdefault('capture_output', True)
+ return rh.utils.run(
+ [CLANG_FORMAT, '--git-clang-format', script] + args, **kwargs)
+
+
+class GitClangFormatExit(unittest.TestCase):
+ """Test git-clang-format parsing."""
+
+ def test_diff_exit_0_no_output(self):
+ """Test exit 0 w/no output."""
+ with git_clang_format('exit 0') as script:
+ result = run_clang_format(script, ['--working-tree'])
+ self.assertEqual(result.stdout, '')
+
+ def test_diff_exit_0_stderr(self):
+ """Test exit 0 w/stderr output."""
+ with git_clang_format('echo bad >&2; exit 0') as script:
+ with self.assertRaises(rh.utils.CalledProcessError) as e:
+ run_clang_format(script, ['--working-tree'])
+ self.assertIn('clang-format failed', e.exception.stderr)
+
+ def test_diff_exit_1_no_output(self):
+ """Test exit 1 w/no output."""
+ with git_clang_format('exit 1') as script:
+ with self.assertRaises(rh.utils.CalledProcessError) as e:
+ run_clang_format(script, ['--working-tree'])
+ self.assertIn('clang-format failed', e.exception.stderr)
+
+ def test_diff_exit_1_stderr(self):
+ """Test exit 1 w/stderr."""
+ with git_clang_format('echo bad >&2; exit 1') as script:
+ with self.assertRaises(rh.utils.CalledProcessError) as e:
+ run_clang_format(script, ['--working-tree'])
+ self.assertIn('clang-format failed', e.exception.stderr)
+
+ def test_diff_exit_2(self):
+ """Test exit 2."""
+ with git_clang_format('exit 2') as script:
+ with self.assertRaises(rh.utils.CalledProcessError) as e:
+ run_clang_format(script, ['--working-tree'])
+ self.assertIn('clang-format failed', e.exception.stderr)
+
+
+if __name__ == '__main__':
+ unittest.main()