From 66ef9457cba0c2b311a7f57a5dc2a4b68a00e0cb Mon Sep 17 00:00:00 2001 From: Pau Espin Pedrol Date: Mon, 25 May 2020 13:26:41 +0200 Subject: [PATCH] config: suites_dir and scenarios_dir are now a list of paths This allows inheriting suites or scenarios from eg. sysmocom/ dir, while still allowing to apply new suites and scenarios on top. Change-Id: Icecdae32d400a6b6da2ebf167c1c795f7a74ae96 --- doc/examples/2g_osmocom/main.conf | 4 +-- doc/examples/4g_srsLTE/main.conf | 4 +-- doc/manuals/chapters/config.adoc | 14 +++++--- selftest/resource_test/conf/paths.conf | 2 +- selftest/resource_test/resource_test.ok | 4 +-- selftest/scenario_test/paths.conf | 4 +-- selftest/scenario_test/scenario_test.ok | 10 +++--- selftest/suite_test/paths.conf | 2 +- selftest/suite_test/suite_test.ok | 9 ++--- .../{ => suitedirA}/empty_dir/.unrelated_file | 0 .../{ => suitedirA}/test_suite/hello_world.py | 0 .../{ => suitedirA}/test_suite/mo_mt_sms.py | 0 .../{ => suitedirA}/test_suite/mo_sms.py | 0 .../{ => suitedirA}/test_suite/suite.conf | 0 .../{ => suitedirA}/test_suite/test_error.py | 0 .../{ => suitedirA}/test_suite/test_fail.py | 0 .../test_suite/test_fail_raise.py | 0 .../test_suite/test_suite_params.py | 0 .../suite_test/suitedirB/suiteB/suite.conf | 4 +++ .../suitedirB/suiteB/test_success.py | 5 +++ src/osmo_gsm_tester/core/config.py | 29 ++++++++------- src/osmo_gsm_tester/core/scenario.py | 36 +++++++++++++------ src/osmo_gsm_tester/core/suite.py | 16 +++++---- sysmocom/main.conf | 4 +-- sysmocom/ttcn3/main.conf | 4 +-- 25 files changed, 96 insertions(+), 55 deletions(-) rename selftest/suite_test/{ => suitedirA}/empty_dir/.unrelated_file (100%) rename selftest/suite_test/{ => suitedirA}/test_suite/hello_world.py (100%) rename selftest/suite_test/{ => suitedirA}/test_suite/mo_mt_sms.py (100%) rename selftest/suite_test/{ => suitedirA}/test_suite/mo_sms.py (100%) rename selftest/suite_test/{ => suitedirA}/test_suite/suite.conf (100%) rename selftest/suite_test/{ => suitedirA}/test_suite/test_error.py (100%) rename selftest/suite_test/{ => suitedirA}/test_suite/test_fail.py (100%) rename selftest/suite_test/{ => suitedirA}/test_suite/test_fail_raise.py (100%) rename selftest/suite_test/{ => suitedirA}/test_suite/test_suite_params.py (100%) create mode 100644 selftest/suite_test/suitedirB/suiteB/suite.conf create mode 100755 selftest/suite_test/suitedirB/suiteB/test_success.py diff --git a/doc/examples/2g_osmocom/main.conf b/doc/examples/2g_osmocom/main.conf index b8105196..699c5183 100644 --- a/doc/examples/2g_osmocom/main.conf +++ b/doc/examples/2g_osmocom/main.conf @@ -1,6 +1,6 @@ state_dir: '/var/tmp/osmo-gsm-tester/state' -suites_dir: './suites' -scenarios_dir: './scenarios' +suites_dir: ['./suites'] +scenarios_dir: ['./scenarios'] default_suites_conf_path: './default-suites.conf' defaults_conf_path: './defaults.conf' resource_conf_path: './resources.conf' diff --git a/doc/examples/4g_srsLTE/main.conf b/doc/examples/4g_srsLTE/main.conf index b8105196..699c5183 100644 --- a/doc/examples/4g_srsLTE/main.conf +++ b/doc/examples/4g_srsLTE/main.conf @@ -1,6 +1,6 @@ state_dir: '/var/tmp/osmo-gsm-tester/state' -suites_dir: './suites' -scenarios_dir: './scenarios' +suites_dir: ['./suites'] +scenarios_dir: ['./scenarios'] default_suites_conf_path: './default-suites.conf' defaults_conf_path: './defaults.conf' resource_conf_path: './resources.conf' diff --git a/doc/manuals/chapters/config.adoc b/doc/manuals/chapters/config.adoc index 17cf825e..18ff55dd 100644 --- a/doc/manuals/chapters/config.adoc +++ b/doc/manuals/chapters/config.adoc @@ -26,13 +26,19 @@ then overrides the above locations. {app-name} expects to find the following configuration settings in 'main.conf': - 'state_dir': Path to <> directory -- 'suites_dir': Path to <> directory - 'trial_dir': Path to <> directory to test against (overridden by cmdline argument) -- 'scenarios_dir': Path to <> directory (optional) +- 'suites_dir': List of paths to <> directories. +- 'scenarios_dir': List of paths to <> directories (optional) - 'default_suites_conf_path': Path to <> file (optional) - 'defaults_conf_path': Path to <> file (optional) - 'resource_conf_path': Path to <> file (optional) +Configuration settings holding a list of paths, such as 'suites_dir' or +'scenarios_dir', are used to look up for paths in regular list of order, meaning +first paths in list take preference over last ones. As a result, if a suite +named 'A' is found in several paths, the one on the first path in the list will +be used. + These are described in detail in the following sections. If no value is provided for a given setting, sane default paths are used: For 'state_dir', '/var/tmp/osmo-gsm-tester/state/' is used. All other files and directories are @@ -45,8 +51,8 @@ of the {app-name} process parsing it. .Sample main.conf file: ---- state_dir: '/var/tmp/osmo-gsm-tester/state' -suites_dir: '/usr/local/src/osmo-gsm-tester/suites' -scenarios_dir: './scenarios' +suites_dir: [ '/usr/local/src/osmo-gsm-tester/suites' ] +scenarios_dir: [ './scenarios' ] trial_dir: './trial' default_suites_conf_path: './default-suites.conf' defaults_conf_path: './defaults.conf' diff --git a/selftest/resource_test/conf/paths.conf b/selftest/resource_test/conf/paths.conf index 0b2d0355..3280a12a 100644 --- a/selftest/resource_test/conf/paths.conf +++ b/selftest/resource_test/conf/paths.conf @@ -1,2 +1,2 @@ state_dir: ./test_work/state_dir -suites_dir: ./suite_test +suites_dir: ['./suite_test'] diff --git a/selftest/resource_test/resource_test.ok b/selftest/resource_test/resource_test.ok index 0ad760ed..91acaaf0 100644 --- a/selftest/resource_test/resource_test.ok +++ b/selftest/resource_test/resource_test.ok @@ -14,9 +14,9 @@ cnf -: DBG: MAIN CONFIG: {'default_suites_conf_path': '[PATH]/selftest/resource_test/conf/default-suites.conf', 'defaults_conf_path': '[PATH]/selftest/resource_test/conf/defaults.conf', 'resource_conf_path': '[PATH]/selftest/resource_test/conf/resources.conf', - 'scenarios_dir': '[PATH]/selftest/resource_test/conf/scenarios', + 'scenarios_dir': ['[PATH]/selftest/resource_test/conf/scenarios'], 'state_dir': '[PATH]/selftest/resource_test/conf/test_work/state_dir', - 'suites_dir': '[PATH]/selftest/resource_test/conf/suite_test', + 'suites_dir': ['[PATH]/selftest/resource_test/conf/suite_test'], 'trial_dir': '[PATH]/selftest/resource_test/conf/trial'} *** all resources: {'arfcn': [{'_hash': 'e620569450f8259b3f0212ec19c285dd07df063c', diff --git a/selftest/scenario_test/paths.conf b/selftest/scenario_test/paths.conf index c7df5ac3..11e59bf0 100644 --- a/selftest/scenario_test/paths.conf +++ b/selftest/scenario_test/paths.conf @@ -1,3 +1,3 @@ state_dir: ./test_work/state_dir -suites_dir: . -scenarios_dir: . +suites_dir: ['.'] +scenarios_dir: ['.'] diff --git a/selftest/scenario_test/scenario_test.ok b/selftest/scenario_test/scenario_test.ok index e845f4ca..06db4f49 100644 --- a/selftest/scenario_test/scenario_test.ok +++ b/selftest/scenario_test/scenario_test.ok @@ -3,9 +3,9 @@ cnf -: DBG: MAIN CONFIG: {'default_suites_conf_path': '[PATH]/selftest/scenario_test/default-suites.conf', 'defaults_conf_path': '[PATH]/selftest/scenario_test/defaults.conf', 'resource_conf_path': '[PATH]/selftest/scenario_test/resources.conf', - 'scenarios_dir': '[PATH]/selftest/scenario_test', + 'scenarios_dir': ['[PATH]/selftest/scenario_test'], 'state_dir': '[PATH]/selftest/scenario_test/test_work/state_dir', - 'suites_dir': '[PATH]/selftest/scenario_test', + 'suites_dir': ['[PATH]/selftest/scenario_test'], 'trial_dir': '[PATH]/selftest/scenario_test/trial'} scenario_case_01.conf {'anotherlist': ['4', '0'], @@ -19,11 +19,11 @@ scenario_case_01.conf 'somelist': [{'somelistitem': 'firststring'}, {'somelistitem': 'secondstring'}, {'somelistitem': 'thirdstring'}]} -OK: expected RuntimeError: No such scenario file: '[PATH]/selftest/scenario_test/scenario_case_01@.conf' (nor scenario_case_01@.conf) +OK: expected RuntimeError: No such scenario file 'scenario_case_01@.conf' (nor scenario_case_01@.conf) in [[PATH]/selftest/scenario_test] OK: expected ValueError OK: expected ValueError -OK: expected RuntimeError: No such scenario file: '[PATH]/selftest/scenario_test/scenario_case_03.conf' -OK: expected RuntimeError: No such scenario file: '[PATH]/selftest/scenario_test/scenario_case_03.conf' +OK: expected RuntimeError: No such scenario file scenario_case_03.conf in [[PATH]/selftest/scenario_test] +OK: expected RuntimeError: No such scenario file scenario_case_03.conf in [[PATH]/selftest/scenario_test] tst scenario_case_03@heyho,1,yes.conf: DBG: {param_dict={param1='heyho', param2='1', param3='yes'}} scenario_case_03@heyho,1,yes.conf {'anotherlist': ['1', '0'], diff --git a/selftest/suite_test/paths.conf b/selftest/suite_test/paths.conf index 2b0a274a..a2f91243 100644 --- a/selftest/suite_test/paths.conf +++ b/selftest/suite_test/paths.conf @@ -1,2 +1,2 @@ state_dir: ./test_work/state_dir -suites_dir: . +suites_dir: ['suitedirA', 'suitedirB'] diff --git a/selftest/suite_test/suite_test.ok b/selftest/suite_test/suite_test.ok index d55317b2..fee07d00 100644 --- a/selftest/suite_test/suite_test.ok +++ b/selftest/suite_test/suite_test.ok @@ -4,14 +4,15 @@ cnf -: DBG: MAIN CONFIG: {'default_suites_conf_path': '[PATH]/selftest/suite_test/default-suites.conf', 'defaults_conf_path': '[PATH]/selftest/suite_test/defaults.conf', 'resource_conf_path': '[PATH]/selftest/suite_test/resources.conf', - 'scenarios_dir': '[PATH]/selftest/suite_test/scenarios', + 'scenarios_dir': ['[PATH]/selftest/suite_test/scenarios'], 'state_dir': '[PATH]/selftest/suite_test/test_work/state_dir', - 'suites_dir': '[PATH]/selftest/suite_test', + 'suites_dir': ['[PATH]/selftest/suite_test/suitedirA', + '[PATH]/selftest/suite_test/suitedirB'], 'trial_dir': '[PATH]/selftest/suite_test/trial'} ---- -: ERR: RuntimeError: Suite not found: 'does_not_exist' in [PATH]/selftest/suite_test +--- -: ERR: RuntimeError: Suite not found: 'does_not_exist' in [[PATH]/selftest/suite_test/suitedirA, [PATH]/selftest/suite_test/suitedirB] - no suite.conf cnf empty_dir: DBG: reading suite.conf -cnf [PATH]/selftest/suite_test/empty_dir/suite.conf: ERR: FileNotFoundError: [Errno 2] No such file or directory: '[PATH]/selftest/suite_test/empty_dir/suite.conf' [empty_dir↪[PATH]/selftest/suite_test/empty_dir/suite.conf] +cnf [PATH]/selftest/suite_test/suitedirA/empty_dir/suite.conf: ERR: FileNotFoundError: [Errno 2] No such file or directory: '[PATH]/selftest/suite_test/suitedirA/empty_dir/suite.conf' [empty_dir↪[PATH]/selftest/suite_test/suitedirA/empty_dir/suite.conf] - valid suite dir cnf test_suite: DBG: reading suite.conf defaults: diff --git a/selftest/suite_test/empty_dir/.unrelated_file b/selftest/suite_test/suitedirA/empty_dir/.unrelated_file similarity index 100% rename from selftest/suite_test/empty_dir/.unrelated_file rename to selftest/suite_test/suitedirA/empty_dir/.unrelated_file diff --git a/selftest/suite_test/test_suite/hello_world.py b/selftest/suite_test/suitedirA/test_suite/hello_world.py similarity index 100% rename from selftest/suite_test/test_suite/hello_world.py rename to selftest/suite_test/suitedirA/test_suite/hello_world.py diff --git a/selftest/suite_test/test_suite/mo_mt_sms.py b/selftest/suite_test/suitedirA/test_suite/mo_mt_sms.py similarity index 100% rename from selftest/suite_test/test_suite/mo_mt_sms.py rename to selftest/suite_test/suitedirA/test_suite/mo_mt_sms.py diff --git a/selftest/suite_test/test_suite/mo_sms.py b/selftest/suite_test/suitedirA/test_suite/mo_sms.py similarity index 100% rename from selftest/suite_test/test_suite/mo_sms.py rename to selftest/suite_test/suitedirA/test_suite/mo_sms.py diff --git a/selftest/suite_test/test_suite/suite.conf b/selftest/suite_test/suitedirA/test_suite/suite.conf similarity index 100% rename from selftest/suite_test/test_suite/suite.conf rename to selftest/suite_test/suitedirA/test_suite/suite.conf diff --git a/selftest/suite_test/test_suite/test_error.py b/selftest/suite_test/suitedirA/test_suite/test_error.py similarity index 100% rename from selftest/suite_test/test_suite/test_error.py rename to selftest/suite_test/suitedirA/test_suite/test_error.py diff --git a/selftest/suite_test/test_suite/test_fail.py b/selftest/suite_test/suitedirA/test_suite/test_fail.py similarity index 100% rename from selftest/suite_test/test_suite/test_fail.py rename to selftest/suite_test/suitedirA/test_suite/test_fail.py diff --git a/selftest/suite_test/test_suite/test_fail_raise.py b/selftest/suite_test/suitedirA/test_suite/test_fail_raise.py similarity index 100% rename from selftest/suite_test/test_suite/test_fail_raise.py rename to selftest/suite_test/suitedirA/test_suite/test_fail_raise.py diff --git a/selftest/suite_test/test_suite/test_suite_params.py b/selftest/suite_test/suitedirA/test_suite/test_suite_params.py similarity index 100% rename from selftest/suite_test/test_suite/test_suite_params.py rename to selftest/suite_test/suitedirA/test_suite/test_suite_params.py diff --git a/selftest/suite_test/suitedirB/suiteB/suite.conf b/selftest/suite_test/suitedirB/suiteB/suite.conf new file mode 100644 index 00000000..3645cd9e --- /dev/null +++ b/selftest/suite_test/suitedirB/suiteB/suite.conf @@ -0,0 +1,4 @@ +resources: + +defaults: + timeout: 60s diff --git a/selftest/suite_test/suitedirB/suiteB/test_success.py b/selftest/suite_test/suitedirB/suiteB/test_success.py new file mode 100755 index 00000000..97bb9155 --- /dev/null +++ b/selftest/suite_test/suitedirB/suiteB/test_success.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 +from osmo_gsm_tester.testenv import * + +print('I am %r / %r' % (tenv.suite().name(), test.name())) + diff --git a/src/osmo_gsm_tester/core/config.py b/src/osmo_gsm_tester/core/config.py index 9380ccad..398e8ba9 100644 --- a/src/osmo_gsm_tester/core/config.py +++ b/src/osmo_gsm_tester/core/config.py @@ -71,8 +71,8 @@ CFG_DEFAULTS_CONF = 'defaults_conf_path' CFG_RESOURCES_CONF = 'resource_conf_path' MAIN_CONFIG_SCHEMA = { CFG_STATE_DIR: schema.STR, - CFG_SUITES_DIR: schema.STR, - CFG_SCENARIOS_DIR: schema.STR, + CFG_SUITES_DIR + '[]': schema.STR, + CFG_SCENARIOS_DIR + '[]': schema.STR, CFG_TRIAL_DIR: schema.STR, CFG_DEFAULT_SUITES_CONF: schema.STR, CFG_DEFAULTS_CONF: schema.STR, @@ -80,8 +80,8 @@ MAIN_CONFIG_SCHEMA = { } DF_CFG_STATE_DIR = '/var/tmp/osmo-gsm-tester/state/' -DF_CFG_SUITES_DIR = './suites' -DF_CFG_SCENARIOS_DIR = './scenarios' +DF_CFG_SUITES_DIR = ['./suites'] +DF_CFG_SCENARIOS_DIR = ['./scenarios'] DF_CFG_TRIAL_DIR = './trial' DF_CFG_DEFAULT_SUITES_CONF = './default-suites.conf' DF_CFG_DEFAULTS_CONF = './defaults.conf' @@ -122,11 +122,16 @@ def _get_main_config_path(): MAIN_CONFIG_PATH = _find_main_config_path() return MAIN_CONFIG_PATH -def main_config_path_to_abspath(path): +def main_config_path_to_abspath(val): 'Relative files in main config are relative towards the config file, not towards $CWD' - if not path.startswith(os.pathsep): - return os.path.realpath(os.path.join(os.path.dirname(_get_main_config_path()), path)) - return path + # If val is a list of paths, recurse to translate its paths. + if isinstance(val, list): + for i in range(len(val)): + val[i] = main_config_path_to_abspath(val[i]) + return val + if not val.startswith(os.pathsep): + return os.path.realpath(os.path.join(os.path.dirname(_get_main_config_path()), val)) + return val def _get_main_config(): global MAIN_CONFIG @@ -169,11 +174,11 @@ def read_config_file(cfg_name, validation_schema=None, if_missing_return=False): def get_state_dir(): return Dir(get_main_config_value(CFG_STATE_DIR)) -def get_suites_dir(): - return Dir(get_main_config_value(CFG_SUITES_DIR)) +def get_suites_dirs(): + return [Dir(d) for d in get_main_config_value(CFG_SUITES_DIR)] -def get_scenarios_dir(): - return Dir(get_main_config_value(CFG_SCENARIOS_DIR)) +def get_scenarios_dirs(): + return [Dir(d) for d in get_main_config_value(CFG_SCENARIOS_DIR)] DEFAULTS_CONF = None def get_defaults(for_kind): diff --git a/src/osmo_gsm_tester/core/scenario.py b/src/osmo_gsm_tester/core/scenario.py index efa045b1..83ce490f 100644 --- a/src/osmo_gsm_tester/core/scenario.py +++ b/src/osmo_gsm_tester/core/scenario.py @@ -88,25 +88,41 @@ class Scenario(log.Origin, dict): self.update(conf) def get_scenario(name, validation_schema=None): - scenarios_dir = config.get_scenarios_dir() + found = False + path = None + param_list = [] if not name.endswith('.conf'): name = name + '.conf' is_parametrized_file = '@' in name - param_list = [] - path = scenarios_dir.child(name) if not is_parametrized_file: - if not os.path.isfile(path): - raise RuntimeError('No such scenario file: %r' % path) + scenarios_dirs = config.get_scenarios_dirs() + for d in scenarios_dirs: + path = d.child(name) + if os.path.isfile(path): + found = True + break + if not found: + raise RuntimeError('No such scenario file %s in %r' % (name, scenarios_dirs)) sc = Scenario(name, path) else: # parametrized scenario: # Allow first matching complete matching names (eg: scenario@param1,param2.conf), # this allows setting specific content in different files for specific values. - if not os.path.isfile(path): + scenarios_dirs = config.get_scenarios_dirs() + for d in scenarios_dirs: + path = d.child(name) + if os.path.isfile(path): + found = True + break + if not found: # get "scenario@.conf" from "scenario@param1,param2.conf": - prefix_name = name[:name.index("@")+1] + '.conf' - path = scenarios_dir.child(prefix_name) - if not os.path.isfile(path): - raise RuntimeError('No such scenario file: %r (nor %s)' % (path, name)) + for d in scenarios_dirs: + prefix_name = name[:name.index("@")+1] + '.conf' + path = d.child(prefix_name) + if os.path.isfile(path): + found = True + break + if not found: + raise RuntimeError('No such scenario file %r (nor %s) in %r' % (name, prefix_name, scenarios_dirs)) # At this point, we have existing file path. Let's now scrap the parameter(s): # get param1,param2 str from scenario@param1,param2.conf param_list_str = name.split('@', 1)[1][:-len('.conf')] diff --git a/src/osmo_gsm_tester/core/suite.py b/src/osmo_gsm_tester/core/suite.py index a6eaca27..c55c5e9a 100644 --- a/src/osmo_gsm_tester/core/suite.py +++ b/src/osmo_gsm_tester/core/suite.py @@ -232,12 +232,16 @@ def load(suite_name): if suite is not None: return suite - suites_dir = config.get_suites_dir() - suite_dir = suites_dir.child(suite_name) - if not suites_dir.exists(suite_name): - raise RuntimeError('Suite not found: %r in %r' % (suite_name, suites_dir)) - if not suites_dir.isdir(suite_name): - raise RuntimeError('Suite name found, but not a directory: %r' % (suite_dir)) + suites_dirs = config.get_suites_dirs() + suite_dir = None + found = False + for d in suites_dirs: + suite_dir = d.child(suite_name) + if d.exists(suite_name) and d.isdir(suite_name): + found = True + break + if not found: + raise RuntimeError('Suite not found: %r in %r' % (suite_name, suites_dirs)) suite_def = SuiteDefinition(suite_dir) loaded_suite_definitions[suite_name] = suite_def diff --git a/sysmocom/main.conf b/sysmocom/main.conf index b8105196..699c5183 100644 --- a/sysmocom/main.conf +++ b/sysmocom/main.conf @@ -1,6 +1,6 @@ state_dir: '/var/tmp/osmo-gsm-tester/state' -suites_dir: './suites' -scenarios_dir: './scenarios' +suites_dir: ['./suites'] +scenarios_dir: ['./scenarios'] default_suites_conf_path: './default-suites.conf' defaults_conf_path: './defaults.conf' resource_conf_path: './resources.conf' diff --git a/sysmocom/ttcn3/main.conf b/sysmocom/ttcn3/main.conf index 9f38b4e9..e4a12d10 100644 --- a/sysmocom/ttcn3/main.conf +++ b/sysmocom/ttcn3/main.conf @@ -1,6 +1,6 @@ state_dir: '/var/tmp/osmo-gsm-tester/state' -suites_dir: './suites' -scenarios_dir: './scenarios' +suites_dir: ['./suites'] +scenarios_dir: ['./scenarios'] default_suites_conf_path: './default-suites.conf' defaults_conf_path: '../defaults.conf' resource_conf_path: './resources.conf'