Split Scenario class to its own file

Change-Id: Ia029de7ecda4c8dc3d0b4c412e4c9c0a65cf0185
This commit is contained in:
Pau Espin 2020-05-11 15:12:07 +02:00
parent b1d8d30526
commit 4e6b5077d0
4 changed files with 148 additions and 112 deletions

View File

@ -3,7 +3,11 @@ import os
import sys
import _prep
import shutil
from osmo_gsm_tester.core import log, config, util, report
from osmo_gsm_tester.core import log
from osmo_gsm_tester.core import config
from osmo_gsm_tester.core import util
from osmo_gsm_tester.core import report
from osmo_gsm_tester.core import scenario
from osmo_gsm_tester.core import suite
from osmo_gsm_tester.core.schema import generate_schemas, get_all_schema
@ -66,26 +70,26 @@ print(output)
print('- test with half empty scenario')
trial = FakeTrial()
scenario = config.Scenario('foo', 'bar')
scenario['resources'] = { 'bts': [{'type': 'osmo-bts-trx'}] }
s = suite.SuiteRun(trial, 'test_suite', s_def, [scenario])
sc = scenario.Scenario('foo', 'bar')
sc['resources'] = { 'bts': [{'type': 'osmo-bts-trx'}] }
s = suite.SuiteRun(trial, 'test_suite', s_def, [sc])
results = s.run_tests('hello_world.py')
print(report.suite_to_text(s))
print('- test with scenario')
trial = FakeTrial()
scenario = config.Scenario('foo', 'bar')
scenario['resources'] = { 'bts': [{ 'times': '2', 'type': 'osmo-bts-trx', 'trx_list': [{'nominal_power': '10'}, {'nominal_power': '12'}]}, {'type': 'sysmo'}] }
s = suite.SuiteRun(trial, 'test_suite', s_def, [scenario])
sc = scenario.Scenario('foo', 'bar')
sc['resources'] = { 'bts': [{ 'times': '2', 'type': 'osmo-bts-trx', 'trx_list': [{'nominal_power': '10'}, {'nominal_power': '12'}]}, {'type': 'sysmo'}] }
s = suite.SuiteRun(trial, 'test_suite', s_def, [sc])
results = s.run_tests('hello_world.py')
print(report.suite_to_text(s))
print('- test with scenario and modifiers')
trial = FakeTrial()
scenario = config.Scenario('foo', 'bar')
scenario['resources'] = { 'bts': [{ 'times': '2', 'type': 'osmo-bts-trx', 'trx_list': [{'nominal_power': '10'}, {'nominal_power': '12'}]}, {'type': 'sysmo'}] }
scenario['modifiers'] = { 'bts': [{ 'times': '2', 'trx_list': [{'nominal_power': '20'}, {'nominal_power': '20'}]}, {'type': 'sysmo'}] }
s = suite.SuiteRun(trial, 'test_suite', s_def, [scenario])
sc = scenario.Scenario('foo', 'bar')
sc['resources'] = { 'bts': [{ 'times': '2', 'type': 'osmo-bts-trx', 'trx_list': [{'nominal_power': '10'}, {'nominal_power': '12'}]}, {'type': 'sysmo'}] }
sc['modifiers'] = { 'bts': [{ 'times': '2', 'trx_list': [{'nominal_power': '20'}, {'nominal_power': '20'}]}, {'type': 'sysmo'}] }
s = suite.SuiteRun(trial, 'test_suite', s_def, [sc])
s.reserve_resources()
print(repr(s.reserved_resources))
results = s.run_tests('hello_world.py')
@ -93,9 +97,9 @@ print(report.suite_to_text(s))
print('- test with suite-specific config')
trial = FakeTrial()
scenario = config.Scenario('foo', 'bar')
scenario['config'] = {'suite': {s.name(): { 'some_suite_global_param': 'heyho', 'test_suite_params': {'one_bool_parameter': 'true', 'second_list_parameter': ['23', '45']}}}}
s = suite.SuiteRun(trial, 'test_suite', s_def, [scenario])
sc = scenario.Scenario('foo', 'bar')
sc['config'] = {'suite': {s.name(): { 'some_suite_global_param': 'heyho', 'test_suite_params': {'one_bool_parameter': 'true', 'second_list_parameter': ['23', '45']}}}}
s = suite.SuiteRun(trial, 'test_suite', s_def, [sc])
s.reserve_resources()
print(repr(s.reserved_resources))
results = s.run_tests('test_suite_params.py')

View File

