4 # Copyright (C) 2009 Google Inc.
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 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # 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, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 """Script for unittesting documentation"""
27 from ganeti import _autoconf
28 from ganeti import utils
29 from ganeti import cmdlib
30 from ganeti import build
31 from ganeti import compat
32 from ganeti.rapi import connector
37 VALID_URI_RE = re.compile(r"^[-/a-z0-9]*$")
40 class TestDocs(unittest.TestCase):
41 """Documentation tests"""
44 def _ReadDocFile(filename):
45 return utils.ReadFile("%s/doc/%s" %
46 (testutils.GetSourceDir(), filename))
48 def testHookDocs(self):
49 """Check whether all hooks are documented.
52 hooksdoc = self._ReadDocFile("hooks.rst")
54 for name in dir(cmdlib):
55 obj = getattr(cmdlib, name)
57 if (isinstance(obj, type) and
58 issubclass(obj, cmdlib.LogicalUnit) and
59 hasattr(obj, "HPATH")):
60 self._CheckHook(name, obj, hooksdoc)
62 def _CheckHook(self, name, lucls, hooksdoc):
63 if lucls.HTYPE is None:
66 # TODO: Improve this test (e.g. find hooks documented but no longer
69 pattern = r"^:directory:\s*%s\s*$" % re.escape(lucls.HPATH)
71 self.assert_(re.findall(pattern, hooksdoc, re.M),
72 msg=("Missing documentation for hook %s/%s" %
73 (lucls.HTYPE, lucls.HPATH)))
75 def _CheckRapiResource(self, uri, fixup, handler):
76 docline = "%s resource." % uri
77 self.assertEqual(handler.__doc__.splitlines()[0].strip(), docline,
78 msg=("First line of %r's docstring is not %r" %
81 # Apply fixes before testing
82 for (rx, value) in fixup.items():
83 uri = rx.sub(value, uri)
85 self.assertTrue(VALID_URI_RE.match(uri), msg="Invalid URI %r" % uri)
87 def testRapiDocs(self):
88 """Check whether all RAPI resources are documented.
91 rapidoc = self._ReadDocFile("rapi.rst")
93 node_name = re.escape("[node_name]")
94 instance_name = re.escape("[instance_name]")
95 group_name = re.escape("[group_name]")
96 job_id = re.escape("[job_id]")
97 disk_index = re.escape("[disk_index]")
99 resources = connector.GetHandlers(node_name, instance_name, group_name,
102 handler_dups = utils.FindDuplicates(resources.values())
103 self.assertFalse(handler_dups,
104 msg=("Resource handlers used more than once: %r" %
108 re.compile(node_name): "node1examplecom",
109 re.compile(instance_name): "inst1examplecom",
110 re.compile(group_name): "group4440",
111 re.compile(job_id): "9409",
112 re.compile(disk_index): "123",
115 assert compat.all(VALID_URI_RE.match(value)
116 for value in uri_check_fixup.values()), \
117 "Fixup values must be valid URIs, too"
122 for line in rapidoc.splitlines():
123 if re.match(r"^\++$", line):
124 titles.append(prevline)
128 prefix_exception = frozenset(["/", "/version", "/2"])
133 for key, handler in resources.iteritems():
135 if hasattr(key, "match"):
136 self.assert_(key.pattern.startswith("^/2/"),
137 msg="Pattern %r does not start with '^/2/'" % key.pattern)
138 self.assertEqual(key.pattern[-1], "$")
142 if title.startswith("``") and title.endswith("``"):
145 self._CheckRapiResource(uri, uri_check_fixup, handler)
146 used_uris.append(uri)
151 # TODO: Find better way of identifying resource
152 undocumented.append(key.pattern)
155 self.assert_(key.startswith("/2/") or key in prefix_exception,
156 msg="Path %r does not start with '/2/'" % key)
158 if ("``%s``" % key) in titles:
159 self._CheckRapiResource(key, {}, handler)
160 used_uris.append(key)
162 undocumented.append(key)
164 self.failIf(undocumented,
165 msg=("Missing RAPI resource documentation for %s" %
166 utils.CommaJoin(undocumented)))
168 uri_dups = utils.FindDuplicates(used_uris)
169 self.failIf(uri_dups,
170 msg=("URIs matched by more than one resource: %s" %
171 utils.CommaJoin(uri_dups)))
174 class TestManpages(unittest.TestCase):
178 def _ReadManFile(name):
179 return utils.ReadFile("%s/man/%s.rst" %
180 (testutils.GetSourceDir(), name))
183 def _LoadScript(name):
184 return build.LoadModule("scripts/%s" % name)
187 for script in _autoconf.GNT_SCRIPTS:
188 self._CheckManpage(script,
189 self._ReadManFile(script),
190 self._LoadScript(script).commands.keys())
192 def _CheckManpage(self, script, mantext, commands):
196 pattern = r"^(\| )?\*\*%s\*\*" % re.escape(cmd)
197 if not re.findall(pattern, mantext, re.DOTALL | re.MULTILINE):
201 msg=("Manpage for '%s' missing documentation for %s" %
202 (script, utils.CommaJoin(missing))))
205 if __name__ == "__main__":
206 testutils.GanetiTestProgram()