Fix: os.walk patch
[docutils.git] / test / test_functional.py
blob91e1f5f408fff53cf36aec374a8be9da62a65f14
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 def __init__(self, *args, **kwargs):
89 """Set self.configfile, pass arguments to parent __init__."""
90 self.configfile = kwargs['configfile']
91 del kwargs['configfile']
92 DocutilsTestSupport.CustomTestCase.__init__(self, *args, **kwargs)
94 def shortDescription(self):
95 return 'test_functional.py: ' + self.configfile
97 def test(self):
98 """Process self.configfile."""
99 os.chdir(DocutilsTestSupport.testroot)
100 # Keyword parameters for publish_file:
101 namespace = {}
102 # Initialize 'settings_overrides' for test settings scripts,
103 # and disable configuration files:
104 namespace['settings_overrides'] = {'_disable_config': 1}
105 # Read the variables set in the default config file and in
106 # the current config file into namespace:
107 defaultpy = open(join_path(datadir, 'tests', '_default.py')).read()
108 exec(defaultpy, namespace)
109 exec(open(self.configfile).read(), namespace)
110 # Check for required settings:
111 assert 'test_source' in namespace,\
112 "No 'test_source' supplied in " + self.configfile
113 assert 'test_destination' in namespace,\
114 "No 'test_destination' supplied in " + self.configfile
115 # Set source_path and destination_path if not given:
116 namespace.setdefault('source_path',
117 join_path(datadir, 'input',
118 namespace['test_source']))
119 # Path for actual output:
120 namespace.setdefault('destination_path',
121 join_path(datadir, 'output',
122 namespace['test_destination']))
123 # Path for expected output:
124 expected_path = join_path(datadir, 'expected',
125 namespace['test_destination'])
126 # shallow copy of namespace to minimize:
127 params = namespace.copy()
128 # remove unneeded parameters:
129 del params['test_source']
130 del params['test_destination']
131 # Delete private stuff like params['__builtins__']:
132 for key in params.keys():
133 if key.startswith('_'):
134 del params[key]
135 # Get output (automatically written to the output/ directory
136 # by publish_file):
137 output = docutils.core.publish_file(**params)
138 # Get the expected output *after* writing the actual output.
139 self.assert_(os.access(expected_path, os.R_OK),\
140 'Cannot find expected output at\n' + expected_path)
141 f = open(expected_path, 'rU')
142 expected = f.read()
143 f.close()
144 diff = ('The expected and actual output differs.\n'
145 'Please compare the expected and actual output files:\n'
146 ' diff %s %s\n'
147 'If the actual output is correct, please replace the\n'
148 'expected output and check it in to Subversion:\n'
149 ' mv %s %s\n'
150 ' svn commit -m "<comment>" %s'
151 % (expected_path, params['destination_path'],
152 params['destination_path'], expected_path, expected_path))
153 try:
154 self.assertEquals(output, expected, diff)
155 except AssertionError:
156 if hasattr(difflib, 'unified_diff'):
157 # Generate diff if unified_diff available:
158 diff = ''.join(
159 difflib.unified_diff(expected.splitlines(1),
160 output.splitlines(1),
161 expected_path,
162 params['destination_path']))
163 print >>sys.stderr, '\n%s:' % (self,)
164 print >>sys.stderr, diff
165 raise
166 # Execute optional function containing extra tests:
167 if '_test_more' in namespace:
168 namespace['_test_more'](join_path(datadir, 'expected'),
169 join_path(datadir, 'output'),
170 self, namespace)
173 def suite():
174 return FunctionalTestSuite()
177 if __name__ == '__main__':
178 unittest.main(defaultTest='suite')