* Consolidated version numbering documentation in docs/dev/policies.txt (moved text...
[docutils.git] / docutils / test / DocutilsTestSupport.py
blobda3968ae845c4a7687b2222b84a064ab00cdb7bc
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 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 # assertIn and assertNotIn: new in Python 2.7:
134 def assertIn(self, a, b, msg=None):
135 if a not in b:
136 raise self.failureException, (
137 msg or '%s not in %s' % _format_str(a, b))
139 def assertNotIn(self, a, b, msg=None):
140 if a in b:
141 raise self.failureException, (
142 msg or '%s in %s' % _format_str(a, b))
144 # aliases for assertion methods, deprecated since Python 2.7
146 failUnlessEqual = assertEquals = assertEqual
148 assertNotEquals = failIfEqual = assertNotEqual
151 class CustomTestCase(StandardTestCase):
154 Helper class, providing extended functionality over unittest.TestCase.
156 The methods assertEqual and assertNotEqual have been overwritten
157 to provide better support for multi-line strings. Furthermore,
158 see the compare_output method and the parameter list of __init__.
161 compare = difflib.Differ().compare
162 """Comparison method shared by all subclasses."""
164 def __init__(self, method_name, input, expected, id,
165 run_in_debugger=True, suite_settings=None):
167 Initialise the CustomTestCase.
169 Arguments:
171 method_name -- name of test method to run.
172 input -- input to the parser.
173 expected -- expected output from the parser.
174 id -- unique test identifier, used by the test framework.
175 run_in_debugger -- if true, run this test under the pdb debugger.
176 suite_settings -- settings overrides for this test suite.
178 self.id = id
179 self.input = input
180 self.expected = expected
181 self.run_in_debugger = run_in_debugger
182 self.suite_settings = suite_settings.copy() or {}
184 # Ring your mother.
185 unittest.TestCase.__init__(self, method_name)
187 def __str__(self):
189 Return string conversion. Overridden to give test id, in addition to
190 method name.
192 return '%s; %s' % (self.id, unittest.TestCase.__str__(self))
194 def __repr__(self):
195 return "<%s %s>" % (self.id, unittest.TestCase.__repr__(self))
197 def clear_roles(self):
198 # Language-specific roles and roles added by the
199 # "default-role" and "role" directives are currently stored
200 # globally in the roles._roles dictionary. This workaround
201 # empties that dictionary.
202 roles._roles = {}
204 def setUp(self):
205 StandardTestCase.setUp(self)
206 self.clear_roles()
208 def compare_output(self, input, output, expected):
209 """`input`, `output`, and `expected` should all be strings."""
210 if isinstance(input, unicode):
211 input = input.encode('raw_unicode_escape')
212 if sys.version_info > (3,):
213 # API difference: Python 3's node.__str__ doesn't escape
214 #assert expected is None or isinstance(expected, unicode)
215 if isinstance(expected, bytes):
216 expected = expected.decode('utf-8')
217 if isinstance(output, bytes):
218 output = output.decode('utf-8')
219 else:
220 if isinstance(expected, unicode):
221 expected = expected.encode('raw_unicode_escape')
222 if isinstance(output, unicode):
223 output = output.encode('raw_unicode_escape')
224 # Normalize line endings:
225 if expected:
226 expected = '\n'.join(expected.splitlines())
227 if output:
228 output = '\n'.join(output.splitlines())
229 try:
230 self.assertEqual(output, expected)
231 except AssertionError, error:
232 print >>sys.stderr, '\n%s\ninput:' % (self,)
233 print >>sys.stderr, input
234 try:
235 comparison = ''.join(self.compare(expected.splitlines(1),
236 output.splitlines(1)))
237 print >>sys.stderr, '-: expected\n+: output'
238 print >>sys.stderr, comparison
239 except AttributeError: # expected or output not a string
240 # alternative output for non-strings:
241 print >>sys.stderr, 'expected: %r' % expected
242 print >>sys.stderr, 'output: %r' % output
243 raise error
246 class CustomTestSuite(unittest.TestSuite):
249 A collection of CustomTestCases.
251 Provides test suite ID generation and a method for adding test cases.
254 id = ''
255 """Identifier for the TestSuite. Prepended to the
256 TestCase identifiers to make identification easier."""
258 next_test_case_id = 0
259 """The next identifier to use for non-identified test cases."""
261 def __init__(self, tests=(), id=None, suite_settings=None):
263 Initialize the CustomTestSuite.
265 Arguments:
267 id -- identifier for the suite, prepended to test cases.
268 suite_settings -- settings overrides for this test suite.
270 unittest.TestSuite.__init__(self, tests)
271 self.suite_settings = suite_settings or {}
272 if id is None:
273 mypath = os.path.abspath(
274 sys.modules[CustomTestSuite.__module__].__file__)
275 outerframes = inspect.getouterframes(inspect.currentframe())
276 for outerframe in outerframes[1:]:
277 if outerframe[3] != '__init__':
278 callerpath = outerframe[1]
279 if callerpath is None:
280 # It happens sometimes. Why is a mystery.
281 callerpath = os.getcwd()
282 callerpath = os.path.abspath(callerpath)
283 break
284 mydir, myname = os.path.split(mypath)
285 if not mydir:
286 mydir = os.curdir
287 if callerpath.startswith(mydir):
288 self.id = callerpath[len(mydir) + 1:] # caller's module
289 else:
290 self.id = callerpath
291 else:
292 self.id = id
294 def addTestCase(self, test_case_class, method_name, input, expected,
295 id=None, run_in_debugger=False, **kwargs):
297 Create a CustomTestCase in the CustomTestSuite.
298 Also return it, just in case.
300 Arguments:
302 test_case_class -- the CustomTestCase to add
303 method_name -- a string; CustomTestCase.method_name is the test
304 input -- input to the parser.
305 expected -- expected output from the parser.
306 id -- unique test identifier, used by the test framework.
307 run_in_debugger -- if true, run this test under the pdb debugger.
309 if id is None: # generate id if required
310 id = self.next_test_case_id
311 self.next_test_case_id += 1
312 # test identifier will become suiteid.testid
313 tcid = '%s: %s' % (self.id, id)
314 # suite_settings may be passed as a parameter;
315 # if not, set from attribute:
316 kwargs.setdefault('suite_settings', self.suite_settings)
317 # generate and add test case
318 tc = test_case_class(method_name, input, expected, tcid,
319 run_in_debugger=run_in_debugger, **kwargs)
320 self.addTest(tc)
321 return tc
323 def generate_no_tests(self, *args, **kwargs):
324 pass
327 class TransformTestCase(CustomTestCase):
330 Output checker for the transform.
332 Should probably be called TransformOutputChecker, but I can deal with
333 that later when/if someone comes up with a category of transform test
334 cases that have nothing to do with the input and output of the transform.
337 option_parser = frontend.OptionParser(components=(rst.Parser,))
338 settings = option_parser.get_default_values()
339 settings.report_level = 1
340 settings.halt_level = 5
341 settings.debug = package_unittest.debug
342 settings.warning_stream = DevNull()
343 unknown_reference_resolvers = ()
345 def __init__(self, *args, **kwargs):
346 self.transforms = kwargs['transforms']
347 """List of transforms to perform for this test case."""
349 self.parser = kwargs['parser']
350 """Input parser for this test case."""
352 del kwargs['transforms'], kwargs['parser'] # only wanted here
353 CustomTestCase.__init__(self, *args, **kwargs)
355 def supports(self, format):
356 return 1
358 def test_transforms(self):
359 if self.run_in_debugger:
360 pdb.set_trace()
361 settings = self.settings.copy()
362 settings.__dict__.update(self.suite_settings)
363 document = utils.new_document('test data', settings)
364 self.parser.parse(self.input, document)
365 # Don't do a ``populate_from_components()`` because that would
366 # enable the Transformer's default transforms.
367 document.transformer.add_transforms(self.transforms)
368 document.transformer.add_transform(universal.TestMessages)
369 document.transformer.components['writer'] = self
370 document.transformer.apply_transforms()
371 output = document.pformat()
372 self.compare_output(self.input, output, self.expected)
374 def test_transforms_verbosely(self):
375 if self.run_in_debugger:
376 pdb.set_trace()
377 print '\n', self.id
378 print '-' * 70
379 print self.input
380 settings = self.settings.copy()
381 settings.__dict__.update(self.suite_settings)
382 document = utils.new_document('test data', settings)
383 self.parser.parse(self.input, document)
384 print '-' * 70
385 print document.pformat()
386 for transformClass in self.transforms:
387 transformClass(document).apply()
388 output = document.pformat()
389 print '-' * 70
390 print output
391 self.compare_output(self.input, output, self.expected)
394 class TransformTestSuite(CustomTestSuite):
397 A collection of TransformTestCases.
399 A TransformTestSuite instance manufactures TransformTestCases,
400 keeps track of them, and provides a shared test fixture (a-la
401 setUp and tearDown).
404 def __init__(self, parser, suite_settings=None):
405 self.parser = parser
406 """Parser shared by all test cases."""
408 CustomTestSuite.__init__(self, suite_settings=suite_settings)
410 def generateTests(self, dict, dictname='totest',
411 testmethod='test_transforms'):
413 Stock the suite with test cases generated from a test data dictionary.
415 Each dictionary key (test type's name) maps to a tuple, whose
416 first item is a list of transform classes and whose second
417 item is a list of tests. Each test is a list: input, expected
418 output, optional modifier. The optional third entry, a
419 behavior modifier, can be 0 (temporarily disable this test) or
420 1 (run this test under the pdb debugger). Tests should be
421 self-documenting and not require external comments.
423 for name, (transforms, cases) in dict.items():
424 for casenum in range(len(cases)):
425 case = cases[casenum]
426 run_in_debugger = False
427 if len(case)==3:
428 # TODO: (maybe) change the 3rd argument to a dict, so it
429 # can handle more cases by keyword ('disable', 'debug',
430 # 'settings'), here and in other generateTests methods.
431 # But there's also the method that
432 # HtmlPublishPartsTestSuite uses <DJG>
433 if case[2]:
434 run_in_debugger = True
435 else:
436 continue
437 self.addTestCase(
438 TransformTestCase, testmethod,
439 transforms=transforms, parser=self.parser,
440 input=case[0], expected=case[1],
441 id='%s[%r][%s]' % (dictname, name, casenum),
442 run_in_debugger=run_in_debugger)
445 class ParserTestCase(CustomTestCase):
448 Output checker for the parser.
450 Should probably be called ParserOutputChecker, but I can deal with
451 that later when/if someone comes up with a category of parser test
452 cases that have nothing to do with the input and output of the parser.
455 parser = rst.Parser()
456 """Parser shared by all ParserTestCases."""
458 option_parser = frontend.OptionParser(components=(rst.Parser,))
459 settings = option_parser.get_default_values()
460 settings.report_level = 5
461 settings.halt_level = 5
462 settings.debug = package_unittest.debug
464 def test_parser(self):
465 if self.run_in_debugger:
466 pdb.set_trace()
467 settings = self.settings.copy()
468 settings.__dict__.update(self.suite_settings)
469 document = utils.new_document('test data', settings)
470 self.parser.parse(self.input, document)
471 output = document.pformat()
472 self.compare_output(self.input, output, self.expected)
475 class ParserTestSuite(CustomTestSuite):
478 A collection of ParserTestCases.
480 A ParserTestSuite instance manufactures ParserTestCases,
481 keeps track of them, and provides a shared test fixture (a-la
482 setUp and tearDown).
485 test_case_class = ParserTestCase
487 def generateTests(self, dict, dictname='totest'):
489 Stock the suite with test cases generated from a test data dictionary.
491 Each dictionary key (test type name) maps to a list of tests. Each
492 test is a list: input, expected output, optional modifier. The
493 optional third entry, a behavior modifier, can be 0 (temporarily
494 disable this test) or 1 (run this test under the pdb debugger). Tests
495 should be self-documenting and not require external comments.
497 for name, cases in dict.items():
498 for casenum in range(len(cases)):
499 case = cases[casenum]
500 run_in_debugger = False
501 if len(case)==3:
502 if case[2]:
503 run_in_debugger = True
504 else:
505 continue
506 self.addTestCase(
507 self.test_case_class, 'test_parser',
508 input=case[0], expected=case[1],
509 id='%s[%r][%s]' % (dictname, name, casenum),
510 run_in_debugger=run_in_debugger)
513 class PEPParserTestCase(ParserTestCase):
515 """PEP-specific parser test case."""
517 parser = rst.Parser(rfc2822=True, inliner=rst.states.Inliner())
518 """Parser shared by all PEPParserTestCases."""
520 option_parser = frontend.OptionParser(components=(rst.Parser, pep.Reader))
521 settings = option_parser.get_default_values()
522 settings.report_level = 5
523 settings.halt_level = 5
524 settings.debug = package_unittest.debug
527 class PEPParserTestSuite(ParserTestSuite):
529 """A collection of PEPParserTestCases."""
531 test_case_class = PEPParserTestCase
534 class GridTableParserTestCase(CustomTestCase):
536 parser = tableparser.GridTableParser()
538 def test_parse_table(self):
539 self.parser.setup(StringList(string2lines(self.input), 'test data'))
540 try:
541 self.parser.find_head_body_sep()
542 self.parser.parse_table()
543 output = self.parser.cells
544 except Exception, details:
545 output = '%s: %s' % (details.__class__.__name__, details)
546 self.compare_output(self.input, pformat(output) + '\n',
547 pformat(self.expected) + '\n')
549 def test_parse(self):
550 try:
551 output = self.parser.parse(StringList(string2lines(self.input),
552 'test data'))
553 except Exception, details:
554 output = '%s: %s' % (details.__class__.__name__, details)
555 self.compare_output(self.input, pformat(output) + '\n',
556 pformat(self.expected) + '\n')
559 class GridTableParserTestSuite(CustomTestSuite):
562 A collection of GridTableParserTestCases.
564 A GridTableParserTestSuite instance manufactures GridTableParserTestCases,
565 keeps track of them, and provides a shared test fixture (a-la setUp and
566 tearDown).
569 test_case_class = GridTableParserTestCase
571 def generateTests(self, dict, dictname='totest'):
573 Stock the suite with test cases generated from a test data dictionary.
575 Each dictionary key (test type name) maps to a list of tests. Each
576 test is a list: an input table, expected output from parse_table(),
577 expected output from parse(), optional modifier. The optional fourth
578 entry, a behavior modifier, can be 0 (temporarily disable this test)
579 or 1 (run this test under the pdb debugger). Tests should be
580 self-documenting and not require external comments.
582 for name, cases in dict.items():
583 for casenum in range(len(cases)):
584 case = cases[casenum]
585 run_in_debugger = False
586 if len(case) == 4:
587 if case[-1]:
588 run_in_debugger = True
589 else:
590 continue
591 self.addTestCase(self.test_case_class, 'test_parse_table',
592 input=case[0], expected=case[1],
593 id='%s[%r][%s]' % (dictname, name, casenum),
594 run_in_debugger=run_in_debugger)
595 self.addTestCase(self.test_case_class, 'test_parse',
596 input=case[0], expected=case[2],
597 id='%s[%r][%s]' % (dictname, name, casenum),
598 run_in_debugger=run_in_debugger)
601 class SimpleTableParserTestCase(GridTableParserTestCase):
603 parser = tableparser.SimpleTableParser()
606 class SimpleTableParserTestSuite(CustomTestSuite):
609 A collection of SimpleTableParserTestCases.
612 test_case_class = SimpleTableParserTestCase
614 def generateTests(self, dict, dictname='totest'):
616 Stock the suite with test cases generated from a test data dictionary.
618 Each dictionary key (test type name) maps to a list of tests. Each
619 test is a list: an input table, expected output from parse(), optional
620 modifier. The optional third entry, a behavior modifier, can be 0
621 (temporarily disable this test) or 1 (run this test under the pdb
622 debugger). Tests should be self-documenting and not require external
623 comments.
625 for name, cases in dict.items():
626 for casenum in range(len(cases)):
627 case = cases[casenum]
628 run_in_debugger = False
629 if len(case) == 3:
630 if case[-1]:
631 run_in_debugger = True
632 else:
633 continue
634 self.addTestCase(self.test_case_class, 'test_parse',
635 input=case[0], expected=case[1],
636 id='%s[%r][%s]' % (dictname, name, casenum),
637 run_in_debugger=run_in_debugger)
640 class PythonModuleParserTestCase(CustomTestCase):
642 def test_parser(self):
643 if self.run_in_debugger:
644 pdb.set_trace()
645 try:
646 import compiler
647 except ImportError:
648 # skip on Python 3
649 return
650 from docutils.readers.python import moduleparser
651 module = moduleparser.parse_module(self.input, 'test data').pformat()
652 output = str(module)
653 self.compare_output(self.input, output, self.expected)
655 def test_token_parser_rhs(self):
656 if self.run_in_debugger:
657 pdb.set_trace()
658 try:
659 import compiler
660 except ImportError:
661 # skip on Python 3
662 return
663 from docutils.readers.python import moduleparser
664 tr = moduleparser.TokenParser(self.input)
665 output = tr.rhs(1)
666 self.compare_output(self.input, output, self.expected)
669 class PythonModuleParserTestSuite(CustomTestSuite):
672 A collection of PythonModuleParserTestCase.
675 def generateTests(self, dict, dictname='totest',
676 testmethod='test_parser'):
678 Stock the suite with test cases generated from a test data dictionary.
680 Each dictionary key (test type's name) maps to a list of tests. Each
681 test is a list: input, expected output, optional modifier. The
682 optional third entry, a behavior modifier, can be 0 (temporarily
683 disable this test) or 1 (run this test under the pdb debugger). Tests
684 should be self-documenting and not require external comments.
686 for name, cases in dict.items():
687 for casenum in range(len(cases)):
688 case = cases[casenum]
689 run_in_debugger = False
690 if len(case)==3:
691 if case[2]:
692 run_in_debugger = True
693 else:
694 continue
695 self.addTestCase(
696 PythonModuleParserTestCase, testmethod,
697 input=case[0], expected=case[1],
698 id='%s[%r][%s]' % (dictname, name, casenum),
699 run_in_debugger=run_in_debugger)
702 class WriterPublishTestCase(CustomTestCase, docutils.SettingsSpec):
705 Test case for publish.
708 settings_default_overrides = {'_disable_config': True,
709 'strict_visitor': True}
710 writer_name = '' # set in subclasses or constructor
712 def __init__(self, *args, **kwargs):
713 if 'writer_name' in kwargs:
714 self.writer_name = kwargs['writer_name']
715 del kwargs['writer_name']
716 CustomTestCase.__init__(self, *args, **kwargs)
718 def test_publish(self):
719 if self.run_in_debugger:
720 pdb.set_trace()
721 output = docutils.core.publish_string(
722 source=self.input,
723 reader_name='standalone',
724 parser_name='restructuredtext',
725 writer_name=self.writer_name,
726 settings_spec=self,
727 settings_overrides=self.suite_settings)
728 self.compare_output(self.input, output, self.expected)
731 class PublishTestSuite(CustomTestSuite):
733 def __init__(self, writer_name, suite_settings=None):
735 `writer_name` is the name of the writer to use.
737 CustomTestSuite.__init__(self, suite_settings=suite_settings)
738 self.test_class = WriterPublishTestCase
739 self.writer_name = writer_name
741 def generateTests(self, dict, dictname='totest'):
742 for name, cases in dict.items():
743 for casenum in range(len(cases)):
744 case = cases[casenum]
745 run_in_debugger = False
746 if len(case)==3:
747 if case[2]:
748 run_in_debugger = True
749 else:
750 continue
751 self.addTestCase(
752 self.test_class, 'test_publish',
753 input=case[0], expected=case[1],
754 id='%s[%r][%s]' % (dictname, name, casenum),
755 run_in_debugger=run_in_debugger,
756 # Passed to constructor of self.test_class:
757 writer_name=self.writer_name)
760 class HtmlPublishPartsTestSuite(CustomTestSuite):
762 def generateTests(self, dict, dictname='totest'):
763 for name, (settings_overrides, cases) in dict.items():
764 settings = self.suite_settings.copy()
765 settings.update(settings_overrides)
766 for casenum in range(len(cases)):
767 case = cases[casenum]
768 run_in_debugger = False
769 if len(case)==3:
770 if case[2]:
771 run_in_debugger = True
772 else:
773 continue
774 self.addTestCase(
775 HtmlWriterPublishPartsTestCase, 'test_publish',
776 input=case[0], expected=case[1],
777 id='%s[%r][%s]' % (dictname, name, casenum),
778 run_in_debugger=run_in_debugger,
779 suite_settings=settings)
782 class HtmlWriterPublishPartsTestCase(WriterPublishTestCase):
785 Test case for HTML writer via the publish_parts interface.
788 writer_name = 'html'
790 settings_default_overrides = \
791 WriterPublishTestCase.settings_default_overrides.copy()
792 settings_default_overrides['stylesheet'] = ''
794 def test_publish(self):
795 if self.run_in_debugger:
796 pdb.set_trace()
797 parts = docutils.core.publish_parts(
798 source=self.input,
799 reader_name='standalone',
800 parser_name='restructuredtext',
801 writer_name=self.writer_name,
802 settings_spec=self,
803 settings_overrides=self.suite_settings)
804 output = self.format_output(parts)
805 # interpolate standard variables:
806 expected = self.expected % {'version': docutils.__version__}
807 self.compare_output(self.input, output, expected)
809 standard_content_type_template = ('<meta http-equiv="Content-Type"'
810 ' content="text/html; charset=%s" />\n')
811 standard_generator_template = (
812 '<meta name="generator"'
813 ' content="Docutils %s: http://docutils.sourceforge.net/" />\n')
814 standard_html_meta_value = (
815 standard_content_type_template
816 + standard_generator_template % docutils.__version__)
817 standard_meta_value = standard_html_meta_value % 'utf-8'
818 standard_html_prolog = """\
819 <?xml version="1.0" encoding="%s" ?>
820 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
823 def format_output(self, parts):
824 """Minimize & standardize the output."""
825 # remove redundant parts & uninteresting parts:
826 del parts['whole']
827 assert parts['body'] == parts['fragment']
828 del parts['body']
829 del parts['body_pre_docinfo']
830 del parts['body_prefix']
831 del parts['body_suffix']
832 del parts['head']
833 del parts['head_prefix']
834 del parts['encoding']
835 del parts['version']
836 # remove standard portions:
837 parts['meta'] = parts['meta'].replace(self.standard_meta_value, '')
838 parts['html_head'] = parts['html_head'].replace(
839 self.standard_html_meta_value, '...')
840 parts['html_prolog'] = parts['html_prolog'].replace(
841 self.standard_html_prolog, '')
842 # remove empty values:
843 for key in parts.keys():
844 if not parts[key]:
845 del parts[key]
846 # standard output format:
847 keys = parts.keys()
848 keys.sort()
849 output = []
850 for key in keys:
851 output.append("%r: '''%s'''"
852 % (key, parts[key]))
853 if output[-1].endswith("\n'''"):
854 output[-1] = output[-1][:-4] + "\\n'''"
855 return '{' + ',\n '.join(output) + '}\n'
858 def exception_data(func, *args, **kwds):
860 Execute `func(*args, **kwds)` and return the resulting exception, the
861 exception arguments, and the formatted exception string.
863 try:
864 func(*args, **kwds)
865 except Exception, detail:
866 return (detail, detail.args,
867 '%s: %s' % (detail.__class__.__name__, detail))
870 def _format_str(*args):
871 r"""
872 Return a tuple containing representations of all args.
874 Same as map(repr, args) except that it returns multi-line
875 representations for strings containing newlines, e.g.::
877 '''\
878 foo \n\
881 baz'''
883 instead of::
885 'foo \nbar\n\nbaz'
887 This is a helper function for CustomTestCase.
889 return_tuple = []
890 for i in args:
891 r = repr(i)
892 if ( (isinstance(i, bytes) or isinstance(i, unicode))
893 and '\n' in i):
894 stripped = ''
895 if isinstance(i, unicode) and r.startswith('u'):
896 stripped = r[0]
897 r = r[1:]
898 elif isinstance(i, bytes) and r.startswith('b'):
899 stripped = r[0]
900 r = r[1:]
901 # quote_char = "'" or '"'
902 quote_char = r[0]
903 assert quote_char in ("'", '"'), quote_char
904 assert r[0] == r[-1]
905 r = r[1:-1]
906 r = (stripped + 3 * quote_char + '\\\n' +
907 re.sub(r'(?<!\\)((\\\\)*)\\n', r'\1\n', r) +
908 3 * quote_char)
909 r = re.sub(r' \n', r' \\n\\\n', r)
910 return_tuple.append(r)
911 return tuple(return_tuple)