Skip to content

Commit 678d4c7

Browse files
committed
tests: add unit and functional tests for distro module
Add comprehensive test coverage for the distro module: - 28 new unit tests covering LinuxDistro, Probe methods, regex patterns, SUSEProbe custom logic, register_probe, detect scoring/fallback, Spec - 1 new functional test for local system detection - Update TEST_SIZE counts in check.py (unit: 976->1004, functional: 368->369) Coverage: 95% (untested: remote SSH session paths) Assisted-By: Cursor-Claude-4-Sonnet Signed-off-by: Harvey Lynden <hlynden@redhat.com>
1 parent 38f65ea commit 678d4c7

File tree

3 files changed

+330
-2
lines changed

3 files changed

+330
-2
lines changed

selftests/check.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@
2727
"job-api-check-tmp-directory-exists": 1,
2828
"nrunner-interface": 90,
2929
"nrunner-requirement": 28,
30-
"unit": 976,
30+
"unit": 1004,
3131
"jobs": 11,
32-
"functional-parallel": 368,
32+
"functional-parallel": 369,
3333
"functional-serial": 7,
3434
"optional-plugins": 0,
3535
"optional-plugins-golang": 2,

selftests/functional/utils/distro.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import os
2+
import platform
23

34
from avocado import Test
45
from avocado.core.exit_codes import AVOCADO_ALL_OK
56
from avocado.core.job import Job
67
from avocado.core.nrunner.runnable import Runnable
78
from avocado.core.suite import TestSuite
9+
from avocado.utils import distro
810

911

1012
class Distro(Test):
@@ -64,3 +66,28 @@ def test_debian_12_7(self):
6466
+ os.uname().machine.encode()
6567
+ b") version 12 release 7\n",
6668
)
69+
70+
71+
class DistroDetectLocal(Test):
72+
"""Tests distro detection on the local system without containers."""
73+
74+
def test_detect_current_system(self):
75+
"""Verify detect() returns a valid result for the running system."""
76+
result = distro.detect()
77+
self.assertIsInstance(result, distro.LinuxDistro)
78+
has_release_file = any(
79+
os.path.exists(p)
80+
for p in [
81+
"/etc/os-release",
82+
"/etc/redhat-release",
83+
"/etc/fedora-release",
84+
"/etc/debian_version",
85+
]
86+
)
87+
if has_release_file:
88+
self.assertNotEqual(
89+
result.name,
90+
distro.UNKNOWN_DISTRO_NAME,
91+
"detect() should identify a known distro on this system",
92+
)
93+
self.assertEqual(result.arch, platform.machine())

selftests/unit/utils/distro.py

Lines changed: 301 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,28 @@
1+
import os
12
import re
3+
import tempfile
4+
import unittest
25
import unittest.mock
36

47
from avocado.utils import distro
58

69

10+
class LinuxDistroTest(unittest.TestCase):
11+
def test_init(self):
12+
dist = distro.LinuxDistro("fedora", "38", "0", "x86_64")
13+
self.assertEqual(dist.name, "fedora")
14+
self.assertEqual(dist.version, "38")
15+
self.assertEqual(dist.release, "0")
16+
self.assertEqual(dist.arch, "x86_64")
17+
18+
def test_repr(self):
19+
dist = distro.LinuxDistro("rhel", "9", "1", "x86_64")
20+
self.assertEqual(
21+
repr(dist),
22+
"<LinuxDistro: name=rhel, version=9, release=1, arch=x86_64>",
23+
)
24+
25+
726
class ProbeTest(unittest.TestCase):
827
def test_check_name_for_file_fail(self):
928
class MyProbe(distro.Probe):
@@ -68,6 +87,288 @@ class MyProbe(distro.Probe):
6887
mocked.assert_called_once_with(distro_file)
6988
self.assertEqual(distro_name, probed_distro_name)
7089

