summaryrefslogtreecommitdiff
path: root/rtemsspec/tests/test_directorystate.py
diff options
context:
space:
mode:
Diffstat (limited to 'rtemsspec/tests/test_directorystate.py')
-rw-r--r--rtemsspec/tests/test_directorystate.py361
1 files changed, 361 insertions, 0 deletions
diff --git a/rtemsspec/tests/test_directorystate.py b/rtemsspec/tests/test_directorystate.py
new file mode 100644
index 00000000..3b998ad1
--- /dev/null
+++ b/rtemsspec/tests/test_directorystate.py
@@ -0,0 +1,361 @@
+# SPDX-License-Identifier: BSD-2-Clause
+""" Tests for the rtemsspec.directorystate module. """
+
+# Copyright (C) 2020, 2023 embedded brains GmbH & Co. KG
+#
+# 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 os
+import pytest
+
+from rtemsspec.items import Item, EmptyItemCache, Link
+from rtemsspec.directorystate import DirectoryState
+from rtemsspec.packagebuild import BuildItemFactory, PackageBuildDirector
+from rtemsspec.tests.util import get_and_clear_log
+
+
+class _TestState(DirectoryState):
+ pass
+
+
+@pytest.fixture
+def _change_cwd():
+ cwd = os.getcwd()
+ os.chdir(os.path.dirname(__file__))
+ yield
+ os.chdir(cwd)
+
+
+_DOC_RST_HASH = "Cm41zmS2o7TF6FBxnQxWxmPDVhufFst7pFkkQriQnEOwJWXS_zjEwKLVsgBT4L-v1iWzRUCilifIdY4uqkg5Gw=="
+_T_YML_HASH = "_FTeBKV04q5fMTETF65lBzv6dNeHTMLT3dZmHF1BEAOLtmxvPdAJc_7-RDmGRiv3GU_uddvkFc005S0EeSx0PA=="
+_INCLUDE_ALL = [{"include": "**/*", "exclude": []}]
+
+
+def test_directorystate(caplog, tmpdir, _change_cwd):
+ item_cache = EmptyItemCache()
+ factory = BuildItemFactory()
+ factory.add_constructor("qdp/directory-state/generic", _TestState)
+ director = PackageBuildDirector(item_cache, factory)
+ base = "spec-glossary"
+
+ data = {
+ "SPDX-License-Identifier":
+ "CC-BY-SA-4.0 OR BSD-2-Clause",
+ "copyrights":
+ ["Copyright (C) 2020, 2023 embedded brains GmbH & Co. KG"],
+ "copyrights-by-license": {},
+ "directory":
+ base,
+ "enabled-by":
+ True,
+ "files": [
+ {
+ "file": "doc.rst",
+ "hash": None
+ },
+ {
+ "file": "glossary/t.yml",
+ "hash": None
+ },
+ ],
+ "hash":
+ None,
+ "links": [],
+ "patterns": [],
+ }
+ item = item_cache.add_volatile_item("/directory-state", data)
+ item["_type"] = "qdp/directory-state/generic"
+ item_file = os.path.join(tmpdir, "item.yml")
+ item.file = str(item_file)
+ dir_state = director["/directory-state"]
+ assert dir_state.directory == base
+ with pytest.raises(ValueError):
+ dir_state.digest
+ with pytest.raises(ValueError):
+ dir_state.has_changed(Link(item, {"hash": "blub"}))
+ overall_hash = dir_state.lazy_load()
+ assert overall_hash == "SrJDe4-ewVrM9BV9ttASllPsrXz2r_-ts9urtVeBa9s7JuBORQrvuPyW-hvsef80a8HvKvfeNSOmAh2eQ2_aag=="
+ assert dir_state.digest == overall_hash
+ dir_state.save()
+ with open(item_file, "r") as src:
+ assert f"""SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2020, 2023 embedded brains GmbH & Co. KG
+copyrights-by-license: {{}}
+directory: {base}
+enabled-by: true
+files:
+- file: doc.rst
+ hash: {_DOC_RST_HASH}
+- file: glossary/t.yml
+ hash: {_T_YML_HASH}
+hash: SrJDe4-ewVrM9BV9ttASllPsrXz2r_-ts9urtVeBa9s7JuBORQrvuPyW-hvsef80a8HvKvfeNSOmAh2eQ2_aag==
+links: []
+patterns: []
+""" == src.read()
+ assert dir_state.file == "spec-glossary/doc.rst"
+ assert dir_state.substitute("${.:/file}") == "spec-glossary/doc.rst"
+ assert dir_state.substitute("${.:/file[0]}") == "spec-glossary/doc.rst"
+ assert dir_state.substitute(
+ "${.:/file[1]}") == "spec-glossary/glossary/t.yml"
+ assert dir_state.substitute(
+ "${.:/file-without-extension[1]}") == "spec-glossary/glossary/t"
+ assert list(dir_state.files(".")) == ["./doc.rst", "./glossary/t.yml"]
+ assert list(dir_state.files()) == [
+ str(os.path.join(base, "doc.rst")),
+ str(os.path.join(base, "glossary/t.yml"))
+ ]
+ assert list(dir_state.files_and_hashes(".")) == [
+ ("./doc.rst", _DOC_RST_HASH), ("./glossary/t.yml", _T_YML_HASH)
+ ]
+ assert list(dir_state.files_and_hashes()) == [
+ (str(os.path.join(base, "doc.rst")), _DOC_RST_HASH),
+ (str(os.path.join(base, "glossary/t.yml")), _T_YML_HASH)
+ ]
+
+ dir_state.set_files(["doc.rst"])
+ dir_state.add_files(["glossary/t.yml"])
+ with pytest.raises(ValueError):
+ dir_state.digest
+ overall_hash = dir_state.lazy_load()
+ assert overall_hash == "SrJDe4-ewVrM9BV9ttASllPsrXz2r_-ts9urtVeBa9s7JuBORQrvuPyW-hvsef80a8HvKvfeNSOmAh2eQ2_aag=="
+ overall_hash = dir_state.lazy_load()
+ assert overall_hash == "SrJDe4-ewVrM9BV9ttASllPsrXz2r_-ts9urtVeBa9s7JuBORQrvuPyW-hvsef80a8HvKvfeNSOmAh2eQ2_aag=="
+
+ caplog.set_level(logging.DEBUG)
+ data_2 = {
+ "directory": base,
+ "enabled-by": True,
+ "files": [],
+ "links": [{
+ "role": "directory-state-exclude",
+ "uid": "directory-state"
+ }],
+ "patterns": [],
+ }
+ item_2 = item_cache.add_volatile_item("/directory-state-2", data_2)
+ dir_state_2 = DirectoryState(director, item_2)
+ dir_state_2.add_files(
+ os.path.relpath(path, dir_state_2.directory) for path in dir_state)
+ assert list(dir_state_2.files()) == []
+ dir_state_2.set_files(
+ os.path.relpath(path, dir_state_2.directory) for path in dir_state)
+ assert list(dir_state_2.files()) == []
+ dir_state_2.set_files([])
+ assert list(dir_state_2.files()) == []
+ with pytest.raises(ValueError):
+ dir_state_2.digest
+ overall_hash = dir_state_2.load()
+ assert overall_hash == "YtmDhTiLc9q20OthwE35dnsoPQz5gkQqajQQC2K3h5_yzY67hX35LlnhuR_kEx-_blEsjQlT1ijdP5YwUwb3bw=="
+
+ dir_state_2["patterns"] = _INCLUDE_ALL
+ overall_hash = dir_state_2.load()
+ assert overall_hash == "GSGvDhHq3M-csmWHrXBLJPB7yFB1hjxiZt3hROQP_dltVlHvCslNii9PzzbSiEYCEsi5qnOUp1OOs916PUvX4g=="
+
+ item["patterns"] = [{
+ "include": "**/*",
+ "exclude": ["*/glossary.rst", "*/[guv].yml", "*/sub/*"]
+ }]
+ overall_hash = dir_state.load()
+ assert overall_hash == "SrJDe4-ewVrM9BV9ttASllPsrXz2r_-ts9urtVeBa9s7JuBORQrvuPyW-hvsef80a8HvKvfeNSOmAh2eQ2_aag=="
+
+ item["patterns"] = [{
+ "include": "**/doc.rst",
+ "exclude": []
+ }, {
+ "include": "**/t.yml",
+ "exclude": []
+ }]
+ overall_hash = dir_state.load()
+ assert overall_hash == "SrJDe4-ewVrM9BV9ttASllPsrXz2r_-ts9urtVeBa9s7JuBORQrvuPyW-hvsef80a8HvKvfeNSOmAh2eQ2_aag=="
+
+ item["patterns"] = [{"include": "**/foo", "exclude": []}]
+ overall_hash = dir_state.load()
+ assert overall_hash == "YtmDhTiLc9q20OthwE35dnsoPQz5gkQqajQQC2K3h5_yzY67hX35LlnhuR_kEx-_blEsjQlT1ijdP5YwUwb3bw=="
+
+ caplog.set_level(logging.DEBUG)
+ data_3 = {
+ "directory": str(tmpdir),
+ "enabled-by": True,
+ "patterns": [],
+ "files": [],
+ "links": [],
+ }
+ item_3 = item_cache.add_volatile_item("/directory-state-3", data_3)
+ item_3["_type"] = "qdp/directory-state/generic"
+ item_3_file = os.path.join(tmpdir, "item-3.yml")
+ item_3.file = str(item_3_file)
+ dir_state_3 = director["/directory-state-3"]
+
+ src_file = os.path.join(base, "doc.rst")
+ dir_state_3.copy_file(src_file, "doc.rst")
+ dst_file = os.path.join(tmpdir, "doc.rst")
+ assert os.path.exists(dst_file)
+ log = get_and_clear_log(caplog)
+ assert f"INFO /directory-state-3: copy '{src_file}' to '{dst_file}'" in log
+ dir_state_3.load()
+
+ dir_state_3.remove_files()
+ assert not os.path.exists(dst_file)
+ log = get_and_clear_log(caplog)
+ assert f"INFO /directory-state-3: remove: {dst_file}" in log
+
+ dir_state_3.remove_files()
+ log = get_and_clear_log(caplog)
+ assert f"DEBUG /directory-state-3: file not found: {dst_file}" in log
+
+ dir_state_3["patterns"] = _INCLUDE_ALL
+ dir_state_3.remove_files()
+ dir_state_3["patterns"] = []
+ log = get_and_clear_log(caplog)
+ assert f"WARNING /directory-state-3: file not found: {dst_file}" in log
+
+ assert dir_state_3.digest
+ assert list(dir_state_3.files_and_hashes()) == [(str(dst_file),
+ _DOC_RST_HASH)]
+ dir_state_3.invalidate()
+ with pytest.raises(ValueError):
+ dir_state_3.digest
+ assert list(dir_state_3.files_and_hashes()) == [(str(dst_file), None)]
+ dir_state_3["patterns"] = _INCLUDE_ALL
+ dir_state_3.invalidate()
+ dir_state_3["patterns"] = []
+ assert list(dir_state_3.files_and_hashes()) == []
+
+ dir_state_3.copy_tree(base, "x")
+ for path in [
+ "doc.rst", "g.yml", "glossary.rst", "glossary/sub/g.yml",
+ "glossary/sub/x.yml", "glossary/t.yml", "glossary/u.yml",
+ "glossary/v.yml"
+ ]:
+ assert os.path.exists(os.path.join(tmpdir, "x", path))
+
+ dir_state_3["patterns"] = _INCLUDE_ALL
+ dir_state_3.invalidate()
+ dir_state_3["patterns"] = []
+ dir_state_3.add_tree(os.path.join("spec-glossary", "glossary", "sub"),
+ excludes=["/x.*"])
+ assert list(dir_state_3.files()) == [f"{tmpdir}/g.yml"]
+ assert not os.path.exists(os.path.join(tmpdir, "g.yml"))
+ assert os.path.exists(os.path.join(tmpdir, "x", "glossary", "sub",
+ "g.yml"))
+ dir_state_3.move_tree(os.path.join(tmpdir, "x", "glossary", "sub"))
+ assert list(dir_state_3.files()) == [f"{tmpdir}/g.yml", f"{tmpdir}/x.yml"]
+ assert not os.path.exists(
+ os.path.join(tmpdir, "x", "glossary", "sub", "g.yml"))
+ assert os.path.exists(os.path.join(tmpdir, "g.yml"))
+
+ link = Link(item_3, {"hash": None})
+ dir_state_3.load()
+ assert dir_state_3.has_changed(link)
+ dir_state_3.refresh_link(link)
+ assert not dir_state_3.has_changed(link)
+
+ dir_state_3.discard()
+ log = get_and_clear_log(caplog)
+ assert f"INFO /directory-state-3: discard" in log
+
+ dir_state_3.clear()
+ dir_state_3.refresh()
+ log = get_and_clear_log(caplog)
+ assert f"INFO /directory-state-3: refresh" in log
+
+ dir_state_3.clear()
+ dir_state_3.add_tarfile_members("test-files/archive.tar.xz", tmpdir, False)
+ assert list(dir_state_3.files()) == [
+ f"{tmpdir}/member-dir/dir-member.txt", f"{tmpdir}/member.txt"
+ ]
+ assert not os.path.exists(os.path.join(tmpdir, "member.txt"))
+ dir_state_3.add_tarfile_members("test-files/archive.tar.xz", tmpdir, True)
+ assert list(dir_state_3.files()) == [
+ f"{tmpdir}/member-dir/dir-member.txt", f"{tmpdir}/member.txt"
+ ]
+ assert os.path.exists(os.path.join(tmpdir, "member.txt"))
+
+ dir_state_3.clear()
+ src_file = os.path.join(base, "doc.rst")
+ dir_state_3.copy_files(base, ["doc.rst"], "uvw")
+ dst_file = os.path.join(tmpdir, "uvw", "doc.rst")
+ assert os.path.exists(dst_file)
+ log = get_and_clear_log(caplog)
+ assert f"INFO /directory-state-3: copy '{src_file}' to '{dst_file}'" in log
+ assert list(name for name in dir_state_3) == [dst_file]
+
+ symlink = os.path.join(tmpdir, "symlink")
+ os.symlink("foobar", symlink)
+ dir_state_3.set_files(["symlink"])
+ dir_state_3.load()
+ assert list(dir_state_3.files_and_hashes()) == [(
+ symlink,
+ "ClAmHr0aOQ_tK_Mm8mc8FFWCpjQtUjIElz0CGTN_gWFqgGmwElh89WNfaSXxtWw2AjDBmyc1AO4BPgMGAb8kJQ=="
+ )]
+
+ get_and_clear_log(caplog)
+
+ item["patterns"] = [{"include": "**/t.yml", "exclude": []}]
+ dir_state.load()
+ dir_state_3.lazy_clone(dir_state)
+ assert list(dir_state_3.files(".")) == ["./glossary/t.yml"]
+ log = get_and_clear_log(caplog)
+ assert f"INFO /directory-state-3: copy" in log
+
+ item["patterns"] = [{"include": "**/x.yml", "exclude": []}]
+ dir_state.load()
+ dir_state_3.lazy_clone(dir_state)
+ assert list(dir_state_3.files(".")) == ["./glossary/sub/x.yml"]
+ log = get_and_clear_log(caplog)
+ assert f"INFO /directory-state-3: remove" in log
+
+ os.unlink(dir_state_3.file)
+ item["patterns"] = [{"include": "**/t.yml", "exclude": []}]
+ dir_state.load()
+ dir_state_3.lazy_clone(dir_state)
+ log = get_and_clear_log(caplog)
+ assert f"WARNING /directory-state-3: file not found" in log
+
+ dir_state_3.invalidate()
+ dir_state_3.lazy_clone(dir_state)
+ log = get_and_clear_log(caplog)
+ assert f"INFO /directory-state-3: copy" in log
+
+ dir_state_3.lazy_clone(dir_state)
+ assert list(dir_state_3.files(".")) == ["./glossary/t.yml"]
+ log = get_and_clear_log(caplog)
+ assert f"INFO /directory-state-3: keep as is" in log
+
+ assert dir_state_3.directory == tmpdir
+ dir_state_3.set_files(["/a/b", "/a/c"])
+ dir_state_3.compact()
+ assert dir_state_3.directory == tmpdir
+ dir_state_3.set_files(["a/b", "c/d"])
+ dir_state_3.compact()
+ assert dir_state_3.directory == tmpdir
+ dir_state_3.set_files(["a/b", "a/c"])
+ dir_state_3.compact()
+ assert dir_state_3.directory == f"{tmpdir}/a"
+
+ dir_state_3.set_files(["data.json"])
+ assert not os.path.exists(dir_state_3.file)
+ dir_state_3.json_dump({"foo": "bar"})
+ assert os.path.exists(dir_state_3.file)
+ assert dir_state_3.json_load() == {"foo": "bar"}