From 8e9f832d8b920044d2538471a9e3ccc10484b5c3 Mon Sep 17 00:00:00 2001 From: Pavel Cisar Date: Thu, 9 May 2024 16:23:00 +0200 Subject: [PATCH] New existing_db_factory; fixed errors; release 1.20.0 --- CHANGELOG.md | 14 ++++++++++ docs/changelog.txt | 11 ++++++++ docs/reference.txt | 4 +++ docs/requirements.txt | 1 + pyproject.toml | 4 +-- src/firebird/qa/__about__.py | 2 +- src/firebird/qa/__init__.py | 2 +- src/firebird/qa/plugin.py | 50 ++++++++++++++++++++++++++++++------ 8 files changed, 76 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 88042797..8af7ed68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,20 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [0.20.0] - 2024-05-09 + +### Added + +- Fixture `existing_db_factory` to directly use database from `databases` subdirectory. + It's not intended for use in Firebird QA, but it's necessary for other plugin + users. + +### Fixed + +- Report test error also in cases when unexpected stderr is returned from tool execution + while `returncode` is zero. +- Select test marked for current platform also when it's not marked for Firebird version. + ## [0.19.3] - 2024-03-21 ### Fixed diff --git a/docs/changelog.txt b/docs/changelog.txt index 17554ddd..afcb196f 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -4,6 +4,17 @@ Changelog .. currentmodule:: firebird.qa.plugin +Version 0.20.0 +============== + +* New `.existing_db_factory` firxture to directly use database from `databases` subdirectory. + It's not intended for use in Firebird QA, but it's necessary for other plugin + users. +* Fix: Report test error also in cases when unexpected stderr is returned from tool execution + while `returncode` is zero. +* Fix: Select test marked for current platform also when it's not marked for Firebird version. + + Version 0.19.3 ============== diff --git a/docs/reference.txt b/docs/reference.txt index b0b3ef45..b36b0687 100644 --- a/docs/reference.txt +++ b/docs/reference.txt @@ -19,6 +19,10 @@ db_factory ---------- .. autofunction:: db_factory +existing_db_factory +------------------- +.. autofunction:: existing_db_factory + user_factory ------------ .. autofunction:: user_factory diff --git a/docs/requirements.txt b/docs/requirements.txt index b5712db4..980c074c 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,3 +1,4 @@ +Sphinx==7.2.6 sphinx-bootstrap-theme>=0.8.0 sphinx-autodoc-typehints>=1.17.0 . diff --git a/pyproject.toml b/pyproject.toml index f874b6ea..0288846a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,7 +32,7 @@ classifiers = [ "Framework :: Pytest", ] dependencies = [ - "firebird-base>=1.7.2", + "firebird-base>=1.8.0", "firebird-driver~=1.10", "pytest>=7.4", "psutil~=5.9", @@ -89,7 +89,7 @@ python = ["3.8", "3.9", "3.10", "3.11", "3.12"] detached = false platforms = ["linux"] dependencies = [ - "Sphinx>=7.2.6", + "Sphinx==7.2.6", "sphinx-bootstrap-theme>=0.8.1", "sphinx-autodoc-typehints>=1.24.0", "doc2dash>=3.0.0" diff --git a/src/firebird/qa/__about__.py b/src/firebird/qa/__about__.py index b0586911..bf7b2388 100644 --- a/src/firebird/qa/__about__.py +++ b/src/firebird/qa/__about__.py @@ -1,4 +1,4 @@ # SPDX-FileCopyrightText: 2021-present The Firebird Projects # # SPDX-License-Identifier: MIT -__version__ = "0.19.3" +__version__ = "0.20.0" diff --git a/src/firebird/qa/__init__.py b/src/firebird/qa/__init__.py index 69a8209f..6d2af991 100644 --- a/src/firebird/qa/__init__.py +++ b/src/firebird/qa/__init__.py @@ -40,4 +40,4 @@ from .plugin import db_factory, Database, user_factory, User, isql_act, python_act, Action, \ temp_file, temp_files, role_factory, Role, envar_factory, Envar, Mapping, mapping_factory, \ - ServerKeeper, ExecutionError, QA_GLOBALS + ServerKeeper, ExecutionError, QA_GLOBALS, existing_db_factory diff --git a/src/firebird/qa/plugin.py b/src/firebird/qa/plugin.py index 9b1a3cec..9b0d0f54 100644 --- a/src/firebird/qa/plugin.py +++ b/src/firebird/qa/plugin.py @@ -512,6 +512,8 @@ def pytest_collection_modifyitems(session, config, items): item.add_marker(version_skip) else: deselected.append(item) + elif platform_ok: + selected.append(item) items[:] = selected config.hook.pytest_deselected(items=deselected) # Add OUR OWN test metadata to Item @@ -886,6 +888,38 @@ def set_sync_write(self) -> None: with connect_server(_vars_['server']) as srv: srv.database.set_write_mode(database=self.db_path, mode=DbWriteMode.SYNC) +def existing_db_factory(*, filename: str='test.fdb', charset: Optional[str]=None, + user: Optional[str]=None, password: Optional[str]=None, + config_name: str='pytest', utf8filename: bool=False): + """Factory function that returns :doc:`fixture ` providing + the `Database` instance to existing database. + + Arguments: + filename: Test database filename. It's also possible to specify database alias using + '#' as prefix, for example `#employee` means alias `employee`. + The database with this alias must be defined in `databases.conf`. + charset: Default charset for connections. + user: User name used to connect the test database. Default is taken from server configuration. + password: User password used to connect the test database. Default + is taken from server configuration. + config_name: Name for database configuration. + utf8filename: Use utf8filename DPB flag. + + .. note:: + + The returned instance must be assigned to module-level variable. Name of this variable + is important, as it's used to reference the fixture in other fixture-factory functions + that use the database, and the test function itself. + """ + + @pytest.fixture + def existing_database_fixture(request: pytest.FixtureRequest) -> Database: + db = Database(_vars_['databases'], filename, user, password, charset, debug=str(request.module), + config_name=config_name, utf8filename=utf8filename) + yield db + + return existing_database_fixture + def db_factory(*, filename: str='test.fdb', init: Optional[str]=None, from_backup: Optional[str]=None, copy_of: Optional[str]=None, page_size: Optional[int]=None, sql_dialect: Optional[int]=None, @@ -1779,7 +1813,7 @@ def execute(self, *, do_not_connect: bool=False, charset: Optional[str]=None, else: result: CompletedProcess = run(params, input=self.script, encoding=io_enc, capture_output=True) - if result.returncode and not bool(self.expected_stderr) and not combine_output: + if (result.returncode or result.stderr) and not bool(self.expected_stderr) and not combine_output: self._node.add_report_section('call', 'ISQL stdout', result.stdout) self._node.add_report_section('call', 'ISQL stderr', result.stderr) raise ExecutionError("Test script execution failed") @@ -1893,7 +1927,7 @@ def gstat(self, *, switches: List[str], charset: Optional[str]=None, if connect_db: params.append(str(self.db.dsn)) result: CompletedProcess = run(params, encoding=io_enc, capture_output=True) - if result.returncode and not bool(self.expected_stderr): + if (result.returncode or result.stderr) and not bool(self.expected_stderr): self._node.add_report_section('call', 'gstat stdout', result.stdout) self._node.add_report_section('call', 'gstat stderr', result.stderr) raise ExecutionError("gstat execution failed") @@ -1957,7 +1991,7 @@ def gsec(self, *, switches: Optional[List[str]]=None, charset: Optional[str]=Non params.extend(['-user', self.db.user, '-password', self.db.password]) result: CompletedProcess = run(params, input=input, encoding=io_enc, capture_output=True) - if result.returncode and not bool(self.expected_stderr): + if (result.returncode or result.stderr) and not bool(self.expected_stderr): self._node.add_report_section('call', 'gsec stdout', result.stdout) self._node.add_report_section('call', 'gsec stderr', result.stderr) raise ExecutionError("gsec execution failed") @@ -2021,7 +2055,7 @@ def gbak(self, *, switches: Optional[List[str]]=None, charset: Optional[str]=Non result: CompletedProcess = run(params, encoding=io_enc, stdout=PIPE, stderr=STDOUT) else: 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 or result.stderr) and not (bool(self.expected_stderr) or combine_output): self._node.add_report_section('call', 'gbak stdout', result.stdout) self._node.add_report_section('call', 'gbak stderr', result.stderr) raise ExecutionError("gbak execution failed") @@ -2084,7 +2118,7 @@ def nbackup(self, *, switches: List[str], charset: Optional[str]=None, result: CompletedProcess = run(params, encoding=io_enc, stdout=PIPE, stderr=STDOUT) else: 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 or result.stderr) and not (bool(self.expected_stderr) or combine_output): self._node.add_report_section('call', 'nbackup stdout', result.stdout) self._node.add_report_section('call', 'nbackup stderr', result.stderr) raise ExecutionError("nbackup execution failed") @@ -2148,7 +2182,7 @@ def gfix(self, *, switches: Optional[List[str]]=None, charset: Optional[str]=Non result: CompletedProcess = run(params, encoding=io_enc, stdout=PIPE, stderr=STDOUT) else: 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 or result.stderr) and not (bool(self.expected_stderr) or combine_output): self._node.add_report_section('call', 'gfix stdout', result.stdout) self._node.add_report_section('call', 'gfix stderr', result.stderr) raise ExecutionError("gfix execution failed") @@ -2226,7 +2260,7 @@ def isql(self, *, switches: Optional[List[str]]=None, charset: Optional[str]=Non else: result: CompletedProcess = run(params, input=input, encoding=io_enc, capture_output=True) - if result.returncode and not (bool(self.expected_stderr) or combine_output): + if (result.returncode or result.stderr) and not (bool(self.expected_stderr) or combine_output): self._node.add_report_section('call', 'ISQL stdout', result.stdout) self._node.add_report_section('call', 'ISQL stderr', result.stderr) raise ExecutionError("ISQL execution failed") @@ -2289,7 +2323,7 @@ def svcmgr(self, *, switches: Optional[List[str]]=None, charset: Optional[str]=N if switches is not None: params.extend(switches) result: CompletedProcess = run(params, encoding=io_enc, capture_output=True) - if result.returncode and not bool(self.expected_stderr): + if (result.returncode or result.stderr) and not bool(self.expected_stderr): self._node.add_report_section('call', 'fbsvcmgr stdout', result.stdout) self._node.add_report_section('call', 'fbsvcmgr stderr', result.stderr) raise ExecutionError("fbsvcmgr execution failed")