From: Zack Cerza Date: Sat, 30 May 2026 04:46:24 +0000 (-0600) Subject: scripts/tests: Speed up tests with importlib X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=dd4075a652189c88dead99e554e2cb8e932e1e14;p=teuthology.git scripts/tests: Speed up tests with importlib These take one second instead of 31. Signed-off-by: Zack Cerza --- diff --git a/scripts/test/script.py b/scripts/test/script.py index fdabd1b55..a178c8a3f 100644 --- a/scripts/test/script.py +++ b/scripts/test/script.py @@ -1,16 +1,53 @@ -import subprocess -from pytest import raises +import sys +import pytest +from importlib import import_module class Script(object): - script_name = 'teuthology' + script_name = "teuthology" + script_module = None # Override in subclasses, e.g., 'scripts.run' - def test_help(self): - args = (self.script_name, '--help') - out = subprocess.check_output(args).decode() - assert out.startswith('usage') + @pytest.fixture(scope="class") + def module_name(self) -> str: + # e.g., 'teuthology-dispatcher' -> 'scripts.dispatcher' + return self.script_name.replace("teuthology-", "").replace("teuthology", "run") - def test_invalid(self): - args = (self.script_name, '--invalid-option') - with raises(subprocess.CalledProcessError): - subprocess.check_call(args) + @pytest.fixture(scope="class") + def module(self, module_name): + return import_module(self.script_module or f"scripts.{module_name}") + + def test_help(self, capsys: pytest.CaptureFixture[str], module): + # docopt + if module.__doc__ and "usage" in module.__doc__.lower(): + return + if hasattr(module, "doc") and module.doc and "usage" in module.doc.lower(): + return + # argparse + if hasattr(module, "parse_args"): + with pytest.raises(SystemExit): + module.parse_args([]) + captured = capsys.readouterr() + assert "usage: " in captured.err + return + # If neither, fail + raise AssertionError( + f"{self.script_name} has neither a docstring/doc variable with usage info " + f"nor a parse_args function" + ) + + def test_invalid(self, module): + original_argv = sys.argv + try: + sys.argv = [self.script_name, "--invalid-option"] + with pytest.raises(SystemExit): + if hasattr(module, "parse_args"): + module.parse_args(sys.argv[1:]) + elif hasattr(module, "main"): + # For docopt-based scripts, main() will call docopt which exits on error + module.main() + else: + raise NotImplementedError( + f"Don't know how to test {self.script_name}" + ) + finally: + sys.argv = original_argv diff --git a/scripts/test/test_dispatcher_.py b/scripts/test/test_dispatcher_.py index 4d201aae5..3dc16593f 100644 --- a/scripts/test/test_dispatcher_.py +++ b/scripts/test/test_dispatcher_.py @@ -3,3 +3,4 @@ from script import Script class TestDispatcher(Script): script_name = 'teuthology-dispatcher' + script_module = 'scripts.dispatcher' diff --git a/scripts/test/test_exporter_.py b/scripts/test/test_exporter_.py index b0611a337..0d3c171eb 100644 --- a/scripts/test/test_exporter_.py +++ b/scripts/test/test_exporter_.py @@ -3,3 +3,4 @@ from script import Script class TestExporter(Script): script_name = 'teuthology-exporter' + script_module = 'scripts.exporter' diff --git a/scripts/test/test_lock.py b/scripts/test/test_lock.py index 3fc803aae..b1687c121 100644 --- a/scripts/test/test_lock.py +++ b/scripts/test/test_lock.py @@ -3,3 +3,4 @@ from script import Script class TestLock(Script): script_name = 'teuthology-lock' + script_module = 'scripts.lock' diff --git a/scripts/test/test_ls.py b/scripts/test/test_ls.py index d0e4d8145..296578960 100644 --- a/scripts/test/test_ls.py +++ b/scripts/test/test_ls.py @@ -8,6 +8,7 @@ doc = ls.__doc__ class TestLs(Script): script_name = 'teuthology-ls' + script_module = 'scripts.ls' def test_args(self): args = docopt.docopt(doc, ["--verbose", "some/archive/dir"]) diff --git a/scripts/test/test_prune_logs.py b/scripts/test/test_prune_logs.py index 8e967522f..1688017c2 100644 --- a/scripts/test/test_prune_logs.py +++ b/scripts/test/test_prune_logs.py @@ -3,3 +3,4 @@ from script import Script class TestPruneLogs(Script): script_name = 'teuthology-prune-logs' + script_module = 'scripts.prune_logs' diff --git a/scripts/test/test_report.py b/scripts/test/test_report.py index c8065fd1f..40b750a19 100644 --- a/scripts/test/test_report.py +++ b/scripts/test/test_report.py @@ -3,3 +3,4 @@ from script import Script class TestReport(Script): script_name = 'teuthology-report' + script_module = 'scripts.report' diff --git a/scripts/test/test_results.py b/scripts/test/test_results.py index a97981cb6..9f201b80e 100644 --- a/scripts/test/test_results.py +++ b/scripts/test/test_results.py @@ -3,3 +3,4 @@ from script import Script class TestResults(Script): script_name = 'teuthology-results' + script_module = 'scripts.results' diff --git a/scripts/test/test_run.py b/scripts/test/test_run.py index 74fa1b926..de33e6e69 100644 --- a/scripts/test/test_run.py +++ b/scripts/test/test_run.py @@ -8,6 +8,7 @@ doc = run.__doc__ class TestRun(Script): script_name = 'teuthology' + script_module = 'scripts.run' def test_all_args(self): args = docopt.docopt(doc, [ diff --git a/scripts/test/test_schedule.py b/scripts/test/test_schedule.py index e89f983a7..d69c13a7b 100644 --- a/scripts/test/test_schedule.py +++ b/scripts/test/test_schedule.py @@ -3,3 +3,4 @@ from script import Script class TestSchedule(Script): script_name = 'teuthology-schedule' + script_module = 'scripts.schedule' diff --git a/scripts/test/test_suite.py b/scripts/test/test_suite.py index 062aba470..f890c800d 100644 --- a/scripts/test/test_suite.py +++ b/scripts/test/test_suite.py @@ -3,3 +3,4 @@ from script import Script class TestSuite(Script): script_name = 'teuthology-suite' + script_module = 'scripts.suite' diff --git a/scripts/test/test_supervisor_.py b/scripts/test/test_supervisor_.py index 81298995c..dad7882c8 100644 --- a/scripts/test/test_supervisor_.py +++ b/scripts/test/test_supervisor_.py @@ -3,3 +3,4 @@ from script import Script class TestSupervisor(Script): script_name = 'teuthology-supervisor' + script_module = 'scripts.supervisor' diff --git a/scripts/test/test_updatekeys.py b/scripts/test/test_updatekeys.py index c4122b0f7..5a3882901 100644 --- a/scripts/test/test_updatekeys.py +++ b/scripts/test/test_updatekeys.py @@ -1,21 +1,22 @@ from script import Script -import subprocess +import docopt from pytest import raises from pytest import skip +from scripts import updatekeys class TestUpdatekeys(Script): script_name = 'teuthology-updatekeys' + script_module = 'scripts.updatekeys' def test_invalid(self): skip("teuthology.lock needs to be partially refactored to allow" + "teuthology-updatekeys to return nonzero in all erorr cases") def test_all_and_targets(self): - args = (self.script_name, '-a', '-t', 'foo') - with raises(subprocess.CalledProcessError): - subprocess.check_call(args) + with raises(docopt.DocoptExit): + docopt.docopt(updatekeys.doc, ['-a', '-t', 'foo']) def test_no_args(self): - with raises(subprocess.CalledProcessError): - subprocess.check_call(self.script_name) + with raises(docopt.DocoptExit): + docopt.docopt(updatekeys.doc, [])