[Fuego] [PATCH] ftc: improve test class and defaults handling

Daniel Sangorrin daniel.sangorrin at toshiba.co.jp
Wed Jul 25 05:54:20 UTC 2018


Rename plantest_class to test_class because it represents
any test, not just a test in a testplan.

Move the test flags default values to the test_class to
have them shared (see DEFAULT_DEFAULTS).

Enforce the priority order of test flags (timeout, reboot, etc)
to satisfy the following:
commandline flags > per-test flags > default_xxx flags > DEFAULT_DEFAULTS

Signed-off-by: Daniel Sangorrin <daniel.sangorrin at toshiba.co.jp>
---
 engine/scripts/ftc | 173 ++++++++++++++++++++++++++++++-----------------------
 1 file changed, 98 insertions(+), 75 deletions(-)

diff --git a/engine/scripts/ftc b/engine/scripts/ftc
index cc1556d..92546bb 100755
--- a/engine/scripts/ftc
+++ b/engine/scripts/ftc
@@ -610,17 +610,28 @@ class board_class:
     # FIXTHIS - board_class should have methods to read board and dist file
 
 
-class plantest_class:
-    def __init__(self, test_dict, defaults):
+class test_class:
+    DEFAULT_DEFAULTS = {
+        'timeout' : '30m',
+        'spec'    : 'default',
+        'reboot'  : 'false',
+        'rebuild' : 'false',
+        'precleanup'  : 'true',
+        'postcleanup' : 'true'
+    }
+    def __init__(self, test_dict, test_flags={}):
+        merged_defaults = dict(self.DEFAULT_DEFAULTS)
+        for key, value in test_flags.iteritems():
+            merged_defaults[key] = value
         self.name = str(test_dict["testName"])
         self.test_type = self.name.split(".")[0]
         self.base_name = self.name.split(".")[1]
-        self.spec = str(test_dict.get("spec", defaults['spec']))
-        self.timeout = str(test_dict.get("timeout", defaults['timeout']))
-        self.reboot = str(test_dict.get("reboot", defaults['reboot']))
-        self.rebuild = str(test_dict.get("rebuild", defaults['rebuild']))
-        self.precleanup = str(test_dict.get("precleanup", defaults['precleanup']))
-        self.postcleanup = str(test_dict.get("postcleanup", defaults['postcleanup']))
+        self.spec = str(test_dict.get("spec", merged_defaults['spec']))
+        self.timeout = str(test_dict.get("timeout", merged_defaults['timeout']))
+        self.reboot = str(test_dict.get("reboot", merged_defaults['reboot']))
+        self.rebuild = str(test_dict.get("rebuild", merged_defaults['rebuild']))
+        self.precleanup = str(test_dict.get("precleanup", merged_defaults['precleanup']))
+        self.postcleanup = str(test_dict.get("postcleanup", merged_defaults['postcleanup']))
 
 
 class run_class:
@@ -1371,42 +1382,54 @@ def create_batch_job(board, testplan, plan_tests):
         sys.exit(1)
 
 
-# returns a list of plantest_class instances
-def parse_testplan(testplan, defaults):
+# parse the testplan and returns a list of test_class instances where
+# test flags (timeout, reboot, etc) have been merged in this order
+# commandline flags > per-test flags > default_xxx flags > DEFAULT_DEFAULTS
+def parse_testplan(testplan, test_dict):
     abspath = '/fuego-core/engine/overlays/testplans/' + testplan + '.json'
 
-    plan_tests = []
     with open(abspath, "r") as f:
         plan = json.load(f)
 
-    if 'default_timeout' in plan:
-        defaults['timeout'] = plan['default_timeout']
+    plan_tests = []
+    for plan_test_dict in plan['tests']:
+        # set testplan flags
+        test_flags = dict()
+        if 'default_timeout' in plan:
+            test_flags['timeout'] = plan['default_timeout']
 
