libsmb: Add cli_create_send/recv
[Samba.git] / python / samba / tests / docs.py
blob927b3749020116ac029a8bb75db8516f4ca9f9bf
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
24 from samba.tests import TestSkipped, TestCaseInTempDir
26 import errno
27 import os
28 import re
29 import subprocess
30 import xml.etree.ElementTree as ET
32 class TestCase(samba.tests.TestCaseInTempDir):
34 def _format_message(self, parameters, message):
35 parameters = list(parameters)
36 parameters.sort()
37 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_implementation_parameters(sourcedir):
63 # Reading entries from source code
64 f = open(os.path.join(sourcedir, "lib/param/param_table.c"), "r")
65 try:
66 # burn through the preceding lines
67 while True:
68 l = f.readline()
69 if l.startswith("struct parm_struct parm_table"):
70 break
72 for l in f.readlines():
73 if re.match("^\s*\}\;\s*$", l):
74 break
75 # pull in the param names only
76 if re.match(".*P_SEPARATOR.*", l):
77 continue
78 m = re.match("\s*\.label\s*=\s*\"(.*)\".*", l)
79 if not m:
80 continue
82 name = m.group(1)
83 yield name
84 finally:
85 f.close()
87 def get_documented_tuples(sourcedir, omit_no_default=True):
88 path = os.path.join(sourcedir, "bin", "default", "docs-xml", "smbdotconf")
89 if not os.path.exists(os.path.join(path, "parameters.all.xml")):
90 raise Exception("Unable to find parameters.all.xml")
91 try:
92 p = open(os.path.join(path, "parameters.all.xml"), 'r')
93 except IOError, e:
94 raise Exception("Error opening parameters file")
95 out = p.read()
97 root = ET.fromstring(out)
98 for parameter in root:
99 name = parameter.attrib.get("name")
100 param_type = parameter.attrib.get("type")
101 if parameter.attrib.get('removed') == "1":
102 continue
103 values = parameter.findall("value")
104 defaults = []
105 for value in values:
106 if value.attrib.get("type") == "default":
107 defaults.append(value)
109 default_text = None
110 if len(defaults) == 0:
111 if omit_no_default:
112 continue
113 elif len(defaults) > 1:
114 raise Exception("More than one default found for parameter %s" % name)
115 else:
116 default_text = defaults[0].text
118 if default_text is None:
119 default_text = ""
120 context = parameter.attrib.get("context")
121 yield name, default_text, context, param_type
122 p.close()
124 class SmbDotConfTests(TestCase):
126 # defines the cases where the defaults may differ from the documentation
127 special_cases = set(['log level', 'path', 'ldapsam:trusted', 'spoolss: architecture',
128 'share:fake_fscaps', 'ldapsam:editposix', 'rpc_daemon:DAEMON',
129 'rpc_server:SERVER', 'panic action', 'homedir map', 'NIS homedir',
130 'server string', 'netbios name', 'socket options', 'use mmap',
131 'ctdbd socket', 'printing', 'printcap name', 'queueresume command',
132 'queuepause command','lpresume command', 'lppause command',
133 'lprm command', 'lpq command', 'print command', 'template homedir',
134 'spoolss: os_major', 'spoolss: os_minor', 'spoolss: os_build'])
136 def setUp(self):
137 super(SmbDotConfTests, self).setUp()
138 # create a minimal smb.conf file for testparm
139 self.smbconf = os.path.join(self.tempdir, "paramtestsmb.conf")
140 f = open(self.smbconf, 'w')
141 try:
142 f.write("""
143 [test]
144 path = /
145 """)
146 finally:
147 f.close()
149 def tearDown(self):
150 super(SmbDotConfTests, self).tearDown()
151 os.unlink(self.smbconf)
153 def test_unknown(self):
154 topdir = os.path.abspath(samba.source_tree_topdir())
155 try:
156 documented = set(get_documented_parameters(topdir))
157 except e:
158 self.fail("Unable to load parameters")
159 parameters = set(get_implementation_parameters(topdir))
160 # Filter out parametric options, since we can't find them in the parm
161 # table
162 documented = set([p for p in documented if not ":" in p])
163 unknown = documented.difference(parameters)
164 if len(unknown) > 0:
165 self.fail(self._format_message(unknown,
166 "Parameters that are documented but not in the implementation:"))
168 def test_undocumented(self):
169 topdir = os.path.abspath(samba.source_tree_topdir())
170 try:
171 documented = set(get_documented_parameters(topdir))
172 except:
173 self.fail("Unable to load parameters")
174 parameters = set(get_implementation_parameters(topdir))
175 undocumented = parameters.difference(documented)
176 if len(undocumented) > 0:
177 self.fail(self._format_message(undocumented,
178 "Parameters that are in the implementation but undocumented:"))
180 def test_default_s3(self):
181 self._test_default(['bin/testparm'])
182 self._set_defaults(['bin/testparm'])
184 # registry shares appears to need sudo
185 self._set_arbitrary(['bin/testparm'],
186 exceptions = ['client lanman auth',
187 'client plaintext auth',
188 'registry shares',
189 'idmap backend',
190 'idmap gid',
191 'idmap uid'])
193 def test_default_s4(self):
194 self._test_default(['bin/samba-tool', 'testparm'])
195 self._set_defaults(['bin/samba-tool', 'testparm'])
196 self._set_arbitrary(['bin/samba-tool', 'testparm'])
198 def _test_default(self, program):
199 topdir = os.path.abspath(samba.source_tree_topdir())
200 try:
201 defaults = set(get_documented_tuples(topdir))
202 except:
203 self.fail("Unable to load parameters")
204 bindir = os.path.join(topdir, "bin")
205 failset = set()
206 count = 0
208 for tuples in defaults:
209 param, default, context, param_type = tuples
210 if param in self.special_cases:
211 continue
212 section = None
213 if context == "G":
214 section = "global"
215 elif context == "S":
216 section = "test"
217 else:
218 self.fail("%s has no valid context" % param)
219 p = subprocess.Popen(program + ["-s", self.smbconf,
220 "--section-name", section, "--parameter-name", param],
221 stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=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_defaults(self, program):
232 topdir = os.path.abspath(samba.source_tree_topdir())
233 try:
234 defaults = set(get_documented_tuples(topdir))
235 except:
236 self.fail("Unable to load parameters")
237 bindir = os.path.join(topdir, "bin")
238 failset = set()
239 count = 0
241 for tuples in defaults:
242 param, default, context, param_type = tuples
244 if param in ['printing']:
245 continue
247 section = None
248 if context == "G":
249 section = "global"
250 elif context == "S":
251 section = "test"
252 else:
253 self.fail("%s has no valid context" % param)
254 p = subprocess.Popen(program + ["-s", self.smbconf,
255 "--section-name", section, "--parameter-name", param,
256 "--option", "%s = %s" % (param, default)],
257 stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=topdir).communicate()
258 if p[0].upper().strip() != default.upper():
259 if not (p[0].upper().strip() == "" and default == '""'):
260 doc_triple = "%s\n Expected: %s" % (param, default)
261 failset.add("%s\n Got: %s" % (doc_triple, p[0].upper().strip()))
263 if len(failset) > 0:
264 self.fail(self._format_message(failset,
265 "Parameters that do not have matching defaults:"))
267 def _set_arbitrary(self, program, exceptions=None):
268 arbitrary = {'string': 'string', 'boolean': 'yes', 'integer': '5',
269 'enum':'', 'boolean-auto': '', 'char': 'a', 'list': 'a, b, c'}
271 topdir = os.path.abspath(samba.source_tree_topdir())
272 try:
273 defaults = set(get_documented_tuples(topdir, False))
274 except Exception,e:
275 self.fail("Unable to load parameters" + e)
276 bindir = os.path.join(topdir, "bin")
277 failset = set()
278 count = 0
280 for tuples in defaults:
281 param, default, context, param_type = tuples
283 if param in ['printing', 'copy', 'include', 'log level']:
284 continue
286 # currently no easy way to set an arbitrary value for these
287 if param_type in ['enum', 'boolean-auto']:
288 continue
290 if exceptions is not None:
291 if param in exceptions:
292 continue
294 section = None
295 if context == "G":
296 section = "global"
297 elif context == "S":
298 section = "test"
299 else:
300 self.fail("%s has no valid context" % param)
302 value_to_use = arbitrary.get(param_type)
303 if value_to_use is None:
304 self.fail("%s has an invalid type" % param)
306 p = subprocess.Popen(program + ["-s", self.smbconf,
307 "--section-name", section, "--parameter-name", param,
308 "--option", "%s = %s" % (param, value_to_use)],
309 stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=topdir).communicate()
310 if p[0].upper().strip() != value_to_use.upper():
311 # currently no way to distinguish command lists
312 if param_type == 'list':
313 if ", ".join(p[0].upper().strip().split()) == value_to_use.upper():
314 continue
316 # currently no way to identify octal
317 if param_type == 'integer':
318 try:
319 if int(value_to_use, 8) == int(p[0].strip(), 8):
320 continue
321 except:
322 pass
324 doc_triple = "%s\n Expected: %s" % (param, value_to_use)
325 failset.add("%s\n Got: %s" % (doc_triple, p[0].upper().strip()))
327 if len(failset) > 0:
328 self.fail(self._format_message(failset,
329 "Parameters that were unexpectedly not set:"))