#!/usr/bin/env python # Copyright 2014 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. import difflib import os import re import unittest from strict_enum_value_checker import StrictEnumValueChecker class MockLogging(object): def __init__(self): self.lines = [] def info(self, message): self.lines.append(message) def debug(self, message): self.lines.append(message) class MockInputApi(object): def __init__(self): self.re = re self.os_path = os.path self.files = [] self.is_committing = False self.logging = MockLogging() def AffectedFiles(self, include_deletes=None): return self.files class MockOutputApi(object): class PresubmitResult(object): def __init__(self, message, items=None, long_text=""): self.message = message self.items = items self.long_text = long_text class PresubmitError(PresubmitResult): def __init__(self, message, items, long_text=""): MockOutputApi.PresubmitResult.__init__(self, message, items, long_text) self.type = "error" class PresubmitPromptWarning(PresubmitResult): def __init__(self, message, items, long_text=""): MockOutputApi.PresubmitResult.__init__(self, message, items, long_text) self.type = "warning" class PresubmitNotifyResult(PresubmitResult): def __init__(self, message, items, long_text=""): MockOutputApi.PresubmitResult.__init__(self, message, items, long_text) self.type = "notify" class MockFile(object): def __init__(self, local_path, old_contents, new_contents): self._local_path = local_path self._new_contents = new_contents self._old_contents = old_contents self._cached_changed_contents = None def ChangedContents(self): return self._changed_contents def NewContents(self): return self._new_contents def LocalPath(self): return self._local_path def IsDirectory(self): return False def GenerateScmDiff(self): result = "" for line in difflib.unified_diff(self._old_contents, self._new_contents, self._local_path, self._local_path): result += line return result # NOTE: This method is a copy of ChangeContents method of AffectedFile in # presubmit_support.py def ChangedContents(self): """Returns a list of tuples (line number, line text) of all new lines. This relies on the scm diff output describing each changed code section with a line of the form ^@@ , , @@$ """ if self._cached_changed_contents is not None: return self._cached_changed_contents[:] self._cached_changed_contents = [] line_num = 0 if self.IsDirectory(): return [] for line in self.GenerateScmDiff().splitlines(): m = re.match(r"^@@ [0-9\,\+\-]+ \+([0-9]+)\,[0-9]+ @@", line) if m: line_num = int(m.groups(1)[0]) continue if line.startswith("+") and not line.startswith("++"): self._cached_changed_contents.append((line_num, line[1:])) if not line.startswith("-"): line_num += 1 return self._cached_changed_contents[:] class MockChange(object): def __init__(self, changed_files): self._changed_files = changed_files def LocalPaths(self): return self._changed_files class StrictEnumValueCheckerTest(unittest.TestCase): TEST_FILE_PATTERN = "changed_file_%s.h" MOCK_FILE_LOCAL_PATH = "mock_enum.h" START_MARKER = "enum MockEnum {" END_MARKER = " mBoundary" def _ReadTextFileContents(self, path): """Given a path, returns a list of strings corresponding to the text lines in the file. Reads files in text format. """ fo = open(path, "r") try: contents = fo.readlines() finally: fo.close() return contents def _ReadInputFile(self): return self._ReadTextFileContents("mock_enum.h") def _PrepareTest(self, new_file_path): old_contents = self._ReadInputFile() if not new_file_path: new_contents = [] else: new_contents = self._ReadTextFileContents(new_file_path) input_api = MockInputApi() mock_file = MockFile(self.MOCK_FILE_LOCAL_PATH, old_contents, new_contents) input_api.files.append(mock_file) output_api = MockOutputApi() return input_api, output_api def _RunTest(self, new_file_path): input_api, output_api = self._PrepareTest(new_file_path) checker = StrictEnumValueChecker(input_api, output_api, self.START_MARKER, self.END_MARKER, self.MOCK_FILE_LOCAL_PATH) results = checker.Run() return results def testDeleteFile(self): results = self._RunTest(new_file_path=None) # TODO(rpaquay) How to check it's the expected warning?' self.assertEquals(1, len(results), "We should get a single warning about file deletion.") def testSimpleValidEdit(self): results = self._RunTest(self.TEST_FILE_PATTERN % "1") # TODO(rpaquay) How to check it's the expected warning?' self.assertEquals(0, len(results), "We should get no warning for simple edits.") def testSingleDeletionOfEntry(self): results = self._RunTest(self.TEST_FILE_PATTERN % "2") # TODO(rpaquay) How to check it's the expected warning?' self.assertEquals(1, len(results), "We should get a warning for an entry deletion.") def testSingleRenameOfEntry(self): results = self._RunTest(self.TEST_FILE_PATTERN % "3") # TODO(rpaquay) How to check it's the expected warning?' self.assertEquals(1, len(results), "We should get a warning for an entry rename, even " "though it is not optimal.") def testMissingEnumStartOfEntry(self): results = self._RunTest(self.TEST_FILE_PATTERN % "4") # TODO(rpaquay) How to check it's the expected warning?' self.assertEquals(1, len(results), "We should get a warning for a missing enum marker.") def testMissingEnumEndOfEntry(self): results = self._RunTest(self.TEST_FILE_PATTERN % "5") # TODO(rpaquay) How to check it's the expected warning?' self.assertEquals(1, len(results), "We should get a warning for a missing enum marker.") def testInvertedEnumMarkersOfEntry(self): results = self._RunTest(self.TEST_FILE_PATTERN % "6") # TODO(rpaquay) How to check it's the expected warning?' self.assertEquals(1, len(results), "We should get a warning for inverted enum markers.") def testMultipleInvalidEdits(self): results = self._RunTest(self.TEST_FILE_PATTERN % "7") # TODO(rpaquay) How to check it's the expected warning?' self.assertEquals(3, len(results), "We should get 3 warnings (one per edit).") def testSingleInvalidInserts(self): results = self._RunTest(self.TEST_FILE_PATTERN % "8") # TODO(rpaquay) How to check it's the expected warning?' self.assertEquals(1, len(results), "We should get a warning for a single invalid " "insertion inside the enum.") def testMulitpleValidInserts(self): results = self._RunTest(self.TEST_FILE_PATTERN % "9") # TODO(rpaquay) How to check it's the expected warning?' self.assertEquals(0, len(results), "We should not get a warning mulitple valid edits") def testSingleValidDeleteOutsideOfEnum(self): results = self._RunTest(self.TEST_FILE_PATTERN % "10") # TODO(rpaquay) How to check it's the expected warning?' self.assertEquals(0, len(results), "We should not get a warning for a deletion outside of " "the enum") if __name__ == '__main__': unittest.main()