-    if 'default_spec' in plan:
-        defaults['spec'] = plan['default_spec']
+        if 'default_spec' in plan:
+            test_flags['spec'] = plan['default_spec']
 
-    if 'default_reboot' in plan:
-        defaults['reboot'] = plan['default_reboot']
+        if 'default_reboot' in plan:
+            test_flags['reboot'] = plan['default_reboot']
 
-    if 'default_rebuild' in plan:
-        defaults['rebuild'] = plan['default_rebuild']
+        if 'default_rebuild' in plan:
+            test_flags['rebuild'] = plan['default_rebuild']
 
-    if 'default_precleanup' in plan:
-        defaults['precleanup'] = plan['default_precleanup']
+        if 'default_precleanup' in plan:
+            test_flags['precleanup'] = plan['default_precleanup']
 
-    if 'default_postcleanup' in plan:
-        defaults['postcleanup'] = plan['default_postcleanup']
+        if 'default_postcleanup' in plan:
+            test_flags['postcleanup'] = plan['default_postcleanup']
 
+        # override with testplan per-test flags
+        for key, value in plan_test_dict.iteritems():
+            if key == "testName":
+                test_dict[key] = value
+                continue
+            test_flags[key] = value
 
-    for test_dict in plan['tests']:
-        # test_dict is a dictionary with values for the test
-        test = plantest_class(test_dict, defaults)
+        # test_class overrides flags with those in test_dict and applies
+        # DEFAULT_DEFAULTS for non-specified flags
+        test = test_class(test_dict, test_flags)
         plan_tests.append(test)
 
     return plan_tests
 
 
 def do_add_jobs(conf, options):
+    test_dict = {}
     if '-b' in options:
         try:
             board = options[options.index('-b') + 1]
@@ -1414,16 +1437,15 @@ def do_add_jobs(conf, options):
             error_out("Board not provided after -b.")
         options.remove('-b')
         options.remove(board)
-
+        test_dict["board"] = board
         boards = board.split(",")
         board_list = get_fuego_boards(conf).keys()
         for board in boards:
             if board not in board_list:
-                raise Exception("Board '%s' not found." % board)
+                error_out("Board '%s' not found." % board)
     else:
-        raise Exception("No board name supplied.")
+        error_out("No board name supplied.")
 
-    rebuild = ''
     if '--rebuild' in options:
         try:
             rebuild = options[options.index('--rebuild') + 1]
@@ -1431,9 +1453,13 @@ def do_add_jobs(conf, options):
             error_out('Rebuild option not provided after --rebuild')
         if rebuild not in ['true', 'false']:
             error_out("Invalid rebuild option '%s'" % rebuild)
+        options.remove(rebuild)
+        options.remove('--rebuild')
+        test_dict["rebuild"] = rebuild
 
-    timeout = '30m'
-    if '-p' in options:
+    if '-p' in options and '-t' in options:
+        error_out('Testplan (-p) and test (-t) options are mutually exclusive.')
+    elif '-p' in options:
         try:
             testplan = options[options.index('-p') + 1]
         except IndexError:
@@ -1447,52 +1473,43 @@ def do_add_jobs(conf, options):
     elif '-t' in options:
         # FIXTHIS: have add-jobs support wildcards in the test name
         test_name, options = get_test_arg("Add-jobs", conf, options)
-        if '-s' in options:
-            try:
-                spec = options[options.index('-s') + 1]
-            except IndexError:
-                error_out('Testspec not provided after -s.')
-            specnames = get_specs(conf, test_name)
-            if spec not in specnames:
-                error_out('Unknown spec %s' % spec)
-            options.remove('-s')
-            options.remove(spec)
-        else:
-            spec = 'default'
-        if '-k' in options:
-            try:
-                timeout = options[options.index('-k') + 1]
-                options.remove('-k')
-            except IndexError:
-                error_out('No timeout specified after -k')
-
+        test_dict["testName"] = test_name
     else:
-        error_out('No testplan or testcase supplied.')
+        error_out('No testplan (-p) or test (-t) supplied.')
 
