Skip to content

Commit d98dce5

Browse files
Steap2448rosik
andauthored
Implement group parametrization (#163)
* Implement parametrization Co-authored-by: Yaroslav Dynnikov <[email protected]>
1 parent a7798e3 commit d98dce5

File tree

10 files changed

+558
-11
lines changed

10 files changed

+558
-11
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## Unreleased
44

55
- Repeat `_each` and `_test` hooks when `--repeat` is specified
6+
- Add group parametrization
67

78
## 0.5.4
89

README.rst

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,20 @@ Define tests.
7373
g.test_example_2 = function() ... end
7474
g.test_example_m = function() ... end
7575
76+
-- Define parametrized groups
77+
local pg = t.group('pgroup', {{engine = 'memtx'}, {engine = 'vinyl'}})
78+
pg.test_example_3 = function(cg)
79+
-- Use cg.params here
80+
box.schema.space.create('test', {
81+
engine = cg.params.engine,
82+
})
83+
end
84+
85+
-- Hooks can be specified for one parameter
86+
pg.before_all({engine = 'memtx'}, function() ... end)
87+
pg.before_each({engine = 'memtx'}, function() ... end)
88+
pg.before_test('test_example_3', {engine = 'vinyl'}, function() ... end)
89+
7690
7791
Run them.
7892

@@ -232,6 +246,62 @@ Capturing output
232246
By default runner captures all stdout/stderr output and shows it only for failed tests.
233247
Capturing can be disabled with ``-c`` flag.
234248

249+
.. _parametrization:
250+
251+
---------------------------------
252+
Parametrization
253+
---------------------------------
254+
255+
Test group can be parametrized.
256+
257+
.. code-block:: Lua
258+
259+
local g = t.group('pgroup', {{a = 1, b = 4}, {a = 2, b = 3}})
260+
261+
g.test_params = function(cg)
262+
...
263+
log.info('a = %s', cg.params.a)
264+
log.info('b = %s', cg.params.b)
265+
...
266+
end
267+
268+
Group can be parametrized with a matrix of parameters using `luatest.helpers`:
269+
270+
.. code-block:: Lua
271+
272+
local g = t.group('pgroup', t.helpers.matrix({a = {1, 2}, b = {3, 4}}))
273+
-- Will run:
274+
-- * a = 1, b = 3
275+
-- * a = 1, b = 4
276+
-- * a = 2, b = 3
277+
-- * a = 2, b = 4
278+
279+
Each test will be performed for every params combination. Hooks will work as usual
280+
unless there are specified params. The order of execution in the hook group is
281+
determined by the order of declaration.
282+
283+
.. code-block:: Lua
284+
285+
-- called before every test
286+
g.before_each(function(cg) ... end)
287+
288+
-- called before tests when a == 1
289+
g.before_each({a = 1}, function(cg) ... end)
290+
291+
-- called only before the test when a == 1 and b == 3
292+
g.before_each({a = 1, b = 3}, function(cg) ... end)
293+
294+
-- called before test named 'test_something' when a == 1
295+
g.before_test('test_something', {a = 1}, function(cg) ... end)
296+
297+
--etc
298+
299+
Test with specific params can be called from command line.
300+
301+
.. code-block:: Bash
302+
303+
luatest path-to-file pgroup.a:1.b:3.test_params
304+
235305
.. _test-helpers:
236306

237307
---------------------------------

luatest/helpers.lua

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,43 @@ function helpers.retrying(config, fn, ...)
7575
end
7676
end
7777

78+
--- Return all combinations of parameters.
79+
-- Accepts params' names and thier every possible value.
80+
--
81+
-- helpers.matrix({a = {1, 2}, b = {3, 4}})
82+
--
83+
-- {
84+
-- {a = 1, b = 3},
85+
-- {a = 2, b = 3},
86+
-- {a = 1, b = 4},
87+
-- {a = 2, b = 4},
88+
-- }
89+
--
90+
-- @tab parameters_values
91+
function helpers.matrix(parameters_values)
92+
local combinations = {}
93+
94+
local params_names = {}
95+
for name, _ in pairs(parameters_values) do
96+
table.insert(params_names, name)
97+
end
98+
table.sort(params_names)
99+
100+
local function combinator(_params, ind, ...)
101+
if ind < 1 then
102+
local combination = {}
103+
for _, entry in ipairs({...}) do
104+
combination[entry[1]] = entry[2]
105+
end
106+
table.insert(combinations, combination)
107+
else
108+
local name = params_names[ind]
109+
for i=1,#(_params[name]) do combinator(_params, ind - 1, {name, _params[name][i]}, ...) end
110+
end
111+
end
112+
113+
combinator(parameters_values, #params_names)
114+
return combinations
115+
end
116+
78117
return helpers

luatest/hooks.lua

Lines changed: 62 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,45 @@
11
local utils = require('luatest.utils')
2+
local comparator = require('luatest.comparator')
23

34
local export = {}
45

6+
local function check_params(required, actual)
7+
for param_name, param_val in pairs(required) do
8+
if not comparator.equals(param_val, actual[param_name]) then
9+
return false
10+
end
11+
end
12+
13+
return true
14+
end
15+
516
local function define_hooks(object, hooks_type)
617
local hooks = {}
718
object[hooks_type .. '_hooks'] = hooks
819

9-
object[hooks_type] = function(fn)
10-
table.insert(hooks, fn)
20+
object[hooks_type] = function(...)
21+
local params, fn = ...
22+
if fn == nil then
23+
fn = params
24+
params = {}
25+
end
26+
27+
assert(type(params) == 'table',
28+
string.format('params should be table, got %s', type(params)))
29+
assert(type(fn) == 'function',
30+
string.format('hook should be function, got %s', type(fn)))
31+
32+
params = params or {}
33+
table.insert(hooks, {fn, params})
1134
end
1235
object['_original_' .. hooks_type] = object[hooks_type] -- for leagacy hooks support
1336

1437
object['run_' .. hooks_type] = function()
15-
for _, fn in ipairs(hooks) do
16-
fn()
38+
local active_hooks = object[hooks_type .. '_hooks']
39+
for _, hook in ipairs(active_hooks) do
40+
if check_params(hook[2], object.params) then
41+
hook[1](object)
42+
end
1743
end
1844
end
1945
end
@@ -22,22 +48,49 @@ local function define_named_hooks(object, hooks_type)
2248
local hooks = {}
2349
object[hooks_type .. '_hooks'] = hooks
2450

25-
object[hooks_type] = function(test_name, fn)
51+
object[hooks_type] = function(...)
52+
local test_name, params, fn = ...
53+
if fn == nil then
54+
fn = params
55+
params = {}
56+
end
57+
58+
assert(type(test_name) == 'string',
59+
string.format('test name should be string, got %s', type(test_name)))
60+
assert(type(params) == 'table',
61+
string.format('params should be table, got %s', type(params)))
62+
assert(type(fn) == 'function',
63+
string.format('hook should be function, got %s', type(fn)))
64+
2665
test_name = object.name .. '.' .. test_name
66+
params = params or {}
2767
if not hooks[test_name] then
2868
hooks[test_name] = {}
2969
end
30-
table.insert(hooks[test_name], fn)
70+
table.insert(hooks[test_name], {fn, params})
3171
end
3272

3373
object['run_' .. hooks_type] = function(test)
74+
local active_hooks = object[hooks_type .. '_hooks']
3475
local test_name = test.name
35-
if not hooks[test_name] then
76+
77+
-- When parametrized groups are defined named hooks saved by
78+
-- super group test name. When they are called test name is
79+
-- specific to the parametrized group. So, it should be
80+
-- converted back to the super one.
81+
if object.super_group then
82+
local test_name_parts, parts_amount = utils.split_test_name(test_name)
83+
test_name = object.super_group.name .. '.' .. test_name_parts[parts_amount]
84+
end
85+
86+
if not active_hooks[test_name] then
3687
return
3788
end
3889

39-
for _, fn in ipairs(hooks[test_name]) do
40-
fn()
90+
for _, hook in ipairs(active_hooks[test_name]) do
91+
if check_params(hook[2], object.params) then
92+
hook[1](object)
93+
end
4194
end
4295
end
4396
end

luatest/init.lua

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ luatest.Server = require('luatest.server')
1717

1818
local Group = require('luatest.group')
1919
local hooks = require('luatest.hooks')
20+
local parametrizer = require('luatest.parametrizer')
2021

2122
--- Add before suite hook.
2223
--
@@ -34,15 +35,29 @@ luatest.groups = {}
3435
--- Create group of tests.
3536
--
3637
-- @string[opt] name
38+
-- @table[opt] params
3739
-- @return Group object
3840
-- @see luatest.group
39-
function luatest.group(name)
41+
function luatest.group(name, params)
4042
local group = Group:new(name)
4143
name = group.name
4244
if luatest.groups[name] then
4345
error('Test group already exists: ' .. name ..'.')
4446
end
45-
luatest.groups[name] = group
47+
48+
if params then
49+
parametrizer.parametrize(group, params)
50+
end
51+
52+
-- Register all parametrized groups
53+
if group.pgroups then
54+
for _, pgroup in ipairs(group.pgroups) do
55+
luatest.groups[pgroup.name] = pgroup
56+
end
57+
else
58+
luatest.groups[name] = group
59+
end
60+
4661
return group
4762
end
4863

luatest/parametrizer.lua

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
local checks = require('checks')
2+
3+
local Group = require('luatest.group')
4+
local pp = require('luatest.pp')
5+
6+
local export = {}
7+
8+
local function get_group_name(params)
9+
local params_names = {}
10+
for name, _ in pairs(params) do
11+
table.insert(params_names, name)
12+
end
13+
table.sort(params_names)
14+
15+
for ind, param_name in ipairs(params_names) do
16+
params_names[ind] = param_name .. ':' .. pp.tostring(params[param_name])
17+
end
18+
return table.concat(params_names, ".")
19+
end
20+
21+
local function redefine_hooks(group, hooks_type)
22+
-- Super group shares its hooks with pgroups
23+
for _, pgroup in ipairs(group.pgroups) do
24+
pgroup[hooks_type .. '_hooks'] = group[hooks_type .. '_hooks']
25+
end
26+
end
27+
28+
local function redefine_pgroups_hooks(group)
29+
redefine_hooks(group, 'before_each')
30+
redefine_hooks(group, 'after_each')
31+
redefine_hooks(group, 'before_all')
32+
redefine_hooks(group, 'after_all')
33+
34+
redefine_hooks(group, 'before_test')
35+
redefine_hooks(group, 'after_test')
36+
end
37+
38+
local function redirect_index(group)
39+
local super_group_mt = getmetatable(group)
40+
if super_group_mt.__newindex then
41+
return
42+
end
43+
44+
super_group_mt.__newindex = function(_group, key, value)
45+
if _group.pgroups then
46+
for _, pgroup in ipairs(_group.pgroups) do
47+
pgroup[key] = value
48+
end
49+
else
50+
rawset(_group, key, value)
51+
end
52+
end
53+
end
54+
55+
function export.parametrize(object, parameters_combinations)
56+
checks('table', 'table')
57+
-- Validate params' name and values
58+
local counter = 0
59+
for _, _ in pairs(parameters_combinations) do
60+
counter = counter + 1
61+
assert(parameters_combinations[counter] ~= nil,
62+
'parameters_combinations should be a contiguous array')
63+
64+
assert(type(parameters_combinations[counter]) == 'table',
65+
string.format('parameters_combinations\' entry should be table, got %s',
66+
type(parameters_combinations[counter])))
67+
68+
for parameter_name, _ in pairs(parameters_combinations[counter]) do
69+
assert(type(parameter_name) == 'string',
70+
string.format('parameter name should be string, got %s', type(parameter_name)))
71+
end
72+
end
73+
74+
-- Create a subgroup on every param combination
75+
object.pgroups = {}
76+
for _, pgroup_params in ipairs(parameters_combinations) do
77+
local pgroup_name = get_group_name(pgroup_params)
78+
local pgroup = Group:new(object.name .. '.' .. pgroup_name)
79+
pgroup.params = pgroup_params
80+
81+
pgroup.super_group = object
82+
table.insert(object.pgroups, pgroup)
83+
end
84+
85+
redirect_index(object)
86+
redefine_pgroups_hooks(object)
87+
end
88+
89+
return export

luatest/utils.lua

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,4 +120,17 @@ function utils.pattern_filter(patterns, expr)
120120
return result
121121
end
122122

123+
function utils.split_test_name(test_name)
124+
local test_name_parts = string.split(test_name, '.')
125+
return test_name_parts, #test_name_parts
126+
end
127+
128+
function utils.table_len(t)
129+
local counter = 0
130+
for _, _ in pairs(t) do
131+
counter = counter + 1
132+
end
133+
return counter
134+
end
135+
123136
return utils

test/fixtures/parametrized.lua

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
local t = require('luatest')
2+
local g = t.group('parametrized_fixture', t.helpers.matrix{a = {1, 2, 3}, b = {4, 5, 6}})
3+
4+
g.test_something = function(cg)
5+
t.assert_not_equals(cg.params.a, 3)
6+
end

0 commit comments

Comments
 (0)