Home | History | Annotate | Download | only in extensions
      1 #!/usr/bin/env python
      2 # Copyright (c) 2012 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 difflib
      7 import os
      8 import re
      9 import unittest
     10 
     11 import PRESUBMIT
     12 
     13 class MockLogging(object):
     14   def __init__(self):
     15     self.lines = []
     16 
     17   def info(self, message):
     18     self.lines.append(message)
     19 
     20   def debug(self, message):
     21     self.lines.append(message)
     22 
     23 class MockInputApi(object):
     24   def __init__(self):
     25     self.re = re
     26     self.os_path = os.path
     27     self.files = []
     28     self.is_committing = False
     29     self.logging = MockLogging()
     30 
     31   def AffectedFiles(self, include_deletes=None):
     32     return self.files
     33 
     34 
     35 class MockOutputApi(object):
     36   class PresubmitResult(object):
     37     def __init__(self, message, items=None, long_text=''):
     38       self.message = message
     39       self.items = items
     40       self.long_text = long_text
     41 
     42   class PresubmitError(PresubmitResult):
     43     def __init__(self, message, items, long_text=''):
     44       MockOutputApi.PresubmitResult.__init__(self, message, items, long_text)
     45       self.type = 'error'
     46 
     47   class PresubmitPromptWarning(PresubmitResult):
     48     def __init__(self, message, items, long_text=''):
     49       MockOutputApi.PresubmitResult.__init__(self, message, items, long_text)
     50       self.type = 'warning'
     51 
     52   class PresubmitNotifyResult(PresubmitResult):
     53     def __init__(self, message, items, long_text=''):
     54       MockOutputApi.PresubmitResult.__init__(self, message, items, long_text)
     55       self.type = 'notify'
     56 
     57 
     58 class MockFile(object):
     59   def __init__(self, local_path, old_contents, new_contents):
     60     self._local_path = local_path
     61     self._new_contents = new_contents
     62     self._old_contents = old_contents
     63     self._cached_changed_contents = None
     64 
     65   def ChangedContents(self):
     66     return self._changed_contents
     67 
     68   def NewContents(self):
     69     return self._new_contents
     70 
     71   def LocalPath(self):
     72     return self._local_path
     73 
     74   def IsDirectory(self):
     75     return False
     76 
     77   def GenerateScmDiff(self):
     78     result = ""
     79     for line in difflib.unified_diff(self._old_contents, self._new_contents,
     80                                      self._local_path, self._local_path):
     81       result += line
     82     return result
     83 
     84   # NOTE: This method is a copy of ChangeContents method of AffectedFile in
     85   # presubmit_support.py
     86   def ChangedContents(self):
     87     """Returns a list of tuples (line number, line text) of all new lines.
     88 
     89      This relies on the scm diff output describing each changed code section
     90      with a line of the form
     91 
     92      ^@@ <old line num>,<old size> <new line num>,<new size> @@$
     93     """
     94     if self._cached_changed_contents is not None:
     95       return self._cached_changed_contents[:]
     96     self._cached_changed_contents = []
     97     line_num = 0
     98 
     99     if self.IsDirectory():
    100       return []
    101 
    102     for line in self.GenerateScmDiff().splitlines():
    103       m = re.match(r'^@@ [0-9\,\+\-]+ \+([0-9]+)\,[0-9]+ @@', line)
    104       if m:
    105         line_num = int(m.groups(1)[0])
    106         continue
    107       if line.startswith('+') and not line.startswith('++'):
    108         self._cached_changed_contents.append((line_num, line[1:]))
    109       if not line.startswith('-'):
    110         line_num += 1
    111     return self._cached_changed_contents[:]
    112 
    113 
    114 class MockChange(object):
    115   def __init__(self, changed_files):
    116     self._changed_files = changed_files
    117 
    118   def LocalPaths(self):
    119     return self._changed_files
    120 
    121 
    122 class HistogramValueCheckerTest(unittest.TestCase):
    123   TEST_FILE_PATTERN = "PRESUBMIT_test_new_file_%s.txt"
    124 
    125   def _ReadTextFileContents(self, path):
    126     """Given a path, returns a list of strings corresponding to the text lines
    127     in the file. Reads files in text format.
    128 
    129     """
    130     fo = open(path, 'r')
    131     try:
    132       contents = fo.readlines()
    133     finally:
    134       fo.close()
    135     return contents
    136 
    137   def _ReadInputFile(self):
    138     return self._ReadTextFileContents("PRESUBMIT_test_old_file.txt")
    139 
    140   def _PrepareTest(self, new_file_path):
    141     old_contents = self._ReadInputFile()
    142     if not new_file_path:
    143       new_contents = []
    144     else:
    145       new_contents = self._ReadTextFileContents(new_file_path)
    146     input_api = MockInputApi()
    147     mock_file = MockFile(PRESUBMIT.HistogramValueChecker.LOCAL_PATH,
    148                          old_contents,
    149                          new_contents)
    150     input_api.files.append(mock_file)
    151     output_api = MockOutputApi()
    152     return input_api, output_api
    153 
    154   def _RunTest(self, new_file_path):
    155     input_api, output_api = self._PrepareTest(new_file_path)
    156     checker = PRESUBMIT.HistogramValueChecker(input_api, output_api)
    157     results = checker.Run()
    158     return results
    159 
    160   def testDeleteFile(self):
    161     results = self._RunTest(new_file_path=None)
    162     # TODO(rpaquay) How to check it's the expected warning?'
    163     self.assertEquals(1, len(results),
    164                       "We hould get a single warning about file deletion.")
    165 
    166   def testSimpleValidEdit(self):
    167     results = self._RunTest(self.TEST_FILE_PATTERN % "1")
    168     # TODO(rpaquay) How to check it's the expected warning?'
    169     self.assertEquals(0, len(results),
    170                       "We should get no warning for simple edits.")
    171 
    172   def testSingleDeletionOfEntry(self):
    173     results = self._RunTest(self.TEST_FILE_PATTERN % "2")
    174     # TODO(rpaquay) How to check it's the expected warning?'
    175     self.assertEquals(1, len(results),
    176                       "We should get a warning for an entry deletion.")
    177 
    178   def testSingleRenameOfEntry(self):
    179     results = self._RunTest(self.TEST_FILE_PATTERN % "3")
    180     # TODO(rpaquay) How to check it's the expected warning?'
    181     self.assertEquals(1, len(results),
    182                       "We should get a warning for an entry rename, even "
    183                       "though it is not optimal.")
    184 
    185   def testMissingEnumStartOfEntry(self):
    186     results = self._RunTest(self.TEST_FILE_PATTERN % "4")
    187     # TODO(rpaquay) How to check it's the expected warning?'
    188     self.assertEquals(1, len(results),
    189                       "We should get a warning for a missing enum marker.")
    190 
    191   def testMissingEnumEndOfEntry(self):
    192     results = self._RunTest(self.TEST_FILE_PATTERN % "5")
    193     # TODO(rpaquay) How to check it's the expected warning?'
    194     self.assertEquals(1, len(results),
    195                       "We should get a warning for a missing enum marker.")
    196 
    197   def testInvertedEnumMarkersOfEntry(self):
    198     results = self._RunTest(self.TEST_FILE_PATTERN % "6")
    199     # TODO(rpaquay) How to check it's the expected warning?'
    200     self.assertEquals(1, len(results),
    201                       "We should get a warning for inverted enum markers.")
    202 
    203   def testMultipleInvalidEdits(self):
    204     results = self._RunTest(self.TEST_FILE_PATTERN % "7")
    205     # TODO(rpaquay) How to check it's the expected warning?'
    206     self.assertEquals(3, len(results),
    207                       "We should get 3 warnings (one per edit).")
    208 
    209   def testSingleInvalidInserts(self):
    210     results = self._RunTest(self.TEST_FILE_PATTERN % "8")
    211     # TODO(rpaquay) How to check it's the expected warning?'
    212     self.assertEquals(1, len(results),
    213                       "We should get a warning for a single invalid "
    214                       "insertion inside the enum.")
    215 
    216   def testMulitpleValidInserts(self):
    217     results = self._RunTest(self.TEST_FILE_PATTERN % "9")
    218     # TODO(rpaquay) How to check it's the expected warning?'
    219     self.assertEquals(0, len(results),
    220                       "We should not get a warning mulitple valid edits")
    221 
    222   def testSingleValidDeleteOutsideOfEnum(self):
    223     results = self._RunTest(self.TEST_FILE_PATTERN % "10")
    224     # TODO(rpaquay) How to check it's the expected warning?'
    225     self.assertEquals(0, len(results),
    226                       "We should not get a warning for a deletion outside of "
    227                       "the enum")
    228 
    229 
    230 if __name__ == '__main__':
    231   unittest.main()
    232