Skip to content

Commit 64de10f

Browse files
committed
scripts: twister: adding running capabilities to bsim harness
Bsim harness now can also be used to run tests. Signed-off-by: Artur Dobrynin <[email protected]>
1 parent 70fc4a3 commit 64de10f

File tree

4 files changed

+130
-15
lines changed

4 files changed

+130
-15
lines changed

scripts/pylib/twister/twisterlib/harness.py

Lines changed: 109 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -964,6 +964,34 @@ class Ztest(Test):
964964

965965
class Bsim(Harness):
966966

967+
DEFAULT_VERBOSITY = 2
968+
DEFAULT_SIM_LENGTH = 60e6
969+
970+
def __init__(self):
971+
super().__init__()
972+
self._bsim_out_path = os.getenv('BSIM_OUT_PATH', '')
973+
self._exe_path = None
974+
self._tc_output = []
975+
976+
@property
977+
def exe_path(self):
978+
if self._exe_path:
979+
return self._exe_path
980+
981+
if not self._bsim_out_path:
982+
logger.warning('Cannot copy bsim exe - BSIM_OUT_PATH not provided.')
983+
return self._exe_path
984+
985+
new_exe_name: str = self.instance.testsuite.harness_config.get('bsim_exe_name', '')
986+
if new_exe_name:
987+
new_exe_name = f'bs_{self.instance.platform.name}_{new_exe_name}'
988+
else:
989+
new_exe_name = f'bs_{self.instance.name}'
990+
991+
new_exe_name = new_exe_name.replace(os.path.sep, '_').replace('.', '_').replace('@', '_')
992+
self._exe_path = os.path.join(self._bsim_out_path, 'bin', new_exe_name)
993+
return self._exe_path
994+
967995
def build(self):
968996
"""
969997
Copying the application executable to BabbleSim's bin directory enables
@@ -978,23 +1006,91 @@ def build(self):
9781006
logger.warning('Cannot copy bsim exe - cannot find original executable.')
9791007
return
9801008

981-
bsim_out_path: str = os.getenv('BSIM_OUT_PATH', '')
982-
if not bsim_out_path:
983-
logger.warning('Cannot copy bsim exe - BSIM_OUT_PATH not provided.')
984-
return
1009+
logger.debug(f'Copying executable from {original_exe_path} to {self.exe_path}')
1010+
shutil.copy(original_exe_path, self.exe_path)
9851011

986-
new_exe_name: str = self.instance.testsuite.harness_config.get('bsim_exe_name', '')
987-
if new_exe_name:
988-
new_exe_name = f'bs_{self.instance.platform.name}_{new_exe_name}'
1012+
def _run_cmd(self, cmd, timeout):
1013+
with subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
1014+
cwd=os.path.join(self._bsim_out_path, 'bin')) as proc:
1015+
try:
1016+
reader_t = threading.Thread(target=self._output_reader, args=(proc,), daemon=True)
1017+
reader_t.start()
1018+
reader_t.join(timeout)
1019+
if reader_t.is_alive():
1020+
terminate_process(proc)
1021+
logger.warning('Timeout has occurred. Can be extended in testspec file. '
1022+
f'Currently set to {timeout} seconds.')
1023+
self.status = TwisterStatus.FAIL
1024+
proc.wait(timeout)
1025+
except subprocess.TimeoutExpired:
1026+
self.status = TwisterStatus.FAIL
1027+
proc.kill()
1028+
1029+
if self.status == TwisterStatus.NONE:
1030+
self.status = TwisterStatus.FAIL if proc.returncode != 0 else TwisterStatus.PASS
9891031
else:
990-
new_exe_name = self.instance.name
991-
new_exe_name = f'bs_{new_exe_name}'
1032+
self.status = TwisterStatus.FAIL \
1033+
if proc.returncode != 0 or self.status in [TwisterStatus.ERROR, TwisterStatus.FAIL] \
1034+
else TwisterStatus.PASS
9921035

993-
new_exe_name = new_exe_name.replace(os.path.sep, '_').replace('.', '_').replace('@', '_')
1036+
def _output_reader(self, proc):
1037+
while proc.stdout.readable() and proc.poll() is None:
1038+
line = proc.stdout.readline().decode().strip()
1039+
if not line:
1040+
continue
1041+
logger.debug(line)
1042+
self._tc_output.append(line)
1043+
proc.communicate()
1044+
1045+
def _generate_commands(self):
1046+
bsim_phy_path = os.path.join(self._bsim_out_path, 'bin', 'bs_2G4_phy_v1')
1047+
suite_id = f'-s={self.instance.name.split(os.path.sep)[-1].replace(".", "_")}'
1048+
1049+
cfg = self.instance.testsuite.harness_config
1050+
verbosity = f'-v={cfg.get("bsim_verbosity", self.DEFAULT_VERBOSITY)}'
1051+
sim_length = f'-sim_length={cfg.get("bsim_sim_length", self.DEFAULT_SIM_LENGTH)}'
1052+
extra_args = cfg.get('bsim_options', [])
1053+
test_ids = cfg.get('bsim_test_ids', [])
1054+
if not test_ids:
1055+
logger.error('No test ids specified for bsim test')
1056+
self.status = TwisterStatus.ERROR
1057+
return
1058+
1059+
cmds = [[self.exe_path, verbosity, suite_id, f'-d={i}', f'-testid={t_id}'] + extra_args
1060+
for i, t_id in enumerate(test_ids)]
1061+
cmds.append([bsim_phy_path, verbosity, suite_id, f'-D={len(test_ids)}', sim_length])
1062+
1063+
return cmds
1064+
1065+
def bsim_run(self, timeout):
1066+
try:
1067+
threads = []
1068+
start_time = time.time()
1069+
for cmd in self._generate_commands():
1070+
t = threading.Thread(target=lambda c=cmd: self._run_cmd(c, timeout))
1071+
threads.append(t)
1072+
t.start()
1073+
1074+
for t in threads:
1075+
t.join(timeout=timeout)
1076+
1077+
self.instance.execution_time = time.time() - start_time
1078+
finally:
1079+
self._update_test_status()
1080+
1081+
def _update_test_status(self):
1082+
if not self.instance.testcases:
1083+
self.instance.init_cases()
1084+
1085+
# currently there is alays one testcase per bsim suite
1086+
self.instance.testcases[0].status = self.status if self.status != TwisterStatus.NONE else \
1087+
TwisterStatus.FAIL
1088+
self.instance.status = self.instance.testcases[0].status
1089+
if self.instance.status in [TwisterStatus.ERROR, TwisterStatus.FAIL]:
1090+
logger.warning(f'BSIM test failed: {self.instance.reason}')
1091+
self.instance.reason = self.instance.reason or 'Bsim test failed'
1092+
self.instance.testcases[0].output = '\n'.join(self._tc_output)
9941093

995-
new_exe_path: str = os.path.join(bsim_out_path, 'bin', new_exe_name)
996-
logger.debug(f'Copying executable from {original_exe_path} to {new_exe_path}')
997-
shutil.copy(original_exe_path, new_exe_path)
9981094

9991095
class Ctest(Harness):
10001096
def configure(self, instance: TestInstance):

scripts/pylib/twister/twisterlib/runner.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
from domains import Domains
4646
from twisterlib.coverage import run_coverage_instance
4747
from twisterlib.environment import TwisterEnv
48-
from twisterlib.harness import Ctest, HarnessImporter, Pytest
48+
from twisterlib.harness import Ctest, HarnessImporter, Pytest, Bsim
4949
from twisterlib.log_helper import log_command
5050
from twisterlib.platform import Platform
5151
from twisterlib.testinstance import TestInstance
@@ -1778,6 +1778,8 @@ def run(self):
17781778
harness.pytest_run(instance.handler.get_test_timeout())
17791779
elif isinstance(harness, Ctest):
17801780
harness.ctest_run(instance.handler.get_test_timeout())
1781+
elif isinstance(harness, Bsim):
1782+
harness.bsim_run(instance.handler.get_test_timeout())
17811783
else:
17821784
instance.handler.handle(harness)
17831785

scripts/pylib/twister/twisterlib/testinstance.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,8 @@ def testsuite_runnable(testsuite, fixtures):
226226
'gtest',
227227
'robot',
228228
'ctest',
229-
'shell'
229+
'shell',
230+
'bsim'
230231
]:
231232
can_run = True
232233
# if we have a fixture that is also being supplied on the

scripts/schemas/twister/testsuite-schema.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,22 @@ schema;scenario-schema:
180180
"bsim_exe_name":
181181
type: str
182182
required: false
183+
"bsim_test_ids":
184+
type: seq
185+
required: false
186+
sequence:
187+
- type: str
188+
"bsim_verbosity":
189+
type: int
190+
required: false
191+
"bsim_sim_length":
192+
type: float
193+
required: false
194+
"bsim_options":
195+
type: seq
196+
required: false
197+
sequence:
198+
- type: str
183199
"min_ram":
184200
type: int
185201
required: false

0 commit comments

Comments
 (0)