diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 00000000..d4bb2cbb --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/_static/basic.css b/docs/_static/basic.css new file mode 100644 index 00000000..ed50512c --- /dev/null +++ b/docs/_static/basic.css @@ -0,0 +1,643 @@ +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox input[type="text"] { + float: left; + width: 80%; + padding: 0.25em; + box-sizing: border-box; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + float: left; + width: 20%; + border-left: none; + padding: 0.25em; + box-sizing: border-box; +} + + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li div.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body { + min-width: 450px; + max-width: 1920px; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px 7px 0 7px; + background-color: #ffe; + width: 40%; + float: right; +} + +p.sidebar-title { + font-weight: bold; +} + +/* -- topics ---------------------------------------------------------------- */ + +div.topic { + border: 1px solid #ccc; + padding: 7px 7px 0 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +div.admonition dl { + margin-bottom: 0; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; +} + +td.linenos pre { + padding: 5px 0px; + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + margin-left: 0.5em; +} + +table.highlighttable td { + padding: 0 0.5em 0 0.5em; +} + +div.code-block-caption { + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +div.code-block-caption + div > div.highlight > pre { + margin-top: 0; +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + padding: 1em 1em 0; +} + +div.literal-block-wrapper div.highlight { + margin: 0; +} + +code.descname { + background-color: transparent; + font-weight: bold; + font-size: 1.2em; + border-style: none; + padding: 0; +} + +code.descclassname { + background-color: transparent; + border-style: none; + padding: 0; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; + border-style: none; + padding: 0; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: relative; + left: 0px; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} + +/* -- My additions ---------------------------------------------------------- */ + +div.note { + color: black; + border: 2px solid #7a9eec; + border-right-style: none; + border-left-style: none; + padding: 10px 20px 0px 60px; + background: #e1ecfe url(dialog-note.png) no-repeat 10px 8px; +} + +div.danger { + color: black; + border: 2px solid #fbc2c4; + border-right-style: none; + border-left-style: none; + padding: 10px 20px 0px 60px; + background: #fbe3e4 url(dialog-note.png) no-repeat 10px 8px; +} + +div.attention { + color: black; + border: 2px solid #ffd324; + border-right-style: none; + border-left-style: none; + padding: 10px 20px 0px 60px; + background: #fff6bf url(dialog-note.png) no-repeat 10px 8px; +} + +div.caution { + color: black; + border: 2px solid #ffd324; + border-right-style: none; + border-left-style: none; + padding: 10px 20px 0px 60px; + background: #fff6bf url(dialog-warning.png) no-repeat 10px 8px; +} + +div.important { + color: black; + background: #fbe3e4 url(dialog-seealso.png) no-repeat 10px 8px; + border: 2px solid #fbc2c4; + border-left-style: none; + border-right-style: none; + padding: 10px 20px 0px 60px; +} + +div.seealso { + color: black; + background: #fff6bf url(dialog-seealso.png) no-repeat 10px 8px; + border: 2px solid #ffd324; + border-left-style: none; + border-right-style: none; + padding: 10px 20px 0px 60px; +} + +div.hint, div.tip { + color: black; + background: #eeffcc url(dialog-topic.png) no-repeat 10px 8px; + border: 2px solid #aacc99; + border-left-style: none; + border-right-style: none; + padding: 10px 20px 0px 60px; +} + +div.admonition-example { + color: black; + background: white url(dialog-topic.png) no-repeat 10px 8px; + border: 2px solid #aacc99; + border-left-style: none; + border-right-style: none; + padding: 10px 0px 20px 60px; +} +div.warning, div.error { + color: black; + background: #fbe3e4 url(dialog-warning.png) no-repeat 10px 8px; + border: 2px solid #fbc2c4; + border-right-style: none; + border-left-style: none; + padding: 10px 20px 0px 60px; +} + +p { + text-align: justify; + padding-bottom: 5px; +} + +h1 { + background: #fff6bf; + border: 2px solid #ffd324; + border-left-style: none; + border-right-style: none; + padding: 10px 10px 10px 10px; + text-align: center; +} + +h2 { + /* background: #eeffcc; */ + border: 2px solid #aacc99; + border-left-style: none; + border-right-style: none; + border-top-style: none; + padding: 10px 0px 0px 0px; + /* text-align: center; */ +} + +h3 { + /* background: #eeffcc; */ + border: 1px solid #7a9eec; + border-left-style: none; + border-right-style: none; + border-top-style: none; + padding: 0; + /* text-align: center; */ +} + +h4 { + background: #eeffcc; + /* border: 1px solid #aacc99; */ + border-left-style: none; + border-right-style: none; + border-top-style: none; + padding: 5px 5px 5px 5px; + /* text-align: center; */ +} + +cite { + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + border: 1px solid #e1e1e8; + background: #f7f7f9; + margin: 0 0 10px; + padding: 0 5px 0 5px; + font-size: 13px; + font-style: italic; +} + +.program { + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + border: 1px solid #e1e1e8; + background: #f7f7f9; + margin: 0 0 10px; + padding: 0 5px 0 5px; + font-size: 13px; +} + +/* dt/dd on single line */ + +dl.field-list { + display: grid; + grid-template-columns: max-content auto; +} + +dt.field-list { + grid-column-start: 1; +} + +dt.field-odd:after { + content: ':'; +} + +dt.field-even:after { + content: ':'; +} + +dd.field-list { + grid-column-start: 2; +} diff --git a/docs/_static/dialog-note.png b/docs/_static/dialog-note.png new file mode 100644 index 00000000..263fbd58 Binary files /dev/null and b/docs/_static/dialog-note.png differ diff --git a/docs/_static/dialog-seealso.png b/docs/_static/dialog-seealso.png new file mode 100644 index 00000000..3eb7b05c Binary files /dev/null and b/docs/_static/dialog-seealso.png differ diff --git a/docs/_static/dialog-topic.png b/docs/_static/dialog-topic.png new file mode 100644 index 00000000..2ac57475 Binary files /dev/null and b/docs/_static/dialog-topic.png differ diff --git a/docs/_static/dialog-warning.png b/docs/_static/dialog-warning.png new file mode 100644 index 00000000..7233d45d Binary files /dev/null and b/docs/_static/dialog-warning.png differ diff --git a/docs/changelog.txt b/docs/changelog.txt new file mode 100644 index 00000000..d3e990e3 --- /dev/null +++ b/docs/changelog.txt @@ -0,0 +1,8 @@ +######### +Changelog +######### + +Version 0.12.1 +============== + +Initial release. diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 00000000..6f842be4 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,199 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) + +import sphinx_bootstrap_theme + +# -- Project information ----------------------------------------------------- + +project = 'Firebird QA' +copyright = '2022, Pavel Cisar' +author = 'Pavel Císař' + +# The short X.Y version +version = '0.12.1' + +# The full version, including alpha/beta/rc tags +release = '0.12.1' + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.intersphinx', + 'sphinx.ext.napoleon', + 'sphinx_autodoc_typehints', + 'sphinx.ext.todo', +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +source_suffix = '.txt' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +default_role = 'py:obj' + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +#html_theme = 'alabaster' + +html_theme = "bootstrap" +html_theme_path = sphinx_bootstrap_theme.get_html_theme_path() + +# bootstrap theme config + +# (Optional) Logo. Should be small enough to fit the navbar (ideally 24x24). +# Path should be relative to the ``_static`` files directory. +#html_logo = "my_logo.png" + +# Theme options are theme-specific and customize the look and feel of a +# theme further. +html_theme_options = { + # Navigation bar title. (Default: ``project`` value) + #'navbar_title': "Firebird-qa", + + # Tab name for entire site. (Default: "Site") + 'navbar_site_name': "Content", + + # A list of tuples containing pages or urls to link to. + # Valid tuples should be in the following forms: + # (name, page) # a link to a page + # (name, "/aa/bb", 1) # a link to an arbitrary relative url + # (name, "http://example.com", True) # arbitrary absolute url + # Note the "1" or "True" value above as the third argument to indicate + # an arbitrary url. + 'navbar_links': [ + ("Usage Guide", "usage-guide"), + ("Reference", "reference"), + ("Index", "genindex"), + ], + + # Render the next and previous page links in navbar. (Default: true) + 'navbar_sidebarrel': False, + + # Render the current pages TOC in the navbar. (Default: true) + #'navbar_pagenav': True, + + # Tab name for the current pages TOC. (Default: "Page") + #'navbar_pagenav_name': "Page", + + # Global TOC depth for "site" navbar tab. (Default: 1) + # Switching to -1 shows all levels. + 'globaltoc_depth': 3, + + # Include hidden TOCs in Site navbar? + # + # Note: If this is "false", you cannot have mixed ``:hidden:`` and + # non-hidden ``toctree`` directives in the same page, or else the build + # will break. + # + # Values: "true" (default) or "false" + 'globaltoc_includehidden': "true", + + # HTML navbar class (Default: "navbar") to attach to
element. + # For black navbar, do "navbar navbar-inverse" + 'navbar_class': "navbar navbar-inverse", + + # Fix navigation bar to top of page? + # Values: "true" (default) or "false" + 'navbar_fixed_top': "true", + + # Location of link to source. + # Options are "nav" (default), "footer" or anything else to exclude. + 'source_link_position': "none", + + # Bootswatch (http://bootswatch.com/) theme. + # + # Options are nothing (default) or the name of a valid theme + # such as "cosmo" or "sandstone". + # + # The set of valid themes depend on the version of Bootstrap + # that's used (the next config option). + # + # Currently, the supported themes are: + # - Bootstrap 2: https://bootswatch.com/2 + # - Bootstrap 3: https://bootswatch.com/3 + #'bootswatch_theme': "united", # cerulean, flatly, lumen, materia, united, yeti + 'bootswatch_theme': "cerulean", + + # Choose Bootstrap version. + # Values: "3" (default) or "2" (in quotes) + 'bootstrap_version': "2", +} + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# -- Extension configuration ------------------------------------------------- + +# Autodoc options +# --------------- +autodoc_default_options = { + 'content': 'both', + 'members': True, + 'member-order': 'groupwise', + 'undoc-members': True, + 'exclude-members': '__weakref__', + 'show-inheritance': True, + 'no-inherited-members': True, +} +set_type_checking_flag = True +#always_document_param_types = True + +# Napoleon options +# ---------------- +napoleon_include_init_with_doc = True +napoleon_include_private_with_doc = True +napoleon_include_special_with_doc = True +napoleon_use_admonition_for_examples = False +napoleon_use_admonition_for_notes = True +napoleon_use_admonition_for_references = True +napoleon_use_ivar = False +napoleon_use_rtype = True +napoleon_attr_annotations = True + +# -- Options for intersphinx extension --------------------------------------- + +# intersphinx +intersphinx_mapping = {'python': ('https://docs.python.org/3', None), + 'base': ('https://firebird-base.rtfd.io/en/latest', None), + 'driver': ('https://firebird-driver.rtfd.io/en/latest', None), + 'pytest': ('https://docs.pytest.org/en/latest', None), + } + +intersphinx_disabled_reftypes = [] + +# -- Options for todo extension ---------------------------------------------- + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = True diff --git a/docs/index.txt b/docs/index.txt new file mode 100644 index 00000000..3aebf574 --- /dev/null +++ b/docs/index.txt @@ -0,0 +1,30 @@ + +############### +The Firebird QA +############### + +The `firebird-qa`_ package contains official QA suite of the Firebird Project. + +.. seealso:: Documentation for `firebird-driver`_. + +Content +******* + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + usage-guide + reference + changelog + license + + +Indices and tables +****************** + +* :ref:`genindex` +* :ref:`modindex` + +.. _firebird-driver: https://firebird-driver.rtfd.io/en/latest +.. _firebird-qa: https://github.com/FirebirdSQL/firebird-qa diff --git a/docs/license.txt b/docs/license.txt new file mode 100644 index 00000000..935e89de --- /dev/null +++ b/docs/license.txt @@ -0,0 +1,6 @@ +####### +License +####### + +.. include:: ../LICENSE + diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 00000000..153be5e2 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/reference.txt b/docs/reference.txt new file mode 100644 index 00000000..8f08fc18 --- /dev/null +++ b/docs/reference.txt @@ -0,0 +1,128 @@ +.. module:: firebird.qa.plugin + :synopsis: Main Firebird driver code + +############################ +Firebird-QA plugin Reference +############################ + +functions and classes for use in tests +====================================== + +db_factory +---------- +.. autofunction:: db_factory + +user_factory +------------ +.. autofunction:: user_factory + +role_factory +------------ +.. autofunction:: role_factory + +envar_factory +------------- +.. autofunction:: envar_factory + +temp_file +--------- +.. autofunction:: temp_file + +temp_files +---------- +.. autofunction:: temp_files + +isql_act +-------- +.. autofunction:: isql_act + +python_act +---------- +.. autofunction:: python_act + +Database +-------- +.. autoclass:: Database + +User +---- +.. autoclass:: User + +Role +---- +.. autoclass:: Role + +Envar +----- +.. autoclass:: Envar + +ServerKeeper +------------ +.. autoclass:: ServerKeeper + +Action +------ +.. autoclass:: Action + +ExecutionError +-------------- +.. autoclass:: ExecutionError + +pytest hooks +============ + +pytest_addoption +---------------- +.. autofunction:: pytest_addoption + +pytest_report_header +-------------------- +.. autofunction:: pytest_report_header + +pytest_configure +---------------- +.. autofunction:: pytest_configure + +pytest_collection_modifyitems +----------------------------- +.. autofunction:: pytest_collection_modifyitems + +pytest_runtest_makereport +------------------------- +.. autofunction:: pytest_runtest_makereport + +Internal functions +================== + +log_session_context +------------------- +.. autofunction:: log_session_context + +set_tool +-------- +.. autofunction:: set_tool + +substitute_macros +----------------- +.. autofunction:: substitute_macros + +db_path +------- +.. autofunction:: db_path + +trace_thread +------------ +.. autofunction:: trace_thread + +Internal classes +================ + +TraceSession +------------ +.. autoclass:: TraceSession + +QATerminalReporter +------------------ +.. autoclass:: QATerminalReporter + +.. _firebird-driver: https://firebird-driver.rtfd.io/en/latest diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 00000000..b5712db4 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,3 @@ +sphinx-bootstrap-theme>=0.8.0 +sphinx-autodoc-typehints>=1.17.0 +. diff --git a/docs/usage-guide.txt b/docs/usage-guide.txt new file mode 100644 index 00000000..ba791d87 --- /dev/null +++ b/docs/usage-guide.txt @@ -0,0 +1,925 @@ + +=========== +Usage Guide +=========== + +.. currentModule:: firebird.qa + +Before you start using Firebird QA suite +======================================== + +The Firebird QA suite is based on pytest_. If you are not familiar with this testing framework, +you should read at least next sections from pytest documentation: + +1. `How to invoke pytest `_ +2. `Command-line Flags `_ +3. `Pytest customization `_ + +The Firebird QA suite resides in firebird-qa_ repository at github. This repository +contains a `pytest` plugin, various support files, and a set of tests that uses this plugin +to test Firebird server(s). Currentlly, only local Firebird installations could be tested. + +.. note:: + + The suite could NOT be used to test Firebird servers older than v3. + +Installation +============ + +Requirements +------------ + +1. Requires Python_ 3.8 or newer. +2. Requires `pip` installer. You may check it's availability from command line with:: + + > pip --help + + If `pip` is not installed, you may install it with:: + + > python -m ensurepip + +3. It's **recommended** to create separate Python virtual environment to install and run + the QA suite, especially on Linux where Python `site-packages` are managed by Linux + distribution package manager. + + There are multiple ways how to create and manage Python virtual environments, but we + recommend to use virtualenv_, together with virtualenvwrapper_ (for Linux) or + virtualenvwrapper-win_ (for Windows). + + On Linux, the `virtualenv` and `virtualenvwrapper` are typically available for installation + from ditribution repository via package manager, which is also the preferred way to + install them on this platform. + + On Windows, you should install `virtualenv` and `virtualenvwrapper-win` via `pip`. + +Installation +------------ + +1. Open the command prompt / terminal. + +2. Clone the firebird-qa repository:: + + > git clone git://github.com/FirebirdSQL/firebird-qa.git + +3. Activate the Python virtual environment you created for QA, or skip this step if you + want to install everything into main site-packages. + +4. Switch to directory with cloned `firebird-qa` repository. + + .. note:: + + We'll refer to this directory as `QA root directory`. + +5. Install the plugin with pip, running:: + + > pip install -e . + + This will install Firebird QA plugin for `pytest`, along with required dependencies. + + .. important:: + + You must re-install the plugin every time you see that `git pull` updated + the `setup.cfg` file! + + +Configuration +============= + +Firebird-driver configuration +----------------------------- + +The QA plugin uses firebird-driver_ to access the Firebird servers, and uses +`driver configuration object ` to set up various driver and server/database connection parameters. +The configuration object is initialized from `firebird-driver.conf` file, and plugin +specifically utilizes server sections in this file. When pytest is invoked, you must specify +tested server with **--server ** option, where `` is name of server configuration +section in `firebird-driver.conf` file. + +This file is stored in firebird-qa repository, and defines default configuration suitable +to most QA setups. + +.. note:: + + The `firebird-driver.conf` file is located in QA root directory. In default setup, the + QA plugin is used to test local Firebird installation with default user name and password + (SYSDBA/masterkey) via `local` server (configuration section). + +.. important:: + + The firebird-driver currently does not support specification of client library in server + sections. However, the QA plugin works around that limitation. If server section for + tested server contains `fb_client_library` option specification, it's copied to global + setting. + +.. seealso:: + + See `configuration `_ + chapter in driver documentation for details. + +Pytest configuration +-------------------- + +While it's not required, it's recommended to create `pytest configuration file +`_ in QA root directory. +You may use this file to simplify your use of pytest with `addopts` option, or adjust +pytest behaviour. + +.. tip:: + + Suggested options for pytest.ini:: + + console_output_style = count + testpaths = tests + addopts = --server local --install-terminal + +Firebird server configuration +----------------------------- + +Some tests in Firebird test suite require specific Firebird server configuration to work +properly (as designed). If possible, these tests check the configuration of tested server, +and mark itself to SKIP if required conditions are not met. However, it's not always possible +(or desirable) to perform such check. You have to cosult `Firebird QA README` for current +requirements on Firebird server configuration. + + +Running QA test suite +===================== + +Basics +------ + +1. Open the terminal / command-line. + +2. If you installed Firebird QA in Python virtual environment, **activate it**. + +3. Switch to QA root directory. + +4. To run all tests in suite against local Firebird server, invoke:: + + pytest --server local ./tests + + .. tip:: + + If you created `pytest.ini` with recommended values, you can just invoke `pytest` + without additional parameters. + +pytest report header +-------------------- + +When pytest is invoked, a report header is printed on terminal before individual tests +are executed. The QA plugin extend this header with next information: + +* Python encodings + + - system + - locale + - filesystem + +* Information about tested Firebird server + + - conf. section name + - version + - mode + - architecture + - home directory + - tools directory + - used client library + +Example:: + + > pytest + ====================================================== test session starts ======================================================= + platform linux -- Python 3.8.12, pytest-7.0.0, pluggy-1.0.0 -- /home/job/python/envs/qa/bin/python + cachedir: .pytest_cache + System: + encodings: sys:utf-8 locale:UTF-8 filesystem:utf-8 + Firebird: + server: local [v3.0.9.33562, SuperServer, Firebird/Linux/AMD/Intel/x64] + home: /opt/firebird + bin: /opt/firebird/bin + client library: libfbclient.so.2 + rootdir: /home/job/python/projects/firebird-qa, configfile: pytest.ini, testpaths: tests + plugins: firebird-qa-0.12.1 + collected 2385 items / 475 deselected / 1910 selected + + issue.full-join-push-where-predicate PASSED [ 1/1910] + ... + +pytest switches installed by QA plugin +-------------------------------------- + +The QA plugin installs several pytest command-line switches. When you run `pytest --help`, +they are listed in `Firebird QA` section:: + + Firebird QA: + --server=SERVER Server configuration name + --bin-dir=PATH Path to directory with Firebird utilities + --protocol={xnet,inet,inet4,wnet} + Network protocol used for database attachments + --runslow Run slow tests + --save-output Save test std[out|err] output to files + --skip-deselected={platform,version,any} + SKIP tests instead deselection + --extend-xml Extend XML JUnit report with additional information + --install-terminal Use our own terminal reporter + +server +~~~~~~ + +**REQUIRED option.** Section name in `firebird-driver.conf` with connection parameters for +tested server. + +bin-dir +~~~~~~~ + +Normally, the QA plugin detects and properly sets the directory where Firebird tools are +installed. However, you can set this directory explicitly using the `--bin-dir` switch. + +protocol +~~~~~~~~ + +Override for network protocol specified in `firebird-driver.conf` file (or default). + +runslow +~~~~~~~ + +Tests that run for longer than 10 minutes on equipment used for regular Firebird QA are +marked as `slow`. They are not executed, unless this switch is specified. + +.. note:: Currently, there are no slow tests in Firebird test suite. + +save-output +~~~~~~~~~~~ + +**Experimental switch** + +When this switch is specified, stdout/stderr output of external Firebird tool executed by +test is stored in `./out` subdirectory. Intended for test debugging. + +skip-deselected +~~~~~~~~~~~~~~~ + +Tests that are not applicable to tested server (because they are for specific platform or +Firebird versions) are deselected during pytest collection phase. It means that they are +not shown in test session report. This switch changes the routine, so tests are marked to +skip (with message explaining why) instead deselection, so they show up is session report. + +extend-xml +~~~~~~~~~~ + +When this switch is used together with `--junitxml` switch, the produced JUnitXML file +will contain additional metadata for `testsuite` and `testcase` elements recorded as +`property` sub-elements. + +.. important:: + + Please note that using this feature will break schema verifications for the latest + JUnitXML schema. This might be a problem when used with some CI servers. + +install-terminal +~~~~~~~~~~~~~~~~ + +This option changes default pytest terminal reporter that displays pytest NODE IDs, to custom +reporter that displays Firebord QA test IDs. + +pytest node IDs are of the form `module.py::class::method` or `module.py::function`. + +Firebord QA test IDs are defined in our test metadata. + +.. important:: + + Right now, the custom terminal is `opt-in` feature. This will be changed in some future + release to `opt-out` using new switch. + +Tests for Firebird engine +========================= + +Test suite +---------- + +The Firebird QA test suite is located in `tests` subdirectory of QA root directory. Because +Firebird tests are written in Python, the test suite directory is a `Python package`_, so +each directory **must** contain `__init__.py` file. + +Test files +---------- + +For pytest framework, a single test is a function or class method that is executed during +test session. Single Python module can contain arbitrary number of test functions/methods. +Firebird QA uses slightly different model, where each test is a separate Python file (module_) +that provides one or more specific test implementations as module-level test functions, and +only one function is selected by pytest for execution. The selection is typically performed +by marking tests to be executed only on certain platform and/or Firebird engine version +using `pytest.mark.version` or `pytest.mark.platform` decorators. The QA plugin then uses +these marks to deselect (or skip) test functions that are not applicateble to tested Firebird +engine. + +Test files must have `.py` extension and name that either starts with `test_` or ends with +`_test`. + +Test encoding +------------- + +Test files must be encoded in utf-8, and first line must specify this encoding:: + + #coding:utf-8 + +Test metadata +------------- + +Test files must have a docstring_ with test metadata. Each metadata item must start on +separate line starting with item tag followed by `colon`. + +.. list-table:: **Currently supported metadata items** + :widths: 20 60 10 10 + :header-rows: 1 + + * - Tag + - Description + - Required + - Multiline + * - ID + - Unique test identification. Can contain alphanumeric characters, dot, underscore and + hyphen. Must start with alphanum character. + - **Yes** + - No + * - TITLE + - Test title. Multiline titles are concatenated into single line (line breaks + removed and line contents separated with single space). + - **Yes** + - Yes + * - DESCRIPTION + - Test description + - No + - Yes + * - NOTES + - Notes for test (change log etc.) + - No + - Yes + * - ISSUE + - GitHub issue number + - No + - No + * - JIRA + - Legacy JIRA issue ID + - No + - No + * - FBTEST + - Legacy fbtest test ID + - No + - No + +Test functions +-------------- + +Each test is implemented as module-level function(s) with name starting with `test_`. + +.. important:: + + There could be multiple test variants implemented as separate test functions, but their + implementation must ensure that **only one** version is selected by pytest for execution! + + Typical multi-variant scenario uses individual test variants marked for run on specific + platform, or against specific Firebird versions using `pytest.mark.version` or + `pytest.mark.platform` decorators. + +Test functions typically use various :doc:`fixtures ` provided +by QA plugin or pytest itself. In most cases, the test outcome is determined using `assert` +statements. + +Fixtures +-------- + +The QA plugin implements fixture factories that provide resources and facilities frequently +used by Firebird tests. Fixtures that provide temporary resources (like databases, users, +files) ensure their initialization before test execution and removal when test finishes. + +.. note:: + + Fixtures returned by fixture factories must be assigned to module-level variables. + Variable names are then used as parameter names of test functions. + + Example:: + + # fixture that provides Action object used in test function + act = python_act('db') + + # test function + def test_1(act: Action): + act.execute() + ... + +Fixture values are typically a class instance that allows access to provided resource. + +Database +~~~~~~~~ + +Almost all tests need a database. The `.db_factory` function creates a fixture that provides +`.Database` object. Test may use this object to create connections, access database +parameters or perform other database-related actions. + +User +~~~~ + +Some tests may need to use different user accounts than SYSDBA, or multiple user accounts. +The `.user_factory` function creates a fixture that provides `.User` object. Beside automatic +setup/teardown of temporary Firebird user account, tests may use this object to access +user parameters or perform other user-related actions. + +Action +~~~~~~ + +The `.Action` object is a "Swiss army knife" provided by QA plugin to simplify implementation +of Firebird tests. There are two Action fixture factories: + +* `.isql_act` for simple tests that use single ISQL test script. +* `.python_act` for more complex test implementations. + +Role +~~~~ + +The `.role_factory` function creates a fixture that provides `.Role` object representing +SQL role associated with specified test database. + +Envar +~~~~~ + +The `.envar_factory` function creates a fixture that could be used to temporary set value +to environment variable. + +Temporary files +~~~~~~~~~~~~~~~ + +Although pytest provides fixtures for temporary files, QA plugin provides its own fixture +factories `.temp_file` and `.temp_files`. + +Example test file +----------------- + +Example test file `tests/issue/test_319.py`:: + + #coding:utf-8 + + """ + ID: issue-319 + ISSUE: 319 + JIRA: CORE-1 + TITLE: Server shutdown + DESCRIPTION: Server shuts down when user password is attempted to be modified to a empty string + FBTEST: bugs.core_0001 + """ + + import pytest + from firebird.qa import * + + # fixture providing test database + db = db_factory() + + # fixture providing temporary user + user = user_factory('db', name='tmp$c0001', password='123') + + # isql script executed to test Firebird + test_script = """ + alter user tmp$c0001 password ''; + commit; + """ + + # fixture that provides Action object used in test function + act = isql_act('db', test_script) + + # Expected stderr output from isql + expected_stderr = """ + Statement failed, SQLSTATE = 42000 + unsuccessful metadata update + -ALTER USER TMP$C0001 failed + -Password should not be empty string + """ + + # Test function, marked to run on Firebird v3.0 or newer + @pytest.mark.version('>=3.0') + def test_1(act: Action, user: User): + act.expected_stderr = expected_stderr + act.execute() + # This evaluates test outcome + assert act.clean_stderr == act.clean_expected_stderr + +How-to guides +============= + +How to use databases in tests +----------------------------- + +Database fixture +~~~~~~~~~~~~~~~~ + +It's recommend to use fixtures created by `.db_factory` function. Function arguments specify +how database is created, initialized and removed. + +* If not specified otherwise, the fixture creates new empty database. +* To create database from backup file, use `from_backup` argument. File must be located in + `backups` directory. +* To use copy of prepared database, use `copy_of` argument. File must be located in `databases` + directory. +* The name of created temporary database could be specifid with `filename` argument. Default + database name is `test.fdb`. +* It's possible to specify `page_size` and `sql_dialect` of created database. These options + are ignored if database is created as a copy, or from backup. +* It's possible to specify database `charset` (not applid for backups and copies) that is + also default connection charset. +* After temporary database is created (by either method), it could be initialized with SQL + commands (executed via isql) specified using `init` argument. +* Database is created using default server user and password. It's possible to specify + alternate credentials with `user` and `password` arguments. +* The fixture ensures that database is created an initialized during test setup, and removed + during test teardown. To disable either phase (because create/drop is performed by test + itself), use `do_not_create` or `do_not_drop` arguments. +* By default, database is set to `async` write after creation to speed up database operations. + It's possible to change that with `async_write` argument. +* The database is `registered ` in firebird + driver configuration as `fbtest`. You can specify the configuration name explicitly with + `config` argument. + +.. note:: + + The returned fixture 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. + +Examples:: + + # new empty database with default charset, page size and SQL dialect 3 + db = db_factory() + + # database created from backup + db = db_factory(from_backup='mon-stat-gathering-2_5.fbk') + + # new empty database with default charset, page size and SQL dialect 1 initialized with + # isql script + init_script = """create table T1 (F1 char(4), F2 char(4)); + create index T1_F1 on T1 (F1); + insert into T1 (F1, F2) values ('001', '001'); + insert into T1 (F1, F2) values ('002', '002'); + insert into T1 (F1, F2) values ('003', '003'); + insert into T1 (F1, F2) values ('004', '004'); + commit; + """ + + db = db_factory(sql_dialect=1, init=init_script) + + # new empty database with ISO8859_1 charset, SQL dialect 3 and default page size + db = db_factory(charset='ISO8859_1') + +Primary test database +~~~~~~~~~~~~~~~~~~~~~ + +Because almost all Firebird tests need a database, the QA plugins works with concept of +`primary test database`. This fixture is typically named `db`, and is used by other fixtures +that work with database. + +.. important:: + + Database fixture is referenced by other QA plugin fixtures `by name`, not `by value`, + so you have to pass the fixture name as string! + + Example:: + + db = db_factory() + act = python_act('db') + +.. note:: + + When test has multiple variants, these variants typically use database with the same + parameters and content, so they can use the single database fixture. In rare cases where + individual test variants need different databases, the usual naming scheme for primary + databases is **db_**. + +Test functions that use the `.Action` object provided by fixtures created with `.isql_act()` +and `.python_act()` does not need to use the primary test database directly, because its +exposed as `.Action.db` attribute. + +Example:: + + db = db_factory() + act = python_act('db') + + @pytest.mark.version('>=3.0') + def test_1(act: Action): + # SQL executed on primary test database + act.isql(switches=[], input="show database;") + # Using connection to primary test database + with act.db.connect() as con: + ... + +Additional databases +~~~~~~~~~~~~~~~~~~~~ + +Some tests need more than one database. Fixtures for these databases must be used directly +by test function. + +Example:: + + db = db_factory() + act = python_act('db') + + db_dml_sessions = db_factory(sql_dialect=3, init=init_script, filename='tmp_5087_dml_heavy.fdb') + + @pytest.mark.version('>=3.0') + def test_1(act: Action, db_dml_sessions: Database): + # Using connection to primary test database + with act.db.connect() as con: + ... + # Using connection to secondary test database + with db_dml_sessions.connect() as con: + ... + +The Database object +~~~~~~~~~~~~~~~~~~~ + +Database fixtures provide `.Database` instance that allows access to test database. + +The `~.Database.connect()` method creates new `connection ` +to database. It's recommended to manage connection using the `with` statement:: + + with act.db.connect() as con: + ... + +Database atributes are often needed to use the database with external tools: + +* `~.Database.db_path`: Full path to test database. +* `~.Database.dsn`: DSN to test database. +* `~.Database.charset`: Name of database CHARACTER SET +* `~.Database.config_name`: firebird-driver database configuration name +* `~.Database.user`: User name +* `~.Database.password`: Password + +.. seealso:: `.Database` documentation for full reference. + +How to use the Action object +---------------------------- + +Action fixture +~~~~~~~~~~~~~~ + +The `.Action` object is provided by fixture created by `.isql_act()` or `.python_act()` +factory function. These functions are identical (it's in fact only one function available +under two names). It's a legacy from old `fbtest` QA system that had two types of tests, and +such distinction was retained during conversion of tests from old system to new one. + +Although there is no difference, it's recommended to retain the distinction in new test by +using: + +* `.isql_act` for simple tests that use single ISQL test script. +* `.python_act` for more complex test implementations. + +This function has next arguments: + +* `db_fixture_name`: REQUIRED. Name of database fixture (primary database). +* `script`: OptionalSQL script that tests the server. +* `substitutions`: Optional list of additional substitution for stdout/stderr. + +.. note:: + + The returned fixture must be assigned to module-level variable. It's typically named `act`. + + When test has multiple variants and these variants use the same primary database and + substitutions, they can use the single action fixture. In cases where + individual test variants need different actions, the usual naming scheme for them is + **act_**. + +Example:: + + db = db_factory() + act = python_act('db') + + @pytest.mark.version('>=3.0') + def test_1(act: Action): + ... + +The Action class +~~~~~~~~~~~~~~~~ + +The `.Action` is multipurpose object, that could be used to: + +* Execute external Firebird tools and capture their output with `~.Action.execute()`, + `~.Action.extract_meta()`, `~.Action.isql()`, `~.Action.gstat()`, `~.Action.gsec()`, + `~.Action.gbak()`, `~.Action.gfix()`, `~.Action.nbackup()` and `~.Action.svcmgr()` +* Access primary test database as `~.Action.db` attribute. +* Create `connection ` to Firebird service manager with + `~.Action.connect_server()`. +* Query server configuration with `~.Action.get_config()`. +* Prind data from `cursor ` with `~.Action.print_data()` and + `~.Action.print_data_list()`. +* Get content of Firebird server log with `~.Action.get_firebird_log()`. +* Check Firebird server version with `~.Action.is_version()`. +* Determine Firebird server architecture with `~.Action.get_server_architecture()`. +* Create arbitrary DSN for database connections with `~.Action.get_dsn()`. +* Check presence of regex patterns in string using `~.Action.match_any()`. +* Work with Firebird trace sessions using `~.Action.trace()` and `~.Action.trace_to_stdout()`. +* Temporary set environment variables with `~.Action.envar()`. +* Redirect output of services to stdout with `~.Action.print_callback`. +* Access test execution environment with `.Action` attributes and properties. + +Using external Firebird tools +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +External Firebird tools could be executed with methods `~.Action.execute()`, +`~.Action.extract_meta()`, `~.Action.isql()`, `~.Action.gstat()`, `~.Action.gsec()`, +`~.Action.gbak()`, `~.Action.gfix()`, `~.Action.nbackup()` and `~.Action.svcmgr()`. + +All these methods store results into `~.Action.stdout`, `~.Action.stderr` and +`~.Action.return_code` attributes. Test may assign expected outputs into `~.Action.expected_stdout` +and `~.Action.expected_stderr` attributes to perform asserts between real and expected output. +However, output must be often cleaned from unwanted or irrelevant parts (especially isql +output contains many "noise" parts). The `.Action` properties `~.Action.clean_stdout`, +`~.Action.clean_stderr`, `~.Action.clean_expected_stdout` and `~.Action.clean_expected_stderr` +provide such clean content. + +The tool execution may fail, which could be expected or unexpected by test. Expected +fails must be indicated by assigning *ANY* string to `~.Action.expected_stderr` before +tool is executed. In such case no error is reported and test may assert that execution +failed in expected way. If failure is unexpected, an `.ExecutionError` exception is raised. + +Example of test with expected failure:: + + import pytest + from firebird.qa import * + + db = db_factory() + + user = user_factory('db', name='tmp$c0001', password='123') + + test_script = """ + alter user tmp$c0001 password ''; + commit; + """ + + act = isql_act('db', test_script) + + expected_stderr = """ + Statement failed, SQLSTATE = 42000 + unsuccessful metadata update + -ALTER USER TMP$C0001 failed + -Password should not be empty string + """ + + @pytest.mark.version('>=3.0') + def test_1(act: Action, user: User): + act.expected_stderr = expected_stderr + act.execute() + assert act.clean_stderr == act.clean_expected_stderr + +Example of test that will raise an exception of failure:: + + import pytest + from firebird.qa import * + + db = db_factory() + + test_script = """ + set list on; + create table t1 ( + campo1 numeric(15,2), + campo2 numeric(15,2) + ); + commit; + set term ^; + create procedure teste + returns ( + retorno numeric(15,2)) + as + begin + execute statement 'select first 1 (campo1*campo2) from t1' into :retorno; + suspend; + end + ^ + set term ;^ + commit; + + insert into t1 (campo1, campo2) values (10.5, 5.5); + commit; + + select * from teste; + """ + + act = isql_act('db', test_script) + + expected_stdout = """ + RETORNO 57.75 + """ + + @pytest.mark.version('>=3') + def test_1(act: Action): + act.expected_stdout = expected_stdout + act.execute() + assert act.clean_stdout == act.clean_expected_stdout + +.. important:: + + If test performs multiple executions, it's neccessary to call `.Action.reset()` to + reinitialize internal variables. Otherwise "clean" functions will return wrong values, + and you can experience other annomalies. + + Example:: + + @pytest.mark.version('>=3.0') + def test_1(act: Action, tmp_file: Path): + tmp_file.write_bytes(non_ascii_ddl.encode('cp1251')) + # run without specifying charset + act.expected_stdout = expected_stdout_a + act.expected_stderr = expected_stderr_a_40 if act.is_version('>=4.0') else expected_stderr_a_30 + act.isql(switches=['-q'], input_file=tmp_file, charset=None, io_enc='cp1251') + assert (act.clean_stdout == act.clean_expected_stdout and + act.clean_stderr == act.clean_expected_stderr) + # run with charset + act.reset() # <-- Necessary to reinitialize internal variables + act.isql(switches=['-q'], input_file=tmp_file, charset='win1251', io_enc='cp1251') + assert act.clean_stdout.endswith('Metadata created OK.') + +Using trace +~~~~~~~~~~~ + +Test can use Firebird trace session using `~.Action.trace()` method. This method returns +`.TraceSession` context manager instance that runs trace session in separate thread. + +There are two (mutually exclusive) methods how to configure the trace session: + +1. Using `db_events` and/or `svc_events` lists, and optional `database` specification. + This method is more convenient and readable, as you don't need to worry about + proper construction of trace config string (brackets etc.) +2. Using `config`, when you specifically need to pass configuration in original "full" format. + +.. important:: + + It's absolutely necessary to use the :ref:`with ` statement to manage the + trace session! + +Example:: + + import pytest + import platform + from firebird.qa import * + + db = db_factory() + + act = python_act('db', substitutions=[('^((?!records fetched).)*$', '')]) + + expected_stdout = """ + 1 records fetched + """ + + test_script = """ + set list on; + -- statistics for this statement SHOULD appear in trace log: + select 1 k1 from rdb$database; + commit; + -- statistics for this statement should NOT appear in trace log: + select 2 k2 from rdb$types rows 2 /* no_trace*/; + -- statistics for this statement should NOT appear in trace log: + select 3 no_trace from rdb$types rows 3; + -- statistics for this statement should NOT appear in trace log: + set term ^; + execute block returns(k4 int) as + begin + for select 4 from rdb$types rows 4 into k4 do suspend; + end -- no_trace + ^ + set term ;^ + """ + + trace = ['log_connections = true', + 'log_transactions = true', + 'log_statement_finish = true', + 'print_plan = true', + 'print_perf = true', + 'time_threshold = 0', + 'exclude_filter = %no_trace%', + ] + + @pytest.mark.version('>=3.0') + def test_1(act: Action): + with act.trace(db_events=trace): + # Actions that would be traced + act.isql(switches=['-n'], input=test_script) + # Actions that are not traced + act.expected_stdout = expected_stdout + act.trace_to_stdout() + assert act.clean_stdout == act.clean_expected_stdout + + +How to use users +---------------- + +How to use roles +---------------- + +.. _Python: http://www.python.org +.. _virtualenv: https://virtualenv.pypa.io/en/latest/ +.. _virtualenvwrapper: https://virtualenvwrapper.rtfd.io +.. _virtualenvwrapper-win: https://github.com/davidmarble/virtualenvwrapper-win/ +.. _firebird-base: https://firebird-base.rtfd.io +.. _firebird-driver: https://firebird-driver.rtfd.io +.. _pytest: https://docs.pytest.org +.. _firebird-qa: https://github.com/FirebirdSQL/firebird-qa +.. _Python package: https://docs.python.org/3/tutorial/modules.html#packages +.. _module: https://docs.python.org/3/tutorial/modules.html +.. _docstring: https://docs.python.org/3/glossary.html#term-docstring