Skip to content

Commit 9e117e6

Browse files
committed
Improve luatest auto-requiring in Server:exec
In the commit 58eae75 auto-requiring of the 'luatest' lib was added to the `Server:eval()` function, so it was also available in `Server:exec()`, and it was defined as `t` variable. In the process of development we found that the following construction doesn't work due to the upvalue error: -- foo_bar_test.lua local t = require('luatest') ... g.test_foo_bar = function() server:exec(function() t.assert_equals(1, 1) -- upvalues exception (!) end) end To fix this error we decided to make the global variable `t` that had the 'luatest' lib already loaded. So there was no need to import the 'luatest' lib in the test files and the following example worked: -- foo_bar_test.lua -- local t = require('luatest') -- `t` is already defined ... g.test_foo_bar = function() server:exec(function() t.assert_equals(1, 1) -- it works end) end: But such an approach is not explicit enough and may mislead developers. So it was decided to improve and extend the existing mechanism. Now we just make the needed upvalue with the 'luatest' lib inside available in the `Server:exec()` function via some magic. And now we can do something like this: -- foo_bar_test.lua local t1 = require('luatest') local t2 = require('luatest') ... g.test_foo_bar_1 = function() server:exec(function() t1.assert_equals(1, 1) -- it works end) end g.test_foo_bar_2 = function() server:exec(function() t2.assert_equals(1, 1) -- it works end) end Unfortunately, we cannot do the same for the `Server:eval()` function and now it becomes just a shortcut for `Server.net_box:eval()` as it was before the first version of auto-requiring of the 'luatest' lib. Follows up #277 In scope of #233
1 parent 8de38b3 commit 9e117e6

File tree

6 files changed

+54
-59
lines changed

6 files changed

+54
-59
lines changed

.luacheckrc

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
include_files = {"**/*.lua", "*.rockspec", "*.luacheckrc"}
22
exclude_files = {"lua_modules/", ".luarocks/", ".rocks/", "tmp/"}
33

4-
globals = {"t"}
5-
64
max_line_length = 120

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@
4141
- Print Tarantool version used by luatest.
4242
- Add new module `replica_proxy.lua`.
4343
- Add new module `tarantool.lua`.
44-
- Autorequire `luatest` module in the server instance as `t` variable.
44+
- Auto-require `luatest` module in `Server:exec()` function where it is available
45+
via the corresponding upvalue.
4546

4647
## 0.5.7
4748

luatest/init.lua

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,4 @@ function luatest.defaults(...)
9595
return luatest.configure(...)
9696
end
9797

98-
if not rawget(_G, 't') then
99-
rawset(_G, 't', luatest)
100-
end
101-
10298
return luatest

luatest/server.lua

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -472,25 +472,15 @@ end
472472

473473
--- Evaluate Lua code on the server.
474474
--
475-
-- This is a wrapper for `server.net_box:eval()`.
475+
-- This is a shortcut for `server.net_box:eval()`.
476476
-- @string code
477477
-- @tab[opt] args
478478
-- @tab[opt] options
479479
function Server:eval(code, ...)
480480
if self.net_box == nil then
481481
error('net_box is not connected', 2)
482482
end
483-
local preamble = [[
484-
if not rawget(_G, 't') then
485-
local is_ok, _t = pcall(require, 'luatest')
486-
if is_ok then
487-
rawset(_G, 't', _t)
488-
else
489-
require('log').warn('LUA_PATH is unset or incorrect, ' .. _t)
490-
end
491-
end
492-
]]
493-
return self.net_box:eval(preamble .. code, ...)
483+
return self.net_box:eval(code, ...)
494484
end
495485

496486
--- Call remote function on the server by name.
@@ -535,8 +525,9 @@ end
535525
-- end, {1, 2})
536526
-- -- sum == 3
537527
--
528+
-- local t = require('luatest')
538529
-- server:exec(function()
539-
-- -- luatest is always available as `t`
530+
-- -- luatest is available via `t` upvalue
540531
-- t.assert_equals(math.pi, 3)
541532
-- end)
542533
-- -- mytest.lua:12: expected: 3, actual: 3.1415926535898
@@ -547,24 +538,45 @@ function Server:exec(fn, args, options)
547538
error('net_box is not connected', 2)
548539
end
549540

550-
local ups = utils.upvalues(fn)
551-
if next(ups) ~= nil then
541+
local autorequired_pkgs = {'luatest'}
542+
local passthrough_ups = {}
543+
local other_ups = {}
544+
for i = 1, debug.getinfo(fn, 'u').nups do
545+
local name, value = debug.getupvalue(fn, i)
546+
for _, pkg_name in ipairs(autorequired_pkgs) do
547+
if value == package.loaded[pkg_name] then
548+
passthrough_ups[name] = pkg_name
549+
break
550+
end
551+
end
552+
if not passthrough_ups[name] then
553+
table.insert(other_ups, name)
554+
end
555+
end
556+
557+
if next(other_ups) ~= nil then
552558
local err = string.format(
553559
'bad argument #2 to exec (excess upvalues: %s)',
554-
table.concat(ups, ', ')
560+
table.concat(other_ups, ', ')
555561
)
556562
error(err, 2)
557563
end
558564

