Repository: buildbot/buildbot

Issue:
unicode literrals
based on irc discussion with @rjarry  


Discussion:

Resolving patch:
diff --git a/master/buildbot/util/lineboundaries.py b/master/buildbot/util/lineboundaries.py
--- a/master/buildbot/util/lineboundaries.py
+++ b/master/buildbot/util/lineboundaries.py
@@ -13,6 +13,8 @@
 #
 # Copyright Buildbot Team Members
 
+import re
+
 from twisted.internet import defer
 
 
@@ -20,6 +22,10 @@ class LineBoundaryFinder(object):
 
     __slots__ = ['partialLine', 'callback']
 
+    # the lookahead here (`(?=.)`) ensures that `\r` doesn't match at the end
+    # of the buffer
+    newline_re = re.compile(r'(\r\n|\r(?=.)|\n)')
+
     def __init__(self, callback):
         self.partialLine = None
         self.callback = callback
@@ -28,6 +34,7 @@ def append(self, text):
         if self.partialLine:
             text = self.partialLine + text
             self.partialLine = None
+        text = self.newline_re.sub('\n', text)
         if text:
             if text[-1] != '\n':
                 i = text.rfind('\n')


Test patch:
diff --git a/master/buildbot/test/unit/test_util_lineboundaries.py b/master/buildbot/test/unit/test_util_lineboundaries.py
--- a/master/buildbot/test/unit/test_util_lineboundaries.py
+++ b/master/buildbot/test/unit/test_util_lineboundaries.py
@@ -16,6 +16,7 @@
 from buildbot.util import lineboundaries
 from twisted.internet import defer
 from twisted.internet import reactor
+from twisted.python import log
 from twisted.trial import unittest
 
 
@@ -72,6 +73,45 @@ def test_embedded_newlines(self):
         yield self.lbf.flush()
         self.assertCallbacks(['world\n'])
 
+    @defer.inlineCallbacks
+    def test_windows_newlines_folded(self):
+        r"Windows' \r\n is treated as and converted to a newline"
+        yield self.lbf.append('hello, ')
+        self.assertCallbacks([])
+        yield self.lbf.append('cruel\r\n\r\nworld')
+        self.assertCallbacks(['hello, cruel\n\n'])
+        yield self.lbf.flush()
+        self.assertCallbacks(['world\n'])
+
+    @defer.inlineCallbacks
+    def test_bare_cr_folded(self):
+        r"a bare \r is treated as and converted to a newline"
+        yield self.lbf.append('1%\r5%\r15%\r100%\nfinished')
+        yield self.lbf.flush()
+        self.assertCallbacks(['1%\n5%\n15%\n100%\n', 'finished\n'])
+
+    @defer.inlineCallbacks
+    def test_mixed_consecutive_newlines(self):
+        r"mixing newline styles back-to-back doesn't collapse them"
+        yield self.lbf.append('1\r\n\n\r')
+        self.assertCallbacks(['1\n\n'])  # last \r is delayed until flush
+        yield self.lbf.append('2\n\r\n')
+        self.assertCallbacks(['\n2\n\n'])
+
+    @defer.inlineCallbacks
+    def test_split_newlines(self):
+        r"multi-character newlines, split across chunks, are converted"
+        input = 'a\nb\r\nc\rd\n\re'
+        for splitpoint in range(1, len(input) - 1):
+            a, b = input[:splitpoint], input[splitpoint:]
+            yield self.lbf.append(a)
+            yield self.lbf.append(b)
+            yield self.lbf.flush()
+            res = ''.join(self.callbacks)
+            log.msg('feeding %r, %r gives %r' % (a, b, res))
+            self.assertEqual(res, 'a\nb\nc\nd\n\ne\n')
+            self.callbacks = []
+
     def test_empty_flush(self):
         d = self.lbf.flush()