Added rst2odt/odtwriter to the main branch.
[docutils.git] / test / test_functional.py
blob0a30719d58ba91fe09c937bab4b1a05ab2ada639
1 #!/usr/bin/env python
3 # $Id$
4 # Author: Lea Wiemann <LeWiemann@gmail.com>
5 # Copyright: This module has been placed in the public domain.
7 """
8 Perform tests with the data in the functional/ directory.
10 Read README.txt for details on how this is done.
11 """
13 import sys
14 import os
15 import os.path
16 import shutil
17 import unittest
18 import difflib
19 import DocutilsTestSupport # must be imported before docutils
20 import docutils
21 import docutils.core
24 datadir = 'functional'
25 """The directory to store the data needed for the functional tests."""
28 def join_path(*args):
29 return '/'.join(args) or '.'
32 class FunctionalTestSuite(DocutilsTestSupport.CustomTestSuite):
34 """Test suite containing test cases for all config files."""
36 def __init__(self):
37 """Process all config files in functional/tests/."""
38 DocutilsTestSupport.CustomTestSuite.__init__(self)
39 os.chdir(DocutilsTestSupport.testroot)
40 self.clear_output_directory()
41 self.added = 0
42 try:
43 for root, dirs, files in os.walk(join_path(datadir, 'tests')):
44 # Process all config files among `names` in `dirname`. A config
45 # file is a Python file (*.py) which sets several variables.
46 for name in files:
47 if name.endswith('.py') and not name.startswith('_'):
48 config_file_full_path = join_path(root, name)
49 self.addTestCase(FunctionalTestCase, 'test', None, None,
50 id=config_file_full_path,
51 configfile=config_file_full_path)
52 self.added += 1
53 except (AttributeError): # python2.2 does not have os.walk
54 os.path.walk(join_path(datadir, 'tests'), self.walker, None)
55 assert self.added, 'No functional tests found.'
57 def clear_output_directory(self):
58 files = os.listdir(os.path.join('functional', 'output'))
59 for f in files:
60 if f in ('README.txt', '.svn', 'CVS'):
61 continue # don't touch the infrastructure
62 path = os.path.join('functional', 'output', f)
63 if os.path.isdir(path):
64 shutil.rmtree(path)
65 else:
66 os.remove(path)
68 def walker(self, dummy, dirname, names):
69 """
70 Process all config files among `names` in `dirname`.
72 This is a helper function for os.path.walk. A config file is
73 a Python file (*.py) which sets several variables.
74 """
75 for name in names:
76 if name.endswith('.py') and not name.startswith('_'):
77 config_file_full_path = join_path(dirname, name)
78 self.addTestCase(FunctionalTestCase, 'test', None, None,
79 id=config_file_full_path,
80 configfile=config_file_full_path)
81 self.added += 1
84 class FunctionalTestCase(DocutilsTestSupport.CustomTestCase):
86 """Test case for one config file."""
88 no_expected_template = """\
89 Cannot find expected output at %(exp)s
90 If the output in %(out)s
91 is correct, move it to the expected/ dir and check it in:
93 mv %(out)s %(exp)s
94 svn add %(exp)s
95 svn commit -m "<comment>" %(exp)s"""
97 expected_output_differs_template = """\
98 The expected and actual output differs.
99 Please compare the expected and actual output files:
101 diff %(exp)s %(out)s\n'
103 If the actual output is correct, please replace the
104 expected output and check it in:
106 mv %(out)s %(exp)s
107 svn add %(exp)s
108 svn commit -m "<comment>" %(exp)s"""
110 def __init__(self, *args, **kwargs):
111 """Set self.configfile, pass arguments to parent __init__."""
112 self.configfile = kwargs['configfile']
113 del kwargs['configfile']
114 DocutilsTestSupport.CustomTestCase.__init__(self, *args, **kwargs)
116 def shortDescription(self):
117 return 'test_functional.py: ' + self.configfile
119 def test(self):
120 """Process self.configfile."""
121 os.chdir(DocutilsTestSupport.testroot)
122 # Keyword parameters for publish_file:
123 namespace = {}
124 # Initialize 'settings_overrides' for test settings scripts,
125 # and disable configuration files:
126 namespace['settings_overrides'] = {'_disable_config': 1}
127 # Read the variables set in the default config file and in
128 # the current config file into namespace:
129 defaultpy = open(join_path(datadir, 'tests', '_default.py')).read()
130 exec(defaultpy, namespace)
131 exec(open(self.configfile).read(), namespace)
132 # Check for required settings:
133 assert 'test_source' in namespace,\
134 "No 'test_source' supplied in " + self.configfile
135 assert 'test_destination' in namespace,\
136 "No 'test_destination' supplied in " + self.configfile
137 # Set source_path and destination_path if not given:
138 namespace.setdefault('source_path',
139 join_path(datadir, 'input',
140 namespace['test_source']))
141 # Path for actual output:
142 namespace.setdefault('destination_path',
143 join_path(datadir, 'output',
144 namespace['test_destination']))
145 # Path for expected output:
146 expected_path = join_path(datadir, 'expected',
147 namespace['test_destination'])
148 # shallow copy of namespace to minimize:
149 params = namespace.copy()
150 # remove unneeded parameters:
151 del params['test_source']
152 del params['test_destination']
153 # Delete private stuff like params['__builtins__']:
154 for key in params.keys():
155 if key.startswith('_'):
156 del params[key]
157 # Get output (automatically written to the output/ directory
158 # by publish_file):
159 output = docutils.core.publish_file(**params)
160 # Get the expected output *after* writing the actual output.
161 no_expected = self.no_expected_template % {
162 'exp': expected_path, 'out': params['destination_path']}
163 self.assert_(os.access(expected_path, os.R_OK), no_expected)
164 f = open(expected_path, 'rU')
165 expected = f.read()
166 f.close()
167 diff = self.expected_output_differs_template % {
168 'exp': expected_path, 'out': params['destination_path']}
169 try:
170 self.assertEquals(output, expected, diff)
171 except AssertionError:
172 if hasattr(difflib, 'unified_diff'):
173 # Generate diff if unified_diff available:
174 diff = ''.join(
175 difflib.unified_diff(expected.splitlines(1),
176 output.splitlines(1),
177 expected_path,
178 params['destination_path']))
179 print >>sys.stderr, '\n%s:' % (self,)
180 print >>sys.stderr, diff
181 raise
182 # Execute optional function containing extra tests:
183 if '_test_more' in namespace:
184 namespace['_test_more'](join_path(datadir, 'expected'),
185 join_path(datadir, 'output'),
186 self, namespace)
189 def suite():
190 return FunctionalTestSuite()
193 if __name__ == '__main__':
194 unittest.main(defaultTest='suite')