15616a1f21d7ce695d777d5bcbd169d9fabb9e50
[WebKit-https.git] / Tools / Scripts / webkitpy / layout_tests / models / test_expectations_unittest.py
1 #!/usr/bin/python
2 # Copyright (C) 2010 Google Inc. All rights reserved.
3 #
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are
6 # met:
7 #
8 #     * Redistributions of source code must retain the above copyright
9 # notice, this list of conditions and the following disclaimer.
10 #     * Redistributions in binary form must reproduce the above
11 # copyright notice, this list of conditions and the following disclaimer
12 # in the documentation and/or other materials provided with the
13 # distribution.
14 #     * Neither the name of Google Inc. nor the names of its
15 # contributors may be used to endorse or promote products derived from
16 # this software without specific prior written permission.
17 #
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 import unittest
31
32 from webkitpy.layout_tests import port
33 from webkitpy.layout_tests.port import base
34 from webkitpy.layout_tests.models.test_expectations import *
35
36 class FunctionsTest(unittest.TestCase):
37     def test_result_was_expected(self):
38         # test basics
39         self.assertEquals(result_was_expected(PASS, set([PASS]),
40                                               False, False), True)
41         self.assertEquals(result_was_expected(TEXT, set([PASS]),
42                                               False, False), False)
43
44         # test handling of FAIL expectations
45         self.assertEquals(result_was_expected(IMAGE_PLUS_TEXT, set([FAIL]),
46                                               False, False), True)
47         self.assertEquals(result_was_expected(IMAGE, set([FAIL]),
48                                               False, False), True)
49         self.assertEquals(result_was_expected(TEXT, set([FAIL]),
50                                               False, False), True)
51         self.assertEquals(result_was_expected(CRASH, set([FAIL]),
52                                               False, False), False)
53
54         # test handling of SKIPped tests and results
55         self.assertEquals(result_was_expected(SKIP, set([CRASH]),
56                                               False, True), True)
57         self.assertEquals(result_was_expected(SKIP, set([CRASH]),
58                                               False, False), False)
59
60         # test handling of MISSING results and the REBASELINE modifier
61         self.assertEquals(result_was_expected(MISSING, set([PASS]),
62                                               True, False), True)
63         self.assertEquals(result_was_expected(MISSING, set([PASS]),
64                                               False, False), False)
65
66     def test_remove_pixel_failures(self):
67         self.assertEquals(remove_pixel_failures(set([TEXT])),
68                           set([TEXT]))
69         self.assertEquals(remove_pixel_failures(set([PASS])),
70                           set([PASS]))
71         self.assertEquals(remove_pixel_failures(set([IMAGE])),
72                           set([PASS]))
73         self.assertEquals(remove_pixel_failures(set([IMAGE_PLUS_TEXT])),
74                           set([TEXT]))
75         self.assertEquals(remove_pixel_failures(set([PASS, IMAGE, CRASH])),
76                           set([PASS, CRASH]))
77
78
79 class Base(unittest.TestCase):
80     # Note that all of these tests are written assuming the configuration
81     # being tested is Windows XP, Release build.
82
83     def __init__(self, testFunc, setUp=None, tearDown=None, description=None):
84         self._port = port.get('test-win-xp', None)
85         self._fs = self._port._filesystem
86         self._exp = None
87         unittest.TestCase.__init__(self, testFunc)
88
89     def get_test(self, test_name):
90         # FIXME: Remove this routine and just reference test names directly.
91         return test_name
92
93     def get_basic_tests(self):
94         return [self.get_test('failures/expected/text.html'),
95                 self.get_test('failures/expected/image_checksum.html'),
96                 self.get_test('failures/expected/crash.html'),
97                 self.get_test('failures/expected/missing_text.html'),
98                 self.get_test('failures/expected/image.html'),
99                 self.get_test('passes/text.html')]
100
101     def get_basic_expectations(self):
102         return """
103 BUG_TEST : failures/expected/text.html = TEXT
104 BUG_TEST WONTFIX SKIP : failures/expected/crash.html = CRASH
105 BUG_TEST REBASELINE : failures/expected/missing_image.html = MISSING
106 BUG_TEST WONTFIX : failures/expected/image_checksum.html = IMAGE
107 BUG_TEST WONTFIX MAC : failures/expected/image.html = IMAGE
108 """
109
110     def parse_exp(self, expectations, overrides=None, is_lint_mode=False):
111         test_config = self._port.test_configuration()
112         self._exp = TestExpectations(self._port,
113              tests=self.get_basic_tests(),
114              expectations=expectations,
115              test_config=test_config,
116              is_lint_mode=is_lint_mode,
117              overrides=overrides)
118
119     def assert_exp(self, test, result):
120         self.assertEquals(self._exp.get_expectations(self.get_test(test)),
121                           set([result]))
122
123
124 class BasicTests(Base):
125     def test_basic(self):
126         self.parse_exp(self.get_basic_expectations())
127         self.assert_exp('failures/expected/text.html', TEXT)
128         self.assert_exp('failures/expected/image_checksum.html', IMAGE)
129         self.assert_exp('passes/text.html', PASS)
130         self.assert_exp('failures/expected/image.html', PASS)
131
132
133 class MiscTests(Base):
134     def test_multiple_results(self):
135         self.parse_exp('BUGX : failures/expected/text.html = TEXT CRASH')
136         self.assertEqual(self._exp.get_expectations(
137             self.get_test('failures/expected/text.html')),
138             set([TEXT, CRASH]))
139
140     def test_category_expectations(self):
141         # This test checks unknown tests are not present in the
142         # expectations and that known test part of a test category is
143         # present in the expectations.
144         exp_str = """
145 BUGX WONTFIX : failures/expected = IMAGE
146 """
147         self.parse_exp(exp_str)
148         test_name = 'failures/expected/unknown-test.html'
149         unknown_test = self.get_test(test_name)
150         self.assertRaises(KeyError, self._exp.get_expectations,
151                           unknown_test)
152         self.assert_exp('failures/expected/crash.html', IMAGE)
153
154     def test_get_modifiers(self):
155         self.parse_exp(self.get_basic_expectations())
156         self.assertEqual(self._exp.get_modifiers(
157                          self.get_test('passes/text.html')), [])
158
159     def test_get_expectations_string(self):
160         self.parse_exp(self.get_basic_expectations())
161         self.assertEquals(self._exp.get_expectations_string(
162                           self.get_test('failures/expected/text.html')),
163                           'TEXT')
164
165     def test_expectation_to_string(self):
166         # Normal cases are handled by other tests.
167         self.parse_exp(self.get_basic_expectations())
168         self.assertRaises(ValueError, self._exp.expectation_to_string,
169                           -1)
170
171     def test_get_test_set(self):
172         # Handle some corner cases for this routine not covered by other tests.
173         self.parse_exp(self.get_basic_expectations())
174         s = self._exp.get_test_set(WONTFIX)
175         self.assertEqual(s,
176             set([self.get_test('failures/expected/crash.html'),
177                  self.get_test('failures/expected/image_checksum.html')]))
178         s = self._exp.get_test_set(WONTFIX, CRASH)
179         self.assertEqual(s,
180             set([self.get_test('failures/expected/crash.html')]))
181         s = self._exp.get_test_set(WONTFIX, CRASH, include_skips=False)
182         self.assertEqual(s, set([]))
183
184     def test_parse_error_fatal(self):
185         try:
186             self.parse_exp("""FOO : failures/expected/text.html = TEXT
187 SKIP : failures/expected/image.html""")
188             self.assertFalse(True, "ParseError wasn't raised")
189         except ParseError, e:
190             self.assertTrue(e.fatal)
191             exp_errors = [u"Line:1 Unrecognized modifier 'foo' failures/expected/text.html",
192                           u"Line:2 Missing expectations SKIP : failures/expected/image.html"]
193             self.assertEqual(str(e), '\n'.join(map(str, exp_errors)))
194             self.assertEqual(e.errors, exp_errors)
195
196     def test_parse_error_nonfatal(self):
197         try:
198             self.parse_exp('SKIP : failures/expected/text.html = TEXT',
199                            is_lint_mode=True)
200             self.assertFalse(True, "ParseError wasn't raised")
201         except ParseError, e:
202             self.assertFalse(e.fatal)
203             exp_errors = [u'Line:1 Test lacks BUG modifier. failures/expected/text.html']
204             self.assertEqual(str(e), '\n'.join(map(str, exp_errors)))
205             self.assertEqual(e.errors, exp_errors)
206
207     def test_overrides(self):
208         self.parse_exp("BUG_EXP: failures/expected/text.html = TEXT",
209                        "BUG_OVERRIDE : failures/expected/text.html = IMAGE")
210         self.assert_exp('failures/expected/text.html', IMAGE)
211
212     def test_overrides__duplicate(self):
213         self.assertRaises(ParseError, self.parse_exp,
214              "BUG_EXP: failures/expected/text.html = TEXT",
215              """
216 BUG_OVERRIDE : failures/expected/text.html = IMAGE
217 BUG_OVERRIDE : failures/expected/text.html = CRASH
218 """)
219
220     def test_pixel_tests_flag(self):
221         def match(test, result, pixel_tests_enabled):
222             return self._exp.matches_an_expected_result(
223                 self.get_test(test), result, pixel_tests_enabled)
224
225         self.parse_exp(self.get_basic_expectations())
226         self.assertTrue(match('failures/expected/text.html', TEXT, True))
227         self.assertTrue(match('failures/expected/text.html', TEXT, False))
228         self.assertFalse(match('failures/expected/text.html', CRASH, True))
229         self.assertFalse(match('failures/expected/text.html', CRASH, False))
230         self.assertTrue(match('failures/expected/image_checksum.html', IMAGE,
231                               True))
232         self.assertTrue(match('failures/expected/image_checksum.html', PASS,
233                               False))
234         self.assertTrue(match('failures/expected/crash.html', SKIP, False))
235         self.assertTrue(match('passes/text.html', PASS, False))
236
237     def test_more_specific_override_resets_skip(self):
238         self.parse_exp("BUGX SKIP : failures/expected = TEXT\n"
239                        "BUGX : failures/expected/text.html = IMAGE\n")
240         self.assert_exp('failures/expected/text.html', IMAGE)
241         self.assertFalse(self._port._filesystem.join(self._port.layout_tests_dir(),
242                                                      'failures/expected/text.html') in
243                          self._exp.get_tests_with_result_type(SKIP))
244
245 class ExpectationSyntaxTests(Base):
246     def test_missing_expectation(self):
247         # This is missing the expectation.
248         self.assertRaises(ParseError, self.parse_exp,
249                           'BUG_TEST: failures/expected/text.html')
250
251     def test_missing_colon(self):
252         # This is missing the modifiers and the ':'
253         self.assertRaises(ParseError, self.parse_exp,
254                           'failures/expected/text.html = TEXT')
255
256     def disabled_test_too_many_colons(self):
257         # FIXME: Enable this test and fix the underlying bug.
258         self.assertRaises(ParseError, self.parse_exp,
259                           'BUG_TEST: failures/expected/text.html = PASS :')
260
261     def test_too_many_equals_signs(self):
262         self.assertRaises(ParseError, self.parse_exp,
263                           'BUG_TEST: failures/expected/text.html = TEXT = IMAGE')
264
265     def test_unrecognized_expectation(self):
266         self.assertRaises(ParseError, self.parse_exp,
267                           'BUG_TEST: failures/expected/text.html = UNKNOWN')
268
269     def test_macro(self):
270         exp_str = """
271 BUG_TEST WIN : failures/expected/text.html = TEXT
272 """
273         self.parse_exp(exp_str)
274         self.assert_exp('failures/expected/text.html', TEXT)
275
276
277 class SemanticTests(Base):
278     def test_bug_format(self):
279         self.assertRaises(ParseError, self.parse_exp, 'BUG1234 : failures/expected/text.html = TEXT')
280
281     def test_missing_bugid(self):
282         # This should log a non-fatal error.
283         self.parse_exp('SLOW : failures/expected/text.html = TEXT')
284         self.assertTrue(self._exp.has_warnings())
285
286     def test_slow_and_timeout(self):
287         # A test cannot be SLOW and expected to TIMEOUT.
288         self.assertRaises(ParseError, self.parse_exp,
289             'BUG_TEST SLOW : failures/expected/timeout.html = TIMEOUT')
290
291     def test_rebaseline(self):
292         # Can't lint a file w/ 'REBASELINE' in it.
293         self.assertRaises(ParseError, self.parse_exp,
294             'BUG_TEST REBASELINE : failures/expected/text.html = TEXT',
295             is_lint_mode=True)
296
297     def test_duplicates(self):
298         self.assertRaises(ParseError, self.parse_exp, """
299 BUG_EXP : failures/expected/text.html = TEXT
300 BUG_EXP : failures/expected/text.html = IMAGE""")
301
302         self.assertRaises(ParseError, self.parse_exp,
303             self.get_basic_expectations(), overrides="""
304 BUG_OVERRIDE : failures/expected/text.html = TEXT
305 BUG_OVERRIDE : failures/expected/text.html = IMAGE""", )
306
307     def test_missing_file(self):
308         # This should log a non-fatal error.
309         self.parse_exp('BUG_TEST : missing_file.html = TEXT')
310         self.assertTrue(self._exp.has_warnings(), 1)
311
312
313 class PrecedenceTests(Base):
314     def test_file_over_directory(self):
315         # This tests handling precedence of specific lines over directories
316         # and tests expectations covering entire directories.
317         exp_str = """
318 BUGX : failures/expected/text.html = TEXT
319 BUGX WONTFIX : failures/expected = IMAGE
320 """
321         self.parse_exp(exp_str)
322         self.assert_exp('failures/expected/text.html', TEXT)
323         self.assert_exp('failures/expected/crash.html', IMAGE)
324
325         exp_str = """
326 BUGX WONTFIX : failures/expected = IMAGE
327 BUGX : failures/expected/text.html = TEXT
328 """
329         self.parse_exp(exp_str)
330         self.assert_exp('failures/expected/text.html', TEXT)
331         self.assert_exp('failures/expected/crash.html', IMAGE)
332
333     def test_ambiguous(self):
334         self.assertRaises(ParseError, self.parse_exp, """
335 BUG_TEST RELEASE : passes/text.html = PASS
336 BUG_TEST WIN : passes/text.html = FAIL
337 """)
338
339     def test_more_modifiers(self):
340         exp_str = """
341 BUG_TEST RELEASE : passes/text.html = PASS
342 BUG_TEST WIN RELEASE : passes/text.html = TEXT
343 """
344         self.assertRaises(ParseError, self.parse_exp, exp_str)
345
346     def test_order_in_file(self):
347         exp_str = """
348 BUG_TEST WIN RELEASE : passes/text.html = TEXT
349 BUG_TEST RELEASE : passes/text.html = PASS
350 """
351         self.assertRaises(ParseError, self.parse_exp, exp_str)
352
353     def test_macro_overrides(self):
354         exp_str = """
355 BUG_TEST WIN : passes/text.html = PASS
356 BUG_TEST XP : passes/text.html = TEXT
357 """
358         self.assertRaises(ParseError, self.parse_exp, exp_str)
359
360
361 class RebaseliningTest(Base):
362     """Test rebaselining-specific functionality."""
363     def assertRemove(self, input_expectations, tests, expected_expectations):
364         self.parse_exp(input_expectations)
365         actual_expectations = self._exp.remove_rebaselined_tests(tests)
366         self.assertEqual(expected_expectations, actual_expectations)
367
368     def test_remove(self):
369         self.assertRemove('BUGX REBASELINE : failures/expected/text.html = TEXT\n'
370                           'BUGY : failures/expected/image.html = IMAGE\n'
371                           'BUGZ REBASELINE : failures/expected/crash.html = CRASH\n',
372                           ['failures/expected/text.html'],
373                           'BUGY : failures/expected/image.html = IMAGE\n'
374                           'BUGZ REBASELINE : failures/expected/crash.html = CRASH\n')
375
376     def test_no_get_rebaselining_failures(self):
377         self.parse_exp(self.get_basic_expectations())
378         self.assertEqual(len(self._exp.get_rebaselining_failures()), 0)
379
380
381 class SpecificityCalculatorTests(unittest.TestCase):
382     def setUp(self):
383         port_obj = port.get('test-win-xp', None)
384         self.config = port_obj.test_configuration()
385         self.calculator = SpecificityCalculator(self.config)
386
387     def assert_specificity(self, modifiers, expected_specificity=-1, num_errors=0):
388         expectation = TestExpectationLine()
389         expectation.modifiers = modifiers
390         self.calculator.calculate(expectation)
391         self.assertEqual(len(expectation.warnings), 0)
392         self.assertEqual(len(expectation.errors), num_errors)
393         self.assertEqual(expectation.specificity, expected_specificity,
394              'match(%s, %s) returned -> %d, expected %d' %
395              (modifiers, str(self.config.values()),
396               expectation.specificity, expected_specificity))
397
398     def test_bad_match_modifier(self):
399         self.assert_specificity(['foo'], num_errors=1)
400
401     def test_none(self):
402         self.assert_specificity([], 0)
403
404     def test_one(self):
405         self.assert_specificity(['xp'], 1)
406         self.assert_specificity(['win'], 1)
407         self.assert_specificity(['release'], 1)
408         self.assert_specificity(['cpu'], 1)
409         self.assert_specificity(['x86'], 1)
410         self.assert_specificity(['leopard'], -1)
411         self.assert_specificity(['gpu'], -1)
412         self.assert_specificity(['debug'], -1)
413
414     def test_two(self):
415         self.assert_specificity(['xp', 'release'], 2)
416         self.assert_specificity(['win7', 'release'], -1)
417         self.assert_specificity(['win7', 'xp'], 1)
418
419     def test_three(self):
420         self.assert_specificity(['win7', 'xp', 'release'], 2)
421         self.assert_specificity(['xp', 'debug', 'x86'], -1)
422         self.assert_specificity(['xp', 'release', 'x86'], 3)
423         self.assert_specificity(['xp', 'cpu', 'release'], 3)
424
425     def test_four(self):
426         self.assert_specificity(['xp', 'release', 'cpu', 'x86'], 4)
427         self.assert_specificity(['win7', 'xp', 'release', 'cpu'], 3)
428         self.assert_specificity(['win7', 'xp', 'debug', 'cpu'], -1)
429
430     def test_case_insensitivity(self):
431         self.assert_specificity(['Win'], num_errors=1)
432         self.assert_specificity(['WIN'], num_errors=1)
433         self.assert_specificity(['win'], 1)
434
435     def test_duplicates(self):
436         self.assert_specificity(['release', 'release'], num_errors=1)
437         self.assert_specificity(['win', 'xp'], num_errors=1)
438         self.assert_specificity(['xp', 'xp'], num_errors=1)
439         self.assert_specificity(['xp', 'release', 'xp', 'release'], num_errors=2)
440         self.assert_specificity(['rebaseline', 'rebaseline'], num_errors=1)
441
442     def test_unknown_modifier(self):
443         self.assert_specificity(['vms'], num_errors=1)
444
445     def test_duplicate_bugs(self):
446         # BUG* regexes can appear multiple times.
447         self.assert_specificity(['bugfoo', 'bugbar'], 0)
448
449     def test_regexes_are_ignored(self):
450         self.assert_specificity(['bug123xy', 'rebaseline', 'wontfix', 'slow', 'skip'], 0)
451
452     def test_none_is_invalid(self):
453         self.assert_specificity(['none'], num_errors=1)
454
455
456 class TestExpectationParserTests(unittest.TestCase):
457     def test_tokenize_blank(self):
458         expectation = TestExpectationParser.tokenize('')
459         self.assertEqual(expectation.is_malformed(), False)
460         self.assertEqual(expectation.comment, None)
461         self.assertEqual(len(expectation.errors), 0)
462
463     def test_tokenize_missing_colon(self):
464         expectation = TestExpectationParser.tokenize('Qux.')
465         self.assertEqual(expectation.is_malformed(), True)
466         self.assertEqual(expectation.comment, 'Qux.')
467         self.assertEqual(str(expectation.errors), '["Missing a \':\'"]')
468
469     def test_tokenize_extra_colon(self):
470         expectation = TestExpectationParser.tokenize('FOO : : bar')
471         self.assertEqual(expectation.is_malformed(), True)
472         self.assertEqual(expectation.comment, 'FOO : : bar')
473         self.assertEqual(str(expectation.errors), '["Extraneous \':\'"]')
474
475     def test_tokenize_empty_comment(self):
476         expectation = TestExpectationParser.tokenize('//')
477         self.assertEqual(expectation.is_malformed(), False)
478         self.assertEqual(expectation.comment, '')
479         self.assertEqual(len(expectation.errors), 0)
480
481     def test_tokenize_comment(self):
482         expectation = TestExpectationParser.tokenize('//Qux.')
483         self.assertEqual(expectation.is_malformed(), False)
484         self.assertEqual(expectation.comment, 'Qux.')
485         self.assertEqual(len(expectation.errors), 0)
486
487     def test_tokenize_missing_equal(self):
488         expectation = TestExpectationParser.tokenize('FOO : bar')
489         self.assertEqual(expectation.is_malformed(), True)
490         self.assertEqual(expectation.comment, 'FOO : bar')
491         self.assertEqual(str(expectation.errors), "['Missing expectations\']")
492
493     def test_tokenize_extra_equal(self):
494         expectation = TestExpectationParser.tokenize('FOO : bar = BAZ = Qux.')
495         self.assertEqual(expectation.is_malformed(), True)
496         self.assertEqual(expectation.comment, 'FOO : bar = BAZ = Qux.')
497         self.assertEqual(str(expectation.errors), '["Extraneous \'=\'"]')
498
499     def test_tokenize_valid(self):
500         expectation = TestExpectationParser.tokenize('FOO : bar = BAZ')
501         self.assertEqual(expectation.is_malformed(), False)
502         self.assertEqual(expectation.comment, None)
503         self.assertEqual(len(expectation.errors), 0)
504
505     def test_tokenize_valid_with_comment(self):
506         expectation = TestExpectationParser.tokenize('FOO : bar = BAZ //Qux.')
507         self.assertEqual(expectation.is_malformed(), False)
508         self.assertEqual(expectation.comment, 'Qux.')
509         self.assertEqual(str(expectation.modifiers), '[\'foo\']')
510         self.assertEqual(str(expectation.expectations), '[\'baz\']')
511         self.assertEqual(len(expectation.errors), 0)
512
513     def test_tokenize_valid_with_multiple_modifiers(self):
514         expectation = TestExpectationParser.tokenize('FOO1 FOO2 : bar = BAZ //Qux.')
515         self.assertEqual(expectation.is_malformed(), False)
516         self.assertEqual(expectation.comment, 'Qux.')
517         self.assertEqual(str(expectation.modifiers), '[\'foo1\', \'foo2\']')
518         self.assertEqual(str(expectation.expectations), '[\'baz\']')
519         self.assertEqual(len(expectation.errors), 0)
520
521
522 class TestExpectationSerializerTests(unittest.TestCase):
523     def assert_round_trip(self, in_string, expected_string=None):
524         expectation = TestExpectationParser.tokenize(in_string)
525         if expected_string is None:
526             expected_string = in_string
527         self.assertEqual(expected_string, TestExpectationSerializer.to_string(expectation))
528
529     def assert_list_round_trip(self, in_string, expected_string=None):
530         expectations = TestExpectationParser.tokenize_list(in_string)
531         if expected_string is None:
532             expected_string = in_string
533         self.assertEqual(expected_string, TestExpectationSerializer.list_to_string(expectations))
534
535     def assert_to_string(self, expectation, expected_string):
536         self.assertEqual(TestExpectationSerializer.to_string(expectation), expected_string)
537
538     def test_string_serializer(self):
539         expectation = TestExpectationLine()
540         self.assert_to_string(expectation, '')
541         expectation.comment = 'Qux.'
542         self.assert_to_string(expectation, '//Qux.')
543         expectation.name = 'bar'
544         self.assert_to_string(expectation, ' : bar =  //Qux.')
545         expectation.modifiers = ['foo']
546         self.assert_to_string(expectation, 'FOO : bar =  //Qux.')
547         expectation.expectations = ['bAz']
548         self.assert_to_string(expectation, 'FOO : bar = BAZ //Qux.')
549         expectation.expectations = ['bAz1', 'baZ2']
550         self.assert_to_string(expectation, 'FOO : bar = BAZ1 BAZ2 //Qux.')
551         expectation.modifiers = ['foo1', 'foO2']
552         self.assert_to_string(expectation, 'FOO1 FOO2 : bar = BAZ1 BAZ2 //Qux.')
553         expectation.errors.append('Oh the horror.')
554         self.assert_to_string(expectation, 'Qux.')
555
556     def test_string_roundtrip(self):
557         self.assert_round_trip('')
558         self.assert_round_trip('FOO')
559         self.assert_round_trip(':')
560         self.assert_round_trip('FOO :')
561         self.assert_round_trip('FOO : bar')
562         self.assert_round_trip('  FOO :')
563         self.assert_round_trip('    FOO : bar')
564         self.assert_round_trip('FOO : bar = BAZ')
565         self.assert_round_trip('FOO : bar = BAZ //Qux.')
566         self.assert_round_trip('FOO : bar = BAZ // Qux.')
567         self.assert_round_trip('FOO : bar = BAZ // Qux.     ')
568         self.assert_round_trip('FOO : bar = BAZ //        Qux.     ')
569         self.assert_round_trip('FOO : : bar = BAZ')
570         self.assert_round_trip('FOO : : bar = BAZ')
571         self.assert_round_trip('FOO : : bar ==== BAZ')
572         self.assert_round_trip('=')
573         self.assert_round_trip('//')
574         self.assert_round_trip('// ')
575         self.assert_round_trip('// Foo')
576         self.assert_round_trip('// Foo')
577         self.assert_round_trip('// Foo :')
578         self.assert_round_trip('// Foo : =')
579
580     def test_list_roundtrip(self):
581         self.assert_list_round_trip('')
582         self.assert_list_round_trip('\n')
583         self.assert_list_round_trip('\n\n')
584         self.assert_list_round_trip('bar')
585         self.assert_list_round_trip('bar\n//Qux.')
586         self.assert_list_round_trip('bar\n//Qux.\n')
587
588     def test_string_whitespace_stripping(self):
589         self.assert_round_trip('\n', '')
590         self.assert_round_trip('  FOO : bar = BAZ', 'FOO : bar = BAZ')
591         self.assert_round_trip('FOO    : bar = BAZ', 'FOO : bar = BAZ')
592         self.assert_round_trip('FOO : bar = BAZ       // Qux.', 'FOO : bar = BAZ // Qux.')
593         self.assert_round_trip('FOO : bar =        BAZ // Qux.', 'FOO : bar = BAZ // Qux.')
594         self.assert_round_trip('FOO :       bar =    BAZ // Qux.', 'FOO : bar = BAZ // Qux.')
595         self.assert_round_trip('FOO :       bar     =    BAZ // Qux.', 'FOO : bar = BAZ // Qux.')
596
597 if __name__ == '__main__':
598     unittest.main()