diff --git a/doc/specs/README.rst b/doc/specs/README.rst new file mode 100644 index 0000000..3c22dd3 --- /dev/null +++ b/doc/specs/README.rst @@ -0,0 +1,11 @@ +OSProfiler Specs +================ + +Specs are detailed description of proposed changes in project. Usually they +answer on what, why, how to change in project and who is going to work on +change. + +This directory contains 2 subdirectories: + +- in-progress - These specs are approved, but they are not implemented yet +- implemented - Implemented specs archive diff --git a/doc/specs/implemented/README.rst b/doc/specs/implemented/README.rst new file mode 100644 index 0000000..6d86ff8 --- /dev/null +++ b/doc/specs/implemented/README.rst @@ -0,0 +1,8 @@ +OSprofiler Implemented Specs +============================ + +Specs are detailed description of proposed changes in project. Usually they +answer on what, why, how to change in project and who is going to work on +change. + +This directory contains files with implemented specs, 1 file is 1 spec. diff --git a/doc/specs/in-progress/README.rst b/doc/specs/in-progress/README.rst new file mode 100644 index 0000000..b17b7d3 --- /dev/null +++ b/doc/specs/in-progress/README.rst @@ -0,0 +1,9 @@ +OSprofiler In-Progress Specs +============================ + +Specs are detailed description of proposed changes in project. Usually they +answer on what, why, how to change in project and who is going to work on +change. + +This directory contains files with accepted by not implemented specs, +1 file is 1 spec. diff --git a/doc/specs/template.rst b/doc/specs/template.rst new file mode 100644 index 0000000..a202654 --- /dev/null +++ b/doc/specs/template.rst @@ -0,0 +1,78 @@ +.. + This work is licensed under a Creative Commons Attribution 3.0 Unported + License. + + http://creativecommons.org/licenses/by/3.0/legalcode + +.. + This template should be in ReSTructured text. The filename in the git + repository should match the launchpad URL, for example a URL of + https://blueprints.launchpad.net/heat/+spec/awesome-thing should be named + awesome-thing.rst . Please do not delete any of the sections in this + template. If you have nothing to say for a whole section, just write: None + For help with syntax, see http://sphinx-doc.org/rest.html + To test out your formatting, see http://www.tele3.cz/jbar/rest/rest.html + +======================= + The title of your Spec +======================= + +Introduction paragraph -- why are we doing anything? + +Problem description +=================== + +A detailed description of the problem. + +Proposed change +=============== + +Here is where you cover the change you propose to make in detail. How do you +propose to solve this problem? + +If this is one part of a larger effort make it clear where this piece ends. In +other words, what's the scope of this effort? + +Include where in the heat tree hierarchy this will reside. + +Alternatives +------------ + +This is an optional section, where it does apply we'd just like a demonstration +that some thought has been put into why the proposed approach is the best one. + +Implementation +============== + +Assignee(s) +----------- + +Who is leading the writing of the code? Or is this a blueprint where you're +throwing it out there to see who picks it up? + +If more than one person is working on the implementation, please designate the +primary author and contact. + +Primary assignee: + + +Can optionally can list additional ids if they intend on doing +substantial implementation work on this blueprint. + +Work Items +---------- + +Work items or tasks -- break the feature up into the things that need to be +done to implement it. Those parts might end up being done by different people, +but we're mostly trying to understand the timeline for implementation. + + +Dependencies +============ + +- Include specific references to specs and/or blueprints in heat, or in other + projects, that this one either depends on or is related to. + +- Does this feature require any new library dependencies or code otherwise not + included in OpenStack? Or does it depend on a specific version of library? + diff --git a/tests/doc/__init__.py b/tests/doc/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/doc/test_specs.py b/tests/doc/test_specs.py new file mode 100644 index 0000000..39e7629 --- /dev/null +++ b/tests/doc/test_specs.py @@ -0,0 +1,117 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import glob +import os +import re + +import docutils.core + +from tests import test + + +class TitlesTestCase(test.TestCase): + + specs_path = os.path.join( + os.path.dirname(__file__), os.pardir, os.pardir, "doc", "specs") + + def _get_title(self, section_tree): + section = {"subtitles": []} + for node in section_tree: + if node.tagname == "title": + section["name"] = node.rawsource + elif node.tagname == "section": + subsection = self._get_title(node) + section["subtitles"].append(subsection["name"]) + return section + + def _get_titles(self, spec): + titles = {} + for node in spec: + if node.tagname == "section": + # Note subsection subtitles are thrown away + section = self._get_title(node) + titles[section["name"]] = section["subtitles"] + return titles + + def _check_titles(self, filename, expect, actual): + missing_sections = [x for x in expect.keys() if x not in actual.keys()] + extra_sections = [x for x in actual.keys() if x not in expect.keys()] + + msgs = [] + if len(missing_sections) > 0: + msgs.append("Missing sections: %s" % missing_sections) + if len(extra_sections) > 0: + msgs.append("Extra sections: %s" % extra_sections) + + for section in expect.keys(): + missing_subsections = [x for x in expect[section] + if x not in actual.get(section, {})] + # extra subsections are allowed + if len(missing_subsections) > 0: + msgs.append("Section '%s' is missing subsections: %s" + % (section, missing_subsections)) + + if len(msgs) > 0: + self.fail("While checking '%s':\n %s" + % (filename, "\n ".join(msgs))) + + def _check_lines_wrapping(self, tpl, raw): + for i, line in enumerate(raw.split("\n")): + if "http://" in line or "https://" in line: + continue + self.assertTrue( + len(line) < 80, + msg="%s:%d: Line limited to a maximum of 79 characters." % + (tpl, i+1)) + + def _check_no_cr(self, tpl, raw): + matches = re.findall("\r", raw) + self.assertEqual( + len(matches), 0, + "Found %s literal carriage returns in file %s" % + (len(matches), tpl)) + + def _check_trailing_spaces(self, tpl, raw): + for i, line in enumerate(raw.split("\n")): + trailing_spaces = re.findall(" +$", line) + self.assertEqual( + len(trailing_spaces), 0, + "Found trailing spaces on line %s of %s" % (i+1, tpl)) + + def test_template(self): + with open(os.path.join(self.specs_path, "template.rst")) as f: + template = f.read() + + spec = docutils.core.publish_doctree(template) + template_titles = self._get_titles(spec) + + for d in ["implemented", "in-progress"]: + spec_dir = "%s/%s" % (self.specs_path, d) + + self.assertTrue(os.path.isdir(spec_dir), + "%s is not a directory" % spec_dir) + for filename in glob.glob(spec_dir + "/*"): + if filename.endswith("README.rst"): + continue + + self.assertTrue( + filename.endswith(".rst"), + "spec's file must have .rst ext. Found: %s" % filename) + with open(filename) as f: + data = f.read() + + titles = self._get_titles(docutils.core.publish_doctree(data)) + self._check_titles(filename, template_titles, titles) + self._check_lines_wrapping(filename, data) + self._check_no_cr(filename, data) + self._check_trailing_spaces(filename, data)