-    defaults = {
-        'timeout' : '30m',
-        'spec'    : 'default',
-        'reboot'  : 'false',
-        'rebuild' : 'false',
-        'precleanup'  : 'true',
-        'postcleanup' : 'true'
-    }
+    if '-s' in options:
+        if test_name is None:
+            error_out("-s option requires the test option (-t) to be set")
+        try:
+            spec = options[options.index('-s') + 1]
+        except IndexError:
+            error_out('Testspec not provided after -s.')
+        specnames = get_specs(conf, test_name)
+        if spec not in specnames:
+            error_out('Unknown spec %s' % spec)
+        options.remove('-s')
+        options.remove(spec)
+        test_dict["spec"] = spec
+
+    if '-k' in options:
+        try:
+            timeout = options[options.index('-k') + 1]
+        except IndexError:
+            error_out('No timeout specified after -k')
+        if re.match('^\d+[dhms]', timeout) is None:
+            error_out('%s: Timeout format not supported.' % timeout)
+        options.remove('-k')
+        options.remove(timeout)
+        test_dict["timeout"] = timeout
 
     if test_name:
-        # FIXTHIS - we could parse more parameters for the job here, from the ftc command line
-        # use all defaults, except for the spec
-        tp_dict = {"testName": test_name, "timeout": timeout, "spec": spec }
-        if rebuild:
-            tp_dict["rebuild"] = rebuild
-        test = plantest_class(tp_dict, defaults)
+        test = test_class(test_dict)
         for board in boards:
             create_job(board, test)
     else:
-        plan_tests = parse_testplan(testplan, defaults)
+        plan_tests = parse_testplan(testplan, test_dict)
         for board in boards:
             for test in plan_tests:
-                if rebuild:
-                    test.rebuild = rebuild
                 create_job(board, test)
             create_batch_job(board, testplan, plan_tests)
     sys.exit(0)
@@ -3102,10 +3119,14 @@ def do_run_test(conf, options):
     board, options = get_board_arg("Run-test", conf, options)
     board_name = board.name
 
-    test_name = None
+    test_dict = {}
+
     if '-t' in options:
         # FIXTHIS: have run-test support wildcards in the test name
         test_name, options = get_test_arg("Run-test", conf, options)
+        test_dict["testName"] = test_name
+    else:
+        error_out("Run-test command requires a test name")
 
     if '-s' in options:
         try:
@@ -3117,8 +3138,10 @@ def do_run_test(conf, options):
             error_out('Unknown spec %s' % spec_name)
         options.remove(spec_name)
         options.remove('-s')
-    else:
-        spec_name = 'default'
+        test_dict["spec"] = spec_name
+
+    # apply defaults for test flags that were not specified on the command line
+    test = test_class(test_dict=test_dict)
 
     if '-p' in options:
         phase_map = { "p": "pre_test",
@@ -3153,7 +3176,7 @@ def do_run_test(conf, options):
         fuego_caller = "ftc"
         print "Notice: non-Jenkins test request detected"
 
-    print "Running test '%s' on board '%s' using spec '%s'" % (test_name, board_name, spec_name)
+    print "Running test '%s' on board '%s' using spec '%s'" % (test_name, board_name, test.spec)
 
     # check if there's a Jenkins job that matches this request
     # Job names have the syntax: <board>.<spec>.<test_name>
@@ -3181,9 +3204,9 @@ def do_run_test(conf, options):
     build_data.board_name = board_name
     build_data.test_name = test_name
     build_data.testplan_name = "None"
-    build_data.spec_name = spec_name
+    build_data.spec_name = test.spec
     build_data.testdir = test_name
-    build_data.job_name = job_name
+    build_data.job_name = "%s.%s.%s" % (board_name, test.spec, test_name)
     build_data.workspace = conf.FUEGO_RW+"/buildzone"
     build_data.start_time = long(time.time() * 1000)
 
-- 
2.7.4



More information about the Fuego mailing list