blob: 4c5a60e5decc87fb219d34bbac1b922c97018232 [file] [log] [blame]
#!/usr/bin/env python3
# Copyright (c) 2024 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
"""
Blackbox tests for twister's command line functions changing test output.
"""
import importlib
import re
import mock
import os
import pytest
import sys
import json
from conftest import ZEPHYR_BASE, TEST_DATA, testsuite_filename_mock, clear_log_in_test
from twisterlib.testplan import TestPlan
@mock.patch.object(TestPlan, 'TESTSUITE_FILENAME', testsuite_filename_mock)
class TestOutput:
TESTDATA_1 = [
(
os.path.join(TEST_DATA, 'tests', 'dummy', 'agnostic')
),
]
@classmethod
def setup_class(cls):
apath = os.path.join(ZEPHYR_BASE, 'scripts', 'twister')
cls.loader = importlib.machinery.SourceFileLoader('__main__', apath)
cls.spec = importlib.util.spec_from_loader(cls.loader.name, cls.loader)
cls.twister_module = importlib.util.module_from_spec(cls.spec)
@classmethod
def teardown_class(cls):
pass
@pytest.mark.parametrize(
'flag, expect_paths',
[
('--no-detailed-test-id', False),
('--detailed-test-id', True)
],
ids=['no-detailed-test-id', 'detailed-test-id']
)
def test_detailed_test_id(self, out_path, flag, expect_paths):
test_platforms = ['qemu_x86', 'frdm_k64f']
path = os.path.join(TEST_DATA, 'tests', 'dummy')
args = ['-i', '--outdir', out_path, '-T', path, '-y'] + \
[flag] + \
[val for pair in zip(
['-p'] * len(test_platforms), test_platforms
) for val in pair]
with mock.patch.object(sys, 'argv', [sys.argv[0]] + args), \
pytest.raises(SystemExit) as sys_exit:
self.loader.exec_module(self.twister_module)
assert str(sys_exit.value) == '0'
with open(os.path.join(out_path, 'testplan.json')) as f:
j = json.load(f)
filtered_j = [
(ts['platform'], ts['name'], tc['identifier']) \
for ts in j['testsuites'] \
for tc in ts['testcases'] if 'reason' not in tc
]
assert len(filtered_j) > 0, "No dummy tests found."
expected_start = os.path.relpath(TEST_DATA, ZEPHYR_BASE) if expect_paths else 'dummy.'
assert all([testsuite.startswith(expected_start)for _, testsuite, _ in filtered_j])
def test_inline_logs(self, out_path):
test_platforms = ['qemu_x86', 'frdm_k64f']
path = os.path.join(TEST_DATA, 'tests', 'always_build_error', 'dummy')
args = ['--outdir', out_path, '-T', path] + \
[val for pair in zip(
['-p'] * len(test_platforms), test_platforms
) for val in pair]
with mock.patch.object(sys, 'argv', [sys.argv[0]] + args), \
pytest.raises(SystemExit) as sys_exit:
self.loader.exec_module(self.twister_module)
assert str(sys_exit.value) == '1'
rel_path = os.path.relpath(path, ZEPHYR_BASE)
build_path = os.path.join(out_path, 'qemu_x86', rel_path, 'always_fail.dummy', 'build.log')
with open(build_path) as f:
build_log = f.read()
clear_log_in_test()
args = ['--outdir', out_path, '-T', path] + \
['--inline-logs'] + \
[val for pair in zip(
['-p'] * len(test_platforms), test_platforms
) for val in pair]
with mock.patch.object(sys, 'argv', [sys.argv[0]] + args), \
pytest.raises(SystemExit) as sys_exit:
self.loader.exec_module(self.twister_module)
assert str(sys_exit.value) == '1'
with open(os.path.join(out_path, 'twister.log')) as f:
inline_twister_log = f.read()
# Remove information that differs between the runs
removal_patterns = [
# Remove tmp filepaths, as they will differ
r'(/|\\)tmp(/|\\)\S+',
# Remove object creation order, as it can change
r'^\[[0-9]+/[0-9]+\] ',
# Remove variable CMake flag
r'-DTC_RUNID=[0-9a-zA-Z]+',
# Remove variable order CMake flags
r'-I[0-9a-zA-Z/\\]+',
# Cache location may vary between CI runs
r'^.*-- Cache files will be written to:.*$'
]
for pattern in removal_patterns:
inline_twister_log = re.sub(pattern, '', inline_twister_log, flags=re.MULTILINE)
build_log = re.sub(pattern, '', build_log, flags=re.MULTILINE)
split_build_log = build_log.split('\n')
for r in split_build_log:
assert r in inline_twister_log
def _get_matches(self, err, regex_line):
matches = []
for line in err.split('\n'):
columns = line.split()
if len(columns) == 8:
for i in range(8):
match = re.fullmatch(regex_line[i], columns[i])
if match:
matches.append(match)
if len(matches) == 8:
return matches
else:
matches = []
return matches
@pytest.mark.parametrize(
'test_path',
TESTDATA_1,
ids=[
'single_v',
]
)
def test_single_v(self, capfd, out_path, test_path):
args = ['--outdir', out_path, '-T', test_path, '-v']
with mock.patch.object(sys, 'argv', [sys.argv[0]] + args), \
pytest.raises(SystemExit) as sys_exit:
self.loader.exec_module(self.twister_module)
out, err = capfd.readouterr()
sys.stdout.write(out)
sys.stderr.write(err)
regex_line = [r'INFO', r'-', r'\d+/\d+', r'\S+', r'\S+', r'[A-Z]+', r'\(\w+', r'[\d.]+s\)']
matches = self._get_matches(err, regex_line)
print(matches)
assert str(sys_exit.value) == '0'
assert len(matches) > 0
@pytest.mark.parametrize(
'test_path',
TESTDATA_1,
ids=[
'double_v',
]
)
def test_double_v(self, capfd, out_path, test_path):
args = ['--outdir', out_path, '-T', test_path, '-vv']
with mock.patch.object(sys, 'argv', [sys.argv[0]] + args), \
pytest.raises(SystemExit) as sys_exit:
self.loader.exec_module(self.twister_module)
out, err = capfd.readouterr()
sys.stdout.write(out)
sys.stderr.write(err)
regex_line = [r'INFO', r'-', r'\d+/\d+', r'\S+', r'\S+', r'[A-Z]+', r'\(\w+', r'[\d.]+s\)']
matches = self._get_matches(err, regex_line)
booting_zephyr_regex = re.compile(r'^DEBUG\s+-\s+([^*]+)\*\*\*\s+Booting\s+Zephyr\s+OS\s+build.*$', re.MULTILINE)
info_debug_line_regex = r'^\s*(INFO|DEBUG)'
assert str(sys_exit.value) == '0'
assert re.search(booting_zephyr_regex, err) is not None
assert re.search(info_debug_line_regex, err) is not None
assert len(matches) > 0