Skip to content

Commit

Permalink
console: add line carrying backslash
Browse files Browse the repository at this point in the history
When using interactive console(stdin) instead of \set delimiter <delimiter>
with "\", "\" in the end if line can be used. Works on both server/client
side.

Local console is impossible to test with a pipe or tarantool interactive
mode. The problem with interactive mode (-i) is that the flag that sets it,
does not influence the isatty() check, while startup script is executed
(tarantool#5064). To overload this check and test the local console isatty lib
was added.
  • Loading branch information
Olga authored and Lord-KA committed Jul 3, 2022
1 parent 28426f6 commit 9c35564
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 3 deletions.
36 changes: 33 additions & 3 deletions src/box/lua/console.lua
Expand Up @@ -606,7 +606,10 @@ local function local_read(self)
if buf:sub(1, 1) == '\\' then
break
end
if delim == "" then
if delim == "" and line:endswith("\\") then
buf = buf:sub(1, -2)
-- Continue reading.
elseif delim == "" then
local lang = box.session.language
if not lang or lang == 'lua' then
-- stop once a complete Lua statement is entered
Expand Down Expand Up @@ -642,9 +645,12 @@ local function local_print(self, output)
end

--
-- Read command from connected client console.listen()
-- Read a line or a chunk from connected client console.listen().
--
local function client_read(self)
-- The value is returned without delimiter (if any) and trailing
-- newline character.
--
local function client_read_line(self)
--
-- Byte sequences that come over the network and come from
-- the local client console may have a different terminal
Expand All @@ -665,6 +671,30 @@ local function client_read(self)
return buf:sub(1, -#self.delimiter-2)
end

--
-- Read a command from connected client console.listen().
--
local function client_read(self)
local buf = ""
while true do
local line = client_read_line(self)
if line == nil then
-- EOF or error.
--
-- Note: Even if `buf` is not empty, skip unfinished
-- command.
return nil
end
buf = buf .. line
if self.delimiter == "" and line:endswith("\\") then
buf = buf:sub(1, -2)
else
break
end
end
return buf
end

--
-- Print result to connected client from console.listen()
--
Expand Down
8 changes: 8 additions & 0 deletions test/CMakeLists.txt
Expand Up @@ -68,6 +68,14 @@ add_subdirectory(box-tap)
add_subdirectory(box-luatest)
add_subdirectory(sql-tap)
add_subdirectory(sql-luatest)
# Build a library with isatty() that always returns 1.
#
# It is needed to test the local tarantool console. Consider an
# example:
#
# echo 42 | LD_PRELOAD=test/libisatty.so ./src/tarantool
add_library(isatty SHARED ./app-tap/isatty.c)

if(ENABLE_FUZZER)
add_subdirectory(fuzz)
endif()
Expand Down
57 changes: 57 additions & 0 deletions test/app-luatest/gh_4317_console_backslash_test.lua
@@ -0,0 +1,57 @@
local t = require('luatest')
local g = t.group()
local console = require('console')
local socket = require('socket')
local tap = require('tap')
local fio = require('fio')
local log = require('log')

local result_str = [[tarantool> local a = 0 \
> for i = 1, 10 do
> a = a + i
> end \
> print(a)
55
---
...
tarantool> ]]

g.test_local_console = function()
t.skip_if(jit.os == 'OSX', "LD_PRELOAD doesn't work on OSX")

-- libisatty.so provides isatty() that always returns 1.
local binary = arg[-1]
local isatty_lib_path = binary:gsub('tarantool$', '../test/libisatty.so')
assert(isatty_lib_path:match('libisatty.so$'), 'Failed to locate libisatty.so')
local tarantool_command = "local a = 0 \\\nfor i = 1, 10 do\na = a + i\nend \\\nprint(a)"

local cmd = ([[printf '%s\n' | LD_PRELOAD='%s' tarantool 2>/dev/null]]):format(tarantool_command, isatty_lib_path)
local fh = io.popen(cmd, 'r')

-- Readline on CentOS 7 produces \e[?1034h escape sequence before tarantool> prompt, remove it.
local result = fh:read('*a'):gsub('\x1b%[%?1034h', '')

fh:close()
t.assert_equals(result, result_str)
end

g.test_remote_console = function()
-- Suppress console log messages
log.level(4)
local CONSOLE_SOCKET = fio.pathjoin(fio.cwd(), 'tarantool-test-console.sock')
local IPROTO_SOCKET = fio.pathjoin(fio.cwd(), 'tarantool-test-iproto.sock')
local EOL = "\n...\n"
local server = console.listen(CONSOLE_SOCKET)
local client = socket.tcp_connect("unix/", CONSOLE_SOCKET)
local cmd = 'local a = 0 \\\nfor i = 1, 10 do\\\n a = a + i \\\n end \\\n print(a)'

client = socket.tcp_connect("unix/", CONSOLE_SOCKET)
client:read(128)
client:write(('%s\n'):format(cmd))
local result = client:read(EOL)
t.assert_not_str_contains(result, 'error')
client:close()
os.remove(CONSOLE_SOCKET)
os.remove(IPROTO_SOCKET)
end
5 changes: 5 additions & 0 deletions test/app-tap/isatty.c
@@ -0,0 +1,5 @@
int
isatty(int fd)
{
return 1;
}

0 comments on commit 9c35564

Please sign in to comment.