Skip to content

Commit 9833125

Browse files
committed
TestResults: introduce extend() method
This notably allows removing results argument from many functions in Controller. One motivation of this change is to satisfy pylint which emits warnings when there are more than 5 arguments on a function. A testcase is also introduced to cover all method of TestResults class.
1 parent af6e0cf commit 9833125

File tree

3 files changed

+151
-38
lines changed

3 files changed

+151
-38
lines changed

lib/rift/Controller.py

Lines changed: 51 additions & 38 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
568+
568569

569-
def validate_pkgs(config, args, results, pkgs, arch):
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,8 +747,12 @@ def action_vm(args, config):
742747
ret = vm_build(vm, args, config)
743748
return ret
744749

745-
def build_pkgs(config, args, pkgs, arch, results):
746-
"""Build a list of packages on a given architecture and report results."""
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+
747756
for pkg in pkgs:
748757
case = TestCase('build', pkg.name, arch)
749758
now = time.time()
@@ -774,6 +783,8 @@ def build_pkgs(config, args, pkgs, arch, results):
774783
else:
775784
results.add_success(case, time.time() - now)
776785

786+
return results
787+
777788
def action_build(args, config):
778789
"""Action for 'build' command."""
779790

@@ -794,7 +805,7 @@ def action_build(args, config):
794805
for arch in config.get('arch'):
795806

796807
pkgs = Package.list(config, staff, modules, args.packages)
797-
build_pkgs(config, args, pkgs, arch, results)
808+
results.extend(build_pkgs(config, args, pkgs, arch))
798809

799810
if getattr(args, 'junit', False):
800811
logging.info('Writing test results in %s', args.junit)
@@ -825,12 +836,13 @@ def action_test(args, config):
825836
results = TestResults('test')
826837
# Test package on all project supported architectures
827838
for arch in config.get('arch'):
828-
test_pkgs(
829-
config,
830-
args,
831-
results,
832-
Package.list(config, staff, modules, args.packages),
833-
arch
839+
results.extend(
840+
test_pkgs(
841+
config,
842+
args,
843+
Package.list(config, staff, modules, args.packages),
844+
arch
845+
)
834846
)
835847
if getattr(args, 'junit', False):
836848
logging.info('Writing test results in %s', args.junit)
@@ -856,12 +868,13 @@ def action_validate(args, config):
856868
results = TestResults('validate')
857869
# Validate packages on all project supported architectures
858870
for arch in config.get('arch'):
859-
validate_pkgs(
860-
config,
861-
args,
862-
results,
863-
Package.list(config, staff, modules, args.packages),
864-
arch
871+
results.extend(
872+
validate_pkgs(
873+
config,
874+
args,
875+
Package.list(config, staff, modules, args.packages),
876+
arch
877+
)
865878
)
866879
banner('All packages checked on all architectures')
867880

@@ -894,7 +907,7 @@ def action_validdiff(args, config):
894907
# Re-validate all updated packages for all architectures supported by the
895908
# project.
896909
for arch in config.get('arch'):
897-
validate_pkgs(config, args, results, updated.values(), arch)
910+
results.extend(validate_pkgs(config, args, updated.values(), arch))
898911

899912

900913
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.

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)