Skip to content

Commit 9b37415

Browse files
authored
Merge pull request #6 from rezib/pr/refactor-pylint
TestResults and Controller refactoring to reduce functions complexity
2 parents e0af0a5 + fe134a1 commit 9b37415

File tree

4 files changed

+183
-66
lines changed

4 files changed

+183
-66
lines changed

lib/rift/Controller.py

Lines changed: 82 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -481,9 +481,10 @@ def build_pkg(config, args, pkg, arch):
481481

482482
mock.clean()
483483

484-
def test_one_pkg(config, args, pkg, vm, arch, repos, results):
484+
def test_one_pkg(config, args, pkg, vm, arch, repos):
485485
"""
486-
Launch tests on a given package on a specific VM and a set of repositories.
486+
Launch tests on a given package on a specific VM and a set of repositories
487+
and return results.
487488
"""
488489
message(f"Preparing {arch} test environment")
489490
_vm_start(vm)
@@ -495,7 +496,7 @@ def test_one_pkg(config, args, pkg, vm, arch, repos, results):
495496

496497
banner(f"Starting tests of package {pkg.name} on architecture {arch}")
497498

498-
rc = 0
499+
results = TestResults()
499500

500501
tests = list(pkg.tests())
501502
if not args.noauto:
@@ -509,7 +510,6 @@ def test_one_pkg(config, args, pkg, vm, arch, repos, results):
509510
results.add_success(case, time.time() - now, out=proc.out, err=proc.err)
510511
message(f"Test '{case.fullname}' on architecture {arch}: OK")
511512
else:
512-
rc = 1
513513
results.add_failure(case, time.time() - now, out=proc.out, err=proc.err)
514514
message(f"Test '{case.fullname}' on architecture {arch}: ERROR")
515515

@@ -519,10 +519,10 @@ def test_one_pkg(config, args, pkg, vm, arch, repos, results):
519519
time.sleep(5)
520520
vm.stop()
521521

522-
return rc
522+
return results
523523

524-
def test_pkgs(config, args, results, pkgs, arch, extra_repos=None):
525-
"""Test a list of packages on a specific architecture."""
524+
def test_pkgs(config, args, pkgs, arch, extra_repos=None):
525+
"""Test a list of packages on a specific architecture and return results."""
526526

527527
if extra_repos is None:
528528
extra_repos = []
@@ -533,7 +533,7 @@ def test_pkgs(config, args, results, pkgs, arch, extra_repos=None):
533533
if vm.running():
534534
raise RiftError('VM is already running')
535535

536-
rc = 0
536+
results = TestResults()
537537

538538
for pkg in pkgs:
539539

@@ -559,27 +559,30 @@ def test_pkgs(config, args, results, pkgs, arch, extra_repos=None):
559559
continue
560560

561561
pkg.load()
562-
rc += test_one_pkg(config, args, pkg, vm, arch, repos, results)
562+
results.extend(test_one_pkg(config, args, pkg, vm, arch, repos))
563563

564564
if getattr(args, 'noquit', False):
565565
message("Not stopping the VM. Use: rift vm connect")
566566

567-
return rc
567+
return results
568568

569-
def validate_pkgs(config, args, results, pkgs, arch):
569+
570+
def validate_pkgs(config, args, pkgs, arch):
570571
"""
571-
Validate packages on a specific architecture:
572+
Validate packages on a specific architecture and return results:
572573
- rpmlint on specfile
573574
- check file patterns
574575
- build it
575-
- lauch tests
576+
- launch tests
576577
"""
577578

578579
repos = ProjectArchRepositories(config, arch)
579580

580581
if args.publish and not repos.can_publish():
581582
raise RiftError("Cannot publish if 'working_repo' is undefined")
582583

584+
results = TestResults()
585+
583586
for pkg in pkgs:
584587

585588
case = TestCase('build', pkg.name, arch)
@@ -638,20 +641,20 @@ def validate_pkgs(config, args, results, pkgs, arch):
638641
mock.publish(staging)
639642
staging.update()
640643

