Fixup: more save implementation of binary data output under Python 3.
[docutils.git] / test / DocutilsTestSupport.py
blobdae25ccc31277e4fac7c02b096e0db2944291781
1 # $Id$
2 # Authors: David Goodger <goodger@python.org>;
3 # Garth Kidd <garth@deadlybloodyserious.com>
4 # Copyright: This module has been placed in the public domain.
6 """
7 Exports the following:
9 :Modules:
10 - `statemachine` is 'docutils.statemachine'
11 - `nodes` is 'docutils.nodes'
12 - `urischemes` is 'docutils.urischemes'
13 - `utils` is 'docutils.utils'
14 - `transforms` is 'docutils.transforms'
15 - `states` is 'docutils.parsers.rst.states'
16 - `tableparser` is 'docutils.parsers.rst.tableparser'
18 :Classes:
19 - `StandardTestCase`
20 - `CustomTestCase`
21 - `CustomTestSuite`
22 - `TransformTestCase`
23 - `TransformTestSuite`
24 - `ParserTestCase`
25 - `ParserTestSuite`
26 - `ParserTransformTestCase`
27 - `PEPParserTestCase`
28 - `PEPParserTestSuite`
29 - `GridTableParserTestCase`
30 - `GridTableParserTestSuite`
31 - `SimpleTableParserTestCase`
32 - `SimpleTableParserTestSuite`
33 - `WriterPublishTestCase`
34 - `LatexWriterPublishTestCase`
35 - `PseudoXMLWriterPublishTestCase`
36 - `HtmlWriterPublishTestCase`
37 - `PublishTestSuite`
38 - `HtmlFragmentTestSuite`
39 - `DevNull` (output sink)
40 """
41 __docformat__ = 'reStructuredText'
43 import sys
44 import os
45 import unittest
46 import re
47 import inspect
48 import traceback
49 from pprint import pformat
51 testroot = os.path.abspath(os.path.dirname(__file__) or os.curdir)
52 os.chdir(testroot)
53 if sys.version_info >= (3,0):
54 sys.path.insert(0, os.path.normpath(os.path.join(testroot,
55 '..', 'build', 'lib')))
56 sys.path.append(os.path.normpath(os.path.join(testroot, '..',
57 'build', 'lib', 'extras')))
58 else:
59 sys.path.insert(0, os.path.normpath(os.path.join(testroot, '..')))
60 sys.path.append(os.path.normpath(os.path.join(testroot, '..', 'extras')))
61 sys.path.insert(0, testroot)
63 try:
64 import difflib
65 import package_unittest
66 import docutils
67 import docutils.core
68 from docutils import frontend, nodes, statemachine, urischemes, utils
69 from docutils.transforms import universal
70 from docutils.parsers import rst
71 from docutils.parsers.rst import states, tableparser, roles, languages
72 from docutils.readers import standalone, pep
73 from docutils.statemachine import StringList, string2lines
74 from docutils._compat import bytes
75 except ImportError:
76 # The importing module (usually __init__.py in one of the
77 # subdirectories) may catch ImportErrors in order to detect the
78 # absence of DocutilsTestSupport in sys.path. Thus, ImportErrors
79 # resulting from problems with importing Docutils modules must
80 # caught here.
81 traceback.print_exc()
82 sys.exit(1)
85 try:
86 import mypdb as pdb
87 except:
88 import pdb
91 # Hack to make repr(StringList) look like repr(list):
92 StringList.__repr__ = StringList.__str__
95 class DevNull:
97 """Output sink."""
99 def write(self, string):
100 pass
102 def close(self):
103 pass
106 class StandardTestCase(unittest.TestCase):
109 Helper class, providing the same interface as unittest.TestCase,
110 but with useful setUp and comparison methods.
113 def setUp(self):
114 os.chdir(testroot)
116 def assertEqual(self, first, second, msg=None):
117 """Fail if the two objects are unequal as determined by the '=='
118 operator.
120 if not first == second:
121 raise self.failureException, \
122 (msg or '%s != %s' % _format_str(first, second))
124 def assertNotEqual(self, first, second, msg=None):
125 """Fail if the two objects are equal as determined by the '=='
126 operator.
128 if first == second:
129 raise self.failureException, \
130 (msg or '%s == %s' % _format_str(first, second))
132 # python 2.3
133 if not hasattr(unittest.TestCase, "assertTrue"):
134 assertTrue = unittest.TestCase.failUnless
136 # aliases for assertion methods, deprecated since Python 2.7
138 failUnlessEqual = assertEquals = assertEqual
140 assertNotEquals = failIfEqual = assertNotEqual
143 class CustomTestCase(StandardTestCase):
146 Helper class, providing extended functionality over unittest.TestCase.
148 The methods assertEqual and assertNotEqual have been overwritten
149 to provide better support for multi-line strings. Furthermore,
150 see the compare_output method and the parameter list of __init__.
153 compare = difflib.Differ().compare
154 """Comparison method shared by all subclasses."""
156 def __init__(self, method_name, input, expected, id,
157 run_in_debugger=True, suite_settings=None):
159 Initialise the CustomTestCase.
161 Arguments:
163 method_name -- name of test method to run.
164 input -- input to the parser.
165 expected -- expected output from the parser.
166 id -- unique test identifier, used by the test framework.
167 run_in_debugger -- if true, run this test under the pdb debugger.
168 suite_settings -- settings overrides for this test suite.
170 self.id = id
171 self.input = input
172 self.expected = expected
173 self.run_in_debugger = run_in_debugger
174 self.suite_settings = suite_settings.copy() or {}
176 # Ring your mother.
177 unittest.TestCase.__init__(self, method_name)
179 def __str__(self):
181 Return string conversion. Overridden to give test id, in addition to
182 method name.
184 return '%s; %s' % (self.id, unittest.TestCase.__str__(self))
186 def __repr__(self):
187 return "<%s %s>" % (self.id, unittest.TestCase.__repr__(self))
189 def clear_roles(self):
190 # Language-specific roles and roles added by the
191 # "default-role" and "role" directives are currently stored
192 # globally in the roles._roles dictionary. This workaround
193 # empties that dictionary.
194 roles._roles = {}
196 def setUp(self):
197 StandardTestCase.setUp(self)
198 self.clear_roles()
200 def compare_output(self, input, output, expected):
201 """`input`, `output`, and `expected` should all be strings."""
202 if isinstance(input, unicode):
203 input = input.encode('raw_unicode_escape')
204 if sys.version_info > (3,):
205 # API difference: Python 3's node.__str__ doesn't escape
206 #assert expected is None or isinstance(expected, unicode)
207 if isinstance(expected, bytes):
208 expected = expected.decode('utf-8')
209 if isinstance(output, bytes):
210 output = output.decode('utf-8')
211 else:
212 if isinstance(expected, unicode):
213 expected = expected.encode('raw_unicode_escape')
214 if isinstance(output, unicode):
215 output = output.encode('raw_unicode_escape')
216 # Normalize line endings:
217 if expected:
218 expected = '\n'.join(expected.splitlines())
219 if output:
220 output = '\n'.join(output.splitlines())
221 try:
222 self.assertEqual(output, expected)
223 except AssertionError, error:
224 print >>sys.stderr, '\n%s\ninput:' % (self,)
225 print >>sys.stderr, input
226 try:
227 comparison = ''.join(self.compare(expected.splitlines(1),
228 output.splitlines(1)))
229 print >>sys.stderr, '-: expected\n+: output'
230 print >>sys.stderr, comparison
231 except AttributeError: # expected or output not a string
232 # alternative output for non-strings:
233 print >>sys.stderr, 'expected: %r' % expected
234 print >>sys.stderr, 'output: %r' % output
235 raise error
238 class CustomTestSuite(unittest.TestSuite):
241 A collection of CustomTestCases.
243 Provides test suite ID generation and a method for adding test cases.
246 id = ''
247 """Identifier for the TestSuite. Prepended to the
248 TestCase identifiers to make identification easier."""
250 next_test_case_id = 0
251 """The next identifier to use for non-identified test cases."""
253 def __init__(self, tests=(), id=None, suite_settings=None):
255 Initialize the CustomTestSuite.
257 Arguments:
259 id -- identifier for the suite, prepended to test cases.
260 suite_settings -- settings overrides for this test suite.
262 unittest.TestSuite.__init__(self, tests)
263 self.suite_settings = suite_settings or {}
264 if id is None:
265 mypath = os.path.abspath(
266 sys.modules[CustomTestSuite.__module__].__file__)
267 outerframes = inspect.getouterframes(inspect.currentframe())
268 for outerframe in outerframes[1:]:
269 if outerframe[3] != '__init__':
270 callerpath = outerframe[1]
271 if callerpath is None:
272 # It happens sometimes. Why is a mystery.
273 callerpath = os.getcwd()
274 callerpath = os.path.abspath(callerpath)
275 break
276 mydir, myname = os.path.split(mypath)
277 if not mydir:
278 mydir = os.curdir
279 if callerpath.startswith(mydir):
280 self.id = callerpath[len(mydir) + 1:] # caller's module
281 else:
282 self.id = callerpath
283 else:
284 self.id = id
286 def addTestCase(self, test_case_class, method_name, input, expected,
287 id=None, run_in_debugger=False, **kwargs):
289 Create a CustomTestCase in the CustomTestSuite.
290 Also return it, just in case.
292 Arguments:
294 test_case_class -- the CustomTestCase to add
295 method_name -- a string; CustomTestCase.method_name is the test
296 input -- input to the parser.
297 expected -- expected output from the parser.
298 id -- unique test identifier, used by the test framework.
299 run_in_debugger -- if true, run this test under the pdb debugger.
301 if id is None: # generate id if required
302 id = self.next_test_case_id
303 self.next_test_case_id += 1
304 # test identifier will become suiteid.testid
305 tcid = '%s: %s' % (self.id, id)
306 # suite_settings may be passed as a parameter;
307 # if not, set from attribute:
308 kwargs.setdefault('suite_settings', self.suite_settings)
309 # generate and add test case
310 tc = test_case_class(method_name, input, expected, tcid,
311 run_in_debugger=run_in_debugger, **kwargs)
312 self.addTest(tc)
313 return tc
315 def generate_no_tests(self, *args, **kwargs):
316 pass
319 class TransformTestCase(CustomTestCase):
322 Output checker for the transform.
324 Should probably be called TransformOutputChecker, but I can deal with
325 that later when/if someone comes up with a category of transform test
326 cases that have nothing to do with the input and output of the transform.
329 option_parser = frontend.OptionParser(components=(rst.Parser,))
330 settings = option_parser.get_default_values()
331 settings.report_level = 1
332 settings.halt_level = 5
333 settings.debug = package_unittest.debug
334 settings.warning_stream = DevNull()
335 unknown_reference_resolvers = ()
337 def __init__(self, *args, **kwargs):
338 self.transforms = kwargs['transforms']
339 """List of transforms to perform for this test case."""
341 self.parser = kwargs['parser']
342 """Input parser for this test case."""
344 del kwargs['transforms'], kwargs['parser'] # only wanted here
345 CustomTestCase.__init__(self, *args, **kwargs)
347 def supports(self, format):
348 return 1
350 def test_transforms(self):
351 if self.run_in_debugger:
352 pdb.set_trace()
353 settings = self.settings.copy()
354 settings.__dict__.update(self.suite_settings)
355 document = utils.new_document('test data', settings)
356 self.parser.parse(self.input, document)
357 # Don't do a ``populate_from_components()`` because that would
358 # enable the Transformer's default transforms.
359 document.transformer.add_transforms(self.transforms)
360 document.transformer.add_transform(universal.TestMessages)
361 document.transformer.components['writer'] = self
362 document.transformer.apply_transforms()
363 output = document.pformat()
364 self.compare_output(self.input, output, self.expected)
366 def test_transforms_verbosely(self):
367 if self.run_in_debugger:
368 pdb.set_trace()
369 print '\n', self.id
370 print '-' * 70
371 print self.input
372 settings = self.settings.copy()
373 settings.__dict__.update(self.suite_settings)
374 document = utils.new_document('test data', settings)
375 self.parser.parse(self.input, document)
376 print '-' * 70
377 print document.pformat()
378 for transformClass in self.transforms:
379 transformClass(document).apply()
380 output = document.pformat()
381 print '-' * 70
382 print output
383 self.compare_output(self.input, output, self.expected)
386 class TransformTestSuite(CustomTestSuite):
389 A collection of TransformTestCases.
391 A TransformTestSuite instance manufactures TransformTestCases,
392 keeps track of them, and provides a shared test fixture (a-la
393 setUp and tearDown).
396 def __init__(self, parser, suite_settings=None):
397 self.parser = parser
398 """Parser shared by all test cases."""
400 CustomTestSuite.__init__(self, suite_settings=suite_settings)
402 def generateTests(self, dict, dictname='totest',
403 testmethod='test_transforms'):
405 Stock the suite with test cases generated from a test data dictionary.
407 Each dictionary key (test type's name) maps to a tuple, whose
408 first item is a list of transform classes and whose second
409 item is a list of tests. Each test is a list: input, expected
410 output, optional modifier. The optional third entry, a
411 behavior modifier, can be 0 (temporarily disable this test) or
412 1 (run this test under the pdb debugger). Tests should be
413 self-documenting and not require external comments.
415 for name, (transforms, cases) in dict.items():
416 for casenum in range(len(cases)):
417 case = cases[casenum]
418 run_in_debugger = False
419 if len(case)==3:
420 # TODO: (maybe) change the 3rd argument to a dict, so it
421 # can handle more cases by keyword ('disable', 'debug',
422 # 'settings'), here and in other generateTests methods.
423 # But there's also the method that
424 # HtmlPublishPartsTestSuite uses <DJG>
425 if case[2]:
426 run_in_debugger = True
427 else:
428 continue
429 self.addTestCase(
430 TransformTestCase, testmethod,
431 transforms=transforms, parser=self.parser,
432 input=case[0], expected=case[1],
433 id='%s[%r][%s]' % (dictname, name, casenum),
434 run_in_debugger=run_in_debugger)
437 class ParserTestCase(CustomTestCase):
440 Output checker for the parser.
442 Should probably be called ParserOutputChecker, but I can deal with
443 that later when/if someone comes up with a category of parser test
444 cases that have nothing to do with the input and output of the parser.
447 parser = rst.Parser()
448 """Parser shared by all ParserTestCases."""
450 option_parser = frontend.OptionParser(components=(rst.Parser,))
451 settings = option_parser.get_default_values()
452 settings.report_level = 5
453 settings.halt_level = 5
454 settings.debug = package_unittest.debug
456 def test_parser(self):
457 if self.run_in_debugger:
458 pdb.set_trace()
459 settings = self.settings.copy()
460 settings.__dict__.update(self.suite_settings)
461 document = utils.new_document('test data', settings)
462 self.parser.parse(self.input, document)
463 output = document.pformat()
464 self.compare_output(self.input, output, self.expected)
467 class ParserTestSuite(CustomTestSuite):
470 A collection of ParserTestCases.
472 A ParserTestSuite instance manufactures ParserTestCases,
473 keeps track of them, and provides a shared test fixture (a-la
474 setUp and tearDown).
477 test_case_class = ParserTestCase
479 def generateTests(self, dict, dictname='totest'):
481 Stock the suite with test cases generated from a test data dictionary.
483 Each dictionary key (test type name) maps to a list of tests. Each
484 test is a list: input, expected output, optional modifier. The
485 optional third entry, a behavior modifier, can be 0 (temporarily
486 disable this test) or 1 (run this test under the pdb debugger). Tests
487 should be self-documenting and not require external comments.
489 for name, cases in dict.items():
490 for casenum in range(len(cases)):
491 case = cases[casenum]
492 run_in_debugger = False
493 if len(case)==3:
494 if case[2]:
495 run_in_debugger = True
496 else:
497 continue
498 self.addTestCase(
499 self.test_case_class, 'test_parser',
500 input=case[0], expected=case[1],
501 id='%s[%r][%s]' % (dictname, name, casenum),
502 run_in_debugger=run_in_debugger)
505 class PEPParserTestCase(ParserTestCase):
507 """PEP-specific parser test case."""
509 parser = rst.Parser(rfc2822=True, inliner=rst.states.Inliner())
510 """Parser shared by all PEPParserTestCases."""
512 option_parser = frontend.OptionParser(components=(rst.Parser, pep.Reader))
513 settings = option_parser.get_default_values()
514 settings.report_level = 5
515 settings.halt_level = 5
516 settings.debug = package_unittest.debug
519 class PEPParserTestSuite(ParserTestSuite):
521 """A collection of PEPParserTestCases."""
523 test_case_class = PEPParserTestCase
526 class GridTableParserTestCase(CustomTestCase):
528 parser = tableparser.GridTableParser()
530 def test_parse_table(self):
531 self.parser.setup(StringList(string2lines(self.input), 'test data'))
532 try:
533 self.parser.find_head_body_sep()
534 self.parser.parse_table()
535 output = self.parser.cells
536 except Exception, details:
537 output = '%s: %s' % (details.__class__.__name__, details)
538 self.compare_output(self.input, pformat(output) + '\n',
539 pformat(self.expected) + '\n')
541 def test_parse(self):
542 try:
543 output = self.parser.parse(StringList(string2lines(self.input),
544 'test data'))
545 except Exception, details:
546 output = '%s: %s' % (details.__class__.__name__, details)
547 self.compare_output(self.input, pformat(output) + '\n',
548 pformat(self.expected) + '\n')
551 class GridTableParserTestSuite(CustomTestSuite):
554 A collection of GridTableParserTestCases.
556 A GridTableParserTestSuite instance manufactures GridTableParserTestCases,
557 keeps track of them, and provides a shared test fixture (a-la setUp and
558 tearDown).
561 test_case_class = GridTableParserTestCase
563 def generateTests(self, dict, dictname='totest'):
565 Stock the suite with test cases generated from a test data dictionary.
567 Each dictionary key (test type name) maps to a list of tests. Each
568 test is a list: an input table, expected output from parse_table(),
569 expected output from parse(), optional modifier. The optional fourth
570 entry, a behavior modifier, can be 0 (temporarily disable this test)
571 or 1 (run this test under the pdb debugger). Tests should be
572 self-documenting and not require external comments.
574 for name, cases in dict.items():
575 for casenum in range(len(cases)):
576 case = cases[casenum]
577 run_in_debugger = False
578 if len(case) == 4:
579 if case[-1]:
580 run_in_debugger = True
581 else:
582 continue
583 self.addTestCase(self.test_case_class, 'test_parse_table',
584 input=case[0], expected=case[1],
585 id='%s[%r][%s]' % (dictname, name, casenum),
586 run_in_debugger=run_in_debugger)
587 self.addTestCase(self.test_case_class, 'test_parse',
588 input=case[0], expected=case[2],
589 id='%s[%r][%s]' % (dictname, name, casenum),
590 run_in_debugger=run_in_debugger)
593 class SimpleTableParserTestCase(GridTableParserTestCase):
595 parser = tableparser.SimpleTableParser()
598 class SimpleTableParserTestSuite(CustomTestSuite):
601 A collection of SimpleTableParserTestCases.
604 test_case_class = SimpleTableParserTestCase
606 def generateTests(self, dict, dictname='totest'):
608 Stock the suite with test cases generated from a test data dictionary.
610 Each dictionary key (test type name) maps to a list of tests. Each
611 test is a list: an input table, expected output from parse(), optional
612 modifier. The optional third entry, a behavior modifier, can be 0
613 (temporarily disable this test) or 1 (run this test under the pdb
614 debugger). Tests should be self-documenting and not require external
615 comments.
617 for name, cases in dict.items():
618 for casenum in range(len(cases)):
619 case = cases[casenum]
620 run_in_debugger = False
621 if len(case) == 3:
622 if case[-1]:
623 run_in_debugger = True
624 else:
625 continue
626 self.addTestCase(self.test_case_class, 'test_parse',
627 input=case[0], expected=case[1],
628 id='%s[%r][%s]' % (dictname, name, casenum),
629 run_in_debugger=run_in_debugger)
632 class PythonModuleParserTestCase(CustomTestCase):
634 def test_parser(self):
635 if self.run_in_debugger:
636 pdb.set_trace()
637 try:
638 import compiler
639 except ImportError:
640 # skip on Python 3
641 return
642 from docutils.readers.python import moduleparser
643 module = moduleparser.parse_module(self.input, 'test data').pformat()
644 output = str(module)
645 self.compare_output(self.input, output, self.expected)
647 def test_token_parser_rhs(self):
648 if self.run_in_debugger:
649 pdb.set_trace()
650 try:
651 import compiler
652 except ImportError:
653 # skip on Python 3
654 return
655 from docutils.readers.python import moduleparser
656 tr = moduleparser.TokenParser(self.input)
657 output = tr.rhs(1)
658 self.compare_output(self.input, output, self.expected)
661 class PythonModuleParserTestSuite(CustomTestSuite):
664 A collection of PythonModuleParserTestCase.
667 def generateTests(self, dict, dictname='totest',
668 testmethod='test_parser'):
670 Stock the suite with test cases generated from a test data dictionary.
672 Each dictionary key (test type's name) maps to a list of tests. Each
673 test is a list: input, expected output, optional modifier. The
674 optional third entry, a behavior modifier, can be 0 (temporarily
675 disable this test) or 1 (run this test under the pdb debugger). Tests
676 should be self-documenting and not require external comments.
678 for name, cases in dict.items():
679 for casenum in range(len(cases)):
680 case = cases[casenum]
681 run_in_debugger = False
682 if len(case)==3:
683 if case[2]:
684 run_in_debugger = True
685 else:
686 continue
687 self.addTestCase(
688 PythonModuleParserTestCase, testmethod,
689 input=case[0], expected=case[1],
690 id='%s[%r][%s]' % (dictname, name, casenum),
691 run_in_debugger=run_in_debugger)
694 class WriterPublishTestCase(CustomTestCase, docutils.SettingsSpec):
697 Test case for publish.
700 settings_default_overrides = {'_disable_config': 1,
701 'strict_visitor': 1}
702 writer_name = '' # set in subclasses or constructor
704 def __init__(self, *args, **kwargs):
705 if 'writer_name' in kwargs:
706 self.writer_name = kwargs['writer_name']
707 del kwargs['writer_name']
708 CustomTestCase.__init__(self, *args, **kwargs)
710 def test_publish(self):
711 if self.run_in_debugger:
712 pdb.set_trace()
713 output = docutils.core.publish_string(
714 source=self.input,
715 reader_name='standalone',
716 parser_name='restructuredtext',
717 writer_name=self.writer_name,
718 settings_spec=self,
719 settings_overrides=self.suite_settings)
720 self.compare_output(self.input, output, self.expected)
723 class PublishTestSuite(CustomTestSuite):
725 def __init__(self, writer_name, suite_settings=None):
727 `writer_name` is the name of the writer to use.
729 CustomTestSuite.__init__(self, suite_settings=suite_settings)
730 self.test_class = WriterPublishTestCase
731 self.writer_name = writer_name
733 def generateTests(self, dict, dictname='totest'):
734 for name, cases in dict.items():
735 for casenum in range(len(cases)):
736 case = cases[casenum]
737 run_in_debugger = False
738 if len(case)==3:
739 if case[2]:
740 run_in_debugger = True
741 else:
742 continue
743 self.addTestCase(
744 self.test_class, 'test_publish',
745 input=case[0], expected=case[1],
746 id='%s[%r][%s]' % (dictname, name, casenum),
747 run_in_debugger=run_in_debugger,
748 # Passed to constructor of self.test_class:
749 writer_name=self.writer_name)
752 class HtmlPublishPartsTestSuite(CustomTestSuite):
754 def generateTests(self, dict, dictname='totest'):
755 for name, (settings_overrides, cases) in dict.items():
756 settings = self.suite_settings.copy()
757 settings.update(settings_overrides)
758 for casenum in range(len(cases)):
759 case = cases[casenum]
760 run_in_debugger = False
761 if len(case)==3:
762 if case[2]:
763 run_in_debugger = True
764 else:
765 continue
766 self.addTestCase(
767 HtmlWriterPublishPartsTestCase, 'test_publish',
768 input=case[0], expected=case[1],
769 id='%s[%r][%s]' % (dictname, name, casenum),
770 run_in_debugger=run_in_debugger,
771 suite_settings=settings)
774 class HtmlWriterPublishPartsTestCase(WriterPublishTestCase):
777 Test case for HTML writer via the publish_parts interface.
780 writer_name = 'html'
782 settings_default_overrides = \
783 WriterPublishTestCase.settings_default_overrides.copy()
784 settings_default_overrides['stylesheet'] = ''
786 def test_publish(self):
787 if self.run_in_debugger:
788 pdb.set_trace()
789 parts = docutils.core.publish_parts(
790 source=self.input,
791 reader_name='standalone',
792 parser_name='restructuredtext',
793 writer_name=self.writer_name,
794 settings_spec=self,
795 settings_overrides=self.suite_settings)
796 output = self.format_output(parts)
797 # interpolate standard variables:
798 expected = self.expected % {'version': docutils.__version__}
799 self.compare_output(self.input, output, expected)
801 standard_content_type_template = ('<meta http-equiv="Content-Type"'
802 ' content="text/html; charset=%s" />\n')
803 standard_generator_template = (
804 '<meta name="generator"'
805 ' content="Docutils %s: http://docutils.sourceforge.net/" />\n')
806 standard_html_meta_value = (
807 standard_content_type_template
808 + standard_generator_template % docutils.__version__)
809 standard_meta_value = standard_html_meta_value % 'utf-8'
810 standard_html_prolog = """\
811 <?xml version="1.0" encoding="%s" ?>
812 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
815 def format_output(self, parts):
816 """Minimize & standardize the output."""
817 # remove redundant parts & uninteresting parts:
818 del parts['whole']
819 assert parts['body'] == parts['fragment']
820 del parts['body']
821 del parts['body_pre_docinfo']
822 del parts['body_prefix']
823 del parts['body_suffix']
824 del parts['head']
825 del parts['head_prefix']
826 del parts['encoding']
827 del parts['version']
828 # remove standard portions:
829 parts['meta'] = parts['meta'].replace(self.standard_meta_value, '')
830 parts['html_head'] = parts['html_head'].replace(
831 self.standard_html_meta_value, '...')
832 parts['html_prolog'] = parts['html_prolog'].replace(
833 self.standard_html_prolog, '')
834 # remove empty values:
835 for key in parts.keys():
836 if not parts[key]:
837 del parts[key]
838 # standard output format:
839 keys = parts.keys()
840 keys.sort()
841 output = []
842 for key in keys:
843 output.append("%r: '''%s'''"
844 % (key, parts[key]))
845 if output[-1].endswith("\n'''"):
846 output[-1] = output[-1][:-4] + "\\n'''"
847 return '{' + ',\n '.join(output) + '}\n'
850 def exception_data(func, *args, **kwds):
852 Execute `func(*args, **kwds)` and return the resulting exception, the
853 exception arguments, and the formatted exception string.
855 try:
856 func(*args, **kwds)
857 except Exception, detail:
858 return (detail, detail.args,
859 '%s: %s' % (detail.__class__.__name__, detail))
862 def _format_str(*args):
863 r"""
864 Return a tuple containing representations of all args.
866 Same as map(repr, args) except that it returns multi-line
867 representations for strings containing newlines, e.g.::
869 '''\
870 foo \n\
873 baz'''
875 instead of::
877 'foo \nbar\n\nbaz'
879 This is a helper function for CustomTestCase.
881 return_tuple = []
882 for i in args:
883 r = repr(i)
884 if ( (isinstance(i, bytes) or isinstance(i, unicode))
885 and '\n' in i):
886 stripped = ''
887 if isinstance(i, unicode) and r.startswith('u'):
888 stripped = r[0]
889 r = r[1:]
890 elif isinstance(i, bytes) and r.startswith('b'):
891 stripped = r[0]
892 r = r[1:]
893 # quote_char = "'" or '"'
894 quote_char = r[0]
895 assert quote_char in ("'", '"'), quote_char
896 assert r[0] == r[-1]
897 r = r[1:-1]
898 r = (stripped + 3 * quote_char + '\\\n' +
899 re.sub(r'(?<!\\)((\\\\)*)\\n', r'\1\n', r) +
900 3 * quote_char)
901 r = re.sub(r' \n', r' \\n\\\n', r)
902 return_tuple.append(r)
903 return tuple(return_tuple)