<p>pespin <strong>submitted</strong> this change.</p><p><a href="https://gerrit.osmocom.org/c/osmo-gsm-tester/+/18446">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  Jenkins Builder: Verified
  srs_andre: Looks good to me, approved

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">config: suites_dir and scenarios_dir are now a list of paths<br><br>This allows inheriting suites or scenarios from eg. sysmocom/ dir, while<br>still allowing to apply new suites and scenarios on top.<br><br>Change-Id: Icecdae32d400a6b6da2ebf167c1c795f7a74ae96<br>---<br>M doc/examples/2g_osmocom/main.conf<br>M doc/examples/4g_srsLTE/main.conf<br>M doc/manuals/chapters/config.adoc<br>M selftest/resource_test/conf/paths.conf<br>M selftest/resource_test/resource_test.ok<br>M selftest/scenario_test/paths.conf<br>M selftest/scenario_test/scenario_test.ok<br>M selftest/suite_test/paths.conf<br>M selftest/suite_test/suite_test.ok<br>R selftest/suite_test/suitedirA/empty_dir/.unrelated_file<br>R selftest/suite_test/suitedirA/test_suite/hello_world.py<br>R selftest/suite_test/suitedirA/test_suite/mo_mt_sms.py<br>R selftest/suite_test/suitedirA/test_suite/mo_sms.py<br>R selftest/suite_test/suitedirA/test_suite/suite.conf<br>R selftest/suite_test/suitedirA/test_suite/test_error.py<br>R selftest/suite_test/suitedirA/test_suite/test_fail.py<br>R selftest/suite_test/suitedirA/test_suite/test_fail_raise.py<br>R selftest/suite_test/suitedirA/test_suite/test_suite_params.py<br>A selftest/suite_test/suitedirB/suiteB/suite.conf<br>A selftest/suite_test/suitedirB/suiteB/test_success.py<br>M src/osmo_gsm_tester/core/config.py<br>M src/osmo_gsm_tester/core/scenario.py<br>M src/osmo_gsm_tester/core/suite.py<br>M sysmocom/main.conf<br>M sysmocom/ttcn3/main.conf<br>25 files changed, 96 insertions(+), 55 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/doc/examples/2g_osmocom/main.conf b/doc/examples/2g_osmocom/main.conf</span><br><span>index b810519..699c518 100644</span><br><span>--- a/doc/examples/2g_osmocom/main.conf</span><br><span>+++ b/doc/examples/2g_osmocom/main.conf</span><br><span>@@ -1,6 +1,6 @@</span><br><span> state_dir: '/var/tmp/osmo-gsm-tester/state'</span><br><span style="color: hsl(0, 100%, 40%);">-suites_dir: './suites'</span><br><span style="color: hsl(0, 100%, 40%);">-scenarios_dir: './scenarios'</span><br><span style="color: hsl(120, 100%, 40%);">+suites_dir: ['./suites']</span><br><span style="color: hsl(120, 100%, 40%);">+scenarios_dir: ['./scenarios']</span><br><span> default_suites_conf_path: './default-suites.conf'</span><br><span> defaults_conf_path: './defaults.conf'</span><br><span> resource_conf_path: './resources.conf'</span><br><span>diff --git a/doc/examples/4g_srsLTE/main.conf b/doc/examples/4g_srsLTE/main.conf</span><br><span>index b810519..699c518 100644</span><br><span>--- a/doc/examples/4g_srsLTE/main.conf</span><br><span>+++ b/doc/examples/4g_srsLTE/main.conf</span><br><span>@@ -1,6 +1,6 @@</span><br><span> state_dir: '/var/tmp/osmo-gsm-tester/state'</span><br><span style="color: hsl(0, 100%, 40%);">-suites_dir: './suites'</span><br><span style="color: hsl(0, 100%, 40%);">-scenarios_dir: './scenarios'</span><br><span style="color: hsl(120, 100%, 40%);">+suites_dir: ['./suites']</span><br><span style="color: hsl(120, 100%, 40%);">+scenarios_dir: ['./scenarios']</span><br><span> default_suites_conf_path: './default-suites.conf'</span><br><span> defaults_conf_path: './defaults.conf'</span><br><span> resource_conf_path: './resources.conf'</span><br><span>diff --git a/doc/manuals/chapters/config.adoc b/doc/manuals/chapters/config.adoc</span><br><span>index 17cf825..18ff55d 100644</span><br><span>--- a/doc/manuals/chapters/config.adoc</span><br><span>+++ b/doc/manuals/chapters/config.adoc</span><br><span>@@ -26,13 +26,19 @@</span><br><span> {app-name} expects to find the following configuration settings in 'main.conf':</span><br><span> </span><br><span> - 'state_dir': Path to <<state_dir,state_dir>> directory</span><br><span style="color: hsl(0, 100%, 40%);">-- 'suites_dir': Path to <<suites_dir,suites_dir>> directory</span><br><span> - 'trial_dir': Path to <<trials,trial>> directory to test against (overridden by cmdline argument)</span><br><span style="color: hsl(0, 100%, 40%);">-- 'scenarios_dir': Path to <<scenarios_dir,scenarios_dir>> directory (optional)</span><br><span style="color: hsl(120, 100%, 40%);">+- 'suites_dir': List of paths to <<suites_dir,suites_dir>> directories.</span><br><span style="color: hsl(120, 100%, 40%);">+- 'scenarios_dir': List of paths to <<scenarios_dir,scenarios_dir>> directories (optional)</span><br><span> - 'default_suites_conf_path': Path to <<default_suites_conf,default-suites.conf>> file (optional)</span><br><span> - 'defaults_conf_path': Path to <<defaults_conf,defaults.conf>> file (optional)</span><br><span> - 'resource_conf_path': Path to <<resource_conf,resources.conf>> file (optional)</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+Configuration settings holding a list of paths, such as 'suites_dir' or</span><br><span style="color: hsl(120, 100%, 40%);">+'scenarios_dir', are used to look up for paths in regular list of order, meaning</span><br><span style="color: hsl(120, 100%, 40%);">+first paths in list take preference over last ones. As a result, if a suite</span><br><span style="color: hsl(120, 100%, 40%);">+named 'A' is found in several paths, the one on the first path in the list will</span><br><span style="color: hsl(120, 100%, 40%);">+be used.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> These are described in detail in the following sections. If no value is provided</span><br><span> for a given setting, sane default paths are used: For 'state_dir',</span><br><span> '/var/tmp/osmo-gsm-tester/state/' is used. All other files and directories are</span><br><span>@@ -45,8 +51,8 @@</span><br><span> .Sample main.conf file:</span><br><span> ----</span><br><span> state_dir: '/var/tmp/osmo-gsm-tester/state'</span><br><span style="color: hsl(0, 100%, 40%);">-suites_dir: '/usr/local/src/osmo-gsm-tester/suites'</span><br><span style="color: hsl(0, 100%, 40%);">-scenarios_dir: './scenarios'</span><br><span style="color: hsl(120, 100%, 40%);">+suites_dir: [ '/usr/local/src/osmo-gsm-tester/suites' ]</span><br><span style="color: hsl(120, 100%, 40%);">+scenarios_dir: [ './scenarios' ]</span><br><span> trial_dir: './trial'</span><br><span> default_suites_conf_path: './default-suites.conf'</span><br><span> defaults_conf_path: './defaults.conf'</span><br><span>diff --git a/selftest/resource_test/conf/paths.conf b/selftest/resource_test/conf/paths.conf</span><br><span>index 0b2d035..3280a12 100644</span><br><span>--- a/selftest/resource_test/conf/paths.conf</span><br><span>+++ b/selftest/resource_test/conf/paths.conf</span><br><span>@@ -1,2 +1,2 @@</span><br><span> state_dir: ./test_work/state_dir</span><br><span style="color: hsl(0, 100%, 40%);">-suites_dir: ./suite_test</span><br><span style="color: hsl(120, 100%, 40%);">+suites_dir: ['./suite_test']</span><br><span>diff --git a/selftest/resource_test/resource_test.ok b/selftest/resource_test/resource_test.ok</span><br><span>index 0ad760e..91acaaf 100644</span><br><span>--- a/selftest/resource_test/resource_test.ok</span><br><span>+++ b/selftest/resource_test/resource_test.ok</span><br><span>@@ -14,9 +14,9 @@</span><br><span> {'default_suites_conf_path': '[PATH]/selftest/resource_test/conf/default-suites.conf',</span><br><span>  'defaults_conf_path': '[PATH]/selftest/resource_test/conf/defaults.conf',</span><br><span>  'resource_conf_path': '[PATH]/selftest/resource_test/conf/resources.conf',</span><br><span style="color: hsl(0, 100%, 40%);">- 'scenarios_dir': '[PATH]/selftest/resource_test/conf/scenarios',</span><br><span style="color: hsl(120, 100%, 40%);">+ 'scenarios_dir': ['[PATH]/selftest/resource_test/conf/scenarios'],</span><br><span>  'state_dir': '[PATH]/selftest/resource_test/conf/test_work/state_dir',</span><br><span style="color: hsl(0, 100%, 40%);">- 'suites_dir': '[PATH]/selftest/resource_test/conf/suite_test',</span><br><span style="color: hsl(120, 100%, 40%);">+ 'suites_dir': ['[PATH]/selftest/resource_test/conf/suite_test'],</span><br><span>  'trial_dir': '[PATH]/selftest/resource_test/conf/trial'}</span><br><span> *** all resources:</span><br><span> {'arfcn': [{'_hash': 'e620569450f8259b3f0212ec19c285dd07df063c',</span><br><span>diff --git a/selftest/scenario_test/paths.conf b/selftest/scenario_test/paths.conf</span><br><span>index c7df5ac..11e59bf 100644</span><br><span>--- a/selftest/scenario_test/paths.conf</span><br><span>+++ b/selftest/scenario_test/paths.conf</span><br><span>@@ -1,3 +1,3 @@</span><br><span> state_dir: ./test_work/state_dir</span><br><span style="color: hsl(0, 100%, 40%);">-suites_dir: .</span><br><span style="color: hsl(0, 100%, 40%);">-scenarios_dir: .</span><br><span style="color: hsl(120, 100%, 40%);">+suites_dir: ['.']</span><br><span style="color: hsl(120, 100%, 40%);">+scenarios_dir: ['.']</span><br><span>diff --git a/selftest/scenario_test/scenario_test.ok b/selftest/scenario_test/scenario_test.ok</span><br><span>index e845f4c..06db4f4 100644</span><br><span>--- a/selftest/scenario_test/scenario_test.ok</span><br><span>+++ b/selftest/scenario_test/scenario_test.ok</span><br><span>@@ -3,9 +3,9 @@</span><br><span> {'default_suites_conf_path': '[PATH]/selftest/scenario_test/default-suites.conf',</span><br><span>  'defaults_conf_path': '[PATH]/selftest/scenario_test/defaults.conf',</span><br><span>  'resource_conf_path': '[PATH]/selftest/scenario_test/resources.conf',</span><br><span style="color: hsl(0, 100%, 40%);">- 'scenarios_dir': '[PATH]/selftest/scenario_test',</span><br><span style="color: hsl(120, 100%, 40%);">+ 'scenarios_dir': ['[PATH]/selftest/scenario_test'],</span><br><span>  'state_dir': '[PATH]/selftest/scenario_test/test_work/state_dir',</span><br><span style="color: hsl(0, 100%, 40%);">- 'suites_dir': '[PATH]/selftest/scenario_test',</span><br><span style="color: hsl(120, 100%, 40%);">+ 'suites_dir': ['[PATH]/selftest/scenario_test'],</span><br><span>  'trial_dir': '[PATH]/selftest/scenario_test/trial'}</span><br><span> scenario_case_01.conf</span><br><span> {'anotherlist': ['4', '0'],</span><br><span>@@ -19,11 +19,11 @@</span><br><span>  'somelist': [{'somelistitem': 'firststring'},</span><br><span>               {'somelistitem': 'secondstring'},</span><br><span>               {'somelistitem': 'thirdstring'}]}</span><br><span style="color: hsl(0, 100%, 40%);">-OK: expected RuntimeError: No such scenario file: '[PATH]/selftest/scenario_test/scenario_case_01@.conf' (nor scenario_case_01@.conf)</span><br><span style="color: hsl(120, 100%, 40%);">+OK: expected RuntimeError: No such scenario file 'scenario_case_01@.conf' (nor scenario_case_01@.conf) in [[PATH]/selftest/scenario_test]</span><br><span> OK: expected ValueError</span><br><span> OK: expected ValueError</span><br><span style="color: hsl(0, 100%, 40%);">-OK: expected RuntimeError: No such scenario file: '[PATH]/selftest/scenario_test/scenario_case_03.conf'</span><br><span style="color: hsl(0, 100%, 40%);">-OK: expected RuntimeError: No such scenario file: '[PATH]/selftest/scenario_test/scenario_case_03.conf'</span><br><span style="color: hsl(120, 100%, 40%);">+OK: expected RuntimeError: No such scenario file scenario_case_03.conf in [[PATH]/selftest/scenario_test]</span><br><span style="color: hsl(120, 100%, 40%);">+OK: expected RuntimeError: No such scenario file scenario_case_03.conf in [[PATH]/selftest/scenario_test]</span><br><span> tst scenario_case_03@heyho,1,yes.conf: DBG: {param_dict={param1='heyho', param2='1', param3='yes'}}</span><br><span> scenario_case_03@heyho,1,yes.conf</span><br><span> {'anotherlist': ['1', '0'],</span><br><span>diff --git a/selftest/suite_test/paths.conf b/selftest/suite_test/paths.conf</span><br><span>index 2b0a274..a2f9124 100644</span><br><span>--- a/selftest/suite_test/paths.conf</span><br><span>+++ b/selftest/suite_test/paths.conf</span><br><span>@@ -1,2 +1,2 @@</span><br><span> state_dir: ./test_work/state_dir</span><br><span style="color: hsl(0, 100%, 40%);">-suites_dir: .</span><br><span style="color: hsl(120, 100%, 40%);">+suites_dir: ['suitedirA', 'suitedirB']</span><br><span>diff --git a/selftest/suite_test/suite_test.ok b/selftest/suite_test/suite_test.ok</span><br><span>index d55317b..fee07d0 100644</span><br><span>--- a/selftest/suite_test/suite_test.ok</span><br><span>+++ b/selftest/suite_test/suite_test.ok</span><br><span>@@ -4,14 +4,15 @@</span><br><span> {'default_suites_conf_path': '[PATH]/selftest/suite_test/default-suites.conf',</span><br><span>  'defaults_conf_path': '[PATH]/selftest/suite_test/defaults.conf',</span><br><span>  'resource_conf_path': '[PATH]/selftest/suite_test/resources.conf',</span><br><span style="color: hsl(0, 100%, 40%);">- 'scenarios_dir': '[PATH]/selftest/suite_test/scenarios',</span><br><span style="color: hsl(120, 100%, 40%);">+ 'scenarios_dir': ['[PATH]/selftest/suite_test/scenarios'],</span><br><span>  'state_dir': '[PATH]/selftest/suite_test/test_work/state_dir',</span><br><span style="color: hsl(0, 100%, 40%);">- 'suites_dir': '[PATH]/selftest/suite_test',</span><br><span style="color: hsl(120, 100%, 40%);">+ 'suites_dir': ['[PATH]/selftest/suite_test/suitedirA',</span><br><span style="color: hsl(120, 100%, 40%);">+                '[PATH]/selftest/suite_test/suitedirB'],</span><br><span>  'trial_dir': '[PATH]/selftest/suite_test/trial'}</span><br><span>---- -: ERR: RuntimeError: Suite not found: 'does_not_exist' in [PATH]/selftest/suite_test</span><br><span style="color: hsl(120, 100%, 40%);">+--- -: ERR: RuntimeError: Suite not found: 'does_not_exist' in [[PATH]/selftest/suite_test/suitedirA, [PATH]/selftest/suite_test/suitedirB]</span><br><span> - no suite.conf</span><br><span> cnf empty_dir: DBG: reading suite.conf</span><br><span style="color: hsl(0, 100%, 40%);">-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]</span><br><span style="color: hsl(120, 100%, 40%);">+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]</span><br><span> - valid suite dir</span><br><span> cnf test_suite: DBG: reading suite.conf</span><br><span> defaults:</span><br><span>diff --git a/selftest/suite_test/empty_dir/.unrelated_file b/selftest/suite_test/suitedirA/empty_dir/.unrelated_file</span><br><span>similarity index 100%</span><br><span>rename from selftest/suite_test/empty_dir/.unrelated_file</span><br><span>rename to selftest/suite_test/suitedirA/empty_dir/.unrelated_file</span><br><span>diff --git a/selftest/suite_test/test_suite/hello_world.py b/selftest/suite_test/suitedirA/test_suite/hello_world.py</span><br><span>similarity index 100%</span><br><span>rename from selftest/suite_test/test_suite/hello_world.py</span><br><span>rename to selftest/suite_test/suitedirA/test_suite/hello_world.py</span><br><span>diff --git a/selftest/suite_test/test_suite/mo_mt_sms.py b/selftest/suite_test/suitedirA/test_suite/mo_mt_sms.py</span><br><span>similarity index 100%</span><br><span>rename from selftest/suite_test/test_suite/mo_mt_sms.py</span><br><span>rename to selftest/suite_test/suitedirA/test_suite/mo_mt_sms.py</span><br><span>diff --git a/selftest/suite_test/test_suite/mo_sms.py b/selftest/suite_test/suitedirA/test_suite/mo_sms.py</span><br><span>similarity index 100%</span><br><span>rename from selftest/suite_test/test_suite/mo_sms.py</span><br><span>rename to selftest/suite_test/suitedirA/test_suite/mo_sms.py</span><br><span>diff --git a/selftest/suite_test/test_suite/suite.conf b/selftest/suite_test/suitedirA/test_suite/suite.conf</span><br><span>similarity index 100%</span><br><span>rename from selftest/suite_test/test_suite/suite.conf</span><br><span>rename to selftest/suite_test/suitedirA/test_suite/suite.conf</span><br><span>diff --git a/selftest/suite_test/test_suite/test_error.py b/selftest/suite_test/suitedirA/test_suite/test_error.py</span><br><span>similarity index 100%</span><br><span>rename from selftest/suite_test/test_suite/test_error.py</span><br><span>rename to selftest/suite_test/suitedirA/test_suite/test_error.py</span><br><span>diff --git a/selftest/suite_test/test_suite/test_fail.py b/selftest/suite_test/suitedirA/test_suite/test_fail.py</span><br><span>similarity index 100%</span><br><span>rename from selftest/suite_test/test_suite/test_fail.py</span><br><span>rename to selftest/suite_test/suitedirA/test_suite/test_fail.py</span><br><span>diff --git a/selftest/suite_test/test_suite/test_fail_raise.py b/selftest/suite_test/suitedirA/test_suite/test_fail_raise.py</span><br><span>similarity index 100%</span><br><span>rename from selftest/suite_test/test_suite/test_fail_raise.py</span><br><span>rename to selftest/suite_test/suitedirA/test_suite/test_fail_raise.py</span><br><span>diff --git a/selftest/suite_test/test_suite/test_suite_params.py b/selftest/suite_test/suitedirA/test_suite/test_suite_params.py</span><br><span>similarity index 100%</span><br><span>rename from selftest/suite_test/test_suite/test_suite_params.py</span><br><span>rename to selftest/suite_test/suitedirA/test_suite/test_suite_params.py</span><br><span>diff --git a/selftest/suite_test/suitedirB/suiteB/suite.conf b/selftest/suite_test/suitedirB/suiteB/suite.conf</span><br><span>new file mode 100644</span><br><span>index 0000000..3645cd9</span><br><span>--- /dev/null</span><br><span>+++ b/selftest/suite_test/suitedirB/suiteB/suite.conf</span><br><span>@@ -0,0 +1,4 @@</span><br><span style="color: hsl(120, 100%, 40%);">+resources:</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+defaults:</span><br><span style="color: hsl(120, 100%, 40%);">+  timeout: 60s</span><br><span>diff --git a/selftest/suite_test/suitedirB/suiteB/test_success.py b/selftest/suite_test/suitedirB/suiteB/test_success.py</span><br><span>new file mode 100755</span><br><span>index 0000000..97bb915</span><br><span>--- /dev/null</span><br><span>+++ b/selftest/suite_test/suitedirB/suiteB/test_success.py</span><br><span>@@ -0,0 +1,5 @@</span><br><span style="color: hsl(120, 100%, 40%);">+#!/usr/bin/env python3</span><br><span style="color: hsl(120, 100%, 40%);">+from osmo_gsm_tester.testenv import *</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+print('I am %r / %r' % (tenv.suite().name(), test.name()))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>diff --git a/src/osmo_gsm_tester/core/config.py b/src/osmo_gsm_tester/core/config.py</span><br><span>index 9380cca..398e8ba 100644</span><br><span>--- a/src/osmo_gsm_tester/core/config.py</span><br><span>+++ b/src/osmo_gsm_tester/core/config.py</span><br><span>@@ -71,8 +71,8 @@</span><br><span> CFG_RESOURCES_CONF = 'resource_conf_path'</span><br><span> MAIN_CONFIG_SCHEMA = {</span><br><span>         CFG_STATE_DIR: schema.STR,</span><br><span style="color: hsl(0, 100%, 40%);">-        CFG_SUITES_DIR: schema.STR,</span><br><span style="color: hsl(0, 100%, 40%);">-        CFG_SCENARIOS_DIR: schema.STR,</span><br><span style="color: hsl(120, 100%, 40%);">+        CFG_SUITES_DIR + '[]': schema.STR,</span><br><span style="color: hsl(120, 100%, 40%);">+        CFG_SCENARIOS_DIR + '[]': schema.STR,</span><br><span>         CFG_TRIAL_DIR: schema.STR,</span><br><span>         CFG_DEFAULT_SUITES_CONF: schema.STR,</span><br><span>         CFG_DEFAULTS_CONF: schema.STR,</span><br><span>@@ -80,8 +80,8 @@</span><br><span>     }</span><br><span> </span><br><span> DF_CFG_STATE_DIR = '/var/tmp/osmo-gsm-tester/state/'</span><br><span style="color: hsl(0, 100%, 40%);">-DF_CFG_SUITES_DIR = './suites'</span><br><span style="color: hsl(0, 100%, 40%);">-DF_CFG_SCENARIOS_DIR = './scenarios'</span><br><span style="color: hsl(120, 100%, 40%);">+DF_CFG_SUITES_DIR = ['./suites']</span><br><span style="color: hsl(120, 100%, 40%);">+DF_CFG_SCENARIOS_DIR = ['./scenarios']</span><br><span> DF_CFG_TRIAL_DIR = './trial'</span><br><span> DF_CFG_DEFAULT_SUITES_CONF = './default-suites.conf'</span><br><span> DF_CFG_DEFAULTS_CONF = './defaults.conf'</span><br><span>@@ -122,11 +122,16 @@</span><br><span>         MAIN_CONFIG_PATH = _find_main_config_path()</span><br><span>     return MAIN_CONFIG_PATH</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-def main_config_path_to_abspath(path):</span><br><span style="color: hsl(120, 100%, 40%);">+def main_config_path_to_abspath(val):</span><br><span>     'Relative files in main config are relative towards the config file, not towards $CWD'</span><br><span style="color: hsl(0, 100%, 40%);">-    if not path.startswith(os.pathsep):</span><br><span style="color: hsl(0, 100%, 40%);">-        return os.path.realpath(os.path.join(os.path.dirname(_get_main_config_path()), path))</span><br><span style="color: hsl(0, 100%, 40%);">-    return path</span><br><span style="color: hsl(120, 100%, 40%);">+    # If val is a list of paths, recurse to translate its paths.</span><br><span style="color: hsl(120, 100%, 40%);">+    if isinstance(val, list):</span><br><span style="color: hsl(120, 100%, 40%);">+        for i in range(len(val)):</span><br><span style="color: hsl(120, 100%, 40%);">+            val[i] = main_config_path_to_abspath(val[i])</span><br><span style="color: hsl(120, 100%, 40%);">+        return val</span><br><span style="color: hsl(120, 100%, 40%);">+    if not val.startswith(os.pathsep):</span><br><span style="color: hsl(120, 100%, 40%);">+        return os.path.realpath(os.path.join(os.path.dirname(_get_main_config_path()), val))</span><br><span style="color: hsl(120, 100%, 40%);">+    return val</span><br><span> </span><br><span> def _get_main_config():</span><br><span>     global MAIN_CONFIG</span><br><span>@@ -169,11 +174,11 @@</span><br><span> def get_state_dir():</span><br><span>     return Dir(get_main_config_value(CFG_STATE_DIR))</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-def get_suites_dir():</span><br><span style="color: hsl(0, 100%, 40%);">-    return Dir(get_main_config_value(CFG_SUITES_DIR))</span><br><span style="color: hsl(120, 100%, 40%);">+def get_suites_dirs():</span><br><span style="color: hsl(120, 100%, 40%);">+    return [Dir(d) for d in get_main_config_value(CFG_SUITES_DIR)]</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-def get_scenarios_dir():</span><br><span style="color: hsl(0, 100%, 40%);">-    return Dir(get_main_config_value(CFG_SCENARIOS_DIR))</span><br><span style="color: hsl(120, 100%, 40%);">+def get_scenarios_dirs():</span><br><span style="color: hsl(120, 100%, 40%);">+    return [Dir(d) for d in get_main_config_value(CFG_SCENARIOS_DIR)]</span><br><span> </span><br><span> DEFAULTS_CONF = None</span><br><span> def get_defaults(for_kind):</span><br><span>diff --git a/src/osmo_gsm_tester/core/scenario.py b/src/osmo_gsm_tester/core/scenario.py</span><br><span>index efa045b..83ce490 100644</span><br><span>--- a/src/osmo_gsm_tester/core/scenario.py</span><br><span>+++ b/src/osmo_gsm_tester/core/scenario.py</span><br><span>@@ -88,25 +88,41 @@</span><br><span>         self.update(conf)</span><br><span> </span><br><span> def get_scenario(name, validation_schema=None):</span><br><span style="color: hsl(0, 100%, 40%);">-    scenarios_dir = config.get_scenarios_dir()</span><br><span style="color: hsl(120, 100%, 40%);">+    found = False</span><br><span style="color: hsl(120, 100%, 40%);">+    path = None</span><br><span style="color: hsl(120, 100%, 40%);">+    param_list = []</span><br><span>     if not name.endswith('.conf'):</span><br><span>         name = name + '.conf'</span><br><span>     is_parametrized_file = '@' in name</span><br><span style="color: hsl(0, 100%, 40%);">-    param_list = []</span><br><span style="color: hsl(0, 100%, 40%);">-    path = scenarios_dir.child(name)</span><br><span>     if not is_parametrized_file:</span><br><span style="color: hsl(0, 100%, 40%);">-        if not os.path.isfile(path):</span><br><span style="color: hsl(0, 100%, 40%);">-            raise RuntimeError('No such scenario file: %r' % path)</span><br><span style="color: hsl(120, 100%, 40%);">+        scenarios_dirs = config.get_scenarios_dirs()</span><br><span style="color: hsl(120, 100%, 40%);">+        for d in scenarios_dirs:</span><br><span style="color: hsl(120, 100%, 40%);">+            path = d.child(name)</span><br><span style="color: hsl(120, 100%, 40%);">+            if  os.path.isfile(path):</span><br><span style="color: hsl(120, 100%, 40%);">+                found = True</span><br><span style="color: hsl(120, 100%, 40%);">+                break</span><br><span style="color: hsl(120, 100%, 40%);">+        if not found:</span><br><span style="color: hsl(120, 100%, 40%);">+            raise RuntimeError('No such scenario file %s in %r' % (name, scenarios_dirs))</span><br><span>         sc = Scenario(name, path)</span><br><span>     else: # parametrized scenario:</span><br><span>         # Allow first matching complete matching names (eg: scenario@param1,param2.conf),</span><br><span>         # this allows setting specific content in different files for specific values.</span><br><span style="color: hsl(0, 100%, 40%);">-        if not os.path.isfile(path):</span><br><span style="color: hsl(120, 100%, 40%);">+        scenarios_dirs = config.get_scenarios_dirs()</span><br><span style="color: hsl(120, 100%, 40%);">+        for d in scenarios_dirs:</span><br><span style="color: hsl(120, 100%, 40%);">+            path = d.child(name)</span><br><span style="color: hsl(120, 100%, 40%);">+            if os.path.isfile(path):</span><br><span style="color: hsl(120, 100%, 40%);">+                found = True</span><br><span style="color: hsl(120, 100%, 40%);">+                break</span><br><span style="color: hsl(120, 100%, 40%);">+        if not found:</span><br><span>             # get "scenario@.conf" from "scenario@param1,param2.conf":</span><br><span style="color: hsl(0, 100%, 40%);">-            prefix_name = name[:name.index("@")+1] + '.conf'</span><br><span style="color: hsl(0, 100%, 40%);">-            path = scenarios_dir.child(prefix_name)</span><br><span style="color: hsl(0, 100%, 40%);">-            if not os.path.isfile(path):</span><br><span style="color: hsl(0, 100%, 40%);">-                raise RuntimeError('No such scenario file: %r (nor %s)' % (path, name))</span><br><span style="color: hsl(120, 100%, 40%);">+            for d in scenarios_dirs:</span><br><span style="color: hsl(120, 100%, 40%);">+                prefix_name = name[:name.index("@")+1] + '.conf'</span><br><span style="color: hsl(120, 100%, 40%);">+                path = d.child(prefix_name)</span><br><span style="color: hsl(120, 100%, 40%);">+                if os.path.isfile(path):</span><br><span style="color: hsl(120, 100%, 40%);">+                    found = True</span><br><span style="color: hsl(120, 100%, 40%);">+                    break</span><br><span style="color: hsl(120, 100%, 40%);">+        if not found:</span><br><span style="color: hsl(120, 100%, 40%);">+            raise RuntimeError('No such scenario file %r (nor %s) in %r' % (name, prefix_name, scenarios_dirs))</span><br><span>         # At this point, we have existing file path. Let's now scrap the parameter(s):</span><br><span>         # get param1,param2 str from scenario@param1,param2.conf</span><br><span>         param_list_str = name.split('@', 1)[1][:-len('.conf')]</span><br><span>diff --git a/src/osmo_gsm_tester/core/suite.py b/src/osmo_gsm_tester/core/suite.py</span><br><span>index a6eaca2..c55c5e9 100644</span><br><span>--- a/src/osmo_gsm_tester/core/suite.py</span><br><span>+++ b/src/osmo_gsm_tester/core/suite.py</span><br><span>@@ -232,12 +232,16 @@</span><br><span>     if suite is not None:</span><br><span>         return suite</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    suites_dir = config.get_suites_dir()</span><br><span style="color: hsl(0, 100%, 40%);">-    suite_dir = suites_dir.child(suite_name)</span><br><span style="color: hsl(0, 100%, 40%);">-    if not suites_dir.exists(suite_name):</span><br><span style="color: hsl(0, 100%, 40%);">-        raise RuntimeError('Suite not found: %r in %r' % (suite_name, suites_dir))</span><br><span style="color: hsl(0, 100%, 40%);">-    if not suites_dir.isdir(suite_name):</span><br><span style="color: hsl(0, 100%, 40%);">-        raise RuntimeError('Suite name found, but not a directory: %r' % (suite_dir))</span><br><span style="color: hsl(120, 100%, 40%);">+    suites_dirs = config.get_suites_dirs()</span><br><span style="color: hsl(120, 100%, 40%);">+    suite_dir = None</span><br><span style="color: hsl(120, 100%, 40%);">+    found = False</span><br><span style="color: hsl(120, 100%, 40%);">+    for d in suites_dirs:</span><br><span style="color: hsl(120, 100%, 40%);">+        suite_dir = d.child(suite_name)</span><br><span style="color: hsl(120, 100%, 40%);">+        if d.exists(suite_name) and d.isdir(suite_name):</span><br><span style="color: hsl(120, 100%, 40%);">+            found = True</span><br><span style="color: hsl(120, 100%, 40%);">+            break</span><br><span style="color: hsl(120, 100%, 40%);">+    if not found:</span><br><span style="color: hsl(120, 100%, 40%);">+        raise RuntimeError('Suite not found: %r in %r' % (suite_name, suites_dirs))</span><br><span> </span><br><span>     suite_def = SuiteDefinition(suite_dir)</span><br><span>     loaded_suite_definitions[suite_name] = suite_def</span><br><span>diff --git a/sysmocom/main.conf b/sysmocom/main.conf</span><br><span>index b810519..699c518 100644</span><br><span>--- a/sysmocom/main.conf</span><br><span>+++ b/sysmocom/main.conf</span><br><span>@@ -1,6 +1,6 @@</span><br><span> state_dir: '/var/tmp/osmo-gsm-tester/state'</span><br><span style="color: hsl(0, 100%, 40%);">-suites_dir: './suites'</span><br><span style="color: hsl(0, 100%, 40%);">-scenarios_dir: './scenarios'</span><br><span style="color: hsl(120, 100%, 40%);">+suites_dir: ['./suites']</span><br><span style="color: hsl(120, 100%, 40%);">+scenarios_dir: ['./scenarios']</span><br><span> default_suites_conf_path: './default-suites.conf'</span><br><span> defaults_conf_path: './defaults.conf'</span><br><span> resource_conf_path: './resources.conf'</span><br><span>diff --git a/sysmocom/ttcn3/main.conf b/sysmocom/ttcn3/main.conf</span><br><span>index 9f38b4e..e4a12d1 100644</span><br><span>--- a/sysmocom/ttcn3/main.conf</span><br><span>+++ b/sysmocom/ttcn3/main.conf</span><br><span>@@ -1,6 +1,6 @@</span><br><span> state_dir: '/var/tmp/osmo-gsm-tester/state'</span><br><span style="color: hsl(0, 100%, 40%);">-suites_dir: './suites'</span><br><span style="color: hsl(0, 100%, 40%);">-scenarios_dir: './scenarios'</span><br><span style="color: hsl(120, 100%, 40%);">+suites_dir: ['./suites']</span><br><span style="color: hsl(120, 100%, 40%);">+scenarios_dir: ['./scenarios']</span><br><span> default_suites_conf_path: './default-suites.conf'</span><br><span> defaults_conf_path: '../defaults.conf'</span><br><span> resource_conf_path: './resources.conf'</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/osmo-gsm-tester/+/18446">change 18446</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/c/osmo-gsm-tester/+/18446"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: osmo-gsm-tester </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: Icecdae32d400a6b6da2ebf167c1c795f7a74ae96 </div>
<div style="display:none"> Gerrit-Change-Number: 18446 </div>
<div style="display:none"> Gerrit-PatchSet: 2 </div>
<div style="display:none"> Gerrit-Owner: pespin <pespin@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder </div>
<div style="display:none"> Gerrit-Reviewer: pespin <pespin@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: srs_andre <andre@softwareradiosystems.com> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>