freeipa/ipatests/test_ipatests_plugins/test_slicing.py

128 lines
4.1 KiB
Python
Raw Normal View History

Make use of Azure Pipeline slicing The unit tests execution time within Azure Pipelines(AP) is not balanced. One test job(Base) takes ~13min, while another(XMLRPC) ~28min. Fortunately, AP supports slicing: > An agent job can be used to run a suite of tests in parallel. For example, you can run a large suite of 1000 tests on a single agent. Or, you can use two agents and run 500 tests on each one in parallel. To leverage slicing, the tasks in the job should be smart enough to understand the slice they belong to. >The step that runs the tests in a job needs to know which test slice should be run. The variables System.JobPositionInPhase and System.TotalJobsInPhase can be used for this purpose. Thus, to support this pytest should know how to split the test suite into groups(slices). For this, a new internal pytest plugin was added. About plugin. - Tests within a slice are grouped by test modules because not all of the tests within the module are independent from each other. - Slices are balanced by the number of tests within test module. - To run some module within its own environment there is a dedicated slice option (could help with extremely slow tests) Examples. - To split `test_cmdline` tests into 2 slices and run the first one: ipa-run-tests --slices=2 --slice-num=1 test_cmdline - To split tests into 2 slices, then to move one module out to its own slice and run the second one: ipa-run-tests --slices=2 --slice-dedicated=test_cmdline/test_cli.py \ --slice-num=2 test_cmdline Fixes: https://pagure.io/freeipa/issue/8008 Signed-off-by: Stanislav Levin <slev@altlinux.org> Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
2019-07-03 04:56:32 -05:00
#
# Copyright (C) 2019 FreeIPA Contributors see COPYING for license
#
import glob
import pytest
MOD_NAME = "test_module_{}"
FUNC_NAME = "test_func_{}"
PYTEST_INTERNAL_ERROR = 3
MODS_NUM = 5
@pytest.fixture
def ipatestdir(testdir):
"""
Create MODS_NUM test modules within testdir.
Each module contains 1 test function.
"""
testdir.makeconftest(
"""
pytest_plugins = ["ipatests.pytest_ipa.slicing"]
"""
)
for i in range(MODS_NUM):
testdir.makepyfile(
**{MOD_NAME.format(i):
"""
def {func}():
pass
""".format(func=FUNC_NAME.format(i))
}
)
return testdir
@pytest.mark.parametrize(
"nslices,nslices_d,groups",
[(2, 0, [[x for x in range(MODS_NUM) if x % 2 == 0],
[x for x in range(MODS_NUM) if x % 2 != 0]]),
(2, 1, [[0], list(range(1, MODS_NUM))]),
(1, 0, [list(range(MODS_NUM))]),
(1, 1, [list(range(MODS_NUM))]),
Make use of Azure Pipeline slicing The unit tests execution time within Azure Pipelines(AP) is not balanced. One test job(Base) takes ~13min, while another(XMLRPC) ~28min. Fortunately, AP supports slicing: > An agent job can be used to run a suite of tests in parallel. For example, you can run a large suite of 1000 tests on a single agent. Or, you can use two agents and run 500 tests on each one in parallel. To leverage slicing, the tasks in the job should be smart enough to understand the slice they belong to. >The step that runs the tests in a job needs to know which test slice should be run. The variables System.JobPositionInPhase and System.TotalJobsInPhase can be used for this purpose. Thus, to support this pytest should know how to split the test suite into groups(slices). For this, a new internal pytest plugin was added. About plugin. - Tests within a slice are grouped by test modules because not all of the tests within the module are independent from each other. - Slices are balanced by the number of tests within test module. - To run some module within its own environment there is a dedicated slice option (could help with extremely slow tests) Examples. - To split `test_cmdline` tests into 2 slices and run the first one: ipa-run-tests --slices=2 --slice-num=1 test_cmdline - To split tests into 2 slices, then to move one module out to its own slice and run the second one: ipa-run-tests --slices=2 --slice-dedicated=test_cmdline/test_cli.py \ --slice-num=2 test_cmdline Fixes: https://pagure.io/freeipa/issue/8008 Signed-off-by: Stanislav Levin <slev@altlinux.org> Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
2019-07-03 04:56:32 -05:00
(MODS_NUM, MODS_NUM, [[x] for x in range(MODS_NUM)]),
])
def test_slicing(ipatestdir, nslices, nslices_d, groups):
"""
Positive tests.
Run `nslices` slices, including `nslices_d` dedicated slices.
The `groups` is an expected result of slices grouping.
For example, there are 5 test modules. If one runs them in
two slices (without dedicated ones) the expected result will
be [[0, 2, 4], [1, 3]]. This means, that first slice will run
modules 0, 2, 4, second one - 1 and 3.
Another example, there are 5 test modules. We want to run them
in two slices. Also we specify module 0 as dedicated.
The expected result will be [[0], [1, 2, 3, 4]], which means, that
first slice will run module 0, second one - 1, 2, 3, 4.
If the given slice count is one, then this plugin does nothing.
"""
for sl in range(nslices):
args = [
"-v",
"--slices={}".format(nslices),
"--slice-num={}".format(sl + 1)
]
for dslice in range(nslices_d):
args.append(
"--slice-dedicated={}.py".format(MOD_NAME.format(dslice)))
result = ipatestdir.runpytest(*args)
assert result.ret == 0
result.assert_outcomes(passed=len(groups[sl]))
for mod_num in groups[sl]:
result.stdout.fnmatch_lines(["*{mod}.py::{func} PASSED*".format(
mod=MOD_NAME.format(mod_num),
func=FUNC_NAME.format(mod_num))])
@pytest.mark.parametrize(
"nslices,nslices_d,nslice,dmod,err_message",
[(2, 3, 1, None,
"Dedicated slice number({}) shouldn't be greater than"
" the number of slices({})".format(3, 2)),
(MODS_NUM, 0, MODS_NUM + 1, None,
"Slice number({}) shouldn't be greater than the number of slices"
"({})".format(
MODS_NUM + 1, MODS_NUM)),
(MODS_NUM + 1, 1, 1, None,
"Total number of slices({}) shouldn't be greater"
" than the number of Python test modules({})".format(
MODS_NUM + 1, MODS_NUM)),
(MODS_NUM, MODS_NUM, 1, "notexisted_module",
"The number of dedicated slices({}) should be equal to the "
"number of dedicated modules({})".format(
[], ["notexisted_module.py"])),
(MODS_NUM - 1, MODS_NUM - 1, 1, None,
"The total number of slices({}) is not sufficient to"
" run dedicated modules({}) as well as usual ones({})".format(
MODS_NUM - 1, MODS_NUM - 1, 1)),
])
def test_slicing_negative(ipatestdir, nslices, nslices_d, nslice, dmod,
err_message):
"""
Negative scenarios
"""
args = [
"-v",
"--slices={}".format(nslices),
"--slice-num={}".format(nslice)
]
if dmod is None:
for dslice in range(nslices_d):
args.append(
"--slice-dedicated={}.py".format(MOD_NAME.format(dslice)))
else:
args.append(
"--slice-dedicated={}.py".format(dmod))
result = ipatestdir.runpytest(*args)
assert result.ret == PYTEST_INTERNAL_ERROR
result.assert_outcomes()
result.stdout.fnmatch_lines(["*ValueError: {err_message}*".format(
err_message=glob.escape(err_message))])