mirror of
https://github.com/FirebirdSQL/firebird-qa.git
synced 2025-01-22 13:33:07 +01:00
Documentation (Sphinx)
This commit is contained in:
parent
1826c8cbec
commit
1ab7e9b1b7
20
docs/Makefile
Normal file
20
docs/Makefile
Normal file
@ -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)
|
643
docs/_static/basic.css
vendored
Normal file
643
docs/_static/basic.css
vendored
Normal file
@ -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;
|
||||||
|
}
|
BIN
docs/_static/dialog-note.png
vendored
Normal file
BIN
docs/_static/dialog-note.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
BIN
docs/_static/dialog-seealso.png
vendored
Normal file
BIN
docs/_static/dialog-seealso.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
BIN
docs/_static/dialog-topic.png
vendored
Normal file
BIN
docs/_static/dialog-topic.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
BIN
docs/_static/dialog-warning.png
vendored
Normal file
BIN
docs/_static/dialog-warning.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
8
docs/changelog.txt
Normal file
8
docs/changelog.txt
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#########
|
||||||
|
Changelog
|
||||||
|
#########
|
||||||
|
|
||||||
|
Version 0.12.1
|
||||||
|
==============
|
||||||
|
|
||||||
|
Initial release.
|
199
docs/conf.py
Normal file
199
docs/conf.py
Normal file
@ -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 <div> 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
|
30
docs/index.txt
Normal file
30
docs/index.txt
Normal file
@ -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
|
6
docs/license.txt
Normal file
6
docs/license.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#######
|
||||||
|
License
|
||||||
|
#######
|
||||||
|
|
||||||
|
.. include:: ../LICENSE
|
||||||
|
|
35
docs/make.bat
Normal file
35
docs/make.bat
Normal file
@ -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
|
128
docs/reference.txt
Normal file
128
docs/reference.txt
Normal file
@ -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
|
3
docs/requirements.txt
Normal file
3
docs/requirements.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
sphinx-bootstrap-theme>=0.8.0
|
||||||
|
sphinx-autodoc-typehints>=1.17.0
|
||||||
|
.
|
925
docs/usage-guide.txt
Normal file
925
docs/usage-guide.txt
Normal file
@ -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 <https://docs.pytest.org/en/latest/how-to/usage.html>`_
|
||||||
|
2. `Command-line Flags <https://docs.pytest.org/en/latest/reference/reference.html#command-line-flags>`_
|
||||||
|
3. `Pytest customization <https://docs.pytest.org/en/latest/reference/customize.html>`_
|
||||||
|
|
||||||
|
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 <firebird.driver.config.DriverConfig>` 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 <name>** option, where `<name>` 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 <https://firebird-driver.readthedocs.io/en/latest/usage-guide.html#configuration>`_
|
||||||
|
chapter in driver documentation for details.
|
||||||
|
|
||||||
|
Pytest configuration
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
While it's not required, it's recommended to create `pytest configuration file
|
||||||
|
<https://docs.pytest.org/en/latest/reference/customize.html>`_ 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 <pytest:explanation/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 <firebird.driver.config.DriverConfig.register_database>` 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_<number>**.
|
||||||
|
|
||||||
|
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 <firebird.driver.core.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_<number>**.
|
||||||
|
|
||||||
|
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 <firebird.driver.core.Server>` to Firebird service manager with
|
||||||
|
`~.Action.connect_server()`.
|
||||||
|
* Query server configuration with `~.Action.get_config()`.
|
||||||
|
* Prind data from `cursor <firebird.driver.core.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 <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
|
Loading…
Reference in New Issue
Block a user