The test framework for testing Apache Ignite in distributed environment (Testing Ignite in Distributed ENvironment).
Python 3.7 with installed modules on the host starting tests:
- PyYaml
- psutil
- jinja2
- requests (optional)
- ansible (optional)
- py4j (optional)
Environment:
- The hosts running under *NIX bases OS (RedHat, CentOS, Ubuntu)
with installed
bash
and regular commands likecat, grep, ls, head, ps
. - Public/private SSH keys authentication without password prompt from the host starting tests.
- There is no running java processes by default. Tiden removes all
java processes by
sudo killall -9 java
. Thus you must add killall to sudoers list with no password.
tiden run-tests \
--tc=<path_to_configuration_file> \
--tc=<path_to_configuration_file> \
--to=<option_path>=<option_value> \
--to=<option_path>=<option_value> \
--ts=<suite_name>.<test_module_name> \
--var_dir=<path_to_work_directory> \
--clean=all \
--attr=<attr1> \
--attr=<attr2> \
--collect-only
--tc=<path_to_configuration_file>
Argument type: mandatory
Tiden uses one or more YAML formatted configuration file to construct the test configuration that should like following:
artifacts:
<artifact_1_name>:
glob_path: <local_path_to_artifact>
type: [ignite|...]
<artifact_2_name>:
glob_path: <local_path_to_artifact>
type: [ignite|...]
environment:
username: <user_name>
private_key_path: <local_path_to_private_key>
server_hosts: [<ip_address_of_host_1>, <ip_address_of_host_2>,...]
common_hosts: [<ip_address_of_host_1>, <ip_address_of_host_2>,...]
client_hosts: [<ip_address_of_host_1>, <ip_address_of_host_2>,...]
home: <remote_path_for_tiden>
Note: use '.' (dot) in key names is forbidden. All passed configuration files merged into one
--ts=<suite_name>.<test_module_name>
Argument type: mandatory
The suite is name of directory in ./suites
.
The test module name is the name of python module in the suite directory.
--var_dir=<path_to_existing_directory>
Argument type: optional
The directory to collect test artifacts, reports, temporary files.
By default the directory is ./var
--to=<key_path>=<value>
Argument type: optional
Override the option of test configuration.
<key_path> is path to a key to override delimited by dots e.g.
--to=key1.key2.key3=<value>
:
key1:
key2:
key3: value
If the original value is the list then new value can be provided as the string of values delimited by commas:
tiden run-tests ... --to=<key_path>=value1,value2,value3
If you provide the +
symbol before the list of values, then these values will be appended to ones already
set for the given configuration option:
tiden run-tests ... --to=<key_path>=+value1,value2,value3
--clean=[mode]
Argument type: optional
Modes:
all
- clean up localvar_dir
and remoteenvironment.home
directorytests
- clean up local tests dirs , but didn't repack already collected artifacts. Also clean tests data fromenvironment.home
directoryremote_tests
- same astests
, but didn't clean local tests data
--attr=<attr>
Argument type: optional
Choose the test methods decorated by @attr('<attr_name>')
.
The multiple usage of that argument processed by following rules:
By default or if --to=attr_match=any
: any test method will be executed
if at least one from provided attributes found in the attribute
decoration list of that method.
If --to=attr_match=all
then all provided attributes should be a subset
of attribute decoration list of that method.
--collect-only
Argument type: optional
Settings this option makes Tiden only enumerate and print test names matching to given --ts
and --attr
values,
but do not run any tests. Note, that artifacts are still processed and repacked to allow conditional skips work.
- The rules to create a test:
- Create a python module prefixed by
test_
(and in lower case) in the chosen suite directory. - The created test python module must contain single class only.
- The name of module must reflect the name of class:
test_new_feature.py
toTestNewFeature
class. - The resource directory should be called as test module name without
test_
prefix:test_new_feature.py
tores/new_feature
. - The test class must be inherited from a class in
pt/*testcase
directory. - The example:
suites/features/test_new_features.py
from tiden.case.generaltestcase import GeneralTestCase
from tiden import attr
class TestNewFeatures (GeneralTestCase):
def setup(self):
super().setup()
def teardown(self):
pass
@attr('simple_test', 'new')
def test_feature_1(self):
a = 1
b = 1
assert a == b, 'test a == b'
The tool provides XUnit report in var
directory.
- Construct the test configuration file and store it in
var/<suite_name>-<timestamp>
directory - Copy (and repack if required) the artifacts in
var/artifacts
directory. - Check connection to remote hosts
- Kill all java processes and clean up
environment.home
directory if--clean=all
passed - Deploy artifacts on remote hosts and unzip them
- Iterate over test modules (if more that one found). Repeat 7-11 for every test module.
- Execute
setup()
method from the test class (if exists). - Deploy test class resource directory (if exists) on remote hosts.
- Iterate over (filtered) test class methods
- Execute
teardown()
method from test class (if exists) - Collect log files on remote hosts and download them
as
<ip_address>_logs.zip
invar/<suite_name>-<timestamp>/<test_module_name>
directory - Store report files in
var
directory
-
<var_dir>/artifacts
- directory for artifacts found inartifacts
key of test configuration -
<var_dir>/<suite_name>-<timestamp>
- suite work directory -
<var_dir>/<suite_name>-<timestamp>/tmp
- suite tmp directory -
<var_dir>/<suite_name>-<timestamp>/<test_module_name>
- test module directory -
<var_dir>/<suite_name>-<timestamp>/<test_module_name>/res
- test module resource directory
-
<environment.home>/artifacts
- directory for artifacts -
<environment.home>/<suite_name>-<timestamp>
- suite work directory -
<environment.home>/<suite_name>-<timestamp>/<artifact_name_1>
- artifact directory if setartifacts.<artifact_name>.remote_unzip: true
in the test configuration -
<environment.home>/<suite_name>-<timestamp>/<test_module_name>
- test module work directory, resource copied here -
<environment.home>/<suite_name>-<timestamp>/<test_module_name>/<TestClass>
- test class work directory -
<environment.home>/<suite_name>-<timestamp>/<test_module_name>/<TestClass>/setup
- test class setup work directory -
<environment.home>/<suite_name>-<timestamp>/<test_module_name>/<TestClass>/teardown
- test class teardown work directory -
<environment.home>/<suite_name>-<timestamp>/<test_module_name>/<TestClass>/<test_method_name>
- test class method work directory
-
artifacts.<artifact_name>.remote_unzip: true
Unzip the artifact archive on remote hosts after deployment. -
artifacts.<artifact_name>.repack: <command list>
Repack the artifact archive and reduce the size for deployment. The options supports the following commands:move self:<path1> self:</path2>
- move directory<path1>
in<path2>
copy self:<path1> self:</path2>
- copy directory<path1>
in<path2>
copy $<artifact_name>$<path1> self:</path2>
- copy other artifact by name$<artifact_name>$
or separate files from specific path in other artifact$<artifact_name>$<path1>
in</path2>
delete self:<path1>
- delete<path>
By default newly founded artifacts will upload on remote hosts or replaced. All changed artifacts will be automatically redeployed
-
connection_mode: [paramiko|ansible|local]
The way to connect to remote hosts. Pythonparamiko
is by default. Useansible
if the deployment is large. Uselocal
to turn tiden into local testing framework, in that case all[server|client|common]_hosts
in environment configuration must start with '127.0' network. -
ignite
: dictionary with default options for Ignite deployments.-
bind_to_host: True|False
Defaults to False, unlessconnection_mode
is local, True otherwise. Ifbind_to_host
is True, grid config files are patched to tie each node to its host viaIgniteConfiguration.localHost
property. -
unique_node_ports: True|False
Defaults to False, unlessconnection_mode
is local, True otherwise. Ifunique_node_ports
is True, grid config files are patched to tie each node'TcpCommunicationSpi
to specfic port number unique for all grid nodes (e.g. there would be no node with equalTcpCommunicationSpi.port
property in the grid).
-
-
environment.client_jvm_options: <list>
The list of JVM options passed to client nodes. -
environment.env_vars: <dictionary>
The dictionary of remote host environment variables, where key is a variable name. Also the value can be read from an another environment variable:<variable_name>:$<another_variable_name>
-
environment.server_jvm_options: <list>
The list of JVM options passed to server nodes. -
xunit_file: <filename>
The name of file with test report in xUnit format. The report file with given name will be created in thevar_dir
directory. Optional, defaults to 'xunit.xml'. -
testrail_report: <filename>
The name of file with test report in GG QAtestrail-report.py
utility format.
The report file with given name will be created in thevar_dir
directory. Optional, defaults to 'testrail_report.yaml'. -
repeated_test
orrepeated_test.<attr_name>
When given--to=repeated_test=N
, executes all tests otherwise matched by--attr
in all suites matched by--ts
at most N number of iterations or until first test failure.
When given --to=repeated_test.<testattr>=N
, executes only tests matched by <testattr>
at most N number of
iterations or until first failure, all other test remains executed as usual.
- attribute
@attr('<attr_name_1>', '<attr_name_2>', ...)
...
@attr('simple_test', 'new')
def test_1(self):
pass
Note: The name of test method always added to attributes list.
- fixture
@with_setup('<method_name>', '<method_name>')
...
def before_test(self):
pass
def after_test(self):
pass
@with_setup('before_test', 'after_test')
def test_1(self):
pass
- unconditional skip
@skip('<the_reason>')
...
@skip('issue BUG12345')
def test_1(self):
pass
- conditional skip
@require(<requirements>)
.
Note: All requirements must be met for test to run.
Supported requirements:
min_ignite_version
=<version string>
min_server_nodes
=<number of server nodes>
min_client_nodes
=<number of client nodes>
from tiden.util import require
...
@require(min_server_nodes=4, min_ignite_version='2.4.2-p4')
def test_2(self):
pass
Unnamed arguments to @require
are evaluated during test compilation, any False result will lead to test skip,
which allows following example usage as well:
from tiden.testconfig import test_config
from tiden.util import require
...
@require(test_config.ignite.pitr_enabled)
def test():
pass
- explicit repeated iterations
@repeated_test(<N>)
or@repeate_test(<N>, test_names=[<test_names>])
.
from tiden.util import repeated_test
...
@repeated_test(3)
def test():
pass
Makes given test run at most N times or until first failure. Each successive test iteration would have its own variable directory suffixed by iteration number.
When test fails, its name will be renamed in report file to <test_name>_iteration_<M>
, where M
is failed iteration number.
The test framework encompasses unit-tests for self-testing misc functionality. Feel free to contribute at will, but ensure unit-tests remain stable. It is desired that contributor provided unit-tests for added functionality.
Running unit tests requires installed py.test
dependency.
Example:
py.test tests -x --tb=long