Statistics
| Branch: | Tag: | Revision:

root / test / docs_unittest.py @ 7578ab0a

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