mirror of
https://github.com/FirebirdSQL/firebird-qa.git
synced 2025-01-22 21:43:06 +01:00
Better handling of captured stdout/stderr on external utility execution failure. Metadata for JIRA ticket ID.
This commit is contained in:
parent
63b28f9dd4
commit
c3ddfee00b
@ -37,4 +37,5 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from .plugin import db_factory, Database, user_factory, User, isql_act, python_act, Action, \
|
from .plugin import db_factory, Database, user_factory, User, isql_act, python_act, Action, \
|
||||||
temp_file, temp_files, role_factory, Role, envar_factory, Envar, ServerKeeper
|
temp_file, temp_files, role_factory, Role, envar_factory, Envar, ServerKeeper, \
|
||||||
|
ExecutionError
|
||||||
|
@ -75,6 +75,10 @@ FIELD_ISSUE = 'ISSUE:'
|
|||||||
FIELD_TITLE = 'TITLE:'
|
FIELD_TITLE = 'TITLE:'
|
||||||
FIELD_DECRIPTION = 'DESCRIPTION:'
|
FIELD_DECRIPTION = 'DESCRIPTION:'
|
||||||
FIELD_NOTES = 'NOTES:'
|
FIELD_NOTES = 'NOTES:'
|
||||||
|
FIELD_JIRA = 'JIRA:'
|
||||||
|
|
||||||
|
class ExecutionError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
def format_title(title, patterns):
|
def format_title(title, patterns):
|
||||||
return _remove_patterns(title, patterns).replace('_', ' ').strip()
|
return _remove_patterns(title, patterns).replace('_', ' ').strip()
|
||||||
@ -385,6 +389,7 @@ def pytest_collection_modifyitems(session, config, items):
|
|||||||
for item in items:
|
for item in items:
|
||||||
item._qa_id_ = item.nodeid
|
item._qa_id_ = item.nodeid
|
||||||
item._qa_issue_ = None
|
item._qa_issue_ = None
|
||||||
|
item._qa_jira_ = None
|
||||||
item._qa_title_ = 'UNKNOWN'
|
item._qa_title_ = 'UNKNOWN'
|
||||||
item._qa_description_ = ''
|
item._qa_description_ = ''
|
||||||
item._qa_notes_ = ''
|
item._qa_notes_ = ''
|
||||||
@ -401,6 +406,9 @@ def pytest_collection_modifyitems(session, config, items):
|
|||||||
elif uline.startswith(FIELD_ISSUE):
|
elif uline.startswith(FIELD_ISSUE):
|
||||||
current_field = FIELD_ISSUE
|
current_field = FIELD_ISSUE
|
||||||
item._qa_issue_ = line[len(FIELD_ISSUE):].strip()
|
item._qa_issue_ = line[len(FIELD_ISSUE):].strip()
|
||||||
|
elif uline.startswith(FIELD_JIRA):
|
||||||
|
current_field = FIELD_JIRA
|
||||||
|
item._qa_jira_ = line[len(FIELD_JIRA):].strip()
|
||||||
elif uline.startswith(FIELD_TITLE):
|
elif uline.startswith(FIELD_TITLE):
|
||||||
current_field = FIELD_TITLE
|
current_field = FIELD_TITLE
|
||||||
item._qa_title_ = line[len(FIELD_TITLE):].strip()
|
item._qa_title_ = line[len(FIELD_TITLE):].strip()
|
||||||
@ -1232,7 +1240,8 @@ class Action:
|
|||||||
Do not create instances of this class directly! Use **only** fixtures created by
|
Do not create instances of this class directly! Use **only** fixtures created by
|
||||||
`isql_act` or `python_act`.
|
`isql_act` or `python_act`.
|
||||||
"""
|
"""
|
||||||
def __init__(self, db: Database, script: str, substitutions: List[str], outfile: Path):
|
def __init__(self, db: Database, script: str, substitutions: List[str], outfile: Path, node: pytest.Item):
|
||||||
|
self._node: pytest.Item = node
|
||||||
#: Primary test database.
|
#: Primary test database.
|
||||||
self.db: Database = db
|
self.db: Database = db
|
||||||
#: Test script
|
#: Test script
|
||||||
@ -1321,11 +1330,9 @@ class Action:
|
|||||||
result: CompletedProcess = run(params, input=self.script,
|
result: CompletedProcess = run(params, input=self.script,
|
||||||
encoding=io_enc, capture_output=True)
|
encoding=io_enc, capture_output=True)
|
||||||
if result.returncode and not bool(self.expected_stderr) and not combine_output:
|
if result.returncode and not bool(self.expected_stderr) and not combine_output:
|
||||||
print(f"-- ISQL script stdout {'-' * 20}")
|
self._node.add_report_section('call', 'ISQL stdout', result.stdout)
|
||||||
print(result.stdout)
|
self._node.add_report_section('call', 'ISQL stderr', result.stderr)
|
||||||
print(f"-- ISQL script stderr {'-' * 20}")
|
raise ExecutionError("ISQL script execution failed")
|
||||||
print(result.stderr)
|
|
||||||
raise Exception("ISQL script execution failed")
|
|
||||||
|
|
||||||
self.return_code: int = result.returncode
|
self.return_code: int = result.returncode
|
||||||
self.stdout: str = result.stdout
|
self.stdout: str = result.stdout
|
||||||
@ -1372,11 +1379,9 @@ class Action:
|
|||||||
'-password', db.password, str(db.dsn)],
|
'-password', db.password, str(db.dsn)],
|
||||||
encoding=io_enc, capture_output=True)
|
encoding=io_enc, capture_output=True)
|
||||||
if result.returncode:
|
if result.returncode:
|
||||||
print(f"-- ISQL stdout {'-' * 20}")
|
self._node.add_report_section('call', 'ISQL stdout', result.stdout)
|
||||||
print(result.stdout)
|
self._node.add_report_section('call', 'ISQL stderr', result.stderr)
|
||||||
print(f"-- ISQL stderr {'-' * 20}")
|
raise ExecutionError("ISQL execution failed")
|
||||||
print(result.stderr)
|
|
||||||
raise Exception("ISQL execution failed")
|
|
||||||
self.return_code: int = result.returncode
|
self.return_code: int = result.returncode
|
||||||
self.stdout: str = result.stdout
|
self.stdout: str = result.stdout
|
||||||
self.stderr: str = result.stderr
|
self.stderr: str = result.stderr
|
||||||
@ -1434,11 +1439,9 @@ class Action:
|
|||||||
params.append(str(self.db.dsn))
|
params.append(str(self.db.dsn))
|
||||||
result: CompletedProcess = run(params, encoding=io_enc, capture_output=True)
|
result: CompletedProcess = run(params, encoding=io_enc, capture_output=True)
|
||||||
if result.returncode and not bool(self.expected_stderr):
|
if result.returncode and not bool(self.expected_stderr):
|
||||||
print(f"-- gstat stdout {'-' * 20}")
|
self._node.add_report_section('call', 'gstat stdout', result.stdout)
|
||||||
print(result.stdout)
|
self._node.add_report_section('call', 'gstat stderr', result.stderr)
|
||||||
print(f"-- gstat stderr {'-' * 20}")
|
raise ExecutionError("gstat execution failed")
|
||||||
print(result.stderr)
|
|
||||||
raise Exception("gstat execution failed")
|
|
||||||
self.return_code: int = result.returncode
|
self.return_code: int = result.returncode
|
||||||
self.stdout: str = result.stdout
|
self.stdout: str = result.stdout
|
||||||
self.stderr: str = result.stderr
|
self.stderr: str = result.stderr
|
||||||
@ -1495,11 +1498,9 @@ class Action:
|
|||||||
result: CompletedProcess = run(params, input=input,
|
result: CompletedProcess = run(params, input=input,
|
||||||
encoding=io_enc, capture_output=True)
|
encoding=io_enc, capture_output=True)
|
||||||
if result.returncode and not bool(self.expected_stderr):
|
if result.returncode and not bool(self.expected_stderr):
|
||||||
print(f"-- gsec stdout {'-' * 20}")
|
self._node.add_report_section('call', 'gsec stdout', result.stdout)
|
||||||
print(result.stdout)
|
self._node.add_report_section('call', 'gsec stderr', result.stderr)
|
||||||
print(f"-- gsec stderr {'-' * 20}")
|
raise ExecutionError("gsec execution failed")
|
||||||
print(result.stderr)
|
|
||||||
raise Exception("gsec execution failed")
|
|
||||||
self.return_code: int = result.returncode
|
self.return_code: int = result.returncode
|
||||||
self.stdout: str = result.stdout
|
self.stdout: str = result.stdout
|
||||||
self.stderr: str = result.stderr
|
self.stderr: str = result.stderr
|
||||||
@ -1555,11 +1556,9 @@ class Action:
|
|||||||
result: CompletedProcess = run(params, input=input,
|
result: CompletedProcess = run(params, input=input,
|
||||||
encoding=io_enc, capture_output=True)
|
encoding=io_enc, capture_output=True)
|
||||||
if result.returncode and not (bool(self.expected_stderr) or combine_output):
|
if result.returncode and not (bool(self.expected_stderr) or combine_output):
|
||||||
print(f"-- gbak stdout {'-' * 20}")
|
self._node.add_report_section('call', 'gbak stdout', result.stdout)
|
||||||
print(result.stdout)
|
self._node.add_report_section('call', 'gbak stderr', result.stderr)
|
||||||
print(f"-- gbak stderr {'-' * 20}")
|
raise ExecutionError("gbak execution failed")
|
||||||
print(result.stderr)
|
|
||||||
raise Exception("gbak execution failed")
|
|
||||||
self.return_code: int = result.returncode
|
self.return_code: int = result.returncode
|
||||||
self.stdout: str = result.stdout
|
self.stdout: str = result.stdout
|
||||||
self.stderr: str = result.stderr
|
self.stderr: str = result.stderr
|
||||||
@ -1612,11 +1611,9 @@ class Action:
|
|||||||
else:
|
else:
|
||||||
result: CompletedProcess = run(params, encoding=io_enc, capture_output=True)
|
result: CompletedProcess = run(params, encoding=io_enc, capture_output=True)
|
||||||
if result.returncode and not (bool(self.expected_stderr) or combine_output):
|
if result.returncode and not (bool(self.expected_stderr) or combine_output):
|
||||||
print(f"-- nbackup stdout {'-' * 20}")
|
self._node.add_report_section('call', 'nbackup stdout', result.stdout)
|
||||||
print(result.stdout)
|
self._node.add_report_section('call', 'nbackup stderr', result.stderr)
|
||||||
print(f"-- nbackup stderr {'-' * 20}")
|
raise ExecutionError("nbackup execution failed")
|
||||||
print(result.stderr)
|
|
||||||
raise Exception("nbackup execution failed")
|
|
||||||
self.return_code: int = result.returncode
|
self.return_code: int = result.returncode
|
||||||
self.stdout: str = result.stdout
|
self.stdout: str = result.stdout
|
||||||
self.stderr: str = result.stderr
|
self.stderr: str = result.stderr
|
||||||
@ -1668,11 +1665,9 @@ class Action:
|
|||||||
result: CompletedProcess = run(params, input=input,
|
result: CompletedProcess = run(params, input=input,
|
||||||
encoding=io_enc, capture_output=True)
|
encoding=io_enc, capture_output=True)
|
||||||
if result.returncode and not bool(self.expected_stderr):
|
if result.returncode and not bool(self.expected_stderr):
|
||||||
print(f"-- gfix stdout {'-' * 20}")
|
self._node.add_report_section('call', 'gfix stdout', result.stdout)
|
||||||
print(result.stdout)
|
self._node.add_report_section('call', 'gfix stderr', result.stderr)
|
||||||
print(f"-- gfix stderr {'-' * 20}")
|
raise ExecutionError("gfix execution failed")
|
||||||
print(result.stderr)
|
|
||||||
raise Exception("gfix execution failed")
|
|
||||||
self.return_code: int = result.returncode
|
self.return_code: int = result.returncode
|
||||||
self.stdout: str = result.stdout
|
self.stdout: str = result.stdout
|
||||||
self.stderr: str = result.stderr
|
self.stderr: str = result.stderr
|
||||||
@ -1733,11 +1728,9 @@ class Action:
|
|||||||
result: CompletedProcess = run(params, input=input,
|
result: CompletedProcess = run(params, input=input,
|
||||||
encoding=io_enc, capture_output=True)
|
encoding=io_enc, capture_output=True)
|
||||||
if result.returncode and not (bool(self.expected_stderr) or combine_output):
|
if result.returncode and not (bool(self.expected_stderr) or combine_output):
|
||||||
print(f"-- isql stdout {'-' * 20}")
|
self._node.add_report_section('call', 'ISQL stdout', result.stdout)
|
||||||
print(result.stdout)
|
self._node.add_report_section('call', 'ISQL stderr', result.stderr)
|
||||||
print(f"-- isql stderr {'-' * 20}")
|
raise ExecutionError("ISQL execution failed")
|
||||||
print(result.stderr)
|
|
||||||
raise Exception("isql execution failed")
|
|
||||||
self.return_code: int = result.returncode
|
self.return_code: int = result.returncode
|
||||||
self.stdout: str = result.stdout
|
self.stdout: str = result.stdout
|
||||||
self.stderr: str = result.stderr
|
self.stderr: str = result.stderr
|
||||||
@ -1789,11 +1782,9 @@ class Action:
|
|||||||
params.extend(switches)
|
params.extend(switches)
|
||||||
result: CompletedProcess = run(params, encoding=io_enc, capture_output=True)
|
result: CompletedProcess = run(params, encoding=io_enc, capture_output=True)
|
||||||
if result.returncode and not bool(self.expected_stderr):
|
if result.returncode and not bool(self.expected_stderr):
|
||||||
print(f"-- fbsvcmgr stdout {'-' * 20}")
|
self._node.add_report_section('call', 'fbsvcmgr stdout', result.stdout)
|
||||||
print(result.stdout)
|
self._node.add_report_section('call', 'fbsvcmgr stderr', result.stderr)
|
||||||
print(f"-- fbsvcmgr stderr {'-' * 20}")
|
raise ExecutionError("fbsvcmgr execution failed")
|
||||||
print(result.stderr)
|
|
||||||
raise Exception("fbsvcmgr execution failed")
|
|
||||||
self.return_code: int = result.returncode
|
self.return_code: int = result.returncode
|
||||||
self.stdout: str = result.stdout
|
self.stdout: str = result.stdout
|
||||||
self.stderr: str = result.stderr
|
self.stderr: str = result.stderr
|
||||||
@ -1817,29 +1808,7 @@ class Action:
|
|||||||
spec = SpecifierSet(version_spec)
|
spec = SpecifierSet(version_spec)
|
||||||
return _vars_['version'] in spec
|
return _vars_['version'] in spec
|
||||||
def get_server_architecture(self) -> str:
|
def get_server_architecture(self) -> str:
|
||||||
with self.db.connect() as con1, self.db.connect() as con2:
|
return _vars_['server-arch']
|
||||||
sql = f"""
|
|
||||||
select count(distinct a.mon$server_pid), min(a.mon$remote_protocol),
|
|
||||||
max(iif(a.mon$remote_protocol is null, 1, 0))
|
|
||||||
from mon$attachments a
|
|
||||||
where a.mon$attachment_id in ({con1.info.id}, {con2.info.id}) or upper(a.mon$user) = upper('cache writer')
|
|
||||||
"""
|
|
||||||
cur1 = con1.cursor()
|
|
||||||
cur1.execute(sql)
|
|
||||||
server_cnt, server_pro, cache_wrtr = cur1.fetchone()
|
|
||||||
if server_pro is None:
|
|
||||||
result = 'Embedded'
|
|
||||||
elif cache_wrtr == 1:
|
|
||||||
result = 'SS'
|
|
||||||
elif server_cnt == 2:
|
|
||||||
result = 'CS'
|
|
||||||
else:
|
|
||||||
f1 = con1.info.get_info(DbInfoCode.FETCHES)
|
|
||||||
cur2 = con2.cursor()
|
|
||||||
cur2.execute('select 1 from rdb$database').fetchall()
|
|
||||||
f2 = con1.info.get_info(DbInfoCode.FETCHES)
|
|
||||||
result = 'SC' if f1 == f2 else 'SS'
|
|
||||||
return result
|
|
||||||
def get_dsn(self, filename: Union[str, Path], protocol: str=None) -> str:
|
def get_dsn(self, filename: Union[str, Path], protocol: str=None) -> str:
|
||||||
return _connect_helper('', self.host, self.port, str(filename),
|
return _connect_helper('', self.host, self.port, str(filename),
|
||||||
protocol if protocol else self.protocol)
|
protocol if protocol else self.protocol)
|
||||||
@ -1988,7 +1957,7 @@ def isql_act(db_fixture_name: str, script: str, *, substitutions: List[str]=None
|
|||||||
if _vars_['save-output'] and not f.parent.exists():
|
if _vars_['save-output'] and not f.parent.exists():
|
||||||
f.parent.mkdir(parents=True)
|
f.parent.mkdir(parents=True)
|
||||||
f = f.with_name(f'{f.stem}-{request.function.__name__}.out')
|
f = f.with_name(f'{f.stem}-{request.function.__name__}.out')
|
||||||
result: Action = Action(db, script, substitutions, f)
|
result: Action = Action(db, script, substitutions, f, request.node)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
return isql_act_fixture
|
return isql_act_fixture
|
||||||
@ -2002,7 +1971,7 @@ def python_act(db_fixture_name: str, *, substitutions: List[str]=None):
|
|||||||
if _vars_['save-output'] and not f.parent.exists():
|
if _vars_['save-output'] and not f.parent.exists():
|
||||||
f.parent.mkdir(parents=True)
|
f.parent.mkdir(parents=True)
|
||||||
f = f.with_name(f'{f.stem}-{request.function.__name__}.out')
|
f = f.with_name(f'{f.stem}-{request.function.__name__}.out')
|
||||||
result: Action = Action(db, '', substitutions, f)
|
result: Action = Action(db, '', substitutions, f, request.node)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
return python_act_fixture
|
return python_act_fixture
|
||||||
|
@ -5,7 +5,7 @@ all-files=True
|
|||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
name = firebird-qa
|
name = firebird-qa
|
||||||
version = 0.10.0
|
version = 0.10.1
|
||||||
description = pytest plugin for Firebird QA
|
description = pytest plugin for Firebird QA
|
||||||
long_description = file: README.rst
|
long_description = file: README.rst
|
||||||
long_description_content_type = text/x-rst; charset=UTF-8
|
long_description_content_type = text/x-rst; charset=UTF-8
|
||||||
|
Loading…
Reference in New Issue
Block a user