@ -165,6 +165,13 @@ def write(path, config):
with open(path, 'w') as f:
f.write(tostr(config))
def fromstr(config_str, validation_schema=None):
config = yaml.safe_load(config_str)
config = _standardize(config)
if validation_schema is not None:
schema.validate(config, validation_schema)
return config
def tostr(config):
return _tostr(_standardize(config))
@ -188,99 +195,6 @@ def get_defaults(for_kind):
defaults = read_config_file(DEFAULTS_CONF, if_missing_return={})
return defaults.get(for_kind, {})
class Scenario(log.Origin, dict):
def __init__(self, name, path, param_list=[]):
super().__init__(log.C_TST, name)
self.path = path
self.param_list = param_list
@classmethod
def count_cont_char_backward(cls, str, before_pos, c):
n = 0
i = before_pos - 1
while i >= 0:
if str[i] != c:
break
n += 1
i -= 1
return n
@classmethod
def split_scenario_parameters(cls, str):
cur_pos = 0
param_li = []
saved = ''
# Split into a list, but we want to escape '\,' to avoid splitting parameters containing commas.
while True:
prev_pos = cur_pos
cur_pos = str.find(',', prev_pos)
if cur_pos == -1:
param_li.append(str[prev_pos:])
break
if cur_pos == 0:
param_li.append('')
elif cur_pos != 0 and str[cur_pos - 1] == '\\' and cls.count_cont_char_backward(str, cur_pos, '\\') % 2 == 1:
saved += str[prev_pos:cur_pos - 1] + ','
else:
param_li.append(saved + str[prev_pos:cur_pos])
saved = ''
cur_pos += 1
i = 0
# Also escape '\\' -> '\'
while i < len(param_li):
param_li[i] = param_li[i].replace('\\\\', '\\')
i += 1
return param_li
@classmethod
def from_param_list_str(cls, name, path, param_list_str):
param_list = cls.split_scenario_parameters(param_list_str)
return cls(name, path, param_list)
def read_from_file(self, validation_schema):
with open(self.path, 'r') as f:
config_str = f.read()
if len(self.param_list) != 0:
param_dict = {}
i = 1
for param in self.param_list:
param_dict['param' + str(i)] = param
i += 1
self.dbg(param_dict=param_dict)
config_str = template.render_strbuf_inline(config_str, param_dict)
config = yaml.safe_load(config_str)
config = _standardize(config)
if validation_schema:
schema.validate(config, validation_schema)
self.update(config)
def get_scenario(name, validation_schema=None):
scenarios_dir = get_scenarios_dir()
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)
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):
# 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))
# 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')]
sc = Scenario.from_param_list_str(name, path, param_list_str)
sc.read_from_file(validation_schema)
return sc
def overlay(dest, src):
if is_dict(dest):
if not is_dict(src):

View File

@ -0,0 +1,117 @@
# osmo_gsm_tester: Suite scenario
#
# Copyright (C) 2016-2020 by sysmocom - s.f.m.c. GmbH
#
# Author: Neels Hofmeyr <neels@hofmeyr.de>
# Author: Pau Espin Pedrol <pespin@sysmocom.de>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
from . import log
from . import template
from . import config
class Scenario(log.Origin, dict):
def __init__(self, name, path, param_list=[]):
super().__init__(log.C_TST, name)
self.path = path
self.param_list = param_list
@classmethod
def count_cont_char_backward(cls, str, before_pos, c):
n = 0
i = before_pos - 1
while i >= 0:
if str[i] != c:
break
n += 1
i -= 1
return n
@classmethod
def split_scenario_parameters(cls, str):
cur_pos = 0
param_li = []
saved = ''
# Split into a list, but we want to escape '\,' to avoid splitting parameters containing commas.
while True:
prev_pos = cur_pos
cur_pos = str.find(',', prev_pos)
if cur_pos == -1:
param_li.append(str[prev_pos:])
break
if cur_pos == 0:
param_li.append('')
elif cur_pos != 0 and str[cur_pos - 1] == '\\' and cls.count_cont_char_backward(str, cur_pos, '\\') % 2 == 1:
saved += str[prev_pos:cur_pos - 1] + ','
else:
param_li.append(saved + str[prev_pos:cur_pos])
saved = ''
cur_pos += 1
i = 0
# Also escape '\\' -> '\'
while i < len(param_li):
param_li[i] = param_li[i].replace('\\\\', '\\')
i += 1
return param_li
@classmethod
def from_param_list_str(cls, name, path, param_list_str):
param_list = cls.split_scenario_parameters(param_list_str)
return cls(name, path, param_list)
def read_from_file(self, validation_schema):
with open(self.path, 'r') as f:
config_str = f.read()
if len(self.param_list) != 0:
param_dict = {}
i = 1
for param in self.param_list:
param_dict['param' + str(i)] = param
i += 1
self.dbg(param_dict=param_dict)
config_str = template.render_strbuf_inline(config_str, param_dict)
conf = config.fromstr(config_str, validation_schema)
self.update(conf)
def get_scenario(name, validation_schema=None):
scenarios_dir = config.get_scenarios_dir()
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)
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):
# 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))
# 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')]
sc = Scenario.from_param_list_str(name, path, param_list_str)
sc.read_from_file(validation_schema)
return sc
# vim: expandtab tabstop=4 shiftwidth=4

View File

@ -26,6 +26,7 @@ from . import log
from . import util
from . import schema
from . import resource
from . import scenario
from . import test
class SuiteDefinition(log.Origin):
@ -111,12 +112,12 @@ class SuiteRun(log.Origin):
if replicate_times:
combination = config.replicate_times(combination)
log.dbg(definition_conf=combination)
for scenario in self.scenarios:
log.ctx(combining_scenarios=conf_name, scenario=scenario.name())
c = scenario.get(conf_name, {})
for sc in self.scenarios:
log.ctx(combining_scenarios=conf_name, scenario=sc.name())
c = sc.get(conf_name, {})
if replicate_times:
c = config.replicate_times(c)
log.dbg(scenario=scenario.name(), conf=c)
log.dbg(scenario=sc.name(), conf=c)
if c is None:
continue
schema.combine(combination, c)
@ -258,7 +259,7 @@ def parse_suite_scenario_str(suite_scenario_str):
def load_suite_scenario_str(suite_scenario_str):
suite_name, scenario_names = parse_suite_scenario_str(suite_scenario_str)
suite = load(suite_name)
scenarios = [config.get_scenario(scenario_name, schema.get_all_schema()) for scenario_name in scenario_names]
scenarios = [scenario.get_scenario(scenario_name, schema.get_all_schema()) for scenario_name in scenario_names]
return (suite_scenario_str, suite, scenarios)
# vim: expandtab tabstop=4 shiftwidth=4