Skip to content

Commit b7edbbf

Browse files
committed
tests/functional: Extract the find_free_ports() function into a helper file
We'll need this functionality in other functional tests, too, so let's extract it into the qemu_test module. Also add an __enter__ and __exit__ function that can be used for using this functionality in a locked context, so that tests that are running in parallel don't try to compete for the same ports later. Also make sure to only use ports in the "Dynamic Ports" range (see https://www.rfc-editor.org/rfc/rfc6335) and "randomize" the start of the probed range with the PID of the test process to further avoid possible clashes with other competing processes. Message-ID: <[email protected]> Signed-off-by: Thomas Huth <[email protected]>
1 parent 93a9fdc commit b7edbbf

File tree

2 files changed

+64
-28
lines changed

2 files changed

+64
-28
lines changed

tests/functional/qemu_test/ports.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#!/usr/bin/env python3
2+
#
3+
# Simple functional tests for VNC functionality
4+
#
5+
# Copyright 2018, 2024 Red Hat, Inc.
6+
#
7+
# This work is licensed under the terms of the GNU GPL, version 2 or
8+
# later. See the COPYING file in the top-level directory.
9+
10+
import fcntl
11+
import os
12+
import socket
13+
import sys
14+
import tempfile
15+
16+
from .config import BUILD_DIR
17+
from typing import List
18+
19+
class Ports():
20+
21+
PORTS_ADDR = '127.0.0.1'
22+
PORTS_RANGE_SIZE = 1024
23+
PORTS_START = 49152 + ((os.getpid() * PORTS_RANGE_SIZE) % 16384)
24+
PORTS_END = PORTS_START + PORTS_RANGE_SIZE
25+
26+
def __enter__(self):
27+
lock_file = os.path.join(BUILD_DIR, "tests", "functional", "port_lock")
28+
self.lock_fh = os.open(lock_file, os.O_CREAT)
29+
fcntl.flock(self.lock_fh, fcntl.LOCK_EX)
30+
return self
31+
32+
def __exit__(self, exc_type, exc_value, traceback):
33+
fcntl.flock(self.lock_fh, fcntl.LOCK_UN)
34+
os.close(self.lock_fh)
35+
36+
def check_bind(self, port: int) -> bool:
37+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
38+
try:
39+
sock.bind((self.PORTS_ADDR, port))
40+
except OSError:
41+
return False
42+
43+
return True
44+
45+
def find_free_ports(self, count: int) -> List[int]:
46+
result = []
47+
for port in range(self.PORTS_START, self.PORTS_END):
48+
if self.check_bind(port):
49+
result.append(port)
50+
if len(result) >= count:
51+
break
52+
assert len(result) == count
53+
return result
54+
55+
def find_free_port(self) -> int:
56+
return self.find_free_ports(1)[0]

tests/functional/test_vnc.py

Lines changed: 8 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,9 @@
1414
from typing import List
1515

1616
from qemu_test import QemuSystemTest
17-
17+
from qemu_test.ports import Ports
1818

1919
VNC_ADDR = '127.0.0.1'
20-
VNC_PORT_START = 32768
21-
VNC_PORT_END = VNC_PORT_START + 1024
22-
23-
24-
def check_bind(port: int) -> bool:
25-
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
26-
try:
27-
sock.bind((VNC_ADDR, port))
28-
except OSError:
29-
return False
30-
31-
return True
32-
3320

3421
def check_connect(port: int) -> bool:
3522
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
@@ -40,18 +27,6 @@ def check_connect(port: int) -> bool:
4027

4128
return True
4229

43-
44-
def find_free_ports(count: int) -> List[int]:
45-
result = []
46-
for port in range(VNC_PORT_START, VNC_PORT_END):
47-
if check_bind(port):
48-
result.append(port)
49-
if len(result) >= count:
50-
break
51-
assert len(result) == count
52-
return result
53-
54-
5530
class Vnc(QemuSystemTest):
5631

5732
def test_no_vnc_change_password(self):
@@ -85,8 +60,7 @@ def test_change_password(self):
8560
self.vm.cmd('change-vnc-password',
8661
password='new_password')
8762

88-
def test_change_listen(self):
89-
a, b, c = find_free_ports(3)
63+
def do_test_change_listen(self, a, b, c):
9064
self.assertFalse(check_connect(a))
9165
self.assertFalse(check_connect(b))
9266
self.assertFalse(check_connect(c))
@@ -108,5 +82,11 @@ def test_change_listen(self):
10882
self.assertTrue(check_connect(b))
10983
self.assertTrue(check_connect(c))
11084

85+
def test_change_listen(self):
86+
with Ports() as ports:
87+
a, b, c = ports.find_free_ports(3)
88+
self.do_test_change_listen(a, b, c)
89+
90+
11191
if __name__ == '__main__':
11292
QemuSystemTest.main()

0 commit comments

Comments
 (0)