source: rtems-central/rtemsqual/applconfig.py @ a87154a

Last change on this file since a87154a was a87154a, checked in by Sebastian Huber <sebastian.huber@…>, on Apr 17, 2020 at 5:15:26 AM

applconfig: Add unit tests

  • Property mode set to 100644
File size: 9.5 KB
Line 
1# SPDX-License-Identifier: BSD-2-Clause
2""" Functions for application configuration documentation generation. """
3
4# Copyright (C) 2019, 2020 embedded brains GmbH (http://www.embedded-brains.de)
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions
8# are met:
9# 1. Redistributions of source code must retain the above copyright
10#    notice, this list of conditions and the following disclaimer.
11# 2. Redistributions in binary form must reproduce the above copyright
12#    notice, this list of conditions and the following disclaimer in the
13#    documentation and/or other materials provided with the distribution.
14#
15# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
19# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25# POSSIBILITY OF SUCH DAMAGE.
26
27from typing import Any, Dict, List, Optional
28
29from rtemsqual.content import SphinxContent
30from rtemsqual.items import Item, ItemCache
31
32ItemMap = Dict[str, Item]
33
34
35def _gather_groups(item: Item, groups: ItemMap) -> None:
36    for child in item.children:
37        _gather_groups(child, groups)
38    if item["type"] == "interface" and item[
39            "interface-type"] == "appl-config-group":
40        groups[item.uid] = item
41
42
43def _gather_options(item: Item, options: ItemMap) -> None:
44    for child in item.children:
45        _gather_options(child, options)
46    if item["type"] == "interface" and item[
47            "interface-type"] == "appl-config-option":
48        options[item.uid] = item
49
50
51_FEATURE = "This configuration option is a boolean feature define."
52
53_OPTION_TYPES = {
54    "feature": _FEATURE,
55    "feature-enable": _FEATURE,
56    "integer": "This configuration option is an integer define.",
57    "initializer": "This configuration option is an initializer define."
58}
59
60_OPTION_DEFAULT_CONFIG = {
61    "feature":
62    lambda item: item["appl-config-option-default"],
63    "feature-enable":
64    lambda item:
65    """If this configuration option is undefined, then the described feature is not
66enabled."""
67}
68
69
70def _generate_feature(content: SphinxContent, item: Item, option_type: str,
71                      _item_cache: ItemCache) -> None:
72    content.add_definition_item("DEFAULT CONFIGURATION:",
73                                _OPTION_DEFAULT_CONFIG[option_type](item))
74
75
76def _generate_min_max(lines: List[str], value: str, word: str) -> None:
77    lines.append("The value of this configuration option shall be "
78                 f"{word} than or equal to {value}.")
79
80
81def _generate_set(lines: List[str], values: List[Any]) -> None:
82    value_set = "{" + ", ".join([str(x) for x in values]) + "}"
83    lines.append("The value of this configuration option shall be")
84    lines.append(f"an element of {value_set}.")
85
86
87def _start_constraint_list(lines: List[str]) -> None:
88    lines.append("The value of this configuration option shall "
89                 "satisfy all of the following")
90    lines.append("constraints:")
91
92
93def _generate_item_min(lines: List[str], constraint: Dict[str, Any]) -> None:
94    if "min" in constraint:
95        value = constraint["min"]
96        lines.append("")
97        lines.append(f"* It shall be greater than or equal to {value}.")
98
99
100def _generate_item_max(lines: List[str], constraint: Dict[str, Any]) -> None:
101    if "max" in constraint:
102        value = constraint["max"]
103        lines.append("")
104        lines.append(f"* It shall be less than or equal to {value}.")
105
106
107def _generate_item_set(lines: List[str], constraint: Dict[str, Any]) -> None:
108    if "set" in constraint:
109        value_set = constraint["set"]
110        lines.append("")
111        lines.append(f"* It shall be an element of {value_set}.")
112
113
114def _generate_item_custom(lines: List[str], constraint: Dict[str,
115                                                             Any]) -> None:
116    for custom in constraint.get("custom", []):
117        lines.append("")
118        custom = custom.replace("The value of this configuration option", "It")
119        custom = custom.strip().split("\n")
120        lines.append(f"* {custom[0]}")
121        lines.extend([f"  {x}" if x else "" for x in custom[1:]])
122
123
124def _resolve_constraint_links(content: SphinxContent, constraint: Dict[str,
125                                                                       Any],
126                              item_cache: ItemCache) -> None:
127    if "links" in constraint:
128        if "custom" not in constraint:
129            constraint["custom"] = []
130        for link in reversed(constraint["links"]):
131            item = item_cache[link]
132            item.register_license_and_copyrights(content)
133            constraint["custom"].append(item["text"])
134
135
136def _generate_constraint(content: SphinxContent, item: Item,
137                         item_cache: ItemCache) -> None:
138    constraint = item["appl-config-option-constraint"]
139    count = len(constraint)
140    lines = []  # type: List[str]
141    _resolve_constraint_links(content, constraint, item_cache)
142    if count == 1:
143        if "min" in constraint:
144            _generate_min_max(lines, constraint["min"], "greater")
145        elif "max" in constraint:
146            _generate_min_max(lines, constraint["max"], "less")
147        elif "set" in constraint:
148            _generate_set(lines, constraint["set"])
149        elif "custom" in constraint:
150            if len(constraint["custom"]) == 1:
151                lines.extend(constraint["custom"][0].strip().split("\n"))
152            else:
153                _start_constraint_list(lines)
154                _generate_item_custom(lines, constraint)
155    elif count == 2 and "min" in constraint and "max" in constraint:
156        minimum = constraint["min"]
157        maximum = constraint["max"]
158        lines.append("The value of this configuration option shall be "
159                     f"greater than or equal to {minimum}")
160        lines.append(f"and less than or equal to {maximum}.")
161    else:
162        _start_constraint_list(lines)
163        _generate_item_min(lines, constraint)
164        _generate_item_max(lines, constraint)
165        _generate_item_set(lines, constraint)
166        _generate_item_custom(lines, constraint)
167    content.add_definition_item("VALUE CONSTRAINTS:", lines)
168
169
170def _generate_initializer_or_integer(content: SphinxContent, item: Item,
171                                     _option_type: str,
172                                     item_cache: ItemCache) -> None:
173    default_value = item["appl-config-option-default-value"]
174    if not isinstance(default_value, str) or " " not in default_value:
175        default_value = f"The default value is {default_value}."
176    content.add_definition_item("DEFAULT VALUE:", default_value)
177    _generate_constraint(content, item, item_cache)
178
179
180_OPTION_GENERATORS = {
181    "feature": _generate_feature,
182    "feature-enable": _generate_feature,
183    "initializer": _generate_initializer_or_integer,
184    "integer": _generate_initializer_or_integer
185}
186
187
188def _generate_notes(content: SphinxContent, notes: Optional[str]) -> None:
189    if not notes:
190        notes = "None."
191    content.add_definition_item("NOTES:", notes)
192
193
194def _generate_content(group: Item, options: ItemMap,
195                      item_cache: ItemCache) -> SphinxContent:
196    content = SphinxContent()
197    group.register_license_and_copyrights(content)
198    content.add_header(group["appl-config-group-name"], level="=")
199    content.add_blank_line()
200    content.add_lines(group["appl-config-group-description"])
201    for item in sorted(options.values(), key=lambda x: x.uid):
202        name = item["appl-config-option-name"]
203        item.register_license_and_copyrights(content)
204        content.add_index_entries([name] + item["appl-config-option-index"])
205        content.add_blank_line()
206        content.add_label(name)
207        content.add_blank_line()
208        content.add_header(name, level="-")
209        content.add_definition_item("CONSTANT:", f"``{name}``")
210        option_type = item["appl-config-option-type"]
211        content.add_definition_item("OPTION TYPE:", _OPTION_TYPES[option_type])
212        _OPTION_GENERATORS[option_type](content, item, option_type, item_cache)
213        content.add_definition_item("DESCRIPTION:",
214                                    item["appl-config-option-description"])
215        _generate_notes(content, item["appl-config-option-notes"])
216    content.add_licence_and_copyrights()
217    return content
218
219
220def generate(config: dict, item_cache: ItemCache) -> None:
221    """
222    Generates application configuration documentation sources according to the
223    configuration.
224
225    :param config: A dictionary with configuration entries.
226    :param item_cache: The specification item cache containing the application
227                       configuration groups and options.
228    """
229    groups = {}  # type: ItemMap
230    for item in item_cache.top_level.values():
231        _gather_groups(item, groups)
232
233    for group_config in config["groups"]:
234        group = groups[group_config["uid"]]
235        options = {}  # type: ItemMap
236        _gather_options(group, options)
237        content = _generate_content(group, options, item_cache)
238        content.write(group_config["target"])
Note: See TracBrowser for help on using the repository browser.