summaryrefslogtreecommitdiffstats
path: root/rtemsspec
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2020-07-15 10:04:25 +0200
committerSebastian Huber <sebastian.huber@embedded-brains.de>2020-07-15 10:12:50 +0200
commite49c7597c091559caa5b1e0f2cabe1bb64ae9cb5 (patch)
tree33978dc3fba68ec39a77855148fc7816d0b56764 /rtemsspec
parentRename "external" in "modules" (diff)
downloadrtems-central-e49c7597c091559caa5b1e0f2cabe1bb64ae9cb5.tar.bz2
Rename "rtemsqual" in "rtemsspec"
Diffstat (limited to 'rtemsspec')
-rw-r--r--rtemsspec/__init__.py42
-rw-r--r--rtemsspec/applconfig.py487
-rw-r--r--rtemsspec/build.py88
-rw-r--r--rtemsspec/content.py1017
-rw-r--r--rtemsspec/glossary.py139
-rw-r--r--rtemsspec/interface.py585
-rw-r--r--rtemsspec/interfacedoc.py200
-rw-r--r--rtemsspec/items.py511
-rw-r--r--rtemsspec/specdoc.py572
-rw-r--r--rtemsspec/specverify.py486
-rw-r--r--rtemsspec/sphinxcontent.py240
-rw-r--r--rtemsspec/tests/config/a.yml2
-rw-r--r--rtemsspec/tests/config/b.yml1
-rw-r--r--rtemsspec/tests/config/c/d.yml1
-rw-r--r--rtemsspec/tests/config/c/e.yml1
-rw-r--r--rtemsspec/tests/spec-applconfig/a.yml27
-rw-r--r--rtemsspec/tests/spec-applconfig/b.yml14
-rw-r--r--rtemsspec/tests/spec-applconfig/c.yml23
-rw-r--r--rtemsspec/tests/spec-applconfig/d.yml7
-rw-r--r--rtemsspec/tests/spec-applconfig/e.yml17
-rw-r--r--rtemsspec/tests/spec-applconfig/f.yml17
-rw-r--r--rtemsspec/tests/spec-applconfig/func.yml21
-rw-r--r--rtemsspec/tests/spec-applconfig/g.yml9
-rw-r--r--rtemsspec/tests/spec-applconfig/h.yml18
-rw-r--r--rtemsspec/tests/spec-applconfig/i.yml20
-rw-r--r--rtemsspec/tests/spec-applconfig/j.yml21
-rw-r--r--rtemsspec/tests/spec-applconfig/k.yml19
-rw-r--r--rtemsspec/tests/spec-applconfig/l.yml18
-rw-r--r--rtemsspec/tests/spec-applconfig/m.yml17
-rw-r--r--rtemsspec/tests/spec-applconfig/td.yml14
-rw-r--r--rtemsspec/tests/spec-build/bsp.yml15
-rw-r--r--rtemsspec/tests/spec-build/bsp2.yml11
-rw-r--r--rtemsspec/tests/spec-build/d.yml7
-rw-r--r--rtemsspec/tests/spec-build/g.yml7
-rw-r--r--rtemsspec/tests/spec-build/o.yml11
-rw-r--r--rtemsspec/tests/spec-build/s.yml7
-rw-r--r--rtemsspec/tests/spec-build/x.yml3
-rw-r--r--rtemsspec/tests/spec-doc/a.yml25
-rw-r--r--rtemsspec/tests/spec-doc/b.yml29
-rw-r--r--rtemsspec/tests/spec-doc/c.yml21
-rw-r--r--rtemsspec/tests/spec-doc/d.yml25
-rw-r--r--rtemsspec/tests/spec-doc/list-only.yml16
-rw-r--r--rtemsspec/tests/spec-doc/root.yml93
-rw-r--r--rtemsspec/tests/spec-glossary/doc.rst1
-rw-r--r--rtemsspec/tests/spec-glossary/g.yml7
-rw-r--r--rtemsspec/tests/spec-glossary/glossary.rst0
-rw-r--r--rtemsspec/tests/spec-glossary/glossary/t.yml12
-rw-r--r--rtemsspec/tests/spec-glossary/glossary/u.yml11
-rw-r--r--rtemsspec/tests/spec-glossary/glossary/v.yml11
-rw-r--r--rtemsspec/tests/spec-interface/asm.yml18
-rw-r--r--rtemsspec/tests/spec-interface/command-line.yml9
-rw-r--r--rtemsspec/tests/spec-interface/define-a.yml18
-rw-r--r--rtemsspec/tests/spec-interface/define-b.yml18
-rw-r--r--rtemsspec/tests/spec-interface/define-c.yml18
-rw-r--r--rtemsspec/tests/spec-interface/define.yml29
-rw-r--r--rtemsspec/tests/spec-interface/domain-abc.yml9
-rw-r--r--rtemsspec/tests/spec-interface/domain-c.yml9
-rw-r--r--rtemsspec/tests/spec-interface/domain-command-line.yml9
-rw-r--r--rtemsspec/tests/spec-interface/enum.yml22
-rw-r--r--rtemsspec/tests/spec-interface/enum2.yml20
-rw-r--r--rtemsspec/tests/spec-interface/enum3.yml18
-rw-r--r--rtemsspec/tests/spec-interface/enum4.yml14
-rw-r--r--rtemsspec/tests/spec-interface/enumerator-0.yml14
-rw-r--r--rtemsspec/tests/spec-interface/enumerator-1.yml14
-rw-r--r--rtemsspec/tests/spec-interface/enumerator-2.yml14
-rw-r--r--rtemsspec/tests/spec-interface/enumerator-a.yml14
-rw-r--r--rtemsspec/tests/spec-interface/enumerator-b.yml14
-rw-r--r--rtemsspec/tests/spec-interface/float_t.yml10
-rw-r--r--rtemsspec/tests/spec-interface/forward-decl.yml11
-rw-r--r--rtemsspec/tests/spec-interface/func.yml44
-rw-r--r--rtemsspec/tests/spec-interface/func2.yml53
-rw-r--r--rtemsspec/tests/spec-interface/func3.yml27
-rw-r--r--rtemsspec/tests/spec-interface/func4.yml30
-rw-r--r--rtemsspec/tests/spec-interface/ga.yml13
-rw-r--r--rtemsspec/tests/spec-interface/gb.yml17
-rw-r--r--rtemsspec/tests/spec-interface/gc.yml13
-rw-r--r--rtemsspec/tests/spec-interface/h.yml28
-rw-r--r--rtemsspec/tests/spec-interface/h2.yml12
-rw-r--r--rtemsspec/tests/spec-interface/h3.yml11
-rw-r--r--rtemsspec/tests/spec-interface/h4.yml11
-rw-r--r--rtemsspec/tests/spec-interface/macro.yml46
-rw-r--r--rtemsspec/tests/spec-interface/macro2.yml29
-rw-r--r--rtemsspec/tests/spec-interface/macro3.yml22
-rw-r--r--rtemsspec/tests/spec-interface/math.yml11
-rw-r--r--rtemsspec/tests/spec-interface/option.yml14
-rw-r--r--rtemsspec/tests/spec-interface/other.yml6
-rw-r--r--rtemsspec/tests/spec-interface/other2.yml3
-rw-r--r--rtemsspec/tests/spec-interface/other3.yml3
-rw-r--r--rtemsspec/tests/spec-interface/s.yml59
-rw-r--r--rtemsspec/tests/spec-interface/smp.yml10
-rw-r--r--rtemsspec/tests/spec-interface/stdint.yml11
-rw-r--r--rtemsspec/tests/spec-interface/td.yml21
-rw-r--r--rtemsspec/tests/spec-interface/td3.yml21
-rw-r--r--rtemsspec/tests/spec-interface/uint32_t.yml12
-rw-r--r--rtemsspec/tests/spec-interface/var.yml20
-rw-r--r--rtemsspec/tests/spec-item-cache/.z.yml0
-rw-r--r--rtemsspec/tests/spec-item-cache/d/c.yml13
-rw-r--r--rtemsspec/tests/spec-item-cache/empty.txt0
-rw-r--r--rtemsspec/tests/spec-item-cache/p.yml4
-rw-r--r--rtemsspec/tests/spec-sphinx/x.yml4
-rw-r--r--rtemsspec/tests/spec-sphinx/y.yml3
-rw-r--r--rtemsspec/tests/spec-validation/action2.yml146
-rw-r--r--rtemsspec/tests/spec-validation/directive.yml300
-rw-r--r--rtemsspec/tests/spec-validation/other.yml5
-rw-r--r--rtemsspec/tests/spec-validation/other2.yml3
-rw-r--r--rtemsspec/tests/spec-validation/tc.yml54
-rw-r--r--rtemsspec/tests/spec-validation/tc2.yml41
-rw-r--r--rtemsspec/tests/spec-validation/tc3.yml30
-rw-r--r--rtemsspec/tests/spec-validation/tc4.yml18
-rw-r--r--rtemsspec/tests/spec-validation/tp.yml10
-rw-r--r--rtemsspec/tests/spec-validation/ts.yml15
-rw-r--r--rtemsspec/tests/spec-verify/c1.yml12
-rw-r--r--rtemsspec/tests/spec-verify/c2.yml10
-rw-r--r--rtemsspec/tests/spec-verify/c3.yml13
-rw-r--r--rtemsspec/tests/spec-verify/c4.yml18
-rw-r--r--rtemsspec/tests/spec-verify/d.yml7
-rw-r--r--rtemsspec/tests/spec-verify/d2.yml7
-rw-r--r--rtemsspec/tests/spec-verify/d3.yml6
-rw-r--r--rtemsspec/tests/spec-verify/e.yml6
-rw-r--r--rtemsspec/tests/spec-verify/invalid.yml7
-rw-r--r--rtemsspec/tests/spec-verify/keys-at-least-one-0.yml6
-rw-r--r--rtemsspec/tests/spec-verify/keys-at-least-one-1.yml7
-rw-r--r--rtemsspec/tests/spec-verify/keys-at-least-one-2.yml8
-rw-r--r--rtemsspec/tests/spec-verify/keys-at-most-one-0.yml6
-rw-r--r--rtemsspec/tests/spec-verify/keys-at-most-one-1.yml7
-rw-r--r--rtemsspec/tests/spec-verify/keys-at-most-one-2.yml8
-rw-r--r--rtemsspec/tests/spec-verify/keys-exactly-one-0.yml6
-rw-r--r--rtemsspec/tests/spec-verify/keys-exactly-one-1.yml7
-rw-r--r--rtemsspec/tests/spec-verify/keys-exactly-one-2.yml8
-rw-r--r--rtemsspec/tests/spec-verify/notype.yml5
-rw-r--r--rtemsspec/tests/spec-verify/spec/copyright.yml21
-rw-r--r--rtemsspec/tests/spec-verify/spec/copyrights.yml16
-rw-r--r--rtemsspec/tests/spec-verify/spec/enabled-by-list.yml16
-rw-r--r--rtemsspec/tests/spec-verify/spec/enabled-by.yml49
-rw-r--r--rtemsspec/tests/spec-verify/spec/link.yml26
-rw-r--r--rtemsspec/tests/spec-verify/spec/links.yml17
-rw-r--r--rtemsspec/tests/spec-verify/spec/list-str.yml17
-rw-r--r--rtemsspec/tests/spec-verify/spec/optional-str.yml16
-rw-r--r--rtemsspec/tests/spec-verify/spec/root.yml39
-rw-r--r--rtemsspec/tests/spec-verify/spec/spdx-license-identifier.yml21
-rw-r--r--rtemsspec/tests/spec-verify/spec/spec-assert-float-list.yml16
-rw-r--r--rtemsspec/tests/spec-verify/spec/spec-assert-float.yml71
-rw-r--r--rtemsspec/tests/spec-verify/spec/spec-assert-int-list.yml16
-rw-r--r--rtemsspec/tests/spec-verify/spec/spec-assert-int.yml70
-rw-r--r--rtemsspec/tests/spec-verify/spec/spec-assert-str-list.yml16
-rw-r--r--rtemsspec/tests/spec-verify/spec/spec-assert-str.yml94
-rw-r--r--rtemsspec/tests/spec-verify/spec/spec-attribute-value.yml26
-rw-r--r--rtemsspec/tests/spec-verify/spec/spec-attributes.yml24
-rw-r--r--rtemsspec/tests/spec-verify/spec/spec-bool.yml33
-rw-r--r--rtemsspec/tests/spec-verify/spec/spec-dict.yml37
-rw-r--r--rtemsspec/tests/spec-verify/spec/spec-float.yml30
-rw-r--r--rtemsspec/tests/spec-verify/spec/spec-generic-attributes.yml33
-rw-r--r--rtemsspec/tests/spec-verify/spec/spec-info.yml46
-rw-r--r--rtemsspec/tests/spec-verify/spec/spec-int.yml30
-rw-r--r--rtemsspec/tests/spec-verify/spec/spec-list.yml26
-rw-r--r--rtemsspec/tests/spec-verify/spec/spec-mandatory-attributes.yml30
-rw-r--r--rtemsspec/tests/spec-verify/spec/spec-member.yml22
-rw-r--r--rtemsspec/tests/spec-verify/spec/spec-refinement.yml32
-rw-r--r--rtemsspec/tests/spec-verify/spec/spec-str.yml29
-rw-r--r--rtemsspec/tests/spec-verify/spec/spec.yml72
-rw-r--r--rtemsspec/tests/spec-verify/spec2/any-dict.yml17
-rw-r--r--rtemsspec/tests/spec-verify/spec2/c.yml40
-rw-r--r--rtemsspec/tests/spec-verify/spec2/d-a.yml22
-rw-r--r--rtemsspec/tests/spec-verify/spec2/d.yml21
-rw-r--r--rtemsspec/tests/spec-verify/spec2/invalid.yml24
-rw-r--r--rtemsspec/tests/spec-verify/spec2/keys-at-least-one.yml22
-rw-r--r--rtemsspec/tests/spec-verify/spec2/keys-at-most-one.yml22
-rw-r--r--rtemsspec/tests/spec-verify/spec2/keys-exactly-one.yml22
-rw-r--r--rtemsspec/tests/spec-verify/spec2/must-be-true.yml13
-rw-r--r--rtemsspec/tests/spec-verify/spec2/other-int.yml12
-rw-r--r--rtemsspec/tests/spec-verify/spec2/some-bool.yml12
-rw-r--r--rtemsspec/tests/spec-verify/spec2/some-dict.yml17
-rw-r--r--rtemsspec/tests/spec-verify/spec2/some-float.yml14
-rw-r--r--rtemsspec/tests/spec-verify/spec2/some-int.yml42
-rw-r--r--rtemsspec/tests/spec-verify/spec2/some-list.yml13
-rw-r--r--rtemsspec/tests/spec-verify/spec2/some-str.yml17
-rw-r--r--rtemsspec/tests/spec-verify/spec2/sta.yml9
-rw-r--r--rtemsspec/tests/spec-verify/spec2/str-contains.yml20
-rw-r--r--rtemsspec/tests/spec-verify/spec2/x.yml14
-rw-r--r--rtemsspec/tests/spec/build-more.yml21
-rw-r--r--rtemsspec/tests/spec/build.yml24
-rw-r--r--rtemsspec/tests/spec/constraint.yml24
-rw-r--r--rtemsspec/tests/spec/functional-more.yml21
-rw-r--r--rtemsspec/tests/spec/functional.yml24
-rw-r--r--rtemsspec/tests/spec/glossary-more.yml25
-rw-r--r--rtemsspec/tests/spec/glossary.yml24
-rw-r--r--rtemsspec/tests/spec/interface-appl-config-more.yml33
-rw-r--r--rtemsspec/tests/spec/interface-appl-config-option.yml21
-rw-r--r--rtemsspec/tests/spec/interface-more.yml81
-rw-r--r--rtemsspec/tests/spec/interface.yml24
-rw-r--r--rtemsspec/tests/spec/other.yml21
-rw-r--r--rtemsspec/tests/spec/requirement.yml24
-rw-r--r--rtemsspec/tests/spec/root.yml18
-rw-r--r--rtemsspec/tests/spec/spec.yml21
-rw-r--r--rtemsspec/tests/spec/test-case.yml21
-rw-r--r--rtemsspec/tests/spec/test-suite.yml21
-rw-r--r--rtemsspec/tests/test_applconfig.py589
-rw-r--r--rtemsspec/tests/test_build.py44
-rw-r--r--rtemsspec/tests/test_content.py276
-rw-r--r--rtemsspec/tests/test_content_c.py474
-rw-r--r--rtemsspec/tests/test_content_copyrights.py78
-rw-r--r--rtemsspec/tests/test_content_sphinx.py274
-rw-r--r--rtemsspec/tests/test_glossary.py91
-rw-r--r--rtemsspec/tests/test_interface.py354
-rw-r--r--rtemsspec/tests/test_interfacedoc.py224
-rw-r--r--rtemsspec/tests/test_items_item.py196
-rw-r--r--rtemsspec/tests/test_items_itemcache.py128
-rw-r--r--rtemsspec/tests/test_specdoc.py291
-rw-r--r--rtemsspec/tests/test_specverify.py1909
-rw-r--r--rtemsspec/tests/test_util.py45
-rw-r--r--rtemsspec/tests/test_validation.py1271
-rw-r--r--rtemsspec/tests/util.py53
-rw-r--r--rtemsspec/util.py65
-rw-r--r--rtemsspec/validation.py714
214 files changed, 15483 insertions, 0 deletions
diff --git a/rtemsspec/__init__.py b/rtemsspec/__init__.py
new file mode 100644
index 00000000..13e4cfd0
--- /dev/null
+++ b/rtemsspec/__init__.py
@@ -0,0 +1,42 @@
+# SPDX-License-Identifier: BSD-2-Clause
+""" The RTEMS pre-qualification package. """
+
+# Copyright (C) 2019, 2020 embedded brains GmbH (http://www.embedded-brains.de)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+__all__ = [
+ "applconfig", "build", "content", "glossary", "interface", "interfacedoc",
+ "items", "specdoc", "specverify", "util", "validation"
+]
+
+import rtemsspec.applconfig
+import rtemsspec.build
+import rtemsspec.content
+import rtemsspec.glossary
+import rtemsspec.interface
+import rtemsspec.interfacedoc
+import rtemsspec.items # noqa: F401
+import rtemsspec.specdoc
+import rtemsspec.specverify
+import rtemsspec.util # noqa: F401
+import rtemsspec.validation # noqa: F401
diff --git a/rtemsspec/applconfig.py b/rtemsspec/applconfig.py
new file mode 100644
index 00000000..bd08c8c7
--- /dev/null
+++ b/rtemsspec/applconfig.py
@@ -0,0 +1,487 @@
+# SPDX-License-Identifier: BSD-2-Clause
+""" Functions for application configuration documentation generation. """
+
+# Copyright (C) 2019, 2020 embedded brains GmbH (http://www.embedded-brains.de)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import copy
+from typing import Any, Dict, List, Optional
+
+from rtemsspec.content import CContent, get_value_double_colon, \
+ get_value_doxygen_function, get_value_hash
+from rtemsspec.sphinxcontent import SphinxContent, SphinxMapper
+from rtemsspec.items import EmptyItem, Item, ItemCache, ItemGetValueContext, \
+ ItemMapper
+
+ItemMap = Dict[str, Item]
+
+_FEATURE = "This configuration option is a boolean feature define."
+
+_OPTION_TYPES = {
+ "feature": _FEATURE,
+ "feature-enable": _FEATURE,
+ "integer": "This configuration option is an integer define.",
+ "initializer": "This configuration option is an initializer define."
+}
+
+_OPTION_DEFAULT_CONFIG = {
+ "feature":
+ lambda item: item["default"],
+ "feature-enable":
+ lambda item:
+ """If this configuration option is undefined, then the described feature is not
+enabled."""
+}
+
+
+class _ContentAdaptor:
+ """
+ The content adaptor provides a specialized interface to a content class.
+
+ By default, Sphinx content is generated.
+ """
+ def __init__(self, mapper: ItemMapper, content: Any) -> None:
+ self.mapper = mapper
+ self.content = content
+
+ def substitute(self, text: Optional[str]) -> str:
+ """ Substitutes the optional text using the item mapper. """
+ return self.mapper.substitute(text)
+
+ def add_group(self, name: str, description: str) -> None:
+ """ Adds an option group. """
+ self.content.add_header(name, level=2)
+ self.content.add(description)
+
+ def add_option(self, name: str, index_entries: List[str]) -> None:
+ """ Adds an option. """
+ self.content.add_index_entries([name] + index_entries)
+ self.content.add_label(name)
+ self.content.add_header(name, level=3)
+ self.content.add_definition_item("CONSTANT:", f"``{name}``")
+
+ def add_option_type(self, option_type: str) -> None:
+ """ Adds an option type. """
+ self.content.add_definition_item("OPTION TYPE:", option_type)
+
+ def add_option_default_value(self, value: str) -> None:
+ """ Adds an option default value. """
+ self.content.add_definition_item("DEFAULT VALUE:", value)
+
+ def add_option_default_config(self, config: str) -> None:
+ """ Adds an option default configuration. """
+ self.content.add_definition_item("DEFAULT CONFIGURATION:", config)
+
+ def add_option_value_constraints(self, lines: List[str]) -> None:
+ """ Adds a option value constraints. """
+ self.content.add_definition_item("VALUE CONSTRAINTS:", lines)
+
+ def add_option_description(self, description: str) -> None:
+ """ Adds a option description. """
+ self.content.add_definition_item("DESCRIPTION:", description)
+
+ def add_option_notes(self, notes: Optional[str]) -> None:
+ """ Adds option notes. """
+ if not notes:
+ notes = "None."
+ self.content.add_definition_item("NOTES:", notes)
+
+ def add_licence_and_copyrights(self) -> None:
+ """ Adds the license and copyrights. """
+ self.content.add_licence_and_copyrights()
+
+ def register_license_and_copyrights_of_item(self, item: Item) -> None:
+ """ Registers the license and copyrights of the item. """
+ self.content.register_license_and_copyrights_of_item(item)
+
+ def write(self, filename: str):
+ """ Writes the content to the file specified by the path. """
+ self.content.write(filename)
+
+
+class _SphinxContentAdaptor(_ContentAdaptor):
+ def __init__(self, mapper: ItemMapper) -> None:
+ super().__init__(mapper, SphinxContent())
+
+
+class _DoxygenContentAdaptor(_ContentAdaptor):
+ # pylint: disable=attribute-defined-outside-init
+
+ def __init__(self, mapper: ItemMapper) -> None:
+ super().__init__(mapper, CContent())
+ self._reset()
+
+ def _reset(self) -> None:
+ self._name = ""
+ self._option_type = ""
+ self._default_value = ""
+ self._default_config = ""
+ self._value_constraints = [] # type: List[str]
+ self._description = ""
+
+ def add_group(self, name: str, description: str) -> None:
+ identifier = f"RTEMSApplConfig{name.replace(' ', '')}"
+ with self.content.defgroup_block(identifier, name):
+ self.content.add("@ingroup RTEMSApplConfig")
+ self.content.doxyfy(description)
+ self.content.add("@{")
+
+ def add_option(self, name: str, _index_entries: List[str]) -> None:
+ self.content.open_doxygen_block()
+ self._name = name
+
+ def add_option_type(self, option_type: str) -> None:
+ self._option_type = option_type
+
+ def add_option_default_value(self, value: str) -> None:
+ self._default_value = value
+
+ def add_option_default_config(self, config: str) -> None:
+ self._default_config = config
+
+ def add_option_value_constraints(self, lines: List[str]) -> None:
+ self._value_constraints = lines
+
+ def add_option_description(self, description: str) -> None:
+ self._description = description
+
+ def add_option_notes(self, notes: Optional[str]) -> None:
+ self.content.add_brief_description(self._option_type)
+ self.content.doxyfy(self._description)
+ self.content.add_paragraph("Default Value", self._default_value)
+ self.content.add_paragraph("Default Configuration",
+ self._default_config)
+ self.content.add_paragraph("Value Constraints",
+ self._value_constraints)
+ self.content.add_paragraph("Notes", notes)
+ self.content.close_comment_block()
+ self.content.append(f"#define {self._name}")
+ self._reset()
+
+ def add_licence_and_copyrights(self) -> None:
+ self.content.add("/** @} */")
+
+
+def _generate_feature(content: _ContentAdaptor, item: Item,
+ option_type: str) -> None:
+ content.add_option_default_config(
+ content.substitute(_OPTION_DEFAULT_CONFIG[option_type](item)))
+
+
+def _generate_min_max(lines: List[str], value: str, word: str) -> None:
+ lines.append("The value of this configuration option shall be "
+ f"{word} than or equal to {value}.")
+
+
+def _generate_set(lines: List[str], values: List[Any]) -> None:
+ value_set = "{" + ", ".join([str(x) for x in values]) + "}"
+ lines.append("The value of this configuration option shall be")
+ lines.append(f"an element of {value_set}.")
+
+
+def _start_constraint_list(lines: List[str]) -> None:
+ lines.append("The value of this configuration option shall "
+ "satisfy all of the following")
+ lines.append("constraints:")
+
+
+def _generate_item_min(lines: List[str], constraints: Dict[str, Any]) -> None:
+ if "min" in constraints:
+ value = constraints["min"]
+ lines.append("")
+ lines.append(f"* It shall be greater than or equal to {value}.")
+
+
+def _generate_item_max(lines: List[str], constraints: Dict[str, Any]) -> None:
+ if "max" in constraints:
+ value = constraints["max"]
+ lines.append("")
+ lines.append(f"* It shall be less than or equal to {value}.")
+
+
+def _generate_item_set(lines: List[str], constraints: Dict[str, Any]) -> None:
+ if "set" in constraints:
+ value_set = constraints["set"]
+ lines.append("")
+ lines.append(f"* It shall be an element of {value_set}.")
+
+
+def _generate_item_texts(lines: List[str], constraints: Dict[str,
+ Any]) -> None:
+ for text in constraints.get("texts", []):
+ lines.append("")
+ text = text.replace("The value of this configuration option", "It")
+ text = text.strip().split("\n")
+ lines.append(f"* {text[0]}")
+ lines.extend([f" {x}" if x else "" for x in text[1:]])
+
+
+def _resolve_constraint_links(content: _ContentAdaptor, item: Item,
+ constraints: Dict[str, Any]) -> None:
+ texts = [] # type: List[str]
+ for parent in item.parents("constraint"):
+ content.register_license_and_copyrights_of_item(parent)
+ texts.append(parent["text"])
+ if texts:
+ constraints.setdefault("texts", []).extend(reversed(texts))
+
+
+def _generate_constraint(content: _ContentAdaptor, item: Item) -> None:
+ constraints = copy.deepcopy(item["constraints"])
+ _resolve_constraint_links(content, item, constraints)
+ lines = [] # type: List[str]
+ count = len(constraints)
+ if count == 1:
+ if "min" in constraints:
+ _generate_min_max(lines, constraints["min"], "greater")
+ elif "max" in constraints:
+ _generate_min_max(lines, constraints["max"], "less")
+ elif "set" in constraints:
+ _generate_set(lines, constraints["set"])
+ elif "texts" in constraints:
+ if len(constraints["texts"]) == 1:
+ lines.extend(constraints["texts"][0].strip().split("\n"))
+ else:
+ _start_constraint_list(lines)
+ _generate_item_texts(lines, constraints)
+ elif count == 2 and "min" in constraints and "max" in constraints:
+ minimum = constraints["min"]
+ maximum = constraints["max"]
+ lines.append("The value of this configuration option shall be "
+ f"greater than or equal to {minimum}")
+ lines.append(f"and less than or equal to {maximum}.")
+ else:
+ _start_constraint_list(lines)
+ _generate_item_min(lines, constraints)
+ _generate_item_max(lines, constraints)
+ _generate_item_set(lines, constraints)
+ _generate_item_texts(lines, constraints)
+ content.add_option_value_constraints(
+ [content.substitute(line) for line in lines])
+
+
+def _generate_initializer_or_integer(content: _ContentAdaptor, item: Item,
+ _option_type: str) -> None:
+ default_value = item["default-value"]
+ if not isinstance(default_value, str) or " " not in default_value:
+ default_value = f"The default value is {default_value}."
+ content.add_option_default_value(content.substitute(default_value))
+ _generate_constraint(content, item)
+
+
+_OPTION_GENERATORS = {
+ "feature": _generate_feature,
+ "feature-enable": _generate_feature,
+ "initializer": _generate_initializer_or_integer,
+ "integer": _generate_initializer_or_integer
+}
+
+
+def _generate(group: Item, options: ItemMap, content: _ContentAdaptor) -> None:
+ content.register_license_and_copyrights_of_item(group)
+ content.add_group(group["name"], content.substitute(group["description"]))
+ for item in sorted(options.values(), key=lambda x: x["name"]):
+ content.mapper.item = item
+ name = item["name"]
+ content.register_license_and_copyrights_of_item(item)
+ content.add_option(name, item["index-entries"])
+ option_type = item["appl-config-option-type"]
+ content.add_option_type(_OPTION_TYPES[option_type])
+ _OPTION_GENERATORS[option_type](content, item, option_type)
+ content.add_option_description(content.substitute(item["description"]))
+ content.add_option_notes(content.substitute(item["notes"]))
+ content.add_licence_and_copyrights()
+
+
+def _get_value_none(_ctx: ItemGetValueContext) -> Any:
+ return None
+
+
+def _sphinx_ref(ref: str) -> str:
+ return f":ref:`{ref}`"
+
+
+_PTHREAD_NAME_NP = "http://man7.org/linux/man-pages/man3/" \
+ "pthread_setname_np.3.html"
+
+_SPHINX_DOC_REFS = {
+ "config-scheduler-clustered":
+ _sphinx_ref("ConfigurationSchedulersClustered"),
+ "config-scheduler-table": _sphinx_ref("ConfigurationSchedulerTable"),
+ "config-unlimited-objects": _sphinx_ref("ConfigUnlimitedObjects"),
+ "mp-proxies": _sphinx_ref("MPCIProxies"),
+ "mrsp": _sphinx_ref("MrsP"),
+ "pthread-setname-np": f"`PTHREAD_SETNAME_NP(3) <{_PTHREAD_NAME_NP}>`_",
+ "scheduler-cbs": _sphinx_ref("SchedulerCBS"),
+ "scheduler-concepts": _sphinx_ref("SchedulingConcepts"),
+ "scheduler-edf": _sphinx_ref("SchedulerEDF"),
+ "scheduler-priority": _sphinx_ref("SchedulerPriority"),
+ "scheduler-priority-simple": _sphinx_ref("SchedulerPrioritySimple"),
+ "scheduler-smp-edf": _sphinx_ref("SchedulerSMPEDF"),
+ "scheduler-smp-priority-affinity":
+ _sphinx_ref("SchedulerSMPPriorityAffinity"),
+ "scheduler-smp-priority": _sphinx_ref("SchedulerSMPPriority"),
+ "scheduler-smp-priority-simple": _sphinx_ref("SchedulerSMPPrioritySimple"),
+ "terminate": _sphinx_ref("Terminate"),
+}
+
+
+def _get_value_sphinx_reference(ctx: ItemGetValueContext) -> Any:
+ return _SPHINX_DOC_REFS[ctx.key]
+
+
+def _get_value_sphinx_function(ctx: ItemGetValueContext) -> Any:
+ return f"``{ctx.value[ctx.key]}()``"
+
+
+def _get_value_sphinx_code(ctx: ItemGetValueContext) -> Any:
+ return f"``{ctx.value[ctx.key]}``"
+
+
+def _add_sphinx_get_values(mapper: ItemMapper) -> None:
+ for key in _SPHINX_DOC_REFS:
+ for opt in ["feature-enable", "feature", "initializer", "integer"]:
+ doc_ref = f"interface/appl-config-option/{opt}:/document-reference"
+ mapper.add_get_value(doc_ref, _get_value_none)
+ mapper.add_get_value(f"{doc_ref}/{key}",
+ _get_value_sphinx_reference)
+ mapper.add_get_value("interface/function:/name",
+ _get_value_sphinx_function)
+ mapper.add_get_value("interface/macro:/name", _get_value_sphinx_function)
+ mapper.add_get_value("interface/struct:/name", _get_value_sphinx_code)
+ mapper.add_get_value("interface/typedef:/name", _get_value_sphinx_code)
+ mapper.add_get_value("interface/union:/name", _get_value_sphinx_code)
+
+
+def _c_user_ref(ref: str, name: str) -> str:
+ c_user = "https://docs.rtems.org/branches/master/c-user/"
+ return f"<a href={c_user}{ref}>{name}</a>"
+
+
+_DOXYGEN_DOC_REFS = {
+ "config-scheduler-clustered":
+ _c_user_ref("config/scheduler-clustered.html",
+ "Clustered Scheduler Configuration"),
+ "config-scheduler-table":
+ _c_user_ref(
+ "config/scheduler-clustered.html#configuration-step-3-scheduler-table",
+ "Configuration Step 3 - Scheduler Table"),
+ "config-unlimited-objects":
+ _c_user_ref("config/intro.html#unlimited-objects", "Unlimited Objects"),
+ "mp-proxies":
+ _c_user_ref("multiprocessing.html#proxies", "Proxies"),
+ "mrsp":
+ _c_user_ref(
+ "key_concepts.html#multiprocessor-resource-sharing-protocol-mrsp",
+ "Multiprocessor Resource Sharing Protocol (MrsP)"),
+ "pthread-setname-np":
+ f"<a href={_PTHREAD_NAME_NP}>PTHREAD_SETNAME_NP(3)</a>",
+ "scheduler-cbs":
+ _c_user_ref(
+ "scheduling_concepts.html#constant-bandwidth-server-scheduling-cbs",
+ "Constant Bandwidth Server Scheduling (CBS)"),
+ "scheduler-concepts":
+ _c_user_ref("scheduling_concepts.html", "Scheduling Concepts"),
+ "scheduler-edf":
+ _c_user_ref("scheduling_concepts.html#earliest-deadline-first-scheduler",
+ "Earliest Deadline First Scheduler"),
+ "scheduler-priority":
+ _c_user_ref("scheduling_concepts.html#deterministic-priority-scheduler",
+ "Deterministic Priority Scheduler"),
+ "scheduler-priority-simple":
+ _c_user_ref("scheduling_concepts.html#simple-priority-scheduler",
+ "Simple Priority Scheduler"),
+ "scheduler-smp-edf":
+ _c_user_ref(
+ "scheduling_concepts.html#earliest-deadline-first-smp-scheduler",
+ "Earliest Deadline First SMP Scheduler"),
+ "scheduler-smp-priority-affinity":
+ _c_user_ref(
+ "scheduling_concepts.html"
+ "#arbitrary-processor-affinity-priority-smp-scheduler",
+ "Arbitrary Processor Affinity Priority SMP Scheduler"),
+ "scheduler-smp-priority":
+ _c_user_ref(
+ "scheduling_concepts.html#deterministic-priority-smp-scheduler",
+ "Deterministic Priority SMP Scheduler"),
+ "scheduler-smp-priority-simple":
+ _c_user_ref("scheduling_concepts.html#simple-priority-smp-scheduler",
+ "Simple Priority SMP Scheduler"),
+ "terminate":
+ _c_user_ref("fatal_error.html#announcing-a-fatal-error",
+ "Announcing a Fatal Error"),
+}
+
+
+def _get_value_doxygen_reference(ctx: ItemGetValueContext) -> Any:
+ return _DOXYGEN_DOC_REFS[ctx.key]
+
+
+def _add_doxygen_get_values(mapper: ItemMapper) -> None:
+ for key in _DOXYGEN_DOC_REFS:
+ for opt in ["feature-enable", "feature", "initializer", "integer"]:
+ doc_ref = f"interface/appl-config-option/{opt}:/document-reference"
+ mapper.add_get_value(doc_ref, _get_value_none)
+ mapper.add_get_value(f"{doc_ref}/{key}",
+ _get_value_doxygen_reference)
+ name = f"interface/appl-config-option/{opt}:/name"
+ mapper.add_get_value(name, get_value_hash)
+ mapper.add_get_value("interface/function:/name",
+ get_value_doxygen_function)
+ mapper.add_get_value("interface/macro:/name", get_value_doxygen_function)
+ mapper.add_get_value("interface/struct:/name", get_value_double_colon)
+ mapper.add_get_value("interface/typedef:/name", get_value_double_colon)
+ mapper.add_get_value("interface/union:/name", get_value_double_colon)
+
+
+def generate(config: dict, item_cache: ItemCache) -> None:
+ """
+ Generates application configuration documentation sources according to the
+ configuration.
+
+ :param config: A dictionary with configuration entries.
+ :param item_cache: The specification item cache containing the application
+ configuration groups and options.
+ """
+ sphinx_mapper = SphinxMapper(EmptyItem())
+ _add_sphinx_get_values(sphinx_mapper)
+ doxygen_mapper = ItemMapper(EmptyItem())
+ _add_doxygen_get_values(doxygen_mapper)
+ doxygen_content = _DoxygenContentAdaptor(doxygen_mapper)
+ with doxygen_content.content.defgroup_block(
+ "RTEMSApplConfig", "Application Configuration Options"):
+ doxygen_content.content.add("@ingroup RTEMSAPI")
+ for group_config in config["groups"]:
+ group = item_cache[group_config["uid"]]
+ assert group.type == "interface/appl-config-group"
+ options = {} # type: ItemMap
+ for child in group.children("appl-config-group-member"):
+ assert child.type.startswith("interface/appl-config-option")
+ options[child.uid] = child
+ sphinx_content = _SphinxContentAdaptor(sphinx_mapper)
+ _generate(group, options, sphinx_content)
+ sphinx_content.write(group_config["target"])
+ _generate(group, options, doxygen_content)
+ doxygen_content.content.prepend_copyrights_and_licenses()
+ doxygen_content.content.prepend_spdx_license_identifier()
+ doxygen_content.write(config["doxygen-target"])
diff --git a/rtemsspec/build.py b/rtemsspec/build.py
new file mode 100644
index 00000000..f1ca2be9
--- /dev/null
+++ b/rtemsspec/build.py
@@ -0,0 +1,88 @@
+# SPDX-License-Identifier: BSD-2-Clause
+""" This module provides support for the build specification. """
+
+# Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+from typing import Dict, List
+
+from rtemsspec.items import Item, ItemCache
+
+BSPMap = Dict[str, Dict[str, Item]]
+ItemMap = Dict[str, Item]
+
+
+def _extend_by_install_and_source(item: Item, source_files: List[str]) -> None:
+ for install in item["install"]:
+ source_files.extend(install["source"])
+ source_files.extend(item["source"])
+
+
+def _extend_by_source(item: Item, source_files: List[str]) -> None:
+ source_files.extend(item["source"])
+
+
+def _extend_by_nothing(_item: Item, _source_files: List[str]) -> None:
+ pass
+
+
+_EXTEND_SOURCE_FILES = {
+ "ada-test-program": _extend_by_nothing,
+ "bsp": _extend_by_install_and_source,
+ "config-file": _extend_by_nothing,
+ "config-header": _extend_by_nothing,
+ "test-program": _extend_by_source,
+ "group": _extend_by_nothing,
+ "library": _extend_by_install_and_source,
+ "objects": _extend_by_install_and_source,
+ "option": _extend_by_nothing,
+ "script": _extend_by_nothing,
+ "start-file": _extend_by_source,
+}
+
+
+def _gather_source_files(item: Item, enabled: List[str],
+ source_files: List[str]) -> None:
+ for parent in item.parents():
+ if parent["type"] == "build" and parent["build-type"] in [
+ "group", "objects", "start-file", "test-program"
+ ] and parent.is_enabled(enabled):
+ _gather_source_files(parent, enabled, source_files)
+ _EXTEND_SOURCE_FILES[item["build-type"]](item, source_files)
+
+
+def gather_files(config: dict, item_cache: ItemCache) -> List[str]:
+ """ Generates a list of files form the build specification. """
+ bsps = {} # type: BSPMap
+ for item in item_cache.all.values():
+ if item["type"] == "build" and item["build-type"] == "bsp":
+ arch_bsps = bsps.setdefault(item["arch"].strip(), {})
+ arch_bsps[item["bsp"].strip()] = item
+ source_files = config["sources"] # type: List[str]
+ arch = config["arch"]
+ bsp = config["bsp"]
+ enabled = [arch, arch + "/" + bsp] + config["enabled"]
+ _gather_source_files(bsps[arch][bsp], enabled, source_files)
+ for uid in config["uids"]:
+ _gather_source_files(item_cache[uid], enabled, source_files)
+ return source_files
diff --git a/rtemsspec/content.py b/rtemsspec/content.py
new file mode 100644
index 00000000..30a1a81d
--- /dev/null
+++ b/rtemsspec/content.py
@@ -0,0 +1,1017 @@
+# SPDX-License-Identifier: BSD-2-Clause
+""" This module provides classes for content generation. """
+
+# Copyright (C) 2019, 2020 embedded brains GmbH (http://www.embedded-brains.de)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+# pylint: disable=too-many-lines
+
+from contextlib import contextmanager
+import collections
+import itertools
+import os
+import re
+import sys
+import textwrap
+from typing import Any, Callable, ContextManager, Dict, Iterable, Iterator, \
+ List, NamedTuple, Optional, Set, Tuple, Union
+
+from rtemsspec.items import Item, ItemGetValueContext
+
+AddContext = Callable[["Content"], ContextManager[None]]
+GenericContent = Union[str, List[str], "Content"]
+GenericContentIterable = Union[Iterable[str], Iterable[List[str]],
+ Iterable[GenericContent]]
+
+
+class Copyright:
+ """
+ This class represents a copyright holder with its years of substantial
+ contributions.
+ """
+ def __init__(self, holder):
+ self._holder = holder
+ self._years = set()
+
+ def add_year(self, year: str):
+ """
+ Adds a year to the set of substantial contributions of this copyright
+ holder.
+ """
+ self._years.add(year)
+
+ def get_statement(self) -> str:
+ """ Returns a copyright statement. """
+ line = "Copyright (C)"
+ years = sorted(self._years)
+ year_count = len(years)
+ if year_count == 1:
+ line += " " + years[0]
+ elif year_count > 1:
+ line += " " + years[0] + ", " + years[-1]
+ line += " " + self._holder
+ return line
+
+ def __lt__(self, other: "Copyright") -> bool:
+ # pylint: disable=protected-access
+ if self._years and other._years:
+ self_first_year = sorted(self._years)[0]
+ other_first_year = sorted(other._years)[0]
+ if self_first_year == other_first_year:
+ return self._holder > other._holder
+ return self_first_year > other_first_year
+ if self._years or other._years:
+ return True
+ return self._holder > other._holder
+
+
+class Copyrights:
+ """ This class represents a set of copyright holders. """
+ def __init__(self):
+ self.copyrights = {}
+
+ def register(self, statement):
+ """ Registers a copyright statement. """
+ match = re.search(
+ r"^\s*Copyright\s+\(C\)\s+([0-9]+),\s*([0-9]+)\s+(.+)\s*$",
+ statement,
+ flags=re.I,
+ )
+ if match:
+ holder = match.group(3)
+ the_copyright = self.copyrights.setdefault(holder,
+ Copyright(holder))
+ the_copyright.add_year(match.group(1))
+ the_copyright.add_year(match.group(2))
+ return
+ match = re.search(
+ r"^\s*Copyright\s+\(C\)\s+([0-9]+)\s+(.+)\s*$",
+ statement,
+ flags=re.I,
+ )
+ if match:
+ holder = match.group(2)
+ the_copyright = self.copyrights.setdefault(holder,
+ Copyright(holder))
+ the_copyright.add_year(match.group(1))
+ return
+ match = re.search(r"^\s*Copyright\s+\(C\)\s+(.+)\s*$",
+ statement,
+ flags=re.I)
+ if match:
+ holder = match.group(1)
+ self.copyrights.setdefault(holder, Copyright(holder))
+ return
+ raise ValueError(statement)
+
+ def get_statements(self):
+ """ Returns all registered copyright statements as a sorted list. """
+ statements = []
+ for the_copyright in sorted(self.copyrights.values()):
+ statements.append(the_copyright.get_statement())
+ return statements
+
+
+def make_lines(content: GenericContent) -> List[str]:
+ """ Makes a list of lines from a generic content. """
+ if isinstance(content, str):
+ return content.strip("\n").split("\n")
+ if isinstance(content, list):
+ return content
+ return content.lines
+
+
+def _indent(lines: List[str], indent: str,
+ empty_line_indent: str) -> List[str]:
+ if indent:
+ return [
+ indent + line if line else empty_line_indent + line
+ for line in lines
+ ]
+ return lines
+
+
+@contextmanager
+def _add_context(_content: "Content") -> Iterator[None]:
+ yield
+
+
+_SPECIAL_BLOCK = re.compile(r"^( *\* | *[0-9]+\. | +)")
+
+
+class Content:
+ """ This class builds content. """
+
+ # pylint: disable=too-many-instance-attributes
+ def __init__(self, the_license: str, pop_indent_gap: bool):
+ self._lines = [] # type: List[str]
+ self._license = the_license
+ self._copyrights = Copyrights()
+ self._gap = False
+ self._tab = " "
+ self._indents = [""]
+ self._indent = ""
+ self._empty_line_indents = [""]
+ self._empty_line_indent = ""
+ self._pop_indent_gap = pop_indent_gap
+
+ def __str__(self):
+ return "\n".join(itertools.chain(self._lines, [""]))
+
+ @property
+ def lines(self) -> List[str]:
+ """ The lines. """
+ return self._lines
+
+ @property
+ def tab(self) -> str:
+ """ The tabulator. """
+ return self._tab
+
+ def append(self, content: GenericContent) -> None:
+ """ Appends the content. """
+ self._lines.extend(
+ _indent(make_lines(content), self._indent,
+ self._empty_line_indent))
+
+ def prepend(self, content: GenericContent) -> None:
+ """ Prepends the content. """
+ self._lines[0:0] = _indent(make_lines(content), self._indent,
+ self._empty_line_indent)
+
+ def add(self,
+ content: Optional[GenericContent],
+ context: AddContext = _add_context) -> None:
+ """
+ Skips leading empty lines, adds a gap if needed, then adds the content.
+ """
+ if not content:
+ return
+ lines = make_lines(content)
+ for index, line in enumerate(lines):
+ if line:
+ self._add_gap()
+ with context(self):
+ self._lines.extend(
+ _indent(lines[index:], self._indent,
+ self._empty_line_indent))
+ break
+
+ def wrap_text(self,
+ text: str,
+ initial_indent: str = "",
+ subsequent_indent: Optional[str] = None,
+ context: AddContext = _add_context) -> None:
+ """ Adds a gap if needed, then adds the wrapped text. """
+ self._add_gap()
+ with context(self):
+ if subsequent_indent is None:
+ if initial_indent:
+ subsequent_indent = self._tab
+ else:
+ subsequent_indent = ""
+ wrapper = textwrap.TextWrapper()
+ wrapper.break_long_words = False
+ wrapper.break_on_hyphens = False
+ wrapper.initial_indent = initial_indent
+ wrapper.width = 79 - len(self._indent)
+ gap = [] # type: List[str]
+ for block in text.split("\n\n"):
+ match = _SPECIAL_BLOCK.search(block)
+ if match:
+ space = len(match.group(0)) * " "
+ wrapper.subsequent_indent = f"{subsequent_indent}{space}"
+ block = block.replace(f"\n{space}", "\n")
+ else:
+ wrapper.subsequent_indent = subsequent_indent
+ self._lines.extend(gap)
+ self._lines.extend(
+ _indent(wrapper.wrap(block), self._indent,
+ self._empty_line_indent))
+ gap = [self._empty_line_indent]
+
+ def wrap(self,
+ content: Optional[GenericContent],
+ initial_indent: str = "",
+ subsequent_indent: Optional[str] = None,
+ context: AddContext = _add_context) -> None:
+ """ Adds a gap if needed, then adds the wrapped content. """
+ if not content:
+ return
+ if isinstance(content, str):
+ text = content
+ elif isinstance(content, list):
+ text = "\n".join(content)
+ else:
+ text = "\n".join(content.lines)
+ text = text.strip()
+ if not text:
+ return
+ self.wrap_text(text, initial_indent, subsequent_indent, context)
+
+ def paste(self, content: Optional[GenericContent]) -> None:
+ """ Pastes the wrapped content directly to the last line. """
+ if not content:
+ return
+ if isinstance(content, str):
+ text = content
+ elif isinstance(content, list):
+ text = "\n".join(content)
+ else:
+ text = "\n".join(content.lines)
+ indent_len = len(self._indent)
+ try:
+ last = self._lines[-1]
+ text = last[indent_len:] + " " + text
+ except IndexError:
+ last = ""
+ text = text.strip()
+ if not text:
+ return
+ wrapper = textwrap.TextWrapper()
+ wrapper.break_long_words = False
+ wrapper.break_on_hyphens = False
+ wrapper.initial_indent = ""
+ wrapper.width = 79 - len(self._indent)
+ for index, block in enumerate(text.split("\n\n")):
+ if index == 0:
+ wrapper.subsequent_indent = ""
+ lines = wrapper.wrap(block)
+ if 0 < len(last) >= indent_len:
+ self._lines[-1] = last[0:indent_len] + lines[0]
+ lines = lines[1:]
+ self.gap = True
+ else:
+ match = _SPECIAL_BLOCK.search(block)
+ if match:
+ space = len(match.group(0)) * " "
+ wrapper.subsequent_indent = space
+ block = block.replace(f"\n{space}", "\n")
+ else:
+ wrapper.subsequent_indent = ""
+ lines = wrapper.wrap(block)
+ self._lines.append(self._empty_line_indent)
+ self._lines.extend(
+ _indent(lines, self._indent, self._empty_line_indent))
+
+ def _add_gap(self) -> None:
+ if self._gap:
+ self._lines.extend(
+ _indent([""], self._indent, self._empty_line_indent))
+ self._gap = True
+
+ @property
+ def gap(self) -> bool:
+ """
+ True if the next Content.add() adds a gap before the new content,
+ otherwise False.
+ """
+ return self._gap
+
+ @gap.setter
+ def gap(self, value: bool) -> None:
+ """ Sets the gap indicator for Content.add(). """
+ self._gap = value
+
+ def _update_indent(self) -> None:
+ self._indent = "".join(self._indents)
+ empty_line_indent = "".join(self._empty_line_indents)
+ if empty_line_indent.isspace():
+ self._empty_line_indent = ""
+ else:
+ self._empty_line_indent = empty_line_indent
+
+ def push_indent(self,
+ indent: Optional[str] = None,
+ empty_line_indent: Optional[str] = None) -> None:
+ """ Pushes an indent level. """
+ self._indents.append(indent if indent else self._tab)
+ self._empty_line_indents.append(
+ empty_line_indent if empty_line_indent else self._tab)
+ self._update_indent()
+ self.gap = False
+
+ def pop_indent(self) -> None:
+ """ Pops an indent level. """
+ self._indents.pop()
+ self._empty_line_indents.pop()
+ self._update_indent()
+ self.gap = self._pop_indent_gap
+
+ @contextmanager
+ def indent(self,
+ indent: Optional[str] = None,
+ empty_line_indent: Optional[str] = None) -> Iterator[None]:
+ """ Opens an indent context. """
+ self.push_indent(indent, empty_line_indent)
+ yield
+ self.pop_indent()
+
+ def indent_lines(self, level: int) -> None:
+ """ Indents all lines by the specified indent level. """
+ prefix = level * self._tab
+ self._lines = [prefix + line if line else line for line in self._lines]
+
+ def add_blank_line(self):
+ """ Adds a blank line. """
+ self._lines.append("")
+
+ def register_license(self, the_license: str) -> None:
+ """ Registers a licence for the content. """
+ licenses = re.split(r"\s+OR\s+", the_license)
+ if self._license not in licenses:
+ raise ValueError(the_license)
+
+ def register_copyright(self, statement: str) -> None:
+ """ Registers a copyright statement for the content. """
+ self._copyrights.register(statement)
+
+ def register_license_and_copyrights_of_item(self, item: Item) -> None:
+ """ Registers the license and copyrights of the item. """
+ self.register_license(item["SPDX-License-Identifier"])
+ for statement in item["copyrights"]:
+ self.register_copyright(statement)
+
+ def write(self, path: str) -> None:
+ """ Writes the content to the file specified by the path. """
+ directory = os.path.dirname(path)
+ if directory:
+ os.makedirs(directory, exist_ok=True)
+ with open(path, "w+") as out:
+ out.write(str(self))
+
+
+_BSD_2_CLAUSE_LICENSE = """Redistribution and use in source and binary \
+forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE."""
+
+_PARAM = {
+ None: "@param ",
+ "in": "@param[in] ",
+ "out": "@param[out] ",
+ "inout": "@param[in,out] ",
+}
+
+
+class CInclude(NamedTuple):
+ """ A C include file. """
+ path: str
+ enabled_by: str = ""
+
+
+def _split_includes(
+ includes: List[CInclude]) -> Tuple[Set[str], Dict[str, Set[str]]]:
+ includes_unconditional = set() # type: Set[str]
+ includes_enabled_by = {} # type: Dict[str, Set[str]]
+ for inc in set(includes):
+ if inc.enabled_by and inc.enabled_by != "1":
+ try:
+ includes_unconditional.remove(inc.path)
+ except KeyError:
+ pass
+ includes_enabled_by.setdefault(inc.path, set()).add(inc.enabled_by)
+ elif inc.path not in includes_enabled_by:
+ includes_unconditional.add(inc.path)
+ return includes_unconditional, includes_enabled_by
+
+
+_FUNCTION_POINTER = re.compile(r"^[^(]+\(\s\*([^)]+)\)\s*\(")
+_DESIGNATOR = re.compile(r"([a-zA-Z0-9_]+)$")
+
+
+def _get_align_pos(param: str) -> Tuple[int, int]:
+ if param == "...":
+ return 0, sys.maxsize
+ match = _DESIGNATOR.search(param)
+ if not match:
+ match = _FUNCTION_POINTER.search(param)
+ assert match
+ star = param.find("*")
+ if star >= 0:
+ return star, match.start(1)
+ return match.start(1), match.start(1)
+
+
+def _align_params(params: List[str]) -> List[str]:
+ positions = list(map(_get_align_pos, params))
+ max_pos = max(positions)[1]
+ return [
+ param[:pos[0]] + (max_pos - pos[1]) * " " + param[pos[0]:]
+ for param, pos in zip(params, positions)
+ ]
+
+
+_NOT_ALPHANUM = re.compile(r"[^a-zA-Z0-9_]")
+
+
+class CContent(Content):
+ """ This class builds C content. """
+
+ # pylint: disable=too-many-public-methods
+ def __init__(self):
+ super().__init__("BSD-2-Clause", False)
+
+ def doxyfy(self, content: Optional[GenericContent]) -> None:
+ """
+ Adds a gap if needed, then adds the wrapped text with some conversion
+ to Doxygen markup.
+ """
+ if not content:
+ return
+ if isinstance(content, str):
+ text = content
+ elif isinstance(content, list):
+ text = "\n".join(content)
+ else:
+ text = "\n".join(content.lines)
+ text = text.strip()
+ if not text:
+ return
+ blocks = collections.deque(text.split("\n\n"))
+ while blocks:
+ block = blocks.popleft()
+ if block.startswith(".. code-block"):
+ self.add("@code")
+ self.gap = False
+ while blocks:
+ block = blocks.popleft()
+ if block.startswith(" "):
+ self.add(block[4:].replace("\n ", "\n"))
+ else:
+ blocks.appendleft(block)
+ break
+ self.append("@endcode")
+ else:
+ self.wrap_text(block)
+
+ def prepend_spdx_license_identifier(self):
+ """
+ Adds an SPDX License Identifier according to the registered licenses.
+ """
+ self.prepend([f"/* SPDX-License-Identifier: {self._license} */", ""])
+
+ def add_copyrights_and_licenses(self):
+ """
+ Adds the copyrights and licenses according to the registered copyrights
+ and licenses.
+ """
+ with self.comment_block():
+ self.add(self._copyrights.get_statements())
+ self.add(_BSD_2_CLAUSE_LICENSE)
+
+ def prepend_copyrights_and_licenses(self):
+ """
+ Prepends the copyrights and licenses according to the registered
+ copyrights and licenses.
+ """
+ content = CContent()
+ with content.comment_block():
+ content.add(self._copyrights.get_statements())
+ content.add(_BSD_2_CLAUSE_LICENSE)
+ content.append("")
+ self.prepend(content)
+
+ def add_have_config(self):
+ """ Adds a guarded config.h include. """
+ self.add(["#ifdef HAVE_CONFIG_H", "#include \"config.h\"", "#endif"])
+
+ def _add_includes(self, includes: Set[str], local: bool) -> None:
+ class IncludeKey: # pylint: disable=too-few-public-methods
+ """ Provides a key to sort includes. """
+ def __init__(self, inc: str):
+ self._inc = inc
+
+ def __lt__(self, other: "IncludeKey") -> bool:
+ left = self._inc.split("/")
+ right = other._inc.split("/")
+ left_len = len(left)
+ right_len = len(right)
+ if left_len == right_len:
+ for left_part, right_part in zip(left[:-1], right[:-1]):
+ if left_part != right_part:
+ return left_part < right_part
+ return left[-1] < right[-1]
+ return left_len < right_len
+
+ left = "\"" if local else "<"
+ right = "\"" if local else ">"
+ self.add([
+ f"#include {left}{inc}{right}"
+ for inc in sorted(includes, key=IncludeKey)
+ ])
+
+ def _add_includes_enabled_by(self, includes: Dict[str, Set[str]],
+ local: bool) -> None:
+ enabled_by_includes = {} # type: Dict[str, Set[str]]
+ for inc, enabled_bys in iter(includes.items()):
+ enabled_by_includes.setdefault(" && ".join(sorted(enabled_bys)),
+ set()).add(inc)
+ for enabled_by, incs in sorted(iter(enabled_by_includes.items())):
+ self.add(f"#if {enabled_by}")
+ with self.indent():
+ self._add_includes(incs, local)
+ self.add("#endif")
+
+ def add_includes(self,
+ includes: List[CInclude],
+ local: bool = False) -> None:
+ """ Adds a block of includes. """
+ includes_unconditional, includes_enabled_by = _split_includes(includes)
+ self._add_includes(includes_unconditional, local)
+ self._add_includes_enabled_by(includes_enabled_by, local)
+
+ def _open_comment_block(self, begin) -> None:
+ self.add(begin)
+ self.push_indent(" * ", " *")
+
+ def open_comment_block(self) -> None:
+ """ Opens a comment block. """
+ self._open_comment_block("/*")
+
+ def open_doxygen_block(self) -> None:
+ """ Opens a Doxygen comment block. """
+ self._open_comment_block("/**")
+
+ def open_file_block(self) -> None:
+ """ Opens a Doxygen @file comment block. """
+ self._open_comment_block(["/**", " * @file"])
+ self.gap = True
+
+ def open_defgroup_block(self, identifier: str, name: str) -> None:
+ """ Opens a Doxygen @defgroup comment block. """
+ self._open_comment_block(["/**", f" * @defgroup {identifier} {name}"])
+ self.gap = True
+
+ def open_function_block(self, function: str) -> None:
+ """ Opens a Doxygen @fn comment block. """
+ self._open_comment_block(["/**", f" * @fn {function}"])
+ self.gap = True
+
+ def close_comment_block(self) -> None:
+ """ Closes a comment block. """
+ self.pop_indent()
+ self.append(" */")
+ self.gap = True
+
+ @contextmanager
+ def comment_block(self) -> Iterator[None]:
+ """ Opens a comment block context. """
+ self.open_comment_block()
+ yield
+ self.close_comment_block()
+
+ @contextmanager
+ def doxygen_block(self) -> Iterator[None]:
+ """ Opens a Doxygen comment block context. """
+ self.open_doxygen_block()
+ yield
+ self.close_comment_block()
+
+ @contextmanager
+ def file_block(self) -> Iterator[None]:
+ """ Opens a Doxygen @file comment block context. """
+ self.open_file_block()
+ yield
+ self.close_comment_block()
+
+ @contextmanager
+ def defgroup_block(self, identifier: str, name: str) -> Iterator[None]:
+ """ Opens a Doxygen @defgroup comment block context. """
+ self.open_defgroup_block(identifier, name)
+ yield
+ self.close_comment_block()
+
+ @contextmanager
+ def function_block(self, function: str) -> Iterator[None]:
+ """ Opens a Doxygen @fn comment block context. """
+ self.open_function_block(function)
+ yield
+ self.close_comment_block()
+
+ def open_add_to_group(self, group: str) -> None:
+ """ Opens an add to group. """
+ with self.doxygen_block():
+ self.append([f"@addtogroup {group}", "", "@{"])
+
+ def close_add_to_group(self) -> None:
+ """ Closes an add to group. """
+ self.add("/** @} */")
+
+ @contextmanager
+ def add_to_group(self, group: str) -> Iterator[None]:
+ """ Opens an add to group context. """
+ self.open_add_to_group(group)
+ yield
+ self.close_add_to_group()
+
+ def open_for_loop(self, begin: str, end: str, step: str) -> None:
+ """ Opens a for loop. """
+ for_loop = [f"for ( {begin}; {end}; {step} ) {{"]
+ if len(self._indent) + len(for_loop[0]) > 79:
+ for_loop = [
+ "for (", f"{self.tab}{begin};", f"{self.tab}{end};",
+ f"{self.tab}{step}", ") {"
+ ]
+ self.add(for_loop)
+ self.push_indent()
+
+ def close_for_loop(self) -> None:
+ """ Closes a for loop. """
+ self.pop_indent()
+ self.append(["}"])
+ self.gap = True
+
+ @contextmanager
+ def for_loop(self, begin: str, end: str, step: str) -> Iterator[None]:
+ """ Opens a for loop context. """
+ self.open_for_loop(begin, end, step)
+ yield
+ self.close_for_loop()
+
+ def _function(self, ret: str, name: str, params: List[str],
+ param_line: str, space: str, semicolon: str) -> None:
+ # pylint: disable=too-many-arguments
+ line = f"{ret}{space}{name}("
+ if len(self._indent) + len(line) > 79:
+ line = f"{name}{param_line}{semicolon}"
+ if len(self._indent) + len(line) > 79:
+ self.add([ret, f"{name}("])
+ else:
+ self.add([ret, line])
+ return
+ else:
+ self.add(line)
+ with self.indent():
+ self.add(",\n".join(params))
+ self.add(f"){semicolon}")
+
+ def call_function(self,
+ ret: Optional[str],
+ name: str,
+ params: Optional[List[str]] = None) -> None:
+ """ Adds a function call. """
+ if ret:
+ space = " "
+ else:
+ ret = ""
+ space = ""
+ if params:
+ param_line = "( " + ", ".join(params) + " )"
+ else:
+ param_line = "()"
+ line = f"{ret}{space}{name}{param_line};"
+ if len(self._indent) + len(line) > 79:
+ if params:
+ self._function(ret, name, params, param_line, space, ";")
+ elif ret:
+ self.add(ret)
+ with self.indent():
+ self.add(f"{name}();")
+ else:
+ self.add(f"{name}();")
+ else:
+ self.add(line)
+
+ def declare_function(self,
+ ret: str,
+ name: str,
+ params: Optional[List[str]] = None,
+ define: bool = False,
+ align: bool = True) -> None:
+ # pylint: disable=too-many-arguments
+ """ Adds a function declaration. """
+ if not params:
+ params = ["void"]
+ param_line = f"( {', '.join(params)} )"
+ space = "" if not ret or ret.endswith("*") else " "
+ semicolon = "" if define else ";"
+ line = f"{ret}{space}{name}{param_line}{semicolon}"
+ if len(self._indent) + len(line) > 79:
+ if align:
+ params = _align_params(params)
+ self._function(ret, name, params, param_line, space, semicolon)
+ else:
+ self.add(line)
+
+ def open_function(self,
+ ret: str,
+ name: str,
+ params: Optional[List[str]] = None,
+ align: bool = True) -> None:
+ """ Opens a function definition. """
+ self.declare_function(ret, name, params, define=True, align=align)
+ self.append("{")
+ self.push_indent()
+
+ def close_function(self) -> None:
+ """ Closes a function definition. """
+ self.pop_indent()
+ self.add("}")
+
+ @contextmanager
+ def function(self,
+ ret: str,
+ name: str,
+ params: Optional[List[str]] = None,
+ align: bool = True) -> Iterator[None]:
+ """ Opens a function context. """
+ self.open_function(ret, name, params, align=align)
+ yield
+ self.close_function()
+
+ def open_condition(self,
+ expression: Optional[str],
+ chain: bool = False) -> None:
+ """ Opens a condition. """
+ begin = "} else " if chain else ""
+ ifelse = f"if ( {expression} ) " if expression else ""
+ self.add(f"{begin}{ifelse}{{")
+ self.push_indent()
+
+ def close_condition(self) -> None:
+ """ Closes a condition. """
+ self.pop_indent()
+ self.add("}")
+
+ @contextmanager
+ def condition(self, expression: Optional[str]) -> Iterator[None]:
+ """ Opens a condition context. """
+ self.open_condition(expression)
+ yield
+ self.close_condition()
+
+ @contextmanager
+ def first_condition(self, expression: Optional[str]) -> Iterator[None]:
+ """ Opens the first condition context. """
+ self.open_condition(expression, False)
+ yield
+ self.pop_indent()
+
+ @contextmanager
+ def next_condition(self, expression: Optional[str]) -> Iterator[None]:
+ """ Opens the next condition context. """
+ self.open_condition(expression, True)
+ yield
+ self.pop_indent()
+
+ @contextmanager
+ def final_condition(self, expression: Optional[str]) -> Iterator[None]:
+ """ Opens the final condition context. """
+ self.open_condition(expression, True)
+ yield
+ self.close_condition()
+
+ def add_brief_description(self, description: Optional[str]) -> None:
+ """ Adds a brief description. """
+ self.wrap(description, initial_indent="@brief ")
+
+ def add_param_description(
+ self,
+ params: Iterable[Dict[str, str]],
+ substitute: Callable[[str], str] = lambda x: x) -> None:
+ """ Adds a list of parameter descriptions. """
+ for param in params:
+ self.wrap(param["name"] + " " + substitute(param["description"]),
+ initial_indent=_PARAM[param["dir"]])
+
+ def add_description_block(self, brief: Optional[str],
+ description: Optional[str]) -> None:
+ """ Adds a description block. """
+ if brief or description:
+ with self.doxygen_block():
+ self.add_brief_description(brief)
+ self.wrap(description)
+ self.gap = False
+
+ def add_ingroup(self, ingroups: List[str]) -> None:
+ """ Adds an ingroup comment block. """
+ self.add(["@ingroup " + ingroup for ingroup in sorted(set(ingroups))])
+
+ def add_group(self, identifier: str, name: str, ingroups: List[str],
+ brief: Optional[str], description: Optional[str]) -> None:
+ # pylint: disable=too-many-arguments
+ """ Adds a group definition. """
+ with self.defgroup_block(identifier, name):
+ self.add_ingroup(ingroups)
+ self.add_brief_description(brief)
+ self.wrap(description)
+
+ @contextmanager
+ def header_guard(self, filename: str) -> Iterator[None]:
+ """ Opens a header guard context. """
+ guard = "_" + _NOT_ALPHANUM.sub("_", filename).upper()
+ self.add([f"#ifndef {guard}", f"#define {guard}"])
+ yield
+ self.add(f"#endif /* {guard} */")
+
+ @contextmanager
+ def extern_c(self) -> Iterator[None]:
+ """ Opens an extern "C" context. """
+ self.add(["#ifdef __cplusplus", "extern \"C\" {", "#endif"])
+ yield
+ self.add(["#ifdef __cplusplus", "}", "#endif"])
+
+ def add_paragraph(self, name: str,
+ content: Optional[GenericContent]) -> None:
+ """ Adds a Doxygen paragraph block. """
+ if content:
+ self.add(f"@par {name}")
+ self.gap = False
+ last = len(self.lines)
+ self.doxyfy(content)
+ if self._empty_line_indent in self.lines[last:]:
+ self.lines.insert(last, f"{self._indent}@parblock")
+ self.lines.append(f"{self._indent}@endparblock")
+
+
+def get_value_doxygen_function(ctx: ItemGetValueContext) -> Any:
+ """ Gets a value as a function for Doxygen markup. """
+ return f"{ctx.value[ctx.key]}()"
+
+
+def get_value_double_colon(ctx: ItemGetValueContext) -> Any:
+ """ Gets a value with a :: prefix. """
+ return f"::{ctx.value[ctx.key]}"
+
+
+def get_value_hash(ctx: ItemGetValueContext) -> Any:
+ """ Gets a value with a # prefix. """
+ return f"#{ctx.value[ctx.key]}"
+
+
+class ExpressionMapper:
+ """ Maps symbols and operations to form a C expression. """
+
+ # pylint: disable=no-self-use
+ def map_bool(self, value: bool) -> str:
+ """ Maps a boolean value to build an expression. """
+ return str(int(value))
+
+ # pylint: disable=no-self-use
+ def map_symbol(self, symbol: str) -> str:
+ """ Maps a symbol to build an expression. """
+ return f"defined({symbol})"
+
+ def op_and(self) -> str:
+ """ Returns the and operator. """
+ return " && "
+
+ def op_or(self) -> str:
+ """ Returns the or operator. """
+ return " || "
+
+ def op_not(self, symbol: str) -> str:
+ """ Returns the negation of the symbol. """
+ return f"!{symbol}"
+
+
+class PythonExpressionMapper(ExpressionMapper):
+ """ Maps symbols and operations to form a Python expression. """
+
+ # pylint: disable=no-self-use
+ def map_bool(self, value: bool) -> str:
+ return str(value)
+
+ # pylint: disable=no-self-use
+ def map_symbol(self, symbol: str) -> str:
+ return symbol
+
+ def op_and(self) -> str:
+ return " and "
+
+ def op_or(self) -> str:
+ return " or "
+
+ def op_not(self, symbol: str) -> str:
+ return f"not {symbol}"
+
+
+def _to_expression_op(enabled_by: Any, mapper: ExpressionMapper,
+ operation: str) -> str:
+ symbols = [
+ _to_expression(next_enabled_by, mapper)
+ for next_enabled_by in enabled_by
+ ]
+ if len(symbols) == 1:
+ return symbols[0]
+ return f"({operation.join(symbols)})"
+
+
+def _to_expression_op_and(enabled_by: Any, mapper: ExpressionMapper) -> str:
+ return _to_expression_op(enabled_by, mapper, mapper.op_and())
+
+
+def _to_expression_op_not(enabled_by: Any, mapper: ExpressionMapper) -> str:
+ return mapper.op_not(_to_expression(enabled_by, mapper))
+
+
+def _to_expression_op_or(enabled_by: Any, mapper: ExpressionMapper) -> str:
+ return _to_expression_op(enabled_by, mapper, mapper.op_or())
+
+
+_TO_EXPRESSION_OP = {
+ "and": _to_expression_op_and,
+ "not": _to_expression_op_not,
+ "or": _to_expression_op_or
+}
+
+
+def _to_expression(enabled_by: Any, mapper: ExpressionMapper) -> str:
+ if isinstance(enabled_by, bool):
+ return mapper.map_bool(enabled_by)
+ if isinstance(enabled_by, list):
+ return _to_expression_op_or(enabled_by, mapper)
+ if isinstance(enabled_by, dict):
+ if len(enabled_by) == 1:
+ key = next(iter(enabled_by))
+ return _TO_EXPRESSION_OP[key](enabled_by[key], mapper)
+ raise ValueError
+ return mapper.map_symbol(enabled_by)
+
+
+def enabled_by_to_exp(enabled_by: Any, mapper: ExpressionMapper) -> str:
+ """
+ Returns an expression for an enabled-by attribute value.
+ """
+ exp = _to_expression(enabled_by, mapper)
+ if exp.startswith("("):
+ return exp[1:-1]
+ return exp
diff --git a/rtemsspec/glossary.py b/rtemsspec/glossary.py
new file mode 100644
index 00000000..1131f45b
--- /dev/null
+++ b/rtemsspec/glossary.py
@@ -0,0 +1,139 @@
+# SPDX-License-Identifier: BSD-2-Clause
+""" This module provides functions for glossary of terms generation. """
+
+# Copyright (C) 2019, 2020 embedded brains GmbH (http://www.embedded-brains.de)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import glob
+import re
+from typing import Any, Dict, NamedTuple
+
+from rtemsspec.sphinxcontent import SphinxContent, SphinxMapper
+from rtemsspec.items import Item, ItemCache, ItemGetValueContext, ItemMapper
+
+ItemMap = Dict[str, Item]
+
+
+class _Glossary(NamedTuple):
+ """ A glossary of terms. """
+ uid_to_item: ItemMap = {}
+ term_to_item: ItemMap = {}
+
+
+def _gather_glossary_groups(item: Item, glossary_groups: ItemMap) -> None:
+ for child in item.children():
+ _gather_glossary_groups(child, glossary_groups)
+ if item["type"] == "glossary" and item["glossary-type"] == "group":
+ glossary_groups[item.uid] = item
+
+
+def _gather_glossary_terms(item: Item, glossary: _Glossary) -> None:
+ for child in item.children():
+ _gather_glossary_terms(child, glossary)
+ if item["type"] == "glossary" and item["glossary-type"] == "term":
+ glossary.uid_to_item[item.uid] = item
+ term = item["term"]
+ assert term not in glossary.term_to_item
+ glossary.term_to_item[term] = item
+
+
+def _generate_glossary_content(terms: ItemMap) -> SphinxContent:
+ content = SphinxContent()
+ content.add_header("Glossary", level=1)
+ content.add(".. glossary::")
+ with content.indent():
+ content.add(":sorted:")
+ for item in sorted(terms.values(), key=lambda x: x["term"].lower()):
+ content.register_license_and_copyrights_of_item(item)
+ text = SphinxMapper(item).substitute(item["text"])
+ content.add_definition_item(item["term"], text)
+ content.add_licence_and_copyrights()
+ return content
+
+
+def _find_glossary_terms(path: str, document_terms: ItemMap,
+ glossary: _Glossary) -> None:
+ for src in glob.glob(path + "/**/*.rst", recursive=True):
+ if src.endswith("glossary.rst"):
+ continue
+ with open(src, "r") as out:
+ for term in re.findall(":term:`([^`]+)`", out.read()):
+ item = glossary.term_to_item[term]
+ document_terms[item.uid] = item
+
+
+class _GlossaryMapper(ItemMapper):
+ def __init__(self, item: Item, document_terms: ItemMap):
+ super().__init__(item)
+ self._document_terms = document_terms
+
+ def get_value(self, ctx: ItemGetValueContext) -> Any:
+ """ Recursively adds glossary terms to the document terms. """
+ if ctx.type_path_key == "glossary/term:/term":
+ if ctx.item.uid not in self._document_terms:
+ self._document_terms[ctx.item.uid] = ctx.item
+ _GlossaryMapper(ctx.item, self._document_terms).substitute(
+ ctx.item["text"])
+ # The value of this substitute is unused.
+ return ""
+
+
+def _resolve_glossary_terms(document_terms: ItemMap) -> None:
+ for term in list(document_terms.values()):
+ _GlossaryMapper(term, document_terms).substitute(term["text"])
+
+
+def _generate_project_glossary(target: str, glossary: _Glossary) -> None:
+ content = _generate_glossary_content(glossary.uid_to_item)
+ content.write(target)
+
+
+def _generate_document_glossary(config: dict, glossary: _Glossary) -> None:
+ document_terms = {} # type: ItemMap
+ for path in config["rest-source-paths"]:
+ _find_glossary_terms(path, document_terms, glossary)
+ _resolve_glossary_terms(document_terms)
+ content = _generate_glossary_content(document_terms)
+ content.write(config["target"])
+
+
+def generate(config: dict, item_cache: ItemCache) -> None:
+ """
+ Generates glossaries of terms according to the configuration.
+
+ :param config: A dictionary with configuration entries.
+ :param item_cache: The specification item cache containing the glossary
+ groups and terms.
+ """
+ groups = {} # type: ItemMap
+ for item in item_cache.top_level.values():
+ _gather_glossary_groups(item, groups)
+
+ project_glossary = _Glossary()
+ for group in config["project-groups"]:
+ _gather_glossary_terms(groups[group], project_glossary)
+
+ _generate_project_glossary(config["project-target"], project_glossary)
+
+ for document_config in config["documents"]:
+ _generate_document_glossary(document_config, project_glossary)
diff --git a/rtemsspec/interface.py b/rtemsspec/interface.py
new file mode 100644
index 00000000..13cab4b6
--- /dev/null
+++ b/rtemsspec/interface.py
@@ -0,0 +1,585 @@
+# SPDX-License-Identifier: BSD-2-Clause
+""" This module provides functions for the generation of interfaces. """
+
+# Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+from contextlib import contextmanager
+import os
+from typing import Any, Callable, Dict, Iterator, List, Union
+
+from rtemsspec.content import CContent, CInclude, enabled_by_to_exp, \
+ ExpressionMapper, get_value_double_colon, get_value_doxygen_function, \
+ get_value_hash
+from rtemsspec.items import Item, ItemCache, ItemGetValueContext, ItemMapper
+
+ItemMap = Dict[str, Item]
+Lines = Union[str, List[str]]
+GetLines = Callable[["Node", Item, Any], Lines]
+
+
+def _get_ingroups(item: Item) -> ItemMap:
+ ingroups = {} # type: ItemMap
+ for link in item.links_to_parents():
+ if link.role == "interface-ingroup":
+ ingroups[link.item.uid] = link.item
+ return ingroups
+
+
+def _get_group_identifiers(groups: ItemMap) -> List[str]:
+ return [item["identifier"] for item in groups.values()]
+
+
+def _forward_declaration(item: Item) -> str:
+ target = next(item.parents("interface-target"))
+ return f"{target['interface-type']} {target['name']}"
+
+
+def _get_value_forward_declaration(ctx: ItemGetValueContext) -> Any:
+ return _forward_declaration(ctx.item)
+
+
+class _InterfaceMapper(ItemMapper):
+ def __init__(self, node: "Node"):
+ super().__init__(node.item)
+ self._node = node
+ self._code_or_doc = "doc"
+ self.add_get_value("interface/forward-declaration:code:/name",
+ _get_value_forward_declaration)
+ self.add_get_value("interface/forward-declaration:doc:/name",
+ _get_value_forward_declaration)
+ self.add_get_value("interface/function:doc:/name",
+ get_value_doxygen_function)
+ self.add_get_value("interface/enumerator:doc:/name",
+ get_value_double_colon)
+ self.add_get_value("interface/typedef:doc:/name",
+ get_value_double_colon)
+ self.add_get_value("interface/define:doc:/name", get_value_hash)
+ self.add_get_value("interface/enum:doc:/name", get_value_hash)
+ self.add_get_value("interface/macro:doc:/name", get_value_hash)
+ self.add_get_value("interface/variable:doc:/name", get_value_hash)
+
+ @contextmanager
+ def code(self) -> Iterator[None]:
+ """ Enables code mapping. """
+ code_or_doc = self._code_or_doc
+ self._code_or_doc = "code"
+ yield
+ self._code_or_doc = code_or_doc
+
+ def get_value(self, ctx: ItemGetValueContext) -> Any:
+ if self._code_or_doc == "code" and ctx.item["type"] == "interface":
+ node = self._node
+ header_file = node.header_file
+ if ctx.item["interface-type"] == "enumerator":
+ for link in ctx.item.links_to_children():
+ if link.role == "interface-enumerator":
+ header_file.add_includes(link.item)
+ else:
+ header_file.add_includes(ctx.item)
+ header_file.add_potential_edge(node, ctx.item)
+ return super().get_value(
+ ItemGetValueContext(ctx.item, f"{self._code_or_doc}:{ctx.path}",
+ ctx.value, ctx.key, ctx.index))
+
+ def enabled_by_to_defined(self, enabled_by: str) -> str:
+ """
+ Maps an item-level enabled-by attribute value to the corresponding
+ defined expression.
+ """
+ return self._node.header_file.enabled_by_defined[enabled_by]
+
+
+class _InterfaceExpressionMapper(ExpressionMapper):
+ def __init__(self, mapper: _InterfaceMapper):
+ super().__init__()
+ self._mapper = mapper
+
+ def map_symbol(self, symbol: str) -> str:
+ with self._mapper.code():
+ return self._mapper.substitute(symbol)
+
+
+class _ItemLevelExpressionMapper(ExpressionMapper):
+ def __init__(self, mapper: _InterfaceMapper):
+ super().__init__()
+ self._mapper = mapper
+
+ def map_symbol(self, symbol: str) -> str:
+ with self._mapper.code():
+ return self._mapper.substitute(
+ self._mapper.enabled_by_to_defined(symbol))
+
+
+class _HeaderExpressionMapper(ExpressionMapper):
+ def __init__(self, item: Item, enabled_by_defined: Dict[str, str]):
+ super().__init__()
+ self._mapper = ItemMapper(item)
+ self._enabled_by_defined = enabled_by_defined
+
+ def map_symbol(self, symbol: str) -> str:
+ return self._mapper.substitute(self._enabled_by_defined[symbol])
+
+
+def _add_definition(node: "Node", item: Item, prefix: str,
+ value: Dict[str, Any], get_lines: GetLines) -> CContent:
+ content = CContent()
+ default = value["default"]
+ variants = value["variants"]
+ if variants:
+ ifelse = "#if "
+ with node.mapper.prefix(os.path.join(prefix, "variants")):
+ for variant in variants:
+ enabled_by = enabled_by_to_exp(
+ variant["enabled-by"],
+ _InterfaceExpressionMapper(node.mapper))
+ content.append(f"{ifelse}{enabled_by}")
+ with content.indent():
+ content.append(get_lines(node, item,
+ variant["definition"]))
+ ifelse = "#elif "
+ if default is not None:
+ content.append("#else")
+ with node.mapper.prefix(os.path.join(prefix, "default")):
+ with content.indent():
+ content.append(get_lines(node, item, default))
+ content.append("#endif")
+ else:
+ with node.mapper.prefix(os.path.join(prefix, "default")):
+ content.append(get_lines(node, item, default))
+ return content
+
+
+class Node:
+ """ Nodes of a header file. """
+ def __init__(self, header_file: "_HeaderFile", item: Item,
+ ingroups: ItemMap):
+ self.header_file = header_file
+ self.item = item
+ self.ingroups = ingroups
+ self.in_edges = {} # type: ItemMap
+ self.out_edges = {} # type: ItemMap
+ self.content = CContent()
+ self.mapper = _InterfaceMapper(self)
+
+ def __lt__(self, other: "Node") -> bool:
+ return self.item.uid < other.item.uid
+
+ @contextmanager
+ def _enum_struct_or_union(self) -> Iterator[None]:
+ self.content.add(self._get_description(self.item, self.ingroups))
+ name = self.item["name"]
+ typename = self.item["interface-type"]
+ kind = self.item["definition-kind"]
+ if kind == f"{typename}-only":
+ self.content.append(f"{typename} {name} {{")
+ elif kind == "typedef-only":
+ self.content.append(f"typedef {typename} {{")
+ else:
+ self.content.append(f"typedef {typename} {name} {{")
+ self.content.push_indent()
+ yield
+ self.content.pop_indent()
+ if kind == f"{typename}-only":
+ self.content.append("};")
+ else:
+ self.content.append(f"}} {name};")
+
+ def _generate(self) -> None:
+ _NODE_GENERATORS[self.item["interface-type"]](self)
+
+ def generate(self) -> None:
+ """ Generates a node to generate the node content. """
+ enabled_by = self.item["enabled-by"]
+ if not isinstance(enabled_by, bool) or not enabled_by:
+ mapper = _ItemLevelExpressionMapper(self.mapper)
+ self.content.add(f"#if {enabled_by_to_exp(enabled_by, mapper)}")
+ with self.content.indent():
+ self._generate()
+ self.content.add("#endif")
+ else:
+ self._generate()
+
+ def generate_compound(self) -> None:
+ """ Generates a compound (struct or union). """
+ with self._enum_struct_or_union():
+ for index, definition in enumerate(self.item["definition"]):
+ self.content.add(
+ _add_definition(self, self.item, f"definition[{index}]",
+ definition, Node._get_compound_definition))
+
+ def generate_enum(self) -> None:
+ """ Generates an enum. """
+ with self._enum_struct_or_union():
+ enumerators = [] # type: List[CContent]
+ for link in self.item.links_to_parents():
+ if link.role != "interface-enumerator":
+ continue
+ enumerator = self._get_description(link.item, {})
+ enumerator.append(
+ _add_definition(self, link.item, "definition",
+ link.item["definition"],
+ Node._get_enumerator_definition))
+ enumerators.append(enumerator)
+ for enumerator in enumerators[0:-1]:
+ enumerator.lines[-1] += ","
+ enumerator.append("")
+ self.content.append(enumerator)
+ try:
+ self.content.append(enumerators[-1])
+ except IndexError:
+ pass
+
+ def generate_define(self) -> None:
+ """ Generates a define. """
+ self._add_generic_definition(Node._get_define_definition)
+
+ def generate_forward_declaration(self) -> None:
+ """ Generates a forward declaration. """
+ self.content.append([
+ "", "/* Forward declaration */",
+ _forward_declaration(self.item) + ";"
+ ])
+
+ def generate_function(self) -> None:
+ """ Generates a function. """
+ self._add_generic_definition(Node._get_function_definition)
+
+ def generate_group(self) -> None:
+ """ Generates a group. """
+ self.header_file.add_ingroup(self.item)
+ for ingroup in self.ingroups.values():
+ self.header_file.add_potential_edge(self, ingroup)
+ self.content.add_group(self.item["identifier"], self.item["name"],
+ _get_group_identifiers(self.ingroups),
+ self.item["brief"], self.item["description"])
+
+ def generate_macro(self) -> None:
+ """ Generates a macro. """
+ self._add_generic_definition(Node._get_macro_definition)
+
+ def generate_typedef(self) -> None:
+ """ Generates a typedef. """
+ self._add_generic_definition(Node._get_typedef_definition)
+
+ def generate_variable(self) -> None:
+ """ Generates a variable. """
+ self._add_generic_definition(Node._get_variable_definition)
+
+ def substitute_code(self, text: str) -> str:
+ """
+ Performs a variable substitution on code using the item mapper of the
+ node.
+ """
+ if text:
+ with self.mapper.code():
+ return self.mapper.substitute(text.strip("\n"))
+ return text
+
+ def substitute_text(self, text: str) -> str:
+ """
+ Performs a variable substitution on a description using the item mapper
+ of the node.
+ """
+ if text:
+ return self.mapper.substitute(text.strip("\n"))
+ return text
+
+ def _get_compound_definition(self, item: Item, definition: Any) -> Lines:
+ content = CContent()
+ content.add_description_block(
+ self.substitute_text(definition["brief"]),
+ self.substitute_text(definition["description"]))
+ kind = definition["kind"]
+ if kind == "member":
+ member = self.substitute_code(definition["definition"]) + ";"
+ content.append(member.split("\n"))
+ else:
+ content.append(f"{kind} {{")
+ content.gap = False
+ with content.indent():
+ for index, compound_member in enumerate(
+ definition["definition"]):
+ content.add(
+ _add_definition(self, item, f"definition[{index}]",
+ compound_member,
+ Node._get_compound_definition))
+ name = definition["name"]
+ content.append(f"}} {name};")
+ return content.lines
+
+ def _get_enumerator_definition(self, item: Item, definition: Any) -> Lines:
+ name = item["name"]
+ if definition:
+ return f"{name} = {self.substitute_code(definition)}"
+ return f"{name}"
+
+ def _get_define_definition(self, item: Item, definition: Any) -> Lines:
+ name = item["name"]
+ value = self.substitute_code(definition)
+ if value:
+ return f"#define {name} {value}".split("\n")
+ return f"#define {name}"
+
+ def _get_function_definition(self, item: Item, definition: Any) -> Lines:
+ content = CContent()
+ name = item["name"]
+ ret = self.substitute_code(definition["return"])
+ params = [
+ self.substitute_code(param) for param in definition["params"]
+ ]
+ body = definition["body"]
+ if body:
+ with content.function("static inline " + ret, name, params):
+ content.add(self.substitute_code(body))
+ else:
+ content.declare_function(ret, name, params)
+ return content.lines
+
+ def _get_macro_definition(self, item: Item, definition: Any) -> Lines:
+ name = item["name"]
+ params = [param["name"] for param in item["params"]]
+ if params:
+ param_line = " " + ", ".join(params) + " "
+ else:
+ param_line = ""
+ line = f"#define {name}({param_line})"
+ if len(line) > 79:
+ param_block = ", \\\n ".join(params)
+ line = f"#define {name}( \\\n {param_block} \\\n)"
+ if not definition:
+ return line
+ body_lines = self.substitute_code(definition).split("\n")
+ if len(body_lines) == 1 and len(line + body_lines[0]) <= 79:
+ body = " "
+ else:
+ body = " \\\n "
+ body += " \\\n ".join(body_lines)
+ return line + body
+
+ def _get_typedef_definition(self, _item: Item, definition: Any) -> Lines:
+ return f"typedef {self.substitute_code(definition)};"
+
+ def _get_variable_definition(self, _item: Item, definition: Any) -> Lines:
+ return f"extern {self.substitute_code(definition)};"
+
+ def _get_description(self, item: Item, ingroups: ItemMap) -> CContent:
+ content = CContent()
+ with content.doxygen_block():
+ content.add_ingroup(_get_group_identifiers(ingroups))
+ content.add_brief_description(self.substitute_text(item["brief"]))
+ content.wrap(self.substitute_text(item["description"]))
+ content.wrap(self.substitute_text(item["notes"]))
+ if "params" in item:
+ content.add_param_description(item["params"],
+ self.substitute_text)
+ if "return" in item:
+ ret = item["return"]
+ for retval in ret["return-values"]:
+ content.wrap(self.substitute_text(retval["description"]),
+ initial_indent=self.substitute_text(
+ f"@retval {retval['value']} "))
+ content.wrap(self.substitute_text(ret["return"]),
+ initial_indent="@return ")
+ return content
+
+ def _add_generic_definition(self, get_lines: GetLines) -> None:
+ self.content.add(self._get_description(self.item, self.ingroups))
+ self.content.append(
+ _add_definition(self, self.item, "definition",
+ self.item["definition"], get_lines))
+
+
+_NODE_GENERATORS = {
+ "enum": Node.generate_enum,
+ "define": Node.generate_define,
+ "forward-declaration": Node.generate_forward_declaration,
+ "function": Node.generate_function,
+ "group": Node.generate_group,
+ "macro": Node.generate_macro,
+ "struct": Node.generate_compound,
+ "typedef": Node.generate_typedef,
+ "union": Node.generate_compound,
+ "variable": Node.generate_variable,
+}
+
+
+class _HeaderFile:
+ """ A header file. """
+ def __init__(self, item: Item, enabled_by_defined: Dict[str, str]):
+ self._item = item
+ self._content = CContent()
+ self._ingroups = {} # type: ItemMap
+ self._includes = [] # type: List[Item]
+ self._nodes = {} # type: Dict[str, Node]
+ self.enabled_by_defined = enabled_by_defined
+
+ def add_includes(self, item: Item) -> None:
+ """ Adds the includes of the item to the header file includes. """
+ for link in item.links_to_parents():
+ if link.role == "interface-placement" and link.item[
+ "interface-type"] == "header-file":
+ self._includes.append(link.item)
+
+ def add_ingroup(self, item: Item) -> None:
+ """ Adds an ingroup to the header file. """
+ self._ingroups[item.uid] = item
+
+ def _add_child(self, item: Item) -> None:
+ ingroups = _get_ingroups(item)
+ if item["interface-type"] != "group":
+ self._ingroups.update(ingroups)
+ self._nodes[item.uid] = Node(self, item, ingroups)
+ self._content.register_license_and_copyrights_of_item(item)
+
+ def add_potential_edge(self, node: Node, item: Item) -> None:
+ """
+ Adds a potential edge from a node to another node identified by an
+ item.
+ """
+ if item.uid in self._nodes and item.uid != node.item.uid:
+ node.out_edges[item.uid] = item
+ self._nodes[item.uid].in_edges[node.item.uid] = node.item
+
+ def _resolve_ingroups(self, node: Node) -> None:
+ for ingroup in node.ingroups.values():
+ self.add_potential_edge(node, ingroup)
+
+ def generate_nodes(self) -> None:
+ """ Generates all nodes of this header file. """
+ for link in self._item.links_to_children():
+ if link.role == "interface-placement":
+ self._add_child(link.item)
+ for node in self._nodes.values():
+ self._resolve_ingroups(node)
+ node.generate()
+
+ def _get_nodes_in_dependency_order(self) -> List[Node]:
+ """
+ Gets the nodes of this header file ordered according to node
+ dependencies and UIDs.
+
+ Performs a topological sort using Kahn's algorithm.
+ """
+ nodes_in_dependency_order = [] # type: List[Node]
+
+ # Get incoming edge degrees for all nodes
+ in_degree = {} # type: Dict[str, int]
+ for node in self._nodes.values():
+ in_degree[node.item.uid] = len(node.in_edges)
+
+ # Create a queue with all nodes with no incoming edges sorted by UID
+ queue = [] # type: List[Node]
+ for node in self._nodes.values():
+ if in_degree[node.item.uid] == 0:
+ queue.append(node)
+ queue.sort(reverse=True)
+
+ # Topological sort
+ while queue:
+ node = queue.pop(0)
+ nodes_in_dependency_order.insert(0, node)
+
+ # Sort by UID
+ for uid in sorted(node.out_edges):
+ in_degree[uid] -= 1
+ if in_degree[uid] == 0:
+ queue.append(self._nodes[uid])
+
+ return nodes_in_dependency_order
+
+ def finalize(self) -> None:
+ """ Finalizes the header file. """
+ self._content.prepend_spdx_license_identifier()
+ with self._content.file_block():
+ self._content.add_ingroup(_get_group_identifiers(self._ingroups))
+ self._content.add_copyrights_and_licenses()
+ with self._content.header_guard(self._item["path"]):
+ exp_mapper = _HeaderExpressionMapper(self._item,
+ self.enabled_by_defined)
+ includes = [
+ CInclude(item["path"],
+ enabled_by_to_exp(item["enabled-by"], exp_mapper))
+ for item in self._includes if item != self._item
+ ]
+ includes.extend([
+ CInclude(link.item["path"],
+ enabled_by_to_exp(link["enabled-by"], exp_mapper))
+ for link in self._item.links_to_parents()
+ if link.role == "interface-include"
+ ])
+ self._content.add_includes(includes)
+ with self._content.extern_c():
+ for node in self._get_nodes_in_dependency_order():
+ self._content.add(node.content)
+
+ def write(self, domain_path: str) -> None:
+ """ Writes the header file. """
+ self._content.write(
+ os.path.join(domain_path, self._item["prefix"],
+ self._item["path"]))
+
+
+def _generate_header_file(item: Item, domains: Dict[str, str],
+ enabled_by_defined: Dict[str, str]) -> None:
+ domain = next(item.parents("interface-placement"))
+ assert domain["interface-type"] == "domain"
+ domain_path = domains.get(domain.uid, None)
+ if domain_path is None:
+ return
+ header_file = _HeaderFile(item, enabled_by_defined)
+ header_file.generate_nodes()
+ header_file.finalize()
+ header_file.write(domain_path)
+
+
+def _visit_header_files(item: Item, domains: Dict[str, str],
+ enabled_by_defined: Dict[str, str]) -> None:
+ for child in item.children():
+ _visit_header_files(child, domains, enabled_by_defined)
+ if item["type"] == "interface" and item["interface-type"] == "header-file":
+ _generate_header_file(item, domains, enabled_by_defined)
+
+
+def _gather_enabled_by_defined(item_level_interfaces: List[str],
+ item_cache: ItemCache) -> Dict[str, str]:
+ enabled_by_defined = {} # type: Dict[str, str]
+ for uid in item_level_interfaces:
+ for link in item_cache[uid].links_to_children():
+ if link.role == "interface-placement":
+ define = f"defined(${{{link.item.uid}:/name}})"
+ enabled_by_defined[link.item["name"]] = define
+ return enabled_by_defined
+
+
+def generate(config: dict, item_cache: ItemCache) -> None:
+ """
+ Generates header files according to the configuration.
+
+ :param config: A dictionary with configuration entries.
+ :param item_cache: The specification item cache containing the interfaces.
+ """
+ enabled_by_defined = _gather_enabled_by_defined(
+ config["item-level-interfaces"], item_cache)
+ for item in item_cache.top_level.values():
+ _visit_header_files(item, config["domains"], enabled_by_defined)
diff --git a/rtemsspec/interfacedoc.py b/rtemsspec/interfacedoc.py
new file mode 100644
index 00000000..5843bb0f
--- /dev/null
+++ b/rtemsspec/interfacedoc.py
@@ -0,0 +1,200 @@
+# SPDX-License-Identifier: BSD-2-Clause
+"""
+This module provides functions for the generation of interface documentation.
+"""
+
+# Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import os
+from typing import Any, Callable, Dict, List
+
+from rtemsspec.content import CContent, enabled_by_to_exp, ExpressionMapper
+from rtemsspec.sphinxcontent import get_label, get_reference, SphinxContent, \
+ SphinxMapper
+from rtemsspec.items import Item, ItemCache, ItemGetValueContext, ItemMapper
+
+ItemMap = Dict[str, Item]
+AddDefinition = Callable[[CContent, ItemMapper, Item, Dict[str, Any]], None]
+
+INTERFACE = "Interface"
+
+
+def _forward_declaration(item: Item) -> str:
+ target = next(item.parents("interface-target"))
+ return f"{target['interface-type']} {target['name']}"
+
+
+def _get_reference(name: str) -> str:
+ return get_reference(get_label(f"{INTERFACE}{name}"), f"{name}()")
+
+
+def _get_value_forward_declaration(ctx: ItemGetValueContext) -> Any:
+ return _forward_declaration(ctx.item)
+
+
+class _CodeMapper(ItemMapper):
+ def __init__(self, item: Item):
+ super().__init__(item)
+ self.add_get_value("interface/forward-declaration:/name",
+ _get_value_forward_declaration)
+
+
+def _get_value_function(ctx: ItemGetValueContext) -> Any:
+ return _get_reference(ctx.value[ctx.key])
+
+
+class _Mapper(SphinxMapper):
+ def __init__(self, item: Item):
+ super().__init__(item)
+ self.add_get_value("interface/function:/name", _get_value_function)
+
+
+def _generate_introduction(target: str, group: Item,
+ items: List[Item]) -> None:
+ content = SphinxContent()
+ content.register_license_and_copyrights_of_item(group)
+ group_name = group["name"]
+ with content.section("Introduction", get_label(group_name)):
+ content.append("")
+ content.gap = False
+ content.wrap(group["brief"])
+ content.wrap(group["description"])
+ content.paste(f"The directives provided by the {group_name} are:")
+ for item in items:
+ content.register_license_and_copyrights_of_item(item)
+ name = item["name"]
+ brief = item["brief"]
+ if brief:
+ brief = f" - {brief}"
+ else:
+ brief = ""
+ ref = _get_reference(name)
+ content.add_list_item(f"{ref}{brief}")
+ content.add_licence_and_copyrights()
+ content.write(target)
+
+
+def _add_function_definition(content: CContent, mapper: ItemMapper, item: Item,
+ value: Dict[str, Any]) -> None:
+ name = item["name"]
+ ret = mapper.substitute(value["return"])
+ params = [mapper.substitute(param) for param in value["params"]]
+ content.declare_function(ret, name, params)
+
+
+def _add_definition(content: CContent, mapper: ItemMapper, item: Item,
+ prefix: str, value: Dict[str, Any],
+ add_definition: AddDefinition) -> None:
+ # pylint: disable=too-many-arguments
+ assert item["interface-type"] == "function"
+ default = value["default"]
+ variants = value["variants"]
+ if variants:
+ ifelse = "#if "
+ with mapper.prefix(os.path.join(prefix, "variants")):
+ for variant in variants:
+ enabled_by = enabled_by_to_exp(variant["enabled-by"],
+ ExpressionMapper())
+ content.append(f"{ifelse}{enabled_by}")
+ with content.indent():
+ add_definition(content, mapper, item,
+ variant["definition"])
+ ifelse = "#elif "
+ if default is not None:
+ content.append("#else")
+ with mapper.prefix(os.path.join(prefix, "default")):
+ with content.indent():
+ add_definition(content, mapper, item, default)
+ content.append("#endif")
+ else:
+ with mapper.prefix(os.path.join(prefix, "default")):
+ add_definition(content, mapper, item, default)
+
+
+def _generate_directives(target: str, group: Item, items: List[Item]) -> None:
+ content = SphinxContent()
+ content.register_license_and_copyrights_of_item(group)
+ group_name = group["name"]
+ with content.section("Directives", get_label(group_name)):
+ for item in items:
+ content.register_license_and_copyrights_of_item(item)
+ name = item["name"]
+ code_mapper = _CodeMapper(item)
+ mapper = _Mapper(item)
+ with content.section(f"{name}()", "Interface"):
+ content.wrap(item["brief"])
+ with content.definition_item("CALLING SEQUENCE:"):
+ with content.directive("code-block", "c"):
+ code = CContent()
+ _add_definition(code, code_mapper, item, "definition",
+ item["definition"],
+ _add_function_definition)
+ content.add(code)
+ if item["params"]:
+ with content.definition_item("DIRECTIVE PARAMETERS:"):
+ for param in item["params"]:
+ content.add_definition_item(
+ mapper.substitute(param["name"]),
+ mapper.substitute(
+ f"This parameter {param['description']}"),
+ wrap=True)
+ ret = item["return"]
+ if ret["return"] or ret["return-values"]:
+ with content.definition_item("DIRECTIVE RETURN VALUES:"):
+ if ret["return-values"]:
+ for retval in ret["return-values"]:
+ content.add_definition_item(
+ mapper.substitute(str(retval["value"])),
+ mapper.substitute(retval["description"]),
+ wrap=True)
+ content.wrap(mapper.substitute(ret["return"]))
+ content.add_definition_item("DESCRIPTION:",
+ mapper.substitute(
+ item["description"]),
+ wrap=True)
+ content.add_definition_item("NOTES:",
+ mapper.substitute(item["notes"]),
+ wrap=True)
+ content.add_licence_and_copyrights()
+ content.write(target)
+
+
+def generate(config: list, item_cache: ItemCache) -> None:
+ """
+ Generates interface documentation according to the configuration.
+
+ :param config: A dictionary with configuration entries.
+ :param item_cache: The specification item cache containing the interfaces.
+ """
+ for doc_config in config:
+ items = [] # type: List[Item]
+ group = item_cache[doc_config["group"]]
+ assert group["type"] == "interface"
+ assert group["interface-type"] == "group"
+ for child in group.children("interface-ingroup"):
+ if child["interface-type"] in ["function"]:
+ items.append(child)
+ items.sort(key=lambda x: x["name"])
+ _generate_introduction(doc_config["introduction-target"], group, items)
+ _generate_directives(doc_config["directives-target"], group, items)
diff --git a/rtemsspec/items.py b/rtemsspec/items.py
new file mode 100644
index 00000000..06c13525
--- /dev/null
+++ b/rtemsspec/items.py
@@ -0,0 +1,511 @@
+# SPDX-License-Identifier: BSD-2-Clause
+""" This module provides specification items and an item cache. """
+
+# Copyright (C) 2019, 2020 embedded brains GmbH (http://www.embedded-brains.de)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+from contextlib import contextmanager
+import os
+import pickle
+import string
+import stat
+from typing import Any, Callable, Dict, Iterator, List, NamedTuple, Mapping, \
+ Optional, Tuple
+import yaml
+
+
+class ItemGetValueContext(NamedTuple):
+ """ Context used to get an item value. """
+ item: "Item"
+ path: str
+ value: Any
+ key: str
+ index: Any # should be int, but this triggers a mypy error
+
+ @property
+ def type_path_key(self) -> str:
+ """ Returns the item type followed the path to the key. """
+ return f"{self.item.type}:{os.path.join(self.path, self.key)}"
+
+
+ItemMap = Dict[str, "Item"]
+ItemGetValue = Callable[[ItemGetValueContext], Any]
+
+
+def _is_enabled_op_and(enabled: List[str], enabled_by: Any) -> bool:
+ for next_enabled_by in enabled_by:
+ if not is_enabled(enabled, next_enabled_by):
+ return False
+ return True
+
+
+def _is_enabled_op_not(enabled: List[str], enabled_by: Any) -> bool:
+ return not is_enabled(enabled, enabled_by)
+
+
+def _is_enabled_op_or(enabled: List[str], enabled_by: Any) -> bool:
+ for next_enabled_by in enabled_by:
+ if is_enabled(enabled, next_enabled_by):
+ return True
+ return False
+
+
+_IS_ENABLED_OP = {
+ "and": _is_enabled_op_and,
+ "not": _is_enabled_op_not,
+ "or": _is_enabled_op_or
+}
+
+
+def is_enabled(enabled: List[str], enabled_by: Any) -> bool:
+ """ Verifies if the given parameter is enabled by specific enables. """
+ if isinstance(enabled_by, bool):
+ return enabled_by
+ if isinstance(enabled_by, list):
+ return _is_enabled_op_or(enabled, enabled_by)
+ if isinstance(enabled_by, dict):
+ key, value = next(iter(enabled_by.items()))
+ return _IS_ENABLED_OP[key](enabled, value)
+ return enabled_by in enabled
+
+
+def _str_representer(dumper, data):
+ return dumper.represent_scalar("tag:yaml.org,2002:str",
+ data,
+ style="|" if "\n" in data else "")
+
+
+yaml.add_representer(str, _str_representer)
+
+
+class Link:
+ """ A link to an item. """
+ def __init__(self, item: "Item", data: Any):
+ self._item = item
+ self._data = data
+
+ @classmethod
+ def create(cls, link: "Link", item: "Item") -> "Link":
+ """ Creates a link using an existing link with a new target item. """
+ return cls(item, link._data) # pylint: disable=protected-access
+
+ def __getitem__(self, name: str) -> Any:
+ return self._data[name]
+
+ @property
+ def item(self) -> "Item":
+ """ The item referenced by this link. """
+ return self._item
+
+ @property
+ def role(self) -> str:
+ """ The link role. """
+ return self._data["role"]
+
+
+def _get_value(ctx: ItemGetValueContext) -> Any:
+ value = ctx.value[ctx.key]
+ if ctx.index >= 0:
+ return value[ctx.index]
+ return value
+
+
+class Item:
+ """ Objects of this class represent a specification item. """
+ def __init__(self, item_cache: "ItemCache", uid: str, data: Any):
+ self._item_cache = item_cache
+ self._uid = uid
+ self._data = data
+ self._links_to_parents = [] # type: List[Link]
+ self._links_to_children = [] # type: List[Link]
+
+ def __contains__(self, key: str) -> bool:
+ return key in self._data
+
+ def __getitem__(self, key: str) -> Any:
+ return self._data[key]
+
+ def __setitem__(self, key: str, value: Any) -> None:
+ self._data[key] = value
+
+ def get(self, key: str, default: Any) -> Any:
+ """
+ Gets the attribute value if the attribute exists, otherwise the
+ specified default value is returned.
+ """
+ return self._data.get(key, default)
+
+ def get_by_key_path(self,
+ key_path: str,
+ prefix: str = "",
+ get_value: ItemGetValue = _get_value) -> Any:
+ """ Gets the attribute value corresponding to the key path. """
+ if not os.path.isabs(key_path):
+ key_path = os.path.join(prefix, key_path)
+ key_path = os.path.normpath(key_path)
+ path = "/"
+ value = self._data
+ for key in key_path.strip("/").split("/"):
+ parts = key.split("[")
+ try:
+ index = int(parts[1].split("]")[0])
+ except IndexError:
+ index = -1
+ ctx = ItemGetValueContext(self, path, value, parts[0], index)
+ try:
+ value = get_value(ctx)
+ except KeyError:
+ value = _get_value(ctx)
+ path = os.path.join(path, key)
+ return value
+
+ @property
+ def uid(self) -> str:
+ """ Returns the UID of the item. """
+ return self._uid
+
+ def to_abs_uid(self, abs_or_rel_uid: str) -> str:
+ """
+ Returns the absolute UID of an absolute UID or an UID relative to this
+ item.
+ """
+ if abs_or_rel_uid == ".":
+ return self._uid
+ if os.path.isabs(abs_or_rel_uid):
+ return abs_or_rel_uid
+ return os.path.normpath(
+ os.path.join(os.path.dirname(self.uid), abs_or_rel_uid))
+
+ def map(self, abs_or_rel_uid: str) -> "Item":
+ """
+ Maps the absolute UID or the UID relative to this item to the
+ corresponding item.
+ """
+ return self._item_cache[self.to_abs_uid(abs_or_rel_uid)]
+
+ def links_to_parents(self) -> Iterator[Link]:
+ """ Yields the links to the parents of this items. """
+ yield from self._links_to_parents
+
+ def parents(self, role: Optional[str] = None) -> Iterator["Item"]:
+ """ Yields the parents of this items. """
+ if role is None:
+ for link in self._links_to_parents:
+ yield link.item
+ else:
+ for link in self._links_to_parents:
+ if link.role == role:
+ yield link.item
+
+ def links_to_children(self) -> Iterator[Link]:
+ """ Yields the links to the children of this items. """
+ yield from self._links_to_children
+
+ def children(self, role: Optional[str] = None) -> Iterator["Item"]:
+ """ Yields the children of this items. """
+ if role is None:
+ for link in self._links_to_children:
+ yield link.item
+ else:
+ for link in self._links_to_children:
+ if link.role == role:
+ yield link.item
+
+ def init_parents(self, item_cache: "ItemCache"):
+ """ Initializes the list of links to parents of this items. """
+ for data in self._data["links"]:
+ link = Link(item_cache[self.to_abs_uid(data["uid"])], data)
+ self._links_to_parents.append(link)
+
+ def add_link_to_child(self, link: Link):
+ """ Adds a link to a child item of this items. """
+ self._links_to_children.append(link)
+
+ def is_enabled(self, enabled: List[str]):
+ """ Returns true if the item is enabled by the specified enables. """
+ return is_enabled(enabled, self["enabled-by"])
+
+ @property
+ def data(self) -> Any:
+ """ The item data. """
+ return self._data
+
+ @property
+ def file(self) -> str:
+ """ Returns the file of the item. """
+ return self._data["_file"]
+
+ @file.setter
+ def file(self, value: str):
+ """ Sets the file of the item. """
+ self._data["_file"] = value
+
+ @property
+ def type(self) -> str:
+ """ Returns the type of the item. """
+ return self._data["_type"]
+
+ def save(self):
+ """ Saves the item to the corresponding file. """
+ with open(self.file, "w") as dst:
+ data = {}
+ for key, value in self._data.items():
+ if not key.startswith("_"):
+ data[key] = value
+ dst.write(
+ yaml.dump(data, default_flow_style=False, allow_unicode=True))
+
+ def load(self):
+ """ Loads the item from the corresponding file. """
+ filename = self.file
+ with open(filename, "r") as src:
+ self._data = yaml.safe_load(src.read())
+ self._data["_file"] = filename
+
+
+class ItemTemplate(string.Template):
+ """ String template for item mapper identifiers. """
+ idpattern = "[a-zA-Z0-9._/-]+(:[][a-zA-Z0-9._/-]+)?(|[a-zA-Z0-9_]+)*"
+
+
+class ItemMapper(Mapping[str, object]):
+ """ Maps identifiers to items and attribute values. """
+ def __init__(self, item: Item):
+ self._item = item
+ self._prefix = [""]
+ self._get_value = {} # type: Dict[str, ItemGetValue]
+
+ @property
+ def item(self) -> Item:
+ """ The item of the mapper. """
+ return self._item
+
+ @item.setter
+ def item(self, item: Item) -> None:
+ """ Sets the item of the mapper. """
+ self._item = item
+
+ def add_get_value(self, type_path_key: str,
+ get_value: ItemGetValue) -> None:
+ """
+ Adds a get value for the specified type and key path.
+ """
+ self._get_value[type_path_key] = get_value
+
+ def push_prefix(self, prefix: str) -> None:
+ """ Pushes a key path prefix. """
+ self._prefix.append(prefix)
+
+ def pop_prefix(self) -> None:
+ """ Pops a key path prefix. """
+ self._prefix.pop()
+
+ @contextmanager
+ def prefix(self, prefix: str) -> Iterator[None]:
+ """ Opens a key path prefix context. """
+ self.push_prefix(prefix)
+ yield
+ self.pop_prefix()
+
+ def map(self, identifier: str) -> Tuple[Item, Any]:
+ """
+ Maps an identifier to the corresponding item and attribute value.
+ """
+ uid_key_path, *pipes = identifier.split("|")
+ colon = uid_key_path.find(":")
+ if colon >= 0:
+ uid, key_path = uid_key_path[:colon], uid_key_path[colon + 1:]
+ else:
+ uid, key_path = uid_key_path, "/_uid"
+ if uid == ".":
+ item = self._item
+ prefix = "/".join(self._prefix)
+ else:
+ item = self._item.map(uid)
+ prefix = ""
+ value = item.get_by_key_path(key_path, prefix, self.get_value)
+ for func in pipes:
+ value = getattr(self, func)(value)
+ return item, value
+
+ def __getitem__(self, identifier):
+ return self.map(identifier)[1]
+
+ def __iter__(self):
+ raise StopIteration
+
+ def __len__(self):
+ raise AttributeError
+
+ def substitute(self, text: Optional[str]) -> str:
+ """ Performs a variable substitution using the item mapper. """
+ if not text:
+ return ""
+ return ItemTemplate(text).substitute(self)
+
+ def substitute_with_prefix(self, text: Optional[str], prefix: str) -> str:
+ """
+ Performs a variable substitution using the item mapper with a prefix.
+ """
+ if not text:
+ return ""
+ with self.prefix(prefix):
+ return ItemTemplate(text).substitute(self)
+
+ def get_value(self, ctx: ItemGetValueContext) -> Any:
+ """ Gets a value by key and optional index. """
+ return self._get_value[ctx.type_path_key](ctx)
+
+
+class _SpecType(NamedTuple):
+ key: str
+ refinements: Dict[str, Any]
+
+
+def _gather_spec_refinements(item: Item) -> Optional[_SpecType]:
+ new_type = None # type: Optional[_SpecType]
+ for link in item.links_to_children():
+ if link.role == "spec-refinement":
+ key = link["spec-key"]
+ if new_type is None:
+ new_type = _SpecType(key, {})
+ assert new_type.key == key
+ new_type.refinements[
+ link["spec-value"]] = _gather_spec_refinements(link.item)
+ return new_type
+
+
+class ItemCache:
+ """ This class provides a cache of specification items. """
+ def __init__(self, config: Any):
+ self._items = {} # type: ItemMap
+ self._top_level = {} # type: ItemMap
+ self._load_items(config)
+ self._set_types(config)
+
+ def __getitem__(self, uid: str) -> Item:
+ return self._items[uid]
+
+ @property
+ def all(self) -> ItemMap:
+ """ Returns the map of all specification items. """
+ return self._items
+
+ @property
+ def top_level(self) -> ItemMap:
+ """ Returns the map of top-level specification items. """
+ return self._top_level
+
+ def _load_items_in_dir(self, base: str, path: str, cache_file: str,
+ update_cache: bool) -> None:
+ data_by_uid = {} # type: Dict[str, Any]
+ if update_cache:
+ for name in os.listdir(path):
+ path2 = os.path.join(path, name)
+ if name.endswith(".yml") and not name.startswith("."):
+ uid = "/" + os.path.relpath(path2, base).replace(
+ ".yml", "")
+ with open(path2, "r") as yaml_src:
+ data = yaml.safe_load(yaml_src.read())
+ data["_file"] = os.path.abspath(path2)
+ data["_uid"] = uid
+ data_by_uid[uid] = data
+ os.makedirs(os.path.dirname(cache_file), exist_ok=True)
+ with open(cache_file, "wb") as out:
+ pickle.dump(data_by_uid, out)
+ else:
+ with open(cache_file, "rb") as pickle_src:
+ data_by_uid = pickle.load(pickle_src)
+ for uid, data in iter(data_by_uid.items()):
+ item = Item(self, uid, data)
+ self._items[uid] = item
+ if not item["links"]:
+ self._top_level[uid] = item
+
+ def _load_items_recursive(self, base: str, path: str,
+ cache_dir: str) -> None:
+ mid = os.path.abspath(path)
+ mid = mid.replace(os.path.commonprefix([cache_dir, mid]), "")
+ cache_file = os.path.join(cache_dir, mid, "spec.pickle")
+ try:
+ mtime = os.path.getmtime(cache_file)
+ update_cache = False
+ except FileNotFoundError:
+ update_cache = True
+ for name in os.listdir(path):
+ path2 = os.path.join(path, name)
+ if name.endswith(".yml") and not name.startswith("."):
+ update_cache = update_cache or mtime <= os.path.getmtime(path2)
+ else:
+ if stat.S_ISDIR(os.lstat(path2).st_mode):
+ self._load_items_recursive(base, path2, cache_dir)
+ self._load_items_in_dir(base, path, cache_file, update_cache)
+
+ def _init_parents(self) -> None:
+ for item in self._items.values():
+ item.init_parents(self)
+
+ def _init_children(self) -> None:
+ for uid in sorted(self._items):
+ item = self._items[uid]
+ for link in item.links_to_parents():
+ link.item.add_link_to_child(Link.create(link, item))
+
+ def _load_items(self, config: Any) -> None:
+ cache_dir = os.path.abspath(config["cache-directory"])
+ for path in config["paths"]:
+ self._load_items_recursive(path, path, cache_dir)
+ self._init_parents()
+ self._init_children()
+
+ def _set_types(self, config: Any) -> None:
+ spec_root = config["spec-type-root-uid"]
+ if spec_root:
+ root_type = _gather_spec_refinements(self[spec_root])
+ else:
+ root_type = None
+ for item in self._items.values():
+ spec_type = root_type
+ value = item.data
+ path = [] # type: List[str]
+ while spec_type is not None:
+ type_name = value[spec_type.key]
+ path.append(type_name)
+ spec_type = spec_type.refinements[type_name]
+ item["_type"] = "/".join(path)
+
+
+class EmptyItemCache(ItemCache):
+ """ This class provides a empty cache of specification items. """
+ def __init__(self):
+ super().__init__({
+ "cache-directory": ".",
+ "paths": [],
+ "spec-type-root-uid": None
+ })
+
+
+class EmptyItem(Item):
+ """ Objects of this class represent empty items. """
+ def __init__(self):
+ super().__init__(EmptyItemCache(), "", {})
diff --git a/rtemsspec/specdoc.py b/rtemsspec/specdoc.py
new file mode 100644
index 00000000..40e4a47b
--- /dev/null
+++ b/rtemsspec/specdoc.py
@@ -0,0 +1,572 @@
+# SPDX-License-Identifier: BSD-2-Clause
+""" This module provides functions to document specification items. """
+
+# Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+from typing import Any, Dict, Iterator, List, Optional, Set, Tuple
+
+from rtemsspec.sphinxcontent import get_reference, get_label, \
+ SphinxContent, SphinxMapper
+from rtemsspec.items import Item, ItemCache, ItemGetValueContext
+from rtemsspec.specverify import NAME
+
+_DocumenterMap = Dict[str, "_Documenter"]
+
+_PRIMITIVE_TYPES = {
+ "bool": "{} {} be a boolean.",
+ "float": "{} {} be a floating-point number.",
+ "int": "{} {} be an integer number.",
+ "list-str": "{} {} be a list of strings.",
+ "none": "The attribute shall have no value.",
+ "optional-str": "{} {} be an optional string.",
+ "str": "{} {} be a string.",
+}
+
+_MANDATORY_ATTRIBUTES = {
+ "all":
+ "All explicit attributes shall be specified.",
+ "at-least-one":
+ "At least one of the explicit attributes shall be specified.",
+ "at-most-one":
+ "At most one of the explicit attributes shall be specified.",
+ "exactly-one":
+ "Exactly one of the explicit attributes shall be specified.",
+ "none":
+ "None of the explicit attributes is mandatory, "
+ "they are all are optional.",
+}
+
+_SPEC_TYPE_PREFIX = "SpecType"
+
+
+def _a_or_an(value: str) -> str:
+ if value[0].lower() in ["a", "e", "i", "o", "u"]:
+ return "an"
+ return "a"
+
+
+def _get_ref_specification_type(ctx: ItemGetValueContext) -> Any:
+ return get_reference(_SPEC_TYPE_PREFIX + get_label(ctx.value[ctx.key]))
+
+
+class _AssertContext:
+ """ This class provides a context to document assert expressions. """
+ def __init__(self, content: SphinxContent, ops: Dict[str, Any]):
+ self.content = content
+ self.ops = ops
+ self._comma = ""
+
+ def comma(self):
+ """ Adds a comma to the content if necessary. """
+ if not self.content.lines[-1].endswith(","):
+ self.content.lines[-1] += self._comma
+ self._comma = ","
+
+ def paste(self, text: str):
+ """ Pastes a text to the content. """
+ self.content.paste(text)
+
+
+def _negate(negate: bool) -> str:
+ if negate:
+ return "not "
+ return ""
+
+
+def _value(value: Any) -> str:
+ if isinstance(value, str):
+ return f"\"``{value}``\""
+ if isinstance(value, bool):
+ if value:
+ return "true"
+ return "false"
+ return str(value)
+
+
+def _list(ctx: _AssertContext, assert_info: List[str]) -> None:
+ ctx.content.add_list([f"{_value(value)}," for value in assert_info[:-2]])
+ try:
+ ctx.content.add_list_item(f"{_value(assert_info[-2])}, and")
+ except IndexError:
+ pass
+ try:
+ ctx.content.add_list_item(f"{_value(assert_info[-1])}")
+ except IndexError:
+ pass
+
+
+def _document_op_and_or(ctx: _AssertContext, negate: bool, assert_info: Any,
+ and_or: str) -> None:
+ if len(assert_info) == 1:
+ _document_assert(ctx, negate, assert_info[0])
+ else:
+ if negate or ctx.content.lines[-1].endswith("* "):
+ ctx.paste(f"shall {_negate(negate)}meet")
+ intro = ""
+ for element in assert_info:
+ ctx.comma()
+ with ctx.content.list_item(intro):
+ _document_assert(ctx, False, element)
+ intro = and_or
+
+
+def _document_op_and(ctx: _AssertContext, negate: bool,
+ assert_info: Any) -> None:
+ _document_op_and_or(ctx, negate, assert_info, "and, ")
+
+
+def _document_op_not(ctx: _AssertContext, negate: bool,
+ assert_info: Any) -> None:
+ _document_assert(ctx, not negate, assert_info)
+
+
+def _document_op_or(ctx: _AssertContext, negate: bool,
+ assert_info: Any) -> None:
+ _document_op_and_or(ctx, negate, assert_info, "or, ")
+
+
+def _document_op_eq(ctx: _AssertContext, negate: bool,
+ assert_info: Any) -> None:
+ if negate:
+ _document_op_ne(ctx, False, assert_info)
+ else:
+ ctx.paste(f"shall be equal to {_value(assert_info)}")
+
+
+def _document_op_ne(ctx: _AssertContext, negate: bool,
+ assert_info: Any) -> None:
+ if negate:
+ _document_op_eq(ctx, False, assert_info)
+ else:
+ ctx.paste(f"shall be not equal to {_value(assert_info)}")
+
+
+def _document_op_le(ctx: _AssertContext, negate: bool,
+ assert_info: Any) -> None:
+ if negate:
+ _document_op_gt(ctx, False, assert_info)
+ else:
+ ctx.paste(f"shall be less than or equal to {_value(assert_info)}")
+
+
+def _document_op_lt(ctx: _AssertContext, negate: bool,
+ assert_info: Any) -> None:
+ if negate:
+ _document_op_ge(ctx, False, assert_info)
+ else:
+ ctx.paste(f"shall be less than {_value(assert_info)}")
+
+
+def _document_op_ge(ctx: _AssertContext, negate: bool,
+ assert_info: Any) -> None:
+ if negate:
+ _document_op_lt(ctx, False, assert_info)
+ else:
+ ctx.paste(f"shall be greater than or equal to {_value(assert_info)}")
+
+
+def _document_op_gt(ctx: _AssertContext, negate: bool,
+ assert_info: Any) -> None:
+ if negate:
+ _document_op_le(ctx, False, assert_info)
+ else:
+ ctx.paste(f"shall be greater than {_value(assert_info)}")
+
+
+def _document_op_uid(ctx: _AssertContext, negate: bool,
+ _assert_info: Any) -> None:
+ if negate:
+ ctx.paste("shall be an invalid item UID")
+ else:
+ ctx.paste("shall be a valid item UID")
+
+
+def _document_op_re(ctx: _AssertContext, negate: bool,
+ assert_info: Any) -> None:
+ ctx.paste(f"shall {_negate(negate)}match with "
+ f"the regular expression \"``{assert_info}\"``")
+
+
+def _document_op_in(ctx: _AssertContext, negate: bool,
+ assert_info: Any) -> None:
+ ctx.paste(f"shall {_negate(negate)}be an element of")
+ _list(ctx, assert_info)
+
+
+def _document_op_contains(ctx: _AssertContext, negate: bool,
+ assert_info: Any) -> None:
+ ctx.paste(f"shall {_negate(negate)}contain an element of")
+ _list(ctx, assert_info)
+
+
+def _document_assert(ctx: _AssertContext, negate: bool,
+ assert_info: Any) -> None:
+ if isinstance(assert_info, bool):
+ if negate:
+ assert_info = not assert_info
+ ctx.paste(f"shall be {_value(assert_info)}")
+ elif isinstance(assert_info, list):
+ _document_op_or(ctx, negate, assert_info)
+ else:
+ key = next(iter(assert_info))
+ ctx.ops[key](ctx, negate, assert_info[key])
+
+
+_DOCUMENT_OPS = {
+ "and": _document_op_and,
+ "contains": _document_op_contains,
+ "eq": _document_op_eq,
+ "ge": _document_op_ge,
+ "gt": _document_op_gt,
+ "in": _document_op_in,
+ "le": _document_op_le,
+ "lt": _document_op_lt,
+ "ne": _document_op_ne,
+ "not": _document_op_not,
+ "or": _document_op_or,
+ "re": _document_op_re,
+ "uid": _document_op_uid,
+}
+
+
+def _maybe_document_assert(content: SphinxContent, type_info: Any) -> None:
+ if "assert" in type_info:
+ content.paste("The value ")
+ _document_assert(_AssertContext(content, _DOCUMENT_OPS), False,
+ type_info["assert"])
+ content.lines[-1] += "."
+
+
+class _Documenter:
+ # pylint: disable=too-many-instance-attributes
+ def __init__(self, item: Item, documenter_map: _DocumenterMap):
+ self._name = item["spec-type"]
+ self.section = item["spec-name"]
+ self._description = item["spec-description"]
+ self._info_map = item["spec-info"]
+ self._item = item
+ self._documenter_map = documenter_map
+ self.used_by = set() # type: Set[str]
+ self._mapper = SphinxMapper(item)
+ self._mapper.add_get_value("spec:/spec-name",
+ _get_ref_specification_type)
+ assert self._name not in documenter_map
+ documenter_map[self._name] = self
+
+ def _substitute(self, text: str) -> str:
+ if text:
+ return self._mapper.substitute(text)
+ return text
+
+ def get_section_reference(self) -> str:
+ """ Returns the section reference. """
+ return get_reference(_SPEC_TYPE_PREFIX + get_label(self.section))
+
+ def get_a_section_reference(self) -> str:
+ """ Returns a section reference. """
+ return f"{_a_or_an(self.section)} {self.get_section_reference()}"
+
+ def get_list_element_type(self) -> str:
+ """ Returns the list element type if this is a list only type. """
+ if len(self._info_map) == 1 and "list" in self._info_map:
+ return self._info_map["list"]["spec-type"]
+ return ""
+
+ def get_list_phrase(self, value: str, shall: str, type_name: str) -> str:
+ """ Returns a list phrase. """
+ if type_name in _PRIMITIVE_TYPES:
+ type_phrase = _PRIMITIVE_TYPES[type_name].format(
+ "Each list element", "shall")
+ else:
+ documenter = self._documenter_map[type_name]
+ ref = documenter.get_a_section_reference()
+ type_phrase = f"Each list element shall be {ref}."
+ return f"{value} {shall} be a list. {type_phrase}"
+
+ def get_value_type_phrase(self, value: str, shall: str,
+ type_name: str) -> str:
+ """ Returns a value type phrase. """
+ if type_name in _PRIMITIVE_TYPES:
+ return _PRIMITIVE_TYPES[type_name].format(value, shall)
+ documenter = self._documenter_map[type_name]
+ element_type_name = documenter.get_list_element_type()
+ if element_type_name:
+ return self.get_list_phrase(value, shall, element_type_name)
+ return (f"{value} {shall} be "
+ f"{documenter.get_a_section_reference()}.")
+
+ def refinements(self) -> Iterator["_Documenter"]:
+ """ Yields the refinements of this type. """
+ refinements = set(self._documenter_map[link.item["spec-type"]]
+ for link in self._item.links_to_children()
+ if link.role == "spec-refinement")
+ yield from sorted(refinements, key=lambda x: x.section)
+
+ def refines(self) -> Iterator[Tuple["_Documenter", str, str]]:
+ """ Yields the types refined by type. """
+ refines = [(self._documenter_map[link.item["spec-type"]],
+ link["spec-key"], link["spec-value"])
+ for link in self._item.links_to_parents()
+ if link.role == "spec-refinement"]
+ yield from sorted(refines, key=lambda x: x[0].section)
+
+ def hierarchy(self, content: SphinxContent) -> None:
+ """ Documents the item type hierarchy. """
+ with content.list_item(self.get_section_reference()):
+ for refinement in self.refinements():
+ refinement.hierarchy(content)
+
+ def _document_attributes(self, content: SphinxContent,
+ attributes: Any) -> None:
+ for key in sorted(attributes):
+ info = attributes[key]
+ content.add(key)
+ with content.indent():
+ content.wrap(
+ self.get_value_type_phrase("The attribute value", "shall",
+ info["spec-type"]))
+ content.paste(self._substitute(info["description"]))
+
+ def document_dict(self, content: SphinxContent, _variant: str, shall: str,
+ info: Any) -> None:
+ """ Documents an attribute set. """
+ if shall == "may":
+ content.paste("The value may be a set of attributes.")
+ content.paste(self._substitute(info["description"]))
+ has_explicit_attributes = len(info["attributes"]) > 0
+ if has_explicit_attributes:
+ mandatory_attributes = info["mandatory-attributes"]
+ if isinstance(mandatory_attributes, str):
+ content.paste(_MANDATORY_ATTRIBUTES[mandatory_attributes])
+ else:
+ assert isinstance(mandatory_attributes, list)
+ mandatory_attribute_count = len(mandatory_attributes)
+ if mandatory_attribute_count == 1:
+ content.paste(f"Only the ``{mandatory_attributes[0]}`` "
+ "attribute is mandatory.")
+ elif mandatory_attribute_count > 1:
+ content.paste("The following explicit "
+ "attributes are mandatory:")
+ for attribute in sorted(mandatory_attributes):
+ content.add_list_item(f"``{attribute}``")
+ content.add_blank_line()
+ else:
+ content.add_blank_line()
+ content.paste("The explicit attributes for this type are:")
+ self._document_attributes(content, info["attributes"])
+ if "generic-attributes" in info:
+ if has_explicit_attributes:
+ content.wrap("In addition to the explicit attributes, "
+ "generic attributes may be specified.")
+ else:
+ content.paste("Generic attributes may be specified.")
+ content.paste(
+ self.get_value_type_phrase(
+ "Each generic attribute key", "shall",
+ info["generic-attributes"]["key-spec-type"]))
+ content.paste(
+ self.get_value_type_phrase(
+ "Each generic attribute value", "shall",
+ info["generic-attributes"]["value-spec-type"]))
+ content.paste(
+ self._substitute(info["generic-attributes"]["description"]))
+
+ def document_value(self, content: SphinxContent, variant: str, shall: str,
+ info: Any) -> None:
+ """ Documents a value. """
+ content.paste(self.get_value_type_phrase("The value", shall, variant))
+ content.paste(self._substitute(info["description"]))
+ _maybe_document_assert(content, info)
+
+ def document_list(self, content: SphinxContent, _variant: str, shall: str,
+ info: Any) -> None:
+ """ Documents a list value. """
+ content.paste(
+ self.get_list_phrase("The value", shall, info["spec-type"]))
+ content.paste(self._substitute(info["description"]))
+
+ def document_none(self, content: SphinxContent, _variant: str, shall: str,
+ _info: Any) -> None:
+ """ Documents a none value. """
+ # pylint: disable=no-self-use
+ content.paste(f"There {shall} by be no value (null).")
+
+ def _add_description(self, content: SphinxContent) -> None:
+ refines = [
+ f"{documenter.get_section_reference()} though the "
+ f"``{key}`` attribute if the value is ``{value}``"
+ for documenter, key, value in self.refines()
+ ]
+ if len(refines) == 1:
+ content.wrap(f"This type refines the {refines[0]}.")
+ content.paste(self._description)
+ else:
+ content.add_list(refines,
+ "This type refines the following types:",
+ add_blank_line=True)
+ content.wrap(self._description)
+
+ def document(self,
+ content: SphinxContent,
+ names: Optional[Set[str]] = None) -> None:
+ """ Document this type. """
+ if self.get_list_element_type():
+ return
+ content.register_license_and_copyrights_of_item(self._item)
+ with content.section(self.section, _SPEC_TYPE_PREFIX):
+ last = content.lines[-1]
+ self._add_description(content)
+ if len(self._info_map) == 1:
+ if last == content.lines[-1]:
+ content.add_blank_line()
+ key, info = next(iter(self._info_map.items()))
+ _DOCUMENT[key](self, content, key, "shall", info)
+ else:
+ content.add("A value of this type shall be of one of "
+ "the following variants:")
+ for key in sorted(self._info_map):
+ with content.list_item(""):
+ _DOCUMENT[key](self, content, key, "may",
+ self._info_map[key])
+ content.add_list([
+ refinement.get_section_reference()
+ for refinement in self.refinements()
+ ], "This type is refined by the following types:")
+ content.add_list(sorted(self.used_by),
+ "This type is used by the following types:")
+ example = self._item["spec-example"]
+ if example:
+ content.add("Please have a look at the following example:")
+ with content.directive("code-block", "yaml"):
+ content.add(example)
+ if names:
+ names.remove(self._name)
+ for refinement in self.refinements():
+ refinement.document(content, names)
+
+ def _add_used_by(self, type_name: str) -> None:
+ if type_name not in _PRIMITIVE_TYPES:
+ documenter = self._documenter_map[type_name]
+ element_type_name = documenter.get_list_element_type()
+ if element_type_name:
+ type_name = element_type_name
+ if type_name not in _PRIMITIVE_TYPES:
+ documenter = self._documenter_map[type_name]
+ documenter.used_by.add(self.get_section_reference())
+
+ def resolve_used_by(self) -> None:
+ """ Resolves type uses in attribute sets. """
+ info = self._info_map.get("dict", None)
+ if info is not None:
+ for attribute in info["attributes"].values():
+ self._add_used_by(attribute["spec-type"])
+ if "generic-attributes" in info:
+ self._add_used_by(info["generic-attributes"]["key-spec-type"])
+ self._add_used_by(
+ info["generic-attributes"]["value-spec-type"])
+
+
+_DOCUMENT = {
+ "bool": _Documenter.document_value,
+ "dict": _Documenter.document_dict,
+ "float": _Documenter.document_value,
+ "int": _Documenter.document_value,
+ "list": _Documenter.document_list,
+ "none": _Documenter.document_none,
+ "str": _Documenter.document_value,
+}
+
+
+def _gather_item_documenters(item: Item,
+ documenter_map: _DocumenterMap) -> None:
+ for link in item.links_to_children():
+ if link.role == "spec-member":
+ _Documenter(link.item, documenter_map)
+
+
+def _create_str_documenter(item_cache: ItemCache, name: str, description: str,
+ documenter_map: _DocumenterMap) -> None:
+ type_name = name.lower()
+ _Documenter(
+ Item(
+ item_cache, f"/spec/{type_name}", {
+ "SPDX-License-Identifier":
+ "CC-BY-SA-4.0 OR BSD-2-Clause",
+ "copyrights": [
+ "Copyright (C) 2020 embedded brains GmbH "
+ "(http://www.embedded-brains.de)"
+ ],
+ "spec-description":
+ None,
+ "spec-example":
+ None,
+ "spec-info": {
+ "str": {
+ "description": description
+ }
+ },
+ "spec-name":
+ name,
+ "spec-type":
+ type_name,
+ }), documenter_map)
+
+
+def document(config: dict, item_cache: ItemCache) -> None:
+ """
+ Documents specification items according to the configuration.
+
+ :param config: A dictionary with configuration entries.
+ :param item_cache: The specification item cache.
+ """
+ documenter_map = {} # type: _DocumenterMap
+ root_item = item_cache[config["root-type"]]
+ _create_str_documenter(
+ item_cache, "Name", "A string is a valid name if it matches with the "
+ f"``{NAME.pattern.replace('$', '$$')}`` regular expression.",
+ documenter_map)
+ _create_str_documenter(
+ item_cache, "UID",
+ "The string shall be a valid absolute or relative item UID.",
+ documenter_map)
+ root_documenter = _Documenter(root_item, documenter_map)
+ _gather_item_documenters(root_item, documenter_map)
+ content = SphinxContent()
+ for documenter in documenter_map.values():
+ documenter.resolve_used_by()
+ documenter_names = set(documenter_map)
+ content.section_label_prefix = "ReqEng"
+ with content.section("Specification Items"):
+ with content.section("Specification Item Hierarchy"):
+ content.add(
+ "The specification item types have the following hierarchy:")
+ root_documenter.hierarchy(content)
+ with content.section("Specification Item Types"):
+ root_documenter.document(content, documenter_names)
+ with content.section("Specification Attribute Sets and Value Types"):
+ documenters = [documenter_map[name] for name in documenter_names]
+ for documenter in sorted(documenters, key=lambda x: x.section):
+ documenter.document(content)
+ content.add_licence_and_copyrights()
+ content.write(config["doc-target"])
diff --git a/rtemsspec/specverify.py b/rtemsspec/specverify.py
new file mode 100644
index 00000000..3e6dd15e
--- /dev/null
+++ b/rtemsspec/specverify.py
@@ -0,0 +1,486 @@
+# SPDX-License-Identifier: BSD-2-Clause
+""" This module provides functions for specification item verification. """
+
+# Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import logging
+import re
+from typing import Any, Dict, List, NamedTuple, Set
+
+from rtemsspec.items import Item, ItemCache
+
+_VerifierMap = Dict[str, "_Verifier"]
+
+
+def _type_name(value: Any):
+ type_name = type(value).__name__
+ if type_name == "NoneType":
+ return "none"
+ return type_name
+
+
+class _Path(NamedTuple):
+ item: Item
+ path: str
+
+
+class _AssertContext(NamedTuple):
+ path: _Path
+ value: Any
+ type_info: Dict[str, Any]
+ ops: Dict[str, Any]
+
+
+def _assert_op_and(ctx: _AssertContext, assert_info: Any) -> bool:
+ for element in assert_info:
+ if not _assert(ctx, element):
+ return False
+ return True
+
+
+def _assert_op_not(ctx: _AssertContext, assert_info: Any) -> bool:
+ return not _assert(ctx, assert_info)
+
+
+def _assert_op_or(ctx: _AssertContext, assert_info: Any) -> bool:
+ for element in assert_info:
+ if _assert(ctx, element):
+ return True
+ return False
+
+
+def _assert_op_eq(ctx: _AssertContext, assert_info: Any) -> bool:
+ return ctx.value == assert_info
+
+
+def _assert_op_ne(ctx: _AssertContext, assert_info: Any) -> bool:
+ return ctx.value != assert_info
+
+
+def _assert_op_le(ctx: _AssertContext, assert_info: Any) -> bool:
+ return ctx.value <= assert_info
+
+
+def _assert_op_lt(ctx: _AssertContext, assert_info: Any) -> bool:
+ return ctx.value < assert_info
+
+
+def _assert_op_ge(ctx: _AssertContext, assert_info: Any) -> bool:
+ return ctx.value >= assert_info
+
+
+def _assert_op_gt(ctx: _AssertContext, assert_info: Any) -> bool:
+ return ctx.value > assert_info
+
+
+def _assert_op_uid(ctx: _AssertContext, _assert_info: Any) -> bool:
+ try:
+ ctx.path.item.map(ctx.value)
+ except KeyError:
+ logging.warning("%s cannot resolve UID: %s", _prefix(ctx.path),
+ ctx.value)
+ return False
+ return True
+
+
+def _assert_op_re(ctx: _AssertContext, assert_info: Any) -> bool:
+ return re.search(assert_info, ctx.value) is not None
+
+
+def _assert_op_in(ctx: _AssertContext, assert_info: Any) -> bool:
+ return ctx.value in assert_info
+
+
+_WORD_SEPARATOR = re.compile(r"[ \t\n\r\f\v-]+")
+
+
+def _assert_op_contains(ctx: _AssertContext, assert_info: Any) -> bool:
+ value = " " + " ".join(_WORD_SEPARATOR.split(ctx.value.lower())) + " "
+ return any(f" {substring} " in value for substring in assert_info)
+
+
+def _assert(ctx: _AssertContext, assert_info: Any) -> bool:
+ if isinstance(assert_info, list):
+ return _assert_op_or(ctx, assert_info)
+ key = next(iter(assert_info))
+ return ctx.ops[key](ctx, assert_info[key])
+
+
+def _maybe_assert(path: _Path, value: Any, type_info: Any,
+ ops: Dict[str, Any]) -> bool:
+ if "assert" in type_info:
+ return _assert(_AssertContext(path, value, type_info, ops),
+ type_info["assert"])
+ return True
+
+
+_ASSERT_OPS_INT_OR_FLOAT = {
+ "and": _assert_op_and,
+ "not": _assert_op_not,
+ "or": _assert_op_or,
+ "eq": _assert_op_eq,
+ "ne": _assert_op_ne,
+ "le": _assert_op_le,
+ "lt": _assert_op_lt,
+ "ge": _assert_op_ge,
+ "gt": _assert_op_gt,
+}
+
+
+def _assert_int_or_float(path: _Path, value: Any, type_info: Any) -> bool:
+ return _maybe_assert(path, value, type_info, _ASSERT_OPS_INT_OR_FLOAT)
+
+
+_ASSERT_OPS_STR = {
+ "and": _assert_op_and,
+ "not": _assert_op_not,
+ "or": _assert_op_or,
+ "eq": _assert_op_eq,
+ "ne": _assert_op_ne,
+ "le": _assert_op_le,
+ "lt": _assert_op_lt,
+ "ge": _assert_op_ge,
+ "gt": _assert_op_gt,
+ "uid": _assert_op_uid,
+ "re": _assert_op_re,
+ "in": _assert_op_in,
+ "contains": _assert_op_contains,
+}
+
+
+def _assert_str(path: _Path, value: Any, type_info: Any) -> bool:
+ return _maybe_assert(path, value, type_info, _ASSERT_OPS_STR)
+
+
+def _assert_type(path: _Path, value: Any, type_expected: str) -> bool:
+ type_actual = _type_name(value)
+ if type_actual == type_expected:
+ return True
+ logging.error("%s expected type '%s', actual type '%s'", _prefix(path),
+ type_expected, type_actual)
+ return False
+
+
+NAME = re.compile(r"^([a-z][a-z0-9-]*|SPDX-License-Identifier)$")
+
+
+def _prefix(prefix: _Path) -> str:
+ if prefix.path.endswith(":"):
+ return prefix.path
+ return prefix.path + ":"
+
+
+class _Verifier:
+ def __init__(self, name: str, verifier_map: _VerifierMap):
+ self._name = name
+ self._verifier_map = verifier_map
+ self.is_subtype = False
+ verifier_map[name] = self
+
+ def verify_info(self, path: _Path) -> None:
+ """ Produces a verify logging information. """
+ logging.info("%s verify using type '%s'", _prefix(path), self._name)
+
+ def verify(self, path: _Path, value: Any) -> Set[str]:
+ """ Verifies a value according to the type information. """
+ self.verify_info(path)
+ _assert_type(path, value, self._name)
+ return set()
+
+ def resolve_type_refinements(self) -> None:
+ """ Resolves the type refinements for this type. """
+
+
+class _NameVerifier(_Verifier):
+ def verify(self, path: _Path, value: Any) -> Set[str]:
+ """ Verifies a name. """
+ self.verify_info(path)
+ if _assert_type(path, value, "str") and NAME.search(value) is None:
+ logging.error("%s invalid name: %s", _prefix(path), value)
+ return set()
+
+
+class _UIDVerifier(_Verifier):
+ def verify(self, path: _Path, value: Any) -> Set[str]:
+ """ Verifies an attribute key. """
+ self.verify_info(path)
+ if _assert_type(path, value, "str"):
+ try:
+ path.item.map(value)
+ except KeyError:
+ logging.error("%s cannot resolve UID: %s", _prefix(path),
+ value)
+ return set()
+
+
+class _ItemVerifier(_Verifier):
+ def __init__(self, name: str, verifier_map: _VerifierMap,
+ info_map: Dict[str, Any], item: Item):
+ super().__init__(name, verifier_map)
+ self._info_map = info_map
+ self._item = item
+ self._subtype_key = ""
+ self._subtype_verifiers = {} # type: _VerifierMap
+
+ def verify_bool(self, path: _Path, value: Any, type_info: Any) -> Set[str]:
+ """ Verifies a boolean value. """
+ # pylint: disable=no-self-use
+ if type_info and "assert" in type_info:
+ expected = type_info["assert"]
+ if expected != value:
+ logging.error("%s expected %r, actual %r", _prefix(path),
+ expected, value)
+ return set()
+
+ def _verify_key(self, path: _Path, value: Any, type_name: str,
+ key: str) -> None:
+ if type_name in self._verifier_map:
+ self._verifier_map[type_name].verify(
+ _Path(path.item, path.path + f"/{key}"), value[key])
+ else:
+ logging.error("%s unknown specification type: %s", _prefix(path),
+ type_name)
+
+ def assert_keys_no_constraints(self, path: _Path, specified_keys: Set[str],
+ keys: List[str]) -> None:
+ """ Asserts nothing in particular. """
+
+ def assert_keys_at_least_one(self, path: _Path, specified_keys: Set[str],
+ keys: List[str]) -> None:
+ """ Asserts that at least one specified key is present in the keys. """
+ present_keys = specified_keys.intersection(keys)
+ if len(present_keys) == 0:
+ logging.error(
+ "%s not at least one key out of %s is present for type '%s'",
+ _prefix(path), str(sorted(specified_keys)), self._name)
+
+ def assert_keys_at_most_one(self, path: _Path, specified_keys: Set[str],
+ keys: List[str]) -> None:
+ """ Asserts that at most one specified key is present in the keys. """
+ present_keys = specified_keys.intersection(keys)
+ if len(present_keys) > 1:
+ logging.error(
+ "%s not at most one key out of %s "
+ "is present for type '%s': %s", _prefix(path),
+ str(sorted(specified_keys)), self._name,
+ str(sorted(present_keys)))
+
+ def assert_keys_exactly_one(self, path: _Path, specified_keys: Set[str],
+ keys: List[str]) -> None:
+ """ Asserts that exactly one specified key is present in the keys. """
+ present_keys = specified_keys.intersection(keys)
+ if len(present_keys) != 1:
+ logging.error(
+ "%s not exactly one key out of %s "
+ "is present for type '%s': %s", _prefix(path),
+ str(sorted(specified_keys)), self._name,
+ str(sorted(present_keys)))
+
+ def assert_keys_subset(self, path: _Path, specified_keys: Set[str],
+ keys: List[str]) -> None:
+ """ Asserts that the specified keys are a subset of the keys. """
+ if not specified_keys.issubset(keys):
+ missing_keys = specified_keys.difference(
+ specified_keys.intersection(keys))
+ logging.error("%s missing mandatory keys for type '%s': %s",
+ _prefix(path), self._name, str(sorted(missing_keys)))
+
+ def _assert_mandatory_keys(self, path: _Path, type_info: Any,
+ attr_info: Any, keys: List[str]) -> None:
+ mandatory_attr_info = type_info["mandatory-attributes"]
+ if isinstance(mandatory_attr_info, str):
+ _ASSERT_KEYS[mandatory_attr_info](self, path, set(attr_info), keys)
+ else:
+ assert isinstance(mandatory_attr_info, list)
+ self.assert_keys_subset(path, set(mandatory_attr_info), keys)
+
+ def verify_dict(self, path: _Path, value: Any, type_info: Any) -> Set[str]:
+ """ Verifies a dictionary value. """
+ keys = sorted(filter(lambda key: not key.startswith("_"), value))
+ attr_info = type_info["attributes"]
+ self._assert_mandatory_keys(path, type_info, attr_info, keys)
+ verified_keys = set() # type: Set[str]
+ for key in keys:
+ if key in attr_info:
+ self._verify_key(path, value, attr_info[key]["spec-type"], key)
+ verified_keys.add(key)
+ elif "generic-attributes" in type_info:
+ key_as_value = {key: key}
+ self._verify_key(
+ path, key_as_value,
+ type_info["generic-attributes"]["key-spec-type"], key)
+ self._verify_key(
+ path, value,
+ type_info["generic-attributes"]["value-spec-type"], key)
+ verified_keys.add(key)
+ if self._subtype_key:
+ if self._subtype_key in keys:
+ subtype_value = value[self._subtype_key]
+ if subtype_value in self._subtype_verifiers:
+ verified_keys.update(
+ self._subtype_verifiers[subtype_value].verify(
+ path, value))
+ else:
+ logging.error(
+ "%s unknown subtype for key '%s' for type '%s': %s",
+ _prefix(path), self._subtype_key, self._name,
+ subtype_value)
+ else:
+ logging.error("%s subtype key '%s' not present for type '%s'",
+ _prefix(path), self._subtype_key, self._name)
+ if not self.is_subtype:
+ unverified_keys = set(keys).difference(verified_keys)
+ if unverified_keys:
+ logging.error(
+ "%s has unverfied keys for type '%s' and its subtypes: %s",
+ _prefix(path), self._name, str(sorted(unverified_keys)))
+ return verified_keys
+
+ def verify_int_or_float(self, path: _Path, value: Any,
+ type_info: Any) -> Set[str]:
+ """ Verifies an integer or float value. """
+ # pylint: disable=no-self-use
+ if not _assert_int_or_float(path, value, type_info):
+ logging.error("%s invalid value: %s", _prefix(path), str(value))
+ return set()
+
+ def verify_list(self, path: _Path, value: Any, type_info: Any) -> Set[str]:
+ """ Verifies a list value. """
+ verifier = self._verifier_map[type_info["spec-type"]]
+ for index, element in enumerate(value):
+ verifier.verify(_Path(path.item, path.path + f"[{index}]"),
+ element)
+ return set()
+
+ def verify_none(self, _path: _Path, _value: Any,
+ _type_info: Any) -> Set[str]:
+ """ Verifies a none value. """
+ # pylint: disable=no-self-use
+ return set()
+
+ def verify_str(self, path: _Path, value: Any, type_info: Any) -> Set[str]:
+ """ Verifies a string value. """
+ # pylint: disable=no-self-use
+ if not _assert_str(path, value, type_info):
+ logging.error("%s invalid value: %s", _prefix(path), value)
+ return set()
+
+ def verify(self, path: _Path, value: Any) -> Set[str]:
+ self.verify_info(path)
+ type_name = _type_name(value)
+ if type_name in self._info_map:
+ return _VERIFY[type_name](self, path, value,
+ self._info_map[type_name])
+ logging.error(
+ "%s expected value of types %s for type '%s', "
+ "actual type '%s'", _prefix(path), str(sorted(self._info_map)),
+ self._name, type_name)
+ return set()
+
+ def _add_subtype_verifier(self, subtype_key: str, subtype_value: str,
+ subtype_name: str) -> None:
+ logging.info("add subtype '%s' to '%s'", subtype_name, self._name)
+ assert not self._subtype_key or self._subtype_key == subtype_key
+ assert subtype_value not in self._subtype_verifiers
+ subtype_verifier = self._verifier_map[subtype_name]
+ subtype_verifier.is_subtype = True
+ self._subtype_key = subtype_key
+ self._subtype_verifiers[subtype_value] = subtype_verifier
+
+ def resolve_type_refinements(self) -> None:
+ for link in self._item.links_to_children():
+ if link.role == "spec-refinement":
+ self._add_subtype_verifier(link["spec-key"],
+ link["spec-value"],
+ link.item["spec-type"])
+
+
+_VERIFY = {
+ "bool": _ItemVerifier.verify_bool,
+ "dict": _ItemVerifier.verify_dict,
+ "float": _ItemVerifier.verify_int_or_float,
+ "int": _ItemVerifier.verify_int_or_float,
+ "list": _ItemVerifier.verify_list,
+ "none": _ItemVerifier.verify_none,
+ "str": _ItemVerifier.verify_str,
+}
+
+_ASSERT_KEYS = {
+ "all": _ItemVerifier.assert_keys_subset,
+ "at-least-one": _ItemVerifier.assert_keys_at_least_one,
+ "at-most-one": _ItemVerifier.assert_keys_at_most_one,
+ "exactly-one": _ItemVerifier.assert_keys_exactly_one,
+ "none": _ItemVerifier.assert_keys_no_constraints,
+}
+
+
+def _create_verifier(item: Item, verifier_map: _VerifierMap) -> _Verifier:
+ spec_type = item["spec-type"]
+ assert spec_type not in verifier_map
+ spec_info = item["spec-info"]
+ assert isinstance(spec_info, dict)
+ return _ItemVerifier(spec_type, verifier_map, spec_info, item)
+
+
+def _gather_item_verifiers(item: Item, verifier_map: _VerifierMap) -> None:
+ for link in item.links_to_children():
+ if link.role == "spec-member":
+ _create_verifier(link.item, verifier_map)
+
+
+def verify(config: dict, item_cache: ItemCache) -> None:
+ """
+ Verifies specification items according to the configuration.
+
+ :param config: A dictionary with configuration entries.
+ :param item_cache: The specification item cache.
+ """
+ verifier_map = {} # type: _VerifierMap
+ _NameVerifier("name", verifier_map)
+ _UIDVerifier("uid", verifier_map)
+ _Verifier("bool", verifier_map)
+ _Verifier("float", verifier_map)
+ _Verifier("int", verifier_map)
+ _Verifier("none", verifier_map)
+ _Verifier("str", verifier_map)
+ try:
+ root_uid = config["root-type"]
+ except KeyError:
+ logging.error("configuration has no root type")
+ return
+ try:
+ root_item = item_cache[root_uid]
+ except KeyError:
+ logging.error("root type item does not exist in item cache")
+ return
+ root_verifier = _create_verifier(root_item, verifier_map)
+ _gather_item_verifiers(root_item, verifier_map)
+ for name in sorted(verifier_map):
+ logging.info("type: %s", name)
+ verifier_map[name].resolve_type_refinements()
+ logging.info("start specification item verification")
+ for key in sorted(item_cache.all):
+ item = item_cache[key]
+ root_verifier.verify(_Path(item, f"{item.uid}:"), item.data)
+ logging.info("finished specification item verification")
diff --git a/rtemsspec/sphinxcontent.py b/rtemsspec/sphinxcontent.py
new file mode 100644
index 00000000..6247f987
--- /dev/null
+++ b/rtemsspec/sphinxcontent.py
@@ -0,0 +1,240 @@
+# SPDX-License-Identifier: BSD-2-Clause
+""" This module provides classes for Sphinx content generation. """
+
+# Copyright (C) 2019, 2020 embedded brains GmbH (http://www.embedded-brains.de)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+from contextlib import contextmanager
+import re
+from typing import Any, Iterable, Iterator, List, Optional, Union
+
+from rtemsspec.content import Content, make_lines
+from rtemsspec.items import Item, ItemGetValueContext, ItemMapper
+
+GenericContent = Union[str, List[str], "Content"]
+GenericContentIterable = Union[Iterable[str], Iterable[List[str]],
+ Iterable[GenericContent]]
+
+_HEADER_LEVELS = ["#", "*", "=", "-", "^", "\""]
+
+
+def _to_camel_case(name: str) -> str:
+ return name[0].upper() + re.sub(
+ r"\s+(.)", lambda match: match.group(1).upper(),
+ re.sub(r"[^ \t\n\r\f\va-zA-Z0-9]", " ", name[1:].replace("+", "X")))
+
+
+def get_reference(label: str, name: Optional[str] = None) -> str:
+ """ Returns the reference to the specified label. """
+ if name:
+ return f":ref:`{name} <{label}>`"
+ return f":ref:`{label}`"
+
+
+def get_label(name: str) -> str:
+ """ Returns the label for the specified name. """
+ return _to_camel_case(name.strip())
+
+
+class SphinxContent(Content):
+ """ This class builds Sphinx content. """
+ def __init__(self, section_level: int = 2):
+ super().__init__("CC-BY-SA-4.0", True)
+ self._tab = " "
+ self._section_level = section_level
+ self.section_label_prefix = "Section"
+
+ def add_label(self, label: str) -> None:
+ """ Adds a label. """
+ self.add(".. _" + label.strip() + ":")
+
+ def add_header(self, name, level=2) -> None:
+ """ Adds a header. """
+ name = name.strip()
+ self.add([name, _HEADER_LEVELS[level] * len(name)])
+
+ def add_header_with_label(self,
+ name: str,
+ level: int = 2,
+ label_prefix: Optional[str] = None) -> str:
+ """ Adds a header with label. """
+ if label_prefix is None:
+ label_prefix = self.section_label_prefix
+ label = label_prefix + get_label(name)
+ self.add_label(label)
+ self.add_header(name, level)
+ return label
+
+ def add_index_entries(self, entries) -> None:
+ """ Adds a list of index entries the content. """
+ self.add([".. index:: " + entry for entry in make_lines(entries)])
+
+ def add_definition_item(self,
+ name: GenericContent,
+ definition: GenericContent,
+ wrap: bool = False) -> None:
+ """ Adds a definition item the content. """
+ @contextmanager
+ def _definition_item_context(content: Content) -> Iterator[None]:
+ content.append(name)
+ content.push_indent()
+ yield
+ content.pop_indent()
+
+ if wrap:
+ self.wrap(definition, context=_definition_item_context)
+ else:
+ self.add(definition, context=_definition_item_context)
+
+ @contextmanager
+ def definition_item(self, name: GenericContent) -> Iterator[None]:
+ """ Opens a definition item context. """
+ self.wrap(name)
+ self.push_indent()
+ yield
+ self.pop_indent()
+
+ def open_directive(self,
+ name: str,
+ value: Optional[str] = None,
+ options: Optional[List[str]] = None) -> None:
+ """ Opens a directive. """
+ value = " " + value if value else ""
+ self.add(f".. {name.strip()}::{value}")
+ self.push_indent()
+ self.add(options)
+ self.gap = True
+
+ def close_directive(self) -> None:
+ """ Closes a directive. """
+ self.pop_indent()
+
+ @contextmanager
+ def directive(self,
+ name: str,
+ value: Optional[str] = None,
+ options: Optional[List[str]] = None):
+ """ Opens a directive context. """
+ self.open_directive(name, value, options)
+ yield
+ self.close_directive()
+
+ def open_section(self,
+ name: str,
+ label_prefix: Optional[str] = None) -> str:
+ """ Opens a section. """
+ label = self.add_header_with_label(name, self._section_level,
+ label_prefix)
+ self._section_level += 1
+ return label
+
+ def close_section(self) -> None:
+ """ Closes a section. """
+ self._section_level -= 1
+
+ @contextmanager
+ def section(self,
+ name: str,
+ label_prefix: Optional[str] = None) -> Iterator[str]:
+ """ Opens a section context. """
+ yield self.open_section(name, label_prefix)
+ self.close_section()
+
+ def add_list_item(self, content: GenericContent) -> None:
+ """ Adds a list item. """
+ self.wrap(content, initial_indent="* ", subsequent_indent=" ")
+
+ def add_list(self,
+ items: GenericContentIterable,
+ prologue: Optional[GenericContent] = None,
+ epilogue: Optional[GenericContent] = None,
+ add_blank_line: bool = False) -> None:
+ """ Adds a list with introduction. """
+ if items:
+ self.wrap(prologue)
+ for item in items:
+ self.add_list_item(item)
+ if add_blank_line:
+ self.add_blank_line()
+ self.wrap(epilogue)
+
+ def open_list_item(self, content: GenericContent) -> None:
+ """ Opens a list item. """
+ self.add(["* "])
+ self.push_indent(" ")
+ self.gap = True
+ self.paste(content)
+
+ def close_list_item(self) -> None:
+ """ Closes a list item. """
+ self.pop_indent()
+ self.gap = True
+
+ @contextmanager
+ def list_item(self, content: GenericContent) -> Iterator[None]:
+ """ Opens a list item context. """
+ self.open_list_item(content)
+ yield
+ self.close_list_item()
+
+ def add_licence_and_copyrights(self) -> None:
+ """
+ Adds a licence and copyright block according to the registered licenses
+ and copyrights.
+ """
+ statements = self._copyrights.get_statements()
+ if statements:
+ self.prepend("")
+ self.prepend([f".. {stm}" for stm in statements])
+ self.prepend([f".. SPDX-License-Identifier: {self._license}", ""])
+
+
+def _get_ref_term(ctx: ItemGetValueContext) -> Any:
+ return f":term:`{ctx.value[ctx.key]}`"
+
+
+def _get_ref_term_plural(ctx: ItemGetValueContext) -> Any:
+ try:
+ return f":term:`{ctx.value[ctx.key]} <{ctx.value['term']}>`"
+ except KeyError:
+ return f":term:`{ctx.value['term']}s <{ctx.value['term']}>`"
+
+
+def _get_appl_config_option(ctx: ItemGetValueContext) -> Any:
+ return f":ref:`{ctx.value[ctx.key]}`"
+
+
+class SphinxMapper(ItemMapper):
+ """ Sphinx item mapper. """
+ def __init__(self, item: Item):
+ super().__init__(item)
+ self.add_get_value("glossary/term:/term", _get_ref_term)
+ self.add_get_value("glossary/term:/plural", _get_ref_term_plural)
+ self.add_get_value("interface/appl-config-option/feature-enable:/name",
+ _get_appl_config_option)
+ self.add_get_value("interface/appl-config-option/feature:/name",
+ _get_appl_config_option)
+ self.add_get_value("interface/appl-config-option/initializer:/name",
+ _get_appl_config_option)
+ self.add_get_value("interface/appl-config-option/integer:/name",
+ _get_appl_config_option)
diff --git a/rtemsspec/tests/config/a.yml b/rtemsspec/tests/config/a.yml
new file mode 100644
index 00000000..162db4e9
--- /dev/null
+++ b/rtemsspec/tests/config/a.yml
@@ -0,0 +1,2 @@
+a: b
+c: !include b.yml
diff --git a/rtemsspec/tests/config/b.yml b/rtemsspec/tests/config/b.yml
new file mode 100644
index 00000000..e7e2f941
--- /dev/null
+++ b/rtemsspec/tests/config/b.yml
@@ -0,0 +1 @@
+!include c/d.yml
diff --git a/rtemsspec/tests/config/c/d.yml b/rtemsspec/tests/config/c/d.yml
new file mode 100644
index 00000000..dd4175c8
--- /dev/null
+++ b/rtemsspec/tests/config/c/d.yml
@@ -0,0 +1 @@
+!include e.yml
diff --git a/rtemsspec/tests/config/c/e.yml b/rtemsspec/tests/config/c/e.yml
new file mode 100644
index 00000000..4bcfe98e
--- /dev/null
+++ b/rtemsspec/tests/config/c/e.yml
@@ -0,0 +1 @@
+d
diff --git a/rtemsspec/tests/spec-applconfig/a.yml b/rtemsspec/tests/spec-applconfig/a.yml
new file mode 100644
index 00000000..bde070c0
--- /dev/null
+++ b/rtemsspec/tests/spec-applconfig/a.yml
@@ -0,0 +1,27 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+default: default a
+description: description a
+index-entries:
+- index a
+name: a
+notes: |
+ notes a
+
+ references:
+
+ * ${b:/name}
+
+ * ${.:/document-reference/terminate}
+
+ * ${func:/name}
+
+ * ${td:/name}
+appl-config-option-type: feature
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+interface-type: appl-config-option
+links:
+- role: appl-config-group-member
+ uid: g
+type: interface
diff --git a/rtemsspec/tests/spec-applconfig/b.yml b/rtemsspec/tests/spec-applconfig/b.yml
new file mode 100644
index 00000000..34b5a3c3
--- /dev/null
+++ b/rtemsspec/tests/spec-applconfig/b.yml
@@ -0,0 +1,14 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+description: description b
+index-entries: []
+name: b
+notes: null
+appl-config-option-type: feature-enable
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+interface-type: appl-config-option
+links:
+- role: appl-config-group-member
+ uid: g
+type: interface
diff --git a/rtemsspec/tests/spec-applconfig/c.yml b/rtemsspec/tests/spec-applconfig/c.yml
new file mode 100644
index 00000000..61a645c4
--- /dev/null
+++ b/rtemsspec/tests/spec-applconfig/c.yml
@@ -0,0 +1,23 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+appl-config-option-type: integer
+constraints:
+ max: 99
+ min: -1
+ texts:
+ - custom c 1
+ - custom c 2
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+default-value: 13
+description: description c
+enabled-by: true
+index-entries: []
+interface-type: appl-config-option
+links:
+- role: appl-config-group-member
+ uid: g
+- role: constraint
+ uid: d
+name: c
+notes: notes c
+type: interface
diff --git a/rtemsspec/tests/spec-applconfig/d.yml b/rtemsspec/tests/spec-applconfig/d.yml
new file mode 100644
index 00000000..93fbd416
--- /dev/null
+++ b/rtemsspec/tests/spec-applconfig/d.yml
@@ -0,0 +1,7 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links: []
+text: constraint d
+type: constraint
diff --git a/rtemsspec/tests/spec-applconfig/e.yml b/rtemsspec/tests/spec-applconfig/e.yml
new file mode 100644
index 00000000..77686aca
--- /dev/null
+++ b/rtemsspec/tests/spec-applconfig/e.yml
@@ -0,0 +1,17 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+appl-config-option-type: integer
+constraints:
+ min: -2
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+default-value: 7
+description: description e
+enabled-by: true
+index-entries: []
+interface-type: appl-config-option
+links:
+- role: appl-config-group-member
+ uid: g
+name: e
+notes: null
+type: interface
diff --git a/rtemsspec/tests/spec-applconfig/f.yml b/rtemsspec/tests/spec-applconfig/f.yml
new file mode 100644
index 00000000..9fdb0b26
--- /dev/null
+++ b/rtemsspec/tests/spec-applconfig/f.yml
@@ -0,0 +1,17 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+appl-config-option-type: integer
+constraints:
+ max: 2
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+default-value: 1
+description: description f
+enabled-by: true
+index-entries: []
+interface-type: appl-config-option
+links:
+- role: appl-config-group-member
+ uid: g
+name: f
+notes: null
+type: interface
diff --git a/rtemsspec/tests/spec-applconfig/func.yml b/rtemsspec/tests/spec-applconfig/func.yml
new file mode 100644
index 00000000..fc08ddc0
--- /dev/null
+++ b/rtemsspec/tests/spec-applconfig/func.yml
@@ -0,0 +1,21 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+brief: null
+definition:
+ default:
+ body: null
+ params: []
+ return: void
+ variants: []
+description: null
+name: func
+notes: null
+params: []
+interface-type: function
+links: []
+return:
+ return: null
+ return-values: []
+type: interface
diff --git a/rtemsspec/tests/spec-applconfig/g.yml b/rtemsspec/tests/spec-applconfig/g.yml
new file mode 100644
index 00000000..a0bccd0b
--- /dev/null
+++ b/rtemsspec/tests/spec-applconfig/g.yml
@@ -0,0 +1,9 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+description: description
+name: group name
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+interface-type: appl-config-group
+links: []
+type: interface
diff --git a/rtemsspec/tests/spec-applconfig/h.yml b/rtemsspec/tests/spec-applconfig/h.yml
new file mode 100644
index 00000000..513b49ab
--- /dev/null
+++ b/rtemsspec/tests/spec-applconfig/h.yml
@@ -0,0 +1,18 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+appl-config-option-type: integer
+constraints:
+ texts:
+ - custom h
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+default-value: 1
+description: description h
+enabled-by: true
+index-entries: []
+interface-type: appl-config-option
+links:
+- role: appl-config-group-member
+ uid: g
+name: h
+notes: null
+type: interface
diff --git a/rtemsspec/tests/spec-applconfig/i.yml b/rtemsspec/tests/spec-applconfig/i.yml
new file mode 100644
index 00000000..6d875ac0
--- /dev/null
+++ b/rtemsspec/tests/spec-applconfig/i.yml
@@ -0,0 +1,20 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+appl-config-option-type: integer
+constraints:
+ set:
+ - 1
+ - 2
+ - 3
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+default-value: 1
+description: description i
+enabled-by: true
+index-entries: []
+interface-type: appl-config-option
+links:
+- role: appl-config-group-member
+ uid: g
+name: i
+notes: null
+type: interface
diff --git a/rtemsspec/tests/spec-applconfig/j.yml b/rtemsspec/tests/spec-applconfig/j.yml
new file mode 100644
index 00000000..093c9cc4
--- /dev/null
+++ b/rtemsspec/tests/spec-applconfig/j.yml
@@ -0,0 +1,21 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+appl-config-option-type: integer
+constraints:
+ set:
+ - 1
+ - 2
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+default-value: Foo bar.
+description: description j
+enabled-by: true
+index-entries: []
+interface-type: appl-config-option
+links:
+- role: appl-config-group-member
+ uid: g
+- role: constraint
+ uid: d
+name: j
+notes: null
+type: interface
diff --git a/rtemsspec/tests/spec-applconfig/k.yml b/rtemsspec/tests/spec-applconfig/k.yml
new file mode 100644
index 00000000..6900d41c
--- /dev/null
+++ b/rtemsspec/tests/spec-applconfig/k.yml
@@ -0,0 +1,19 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+appl-config-option-type: integer
+constraints:
+ texts:
+ - custom k 1
+ - custom k 2
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+default-value: 1
+description: description k
+enabled-by: true
+index-entries: []
+interface-type: appl-config-option
+links:
+- role: appl-config-group-member
+ uid: g
+name: k
+notes: null
+type: interface
diff --git a/rtemsspec/tests/spec-applconfig/l.yml b/rtemsspec/tests/spec-applconfig/l.yml
new file mode 100644
index 00000000..1506dddc
--- /dev/null
+++ b/rtemsspec/tests/spec-applconfig/l.yml
@@ -0,0 +1,18 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+appl-config-option-type: initializer
+constraints:
+ max: 2
+ min: 0
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+default-value: 1
+description: description l
+enabled-by: true
+index-entries: []
+interface-type: appl-config-option
+links:
+- role: appl-config-group-member
+ uid: g
+name: l
+notes: null
+type: interface
diff --git a/rtemsspec/tests/spec-applconfig/m.yml b/rtemsspec/tests/spec-applconfig/m.yml
new file mode 100644
index 00000000..267f6bc4
--- /dev/null
+++ b/rtemsspec/tests/spec-applconfig/m.yml
@@ -0,0 +1,17 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+appl-config-option-type: initializer
+constraints:
+ some: garbage
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+default-value: 1
+description: description m
+enabled-by: true
+index-entries: []
+interface-type: appl-config-option
+links:
+- role: appl-config-group-member
+ uid: g
+name: m
+notes: null
+type: interface
diff --git a/rtemsspec/tests/spec-applconfig/td.yml b/rtemsspec/tests/spec-applconfig/td.yml
new file mode 100644
index 00000000..9ef5c4fa
--- /dev/null
+++ b/rtemsspec/tests/spec-applconfig/td.yml
@@ -0,0 +1,14 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+brief: null
+definition:
+ default: void ${.:/name}
+ variants: []
+description: null
+name: td
+notes: null
+interface-type: typedef
+links: []
+type: interface
diff --git a/rtemsspec/tests/spec-build/bsp.yml b/rtemsspec/tests/spec-build/bsp.yml
new file mode 100644
index 00000000..8e42c1b6
--- /dev/null
+++ b/rtemsspec/tests/spec-build/bsp.yml
@@ -0,0 +1,15 @@
+arch: foo
+bsp: bar
+build-type: bsp
+enabled-by: true
+install:
+- source:
+ - abc
+links:
+- role: null
+ uid: o
+- role: null
+ uid: d
+source:
+- def
+type: build
diff --git a/rtemsspec/tests/spec-build/bsp2.yml b/rtemsspec/tests/spec-build/bsp2.yml
new file mode 100644
index 00000000..7488559f
--- /dev/null
+++ b/rtemsspec/tests/spec-build/bsp2.yml
@@ -0,0 +1,11 @@
+arch: foo
+bsp: bla
+build-type: bsp
+enabled-by: true
+install:
+- source:
+ - blub
+links: []
+source:
+- nada
+type: build
diff --git a/rtemsspec/tests/spec-build/d.yml b/rtemsspec/tests/spec-build/d.yml
new file mode 100644
index 00000000..b07f55bd
--- /dev/null
+++ b/rtemsspec/tests/spec-build/d.yml
@@ -0,0 +1,7 @@
+build-type: start-file
+enabled-by:
+- not: A
+links: []
+source:
+- pqr
+type: build
diff --git a/rtemsspec/tests/spec-build/g.yml b/rtemsspec/tests/spec-build/g.yml
new file mode 100644
index 00000000..de26b4d5
--- /dev/null
+++ b/rtemsspec/tests/spec-build/g.yml
@@ -0,0 +1,7 @@
+build-type: group
+enabled-by: true
+install:
+- source:
+ - ghi
+links: []
+type: build
diff --git a/rtemsspec/tests/spec-build/o.yml b/rtemsspec/tests/spec-build/o.yml
new file mode 100644
index 00000000..3b45d873
--- /dev/null
+++ b/rtemsspec/tests/spec-build/o.yml
@@ -0,0 +1,11 @@
+build-type: objects
+enabled-by: true
+install:
+- source:
+ - jkl
+links:
+- role: null
+ uid: s
+source:
+- mno
+type: build
diff --git a/rtemsspec/tests/spec-build/s.yml b/rtemsspec/tests/spec-build/s.yml
new file mode 100644
index 00000000..a0b7eae7
--- /dev/null
+++ b/rtemsspec/tests/spec-build/s.yml
@@ -0,0 +1,7 @@
+build-type: start-file
+enabled-by:
+- A
+links: []
+source:
+- stu
+type: build
diff --git a/rtemsspec/tests/spec-build/x.yml b/rtemsspec/tests/spec-build/x.yml
new file mode 100644
index 00000000..26e98206
--- /dev/null
+++ b/rtemsspec/tests/spec-build/x.yml
@@ -0,0 +1,3 @@
+enabled-by: true
+links: []
+type: nix
diff --git a/rtemsspec/tests/spec-doc/a.yml b/rtemsspec/tests/spec-doc/a.yml
new file mode 100644
index 00000000..2384ae3c
--- /dev/null
+++ b/rtemsspec/tests/spec-doc/a.yml
@@ -0,0 +1,25 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+- role: spec-refinement
+ spec-key: type
+ spec-value: spec
+ uid: root
+spec-description: null
+spec-example: |
+ a: null
+spec-info:
+ dict:
+ attributes:
+ a:
+ description: null
+ spec-type: a
+ description: null
+ mandatory-attributes: []
+spec-name: A
+spec-type: a
+type: spec
diff --git a/rtemsspec/tests/spec-doc/b.yml b/rtemsspec/tests/spec-doc/b.yml
new file mode 100644
index 00000000..7ee8daa2
--- /dev/null
+++ b/rtemsspec/tests/spec-doc/b.yml
@@ -0,0 +1,29 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+- role: spec-refinement
+ spec-key: type
+ spec-value: b
+ uid: root
+- role: spec-refinement
+ spec-key: d1
+ spec-value: b
+ uid: d
+spec-description: null
+spec-example: null
+spec-info:
+ dict:
+ attributes: {}
+ description: null
+ generic-attributes:
+ description: null
+ key-spec-type: name
+ value-spec-type: list-only
+ mandatory-attributes: all
+spec-name: B
+spec-type: b
+type: spec
diff --git a/rtemsspec/tests/spec-doc/c.yml b/rtemsspec/tests/spec-doc/c.yml
new file mode 100644
index 00000000..314bda78
--- /dev/null
+++ b/rtemsspec/tests/spec-doc/c.yml
@@ -0,0 +1,21 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+spec-description: null
+spec-example: null
+spec-info:
+ dict:
+ attributes:
+ c:
+ description: null
+ spec-type: float
+ description: null
+ mandatory-attributes:
+ - c
+spec-name: C
+spec-type: c
+type: spec
diff --git a/rtemsspec/tests/spec-doc/d.yml b/rtemsspec/tests/spec-doc/d.yml
new file mode 100644
index 00000000..38abd2ea
--- /dev/null
+++ b/rtemsspec/tests/spec-doc/d.yml
@@ -0,0 +1,25 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+spec-description: null
+spec-example: null
+spec-info:
+ dict:
+ attributes:
+ d1:
+ description: null
+ spec-type: name
+ d2:
+ description: null
+ spec-type: none
+ description: null
+ mandatory-attributes:
+ - d1
+ - d2
+spec-name: D
+spec-type: d
+type: spec
diff --git a/rtemsspec/tests/spec-doc/list-only.yml b/rtemsspec/tests/spec-doc/list-only.yml
new file mode 100644
index 00000000..5cd964c7
--- /dev/null
+++ b/rtemsspec/tests/spec-doc/list-only.yml
@@ -0,0 +1,16 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+spec-description: null
+spec-example: null
+spec-info:
+ list:
+ description: null
+ spec-type: str
+spec-name: List Only
+spec-type: list-only
+type: spec
diff --git a/rtemsspec/tests/spec-doc/root.yml b/rtemsspec/tests/spec-doc/root.yml
new file mode 100644
index 00000000..c6fa898a
--- /dev/null
+++ b/rtemsspec/tests/spec-doc/root.yml
@@ -0,0 +1,93 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links: []
+spec-description: null
+spec-example: null
+spec-info:
+ bool:
+ assert: true
+ description: |
+ A reference to ${.:/spec-name}.
+ dict:
+ attributes:
+ type:
+ description: null
+ spec-type: name
+ description: null
+ generic-attributes:
+ description: null
+ key-spec-type: name
+ value-spec-type: root
+ mandatory-attributes: all
+ float:
+ assert:
+ eq: 0.0
+ description: null
+ spec-type: float
+ int:
+ assert:
+ eq: 0
+ description: null
+ spec-type: int
+ list:
+ description: null
+ spec-type: root
+ none: null
+ str:
+ assert:
+ - and:
+ - contains:
+ - a
+ - b
+ - c
+ - eq: d
+ - ge: e
+ - gt: f
+ - in:
+ - g
+ - h
+ - le: i
+ - lt: j
+ - ne: k
+ - re: l
+ - true
+ - uid: null
+ - or:
+ - and:
+ - in: []
+ - in:
+ - x
+ - not:
+ - not:
+ contains:
+ - a
+ - b
+ - c
+ - not:
+ eq: d
+ - not:
+ ge: e
+ - not:
+ gt: f
+ - not:
+ in:
+ - g
+ - h
+ - not:
+ le: i
+ - not:
+ lt: j
+ - not:
+ ne: k
+ - not:
+ re: l
+ - not: true
+ - not:
+ uid: null
+ description: null
+ spec-type: str
+spec-name: Root
+spec-type: root
+type: spec
diff --git a/rtemsspec/tests/spec-glossary/doc.rst b/rtemsspec/tests/spec-glossary/doc.rst
new file mode 100644
index 00000000..e07b9a35
--- /dev/null
+++ b/rtemsspec/tests/spec-glossary/doc.rst
@@ -0,0 +1 @@
+:term:`T`
diff --git a/rtemsspec/tests/spec-glossary/g.yml b/rtemsspec/tests/spec-glossary/g.yml
new file mode 100644
index 00000000..86797bfc
--- /dev/null
+++ b/rtemsspec/tests/spec-glossary/g.yml
@@ -0,0 +1,7 @@
+SPDX-License-Identifier: CC-BY-SA-4.0
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+glossary-type: group
+links: []
+name: G
+type: glossary
diff --git a/rtemsspec/tests/spec-glossary/glossary.rst b/rtemsspec/tests/spec-glossary/glossary.rst
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/rtemsspec/tests/spec-glossary/glossary.rst
diff --git a/rtemsspec/tests/spec-glossary/glossary/t.yml b/rtemsspec/tests/spec-glossary/glossary/t.yml
new file mode 100644
index 00000000..0b82d582
--- /dev/null
+++ b/rtemsspec/tests/spec-glossary/glossary/t.yml
@@ -0,0 +1,12 @@
+SPDX-License-Identifier: CC-BY-SA-4.0
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+term: T
+glossary-type: term
+links:
+- role: null
+ uid: ../g
+text: |
+ Term text $$${/glossary/u:/term} ${/glossary/t:/term}
+ ${/glossary/u:/glossary-type}.
+type: glossary
diff --git a/rtemsspec/tests/spec-glossary/glossary/u.yml b/rtemsspec/tests/spec-glossary/glossary/u.yml
new file mode 100644
index 00000000..1ce28ff8
--- /dev/null
+++ b/rtemsspec/tests/spec-glossary/glossary/u.yml
@@ -0,0 +1,11 @@
+SPDX-License-Identifier: CC-BY-SA-4.0
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+term: U
+glossary-type: term
+links:
+- role: null
+ uid: ../g
+text: |
+ Term text U.
+type: glossary
diff --git a/rtemsspec/tests/spec-glossary/glossary/v.yml b/rtemsspec/tests/spec-glossary/glossary/v.yml
new file mode 100644
index 00000000..dca475a7
--- /dev/null
+++ b/rtemsspec/tests/spec-glossary/glossary/v.yml
@@ -0,0 +1,11 @@
+SPDX-License-Identifier: CC-BY-SA-4.0
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+term: V
+glossary-type: term
+links:
+- role: null
+ uid: ../g
+text: |
+ Term text V.
+type: glossary
diff --git a/rtemsspec/tests/spec-interface/asm.yml b/rtemsspec/tests/spec-interface/asm.yml
new file mode 100644
index 00000000..2d4fe860
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/asm.yml
@@ -0,0 +1,18 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+brief: null
+definition:
+ defaults: null
+ variants: []
+description: null
+name: ASM
+notes: null
+interface-type: define
+links:
+- role: interface-placement
+ uid: command-line
+- role: other
+ uid: command-line
+type: interface
diff --git a/rtemsspec/tests/spec-interface/command-line.yml b/rtemsspec/tests/spec-interface/command-line.yml
new file mode 100644
index 00000000..5ecd875f
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/command-line.yml
@@ -0,0 +1,9 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+interface-type: container
+links:
+- role: interface-placement
+ uid: domain-command-line
+type: interface
diff --git a/rtemsspec/tests/spec-interface/define-a.yml b/rtemsspec/tests/spec-interface/define-a.yml
new file mode 100644
index 00000000..6078f749
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/define-a.yml
@@ -0,0 +1,18 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+brief: null
+definition:
+ default: ''
+ variants: []
+description: null
+name: A
+notes: null
+interface-type: define
+links:
+- role: interface-placement
+ uid: h2
+- role: interface-ingroup
+ uid: ga
+type: interface
diff --git a/rtemsspec/tests/spec-interface/define-b.yml b/rtemsspec/tests/spec-interface/define-b.yml
new file mode 100644
index 00000000..3136db5e
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/define-b.yml
@@ -0,0 +1,18 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+brief: null
+definition:
+ default: '1'
+ variants: []
+description: null
+name: B
+notes: null
+interface-type: define
+links:
+- role: interface-placement
+ uid: h2
+- role: interface-ingroup
+ uid: ga
+type: interface
diff --git a/rtemsspec/tests/spec-interface/define-c.yml b/rtemsspec/tests/spec-interface/define-c.yml
new file mode 100644
index 00000000..1a515a29
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/define-c.yml
@@ -0,0 +1,18 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+brief: null
+definition:
+ default: '2'
+ variants: []
+description: null
+name: C
+notes: null
+interface-type: define
+links:
+- role: interface-placement
+ uid: h2
+- role: interface-ingroup
+ uid: ga
+type: interface
diff --git a/rtemsspec/tests/spec-interface/define.yml b/rtemsspec/tests/spec-interface/define.yml
new file mode 100644
index 00000000..2f72c770
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/define.yml
@@ -0,0 +1,29 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+brief: null
+definition:
+ default: |
+ \
+ ((${float_t:/name}) 123)
+ variants:
+ - definition: ((${float_t:/name}) 456)
+ enabled-by:
+ - defined(${define-a:/name})
+ - (${define-b:/name} > ${define-c:/name})
+ - definition: ((${float_t:/name}) 789)
+ enabled-by:
+ and:
+ - defined(C)
+ - defined(D)
+description: null
+name: DEFINE
+notes: null
+interface-type: define
+links:
+- role: interface-placement
+ uid: h
+- role: interface-ingroup
+ uid: ga
+type: interface
diff --git a/rtemsspec/tests/spec-interface/domain-abc.yml b/rtemsspec/tests/spec-interface/domain-abc.yml
new file mode 100644
index 00000000..e878fb8c
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/domain-abc.yml
@@ -0,0 +1,9 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+description: ABC
+enabled-by: true
+interface-type: domain
+links: []
+name: ABC
+type: interface
diff --git a/rtemsspec/tests/spec-interface/domain-c.yml b/rtemsspec/tests/spec-interface/domain-c.yml
new file mode 100644
index 00000000..9eaea624
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/domain-c.yml
@@ -0,0 +1,9 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+description: C
+enabled-by: true
+interface-type: domain
+links: []
+name: C
+type: interface
diff --git a/rtemsspec/tests/spec-interface/domain-command-line.yml b/rtemsspec/tests/spec-interface/domain-command-line.yml
new file mode 100644
index 00000000..ba2a3a42
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/domain-command-line.yml
@@ -0,0 +1,9 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+description: Command Line
+enabled-by: true
+interface-type: domain
+links: []
+name: Command Line
+type: interface
diff --git a/rtemsspec/tests/spec-interface/enum.yml b/rtemsspec/tests/spec-interface/enum.yml
new file mode 100644
index 00000000..f3bcbc17
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/enum.yml
@@ -0,0 +1,22 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+brief: Enum brief description.
+definition-kind: typedef-only
+description: Enum description.
+name: Enum
+notes: null
+interface-type: enum
+links:
+- role: interface-placement
+ uid: h
+- role: interface-ingroup
+ uid: gb
+- role: interface-enumerator
+ uid: enumerator-0
+- role: interface-enumerator
+ uid: enumerator-1
+- role: interface-enumerator
+ uid: enumerator-2
+type: interface
diff --git a/rtemsspec/tests/spec-interface/enum2.yml b/rtemsspec/tests/spec-interface/enum2.yml
new file mode 100644
index 00000000..54ac9c21
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/enum2.yml
@@ -0,0 +1,20 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+brief: Enum A brief description.
+definition-kind: enum-only
+description: null
+name: EnumA
+notes: null
+interface-type: enum
+links:
+- role: interface-placement
+ uid: h3
+- role: interface-enumerator
+ uid: enumerator-a
+- role: other
+ uid: enumerator-a
+- role: interface-ingroup
+ uid: gb
+type: interface
diff --git a/rtemsspec/tests/spec-interface/enum3.yml b/rtemsspec/tests/spec-interface/enum3.yml
new file mode 100644
index 00000000..eaaf1400
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/enum3.yml
@@ -0,0 +1,18 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+brief: Enum B brief description.
+definition-kind: typedef-and-enum
+description: null
+name: EnumB
+notes: null
+interface-type: enum
+links:
+- role: interface-enumerator
+ uid: enumerator-b
+- role: interface-placement
+ uid: h
+- role: interface-ingroup
+ uid: gb
+type: interface
diff --git a/rtemsspec/tests/spec-interface/enum4.yml b/rtemsspec/tests/spec-interface/enum4.yml
new file mode 100644
index 00000000..94297498
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/enum4.yml
@@ -0,0 +1,14 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+brief: Enum C brief description.
+definition-kind: typedef-and-enum
+description: null
+name: EnumC
+notes: null
+interface-type: enum
+links:
+- role: interface-placement
+ uid: h2
+type: interface
diff --git a/rtemsspec/tests/spec-interface/enumerator-0.yml b/rtemsspec/tests/spec-interface/enumerator-0.yml
new file mode 100644
index 00000000..7937340e
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/enumerator-0.yml
@@ -0,0 +1,14 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+brief: Enumerator 0 brief description.
+definition:
+ default: null
+ variants: []
+description: null
+name: ENUMERATOR_0
+notes: null
+interface-type: enumerator
+links: []
+type: interface
diff --git a/rtemsspec/tests/spec-interface/enumerator-1.yml b/rtemsspec/tests/spec-interface/enumerator-1.yml
new file mode 100644
index 00000000..83840471
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/enumerator-1.yml
@@ -0,0 +1,14 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+brief: Enumerator 1 brief description.
+definition:
+ default: null
+ variants: []
+description: null
+name: ENUMERATOR_1
+notes: null
+interface-type: enumerator
+links: []
+type: interface
diff --git a/rtemsspec/tests/spec-interface/enumerator-2.yml b/rtemsspec/tests/spec-interface/enumerator-2.yml
new file mode 100644
index 00000000..7dd4e7b1
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/enumerator-2.yml
@@ -0,0 +1,14 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+brief: Enumerator 2 brief description.
+definition:
+ default: null
+ variants: []
+description: null
+name: ENUMERATOR_2
+notes: null
+interface-type: enumerator
+links: []
+type: interface
diff --git a/rtemsspec/tests/spec-interface/enumerator-a.yml b/rtemsspec/tests/spec-interface/enumerator-a.yml
new file mode 100644
index 00000000..c5018b70
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/enumerator-a.yml
@@ -0,0 +1,14 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+brief: Enumerator A brief description.
+definition:
+ default: '17'
+ variants: []
+description: null
+name: ENUMERATOR_A
+notes: null
+interface-type: enumerator
+links: []
+type: interface
diff --git a/rtemsspec/tests/spec-interface/enumerator-b.yml b/rtemsspec/tests/spec-interface/enumerator-b.yml
new file mode 100644
index 00000000..9bcac74f
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/enumerator-b.yml
@@ -0,0 +1,14 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+brief: Enumerator B brief description.
+definition:
+ default: ${enumerator-a:/name}
+ variants: []
+description: null
+name: ENUMERATOR_B
+notes: null
+interface-type: enumerator
+links: []
+type: interface
diff --git a/rtemsspec/tests/spec-interface/float_t.yml b/rtemsspec/tests/spec-interface/float_t.yml
new file mode 100644
index 00000000..b390553d
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/float_t.yml
@@ -0,0 +1,10 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+name: float_t
+interface-type: typedef
+links:
+- role: interface-placement
+ uid: math
+type: interface
diff --git a/rtemsspec/tests/spec-interface/forward-decl.yml b/rtemsspec/tests/spec-interface/forward-decl.yml
new file mode 100644
index 00000000..31f104ad
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/forward-decl.yml
@@ -0,0 +1,11 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+interface-type: forward-declaration
+links:
+- role: interface-placement
+ uid: h
+- role: interface-target
+ uid: s
+type: interface
diff --git a/rtemsspec/tests/spec-interface/func.yml b/rtemsspec/tests/spec-interface/func.yml
new file mode 100644
index 00000000..af2f1598
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/func.yml
@@ -0,0 +1,44 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+brief: Function brief description.
+definition:
+ default:
+ body: null
+ params:
+ - int ${.:/params[0]/name}
+ - const int *${.:/params[1]/name}
+ - int *${.:/params[2]/name}
+ - int *${.:/params[3]/name}
+ return: void
+ variants: []
+description: |
+ Function description. References to ${func2:/name}, ${td:/name},
+ ${enum:/name}, ${define:/name}, ${macro:/name}, ${var:/name},
+ ${enumerator-0:/name}, ${s:/name}, ${option:/name}, and ${option:/type}.
+name: Function
+notes: null
+params:
+- description: is parameter 0.
+ dir: null
+ name: Param0
+- description: is parameter 1.
+ dir: in
+ name: Param1
+- description: is parameter 2.
+ dir: out
+ name: Param2
+- description: is parameter 3.
+ dir: inout
+ name: Param3
+interface-type: function
+links:
+- role: interface-placement
+ uid: h
+- role: interface-ingroup
+ uid: ga
+return:
+ return: null
+ return-values: []
+type: interface
diff --git a/rtemsspec/tests/spec-interface/func2.yml b/rtemsspec/tests/spec-interface/func2.yml
new file mode 100644
index 00000000..295faeac
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/func2.yml
@@ -0,0 +1,53 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+brief: Very long function brief description.
+definition:
+ default:
+ body: |
+ (void) ${.:/params[1]/name};
+ (void) ${.:/params[2]/name};
+ (void) ${.:/params[3]/name};
+ return ${.:/params[0]/name} + 1;
+ params:
+ - int ${.:/params[0]/name}
+ - const ${forward-decl:/name} *${.:/params[1]/name}
+ - ${forward-decl:/name} *( *${.:/params[2]/name} )( void )
+ - ${forward-decl:/name} *${.:/params[3]/name}
+ return: int
+ variants: []
+description: |
+ VeryLongFunction description.
+name: VeryLongFunction
+notes: |
+ VeryLongFunction notes.
+params:
+- description: |
+ is very long parameter 0 with some super important and extra very long
+ description which makes a lot of sense.
+ dir: null
+ name: VeryLongParam0
+- description: is very long parameter 1.
+ dir: in
+ name: VeryLongParam1
+- description: is very long parameter 2.
+ dir: out
+ name: VeryLongParam2
+- description: is very long parameter 3.
+ dir: inout
+ name: VeryLongParam3
+return:
+ return: Sometimes some value. See ${func:/name}.
+ return-values:
+ - description: is returned, in case A.
+ value: 1
+ - description: is returned, in case B.
+ value: 2
+interface-type: function
+links:
+- role: interface-placement
+ uid: h
+- role: interface-ingroup
+ uid: gb
+type: interface
diff --git a/rtemsspec/tests/spec-interface/func3.yml b/rtemsspec/tests/spec-interface/func3.yml
new file mode 100644
index 00000000..28b30eb1
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/func3.yml
@@ -0,0 +1,27 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+brief: null
+definition:
+ default: null
+ variants:
+ - enabled-by: true
+ definition:
+ body: null
+ params: []
+ return: void
+description: null
+name: VoidFunction
+notes: null
+params: []
+return:
+ return: null
+ return-values: []
+interface-type: function
+links:
+- role: interface-placement
+ uid: h4
+- role: interface-ingroup
+ uid: gb
+type: interface
diff --git a/rtemsspec/tests/spec-interface/func4.yml b/rtemsspec/tests/spec-interface/func4.yml
new file mode 100644
index 00000000..4e033fe9
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/func4.yml
@@ -0,0 +1,30 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+brief: Function brief description with very long return type.
+definition:
+ default:
+ body: null
+ params: []
+ return: VeryLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongType
+ variants:
+ - enabled-by: true
+ definition:
+ body: null
+ params: []
+ return: NotSoVeryLongType
+description: null
+name: VeryLongTypeFunction
+notes: null
+params: []
+return:
+ return: This function returns an object with a very long type.
+ return-values: []
+interface-type: function
+links:
+- role: interface-placement
+ uid: h4
+- role: interface-ingroup
+ uid: gb
+type: interface
diff --git a/rtemsspec/tests/spec-interface/ga.yml b/rtemsspec/tests/spec-interface/ga.yml
new file mode 100644
index 00000000..7d934baa
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/ga.yml
@@ -0,0 +1,13 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+brief: Group A brief description.
+description: Group A description.
+identifier: GroupA
+name: Group A
+interface-type: group
+links:
+- role: interface-placement
+ uid: h
+type: interface
diff --git a/rtemsspec/tests/spec-interface/gb.yml b/rtemsspec/tests/spec-interface/gb.yml
new file mode 100644
index 00000000..9a363756
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/gb.yml
@@ -0,0 +1,17 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+brief: null
+description: null
+identifier: GroupB
+name: Group B
+interface-type: group
+links:
+- role: interface-ingroup
+ uid: ga
+- role: other
+ uid: other
+- role: interface-placement
+ uid: h
+type: interface
diff --git a/rtemsspec/tests/spec-interface/gc.yml b/rtemsspec/tests/spec-interface/gc.yml
new file mode 100644
index 00000000..c1428b82
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/gc.yml
@@ -0,0 +1,13 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+brief: Group C brief description.
+description: Group C description.
+identifier: GroupC
+name: Group C
+interface-type: group
+links:
+- role: interface-placement
+ uid: h2
+type: interface
diff --git a/rtemsspec/tests/spec-interface/h.yml b/rtemsspec/tests/spec-interface/h.yml
new file mode 100644
index 00000000..0733837a
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/h.yml
@@ -0,0 +1,28 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: []
+interface-type: header-file
+links:
+- enabled-by: RTEMS_SMP
+ role: interface-include
+ uid: h2
+- enabled-by: []
+ role: interface-include
+ uid: h4
+- enabled-by: RTEMS_SMP
+ role: interface-include
+ uid: h4
+- enabled-by: ASM
+ role: interface-include
+ uid: h4
+- enabled-by: []
+ role: interface-include
+ uid: h4
+- role: other
+ uid: h4
+- role: interface-placement
+ uid: domain-abc
+path: h.h
+prefix: include
+type: interface
diff --git a/rtemsspec/tests/spec-interface/h2.yml b/rtemsspec/tests/spec-interface/h2.yml
new file mode 100644
index 00000000..194e182e
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/h2.yml
@@ -0,0 +1,12 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by:
+ not: ASM
+interface-type: header-file
+links:
+- role: interface-placement
+ uid: domain-abc
+path: h2.h
+prefix: include
+type: interface
diff --git a/rtemsspec/tests/spec-interface/h3.yml b/rtemsspec/tests/spec-interface/h3.yml
new file mode 100644
index 00000000..fc61a1a5
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/h3.yml
@@ -0,0 +1,11 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+interface-type: header-file
+links:
+- role: interface-placement
+ uid: domain-abc
+path: h3.h
+prefix: include
+type: interface
diff --git a/rtemsspec/tests/spec-interface/h4.yml b/rtemsspec/tests/spec-interface/h4.yml
new file mode 100644
index 00000000..6233efe5
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/h4.yml
@@ -0,0 +1,11 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+interface-type: header-file
+links:
+- role: interface-placement
+ uid: domain-abc
+path: h4.h
+prefix: include
+type: interface
diff --git a/rtemsspec/tests/spec-interface/macro.yml b/rtemsspec/tests/spec-interface/macro.yml
new file mode 100644
index 00000000..a1e0e788
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/macro.yml
@@ -0,0 +1,46 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+brief: Very long macro brief description.
+definition:
+ default: |
+ do {
+ (void) ${.:/params[1]/name};
+ (void) ${.:/params[2]/name};
+ (void) ${.:/params[3]/name};
+ } while ( 0 );
+ ${.:/params[0]/name} + 1;
+ variants: []
+description: null
+name: VERY_LONG_MACRO
+notes: null
+params:
+- description: |
+ is very long parameter 0 with some super important and extra very long
+ description which makes a lot of sense.
+ dir: null
+ name: VeryLongParam0
+- description: is very long parameter 1.
+ dir: in
+ name: VeryLongParam1
+- description: is very long parameter 2.
+ dir: out
+ name: VeryLongParam2
+- description: is very long parameter 3.
+ dir: inout
+ name: VeryLongParam3
+return:
+ return: Sometimes some value.
+ return-values:
+ - description: is returned, in case A.
+ value: 1
+ - description: is returned, in case B.
+ value: 2
+interface-type: macro
+links:
+- role: interface-placement
+ uid: h
+- role: interface-ingroup
+ uid: gb
+type: interface
diff --git a/rtemsspec/tests/spec-interface/macro2.yml b/rtemsspec/tests/spec-interface/macro2.yml
new file mode 100644
index 00000000..36e05169
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/macro2.yml
@@ -0,0 +1,29 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+brief: Short macro brief description.
+definition:
+ default: |
+ ( ( ${.:/params[0]/name} ) + 1 )
+ variants:
+ - definition: ''
+ enabled-by: '0'
+description: null
+name: MACRO
+notes: null
+params:
+- description: |
+ is parameter 0.
+ dir: null
+ name: Param0
+return:
+ return: Sometimes some value.
+ return-values: []
+interface-type: macro
+links:
+- role: interface-placement
+ uid: h
+- role: interface-ingroup
+ uid: gb
+type: interface
diff --git a/rtemsspec/tests/spec-interface/macro3.yml b/rtemsspec/tests/spec-interface/macro3.yml
new file mode 100644
index 00000000..d99179c5
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/macro3.yml
@@ -0,0 +1,22 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+brief: Macro without parameters.
+definition:
+ default: '0'
+ variants: []
+description: null
+name: MACRO
+notes: null
+params: []
+return:
+ return: null
+ return-values: []
+interface-type: macro
+links:
+- role: interface-placement
+ uid: h2
+- role: interface-ingroup
+ uid: gb
+type: interface
diff --git a/rtemsspec/tests/spec-interface/math.yml b/rtemsspec/tests/spec-interface/math.yml
new file mode 100644
index 00000000..020449fa
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/math.yml
@@ -0,0 +1,11 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+interface-type: header-file
+links:
+- role: interface-placement
+ uid: domain-c
+path: math.h
+prefix: null
+type: interface
diff --git a/rtemsspec/tests/spec-interface/option.yml b/rtemsspec/tests/spec-interface/option.yml
new file mode 100644
index 00000000..7580bb8e
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/option.yml
@@ -0,0 +1,14 @@
+SPDX-License-Identifier: CC-BY-SA-4.0
+default: default a
+description: description a
+index-entries:
+- index a
+name: a
+notes: notes a
+appl-config-option-type: feature
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+interface-type: appl-config-option
+links: []
+type: interface
diff --git a/rtemsspec/tests/spec-interface/other.yml b/rtemsspec/tests/spec-interface/other.yml
new file mode 100644
index 00000000..02b17731
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/other.yml
@@ -0,0 +1,6 @@
+comment: Some comment.
+enabled-by: true
+links:
+- role: other
+ uid: h
+type: other
diff --git a/rtemsspec/tests/spec-interface/other2.yml b/rtemsspec/tests/spec-interface/other2.yml
new file mode 100644
index 00000000..2678ce82
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/other2.yml
@@ -0,0 +1,3 @@
+enabled-by: true
+links: []
+type: other
diff --git a/rtemsspec/tests/spec-interface/other3.yml b/rtemsspec/tests/spec-interface/other3.yml
new file mode 100644
index 00000000..2678ce82
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/other3.yml
@@ -0,0 +1,3 @@
+enabled-by: true
+links: []
+type: other
diff --git a/rtemsspec/tests/spec-interface/s.yml b/rtemsspec/tests/spec-interface/s.yml
new file mode 100644
index 00000000..bd040ed9
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/s.yml
@@ -0,0 +1,59 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+brief: null
+definition:
+- default:
+ brief: Brief union description.
+ definition:
+ - default:
+ brief: Brief member description.
+ definition: ${uint32_t:/name} ${.:name}
+ description: Member description.
+ kind: member
+ name: some_member
+ variants: []
+ - default:
+ brief: Brief struct description.
+ definition:
+ - default:
+ brief: Brief member 2 description.
+ definition: ${uint32_t:/name} ${.:name}
+ description: Member 2 description.
+ kind: member
+ name: some_member_2
+ variants: []
+ - default:
+ brief: Brief member 3 description.
+ definition: ${enum:/name} ${.:name}
+ description: Member 3 description.
+ kind: member
+ name: some_member_3
+ variants: []
+ description: struct description.
+ kind: struct
+ name: some_struct
+ variants: []
+ description: Union description.
+ kind: union
+ name: some_union
+ variants: []
+- default:
+ brief: Brief member 4 description.
+ definition: ${enum:/name} ${.:name}
+ description: Member 4 description.
+ kind: member
+ name: some_member_4
+ variants: []
+definition-kind: struct-only
+description: null
+name: Struct
+notes: null
+interface-type: struct
+links:
+- role: interface-placement
+ uid: h
+- role: interface-ingroup
+ uid: gc
+type: interface
diff --git a/rtemsspec/tests/spec-interface/smp.yml b/rtemsspec/tests/spec-interface/smp.yml
new file mode 100644
index 00000000..dcf41bc0
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/smp.yml
@@ -0,0 +1,10 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+name: RTEMS_SMP
+interface-type: define
+links:
+- role: interface-placement
+ uid: command-line
+type: interface
diff --git a/rtemsspec/tests/spec-interface/stdint.yml b/rtemsspec/tests/spec-interface/stdint.yml
new file mode 100644
index 00000000..559202c3
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/stdint.yml
@@ -0,0 +1,11 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+interface-type: header-file
+links:
+- role: interface-placement
+ uid: domain-c
+path: stdint.h
+prefix: null
+type: interface
diff --git a/rtemsspec/tests/spec-interface/td.yml b/rtemsspec/tests/spec-interface/td.yml
new file mode 100644
index 00000000..42f6305e
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/td.yml
@@ -0,0 +1,21 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+brief: Typedef Integer brief description.
+definition:
+ default: |
+ ${uint32_t:/name} ${.:/name} /* ${other:/comment} */
+ variants: []
+description: Typedef Integer description.
+name: Integer
+notes: null
+interface-type: typedef
+links:
+- role: other
+ uid: other
+- role: interface-ingroup
+ uid: gb
+- role: interface-placement
+ uid: h
+type: interface
diff --git a/rtemsspec/tests/spec-interface/td3.yml b/rtemsspec/tests/spec-interface/td3.yml
new file mode 100644
index 00000000..533976b2
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/td3.yml
@@ -0,0 +1,21 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+brief: null
+definition:
+ default: null
+ variants:
+ - definition: |
+ ${uint32_t:/name} ${.:/name}
+ enabled-by: defined(RTEMS_SMP)
+description: null
+name: Integer3
+notes: null
+interface-type: typedef
+links:
+- role: interface-placement
+ uid: h
+- role: interface-ingroup
+ uid: gb
+type: interface
diff --git a/rtemsspec/tests/spec-interface/uint32_t.yml b/rtemsspec/tests/spec-interface/uint32_t.yml
new file mode 100644
index 00000000..a15c93a6
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/uint32_t.yml
@@ -0,0 +1,12 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+name: uint32_t
+interface-type: typedef
+links:
+- role: other
+ uid: other3
+- role: interface-placement
+ uid: stdint
+type: interface
diff --git a/rtemsspec/tests/spec-interface/var.yml b/rtemsspec/tests/spec-interface/var.yml
new file mode 100644
index 00000000..191dbfa3
--- /dev/null
+++ b/rtemsspec/tests/spec-interface/var.yml
@@ -0,0 +1,20 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by:
+ not: ASM
+brief: Variable brief description.
+definition:
+ default: |
+ ${forward-decl:/name} *${.:/name}
+ variants: []
+description: Variable description.
+name: Variable
+notes: null
+interface-type: variable
+links:
+- role: interface-ingroup
+ uid: gc
+- role: interface-placement
+ uid: h
+type: interface
diff --git a/rtemsspec/tests/spec-item-cache/.z.yml b/rtemsspec/tests/spec-item-cache/.z.yml
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/rtemsspec/tests/spec-item-cache/.z.yml
diff --git a/rtemsspec/tests/spec-item-cache/d/c.yml b/rtemsspec/tests/spec-item-cache/d/c.yml
new file mode 100644
index 00000000..f901443f
--- /dev/null
+++ b/rtemsspec/tests/spec-item-cache/d/c.yml
@@ -0,0 +1,13 @@
+a:
+ b: e
+ f:
+ - 1
+ - 2
+ - 3
+ - g:
+ - 4
+ - 5
+links:
+- role: null
+ uid: ../p
+v: c
diff --git a/rtemsspec/tests/spec-item-cache/empty.txt b/rtemsspec/tests/spec-item-cache/empty.txt
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/rtemsspec/tests/spec-item-cache/empty.txt
diff --git a/rtemsspec/tests/spec-item-cache/p.yml b/rtemsspec/tests/spec-item-cache/p.yml
new file mode 100644
index 00000000..4c3a2964
--- /dev/null
+++ b/rtemsspec/tests/spec-item-cache/p.yml
@@ -0,0 +1,4 @@
+links: []
+v: p
+x:
+ y: z
diff --git a/rtemsspec/tests/spec-sphinx/x.yml b/rtemsspec/tests/spec-sphinx/x.yml
new file mode 100644
index 00000000..1dd0b7c2
--- /dev/null
+++ b/rtemsspec/tests/spec-sphinx/x.yml
@@ -0,0 +1,4 @@
+term: y
+glossary-type: term
+links: []
+type: glossary
diff --git a/rtemsspec/tests/spec-sphinx/y.yml b/rtemsspec/tests/spec-sphinx/y.yml
new file mode 100644
index 00000000..79a22535
--- /dev/null
+++ b/rtemsspec/tests/spec-sphinx/y.yml
@@ -0,0 +1,3 @@
+name: foobar
+links: []
+type: other
diff --git a/rtemsspec/tests/spec-validation/action2.yml b/rtemsspec/tests/spec-validation/action2.yml
new file mode 100644
index 00000000..caafed70
--- /dev/null
+++ b/rtemsspec/tests/spec-validation/action2.yml
@@ -0,0 +1,146 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+functional-type: action
+links: []
+post-conditions:
+- name: A
+ states:
+ - name: X
+ test-code: |
+ /* Post A X */
+ text: |
+ Post A X.
+ - name: Y
+ test-code: |
+ /* Post A Y */
+ text: |
+ Post A Y.
+ test-epilogue: |
+ /* Post A epilogue. */
+ test-prologue: |
+ /* Post A prologue. */
+- name: B
+ states:
+ - name: X
+ test-code: |
+ /* Post B X */
+ text: |
+ Post B X.
+ - name: Y
+ test-code: |
+ /* Post B Y */
+ text: |
+ Post B Y.
+ test-epilogue: |
+ /* Post B epilogue. */
+ test-prologue: |
+ /* Post B prologue. */
+pre-conditions:
+- name: A
+ states:
+ - name: X
+ test-code: |
+ /* Pre A X */
+ text: |
+ Pre A X.
+ - name: Y
+ test-code: |
+ /* Pre A Y */
+ text: |
+ Pre A Y.
+ test-epilogue: |
+ /* Pre A epilogue. */
+ test-prologue: |
+ /* Pre A prologue. */
+- name: B
+ states:
+ - name: X
+ test-code: |
+ /* Pre B X */
+ text: |
+ Pre B X.
+ - name: Y
+ test-code: |
+ /* Pre B Y */
+ text: |
+ Pre B Y.
+ test-epilogue: |
+ /* Pre B epilogue. */
+ test-prologue: |
+ /* Pre B prologue. */
+requirement-type: functional
+test-action: |
+ /* Action */
+test-brief: |
+ Test brief.
+test-context:
+- brief: |
+ Context member brief.
+ description: |
+ Context member description.
+ member: |
+ int member
+test-description: |
+ Test description.
+test-header:
+ code: |
+ /* Header code for ${.:/test-name} with ${.:/test-run}() */
+ includes:
+ - d.h
+ local-includes:
+ - e.h
+ run-params:
+ - description: |
+ Parameter A description.
+ dir: in
+ name: a
+ specifier: int *${.:name}
+ - description: |
+ Parameter B description.
+ dir: null
+ name: b
+ specifier: int ${.:name}
+ - description: |
+ Parameter C description.
+ dir: out
+ name: c
+ specifier: int *${.:name}
+ target: action2.h
+test-includes:
+- a.h
+test-local-includes:
+- b.h
+test-name: Action 2
+test-setup:
+ brief: |
+ Setup brief.
+ code: |
+ /* Setup code */
+ description: |
+ Setup description.
+test-stop: null
+test-support: |
+ /* Support code */
+test-target: action2.c
+test-teardown:
+ brief: |
+ Teardown brief.
+ code: |
+ /* Teardown code */
+ description: |
+ Teardown description.
+transition-map:
+- enabled-by: true
+ post-conditions:
+ A: X
+ B: Y
+ pre-conditions:
+ A: all
+ B: all
+rationale: null
+references: []
+text: |
+ Text.
+type: requirement
diff --git a/rtemsspec/tests/spec-validation/directive.yml b/rtemsspec/tests/spec-validation/directive.yml
new file mode 100644
index 00000000..e8842fe3
--- /dev/null
+++ b/rtemsspec/tests/spec-validation/directive.yml
@@ -0,0 +1,300 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+functional-type: action
+links: []
+post-conditions:
+- name: Status
+ states:
+ - name: Ok
+ test-code: |
+ T_rsc(ctx->status, RTEMS_SUCCESSFUL);
+ text: |
+ The status shall be RTEMS_SUCCESSFUL.
+ - name: InvAddr
+ test-code: |
+ T_rsc(ctx->status, RTEMS_INVALID_ADDRESS);
+ text: |
+ The status shall be RTEMS_INVALID_ADDRESS.
+ - name: InvName
+ test-code: |
+ T_rsc(ctx->status, RTEMS_INVALID_NAME);
+ text: |
+ The status shall be RTEMS_INVALID_NAME.
+ - name: InvNode
+ test-code: |
+ T_rsc(ctx->status, RTEMS_INVALID_NODE);
+ text: |
+ The status shall be RTEMS_INVALID_NODE.
+ - name: InvId
+ test-code: |
+ T_rsc(ctx->status, RTEMS_INVALID_ID);
+ text: |
+ The status shall be RTEMS_INVALID_ID.
+ test-epilogue: null
+ test-prologue: null
+- name: Id
+ states:
+ - name: Nop
+ test-code: |
+ T_eq_ptr(ctx->id, &ctx->id_value);
+ T_eq_u32(ctx->id_value, 0xffffffff);
+ text: |
+ The value of the object identifier referenced by the id parameter shall
+ be the value before the call to rtems_task_ident().
+ - name: NullPtr
+ test-code: |
+ T_null(ctx->id)
+ text: |
+ The id parameter shall be NULL.
+ - name: Self
+ test-code: |
+ T_eq_ptr(ctx->id, &ctx->id_value);
+ T_eq_u32(ctx->id_value, rtems_task_self());
+ text: |
+ The value of the object identifier referenced by the id parameter shall
+ be the identifier of the executing thread.
+ - name: LocalTask
+ test-code: |
+ T_eq_ptr(ctx->id, &ctx->id_value);
+ T_eq_u32(ctx->id_value, ctx->id_local_task);
+ text: |
+ The value of the object identifier referenced by the id parameter shall
+ be the identifier of a local task with a name equal to the name
+ parameter. If more than one local task with such a name exists, then it
+ shall be the identifier of the task with the lowest object index.
+ - name: RemoteTask
+ test-code: |
+ T_eq_ptr(ctx->id, &ctx->id_value);
+ T_eq_u32(ctx->id_value, ctx->id_remote_task);
+ text: |
+ The value of the object identifier referenced by the id parameter shall
+ be the identifier of a remote task on a eligible node defined by the node
+ parameter with a name equal to the name parameter. If more than one task
+ with such a name exists on the same node, then it shall be the identifier
+ of the task with the lowest object index. If more than one task with
+ such a name exists on different eligible nodes, then it shall be the
+ identifier of the task with the lowest node index.
+ test-epilogue: null
+ test-prologue: null
+pre-conditions:
+- name: Name
+ states:
+ - name: Invalid
+ test-code: |
+ ctx->name = 1;
+ text: |
+ The name parameter shall not equal to a name of an active Classic API
+ task object and not equal to RTEMS_SELF.
+ - name: Self
+ test-code: |
+ ctx->name = RTEMS_SELF;
+ text: |
+ The name parameter shall be RTEMS_SELF.
+ - name: Valid
+ test-code: |
+ ctx->name = rtems_build_name( 'T', 'A', 'S', 'K' );
+ text: |
+ The name parameter shall equal to a name of an active Classic API task
+ object.
+ test-epilogue: |
+ /* Epilogue */
+ test-prologue: |
+ /* Prologue */
+- name: Node
+ states:
+ - name: Local
+ test-code: |
+ ctx->node = 1;
+ text: |
+ The node parameter shall be the local node number.
+ - name: Remote
+ test-code: |
+ ctx->node = 2;
+ text: |
+ The node parameter shall be a remote node number.
+ - name: Invalid
+ test-code: |
+ ctx->node = 256;
+ text: |
+ The node parameter shall be an invalid node number.
+ - name: SearchAll
+ test-code: |
+ ctx->node = RTEMS_SEARCH_ALL_NODES;
+ text: |
+ The node parameter shall be RTEMS_SEARCH_ALL_NODES.
+ - name: SearchOther
+ test-code: |
+ ctx->node = RTEMS_SEARCH_OTHER_NODES;
+ text: |
+ The node parameter shall be RTEMS_SEARCH_OTHER_NODES.
+ - name: SearchLocal
+ test-code: |
+ ctx->node = RTEMS_SEARCH_LOCAL_NODE;
+ text: |
+ The node parameter shall be RTEMS_SEARCH_LOCAL_NODE.
+ test-epilogue: null
+ test-prologue: null
+- name: Id
+ states:
+ - name: NullPtr
+ test-code: |
+ ctx->id = NULL;
+ text: |
+ The id parameter shall be NULL.
+ - name: Valid
+ test-code: |
+ ctx->id_value = 0xffffffff;
+ ctx->id = &ctx->id_value;
+ text: |
+ The id parameter shall point to an object identifier.
+ test-epilogue: null
+ test-prologue: null
+requirement-type: functional
+test-action: |
+ ctx->status = rtems_task_ident( ctx->name, ctx->node, ctx->id );
+test-brief: Test rtems_task_ident() brief description.
+test-context:
+- brief: |
+ Brief context member description.
+ description: |
+ Context member description.
+ member: |
+ rtems_status_code status
+- brief: null
+ description: null
+ member: |
+ rtems_name name
+- brief: null
+ description: null
+ member: |
+ uint32_t node
+- brief: null
+ description: null
+ member: |
+ rtems_id *id
+- brief: null
+ description: null
+ member: |
+ rtems_id id_value
+- brief: null
+ description: null
+ member: |
+ rtems_id id_local_task
+- brief: null
+ description: null
+ member: |
+ rtems_id id_remote_task
+test-description: Test rtems_task_ident() description.
+test-includes:
+- rtems.h
+test-local-includes:
+- x.h
+test-name: Classic Task Identification
+test-setup:
+ brief: |
+ Setup brief description.
+ code: |
+ rtems_status_code sc;
+
+ sc = rtems_task_create(
+ rtems_build_name( 'T', 'A', 'S', 'K' ),
+ 1,
+ RTEMS_MINIMUM_STACK_SIZE,
+ RTEMS_DEFAULT_MODES,
+ RTEMS_DEFAULT_ATTRIBUTES,
+ &ctx->id_local_task
+ );
+ T_assert_rsc_success( sc );
+ description: |
+ Setup description.
+test-stop: null
+test-support: |
+ /* Test rtems_task_ident() support */
+test-header: null
+test-target: tc12.c
+test-teardown:
+ brief: null
+ code: |
+ rtems_status_code sc;
+
+ if ( ctx->id_local_task != 0 ) {
+ sc = rtems_task_delete( ctx->id_local_task );
+ T_rsc_success( sc );
+ }
+ description: null
+transition-map:
+- enabled-by: true
+ post-conditions:
+ Id: Self
+ Status: Ok
+ pre-conditions:
+ Id:
+ - Valid
+ Name:
+ - Self
+ Node: all
+- enabled-by: true
+ post-conditions:
+ Id: LocalTask
+ Status: Ok
+ pre-conditions:
+ Id:
+ - Valid
+ Name:
+ - Valid
+ Node:
+ - SearchAll
+ - SearchLocal
+ - Local
+- enabled-by: true
+ post-conditions:
+ Id: Nop
+ Status: InvName
+ pre-conditions:
+ Id:
+ - Valid
+ Name:
+ - Valid
+ Node:
+ - Invalid
+ - SearchOther
+ - Remote
+- enabled-by: true
+ post-conditions:
+ Id: Nop
+ Status: InvName
+ pre-conditions:
+ Id:
+ - Valid
+ Name:
+ - Invalid
+ Node: all
+- enabled-by: true
+ post-conditions:
+ Id: NullPtr
+ Status: InvAddr
+ pre-conditions:
+ Id:
+ - NullPtr
+ Name: all
+ Node: all
+- enabled-by: RTEMS_MULTIPROCESSING
+ post-conditions:
+ Id: RemoteTask
+ Status: Ok
+ pre-conditions:
+ Id:
+ - Valid
+ Name:
+ - Valid
+ Node:
+ - SearchOther
+ - Remote
+rationale: null
+references: []
+text: |
+ Calling the rtems_task_ident() directive shall yield the specified
+ post-conditions under the specified pre-conditions.
+type: requirement
diff --git a/rtemsspec/tests/spec-validation/other.yml b/rtemsspec/tests/spec-validation/other.yml
new file mode 100644
index 00000000..112c85a5
--- /dev/null
+++ b/rtemsspec/tests/spec-validation/other.yml
@@ -0,0 +1,5 @@
+enabled-by: []
+links:
+- role: null
+ uid: other2
+type: other
diff --git a/rtemsspec/tests/spec-validation/other2.yml b/rtemsspec/tests/spec-validation/other2.yml
new file mode 100644
index 00000000..ce9e0104
--- /dev/null
+++ b/rtemsspec/tests/spec-validation/other2.yml
@@ -0,0 +1,3 @@
+enabled-by: []
+links: []
+type: other
diff --git a/rtemsspec/tests/spec-validation/tc.yml b/rtemsspec/tests/spec-validation/tc.yml
new file mode 100644
index 00000000..bb63ea74
--- /dev/null
+++ b/rtemsspec/tests/spec-validation/tc.yml
@@ -0,0 +1,54 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+includes:
+- a.h
+links: []
+local-includes:
+- x.h
+target: tc12.c
+actions:
+- action: |
+ /* Test case action 0 code */
+ checks:
+ - check: |
+ /* Test case action 0 check 0 code */
+ description: |
+ Test case action 0 check 0 description.
+ links: []
+ - check: |
+ /* Test case action 0 check 1 code; step ${step} */
+ description: |
+ Test case action 0 check 1 description.
+ links: []
+ description: |
+ Test case action 0 description.
+ links: []
+- action: |
+ /* Test case action 1 code */
+ checks:
+ - check: |
+ /* Test case action 1 check 0 code; step ${step} */
+ description: |
+ Test case action 1 check 0 description.
+ links: []
+ - check: |
+ /* Test case action 1 check 1 code */
+ description: |
+ Test case action 1 check 1 description.
+ links: []
+ description: |
+ Test case action 1 description.
+ links: []
+brief: Test case brief description.
+description: Test case description.
+epilogue: |
+ /* Test case epilogue code */
+fixture: null
+name: Test Case
+prologue: |
+ /* Test case prologue code */
+support: |
+ /* Test case support code */
+type: test-case
diff --git a/rtemsspec/tests/spec-validation/tc2.yml b/rtemsspec/tests/spec-validation/tc2.yml
new file mode 100644
index 00000000..df236b99
--- /dev/null
+++ b/rtemsspec/tests/spec-validation/tc2.yml
@@ -0,0 +1,41 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+includes:
+- b.h
+links: []
+local-includes:
+- y.h
+target: tc12.c
+actions:
+- action: |
+ /* Test case 2 action 0 code */
+ checks:
+ - check: |
+ /* Test case 2 action 0 check 0 code */
+ description: |
+ Test case 2 action 0 check 0 description.
+ links: []
+ - check: |
+ /* Test case 2 action 0 check 1 code */
+ description: |
+ Test case 2 action 0 check 1 description.
+ links: []
+ description: |
+ Test case 2 action 0 description.
+ links: []
+- action: |
+ /* Test case 2 action 1 code */
+ checks: []
+ description: |
+ Test case 2 action 1 description.
+ links: []
+brief: Test case 2 brief description.
+description: Test case 2 description.
+epilogue: null
+fixture: test_case_2_fixture
+name: Test Case 2
+prologue: null
+support: null
+type: test-case
diff --git a/rtemsspec/tests/spec-validation/tc3.yml b/rtemsspec/tests/spec-validation/tc3.yml
new file mode 100644
index 00000000..fd946e3e
--- /dev/null
+++ b/rtemsspec/tests/spec-validation/tc3.yml
@@ -0,0 +1,30 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+includes:
+- c.h
+links: []
+local-includes:
+- z.h
+target: tc34.c
+actions:
+- action: |
+ /* Test case 3 action 0 code */
+ checks:
+ - check: |
+ /* Test case 3 action 0 check 0 code; step ${step} */
+ description: |
+ Test case 3 action 0 check 0 description.
+ links: []
+ description: |
+ Test case 3 action 0 description.
+ links: []
+brief: Test case 3 brief description.
+description: Test case 3 description.
+epilogue: null
+fixture: null
+name: Test Case 3
+prologue: null
+support: null
+type: test-case
diff --git a/rtemsspec/tests/spec-validation/tc4.yml b/rtemsspec/tests/spec-validation/tc4.yml
new file mode 100644
index 00000000..d415ff79
--- /dev/null
+++ b/rtemsspec/tests/spec-validation/tc4.yml
@@ -0,0 +1,18 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+includes: []
+links: []
+local-includes: []
+target: tc34.c
+actions: []
+brief: Test case 4 brief description.
+description: Test case 4 description.
+epilogue: |
+ /* Test case 4 epilogue code */
+fixture: null
+name: Test Case 4
+prologue: null
+support: null
+type: test-case
diff --git a/rtemsspec/tests/spec-validation/tp.yml b/rtemsspec/tests/spec-validation/tp.yml
new file mode 100644
index 00000000..a8b281d8
--- /dev/null
+++ b/rtemsspec/tests/spec-validation/tp.yml
@@ -0,0 +1,10 @@
+build-type: test-program
+enabled-by: true
+links: []
+source:
+- tc12.c
+- tc34.c
+- ts.c
+- action2.c
+- other.c
+type: build
diff --git a/rtemsspec/tests/spec-validation/ts.yml b/rtemsspec/tests/spec-validation/ts.yml
new file mode 100644
index 00000000..ff80e8e8
--- /dev/null
+++ b/rtemsspec/tests/spec-validation/ts.yml
@@ -0,0 +1,15 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+includes:
+- blue.h
+links: []
+local-includes:
+- green.h
+target: ts.c
+code: |
+ /* Blue green code */
+description: The Blue Green description.
+name: Blue Green
+type: test-suite
diff --git a/rtemsspec/tests/spec-verify/c1.yml b/rtemsspec/tests/spec-verify/c1.yml
new file mode 100644
index 00000000..ac9caf74
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/c1.yml
@@ -0,0 +1,12 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de
+dict:
+ a: null
+enabled-by: true
+links:
+- role: x
+ uid: c2
+list: []
+type: c
+uid: 1
diff --git a/rtemsspec/tests/spec-verify/c2.yml b/rtemsspec/tests/spec-verify/c2.yml
new file mode 100644
index 00000000..5ea57f13
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/c2.yml
@@ -0,0 +1,10 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de
+dict:
+ b: null
+enabled-by: true
+links: []
+list: []
+str-contains: uvw
+type: c
diff --git a/rtemsspec/tests/spec-verify/c3.yml b/rtemsspec/tests/spec-verify/c3.yml
new file mode 100644
index 00000000..9a427110
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/c3.yml
@@ -0,0 +1,13 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de
+dict: []
+enabled-by: true
+links: []
+list:
+ invalid: null
+must-be-true: false
+str: /c4
+str-contains: abc ghi
+type: c
+uid: nix
diff --git a/rtemsspec/tests/spec-verify/c4.yml b/rtemsspec/tests/spec-verify/c4.yml
new file mode 100644
index 00000000..e0195f37
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/c4.yml
@@ -0,0 +1,18 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+any-dict:
+ a: b
+bool: true
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de
+dict: null
+enabled-by: true
+float: 123.567
+int: 123
+links: []
+list: []
+must-be-true: true
+other-int: 0
+str: abc
+str-contains: abc uvw
+type: c
+uid: c3
diff --git a/rtemsspec/tests/spec-verify/d.yml b/rtemsspec/tests/spec-verify/d.yml
new file mode 100644
index 00000000..68f65b9c
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/d.yml
@@ -0,0 +1,7 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de
+d-type: blub
+enabled-by: true
+links: []
+type: d
diff --git a/rtemsspec/tests/spec-verify/d2.yml b/rtemsspec/tests/spec-verify/d2.yml
new file mode 100644
index 00000000..488487d1
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/d2.yml
@@ -0,0 +1,7 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de
+d-type: bla
+enabled-by: true
+links: []
+type: d
diff --git a/rtemsspec/tests/spec-verify/d3.yml b/rtemsspec/tests/spec-verify/d3.yml
new file mode 100644
index 00000000..06b30dad
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/d3.yml
@@ -0,0 +1,6 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de
+enabled-by: true
+links: []
+type: d
diff --git a/rtemsspec/tests/spec-verify/e.yml b/rtemsspec/tests/spec-verify/e.yml
new file mode 100644
index 00000000..b7527dee
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/e.yml
@@ -0,0 +1,6 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de
+enabled-by: true
+links: []
+type: e
diff --git a/rtemsspec/tests/spec-verify/invalid.yml b/rtemsspec/tests/spec-verify/invalid.yml
new file mode 100644
index 00000000..3c193f78
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/invalid.yml
@@ -0,0 +1,7 @@
+INVALID: []
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de
+enabled-by: true
+links: []
+type: spec-invalid
diff --git a/rtemsspec/tests/spec-verify/keys-at-least-one-0.yml b/rtemsspec/tests/spec-verify/keys-at-least-one-0.yml
new file mode 100644
index 00000000..8f9e4b5d
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/keys-at-least-one-0.yml
@@ -0,0 +1,6 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de
+enabled-by: true
+links: []
+type: keys-at-least-one
diff --git a/rtemsspec/tests/spec-verify/keys-at-least-one-1.yml b/rtemsspec/tests/spec-verify/keys-at-least-one-1.yml
new file mode 100644
index 00000000..31d17a4e
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/keys-at-least-one-1.yml
@@ -0,0 +1,7 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de
+enabled-by: true
+links: []
+key-a: null
+type: keys-at-least-one
diff --git a/rtemsspec/tests/spec-verify/keys-at-least-one-2.yml b/rtemsspec/tests/spec-verify/keys-at-least-one-2.yml
new file mode 100644
index 00000000..d0eb8a29
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/keys-at-least-one-2.yml
@@ -0,0 +1,8 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de
+enabled-by: true
+links: []
+key-a: null
+key-b: null
+type: keys-at-least-one
diff --git a/rtemsspec/tests/spec-verify/keys-at-most-one-0.yml b/rtemsspec/tests/spec-verify/keys-at-most-one-0.yml
new file mode 100644
index 00000000..2e3f0202
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/keys-at-most-one-0.yml
@@ -0,0 +1,6 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de
+enabled-by: true
+links: []
+type: keys-at-most-one
diff --git a/rtemsspec/tests/spec-verify/keys-at-most-one-1.yml b/rtemsspec/tests/spec-verify/keys-at-most-one-1.yml
new file mode 100644
index 00000000..e2003342
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/keys-at-most-one-1.yml
@@ -0,0 +1,7 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de
+enabled-by: true
+links: []
+key-a: null
+type: keys-at-most-one
diff --git a/rtemsspec/tests/spec-verify/keys-at-most-one-2.yml b/rtemsspec/tests/spec-verify/keys-at-most-one-2.yml
new file mode 100644
index 00000000..88ee2aaa
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/keys-at-most-one-2.yml
@@ -0,0 +1,8 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de
+enabled-by: true
+links: []
+key-a: null
+key-b: null
+type: keys-at-most-one
diff --git a/rtemsspec/tests/spec-verify/keys-exactly-one-0.yml b/rtemsspec/tests/spec-verify/keys-exactly-one-0.yml
new file mode 100644
index 00000000..1a052978
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/keys-exactly-one-0.yml
@@ -0,0 +1,6 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de
+enabled-by: true
+links: []
+type: keys-exactly-one
diff --git a/rtemsspec/tests/spec-verify/keys-exactly-one-1.yml b/rtemsspec/tests/spec-verify/keys-exactly-one-1.yml
new file mode 100644
index 00000000..2f233dd8
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/keys-exactly-one-1.yml
@@ -0,0 +1,7 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de
+enabled-by: true
+links: []
+key-a: null
+type: keys-exactly-one
diff --git a/rtemsspec/tests/spec-verify/keys-exactly-one-2.yml b/rtemsspec/tests/spec-verify/keys-exactly-one-2.yml
new file mode 100644
index 00000000..6226ec51
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/keys-exactly-one-2.yml
@@ -0,0 +1,8 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de
+enabled-by: true
+links: []
+key-a: null
+key-b: null
+type: keys-exactly-one
diff --git a/rtemsspec/tests/spec-verify/notype.yml b/rtemsspec/tests/spec-verify/notype.yml
new file mode 100644
index 00000000..8da253c5
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/notype.yml
@@ -0,0 +1,5 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de
+enabled-by: true
+links: []
diff --git a/rtemsspec/tests/spec-verify/spec/copyright.yml b/rtemsspec/tests/spec-verify/spec/copyright.yml
new file mode 100644
index 00000000..a8f46080
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec/copyright.yml
@@ -0,0 +1,21 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+spec-description: null
+spec-example: null
+spec-info:
+ str:
+ assert:
+ - re: ^\s*Copyright\s+\(C\)\s+[0-9]+,\s*[0-9]+\s+.+\s*$
+ - re: ^\s*Copyright\s+\(C\)\s+[0-9]+\s+.+\s*$
+ - re: ^\s*Copyright\s+\(C\)\s+.+\s*$
+ description: |
+ A value of this type shall be a copyright statement of a copyright holder
+ of the specification item.
+spec-name: Copyright
+spec-type: copyright
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec/copyrights.yml b/rtemsspec/tests/spec-verify/spec/copyrights.yml
new file mode 100644
index 00000000..522e4f43
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec/copyrights.yml
@@ -0,0 +1,16 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+spec-description: null
+spec-example: null
+spec-info:
+ list:
+ description: null
+ spec-type: copyright
+spec-name: Copyrights Type
+spec-type: copyrights
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec/enabled-by-list.yml b/rtemsspec/tests/spec-verify/spec/enabled-by-list.yml
new file mode 100644
index 00000000..f84fff28
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec/enabled-by-list.yml
@@ -0,0 +1,16 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+spec-description: null
+spec-example: null
+spec-info:
+ list:
+ description: null
+ spec-type: enabled-by
+spec-name: Enabled-By Expression List
+spec-type: enabled-by-list
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec/enabled-by.yml b/rtemsspec/tests/spec-verify/spec/enabled-by.yml
new file mode 100644
index 00000000..2f3bb287
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec/enabled-by.yml
@@ -0,0 +1,49 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+spec-description: |
+ A value of this type shall be an expression which defines under which
+ conditions the specification item or parts of it are enabled. The expression
+ is evaluated with the use of an *enabled set*. This is a set of strings
+ which indicate enabled features.
+spec-example: null
+spec-info:
+ bool:
+ description: |
+ This expression evaluates directly to the boolean value.
+ dict:
+ attributes:
+ and:
+ description: |
+ The *and* operator evaluates to the *logical and* of the evaluation
+ results of the expressions in the list.
+ spec-type: enabled-by-list
+ not:
+ description: |
+ The *not* operator evaluates to the *logical not* of the evaluation
+ results of the expression.
+ spec-type: enabled-by
+ or:
+ description: |
+ The *or* operator evaluates to the *logical or* of the evaluation
+ results of the expressions in the list.
+ spec-type: enabled-by-list
+ description: |
+ Each attribute defines an operator.
+ mandatory-attributes: exactly-one
+ list:
+ description: |
+ This list of expressions evaluates to the *logical or* of the evaluation
+ results of the expressions in the list.
+ spec-type: enabled-by
+ str:
+ description: |
+ If the value is in the *enabled set*, this expression evaluates to true,
+ otherwise to false.
+spec-name: Enabled-By Expression
+spec-type: enabled-by
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec/link.yml b/rtemsspec/tests/spec-verify/spec/link.yml
new file mode 100644
index 00000000..c9dadb7f
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec/link.yml
@@ -0,0 +1,26 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+spec-description: null
+spec-example: null
+spec-info:
+ dict:
+ attributes:
+ role:
+ description: |
+ This mandatory attribute defines the role of the link.
+ spec-type: name
+ uid:
+ description: |
+ This mandatory attribute defines the link target item UID.
+ spec-type: uid
+ description: |
+ This attribute value shall a link to a specification item.
+ mandatory-attributes: all
+spec-name: Link
+spec-type: link
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec/links.yml b/rtemsspec/tests/spec-verify/spec/links.yml
new file mode 100644
index 00000000..77092dfa
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec/links.yml
@@ -0,0 +1,17 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+spec-description: |
+ This attribute value shall be a list of links to specification items.
+spec-example: null
+spec-info:
+ list:
+ description: null
+ spec-type: link
+spec-name: Item Links Type
+spec-type: links
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec/list-str.yml b/rtemsspec/tests/spec-verify/spec/list-str.yml
new file mode 100644
index 00000000..4d1246fb
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec/list-str.yml
@@ -0,0 +1,17 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+spec-description: |
+ This attribute value shall a list of strings.
+spec-example: null
+spec-info:
+ list:
+ description: null
+ spec-type: str
+spec-name: String List Type
+spec-type: list-str
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec/optional-str.yml b/rtemsspec/tests/spec-verify/spec/optional-str.yml
new file mode 100644
index 00000000..3f409247
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec/optional-str.yml
@@ -0,0 +1,16 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+spec-description: null
+spec-example: null
+spec-info:
+ none: null
+ str:
+ description: null
+spec-name: Optional String
+spec-type: optional-str
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec/root.yml b/rtemsspec/tests/spec-verify/spec/root.yml
new file mode 100644
index 00000000..466d01a2
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec/root.yml
@@ -0,0 +1,39 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links: []
+spec-description: null
+spec-example: null
+spec-info:
+ dict:
+ attributes:
+ SPDX-License-Identifier:
+ description: |
+ This mandatory attribute defines the license of this item.
+ spec-type: spdx-license-identifier
+ copyrights:
+ description: |
+ This mandatory attribute defines the copyright holders and copyright
+ information of this item.
+ spec-type: copyrights
+ enabled-by:
+ description: |
+ This mandatory attribute defines the conditions under which this item
+ is enabled.
+ spec-type: enabled-by
+ links:
+ description: |
+ This mandatory attribute defines the links of this items to other
+ items.
+ spec-type: links
+ type:
+ description: |
+ This mandatory attribute defines the type of this item.
+ spec-type: name
+ description: |
+ This is the root specification item type.
+ mandatory-attributes: all
+spec-name: Root Item Type
+spec-type: root
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec/spdx-license-identifier.yml b/rtemsspec/tests/spec-verify/spec/spdx-license-identifier.yml
new file mode 100644
index 00000000..380b61bc
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec/spdx-license-identifier.yml
@@ -0,0 +1,21 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+spec-description: null
+spec-example: null
+spec-info:
+ str:
+ assert:
+ - eq: CC-BY-SA-4.0 OR BSD-2-Clause
+ - eq: BSD-2-Clause
+ - eq: CC-BY-SA-4.0
+ description: |
+ This attribute value defines the license of this item expressed though an
+ SPDX License Identifier.
+spec-name: SPDX License Identifier
+spec-type: spdx-license-identifier
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec/spec-assert-float-list.yml b/rtemsspec/tests/spec-verify/spec/spec-assert-float-list.yml
new file mode 100644
index 00000000..30addf5c
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec/spec-assert-float-list.yml
@@ -0,0 +1,16 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+spec-description: null
+spec-example: null
+spec-info:
+ list:
+ description: null
+ spec-type: spec-assert-float
+spec-name: Specification Floating-Point Assert List
+spec-type: spec-assert-float-list
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec/spec-assert-float.yml b/rtemsspec/tests/spec-verify/spec/spec-assert-float.yml
new file mode 100644
index 00000000..fd6f2564
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec/spec-assert-float.yml
@@ -0,0 +1,71 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+spec-description: |
+ A value of this type shall be an expression which asserts that the
+ floating-point value of the specified attribute satisfies the required
+ constraints.
+spec-example: null
+spec-info:
+ dict:
+ attributes:
+ and:
+ description: |
+ The *and* operator evaluates to the *logical and* of the evaluation
+ results of the expressions in the list.
+ spec-type: spec-assert-float-list
+ eq:
+ description: |
+ The *eq* operator evaluates to true, if the value to check is equal
+ to the value of this attribute, otherwise to false.
+ spec-type: float
+ ge:
+ description: |
+ The *ge* operator evaluates to true, if the value to check is greater
+ than or equal to the value of this attribute, otherwise to false.
+ spec-type: float
+ gt:
+ description: |
+ The *gt* operator evaluates to true, if the value to check is greater
+ than the value of this attribute, otherwise to false.
+ spec-type: float
+ le:
+ description: |
+ The *le* operator evaluates to true, if the value to check is less
+ than or equal to the value of this attribute, otherwise to false.
+ spec-type: float
+ lt:
+ description: |
+ The *lt* operator evaluates to true, if the value to check is less
+ than the value of this attribute, otherwise to false.
+ spec-type: float
+ ne:
+ description: |
+ The *ne* operator evaluates to true, if the value to check is not
+ equal to the value of this attribute, otherwise to false.
+ spec-type: float
+ not:
+ description: |
+ The *not* operator evaluates to the *logical not* of the evaluation
+ results of the expression.
+ spec-type: spec-assert-float
+ or:
+ description: |
+ The *or* operator evaluates to the *logical or* of the evaluation
+ results of the expressions in the list.
+ spec-type: spec-assert-float-list
+ description: |
+ Each attribute defines an operator.
+ mandatory-attributes: exactly-one
+ list:
+ description: |
+ This list of expressions evaluates to the *logical or* of the evaluation
+ results of the expressions in the list.
+ spec-type: spec-assert-float
+spec-name: Specification Floating-Point Assert
+spec-type: spec-assert-float
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec/spec-assert-int-list.yml b/rtemsspec/tests/spec-verify/spec/spec-assert-int-list.yml
new file mode 100644
index 00000000..98f20673
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec/spec-assert-int-list.yml
@@ -0,0 +1,16 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+spec-description: null
+spec-example: null
+spec-info:
+ list:
+ description: null
+ spec-type: spec-assert-int
+spec-name: Specification Integer Assert List
+spec-type: spec-assert-int-list
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec/spec-assert-int.yml b/rtemsspec/tests/spec-verify/spec/spec-assert-int.yml
new file mode 100644
index 00000000..ec903b80
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec/spec-assert-int.yml
@@ -0,0 +1,70 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+spec-description: |
+ A value of this type shall be an expression which asserts that the integer
+ value of the specified attribute satisfies the required constraints.
+spec-example: null
+spec-info:
+ dict:
+ attributes:
+ and:
+ description: |
+ The *and* operator evaluates to the *logical and* of the evaluation
+ results of the expressions in the list.
+ spec-type: spec-assert-int-list
+ eq:
+ description: |
+ The *eq* operator evaluates to true, if the value to check is equal
+ to the value of this attribute, otherwise to false.
+ spec-type: int
+ ge:
+ description: |
+ The *ge* operator evaluates to true, if the value to check is greater
+ than or equal to the value of this attribute, otherwise to false.
+ spec-type: int
+ gt:
+ description: |
+ The *gt* operator evaluates to true, if the value to check is greater
+ than the value of this attribute, otherwise to false.
+ spec-type: int
+ le:
+ description: |
+ The *le* operator evaluates to true, if the value to check is less
+ than or equal to the value of this attribute, otherwise to false.
+ spec-type: int
+ lt:
+ description: |
+ The *lt* operator evaluates to true, if the value to check is less
+ than the value of this attribute, otherwise to false.
+ spec-type: int
+ ne:
+ description: |
+ The *ne* operator evaluates to true, if the value to check is not
+ equal to the value of this attribute, otherwise to false.
+ spec-type: int
+ not:
+ description: |
+ The *not* operator evaluates to the *logical not* of the evaluation
+ results of the expression.
+ spec-type: spec-assert-int
+ or:
+ description: |
+ The *or* operator evaluates to the *logical or* of the evaluation
+ results of the expressions in the list.
+ spec-type: spec-assert-int-list
+ description: |
+ Each attribute defines an operator.
+ mandatory-attributes: exactly-one
+ list:
+ description: |
+ This list of expressions evaluates to the *logical or* of the evaluation
+ results of the expressions in the list.
+ spec-type: spec-assert-int
+spec-name: Specification Integer Assert
+spec-type: spec-assert-int
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec/spec-assert-str-list.yml b/rtemsspec/tests/spec-verify/spec/spec-assert-str-list.yml
new file mode 100644
index 00000000..290c0bd9
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec/spec-assert-str-list.yml
@@ -0,0 +1,16 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+spec-description: null
+spec-example: null
+spec-info:
+ list:
+ description: null
+ spec-type: spec-assert-str
+spec-name: Specification String Assert List
+spec-type: spec-assert-str-list
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec/spec-assert-str.yml b/rtemsspec/tests/spec-verify/spec/spec-assert-str.yml
new file mode 100644
index 00000000..915da6ed
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec/spec-assert-str.yml
@@ -0,0 +1,94 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+spec-description: |
+ A value of this type shall be an expression which asserts that the string of
+ the specified attribute satisfies the required constraints.
+spec-example: null
+spec-info:
+ dict:
+ attributes:
+ and:
+ description: |
+ The *and* operator evaluates to the *logical and* of the evaluation
+ results of the expressions in the list.
+ spec-type: spec-assert-str-list
+ contains:
+ description: |
+ The *contains* operator evaluates to true, if the string to check
+ converted to lower case with all white space characters converted to
+ a single space character contains a string of the list of strings of
+ this attribute, otherwise to false.
+ spec-type: list-str
+ eq:
+ description: |
+ The *eq* operator evaluates to true, if the string to check is equal
+ to the value of this attribute, otherwise to false.
+ spec-type: str
+ ge:
+ description: |
+ The *ge* operator evaluates to true, if the string to check is
+ greater than or equal to the value of this attribute, otherwise to
+ false.
+ spec-type: str
+ gt:
+ description: |
+ The *gt* operator evaluates to true, if the string to check is
+ greater than the value of this attribute, otherwise to false.
+ spec-type: str
+ in:
+ description: |
+ The *in* operator evaluates to true, if the string to check is
+ contained in the list of strings of this attribute, otherwise to
+ false.
+ spec-type: list-str
+ le:
+ description: |
+ The *le* operator evaluates to true, if the string to check is less
+ than or equal to the value of this attribute, otherwise to false.
+ spec-type: str
+ lt:
+ description: |
+ The *lt* operator evaluates to true, if the string to check is less
+ than the value of this attribute, otherwise to false.
+ spec-type: str
+ ne:
+ description: |
+ The *ne* operator evaluates to true, if the string to check is not
+ equal to the value of this attribute, otherwise to false.
+ spec-type: str
+ not:
+ description: |
+ The *not* operator evaluates to the *logical not* of the evaluation
+ results of the expression.
+ spec-type: spec-assert-str
+ or:
+ description: |
+ The *or* operator evaluates to the *logical or* of the evaluation
+ results of the expressions in the list.
+ spec-type: spec-assert-str-list
+ re:
+ description: |
+ The *re* operator evaluates to true, if the string to check matches
+ with the regular expression of this attribute, otherwise to false.
+ spec-type: str
+ uid:
+ description: |
+ The *uid* operator evaluates to true, if the string is a valid UID,
+ otherwise to false.
+ spec-type: none
+ description: |
+ Each attribute defines an operator.
+ mandatory-attributes: exactly-one
+ list:
+ description: |
+ This list of expressions evaluates to the *logical or* of the evaluation
+ results of the expressions in the list.
+ spec-type: spec-assert-str
+spec-name: Specification String Assert
+spec-type: spec-assert-str
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec/spec-attribute-value.yml b/rtemsspec/tests/spec-verify/spec/spec-attribute-value.yml
new file mode 100644
index 00000000..d44e2256
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec/spec-attribute-value.yml
@@ -0,0 +1,26 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+spec-description: null
+spec-example: null
+spec-info:
+ dict:
+ attributes:
+ description:
+ description: |
+ It shall be the description of the attribute value.
+ spec-type: optional-str
+ spec-type:
+ description: |
+ It shall be the specification type of the attribute value.
+ spec-type: name
+ description: |
+ This set of attributes specifies an attribute value.
+ mandatory-attributes: all
+spec-name: Specification Attribute Value
+spec-type: spec-attribute-value
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec/spec-attributes.yml b/rtemsspec/tests/spec-verify/spec/spec-attributes.yml
new file mode 100644
index 00000000..eb489783
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec/spec-attributes.yml
@@ -0,0 +1,24 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+spec-description: null
+spec-example: null
+spec-info:
+ dict:
+ attributes: {}
+ description: null
+ generic-attributes:
+ description: |
+ Each generic attribute specifies an explicit attribute of the attribute
+ set. The key of the each generic attribute defines the attribute key
+ of the explicit attribute.
+ key-spec-type: name
+ value-spec-type: spec-attribute-value
+ mandatory-attributes: none
+spec-name: Specification Explicit Attributes
+spec-type: spec-attributes
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec/spec-bool.yml b/rtemsspec/tests/spec-verify/spec/spec-bool.yml
new file mode 100644
index 00000000..e23f3b1f
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec/spec-bool.yml
@@ -0,0 +1,33 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+spec-description: null
+spec-example: null
+spec-info:
+ dict:
+ attributes:
+ assert:
+ description: |
+ This optional attribute defines the value constraint of the specified
+ boolean value. If the value of the assert attribute is true, then
+ the value of the specified boolean value shall be true. If the value
+ of the assert attribute is false, then the value of the specified
+ boolean value shall be false. In case the assert attribute is not
+ present, then the value of the specified boolean value may be true or
+ false.
+ spec-type: bool
+ description:
+ description: |
+ It shall be the description of the specified boolean value.
+ spec-type: optional-str
+ description: |
+ This attribute set specifies a boolean value.
+ mandatory-attributes:
+ - description
+spec-name: Specification Boolean Value
+spec-type: spec-bool
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec/spec-dict.yml b/rtemsspec/tests/spec-verify/spec/spec-dict.yml
new file mode 100644
index 00000000..1fae4d15
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec/spec-dict.yml
@@ -0,0 +1,37 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+spec-description: null
+spec-example: null
+spec-info:
+ dict:
+ attributes:
+ attributes:
+ description: |
+ It shall specify the explicit attributes of the attribute set.
+ spec-type: spec-attributes
+ description:
+ description: |
+ It shall be the description of the attribute set.
+ spec-type: optional-str
+ generic-attributes:
+ description: |
+ It shall specify the generic attributes of the attribute set.
+ spec-type: spec-generic-attributes
+ mandatory-attributes:
+ description: |
+ It shall specify the mandatory attributes of the attribute set.
+ spec-type: spec-mandatory-attributes
+ description: |
+ This set of attributes specifies a set of attributes.
+ mandatory-attributes:
+ - attributes
+ - description
+ - mandatory-attributes
+spec-name: Specification Attribute Set
+spec-type: spec-dict
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec/spec-float.yml b/rtemsspec/tests/spec-verify/spec/spec-float.yml
new file mode 100644
index 00000000..1f831eb8
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec/spec-float.yml
@@ -0,0 +1,30 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+spec-description: null
+spec-example: null
+spec-info:
+ dict:
+ attributes:
+ assert:
+ description: |
+ This optional attribute defines the value constraints of the
+ specified floating-point value. In case the assert attribute is not
+ present, then the value of the specified floating-point value may be
+ every valid floating-point number.
+ spec-type: spec-assert-float
+ description:
+ description: |
+ It shall be the description of the specified floating-point value.
+ spec-type: optional-str
+ description: |
+ This set of attributes specifies a floating-point value.
+ mandatory-attributes:
+ - description
+spec-name: Specification Floating-Point Value
+spec-type: spec-float
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec/spec-generic-attributes.yml b/rtemsspec/tests/spec-verify/spec/spec-generic-attributes.yml
new file mode 100644
index 00000000..8f9b4180
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec/spec-generic-attributes.yml
@@ -0,0 +1,33 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+spec-description: null
+spec-example: null
+spec-info:
+ dict:
+ attributes:
+ description:
+ description: |
+ It shall be the description of the generic attributes.
+ spec-type: optional-str
+ key-spec-type:
+ description: |
+ It shall be the specification type of the generic attribute keys.
+ spec-type: name
+ value-spec-type:
+ description: |
+ It shall be the specification type of the generic attribute values.
+ spec-type: name
+ description: |
+ This set of attributes specifies generic attributes. Generic attributes
+ are attributes which are not explicitly specified by
+ ${spec-attributes:/spec-name}. They are restricted to uniform attribute
+ key and value types.
+ mandatory-attributes: all
+spec-name: Specification Generic Attributes
+spec-type: spec-generic-attributes
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec/spec-info.yml b/rtemsspec/tests/spec-verify/spec/spec-info.yml
new file mode 100644
index 00000000..5bdbeee0
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec/spec-info.yml
@@ -0,0 +1,46 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+spec-description: null
+spec-example: null
+spec-info:
+ dict:
+ attributes:
+ bool:
+ description: |
+ It shall specify a boolean value.
+ spec-type: spec-bool
+ dict:
+ description: |
+ It shall specify a set of attributes.
+ spec-type: spec-dict
+ float:
+ description: |
+ It shall specify a floating-point value.
+ spec-type: spec-float
+ int:
+ description: |
+ It shall specify an integer value.
+ spec-type: spec-int
+ list:
+ description: |
+ It shall specify a list of attributes or values.
+ spec-type: spec-list
+ none:
+ description: |
+ It specifies that no value is required.
+ spec-type: none
+ str:
+ description: |
+ It shall specify a string.
+ spec-type: spec-str
+ description: |
+ This set of attributes specifies attribute values.
+ mandatory-attributes: at-least-one
+spec-name: Specification Information
+spec-type: spec-info
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec/spec-int.yml b/rtemsspec/tests/spec-verify/spec/spec-int.yml
new file mode 100644
index 00000000..080a3a9d
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec/spec-int.yml
@@ -0,0 +1,30 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+spec-description: null
+spec-example: null
+spec-info:
+ dict:
+ attributes:
+ assert:
+ description: |
+ This optional attribute defines the value constraints of the
+ specified integer value. In case the assert attribute is not
+ present, then the value of the specified integer value may be every
+ valid integer number.
+ spec-type: spec-assert-int
+ description:
+ description: |
+ It shall be the description of the specified integer value.
+ spec-type: optional-str
+ description: |
+ This set of attributes specifies an integer value.
+ mandatory-attributes:
+ - description
+spec-name: Specification Integer Value
+spec-type: spec-int
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec/spec-list.yml b/rtemsspec/tests/spec-verify/spec/spec-list.yml
new file mode 100644
index 00000000..b8f87807
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec/spec-list.yml
@@ -0,0 +1,26 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+spec-description: null
+spec-example: null
+spec-info:
+ dict:
+ attributes:
+ description:
+ description: |
+ It shall be the description of the list.
+ spec-type: optional-str
+ spec-type:
+ description: |
+ It shall be the specification type of elements of the list.
+ spec-type: name
+ description: |
+ This set of attributes specifies a list of attributes or values.
+ mandatory-attributes: all
+spec-name: Specification List
+spec-type: spec-list
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec/spec-mandatory-attributes.yml b/rtemsspec/tests/spec-verify/spec/spec-mandatory-attributes.yml
new file mode 100644
index 00000000..700de958
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec/spec-mandatory-attributes.yml
@@ -0,0 +1,30 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+spec-description: |
+ It defines which explicit attributes are mandatory.
+spec-example: null
+spec-info:
+ list:
+ description: |
+ The list defines the mandatory attributes through their key names.
+ spec-type: name
+ str:
+ assert:
+ in:
+ - all
+ - at-least-one
+ - at-most-one
+ - exactly-one
+ - none
+ description: |
+ It defines how many explicit attributes are mandatory. If `none` is
+ used, then none of the explicit attributes is mandatory, they are all
+ optional.
+spec-name: Specification Mandatory Attributes
+spec-type: spec-mandatory-attributes
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec/spec-member.yml b/rtemsspec/tests/spec-verify/spec/spec-member.yml
new file mode 100644
index 00000000..790500ef
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec/spec-member.yml
@@ -0,0 +1,22 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+- role: spec-refinement
+ spec-key: role
+ spec-value: spec-member
+ uid: link
+spec-description: null
+spec-example: null
+spec-info:
+ dict:
+ attributes: {}
+ description: |
+ It defines the specification membership role of links.
+ mandatory-attributes: all
+spec-name: Specification Member Link Role
+spec-type: spec-member
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec/spec-refinement.yml b/rtemsspec/tests/spec-verify/spec/spec-refinement.yml
new file mode 100644
index 00000000..8fd16da1
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec/spec-refinement.yml
@@ -0,0 +1,32 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+- role: spec-refinement
+ spec-key: role
+ spec-value: spec-refinement
+ uid: link
+spec-description: null
+spec-example: null
+spec-info:
+ dict:
+ attributes:
+ spec-key:
+ description: |
+ It shall be the specification type refinement attribute key of the
+ specification refinement.
+ spec-type: name
+ spec-value:
+ description: |
+ It shall be the specification type refinement attribute value of the
+ specification refinement.
+ spec-type: name
+ description: |
+ It defines the specification refinement role of links.
+ mandatory-attributes: all
+spec-name: Specification Refinement Link Role
+spec-type: spec-refinement
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec/spec-str.yml b/rtemsspec/tests/spec-verify/spec/spec-str.yml
new file mode 100644
index 00000000..f83dae5e
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec/spec-str.yml
@@ -0,0 +1,29 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+spec-description: null
+spec-example: null
+spec-info:
+ dict:
+ attributes:
+ assert:
+ description: |
+ This optional attribute defines the constraints of the specified
+ string. In case the assert attribute is not present, then the
+ specified string may be every valid string.
+ spec-type: spec-assert-str
+ description:
+ description: |
+ It shall be the description of the specified string attribute.
+ spec-type: optional-str
+ description: |
+ This set of attributes specifies a string.
+ mandatory-attributes:
+ - description
+spec-name: Specification String Value
+spec-type: spec-str
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec/spec.yml b/rtemsspec/tests/spec-verify/spec/spec.yml
new file mode 100644
index 00000000..e67e920e
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec/spec.yml
@@ -0,0 +1,72 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+- role: spec-refinement
+ spec-key: type
+ spec-value: spec
+ uid: root
+spec-description: null
+spec-example: |
+ SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+ copyrights:
+ - Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+ enabled-by: true
+ links:
+ - role: spec-member
+ uid: root
+ - role: spec-refinement
+ spec-key: type
+ spec-value: example
+ uid: root
+ spec-description: null
+ spec-example: null
+ spec-info:
+ dict:
+ attributes:
+ an-example-attribute:
+ description: |
+ It shall be an example.
+ spec-type: optional-str
+ example-number:
+ description: |
+ It shall be the example number.
+ spec-type: int
+ description: |
+ This set of attributes specifies an example.
+ mandatory-attributes: all
+ spec-name: Example Item Type
+ spec-type: spec
+ type: spec
+spec-info:
+ dict:
+ attributes:
+ spec-description:
+ description: |
+ It shall be the description of the specification type.
+ spec-type: optional-str
+ spec-example:
+ description: |
+ If the value is present, then it shall be an example of the
+ specification type.
+ spec-type: optional-str
+ spec-info:
+ description: null
+ spec-type: spec-info
+ spec-name:
+ description: |
+ It shall be the human readable name of the specification type.
+ spec-type: optional-str
+ spec-type:
+ description: |
+ It shall the specification type.
+ spec-type: name
+ description: |
+ This set of attributes specifies specification types.
+ mandatory-attributes: all
+spec-name: Specification Item Type
+spec-type: spec
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec2/any-dict.yml b/rtemsspec/tests/spec-verify/spec2/any-dict.yml
new file mode 100644
index 00000000..1f1176d9
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec2/any-dict.yml
@@ -0,0 +1,17 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: /spec/root
+spec-example: null
+spec-info:
+ dict:
+ attributes: {}
+ generic-attributes:
+ key-spec-type: name
+ value-spec-type: str
+ mandatory-attributes: none
+spec-type: any-dict
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec2/c.yml b/rtemsspec/tests/spec-verify/spec2/c.yml
new file mode 100644
index 00000000..4154288c
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec2/c.yml
@@ -0,0 +1,40 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: /spec/root
+- role: spec-refinement
+ spec-key: type
+ spec-value: c
+ uid: /spec/root
+spec-example: null
+spec-info:
+ dict:
+ attributes:
+ any-dict:
+ spec-type: any-dict
+ bool:
+ spec-type: some-bool
+ dict:
+ spec-type: some-dict
+ float:
+ spec-type: some-float
+ int:
+ spec-type: some-int
+ list:
+ spec-type: some-list
+ must-be-true:
+ spec-type: must-be-true
+ other-int:
+ spec-type: other-int
+ str:
+ spec-type: some-str
+ str-contains:
+ spec-type: str-contains
+ uid:
+ spec-type: uid
+ mandatory-attributes: all
+spec-type: c
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec2/d-a.yml b/rtemsspec/tests/spec-verify/spec2/d-a.yml
new file mode 100644
index 00000000..e011310d
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec2/d-a.yml
@@ -0,0 +1,22 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: /spec/root
+- role: spec-refinement
+ spec-key: d-type
+ spec-value: blub
+ uid: d
+- role: other
+ uid: d
+spec-example: null
+spec-info:
+ dict:
+ attributes:
+ d-type:
+ spec-type: x
+ mandatory-attributes: none
+spec-type: d-a
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec2/d.yml b/rtemsspec/tests/spec-verify/spec2/d.yml
new file mode 100644
index 00000000..27089960
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec2/d.yml
@@ -0,0 +1,21 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: /spec/root
+- role: spec-refinement
+ spec-key: type
+ spec-value: d
+ uid: /spec/root
+spec-example: null
+spec-info:
+ dict:
+ attributes:
+ d-type:
+ spec-type: name
+ mandatory-attributes:
+ - d-type
+spec-type: d
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec2/invalid.yml b/rtemsspec/tests/spec-verify/spec2/invalid.yml
new file mode 100644
index 00000000..a38e724f
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec2/invalid.yml
@@ -0,0 +1,24 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: /spec/root
+- role: spec-refinement
+ spec-key: type
+ spec-value: spec-invalid
+ uid: /spec/root
+spec-example: null
+spec-info:
+ dict:
+ attributes:
+ INVALID:
+ spec-type: invalid
+ mandatory-attributes: none
+ none: true
+ str:
+ foo: bar
+ unexpected: null
+spec-type: spec-invalid
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec2/keys-at-least-one.yml b/rtemsspec/tests/spec-verify/spec2/keys-at-least-one.yml
new file mode 100644
index 00000000..2336efe1
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec2/keys-at-least-one.yml
@@ -0,0 +1,22 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: /spec/root
+- role: spec-refinement
+ spec-key: type
+ spec-value: keys-at-least-one
+ uid: /spec/root
+spec-example: null
+spec-info:
+ dict:
+ attributes:
+ key-a:
+ spec-type: none
+ key-b:
+ spec-type: none
+ mandatory-attributes: at-least-one
+spec-type: keys-at-least-one
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec2/keys-at-most-one.yml b/rtemsspec/tests/spec-verify/spec2/keys-at-most-one.yml
new file mode 100644
index 00000000..b8fb29c5
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec2/keys-at-most-one.yml
@@ -0,0 +1,22 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: /spec/root
+- role: spec-refinement
+ spec-key: type
+ spec-value: keys-at-most-one
+ uid: /spec/root
+spec-example: null
+spec-info:
+ dict:
+ attributes:
+ key-a:
+ spec-type: none
+ key-b:
+ spec-type: none
+ mandatory-attributes: at-most-one
+spec-type: keys-at-most-one
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec2/keys-exactly-one.yml b/rtemsspec/tests/spec-verify/spec2/keys-exactly-one.yml
new file mode 100644
index 00000000..c546d574
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec2/keys-exactly-one.yml
@@ -0,0 +1,22 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: /spec/root
+- role: spec-refinement
+ spec-key: type
+ spec-value: keys-exactly-one
+ uid: /spec/root
+spec-example: null
+spec-info:
+ dict:
+ attributes:
+ key-a:
+ spec-type: none
+ key-b:
+ spec-type: none
+ mandatory-attributes: exactly-one
+spec-type: keys-exactly-one
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec2/must-be-true.yml b/rtemsspec/tests/spec-verify/spec2/must-be-true.yml
new file mode 100644
index 00000000..eaf0ea14
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec2/must-be-true.yml
@@ -0,0 +1,13 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: /spec/root
+spec-example: null
+spec-info:
+ bool:
+ assert: true
+spec-type: must-be-true
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec2/other-int.yml b/rtemsspec/tests/spec-verify/spec2/other-int.yml
new file mode 100644
index 00000000..f0209c08
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec2/other-int.yml
@@ -0,0 +1,12 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: /spec/root
+spec-example: null
+spec-info:
+ int: {}
+spec-type: other-int
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec2/some-bool.yml b/rtemsspec/tests/spec-verify/spec2/some-bool.yml
new file mode 100644
index 00000000..3204d479
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec2/some-bool.yml
@@ -0,0 +1,12 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: /spec/root
+spec-example: null
+spec-info:
+ bool: {}
+spec-type: some-bool
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec2/some-dict.yml b/rtemsspec/tests/spec-verify/spec2/some-dict.yml
new file mode 100644
index 00000000..c0bd51b8
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec2/some-dict.yml
@@ -0,0 +1,17 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: /spec/root
+spec-example: null
+spec-info:
+ dict:
+ attributes:
+ a:
+ spec-type: none
+ mandatory-attributes: none
+ none: null
+spec-type: some-dict
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec2/some-float.yml b/rtemsspec/tests/spec-verify/spec2/some-float.yml
new file mode 100644
index 00000000..114f6feb
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec2/some-float.yml
@@ -0,0 +1,14 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: /spec/root
+spec-example: null
+spec-info:
+ float:
+ assert:
+ - le: 0.0
+spec-type: some-float
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec2/some-int.yml b/rtemsspec/tests/spec-verify/spec2/some-int.yml
new file mode 100644
index 00000000..8f59d8a1
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec2/some-int.yml
@@ -0,0 +1,42 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: /spec/root
+spec-example: null
+spec-info:
+ int:
+ assert:
+ - and:
+ - not:
+ eq: 8
+ - eq: 8
+ - and:
+ - eq: 8
+ - ne: 8
+ - and:
+ - ge: 8
+ - le: 8
+ - and:
+ - gt: 8
+ - lt: 8
+ - and:
+ - le: 8
+ - ge: 8
+ - and:
+ - lt: 8
+ - gt: 8
+ - and:
+ - or:
+ - eq: 8
+ - ne: 8
+ - eq: 1
+ - ne: 2
+ - le: 3
+ - lt: 4
+ - ge: 5
+ - gt: 6
+spec-type: some-int
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec2/some-list.yml b/rtemsspec/tests/spec-verify/spec2/some-list.yml
new file mode 100644
index 00000000..b3b45086
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec2/some-list.yml
@@ -0,0 +1,13 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: /spec/root
+spec-example: null
+spec-info:
+ list:
+ spec-type: none
+spec-type: some-list
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec2/some-str.yml b/rtemsspec/tests/spec-verify/spec2/some-str.yml
new file mode 100644
index 00000000..c75aaf10
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec2/some-str.yml
@@ -0,0 +1,17 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: /spec/root
+spec-example: null
+spec-info:
+ str:
+ assert:
+ - uid: null
+ - eq: abc
+ - ne: abc
+ - re: abc
+spec-type: some-str
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec2/sta.yml b/rtemsspec/tests/spec-verify/spec2/sta.yml
new file mode 100644
index 00000000..1c70a1ce
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec2/sta.yml
@@ -0,0 +1,9 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links: []
+spec-example: null
+spec-info: {}
+spec-type: a
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec2/str-contains.yml b/rtemsspec/tests/spec-verify/spec2/str-contains.yml
new file mode 100644
index 00000000..fbd7b856
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec2/str-contains.yml
@@ -0,0 +1,20 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: /spec/root
+spec-example: null
+spec-info:
+ str:
+ assert:
+ and:
+ - contains:
+ - abc
+ - def
+ - not:
+ contains:
+ - ghi
+spec-type: str-contains
+type: spec
diff --git a/rtemsspec/tests/spec-verify/spec2/x.yml b/rtemsspec/tests/spec-verify/spec2/x.yml
new file mode 100644
index 00000000..e96d281a
--- /dev/null
+++ b/rtemsspec/tests/spec-verify/spec2/x.yml
@@ -0,0 +1,14 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: /spec/root
+spec-example: null
+spec-info:
+ str:
+ assert:
+ eq: x
+spec-type: x
+type: spec
diff --git a/rtemsspec/tests/spec/build-more.yml b/rtemsspec/tests/spec/build-more.yml
new file mode 100644
index 00000000..fb35b357
--- /dev/null
+++ b/rtemsspec/tests/spec/build-more.yml
@@ -0,0 +1,21 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+- role: spec-refinement
+ spec-key: build-type
+ spec-value: test-program
+ uid: build
+spec-description: null
+spec-example: null
+spec-info:
+ dict:
+ attributes: {}
+ description: null
+ mandatory-attributes: all
+spec-name: Build More
+spec-type: build-more
+type: spec
diff --git a/rtemsspec/tests/spec/build.yml b/rtemsspec/tests/spec/build.yml
new file mode 100644
index 00000000..8329c6d4
--- /dev/null
+++ b/rtemsspec/tests/spec/build.yml
@@ -0,0 +1,24 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+- role: spec-refinement
+ spec-key: type
+ spec-value: build
+ uid: root
+spec-description: null
+spec-example: null
+spec-info:
+ dict:
+ attributes:
+ build-type:
+ description: null
+ spec-type: name
+ description: null
+ mandatory-attributes: all
+spec-name: Build
+spec-type: build
+type: spec
diff --git a/rtemsspec/tests/spec/constraint.yml b/rtemsspec/tests/spec/constraint.yml
new file mode 100644
index 00000000..5e6af544
--- /dev/null
+++ b/rtemsspec/tests/spec/constraint.yml
@@ -0,0 +1,24 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+- role: spec-refinement
+ spec-key: type
+ spec-value: constraint
+ uid: root
+spec-description: null
+spec-example: null
+spec-info:
+ dict:
+ attributes:
+ requirement-type:
+ description: null
+ spec-type: name
+ description: null
+ mandatory-attributes: all
+spec-name: Constraint
+spec-type: constraint
+type: spec
diff --git a/rtemsspec/tests/spec/functional-more.yml b/rtemsspec/tests/spec/functional-more.yml
new file mode 100644
index 00000000..dee60872
--- /dev/null
+++ b/rtemsspec/tests/spec/functional-more.yml
@@ -0,0 +1,21 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+- role: spec-refinement
+ spec-key: functional-type
+ spec-value: action
+ uid: functional
+spec-description: null
+spec-example: null
+spec-info:
+ dict:
+ attributes: {}
+ description: null
+ mandatory-attributes: all
+spec-name: Functional More
+spec-type: functional-more
+type: spec
diff --git a/rtemsspec/tests/spec/functional.yml b/rtemsspec/tests/spec/functional.yml
new file mode 100644
index 00000000..9ae9cfec
--- /dev/null
+++ b/rtemsspec/tests/spec/functional.yml
@@ -0,0 +1,24 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+- role: spec-refinement
+ spec-key: requirement-type
+ spec-value: functional
+ uid: requirement
+spec-description: null
+spec-example: null
+spec-info:
+ dict:
+ attributes:
+ functional-type:
+ description: null
+ spec-type: name
+ description: null
+ mandatory-attributes: all
+spec-name: Functional
+spec-type: functional
+type: spec
diff --git a/rtemsspec/tests/spec/glossary-more.yml b/rtemsspec/tests/spec/glossary-more.yml
new file mode 100644
index 00000000..bbcad8c3
--- /dev/null
+++ b/rtemsspec/tests/spec/glossary-more.yml
@@ -0,0 +1,25 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+- role: spec-refinement
+ spec-key: glossary-type
+ spec-value: group
+ uid: glossary
+- role: spec-refinement
+ spec-key: glossary-type
+ spec-value: term
+ uid: glossary
+spec-description: null
+spec-example: null
+spec-info:
+ dict:
+ attributes: {}
+ description: null
+ mandatory-attributes: all
+spec-name: Glossary More
+spec-type: glossary-more
+type: spec
diff --git a/rtemsspec/tests/spec/glossary.yml b/rtemsspec/tests/spec/glossary.yml
new file mode 100644
index 00000000..6a34d364
--- /dev/null
+++ b/rtemsspec/tests/spec/glossary.yml
@@ -0,0 +1,24 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+- role: spec-refinement
+ spec-key: type
+ spec-value: glossary
+ uid: root
+spec-description: null
+spec-example: null
+spec-info:
+ dict:
+ attributes:
+ glossary-type:
+ description: null
+ spec-type: name
+ description: null
+ mandatory-attributes: all
+spec-name: Glossary
+spec-type: glossary
+type: spec
diff --git a/rtemsspec/tests/spec/interface-appl-config-more.yml b/rtemsspec/tests/spec/interface-appl-config-more.yml
new file mode 100644
index 00000000..8a8ecc56
--- /dev/null
+++ b/rtemsspec/tests/spec/interface-appl-config-more.yml
@@ -0,0 +1,33 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+- role: spec-refinement
+ spec-key: appl-config-option-type
+ spec-value: feature
+ uid: interface-appl-config-option
+- role: spec-refinement
+ spec-key: appl-config-option-type
+ spec-value: feature-enable
+ uid: interface-appl-config-option
+- role: spec-refinement
+ spec-key: appl-config-option-type
+ spec-value: integer
+ uid: interface-appl-config-option
+- role: spec-refinement
+ spec-key: appl-config-option-type
+ spec-value: initializer
+ uid: interface-appl-config-option
+spec-description: null
+spec-example: null
+spec-info:
+ dict:
+ attributes: {}
+ description: null
+ mandatory-attributes: all
+spec-name: Interface Application Configuration More
+spec-type: interface-appl-config-more
+type: spec
diff --git a/rtemsspec/tests/spec/interface-appl-config-option.yml b/rtemsspec/tests/spec/interface-appl-config-option.yml
new file mode 100644
index 00000000..bb6997b7
--- /dev/null
+++ b/rtemsspec/tests/spec/interface-appl-config-option.yml
@@ -0,0 +1,21 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+- role: spec-refinement
+ spec-key: interface-type
+ spec-value: appl-config-option
+ uid: interface
+spec-description: null
+spec-example: null
+spec-info:
+ dict:
+ attributes: {}
+ description: null
+ mandatory-attributes: all
+spec-name: Interface Application Configuration Option
+spec-type: interface-appl-config-option
+type: spec
diff --git a/rtemsspec/tests/spec/interface-more.yml b/rtemsspec/tests/spec/interface-more.yml
new file mode 100644
index 00000000..deaf1e60
--- /dev/null
+++ b/rtemsspec/tests/spec/interface-more.yml
@@ -0,0 +1,81 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+- role: spec-refinement
+ spec-key: interface-type
+ spec-value: appl-config-group
+ uid: interface
+- role: spec-refinement
+ spec-key: interface-type
+ spec-value: struct
+ uid: interface
+- role: spec-refinement
+ spec-key: interface-type
+ spec-value: union
+ uid: interface
+- role: spec-refinement
+ spec-key: interface-type
+ spec-value: container
+ uid: interface
+- role: spec-refinement
+ spec-key: interface-type
+ spec-value: define
+ uid: interface
+- role: spec-refinement
+ spec-key: interface-type
+ spec-value: domain
+ uid: interface
+- role: spec-refinement
+ spec-key: interface-type
+ spec-value: enum
+ uid: interface
+- role: spec-refinement
+ spec-key: interface-type
+ spec-value: enumerator
+ uid: interface
+- role: spec-refinement
+ spec-key: interface-type
+ spec-value: forward-declaration
+ uid: interface
+- role: spec-refinement
+ spec-key: interface-type
+ spec-value: function
+ uid: interface
+- role: spec-refinement
+ spec-key: interface-type
+ spec-value: group
+ uid: interface
+- role: spec-refinement
+ spec-key: interface-type
+ spec-value: header-file
+ uid: interface
+- role: spec-refinement
+ spec-key: interface-type
+ spec-value: macro
+ uid: interface
+- role: spec-refinement
+ spec-key: interface-type
+ spec-value: typedef
+ uid: interface
+- role: spec-refinement
+ spec-key: interface-type
+ spec-value: unspecified
+ uid: interface
+- role: spec-refinement
+ spec-key: interface-type
+ spec-value: variable
+ uid: interface
+spec-description: null
+spec-example: null
+spec-info:
+ dict:
+ attributes: {}
+ description: null
+ mandatory-attributes: all
+spec-name: Interface More
+spec-type: interface-more
+type: spec
diff --git a/rtemsspec/tests/spec/interface.yml b/rtemsspec/tests/spec/interface.yml
new file mode 100644
index 00000000..99ba9ac5
--- /dev/null
+++ b/rtemsspec/tests/spec/interface.yml
@@ -0,0 +1,24 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+- role: spec-refinement
+ spec-key: type
+ spec-value: interface
+ uid: root
+spec-description: null
+spec-example: null
+spec-info:
+ dict:
+ attributes:
+ build-type:
+ description: null
+ spec-type: name
+ description: null
+ mandatory-attributes: all
+spec-name: Interface
+spec-type: interface
+type: spec
diff --git a/rtemsspec/tests/spec/other.yml b/rtemsspec/tests/spec/other.yml
new file mode 100644
index 00000000..ae7b8770
--- /dev/null
+++ b/rtemsspec/tests/spec/other.yml
@@ -0,0 +1,21 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+- role: spec-refinement
+ spec-key: type
+ spec-value: other
+ uid: root
+spec-description: null
+spec-example: null
+spec-info:
+ dict:
+ attributes: {}
+ description: null
+ mandatory-attributes: all
+spec-name: Other
+spec-type: other
+type: spec
diff --git a/rtemsspec/tests/spec/requirement.yml b/rtemsspec/tests/spec/requirement.yml
new file mode 100644
index 00000000..72efb21c
--- /dev/null
+++ b/rtemsspec/tests/spec/requirement.yml
@@ -0,0 +1,24 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+- role: spec-refinement
+ spec-key: type
+ spec-value: requirement
+ uid: root
+spec-description: null
+spec-example: null
+spec-info:
+ dict:
+ attributes:
+ requirement-type:
+ description: null
+ spec-type: name
+ description: null
+ mandatory-attributes: all
+spec-name: Requirement
+spec-type: requirement
+type: spec
diff --git a/rtemsspec/tests/spec/root.yml b/rtemsspec/tests/spec/root.yml
new file mode 100644
index 00000000..4c3ea506
--- /dev/null
+++ b/rtemsspec/tests/spec/root.yml
@@ -0,0 +1,18 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links: []
+spec-description: null
+spec-example: null
+spec-info:
+ dict:
+ attributes:
+ type:
+ description: null
+ spec-type: name
+ description: null
+ mandatory-attributes: all
+spec-name: Root
+spec-type: root
+type: spec
diff --git a/rtemsspec/tests/spec/spec.yml b/rtemsspec/tests/spec/spec.yml
new file mode 100644
index 00000000..b4e42b2e
--- /dev/null
+++ b/rtemsspec/tests/spec/spec.yml
@@ -0,0 +1,21 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+- role: spec-refinement
+ spec-key: type
+ spec-value: spec
+ uid: root
+spec-description: null
+spec-example: null
+spec-info:
+ dict:
+ attributes: {}
+ description: null
+ mandatory-attributes: all
+spec-name: Spec
+spec-type: spec
+type: spec
diff --git a/rtemsspec/tests/spec/test-case.yml b/rtemsspec/tests/spec/test-case.yml
new file mode 100644
index 00000000..9eeeb674
--- /dev/null
+++ b/rtemsspec/tests/spec/test-case.yml
@@ -0,0 +1,21 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+- role: spec-refinement
+ spec-key: type
+ spec-value: test-case
+ uid: root
+spec-description: null
+spec-example: null
+spec-info:
+ dict:
+ attributes: {}
+ description: null
+ mandatory-attributes: all
+spec-name: Test Case
+spec-type: test-case
+type: spec
diff --git a/rtemsspec/tests/spec/test-suite.yml b/rtemsspec/tests/spec/test-suite.yml
new file mode 100644
index 00000000..8818c410
--- /dev/null
+++ b/rtemsspec/tests/spec/test-suite.yml
@@ -0,0 +1,21 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+ uid: root
+- role: spec-refinement
+ spec-key: type
+ spec-value: test-suite
+ uid: root
+spec-description: null
+spec-example: null
+spec-info:
+ dict:
+ attributes: {}
+ description: null
+ mandatory-attributes: all
+spec-name: Test Suite
+spec-type: test-suite
+type: spec
diff --git a/rtemsspec/tests/test_applconfig.py b/rtemsspec/tests/test_applconfig.py
new file mode 100644
index 00000000..e98db813
--- /dev/null
+++ b/rtemsspec/tests/test_applconfig.py
@@ -0,0 +1,589 @@
+# SPDX-License-Identifier: BSD-2-Clause
+""" Unit tests for the rtemsspec.applconfig module. """
+
+# Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import os
+
+from rtemsspec.applconfig import generate
+from rtemsspec.items import ItemCache
+from rtemsspec.tests.util import create_item_cache_config_and_copy_spec
+
+
+def test_applconfig(tmpdir):
+ item_cache_config = create_item_cache_config_and_copy_spec(
+ tmpdir, "spec-applconfig", with_spec_types=True)
+ item_cache = ItemCache(item_cache_config)
+
+ applconfig_config = {}
+ g_rst = os.path.join(tmpdir, "g.rst")
+ applconfig_config["groups"] = [{"uid": "/g", "target": g_rst}]
+ doxygen_h = os.path.join(tmpdir, "doxygen.h")
+ applconfig_config["doxygen-target"] = doxygen_h
+ generate(applconfig_config, item_cache)
+
+ with open(g_rst, "r") as src:
+ content = """.. SPDX-License-Identifier: CC-BY-SA-4.0
+
+.. Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+
+group name
+==========
+
+description
+
+.. index:: a
+.. index:: index a
+
+.. _a:
+
+a
+-
+
+CONSTANT:
+ ``a``
+
+OPTION TYPE:
+ This configuration option is a boolean feature define.
+
+DEFAULT CONFIGURATION:
+ default a
+
+DESCRIPTION:
+ description a
+
+NOTES:
+ notes a
+
+ references:
+
+ * :ref:`b`
+
+ * :ref:`Terminate`
+
+ * ``func()``
+
+ * ``td``
+
+.. index:: b
+
+.. _b:
+
+b
+-
+
+CONSTANT:
+ ``b``
+
+OPTION TYPE:
+ This configuration option is a boolean feature define.
+
+DEFAULT CONFIGURATION:
+ If this configuration option is undefined, then the described feature is \
+not
+ enabled.
+
+DESCRIPTION:
+ description b
+
+NOTES:
+ None.
+
+.. index:: c
+
+.. _c:
+
+c
+-
+
+CONSTANT:
+ ``c``
+
+OPTION TYPE:
+ This configuration option is an integer define.
+
+DEFAULT VALUE:
+ The default value is 13.
+
+VALUE CONSTRAINTS:
+ The value of this configuration option shall satisfy all of the following
+ constraints:
+
+ * It shall be greater than or equal to -1.
+
+ * It shall be less than or equal to 99.
+
+ * custom c 1
+
+ * custom c 2
+
+ * constraint d
+
+DESCRIPTION:
+ description c
+
+NOTES:
+ notes c
+
+.. index:: e
+
+.. _e:
+
+e
+-
+
+CONSTANT:
+ ``e``
+
+OPTION TYPE:
+ This configuration option is an integer define.
+
+DEFAULT VALUE:
+ The default value is 7.
+
+VALUE CONSTRAINTS:
+ The value of this configuration option shall be greater than or equal to \
+-2.
+
+DESCRIPTION:
+ description e
+
+NOTES:
+ None.
+
+.. index:: f
+
+.. _f:
+
+f
+-
+
+CONSTANT:
+ ``f``
+
+OPTION TYPE:
+ This configuration option is an integer define.
+
+DEFAULT VALUE:
+ The default value is 1.
+
+VALUE CONSTRAINTS:
+ The value of this configuration option shall be less than or equal to 2.
+
+DESCRIPTION:
+ description f
+
+NOTES:
+ None.
+
+.. index:: h
+
+.. _h:
+
+h
+-
+
+CONSTANT:
+ ``h``
+
+OPTION TYPE:
+ This configuration option is an integer define.
+
+DEFAULT VALUE:
+ The default value is 1.
+
+VALUE CONSTRAINTS:
+ custom h
+
+DESCRIPTION:
+ description h
+
+NOTES:
+ None.
+
+.. index:: i
+
+.. _i:
+
+i
+-
+
+CONSTANT:
+ ``i``
+
+OPTION TYPE:
+ This configuration option is an integer define.
+
+DEFAULT VALUE:
+ The default value is 1.
+
+VALUE CONSTRAINTS:
+ The value of this configuration option shall be
+ an element of {1, 2, 3}.
+
+DESCRIPTION:
+ description i
+
+NOTES:
+ None.
+
+.. index:: j
+
+.. _j:
+
+j
+-
+
+CONSTANT:
+ ``j``
+
+OPTION TYPE:
+ This configuration option is an integer define.
+
+DEFAULT VALUE:
+ Foo bar.
+
+VALUE CONSTRAINTS:
+ The value of this configuration option shall satisfy all of the following
+ constraints:
+
+ * It shall be an element of [1, 2].
+
+ * constraint d
+
+DESCRIPTION:
+ description j
+
+NOTES:
+ None.
+
+.. index:: k
+
+.. _k:
+
+k
+-
+
+CONSTANT:
+ ``k``
+
+OPTION TYPE:
+ This configuration option is an integer define.
+
+DEFAULT VALUE:
+ The default value is 1.
+
+VALUE CONSTRAINTS:
+ The value of this configuration option shall satisfy all of the following
+ constraints:
+
+ * custom k 1
+
+ * custom k 2
+
+DESCRIPTION:
+ description k
+
+NOTES:
+ None.
+
+.. index:: l
+
+.. _l:
+
+l
+-
+
+CONSTANT:
+ ``l``
+
+OPTION TYPE:
+ This configuration option is an initializer define.
+
+DEFAULT VALUE:
+ The default value is 1.
+
+VALUE CONSTRAINTS:
+ The value of this configuration option shall be greater than or equal to 0
+ and less than or equal to 2.
+
+DESCRIPTION:
+ description l
+
+NOTES:
+ None.
+
+.. index:: m
+
+.. _m:
+
+m
+-
+
+CONSTANT:
+ ``m``
+
+OPTION TYPE:
+ This configuration option is an initializer define.
+
+DEFAULT VALUE:
+ The default value is 1.
+
+DESCRIPTION:
+ description m
+
+NOTES:
+ None.
+"""
+ assert content == src.read()
+ with open(doxygen_h, "r") as src:
+ content = """/* SPDX-License-Identifier: BSD-2-Clause */
+
+/*
+ * Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @defgroup RTEMSApplConfig Application Configuration Options
+ *
+ * @ingroup RTEMSAPI
+ */
+
+/**
+ * @defgroup RTEMSApplConfiggroupname group name
+ *
+ * @ingroup RTEMSApplConfig
+ *
+ * description
+ *
+ * @{
+ */
+
+/**
+ * @brief This configuration option is a boolean feature define.
+ *
+ * description a
+ *
+ * @par Default Configuration
+ * default a
+ *
+ * @par Notes
+ * @parblock
+ * notes a
+ *
+ * references:
+ *
+ * * #b
+ *
+ * * <a
+ * href=https://docs.rtems.org/branches/master/c-user/fatal_error.html#announcing-a-fatal-error>Announcing
+ * a Fatal Error</a>
+ *
+ * * func()
+ *
+ * * ::td
+ * @endparblock
+ */
+#define a
+
+/**
+ * @brief This configuration option is a boolean feature define.
+ *
+ * description b
+ *
+ * @par Default Configuration
+ * If this configuration option is undefined, then the described feature is not
+ * enabled.
+ */
+#define b
+
+/**
+ * @brief This configuration option is an integer define.
+ *
+ * description c
+ *
+ * @par Default Value
+ * The default value is 13.
+ *
+ * @par Value Constraints
+ * @parblock
+ * The value of this configuration option shall satisfy all of the following
+ * constraints:
+ *
+ * * It shall be greater than or equal to -1.
+ *
+ * * It shall be less than or equal to 99.
+ *
+ * * custom c 1
+ *
+ * * custom c 2
+ *
+ * * constraint d
+ * @endparblock
+ *
+ * @par Notes
+ * notes c
+ */
+#define c
+
+/**
+ * @brief This configuration option is an integer define.
+ *
+ * description e
+ *
+ * @par Default Value
+ * The default value is 7.
+ *
+ * @par Value Constraints
+ * The value of this configuration option shall be greater than or equal to -2.
+ */
+#define e
+
+/**
+ * @brief This configuration option is an integer define.
+ *
+ * description f
+ *
+ * @par Default Value
+ * The default value is 1.
+ *
+ * @par Value Constraints
+ * The value of this configuration option shall be less than or equal to 2.
+ */
+#define f
+
+/**
+ * @brief This configuration option is an integer define.
+ *
+ * description h
+ *
+ * @par Default Value
+ * The default value is 1.
+ *
+ * @par Value Constraints
+ * custom h
+ */
+#define h
+
+/**
+ * @brief This configuration option is an integer define.
+ *
+ * description i
+ *
+ * @par Default Value
+ * The default value is 1.
+ *
+ * @par Value Constraints
+ * The value of this configuration option shall be an element of {1, 2, 3}.
+ */
+#define i
+
+/**
+ * @brief This configuration option is an integer define.
+ *
+ * description j
+ *
+ * @par Default Value
+ * Foo bar.
+ *
+ * @par Value Constraints
+ * @parblock
+ * The value of this configuration option shall satisfy all of the following
+ * constraints:
+ *
+ * * It shall be an element of [1, 2].
+ *
+ * * constraint d
+ * @endparblock
+ */
+#define j
+
+/**
+ * @brief This configuration option is an integer define.
+ *
+ * description k
+ *
+ * @par Default Value
+ * The default value is 1.
+ *
+ * @par Value Constraints
+ * @parblock
+ * The value of this configuration option shall satisfy all of the following
+ * constraints:
+ *
+ * * custom k 1
+ *
+ * * custom k 2
+ * @endparblock
+ */
+#define k
+
+/**
+ * @brief This configuration option is an initializer define.
+ *
+ * description l
+ *
+ * @par Default Value
+ * The default value is 1.
+ *
+ * @par Value Constraints
+ * The value of this configuration option shall be greater than or equal to 0
+ * and less than or equal to 2.
+ */
+#define l
+
+/**
+ * @brief This configuration option is an initializer define.
+ *
+ * description m
+ *
+ * @par Default Value
+ * The default value is 1.
+ */
+#define m
+
+/** @} */
+"""
+ assert content == src.read()
diff --git a/rtemsspec/tests/test_build.py b/rtemsspec/tests/test_build.py
new file mode 100644
index 00000000..28e971fb
--- /dev/null
+++ b/rtemsspec/tests/test_build.py
@@ -0,0 +1,44 @@
+# SPDX-License-Identifier: BSD-2-Clause
+""" Unit tests for the rtemsspec.build module. """
+
+# Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+from rtemsspec.build import gather_files
+from rtemsspec.items import ItemCache
+from rtemsspec.tests.util import create_item_cache_config_and_copy_spec
+
+
+def test_build(tmpdir):
+ item_cache_config = create_item_cache_config_and_copy_spec(
+ tmpdir, "spec-build")
+ item_cache = ItemCache(item_cache_config)
+
+ build_config = {}
+ build_config["arch"] = "foo"
+ build_config["bsp"] = "bar"
+ build_config["enabled"] = ["A"]
+ build_config["sources"] = ["a", "b"]
+ build_config["uids"] = ["/g"]
+ files = gather_files(build_config, item_cache)
+ assert files == ["a", "b", "stu", "jkl", "mno", "abc", "def"]
diff --git a/rtemsspec/tests/test_content.py b/rtemsspec/tests/test_content.py
new file mode 100644
index 00000000..27118e1a
--- /dev/null
+++ b/rtemsspec/tests/test_content.py
@@ -0,0 +1,276 @@
+# SPDX-License-Identifier: BSD-2-Clause
+""" Unit tests for the rtemsspec.content module. """
+
+# Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import os
+import pytest
+
+from rtemsspec.content import Content, enabled_by_to_exp, \
+ ExpressionMapper, PythonExpressionMapper
+
+
+def test_tab():
+ content = Content("BSD-2-Clause", True)
+ assert content.tab == " "
+
+
+def test_append():
+ content = Content("BSD-2-Clause", True)
+ content.append("")
+ assert str(content) == """
+"""
+ content.append(["a", "b"])
+ assert str(content) == """
+a
+b
+"""
+ with content.indent():
+ content.append(["c", "d"])
+ assert str(content) == """
+a
+b
+ c
+ d
+"""
+
+
+def test_prepend():
+ content = Content("BSD-2-Clause", True)
+ content.prepend("")
+ assert str(content) == """
+"""
+ content.prepend(["a", "b"])
+ assert str(content) == """a
+b
+
+"""
+ with content.indent():
+ content.prepend(["c", "d"])
+ assert str(content) == """ c
+ d
+a
+b
+
+"""
+
+
+def test_add():
+ content = Content("BSD-2-Clause", True)
+ content.add("")
+ assert str(content) == ""
+ content.add("a")
+ assert str(content) == """a
+"""
+ content.add(["b", "c"])
+ assert str(content) == """a
+
+b
+c
+"""
+
+
+def test_wrap():
+ content = Content("BSD-2-Clause", True)
+ content.wrap("")
+ assert str(content) == ""
+ content.wrap("a")
+ assert str(content) == """a
+"""
+ content.wrap(["b", "c"])
+ assert str(content) == """a
+
+b c
+"""
+ content.wrap(content)
+ assert str(content) == """a
+
+b c
+
+a
+
+b c
+"""
+ content = Content("BSD-2-Clause", True)
+ content.wrap("\n")
+ assert str(content) == ""
+ content = Content("BSD-2-Clause", True)
+ content.wrap(["a", "", " b"])
+ assert str(content) == """a
+
+ b
+"""
+ content = Content("BSD-2-Clause", True)
+ content.wrap([
+ "a", "", "* b",
+ "cccccccccccc ddddddddddddddddd eeeeeeeeeeeeeeeeeeee ffffffffffffffff",
+ "ggggggggggggggggg hhhhhhhhhhhhhhhhhhhhhhhhh iiiiiiiiiiiiiiii",
+ "jjjjjjjjjjjjjjjjjjj"
+ ])
+ assert str(content) == """a
+
+* b cccccccccccc ddddddddddddddddd eeeeeeeeeeeeeeeeeeee ffffffffffffffff
+ ggggggggggggggggg hhhhhhhhhhhhhhhhhhhhhhhhh iiiiiiiiiiiiiiii
+ jjjjjjjjjjjjjjjjjjj
+"""
+
+
+def test_paste():
+ content = Content("BSD-2-Clause", True)
+ content.paste("")
+ assert str(content) == ""
+ content.paste("a")
+ assert str(content) == """a
+"""
+ content.paste(["b", "c"])
+ assert str(content) == """a b c
+"""
+ content.paste(content)
+ assert str(content) == """a b c a b c
+"""
+ content = Content("BSD-2-Clause", True)
+ content.paste("\n")
+ assert str(content) == ""
+ content.append("")
+ content.paste("a")
+ assert str(content) == """
+a
+"""
+ content = Content("BSD-2-Clause", True)
+ content.paste(["a", "b", "", "c"])
+ assert str(content) == """a b
+
+c
+"""
+ content = Content("BSD-2-Clause", True)
+ content.paste(["a", "b", "", " c"])
+ assert str(content) == """a b
+
+ c
+"""
+
+
+def test_add_blank_line():
+ content = Content("BSD-2-Clause", True)
+ content.add_blank_line()
+ assert str(content) == """
+"""
+
+
+def test_indent():
+ content = Content("BSD-2-Clause", True)
+ content.add_blank_line()
+ content.append("x")
+ content.indent_lines(3)
+ assert str(content) == """
+ x
+"""
+
+
+def test_write(tmpdir):
+ content = Content("BSD-2-Clause", True)
+ content.append("x")
+ path = os.path.join(tmpdir, "x", "y")
+ content.write(path)
+ with open(path, "r") as src:
+ assert src.read() == """x
+"""
+ tmpdir.chdir()
+ path = "z"
+ content.write(path)
+ with open(path, "r") as src:
+ assert src.read() == """x
+"""
+
+
+def to_c_exp(enabled_by):
+ return enabled_by_to_exp(enabled_by, ExpressionMapper())
+
+
+def test_enabled_by_to_exp():
+ assert to_c_exp(True) == "1"
+ assert to_c_exp(False) == "0"
+ assert to_c_exp([]) == ""
+ assert to_c_exp(["A"]) == "defined(A)"
+ assert to_c_exp(["B"]) == "defined(B)"
+ assert to_c_exp(["A", "B"]) == "defined(A) || defined(B)"
+ assert to_c_exp({"not": "A"}) == "!defined(A)"
+ assert to_c_exp({"and": ["A", "B"]}) == "defined(A) && defined(B)"
+ assert to_c_exp({"and": ["A", "B", {
+ "not": "C"
+ }]}) == "defined(A) && defined(B) && !defined(C)"
+ assert to_c_exp(
+ {
+ "not": {
+ "and":
+ ["A",
+ {
+ "not": ["B", "C",
+ {
+ "and": ["D",
+ {
+ "not": "E"
+ }]
+ }]
+ }]
+ }
+ }
+ ) == "!(defined(A) && !(defined(B) || defined(C) || (defined(D) && !defined(E))))"
+ with pytest.raises(KeyError):
+ to_c_exp({"foo": "bar"})
+ with pytest.raises(ValueError):
+ to_c_exp({"foo": "bar", "bla": "blub"})
+
+
+def to_python_exp(enabled_by):
+ return enabled_by_to_exp(enabled_by, PythonExpressionMapper())
+
+
+def test_enabled_by_to_python_exp():
+ assert to_python_exp(True) == "True"
+ assert to_python_exp(False) == "False"
+ assert to_python_exp([]) == ""
+ assert to_python_exp(["A"]) == "A"
+ assert to_python_exp(["B"]) == "B"
+ assert to_python_exp(["A", "B"]) == "A or B"
+ assert to_python_exp({"not": "A"}) == "not A"
+ assert to_python_exp({"and": ["A", "B"]}) == "A and B"
+ assert to_python_exp({"and": ["A", "B", {
+ "not": "C"
+ }]}) == "A and B and not C"
+ assert to_python_exp({
+ "not": {
+ "and": ["A", {
+ "not": ["B", "C", {
+ "and": ["D", {
+ "not": "E"
+ }]
+ }]
+ }]
+ }
+ }) == "not (A and not (B or C or (D and not E)))"
+ with pytest.raises(KeyError):
+ to_python_exp({"foo": "bar"})
+ with pytest.raises(ValueError):
+ to_python_exp({"foo": "bar", "bla": "blub"})
diff --git a/rtemsspec/tests/test_content_c.py b/rtemsspec/tests/test_content_c.py
new file mode 100644
index 00000000..81f15727
--- /dev/null
+++ b/rtemsspec/tests/test_content_c.py
@@ -0,0 +1,474 @@
+# SPDX-License-Identifier: BSD-2-Clause
+""" Unit tests for the rtemsspec.content module. """
+
+# Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import pytest
+
+from rtemsspec.content import CContent, CInclude
+
+
+def test_doxyfy():
+ content = CContent()
+ content.doxyfy(None)
+ assert str(content) == ""
+ content.doxyfy(" ")
+ assert str(content) == ""
+ content.doxyfy([" "])
+ assert str(content) == ""
+ content.doxyfy(CContent())
+ assert str(content) == ""
+ content.doxyfy(""".. code-block:: c
+
+ abc
+
+ def
+
+ghi
+
+.. code-block:: c
+""")
+ assert str(content) == """@code
+abc
+
+def
+@endcode
+
+ghi
+
+@code
+@endcode
+"""
+
+
+def test_add_have_config():
+ content = CContent()
+ content.add_have_config()
+ assert str(content) == """#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+"""
+ content.add_have_config()
+ assert str(content) == """#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+"""
+
+
+def test_add_includes():
+ assert not CInclude("a") == "a"
+ content = CContent()
+ content.add_includes([])
+ assert str(content) == ""
+ content = CContent()
+ content.add_includes([CInclude("a"), CInclude("a")])
+ assert str(content) == """#include <a>
+"""
+ content.add_includes([CInclude("b")])
+ assert str(content) == """#include <a>
+
+#include <b>
+"""
+ content = CContent()
+ content.add_includes([CInclude("c"), CInclude("b")], local=True)
+ assert str(content) == """#include "b"
+#include "c"
+"""
+ content = CContent()
+ content.add_includes([CInclude("d/f"), CInclude("d/e")])
+ assert str(content) == """#include <d/e>
+#include <d/f>
+"""
+ content = CContent()
+ content.add_includes([CInclude("h"), CInclude("g/h")])
+ assert str(content) == """#include <h>
+#include <g/h>
+"""
+ content = CContent()
+ content.add_includes([CInclude("i/l/k"), CInclude("i/j/k")])
+ assert str(content) == """#include <i/j/k>
+#include <i/l/k>
+"""
+ content = CContent()
+ content.add_includes([CInclude("a", "X"), CInclude("a")])
+ assert str(content) == """#if X
+ #include <a>
+#endif
+"""
+ content = CContent()
+ content.add_includes([CInclude("a"), CInclude("a", "X")])
+ assert str(content) == """#if X
+ #include <a>
+#endif
+"""
+ content = CContent()
+ content.add_includes([CInclude("a", "X"), CInclude("b", "X")])
+ assert str(content) == """#if X
+ #include <a>
+ #include <b>
+#endif
+"""
+ content = CContent()
+ content.add_includes(
+ [CInclude("a", "Y"),
+ CInclude("a", "X"),
+ CInclude("b", "X")])
+ assert str(content) == """#if X
+ #include <b>
+#endif
+
+#if X && Y
+ #include <a>
+#endif
+"""
+
+
+def test_comment_block():
+ content = CContent()
+ with content.comment_block():
+ assert not content.gap
+ content.add("")
+ assert not content.gap
+ assert str(content) == """/*
+"""
+ content.add("a")
+ assert content.gap
+ assert str(content) == """/*
+ * a
+"""
+ content.add("b")
+ assert content.gap
+ assert str(content) == """/*
+ * a
+ *
+ * b
+"""
+ content.gap = False
+ content.add("c")
+ assert content.gap
+ assert str(content) == """/*
+ * a
+ *
+ * b
+ * c
+"""
+
+
+def test_for_loop():
+ content = CContent()
+ with content.for_loop("i = 0", "i < 3", "++i"):
+ content.add("j[i] = i;")
+ assert str(content) == """for ( i = 0; i < 3; ++i ) {
+ j[i] = i;
+}
+"""
+ content = CContent()
+ with content.for_loop("iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii = 0",
+ "iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii < 3",
+ "++iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii"):
+ content.add("j[i] = i;")
+ assert str(content) == """for (
+ iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii = 0;
+ iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii < 3;
+ ++iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
+) {
+ j[i] = i;
+}
+"""
+
+
+def test_add_brief_description():
+ content = CContent()
+ content.add_brief_description("")
+ assert str(content) == ""
+ content.gap = True
+ content.add_brief_description(
+ "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT "
+ "HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED "
+ "WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES "
+ "OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE "
+ "DISCLAIMED.")
+ assert str(content) == """
+@brief THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED.
+"""
+
+
+def test_add_param_description():
+ content = CContent()
+ content.add_param_description([])
+ assert str(content) == ""
+ params = [{"description": "A", "dir": None, "name": "a"}]
+ content.add_param_description(params)
+ assert str(content) == """@param a A
+"""
+ content = CContent()
+ params = [
+ {
+ "description": "A",
+ "dir": None,
+ "name": "a"
+ },
+ {
+ "description": "B",
+ "dir": "in",
+ "name": "b"
+ },
+ {
+ "description": "C",
+ "dir": "out",
+ "name": "c"
+ },
+ {
+ "description": "D",
+ "dir": "inout",
+ "name": "d"
+ },
+ ]
+ content.add_param_description(params, lambda x: x + x)
+ assert str(content) == """@param a AA
+
+@param[in] b BB
+
+@param[out] c CC
+
+@param[in,out] d DD
+"""
+
+
+def test_add_description_block():
+ content = CContent()
+ content.add_description_block("", None)
+ assert str(content) == ""
+ content.add_description_block("a", "b")
+ assert str(content) == """/**
+ * @brief a
+ *
+ * b
+ */
+"""
+ content = CContent()
+ content.add_description_block("a", None)
+ assert str(content) == """/**
+ * @brief a
+ */
+"""
+ content = CContent()
+ content.add_description_block(None, "b")
+ assert str(content) == """/**
+ * b
+ */
+"""
+
+
+def test_add_to_group():
+ content = CContent()
+ with content.add_to_group("a"):
+ content.add("b")
+ assert str(content) == """/**
+ * @addtogroup a
+ *
+ * @{
+ */
+
+b
+
+/** @} */
+"""
+
+
+def test_function():
+ content = CContent()
+ content.call_function("a =", "b", [])
+ assert str(content) == """a = b();
+"""
+ content = CContent()
+ content.call_function(None, "a", [
+ "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
+ ])
+ assert str(content) == """a(
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+);
+"""
+ content = CContent()
+ content.call_function(
+ None,
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ [])
+ assert str(
+ content
+ ) == """aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa();
+"""
+ content = CContent()
+ content.call_function("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa =",
+ "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", [])
+ assert str(content) == """aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa =
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb();
+"""
+ content = CContent()
+ content.declare_function("a", "b", [])
+ assert str(content) == """a b( void );
+"""
+ content = CContent()
+ content.declare_function("a *", "b", [])
+ assert str(content) == """a *b( void );
+"""
+ content = CContent()
+ content.declare_function("a", "b", ["..."])
+ assert str(content) == """a b( ... );
+"""
+ content = CContent()
+ content.declare_function("a *", "b", [
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x",
+ "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy y",
+ "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz z", "..."
+ ])
+ assert str(content) == """a *b(
+ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x,
+ yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy y,
+ zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz z,
+ ...
+);
+"""
+ content = CContent()
+ content.declare_function(
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa *",
+ "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", [
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x",
+ "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy *y",
+ "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz *( *z )( void )"
+ ])
+ assert str(content) == """aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa *
+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb(
+ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x,
+ yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy *y,
+ zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz *( *z )( void )
+);
+"""
+ content = CContent()
+ content.declare_function("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa *",
+ "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", [])
+ assert str(content) == """aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa *
+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb( void );
+"""
+ content = CContent()
+ with content.function("a", "b", []):
+ content.add("c")
+ assert str(content) == """a b( void )
+{
+ c
+}
+"""
+
+
+def test_condition():
+ content = CContent()
+ with content.condition("a"):
+ content.add("b")
+ assert str(content) == """if ( a ) {
+ b
+}
+"""
+ content = CContent()
+ with content.first_condition("a"):
+ content.add("b")
+ with content.next_condition("c"):
+ content.add("d")
+ with content.final_condition(None):
+ content.add("e")
+ assert str(content) == """if ( a ) {
+ b
+} else if ( c ) {
+ d
+} else {
+ e
+}
+"""
+
+
+def test_add_paragraph():
+ content = CContent()
+ content.add_paragraph("a", "")
+ assert str(content) == ""
+ content.add_paragraph("a", "b")
+ assert str(content) == """@par a
+b
+"""
+ content = CContent()
+ with content.doxygen_block():
+ content.add_paragraph("a", ["b", "", "c"])
+ assert str(content) == """/**
+ * @par a
+ * @parblock
+ * b
+ *
+ * c
+ * @endparblock
+ */
+"""
+
+
+def test_prepend_copyrights_and_licenses():
+ content = CContent()
+ content.add("x")
+ assert str(content) == """x
+"""
+ content.register_copyright("Copyright (C) 1234 Foo Bar")
+ content.prepend_copyrights_and_licenses()
+ content.prepend_spdx_license_identifier()
+ assert str(content) == """/* SPDX-License-Identifier: BSD-2-Clause */
+
+/*
+ * Copyright (C) 1234 Foo Bar
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+x
+"""
diff --git a/rtemsspec/tests/test_content_copyrights.py b/rtemsspec/tests/test_content_copyrights.py
new file mode 100644
index 00000000..b92c06bc
--- /dev/null
+++ b/rtemsspec/tests/test_content_copyrights.py
@@ -0,0 +1,78 @@
+# SPDX-License-Identifier: BSD-2-Clause
+""" Unit tests for the rtemsspec.content module. """
+
+# Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import pytest
+
+from rtemsspec.content import Copyright
+from rtemsspec.content import Copyrights
+
+
+def test_copyright_get_statement():
+ c = Copyright("John Doe")
+ assert "Copyright (C) John Doe" == c.get_statement()
+ c.add_year("3")
+ assert "Copyright (C) 3 John Doe" == c.get_statement()
+ c.add_year("3")
+ assert "Copyright (C) 3 John Doe" == c.get_statement()
+ c.add_year("5")
+ assert "Copyright (C) 3, 5 John Doe" == c.get_statement()
+ c.add_year("4")
+ assert "Copyright (C) 3, 5 John Doe" == c.get_statement()
+ c.add_year("2")
+ assert "Copyright (C) 2, 5 John Doe" == c.get_statement()
+
+
+def test_copyright_lt():
+ a = Copyright("A")
+ b = Copyright("B")
+ c = Copyright("C")
+ assert b < a
+ assert c < a
+ assert c < b
+ b.add_year("1")
+ assert b < c
+ a.add_year("2")
+ assert a < b
+
+
+def test_copyrights_register():
+ c = Copyrights()
+ with pytest.raises(ValueError):
+ c.register("abc")
+ c.register("Copyright (C) A")
+ c.register("Copyright (C) 2 A")
+ c.register("Copyright (C) 2, 3 A")
+ c.register("Copyright (C) D")
+ c.register("Copyright (C) 1 D")
+ c.register("Copyright (C) 1, 4 D")
+ c.register("Copyright (C) C")
+ c.register("Copyright (C) 1 B")
+ s = c.get_statements()
+ assert 4 == len(s)
+ assert "Copyright (C) C" == s[0]
+ assert "Copyright (C) 2, 3 A" == s[1]
+ assert "Copyright (C) 1, 4 D" == s[2]
+ assert "Copyright (C) 1 B" == s[3]
diff --git a/rtemsspec/tests/test_content_sphinx.py b/rtemsspec/tests/test_content_sphinx.py
new file mode 100644
index 00000000..6581b173
--- /dev/null
+++ b/rtemsspec/tests/test_content_sphinx.py
@@ -0,0 +1,274 @@
+# SPDX-License-Identifier: BSD-2-Clause
+""" Unit tests for the rtemsspec.sphinxcontent module. """
+
+# Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import pytest
+
+from rtemsspec.sphinxcontent import get_reference, get_label, \
+ SphinxContent, SphinxMapper
+from rtemsspec.items import Item, ItemCache, ItemMapper
+from rtemsspec.tests.util import create_item_cache_config_and_copy_spec
+
+
+def test_add_label():
+ content = SphinxContent()
+ content.add_label("x")
+ assert str(content) == """.. _x:
+"""
+
+
+def test_directive():
+ content = SphinxContent()
+ with content.directive("x"):
+ content.add("y")
+ assert str(content) == """.. x::
+
+ y
+"""
+ with content.directive("z", "xy", [":a:", ":b:"]):
+ content.add("c")
+ assert str(content) == """.. x::
+
+ y
+
+.. z:: xy
+ :a:
+ :b:
+
+ c
+"""
+
+
+def test_add_header():
+ content = SphinxContent()
+ content.add_header("x")
+ assert str(content) == """x
+=
+"""
+ content.add_header("yz", 1)
+ assert str(content) == """x
+=
+
+yz
+**
+"""
+
+
+def test_add_header_with_label():
+ content = SphinxContent()
+ label = content.add_header_with_label("x", 1)
+ assert label == "SectionX"
+ assert str(content) == """.. _SectionX:
+
+x
+*
+"""
+ label = content.add_header_with_label("yz w", 2)
+ assert label == "SectionYzW"
+ assert str(content) == """.. _SectionX:
+
+x
+*
+
+.. _SectionYzW:
+
+yz w
+====
+"""
+
+
+def test_get_reference():
+ assert get_reference("a") == ":ref:`a`"
+ assert get_reference("a", "b") == ":ref:`b <a>`"
+
+
+def test_get_label():
+ assert get_label("ab cd") == "AbCd"
+
+
+def test_section():
+ content = SphinxContent()
+ with content.section("ab cd") as label:
+ content.add(label)
+ with content.section("ef gh") as label2:
+ content.add(label2)
+ assert str(content) == """.. _SectionAbCd:
+
+ab cd
+=====
+
+SectionAbCd
+
+.. _SectionEfGh:
+
+ef gh
+-----
+
+SectionEfGh
+"""
+
+
+def test_list_item():
+ content = SphinxContent()
+ with content.list_item("ab cd"):
+ content.paste("ef gh")
+ with content.list_item("ij kl"):
+ content.add("mn op")
+ content.paste("qr st")
+ with content.list_item("uv"):
+ pass
+ content.add_list_item("wx")
+ assert str(content) == """* ab cd ef gh
+
+ * ij kl
+
+ mn op qr st
+
+* uv
+
+* wx
+"""
+
+
+def test_add_list():
+ content = SphinxContent()
+ content.add_list([], "a")
+ assert str(content) == ""
+ content.add_list(["b", "c"], "a", "d")
+ assert str(content) == """a
+
+* b
+
+* c
+
+d
+"""
+ content = SphinxContent()
+ content.add_list(["b", "c"], add_blank_line=True)
+ assert str(content) == """* b
+
+* c
+
+"""
+
+
+def test_append():
+ content = SphinxContent()
+ content.append("x")
+ assert str(content) == """x
+"""
+ with content.indent():
+ content.append("y")
+ assert str(content) == """x
+ y
+"""
+ content.append("")
+ assert str(content) == """x
+ y
+
+"""
+
+
+def test_add_index_entries():
+ content = SphinxContent()
+ content.add_index_entries(["x", "y"])
+ assert str(content) == """.. index:: x
+.. index:: y
+"""
+ content.add_index_entries("z")
+ assert str(content) == """.. index:: x
+.. index:: y
+
+.. index:: z
+"""
+
+
+def test_add_definition_item():
+ content = SphinxContent()
+ content.add_definition_item("x", ["y", "z"])
+ assert str(content) == """x
+ y
+ z
+"""
+ content = SphinxContent()
+ content.add_definition_item("a", "\n b\n")
+ assert str(content) == """a
+ b
+"""
+ content = SphinxContent()
+ content.add_definition_item("a", "\n b\nc", wrap=True)
+ assert str(content) == """a
+ b c
+"""
+
+
+def test_definition_item():
+ content = SphinxContent()
+ with content.definition_item("x"):
+ content.add(["y", "z"])
+ assert str(content) == """x
+ y
+ z
+"""
+
+
+def test_license():
+ content = SphinxContent()
+ with pytest.raises(ValueError):
+ content.register_license("x")
+ content.register_license("CC-BY-SA-4.0")
+ assert str(content) == ""
+ content.add_licence_and_copyrights()
+ assert str(content) == """.. SPDX-License-Identifier: CC-BY-SA-4.0
+
+"""
+
+
+def test_license_and_copyrights():
+ content = SphinxContent()
+ with pytest.raises(ValueError):
+ content.register_license("x")
+ content.register_copyright("Copyright (C) A")
+ assert str(content) == ""
+ content.add_licence_and_copyrights()
+ assert str(content) == """.. SPDX-License-Identifier: CC-BY-SA-4.0
+
+.. Copyright (C) A
+
+"""
+
+
+def test_substitute(tmpdir):
+ config = create_item_cache_config_and_copy_spec(tmpdir,
+ "spec-sphinx",
+ with_spec_types=True)
+ item_cache = ItemCache(config)
+ mapper = SphinxMapper(item_cache["/x"])
+ with pytest.raises(KeyError):
+ mapper.substitute("${x:/y}")
+ assert mapper.substitute("${x:/term}") == ":term:`y`"
+ assert mapper.substitute("${x:/plural}") == ":term:`ys <y>`"
+ mapper.add_get_value("other:/name", lambda ctx: ctx.value[ctx.key])
+ assert mapper.substitute("${y:/name}") == "foobar"
diff --git a/rtemsspec/tests/test_glossary.py b/rtemsspec/tests/test_glossary.py
new file mode 100644
index 00000000..2682b5da
--- /dev/null
+++ b/rtemsspec/tests/test_glossary.py
@@ -0,0 +1,91 @@
+# SPDX-License-Identifier: BSD-2-Clause
+""" Unit tests for the rtemsspec.glossary module. """
+
+# Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import os
+
+from rtemsspec.glossary import generate
+from rtemsspec.items import ItemCache
+from rtemsspec.tests.util import create_item_cache_config_and_copy_spec
+
+
+def test_glossary(tmpdir):
+ item_cache_config = create_item_cache_config_and_copy_spec(
+ tmpdir, "spec-glossary", with_spec_types=True)
+ item_cache = ItemCache(item_cache_config)
+
+ glossary_config = {}
+ glossary_config["project-groups"] = ["/g"]
+ project_glossary = os.path.join(tmpdir, "project", "glossary.rst")
+ glossary_config["project-target"] = project_glossary
+ doc = {}
+ doc["rest-source-paths"] = [str(tmpdir)]
+ document_glossary = os.path.join(tmpdir, "document", "glossary.rst")
+ doc["target"] = document_glossary
+ glossary_config["documents"] = [doc]
+ generate(glossary_config, item_cache)
+
+ with open(project_glossary, "r") as src:
+ content = """.. SPDX-License-Identifier: CC-BY-SA-4.0
+
+.. Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+
+Glossary
+********
+
+.. glossary::
+ :sorted:
+
+ T
+ Term text $:term:`U` :term:`T`
+ term.
+
+ U
+ Term text U.
+
+ V
+ Term text V.
+"""
+ assert content == src.read()
+
+ with open(document_glossary, "r") as src:
+ content = """.. SPDX-License-Identifier: CC-BY-SA-4.0
+
+.. Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+
+Glossary
+********
+
+.. glossary::
+ :sorted:
+
+ T
+ Term text $:term:`U` :term:`T`
+ term.
+
+ U
+ Term text U.
+"""
+ assert content == src.read()
diff --git a/rtemsspec/tests/test_interface.py b/rtemsspec/tests/test_interface.py
new file mode 100644
index 00000000..c6aa625b
--- /dev/null
+++ b/rtemsspec/tests/test_interface.py
@@ -0,0 +1,354 @@
+# SPDX-License-Identifier: BSD-2-Clause
+""" Unit tests for the rtemsspec.interface module. """
+
+# Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import os
+import pytest
+
+from rtemsspec.interface import generate
+from rtemsspec.items import EmptyItemCache, ItemCache
+from rtemsspec.tests.util import create_item_cache_config_and_copy_spec
+
+
+def test_interface(tmpdir):
+ interface_config = {}
+ interface_config["item-level-interfaces"] = []
+ base_directory = os.path.join(tmpdir, "base")
+ interface_domains = {"/domain-abc": base_directory}
+ interface_config["domains"] = interface_domains
+
+ generate(interface_config, EmptyItemCache())
+
+ interface_config["item-level-interfaces"] = ["/command-line"]
+
+ item_cache_config = create_item_cache_config_and_copy_spec(
+ tmpdir, "spec-interface", with_spec_types=True)
+ generate(interface_config, ItemCache(item_cache_config))
+
+ with open(os.path.join(base_directory, "include", "h.h"), "r") as src:
+ content = """/* SPDX-License-Identifier: BSD-2-Clause */
+
+/**
+ * @file
+ *
+ * @ingroup GroupA
+ * @ingroup GroupB
+ * @ingroup GroupC
+ */
+
+/*
+ * Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _H_H
+#define _H_H
+
+#include <h3.h>
+#include <math.h>
+#include <stdint.h>
+
+#if !defined(ASM) && defined(RTEMS_SMP)
+ #include <h2.h>
+#endif
+
+#if defined(ASM) && defined(RTEMS_SMP)
+ #include <h4.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup GroupA Group A
+ *
+ * @brief Group A brief description.
+ *
+ * Group A description.
+ */
+
+/**
+ * @defgroup GroupB Group B
+ *
+ * @ingroup GroupA
+ */
+
+/* Forward declaration */
+struct Struct;
+
+/**
+ * @ingroup GroupB
+ *
+ * @brief Enum brief description.
+ *
+ * Enum description.
+ */
+typedef enum {
+ /**
+ * @brief Enumerator 0 brief description.
+ */
+ ENUMERATOR_0,
+
+ /**
+ * @brief Enumerator 1 brief description.
+ */
+ ENUMERATOR_1,
+
+ /**
+ * @brief Enumerator 2 brief description.
+ */
+ ENUMERATOR_2
+} Enum;
+
+/**
+ * @ingroup GroupA
+ */
+#if defined(A) || (B > C)
+ #define DEFINE ((float_t) 456)
+#elif defined(C) && defined(D)
+ #define DEFINE ((float_t) 789)
+#else
+ #define DEFINE \\
+ ((float_t) 123)
+#endif
+
+/**
+ * @ingroup GroupB
+ *
+ * @brief Enum B brief description.
+ */
+typedef enum EnumB {
+ /**
+ * @brief Enumerator B brief description.
+ */
+ ENUMERATOR_B = ENUMERATOR_A
+} EnumB;
+
+/**
+ * @ingroup GroupA
+ *
+ * @brief Function brief description.
+ *
+ * Function description. References to VeryLongFunction(), ::Integer, #Enum,
+ * #DEFINE, #VERY_LONG_MACRO, #Variable, ::ENUMERATOR_0, Struct, a, and
+ * interface.
+ *
+ * @param Param0 is parameter 0.
+ *
+ * @param[in] Param1 is parameter 1.
+ *
+ * @param[out] Param2 is parameter 2.
+ *
+ * @param[in,out] Param3 is parameter 3.
+ */
+void Function( int Param0, const int *Param1, int *Param2, int *Param3 );
+
+/**
+ * @ingroup GroupB
+ *
+ * @brief Very long function brief description.
+ *
+ * VeryLongFunction description.
+ *
+ * VeryLongFunction notes.
+ *
+ * @param VeryLongParam0 is very long parameter 0 with some super important and
+ * extra very long description which makes a lot of sense.
+ *
+ * @param[in] VeryLongParam1 is very long parameter 1.
+ *
+ * @param[out] VeryLongParam2 is very long parameter 2.
+ *
+ * @param[in,out] VeryLongParam3 is very long parameter 3.
+ *
+ * @retval 1 is returned, in case A.
+ *
+ * @retval 2 is returned, in case B.
+ *
+ * @return Sometimes some value. See Function().
+ */
+static inline int VeryLongFunction(
+ int VeryLongParam0,
+ const struct Struct *VeryLongParam1,
+ struct Struct *( *VeryLongParam2 )( void ),
+ struct Struct *VeryLongParam3
+)
+{
+ (void) VeryLongParam1;
+ (void) VeryLongParam2;
+ (void) VeryLongParam3;
+ return VeryLongParam0 + 1;
+}
+
+/**
+ * @ingroup GroupB
+ *
+ * @brief Very long macro brief description.
+ *
+ * @param VeryLongParam0 is very long parameter 0 with some super important and
+ * extra very long description which makes a lot of sense.
+ *
+ * @param[in] VeryLongParam1 is very long parameter 1.
+ *
+ * @param[out] VeryLongParam2 is very long parameter 2.
+ *
+ * @param[in,out] VeryLongParam3 is very long parameter 3.
+ *
+ * @retval 1 is returned, in case A.
+ *
+ * @retval 2 is returned, in case B.
+ *
+ * @return Sometimes some value.
+ */
+#define VERY_LONG_MACRO( \\
+ VeryLongParam0, \\
+ VeryLongParam1, \\
+ VeryLongParam2, \\
+ VeryLongParam3 \\
+) \\
+ do { \\
+ (void) VeryLongParam1; \\
+ (void) VeryLongParam2; \\
+ (void) VeryLongParam3; \\
+ } while ( 0 ); \\
+ VeryLongParam0 + 1;
+
+/**
+ * @ingroup GroupB
+ *
+ * @brief Short macro brief description.
+ *
+ * @param Param0 is parameter 0.
+ *
+ * @return Sometimes some value.
+ */
+#if 0
+ #define MACRO( Param0 )
+#else
+ #define MACRO( Param0 ) ( ( Param0 ) + 1 )
+#endif
+
+/**
+ * @ingroup GroupC
+ */
+struct Struct {
+ /**
+ * @brief Brief union description.
+ *
+ * Union description.
+ */
+ union {
+ /**
+ * @brief Brief member description.
+ *
+ * Member description.
+ */
+ uint32_t some_member;
+
+ /**
+ * @brief Brief struct description.
+ *
+ * struct description.
+ */
+ struct {
+ /**
+ * @brief Brief member 2 description.
+ *
+ * Member 2 description.
+ */
+ uint32_t some_member_2;
+
+ /**
+ * @brief Brief member 3 description.
+ *
+ * Member 3 description.
+ */
+ Enum some_member_3;
+ } some_struct;
+ } some_union;
+
+ /**
+ * @brief Brief member 4 description.
+ *
+ * Member 4 description.
+ */
+ Enum some_member_4;
+};
+
+/**
+ * @ingroup GroupB
+ *
+ * @brief Typedef Integer brief description.
+ *
+ * Typedef Integer description.
+ */
+typedef uint32_t Integer /* Some comment. */;
+
+/**
+ * @ingroup GroupB
+ */
+#if defined(RTEMS_SMP)
+ typedef uint32_t Integer3;
+#endif
+
+#if !defined(ASM)
+ /**
+ * @ingroup GroupC
+ *
+ * @brief Variable brief description.
+ *
+ * Variable description.
+ */
+ extern struct Struct *Variable;
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _H_H */
+"""
+ assert content == src.read()
diff --git a/rtemsspec/tests/test_interfacedoc.py b/rtemsspec/tests/test_interfacedoc.py
new file mode 100644
index 00000000..b9948fc8
--- /dev/null
+++ b/rtemsspec/tests/test_interfacedoc.py
@@ -0,0 +1,224 @@
+# SPDX-License-Identifier: BSD-2-Clause
+""" Unit tests for the rtemsspec.interfacedoc module. """
+
+# Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import os
+import pytest
+
+from rtemsspec.interfacedoc import generate
+from rtemsspec.items import EmptyItemCache, ItemCache
+from rtemsspec.tests.util import create_item_cache_config_and_copy_spec
+
+
+def test_interfacedoc(tmpdir):
+ doc_config = {}
+ doc_config["group"] = "/gb"
+ introduction_rst = os.path.join(tmpdir, "introduction.rst")
+ doc_config["introduction-target"] = introduction_rst
+ directives_rst = os.path.join(tmpdir, "directives.rst")
+ doc_config["directives-target"] = directives_rst
+
+ doc_config_2 = {}
+ doc_config_2["group"] = "/ga"
+ introduction_2_rst = os.path.join(tmpdir, "introduction-2.rst")
+ doc_config_2["introduction-target"] = introduction_2_rst
+ directives_2_rst = os.path.join(tmpdir, "directives-2.rst")
+ doc_config_2["directives-target"] = directives_2_rst
+
+ item_cache_config = create_item_cache_config_and_copy_spec(
+ tmpdir, "spec-interface", with_spec_types=True)
+ generate([doc_config, doc_config_2], ItemCache(item_cache_config))
+
+ with open(introduction_rst, "r") as src:
+ content = """.. SPDX-License-Identifier: CC-BY-SA-4.0
+
+.. Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+
+.. _GroupBIntroduction:
+
+Introduction
+============
+
+The directives provided by the Group B are:
+
+* :ref:`VeryLongFunction() <InterfaceVeryLongFunction>` - Very long function
+ brief description.
+
+* :ref:`VeryLongTypeFunction() <InterfaceVeryLongTypeFunction>` - Function
+ brief description with very long return type.
+
+* :ref:`VoidFunction() <InterfaceVoidFunction>`
+"""
+ assert content == src.read()
+
+ with open(directives_rst, "r") as src:
+ content = """.. SPDX-License-Identifier: CC-BY-SA-4.0
+
+.. Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+
+.. _GroupBDirectives:
+
+Directives
+==========
+
+.. _InterfaceVeryLongFunction:
+
+VeryLongFunction()
+------------------
+
+Very long function brief description.
+
+CALLING SEQUENCE:
+ .. code-block:: c
+
+ int VeryLongFunction(
+ int VeryLongParam0,
+ const struct Struct *VeryLongParam1,
+ struct Struct *( *VeryLongParam2 )( void ),
+ struct Struct *VeryLongParam3
+ );
+
+DIRECTIVE PARAMETERS:
+ VeryLongParam0
+ This parameter is very long parameter 0 with some super important and
+ extra very long description which makes a lot of sense.
+
+ VeryLongParam1
+ This parameter is very long parameter 1.
+
+ VeryLongParam2
+ This parameter is very long parameter 2.
+
+ VeryLongParam3
+ This parameter is very long parameter 3.
+
+DIRECTIVE RETURN VALUES:
+ 1
+ is returned, in case A.
+
+ 2
+ is returned, in case B.
+
+ Sometimes some value. See :ref:`Function() <InterfaceFunction>`.
+
+DESCRIPTION:
+ VeryLongFunction description.
+
+NOTES:
+ VeryLongFunction notes.
+
+.. _InterfaceVeryLongTypeFunction:
+
+VeryLongTypeFunction()
+----------------------
+
+Function brief description with very long return type.
+
+CALLING SEQUENCE:
+ .. code-block:: c
+
+ #if 1
+ NotSoVeryLongType VeryLongTypeFunction( void );
+ #else
+ VeryLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongType
+ VeryLongTypeFunction( void );
+ #endif
+
+DIRECTIVE RETURN VALUES:
+ This function returns an object with a very long type.
+
+.. _InterfaceVoidFunction:
+
+VoidFunction()
+--------------
+
+CALLING SEQUENCE:
+ .. code-block:: c
+
+ #if 1
+ void VoidFunction( void );
+ #endif
+"""
+ assert content == src.read()
+
+ with open(introduction_2_rst, "r") as src:
+ content = """.. SPDX-License-Identifier: CC-BY-SA-4.0
+
+.. Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+
+.. _GroupAIntroduction:
+
+Introduction
+============
+
+Group A brief description.
+
+Group A description. The directives provided by the Group A are:
+
+* :ref:`Function() <InterfaceFunction>` - Function brief description.
+"""
+ assert content == src.read()
+
+ with open(directives_2_rst, "r") as src:
+ content = """.. SPDX-License-Identifier: CC-BY-SA-4.0
+
+.. Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+
+.. _GroupADirectives:
+
+Directives
+==========
+
+.. _InterfaceFunction:
+
+Function()
+----------
+
+Function brief description.
+
+CALLING SEQUENCE:
+ .. code-block:: c
+
+ void Function( int Param0, const int *Param1, int *Param2, int *Param3 );
+
+DIRECTIVE PARAMETERS:
+ Param0
+ This parameter is parameter 0.
+
+ Param1
+ This parameter is parameter 1.
+
+ Param2
+ This parameter is parameter 2.
+
+ Param3
+ This parameter is parameter 3.
+
+DESCRIPTION:
+ Function description. References to :ref:`VeryLongFunction()
+ <InterfaceVeryLongFunction>`, Integer, Enum, DEFINE, VERY_LONG_MACRO,
+ Variable, ENUMERATOR_0, Struct, :ref:`a`, and interface.
+"""
+ assert content == src.read()
diff --git a/rtemsspec/tests/test_items_item.py b/rtemsspec/tests/test_items_item.py
new file mode 100644
index 00000000..511349ab
--- /dev/null
+++ b/rtemsspec/tests/test_items_item.py
@@ -0,0 +1,196 @@
+# SPDX-License-Identifier: BSD-2-Clause
+""" Unit tests for the rtemsspec.items module. """
+
+# Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import os
+import pytest
+
+from rtemsspec.items import EmptyItemCache, Item, ItemCache, Link
+
+
+def test_to_abs_uid():
+ item = Item(EmptyItemCache(), "/x/y", {})
+ assert item.to_abs_uid(".") == "/x/y"
+ assert item.to_abs_uid("z") == "/x/z"
+ assert item.to_abs_uid("/z") == "/z"
+ assert item.to_abs_uid("../z") == "/z"
+ assert item.to_abs_uid("../../z") == "/z"
+
+
+def test_uid():
+ item = Item(EmptyItemCache(), "x", {})
+ assert item.uid == "x"
+
+
+def test_contains():
+ data = {}
+ data["x"] = "y"
+ item = Item(EmptyItemCache(), "z", data)
+ assert "x" in item
+ assert "a" not in item
+
+
+def test_data():
+ data = {}
+ data["x"] = "y"
+ item = Item(EmptyItemCache(), "z", data)
+ assert item.data == {"x": "y"}
+
+
+def test_get_key_path():
+ data = {}
+ data["a"] = {"b": "c", "d": [1, 2, 3]}
+ data["x"] = "y"
+ item = Item(EmptyItemCache(), "z", data)
+ assert item.get_by_key_path("x") == "y"
+ assert item.get_by_key_path("a/d[2]") == 3
+ assert item.get_by_key_path("a/b/../d[0]") == 1
+ assert item.get_by_key_path("/a/b/../d[0]") == 1
+ assert item.get_by_key_path("../d[0]", "a/b") == 1
+ with pytest.raises(KeyError):
+ assert item.get_by_key_path("y")
+ with pytest.raises(ValueError):
+ assert item.get_by_key_path("[")
+ with pytest.raises(ValueError):
+ assert item.get_by_key_path("x[y]")
+
+
+def test_getitem():
+ data = {}
+ data["x"] = "y"
+ item = Item(EmptyItemCache(), "z", data)
+ assert item["x"] == "y"
+
+
+def test_setitem():
+ data = {}
+ item = Item(EmptyItemCache(), "z", data)
+ with pytest.raises(KeyError):
+ item["a"]
+ item["a"] = "b"
+ assert item["a"] == "b"
+
+
+def test_get():
+ data = {}
+ data["x"] = "y"
+ item = Item(EmptyItemCache(), "z", data)
+ assert item.get("x", "z") == "y"
+ assert item.get("z", "a") == "a"
+
+
+def test_children():
+ child = Item(EmptyItemCache(), "c", {})
+ parent = Item(EmptyItemCache(), "p", {})
+ parent.add_link_to_child(Link(child, {"a": "b", "role": "c"}))
+ children = [item for item in parent.children()]
+ assert len(children) == 1
+ assert children[0] == child
+ children = [item for item in parent.children("c")]
+ assert len(children) == 1
+ assert children[0] == child
+ children = [item for item in parent.children("d")]
+ assert len(children) == 0
+ links = [link for link in parent.links_to_children()]
+ assert len(links) == 1
+ assert links[0].item == child
+ assert links[0]["a"] == "b"
+ assert links[0].role == "c"
+
+
+def test_parents():
+ item_cache = EmptyItemCache()
+ child = Item(item_cache, "c",
+ {"links": [{
+ "a": "b",
+ "role": "c",
+ "uid": "p"
+ }]})
+ parent = Item(item_cache, "p", {"links": []})
+ item_cache._items["p"] = parent
+ child.init_parents(item_cache)
+ for link in child.links_to_parents():
+ link.item.add_link_to_child(Link.create(link, child))
+ parents = [item for item in child.parents()]
+ assert len(parents) == 1
+ assert parents[0] == parent
+ parents = [item for item in child.parents("c")]
+ assert len(parents) == 1
+ assert parents[0] == parent
+ parents = [item for item in child.parents("d")]
+ assert len(parents) == 0
+ links = [link for link in child.links_to_parents()]
+ assert len(links) == 1
+ assert links[0].item == parent
+ assert links[0]["a"] == "b"
+ assert links[0].role == "c"
+
+
+def _is_enabled(enabled, enabled_by):
+ item = Item(EmptyItemCache(), "i", {"enabled-by": enabled_by})
+ return item.is_enabled(enabled)
+
+
+def test_is_enabled():
+ assert _is_enabled([], True)
+ assert not _is_enabled([], False)
+ assert not _is_enabled([], [])
+ assert not _is_enabled([], ["A"])
+ assert _is_enabled(["A"], "A")
+ assert not _is_enabled(["B"], "A")
+ assert _is_enabled(["A"], ["A"])
+ assert not _is_enabled(["B"], ["A"])
+ assert _is_enabled(["A"], ["A", "B"])
+ assert _is_enabled(["B"], ["A", "B"])
+ assert not _is_enabled(["C"], ["A", "B"])
+ assert not _is_enabled(["A"], {"not": "A"})
+ assert _is_enabled(["B"], {"not": "A"})
+ assert not _is_enabled(["A"], {"and": ["A", "B"]})
+ assert _is_enabled(["A", "B"], {"and": ["A", "B"]})
+ assert _is_enabled(["A", "B", "C"], {"and": ["A", "B"]})
+ assert _is_enabled(["A", "B"], {"and": ["A", "B", {"not": "C"}]})
+ assert not _is_enabled(["A", "B", "C"], {"and": ["A", "B", {"not": "C"}]})
+ with pytest.raises(KeyError):
+ _is_enabled(["A"], {"x": "A"})
+ assert _is_enabled([], {"not": {"and": ["A", {"not": "A"}]}})
+
+
+def test_save_and_load(tmpdir):
+ item_file = os.path.join(tmpdir, "i.yml")
+ item = Item(EmptyItemCache(), "i", {"k": "v"})
+ item.file = item_file
+ assert item.file == item_file
+ item.save()
+ with open(item_file, "r") as src:
+ assert src.read() == "k: v\n"
+ assert item.file == item_file
+
+ item2 = Item(EmptyItemCache(), "i2", {})
+ item2.file = item_file
+ with pytest.raises(KeyError):
+ item2["k"]
+ item2.load()
+ assert item2["k"] == "v"
+ assert item.file == item_file
diff --git a/rtemsspec/tests/test_items_itemcache.py b/rtemsspec/tests/test_items_itemcache.py
new file mode 100644
index 00000000..678ae2b2
--- /dev/null
+++ b/rtemsspec/tests/test_items_itemcache.py
@@ -0,0 +1,128 @@
+# SPDX-License-Identifier: BSD-2-Clause
+""" Unit tests for the rtemsspec.items module. """
+
+# Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import os
+import pytest
+
+from rtemsspec.items import EmptyItem, ItemCache, ItemMapper, ItemTemplate
+from rtemsspec.tests.util import create_item_cache_config_and_copy_spec
+
+
+def test_config_error():
+ with pytest.raises(KeyError):
+ ItemCache({})
+
+
+def test_load(tmpdir):
+ config = create_item_cache_config_and_copy_spec(tmpdir, "spec-item-cache")
+ item_cache = ItemCache(config)
+ cache_dir = config["cache-directory"]
+ assert os.path.exists(os.path.join(cache_dir, "spec", "spec.pickle"))
+ assert os.path.exists(os.path.join(cache_dir, "spec", "d", "spec.pickle"))
+ assert item_cache["/d/c"]["v"] == "c"
+ assert item_cache["/p"]["v"] == "p"
+ t = item_cache.top_level
+ assert len(t) == 1
+ p = t["/p"]
+ assert p["v"] == "p"
+ assert p.map("/p") == p
+ assert p.map("p") == p
+ a = item_cache.all
+ assert len(a) == 2
+ assert a["/p"]["v"] == "p"
+ assert a["/d/c"]["v"] == "c"
+ item_cache_2 = ItemCache(config)
+ assert item_cache_2["/d/c"]["v"] == "c"
+ with open(os.path.join(tmpdir, "spec", "d", "c.yml"), "w+") as out:
+ out.write("links:\n- role: null\n uid: ../p\nv: x\n")
+ item_cache_3 = ItemCache(config)
+ assert item_cache_3["/d/c"]["v"] == "x"
+
+
+class Mapper(ItemMapper):
+ def __init__(self, item):
+ super().__init__(item)
+
+ def u(self, value):
+ return "u" + value
+
+ def v(self, value):
+ return "v" + value
+
+ def dup(self, value):
+ return value + value
+
+ def get_value(self, ctx):
+ if ctx.key == "x-to-b":
+ return ctx.value["b"]
+ raise KeyError
+
+
+def test_item_mapper(tmpdir):
+ config = create_item_cache_config_and_copy_spec(tmpdir, "spec-item-cache")
+ item_cache = ItemCache(config)
+ item = item_cache["/p"]
+ base_mapper = ItemMapper(item)
+ assert base_mapper["d/c:v"] == "c"
+ mapper = Mapper(item)
+ assert mapper.substitute(None) == ""
+ assert mapper.substitute_with_prefix(None, "v") == ""
+ with mapper.prefix("v"):
+ assert mapper[".:."] == "p"
+ assert mapper[".:../x/y"] == "z"
+ item_2, value_2 = mapper.map(".:.")
+ assert item == item_2
+ assert value_2 == "p"
+ assert mapper.substitute("$$${.:.}") == "$p"
+ assert mapper.substitute_with_prefix("$$${.:.}", "v") == "$p"
+ with mapper.prefix("x"):
+ with mapper.prefix("y"):
+ assert mapper[".:."] == "z"
+ assert mapper["."] == "/p"
+ assert mapper["d/c"] == "/d/c"
+ assert mapper["d/c:v"] == "c"
+ assert mapper["d/c:a/b"] == "e"
+ assert mapper["d/c:a/b|u"] == "ue"
+ assert mapper["d/c:a/x-to-b|u|v"] == "vue"
+ assert mapper["d/c:a/f[1]"] == 2
+ assert mapper["d/c:a/../a/f[3]/g[0]|dup"] == 8
+ item_3, value_3 = mapper.map("/p:/v")
+ assert item == item_3
+ assert value_3 == "p"
+ with pytest.raises(StopIteration):
+ for something in mapper:
+ pass
+ with pytest.raises(AttributeError):
+ len(mapper)
+
+
+def test_empty_item_mapper():
+ item = EmptyItem()
+ mapper = ItemMapper(item)
+ assert mapper.item == item
+ item_2 = EmptyItem()
+ mapper.item = item_2
+ assert mapper.item == item_2
diff --git a/rtemsspec/tests/test_specdoc.py b/rtemsspec/tests/test_specdoc.py
new file mode 100644
index 00000000..0ac432e9
--- /dev/null
+++ b/rtemsspec/tests/test_specdoc.py
@@ -0,0 +1,291 @@
+# SPDX-License-Identifier: BSD-2-Clause
+""" Unit tests for the rtemsspec.specdoc module. """
+
+# Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import os
+
+from rtemsspec.items import ItemCache
+from rtemsspec.specdoc import document
+from rtemsspec.tests.util import create_item_cache_config_and_copy_spec
+
+
+def test_document(tmpdir):
+ item_cache_config = create_item_cache_config_and_copy_spec(
+ tmpdir, "spec-doc")
+ item_cache_config["spec-type-root-uid"] = "/root"
+ item_cache = ItemCache(item_cache_config)
+ assert item_cache["/root"].type == "spec"
+ doc_target = os.path.join(tmpdir, "items.rst")
+ config = {
+ "root-type": "/root",
+ "doc-target": doc_target,
+ }
+ document(config, item_cache)
+ with open(doc_target, "r") as src:
+ content = """.. SPDX-License-Identifier: CC-BY-SA-4.0
+
+.. Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+
+.. _ReqEngSpecificationItems:
+
+Specification Items
+===================
+
+.. _ReqEngSpecificationItemHierarchy:
+
+Specification Item Hierarchy
+----------------------------
+
+The specification item types have the following hierarchy:
+
+* :ref:`SpecTypeRoot`
+
+ * :ref:`SpecTypeA`
+
+ * :ref:`SpecTypeB`
+
+.. _ReqEngSpecificationItemTypes:
+
+Specification Item Types
+------------------------
+
+.. _SpecTypeRoot:
+
+Root
+^^^^
+
+A value of this type shall be of one of the following variants:
+
+* The value may be a boolean. A reference to :ref:`SpecTypeRoot`. The value
+ shall be true.
+
+* The value may be a set of attributes. All explicit attributes shall be
+ specified. The explicit attributes for this type are:
+
+ type
+ The attribute value shall be a :ref:`SpecTypeName`.
+
+ In addition to the explicit attributes, generic attributes may be specified.
+ Each generic attribute key shall be a :ref:`SpecTypeName`. Each generic
+ attribute value shall be a :ref:`SpecTypeRoot`.
+
+* The value may be a floating-point number. The value shall be equal to 0.0.
+
+* The value may be an integer number. The value shall be equal to 0.
+
+* The value may be a list. Each list element shall be a :ref:`SpecTypeRoot`.
+
+* There may by be no value (null).
+
+* The value may be a string. The value
+
+ * shall meet,
+
+ * shall contain an element of
+
+ * "``a``",
+
+ * "``b``", and
+
+ * "``c``",
+
+ * and, shall be equal to "``d``",
+
+ * and, shall be greater than or equal to "``e``",
+
+ * and, shall be greater than "``f``",
+
+ * and, shall be an element of
+
+ * "``g``", and
+
+ * "``h``",
+
+ * and, shall be less than or equal to "``i``",
+
+ * and, shall be less than "``j``",
+
+ * and, shall be not equal to "``k``",
+
+ * and, shall match with the regular expression "``l"``,
+
+ * and, shall be true,
+
+ * and, shall be a valid item UID,
+
+ * or,
+
+ * shall be an element of,
+
+ * or, shall be an element of
+
+ * "``x``",
+
+ * or, shall not meet,
+
+ * shall not contain an element of
+
+ * "``a``",
+
+ * "``b``", and
+
+ * "``c``",
+
+ * or, shall be not equal to "``d``",
+
+ * or, shall be less than "``e``",
+
+ * or, shall be less than or equal to "``f``",
+
+ * or, shall not be an element of
+
+ * "``g``", and
+
+ * "``h``",
+
+ * or, shall be greater than "``i``",
+
+ * or, shall be greater than or equal to "``j``",
+
+ * or, shall be equal to "``k``",
+
+ * or, shall not match with the regular expression "``l"``,
+
+ * or, shall be false,
+
+ * or, shall be an invalid item UID.
+
+This type is refined by the following types:
+
+* :ref:`SpecTypeA`
+
+* :ref:`SpecTypeB`
+
+This type is used by the following types:
+
+* :ref:`SpecTypeRoot`
+
+.. _SpecTypeA:
+
+A
+^
+
+This type refines the :ref:`SpecTypeRoot` though the ``type`` attribute if the
+value is ``spec``.
+
+The explicit attributes for this type are:
+
+a
+ The attribute value shall be an :ref:`SpecTypeA`.
+
+This type is used by the following types:
+
+* :ref:`SpecTypeA`
+
+Please have a look at the following example:
+
+.. code-block:: yaml
+
+ a: null
+
+.. _SpecTypeB:
+
+B
+^
+
+This type refines the following types:
+
+* :ref:`SpecTypeD` though the ``d1`` attribute if the value is ``b``
+
+* :ref:`SpecTypeRoot` though the ``type`` attribute if the value is ``b``
+
+Generic attributes may be specified. Each generic attribute key shall be a
+:ref:`SpecTypeName`. Each generic attribute value shall be a list. Each list
+element shall be a string.
+
+.. _ReqEngSpecificationAttributeSetsAndValueTypes:
+
+Specification Attribute Sets and Value Types
+--------------------------------------------
+
+.. _SpecTypeC:
+
+C
+^
+
+Only the ``c`` attribute is mandatory. The explicit attributes for this type
+are:
+
+c
+ The attribute value shall be a floating-point number.
+
+.. _SpecTypeD:
+
+D
+^
+
+The following explicit attributes are mandatory:
+
+* ``d1``
+
+* ``d2``
+
+The explicit attributes for this type are:
+
+d1
+ The attribute value shall be a :ref:`SpecTypeName`.
+
+d2
+ The attribute shall have no value.
+
+This type is refined by the following types:
+
+* :ref:`SpecTypeB`
+
+.. _SpecTypeName:
+
+Name
+^^^^
+
+The value shall be a string. A string is a valid name if it matches with the
+``^([a-z][a-z0-9-]*|SPDX-License-Identifier)$`` regular expression.
+
+This type is used by the following types:
+
+* :ref:`SpecTypeB`
+
+* :ref:`SpecTypeD`
+
+* :ref:`SpecTypeRoot`
+
+.. _SpecTypeUID:
+
+UID
+^^^
+
+The value shall be a string. The string shall be a valid absolute or relative
+item UID.
+"""
+ assert content == src.read()
diff --git a/rtemsspec/tests/test_specverify.py b/rtemsspec/tests/test_specverify.py
new file mode 100644
index 00000000..076a6fab
--- /dev/null
+++ b/rtemsspec/tests/test_specverify.py
@@ -0,0 +1,1909 @@
+# SPDX-License-Identifier: BSD-2-Clause
+""" Unit tests for the rtemsspec.specverify module. """
+
+# Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import logging
+
+from rtemsspec.items import ItemCache
+from rtemsspec.specverify import verify
+from rtemsspec.tests.util import create_item_cache_config_and_copy_spec
+
+
+def test_no_root_type(caplog, tmpdir):
+ item_cache_config = create_item_cache_config_and_copy_spec(
+ tmpdir, "spec-verify")
+ item_cache = ItemCache(item_cache_config)
+ config = {}
+ caplog.set_level(logging.INFO)
+ verify(config, item_cache)
+ log = "\n".join(
+ [f"{rec.levelname} {rec.message}" for rec in caplog.records])
+ assert log == """ERROR configuration has no root type"""
+
+
+def test_no_root_item(caplog, tmpdir):
+ item_cache_config = create_item_cache_config_and_copy_spec(
+ tmpdir, "spec-verify")
+ item_cache = ItemCache(item_cache_config)
+ config = {"root-type": "/nix"}
+ caplog.set_level(logging.INFO)
+ verify(config, item_cache)
+ log = "\n".join(
+ [f"{rec.levelname} {rec.message}" for rec in caplog.records])
+ assert log == """ERROR root type item does not exist in item cache"""
+
+
+def test_verify(caplog, tmpdir):
+ item_cache_config = create_item_cache_config_and_copy_spec(
+ tmpdir, "spec-verify")
+ item_cache = ItemCache(item_cache_config)
+ config = {"root-type": "/spec/root"}
+ caplog.set_level(logging.INFO)
+ verify(config, item_cache)
+ log = "\n".join(
+ [f"{rec.levelname} {rec.message}" for rec in caplog.records])
+ assert log == """INFO type: any-dict
+INFO type: bool
+INFO type: c
+INFO type: copyright
+INFO type: copyrights
+INFO type: d
+INFO add subtype 'd-a' to 'd'
+INFO type: d-a
+INFO type: enabled-by
+INFO type: enabled-by-list
+INFO type: float
+INFO type: int
+INFO type: keys-at-least-one
+INFO type: keys-at-most-one
+INFO type: keys-exactly-one
+INFO type: link
+INFO add subtype 'spec-member' to 'link'
+INFO add subtype 'spec-refinement' to 'link'
+INFO type: links
+INFO type: list-str
+INFO type: must-be-true
+INFO type: name
+INFO type: none
+INFO type: optional-str
+INFO type: other-int
+INFO type: root
+INFO add subtype 'spec' to 'root'
+INFO add subtype 'c' to 'root'
+INFO add subtype 'd' to 'root'
+INFO add subtype 'spec-invalid' to 'root'
+INFO add subtype 'keys-at-least-one' to 'root'
+INFO add subtype 'keys-at-most-one' to 'root'
+INFO add subtype 'keys-exactly-one' to 'root'
+INFO type: some-bool
+INFO type: some-dict
+INFO type: some-float
+INFO type: some-int
+INFO type: some-list
+INFO type: some-str
+INFO type: spdx-license-identifier
+INFO type: spec
+INFO type: spec-assert-float
+INFO type: spec-assert-float-list
+INFO type: spec-assert-int
+INFO type: spec-assert-int-list
+INFO type: spec-assert-str
+INFO type: spec-assert-str-list
+INFO type: spec-attribute-value
+INFO type: spec-attributes
+INFO type: spec-bool
+INFO type: spec-dict
+INFO type: spec-float
+INFO type: spec-generic-attributes
+INFO type: spec-info
+INFO type: spec-int
+INFO type: spec-invalid
+INFO type: spec-list
+INFO type: spec-mandatory-attributes
+INFO type: spec-member
+INFO type: spec-refinement
+INFO type: spec-str
+INFO type: str
+INFO type: str-contains
+INFO type: uid
+INFO type: x
+INFO start specification item verification
+INFO /c1: verify using type 'root'
+INFO /c1:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /c1:/copyrights: verify using type 'copyrights'
+INFO /c1:/copyrights[0]: verify using type 'copyright'
+INFO /c1:/enabled-by: verify using type 'enabled-by'
+INFO /c1:/links: verify using type 'links'
+INFO /c1:/links[0]: verify using type 'link'
+INFO /c1:/links[0]/role: verify using type 'name'
+INFO /c1:/links[0]/uid: verify using type 'uid'
+ERROR /c1:/links[0]: unknown subtype for key 'role' for type 'link': x
+INFO /c1:/type: verify using type 'name'
+INFO /c1: verify using type 'c'
+ERROR /c1: missing mandatory keys for type 'c': ['any-dict', 'bool', 'float', 'int', 'must-be-true', 'other-int', 'str', 'str-contains']
+INFO /c1:/dict: verify using type 'some-dict'
+INFO /c1:/dict/a: verify using type 'none'
+INFO /c1:/list: verify using type 'some-list'
+INFO /c1:/uid: verify using type 'uid'
+ERROR /c1:/uid: expected type 'str', actual type 'int'
+INFO /c2: verify using type 'root'
+INFO /c2:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /c2:/copyrights: verify using type 'copyrights'
+INFO /c2:/copyrights[0]: verify using type 'copyright'
+INFO /c2:/enabled-by: verify using type 'enabled-by'
+INFO /c2:/links: verify using type 'links'
+INFO /c2:/type: verify using type 'name'
+INFO /c2: verify using type 'c'
+ERROR /c2: missing mandatory keys for type 'c': ['any-dict', 'bool', 'float', 'int', 'must-be-true', 'other-int', 'str', 'uid']
+INFO /c2:/dict: verify using type 'some-dict'
+ERROR /c2:/dict: has unverfied keys for type 'some-dict' and its subtypes: ['b']
+INFO /c2:/list: verify using type 'some-list'
+INFO /c2:/str-contains: verify using type 'str-contains'
+ERROR /c2:/str-contains: invalid value: uvw
+INFO /c3: verify using type 'root'
+INFO /c3:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /c3:/copyrights: verify using type 'copyrights'
+INFO /c3:/copyrights[0]: verify using type 'copyright'
+INFO /c3:/enabled-by: verify using type 'enabled-by'
+INFO /c3:/links: verify using type 'links'
+INFO /c3:/type: verify using type 'name'
+INFO /c3: verify using type 'c'
+ERROR /c3: missing mandatory keys for type 'c': ['any-dict', 'bool', 'float', 'int', 'other-int']
+INFO /c3:/dict: verify using type 'some-dict'
+ERROR /c3:/dict: expected value of types ['dict', 'none'] for type 'some-dict', actual type 'list'
+INFO /c3:/list: verify using type 'some-list'
+ERROR /c3:/list: expected value of types ['list'] for type 'some-list', actual type 'dict'
+INFO /c3:/must-be-true: verify using type 'must-be-true'
+ERROR /c3:/must-be-true: expected True, actual False
+INFO /c3:/str: verify using type 'some-str'
+INFO /c3:/str-contains: verify using type 'str-contains'
+ERROR /c3:/str-contains: invalid value: abc ghi
+INFO /c3:/uid: verify using type 'uid'
+ERROR /c3:/uid: cannot resolve UID: nix
+INFO /c4: verify using type 'root'
+INFO /c4:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /c4:/copyrights: verify using type 'copyrights'
+INFO /c4:/copyrights[0]: verify using type 'copyright'
+INFO /c4:/enabled-by: verify using type 'enabled-by'
+INFO /c4:/links: verify using type 'links'
+INFO /c4:/type: verify using type 'name'
+INFO /c4: verify using type 'c'
+INFO /c4:/any-dict: verify using type 'any-dict'
+INFO /c4:/any-dict/a: verify using type 'name'
+INFO /c4:/any-dict/a: verify using type 'str'
+INFO /c4:/bool: verify using type 'some-bool'
+INFO /c4:/dict: verify using type 'some-dict'
+INFO /c4:/float: verify using type 'some-float'
+ERROR /c4:/float: invalid value: 123.567
+INFO /c4:/int: verify using type 'some-int'
+INFO /c4:/list: verify using type 'some-list'
+INFO /c4:/must-be-true: verify using type 'must-be-true'
+INFO /c4:/other-int: verify using type 'other-int'
+INFO /c4:/str: verify using type 'some-str'
+WARNING /c4:/str: cannot resolve UID: abc
+INFO /c4:/str-contains: verify using type 'str-contains'
+INFO /c4:/uid: verify using type 'uid'
+INFO /d: verify using type 'root'
+INFO /d:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /d:/copyrights: verify using type 'copyrights'
+INFO /d:/copyrights[0]: verify using type 'copyright'
+INFO /d:/enabled-by: verify using type 'enabled-by'
+INFO /d:/links: verify using type 'links'
+INFO /d:/type: verify using type 'name'
+INFO /d: verify using type 'd'
+INFO /d:/d-type: verify using type 'name'
+INFO /d: verify using type 'd-a'
+INFO /d:/d-type: verify using type 'x'
+ERROR /d:/d-type: invalid value: blub
+INFO /d2: verify using type 'root'
+INFO /d2:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /d2:/copyrights: verify using type 'copyrights'
+INFO /d2:/copyrights[0]: verify using type 'copyright'
+INFO /d2:/enabled-by: verify using type 'enabled-by'
+INFO /d2:/links: verify using type 'links'
+INFO /d2:/type: verify using type 'name'
+INFO /d2: verify using type 'd'
+INFO /d2:/d-type: verify using type 'name'
+ERROR /d2: unknown subtype for key 'd-type' for type 'd': bla
+INFO /d3: verify using type 'root'
+INFO /d3:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /d3:/copyrights: verify using type 'copyrights'
+INFO /d3:/copyrights[0]: verify using type 'copyright'
+INFO /d3:/enabled-by: verify using type 'enabled-by'
+INFO /d3:/links: verify using type 'links'
+INFO /d3:/type: verify using type 'name'
+INFO /d3: verify using type 'd'
+ERROR /d3: missing mandatory keys for type 'd': ['d-type']
+ERROR /d3: subtype key 'd-type' not present for type 'd'
+INFO /e: verify using type 'root'
+INFO /e:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /e:/copyrights: verify using type 'copyrights'
+INFO /e:/copyrights[0]: verify using type 'copyright'
+INFO /e:/enabled-by: verify using type 'enabled-by'
+INFO /e:/links: verify using type 'links'
+INFO /e:/type: verify using type 'name'
+ERROR /e: unknown subtype for key 'type' for type 'root': e
+INFO /invalid: verify using type 'root'
+INFO /invalid:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /invalid:/copyrights: verify using type 'copyrights'
+INFO /invalid:/copyrights[0]: verify using type 'copyright'
+INFO /invalid:/enabled-by: verify using type 'enabled-by'
+INFO /invalid:/links: verify using type 'links'
+INFO /invalid:/type: verify using type 'name'
+INFO /invalid: verify using type 'spec-invalid'
+ERROR /invalid: unknown specification type: invalid
+INFO /keys-at-least-one-0: verify using type 'root'
+INFO /keys-at-least-one-0:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /keys-at-least-one-0:/copyrights: verify using type 'copyrights'
+INFO /keys-at-least-one-0:/copyrights[0]: verify using type 'copyright'
+INFO /keys-at-least-one-0:/enabled-by: verify using type 'enabled-by'
+INFO /keys-at-least-one-0:/links: verify using type 'links'
+INFO /keys-at-least-one-0:/type: verify using type 'name'
+INFO /keys-at-least-one-0: verify using type 'keys-at-least-one'
+ERROR /keys-at-least-one-0: not at least one key out of ['key-a', 'key-b'] is present for type 'keys-at-least-one'
+INFO /keys-at-least-one-1: verify using type 'root'
+INFO /keys-at-least-one-1:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /keys-at-least-one-1:/copyrights: verify using type 'copyrights'
+INFO /keys-at-least-one-1:/copyrights[0]: verify using type 'copyright'
+INFO /keys-at-least-one-1:/enabled-by: verify using type 'enabled-by'
+INFO /keys-at-least-one-1:/links: verify using type 'links'
+INFO /keys-at-least-one-1:/type: verify using type 'name'
+INFO /keys-at-least-one-1: verify using type 'keys-at-least-one'
+INFO /keys-at-least-one-1:/key-a: verify using type 'none'
+INFO /keys-at-least-one-2: verify using type 'root'
+INFO /keys-at-least-one-2:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /keys-at-least-one-2:/copyrights: verify using type 'copyrights'
+INFO /keys-at-least-one-2:/copyrights[0]: verify using type 'copyright'
+INFO /keys-at-least-one-2:/enabled-by: verify using type 'enabled-by'
+INFO /keys-at-least-one-2:/links: verify using type 'links'
+INFO /keys-at-least-one-2:/type: verify using type 'name'
+INFO /keys-at-least-one-2: verify using type 'keys-at-least-one'
+INFO /keys-at-least-one-2:/key-a: verify using type 'none'
+INFO /keys-at-least-one-2:/key-b: verify using type 'none'
+INFO /keys-at-most-one-0: verify using type 'root'
+INFO /keys-at-most-one-0:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /keys-at-most-one-0:/copyrights: verify using type 'copyrights'
+INFO /keys-at-most-one-0:/copyrights[0]: verify using type 'copyright'
+INFO /keys-at-most-one-0:/enabled-by: verify using type 'enabled-by'
+INFO /keys-at-most-one-0:/links: verify using type 'links'
+INFO /keys-at-most-one-0:/type: verify using type 'name'
+INFO /keys-at-most-one-0: verify using type 'keys-at-most-one'
+INFO /keys-at-most-one-1: verify using type 'root'
+INFO /keys-at-most-one-1:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /keys-at-most-one-1:/copyrights: verify using type 'copyrights'
+INFO /keys-at-most-one-1:/copyrights[0]: verify using type 'copyright'
+INFO /keys-at-most-one-1:/enabled-by: verify using type 'enabled-by'
+INFO /keys-at-most-one-1:/links: verify using type 'links'
+INFO /keys-at-most-one-1:/type: verify using type 'name'
+INFO /keys-at-most-one-1: verify using type 'keys-at-most-one'
+INFO /keys-at-most-one-1:/key-a: verify using type 'none'
+INFO /keys-at-most-one-2: verify using type 'root'
+INFO /keys-at-most-one-2:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /keys-at-most-one-2:/copyrights: verify using type 'copyrights'
+INFO /keys-at-most-one-2:/copyrights[0]: verify using type 'copyright'
+INFO /keys-at-most-one-2:/enabled-by: verify using type 'enabled-by'
+INFO /keys-at-most-one-2:/links: verify using type 'links'
+INFO /keys-at-most-one-2:/type: verify using type 'name'
+INFO /keys-at-most-one-2: verify using type 'keys-at-most-one'
+ERROR /keys-at-most-one-2: not at most one key out of ['key-a', 'key-b'] is present for type 'keys-at-most-one': ['key-a', 'key-b']
+INFO /keys-at-most-one-2:/key-a: verify using type 'none'
+INFO /keys-at-most-one-2:/key-b: verify using type 'none'
+INFO /keys-exactly-one-0: verify using type 'root'
+INFO /keys-exactly-one-0:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /keys-exactly-one-0:/copyrights: verify using type 'copyrights'
+INFO /keys-exactly-one-0:/copyrights[0]: verify using type 'copyright'
+INFO /keys-exactly-one-0:/enabled-by: verify using type 'enabled-by'
+INFO /keys-exactly-one-0:/links: verify using type 'links'
+INFO /keys-exactly-one-0:/type: verify using type 'name'
+INFO /keys-exactly-one-0: verify using type 'keys-exactly-one'
+ERROR /keys-exactly-one-0: not exactly one key out of ['key-a', 'key-b'] is present for type 'keys-exactly-one': []
+INFO /keys-exactly-one-1: verify using type 'root'
+INFO /keys-exactly-one-1:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /keys-exactly-one-1:/copyrights: verify using type 'copyrights'
+INFO /keys-exactly-one-1:/copyrights[0]: verify using type 'copyright'
+INFO /keys-exactly-one-1:/enabled-by: verify using type 'enabled-by'
+INFO /keys-exactly-one-1:/links: verify using type 'links'
+INFO /keys-exactly-one-1:/type: verify using type 'name'
+INFO /keys-exactly-one-1: verify using type 'keys-exactly-one'
+INFO /keys-exactly-one-1:/key-a: verify using type 'none'
+INFO /keys-exactly-one-2: verify using type 'root'
+INFO /keys-exactly-one-2:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /keys-exactly-one-2:/copyrights: verify using type 'copyrights'
+INFO /keys-exactly-one-2:/copyrights[0]: verify using type 'copyright'
+INFO /keys-exactly-one-2:/enabled-by: verify using type 'enabled-by'
+INFO /keys-exactly-one-2:/links: verify using type 'links'
+INFO /keys-exactly-one-2:/type: verify using type 'name'
+INFO /keys-exactly-one-2: verify using type 'keys-exactly-one'
+ERROR /keys-exactly-one-2: not exactly one key out of ['key-a', 'key-b'] is present for type 'keys-exactly-one': ['key-a', 'key-b']
+INFO /keys-exactly-one-2:/key-a: verify using type 'none'
+INFO /keys-exactly-one-2:/key-b: verify using type 'none'
+INFO /notype: verify using type 'root'
+ERROR /notype: missing mandatory keys for type 'root': ['type']
+INFO /notype:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /notype:/copyrights: verify using type 'copyrights'
+INFO /notype:/copyrights[0]: verify using type 'copyright'
+INFO /notype:/enabled-by: verify using type 'enabled-by'
+INFO /notype:/links: verify using type 'links'
+ERROR /notype: subtype key 'type' not present for type 'root'
+INFO /spec/copyright: verify using type 'root'
+INFO /spec/copyright:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec/copyright:/copyrights: verify using type 'copyrights'
+INFO /spec/copyright:/copyrights[0]: verify using type 'copyright'
+INFO /spec/copyright:/enabled-by: verify using type 'enabled-by'
+INFO /spec/copyright:/links: verify using type 'links'
+INFO /spec/copyright:/links[0]: verify using type 'link'
+INFO /spec/copyright:/links[0]/role: verify using type 'name'
+INFO /spec/copyright:/links[0]/uid: verify using type 'uid'
+INFO /spec/copyright:/links[0]: verify using type 'spec-member'
+INFO /spec/copyright:/type: verify using type 'name'
+INFO /spec/copyright: verify using type 'spec'
+INFO /spec/copyright:/spec-description: verify using type 'optional-str'
+INFO /spec/copyright:/spec-example: verify using type 'optional-str'
+INFO /spec/copyright:/spec-info: verify using type 'spec-info'
+INFO /spec/copyright:/spec-info/str: verify using type 'spec-str'
+INFO /spec/copyright:/spec-info/str/assert: verify using type 'spec-assert-str'
+INFO /spec/copyright:/spec-info/str/assert[0]: verify using type 'spec-assert-str'
+INFO /spec/copyright:/spec-info/str/assert[0]/re: verify using type 'str'
+INFO /spec/copyright:/spec-info/str/assert[1]: verify using type 'spec-assert-str'
+INFO /spec/copyright:/spec-info/str/assert[1]/re: verify using type 'str'
+INFO /spec/copyright:/spec-info/str/assert[2]: verify using type 'spec-assert-str'
+INFO /spec/copyright:/spec-info/str/assert[2]/re: verify using type 'str'
+INFO /spec/copyright:/spec-info/str/description: verify using type 'optional-str'
+INFO /spec/copyright:/spec-name: verify using type 'optional-str'
+INFO /spec/copyright:/spec-type: verify using type 'name'
+INFO /spec/copyrights: verify using type 'root'
+INFO /spec/copyrights:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec/copyrights:/copyrights: verify using type 'copyrights'
+INFO /spec/copyrights:/copyrights[0]: verify using type 'copyright'
+INFO /spec/copyrights:/enabled-by: verify using type 'enabled-by'
+INFO /spec/copyrights:/links: verify using type 'links'
+INFO /spec/copyrights:/links[0]: verify using type 'link'
+INFO /spec/copyrights:/links[0]/role: verify using type 'name'
+INFO /spec/copyrights:/links[0]/uid: verify using type 'uid'
+INFO /spec/copyrights:/links[0]: verify using type 'spec-member'
+INFO /spec/copyrights:/type: verify using type 'name'
+INFO /spec/copyrights: verify using type 'spec'
+INFO /spec/copyrights:/spec-description: verify using type 'optional-str'
+INFO /spec/copyrights:/spec-example: verify using type 'optional-str'
+INFO /spec/copyrights:/spec-info: verify using type 'spec-info'
+INFO /spec/copyrights:/spec-info/list: verify using type 'spec-list'
+INFO /spec/copyrights:/spec-info/list/description: verify using type 'optional-str'
+INFO /spec/copyrights:/spec-info/list/spec-type: verify using type 'name'
+INFO /spec/copyrights:/spec-name: verify using type 'optional-str'
+INFO /spec/copyrights:/spec-type: verify using type 'name'
+INFO /spec/enabled-by: verify using type 'root'
+INFO /spec/enabled-by:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec/enabled-by:/copyrights: verify using type 'copyrights'
+INFO /spec/enabled-by:/copyrights[0]: verify using type 'copyright'
+INFO /spec/enabled-by:/enabled-by: verify using type 'enabled-by'
+INFO /spec/enabled-by:/links: verify using type 'links'
+INFO /spec/enabled-by:/links[0]: verify using type 'link'
+INFO /spec/enabled-by:/links[0]/role: verify using type 'name'
+INFO /spec/enabled-by:/links[0]/uid: verify using type 'uid'
+INFO /spec/enabled-by:/links[0]: verify using type 'spec-member'
+INFO /spec/enabled-by:/type: verify using type 'name'
+INFO /spec/enabled-by: verify using type 'spec'
+INFO /spec/enabled-by:/spec-description: verify using type 'optional-str'
+INFO /spec/enabled-by:/spec-example: verify using type 'optional-str'
+INFO /spec/enabled-by:/spec-info: verify using type 'spec-info'
+INFO /spec/enabled-by:/spec-info/bool: verify using type 'spec-bool'
+INFO /spec/enabled-by:/spec-info/bool/description: verify using type 'optional-str'
+INFO /spec/enabled-by:/spec-info/dict: verify using type 'spec-dict'
+INFO /spec/enabled-by:/spec-info/dict/attributes: verify using type 'spec-attributes'
+INFO /spec/enabled-by:/spec-info/dict/attributes/and: verify using type 'name'
+INFO /spec/enabled-by:/spec-info/dict/attributes/and: verify using type 'spec-attribute-value'
+INFO /spec/enabled-by:/spec-info/dict/attributes/and/description: verify using type 'optional-str'
+INFO /spec/enabled-by:/spec-info/dict/attributes/and/spec-type: verify using type 'name'
+INFO /spec/enabled-by:/spec-info/dict/attributes/not: verify using type 'name'
+INFO /spec/enabled-by:/spec-info/dict/attributes/not: verify using type 'spec-attribute-value'
+INFO /spec/enabled-by:/spec-info/dict/attributes/not/description: verify using type 'optional-str'
+INFO /spec/enabled-by:/spec-info/dict/attributes/not/spec-type: verify using type 'name'
+INFO /spec/enabled-by:/spec-info/dict/attributes/or: verify using type 'name'
+INFO /spec/enabled-by:/spec-info/dict/attributes/or: verify using type 'spec-attribute-value'
+INFO /spec/enabled-by:/spec-info/dict/attributes/or/description: verify using type 'optional-str'
+INFO /spec/enabled-by:/spec-info/dict/attributes/or/spec-type: verify using type 'name'
+INFO /spec/enabled-by:/spec-info/dict/description: verify using type 'optional-str'
+INFO /spec/enabled-by:/spec-info/dict/mandatory-attributes: verify using type 'spec-mandatory-attributes'
+INFO /spec/enabled-by:/spec-info/list: verify using type 'spec-list'
+INFO /spec/enabled-by:/spec-info/list/description: verify using type 'optional-str'
+INFO /spec/enabled-by:/spec-info/list/spec-type: verify using type 'name'
+INFO /spec/enabled-by:/spec-info/str: verify using type 'spec-str'
+INFO /spec/enabled-by:/spec-info/str/description: verify using type 'optional-str'
+INFO /spec/enabled-by:/spec-name: verify using type 'optional-str'
+INFO /spec/enabled-by:/spec-type: verify using type 'name'
+INFO /spec/enabled-by-list: verify using type 'root'
+INFO /spec/enabled-by-list:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec/enabled-by-list:/copyrights: verify using type 'copyrights'
+INFO /spec/enabled-by-list:/copyrights[0]: verify using type 'copyright'
+INFO /spec/enabled-by-list:/enabled-by: verify using type 'enabled-by'
+INFO /spec/enabled-by-list:/links: verify using type 'links'
+INFO /spec/enabled-by-list:/links[0]: verify using type 'link'
+INFO /spec/enabled-by-list:/links[0]/role: verify using type 'name'
+INFO /spec/enabled-by-list:/links[0]/uid: verify using type 'uid'
+INFO /spec/enabled-by-list:/links[0]: verify using type 'spec-member'
+INFO /spec/enabled-by-list:/type: verify using type 'name'
+INFO /spec/enabled-by-list: verify using type 'spec'
+INFO /spec/enabled-by-list:/spec-description: verify using type 'optional-str'
+INFO /spec/enabled-by-list:/spec-example: verify using type 'optional-str'
+INFO /spec/enabled-by-list:/spec-info: verify using type 'spec-info'
+INFO /spec/enabled-by-list:/spec-info/list: verify using type 'spec-list'
+INFO /spec/enabled-by-list:/spec-info/list/description: verify using type 'optional-str'
+INFO /spec/enabled-by-list:/spec-info/list/spec-type: verify using type 'name'
+INFO /spec/enabled-by-list:/spec-name: verify using type 'optional-str'
+INFO /spec/enabled-by-list:/spec-type: verify using type 'name'
+INFO /spec/link: verify using type 'root'
+INFO /spec/link:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec/link:/copyrights: verify using type 'copyrights'
+INFO /spec/link:/copyrights[0]: verify using type 'copyright'
+INFO /spec/link:/enabled-by: verify using type 'enabled-by'
+INFO /spec/link:/links: verify using type 'links'
+INFO /spec/link:/links[0]: verify using type 'link'
+INFO /spec/link:/links[0]/role: verify using type 'name'
+INFO /spec/link:/links[0]/uid: verify using type 'uid'
+INFO /spec/link:/links[0]: verify using type 'spec-member'
+INFO /spec/link:/type: verify using type 'name'
+INFO /spec/link: verify using type 'spec'
+INFO /spec/link:/spec-description: verify using type 'optional-str'
+INFO /spec/link:/spec-example: verify using type 'optional-str'
+INFO /spec/link:/spec-info: verify using type 'spec-info'
+INFO /spec/link:/spec-info/dict: verify using type 'spec-dict'
+INFO /spec/link:/spec-info/dict/attributes: verify using type 'spec-attributes'
+INFO /spec/link:/spec-info/dict/attributes/role: verify using type 'name'
+INFO /spec/link:/spec-info/dict/attributes/role: verify using type 'spec-attribute-value'
+INFO /spec/link:/spec-info/dict/attributes/role/description: verify using type 'optional-str'
+INFO /spec/link:/spec-info/dict/attributes/role/spec-type: verify using type 'name'
+INFO /spec/link:/spec-info/dict/attributes/uid: verify using type 'name'
+INFO /spec/link:/spec-info/dict/attributes/uid: verify using type 'spec-attribute-value'
+INFO /spec/link:/spec-info/dict/attributes/uid/description: verify using type 'optional-str'
+INFO /spec/link:/spec-info/dict/attributes/uid/spec-type: verify using type 'name'
+INFO /spec/link:/spec-info/dict/description: verify using type 'optional-str'
+INFO /spec/link:/spec-info/dict/mandatory-attributes: verify using type 'spec-mandatory-attributes'
+INFO /spec/link:/spec-name: verify using type 'optional-str'
+INFO /spec/link:/spec-type: verify using type 'name'
+INFO /spec/links: verify using type 'root'
+INFO /spec/links:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec/links:/copyrights: verify using type 'copyrights'
+INFO /spec/links:/copyrights[0]: verify using type 'copyright'
+INFO /spec/links:/enabled-by: verify using type 'enabled-by'
+INFO /spec/links:/links: verify using type 'links'
+INFO /spec/links:/links[0]: verify using type 'link'
+INFO /spec/links:/links[0]/role: verify using type 'name'
+INFO /spec/links:/links[0]/uid: verify using type 'uid'
+INFO /spec/links:/links[0]: verify using type 'spec-member'
+INFO /spec/links:/type: verify using type 'name'
+INFO /spec/links: verify using type 'spec'
+INFO /spec/links:/spec-description: verify using type 'optional-str'
+INFO /spec/links:/spec-example: verify using type 'optional-str'
+INFO /spec/links:/spec-info: verify using type 'spec-info'
+INFO /spec/links:/spec-info/list: verify using type 'spec-list'
+INFO /spec/links:/spec-info/list/description: verify using type 'optional-str'
+INFO /spec/links:/spec-info/list/spec-type: verify using type 'name'
+INFO /spec/links:/spec-name: verify using type 'optional-str'
+INFO /spec/links:/spec-type: verify using type 'name'
+INFO /spec/list-str: verify using type 'root'
+INFO /spec/list-str:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec/list-str:/copyrights: verify using type 'copyrights'
+INFO /spec/list-str:/copyrights[0]: verify using type 'copyright'
+INFO /spec/list-str:/enabled-by: verify using type 'enabled-by'
+INFO /spec/list-str:/links: verify using type 'links'
+INFO /spec/list-str:/links[0]: verify using type 'link'
+INFO /spec/list-str:/links[0]/role: verify using type 'name'
+INFO /spec/list-str:/links[0]/uid: verify using type 'uid'
+INFO /spec/list-str:/links[0]: verify using type 'spec-member'
+INFO /spec/list-str:/type: verify using type 'name'
+INFO /spec/list-str: verify using type 'spec'
+INFO /spec/list-str:/spec-description: verify using type 'optional-str'
+INFO /spec/list-str:/spec-example: verify using type 'optional-str'
+INFO /spec/list-str:/spec-info: verify using type 'spec-info'
+INFO /spec/list-str:/spec-info/list: verify using type 'spec-list'
+INFO /spec/list-str:/spec-info/list/description: verify using type 'optional-str'
+INFO /spec/list-str:/spec-info/list/spec-type: verify using type 'name'
+INFO /spec/list-str:/spec-name: verify using type 'optional-str'
+INFO /spec/list-str:/spec-type: verify using type 'name'
+INFO /spec/optional-str: verify using type 'root'
+INFO /spec/optional-str:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec/optional-str:/copyrights: verify using type 'copyrights'
+INFO /spec/optional-str:/copyrights[0]: verify using type 'copyright'
+INFO /spec/optional-str:/enabled-by: verify using type 'enabled-by'
+INFO /spec/optional-str:/links: verify using type 'links'
+INFO /spec/optional-str:/links[0]: verify using type 'link'
+INFO /spec/optional-str:/links[0]/role: verify using type 'name'
+INFO /spec/optional-str:/links[0]/uid: verify using type 'uid'
+INFO /spec/optional-str:/links[0]: verify using type 'spec-member'
+INFO /spec/optional-str:/type: verify using type 'name'
+INFO /spec/optional-str: verify using type 'spec'
+INFO /spec/optional-str:/spec-description: verify using type 'optional-str'
+INFO /spec/optional-str:/spec-example: verify using type 'optional-str'
+INFO /spec/optional-str:/spec-info: verify using type 'spec-info'
+INFO /spec/optional-str:/spec-info/none: verify using type 'none'
+INFO /spec/optional-str:/spec-info/str: verify using type 'spec-str'
+INFO /spec/optional-str:/spec-info/str/description: verify using type 'optional-str'
+INFO /spec/optional-str:/spec-name: verify using type 'optional-str'
+INFO /spec/optional-str:/spec-type: verify using type 'name'
+INFO /spec/root: verify using type 'root'
+INFO /spec/root:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec/root:/copyrights: verify using type 'copyrights'
+INFO /spec/root:/copyrights[0]: verify using type 'copyright'
+INFO /spec/root:/enabled-by: verify using type 'enabled-by'
+INFO /spec/root:/links: verify using type 'links'
+INFO /spec/root:/type: verify using type 'name'
+INFO /spec/root: verify using type 'spec'
+INFO /spec/root:/spec-description: verify using type 'optional-str'
+INFO /spec/root:/spec-example: verify using type 'optional-str'
+INFO /spec/root:/spec-info: verify using type 'spec-info'
+INFO /spec/root:/spec-info/dict: verify using type 'spec-dict'
+INFO /spec/root:/spec-info/dict/attributes: verify using type 'spec-attributes'
+INFO /spec/root:/spec-info/dict/attributes/SPDX-License-Identifier: verify using type 'name'
+INFO /spec/root:/spec-info/dict/attributes/SPDX-License-Identifier: verify using type 'spec-attribute-value'
+INFO /spec/root:/spec-info/dict/attributes/SPDX-License-Identifier/description: verify using type 'optional-str'
+INFO /spec/root:/spec-info/dict/attributes/SPDX-License-Identifier/spec-type: verify using type 'name'
+INFO /spec/root:/spec-info/dict/attributes/copyrights: verify using type 'name'
+INFO /spec/root:/spec-info/dict/attributes/copyrights: verify using type 'spec-attribute-value'
+INFO /spec/root:/spec-info/dict/attributes/copyrights/description: verify using type 'optional-str'
+INFO /spec/root:/spec-info/dict/attributes/copyrights/spec-type: verify using type 'name'
+INFO /spec/root:/spec-info/dict/attributes/enabled-by: verify using type 'name'
+INFO /spec/root:/spec-info/dict/attributes/enabled-by: verify using type 'spec-attribute-value'
+INFO /spec/root:/spec-info/dict/attributes/enabled-by/description: verify using type 'optional-str'
+INFO /spec/root:/spec-info/dict/attributes/enabled-by/spec-type: verify using type 'name'
+INFO /spec/root:/spec-info/dict/attributes/links: verify using type 'name'
+INFO /spec/root:/spec-info/dict/attributes/links: verify using type 'spec-attribute-value'
+INFO /spec/root:/spec-info/dict/attributes/links/description: verify using type 'optional-str'
+INFO /spec/root:/spec-info/dict/attributes/links/spec-type: verify using type 'name'
+INFO /spec/root:/spec-info/dict/attributes/type: verify using type 'name'
+INFO /spec/root:/spec-info/dict/attributes/type: verify using type 'spec-attribute-value'
+INFO /spec/root:/spec-info/dict/attributes/type/description: verify using type 'optional-str'
+INFO /spec/root:/spec-info/dict/attributes/type/spec-type: verify using type 'name'
+INFO /spec/root:/spec-info/dict/description: verify using type 'optional-str'
+INFO /spec/root:/spec-info/dict/mandatory-attributes: verify using type 'spec-mandatory-attributes'
+INFO /spec/root:/spec-name: verify using type 'optional-str'
+INFO /spec/root:/spec-type: verify using type 'name'
+INFO /spec/spdx-license-identifier: verify using type 'root'
+INFO /spec/spdx-license-identifier:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec/spdx-license-identifier:/copyrights: verify using type 'copyrights'
+INFO /spec/spdx-license-identifier:/copyrights[0]: verify using type 'copyright'
+INFO /spec/spdx-license-identifier:/enabled-by: verify using type 'enabled-by'
+INFO /spec/spdx-license-identifier:/links: verify using type 'links'
+INFO /spec/spdx-license-identifier:/links[0]: verify using type 'link'
+INFO /spec/spdx-license-identifier:/links[0]/role: verify using type 'name'
+INFO /spec/spdx-license-identifier:/links[0]/uid: verify using type 'uid'
+INFO /spec/spdx-license-identifier:/links[0]: verify using type 'spec-member'
+INFO /spec/spdx-license-identifier:/type: verify using type 'name'
+INFO /spec/spdx-license-identifier: verify using type 'spec'
+INFO /spec/spdx-license-identifier:/spec-description: verify using type 'optional-str'
+INFO /spec/spdx-license-identifier:/spec-example: verify using type 'optional-str'
+INFO /spec/spdx-license-identifier:/spec-info: verify using type 'spec-info'
+INFO /spec/spdx-license-identifier:/spec-info/str: verify using type 'spec-str'
+INFO /spec/spdx-license-identifier:/spec-info/str/assert: verify using type 'spec-assert-str'
+INFO /spec/spdx-license-identifier:/spec-info/str/assert[0]: verify using type 'spec-assert-str'
+INFO /spec/spdx-license-identifier:/spec-info/str/assert[0]/eq: verify using type 'str'
+INFO /spec/spdx-license-identifier:/spec-info/str/assert[1]: verify using type 'spec-assert-str'
+INFO /spec/spdx-license-identifier:/spec-info/str/assert[1]/eq: verify using type 'str'
+INFO /spec/spdx-license-identifier:/spec-info/str/assert[2]: verify using type 'spec-assert-str'
+INFO /spec/spdx-license-identifier:/spec-info/str/assert[2]/eq: verify using type 'str'
+INFO /spec/spdx-license-identifier:/spec-info/str/description: verify using type 'optional-str'
+INFO /spec/spdx-license-identifier:/spec-name: verify using type 'optional-str'
+INFO /spec/spdx-license-identifier:/spec-type: verify using type 'name'
+INFO /spec/spec: verify using type 'root'
+INFO /spec/spec:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec/spec:/copyrights: verify using type 'copyrights'
+INFO /spec/spec:/copyrights[0]: verify using type 'copyright'
+INFO /spec/spec:/enabled-by: verify using type 'enabled-by'
+INFO /spec/spec:/links: verify using type 'links'
+INFO /spec/spec:/links[0]: verify using type 'link'
+INFO /spec/spec:/links[0]/role: verify using type 'name'
+INFO /spec/spec:/links[0]/uid: verify using type 'uid'
+INFO /spec/spec:/links[0]: verify using type 'spec-member'
+INFO /spec/spec:/links[1]: verify using type 'link'
+INFO /spec/spec:/links[1]/role: verify using type 'name'
+INFO /spec/spec:/links[1]/uid: verify using type 'uid'
+INFO /spec/spec:/links[1]: verify using type 'spec-refinement'
+INFO /spec/spec:/links[1]/spec-key: verify using type 'name'
+INFO /spec/spec:/links[1]/spec-value: verify using type 'name'
+INFO /spec/spec:/type: verify using type 'name'
+INFO /spec/spec: verify using type 'spec'
+INFO /spec/spec:/spec-description: verify using type 'optional-str'
+INFO /spec/spec:/spec-example: verify using type 'optional-str'
+INFO /spec/spec:/spec-info: verify using type 'spec-info'
+INFO /spec/spec:/spec-info/dict: verify using type 'spec-dict'
+INFO /spec/spec:/spec-info/dict/attributes: verify using type 'spec-attributes'
+INFO /spec/spec:/spec-info/dict/attributes/spec-description: verify using type 'name'
+INFO /spec/spec:/spec-info/dict/attributes/spec-description: verify using type 'spec-attribute-value'
+INFO /spec/spec:/spec-info/dict/attributes/spec-description/description: verify using type 'optional-str'
+INFO /spec/spec:/spec-info/dict/attributes/spec-description/spec-type: verify using type 'name'
+INFO /spec/spec:/spec-info/dict/attributes/spec-example: verify using type 'name'
+INFO /spec/spec:/spec-info/dict/attributes/spec-example: verify using type 'spec-attribute-value'
+INFO /spec/spec:/spec-info/dict/attributes/spec-example/description: verify using type 'optional-str'
+INFO /spec/spec:/spec-info/dict/attributes/spec-example/spec-type: verify using type 'name'
+INFO /spec/spec:/spec-info/dict/attributes/spec-info: verify using type 'name'
+INFO /spec/spec:/spec-info/dict/attributes/spec-info: verify using type 'spec-attribute-value'
+INFO /spec/spec:/spec-info/dict/attributes/spec-info/description: verify using type 'optional-str'
+INFO /spec/spec:/spec-info/dict/attributes/spec-info/spec-type: verify using type 'name'
+INFO /spec/spec:/spec-info/dict/attributes/spec-name: verify using type 'name'
+INFO /spec/spec:/spec-info/dict/attributes/spec-name: verify using type 'spec-attribute-value'
+INFO /spec/spec:/spec-info/dict/attributes/spec-name/description: verify using type 'optional-str'
+INFO /spec/spec:/spec-info/dict/attributes/spec-name/spec-type: verify using type 'name'
+INFO /spec/spec:/spec-info/dict/attributes/spec-type: verify using type 'name'
+INFO /spec/spec:/spec-info/dict/attributes/spec-type: verify using type 'spec-attribute-value'
+INFO /spec/spec:/spec-info/dict/attributes/spec-type/description: verify using type 'optional-str'
+INFO /spec/spec:/spec-info/dict/attributes/spec-type/spec-type: verify using type 'name'
+INFO /spec/spec:/spec-info/dict/description: verify using type 'optional-str'
+INFO /spec/spec:/spec-info/dict/mandatory-attributes: verify using type 'spec-mandatory-attributes'
+INFO /spec/spec:/spec-name: verify using type 'optional-str'
+INFO /spec/spec:/spec-type: verify using type 'name'
+INFO /spec/spec-assert-float: verify using type 'root'
+INFO /spec/spec-assert-float:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec/spec-assert-float:/copyrights: verify using type 'copyrights'
+INFO /spec/spec-assert-float:/copyrights[0]: verify using type 'copyright'
+INFO /spec/spec-assert-float:/enabled-by: verify using type 'enabled-by'
+INFO /spec/spec-assert-float:/links: verify using type 'links'
+INFO /spec/spec-assert-float:/links[0]: verify using type 'link'
+INFO /spec/spec-assert-float:/links[0]/role: verify using type 'name'
+INFO /spec/spec-assert-float:/links[0]/uid: verify using type 'uid'
+INFO /spec/spec-assert-float:/links[0]: verify using type 'spec-member'
+INFO /spec/spec-assert-float:/type: verify using type 'name'
+INFO /spec/spec-assert-float: verify using type 'spec'
+INFO /spec/spec-assert-float:/spec-description: verify using type 'optional-str'
+INFO /spec/spec-assert-float:/spec-example: verify using type 'optional-str'
+INFO /spec/spec-assert-float:/spec-info: verify using type 'spec-info'
+INFO /spec/spec-assert-float:/spec-info/dict: verify using type 'spec-dict'
+INFO /spec/spec-assert-float:/spec-info/dict/attributes: verify using type 'spec-attributes'
+INFO /spec/spec-assert-float:/spec-info/dict/attributes/and: verify using type 'name'
+INFO /spec/spec-assert-float:/spec-info/dict/attributes/and: verify using type 'spec-attribute-value'
+INFO /spec/spec-assert-float:/spec-info/dict/attributes/and/description: verify using type 'optional-str'
+INFO /spec/spec-assert-float:/spec-info/dict/attributes/and/spec-type: verify using type 'name'
+INFO /spec/spec-assert-float:/spec-info/dict/attributes/eq: verify using type 'name'
+INFO /spec/spec-assert-float:/spec-info/dict/attributes/eq: verify using type 'spec-attribute-value'
+INFO /spec/spec-assert-float:/spec-info/dict/attributes/eq/description: verify using type 'optional-str'
+INFO /spec/spec-assert-float:/spec-info/dict/attributes/eq/spec-type: verify using type 'name'
+INFO /spec/spec-assert-float:/spec-info/dict/attributes/ge: verify using type 'name'
+INFO /spec/spec-assert-float:/spec-info/dict/attributes/ge: verify using type 'spec-attribute-value'
+INFO /spec/spec-assert-float:/spec-info/dict/attributes/ge/description: verify using type 'optional-str'
+INFO /spec/spec-assert-float:/spec-info/dict/attributes/ge/spec-type: verify using type 'name'
+INFO /spec/spec-assert-float:/spec-info/dict/attributes/gt: verify using type 'name'
+INFO /spec/spec-assert-float:/spec-info/dict/attributes/gt: verify using type 'spec-attribute-value'
+INFO /spec/spec-assert-float:/spec-info/dict/attributes/gt/description: verify using type 'optional-str'
+INFO /spec/spec-assert-float:/spec-info/dict/attributes/gt/spec-type: verify using type 'name'
+INFO /spec/spec-assert-float:/spec-info/dict/attributes/le: verify using type 'name'
+INFO /spec/spec-assert-float:/spec-info/dict/attributes/le: verify using type 'spec-attribute-value'
+INFO /spec/spec-assert-float:/spec-info/dict/attributes/le/description: verify using type 'optional-str'
+INFO /spec/spec-assert-float:/spec-info/dict/attributes/le/spec-type: verify using type 'name'
+INFO /spec/spec-assert-float:/spec-info/dict/attributes/lt: verify using type 'name'
+INFO /spec/spec-assert-float:/spec-info/dict/attributes/lt: verify using type 'spec-attribute-value'
+INFO /spec/spec-assert-float:/spec-info/dict/attributes/lt/description: verify using type 'optional-str'
+INFO /spec/spec-assert-float:/spec-info/dict/attributes/lt/spec-type: verify using type 'name'
+INFO /spec/spec-assert-float:/spec-info/dict/attributes/ne: verify using type 'name'
+INFO /spec/spec-assert-float:/spec-info/dict/attributes/ne: verify using type 'spec-attribute-value'
+INFO /spec/spec-assert-float:/spec-info/dict/attributes/ne/description: verify using type 'optional-str'
+INFO /spec/spec-assert-float:/spec-info/dict/attributes/ne/spec-type: verify using type 'name'
+INFO /spec/spec-assert-float:/spec-info/dict/attributes/not: verify using type 'name'
+INFO /spec/spec-assert-float:/spec-info/dict/attributes/not: verify using type 'spec-attribute-value'
+INFO /spec/spec-assert-float:/spec-info/dict/attributes/not/description: verify using type 'optional-str'
+INFO /spec/spec-assert-float:/spec-info/dict/attributes/not/spec-type: verify using type 'name'
+INFO /spec/spec-assert-float:/spec-info/dict/attributes/or: verify using type 'name'
+INFO /spec/spec-assert-float:/spec-info/dict/attributes/or: verify using type 'spec-attribute-value'
+INFO /spec/spec-assert-float:/spec-info/dict/attributes/or/description: verify using type 'optional-str'
+INFO /spec/spec-assert-float:/spec-info/dict/attributes/or/spec-type: verify using type 'name'
+INFO /spec/spec-assert-float:/spec-info/dict/description: verify using type 'optional-str'
+INFO /spec/spec-assert-float:/spec-info/dict/mandatory-attributes: verify using type 'spec-mandatory-attributes'
+INFO /spec/spec-assert-float:/spec-info/list: verify using type 'spec-list'
+INFO /spec/spec-assert-float:/spec-info/list/description: verify using type 'optional-str'
+INFO /spec/spec-assert-float:/spec-info/list/spec-type: verify using type 'name'
+INFO /spec/spec-assert-float:/spec-name: verify using type 'optional-str'
+INFO /spec/spec-assert-float:/spec-type: verify using type 'name'
+INFO /spec/spec-assert-float-list: verify using type 'root'
+INFO /spec/spec-assert-float-list:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec/spec-assert-float-list:/copyrights: verify using type 'copyrights'
+INFO /spec/spec-assert-float-list:/copyrights[0]: verify using type 'copyright'
+INFO /spec/spec-assert-float-list:/enabled-by: verify using type 'enabled-by'
+INFO /spec/spec-assert-float-list:/links: verify using type 'links'
+INFO /spec/spec-assert-float-list:/links[0]: verify using type 'link'
+INFO /spec/spec-assert-float-list:/links[0]/role: verify using type 'name'
+INFO /spec/spec-assert-float-list:/links[0]/uid: verify using type 'uid'
+INFO /spec/spec-assert-float-list:/links[0]: verify using type 'spec-member'
+INFO /spec/spec-assert-float-list:/type: verify using type 'name'
+INFO /spec/spec-assert-float-list: verify using type 'spec'
+INFO /spec/spec-assert-float-list:/spec-description: verify using type 'optional-str'
+INFO /spec/spec-assert-float-list:/spec-example: verify using type 'optional-str'
+INFO /spec/spec-assert-float-list:/spec-info: verify using type 'spec-info'
+INFO /spec/spec-assert-float-list:/spec-info/list: verify using type 'spec-list'
+INFO /spec/spec-assert-float-list:/spec-info/list/description: verify using type 'optional-str'
+INFO /spec/spec-assert-float-list:/spec-info/list/spec-type: verify using type 'name'
+INFO /spec/spec-assert-float-list:/spec-name: verify using type 'optional-str'
+INFO /spec/spec-assert-float-list:/spec-type: verify using type 'name'
+INFO /spec/spec-assert-int: verify using type 'root'
+INFO /spec/spec-assert-int:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec/spec-assert-int:/copyrights: verify using type 'copyrights'
+INFO /spec/spec-assert-int:/copyrights[0]: verify using type 'copyright'
+INFO /spec/spec-assert-int:/enabled-by: verify using type 'enabled-by'
+INFO /spec/spec-assert-int:/links: verify using type 'links'
+INFO /spec/spec-assert-int:/links[0]: verify using type 'link'
+INFO /spec/spec-assert-int:/links[0]/role: verify using type 'name'
+INFO /spec/spec-assert-int:/links[0]/uid: verify using type 'uid'
+INFO /spec/spec-assert-int:/links[0]: verify using type 'spec-member'
+INFO /spec/spec-assert-int:/type: verify using type 'name'
+INFO /spec/spec-assert-int: verify using type 'spec'
+INFO /spec/spec-assert-int:/spec-description: verify using type 'optional-str'
+INFO /spec/spec-assert-int:/spec-example: verify using type 'optional-str'
+INFO /spec/spec-assert-int:/spec-info: verify using type 'spec-info'
+INFO /spec/spec-assert-int:/spec-info/dict: verify using type 'spec-dict'
+INFO /spec/spec-assert-int:/spec-info/dict/attributes: verify using type 'spec-attributes'
+INFO /spec/spec-assert-int:/spec-info/dict/attributes/and: verify using type 'name'
+INFO /spec/spec-assert-int:/spec-info/dict/attributes/and: verify using type 'spec-attribute-value'
+INFO /spec/spec-assert-int:/spec-info/dict/attributes/and/description: verify using type 'optional-str'
+INFO /spec/spec-assert-int:/spec-info/dict/attributes/and/spec-type: verify using type 'name'
+INFO /spec/spec-assert-int:/spec-info/dict/attributes/eq: verify using type 'name'
+INFO /spec/spec-assert-int:/spec-info/dict/attributes/eq: verify using type 'spec-attribute-value'
+INFO /spec/spec-assert-int:/spec-info/dict/attributes/eq/description: verify using type 'optional-str'
+INFO /spec/spec-assert-int:/spec-info/dict/attributes/eq/spec-type: verify using type 'name'
+INFO /spec/spec-assert-int:/spec-info/dict/attributes/ge: verify using type 'name'
+INFO /spec/spec-assert-int:/spec-info/dict/attributes/ge: verify using type 'spec-attribute-value'
+INFO /spec/spec-assert-int:/spec-info/dict/attributes/ge/description: verify using type 'optional-str'
+INFO /spec/spec-assert-int:/spec-info/dict/attributes/ge/spec-type: verify using type 'name'
+INFO /spec/spec-assert-int:/spec-info/dict/attributes/gt: verify using type 'name'
+INFO /spec/spec-assert-int:/spec-info/dict/attributes/gt: verify using type 'spec-attribute-value'
+INFO /spec/spec-assert-int:/spec-info/dict/attributes/gt/description: verify using type 'optional-str'
+INFO /spec/spec-assert-int:/spec-info/dict/attributes/gt/spec-type: verify using type 'name'
+INFO /spec/spec-assert-int:/spec-info/dict/attributes/le: verify using type 'name'
+INFO /spec/spec-assert-int:/spec-info/dict/attributes/le: verify using type 'spec-attribute-value'
+INFO /spec/spec-assert-int:/spec-info/dict/attributes/le/description: verify using type 'optional-str'
+INFO /spec/spec-assert-int:/spec-info/dict/attributes/le/spec-type: verify using type 'name'
+INFO /spec/spec-assert-int:/spec-info/dict/attributes/lt: verify using type 'name'
+INFO /spec/spec-assert-int:/spec-info/dict/attributes/lt: verify using type 'spec-attribute-value'
+INFO /spec/spec-assert-int:/spec-info/dict/attributes/lt/description: verify using type 'optional-str'
+INFO /spec/spec-assert-int:/spec-info/dict/attributes/lt/spec-type: verify using type 'name'
+INFO /spec/spec-assert-int:/spec-info/dict/attributes/ne: verify using type 'name'
+INFO /spec/spec-assert-int:/spec-info/dict/attributes/ne: verify using type 'spec-attribute-value'
+INFO /spec/spec-assert-int:/spec-info/dict/attributes/ne/description: verify using type 'optional-str'
+INFO /spec/spec-assert-int:/spec-info/dict/attributes/ne/spec-type: verify using type 'name'
+INFO /spec/spec-assert-int:/spec-info/dict/attributes/not: verify using type 'name'
+INFO /spec/spec-assert-int:/spec-info/dict/attributes/not: verify using type 'spec-attribute-value'
+INFO /spec/spec-assert-int:/spec-info/dict/attributes/not/description: verify using type 'optional-str'
+INFO /spec/spec-assert-int:/spec-info/dict/attributes/not/spec-type: verify using type 'name'
+INFO /spec/spec-assert-int:/spec-info/dict/attributes/or: verify using type 'name'
+INFO /spec/spec-assert-int:/spec-info/dict/attributes/or: verify using type 'spec-attribute-value'
+INFO /spec/spec-assert-int:/spec-info/dict/attributes/or/description: verify using type 'optional-str'
+INFO /spec/spec-assert-int:/spec-info/dict/attributes/or/spec-type: verify using type 'name'
+INFO /spec/spec-assert-int:/spec-info/dict/description: verify using type 'optional-str'
+INFO /spec/spec-assert-int:/spec-info/dict/mandatory-attributes: verify using type 'spec-mandatory-attributes'
+INFO /spec/spec-assert-int:/spec-info/list: verify using type 'spec-list'
+INFO /spec/spec-assert-int:/spec-info/list/description: verify using type 'optional-str'
+INFO /spec/spec-assert-int:/spec-info/list/spec-type: verify using type 'name'
+INFO /spec/spec-assert-int:/spec-name: verify using type 'optional-str'
+INFO /spec/spec-assert-int:/spec-type: verify using type 'name'
+INFO /spec/spec-assert-int-list: verify using type 'root'
+INFO /spec/spec-assert-int-list:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec/spec-assert-int-list:/copyrights: verify using type 'copyrights'
+INFO /spec/spec-assert-int-list:/copyrights[0]: verify using type 'copyright'
+INFO /spec/spec-assert-int-list:/enabled-by: verify using type 'enabled-by'
+INFO /spec/spec-assert-int-list:/links: verify using type 'links'
+INFO /spec/spec-assert-int-list:/links[0]: verify using type 'link'
+INFO /spec/spec-assert-int-list:/links[0]/role: verify using type 'name'
+INFO /spec/spec-assert-int-list:/links[0]/uid: verify using type 'uid'
+INFO /spec/spec-assert-int-list:/links[0]: verify using type 'spec-member'
+INFO /spec/spec-assert-int-list:/type: verify using type 'name'
+INFO /spec/spec-assert-int-list: verify using type 'spec'
+INFO /spec/spec-assert-int-list:/spec-description: verify using type 'optional-str'
+INFO /spec/spec-assert-int-list:/spec-example: verify using type 'optional-str'
+INFO /spec/spec-assert-int-list:/spec-info: verify using type 'spec-info'
+INFO /spec/spec-assert-int-list:/spec-info/list: verify using type 'spec-list'
+INFO /spec/spec-assert-int-list:/spec-info/list/description: verify using type 'optional-str'
+INFO /spec/spec-assert-int-list:/spec-info/list/spec-type: verify using type 'name'
+INFO /spec/spec-assert-int-list:/spec-name: verify using type 'optional-str'
+INFO /spec/spec-assert-int-list:/spec-type: verify using type 'name'
+INFO /spec/spec-assert-str: verify using type 'root'
+INFO /spec/spec-assert-str:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec/spec-assert-str:/copyrights: verify using type 'copyrights'
+INFO /spec/spec-assert-str:/copyrights[0]: verify using type 'copyright'
+INFO /spec/spec-assert-str:/enabled-by: verify using type 'enabled-by'
+INFO /spec/spec-assert-str:/links: verify using type 'links'
+INFO /spec/spec-assert-str:/links[0]: verify using type 'link'
+INFO /spec/spec-assert-str:/links[0]/role: verify using type 'name'
+INFO /spec/spec-assert-str:/links[0]/uid: verify using type 'uid'
+INFO /spec/spec-assert-str:/links[0]: verify using type 'spec-member'
+INFO /spec/spec-assert-str:/type: verify using type 'name'
+INFO /spec/spec-assert-str: verify using type 'spec'
+INFO /spec/spec-assert-str:/spec-description: verify using type 'optional-str'
+INFO /spec/spec-assert-str:/spec-example: verify using type 'optional-str'
+INFO /spec/spec-assert-str:/spec-info: verify using type 'spec-info'
+INFO /spec/spec-assert-str:/spec-info/dict: verify using type 'spec-dict'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes: verify using type 'spec-attributes'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/and: verify using type 'name'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/and: verify using type 'spec-attribute-value'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/and/description: verify using type 'optional-str'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/and/spec-type: verify using type 'name'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/contains: verify using type 'name'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/contains: verify using type 'spec-attribute-value'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/contains/description: verify using type 'optional-str'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/contains/spec-type: verify using type 'name'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/eq: verify using type 'name'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/eq: verify using type 'spec-attribute-value'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/eq/description: verify using type 'optional-str'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/eq/spec-type: verify using type 'name'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/ge: verify using type 'name'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/ge: verify using type 'spec-attribute-value'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/ge/description: verify using type 'optional-str'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/ge/spec-type: verify using type 'name'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/gt: verify using type 'name'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/gt: verify using type 'spec-attribute-value'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/gt/description: verify using type 'optional-str'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/gt/spec-type: verify using type 'name'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/in: verify using type 'name'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/in: verify using type 'spec-attribute-value'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/in/description: verify using type 'optional-str'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/in/spec-type: verify using type 'name'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/le: verify using type 'name'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/le: verify using type 'spec-attribute-value'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/le/description: verify using type 'optional-str'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/le/spec-type: verify using type 'name'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/lt: verify using type 'name'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/lt: verify using type 'spec-attribute-value'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/lt/description: verify using type 'optional-str'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/lt/spec-type: verify using type 'name'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/ne: verify using type 'name'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/ne: verify using type 'spec-attribute-value'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/ne/description: verify using type 'optional-str'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/ne/spec-type: verify using type 'name'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/not: verify using type 'name'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/not: verify using type 'spec-attribute-value'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/not/description: verify using type 'optional-str'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/not/spec-type: verify using type 'name'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/or: verify using type 'name'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/or: verify using type 'spec-attribute-value'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/or/description: verify using type 'optional-str'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/or/spec-type: verify using type 'name'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/re: verify using type 'name'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/re: verify using type 'spec-attribute-value'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/re/description: verify using type 'optional-str'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/re/spec-type: verify using type 'name'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/uid: verify using type 'name'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/uid: verify using type 'spec-attribute-value'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/uid/description: verify using type 'optional-str'
+INFO /spec/spec-assert-str:/spec-info/dict/attributes/uid/spec-type: verify using type 'name'
+INFO /spec/spec-assert-str:/spec-info/dict/description: verify using type 'optional-str'
+INFO /spec/spec-assert-str:/spec-info/dict/mandatory-attributes: verify using type 'spec-mandatory-attributes'
+INFO /spec/spec-assert-str:/spec-info/list: verify using type 'spec-list'
+INFO /spec/spec-assert-str:/spec-info/list/description: verify using type 'optional-str'
+INFO /spec/spec-assert-str:/spec-info/list/spec-type: verify using type 'name'
+INFO /spec/spec-assert-str:/spec-name: verify using type 'optional-str'
+INFO /spec/spec-assert-str:/spec-type: verify using type 'name'
+INFO /spec/spec-assert-str-list: verify using type 'root'
+INFO /spec/spec-assert-str-list:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec/spec-assert-str-list:/copyrights: verify using type 'copyrights'
+INFO /spec/spec-assert-str-list:/copyrights[0]: verify using type 'copyright'
+INFO /spec/spec-assert-str-list:/enabled-by: verify using type 'enabled-by'
+INFO /spec/spec-assert-str-list:/links: verify using type 'links'
+INFO /spec/spec-assert-str-list:/links[0]: verify using type 'link'
+INFO /spec/spec-assert-str-list:/links[0]/role: verify using type 'name'
+INFO /spec/spec-assert-str-list:/links[0]/uid: verify using type 'uid'
+INFO /spec/spec-assert-str-list:/links[0]: verify using type 'spec-member'
+INFO /spec/spec-assert-str-list:/type: verify using type 'name'
+INFO /spec/spec-assert-str-list: verify using type 'spec'
+INFO /spec/spec-assert-str-list:/spec-description: verify using type 'optional-str'
+INFO /spec/spec-assert-str-list:/spec-example: verify using type 'optional-str'
+INFO /spec/spec-assert-str-list:/spec-info: verify using type 'spec-info'
+INFO /spec/spec-assert-str-list:/spec-info/list: verify using type 'spec-list'
+INFO /spec/spec-assert-str-list:/spec-info/list/description: verify using type 'optional-str'
+INFO /spec/spec-assert-str-list:/spec-info/list/spec-type: verify using type 'name'
+INFO /spec/spec-assert-str-list:/spec-name: verify using type 'optional-str'
+INFO /spec/spec-assert-str-list:/spec-type: verify using type 'name'
+INFO /spec/spec-attribute-value: verify using type 'root'
+INFO /spec/spec-attribute-value:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec/spec-attribute-value:/copyrights: verify using type 'copyrights'
+INFO /spec/spec-attribute-value:/copyrights[0]: verify using type 'copyright'
+INFO /spec/spec-attribute-value:/enabled-by: verify using type 'enabled-by'
+INFO /spec/spec-attribute-value:/links: verify using type 'links'
+INFO /spec/spec-attribute-value:/links[0]: verify using type 'link'
+INFO /spec/spec-attribute-value:/links[0]/role: verify using type 'name'
+INFO /spec/spec-attribute-value:/links[0]/uid: verify using type 'uid'
+INFO /spec/spec-attribute-value:/links[0]: verify using type 'spec-member'
+INFO /spec/spec-attribute-value:/type: verify using type 'name'
+INFO /spec/spec-attribute-value: verify using type 'spec'
+INFO /spec/spec-attribute-value:/spec-description: verify using type 'optional-str'
+INFO /spec/spec-attribute-value:/spec-example: verify using type 'optional-str'
+INFO /spec/spec-attribute-value:/spec-info: verify using type 'spec-info'
+INFO /spec/spec-attribute-value:/spec-info/dict: verify using type 'spec-dict'
+INFO /spec/spec-attribute-value:/spec-info/dict/attributes: verify using type 'spec-attributes'
+INFO /spec/spec-attribute-value:/spec-info/dict/attributes/description: verify using type 'name'
+INFO /spec/spec-attribute-value:/spec-info/dict/attributes/description: verify using type 'spec-attribute-value'
+INFO /spec/spec-attribute-value:/spec-info/dict/attributes/description/description: verify using type 'optional-str'
+INFO /spec/spec-attribute-value:/spec-info/dict/attributes/description/spec-type: verify using type 'name'
+INFO /spec/spec-attribute-value:/spec-info/dict/attributes/spec-type: verify using type 'name'
+INFO /spec/spec-attribute-value:/spec-info/dict/attributes/spec-type: verify using type 'spec-attribute-value'
+INFO /spec/spec-attribute-value:/spec-info/dict/attributes/spec-type/description: verify using type 'optional-str'
+INFO /spec/spec-attribute-value:/spec-info/dict/attributes/spec-type/spec-type: verify using type 'name'
+INFO /spec/spec-attribute-value:/spec-info/dict/description: verify using type 'optional-str'
+INFO /spec/spec-attribute-value:/spec-info/dict/mandatory-attributes: verify using type 'spec-mandatory-attributes'
+INFO /spec/spec-attribute-value:/spec-name: verify using type 'optional-str'
+INFO /spec/spec-attribute-value:/spec-type: verify using type 'name'
+INFO /spec/spec-attributes: verify using type 'root'
+INFO /spec/spec-attributes:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec/spec-attributes:/copyrights: verify using type 'copyrights'
+INFO /spec/spec-attributes:/copyrights[0]: verify using type 'copyright'
+INFO /spec/spec-attributes:/enabled-by: verify using type 'enabled-by'
+INFO /spec/spec-attributes:/links: verify using type 'links'
+INFO /spec/spec-attributes:/links[0]: verify using type 'link'
+INFO /spec/spec-attributes:/links[0]/role: verify using type 'name'
+INFO /spec/spec-attributes:/links[0]/uid: verify using type 'uid'
+INFO /spec/spec-attributes:/links[0]: verify using type 'spec-member'
+INFO /spec/spec-attributes:/type: verify using type 'name'
+INFO /spec/spec-attributes: verify using type 'spec'
+INFO /spec/spec-attributes:/spec-description: verify using type 'optional-str'
+INFO /spec/spec-attributes:/spec-example: verify using type 'optional-str'
+INFO /spec/spec-attributes:/spec-info: verify using type 'spec-info'
+INFO /spec/spec-attributes:/spec-info/dict: verify using type 'spec-dict'
+INFO /spec/spec-attributes:/spec-info/dict/attributes: verify using type 'spec-attributes'
+INFO /spec/spec-attributes:/spec-info/dict/description: verify using type 'optional-str'
+INFO /spec/spec-attributes:/spec-info/dict/generic-attributes: verify using type 'spec-generic-attributes'
+INFO /spec/spec-attributes:/spec-info/dict/generic-attributes/description: verify using type 'optional-str'
+INFO /spec/spec-attributes:/spec-info/dict/generic-attributes/key-spec-type: verify using type 'name'
+INFO /spec/spec-attributes:/spec-info/dict/generic-attributes/value-spec-type: verify using type 'name'
+INFO /spec/spec-attributes:/spec-info/dict/mandatory-attributes: verify using type 'spec-mandatory-attributes'
+INFO /spec/spec-attributes:/spec-name: verify using type 'optional-str'
+INFO /spec/spec-attributes:/spec-type: verify using type 'name'
+INFO /spec/spec-bool: verify using type 'root'
+INFO /spec/spec-bool:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec/spec-bool:/copyrights: verify using type 'copyrights'
+INFO /spec/spec-bool:/copyrights[0]: verify using type 'copyright'
+INFO /spec/spec-bool:/enabled-by: verify using type 'enabled-by'
+INFO /spec/spec-bool:/links: verify using type 'links'
+INFO /spec/spec-bool:/links[0]: verify using type 'link'
+INFO /spec/spec-bool:/links[0]/role: verify using type 'name'
+INFO /spec/spec-bool:/links[0]/uid: verify using type 'uid'
+INFO /spec/spec-bool:/links[0]: verify using type 'spec-member'
+INFO /spec/spec-bool:/type: verify using type 'name'
+INFO /spec/spec-bool: verify using type 'spec'
+INFO /spec/spec-bool:/spec-description: verify using type 'optional-str'
+INFO /spec/spec-bool:/spec-example: verify using type 'optional-str'
+INFO /spec/spec-bool:/spec-info: verify using type 'spec-info'
+INFO /spec/spec-bool:/spec-info/dict: verify using type 'spec-dict'
+INFO /spec/spec-bool:/spec-info/dict/attributes: verify using type 'spec-attributes'
+INFO /spec/spec-bool:/spec-info/dict/attributes/assert: verify using type 'name'
+INFO /spec/spec-bool:/spec-info/dict/attributes/assert: verify using type 'spec-attribute-value'
+INFO /spec/spec-bool:/spec-info/dict/attributes/assert/description: verify using type 'optional-str'
+INFO /spec/spec-bool:/spec-info/dict/attributes/assert/spec-type: verify using type 'name'
+INFO /spec/spec-bool:/spec-info/dict/attributes/description: verify using type 'name'
+INFO /spec/spec-bool:/spec-info/dict/attributes/description: verify using type 'spec-attribute-value'
+INFO /spec/spec-bool:/spec-info/dict/attributes/description/description: verify using type 'optional-str'
+INFO /spec/spec-bool:/spec-info/dict/attributes/description/spec-type: verify using type 'name'
+INFO /spec/spec-bool:/spec-info/dict/description: verify using type 'optional-str'
+INFO /spec/spec-bool:/spec-info/dict/mandatory-attributes: verify using type 'spec-mandatory-attributes'
+INFO /spec/spec-bool:/spec-info/dict/mandatory-attributes[0]: verify using type 'name'
+INFO /spec/spec-bool:/spec-name: verify using type 'optional-str'
+INFO /spec/spec-bool:/spec-type: verify using type 'name'
+INFO /spec/spec-dict: verify using type 'root'
+INFO /spec/spec-dict:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec/spec-dict:/copyrights: verify using type 'copyrights'
+INFO /spec/spec-dict:/copyrights[0]: verify using type 'copyright'
+INFO /spec/spec-dict:/enabled-by: verify using type 'enabled-by'
+INFO /spec/spec-dict:/links: verify using type 'links'
+INFO /spec/spec-dict:/links[0]: verify using type 'link'
+INFO /spec/spec-dict:/links[0]/role: verify using type 'name'
+INFO /spec/spec-dict:/links[0]/uid: verify using type 'uid'
+INFO /spec/spec-dict:/links[0]: verify using type 'spec-member'
+INFO /spec/spec-dict:/type: verify using type 'name'
+INFO /spec/spec-dict: verify using type 'spec'
+INFO /spec/spec-dict:/spec-description: verify using type 'optional-str'
+INFO /spec/spec-dict:/spec-example: verify using type 'optional-str'
+INFO /spec/spec-dict:/spec-info: verify using type 'spec-info'
+INFO /spec/spec-dict:/spec-info/dict: verify using type 'spec-dict'
+INFO /spec/spec-dict:/spec-info/dict/attributes: verify using type 'spec-attributes'
+INFO /spec/spec-dict:/spec-info/dict/attributes/attributes: verify using type 'name'
+INFO /spec/spec-dict:/spec-info/dict/attributes/attributes: verify using type 'spec-attribute-value'
+INFO /spec/spec-dict:/spec-info/dict/attributes/attributes/description: verify using type 'optional-str'
+INFO /spec/spec-dict:/spec-info/dict/attributes/attributes/spec-type: verify using type 'name'
+INFO /spec/spec-dict:/spec-info/dict/attributes/description: verify using type 'name'
+INFO /spec/spec-dict:/spec-info/dict/attributes/description: verify using type 'spec-attribute-value'
+INFO /spec/spec-dict:/spec-info/dict/attributes/description/description: verify using type 'optional-str'
+INFO /spec/spec-dict:/spec-info/dict/attributes/description/spec-type: verify using type 'name'
+INFO /spec/spec-dict:/spec-info/dict/attributes/generic-attributes: verify using type 'name'
+INFO /spec/spec-dict:/spec-info/dict/attributes/generic-attributes: verify using type 'spec-attribute-value'
+INFO /spec/spec-dict:/spec-info/dict/attributes/generic-attributes/description: verify using type 'optional-str'
+INFO /spec/spec-dict:/spec-info/dict/attributes/generic-attributes/spec-type: verify using type 'name'
+INFO /spec/spec-dict:/spec-info/dict/attributes/mandatory-attributes: verify using type 'name'
+INFO /spec/spec-dict:/spec-info/dict/attributes/mandatory-attributes: verify using type 'spec-attribute-value'
+INFO /spec/spec-dict:/spec-info/dict/attributes/mandatory-attributes/description: verify using type 'optional-str'
+INFO /spec/spec-dict:/spec-info/dict/attributes/mandatory-attributes/spec-type: verify using type 'name'
+INFO /spec/spec-dict:/spec-info/dict/description: verify using type 'optional-str'
+INFO /spec/spec-dict:/spec-info/dict/mandatory-attributes: verify using type 'spec-mandatory-attributes'
+INFO /spec/spec-dict:/spec-info/dict/mandatory-attributes[0]: verify using type 'name'
+INFO /spec/spec-dict:/spec-info/dict/mandatory-attributes[1]: verify using type 'name'
+INFO /spec/spec-dict:/spec-info/dict/mandatory-attributes[2]: verify using type 'name'
+INFO /spec/spec-dict:/spec-name: verify using type 'optional-str'
+INFO /spec/spec-dict:/spec-type: verify using type 'name'
+INFO /spec/spec-float: verify using type 'root'
+INFO /spec/spec-float:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec/spec-float:/copyrights: verify using type 'copyrights'
+INFO /spec/spec-float:/copyrights[0]: verify using type 'copyright'
+INFO /spec/spec-float:/enabled-by: verify using type 'enabled-by'
+INFO /spec/spec-float:/links: verify using type 'links'
+INFO /spec/spec-float:/links[0]: verify using type 'link'
+INFO /spec/spec-float:/links[0]/role: verify using type 'name'
+INFO /spec/spec-float:/links[0]/uid: verify using type 'uid'
+INFO /spec/spec-float:/links[0]: verify using type 'spec-member'
+INFO /spec/spec-float:/type: verify using type 'name'
+INFO /spec/spec-float: verify using type 'spec'
+INFO /spec/spec-float:/spec-description: verify using type 'optional-str'
+INFO /spec/spec-float:/spec-example: verify using type 'optional-str'
+INFO /spec/spec-float:/spec-info: verify using type 'spec-info'
+INFO /spec/spec-float:/spec-info/dict: verify using type 'spec-dict'
+INFO /spec/spec-float:/spec-info/dict/attributes: verify using type 'spec-attributes'
+INFO /spec/spec-float:/spec-info/dict/attributes/assert: verify using type 'name'
+INFO /spec/spec-float:/spec-info/dict/attributes/assert: verify using type 'spec-attribute-value'
+INFO /spec/spec-float:/spec-info/dict/attributes/assert/description: verify using type 'optional-str'
+INFO /spec/spec-float:/spec-info/dict/attributes/assert/spec-type: verify using type 'name'
+INFO /spec/spec-float:/spec-info/dict/attributes/description: verify using type 'name'
+INFO /spec/spec-float:/spec-info/dict/attributes/description: verify using type 'spec-attribute-value'
+INFO /spec/spec-float:/spec-info/dict/attributes/description/description: verify using type 'optional-str'
+INFO /spec/spec-float:/spec-info/dict/attributes/description/spec-type: verify using type 'name'
+INFO /spec/spec-float:/spec-info/dict/description: verify using type 'optional-str'
+INFO /spec/spec-float:/spec-info/dict/mandatory-attributes: verify using type 'spec-mandatory-attributes'
+INFO /spec/spec-float:/spec-info/dict/mandatory-attributes[0]: verify using type 'name'
+INFO /spec/spec-float:/spec-name: verify using type 'optional-str'
+INFO /spec/spec-float:/spec-type: verify using type 'name'
+INFO /spec/spec-generic-attributes: verify using type 'root'
+INFO /spec/spec-generic-attributes:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec/spec-generic-attributes:/copyrights: verify using type 'copyrights'
+INFO /spec/spec-generic-attributes:/copyrights[0]: verify using type 'copyright'
+INFO /spec/spec-generic-attributes:/enabled-by: verify using type 'enabled-by'
+INFO /spec/spec-generic-attributes:/links: verify using type 'links'
+INFO /spec/spec-generic-attributes:/links[0]: verify using type 'link'
+INFO /spec/spec-generic-attributes:/links[0]/role: verify using type 'name'
+INFO /spec/spec-generic-attributes:/links[0]/uid: verify using type 'uid'
+INFO /spec/spec-generic-attributes:/links[0]: verify using type 'spec-member'
+INFO /spec/spec-generic-attributes:/type: verify using type 'name'
+INFO /spec/spec-generic-attributes: verify using type 'spec'
+INFO /spec/spec-generic-attributes:/spec-description: verify using type 'optional-str'
+INFO /spec/spec-generic-attributes:/spec-example: verify using type 'optional-str'
+INFO /spec/spec-generic-attributes:/spec-info: verify using type 'spec-info'
+INFO /spec/spec-generic-attributes:/spec-info/dict: verify using type 'spec-dict'
+INFO /spec/spec-generic-attributes:/spec-info/dict/attributes: verify using type 'spec-attributes'
+INFO /spec/spec-generic-attributes:/spec-info/dict/attributes/description: verify using type 'name'
+INFO /spec/spec-generic-attributes:/spec-info/dict/attributes/description: verify using type 'spec-attribute-value'
+INFO /spec/spec-generic-attributes:/spec-info/dict/attributes/description/description: verify using type 'optional-str'
+INFO /spec/spec-generic-attributes:/spec-info/dict/attributes/description/spec-type: verify using type 'name'
+INFO /spec/spec-generic-attributes:/spec-info/dict/attributes/key-spec-type: verify using type 'name'
+INFO /spec/spec-generic-attributes:/spec-info/dict/attributes/key-spec-type: verify using type 'spec-attribute-value'
+INFO /spec/spec-generic-attributes:/spec-info/dict/attributes/key-spec-type/description: verify using type 'optional-str'
+INFO /spec/spec-generic-attributes:/spec-info/dict/attributes/key-spec-type/spec-type: verify using type 'name'
+INFO /spec/spec-generic-attributes:/spec-info/dict/attributes/value-spec-type: verify using type 'name'
+INFO /spec/spec-generic-attributes:/spec-info/dict/attributes/value-spec-type: verify using type 'spec-attribute-value'
+INFO /spec/spec-generic-attributes:/spec-info/dict/attributes/value-spec-type/description: verify using type 'optional-str'
+INFO /spec/spec-generic-attributes:/spec-info/dict/attributes/value-spec-type/spec-type: verify using type 'name'
+INFO /spec/spec-generic-attributes:/spec-info/dict/description: verify using type 'optional-str'
+INFO /spec/spec-generic-attributes:/spec-info/dict/mandatory-attributes: verify using type 'spec-mandatory-attributes'
+INFO /spec/spec-generic-attributes:/spec-name: verify using type 'optional-str'
+INFO /spec/spec-generic-attributes:/spec-type: verify using type 'name'
+INFO /spec/spec-info: verify using type 'root'
+INFO /spec/spec-info:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec/spec-info:/copyrights: verify using type 'copyrights'
+INFO /spec/spec-info:/copyrights[0]: verify using type 'copyright'
+INFO /spec/spec-info:/enabled-by: verify using type 'enabled-by'
+INFO /spec/spec-info:/links: verify using type 'links'
+INFO /spec/spec-info:/links[0]: verify using type 'link'
+INFO /spec/spec-info:/links[0]/role: verify using type 'name'
+INFO /spec/spec-info:/links[0]/uid: verify using type 'uid'
+INFO /spec/spec-info:/links[0]: verify using type 'spec-member'
+INFO /spec/spec-info:/type: verify using type 'name'
+INFO /spec/spec-info: verify using type 'spec'
+INFO /spec/spec-info:/spec-description: verify using type 'optional-str'
+INFO /spec/spec-info:/spec-example: verify using type 'optional-str'
+INFO /spec/spec-info:/spec-info: verify using type 'spec-info'
+INFO /spec/spec-info:/spec-info/dict: verify using type 'spec-dict'
+INFO /spec/spec-info:/spec-info/dict/attributes: verify using type 'spec-attributes'
+INFO /spec/spec-info:/spec-info/dict/attributes/bool: verify using type 'name'
+INFO /spec/spec-info:/spec-info/dict/attributes/bool: verify using type 'spec-attribute-value'
+INFO /spec/spec-info:/spec-info/dict/attributes/bool/description: verify using type 'optional-str'
+INFO /spec/spec-info:/spec-info/dict/attributes/bool/spec-type: verify using type 'name'
+INFO /spec/spec-info:/spec-info/dict/attributes/dict: verify using type 'name'
+INFO /spec/spec-info:/spec-info/dict/attributes/dict: verify using type 'spec-attribute-value'
+INFO /spec/spec-info:/spec-info/dict/attributes/dict/description: verify using type 'optional-str'
+INFO /spec/spec-info:/spec-info/dict/attributes/dict/spec-type: verify using type 'name'
+INFO /spec/spec-info:/spec-info/dict/attributes/float: verify using type 'name'
+INFO /spec/spec-info:/spec-info/dict/attributes/float: verify using type 'spec-attribute-value'
+INFO /spec/spec-info:/spec-info/dict/attributes/float/description: verify using type 'optional-str'
+INFO /spec/spec-info:/spec-info/dict/attributes/float/spec-type: verify using type 'name'
+INFO /spec/spec-info:/spec-info/dict/attributes/int: verify using type 'name'
+INFO /spec/spec-info:/spec-info/dict/attributes/int: verify using type 'spec-attribute-value'
+INFO /spec/spec-info:/spec-info/dict/attributes/int/description: verify using type 'optional-str'
+INFO /spec/spec-info:/spec-info/dict/attributes/int/spec-type: verify using type 'name'
+INFO /spec/spec-info:/spec-info/dict/attributes/list: verify using type 'name'
+INFO /spec/spec-info:/spec-info/dict/attributes/list: verify using type 'spec-attribute-value'
+INFO /spec/spec-info:/spec-info/dict/attributes/list/description: verify using type 'optional-str'
+INFO /spec/spec-info:/spec-info/dict/attributes/list/spec-type: verify using type 'name'
+INFO /spec/spec-info:/spec-info/dict/attributes/none: verify using type 'name'
+INFO /spec/spec-info:/spec-info/dict/attributes/none: verify using type 'spec-attribute-value'
+INFO /spec/spec-info:/spec-info/dict/attributes/none/description: verify using type 'optional-str'
+INFO /spec/spec-info:/spec-info/dict/attributes/none/spec-type: verify using type 'name'
+INFO /spec/spec-info:/spec-info/dict/attributes/str: verify using type 'name'
+INFO /spec/spec-info:/spec-info/dict/attributes/str: verify using type 'spec-attribute-value'
+INFO /spec/spec-info:/spec-info/dict/attributes/str/description: verify using type 'optional-str'
+INFO /spec/spec-info:/spec-info/dict/attributes/str/spec-type: verify using type 'name'
+INFO /spec/spec-info:/spec-info/dict/description: verify using type 'optional-str'
+INFO /spec/spec-info:/spec-info/dict/mandatory-attributes: verify using type 'spec-mandatory-attributes'
+INFO /spec/spec-info:/spec-name: verify using type 'optional-str'
+INFO /spec/spec-info:/spec-type: verify using type 'name'
+INFO /spec/spec-int: verify using type 'root'
+INFO /spec/spec-int:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec/spec-int:/copyrights: verify using type 'copyrights'
+INFO /spec/spec-int:/copyrights[0]: verify using type 'copyright'
+INFO /spec/spec-int:/enabled-by: verify using type 'enabled-by'
+INFO /spec/spec-int:/links: verify using type 'links'
+INFO /spec/spec-int:/links[0]: verify using type 'link'
+INFO /spec/spec-int:/links[0]/role: verify using type 'name'
+INFO /spec/spec-int:/links[0]/uid: verify using type 'uid'
+INFO /spec/spec-int:/links[0]: verify using type 'spec-member'
+INFO /spec/spec-int:/type: verify using type 'name'
+INFO /spec/spec-int: verify using type 'spec'
+INFO /spec/spec-int:/spec-description: verify using type 'optional-str'
+INFO /spec/spec-int:/spec-example: verify using type 'optional-str'
+INFO /spec/spec-int:/spec-info: verify using type 'spec-info'
+INFO /spec/spec-int:/spec-info/dict: verify using type 'spec-dict'
+INFO /spec/spec-int:/spec-info/dict/attributes: verify using type 'spec-attributes'
+INFO /spec/spec-int:/spec-info/dict/attributes/assert: verify using type 'name'
+INFO /spec/spec-int:/spec-info/dict/attributes/assert: verify using type 'spec-attribute-value'
+INFO /spec/spec-int:/spec-info/dict/attributes/assert/description: verify using type 'optional-str'
+INFO /spec/spec-int:/spec-info/dict/attributes/assert/spec-type: verify using type 'name'
+INFO /spec/spec-int:/spec-info/dict/attributes/description: verify using type 'name'
+INFO /spec/spec-int:/spec-info/dict/attributes/description: verify using type 'spec-attribute-value'
+INFO /spec/spec-int:/spec-info/dict/attributes/description/description: verify using type 'optional-str'
+INFO /spec/spec-int:/spec-info/dict/attributes/description/spec-type: verify using type 'name'
+INFO /spec/spec-int:/spec-info/dict/description: verify using type 'optional-str'
+INFO /spec/spec-int:/spec-info/dict/mandatory-attributes: verify using type 'spec-mandatory-attributes'
+INFO /spec/spec-int:/spec-info/dict/mandatory-attributes[0]: verify using type 'name'
+INFO /spec/spec-int:/spec-name: verify using type 'optional-str'
+INFO /spec/spec-int:/spec-type: verify using type 'name'
+INFO /spec/spec-list: verify using type 'root'
+INFO /spec/spec-list:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec/spec-list:/copyrights: verify using type 'copyrights'
+INFO /spec/spec-list:/copyrights[0]: verify using type 'copyright'
+INFO /spec/spec-list:/enabled-by: verify using type 'enabled-by'
+INFO /spec/spec-list:/links: verify using type 'links'
+INFO /spec/spec-list:/links[0]: verify using type 'link'
+INFO /spec/spec-list:/links[0]/role: verify using type 'name'
+INFO /spec/spec-list:/links[0]/uid: verify using type 'uid'
+INFO /spec/spec-list:/links[0]: verify using type 'spec-member'
+INFO /spec/spec-list:/type: verify using type 'name'
+INFO /spec/spec-list: verify using type 'spec'
+INFO /spec/spec-list:/spec-description: verify using type 'optional-str'
+INFO /spec/spec-list:/spec-example: verify using type 'optional-str'
+INFO /spec/spec-list:/spec-info: verify using type 'spec-info'
+INFO /spec/spec-list:/spec-info/dict: verify using type 'spec-dict'
+INFO /spec/spec-list:/spec-info/dict/attributes: verify using type 'spec-attributes'
+INFO /spec/spec-list:/spec-info/dict/attributes/description: verify using type 'name'
+INFO /spec/spec-list:/spec-info/dict/attributes/description: verify using type 'spec-attribute-value'
+INFO /spec/spec-list:/spec-info/dict/attributes/description/description: verify using type 'optional-str'
+INFO /spec/spec-list:/spec-info/dict/attributes/description/spec-type: verify using type 'name'
+INFO /spec/spec-list:/spec-info/dict/attributes/spec-type: verify using type 'name'
+INFO /spec/spec-list:/spec-info/dict/attributes/spec-type: verify using type 'spec-attribute-value'
+INFO /spec/spec-list:/spec-info/dict/attributes/spec-type/description: verify using type 'optional-str'
+INFO /spec/spec-list:/spec-info/dict/attributes/spec-type/spec-type: verify using type 'name'
+INFO /spec/spec-list:/spec-info/dict/description: verify using type 'optional-str'
+INFO /spec/spec-list:/spec-info/dict/mandatory-attributes: verify using type 'spec-mandatory-attributes'
+INFO /spec/spec-list:/spec-name: verify using type 'optional-str'
+INFO /spec/spec-list:/spec-type: verify using type 'name'
+INFO /spec/spec-mandatory-attributes: verify using type 'root'
+INFO /spec/spec-mandatory-attributes:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec/spec-mandatory-attributes:/copyrights: verify using type 'copyrights'
+INFO /spec/spec-mandatory-attributes:/copyrights[0]: verify using type 'copyright'
+INFO /spec/spec-mandatory-attributes:/enabled-by: verify using type 'enabled-by'
+INFO /spec/spec-mandatory-attributes:/links: verify using type 'links'
+INFO /spec/spec-mandatory-attributes:/links[0]: verify using type 'link'
+INFO /spec/spec-mandatory-attributes:/links[0]/role: verify using type 'name'
+INFO /spec/spec-mandatory-attributes:/links[0]/uid: verify using type 'uid'
+INFO /spec/spec-mandatory-attributes:/links[0]: verify using type 'spec-member'
+INFO /spec/spec-mandatory-attributes:/type: verify using type 'name'
+INFO /spec/spec-mandatory-attributes: verify using type 'spec'
+INFO /spec/spec-mandatory-attributes:/spec-description: verify using type 'optional-str'
+INFO /spec/spec-mandatory-attributes:/spec-example: verify using type 'optional-str'
+INFO /spec/spec-mandatory-attributes:/spec-info: verify using type 'spec-info'
+INFO /spec/spec-mandatory-attributes:/spec-info/list: verify using type 'spec-list'
+INFO /spec/spec-mandatory-attributes:/spec-info/list/description: verify using type 'optional-str'
+INFO /spec/spec-mandatory-attributes:/spec-info/list/spec-type: verify using type 'name'
+INFO /spec/spec-mandatory-attributes:/spec-info/str: verify using type 'spec-str'
+INFO /spec/spec-mandatory-attributes:/spec-info/str/assert: verify using type 'spec-assert-str'
+INFO /spec/spec-mandatory-attributes:/spec-info/str/assert/in: verify using type 'list-str'
+INFO /spec/spec-mandatory-attributes:/spec-info/str/assert/in[0]: verify using type 'str'
+INFO /spec/spec-mandatory-attributes:/spec-info/str/assert/in[1]: verify using type 'str'
+INFO /spec/spec-mandatory-attributes:/spec-info/str/assert/in[2]: verify using type 'str'
+INFO /spec/spec-mandatory-attributes:/spec-info/str/assert/in[3]: verify using type 'str'
+INFO /spec/spec-mandatory-attributes:/spec-info/str/assert/in[4]: verify using type 'str'
+INFO /spec/spec-mandatory-attributes:/spec-info/str/description: verify using type 'optional-str'
+INFO /spec/spec-mandatory-attributes:/spec-name: verify using type 'optional-str'
+INFO /spec/spec-mandatory-attributes:/spec-type: verify using type 'name'
+INFO /spec/spec-member: verify using type 'root'
+INFO /spec/spec-member:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec/spec-member:/copyrights: verify using type 'copyrights'
+INFO /spec/spec-member:/copyrights[0]: verify using type 'copyright'
+INFO /spec/spec-member:/enabled-by: verify using type 'enabled-by'
+INFO /spec/spec-member:/links: verify using type 'links'
+INFO /spec/spec-member:/links[0]: verify using type 'link'
+INFO /spec/spec-member:/links[0]/role: verify using type 'name'
+INFO /spec/spec-member:/links[0]/uid: verify using type 'uid'
+INFO /spec/spec-member:/links[0]: verify using type 'spec-member'
+INFO /spec/spec-member:/links[1]: verify using type 'link'
+INFO /spec/spec-member:/links[1]/role: verify using type 'name'
+INFO /spec/spec-member:/links[1]/uid: verify using type 'uid'
+INFO /spec/spec-member:/links[1]: verify using type 'spec-refinement'
+INFO /spec/spec-member:/links[1]/spec-key: verify using type 'name'
+INFO /spec/spec-member:/links[1]/spec-value: verify using type 'name'
+INFO /spec/spec-member:/type: verify using type 'name'
+INFO /spec/spec-member: verify using type 'spec'
+INFO /spec/spec-member:/spec-description: verify using type 'optional-str'
+INFO /spec/spec-member:/spec-example: verify using type 'optional-str'
+INFO /spec/spec-member:/spec-info: verify using type 'spec-info'
+INFO /spec/spec-member:/spec-info/dict: verify using type 'spec-dict'
+INFO /spec/spec-member:/spec-info/dict/attributes: verify using type 'spec-attributes'
+INFO /spec/spec-member:/spec-info/dict/description: verify using type 'optional-str'
+INFO /spec/spec-member:/spec-info/dict/mandatory-attributes: verify using type 'spec-mandatory-attributes'
+INFO /spec/spec-member:/spec-name: verify using type 'optional-str'
+INFO /spec/spec-member:/spec-type: verify using type 'name'
+INFO /spec/spec-refinement: verify using type 'root'
+INFO /spec/spec-refinement:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec/spec-refinement:/copyrights: verify using type 'copyrights'
+INFO /spec/spec-refinement:/copyrights[0]: verify using type 'copyright'
+INFO /spec/spec-refinement:/enabled-by: verify using type 'enabled-by'
+INFO /spec/spec-refinement:/links: verify using type 'links'
+INFO /spec/spec-refinement:/links[0]: verify using type 'link'
+INFO /spec/spec-refinement:/links[0]/role: verify using type 'name'
+INFO /spec/spec-refinement:/links[0]/uid: verify using type 'uid'
+INFO /spec/spec-refinement:/links[0]: verify using type 'spec-member'
+INFO /spec/spec-refinement:/links[1]: verify using type 'link'
+INFO /spec/spec-refinement:/links[1]/role: verify using type 'name'
+INFO /spec/spec-refinement:/links[1]/uid: verify using type 'uid'
+INFO /spec/spec-refinement:/links[1]: verify using type 'spec-refinement'
+INFO /spec/spec-refinement:/links[1]/spec-key: verify using type 'name'
+INFO /spec/spec-refinement:/links[1]/spec-value: verify using type 'name'
+INFO /spec/spec-refinement:/type: verify using type 'name'
+INFO /spec/spec-refinement: verify using type 'spec'
+INFO /spec/spec-refinement:/spec-description: verify using type 'optional-str'
+INFO /spec/spec-refinement:/spec-example: verify using type 'optional-str'
+INFO /spec/spec-refinement:/spec-info: verify using type 'spec-info'
+INFO /spec/spec-refinement:/spec-info/dict: verify using type 'spec-dict'
+INFO /spec/spec-refinement:/spec-info/dict/attributes: verify using type 'spec-attributes'
+INFO /spec/spec-refinement:/spec-info/dict/attributes/spec-key: verify using type 'name'
+INFO /spec/spec-refinement:/spec-info/dict/attributes/spec-key: verify using type 'spec-attribute-value'
+INFO /spec/spec-refinement:/spec-info/dict/attributes/spec-key/description: verify using type 'optional-str'
+INFO /spec/spec-refinement:/spec-info/dict/attributes/spec-key/spec-type: verify using type 'name'
+INFO /spec/spec-refinement:/spec-info/dict/attributes/spec-value: verify using type 'name'
+INFO /spec/spec-refinement:/spec-info/dict/attributes/spec-value: verify using type 'spec-attribute-value'
+INFO /spec/spec-refinement:/spec-info/dict/attributes/spec-value/description: verify using type 'optional-str'
+INFO /spec/spec-refinement:/spec-info/dict/attributes/spec-value/spec-type: verify using type 'name'
+INFO /spec/spec-refinement:/spec-info/dict/description: verify using type 'optional-str'
+INFO /spec/spec-refinement:/spec-info/dict/mandatory-attributes: verify using type 'spec-mandatory-attributes'
+INFO /spec/spec-refinement:/spec-name: verify using type 'optional-str'
+INFO /spec/spec-refinement:/spec-type: verify using type 'name'
+INFO /spec/spec-str: verify using type 'root'
+INFO /spec/spec-str:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec/spec-str:/copyrights: verify using type 'copyrights'
+INFO /spec/spec-str:/copyrights[0]: verify using type 'copyright'
+INFO /spec/spec-str:/enabled-by: verify using type 'enabled-by'
+INFO /spec/spec-str:/links: verify using type 'links'
+INFO /spec/spec-str:/links[0]: verify using type 'link'
+INFO /spec/spec-str:/links[0]/role: verify using type 'name'
+INFO /spec/spec-str:/links[0]/uid: verify using type 'uid'
+INFO /spec/spec-str:/links[0]: verify using type 'spec-member'
+INFO /spec/spec-str:/type: verify using type 'name'
+INFO /spec/spec-str: verify using type 'spec'
+INFO /spec/spec-str:/spec-description: verify using type 'optional-str'
+INFO /spec/spec-str:/spec-example: verify using type 'optional-str'
+INFO /spec/spec-str:/spec-info: verify using type 'spec-info'
+INFO /spec/spec-str:/spec-info/dict: verify using type 'spec-dict'
+INFO /spec/spec-str:/spec-info/dict/attributes: verify using type 'spec-attributes'
+INFO /spec/spec-str:/spec-info/dict/attributes/assert: verify using type 'name'
+INFO /spec/spec-str:/spec-info/dict/attributes/assert: verify using type 'spec-attribute-value'
+INFO /spec/spec-str:/spec-info/dict/attributes/assert/description: verify using type 'optional-str'
+INFO /spec/spec-str:/spec-info/dict/attributes/assert/spec-type: verify using type 'name'
+INFO /spec/spec-str:/spec-info/dict/attributes/description: verify using type 'name'
+INFO /spec/spec-str:/spec-info/dict/attributes/description: verify using type 'spec-attribute-value'
+INFO /spec/spec-str:/spec-info/dict/attributes/description/description: verify using type 'optional-str'
+INFO /spec/spec-str:/spec-info/dict/attributes/description/spec-type: verify using type 'name'
+INFO /spec/spec-str:/spec-info/dict/description: verify using type 'optional-str'
+INFO /spec/spec-str:/spec-info/dict/mandatory-attributes: verify using type 'spec-mandatory-attributes'
+INFO /spec/spec-str:/spec-info/dict/mandatory-attributes[0]: verify using type 'name'
+INFO /spec/spec-str:/spec-name: verify using type 'optional-str'
+INFO /spec/spec-str:/spec-type: verify using type 'name'
+INFO /spec2/any-dict: verify using type 'root'
+INFO /spec2/any-dict:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec2/any-dict:/copyrights: verify using type 'copyrights'
+INFO /spec2/any-dict:/copyrights[0]: verify using type 'copyright'
+INFO /spec2/any-dict:/enabled-by: verify using type 'enabled-by'
+INFO /spec2/any-dict:/links: verify using type 'links'
+INFO /spec2/any-dict:/links[0]: verify using type 'link'
+INFO /spec2/any-dict:/links[0]/role: verify using type 'name'
+INFO /spec2/any-dict:/links[0]/uid: verify using type 'uid'
+INFO /spec2/any-dict:/links[0]: verify using type 'spec-member'
+INFO /spec2/any-dict:/type: verify using type 'name'
+INFO /spec2/any-dict: verify using type 'spec'
+ERROR /spec2/any-dict: missing mandatory keys for type 'spec': ['spec-description', 'spec-name']
+INFO /spec2/any-dict:/spec-example: verify using type 'optional-str'
+INFO /spec2/any-dict:/spec-info: verify using type 'spec-info'
+INFO /spec2/any-dict:/spec-info/dict: verify using type 'spec-dict'
+ERROR /spec2/any-dict:/spec-info/dict: missing mandatory keys for type 'spec-dict': ['description']
+INFO /spec2/any-dict:/spec-info/dict/attributes: verify using type 'spec-attributes'
+INFO /spec2/any-dict:/spec-info/dict/generic-attributes: verify using type 'spec-generic-attributes'
+ERROR /spec2/any-dict:/spec-info/dict/generic-attributes: missing mandatory keys for type 'spec-generic-attributes': ['description']
+INFO /spec2/any-dict:/spec-info/dict/generic-attributes/key-spec-type: verify using type 'name'
+INFO /spec2/any-dict:/spec-info/dict/generic-attributes/value-spec-type: verify using type 'name'
+INFO /spec2/any-dict:/spec-info/dict/mandatory-attributes: verify using type 'spec-mandatory-attributes'
+INFO /spec2/any-dict:/spec-type: verify using type 'name'
+INFO /spec2/c: verify using type 'root'
+INFO /spec2/c:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec2/c:/copyrights: verify using type 'copyrights'
+INFO /spec2/c:/copyrights[0]: verify using type 'copyright'
+INFO /spec2/c:/enabled-by: verify using type 'enabled-by'
+INFO /spec2/c:/links: verify using type 'links'
+INFO /spec2/c:/links[0]: verify using type 'link'
+INFO /spec2/c:/links[0]/role: verify using type 'name'
+INFO /spec2/c:/links[0]/uid: verify using type 'uid'
+INFO /spec2/c:/links[0]: verify using type 'spec-member'
+INFO /spec2/c:/links[1]: verify using type 'link'
+INFO /spec2/c:/links[1]/role: verify using type 'name'
+INFO /spec2/c:/links[1]/uid: verify using type 'uid'
+INFO /spec2/c:/links[1]: verify using type 'spec-refinement'
+INFO /spec2/c:/links[1]/spec-key: verify using type 'name'
+INFO /spec2/c:/links[1]/spec-value: verify using type 'name'
+INFO /spec2/c:/type: verify using type 'name'
+INFO /spec2/c: verify using type 'spec'
+ERROR /spec2/c: missing mandatory keys for type 'spec': ['spec-description', 'spec-name']
+INFO /spec2/c:/spec-example: verify using type 'optional-str'
+INFO /spec2/c:/spec-info: verify using type 'spec-info'
+INFO /spec2/c:/spec-info/dict: verify using type 'spec-dict'
+ERROR /spec2/c:/spec-info/dict: missing mandatory keys for type 'spec-dict': ['description']
+INFO /spec2/c:/spec-info/dict/attributes: verify using type 'spec-attributes'
+INFO /spec2/c:/spec-info/dict/attributes/any-dict: verify using type 'name'
+INFO /spec2/c:/spec-info/dict/attributes/any-dict: verify using type 'spec-attribute-value'
+ERROR /spec2/c:/spec-info/dict/attributes/any-dict: missing mandatory keys for type 'spec-attribute-value': ['description']
+INFO /spec2/c:/spec-info/dict/attributes/any-dict/spec-type: verify using type 'name'
+INFO /spec2/c:/spec-info/dict/attributes/bool: verify using type 'name'
+INFO /spec2/c:/spec-info/dict/attributes/bool: verify using type 'spec-attribute-value'
+ERROR /spec2/c:/spec-info/dict/attributes/bool: missing mandatory keys for type 'spec-attribute-value': ['description']
+INFO /spec2/c:/spec-info/dict/attributes/bool/spec-type: verify using type 'name'
+INFO /spec2/c:/spec-info/dict/attributes/dict: verify using type 'name'
+INFO /spec2/c:/spec-info/dict/attributes/dict: verify using type 'spec-attribute-value'
+ERROR /spec2/c:/spec-info/dict/attributes/dict: missing mandatory keys for type 'spec-attribute-value': ['description']
+INFO /spec2/c:/spec-info/dict/attributes/dict/spec-type: verify using type 'name'
+INFO /spec2/c:/spec-info/dict/attributes/float: verify using type 'name'
+INFO /spec2/c:/spec-info/dict/attributes/float: verify using type 'spec-attribute-value'
+ERROR /spec2/c:/spec-info/dict/attributes/float: missing mandatory keys for type 'spec-attribute-value': ['description']
+INFO /spec2/c:/spec-info/dict/attributes/float/spec-type: verify using type 'name'
+INFO /spec2/c:/spec-info/dict/attributes/int: verify using type 'name'
+INFO /spec2/c:/spec-info/dict/attributes/int: verify using type 'spec-attribute-value'
+ERROR /spec2/c:/spec-info/dict/attributes/int: missing mandatory keys for type 'spec-attribute-value': ['description']
+INFO /spec2/c:/spec-info/dict/attributes/int/spec-type: verify using type 'name'
+INFO /spec2/c:/spec-info/dict/attributes/list: verify using type 'name'
+INFO /spec2/c:/spec-info/dict/attributes/list: verify using type 'spec-attribute-value'
+ERROR /spec2/c:/spec-info/dict/attributes/list: missing mandatory keys for type 'spec-attribute-value': ['description']
+INFO /spec2/c:/spec-info/dict/attributes/list/spec-type: verify using type 'name'
+INFO /spec2/c:/spec-info/dict/attributes/must-be-true: verify using type 'name'
+INFO /spec2/c:/spec-info/dict/attributes/must-be-true: verify using type 'spec-attribute-value'
+ERROR /spec2/c:/spec-info/dict/attributes/must-be-true: missing mandatory keys for type 'spec-attribute-value': ['description']
+INFO /spec2/c:/spec-info/dict/attributes/must-be-true/spec-type: verify using type 'name'
+INFO /spec2/c:/spec-info/dict/attributes/other-int: verify using type 'name'
+INFO /spec2/c:/spec-info/dict/attributes/other-int: verify using type 'spec-attribute-value'
+ERROR /spec2/c:/spec-info/dict/attributes/other-int: missing mandatory keys for type 'spec-attribute-value': ['description']
+INFO /spec2/c:/spec-info/dict/attributes/other-int/spec-type: verify using type 'name'
+INFO /spec2/c:/spec-info/dict/attributes/str: verify using type 'name'
+INFO /spec2/c:/spec-info/dict/attributes/str: verify using type 'spec-attribute-value'
+ERROR /spec2/c:/spec-info/dict/attributes/str: missing mandatory keys for type 'spec-attribute-value': ['description']
+INFO /spec2/c:/spec-info/dict/attributes/str/spec-type: verify using type 'name'
+INFO /spec2/c:/spec-info/dict/attributes/str-contains: verify using type 'name'
+INFO /spec2/c:/spec-info/dict/attributes/str-contains: verify using type 'spec-attribute-value'
+ERROR /spec2/c:/spec-info/dict/attributes/str-contains: missing mandatory keys for type 'spec-attribute-value': ['description']
+INFO /spec2/c:/spec-info/dict/attributes/str-contains/spec-type: verify using type 'name'
+INFO /spec2/c:/spec-info/dict/attributes/uid: verify using type 'name'
+INFO /spec2/c:/spec-info/dict/attributes/uid: verify using type 'spec-attribute-value'
+ERROR /spec2/c:/spec-info/dict/attributes/uid: missing mandatory keys for type 'spec-attribute-value': ['description']
+INFO /spec2/c:/spec-info/dict/attributes/uid/spec-type: verify using type 'name'
+INFO /spec2/c:/spec-info/dict/mandatory-attributes: verify using type 'spec-mandatory-attributes'
+INFO /spec2/c:/spec-type: verify using type 'name'
+INFO /spec2/d: verify using type 'root'
+INFO /spec2/d:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec2/d:/copyrights: verify using type 'copyrights'
+INFO /spec2/d:/copyrights[0]: verify using type 'copyright'
+INFO /spec2/d:/enabled-by: verify using type 'enabled-by'
+INFO /spec2/d:/links: verify using type 'links'
+INFO /spec2/d:/links[0]: verify using type 'link'
+INFO /spec2/d:/links[0]/role: verify using type 'name'
+INFO /spec2/d:/links[0]/uid: verify using type 'uid'
+INFO /spec2/d:/links[0]: verify using type 'spec-member'
+INFO /spec2/d:/links[1]: verify using type 'link'
+INFO /spec2/d:/links[1]/role: verify using type 'name'
+INFO /spec2/d:/links[1]/uid: verify using type 'uid'
+INFO /spec2/d:/links[1]: verify using type 'spec-refinement'
+INFO /spec2/d:/links[1]/spec-key: verify using type 'name'
+INFO /spec2/d:/links[1]/spec-value: verify using type 'name'
+INFO /spec2/d:/type: verify using type 'name'
+INFO /spec2/d: verify using type 'spec'
+ERROR /spec2/d: missing mandatory keys for type 'spec': ['spec-description', 'spec-name']
+INFO /spec2/d:/spec-example: verify using type 'optional-str'
+INFO /spec2/d:/spec-info: verify using type 'spec-info'
+INFO /spec2/d:/spec-info/dict: verify using type 'spec-dict'
+ERROR /spec2/d:/spec-info/dict: missing mandatory keys for type 'spec-dict': ['description']
+INFO /spec2/d:/spec-info/dict/attributes: verify using type 'spec-attributes'
+INFO /spec2/d:/spec-info/dict/attributes/d-type: verify using type 'name'
+INFO /spec2/d:/spec-info/dict/attributes/d-type: verify using type 'spec-attribute-value'
+ERROR /spec2/d:/spec-info/dict/attributes/d-type: missing mandatory keys for type 'spec-attribute-value': ['description']
+INFO /spec2/d:/spec-info/dict/attributes/d-type/spec-type: verify using type 'name'
+INFO /spec2/d:/spec-info/dict/mandatory-attributes: verify using type 'spec-mandatory-attributes'
+INFO /spec2/d:/spec-info/dict/mandatory-attributes[0]: verify using type 'name'
+INFO /spec2/d:/spec-type: verify using type 'name'
+INFO /spec2/d-a: verify using type 'root'
+INFO /spec2/d-a:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec2/d-a:/copyrights: verify using type 'copyrights'
+INFO /spec2/d-a:/copyrights[0]: verify using type 'copyright'
+INFO /spec2/d-a:/enabled-by: verify using type 'enabled-by'
+INFO /spec2/d-a:/links: verify using type 'links'
+INFO /spec2/d-a:/links[0]: verify using type 'link'
+INFO /spec2/d-a:/links[0]/role: verify using type 'name'
+INFO /spec2/d-a:/links[0]/uid: verify using type 'uid'
+INFO /spec2/d-a:/links[0]: verify using type 'spec-member'
+INFO /spec2/d-a:/links[1]: verify using type 'link'
+INFO /spec2/d-a:/links[1]/role: verify using type 'name'
+INFO /spec2/d-a:/links[1]/uid: verify using type 'uid'
+INFO /spec2/d-a:/links[1]: verify using type 'spec-refinement'
+INFO /spec2/d-a:/links[1]/spec-key: verify using type 'name'
+INFO /spec2/d-a:/links[1]/spec-value: verify using type 'name'
+INFO /spec2/d-a:/links[2]: verify using type 'link'
+INFO /spec2/d-a:/links[2]/role: verify using type 'name'
+INFO /spec2/d-a:/links[2]/uid: verify using type 'uid'
+ERROR /spec2/d-a:/links[2]: unknown subtype for key 'role' for type 'link': other
+INFO /spec2/d-a:/type: verify using type 'name'
+INFO /spec2/d-a: verify using type 'spec'
+ERROR /spec2/d-a: missing mandatory keys for type 'spec': ['spec-description', 'spec-name']
+INFO /spec2/d-a:/spec-example: verify using type 'optional-str'
+INFO /spec2/d-a:/spec-info: verify using type 'spec-info'
+INFO /spec2/d-a:/spec-info/dict: verify using type 'spec-dict'
+ERROR /spec2/d-a:/spec-info/dict: missing mandatory keys for type 'spec-dict': ['description']
+INFO /spec2/d-a:/spec-info/dict/attributes: verify using type 'spec-attributes'
+INFO /spec2/d-a:/spec-info/dict/attributes/d-type: verify using type 'name'
+INFO /spec2/d-a:/spec-info/dict/attributes/d-type: verify using type 'spec-attribute-value'
+ERROR /spec2/d-a:/spec-info/dict/attributes/d-type: missing mandatory keys for type 'spec-attribute-value': ['description']
+INFO /spec2/d-a:/spec-info/dict/attributes/d-type/spec-type: verify using type 'name'
+INFO /spec2/d-a:/spec-info/dict/mandatory-attributes: verify using type 'spec-mandatory-attributes'
+INFO /spec2/d-a:/spec-type: verify using type 'name'
+INFO /spec2/invalid: verify using type 'root'
+INFO /spec2/invalid:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec2/invalid:/copyrights: verify using type 'copyrights'
+INFO /spec2/invalid:/copyrights[0]: verify using type 'copyright'
+INFO /spec2/invalid:/enabled-by: verify using type 'enabled-by'
+INFO /spec2/invalid:/links: verify using type 'links'
+INFO /spec2/invalid:/links[0]: verify using type 'link'
+INFO /spec2/invalid:/links[0]/role: verify using type 'name'
+INFO /spec2/invalid:/links[0]/uid: verify using type 'uid'
+INFO /spec2/invalid:/links[0]: verify using type 'spec-member'
+INFO /spec2/invalid:/links[1]: verify using type 'link'
+INFO /spec2/invalid:/links[1]/role: verify using type 'name'
+INFO /spec2/invalid:/links[1]/uid: verify using type 'uid'
+INFO /spec2/invalid:/links[1]: verify using type 'spec-refinement'
+INFO /spec2/invalid:/links[1]/spec-key: verify using type 'name'
+INFO /spec2/invalid:/links[1]/spec-value: verify using type 'name'
+INFO /spec2/invalid:/type: verify using type 'name'
+INFO /spec2/invalid: verify using type 'spec'
+ERROR /spec2/invalid: missing mandatory keys for type 'spec': ['spec-description', 'spec-name']
+INFO /spec2/invalid:/spec-example: verify using type 'optional-str'
+INFO /spec2/invalid:/spec-info: verify using type 'spec-info'
+INFO /spec2/invalid:/spec-info/dict: verify using type 'spec-dict'
+ERROR /spec2/invalid:/spec-info/dict: missing mandatory keys for type 'spec-dict': ['description']
+INFO /spec2/invalid:/spec-info/dict/attributes: verify using type 'spec-attributes'
+INFO /spec2/invalid:/spec-info/dict/attributes/INVALID: verify using type 'name'
+ERROR /spec2/invalid:/spec-info/dict/attributes/INVALID: invalid name: INVALID
+INFO /spec2/invalid:/spec-info/dict/attributes/INVALID: verify using type 'spec-attribute-value'
+ERROR /spec2/invalid:/spec-info/dict/attributes/INVALID: missing mandatory keys for type 'spec-attribute-value': ['description']
+INFO /spec2/invalid:/spec-info/dict/attributes/INVALID/spec-type: verify using type 'name'
+INFO /spec2/invalid:/spec-info/dict/mandatory-attributes: verify using type 'spec-mandatory-attributes'
+INFO /spec2/invalid:/spec-info/none: verify using type 'none'
+ERROR /spec2/invalid:/spec-info/none: expected type 'none', actual type 'bool'
+INFO /spec2/invalid:/spec-info/str: verify using type 'spec-str'
+ERROR /spec2/invalid:/spec-info/str: missing mandatory keys for type 'spec-str': ['description']
+ERROR /spec2/invalid:/spec-info/str: has unverfied keys for type 'spec-str' and its subtypes: ['foo']
+ERROR /spec2/invalid:/spec-info: has unverfied keys for type 'spec-info' and its subtypes: ['unexpected']
+INFO /spec2/invalid:/spec-type: verify using type 'name'
+INFO /spec2/keys-at-least-one: verify using type 'root'
+INFO /spec2/keys-at-least-one:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec2/keys-at-least-one:/copyrights: verify using type 'copyrights'
+INFO /spec2/keys-at-least-one:/copyrights[0]: verify using type 'copyright'
+INFO /spec2/keys-at-least-one:/enabled-by: verify using type 'enabled-by'
+INFO /spec2/keys-at-least-one:/links: verify using type 'links'
+INFO /spec2/keys-at-least-one:/links[0]: verify using type 'link'
+INFO /spec2/keys-at-least-one:/links[0]/role: verify using type 'name'
+INFO /spec2/keys-at-least-one:/links[0]/uid: verify using type 'uid'
+INFO /spec2/keys-at-least-one:/links[0]: verify using type 'spec-member'
+INFO /spec2/keys-at-least-one:/links[1]: verify using type 'link'
+INFO /spec2/keys-at-least-one:/links[1]/role: verify using type 'name'
+INFO /spec2/keys-at-least-one:/links[1]/uid: verify using type 'uid'
+INFO /spec2/keys-at-least-one:/links[1]: verify using type 'spec-refinement'
+INFO /spec2/keys-at-least-one:/links[1]/spec-key: verify using type 'name'
+INFO /spec2/keys-at-least-one:/links[1]/spec-value: verify using type 'name'
+INFO /spec2/keys-at-least-one:/type: verify using type 'name'
+INFO /spec2/keys-at-least-one: verify using type 'spec'
+ERROR /spec2/keys-at-least-one: missing mandatory keys for type 'spec': ['spec-description', 'spec-name']
+INFO /spec2/keys-at-least-one:/spec-example: verify using type 'optional-str'
+INFO /spec2/keys-at-least-one:/spec-info: verify using type 'spec-info'
+INFO /spec2/keys-at-least-one:/spec-info/dict: verify using type 'spec-dict'
+ERROR /spec2/keys-at-least-one:/spec-info/dict: missing mandatory keys for type 'spec-dict': ['description']
+INFO /spec2/keys-at-least-one:/spec-info/dict/attributes: verify using type 'spec-attributes'
+INFO /spec2/keys-at-least-one:/spec-info/dict/attributes/key-a: verify using type 'name'
+INFO /spec2/keys-at-least-one:/spec-info/dict/attributes/key-a: verify using type 'spec-attribute-value'
+ERROR /spec2/keys-at-least-one:/spec-info/dict/attributes/key-a: missing mandatory keys for type 'spec-attribute-value': ['description']
+INFO /spec2/keys-at-least-one:/spec-info/dict/attributes/key-a/spec-type: verify using type 'name'
+INFO /spec2/keys-at-least-one:/spec-info/dict/attributes/key-b: verify using type 'name'
+INFO /spec2/keys-at-least-one:/spec-info/dict/attributes/key-b: verify using type 'spec-attribute-value'
+ERROR /spec2/keys-at-least-one:/spec-info/dict/attributes/key-b: missing mandatory keys for type 'spec-attribute-value': ['description']
+INFO /spec2/keys-at-least-one:/spec-info/dict/attributes/key-b/spec-type: verify using type 'name'
+INFO /spec2/keys-at-least-one:/spec-info/dict/mandatory-attributes: verify using type 'spec-mandatory-attributes'
+INFO /spec2/keys-at-least-one:/spec-type: verify using type 'name'
+INFO /spec2/keys-at-most-one: verify using type 'root'
+INFO /spec2/keys-at-most-one:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec2/keys-at-most-one:/copyrights: verify using type 'copyrights'
+INFO /spec2/keys-at-most-one:/copyrights[0]: verify using type 'copyright'
+INFO /spec2/keys-at-most-one:/enabled-by: verify using type 'enabled-by'
+INFO /spec2/keys-at-most-one:/links: verify using type 'links'
+INFO /spec2/keys-at-most-one:/links[0]: verify using type 'link'
+INFO /spec2/keys-at-most-one:/links[0]/role: verify using type 'name'
+INFO /spec2/keys-at-most-one:/links[0]/uid: verify using type 'uid'
+INFO /spec2/keys-at-most-one:/links[0]: verify using type 'spec-member'
+INFO /spec2/keys-at-most-one:/links[1]: verify using type 'link'
+INFO /spec2/keys-at-most-one:/links[1]/role: verify using type 'name'
+INFO /spec2/keys-at-most-one:/links[1]/uid: verify using type 'uid'
+INFO /spec2/keys-at-most-one:/links[1]: verify using type 'spec-refinement'
+INFO /spec2/keys-at-most-one:/links[1]/spec-key: verify using type 'name'
+INFO /spec2/keys-at-most-one:/links[1]/spec-value: verify using type 'name'
+INFO /spec2/keys-at-most-one:/type: verify using type 'name'
+INFO /spec2/keys-at-most-one: verify using type 'spec'
+ERROR /spec2/keys-at-most-one: missing mandatory keys for type 'spec': ['spec-description', 'spec-name']
+INFO /spec2/keys-at-most-one:/spec-example: verify using type 'optional-str'
+INFO /spec2/keys-at-most-one:/spec-info: verify using type 'spec-info'
+INFO /spec2/keys-at-most-one:/spec-info/dict: verify using type 'spec-dict'
+ERROR /spec2/keys-at-most-one:/spec-info/dict: missing mandatory keys for type 'spec-dict': ['description']
+INFO /spec2/keys-at-most-one:/spec-info/dict/attributes: verify using type 'spec-attributes'
+INFO /spec2/keys-at-most-one:/spec-info/dict/attributes/key-a: verify using type 'name'
+INFO /spec2/keys-at-most-one:/spec-info/dict/attributes/key-a: verify using type 'spec-attribute-value'
+ERROR /spec2/keys-at-most-one:/spec-info/dict/attributes/key-a: missing mandatory keys for type 'spec-attribute-value': ['description']
+INFO /spec2/keys-at-most-one:/spec-info/dict/attributes/key-a/spec-type: verify using type 'name'
+INFO /spec2/keys-at-most-one:/spec-info/dict/attributes/key-b: verify using type 'name'
+INFO /spec2/keys-at-most-one:/spec-info/dict/attributes/key-b: verify using type 'spec-attribute-value'
+ERROR /spec2/keys-at-most-one:/spec-info/dict/attributes/key-b: missing mandatory keys for type 'spec-attribute-value': ['description']
+INFO /spec2/keys-at-most-one:/spec-info/dict/attributes/key-b/spec-type: verify using type 'name'
+INFO /spec2/keys-at-most-one:/spec-info/dict/mandatory-attributes: verify using type 'spec-mandatory-attributes'
+INFO /spec2/keys-at-most-one:/spec-type: verify using type 'name'
+INFO /spec2/keys-exactly-one: verify using type 'root'
+INFO /spec2/keys-exactly-one:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec2/keys-exactly-one:/copyrights: verify using type 'copyrights'
+INFO /spec2/keys-exactly-one:/copyrights[0]: verify using type 'copyright'
+INFO /spec2/keys-exactly-one:/enabled-by: verify using type 'enabled-by'
+INFO /spec2/keys-exactly-one:/links: verify using type 'links'
+INFO /spec2/keys-exactly-one:/links[0]: verify using type 'link'
+INFO /spec2/keys-exactly-one:/links[0]/role: verify using type 'name'
+INFO /spec2/keys-exactly-one:/links[0]/uid: verify using type 'uid'
+INFO /spec2/keys-exactly-one:/links[0]: verify using type 'spec-member'
+INFO /spec2/keys-exactly-one:/links[1]: verify using type 'link'
+INFO /spec2/keys-exactly-one:/links[1]/role: verify using type 'name'
+INFO /spec2/keys-exactly-one:/links[1]/uid: verify using type 'uid'
+INFO /spec2/keys-exactly-one:/links[1]: verify using type 'spec-refinement'
+INFO /spec2/keys-exactly-one:/links[1]/spec-key: verify using type 'name'
+INFO /spec2/keys-exactly-one:/links[1]/spec-value: verify using type 'name'
+INFO /spec2/keys-exactly-one:/type: verify using type 'name'
+INFO /spec2/keys-exactly-one: verify using type 'spec'
+ERROR /spec2/keys-exactly-one: missing mandatory keys for type 'spec': ['spec-description', 'spec-name']
+INFO /spec2/keys-exactly-one:/spec-example: verify using type 'optional-str'
+INFO /spec2/keys-exactly-one:/spec-info: verify using type 'spec-info'
+INFO /spec2/keys-exactly-one:/spec-info/dict: verify using type 'spec-dict'
+ERROR /spec2/keys-exactly-one:/spec-info/dict: missing mandatory keys for type 'spec-dict': ['description']
+INFO /spec2/keys-exactly-one:/spec-info/dict/attributes: verify using type 'spec-attributes'
+INFO /spec2/keys-exactly-one:/spec-info/dict/attributes/key-a: verify using type 'name'
+INFO /spec2/keys-exactly-one:/spec-info/dict/attributes/key-a: verify using type 'spec-attribute-value'
+ERROR /spec2/keys-exactly-one:/spec-info/dict/attributes/key-a: missing mandatory keys for type 'spec-attribute-value': ['description']
+INFO /spec2/keys-exactly-one:/spec-info/dict/attributes/key-a/spec-type: verify using type 'name'
+INFO /spec2/keys-exactly-one:/spec-info/dict/attributes/key-b: verify using type 'name'
+INFO /spec2/keys-exactly-one:/spec-info/dict/attributes/key-b: verify using type 'spec-attribute-value'
+ERROR /spec2/keys-exactly-one:/spec-info/dict/attributes/key-b: missing mandatory keys for type 'spec-attribute-value': ['description']
+INFO /spec2/keys-exactly-one:/spec-info/dict/attributes/key-b/spec-type: verify using type 'name'
+INFO /spec2/keys-exactly-one:/spec-info/dict/mandatory-attributes: verify using type 'spec-mandatory-attributes'
+INFO /spec2/keys-exactly-one:/spec-type: verify using type 'name'
+INFO /spec2/must-be-true: verify using type 'root'
+INFO /spec2/must-be-true:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec2/must-be-true:/copyrights: verify using type 'copyrights'
+INFO /spec2/must-be-true:/copyrights[0]: verify using type 'copyright'
+INFO /spec2/must-be-true:/enabled-by: verify using type 'enabled-by'
+INFO /spec2/must-be-true:/links: verify using type 'links'
+INFO /spec2/must-be-true:/links[0]: verify using type 'link'
+INFO /spec2/must-be-true:/links[0]/role: verify using type 'name'
+INFO /spec2/must-be-true:/links[0]/uid: verify using type 'uid'
+INFO /spec2/must-be-true:/links[0]: verify using type 'spec-member'
+INFO /spec2/must-be-true:/type: verify using type 'name'
+INFO /spec2/must-be-true: verify using type 'spec'
+ERROR /spec2/must-be-true: missing mandatory keys for type 'spec': ['spec-description', 'spec-name']
+INFO /spec2/must-be-true:/spec-example: verify using type 'optional-str'
+INFO /spec2/must-be-true:/spec-info: verify using type 'spec-info'
+INFO /spec2/must-be-true:/spec-info/bool: verify using type 'spec-bool'
+ERROR /spec2/must-be-true:/spec-info/bool: missing mandatory keys for type 'spec-bool': ['description']
+INFO /spec2/must-be-true:/spec-info/bool/assert: verify using type 'bool'
+INFO /spec2/must-be-true:/spec-type: verify using type 'name'
+INFO /spec2/other-int: verify using type 'root'
+INFO /spec2/other-int:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec2/other-int:/copyrights: verify using type 'copyrights'
+INFO /spec2/other-int:/copyrights[0]: verify using type 'copyright'
+INFO /spec2/other-int:/enabled-by: verify using type 'enabled-by'
+INFO /spec2/other-int:/links: verify using type 'links'
+INFO /spec2/other-int:/links[0]: verify using type 'link'
+INFO /spec2/other-int:/links[0]/role: verify using type 'name'
+INFO /spec2/other-int:/links[0]/uid: verify using type 'uid'
+INFO /spec2/other-int:/links[0]: verify using type 'spec-member'
+INFO /spec2/other-int:/type: verify using type 'name'
+INFO /spec2/other-int: verify using type 'spec'
+ERROR /spec2/other-int: missing mandatory keys for type 'spec': ['spec-description', 'spec-name']
+INFO /spec2/other-int:/spec-example: verify using type 'optional-str'
+INFO /spec2/other-int:/spec-info: verify using type 'spec-info'
+INFO /spec2/other-int:/spec-info/int: verify using type 'spec-int'
+ERROR /spec2/other-int:/spec-info/int: missing mandatory keys for type 'spec-int': ['description']
+INFO /spec2/other-int:/spec-type: verify using type 'name'
+INFO /spec2/some-bool: verify using type 'root'
+INFO /spec2/some-bool:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec2/some-bool:/copyrights: verify using type 'copyrights'
+INFO /spec2/some-bool:/copyrights[0]: verify using type 'copyright'
+INFO /spec2/some-bool:/enabled-by: verify using type 'enabled-by'
+INFO /spec2/some-bool:/links: verify using type 'links'
+INFO /spec2/some-bool:/links[0]: verify using type 'link'
+INFO /spec2/some-bool:/links[0]/role: verify using type 'name'
+INFO /spec2/some-bool:/links[0]/uid: verify using type 'uid'
+INFO /spec2/some-bool:/links[0]: verify using type 'spec-member'
+INFO /spec2/some-bool:/type: verify using type 'name'
+INFO /spec2/some-bool: verify using type 'spec'
+ERROR /spec2/some-bool: missing mandatory keys for type 'spec': ['spec-description', 'spec-name']
+INFO /spec2/some-bool:/spec-example: verify using type 'optional-str'
+INFO /spec2/some-bool:/spec-info: verify using type 'spec-info'
+INFO /spec2/some-bool:/spec-info/bool: verify using type 'spec-bool'
+ERROR /spec2/some-bool:/spec-info/bool: missing mandatory keys for type 'spec-bool': ['description']
+INFO /spec2/some-bool:/spec-type: verify using type 'name'
+INFO /spec2/some-dict: verify using type 'root'
+INFO /spec2/some-dict:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec2/some-dict:/copyrights: verify using type 'copyrights'
+INFO /spec2/some-dict:/copyrights[0]: verify using type 'copyright'
+INFO /spec2/some-dict:/enabled-by: verify using type 'enabled-by'
+INFO /spec2/some-dict:/links: verify using type 'links'
+INFO /spec2/some-dict:/links[0]: verify using type 'link'
+INFO /spec2/some-dict:/links[0]/role: verify using type 'name'
+INFO /spec2/some-dict:/links[0]/uid: verify using type 'uid'
+INFO /spec2/some-dict:/links[0]: verify using type 'spec-member'
+INFO /spec2/some-dict:/type: verify using type 'name'
+INFO /spec2/some-dict: verify using type 'spec'
+ERROR /spec2/some-dict: missing mandatory keys for type 'spec': ['spec-description', 'spec-name']
+INFO /spec2/some-dict:/spec-example: verify using type 'optional-str'
+INFO /spec2/some-dict:/spec-info: verify using type 'spec-info'
+INFO /spec2/some-dict:/spec-info/dict: verify using type 'spec-dict'
+ERROR /spec2/some-dict:/spec-info/dict: missing mandatory keys for type 'spec-dict': ['description']
+INFO /spec2/some-dict:/spec-info/dict/attributes: verify using type 'spec-attributes'
+INFO /spec2/some-dict:/spec-info/dict/attributes/a: verify using type 'name'
+INFO /spec2/some-dict:/spec-info/dict/attributes/a: verify using type 'spec-attribute-value'
+ERROR /spec2/some-dict:/spec-info/dict/attributes/a: missing mandatory keys for type 'spec-attribute-value': ['description']
+INFO /spec2/some-dict:/spec-info/dict/attributes/a/spec-type: verify using type 'name'
+INFO /spec2/some-dict:/spec-info/dict/mandatory-attributes: verify using type 'spec-mandatory-attributes'
+INFO /spec2/some-dict:/spec-info/none: verify using type 'none'
+INFO /spec2/some-dict:/spec-type: verify using type 'name'
+INFO /spec2/some-float: verify using type 'root'
+INFO /spec2/some-float:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec2/some-float:/copyrights: verify using type 'copyrights'
+INFO /spec2/some-float:/copyrights[0]: verify using type 'copyright'
+INFO /spec2/some-float:/enabled-by: verify using type 'enabled-by'
+INFO /spec2/some-float:/links: verify using type 'links'
+INFO /spec2/some-float:/links[0]: verify using type 'link'
+INFO /spec2/some-float:/links[0]/role: verify using type 'name'
+INFO /spec2/some-float:/links[0]/uid: verify using type 'uid'
+INFO /spec2/some-float:/links[0]: verify using type 'spec-member'
+INFO /spec2/some-float:/type: verify using type 'name'
+INFO /spec2/some-float: verify using type 'spec'
+ERROR /spec2/some-float: missing mandatory keys for type 'spec': ['spec-description', 'spec-name']
+INFO /spec2/some-float:/spec-example: verify using type 'optional-str'
+INFO /spec2/some-float:/spec-info: verify using type 'spec-info'
+INFO /spec2/some-float:/spec-info/float: verify using type 'spec-float'
+ERROR /spec2/some-float:/spec-info/float: missing mandatory keys for type 'spec-float': ['description']
+INFO /spec2/some-float:/spec-info/float/assert: verify using type 'spec-assert-float'
+INFO /spec2/some-float:/spec-info/float/assert[0]: verify using type 'spec-assert-float'
+INFO /spec2/some-float:/spec-info/float/assert[0]/le: verify using type 'float'
+INFO /spec2/some-float:/spec-type: verify using type 'name'
+INFO /spec2/some-int: verify using type 'root'
+INFO /spec2/some-int:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec2/some-int:/copyrights: verify using type 'copyrights'
+INFO /spec2/some-int:/copyrights[0]: verify using type 'copyright'
+INFO /spec2/some-int:/enabled-by: verify using type 'enabled-by'
+INFO /spec2/some-int:/links: verify using type 'links'
+INFO /spec2/some-int:/links[0]: verify using type 'link'
+INFO /spec2/some-int:/links[0]/role: verify using type 'name'
+INFO /spec2/some-int:/links[0]/uid: verify using type 'uid'
+INFO /spec2/some-int:/links[0]: verify using type 'spec-member'
+INFO /spec2/some-int:/type: verify using type 'name'
+INFO /spec2/some-int: verify using type 'spec'
+ERROR /spec2/some-int: missing mandatory keys for type 'spec': ['spec-description', 'spec-name']
+INFO /spec2/some-int:/spec-example: verify using type 'optional-str'
+INFO /spec2/some-int:/spec-info: verify using type 'spec-info'
+INFO /spec2/some-int:/spec-info/int: verify using type 'spec-int'
+ERROR /spec2/some-int:/spec-info/int: missing mandatory keys for type 'spec-int': ['description']
+INFO /spec2/some-int:/spec-info/int/assert: verify using type 'spec-assert-int'
+INFO /spec2/some-int:/spec-info/int/assert[0]: verify using type 'spec-assert-int'
+INFO /spec2/some-int:/spec-info/int/assert[0]/and: verify using type 'spec-assert-int-list'
+INFO /spec2/some-int:/spec-info/int/assert[0]/and[0]: verify using type 'spec-assert-int'
+INFO /spec2/some-int:/spec-info/int/assert[0]/and[0]/not: verify using type 'spec-assert-int'
+INFO /spec2/some-int:/spec-info/int/assert[0]/and[0]/not/eq: verify using type 'int'
+INFO /spec2/some-int:/spec-info/int/assert[0]/and[1]: verify using type 'spec-assert-int'
+INFO /spec2/some-int:/spec-info/int/assert[0]/and[1]/eq: verify using type 'int'
+INFO /spec2/some-int:/spec-info/int/assert[1]: verify using type 'spec-assert-int'
+INFO /spec2/some-int:/spec-info/int/assert[1]/and: verify using type 'spec-assert-int-list'
+INFO /spec2/some-int:/spec-info/int/assert[1]/and[0]: verify using type 'spec-assert-int'
+INFO /spec2/some-int:/spec-info/int/assert[1]/and[0]/eq: verify using type 'int'
+INFO /spec2/some-int:/spec-info/int/assert[1]/and[1]: verify using type 'spec-assert-int'
+INFO /spec2/some-int:/spec-info/int/assert[1]/and[1]/ne: verify using type 'int'
+INFO /spec2/some-int:/spec-info/int/assert[2]: verify using type 'spec-assert-int'
+INFO /spec2/some-int:/spec-info/int/assert[2]/and: verify using type 'spec-assert-int-list'
+INFO /spec2/some-int:/spec-info/int/assert[2]/and[0]: verify using type 'spec-assert-int'
+INFO /spec2/some-int:/spec-info/int/assert[2]/and[0]/ge: verify using type 'int'
+INFO /spec2/some-int:/spec-info/int/assert[2]/and[1]: verify using type 'spec-assert-int'
+INFO /spec2/some-int:/spec-info/int/assert[2]/and[1]/le: verify using type 'int'
+INFO /spec2/some-int:/spec-info/int/assert[3]: verify using type 'spec-assert-int'
+INFO /spec2/some-int:/spec-info/int/assert[3]/and: verify using type 'spec-assert-int-list'
+INFO /spec2/some-int:/spec-info/int/assert[3]/and[0]: verify using type 'spec-assert-int'
+INFO /spec2/some-int:/spec-info/int/assert[3]/and[0]/gt: verify using type 'int'
+INFO /spec2/some-int:/spec-info/int/assert[3]/and[1]: verify using type 'spec-assert-int'
+INFO /spec2/some-int:/spec-info/int/assert[3]/and[1]/lt: verify using type 'int'
+INFO /spec2/some-int:/spec-info/int/assert[4]: verify using type 'spec-assert-int'
+INFO /spec2/some-int:/spec-info/int/assert[4]/and: verify using type 'spec-assert-int-list'
+INFO /spec2/some-int:/spec-info/int/assert[4]/and[0]: verify using type 'spec-assert-int'
+INFO /spec2/some-int:/spec-info/int/assert[4]/and[0]/le: verify using type 'int'
+INFO /spec2/some-int:/spec-info/int/assert[4]/and[1]: verify using type 'spec-assert-int'
+INFO /spec2/some-int:/spec-info/int/assert[4]/and[1]/ge: verify using type 'int'
+INFO /spec2/some-int:/spec-info/int/assert[5]: verify using type 'spec-assert-int'
+INFO /spec2/some-int:/spec-info/int/assert[5]/and: verify using type 'spec-assert-int-list'
+INFO /spec2/some-int:/spec-info/int/assert[5]/and[0]: verify using type 'spec-assert-int'
+INFO /spec2/some-int:/spec-info/int/assert[5]/and[0]/lt: verify using type 'int'
+INFO /spec2/some-int:/spec-info/int/assert[5]/and[1]: verify using type 'spec-assert-int'
+INFO /spec2/some-int:/spec-info/int/assert[5]/and[1]/gt: verify using type 'int'
+INFO /spec2/some-int:/spec-info/int/assert[6]: verify using type 'spec-assert-int'
+INFO /spec2/some-int:/spec-info/int/assert[6]/and: verify using type 'spec-assert-int-list'
+INFO /spec2/some-int:/spec-info/int/assert[6]/and[0]: verify using type 'spec-assert-int'
+INFO /spec2/some-int:/spec-info/int/assert[6]/and[0]/or: verify using type 'spec-assert-int-list'
+INFO /spec2/some-int:/spec-info/int/assert[6]/and[0]/or[0]: verify using type 'spec-assert-int'
+INFO /spec2/some-int:/spec-info/int/assert[6]/and[0]/or[0]/eq: verify using type 'int'
+INFO /spec2/some-int:/spec-info/int/assert[6]/and[0]/or[1]: verify using type 'spec-assert-int'
+INFO /spec2/some-int:/spec-info/int/assert[6]/and[0]/or[1]/ne: verify using type 'int'
+INFO /spec2/some-int:/spec-info/int/assert[7]: verify using type 'spec-assert-int'
+INFO /spec2/some-int:/spec-info/int/assert[7]/eq: verify using type 'int'
+INFO /spec2/some-int:/spec-info/int/assert[8]: verify using type 'spec-assert-int'
+INFO /spec2/some-int:/spec-info/int/assert[8]/ne: verify using type 'int'
+INFO /spec2/some-int:/spec-info/int/assert[9]: verify using type 'spec-assert-int'
+INFO /spec2/some-int:/spec-info/int/assert[9]/le: verify using type 'int'
+INFO /spec2/some-int:/spec-info/int/assert[10]: verify using type 'spec-assert-int'
+INFO /spec2/some-int:/spec-info/int/assert[10]/lt: verify using type 'int'
+INFO /spec2/some-int:/spec-info/int/assert[11]: verify using type 'spec-assert-int'
+INFO /spec2/some-int:/spec-info/int/assert[11]/ge: verify using type 'int'
+INFO /spec2/some-int:/spec-info/int/assert[12]: verify using type 'spec-assert-int'
+INFO /spec2/some-int:/spec-info/int/assert[12]/gt: verify using type 'int'
+INFO /spec2/some-int:/spec-type: verify using type 'name'
+INFO /spec2/some-list: verify using type 'root'
+INFO /spec2/some-list:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec2/some-list:/copyrights: verify using type 'copyrights'
+INFO /spec2/some-list:/copyrights[0]: verify using type 'copyright'
+INFO /spec2/some-list:/enabled-by: verify using type 'enabled-by'
+INFO /spec2/some-list:/links: verify using type 'links'
+INFO /spec2/some-list:/links[0]: verify using type 'link'
+INFO /spec2/some-list:/links[0]/role: verify using type 'name'
+INFO /spec2/some-list:/links[0]/uid: verify using type 'uid'
+INFO /spec2/some-list:/links[0]: verify using type 'spec-member'
+INFO /spec2/some-list:/type: verify using type 'name'
+INFO /spec2/some-list: verify using type 'spec'
+ERROR /spec2/some-list: missing mandatory keys for type 'spec': ['spec-description', 'spec-name']
+INFO /spec2/some-list:/spec-example: verify using type 'optional-str'
+INFO /spec2/some-list:/spec-info: verify using type 'spec-info'
+INFO /spec2/some-list:/spec-info/list: verify using type 'spec-list'
+ERROR /spec2/some-list:/spec-info/list: missing mandatory keys for type 'spec-list': ['description']
+INFO /spec2/some-list:/spec-info/list/spec-type: verify using type 'name'
+INFO /spec2/some-list:/spec-type: verify using type 'name'
+INFO /spec2/some-str: verify using type 'root'
+INFO /spec2/some-str:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec2/some-str:/copyrights: verify using type 'copyrights'
+INFO /spec2/some-str:/copyrights[0]: verify using type 'copyright'
+INFO /spec2/some-str:/enabled-by: verify using type 'enabled-by'
+INFO /spec2/some-str:/links: verify using type 'links'
+INFO /spec2/some-str:/links[0]: verify using type 'link'
+INFO /spec2/some-str:/links[0]/role: verify using type 'name'
+INFO /spec2/some-str:/links[0]/uid: verify using type 'uid'
+INFO /spec2/some-str:/links[0]: verify using type 'spec-member'
+INFO /spec2/some-str:/type: verify using type 'name'
+INFO /spec2/some-str: verify using type 'spec'
+ERROR /spec2/some-str: missing mandatory keys for type 'spec': ['spec-description', 'spec-name']
+INFO /spec2/some-str:/spec-example: verify using type 'optional-str'
+INFO /spec2/some-str:/spec-info: verify using type 'spec-info'
+INFO /spec2/some-str:/spec-info/str: verify using type 'spec-str'
+ERROR /spec2/some-str:/spec-info/str: missing mandatory keys for type 'spec-str': ['description']
+INFO /spec2/some-str:/spec-info/str/assert: verify using type 'spec-assert-str'
+INFO /spec2/some-str:/spec-info/str/assert[0]: verify using type 'spec-assert-str'
+INFO /spec2/some-str:/spec-info/str/assert[0]/uid: verify using type 'none'
+INFO /spec2/some-str:/spec-info/str/assert[1]: verify using type 'spec-assert-str'
+INFO /spec2/some-str:/spec-info/str/assert[1]/eq: verify using type 'str'
+INFO /spec2/some-str:/spec-info/str/assert[2]: verify using type 'spec-assert-str'
+INFO /spec2/some-str:/spec-info/str/assert[2]/ne: verify using type 'str'
+INFO /spec2/some-str:/spec-info/str/assert[3]: verify using type 'spec-assert-str'
+INFO /spec2/some-str:/spec-info/str/assert[3]/re: verify using type 'str'
+INFO /spec2/some-str:/spec-type: verify using type 'name'
+INFO /spec2/sta: verify using type 'root'
+INFO /spec2/sta:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec2/sta:/copyrights: verify using type 'copyrights'
+INFO /spec2/sta:/copyrights[0]: verify using type 'copyright'
+INFO /spec2/sta:/enabled-by: verify using type 'enabled-by'
+INFO /spec2/sta:/links: verify using type 'links'
+INFO /spec2/sta:/type: verify using type 'name'
+INFO /spec2/sta: verify using type 'spec'
+ERROR /spec2/sta: missing mandatory keys for type 'spec': ['spec-description', 'spec-name']
+INFO /spec2/sta:/spec-example: verify using type 'optional-str'
+INFO /spec2/sta:/spec-info: verify using type 'spec-info'
+ERROR /spec2/sta:/spec-info: not at least one key out of ['bool', 'dict', 'float', 'int', 'list', 'none', 'str'] is present for type 'spec-info'
+INFO /spec2/sta:/spec-type: verify using type 'name'
+INFO /spec2/str-contains: verify using type 'root'
+INFO /spec2/str-contains:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec2/str-contains:/copyrights: verify using type 'copyrights'
+INFO /spec2/str-contains:/copyrights[0]: verify using type 'copyright'
+INFO /spec2/str-contains:/enabled-by: verify using type 'enabled-by'
+INFO /spec2/str-contains:/links: verify using type 'links'
+INFO /spec2/str-contains:/links[0]: verify using type 'link'
+INFO /spec2/str-contains:/links[0]/role: verify using type 'name'
+INFO /spec2/str-contains:/links[0]/uid: verify using type 'uid'
+INFO /spec2/str-contains:/links[0]: verify using type 'spec-member'
+INFO /spec2/str-contains:/type: verify using type 'name'
+INFO /spec2/str-contains: verify using type 'spec'
+ERROR /spec2/str-contains: missing mandatory keys for type 'spec': ['spec-description', 'spec-name']
+INFO /spec2/str-contains:/spec-example: verify using type 'optional-str'
+INFO /spec2/str-contains:/spec-info: verify using type 'spec-info'
+INFO /spec2/str-contains:/spec-info/str: verify using type 'spec-str'
+ERROR /spec2/str-contains:/spec-info/str: missing mandatory keys for type 'spec-str': ['description']
+INFO /spec2/str-contains:/spec-info/str/assert: verify using type 'spec-assert-str'
+INFO /spec2/str-contains:/spec-info/str/assert/and: verify using type 'spec-assert-str-list'
+INFO /spec2/str-contains:/spec-info/str/assert/and[0]: verify using type 'spec-assert-str'
+INFO /spec2/str-contains:/spec-info/str/assert/and[0]/contains: verify using type 'list-str'
+INFO /spec2/str-contains:/spec-info/str/assert/and[0]/contains[0]: verify using type 'str'
+INFO /spec2/str-contains:/spec-info/str/assert/and[0]/contains[1]: verify using type 'str'
+INFO /spec2/str-contains:/spec-info/str/assert/and[1]: verify using type 'spec-assert-str'
+INFO /spec2/str-contains:/spec-info/str/assert/and[1]/not: verify using type 'spec-assert-str'
+INFO /spec2/str-contains:/spec-info/str/assert/and[1]/not/contains: verify using type 'list-str'
+INFO /spec2/str-contains:/spec-info/str/assert/and[1]/not/contains[0]: verify using type 'str'
+INFO /spec2/str-contains:/spec-type: verify using type 'name'
+INFO /spec2/x: verify using type 'root'
+INFO /spec2/x:/SPDX-License-Identifier: verify using type 'spdx-license-identifier'
+INFO /spec2/x:/copyrights: verify using type 'copyrights'
+INFO /spec2/x:/copyrights[0]: verify using type 'copyright'
+INFO /spec2/x:/enabled-by: verify using type 'enabled-by'
+INFO /spec2/x:/links: verify using type 'links'
+INFO /spec2/x:/links[0]: verify using type 'link'
+INFO /spec2/x:/links[0]/role: verify using type 'name'
+INFO /spec2/x:/links[0]/uid: verify using type 'uid'
+INFO /spec2/x:/links[0]: verify using type 'spec-member'
+INFO /spec2/x:/type: verify using type 'name'
+INFO /spec2/x: verify using type 'spec'
+ERROR /spec2/x: missing mandatory keys for type 'spec': ['spec-description', 'spec-name']
+INFO /spec2/x:/spec-example: verify using type 'optional-str'
+INFO /spec2/x:/spec-info: verify using type 'spec-info'
+INFO /spec2/x:/spec-info/str: verify using type 'spec-str'
+ERROR /spec2/x:/spec-info/str: missing mandatory keys for type 'spec-str': ['description']
+INFO /spec2/x:/spec-info/str/assert: verify using type 'spec-assert-str'
+INFO /spec2/x:/spec-info/str/assert/eq: verify using type 'str'
+INFO /spec2/x:/spec-type: verify using type 'name'
+INFO finished specification item verification"""
diff --git a/rtemsspec/tests/test_util.py b/rtemsspec/tests/test_util.py
new file mode 100644
index 00000000..c85e6833
--- /dev/null
+++ b/rtemsspec/tests/test_util.py
@@ -0,0 +1,45 @@
+# SPDX-License-Identifier: BSD-2-Clause
+""" Unit tests for the rtemsspec.util module. """
+
+# Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import os
+
+from rtemsspec.util import copy_files, load_config
+
+
+def test_copy_files(tmpdir):
+ src_dir = os.path.dirname(__file__)
+ copy_files(src_dir, tmpdir, [])
+ filename = "config/c/d.yml"
+ assert not os.path.exists(os.path.join(tmpdir, filename))
+ copy_files(src_dir, tmpdir, [filename])
+ assert os.path.exists(os.path.join(tmpdir, filename))
+
+
+def test_load_config():
+ filename = os.path.join(os.path.dirname(__file__), "config", "a.yml")
+ config = load_config(filename)
+ assert config["a"] == "b"
+ assert config["c"] == "d"
diff --git a/rtemsspec/tests/test_validation.py b/rtemsspec/tests/test_validation.py
new file mode 100644
index 00000000..d2a79f8c
--- /dev/null
+++ b/rtemsspec/tests/test_validation.py
@@ -0,0 +1,1271 @@
+# SPDX-License-Identifier: BSD-2-Clause
+""" Unit tests for the rtemsspec.validation module. """
+
+# Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import os
+import pytest
+
+from rtemsspec.validation import generate
+from rtemsspec.items import EmptyItemCache, ItemCache
+from rtemsspec.tests.util import create_item_cache_config_and_copy_spec
+
+
+def test_validation(tmpdir):
+ validation_config = {}
+ base_directory = os.path.join(tmpdir, "base")
+ validation_config["base-directory"] = base_directory
+
+ generate(validation_config, EmptyItemCache())
+
+ item_cache_config = create_item_cache_config_and_copy_spec(
+ tmpdir, "spec-validation", with_spec_types=True)
+ generate(validation_config, ItemCache(item_cache_config))
+
+ with open(os.path.join(base_directory, "ts.c"), "r") as src:
+ content = """/* SPDX-License-Identifier: BSD-2-Clause */
+
+/**
+ * @file
+ *
+ * @ingroup RTEMSTestSuiteBlueGreen
+ */
+
+/*
+ * Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <blue.h>
+
+#include "green.h"
+
+#include <t.h>
+
+/**
+ * @defgroup RTEMSTestSuiteBlueGreen Blue Green
+ *
+ * @ingroup RTEMSTestSuites
+ *
+ * @brief Test Suite
+ *
+ * The Blue Green description.
+ *
+ * @{
+ */
+
+/* Blue green code */
+
+/** @} */
+"""
+ assert content == src.read()
+
+ with open(os.path.join(base_directory, "tc12.c"), "r") as src:
+ content = """/* SPDX-License-Identifier: BSD-2-Clause */
+
+/**
+ * @file
+ *
+ * @ingroup RTEMSTestCaseClassicTaskIdentification
+ * @ingroup RTEMSTestCaseTestCase
+ * @ingroup RTEMSTestCaseTestCase2
+ */
+
+/*
+ * Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <a.h>
+#include <b.h>
+#include <rtems.h>
+
+#include "x.h"
+#include "y.h"
+
+#include <t.h>
+
+/**
+ * @defgroup RTEMSTestCaseClassicTaskIdentification Classic Task Identification
+ *
+ * @ingroup RTEMSTestSuiteBlueGreen
+ *
+ * @brief Test Case
+ *
+ * @{
+ */
+
+typedef enum {
+ ClassicTaskIdentification_Pre_Name_Invalid,
+ ClassicTaskIdentification_Pre_Name_Self,
+ ClassicTaskIdentification_Pre_Name_Valid
+} ClassicTaskIdentification_Pre_Name;
+
+typedef enum {
+ ClassicTaskIdentification_Pre_Node_Local,
+ ClassicTaskIdentification_Pre_Node_Remote,
+ ClassicTaskIdentification_Pre_Node_Invalid,
+ ClassicTaskIdentification_Pre_Node_SearchAll,
+ ClassicTaskIdentification_Pre_Node_SearchOther,
+ ClassicTaskIdentification_Pre_Node_SearchLocal
+} ClassicTaskIdentification_Pre_Node;
+
+typedef enum {
+ ClassicTaskIdentification_Pre_Id_NullPtr,
+ ClassicTaskIdentification_Pre_Id_Valid
+} ClassicTaskIdentification_Pre_Id;
+
+typedef enum {
+ ClassicTaskIdentification_Post_Status_Ok,
+ ClassicTaskIdentification_Post_Status_InvAddr,
+ ClassicTaskIdentification_Post_Status_InvName,
+ ClassicTaskIdentification_Post_Status_InvNode,
+ ClassicTaskIdentification_Post_Status_InvId
+} ClassicTaskIdentification_Post_Status;
+
+typedef enum {
+ ClassicTaskIdentification_Post_Id_Nop,
+ ClassicTaskIdentification_Post_Id_NullPtr,
+ ClassicTaskIdentification_Post_Id_Self,
+ ClassicTaskIdentification_Post_Id_LocalTask,
+ ClassicTaskIdentification_Post_Id_RemoteTask
+} ClassicTaskIdentification_Post_Id;
+
+/**
+ * @brief Test context for Classic Task Identification test case.
+ */
+typedef struct {
+ /**
+ * @brief Brief context member description.
+ *
+ * Context member description.
+ */
+ rtems_status_code status;
+
+ rtems_name name;
+
+ uint32_t node;
+
+ rtems_id *id;
+
+ rtems_id id_value;
+
+ rtems_id id_local_task;
+
+ rtems_id id_remote_task;
+
+ /**
+ * @brief This member defines the pre-condition states for the next action.
+ */
+ size_t pcs[ 3 ];
+
+ /**
+ * @brief This member indicates if the test action loop is currently
+ * executed.
+ */
+ bool in_action_loop;
+} ClassicTaskIdentification_Context;
+
+static ClassicTaskIdentification_Context
+ ClassicTaskIdentification_Instance;
+
+static const char * const ClassicTaskIdentification_PreDesc_Name[] = {
+ "Invalid",
+ "Self",
+ "Valid"
+};
+
+static const char * const ClassicTaskIdentification_PreDesc_Node[] = {
+ "Local",
+ "Remote",
+ "Invalid",
+ "SearchAll",
+ "SearchOther",
+ "SearchLocal"
+};
+
+static const char * const ClassicTaskIdentification_PreDesc_Id[] = {
+ "NullPtr",
+ "Valid"
+};
+
+static const char * const * const ClassicTaskIdentification_PreDesc[] = {
+ ClassicTaskIdentification_PreDesc_Name,
+ ClassicTaskIdentification_PreDesc_Node,
+ ClassicTaskIdentification_PreDesc_Id,
+ NULL
+};
+
+/* Test rtems_task_ident() support */
+
+static void ClassicTaskIdentification_Pre_Name_Prepare(
+ ClassicTaskIdentification_Context *ctx,
+ ClassicTaskIdentification_Pre_Name state
+)
+{
+ /* Prologue */
+
+ switch ( state ) {
+ case ClassicTaskIdentification_Pre_Name_Invalid: {
+ ctx->name = 1;
+ break;
+ }
+
+ case ClassicTaskIdentification_Pre_Name_Self: {
+ ctx->name = RTEMS_SELF;
+ break;
+ }
+
+ case ClassicTaskIdentification_Pre_Name_Valid: {
+ ctx->name = rtems_build_name( 'T', 'A', 'S', 'K' );
+ break;
+ }
+ }
+
+ /* Epilogue */
+}
+
+static void ClassicTaskIdentification_Pre_Node_Prepare(
+ ClassicTaskIdentification_Context *ctx,
+ ClassicTaskIdentification_Pre_Node state
+)
+{
+ switch ( state ) {
+ case ClassicTaskIdentification_Pre_Node_Local: {
+ ctx->node = 1;
+ break;
+ }
+
+ case ClassicTaskIdentification_Pre_Node_Remote: {
+ ctx->node = 2;
+ break;
+ }
+
+ case ClassicTaskIdentification_Pre_Node_Invalid: {
+ ctx->node = 256;
+ break;
+ }
+
+ case ClassicTaskIdentification_Pre_Node_SearchAll: {
+ ctx->node = RTEMS_SEARCH_ALL_NODES;
+ break;
+ }
+
+ case ClassicTaskIdentification_Pre_Node_SearchOther: {
+ ctx->node = RTEMS_SEARCH_OTHER_NODES;
+ break;
+ }
+
+ case ClassicTaskIdentification_Pre_Node_SearchLocal: {
+ ctx->node = RTEMS_SEARCH_LOCAL_NODE;
+ break;
+ }
+ }
+}
+
+static void ClassicTaskIdentification_Pre_Id_Prepare(
+ ClassicTaskIdentification_Context *ctx,
+ ClassicTaskIdentification_Pre_Id state
+)
+{
+ switch ( state ) {
+ case ClassicTaskIdentification_Pre_Id_NullPtr: {
+ ctx->id = NULL;
+ break;
+ }
+
+ case ClassicTaskIdentification_Pre_Id_Valid: {
+ ctx->id_value = 0xffffffff;
+ ctx->id = &ctx->id_value;
+ break;
+ }
+ }
+}
+
+static void ClassicTaskIdentification_Post_Status_Check(
+ ClassicTaskIdentification_Context *ctx,
+ ClassicTaskIdentification_Post_Status state
+)
+{
+ switch ( state ) {
+ case ClassicTaskIdentification_Post_Status_Ok: {
+ T_rsc(ctx->status, RTEMS_SUCCESSFUL);
+ break;
+ }
+
+ case ClassicTaskIdentification_Post_Status_InvAddr: {
+ T_rsc(ctx->status, RTEMS_INVALID_ADDRESS);
+ break;
+ }
+
+ case ClassicTaskIdentification_Post_Status_InvName: {
+ T_rsc(ctx->status, RTEMS_INVALID_NAME);
+ break;
+ }
+
+ case ClassicTaskIdentification_Post_Status_InvNode: {
+ T_rsc(ctx->status, RTEMS_INVALID_NODE);
+ break;
+ }
+
+ case ClassicTaskIdentification_Post_Status_InvId: {
+ T_rsc(ctx->status, RTEMS_INVALID_ID);
+ break;
+ }
+ }
+}
+
+static void ClassicTaskIdentification_Post_Id_Check(
+ ClassicTaskIdentification_Context *ctx,
+ ClassicTaskIdentification_Post_Id state
+)
+{
+ switch ( state ) {
+ case ClassicTaskIdentification_Post_Id_Nop: {
+ T_eq_ptr(ctx->id, &ctx->id_value);
+ T_eq_u32(ctx->id_value, 0xffffffff);
+ break;
+ }
+
+ case ClassicTaskIdentification_Post_Id_NullPtr: {
+ T_null(ctx->id)
+ break;
+ }
+
+ case ClassicTaskIdentification_Post_Id_Self: {
+ T_eq_ptr(ctx->id, &ctx->id_value);
+ T_eq_u32(ctx->id_value, rtems_task_self());
+ break;
+ }
+
+ case ClassicTaskIdentification_Post_Id_LocalTask: {
+ T_eq_ptr(ctx->id, &ctx->id_value);
+ T_eq_u32(ctx->id_value, ctx->id_local_task);
+ break;
+ }
+
+ case ClassicTaskIdentification_Post_Id_RemoteTask: {
+ T_eq_ptr(ctx->id, &ctx->id_value);
+ T_eq_u32(ctx->id_value, ctx->id_remote_task);
+ break;
+ }
+ }
+}
+
+/**
+ * @brief Setup brief description.
+ *
+ * Setup description.
+ */
+static void ClassicTaskIdentification_Setup(
+ ClassicTaskIdentification_Context *ctx
+)
+{
+ rtems_status_code sc;
+
+ sc = rtems_task_create(
+ rtems_build_name( 'T', 'A', 'S', 'K' ),
+ 1,
+ RTEMS_MINIMUM_STACK_SIZE,
+ RTEMS_DEFAULT_MODES,
+ RTEMS_DEFAULT_ATTRIBUTES,
+ &ctx->id_local_task
+ );
+ T_assert_rsc_success( sc );
+}
+
+static void ClassicTaskIdentification_Setup_Wrap( void *arg )
+{
+ ClassicTaskIdentification_Context *ctx;
+
+ ctx = arg;
+ ctx->in_action_loop = false;
+ ClassicTaskIdentification_Setup( ctx );
+}
+
+static void ClassicTaskIdentification_Teardown(
+ ClassicTaskIdentification_Context *ctx
+)
+{
+ rtems_status_code sc;
+
+ if ( ctx->id_local_task != 0 ) {
+ sc = rtems_task_delete( ctx->id_local_task );
+ T_rsc_success( sc );
+ }
+}
+
+static void ClassicTaskIdentification_Teardown_Wrap( void *arg )
+{
+ ClassicTaskIdentification_Context *ctx;
+
+ ctx = arg;
+ ctx->in_action_loop = false;
+ ClassicTaskIdentification_Teardown( ctx );
+}
+
+static void ClassicTaskIdentification_Scope( void *arg, char *buf, size_t n )
+{
+ ClassicTaskIdentification_Context *ctx;
+
+ ctx = arg;
+
+ if ( ctx->in_action_loop ) {
+ T_get_scope( ClassicTaskIdentification_PreDesc, buf, n, ctx->pcs );
+ }
+}
+
+static T_fixture ClassicTaskIdentification_Fixture = {
+ .setup = ClassicTaskIdentification_Setup_Wrap,
+ .stop = NULL,
+ .teardown = ClassicTaskIdentification_Teardown_Wrap,
+ .scope = ClassicTaskIdentification_Scope,
+ .initial_context = &ClassicTaskIdentification_Instance
+};
+
+static const uint8_t ClassicTaskIdentification_TransitionMap[][ 2 ] = {
+ {
+ ClassicTaskIdentification_Post_Status_InvAddr,
+ ClassicTaskIdentification_Post_Id_NullPtr
+ }, {
+ ClassicTaskIdentification_Post_Status_InvName,
+ ClassicTaskIdentification_Post_Id_Nop
+ }, {
+ ClassicTaskIdentification_Post_Status_InvAddr,
+ ClassicTaskIdentification_Post_Id_NullPtr
+ }, {
+ ClassicTaskIdentification_Post_Status_InvName,
+ ClassicTaskIdentification_Post_Id_Nop
+ }, {
+ ClassicTaskIdentification_Post_Status_InvAddr,
+ ClassicTaskIdentification_Post_Id_NullPtr
+ }, {
+ ClassicTaskIdentification_Post_Status_InvName,
+ ClassicTaskIdentification_Post_Id_Nop
+ }, {
+ ClassicTaskIdentification_Post_Status_InvAddr,
+ ClassicTaskIdentification_Post_Id_NullPtr
+ }, {
+ ClassicTaskIdentification_Post_Status_InvName,
+ ClassicTaskIdentification_Post_Id_Nop
+ }, {
+ ClassicTaskIdentification_Post_Status_InvAddr,
+ ClassicTaskIdentification_Post_Id_NullPtr
+ }, {
+ ClassicTaskIdentification_Post_Status_InvName,
+ ClassicTaskIdentification_Post_Id_Nop
+ }, {
+ ClassicTaskIdentification_Post_Status_InvAddr,
+ ClassicTaskIdentification_Post_Id_NullPtr
+ }, {
+ ClassicTaskIdentification_Post_Status_InvName,
+ ClassicTaskIdentification_Post_Id_Nop
+ }, {
+ ClassicTaskIdentification_Post_Status_InvAddr,
+ ClassicTaskIdentification_Post_Id_NullPtr
+ }, {
+ ClassicTaskIdentification_Post_Status_Ok,
+ ClassicTaskIdentification_Post_Id_Self
+ }, {
+ ClassicTaskIdentification_Post_Status_InvAddr,
+ ClassicTaskIdentification_Post_Id_NullPtr
+ }, {
+ ClassicTaskIdentification_Post_Status_Ok,
+ ClassicTaskIdentification_Post_Id_Self
+ }, {
+ ClassicTaskIdentification_Post_Status_InvAddr,
+ ClassicTaskIdentification_Post_Id_NullPtr
+ }, {
+ ClassicTaskIdentification_Post_Status_Ok,
+ ClassicTaskIdentification_Post_Id_Self
+ }, {
+ ClassicTaskIdentification_Post_Status_InvAddr,
+ ClassicTaskIdentification_Post_Id_NullPtr
+ }, {
+ ClassicTaskIdentification_Post_Status_Ok,
+ ClassicTaskIdentification_Post_Id_Self
+ }, {
+ ClassicTaskIdentification_Post_Status_InvAddr,
+ ClassicTaskIdentification_Post_Id_NullPtr
+ }, {
+ ClassicTaskIdentification_Post_Status_Ok,
+ ClassicTaskIdentification_Post_Id_Self
+ }, {
+ ClassicTaskIdentification_Post_Status_InvAddr,
+ ClassicTaskIdentification_Post_Id_NullPtr
+ }, {
+ ClassicTaskIdentification_Post_Status_Ok,
+ ClassicTaskIdentification_Post_Id_Self
+ }, {
+ ClassicTaskIdentification_Post_Status_InvAddr,
+ ClassicTaskIdentification_Post_Id_NullPtr
+ }, {
+ ClassicTaskIdentification_Post_Status_Ok,
+ ClassicTaskIdentification_Post_Id_LocalTask
+ }, {
+ ClassicTaskIdentification_Post_Status_InvAddr,
+ ClassicTaskIdentification_Post_Id_NullPtr
+ }, {
+#if defined(RTEMS_MULTIPROCESSING)
+ ClassicTaskIdentification_Post_Status_Ok,
+ ClassicTaskIdentification_Post_Id_RemoteTask
+#else
+ ClassicTaskIdentification_Post_Status_InvName,
+ ClassicTaskIdentification_Post_Id_Nop
+#endif
+ }, {
+ ClassicTaskIdentification_Post_Status_InvAddr,
+ ClassicTaskIdentification_Post_Id_NullPtr
+ }, {
+ ClassicTaskIdentification_Post_Status_InvName,
+ ClassicTaskIdentification_Post_Id_Nop
+ }, {
+ ClassicTaskIdentification_Post_Status_InvAddr,
+ ClassicTaskIdentification_Post_Id_NullPtr
+ }, {
+ ClassicTaskIdentification_Post_Status_Ok,
+ ClassicTaskIdentification_Post_Id_LocalTask
+ }, {
+ ClassicTaskIdentification_Post_Status_InvAddr,
+ ClassicTaskIdentification_Post_Id_NullPtr
+ }, {
+#if defined(RTEMS_MULTIPROCESSING)
+ ClassicTaskIdentification_Post_Status_Ok,
+ ClassicTaskIdentification_Post_Id_RemoteTask
+#else
+ ClassicTaskIdentification_Post_Status_InvName,
+ ClassicTaskIdentification_Post_Id_Nop
+#endif
+ }, {
+ ClassicTaskIdentification_Post_Status_InvAddr,
+ ClassicTaskIdentification_Post_Id_NullPtr
+ }, {
+ ClassicTaskIdentification_Post_Status_Ok,
+ ClassicTaskIdentification_Post_Id_LocalTask
+ }
+};
+
+/**
+ * @fn void T_case_body_ClassicTaskIdentification( void )
+ *
+ * @brief Test rtems_task_ident() brief description.
+ *
+ * Test rtems_task_ident() description.
+ */
+T_TEST_CASE_FIXTURE(
+ ClassicTaskIdentification,
+ &ClassicTaskIdentification_Fixture
+)
+{
+ ClassicTaskIdentification_Context *ctx;
+ size_t index;
+
+ ctx = T_fixture_context();
+ ctx->in_action_loop = true;
+ index = 0;
+
+ for (
+ ctx->pcs[ 0 ] = ClassicTaskIdentification_Pre_Name_Invalid;
+ ctx->pcs[ 0 ] != ClassicTaskIdentification_Pre_Name_Valid + 1;
+ ++ctx->pcs[ 0 ]
+ ) {
+ for (
+ ctx->pcs[ 1 ] = ClassicTaskIdentification_Pre_Node_Local;
+ ctx->pcs[ 1 ] != ClassicTaskIdentification_Pre_Node_SearchLocal + 1;
+ ++ctx->pcs[ 1 ]
+ ) {
+ for (
+ ctx->pcs[ 2 ] = ClassicTaskIdentification_Pre_Id_NullPtr;
+ ctx->pcs[ 2 ] != ClassicTaskIdentification_Pre_Id_Valid + 1;
+ ++ctx->pcs[ 2 ]
+ ) {
+ ClassicTaskIdentification_Pre_Name_Prepare( ctx, ctx->pcs[ 0 ] );
+ ClassicTaskIdentification_Pre_Node_Prepare( ctx, ctx->pcs[ 1 ] );
+ ClassicTaskIdentification_Pre_Id_Prepare( ctx, ctx->pcs[ 2 ] );
+ ctx->status = rtems_task_ident( ctx->name, ctx->node, ctx->id );
+ ClassicTaskIdentification_Post_Status_Check(
+ ctx,
+ ClassicTaskIdentification_TransitionMap[ index ][ 0 ]
+ );
+ ClassicTaskIdentification_Post_Id_Check(
+ ctx,
+ ClassicTaskIdentification_TransitionMap[ index ][ 1 ]
+ );
+ ++index;
+ }
+ }
+ }
+}
+
+/** @} */
+
+/**
+ * @defgroup RTEMSTestCaseTestCase Test Case
+ *
+ * @ingroup RTEMSTestSuiteBlueGreen
+ *
+ * @brief Test Case
+ *
+ * @{
+ */
+
+/* Test case support code */
+
+/**
+ * @fn void T_case_body_TestCase( void )
+ *
+ * @brief Test case brief description.
+ *
+ * Test case description.
+ *
+ * This test case performs the following actions:
+ *
+ * - Test case action 0 description.
+ *
+ * - Test case action 0 check 0 description.
+ *
+ * - Test case action 0 check 1 description.
+ *
+ * - Test case action 1 description.
+ *
+ * - Test case action 1 check 0 description.
+ *
+ * - Test case action 1 check 1 description.
+ */
+T_TEST_CASE( TestCase )
+{
+ /* Test case prologue code */
+
+ T_plan(2);
+
+ /* Test case action 0 code */
+ /* Test case action 0 check 0 code */
+ /* Test case action 0 check 1 code; step 0 */
+
+ /* Test case action 1 code */
+ /* Test case action 1 check 0 code; step 1 */
+ /* Test case action 1 check 1 code */
+
+ /* Test case epilogue code */
+}
+
+/** @} */
+
+/**
+ * @defgroup RTEMSTestCaseTestCase2 Test Case 2
+ *
+ * @ingroup RTEMSTestSuiteBlueGreen
+ *
+ * @brief Test Case
+ *
+ * @{
+ */
+
+/**
+ * @fn void T_case_body_TestCase2( void )
+ *
+ * @brief Test case 2 brief description.
+ *
+ * Test case 2 description.
+ *
+ * This test case performs the following actions:
+ *
+ * - Test case 2 action 0 description.
+ *
+ * - Test case 2 action 0 check 0 description.
+ *
+ * - Test case 2 action 0 check 1 description.
+ *
+ * - Test case 2 action 1 description.
+ */
+T_TEST_CASE_FIXTURE( TestCase2, &test_case_2_fixture )
+{
+ /* Test case 2 action 0 code */
+ /* Test case 2 action 0 check 0 code */
+ /* Test case 2 action 0 check 1 code */
+
+ /* Test case 2 action 1 code */
+}
+
+/** @} */
+"""
+ assert content == src.read()
+
+ with open(os.path.join(base_directory, "tc34.c"), "r") as src:
+ content = """/* SPDX-License-Identifier: BSD-2-Clause */
+
+/**
+ * @file
+ *
+ * @ingroup RTEMSTestCaseTestCase3
+ * @ingroup RTEMSTestCaseTestCase4
+ */
+
+/*
+ * Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <c.h>
+
+#include "z.h"
+
+#include <t.h>
+
+/**
+ * @defgroup RTEMSTestCaseTestCase3 Test Case 3
+ *
+ * @ingroup RTEMSTestSuiteBlueGreen
+ *
+ * @brief Test Case
+ *
+ * @{
+ */
+
+/**
+ * @fn void T_case_body_TestCase3( void )
+ *
+ * @brief Test case 3 brief description.
+ *
+ * Test case 3 description.
+ *
+ * This test case performs the following actions:
+ *
+ * - Test case 3 action 0 description.
+ *
+ * - Test case 3 action 0 check 0 description.
+ */
+T_TEST_CASE( TestCase3 )
+{
+ T_plan(1);
+
+ /* Test case 3 action 0 code */
+ /* Test case 3 action 0 check 0 code; step 0 */
+}
+
+/** @} */
+
+/**
+ * @defgroup RTEMSTestCaseTestCase4 Test Case 4
+ *
+ * @ingroup RTEMSTestSuiteBlueGreen
+ *
+ * @brief Test Case
+ *
+ * @{
+ */
+
+/**
+ * @fn void T_case_body_TestCase4( void )
+ *
+ * @brief Test case 4 brief description.
+ *
+ * Test case 4 description.
+ */
+T_TEST_CASE( TestCase4 )
+{
+ /* Test case 4 epilogue code */
+}
+
+/** @} */
+"""
+ assert content == src.read()
+ with open(os.path.join(base_directory, "action2.h"), "r") as src:
+ content = """/* SPDX-License-Identifier: BSD-2-Clause */
+
+/**
+ * @file
+ *
+ * @ingroup RTEMSTestCaseAction2
+ */
+
+/*
+ * Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _ACTION2_H
+#define _ACTION2_H
+
+#include <d.h>
+
+#include "e.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @addtogroup RTEMSTestCaseAction2
+ *
+ * @{
+ */
+
+typedef enum {
+ Action2_Pre_A_X,
+ Action2_Pre_A_Y
+} Action2_Pre_A;
+
+typedef enum {
+ Action2_Pre_B_X,
+ Action2_Pre_B_Y
+} Action2_Pre_B;
+
+typedef enum {
+ Action2_Post_A_X,
+ Action2_Post_A_Y
+} Action2_Post_A;
+
+typedef enum {
+ Action2_Post_B_X,
+ Action2_Post_B_Y
+} Action2_Post_B;
+
+/* Header code for Action 2 with Action2_Run() */
+
+/**
+ * @brief Test brief.
+ *
+ * Test description.
+ *
+ * @param[in] a Parameter A description.
+ *
+ * @param b Parameter B description.
+ *
+ * @param[out] c Parameter C description.
+ */
+void Action2_Run( int *a, int b, int *c );
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ACTION2_H */
+"""
+ assert content == src.read()
+ with open(os.path.join(base_directory, "action2.c"), "r") as src:
+ content = """/* SPDX-License-Identifier: BSD-2-Clause */
+
+/**
+ * @file
+ *
+ * @ingroup RTEMSTestCaseAction2
+ */
+
+/*
+ * Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <a.h>
+
+#include "b.h"
+
+#include <t.h>
+
+/**
+ * @defgroup RTEMSTestCaseAction2 Action 2
+ *
+ * @ingroup RTEMSTestSuiteBlueGreen
+ *
+ * @brief Test Case
+ *
+ * @{
+ */
+
+/**
+ * @brief Test context for Action 2 test case.
+ */
+typedef struct {
+ /**
+ * @brief Context member brief.
+ *
+ * Context member description.
+ */
+ int member;
+
+ /**
+ * @brief This member contains a copy of the corresponding Action2_Run()
+ * parameter.
+ */
+ int *a;
+
+ /**
+ * @brief This member contains a copy of the corresponding Action2_Run()
+ * parameter.
+ */
+ int b;
+
+ /**
+ * @brief This member contains a copy of the corresponding Action2_Run()
+ * parameter.
+ */
+ int *c;
+
+ /**
+ * @brief This member defines the pre-condition states for the next action.
+ */
+ size_t pcs[ 2 ];
+
+ /**
+ * @brief This member indicates if the test action loop is currently
+ * executed.
+ */
+ bool in_action_loop;
+} Action2_Context;
+
+static Action2_Context
+ Action2_Instance;
+
+static const char * const Action2_PreDesc_A[] = {
+ "X",
+ "Y"
+};
+
+static const char * const Action2_PreDesc_B[] = {
+ "X",
+ "Y"
+};
+
+static const char * const * const Action2_PreDesc[] = {
+ Action2_PreDesc_A,
+ Action2_PreDesc_B,
+ NULL
+};
+
+/* Support code */
+
+static void Action2_Pre_A_Prepare( Action2_Context *ctx, Action2_Pre_A state )
+{
+ /* Pre A prologue. */
+
+ switch ( state ) {
+ case Action2_Pre_A_X: {
+ /* Pre A X */
+ break;
+ }
+
+ case Action2_Pre_A_Y: {
+ /* Pre A Y */
+ break;
+ }
+ }
+
+ /* Pre A epilogue. */
+}
+
+static void Action2_Pre_B_Prepare( Action2_Context *ctx, Action2_Pre_B state )
+{
+ /* Pre B prologue. */
+
+ switch ( state ) {
+ case Action2_Pre_B_X: {
+ /* Pre B X */
+ break;
+ }
+
+ case Action2_Pre_B_Y: {
+ /* Pre B Y */
+ break;
+ }
+ }
+
+ /* Pre B epilogue. */
+}
+
+static void Action2_Post_A_Check( Action2_Context *ctx, Action2_Post_A state )
+{
+ /* Post A prologue. */
+
+ switch ( state ) {
+ case Action2_Post_A_X: {
+ /* Post A X */
+ break;
+ }
+
+ case Action2_Post_A_Y: {
+ /* Post A Y */
+ break;
+ }
+ }
+
+ /* Post A epilogue. */
+}
+
+static void Action2_Post_B_Check( Action2_Context *ctx, Action2_Post_B state )
+{
+ /* Post B prologue. */
+
+ switch ( state ) {
+ case Action2_Post_B_X: {
+ /* Post B X */
+ break;
+ }
+
+ case Action2_Post_B_Y: {
+ /* Post B Y */
+ break;
+ }
+ }
+
+ /* Post B epilogue. */
+}
+
+/**
+ * @brief Setup brief.
+ *
+ * Setup description.
+ */
+static void Action2_Setup( Action2_Context *ctx )
+{
+ /* Setup code */
+}
+
+static void Action2_Setup_Wrap( void *arg )
+{
+ Action2_Context *ctx;
+
+ ctx = arg;
+ ctx->in_action_loop = false;
+ Action2_Setup( ctx );
+}
+
+/**
+ * @brief Teardown brief.
+ *
+ * Teardown description.
+ */
+static void Action2_Teardown( Action2_Context *ctx )
+{
+ /* Teardown code */
+}
+
+static void Action2_Teardown_Wrap( void *arg )
+{
+ Action2_Context *ctx;
+
+ ctx = arg;
+ ctx->in_action_loop = false;
+ Action2_Teardown( ctx );
+}
+
+static void Action2_Scope( void *arg, char *buf, size_t n )
+{
+ Action2_Context *ctx;
+
+ ctx = arg;
+
+ if ( ctx->in_action_loop ) {
+ T_get_scope( Action2_PreDesc, buf, n, ctx->pcs );
+ }
+}
+
+static T_fixture Action2_Fixture = {
+ .setup = Action2_Setup_Wrap,
+ .stop = NULL,
+ .teardown = Action2_Teardown_Wrap,
+ .scope = Action2_Scope,
+ .initial_context = &Action2_Instance
+};
+
+static const uint8_t Action2_TransitionMap[][ 2 ] = {
+ {
+ Action2_Post_A_X,
+ Action2_Post_B_Y
+ }, {
+ Action2_Post_A_X,
+ Action2_Post_B_Y
+ }, {
+ Action2_Post_A_X,
+ Action2_Post_B_Y
+ }, {
+ Action2_Post_A_X,
+ Action2_Post_B_Y
+ }
+};
+
+static T_fixture_node Action2_Node;
+
+void Action2_Run( int *a, int b, int *c )
+{
+ Action2_Context *ctx;
+ size_t index;
+
+ ctx = T_push_fixture( &Action2_Node, &Action2_Fixture );
+
+ ctx->a = a;
+ ctx->b = b;
+ ctx->c = c;
+ ctx->in_action_loop = true;
+ index = 0;
+
+ for (
+ ctx->pcs[ 0 ] = Action2_Pre_A_X;
+ ctx->pcs[ 0 ] != Action2_Pre_A_Y + 1;
+ ++ctx->pcs[ 0 ]
+ ) {
+ for (
+ ctx->pcs[ 1 ] = Action2_Pre_B_X;
+ ctx->pcs[ 1 ] != Action2_Pre_B_Y + 1;
+ ++ctx->pcs[ 1 ]
+ ) {
+ Action2_Pre_A_Prepare( ctx, ctx->pcs[ 0 ] );
+ Action2_Pre_B_Prepare( ctx, ctx->pcs[ 1 ] );
+ /* Action */
+ Action2_Post_A_Check(
+ ctx,
+ Action2_TransitionMap[ index ][ 0 ]
+ );
+ Action2_Post_B_Check(
+ ctx,
+ Action2_TransitionMap[ index ][ 1 ]
+ );
+ ++index;
+ }
+ }
+
+ T_pop_fixture();
+}
+
+/** @} */
+"""
+ assert content == src.read()
diff --git a/rtemsspec/tests/util.py b/rtemsspec/tests/util.py
new file mode 100644
index 00000000..d07df74c
--- /dev/null
+++ b/rtemsspec/tests/util.py
@@ -0,0 +1,53 @@
+# SPDX-License-Identifier: BSD-2-Clause
+""" Unit tests utility module. """
+
+# Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import os
+import shutil
+from typing import Dict
+
+
+def create_item_cache_config_and_copy_spec(
+ tmp_dir: str,
+ spec_dir: str,
+ with_spec_types: bool = False) -> Dict[str, str]:
+ """
+ Creates an item cache configuration and copies a specification
+ directory to the temporary tests directory.
+ """
+ config = {}
+ cache_dir = os.path.join(tmp_dir, "cache")
+ config["cache-directory"] = os.path.normpath(cache_dir)
+ spec_src = os.path.join(os.path.dirname(__file__), spec_dir)
+ spec_dst = os.path.join(tmp_dir, "spec")
+ shutil.copytree(spec_src, spec_dst)
+ config["paths"] = [os.path.normpath(spec_dst)]
+ if with_spec_types:
+ spec = os.path.join(os.path.dirname(__file__), "spec")
+ shutil.copytree(spec, os.path.join(spec_dst, "spec"))
+ config["spec-type-root-uid"] = "/spec/root"
+ else:
+ config["spec-type-root-uid"] = None
+ return config
diff --git a/rtemsspec/util.py b/rtemsspec/util.py
new file mode 100644
index 00000000..d2403cd3
--- /dev/null
+++ b/rtemsspec/util.py
@@ -0,0 +1,65 @@
+# SPDX-License-Identifier: BSD-2-Clause
+""" This module provides utility functions. """
+
+# Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import os
+import shutil
+from typing import Any, List
+import yaml
+
+
+def copy_files(src_dir: str, dst_dir: str, files: List[str]) -> None:
+ """
+ Copies a list of files in the source directory to the destination
+ directory preserving the directory of the files relative to the source
+ directory.
+ """
+ for a_file in files:
+ src = os.path.join(src_dir, a_file)
+ dst = os.path.join(dst_dir, a_file)
+ os.makedirs(os.path.dirname(dst), exist_ok=True)
+ shutil.copy2(src, dst)
+
+
+def load_config(config_filename: str) -> Any:
+ """ Loads the configuration file with recursive includes. """
+ class IncludeLoader(yaml.SafeLoader): # pylint: disable=too-many-ancestors
+ """ YAML loader customization to process custom include tags. """
+ _filenames = [config_filename]
+
+ def include(self, node):
+ """ Processes the custom include tag. """
+ container = IncludeLoader._filenames[0]
+ dirname = os.path.dirname(container)
+ filename = os.path.join(dirname, self.construct_scalar(node))
+ IncludeLoader._filenames.insert(0, filename)
+ with open(filename, "r") as included_file:
+ data = yaml.load(included_file, IncludeLoader)
+ IncludeLoader._filenames.pop()
+ return data
+
+ IncludeLoader.add_constructor("!include", IncludeLoader.include)
+ with open(config_filename, "r") as config_file:
+ return yaml.load(config_file.read(), Loader=IncludeLoader)
diff --git a/rtemsspec/validation.py b/rtemsspec/validation.py
new file mode 100644
index 00000000..d2d5a500
--- /dev/null
+++ b/rtemsspec/validation.py
@@ -0,0 +1,714 @@
+# SPDX-License-Identifier: BSD-2-Clause
+""" This module provides functions for the generation of validation tests. """
+
+# Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import itertools
+import os
+from typing import Any, Dict, List, NamedTuple, Optional, Tuple
+
+from rtemsspec.content import CContent, CInclude, enabled_by_to_exp, \
+ ExpressionMapper
+from rtemsspec.items import Item, ItemCache, ItemGetValueContext, ItemMapper
+
+ItemMap = Dict[str, Item]
+
+
+class _CodeMapper(ItemMapper):
+ def get_value(self, ctx: ItemGetValueContext) -> Any:
+ if ctx.type_path_key == "requirement/functional/action:/test-run":
+ return f"{ctx.item['test-name'].replace(' ', '')}_Run"
+ raise KeyError
+
+
+class _TextMapper(ItemMapper):
+ def __init__(self, item: Item):
+ super().__init__(item)
+ self._step = 0
+
+ @property
+ def steps(self):
+ """ The count of test steps. """
+ return self._step
+
+ def reset_step(self):
+ """ Resets the test step counter. """
+ self._step = 0
+
+ def map(self, identifier: str) -> Tuple[Item, Any]:
+ if identifier == "step":
+ step = self._step
+ self._step = step + 1
+ return self._item, str(step)
+ return super().map(identifier)
+
+
+def _add_ingroup(content: CContent, items: List["_TestItem"]) -> None:
+ content.add_ingroup([item.group_identifier for item in items])
+
+
+class _TestItem:
+ """ A test item with a defaul implementation for test cases. """
+ def __init__(self, item: Item):
+ self._item = item
+ self._ident = self.name.replace(" ", "")
+ self._code_mapper = _CodeMapper(self._item)
+ self._text_mapper = _TextMapper(self._item)
+
+ def __getitem__(self, key: str):
+ return self._item[key]
+
+ @property
+ def item(self) -> Item:
+ """ Returns the item. """
+ return self._item
+
+ @property
+ def uid(self) -> str:
+ """ Returns the item UID. """
+ return self._item.uid
+
+ @property
+ def ident(self) -> str:
+ """ Returns the test identifier. """
+ return self._ident
+
+ @property
+ def name(self) -> str:
+ """ Returns the name. """
+ return self._item["name"]
+
+ @property
+ def includes(self) -> List[str]:
+ """ Returns the list of includes. """
+ return self._item["includes"]
+
+ @property
+ def local_includes(self) -> List[str]:
+ """ Returns the list of local includes. """
+ return self._item["local-includes"]
+
+ @property
+ def group_identifier(self) -> str:
+ """ Returns the group identifier. """
+ return f"RTEMSTestCase{self.ident}"
+
+ def substitute_code(self, text: Optional[str]) -> str:
+ """ Performs a variable substitution for code. """
+ return self._code_mapper.substitute(text)
+
+ def substitute_text(self,
+ text: Optional[str],
+ prefix: Optional[str] = None) -> str:
+ """
+ Performs a variable substitution for text with an optinal prefix.
+ """
+ if prefix:
+ return self._text_mapper.substitute_with_prefix(text, prefix)
+ return self._text_mapper.substitute(text)
+
+ def add_test_case_description(
+ self, content: CContent,
+ test_case_to_suites: Dict[str, List["_TestItem"]]) -> None:
+ """ Adds the test case description. """
+ with content.defgroup_block(self.group_identifier, self.name):
+ _add_ingroup(content, test_case_to_suites[self.uid])
+ content.add(["@brief Test Case", "", "@{"])
+
+ def _add_test_case_action_description(self, content: CContent) -> None:
+ actions = self["actions"]
+ if actions:
+ content.add("This test case performs the following actions:")
+ for action in actions:
+ content.wrap(self.substitute_text(action["description"]),
+ initial_indent="- ")
+ for check in action["checks"]:
+ content.wrap(self.substitute_text(check["description"]),
+ initial_indent=" - ",
+ subsequent_indent=" ")
+
+ def _generate_test_case_actions(self) -> CContent:
+ content = CContent()
+ for action in self["actions"]:
+ content.add(self.substitute_code(action["action"]))
+ for check in action["checks"]:
+ content.append(self.substitute_text(check["check"]))
+ return content
+
+ def generate(self, content: CContent, _base_directory: str,
+ test_case_to_suites: Dict[str, List["_TestItem"]]) -> None:
+ """ Generates the content. """
+ self.add_test_case_description(content, test_case_to_suites)
+ content.add(self.substitute_code(self["support"]))
+ with content.function_block(f"void T_case_body_{self.ident}( void )"):
+ content.add_brief_description(self.substitute_text(self["brief"]))
+ content.wrap(self.substitute_text(self["description"]))
+ self._add_test_case_action_description(content)
+ content.gap = False
+ params = [f"{self.ident}"]
+ fixture = self["fixture"]
+ if fixture:
+ params.append(f"&{fixture}")
+ name = "T_TEST_CASE_FIXTURE"
+ else:
+ name = "T_TEST_CASE"
+ with content.function("", name, params):
+ content.add(self.substitute_code(self["prologue"]))
+ self._text_mapper.reset_step()
+ action_content = self._generate_test_case_actions()
+ if self._text_mapper.steps > 0:
+ content.add(f"T_plan({self._text_mapper.steps});")
+ content.add(action_content)
+ content.add(self.substitute_code(self["epilogue"]))
+ content.add("/** @} */")
+
+
+class _TestSuiteItem(_TestItem):
+ """ A test suite item. """
+ @property
+ def group_identifier(self) -> str:
+ return f"RTEMSTestSuite{self.ident}"
+
+ def generate(self, content: CContent, _base_directory: str,
+ _test_case_to_suites: Dict[str, List[_TestItem]]) -> None:
+ with content.defgroup_block(self.group_identifier, self.name):
+ content.add(["@ingroup RTEMSTestSuites", "", "@brief Test Suite"])
+ content.wrap(self.substitute_text(self["description"]))
+ content.add("@{")
+ content.add(self.substitute_code(self["code"]))
+ content.add("/** @} */")
+
+
+class _PostCondition(NamedTuple):
+ """ A set of post conditions with an enabled by expression. """
+ enabled_by: str
+ conditions: Tuple[int, ...]
+
+
+_DirectiveIndexToEnum = Tuple[Tuple[str, ...], ...]
+_TransitionMap = List[List[_PostCondition]]
+
+
+def _directive_state_to_index(
+ conditions: List[Any]) -> Tuple[Dict[str, int], ...]:
+ return tuple(
+ dict((state["name"], index)
+ for index, state in enumerate(condition["states"]))
+ for condition in conditions)
+
+
+def _directive_index_to_enum(prefix: str,
+ conditions: List[Any]) -> _DirectiveIndexToEnum:
+ return tuple(
+ tuple([f"{prefix}_{condition['name']}"] + [
+ f"{prefix}_{condition['name']}_{state['name']}"
+ for state in condition["states"]
+ ]) for index, condition in enumerate(conditions))
+
+
+def _directive_add_enum(content: CContent,
+ index_to_enum: _DirectiveIndexToEnum) -> None:
+ for enum in index_to_enum:
+ content.add("typedef enum {")
+ with content.indent():
+ content.add(",\n".join(enum[1:]))
+ content.add(f"}} {enum[0]};")
+
+
+class _TestDirectiveItem(_TestItem):
+ """ A test directive item. """
+
+ # pylint: disable=too-many-instance-attributes
+ def __init__(self, item: Item):
+ super().__init__(item)
+ self._pre_condition_count = len(item["pre-conditions"])
+ self._post_condition_count = len(item["post-conditions"])
+ self._pre_index_to_enum = _directive_index_to_enum(
+ f"{self.ident}_Pre", item["pre-conditions"])
+ self._post_index_to_enum = _directive_index_to_enum(
+ f"{self.ident}_Post", item["post-conditions"])
+ self._pre_state_to_index = _directive_state_to_index(
+ item["pre-conditions"])
+ self._post_state_to_index = _directive_state_to_index(
+ item["post-conditions"])
+ self._pre_index_to_condition = dict(
+ (index, condition)
+ for index, condition in enumerate(item["pre-conditions"]))
+ self._post_index_to_name = dict(
+ (index, condition["name"])
+ for index, condition in enumerate(item["post-conditions"]))
+
+ @property
+ def name(self) -> str:
+ return self._item["test-name"]
+
+ @property
+ def context(self) -> str:
+ """ Returns the test case context type. """
+ return f"{self._ident}_Context"
+
+ @property
+ def includes(self) -> List[str]:
+ return self._item["test-includes"]
+
+ @property
+ def local_includes(self) -> List[str]:
+ return self._item["test-local-includes"]
+
+ def _add_pre_condition_descriptions(self, content: CContent) -> None:
+ for condition in self["pre-conditions"]:
+ content.add("static const char * const "
+ f"{self.ident}_PreDesc_{condition['name']}[] = {{")
+ with content.indent():
+ content.add(",\n".join(f"\"{state['name']}\""
+ for state in condition["states"]))
+ content.add("};")
+ content.add("static const char * const * const "
+ f"{self.ident}_PreDesc[] = {{")
+ with content.indent():
+ content.add(",\n".join([
+ f"{self.ident}_PreDesc_{condition['name']}"
+ for condition in self["pre-conditions"]
+ ] + ["NULL"]))
+ content.add("};")
+
+ def _add_context(self, content: CContent, header: Dict[str, Any]) -> None:
+ with content.doxygen_block():
+ content.add_brief_description(
+ f"Test context for {self.name} test case.")
+ content.append("typedef struct {")
+ with content.indent():
+ for info in self["test-context"]:
+ content.add_description_block(info["brief"],
+ info["description"])
+ content.add(f"{info['member'].strip()};")
+ for param in self._get_run_params(header):
+ content.add_description_block(
+ "This member contains a copy of the corresponding "
+ f"{self.ident}_Run() parameter.", None)
+ content.add(f"{param};")
+ content.add_description_block(
+ "This member defines the pre-condition states "
+ "for the next action.", None)
+ content.add(f"size_t pcs[ {self._pre_condition_count} ];")
+ content.add_description_block(
+ "This member indicates if the test action loop "
+ "is currently executed.", None)
+ content.add("bool in_action_loop;")
+ content.add([
+ f"}} {self.context};", "", f"static {self.context}",
+ f" {self.ident}_Instance;"
+ ])
+
+ def _add_scope_body(self, content: CContent) -> None:
+ with content.condition("ctx->in_action_loop"):
+ content.call_function(
+ None, "T_get_scope",
+ [f"{self.ident}_PreDesc", "buf", "n", "ctx->pcs"])
+
+ def _add_fixture_scope(self, content: CContent) -> None:
+ params = ["void *arg", "char *buf", "size_t n"]
+ with content.function("static void", f"{self.ident}_Scope", params):
+ content.add([f"{self.context} *ctx;", "", "ctx = arg;"])
+ self._add_scope_body(content)
+
+ def _add_fixture_method(self, content: CContent,
+ info: Optional[Dict[str, Optional[str]]],
+ name: str) -> str:
+ if not info:
+ return "NULL"
+ method = f"{self.ident}_{name}"
+ wrap = f"{method}_Wrap"
+ content.add_description_block(info["brief"], info["description"])
+ with content.function("static void", method, [f"{self.context} *ctx"]):
+ content.add(self.substitute_code(info["code"]))
+ with content.function("static void", wrap, ["void *arg"]):
+ content.add([
+ f"{self.context} *ctx;", "", "ctx = arg;",
+ "ctx->in_action_loop = false;", f"{method}( ctx );"
+ ])
+ return wrap
+
+ def _add_transitions(self, condition_index: int, map_index: int,
+ transition: Dict[str,
+ Any], transition_map: _TransitionMap,
+ post: Tuple[int, ...]) -> None:
+ # pylint: disable=too-many-arguments
+ if condition_index < self._pre_condition_count:
+ condition = self._pre_index_to_condition[condition_index]
+ state_count = len(condition["states"])
+ map_index *= state_count
+ states = transition["pre-conditions"][condition["name"]]
+ if isinstance(states, str):
+ assert states == "all"
+ for index in range(state_count):
+ self._add_transitions(condition_index + 1,
+ map_index + index, transition,
+ transition_map, post)
+ else:
+ for state in states:
+ self._add_transitions(
+ condition_index + 1, map_index +
+ self._pre_state_to_index[condition_index][state],
+ transition, transition_map, post)
+ else:
+ enabled_by = enabled_by_to_exp(transition["enabled-by"],
+ ExpressionMapper())
+ assert enabled_by != "1" or not transition_map[map_index]
+ transition_map[map_index].append(_PostCondition(enabled_by, post))
+
+ def _get_transition_map(self) -> _TransitionMap:
+ transition_count = 1
+ for condition in self["pre-conditions"]:
+ state_count = len(condition["states"])
+ assert state_count > 0
+ transition_count *= state_count
+ transition_map = [list() for _ in range(transition_count)
+ ] # type: _TransitionMap
+ for transition in self["transition-map"]:
+ post = tuple(self._post_state_to_index[index][
+ transition["post-conditions"][self._post_index_to_name[index]]]
+ for index in range(self._post_condition_count))
+ self._add_transitions(0, 0, transition, transition_map, post)
+ return transition_map
+
+ def _post_condition_enumerators(self, conditions: Any) -> str:
+ return ",\n".join(
+ f" {self._post_index_to_enum[index][condition + 1]}"
+ for index, condition in enumerate(conditions))
+
+ def _add_transition_map(self, content: CContent) -> None:
+ transition_map = self._get_transition_map()
+ content.add([
+ "static const uint8_t "
+ f"{self.ident}_TransitionMap[][ {self._post_condition_count} ]"
+ " = {", " {"
+ ])
+ elements = []
+ for transistions in transition_map:
+ assert transistions[0].enabled_by == "1"
+ if len(transistions) == 1:
+ elements.append(
+ self._post_condition_enumerators(
+ transistions[0].conditions))
+ else:
+ ifelse = "#if "
+ enumerators = [] # type: List[str]
+ for variant in transistions[1:]:
+ enumerators.append(ifelse + variant.enabled_by)
+ enumerators.append(
+ self._post_condition_enumerators(variant.conditions))
+ ifelse = "#elif "
+ enumerators.append("#else")
+ enumerators.append(
+ self._post_condition_enumerators(
+ transistions[0].conditions))
+ enumerators.append("#endif")
+ elements.append("\n".join(enumerators))
+ content.append(["\n }, {\n".join(elements), " }", "};"])
+
+ def _add_action(self, content: CContent) -> None:
+ for index, enum in enumerate(self._pre_index_to_enum):
+ content.append(f"{enum[0]}_Prepare( ctx, ctx->pcs[ {index} ] );")
+ content.append(self.substitute_code(self["test-action"]))
+ transition_map = f"{self.ident}_TransitionMap"
+ for index, enum in enumerate(self._post_index_to_enum):
+ content.append([
+ f"{enum[0]}_Check(", " ctx,",
+ f" {transition_map}[ index ][ {index} ]", ");"
+ ])
+ content.append("++index;")
+
+ def _add_for_loops(self, content: CContent, index: int) -> None:
+ if index < self._pre_condition_count:
+ var = f"ctx->pcs[ {index} ]"
+ begin = self._pre_index_to_enum[index][1]
+ end = self._pre_index_to_enum[index][-1]
+ with content.for_loop(f"{var} = {begin}", f"{var} != {end} + 1",
+ f"++{var}"):
+ self._add_for_loops(content, index + 1)
+ else:
+ self._add_action(content)
+
+ def _add_test_case(self, content: CContent, header: Dict[str,
+ Any]) -> None:
+ fixture = f"{self.ident}_Fixture"
+ prologue = CContent()
+ epilogue = CContent()
+ if header:
+ content.add(f"static T_fixture_node {self.ident}_Node;")
+ ret = "void"
+ name = f"{self.ident}_Run"
+ params = self._get_run_params(header)
+ prologue.add([f"{self.context} *ctx;", "size_t index;"])
+ prologue.call_function("ctx =", "T_push_fixture",
+ [f"&{self.ident}_Node", f"&{fixture}"])
+ prologue.add([
+ f"ctx->{param['name']} = {param['name']};"
+ for param in header["run-params"]
+ ] + ["ctx->in_action_loop = true;", "index = 0;"])
+ epilogue.add("T_pop_fixture();")
+ align = True
+ else:
+ with content.function_block(
+ f"void T_case_body_{self.ident}( void )"):
+ content.add_brief_description(self["test-brief"])
+ content.wrap(self["test-description"])
+ content.gap = False
+ ret = ""
+ name = "T_TEST_CASE_FIXTURE"
+ params = [f"{self.ident}", f"&{fixture}"]
+ prologue.add([
+ f"{self.context} *ctx;", "size_t index;", "",
+ "ctx = T_fixture_context();", "ctx->in_action_loop = true;",
+ "index = 0;"
+ ])
+ align = False
+ with content.function(ret, name, params, align=align):
+ content.add(prologue)
+ self._add_for_loops(content, 0)
+ content.add(epilogue)
+
+ def _add_handler(self, content: CContent, conditions: List[Any],
+ index_to_enum: _DirectiveIndexToEnum,
+ action: str) -> None:
+ for condition_index, condition in enumerate(conditions):
+ enum = index_to_enum[condition_index]
+ handler = f"{enum[0]}_{action}"
+ params = [f"{self.context} *ctx", f"{enum[0]} state"]
+ with content.function("static void", handler, params):
+ content.add(self.substitute_code(condition["test-prologue"]))
+ content.add("switch ( state ) {")
+ with content.indent():
+ for state_index, state in enumerate(condition["states"]):
+ content.add(f"case {enum[state_index + 1]}: {{")
+ with content.indent():
+ content.add(
+ self.substitute_code(state["test-code"]))
+ content.append("break;")
+ content.add("}")
+ content.add("}")
+ content.add(self.substitute_code(condition["test-epilogue"]))
+
+ def _get_run_params(self, header: Optional[Dict[str, Any]]) -> List[str]:
+ if not header:
+ return []
+ return [
+ self.substitute_text(param["specifier"],
+ f"test-header/run-params[{index}]")
+ for index, param in enumerate(header["run-params"])
+ ]
+
+ def _generate_header_body(self, content: CContent,
+ header: Dict[str, Any]) -> None:
+ _directive_add_enum(content, self._pre_index_to_enum)
+ _directive_add_enum(content, self._post_index_to_enum)
+ content.add(self.substitute_code(header["code"]))
+ with content.doxygen_block():
+ content.add_brief_description(self["test-brief"])
+ content.wrap(self["test-description"])
+ content.add_param_description(header["run-params"])
+ content.gap = False
+ content.declare_function("void", f"{self.ident}_Run",
+ self._get_run_params(header))
+
+ def _generate_header(self, base_directory: str, header: Dict[str,
+ Any]) -> None:
+ content = CContent()
+ content.register_license_and_copyrights_of_item(self._item)
+ content.prepend_spdx_license_identifier()
+ with content.file_block():
+ content.add_ingroup([self.group_identifier])
+ content.add_copyrights_and_licenses()
+ with content.header_guard(os.path.basename(header["target"])):
+ content.add_includes(list(map(CInclude, header["includes"])))
+ content.add_includes(list(map(CInclude, header["local-includes"])),
+ local=True)
+ with content.extern_c():
+ with content.add_to_group(self.group_identifier):
+ self._generate_header_body(content, header)
+ content.write(os.path.join(base_directory, header["target"]))
+
+ def generate(self, content: CContent, base_directory: str,
+ test_case_to_suites: Dict[str, List[_TestItem]]) -> None:
+ self.add_test_case_description(content, test_case_to_suites)
+ header = self["test-header"]
+ if header:
+ self._generate_header(base_directory, header)
+ else:
+ _directive_add_enum(content, self._pre_index_to_enum)
+ _directive_add_enum(content, self._post_index_to_enum)
+ self._add_context(content, header)
+ self._add_pre_condition_descriptions(content)
+ content.add(self.substitute_code(self["test-support"]))
+ self._add_handler(content, self["pre-conditions"],
+ self._pre_index_to_enum, "Prepare")
+ self._add_handler(content, self["post-conditions"],
+ self._post_index_to_enum, "Check")
+ setup = self._add_fixture_method(content, self["test-setup"], "Setup")
+ stop = self._add_fixture_method(content, self["test-stop"], "Stop")
+ teardown = self._add_fixture_method(content, self["test-teardown"],
+ "Teardown")
+ self._add_fixture_scope(content)
+ content.add([
+ f"static T_fixture {self.ident}_Fixture = {{",
+ f" .setup = {setup},", f" .stop = {stop},",
+ f" .teardown = {teardown},", f" .scope = {self.ident}_Scope,",
+ f" .initial_context = &{self.ident}_Instance", "};"
+ ])
+ self._add_transition_map(content)
+ self._add_test_case(content, header)
+ content.add("/** @} */")
+
+
+class _SourceFile:
+ """ A test source file. """
+ def __init__(self, filename: str):
+ """ Initializes a test source file. """
+ self._file = filename
+ self._test_suites = [] # type: List[_TestItem]
+ self._test_cases = [] # type: List[_TestItem]
+
+ @property
+ def test_suites(self) -> List[_TestItem]:
+ """ The test suites of the source file. """
+ return self._test_suites
+
+ @property
+ def test_cases(self) -> List[_TestItem]:
+ """ The test cases of the source file. """
+ return self._test_cases
+
+ def add_test_suite(self, test_suite: Item) -> None:
+ """ Adds a test suite to the source file. """
+ self._test_suites.append(_TestSuiteItem(test_suite))
+
+ def add_test_case(self, test_case: Item) -> None:
+ """ Adds a test case to the source file. """
+ self._test_cases.append(_TestItem(test_case))
+
+ def add_test_directive(self, test_directive: Item) -> None:
+ """ Adds a test directive to the source file. """
+ self._test_cases.append(_TestDirectiveItem(test_directive))
+
+ def generate(self, base_directory: str,
+ test_case_to_suites: Dict[str, List[_TestItem]]) -> None:
+ """
+ Generates the source file and the corresponding build specification.
+ """
+ content = CContent()
+ includes = [] # type: List[CInclude]
+ local_includes = [] # type: List[CInclude]
+ for item in itertools.chain(self._test_suites, self._test_cases):
+ includes.extend(map(CInclude, item.includes))
+ local_includes.extend(map(CInclude, item.local_includes))
+ content.register_license_and_copyrights_of_item(item.item)
+ content.prepend_spdx_license_identifier()
+ with content.file_block():
+ _add_ingroup(content, self._test_suites)
+ _add_ingroup(content, self._test_cases)
+ content.add_copyrights_and_licenses()
+ content.add_have_config()
+ content.add_includes(includes)
+ content.add_includes(local_includes, local=True)
+ content.add_includes([CInclude("t.h")])
+ for item in sorted(self._test_cases, key=lambda x: x.name):
+ item.generate(content, base_directory, test_case_to_suites)
+ for item in sorted(self._test_suites, key=lambda x: x.name):
+ item.generate(content, base_directory, test_case_to_suites)
+ content.write(os.path.join(base_directory, self._file))
+
+
+class _TestProgram:
+ """ A test program. """
+ def __init__(self, item: Item):
+ """ Initializes a test program. """
+ self._item = item
+ self._source_files = [] # type: List[_SourceFile]
+
+ @property
+ def source_files(self) -> List[_SourceFile]:
+ """ The source files of the test program. """
+ return self._source_files
+
+ def add_source_files(self, source_files: Dict[str, _SourceFile]) -> None:
+ """
+ Adds the source files of the test program which are present in the
+ source file map.
+ """
+ for filename in self._item["source"]:
+ source_file = source_files.get(filename, None)
+ if source_file is not None:
+ self._source_files.append(source_file)
+
+
+def _get_source_file(filename: str,
+ source_files: Dict[str, _SourceFile]) -> _SourceFile:
+ return source_files.setdefault(filename, _SourceFile(filename))
+
+
+def _gather_items(item: Item, source_files: Dict[str, _SourceFile],
+ test_programs: List[_TestProgram]) -> None:
+ if item["type"] == "test-suite":
+ src = _get_source_file(item["target"], source_files)
+ src.add_test_suite(item)
+ elif item["type"] == "test-case":
+ src = _get_source_file(item["target"], source_files)
+ src.add_test_case(item)
+ elif item["type"] == "requirement" and item[
+ "requirement-type"] == "functional" and item[
+ "functional-type"] == "action":
+ src = _get_source_file(item["test-target"], source_files)
+ src.add_test_directive(item)
+ elif item["type"] == "build" and item["build-type"] == "test-program":
+ test_programs.append(_TestProgram(item))
+
+
+def generate(config: dict, item_cache: ItemCache) -> None:
+ """
+ Generates source files and build specification items for validation test
+ suites and test cases according to the configuration.
+
+ :param config: A dictionary with configuration entries.
+ :param item_cache: The specification item cache containing the validation
+ test suites and test cases.
+ """
+ source_files = {} # type: Dict[str, _SourceFile]
+ test_programs = [] # type: List[_TestProgram]
+ for item in item_cache.all.values():
+ _gather_items(item, source_files, test_programs)
+
+ test_case_to_suites = {} # type: Dict[str, List[_TestItem]]
+ for test_program in test_programs:
+ test_program.add_source_files(source_files)
+ test_suites = [] # type: List[_TestItem]
+ for source_file in test_program.source_files:
+ test_suites.extend(source_file.test_suites)
+ for source_file in test_program.source_files:
+ for test_case in source_file.test_cases:
+ test_case_to_suites.setdefault(test_case.uid,
+ []).extend(test_suites)
+
+ for src in source_files.values():
+ src.generate(config["base-directory"], test_case_to_suites)