Skip to content

Commit

Permalink
exec: restore sparse output support
Browse files Browse the repository at this point in the history
After [1], returning values after nil (for example, classic
`return nil, err` scenario) is broken. This patch restores the behavior.

Since `net.box` already handles `nil` args input with `box.NULL`,
everything is fine for exec arguments.

1. e6a2093

Closes #350
  • Loading branch information
DifferentialOrange committed Jan 19, 2024
1 parent f37b353 commit 3eb6f10
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

* Fixed incorrent unix socket path length check (gh-341).
* Now net_box_uri can be accepted as table (gh-342).
* Fixed returning values from `Server:exec()` if some of them are nil (gh-350).

## 1.0.0

Expand Down
5 changes: 4 additions & 1 deletion luatest/server.lua
Original file line number Diff line number Diff line change
Expand Up @@ -702,6 +702,9 @@ function Server:exec(fn, args, options)
debug.setupvalue(fn, i, require(passthrough_ups[name]))
end
end
local unpack_sparse_array = require('luatest.utils').unpack_sparse_array
local result
if args == nil then
result = {pcall(fn)}
Expand All @@ -714,7 +717,7 @@ function Server:exec(fn, args, options)
end
error(result[2], 0)
end
return unpack(result, 2)
return unpack_sparse_array(result, 2)
]], {string.dump(fn), args, passthrough_ups}, options))
end

Expand Down
27 changes: 27 additions & 0 deletions luatest/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -191,4 +191,31 @@ function utils.is_tarantool_binary(path)
return path:find('^.*/tarantool[^/]*$') ~= nil
end

function utils.sparse_array_len(t)
local len = 0

for k, _ in pairs(t) do
if type(k) == 'number' then
len = math.max(len, k)
end
end

return len
end

local function unpack_sparse_array_tail(t, i, len)
if i > len then
return
end

return t[i], unpack_sparse_array_tail(t, i + 1, len)
end

function utils.unpack_sparse_array(t, i)
local len = utils.sparse_array_len(t)
i = i or 1

return unpack_sparse_array_tail(t, i, len)
end

return utils
9 changes: 9 additions & 0 deletions test/autorequire_luatest_test.lua
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,12 @@ end
g.after_test('test_exec_when_luatest_not_found', function()
g.bad_env_server:drop()
end)

g.test_exec_with_sparse_output = function()
local res1, res2 = g.server:exec(function()
return nil, 'some error'
end)

t.assert_equals(res1, nil)
t.assert_equals(res2, 'some error')
end
98 changes: 98 additions & 0 deletions test/utils_test.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
local json = require('json')

local t = require('luatest')
local g = t.group()

Expand All @@ -22,3 +24,99 @@ g.test_is_tarantool_binary = function()
("Unexpected result for %q"):format(path))
end
end

g.test_sparse_array_len = function()
local cases = {
{{1, 2, 3, 4}, 4},
{{}, 0},
{{1, nil, 3}, 3},
{{nil, 2, nil}, 2},
{{nil, 2, box.NULL}, 3},
{{nil, nil, nil, nil, 5}, 5},
}

for _, case in ipairs(cases) do
local array, result = unpack(case)
t.assert_equals(utils.sparse_array_len(array), result,
("Unexpected result for %q"):format(json.encode(array)))
end
end

g.test_unpack_sparse_array_with_values = function()
local non_sparse_input = {
{{1, 2, 3, 4}, nil},
{{1, 2, 3, 4}, 3},
}

-- Test unpack_sparse_array() is unpack() if non-sparse input.
local non_sparse_cases = {}
for _, v in ipairs(non_sparse_input) do
table.insert(non_sparse_cases, {v[1], v[2], {unpack(v[1], v[2])}})
end

local sparse_cases = {
{{1, nil, 3}, nil, {1, nil, 3}},
{{1, nil, 3}, 2, {nil, 3}},
{{nil, 2, nil}, nil, {nil, 2}},
{{nil, 2, nil}, 2, {2}},
{{nil, 2, box.NULL}, nil, {nil, 2, box.NULL}},
{{nil, 2, box.NULL}, 3 ,{box.NULL}},
{{nil, nil, nil, nil, 5}, 4, {nil, 5}},
{{nil, nil, nil, nil, 5}, 5, {5}},
}

local cases = {unpack(non_sparse_cases), unpack(sparse_cases)}

for _, case in ipairs(cases) do
local array, index, result_packed = unpack(case)

local assert_msg
if index ~= nil then
assert_msg = ("Unexpected result for unpack_sparse_array(%q, %d)"):format(
json.encode(array), index)
else
assert_msg = ("Unexpected result for unpack_sparse_array(%q)"):format(
json.encode(array))
end

t.assert_equals(
{utils.unpack_sparse_array(array, index)},
result_packed,
assert_msg
)
end
end

local function assert_return_no_values(func, ...)
-- http://lua-users.org/lists/lua-l/2011-09/msg00312.html
t.assert_error_msg_contains(
"bad argument #1 to 'assert' (value expected)",
function(...)
assert(func(...))
end,
...
)
end

g.test_unpack_sparse_array_no_values = function()
local non_sparse_cases = {
{{1, 2, 3, 4}, 5},
{{}, 1},
}

local sparse_cases = {
{{1, nil, 3}, 6},
}

-- Assert built-in unpack() symmetric behavior.
for _, case in ipairs(sparse_cases) do
local array, index = unpack(case)
assert_return_no_values(unpack, array, index)
end

local cases = {unpack(non_sparse_cases), unpack(sparse_cases)}
for _, case in ipairs(cases) do
local array, index = unpack(case)
assert_return_no_values(utils.unpack_sparse_array, array, index)
end
end

0 comments on commit 3eb6f10

Please sign in to comment.