Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Various test improvement, especially for testing minimal templates #639

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
30 changes: 27 additions & 3 deletions qubes/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,22 @@ def skipUnlessEnv(varname):
return unittest.skipUnless(os.getenv(varname), "no {} set".format(varname))


def skipIfTemplate(*templates):
"""Decorator generator for skipping on specific templates.

Some tests are supported only on some templates. This decorator allows
excluding test for some of them, especially useful for excluding tests on
minimal templates or Whonix.
Multiple templates can be given.
"""

def decorator(func):
func.__qubestest_skip_templates__ = templates
return func

return decorator


class TestEmitter(qubes.events.Emitter):
"""Dummy event emitter which records events fired on it.

Expand Down Expand Up @@ -439,9 +455,17 @@ class QubesTestCase(unittest.TestCase):
def __init__(self, methodName="runTest"):
try:
test_method = getattr(self, methodName)
setattr(
self, methodName, _clear_ex_info(self.set_result)(test_method)
)
skip_templates = getattr(
self, "__qubestest_skip_templates__", []
) or getattr(test_method, "__qubestest_skip_templates__", [])
template = getattr(self, "template", "")
if any(skip in template for skip in skip_templates):
test_method = unittest.skip(
f"Test skipped on template {template}"
)(test_method)
else:
test_method = _clear_ex_info(self.set_result)(test_method)
setattr(self, methodName, test_method)
except AttributeError:
pass
super(QubesTestCase, self).__init__(methodName)
Expand Down
7 changes: 7 additions & 0 deletions qubes/tests/integ/audio.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,13 @@ def prepare_audio_test(self, backend):
if "whonix-g" in self.template:
self.skipTest("whonix gateway have no audio")
self.loop.run_until_complete(self.testvm1.start())
self.loop.run_until_complete(self.testvm1.run(
"chmod a+w /dev/console; systemd-run --no-block sh -c 'tail -F "
"/home/user/.xsession-errors >> /dev/console'",
user='root'))
self.loop.run_until_complete(self.testvm1.run(
"systemd-run --user --no-block sh -c "
"'journalctl --user -f >> /dev/console'"))
pulseaudio_units = "pulseaudio.socket pulseaudio.service"
pipewire_units = "pipewire.socket wireplumber.service pipewire.service"
if backend == "pipewire":
Expand Down
17 changes: 17 additions & 0 deletions qubes/tests/integ/grub.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ def setUp(self):
)

def install_packages(self, vm):
if os.environ.get("QUBES_TEST_SKIP_KERNEL_INSTALL") == "1":
return
else:
print(
"Installing kernel packages, you can skip by setting "
"QUBES_TEST_SKIP_KERNEL_INSTALL=1 in environment"
)
if self.template.startswith("fedora-"):
cmd_install1 = (
"dnf clean expire-cache && "
Expand Down Expand Up @@ -122,6 +129,11 @@ def test_000_standalone_vm(self):
self.loop.run_until_complete(self.testvm1.shutdown(wait=True))

self.testvm1.kernel = self.kernel
if self.virt_mode == "hvm":
# HVM has disabled memory-hotplug, which means VM is started with
# full maxmem and need extra memory for page structures for full
# maxmem
self.testvm1.memory = 450
self.loop.run_until_complete(self.testvm1.start())
(actual_kver, _) = self.loop.run_until_complete(
self.testvm1.run_for_stdio("uname -r")
Expand Down Expand Up @@ -158,6 +170,11 @@ def test_010_template_based_vm(self):

self.test_template.kernel = self.kernel
self.testvm1.kernel = self.kernel
if self.virt_mode == "hvm":
# HVM has disabled memory-hotplug, which means VM is started with
# full maxmem and need extra memory for page structures for full
# maxmem
self.testvm1.memory = 450

# Check if TemplateBasedVM boots and has the right kernel
self.loop.run_until_complete(self.testvm1.start())
Expand Down
6 changes: 1 addition & 5 deletions qubes/tests/integ/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@


# noinspection PyAttributeOutsideInit,PyPep8Naming
@qubes.tests.skipIfTemplate("whonix")
class VmNetworkingMixin(object):
test_ip = "192.168.123.45"
test_name = "test.example.com"
Expand Down Expand Up @@ -71,11 +72,6 @@ def setUp(self):
"Test not supported here - Whonix uses its own "
"firewall settings"
)
if self.template.endswith("-minimal"):
self.skipTest(
"Test not supported here - minimal template don't have "
"networking packages by default"
)
self.init_default_template(self.template)
self.testnetvm = self.app.add_new_vm(
qubes.vm.appvm.AppVM, name=self.make_vm_name("netvm1"), label="red"
Expand Down
22 changes: 20 additions & 2 deletions qubes/tests/integ/salt.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,12 +311,28 @@ def setUp(self):
super(SaltVMTestMixin, self).setUp()
self.init_default_template(self.template)

mgmt_tpl = self.app.domains[self.template]
if "minimal" in self.template:
# minimal template doesn't support being mgmt vm, but still test
# it being a target
mgmt_tpl = os.environ.get("QUBES_TEST_MGMT_TPL")
if not mgmt_tpl:
mgmt_tpl = str(self.host_app.default_template)
print(
f"Using {mgmt_tpl} template for mgmt vm when testing "
f"minimal template as target. You can set "
f"QUBES_TEST_MGMT_TPL env variable to use "
f"different template for mgmt vm"
)
mgmt_tpl = self.app.domains[mgmt_tpl]

dispvm_tpl_name = self.make_vm_name("disp-tpl")
dispvm_tpl = self.app.add_new_vm(
"AppVM",
label="red",
template_for_dispvms=True,
name=dispvm_tpl_name,
template=mgmt_tpl,
)
self.loop.run_until_complete(dispvm_tpl.create_on_disk())
self.app.default_dispvm = dispvm_tpl
Expand Down Expand Up @@ -611,8 +627,10 @@ def test_002_grains_id(self):

def test_003_update(self):
vmname = self.make_vm_name("target")
self.vm = self.app.add_new_vm("AppVM", name=vmname, label="red")
self.loop.run_until_complete(self.vm.create_on_disk())
self.vm = self.app.add_new_vm("TemplateVM", name=vmname, label="red")
self.loop.run_until_complete(
self.vm.clone_disk_files(self.app.default_template)
)
# start the VM manually, so it stays running after applying salt state
self.loop.run_until_complete(self.vm.start())
state_output = self.salt_call(
Expand Down
6 changes: 4 additions & 2 deletions qubes/tests/integ/vm_qrexec_gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,9 @@ def test_011_run_gnome_terminal(self):
self.loop.run_until_complete(self.testvm1.start())
self.assertEqual(self.testvm1.get_power_state(), "Running")
self.loop.run_until_complete(self.wait_for_session(self.testvm1))
p = self.loop.run_until_complete(self.testvm1.run("gnome-terminal"))
p = self.loop.run_until_complete(
self.testvm1.run("gnome-terminal || " "ptyxis")
)
try:
title = "user@{}".format(self.testvm1.name)
if self.template.count("whonix"):
Expand Down Expand Up @@ -156,7 +158,7 @@ def test_011_run_gnome_terminal(self):
wait_count += 1
if wait_count > 100:
self.fail(
"Timeout while waiting for gnome-terminal "
"Timeout while waiting for gnome-terminal/ptyxis "
"termination"
)
self.loop.run_until_complete(asyncio.sleep(0.1))
Expand Down
19 changes: 13 additions & 6 deletions qubes/tests/integ/vm_update.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,13 @@ def setUp(self):
self.loop.run_until_complete(self.testvm1.create_on_disk())
self.repo_proc = None

# template used for repo-hosting vm
self.repo_template = self.app.default_template
if self.template.count("minimal"):
self.repo_template = self.host_app.default_template
print(f"Using {self.repo_template!s} for repo hosting vm when "
f"testing minimal template")

def tearDown(self):
if self.repo_proc:
self.repo_proc.terminate()
Expand All @@ -256,6 +263,10 @@ def test_000_simple_update(self):

:type self: qubes.tests.SystemTestCase | VmUpdatesMixin
"""
if self.template.count("minimal"):
self.skipTest(
"Template {} not supported by this test".format(self.template)
)
self.app.save()
self.testvm1 = self.app.domains[self.testvm1.qid]
self.loop.run_until_complete(self.testvm1.start())
Expand Down Expand Up @@ -449,7 +460,8 @@ def start_vm_with_proxy_repo(self):
:type self: qubes.tests.SystemTestCase | VmUpdatesMixin
"""
self.netvm_repo = self.app.add_new_vm(
qubes.vm.appvm.AppVM, name=self.make_vm_name("net"), label="red"
qubes.vm.appvm.AppVM, name=self.make_vm_name("net"), label="red",
template = self.repo_template,
)
self.netvm_repo.provides_network = True
self.loop.run_until_complete(self.netvm_repo.create_on_disk())
Expand Down Expand Up @@ -574,11 +586,6 @@ def update_via_proxy_qubes_vm_update_impl(
:type break_repo: bool
:type: expect_updated: bool
"""
if self.template.count("minimal"):
self.skipTest(
"Template {} not supported by this test".format(self.template)
)

if expected_ret_codes is None:
expected_ret_codes = self.ret_code_ok

Expand Down