普通文本  |  188行  |  7.01 KB

#!/usr/bin/env python
# Copyright (c) 2011 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""Unit tests for croc_scan.py."""

import re
import unittest
import croc_scan


class TestScanner(unittest.TestCase):
  """Tests for croc_scan.Scanner."""

  def testInit(self):
    """Test __init()__."""
    s = croc_scan.Scanner()

    self.assertEqual(s.re_token.pattern, '#')
    self.assertEqual(s.comment_to_eol, ['#'])
    self.assertEqual(s.comment_start, None)
    self.assertEqual(s.comment_end, None)

  def testScanLines(self):
    """Test ScanLines()."""
    s = croc_scan.Scanner()
    # Set up imaginary language:
    #   ':' = comment to EOL
    #   '"' = string start/end
    #   '(' = comment start
    #   ')' = comment end
    s.re_token = re.compile(r'([\:\"\(\)])')
    s.comment_to_eol = [':']
    s.comment_start = '('
    s.comment_end = ')'

    # No input file = no output lines
    self.assertEqual(s.ScanLines([]), [])

    # Empty lines and lines with only whitespace are ignored
    self.assertEqual(s.ScanLines([
        '',             # 1
        'line',         # 2 exe
        ' \t  ',        # 3
    ]), [2])

    # Comments to EOL are stripped, but not inside strings
    self.assertEqual(s.ScanLines([
        'test',                                                 # 1 exe
        '   : A comment',                                       # 2
        '"a : in a string"',                                    # 3 exe
        'test2 : with comment to EOL',                          # 4 exe
        'foo = "a multiline string with an empty line',         # 5 exe
        '',                                                     # 6 exe
        ': and a comment-to-EOL character"',                    # 7 exe
        ': done',                                               # 8
    ]), [1, 3, 4, 5, 6, 7])

    # Test Comment start/stop detection
    self.assertEqual(s.ScanLines([
        '( a comment on one line)',                     # 1
        'text (with a comment)',                        # 2 exe
        '( a comment with a : in the middle)',          # 3
        '( a multi-line',                               # 4
        '  comment)',                                   # 5
        'a string "with a ( in it"',                    # 6 exe
        'not in a multi-line comment',                  # 7 exe
        '(a comment with a " in it)',                   # 8
        ': not in a string, so this gets stripped',     # 9
        'more text "with an uninteresting string"',     # 10 exe
    ]), [2, 6, 7, 10])

  # TODO: Test Scan().  Low priority, since it just wraps ScanLines().


class TestPythonScanner(unittest.TestCase):
  """Tests for croc_scan.PythonScanner."""

  def testScanLines(self):
    """Test ScanLines()."""
    s = croc_scan.PythonScanner()

    # No input file = no output lines
    self.assertEqual(s.ScanLines([]), [])

    self.assertEqual(s.ScanLines([
        '# a comment',                                  # 1
        '',                                             # 2
        '"""multi-line string',                         # 3 exe
        '# not a comment',                              # 4 exe
        'end of multi-line string"""',                  # 5 exe
        '  ',                                           # 6
        '"single string with #comment"',                # 7 exe
        '',                                             # 8
        '\'\'\'multi-line string, single-quote',        # 9 exe
        '# not a comment',                              # 10 exe
        'end of multi-line string\'\'\'',               # 11 exe
        '',                                             # 12
        '"string with embedded \\" is handled"',        # 13 exe
        '# quoted "',                                   # 14
        '"\\""',                                        # 15 exe
        '# quoted backslash',                           # 16
        '"\\\\"',                                       # 17 exe
        'main()',                                       # 18 exe
        '# end',                                        # 19
    ]), [3, 4, 5, 7, 9, 10, 11, 13, 15, 17, 18])


class TestCppScanner(unittest.TestCase):
  """Tests for croc_scan.CppScanner."""

  def testScanLines(self):
    """Test ScanLines()."""
    s = croc_scan.CppScanner()

    # No input file = no output lines
    self.assertEqual(s.ScanLines([]), [])

    self.assertEqual(s.ScanLines([
        '// a comment',                                 # 1
        '# a preprocessor define',                      # 2
        '',                                             # 3
        '\'#\', \'"\'',                                 # 4 exe
        '',                                             # 5
        '/* a multi-line comment',                      # 6
        'with a " in it',                               # 7
        '*/',                                           # 8
        '',                                             # 9
        '"a string with /* and \' in it"',              # 10 exe
        '',                                             # 11
        '"a multi-line string\\',                       # 12 exe
        '// not a comment\\',                           # 13 exe
        'ending here"',                                 # 14 exe
        '',                                             # 15
        '"string with embedded \\" is handled"',        # 16 exe
        '',                                             # 17
        'main()',                                       # 18 exe
        '// end',                                       # 19
    ]), [4, 10, 12, 13, 14, 16, 18])


class TestScanFile(unittest.TestCase):
  """Tests for croc_scan.ScanFile()."""

  class MockScanner(object):
    """Mock scanner."""

    def __init__(self, language):
      """Constructor."""
      self.language = language

    def Scan(self, filename):
      """Mock Scan() method."""
      return 'scan %s %s' % (self.language, filename)

  def MockPythonScanner(self):
    return self.MockScanner('py')

  def MockCppScanner(self):
    return self.MockScanner('cpp')

  def setUp(self):
    """Per-test setup."""
    # Hook scanners
    self.old_python_scanner = croc_scan.PythonScanner
    self.old_cpp_scanner = croc_scan.CppScanner
    croc_scan.PythonScanner = self.MockPythonScanner
    croc_scan.CppScanner = self.MockCppScanner

  def tearDown(self):
    """Per-test cleanup."""
    croc_scan.PythonScanner = self.old_python_scanner
    croc_scan.CppScanner = self.old_cpp_scanner

  def testScanFile(self):
    """Test ScanFile()."""
    self.assertEqual(croc_scan.ScanFile('foo', 'python'), 'scan py foo')
    self.assertEqual(croc_scan.ScanFile('bar1', 'C'), 'scan cpp bar1')
    self.assertEqual(croc_scan.ScanFile('bar2', 'C++'), 'scan cpp bar2')
    self.assertEqual(croc_scan.ScanFile('bar3', 'ObjC'), 'scan cpp bar3')
    self.assertEqual(croc_scan.ScanFile('bar4', 'ObjC++'), 'scan cpp bar4')
    self.assertEqual(croc_scan.ScanFile('bar', 'fortran'), [])


if __name__ == '__main__':
  unittest.main()