559-
return exec_tail(self:eval([[
560-
local dump, args = ...
565+
return exec_tail(self.net_box:eval([[
566+
local dump, args, passthrough_ups = ...
561567
local fn = loadstring(dump)
568+
for i = 1, debug.getinfo(fn, 'u').nups do
569+
local name, _ = debug.getupvalue(fn, i)
570+
if passthrough_ups[name] then
571+
debug.setupvalue(fn, i, require(passthrough_ups[name]))
572+
end
573+
end
562574
if args == nil then
563575
return pcall(fn)
564576
else
565577
return pcall(fn, unpack(args))
566578
end
567-
]], {string.dump(fn), args}, options))
579+
]], {string.dump(fn), args, passthrough_ups}, options))
568580
end
569581

570582
function Server:coverage(action)

test/autorequire_luatest_test.lua

Lines changed: 19 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
local fio = require('fio')
2+
local t = require('luatest')
23

34
local g = t.group()
45
local Server = t.Server
@@ -14,8 +15,7 @@ g.before_all(function()
1415
command = command,
1516
workdir = fio.pathjoin(datadir, 'common'),
1617
env = {
17-
LUA_PATH =
18-
root .. '/?.lua;' ..
18+
LUA_PATH = root .. '/?.lua;' ..
1919
root .. '/?/init.lua;' ..
2020
root .. '/.rocks/share/tarantool/?.lua'
2121
},
@@ -37,29 +37,35 @@ g.after_all(function()
3737
fio.rmtree(datadir)
3838
end)
3939

40-
g.test_exec_without_t = function()
40+
g.test_exec_without_upvalue = function()
4141
local actual = g.server:exec(function()
4242
return 1 + 1
4343
end)
4444
t.assert_equals(actual, 2)
4545
end
4646

47-
g.test_exec_with_global_variable = function()
47+
g.test_exec_with_upvalue = function()
4848
g.server:exec(function()
4949
t.assert_equals(1, 1)
5050
end)
5151
t.assert_equals(1, 1)
52+
53+
local lt = require('luatest')
54+
g.server:exec(function()
55+
lt.assert_equals(1, 1)
56+
end)
57+
lt.assert_equals(1, 1)
5258
end
5359

5460
g.test_exec_with_local_variable = function()
5561
g.server:exec(function()
56-
local t = require('luatest')
62+
local t = require('luatest') -- luacheck: ignore 431
5763
t.assert_equals(1, 1)
5864
end)
5965
t.assert_equals(1, 1)
6066
end
6167

62-
g.test_exec_with_local_duplicate = function()
68+
g.test_exec_with_upvalue_and_local_variable = function()
6369
g.server:exec(function()
6470
local tt = require('luatest')
6571
t.assert_equals(1, 1)
@@ -68,24 +74,11 @@ g.test_exec_with_local_duplicate = function()
6874
end)
6975
end
7076

71-
g.test_eval_with_t = function()
72-
local actual = g.server:eval([[
73-
t.assert_equals(1, 1)
74-
return 1
75-
]])
76-
t.assert_equals(actual, 1)
77-
end
78-
79-
g.before_test('test_exec_when_lua_path_is_unset', function()
77+
g.before_test('test_exec_when_luatest_not_found', function()
8078
-- Setup custom server without LUA_PATH variable
81-
local workdir = fio.tempdir()
82-
local log = fio.pathjoin(workdir, 'bad_env_server.log')
8379
g.bad_env_server = Server:new({
8480
command = command,
85-
workdir = workdir,
86-
env = {
87-
TARANTOOL_LOG = log
88-
},
81+
workdir = fio.tempdir(),
8982
http_port = 8183,
9083
net_box_port = 3134,
9184
})
@@ -101,16 +94,13 @@ g.before_test('test_exec_when_lua_path_is_unset', function()
10194
g.bad_env_server:connect_net_box()
10295
end)
10396

104-
g.test_exec_when_lua_path_is_unset = function()
105-
g.bad_env_server:exec(function() return 1 + 1 end)
106-
107-
t.assert(
108-
g.bad_env_server:grep_log(
109-
"W> LUA_PATH is unset or incorrect, module 'luatest' not found"
110-
)
97+
g.test_exec_when_luatest_not_found = function()
98+
t.assert_error_msg_contains(
99+
"module 'luatest' not found:", g.bad_env_server.exec, g.bad_env_server,
100+
function() t.assert_equals(1, 1) end
111101
)
112102
end
113103

114-
g.after_test('test_exec_when_lua_path_is_unset', function()
104+
g.after_test('test_exec_when_luatest_not_found', function()
115105
g.bad_env_server:drop()
116106
end)

test/server_instance.lua

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,13 @@ local json = require('json')
55
local workdir = os.getenv('TARANTOOL_WORKDIR')
66
local listen = os.getenv('TARANTOOL_LISTEN')
77
local http_port = os.getenv('TARANTOOL_HTTP_PORT')
8-
local log = os.getenv('TARANTOOL_LOG')
98

109
local httpd = require('http.server').new('0.0.0.0', http_port)
1110

12-
box.cfg({work_dir = workdir, log = log})
13-
box.schema.user.grant('guest', 'super', nil, nil, {if_not_exists=true})
11+
box.cfg({work_dir = workdir})
12+
box.schema.user.grant('guest', 'super', nil, nil, {if_not_exists = true})
1413
box.cfg({listen = listen})
1514

16-
1715
httpd:route({path = '/ping', method = 'GET'}, function()
1816
return {status = 200, body = 'pong'}
1917
end)

0 commit comments

Comments
 (0)