From 87c83ad0b8a29ccfdf18b1589ab760f2b9672239 Mon Sep 17 00:00:00 2001 From: jobrauer Date: Aug 07 2020 13:47:39 +0000 Subject: Add test 'reuse components if added' --- diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index aa4295a..999d255 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -49,7 +49,10 @@ def scenario(request, test_env): scenario configuration. """ scenario_name = request.function.__name__.split("test_", 1)[1] - return test_env["testdata"][scenario_name] + scenario = test_env["testdata"].get(scenario_name) + if not scenario: + pytest.skip("No test scenario in 'test.env' for: {}".format(request.function.__name__)) + return scenario @pytest.fixture(scope="function") diff --git a/tests/integration/example.test.env.yaml b/tests/integration/example.test.env.yaml index 97318be..420eab7 100644 --- a/tests/integration/example.test.env.yaml +++ b/tests/integration/example.test.env.yaml @@ -115,3 +115,21 @@ testdata: build_id: 3345 module: testmodule branch: test-scratch-final-mmd + reuse_components_if_removed_1: + #... + reuse_components_if_removed_n: + #... + reuse_components_if_added_1: + #... + reuse_components_if_added_n: + # reuse_components_if_added_n or reuse_components_if_removed_n, where n = {1, 2, ...} + # Same test case divided into different test functions to enable parallel execution. + # If any of these scenarios is missing, the appropriate test will simply be skipped. + module: testmodule + branch: test-reuse-components-if-added-2 + # indexes of RPMs found in the test branch modulemd + first_build: [0, 2] + second_build: [0, 1, 2] + expected_reused: [0] + expected_rebuilt: [1, 2] + rebuild_strategy: "changed-and-after" diff --git a/tests/integration/test_reuse_components_if_added.py b/tests/integration/test_reuse_components_if_added.py new file mode 100644 index 0000000..ae5cf77 --- /dev/null +++ b/tests/integration/test_reuse_components_if_added.py @@ -0,0 +1,96 @@ +import copy + + +def extracted_test_function(repo, pkg_util, scenario): + """Test if previous components are reused properly, after components are added/removed. + + Prerequisites: + EMPTY (no components/RPMs) module MD file + Steps: + 1) Make changes to the modulemd according to test scenario (first build) + 2) Make a clean build (rebuild strategy : all). + 3) Add/remove RPMs according to test scenario (second build) + 4) Rebuild, rebuild strategy according to test scenario + 5) Revert to the initial state. + + :param utils.Repo repo: repo fixture + :param utils.PackagingUtility pkg_util: pkg_util fixture + :param dict scenario: see example.test.env + """ + test_rpms = repo.components + + # Prepare test data from test.env scenario + first_build_rpms = {test_rpms[i] for i in scenario["first_build"]} + second_build_rpms = {test_rpms[i] for i in scenario["second_build"]} + expected_reused = {test_rpms[i] for i in scenario["expected_reused"]} + expected_rebuilt = {test_rpms[i] for i in scenario["expected_rebuilt"]} + rebuild_strategy = scenario["rebuild_strategy"] + + # Save initial state + original_metadata = repo.modulemd + original_rpms = repo.modulemd["data"]["components"]["rpms"] + + # Prepare initial build metadata & push + tmp_metadata = copy.deepcopy(original_metadata) + tmp_metadata["data"]["components"]["rpms"] = {} + for rpm in first_build_rpms: + tmp_metadata["data"]["components"]["rpms"][rpm] = original_rpms[rpm] + + repo.write_to_modulemd(tmp_metadata) + repo.add_all_commit_and_push(f'1st build: "{first_build_rpms}"') + + try: + # Make an initial build (to be later reused) + builds = pkg_util.run("--watch", "--optional", "rebuild_strategy=all") + assert len(builds) == 1, "Initial build failed!" + + # Prepare 2nd build metadata & push + tmp_metadata["data"]["components"]["rpms"] = {} + for rpm in second_build_rpms: + tmp_metadata["data"]["components"]["rpms"][rpm] = original_rpms[rpm] + repo.write_to_modulemd(tmp_metadata) + repo.add_all_commit_and_push(f'2nd build: "{second_build_rpms}"') + + # Make a new build + builds = pkg_util.run("--watch", "--optional", f"rebuild_strategy={rebuild_strategy}") + assert len(builds) == 1, "Second (re)build failed!" + + build = builds[0] + # we don"t care about module-build-macros + build_components = [c for c in build.components() if c["package"] != "module-build-macros"] + + # Partition components by "reused" state - package name only + reused_msg = "Reused component from previous module build" + actually_reused = { + c["package"] for c in build_components if c["state_reason"] == reused_msg + } + actually_rebuilt = { + c["package"] for c in build_components if c["state_reason"] != reused_msg + } + + assert actually_reused == expected_reused + assert actually_rebuilt == expected_rebuilt + + finally: # Revert the change + repo.write_to_modulemd(original_metadata) + repo.add_all_commit_and_push("Revert") + + +# Each function needs its own test branch due to parallel execution. +# Module branch provides data - i.e. RPM components - and test.env provides +# scenario to be executed, i.e. which RPMs go to the 1st and 2nd build +# as well as expected result - see test.env example. +def test_reuse_components_if_added_1(repo, pkg_util, scenario): + extracted_test_function(repo, pkg_util, scenario) + + +def test_reuse_components_if_added_2(repo, pkg_util, scenario): + extracted_test_function(repo, pkg_util, scenario) + + +def test_reuse_components_if_removed_1(repo, pkg_util, scenario): + extracted_test_function(repo, pkg_util, scenario) + + +def test_reuse_components_if_removed_2(repo, pkg_util, scenario): + extracted_test_function(repo, pkg_util, scenario) diff --git a/tests/integration/utils.py b/tests/integration/utils.py index 4e618b5..a94c552 100644 --- a/tests/integration/utils.py +++ b/tests/integration/utils.py @@ -125,6 +125,11 @@ class Repo: self._version = None @property + def modulemd_path(self): + """Modulemd file absolute path""" + return os.path.join(os.path.abspath(os.curdir), self.module_name + ".yaml") + + @property def modulemd(self): """Modulemd file as read from the repo @@ -160,6 +165,20 @@ class Repo: elif self._version == 2: return self._modulemd["data"]["dependencies"][0]["buildrequires"].get("platform") + def write_to_modulemd(self, content_yaml): + """Write new content (replace) to the modulemd file + + :param dict content_yaml: module metadata dictionary + """ + with open(self.modulemd_path, 'w') as md_file: + yaml.dump(content_yaml, md_file, sort_keys=False) + + def add_all_commit_and_push(self, message="Bump"): + """Add all current changes and commit with a custom message""" + git("add", "--all") + git("commit", "--allow-empty", "-m", message) + git("push") + def bump(self): """Create a "bump" commit""" args = [