math, error_reporting, and urischemes moved to the utils package.
[docutils.git] / test / DocutilsTestSupport.py
blob2988768b6a84f54a8de7fe0a27fbe7b02fdec22f
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.utils.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, utils
69 from docutils.utils import urischemes
70 from docutils.transforms import universal
71 from docutils.parsers import rst
72 from docutils.parsers.rst import states, tableparser, roles, languages
73 from docutils.readers import standalone, pep
74 from docutils.statemachine import StringList, string2lines
75 from docutils._compat import bytes
76 except ImportError:
77 # The importing module (usually __init__.py in one of the
78 # subdirectories) may catch ImportErrors in order to detect the
79 # absence of DocutilsTestSupport in sys.path. Thus, ImportErrors
80 # resulting from problems with importing Docutils modules must
81 # caught here.
82 traceback.print_exc()
83 sys.exit(1)
86 try:
87 import mypdb as pdb
88 except:
89 import pdb
92 # Hack to make repr(StringList) look like repr(list):
93 StringList.__repr__ = StringList.__str__
96 class DevNull:
98 """Output sink."""
100 def write(self, string):
101 pass
103 def close(self):
104 pass
107 class StandardTestCase(unittest.TestCase):
110 Helper class, providing the same interface as unittest.TestCase,
111 but with useful setUp and comparison methods.
114 def setUp(self):
115 os.chdir(testroot)
117 def assertEqual(self, first, second, msg=None):
118 """Fail if the two objects are unequal as determined by the '=='
119 operator.
121 if not first == second:
122 raise self.failureException, \
123 (msg or '%s != %s' % _format_str(first, second))
125 def assertNotEqual(self, first, second, msg=None):
126 """Fail if the two objects are equal as determined by the '=='
127 operator.
129 if first == second:
130 raise self.failureException, \
131 (msg or '%s == %s' % _format_str(first, second))
133 # aliases for assertion methods, deprecated since Python 2.7
135 failUnlessEqual = assertEquals = assertEqual
137 assertNotEquals = failIfEqual = assertNotEqual
140 class CustomTestCase(StandardTestCase):
143 Helper class, providing extended functionality over unittest.TestCase.
145 The methods assertEqual and assertNotEqual have been overwritten
146 to provide better support for multi-line strings. Furthermore,
147 see the compare_output method and the parameter list of __init__.
150 compare = difflib.Differ().compare
151 """Comparison method shared by all subclasses."""
153 def __init__(self, method_name, input, expected, id,
154 run_in_debugger=True, suite_settings=None):
156 Initialise the CustomTestCase.
158 Arguments:
160 method_name -- name of test method to run.
161 input -- input to the parser.
162 expected -- expected output from the parser.
163 id -- unique test identifier, used by the test framework.
164 run_in_debugger -- if true, run this test under the pdb debugger.
165 suite_settings -- settings overrides for this test suite.
167 self.id = id
168 self.input = input
169 self.expected = expected
170 self.run_in_debugger = run_in_debugger
171 self.suite_settings = suite_settings.copy() or {}
173 # Ring your mother.
174 unittest.TestCase.__init__(self, method_name)
176 def __str__(self):
178 Return string conversion. Overridden to give test id, in addition to
179 method name.
181 return '%s; %s' % (self.id, unittest.TestCase.__str__(self))
183 def __repr__(self):
184 return "<%s %s>" % (self.id, unittest.TestCase.__repr__(self))
186 def clear_roles(self):
187 # Language-specific roles and roles added by the
188 # "default-role" and "role" directives are currently stored
189 # globally in the roles._roles dictionary. This workaround
190 # empties that dictionary.
191 roles._roles = {}
193 def setUp(self):
194 StandardTestCase.setUp(self)
195 self.clear_roles()
197 def compare_output(self, input, output, expected):
198 """`input`, `output`, and `expected` should all be strings."""
199 if isinstance(input, unicode):
200 input = input.encode('raw_unicode_escape')
201 if sys.version_info > (3,):
202 # API difference: Python 3's node.__str__ doesn't escape
203 #assert expected is None or isinstance(expected, unicode)
204 if isinstance(expected, bytes):
205 expected = expected.decode('utf-8')
206 if isinstance(output, bytes):
207 output = output.decode('utf-8')
208 else:
209 if isinstance(expected, unicode):
210 expected = expected.encode('raw_unicode_escape')
211 if isinstance(output, unicode):
212 output = output.encode('raw_unicode_escape')
213 # Normalize line endings:
214 if expected:
215 expected = '\n'.join(expected.splitlines())
216 if output:
217 output = '\n'.join(output.splitlines())
218 try:
219 self.assertEqual(output, expected)
220 except AssertionError, error:
221 print >>sys.stderr, '\n%s\ninput:' % (self,)
222 print >>sys.stderr, input
223 try:
224 comparison = ''.join(self.compare(expected.splitlines(1),
225 output.splitlines(1)))
226 print >>sys.stderr, '-: expected\n+: output'
227 print >>sys.stderr, comparison
228 except AttributeError: # expected or output not a string
229 # alternative output for non-strings:
230 print >>sys.stderr, 'expected: %r' % expected
231 print >>sys.stderr, 'output: %r' % output
232 raise error
235 class CustomTestSuite(unittest.TestSuite):
238 A collection of CustomTestCases.
240 Provides test suite ID generation and a method for adding test cases.
243 id = ''
244 """Identifier for the TestSuite. Prepended to the
245 TestCase identifiers to make identification easier."""
247 next_test_case_id = 0
248 """The next identifier to use for non-identified test cases."""
250 def __init__(self, tests=(), id=None, suite_settings=None):
252 Initialize the CustomTestSuite.
254 Arguments:
256 id -- identifier for the suite, prepended to test cases.
257 suite_settings -- settings overrides for this test suite.
259 unittest.TestSuite.__init__(self, tests)
260 self.suite_settings = suite_settings or {}
261 if id is None:
262 mypath = os.path.abspath(
263 sys.modules[CustomTestSuite.__module__].__file__)
264 outerframes = inspect.getouterframes(inspect.currentframe())
265 for outerframe in outerframes[1:]:
266 if outerframe[3] != '__init__':
267 callerpath = outerframe[1]
268 if callerpath is None:
269 # It happens sometimes. Why is a mystery.
270 callerpath = os.getcwd()
271 callerpath = os.path.abspath(callerpath)
272 break
273 mydir, myname = os.path.split(mypath)
274 if not mydir:
275 mydir = os.curdir
276 if callerpath.startswith(mydir):
277 self.id = callerpath[len(mydir) + 1:] # caller's module
278 else:
279 self.id = callerpath
280 else:
281 self.id = id
283 def addTestCase(self, test_case_class, method_name, input, expected,
284 id=None, run_in_debugger=False, **kwargs):
286 Create a CustomTestCase in the CustomTestSuite.
287 Also return it, just in case.
289 Arguments:
291 test_case_class -- the CustomTestCase to add
292 method_name -- a string; CustomTestCase.method_name is the test
293 input -- input to the parser.
294 expected -- expected output from the parser.
295 id -- unique test identifier, used by the test framework.
296 run_in_debugger -- if true, run this test under the pdb debugger.
298 if id is None: # generate id if required
299 id = self.next_test_case_id
300 self.next_test_case_id += 1
301 # test identifier will become suiteid.testid
302 tcid = '%s: %s' % (self.id, id)
303 # suite_settings may be passed as a parameter;
304 # if not, set from attribute:
305 kwargs.setdefault('suite_settings', self.suite_settings)
306 # generate and add test case
307 tc = test_case_class(method_name, input, expected, tcid,
308 run_in_debugger=run_in_debugger, **kwargs)
309 self.addTest(tc)
310 return tc
312 def generate_no_tests(self, *args, **kwargs):
313 pass
316 class TransformTestCase(CustomTestCase):
319 Output checker for the transform.
321 Should probably be called TransformOutputChecker, but I can deal with
322 that later when/if someone comes up with a category of transform test
323 cases that have nothing to do with the input and output of the transform.
326 option_parser = frontend.OptionParser(components=(rst.Parser,))
327 settings = option_parser.get_default_values()
328 settings.report_level = 1
329 settings.halt_level = 5
330 settings.debug = package_unittest.debug
331 settings.warning_stream = DevNull()
332 unknown_reference_resolvers = ()
334 def __init__(self, *args, **kwargs):
335 self.transforms = kwargs['transforms']
336 """List of transforms to perform for this test case."""
338 self.parser = kwargs['parser']
339 """Input parser for this test case."""
341 del kwargs['transforms'], kwargs['parser'] # only wanted here
342 CustomTestCase.__init__(self, *args, **kwargs)
344 def supports(self, format):
345 return 1
347 def test_transforms(self):
348 if self.run_in_debugger:
349 pdb.set_trace()
350 settings = self.settings.copy()
351 settings.__dict__.update(self.suite_settings)
352 document = utils.new_document('test data', settings)
353 self.parser.parse(self.input, document)
354 # Don't do a ``populate_from_components()`` because that would
355 # enable the Transformer's default transforms.
356 document.transformer.add_transforms(self.transforms)
357 document.transformer.add_transform(universal.TestMessages)
358 document.transformer.components['writer'] = self
359 document.transformer.apply_transforms()
360 output = document.pformat()
361 self.compare_output(self.input, output, self.expected)
363 def test_transforms_verbosely(self):
364 if self.run_in_debugger:
365 pdb.set_trace()
366 print '\n', self.id
367 print '-' * 70
368 print self.input
369 settings = self.settings.copy()
370 settings.__dict__.update(self.suite_settings)
371 document = utils.new_document('test data', settings)
372 self.parser.parse(self.input, document)
373 print '-' * 70
374 print document.pformat()
375 for transformClass in self.transforms:
376 transformClass(document).apply()
377 output = document.pformat()
378 print '-' * 70
379 print output
380 self.compare_output(self.input, output, self.expected)
383 class TransformTestSuite(CustomTestSuite):
386 A collection of TransformTestCases.
388 A TransformTestSuite instance manufactures TransformTestCases,
389 keeps track of them, and provides a shared test fixture (a-la
390 setUp and tearDown).
393 def __init__(self, parser, suite_settings=None):
394 self.parser = parser
395 """Parser shared by all test cases."""
397 CustomTestSuite.__init__(self, suite_settings=suite_settings)
399 def generateTests(self, dict, dictname='totest',
400 testmethod='test_transforms'):
402 Stock the suite with test cases generated from a test data dictionary.
404 Each dictionary key (test type's name) maps to a tuple, whose
405 first item is a list of transform classes and whose second
406 item is a list of tests. Each test is a list: input, expected
407 output, optional modifier. The optional third entry, a
408 behavior modifier, can be 0 (temporarily disable this test) or
409 1 (run this test under the pdb debugger). Tests should be
410 self-documenting and not require external comments.
412 for name, (transforms, cases) in dict.items():
413 for casenum in range(len(cases)):
414 case = cases[casenum]
415 run_in_debugger = False
416 if len(case)==3:
417 # TODO: (maybe) change the 3rd argument to a dict, so it
418 # can handle more cases by keyword ('disable', 'debug',
419 # 'settings'), here and in other generateTests methods.
420 # But there's also the method that
421 # HtmlPublishPartsTestSuite uses <DJG>
422 if case[2]:
423 run_in_debugger = True
424 else:
425 continue
426 self.addTestCase(
427 TransformTestCase, testmethod,
428 transforms=transforms, parser=self.parser,
429 input=case[0], expected=case[1],
430 id='%s[%r][%s]' % (dictname, name, casenum),
431 run_in_debugger=run_in_debugger)
434 class ParserTestCase(CustomTestCase):
437 Output checker for the parser.
439 Should probably be called ParserOutputChecker, but I can deal with
440 that later when/if someone comes up with a category of parser test
441 cases that have nothing to do with the input and output of the parser.
444 parser = rst.Parser()
445 """Parser shared by all ParserTestCases."""
447 option_parser = frontend.OptionParser(components=(rst.Parser,))
448 settings = option_parser.get_default_values()
449 settings.report_level = 5
450 settings.halt_level = 5
451 settings.debug = package_unittest.debug
453 def test_parser(self):
454 if self.run_in_debugger:
455 pdb.set_trace()
456 settings = self.settings.copy()
457 settings.__dict__.update(self.suite_settings)
458 document = utils.new_document('test data', settings)
459 self.parser.parse(self.input, document)
460 output = document.pformat()
461 self.compare_output(self.input, output, self.expected)
464 class ParserTestSuite(CustomTestSuite):
467 A collection of ParserTestCases.
469 A ParserTestSuite instance manufactures ParserTestCases,
470 keeps track of them, and provides a shared test fixture (a-la
471 setUp and tearDown).
474 test_case_class = ParserTestCase
476 def generateTests(self, dict, dictname='totest'):
478 Stock the suite with test cases generated from a test data dictionary.
480 Each dictionary key (test type name) maps to a list of tests. Each
481 test is a list: input, expected output, optional modifier. The
482 optional third entry, a behavior modifier, can be 0 (temporarily
483 disable this test) or 1 (run this test under the pdb debugger). Tests
484 should be self-documenting and not require external comments.
486 for name, cases in dict.items():
487 for casenum in range(len(cases)):
488 case = cases[casenum]
489 run_in_debugger = False
490 if len(case)==3:
491 if case[2]:
492 run_in_debugger = True
493 else:
494 continue
495 self.addTestCase(
496 self.test_case_class, 'test_parser',
497 input=case[0], expected=case[1],
498 id='%s[%r][%s]' % (dictname, name, casenum),
499 run_in_debugger=run_in_debugger)
502 class PEPParserTestCase(ParserTestCase):
504 """PEP-specific parser test case."""
506 parser = rst.Parser(rfc2822=True, inliner=rst.states.Inliner())
507 """Parser shared by all PEPParserTestCases."""
509 option_parser = frontend.OptionParser(components=(rst.Parser, pep.Reader))
510 settings = option_parser.get_default_values()
511 settings.report_level = 5
512 settings.halt_level = 5
513 settings.debug = package_unittest.debug
516 class PEPParserTestSuite(ParserTestSuite):
518 """A collection of PEPParserTestCases."""
520 test_case_class = PEPParserTestCase
523 class GridTableParserTestCase(CustomTestCase):
525 parser = tableparser.GridTableParser()
527 def test_parse_table(self):
528 self.parser.setup(StringList(string2lines(self.input), 'test data'))
529 try:
530 self.parser.find_head_body_sep()
531 self.parser.parse_table()
532 output = self.parser.cells
533 except Exception, details:
534 output = '%s: %s' % (details.__class__.__name__, details)
535 self.compare_output(self.input, pformat(output) + '\n',
536 pformat(self.expected) + '\n')
538 def test_parse(self):
539 try:
540 output = self.parser.parse(StringList(string2lines(self.input),
541 'test data'))
542 except Exception, details:
543 output = '%s: %s' % (details.__class__.__name__, details)
544 self.compare_output(self.input, pformat(output) + '\n',
545 pformat(self.expected) + '\n')
548 class GridTableParserTestSuite(CustomTestSuite):
551 A collection of GridTableParserTestCases.
553 A GridTableParserTestSuite instance manufactures GridTableParserTestCases,
554 keeps track of them, and provides a shared test fixture (a-la setUp and
555 tearDown).
558 test_case_class = GridTableParserTestCase
560 def generateTests(self, dict, dictname='totest'):
562 Stock the suite with test cases generated from a test data dictionary.
564 Each dictionary key (test type name) maps to a list of tests. Each
565 test is a list: an input table, expected output from parse_table(),
566 expected output from parse(), optional modifier. The optional fourth
567 entry, a behavior modifier, can be 0 (temporarily disable this test)
568 or 1 (run this test under the pdb debugger). Tests should be
569 self-documenting and not require external comments.
571 for name, cases in dict.items():
572 for casenum in range(len(cases)):
573 case = cases[casenum]
574 run_in_debugger = False
575 if len(case) == 4:
576 if case[-1]:
577 run_in_debugger = True
578 else:
579 continue
580 self.addTestCase(self.test_case_class, 'test_parse_table',
581 input=case[0], expected=case[1],
582 id='%s[%r][%s]' % (dictname, name, casenum),
583 run_in_debugger=run_in_debugger)
584 self.addTestCase(self.test_case_class, 'test_parse',
585 input=case[0], expected=case[2],
586 id='%s[%r][%s]' % (dictname, name, casenum),
587 run_in_debugger=run_in_debugger)
590 class SimpleTableParserTestCase(GridTableParserTestCase):
592 parser = tableparser.SimpleTableParser()
595 class SimpleTableParserTestSuite(CustomTestSuite):
598 A collection of SimpleTableParserTestCases.
601 test_case_class = SimpleTableParserTestCase
603 def generateTests(self, dict, dictname='totest'):
605 Stock the suite with test cases generated from a test data dictionary.
607 Each dictionary key (test type name) maps to a list of tests. Each
608 test is a list: an input table, expected output from parse(), optional
609 modifier. The optional third entry, a behavior modifier, can be 0
610 (temporarily disable this test) or 1 (run this test under the pdb
611 debugger). Tests should be self-documenting and not require external
612 comments.
614 for name, cases in dict.items():
615 for casenum in range(len(cases)):
616 case = cases[casenum]
617 run_in_debugger = False
618 if len(case) == 3:
619 if case[-1]:
620 run_in_debugger = True
621 else:
622 continue
623 self.addTestCase(self.test_case_class, 'test_parse',
624 input=case[0], expected=case[1],
625 id='%s[%r][%s]' % (dictname, name, casenum),
626 run_in_debugger=run_in_debugger)
629 class PythonModuleParserTestCase(CustomTestCase):
631 def test_parser(self):
632 if self.run_in_debugger:
633 pdb.set_trace()
634 try:
635 import compiler
636 except ImportError:
637 # skip on Python 3
638 return
639 from docutils.readers.python import moduleparser
640 module = moduleparser.parse_module(self.input, 'test data').pformat()
641 output = str(module)
642 self.compare_output(self.input, output, self.expected)
644 def test_token_parser_rhs(self):
645 if self.run_in_debugger:
646 pdb.set_trace()
647 try:
648 import compiler
649 except ImportError:
650 # skip on Python 3
651 return
652 from docutils.readers.python import moduleparser
653 tr = moduleparser.TokenParser(self.input)
654 output = tr.rhs(1)
655 self.compare_output(self.input, output, self.expected)
658 class PythonModuleParserTestSuite(CustomTestSuite):
661 A collection of PythonModuleParserTestCase.
664 def generateTests(self, dict, dictname='totest',
665 testmethod='test_parser'):
667 Stock the suite with test cases generated from a test data dictionary.
669 Each dictionary key (test type's name) maps to a list of tests. Each
670 test is a list: input, expected output, optional modifier. The
671 optional third entry, a behavior modifier, can be 0 (temporarily
672 disable this test) or 1 (run this test under the pdb debugger). Tests
673 should be self-documenting and not require external comments.
675 for name, cases in dict.items():
676 for casenum in range(len(cases)):
677 case = cases[casenum]
678 run_in_debugger = False
679 if len(case)==3:
680 if case[2]:
681 run_in_debugger = True
682 else:
683 continue
684 self.addTestCase(
685 PythonModuleParserTestCase, testmethod,
686 input=case[0], expected=case[1],
687 id='%s[%r][%s]' % (dictname, name, casenum),
688 run_in_debugger=run_in_debugger)
691 class WriterPublishTestCase(CustomTestCase, docutils.SettingsSpec):
694 Test case for publish.
697 settings_default_overrides = {'_disable_config': 1,
698 'strict_visitor': 1}
699 writer_name = '' # set in subclasses or constructor
701 def __init__(self, *args, **kwargs):
702 if 'writer_name' in kwargs:
703 self.writer_name = kwargs['writer_name']
704 del kwargs['writer_name']
705 CustomTestCase.__init__(self, *args, **kwargs)
707 def test_publish(self):
708 if self.run_in_debugger:
709 pdb.set_trace()
710 output = docutils.core.publish_string(
711 source=self.input,
712 reader_name='standalone',
713 parser_name='restructuredtext',
714 writer_name=self.writer_name,
715 settings_spec=self,
716 settings_overrides=self.suite_settings)
717 self.compare_output(self.input, output, self.expected)
720 class PublishTestSuite(CustomTestSuite):
722 def __init__(self, writer_name, suite_settings=None):
724 `writer_name` is the name of the writer to use.
726 CustomTestSuite.__init__(self, suite_settings=suite_settings)
727 self.test_class = WriterPublishTestCase
728 self.writer_name = writer_name
730 def generateTests(self, dict, dictname='totest'):
731 for name, cases in dict.items():
732 for casenum in range(len(cases)):
733 case = cases[casenum]
734 run_in_debugger = False
735 if len(case)==3:
736 if case[2]:
737 run_in_debugger = True
738 else:
739 continue
740 self.addTestCase(
741 self.test_class, 'test_publish',
742 input=case[0], expected=case[1],
743 id='%s[%r][%s]' % (dictname, name, casenum),
744 run_in_debugger=run_in_debugger,
745 # Passed to constructor of self.test_class:
746 writer_name=self.writer_name)
749 class HtmlPublishPartsTestSuite(CustomTestSuite):
751 def generateTests(self, dict, dictname='totest'):
752 for name, (settings_overrides, cases) in dict.items():
753 settings = self.suite_settings.copy()
754 settings.update(settings_overrides)
755 for casenum in range(len(cases)):
756 case = cases[casenum]
757 run_in_debugger = False
758 if len(case)==3:
759 if case[2]:
760 run_in_debugger = True
761 else:
762 continue
763 self.addTestCase(
764 HtmlWriterPublishPartsTestCase, 'test_publish',
765 input=case[0], expected=case[1],
766 id='%s[%r][%s]' % (dictname, name, casenum),
767 run_in_debugger=run_in_debugger,
768 suite_settings=settings)
771 class HtmlWriterPublishPartsTestCase(WriterPublishTestCase):
774 Test case for HTML writer via the publish_parts interface.
777 writer_name = 'html'
779 settings_default_overrides = \
780 WriterPublishTestCase.settings_default_overrides.copy()
781 settings_default_overrides['stylesheet'] = ''
783 def test_publish(self):
784 if self.run_in_debugger:
785 pdb.set_trace()
786 parts = docutils.core.publish_parts(
787 source=self.input,
788 reader_name='standalone',
789 parser_name='restructuredtext',
790 writer_name=self.writer_name,
791 settings_spec=self,
792 settings_overrides=self.suite_settings)
793 output = self.format_output(parts)
794 # interpolate standard variables:
795 expected = self.expected % {'version': docutils.__version__}
796 self.compare_output(self.input, output, expected)
798 standard_content_type_template = ('<meta http-equiv="Content-Type"'
799 ' content="text/html; charset=%s" />\n')
800 standard_generator_template = (
801 '<meta name="generator"'
802 ' content="Docutils %s: http://docutils.sourceforge.net/" />\n')
803 standard_html_meta_value = (
804 standard_content_type_template
805 + standard_generator_template % docutils.__version__)
806 standard_meta_value = standard_html_meta_value % 'utf-8'
807 standard_html_prolog = """\
808 <?xml version="1.0" encoding="%s" ?>
809 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
812 def format_output(self, parts):
813 """Minimize & standardize the output."""
814 # remove redundant parts & uninteresting parts:
815 del parts['whole']
816 assert parts['body'] == parts['fragment']
817 del parts['body']
818 del parts['body_pre_docinfo']
819 del parts['body_prefix']
820 del parts['body_suffix']
821 del parts['head']
822 del parts['head_prefix']
823 del parts['encoding']
824 del parts['version']
825 # remove standard portions:
826 parts['meta'] = parts['meta'].replace(self.standard_meta_value, '')
827 parts['html_head'] = parts['html_head'].replace(
828 self.standard_html_meta_value, '...')
829 parts['html_prolog'] = parts['html_prolog'].replace(
830 self.standard_html_prolog, '')
831 # remove empty values:
832 for key in parts.keys():
833 if not parts[key]:
834 del parts[key]
835 # standard output format:
836 keys = parts.keys()
837 keys.sort()
838 output = []
839 for key in keys:
840 output.append("%r: '''%s'''"
841 % (key, parts[key]))
842 if output[-1].endswith("\n'''"):
843 output[-1] = output[-1][:-4] + "\\n'''"
844 return '{' + ',\n '.join(output) + '}\n'
847 def exception_data(func, *args, **kwds):
849 Execute `func(*args, **kwds)` and return the resulting exception, the
850 exception arguments, and the formatted exception string.
852 try:
853 func(*args, **kwds)
854 except Exception, detail:
855 return (detail, detail.args,
856 '%s: %s' % (detail.__class__.__name__, detail))
859 def _format_str(*args):
860 r"""
861 Return a tuple containing representations of all args.
863 Same as map(repr, args) except that it returns multi-line
864 representations for strings containing newlines, e.g.::
866 '''\
867 foo \n\
870 baz'''
872 instead of::
874 'foo \nbar\n\nbaz'
876 This is a helper function for CustomTestCase.
878 return_tuple = []
879 for i in args:
880 r = repr(i)
881 if ( (isinstance(i, bytes) or isinstance(i, unicode))
882 and '\n' in i):
883 stripped = ''
884 if isinstance(i, unicode) and r.startswith('u'):
885 stripped = r[0]
886 r = r[1:]
887 elif isinstance(i, bytes) and r.startswith('b'):
888 stripped = r[0]
889 r = r[1:]
890 # quote_char = "'" or '"'
891 quote_char = r[0]
892 assert quote_char in ("'", '"'), quote_char
893 assert r[0] == r[-1]
894 r = r[1:-1]
895 r = (stripped + 3 * quote_char + '\\\n' +
896 re.sub(r'(?<!\\)((\\\\)*)\\n', r'\1\n', r) +
897 3 * quote_char)
898 r = re.sub(r' \n', r' \\n\\\n', r)
899 return_tuple.append(r)
900 return tuple(return_tuple)