Skip to content

Commit

Permalink
several improvements for tests in Ubuntu 24.10 (#276)
Browse files Browse the repository at this point in the history
tests :several improvements

* tests : do not display fwd_port since it is not always available

this bug has been introduced in the commit 198e71a

* tests : allow only one instance of monitor per qemu machine

* tests : add prefix to test function to be aligned with test file

* tests : tox : add all tests except perf tests category

* tests : increase ssh timeout for tests

test_stress_huge_resource_vm and test_stress_max_vcpous need more time
for the guest to boot

* tests : tox : ignore tests in tests/guest folder

this folder contains the tests that should be directly executed in the
guest, we have to skip them

* tests : does not make the test fail if we cannot stop correctly the guest
  • Loading branch information
hector-cao authored Nov 18, 2024
1 parent 198e71a commit f36e38c
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 27 deletions.
50 changes: 39 additions & 11 deletions tests/lib/Qemu.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,12 @@ class QemuMonitor():
READ_TIMEOUT = 2
CONNECT_RETRIES = 60

def __new__(cls, qemu):
# only 1 monitor per qemu machine
if qemu.monitor is None:
qemu.monitor = super().__new__(cls)
return qemu.monitor

def __init__(self, qemu):
self.socket = None
assert qemu.qcmd.monitor_file != None, "Monitor socket file is undefined"
Expand All @@ -312,11 +318,13 @@ def __init__(self, qemu):
break
except Exception as e:
# give some time to make sure socket file is available
print(f'Try to connect to qemu : {qemu.qcmd.monitor_file} : {e}')
time.sleep(1)
self.socket.settimeout(self.READ_TIMEOUT)
# wait for prompt
print(f'Connected : {qemu.qcmd.monitor_file}, wait for prompt.')
self.wait_prompt()

def recv(self):
def recv_data(self):
msg = ''
try:
while True:
Expand All @@ -327,6 +335,22 @@ def recv(self):
msg += recv_data.decode('utf-8')
except:
pass
return msg

def wait_prompt(self):
msg = self.recv_data()
assert self.DELIMITER_STRING in msg, f'Fail on wait for monitor prompt : {msg}'

def recv(self):
"""
Return an array of messages from qemu process
separated by the prompt string (qemu)
Example:
(qemu) running
(qemu) rebooting
will result in the returned value : [' running', ' rebooting']
"""
msg = self.recv_data()
return msg.split(self.DELIMITER_STRING)

def send_command(self, cmd):
Expand Down Expand Up @@ -481,7 +505,6 @@ class QemuMachineService:
# to run the guest manually
qemu_run_script = """
#!/bin/bash
echo "To connect to the VM : ssh -p {fwd_port} root@localhost"
{cmd_str}
"""

Expand Down Expand Up @@ -511,6 +534,10 @@ def __init__(self,
)
self.qcmd.add_image(self.image_path)
self.qcmd.add_monitor()
# monitor client associated to this machine
# since there could be only one client, we keep track
# of this client instance in the qemu machine object
self.monitor = None
self.qcmd.add_qmp()
if QemuMachineService.QEMU_MACHINE_PORT_FWD not in service_blacklist:
self.fwd_port = util.tcp_port_available()
Expand Down Expand Up @@ -606,7 +633,7 @@ def communicate(self):
"""
Wait for qemu to exit
"""
self.out, self.err = self.proc.communicate()
self.out, self.err = self.proc.communicate(timeout=60)
if self.proc.returncode != 0:
print(self.err.decode())
return self.out, self.err
Expand All @@ -622,17 +649,19 @@ def stop(self):

# self.proc.returncode == None -> not yet terminated

# try to shutdown the VM properly, this is important to avoid
# rootfs corruption if we want to run the guest again
mon = QemuMonitor(self)
mon.powerdown()
try:
# try to shutdown the VM properly, this is important to avoid
# rootfs corruption if we want to run the guest again
# catch exception and ignore it since we are stopping .... no need to fail the test
mon = QemuMonitor(self)
mon.powerdown()

self.communicate()
return
except Exception as e:
pass

print('Qemu process did not shutdown properly, terminate it ...')
print(f'Qemu process did not shutdown properly, terminate it ... ({self.workdir_name})')
# terminate qemu process (SIGTERM)
try:
self.proc.terminate()
Expand Down Expand Up @@ -671,8 +700,7 @@ def write_cmd_to_file(self, fname : str):
cmd_str += f'\"{el}\" '
else:
cmd_str += f'{el} '
script_contents = qemu_run_script.format(fwd_port=self.fwd_port,
cmd_str=cmd_str)
script_contents = qemu_run_script.format(cmd_str=cmd_str)
run_script.write(script_contents)
f = pathlib.Path(fname)
f.chmod(f.stat().st_mode | stat.S_IEXEC)
Expand Down
2 changes: 1 addition & 1 deletion tests/tests/test_stress_boot.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

import util

def test_boot():
def test_stress_boot():
"""
Boot in loop
"""
Expand Down
13 changes: 7 additions & 6 deletions tests/tests/test_stress_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import Qemu
import util

def test_huge_resource_vm(qm):
def test_stress_huge_resource_vm(qm):
"""
Test huge resources (Intel Case ID 007)
"""
Expand All @@ -34,11 +34,12 @@ def test_huge_resource_vm(qm):
qm.qcmd.plugins['memory'].memory = '%dG' % (huge_mem_gb)
qm.run()

ssh = Qemu.QemuSSH(qm)
# huge guest memory -> increase the timeout to give more time to guest to boot
ssh = Qemu.QemuSSH(qm, timeout=100)

qm.stop()

def test_memory_limit_resource_vm(qm):
def test_stress_memory_limit_resource_vm(qm):
"""
Test memory limit resource (No Intel Case)
"""
Expand All @@ -54,7 +55,7 @@ def test_memory_limit_resource_vm(qm):
qm.stop()


def test_max_vcpus(qm):
def test_stress_max_vcpus(qm):
"""
Test max vcpus (No Intel Case ID)
"""
Expand All @@ -65,12 +66,12 @@ def test_max_vcpus(qm):
qm.qcmd.plugins['cpu'].nb_cores = num_cpus
qm.run()

ssh = Qemu.QemuSSH(qm)
ssh = Qemu.QemuSSH(qm, timeout=100)

qm.stop()


def test_max_guests():
def test_stress_max_guests():
"""
Test max guests (No Intel Case ID)
"""
Expand Down
22 changes: 13 additions & 9 deletions tests/tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -24,36 +24,40 @@ commands_pre =

[testenv:test_guest]
commands =
python3 -m pytest -s -v --junitxml=test_guest_report.xml -k test_guest
python3 -m pytest -s -v --junitxml=test_guest_report.xml --ignore=tests/guest -k test_guest

[testenv:test_host]
commands =
python3 -m pytest -s -v --junitxml=test_host_report.xml -k test_host
python3 -m pytest -s -v --junitxml=test_host_report.xml --ignore=tests/guest -k test_host

[testenv:test_boot]
commands =
python3 -m pytest -s -v --junitxml=test_boot_report.xml -k test_boot
python3 -m pytest -s -v --junitxml=test_boot_report.xml --ignore=tests/guest -k test_boot

[testenv:test_perf]
commands =
python3 -m pytest -s -v --junitxml=test_perf_report.xml -k test_perf
python3 -m pytest -s -v --junitxml=test_perf_report.xml --ignore=tests/guest -k test_perf

[testenv:test_quote]
commands =
python3 -m pytest -s -v --junitxml=test_quote_report.xml -k test_quote
python3 -m pytest -s -v --junitxml=test_quote_report.xml --ignore=tests/guest -k test_quote

[testenv:test_stress]
commands =
python3 -m pytest -s -v --junitxml=test_stress_report.xml -k test_stress
python3 -m pytest -s -v --junitxml=test_stress_report.xml --ignore=tests/guest -k test_stress

[testenv:test_all_except_perf]
commands =
python3 -m pytest -s -v --junitxml=test_all_report.xml --ignore=tests/guest -k 'not test_perf'

[testenv:test_all]
commands =
python3 -m pytest -s -v --junitxml=test_all_report.xml
python3 -m pytest -s -v --junitxml=test_all_report.xml --ignore=tests/guest

[testenv:test_specify]
commands =
python3 -m pytest -s -v --junitxml=test_specify_report.xml -k {posargs}
python3 -m pytest -s -v --junitxml=test_specify_report.xml --ignore=tests/guest -k {posargs}

[testenv:collect_tests]
commands =
python3 -m pytest -s -v --collect-only -k {posargs}
python3 -m pytest -s -v --collect-only --ignore=tests/guest -k {posargs}

0 comments on commit f36e38c

Please sign in to comment.