+ handler_dups = utils.FindDuplicates(resources.values())
+ self.assertFalse(handler_dups,
+ msg=("Resource handlers used more than once: %r" %
+ handler_dups))
+
+ uri_check_fixup = {
+ re.compile(node_name): "node1examplecom",
+ re.compile(instance_name): "inst1examplecom",
+ re.compile(group_name): "group4440",
+ re.compile(job_id): "9409",
+ re.compile(disk_index): "123",
+ re.compile(query_res): "lock",
+ }
+
+ assert compat.all(VALID_URI_RE.match(value)
+ for value in uri_check_fixup.values()), \
+ "Fixup values must be valid URIs, too"
+
+ titles = []
+
+ prevline = None
+ for line in rapidoc.splitlines():
+ if re.match(r"^\++$", line):
+ titles.append(prevline)
+
+ prevline = line
+
+ prefix_exception = frozenset(["/", "/version", "/2"])
+
+ undocumented = []
+ used_uris = []
+
+ for key, handler in resources.iteritems():
+ # Regex objects
+ if hasattr(key, "match"):
+ self.assert_(key.pattern.startswith("^/2/"),
+ msg="Pattern %r does not start with '^/2/'" % key.pattern)
+ self.assertEqual(key.pattern[-1], "$")
+
+ found = False
+ for title in titles:
+ if title.startswith("``") and title.endswith("``"):
+ uri = title[2:-2]
+ if key.match(uri):
+ self._CheckRapiResource(uri, uri_check_fixup, handler)
+ used_uris.append(uri)
+ found = True
+ break
+
+ if not found:
+ # TODO: Find better way of identifying resource
+ undocumented.append(key.pattern)
+
+ else:
+ self.assert_(key.startswith("/2/") or key in prefix_exception,
+ msg="Path %r does not start with '/2/'" % key)
+
+ if ("``%s``" % key) in titles:
+ self._CheckRapiResource(key, {}, handler)
+ used_uris.append(key)
+ else:
+ undocumented.append(key)
+
+ self.failIf(undocumented,
+ msg=("Missing RAPI resource documentation for %s" %
+ utils.CommaJoin(undocumented)))
+
+ uri_dups = utils.FindDuplicates(used_uris)
+ self.failIf(uri_dups,
+ msg=("URIs matched by more than one resource: %s" %
+ utils.CommaJoin(uri_dups)))
+
+ self._FindRapiMissing(resources.values())
+ self._CheckTagHandlers(resources.values())
+
+ def _FindRapiMissing(self, handlers):
+ used = frozenset(itertools.chain(*map(baserlib.GetResourceOpcodes,
+ handlers)))
+
+ unexpected = used & RAPI_OPCODE_EXCLUDE
+ self.assertFalse(unexpected,
+ msg=("Found RAPI resources for excluded opcodes: %s" %
+ utils.CommaJoin(_GetOpIds(unexpected))))
+
+ missing = (frozenset(opcodes.OP_MAPPING.values()) - used -
+ RAPI_OPCODE_EXCLUDE)
+ self.assertFalse(missing,
+ msg=("Missing RAPI resources for opcodes: %s" %
+ utils.CommaJoin(_GetOpIds(missing))))
+
+ def _CheckTagHandlers(self, handlers):
+ tag_handlers = filter(lambda x: issubclass(x, rlib2._R_Tags), handlers)
+ self.assertEqual(frozenset(map(operator.attrgetter("TAG_LEVEL"),
+ tag_handlers)),
+ constants.VALID_TAG_TYPES)
+
+
+def _GetOpIds(ops):
+ """Returns C{OP_ID} for all opcodes in passed sequence.
+
+ """
+ return sorted(opcls.OP_ID for opcls in ops)
+
+
+class TestManpages(unittest.TestCase):
+ """Manpage tests"""
+
+ @staticmethod
+ def _ReadManFile(name):
+ return utils.ReadFile("%s/man/%s.rst" %
+ (testutils.GetSourceDir(), name))
+
+ @staticmethod
+ def _LoadScript(name):
+ return build.LoadModule("scripts/%s" % name)