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