Statistics
| Branch: | Tag: | Revision:

root / test / docs_unittest.py @ b6fa9a44

History | View | Annotate | Download (6.1 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 3f991867 Michael Hanselmann
27 36bf7973 Michael Hanselmann
from ganeti import _autoconf
28 3f991867 Michael Hanselmann
from ganeti import utils
29 3f991867 Michael Hanselmann
from ganeti import cmdlib
30 e948770c Michael Hanselmann
from ganeti import build
31 3af47e13 Michael Hanselmann
from ganeti import compat
32 bf968b7f Michael Hanselmann
from ganeti.rapi import connector
33 3f991867 Michael Hanselmann
34 3f991867 Michael Hanselmann
import testutils
35 3f991867 Michael Hanselmann
36 3f991867 Michael Hanselmann
37 3af47e13 Michael Hanselmann
VALID_URI_RE = re.compile(r"^[-/a-z0-9]*$")
38 3af47e13 Michael Hanselmann
39 3af47e13 Michael Hanselmann
40 3f991867 Michael Hanselmann
class TestDocs(unittest.TestCase):
41 3f991867 Michael Hanselmann
  """Documentation tests"""
42 3f991867 Michael Hanselmann
43 3f991867 Michael Hanselmann
  @staticmethod
44 3f991867 Michael Hanselmann
  def _ReadDocFile(filename):
45 3f991867 Michael Hanselmann
    return utils.ReadFile("%s/doc/%s" %
46 3f991867 Michael Hanselmann
                          (testutils.GetSourceDir(), filename))
47 3f991867 Michael Hanselmann
48 3f991867 Michael Hanselmann
  def testHookDocs(self):
49 3f991867 Michael Hanselmann
    """Check whether all hooks are documented.
50 3f991867 Michael Hanselmann

51 3f991867 Michael Hanselmann
    """
52 3f991867 Michael Hanselmann
    hooksdoc = self._ReadDocFile("hooks.rst")
53 3f991867 Michael Hanselmann
54 3f991867 Michael Hanselmann
    for name in dir(cmdlib):
55 3f991867 Michael Hanselmann
      obj = getattr(cmdlib, name)
56 3f991867 Michael Hanselmann
57 3f991867 Michael Hanselmann
      if (isinstance(obj, type) and
58 3f991867 Michael Hanselmann
          issubclass(obj, cmdlib.LogicalUnit) and
59 3f991867 Michael Hanselmann
          hasattr(obj, "HPATH")):
60 3f991867 Michael Hanselmann
        self._CheckHook(name, obj, hooksdoc)
61 3f991867 Michael Hanselmann
62 3f991867 Michael Hanselmann
  def _CheckHook(self, name, lucls, hooksdoc):
63 3f991867 Michael Hanselmann
    if lucls.HTYPE is None:
64 3f991867 Michael Hanselmann
      return
65 3f991867 Michael Hanselmann
66 3f991867 Michael Hanselmann
    # TODO: Improve this test (e.g. find hooks documented but no longer
67 3f991867 Michael Hanselmann
    # existing)
68 3f991867 Michael Hanselmann
69 3f991867 Michael Hanselmann
    pattern = r"^:directory:\s*%s\s*$" % re.escape(lucls.HPATH)
70 3f991867 Michael Hanselmann
71 3f991867 Michael Hanselmann
    self.assert_(re.findall(pattern, hooksdoc, re.M),
72 3f991867 Michael Hanselmann
                 msg=("Missing documentation for hook %s/%s" %
73 3f991867 Michael Hanselmann
                      (lucls.HTYPE, lucls.HPATH)))
74 3f991867 Michael Hanselmann
75 b58a4d16 Michael Hanselmann
  def _CheckRapiResource(self, uri, fixup, handler):
76 b58a4d16 Michael Hanselmann
    docline = "%s resource." % uri
77 b58a4d16 Michael Hanselmann
    self.assertEqual(handler.__doc__.splitlines()[0].strip(), docline,
78 b58a4d16 Michael Hanselmann
                     msg=("First line of %r's docstring is not %r" %
79 b58a4d16 Michael Hanselmann
                          (handler, docline)))
80 b58a4d16 Michael Hanselmann
81 3af47e13 Michael Hanselmann
    # Apply fixes before testing
82 3af47e13 Michael Hanselmann
    for (rx, value) in fixup.items():
83 3af47e13 Michael Hanselmann
      uri = rx.sub(value, uri)
84 3af47e13 Michael Hanselmann
85 3af47e13 Michael Hanselmann
    self.assertTrue(VALID_URI_RE.match(uri), msg="Invalid URI %r" % uri)
86 3f991867 Michael Hanselmann
87 bf968b7f Michael Hanselmann
  def testRapiDocs(self):
88 bf968b7f Michael Hanselmann
    """Check whether all RAPI resources are documented.
89 bf968b7f Michael Hanselmann

90 bf968b7f Michael Hanselmann
    """
91 bf968b7f Michael Hanselmann
    rapidoc = self._ReadDocFile("rapi.rst")
92 bf968b7f Michael Hanselmann
93 3af47e13 Michael Hanselmann
    node_name = re.escape("[node_name]")
94 3af47e13 Michael Hanselmann
    instance_name = re.escape("[instance_name]")
95 3af47e13 Michael Hanselmann
    group_name = re.escape("[group_name]")
96 3af47e13 Michael Hanselmann
    job_id = re.escape("[job_id]")
97 3af47e13 Michael Hanselmann
    disk_index = re.escape("[disk_index]")
98 3af47e13 Michael Hanselmann
99 3af47e13 Michael Hanselmann
    resources = connector.GetHandlers(node_name, instance_name, group_name,
100 3af47e13 Michael Hanselmann
                                      job_id, disk_index)
101 3af47e13 Michael Hanselmann
102 d50a2223 Michael Hanselmann
    handler_dups = utils.FindDuplicates(resources.values())
103 d50a2223 Michael Hanselmann
    self.assertFalse(handler_dups,
104 d50a2223 Michael Hanselmann
                     msg=("Resource handlers used more than once: %r" %
105 d50a2223 Michael Hanselmann
                          handler_dups))
106 d50a2223 Michael Hanselmann
107 3af47e13 Michael Hanselmann
    uri_check_fixup = {
108 3af47e13 Michael Hanselmann
      re.compile(node_name): "node1examplecom",
109 3af47e13 Michael Hanselmann
      re.compile(instance_name): "inst1examplecom",
110 3af47e13 Michael Hanselmann
      re.compile(group_name): "group4440",
111 3af47e13 Michael Hanselmann
      re.compile(job_id): "9409",
112 3af47e13 Michael Hanselmann
      re.compile(disk_index): "123",
113 3af47e13 Michael Hanselmann
      }
114 bf968b7f Michael Hanselmann
115 3af47e13 Michael Hanselmann
    assert compat.all(VALID_URI_RE.match(value)
116 3af47e13 Michael Hanselmann
                      for value in uri_check_fixup.values()), \
117 3af47e13 Michael Hanselmann
           "Fixup values must be valid URIs, too"
118 bf968b7f Michael Hanselmann
119 bf968b7f Michael Hanselmann
    titles = []
120 bf968b7f Michael Hanselmann
121 bf968b7f Michael Hanselmann
    prevline = None
122 bf968b7f Michael Hanselmann
    for line in rapidoc.splitlines():
123 bf968b7f Michael Hanselmann
      if re.match(r"^\++$", line):
124 bf968b7f Michael Hanselmann
        titles.append(prevline)
125 bf968b7f Michael Hanselmann
126 bf968b7f Michael Hanselmann
      prevline = line
127 bf968b7f Michael Hanselmann
128 2c0be3d0 Michael Hanselmann
    prefix_exception = frozenset(["/", "/version", "/2"])
129 2c0be3d0 Michael Hanselmann
130 bf968b7f Michael Hanselmann
    undocumented = []
131 d50a2223 Michael Hanselmann
    used_uris = []
132 bf968b7f Michael Hanselmann
133 bf968b7f Michael Hanselmann
    for key, handler in resources.iteritems():
134 bf968b7f Michael Hanselmann
      # Regex objects
135 bf968b7f Michael Hanselmann
      if hasattr(key, "match"):
136 2c0be3d0 Michael Hanselmann
        self.assert_(key.pattern.startswith("^/2/"),
137 2c0be3d0 Michael Hanselmann
                     msg="Pattern %r does not start with '^/2/'" % key.pattern)
138 3af47e13 Michael Hanselmann
        self.assertEqual(key.pattern[-1], "$")
139 2c0be3d0 Michael Hanselmann
140 bf968b7f Michael Hanselmann
        found = False
141 bf968b7f Michael Hanselmann
        for title in titles:
142 3af47e13 Michael Hanselmann
          if title.startswith("``") and title.endswith("``"):
143 3af47e13 Michael Hanselmann
            uri = title[2:-2]
144 3af47e13 Michael Hanselmann
            if key.match(uri):
145 b58a4d16 Michael Hanselmann
              self._CheckRapiResource(uri, uri_check_fixup, handler)
146 d50a2223 Michael Hanselmann
              used_uris.append(uri)
147 3af47e13 Michael Hanselmann
              found = True
148 3af47e13 Michael Hanselmann
              break
149 bf968b7f Michael Hanselmann
150 bf968b7f Michael Hanselmann
        if not found:
151 bf968b7f Michael Hanselmann
          # TODO: Find better way of identifying resource
152 2c0be3d0 Michael Hanselmann
          undocumented.append(key.pattern)
153 2c0be3d0 Michael Hanselmann
154 2c0be3d0 Michael Hanselmann
      else:
155 2c0be3d0 Michael Hanselmann
        self.assert_(key.startswith("/2/") or key in prefix_exception,
156 2c0be3d0 Michael Hanselmann
                     msg="Path %r does not start with '/2/'" % key)
157 bf968b7f Michael Hanselmann
158 3af47e13 Michael Hanselmann
        if ("``%s``" % key) in titles:
159 b58a4d16 Michael Hanselmann
          self._CheckRapiResource(key, {}, handler)
160 d50a2223 Michael Hanselmann
          used_uris.append(key)
161 3af47e13 Michael Hanselmann
        else:
162 2c0be3d0 Michael Hanselmann
          undocumented.append(key)
163 bf968b7f Michael Hanselmann
164 bf968b7f Michael Hanselmann
    self.failIf(undocumented,
165 bf968b7f Michael Hanselmann
                msg=("Missing RAPI resource documentation for %s" %
166 ab3e6da8 Iustin Pop
                     utils.CommaJoin(undocumented)))
167 bf968b7f Michael Hanselmann
168 d50a2223 Michael Hanselmann
    uri_dups = utils.FindDuplicates(used_uris)
169 d50a2223 Michael Hanselmann
    self.failIf(uri_dups,
170 d50a2223 Michael Hanselmann
                msg=("URIs matched by more than one resource: %s" %
171 d50a2223 Michael Hanselmann
                     utils.CommaJoin(uri_dups)))
172 d50a2223 Michael Hanselmann
173 bf968b7f Michael Hanselmann
174 36bf7973 Michael Hanselmann
class TestManpages(unittest.TestCase):
175 36bf7973 Michael Hanselmann
  """Manpage tests"""
176 36bf7973 Michael Hanselmann
177 36bf7973 Michael Hanselmann
  @staticmethod
178 36bf7973 Michael Hanselmann
  def _ReadManFile(name):
179 6be8e2bf Iustin Pop
    return utils.ReadFile("%s/man/%s.rst" %
180 36bf7973 Michael Hanselmann
                          (testutils.GetSourceDir(), name))
181 36bf7973 Michael Hanselmann
182 36bf7973 Michael Hanselmann
  @staticmethod
183 36bf7973 Michael Hanselmann
  def _LoadScript(name):
184 e948770c Michael Hanselmann
    return build.LoadModule("scripts/%s" % name)
185 36bf7973 Michael Hanselmann
186 36bf7973 Michael Hanselmann
  def test(self):
187 36bf7973 Michael Hanselmann
    for script in _autoconf.GNT_SCRIPTS:
188 36bf7973 Michael Hanselmann
      self._CheckManpage(script,
189 36bf7973 Michael Hanselmann
                         self._ReadManFile(script),
190 36bf7973 Michael Hanselmann
                         self._LoadScript(script).commands.keys())
191 36bf7973 Michael Hanselmann
192 36bf7973 Michael Hanselmann
  def _CheckManpage(self, script, mantext, commands):
193 36bf7973 Michael Hanselmann
    missing = []
194 36bf7973 Michael Hanselmann
195 36bf7973 Michael Hanselmann
    for cmd in commands:
196 6be8e2bf Iustin Pop
      pattern = r"^(\| )?\*\*%s\*\*" % re.escape(cmd)
197 6be8e2bf Iustin Pop
      if not re.findall(pattern, mantext, re.DOTALL | re.MULTILINE):
198 36bf7973 Michael Hanselmann
        missing.append(cmd)
199 36bf7973 Michael Hanselmann
200 36bf7973 Michael Hanselmann
    self.failIf(missing,
201 36bf7973 Michael Hanselmann
                msg=("Manpage for '%s' missing documentation for %s" %
202 ab3e6da8 Iustin Pop
                     (script, utils.CommaJoin(missing))))
203 36bf7973 Michael Hanselmann
204 36bf7973 Michael Hanselmann
205 3f991867 Michael Hanselmann
if __name__ == "__main__":
206 25231ec5 Michael Hanselmann
  testutils.GanetiTestProgram()