diff options
-rw-r--r-- | pygerrit/__init__.py | 85 | ||||
-rwxr-xr-x | unittests.py | 112 |
2 files changed, 197 insertions, 0 deletions
diff --git a/pygerrit/__init__.py b/pygerrit/__init__.py index 7424b74..d9ce98d 100644 --- a/pygerrit/__init__.py +++ b/pygerrit/__init__.py @@ -48,3 +48,88 @@ def escape_string(string): result = result.replace('\\', '\\\\') result = result.replace('"', '\\"') return '"' + result + '"' + + +class GerritReviewMessageFormatter(object): + + """ Helper class to format review messages that are sent to Gerrit. """ + + def __init__(self, header=None, footer=None): + """ Constructor. + + If `header` is specified, it will be prepended as the first + paragraph of the output message. If `footer` is specified it + will be appended as the last paragraph of the output message. + + """ + + self.paragraphs = [] + if header: + self.header = header.strip() + else: + self.header = "" + if footer: + self.footer = footer.strip() + else: + self.footer = "" + + def append(self, data): + """ Append the given `data` to the output. + + If `data` is a list, it is formatted as a bullet list with each + entry in the list being a separate bullet. Otherwise if it is a + string, the string is added as a paragraph. + + Raises ValueError if `data` is not a list or a string. + + """ + if not data: + return + + if isinstance(data, list): + # First we need to clean up the data. + # + # Gerrit creates new bullet items when it gets newline characters + # within a bullet list paragraph, so unless we remove the newlines + # from the texts the resulting bullet list will contain multiple + # bullets and look crappy. + # + # We add the '*' character on the beginning of each bullet text in + # the next step, so we strip off any existing leading '*' that the + # caller has added, and then strip off any leading or trailing + # whitespace. + _items = [x.replace("\n", " ").strip().lstrip('*').strip() + for x in data] + + # Create the bullet list only with the items that still have any + # text in them after cleaning up. + _paragraph = "\n".join(["* %s" % x for x in _items if x]) + if _paragraph: + self.paragraphs.append(_paragraph) + elif isinstance(data, str): + _paragraph = data.strip() + if _paragraph: + self.paragraphs.append(_paragraph) + else: + raise ValueError('Data must be a list or a string') + + def is_empty(self): + """ Return True if no paragraphs have been added. """ + return not self.paragraphs + + def format(self): + """ Format the message parts to a string. + + Return a string of all the message parts separated into paragraphs, + with header and footer paragraphs if they were specified in the + constructor. + + """ + message = "" + if self.paragraphs: + if self.header: + message += (self.header + '\n\n') + message += "\n\n".join(self.paragraphs) + if self.footer: + message += ('\n\n' + self.footer) + return message diff --git a/unittests.py b/unittests.py index b4b3a23..28d985f 100755 --- a/unittests.py +++ b/unittests.py @@ -35,6 +35,84 @@ from pygerrit.events import PatchsetCreatedEvent, \ DraftPublishedEvent, GerritEventFactory, GerritEvent, UnhandledEvent, \ ErrorEvent, MergeFailedEvent, ReviewerAddedEvent, TopicChangedEvent from pygerrit.client import GerritClient +from pygerrit import GerritReviewMessageFormatter + +EXPECTED_TEST_CASE_FIELDS = ['header', 'footer', 'paragraphs', 'result'] + + +TEST_CASES = [ + {'header': None, + 'footer': None, + 'paragraphs': [], + 'result': ""}, + {'header': "Header", + 'footer': "Footer", + 'paragraphs': [], + 'result': ""}, + {'header': None, + 'footer': None, + 'paragraphs': ["Test"], + 'result': "Test"}, + {'header': None, + 'footer': None, + 'paragraphs': ["Test", "Test"], + 'result': "Test\n\nTest"}, + {'header': "Header", + 'footer': None, + 'paragraphs': ["Test"], + 'result': "Header\n\nTest"}, + {'header': "Header", + 'footer': None, + 'paragraphs': ["Test", "Test"], + 'result': "Header\n\nTest\n\nTest"}, + {'header': "Header", + 'footer': "Footer", + 'paragraphs': ["Test", "Test"], + 'result': "Header\n\nTest\n\nTest\n\nFooter"}, + {'header': "Header", + 'footer': "Footer", + 'paragraphs': [["One"]], + 'result': "Header\n\n* One\n\nFooter"}, + {'header': "Header", + 'footer': "Footer", + 'paragraphs': [["One", "Two"]], + 'result': "Header\n\n* One\n* Two\n\nFooter"}, + {'header': "Header", + 'footer': "Footer", + 'paragraphs': ["Test", ["One"], "Test"], + 'result': "Header\n\nTest\n\n* One\n\nTest\n\nFooter"}, + {'header': "Header", + 'footer': "Footer", + 'paragraphs': ["Test", ["One", "Two"], "Test"], + 'result': "Header\n\nTest\n\n* One\n* Two\n\nTest\n\nFooter"}, + {'header': "Header", + 'footer': "Footer", + 'paragraphs': ["Test", "Test", ["One"]], + 'result': "Header\n\nTest\n\nTest\n\n* One\n\nFooter"}, + {'header': None, + 'footer': None, + 'paragraphs': [["* One", "* Two"]], + 'result': "* One\n* Two"}, + {'header': None, + 'footer': None, + 'paragraphs': [["* One ", " * Two "]], + 'result': "* One\n* Two"}, + {'header': None, + 'footer': None, + 'paragraphs': [["*", "*"]], + 'result': ""}, + {'header': None, + 'footer': None, + 'paragraphs': [["", ""]], + 'result': ""}, + {'header': None, + 'footer': None, + 'paragraphs': [[" ", " "]], + 'result': ""}, + {'header': None, + 'footer': None, + 'paragraphs': [["* One", " ", "* Two"]], + 'result': "* One\n* Two"}] @GerritEventFactory.register("user-defined-event") @@ -315,5 +393,39 @@ class TestGerritEvents(unittest.TestCase): return self.fail("Did not raise exception when duplicate event registered") + +class TestGerritReviewMessageFormatter(unittest.TestCase): + + """ Test that the GerritReviewMessageFormatter class behaves properly. """ + + def _check_test_case_fields(self, test_case, i): + for field in EXPECTED_TEST_CASE_FIELDS: + self.assertTrue(field in test_case, + "field '%s' not present in test case #%d" % + (field, i)) + self.assertTrue(isinstance(test_case['paragraphs'], list), + "'paragraphs' field is not a list in test case #%d" % i) + + def test_is_empty(self): + """ Test if message is empty for missing header and footer. """ + f = GerritReviewMessageFormatter(header=None, footer=None) + self.assertTrue(f.is_empty()) + f.append(['test']) + self.assertFalse(f.is_empty()) + + def test_message_formatting(self): + """ Test message formatter for different test cases. """ + for i in range(len(TEST_CASES)): + test_case = TEST_CASES[i] + self._check_test_case_fields(test_case, i) + f = GerritReviewMessageFormatter(header=test_case['header'], + footer=test_case['footer']) + for paragraph in test_case['paragraphs']: + f.append(paragraph) + m = f.format() + self.assertEqual(m, test_case['result'], + "Formatted message does not match expected " + "result in test case #%d:\n[%s]" % (i, m)) + if __name__ == '__main__': unittest.main() |