Skip to content

Commit 82e8777

Browse files
Oleg Chaplashkinylobankov
Oleg Chaplashkin
authored andcommitted
Migrate treegen module and adapt it
The original `treegen` module (path: tarantool/test/treegen.lua) has been moved to the current project with the following changes: - refactoring; - updated documentation. Closes #364
1 parent 13ca5a6 commit 82e8777

File tree

5 files changed

+273
-11
lines changed

5 files changed

+273
-11
lines changed

Diff for: CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
- Make `assert_error_*` additionally check error trace if required.
1414
- Add `--list-test-cases` and `--run-test-case` CLI options.
1515
- Introduce preloaded hooks (gh-380).
16+
- Add `treegen` helper as a tree generator (gh-364).
1617

1718
## 1.0.1
1819

Diff for: README.rst

+10-10
Original file line numberDiff line numberDiff line change
@@ -165,14 +165,14 @@ Preloaded hooks extend base hooks. They behave like the pytest fixture with the
165165
-- my_helper.lua
166166
local hooks = require('luatest.hooks')
167167
168-
hooks.before_all_preloaded(function() print('start foo') end)
169-
hooks.after_all_preloaded(function() print('stop foo') end)
168+
hooks.before_suite_preloaded(function() print('start foo') end)
169+
hooks.after_suite_preloaded(function() print('stop foo') end)
170170
171-
hooks.before_each_preloaded(function() print('start bar') end)
172-
hooks.after_each_preloaded(function() print('stop bar') end)
171+
hooks.before_all_preloaded(function() print('start bar') end)
172+
hooks.after_all_preloaded(function() print('stop bar') end)
173173
174-
hooks.before_all_preloaded(function() print('start baz') end)
175-
hooks.after_all_preloaded(function() print('stop baz') end)
174+
hooks.before_each_preloaded(function() print('start baz') end)
175+
hooks.after_each_preloaded(function() print('stop baz') end)
176176
177177
If you run the following test:
178178

@@ -190,13 +190,13 @@ Then the hooks are executed in the following sequence:
190190

191191
.. code-block:: text
192192
|\ start foo
193-
| \ start baz
193+
| \ start bar
194194
| \ prepare
195-
| \ start bar
195+
| \ start baz
196196
| test_print (everythings is ok)
197-
| / stop bar
197+
| / stop baz
198198
| / cleanup
199-
| / stop baz
199+
| / stop bar
200200
|/ stop foo
201201
202202
---------------------------------

