1 #!/usr/bin/env python 2 # Copyright (c) 2011 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 """Unit tests for croc_scan.py.""" 7 8 import re 9 import unittest 10 import croc_scan 11 12 13 class TestScanner(unittest.TestCase): 14 """Tests for croc_scan.Scanner.""" 15 16 def testInit(self): 17 """Test __init()__.""" 18 s = croc_scan.Scanner() 19 20 self.assertEqual(s.re_token.pattern, '#') 21 self.assertEqual(s.comment_to_eol, ['#']) 22 self.assertEqual(s.comment_start, None) 23 self.assertEqual(s.comment_end, None) 24 25 def testScanLines(self): 26 """Test ScanLines().""" 27 s = croc_scan.Scanner() 28 # Set up imaginary language: 29 # ':' = comment to EOL 30 # '"' = string start/end 31 # '(' = comment start 32 # ')' = comment end 33 s.re_token = re.compile(r'([\:\"\(\)])') 34 s.comment_to_eol = [':'] 35 s.comment_start = '(' 36 s.comment_end = ')' 37 38 # No input file = no output lines 39 self.assertEqual(s.ScanLines([]), []) 40 41 # Empty lines and lines with only whitespace are ignored 42 self.assertEqual(s.ScanLines([ 43 '', # 1 44 'line', # 2 exe 45 ' \t ', # 3 46 ]), [2]) 47 48 # Comments to EOL are stripped, but not inside strings 49 self.assertEqual(s.ScanLines([ 50 'test', # 1 exe 51 ' : A comment', # 2 52 '"a : in a string"', # 3 exe 53 'test2 : with comment to EOL', # 4 exe 54 'foo = "a multiline string with an empty line', # 5 exe 55 '', # 6 exe 56 ': and a comment-to-EOL character"', # 7 exe 57 ': done', # 8 58 ]), [1, 3, 4, 5, 6, 7]) 59 60 # Test Comment start/stop detection 61 self.assertEqual(s.ScanLines([ 62 '( a comment on one line)', # 1 63 'text (with a comment)', # 2 exe 64 '( a comment with a : in the middle)', # 3 65 '( a multi-line', # 4 66 ' comment)', # 5 67 'a string "with a ( in it"', # 6 exe 68 'not in a multi-line comment', # 7 exe 69 '(a comment with a " in it)', # 8 70 ': not in a string, so this gets stripped', # 9 71 'more text "with an uninteresting string"', # 10 exe 72 ]), [2, 6, 7, 10]) 73 74 # TODO: Test Scan(). Low priority, since it just wraps ScanLines(). 75 76 77 class TestPythonScanner(unittest.TestCase): 78 """Tests for croc_scan.PythonScanner.""" 79 80 def testScanLines(self): 81 """Test ScanLines().""" 82 s = croc_scan.PythonScanner() 83 84 # No input file = no output lines 85 self.assertEqual(s.ScanLines([]), []) 86 87 self.assertEqual(s.ScanLines([ 88 '# a comment', # 1 89 '', # 2 90 '"""multi-line string', # 3 exe 91 '# not a comment', # 4 exe 92 'end of multi-line string"""', # 5 exe 93 ' ', # 6 94 '"single string with #comment"', # 7 exe 95 '', # 8 96 '\'\'\'multi-line string, single-quote', # 9 exe 97 '# not a comment', # 10 exe 98 'end of multi-line string\'\'\'', # 11 exe 99 '', # 12 100 '"string with embedded \\" is handled"', # 13 exe 101 '# quoted "', # 14 102 '"\\""', # 15 exe 103 '# quoted backslash', # 16 104 '"\\\\"', # 17 exe 105 'main()', # 18 exe 106 '# end', # 19 107 ]), [3, 4, 5, 7, 9, 10, 11, 13, 15, 17, 18]) 108 109 110 class TestCppScanner(unittest.TestCase): 111 """Tests for croc_scan.CppScanner.""" 112 113 def testScanLines(self): 114 """Test ScanLines().""" 115 s = croc_scan.CppScanner() 116 117 # No input file = no output lines 118 self.assertEqual(s.ScanLines([]), []) 119 120 self.assertEqual(s.ScanLines([ 121 '// a comment', # 1 122 '# a preprocessor define', # 2 123 '', # 3 124 '\'#\', \'"\'', # 4 exe 125 '', # 5 126 '/* a multi-line comment', # 6 127 'with a " in it', # 7 128 '*/', # 8 129 '', # 9 130 '"a string with /* and \' in it"', # 10 exe 131 '', # 11 132 '"a multi-line string\\', # 12 exe 133 '// not a comment\\', # 13 exe 134 'ending here"', # 14 exe 135 '', # 15 136 '"string with embedded \\" is handled"', # 16 exe 137 '', # 17 138 'main()', # 18 exe 139 '// end', # 19 140 ]), [4, 10, 12, 13, 14, 16, 18]) 141 142 143 class TestScanFile(unittest.TestCase): 144 """Tests for croc_scan.ScanFile().""" 145 146 class MockScanner(object): 147 """Mock scanner.""" 148 149 def __init__(self, language): 150 """Constructor.""" 151 self.language = language 152 153 def Scan(self, filename): 154 """Mock Scan() method.""" 155 return 'scan %s %s' % (self.language, filename) 156 157 def MockPythonScanner(self): 158 return self.MockScanner('py') 159 160 def MockCppScanner(self): 161 return self.MockScanner('cpp') 162 163 def setUp(self): 164 """Per-test setup.""" 165 # Hook scanners 166 self.old_python_scanner = croc_scan.PythonScanner 167 self.old_cpp_scanner = croc_scan.CppScanner 168 croc_scan.PythonScanner = self.MockPythonScanner 169 croc_scan.CppScanner = self.MockCppScanner 170 171 def tearDown(self): 172 """Per-test cleanup.""" 173 croc_scan.PythonScanner = self.old_python_scanner 174 croc_scan.CppScanner = self.old_cpp_scanner 175 176 def testScanFile(self): 177 """Test ScanFile().""" 178 self.assertEqual(croc_scan.ScanFile('foo', 'python'), 'scan py foo') 179 self.assertEqual(croc_scan.ScanFile('bar1', 'C'), 'scan cpp bar1') 180 self.assertEqual(croc_scan.ScanFile('bar2', 'C++'), 'scan cpp bar2') 181 self.assertEqual(croc_scan.ScanFile('bar3', 'ObjC'), 'scan cpp bar3') 182 self.assertEqual(croc_scan.ScanFile('bar4', 'ObjC++'), 'scan cpp bar4') 183 self.assertEqual(croc_scan.ScanFile('bar', 'fortran'), []) 184 185 186 if __name__ == '__main__': 187 unittest.main() 188