90+
def test_name_for_file_contains_match(self):
91+
fd, tmpfile = tempfile.mkstemp(suffix="-release")
92+
with os.fdopen(fd, "w") as tmp:
93+
tmp.write("Welcome to MyDistro 5.0\n")
94+
self.addCleanup(os.unlink, tmpfile)
95+
96+
class MyProbe(distro.Probe):
97+
CHECK_FILE = tmpfile
98+
CHECK_FILE_CONTAINS = "MyDistro"
99+
CHECK_FILE_DISTRO_NAME = "mydistro"
100+
101+
self.assertEqual(MyProbe().name_for_file_contains(), "mydistro")
102+
103+
def test_name_for_file_contains_no_match(self):
104+
fd, tmpfile = tempfile.mkstemp(suffix="-release")
105+
with os.fdopen(fd, "w") as tmp:
106+
tmp.write("Some other content\n")
107+
self.addCleanup(os.unlink, tmpfile)
108+
109+
class MyProbe(distro.Probe):
110+
CHECK_FILE = tmpfile
111+
CHECK_FILE_CONTAINS = "MyDistro"
112+
CHECK_FILE_DISTRO_NAME = "mydistro"
113+
114+
self.assertIsNone(MyProbe().name_for_file_contains())
115+
116+
def test_name_for_file_contains_ioerror(self):
117+
class MyProbe(distro.Probe):
118+
CHECK_FILE = "/etc/fake-release"
119+
CHECK_FILE_CONTAINS = "MyDistro"
120+
CHECK_FILE_DISTRO_NAME = "mydistro"
121+
122+
with unittest.mock.patch(
123+
"avocado.utils.distro.os.path.exists", return_value=True
124+
):
125+
with unittest.mock.patch(
126+
"builtins.open", side_effect=IOError("Permission denied")
127+
):
128+
self.assertIsNone(MyProbe().name_for_file_contains())
129+
130+
def test_version_from_file(self):
131+
class MyProbe(distro.Probe):
132+
CHECK_FILE = "/etc/fake-release"
133+
CHECK_VERSION_REGEX = re.compile(r"Release (\d+)\.(\d+)")
134+
135+
with unittest.mock.patch(
136+
"avocado.utils.distro.os.path.exists", return_value=True
137+
):
138+
with unittest.mock.patch(
139+
"builtins.open",
140+
unittest.mock.mock_open(read_data="Release 9.3\n"),
141+
):
142+
self.assertEqual(MyProbe().version(), "9")
143+
144+
def test_version_no_match(self):
145+
class MyProbe(distro.Probe):
146+
CHECK_FILE = "/etc/fake-release"
147+
CHECK_VERSION_REGEX = re.compile(r"Release (\d+)\.(\d+)")
148+
149+
with unittest.mock.patch(
150+
"avocado.utils.distro.os.path.exists", return_value=True
151+
):
152+
with unittest.mock.patch(
153+
"builtins.open",
154+
unittest.mock.mock_open(read_data="No version here\n"),
155+
):
156+
self.assertEqual(MyProbe().version(), distro.UNKNOWN_DISTRO_VERSION)
157+
158+
def test_release_from_file(self):
159+
class MyProbe(distro.Probe):
160+
CHECK_FILE = "/etc/fake-release"
161+
CHECK_VERSION_REGEX = re.compile(r"Release (\d+)\.(\d+)")
162+
163+
with unittest.mock.patch(
164+
"avocado.utils.distro.os.path.exists", return_value=True
165+
):
166+
with unittest.mock.patch(
167+
"builtins.open",
168+
unittest.mock.mock_open(read_data="Release 9.3\n"),
169+
):
170+
self.assertEqual(MyProbe().release(), "3")
171+
172+
def test_release_single_group(self):
173+
class MyProbe(distro.Probe):
174+
CHECK_FILE = "/etc/fake-release"
175+
CHECK_VERSION_REGEX = re.compile(r"Release (\d+)")
176+
177+
with unittest.mock.patch(
178+
"avocado.utils.distro.os.path.exists", return_value=True
179+
):
180+
with unittest.mock.patch(
181+
"builtins.open",
182+
unittest.mock.mock_open(read_data="Release 9\n"),
183+
):
184+
self.assertEqual(MyProbe().release(), distro.UNKNOWN_DISTRO_RELEASE)
185+
186+
def test_check_release_multiple_groups(self):
187+
class MyProbe(distro.Probe):
188+
CHECK_FILE = "/etc/fake-release"
189+
CHECK_VERSION_REGEX = re.compile(r"Release (\d+)\.(\d+)")
190+
191+
self.assertTrue(MyProbe().check_release())
192+
193+
def test_check_release_single_group(self):
194+
class MyProbe(distro.Probe):
195+
CHECK_FILE = "/etc/fake-release"
196+
CHECK_VERSION_REGEX = re.compile(r"Release (\d+)")
197+
198+
self.assertFalse(MyProbe().check_release())
199+
200+
def test_get_distro_detected(self):
201+
fd, tmpfile = tempfile.mkstemp(suffix="-release")
202+
with os.fdopen(fd, "w") as tmp:
203+
tmp.write("TestDistro release 5.2 (Final)\n")
204+
self.addCleanup(os.unlink, tmpfile)
205+
206+
class MyProbe(distro.Probe):
207+
CHECK_FILE = tmpfile
208+
CHECK_FILE_CONTAINS = "TestDistro"
209+
CHECK_FILE_DISTRO_NAME = "testdistro"
210+
CHECK_VERSION_REGEX = re.compile(r"TestDistro release (\d+)\.(\d+).*")
211+
212+
result = MyProbe().get_distro()
213+
self.assertEqual(result.name, "testdistro")
214+
self.assertEqual(result.version, "5")
215+
self.assertEqual(result.release, "2")
216+
217+
def test_get_distro_unknown(self):
218+
result = distro.Probe().get_distro()
219+
self.assertIs(result, distro.UNKNOWN_DISTRO)
220+
221+
def test_check_for_remote_file_found(self):
222+
mock_session = unittest.mock.MagicMock()
223+
mock_session.cmd.return_value.exit_status = 0
224+
probe = distro.Probe(session=mock_session)
225+
self.assertTrue(probe.check_for_remote_file("/etc/some-file"))
226+
mock_session.cmd.assert_called_once_with("test -f /etc/some-file")
227+
228+
def test_check_for_remote_file_not_found(self):
229+
mock_session = unittest.mock.MagicMock()
230+
mock_session.cmd.return_value.exit_status = 1
231+
probe = distro.Probe(session=mock_session)
232+
self.assertFalse(probe.check_for_remote_file("/etc/nonexistent"))
233+
234+
235+
class ProbeRegexTest(unittest.TestCase):
236+
"""Tests version regex patterns against real release file strings."""
237+
238+
def test_redhat_regex(self):
239+
match = distro.RedHatProbe.CHECK_VERSION_REGEX.match(
240+
"Red Hat Enterprise Linux Server release 9.3 (Plow)"
241+
)
242+
self.assertIsNotNone(match)
243+
self.assertEqual(match.group(1), "9")
244+
self.assertEqual(match.group(2), "3")
245+
246+
def test_centos_stream_regex(self):
247+
match = distro.CentosStreamProbe.CHECK_VERSION_REGEX.match(
248+
"CentOS Stream release 9"
249+
)
250+
self.assertIsNotNone(match)
251+
self.assertEqual(match.group(1), "9")
252+
253+
def test_debian_numeric_version(self):
254+
match = distro.DebianProbe.CHECK_VERSION_REGEX.match("12.7")
255+
self.assertIsNotNone(match)
256+
self.assertEqual(match.group(2), "12")
257+
258+
def test_debian_codename_version(self):
259+
match = distro.DebianProbe.CHECK_VERSION_REGEX.match("trixie/sid")
260+
self.assertIsNotNone(match)
261+
self.assertEqual(match.group(1), "sid")
262+
263+
def test_ubuntu_regex(self):
264+
content = 'NAME="Ubuntu"\nVERSION_ID="22.04"\nID=ubuntu\n'
265+
match = distro.UbuntuProbe.CHECK_VERSION_REGEX.match(content)
266+
self.assertIsNotNone(match)
267+
self.assertEqual(match.group(1), "22.04")
268+
269+
def test_amazon_regex(self):
270+
content = 'NAME="Amazon Linux"\nVERSION="2"\nVERSION_ID="2"\n'
271+
match = distro.AmazonLinuxProbe.CHECK_VERSION_REGEX.match(content)
272+
self.assertIsNotNone(match)
273+
self.assertEqual(match.group(1), "2")
274+
275+
276+
class SUSEProbeTest(unittest.TestCase):
277+
def test_suse_version_parsing(self):
278+
os_release = (
279+
'NAME="SUSE Linux Enterprise Server"\n'
280+
'VERSION="12-SP2"\n'
281+
'VERSION_ID="12.2"\n'
282+
'ID="sles"\n'
283+
)
284+
fd, tmpfile = tempfile.mkstemp(suffix="-os-release")
285+
with os.fdopen(fd, "w") as tmp:
286+
tmp.write(os_release)
287+
self.addCleanup(os.unlink, tmpfile)
288+
289+
with unittest.mock.patch.object(distro.SUSEProbe, "CHECK_FILE", tmpfile):
290+
result = distro.SUSEProbe().get_distro()
291+
self.assertEqual(result.name, "SuSE")
292+
self.assertEqual(result.version, 12)
293+
self.assertEqual(result.release, 2)
294+
295+
296+
class RegisterProbeTest(unittest.TestCase):
297+
def setUp(self):
298+
self._original_probes = distro.REGISTERED_PROBES[:]
299+
300+
def tearDown(self):
301+
distro.REGISTERED_PROBES[:] = self._original_probes
302+
303+
def test_register_new_probe(self):
304+
class CustomProbe(distro.Probe):
305+
CHECK_FILE = "/etc/custom-release"
306+
307+
initial_count = len(distro.REGISTERED_PROBES)
308+
distro.register_probe(CustomProbe)
309+
self.assertEqual(len(distro.REGISTERED_PROBES), initial_count + 1)
310+
self.assertIn(CustomProbe, distro.REGISTERED_PROBES)
311+
312+
def test_register_duplicate_ignored(self):
313+
class CustomProbe(distro.Probe):
314+
CHECK_FILE = "/etc/custom-release"
315+
316+
distro.register_probe(CustomProbe)
317+
count_after_first = len(distro.REGISTERED_PROBES)
318+
distro.register_probe(CustomProbe)
319+
self.assertEqual(len(distro.REGISTERED_PROBES), count_after_first)
320+
321+
322+
class DetectTest(unittest.TestCase):
323+
def setUp(self):
324+
self._original_probes = distro.REGISTERED_PROBES[:]
325+
326+
def tearDown(self):
327+
distro.REGISTERED_PROBES[:] = self._original_probes
328+
329+
def test_detect_returns_best_scoring(self):
330+
fd, tmpfile = tempfile.mkstemp(suffix="-release")
331+
with os.fdopen(fd, "w") as tmp:
332+
tmp.write("HighScore release 3.1\n")
333+
self.addCleanup(os.unlink, tmpfile)
334+
335+
class LowProbe(distro.Probe):
336+
CHECK_FILE = tmpfile
337+
CHECK_FILE_DISTRO_NAME = "lowscore"
338+
339+
class HighProbe(distro.Probe):
340+
CHECK_FILE = tmpfile
341+
CHECK_FILE_CONTAINS = "HighScore"
342+
CHECK_FILE_DISTRO_NAME = "highscore"
343+
CHECK_VERSION_REGEX = re.compile(r"HighScore release (\d+)\.(\d+).*")
344+
345+
distro.REGISTERED_PROBES[:] = [LowProbe, HighProbe]
346+
result = distro.detect()
347+
self.assertEqual(result.name, "highscore")
348+
self.assertEqual(result.version, "3")
349+
self.assertEqual(result.release, "1")
350+
351+
def test_detect_no_probes_returns_unknown(self):
352+
distro.REGISTERED_PROBES[:] = []
353+
result = distro.detect()
354+
self.assertIs(result, distro.UNKNOWN_DISTRO)
355+
356+
357+
class SpecTest(unittest.TestCase):
358+
def test_init_with_all_args(self):
359+
spec = distro.Spec("fedora", min_version=38, min_release=0, arch="x86_64")
360+
self.assertEqual(spec.name, "fedora")
361+
self.assertEqual(spec.min_version, 38)
362+
self.assertEqual(spec.min_release, 0)
363+
self.assertEqual(spec.arch, "x86_64")
364+
365+
def test_init_defaults(self):
366+
spec = distro.Spec("rhel")
367+
self.assertEqual(spec.name, "rhel")
368+
self.assertIsNone(spec.min_version)
369+
self.assertIsNone(spec.min_release)
370+
self.assertIsNone(spec.arch)
371+
71372

72373
if __name__ == "__main__":
73374
unittest.main()

0 commit comments

Comments
 (0)