root / test / py / docs_unittest.py @ 11414807
History | View | Annotate | Download (10.2 kB)
1 | 3f991867 | Michael Hanselmann | #!/usr/bin/python
|
---|---|---|---|
2 | 3f991867 | Michael Hanselmann | #
|
3 | 3f991867 | Michael Hanselmann | |
4 | 3f991867 | Michael Hanselmann | # Copyright (C) 2009 Google Inc.
|
5 | 3f991867 | Michael Hanselmann | #
|
6 | 3f991867 | Michael Hanselmann | # This program is free software; you can redistribute it and/or modify
|
7 | 3f991867 | Michael Hanselmann | # it under the terms of the GNU General Public License as published by
|
8 | 3f991867 | Michael Hanselmann | # the Free Software Foundation; either version 2 of the License, or
|
9 | 3f991867 | Michael Hanselmann | # (at your option) any later version.
|
10 | 3f991867 | Michael Hanselmann | #
|
11 | 3f991867 | Michael Hanselmann | # This program is distributed in the hope that it will be useful, but
|
12 | 3f991867 | Michael Hanselmann | # WITHOUT ANY WARRANTY; without even the implied warranty of
|
13 | 3f991867 | Michael Hanselmann | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14 | 3f991867 | Michael Hanselmann | # General Public License for more details.
|
15 | 3f991867 | Michael Hanselmann | #
|
16 | 3f991867 | Michael Hanselmann | # You should have received a copy of the GNU General Public License
|
17 | 3f991867 | Michael Hanselmann | # along with this program; if not, write to the Free Software
|
18 | 3f991867 | Michael Hanselmann | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
19 | 3f991867 | Michael Hanselmann | # 02110-1301, USA.
|
20 | 3f991867 | Michael Hanselmann | |
21 | 3f991867 | Michael Hanselmann | |
22 | 3f991867 | Michael Hanselmann | """Script for unittesting documentation"""
|
23 | 3f991867 | Michael Hanselmann | |
24 | 3f991867 | Michael Hanselmann | import unittest |
25 | 3f991867 | Michael Hanselmann | import re |
26 | 8497c267 | Michael Hanselmann | import itertools |
27 | 8497c267 | Michael Hanselmann | import operator |
28 | 3f991867 | Michael Hanselmann | |
29 | 36bf7973 | Michael Hanselmann | from ganeti import _autoconf |
30 | 3f991867 | Michael Hanselmann | from ganeti import utils |
31 | 3f991867 | Michael Hanselmann | from ganeti import cmdlib |
32 | e948770c | Michael Hanselmann | from ganeti import build |
33 | 3af47e13 | Michael Hanselmann | from ganeti import compat |
34 | 83a2da0f | Michael Hanselmann | from ganeti import mcpu |
35 | 8497c267 | Michael Hanselmann | from ganeti import opcodes |
36 | 8497c267 | Michael Hanselmann | from ganeti import constants |
37 | 8497c267 | Michael Hanselmann | from ganeti.rapi import baserlib |
38 | 8497c267 | Michael Hanselmann | from ganeti.rapi import rlib2 |
39 | bf968b7f | Michael Hanselmann | from ganeti.rapi import connector |
40 | 3f991867 | Michael Hanselmann | |
41 | 3f991867 | Michael Hanselmann | import testutils |
42 | 3f991867 | Michael Hanselmann | |
43 | 3f991867 | Michael Hanselmann | |
44 | 3af47e13 | Michael Hanselmann | VALID_URI_RE = re.compile(r"^[-/a-z0-9]*$")
|
45 | 3af47e13 | Michael Hanselmann | |
46 | b8028dcf | Michael Hanselmann | RAPI_OPCODE_EXCLUDE = compat.UniqueFrozenset([ |
47 | 8497c267 | Michael Hanselmann | # Not yet implemented
|
48 | 8497c267 | Michael Hanselmann | opcodes.OpBackupQuery, |
49 | 8497c267 | Michael Hanselmann | opcodes.OpBackupRemove, |
50 | 8497c267 | Michael Hanselmann | opcodes.OpClusterConfigQuery, |
51 | 8497c267 | Michael Hanselmann | opcodes.OpClusterRepairDiskSizes, |
52 | 8497c267 | Michael Hanselmann | opcodes.OpClusterVerify, |
53 | 8497c267 | Michael Hanselmann | opcodes.OpClusterVerifyDisks, |
54 | 8497c267 | Michael Hanselmann | opcodes.OpInstanceChangeGroup, |
55 | 8497c267 | Michael Hanselmann | opcodes.OpInstanceMove, |
56 | 8497c267 | Michael Hanselmann | opcodes.OpNodeQueryvols, |
57 | 8497c267 | Michael Hanselmann | opcodes.OpOobCommand, |
58 | 8497c267 | Michael Hanselmann | opcodes.OpTagsSearch, |
59 | fb926117 | Andrea Spadaccini | opcodes.OpClusterActivateMasterIp, |
60 | fb926117 | Andrea Spadaccini | opcodes.OpClusterDeactivateMasterIp, |
61 | b954f097 | Constantinos Venetsanopoulos | opcodes.OpExtStorageDiagnose, |
62 | 8497c267 | Michael Hanselmann | |
63 | 8497c267 | Michael Hanselmann | # Difficult if not impossible
|
64 | 8497c267 | Michael Hanselmann | opcodes.OpClusterDestroy, |
65 | 8497c267 | Michael Hanselmann | opcodes.OpClusterPostInit, |
66 | 8497c267 | Michael Hanselmann | opcodes.OpClusterRename, |
67 | 8497c267 | Michael Hanselmann | opcodes.OpNodeAdd, |
68 | 8497c267 | Michael Hanselmann | opcodes.OpNodeRemove, |
69 | 8497c267 | Michael Hanselmann | |
70 | e4d745a7 | Michael Hanselmann | # Very sensitive in nature
|
71 | e4d745a7 | Michael Hanselmann | opcodes.OpRestrictedCommand, |
72 | e4d745a7 | Michael Hanselmann | |
73 | 8497c267 | Michael Hanselmann | # Helper opcodes (e.g. submitted by LUs)
|
74 | 8497c267 | Michael Hanselmann | opcodes.OpClusterVerifyConfig, |
75 | 8497c267 | Michael Hanselmann | opcodes.OpClusterVerifyGroup, |
76 | 8497c267 | Michael Hanselmann | opcodes.OpGroupEvacuate, |
77 | 8497c267 | Michael Hanselmann | opcodes.OpGroupVerifyDisks, |
78 | 8497c267 | Michael Hanselmann | |
79 | 8497c267 | Michael Hanselmann | # Test opcodes
|
80 | 8497c267 | Michael Hanselmann | opcodes.OpTestAllocator, |
81 | 8497c267 | Michael Hanselmann | opcodes.OpTestDelay, |
82 | 8497c267 | Michael Hanselmann | opcodes.OpTestDummy, |
83 | 8497c267 | Michael Hanselmann | opcodes.OpTestJqueue, |
84 | 8497c267 | Michael Hanselmann | ]) |
85 | 8497c267 | Michael Hanselmann | |
86 | 3af47e13 | Michael Hanselmann | |
87 | bf317058 | Michael Hanselmann | def _ReadDocFile(filename): |
88 | bf317058 | Michael Hanselmann | return utils.ReadFile("%s/doc/%s" % |
89 | bf317058 | Michael Hanselmann | (testutils.GetSourceDir(), filename)) |
90 | bf317058 | Michael Hanselmann | |
91 | 3f991867 | Michael Hanselmann | |
92 | 1315b792 | Michael Hanselmann | class TestHooksDocs(unittest.TestCase): |
93 | b8028dcf | Michael Hanselmann | HOOK_PATH_OK = compat.UniqueFrozenset([ |
94 | 2fd213a6 | René Nussbaumer | "master-ip-turnup",
|
95 | 2fd213a6 | René Nussbaumer | "master-ip-turndown",
|
96 | 2fd213a6 | René Nussbaumer | ]) |
97 | 2fd213a6 | René Nussbaumer | |
98 | 1315b792 | Michael Hanselmann | def test(self): |
99 | 3f991867 | Michael Hanselmann | """Check whether all hooks are documented.
|
100 | 3f991867 | Michael Hanselmann |
|
101 | 3f991867 | Michael Hanselmann | """
|
102 | bf317058 | Michael Hanselmann | hooksdoc = _ReadDocFile("hooks.rst")
|
103 | 3f991867 | Michael Hanselmann | |
104 | 83a2da0f | Michael Hanselmann | # Reverse mapping from LU to opcode
|
105 | 83a2da0f | Michael Hanselmann | lu2opcode = dict((lu, op)
|
106 | 83a2da0f | Michael Hanselmann | for (op, lu) in mcpu.Processor.DISPATCH_TABLE.items()) |
107 | 83a2da0f | Michael Hanselmann | assert len(lu2opcode) == len(mcpu.Processor.DISPATCH_TABLE), \ |
108 | 83a2da0f | Michael Hanselmann | "Found duplicate entries"
|
109 | 83a2da0f | Michael Hanselmann | |
110 | 2fd213a6 | René Nussbaumer | hooks_paths = frozenset(re.findall("^:directory:\s*(.+)\s*$", hooksdoc, |
111 | 2fd213a6 | René Nussbaumer | re.M)) |
112 | 2fd213a6 | René Nussbaumer | self.assertTrue(self.HOOK_PATH_OK.issubset(hooks_paths), |
113 | 2fd213a6 | René Nussbaumer | msg="Whitelisted path not found in documentation")
|
114 | 83a2da0f | Michael Hanselmann | |
115 | 2fd213a6 | René Nussbaumer | raw_hooks_ops = re.findall("^OP_(?!CODE$).+$", hooksdoc, re.M)
|
116 | 2fd213a6 | René Nussbaumer | hooks_ops = set()
|
117 | 2fd213a6 | René Nussbaumer | duplicate_ops = set()
|
118 | 2fd213a6 | René Nussbaumer | for op in raw_hooks_ops: |
119 | 2fd213a6 | René Nussbaumer | if op in hooks_ops: |
120 | 2fd213a6 | René Nussbaumer | duplicate_ops.add(op) |
121 | 2fd213a6 | René Nussbaumer | else:
|
122 | 2fd213a6 | René Nussbaumer | hooks_ops.add(op) |
123 | 3f991867 | Michael Hanselmann | |
124 | 2fd213a6 | René Nussbaumer | self.assertFalse(duplicate_ops,
|
125 | 2fd213a6 | René Nussbaumer | msg="Found duplicate opcode documentation: %s" %
|
126 | 2fd213a6 | René Nussbaumer | utils.CommaJoin(duplicate_ops)) |
127 | 3f991867 | Michael Hanselmann | |
128 | 2fd213a6 | René Nussbaumer | seen_paths = set()
|
129 | 2fd213a6 | René Nussbaumer | seen_ops = set()
|
130 | 3f991867 | Michael Hanselmann | |
131 | 2fd213a6 | René Nussbaumer | self.assertFalse(duplicate_ops,
|
132 | 2fd213a6 | René Nussbaumer | msg="Found duplicated hook documentation: %s" %
|
133 | 2fd213a6 | René Nussbaumer | utils.CommaJoin(duplicate_ops)) |
134 | 83a2da0f | Michael Hanselmann | |
135 | 2fd213a6 | René Nussbaumer | for name in dir(cmdlib): |
136 | 2fd213a6 | René Nussbaumer | lucls = getattr(cmdlib, name)
|
137 | 2fd213a6 | René Nussbaumer | |
138 | 2fd213a6 | René Nussbaumer | if (isinstance(lucls, type) and |
139 | 2fd213a6 | René Nussbaumer | issubclass(lucls, cmdlib.LogicalUnit) and |
140 | 2fd213a6 | René Nussbaumer | hasattr(lucls, "HPATH")): |
141 | 2fd213a6 | René Nussbaumer | if lucls.HTYPE is None: |
142 | 2fd213a6 | René Nussbaumer | continue
|
143 | 2fd213a6 | René Nussbaumer | |
144 | 2fd213a6 | René Nussbaumer | opcls = lu2opcode.get(lucls, None)
|
145 | 2fd213a6 | René Nussbaumer | |
146 | 2fd213a6 | René Nussbaumer | if opcls:
|
147 | 2fd213a6 | René Nussbaumer | seen_ops.add(opcls.OP_ID) |
148 | 2fd213a6 | René Nussbaumer | self.assertTrue(opcls.OP_ID in hooks_ops, |
149 | 2fd213a6 | René Nussbaumer | msg="Missing hook documentation for %s" %
|
150 | 2fd213a6 | René Nussbaumer | opcls.OP_ID) |
151 | 2fd213a6 | René Nussbaumer | self.assertTrue(lucls.HPATH in hooks_paths, |
152 | 2fd213a6 | René Nussbaumer | msg="Missing documentation for hook %s/%s" %
|
153 | 2fd213a6 | René Nussbaumer | (lucls.HTYPE, lucls.HPATH)) |
154 | 2fd213a6 | René Nussbaumer | seen_paths.add(lucls.HPATH) |
155 | 2fd213a6 | René Nussbaumer | |
156 | 2fd213a6 | René Nussbaumer | missed_ops = hooks_ops - seen_ops |
157 | 2fd213a6 | René Nussbaumer | missed_paths = hooks_paths - seen_paths - self.HOOK_PATH_OK
|
158 | 2fd213a6 | René Nussbaumer | |
159 | 2fd213a6 | René Nussbaumer | self.assertFalse(missed_ops,
|
160 | 2fd213a6 | René Nussbaumer | msg="Op documents hook not existing anymore: %s" %
|
161 | 2fd213a6 | René Nussbaumer | utils.CommaJoin(missed_ops)) |
162 | 2fd213a6 | René Nussbaumer | |
163 | 2fd213a6 | René Nussbaumer | self.assertFalse(missed_paths,
|
164 | 2fd213a6 | René Nussbaumer | msg="Hook path does not exist in opcode: %s" %
|
165 | 2fd213a6 | René Nussbaumer | utils.CommaJoin(missed_paths)) |
166 | 3f991867 | Michael Hanselmann | |
167 | 1315b792 | Michael Hanselmann | |
168 | 1315b792 | Michael Hanselmann | class TestRapiDocs(unittest.TestCase): |
169 | b58a4d16 | Michael Hanselmann | def _CheckRapiResource(self, uri, fixup, handler): |
170 | b58a4d16 | Michael Hanselmann | docline = "%s resource." % uri
|
171 | b58a4d16 | Michael Hanselmann | self.assertEqual(handler.__doc__.splitlines()[0].strip(), docline, |
172 | b58a4d16 | Michael Hanselmann | msg=("First line of %r's docstring is not %r" %
|
173 | b58a4d16 | Michael Hanselmann | (handler, docline))) |
174 | b58a4d16 | Michael Hanselmann | |
175 | 3af47e13 | Michael Hanselmann | # Apply fixes before testing
|
176 | 3af47e13 | Michael Hanselmann | for (rx, value) in fixup.items(): |
177 | 3af47e13 | Michael Hanselmann | uri = rx.sub(value, uri) |
178 | 3af47e13 | Michael Hanselmann | |
179 | 3af47e13 | Michael Hanselmann | self.assertTrue(VALID_URI_RE.match(uri), msg="Invalid URI %r" % uri) |
180 | 3f991867 | Michael Hanselmann | |
181 | 1315b792 | Michael Hanselmann | def test(self): |
182 | bf968b7f | Michael Hanselmann | """Check whether all RAPI resources are documented.
|
183 | bf968b7f | Michael Hanselmann |
|
184 | bf968b7f | Michael Hanselmann | """
|
185 | bf317058 | Michael Hanselmann | rapidoc = _ReadDocFile("rapi.rst")
|
186 | bf968b7f | Michael Hanselmann | |
187 | 3af47e13 | Michael Hanselmann | node_name = re.escape("[node_name]")
|
188 | 3af47e13 | Michael Hanselmann | instance_name = re.escape("[instance_name]")
|
189 | 3af47e13 | Michael Hanselmann | group_name = re.escape("[group_name]")
|
190 | 6e8091f9 | Dimitris Aragiorgis | network_name = re.escape("[network_name]")
|
191 | 3af47e13 | Michael Hanselmann | job_id = re.escape("[job_id]")
|
192 | 3af47e13 | Michael Hanselmann | disk_index = re.escape("[disk_index]")
|
193 | 1c7fd467 | Michael Hanselmann | query_res = re.escape("[resource]")
|
194 | 3af47e13 | Michael Hanselmann | |
195 | 6e8091f9 | Dimitris Aragiorgis | resources = connector.GetHandlers(node_name, instance_name, |
196 | 6e8091f9 | Dimitris Aragiorgis | group_name, network_name, |
197 | 1c7fd467 | Michael Hanselmann | job_id, disk_index, query_res) |
198 | 3af47e13 | Michael Hanselmann | |
199 | d50a2223 | Michael Hanselmann | handler_dups = utils.FindDuplicates(resources.values()) |
200 | d50a2223 | Michael Hanselmann | self.assertFalse(handler_dups,
|
201 | d50a2223 | Michael Hanselmann | msg=("Resource handlers used more than once: %r" %
|
202 | d50a2223 | Michael Hanselmann | handler_dups)) |
203 | d50a2223 | Michael Hanselmann | |
204 | 3af47e13 | Michael Hanselmann | uri_check_fixup = { |
205 | 3af47e13 | Michael Hanselmann | re.compile(node_name): "node1examplecom",
|
206 | 3af47e13 | Michael Hanselmann | re.compile(instance_name): "inst1examplecom",
|
207 | 3af47e13 | Michael Hanselmann | re.compile(group_name): "group4440",
|
208 | 6e8091f9 | Dimitris Aragiorgis | re.compile(network_name): "network5550",
|
209 | 3af47e13 | Michael Hanselmann | re.compile(job_id): "9409",
|
210 | 3af47e13 | Michael Hanselmann | re.compile(disk_index): "123",
|
211 | 1c7fd467 | Michael Hanselmann | re.compile(query_res): "lock",
|
212 | 3af47e13 | Michael Hanselmann | } |
213 | bf968b7f | Michael Hanselmann | |
214 | 3af47e13 | Michael Hanselmann | assert compat.all(VALID_URI_RE.match(value)
|
215 | 3af47e13 | Michael Hanselmann | for value in uri_check_fixup.values()), \ |
216 | 3af47e13 | Michael Hanselmann | "Fixup values must be valid URIs, too"
|
217 | bf968b7f | Michael Hanselmann | |
218 | bf968b7f | Michael Hanselmann | titles = [] |
219 | bf968b7f | Michael Hanselmann | |
220 | bf968b7f | Michael Hanselmann | prevline = None
|
221 | bf968b7f | Michael Hanselmann | for line in rapidoc.splitlines(): |
222 | bf968b7f | Michael Hanselmann | if re.match(r"^\++$", line): |
223 | bf968b7f | Michael Hanselmann | titles.append(prevline) |
224 | bf968b7f | Michael Hanselmann | |
225 | bf968b7f | Michael Hanselmann | prevline = line |
226 | bf968b7f | Michael Hanselmann | |
227 | b8028dcf | Michael Hanselmann | prefix_exception = compat.UniqueFrozenset(["/", "/version", "/2"]) |
228 | 2c0be3d0 | Michael Hanselmann | |
229 | bf968b7f | Michael Hanselmann | undocumented = [] |
230 | d50a2223 | Michael Hanselmann | used_uris = [] |
231 | bf968b7f | Michael Hanselmann | |
232 | bf968b7f | Michael Hanselmann | for key, handler in resources.iteritems(): |
233 | bf968b7f | Michael Hanselmann | # Regex objects
|
234 | bf968b7f | Michael Hanselmann | if hasattr(key, "match"): |
235 | 2c0be3d0 | Michael Hanselmann | self.assert_(key.pattern.startswith("^/2/"), |
236 | 2c0be3d0 | Michael Hanselmann | msg="Pattern %r does not start with '^/2/'" % key.pattern)
|
237 | 3af47e13 | Michael Hanselmann | self.assertEqual(key.pattern[-1], "$") |
238 | 2c0be3d0 | Michael Hanselmann | |
239 | bf968b7f | Michael Hanselmann | found = False
|
240 | bf968b7f | Michael Hanselmann | for title in titles: |
241 | 3af47e13 | Michael Hanselmann | if title.startswith("``") and title.endswith("``"): |
242 | 3af47e13 | Michael Hanselmann | uri = title[2:-2] |
243 | 3af47e13 | Michael Hanselmann | if key.match(uri):
|
244 | b58a4d16 | Michael Hanselmann | self._CheckRapiResource(uri, uri_check_fixup, handler)
|
245 | d50a2223 | Michael Hanselmann | used_uris.append(uri) |
246 | 3af47e13 | Michael Hanselmann | found = True
|
247 | 3af47e13 | Michael Hanselmann | break
|
248 | bf968b7f | Michael Hanselmann | |
249 | bf968b7f | Michael Hanselmann | if not found: |
250 | bf968b7f | Michael Hanselmann | # TODO: Find better way of identifying resource
|
251 | 2c0be3d0 | Michael Hanselmann | undocumented.append(key.pattern) |
252 | 2c0be3d0 | Michael Hanselmann | |
253 | 2c0be3d0 | Michael Hanselmann | else:
|
254 | 2c0be3d0 | Michael Hanselmann | self.assert_(key.startswith("/2/") or key in prefix_exception, |
255 | 2c0be3d0 | Michael Hanselmann | msg="Path %r does not start with '/2/'" % key)
|
256 | bf968b7f | Michael Hanselmann | |
257 | 3af47e13 | Michael Hanselmann | if ("``%s``" % key) in titles: |
258 | b58a4d16 | Michael Hanselmann | self._CheckRapiResource(key, {}, handler)
|
259 | d50a2223 | Michael Hanselmann | used_uris.append(key) |
260 | 3af47e13 | Michael Hanselmann | else:
|
261 | 2c0be3d0 | Michael Hanselmann | undocumented.append(key) |
262 | bf968b7f | Michael Hanselmann | |
263 | bf968b7f | Michael Hanselmann | self.failIf(undocumented,
|
264 | bf968b7f | Michael Hanselmann | msg=("Missing RAPI resource documentation for %s" %
|
265 | ab3e6da8 | Iustin Pop | utils.CommaJoin(undocumented))) |
266 | bf968b7f | Michael Hanselmann | |
267 | d50a2223 | Michael Hanselmann | uri_dups = utils.FindDuplicates(used_uris) |
268 | d50a2223 | Michael Hanselmann | self.failIf(uri_dups,
|
269 | d50a2223 | Michael Hanselmann | msg=("URIs matched by more than one resource: %s" %
|
270 | d50a2223 | Michael Hanselmann | utils.CommaJoin(uri_dups))) |
271 | d50a2223 | Michael Hanselmann | |
272 | 8497c267 | Michael Hanselmann | self._FindRapiMissing(resources.values())
|
273 | 8497c267 | Michael Hanselmann | self._CheckTagHandlers(resources.values())
|
274 | 8497c267 | Michael Hanselmann | |
275 | 8497c267 | Michael Hanselmann | def _FindRapiMissing(self, handlers): |
276 | 8497c267 | Michael Hanselmann | used = frozenset(itertools.chain(*map(baserlib.GetResourceOpcodes, |
277 | 8497c267 | Michael Hanselmann | handlers))) |
278 | 8497c267 | Michael Hanselmann | |
279 | 8497c267 | Michael Hanselmann | unexpected = used & RAPI_OPCODE_EXCLUDE |
280 | 8497c267 | Michael Hanselmann | self.assertFalse(unexpected,
|
281 | 8497c267 | Michael Hanselmann | msg=("Found RAPI resources for excluded opcodes: %s" %
|
282 | 8497c267 | Michael Hanselmann | utils.CommaJoin(_GetOpIds(unexpected)))) |
283 | 8497c267 | Michael Hanselmann | |
284 | 8497c267 | Michael Hanselmann | missing = (frozenset(opcodes.OP_MAPPING.values()) - used -
|
285 | 8497c267 | Michael Hanselmann | RAPI_OPCODE_EXCLUDE) |
286 | 8497c267 | Michael Hanselmann | self.assertFalse(missing,
|
287 | 8497c267 | Michael Hanselmann | msg=("Missing RAPI resources for opcodes: %s" %
|
288 | 8497c267 | Michael Hanselmann | utils.CommaJoin(_GetOpIds(missing)))) |
289 | 8497c267 | Michael Hanselmann | |
290 | 8497c267 | Michael Hanselmann | def _CheckTagHandlers(self, handlers): |
291 | 8497c267 | Michael Hanselmann | tag_handlers = filter(lambda x: issubclass(x, rlib2._R_Tags), handlers) |
292 | 8497c267 | Michael Hanselmann | self.assertEqual(frozenset(map(operator.attrgetter("TAG_LEVEL"), |
293 | 8497c267 | Michael Hanselmann | tag_handlers)), |
294 | 8497c267 | Michael Hanselmann | constants.VALID_TAG_TYPES) |
295 | 8497c267 | Michael Hanselmann | |
296 | 8497c267 | Michael Hanselmann | |
297 | 8497c267 | Michael Hanselmann | def _GetOpIds(ops): |
298 | 8497c267 | Michael Hanselmann | """Returns C{OP_ID} for all opcodes in passed sequence.
|
299 | 8497c267 | Michael Hanselmann |
|
300 | 8497c267 | Michael Hanselmann | """
|
301 | 8497c267 | Michael Hanselmann | return sorted(opcls.OP_ID for opcls in ops) |
302 | 8497c267 | Michael Hanselmann | |
303 | bf968b7f | Michael Hanselmann | |
304 | 36bf7973 | Michael Hanselmann | class TestManpages(unittest.TestCase): |
305 | 36bf7973 | Michael Hanselmann | """Manpage tests"""
|
306 | 36bf7973 | Michael Hanselmann | |
307 | 36bf7973 | Michael Hanselmann | @staticmethod
|
308 | 36bf7973 | Michael Hanselmann | def _ReadManFile(name): |
309 | 6be8e2bf | Iustin Pop | return utils.ReadFile("%s/man/%s.rst" % |
310 | 36bf7973 | Michael Hanselmann | (testutils.GetSourceDir(), name)) |
311 | 36bf7973 | Michael Hanselmann | |
312 | 36bf7973 | Michael Hanselmann | @staticmethod
|
313 | 36bf7973 | Michael Hanselmann | def _LoadScript(name): |
314 | e948770c | Michael Hanselmann | return build.LoadModule("scripts/%s" % name) |
315 | 36bf7973 | Michael Hanselmann | |
316 | 36bf7973 | Michael Hanselmann | def test(self): |
317 | 36bf7973 | Michael Hanselmann | for script in _autoconf.GNT_SCRIPTS: |
318 | 36bf7973 | Michael Hanselmann | self._CheckManpage(script,
|
319 | 36bf7973 | Michael Hanselmann | self._ReadManFile(script),
|
320 | 36bf7973 | Michael Hanselmann | self._LoadScript(script).commands.keys())
|
321 | 36bf7973 | Michael Hanselmann | |
322 | 36bf7973 | Michael Hanselmann | def _CheckManpage(self, script, mantext, commands): |
323 | 36bf7973 | Michael Hanselmann | missing = [] |
324 | 36bf7973 | Michael Hanselmann | |
325 | 36bf7973 | Michael Hanselmann | for cmd in commands: |
326 | 6be8e2bf | Iustin Pop | pattern = r"^(\| )?\*\*%s\*\*" % re.escape(cmd)
|
327 | 6be8e2bf | Iustin Pop | if not re.findall(pattern, mantext, re.DOTALL | re.MULTILINE): |
328 | 36bf7973 | Michael Hanselmann | missing.append(cmd) |
329 | 36bf7973 | Michael Hanselmann | |
330 | 36bf7973 | Michael Hanselmann | self.failIf(missing,
|
331 | 36bf7973 | Michael Hanselmann | msg=("Manpage for '%s' missing documentation for %s" %
|
332 | ab3e6da8 | Iustin Pop | (script, utils.CommaJoin(missing)))) |
333 | 36bf7973 | Michael Hanselmann | |
334 | 36bf7973 | Michael Hanselmann | |
335 | 3f991867 | Michael Hanselmann | if __name__ == "__main__": |
336 | 25231ec5 | Michael Hanselmann | testutils.GanetiTestProgram() |