[PATCH 35/36] tuna: Add tests for run --cpuset functionality
John Kacur <[email protected]> Fri, 10 Jul 2026 10:15:13 -0400
| Newsgroups | org.kernel.vger.linux-rt-users |
|---|---|
| Message-ID | <[email protected]> |
Add comprehensive test coverage for the tuna run --cpuset feature, which allows launching processes directly in cpusets. Test coverage includes: - test_run_with_cpuset_basic: Verifies running commands in cpusets - test_run_with_cpuset_and_priority: Tests combining cpuset with priority settings (FIFO:50) - test_run_with_nonexistent_cpuset: Tests error handling for non-existent cpusets - test_run_cpuset_mutually_exclusive_with_cpus: Tests mutual exclusivity with -c option - test_run_help_shows_cpuset_option: Verifies --cpuset appears in help output Also updates: - Makefile: Adds test-run-cpuset target for running these tests - tests/README.md: Updates test counts from 142 to 146 total tests (45 non-root + 101 root-required), documents the new test class All tests require root permissions and cgroup v2 support. Tests create temporary cpusets, run commands, verify behavior, and clean up. Assisted-by: Claude:claude-sonnet-4-5 Signed-off-by: John Kacur <[email protected]> --- Makefile | 5 +- tests/README.md | 36 +++++++++------ tests/test_cpuset_cli.py | 99 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 125 insertions(+), 15 deletions(-) diff --git a/Makefile b/Makefile index b68c2a2a9925..9bee6cd24c1a 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ cleanlogs: .PHONY: clean clean: pyclean -.PHONY: tests unit-tests test-eperm test-converters test-cpuset test-cpuset-cli test-isolate-cpuset test-show-threads-cpuset test-show-threads-cgroups test-all +.PHONY: tests unit-tests test-eperm test-converters test-cpuset test-cpuset-cli test-isolate-cpuset test-run-cpuset test-show-threads-cpuset test-show-threads-cgroups test-all tests: unit-tests unit-tests: @@ -42,6 +42,9 @@ test-cpuset-cli: test-isolate-cpuset: @sudo python3 -m unittest tests.test_cpuset_cli.TestTunaIsolateCpusetCLI -v +test-run-cpuset: + @sudo python3 -m unittest tests.test_cpuset_cli.TestTunaRunCpusetCLI -v + test-show-threads-cpuset: @sudo python3 -m unittest tests.test_show_threads_cpuset -v diff --git a/tests/README.md b/tests/README.md index 799fc1ffe2dd..3e8b84a81fd4 100644 --- a/tests/README.md +++ b/tests/README.md @@ -12,7 +12,7 @@ This directory contains the test suite for tuna, using Python's `unittest` frame # Run all non-root tests only (45 tests) make tests -# Run ALL tests (all 142 tests, requires sudo for root-required tests) +# Run ALL tests (all 146 tests, requires sudo for root-required tests) make test-all # Run specific test module @@ -37,7 +37,7 @@ make tests # or make unit-tests -# Using make (ALL tests including root-required tests, 142 tests) +# Using make (ALL tests including root-required tests, 146 tests) make test-all # Using the test runner directly (non-root tests only) @@ -63,6 +63,7 @@ make test-converters make test-cpuset make test-cpuset-cli make test-isolate-cpuset +make test-run-cpuset make test-show-threads-cpuset make test-show-threads-cgroups @@ -167,7 +168,7 @@ Tests are organized using Python's `unittest` framework. Each test file contains - Tests edge cases: empty string and None inputs - Run: `python3 -m unittest tests.test_show_threads_cgroups.TestExtractCpusetName -v` -#### Root-Required Tests (97 tests) +#### Root-Required Tests (102 tests) - **test_process_blocklist.py** (2 tests) - Process blocklist root tests - Tests actual blocking of systemd from being moved to cpusets @@ -185,7 +186,7 @@ Tests are organized using Python's `unittest` framework. Each test file contains - Note: TestCpusetsInit (5 tests) and TestNumaDetection (6 tests) run without root, see non-root section - Run: `sudo python3 -m unittest tests.test_cpuset -v` (runs all 43 tests with root) -- **test_cpuset_cli.py** (45 tests) - CLI command tests +- **test_cpuset_cli.py** (50 tests) - CLI command tests - TestGetNextTunaCpusetName: Auto-naming logic (tuna0, tuna1, gap-filling) - TestCpusetCreateCLI: Create command with auto/custom names, --isolated, and NUMA auto-detection/override - TestCpusetListCLI: List command with --pattern, --verbose, --skip-empty @@ -199,6 +200,12 @@ Tests are organized using Python's `unittest` framework. Each test file contains - Tests cpuset-based CPU isolation with isolated and housekeeping cpusets - Verifies partition types, CPU assignments, and process migration - Tests error handling and backward compatibility + - TestTunaRunCpusetCLI: Integration of --cpuset with tuna run command (5 tests) + - test_run_with_cpuset_basic: Verifies running commands in cpusets + - test_run_with_cpuset_and_priority: Tests combining cpuset with priority settings + - test_run_with_nonexistent_cpuset: Tests error handling for non-existent cpusets + - test_run_cpuset_mutually_exclusive_with_cpus: Tests mutual exclusivity with -c + - test_run_help_shows_cpuset_option: Verifies --cpuset appears in help - Run: `sudo python3 -m unittest tests.test_cpuset_cli -v` - **test_show_threads_cpuset.py** (6 tests) - show_threads --cpuset filter tests @@ -219,7 +226,7 @@ Tests are organized using Python's `unittest` framework. Each test file contains - test_cgroups_displays_system_slice_shortened: Verifies system.slice shows shortened name - Run: `sudo python3 -m unittest tests.test_show_threads_cgroups.TestShowThreadsCgroupsDisplay -v` -**Total: 142 tests** (45 non-root + 97 root-required) +**Total: 146 tests** (45 non-root + 101 root-required) ### Test Requirements: Root vs Non-Root @@ -268,7 +275,7 @@ These tests run without root privileges: - Tests are fast and don't affect the running system - Tests verify both happy path (with mocks) and error path (with real EPERM) -#### Root-Required Tests (97 tests) +#### Root-Required Tests (102 tests) These tests require root to create/manipulate cgroups: @@ -278,7 +285,7 @@ These tests require root to create/manipulate cgroups: - Test task migration between cpusets - All tests clean up created cpusets in tearDown() -2. **Cpuset CLI tests** (45 tests) test command-line interface functions +2. **Cpuset CLI tests** (50 tests) test command-line interface functions - Test cpuset create/list/destroy/move/show/status/modify commands - Verify auto-naming logic (tuna0, tuna1, etc.) - Test NUMA-aware memory node auto-detection and manual override @@ -287,6 +294,7 @@ These tests require root to create/manipulate cgroups: - Test cpuset modification with --add-cpus, --remove-cpus, --memory-nodes, --isolated - Test tuna move --cpuset integration (5 tests) - Test tuna isolate --cpuset integration (8 tests) - cpuset-based CPU isolation + - Test tuna run --cpuset integration (5 tests) - running commands in cpusets - All tests clean up created cpusets afterwards 3. **show_threads --cpuset filter tests** (6 tests) test process filtering by cpuset @@ -435,12 +443,12 @@ test_extract_cpuset_name_none (test_show_threads_cgroups.TestExtractCpusetName) ---------------------------------------------------------------------- Ran 45 tests in 0.XXXs -OK (skipped=97) +OK (skipped=102) ``` -### All Tests Including Root-Required (142 tests) +### All Tests Including Root-Required (146 tests) -Running with sudo will run all 142 tests: +Running with sudo will run all 146 tests: ```bash $ sudo python3 -m unittest discover -s tests -p "test_*.py" -v @@ -450,12 +458,12 @@ test_cpuset_path (test_cpuset.TestCpusetsInit) ... ok ... (32 cpuset module tests) test_first_name_is_tuna0 (test_cpuset_cli.TestGetNextTunaCpusetName) ... ok test_sequential_naming (test_cpuset_cli.TestGetNextTunaCpusetName) ... ok -... (45 cpuset CLI tests) +... (50 cpuset CLI tests) ... (6 show_threads --cpuset tests) ... (4 show_threads -G display tests) ---------------------------------------------------------------------- -Ran 142 tests in X.XXXs +Ran 146 tests in X.XXXs OK ``` @@ -472,7 +480,7 @@ The test suite includes: - **show_threads --cpuset help test**: CLI documentation verification (read-only) - **Cpuset name extraction tests**: Cgroup path parsing logic (pure function testing) -### Integration Tests (97 tests - require root) +### Integration Tests (102 tests - require root) - **Process blocklist tests**: Test blocklist protection (2 root-required tests) - **Cpuset module tests**: Test interaction with cgroup v2 subsystem - **Cpuset CLI tests**: Test command-line interface integration @@ -502,7 +510,7 @@ python3 -m unittest discover -s tests -p "test_*.py" -v ### Root-Required Tests (Optional for CI) -The cpuset tests (97 tests) can run in CI with special setup: +The cpuset tests (102 tests) can run in CI with special setup: - Requires root access or privileged containers - Requires cgroup v2 support - May need dedicated test runners with appropriate permissions diff --git a/tests/test_cpuset_cli.py b/tests/test_cpuset_cli.py index cc51428cc55b..c95cea29b4a3 100644 --- a/tests/test_cpuset_cli.py +++ b/tests/test_cpuset_cli.py @@ -1195,6 +1195,104 @@ class TestTunaIsolateCpusetCLI(unittest.TestCase): self.assertFalse(os.path.exists('/sys/fs/cgroup/tuna_housekeeping')) [email protected](os.geteuid() == 0, "Requires root permissions") [email protected](cpuset.CpusetsInit().supported, "Requires cgroup v2 support") +class TestTunaRunCpusetCLI(unittest.TestCase): + """Test tuna run --cpuset command""" + + def setUp(self): + """Clean up before each test""" + self.test_cpuset = 'test_run_cpuset' + try: + cpuset.destroy_cpuset(self.test_cpuset, force=True) + except: + pass + + def tearDown(self): + """Clean up after each test""" + try: + cpuset.destroy_cpuset(self.test_cpuset, force=True) + except: + pass + + def test_run_with_cpuset_basic(self): + """Test running a command in a cpuset""" + # Create test cpuset + cs = cpuset.Cpuset(self.test_cpuset) + cs.assign_cpus('0-1') + cs.write_memnode('0') + + # Run a simple command in the cpuset + result = subprocess.run( + [sys.executable, 'tuna-cmd.py', 'run', '--cpuset', self.test_cpuset, 'echo hello'], + capture_output=True, + text=True + ) + + self.assertEqual(result.returncode, 0) + self.assertIn('hello', result.stdout) + + def test_run_with_cpuset_and_priority(self): + """Test running a command with both cpuset and priority""" + # Create test cpuset + cs = cpuset.Cpuset(self.test_cpuset) + cs.assign_cpus('0-1') + cs.write_memnode('0') + + # Run command with priority + result = subprocess.run( + [sys.executable, 'tuna-cmd.py', 'run', '--cpuset', self.test_cpuset, + '-p', 'FIFO:50', 'echo priority test'], + capture_output=True, + text=True + ) + + self.assertEqual(result.returncode, 0) + self.assertIn('priority test', result.stdout) + + def test_run_with_nonexistent_cpuset(self): + """Test error handling for non-existent cpuset""" + result = subprocess.run( + [sys.executable, 'tuna-cmd.py', 'run', '--cpuset', 'nonexistent_cpuset', 'echo test'], + capture_output=True, + text=True + ) + + self.assertNotEqual(result.returncode, 0) + self.assertIn('does not exist', result.stderr) + + def test_run_cpuset_mutually_exclusive_with_cpus(self): + """Test that --cpuset and -c are mutually exclusive""" + # Create test cpuset + cs = cpuset.Cpuset(self.test_cpuset) + cs.assign_cpus('0-1') + cs.write_memnode('0') + + # Try to use both --cpuset and -c + result = subprocess.run( + [sys.executable, 'tuna-cmd.py', 'run', '--cpuset', self.test_cpuset, + '-c', '0-1', 'echo test'], + capture_output=True, + text=True + ) + + # Should fail with error about mutually exclusive arguments + self.assertNotEqual(result.returncode, 0) + self.assertIn('not allowed with argument', result.stderr) + + def test_run_help_shows_cpuset_option(self): + """Test that --cpuset option appears in help""" + result = subprocess.run( + [sys.executable, 'tuna-cmd.py', 'run', '--help'], + capture_output=True, + text=True + ) + + self.assertEqual(result.returncode, 0) + self.assertIn('--cpuset', result.stdout) + self.assertIn('Run process in specified cpuset', result.stdout) + + def suite(): """Create test suite""" loader = unittest.TestLoader() @@ -1211,6 +1309,7 @@ def suite(): suite.addTests(loader.loadTestsFromTestCase(TestCpusetModifyCLI)) suite.addTests(loader.loadTestsFromTestCase(TestTunaMoveCpusetCLI)) suite.addTests(loader.loadTestsFromTestCase(TestTunaIsolateCpusetCLI)) + suite.addTests(loader.loadTestsFromTestCase(TestTunaRunCpusetCLI)) return suite -- 2.54.0