Skip to content

Commit 0d58bc5

Browse files
committed
Implement parametrization
1 parent a7798e3 commit 0d58bc5

File tree

10 files changed

+541
-10
lines changed

10 files changed

+541
-10
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: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,18 @@ 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', t.helpers.matrix({param_1 = {1, 2}, param_2 = {3, 4}}))
78+
pg.test_example_1 = function(cg) ... end
79+
-- type(cg.params.param_1) == 'number'
80+
pg.test_example_n = function(cg) ... end
81+
82+
-- Hooks can be specified for one parameter
83+
pg.before_all(function() ... end)
84+
pg.before_each({param_1 = 1}, function() ... end)
85+
pg.after_each({param_2 = 3}, function() ... end)
86+
pg.after_test('test_example_1', {param_1 = 2, param_2 = 4}, function() ... end)
87+
7688
7789
Run them.
7890

@@ -232,6 +244,58 @@ Capturing output
232244
By default runner captures all stdout/stderr output and shows it only for failed tests.
233245
Capturing can be disabled with ``-c`` flag.
234246

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

237301
---------------------------------

luatest/helpers.lua

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,41 @@ 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+
-- {{a = 1, b = 3},
84+
-- {a = 2, b = 3},
85+
-- {a = 1, b = 4},
86+
-- {a = 2, b = 4}}
87+
--
88+
-- @tab params
89+
function helpers.matrix(params)
90+
local combinations = {}
91+
92+
local params_names = {}
93+
for name, _ in pairs(params) do
94+
table.insert(params_names, name)
95+
end
96+
table.sort(params_names)
97+
98+
local function combinator(_params, ind, ...)
99+
if ind < 1 then
100+
local combination = {}
101+
for _, entry in ipairs({...}) do
102+
combination[entry[1]] = entry[2]
103+
end
104+
table.insert(combinations, combination)
105+
else
106+
local name = params_names[ind]
107+
for i=1,#(_params[name]) do combinator(_params, ind - 1, {name, _params[name][i]}, ...) end
108+
end
109+
end
110+
111+
combinator(params, #params_names)
112+
return combinations
113+
end
114+
78115
return helpers

luatest/hooks.lua

Lines changed: 61 additions & 8 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 hooks = object[hooks_type .. '_hooks']
39+
for _, hook in ipairs(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 hooks = object[hooks_type .. '_hooks']
3475
local test_name = test.name
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+
3586
if not 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(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: 16 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
--
@@ -36,13 +37,26 @@ luatest.groups = {}
3637
-- @string[opt] name
3738
-- @return Group object
3839
-- @see luatest.group
39-
function luatest.group(name)
40+
function luatest.group(name, params)
4041
local group = Group:new(name)
4142
name = group.name
4243
if luatest.groups[name] then
4344
error('Test group already exists: ' .. name ..'.')
4445
end
45-
luatest.groups[name] = group
46+
47+
if params then
48+
parametrizer.parametrize(group, params)
49+
end
50+
51+
-- Register all parametrized groups
52+
if group.pgroups then
53+
for _, pgroup in ipairs(group.pgroups) do
54+
luatest.groups[pgroup.name] = pgroup
55+
end
56+
else
57+
luatest.groups[name] = group
58+
end
59+
4660
return group
4761
end
4862

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, params)
56+
checks('table', 'table')
57+
-- Validate params' name and values
58+
local counter = 0
59+
for _, parameter_combination in pairs(params) do
60+
counter = counter + 1
61+
assert(params[counter] ~= nil,
62+
'Params should be a contiguous array')
63+
64+
assert(type(params[counter]) == 'table',
65+
string.format('Params\' entry should be table, got %s', type(params[counter])))
66+
67+
for parameter_name, _ in pairs(params[counter]) do
68+
assert(type(parameter_name) == 'string',
69+
string.format('Parameter name should be string, got %s', type(parameter_name)))
70+
end
71+
end
72+
object.params = params
73+
74+
-- Create a subgroup on every param combination
75+
object.pgroups = {}
76+
for _, pgroup_params in ipairs(params) 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)