1 #!/usr/bin/env python 2 # Copyright 2014 The Chromium Authors. All rights reserved. 3 # Use of this source code is governed by a BSD-style license that can be 4 # found in the LICENSE file. 5 6 import optparse 7 import os 8 import sys 9 import unittest 10 11 SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) 12 PARENT_DIR = os.path.dirname(SCRIPT_DIR) 13 14 sys.path.append(PARENT_DIR) 15 16 import quote 17 18 verbose = False 19 20 21 # Wrapped versions of the functions that we're testing, so that during 22 # debugging we can more easily see what their inputs were. 23 24 def VerboseQuote(in_string, specials, *args, **kwargs): 25 if verbose: 26 sys.stdout.write('Invoking quote(%s, %s, %s)\n' % 27 (repr(in_string), repr(specials), 28 ', '.join([repr(a) for a in args] + 29 [repr(k) + ':' + repr(v) 30 for k, v in kwargs]))) 31 return quote.quote(in_string, specials, *args, **kwargs) 32 33 34 def VerboseUnquote(in_string, specials, *args, **kwargs): 35 if verbose: 36 sys.stdout.write('Invoking unquote(%s, %s, %s)\n' % 37 (repr(in_string), repr(specials), 38 ', '.join([repr(a) for a in args] + 39 [repr(k) + ':' + repr(v) 40 for k, v in kwargs]))) 41 return quote.unquote(in_string, specials, *args, **kwargs) 42 43 44 class TestQuote(unittest.TestCase): 45 # test utilities 46 def generic_test(self, fn, in_args, expected_out_obj): 47 actual = apply(fn, in_args) 48 self.assertEqual(actual, expected_out_obj) 49 50 def check_invertible(self, in_string, specials, escape='\\'): 51 q = VerboseQuote(in_string, specials, escape) 52 qq = VerboseUnquote(q, specials, escape) 53 self.assertEqual(''.join(qq), in_string) 54 55 def run_test_tuples(self, test_tuples): 56 for func, in_args, expected in test_tuples: 57 self.generic_test(func, in_args, expected) 58 59 def testQuote(self): 60 test_tuples = [[VerboseQuote, 61 ['foo, bar, baz, and quux too!', 'abc'], 62 'foo, \\b\\ar, \\b\\az, \\and quux too!'], 63 [VerboseQuote, 64 ['when \\ appears in the input', 'a'], 65 'when \\\\ \\appe\\ars in the input']] 66 self.run_test_tuples(test_tuples) 67 68 def testUnquote(self): 69 test_tuples = [[VerboseUnquote, 70 ['key\\:still_key:value\\:more_value', ':'], 71 ['key:still_key', ':', 'value:more_value']], 72 [VerboseUnquote, 73 ['about that sep\\ar\\ator in the beginning', 'ab'], 74 ['', 'ab', 'out th', 'a', 't separator in the ', 75 'b', 'eginning']], 76 [VerboseUnquote, 77 ['the rain in spain fall\\s ma\\i\\nly on the plains', 78 'ins'], 79 ['the ra', 'in', ' ', 'in', ' ', 's', 'pa', 'in', 80 ' falls mainly o', 'n', ' the pla', 'ins']], 81 ] 82 self.run_test_tuples(test_tuples) 83 84 def testInvertible(self): 85 self.check_invertible('abcdefg', 'bc') 86 self.check_invertible('a\\bcdefg', 'bc') 87 self.check_invertible('ab\\cdefg', 'bc') 88 self.check_invertible('\\ab\\cdefg', 'abc') 89 self.check_invertible('abcde\\fg', 'efg') 90 self.check_invertible('a\\b', '') 91 92 93 # Invoke this file directly for simple manual testing. For running 94 # the unittests, use the -t flag. Any flags to be passed to the 95 # unittest module should be passed as after the optparse processing, 96 # e.g., "quote_test.py -t -- -v" to pass the -v flag to the unittest 97 # module. 98 99 def main(argv): 100 global verbose 101 parser = optparse.OptionParser( 102 usage='Usage: %prog [options] word...') 103 parser.add_option('-s', '--special-chars', dest='special_chars', default=':', 104 help='Special characters to quote (default is ":")') 105 parser.add_option('-q', '--quote', dest='quote', default='\\', 106 help='Quote or escape character (default is "\")') 107 parser.add_option('-t', '--run-tests', dest='tests', action='store_true', 108 help='Run built-in tests\n') 109 parser.add_option('-u', '--unquote-input', dest='unquote_input', 110 action='store_true', help='Unquote command line argument') 111 parser.add_option('-v', '--verbose', dest='verbose', action='store_true', 112 help='Verbose test output') 113 options, args = parser.parse_args(argv) 114 115 if options.verbose: 116 verbose = True 117 118 num_errors = 0 119 if options.tests: 120 sys.argv = [sys.argv[0]] + args 121 unittest.main() 122 else: 123 for word in args: 124 # NB: there are inputs x for which quote(unquote(x) != x, but 125 # there should be no input x for which unquote(quote(x)) != x. 126 if options.unquote_input: 127 qq = quote.unquote(word, options.special_chars, options.quote) 128 sys.stdout.write('unquote(%s) = %s\n' 129 % (word, ''.join(qq))) 130 # There is no expected output for unquote -- this is just for 131 # manual testing, so it is okay that we do not (and cannot) 132 # update num_errors here. 133 else: 134 q = quote.quote(word, options.special_chars, options.quote) 135 qq = quote.unquote(q, options.special_chars, options.quote) 136 sys.stdout.write('quote(%s) = %s, unquote(%s) = %s\n' 137 % (word, q, q, ''.join(qq))) 138 if word != ''.join(qq): 139 num_errors += 1 140 if num_errors > 0: 141 sys.stderr.write('[ FAILED ] %d test failures\n' % num_errors) 142 return num_errors 143 144 if __name__ == '__main__': 145 sys.exit(main(sys.argv[1:])) 146