multigrid: add multigrid support to test infrastructure

make Screen explicitly tied to its session
This commit is contained in:
Björn Linse
2017-12-09 11:26:06 +01:00
parent f77f09ea6e
commit 1f8afe15a4
6 changed files with 171 additions and 72 deletions

View File

@@ -1261,6 +1261,7 @@ describe('API', function()
ext_tabline = false, ext_tabline = false,
ext_wildmenu = false, ext_wildmenu = false,
ext_linegrid = screen._options.ext_linegrid or false, ext_linegrid = screen._options.ext_linegrid or false,
ext_multigrid = false,
ext_hlstate=false, ext_hlstate=false,
height = 4, height = 4,
rgb = true, rgb = true,

View File

@@ -84,6 +84,10 @@ end
local session, loop_running, last_error local session, loop_running, last_error
local function get_session()
return session
end
local function set_session(s, keep) local function set_session(s, keep)
if session and not keep then if session and not keep then
session:close() session:close()
@@ -164,34 +168,34 @@ local function expect_msg_seq(...)
error(final_error) error(final_error)
end end
local function call_and_stop_on_error(...) local function call_and_stop_on_error(lsession, ...)
local status, result = copcall(...) -- luacheck: ignore local status, result = copcall(...) -- luacheck: ignore
if not status then if not status then
session:stop() lsession:stop()
last_error = result last_error = result
return '' return ''
end end
return result return result
end end
local function run(request_cb, notification_cb, setup_cb, timeout) local function run_session(lsession, request_cb, notification_cb, setup_cb, timeout)
local on_request, on_notification, on_setup local on_request, on_notification, on_setup
if request_cb then if request_cb then
function on_request(method, args) function on_request(method, args)
return call_and_stop_on_error(request_cb, method, args) return call_and_stop_on_error(lsession, request_cb, method, args)
end end
end end
if notification_cb then if notification_cb then
function on_notification(method, args) function on_notification(method, args)
call_and_stop_on_error(notification_cb, method, args) call_and_stop_on_error(lsession, notification_cb, method, args)
end end
end end
if setup_cb then if setup_cb then
function on_setup() function on_setup()
call_and_stop_on_error(setup_cb) call_and_stop_on_error(lsession, setup_cb)
end end
end end
@@ -205,6 +209,10 @@ local function run(request_cb, notification_cb, setup_cb, timeout)
end end
end end
local function run(request_cb, notification_cb, setup_cb, timeout)
run_session(session, request_cb, notification_cb, setup_cb, timeout)
end
local function stop() local function stop()
session:stop() session:stop()
end end
@@ -677,6 +685,7 @@ local module = {
buffer = buffer, buffer = buffer,
bufmeths = bufmeths, bufmeths = bufmeths,
call = nvim_call, call = nvim_call,
create_callindex = create_callindex,
clear = clear, clear = clear,
command = nvim_command, command = nvim_command,
connect = connect, connect = connect,
@@ -701,6 +710,7 @@ local module = {
filter = filter, filter = filter,
funcs = funcs, funcs = funcs,
get_pathsep = get_pathsep, get_pathsep = get_pathsep,
get_session = get_session,
insert = insert, insert = insert,
iswin = iswin, iswin = iswin,
map = map, map = map,
@@ -732,6 +742,7 @@ local module = {
retry = retry, retry = retry,
rmdir = rmdir, rmdir = rmdir,
run = run, run = run,
run_session = run_session,
set_session = set_session, set_session = set_session,
set_shell_powershell = set_shell_powershell, set_shell_powershell = set_shell_powershell,
skip_fragile = skip_fragile, skip_fragile = skip_fragile,

View File

@@ -259,10 +259,10 @@ describe('tui', function()
feed_data(':echo map(nvim_list_uis(), {k,v -> sort(items(v))})\013') feed_data(':echo map(nvim_list_uis(), {k,v -> sort(items(v))})\013')
screen:expect([=[ screen:expect([=[
[[['ext_cmdline', v:false], ['ext_hlstate', v:fals| [[['ext_cmdline', v:false], ['ext_hlstate', v:fals|
e], ['ext_linegrid', v:true], ['ext_popupmenu', v:| e], ['ext_linegrid', v:true], ['ext_multigrid', v:|
false], ['ext_tabline', v:false], ['ext_wildmenu',| false], ['ext_popupmenu', v:false], ['ext_tabline'|
v:false], ['height', 6], ['rgb', v:false], ['widt| , v:false], ['ext_wildmenu', v:false], ['height', |
h', 50]]] | 6], ['rgb', v:false], ['width', 50]]] |
{10:Press ENTER or type command to continue}{1: } | {10:Press ENTER or type command to continue}{1: } |
{3:-- TERMINAL --} | {3:-- TERMINAL --} |
]=]) ]=])

View File

@@ -26,6 +26,7 @@ describe('ui receives option updates', function()
ext_wildmenu=false, ext_wildmenu=false,
ext_linegrid=false, ext_linegrid=false,
ext_hlstate=false, ext_hlstate=false,
ext_multigrid=false,
} }
clear(...) clear(...)

View File

@@ -75,9 +75,11 @@ local global_helpers = require('test.helpers')
local deepcopy = global_helpers.deepcopy local deepcopy = global_helpers.deepcopy
local shallowcopy = global_helpers.shallowcopy local shallowcopy = global_helpers.shallowcopy
local helpers = require('test.functional.helpers')(nil) local helpers = require('test.functional.helpers')(nil)
local request, run, uimeths = helpers.request, helpers.run, helpers.uimeths local request, run_session = helpers.request, helpers.run_session
local eq = helpers.eq local eq = helpers.eq
local dedent = helpers.dedent local dedent = helpers.dedent
local get_session = helpers.get_session
local create_callindex = helpers.create_callindex
local inspect = require('inspect') local inspect = require('inspect')
@@ -155,6 +157,7 @@ function Screen.new(width, height)
cmdline_block = {}, cmdline_block = {},
wildmenu_items = nil, wildmenu_items = nil,
wildmenu_selected = nil, wildmenu_selected = nil,
_session = nil,
_default_attr_ids = nil, _default_attr_ids = nil,
_default_attr_ignore = nil, _default_attr_ignore = nil,
_mouse_enabled = true, _mouse_enabled = true,
@@ -165,11 +168,19 @@ function Screen.new(width, height)
_new_attrs = false, _new_attrs = false,
_width = width, _width = width,
_height = height, _height = height,
_grids = {},
_cursor = { _cursor = {
row = 1, col = 1 grid = 1, row = 1, col = 1
}, },
_busy = false _busy = false,
}, Screen) }, Screen)
local function ui(method, ...)
local status, rv = self._session:request('nvim_ui_'..method, ...)
if not status then
error(rv[2])
end
end
self.uimeths = create_callindex(ui)
return self return self
end end
@@ -189,34 +200,51 @@ function Screen:set_hlstate_cterm(val)
self._hlstate_cterm = val self._hlstate_cterm = val
end end
function Screen:attach(options) function Screen:attach(options, session)
if session == nil then
session = get_session()
end
if options == nil then if options == nil then
options = {} options = {}
end end
if options.ext_linegrid == nil then if options.ext_linegrid == nil then
options.ext_linegrid = true options.ext_linegrid = true
end end
self._session = session
self._options = options self._options = options
self._clear_attrs = (options.ext_linegrid and {{},{}}) or {} self._clear_attrs = (options.ext_linegrid and {{},{}}) or {}
self:_handle_resize(self._width, self._height) self:_handle_resize(self._width, self._height)
uimeths.attach(self._width, self._height, options) self.uimeths.attach(self._width, self._height, options)
if self._options.rgb == nil then if self._options.rgb == nil then
-- nvim defaults to rgb=true internally, -- nvim defaults to rgb=true internally,
-- simplify test code by doing the same. -- simplify test code by doing the same.
self._options.rgb = true self._options.rgb = true
end end
if self._options.ext_multigrid then
self._options.ext_linegrid = true
end
self._session = session
end end
function Screen:detach() function Screen:detach()
uimeths.detach() self.uimeths.detach()
self._session = nil
end end
function Screen:try_resize(columns, rows) function Screen:try_resize(columns, rows)
uimeths.try_resize(columns, rows) self._width = columns
self._height = rows
self.uimeths.try_resize(columns, rows)
end
function Screen:try_resize_grid(grid, columns, rows)
self._grid = self._grids[1]
self.uimeths.try_resize_grid(grid, columns, rows)
end end
function Screen:set_option(option, value) function Screen:set_option(option, value)
uimeths.set_option(option, value) self.uimeths.set_option(option, value)
self._options[option] = value self._options[option] = value
end end
@@ -341,19 +369,11 @@ function Screen:expect(expected, attr_ids, attr_ignore)
end end
end end
if grid ~= nil and self._height ~= #expected_rows then
return ("Expected screen state's row count(" .. #expected_rows
.. ') differs from configured height(' .. self._height .. ') of Screen.')
end
if self._options.ext_hlstate and self._new_attrs then if self._options.ext_hlstate and self._new_attrs then
attr_state.id_to_index = self:hlstate_check_attrs(attr_state.ids or {}) attr_state.id_to_index = self:hlstate_check_attrs(attr_state.ids or {})
end end
local actual_rows = {} local actual_rows = self:render(not expected.any, attr_state)
for i = 1, self._height do
actual_rows[i] = self:_row_repr(self._rows[i], attr_state)
end
if expected.any ~= nil then if expected.any ~= nil then
-- Search for `any` anywhere in the screen lines. -- Search for `any` anywhere in the screen lines.
@@ -368,7 +388,11 @@ function Screen:expect(expected, attr_ids, attr_ignore)
if grid ~= nil then if grid ~= nil then
-- `expected` must match the screen lines exactly. -- `expected` must match the screen lines exactly.
for i = 1, self._height do if #actual_rows ~= #expected_rows then
return "Expected screen state's row count(" .. #expected_rows
.. ') differs from configured height(' .. #actual_rows .. ') of Screen.'
end
for i = 1, #actual_rows do
if expected_rows[i] ~= actual_rows[i] then if expected_rows[i] ~= actual_rows[i] then
local msg_expected_rows = {} local msg_expected_rows = {}
for j = 1, #expected_rows do for j = 1, #expected_rows do
@@ -465,7 +489,7 @@ function Screen:_wait(check, flags)
if not err then if not err then
success_seen = true success_seen = true
if did_miminal_timeout then if did_miminal_timeout then
helpers.stop() self._session:stop()
end end
elseif success_seen and #args > 0 then elseif success_seen and #args > 0 then
failure_after_success = true failure_after_success = true
@@ -474,7 +498,7 @@ function Screen:_wait(check, flags)
return true return true
end end
run(nil, notification_cb, nil, minimal_timeout) run_session(self._session, nil, notification_cb, nil, minimal_timeout)
if not did_flush then if not did_flush then
err = "no flush received" err = "no flush received"
elseif not checked then elseif not checked then
@@ -487,7 +511,7 @@ function Screen:_wait(check, flags)
if not success_seen then if not success_seen then
did_miminal_timeout = true did_miminal_timeout = true
run(nil, notification_cb, nil, timeout-minimal_timeout) run_session(self._session, nil, notification_cb, nil, timeout-minimal_timeout)
end end
local did_warn = false local did_warn = false
@@ -547,7 +571,7 @@ function Screen:sleep(ms)
assert(method == 'redraw') assert(method == 'redraw')
self:_redraw(args) self:_redraw(args)
end end
run(nil, notification_cb, nil, ms) run_session(self._session, nil, notification_cb, nil, ms)
end end
function Screen:_redraw(updates) function Screen:_redraw(updates)
@@ -579,6 +603,22 @@ function Screen:set_on_event_handler(callback)
end end
function Screen:_handle_resize(width, height) function Screen:_handle_resize(width, height)
self:_handle_grid_resize(1, width, height)
self._scroll_region = {
top = 1, bot = height, left = 1, right = width
}
self._grid = self._grids[1]
end
local function min(x,y)
if x < y then
return x
else
return y
end
end
function Screen:_handle_grid_resize(grid, width, height)
local rows = {} local rows = {}
for _ = 1, height do for _ = 1, height do
local cols = {} local cols = {}
@@ -587,24 +627,29 @@ function Screen:_handle_resize(width, height)
end end
table.insert(rows, cols) table.insert(rows, cols)
end end
if grid > 1 and self._grids[grid] ~= nil then
local old = self._grids[grid]
for i = 1, min(height,old.height) do
for j = 1, min(width,old.width) do
rows[i][j] = old.rows[i][j]
end
end
end
if self._cursor.grid == grid then
self._cursor.row = 1 self._cursor.row = 1
self._cursor.col = 1 self._cursor.col = 1
self._rows = rows end
self._width = width self._grids[grid] = {
self._height = height rows=rows,
self._scroll_region = { width=width,
top = 1, bot = height, left = 1, right = width height=height,
} }
end end
function Screen:_handle_flush() function Screen:_handle_flush()
end end
function Screen:_handle_grid_resize(grid, width, height)
assert(grid == 1)
self:_handle_resize(width, height)
end
function Screen:_reset() function Screen:_reset()
-- TODO: generalize to multigrid later -- TODO: generalize to multigrid later
self:_handle_grid_clear(1) self:_handle_grid_clear(1)
@@ -641,20 +686,27 @@ function Screen:_handle_clear()
-- newer clients, to check we remain compatible with both kind of clients, -- newer clients, to check we remain compatible with both kind of clients,
-- ensure the scroll region is in a reset state. -- ensure the scroll region is in a reset state.
local expected_region = { local expected_region = {
top = 1, bot = self._height, left = 1, right = self._width top = 1, bot = self._grid.height, left = 1, right = self._grid.width
} }
eq(expected_region, self._scroll_region) eq(expected_region, self._scroll_region)
self:_clear_block(1, self._height, 1, self._width) self:_handle_grid_clear(1)
end end
function Screen:_handle_grid_clear(grid) function Screen:_handle_grid_clear(grid)
assert(grid == 1) self:_clear_block(self._grids[grid], 1, self._grids[grid].height, 1, self._grids[grid].width)
self:_clear_block(1, self._height, 1, self._width) end
function Screen:_handle_grid_destroy(grid)
self._grids[grid] = nil
if self._multigrid then
assert(self.win_position[grid])
self.win_position[grid] = nil
end
end end
function Screen:_handle_eol_clear() function Screen:_handle_eol_clear()
local row, col = self._cursor.row, self._cursor.col local row, col = self._cursor.row, self._cursor.col
self:_clear_block(row, row, col, self._scroll_region.right) self:_clear_block(self._grid, row, row, col, self._grid.width)
end end
function Screen:_handle_cursor_goto(row, col) function Screen:_handle_cursor_goto(row, col)
@@ -663,7 +715,7 @@ function Screen:_handle_cursor_goto(row, col)
end end
function Screen:_handle_grid_cursor_goto(grid, row, col) function Screen:_handle_grid_cursor_goto(grid, row, col)
assert(grid == 1) self._cursor.grid = grid
self._cursor.row = row + 1 self._cursor.row = row + 1
self._cursor.col = col + 1 self._cursor.col = col + 1
end end
@@ -704,11 +756,11 @@ function Screen:_handle_scroll(count)
self:_handle_grid_scroll(1, top-1, bot, left-1, right, count, 0) self:_handle_grid_scroll(1, top-1, bot, left-1, right, count, 0)
end end
function Screen:_handle_grid_scroll(grid, top, bot, left, right, rows, cols) function Screen:_handle_grid_scroll(g, top, bot, left, right, rows, cols)
top = top+1 top = top+1
left = left+1 left = left+1
assert(grid == 1)
assert(cols == 0) assert(cols == 0)
local grid = self._grids[g]
local start, stop, step local start, stop, step
if rows > 0 then if rows > 0 then
@@ -723,8 +775,8 @@ function Screen:_handle_grid_scroll(grid, top, bot, left, right, rows, cols)
-- shift scroll region -- shift scroll region
for i = start, stop, step do for i = start, stop, step do
local target = self._rows[i] local target = grid.rows[i]
local source = self._rows[i + rows] local source = grid.rows[i + rows]
for j = left, right do for j = left, right do
target[j].text = source[j].text target[j].text = source[j].text
target[j].attrs = source[j].attrs target[j].attrs = source[j].attrs
@@ -734,7 +786,7 @@ function Screen:_handle_grid_scroll(grid, top, bot, left, right, rows, cols)
-- clear invalid rows -- clear invalid rows
for i = stop + step, stop + rows, step do for i = stop + step, stop + rows, step do
self:_clear_row_section(i, left, right) self:_clear_row_section(grid, i, left, right)
end end
end end
@@ -744,13 +796,35 @@ function Screen:_handle_hl_attr_define(id, rgb_attrs, cterm_attrs, info)
self._new_attrs = true self._new_attrs = true
end end
function Screen:_handle_win_position(win, grid, startrow, startcol, width, height)
if self.win_position == nil then
self.win_position = {}
end
self.win_position[grid] = {
win = win,
startrow = startrow,
startcol = startcol,
width = width,
height = height
}
-- TODO(utkarshme): Take apt action
end
function Screen:get_hl(val)
if self._options.ext_newgrid then
return self._attr_table[val][1]
else
return val
end
end
function Screen:_handle_highlight_set(attrs) function Screen:_handle_highlight_set(attrs)
self._attrs = attrs self._attrs = attrs
end end
function Screen:_handle_put(str) function Screen:_handle_put(str)
assert(not self._options.ext_linegrid) assert(not self._options.ext_linegrid)
local cell = self._rows[self._cursor.row][self._cursor.col] local cell = self._grid.rows[self._cursor.row][self._cursor.col]
cell.text = str cell.text = str
cell.attrs = self._attrs cell.attrs = self._attrs
cell.hl_id = -1 cell.hl_id = -1
@@ -759,8 +833,7 @@ end
function Screen:_handle_grid_line(grid, row, col, items) function Screen:_handle_grid_line(grid, row, col, items)
assert(self._options.ext_linegrid) assert(self._options.ext_linegrid)
assert(grid == 1) local line = self._grids[grid].rows[row+1]
local line = self._rows[row+1]
local colpos = col+1 local colpos = col+1
local hl = self._clear_attrs local hl = self._clear_attrs
local hl_id = 0 local hl_id = 0
@@ -887,24 +960,24 @@ function Screen:_handle_wildmenu_hide()
self.wildmenu_items, self.wildmenu_pos = nil, nil self.wildmenu_items, self.wildmenu_pos = nil, nil
end end
function Screen:_clear_block(top, bot, left, right) function Screen:_clear_block(grid, top, bot, left, right)
for i = top, bot do for i = top, bot do
self:_clear_row_section(i, left, right) self:_clear_row_section(grid, i, left, right)
end end
end end
function Screen:_clear_row_section(rownum, startcol, stopcol) function Screen:_clear_row_section(grid, rownum, startcol, stopcol)
local row = self._rows[rownum] local row = grid.rows[rownum]
for i = startcol, stopcol do for i = startcol, stopcol do
row[i].text = ' ' row[i].text = ' '
row[i].attrs = self._clear_attrs row[i].attrs = self._clear_attrs
end end
end end
function Screen:_row_repr(row, attr_state) function Screen:_row_repr(row, attr_state, cursor)
local rv = {} local rv = {}
local current_attr_id local current_attr_id
for i = 1, self._width do for i = 1, #row do
local attrs = row[i].attrs local attrs = row[i].attrs
if self._options.ext_linegrid then if self._options.ext_linegrid then
attrs = attrs[(self._options.rgb and 1) or 2] attrs = attrs[(self._options.rgb and 1) or 2]
@@ -922,7 +995,7 @@ function Screen:_row_repr(row, attr_state)
table.insert(rv, '{' .. attr_id .. ':') table.insert(rv, '{' .. attr_id .. ':')
current_attr_id = attr_id current_attr_id = attr_id
end end
if not self._busy and self._rows[self._cursor.row] == row and self._cursor.col == i then if not self._busy and cursor and self._cursor.col == i then
table.insert(rv, '^') table.insert(rv, '^')
end end
table.insert(rv, row[i].text) table.insert(rv, row[i].text)
@@ -997,7 +1070,23 @@ function Screen:redraw_debug(attrs, ignore, timeout)
if timeout == nil then if timeout == nil then
timeout = 250 timeout = 250
end end
run(nil, notification_cb, nil, timeout) run_session(self._session, nil, notification_cb, nil, timeout)
end
function Screen:render(headers, attr_state, preview)
headers = headers and self._options.ext_multigrid
local rv = {}
for igrid,grid in pairs(self._grids) do
if headers then
table.insert(rv, "## grid "..igrid)
end
for i = 1, grid.height do
local cursor = self._cursor.grid == igrid and self._cursor.row == i
local prefix = (headers or preview) and " " or ""
table.insert(rv, prefix..self:_row_repr(grid.rows[i], attr_state, cursor).."|")
end
end
return rv
end end
function Screen:print_snapshot(attrs, ignore) function Screen:print_snapshot(attrs, ignore)
@@ -1020,10 +1109,7 @@ function Screen:print_snapshot(attrs, ignore)
attr_state.id_to_index = self:hlstate_check_attrs(attr_state.ids) attr_state.id_to_index = self:hlstate_check_attrs(attr_state.ids)
end end
local lines = {} local lines = self:render(true, attr_state, true)
for i = 1, self._height do
table.insert(lines, " "..self:_row_repr(self._rows[i], attr_state).."|")
end
local ext_state = self:_extstate_repr(attr_state) local ext_state = self:_extstate_repr(attr_state)
local keys = false local keys = false

View File

@@ -49,8 +49,8 @@ local check_logs_useless_lines = {
local function eq(expected, actual, ctx) local function eq(expected, actual, ctx)
return assert.are.same(expected, actual, ctx) return assert.are.same(expected, actual, ctx)
end end
local function neq(expected, actual) local function neq(expected, actual, context)
return assert.are_not.same(expected, actual) return assert.are_not.same(expected, actual, context)
end end
local function ok(res) local function ok(res)
return assert.is_true(res) return assert.is_true(res)