6
0
mirror of https://github.com/FirebirdSQL/firebird-qa.git synced 2025-01-22 13:33:07 +01:00

Plugin v0.9.1

This commit is contained in:
Pavel Císař 2021-12-22 20:20:44 +01:00
parent 75419c92a2
commit ec75b37214
3 changed files with 83 additions and 41 deletions

View File

@ -41,6 +41,7 @@ from typing import Dict, List, Tuple
import os
from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
from pathlib import Path
from operator import attrgetter
from packaging.version import Version, parse
PROG_NAME = 'fbt-convert'
@ -69,6 +70,12 @@ tests = []
slow_tests = ['bugs.core_1544', 'bugs.core_3058']
def clean_last(txt: str) -> str:
if not txt:
return txt
l = txt.splitlines()
l[-1] = l[-1].strip()
return '\n'.join(l)
class TestVersion:
def __init__(self, id, platform, firebird_version, test_type,
@ -82,10 +89,10 @@ class TestVersion:
self.platform: str = platform
self.firebird_version: Version = parse(firebird_version)
self.test_type: str = test_type
self.test_script: str = test_script
self.test_script: str = clean_last(test_script)
self.database: str = database
self.expected_stdout: str = '' if expected_stdout.strip() == '' else expected_stdout
self.expected_stderr: str = '' if expected_stderr.strip() == '' else expected_stderr
self.expected_stdout: str = clean_last('' if expected_stdout.strip() == '' else expected_stdout)
self.expected_stderr: str = clean_last('' if expected_stderr.strip() == '' else expected_stderr)
self.database_name: str = database_name
self.backup_file: str = backup_file
self.user_name: str = user_name
@ -98,6 +105,7 @@ class TestVersion:
self.resources: List[str] = None if resources is None else list(resources)
self.substitutions: List[str] = substitutions if substitutions is not None else []
self.qmid: str = qmid
# Clean
def escape(self, subs: List[Tuple[str, str]]) -> List[Tuple[str, str]]:
return [tuple([a.replace('\\', '\\\\'), b.replace('\\', '\\\\')]) for a, b in subs]
@ -117,6 +125,7 @@ class Test:
if versions:
for i in versions:
self.versions.append(TestVersion(id, **i))
self.versions.sort(key=attrgetter('firebird_version'))
def show(self):
for attr in (a for a in dir(self) if not a.startswith('_')):
if attr not in ('show'):
@ -168,6 +177,7 @@ def load_tests(path: Path, verbose: bool=False):
def clean_tests():
v30: Version = parse('3.0')
v40: Version = parse('4.0')
for t in tests:
new_versions = []
last: Version = parse('0.1')
@ -179,15 +189,15 @@ def clean_tests():
if mv > v.firebird_version:
v.firebird_version = mv
#
if last < v.firebird_version:
if (last < v.firebird_version) and (v.firebird_version < v30):
last = v.firebird_version
if v.firebird_version >= v30:
has_30 = True
has_30 = has_30 or v.firebird_version < v40
new_versions.append(v)
if not has_30:
for v in t.versions:
if v.firebird_version >= last:
new_versions.append(v)
if v.firebird_version == last:
new_versions.insert(0, v)
t.versions[:] = new_versions
def list_tests(root_path: Path, verbose: bool=False):
@ -233,7 +243,7 @@ def write_tests(root_path: Path, verbose: bool=False):
# qmid: {t.qmid}
import pytest
from firebird.qa import db_factory, isql_act, Action
from firebird.qa import db_factory, {'isql_act' if t.versions[0].test_type == TYPE_ISQL else 'python_act'}, Action
"""
# verbose output
@ -292,25 +302,34 @@ from firebird.qa import db_factory, isql_act, Action
content += f' act_{seq}.expected_stderr = expected_stderr_{seq}\n'
content += f' act_{seq}.execute()\n'
if v.expected_stderr:
content += f' assert act_{seq}.clean_expected_stderr == act_{seq}.clean_stderr\n'
content += f' assert act_{seq}.clean_stderr == act_{seq}.clean_expected_stderr\n'
if v.expected_stdout:
content += f' assert act_{seq}.clean_expected_stdout == act_{seq}.clean_stdout\n'
if v.expected_stderr:
content += '\n'
content += f' assert act_{seq}.clean_stdout == act_{seq}.clean_expected_stdout\n'
content += '\n'
elif v.test_type == TYPE_PYTHON:
#
content += f'''# test_script_{seq}\n#---\n# {multiline_comment(escape(v.test_script), 2)}\n#---\n'''
content += f"#act_{seq} = python_act('db_{seq}', test_script_{seq}, substitutions=substitutions_{seq})\n\n"
if v.expected_stdout:
sep = "'''" if v.expected_stdout.startswith('"') or v.expected_stdout.endswith('"') else '"""'
content += f'expected_stdout_{seq} = {sep}{escape(v.expected_stdout)}{sep}\n'
content += f"act_{seq} = python_act('db_{seq}', substitutions=substitutions_{seq})\n\n"
if v.expected_stderr:
sep = "'''" if v.expected_stderr.startswith('"') or v.expected_stderr.endswith('"') else '"""'
content += f'expected_stderr_{seq} = {sep}{escape(v.expected_stderr)}{sep}\n'
content += f"""\n@pytest.mark.version('>={str(v.firebird_version)}')\n"""
if v.expected_stdout:
if v.expected_stderr:
content += '\n'
sep = "'''" if v.expected_stdout.startswith('"') or v.expected_stdout.endswith('"') else '"""'
content += f'expected_stdout_{seq} = {sep}{escape(v.expected_stdout)}{sep}\n'
# Version specification
if seq < len(t.versions):
ver_spec = f'>={str(v.firebird_version)},<{str(t.versions[seq].firebird_version)}'
else:
ver_spec = f'>={str(v.firebird_version)}'
content += f"""\n@pytest.mark.version('{ver_spec}')\n"""
if v.platform != 'All':
content += f"""@pytest.mark.platform({", ".join([f"'{i}'" for i in v.platform.split(':')])})\n"""
content += "@pytest.mark.xfail\n"
content += f"""def test_{seq}(db_{seq}):\n pytest.fail("Test not IMPLEMENTED")\n\n"""
#content += "@pytest.mark.xfail\n"
content += f"""def test_{seq}(act_{seq}: Action):\n pytest.fail("Test not IMPLEMENTED")\n\n"""
content += '\n'
#

View File

@ -47,7 +47,7 @@ import pytest
from _pytest.fixtures import FixtureRequest
from subprocess import run, CompletedProcess, PIPE, STDOUT
from pathlib import Path
#from configparser import ConfigParser, ExtendedInterpolation
from configparser import ConfigParser, ExtendedInterpolation
from packaging.specifiers import SpecifierSet
from packaging.version import parse
import time
@ -55,7 +55,7 @@ from threading import Thread, Barrier
from firebird.driver import connect, connect_server, create_database, driver_config, \
NetProtocol, Server, CHARSET_MAP, Connection, Cursor, \
DESCRIPTION_NAME, DESCRIPTION_DISPLAY_SIZE, DatabaseConfig, DBKeyScope, DbInfoCode, \
DbWriteMode, DatabaseError
DbWriteMode, get_api
from firebird.driver.core import _connect_helper
_vars_ = {'server': None,
@ -90,6 +90,7 @@ def pytest_report_header(config):
f" home: {_vars_['home-dir']}",
f" bin: {_vars_['bin-dir']}",
f" security db: {_vars_['security-db']}",
f" client library: {_vars_['fbclient']}",
f" run slow tests: {_vars_['runslow']}",
f" save test output: {_vars_['save-output']}",
]
@ -142,7 +143,18 @@ def pytest_configure(config):
db_conf = driver_config.get_database('employee')
db_conf.server.value = _vars_['server']
db_conf.database.value = 'employee.fdb'
#
# Handle server-specific "fb_client_library" configuration option
_vars_['fbclient'] = 'UNKNOWN'
cfg = ConfigParser(interpolation=ExtendedInterpolation())
cfg.read(str(config_path))
if cfg.has_option(_vars_['server'], 'fb_client_library'):
fbclient = Path(cfg.get(_vars_['server'], 'fb_client_library'))
if not fbclient.is_file():
pytest.exit(f"Client library '{fbclient}' not found!")
driver_config.fb_client_library.value = str(fbclient)
cfg.clear()
# THIS should load the driver API, do not connect db or server earlier!
_vars_['fbclient'] = get_api().client_library_name
with connect_server(_vars_['server'], user='SYSDBA',
password=_vars_['password']) as srv:
_vars_['version'] = parse(srv.info.version.replace('-dev', ''))
@ -187,16 +199,15 @@ def pytest_collection_modifyitems(session, config, items):
items[:] = selected
config.hook.pytest_deselected(items=deselected)
#@pytest.fixture(autouse=True)
#def firebird_server():
#try:
#with connect_server(_vars_['server']) as srv:
#yield srv
#except DatabaseError as e:
#print('ERROR WHILE DETACH FROM SERVER:', e)
## Shield from crashing server
#if 'Error reading data from the connection' not in str(e):
#raise
#@pytest.hookimpl(hookwrapper=True)
#def pytest_runtest_makereport(item, call):
#outcome = yield
#report = outcome.get_result()
#test_fn = item.obj
#docstring = getattr(test_fn, '__doc__')
#if docstring:
#report.nodeid = docstring
def substitute_macros(text: str, macros: Dict[str, str]):
f_text = text
@ -208,7 +219,8 @@ def substitute_macros(text: str, macros: Dict[str, str]):
class Database:
""
def __init__(self, path: Path, filename: str='test.fdb',
user: str=None, password: str=None, charset: str=None):
user: str=None, password: str=None, charset: str=None, debug: str=''):
self._debug: str = debug
self.db_path: Path = path / filename
self.dsn: str = None
self.charset: str = 'NONE' if charset is None else charset.upper()
@ -298,19 +310,30 @@ class Database:
with connect_server(_vars_['server']) as srv:
srv.database.no_linger(database=self.db_path)
self._make_config()
db = connect('pytest')
with connect('pytest') as db:
db._att._name = self._debug
try:
db.execute_immediate('delete from mon$attachments where mon$attachment_id != current_connection')
db.commit()
except:
pass
#print(f"Removing db: {self.db_path}")
try:
db.drop_database()
except:
pass
if self.db_path.is_file():
self.db_path.unlink(missing_ok=True)
def connect(self, *, user: str=None, password: str=None, role: str=None, no_gc: bool=None,
no_db_triggers: bool=None, dbkey_scope: DBKeyScope=None,
session_time_zone: str=None, charset: str=None, sql_dialect: int=None,
auth_plugin_list: str=None) -> Connection:
self._make_config(user=user, password=password, charset=charset, sql_dialect=sql_dialect)
return connect('pytest', role=role, no_gc=no_gc, no_db_triggers=no_db_triggers,
result = connect('pytest', role=role, no_gc=no_gc, no_db_triggers=no_db_triggers,
dbkey_scope=dbkey_scope, session_time_zone=session_time_zone,
auth_plugin_list=auth_plugin_list)
result._att._name = self._debug
return result
def set_async_write(self) -> None:
with connect_server(_vars_['server']) as srv:
srv.database.set_write_mode(database=self.db_path, mode=DbWriteMode.ASYNC)
@ -325,7 +348,7 @@ def db_factory(*, filename: str='test.fdb', init: str=None, from_backup: str=Non
@pytest.fixture
def database_fixture(request: FixtureRequest, db_path) -> Database:
db = Database(db_path, filename, user, password, charset)
db = Database(db_path, filename, user, password, charset, debug=str(request.module))
if not do_not_create:
if from_backup is None and copy_of is None:
db.create(page_size, sql_dialect)

View File

@ -5,7 +5,7 @@ all-files=True
[metadata]
name = firebird-qa
version = 0.9.0
version = 0.9.1
description = pytest plugin for Firebird QA
long_description = file: README.rst
long_description_content_type = text/x-rst; charset=UTF-8
@ -38,7 +38,7 @@ zip_safe = True
python_requires = >=3.8, <4
install_requires =
firebird-base>=1.3.0
firebird-driver>=1.4.0
firebird-driver>=1.4.1
pytest>=6.2.5
packages = find_namespace: