bug 1310980, part 8: defined and tested behaviour for all-whitespace files draft
authorAxel Hecht <axel@pike.org>
Mon, 24 Oct 2016 17:56:20 +0200
changeset 150 2c6171acb00d268723913c7277ec21c7765c138a
parent 149 e6f765f205272f7569cb02cbe726020424414568
push id31
push useraxel@mozilla.com
push dateMon, 24 Oct 2016 16:06:06 +0000
bugs1310980
bug 1310980, part 8: defined and tested behaviour for all-whitespace files MozReview-Commit-ID: ARYGW2hrHkw
compare_locales/parser.py
compare_locales/tests/test_defines.py
compare_locales/tests/test_dtd.py
compare_locales/tests/test_ini.py
compare_locales/tests/test_properties.py
--- a/compare_locales/parser.py
+++ b/compare_locales/parser.py
@@ -156,18 +156,34 @@ class Junk(object):
     # getters
     all = property(get_all)
     val = property(get_all)
 
     def __repr__(self):
         return self.key
 
 
+class Whitespace(Entity):
+    '''Entity-like object representing an empty file with whitespace,
+    if allowed
+    '''
+    def __init__(self, ctx, span):
+        self.ctx = ctx
+        self.key_span = self.val_span = self.span = span
+        self.def_span = self.pre_ws_span = (span[0], span[0])
+        self.post_span = (span[1], span[1])
+        self.pp = lambda v: v
+
+    def __repr__(self):
+        return self.raw_val
+
+
 class Parser:
     canMerge = True
+    tail = re.compile('\s+\Z')
 
     class Context(object):
         "Fixture for content and line numbers"
         def __init__(self, contents):
             self.contents = contents
             self._lines = None
 
         def lines(self, *positions):
@@ -244,19 +260,23 @@ class Parser:
         return self.getTrailing(ctx, offset, self.reKey, self.reComment)
 
     def getTrailing(self, ctx, offset, *expressions):
         junkend = None
         for exp in expressions:
             m = exp.search(ctx.contents, offset)
             if m:
                 junkend = min(junkend, m.start()) if junkend else m.start()
-        if junkend is not None:
-            return (Junk(ctx, (offset, junkend)), junkend)
-        return (None, offset)
+        if junkend is None:
+            if self.tail.match(ctx.contents, offset):
+                white_end = len(ctx.contents)
+                return (Whitespace(ctx, (offset, white_end)), white_end)
+            else:
+                return (None, offset)
+        return (Junk(ctx, (offset, junkend)), junkend)
 
     def createEntity(self, ctx, m):
         pre_comment = str(self.last_comment) if self.last_comment else ''
         self.last_comment = ''
         return Entity(ctx, self.postProcessValue, pre_comment,
                       *[m.span(i) for i in xrange(6)])
 
 
@@ -434,16 +454,17 @@ class DefinesInstruction(Entity):
 
     def __repr__(self):
         return self.raw_val
 
 
 class DefinesParser(Parser):
     # can't merge, #unfilter needs to be the last item, which we don't support
     canMerge = False
+    tail = re.compile(r'(?!)')  # never match
 
     def __init__(self):
         self.reComment = re.compile(
             '((?:[ \t]*\n)*)'
             '((?:^# .*?(?:\n|\Z))+)'
             '((?:[ \t]*(?:\n|\Z))*)', re.M)
         self.reKey = re.compile('((?:[ \t]*\n)*)'
                                 '(#define[ \t]+(\w+)[ \t]+(.*?)(?:\n|\Z))'
--- a/compare_locales/tests/test_defines.py
+++ b/compare_locales/tests/test_defines.py
@@ -60,11 +60,21 @@ class TestDefinesParser(ParserTestMixin,
     def testToolkit(self):
         self._test('''#define MOZ_LANG_TITLE English (US)
 ''', (
             ('MOZ_LANG_TITLE', 'English (US)'),))
 
     def testToolkitEmpty(self):
         self._test('', tuple())
 
+    def test_empty_file(self):
+        '''Test that empty files generate errors
+
+        defines.inc are interesting that way, as their
+        content is added to the generated file.
+        '''
+        self._test('\n', (('Junk', '\n'),))
+        self._test('\n\n', (('Junk', '\n\n'),))
+        self._test(' \n\n', (('Junk', ' \n\n'),))
+
 
 if __name__ == '__main__':
     unittest.main()
--- a/compare_locales/tests/test_dtd.py
+++ b/compare_locales/tests/test_dtd.py
@@ -98,16 +98,22 @@ class TestDTD(ParserTestMixin, unittest.
     def test_trailing_whitespace(self):
         self._test('<!ENTITY foo.label "stuff">\n  \n',
                    (('foo.label', 'stuff'),))
 
     def test_unicode_comment(self):
         self._test('<!-- \xe5\x8f\x96 -->',
                    (('Comment', u'\u53d6'),))
 
+    def test_empty_file(self):
+        self._test('', tuple())
+        self._test('\n', (('Whitespace', '\n'),))
+        self._test('\n\n', (('Whitespace', '\n\n'),))
+        self._test(' \n\n', (('Whitespace', ' \n\n'),))
+
     def test_positions(self):
         self.parser.readContents('''\
 <!ENTITY one  "value">
 <!ENTITY  two "other
 escaped value">
 ''')
         one, two = list(self.parser)
         self.assertEqual(one.position(), (1, 1))
--- a/compare_locales/tests/test_ini.py
+++ b/compare_locales/tests/test_ini.py
@@ -124,10 +124,16 @@ Junk
 Good=other string
 ''', (
             ('Comment', mpl2),
             ('IniSection', 'Strings'),
             ('TitleText', 'Some Title'),
             ('Junk', 'Junk'),
             ('Good', 'other string')))
 
+    def test_empty_file(self):
+        self._test('', tuple())
+        self._test('\n', (('Whitespace', '\n'),))
+        self._test('\n\n', (('Whitespace', '\n\n'),))
+        self._test(' \n\n', (('Whitespace', ' \n\n'),))
+
 if __name__ == '__main__':
     unittest.main()
--- a/compare_locales/tests/test_properties.py
+++ b/compare_locales/tests/test_properties.py
@@ -121,16 +121,22 @@ foo = bar
         self._test('''\
 # LOCALIZATION NOTE These strings are used inside the Promise debugger
 # which is available as a panel in the Debugger.
 
 
 
 ''',  (('Comment', 'LOCALIZATION NOTE'),))
 
+    def test_empty_file(self):
+        self._test('', tuple())
+        self._test('\n', (('Whitespace', '\n'),))
+        self._test('\n\n', (('Whitespace', '\n\n'),))
+        self._test(' \n\n', (('Whitespace', ' \n\n'),))
+
     def test_positions(self):
         self.parser.readContents('''\
 one = value
 two = other \\
 escaped value
 ''')
         one, two = list(self.parser)
         self.assertEqual(one.position(), (1, 1))