Diff for: config.ld

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ file = {
99
'luatest/replica_set.lua',
1010
'luatest/justrun.lua',
1111
'luatest/cbuilder.lua',
12-
'luatest/hooks.lua'
12+
'luatest/hooks.lua',
13+
'luatest/treegen.lua'
1314
}
1415
topics = {
1516
'CHANGELOG.md',

Diff for: luatest/treegen.lua

+212
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
--- Working tree generator.
2+
--
3+
-- Generates a tree of Lua files using provided templates and
4+
-- filenames.
5+
--
6+
-- @usage
7+
--
8+
-- local t = require('luatest')
9+
-- local treegen = require('luatest.treegen')
10+
--
11+
-- local g = t.group()
12+
--
13+
-- g.test_foo = function(g)
14+
-- treegen.add_template('^.*$', 'test_script')
15+
-- local dir = treegen.prepare_directory({'foo/bar.lua', 'main.lua'})
16+
-- ...
17+
-- end
18+
--
19+
-- @module luatest.treegen
20+
21+
local hooks = require('luatest.hooks')
22+
23+
local fio = require('fio')
24+
local fun = require('fun')
25+
local checks = require('checks')
26+
27+
local log = require('luatest.log')
28+
29+
local treegen = {
30+
_group = {}
31+
}
32+
33+
local function find_template(group, script)
34+
for position, template_def in ipairs(group._treegen.templates) do
35+
if script:match(template_def.pattern) then
36+
return position, template_def.template
37+
end
38+
end
39+
error(("treegen: can't find a template for script %q"):format(script))
40+
end
41+
42+
--- Write provided content into the given directory.
43+
--
44+
-- @string directory Directory where the content will be created.
45+
-- @string filename File to write (possible nested path: /foo/bar/main.lua).
46+
-- @string content The body to write.
47+
-- @return string
48+
function treegen.write_file(directory, filename, content)
49+
checks('string', 'string', 'string')
50+
local content_abspath = fio.pathjoin(directory, filename)
51+
local flags = {'O_CREAT', 'O_WRONLY', 'O_TRUNC'}
52+
local mode = tonumber('644', 8)
53+
54+
local contentdir_abspath = fio.dirname(content_abspath)
55+
log.info('Creating a directory: %s', contentdir_abspath)
56+
fio.mktree(contentdir_abspath)
57+
58+
log.info('Writing a content: %s', content_abspath)
59+
local fh = fio.open(content_abspath, flags, mode)
60+
fh:write(content)
61+
fh:close()
62+
return content_abspath
63+
end
64+
65+
-- Generate a content that follows a template and write it at the
66+
-- given path in the given directory.
67+
--
68+
-- @table group Group of tests.
69+
-- @string directory Directory where the content will be created.
70+
-- @string filename File to write (possible nested path: /foo/bar/main.lua).
71+
-- @table replacements List of replacement templates.
72+
-- @return string
73+
local function gen_content(group, directory, filename, replacements)
74+
checks('table', 'string', 'string', 'table')
75+
local _, template = find_template(group, filename)
76+
replacements = fun.chain({filename = filename}, replacements):tomap()
77+
local body = template:gsub('<(.-)>', replacements)
78+
return treegen.write_file(directory, filename, body)
79+
end
80+
81+
--- Initialize treegen module in the given group of tests.
82+
--
83+
-- @tab group Group of tests.
84+
local function init(group)
85+
checks('table')
86+
group._treegen = {
87+
tempdirs = {},
88+
templates = {}
89+
}
90+
treegen._group = group
91+
end
92+
93+
--- Remove all temporary directories created by the test
94+
-- unless KEEP_DATA environment variable is set to a
95+
-- non-empty value.
96+
local function clean()
97+
if treegen._group._treegen == nil then
98+
return
99+
end
100+
101+
local dirs = table.copy(treegen._group._treegen.tempdirs) or {}
102+
treegen._group._treegen.tempdirs = nil
103+
104+
local keep_data = (os.getenv('KEEP_DATA') or '') ~= ''
105+
106+
for _, dir in ipairs(dirs) do
107+
if keep_data then
108+
log.info('Left intact due to KEEP_DATA env var: %s', dir)
109+
else
110+
log.info('Recursively removing: %s', dir)
111+
fio.rmtree(dir)
112+
end
113+
end
114+
115+
treegen._group._treegen.templates = nil
116+
end
117+
118+
--- Save the template with the given pattern.
119+
--
120+
-- @string pattern File name template
121+
-- @string template A content template for creating a file.
122+
function treegen.add_template(pattern, template)
123+
checks('string', 'string')
124+
table.insert(treegen._group._treegen.templates, {
125+
pattern = pattern,
126+
template = template,
127+
})
128+
end
129+
130+
--- Remove the template by pattern.
131+
--
132+
-- @string pattern File name template
133+
function treegen.remove_template(pattern)
134+
checks('string')
135+
local is_found, position, _ = pcall(find_template, treegen._group, pattern)
136+
if is_found then
137+
table.remove(treegen._group._treegen.templates, position)
138+
end
139+
end
140+
141+
--- Create a temporary directory with given contents.
142+
--
143+
-- The contents are generated using templates added by
144+
-- treegen.add_template().
145+
--
146+
-- @usage
147+
--
148+
-- Example for {'foo/bar.lua', 'baz.lua'}:
149+
--
150+
-- /
151+
-- + tmp/
152+
-- + rfbWOJ/
153+
-- + foo/
154+
-- | + bar.lua
155+
-- + baz.lua
156+
--
157+
-- The return value is '/tmp/rfbWOJ' for this example.
158+
--
159+
-- @tab contents List of bodies of the content to write.
160+
-- @tab[opt] replacements List of replacement templates.
161+
-- @return string
162+
function treegen.prepare_directory(contents, replacements)
163+
checks('?table', '?table')
164+
replacements = replacements or {}
165+
166+
local dir = fio.tempdir()
167+
168+
-- fio.tempdir() follows the TMPDIR environment variable.
169+
-- If it ends with a slash, the return value contains a double
170+
-- slash in the middle: for example, if TMPDIR=/tmp/, the
171+
-- result is like `/tmp//rfbWOJ`.
172+
--
173+
-- It looks harmless on the first glance, but this directory
174+
-- path may be used later to form an URI for a Unix domain
175+
-- socket. As result the URI looks like
176+
-- `unix/:/tmp//rfbWOJ/instance-001.iproto`.
177+
--
178+
-- It confuses net_box.connect(): it reports EAI_NONAME error
179+
-- from getaddrinfo().
180+
--
181+
-- It seems, the reason is a peculiar of the URI parsing:
182+
--
183+
-- tarantool> uri.parse('unix/:/foo/bar.iproto')
184+
-- ---
185+
-- - host: unix/
186+
-- service: /foo/bar.iproto
187+
-- unix: /foo/bar.iproto
188+
-- ...
189+
--
190+
-- tarantool> uri.parse('unix/:/foo//bar.iproto')
191+
-- ---
192+
-- - host: unix
193+
-- path: /foo//bar.iproto
194+
-- ...
195+
--
196+
-- Let's normalize the path using fio.abspath(), which
197+
-- eliminates the double slashes.
198+
dir = fio.abspath(dir)
199+
200+
table.insert(treegen._group._treegen.tempdirs, dir)
201+
202+
for _, content in ipairs(contents) do
203+
gen_content(treegen._group, dir, content, replacements)
204+
end
205+
206+
return dir
207+
end
208+
209+
hooks.before_all_preloaded(init)
210+
hooks.after_all_preloaded(clean)
211+
212+
return treegen

Diff for: test/treegen_test.lua

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
local t = require('luatest')
2+
local fio = require('fio')
3+
4+
local treegen = require('luatest.treegen')
5+
6+
local g = t.group()
7+
8+
9+
local function assert_file_content_equals(file, expected)
10+
local fh = fio.open(file)
11+
t.assert_equals(fh:read(), expected)
12+
end
13+
14+
g.test_prepare_directory = function()
15+
treegen.add_template('^.*$', 'test_script')
16+
local dir = treegen.prepare_directory({'foo/bar.lua', 'baz.lua'})
17+
18+
t.assert(fio.path.is_dir(dir))
19+
t.assert(fio.path.exists(dir))
20+
21+
t.assert(fio.path.exists(fio.pathjoin(dir, 'foo', 'bar.lua')))
22+
t.assert(fio.path.exists(fio.pathjoin(dir, 'baz.lua')))
23+
24+
assert_file_content_equals(fio.pathjoin(dir, 'foo', 'bar.lua'), 'test_script')
25+
assert_file_content_equals(fio.pathjoin(dir, 'baz.lua'), 'test_script')
26+
end
27+
28+
g.before_test('test_clean_keep_data', function()
29+
treegen.add_template('^.*$', 'test_script')
30+
31+
os.setenv('KEEP_DATA', 'true')
32+
33+
g.dir = treegen.prepare_directory(g, {'foo.lua'})
34+
35+
t.assert(fio.path.is_dir(g.dir))
36+
t.assert(fio.path.exists(g.dir))
37+
end)
38+
39+
g.test_clean_keep_data = function()
40+
t.assert(fio.path.is_dir(g.dir))
41+
t.assert(fio.path.exists(g.dir))
42+
end
43+
44+
g.after_test('test_clean_keep_data', function()
45+
os.setenv('KEEP_DATA', '')
46+
t.assert(fio.path.is_dir(g.dir))
47+
t.assert(fio.path.exists(g.dir))
48+
end)

0 commit comments

Comments
 (0)