xref: /aosp_15_r20/tools/repohooks/rh/shell_unittest.py (revision d68f33bc6fb0cc2476107c2af0573a2f5a63dfc1)
1*d68f33bcSAndroid Build Coastguard Worker#!/usr/bin/env python3
2*d68f33bcSAndroid Build Coastguard Worker# Copyright 2016 The Android Open Source Project
3*d68f33bcSAndroid Build Coastguard Worker#
4*d68f33bcSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
5*d68f33bcSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
6*d68f33bcSAndroid Build Coastguard Worker# You may obtain a copy of the License at
7*d68f33bcSAndroid Build Coastguard Worker#
8*d68f33bcSAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
9*d68f33bcSAndroid Build Coastguard Worker#
10*d68f33bcSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
11*d68f33bcSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
12*d68f33bcSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*d68f33bcSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
14*d68f33bcSAndroid Build Coastguard Worker# limitations under the License.
15*d68f33bcSAndroid Build Coastguard Worker
16*d68f33bcSAndroid Build Coastguard Worker"""Unittests for the shell module."""
17*d68f33bcSAndroid Build Coastguard Worker
18*d68f33bcSAndroid Build Coastguard Workerimport difflib
19*d68f33bcSAndroid Build Coastguard Workerimport os
20*d68f33bcSAndroid Build Coastguard Workerfrom pathlib import Path
21*d68f33bcSAndroid Build Coastguard Workerimport sys
22*d68f33bcSAndroid Build Coastguard Workerimport unittest
23*d68f33bcSAndroid Build Coastguard Worker
24*d68f33bcSAndroid Build Coastguard Worker_path = os.path.realpath(__file__ + '/../..')
25*d68f33bcSAndroid Build Coastguard Workerif sys.path[0] != _path:
26*d68f33bcSAndroid Build Coastguard Worker    sys.path.insert(0, _path)
27*d68f33bcSAndroid Build Coastguard Workerdel _path
28*d68f33bcSAndroid Build Coastguard Worker
29*d68f33bcSAndroid Build Coastguard Worker# We have to import our local modules after the sys.path tweak.  We can't use
30*d68f33bcSAndroid Build Coastguard Worker# relative imports because this is an executable program, not a module.
31*d68f33bcSAndroid Build Coastguard Worker# pylint: disable=wrong-import-position
32*d68f33bcSAndroid Build Coastguard Workerimport rh.shell
33*d68f33bcSAndroid Build Coastguard Worker
34*d68f33bcSAndroid Build Coastguard Worker
35*d68f33bcSAndroid Build Coastguard Workerclass DiffTestCase(unittest.TestCase):
36*d68f33bcSAndroid Build Coastguard Worker    """Helper that includes diff output when failing."""
37*d68f33bcSAndroid Build Coastguard Worker
38*d68f33bcSAndroid Build Coastguard Worker    def setUp(self):
39*d68f33bcSAndroid Build Coastguard Worker        self.differ = difflib.Differ()
40*d68f33bcSAndroid Build Coastguard Worker
41*d68f33bcSAndroid Build Coastguard Worker    def _assertEqual(self, func, test_input, test_output, result):
42*d68f33bcSAndroid Build Coastguard Worker        """Like assertEqual but with built in diff support."""
43*d68f33bcSAndroid Build Coastguard Worker        diff = '\n'.join(list(self.differ.compare([test_output], [result])))
44*d68f33bcSAndroid Build Coastguard Worker        msg = (f'Expected {func} to translate {test_input!r} to '
45*d68f33bcSAndroid Build Coastguard Worker               f'{test_output!r}, but got {result!r}\n{diff}')
46*d68f33bcSAndroid Build Coastguard Worker        self.assertEqual(test_output, result, msg)
47*d68f33bcSAndroid Build Coastguard Worker
48*d68f33bcSAndroid Build Coastguard Worker    def _testData(self, functor, tests, check_type=True):
49*d68f33bcSAndroid Build Coastguard Worker        """Process a dict of test data."""
50*d68f33bcSAndroid Build Coastguard Worker        for test_output, test_input in tests.items():
51*d68f33bcSAndroid Build Coastguard Worker            result = functor(test_input)
52*d68f33bcSAndroid Build Coastguard Worker            self._assertEqual(functor.__name__, test_input, test_output, result)
53*d68f33bcSAndroid Build Coastguard Worker
54*d68f33bcSAndroid Build Coastguard Worker            if check_type:
55*d68f33bcSAndroid Build Coastguard Worker                # Also make sure the result is a string, otherwise the %r
56*d68f33bcSAndroid Build Coastguard Worker                # output will include a "u" prefix and that is not good for
57*d68f33bcSAndroid Build Coastguard Worker                # logging.
58*d68f33bcSAndroid Build Coastguard Worker                self.assertEqual(type(test_output), str)
59*d68f33bcSAndroid Build Coastguard Worker
60*d68f33bcSAndroid Build Coastguard Worker
61*d68f33bcSAndroid Build Coastguard Workerclass ShellQuoteTest(DiffTestCase):
62*d68f33bcSAndroid Build Coastguard Worker    """Test the quote & unquote functions."""
63*d68f33bcSAndroid Build Coastguard Worker
64*d68f33bcSAndroid Build Coastguard Worker    def testShellQuote(self):
65*d68f33bcSAndroid Build Coastguard Worker        """Basic ShellQuote tests."""
66*d68f33bcSAndroid Build Coastguard Worker        # Dict of expected output strings to input lists.
67*d68f33bcSAndroid Build Coastguard Worker        tests_quote = {
68*d68f33bcSAndroid Build Coastguard Worker            "''": '',
69*d68f33bcSAndroid Build Coastguard Worker            'a': 'a',
70*d68f33bcSAndroid Build Coastguard Worker            "'a b c'": 'a b c',
71*d68f33bcSAndroid Build Coastguard Worker            "'a\tb'": 'a\tb',
72*d68f33bcSAndroid Build Coastguard Worker            "'/a$file'": '/a$file',
73*d68f33bcSAndroid Build Coastguard Worker            "'/a#file'": '/a#file',
74*d68f33bcSAndroid Build Coastguard Worker            """'b"c'""": 'b"c',
75*d68f33bcSAndroid Build Coastguard Worker            "'a@()b'": 'a@()b',
76*d68f33bcSAndroid Build Coastguard Worker            'j%k': 'j%k',
77*d68f33bcSAndroid Build Coastguard Worker            r'''"s'a\$va\\rs"''': r"s'a$va\rs",
78*d68f33bcSAndroid Build Coastguard Worker            r'''"\\'\\\""''': r'''\'\"''',
79*d68f33bcSAndroid Build Coastguard Worker            r'''"'\\\$"''': r"""'\$""",
80*d68f33bcSAndroid Build Coastguard Worker        }
81*d68f33bcSAndroid Build Coastguard Worker
82*d68f33bcSAndroid Build Coastguard Worker        # Expected input output specific to ShellUnquote.  This string cannot
83*d68f33bcSAndroid Build Coastguard Worker        # be produced by ShellQuote but is still a valid bash escaped string.
84*d68f33bcSAndroid Build Coastguard Worker        tests_unquote = {
85*d68f33bcSAndroid Build Coastguard Worker            r'''\$''': r'''"\\$"''',
86*d68f33bcSAndroid Build Coastguard Worker        }
87*d68f33bcSAndroid Build Coastguard Worker
88*d68f33bcSAndroid Build Coastguard Worker        def aux(s):
89*d68f33bcSAndroid Build Coastguard Worker            return rh.shell.unquote(rh.shell.quote(s))
90*d68f33bcSAndroid Build Coastguard Worker
91*d68f33bcSAndroid Build Coastguard Worker        self._testData(rh.shell.quote, tests_quote)
92*d68f33bcSAndroid Build Coastguard Worker        self._testData(rh.shell.unquote, tests_unquote)
93*d68f33bcSAndroid Build Coastguard Worker
94*d68f33bcSAndroid Build Coastguard Worker        # Test that the operations are reversible.
95*d68f33bcSAndroid Build Coastguard Worker        self._testData(aux, {k: k for k in tests_quote.values()}, False)
96*d68f33bcSAndroid Build Coastguard Worker        self._testData(aux, {k: k for k in tests_quote}, False)
97*d68f33bcSAndroid Build Coastguard Worker
98*d68f33bcSAndroid Build Coastguard Worker    def testPathlib(self):
99*d68f33bcSAndroid Build Coastguard Worker        """Verify pathlib is handled."""
100*d68f33bcSAndroid Build Coastguard Worker        self.assertEqual(rh.shell.quote(Path('/')), '/')
101*d68f33bcSAndroid Build Coastguard Worker
102*d68f33bcSAndroid Build Coastguard Worker    def testBadInputs(self):
103*d68f33bcSAndroid Build Coastguard Worker        """Verify bad inputs do not crash."""
104*d68f33bcSAndroid Build Coastguard Worker        for arg, exp in (
105*d68f33bcSAndroid Build Coastguard Worker            (1234, '1234'),
106*d68f33bcSAndroid Build Coastguard Worker            (Exception('hi'), "Exception('hi')"),
107*d68f33bcSAndroid Build Coastguard Worker        ):
108*d68f33bcSAndroid Build Coastguard Worker            self.assertEqual(rh.shell.quote(arg), exp)
109*d68f33bcSAndroid Build Coastguard Worker
110*d68f33bcSAndroid Build Coastguard Worker
111*d68f33bcSAndroid Build Coastguard Workerclass CmdToStrTest(DiffTestCase):
112*d68f33bcSAndroid Build Coastguard Worker    """Test the cmd_to_str function."""
113*d68f33bcSAndroid Build Coastguard Worker
114*d68f33bcSAndroid Build Coastguard Worker    def testCmdToStr(self):
115*d68f33bcSAndroid Build Coastguard Worker        # Dict of expected output strings to input lists.
116*d68f33bcSAndroid Build Coastguard Worker        tests = {
117*d68f33bcSAndroid Build Coastguard Worker            r"a b": ['a', 'b'],
118*d68f33bcSAndroid Build Coastguard Worker            r"'a b' c": ['a b', 'c'],
119*d68f33bcSAndroid Build Coastguard Worker            r'''a "b'c"''': ['a', "b'c"],
120*d68f33bcSAndroid Build Coastguard Worker            r'''a "/'\$b" 'a b c' "xy'z"''':
121*d68f33bcSAndroid Build Coastguard Worker                ['a', "/'$b", 'a b c', "xy'z"],
122*d68f33bcSAndroid Build Coastguard Worker            '': [],
123*d68f33bcSAndroid Build Coastguard Worker        }
124*d68f33bcSAndroid Build Coastguard Worker        self._testData(rh.shell.cmd_to_str, tests)
125*d68f33bcSAndroid Build Coastguard Worker
126*d68f33bcSAndroid Build Coastguard Worker
127*d68f33bcSAndroid Build Coastguard Workerclass BooleanShellTest(unittest.TestCase):
128*d68f33bcSAndroid Build Coastguard Worker    """Test the boolean_shell_value function."""
129*d68f33bcSAndroid Build Coastguard Worker
130*d68f33bcSAndroid Build Coastguard Worker    def testFull(self):
131*d68f33bcSAndroid Build Coastguard Worker        """Verify nputs work as expected"""
132*d68f33bcSAndroid Build Coastguard Worker        for v in (None,):
133*d68f33bcSAndroid Build Coastguard Worker            self.assertTrue(rh.shell.boolean_shell_value(v, True))
134*d68f33bcSAndroid Build Coastguard Worker            self.assertFalse(rh.shell.boolean_shell_value(v, False))
135*d68f33bcSAndroid Build Coastguard Worker
136*d68f33bcSAndroid Build Coastguard Worker        for v in (1234, '', 'akldjsf', '"'):
137*d68f33bcSAndroid Build Coastguard Worker            self.assertRaises(ValueError, rh.shell.boolean_shell_value, v, True)
138*d68f33bcSAndroid Build Coastguard Worker
139*d68f33bcSAndroid Build Coastguard Worker        for v in ('yes', 'YES', 'YeS', 'y', 'Y', '1', 'true', 'True', 'TRUE',):
140*d68f33bcSAndroid Build Coastguard Worker            self.assertTrue(rh.shell.boolean_shell_value(v, True))
141*d68f33bcSAndroid Build Coastguard Worker            self.assertTrue(rh.shell.boolean_shell_value(v, False))
142*d68f33bcSAndroid Build Coastguard Worker
143*d68f33bcSAndroid Build Coastguard Worker        for v in ('no', 'NO', 'nO', 'n', 'N', '0', 'false', 'False', 'FALSE',):
144*d68f33bcSAndroid Build Coastguard Worker            self.assertFalse(rh.shell.boolean_shell_value(v, True))
145*d68f33bcSAndroid Build Coastguard Worker            self.assertFalse(rh.shell.boolean_shell_value(v, False))
146*d68f33bcSAndroid Build Coastguard Worker
147*d68f33bcSAndroid Build Coastguard Worker
148*d68f33bcSAndroid Build Coastguard Workerif __name__ == '__main__':
149*d68f33bcSAndroid Build Coastguard Worker    unittest.main()
150