Code cleanup and test html math output options.
[docutils.git] / test / DocutilsTestSupport.py
blobe410070d43555690ec313208c1b0ce3644e19cec
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 # assertIn and assertNotIn: new in Python 2.7:
135 def assertIn(self, a, b, msg=None):
136 if a not in b:
137 raise self.failureException, (
138 msg or '%s not in %s' % _format_str(a, b))
140 def assertNotIn(self, a, b, msg=None):
141 if a in b:
142 raise self.failureException, (
143 msg or '%s in %s' % _format_str(a, b))
145 # aliases for assertion methods, deprecated since Python 2.7
147 failUnlessEqual = assertEquals = assertEqual
149 assertNotEquals = failIfEqual = assertNotEqual
152 class CustomTestCase(StandardTestCase):
155 Helper class, providing extended functionality over unittest.TestCase.
157 The methods assertEqual and assertNotEqual have been overwritten
158 to provide better support for multi-line strings. Furthermore,
159 see the compare_output method and the parameter list of __init__.
162 compare = difflib.Differ().compare
163 """Comparison method shared by all subclasses."""
165 def __init__(self, method_name, input, expected, id,
166 run_in_debugger=True, suite_settings=None):
168 Initialise the CustomTestCase.
170 Arguments:
172 method_name -- name of test method to run.
173 input -- input to the parser.
174 expected -- expected output from the parser.
175 id -- unique test identifier, used by the test framework.
176 run_in_debugger -- if true, run this test under the pdb debugger.
177 suite_settings -- settings overrides for this test suite.
179 self.id = id
180 self.input = input
181 self.expected = expected
182 self.run_in_debugger = run_in_debugger
183 self.suite_settings = suite_settings.copy() or {}
185 # Ring your mother.
186 unittest.TestCase.__init__(self, method_name)
188 def __str__(self):
190 Return string conversion. Overridden to give test id, in addition to
191 method name.
193 return '%s; %s' % (self.id, unittest.TestCase.__str__(self))
195 def __repr__(self):
196 return "<%s %s>" % (self.id, unittest.TestCase.__repr__(self))
198 def clear_roles(self):
199 # Language-specific roles and roles added by the
200 # "default-role" and "role" directives are currently stored
201 # globally in the roles._roles dictionary. This workaround
202 # empties that dictionary.
203 roles._roles = {}
205 def setUp(self):
206 StandardTestCase.setUp(self)
207 self.clear_roles()
209 def compare_output(self, input, output, expected):
210 """`input`, `output`, and `expected` should all be strings."""
211 if isinstance(input, unicode):
212 input = input.encode('raw_unicode_escape')
213 if sys.version_info > (3,):
214 # API difference: Python 3's node.__str__ doesn't escape
215 #assert expected is None or isinstance(expected, unicode)
216 if isinstance(expected, bytes):
217 expected = expected.decode('utf-8')
218 if isinstance(output, bytes):
219 output = output.decode('utf-8')
220 else:
221 if isinstance(expected, unicode):
222 expected = expected.encode('raw_unicode_escape')
223 if isinstance(output, unicode):
224 output = output.encode('raw_unicode_escape')
225 # Normalize line endings:
226 if expected:
227 expected = '\n'.join(expected.splitlines())
228 if output:
229 output = '\n'.join(output.splitlines())
230 try:
231 self.assertEqual(output, expected)
232 except AssertionError, error:
233 print >>sys.stderr, '\n%s\ninput:' % (self,)
234 print >>sys.stderr, input
235 try:
236 comparison = ''.join(self.compare(expected.splitlines(1),
237 output.splitlines(1)))
238 print >>sys.stderr, '-: expected\n+: output'
239 print >>sys.stderr, comparison
240 except AttributeError: # expected or output not a string
241 # alternative output for non-strings:
242 print >>sys.stderr, 'expected: %r' % expected
243 print >>sys.stderr, 'output: %r' % output
244 raise error
247 class CustomTestSuite(unittest.TestSuite):
250 A collection of CustomTestCases.
252 Provides test suite ID generation and a method for adding test cases.
255 id = ''
256 """Identifier for the TestSuite. Prepended to the
257 TestCase identifiers to make identification easier."""
259 next_test_case_id = 0
260 """The next identifier to use for non-identified test cases."""
262 def __init__(self, tests=(), id=None, suite_settings=None):
264 Initialize the CustomTestSuite.
266 Arguments:
268 id -- identifier for the suite, prepended to test cases.
269 suite_settings -- settings overrides for this test suite.
271 unittest.TestSuite.__init__(self, tests)
272 self.suite_settings = suite_settings or {}
273 if id is None:
274 mypath = os.path.abspath(
275 sys.modules[CustomTestSuite.__module__].__file__)
276 outerframes = inspect.getouterframes(inspect.currentframe())
277 for outerframe in outerframes[1:]:
278 if outerframe[3] != '__init__':
279 callerpath = outerframe[1]
280 if callerpath is None:
281 # It happens sometimes. Why is a mystery.
282 callerpath = os.getcwd()
283 callerpath = os.path.abspath(callerpath)
284 break
285 mydir, myname = os.path.split(mypath)
286 if not mydir:
287 mydir = os.curdir
288 if callerpath.startswith(mydir):
289 self.id = callerpath[len(mydir) + 1:] # caller's module
290 else:
291 self.id = callerpath
292 else:
293 self.id = id
295 def addTestCase(self, test_case_class, method_name, input, expected,
296 id=None, run_in_debugger=False, **kwargs):
298 Create a CustomTestCase in the CustomTestSuite.
299 Also return it, just in case.
301 Arguments:
303 test_case_class -- the CustomTestCase to add
304 method_name -- a string; CustomTestCase.method_name is the test
305 input -- input to the parser.
306 expected -- expected output from the parser.
307 id -- unique test identifier, used by the test framework.
308 run_in_debugger -- if true, run this test under the pdb debugger.
310 if id is None: # generate id if required
311 id = self.next_test_case_id
312 self.next_test_case_id += 1
313 # test identifier will become suiteid.testid
314 tcid = '%s: %s' % (self.id, id)
315 # suite_settings may be passed as a parameter;
316 # if not, set from attribute:
317 kwargs.setdefault('suite_settings', self.suite_settings)
318 # generate and add test case
319 tc = test_case_class(method_name, input, expected, tcid,
320 run_in_debugger=run_in_debugger, **kwargs)
321 self.addTest(tc)
322 return tc
324 def generate_no_tests(self, *args, **kwargs):
325 pass
328 class TransformTestCase(CustomTestCase):
331 Output checker for the transform.
333 Should probably be called TransformOutputChecker, but I can deal with
334 that later when/if someone comes up with a category of transform test
335 cases that have nothing to do with the input and output of the transform.
338 option_parser = frontend.OptionParser(components=(rst.Parser,))
339 settings = option_parser.get_default_values()
340 settings.report_level = 1
341 settings.halt_level = 5
342 settings.debug = package_unittest.debug
343 settings.warning_stream = DevNull()
344 unknown_reference_resolvers = ()
346 def __init__(self, *args, **kwargs):
347 self.transforms = kwargs['transforms']
348 """List of transforms to perform for this test case."""
350 self.parser = kwargs['parser']
351 """Input parser for this test case."""
353 del kwargs['transforms'], kwargs['parser'] # only wanted here
354 CustomTestCase.__init__(self, *args, **kwargs)
356 def supports(self, format):
357 return 1
359 def test_transforms(self):
360 if self.run_in_debugger:
361 pdb.set_trace()
362 settings = self.settings.copy()
363 settings.__dict__.update(self.suite_settings)
364 document = utils.new_document('test data', settings)
365 self.parser.parse(self.input, document)
366 # Don't do a ``populate_from_components()`` because that would
367 # enable the Transformer's default transforms.
368 document.transformer.add_transforms(self.transforms)
369 document.transformer.add_transform(universal.TestMessages)
370 document.transformer.components['writer'] = self
371 document.transformer.apply_transforms()
372 output = document.pformat()
373 self.compare_output(self.input, output, self.expected)
375 def test_transforms_verbosely(self):
376 if self.run_in_debugger:
377 pdb.set_trace()
378 print '\n', self.id
379 print '-' * 70
380 print self.input
381 settings = self.settings.copy()
382 settings.__dict__.update(self.suite_settings)
383 document = utils.new_document('test data', settings)
384 self.parser.parse(self.input, document)
385 print '-' * 70
386 print document.pformat()
387 for transformClass in self.transforms:
388 transformClass(document).apply()
389 output = document.pformat()
390 print '-' * 70
391 print output
392 self.compare_output(self.input, output, self.expected)
395 class TransformTestSuite(CustomTestSuite):
398 A collection of TransformTestCases.
400 A TransformTestSuite instance manufactures TransformTestCases,
401 keeps track of them, and provides a shared test fixture (a-la
402 setUp and tearDown).
405 def __init__(self, parser, suite_settings=None):
406 self.parser = parser
407 """Parser shared by all test cases."""
409 CustomTestSuite.__init__(self, suite_settings=suite_settings)
411 def generateTests(self, dict, dictname='totest',
412 testmethod='test_transforms'):
414 Stock the suite with test cases generated from a test data dictionary.
416 Each dictionary key (test type's name) maps to a tuple, whose
417 first item is a list of transform classes and whose second
418 item is a list of tests. Each test is a list: input, expected
419 output, optional modifier. The optional third entry, a
420 behavior modifier, can be 0 (temporarily disable this test) or
421 1 (run this test under the pdb debugger). Tests should be
422 self-documenting and not require external comments.
424 for name, (transforms, cases) in dict.items():
425 for casenum in range(len(cases)):
426 case = cases[casenum]
427 run_in_debugger = False
428 if len(case)==3:
429 # TODO: (maybe) change the 3rd argument to a dict, so it
430 # can handle more cases by keyword ('disable', 'debug',
431 # 'settings'), here and in other generateTests methods.
432 # But there's also the method that
433 # HtmlPublishPartsTestSuite uses <DJG>
434 if case[2]:
435 run_in_debugger = True
436 else:
437 continue
438 self.addTestCase(
439 TransformTestCase, testmethod,
440 transforms=transforms, parser=self.parser,
441 input=case[0], expected=case[1],
442 id='%s[%r][%s]' % (dictname, name, casenum),
443 run_in_debugger=run_in_debugger)
446 class ParserTestCase(CustomTestCase):
449 Output checker for the parser.
451 Should probably be called ParserOutputChecker, but I can deal with
452 that later when/if someone comes up with a category of parser test
453 cases that have nothing to do with the input and output of the parser.
456 parser = rst.Parser()
457 """Parser shared by all ParserTestCases."""
459 option_parser = frontend.OptionParser(components=(rst.Parser,))
460 settings = option_parser.get_default_values()
461 settings.report_level = 5
462 settings.halt_level = 5
463 settings.debug = package_unittest.debug
465 def test_parser(self):
466 if self.run_in_debugger:
467 pdb.set_trace()
468 settings = self.settings.copy()
469 settings.__dict__.update(self.suite_settings)
470 document = utils.new_document('test data', settings)
471 self.parser.parse(self.input, document)
472 output = document.pformat()
473 self.compare_output(self.input, output, self.expected)
476 class ParserTestSuite(CustomTestSuite):
479 A collection of ParserTestCases.
481 A ParserTestSuite instance manufactures ParserTestCases,
482 keeps track of them, and provides a shared test fixture (a-la
483 setUp and tearDown).
486 test_case_class = ParserTestCase
488 def generateTests(self, dict, dictname='totest'):
490 Stock the suite with test cases generated from a test data dictionary.
492 Each dictionary key (test type name) maps to a list of tests. Each
493 test is a list: input, expected output, optional modifier. The
494 optional third entry, a behavior modifier, can be 0 (temporarily
495 disable this test) or 1 (run this test under the pdb debugger). Tests
496 should be self-documenting and not require external comments.
498 for name, cases in dict.items():
499 for casenum in range(len(cases)):
500 case = cases[casenum]
501 run_in_debugger = False
502 if len(case)==3:
503 if case[2]:
504 run_in_debugger = True
505 else:
506 continue
507 self.addTestCase(
508 self.test_case_class, 'test_parser',
509 input=case[0], expected=case[1],
510 id='%s[%r][%s]' % (dictname, name, casenum),
511 run_in_debugger=run_in_debugger)
514 class PEPParserTestCase(ParserTestCase):
516 """PEP-specific parser test case."""
518 parser = rst.Parser(rfc2822=True, inliner=rst.states.Inliner())
519 """Parser shared by all PEPParserTestCases."""
521 option_parser = frontend.OptionParser(components=(rst.Parser, pep.Reader))
522 settings = option_parser.get_default_values()
523 settings.report_level = 5
524 settings.halt_level = 5
525 settings.debug = package_unittest.debug
528 class PEPParserTestSuite(ParserTestSuite):
530 """A collection of PEPParserTestCases."""
532 test_case_class = PEPParserTestCase
535 class GridTableParserTestCase(CustomTestCase):
537 parser = tableparser.GridTableParser()
539 def test_parse_table(self):
540 self.parser.setup(StringList(string2lines(self.input), 'test data'))
541 try:
542 self.parser.find_head_body_sep()
543 self.parser.parse_table()
544 output = self.parser.cells
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')
550 def test_parse(self):
551 try:
552 output = self.parser.parse(StringList(string2lines(self.input),
553 'test data'))
554 except Exception, details:
555 output = '%s: %s' % (details.__class__.__name__, details)
556 self.compare_output(self.input, pformat(output) + '\n',
557 pformat(self.expected) + '\n')
560 class GridTableParserTestSuite(CustomTestSuite):
563 A collection of GridTableParserTestCases.
565 A GridTableParserTestSuite instance manufactures GridTableParserTestCases,
566 keeps track of them, and provides a shared test fixture (a-la setUp and
567 tearDown).
570 test_case_class = GridTableParserTestCase
572 def generateTests(self, dict, dictname='totest'):
574 Stock the suite with test cases generated from a test data dictionary.
576 Each dictionary key (test type name) maps to a list of tests. Each
577 test is a list: an input table, expected output from parse_table(),
578 expected output from parse(), optional modifier. The optional fourth
579 entry, a behavior modifier, can be 0 (temporarily disable this test)
580 or 1 (run this test under the pdb debugger). Tests should be
581 self-documenting and not require external comments.
583 for name, cases in dict.items():
584 for casenum in range(len(cases)):
585 case = cases[casenum]
586 run_in_debugger = False
587 if len(case) == 4:
588 if case[-1]:
589 run_in_debugger = True
590 else:
591 continue
592 self.addTestCase(self.test_case_class, 'test_parse_table',
593 input=case[0], expected=case[1],
594 id='%s[%r][%s]' % (dictname, name, casenum),
595 run_in_debugger=run_in_debugger)
596 self.addTestCase(self.test_case_class, 'test_parse',
597 input=case[0], expected=case[2],
598 id='%s[%r][%s]' % (dictname, name, casenum),
599 run_in_debugger=run_in_debugger)
602 class SimpleTableParserTestCase(GridTableParserTestCase):
604 parser = tableparser.SimpleTableParser()
607 class SimpleTableParserTestSuite(CustomTestSuite):
610 A collection of SimpleTableParserTestCases.
613 test_case_class = SimpleTableParserTestCase
615 def generateTests(self, dict, dictname='totest'):
617 Stock the suite with test cases generated from a test data dictionary.
619 Each dictionary key (test type name) maps to a list of tests. Each
620 test is a list: an input table, expected output from parse(), optional
621 modifier. The optional third entry, a behavior modifier, can be 0
622 (temporarily disable this test) or 1 (run this test under the pdb
623 debugger). Tests should be self-documenting and not require external
624 comments.
626 for name, cases in dict.items():
627 for casenum in range(len(cases)):
628 case = cases[casenum]
629 run_in_debugger = False
630 if len(case) == 3:
631 if case[-1]:
632 run_in_debugger = True
633 else:
634 continue
635 self.addTestCase(self.test_case_class, 'test_parse',
636 input=case[0], expected=case[1],
637 id='%s[%r][%s]' % (dictname, name, casenum),
638 run_in_debugger=run_in_debugger)
641 class PythonModuleParserTestCase(CustomTestCase):
643 def test_parser(self):
644 if self.run_in_debugger:
645 pdb.set_trace()
646 try:
647 import compiler
648 except ImportError:
649 # skip on Python 3
650 return
651 from docutils.readers.python import moduleparser
652 module = moduleparser.parse_module(self.input, 'test data').pformat()
653 output = str(module)
654 self.compare_output(self.input, output, self.expected)
656 def test_token_parser_rhs(self):
657 if self.run_in_debugger:
658 pdb.set_trace()
659 try:
660 import compiler
661 except ImportError:
662 # skip on Python 3
663 return
664 from docutils.readers.python import moduleparser
665 tr = moduleparser.TokenParser(self.input)
666 output = tr.rhs(1)
667 self.compare_output(self.input, output, self.expected)
670 class PythonModuleParserTestSuite(CustomTestSuite):
673 A collection of PythonModuleParserTestCase.
676 def generateTests(self, dict, dictname='totest',
677 testmethod='test_parser'):
679 Stock the suite with test cases generated from a test data dictionary.
681 Each dictionary key (test type's name) maps to a list of tests. Each
682 test is a list: input, expected output, optional modifier. The
683 optional third entry, a behavior modifier, can be 0 (temporarily
684 disable this test) or 1 (run this test under the pdb debugger). Tests
685 should be self-documenting and not require external comments.
687 for name, cases in dict.items():
688 for casenum in range(len(cases)):
689 case = cases[casenum]
690 run_in_debugger = False
691 if len(case)==3:
692 if case[2]:
693 run_in_debugger = True
694 else:
695 continue
696 self.addTestCase(
697 PythonModuleParserTestCase, testmethod,
698 input=case[0], expected=case[1],
699 id='%s[%r][%s]' % (dictname, name, casenum),
700 run_in_debugger=run_in_debugger)
703 class WriterPublishTestCase(CustomTestCase, docutils.SettingsSpec):
706 Test case for publish.
709 settings_default_overrides = {'_disable_config': 1,
710 'strict_visitor': 1}
711 writer_name = '' # set in subclasses or constructor
713 def __init__(self, *args, **kwargs):
714 if 'writer_name' in kwargs:
715 self.writer_name = kwargs['writer_name']
716 del kwargs['writer_name']
717 CustomTestCase.__init__(self, *args, **kwargs)
719 def test_publish(self):
720 if self.run_in_debugger:
721 pdb.set_trace()
722 output = docutils.core.publish_string(
723 source=self.input,
724 reader_name='standalone',
725 parser_name='restructuredtext',
726 writer_name=self.writer_name,
727 settings_spec=self,
728 settings_overrides=self.suite_settings)
729 self.compare_output(self.input, output, self.expected)
732 class PublishTestSuite(CustomTestSuite):
734 def __init__(self, writer_name, suite_settings=None):
736 `writer_name` is the name of the writer to use.
738 CustomTestSuite.__init__(self, suite_settings=suite_settings)
739 self.test_class = WriterPublishTestCase
740 self.writer_name = writer_name
742 def generateTests(self, dict, dictname='totest'):
743 for name, cases in dict.items():
744 for casenum in range(len(cases)):
745 case = cases[casenum]
746 run_in_debugger = False
747 if len(case)==3:
748 if case[2]:
749 run_in_debugger = True
750 else:
751 continue
752 self.addTestCase(
753 self.test_class, 'test_publish',
754 input=case[0], expected=case[1],
755 id='%s[%r][%s]' % (dictname, name, casenum),
756 run_in_debugger=run_in_debugger,
757 # Passed to constructor of self.test_class:
758 writer_name=self.writer_name)
761 class HtmlPublishPartsTestSuite(CustomTestSuite):
763 def generateTests(self, dict, dictname='totest'):
764 for name, (settings_overrides, cases) in dict.items():
765 settings = self.suite_settings.copy()
766 settings.update(settings_overrides)
767 for casenum in range(len(cases)):
768 case = cases[casenum]
769 run_in_debugger = False
770 if len(case)==3:
771 if case[2]:
772 run_in_debugger = True
773 else:
774 continue
775 self.addTestCase(
776 HtmlWriterPublishPartsTestCase, 'test_publish',
777 input=case[0], expected=case[1],
778 id='%s[%r][%s]' % (dictname, name, casenum),
779 run_in_debugger=run_in_debugger,
780 suite_settings=settings)
783 class HtmlWriterPublishPartsTestCase(WriterPublishTestCase):
786 Test case for HTML writer via the publish_parts interface.
789 writer_name = 'html'
791 settings_default_overrides = \
792 WriterPublishTestCase.settings_default_overrides.copy()
793 settings_default_overrides['stylesheet'] = ''
795 def test_publish(self):
796 if self.run_in_debugger:
797 pdb.set_trace()
798 parts = docutils.core.publish_parts(
799 source=self.input,
800 reader_name='standalone',
801 parser_name='restructuredtext',
802 writer_name=self.writer_name,
803 settings_spec=self,
804 settings_overrides=self.suite_settings)
805 output = self.format_output(parts)
806 # interpolate standard variables:
807 expected = self.expected % {'version': docutils.__version__}
808 self.compare_output(self.input, output, expected)
810 standard_content_type_template = ('<meta http-equiv="Content-Type"'
811 ' content="text/html; charset=%s" />\n')
812 standard_generator_template = (
813 '<meta name="generator"'
814 ' content="Docutils %s: http://docutils.sourceforge.net/" />\n')
815 standard_html_meta_value = (
816 standard_content_type_template
817 + standard_generator_template % docutils.__version__)
818 standard_meta_value = standard_html_meta_value % 'utf-8'
819 standard_html_prolog = """\
820 <?xml version="1.0" encoding="%s" ?>
821 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
824 def format_output(self, parts):
825 """Minimize & standardize the output."""
826 # remove redundant parts & uninteresting parts:
827 del parts['whole']
828 assert parts['body'] == parts['fragment']
829 del parts['body']
830 del parts['body_pre_docinfo']
831 del parts['body_prefix']
832 del parts['body_suffix']
833 del parts['head']
834 del parts['head_prefix']
835 del parts['encoding']
836 del parts['version']
837 # remove standard portions:
838 parts['meta'] = parts['meta'].replace(self.standard_meta_value, '')
839 parts['html_head'] = parts['html_head'].replace(
840 self.standard_html_meta_value, '...')
841 parts['html_prolog'] = parts['html_prolog'].replace(
842 self.standard_html_prolog, '')
843 # remove empty values:
844 for key in parts.keys():
845 if not parts[key]:
846 del parts[key]
847 # standard output format:
848 keys = parts.keys()
849 keys.sort()
850 output = []
851 for key in keys:
852 output.append("%r: '''%s'''"
853 % (key, parts[key]))
854 if output[-1].endswith("\n'''"):
855 output[-1] = output[-1][:-4] + "\\n'''"
856 return '{' + ',\n '.join(output) + '}\n'
859 def exception_data(func, *args, **kwds):
861 Execute `func(*args, **kwds)` and return the resulting exception, the
862 exception arguments, and the formatted exception string.
864 try:
865 func(*args, **kwds)
866 except Exception, detail:
867 return (detail, detail.args,
868 '%s: %s' % (detail.__class__.__name__, detail))
871 def _format_str(*args):
872 r"""
873 Return a tuple containing representations of all args.
875 Same as map(repr, args) except that it returns multi-line
876 representations for strings containing newlines, e.g.::
878 '''\
879 foo \n\
882 baz'''
884 instead of::
886 'foo \nbar\n\nbaz'
888 This is a helper function for CustomTestCase.
890 return_tuple = []
891 for i in args:
892 r = repr(i)
893 if ( (isinstance(i, bytes) or isinstance(i, unicode))
894 and '\n' in i):
895 stripped = ''
896 if isinstance(i, unicode) and r.startswith('u'):
897 stripped = r[0]
898 r = r[1:]
899 elif isinstance(i, bytes) and r.startswith('b'):
900 stripped = r[0]
901 r = r[1:]
902 # quote_char = "'" or '"'
903 quote_char = r[0]
904 assert quote_char in ("'", '"'), quote_char
905 assert r[0] == r[-1]
906 r = r[1:-1]
907 r = (stripped + 3 * quote_char + '\\\n' +
908 re.sub(r'(?<!\\)((\\\\)*)\\n', r'\1\n', r) +
909 3 * quote_char)
910 r = re.sub(r' \n', r' \\n\\\n', r)
911 return_tuple.append(r)
912 return tuple(return_tuple)