From beb4c2bc49a6507673bafe184ec09d12e440616c Mon Sep 17 00:00:00 2001
From: Martyn Gigg <martyn.gigg@gmail.com>
Date: Wed, 21 Aug 2019 16:17:10 +0100
Subject: [PATCH] Add option to ignore module import failure for system tests

It helps in some circumstances where not all dependencies
are available.
---
 .../SystemTests/lib/systemtests/systemtesting.py  | 15 ++++++++++-----
 Testing/SystemTests/scripts/InstallerTests.py     |  4 ++++
 Testing/SystemTests/scripts/runSystemTests.py     | 13 ++++++++-----
 3 files changed, 22 insertions(+), 10 deletions(-)

diff --git a/Testing/SystemTests/lib/systemtests/systemtesting.py b/Testing/SystemTests/lib/systemtests/systemtesting.py
index 02fb9ca8b0d..300875c94b1 100644
--- a/Testing/SystemTests/lib/systemtests/systemtesting.py
+++ b/Testing/SystemTests/lib/systemtests/systemtesting.py
@@ -775,9 +775,9 @@ class TestManager(object):
     '''
 
     def __init__(self, test_loc=None, runner=None, output=[TextResultReporter()],
-                 quiet=False, testsInclude=None, testsExclude=None, showSkipped=False,
-                 exclude_in_pr_builds=None, output_on_failure=False, clean=False,
-                 process_number=0, ncores=1, list_of_tests=None):
+                 quiet=False, testsInclude=None, testsExclude=None, ignore_failed_imports=False,
+                 showSkipped=False, exclude_in_pr_builds=None, output_on_failure=False,
+                 clean=False, process_number=0, ncores=1, list_of_tests=None):
         '''Initialize a class instance'''
 
         # Runners and reporters
@@ -794,6 +794,7 @@ class TestManager(object):
         self._testsInclude=testsInclude
         self._testsExclude=testsExclude
         self._exclude_in_pr_builds=exclude_in_pr_builds
+        self._ignore_failed_imports = ignore_failed_imports
 
         self._passedTests = 0
         self._skippedTests = 0
@@ -803,7 +804,6 @@ class TestManager(object):
         self._tests = list_of_tests
 
     def generateMasterTestList(self):
-
         # If given option is a directory
         if os.path.isdir(self._testDir):
             test_dir = os.path.abspath(self._testDir).replace('\\', '/')
@@ -820,7 +820,10 @@ class TestManager(object):
             all_loaded, full_test_list = self.loadTestsFromModule(os.path.basename(self._testDir))
 
         if not all_loaded:
-            sys.exit("\nUnable to load all test modules. Cannot continue.")
+            if  self._ignore_failed_imports:
+                print('\nUnable to load all test modules. Continuing as requested.')
+            else:
+                sys.exit('\nUnable to load all test modules. Cannot continue.')
 
         # Gather statistics on full test list
         test_stats = [0, 0, 0]
@@ -1015,6 +1018,8 @@ class TestManager(object):
             import traceback
             traceback.print_exc()
             module_loaded = False
+            # append a test with a blank name to indicate it was skipped
+            tests.append(TestSuite(self._runner.getTestDir(), modname, None, filename))
         finally:
             pyfile.close()
 
diff --git a/Testing/SystemTests/scripts/InstallerTests.py b/Testing/SystemTests/scripts/InstallerTests.py
index 186301cabdd..0823320e508 100644
--- a/Testing/SystemTests/scripts/InstallerTests.py
+++ b/Testing/SystemTests/scripts/InstallerTests.py
@@ -34,6 +34,8 @@ parser.add_argument('--archivesearch', dest='archivesearch', action='store_true'
                     help='Turn on archive search for file finder')
 parser.add_argument('--exclude-in-pull-requests', dest="exclude_in_pr_builds",action="store_true",
                     help="Skip tests that are not run in pull request builds")
+parser.add_argument('--ignore-failed-imports', dest="ignore_failed_imports", action="store_true",
+                    help="Skip tests that do not import correctly rather raising an error.")
 log_levels = ['error', 'warning', 'notice', 'information', 'debug']
 parser.add_argument('-l', dest='log_level', metavar='level', default='information',
                     choices=log_levels, help='Log level '+str(log_levels))
@@ -99,6 +101,8 @@ try:
         run_test_cmd += ' --archivesearch'
     if options.exclude_in_pr_builds:
         run_test_cmd += ' --exclude-in-pull-requests'
+    if options.ignore_failed_imports:
+        run_test_cmd += ' --ignore-failed-imports'
     if options.out2stdout:
         print("Executing command '{0}'".format(run_test_cmd))
         p = subprocess.Popen(run_test_cmd, shell=True) # no PIPE: print on screen for debugging
diff --git a/Testing/SystemTests/scripts/runSystemTests.py b/Testing/SystemTests/scripts/runSystemTests.py
index 4a0510e6446..19470c1e1e8 100755
--- a/Testing/SystemTests/scripts/runSystemTests.py
+++ b/Testing/SystemTests/scripts/runSystemTests.py
@@ -10,14 +10,14 @@ from __future__ import (absolute_import, division, print_function)
 
 import optparse
 import os
-# If any tests happen to hit a PyQt4 import make sure item uses version 2 of the api
-# Remove this when everything is switched to qtpy
-import sip
 import sys
 import time
 from multiprocessing import Process, Array, Manager, Value, Lock
 
 try:
+   # If any tests happen to hit a PyQt4 import make sure item uses version 2 of the api
+   # Remove this when everything is switched to qtpy
+    import sip
     sip.setapi('QString', 2)
     sip.setapi('QVariant', 2)
     sip.setapi('QDate', 2)
@@ -25,7 +25,7 @@ try:
     sip.setapi('QTextStream', 2)
     sip.setapi('QTime', 2)
     sip.setapi('QUrl', 2)
-except AttributeError:
+except (AttributeError, ImportError):
     # PyQt < v4.6
     pass
 
@@ -100,6 +100,8 @@ def main():
                       help="Turn on archive search for file finder.")
     parser.add_option("", "--exclude-in-pull-requests", dest="exclude_in_pr_builds", action="store_true",
                       help="Skip tests that are not run in pull request builds")
+    parser.add_option("", "--ignore-failed-imports", dest="ignore_failed_imports", action="store_true",
+                      help="Skip tests that do not import correctly rather raising an error.")
     parser.set_defaults(frameworkLoc=DEFAULT_FRAMEWORK_LOC, executable=sys.executable, makeprop=True,
                         loglevel="information", ncores=1, quiet=False, output_on_failure=False, clean=False)
     (options, args) = parser.parse_args()
@@ -147,7 +149,8 @@ def main():
                                      quiet=options.quiet,
                                      testsInclude=options.testsInclude,
                                      testsExclude=options.testsExclude,
-                                     exclude_in_pr_builds=options.exclude_in_pr_builds)
+                                     exclude_in_pr_builds=options.exclude_in_pr_builds,
+                                     ignore_failed_imports=options.ignore_failed_imports)
 
     test_counts, test_list, test_stats, files_required_by_test_module, data_file_lock_status = \
         tmgr.generateMasterTestList()
-- 
GitLab