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
8import re
9import unittest
10import croc_scan
11
12
13class 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
77class 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
110class 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
143class 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
186if __name__ == '__main__':
187  unittest.main()
188