Commit 98cfbe42 authored by Pedro Tammela's avatar Pedro Tammela Committed by Paolo Abeni

selftests/tc-testing: localize test resources

As of today, the current tdc architecture creates one netns and uses it
to run all tests. This assumption was embedded into the nsPlugin which
carried over as how the tests were written.

The tdc tests are by definition self contained and can,
theoretically, run in parallel. Even though in the kernel they will
serialize over the rtnl lock, we should expect a significant speedup of the
total wall time for the entire test suite, which is hitting close to
1100 tests at this point.

A first step to achieve this goal is to remove sharing of global resources like
veth/dummy interfaces and the netns. In this patch we 'localize' these
resources on a per test basis. Each test gets it's own netns, VETH/dummy interfaces.
The resources are spawned in the pre_suite phase, where tdc will prepare
all netns and interfaces for all tests. This is done in order to avoid
concurrency issues with netns / interfaces spawning and commands using
them. As tdc progresses, the resources are deleted after each test finishes
executing.

Tests that don't use the nsPlugin still run under the root namespace,
but are now required to manage any external resources like interfaces.
These cannot be parallelized as their definition doesn't allow it.
On the other hand, when using the nsPlugin, tests don't need to create
dummy/veth interfaces as these are handled already.
Tested-by: default avatarDavide Caratti <dcaratti@redhat.com>
Signed-off-by: default avatarPedro Tammela <pctammela@mojatatu.com>
Acked-by: default avatarJamal Hadi Salim <jhs@mojatatu.com>
Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
parent d387e34f
...@@ -5,10 +5,10 @@ class TdcPlugin: ...@@ -5,10 +5,10 @@ class TdcPlugin:
super().__init__() super().__init__()
print(' -- {}.__init__'.format(self.sub_class)) print(' -- {}.__init__'.format(self.sub_class))
def pre_suite(self, testcount, testidlist): def pre_suite(self, testcount, testlist):
'''run commands before test_runner goes into a test loop''' '''run commands before test_runner goes into a test loop'''
self.testcount = testcount self.testcount = testcount
self.testidlist = testidlist self.testlist = testlist
if self.args.verbose > 1: if self.args.verbose > 1:
print(' -- {}.pre_suite'.format(self.sub_class)) print(' -- {}.pre_suite'.format(self.sub_class))
......
...@@ -3,6 +3,7 @@ import signal ...@@ -3,6 +3,7 @@ import signal
from string import Template from string import Template
import subprocess import subprocess
import time import time
from functools import cached_property
from TdcPlugin import TdcPlugin from TdcPlugin import TdcPlugin
from tdc_config import * from tdc_config import *
...@@ -12,26 +13,77 @@ class SubPlugin(TdcPlugin): ...@@ -12,26 +13,77 @@ class SubPlugin(TdcPlugin):
self.sub_class = 'ns/SubPlugin' self.sub_class = 'ns/SubPlugin'
super().__init__() super().__init__()
def pre_suite(self, testcount, testidlist): def pre_suite(self, testcount, testlist):
'''run commands before test_runner goes into a test loop''' super().pre_suite(testcount, testlist)
super().pre_suite(testcount, testidlist)
if self.args.namespace: print("Setting up namespaces and devices...")
self._ns_create()
else:
self._ports_create()
def post_suite(self, index): original = self.args.NAMES
'''run commands after test_runner goes into a test loop'''
super().post_suite(index) for t in testlist:
if 'skip' in t and t['skip'] == 'yes':
continue
if 'nsPlugin' not in t['plugins']:
continue
shadow = {}
shadow['IP'] = original['IP']
shadow['TC'] = original['TC']
shadow['NS'] = '{}-{}'.format(original['NS'], t['random'])
shadow['DEV0'] = '{}id{}'.format(original['DEV0'], t['id'])
shadow['DEV1'] = '{}id{}'.format(original['DEV1'], t['id'])
shadow['DUMMY'] = '{}id{}'.format(original['DUMMY'], t['id'])
shadow['DEV2'] = original['DEV2']
self.args.NAMES = shadow
if self.args.namespace:
self._ns_create()
else:
self._ports_create()
self.args.NAMES = original
def pre_case(self, caseinfo, test_skip):
if self.args.verbose: if self.args.verbose:
print('{}.post_suite'.format(self.sub_class)) print('{}.pre_case'.format(self.sub_class))
if test_skip:
return
# Make sure the netns is visible in the fs
while True:
self._proc_check()
try:
ns = self.args.NAMES['NS']
f = open('/run/netns/{}'.format(ns))
f.close()
break
except:
continue
def post_case(self):
if self.args.verbose:
print('{}.post_case'.format(self.sub_class))
if self.args.namespace: if self.args.namespace:
self._ns_destroy() self._ns_destroy()
else: else:
self._ports_destroy() self._ports_destroy()
def post_suite(self, index):
if self.args.verbose:
print('{}.post_suite'.format(self.sub_class))
# Make sure we don't leak resources
for f in os.listdir('/run/netns/'):
cmd = self._replace_keywords("$IP netns del {}".format(f))
if self.args.verbose > 3:
print('_exec_cmd: command "{}"'.format(cmd))
subprocess.run(cmd, shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
def add_args(self, parser): def add_args(self, parser):
super().add_args(parser) super().add_args(parser)
self.argparser_group = self.argparser.add_argument_group( self.argparser_group = self.argparser.add_argument_group(
...@@ -77,18 +129,43 @@ class SubPlugin(TdcPlugin): ...@@ -77,18 +129,43 @@ class SubPlugin(TdcPlugin):
print('adjust_command: return command [{}]'.format(command)) print('adjust_command: return command [{}]'.format(command))
return command return command
def _ports_create(self): def _ports_create_cmds(self):
cmd = '$IP link add $DEV0 type veth peer name $DEV1' cmds = []
self._exec_cmd('pre', cmd)
cmd = '$IP link set $DEV0 up' cmds.append(self._replace_keywords('link add $DEV0 type veth peer name $DEV1'))
self._exec_cmd('pre', cmd) cmds.append(self._replace_keywords('link set $DEV0 up'))
cmds.append(self._replace_keywords('link add $DUMMY type dummy'))
if not self.args.namespace: if not self.args.namespace:
cmd = '$IP link set $DEV1 up' cmds.append(self._replace_keywords('link set $DEV1 up'))
self._exec_cmd('pre', cmd)
return cmds
def _ports_create(self):
self._exec_cmd_batched('pre', self._ports_create_cmds())
def _ports_destroy_cmd(self):
return self._replace_keywords('link del $DEV0')
def _ports_destroy(self): def _ports_destroy(self):
cmd = '$IP link del $DEV0' self._exec_cmd('post', self._ports_destroy_cmd())
self._exec_cmd('post', cmd)
def _ns_create_cmds(self):
cmds = []
if self.args.namespace:
ns = self.args.NAMES['NS']
cmds.append(self._replace_keywords('netns add {}'.format(ns)))
cmds.append(self._replace_keywords('link set $DEV1 netns {}'.format(ns)))
cmds.append(self._replace_keywords('link set $DUMMY netns {}'.format(ns)))
cmds.append(self._replace_keywords('netns exec {} $IP link set $DEV1 up'.format(ns)))
cmds.append(self._replace_keywords('netns exec {} $IP link set $DUMMY up'.format(ns)))
if self.args.device:
cmds.append(self._replace_keywords('link set $DEV2 netns {}'.format(ns)))
cmds.append(self._replace_keywords('netns exec {} $IP link set $DEV2 up'.format(ns)))
return cmds
def _ns_create(self): def _ns_create(self):
''' '''
...@@ -96,18 +173,10 @@ class SubPlugin(TdcPlugin): ...@@ -96,18 +173,10 @@ class SubPlugin(TdcPlugin):
the required network devices for it. the required network devices for it.
''' '''
self._ports_create() self._ports_create()
if self.args.namespace: self._exec_cmd_batched('pre', self._ns_create_cmds())
cmd = '$IP netns add {}'.format(self.args.NAMES['NS'])
self._exec_cmd('pre', cmd) def _ns_destroy_cmd(self):
cmd = '$IP link set $DEV1 netns {}'.format(self.args.NAMES['NS']) return self._replace_keywords('netns delete {}'.format(self.args.NAMES['NS']))
self._exec_cmd('pre', cmd)
cmd = '$IP -n {} link set $DEV1 up'.format(self.args.NAMES['NS'])
self._exec_cmd('pre', cmd)
if self.args.device:
cmd = '$IP link set $DEV2 netns {}'.format(self.args.NAMES['NS'])
self._exec_cmd('pre', cmd)
cmd = '$IP -n {} link set $DEV2 up'.format(self.args.NAMES['NS'])
self._exec_cmd('pre', cmd)
def _ns_destroy(self): def _ns_destroy(self):
''' '''
...@@ -115,35 +184,49 @@ class SubPlugin(TdcPlugin): ...@@ -115,35 +184,49 @@ class SubPlugin(TdcPlugin):
devices as well) devices as well)
''' '''
if self.args.namespace: if self.args.namespace:
cmd = '$IP netns delete {}'.format(self.args.NAMES['NS']) self._exec_cmd('post', self._ns_destroy_cmd())
self._exec_cmd('post', cmd) self._ports_destroy()
@cached_property
def _proc(self):
ip = self._replace_keywords("$IP -b -")
proc = subprocess.Popen(ip,
shell=True,
stdin=subprocess.PIPE,
env=ENVIR)
return proc
def _proc_check(self):
proc = self._proc
proc.poll()
if proc.returncode is not None and proc.returncode != 0:
raise RuntimeError("iproute2 exited with an error code")
def _exec_cmd(self, stage, command): def _exec_cmd(self, stage, command):
''' '''
Perform any required modifications on an executable command, then run Perform any required modifications on an executable command, then run
it in a subprocess and return the results. it in a subprocess and return the results.
''' '''
if '$' in command:
command = self._replace_keywords(command)
self.adjust_command(stage, command) if self.args.verbose > 3:
if self.args.verbose:
print('_exec_cmd: command "{}"'.format(command)) print('_exec_cmd: command "{}"'.format(command))
proc = subprocess.Popen(command,
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=ENVIR)
(rawout, serr) = proc.communicate()
if proc.returncode != 0 and len(serr) > 0: proc = self._proc
foutput = serr.decode("utf-8")
else: proc.stdin.write((command + '\n').encode())
foutput = rawout.decode("utf-8") proc.stdin.flush()
if self.args.verbose > 3:
print('_exec_cmd proc: {}'.format(proc))
self._proc_check()
proc.stdout.close() def _exec_cmd_batched(self, stage, commands):
proc.stderr.close() for cmd in commands:
return proc, foutput self._exec_cmd(stage, cmd)
def _replace_keywords(self, cmd): def _replace_keywords(self, cmd):
""" """
......
...@@ -10,9 +10,9 @@ class SubPlugin(TdcPlugin): ...@@ -10,9 +10,9 @@ class SubPlugin(TdcPlugin):
self.sub_class = 'root/SubPlugin' self.sub_class = 'root/SubPlugin'
super().__init__() super().__init__()
def pre_suite(self, testcount, testidlist): def pre_suite(self, testcount, testlist):
# run commands before test_runner goes into a test loop # run commands before test_runner goes into a test loop
super().pre_suite(testcount, testidlist) super().pre_suite(testcount, testlist)
if os.geteuid(): if os.geteuid():
print('This script must be run with root privileges', file=sys.stderr) print('This script must be run with root privileges', file=sys.stderr)
......
...@@ -25,9 +25,10 @@ class SubPlugin(TdcPlugin): ...@@ -25,9 +25,10 @@ class SubPlugin(TdcPlugin):
self._tsr = TestSuiteReport() self._tsr = TestSuiteReport()
super().__init__() super().__init__()
def pre_suite(self, testcount, testidlist): def pre_suite(self, testcount, testist):
'''run commands before test_runner goes into a test loop''' '''run commands before test_runner goes into a test loop'''
super().pre_suite(testcount, testidlist) self.testidlist = [tidx['id'] for tidx in testlist]
super().pre_suite(testcount, testlist)
if self.args.verbose > 1: if self.args.verbose > 1:
print('{}.pre_suite'.format(self.sub_class)) print('{}.pre_suite'.format(self.sub_class))
if self.args.valgrind: if self.args.valgrind:
......
...@@ -16,6 +16,7 @@ import json ...@@ -16,6 +16,7 @@ import json
import subprocess import subprocess
import time import time
import traceback import traceback
import random
from collections import OrderedDict from collections import OrderedDict
from string import Template from string import Template
...@@ -38,12 +39,11 @@ class PluginMgrTestFail(Exception): ...@@ -38,12 +39,11 @@ class PluginMgrTestFail(Exception):
class PluginMgr: class PluginMgr:
def __init__(self, argparser): def __init__(self, argparser):
super().__init__() super().__init__()
self.plugins = {} self.plugins = set()
self.plugin_instances = [] self.plugin_instances = []
self.failed_plugins = {} self.failed_plugins = {}
self.argparser = argparser self.argparser = argparser
# TODO, put plugins in order
plugindir = os.getenv('TDC_PLUGIN_DIR', './plugins') plugindir = os.getenv('TDC_PLUGIN_DIR', './plugins')
for dirpath, dirnames, filenames in os.walk(plugindir): for dirpath, dirnames, filenames in os.walk(plugindir):
for fn in filenames: for fn in filenames:
...@@ -53,32 +53,43 @@ class PluginMgr: ...@@ -53,32 +53,43 @@ class PluginMgr:
not fn.startswith('.#')): not fn.startswith('.#')):
mn = fn[0:-3] mn = fn[0:-3]
foo = importlib.import_module('plugins.' + mn) foo = importlib.import_module('plugins.' + mn)
self.plugins[mn] = foo self.plugins.add(mn)
self.plugin_instances.append(foo.SubPlugin()) self.plugin_instances[mn] = foo.SubPlugin()
def load_plugin(self, pgdir, pgname): def load_plugin(self, pgdir, pgname):
pgname = pgname[0:-3] pgname = pgname[0:-3]
self.plugins.add(pgname)
foo = importlib.import_module('{}.{}'.format(pgdir, pgname)) foo = importlib.import_module('{}.{}'.format(pgdir, pgname))
self.plugins[pgname] = foo
self.plugin_instances.append(foo.SubPlugin()) # nsPlugin must always be the first one
self.plugin_instances[-1].check_args(self.args, None) if pgname == "nsPlugin":
self.plugin_instances.insert(0, (pgname, foo.SubPlugin()))
self.plugin_instances[0][1].check_args(self.args, None)
else:
self.plugin_instances.append((pgname, foo.SubPlugin()))
self.plugin_instances[-1][1].check_args(self.args, None)
def get_required_plugins(self, testlist): def get_required_plugins(self, testlist):
''' '''
Get all required plugins from the list of test cases and return Get all required plugins from the list of test cases and return
all unique items. all unique items.
''' '''
reqs = [] reqs = set()
for t in testlist: for t in testlist:
try: try:
if 'requires' in t['plugins']: if 'requires' in t['plugins']:
if isinstance(t['plugins']['requires'], list): if isinstance(t['plugins']['requires'], list):
reqs.extend(t['plugins']['requires']) reqs.update(set(t['plugins']['requires']))
else: else:
reqs.append(t['plugins']['requires']) reqs.add(t['plugins']['requires'])
t['plugins'] = t['plugins']['requires']
else:
t['plugins'] = []
except KeyError: except KeyError:
t['plugins'] = []
continue continue
reqs = get_unique_item(reqs)
return reqs return reqs
def load_required_plugins(self, reqs, parser, args, remaining): def load_required_plugins(self, reqs, parser, args, remaining):
...@@ -115,15 +126,17 @@ class PluginMgr: ...@@ -115,15 +126,17 @@ class PluginMgr:
return args return args
def call_pre_suite(self, testcount, testidlist): def call_pre_suite(self, testcount, testidlist):
for pgn_inst in self.plugin_instances: for (_, pgn_inst) in self.plugin_instances:
pgn_inst.pre_suite(testcount, testidlist) pgn_inst.pre_suite(testcount, testidlist)
def call_post_suite(self, index): def call_post_suite(self, index):
for pgn_inst in reversed(self.plugin_instances): for (_, pgn_inst) in reversed(self.plugin_instances):
pgn_inst.post_suite(index) pgn_inst.post_suite(index)
def call_pre_case(self, caseinfo, *, test_skip=False): def call_pre_case(self, caseinfo, *, test_skip=False):
for pgn_inst in self.plugin_instances: for (pgn, pgn_inst) in self.plugin_instances:
if pgn not in caseinfo['plugins']:
continue
try: try:
pgn_inst.pre_case(caseinfo, test_skip) pgn_inst.pre_case(caseinfo, test_skip)
except Exception as ee: except Exception as ee:
...@@ -133,29 +146,37 @@ class PluginMgr: ...@@ -133,29 +146,37 @@ class PluginMgr:
print('testid is {}'.format(caseinfo['id'])) print('testid is {}'.format(caseinfo['id']))
raise raise
def call_post_case(self): def call_post_case(self, caseinfo):
for pgn_inst in reversed(self.plugin_instances): for (pgn, pgn_inst) in reversed(self.plugin_instances):
if pgn not in caseinfo['plugins']:
continue
pgn_inst.post_case() pgn_inst.post_case()
def call_pre_execute(self): def call_pre_execute(self, caseinfo):
for pgn_inst in self.plugin_instances: for (pgn, pgn_inst) in self.plugin_instances:
if pgn not in caseinfo['plugins']:
continue
pgn_inst.pre_execute() pgn_inst.pre_execute()
def call_post_execute(self): def call_post_execute(self, caseinfo):
for pgn_inst in reversed(self.plugin_instances): for (pgn, pgn_inst) in reversed(self.plugin_instances):
if pgn not in caseinfo['plugins']:
continue
pgn_inst.post_execute() pgn_inst.post_execute()
def call_add_args(self, parser): def call_add_args(self, parser):
for pgn_inst in self.plugin_instances: for (pgn, pgn_inst) in self.plugin_instances:
parser = pgn_inst.add_args(parser) parser = pgn_inst.add_args(parser)
return parser return parser
def call_check_args(self, args, remaining): def call_check_args(self, args, remaining):
for pgn_inst in self.plugin_instances: for (pgn, pgn_inst) in self.plugin_instances:
pgn_inst.check_args(args, remaining) pgn_inst.check_args(args, remaining)
def call_adjust_command(self, stage, command): def call_adjust_command(self, caseinfo, stage, command):
for pgn_inst in self.plugin_instances: for (pgn, pgn_inst) in self.plugin_instances:
if pgn not in caseinfo['plugins']:
continue
command = pgn_inst.adjust_command(stage, command) command = pgn_inst.adjust_command(stage, command)
return command return command
...@@ -177,7 +198,7 @@ def replace_keywords(cmd): ...@@ -177,7 +198,7 @@ def replace_keywords(cmd):
return subcmd return subcmd
def exec_cmd(args, pm, stage, command): def exec_cmd(caseinfo, args, pm, stage, command):
""" """
Perform any required modifications on an executable command, then run Perform any required modifications on an executable command, then run
it in a subprocess and return the results. it in a subprocess and return the results.
...@@ -187,9 +208,10 @@ def exec_cmd(args, pm, stage, command): ...@@ -187,9 +208,10 @@ def exec_cmd(args, pm, stage, command):
if '$' in command: if '$' in command:
command = replace_keywords(command) command = replace_keywords(command)
command = pm.call_adjust_command(stage, command) command = pm.call_adjust_command(caseinfo, stage, command)
if args.verbose > 0: if args.verbose > 0:
print('command "{}"'.format(command)) print('command "{}"'.format(command))
proc = subprocess.Popen(command, proc = subprocess.Popen(command,
shell=True, shell=True,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
...@@ -211,7 +233,7 @@ def exec_cmd(args, pm, stage, command): ...@@ -211,7 +233,7 @@ def exec_cmd(args, pm, stage, command):
return proc, foutput return proc, foutput
def prepare_env(args, pm, stage, prefix, cmdlist, output = None): def prepare_env(caseinfo, args, pm, stage, prefix, cmdlist, output = None):
""" """
Execute the setup/teardown commands for a test case. Execute the setup/teardown commands for a test case.
Optionally terminate test execution if the command fails. Optionally terminate test execution if the command fails.
...@@ -229,7 +251,7 @@ def prepare_env(args, pm, stage, prefix, cmdlist, output = None): ...@@ -229,7 +251,7 @@ def prepare_env(args, pm, stage, prefix, cmdlist, output = None):
if not cmd: if not cmd:
continue continue
(proc, foutput) = exec_cmd(args, pm, stage, cmd) (proc, foutput) = exec_cmd(caseinfo, args, pm, stage, cmd)
if proc and (proc.returncode not in exit_codes): if proc and (proc.returncode not in exit_codes):
print('', file=sys.stderr) print('', file=sys.stderr)
...@@ -352,6 +374,10 @@ def find_in_json_other(res, outputJSONVal, matchJSONVal, matchJSONKey=None): ...@@ -352,6 +374,10 @@ def find_in_json_other(res, outputJSONVal, matchJSONVal, matchJSONKey=None):
def run_one_test(pm, args, index, tidx): def run_one_test(pm, args, index, tidx):
global NAMES global NAMES
ns = NAMES['NS']
dev0 = NAMES['DEV0']
dev1 = NAMES['DEV1']
dummy = NAMES['DUMMY']
result = True result = True
tresult = "" tresult = ""
tap = "" tap = ""
...@@ -366,38 +392,42 @@ def run_one_test(pm, args, index, tidx): ...@@ -366,38 +392,42 @@ def run_one_test(pm, args, index, tidx):
res.set_result(ResultState.skip) res.set_result(ResultState.skip)
res.set_errormsg('Test case designated as skipped.') res.set_errormsg('Test case designated as skipped.')
pm.call_pre_case(tidx, test_skip=True) pm.call_pre_case(tidx, test_skip=True)
pm.call_post_execute() pm.call_post_execute(tidx)
return res return res
if 'dependsOn' in tidx: if 'dependsOn' in tidx:
if (args.verbose > 0): if (args.verbose > 0):
print('probe command for test skip') print('probe command for test skip')
(p, procout) = exec_cmd(args, pm, 'execute', tidx['dependsOn']) (p, procout) = exec_cmd(tidx, args, pm, 'execute', tidx['dependsOn'])
if p: if p:
if (p.returncode != 0): if (p.returncode != 0):
res = TestResult(tidx['id'], tidx['name']) res = TestResult(tidx['id'], tidx['name'])
res.set_result(ResultState.skip) res.set_result(ResultState.skip)
res.set_errormsg('probe command: test skipped.') res.set_errormsg('probe command: test skipped.')
pm.call_pre_case(tidx, test_skip=True) pm.call_pre_case(tidx, test_skip=True)
pm.call_post_execute() pm.call_post_execute(tidx)
return res return res
# populate NAMES with TESTID for this test # populate NAMES with TESTID for this test
NAMES['TESTID'] = tidx['id'] NAMES['TESTID'] = tidx['id']
NAMES['NS'] = '{}-{}'.format(NAMES['NS'], tidx['random'])
NAMES['DEV0'] = '{}id{}'.format(NAMES['DEV0'], tidx['id'])
NAMES['DEV1'] = '{}id{}'.format(NAMES['DEV1'], tidx['id'])
NAMES['DUMMY'] = '{}id{}'.format(NAMES['DUMMY'], tidx['id'])
pm.call_pre_case(tidx) pm.call_pre_case(tidx)
prepare_env(args, pm, 'setup', "-----> prepare stage", tidx["setup"]) prepare_env(tidx, args, pm, 'setup', "-----> prepare stage", tidx["setup"])
if (args.verbose > 0): if (args.verbose > 0):
print('-----> execute stage') print('-----> execute stage')
pm.call_pre_execute() pm.call_pre_execute(tidx)
(p, procout) = exec_cmd(args, pm, 'execute', tidx["cmdUnderTest"]) (p, procout) = exec_cmd(tidx, args, pm, 'execute', tidx["cmdUnderTest"])
if p: if p:
exit_code = p.returncode exit_code = p.returncode
else: else:
exit_code = None exit_code = None
pm.call_post_execute() pm.call_post_execute(tidx)
if (exit_code is None or exit_code != int(tidx["expExitCode"])): if (exit_code is None or exit_code != int(tidx["expExitCode"])):
print("exit: {!r}".format(exit_code)) print("exit: {!r}".format(exit_code))
...@@ -409,7 +439,7 @@ def run_one_test(pm, args, index, tidx): ...@@ -409,7 +439,7 @@ def run_one_test(pm, args, index, tidx):
else: else:
if args.verbose > 0: if args.verbose > 0:
print('-----> verify stage') print('-----> verify stage')
(p, procout) = exec_cmd(args, pm, 'verify', tidx["verifyCmd"]) (p, procout) = exec_cmd(tidx, args, pm, 'verify', tidx["verifyCmd"])
if procout: if procout:
if 'matchJSON' in tidx: if 'matchJSON' in tidx:
verify_by_json(procout, res, tidx, args, pm) verify_by_json(procout, res, tidx, args, pm)
...@@ -431,13 +461,20 @@ def run_one_test(pm, args, index, tidx): ...@@ -431,13 +461,20 @@ def run_one_test(pm, args, index, tidx):
else: else:
res.set_result(ResultState.success) res.set_result(ResultState.success)
prepare_env(args, pm, 'teardown', '-----> teardown stage', tidx['teardown'], procout) prepare_env(tidx, args, pm, 'teardown', '-----> teardown stage', tidx['teardown'], procout)
pm.call_post_case() pm.call_post_case(tidx)
index += 1 index += 1
# remove TESTID from NAMES # remove TESTID from NAMES
del(NAMES['TESTID']) del(NAMES['TESTID'])
# Restore names
NAMES['NS'] = ns
NAMES['DEV0'] = dev0
NAMES['DEV1'] = dev1
NAMES['DUMMY'] = dummy
return res return res
def test_runner(pm, args, filtered_tests): def test_runner(pm, args, filtered_tests):
...@@ -461,7 +498,7 @@ def test_runner(pm, args, filtered_tests): ...@@ -461,7 +498,7 @@ def test_runner(pm, args, filtered_tests):
tsr = TestSuiteReport() tsr = TestSuiteReport()
try: try:
pm.call_pre_suite(tcount, [tidx['id'] for tidx in testlist]) pm.call_pre_suite(tcount, testlist)
except Exception as ee: except Exception as ee:
ex_type, ex, ex_tb = sys.exc_info() ex_type, ex, ex_tb = sys.exc_info()
print('Exception {} {} (caught in pre_suite).'. print('Exception {} {} (caught in pre_suite).'.
...@@ -661,7 +698,6 @@ def get_id_list(alltests): ...@@ -661,7 +698,6 @@ def get_id_list(alltests):
""" """
return [x["id"] for x in alltests] return [x["id"] for x in alltests]
def check_case_id(alltests): def check_case_id(alltests):
""" """
Check for duplicate test case IDs. Check for duplicate test case IDs.
...@@ -683,7 +719,6 @@ def generate_case_ids(alltests): ...@@ -683,7 +719,6 @@ def generate_case_ids(alltests):
If a test case has a blank ID field, generate a random hex ID for it If a test case has a blank ID field, generate a random hex ID for it
and then write the test cases back to disk. and then write the test cases back to disk.
""" """
import random
for c in alltests: for c in alltests:
if (c["id"] == ""): if (c["id"] == ""):
while True: while True:
...@@ -742,6 +777,9 @@ def filter_tests_by_category(args, testlist): ...@@ -742,6 +777,9 @@ def filter_tests_by_category(args, testlist):
return answer return answer
def set_random(alltests):
for tidx in alltests:
tidx['random'] = random.getrandbits(32)
def get_test_cases(args): def get_test_cases(args):
""" """
...@@ -840,6 +878,8 @@ def set_operation_mode(pm, parser, args, remaining): ...@@ -840,6 +878,8 @@ def set_operation_mode(pm, parser, args, remaining):
list_test_cases(alltests) list_test_cases(alltests)
exit(0) exit(0)
set_random(alltests)
exit_code = 0 # KSFT_PASS exit_code = 0 # KSFT_PASS
if len(alltests): if len(alltests):
req_plugins = pm.get_required_plugins(alltests) req_plugins = pm.get_required_plugins(alltests)
...@@ -883,6 +923,13 @@ def main(): ...@@ -883,6 +923,13 @@ def main():
Start of execution; set up argument parser and get the arguments, Start of execution; set up argument parser and get the arguments,
and start operations. and start operations.
""" """
import resource
if sys.version_info.major < 3 or sys.version_info.minor < 8:
sys.exit("tdc requires at least python 3.8")
resource.setrlimit(resource.RLIMIT_NOFILE, (1048576, 1048576))
parser = args_parse() parser = args_parse()
parser = set_args(parser) parser = set_args(parser)
pm = PluginMgr(parser) pm = PluginMgr(parser)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment