validate:launcher: Ensure a positive job count.
authorBrady J. Garvin <bgarvin@cse.unl.edu>
Sat, 30 Jan 2021 16:01:54 +0000 (10:01 -0600)
committerBrady J. Garvin <bgarvin@cse.unl.edu>
Sat, 30 Jan 2021 16:11:57 +0000 (10:11 -0600)
The default number of jobs to use is half of the available cores
rounded down, but in situations where only one core is available (such
as under some VMs), this means that `gst-validate-launcher` defaults
to using zero jobs, a case that the test-running code is not prepared
to handle.

This change makes the code match the documentation for the `--jobs` option,
guards against negative values both in the default setting and in argument
parsing, and introduces some defensive programming to prevent other situations
where the code might try to use zero jobs.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-devtools/-/merge_requests/154>

validate/launcher/apps/gstcheck.py
validate/launcher/baseclasses.py
validate/launcher/main.py

index 7cd5a94..8b29fb8 100644 (file)
@@ -372,6 +372,7 @@ class GstCheckTestsManager(MesonTestsManager):
             to_inspect.append(test)
 
         if to_inspect:
+            assert self.options.num_jobs >= 0
             executor = conc.ThreadPoolExecutor(
                 max_workers=self.options.num_jobs)
             tmp = []
index 67c4842..00832bc 100644 (file)
@@ -2091,7 +2091,8 @@ class _TestsLauncher(Loggable):
             else:
                 alone_tests.append(test)
 
-        max_num_jobs = min(self.options.num_jobs, len(tests))
+        # use max to defend against the case where all tests are alone_tests
+        max_num_jobs = max(min(self.options.num_jobs, len(tests)), 1)
         jobs_running = 0
 
         if self.options.forever and len(tests) < self.options.num_jobs and len(tests):
index 9214b80..745a774 100644 (file)
@@ -181,6 +181,13 @@ class PrintUsage(argparse.Action):
         parser.exit()
 
 
+def _positive_integer_type(value):
+    cast = int(value)
+    if cast <= 0:
+        raise argparse.ArgumentTypeError(f'`{value}\' is not a positive integer')
+    return cast
+
+
 class LauncherConfig(Loggable):
 
     def __init__(self):
@@ -210,7 +217,7 @@ class LauncherConfig(Loggable):
         self.logsdir = None
         self.privatedir = None
         self.redirect_logs = False
-        self.num_jobs = int(multiprocessing.cpu_count() / 2)
+        self.num_jobs = max(multiprocessing.cpu_count(), 1)
         self.dest = None
         self._using_default_paths = False
         # paths passed with --media-path, and not defined by a testsuite
@@ -543,8 +550,8 @@ class LauncherConfig(Loggable):
                                help="Redirect logs to stdout.")
         dir_group.add_argument("-j", "--jobs", dest="num_jobs",
                                help="Number of tests to execute simultaneously"
-                               " (Defaults to number of cores of the processor)",
-                               type=int)
+                               " (Defaults to the number of cores of the processor)",
+                               type=_positive_integer_type)
         dir_group.add_argument("--ignore-numfailures", dest="ignore_numfailures",
                                help="Ignore the number of failed test in exit code",
                                default=False, action='store_true')