641-
rc = 0
642644
if args.test:
643-
rc = test_pkgs(
644-
config,
645-
args,
646-
results,
647-
[pkg],
648-
arch,
649-
[staging.consumables[arch]]
645+
results.extend(
646+
test_pkgs(
647+
config,
648+
args,
649+
[pkg],
650+
arch,
651+
[staging.consumables[arch]]
652+
)
650653
)
651654

652655
# Also publish on working repo if requested
653656
# XXX: All RPMs should be published when all of them have been validated
654-
if rc == 0 and args.publish:
657+
if results.global_result and args.publish:
655658
message("Publishing RPMS...")
656659
mock.publish(repos.working)
657660

@@ -666,6 +669,8 @@ def validate_pkgs(config, args, results, pkgs, arch):
666669

667670
banner(f"All packages checked on architecture {arch}")
668671

672+
return results
673+
669674
def vm_build(vm, args, config):
670675
"""Build VM image."""
671676
if not args.deploy and args.output is None:
@@ -742,6 +747,44 @@ def action_vm(args, config):
742747
ret = vm_build(vm, args, config)
743748
return ret
744749

750+
def build_pkgs(config, args, pkgs, arch):
751+
"""
752+
Build a list of packages on a given architecture and return results.
753+
"""
754+
results = TestResults()
755+
756+
for pkg in pkgs:
757+
case = TestCase('build', pkg.name, arch)
758+
now = time.time()
759+
try:
760+
spec = Spec(pkg.specfile, config=config)
761+
except RiftError as ex:
762+
logging.error("Unable to load spec file: %s", str(ex))
763+
results.add_failure(case, time.time() - now, err=str(ex))
764+
continue # skip current package
765+
766+
if not spec.supports_arch(arch):
767+
logging.info(
768+
"Skipping build on architecture %s not supported by "
769+
"package %s",
770+
arch,
771+
pkg.name
772+
)
773+
continue
774+
775+
banner(f"Building package '{pkg.name}' for architecture {arch}")
776+
now = time.time()
777+
try:
778+
pkg.load()
779+
build_pkg(config, args, pkg, arch)
780+
except RiftError as ex:
781+
logging.error("Build failure: %s", str(ex))
782+
results.add_failure(case, time.time() - now, err=str(ex))
783+
else:
784+
results.add_success(case, time.time() - now)
785+
786+
return results
787+
745788
def action_build(args, config):
746789
"""Action for 'build' command."""
747790

@@ -761,36 +804,8 @@ def action_build(args, config):
761804
# Build all packages for all project supported architectures
762805
for arch in config.get('arch'):
763806

764-
for pkg in Package.list(config, staff, modules, args.packages):
765-
766-
case = TestCase('build', pkg.name, arch)
767-
now = time.time()
768-
try:
769-
spec = Spec(pkg.specfile, config=config)
770-
except RiftError as ex:
771-
logging.error("Unable to load spec file: %s", str(ex))
772-
results.add_failure(case, time.time() - now, err=str(ex))
773-
continue # skip current package
774-
775-
if not spec.supports_arch(arch):
776-
logging.info(
777-
"Skipping build on architecture %s not supported by "
778-
"package %s",
779-
arch,
780-
pkg.name
781-
)
782-
continue
783-
784-
banner(f"Building package '{pkg.name}' for architecture {arch}")
785-
now = time.time()
786-
try:
787-
pkg.load()
788-
build_pkg(config, args, pkg, arch)
789-
except RiftError as ex:
790-
logging.error("Build failure: %s", str(ex))
791-
results.add_failure(case, time.time() - now, err=str(ex))
792-
else:
793-
results.add_success(case, time.time() - now)
807+
pkgs = Package.list(config, staff, modules, args.packages)
808+
results.extend(build_pkgs(config, args, pkgs, arch))
794809

795810
if getattr(args, 'junit', False):
796811
logging.info('Writing test results in %s', args.junit)
@@ -821,12 +836,13 @@ def action_test(args, config):
821836
results = TestResults('test')
822837
# Test package on all project supported architectures
823838
for arch in config.get('arch'):
824-
test_pkgs(
825-
config,
826-
args,
827-
results,
828-
Package.list(config, staff, modules, args.packages),
829-
arch
839+
results.extend(
840+
test_pkgs(
841+
config,
842+
args,
843+
Package.list(config, staff, modules, args.packages),
844+
arch
845+
)
830846
)
831847
if getattr(args, 'junit', False):
832848
logging.info('Writing test results in %s', args.junit)
@@ -852,12 +868,13 @@ def action_validate(args, config):
852868
results = TestResults('validate')
853869
# Validate packages on all project supported architectures
854870
for arch in config.get('arch'):
855-
validate_pkgs(
856-
config,
857-
args,
858-
results,
859-
Package.list(config, staff, modules, args.packages),
860-
arch
871+
results.extend(
872+
validate_pkgs(
873+
config,
874+
args,
875+
Package.list(config, staff, modules, args.packages),
876+
arch
877+
)
861878
)
862879
banner('All packages checked on all architectures')
863880

@@ -890,7 +907,7 @@ def action_validdiff(args, config):
890907
# Re-validate all updated packages for all architectures supported by the
891908
# project.
892909
for arch in config.get('arch'):
893-
validate_pkgs(config, args, results, updated.values(), arch)
910+
results.extend(validate_pkgs(config, args, updated.values(), arch))
894911

895912

896913
if getattr(args, 'junit', False):

lib/rift/TestResults.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,15 @@ def _add_result(self, result):
137137
"""
138138
self.results.append(result)
139139

140+
def extend(self, other):
141+
"""
142+
Extend TestResults with all TestResult from the other TestResults.
143+
"""
144+
for result in other.results:
145+
self.results.append(result)
146+
if result.value == 'Failure':
147+
self.global_result = False
148+
140149
def junit(self, filename):
141150
"""
142151
Generate a junit xml file containing all tests results.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ addopts = "-v --cov=rift --cov-report=term-missing"
88

99
[tool.pylint.main]
1010
# Specify a score threshold under which the program will exit with error.
11-
fail-under = 9.74
11+
fail-under = 9.80
1212

1313
# Minimum Python version to use for version dependent checks. Will default to the
1414
# version used to run pylint.

tests/TestResults.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
#
2+
# Copyright (C) 2025 CEA
3+
#
4+
import textwrap
5+
import xml.etree.ElementTree as ET
6+
from io import BytesIO
7+
8+
from TestUtils import RiftTestCase
9+
from rift.TestResults import TestResults, TestCase
10+
11+
class TestResultsTest(RiftTestCase):
12+
"""
13+
Tests class for TestResults
14+
"""
15+
16+
def test_init(self):
17+
""" TestResults instance """
18+
results = TestResults()
19+
20+
self.assertCountEqual(results.results, [])
21+
self.assertTrue(results.global_result)
22+
self.assertEqual(len(results), 0)
23+
24+
def test_add_success(self):
25+
results = TestResults()
26+
results.add_success(TestCase('test1', 'pkg', 'x86_64'), 1)
27+
results.add_success(TestCase('test2', 'pkg', 'x86_64'), 1)
28+
self.assertTrue(results.global_result)
29+
self.assertEqual(len(results), 2)
30+
31+
def test_add_failure(self):
32+
results = TestResults()
33+
results.add_failure(TestCase('test1', 'pkg', 'x86_64'), 1)
34+
results.add_failure(TestCase('test2', 'pkg', 'x86_64'), 1)
35+
self.assertFalse(results.global_result)
36+
self.assertEqual(len(results), 2)
37+
38+
def test_add_success_failure(self):
39+
results = TestResults()
40+
results.add_success(TestCase('test1', 'pkg', 'x86_64'), 1)
41+
results.add_failure(TestCase('test2', 'pkg', 'x86_64'), 1)
42+
self.assertFalse(results.global_result)
43+
self.assertEqual(len(results), 2)
44+
45+
def test_extend(self):
46+
results = TestResults()
47+
results.add_success(TestCase('test1', 'pkg1', 'x86_64'), 1)
48+
results.add_failure(TestCase('test2', 'pkg1', 'x86_64'), 1)
49+
others = TestResults()
50+
others.add_success(TestCase('test1', 'pkg2', 'x86_64'), 1)
51+
others.add_failure(TestCase('test2', 'pkg2', 'x86_64'), 1)
52+
results.extend(others)
53+
self.assertFalse(results.global_result)
54+
self.assertEqual(len(results), 4)
55+
56+
def test_junit(self):
57+
results = TestResults()
58+
results.add_success(TestCase('test1', 'pkg', 'x86_64'), 1, out="output test1")
59+
results.add_failure(TestCase('test2', 'pkg', 'x86_64'), 1, out="output test2")
60+
output = BytesIO()
61+
results.junit(output)
62+
root = ET.fromstring(output.getvalue().decode())
63+
self.assertEqual(root.tag, 'testsuite')
64+
self.assertEqual(root.attrib, { 'tests': '2'})
65+
self.assertEqual(len(root.findall('*')), 2)
66+
for element in root:
67+
self.assertEqual(element.tag, 'testcase')
68+
self.assertIn(element.attrib['name'], ['test1', 'test2'])
69+
self.assertEqual(element.attrib['classname'], 'rift.pkg')
70+
self.assertEqual(element.attrib['time'], '1.00')
71+
if element.attrib['name'] == 'test1':
72+
self.assertIsNone(element.find('failure'))
73+
self.assertEqual(element.find('system-out').text, 'output test1')
74+
else:
75+
self.assertIsNotNone(element.find('failure'))
76+
self.assertEqual(element.find('system-out').text, 'output test2')
77+
78+
def test_summary(self):
79+
results = TestResults()
80+
results.add_success(TestCase('test1', 'pkg', 'x86_64'), 1)
81+
results.add_failure(TestCase('test2', 'pkg', 'x86_64'), 1)
82+
self.assertEqual(
83+
results.summary(),
84+
textwrap.dedent(
85+
"""\
86+
NAME ARCH DURATION RESULT
87+
---- ---- -------- ------
88+
pkg.test1 x86_64 1s Success
89+
pkg.test2 x86_64 1s FAILURE"""
90+
)
91+
)

0 commit comments

Comments
 (0)