* [PATCH v1] rteval: Added integration tests multi stressor support in stress-ng
@ 2026-10-30 13:49 Sana Sharma
0 siblings, 0 replies; only message in thread
From: Sana Sharma @ 2026-10-30 13:49 UTC (permalink / raw)
To: linux-rt-users; +Cc: jkacur
Added integrations test that checked the single-stressor and
multi-stressor support as well as the cpuset logic in this
context.
Assisted-by: Claude Opus 4.8
Signed-off-by: Sana Sharma <sansshar@redhat.com>
---
tests/test_stressng.py | 239 ++++++++++++++++++++++++++++++++++++++++-
1 file changed, 238 insertions(+), 1 deletion(-)
diff --git a/tests/test_stressng.py b/tests/test_stressng.py
index 29291e6..d684ff6 100644
--- a/tests/test_stressng.py
+++ b/tests/test_stressng.py
@@ -6,8 +6,11 @@
import sys
import subprocess
import unittest
+from types import SimpleNamespace
from unittest.mock import patch, MagicMock
-sys.path.insert(0,'.')
+from pathlib import Path
+
+sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from rteval.modules.loads import stressng
class TestGetValidStressors(unittest.TestCase):
@@ -50,6 +53,240 @@ class TestValidateStressor(unittest.TestCase):
with self.assertRaises(ValueError):
stressng.validate_stressors('bogus')
+
+"""Integration Tests for stress_ng stressor functionality"""
+
+class TestSingleStressor(unittest.TestCase):
+ """Test suite for single stressor case"""
+
+ def test_no_stressor_single(self):
+ """Test that the single stressor argument with no stressors throws the correct error"""
+ result = subprocess.run(
+ ["./rteval-cmd", "--stressng-stressor"],
+ capture_output=True,
+ text=True
+ )
+ assert result.returncode != 0
+ assert "usage: rteval-cmd" in result.stderr
+ assert "rteval-cmd: error: argument --stressng-stressor: expected one argument" in result.stderr
+
+ def test_default_worker_single(self):
+ """Test that for the single stressor the undefined workers defaults to 0"""
+ result = subprocess.run(
+ ["./rteval-cmd", "--stressng-stressor", "cpu"],
+ capture_output=True,
+ text=True
+ )
+ assert result.returncode == 0
+ assert "stressng: stress-ng --cpu 0 --taskset" in result.stdout
+
+ def test_set_worker_single(self):
+ """Test that single stressor with a set number of workers passes the number through"""
+ result = subprocess.run(
+ ["./rteval-cmd", "--stressng-stressor", "cpu", "--stressng-workers", "3"],
+ capture_output=True,
+ text=True
+ )
+ assert result.returncode == 0
+ assert "stressng: stress-ng --cpu 3 --taskset" in result.stdout
+
+ def test_single_with_multi(self):
+ """Tests that passing multiple stressors with the single stressor argument raises a Value Error """
+ result = subprocess.run(
+ ["./rteval-cmd", "--stressng-stressor", "cpu,vm", "--stressng-workers", "3"],
+ capture_output=True,
+ text=True
+ )
+ assert result.returncode != 0
+ assert "ValueError: Invalid stress-ng stressors: cpu,vm. Run 'stress-ng --stressors' to see valid options." in result.stderr
+
+ def test_multiple_singles(self):
+ """Tests that when multiple single stressors are passed with single arguments the code runs all the stressors """
+ result = subprocess.run(
+ ["./rteval-cmd", "--stressng-stressor", "cpu", "--stressng-stressor", "vm", "--stressng-workers", "3"],
+ capture_output=True,
+ text=True
+ )
+ assert result.returncode == 0
+ assert "stressng: stress-ng --cpu 3 --vm 3 --taskset" in result.stdout
+
+class TestMultiStressor(unittest.TestCase):
+
+ def test_no_stressor_multi(self):
+ """Tests that multi stressors with no stressor name leads to an error"""
+ result = subprocess.run(
+ ["./rteval-cmd", "--stressng-stressors"],
+ capture_output=True,
+ text=True
+ )
+ assert result.returncode != 0
+ assert "usage: rteval-cmd" in result.stderr
+ assert "rteval-cmd: error: argument --stressng-stressors: expected one argument" in result.stderr
+
+ def test_one_stressor_multi(self):
+ """Tests that multiple stressors with one stressor argument works"""
+ result = subprocess.run(
+ ["./rteval-cmd", "--stressng-stressors","cpu"],
+ capture_output=True,
+ text=True
+ )
+ assert result.returncode == 0
+ assert "stressng: stress-ng --cpu 0 --taskset" in result.stdout
+
+ def test_multi_stressor_default_workers(self):
+ """Tests that the default workers is 0"""
+ result = subprocess.run(
+ ["./rteval-cmd", "--stressng-stressors", "cpu,vm"],
+ capture_output=True,
+ text=True
+ )
+ assert result.returncode == 0
+ assert "stressng: stress-ng --cpu 0 --vm 0 --taskset" in result.stdout
+
+ def test_multi_stressor_set_workers(self):
+ """Tests that multiple stressors with a set number of workers passes correctly"""
+ result = subprocess.run(
+ ["./rteval-cmd", "--stressng-stressors", "cpu,vm", "--stressng-workers", "2"],
+ capture_output=True,
+ text=True
+ )
+ assert result.returncode == 0
+ assert "stressng: stress-ng --cpu 2 --vm 2 --taskset" in result.stdout
+
+class TestWorkloadPrepareCpuset(unittest.TestCase):
+ """
+ Integration tests for the cpuset/taskset logic in _WorkloadPrepare.
+
+ Each scenario is exercised for both ways of configuring stressors:
+ * single stressor form -> cfg.stressors is a list, e.g. ['cpu']
+ (this is what __init__ produces from --stressng-stressor)
+ * multi stressor form -> cfg.stressors is a comma-separated string,
+ e.g. 'cpu,vm' (this comes from --stressng-stressors)
+ """
+
+ # stressor configs covered by every cpuset scenario: (label, cfg.stressors,
+ # expected --<flag> options that should land in args)
+ STRESSOR_FORMS = (
+ ("single", ['cpu'], ['--cpu']),
+ ("multi", 'cpu,vm', ['--cpu', '--vm']),
+ )
+
+ @staticmethod
+ def make_stressng(stressors, workers='0', timeout=None, cpulist=""):
+ """Build a Stressng instance without running the heavy __init__.
+
+ Only the attributes read by _WorkloadPrepare() are populated. The
+ cfg.stressors attribute mirrors what __init__ would store: a list for
+ the single-stressor flag, or a comma-separated string for the
+ multi-stressor flag.
+ """
+ inst = stressng.Stressng.__new__(stressng.Stressng)
+ inst.cfg = SimpleNamespace(stressors=stressors, workers=workers,
+ timeout=timeout)
+ inst.cpulist = cpulist
+ inst._logging = False
+ inst._log = MagicMock()
+ inst.args = None
+ return inst
+
+ @staticmethod
+ def taskset_arg(args):
+ """Return the value following --taskset, or None if not present."""
+ if '--taskset' not in args:
+ return None
+ return args[args.index('--taskset') + 1]
+
+ @patch('rteval.modules.loads.stressng.get_valid_stressors',
+ return_value=['cpu', 'vm', 'matrix', 'io'])
+ @patch('rteval.modules.loads.stressng.os.open', return_value=42)
+ @patch('rteval.modules.loads.stressng.SysTopology')
+ def test_cpulist_restricts_taskset(self, mock_systop, _mock_open, _mock_valid):
+ """A cpulist keeps only its CPUs, intersected per node."""
+ topo = mock_systop.return_value
+ topo.getnodes.return_value = [0, 1]
+ topo.getcpus.side_effect = lambda n: {0: [0, 1, 2, 3],
+ 1: [4, 5, 6, 7]}[n]
+
+ for label, stressors, flags in self.STRESSOR_FORMS:
+ with self.subTest(form=label):
+ s = self.make_stressng(stressors=stressors, cpulist="0,1,4,5")
+ s._WorkloadPrepare()
+
+ self.assertEqual(self.taskset_arg(s.args), "0,1,4,5")
+ for flag in flags:
+ self.assertIn(flag, s.args)
+
+ @patch('rteval.modules.loads.stressng.get_valid_stressors',
+ return_value=['cpu', 'vm', 'matrix', 'io'])
+ @patch('rteval.modules.loads.stressng.os.open', return_value=42)
+ @patch('rteval.modules.loads.stressng.SysTopology')
+ def test_empty_node_is_dropped(self, mock_systop, _mock_open, _mock_valid):
+ """A node whose CPUs are all filtered out is removed from the taskset."""
+ topo = mock_systop.return_value
+ topo.getnodes.return_value = [0, 1]
+ topo.getcpus.side_effect = lambda n: {0: [0, 1, 2, 3],
+ 1: [4, 5, 6, 7]}[n]
+
+ # cpulist only matches CPUs on node 0, so node 1 has no usable CPUs
+ for label, stressors, flags in self.STRESSOR_FORMS:
+ with self.subTest(form=label):
+ s = self.make_stressng(stressors=stressors, cpulist="0,1")
+ s._WorkloadPrepare()
+
+ self.assertEqual(self.taskset_arg(s.args), "0,1")
+ for flag in flags:
+ self.assertIn(flag, s.args)
+
+ @patch('rteval.cpulist_utils.sysread', return_value="2-3")
+ @patch('rteval.cpulist_utils._isolated_file_exists', return_value=True)
+ @patch('rteval.modules.loads.stressng.get_valid_stressors',
+ return_value=['cpu', 'vm', 'matrix', 'io'])
+ @patch('rteval.modules.loads.stressng.os.open', return_value=42)
+ @patch('rteval.modules.loads.stressng.SysTopology')
+ def test_no_cpulist_excludes_isolated(self, mock_systop, _mock_open,
+ _mock_valid, _mock_isofile, _mock_sysread):
+ """Without a cpulist, isolated CPUs (2,3 here) are excluded."""
+ topo = mock_systop.return_value
+ topo.getnodes.return_value = [0]
+ topo.getcpus.side_effect = lambda n: [0, 1, 2, 3]
+
+ for label, stressors, flags in self.STRESSOR_FORMS:
+ with self.subTest(form=label):
+ s = self.make_stressng(stressors=stressors, cpulist="")
+ s._WorkloadPrepare()
+
+ self.assertEqual(self.taskset_arg(s.args), "0,1")
+ for flag in flags:
+ self.assertIn(flag, s.args)
+
+ @patch('rteval.modules.loads.stressng.get_valid_stressors',
+ return_value=['cpu', 'vm', 'matrix', 'io'])
+ @patch('rteval.modules.loads.stressng.os.open', return_value=42)
+ @patch('rteval.modules.loads.stressng.SysTopology')
+ def test_workers_and_timeout_appended(self, mock_systop, _mock_open, _mock_valid):
+ """Worker count and timeout are placed in args before --taskset."""
+ topo = mock_systop.return_value
+ topo.getnodes.return_value = [0]
+ topo.getcpus.side_effect = lambda n: [0, 1]
+
+ # single stressor: --cpu <workers> --timeout <t> ...
+ s = self.make_stressng(stressors=['cpu'], workers='3', timeout='30',
+ cpulist="0,1")
+ s._WorkloadPrepare()
+ self.assertEqual(s.args[:5],
+ ['stress-ng', '--cpu', '3', '--timeout', '30'])
+ self.assertEqual(self.taskset_arg(s.args), "0,1")
+
+ # multi stressor: each stressor gets the worker count, then --timeout
+ s = self.make_stressng(stressors='cpu,vm', workers='2', timeout='30',
+ cpulist="0,1")
+ s._WorkloadPrepare()
+ self.assertEqual(s.args[:7],
+ ['stress-ng', '--cpu', '2', '--vm', '2',
+ '--timeout', '30'])
+ self.assertEqual(self.taskset_arg(s.args), "0,1")
+
+
def main():
"""Run the test suite"""
unittest.main(verbosity=2)
--
2.54.0
--
Sana Sharma <sansshar@redhat.com>
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2026-10-30 13:49 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-30 13:49 [PATCH v1] rteval: Added integration tests multi stressor support in stress-ng Sana Sharma
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.