Merge #1567 Module Stream Expansion integration test

This commit is contained in:
Hunor Csomortáni
2020-01-23 13:28:24 +00:00
11 changed files with 133 additions and 73 deletions

View File

@@ -28,6 +28,16 @@ def test_env():
return env
@pytest.fixture(scope="session")
def pkg_util(test_env):
"""Fixture to interact with the packaging utility
:return: Packaging utility configured for the tests
:rtype: object of utils.PackagingUtility
"""
return utils.PackagingUtility(test_env["packaging_utility"], test_env["mbs_api"])
@pytest.fixture(scope="function")
def scenario(request, test_env):
"""Configuration data for the scenario

View File

@@ -64,3 +64,9 @@ testdata:
no_components:
module: testmodule
branch: test-no-components-branch
stream_expansion:
# testmodule2 buildrequires and requires 2 streams from testmodule.
# These are expected to be built already.
# For this scenario reusing former builds doesn't make sense.
module: testmodule2
branch: test-stream-expans-branch

View File

@@ -1,10 +1,8 @@
# -*- coding: utf-8 -*-
# SPDX-License-Identifier: MIT
import utils
def test_failed_build(test_env, scenario, repo, koji):
def test_failed_build(pkg_util, scenario, repo, koji):
"""
Run the build with "rebuild_strategy=all".
@@ -13,14 +11,15 @@ def test_failed_build(test_env, scenario, repo, koji):
* Check that any other components in the same batch as the failed component are
cancelled, if not completed.
"""
build = utils.Build(test_env["packaging_utility"], test_env["mbs_api"])
repo.bump()
build.run(
builds = pkg_util.run(
"--optional",
"rebuild_strategy=all",
reuse=scenario.get("build_id"),
)
build.watch()
assert len(builds) == 1
build = builds[0]
pkg_util.watch(builds)
assert build.state_name == "failed"
batch = scenario["batch"]

View File

@@ -1,10 +1,8 @@
# -*- coding: utf-8 -*-
# SPDX-License-Identifier: MIT
import utils
def test_no_components(test_env, scenario, repo, koji):
def test_no_components(pkg_util, scenario, repo, koji):
"""
Submit the testmodule build with `fedpkg module-build`
@@ -13,10 +11,12 @@ def test_no_components(test_env, scenario, repo, koji):
* Verify that the testmodule build succeeds
"""
build = utils.Build(test_env["packaging_utility"], test_env["mbs_api"])
repo.bump()
build.run(reuse=scenario.get("build_id"))
build.watch()
builds = pkg_util.run(reuse=scenario.get("build_id"))
assert len(builds) == 1
pkg_util.watch(builds)
build = builds[0]
assert build.state_name == "ready"
assert not build.data["component_builds"]

View File

@@ -1,10 +1,8 @@
# -*- coding: utf-8 -*-
# SPDX-License-Identifier: MIT
import utils
def test_normal_build(test_env, scenario, repo, koji):
def test_normal_build(pkg_util, scenario, repo, koji):
"""
Run build with `rhpkg-stage module-build --optional rebuild_strategy=all`
@@ -17,14 +15,15 @@ def test_normal_build(test_env, scenario, repo, koji):
* Check that MBS changed the buildrequired platform to have a suffix of “z”
if a Platform stream is representing a GA RHEL release.
"""
build = utils.Build(test_env["packaging_utility"], test_env["mbs_api"])
repo.bump()
build_id = build.run(
builds = pkg_util.run(
"--optional",
"rebuild_strategy=all",
reuse=scenario.get("build_id"),
)
build.watch()
assert len(builds) == 1
pkg_util.watch(builds)
build = builds[0]
assert sorted(build.component_names()) == sorted(repo.components + ["module-build-macros"])
@@ -36,7 +35,7 @@ def test_normal_build(test_env, scenario, repo, koji):
cg_build = koji.get_build(build.nvr())
cg_devel_build = koji.get_build(build.nvr(name_suffix="-devel"))
assert cg_build and cg_devel_build
assert cg_devel_build['extra']['typeinfo']['module']['module_build_service_id'] == int(build_id)
assert cg_devel_build['extra']['typeinfo']['module']['module_build_service_id'] == int(build.id)
modulemd = koji.get_modulemd(cg_build)
actual_platforms = modulemd["data"]["dependencies"][0]["buildrequires"]["platform"]

View File

@@ -1,11 +1,10 @@
# -*- coding: utf-8 -*-
# SPDX-License-Identifier: MIT
import utils
import time
def test_resume_cancelled_build(test_env, scenario, repo, koji):
def test_resume_cancelled_build(pkg_util, scenario, repo, koji):
"""
Run the build with "rebuild_strategy=all".
Wait until the module-build-macros build is submitted to Koji.
@@ -17,17 +16,20 @@ def test_resume_cancelled_build(test_env, scenario, repo, koji):
* Check that the testmodule build succeeded
"""
build = utils.Build(test_env["packaging_utility"], test_env["mbs_api"])
repo.bump()
build.run(
builds = pkg_util.run(
"--optional",
"rebuild_strategy=all",
)
assert len(builds) == 1
build = builds[0]
build.wait_for_koji_task_id(package="module-build-macros", batch=1)
build.cancel()
pkg_util.cancel(build)
# Behave like a human: restarting the build too quickly would lead to an error.
time.sleep(10)
build.run("--watch")
builds = pkg_util.run("--watch")
assert len(builds) == 1
build = builds[0]
assert build.state_name == "ready"
assert build.was_cancelled()

View File

@@ -1,10 +1,8 @@
# -*- coding: utf-8 -*-
# SPDX-License-Identifier: MIT
import utils
def test_reuse_all_components(test_env, scenario, repo, koji):
def test_reuse_all_components(pkg_util, scenario, repo, koji):
"""Rebuild the test module again, without changing any of the components with:
`fedpkg module-build -w --optional rebuild_strategy=only-changed`
@@ -13,23 +11,28 @@ def test_reuse_all_components(test_env, scenario, repo, koji):
* Verify that all the components are reused from the first build.
* Verify that module-build-macros is not built in the second build.
"""
build = utils.Build(test_env["packaging_utility"], test_env["mbs_api"])
repo.bump()
build.run(
builds = pkg_util.run(
"--watch",
"--optional",
"rebuild_strategy=all",
reuse=scenario.get("build_id"),
)
assert len(builds) == 1
build = builds[0]
task_ids = build.component_task_ids()
task_ids.pop("module-build-macros")
repo.bump()
build.run(
builds = pkg_util.run(
"-w",
"--optional",
"rebuild_strategy=only-changed",
reuse=scenario.get("build_id_reused"))
assert len(builds) == 1
build = builds[0]
reused_task_ids = build.component_task_ids()
assert not build.components(package="module-build-macros")

View File

@@ -4,7 +4,7 @@
import utils
def test_reuse_components(test_env, scenario, repo, koji):
def test_reuse_components(pkg_util, test_env, scenario, repo, koji):
"""
Bump the commit of one of the components that MBS uses.
Bump the commit of the same testmodule that was mentioned in the preconditions.
@@ -16,14 +16,15 @@ def test_reuse_components(test_env, scenario, repo, koji):
* Verify that the component with the changed commit was rebuilt.
"""
repo.bump()
baseline_build = utils.Build(test_env["packaging_utility"], test_env["mbs_api"])
baseline_build.run(
baseline_builds = pkg_util.run(
"--watch",
"--optional",
"rebuild_strategy=all",
reuse=scenario.get("baseline_build_id"),
)
assert len(baseline_builds) == 1
baseline_build = baseline_builds[0]
package = scenario.get("package")
component = utils.Component(
package,
@@ -33,14 +34,14 @@ def test_reuse_components(test_env, scenario, repo, koji):
component.bump()
repo.bump()
build = utils.Build(test_env["packaging_utility"], test_env["mbs_api"])
build.run(
builds = pkg_util.run(
"--watch",
"--optional",
"rebuild_strategy=only-changed",
reuse=scenario.get("build_id"),
)
assert len(builds) == 1
build = builds[0]
comp_task_ids_base = baseline_build.component_task_ids()
comp_task_ids = build.component_task_ids()
comp_task_ids_base.pop('module-build-macros')

View File

@@ -1,10 +1,8 @@
# -*- coding: utf-8 -*-
# SPDX-License-Identifier: MIT
import utils
def test_scratch_build(test_env, scenario, repo, koji):
def test_scratch_build(pkg_util, scenario, repo, koji):
"""
Run a scratch build with "rebuild_strategy=all".
@@ -14,14 +12,16 @@ def test_scratch_build(test_env, scenario, repo, koji):
(as opposed to the "ready" state)
* no content generator builds are created in Koji
"""
build = utils.Build(test_env["packaging_utility"], test_env["mbs_api"])
build.run(
builds = pkg_util.run(
"--scratch",
"--optional",
"rebuild_strategy=all",
reuse=scenario.get("build_id"),
)
build.watch()
assert len(builds) == 1
pkg_util.watch(builds)
build = builds[0]
assert build.state_name == "done"
assert sorted(build.component_names(state="COMPLETE")) == sorted(

View File

@@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
# SPDX-License-Identifier: MIT
def test_stream_expansion(pkg_util, scenario, repo, koji):
"""
Submit the testmodule2 build with `rhpkg-stage module-build`
The produced builds can be cancelled after the test cases have been verified to save on
resources and time
Checks:
* Verify two module builds were generated from this build submission
"""
repo.bump()
builds = pkg_util.run()
assert len(builds) == 2
for build in builds:
pkg_util.cancel(build)

View File

@@ -114,15 +114,12 @@ class Repo:
git("push")
class Build:
"""Wrapper class to work with module builds
class PackagingUtility:
"""Wrapper class to work with the packaging utility configured for the tests
:attribute sh.Command _packaging_utility: packaging utility command used to
kick off this build
:attribute string _mbs_api: URL of the MBS API (including trailing '/')
:attribute string _url: URL of this MBS module build
:attribute string _data: Module build data cache for this build fetched from MBS
:attribute string _module_build_data: Verbose module build data cache for this build
"""
def __init__(self, packaging_utility, mbs_api):
@@ -130,54 +127,76 @@ class Build:
_out=sys.stdout, _err=sys.stderr, _tee=True
)
self._mbs_api = mbs_api
self._data = None
self._component_data = None
self._build_id = None
self._module_build_data = None
def run(self, *args, reuse=None):
"""Run a module build
"""Run one or more module builds
:param args: Options and arguments for the build command
:param int reuse: Optional MBS build id to be reused for this run.
When specified, the corresponding module build will be used,
instead of triggering and waiting for a new one to finish.
:param int reuse: An optional MBS build id or a list of MBS build
ids to be reused for this run.
When specified, the corresponding module build(s) will be used,
instead of triggering and waiting for new one(s) to finish.
Intended to be used while developing the tests.
:return: MBS build id of the build created
:rtype: int
:return: list of Build objects for the MBS builds created
:rtype: list of Build objects
"""
current_build_id = self._build_id
build_ids = []
if reuse is not None:
self._build_id = int(reuse)
if isinstance(reuse, list):
build_ids = reuse
else:
build_ids = [reuse]
else:
stdout = self._packaging_utility("module-build", *args).stdout.decode("utf-8")
self._build_id = int(re.search(self._mbs_api + r"module-builds/(\d+)", stdout).group(1))
# Clear cached data
if current_build_id != self._build_id:
self._component_data = None
return self._build_id
build_ids = re.findall(self._mbs_api + r"module-builds/(\d+)", stdout)
return [Build(self._mbs_api, int(build_id)) for build_id in build_ids]
def watch(self):
"""Watch the build till the finish"""
if self._build_id is None:
raise RuntimeError("Build was not started. Cannot watch.")
def watch(self, builds):
"""Watch one or more builds till the finish
:param list builds: list of Build objects of the builds to be watched.
:return: Stdout of the watch command
:rtype: string
"""
stdout = self._packaging_utility(
"module-build-watch", str(self._build_id)
"module-build-watch", [str(build.id) for build in builds]
).stdout.decode("utf-8")
return stdout
def cancel(self):
def cancel(self, build):
"""Cancel the module build
:param list build: the Build object of the module build to be cancelled.
:return: Standard output of the "module-build-cancel <build id=""> command
:rtype: str
"""
stdout = self._packaging_utility("module-build-cancel", self._build_id).stdout.decode(
stdout = self._packaging_utility("module-build-cancel", build.id).stdout.decode(
"utf-8")
return stdout
class Build:
"""Wrapper class to work with module builds
:attribute string _mbs_api: URL of the MBS API (including trailing '/')
:attribute int _build_id: id of this MBS module build
:attribute string _data: Module build data cache for this build fetched from MBS
:attribute string _module_build_data: Verbose module build data cache for this build
"""
def __init__(self, mbs_api, build_id):
self._mbs_api = mbs_api
self._data = None
self._component_data = None
self._build_id = build_id
self._module_build_data = None
@property
def id(self):
return self._build_id
@property
def data(self):
"""Module build data cache for this build fetched from MBS"""