Statistics
| Branch: | Tag: | Revision:

root / test / docs_unittest.py @ b58a4d16

History | View | Annotate | Download (5.6 kB)

1
#!/usr/bin/python
2
#
3

    
4
# Copyright (C) 2009 Google Inc.
5
#
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.
10
#
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.
15
#
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
19
# 02110-1301, USA.
20

    
21

    
22
"""Script for unittesting documentation"""
23

    
24
import unittest
25
import re
26

    
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
33

    
34
import testutils
35

    
36

    
37
VALID_URI_RE = re.compile(r"^[-/a-z0-9]*$")
38

    
39

    
40
class TestDocs(unittest.TestCase):
41
  """Documentation tests"""
42

    
43
  @staticmethod
44
  def _ReadDocFile(filename):
45
    return utils.ReadFile("%s/doc/%s" %
46
                          (testutils.GetSourceDir(), filename))
47

    
48
  def testHookDocs(self):
49
    """Check whether all hooks are documented.
50

51
    """
52
    hooksdoc = self._ReadDocFile("hooks.rst")
53

    
54
    for name in dir(cmdlib):
55
      obj = getattr(cmdlib, name)
56

    
57
      if (isinstance(obj, type) and
58
          issubclass(obj, cmdlib.LogicalUnit) and
59
          hasattr(obj, "HPATH")):
60
        self._CheckHook(name, obj, hooksdoc)
61

    
62
  def _CheckHook(self, name, lucls, hooksdoc):
63
    if lucls.HTYPE is None:
64
      return
65

    
66
    # TODO: Improve this test (e.g. find hooks documented but no longer
67
    # existing)
68

    
69
    pattern = r"^:directory:\s*%s\s*$" % re.escape(lucls.HPATH)
70

    
71
    self.assert_(re.findall(pattern, hooksdoc, re.M),
72
                 msg=("Missing documentation for hook %s/%s" %
73
                      (lucls.HTYPE, lucls.HPATH)))
74

    
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" %
79
                          (handler, docline)))
80

    
81
    # Apply fixes before testing
82
    for (rx, value) in fixup.items():
83
      uri = rx.sub(value, uri)
84

    
85
    self.assertTrue(VALID_URI_RE.match(uri), msg="Invalid URI %r" % uri)
86

    
87
  def testRapiDocs(self):
88
    """Check whether all RAPI resources are documented.
89

90
    """
91
    rapidoc = self._ReadDocFile("rapi.rst")
92

    
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]")
98

    
99
    resources = connector.GetHandlers(node_name, instance_name, group_name,
100
                                      job_id, disk_index)
101

    
102
    uri_check_fixup = {
103
      re.compile(node_name): "node1examplecom",
104
      re.compile(instance_name): "inst1examplecom",
105
      re.compile(group_name): "group4440",
106
      re.compile(job_id): "9409",
107
      re.compile(disk_index): "123",
108
      }
109

    
110
    assert compat.all(VALID_URI_RE.match(value)
111
                      for value in uri_check_fixup.values()), \
112
           "Fixup values must be valid URIs, too"
113

    
114
    titles = []
115

    
116
    prevline = None
117
    for line in rapidoc.splitlines():
118
      if re.match(r"^\++$", line):
119
        titles.append(prevline)
120

    
121
      prevline = line
122

    
123
    prefix_exception = frozenset(["/", "/version", "/2"])
124

    
125
    undocumented = []
126

    
127
    for key, handler in resources.iteritems():
128
      # Regex objects
129
      if hasattr(key, "match"):
130
        self.assert_(key.pattern.startswith("^/2/"),
131
                     msg="Pattern %r does not start with '^/2/'" % key.pattern)
132
        self.assertEqual(key.pattern[-1], "$")
133

    
134
        found = False
135
        for title in titles:
136
          if title.startswith("``") and title.endswith("``"):
137
            uri = title[2:-2]
138
            if key.match(uri):
139
              self._CheckRapiResource(uri, uri_check_fixup, handler)
140
              found = True
141
              break
142

    
143
        if not found:
144
          # TODO: Find better way of identifying resource
145
          undocumented.append(key.pattern)
146

    
147
      else:
148
        self.assert_(key.startswith("/2/") or key in prefix_exception,
149
                     msg="Path %r does not start with '/2/'" % key)
150

    
151
        if ("``%s``" % key) in titles:
152
          self._CheckRapiResource(key, {}, handler)
153
        else:
154
          undocumented.append(key)
155

    
156
    self.failIf(undocumented,
157
                msg=("Missing RAPI resource documentation for %s" %
158
                     utils.CommaJoin(undocumented)))
159

    
160

    
161
class TestManpages(unittest.TestCase):
162
  """Manpage tests"""
163

    
164
  @staticmethod
165
  def _ReadManFile(name):
166
    return utils.ReadFile("%s/man/%s.rst" %
167
                          (testutils.GetSourceDir(), name))
168

    
169
  @staticmethod
170
  def _LoadScript(name):
171
    return build.LoadModule("scripts/%s" % name)
172

    
173
  def test(self):
174
    for script in _autoconf.GNT_SCRIPTS:
175
      self._CheckManpage(script,
176
                         self._ReadManFile(script),
177
                         self._LoadScript(script).commands.keys())
178

    
179
  def _CheckManpage(self, script, mantext, commands):
180
    missing = []
181

    
182
    for cmd in commands:
183
      pattern = r"^(\| )?\*\*%s\*\*" % re.escape(cmd)
184
      if not re.findall(pattern, mantext, re.DOTALL | re.MULTILINE):
185
        missing.append(cmd)
186

    
187
    self.failIf(missing,
188
                msg=("Manpage for '%s' missing documentation for %s" %
189
                     (script, utils.CommaJoin(missing))))
190

    
191

    
192
if __name__ == "__main__":
193
  testutils.GanetiTestProgram()