tests/dns_forwarder: Add testing for DNS forwarding
[Samba.git] / python / samba / tests / docs.py
blob238b5c7e7d65d1ccdf40e19b080945daad0ddd65
1 # Unix SMB/CIFS implementation.
2 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2012
4 # Tests for documentation.
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 3 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 """Tests for presence of documentation."""
22 import samba
23 import samba.tests
25 import os
26 import re
27 import subprocess
28 import xml.etree.ElementTree as ET
30 class TestCase(samba.tests.TestCaseInTempDir):
32 def _format_message(self, parameters, message):
33 parameters = list(parameters)
34 parameters = map(str, parameters)
35 parameters.sort()
36 return message + '\n\n %s' % ('\n '.join(parameters))
39 def get_documented_parameters(sourcedir):
40 path = os.path.join(sourcedir, "bin", "default", "docs-xml", "smbdotconf")
41 if not os.path.exists(os.path.join(path, "parameters.all.xml")):
42 raise Exception("Unable to find parameters.all.xml")
43 try:
44 p = open(os.path.join(path, "parameters.all.xml"), 'r')
45 except IOError, e:
46 raise Exception("Error opening parameters file")
47 out = p.read()
49 root = ET.fromstring(out)
50 for parameter in root:
51 name = parameter.attrib.get('name')
52 if parameter.attrib.get('removed') == "1":
53 continue
54 yield name
55 syn = parameter.findall('synonym')
56 if syn is not None:
57 for sy in syn:
58 yield sy.text
59 p.close()
62 def get_documented_tuples(sourcedir, omit_no_default=True):
63 path = os.path.join(sourcedir, "bin", "default", "docs-xml", "smbdotconf")
64 if not os.path.exists(os.path.join(path, "parameters.all.xml")):
65 raise Exception("Unable to find parameters.all.xml")
66 try:
67 p = open(os.path.join(path, "parameters.all.xml"), 'r')
68 except IOError, e:
69 raise Exception("Error opening parameters file")
70 out = p.read()
72 root = ET.fromstring(out)
73 for parameter in root:
74 name = parameter.attrib.get("name")
75 param_type = parameter.attrib.get("type")
76 if parameter.attrib.get('removed') == "1":
77 continue
78 values = parameter.findall("value")
79 defaults = []
80 for value in values:
81 if value.attrib.get("type") == "default":
82 defaults.append(value)
84 default_text = None
85 if len(defaults) == 0:
86 if omit_no_default:
87 continue
88 elif len(defaults) > 1:
89 raise Exception("More than one default found for parameter %s" % name)
90 else:
91 default_text = defaults[0].text
93 if default_text is None:
94 default_text = ""
95 context = parameter.attrib.get("context")
96 yield name, default_text, context, param_type
97 p.close()
99 class SmbDotConfTests(TestCase):
101 # defines the cases where the defaults may differ from the documentation
102 special_cases = set(['log level', 'path', 'ldapsam:trusted', 'spoolss: architecture',
103 'share:fake_fscaps', 'ldapsam:editposix', 'rpc_daemon:DAEMON',
104 'rpc_server:SERVER', 'panic action', 'homedir map', 'NIS homedir',
105 'server string', 'netbios name', 'socket options', 'use mmap',
106 'ctdbd socket', 'printing', 'printcap name', 'queueresume command',
107 'queuepause command','lpresume command', 'lppause command',
108 'lprm command', 'lpq command', 'print command', 'template homedir',
109 'spoolss: os_major', 'spoolss: os_minor', 'spoolss: os_build',
110 'max open files', 'fss: prune stale', 'fss: sequence timeout'])
112 def setUp(self):
113 super(SmbDotConfTests, self).setUp()
114 # create a minimal smb.conf file for testparm
115 self.smbconf = os.path.join(self.tempdir, "paramtestsmb.conf")
116 f = open(self.smbconf, 'w')
117 try:
118 f.write("""
119 [test]
120 path = /
121 """)
122 finally:
123 f.close()
125 self.blankconf = os.path.join(self.tempdir, "emptytestsmb.conf")
126 f = open(self.blankconf, 'w')
127 try:
128 f.write("")
129 finally:
130 f.close()
132 self.topdir = os.path.abspath(samba.source_tree_topdir())
134 try:
135 self.documented = set(get_documented_parameters(self.topdir))
136 except:
137 self.fail("Unable to load documented parameters")
139 try:
140 self.defaults = set(get_documented_tuples(self.topdir))
141 except:
142 self.fail("Unable to load parameters")
144 try:
145 self.defaults_all = set(get_documented_tuples(self.topdir, False))
146 except:
147 self.fail("Unable to load parameters")
150 def tearDown(self):
151 super(SmbDotConfTests, self).tearDown()
152 os.unlink(self.smbconf)
153 os.unlink(self.blankconf)
155 def test_default_s3(self):
156 self._test_default(['bin/testparm'])
157 self._set_defaults(['bin/testparm'])
159 # registry shares appears to need sudo
160 self._set_arbitrary(['bin/testparm'],
161 exceptions = ['client lanman auth',
162 'client plaintext auth',
163 'registry shares',
164 'smb ports'])
165 self._test_empty(['bin/testparm'])
167 def test_default_s4(self):
168 self._test_default(['bin/samba-tool', 'testparm'])
169 self._set_defaults(['bin/samba-tool', 'testparm'])
170 self._set_arbitrary(['bin/samba-tool', 'testparm'],
171 exceptions = ['smb ports'])
172 self._test_empty(['bin/samba-tool', 'testparm'])
174 def _test_default(self, program):
175 failset = set()
176 count = 0
178 for tuples in self.defaults:
179 param, default, context, param_type = tuples
180 if param in self.special_cases:
181 continue
182 section = None
183 if context == "G":
184 section = "global"
185 elif context == "S":
186 section = "test"
187 else:
188 self.fail("%s has no valid context" % param)
189 p = subprocess.Popen(program + ["-s", self.smbconf,
190 "--section-name", section, "--parameter-name", param],
191 stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=self.topdir).communicate()
192 if p[0].upper().strip() != default.upper():
193 if not (p[0].upper().strip() == "" and default == '""'):
194 doc_triple = "%s\n Expected: %s" % (param, default)
195 failset.add("%s\n Got: %s" % (doc_triple, p[0].upper().strip()))
197 if len(failset) > 0:
198 self.fail(self._format_message(failset,
199 "Parameters that do not have matching defaults:"))
201 def _set_defaults(self, program):
202 failset = set()
203 count = 0
205 for tuples in self.defaults:
206 param, default, context, param_type = tuples
208 if param in ['printing']:
209 continue
211 section = None
212 if context == "G":
213 section = "global"
214 elif context == "S":
215 section = "test"
216 else:
217 self.fail("%s has no valid context" % param)
218 p = subprocess.Popen(program + ["-s", self.smbconf,
219 "--section-name", section, "--parameter-name", param,
220 "--option", "%s = %s" % (param, default)],
221 stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=self.topdir).communicate()
222 if p[0].upper().strip() != default.upper():
223 if not (p[0].upper().strip() == "" and default == '""'):
224 doc_triple = "%s\n Expected: %s" % (param, default)
225 failset.add("%s\n Got: %s" % (doc_triple, p[0].upper().strip()))
227 if len(failset) > 0:
228 self.fail(self._format_message(failset,
229 "Parameters that do not have matching defaults:"))
231 def _set_arbitrary(self, program, exceptions=None):
232 arbitrary = {'string': 'string', 'boolean': 'yes', 'integer': '5',
233 'boolean-rev': 'yes',
234 'cmdlist': 'a b c',
235 'bytes': '10',
236 'octal': '0123',
237 'ustring': 'ustring',
238 'enum':'', 'boolean-auto': '', 'char': 'a', 'list': 'a, b, c'}
239 opposite_arbitrary = {'string': 'string2', 'boolean': 'no', 'integer': '6',
240 'boolean-rev': 'no',
241 'cmdlist': 'd e f',
242 'bytes': '11',
243 'octal': '0567',
244 'ustring': 'ustring2',
245 'enum':'', 'boolean-auto': '', 'char': 'b', 'list': 'd, e, f'}
247 failset = set()
248 count = 0
250 for tuples in self.defaults_all:
251 param, default, context, param_type = tuples
253 if param in ['printing', 'copy', 'include', 'log level']:
254 continue
256 # currently no easy way to set an arbitrary value for these
257 if param_type in ['enum', 'boolean-auto']:
258 continue
260 if exceptions is not None:
261 if param in exceptions:
262 continue
264 section = None
265 if context == "G":
266 section = "global"
267 elif context == "S":
268 section = "test"
269 else:
270 self.fail("%s has no valid context" % param)
272 value_to_use = arbitrary.get(param_type)
273 if value_to_use is None:
274 self.fail("%s has an invalid type" % param)
276 p = subprocess.Popen(program + ["-s", self.smbconf,
277 "--section-name", section, "--parameter-name", param,
278 "--option", "%s = %s" % (param, value_to_use)],
279 stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=self.topdir).communicate()
280 if p[0].upper().strip() != value_to_use.upper():
281 # currently no way to distinguish command lists
282 if param_type == 'list':
283 if ", ".join(p[0].upper().strip().split()) == value_to_use.upper():
284 continue
286 # currently no way to identify octal
287 if param_type == 'integer':
288 try:
289 if int(value_to_use, 8) == int(p[0].strip(), 8):
290 continue
291 except:
292 pass
294 doc_triple = "%s\n Expected: %s" % (param, value_to_use)
295 failset.add("%s\n Got: %s" % (doc_triple, p[0].upper().strip()))
297 opposite_value = opposite_arbitrary.get(param_type)
298 tempconf = os.path.join(self.tempdir, "tempsmb.conf")
299 g = open(tempconf, 'w')
300 try:
301 towrite = section + "\n"
302 towrite += param + " = " + opposite_value
303 g.write(towrite)
304 finally:
305 g.close()
307 p = subprocess.Popen(program + ["-s", tempconf, "--suppress-prompt",
308 "--option", "%s = %s" % (param, value_to_use)],
309 stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=self.topdir).communicate()
311 os.unlink(tempconf)
313 # testparm doesn't display a value if they are equivalent
314 if (value_to_use.lower() != opposite_value.lower()):
315 for line in p[0].splitlines():
316 if not line.strip().startswith(param):
317 continue
319 value_found = line.split("=")[1].upper().strip()
320 if value_found != value_to_use.upper():
321 # currently no way to distinguish command lists
322 if param_type == 'list':
323 if ", ".join(value_found.split()) == value_to_use.upper():
324 continue
326 # currently no way to identify octal
327 if param_type == 'integer':
328 try:
329 if int(value_to_use, 8) == int(value_found, 8):
330 continue
331 except:
332 pass
334 doc_triple = "%s\n Expected: %s" % (param, value_to_use)
335 failset.add("%s\n Got: %s" % (doc_triple, value_found))
338 if len(failset) > 0:
339 self.fail(self._format_message(failset,
340 "Parameters that were unexpectedly not set:"))
342 def _test_empty(self, program):
343 p = subprocess.Popen(program + ["-s", self.blankconf, "--suppress-prompt"],
344 stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=self.topdir).communicate()
345 output = ""
347 for line in p[0].splitlines():
348 if line.strip().startswith('#'):
349 continue
350 if line.strip().startswith("idmap config *"):
351 continue
352 output += line.strip().lower() + '\n'
354 if output.strip() != '[global]' and output.strip() != '[globals]':
355 self.fail("Testparm returned unexpected output on an empty smb.conf.")