1 # Authors: David Goodger; Garth Kidd
2 # Contact: goodger@users.sourceforge.net
5 # Copyright: This module has been placed in the public domain.
11 - `statemachine` is 'docutils.statemachine'
12 - `nodes` is 'docutils.nodes'
13 - `urischemes` is 'docutils.urischemes'
14 - `utils` is 'docutils.utils'
15 - `transforms` is 'docutils.transforms'
16 - `states` is 'docutils.parsers.rst.states'
17 - `tableparser` is 'docutils.parsers.rst.tableparser'
22 - `TransformTestSuite`
26 - `PEPParserTestSuite`
28 - `GridTableParserTestSuite`
29 - `GridTableParserTestCase`
30 - `SimpleTableParserTestSuite`
31 - `SimpleTableParserTestCase`
32 - `WriterPublishTestCase`
33 - `LatexWriterPublishTestCase`
34 - `PseudoXMLWriterPublishTestCase`
35 - `HtmlWriterPublishTestCase`
37 - `HtmlFragmentTestSuite`
38 - `DevNull` (output sink)
40 __docformat__
= 'reStructuredText'
47 from pprint
import pformat
48 from types
import UnicodeType
49 import package_unittest
52 from docutils
import frontend
, nodes
, statemachine
, urischemes
, utils
53 from docutils
.transforms
import universal
54 from docutils
.parsers
import rst
55 from docutils
.parsers
.rst
import states
, tableparser
, roles
, languages
56 from docutils
.readers
import standalone
, pep
57 from docutils
.statemachine
import StringList
, string2lines
59 if sys
.hexversion
>= 0x02020000: # Python 2.2
60 from docutils
.readers
.python
import moduleparser
70 # Hack to make repr(StringList) look like repr(list):
71 StringList
.__repr
__ = StringList
.__str
__
78 def write(self
, string
):
82 class CustomTestSuite(unittest
.TestSuite
):
85 A collection of custom TestCases.
90 """Identifier for the TestSuite. Prepended to the
91 TestCase identifiers to make identification easier."""
94 """The next identifier to use for non-identified test cases."""
96 def __init__(self
, tests
=(), id=None):
98 Initialize the CustomTestSuite.
102 id -- identifier for the suite, prepended to test cases.
104 unittest
.TestSuite
.__init
__(self
, tests
)
106 mypath
= os
.path
.abspath(
107 sys
.modules
[CustomTestSuite
.__module
__].__file
__)
108 outerframes
= inspect
.getouterframes(inspect
.currentframe())
109 for outerframe
in outerframes
[1:]:
110 if outerframe
[3] != '__init__':
111 callerpath
= outerframe
[1]
112 if callerpath
is None:
113 # It happens sometimes. Why is a mystery.
114 callerpath
= os
.getcwd()
115 callerpath
= os
.path
.abspath(callerpath
)
117 mydir
, myname
= os
.path
.split(mypath
)
120 if callerpath
.startswith(mydir
):
121 self
.id = callerpath
[len(mydir
) + 1:] # caller's module
127 def addTestCase(self
, test_case_class
, method_name
, input, expected
,
128 id=None, run_in_debugger
=0, short_description
=None,
131 Create a custom TestCase in the CustomTestSuite.
132 Also return it, just in case.
138 input -- input to the parser.
139 expected -- expected output from the parser.
140 id -- unique test identifier, used by the test framework.
141 run_in_debugger -- if true, run this test under the pdb debugger.
142 short_description -- override to default test description.
144 if id is None: # generate id if required
145 id = self
.next_test_case_id
146 self
.next_test_case_id
+= 1
147 # test identifier will become suiteid.testid
148 tcid
= '%s: %s' % (self
.id, id)
149 # generate and add test case
150 tc
= test_case_class(method_name
, input, expected
, tcid
,
151 run_in_debugger
=run_in_debugger
,
152 short_description
=short_description
,
157 def generate_no_tests(self
, *args
, **kwargs
):
161 class CustomTestCase(unittest
.TestCase
):
163 compare
= difflib
.Differ().compare
164 """Comparison method shared by all subclasses."""
166 def __init__(self
, method_name
, input, expected
, id,
167 run_in_debugger
=0, short_description
=None):
169 Initialise the CustomTestCase.
173 method_name -- name of test method to run.
174 input -- input to the parser.
175 expected -- expected output from the parser.
176 id -- unique test identifier, used by the test framework.
177 run_in_debugger -- if true, run this test under the pdb debugger.
178 short_description -- override to default test description.
182 self
.expected
= expected
183 self
.run_in_debugger
= run_in_debugger
185 unittest
.TestCase
.__init
__(self
, method_name
)
189 Return string conversion. Overridden to give test id, in addition to
192 return '%s; %s' % (self
.id, unittest
.TestCase
.__str
__(self
))
195 return "<%s %s>" % (self
.id, unittest
.TestCase
.__repr
__(self
))
197 def compare_output(self
, input, output
, expected
):
198 """`input`, `output`, and `expected` should all be strings."""
199 if type(input) == UnicodeType
:
200 input = input.encode('raw_unicode_escape')
201 if type(output
) == UnicodeType
:
202 output
= output
.encode('raw_unicode_escape')
203 if type(expected
) == UnicodeType
:
204 expected
= expected
.encode('raw_unicode_escape')
206 self
.assertEquals('\n' + output
, '\n' + expected
)
207 except AssertionError:
208 print >>sys
.stderr
, '\n%s\ninput:' % (self
,)
209 print >>sys
.stderr
, input
210 print >>sys
.stderr
, '-: expected\n+: output'
211 print >>sys
.stderr
, ''.join(self
.compare(expected
.splitlines(1),
212 output
.splitlines(1)))
216 print >>sys
.stderr
, '%s: Test skipped' % self
219 class TransformTestSuite(CustomTestSuite
):
222 A collection of TransformTestCases.
224 A TransformTestSuite instance manufactures TransformTestCases,
225 keeps track of them, and provides a shared test fixture (a-la
229 def __init__(self
, parser
):
231 """Parser shared by all test cases."""
233 CustomTestSuite
.__init
__(self
)
235 def generateTests(self
, dict, dictname
='totest',
236 testmethod
='test_transforms'):
238 Stock the suite with test cases generated from a test data dictionary.
240 Each dictionary key (test type's name) maps to a list of transform
241 classes and list of tests. Each test is a list: input, expected
242 output, optional modifier. The optional third entry, a behavior
243 modifier, can be 0 (temporarily disable this test) or 1 (run this test
244 under the pdb debugger). Tests should be self-documenting and not
245 require external comments.
247 for name
, (transforms
, cases
) in dict.items():
248 for casenum
in range(len(cases
)):
249 case
= cases
[casenum
]
257 TransformTestCase
, testmethod
,
258 transforms
=transforms
, parser
=self
.parser
,
259 input=case
[0], expected
=case
[1],
260 id='%s[%r][%s]' % (dictname
, name
, casenum
),
261 run_in_debugger
=run_in_debugger
)
264 class TransformTestCase(CustomTestCase
):
267 Output checker for the transform.
269 Should probably be called TransformOutputChecker, but I can deal with
270 that later when/if someone comes up with a category of transform test
271 cases that have nothing to do with the input and output of the transform.
274 option_parser
= frontend
.OptionParser(components
=(rst
.Parser
,))
275 settings
= option_parser
.get_default_values()
276 settings
.report_level
= 1
277 settings
.halt_level
= 5
278 settings
.debug
= package_unittest
.debug
279 settings
.warning_stream
= DevNull()
280 unknown_reference_resolvers
= ()
282 def __init__(self
, *args
, **kwargs
):
283 self
.transforms
= kwargs
['transforms']
284 """List of transforms to perform for this test case."""
286 self
.parser
= kwargs
['parser']
287 """Input parser for this test case."""
289 del kwargs
['transforms'], kwargs
['parser'] # only wanted here
290 CustomTestCase
.__init
__(self
, *args
, **kwargs
)
292 def supports(self
, format
):
295 def test_transforms(self
):
296 if self
.run_in_debugger
:
298 document
= utils
.new_document('test data', self
.settings
)
299 self
.parser
.parse(self
.input, document
)
300 # Don't do a ``populate_from_components()`` because that would
301 # enable the Transformer's default transforms.
302 document
.transformer
.add_transforms(self
.transforms
)
303 document
.transformer
.add_transform(universal
.TestMessages
)
304 document
.transformer
.components
['writer'] = self
305 document
.transformer
.apply_transforms()
306 output
= document
.pformat()
307 self
.compare_output(self
.input, output
, self
.expected
)
309 def test_transforms_verbosely(self
):
310 if self
.run_in_debugger
:
315 document
= utils
.new_document('test data', self
.settings
)
316 self
.parser
.parse(self
.input, document
)
318 print document
.pformat()
319 for transformClass
in self
.transforms
:
320 transformClass(document
).apply()
321 output
= document
.pformat()
324 self
.compare_output(self
.input, output
, self
.expected
)
327 class ParserTestCase(CustomTestCase
):
330 Output checker for the parser.
332 Should probably be called ParserOutputChecker, but I can deal with
333 that later when/if someone comes up with a category of parser test
334 cases that have nothing to do with the input and output of the parser.
337 parser
= rst
.Parser()
338 """Parser shared by all ParserTestCases."""
340 option_parser
= frontend
.OptionParser(components
=(rst
.Parser
,))
341 settings
= option_parser
.get_default_values()
342 settings
.report_level
= 5
343 settings
.halt_level
= 5
344 settings
.debug
= package_unittest
.debug
346 def test_parser(self
):
347 if self
.run_in_debugger
:
349 document
= utils
.new_document('test data', self
.settings
)
350 # Remove any additions made by "role" directives:
352 self
.parser
.parse(self
.input, document
)
353 output
= document
.pformat()
354 self
.compare_output(self
.input, output
, self
.expected
)
357 class ParserTestSuite(CustomTestSuite
):
360 A collection of ParserTestCases.
362 A ParserTestSuite instance manufactures ParserTestCases,
363 keeps track of them, and provides a shared test fixture (a-la
367 test_case_class
= ParserTestCase
369 def generateTests(self
, dict, dictname
='totest'):
371 Stock the suite with test cases generated from a test data dictionary.
373 Each dictionary key (test type name) maps to a list of tests. Each
374 test is a list: input, expected output, optional modifier. The
375 optional third entry, a behavior modifier, can be 0 (temporarily
376 disable this test) or 1 (run this test under the pdb debugger). Tests
377 should be self-documenting and not require external comments.
379 for name
, cases
in dict.items():
380 for casenum
in range(len(cases
)):
381 case
= cases
[casenum
]
389 self
.test_case_class
, 'test_parser',
390 input=case
[0], expected
=case
[1],
391 id='%s[%r][%s]' % (dictname
, name
, casenum
),
392 run_in_debugger
=run_in_debugger
)
395 class PEPParserTestCase(ParserTestCase
):
397 """PEP-specific parser test case."""
399 parser
= rst
.Parser(rfc2822
=1, inliner
=pep
.Inliner())
400 """Parser shared by all PEPParserTestCases."""
402 option_parser
= frontend
.OptionParser(components
=(rst
.Parser
, pep
.Reader
))
403 settings
= option_parser
.get_default_values()
404 settings
.report_level
= 5
405 settings
.halt_level
= 5
406 settings
.debug
= package_unittest
.debug
409 class PEPParserTestSuite(ParserTestSuite
):
411 """A collection of PEPParserTestCases."""
413 test_case_class
= PEPParserTestCase
416 class GridTableParserTestCase(CustomTestCase
):
418 parser
= tableparser
.GridTableParser()
420 def test_parse_table(self
):
421 self
.parser
.setup(StringList(string2lines(self
.input), 'test data'))
423 self
.parser
.find_head_body_sep()
424 self
.parser
.parse_table()
425 output
= self
.parser
.cells
426 except Exception, details
:
427 output
= '%s: %s' % (details
.__class
__.__name
__, details
)
428 self
.compare_output(self
.input, pformat(output
) + '\n',
429 pformat(self
.expected
) + '\n')
431 def test_parse(self
):
433 output
= self
.parser
.parse(StringList(string2lines(self
.input),
435 except Exception, details
:
436 output
= '%s: %s' % (details
.__class
__.__name
__, details
)
437 self
.compare_output(self
.input, pformat(output
) + '\n',
438 pformat(self
.expected
) + '\n')
441 class GridTableParserTestSuite(CustomTestSuite
):
444 A collection of GridTableParserTestCases.
446 A GridTableParserTestSuite instance manufactures GridTableParserTestCases,
447 keeps track of them, and provides a shared test fixture (a-la setUp and
451 test_case_class
= GridTableParserTestCase
453 def generateTests(self
, dict, dictname
='totest'):
455 Stock the suite with test cases generated from a test data dictionary.
457 Each dictionary key (test type name) maps to a list of tests. Each
458 test is a list: an input table, expected output from parse_table(),
459 expected output from parse(), optional modifier. The optional fourth
460 entry, a behavior modifier, can be 0 (temporarily disable this test)
461 or 1 (run this test under the pdb debugger). Tests should be
462 self-documenting and not require external comments.
464 for name
, cases
in dict.items():
465 for casenum
in range(len(cases
)):
466 case
= cases
[casenum
]
473 self
.addTestCase(self
.test_case_class
, 'test_parse_table',
474 input=case
[0], expected
=case
[1],
475 id='%s[%r][%s]' % (dictname
, name
, casenum
),
476 run_in_debugger
=run_in_debugger
)
477 self
.addTestCase(self
.test_case_class
, 'test_parse',
478 input=case
[0], expected
=case
[2],
479 id='%s[%r][%s]' % (dictname
, name
, casenum
),
480 run_in_debugger
=run_in_debugger
)
483 class SimpleTableParserTestCase(GridTableParserTestCase
):
485 parser
= tableparser
.SimpleTableParser()
488 class SimpleTableParserTestSuite(CustomTestSuite
):
491 A collection of SimpleTableParserTestCases.
494 test_case_class
= SimpleTableParserTestCase
496 def generateTests(self
, dict, dictname
='totest'):
498 Stock the suite with test cases generated from a test data dictionary.
500 Each dictionary key (test type name) maps to a list of tests. Each
501 test is a list: an input table, expected output from parse(), optional
502 modifier. The optional third entry, a behavior modifier, can be 0
503 (temporarily disable this test) or 1 (run this test under the pdb
504 debugger). Tests should be self-documenting and not require external
507 for name
, cases
in dict.items():
508 for casenum
in range(len(cases
)):
509 case
= cases
[casenum
]
516 self
.addTestCase(self
.test_case_class
, 'test_parse',
517 input=case
[0], expected
=case
[1],
518 id='%s[%r][%s]' % (dictname
, name
, casenum
),
519 run_in_debugger
=run_in_debugger
)
522 class PythonModuleParserTestCase(CustomTestCase
):
524 def test_parser(self
):
525 if self
.run_in_debugger
:
527 module
= moduleparser
.parse_module(self
.input, 'test data').pformat()
529 self
.compare_output(self
.input, output
, self
.expected
)
531 def test_token_parser_rhs(self
):
532 if self
.run_in_debugger
:
534 tr
= moduleparser
.TokenParser(self
.input)
536 self
.compare_output(self
.input, output
, self
.expected
)
539 class PythonModuleParserTestSuite(CustomTestSuite
):
542 A collection of PythonModuleParserTestCase.
547 def __init__(self
, *args
, **kwargs
):
548 if moduleparser
is None:
549 if not self
.notified
:
550 print ('Tests of docutils.readers.python skipped; '
551 'Python 2.2 or higher required.')
552 PythonModuleParserTestSuite
.notified
= 1
553 self
.generateTests
= self
.generate_no_tests
554 CustomTestSuite
.__init
__(self
, *args
, **kwargs
)
556 def generateTests(self
, dict, dictname
='totest',
557 testmethod
='test_parser'):
559 Stock the suite with test cases generated from a test data dictionary.
561 Each dictionary key (test type's name) maps to a list of tests. Each
562 test is a list: input, expected output, optional modifier. The
563 optional third entry, a behavior modifier, can be 0 (temporarily
564 disable this test) or 1 (run this test under the pdb debugger). Tests
565 should be self-documenting and not require external comments.
567 for name
, cases
in dict.items():
568 for casenum
in range(len(cases
)):
569 case
= cases
[casenum
]
577 PythonModuleParserTestCase
, testmethod
,
578 input=case
[0], expected
=case
[1],
579 id='%s[%r][%s]' % (dictname
, name
, casenum
),
580 run_in_debugger
=run_in_debugger
)
583 class WriterPublishTestCase(CustomTestCase
, docutils
.SettingsSpec
):
586 Test case for publish.
589 settings_default_overrides
= {'_disable_config': 1}
590 writer_name
= '' # override in subclasses
592 def test_publish(self
):
593 if self
.run_in_debugger
:
595 output
= docutils
.core
.publish_string(
597 reader_name
='standalone',
598 parser_name
='restructuredtext',
599 writer_name
=self
.writer_name
,
601 self
.compare_output(self
.input, output
, self
.expected
)
604 class LatexWriterPublishTestCase(WriterPublishTestCase
):
607 Test case for Latex writer.
610 writer_name
= 'latex'
613 class PseudoXMLWriterPublishTestCase(WriterPublishTestCase
):
616 Test case for pseudo-XML writer.
619 writer_name
= 'pseudoxml'
622 class PublishTestSuite(CustomTestSuite
):
625 'latex': LatexWriterPublishTestCase
,
626 'pseudoxml': PseudoXMLWriterPublishTestCase
,
629 def __init__(self
, writer_name
):
631 `writer_name` is the name of the writer
632 to use. It must be a key in `TEST_CLASSES`.
634 CustomTestSuite
.__init
__(self
)
635 self
.test_class
= self
.TEST_CLASSES
[writer_name
]
637 def generateTests(self
, dict, dictname
='totest'):
638 for name
, cases
in dict.items():
639 for casenum
in range(len(cases
)):
640 case
= cases
[casenum
]
648 self
.test_class
, 'test_publish',
649 input=case
[0], expected
=case
[1],
650 id='%s[%r][%s]' % (dictname
, name
, casenum
),
651 run_in_debugger
=run_in_debugger
)
654 class HtmlPublishPartsTestSuite(CustomTestSuite
):
656 def generateTests(self
, dict, dictname
='totest'):
657 for name
, (settings_overrides
, cases
) in dict.items():
658 for casenum
in range(len(cases
)):
659 case
= cases
[casenum
]
667 HtmlWriterPublishPartsTestCase
, 'test_publish',
668 settings_overrides
=settings_overrides
,
669 input=case
[0], expected
=case
[1],
670 id='%s[%r][%s]' % (dictname
, name
, casenum
),
671 run_in_debugger
=run_in_debugger
)
674 class HtmlWriterPublishPartsTestCase(WriterPublishTestCase
):
677 Test case for HTML writer via the publish_parts interface.
682 def __init__(self
, *args
, **kwargs
):
683 self
.settings_overrides
= kwargs
['settings_overrides']
684 """Settings overrides to use for this test case."""
686 del kwargs
['settings_overrides'] # only wanted here
687 CustomTestCase
.__init
__(self
, *args
, **kwargs
)
689 def test_publish(self
):
690 if self
.run_in_debugger
:
692 parts
= docutils
.core
.publish_parts(
694 reader_name
='standalone',
695 parser_name
='restructuredtext',
696 writer_name
=self
.writer_name
,
698 settings_overrides
=self
.settings_overrides
)
699 output
= self
.format_output(parts
)
700 # interpolate standard variables:
701 expected
= self
.expected
% {'version': docutils
.__version
__}
702 self
.compare_output(self
.input, output
, expected
)
704 standard_meta_value
= """\
705 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
706 <meta name="generator" content="Docutils %s: http://docutils.sourceforge.net/" />
707 """ % docutils
.__version
__
708 standard_stylesheet_value
= ('<link rel="stylesheet" href="default.css" '
709 'type="text/css" />\n')
711 def format_output(self
, parts
):
712 """Minimize & standardize the output."""
713 # remove redundant bits:
716 # remove standard bits:
717 parts
['meta'] = parts
['meta'].replace(self
.standard_meta_value
, '')
718 if parts
['stylesheet'] == self
.standard_stylesheet_value
:
719 del parts
['stylesheet']
720 # remove empty values:
721 for key
in parts
.keys():
724 # standard output format:
729 output
.append("%r: '''%s'''"
730 % (key
, parts
[key
].encode('raw_unicode_escape')))
731 if output
[-1].endswith("\n'''"):
732 output
[-1] = output
[-1][:-4] + "\\n'''"
733 return '{' + ',\n '.join(output
) + '}\n'
736 def exception_data(code
):
738 Execute `code` and return the resulting exception, the exception arguments,
739 and the formatted exception string.
743 except Exception, detail
:
744 return (detail
, detail
.args
,
745 '%s: %s' % (detail
.__class
__.__name
__, detail
))