1# Copyright (C) 2010 Google Inc. All rights reserved.
2#
3# Redistribution and use in source and binary forms, with or without
4# modification, are permitted provided that the following conditions are
5# met:
6#
7#     * Redistributions of source code must retain the above copyright
8# notice, this list of conditions and the following disclaimer.
9#     * Redistributions in binary form must reproduce the above
10# copyright notice, this list of conditions and the following disclaimer
11# in the documentation and/or other materials provided with the
12# distribution.
13#     * Neither the name of Google Inc. nor the names of its
14# contributors may be used to endorse or promote products derived from
15# this software without specific prior written permission.
16#
17# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29import webkitpy.thirdparty.unittest2 as unittest
30
31from webkitpy.common.host_mock import MockHost
32from webkitpy.common.system.outputcapture import OutputCapture
33
34from webkitpy.layout_tests.models.test_configuration import *
35from webkitpy.layout_tests.models.test_expectations import *
36
37try:
38    from collections import OrderedDict
39except ImportError:
40    # Needed for Python < 2.7
41    from webkitpy.thirdparty.ordered_dict import OrderedDict
42
43
44class Base(unittest.TestCase):
45    # Note that all of these tests are written assuming the configuration
46    # being tested is Windows XP, Release build.
47
48    def __init__(self, testFunc):
49        host = MockHost()
50        self._port = host.port_factory.get('test-win-xp', None)
51        self._exp = None
52        unittest.TestCase.__init__(self, testFunc)
53
54    def get_test(self, test_name):
55        # FIXME: Remove this routine and just reference test names directly.
56        return test_name
57
58    def get_basic_tests(self):
59        return [self.get_test('failures/expected/text.html'),
60                self.get_test('failures/expected/image_checksum.html'),
61                self.get_test('failures/expected/crash.html'),
62                self.get_test('failures/expected/needsrebaseline.html'),
63                self.get_test('failures/expected/needsmanualrebaseline.html'),
64                self.get_test('failures/expected/missing_text.html'),
65                self.get_test('failures/expected/image.html'),
66                self.get_test('failures/expected/timeout.html'),
67                self.get_test('passes/text.html')]
68
69
70    def get_basic_expectations(self):
71        return """
72Bug(test) failures/expected/text.html [ Failure ]
73Bug(test) failures/expected/crash.html [ WontFix ]
74Bug(test) failures/expected/needsrebaseline.html [ NeedsRebaseline ]
75Bug(test) failures/expected/needsmanualrebaseline.html [ NeedsManualRebaseline ]
76Bug(test) failures/expected/missing_image.html [ Rebaseline Missing ]
77Bug(test) failures/expected/image_checksum.html [ WontFix ]
78Bug(test) failures/expected/image.html [ WontFix Mac ]
79"""
80
81    def parse_exp(self, expectations, overrides=None, is_lint_mode=False):
82        expectations_dict = OrderedDict()
83        expectations_dict['expectations'] = expectations
84        if overrides:
85            expectations_dict['overrides'] = overrides
86        self._port.expectations_dict = lambda: expectations_dict
87        expectations_to_lint = expectations_dict if is_lint_mode else None
88        self._exp = TestExpectations(self._port, self.get_basic_tests(), expectations_dict=expectations_to_lint, is_lint_mode=is_lint_mode)
89
90    def assert_exp_list(self, test, results):
91        self.assertEqual(self._exp.get_expectations(self.get_test(test)), set(results))
92
93    def assert_exp(self, test, result):
94        self.assert_exp_list(test, [result])
95
96    def assert_bad_expectations(self, expectations, overrides=None):
97        self.assertRaises(ParseError, self.parse_exp, expectations, is_lint_mode=True, overrides=overrides)
98
99
100class BasicTests(Base):
101    def test_basic(self):
102        self.parse_exp(self.get_basic_expectations())
103        self.assert_exp('failures/expected/text.html', FAIL)
104        self.assert_exp_list('failures/expected/image_checksum.html', [WONTFIX, SKIP])
105        self.assert_exp('passes/text.html', PASS)
106        self.assert_exp('failures/expected/image.html', PASS)
107
108
109class MiscTests(Base):
110    def test_multiple_results(self):
111        self.parse_exp('Bug(x) failures/expected/text.html [ Crash Failure ]')
112        self.assertEqual(self._exp.get_expectations(
113            self.get_test('failures/expected/text.html')),
114            set([FAIL, CRASH]))
115
116    def test_result_was_expected(self):
117        # test basics
118        self.assertEqual(TestExpectations.result_was_expected(PASS, set([PASS]), test_needs_rebaselining=False), True)
119        self.assertEqual(TestExpectations.result_was_expected(FAIL, set([PASS]), test_needs_rebaselining=False), False)
120
121        # test handling of SKIPped tests and results
122        self.assertEqual(TestExpectations.result_was_expected(SKIP, set([CRASH]), test_needs_rebaselining=False), True)
123        self.assertEqual(TestExpectations.result_was_expected(SKIP, set([LEAK]), test_needs_rebaselining=False), True)
124
125        # test handling of MISSING results and the REBASELINE specifier
126        self.assertEqual(TestExpectations.result_was_expected(MISSING, set([PASS]), test_needs_rebaselining=True), True)
127        self.assertEqual(TestExpectations.result_was_expected(MISSING, set([PASS]), test_needs_rebaselining=False), False)
128
129        self.assertTrue(TestExpectations.result_was_expected(PASS, set([NEEDS_REBASELINE]), test_needs_rebaselining=False))
130        self.assertTrue(TestExpectations.result_was_expected(MISSING, set([NEEDS_REBASELINE]), test_needs_rebaselining=False))
131        self.assertTrue(TestExpectations.result_was_expected(TEXT, set([NEEDS_REBASELINE]), test_needs_rebaselining=False))
132        self.assertTrue(TestExpectations.result_was_expected(IMAGE, set([NEEDS_REBASELINE]), test_needs_rebaselining=False))
133        self.assertTrue(TestExpectations.result_was_expected(IMAGE_PLUS_TEXT, set([NEEDS_REBASELINE]), test_needs_rebaselining=False))
134        self.assertTrue(TestExpectations.result_was_expected(AUDIO, set([NEEDS_REBASELINE]), test_needs_rebaselining=False))
135        self.assertFalse(TestExpectations.result_was_expected(TIMEOUT, set([NEEDS_REBASELINE]), test_needs_rebaselining=False))
136        self.assertFalse(TestExpectations.result_was_expected(CRASH, set([NEEDS_REBASELINE]), test_needs_rebaselining=False))
137        self.assertFalse(TestExpectations.result_was_expected(LEAK, set([NEEDS_REBASELINE]), test_needs_rebaselining=False))
138
139    def test_remove_pixel_failures(self):
140        self.assertEqual(TestExpectations.remove_pixel_failures(set([FAIL])), set([FAIL]))
141        self.assertEqual(TestExpectations.remove_pixel_failures(set([PASS])), set([PASS]))
142        self.assertEqual(TestExpectations.remove_pixel_failures(set([IMAGE])), set([PASS]))
143        self.assertEqual(TestExpectations.remove_pixel_failures(set([FAIL])), set([FAIL]))
144        self.assertEqual(TestExpectations.remove_pixel_failures(set([PASS, IMAGE, CRASH])), set([PASS, CRASH]))
145
146    def test_suffixes_for_expectations(self):
147        self.assertEqual(TestExpectations.suffixes_for_expectations(set([FAIL])), set(['txt', 'png', 'wav']))
148        self.assertEqual(TestExpectations.suffixes_for_expectations(set([IMAGE])), set(['png']))
149        self.assertEqual(TestExpectations.suffixes_for_expectations(set([FAIL, IMAGE, CRASH])), set(['txt', 'png', 'wav']))
150        self.assertEqual(TestExpectations.suffixes_for_expectations(set()), set())
151
152    def test_category_expectations(self):
153        # This test checks unknown tests are not present in the
154        # expectations and that known test part of a test category is
155        # present in the expectations.
156        exp_str = 'Bug(x) failures/expected [ WontFix ]'
157        self.parse_exp(exp_str)
158        test_name = 'failures/expected/unknown-test.html'
159        unknown_test = self.get_test(test_name)
160        self.assertRaises(KeyError, self._exp.get_expectations,
161                          unknown_test)
162        self.assert_exp_list('failures/expected/crash.html', [WONTFIX, SKIP])
163
164    def test_get_expectations_string(self):
165        self.parse_exp(self.get_basic_expectations())
166        self.assertEqual(self._exp.get_expectations_string(
167                          self.get_test('failures/expected/text.html')),
168                          'FAIL')
169
170    def test_expectation_to_string(self):
171        # Normal cases are handled by other tests.
172        self.parse_exp(self.get_basic_expectations())
173        self.assertRaises(ValueError, self._exp.expectation_to_string,
174                          -1)
175
176    def test_get_test_set(self):
177        # Handle some corner cases for this routine not covered by other tests.
178        self.parse_exp(self.get_basic_expectations())
179        s = self._exp.get_test_set(WONTFIX)
180        self.assertEqual(s,
181            set([self.get_test('failures/expected/crash.html'),
182                 self.get_test('failures/expected/image_checksum.html')]))
183
184    def test_needs_rebaseline_reftest(self):
185        try:
186            filesystem = self._port.host.filesystem
187            filesystem.write_text_file(filesystem.join(self._port.layout_tests_dir(), 'failures/expected/needsrebaseline.html'), 'content')
188            filesystem.write_text_file(filesystem.join(self._port.layout_tests_dir(), 'failures/expected/needsrebaseline-expected.html'), 'content')
189            filesystem.write_text_file(filesystem.join(self._port.layout_tests_dir(), 'failures/expected/needsmanualrebaseline.html'), 'content')
190            filesystem.write_text_file(filesystem.join(self._port.layout_tests_dir(), 'failures/expected/needsmanualrebaseline-expected.html'), 'content')
191            self.parse_exp("""Bug(user) failures/expected/needsrebaseline.html [ NeedsRebaseline ]
192Bug(user) failures/expected/needsmanualrebaseline.html [ NeedsManualRebaseline ]""", is_lint_mode=True)
193            self.assertFalse(True, "ParseError wasn't raised")
194        except ParseError, e:
195            warnings = """expectations:1 A reftest cannot be marked as NeedsRebaseline/NeedsManualRebaseline failures/expected/needsrebaseline.html
196expectations:2 A reftest cannot be marked as NeedsRebaseline/NeedsManualRebaseline failures/expected/needsmanualrebaseline.html"""
197            self.assertEqual(str(e), warnings)
198
199    def test_parse_warning(self):
200        try:
201            filesystem = self._port.host.filesystem
202            filesystem.write_text_file(filesystem.join(self._port.layout_tests_dir(), 'disabled-test.html-disabled'), 'content')
203            self.get_test('disabled-test.html-disabled'),
204            self.parse_exp("Bug(user) [ FOO ] failures/expected/text.html [ Failure ]\n"
205                "Bug(user) non-existent-test.html [ Failure ]\n"
206                "Bug(user) disabled-test.html-disabled [ ImageOnlyFailure ]", is_lint_mode=True)
207            self.assertFalse(True, "ParseError wasn't raised")
208        except ParseError, e:
209            warnings = ("expectations:1 Unrecognized specifier 'foo' failures/expected/text.html\n"
210                        "expectations:2 Path does not exist. non-existent-test.html")
211            self.assertEqual(str(e), warnings)
212
213    def test_parse_warnings_are_logged_if_not_in_lint_mode(self):
214        oc = OutputCapture()
215        try:
216            oc.capture_output()
217            self.parse_exp('-- this should be a syntax error', is_lint_mode=False)
218        finally:
219            _, _, logs = oc.restore_output()
220            self.assertNotEquals(logs, '')
221
222    def test_error_on_different_platform(self):
223        # parse_exp uses a Windows port. Assert errors on Mac show up in lint mode.
224        self.assertRaises(ParseError, self.parse_exp,
225            'Bug(test) [ Mac ] failures/expected/text.html [ Failure ]\nBug(test) [ Mac ] failures/expected/text.html [ Failure ]',
226            is_lint_mode=True)
227
228    def test_error_on_different_build_type(self):
229        # parse_exp uses a Release port. Assert errors on DEBUG show up in lint mode.
230        self.assertRaises(ParseError, self.parse_exp,
231            'Bug(test) [ Debug ] failures/expected/text.html [ Failure ]\nBug(test) [ Debug ] failures/expected/text.html [ Failure ]',
232            is_lint_mode=True)
233
234    def test_overrides(self):
235        self.parse_exp("Bug(exp) failures/expected/text.html [ Failure ]",
236                       "Bug(override) failures/expected/text.html [ ImageOnlyFailure ]")
237        self.assert_exp_list('failures/expected/text.html', [FAIL, IMAGE])
238
239    def test_overrides__directory(self):
240        self.parse_exp("Bug(exp) failures/expected/text.html [ Failure ]",
241                       "Bug(override) failures/expected [ Crash ]")
242        self.assert_exp_list('failures/expected/text.html', [FAIL, CRASH])
243        self.assert_exp_list('failures/expected/image.html', [CRASH])
244
245    def test_overrides__duplicate(self):
246        self.assert_bad_expectations("Bug(exp) failures/expected/text.html [ Failure ]",
247                                     "Bug(override) failures/expected/text.html [ ImageOnlyFailure ]\n"
248                                     "Bug(override) failures/expected/text.html [ Crash ]\n")
249
250    def test_pixel_tests_flag(self):
251        def match(test, result, pixel_tests_enabled):
252            return self._exp.matches_an_expected_result(
253                self.get_test(test), result, pixel_tests_enabled, sanitizer_is_enabled=False)
254
255        self.parse_exp(self.get_basic_expectations())
256        self.assertTrue(match('failures/expected/text.html', FAIL, True))
257        self.assertTrue(match('failures/expected/text.html', FAIL, False))
258        self.assertFalse(match('failures/expected/text.html', CRASH, True))
259        self.assertFalse(match('failures/expected/text.html', CRASH, False))
260        self.assertTrue(match('failures/expected/image_checksum.html', PASS, True))
261        self.assertTrue(match('failures/expected/image_checksum.html', PASS, False))
262        self.assertTrue(match('failures/expected/crash.html', PASS, False))
263        self.assertTrue(match('failures/expected/needsrebaseline.html', TEXT, True))
264        self.assertFalse(match('failures/expected/needsrebaseline.html', CRASH, True))
265        self.assertTrue(match('failures/expected/needsmanualrebaseline.html', TEXT, True))
266        self.assertFalse(match('failures/expected/needsmanualrebaseline.html', CRASH, True))
267        self.assertTrue(match('passes/text.html', PASS, False))
268
269    def test_sanitizer_flag(self):
270        def match(test, result):
271            return self._exp.matches_an_expected_result(
272                self.get_test(test), result, pixel_tests_are_enabled=False, sanitizer_is_enabled=True)
273
274        self.parse_exp("""
275Bug(test) failures/expected/crash.html [ Crash ]
276Bug(test) failures/expected/image.html [ ImageOnlyFailure ]
277Bug(test) failures/expected/text.html [ Failure ]
278Bug(test) failures/expected/timeout.html [ Timeout ]
279""")
280        self.assertTrue(match('failures/expected/crash.html', CRASH))
281        self.assertTrue(match('failures/expected/image.html', PASS))
282        self.assertTrue(match('failures/expected/text.html', PASS))
283        self.assertTrue(match('failures/expected/timeout.html', TIMEOUT))
284
285    def test_more_specific_override_resets_skip(self):
286        self.parse_exp("Bug(x) failures/expected [ Skip ]\n"
287                       "Bug(x) failures/expected/text.html [ ImageOnlyFailure ]\n")
288        self.assert_exp('failures/expected/text.html', IMAGE)
289        self.assertFalse(self._port._filesystem.join(self._port.layout_tests_dir(),
290                                                     'failures/expected/text.html') in
291                         self._exp.get_tests_with_result_type(SKIP))
292
293    def test_bot_test_expectations(self):
294        """Test that expectations are merged rather than overridden when using flaky option 'unexpected'."""
295        test_name1 = 'failures/expected/text.html'
296        test_name2 = 'passes/text.html'
297
298        expectations_dict = OrderedDict()
299        expectations_dict['expectations'] = "Bug(x) %s [ ImageOnlyFailure ]\nBug(x) %s [ Slow ]\n" % (test_name1, test_name2)
300        self._port.expectations_dict = lambda: expectations_dict
301
302        expectations = TestExpectations(self._port, self.get_basic_tests())
303        self.assertEqual(expectations.get_expectations(self.get_test(test_name1)), set([IMAGE]))
304        self.assertEqual(expectations.get_expectations(self.get_test(test_name2)), set([SLOW]))
305
306        def bot_expectations():
307            return {test_name1: ['PASS', 'TIMEOUT'], test_name2: ['CRASH']}
308        self._port.bot_expectations = bot_expectations
309        self._port._options.ignore_flaky_tests = 'unexpected'
310
311        expectations = TestExpectations(self._port, self.get_basic_tests())
312        self.assertEqual(expectations.get_expectations(self.get_test(test_name1)), set([PASS, IMAGE, TIMEOUT]))
313        self.assertEqual(expectations.get_expectations(self.get_test(test_name2)), set([CRASH, SLOW]))
314
315class SkippedTests(Base):
316    def check(self, expectations, overrides, skips, lint=False, expected_results=[WONTFIX, SKIP, FAIL]):
317        port = MockHost().port_factory.get('test-win-xp')
318        port._filesystem.write_text_file(port._filesystem.join(port.layout_tests_dir(), 'failures/expected/text.html'), 'foo')
319        expectations_dict = OrderedDict()
320        expectations_dict['expectations'] = expectations
321        if overrides:
322            expectations_dict['overrides'] = overrides
323        port.expectations_dict = lambda: expectations_dict
324        port.skipped_layout_tests = lambda tests: set(skips)
325        expectations_to_lint = expectations_dict if lint else None
326        exp = TestExpectations(port, ['failures/expected/text.html'], expectations_dict=expectations_to_lint, is_lint_mode=lint)
327        self.assertEqual(exp.get_expectations('failures/expected/text.html'), set(expected_results))
328
329    def test_skipped_tests_work(self):
330        self.check(expectations='', overrides=None, skips=['failures/expected/text.html'], expected_results=[WONTFIX, SKIP])
331
332    def test_duplicate_skipped_test_fails_lint(self):
333        self.assertRaises(ParseError, self.check, expectations='Bug(x) failures/expected/text.html [ Failure ]\n',
334            overrides=None, skips=['failures/expected/text.html'], lint=True)
335
336    def test_skipped_file_overrides_expectations(self):
337        self.check(expectations='Bug(x) failures/expected/text.html [ Failure ]\n', overrides=None,
338                   skips=['failures/expected/text.html'])
339
340    def test_skipped_dir_overrides_expectations(self):
341        self.check(expectations='Bug(x) failures/expected/text.html [ Failure ]\n', overrides=None,
342                   skips=['failures/expected'])
343
344    def test_skipped_file_overrides_overrides(self):
345        self.check(expectations='', overrides='Bug(x) failures/expected/text.html [ Failure ]\n',
346                   skips=['failures/expected/text.html'])
347
348    def test_skipped_dir_overrides_overrides(self):
349        self.check(expectations='', overrides='Bug(x) failures/expected/text.html [ Failure ]\n',
350                   skips=['failures/expected'])
351
352    def test_skipped_entry_dont_exist(self):
353        port = MockHost().port_factory.get('test-win-xp')
354        expectations_dict = OrderedDict()
355        expectations_dict['expectations'] = ''
356        port.expectations_dict = lambda: expectations_dict
357        port.skipped_layout_tests = lambda tests: set(['foo/bar/baz.html'])
358        capture = OutputCapture()
359        capture.capture_output()
360        exp = TestExpectations(port)
361        _, _, logs = capture.restore_output()
362        self.assertEqual('The following test foo/bar/baz.html from the Skipped list doesn\'t exist\n', logs)
363
364    def test_expectations_string(self):
365        self.parse_exp(self.get_basic_expectations())
366        notrun = 'failures/expected/text.html'
367        self._exp.add_extra_skipped_tests([notrun])
368        self.assertEqual('NOTRUN', self._exp.get_expectations_string(notrun))
369
370
371class ExpectationSyntaxTests(Base):
372    def test_unrecognized_expectation(self):
373        self.assert_bad_expectations('Bug(test) failures/expected/text.html [ Unknown ]')
374
375    def test_macro(self):
376        exp_str = 'Bug(test) [ Win ] failures/expected/text.html [ Failure ]'
377        self.parse_exp(exp_str)
378        self.assert_exp('failures/expected/text.html', FAIL)
379
380    def assert_tokenize_exp(self, line, bugs=None, specifiers=None, expectations=None, warnings=None, comment=None, name='foo.html'):
381        bugs = bugs or []
382        specifiers = specifiers or []
383        expectations = expectations or []
384        warnings = warnings or []
385        filename = 'TestExpectations'
386        line_number = '1'
387        expectation_line = TestExpectationParser._tokenize_line(filename, line, line_number)
388        self.assertEqual(expectation_line.warnings, warnings)
389        self.assertEqual(expectation_line.name, name)
390        self.assertEqual(expectation_line.filename, filename)
391        self.assertEqual(expectation_line.line_numbers, line_number)
392        if not warnings:
393            self.assertEqual(expectation_line.specifiers, specifiers)
394            self.assertEqual(expectation_line.expectations, expectations)
395
396    def test_comments(self):
397        self.assert_tokenize_exp("# comment", name=None, comment="# comment")
398        self.assert_tokenize_exp("foo.html [ Pass ] # comment", comment="# comment", expectations=['PASS'], specifiers=[])
399
400    def test_config_specifiers(self):
401        self.assert_tokenize_exp('[ Mac ] foo.html [ Failure ] ', specifiers=['MAC'], expectations=['FAIL'])
402
403    def test_unknown_config(self):
404        self.assert_tokenize_exp('[ Foo ] foo.html [ Pass ]', specifiers=['Foo'], expectations=['PASS'])
405
406    def test_unknown_expectation(self):
407        self.assert_tokenize_exp('foo.html [ Audio ]', warnings=['Unrecognized expectation "Audio"'])
408
409    def test_skip(self):
410        self.assert_tokenize_exp('foo.html [ Skip ]', specifiers=[], expectations=['SKIP'])
411
412    def test_slow(self):
413        self.assert_tokenize_exp('foo.html [ Slow ]', specifiers=[], expectations=['SLOW'])
414
415    def test_wontfix(self):
416        self.assert_tokenize_exp('foo.html [ WontFix ]', specifiers=[], expectations=['WONTFIX', 'SKIP'])
417        self.assert_tokenize_exp('foo.html [ WontFix ImageOnlyFailure ]', specifiers=[], expectations=['WONTFIX', 'SKIP'],
418            warnings=['A test marked Skip or WontFix must not have other expectations.'])
419
420    def test_blank_line(self):
421        self.assert_tokenize_exp('', name=None)
422
423    def test_warnings(self):
424        self.assert_tokenize_exp('[ Mac ]', warnings=['Did not find a test name.', 'Missing expectations.'], name=None)
425        self.assert_tokenize_exp('[ [', warnings=['unexpected "["', 'Missing expectations.'], name=None)
426        self.assert_tokenize_exp('crbug.com/12345 ]', warnings=['unexpected "]"', 'Missing expectations.'], name=None)
427
428        self.assert_tokenize_exp('foo.html crbug.com/12345 ]', warnings=['"crbug.com/12345" is not at the start of the line.', 'Missing expectations.'])
429        self.assert_tokenize_exp('foo.html', warnings=['Missing expectations.'])
430
431
432class SemanticTests(Base):
433    def test_bug_format(self):
434        self.assertRaises(ParseError, self.parse_exp, 'BUG1234 failures/expected/text.html [ Failure ]', is_lint_mode=True)
435
436    def test_bad_bugid(self):
437        try:
438            self.parse_exp('crbug/1234 failures/expected/text.html [ Failure ]', is_lint_mode=True)
439            self.fail('should have raised an error about a bad bug identifier')
440        except ParseError, exp:
441            self.assertEqual(len(exp.warnings), 3)
442
443    def test_missing_bugid(self):
444        self.parse_exp('failures/expected/text.html [ Failure ]', is_lint_mode=False)
445        self.assertFalse(self._exp.has_warnings())
446
447        try:
448            self.parse_exp('failures/expected/text.html [ Failure ]', is_lint_mode=True)
449        except ParseError, exp:
450            self.assertEqual(exp.warnings, ['expectations:1 Test lacks BUG specifier. failures/expected/text.html'])
451
452    def test_skip_and_wontfix(self):
453        # Skip is not allowed to have other expectations as well, because those
454        # expectations won't be exercised and may become stale .
455        self.parse_exp('failures/expected/text.html [ Failure Skip ]')
456        self.assertTrue(self._exp.has_warnings())
457
458        self.parse_exp('failures/expected/text.html [ Crash WontFix ]')
459        self.assertTrue(self._exp.has_warnings())
460
461        self.parse_exp('failures/expected/text.html [ Pass WontFix ]')
462        self.assertTrue(self._exp.has_warnings())
463
464    def test_rebaseline(self):
465        # Can't lint a file w/ 'REBASELINE' in it.
466        self.assertRaises(ParseError, self.parse_exp,
467            'Bug(test) failures/expected/text.html [ Failure Rebaseline ]',
468            is_lint_mode=True)
469
470    def test_duplicates(self):
471        self.assertRaises(ParseError, self.parse_exp, """
472Bug(exp) failures/expected/text.html [ Failure ]
473Bug(exp) failures/expected/text.html [ ImageOnlyFailure ]""", is_lint_mode=True)
474
475        self.assertRaises(ParseError, self.parse_exp,
476            self.get_basic_expectations(), overrides="""
477Bug(override) failures/expected/text.html [ Failure ]
478Bug(override) failures/expected/text.html [ ImageOnlyFailure ]""", is_lint_mode=True)
479
480    def test_duplicate_with_line_before_preceding_line(self):
481        self.assert_bad_expectations("""Bug(exp) [ Debug ] failures/expected/text.html [ Failure ]
482Bug(exp) [ Release ] failures/expected/text.html [ Failure ]
483Bug(exp) [ Debug ] failures/expected/text.html [ Failure ]
484""")
485
486    def test_missing_file(self):
487        self.parse_exp('Bug(test) missing_file.html [ Failure ]')
488        self.assertTrue(self._exp.has_warnings(), 1)
489
490
491class PrecedenceTests(Base):
492    def test_file_over_directory(self):
493        # This tests handling precedence of specific lines over directories
494        # and tests expectations covering entire directories.
495        exp_str = """
496Bug(x) failures/expected/text.html [ Failure ]
497Bug(y) failures/expected [ WontFix ]
498"""
499        self.parse_exp(exp_str)
500        self.assert_exp('failures/expected/text.html', FAIL)
501        self.assert_exp_list('failures/expected/crash.html', [WONTFIX, SKIP])
502
503        exp_str = """
504Bug(x) failures/expected [ WontFix ]
505Bug(y) failures/expected/text.html [ Failure ]
506"""
507        self.parse_exp(exp_str)
508        self.assert_exp('failures/expected/text.html', FAIL)
509        self.assert_exp_list('failures/expected/crash.html', [WONTFIX, SKIP])
510
511    def test_ambiguous(self):
512        self.assert_bad_expectations("Bug(test) [ Release ] passes/text.html [ Pass ]\n"
513                                     "Bug(test) [ Win ] passes/text.html [ Failure ]\n")
514
515    def test_more_specifiers(self):
516        self.assert_bad_expectations("Bug(test) [ Release ] passes/text.html [ Pass ]\n"
517                                     "Bug(test) [ Win Release ] passes/text.html [ Failure ]\n")
518
519    def test_order_in_file(self):
520        self.assert_bad_expectations("Bug(test) [ Win Release ] : passes/text.html [ Failure ]\n"
521                                     "Bug(test) [ Release ] : passes/text.html [ Pass ]\n")
522
523    def test_macro_overrides(self):
524        self.assert_bad_expectations("Bug(test) [ Win ] passes/text.html [ Pass ]\n"
525                                     "Bug(test) [ XP ] passes/text.html [ Failure ]\n")
526
527
528class RemoveConfigurationsTest(Base):
529    def test_remove(self):
530        host = MockHost()
531        test_port = host.port_factory.get('test-win-xp', None)
532        test_port.test_exists = lambda test: True
533        test_port.test_isfile = lambda test: True
534
535        test_config = test_port.test_configuration()
536        test_port.expectations_dict = lambda: {"expectations": """Bug(x) [ Linux Win Release ] failures/expected/foo.html [ Failure ]
537Bug(y) [ Win Mac Debug ] failures/expected/foo.html [ Crash ]
538"""}
539        expectations = TestExpectations(test_port, self.get_basic_tests())
540
541        actual_expectations = expectations.remove_configurations([('failures/expected/foo.html', test_config)])
542
543        self.assertEqual("""Bug(x) [ Linux Win7 Release ] failures/expected/foo.html [ Failure ]
544Bug(y) [ Win Mac Debug ] failures/expected/foo.html [ Crash ]
545""", actual_expectations)
546
547    def test_remove_needs_rebaseline(self):
548        host = MockHost()
549        test_port = host.port_factory.get('test-win-xp', None)
550        test_port.test_exists = lambda test: True
551        test_port.test_isfile = lambda test: True
552
553        test_config = test_port.test_configuration()
554        test_port.expectations_dict = lambda: {"expectations": """Bug(x) [ Win ] failures/expected/foo.html [ NeedsRebaseline ]
555"""}
556        expectations = TestExpectations(test_port, self.get_basic_tests())
557
558        actual_expectations = expectations.remove_configurations([('failures/expected/foo.html', test_config)])
559
560        self.assertEqual("""Bug(x) [ XP Debug ] failures/expected/foo.html [ NeedsRebaseline ]
561Bug(x) [ Win7 ] failures/expected/foo.html [ NeedsRebaseline ]
562""", actual_expectations)
563
564    def test_remove_multiple_configurations(self):
565        host = MockHost()
566        test_port = host.port_factory.get('test-win-xp', None)
567        test_port.test_exists = lambda test: True
568        test_port.test_isfile = lambda test: True
569
570        test_config = test_port.test_configuration()
571        test_port.expectations_dict = lambda: {'expectations': """Bug(y) [ Win Debug ] failures/expected/foo.html [ Crash ]
572Bug(x) [ Win Release ] failures/expected/foo.html [ Failure ]
573"""}
574        expectations = TestExpectations(test_port)
575
576        actual_expectations = expectations.remove_configurations([
577            ('failures/expected/foo.html', test_config),
578            ('failures/expected/foo.html', host.port_factory.get('test-win-win7', None).test_configuration()),
579        ])
580
581        self.assertEqual("""Bug(y) [ Win Debug ] failures/expected/foo.html [ Crash ]
582""", actual_expectations)
583
584    def test_remove_line_with_comments(self):
585        host = MockHost()
586        test_port = host.port_factory.get('test-win-xp', None)
587        test_port.test_exists = lambda test: True
588        test_port.test_isfile = lambda test: True
589
590        test_config = test_port.test_configuration()
591        test_port.expectations_dict = lambda: {'expectations': """Bug(y) [ Win Debug ] failures/expected/foo.html [ Crash ]
592
593 # This comment line should get stripped. As should the preceding line.
594Bug(x) [ Win Release ] failures/expected/foo.html [ Failure ]
595"""}
596        expectations = TestExpectations(test_port)
597
598        actual_expectations = expectations.remove_configurations([('failures/expected/foo.html', test_config)])
599        actual_expectations = expectations.remove_configurations([('failures/expected/foo.html', host.port_factory.get('test-win-win7', None).test_configuration())])
600
601        self.assertEqual("""Bug(y) [ Win Debug ] failures/expected/foo.html [ Crash ]
602""", actual_expectations)
603
604    def test_remove_line_with_comments_at_start(self):
605        host = MockHost()
606        test_port = host.port_factory.get('test-win-xp', None)
607        test_port.test_exists = lambda test: True
608        test_port.test_isfile = lambda test: True
609
610        test_config = test_port.test_configuration()
611        test_port.expectations_dict = lambda: {'expectations': """
612 # This comment line should get stripped. As should the preceding line.
613Bug(x) [ Win Release ] failures/expected/foo.html [ Failure ]
614
615Bug(y) [ Win Debug ] failures/expected/foo.html [ Crash ]
616"""}
617        expectations = TestExpectations(test_port)
618
619        actual_expectations = expectations.remove_configurations([('failures/expected/foo.html', test_config)])
620        actual_expectations = expectations.remove_configurations([('failures/expected/foo.html', host.port_factory.get('test-win-win7', None).test_configuration())])
621
622        self.assertEqual("""
623Bug(y) [ Win Debug ] failures/expected/foo.html [ Crash ]
624""", actual_expectations)
625
626    def test_remove_line_with_comments_at_end_with_no_trailing_newline(self):
627        host = MockHost()
628        test_port = host.port_factory.get('test-win-xp', None)
629        test_port.test_exists = lambda test: True
630        test_port.test_isfile = lambda test: True
631
632        test_config = test_port.test_configuration()
633        test_port.expectations_dict = lambda: {'expectations': """Bug(y) [ Win Debug ] failures/expected/foo.html [ Crash ]
634
635 # This comment line should get stripped. As should the preceding line.
636Bug(x) [ Win Release ] failures/expected/foo.html [ Failure ]"""}
637        expectations = TestExpectations(test_port)
638
639        actual_expectations = expectations.remove_configurations([('failures/expected/foo.html', test_config)])
640        actual_expectations = expectations.remove_configurations([('failures/expected/foo.html', host.port_factory.get('test-win-win7', None).test_configuration())])
641
642        self.assertEqual("""Bug(y) [ Win Debug ] failures/expected/foo.html [ Crash ]""", actual_expectations)
643
644    def test_remove_line_leaves_comments_for_next_line(self):
645        host = MockHost()
646        test_port = host.port_factory.get('test-win-xp', None)
647        test_port.test_exists = lambda test: True
648        test_port.test_isfile = lambda test: True
649
650        test_config = test_port.test_configuration()
651        test_port.expectations_dict = lambda: {'expectations': """
652 # This comment line should not get stripped.
653Bug(x) [ Win Release ] failures/expected/foo.html [ Failure ]
654Bug(y) [ Win Debug ] failures/expected/foo.html [ Crash ]
655"""}
656        expectations = TestExpectations(test_port)
657
658        actual_expectations = expectations.remove_configurations([('failures/expected/foo.html', test_config)])
659        actual_expectations = expectations.remove_configurations([('failures/expected/foo.html', host.port_factory.get('test-win-win7', None).test_configuration())])
660
661        self.assertEqual("""
662 # This comment line should not get stripped.
663Bug(y) [ Win Debug ] failures/expected/foo.html [ Crash ]
664""", actual_expectations)
665
666    def test_remove_line_no_whitespace_lines(self):
667        host = MockHost()
668        test_port = host.port_factory.get('test-win-xp', None)
669        test_port.test_exists = lambda test: True
670        test_port.test_isfile = lambda test: True
671
672        test_config = test_port.test_configuration()
673        test_port.expectations_dict = lambda: {'expectations': """
674 # This comment line should get stripped.
675Bug(x) [ Win Release ] failures/expected/foo.html [ Failure ]
676 # This comment line should not get stripped.
677Bug(y) [ Win Debug ] failures/expected/foo.html [ Crash ]
678"""}
679        expectations = TestExpectations(test_port)
680
681        actual_expectations = expectations.remove_configurations([('failures/expected/foo.html', test_config)])
682        actual_expectations = expectations.remove_configurations([('failures/expected/foo.html', host.port_factory.get('test-win-win7', None).test_configuration())])
683
684        self.assertEqual(""" # This comment line should not get stripped.
685Bug(y) [ Win Debug ] failures/expected/foo.html [ Crash ]
686""", actual_expectations)
687
688    def test_remove_first_line(self):
689        host = MockHost()
690        test_port = host.port_factory.get('test-win-xp', None)
691        test_port.test_exists = lambda test: True
692        test_port.test_isfile = lambda test: True
693
694        test_config = test_port.test_configuration()
695        test_port.expectations_dict = lambda: {'expectations': """Bug(x) [ Win Release ] failures/expected/foo.html [ Failure ]
696 # This comment line should not get stripped.
697Bug(y) [ Win Debug ] failures/expected/foo.html [ Crash ]
698"""}
699        expectations = TestExpectations(test_port)
700
701        actual_expectations = expectations.remove_configurations([('failures/expected/foo.html', test_config)])
702        actual_expectations = expectations.remove_configurations([('failures/expected/foo.html', host.port_factory.get('test-win-win7', None).test_configuration())])
703
704        self.assertEqual(""" # This comment line should not get stripped.
705Bug(y) [ Win Debug ] failures/expected/foo.html [ Crash ]
706""", actual_expectations)
707
708    def test_remove_flaky_line(self):
709        host = MockHost()
710        test_port = host.port_factory.get('test-win-xp', None)
711        test_port.test_exists = lambda test: True
712        test_port.test_isfile = lambda test: True
713
714        test_config = test_port.test_configuration()
715        test_port.expectations_dict = lambda: {'expectations': """Bug(x) [ Win ] failures/expected/foo.html [ Failure Timeout ]
716Bug(y) [ Mac ] failures/expected/foo.html [ Crash ]
717"""}
718        expectations = TestExpectations(test_port)
719
720        actual_expectations = expectations.remove_configurations([('failures/expected/foo.html', test_config)])
721        actual_expectations = expectations.remove_configurations([('failures/expected/foo.html', host.port_factory.get('test-win-win7', None).test_configuration())])
722
723        self.assertEqual("""Bug(x) [ Win Debug ] failures/expected/foo.html [ Failure Timeout ]
724Bug(y) [ Mac ] failures/expected/foo.html [ Crash ]
725""", actual_expectations)
726
727
728class RebaseliningTest(Base):
729    def test_get_rebaselining_failures(self):
730        # Make sure we find a test as needing a rebaseline even if it is not marked as a failure.
731        self.parse_exp('Bug(x) failures/expected/text.html [ Rebaseline ]\n')
732        self.assertEqual(len(self._exp.get_rebaselining_failures()), 1)
733
734        self.parse_exp(self.get_basic_expectations())
735        self.assertEqual(len(self._exp.get_rebaselining_failures()), 0)
736
737
738class TestExpectationsParserTests(unittest.TestCase):
739    def __init__(self, testFunc):
740        host = MockHost()
741        test_port = host.port_factory.get('test-win-xp', None)
742        self._converter = TestConfigurationConverter(test_port.all_test_configurations(), test_port.configuration_specifier_macros())
743        unittest.TestCase.__init__(self, testFunc)
744        self._parser = TestExpectationParser(host.port_factory.get('test-win-xp', None), [], is_lint_mode=False)
745
746    def test_expectation_line_for_test(self):
747        # This is kind of a silly test, but it at least ensures that we don't throw an error.
748        test_name = 'foo/test.html'
749        expectations = set(["PASS", "IMAGE"])
750
751        expectation_line = TestExpectationLine()
752        expectation_line.original_string = test_name
753        expectation_line.name = test_name
754        expectation_line.filename = '<Bot TestExpectations>'
755        expectation_line.line_numbers = '0'
756        expectation_line.expectations = expectations
757        self._parser._parse_line(expectation_line)
758
759        self.assertEqual(self._parser.expectation_line_for_test(test_name, expectations), expectation_line)
760
761
762class TestExpectationSerializationTests(unittest.TestCase):
763    def __init__(self, testFunc):
764        host = MockHost()
765        test_port = host.port_factory.get('test-win-xp', None)
766        self._converter = TestConfigurationConverter(test_port.all_test_configurations(), test_port.configuration_specifier_macros())
767        unittest.TestCase.__init__(self, testFunc)
768
769    def _tokenize(self, line):
770        return TestExpectationParser._tokenize_line('path', line, 0)
771
772    def assert_round_trip(self, in_string, expected_string=None):
773        expectation = self._tokenize(in_string)
774        if expected_string is None:
775            expected_string = in_string
776        self.assertEqual(expected_string, expectation.to_string(self._converter))
777
778    def assert_list_round_trip(self, in_string, expected_string=None):
779        host = MockHost()
780        parser = TestExpectationParser(host.port_factory.get('test-win-xp', None), [], is_lint_mode=False)
781        expectations = parser.parse('path', in_string)
782        if expected_string is None:
783            expected_string = in_string
784        self.assertEqual(expected_string, TestExpectations.list_to_string(expectations, self._converter))
785
786    def test_unparsed_to_string(self):
787        expectation = TestExpectationLine()
788
789        self.assertEqual(expectation.to_string(self._converter), '')
790        expectation.comment = ' Qux.'
791        self.assertEqual(expectation.to_string(self._converter), '# Qux.')
792        expectation.name = 'bar'
793        self.assertEqual(expectation.to_string(self._converter), 'bar # Qux.')
794        expectation.specifiers = ['foo']
795        # FIXME: case should be preserved here but we can't until we drop the old syntax.
796        self.assertEqual(expectation.to_string(self._converter), '[ FOO ] bar # Qux.')
797        expectation.expectations = ['bAz']
798        self.assertEqual(expectation.to_string(self._converter), '[ FOO ] bar [ BAZ ] # Qux.')
799        expectation.expectations = ['bAz1', 'baZ2']
800        self.assertEqual(expectation.to_string(self._converter), '[ FOO ] bar [ BAZ1 BAZ2 ] # Qux.')
801        expectation.specifiers = ['foo1', 'foO2']
802        self.assertEqual(expectation.to_string(self._converter), '[ FOO1 FOO2 ] bar [ BAZ1 BAZ2 ] # Qux.')
803        expectation.warnings.append('Oh the horror.')
804        self.assertEqual(expectation.to_string(self._converter), '')
805        expectation.original_string = 'Yes it is!'
806        self.assertEqual(expectation.to_string(self._converter), 'Yes it is!')
807
808    def test_unparsed_list_to_string(self):
809        expectation = TestExpectationLine()
810        expectation.comment = 'Qux.'
811        expectation.name = 'bar'
812        expectation.specifiers = ['foo']
813        expectation.expectations = ['bAz1', 'baZ2']
814        # FIXME: case should be preserved here but we can't until we drop the old syntax.
815        self.assertEqual(TestExpectations.list_to_string([expectation]), '[ FOO ] bar [ BAZ1 BAZ2 ] #Qux.')
816
817    def test_parsed_to_string(self):
818        expectation_line = TestExpectationLine()
819        expectation_line.bugs = ['Bug(x)']
820        expectation_line.name = 'test/name/for/realz.html'
821        expectation_line.parsed_expectations = set([IMAGE])
822        self.assertEqual(expectation_line.to_string(self._converter), None)
823        expectation_line.matching_configurations = set([TestConfiguration('xp', 'x86', 'release')])
824        self.assertEqual(expectation_line.to_string(self._converter), 'Bug(x) [ XP Release ] test/name/for/realz.html [ ImageOnlyFailure ]')
825        expectation_line.matching_configurations = set([TestConfiguration('xp', 'x86', 'release'), TestConfiguration('xp', 'x86', 'debug')])
826        self.assertEqual(expectation_line.to_string(self._converter), 'Bug(x) [ XP ] test/name/for/realz.html [ ImageOnlyFailure ]')
827
828    def test_serialize_parsed_expectations(self):
829        expectation_line = TestExpectationLine()
830        expectation_line.parsed_expectations = set([])
831        parsed_expectation_to_string = dict([[parsed_expectation, expectation_string] for expectation_string, parsed_expectation in TestExpectations.EXPECTATIONS.items()])
832        self.assertEqual(expectation_line._serialize_parsed_expectations(parsed_expectation_to_string), '')
833        expectation_line.parsed_expectations = set([FAIL])
834        self.assertEqual(expectation_line._serialize_parsed_expectations(parsed_expectation_to_string), 'fail')
835        expectation_line.parsed_expectations = set([PASS, IMAGE])
836        self.assertEqual(expectation_line._serialize_parsed_expectations(parsed_expectation_to_string), 'image pass')
837        expectation_line.parsed_expectations = set([FAIL, PASS])
838        self.assertEqual(expectation_line._serialize_parsed_expectations(parsed_expectation_to_string), 'pass fail')
839
840    def test_serialize_parsed_specifier_string(self):
841        expectation_line = TestExpectationLine()
842        expectation_line.bugs = ['garden-o-matic']
843        expectation_line.parsed_specifiers = ['the', 'for']
844        self.assertEqual(expectation_line._serialize_parsed_specifiers(self._converter, []), 'for the')
845        self.assertEqual(expectation_line._serialize_parsed_specifiers(self._converter, ['win']), 'for the win')
846        expectation_line.bugs = []
847        expectation_line.parsed_specifiers = []
848        self.assertEqual(expectation_line._serialize_parsed_specifiers(self._converter, []), '')
849        self.assertEqual(expectation_line._serialize_parsed_specifiers(self._converter, ['win']), 'win')
850
851    def test_format_line(self):
852        self.assertEqual(TestExpectationLine._format_line([], ['MODIFIERS'], 'name', ['EXPECTATIONS'], 'comment'), '[ MODIFIERS ] name [ EXPECTATIONS ] #comment')
853        self.assertEqual(TestExpectationLine._format_line([], ['MODIFIERS'], 'name', ['EXPECTATIONS'], None), '[ MODIFIERS ] name [ EXPECTATIONS ]')
854
855    def test_string_roundtrip(self):
856        self.assert_round_trip('')
857        self.assert_round_trip('[')
858        self.assert_round_trip('FOO [')
859        self.assert_round_trip('FOO ] bar')
860        self.assert_round_trip('  FOO [')
861        self.assert_round_trip('    [ FOO ] ')
862        self.assert_round_trip('[ FOO ] bar [ BAZ ]')
863        self.assert_round_trip('[ FOO ] bar [ BAZ ] # Qux.')
864        self.assert_round_trip('[ FOO ] bar [ BAZ ] # Qux.')
865        self.assert_round_trip('[ FOO ] bar [ BAZ ] # Qux.     ')
866        self.assert_round_trip('[ FOO ] bar [ BAZ ] #        Qux.     ')
867        self.assert_round_trip('[ FOO ] ] ] bar BAZ')
868        self.assert_round_trip('[ FOO ] ] ] bar [ BAZ ]')
869        self.assert_round_trip('FOO ] ] bar ==== BAZ')
870        self.assert_round_trip('=')
871        self.assert_round_trip('#')
872        self.assert_round_trip('# ')
873        self.assert_round_trip('# Foo')
874        self.assert_round_trip('# Foo')
875        self.assert_round_trip('# Foo :')
876        self.assert_round_trip('# Foo : =')
877
878    def test_list_roundtrip(self):
879        self.assert_list_round_trip('')
880        self.assert_list_round_trip('\n')
881        self.assert_list_round_trip('\n\n')
882        self.assert_list_round_trip('bar')
883        self.assert_list_round_trip('bar\n# Qux.')
884        self.assert_list_round_trip('bar\n# Qux.\n')
885
886    def test_reconstitute_only_these(self):
887        lines = []
888        reconstitute_only_these = []
889
890        def add_line(matching_configurations, reconstitute):
891            expectation_line = TestExpectationLine()
892            expectation_line.original_string = "Nay"
893            expectation_line.bugs = ['Bug(x)']
894            expectation_line.name = 'Yay'
895            expectation_line.parsed_expectations = set([IMAGE])
896            expectation_line.matching_configurations = matching_configurations
897            lines.append(expectation_line)
898            if reconstitute:
899                reconstitute_only_these.append(expectation_line)
900
901        add_line(set([TestConfiguration('xp', 'x86', 'release')]), True)
902        add_line(set([TestConfiguration('xp', 'x86', 'release'), TestConfiguration('xp', 'x86', 'debug')]), False)
903        serialized = TestExpectations.list_to_string(lines, self._converter)
904        self.assertEqual(serialized, "Bug(x) [ XP Release ] Yay [ ImageOnlyFailure ]\nBug(x) [ XP ] Yay [ ImageOnlyFailure ]")
905        serialized = TestExpectations.list_to_string(lines, self._converter, reconstitute_only_these=reconstitute_only_these)
906        self.assertEqual(serialized, "Bug(x) [ XP Release ] Yay [ ImageOnlyFailure ]\nNay")
907
908    def disabled_test_string_whitespace_stripping(self):
909        # FIXME: Re-enable this test once we rework the code to no longer support the old syntax.
910        self.assert_round_trip('\n', '')
911        self.assert_round_trip('  [ FOO ] bar [ BAZ ]', '[ FOO ] bar [ BAZ ]')
912        self.assert_round_trip('[ FOO ]    bar [ BAZ ]', '[ FOO ] bar [ BAZ ]')
913        self.assert_round_trip('[ FOO ] bar [ BAZ ]       # Qux.', '[ FOO ] bar [ BAZ ] # Qux.')
914        self.assert_round_trip('[ FOO ] bar [        BAZ ]  # Qux.', '[ FOO ] bar [ BAZ ] # Qux.')
915        self.assert_round_trip('[ FOO ]       bar [    BAZ ]  # Qux.', '[ FOO ] bar [ BAZ ] # Qux.')
916        self.assert_round_trip('[ FOO ]       bar     [    BAZ ]  # Qux.', '[ FOO ] bar [ BAZ ] # Qux.')
917