build: enable lintlua for test/unit/ dir #26396

Problem:
Not all Lua code is checked by stylua. Automating code-style is an
important mechanism for reducing time spent on accidental
(non-essential) complexity.

Solution:
- Enable lintlua for `test/unit/` directory.
- TODO: only `test/functional/` remains unchecked.

previous: 45fe4d11ad
previous: 517f0cc634
This commit is contained in:
Justin M. Keyes
2023-12-04 14:32:39 -08:00
committed by GitHub
parent 45fe4d11ad
commit c3836e40a2
51 changed files with 4067 additions and 2764 deletions

View File

@ -2,4 +2,10 @@
/runtime/lua/coxpcall.lua /runtime/lua/coxpcall.lua
/runtime/lua/vim/_meta /runtime/lua/vim/_meta
/runtime/lua/vim/re.lua /runtime/lua/vim/re.lua
/test
/test/functional
/test/functional/fixtures/lua/syntax_error.lua
/test/functional/legacy/030_fileformats_spec.lua
/test/functional/legacy/044_099_regexp_multibyte_magic_spec.lua
/test/functional/legacy/093_mksession_cursor_cols_latin1_spec.lua
/test/functional/lua/luaeval_spec.lua

View File

@ -233,7 +233,7 @@ add_glob_target(
TARGET lintlua-stylua TARGET lintlua-stylua
COMMAND ${STYLUA_PRG} COMMAND ${STYLUA_PRG}
FLAGS --color=always --check --respect-ignores FLAGS --color=always --check --respect-ignores
GLOB_DIRS runtime/ scripts/ src/ GLOB_DIRS runtime/ scripts/ src/ test/unit/
GLOB_PAT *.lua GLOB_PAT *.lua
TOUCH_STRATEGY SINGLE) TOUCH_STRATEGY SINGLE)
@ -260,7 +260,7 @@ add_glob_target(
TARGET formatlua TARGET formatlua
COMMAND ${STYLUA_PRG} COMMAND ${STYLUA_PRG}
FLAGS --respect-ignores FLAGS --respect-ignores
GLOB_DIRS runtime/ scripts/ src/ GLOB_DIRS runtime/ scripts/ src/ test/unit/
GLOB_PAT *.lua) GLOB_PAT *.lua)
add_custom_target(format) add_custom_target(format)

View File

@ -27,7 +27,8 @@ describe('autocmd perf', function()
end) end)
it('nvim_create_autocmd, nvim_del_autocmd (same pattern)', function() it('nvim_create_autocmd, nvim_del_autocmd (same pattern)', function()
exec_lua([[ exec_lua(
[[
local N = ... local N = ...
local ids = {} local ids = {}
@ -45,11 +46,14 @@ describe('autocmd perf', function()
vim.api.nvim_del_autocmd(ids[i]) vim.api.nvim_del_autocmd(ids[i])
end end
stop('nvim_del_autocmd') stop('nvim_del_autocmd')
]], N) ]],
N
)
end) end)
it('nvim_create_autocmd, nvim_del_autocmd (unique patterns)', function() it('nvim_create_autocmd, nvim_del_autocmd (unique patterns)', function()
exec_lua([[ exec_lua(
[[
local N = ... local N = ...
local ids = {} local ids = {}
@ -67,11 +71,14 @@ describe('autocmd perf', function()
vim.api.nvim_del_autocmd(ids[i]) vim.api.nvim_del_autocmd(ids[i])
end end
stop('nvim_del_autocmd') stop('nvim_del_autocmd')
]], N) ]],
N
)
end) end)
it('nvim_create_autocmd + nvim_del_autocmd', function() it('nvim_create_autocmd + nvim_del_autocmd', function()
exec_lua([[ exec_lua(
[[
local N = ... local N = ...
start() start()
@ -83,11 +90,14 @@ describe('autocmd perf', function()
vim.api.nvim_del_autocmd(id) vim.api.nvim_del_autocmd(id)
end end
stop('nvim_create_autocmd + nvim_del_autocmd') stop('nvim_create_autocmd + nvim_del_autocmd')
]], N) ]],
N
)
end) end)
it('nvim_exec_autocmds (same pattern)', function() it('nvim_exec_autocmds (same pattern)', function()
exec_lua([[ exec_lua(
[[
local N = ... local N = ...
for i = 1, N do for i = 1, N do
@ -100,11 +110,14 @@ describe('autocmd perf', function()
start() start()
vim.api.nvim_exec_autocmds('User', { pattern = 'Benchmark', modeline = false }) vim.api.nvim_exec_autocmds('User', { pattern = 'Benchmark', modeline = false })
stop('nvim_exec_autocmds') stop('nvim_exec_autocmds')
]], N) ]],
N
)
end) end)
it('nvim_del_augroup_by_id', function() it('nvim_del_augroup_by_id', function()
exec_lua([[ exec_lua(
[[
local N = ... local N = ...
local group = vim.api.nvim_create_augroup('Benchmark', {}) local group = vim.api.nvim_create_augroup('Benchmark', {})
@ -119,11 +132,14 @@ describe('autocmd perf', function()
start() start()
vim.api.nvim_del_augroup_by_id(group) vim.api.nvim_del_augroup_by_id(group)
stop('nvim_del_augroup_by_id') stop('nvim_del_augroup_by_id')
]], N) ]],
N
)
end) end)
it('nvim_del_augroup_by_name', function() it('nvim_del_augroup_by_name', function()
exec_lua([[ exec_lua(
[[
local N = ... local N = ...
local group = vim.api.nvim_create_augroup('Benchmark', {}) local group = vim.api.nvim_create_augroup('Benchmark', {})
@ -138,11 +154,14 @@ describe('autocmd perf', function()
start() start()
vim.api.nvim_del_augroup_by_name('Benchmark') vim.api.nvim_del_augroup_by_name('Benchmark')
stop('nvim_del_augroup_by_id') stop('nvim_del_augroup_by_id')
]], N) ]],
N
)
end) end)
it(':autocmd, :autocmd! (same pattern)', function() it(':autocmd, :autocmd! (same pattern)', function()
exec_lua([[ exec_lua(
[[
local N = ... local N = ...
start() start()
@ -154,11 +173,14 @@ describe('autocmd perf', function()
start() start()
vim.cmd('autocmd! User Benchmark') vim.cmd('autocmd! User Benchmark')
stop(':autocmd!') stop(':autocmd!')
]], N) ]],
N
)
end) end)
it(':autocmd, :autocmd! (unique patterns)', function() it(':autocmd, :autocmd! (unique patterns)', function()
exec_lua([[ exec_lua(
[[
local N = ... local N = ...
start() start()
@ -170,6 +192,8 @@ describe('autocmd perf', function()
start() start()
vim.cmd('autocmd! User') vim.cmd('autocmd! User')
stop(':autocmd!') stop(':autocmd!')
]], N) ]],
N
)
end) end)
end) end)

View File

@ -10,8 +10,7 @@ local result_file = 'benchmark.out'
local sample_file = 'test/old/testdir/samples/re.freeze.txt' local sample_file = 'test/old/testdir/samples/re.freeze.txt'
-- Vim script code that does both the work and the benchmarking of that work. -- Vim script code that does both the work and the benchmarking of that work.
local measure_cmd = local measure_cmd = [[call Measure(%d, ']] .. sample_file .. [[', '\s\+\%%#\@<!$', '+5')]]
[[call Measure(%d, ']] .. sample_file .. [[', '\s\+\%%#\@<!$', '+5')]]
local measure_script = [[ local measure_script = [[
func Measure(re, file, pattern, arg) func Measure(re, file, pattern, arg)
let sstart = reltime() let sstart = reltime()

View File

@ -4,14 +4,13 @@ local clear = helpers.clear
local exec_lua = helpers.exec_lua local exec_lua = helpers.exec_lua
describe('treesitter perf', function() describe('treesitter perf', function()
setup(function() setup(function()
clear() clear()
end) end)
it('can handle large folds', function() it('can handle large folds', function()
helpers.command'edit ./src/nvim/eval.c' helpers.command 'edit ./src/nvim/eval.c'
exec_lua[[ exec_lua [[
local parser = vim.treesitter.get_parser(0, "c", {}) local parser = vim.treesitter.get_parser(0, "c", {})
vim.treesitter.highlighter.new(parser) vim.treesitter.highlighter.new(parser)
@ -47,7 +46,5 @@ describe('treesitter perf', function()
return vim.uv.hrtime() - start return vim.uv.hrtime() - start
]] ]]
end) end)
end) end)

View File

@ -1,12 +1,21 @@
local pretty = require 'pl.pretty' local pretty = require 'pl.pretty'
local global_helpers = require('test.helpers') local global_helpers = require('test.helpers')
local colors = setmetatable({}, {__index = function() return function(s) return s == nil and '' or tostring(s) end end}) local colors = setmetatable({}, {
__index = function()
return function(s)
return s == nil and '' or tostring(s)
end
end,
})
local enable_colors = true local enable_colors = true
if os.getenv "TEST_COLORS" then if os.getenv 'TEST_COLORS' then
local test_colors = os.getenv("TEST_COLORS"):lower() local test_colors = os.getenv('TEST_COLORS'):lower()
local disable_colors = test_colors == 'false' or test_colors == '0' or test_colors == 'no' or test_colors == 'off' local disable_colors = test_colors == 'false'
or test_colors == '0'
or test_colors == 'no'
or test_colors == 'off'
enable_colors = not disable_colors enable_colors = not disable_colors
end end
if enable_colors then if enable_colors then
@ -18,49 +27,73 @@ return function(options)
local handler = require 'busted.outputHandlers.base'() local handler = require 'busted.outputHandlers.base'()
local c = { local c = {
succ = function(s) return colors.bright(colors.green(s)) end, succ = function(s)
skip = function(s) return colors.bright(colors.yellow(s)) end, return colors.bright(colors.green(s))
fail = function(s) return colors.bright(colors.magenta(s)) end, end,
errr = function(s) return colors.bright(colors.red(s)) end, skip = function(s)
return colors.bright(colors.yellow(s))
end,
fail = function(s)
return colors.bright(colors.magenta(s))
end,
errr = function(s)
return colors.bright(colors.red(s))
end,
test = tostring, test = tostring,
file = colors.cyan, file = colors.cyan,
time = colors.dim, time = colors.dim,
note = colors.yellow, note = colors.yellow,
sect = function(s) return colors.green(colors.dim(s)) end, sect = function(s)
return colors.green(colors.dim(s))
end,
nmbr = colors.bright, nmbr = colors.bright,
} }
local repeatSuiteString = '\nRepeating all tests (run %d of %d) . . .\n\n' local repeatSuiteString = '\nRepeating all tests (run %d of %d) . . .\n\n'
local randomizeString = c.note('Note: Randomizing test order with a seed of %d.\n') local randomizeString = c.note('Note: Randomizing test order with a seed of %d.\n')
local globalSetup = c.sect('--------') .. ' Global test environment setup.\n' local globalSetup = c.sect('--------') .. ' Global test environment setup.\n'
local fileStartString = c.sect('--------') .. ' Running tests from ' .. c.file('%s') .. '\n' local fileStartString = c.sect('--------') .. ' Running tests from ' .. c.file('%s') .. '\n'
local runString = c.sect('RUN ') .. ' ' .. c.test('%s') .. ': ' local runString = c.sect('RUN ') .. ' ' .. c.test('%s') .. ': '
local successString = c.succ('OK') .. '\n' local successString = c.succ('OK') .. '\n'
local skippedString = c.skip('SKIP') .. '\n' local skippedString = c.skip('SKIP') .. '\n'
local failureString = c.fail('FAIL') .. '\n' local failureString = c.fail('FAIL') .. '\n'
local errorString = c.errr('ERR') .. '\n' local errorString = c.errr('ERR') .. '\n'
local fileEndString = c.sect('--------') .. ' '.. c.nmbr('%d') .. ' %s from ' .. c.file('%s') .. ' ' .. c.time('(%.2f ms total)') .. '\n\n' local fileEndString = c.sect('--------')
local globalTeardown = c.sect('--------') .. ' Global test environment teardown.\n' .. ' '
local suiteEndString = c.sect('========') .. ' ' .. c.nmbr('%d') .. ' %s from ' .. c.nmbr('%d') .. ' test %s ran. ' .. c.time('(%.2f ms total)') .. '\n' .. c.nmbr('%d')
local successStatus = c.succ('PASSED ') .. ' ' .. c.nmbr('%d') .. ' %s.\n' .. ' %s from '
local timeString = c.time('%.2f ms') .. c.file('%s')
.. ' '
.. c.time('(%.2f ms total)')
.. '\n\n'
local globalTeardown = c.sect('--------') .. ' Global test environment teardown.\n'
local suiteEndString = c.sect('========')
.. ' '
.. c.nmbr('%d')
.. ' %s from '
.. c.nmbr('%d')
.. ' test %s ran. '
.. c.time('(%.2f ms total)')
.. '\n'
local successStatus = c.succ('PASSED ') .. ' ' .. c.nmbr('%d') .. ' %s.\n'
local timeString = c.time('%.2f ms')
local summaryStrings = { local summaryStrings = {
skipped = { skipped = {
header = c.skip('SKIPPED ') .. ' ' .. c.nmbr('%d') .. ' %s, listed below:\n', header = c.skip('SKIPPED ') .. ' ' .. c.nmbr('%d') .. ' %s, listed below:\n',
test = c.skip('SKIPPED ') .. ' %s\n', test = c.skip('SKIPPED ') .. ' %s\n',
footer = ' ' .. c.nmbr('%d') .. ' SKIPPED %s\n', footer = ' ' .. c.nmbr('%d') .. ' SKIPPED %s\n',
}, },
failure = { failure = {
header = c.fail('FAILED ') .. ' ' .. c.nmbr('%d') .. ' %s, listed below:\n', header = c.fail('FAILED ') .. ' ' .. c.nmbr('%d') .. ' %s, listed below:\n',
test = c.fail('FAILED ') .. ' %s\n', test = c.fail('FAILED ') .. ' %s\n',
footer = ' ' .. c.nmbr('%d') .. ' FAILED %s\n', footer = ' ' .. c.nmbr('%d') .. ' FAILED %s\n',
}, },
error = { error = {
header = c.errr('ERROR ') .. ' ' .. c.nmbr('%d') .. ' %s, listed below:\n', header = c.errr('ERROR ') .. ' ' .. c.nmbr('%d') .. ' %s, listed below:\n',
test = c.errr('ERROR ') .. ' %s\n', test = c.errr('ERROR ') .. ' %s\n',
footer = ' ' .. c.nmbr('%d') .. ' %s\n', footer = ' ' .. c.nmbr('%d') .. ' %s\n',
}, },
} }
@ -107,8 +140,10 @@ return function(options)
local getFileLine = function(element) local getFileLine = function(element)
local fileline = '' local fileline = ''
if element.trace or element.trace.short_src then if element.trace or element.trace.short_src then
fileline = colors.cyan(element.trace.short_src) .. ' @ ' .. fileline = colors.cyan(element.trace.short_src)
colors.cyan(element.trace.currentline) .. ': ' .. ' @ '
.. colors.cyan(element.trace.currentline)
.. ': '
end end
return fileline return fileline
end end
@ -219,7 +254,9 @@ return function(options)
local elapsedTime_ms = getElapsedTime(file) local elapsedTime_ms = getElapsedTime(file)
local tests = (fileTestCount == 1 and 'test' or 'tests') local tests = (fileTestCount == 1 and 'test' or 'tests')
fileCount = fileCount + 1 fileCount = fileCount + 1
io.write(fileEndString:format(fileTestCount, tests, vim.fs.normalize(file.name), elapsedTime_ms)) io.write(
fileEndString:format(fileTestCount, tests, vim.fs.normalize(file.name), elapsedTime_ms)
)
io.flush() io.flush()
return nil, true return nil, true
end end
@ -256,7 +293,7 @@ return function(options)
errorCount = errorCount + 1 errorCount = errorCount + 1
string = errorString .. failureDescription(handler.errors[#handler.errors]) string = errorString .. failureDescription(handler.errors[#handler.errors])
else else
string = "unexpected test status! ("..status..")" string = 'unexpected test status! (' .. status .. ')'
end end
write_status(element, string) write_status(element, string)

View File

@ -3,13 +3,19 @@ local mpack = require('mpack')
-- temporary hack to be able to manipulate buffer/window/tabpage -- temporary hack to be able to manipulate buffer/window/tabpage
local Buffer = {} local Buffer = {}
Buffer.__index = Buffer Buffer.__index = Buffer
function Buffer.new(id) return setmetatable({id=id}, Buffer) end function Buffer.new(id)
return setmetatable({ id = id }, Buffer)
end
local Window = {} local Window = {}
Window.__index = Window Window.__index = Window
function Window.new(id) return setmetatable({id=id}, Window) end function Window.new(id)
return setmetatable({ id = id }, Window)
end
local Tabpage = {} local Tabpage = {}
Tabpage.__index = Tabpage Tabpage.__index = Tabpage
function Tabpage.new(id) return setmetatable({id=id}, Tabpage) end function Tabpage.new(id)
return setmetatable({ id = id }, Tabpage)
end
local Response = {} local Response = {}
Response.__index = Response Response.__index = Response
@ -17,7 +23,7 @@ Response.__index = Response
function Response.new(msgpack_rpc_stream, request_id) function Response.new(msgpack_rpc_stream, request_id)
return setmetatable({ return setmetatable({
_msgpack_rpc_stream = msgpack_rpc_stream, _msgpack_rpc_stream = msgpack_rpc_stream,
_request_id = request_id _request_id = request_id,
}, Response) }, Response)
end end
@ -41,19 +47,31 @@ function MsgpackRpcStream.new(stream)
_stream = stream, _stream = stream,
_pack = mpack.Packer({ _pack = mpack.Packer({
ext = { ext = {
[Buffer] = function(o) return 0, mpack.encode(o.id) end, [Buffer] = function(o)
[Window] = function(o) return 1, mpack.encode(o.id) end, return 0, mpack.encode(o.id)
[Tabpage] = function(o) return 2, mpack.encode(o.id) end end,
} [Window] = function(o)
return 1, mpack.encode(o.id)
end,
[Tabpage] = function(o)
return 2, mpack.encode(o.id)
end,
},
}), }),
_session = mpack.Session({ _session = mpack.Session({
unpack = mpack.Unpacker({ unpack = mpack.Unpacker({
ext = { ext = {
[0] = function(_c, s) return Buffer.new(mpack.decode(s)) end, [0] = function(_c, s)
[1] = function(_c, s) return Window.new(mpack.decode(s)) end, return Buffer.new(mpack.decode(s))
[2] = function(_c, s) return Tabpage.new(mpack.decode(s)) end end,
} [1] = function(_c, s)
}) return Window.new(mpack.decode(s))
end,
[2] = function(_c, s)
return Tabpage.new(mpack.decode(s))
end,
},
}),
}), }),
}, MsgpackRpcStream) }, MsgpackRpcStream)
end end
@ -67,7 +85,7 @@ function MsgpackRpcStream:write(method, args, response_cb)
data = self._session:notify() data = self._session:notify()
end end
data = data .. self._pack(method) .. self._pack(args) data = data .. self._pack(method) .. self._pack(args)
self._stream:write(data) self._stream:write(data)
end end
@ -80,12 +98,10 @@ function MsgpackRpcStream:read_start(request_cb, notification_cb, eof_cb)
local pos = 1 local pos = 1
local len = #data local len = #data
while pos <= len do while pos <= len do
type, id_or_cb, method_or_error, args_or_result, pos = type, id_or_cb, method_or_error, args_or_result, pos = self._session:receive(data, pos)
self._session:receive(data, pos)
if type == 'request' or type == 'notification' then if type == 'request' or type == 'notification' then
if type == 'request' then if type == 'request' then
request_cb(method_or_error, args_or_result, Response.new(self, request_cb(method_or_error, args_or_result, Response.new(self, id_or_cb))
id_or_cb))
else else
notification_cb(method_or_error, args_or_result) notification_cb(method_or_error, args_or_result)
end end

View File

@ -7,7 +7,7 @@ if package.loaded['jit'] then
-- luajit pcall is already coroutine safe -- luajit pcall is already coroutine safe
Session.safe_pcall = pcall Session.safe_pcall = pcall
else else
Session.safe_pcall = require'coxpcall'.pcall Session.safe_pcall = require 'coxpcall'.pcall
end end
local function resume(co, ...) local function resume(co, ...)
@ -25,7 +25,7 @@ local function resume(co, ...)
end end
local function coroutine_exec(func, ...) local function coroutine_exec(func, ...)
local args = {...} local args = { ... }
local on_complete local on_complete
if #args > 0 and type(args[#args]) == 'function' then if #args > 0 and type(args[#args]) == 'function' then
@ -50,18 +50,18 @@ function Session.new(stream)
_pending_messages = {}, _pending_messages = {},
_prepare = uv.new_prepare(), _prepare = uv.new_prepare(),
_timer = uv.new_timer(), _timer = uv.new_timer(),
_is_running = false _is_running = false,
}, Session) }, Session)
end end
function Session:next_message(timeout) function Session:next_message(timeout)
local function on_request(method, args, response) local function on_request(method, args, response)
table.insert(self._pending_messages, {'request', method, args, response}) table.insert(self._pending_messages, { 'request', method, args, response })
uv.stop() uv.stop()
end end
local function on_notification(method, args) local function on_notification(method, args)
table.insert(self._pending_messages, {'notification', method, args}) table.insert(self._pending_messages, { 'notification', method, args })
uv.stop() uv.stop()
end end
@ -83,11 +83,11 @@ function Session:next_message(timeout)
end end
function Session:notify(method, ...) function Session:notify(method, ...)
self._msgpack_rpc_stream:write(method, {...}) self._msgpack_rpc_stream:write(method, { ... })
end end
function Session:request(method, ...) function Session:request(method, ...)
local args = {...} local args = { ... }
local err, result local err, result
if self._is_running then if self._is_running then
err, result = self:_yielding_request(method, args) err, result = self:_yielding_request(method, args)
@ -141,8 +141,12 @@ function Session:stop()
end end
function Session:close(signal) function Session:close(signal)
if not self._timer:is_closing() then self._timer:close() end if not self._timer:is_closing() then
if not self._prepare:is_closing() then self._prepare:close() end self._timer:close()
end
if not self._prepare:is_closing() then
self._prepare:close()
end
self._msgpack_rpc_stream:close(signal) self._msgpack_rpc_stream:close(signal)
self.closed = true self.closed = true
end end
@ -159,11 +163,11 @@ function Session:_blocking_request(method, args)
local err, result local err, result
local function on_request(method_, args_, response) local function on_request(method_, args_, response)
table.insert(self._pending_messages, {'request', method_, args_, response}) table.insert(self._pending_messages, { 'request', method_, args_, response })
end end
local function on_notification(method_, args_) local function on_notification(method_, args_)
table.insert(self._pending_messages, {'notification', method_, args_}) table.insert(self._pending_messages, { 'notification', method_, args_ })
end end
self._msgpack_rpc_stream:write(method, args, function(e, r) self._msgpack_rpc_stream:write(method, args, function(e, r)
@ -187,7 +191,7 @@ function Session:_run(request_cb, notification_cb, timeout)
end end
self._msgpack_rpc_stream:read_start(request_cb, notification_cb, function() self._msgpack_rpc_stream:read_start(request_cb, notification_cb, function()
uv.stop() uv.stop()
self.eof_err = {1, "EOF was received from Nvim. Likely the Nvim process crashed."} self.eof_err = { 1, 'EOF was received from Nvim. Likely the Nvim process crashed.' }
end) end)
uv.run() uv.run()
self._prepare:stop() self._prepare:stop()

View File

@ -6,7 +6,7 @@ StdioStream.__index = StdioStream
function StdioStream.open() function StdioStream.open()
local self = setmetatable({ local self = setmetatable({
_in = uv.new_pipe(false), _in = uv.new_pipe(false),
_out = uv.new_pipe(false) _out = uv.new_pipe(false),
}, StdioStream) }, StdioStream)
self._in:open(0) self._in:open(0)
self._out:open(1) self._out:open(1)
@ -42,9 +42,9 @@ function SocketStream.open(file)
local socket = uv.new_pipe(false) local socket = uv.new_pipe(false)
local self = setmetatable({ local self = setmetatable({
_socket = socket, _socket = socket,
_stream_error = nil _stream_error = nil,
}, SocketStream) }, SocketStream)
uv.pipe_connect(socket, file, function (err) uv.pipe_connect(socket, file, function(err)
self._stream_error = self._stream_error or err self._stream_error = self._stream_error or err
end) end)
return self return self
@ -54,15 +54,14 @@ function SocketStream.connect(host, port)
local socket = uv.new_tcp() local socket = uv.new_tcp()
local self = setmetatable({ local self = setmetatable({
_socket = socket, _socket = socket,
_stream_error = nil _stream_error = nil,
}, SocketStream) }, SocketStream)
uv.tcp_connect(socket, host, port, function (err) uv.tcp_connect(socket, host, port, function(err)
self._stream_error = self._stream_error or err self._stream_error = self._stream_error or err
end) end)
return self return self
end end
function SocketStream:write(data) function SocketStream:write(data)
if self._stream_error then if self._stream_error then
error(self._stream_error) error(self._stream_error)
@ -102,9 +101,9 @@ ChildProcessStream.__index = ChildProcessStream
function ChildProcessStream.spawn(argv, env, io_extra) function ChildProcessStream.spawn(argv, env, io_extra)
local self = setmetatable({ local self = setmetatable({
_child_stdin = uv.new_pipe(false); _child_stdin = uv.new_pipe(false),
_child_stdout = uv.new_pipe(false); _child_stdout = uv.new_pipe(false),
_exiting = false; _exiting = false,
}, ChildProcessStream) }, ChildProcessStream)
local prog = argv[1] local prog = argv[1]
local args = {} local args = {}
@ -112,7 +111,7 @@ function ChildProcessStream.spawn(argv, env, io_extra)
args[#args + 1] = argv[i] args[#args + 1] = argv[i]
end end
self._proc, self._pid = uv.spawn(prog, { self._proc, self._pid = uv.spawn(prog, {
stdio = {self._child_stdin, self._child_stdout, 2, io_extra}, stdio = { self._child_stdin, self._child_stdout, 2, io_extra },
args = args, args = args,
env = env, env = env,
}, function(status, signal) }, function(status, signal)
@ -154,7 +153,7 @@ function ChildProcessStream:close(signal)
self._child_stdin:close() self._child_stdin:close()
self._child_stdout:close() self._child_stdout:close()
if type(signal) == 'string' then if type(signal) == 'string' then
self._proc:kill('sig'..signal) self._proc:kill('sig' .. signal)
end end
while self.status == nil do while self.status == nil do
uv.run 'once' uv.run 'once'
@ -163,7 +162,7 @@ function ChildProcessStream:close(signal)
end end
return { return {
StdioStream = StdioStream; StdioStream = StdioStream,
ChildProcessStream = ChildProcessStream; ChildProcessStream = ChildProcessStream,
SocketStream = SocketStream; SocketStream = SocketStream,
} }

View File

@ -25,7 +25,7 @@ local module = {
local function relpath(p) local function relpath(p)
p = vim.fs.normalize(p) p = vim.fs.normalize(p)
local cwd = luv.cwd() local cwd = luv.cwd()
return p:gsub("^" .. cwd) return p:gsub('^' .. cwd)
end end
--- @param path string --- @param path string
@ -60,7 +60,7 @@ function module.argss_to_cmd(...)
for i = 1, select('#', ...) do for i = 1, select('#', ...) do
local arg = select(i, ...) local arg = select(i, ...)
if type(arg) == 'string' then if type(arg) == 'string' then
cmd = cmd .. ' ' ..shell_quote(arg) cmd = cmd .. ' ' .. shell_quote(arg)
else else
for _, subarg in ipairs(arg) do for _, subarg in ipairs(arg) do
cmd = cmd .. ' ' .. shell_quote(subarg) cmd = cmd .. ' ' .. shell_quote(subarg)
@ -92,19 +92,19 @@ function module.retry(max, max_ms, fn)
if status then if status then
return result return result
end end
luv.update_time() -- Update cached value of luv.now() (libuv: uv_now()). luv.update_time() -- Update cached value of luv.now() (libuv: uv_now()).
if (max and tries >= max) or (luv.now() - start_time > timeout) then if (max and tries >= max) or (luv.now() - start_time > timeout) then
busted.fail(string.format("retry() attempts: %d\n%s", tries, tostring(result)), 2) busted.fail(string.format('retry() attempts: %d\n%s', tries, tostring(result)), 2)
end end
tries = tries + 1 tries = tries + 1
luv.sleep(20) -- Avoid hot loop... luv.sleep(20) -- Avoid hot loop...
end end
end end
local check_logs_useless_lines = { local check_logs_useless_lines = {
['Warning: noted but unhandled ioctl']=1, ['Warning: noted but unhandled ioctl'] = 1,
['could cause spurious value errors to appear']=2, ['could cause spurious value errors to appear'] = 2,
['See README_MISSING_SYSCALL_OR_IOCTL for guidance']=3, ['See README_MISSING_SYSCALL_OR_IOCTL for guidance'] = 3,
} }
function module.eq(expected, actual, context) function module.eq(expected, actual, context)
@ -120,7 +120,10 @@ end
--- @param expected (any) description of expected result --- @param expected (any) description of expected result
--- @param actual (any) description of actual result --- @param actual (any) description of actual result
function module.ok(cond, expected, actual) function module.ok(cond, expected, actual)
assert((not expected and not actual) or (expected and actual), 'if "expected" is given, "actual" is also required') assert(
(not expected and not actual) or (expected and actual),
'if "expected" is given, "actual" is also required'
)
local msg = expected and ('expected %s, got: %s'):format(expected, tostring(actual)) or nil local msg = expected and ('expected %s, got: %s'):format(expected, tostring(actual)) or nil
return assert(cond, msg) return assert(cond, msg)
end end
@ -129,7 +132,7 @@ local function epicfail(state, arguments, _)
state.failure_message = arguments[1] state.failure_message = arguments[1]
return false return false
end end
assert:register("assertion", "epicfail", epicfail) assert:register('assertion', 'epicfail', epicfail)
function module.fail(msg) function module.fail(msg)
return assert.epicfail(msg) return assert.epicfail(msg)
end end
@ -157,14 +160,26 @@ function module.assert_log(pat, logfile, nrlines, inverse)
module.retry(nil, 1000, function() module.retry(nil, 1000, function()
local lines = module.read_file_list(logfile, -nrlines) or {} local lines = module.read_file_list(logfile, -nrlines) or {}
local msg = string.format('Pattern %q %sfound in log (last %d lines): %s:\n%s', local msg = string.format(
pat, (inverse and '' or 'not '), nrlines, logfile, ' '..table.concat(lines, '\n ')) 'Pattern %q %sfound in log (last %d lines): %s:\n%s',
for _,line in ipairs(lines) do pat,
(inverse and '' or 'not '),
nrlines,
logfile,
' ' .. table.concat(lines, '\n ')
)
for _, line in ipairs(lines) do
if line:match(pat) then if line:match(pat) then
if inverse then error(msg) else return end if inverse then
error(msg)
else
return
end
end end
end end
if not inverse then error(msg) end if not inverse then
error(msg)
end
end) end)
end end
@ -186,9 +201,10 @@ function module.pcall(fn, ...)
-- C:/long/path/foo.lua:186: Expected string, got number -- C:/long/path/foo.lua:186: Expected string, got number
-- to: -- to:
-- .../foo.lua:0: Expected string, got number -- .../foo.lua:0: Expected string, got number
local errmsg = tostring(rv):gsub('([%s<])vim[/\\]([^%s:/\\]+):%d+', '%1\xffvim\xff%2:0') local errmsg = tostring(rv)
:gsub('[^%s<]-[/\\]([^%s:/\\]+):%d+', '.../%1:0') :gsub('([%s<])vim[/\\]([^%s:/\\]+):%d+', '%1\xffvim\xff%2:0')
:gsub('\xffvim\xff', 'vim/') :gsub('[^%s<]-[/\\]([^%s:/\\]+):%d+', '.../%1:0')
:gsub('\xffvim\xff', 'vim/')
-- Scrub numbers in paths/stacktraces: -- Scrub numbers in paths/stacktraces:
-- shared.lua:0: in function 'gsplit' -- shared.lua:0: in function 'gsplit'
@ -233,9 +249,10 @@ end
function module.pcall_err_withtrace(fn, ...) function module.pcall_err_withtrace(fn, ...)
local errmsg = module.pcall_err_withfile(fn, ...) local errmsg = module.pcall_err_withfile(fn, ...)
return errmsg:gsub('^%.%.%./helpers%.lua:0: ', '') return errmsg
:gsub('^Error executing lua:- ' ,'') :gsub('^%.%.%./helpers%.lua:0: ', '')
:gsub('^%[string "<nvim>"%]:0: ' ,'') :gsub('^Error executing lua:- ', '')
:gsub('^%[string "<nvim>"%]:0: ', '')
end end
function module.pcall_err(...) function module.pcall_err(...)
@ -243,7 +260,7 @@ function module.pcall_err(...)
end end
function module.remove_trace(s) function module.remove_trace(s)
return (s:gsub("\n%s*stack traceback:.*", "")) return (s:gsub('\n%s*stack traceback:.*', ''))
end end
-- initial_path: directory to recurse into -- initial_path: directory to recurse into
@ -251,12 +268,14 @@ end
-- exc_re: exclude pattern(s) (string or table) -- exc_re: exclude pattern(s) (string or table)
function module.glob(initial_path, re, exc_re) function module.glob(initial_path, re, exc_re)
exc_re = type(exc_re) == 'table' and exc_re or { exc_re } exc_re = type(exc_re) == 'table' and exc_re or { exc_re }
local paths_to_check = {initial_path} local paths_to_check = { initial_path }
local ret = {} local ret = {}
local checked_files = {} local checked_files = {}
local function is_excluded(path) local function is_excluded(path)
for _, pat in pairs(exc_re) do for _, pat in pairs(exc_re) do
if path:match(pat) then return true end if path:match(pat) then
return true
end
end end
return false return false
end end
@ -318,7 +337,7 @@ function module.check_logs()
out:write(start_msg .. '\n') out:write(start_msg .. '\n')
if status then if status then
for line in f:lines() do for line in f:lines() do
out:write('= '..line..'\n') out:write('= ' .. line .. '\n')
end end
f:close() f:close()
else else
@ -331,9 +350,10 @@ function module.check_logs()
end end
end end
end end
assert(0 == #runtime_errors, string.format( assert(
'Found runtime errors in logfile(s): %s', 0 == #runtime_errors,
table.concat(runtime_errors, ', '))) string.format('Found runtime errors in logfile(s): %s', table.concat(runtime_errors, ', '))
)
end end
function module.sysname() function module.sysname()
@ -344,18 +364,16 @@ function module.sysname()
end end
function module.is_os(s) function module.is_os(s)
if not (s == 'win' if not (s == 'win' or s == 'mac' or s == 'freebsd' or s == 'openbsd' or s == 'bsd') then
or s == 'mac' error('unknown platform: ' .. tostring(s))
or s == 'freebsd'
or s == 'openbsd'
or s == 'bsd') then
error('unknown platform: '..tostring(s))
end end
return not not ((s == 'win' and (module.sysname():find('windows') or module.sysname():find('mingw'))) return not not (
(s == 'win' and (module.sysname():find('windows') or module.sysname():find('mingw')))
or (s == 'mac' and module.sysname() == 'darwin') or (s == 'mac' and module.sysname() == 'darwin')
or (s == 'freebsd' and module.sysname() == 'freebsd') or (s == 'freebsd' and module.sysname() == 'freebsd')
or (s == 'openbsd' and module.sysname() == 'openbsd') or (s == 'openbsd' and module.sysname() == 'openbsd')
or (s == 'bsd' and module.sysname():find('bsd'))) or (s == 'bsd' and module.sysname():find('bsd'))
)
end end
local function tmpdir_get() local function tmpdir_get()
@ -371,7 +389,7 @@ end
module.tmpname = (function() module.tmpname = (function()
local seq = 0 local seq = 0
local tmpdir = tmpdir_get() local tmpdir = tmpdir_get()
return (function() return function()
if tmpdir_is_local(tmpdir) then if tmpdir_is_local(tmpdir) then
-- Cannot control os.tmpname() dir, so hack our own tmpname() impl. -- Cannot control os.tmpname() dir, so hack our own tmpname() impl.
seq = seq + 1 seq = seq + 1
@ -384,15 +402,15 @@ module.tmpname = (function()
if module.is_os('win') and fname:sub(1, 2) == '\\s' then if module.is_os('win') and fname:sub(1, 2) == '\\s' then
-- In Windows tmpname() returns a filename starting with -- In Windows tmpname() returns a filename starting with
-- special sequence \s, prepend $TEMP path -- special sequence \s, prepend $TEMP path
return tmpdir..fname return tmpdir .. fname
elseif fname:match('^/tmp') and module.is_os('mac') then elseif fname:match('^/tmp') and module.is_os('mac') then
-- In OS X /tmp links to /private/tmp -- In OS X /tmp links to /private/tmp
return '/private'..fname return '/private' .. fname
else else
return fname return fname
end end
end end
end) end
end)() end)()
function module.hasenv(name) function module.hasenv(name)
@ -417,14 +435,17 @@ function module.check_cores(app, force) -- luacheck: ignore
end end
app = app or 'build/bin/nvim' -- luacheck: ignore app = app or 'build/bin/nvim' -- luacheck: ignore
local initial_path, re, exc_re local initial_path, re, exc_re
local gdb_db_cmd = 'gdb -n -batch -ex "thread apply all bt full" "$_NVIM_TEST_APP" -c "$_NVIM_TEST_CORE"' local gdb_db_cmd =
'gdb -n -batch -ex "thread apply all bt full" "$_NVIM_TEST_APP" -c "$_NVIM_TEST_CORE"'
local lldb_db_cmd = 'lldb -Q -o "bt all" -f "$_NVIM_TEST_APP" -c "$_NVIM_TEST_CORE"' local lldb_db_cmd = 'lldb -Q -o "bt all" -f "$_NVIM_TEST_APP" -c "$_NVIM_TEST_CORE"'
local random_skip = false local random_skip = false
-- Workspace-local $TMPDIR, scrubbed and pattern-escaped. -- Workspace-local $TMPDIR, scrubbed and pattern-escaped.
-- "./Xtest-tmpdir/" => "Xtest%-tmpdir" -- "./Xtest-tmpdir/" => "Xtest%-tmpdir"
local local_tmpdir = (tmpdir_is_local(tmpdir_get()) local local_tmpdir = (
and relpath(tmpdir_get()):gsub('^[ ./]+',''):gsub('%/+$',''):gsub('([^%w])', '%%%1') tmpdir_is_local(tmpdir_get())
or nil) and relpath(tmpdir_get()):gsub('^[ ./]+', ''):gsub('%/+$', ''):gsub('([^%w])', '%%%1')
or nil
)
local db_cmd local db_cmd
if module.hasenv('NVIM_TEST_CORE_GLOB_DIRECTORY') then if module.hasenv('NVIM_TEST_CORE_GLOB_DIRECTORY') then
initial_path = os.getenv('NVIM_TEST_CORE_GLOB_DIRECTORY') initial_path = os.getenv('NVIM_TEST_CORE_GLOB_DIRECTORY')
@ -444,7 +465,7 @@ function module.check_cores(app, force) -- luacheck: ignore
else else
re = '/core[^/]*$' re = '/core[^/]*$'
end end
exc_re = { '^/%.deps$', '^/%'..deps_prefix()..'$', local_tmpdir, '^/%node_modules$' } exc_re = { '^/%.deps$', '^/%' .. deps_prefix() .. '$', local_tmpdir, '^/%node_modules$' }
db_cmd = gdb_db_cmd db_cmd = gdb_db_cmd
random_skip = true random_skip = true
end end
@ -457,7 +478,7 @@ function module.check_cores(app, force) -- luacheck: ignore
local found_cores = 0 local found_cores = 0
local out = io.stdout local out = io.stdout
for _, core in ipairs(cores) do for _, core in ipairs(cores) do
local len = 80 - #core - #('Core file ') - 2 local len = 80 - #core - #'Core file ' - 2
local esigns = ('='):rep(len / 2) local esigns = ('='):rep(len / 2)
out:write(('\n%s Core file %s %s\n'):format(esigns, core, esigns)) out:write(('\n%s Core file %s %s\n'):format(esigns, core, esigns))
out:flush() out:flush()
@ -471,7 +492,7 @@ function module.check_cores(app, force) -- luacheck: ignore
end end
tests_skipped = 0 tests_skipped = 0
if found_cores > 0 then if found_cores > 0 then
error("crash detected (see above)") error('crash detected (see above)')
end end
end end
@ -597,9 +618,9 @@ function module.dedent(str, leave_indent)
-- create a pattern for the indent -- create a pattern for the indent
indent = indent:gsub('%s', '[ \t]') indent = indent:gsub('%s', '[ \t]')
-- strip it from the first line -- strip it from the first line
str = str:gsub('^'..indent, left_indent) str = str:gsub('^' .. indent, left_indent)
-- strip it from the remaining lines -- strip it from the remaining lines
str = str:gsub('[\n]'..indent, '\n' .. left_indent) str = str:gsub('[\n]' .. indent, '\n' .. left_indent)
return str return str
end end
@ -611,13 +632,38 @@ local function format_float(v)
end end
local SUBTBL = { local SUBTBL = {
'\\000', '\\001', '\\002', '\\003', '\\004', '\\000',
'\\005', '\\006', '\\007', '\\008', '\\t', '\\001',
'\\n', '\\011', '\\012', '\\r', '\\014', '\\002',
'\\015', '\\016', '\\017', '\\018', '\\019', '\\003',
'\\020', '\\021', '\\022', '\\023', '\\024', '\\004',
'\\025', '\\026', '\\027', '\\028', '\\029', '\\005',
'\\030', '\\031', '\\006',
'\\007',
'\\008',
'\\t',
'\\n',
'\\011',
'\\012',
'\\r',
'\\014',
'\\015',
'\\016',
'\\017',
'\\018',
'\\019',
'\\020',
'\\021',
'\\022',
'\\023',
'\\024',
'\\025',
'\\026',
'\\027',
'\\028',
'\\029',
'\\030',
'\\031',
} }
-- Formats Lua value `v`. -- Formats Lua value `v`.
@ -647,13 +693,14 @@ function module.format_luav(v, indent, opts)
if opts.literal_strings then if opts.literal_strings then
ret = v ret = v
else else
local quote = opts.dquote_strings and '"' or '\'' local quote = opts.dquote_strings and '"' or "'"
ret = quote .. tostring(v):gsub( ret = quote
opts.dquote_strings and '["\\]' or '[\'\\]', .. tostring(v)
'\\%0'):gsub( :gsub(opts.dquote_strings and '["\\]' or "['\\]", '\\%0')
'[%z\1-\31]', function(match) :gsub('[%z\1-\31]', function(match)
return SUBTBL[match:byte() + 1] return SUBTBL[match:byte() + 1]
end) .. quote end)
.. quote
end end
elseif type(v) == 'table' then elseif type(v) == 'table' then
if v == module.REMOVE_THIS then if v == module.REMOVE_THIS then
@ -664,8 +711,7 @@ function module.format_luav(v, indent, opts)
local non_empty = false local non_empty = false
local format_luav = module.format_luav local format_luav = module.format_luav
for i, subv in ipairs(v) do for i, subv in ipairs(v) do
ret = ('%s%s%s,%s'):format(ret, next_indent, ret = ('%s%s%s,%s'):format(ret, next_indent, format_luav(subv, next_indent_arg, opts), nl)
format_luav(subv, next_indent_arg, opts), nl)
processed_keys[i] = true processed_keys[i] = true
non_empty = true non_empty = true
end end
@ -674,8 +720,7 @@ function module.format_luav(v, indent, opts)
if type(k) == 'string' and k:match('^[a-zA-Z_][a-zA-Z0-9_]*$') then if type(k) == 'string' and k:match('^[a-zA-Z_][a-zA-Z0-9_]*$') then
ret = ret .. next_indent .. k .. ' = ' ret = ret .. next_indent .. k .. ' = '
else else
ret = ('%s%s[%s] = '):format(ret, next_indent, ret = ('%s%s[%s] = '):format(ret, next_indent, format_luav(k, nil, opts))
format_luav(k, nil, opts))
end end
ret = ret .. format_luav(subv, next_indent_arg, opts) .. ',' .. nl ret = ret .. format_luav(subv, next_indent_arg, opts) .. ',' .. nl
non_empty = true non_empty = true
@ -684,7 +729,7 @@ function module.format_luav(v, indent, opts)
if nl == ' ' and non_empty then if nl == ' ' and non_empty then
ret = ret:sub(1, -3) ret = ret:sub(1, -3)
end end
ret = ret .. indent .. '}' ret = ret .. indent .. '}'
end end
elseif type(v) == 'number' then elseif type(v) == 'number' then
if v % 1 == 0 then if v % 1 == 0 then
@ -709,7 +754,7 @@ end
-- Commit: 520c0b91a528 -- Commit: 520c0b91a528
function module.format_string(fmt, ...) function module.format_string(fmt, ...)
local i = 0 local i = 0
local args = {...} local args = { ... }
local function getarg() local function getarg()
i = i + 1 i = i + 1
return args[i] return args[i]
@ -728,7 +773,7 @@ function module.format_string(fmt, ...)
-- Builtin %q is replaced here as it gives invalid and inconsistent with -- Builtin %q is replaced here as it gives invalid and inconsistent with
-- luajit results for e.g. "\e" on lua: luajit transforms that into `\27`, -- luajit results for e.g. "\e" on lua: luajit transforms that into `\27`,
-- lua leaves as-is. -- lua leaves as-is.
arg = module.format_luav(arg, nil, {dquote_strings = (subfmt:sub(-1) == 'q')}) arg = module.format_luav(arg, nil, { dquote_strings = (subfmt:sub(-1) == 'q') })
subfmt = subfmt:sub(1, -2) .. 's' subfmt = subfmt:sub(1, -2) .. 's'
end end
if subfmt == '%e' then if subfmt == '%e' then
@ -767,27 +812,27 @@ end
function module.hexdump(str) function module.hexdump(str)
local len = string.len(str) local len = string.len(str)
local dump = "" local dump = ''
local hex = "" local hex = ''
local asc = "" local asc = ''
for i = 1, len do for i = 1, len do
if 1 == i % 8 then if 1 == i % 8 then
dump = dump .. hex .. asc .. "\n" dump = dump .. hex .. asc .. '\n'
hex = string.format("%04x: ", i - 1) hex = string.format('%04x: ', i - 1)
asc = "" asc = ''
end end
local ord = string.byte(str, i) local ord = string.byte(str, i)
hex = hex .. string.format("%02x ", ord) hex = hex .. string.format('%02x ', ord)
if ord >= 32 and ord <= 126 then if ord >= 32 and ord <= 126 then
asc = asc .. string.char(ord) asc = asc .. string.char(ord)
else else
asc = asc .. "." asc = asc .. '.'
end end
end end
return dump .. hex .. string.rep(" ", 8 - len % 8) .. asc return dump .. hex .. string.rep(' ', 8 - len % 8) .. asc
end end
-- Reads text lines from `filename` into a table. -- Reads text lines from `filename` into a table.
@ -805,16 +850,16 @@ function module.read_file_list(filename, start)
-- There is no need to read more than the last 2MB of the log file, so seek -- There is no need to read more than the last 2MB of the log file, so seek
-- to that. -- to that.
local file_size = file:seek("end") local file_size = file:seek('end')
local offset = file_size - 2000000 local offset = file_size - 2000000
if offset < 0 then if offset < 0 then
offset = 0 offset = 0
end end
file:seek("set", offset) file:seek('set', offset)
local lines = {} local lines = {}
local i = 1 local i = 1
local line = file:read("*l") local line = file:read('*l')
while line ~= nil do while line ~= nil do
if i >= start then if i >= start then
table.insert(lines, line) table.insert(lines, line)
@ -823,7 +868,7 @@ function module.read_file_list(filename, start)
end end
end end
i = i + 1 i = i + 1
line = file:read("*l") line = file:read('*l')
end end
file:close() file:close()
return lines return lines
@ -875,13 +920,16 @@ function module.read_nvim_log(logfile, ci_rename)
local is_ci = module.is_ci() local is_ci = module.is_ci()
local keep = is_ci and 100 or 10 local keep = is_ci and 100 or 10
local lines = module.read_file_list(logfile, -keep) or {} local lines = module.read_file_list(logfile, -keep) or {}
local log = (('-'):rep(78)..'\n' local log = (
..string.format('$NVIM_LOG_FILE: %s\n', logfile) ('-'):rep(78)
..(#lines > 0 and '(last '..tostring(keep)..' lines)\n' or '(empty)\n')) .. '\n'
for _,line in ipairs(lines) do .. string.format('$NVIM_LOG_FILE: %s\n', logfile)
log = log..line..'\n' .. (#lines > 0 and '(last ' .. tostring(keep) .. ' lines)\n' or '(empty)\n')
)
for _, line in ipairs(lines) do
log = log .. line .. '\n'
end end
log = log..('-'):rep(78)..'\n' log = log .. ('-'):rep(78) .. '\n'
if is_ci and ci_rename then if is_ci and ci_rename then
os.rename(logfile, logfile .. '.displayed') os.rename(logfile, logfile .. '.displayed')
end end

View File

@ -1,9 +1,13 @@
local platform = vim.uv.os_uname() local platform = vim.uv.os_uname()
local deps_install_dir = table.remove(_G.arg, 1) local deps_install_dir = table.remove(_G.arg, 1)
local subcommand = table.remove(_G.arg, 1) local subcommand = table.remove(_G.arg, 1)
local suffix = (platform and platform.sysname:lower():find'windows') and '.dll' or '.so' local suffix = (platform and platform.sysname:lower():find 'windows') and '.dll' or '.so'
package.path = deps_install_dir.."/share/lua/5.1/?.lua;"..deps_install_dir.."/share/lua/5.1/?/init.lua;"..package.path package.path = deps_install_dir
package.cpath = deps_install_dir.."/lib/lua/5.1/?"..suffix..";"..package.cpath; .. '/share/lua/5.1/?.lua;'
.. deps_install_dir
.. '/share/lua/5.1/?/init.lua;'
.. package.path
package.cpath = deps_install_dir .. '/lib/lua/5.1/?' .. suffix .. ';' .. package.cpath
local uv = vim.uv local uv = vim.uv
@ -15,14 +19,14 @@ local system = {}
package.loaded['system.core'] = system package.loaded['system.core'] = system
function system.monotime() function system.monotime()
uv.update_time() uv.update_time()
return uv.now()*1e-3 return uv.now() * 1e-3
end end
function system.gettime() function system.gettime()
local sec, usec = uv.gettimeofday() local sec, usec = uv.gettimeofday()
return sec+usec*1e-6 return sec + usec * 1e-6
end end
function system.sleep(sec) function system.sleep(sec)
uv.sleep(sec*1e3) uv.sleep(sec * 1e3)
end end
local term = {} local term = {}
@ -31,7 +35,7 @@ function term.isatty(_)
return uv.guess_handle(1) == 'tty' return uv.guess_handle(1) == 'tty'
end end
local lfs = {_VERSION = 'fake'} local lfs = { _VERSION = 'fake' }
package.loaded['lfs'] = lfs package.loaded['lfs'] = lfs
function lfs.attributes(path, attr) function lfs.attributes(path, attr)
@ -39,9 +43,11 @@ function lfs.attributes(path, attr)
if attr == 'mode' then if attr == 'mode' then
return stat and stat.type or '' return stat and stat.type or ''
elseif attr == 'modification' then elseif attr == 'modification' then
if not stat then return nil end if not stat then
return nil
end
local mtime = stat.mtime local mtime = stat.mtime
return mtime.sec + mtime.nsec*1e-9 return mtime.sec + mtime.nsec * 1e-9
else else
error('not implemented') error('not implemented')
end end
@ -74,9 +80,9 @@ function lfs.mkdir(dir)
return uv.fs_mkdir(dir, 493) -- octal 755 return uv.fs_mkdir(dir, 493) -- octal 755
end end
if subcommand == "busted" then if subcommand == 'busted' then
require 'busted.runner'({ standalone = false }) require 'busted.runner'({ standalone = false })
elseif subcommand == "luacheck" then elseif subcommand == 'luacheck' then
require 'luacheck.main' require 'luacheck.main'
else else
error 'unknown subcommand' error 'unknown subcommand'

View File

@ -13,9 +13,11 @@ local int_type = eval_helpers.int_type
local flt_type = eval_helpers.flt_type local flt_type = eval_helpers.flt_type
local type_key = eval_helpers.type_key local type_key = eval_helpers.type_key
local api = cimport('./src/nvim/api/private/defs.h', local api = cimport(
'./src/nvim/api/private/helpers.h', './src/nvim/api/private/defs.h',
'./src/nvim/memory.h') './src/nvim/api/private/helpers.h',
'./src/nvim/memory.h'
)
local obj2lua local obj2lua
@ -27,8 +29,8 @@ local function init_obj2lua_tab()
end end
obj2lua_tab = { obj2lua_tab = {
[tonumber(api.kObjectTypeArray)] = function(obj) [tonumber(api.kObjectTypeArray)] = function(obj)
local ret = {[type_key]=list_type} local ret = { [type_key] = list_type }
for i = 1,tonumber(obj.data.array.size) do for i = 1, tonumber(obj.data.array.size) do
ret[i] = obj2lua(obj.data.array.items[i - 1]) ret[i] = obj2lua(obj.data.array.items[i - 1])
end end
if ret[1] then if ret[1] then
@ -38,7 +40,7 @@ local function init_obj2lua_tab()
end, end,
[tonumber(api.kObjectTypeDictionary)] = function(obj) [tonumber(api.kObjectTypeDictionary)] = function(obj)
local ret = {} local ret = {}
for i = 1,tonumber(obj.data.dictionary.size) do for i = 1, tonumber(obj.data.dictionary.size) do
local kv_pair = obj.data.dictionary.items[i - 1] local kv_pair = obj.data.dictionary.items[i - 1]
ret[ffi.string(kv_pair.key.data, kv_pair.key.size)] = obj2lua(kv_pair.value) ret[ffi.string(kv_pair.key.data, kv_pair.key.size)] = obj2lua(kv_pair.value)
end end
@ -58,7 +60,7 @@ local function init_obj2lua_tab()
return tonumber(obj.data.floating) return tonumber(obj.data.floating)
end, end,
[tonumber(api.kObjectTypeInteger)] = function(obj) [tonumber(api.kObjectTypeInteger)] = function(obj)
return {[type_key]=int_type, value=tonumber(obj.data.integer)} return { [type_key] = int_type, value = tonumber(obj.data.integer) }
end, end,
[tonumber(api.kObjectTypeString)] = function(obj) [tonumber(api.kObjectTypeString)] = function(obj)
return ffi.string(obj.data.string.data, obj.data.string.size) return ffi.string(obj.data.string.data, obj.data.string.size)
@ -68,32 +70,38 @@ end
obj2lua = function(obj) obj2lua = function(obj)
init_obj2lua_tab() init_obj2lua_tab()
return ((obj2lua_tab[tonumber(obj['type'])] or function(obj_inner) return (
assert(false, 'Converting ' .. tostring(tonumber(obj_inner['type'])) .. ' is not implementing yet') (obj2lua_tab[tonumber(obj['type'])] or function(obj_inner)
end)(obj)) assert(
false,
'Converting ' .. tostring(tonumber(obj_inner['type'])) .. ' is not implementing yet'
)
end)(obj)
)
end end
local obj = function(typ, data) local obj = function(typ, data)
return ffi.gc(ffi.new('Object', {['type']=typ, data=data}), return ffi.gc(ffi.new('Object', { ['type'] = typ, data = data }), api.api_free_object)
api.api_free_object)
end end
local lua2obj local lua2obj
local lua2obj_type_tab = { local lua2obj_type_tab = {
[int_type] = function(l) [int_type] = function(l)
return obj(api.kObjectTypeInteger, {integer=l.value}) return obj(api.kObjectTypeInteger, { integer = l.value })
end, end,
[flt_type] = function(l) [flt_type] = function(l)
return obj(api.kObjectTypeFloat, {floating=l}) return obj(api.kObjectTypeFloat, { floating = l })
end, end,
[list_type] = function(l) [list_type] = function(l)
local len = #l local len = #l
local arr = obj(api.kObjectTypeArray, {array={ local arr = obj(api.kObjectTypeArray, {
size=len, array = {
capacity=len, size = len,
items=ffi.cast('Object *', api.xmalloc(len * ffi.sizeof('Object'))), capacity = len,
}}) items = ffi.cast('Object *', api.xmalloc(len * ffi.sizeof('Object'))),
},
})
for i = 1, len do for i = 1, len do
arr.data.array.items[i - 1] = ffi.gc(lua2obj(l[i]), nil) arr.data.array.items[i - 1] = ffi.gc(lua2obj(l[i]), nil)
end end
@ -103,21 +111,23 @@ local lua2obj_type_tab = {
local kvs = {} local kvs = {}
for k, v in pairs(l) do for k, v in pairs(l) do
if type(k) == 'string' then if type(k) == 'string' then
kvs[#kvs + 1] = {k, v} kvs[#kvs + 1] = { k, v }
end end
end end
local len = #kvs local len = #kvs
local dct = obj(api.kObjectTypeDictionary, {dictionary={ local dct = obj(api.kObjectTypeDictionary, {
size=len, dictionary = {
capacity=len, size = len,
items=ffi.cast('KeyValuePair *', capacity = len,
api.xmalloc(len * ffi.sizeof('KeyValuePair'))), items = ffi.cast('KeyValuePair *', api.xmalloc(len * ffi.sizeof('KeyValuePair'))),
}}) },
})
for i = 1, len do for i = 1, len do
local key, val = unpack(kvs[i]) local key, val = unpack(kvs[i])
dct.data.dictionary.items[i - 1] = ffi.new( dct.data.dictionary.items[i - 1] = ffi.new(
'KeyValuePair', {key=ffi.gc(lua2obj(key), nil).data.string, 'KeyValuePair',
value=ffi.gc(lua2obj(val), nil)}) { key = ffi.gc(lua2obj(key), nil).data.string, value = ffi.gc(lua2obj(val), nil) }
)
end end
return dct return dct
end, end,
@ -137,28 +147,31 @@ lua2obj = function(l)
elseif type(l) == 'number' then elseif type(l) == 'number' then
return lua2obj_type_tab[flt_type](l) return lua2obj_type_tab[flt_type](l)
elseif type(l) == 'boolean' then elseif type(l) == 'boolean' then
return obj(api.kObjectTypeBoolean, {boolean=l}) return obj(api.kObjectTypeBoolean, { boolean = l })
elseif type(l) == 'string' then elseif type(l) == 'string' then
return obj(api.kObjectTypeString, {string={ return obj(
size=#l, api.kObjectTypeString,
data=api.xmemdupz(to_cstr(l), #l), { string = {
}}) size = #l,
data = api.xmemdupz(to_cstr(l), #l),
} }
)
elseif l == nil or l == nil_value then elseif l == nil or l == nil_value then
return obj(api.kObjectTypeNil, {integer=0}) return obj(api.kObjectTypeNil, { integer = 0 })
end end
end end
return { return {
list_type=list_type, list_type = list_type,
dict_type=dict_type, dict_type = dict_type,
func_type=func_type, func_type = func_type,
int_type=int_type, int_type = int_type,
flt_type=flt_type, flt_type = flt_type,
nil_value=nil_value, nil_value = nil_value,
type_key=type_key, type_key = type_key,
obj2lua=obj2lua, obj2lua = obj2lua,
lua2obj=lua2obj, lua2obj = lua2obj,
} }

View File

@ -42,49 +42,61 @@ describe('vim_to_object', function()
simple_test('converts -1.5', -1.5) simple_test('converts -1.5', -1.5)
simple_test('converts empty string', '') simple_test('converts empty string', '')
simple_test('converts non-empty string', 'foobar') simple_test('converts non-empty string', 'foobar')
simple_test('converts integer 10', {[type_key]=int_type, value=10}) simple_test('converts integer 10', { [type_key] = int_type, value = 10 })
simple_test('converts empty dictionary', {}) simple_test('converts empty dictionary', {})
simple_test('converts dictionary with scalar values', {test=10, test2=true, test3='test'}) simple_test('converts dictionary with scalar values', { test = 10, test2 = true, test3 = 'test' })
simple_test('converts dictionary with containers inside', {test={}, test2={1, 2}}) simple_test('converts dictionary with containers inside', { test = {}, test2 = { 1, 2 } })
simple_test('converts empty list', {[type_key]=list_type}) simple_test('converts empty list', { [type_key] = list_type })
simple_test('converts list with scalar values', {1, 2, 'test', 'foo'}) simple_test('converts list with scalar values', { 1, 2, 'test', 'foo' })
simple_test('converts list with containers inside', {{}, {test={}, test3={test4=true}}}) simple_test(
'converts list with containers inside',
{ {}, { test = {}, test3 = { test4 = true } } }
)
local dct = {} local dct = {}
dct.dct = dct dct.dct = dct
different_output_test('outputs nil for nested dictionaries (1 level)', dct, {dct=nil_value}) different_output_test('outputs nil for nested dictionaries (1 level)', dct, { dct = nil_value })
local lst = {} local lst = {}
lst[1] = lst lst[1] = lst
different_output_test('outputs nil for nested lists (1 level)', lst, {nil_value}) different_output_test('outputs nil for nested lists (1 level)', lst, { nil_value })
local dct2 = {test=true, dict=nil_value} local dct2 = { test = true, dict = nil_value }
dct2.dct = {dct2} dct2.dct = { dct2 }
different_output_test('outputs nil for nested dictionaries (2 level, in list)', different_output_test(
dct2, {dct={nil_value}, test=true, dict=nil_value}) 'outputs nil for nested dictionaries (2 level, in list)',
dct2,
{ dct = { nil_value }, test = true, dict = nil_value }
)
local dct3 = {test=true, dict=nil_value} local dct3 = { test = true, dict = nil_value }
dct3.dct = {dctin=dct3} dct3.dct = { dctin = dct3 }
different_output_test('outputs nil for nested dictionaries (2 level, in dict)', different_output_test(
dct3, {dct={dctin=nil_value}, test=true, dict=nil_value}) 'outputs nil for nested dictionaries (2 level, in dict)',
dct3,
{ dct = { dctin = nil_value }, test = true, dict = nil_value }
)
local lst2 = {} local lst2 = {}
lst2[1] = {lst2} lst2[1] = { lst2 }
different_output_test('outputs nil for nested lists (2 level, in list)', lst2, {{nil_value}}) different_output_test('outputs nil for nested lists (2 level, in list)', lst2, { { nil_value } })
local lst3 = {nil, true, false, 'ttest'} local lst3 = { nil, true, false, 'ttest' }
lst3[1] = {lst=lst3} lst3[1] = { lst = lst3 }
different_output_test('outputs nil for nested lists (2 level, in dict)', different_output_test(
lst3, {{lst=nil_value}, true, false, 'ttest'}) 'outputs nil for nested lists (2 level, in dict)',
lst3,
{ { lst = nil_value }, true, false, 'ttest' }
)
itp('outputs empty list for NULL list', function() itp('outputs empty list for NULL list', function()
local tt = typvalt('VAR_LIST', {v_list=NULL}) local tt = typvalt('VAR_LIST', { v_list = NULL })
eq(nil, tt.vval.v_list) eq(nil, tt.vval.v_list)
eq({[type_key]=list_type}, obj2lua(api.vim_to_object(tt))) eq({ [type_key] = list_type }, obj2lua(api.vim_to_object(tt)))
end) end)
itp('outputs empty dict for NULL dict', function() itp('outputs empty dict for NULL dict', function()
local tt = typvalt('VAR_DICT', {v_dict=NULL}) local tt = typvalt('VAR_DICT', { v_dict = NULL })
eq(nil, tt.vval.v_dict) eq(nil, tt.vval.v_dict)
eq({}, obj2lua(api.vim_to_object(tt))) eq({}, obj2lua(api.vim_to_object(tt)))
end) end)
@ -92,15 +104,15 @@ describe('vim_to_object', function()
itp('regression: partials in a list', function() itp('regression: partials in a list', function()
local llist = { local llist = {
{ {
[type_key]=func_type, [type_key] = func_type,
value='printf', value = 'printf',
args={'%s'}, args = { '%s' },
dict={v=1}, dict = { v = 1 },
}, },
{}, {},
} }
local list = lua2typvalt(llist) local list = lua2typvalt(llist)
eq(llist, typvalt2lua(list)) eq(llist, typvalt2lua(list))
eq({nil_value, {}}, obj2lua(api.vim_to_object(list))) eq({ nil_value, {} }, obj2lua(api.vim_to_object(list)))
end) end)
end) end)

View File

@ -1,14 +1,13 @@
local helpers = require("test.unit.helpers")(after_each) local helpers = require('test.unit.helpers')(after_each)
local itp = helpers.gen_itp(it) local itp = helpers.gen_itp(it)
local to_cstr = helpers.to_cstr local to_cstr = helpers.to_cstr
local eq = helpers.eq local eq = helpers.eq
local NULL = helpers.NULL local NULL = helpers.NULL
local buffer = helpers.cimport("./src/nvim/buffer.h") local buffer = helpers.cimport('./src/nvim/buffer.h')
describe('buffer functions', function() describe('buffer functions', function()
local buflist_new = function(file, flags) local buflist_new = function(file, flags)
local c_file = to_cstr(file) local c_file = to_cstr(file)
return buffer.buflist_new(c_file, c_file, 1, flags) return buffer.buflist_new(c_file, c_file, 1, flags)
@ -36,7 +35,6 @@ describe('buffer functions', function()
end) end)
describe('buf_valid', function() describe('buf_valid', function()
itp('should view NULL as an invalid buffer', function() itp('should view NULL as an invalid buffer', function()
eq(false, buffer.buf_valid(NULL)) eq(false, buffer.buf_valid(NULL))
end) end)
@ -72,11 +70,9 @@ describe('buffer functions', function()
end) end)
end) end)
describe('buflist_findpat', function() describe('buflist_findpat', function()
local ALLOW_UNLISTED = 1 local ALLOW_UNLISTED = 1
local ONLY_LISTED = 0 local ONLY_LISTED = 0
local buflist_findpat = function(pat, allow_unlisted) local buflist_findpat = function(pat, allow_unlisted)
return buffer.buflist_findpat(to_cstr(pat), NULL, allow_unlisted, 0, 0) return buffer.buflist_findpat(to_cstr(pat), NULL, allow_unlisted, 0, 0)
@ -95,9 +91,9 @@ describe('buffer functions', function()
local buf2 = buflist_new(path2, buffer.BLN_LISTED) local buf2 = buflist_new(path2, buffer.BLN_LISTED)
local buf3 = buflist_new(path3, buffer.BLN_LISTED) local buf3 = buflist_new(path3, buffer.BLN_LISTED)
eq(buf1.handle, buflist_findpat("test", ONLY_LISTED)) eq(buf1.handle, buflist_findpat('test', ONLY_LISTED))
eq(buf2.handle, buflist_findpat("file", ONLY_LISTED)) eq(buf2.handle, buflist_findpat('file', ONLY_LISTED))
eq(buf3.handle, buflist_findpat("path", ONLY_LISTED)) eq(buf3.handle, buflist_findpat('path', ONLY_LISTED))
close_buffer(NULL, buf1, buffer.DOBUF_WIPE, 0, 0) close_buffer(NULL, buf1, buffer.DOBUF_WIPE, 0, 0)
close_buffer(NULL, buf2, buffer.DOBUF_WIPE, 0, 0) close_buffer(NULL, buf2, buffer.DOBUF_WIPE, 0, 0)
@ -111,7 +107,7 @@ describe('buffer functions', function()
local buf3 = buflist_new(path3, buffer.BLN_LISTED) local buf3 = buflist_new(path3, buffer.BLN_LISTED)
-- Then: buf2 is the buffer that is found -- Then: buf2 is the buffer that is found
eq(buf2.handle, buflist_findpat("test", ONLY_LISTED)) eq(buf2.handle, buflist_findpat('test', ONLY_LISTED))
--} --}
--{ When: We close buf2 --{ When: We close buf2
@ -121,7 +117,7 @@ describe('buffer functions', function()
local buf1 = buflist_new(path1, buffer.BLN_LISTED) local buf1 = buflist_new(path1, buffer.BLN_LISTED)
-- Then: buf3 is found since 'file' appears at the end of the name -- Then: buf3 is found since 'file' appears at the end of the name
eq(buf3.handle, buflist_findpat("file", ONLY_LISTED)) eq(buf3.handle, buflist_findpat('file', ONLY_LISTED))
--} --}
close_buffer(NULL, buf1, buffer.DOBUF_WIPE, 0, 0) close_buffer(NULL, buf1, buffer.DOBUF_WIPE, 0, 0)
@ -133,7 +129,7 @@ describe('buffer functions', function()
local buf2 = buflist_new(path2, buffer.BLN_LISTED) local buf2 = buflist_new(path2, buffer.BLN_LISTED)
local buf3 = buflist_new(path3, buffer.BLN_LISTED) local buf3 = buflist_new(path3, buffer.BLN_LISTED)
eq(buf3.handle, buflist_findpat("_test_", ONLY_LISTED)) eq(buf3.handle, buflist_findpat('_test_', ONLY_LISTED))
close_buffer(NULL, buf1, buffer.DOBUF_WIPE, 0, 0) close_buffer(NULL, buf1, buffer.DOBUF_WIPE, 0, 0)
close_buffer(NULL, buf2, buffer.DOBUF_WIPE, 0, 0) close_buffer(NULL, buf2, buffer.DOBUF_WIPE, 0, 0)
@ -145,25 +141,25 @@ describe('buffer functions', function()
local buf3 = buflist_new(path3, buffer.BLN_LISTED) local buf3 = buflist_new(path3, buffer.BLN_LISTED)
-- Then: We should find the buffer when it is given a unique pattern -- Then: We should find the buffer when it is given a unique pattern
eq(buf3.handle, buflist_findpat("_test_", ONLY_LISTED)) eq(buf3.handle, buflist_findpat('_test_', ONLY_LISTED))
--} --}
--{ When: We unlist the buffer --{ When: We unlist the buffer
close_buffer(NULL, buf3, buffer.DOBUF_DEL, 0, 0) close_buffer(NULL, buf3, buffer.DOBUF_DEL, 0, 0)
-- Then: It should not find the buffer when searching only listed buffers -- Then: It should not find the buffer when searching only listed buffers
eq(-1, buflist_findpat("_test_", ONLY_LISTED)) eq(-1, buflist_findpat('_test_', ONLY_LISTED))
-- And: It should find the buffer when including unlisted buffers -- And: It should find the buffer when including unlisted buffers
eq(buf3.handle, buflist_findpat("_test_", ALLOW_UNLISTED)) eq(buf3.handle, buflist_findpat('_test_', ALLOW_UNLISTED))
--} --}
--{ When: We wipe the buffer --{ When: We wipe the buffer
close_buffer(NULL, buf3, buffer.DOBUF_WIPE, 0, 0) close_buffer(NULL, buf3, buffer.DOBUF_WIPE, 0, 0)
-- Then: It should not find the buffer at all -- Then: It should not find the buffer at all
eq(-1, buflist_findpat("_test_", ONLY_LISTED)) eq(-1, buflist_findpat('_test_', ONLY_LISTED))
eq(-1, buflist_findpat("_test_", ALLOW_UNLISTED)) eq(-1, buflist_findpat('_test_', ALLOW_UNLISTED))
--} --}
end) end)
@ -173,7 +169,7 @@ describe('buffer functions', function()
local buf2 = buflist_new(path2, buffer.BLN_LISTED) local buf2 = buflist_new(path2, buffer.BLN_LISTED)
-- Then: The first buffer is preferred when both are listed -- Then: The first buffer is preferred when both are listed
eq(buf1.handle, buflist_findpat("test", ONLY_LISTED)) eq(buf1.handle, buflist_findpat('test', ONLY_LISTED))
--} --}
--{ When: The first buffer is unlisted --{ When: The first buffer is unlisted
@ -181,13 +177,13 @@ describe('buffer functions', function()
-- Then: The second buffer is preferred because -- Then: The second buffer is preferred because
-- unlisted buffers are not allowed -- unlisted buffers are not allowed
eq(buf2.handle, buflist_findpat("test", ONLY_LISTED)) eq(buf2.handle, buflist_findpat('test', ONLY_LISTED))
--} --}
--{ When: We allow unlisted buffers --{ When: We allow unlisted buffers
-- Then: The second buffer is still preferred -- Then: The second buffer is still preferred
-- because listed buffers are preferred to unlisted -- because listed buffers are preferred to unlisted
eq(buf2.handle, buflist_findpat("test", ALLOW_UNLISTED)) eq(buf2.handle, buflist_findpat('test', ALLOW_UNLISTED))
--} --}
--{ When: We unlist the second buffer --{ When: We unlist the second buffer
@ -196,10 +192,10 @@ describe('buffer functions', function()
-- Then: The first buffer is preferred again -- Then: The first buffer is preferred again
-- because buf1 matches better which takes precedence -- because buf1 matches better which takes precedence
-- when both buffers have the same listing status. -- when both buffers have the same listing status.
eq(buf1.handle, buflist_findpat("test", ALLOW_UNLISTED)) eq(buf1.handle, buflist_findpat('test', ALLOW_UNLISTED))
-- And: Neither buffer is returned when ignoring unlisted -- And: Neither buffer is returned when ignoring unlisted
eq(-1, buflist_findpat("test", ONLY_LISTED)) eq(-1, buflist_findpat('test', ONLY_LISTED))
--} --}
close_buffer(NULL, buf1, buffer.DOBUF_WIPE, 0, 0) close_buffer(NULL, buf1, buffer.DOBUF_WIPE, 0, 0)

View File

@ -1,4 +1,4 @@
local helpers = require("test.unit.helpers")(after_each) local helpers = require('test.unit.helpers')(after_each)
local bit = require('bit') local bit = require('bit')
local itp = helpers.gen_itp(it) local itp = helpers.gen_itp(it)
@ -26,10 +26,10 @@ local ucnt = 4242
local function arginit(arg) local function arginit(arg)
if arg == 'unum' then if arg == 'unum' then
ucnt = ucnt + 1 ucnt = ucnt + 1
return ARGTYPES[arg]({ucnt}) return ARGTYPES[arg]({ ucnt })
else else
icnt = icnt - 1 icnt = icnt - 1
return ARGTYPES[arg]({icnt}) return ARGTYPES[arg]({ icnt })
end end
end end
@ -44,7 +44,9 @@ local function argreset(arg, args)
end end
local function test_vim_str2nr(s, what, exp, maxlen, strict) local function test_vim_str2nr(s, what, exp, maxlen, strict)
if strict == nil then strict = true end if strict == nil then
strict = true
end
local bits = {} local bits = {}
for k, _ in pairs(exp) do for k, _ in pairs(exp) do
bits[#bits + 1] = k bits[#bits + 1] = k
@ -54,7 +56,7 @@ local function test_vim_str2nr(s, what, exp, maxlen, strict)
for k, _ in pairs(ARGTYPES) do for k, _ in pairs(ARGTYPES) do
args[k] = arginit(k) args[k] = arginit(k)
end end
for case = 0, ((2 ^ (#bits)) - 1) do for case = 0, ((2 ^ #bits) - 1) do
local cv = {} local cv = {}
for b = 0, (#bits - 1) do for b = 0, (#bits - 1) do
if bit.band(case, (2 ^ b)) == 0 then if bit.band(case, (2 ^ b)) == 0 then
@ -66,9 +68,17 @@ local function test_vim_str2nr(s, what, exp, maxlen, strict)
lib.vim_str2nr(s, cv.pre, cv.len, what, cv.num, cv.unum, maxlen, strict, nil) lib.vim_str2nr(s, cv.pre, cv.len, what, cv.num, cv.unum, maxlen, strict, nil)
for cck, ccv in pairs(cv) do for cck, ccv in pairs(cv) do
if exp[cck] ~= tonumber(ccv[0]) then if exp[cck] ~= tonumber(ccv[0]) then
error(('Failed check (%s = %d) in test (s=%s, w=%u, m=%d, strict=%s): %d'):format( error(
cck, exp[cck], s, tonumber(what), maxlen, tostring(strict), tonumber(ccv[0]) ('Failed check (%s = %d) in test (s=%s, w=%u, m=%d, strict=%s): %d'):format(
)) cck,
exp[cck],
s,
tonumber(what),
maxlen,
tostring(strict),
tonumber(ccv[0])
)
)
end end
end end
end end
@ -82,18 +92,48 @@ end
describe('vim_str2nr()', function() describe('vim_str2nr()', function()
itp('works fine when it has nothing to do', function() itp('works fine when it has nothing to do', function()
test_vim_str2nr('', 0, {len = 0, num = 0, unum = 0, pre = 0}, 0) test_vim_str2nr('', 0, { len = 0, num = 0, unum = 0, pre = 0 }, 0)
test_vim_str2nr('', lib.STR2NR_ALL, {len = 0, num = 0, unum = 0, pre = 0}, 0) test_vim_str2nr('', lib.STR2NR_ALL, { len = 0, num = 0, unum = 0, pre = 0 }, 0)
test_vim_str2nr('', lib.STR2NR_BIN, {len = 0, num = 0, unum = 0, pre = 0}, 0) test_vim_str2nr('', lib.STR2NR_BIN, { len = 0, num = 0, unum = 0, pre = 0 }, 0)
test_vim_str2nr('', lib.STR2NR_OCT, {len = 0, num = 0, unum = 0, pre = 0}, 0) test_vim_str2nr('', lib.STR2NR_OCT, { len = 0, num = 0, unum = 0, pre = 0 }, 0)
test_vim_str2nr('', lib.STR2NR_OOCT, {len = 0, num = 0, unum = 0, pre = 0}, 0) test_vim_str2nr('', lib.STR2NR_OOCT, { len = 0, num = 0, unum = 0, pre = 0 }, 0)
test_vim_str2nr('', lib.STR2NR_HEX, {len = 0, num = 0, unum = 0, pre = 0}, 0) test_vim_str2nr('', lib.STR2NR_HEX, { len = 0, num = 0, unum = 0, pre = 0 }, 0)
test_vim_str2nr('', lib.STR2NR_FORCE + lib.STR2NR_DEC, {len = 0, num = 0, unum = 0, pre = 0}, 0) test_vim_str2nr(
test_vim_str2nr('', lib.STR2NR_FORCE + lib.STR2NR_BIN, {len = 0, num = 0, unum = 0, pre = 0}, 0) '',
test_vim_str2nr('', lib.STR2NR_FORCE + lib.STR2NR_OCT, {len = 0, num = 0, unum = 0, pre = 0}, 0) lib.STR2NR_FORCE + lib.STR2NR_DEC,
test_vim_str2nr('', lib.STR2NR_FORCE + lib.STR2NR_OOCT, {len = 0, num = 0, unum = 0, pre = 0}, 0) { len = 0, num = 0, unum = 0, pre = 0 },
test_vim_str2nr('', lib.STR2NR_FORCE + lib.STR2NR_OCT + lib.STR2NR_OOCT, {len = 0, num = 0, unum = 0, pre = 0}, 0) 0
test_vim_str2nr('', lib.STR2NR_FORCE + lib.STR2NR_HEX, {len = 0, num = 0, unum = 0, pre = 0}, 0) )
test_vim_str2nr(
'',
lib.STR2NR_FORCE + lib.STR2NR_BIN,
{ len = 0, num = 0, unum = 0, pre = 0 },
0
)
test_vim_str2nr(
'',
lib.STR2NR_FORCE + lib.STR2NR_OCT,
{ len = 0, num = 0, unum = 0, pre = 0 },
0
)
test_vim_str2nr(
'',
lib.STR2NR_FORCE + lib.STR2NR_OOCT,
{ len = 0, num = 0, unum = 0, pre = 0 },
0
)
test_vim_str2nr(
'',
lib.STR2NR_FORCE + lib.STR2NR_OCT + lib.STR2NR_OOCT,
{ len = 0, num = 0, unum = 0, pre = 0 },
0
)
test_vim_str2nr(
'',
lib.STR2NR_FORCE + lib.STR2NR_HEX,
{ len = 0, num = 0, unum = 0, pre = 0 },
0
)
end) end)
itp('works with decimal numbers', function() itp('works with decimal numbers', function()
for _, flags in ipairs({ for _, flags in ipairs({
@ -110,30 +150,30 @@ describe('vim_str2nr()', function()
lib.STR2NR_FORCE + lib.STR2NR_DEC, lib.STR2NR_FORCE + lib.STR2NR_DEC,
}) do }) do
-- Check that all digits are recognized -- Check that all digits are recognized
test_vim_str2nr( '12345', flags, {len = 5, num = 12345, unum = 12345, pre = 0}, 0) test_vim_str2nr('12345', flags, { len = 5, num = 12345, unum = 12345, pre = 0 }, 0)
test_vim_str2nr( '67890', flags, {len = 5, num = 67890, unum = 67890, pre = 0}, 0) test_vim_str2nr('67890', flags, { len = 5, num = 67890, unum = 67890, pre = 0 }, 0)
test_vim_str2nr( '12345A', flags, {len = 0}, 0) test_vim_str2nr('12345A', flags, { len = 0 }, 0)
test_vim_str2nr( '67890A', flags, {len = 0}, 0) test_vim_str2nr('67890A', flags, { len = 0 }, 0)
test_vim_str2nr( '12345A', flags, {len = 5, num = 12345, unum = 12345, pre = 0}, 0, false) test_vim_str2nr('12345A', flags, { len = 5, num = 12345, unum = 12345, pre = 0 }, 0, false)
test_vim_str2nr( '67890A', flags, {len = 5, num = 67890, unum = 67890, pre = 0}, 0, false) test_vim_str2nr('67890A', flags, { len = 5, num = 67890, unum = 67890, pre = 0 }, 0, false)
test_vim_str2nr( '42', flags, {len = 2, num = 42, unum = 42, pre = 0}, 0) test_vim_str2nr('42', flags, { len = 2, num = 42, unum = 42, pre = 0 }, 0)
test_vim_str2nr( '42', flags, {len = 1, num = 4, unum = 4, pre = 0}, 1) test_vim_str2nr('42', flags, { len = 1, num = 4, unum = 4, pre = 0 }, 1)
test_vim_str2nr( '42', flags, {len = 2, num = 42, unum = 42, pre = 0}, 2) test_vim_str2nr('42', flags, { len = 2, num = 42, unum = 42, pre = 0 }, 2)
test_vim_str2nr( '42', flags, {len = 2, num = 42, unum = 42, pre = 0}, 3) -- includes NUL byte in maxlen test_vim_str2nr('42', flags, { len = 2, num = 42, unum = 42, pre = 0 }, 3) -- includes NUL byte in maxlen
test_vim_str2nr( '42x', flags, {len = 0}, 0) test_vim_str2nr('42x', flags, { len = 0 }, 0)
test_vim_str2nr( '42x', flags, {len = 0}, 3) test_vim_str2nr('42x', flags, { len = 0 }, 3)
test_vim_str2nr( '42x', flags, {len = 2, num = 42, unum = 42, pre = 0}, 0, false) test_vim_str2nr('42x', flags, { len = 2, num = 42, unum = 42, pre = 0 }, 0, false)
test_vim_str2nr( '42x', flags, {len = 2, num = 42, unum = 42, pre = 0}, 3, false) test_vim_str2nr('42x', flags, { len = 2, num = 42, unum = 42, pre = 0 }, 3, false)
test_vim_str2nr('-42', flags, {len = 3, num = -42, unum = 42, pre = 0}, 3) test_vim_str2nr('-42', flags, { len = 3, num = -42, unum = 42, pre = 0 }, 3)
test_vim_str2nr('-42', flags, {len = 1, num = 0, unum = 0, pre = 0}, 1) test_vim_str2nr('-42', flags, { len = 1, num = 0, unum = 0, pre = 0 }, 1)
test_vim_str2nr('-42x', flags, {len = 0}, 0) test_vim_str2nr('-42x', flags, { len = 0 }, 0)
test_vim_str2nr('-42x', flags, {len = 0}, 4) test_vim_str2nr('-42x', flags, { len = 0 }, 4)
test_vim_str2nr('-42x', flags, {len = 3, num = -42, unum = 42, pre = 0}, 0, false) test_vim_str2nr('-42x', flags, { len = 3, num = -42, unum = 42, pre = 0 }, 0, false)
test_vim_str2nr('-42x', flags, {len = 3, num = -42, unum = 42, pre = 0}, 4, false) test_vim_str2nr('-42x', flags, { len = 3, num = -42, unum = 42, pre = 0 }, 4, false)
end end
end) end)
itp('works with binary numbers', function() itp('works with binary numbers', function()
@ -154,66 +194,66 @@ describe('vim_str2nr()', function()
BIN = ('B'):byte() BIN = ('B'):byte()
end end
test_vim_str2nr( '0b101', flags, {len = 5, num = 5, unum = 5, pre = bin}, 0) test_vim_str2nr('0b101', flags, { len = 5, num = 5, unum = 5, pre = bin }, 0)
test_vim_str2nr( '0b101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) test_vim_str2nr('0b101', flags, { len = 1, num = 0, unum = 0, pre = 0 }, 1)
test_vim_str2nr( '0b101', flags, {len = 0}, 2) test_vim_str2nr('0b101', flags, { len = 0 }, 2)
test_vim_str2nr( '0b101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 2, false) test_vim_str2nr('0b101', flags, { len = 1, num = 0, unum = 0, pre = 0 }, 2, false)
test_vim_str2nr( '0b101', flags, {len = 3, num = 1, unum = 1, pre = bin}, 3) test_vim_str2nr('0b101', flags, { len = 3, num = 1, unum = 1, pre = bin }, 3)
test_vim_str2nr( '0b101', flags, {len = 4, num = 2, unum = 2, pre = bin}, 4) test_vim_str2nr('0b101', flags, { len = 4, num = 2, unum = 2, pre = bin }, 4)
test_vim_str2nr( '0b101', flags, {len = 5, num = 5, unum = 5, pre = bin}, 5) test_vim_str2nr('0b101', flags, { len = 5, num = 5, unum = 5, pre = bin }, 5)
test_vim_str2nr( '0b101', flags, {len = 5, num = 5, unum = 5, pre = bin}, 6) test_vim_str2nr('0b101', flags, { len = 5, num = 5, unum = 5, pre = bin }, 6)
test_vim_str2nr( '0b1012', flags, {len = 0}, 0) test_vim_str2nr('0b1012', flags, { len = 0 }, 0)
test_vim_str2nr( '0b1012', flags, {len = 0}, 6) test_vim_str2nr('0b1012', flags, { len = 0 }, 6)
test_vim_str2nr( '0b1012', flags, {len = 5, num = 5, unum = 5, pre = bin}, 0, false) test_vim_str2nr('0b1012', flags, { len = 5, num = 5, unum = 5, pre = bin }, 0, false)
test_vim_str2nr( '0b1012', flags, {len = 5, num = 5, unum = 5, pre = bin}, 6, false) test_vim_str2nr('0b1012', flags, { len = 5, num = 5, unum = 5, pre = bin }, 6, false)
test_vim_str2nr('-0b101', flags, {len = 6, num = -5, unum = 5, pre = bin}, 0) test_vim_str2nr('-0b101', flags, { len = 6, num = -5, unum = 5, pre = bin }, 0)
test_vim_str2nr('-0b101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) test_vim_str2nr('-0b101', flags, { len = 1, num = 0, unum = 0, pre = 0 }, 1)
test_vim_str2nr('-0b101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 2) test_vim_str2nr('-0b101', flags, { len = 2, num = 0, unum = 0, pre = 0 }, 2)
test_vim_str2nr('-0b101', flags, {len = 0}, 3) test_vim_str2nr('-0b101', flags, { len = 0 }, 3)
test_vim_str2nr('-0b101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 3, false) test_vim_str2nr('-0b101', flags, { len = 2, num = 0, unum = 0, pre = 0 }, 3, false)
test_vim_str2nr('-0b101', flags, {len = 4, num = -1, unum = 1, pre = bin}, 4) test_vim_str2nr('-0b101', flags, { len = 4, num = -1, unum = 1, pre = bin }, 4)
test_vim_str2nr('-0b101', flags, {len = 5, num = -2, unum = 2, pre = bin}, 5) test_vim_str2nr('-0b101', flags, { len = 5, num = -2, unum = 2, pre = bin }, 5)
test_vim_str2nr('-0b101', flags, {len = 6, num = -5, unum = 5, pre = bin}, 6) test_vim_str2nr('-0b101', flags, { len = 6, num = -5, unum = 5, pre = bin }, 6)
test_vim_str2nr('-0b101', flags, {len = 6, num = -5, unum = 5, pre = bin}, 7) test_vim_str2nr('-0b101', flags, { len = 6, num = -5, unum = 5, pre = bin }, 7)
test_vim_str2nr('-0b1012', flags, {len = 0}, 0) test_vim_str2nr('-0b1012', flags, { len = 0 }, 0)
test_vim_str2nr('-0b1012', flags, {len = 0}, 7) test_vim_str2nr('-0b1012', flags, { len = 0 }, 7)
test_vim_str2nr('-0b1012', flags, {len = 6, num = -5, unum = 5, pre = bin}, 0, false) test_vim_str2nr('-0b1012', flags, { len = 6, num = -5, unum = 5, pre = bin }, 0, false)
test_vim_str2nr('-0b1012', flags, {len = 6, num = -5, unum = 5, pre = bin}, 7, false) test_vim_str2nr('-0b1012', flags, { len = 6, num = -5, unum = 5, pre = bin }, 7, false)
test_vim_str2nr( '0B101', flags, {len = 5, num = 5, unum = 5, pre = BIN}, 0) test_vim_str2nr('0B101', flags, { len = 5, num = 5, unum = 5, pre = BIN }, 0)
test_vim_str2nr( '0B101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) test_vim_str2nr('0B101', flags, { len = 1, num = 0, unum = 0, pre = 0 }, 1)
test_vim_str2nr( '0B101', flags, {len = 0}, 2) test_vim_str2nr('0B101', flags, { len = 0 }, 2)
test_vim_str2nr( '0B101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 2, false) test_vim_str2nr('0B101', flags, { len = 1, num = 0, unum = 0, pre = 0 }, 2, false)
test_vim_str2nr( '0B101', flags, {len = 3, num = 1, unum = 1, pre = BIN}, 3) test_vim_str2nr('0B101', flags, { len = 3, num = 1, unum = 1, pre = BIN }, 3)
test_vim_str2nr( '0B101', flags, {len = 4, num = 2, unum = 2, pre = BIN}, 4) test_vim_str2nr('0B101', flags, { len = 4, num = 2, unum = 2, pre = BIN }, 4)
test_vim_str2nr( '0B101', flags, {len = 5, num = 5, unum = 5, pre = BIN}, 5) test_vim_str2nr('0B101', flags, { len = 5, num = 5, unum = 5, pre = BIN }, 5)
test_vim_str2nr( '0B101', flags, {len = 5, num = 5, unum = 5, pre = BIN}, 6) test_vim_str2nr('0B101', flags, { len = 5, num = 5, unum = 5, pre = BIN }, 6)
test_vim_str2nr( '0B1012', flags, {len = 0}, 0) test_vim_str2nr('0B1012', flags, { len = 0 }, 0)
test_vim_str2nr( '0B1012', flags, {len = 0}, 6) test_vim_str2nr('0B1012', flags, { len = 0 }, 6)
test_vim_str2nr( '0B1012', flags, {len = 5, num = 5, unum = 5, pre = BIN}, 0, false) test_vim_str2nr('0B1012', flags, { len = 5, num = 5, unum = 5, pre = BIN }, 0, false)
test_vim_str2nr( '0B1012', flags, {len = 5, num = 5, unum = 5, pre = BIN}, 6, false) test_vim_str2nr('0B1012', flags, { len = 5, num = 5, unum = 5, pre = BIN }, 6, false)
test_vim_str2nr('-0B101', flags, {len = 6, num = -5, unum = 5, pre = BIN}, 0) test_vim_str2nr('-0B101', flags, { len = 6, num = -5, unum = 5, pre = BIN }, 0)
test_vim_str2nr('-0B101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) test_vim_str2nr('-0B101', flags, { len = 1, num = 0, unum = 0, pre = 0 }, 1)
test_vim_str2nr('-0B101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 2) test_vim_str2nr('-0B101', flags, { len = 2, num = 0, unum = 0, pre = 0 }, 2)
test_vim_str2nr('-0B101', flags, {len = 0}, 3) test_vim_str2nr('-0B101', flags, { len = 0 }, 3)
test_vim_str2nr('-0B101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 3, false) test_vim_str2nr('-0B101', flags, { len = 2, num = 0, unum = 0, pre = 0 }, 3, false)
test_vim_str2nr('-0B101', flags, {len = 4, num = -1, unum = 1, pre = BIN}, 4) test_vim_str2nr('-0B101', flags, { len = 4, num = -1, unum = 1, pre = BIN }, 4)
test_vim_str2nr('-0B101', flags, {len = 5, num = -2, unum = 2, pre = BIN}, 5) test_vim_str2nr('-0B101', flags, { len = 5, num = -2, unum = 2, pre = BIN }, 5)
test_vim_str2nr('-0B101', flags, {len = 6, num = -5, unum = 5, pre = BIN}, 6) test_vim_str2nr('-0B101', flags, { len = 6, num = -5, unum = 5, pre = BIN }, 6)
test_vim_str2nr('-0B101', flags, {len = 6, num = -5, unum = 5, pre = BIN}, 7) test_vim_str2nr('-0B101', flags, { len = 6, num = -5, unum = 5, pre = BIN }, 7)
test_vim_str2nr('-0B1012', flags, {len = 0}, 0) test_vim_str2nr('-0B1012', flags, { len = 0 }, 0)
test_vim_str2nr('-0B1012', flags, {len = 0}, 7) test_vim_str2nr('-0B1012', flags, { len = 0 }, 7)
test_vim_str2nr('-0B1012', flags, {len = 6, num = -5, unum = 5, pre = BIN}, 0, false) test_vim_str2nr('-0B1012', flags, { len = 6, num = -5, unum = 5, pre = BIN }, 0, false)
test_vim_str2nr('-0B1012', flags, {len = 6, num = -5, unum = 5, pre = BIN}, 7, false) test_vim_str2nr('-0B1012', flags, { len = 6, num = -5, unum = 5, pre = BIN }, 7, false)
if flags > lib.STR2NR_FORCE then if flags > lib.STR2NR_FORCE then
test_vim_str2nr('-101', flags, {len = 4, num = -5, unum = 5, pre = 0}, 0) test_vim_str2nr('-101', flags, { len = 4, num = -5, unum = 5, pre = 0 }, 0)
end end
end end
end) end)
@ -236,42 +276,42 @@ describe('vim_str2nr()', function()
end end
-- Check that all digits are recognized -- Check that all digits are recognized
test_vim_str2nr( '012345670', flags, {len = 9, num = 2739128, unum = 2739128, pre = oct}, 0) test_vim_str2nr('012345670', flags, { len = 9, num = 2739128, unum = 2739128, pre = oct }, 0)
test_vim_str2nr( '054', flags, {len = 3, num = 44, unum = 44, pre = oct}, 0) test_vim_str2nr('054', flags, { len = 3, num = 44, unum = 44, pre = oct }, 0)
test_vim_str2nr( '054', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) test_vim_str2nr('054', flags, { len = 1, num = 0, unum = 0, pre = 0 }, 1)
test_vim_str2nr( '054', flags, {len = 2, num = 5, unum = 5, pre = oct}, 2) test_vim_str2nr('054', flags, { len = 2, num = 5, unum = 5, pre = oct }, 2)
test_vim_str2nr( '054', flags, {len = 3, num = 44, unum = 44, pre = oct}, 3) test_vim_str2nr('054', flags, { len = 3, num = 44, unum = 44, pre = oct }, 3)
test_vim_str2nr( '0548', flags, {len = 3, num = 44, unum = 44, pre = oct}, 3) test_vim_str2nr('0548', flags, { len = 3, num = 44, unum = 44, pre = oct }, 3)
test_vim_str2nr( '054', flags, {len = 3, num = 44, unum = 44, pre = oct}, 4) test_vim_str2nr('054', flags, { len = 3, num = 44, unum = 44, pre = oct }, 4)
test_vim_str2nr( '054x', flags, {len = 0}, 4) test_vim_str2nr('054x', flags, { len = 0 }, 4)
test_vim_str2nr( '054x', flags, {len = 0}, 0) test_vim_str2nr('054x', flags, { len = 0 }, 0)
test_vim_str2nr( '054x', flags, {len = 3, num = 44, unum = 44, pre = oct}, 4, false) test_vim_str2nr('054x', flags, { len = 3, num = 44, unum = 44, pre = oct }, 4, false)
test_vim_str2nr( '054x', flags, {len = 3, num = 44, unum = 44, pre = oct}, 0, false) test_vim_str2nr('054x', flags, { len = 3, num = 44, unum = 44, pre = oct }, 0, false)
test_vim_str2nr('-054', flags, {len = 4, num = -44, unum = 44, pre = oct}, 0) test_vim_str2nr('-054', flags, { len = 4, num = -44, unum = 44, pre = oct }, 0)
test_vim_str2nr('-054', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) test_vim_str2nr('-054', flags, { len = 1, num = 0, unum = 0, pre = 0 }, 1)
test_vim_str2nr('-054', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 2) test_vim_str2nr('-054', flags, { len = 2, num = 0, unum = 0, pre = 0 }, 2)
test_vim_str2nr('-054', flags, {len = 3, num = -5, unum = 5, pre = oct}, 3) test_vim_str2nr('-054', flags, { len = 3, num = -5, unum = 5, pre = oct }, 3)
test_vim_str2nr('-054', flags, {len = 4, num = -44, unum = 44, pre = oct}, 4) test_vim_str2nr('-054', flags, { len = 4, num = -44, unum = 44, pre = oct }, 4)
test_vim_str2nr('-0548', flags, {len = 4, num = -44, unum = 44, pre = oct}, 4) test_vim_str2nr('-0548', flags, { len = 4, num = -44, unum = 44, pre = oct }, 4)
test_vim_str2nr('-054', flags, {len = 4, num = -44, unum = 44, pre = oct}, 5) test_vim_str2nr('-054', flags, { len = 4, num = -44, unum = 44, pre = oct }, 5)
test_vim_str2nr('-054x', flags, {len = 0}, 5) test_vim_str2nr('-054x', flags, { len = 0 }, 5)
test_vim_str2nr('-054x', flags, {len = 0}, 0) test_vim_str2nr('-054x', flags, { len = 0 }, 0)
test_vim_str2nr('-054x', flags, {len = 4, num = -44, unum = 44, pre = oct}, 5, false) test_vim_str2nr('-054x', flags, { len = 4, num = -44, unum = 44, pre = oct }, 5, false)
test_vim_str2nr('-054x', flags, {len = 4, num = -44, unum = 44, pre = oct}, 0, false) test_vim_str2nr('-054x', flags, { len = 4, num = -44, unum = 44, pre = oct }, 0, false)
if flags > lib.STR2NR_FORCE then if flags > lib.STR2NR_FORCE then
test_vim_str2nr('-54', flags, {len = 3, num = -44, unum = 44, pre = 0}, 0) test_vim_str2nr('-54', flags, { len = 3, num = -44, unum = 44, pre = 0 }, 0)
test_vim_str2nr('-0548', flags, {len = 0}, 5) test_vim_str2nr('-0548', flags, { len = 0 }, 5)
test_vim_str2nr('-0548', flags, {len = 0}, 0) test_vim_str2nr('-0548', flags, { len = 0 }, 0)
test_vim_str2nr('-0548', flags, {len = 4, num = -44, unum = 44, pre = 0}, 5, false) test_vim_str2nr('-0548', flags, { len = 4, num = -44, unum = 44, pre = 0 }, 5, false)
test_vim_str2nr('-0548', flags, {len = 4, num = -44, unum = 44, pre = 0}, 0, false) test_vim_str2nr('-0548', flags, { len = 4, num = -44, unum = 44, pre = 0 }, 0, false)
else else
test_vim_str2nr('-0548', flags, {len = 5, num = -548, unum = 548, pre = 0}, 5) test_vim_str2nr('-0548', flags, { len = 5, num = -548, unum = 548, pre = 0 }, 5)
test_vim_str2nr('-0548', flags, {len = 5, num = -548, unum = 548, pre = 0}, 0) test_vim_str2nr('-0548', flags, { len = 5, num = -548, unum = 548, pre = 0 }, 0)
end end
end end
end) end)
@ -298,73 +338,73 @@ describe('vim_str2nr()', function()
OCT = ('O'):byte() OCT = ('O'):byte()
end end
test_vim_str2nr( '0o054', flags, {len = 5, num = 44, unum = 44, pre = oct}, 0) test_vim_str2nr('0o054', flags, { len = 5, num = 44, unum = 44, pre = oct }, 0)
test_vim_str2nr( '0o054', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) test_vim_str2nr('0o054', flags, { len = 1, num = 0, unum = 0, pre = 0 }, 1)
test_vim_str2nr( '0o054', flags, {len = 0}, 2) test_vim_str2nr('0o054', flags, { len = 0 }, 2)
test_vim_str2nr( '0o054', flags, {len = 3, num = 0, unum = 0, pre = oct}, 3) test_vim_str2nr('0o054', flags, { len = 3, num = 0, unum = 0, pre = oct }, 3)
test_vim_str2nr( '0o054', flags, {len = 4, num = 5, unum = 5, pre = oct}, 4) test_vim_str2nr('0o054', flags, { len = 4, num = 5, unum = 5, pre = oct }, 4)
test_vim_str2nr( '0o054', flags, {len = 5, num = 44, unum = 44, pre = oct}, 5) test_vim_str2nr('0o054', flags, { len = 5, num = 44, unum = 44, pre = oct }, 5)
test_vim_str2nr( '0o0548', flags, {len = 5, num = 44, unum = 44, pre = oct}, 5) test_vim_str2nr('0o0548', flags, { len = 5, num = 44, unum = 44, pre = oct }, 5)
test_vim_str2nr( '0o054', flags, {len = 5, num = 44, unum = 44, pre = oct}, 6) test_vim_str2nr('0o054', flags, { len = 5, num = 44, unum = 44, pre = oct }, 6)
test_vim_str2nr( '0o054x', flags, {len = 0}, 6) test_vim_str2nr('0o054x', flags, { len = 0 }, 6)
test_vim_str2nr( '0o054x', flags, {len = 0}, 0) test_vim_str2nr('0o054x', flags, { len = 0 }, 0)
test_vim_str2nr( '0o054x', flags, {len = 5, num = 44, unum = 44, pre = oct}, 6, false) test_vim_str2nr('0o054x', flags, { len = 5, num = 44, unum = 44, pre = oct }, 6, false)
test_vim_str2nr( '0o054x', flags, {len = 5, num = 44, unum = 44, pre = oct}, 0, false) test_vim_str2nr('0o054x', flags, { len = 5, num = 44, unum = 44, pre = oct }, 0, false)
test_vim_str2nr('-0o054', flags, {len = 6, num = -44, unum = 44, pre = oct}, 0) test_vim_str2nr('-0o054', flags, { len = 6, num = -44, unum = 44, pre = oct }, 0)
test_vim_str2nr('-0o054', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) test_vim_str2nr('-0o054', flags, { len = 1, num = 0, unum = 0, pre = 0 }, 1)
test_vim_str2nr('-0o054', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 2) test_vim_str2nr('-0o054', flags, { len = 2, num = 0, unum = 0, pre = 0 }, 2)
test_vim_str2nr('-0o054', flags, {len = 0}, 3) test_vim_str2nr('-0o054', flags, { len = 0 }, 3)
test_vim_str2nr('-0o054', flags, {len = 4, num = 0, unum = 0, pre = oct}, 4) test_vim_str2nr('-0o054', flags, { len = 4, num = 0, unum = 0, pre = oct }, 4)
test_vim_str2nr('-0o054', flags, {len = 5, num = -5, unum = 5, pre = oct}, 5) test_vim_str2nr('-0o054', flags, { len = 5, num = -5, unum = 5, pre = oct }, 5)
test_vim_str2nr('-0o054', flags, {len = 6, num = -44, unum = 44, pre = oct}, 6) test_vim_str2nr('-0o054', flags, { len = 6, num = -44, unum = 44, pre = oct }, 6)
test_vim_str2nr('-0o0548', flags, {len = 6, num = -44, unum = 44, pre = oct}, 6) test_vim_str2nr('-0o0548', flags, { len = 6, num = -44, unum = 44, pre = oct }, 6)
test_vim_str2nr('-0o054', flags, {len = 6, num = -44, unum = 44, pre = oct}, 7) test_vim_str2nr('-0o054', flags, { len = 6, num = -44, unum = 44, pre = oct }, 7)
test_vim_str2nr('-0o054x', flags, {len = 0}, 7) test_vim_str2nr('-0o054x', flags, { len = 0 }, 7)
test_vim_str2nr('-0o054x', flags, {len = 0}, 0) test_vim_str2nr('-0o054x', flags, { len = 0 }, 0)
test_vim_str2nr('-0o054x', flags, {len = 6, num = -44, unum = 44, pre = oct}, 7, false) test_vim_str2nr('-0o054x', flags, { len = 6, num = -44, unum = 44, pre = oct }, 7, false)
test_vim_str2nr('-0o054x', flags, {len = 6, num = -44, unum = 44, pre = oct}, 0, false) test_vim_str2nr('-0o054x', flags, { len = 6, num = -44, unum = 44, pre = oct }, 0, false)
test_vim_str2nr( '0O054', flags, {len = 5, num = 44, unum = 44, pre = OCT}, 0) test_vim_str2nr('0O054', flags, { len = 5, num = 44, unum = 44, pre = OCT }, 0)
test_vim_str2nr( '0O054', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) test_vim_str2nr('0O054', flags, { len = 1, num = 0, unum = 0, pre = 0 }, 1)
test_vim_str2nr( '0O054', flags, {len = 0}, 2) test_vim_str2nr('0O054', flags, { len = 0 }, 2)
test_vim_str2nr( '0O054', flags, {len = 3, num = 0, unum = 0, pre = OCT}, 3) test_vim_str2nr('0O054', flags, { len = 3, num = 0, unum = 0, pre = OCT }, 3)
test_vim_str2nr( '0O054', flags, {len = 4, num = 5, unum = 5, pre = OCT}, 4) test_vim_str2nr('0O054', flags, { len = 4, num = 5, unum = 5, pre = OCT }, 4)
test_vim_str2nr( '0O054', flags, {len = 5, num = 44, unum = 44, pre = OCT}, 5) test_vim_str2nr('0O054', flags, { len = 5, num = 44, unum = 44, pre = OCT }, 5)
test_vim_str2nr( '0O0548', flags, {len = 5, num = 44, unum = 44, pre = OCT}, 5) test_vim_str2nr('0O0548', flags, { len = 5, num = 44, unum = 44, pre = OCT }, 5)
test_vim_str2nr( '0O054', flags, {len = 5, num = 44, unum = 44, pre = OCT}, 6) test_vim_str2nr('0O054', flags, { len = 5, num = 44, unum = 44, pre = OCT }, 6)
test_vim_str2nr( '0O054x', flags, {len = 0}, 6) test_vim_str2nr('0O054x', flags, { len = 0 }, 6)
test_vim_str2nr( '0O054x', flags, {len = 0}, 0) test_vim_str2nr('0O054x', flags, { len = 0 }, 0)
test_vim_str2nr( '0O054x', flags, {len = 5, num = 44, unum = 44, pre = OCT}, 6, false) test_vim_str2nr('0O054x', flags, { len = 5, num = 44, unum = 44, pre = OCT }, 6, false)
test_vim_str2nr( '0O054x', flags, {len = 5, num = 44, unum = 44, pre = OCT}, 0, false) test_vim_str2nr('0O054x', flags, { len = 5, num = 44, unum = 44, pre = OCT }, 0, false)
test_vim_str2nr('-0O054', flags, {len = 6, num = -44, unum = 44, pre = OCT}, 0) test_vim_str2nr('-0O054', flags, { len = 6, num = -44, unum = 44, pre = OCT }, 0)
test_vim_str2nr('-0O054', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) test_vim_str2nr('-0O054', flags, { len = 1, num = 0, unum = 0, pre = 0 }, 1)
test_vim_str2nr('-0O054', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 2) test_vim_str2nr('-0O054', flags, { len = 2, num = 0, unum = 0, pre = 0 }, 2)
test_vim_str2nr('-0O054', flags, {len = 0}, 3) test_vim_str2nr('-0O054', flags, { len = 0 }, 3)
test_vim_str2nr('-0O054', flags, {len = 4, num = 0, unum = 0, pre = OCT}, 4) test_vim_str2nr('-0O054', flags, { len = 4, num = 0, unum = 0, pre = OCT }, 4)
test_vim_str2nr('-0O054', flags, {len = 5, num = -5, unum = 5, pre = OCT}, 5) test_vim_str2nr('-0O054', flags, { len = 5, num = -5, unum = 5, pre = OCT }, 5)
test_vim_str2nr('-0O054', flags, {len = 6, num = -44, unum = 44, pre = OCT}, 6) test_vim_str2nr('-0O054', flags, { len = 6, num = -44, unum = 44, pre = OCT }, 6)
test_vim_str2nr('-0O0548', flags, {len = 6, num = -44, unum = 44, pre = OCT}, 6) test_vim_str2nr('-0O0548', flags, { len = 6, num = -44, unum = 44, pre = OCT }, 6)
test_vim_str2nr('-0O054', flags, {len = 6, num = -44, unum = 44, pre = OCT}, 7) test_vim_str2nr('-0O054', flags, { len = 6, num = -44, unum = 44, pre = OCT }, 7)
test_vim_str2nr('-0O054x', flags, {len = 0}, 7) test_vim_str2nr('-0O054x', flags, { len = 0 }, 7)
test_vim_str2nr('-0O054x', flags, {len = 0}, 0) test_vim_str2nr('-0O054x', flags, { len = 0 }, 0)
test_vim_str2nr('-0O054x', flags, {len = 6, num = -44, unum = 44, pre = OCT}, 7, false) test_vim_str2nr('-0O054x', flags, { len = 6, num = -44, unum = 44, pre = OCT }, 7, false)
test_vim_str2nr('-0O054x', flags, {len = 6, num = -44, unum = 44, pre = OCT}, 0, false) test_vim_str2nr('-0O054x', flags, { len = 6, num = -44, unum = 44, pre = OCT }, 0, false)
if flags > lib.STR2NR_FORCE then if flags > lib.STR2NR_FORCE then
test_vim_str2nr('-0548', flags, {len = 0}, 5) test_vim_str2nr('-0548', flags, { len = 0 }, 5)
test_vim_str2nr('-0548', flags, {len = 0}, 0) test_vim_str2nr('-0548', flags, { len = 0 }, 0)
test_vim_str2nr('-0548', flags, {len = 4, num = -44, unum = 44, pre = 0}, 5, false) test_vim_str2nr('-0548', flags, { len = 4, num = -44, unum = 44, pre = 0 }, 5, false)
test_vim_str2nr('-0548', flags, {len = 4, num = -44, unum = 44, pre = 0}, 0, false) test_vim_str2nr('-0548', flags, { len = 4, num = -44, unum = 44, pre = 0 }, 0, false)
test_vim_str2nr('-055', flags, {len = 4, num = -45, unum = 45, pre = 0}, 0) test_vim_str2nr('-055', flags, { len = 4, num = -45, unum = 45, pre = 0 }, 0)
else else
test_vim_str2nr('-0548', flags, {len = 5, num = -548, unum = 548, pre = 0}, 5) test_vim_str2nr('-0548', flags, { len = 5, num = -548, unum = 548, pre = 0 }, 5)
test_vim_str2nr('-0548', flags, {len = 5, num = -548, unum = 548, pre = 0}, 0) test_vim_str2nr('-0548', flags, { len = 5, num = -548, unum = 548, pre = 0 }, 0)
end end
end end
end) end)
@ -387,92 +427,92 @@ describe('vim_str2nr()', function()
end end
-- Check that all digits are recognized -- Check that all digits are recognized
test_vim_str2nr('0x12345', flags, {len = 7, num = 74565, unum = 74565, pre = hex}, 0) test_vim_str2nr('0x12345', flags, { len = 7, num = 74565, unum = 74565, pre = hex }, 0)
test_vim_str2nr('0x67890', flags, {len = 7, num = 424080, unum = 424080, pre = hex}, 0) test_vim_str2nr('0x67890', flags, { len = 7, num = 424080, unum = 424080, pre = hex }, 0)
test_vim_str2nr('0xABCDEF', flags, {len = 8, num = 11259375, unum = 11259375, pre = hex}, 0) test_vim_str2nr('0xABCDEF', flags, { len = 8, num = 11259375, unum = 11259375, pre = hex }, 0)
test_vim_str2nr('0xabcdef', flags, {len = 8, num = 11259375, unum = 11259375, pre = hex}, 0) test_vim_str2nr('0xabcdef', flags, { len = 8, num = 11259375, unum = 11259375, pre = hex }, 0)
test_vim_str2nr( '0x101', flags, {len = 5, num = 257, unum =257, pre = hex}, 0) test_vim_str2nr('0x101', flags, { len = 5, num = 257, unum = 257, pre = hex }, 0)
test_vim_str2nr( '0x101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) test_vim_str2nr('0x101', flags, { len = 1, num = 0, unum = 0, pre = 0 }, 1)
test_vim_str2nr( '0x101', flags, {len = 0}, 2) test_vim_str2nr('0x101', flags, { len = 0 }, 2)
test_vim_str2nr( '0x101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 2, false) test_vim_str2nr('0x101', flags, { len = 1, num = 0, unum = 0, pre = 0 }, 2, false)
test_vim_str2nr( '0x101', flags, {len = 3, num = 1, unum = 1, pre = hex}, 3) test_vim_str2nr('0x101', flags, { len = 3, num = 1, unum = 1, pre = hex }, 3)
test_vim_str2nr( '0x101', flags, {len = 4, num = 16, unum = 16, pre = hex}, 4) test_vim_str2nr('0x101', flags, { len = 4, num = 16, unum = 16, pre = hex }, 4)
test_vim_str2nr( '0x101', flags, {len = 5, num = 257, unum =257, pre = hex}, 5) test_vim_str2nr('0x101', flags, { len = 5, num = 257, unum = 257, pre = hex }, 5)
test_vim_str2nr( '0x101', flags, {len = 5, num = 257, unum =257, pre = hex}, 6) test_vim_str2nr('0x101', flags, { len = 5, num = 257, unum = 257, pre = hex }, 6)
test_vim_str2nr( '0x101G', flags, {len = 0}, 0) test_vim_str2nr('0x101G', flags, { len = 0 }, 0)
test_vim_str2nr( '0x101G', flags, {len = 0}, 6) test_vim_str2nr('0x101G', flags, { len = 0 }, 6)
test_vim_str2nr( '0x101G', flags, {len = 5, num = 257, unum =257, pre = hex}, 0, false) test_vim_str2nr('0x101G', flags, { len = 5, num = 257, unum = 257, pre = hex }, 0, false)
test_vim_str2nr( '0x101G', flags, {len = 5, num = 257, unum =257, pre = hex}, 6, false) test_vim_str2nr('0x101G', flags, { len = 5, num = 257, unum = 257, pre = hex }, 6, false)
test_vim_str2nr('-0x101', flags, {len = 6, num =-257, unum =257, pre = hex}, 0) test_vim_str2nr('-0x101', flags, { len = 6, num = -257, unum = 257, pre = hex }, 0)
test_vim_str2nr('-0x101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) test_vim_str2nr('-0x101', flags, { len = 1, num = 0, unum = 0, pre = 0 }, 1)
test_vim_str2nr('-0x101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 2) test_vim_str2nr('-0x101', flags, { len = 2, num = 0, unum = 0, pre = 0 }, 2)
test_vim_str2nr('-0x101', flags, {len = 0}, 3) test_vim_str2nr('-0x101', flags, { len = 0 }, 3)
test_vim_str2nr('-0x101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 3, false) test_vim_str2nr('-0x101', flags, { len = 2, num = 0, unum = 0, pre = 0 }, 3, false)
test_vim_str2nr('-0x101', flags, {len = 4, num = -1, unum = 1, pre = hex}, 4) test_vim_str2nr('-0x101', flags, { len = 4, num = -1, unum = 1, pre = hex }, 4)
test_vim_str2nr('-0x101', flags, {len = 5, num = -16, unum = 16, pre = hex}, 5) test_vim_str2nr('-0x101', flags, { len = 5, num = -16, unum = 16, pre = hex }, 5)
test_vim_str2nr('-0x101', flags, {len = 6, num =-257, unum =257, pre = hex}, 6) test_vim_str2nr('-0x101', flags, { len = 6, num = -257, unum = 257, pre = hex }, 6)
test_vim_str2nr('-0x101', flags, {len = 6, num =-257, unum =257, pre = hex}, 7) test_vim_str2nr('-0x101', flags, { len = 6, num = -257, unum = 257, pre = hex }, 7)
test_vim_str2nr('-0x101G', flags, {len = 0}, 0) test_vim_str2nr('-0x101G', flags, { len = 0 }, 0)
test_vim_str2nr('-0x101G', flags, {len = 0}, 7) test_vim_str2nr('-0x101G', flags, { len = 0 }, 7)
test_vim_str2nr('-0x101G', flags, {len = 6, num =-257, unum =257, pre = hex}, 0, false) test_vim_str2nr('-0x101G', flags, { len = 6, num = -257, unum = 257, pre = hex }, 0, false)
test_vim_str2nr('-0x101G', flags, {len = 6, num =-257, unum =257, pre = hex}, 7, false) test_vim_str2nr('-0x101G', flags, { len = 6, num = -257, unum = 257, pre = hex }, 7, false)
test_vim_str2nr( '0X101', flags, {len = 5, num = 257, unum =257, pre = HEX}, 0) test_vim_str2nr('0X101', flags, { len = 5, num = 257, unum = 257, pre = HEX }, 0)
test_vim_str2nr( '0X101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) test_vim_str2nr('0X101', flags, { len = 1, num = 0, unum = 0, pre = 0 }, 1)
test_vim_str2nr( '0X101', flags, {len = 0}, 2) test_vim_str2nr('0X101', flags, { len = 0 }, 2)
test_vim_str2nr( '0X101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 2, false) test_vim_str2nr('0X101', flags, { len = 1, num = 0, unum = 0, pre = 0 }, 2, false)
test_vim_str2nr( '0X101', flags, {len = 3, num = 1, unum = 1, pre = HEX}, 3) test_vim_str2nr('0X101', flags, { len = 3, num = 1, unum = 1, pre = HEX }, 3)
test_vim_str2nr( '0X101', flags, {len = 4, num = 16, unum = 16, pre = HEX}, 4) test_vim_str2nr('0X101', flags, { len = 4, num = 16, unum = 16, pre = HEX }, 4)
test_vim_str2nr( '0X101', flags, {len = 5, num = 257, unum =257, pre = HEX}, 5) test_vim_str2nr('0X101', flags, { len = 5, num = 257, unum = 257, pre = HEX }, 5)
test_vim_str2nr( '0X101', flags, {len = 5, num = 257, unum =257, pre = HEX}, 6) test_vim_str2nr('0X101', flags, { len = 5, num = 257, unum = 257, pre = HEX }, 6)
test_vim_str2nr( '0X101G', flags, {len = 0}, 0) test_vim_str2nr('0X101G', flags, { len = 0 }, 0)
test_vim_str2nr( '0X101G', flags, {len = 0}, 6) test_vim_str2nr('0X101G', flags, { len = 0 }, 6)
test_vim_str2nr( '0X101G', flags, {len = 5, num = 257, unum =257, pre = HEX}, 0, false) test_vim_str2nr('0X101G', flags, { len = 5, num = 257, unum = 257, pre = HEX }, 0, false)
test_vim_str2nr( '0X101G', flags, {len = 5, num = 257, unum =257, pre = HEX}, 6, false) test_vim_str2nr('0X101G', flags, { len = 5, num = 257, unum = 257, pre = HEX }, 6, false)
test_vim_str2nr('-0X101', flags, {len = 6, num =-257, unum =257, pre = HEX}, 0) test_vim_str2nr('-0X101', flags, { len = 6, num = -257, unum = 257, pre = HEX }, 0)
test_vim_str2nr('-0X101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) test_vim_str2nr('-0X101', flags, { len = 1, num = 0, unum = 0, pre = 0 }, 1)
test_vim_str2nr('-0X101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 2) test_vim_str2nr('-0X101', flags, { len = 2, num = 0, unum = 0, pre = 0 }, 2)
test_vim_str2nr('-0X101', flags, {len = 0}, 3) test_vim_str2nr('-0X101', flags, { len = 0 }, 3)
test_vim_str2nr('-0X101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 3, false) test_vim_str2nr('-0X101', flags, { len = 2, num = 0, unum = 0, pre = 0 }, 3, false)
test_vim_str2nr('-0X101', flags, {len = 4, num = -1, unum = 1, pre = HEX}, 4) test_vim_str2nr('-0X101', flags, { len = 4, num = -1, unum = 1, pre = HEX }, 4)
test_vim_str2nr('-0X101', flags, {len = 5, num = -16, unum = 16, pre = HEX}, 5) test_vim_str2nr('-0X101', flags, { len = 5, num = -16, unum = 16, pre = HEX }, 5)
test_vim_str2nr('-0X101', flags, {len = 6, num =-257, unum =257, pre = HEX}, 6) test_vim_str2nr('-0X101', flags, { len = 6, num = -257, unum = 257, pre = HEX }, 6)
test_vim_str2nr('-0X101', flags, {len = 6, num =-257, unum =257, pre = HEX}, 7) test_vim_str2nr('-0X101', flags, { len = 6, num = -257, unum = 257, pre = HEX }, 7)
test_vim_str2nr('-0X101G', flags, {len = 0}, 0) test_vim_str2nr('-0X101G', flags, { len = 0 }, 0)
test_vim_str2nr('-0X101G', flags, {len = 0}, 7) test_vim_str2nr('-0X101G', flags, { len = 0 }, 7)
test_vim_str2nr('-0X101G', flags, {len = 6, num =-257, unum =257, pre = HEX}, 0, false) test_vim_str2nr('-0X101G', flags, { len = 6, num = -257, unum = 257, pre = HEX }, 0, false)
test_vim_str2nr('-0X101G', flags, {len = 6, num =-257, unum =257, pre = HEX}, 7, false) test_vim_str2nr('-0X101G', flags, { len = 6, num = -257, unum = 257, pre = HEX }, 7, false)
if flags > lib.STR2NR_FORCE then if flags > lib.STR2NR_FORCE then
test_vim_str2nr('-101', flags, {len = 4, num = -257, unum = 257, pre = 0}, 0) test_vim_str2nr('-101', flags, { len = 4, num = -257, unum = 257, pre = 0 }, 0)
end end
end end
end) end)
-- Test_str2nr() in test_functions.vim already tests normal usage -- Test_str2nr() in test_functions.vim already tests normal usage
itp('works with weirdly quoted numbers', function() itp('works with weirdly quoted numbers', function()
local flags = lib.STR2NR_DEC + lib.STR2NR_QUOTE local flags = lib.STR2NR_DEC + lib.STR2NR_QUOTE
test_vim_str2nr("'027", flags, {len = 0}, 0) test_vim_str2nr("'027", flags, { len = 0 }, 0)
test_vim_str2nr("'027", flags, {len = 0}, 0, false) test_vim_str2nr("'027", flags, { len = 0 }, 0, false)
test_vim_str2nr("1'2'3'4", flags, {len = 7, num = 1234, unum = 1234, pre = 0}, 0) test_vim_str2nr("1'2'3'4", flags, { len = 7, num = 1234, unum = 1234, pre = 0 }, 0)
-- counter-intuitive, but like Vim, strict=true should partially accept -- counter-intuitive, but like Vim, strict=true should partially accept
-- these: (' and - are not alphanumeric) -- these: (' and - are not alphanumeric)
test_vim_str2nr("7''331", flags, {len = 1, num = 7, unum = 7, pre = 0}, 0) test_vim_str2nr("7''331", flags, { len = 1, num = 7, unum = 7, pre = 0 }, 0)
test_vim_str2nr("123'x4", flags, {len = 3, num = 123, unum = 123, pre = 0}, 0) test_vim_str2nr("123'x4", flags, { len = 3, num = 123, unum = 123, pre = 0 }, 0)
test_vim_str2nr("1337'", flags, {len = 4, num = 1337, unum = 1337, pre = 0}, 0) test_vim_str2nr("1337'", flags, { len = 4, num = 1337, unum = 1337, pre = 0 }, 0)
test_vim_str2nr("-'", flags, {len = 1, num = 0, unum = 0, pre = 0}, 0) test_vim_str2nr("-'", flags, { len = 1, num = 0, unum = 0, pre = 0 }, 0)
flags = lib.STR2NR_HEX + lib.STR2NR_QUOTE flags = lib.STR2NR_HEX + lib.STR2NR_QUOTE
local hex = ('x'):byte() local hex = ('x'):byte()
test_vim_str2nr("0x'abcd", flags, {len = 0}, 0) test_vim_str2nr("0x'abcd", flags, { len = 0 }, 0)
test_vim_str2nr("0x'abcd", flags, {len = 1, num = 0, unum = 0, pre = 0}, 0, false) test_vim_str2nr("0x'abcd", flags, { len = 1, num = 0, unum = 0, pre = 0 }, 0, false)
test_vim_str2nr("0xab''cd", flags, {len = 4, num = 171, unum = 171, pre = hex}, 0) test_vim_str2nr("0xab''cd", flags, { len = 4, num = 171, unum = 171, pre = hex }, 0)
end) end)
end) end)

View File

@ -6,9 +6,13 @@ local eq = helpers.eq
local neq = helpers.neq local neq = helpers.neq
local ffi = helpers.ffi local ffi = helpers.ffi
local decode = cimport('./src/nvim/eval/decode.h', './src/nvim/eval/typval.h', local decode = cimport(
'./src/nvim/globals.h', './src/nvim/memory.h', './src/nvim/eval/decode.h',
'./src/nvim/message.h') './src/nvim/eval/typval.h',
'./src/nvim/globals.h',
'./src/nvim/memory.h',
'./src/nvim/message.h'
)
describe('json_decode_string()', function() describe('json_decode_string()', function()
local char = function(c) local char = function(c)
@ -16,7 +20,7 @@ describe('json_decode_string()', function()
end end
itp('does not overflow when running with `n…`, `t…`, `f…`', function() itp('does not overflow when running with `n…`, `t…`, `f…`', function()
local rettv = ffi.new('typval_T', {v_type=decode.VAR_UNKNOWN}) local rettv = ffi.new('typval_T', { v_type = decode.VAR_UNKNOWN })
decode.emsg_silent = 1 decode.emsg_silent = 1
-- This will not crash, but if `len` argument will be ignored it will parse -- This will not crash, but if `len` argument will be ignored it will parse
-- `null` as `null` and if not it will parse `null` as `n`. -- `null` as `null` and if not it will parse `null` as `n`.
@ -43,7 +47,7 @@ describe('json_decode_string()', function()
end) end)
itp('does not overflow and crash when running with `n`, `t`, `f`', function() itp('does not overflow and crash when running with `n`, `t`, `f`', function()
local rettv = ffi.new('typval_T', {v_type=decode.VAR_UNKNOWN}) local rettv = ffi.new('typval_T', { v_type = decode.VAR_UNKNOWN })
decode.emsg_silent = 1 decode.emsg_silent = 1
eq(0, decode.json_decode_string(char('n'), 1, rettv)) eq(0, decode.json_decode_string(char('n'), 1, rettv))
eq(decode.VAR_UNKNOWN, rettv.v_type) eq(decode.VAR_UNKNOWN, rettv.v_type)
@ -54,7 +58,7 @@ describe('json_decode_string()', function()
end) end)
itp('does not overflow when running with `"…`', function() itp('does not overflow when running with `"…`', function()
local rettv = ffi.new('typval_T', {v_type=decode.VAR_UNKNOWN}) local rettv = ffi.new('typval_T', { v_type = decode.VAR_UNKNOWN })
decode.emsg_silent = 1 decode.emsg_silent = 1
eq(0, decode.json_decode_string('"t"', 2, rettv)) eq(0, decode.json_decode_string('"t"', 2, rettv))
eq(decode.VAR_UNKNOWN, rettv.v_type) eq(decode.VAR_UNKNOWN, rettv.v_type)
@ -63,7 +67,7 @@ describe('json_decode_string()', function()
end) end)
local check_failure = function(s, len, msg) local check_failure = function(s, len, msg)
local rettv = ffi.new('typval_T', {v_type=decode.VAR_UNKNOWN}) local rettv = ffi.new('typval_T', { v_type = decode.VAR_UNKNOWN })
eq(0, decode.json_decode_string(s, len, rettv)) eq(0, decode.json_decode_string(s, len, rettv))
eq(decode.VAR_UNKNOWN, rettv.v_type) eq(decode.VAR_UNKNOWN, rettv.v_type)
neq(nil, decode.last_msg_hist) neq(nil, decode.last_msg_hist)
@ -74,8 +78,7 @@ describe('json_decode_string()', function()
collectgarbage('restart') collectgarbage('restart')
check_failure(']test', 1, 'E474: No container to close: ]') check_failure(']test', 1, 'E474: No container to close: ]')
check_failure('[}test', 2, 'E474: Closing list with curly bracket: }') check_failure('[}test', 2, 'E474: Closing list with curly bracket: }')
check_failure('{]test', 2, check_failure('{]test', 2, 'E474: Closing dictionary with square bracket: ]')
'E474: Closing dictionary with square bracket: ]')
check_failure('[1,]test', 4, 'E474: Trailing comma: ]') check_failure('[1,]test', 4, 'E474: Trailing comma: ]')
check_failure('{"1":}test', 6, 'E474: Expected value after colon: }') check_failure('{"1":}test', 6, 'E474: Expected value after colon: }')
check_failure('{"1"}test', 5, 'E474: Expected value: }') check_failure('{"1"}test', 5, 'E474: Expected value: }')
@ -93,16 +96,20 @@ describe('json_decode_string()', function()
check_failure('ttest', 1, 'E474: Expected true: t') check_failure('ttest', 1, 'E474: Expected true: t')
check_failure('ftest', 1, 'E474: Expected false: f') check_failure('ftest', 1, 'E474: Expected false: f')
check_failure('"\\test', 2, 'E474: Unfinished escape sequence: "\\') check_failure('"\\test', 2, 'E474: Unfinished escape sequence: "\\')
check_failure('"\\u"test', 4, check_failure('"\\u"test', 4, 'E474: Unfinished unicode escape sequence: "\\u"')
'E474: Unfinished unicode escape sequence: "\\u"') check_failure('"\\uXXXX"est', 8, 'E474: Expected four hex digits after \\u: \\uXXXX"')
check_failure('"\\uXXXX"est', 8,
'E474: Expected four hex digits after \\u: \\uXXXX"')
check_failure('"\\?"test', 4, 'E474: Unknown escape sequence: \\?"') check_failure('"\\?"test', 4, 'E474: Unknown escape sequence: \\?"')
check_failure( check_failure(
'"\t"test', 3, '"\t"test',
'E474: ASCII control characters cannot be present inside string: \t"') 3,
'E474: ASCII control characters cannot be present inside string: \t"'
)
check_failure('"\194"test', 3, 'E474: Only UTF-8 strings allowed: \194"') check_failure('"\194"test', 3, 'E474: Only UTF-8 strings allowed: \194"')
check_failure('"\252\144\128\128\128\128"test', 8, 'E474: Only UTF-8 code points up to U+10FFFF are allowed to appear unescaped: \252\144\128\128\128\128"') check_failure(
'"\252\144\128\128\128\128"test',
8,
'E474: Only UTF-8 code points up to U+10FFFF are allowed to appear unescaped: \252\144\128\128\128\128"'
)
check_failure('"test', 1, 'E474: Expected string end: "') check_failure('"test', 1, 'E474: Expected string end: "')
check_failure('-test', 1, 'E474: Missing number after minus sign: -') check_failure('-test', 1, 'E474: Missing number after minus sign: -')
check_failure('-1.test', 3, 'E474: Missing number after decimal dot: -1.') check_failure('-1.test', 3, 'E474: Missing number after decimal dot: -1.')
@ -117,7 +124,7 @@ describe('json_decode_string()', function()
end) end)
itp('does not overflow and crash when running with `"`', function() itp('does not overflow and crash when running with `"`', function()
local rettv = ffi.new('typval_T', {v_type=decode.VAR_UNKNOWN}) local rettv = ffi.new('typval_T', { v_type = decode.VAR_UNKNOWN })
decode.emsg_silent = 1 decode.emsg_silent = 1
eq(0, decode.json_decode_string(char('"'), 1, rettv)) eq(0, decode.json_decode_string(char('"'), 1, rettv))
eq(decode.VAR_UNKNOWN, rettv.v_type) eq(decode.VAR_UNKNOWN, rettv.v_type)

View File

@ -22,80 +22,83 @@ describe('encode_list_write()', function()
itp('writes empty string', function() itp('writes empty string', function()
local l = list() local l = list()
eq(0, encode_list_write(l, '')) eq(0, encode_list_write(l, ''))
eq({[type_key]=list_type}, lst2tbl(l)) eq({ [type_key] = list_type }, lst2tbl(l))
end) end)
itp('writes ASCII string literal with printable characters', function() itp('writes ASCII string literal with printable characters', function()
local l = list() local l = list()
eq(0, encode_list_write(l, 'abc')) eq(0, encode_list_write(l, 'abc'))
eq({'abc'}, lst2tbl(l)) eq({ 'abc' }, lst2tbl(l))
end) end)
itp('writes string starting with NL', function() itp('writes string starting with NL', function()
local l = list() local l = list()
eq(0, encode_list_write(l, '\nabc')) eq(0, encode_list_write(l, '\nabc'))
eq({null_string, 'abc'}, lst2tbl(l)) eq({ null_string, 'abc' }, lst2tbl(l))
end) end)
itp('writes string starting with NL twice', function() itp('writes string starting with NL twice', function()
local l = list() local l = list()
eq(0, encode_list_write(l, '\nabc')) eq(0, encode_list_write(l, '\nabc'))
eq({null_string, 'abc'}, lst2tbl(l)) eq({ null_string, 'abc' }, lst2tbl(l))
eq(0, encode_list_write(l, '\nabc')) eq(0, encode_list_write(l, '\nabc'))
eq({null_string, 'abc', 'abc'}, lst2tbl(l)) eq({ null_string, 'abc', 'abc' }, lst2tbl(l))
end) end)
itp('writes string ending with NL', function() itp('writes string ending with NL', function()
local l = list() local l = list()
eq(0, encode_list_write(l, 'abc\n')) eq(0, encode_list_write(l, 'abc\n'))
eq({'abc', null_string}, lst2tbl(l)) eq({ 'abc', null_string }, lst2tbl(l))
end) end)
itp('writes string ending with NL twice', function() itp('writes string ending with NL twice', function()
local l = list() local l = list()
eq(0, encode_list_write(l, 'abc\n')) eq(0, encode_list_write(l, 'abc\n'))
eq({'abc', null_string}, lst2tbl(l)) eq({ 'abc', null_string }, lst2tbl(l))
eq(0, encode_list_write(l, 'abc\n')) eq(0, encode_list_write(l, 'abc\n'))
eq({'abc', 'abc', null_string}, lst2tbl(l)) eq({ 'abc', 'abc', null_string }, lst2tbl(l))
end) end)
itp('writes string starting, ending and containing NL twice', function() itp('writes string starting, ending and containing NL twice', function()
local l = list() local l = list()
eq(0, encode_list_write(l, '\na\nb\n')) eq(0, encode_list_write(l, '\na\nb\n'))
eq({null_string, 'a', 'b', null_string}, lst2tbl(l)) eq({ null_string, 'a', 'b', null_string }, lst2tbl(l))
eq(0, encode_list_write(l, '\na\nb\n')) eq(0, encode_list_write(l, '\na\nb\n'))
eq({null_string, 'a', 'b', null_string, 'a', 'b', null_string}, lst2tbl(l)) eq({ null_string, 'a', 'b', null_string, 'a', 'b', null_string }, lst2tbl(l))
end) end)
itp('writes string starting, ending and containing NUL with NL between twice', function() itp('writes string starting, ending and containing NUL with NL between twice', function()
local l = list() local l = list()
eq(0, encode_list_write(l, '\0\n\0\n\0')) eq(0, encode_list_write(l, '\0\n\0\n\0'))
eq({'\n', '\n', '\n'}, lst2tbl(l)) eq({ '\n', '\n', '\n' }, lst2tbl(l))
eq(0, encode_list_write(l, '\0\n\0\n\0')) eq(0, encode_list_write(l, '\0\n\0\n\0'))
eq({'\n', '\n', '\n\n', '\n', '\n'}, lst2tbl(l)) eq({ '\n', '\n', '\n\n', '\n', '\n' }, lst2tbl(l))
end) end)
itp('writes string starting, ending and containing NL with NUL between twice', function() itp('writes string starting, ending and containing NL with NUL between twice', function()
local l = list() local l = list()
eq(0, encode_list_write(l, '\n\0\n\0\n')) eq(0, encode_list_write(l, '\n\0\n\0\n'))
eq({null_string, '\n', '\n', null_string}, lst2tbl(l)) eq({ null_string, '\n', '\n', null_string }, lst2tbl(l))
eq(0, encode_list_write(l, '\n\0\n\0\n')) eq(0, encode_list_write(l, '\n\0\n\0\n'))
eq({null_string, '\n', '\n', null_string, '\n', '\n', null_string}, lst2tbl(l)) eq({ null_string, '\n', '\n', null_string, '\n', '\n', null_string }, lst2tbl(l))
end) end)
itp('writes string containing a single NL twice', function() itp('writes string containing a single NL twice', function()
local l = list() local l = list()
eq(0, encode_list_write(l, '\n')) eq(0, encode_list_write(l, '\n'))
eq({null_string, null_string}, lst2tbl(l)) eq({ null_string, null_string }, lst2tbl(l))
eq(0, encode_list_write(l, '\n')) eq(0, encode_list_write(l, '\n'))
eq({null_string, null_string, null_string}, lst2tbl(l)) eq({ null_string, null_string, null_string }, lst2tbl(l))
end) end)
itp('writes string containing a few NLs twice', function() itp('writes string containing a few NLs twice', function()
local l = list() local l = list()
eq(0, encode_list_write(l, '\n\n\n')) eq(0, encode_list_write(l, '\n\n\n'))
eq({null_string, null_string, null_string, null_string}, lst2tbl(l)) eq({ null_string, null_string, null_string, null_string }, lst2tbl(l))
eq(0, encode_list_write(l, '\n\n\n')) eq(0, encode_list_write(l, '\n\n\n'))
eq({null_string, null_string, null_string, null_string, null_string, null_string, null_string}, lst2tbl(l)) eq(
{ null_string, null_string, null_string, null_string, null_string, null_string, null_string },
lst2tbl(l)
)
end) end)
end) end)

View File

@ -6,21 +6,25 @@ local to_cstr = helpers.to_cstr
local ffi = helpers.ffi local ffi = helpers.ffi
local eq = helpers.eq local eq = helpers.eq
local eval = cimport('./src/nvim/eval.h', './src/nvim/eval/typval.h', local eval = cimport(
'./src/nvim/hashtab.h', './src/nvim/memory.h') './src/nvim/eval.h',
'./src/nvim/eval/typval.h',
'./src/nvim/hashtab.h',
'./src/nvim/memory.h'
)
local null_string = {[true]='NULL string'} local null_string = { [true] = 'NULL string' }
local null_list = {[true]='NULL list'} local null_list = { [true] = 'NULL list' }
local null_dict = {[true]='NULL dict'} local null_dict = { [true] = 'NULL dict' }
local type_key = {[true]='type key'} local type_key = { [true] = 'type key' }
local locks_key = {[true]='locks key'} local locks_key = { [true] = 'locks key' }
local list_type = {[true]='list type'} local list_type = { [true] = 'list type' }
local dict_type = {[true]='dict type'} local dict_type = { [true] = 'dict type' }
local func_type = {[true]='func type'} local func_type = { [true] = 'func type' }
local int_type = {[true]='int type'} local int_type = { [true] = 'int type' }
local flt_type = {[true]='flt type'} local flt_type = { [true] = 'flt type' }
local nil_value = {[true]='nil'} local nil_value = { [true] = 'nil' }
local lua2typvalt local lua2typvalt
@ -35,11 +39,13 @@ end
local function li_alloc(nogc) local function li_alloc(nogc)
local gcfunc = tv_list_item_free local gcfunc = tv_list_item_free
if nogc then gcfunc = nil end if nogc then
gcfunc = nil
end
local li = ffi.gc(tv_list_item_alloc(), gcfunc) local li = ffi.gc(tv_list_item_alloc(), gcfunc)
li.li_next = nil li.li_next = nil
li.li_prev = nil li.li_prev = nil
li.li_tv = {v_type=eval.VAR_UNKNOWN, v_lock=eval.VAR_UNLOCKED} li.li_tv = { v_type = eval.VAR_UNKNOWN, v_lock = eval.VAR_UNLOCKED }
return li return li
end end
@ -121,11 +127,11 @@ local function partial2lua(pt, processed)
end end
end end
return { return {
[type_key]=func_type, [type_key] = func_type,
value=value, value = value,
auto=auto, auto = auto,
args=argv, args = argv,
dict=dict, dict = dict,
} }
end end
@ -148,7 +154,7 @@ local function typvalt2lua_tab_init()
})[tonumber(t.vval.v_special)] })[tonumber(t.vval.v_special)]
end, end,
[tonumber(eval.VAR_NUMBER)] = function(t) [tonumber(eval.VAR_NUMBER)] = function(t)
return {[type_key]=int_type, value=tonumber(t.vval.v_number)} return { [type_key] = int_type, value = tonumber(t.vval.v_number) }
end, end,
[tonumber(eval.VAR_FLOAT)] = function(t) [tonumber(eval.VAR_FLOAT)] = function(t)
return tonumber(t.vval.v_float) return tonumber(t.vval.v_float)
@ -168,7 +174,7 @@ local function typvalt2lua_tab_init()
return dct2tbl(t.vval.v_dict, processed) return dct2tbl(t.vval.v_dict, processed)
end, end,
[tonumber(eval.VAR_FUNC)] = function(t, processed) [tonumber(eval.VAR_FUNC)] = function(t, processed)
return {[type_key]=func_type, value=typvalt2lua_tab[eval.VAR_STRING](t, processed or {})} return { [type_key] = func_type, value = typvalt2lua_tab[eval.VAR_STRING](t, processed or {}) }
end, end,
[tonumber(eval.VAR_PARTIAL)] = function(t, processed) [tonumber(eval.VAR_PARTIAL)] = function(t, processed)
local p_key = ptr2key(t) local p_key = ptr2key(t)
@ -182,15 +188,17 @@ end
typvalt2lua = function(t, processed) typvalt2lua = function(t, processed)
typvalt2lua_tab_init() typvalt2lua_tab_init()
return ((typvalt2lua_tab[tonumber(t.v_type)] or function(t_inner) return (
assert(false, 'Converting ' .. tonumber(t_inner.v_type) .. ' was not implemented yet') (typvalt2lua_tab[tonumber(t.v_type)] or function(t_inner)
end)(t, processed or {})) assert(false, 'Converting ' .. tonumber(t_inner.v_type) .. ' was not implemented yet')
end)(t, processed or {})
)
end end
local function list_iter(l) local function list_iter(l)
local init_s = { local init_s = {
idx=0, idx = 0,
li=l.lv_first, li = l.lv_first,
} }
local function f(s, _) local function f(s, _)
-- (listitem_T *) NULL is equal to nil, but yet it is not false. -- (listitem_T *) NULL is equal to nil, but yet it is not false.
@ -222,7 +230,7 @@ lst2tbl = function(l, processed)
if processed[p_key] then if processed[p_key] then
return processed[p_key] return processed[p_key]
end end
local ret = {[type_key]=list_type} local ret = { [type_key] = list_type }
processed[p_key] = ret processed[p_key] = ret
for i, li in list_iter(l) do for i, li in list_iter(l) do
ret[i] = typvalt2lua(li.li_tv, processed) ret[i] = typvalt2lua(li.li_tv, processed)
@ -238,11 +246,13 @@ local hi_key_removed = nil
local function dict_iter(d, return_hi) local function dict_iter(d, return_hi)
hi_key_removed = hi_key_removed or eval._hash_key_removed() hi_key_removed = hi_key_removed or eval._hash_key_removed()
local init_s = { local init_s = {
todo=d.dv_hashtab.ht_used, todo = d.dv_hashtab.ht_used,
hi=d.dv_hashtab.ht_array, hi = d.dv_hashtab.ht_array,
} }
local function f(s, _) local function f(s, _)
if s.todo == 0 then return nil end if s.todo == 0 then
return nil
end
while s.todo > 0 do while s.todo > 0 do
if s.hi.hi_key ~= nil and s.hi.hi_key ~= hi_key_removed then if s.hi.hi_key ~= nil and s.hi.hi_key ~= hi_key_removed then
local key = ffi.string(s.hi.hi_key) local key = ffi.string(s.hi.hi_key)
@ -250,8 +260,7 @@ local function dict_iter(d, return_hi)
if return_hi then if return_hi then
ret = s.hi ret = s.hi
else else
ret = ffi.cast('dictitem_T*', ret = ffi.cast('dictitem_T*', s.hi.hi_key - ffi.offsetof('dictitem_T', 'di_key'))
s.hi.hi_key - ffi.offsetof('dictitem_T', 'di_key'))
end end
s.todo = s.todo - 1 s.todo = s.todo - 1
s.hi = s.hi + 1 s.hi = s.hi + 1
@ -269,7 +278,7 @@ local function first_di(d)
end end
local function dict_items(d) local function dict_items(d)
local ret = {[0]=0} local ret = { [0] = 0 }
for k, hi in dict_iter(d) do for k, hi in dict_iter(d) do
ret[k] = hi ret[k] = hi
ret[0] = ret[0] + 1 ret[0] = ret[0] + 1
@ -301,12 +310,12 @@ local typvalt = function(typ, vval)
elseif type(typ) == 'string' then elseif type(typ) == 'string' then
typ = eval[typ] typ = eval[typ]
end end
return ffi.gc(ffi.new('typval_T', {v_type=typ, vval=vval}), eval.tv_clear) return ffi.gc(ffi.new('typval_T', { v_type = typ, vval = vval }), eval.tv_clear)
end end
local lua2typvalt_type_tab = { local lua2typvalt_type_tab = {
[int_type] = function(l, _) [int_type] = function(l, _)
return typvalt(eval.VAR_NUMBER, {v_number=l.value}) return typvalt(eval.VAR_NUMBER, { v_number = l.value })
end, end,
[flt_type] = function(l, processed) [flt_type] = function(l, processed)
return lua2typvalt(l.value, processed) return lua2typvalt(l.value, processed)
@ -314,31 +323,34 @@ local lua2typvalt_type_tab = {
[list_type] = function(l, processed) [list_type] = function(l, processed)
if processed[l] then if processed[l] then
processed[l].lv_refcount = processed[l].lv_refcount + 1 processed[l].lv_refcount = processed[l].lv_refcount + 1
return typvalt(eval.VAR_LIST, {v_list=processed[l]}) return typvalt(eval.VAR_LIST, { v_list = processed[l] })
end end
local lst = populate_list(eval.tv_list_alloc(#l), l, processed) local lst = populate_list(eval.tv_list_alloc(#l), l, processed)
return typvalt(eval.VAR_LIST, {v_list=lst}) return typvalt(eval.VAR_LIST, { v_list = lst })
end, end,
[dict_type] = function(l, processed) [dict_type] = function(l, processed)
if processed[l] then if processed[l] then
processed[l].dv_refcount = processed[l].dv_refcount + 1 processed[l].dv_refcount = processed[l].dv_refcount + 1
return typvalt(eval.VAR_DICT, {v_dict=processed[l]}) return typvalt(eval.VAR_DICT, { v_dict = processed[l] })
end end
local dct = populate_dict(eval.tv_dict_alloc(), l, processed) local dct = populate_dict(eval.tv_dict_alloc(), l, processed)
return typvalt(eval.VAR_DICT, {v_dict=dct}) return typvalt(eval.VAR_DICT, { v_dict = dct })
end, end,
[func_type] = function(l, processed) [func_type] = function(l, processed)
if processed[l] then if processed[l] then
processed[l].pt_refcount = processed[l].pt_refcount + 1 processed[l].pt_refcount = processed[l].pt_refcount + 1
return typvalt(eval.VAR_PARTIAL, {v_partial=processed[l]}) return typvalt(eval.VAR_PARTIAL, { v_partial = processed[l] })
end end
if l.args or l.dict then if l.args or l.dict then
local pt = populate_partial(ffi.gc(ffi.cast('partial_T*', local pt = populate_partial(
eval.xcalloc(1, ffi.sizeof('partial_T'))), nil), l, processed) ffi.gc(ffi.cast('partial_T*', eval.xcalloc(1, ffi.sizeof('partial_T'))), nil),
return typvalt(eval.VAR_PARTIAL, {v_partial=pt}) l,
processed
)
return typvalt(eval.VAR_PARTIAL, { v_partial = pt })
else else
return typvalt(eval.VAR_FUNC, { return typvalt(eval.VAR_FUNC, {
v_string=eval.xmemdupz(to_cstr(l.value), #l.value) v_string = eval.xmemdupz(to_cstr(l.value), #l.value),
}) })
end end
end, end,
@ -349,12 +361,12 @@ local special_vals = nil
lua2typvalt = function(l, processed) lua2typvalt = function(l, processed)
if not special_vals then if not special_vals then
special_vals = { special_vals = {
[null_string] = {'VAR_STRING', {v_string=ffi.cast('char*', nil)}}, [null_string] = { 'VAR_STRING', { v_string = ffi.cast('char*', nil) } },
[null_list] = {'VAR_LIST', {v_list=ffi.cast('list_T*', nil)}}, [null_list] = { 'VAR_LIST', { v_list = ffi.cast('list_T*', nil) } },
[null_dict] = {'VAR_DICT', {v_dict=ffi.cast('dict_T*', nil)}}, [null_dict] = { 'VAR_DICT', { v_dict = ffi.cast('dict_T*', nil) } },
[nil_value] = {'VAR_SPECIAL', {v_special=eval.kSpecialVarNull}}, [nil_value] = { 'VAR_SPECIAL', { v_special = eval.kSpecialVarNull } },
[true] = {'VAR_BOOL', {v_bool=eval.kBoolVarTrue}}, [true] = { 'VAR_BOOL', { v_bool = eval.kBoolVarTrue } },
[false] = {'VAR_BOOL', {v_bool=eval.kBoolVarFalse}}, [false] = { 'VAR_BOOL', { v_bool = eval.kBoolVarFalse } },
} }
for k, v in pairs(special_vals) do for k, v in pairs(special_vals) do
@ -382,9 +394,9 @@ lua2typvalt = function(l, processed)
end end
end end
elseif type(l) == 'number' then elseif type(l) == 'number' then
return typvalt(eval.VAR_FLOAT, {v_float=l}) return typvalt(eval.VAR_FLOAT, { v_float = l })
elseif type(l) == 'string' then elseif type(l) == 'string' then
return typvalt(eval.VAR_STRING, {v_string=eval.xmemdupz(to_cstr(l), #l)}) return typvalt(eval.VAR_STRING, { v_string = eval.xmemdupz(to_cstr(l), #l) })
elseif type(l) == 'cdata' then elseif type(l) == 'cdata' then
local tv = typvalt(eval.VAR_UNKNOWN) local tv = typvalt(eval.VAR_UNKNOWN)
eval.tv_copy(l, tv) eval.tv_copy(l, tv)
@ -408,43 +420,64 @@ local function alloc_len(len, get_ptr)
end end
local alloc_logging_helpers = { local alloc_logging_helpers = {
list = function(l) return {func='calloc', args={1, ffi.sizeof('list_T')}, ret=void(l)} end, list = function(l)
li = function(li) return {func='malloc', args={ffi.sizeof('listitem_T')}, ret=void(li)} end, return { func = 'calloc', args = { 1, ffi.sizeof('list_T') }, ret = void(l) }
dict = function(d) return {func='calloc', args={1, ffi.sizeof('dict_T')}, ret=void(d)} end, end,
li = function(li)
return { func = 'malloc', args = { ffi.sizeof('listitem_T') }, ret = void(li) }
end,
dict = function(d)
return { func = 'calloc', args = { 1, ffi.sizeof('dict_T') }, ret = void(d) }
end,
di = function(di, size) di = function(di, size)
size = alloc_len(size, function() return di.di_key end) size = alloc_len(size, function()
return {func='malloc', args={ffi.offsetof('dictitem_T', 'di_key') + size + 1}, ret=void(di)} return di.di_key
end)
return {
func = 'malloc',
args = { ffi.offsetof('dictitem_T', 'di_key') + size + 1 },
ret = void(di),
}
end, end,
str = function(s, size) str = function(s, size)
size = alloc_len(size, function() return s end) size = alloc_len(size, function()
return {func='malloc', args={size + 1}, ret=void(s)} return s
end)
return { func = 'malloc', args = { size + 1 }, ret = void(s) }
end, end,
dwatcher = function(w) return {func='malloc', args={ffi.sizeof('DictWatcher')}, ret=void(w)} end, dwatcher = function(w)
return { func = 'malloc', args = { ffi.sizeof('DictWatcher') }, ret = void(w) }
end,
freed = function(p) return {func='free', args={type(p) == 'table' and p or void(p)}} end, freed = function(p)
return { func = 'free', args = { type(p) == 'table' and p or void(p) } }
end,
-- lua_…: allocated by this file, not by some Neovim function -- lua_…: allocated by this file, not by some Neovim function
lua_pt = function(pt) return {func='calloc', args={1, ffi.sizeof('partial_T')}, ret=void(pt)} end, lua_pt = function(pt)
return { func = 'calloc', args = { 1, ffi.sizeof('partial_T') }, ret = void(pt) }
end,
lua_tvs = function(argv, argc) lua_tvs = function(argv, argc)
argc = alloc_len(argc) argc = alloc_len(argc)
return {func='malloc', args={ffi.sizeof('typval_T')*argc}, ret=void(argv)} return { func = 'malloc', args = { ffi.sizeof('typval_T') * argc }, ret = void(argv) }
end, end,
} }
local function int(n) local function int(n)
return {[type_key]=int_type, value=n} return { [type_key] = int_type, value = n }
end end
local function list(...) local function list(...)
return populate_list(ffi.gc(eval.tv_list_alloc(select('#', ...)), return populate_list(
eval.tv_list_unref), ffi.gc(eval.tv_list_alloc(select('#', ...)), eval.tv_list_unref),
{...}, {}) { ... },
{}
)
end end
local function dict(d) local function dict(d)
return populate_dict(ffi.gc(eval.tv_dict_alloc(), eval.tv_dict_free), return populate_dict(ffi.gc(eval.tv_dict_alloc(), eval.tv_dict_free), d or {}, {})
d or {}, {})
end end
local callback2tbl_type_tab = nil local callback2tbl_type_tab = nil
@ -454,14 +487,16 @@ local function init_callback2tbl_type_tab()
return return
end end
callback2tbl_type_tab = { callback2tbl_type_tab = {
[tonumber(eval.kCallbackNone)] = function(_) return {type='none'} end, [tonumber(eval.kCallbackNone)] = function(_)
return { type = 'none' }
end,
[tonumber(eval.kCallbackFuncref)] = function(cb) [tonumber(eval.kCallbackFuncref)] = function(cb)
return {type='fref', fref=ffi.string(cb.data.funcref)} return { type = 'fref', fref = ffi.string(cb.data.funcref) }
end, end,
[tonumber(eval.kCallbackPartial)] = function(cb) [tonumber(eval.kCallbackPartial)] = function(cb)
local lua_pt = partial2lua(cb.data.partial) local lua_pt = partial2lua(cb.data.partial)
return {type='pt', fref=ffi.string(lua_pt.value), pt=lua_pt} return { type = 'pt', fref = ffi.string(lua_pt.value), pt = lua_pt }
end end,
} }
end end
@ -473,15 +508,18 @@ end
local function tbl2callback(tbl) local function tbl2callback(tbl)
local ret = nil local ret = nil
if tbl.type == 'none' then if tbl.type == 'none' then
ret = ffi.new('Callback[1]', {{type=eval.kCallbackNone}}) ret = ffi.new('Callback[1]', { { type = eval.kCallbackNone } })
elseif tbl.type == 'fref' then elseif tbl.type == 'fref' then
ret = ffi.new('Callback[1]', {{type=eval.kCallbackFuncref, ret = ffi.new(
data={funcref=eval.xstrdup(tbl.fref)}}}) 'Callback[1]',
{ { type = eval.kCallbackFuncref, data = { funcref = eval.xstrdup(tbl.fref) } } }
)
elseif tbl.type == 'pt' then elseif tbl.type == 'pt' then
local pt = ffi.gc(ffi.cast('partial_T*', local pt = ffi.gc(ffi.cast('partial_T*', eval.xcalloc(1, ffi.sizeof('partial_T'))), nil)
eval.xcalloc(1, ffi.sizeof('partial_T'))), nil) ret = ffi.new(
ret = ffi.new('Callback[1]', {{type=eval.kCallbackPartial, 'Callback[1]',
data={partial=populate_partial(pt, tbl.pt, {})}}}) { { type = eval.kCallbackPartial, data = { partial = populate_partial(pt, tbl.pt, {}) } } }
)
else else
assert(false) assert(false)
end end
@ -495,24 +533,23 @@ local function dict_watchers(d)
local qs = {} local qs = {}
local key_patterns = {} local key_patterns = {}
while q ~= h do while q ~= h do
local qitem = ffi.cast('DictWatcher *', local qitem =
ffi.cast('char *', q) - ffi.offsetof('DictWatcher', 'node')) ffi.cast('DictWatcher *', ffi.cast('char *', q) - ffi.offsetof('DictWatcher', 'node'))
ret[#ret + 1] = { ret[#ret + 1] = {
cb=callback2tbl(qitem.callback), cb = callback2tbl(qitem.callback),
pat=ffi.string(qitem.key_pattern, qitem.key_pattern_len), pat = ffi.string(qitem.key_pattern, qitem.key_pattern_len),
busy=qitem.busy, busy = qitem.busy,
} }
qs[#qs + 1] = qitem qs[#qs + 1] = qitem
key_patterns[#key_patterns + 1] = {qitem.key_pattern, qitem.key_pattern_len} key_patterns[#key_patterns + 1] = { qitem.key_pattern, qitem.key_pattern_len }
q = q.next q = q.next
end end
return ret, qs, key_patterns return ret, qs, key_patterns
end end
local function eval0(expr) local function eval0(expr)
local tv = ffi.gc(ffi.new('typval_T', {v_type=eval.VAR_UNKNOWN}), local tv = ffi.gc(ffi.new('typval_T', { v_type = eval.VAR_UNKNOWN }), eval.tv_clear)
eval.tv_clear) local evalarg = ffi.new('evalarg_T', { eval_flags = eval.EVAL_EVALUATE })
local evalarg = ffi.new('evalarg_T', {eval_flags = eval.EVAL_EVALUATE})
if eval.eval0(to_cstr(expr), tv, nil, evalarg) == 0 then if eval.eval0(to_cstr(expr), tv, nil, evalarg) == 0 then
return nil return nil
else else
@ -521,49 +558,49 @@ local function eval0(expr)
end end
return { return {
int=int, int = int,
null_string=null_string, null_string = null_string,
null_list=null_list, null_list = null_list,
null_dict=null_dict, null_dict = null_dict,
list_type=list_type, list_type = list_type,
dict_type=dict_type, dict_type = dict_type,
func_type=func_type, func_type = func_type,
int_type=int_type, int_type = int_type,
flt_type=flt_type, flt_type = flt_type,
nil_value=nil_value, nil_value = nil_value,
type_key=type_key, type_key = type_key,
locks_key=locks_key, locks_key = locks_key,
list=list, list = list,
dict=dict, dict = dict,
lst2tbl=lst2tbl, lst2tbl = lst2tbl,
dct2tbl=dct2tbl, dct2tbl = dct2tbl,
lua2typvalt=lua2typvalt, lua2typvalt = lua2typvalt,
typvalt2lua=typvalt2lua, typvalt2lua = typvalt2lua,
typvalt=typvalt, typvalt = typvalt,
li_alloc=li_alloc, li_alloc = li_alloc,
tv_list_item_free=tv_list_item_free, tv_list_item_free = tv_list_item_free,
dict_iter=dict_iter, dict_iter = dict_iter,
list_iter=list_iter, list_iter = list_iter,
first_di=first_di, first_di = first_di,
alloc_logging_helpers=alloc_logging_helpers, alloc_logging_helpers = alloc_logging_helpers,
list_items=list_items, list_items = list_items,
dict_items=dict_items, dict_items = dict_items,
dict_watchers=dict_watchers, dict_watchers = dict_watchers,
tbl2callback=tbl2callback, tbl2callback = tbl2callback,
callback2tbl=callback2tbl, callback2tbl = callback2tbl,
eval0=eval0, eval0 = eval0,
empty_list = {[type_key]=list_type}, empty_list = { [type_key] = list_type },
} }

View File

@ -8,8 +8,7 @@ local eq = helpers.eq
local eval0 = eval_helpers.eval0 local eval0 = eval_helpers.eval0
local eval = cimport('./src/nvim/eval.h', './src/nvim/eval/typval.h', local eval = cimport('./src/nvim/eval.h', './src/nvim/eval/typval.h', './src/nvim/memory.h')
'./src/nvim/memory.h')
describe('NULL typval_T', function() describe('NULL typval_T', function()
itp('is produced by $XXX_UNEXISTENT_VAR_XXX', function() itp('is produced by $XXX_UNEXISTENT_VAR_XXX', function()

View File

@ -28,8 +28,8 @@ end)
describe('tv_clear()', function() describe('tv_clear()', function()
itp('successfully frees all lists in [&l [1], *l, *l]', function() itp('successfully frees all lists in [&l [1], *l, *l]', function()
local l_inner = {1} local l_inner = { 1 }
local list = {l_inner, l_inner, l_inner} local list = { l_inner, l_inner, l_inner }
local list_tv = ffi.gc(lua2typvalt(list), nil) local list_tv = ffi.gc(lua2typvalt(list), nil)
local list_p = list_tv.vval.v_list local list_p = list_tv.vval.v_list
local lis = list_items(list_p) local lis = list_items(list_p)
@ -55,8 +55,8 @@ describe('tv_clear()', function()
}) })
end) end)
itp('successfully frees all lists in [&l [], *l, *l]', function() itp('successfully frees all lists in [&l [], *l, *l]', function()
local l_inner = {[type_key]=list_type} local l_inner = { [type_key] = list_type }
local list = {l_inner, l_inner, l_inner} local list = { l_inner, l_inner, l_inner }
local list_tv = ffi.gc(lua2typvalt(list), nil) local list_tv = ffi.gc(lua2typvalt(list), nil)
local list_p = list_tv.vval.v_list local list_p = list_tv.vval.v_list
local lis = list_items(list_p) local lis = list_items(list_p)
@ -80,7 +80,7 @@ describe('tv_clear()', function()
end) end)
itp('successfully frees all dictionaries in [&d {}, *d]', function() itp('successfully frees all dictionaries in [&d {}, *d]', function()
local d_inner = {} local d_inner = {}
local list = {d_inner, d_inner} local list = { d_inner, d_inner }
local list_tv = ffi.gc(lua2typvalt(list), nil) local list_tv = ffi.gc(lua2typvalt(list), nil)
local list_p = list_tv.vval.v_list local list_p = list_tv.vval.v_list
local lis = list_items(list_p) local lis = list_items(list_p)
@ -101,8 +101,8 @@ describe('tv_clear()', function()
}) })
end) end)
itp('successfully frees all dictionaries in [&d {a: 1}, *d]', function() itp('successfully frees all dictionaries in [&d {a: 1}, *d]', function()
local d_inner = {a=1} local d_inner = { a = 1 }
local list = {d_inner, d_inner} local list = { d_inner, d_inner }
local list_tv = ffi.gc(lua2typvalt(list), nil) local list_tv = ffi.gc(lua2typvalt(list), nil)
local list_p = list_tv.vval.v_list local list_p = list_tv.vval.v_list
local lis = list_items(list_p) local lis = list_items(list_p)

File diff suppressed because it is too large Load Diff

View File

@ -1,24 +1,23 @@
local helpers = require("test.unit.helpers")(after_each) local helpers = require('test.unit.helpers')(after_each)
local itp = helpers.gen_itp(it) local itp = helpers.gen_itp(it)
--{:cimport, :internalize, :eq, :neq, :ffi, :lib, :cstr, :to_cstr} = require 'test.unit.helpers' --{:cimport, :internalize, :eq, :neq, :ffi, :lib, :cstr, :to_cstr} = require 'test.unit.helpers'
local eq = helpers.eq local eq = helpers.eq
local ffi = helpers.ffi local ffi = helpers.ffi
local to_cstr = helpers.to_cstr local to_cstr = helpers.to_cstr
local NULL = helpers.NULL local NULL = helpers.NULL
local fileio = helpers.cimport("./src/nvim/fileio.h") local fileio = helpers.cimport('./src/nvim/fileio.h')
describe('file_pat functions', function() describe('file_pat functions', function()
describe('file_pat_to_reg_pat', function() describe('file_pat_to_reg_pat', function()
local file_pat_to_reg_pat = function(pat) local file_pat_to_reg_pat = function(pat)
local res = fileio.file_pat_to_reg_pat(to_cstr(pat), NULL, NULL, 0) local res = fileio.file_pat_to_reg_pat(to_cstr(pat), NULL, NULL, 0)
return ffi.string(res) return ffi.string(res)
end end
itp('returns ^path$ regex for literal path input', function() itp('returns ^path$ regex for literal path input', function()
eq( '^path$', file_pat_to_reg_pat('path')) eq('^path$', file_pat_to_reg_pat('path'))
end) end)
itp('does not prepend ^ when there is a starting glob (*)', function() itp('does not prepend ^ when there is a starting glob (*)', function()
@ -34,13 +33,15 @@ describe('file_pat functions', function()
end) end)
itp('replaces the bash any character (?) with the regex any character (.)', function() itp('replaces the bash any character (?) with the regex any character (.)', function()
eq('^foo.bar$', file_pat_to_reg_pat('foo?bar')) eq('^foo.bar$', file_pat_to_reg_pat('foo?bar'))
end) end)
itp('replaces a glob (*) in the middle of a path with regex multiple any character (.*)', itp(
function() 'replaces a glob (*) in the middle of a path with regex multiple any character (.*)',
eq('^foo.*bar$', file_pat_to_reg_pat('foo*bar')) function()
end) eq('^foo.*bar$', file_pat_to_reg_pat('foo*bar'))
end
)
itp([[unescapes \? to ?]], function() itp([[unescapes \? to ?]], function()
eq('^foo?bar$', file_pat_to_reg_pat([[foo\?bar]])) eq('^foo?bar$', file_pat_to_reg_pat([[foo\?bar]]))

View File

@ -24,103 +24,145 @@ SOFTWARE. --]]
-- work. -- work.
-- see: http://lua-users.org/wiki/LpegRecipes -- see: http://lua-users.org/wiki/LpegRecipes
local lpeg = require "lpeg" local lpeg = require 'lpeg'
local C, P, R, S, V = lpeg.C, lpeg.P, lpeg.R, lpeg.S, lpeg.V local C, P, R, S, V = lpeg.C, lpeg.P, lpeg.R, lpeg.S, lpeg.V
local Carg, Cc, Cp, Ct = lpeg.Carg, lpeg.Cc, lpeg.Cp, lpeg.Ct local Carg, Cc, Cp, Ct = lpeg.Carg, lpeg.Cc, lpeg.Cp, lpeg.Ct
local tokens = P { "tokens"; local tokens = P {
'tokens',
-- Comment of form /* ... */ -- Comment of form /* ... */
comment = Ct(P"/*" * C((V"newline" + (1 - P"*/"))^0) * P"*/" * Cc"comment"), comment = Ct(P '/*' * C((V 'newline' + (1 - P '*/')) ^ 0) * P '*/' * Cc 'comment'),
-- Single line comment -- Single line comment
line_comment = Ct(P"//" * C((1 - V"newline")^0) * Cc"comment_line"), line_comment = Ct(P '//' * C((1 - V 'newline') ^ 0) * Cc 'comment_line'),
-- Single platform independent line break which increments line number -- Single platform independent line break which increments line number
newline = (P"\r\n" + P"\n\r" + S"\r\n") * (Cp() * Carg(1)) / function(pos, state) newline = (P '\r\n' + P '\n\r' + S '\r\n') * (Cp() * Carg(1)) / function(pos, state)
state.line = state.line + 1 state.line = state.line + 1
state.line_start = pos state.line_start = pos
end, end,
-- Line continuation -- Line continuation
line_extend = Ct(C(P[[\]] * V"newline") * Cc"line_extend"), line_extend = Ct(C(P [[\]] * V 'newline') * Cc 'line_extend'),
-- Whitespace of any length (includes newlines) -- Whitespace of any length (includes newlines)
whitespace = Ct(C((S" \t" + V"newline")^1) * Cc"whitespace"), whitespace = Ct(C((S ' \t' + V 'newline') ^ 1) * Cc 'whitespace'),
-- Special form of #include with filename followed in angled brackets (matches 3 tokens) -- Special form of #include with filename followed in angled brackets (matches 3 tokens)
include = Ct(C(P"#include") * Cc"preprocessor") * include = Ct(C(P '#include') * Cc 'preprocessor') * Ct(C(S ' \t' ^ 1) * Cc 'whitespace') * Ct(
Ct(C(S" \t"^1) * Cc"whitespace") * C(P '<' * (1 - P '>') ^ 1 * P '>') * Cc 'string'
Ct(C(P"<" * (1 - P">")^1 * P">") * Cc"string"), ),
-- Preprocessor instruction -- Preprocessor instruction
preprocessor = V"include" + preprocessor = V 'include'
Ct(C(P"#" * P" "^0 * ( P"define" + P"elif" + P"else" + P"endif" + P"#" + + Ct(
P"error" + P"ifdef" + P"ifndef" + P"if" + P"import" + C(
P"include" + P"line" + P"pragma" + P"undef" + P"using" + P '#'
P"pragma" * P ' ' ^ 0
) * #S" \r\n\t") * Cc"preprocessor"), * (P 'define' + P 'elif' + P 'else' + P 'endif' + P '#' + P 'error' + P 'ifdef' + P 'ifndef' + P 'if' + P 'import' + P 'include' + P 'line' + P 'pragma' + P 'undef' + P 'using' + P 'pragma')
* #S ' \r\n\t'
) * Cc 'preprocessor'
),
-- Identifier of form [a-zA-Z_][a-zA-Z0-9_]* -- Identifier of form [a-zA-Z_][a-zA-Z0-9_]*
identifier = Ct(C(R("az","AZ","__") * R("09","az","AZ","__")^0) * Cc"identifier"), identifier = Ct(C(R('az', 'AZ', '__') * R('09', 'az', 'AZ', '__') ^ 0) * Cc 'identifier'),
-- Single character in a string -- Single character in a string
sstring_char = R("\001&","([","]\255") + (P"\\" * S[[ntvbrfa\?'"0x]]), sstring_char = R('\001&', '([', ']\255') + (P '\\' * S [[ntvbrfa\?'"0x]]),
dstring_char = R("\001!","#[","]\255") + (P"\\" * S[[ntvbrfa\?'"0x]]), dstring_char = R('\001!', '#[', ']\255') + (P '\\' * S [[ntvbrfa\?'"0x]]),
-- String literal -- String literal
string = Ct(C(P"'" * (V"sstring_char" + P'"')^0 * P"'" + string = Ct(
P'"' * (V"dstring_char" + P"'")^0 * P'"') * Cc"string"), C(
P "'" * (V 'sstring_char' + P '"') ^ 0 * P "'"
+ P '"' * (V 'dstring_char' + P "'") ^ 0 * P '"'
) * Cc 'string'
),
-- Operator -- Operator
operator = Ct(C(P">>=" + P"<<=" + P"..." + operator = Ct(
P"::" + P"<<" + P">>" + P"<=" + P">=" + P"==" + P"!=" + C(
P"||" + P"&&" + P"++" + P"--" + P"->" + P"+=" + P"-=" + P '>>='
P"*=" + P"/=" + P"|=" + P"&=" + P"^=" + S"+-*/=<>%^|&.?:!~,") * Cc"operator"), + P '<<='
+ P '...'
+ P '::'
+ P '<<'
+ P '>>'
+ P '<='
+ P '>='
+ P '=='
+ P '!='
+ P '||'
+ P '&&'
+ P '++'
+ P '--'
+ P '->'
+ P '+='
+ P '-='
+ P '*='
+ P '/='
+ P '|='
+ P '&='
+ P '^='
+ S '+-*/=<>%^|&.?:!~,'
) * Cc 'operator'
),
-- Misc. char (token type is the character itself) -- Misc. char (token type is the character itself)
char = Ct(C(S"[]{}();") / function(x) return x, x end), char = Ct(C(S '[]{}();') / function(x)
return x, x
end),
-- Hex, octal or decimal number -- Hex, octal or decimal number
int = Ct(C((P"0x" * R("09","af","AF")^1) + (P"0" * R"07"^0) + R"09"^1) * Cc"integer"), int = Ct(
C((P '0x' * R('09', 'af', 'AF') ^ 1) + (P '0' * R '07' ^ 0) + R '09' ^ 1) * Cc 'integer'
),
-- Floating point number -- Floating point number
f_exponent = S"eE" + S"+-"^-1 * R"09"^1, f_exponent = S 'eE' + S '+-' ^ -1 * R '09' ^ 1,
f_terminator = S"fFlL", f_terminator = S 'fFlL',
float = Ct(C( float = Ct(
R"09"^1 * V"f_exponent" * V"f_terminator"^-1 + C(
R"09"^0 * P"." * R"09"^1 * V"f_exponent"^-1 * V"f_terminator"^-1 + R '09' ^ 1 * V 'f_exponent' * V 'f_terminator' ^ -1
R"09"^1 * P"." * R"09"^0 * V"f_exponent"^-1 * V"f_terminator"^-1 + R '09' ^ 0 * P '.' * R '09' ^ 1 * V 'f_exponent' ^ -1 * V 'f_terminator' ^ -1
) * Cc"float"), + R '09' ^ 1 * P '.' * R '09' ^ 0 * V 'f_exponent' ^ -1 * V 'f_terminator' ^ -1
) * Cc 'float'
),
-- Any token -- Any token
token = V"comment" + token = V 'comment'
V"line_comment" + + V 'line_comment'
V"identifier" + + V 'identifier'
V"whitespace" + + V 'whitespace'
V"line_extend" + + V 'line_extend'
V"preprocessor" + + V 'preprocessor'
V"string" + + V 'string'
V"char" + + V 'char'
V"operator" + + V 'operator'
V"float" + + V 'float'
V"int", + V 'int',
-- Error for when nothing else matches -- Error for when nothing else matches
error = (Cp() * C(P(1) ^ -8) * Carg(1)) / function(pos, where, state) error = (Cp() * C(P(1) ^ -8) * Carg(1)) / function(pos, where, state)
error(("Tokenising error on line %i, position %i, near '%s'") error(
:format(state.line, pos - state.line_start + 1, where)) ("Tokenising error on line %i, position %i, near '%s'"):format(
state.line,
pos - state.line_start + 1,
where
)
)
end, end,
-- Match end of input or throw error -- Match end of input or throw error
finish = -P(1) + V"error", finish = -P(1) + V 'error',
-- Match stream of tokens into a table -- Match stream of tokens into a table
tokens = Ct(V"token" ^ 0) * V"finish", tokens = Ct(V 'token' ^ 0) * V 'finish',
} }
local function TokeniseC(str) local function TokeniseC(str)
return tokens:match(str, 1, {line = 1, line_start = 1}) return tokens:match(str, 1, { line = 1, line_start = 1 })
end end
local function set(t) local function set(t)
@ -131,11 +173,38 @@ local function set(t)
return s return s
end end
local C_keywords = set { -- luacheck: ignore local C_keywords = set { -- luacheck: ignore
"break", "case", "char", "const", "continue", "default", "do", "double", 'break',
"else", "enum", "extern", "float", "for", "goto", "if", "int", "long", 'case',
"register", "return", "short", "signed", "sizeof", "static", "struct", 'char',
"switch", "typedef", "union", "unsigned", "void", "volatile", "while", 'const',
'continue',
'default',
'do',
'double',
'else',
'enum',
'extern',
'float',
'for',
'goto',
'if',
'int',
'long',
'register',
'return',
'short',
'signed',
'sizeof',
'static',
'struct',
'switch',
'typedef',
'union',
'unsigned',
'void',
'volatile',
'while',
} }
-- Very primitive C formatter that tries to put "things" inside braces on one -- Very primitive C formatter that tries to put "things" inside braces on one
@ -174,7 +243,7 @@ local function formatc(str)
-- if we're not inside a block, we're at the basic statement level, -- if we're not inside a block, we're at the basic statement level,
-- and ';' indicates we're at the end of a statement, so we put end -- and ';' indicates we're at the end of a statement, so we put end
-- it with a newline. -- it with a newline.
token[1] = token[1] .. "\n" token[1] = token[1] .. '\n'
end_at_brace = false end_at_brace = false
end end
elseif typ == 'identifier' then elseif typ == 'identifier' then
@ -194,20 +263,20 @@ local function formatc(str)
-- if we're not inside a block, we're at the basic statement level, -- if we're not inside a block, we're at the basic statement level,
-- and ';' indicates we're at the end of a statement, so we put end -- and ';' indicates we're at the end of a statement, so we put end
-- it with a newline. -- it with a newline.
token[1] = ";\n" token[1] = ';\n'
end end
elseif typ == 'whitespace' then elseif typ == 'whitespace' then
-- replace all whitespace by one space -- replace all whitespace by one space
local repl = " " local repl = ' '
-- except when allow_on_nl is true and there's a newline in the whitespace -- except when allow_on_nl is true and there's a newline in the whitespace
if string.find(token[1], "[\r\n]+") and allow_one_nl == true then if string.find(token[1], '[\r\n]+') and allow_one_nl == true then
-- in that case we replace all whitespace by one newline -- in that case we replace all whitespace by one newline
repl = "\n" repl = '\n'
allow_one_nl = false allow_one_nl = false
end end
token[1] = string.gsub(token[1], "%s+", repl) token[1] = string.gsub(token[1], '%s+', repl)
end end
result[#result + 1] = token[1] result[#result + 1] = token[1]
end end
@ -216,8 +285,8 @@ local function formatc(str)
end end
-- standalone operation (very handy for debugging) -- standalone operation (very handy for debugging)
local function standalone(...) -- luacheck: ignore local function standalone(...) -- luacheck: ignore
local Preprocess = require("preprocess") local Preprocess = require('preprocess')
Preprocess.add_to_include_path('./../../src') Preprocess.add_to_include_path('./../../src')
Preprocess.add_to_include_path('./../../build/include') Preprocess.add_to_include_path('./../../build/include')
Preprocess.add_to_include_path('./../../.deps/usr/include') Preprocess.add_to_include_path('./../../.deps/usr/include')
@ -226,9 +295,9 @@ local function standalone(...) -- luacheck: ignore
local formatted local formatted
if #arg == 2 and arg[2] == 'no' then if #arg == 2 and arg[2] == 'no' then
formatted = raw formatted = raw
else else
formatted = formatc(raw) formatted = formatc(raw)
end end
print(formatted) print(formatted)

View File

@ -1,4 +1,4 @@
local helpers = require("test.unit.helpers")(after_each) local helpers = require('test.unit.helpers')(after_each)
local itp = helpers.gen_itp(it) local itp = helpers.gen_itp(it)
local cimport = helpers.cimport local cimport = helpers.cimport
@ -47,7 +47,7 @@ local ga_size = function(garr)
return ga_len(garr) * ga_itemsize(garr) return ga_len(garr) * ga_itemsize(garr)
end end
local ga_maxsize = function(garr) -- luacheck: ignore local ga_maxsize = function(garr) -- luacheck: ignore
return ga_maxlen(garr) * ga_itemsize(garr) return ga_maxlen(garr) * ga_itemsize(garr)
end end
@ -157,7 +157,9 @@ local ga_append_ints = function(garr, ...)
end end
-- enhanced constructors -- enhanced constructors
local garray_ctype = function(...) return ffi.typeof('garray_T[1]')(...) end local garray_ctype = function(...)
return ffi.typeof('garray_T[1]')(...)
end
local new_garray = function() local new_garray = function()
local garr = garray_ctype() local garr = garray_ctype()
return ffi.gc(garr, ga_clear) return ffi.gc(garr, ga_clear)
@ -165,7 +167,7 @@ end
local new_string_garray = function() local new_string_garray = function()
local garr = garray_ctype() local garr = garray_ctype()
ga_init(garr, ffi.sizeof("unsigned char *"), 1) ga_init(garr, ffi.sizeof('unsigned char *'), 1)
return ffi.gc(garr, ga_clear_strings) return ffi.gc(garr, ga_clear_strings)
end end
@ -182,7 +184,6 @@ local ga_scramble = function(garr)
end end
describe('garray', function() describe('garray', function()
describe('ga_init', function() describe('ga_init', function()
itp('initializes the values of the garray', function() itp('initializes the values of the garray', function()
local garr = new_garray() local garr = new_garray()
@ -199,9 +200,9 @@ describe('garray', function()
local function new_and_grow(itemsize_, growsize_, req) local function new_and_grow(itemsize_, growsize_, req)
local garr = new_garray() local garr = new_garray()
ga_init(garr, itemsize_, growsize_) ga_init(garr, itemsize_, growsize_)
eq(0, ga_size(garr)) -- should be 0 at first eq(0, ga_size(garr)) -- should be 0 at first
eq(NULL, ga_data(garr)) -- should be NULL eq(NULL, ga_data(garr)) -- should be NULL
ga_grow(garr, req) -- add space for `req` items ga_grow(garr, req) -- add space for `req` items
return garr return garr
end end
@ -210,8 +211,8 @@ describe('garray', function()
growsize = 4 growsize = 4
local grow_by = growsize - 1 local grow_by = growsize - 1
local garr = new_and_grow(itemsize, growsize, grow_by) local garr = new_and_grow(itemsize, growsize, grow_by)
neq(NULL, ga_data(garr)) -- data should be a ptr to memory neq(NULL, ga_data(garr)) -- data should be a ptr to memory
eq(growsize, ga_maxlen(garr)) -- we requested LESS than growsize, so... eq(growsize, ga_maxlen(garr)) -- we requested LESS than growsize, so...
end) end)
itp('grows by num items if num > growsize', function() itp('grows by num items if num > growsize', function()
@ -219,8 +220,8 @@ describe('garray', function()
growsize = 4 growsize = 4
local grow_by = growsize + 1 local grow_by = growsize + 1
local garr = new_and_grow(itemsize, growsize, grow_by) local garr = new_and_grow(itemsize, growsize, grow_by)
neq(NULL, ga_data(garr)) -- data should be a ptr to memory neq(NULL, ga_data(garr)) -- data should be a ptr to memory
eq(grow_by, ga_maxlen(garr)) -- we requested MORE than growsize, so... eq(grow_by, ga_maxlen(garr)) -- we requested MORE than growsize, so...
end) end)
itp('does not grow when nothing is requested', function() itp('does not grow when nothing is requested', function()
@ -252,7 +253,7 @@ describe('garray', function()
-- this is the actual ga_append, the others are just emulated lua -- this is the actual ga_append, the others are just emulated lua
-- versions -- versions
local garr = new_garray() local garr = new_garray()
ga_init(garr, ffi.sizeof("uint8_t"), 1) ga_init(garr, ffi.sizeof('uint8_t'), 1)
ga_append(garr, 'h') ga_append(garr, 'h')
ga_append(garr, 'e') ga_append(garr, 'e')
ga_append(garr, 'l') ga_append(garr, 'l')
@ -265,13 +266,13 @@ describe('garray', function()
itp('can append integers', function() itp('can append integers', function()
local garr = new_garray() local garr = new_garray()
ga_init(garr, ffi.sizeof("int"), 1) ga_init(garr, ffi.sizeof('int'), 1)
local input = { local input = {
-20, -20,
94, 94,
867615, 867615,
90927, 90927,
86 86,
} }
ga_append_ints(garr, unpack(input)) ga_append_ints(garr, unpack(input))
local ints = ga_data_as_ints(garr) local ints = ga_data_as_ints(garr)
@ -283,11 +284,11 @@ describe('garray', function()
itp('can append strings to a growing array of strings', function() itp('can append strings to a growing array of strings', function()
local garr = new_string_garray() local garr = new_string_garray()
local input = { local input = {
"some", 'some',
"str", 'str',
"\r\n\r●●●●●●,,,", '\r\n\r●●●●●●,,,',
"hmm", 'hmm',
"got it" 'got it',
} }
ga_append_strings(garr, unpack(input)) ga_append_strings(garr, unpack(input))
-- check that we can get the same strings out of the array -- check that we can get the same strings out of the array
@ -301,8 +302,8 @@ describe('garray', function()
describe('ga_concat', function() describe('ga_concat', function()
itp('concatenates the parameter to the growing byte array', function() itp('concatenates the parameter to the growing byte array', function()
local garr = new_garray() local garr = new_garray()
ga_init(garr, ffi.sizeof("char"), 1) ga_init(garr, ffi.sizeof('char'), 1)
local str = "ohwell●●" local str = 'ohwell●●'
local loop = 5 local loop = 5
for _ = 1, loop do for _ = 1, loop do
ga_concat(garr, str) ga_concat(garr, str)
@ -331,21 +332,21 @@ describe('garray', function()
describe('ga_concat_strings', function() describe('ga_concat_strings', function()
itp('returns an empty string when concatenating an empty array', function() itp('returns an empty string when concatenating an empty array', function()
test_concat_fn({ }, ga_concat_strings) test_concat_fn({}, ga_concat_strings)
end) end)
itp('can concatenate a non-empty array', function() itp('can concatenate a non-empty array', function()
test_concat_fn({ test_concat_fn({
'oh', 'oh',
'my', 'my',
'neovim' 'neovim',
}, ga_concat_strings) }, ga_concat_strings)
end) end)
end) end)
describe('ga_concat_strings_sep', function() describe('ga_concat_strings_sep', function()
itp('returns an empty string when concatenating an empty array', function() itp('returns an empty string when concatenating an empty array', function()
test_concat_fn({ }, ga_concat_strings_sep, '---') test_concat_fn({}, ga_concat_strings_sep, '---')
end) end)
itp('can concatenate a non-empty array', function() itp('can concatenate a non-empty array', function()
@ -353,7 +354,7 @@ describe('garray', function()
test_concat_fn({ test_concat_fn({
'oh', 'oh',
'my', 'my',
'neovim' 'neovim',
}, ga_concat_strings_sep, sep) }, ga_concat_strings_sep, sep)
end) end)
end) end)
@ -370,13 +371,13 @@ describe('garray', function()
'bbb', 'bbb',
'ccc', 'ccc',
'ccc', 'ccc',
'ddd●●' 'ddd●●',
} }
local sorted_dedup_input = { local sorted_dedup_input = {
'aaa', 'aaa',
'bbb', 'bbb',
'ccc', 'ccc',
'ddd●●' 'ddd●●',
} }
ga_append_strings(garr, unpack(input)) ga_append_strings(garr, unpack(input))
ga_remove_duplicate_strings(garr) ga_remove_duplicate_strings(garr)

View File

@ -49,7 +49,7 @@ local function child_call(func, ret)
return function(...) return function(...)
local child_calls = child_calls_mod or child_calls_init local child_calls = child_calls_mod or child_calls_init
if child_pid ~= 0 then if child_pid ~= 0 then
child_calls[#child_calls + 1] = {func=func, args={...}} child_calls[#child_calls + 1] = { func = func, args = { ... } }
return ret return ret
else else
return func(...) return func(...)
@ -62,7 +62,7 @@ end
--- @param func function --- @param func function
local function child_call_once(func, ...) local function child_call_once(func, ...)
if child_pid ~= 0 then if child_pid ~= 0 then
child_calls_mod_once[#child_calls_mod_once + 1] = { func = func, args = {...} } child_calls_mod_once[#child_calls_mod_once + 1] = { func = func, args = { ... } }
else else
func(...) func(...)
end end
@ -75,7 +75,7 @@ local child_cleanups_mod_once = nil --- @type ChildCall[]?
local function child_cleanup_once(func, ...) local function child_cleanup_once(func, ...)
local child_cleanups = child_cleanups_mod_once local child_cleanups = child_cleanups_mod_once
if child_pid ~= 0 then if child_pid ~= 0 then
child_cleanups[#child_cleanups + 1] = {func=func, args={...}} child_cleanups[#child_cleanups + 1] = { func = func, args = { ... } }
else else
func(...) func(...)
end end
@ -133,25 +133,28 @@ local pragma_pack_id = 1
local function filter_complex_blocks(body) local function filter_complex_blocks(body)
local result = {} --- @type string[] local result = {} --- @type string[]
for line in body:gmatch("[^\r\n]+") do for line in body:gmatch('[^\r\n]+') do
if not (string.find(line, "(^)", 1, true) ~= nil if
or string.find(line, "_ISwupper", 1, true) not (
or string.find(line, "_Float") string.find(line, '(^)', 1, true) ~= nil
or string.find(line, "__s128") or string.find(line, '_ISwupper', 1, true)
or string.find(line, "__u128") or string.find(line, '_Float')
or string.find(line, "msgpack_zone_push_finalizer") or string.find(line, '__s128')
or string.find(line, "msgpack_unpacker_reserve_buffer") or string.find(line, '__u128')
or string.find(line, "value_init_") or string.find(line, 'msgpack_zone_push_finalizer')
or string.find(line, "UUID_NULL") -- static const uuid_t UUID_NULL = {...} or string.find(line, 'msgpack_unpacker_reserve_buffer')
or string.find(line, "inline _Bool")) then or string.find(line, 'value_init_')
or string.find(line, 'UUID_NULL') -- static const uuid_t UUID_NULL = {...}
or string.find(line, 'inline _Bool')
)
then
result[#result + 1] = line result[#result + 1] = line
end end
end end
return table.concat(result, "\n") return table.concat(result, '\n')
end end
local cdef = ffi.cdef local cdef = ffi.cdef
local cimportstr local cimportstr
@ -184,9 +187,8 @@ local function cimport(...)
previous_defines = previous_defines_init previous_defines = previous_defines_init
cdefs = cdefs_init cdefs = cdefs_init
end end
for _, path in ipairs({...}) do for _, path in ipairs({ ... }) do
if not (path:sub(1, 1) == '/' or path:sub(1, 1) == '.' if not (path:sub(1, 1) == '/' or path:sub(1, 1) == '.' or path:sub(2, 2) == ':') then
or path:sub(2, 2) == ':') then
path = './' .. path path = './' .. path
end end
if not preprocess_cache[path] then if not preprocess_cache[path] then
@ -205,15 +207,15 @@ local function cimport(...)
body = filter_complex_blocks(body) body = filter_complex_blocks(body)
-- add the formatted lines to a set -- add the formatted lines to a set
local new_cdefs = Set:new() local new_cdefs = Set:new()
for line in body:gmatch("[^\r\n]+") do for line in body:gmatch('[^\r\n]+') do
line = trim(line) line = trim(line)
-- give each #pragma pack an unique id, so that they don't get removed -- give each #pragma pack an unique id, so that they don't get removed
-- if they are inserted into the set -- if they are inserted into the set
-- (they are needed in the right order with the struct definitions, -- (they are needed in the right order with the struct definitions,
-- otherwise luajit has wrong memory layouts for the sturcts) -- otherwise luajit has wrong memory layouts for the sturcts)
if line:match("#pragma%s+pack") then if line:match('#pragma%s+pack') then
--- @type string --- @type string
line = line .. " // " .. pragma_pack_id line = line .. ' // ' .. pragma_pack_id
pragma_pack_id = pragma_pack_id + 1 pragma_pack_id = pragma_pack_id + 1
end end
new_cdefs:add(line) new_cdefs:add(line)
@ -277,13 +279,13 @@ end
local function alloc_log_new() local function alloc_log_new()
local log = { local log = {
log={}, --- @type ChildCallLog[] log = {}, --- @type ChildCallLog[]
lib=cimport('./src/nvim/memory.h'), --- @type table<string,function> lib = cimport('./src/nvim/memory.h'), --- @type table<string,function>
original_functions={}, --- @type table<string,function> original_functions = {}, --- @type table<string,function>
null={['\0:is_null']=true}, null = { ['\0:is_null'] = true },
} }
local allocator_functions = {'malloc', 'free', 'calloc', 'realloc'} local allocator_functions = { 'malloc', 'free', 'calloc', 'realloc' }
function log:save_original_functions() function log:save_original_functions()
for _, funcname in ipairs(allocator_functions) do for _, funcname in ipairs(allocator_functions) do
@ -301,7 +303,7 @@ local function alloc_log_new()
local kk = k local kk = k
self.lib['mem_' .. k] = function(...) self.lib['mem_' .. k] = function(...)
--- @type ChildCallLog --- @type ChildCallLog
local log_entry = { func = kk, args = {...} } local log_entry = { func = kk, args = { ... } }
self.log[#self.log + 1] = log_entry self.log[#self.log + 1] = log_entry
if kk == 'free' then if kk == 'free' then
self.original_functions[kk](...) self.original_functions[kk](...)
@ -314,7 +316,9 @@ local function alloc_log_new()
log_entry.args[i] = self.null log_entry.args[i] = self.null
end end
end end
if self.hook then self:hook(log_entry) end if self.hook then
self:hook(log_entry)
end
if log_entry.ret then if log_entry.ret then
return log_entry.ret return log_entry.ret
end end
@ -355,7 +359,7 @@ local function alloc_log_new()
end end
end end
table.sort(toremove) table.sort(toremove)
for i = #toremove,1,-1 do for i = #toremove, 1, -1 do
table.remove(self.log, toremove[i]) table.remove(self.log, toremove[i])
end end
end end
@ -365,11 +369,9 @@ local function alloc_log_new()
log:set_mocks() log:set_mocks()
end end
function log:before_each() function log:before_each() end
end
function log:after_each() function log:after_each() end
end
log:setup() log:setup()
@ -397,13 +399,12 @@ function sc.fork()
end end
function sc.pipe() function sc.pipe()
local ret = ffi.new('int[2]', {-1, -1}) local ret = ffi.new('int[2]', { -1, -1 })
ffi.errno(0) ffi.errno(0)
local res = ffi.C.pipe(ret) local res = ffi.C.pipe(ret)
if (res ~= 0) then if res ~= 0 then
local err = ffi.errno(0) local err = ffi.errno(0)
assert(res == 0, ("pipe() error: %u: %s"):format( assert(res == 0, ('pipe() error: %u: %s'):format(err, ffi.string(ffi.C.strerror(err))))
err, ffi.string(ffi.C.strerror(err))))
end end
assert(ret[0] ~= -1 and ret[1] ~= -1) assert(ret[0] ~= -1 and ret[1] ~= -1)
return ret[0], ret[1] return ret[0], ret[1]
@ -411,19 +412,16 @@ end
--- @return string --- @return string
function sc.read(rd, len) function sc.read(rd, len)
local ret = ffi.new('char[?]', len, {0}) local ret = ffi.new('char[?]', len, { 0 })
local total_bytes_read = 0 local total_bytes_read = 0
ffi.errno(0) ffi.errno(0)
while total_bytes_read < len do while total_bytes_read < len do
local bytes_read = tonumber(ffi.C.read( local bytes_read =
rd, tonumber(ffi.C.read(rd, ffi.cast('void*', ret + total_bytes_read), len - total_bytes_read))
ffi.cast('void*', ret + total_bytes_read),
len - total_bytes_read))
if bytes_read == -1 then if bytes_read == -1 then
local err = ffi.errno(0) local err = ffi.errno(0)
if err ~= ffi.C.kPOSIXErrnoEINTR then if err ~= ffi.C.kPOSIXErrnoEINTR then
assert(false, ("read() error: %u: %s"):format( assert(false, ('read() error: %u: %s'):format(err, ffi.string(ffi.C.strerror(err))))
err, ffi.string(ffi.C.strerror(err))))
end end
elseif bytes_read == 0 then elseif bytes_read == 0 then
break break
@ -439,15 +437,16 @@ function sc.write(wr, s)
local total_bytes_written = 0 local total_bytes_written = 0
ffi.errno(0) ffi.errno(0)
while total_bytes_written < #s do while total_bytes_written < #s do
local bytes_written = tonumber(ffi.C.write( local bytes_written = tonumber(
wr, ffi.C.write(wr, ffi.cast('void*', wbuf + total_bytes_written), #s - total_bytes_written)
ffi.cast('void*', wbuf + total_bytes_written), )
#s - total_bytes_written))
if bytes_written == -1 then if bytes_written == -1 then
local err = ffi.errno(0) local err = ffi.errno(0)
if err ~= ffi.C.kPOSIXErrnoEINTR then if err ~= ffi.C.kPOSIXErrnoEINTR then
assert(false, ("write() error: %u: %s ('%s')"):format( assert(
err, ffi.string(ffi.C.strerror(err)), s)) false,
("write() error: %u: %s ('%s')"):format(err, ffi.string(ffi.C.strerror(err)), s)
)
end end
elseif bytes_written == 0 then elseif bytes_written == 0 then
break break
@ -464,7 +463,7 @@ sc.close = ffi.C.close
--- @return integer --- @return integer
function sc.wait(pid) function sc.wait(pid)
ffi.errno(0) ffi.errno(0)
local stat_loc = ffi.new('int[1]', {0}) local stat_loc = ffi.new('int[1]', { 0 })
while true do while true do
local r = ffi.C.waitpid(pid, stat_loc, ffi.C.kPOSIXWaitWUNTRACED) local r = ffi.C.waitpid(pid, stat_loc, ffi.C.kPOSIXWaitWUNTRACED)
if r == -1 then if r == -1 then
@ -472,8 +471,7 @@ function sc.wait(pid)
if err == ffi.C.kPOSIXErrnoECHILD then if err == ffi.C.kPOSIXErrnoECHILD then
break break
elseif err ~= ffi.C.kPOSIXErrnoEINTR then elseif err ~= ffi.C.kPOSIXErrnoEINTR then
assert(false, ("waitpid() error: %u: %s"):format( assert(false, ('waitpid() error: %u: %s'):format(err, ffi.string(ffi.C.strerror(err))))
err, ffi.string(ffi.C.strerror(err))))
end end
else else
assert(r == pid) assert(r == pid)
@ -489,7 +487,7 @@ sc.exit = ffi.C._exit
local function format_list(lst) local function format_list(lst)
local ret = {} --- @type string[] local ret = {} --- @type string[]
for _, v in ipairs(lst) do for _, v in ipairs(lst) do
ret[#ret+1] = assert:format({v, n=1})[1] ret[#ret + 1] = assert:format({ v, n = 1 })[1]
end end
return table.concat(ret, ', ') return table.concat(ret, ', ')
end end
@ -498,9 +496,8 @@ if os.getenv('NVIM_TEST_PRINT_SYSCALLS') == '1' then
for k_, v_ in pairs(sc) do for k_, v_ in pairs(sc) do
(function(k, v) (function(k, v)
sc[k] = function(...) sc[k] = function(...)
local rets = {v(...)} local rets = { v(...) }
io.stderr:write(('%s(%s) = %s\n'):format(k, format_list({...}), io.stderr:write(('%s(%s) = %s\n'):format(k, format_list({ ... }), format_list(rets)))
format_list(rets)))
return unpack(rets) return unpack(rets)
end end
end)(k_, v_) end)(k_, v_)
@ -512,9 +509,13 @@ local function just_fail(_)
end end
say:set('assertion.just_fail.positive', '%s') say:set('assertion.just_fail.positive', '%s')
say:set('assertion.just_fail.negative', '%s') say:set('assertion.just_fail.negative', '%s')
assert:register('assertion', 'just_fail', just_fail, assert:register(
'assertion.just_fail.positive', 'assertion',
'assertion.just_fail.negative') 'just_fail',
just_fail,
'assertion.just_fail.positive',
'assertion.just_fail.negative'
)
local hook_fnamelen = 30 local hook_fnamelen = 30
local hook_sfnamelen = 30 local hook_sfnamelen = 30
@ -561,7 +562,7 @@ local function child_sethook(wr)
local info = nil --- @type debuginfo? local info = nil --- @type debuginfo?
if use_prev then if use_prev then
info = prev_info info = prev_info
elseif reason ~= 'tail return' then -- tail return elseif reason ~= 'tail return' then -- tail return
info = debug.getinfo(2, 'nSl') info = debug.getinfo(2, 'nSl')
end end
@ -609,17 +610,20 @@ local function child_sethook(wr)
-- assert(-1 <= lnum and lnum <= 99999) -- assert(-1 <= lnum and lnum <= 99999)
local lnum_s = lnum == -1 and 'nknwn' or ('%u'):format(lnum) local lnum_s = lnum == -1 and 'nknwn' or ('%u'):format(lnum)
--- @type string --- @type string
local msg = ( -- lua does not support %* local msg = ( -- lua does not support %*
'' ''
.. msgchar .. msgchar
.. whatchar .. whatchar
.. namewhatchar .. namewhatchar
.. ' ' .. ' '
.. source .. (' '):rep(hook_sfnamelen - #source) .. source
.. (' '):rep(hook_sfnamelen - #source)
.. ':' .. ':'
.. funcname .. (' '):rep(hook_fnamelen - #funcname) .. funcname
.. (' '):rep(hook_fnamelen - #funcname)
.. ':' .. ':'
.. ('0'):rep(hook_numlen - #lnum_s) .. lnum_s .. ('0'):rep(hook_numlen - #lnum_s)
.. lnum_s
.. '\n' .. '\n'
) )
-- eq(hook_msglen, #msg) -- eq(hook_msglen, #msg)
@ -742,16 +746,16 @@ local function itp_parent(rd, pid, allow_failure, location)
sc.close(rd) sc.close(rd)
if not ok then if not ok then
if allow_failure then if allow_failure then
io.stderr:write('Errorred out ('..status..'):\n' .. tostring(emsg) .. '\n') io.stderr:write('Errorred out (' .. status .. '):\n' .. tostring(emsg) .. '\n')
os.execute([[ os.execute([[
sh -c "source ci/common/test.sh sh -c "source ci/common/test.sh
check_core_dumps --delete \"]] .. Paths.test_luajit_prg .. [[\""]]) check_core_dumps --delete \"]] .. Paths.test_luajit_prg .. [[\""]])
else else
error(tostring(emsg)..'\nexit code: '..status) error(tostring(emsg) .. '\nexit code: ' .. status)
end end
elseif status ~= 0 then elseif status ~= 0 then
if not allow_failure then if not allow_failure then
error("child process errored out with status "..status.."!\n\n"..location) error('child process errored out with status ' .. status .. '!\n\n' .. location)
end end
end end
end end
@ -760,7 +764,9 @@ local function gen_itp(it)
child_calls_mod = {} child_calls_mod = {}
child_calls_mod_once = {} child_calls_mod_once = {}
child_cleanups_mod_once = {} child_cleanups_mod_once = {}
preprocess_cache_mod = map(function(v) return v end, preprocess_cache_init) preprocess_cache_mod = map(function(v)
return v
end, preprocess_cache_init)
previous_defines_mod = previous_defines_init previous_defines_mod = previous_defines_init
cdefs_mod = cdefs_init:copy() cdefs_mod = cdefs_init:copy()
local function itp(name, func, allow_failure) local function itp(name, func, allow_failure)
@ -794,8 +800,7 @@ local function cppimport(path)
return cimport(Paths.test_source_path .. '/test/includes/pre/' .. path) return cimport(Paths.test_source_path .. '/test/includes/pre/' .. path)
end end
cimport('./src/nvim/types.h', './src/nvim/main.h', './src/nvim/os/time.h', cimport('./src/nvim/types.h', './src/nvim/main.h', './src/nvim/os/time.h', './src/nvim/os/fs.h')
'./src/nvim/os/fs.h')
local function conv_enum(etab, eval) local function conv_enum(etab, eval)
local n = tonumber(eval) local n = tonumber(eval)
@ -844,7 +849,7 @@ local function ptr2addr(ptr)
return tonumber(ffi.cast('intptr_t', ffi.cast('void *', ptr))) return tonumber(ffi.cast('intptr_t', ffi.cast('void *', ptr)))
end end
local s = ffi.new('char[64]', {0}) local s = ffi.new('char[64]', { 0 })
local function ptr2key(ptr) local function ptr2key(ptr)
ffi.C.snprintf(s, ffi.sizeof(s), '%p', ffi.cast('void *', ptr)) ffi.C.snprintf(s, ffi.sizeof(s), '%p', ffi.cast('void *', ptr))
@ -853,7 +858,9 @@ end
local function is_asan() local function is_asan()
cimport('./src/nvim/version.h') cimport('./src/nvim/version.h')
local status, res = pcall(function() return lib.version_cflags end) local status, res = pcall(function()
return lib.version_cflags
end)
if status then if status then
return ffi.string(res):match('-fsanitize=[a-z,]*address') return ffi.string(res):match('-fsanitize=[a-z,]*address')
else else

View File

@ -1,10 +1,10 @@
local helpers = require("test.unit.helpers")(after_each) local helpers = require('test.unit.helpers')(after_each)
local itp = helpers.gen_itp(it) local itp = helpers.gen_itp(it)
local eq = helpers.eq local eq = helpers.eq
local indent = helpers.cimport("./src/nvim/indent.h") local indent = helpers.cimport('./src/nvim/indent.h')
local globals = helpers.cimport("./src/nvim/globals.h") local globals = helpers.cimport('./src/nvim/globals.h')
describe('get_sts_value', function() describe('get_sts_value', function()
itp([[returns 'softtabstop' when it is non-negative]], function() itp([[returns 'softtabstop' when it is non-negative]], function()

View File

@ -1,15 +1,14 @@
local helpers = require("test.unit.helpers")(after_each) local helpers = require('test.unit.helpers')(after_each)
local itp = helpers.gen_itp(it) local itp = helpers.gen_itp(it)
local ffi = helpers.ffi local ffi = helpers.ffi
local eq = helpers.eq local eq = helpers.eq
local neq = helpers.neq local neq = helpers.neq
local keycodes = helpers.cimport('./src/nvim/keycodes.h') local keycodes = helpers.cimport('./src/nvim/keycodes.h')
local NULL = helpers.NULL local NULL = helpers.NULL
describe('keycodes.c', function() describe('keycodes.c', function()
describe('find_special_key()', function() describe('find_special_key()', function()
local srcp = ffi.new('const unsigned char *[1]') local srcp = ffi.new('const unsigned char *[1]')
local modp = ffi.new('int[1]') local modp = ffi.new('int[1]')
@ -28,31 +27,26 @@ describe('keycodes.c', function()
itp('case-insensitive', function() itp('case-insensitive', function()
-- Compare other capitalizations to this. -- Compare other capitalizations to this.
srcp[0] = '<C-A>' srcp[0] = '<C-A>'
local all_caps_key = local all_caps_key = keycodes.find_special_key(srcp, 5, modp, 0, NULL)
keycodes.find_special_key(srcp, 5, modp, 0, NULL)
local all_caps_mod = modp[0] local all_caps_mod = modp[0]
srcp[0] = '<C-a>' srcp[0] = '<C-a>'
eq(all_caps_key, eq(all_caps_key, keycodes.find_special_key(srcp, 5, modp, 0, NULL))
keycodes.find_special_key(srcp, 5, modp, 0, NULL))
eq(all_caps_mod, modp[0]) eq(all_caps_mod, modp[0])
srcp[0] = '<c-A>' srcp[0] = '<c-A>'
eq(all_caps_key, eq(all_caps_key, keycodes.find_special_key(srcp, 5, modp, 0, NULL))
keycodes.find_special_key(srcp, 5, modp, 0, NULL))
eq(all_caps_mod, modp[0]) eq(all_caps_mod, modp[0])
srcp[0] = '<c-a>' srcp[0] = '<c-a>'
eq(all_caps_key, eq(all_caps_key, keycodes.find_special_key(srcp, 5, modp, 0, NULL))
keycodes.find_special_key(srcp, 5, modp, 0, NULL))
eq(all_caps_mod, modp[0]) eq(all_caps_mod, modp[0])
end) end)
itp('double-quote in keycode #7411', function() itp('double-quote in keycode #7411', function()
-- Unescaped with in_string=false -- Unescaped with in_string=false
srcp[0] = '<C-">' srcp[0] = '<C-">'
eq(string.byte('"'), eq(string.byte('"'), keycodes.find_special_key(srcp, 5, modp, 0, NULL))
keycodes.find_special_key(srcp, 5, modp, 0, NULL))
-- Unescaped with in_string=true -- Unescaped with in_string=true
eq(0, keycodes.find_special_key(srcp, 5, modp, keycodes.FSK_IN_STRING, NULL)) eq(0, keycodes.find_special_key(srcp, 5, modp, keycodes.FSK_IN_STRING, NULL))
@ -64,9 +58,7 @@ describe('keycodes.c', function()
eq(0, keycodes.find_special_key(srcp, 6, modp, 0, NULL)) eq(0, keycodes.find_special_key(srcp, 6, modp, 0, NULL))
-- Escaped with in_string=true -- Escaped with in_string=true
eq(string.byte('"'), eq(string.byte('"'), keycodes.find_special_key(srcp, 6, modp, keycodes.FSK_IN_STRING, NULL))
keycodes.find_special_key(srcp, 6, modp, keycodes.FSK_IN_STRING, NULL))
end) end)
end) end)
end) end)

View File

@ -1,15 +1,17 @@
local helpers = require("test.unit.helpers")(after_each) local helpers = require('test.unit.helpers')(after_each)
local itp = helpers.gen_itp(it) local itp = helpers.gen_itp(it)
local ffi = helpers.ffi local ffi = helpers.ffi
local eq = helpers.eq local eq = helpers.eq
local ok = helpers.ok local ok = helpers.ok
local lib = helpers.cimport("./src/nvim/marktree.h") local lib = helpers.cimport('./src/nvim/marktree.h')
local function tablelength(t) local function tablelength(t)
local count = 0 local count = 0
for _ in pairs(t) do count = count + 1 end for _ in pairs(t) do
count = count + 1
end
return count return count
end end
@ -32,15 +34,27 @@ local function shadoworder(tree, shadow, iter, giveorder)
local mark = lib.marktree_itr_current(iter) local mark = lib.marktree_itr_current(iter)
local id = tonumber(mark.id) local id = tonumber(mark.id)
local spos = shadow[id] local spos = shadow[id]
if (mark.pos.row ~= spos[1] or mark.pos.col ~= spos[2]) then if mark.pos.row ~= spos[1] or mark.pos.col ~= spos[2] then
error("invalid pos for "..id..":("..mark.pos.row..", "..mark.pos.col..") instead of ("..spos[1]..", "..spos[2]..")") error(
'invalid pos for '
.. id
.. ':('
.. mark.pos.row
.. ', '
.. mark.pos.col
.. ') instead of ('
.. spos[1]
.. ', '
.. spos[2]
.. ')'
)
end end
if lib.mt_right_test(mark) ~= spos[3] then if lib.mt_right_test(mark) ~= spos[3] then
error("invalid gravity for "..id..":("..mark.pos.row..", "..mark.pos.col..")") error('invalid gravity for ' .. id .. ':(' .. mark.pos.row .. ', ' .. mark.pos.col .. ')')
end end
if count > 0 then if count > 0 then
if not pos_leq(last, spos) then if not pos_leq(last, spos) then
error("DISORDER") error('DISORDER')
end end
end end
count = count + 1 count = count + 1
@ -52,17 +66,21 @@ local function shadoworder(tree, shadow, iter, giveorder)
until not lib.marktree_itr_next(tree, iter) until not lib.marktree_itr_next(tree, iter)
local shadowlen = tablelength(shadow) local shadowlen = tablelength(shadow)
if shadowlen ~= count then if shadowlen ~= count then
error("missed some keys? (shadow "..shadowlen..", tree "..count..")") error('missed some keys? (shadow ' .. shadowlen .. ', tree ' .. count .. ')')
end end
return id2pos, pos2id return id2pos, pos2id
end end
local function shadowsplice(shadow, start, old_extent, new_extent) local function shadowsplice(shadow, start, old_extent, new_extent)
local old_end = {start[1] + old_extent[1], local old_end = {
(old_extent[1] == 0 and start[2] or 0) + old_extent[2]} start[1] + old_extent[1],
local new_end = {start[1] + new_extent[1], (old_extent[1] == 0 and start[2] or 0) + old_extent[2],
(new_extent[1] == 0 and start[2] or 0) + new_extent[2]} }
local delta = {new_end[1] - old_end[1], new_end[2] - old_end[2]} local new_end = {
start[1] + new_extent[1],
(new_extent[1] == 0 and start[2] or 0) + new_extent[2],
}
local delta = { new_end[1] - old_end[1], new_end[2] - old_end[2] }
for _, pos in pairs(shadow) do for _, pos in pairs(shadow) do
if pos_leq(start, pos) then if pos_leq(start, pos) then
if pos_leq(pos, old_end) then if pos_leq(pos, old_end) then
@ -83,7 +101,15 @@ local function shadowsplice(shadow, start, old_extent, new_extent)
end end
local function dosplice(tree, shadow, start, old_extent, new_extent) local function dosplice(tree, shadow, start, old_extent, new_extent)
lib.marktree_splice(tree, start[1], start[2], old_extent[1], old_extent[2], new_extent[1], new_extent[2]) lib.marktree_splice(
tree,
start[1],
start[2],
old_extent[1],
old_extent[2],
new_extent[1],
new_extent[2]
)
shadowsplice(shadow, start, old_extent, new_extent) shadowsplice(shadow, start, old_extent, new_extent)
end end
@ -98,7 +124,7 @@ local function put(tree, row, col, gravitate, end_row, end_col, end_gravitate)
end_col = end_col or -1 end_col = end_col or -1
end_gravitate = end_gravitate or false end_gravitate = end_gravitate or false
lib.marktree_put_test(tree, ns, my_id, row, col, gravitate, end_row, end_col, end_gravitate); lib.marktree_put_test(tree, ns, my_id, row, col, gravitate, end_row, end_col, end_gravitate)
return my_id return my_id
end end
@ -108,18 +134,18 @@ describe('marktree', function()
end) end)
itp('works', function() itp('works', function()
local tree = ffi.new("MarkTree[1]") -- zero initialized by luajit local tree = ffi.new('MarkTree[1]') -- zero initialized by luajit
local shadow = {} local shadow = {}
local iter = ffi.new("MarkTreeIter[1]") local iter = ffi.new('MarkTreeIter[1]')
local iter2 = ffi.new("MarkTreeIter[1]") local iter2 = ffi.new('MarkTreeIter[1]')
for i = 1,100 do for i = 1, 100 do
for j = 1,100 do for j = 1, 100 do
local gravitate = (i%2) > 0 local gravitate = (i % 2) > 0
local id = put(tree, j, i, gravitate) local id = put(tree, j, i, gravitate)
ok(id > 0) ok(id > 0)
eq(nil, shadow[id]) eq(nil, shadow[id])
shadow[id] = {j,i,gravitate} shadow[id] = { j, i, gravitate }
end end
-- checking every insert is too slow, but this is ok -- checking every insert is too slow, but this is ok
lib.marktree_check(tree) lib.marktree_check(tree)
@ -133,7 +159,7 @@ describe('marktree', function()
eq({}, pos2id) -- not set if not requested eq({}, pos2id) -- not set if not requested
eq({}, id2pos) eq({}, id2pos)
for i,ipos in pairs(shadow) do for i, ipos in pairs(shadow) do
local p = lib.marktree_lookup_ns(tree, ns, i, false, iter) local p = lib.marktree_lookup_ns(tree, ns, i, false, iter)
eq(ipos[1], p.pos.row) eq(ipos[1], p.pos.row)
eq(ipos[2], p.pos.col) eq(ipos[2], p.pos.col)
@ -145,7 +171,7 @@ describe('marktree', function()
-- local k2 = lib.marktree_itr_current(iter) -- local k2 = lib.marktree_itr_current(iter)
end end
for i,ipos in pairs(shadow) do for i, ipos in pairs(shadow) do
lib.marktree_itr_get(tree, ipos[1], ipos[2], iter) lib.marktree_itr_get(tree, ipos[1], ipos[2], iter)
local k = lib.marktree_itr_current(iter) local k = lib.marktree_itr_current(iter)
eq(i, tonumber(k.id)) eq(i, tonumber(k.id))
@ -160,9 +186,9 @@ describe('marktree', function()
shadow[tonumber(del.id)] = nil shadow[tonumber(del.id)] = nil
shadoworder(tree, shadow, iter) shadoworder(tree, shadow, iter)
for _, ci in ipairs({0,-1,1,-2,2,-10,10}) do for _, ci in ipairs({ 0, -1, 1, -2, 2, -10, 10 }) do
for i = 1,100 do for i = 1, 100 do
lib.marktree_itr_get(tree, i, 50+ci, iter) lib.marktree_itr_get(tree, i, 50 + ci, iter)
local k = lib.marktree_itr_current(iter) local k = lib.marktree_itr_current(iter)
local id = tonumber(k.id) local id = tonumber(k.id)
eq(shadow[id][1], k.pos.row) eq(shadow[id][1], k.pos.row)
@ -177,14 +203,14 @@ describe('marktree', function()
-- NB: this is quite rudimentary. We rely on -- NB: this is quite rudimentary. We rely on
-- functional tests exercising splicing quite a bit -- functional tests exercising splicing quite a bit
lib.marktree_check(tree) lib.marktree_check(tree)
dosplice(tree, shadow, {2,2}, {0,5}, {1, 2}) dosplice(tree, shadow, { 2, 2 }, { 0, 5 }, { 1, 2 })
lib.marktree_check(tree) lib.marktree_check(tree)
shadoworder(tree, shadow, iter) shadoworder(tree, shadow, iter)
dosplice(tree, shadow, {30,2}, {30,5}, {1, 2}) dosplice(tree, shadow, { 30, 2 }, { 30, 5 }, { 1, 2 })
lib.marktree_check(tree) lib.marktree_check(tree)
shadoworder(tree, shadow, iter) shadoworder(tree, shadow, iter)
dosplice(tree, shadow, {5,3}, {0,2}, {0, 5}) dosplice(tree, shadow, { 5, 3 }, { 0, 2 }, { 0, 5 })
shadoworder(tree, shadow, iter) shadoworder(tree, shadow, iter)
lib.marktree_check(tree) lib.marktree_check(tree)
@ -209,7 +235,7 @@ describe('marktree', function()
-- Check iterator validity for 2 specific edge cases: -- Check iterator validity for 2 specific edge cases:
-- https://github.com/neovim/neovim/pull/14719 -- https://github.com/neovim/neovim/pull/14719
lib.marktree_clear(tree) lib.marktree_clear(tree)
for i = 1,20 do for i = 1, 20 do
put(tree, i, i, false) put(tree, i, i, false)
end end
@ -224,46 +250,60 @@ describe('marktree', function()
itp("'intersect_mov' function works correctly", function() itp("'intersect_mov' function works correctly", function()
local function mov(x, y, w) local function mov(x, y, w)
local xa = ffi.new("uint64_t[?]", #x) local xa = ffi.new('uint64_t[?]', #x)
for i, xi in ipairs(x) do xa[i-1] = xi end for i, xi in ipairs(x) do
local ya = ffi.new("uint64_t[?]", #y) xa[i - 1] = xi
for i, yi in ipairs(y) do ya[i-1] = yi end end
local wa = ffi.new("uint64_t[?]", #w) local ya = ffi.new('uint64_t[?]', #y)
for i, wi in ipairs(w) do wa[i-1] = wi end for i, yi in ipairs(y) do
ya[i - 1] = yi
end
local wa = ffi.new('uint64_t[?]', #w)
for i, wi in ipairs(w) do
wa[i - 1] = wi
end
local dummy_size = #x + #y + #w local dummy_size = #x + #y + #w
local wouta = ffi.new("uint64_t[?]", dummy_size) local wouta = ffi.new('uint64_t[?]', dummy_size)
local douta = ffi.new("uint64_t[?]", dummy_size) local douta = ffi.new('uint64_t[?]', dummy_size)
local wsize = ffi.new("size_t[1]") local wsize = ffi.new('size_t[1]')
wsize[0] = dummy_size wsize[0] = dummy_size
local dsize = ffi.new("size_t[1]") local dsize = ffi.new('size_t[1]')
dsize[0] = dummy_size dsize[0] = dummy_size
local status = lib.intersect_mov_test(xa, #x, ya, #y, wa, #w, wouta, wsize, douta, dsize) local status = lib.intersect_mov_test(xa, #x, ya, #y, wa, #w, wouta, wsize, douta, dsize)
if status == 0 then error'wowza' end if status == 0 then
error 'wowza'
end
local wout, dout = {}, {} local wout, dout = {}, {}
for i = 0,tonumber(wsize[0])-1 do table.insert(wout, tonumber(wouta[i])) end for i = 0, tonumber(wsize[0]) - 1 do
for i = 0,tonumber(dsize[0])-1 do table.insert(dout, tonumber(douta[i])) end table.insert(wout, tonumber(wouta[i]))
return {wout, dout} end
for i = 0, tonumber(dsize[0]) - 1 do
table.insert(dout, tonumber(douta[i]))
end
return { wout, dout }
end end
eq({{}, {}}, mov({}, {2, 3}, {2, 3})) eq({ {}, {} }, mov({}, { 2, 3 }, { 2, 3 }))
eq({{2, 3}, {}}, mov({}, {}, {2, 3})) eq({ { 2, 3 }, {} }, mov({}, {}, { 2, 3 }))
eq({{2, 3}, {}}, mov({2, 3}, {}, {})) eq({ { 2, 3 }, {} }, mov({ 2, 3 }, {}, {}))
eq({{}, {2,3}}, mov({}, {2,3}, {})) eq({ {}, { 2, 3 } }, mov({}, { 2, 3 }, {}))
eq({{1, 5}, {}}, mov({1,2,5}, {2, 3}, {3})) eq({ { 1, 5 }, {} }, mov({ 1, 2, 5 }, { 2, 3 }, { 3 }))
eq({{1, 2}, {}}, mov({1,2,5}, {5, 10}, {10})) eq({ { 1, 2 }, {} }, mov({ 1, 2, 5 }, { 5, 10 }, { 10 }))
eq({{1, 2}, {5}}, mov({1,2}, {5, 10}, {10})) eq({ { 1, 2 }, { 5 } }, mov({ 1, 2 }, { 5, 10 }, { 10 }))
eq({{1,3,5,7,9}, {2,4,6,8,10}}, mov({1,3,5,7,9}, {2,4,6,8,10}, {})) eq({ { 1, 3, 5, 7, 9 }, { 2, 4, 6, 8, 10 } }, mov({ 1, 3, 5, 7, 9 }, { 2, 4, 6, 8, 10 }, {}))
eq({{1,3,5,7,9}, {2,6,10}}, mov({1,3,5,7,9}, {2,4,6,8,10}, {4, 8})) eq({ { 1, 3, 5, 7, 9 }, { 2, 6, 10 } }, mov({ 1, 3, 5, 7, 9 }, { 2, 4, 6, 8, 10 }, { 4, 8 }))
eq({{1,4,7}, {2,5,8}}, mov({1,3,4,6,7,9}, {2,3,5,6,8,9}, {})) eq({ { 1, 4, 7 }, { 2, 5, 8 } }, mov({ 1, 3, 4, 6, 7, 9 }, { 2, 3, 5, 6, 8, 9 }, {}))
eq({{1,4,7}, {}}, mov({1,3,4,6,7,9}, {2,3,5,6,8,9}, {2,5,8})) eq({ { 1, 4, 7 }, {} }, mov({ 1, 3, 4, 6, 7, 9 }, { 2, 3, 5, 6, 8, 9 }, { 2, 5, 8 }))
eq({{0,1,4,7,10}, {}}, mov({1,3,4,6,7,9}, {2,3,5,6,8,9}, {0,2,5,8,10})) eq(
{ { 0, 1, 4, 7, 10 }, {} },
mov({ 1, 3, 4, 6, 7, 9 }, { 2, 3, 5, 6, 8, 9 }, { 0, 2, 5, 8, 10 })
)
end) end)
local function check_intersections(tree) local function check_intersections(tree)
lib.marktree_check(tree) lib.marktree_check(tree)
-- to debug stuff disable this branch -- to debug stuff disable this branch
@ -279,13 +319,13 @@ describe('marktree', function()
if not val then if not val then
local str2 = lib.mt_inspect(tree, true, true) local str2 = lib.mt_inspect(tree, true, true)
local dot2 = ffi.string(str2.data, str2.size) local dot2 = ffi.string(str2.data, str2.size)
print("actual:\n\n".."Xafile.dot".."\n\nexpected:\n\n".."Xefile.dot".."\n") print('actual:\n\n' .. 'Xafile.dot' .. '\n\nexpected:\n\n' .. 'Xefile.dot' .. '\n')
print("nivå", tree[0].root.level); print('nivå', tree[0].root.level)
io.stdout:flush() io.stdout:flush()
local afil = io.open("Xafile.dot", "wb") local afil = io.open('Xafile.dot', 'wb')
afil:write(dot1) afil:write(dot1)
afil:close() afil:close()
local efil = io.open("Xefile.dot", "wb") local efil = io.open('Xefile.dot', 'wb')
efil:write(dot2) efil:write(dot2)
efil:close() efil:close()
ok(false) ok(false)
@ -295,28 +335,28 @@ describe('marktree', function()
end end
itp('works with intersections', function() itp('works with intersections', function()
local tree = ffi.new("MarkTree[1]") -- zero initialized by luajit local tree = ffi.new('MarkTree[1]') -- zero initialized by luajit
local ids = {} local ids = {}
for i = 1,80 do for i = 1, 80 do
table.insert(ids, put(tree, 1, i, false, 2, 100-i, false)) table.insert(ids, put(tree, 1, i, false, 2, 100 - i, false))
check_intersections(tree) check_intersections(tree)
end end
for i = 1,80 do for i = 1, 80 do
lib.marktree_del_pair_test(tree, ns, ids[i]) lib.marktree_del_pair_test(tree, ns, ids[i])
check_intersections(tree) check_intersections(tree)
end end
ids = {} ids = {}
for i = 1,80 do for i = 1, 80 do
table.insert(ids, put(tree, 1, i, false, 2, 100-i, false)) table.insert(ids, put(tree, 1, i, false, 2, 100 - i, false))
check_intersections(tree) check_intersections(tree)
end end
for i = 1,10 do for i = 1, 10 do
for j = 1,8 do for j = 1, 8 do
local ival = (j-1)*10+i local ival = (j - 1) * 10 + i
lib.marktree_del_pair_test(tree, ns, ids[ival]) lib.marktree_del_pair_test(tree, ns, ids[ival])
check_intersections(tree) check_intersections(tree)
end end
@ -324,12 +364,12 @@ describe('marktree', function()
end) end)
itp('works with intersections with a big tree', function() itp('works with intersections with a big tree', function()
local tree = ffi.new("MarkTree[1]") -- zero initialized by luajit local tree = ffi.new('MarkTree[1]') -- zero initialized by luajit
local ids = {} local ids = {}
for i = 1,1000 do for i = 1, 1000 do
table.insert(ids, put(tree, 1, i, false, 2, 1000-i, false)) table.insert(ids, put(tree, 1, i, false, 2, 1000 - i, false))
if i % 10 == 1 then if i % 10 == 1 then
check_intersections(tree) check_intersections(tree)
end end
@ -339,13 +379,13 @@ describe('marktree', function()
eq(2000, tree[0].n_keys) eq(2000, tree[0].n_keys)
ok(tree[0].root.level >= 2) ok(tree[0].root.level >= 2)
local iter = ffi.new("MarkTreeIter[1]") local iter = ffi.new('MarkTreeIter[1]')
local k = 0 local k = 0
for i = 1,20 do for i = 1, 20 do
for j = 1,50 do for j = 1, 50 do
k = k + 1 k = k + 1
local ival = (j-1)*20+i local ival = (j - 1) * 20 + i
if false == true then -- if there actually is a failure, this branch will fail out at the actual spot of the error if false == true then -- if there actually is a failure, this branch will fail out at the actual spot of the error
lib.marktree_lookup_ns(tree, ns, ids[ival], false, iter) lib.marktree_lookup_ns(tree, ns, ids[ival], false, iter)
lib.marktree_del_itr(tree, iter, false) lib.marktree_del_itr(tree, iter, false)
@ -367,10 +407,10 @@ describe('marktree', function()
end) end)
itp('works with intersections and marktree_splice', function() itp('works with intersections and marktree_splice', function()
local tree = ffi.new("MarkTree[1]") -- zero initialized by luajit local tree = ffi.new('MarkTree[1]') -- zero initialized by luajit
for i = 1,1000 do for i = 1, 1000 do
put(tree, 1, i, false, 2, 1000-i, false) put(tree, 1, i, false, 2, 1000 - i, false)
if i % 10 == 1 then if i % 10 == 1 then
check_intersections(tree) check_intersections(tree)
end end
@ -380,15 +420,15 @@ describe('marktree', function()
eq(2000, tree[0].n_keys) eq(2000, tree[0].n_keys)
ok(tree[0].root.level >= 2) ok(tree[0].root.level >= 2)
for _ = 1,10 do for _ = 1, 10 do
lib.marktree_splice(tree, 0, 0, 0, 100, 0, 0) lib.marktree_splice(tree, 0, 0, 0, 100, 0, 0)
check_intersections(tree) check_intersections(tree)
end end
end) end)
itp('marktree_move should preserve key order', function() itp('marktree_move should preserve key order', function()
local tree = ffi.new("MarkTree[1]") -- zero initialized by luajit local tree = ffi.new('MarkTree[1]') -- zero initialized by luajit
local iter = ffi.new("MarkTreeIter[1]") local iter = ffi.new('MarkTreeIter[1]')
local ids = {} local ids = {}
-- new index and old index look the same, but still have to move because -- new index and old index look the same, but still have to move because
@ -405,31 +445,30 @@ describe('marktree', function()
end) end)
itp('works with intersections and marktree_move', function() itp('works with intersections and marktree_move', function()
local tree = ffi.new("MarkTree[1]") -- zero initialized by luajit local tree = ffi.new('MarkTree[1]') -- zero initialized by luajit
local ids = {} local ids = {}
for i = 1,1000 do for i = 1, 1000 do
table.insert(ids, put(tree, 1, i, false, 2, 1000-i, false)) table.insert(ids, put(tree, 1, i, false, 2, 1000 - i, false))
if i % 10 == 1 then if i % 10 == 1 then
check_intersections(tree) check_intersections(tree)
end end
end end
local iter = ffi.new("MarkTreeIter[1]") local iter = ffi.new('MarkTreeIter[1]')
for i = 1,1000 do for i = 1, 1000 do
local which = i%2 local which = i % 2
lib.marktree_lookup_ns(tree, ns, ids[i], which, iter) lib.marktree_lookup_ns(tree, ns, ids[i], which, iter)
lib.marktree_move(tree, iter, 1+which, 500+i) lib.marktree_move(tree, iter, 1 + which, 500 + i)
if i % 10 == 1 then if i % 10 == 1 then
check_intersections(tree) check_intersections(tree)
end end
end end
end) end)
itp('works with intersections with a even bigger tree', function() itp('works with intersections with a even bigger tree', function()
local tree = ffi.new("MarkTree[1]") -- zero initialized by luajit local tree = ffi.new('MarkTree[1]') -- zero initialized by luajit
local ids = {} local ids = {}
@ -441,21 +480,21 @@ describe('marktree', function()
at_row[i] = {} at_row[i] = {}
end end
local size = 1000*size_factor local size = 1000 * size_factor
local k = 1 local k = 1
while k <= size do while k <= size do
for row1 = 1,9 do for row1 = 1, 9 do
for row2 = row1,10 do -- note row2 can be == row1, leads to empty ranges being tested when k > size/2 for row2 = row1, 10 do -- note row2 can be == row1, leads to empty ranges being tested when k > size/2
if k > size then if k > size then
break break
end end
local id = put(tree, row1, k, false, row2, size-k, false) local id = put(tree, row1, k, false, row2, size - k, false)
table.insert(ids, id) table.insert(ids, id)
for i = row1+1, row2 do for i = row1 + 1, row2 do
table.insert(at_row[i], id) table.insert(at_row[i], id)
end end
--if tree[0].root.level == 4 then error("kk"..k) end --if tree[0].root.level == 4 then error("kk"..k) end
if k % 100*size_factor == 1 or (k < 2000 and k%100 == 1) then if k % 100 * size_factor == 1 or (k < 2000 and k % 100 == 1) then
check_intersections(tree) check_intersections(tree)
end end
k = k + 1 k = k + 1
@ -463,13 +502,13 @@ describe('marktree', function()
end end
end end
eq(2*size, tree[0].n_keys) eq(2 * size, tree[0].n_keys)
ok(tree[0].root.level >= 3) ok(tree[0].root.level >= 3)
check_intersections(tree) check_intersections(tree)
local iter = ffi.new("MarkTreeIter[1]") local iter = ffi.new('MarkTreeIter[1]')
local pair = ffi.new("MTPair[1]") local pair = ffi.new('MTPair[1]')
for i = 1,10 do for i = 1, 10 do
-- use array as set and not {[id]=true} map, to detect duplicates -- use array as set and not {[id]=true} map, to detect duplicates
local set = {} local set = {}
eq(true, ffi.C.marktree_itr_get_overlap(tree, i, 0, iter)) eq(true, ffi.C.marktree_itr_get_overlap(tree, i, 0, iter))
@ -482,14 +521,14 @@ describe('marktree', function()
end end
k = 0 k = 0
for i = 1,100 do for i = 1, 100 do
for j = 1,(10*size_factor) do for j = 1, (10 * size_factor) do
k = k + 1 k = k + 1
local ival = (j-1)*100+i local ival = (j - 1) * 100 + i
lib.marktree_del_pair_test(tree, ns, ids[ival]) lib.marktree_del_pair_test(tree, ns, ids[ival])
-- just a few stickprov, if there is trouble we need to check -- just a few stickprov, if there is trouble we need to check
-- everyone using the code in the "big tree" case above -- everyone using the code in the "big tree" case above
if k % 100*size_factor == 0 or (k > 3000 and k % 200 == 0) then if k % 100 * size_factor == 0 or (k > 3000 and k % 200 == 0) then
check_intersections(tree) check_intersections(tree)
end end
end end
@ -499,7 +538,7 @@ describe('marktree', function()
end) end)
itp('works with intersections with a even bigger tree and splice', function() itp('works with intersections with a even bigger tree and splice', function()
local tree = ffi.new("MarkTree[1]") -- zero initialized by luajit local tree = ffi.new('MarkTree[1]') -- zero initialized by luajit
-- too much overhead on ASAN -- too much overhead on ASAN
local size_factor = helpers.is_asan() and 3 or 10 local size_factor = helpers.is_asan() and 3 or 10
@ -509,20 +548,20 @@ describe('marktree', function()
at_row[i] = {} at_row[i] = {}
end end
local size = 1000*size_factor local size = 1000 * size_factor
local k = 1 local k = 1
while k <= size do while k <= size do
for row1 = 1,9 do for row1 = 1, 9 do
for row2 = row1,10 do -- note row2 can be == row1, leads to empty ranges being tested when k > size/2 for row2 = row1, 10 do -- note row2 can be == row1, leads to empty ranges being tested when k > size/2
if k > size then if k > size then
break break
end end
local id = put(tree, row1, k, false, row2, size-k, false) local id = put(tree, row1, k, false, row2, size - k, false)
for i = row1+1, row2 do for i = row1 + 1, row2 do
table.insert(at_row[i], id) table.insert(at_row[i], id)
end end
--if tree[0].root.level == 4 then error("kk"..k) end --if tree[0].root.level == 4 then error("kk"..k) end
if k % 100*size_factor == 1 or (k < 2000 and k%100 == 1) then if k % 100 * size_factor == 1 or (k < 2000 and k % 100 == 1) then
check_intersections(tree) check_intersections(tree)
end end
k = k + 1 k = k + 1
@ -530,11 +569,11 @@ describe('marktree', function()
end end
end end
eq(2*size, tree[0].n_keys) eq(2 * size, tree[0].n_keys)
ok(tree[0].root.level >= 3) ok(tree[0].root.level >= 3)
check_intersections(tree) check_intersections(tree)
for _ = 1,10 do for _ = 1, 10 do
for j = 3, 8 do for j = 3, 8 do
lib.marktree_splice(tree, j, 0, 0, 200, 0, 0) lib.marktree_splice(tree, j, 0, 0, 200, 0, 0)
check_intersections(tree) check_intersections(tree)

View File

@ -1,8 +1,8 @@
local helpers = require("test.unit.helpers")(after_each) local helpers = require('test.unit.helpers')(after_each)
local itp = helpers.gen_itp(it) local itp = helpers.gen_itp(it)
local ffi = helpers.ffi local ffi = helpers.ffi
local eq = helpers.eq local eq = helpers.eq
local lib = helpers.cimport('./src/nvim/mbyte.h', './src/nvim/charset.h', './src/nvim/grid.h') local lib = helpers.cimport('./src/nvim/mbyte.h', './src/nvim/charset.h', './src/nvim/grid.h')
@ -16,13 +16,12 @@ describe('mbyte', function()
return table.concat(s) return table.concat(s)
end end
before_each(function() before_each(function() end)
end)
itp('utf_ptr2char', function() itp('utf_ptr2char', function()
-- For strings with length 1 the first byte is returned. -- For strings with length 1 the first byte is returned.
for c = 0, 255 do for c = 0, 255 do
eq(c, lib.utf_ptr2char(to_string({c, 0}))) eq(c, lib.utf_ptr2char(to_string({ c, 0 })))
end end
-- Some ill formed byte sequences that should not be recognized as UTF-8 -- Some ill formed byte sequences that should not be recognized as UTF-8
@ -48,126 +47,160 @@ describe('mbyte', function()
describe('utfc_ptr2schar_len', function() describe('utfc_ptr2schar_len', function()
local function test_seq(seq) local function test_seq(seq)
local firstc = ffi.new("int[1]") local firstc = ffi.new('int[1]')
local buf = ffi.new("char[32]") local buf = ffi.new('char[32]')
lib.schar_get(buf, lib.utfc_ptr2schar_len(to_string(seq), #seq, firstc)) lib.schar_get(buf, lib.utfc_ptr2schar_len(to_string(seq), #seq, firstc))
return {ffi.string(buf), firstc[0]} return { ffi.string(buf), firstc[0] }
end end
local function byte(val) local function byte(val)
return {string.char(val), val} return { string.char(val), val }
end end
itp('1-byte sequences', function() itp('1-byte sequences', function()
eq({'', 0}, test_seq{0}) eq({ '', 0 }, test_seq { 0 })
for c = 1, 127 do for c = 1, 127 do
eq(byte(c), test_seq{c}) eq(byte(c), test_seq { c })
end end
for c = 128, 255 do for c = 128, 255 do
eq({'', c}, test_seq{c}) eq({ '', c }, test_seq { c })
end end
end) end)
itp('2-byte sequences', function() itp('2-byte sequences', function()
-- No combining characters -- No combining characters
eq(byte(0x7f), test_seq{0x7f, 0x7f}) eq(byte(0x7f), test_seq { 0x7f, 0x7f })
-- No combining characters -- No combining characters
eq(byte(0x7f), test_seq{0x7f, 0x80}) eq(byte(0x7f), test_seq { 0x7f, 0x80 })
-- No UTF-8 sequence -- No UTF-8 sequence
eq({'', 0xc2}, test_seq{0xc2, 0x7f}) eq({ '', 0xc2 }, test_seq { 0xc2, 0x7f })
-- One UTF-8 character -- One UTF-8 character
eq({'\xc2\x80', 0x80}, test_seq{0xc2, 0x80}) eq({ '\xc2\x80', 0x80 }, test_seq { 0xc2, 0x80 })
-- No UTF-8 sequence -- No UTF-8 sequence
eq({'', 0xc2}, test_seq{0xc2, 0xc0}) eq({ '', 0xc2 }, test_seq { 0xc2, 0xc0 })
end) end)
itp('3-byte sequences', function() itp('3-byte sequences', function()
-- No second UTF-8 character -- No second UTF-8 character
eq(byte(0x7f), test_seq{0x7f, 0x80, 0x80}) eq(byte(0x7f), test_seq { 0x7f, 0x80, 0x80 })
-- No combining character -- No combining character
eq(byte(0x7f), test_seq{0x7f, 0xc2, 0x80}) eq(byte(0x7f), test_seq { 0x7f, 0xc2, 0x80 })
-- Combining character is U+0300 -- Combining character is U+0300
eq({"\x7f\xcc\x80", 0x7f}, test_seq{0x7f, 0xcc, 0x80}) eq({ '\x7f\xcc\x80', 0x7f }, test_seq { 0x7f, 0xcc, 0x80 })
-- No UTF-8 sequence -- No UTF-8 sequence
eq({'', 0xc2}, test_seq{0xc2, 0x7f, 0xcc}) eq({ '', 0xc2 }, test_seq { 0xc2, 0x7f, 0xcc })
-- Incomplete combining character -- Incomplete combining character
eq({"\xc2\x80", 0x80}, test_seq{0xc2, 0x80, 0xcc}) eq({ '\xc2\x80', 0x80 }, test_seq { 0xc2, 0x80, 0xcc })
-- One UTF-8 character (composing only) -- One UTF-8 character (composing only)
eq({" \xe2\x83\x90", 0x20d0}, test_seq{0xe2, 0x83, 0x90}) eq({ ' \xe2\x83\x90', 0x20d0 }, test_seq { 0xe2, 0x83, 0x90 })
end) end)
itp('4-byte sequences', function() itp('4-byte sequences', function()
-- No following combining character -- No following combining character
eq(byte(0x7f), test_seq{0x7f, 0x7f, 0xcc, 0x80}) eq(byte(0x7f), test_seq { 0x7f, 0x7f, 0xcc, 0x80 })
-- No second UTF-8 character -- No second UTF-8 character
eq(byte(0x7f), test_seq{0x7f, 0xc2, 0xcc, 0x80}) eq(byte(0x7f), test_seq { 0x7f, 0xc2, 0xcc, 0x80 })
-- Combining character U+0300 -- Combining character U+0300
eq({"\x7f\xcc\x80", 0x7f}, test_seq{0x7f, 0xcc, 0x80, 0xcc}) eq({ '\x7f\xcc\x80', 0x7f }, test_seq { 0x7f, 0xcc, 0x80, 0xcc })
-- No UTF-8 sequence -- No UTF-8 sequence
eq({'', 0xc2}, test_seq{0xc2, 0x7f, 0xcc, 0x80}) eq({ '', 0xc2 }, test_seq { 0xc2, 0x7f, 0xcc, 0x80 })
-- No following UTF-8 character -- No following UTF-8 character
eq({"\xc2\x80", 0x80}, test_seq{0xc2, 0x80, 0xcc, 0xcc}) eq({ '\xc2\x80', 0x80 }, test_seq { 0xc2, 0x80, 0xcc, 0xcc })
-- Combining character U+0301 -- Combining character U+0301
eq({"\xc2\x80\xcc\x81", 0x80}, test_seq{0xc2, 0x80, 0xcc, 0x81}) eq({ '\xc2\x80\xcc\x81', 0x80 }, test_seq { 0xc2, 0x80, 0xcc, 0x81 })
-- One UTF-8 character -- One UTF-8 character
eq({"\xf4\x80\x80\x80", 0x100000}, test_seq{0xf4, 0x80, 0x80, 0x80}) eq({ '\xf4\x80\x80\x80', 0x100000 }, test_seq { 0xf4, 0x80, 0x80, 0x80 })
end) end)
itp('5+-byte sequences', function() itp('5+-byte sequences', function()
-- No following combining character -- No following combining character
eq(byte(0x7f), test_seq{0x7f, 0x7f, 0xcc, 0x80, 0x80}) eq(byte(0x7f), test_seq { 0x7f, 0x7f, 0xcc, 0x80, 0x80 })
-- No second UTF-8 character -- No second UTF-8 character
eq(byte(0x7f), test_seq{0x7f, 0xc2, 0xcc, 0x80, 0x80}) eq(byte(0x7f), test_seq { 0x7f, 0xc2, 0xcc, 0x80, 0x80 })
-- Combining character U+0300 -- Combining character U+0300
eq({"\x7f\xcc\x80", 0x7f}, test_seq{0x7f, 0xcc, 0x80, 0xcc, 0x00}) eq({ '\x7f\xcc\x80', 0x7f }, test_seq { 0x7f, 0xcc, 0x80, 0xcc, 0x00 })
-- Combining characters U+0300 and U+0301 -- Combining characters U+0300 and U+0301
eq({"\x7f\xcc\x80\xcc\x81", 0x7f}, test_seq{0x7f, 0xcc, 0x80, 0xcc, 0x81}) eq({ '\x7f\xcc\x80\xcc\x81', 0x7f }, test_seq { 0x7f, 0xcc, 0x80, 0xcc, 0x81 })
-- Combining characters U+0300, U+0301, U+0302 -- Combining characters U+0300, U+0301, U+0302
eq({"\x7f\xcc\x80\xcc\x81\xcc\x82", 0x7f}, test_seq{0x7f, 0xcc, 0x80, 0xcc, 0x81, 0xcc, 0x82}) eq(
{ '\x7f\xcc\x80\xcc\x81\xcc\x82', 0x7f },
test_seq { 0x7f, 0xcc, 0x80, 0xcc, 0x81, 0xcc, 0x82 }
)
-- Combining characters U+0300, U+0301, U+0302, U+0303 -- Combining characters U+0300, U+0301, U+0302, U+0303
eq({"\x7f\xcc\x80\xcc\x81\xcc\x82\xcc\x83", 0x7f}, test_seq{0x7f, 0xcc, 0x80, 0xcc, 0x81, 0xcc, 0x82, 0xcc, 0x83}) eq(
{ '\x7f\xcc\x80\xcc\x81\xcc\x82\xcc\x83', 0x7f },
test_seq { 0x7f, 0xcc, 0x80, 0xcc, 0x81, 0xcc, 0x82, 0xcc, 0x83 }
)
-- Combining characters U+0300, U+0301, U+0302, U+0303, U+0304 -- Combining characters U+0300, U+0301, U+0302, U+0303, U+0304
eq({"\x7f\xcc\x80\xcc\x81\xcc\x82\xcc\x83\xcc\x84", 0x7f}, test_seq{0x7f, 0xcc, 0x80, 0xcc, 0x81, 0xcc, 0x82, 0xcc, 0x83, 0xcc, 0x84}) eq(
{ '\x7f\xcc\x80\xcc\x81\xcc\x82\xcc\x83\xcc\x84', 0x7f },
test_seq { 0x7f, 0xcc, 0x80, 0xcc, 0x81, 0xcc, 0x82, 0xcc, 0x83, 0xcc, 0x84 }
)
-- Combining characters U+0300, U+0301, U+0302, U+0303, U+0304, U+0305 -- Combining characters U+0300, U+0301, U+0302, U+0303, U+0304, U+0305
eq({"\x7f\xcc\x80\xcc\x81\xcc\x82\xcc\x83\xcc\x84\xcc\x85", 0x7f}, test_seq{0x7f, 0xcc, 0x80, 0xcc, 0x81, 0xcc, 0x82, 0xcc, 0x83, 0xcc, 0x84, 0xcc, 0x85}) eq(
{ '\x7f\xcc\x80\xcc\x81\xcc\x82\xcc\x83\xcc\x84\xcc\x85', 0x7f },
test_seq { 0x7f, 0xcc, 0x80, 0xcc, 0x81, 0xcc, 0x82, 0xcc, 0x83, 0xcc, 0x84, 0xcc, 0x85 }
)
-- Combining characters U+0300, U+0301, U+0302, U+0303, U+0304, U+0305, U+0306 -- Combining characters U+0300, U+0301, U+0302, U+0303, U+0304, U+0305, U+0306
eq({"\x7f\xcc\x80\xcc\x81\xcc\x82\xcc\x83\xcc\x84\xcc\x85\xcc\x86", 0x7f}, test_seq{0x7f, 0xcc, 0x80, 0xcc, 0x81, 0xcc, 0x82, 0xcc, 0x83, 0xcc, 0x84, 0xcc, 0x85, 0xcc, 0x86}) eq(
{ '\x7f\xcc\x80\xcc\x81\xcc\x82\xcc\x83\xcc\x84\xcc\x85\xcc\x86', 0x7f },
test_seq {
0x7f,
0xcc,
0x80,
0xcc,
0x81,
0xcc,
0x82,
0xcc,
0x83,
0xcc,
0x84,
0xcc,
0x85,
0xcc,
0x86,
}
)
-- Only three following combining characters U+0300, U+0301, U+0302 -- Only three following combining characters U+0300, U+0301, U+0302
eq({"\x7f\xcc\x80\xcc\x81\xcc\x82", 0x7f}, test_seq{0x7f, 0xcc, 0x80, 0xcc, 0x81, 0xcc, 0x82, 0xc2, 0x80, 0xcc, 0x84, 0xcc, 0x85}) eq(
{ '\x7f\xcc\x80\xcc\x81\xcc\x82', 0x7f },
test_seq { 0x7f, 0xcc, 0x80, 0xcc, 0x81, 0xcc, 0x82, 0xc2, 0x80, 0xcc, 0x84, 0xcc, 0x85 }
)
-- No UTF-8 sequence -- No UTF-8 sequence
eq({'', 0xc2}, test_seq{0xc2, 0x7f, 0xcc, 0x80, 0x80}) eq({ '', 0xc2 }, test_seq { 0xc2, 0x7f, 0xcc, 0x80, 0x80 })
-- No following UTF-8 character -- No following UTF-8 character
eq({"\xc2\x80", 0x80}, test_seq{0xc2, 0x80, 0xcc, 0xcc, 0x80}) eq({ '\xc2\x80', 0x80 }, test_seq { 0xc2, 0x80, 0xcc, 0xcc, 0x80 })
-- Combining character U+0301 -- Combining character U+0301
eq({"\xc2\x80\xcc\x81", 0x80}, test_seq{0xc2, 0x80, 0xcc, 0x81, 0x7f}) eq({ '\xc2\x80\xcc\x81', 0x80 }, test_seq { 0xc2, 0x80, 0xcc, 0x81, 0x7f })
-- Combining character U+0301 -- Combining character U+0301
eq({"\xc2\x80\xcc\x81", 0x80}, test_seq{0xc2, 0x80, 0xcc, 0x81, 0xcc}) eq({ '\xc2\x80\xcc\x81', 0x80 }, test_seq { 0xc2, 0x80, 0xcc, 0x81, 0xcc })
-- One UTF-8 character -- One UTF-8 character
eq({"\xf4\x80\x80\x80", 0x100000}, test_seq{0xf4, 0x80, 0x80, 0x80, 0x7f}) eq({ '\xf4\x80\x80\x80', 0x100000 }, test_seq { 0xf4, 0x80, 0x80, 0x80, 0x7f })
-- One UTF-8 character -- One UTF-8 character
eq({"\xf4\x80\x80\x80", 0x100000}, test_seq{0xf4, 0x80, 0x80, 0x80, 0x80}) eq({ '\xf4\x80\x80\x80', 0x100000 }, test_seq { 0xf4, 0x80, 0x80, 0x80, 0x80 })
-- One UTF-8 character -- One UTF-8 character
eq({"\xf4\x80\x80\x80", 0x100000}, test_seq{0xf4, 0x80, 0x80, 0x80, 0xcc}) eq({ '\xf4\x80\x80\x80', 0x100000 }, test_seq { 0xf4, 0x80, 0x80, 0x80, 0xcc })
-- Combining characters U+1AB0 and U+0301 -- Combining characters U+1AB0 and U+0301
eq({"\xf4\x80\x80\x80\xe1\xaa\xb0\xcc\x81", 0x100000}, test_seq{0xf4, 0x80, 0x80, 0x80, 0xe1, 0xaa, 0xb0, 0xcc, 0x81}) eq(
{ '\xf4\x80\x80\x80\xe1\xaa\xb0\xcc\x81', 0x100000 },
test_seq { 0xf4, 0x80, 0x80, 0x80, 0xe1, 0xaa, 0xb0, 0xcc, 0x81 }
)
end) end)
end) end)
end) end)

View File

@ -1,4 +1,4 @@
local helpers = require("test.unit.helpers")(after_each) local helpers = require('test.unit.helpers')(after_each)
local itp = helpers.gen_itp(it) local itp = helpers.gen_itp(it)
local cimport = helpers.cimport local cimport = helpers.cimport
@ -11,7 +11,7 @@ local cimp = cimport('stdlib.h', './src/nvim/memory.h')
describe('xstrlcat()', function() describe('xstrlcat()', function()
local function test_xstrlcat(dst, src, dsize) local function test_xstrlcat(dst, src, dsize)
assert.is_true(dsize >= 1 + string.len(dst)) -- sanity check for tests assert.is_true(dsize >= 1 + string.len(dst)) -- sanity check for tests
local dst_cstr = cstr(dsize, dst) local dst_cstr = cstr(dsize, dst)
local src_cstr = to_cstr(src) local src_cstr = to_cstr(src)
eq(string.len(dst .. src), cimp.xstrlcat(dst_cstr, src_cstr, dsize)) eq(string.len(dst .. src), cimp.xstrlcat(dst_cstr, src_cstr, dsize))
@ -19,34 +19,32 @@ describe('xstrlcat()', function()
end end
local function test_xstrlcat_overlap(dst, src_idx, dsize) local function test_xstrlcat_overlap(dst, src_idx, dsize)
assert.is_true(dsize >= 1 + string.len(dst)) -- sanity check for tests assert.is_true(dsize >= 1 + string.len(dst)) -- sanity check for tests
local dst_cstr = cstr(dsize, dst) local dst_cstr = cstr(dsize, dst)
local src_cstr = dst_cstr + src_idx -- pointer into `dst` (overlaps) local src_cstr = dst_cstr + src_idx -- pointer into `dst` (overlaps)
eq(string.len(dst) + string.len(dst) - src_idx, eq(string.len(dst) + string.len(dst) - src_idx, cimp.xstrlcat(dst_cstr, src_cstr, dsize))
cimp.xstrlcat(dst_cstr, src_cstr, dsize))
return ffi.string(dst_cstr) return ffi.string(dst_cstr)
end end
itp('concatenates strings', function() itp('concatenates strings', function()
eq('ab', test_xstrlcat('a', 'b', 3)) eq('ab', test_xstrlcat('a', 'b', 3))
eq('ab', test_xstrlcat('a', 'b', 4096)) eq('ab', test_xstrlcat('a', 'b', 4096))
eq('ABCיהZdefgiיהZ', test_xstrlcat('ABCיהZ', 'defgiיהZ', 4096)) eq('ABCיהZdefgiיהZ', test_xstrlcat('ABCיהZ', 'defgiיהZ', 4096))
eq('b', test_xstrlcat('', 'b', 4096)) eq('b', test_xstrlcat('', 'b', 4096))
eq('a', test_xstrlcat('a', '', 4096)) eq('a', test_xstrlcat('a', '', 4096))
end) end)
itp('concatenates overlapping strings', function() itp('concatenates overlapping strings', function()
eq('abcabc', test_xstrlcat_overlap('abc', 0, 7)) eq('abcabc', test_xstrlcat_overlap('abc', 0, 7))
eq('abca', test_xstrlcat_overlap('abc', 0, 5)) eq('abca', test_xstrlcat_overlap('abc', 0, 5))
eq('abcb', test_xstrlcat_overlap('abc', 1, 5)) eq('abcb', test_xstrlcat_overlap('abc', 1, 5))
eq('abcc', test_xstrlcat_overlap('abc', 2, 10)) eq('abcc', test_xstrlcat_overlap('abc', 2, 10))
eq('abcabc', test_xstrlcat_overlap('abc', 0, 2343)) eq('abcabc', test_xstrlcat_overlap('abc', 0, 2343))
end) end)
itp('truncates if `dsize` is too small', function() itp('truncates if `dsize` is too small', function()
eq('a', test_xstrlcat('a', 'b', 2)) eq('a', test_xstrlcat('a', 'b', 2))
eq('', test_xstrlcat('', 'b', 1)) eq('', test_xstrlcat('', 'b', 1))
eq('ABCיהZd', test_xstrlcat('ABCיהZ', 'defgiיהZ', 10)) eq('ABCיהZd', test_xstrlcat('ABCיהZ', 'defgiיהZ', 10))
end) end)
end) end)

View File

@ -1,12 +1,11 @@
local helpers = require("test.unit.helpers")(after_each) local helpers = require('test.unit.helpers')(after_each)
local itp = helpers.gen_itp(it) local itp = helpers.gen_itp(it)
local ffi = helpers.ffi local ffi = helpers.ffi
local eq = helpers.eq local eq = helpers.eq
local to_cstr = helpers.to_cstr local to_cstr = helpers.to_cstr
local cimp = helpers.cimport('./src/nvim/message.h', './src/nvim/memory.h', local cimp = helpers.cimport('./src/nvim/message.h', './src/nvim/memory.h', './src/nvim/strings.h')
'./src/nvim/strings.h')
describe('trunc_string', function() describe('trunc_string', function()
local buflen = 40 local buflen = 40
@ -34,8 +33,8 @@ describe('trunc_string', function()
{ ['desc'] = 'by copy', ['func'] = test_copy }, { ['desc'] = 'by copy', ['func'] = test_copy },
} }
for _,t in ipairs(permutations) do for _, t in ipairs(permutations) do
describe('populates buf '..t.desc, function() describe('populates buf ' .. t.desc, function()
itp('with a small string', function() itp('with a small string', function()
t.func('text', 'text') t.func('text', 'text')
end) end)

View File

@ -35,32 +35,36 @@ end
describe('msgpack', function() describe('msgpack', function()
describe('unpacker', function() describe('unpacker', function()
itp('does not crash when paused between `cells` and `wrap` params of `grid_line` #25184', function() itp(
-- [kMessageTypeNotification, "redraw", [ 'does not crash when paused between `cells` and `wrap` params of `grid_line` #25184',
-- ["grid_line", function()
-- [2, 0, 0, [[" " , 0, 77]], false] -- [kMessageTypeNotification, "redraw", [
-- ] -- ["grid_line",
-- ]] -- [2, 0, 0, [[" " , 0, 77]], false]
local payload = -- ]
'\x93\x02\xa6\x72\x65\x64\x72\x61\x77\x91\x92\xa9\x67\x72\x69\x64\x5f\x6c\x69\x6e\x65\x95\x02\x00\x00\x91\x93\xa1\x20\x00\x4d\xc2' -- ]]
local payload =
'\x93\x02\xa6\x72\x65\x64\x72\x61\x77\x91\x92\xa9\x67\x72\x69\x64\x5f\x6c\x69\x6e\x65\x95\x02\x00\x00\x91\x93\xa1\x20\x00\x4d\xc2'
local unpacker = make_unpacker() local unpacker = make_unpacker()
lib.unpacker_init(unpacker) lib.unpacker_init(unpacker)
unpacker_goto(unpacker, payload, payload:len() - 1) unpacker_goto(unpacker, payload, payload:len() - 1)
local finished = unpacker_advance(unpacker) local finished = unpacker_advance(unpacker)
eq(finished, false) eq(finished, false)
unpacker[0].read_size = unpacker[0].read_size + 1 unpacker[0].read_size = unpacker[0].read_size + 1
finished = unpacker_advance(unpacker) finished = unpacker_advance(unpacker)
eq(finished, true) eq(finished, true)
end) end
)
itp('does not crash when parsing grid_line event with 0 `cells` #25184', function() itp('does not crash when parsing grid_line event with 0 `cells` #25184', function()
local unpacker = make_unpacker() local unpacker = make_unpacker()
lib.unpacker_init(unpacker) lib.unpacker_init(unpacker)
unpacker_goto(unpacker, unpacker_goto(
unpacker,
-- [kMessageTypeNotification, "redraw", [ -- [kMessageTypeNotification, "redraw", [
-- ["grid_line", -- ["grid_line",
-- [2, 0, 0, [], false] -- [2, 0, 0, [], false]

View File

@ -1,4 +1,4 @@
local helpers = require("test.unit.helpers")(after_each) local helpers = require('test.unit.helpers')(after_each)
local itp = helpers.gen_itp(it) local itp = helpers.gen_itp(it)
local child_call_once = helpers.child_call_once local child_call_once = helpers.child_call_once
@ -6,9 +6,9 @@ local cimport = helpers.cimport
local ffi = helpers.ffi local ffi = helpers.ffi
local eq = helpers.eq local eq = helpers.eq
local multiqueue = cimport("./test/unit/fixtures/multiqueue.h") local multiqueue = cimport('./test/unit/fixtures/multiqueue.h')
describe("multiqueue (multi-level event-queue)", function() describe('multiqueue (multi-level event-queue)', function()
local parent, child1, child2, child3 local parent, child1, child2, child3
local function put(q, str) local function put(q, str)

View File

@ -1,28 +1,27 @@
local helpers = require("test.unit.helpers")(after_each) local helpers = require('test.unit.helpers')(after_each)
local itp = helpers.gen_itp(it) local itp = helpers.gen_itp(it)
local to_cstr = helpers.to_cstr local to_cstr = helpers.to_cstr
local eq = helpers.eq local eq = helpers.eq
local optionstr = helpers.cimport("./src/nvim/optionstr.h") local optionstr = helpers.cimport('./src/nvim/optionstr.h')
local check_ff_value = function(ff) local check_ff_value = function(ff)
return optionstr.check_ff_value(to_cstr(ff)) return optionstr.check_ff_value(to_cstr(ff))
end end
describe('check_ff_value', function() describe('check_ff_value', function()
itp('views empty string as valid', function() itp('views empty string as valid', function()
eq(1, check_ff_value("")) eq(1, check_ff_value(''))
end) end)
itp('views "unix", "dos" and "mac" as valid', function() itp('views "unix", "dos" and "mac" as valid', function()
eq(1, check_ff_value("unix")) eq(1, check_ff_value('unix'))
eq(1, check_ff_value("dos")) eq(1, check_ff_value('dos'))
eq(1, check_ff_value("mac")) eq(1, check_ff_value('mac'))
end) end)
itp('views "foo" as invalid', function() itp('views "foo" as invalid', function()
eq(0, check_ff_value("foo")) eq(0, check_ff_value('foo'))
end) end)
end) end)

View File

@ -62,7 +62,7 @@ describe('env.c', function()
eq('non-empty', os.getenv(name)) eq('non-empty', os.getenv(name))
end) end)
itp("`overwrite` behavior", function() itp('`overwrite` behavior', function()
local name = 'NVIM_UNIT_TEST_SETENV_2N' local name = 'NVIM_UNIT_TEST_SETENV_2N'
local value = 'NVIM_UNIT_TEST_SETENV_2V' local value = 'NVIM_UNIT_TEST_SETENV_2V'
local value_updated = 'NVIM_UNIT_TEST_SETENV_2V_UPDATED' local value_updated = 'NVIM_UNIT_TEST_SETENV_2V_UPDATED'
@ -79,7 +79,7 @@ describe('env.c', function()
itp('appends :/foo/bar to $PATH', function() itp('appends :/foo/bar to $PATH', function()
local original_path = os.getenv('PATH') local original_path = os.getenv('PATH')
eq(true, cimp.os_setenv_append_path(to_cstr('/foo/bar/baz.exe'))) eq(true, cimp.os_setenv_append_path(to_cstr('/foo/bar/baz.exe')))
eq(original_path..':/foo/bar', os.getenv('PATH')) eq(original_path .. ':/foo/bar', os.getenv('PATH'))
end) end)
itp('avoids redundant separator when appending to $PATH #7377', function() itp('avoids redundant separator when appending to $PATH #7377', function()
@ -166,7 +166,7 @@ describe('env.c', function()
local test_value = 'NVIM_UNIT_TEST_GETENVNAME_AT_INDEX_1V' local test_value = 'NVIM_UNIT_TEST_GETENVNAME_AT_INDEX_1V'
os_setenv(test_name, test_value, 1) os_setenv(test_name, test_value, 1)
local i = 0 local i = 0
local names = { } local names = {}
local found_name = false local found_name = false
local name = cimp.os_getenvname_at_index(i) local name = cimp.os_getenvname_at_index(i)
while name ~= NULL do while name ~= NULL do
@ -245,7 +245,7 @@ describe('env.c', function()
local input = '~/foo ~ foo' local input = '~/foo ~ foo'
local homedir = cstr(255, '') local homedir = cstr(255, '')
cimp.expand_env_esc(to_cstr('~'), homedir, 255, false, true, NULL) cimp.expand_env_esc(to_cstr('~'), homedir, 255, false, true, NULL)
local output_expected = ffi.string(homedir) .. "/foo ~ foo" local output_expected = ffi.string(homedir) .. '/foo ~ foo'
local output = cstr(255, '') local output = cstr(255, '')
cimp.expand_env_esc(to_cstr(input), output, 255, false, true, NULL) cimp.expand_env_esc(to_cstr(input), output, 255, false, true, NULL)
eq(ffi.string(output), ffi.string(output_expected)) eq(ffi.string(output), ffi.string(output_expected))
@ -256,7 +256,7 @@ describe('env.c', function()
local dst = cstr(255, '') local dst = cstr(255, '')
cimp.expand_env_esc(to_cstr('~'), dst, 255, false, true, NULL) cimp.expand_env_esc(to_cstr('~'), dst, 255, false, true, NULL)
local homedir = ffi.string(dst) local homedir = ffi.string(dst)
local output_expected = homedir .. "/foo " .. homedir .. " foo" local output_expected = homedir .. '/foo ' .. homedir .. ' foo'
local output = cstr(255, '') local output = cstr(255, '')
cimp.expand_env_esc(input, output, 255, false, false, NULL) cimp.expand_env_esc(input, output, 255, false, false, NULL)
eq(output_expected, ffi.string(output)) eq(output_expected, ffi.string(output))
@ -267,8 +267,9 @@ describe('env.c', function()
cimp.os_get_username(name_out, 100) cimp.os_get_username(name_out, 100)
local curuser = ffi.string(name_out) local curuser = ffi.string(name_out)
local src = to_cstr("~"..curuser.."/Vcs/django-rest-framework/rest_framework/renderers.py") local src =
local dst = cstr(256, "~"..curuser) to_cstr('~' .. curuser .. '/Vcs/django-rest-framework/rest_framework/renderers.py')
local dst = cstr(256, '~' .. curuser)
cimp.expand_env_esc(src, dst, 256, false, false, NULL) cimp.expand_env_esc(src, dst, 256, false, false, NULL)
local len = string.len(ffi.string(dst)) local len = string.len(ffi.string(dst))
assert.True(len > 56) assert.True(len > 56)
@ -283,7 +284,7 @@ describe('env.c', function()
cimp.expand_env_esc(input, output, 5, false, true, NULL) cimp.expand_env_esc(input, output, 5, false, true, NULL)
-- Make sure the first few characters are copied properly and that there is a -- Make sure the first few characters are copied properly and that there is a
-- terminating null character -- terminating null character
for i=0,3 do for i = 0, 3 do
eq(input[i], output[i]) eq(input[i], output[i])
end end
eq(0, output[4]) eq(0, output[4])
@ -304,7 +305,7 @@ describe('env.c', function()
-- terminating null character -- terminating null character
-- expand_env_esc SHOULD NOT expand the variable if there is not enough space to -- expand_env_esc SHOULD NOT expand the variable if there is not enough space to
-- contain the result -- contain the result
for i=0,3 do for i = 0, 3 do
eq(output[i], input[i]) eq(output[i], input[i])
end end
eq(output[4], 0) eq(output[4], 0)

View File

@ -26,7 +26,7 @@ local linkb = dir .. '/broken.lnk'
local filec = dir .. '/created-file.dat' local filec = dir .. '/created-file.dat'
before_each(function() before_each(function()
mkdir(dir); mkdir(dir)
local f1 = io.open(file1, 'w') local f1 = io.open(file1, 'w')
f1:write(fcontents) f1:write(fcontents)
@ -56,7 +56,7 @@ local function file_open(fname, flags, mode)
end end
local function file_open_new(fname, flags, mode) local function file_open_new(fname, flags, mode)
local ret1 = ffi.new('int[?]', 1, {0}) local ret1 = ffi.new('int[?]', 1, { 0 })
local ret2 = ffi.gc(m.file_open_new(ret1, fname, flags, mode), nil) local ret2 = ffi.gc(m.file_open_new(ret1, fname, flags, mode), nil)
return ret1[0], ret2 return ret1[0], ret2
end end
@ -68,7 +68,7 @@ local function file_open_fd(fd, flags)
end end
local function file_open_fd_new(fd, flags) local function file_open_fd_new(fd, flags)
local ret1 = ffi.new('int[?]', 1, {0}) local ret1 = ffi.new('int[?]', 1, { 0 })
local ret2 = ffi.gc(m.file_open_fd_new(ret1, fd, flags), nil) local ret2 = ffi.gc(m.file_open_fd_new(ret1, fd, flags), nil)
return ret1[0], ret2 return ret1[0], ret2
end end
@ -116,7 +116,7 @@ describe('file_open_fd', function()
local fd = m.os_open(file1, m.kO_RDONLY, 0) local fd = m.os_open(file1, m.kO_RDONLY, 0)
local err, fp = file_open_fd(fd, m.kFileReadOnly) local err, fp = file_open_fd(fd, m.kFileReadOnly)
eq(0, err) eq(0, err)
eq({#fcontents, fcontents}, {file_read(fp, #fcontents)}) eq({ #fcontents, fcontents }, { file_read(fp, #fcontents) })
eq(0, m.file_close(fp, false)) eq(0, m.file_close(fp, false))
end) end)
itp('can use file descriptor returned by os_open for writing', function() itp('can use file descriptor returned by os_open for writing', function()
@ -136,7 +136,7 @@ describe('file_open_fd_new', function()
local fd = m.os_open(file1, m.kO_RDONLY, 0) local fd = m.os_open(file1, m.kO_RDONLY, 0)
local err, fp = file_open_fd_new(fd, m.kFileReadOnly) local err, fp = file_open_fd_new(fd, m.kFileReadOnly)
eq(0, err) eq(0, err)
eq({#fcontents, fcontents}, {file_read(fp, #fcontents)}) eq({ #fcontents, fcontents }, { file_read(fp, #fcontents) })
eq(0, m.file_free(fp, false)) eq(0, m.file_free(fp, false))
end) end)
itp('can use file descriptor returned by os_open for writing', function() itp('can use file descriptor returned by os_open for writing', function()
@ -193,7 +193,9 @@ describe('file_open', function()
local err, _ = file_open(linkf, m.kFileNoSymlink, 384) local err, _ = file_open(linkf, m.kFileNoSymlink, 384)
-- err is UV_EMLINK in FreeBSD, but if I use `ok(err == m.UV_ELOOP or err == -- err is UV_EMLINK in FreeBSD, but if I use `ok(err == m.UV_ELOOP or err ==
-- m.UV_EMLINK)`, then I loose the ability to see actual `err` value. -- m.UV_EMLINK)`, then I loose the ability to see actual `err` value.
if err ~= m.UV_ELOOP then eq(m.UV_EMLINK, err) end if err ~= m.UV_ELOOP then
eq(m.UV_EMLINK, err)
end
end) end)
itp('can open an existing file write-only with kFileCreate', function() itp('can open an existing file write-only with kFileCreate', function()
@ -249,8 +251,7 @@ describe('file_open', function()
eq(nil, attrs) eq(nil, attrs)
end) end)
itp('can truncate an existing file with kFileTruncate when opening a symlink', itp('can truncate an existing file with kFileTruncate when opening a symlink', function()
function()
local err, fp = file_open(linkf, m.kFileTruncate, 384) local err, fp = file_open(linkf, m.kFileTruncate, 384)
eq(0, err) eq(0, err)
eq(true, fp.wr) eq(true, fp.wr)
@ -356,10 +357,9 @@ describe('file_read', function()
local exp_s = fcontents:sub(shift + 1, shift + size) local exp_s = fcontents:sub(shift + 1, shift + size)
if shift + size >= #fcontents then if shift + size >= #fcontents then
exp_err = #fcontents - shift exp_err = #fcontents - shift
exp_s = (fcontents:sub(shift + 1, shift + size) exp_s = (fcontents:sub(shift + 1, shift + size) .. (('\0'):rep(size - exp_err)))
.. (('\0'):rep(size - exp_err)))
end end
eq({exp_err, exp_s}, {file_read(fp, size)}) eq({ exp_err, exp_s }, { file_read(fp, size) })
shift = shift + size shift = shift + size
end end
eq(0, m.file_close(fp, false)) eq(0, m.file_close(fp, false))
@ -369,8 +369,8 @@ describe('file_read', function()
local err, fp = file_open(file1, 0, 384) local err, fp = file_open(file1, 0, 384)
eq(0, err) eq(0, err)
eq(false, fp.wr) eq(false, fp.wr)
eq({#fcontents, fcontents}, {file_read(fp, #fcontents)}) eq({ #fcontents, fcontents }, { file_read(fp, #fcontents) })
eq({0, ('\0'):rep(#fcontents)}, {file_read(fp, #fcontents)}) eq({ 0, ('\0'):rep(#fcontents) }, { file_read(fp, #fcontents) })
eq(0, m.file_close(fp, false)) eq(0, m.file_close(fp, false))
end) end)
@ -378,9 +378,8 @@ describe('file_read', function()
local err, fp = file_open(file1, 0, 384) local err, fp = file_open(file1, 0, 384)
eq(0, err) eq(0, err)
eq(false, fp.wr) eq(false, fp.wr)
eq({5, fcontents:sub(1, 5)}, {file_read(fp, 5)}) eq({ 5, fcontents:sub(1, 5) }, { file_read(fp, 5) })
eq({#fcontents - 5, fcontents:sub(6) .. (('\0'):rep(5))}, eq({ #fcontents - 5, fcontents:sub(6) .. (('\0'):rep(5)) }, { file_read(fp, #fcontents) })
{file_read(fp, #fcontents)})
eq(0, m.file_close(fp, false)) eq(0, m.file_close(fp, false))
end) end)
@ -395,10 +394,9 @@ describe('file_read', function()
local exp_s = fcontents:sub(shift + 1, shift + size) local exp_s = fcontents:sub(shift + 1, shift + size)
if shift + size >= #fcontents then if shift + size >= #fcontents then
exp_err = #fcontents - shift exp_err = #fcontents - shift
exp_s = (fcontents:sub(shift + 1, shift + size) exp_s = (fcontents:sub(shift + 1, shift + size) .. (('\0'):rep(size - exp_err)))
.. (('\0'):rep(size - exp_err)))
end end
eq({exp_err, exp_s}, {file_read(fp, size)}) eq({ exp_err, exp_s }, { file_read(fp, size) })
shift = shift + size shift = shift + size
end end
eq(0, m.file_close(fp, false)) eq(0, m.file_close(fp, false))

View File

@ -68,7 +68,7 @@ describe('fs.c', function()
end end
before_each(function() before_each(function()
mkdir('unit-test-directory'); mkdir('unit-test-directory')
io.open('unit-test-directory/test.file', 'w'):close() io.open('unit-test-directory/test.file', 'w'):close()
@ -115,8 +115,8 @@ describe('fs.c', function()
eq(OK, fs.os_dirname(expected_cwd, length)) eq(OK, fs.os_dirname(expected_cwd, length))
-- os_chdir returns 0 for success, not OK (1). -- os_chdir returns 0 for success, not OK (1).
neq(0, fs.os_chdir('~')) -- fail neq(0, fs.os_chdir('~')) -- fail
neq(0, fs.os_chdir('~/')) -- fail neq(0, fs.os_chdir('~/')) -- fail
eq(OK, fs.os_dirname(cwd, length)) eq(OK, fs.os_dirname(cwd, length))
-- CWD did not change. -- CWD did not change.
@ -284,31 +284,34 @@ describe('fs.c', function()
end) end)
-- Some systems may not have `id` utility. -- Some systems may not have `id` utility.
if (os.execute('id -G > /dev/null 2>&1') ~= 0) then if os.execute('id -G > /dev/null 2>&1') ~= 0 then
pending('skipped (missing `id` utility)', function() end) pending('skipped (missing `id` utility)', function() end)
else else
itp('owner of a file may change the group of the file to any group of which that owner is a member', function() itp(
local file_gid = luv.fs_stat(filename).gid 'owner of a file may change the group of the file to any group of which that owner is a member',
function()
local file_gid = luv.fs_stat(filename).gid
-- Gets ID of any group of which current user is a member except the -- Gets ID of any group of which current user is a member except the
-- group that owns the file. -- group that owns the file.
local id_fd = io.popen('id -G') local id_fd = io.popen('id -G')
local new_gid = id_fd:read('*n') local new_gid = id_fd:read('*n')
if (new_gid == file_gid) then if new_gid == file_gid then
new_gid = id_fd:read('*n') new_gid = id_fd:read('*n')
end end
id_fd:close() id_fd:close()
-- User can be a member of only one group. -- User can be a member of only one group.
-- In that case we can not perform this test. -- In that case we can not perform this test.
if new_gid then if new_gid then
eq(0, (os_fchown(filename, -1, new_gid))) eq(0, (os_fchown(filename, -1, new_gid)))
eq(new_gid, luv.fs_stat(filename).gid) eq(new_gid, luv.fs_stat(filename).gid)
end
end end
end) )
end end
if (ffi.os == 'Windows' or ffi.C.geteuid() == 0) then if ffi.os == 'Windows' or ffi.C.geteuid() == 0 then
pending('skipped (uv_fs_chown is no-op on Windows)', function() end) pending('skipped (uv_fs_chown is no-op on Windows)', function() end)
else else
itp('returns nonzero if process has not enough permissions', function() itp('returns nonzero if process has not enough permissions', function()
@ -318,7 +321,6 @@ describe('fs.c', function()
end end
end) end)
describe('os_file_is_readable', function() describe('os_file_is_readable', function()
itp('returns false if the file is not readable', function() itp('returns false if the file is not readable', function()
local perm = os_getperm('unit-test-directory/test.file') local perm = os_getperm('unit-test-directory/test.file')
@ -330,13 +332,11 @@ describe('fs.c', function()
end) end)
itp('returns false if the file does not exist', function() itp('returns false if the file does not exist', function()
eq(false, os_file_is_readable( eq(false, os_file_is_readable('unit-test-directory/what_are_you_smoking.gif'))
'unit-test-directory/what_are_you_smoking.gif'))
end) end)
itp('returns true if the file is readable', function() itp('returns true if the file is readable', function()
eq(true, os_file_is_readable( eq(true, os_file_is_readable('unit-test-directory/test.file'))
'unit-test-directory/test.file'))
end) end)
end) end)
@ -387,7 +387,7 @@ describe('fs.c', function()
else else
buf = ffi.new('char[?]', size + 1, ('\0'):rep(size)) buf = ffi.new('char[?]', size + 1, ('\0'):rep(size))
end end
local eof = ffi.new('bool[?]', 1, {true}) local eof = ffi.new('bool[?]', 1, { true })
local ret2 = fs.os_read(fd, eof, buf, size, false) local ret2 = fs.os_read(fd, eof, buf, size, false)
local ret1 = eof[0] local ret1 = eof[0]
local ret3 = '' local ret3 = ''
@ -400,16 +400,16 @@ describe('fs.c', function()
local bufs = {} local bufs = {}
for i, size in ipairs(sizes) do for i, size in ipairs(sizes) do
bufs[i] = { bufs[i] = {
iov_base=ffi.new('char[?]', size + 1, ('\0'):rep(size)), iov_base = ffi.new('char[?]', size + 1, ('\0'):rep(size)),
iov_len=size, iov_len = size,
} }
end end
local iov = ffi.new('struct iovec[?]', #sizes, bufs) local iov = ffi.new('struct iovec[?]', #sizes, bufs)
local eof = ffi.new('bool[?]', 1, {true}) local eof = ffi.new('bool[?]', 1, { true })
local ret2 = fs.os_readv(fd, eof, iov, #sizes, false) local ret2 = fs.os_readv(fd, eof, iov, #sizes, false)
local ret1 = eof[0] local ret1 = eof[0]
local ret3 = {} local ret3 = {}
for i = 1,#sizes do for i = 1, #sizes do
-- Warning: iov may not be used. -- Warning: iov may not be used.
ret3[i] = ffi.string(bufs[i].iov_base, bufs[i].iov_len) ret3[i] = ffi.string(bufs[i].iov_base, bufs[i].iov_len)
end end
@ -445,7 +445,7 @@ describe('fs.c', function()
eq(OK, (os_rename(test, not_exist))) eq(OK, (os_rename(test, not_exist)))
eq(false, (os_path_exists(test))) eq(false, (os_path_exists(test)))
eq(true, (os_path_exists(not_exist))) eq(true, (os_path_exists(not_exist)))
eq(OK, (os_rename(not_exist, test))) -- restore test file eq(OK, (os_rename(not_exist, test))) -- restore test file
end) end)
itp('fail if source file does not exist', function() itp('fail if source file does not exist', function()
@ -494,14 +494,19 @@ describe('fs.c', function()
local dup0 = fs.os_dup(0) local dup0 = fs.os_dup(0)
local dup1 = fs.os_dup(1) local dup1 = fs.os_dup(1)
local dup2 = fs.os_dup(2) local dup2 = fs.os_dup(2)
local tbl = {[0]=true, [1]=true, [2]=true, local tbl = {
[tonumber(dup0)]=true, [tonumber(dup1)]=true, [0] = true,
[tonumber(dup2)]=true} [1] = true,
[2] = true,
[tonumber(dup0)] = true,
[tonumber(dup1)] = true,
[tonumber(dup2)] = true,
}
local i = 0 local i = 0
for _, _ in pairs(tbl) do for _, _ in pairs(tbl) do
i = i + 1 i = i + 1
end end
eq(i, 6) -- All fds must be unique eq(i, 6) -- All fds must be unique
end) end)
end) end)
@ -522,12 +527,15 @@ describe('fs.c', function()
eq(ffi.C.UV_ENOENT, (os_open('non-existing-file', ffi.C.kO_RDWR, 0))) eq(ffi.C.UV_ENOENT, (os_open('non-existing-file', ffi.C.kO_RDWR, 0)))
end) end)
itp('returns non-negative for O_CREAT on a non-existing file which then can be closed', function() itp(
assert_file_does_not_exist(new_file) 'returns non-negative for O_CREAT on a non-existing file which then can be closed',
local fd = os_open(new_file, ffi.C.kO_CREAT, 0) function()
assert.is_true(0 <= fd) assert_file_does_not_exist(new_file)
eq(0, os_close(fd)) local fd = os_open(new_file, ffi.C.kO_CREAT, 0)
end) assert.is_true(0 <= fd)
eq(0, os_close(fd))
end
)
itp('returns non-negative for O_CREAT on a existing file which then can be closed', function() itp('returns non-negative for O_CREAT on a existing file which then can be closed', function()
assert_file_exists(existing_file) assert_file_exists(existing_file)
@ -544,7 +552,7 @@ describe('fs.c', function()
itp('sets `rwx` permissions for O_CREAT 700 which then can be closed', function() itp('sets `rwx` permissions for O_CREAT 700 which then can be closed', function()
assert_file_does_not_exist(new_file) assert_file_does_not_exist(new_file)
--create the file --create the file
local fd = os_open(new_file, ffi.C.kO_CREAT, tonumber("700", 8)) local fd = os_open(new_file, ffi.C.kO_CREAT, tonumber('700', 8))
--verify permissions --verify permissions
eq(33216, luv.fs_stat(new_file).mode) eq(33216, luv.fs_stat(new_file).mode)
eq(0, os_close(fd)) eq(0, os_close(fd))
@ -553,17 +561,20 @@ describe('fs.c', function()
itp('sets `rw` permissions for O_CREAT 600 which then can be closed', function() itp('sets `rw` permissions for O_CREAT 600 which then can be closed', function()
assert_file_does_not_exist(new_file) assert_file_does_not_exist(new_file)
--create the file --create the file
local fd = os_open(new_file, ffi.C.kO_CREAT, tonumber("600", 8)) local fd = os_open(new_file, ffi.C.kO_CREAT, tonumber('600', 8))
--verify permissions --verify permissions
eq(33152, luv.fs_stat(new_file).mode) eq(33152, luv.fs_stat(new_file).mode)
eq(0, os_close(fd)) eq(0, os_close(fd))
end) end)
itp('returns a non-negative file descriptor for an existing file which then can be closed', function() itp(
local fd = os_open(existing_file, ffi.C.kO_RDWR, 0) 'returns a non-negative file descriptor for an existing file which then can be closed',
assert.is_true(0 <= fd) function()
eq(0, os_close(fd)) local fd = os_open(existing_file, ffi.C.kO_RDWR, 0)
end) assert.is_true(0 <= fd)
eq(0, os_close(fd))
end
)
end) end)
describe('os_close', function() describe('os_close', function()
@ -589,43 +600,48 @@ describe('fs.c', function()
itp('can read zero bytes from a file', function() itp('can read zero bytes from a file', function()
local fd = os_open(file, ffi.C.kO_RDONLY, 0) local fd = os_open(file, ffi.C.kO_RDONLY, 0)
ok(fd >= 0) ok(fd >= 0)
eq({false, 0, ''}, {os_read(fd, nil)}) eq({ false, 0, '' }, { os_read(fd, nil) })
eq({false, 0, ''}, {os_read(fd, 0)}) eq({ false, 0, '' }, { os_read(fd, 0) })
eq(0, os_close(fd)) eq(0, os_close(fd))
end) end)
itp('can read from a file multiple times', function() itp('can read from a file multiple times', function()
local fd = os_open(file, ffi.C.kO_RDONLY, 0) local fd = os_open(file, ffi.C.kO_RDONLY, 0)
ok(fd >= 0) ok(fd >= 0)
eq({false, 2, '\000\001'}, {os_read(fd, 2)}) eq({ false, 2, '\000\001' }, { os_read(fd, 2) })
eq({false, 2, '\002\003'}, {os_read(fd, 2)}) eq({ false, 2, '\002\003' }, { os_read(fd, 2) })
eq(0, os_close(fd)) eq(0, os_close(fd))
end) end)
itp('can read the whole file at once and then report eof', function() itp('can read the whole file at once and then report eof', function()
local fd = os_open(file, ffi.C.kO_RDONLY, 0) local fd = os_open(file, ffi.C.kO_RDONLY, 0)
ok(fd >= 0) ok(fd >= 0)
eq({false, #fcontents, fcontents}, {os_read(fd, #fcontents)}) eq({ false, #fcontents, fcontents }, { os_read(fd, #fcontents) })
eq({true, 0, ('\0'):rep(#fcontents)}, {os_read(fd, #fcontents)}) eq({ true, 0, ('\0'):rep(#fcontents) }, { os_read(fd, #fcontents) })
eq(0, os_close(fd)) eq(0, os_close(fd))
end) end)
itp('can read the whole file in two calls, one partially', function() itp('can read the whole file in two calls, one partially', function()
local fd = os_open(file, ffi.C.kO_RDONLY, 0) local fd = os_open(file, ffi.C.kO_RDONLY, 0)
ok(fd >= 0) ok(fd >= 0)
eq({false, #fcontents * 3/4, fcontents:sub(1, #fcontents * 3/4)}, eq(
{os_read(fd, #fcontents * 3/4)}) { false, #fcontents * 3 / 4, fcontents:sub(1, #fcontents * 3 / 4) },
eq({true, { os_read(fd, #fcontents * 3 / 4) }
(#fcontents * 1/4), )
fcontents:sub(#fcontents * 3/4 + 1) .. ('\0'):rep(#fcontents * 2/4)}, eq({
{os_read(fd, #fcontents * 3/4)}) true,
(#fcontents * 1 / 4),
fcontents:sub(#fcontents * 3 / 4 + 1) .. ('\0'):rep(#fcontents * 2 / 4),
}, { os_read(fd, #fcontents * 3 / 4) })
eq(0, os_close(fd)) eq(0, os_close(fd))
end) end)
end) end)
describe('os_readv', function() describe('os_readv', function()
-- Function may be absent -- Function may be absent
if not pcall(function() return fs.os_readv end) then if not pcall(function()
return fs.os_readv
end) then
return return
end end
local file = 'test-unit-os-fs_spec-os_readv.dat' local file = 'test-unit-os-fs_spec-os_readv.dat'
@ -643,45 +659,53 @@ describe('fs.c', function()
itp('can read zero bytes from a file', function() itp('can read zero bytes from a file', function()
local fd = os_open(file, ffi.C.kO_RDONLY, 0) local fd = os_open(file, ffi.C.kO_RDONLY, 0)
ok(fd >= 0) ok(fd >= 0)
eq({false, 0, {}}, {os_readv(fd, {})}) eq({ false, 0, {} }, { os_readv(fd, {}) })
eq({false, 0, {'', '', ''}}, {os_readv(fd, {0, 0, 0})}) eq({ false, 0, { '', '', '' } }, { os_readv(fd, { 0, 0, 0 }) })
eq(0, os_close(fd)) eq(0, os_close(fd))
end) end)
itp('can read from a file multiple times to a differently-sized buffers', function() itp('can read from a file multiple times to a differently-sized buffers', function()
local fd = os_open(file, ffi.C.kO_RDONLY, 0) local fd = os_open(file, ffi.C.kO_RDONLY, 0)
ok(fd >= 0) ok(fd >= 0)
eq({false, 2, {'\000\001'}}, {os_readv(fd, {2})}) eq({ false, 2, { '\000\001' } }, { os_readv(fd, { 2 }) })
eq({false, 5, {'\002\003', '\004\005\006'}}, {os_readv(fd, {2, 3})}) eq({ false, 5, { '\002\003', '\004\005\006' } }, { os_readv(fd, { 2, 3 }) })
eq(0, os_close(fd)) eq(0, os_close(fd))
end) end)
itp('can read the whole file at once and then report eof', function() itp('can read the whole file at once and then report eof', function()
local fd = os_open(file, ffi.C.kO_RDONLY, 0) local fd = os_open(file, ffi.C.kO_RDONLY, 0)
ok(fd >= 0) ok(fd >= 0)
eq({false, eq({
#fcontents, false,
{fcontents:sub(1, #fcontents * 1/4), #fcontents,
fcontents:sub(#fcontents * 1/4 + 1, #fcontents * 3/4), {
fcontents:sub(#fcontents * 3/4 + 1, #fcontents * 15/16), fcontents:sub(1, #fcontents * 1 / 4),
fcontents:sub(#fcontents * 15/16 + 1, #fcontents)}}, fcontents:sub(#fcontents * 1 / 4 + 1, #fcontents * 3 / 4),
{os_readv(fd, {#fcontents * 1/4, fcontents:sub(#fcontents * 3 / 4 + 1, #fcontents * 15 / 16),
#fcontents * 2/4, fcontents:sub(#fcontents * 15 / 16 + 1, #fcontents),
#fcontents * 3/16, },
#fcontents * 1/16})}) }, {
eq({true, 0, {'\0'}}, {os_readv(fd, {1})}) os_readv(
fd,
{ #fcontents * 1 / 4, #fcontents * 2 / 4, #fcontents * 3 / 16, #fcontents * 1 / 16 }
),
})
eq({ true, 0, { '\0' } }, { os_readv(fd, { 1 }) })
eq(0, os_close(fd)) eq(0, os_close(fd))
end) end)
itp('can read the whole file in two calls, one partially', function() itp('can read the whole file in two calls, one partially', function()
local fd = os_open(file, ffi.C.kO_RDONLY, 0) local fd = os_open(file, ffi.C.kO_RDONLY, 0)
ok(fd >= 0) ok(fd >= 0)
eq({false, #fcontents * 3/4, {fcontents:sub(1, #fcontents * 3/4)}}, eq(
{os_readv(fd, {#fcontents * 3/4})}) { false, #fcontents * 3 / 4, { fcontents:sub(1, #fcontents * 3 / 4) } },
eq({true, { os_readv(fd, { #fcontents * 3 / 4 }) }
(#fcontents * 1/4), )
{fcontents:sub(#fcontents * 3/4 + 1) .. ('\0'):rep(#fcontents * 2/4)}}, eq({
{os_readv(fd, {#fcontents * 3/4})}) true,
(#fcontents * 1 / 4),
{ fcontents:sub(#fcontents * 3 / 4 + 1) .. ('\0'):rep(#fcontents * 2 / 4) },
}, { os_readv(fd, { #fcontents * 3 / 4 }) })
eq(0, os_close(fd)) eq(0, os_close(fd))
end) end)
end) end)
@ -744,8 +768,8 @@ describe('fs.c', function()
end end
local function os_mkdir_recurse(path, mode) local function os_mkdir_recurse(path, mode)
local failed_str = ffi.new('char *[1]', {nil}) local failed_str = ffi.new('char *[1]', { nil })
local created_str = ffi.new('char *[1]', {nil}) local created_str = ffi.new('char *[1]', { nil })
local ret = fs.os_mkdir_recurse(path, mode, failed_str, created_str) local ret = fs.os_mkdir_recurse(path, mode, failed_str, created_str)
local failed_dir = failed_str[0] local failed_dir = failed_str[0]
if failed_dir ~= nil then if failed_dir ~= nil then
@ -784,8 +808,7 @@ describe('fs.c', function()
itp('fails to create a directory where there is a file', function() itp('fails to create a directory where there is a file', function()
local mode = ffi.C.kS_IRUSR + ffi.C.kS_IWUSR + ffi.C.kS_IXUSR local mode = ffi.C.kS_IRUSR + ffi.C.kS_IWUSR + ffi.C.kS_IXUSR
local ret, failed_dir, created_dir = os_mkdir_recurse( local ret, failed_dir, created_dir = os_mkdir_recurse('unit-test-directory/test.file', mode)
'unit-test-directory/test.file', mode)
neq(0, ret) neq(0, ret)
eq('unit-test-directory/test.file', failed_dir) eq('unit-test-directory/test.file', failed_dir)
eq(nil, created_dir) eq(nil, created_dir)
@ -793,8 +816,8 @@ describe('fs.c', function()
itp('fails to create a directory where there is a file in path', function() itp('fails to create a directory where there is a file in path', function()
local mode = ffi.C.kS_IRUSR + ffi.C.kS_IWUSR + ffi.C.kS_IXUSR local mode = ffi.C.kS_IRUSR + ffi.C.kS_IWUSR + ffi.C.kS_IXUSR
local ret, failed_dir, created_dir = os_mkdir_recurse( local ret, failed_dir, created_dir =
'unit-test-directory/test.file/test', mode) os_mkdir_recurse('unit-test-directory/test.file/test', mode)
neq(0, ret) neq(0, ret)
eq('unit-test-directory/test.file', failed_dir) eq('unit-test-directory/test.file', failed_dir)
eq(nil, created_dir) eq(nil, created_dir)
@ -802,8 +825,8 @@ describe('fs.c', function()
itp('succeeds to create a directory', function() itp('succeeds to create a directory', function()
local mode = ffi.C.kS_IRUSR + ffi.C.kS_IWUSR + ffi.C.kS_IXUSR local mode = ffi.C.kS_IRUSR + ffi.C.kS_IWUSR + ffi.C.kS_IXUSR
local ret, failed_dir, created_dir = os_mkdir_recurse( local ret, failed_dir, created_dir =
'unit-test-directory/new-dir-recurse', mode) os_mkdir_recurse('unit-test-directory/new-dir-recurse', mode)
eq(0, ret) eq(0, ret)
eq(nil, failed_dir) eq(nil, failed_dir)
ok(endswith(created_dir, 'unit-test-directory/new-dir-recurse')) ok(endswith(created_dir, 'unit-test-directory/new-dir-recurse'))
@ -814,8 +837,8 @@ describe('fs.c', function()
itp('succeeds to create a directory ending with ///', function() itp('succeeds to create a directory ending with ///', function()
local mode = ffi.C.kS_IRUSR + ffi.C.kS_IWUSR + ffi.C.kS_IXUSR local mode = ffi.C.kS_IRUSR + ffi.C.kS_IWUSR + ffi.C.kS_IXUSR
local ret, failed_dir, created_dir = os_mkdir_recurse( local ret, failed_dir, created_dir =
'unit-test-directory/new-dir-recurse///', mode) os_mkdir_recurse('unit-test-directory/new-dir-recurse///', mode)
eq(0, ret) eq(0, ret)
eq(nil, failed_dir) eq(nil, failed_dir)
ok(endswith(created_dir, 'unit-test-directory/new-dir-recurse')) ok(endswith(created_dir, 'unit-test-directory/new-dir-recurse'))
@ -826,8 +849,8 @@ describe('fs.c', function()
itp('succeeds to create a directory ending with /', function() itp('succeeds to create a directory ending with /', function()
local mode = ffi.C.kS_IRUSR + ffi.C.kS_IWUSR + ffi.C.kS_IXUSR local mode = ffi.C.kS_IRUSR + ffi.C.kS_IWUSR + ffi.C.kS_IXUSR
local ret, failed_dir, created_dir = os_mkdir_recurse( local ret, failed_dir, created_dir =
'unit-test-directory/new-dir-recurse/', mode) os_mkdir_recurse('unit-test-directory/new-dir-recurse/', mode)
eq(0, ret) eq(0, ret)
eq(nil, failed_dir) eq(nil, failed_dir)
ok(endswith(created_dir, 'unit-test-directory/new-dir-recurse')) ok(endswith(created_dir, 'unit-test-directory/new-dir-recurse'))
@ -838,8 +861,8 @@ describe('fs.c', function()
itp('succeeds to create a directory tree', function() itp('succeeds to create a directory tree', function()
local mode = ffi.C.kS_IRUSR + ffi.C.kS_IWUSR + ffi.C.kS_IXUSR local mode = ffi.C.kS_IRUSR + ffi.C.kS_IWUSR + ffi.C.kS_IXUSR
local ret, failed_dir, created_dir = os_mkdir_recurse( local ret, failed_dir, created_dir =
'unit-test-directory/new-dir-recurse/1/2/3', mode) os_mkdir_recurse('unit-test-directory/new-dir-recurse/1/2/3', mode)
eq(0, ret) eq(0, ret)
eq(nil, failed_dir) eq(nil, failed_dir)
ok(endswith(created_dir, 'unit-test-directory/new-dir-recurse')) ok(endswith(created_dir, 'unit-test-directory/new-dir-recurse'))

View File

@ -21,9 +21,7 @@ describe('shell functions', function()
end) end)
local function shell_build_argv(cmd, extra_args) local function shell_build_argv(cmd, extra_args)
local res = cimported.shell_build_argv( local res = cimported.shell_build_argv(cmd and to_cstr(cmd), extra_args and to_cstr(extra_args))
cmd and to_cstr(cmd),
extra_args and to_cstr(extra_args))
-- `res` is zero-indexed (C pointer, not Lua table)! -- `res` is zero-indexed (C pointer, not Lua table)!
local argc = 0 local argc = 0
local ret = {} local ret = {}
@ -40,9 +38,7 @@ describe('shell functions', function()
local function shell_argv_to_str(argv_table) local function shell_argv_to_str(argv_table)
-- C string array (char **). -- C string array (char **).
local argv = (argv_table local argv = (argv_table and ffi.new('char*[?]', #argv_table + 1) or NULL)
and ffi.new("char*[?]", #argv_table+1)
or NULL)
local argc = 1 local argc = 1
while argv_table ~= nil and argv_table[argc] ~= nil do while argv_table ~= nil and argv_table[argc] ~= nil do
@ -64,8 +60,7 @@ describe('shell functions', function()
local output = ffi.new('char *[1]') local output = ffi.new('char *[1]')
local nread = ffi.new('size_t[1]') local nread = ffi.new('size_t[1]')
local argv = ffi.cast('char**', local argv = ffi.cast('char**', cimported.shell_build_argv(to_cstr(cmd), nil))
cimported.shell_build_argv(to_cstr(cmd), nil))
local status = cimported.os_system(argv, input_or, input_len, output, nread) local status = cimported.os_system(argv, input_or, input_len, output, nread)
return status, intern(output[0], nread[0]) return status, intern(output[0], nread[0])
@ -101,37 +96,35 @@ describe('shell functions', function()
describe('shell_build_argv', function() describe('shell_build_argv', function()
itp('works with NULL arguments', function() itp('works with NULL arguments', function()
eq({'/bin/sh'}, shell_build_argv(nil, nil)) eq({ '/bin/sh' }, shell_build_argv(nil, nil))
end) end)
itp('works with cmd', function() itp('works with cmd', function()
eq({'/bin/sh', '-c', 'abc def'}, shell_build_argv('abc def', nil)) eq({ '/bin/sh', '-c', 'abc def' }, shell_build_argv('abc def', nil))
end) end)
itp('works with extra_args', function() itp('works with extra_args', function()
eq({'/bin/sh', 'ghi jkl'}, shell_build_argv(nil, 'ghi jkl')) eq({ '/bin/sh', 'ghi jkl' }, shell_build_argv(nil, 'ghi jkl'))
end) end)
itp('works with cmd and extra_args', function() itp('works with cmd and extra_args', function()
eq({'/bin/sh', 'ghi jkl', '-c', 'abc def'}, shell_build_argv('abc def', 'ghi jkl')) eq({ '/bin/sh', 'ghi jkl', '-c', 'abc def' }, shell_build_argv('abc def', 'ghi jkl'))
end) end)
itp('splits and unquotes &shell and &shellcmdflag', function() itp('splits and unquotes &shell and &shellcmdflag', function()
cimported.p_sh = to_cstr('/Program" "Files/zsh -f') cimported.p_sh = to_cstr('/Program" "Files/zsh -f')
cimported.p_shcf = to_cstr('-x -o "sh word split" "-"c') cimported.p_shcf = to_cstr('-x -o "sh word split" "-"c')
eq({'/Program Files/zsh', '-f', eq(
'ghi jkl', { '/Program Files/zsh', '-f', 'ghi jkl', '-x', '-o', 'sh word split', '-c', 'abc def' },
'-x', '-o', 'sh word split', shell_build_argv('abc def', 'ghi jkl')
'-c', 'abc def'}, )
shell_build_argv('abc def', 'ghi jkl'))
end) end)
itp('applies shellxescape (p_sxe) and shellxquote (p_sxq)', function() itp('applies shellxescape (p_sxe) and shellxquote (p_sxq)', function()
cimported.p_sxq = to_cstr('(') cimported.p_sxq = to_cstr('(')
cimported.p_sxe = to_cstr('"&|<>()@^') cimported.p_sxe = to_cstr('"&|<>()@^')
local argv = ffi.cast('char**', local argv = ffi.cast('char**', cimported.shell_build_argv(to_cstr('echo &|<>()@^'), nil))
cimported.shell_build_argv(to_cstr('echo &|<>()@^'), nil))
eq(ffi.string(argv[0]), '/bin/sh') eq(ffi.string(argv[0]), '/bin/sh')
eq(ffi.string(argv[1]), '-c') eq(ffi.string(argv[1]), '-c')
eq(ffi.string(argv[2]), '(echo ^&^|^<^>^(^)^@^^)') eq(ffi.string(argv[2]), '(echo ^&^|^<^>^(^)^@^^)')
@ -142,8 +135,7 @@ describe('shell functions', function()
cimported.p_sxq = to_cstr('"(') cimported.p_sxq = to_cstr('"(')
cimported.p_sxe = to_cstr('"&|<>()@^') cimported.p_sxe = to_cstr('"&|<>()@^')
local argv = ffi.cast('char**', cimported.shell_build_argv( local argv = ffi.cast('char**', cimported.shell_build_argv(to_cstr('echo -n some text'), nil))
to_cstr('echo -n some text'), nil))
eq(ffi.string(argv[0]), '/bin/sh') eq(ffi.string(argv[0]), '/bin/sh')
eq(ffi.string(argv[1]), '-c') eq(ffi.string(argv[1]), '-c')
eq(ffi.string(argv[2]), '"(echo -n some text)"') eq(ffi.string(argv[2]), '"(echo -n some text)"')
@ -154,8 +146,7 @@ describe('shell functions', function()
cimported.p_sxq = to_cstr('"') cimported.p_sxq = to_cstr('"')
cimported.p_sxe = to_cstr('') cimported.p_sxe = to_cstr('')
local argv = ffi.cast('char**', cimported.shell_build_argv( local argv = ffi.cast('char**', cimported.shell_build_argv(to_cstr('echo -n some text'), nil))
to_cstr('echo -n some text'), nil))
eq(ffi.string(argv[0]), '/bin/sh') eq(ffi.string(argv[0]), '/bin/sh')
eq(ffi.string(argv[1]), '-c') eq(ffi.string(argv[1]), '-c')
eq(ffi.string(argv[2]), '"echo -n some text"') eq(ffi.string(argv[2]), '"echo -n some text"')
@ -163,8 +154,7 @@ describe('shell functions', function()
end) end)
itp('with empty shellxquote/shellxescape', function() itp('with empty shellxquote/shellxescape', function()
local argv = ffi.cast('char**', cimported.shell_build_argv( local argv = ffi.cast('char**', cimported.shell_build_argv(to_cstr('echo -n some text'), nil))
to_cstr('echo -n some text'), nil))
eq(ffi.string(argv[0]), '/bin/sh') eq(ffi.string(argv[0]), '/bin/sh')
eq(ffi.string(argv[1]), '-c') eq(ffi.string(argv[1]), '-c')
eq(ffi.string(argv[2]), 'echo -n some text') eq(ffi.string(argv[2]), 'echo -n some text')
@ -176,9 +166,11 @@ describe('shell functions', function()
eq('', shell_argv_to_str({ nil })) eq('', shell_argv_to_str({ nil }))
eq("''", shell_argv_to_str({ '' })) eq("''", shell_argv_to_str({ '' }))
eq("'foo' '' 'bar'", shell_argv_to_str({ 'foo', '', 'bar' })) eq("'foo' '' 'bar'", shell_argv_to_str({ 'foo', '', 'bar' }))
eq("'/bin/sh' '-c' 'abc def'", shell_argv_to_str({'/bin/sh', '-c', 'abc def'})) eq("'/bin/sh' '-c' 'abc def'", shell_argv_to_str({ '/bin/sh', '-c', 'abc def' }))
eq("'abc def' 'ghi jkl'", shell_argv_to_str({'abc def', 'ghi jkl'})) eq("'abc def' 'ghi jkl'", shell_argv_to_str({ 'abc def', 'ghi jkl' }))
eq("'/bin/sh' '-c' 'abc def' '"..('x'):rep(225).."...", eq(
shell_argv_to_str({'/bin/sh', '-c', 'abc def', ('x'):rep(999)})) "'/bin/sh' '-c' 'abc def' '" .. ('x'):rep(225) .. '...',
shell_argv_to_str({ '/bin/sh', '-c', 'abc def', ('x'):rep(999) })
)
end) end)
end) end)

View File

@ -184,7 +184,7 @@ describe('path.c', function()
itp('returns the executable name of an invocation given a relative invocation', function() itp('returns the executable name of an invocation given a relative invocation', function()
local invk, len = invocation_path_tail('directory/exe a b c') local invk, len = invocation_path_tail('directory/exe a b c')
compare("exe a b c", invk, len) compare('exe a b c', invk, len)
eq(3, len) eq(3, len)
end) end)
@ -202,7 +202,7 @@ describe('path.c', function()
itp('does not count arguments to the executable as part of its path', function() itp('does not count arguments to the executable as part of its path', function()
local invk, len = invocation_path_tail('exe a/b\\c') local invk, len = invocation_path_tail('exe a/b\\c')
compare("exe a/b\\c", invk, len) compare('exe a/b\\c', invk, len)
eq(3, len) eq(3, len)
end) end)
@ -212,17 +212,17 @@ describe('path.c', function()
end) end)
itp('is equivalent to path_tail when args do not contain a path separator', function() itp('is equivalent to path_tail when args do not contain a path separator', function()
local ptail = cimp.path_tail(to_cstr("a/b/c x y z")) local ptail = cimp.path_tail(to_cstr('a/b/c x y z'))
neq(NULL, ptail) neq(NULL, ptail)
local tail = ffi.string(ptail) local tail = ffi.string(ptail)
local invk, _ = invocation_path_tail("a/b/c x y z") local invk, _ = invocation_path_tail('a/b/c x y z')
eq(tail, ffi.string(invk)) eq(tail, ffi.string(invk))
end) end)
itp('is not equivalent to path_tail when args contain a path separator', function() itp('is not equivalent to path_tail when args contain a path separator', function()
local ptail = cimp.path_tail(to_cstr("a/b/c x y/z")) local ptail = cimp.path_tail(to_cstr('a/b/c x y/z'))
neq(NULL, ptail) neq(NULL, ptail)
local invk, _ = invocation_path_tail("a/b/c x y/z") local invk, _ = invocation_path_tail('a/b/c x y/z')
neq((ffi.string(ptail)), (ffi.string(invk))) neq((ffi.string(ptail)), (ffi.string(invk)))
end) end)
end) end)
@ -304,12 +304,12 @@ end)
describe('path.c path_guess_exepath', function() describe('path.c path_guess_exepath', function()
local cwd = luv.cwd() local cwd = luv.cwd()
for _,name in ipairs({'./nvim', '.nvim', 'foo/nvim'}) do for _, name in ipairs({ './nvim', '.nvim', 'foo/nvim' }) do
itp('"'..name..'" returns name catenated with CWD', function() itp('"' .. name .. '" returns name catenated with CWD', function()
local bufsize = 255 local bufsize = 255
local buf = cstr(bufsize, '') local buf = cstr(bufsize, '')
cimp.path_guess_exepath(name, buf, bufsize) cimp.path_guess_exepath(name, buf, bufsize)
eq(cwd..'/'..name, ffi.string(buf)) eq(cwd .. '/' .. name, ffi.string(buf))
end) end)
end end
@ -331,10 +331,10 @@ describe('path.c path_guess_exepath', function()
itp('does not crash if $PATH item exceeds MAXPATHL', function() itp('does not crash if $PATH item exceeds MAXPATHL', function()
local orig_path_env = os.getenv('PATH') local orig_path_env = os.getenv('PATH')
local name = 'cat' -- Some executable in $PATH. local name = 'cat' -- Some executable in $PATH.
local bufsize = 255 local bufsize = 255
local buf = cstr(bufsize, '') local buf = cstr(bufsize, '')
local insane_path = orig_path_env..':'..(("x/"):rep(4097)) local insane_path = orig_path_env .. ':' .. (('x/'):rep(4097))
cimp.os_setenv('PATH', insane_path, true) cimp.os_setenv('PATH', insane_path, true)
cimp.path_guess_exepath(name, buf, bufsize) cimp.path_guess_exepath(name, buf, bufsize)
@ -345,7 +345,7 @@ describe('path.c path_guess_exepath', function()
end) end)
itp('returns full path found in $PATH', function() itp('returns full path found in $PATH', function()
local name = 'cat' -- Some executable in $PATH. local name = 'cat' -- Some executable in $PATH.
local bufsize = 255 local bufsize = 255
local buf = cstr(bufsize, '') local buf = cstr(bufsize, '')
cimp.path_guess_exepath(name, buf, bufsize) cimp.path_guess_exepath(name, buf, bufsize)
@ -356,7 +356,7 @@ end)
describe('path.c', function() describe('path.c', function()
setup(function() setup(function()
mkdir('unit-test-directory'); mkdir('unit-test-directory')
io.open('unit-test-directory/test.file', 'w'):close() io.open('unit-test-directory/test.file', 'w'):close()
-- Since the tests are executed, they are called by an executable. We use -- Since the tests are executed, they are called by an executable. We use
@ -365,7 +365,7 @@ describe('path.c', function()
-- Split absolute_executable into a directory and the actual file name for -- Split absolute_executable into a directory and the actual file name for
-- later usage. -- later usage.
local directory, executable_name = string.match(absolute_executable, '^(.*)/(.*)$') -- luacheck: ignore local directory, executable_name = string.match(absolute_executable, '^(.*)/(.*)$') -- luacheck: ignore
end) end)
teardown(function() teardown(function()
@ -441,18 +441,21 @@ describe('path.c', function()
eq(OK, result) eq(OK, result)
end) end)
itp('enters given directory (instead of just concatenating the strings) if possible and if path contains a slash', function() itp(
local old_dir = luv.cwd() 'enters given directory (instead of just concatenating the strings) if possible and if path contains a slash',
luv.chdir('..') function()
local expected = luv.cwd() .. '/test.file' local old_dir = luv.cwd()
luv.chdir(old_dir) luv.chdir('..')
local filename = '../test.file' local expected = luv.cwd() .. '/test.file'
local buflen = get_buf_len(expected, filename) luv.chdir(old_dir)
local do_expand = 1 local filename = '../test.file'
local buf, result = vim_FullName(filename, buflen, do_expand) local buflen = get_buf_len(expected, filename)
eq(expected, ffi.string(buf)) local do_expand = 1
eq(OK, result) local buf, result = vim_FullName(filename, buflen, do_expand)
end) eq(expected, ffi.string(buf))
eq(OK, result)
end
)
itp('just copies the path if it is already absolute and force=0', function() itp('just copies the path if it is already absolute and force=0', function()
local absolute_path = '/absolute/path' local absolute_path = '/absolute/path'
@ -544,8 +547,12 @@ describe('path.c', function()
return ffi.string(c_file) return ffi.string(c_file)
end end
before_each(function() mkdir('CamelCase') end) before_each(function()
after_each(function() luv.fs_rmdir('CamelCase') end) mkdir('CamelCase')
end)
after_each(function()
luv.fs_rmdir('CamelCase')
end)
if ffi.os == 'Windows' or ffi.os == 'OSX' then if ffi.os == 'Windows' or ffi.os == 'OSX' then
itp('Corrects the case of file names in Mac and Windows', function() itp('Corrects the case of file names in Mac and Windows', function()
@ -565,14 +572,14 @@ describe('path.c', function()
local path1 = cstr(100, 'path1') local path1 = cstr(100, 'path1')
local to_append = to_cstr('path2') local to_append = to_cstr('path2')
eq(OK, (cimp.append_path(path1, to_append, 100))) eq(OK, (cimp.append_path(path1, to_append, 100)))
eq("path1/path2", (ffi.string(path1))) eq('path1/path2', (ffi.string(path1)))
end) end)
itp('joins given paths without adding an unnecessary slash', function() itp('joins given paths without adding an unnecessary slash', function()
local path1 = cstr(100, 'path1/') local path1 = cstr(100, 'path1/')
local to_append = to_cstr('path2') local to_append = to_cstr('path2')
eq(OK, cimp.append_path(path1, to_append, 100)) eq(OK, cimp.append_path(path1, to_append, 100))
eq("path1/path2", (ffi.string(path1))) eq('path1/path2', (ffi.string(path1)))
end) end)
itp('fails and uses filename if there is not enough space left for to_append', function() itp('fails and uses filename if there is not enough space left for to_append', function()

View File

@ -1,7 +1,7 @@
-- helps managing loading different headers into the LuaJIT ffi. Untested on -- helps managing loading different headers into the LuaJIT ffi. Untested on
-- windows, will probably need quite a bit of adjustment to run there. -- windows, will probably need quite a bit of adjustment to run there.
local ffi = require("ffi") local ffi = require('ffi')
local global_helpers = require('test.helpers') local global_helpers = require('test.helpers')
local argss_to_cmd = global_helpers.argss_to_cmd local argss_to_cmd = global_helpers.argss_to_cmd
@ -12,37 +12,37 @@ local repeated_read_cmd = global_helpers.repeated_read_cmd
--- @type Compiler[] --- @type Compiler[]
local ccs = {} local ccs = {}
local env_cc = os.getenv("CC") local env_cc = os.getenv('CC')
if env_cc then if env_cc then
table.insert(ccs, {path = {"/usr/bin/env", env_cc}, type = "gcc"}) table.insert(ccs, { path = { '/usr/bin/env', env_cc }, type = 'gcc' })
end end
if ffi.os == "Windows" then if ffi.os == 'Windows' then
table.insert(ccs, {path = {"cl"}, type = "msvc"}) table.insert(ccs, { path = { 'cl' }, type = 'msvc' })
end end
table.insert(ccs, {path = {"/usr/bin/env", "cc"}, type = "gcc"}) table.insert(ccs, { path = { '/usr/bin/env', 'cc' }, type = 'gcc' })
table.insert(ccs, {path = {"/usr/bin/env", "gcc"}, type = "gcc"}) table.insert(ccs, { path = { '/usr/bin/env', 'gcc' }, type = 'gcc' })
table.insert(ccs, {path = {"/usr/bin/env", "gcc-4.9"}, type = "gcc"}) table.insert(ccs, { path = { '/usr/bin/env', 'gcc-4.9' }, type = 'gcc' })
table.insert(ccs, {path = {"/usr/bin/env", "gcc-4.8"}, type = "gcc"}) table.insert(ccs, { path = { '/usr/bin/env', 'gcc-4.8' }, type = 'gcc' })
table.insert(ccs, {path = {"/usr/bin/env", "gcc-4.7"}, type = "gcc"}) table.insert(ccs, { path = { '/usr/bin/env', 'gcc-4.7' }, type = 'gcc' })
table.insert(ccs, {path = {"/usr/bin/env", "clang"}, type = "clang"}) table.insert(ccs, { path = { '/usr/bin/env', 'clang' }, type = 'clang' })
table.insert(ccs, {path = {"/usr/bin/env", "icc"}, type = "gcc"}) table.insert(ccs, { path = { '/usr/bin/env', 'icc' }, type = 'gcc' })
-- parse Makefile format dependencies into a Lua table -- parse Makefile format dependencies into a Lua table
--- @param deps string --- @param deps string
--- @return string[] --- @return string[]
local function parse_make_deps(deps) local function parse_make_deps(deps)
-- remove line breaks and line concatenators -- remove line breaks and line concatenators
deps = deps:gsub("\n", ""):gsub("\\", "") deps = deps:gsub('\n', ''):gsub('\\', '')
-- remove the Makefile "target:" element -- remove the Makefile "target:" element
deps = deps:gsub(".+:", "") deps = deps:gsub('.+:', '')
-- remove redundant spaces -- remove redundant spaces
deps = deps:gsub(" +", " ") deps = deps:gsub(' +', ' ')
-- split according to token (space in this case) -- split according to token (space in this case)
local headers = {} --- @type string[] local headers = {} --- @type string[]
for token in deps:gmatch("[^%s]+") do for token in deps:gmatch('[^%s]+') do
-- headers[token] = true -- headers[token] = true
headers[#headers + 1] = token headers[#headers + 1] = token
end end
@ -50,9 +50,9 @@ local function parse_make_deps(deps)
-- resolve path redirections (..) to normalize all paths -- resolve path redirections (..) to normalize all paths
for i, v in ipairs(headers) do for i, v in ipairs(headers) do
-- double dots (..) -- double dots (..)
headers[i] = v:gsub("/[^/%s]+/%.%.", "") headers[i] = v:gsub('/[^/%s]+/%.%.', '')
-- single dot (.) -- single dot (.)
headers[i] = v:gsub("%./", "") headers[i] = v:gsub('%./', '')
end end
return headers return headers
@ -80,7 +80,7 @@ local function headerize(headers, global)
formatted[#formatted + 1] = string.format(fmt, hdr) formatted[#formatted + 1] = string.format(fmt, hdr)
end end
return table.concat(formatted, "\n") return table.concat(formatted, '\n')
end end
--- @class Gcc --- @class Gcc
@ -90,8 +90,8 @@ end
--- @field get_declarations_extra_flags string[] --- @field get_declarations_extra_flags string[]
local Gcc = { local Gcc = {
preprocessor_extra_flags = {}, preprocessor_extra_flags = {},
get_defines_extra_flags = {'-std=c99', '-dM', '-E'}, get_defines_extra_flags = { '-std=c99', '-dM', '-E' },
get_declarations_extra_flags = {'-std=c99', '-P', '-E'}, get_declarations_extra_flags = { '-std=c99', '-P', '-E' },
} }
--- @param name string --- @param name string
@ -115,13 +115,13 @@ end
function Gcc:init_defines() function Gcc:init_defines()
-- preprocessor flags that will hopefully make the compiler produce C -- preprocessor flags that will hopefully make the compiler produce C
-- declarations that the LuaJIT ffi understands. -- declarations that the LuaJIT ffi understands.
self:define('aligned', {'ARGS'}, '') self:define('aligned', { 'ARGS' }, '')
self:define('__attribute__', {'ARGS'}, '') self:define('__attribute__', { 'ARGS' }, '')
self:define('__asm', {'ARGS'}, '') self:define('__asm', { 'ARGS' }, '')
self:define('__asm__', {'ARGS'}, '') self:define('__asm__', { 'ARGS' }, '')
self:define('__inline__', nil, '') self:define('__inline__', nil, '')
self:define('EXTERN', nil, 'extern') self:define('EXTERN', nil, 'extern')
self:define('INIT', {'...'}, '') self:define('INIT', { '...' }, '')
self:define('_GNU_SOURCE') self:define('_GNU_SOURCE')
self:define('INCLUDE_GENERATED_DECLARATIONS') self:define('INCLUDE_GENERATED_DECLARATIONS')
self:define('UNIT_TESTING') self:define('UNIT_TESTING')
@ -158,9 +158,9 @@ end
--- @return string[]? --- @return string[]?
function Gcc:dependencies(hdr) function Gcc:dependencies(hdr)
--- @type string --- @type string
local cmd = argss_to_cmd(self.path, {'-M', hdr}) .. ' 2>&1' local cmd = argss_to_cmd(self.path, { '-M', hdr }) .. ' 2>&1'
local out = assert(io.popen(cmd)) local out = assert(io.popen(cmd))
local deps = out:read("*a") local deps = out:read('*a')
out:close() out:close()
if deps then if deps then
return parse_make_deps(deps) return parse_make_deps(deps)
@ -174,10 +174,14 @@ function Gcc:filter_standard_defines(defines)
local pseudoheader_fname = 'tmp_empty_pseudoheader.h' local pseudoheader_fname = 'tmp_empty_pseudoheader.h'
local pseudoheader_file = assert(io.open(pseudoheader_fname, 'w')) local pseudoheader_file = assert(io.open(pseudoheader_fname, 'w'))
pseudoheader_file:close() pseudoheader_file:close()
local standard_defines = assert(repeated_read_cmd(self.path, local standard_defines = assert(
self.preprocessor_extra_flags, repeated_read_cmd(
self.get_defines_extra_flags, self.path,
{pseudoheader_fname})) self.preprocessor_extra_flags,
self.get_defines_extra_flags,
{ pseudoheader_fname }
)
)
os.remove(pseudoheader_fname) os.remove(pseudoheader_fname)
self.standard_defines = {} --- @type table<string,true> self.standard_defines = {} --- @type table<string,true>
for line in standard_defines:gmatch('[^\n]+') do for line in standard_defines:gmatch('[^\n]+') do
@ -192,7 +196,7 @@ function Gcc:filter_standard_defines(defines)
end end
end end
return table.concat(ret, "\n") return table.concat(ret, '\n')
end end
--- returns a stream representing a preprocessed form of the passed-in headers. --- returns a stream representing a preprocessed form of the passed-in headers.
@ -202,24 +206,33 @@ end
--- @return string, string --- @return string, string
function Gcc:preprocess(previous_defines, ...) function Gcc:preprocess(previous_defines, ...)
-- create pseudo-header -- create pseudo-header
local pseudoheader = headerize({...}, false) local pseudoheader = headerize({ ... }, false)
local pseudoheader_fname = 'tmp_pseudoheader.h' local pseudoheader_fname = 'tmp_pseudoheader.h'
local pseudoheader_file = assert(io.open(pseudoheader_fname, 'w')) local pseudoheader_file = assert(io.open(pseudoheader_fname, 'w'))
pseudoheader_file:write(previous_defines) pseudoheader_file:write(previous_defines)
pseudoheader_file:write("\n") pseudoheader_file:write('\n')
pseudoheader_file:write(pseudoheader) pseudoheader_file:write(pseudoheader)
pseudoheader_file:flush() pseudoheader_file:flush()
pseudoheader_file:close() pseudoheader_file:close()
local defines = assert(repeated_read_cmd(self.path, self.preprocessor_extra_flags, local defines = assert(
self.get_defines_extra_flags, repeated_read_cmd(
{pseudoheader_fname})) self.path,
self.preprocessor_extra_flags,
self.get_defines_extra_flags,
{ pseudoheader_fname }
)
)
defines = self:filter_standard_defines(defines) defines = self:filter_standard_defines(defines)
local declarations = assert(repeated_read_cmd(self.path, local declarations = assert(
self.preprocessor_extra_flags, repeated_read_cmd(
self.get_declarations_extra_flags, self.path,
{pseudoheader_fname})) self.preprocessor_extra_flags,
self.get_declarations_extra_flags,
{ pseudoheader_fname }
)
)
os.remove(pseudoheader_fname) os.remove(pseudoheader_fname)
@ -233,10 +246,10 @@ end
--- @return Gcc? --- @return Gcc?
local function find_best_cc(compilers) local function find_best_cc(compilers)
for _, meta in pairs(compilers) do for _, meta in pairs(compilers) do
local version = assert(io.popen(tostring(meta.path) .. " -v 2>&1")) local version = assert(io.popen(tostring(meta.path) .. ' -v 2>&1'))
version:close() version:close()
if version then if version then
return Gcc:new({path = meta.path}) return Gcc:new({ path = meta.path })
end end
end end
end end

View File

@ -10,11 +10,11 @@ local prof = cimport('./src/nvim/profile.h')
local function split(inputstr, sep) local function split(inputstr, sep)
if sep == nil then if sep == nil then
sep = "%s" sep = '%s'
end end
local t, i = {}, 1 local t, i = {}, 1
for str in string.gmatch(inputstr, "([^"..sep.."]+)") do for str in string.gmatch(inputstr, '([^' .. sep .. ']+)') do
t[i] = str t[i] = str
i = i + 1 i = i + 1
end end
@ -23,36 +23,44 @@ local function split(inputstr, sep)
end end
local function trim(s) local function trim(s)
local from = s:match"^%s*()" local from = s:match '^%s*()'
return from > #s and "" or s:match(".*%S", from) return from > #s and '' or s:match('.*%S', from)
end end
local function starts(str, start) local function starts(str, start)
return string.sub(str, 1, string.len(start)) == start return string.sub(str, 1, string.len(start)) == start
end end
local function cmp_assert(v1, v2, op, opstr) local function cmp_assert(v1, v2, op, opstr)
local res = op(v1, v2) local res = op(v1, v2)
if res == false then if res == false then
print(string.format("expected: %f %s %f", v1, opstr, v2)) print(string.format('expected: %f %s %f', v1, opstr, v2))
end end
assert.is_true(res) assert.is_true(res)
end end
local function lt(a, b) -- luacheck: ignore local function lt(a, b) -- luacheck: ignore
cmp_assert(a, b, function(x, y) return x < y end, "<") cmp_assert(a, b, function(x, y)
return x < y
end, '<')
end end
local function lte(a, b) -- luacheck: ignore local function lte(a, b) -- luacheck: ignore
cmp_assert(a, b, function(x, y) return x <= y end, "<=") cmp_assert(a, b, function(x, y)
return x <= y
end, '<=')
end end
local function gt(a, b) -- luacheck: ignore local function gt(a, b) -- luacheck: ignore
cmp_assert(a, b, function(x, y) return x > y end, ">") cmp_assert(a, b, function(x, y)
return x > y
end, '>')
end end
local function gte(a, b) local function gte(a, b)
cmp_assert(a, b, function(x, y) return x >= y end, ">=") cmp_assert(a, b, function(x, y)
return x >= y
end, '>=')
end end
-- missing functions: -- missing functions:
@ -61,21 +69,43 @@ end
-- profile_set_wait -- profile_set_wait
-- profile_sub_wait -- profile_sub_wait
describe('profiling related functions', function() describe('profiling related functions', function()
local function profile_start() return prof.profile_start() end local function profile_start()
local function profile_end(t) return prof.profile_end(t) end return prof.profile_start()
local function profile_zero() return prof.profile_zero() end end
local function profile_setlimit(ms) return prof.profile_setlimit(ms) end local function profile_end(t)
local function profile_passed_limit(t) return prof.profile_passed_limit(t) end return prof.profile_end(t)
local function profile_add(t1, t2) return prof.profile_add(t1, t2) end end
local function profile_sub(t1, t2) return prof.profile_sub(t1, t2) end local function profile_zero()
local function profile_divide(t, cnt) return prof.profile_divide(t, cnt) end return prof.profile_zero()
local function profile_cmp(t1, t2) return prof.profile_cmp(t1, t2) end end
local function profile_equal(t1, t2) return prof.profile_equal(t1, t2) end local function profile_setlimit(ms)
local function profile_msg(t) return ffi.string(prof.profile_msg(t)) end return prof.profile_setlimit(ms)
end
local function profile_passed_limit(t)
return prof.profile_passed_limit(t)
end
local function profile_add(t1, t2)
return prof.profile_add(t1, t2)
end
local function profile_sub(t1, t2)
return prof.profile_sub(t1, t2)
end
local function profile_divide(t, cnt)
return prof.profile_divide(t, cnt)
end
local function profile_cmp(t1, t2)
return prof.profile_cmp(t1, t2)
end
local function profile_equal(t1, t2)
return prof.profile_equal(t1, t2)
end
local function profile_msg(t)
return ffi.string(prof.profile_msg(t))
end
local function toseconds(t) -- luacheck: ignore local function toseconds(t) -- luacheck: ignore
local str = trim(profile_msg(t)) local str = trim(profile_msg(t))
local spl = split(str, ".") local spl = split(str, '.')
local s, us = spl[1], spl[2] local s, us = spl[1], spl[2]
return tonumber(s) + tonumber(us) / 1000000 return tonumber(s) + tonumber(us) / 1000000
end end
@ -199,14 +229,14 @@ describe('profiling related functions', function()
describe('profile_msg', function() describe('profile_msg', function()
itp('prints the zero time as 0.00000', function() itp('prints the zero time as 0.00000', function()
local str = trim(profile_msg(profile_zero())) local str = trim(profile_msg(profile_zero()))
eq(str, "0.000000") eq(str, '0.000000')
end) end)
itp('prints the time passed, in seconds.microsends', function() itp('prints the time passed, in seconds.microsends', function()
local start = profile_start() local start = profile_start()
local endt = profile_end(start) local endt = profile_end(start)
local str = trim(profile_msg(endt)) local str = trim(profile_msg(endt))
local spl = split(str, ".") local spl = split(str, '.')
-- string has two parts (before dot and after dot) -- string has two parts (before dot and after dot)
eq(2, #spl) eq(2, #spl)
@ -215,11 +245,11 @@ describe('profiling related functions', function()
-- zero seconds have passed (if this is not true, either LuaJIT is too -- zero seconds have passed (if this is not true, either LuaJIT is too
-- slow or the profiling functions are too slow and need to be fixed) -- slow or the profiling functions are too slow and need to be fixed)
eq(s, "0") eq(s, '0')
-- more or less the same goes for the microsecond part, if it doesn't -- more or less the same goes for the microsecond part, if it doesn't
-- start with 0, it's too slow. -- start with 0, it's too slow.
assert.is_true(starts(us, "0")) assert.is_true(starts(us, '0'))
end) end)
end) end)

View File

@ -1,4 +1,4 @@
local helpers = require("test.unit.helpers")(after_each) local helpers = require('test.unit.helpers')(after_each)
local itp = helpers.gen_itp(it) local itp = helpers.gen_itp(it)
local eq = helpers.eq local eq = helpers.eq
@ -7,7 +7,7 @@ local cstr = helpers.cstr
local to_cstr = helpers.to_cstr local to_cstr = helpers.to_cstr
local child_call_once = helpers.child_call_once local child_call_once = helpers.child_call_once
local rbuffer = helpers.cimport("./test/unit/fixtures/rbuffer.h") local rbuffer = helpers.cimport('./test/unit/fixtures/rbuffer.h')
describe('rbuffer functions', function() describe('rbuffer functions', function()
local capacity = 16 local capacity = 16
@ -56,7 +56,7 @@ describe('rbuffer functions', function()
describe('with empty buffer in one contiguous chunk', function() describe('with empty buffer in one contiguous chunk', function()
itp('is called once with the empty chunk', function() itp('is called once with the empty chunk', function()
collect_write_chunks() collect_write_chunks()
eq({'0000000000000000'}, chunks) eq({ '0000000000000000' }, chunks)
end) end)
end) end)
@ -64,7 +64,7 @@ describe('rbuffer functions', function()
itp('is called once with the empty chunk', function() itp('is called once with the empty chunk', function()
write('string') write('string')
collect_write_chunks() collect_write_chunks()
eq({'0000000000'}, chunks) eq({ '0000000000' }, chunks)
end) end)
end) end)
@ -81,7 +81,7 @@ describe('rbuffer functions', function()
write('1234567890') write('1234567890')
read(8) read(8)
collect_write_chunks() collect_write_chunks()
eq({'000000', '12345678'}, chunks) eq({ '000000', '12345678' }, chunks)
end) end)
end) end)
@ -90,7 +90,7 @@ describe('rbuffer functions', function()
write('12345678') write('12345678')
read(8) read(8)
collect_write_chunks() collect_write_chunks()
eq({'00000000', '12345678'}, chunks) eq({ '00000000', '12345678' }, chunks)
end) end)
end) end)
@ -129,7 +129,7 @@ describe('rbuffer functions', function()
itp('is called once with the filled chunk', function() itp('is called once with the filled chunk', function()
write('string') write('string')
collect_read_chunks() collect_read_chunks()
eq({'string'}, chunks) eq({ 'string' }, chunks)
end) end)
end) end)
@ -137,7 +137,7 @@ describe('rbuffer functions', function()
itp('is called once with the filled chunk', function() itp('is called once with the filled chunk', function()
write('abcdefghijklmnopq') write('abcdefghijklmnopq')
collect_read_chunks() collect_read_chunks()
eq({'abcdefghijklmnop'}, chunks) eq({ 'abcdefghijklmnop' }, chunks)
end) end)
end) end)
@ -147,7 +147,7 @@ describe('rbuffer functions', function()
read(10) read(10)
write('long string') write('long string')
collect_read_chunks() collect_read_chunks()
eq({'long s', 'tring'}, chunks) eq({ 'long s', 'tring' }, chunks)
end) end)
end) end)
@ -157,7 +157,7 @@ describe('rbuffer functions', function()
read(8) read(8)
write('abcdefghijklmnopq') write('abcdefghijklmnopq')
collect_read_chunks() collect_read_chunks()
eq({'abcdefgh', 'ijklmnop'}, chunks) eq({ 'abcdefgh', 'ijklmnop' }, chunks)
end) end)
end) end)
end) end)
@ -167,7 +167,7 @@ describe('rbuffer functions', function()
local function collect_chars() local function collect_chars()
rbuffer.ut_rbuffer_each(rbuf, function(c, i) rbuffer.ut_rbuffer_each(rbuf, function(c, i)
table.insert(chars, {string.char(c), tonumber(i)}) table.insert(chars, { string.char(c), tonumber(i) })
end) end)
end end
before_each(function() before_each(function()
@ -187,8 +187,19 @@ describe('rbuffer functions', function()
read(10) read(10)
write('long string') write('long string')
collect_chars() collect_chars()
eq({{'l', 0}, {'o', 1}, {'n', 2}, {'g', 3}, {' ', 4}, {'s', 5}, eq({
{'t', 6}, {'r', 7}, {'i', 8}, {'n', 9}, {'g', 10}}, chars) { 'l', 0 },
{ 'o', 1 },
{ 'n', 2 },
{ 'g', 3 },
{ ' ', 4 },
{ 's', 5 },
{ 't', 6 },
{ 'r', 7 },
{ 'i', 8 },
{ 'n', 9 },
{ 'g', 10 },
}, chars)
end) end)
end) end)
end) end)
@ -198,7 +209,7 @@ describe('rbuffer functions', function()
local function collect_chars() local function collect_chars()
rbuffer.ut_rbuffer_each_reverse(rbuf, function(c, i) rbuffer.ut_rbuffer_each_reverse(rbuf, function(c, i)
table.insert(chars, {string.char(c), tonumber(i)}) table.insert(chars, { string.char(c), tonumber(i) })
end) end)
end end
before_each(function() before_each(function()
@ -218,8 +229,19 @@ describe('rbuffer functions', function()
read(10) read(10)
write('long string') write('long string')
collect_chars() collect_chars()
eq({{'g', 10}, {'n', 9}, {'i', 8}, {'r', 7}, {'t', 6}, {'s', 5}, eq({
{' ', 4}, {'g', 3}, {'n', 2}, {'o', 1}, {'l', 0}}, chars) { 'g', 10 },
{ 'n', 9 },
{ 'i', 8 },
{ 'r', 7 },
{ 't', 6 },
{ 's', 5 },
{ ' ', 4 },
{ 'g', 3 },
{ 'n', 2 },
{ 'o', 1 },
{ 'l', 0 },
}, chars)
end) end)
end) end)
end) end)

View File

@ -1,42 +1,42 @@
local helpers = require("test.unit.helpers")(after_each) local helpers = require('test.unit.helpers')(after_each)
local itp = helpers.gen_itp(it) local itp = helpers.gen_itp(it)
local to_cstr = helpers.to_cstr local to_cstr = helpers.to_cstr
local eq = helpers.eq local eq = helpers.eq
local search = helpers.cimport("./src/nvim/search.h") local search = helpers.cimport('./src/nvim/search.h')
local globals = helpers.cimport('./src/nvim/globals.h') local globals = helpers.cimport('./src/nvim/globals.h')
local ffi = helpers.ffi local ffi = helpers.ffi
itp('pat_has_uppercase', function() itp('pat_has_uppercase', function()
-- works on empty string -- works on empty string
eq(false, search.pat_has_uppercase(to_cstr(""))) eq(false, search.pat_has_uppercase(to_cstr('')))
-- works with utf uppercase -- works with utf uppercase
eq(false, search.pat_has_uppercase(to_cstr("ä"))) eq(false, search.pat_has_uppercase(to_cstr('ä')))
eq(true, search.pat_has_uppercase(to_cstr("Ä"))) eq(true, search.pat_has_uppercase(to_cstr('Ä')))
eq(true, search.pat_has_uppercase(to_cstr("äaÅ"))) eq(true, search.pat_has_uppercase(to_cstr('äaÅ')))
-- works when pat ends with backslash -- works when pat ends with backslash
eq(false, search.pat_has_uppercase(to_cstr("\\"))) eq(false, search.pat_has_uppercase(to_cstr('\\')))
eq(false, search.pat_has_uppercase(to_cstr("ab$\\"))) eq(false, search.pat_has_uppercase(to_cstr('ab$\\')))
-- skips escaped characters -- skips escaped characters
eq(false, search.pat_has_uppercase(to_cstr("\\Ab"))) eq(false, search.pat_has_uppercase(to_cstr('\\Ab')))
eq(true, search.pat_has_uppercase(to_cstr("\\AU"))) eq(true, search.pat_has_uppercase(to_cstr('\\AU')))
-- skips _X escaped characters -- skips _X escaped characters
eq(false, search.pat_has_uppercase(to_cstr("\\_Ab"))) eq(false, search.pat_has_uppercase(to_cstr('\\_Ab')))
eq(true, search.pat_has_uppercase(to_cstr("\\_AU"))) eq(true, search.pat_has_uppercase(to_cstr('\\_AU')))
-- skips %X escaped characters -- skips %X escaped characters
eq(false, search.pat_has_uppercase(to_cstr("aa\\%Ab"))) eq(false, search.pat_has_uppercase(to_cstr('aa\\%Ab')))
eq(true, search.pat_has_uppercase(to_cstr("aab\\%AU"))) eq(true, search.pat_has_uppercase(to_cstr('aab\\%AU')))
end) end)
describe('search_regcomp', function() describe('search_regcomp', function()
local search_regcomp = function(pat, pat_save, pat_use, options ) local search_regcomp = function(pat, pat_save, pat_use, options)
local regmatch = ffi.new("regmmatch_T") local regmatch = ffi.new('regmmatch_T')
local fail = search.search_regcomp(to_cstr(pat), nil, pat_save, pat_use, options, regmatch) local fail = search.search_regcomp(to_cstr(pat), nil, pat_save, pat_use, options, regmatch)
return fail, regmatch return fail, regmatch
end end
@ -45,13 +45,13 @@ describe('search_regcomp', function()
return helpers.internalize(search.get_search_pat()) return helpers.internalize(search.get_search_pat())
end end
itp("accepts regexp pattern with invalid utf", function() itp('accepts regexp pattern with invalid utf', function()
--crafted to call reverse_text with invalid utf --crafted to call reverse_text with invalid utf
globals.curwin.w_onebuf_opt.wo_rl = 1 globals.curwin.w_onebuf_opt.wo_rl = 1
globals.curwin.w_onebuf_opt.wo_rlc = to_cstr('s') globals.curwin.w_onebuf_opt.wo_rlc = to_cstr('s')
globals.cmdmod.cmod_flags = globals.CMOD_KEEPPATTERNS globals.cmdmod.cmod_flags = globals.CMOD_KEEPPATTERNS
local fail = search_regcomp("a\192", 0,0,0) local fail = search_regcomp('a\192', 0, 0, 0)
eq(1, fail) eq(1, fail)
eq("\192a", get_search_pat()) eq('\192a', get_search_pat())
end) end)
end) end)

View File

@ -33,7 +33,7 @@ end
--- @return Set --- @return Set
function Set:copy() function Set:copy()
local obj = {nelem = self.nelem, tbl = {}, items = {}} --- @type Set local obj = { nelem = self.nelem, tbl = {}, items = {} } --- @type Set
for k, v in pairs(self.tbl) do for k, v in pairs(self.tbl) do
obj.tbl[k] = v obj.tbl[k] = v
end end
@ -128,13 +128,13 @@ function Set:to_table()
-- there might be gaps in @tbl, so we have to be careful and sort first -- there might be gaps in @tbl, so we have to be careful and sort first
local keys = {} --- @type string[] local keys = {} --- @type string[]
for idx, _ in pairs(self.tbl) do for idx, _ in pairs(self.tbl) do
keys[#keys+1] = idx keys[#keys + 1] = idx
end end
table.sort(keys) table.sort(keys)
local copy = {} --- @type string[] local copy = {} --- @type string[]
for _, idx in ipairs(keys) do for _, idx in ipairs(keys) do
copy[#copy+1] = self.tbl[idx] copy[#copy + 1] = self.tbl[idx]
end end
return copy return copy
end end

View File

@ -1,14 +1,14 @@
local helpers = require("test.unit.helpers")(after_each) local helpers = require('test.unit.helpers')(after_each)
local itp = helpers.gen_itp(it) local itp = helpers.gen_itp(it)
local to_cstr = helpers.to_cstr local to_cstr = helpers.to_cstr
local get_str = helpers.ffi.string local get_str = helpers.ffi.string
local eq = helpers.eq local eq = helpers.eq
local NULL = helpers.NULL local NULL = helpers.NULL
local buffer = helpers.cimport("./src/nvim/buffer.h") local buffer = helpers.cimport('./src/nvim/buffer.h')
local globals = helpers.cimport("./src/nvim/globals.h") local globals = helpers.cimport('./src/nvim/globals.h')
local stl = helpers.cimport("./src/nvim/statusline.h") local stl = helpers.cimport('./src/nvim/statusline.h')
describe('build_stl_str_hl', function() describe('build_stl_str_hl', function()
local buffer_byte_size = 100 local buffer_byte_size = 100
@ -22,23 +22,25 @@ describe('build_stl_str_hl', function()
-- .fillchar The fill character used in the statusline -- .fillchar The fill character used in the statusline
-- .maximum_cell_count The number of cells available in the statusline -- .maximum_cell_count The number of cells available in the statusline
local function build_stl_str_hl(arg) local function build_stl_str_hl(arg)
output_buffer = to_cstr(string.rep(" ", buffer_byte_size)) output_buffer = to_cstr(string.rep(' ', buffer_byte_size))
local pat = arg.pat or '' local pat = arg.pat or ''
local fillchar = arg.fillchar or (' '):byte() local fillchar = arg.fillchar or (' '):byte()
local maximum_cell_count = arg.maximum_cell_count or buffer_byte_size local maximum_cell_count = arg.maximum_cell_count or buffer_byte_size
return stl.build_stl_str_hl(globals.curwin, return stl.build_stl_str_hl(
output_buffer, globals.curwin,
buffer_byte_size, output_buffer,
to_cstr(pat), buffer_byte_size,
NULL, to_cstr(pat),
0, NULL,
fillchar, 0,
maximum_cell_count, fillchar,
NULL, maximum_cell_count,
NULL, NULL,
NULL) NULL,
NULL
)
end end
-- Use this function to simplify testing the comparison between -- Use this function to simplify testing the comparison between
@ -54,12 +56,7 @@ describe('build_stl_str_hl', function()
-- .expected_byte_length The expected byte length of the string (defaults to byte length of expected_stl) -- .expected_byte_length The expected byte length of the string (defaults to byte length of expected_stl)
-- .file_name The name of the file to be tested (useful in %f type tests) -- .file_name The name of the file to be tested (useful in %f type tests)
-- .fillchar The character that will be used to fill any 'extra' space in the stl -- .fillchar The character that will be used to fill any 'extra' space in the stl
local function statusline_test(description, local function statusline_test(description, statusline_cell_count, input_stl, expected_stl, arg)
statusline_cell_count,
input_stl,
expected_stl,
arg)
-- arg is the optional parameter -- arg is the optional parameter
-- so we either fill in option with arg or an empty dictionary -- so we either fill in option with arg or an empty dictionary
local option = arg or {} local option = arg or {}
@ -75,9 +72,11 @@ describe('build_stl_str_hl', function()
buffer.setfname(globals.curbuf, nil, NULL, 1) buffer.setfname(globals.curbuf, nil, NULL, 1)
end end
local result_cell_count = build_stl_str_hl{pat=input_stl, local result_cell_count = build_stl_str_hl {
maximum_cell_count=statusline_cell_count, pat = input_stl,
fillchar=fillchar} maximum_cell_count = statusline_cell_count,
fillchar = fillchar,
}
eq(expected_stl, get_str(output_buffer, expected_byte_length)) eq(expected_stl, get_str(output_buffer, expected_byte_length))
eq(expected_cell_count, result_cell_count) eq(expected_cell_count, result_cell_count)
@ -85,198 +84,383 @@ describe('build_stl_str_hl', function()
end end
-- expression testing -- expression testing
statusline_test('Should expand expression', 2, statusline_test('Should expand expression', 2, '%!expand(20+1)', '21')
'%!expand(20+1)', '21') statusline_test('Should expand broken expression to itself', 11, '%!expand(20+1', 'expand(20+1')
statusline_test('Should expand broken expression to itself', 11,
'%!expand(20+1', 'expand(20+1')
-- file name testing -- file name testing
statusline_test('should print no file name', 10, statusline_test('should print no file name', 10, '%f', '[No Name]', { expected_cell_count = 9 })
'%f', '[No Name]', statusline_test(
{expected_cell_count=9}) 'should print the relative file name',
statusline_test('should print the relative file name', 30, 30,
'%f', 'test/unit/buffer_spec.lua', '%f',
{file_name='test/unit/buffer_spec.lua', expected_cell_count=25}) 'test/unit/buffer_spec.lua',
statusline_test('should print the full file name', 40, { file_name = 'test/unit/buffer_spec.lua', expected_cell_count = 25 }
'%F', '/test/unit/buffer_spec.lua', )
{file_name='/test/unit/buffer_spec.lua', expected_cell_count=26}) statusline_test(
'should print the full file name',
40,
'%F',
'/test/unit/buffer_spec.lua',
{ file_name = '/test/unit/buffer_spec.lua', expected_cell_count = 26 }
)
-- fillchar testing -- fillchar testing
statusline_test('should handle `!` as a fillchar', 10, statusline_test(
'abcde%=', 'abcde!!!!!', 'should handle `!` as a fillchar',
{fillchar=('!'):byte()}) 10,
statusline_test('should handle `~` as a fillchar', 10, 'abcde%=',
'%=abcde', '~~~~~abcde', 'abcde!!!!!',
{fillchar=('~'):byte()}) { fillchar = ('!'):byte() }
statusline_test('should put fillchar `!` in between text', 10, )
'abc%=def', 'abc!!!!def', statusline_test(
{fillchar=('!'):byte()}) 'should handle `~` as a fillchar',
statusline_test('should put fillchar `~` in between text', 10, 10,
'abc%=def', 'abc~~~~def', '%=abcde',
{fillchar=('~'):byte()}) '~~~~~abcde',
statusline_test('should put fillchar `━` in between text', 10, { fillchar = ('~'):byte() }
'abc%=def', 'abc━━━━def', )
{fillchar=0x2501}) statusline_test(
statusline_test('should handle zero-fillchar as a space', 10, 'should put fillchar `!` in between text',
'abcde%=', 'abcde ', 10,
{fillchar=0}) 'abc%=def',
statusline_test('should print the tail file name', 80, 'abc!!!!def',
'%t', 'buffer_spec.lua', { fillchar = ('!'):byte() }
{file_name='test/unit/buffer_spec.lua', expected_cell_count=15}) )
statusline_test(
'should put fillchar `~` in between text',
10,
'abc%=def',
'abc~~~~def',
{ fillchar = ('~'):byte() }
)
statusline_test(
'should put fillchar `━` in between text',
10,
'abc%=def',
'abc━━━━def',
{ fillchar = 0x2501 }
)
statusline_test(
'should handle zero-fillchar as a space',
10,
'abcde%=',
'abcde ',
{ fillchar = 0 }
)
statusline_test(
'should print the tail file name',
80,
'%t',
'buffer_spec.lua',
{ file_name = 'test/unit/buffer_spec.lua', expected_cell_count = 15 }
)
-- standard text testing -- standard text testing
statusline_test('should copy plain text', 80, statusline_test(
'this is a test', 'this is a test', 'should copy plain text',
{expected_cell_count=14}) 80,
'this is a test',
'this is a test',
{ expected_cell_count = 14 }
)
-- line number testing -- line number testing
statusline_test('should print the buffer number', 80, statusline_test('should print the buffer number', 80, '%n', '1', { expected_cell_count = 1 })
'%n', '1', statusline_test(
{expected_cell_count=1}) 'should print the current line number in the buffer',
statusline_test('should print the current line number in the buffer', 80, 80,
'%l', '0', '%l',
{expected_cell_count=1}) '0',
statusline_test('should print the number of lines in the buffer', 80, { expected_cell_count = 1 }
'%L', '1', )
{expected_cell_count=1}) statusline_test(
'should print the number of lines in the buffer',
80,
'%L',
'1',
{ expected_cell_count = 1 }
)
-- truncation testing -- truncation testing
statusline_test('should truncate when standard text pattern is too long', 10, statusline_test(
'0123456789abcde', '<6789abcde') 'should truncate when standard text pattern is too long',
statusline_test('should truncate when using =', 10, 10,
'abcdef%=ghijkl', 'abcdef<jkl') '0123456789abcde',
statusline_test('should truncate centered text when using ==', 10, '<6789abcde'
'abcde%=gone%=fghij', 'abcde<ghij') )
statusline_test('should respect the `<` marker', 10, statusline_test('should truncate when using =', 10, 'abcdef%=ghijkl', 'abcdef<jkl')
'abc%<defghijkl', 'abc<ghijkl') statusline_test(
statusline_test('should truncate at `<` with one `=`, test 1', 10, 'should truncate centered text when using ==',
'abc%<def%=ghijklmno', 'abc<jklmno') 10,
statusline_test('should truncate at `<` with one `=`, test 2', 10, 'abcde%=gone%=fghij',
'abcdef%=ghijkl%<mno', 'abcdefghi>') 'abcde<ghij'
statusline_test('should truncate at `<` with one `=`, test 3', 10, )
'abc%<def%=ghijklmno', 'abc<jklmno') statusline_test('should respect the `<` marker', 10, 'abc%<defghijkl', 'abc<ghijkl')
statusline_test('should truncate at `<` with one `=`, test 4', 10, statusline_test(
'abc%<def%=ghij', 'abcdefghij') 'should truncate at `<` with one `=`, test 1',
statusline_test('should truncate at `<` with one `=`, test 4', 10, 10,
'abc%<def%=ghijk', 'abc<fghijk') 'abc%<def%=ghijklmno',
'abc<jklmno'
)
statusline_test(
'should truncate at `<` with one `=`, test 2',
10,
'abcdef%=ghijkl%<mno',
'abcdefghi>'
)
statusline_test(
'should truncate at `<` with one `=`, test 3',
10,
'abc%<def%=ghijklmno',
'abc<jklmno'
)
statusline_test('should truncate at `<` with one `=`, test 4', 10, 'abc%<def%=ghij', 'abcdefghij')
statusline_test(
'should truncate at `<` with one `=`, test 4',
10,
'abc%<def%=ghijk',
'abc<fghijk'
)
statusline_test('should truncate at `<` with many `=`, test 4', 10, statusline_test(
'ab%<cdef%=g%=h%=ijk', 'ab<efghijk') 'should truncate at `<` with many `=`, test 4',
10,
'ab%<cdef%=g%=h%=ijk',
'ab<efghijk'
)
statusline_test('should truncate at the first `<`', 10, statusline_test('should truncate at the first `<`', 10, 'abc%<def%<ghijklm', 'abc<hijklm')
'abc%<def%<ghijklm', 'abc<hijklm')
statusline_test('should ignore trailing %', 3, 'abc%', 'abc') statusline_test('should ignore trailing %', 3, 'abc%', 'abc')
-- alignment testing with fillchar -- alignment testing with fillchar
local function statusline_test_align(description, local function statusline_test_align(
statusline_cell_count, description,
input_stl, statusline_cell_count,
expected_stl, input_stl,
arg) expected_stl,
arg
)
arg = arg or {} arg = arg or {}
statusline_test(description .. ' without fillchar', statusline_test(
statusline_cell_count, input_stl, expected_stl:gsub('%~', ' '), arg) description .. ' without fillchar',
statusline_cell_count,
input_stl,
expected_stl:gsub('%~', ' '),
arg
)
arg.fillchar = ('!'):byte() arg.fillchar = ('!'):byte()
statusline_test(description .. ' with fillchar `!`', statusline_test(
statusline_cell_count, input_stl, expected_stl:gsub('%~', '!'), arg) description .. ' with fillchar `!`',
statusline_cell_count,
input_stl,
expected_stl:gsub('%~', '!'),
arg
)
arg.fillchar = 0x2501 arg.fillchar = 0x2501
statusline_test(description .. ' with fillchar `━`', statusline_test(
statusline_cell_count, input_stl, expected_stl:gsub('%~', ''), arg) description .. ' with fillchar `━`',
statusline_cell_count,
input_stl,
expected_stl:gsub('%~', ''),
arg
)
end end
statusline_test_align('should right align when using =', 20, statusline_test_align('should right align when using =', 20, 'neo%=vim', 'neo~~~~~~~~~~~~~~vim')
'neo%=vim', 'neo~~~~~~~~~~~~~~vim') statusline_test_align(
statusline_test_align('should, when possible, center text when using %=text%=', 20, 'should, when possible, center text when using %=text%=',
'abc%=neovim%=def', 'abc~~~~neovim~~~~def') 20,
statusline_test_align('should handle uneven spacing in the buffer when using %=text%=', 20, 'abc%=neovim%=def',
'abc%=neo_vim%=def', 'abc~~~neo_vim~~~~def') 'abc~~~~neovim~~~~def'
statusline_test_align('should have equal spaces even with non-equal sides when using =', 20, )
'foobar%=test%=baz', 'foobar~~~test~~~~baz') statusline_test_align(
statusline_test_align('should have equal spaces even with longer right side when using =', 20, 'should handle uneven spacing in the buffer when using %=text%=',
'a%=test%=longtext', 'a~~~test~~~~longtext') 20,
statusline_test_align('should handle an empty left side when using ==', 20, 'abc%=neo_vim%=def',
'%=test%=baz', '~~~~~~test~~~~~~~baz') 'abc~~~neo_vim~~~~def'
statusline_test_align('should handle an empty right side when using ==', 20, )
'foobar%=test%=', 'foobar~~~~~test~~~~~') statusline_test_align(
statusline_test_align('should handle consecutive empty ==', 20, 'should have equal spaces even with non-equal sides when using =',
'%=%=test%=', '~~~~~~~~~~test~~~~~~') 20,
statusline_test_align('should handle an = alone', 20, 'foobar%=test%=baz',
'%=', '~~~~~~~~~~~~~~~~~~~~') 'foobar~~~test~~~~baz'
statusline_test_align('should right align text when it is alone with =', 20, )
'%=foo', '~~~~~~~~~~~~~~~~~foo') statusline_test_align(
statusline_test_align('should left align text when it is alone with =', 20, 'should have equal spaces even with longer right side when using =',
'foo%=', 'foo~~~~~~~~~~~~~~~~~') 20,
'a%=test%=longtext',
'a~~~test~~~~longtext'
)
statusline_test_align(
'should handle an empty left side when using ==',
20,
'%=test%=baz',
'~~~~~~test~~~~~~~baz'
)
statusline_test_align(
'should handle an empty right side when using ==',
20,
'foobar%=test%=',
'foobar~~~~~test~~~~~'
)
statusline_test_align(
'should handle consecutive empty ==',
20,
'%=%=test%=',
'~~~~~~~~~~test~~~~~~'
)
statusline_test_align('should handle an = alone', 20, '%=', '~~~~~~~~~~~~~~~~~~~~')
statusline_test_align(
'should right align text when it is alone with =',
20,
'%=foo',
'~~~~~~~~~~~~~~~~~foo'
)
statusline_test_align(
'should left align text when it is alone with =',
20,
'foo%=',
'foo~~~~~~~~~~~~~~~~~'
)
statusline_test_align('should approximately center text when using %=text%=', 21, statusline_test_align(
'abc%=neovim%=def', 'abc~~~~neovim~~~~~def') 'should approximately center text when using %=text%=',
statusline_test_align('should completely fill the buffer when using %=text%=', 21, 21,
'abc%=neo_vim%=def', 'abc~~~~neo_vim~~~~def') 'abc%=neovim%=def',
statusline_test_align('should have equal spacing even with non-equal sides when using =', 21, 'abc~~~~neovim~~~~~def'
'foobar%=test%=baz', 'foobar~~~~test~~~~baz') )
statusline_test_align('should have equal spacing even with longer right side when using =', 21, statusline_test_align(
'a%=test%=longtext', 'a~~~~test~~~~longtext') 'should completely fill the buffer when using %=text%=',
statusline_test_align('should handle an empty left side when using ==', 21, 21,
'%=test%=baz', '~~~~~~~test~~~~~~~baz') 'abc%=neo_vim%=def',
statusline_test_align('should handle an empty right side when using ==', 21, 'abc~~~~neo_vim~~~~def'
'foobar%=test%=', 'foobar~~~~~test~~~~~~') )
statusline_test_align(
'should have equal spacing even with non-equal sides when using =',
21,
'foobar%=test%=baz',
'foobar~~~~test~~~~baz'
)
statusline_test_align(
'should have equal spacing even with longer right side when using =',
21,
'a%=test%=longtext',
'a~~~~test~~~~longtext'
)
statusline_test_align(
'should handle an empty left side when using ==',
21,
'%=test%=baz',
'~~~~~~~test~~~~~~~baz'
)
statusline_test_align(
'should handle an empty right side when using ==',
21,
'foobar%=test%=',
'foobar~~~~~test~~~~~~'
)
statusline_test_align('should quadrant the text when using 3 %=', 40, statusline_test_align(
'abcd%=n%=eovim%=ef', 'abcd~~~~~~~~~n~~~~~~~~~eovim~~~~~~~~~~ef') 'should quadrant the text when using 3 %=',
statusline_test_align('should work well with %t', 40, 40,
'%t%=right_aligned', 'buffer_spec.lua~~~~~~~~~~~~right_aligned', 'abcd%=n%=eovim%=ef',
{file_name='test/unit/buffer_spec.lua'}) 'abcd~~~~~~~~~n~~~~~~~~~eovim~~~~~~~~~~ef'
statusline_test_align('should work well with %t and regular text', 40, )
'l%=m_l %t m_r%=r', 'l~~~~~~~m_l buffer_spec.lua m_r~~~~~~~~r', statusline_test_align(
{file_name='test/unit/buffer_spec.lua'}) 'should work well with %t',
statusline_test_align('should work well with %=, %t, %L, and %l', 40, 40,
'%t %= %L %= %l', 'buffer_spec.lua ~~~~~~~~~ 1 ~~~~~~~~~~ 0', '%t%=right_aligned',
{file_name='test/unit/buffer_spec.lua'}) 'buffer_spec.lua~~~~~~~~~~~~right_aligned',
{ file_name = 'test/unit/buffer_spec.lua' }
)
statusline_test_align(
'should work well with %t and regular text',
40,
'l%=m_l %t m_r%=r',
'l~~~~~~~m_l buffer_spec.lua m_r~~~~~~~~r',
{ file_name = 'test/unit/buffer_spec.lua' }
)
statusline_test_align(
'should work well with %=, %t, %L, and %l',
40,
'%t %= %L %= %l',
'buffer_spec.lua ~~~~~~~~~ 1 ~~~~~~~~~~ 0',
{ file_name = 'test/unit/buffer_spec.lua' }
)
statusline_test_align('should quadrant the text when using 3 %=', 41, statusline_test_align(
'abcd%=n%=eovim%=ef', 'abcd~~~~~~~~~n~~~~~~~~~eovim~~~~~~~~~~~ef') 'should quadrant the text when using 3 %=',
statusline_test_align('should work well with %t', 41, 41,
'%t%=right_aligned', 'buffer_spec.lua~~~~~~~~~~~~~right_aligned', 'abcd%=n%=eovim%=ef',
{file_name='test/unit/buffer_spec.lua'}) 'abcd~~~~~~~~~n~~~~~~~~~eovim~~~~~~~~~~~ef'
statusline_test_align('should work well with %t and regular text', 41, )
'l%=m_l %t m_r%=r', 'l~~~~~~~~m_l buffer_spec.lua m_r~~~~~~~~r', statusline_test_align(
{file_name='test/unit/buffer_spec.lua'}) 'should work well with %t',
statusline_test_align('should work well with %=, %t, %L, and %l', 41, 41,
'%t %= %L %= %l', 'buffer_spec.lua ~~~~~~~~~~ 1 ~~~~~~~~~~ 0', '%t%=right_aligned',
{file_name='test/unit/buffer_spec.lua'}) 'buffer_spec.lua~~~~~~~~~~~~~right_aligned',
{ file_name = 'test/unit/buffer_spec.lua' }
)
statusline_test_align(
'should work well with %t and regular text',
41,
'l%=m_l %t m_r%=r',
'l~~~~~~~~m_l buffer_spec.lua m_r~~~~~~~~r',
{ file_name = 'test/unit/buffer_spec.lua' }
)
statusline_test_align(
'should work well with %=, %t, %L, and %l',
41,
'%t %= %L %= %l',
'buffer_spec.lua ~~~~~~~~~~ 1 ~~~~~~~~~~ 0',
{ file_name = 'test/unit/buffer_spec.lua' }
)
statusline_test_align('should work with 10 %=', 50, statusline_test_align(
'should work with 10 %=',
50,
'aaaa%=b%=c%=d%=e%=fg%=hi%=jk%=lmnop%=qrstuv%=wxyz', 'aaaa%=b%=c%=d%=e%=fg%=hi%=jk%=lmnop%=qrstuv%=wxyz',
'aaaa~~b~~c~~d~~e~~fg~~hi~~jk~~lmnop~~qrstuv~~~wxyz') 'aaaa~~b~~c~~d~~e~~fg~~hi~~jk~~lmnop~~qrstuv~~~wxyz'
)
-- stl item testing -- stl item testing
local tabline = '' local tabline = ''
for i = 1, 1000 do for i = 1, 1000 do
tabline = tabline .. (i % 2 == 0 and '%#TabLineSel#' or '%#TabLineFill#') .. tostring(i % 2) tabline = tabline .. (i % 2 == 0 and '%#TabLineSel#' or '%#TabLineFill#') .. tostring(i % 2)
end end
statusline_test('should handle a large amount of any items', 20, statusline_test('should handle a large amount of any items', 20, tabline, '<1010101010101010101') -- Should not show any error
tabline, statusline_test(
'<1010101010101010101') -- Should not show any error 'should handle a larger amount of = than stl initial item',
statusline_test('should handle a larger amount of = than stl initial item', 20, 20,
('%='):rep(STL_INITIAL_ITEMS * 5), ('%='):rep(STL_INITIAL_ITEMS * 5),
' ') -- Should not show any error ' '
statusline_test('should handle many extra characters', 20, ) -- Should not show any error
statusline_test(
'should handle many extra characters',
20,
'a' .. ('a'):rep(STL_INITIAL_ITEMS * 5), 'a' .. ('a'):rep(STL_INITIAL_ITEMS * 5),
'<aaaaaaaaaaaaaaaaaaa') -- Does not show any error '<aaaaaaaaaaaaaaaaaaa'
statusline_test('should handle many extra characters and flags', 20, ) -- Does not show any error
statusline_test(
'should handle many extra characters and flags',
20,
'a' .. ('%=a'):rep(STL_INITIAL_ITEMS * 2), 'a' .. ('%=a'):rep(STL_INITIAL_ITEMS * 2),
'a<aaaaaaaaaaaaaaaaaa') -- Should not show any error 'a<aaaaaaaaaaaaaaaaaa'
) -- Should not show any error
-- multi-byte testing -- multi-byte testing
statusline_test('should handle multibyte characters', 10, statusline_test('should handle multibyte characters', 10, 'Ĉ%=x', 'Ĉ x')
'Ĉ%=x', 'Ĉ x') statusline_test(
statusline_test('should handle multibyte characters and different fillchars', 10, 'should handle multibyte characters and different fillchars',
'Ą%=mid%=end', 'Ą@mid@@end', 10,
{fillchar=('@'):byte()}) 'Ą%=mid%=end',
'Ą@mid@@end',
{ fillchar = ('@'):byte() }
)
-- escaping % testing -- escaping % testing
statusline_test('should handle escape of %', 4, 'abc%%', 'abc%') statusline_test('should handle escape of %', 4, 'abc%%', 'abc%')
statusline_test('case where escaped % does not fit', 3, 'abc%%abcabc', '<bc') statusline_test('case where escaped % does not fit', 3, 'abc%%abcabc', '<bc')
statusline_test('escaped % is first', 1, '%%', '%') statusline_test('escaped % is first', 1, '%%', '%')
end) end)

View File

@ -1,4 +1,4 @@
local helpers = require("test.unit.helpers")(after_each) local helpers = require('test.unit.helpers')(after_each)
local itp = helpers.gen_itp(it) local itp = helpers.gen_itp(it)
local cimport = helpers.cimport local cimport = helpers.cimport
@ -6,8 +6,7 @@ local eq = helpers.eq
local ffi = helpers.ffi local ffi = helpers.ffi
local to_cstr = helpers.to_cstr local to_cstr = helpers.to_cstr
local strings = cimport('stdlib.h', './src/nvim/strings.h', local strings = cimport('stdlib.h', './src/nvim/strings.h', './src/nvim/memory.h')
'./src/nvim/memory.h')
describe('vim_strsave_escaped()', function() describe('vim_strsave_escaped()', function()
local vim_strsave_escaped = function(s, chars) local vim_strsave_escaped = function(s, chars)
@ -21,19 +20,19 @@ describe('vim_strsave_escaped()', function()
end end
itp('precedes by a backslash all chars from second argument', function() itp('precedes by a backslash all chars from second argument', function()
eq([[\a\b\c\d]], vim_strsave_escaped('abcd','abcd')) eq([[\a\b\c\d]], vim_strsave_escaped('abcd', 'abcd'))
end) end)
itp('precedes by a backslash chars only from second argument', function() itp('precedes by a backslash chars only from second argument', function()
eq([[\a\bcd]], vim_strsave_escaped('abcd','ab')) eq([[\a\bcd]], vim_strsave_escaped('abcd', 'ab'))
end) end)
itp('returns a copy of passed string if second argument is empty', function() itp('returns a copy of passed string if second argument is empty', function()
eq('text \n text', vim_strsave_escaped('text \n text','')) eq('text \n text', vim_strsave_escaped('text \n text', ''))
end) end)
itp('returns an empty string if first argument is empty string', function() itp('returns an empty string if first argument is empty string', function()
eq('', vim_strsave_escaped('','\r')) eq('', vim_strsave_escaped('', '\r'))
end) end)
itp('returns a copy of passed string if it does not contain chars from 2nd argument', function() itp('returns a copy of passed string if it does not contain chars from 2nd argument', function()
@ -148,14 +147,30 @@ describe('vim_snprintf()', function()
end end
end end
local function i(n) return ffi.cast('int', n) end local function i(n)
local function l(n) return ffi.cast('long', n) end return ffi.cast('int', n)
local function ll(n) return ffi.cast('long long', n) end end
local function z(n) return ffi.cast('ptrdiff_t', n) end local function l(n)
local function u(n) return ffi.cast('unsigned', n) end return ffi.cast('long', n)
local function ul(n) return ffi.cast('unsigned long', n) end end
local function ull(n) return ffi.cast('unsigned long long', n) end local function ll(n)
local function uz(n) return ffi.cast('size_t', n) end return ffi.cast('long long', n)
end
local function z(n)
return ffi.cast('ptrdiff_t', n)
end
local function u(n)
return ffi.cast('unsigned', n)
end
local function ul(n)
return ffi.cast('unsigned long', n)
end
local function ull(n)
return ffi.cast('unsigned long long', n)
end
local function uz(n)
return ffi.cast('size_t', n)
end
itp('truncation', function() itp('truncation', function()
for bsize = 0, 14 do for bsize = 0, 14 do
@ -232,49 +247,51 @@ describe('vim_snprintf()', function()
end) end)
end) end)
describe('strcase_save()' , function() describe('strcase_save()', function()
local strcase_save = function(input_string, upper) local strcase_save = function(input_string, upper)
local res = strings.strcase_save(to_cstr(input_string), upper) local res = strings.strcase_save(to_cstr(input_string), upper)
return ffi.string(res) return ffi.string(res)
end end
itp('decodes overlong encoded characters.', function() itp('decodes overlong encoded characters.', function()
eq("A", strcase_save("\xc1\x81", true)) eq('A', strcase_save('\xc1\x81', true))
eq("a", strcase_save("\xc1\x81", false)) eq('a', strcase_save('\xc1\x81', false))
end) end)
end) end)
describe("reverse_text", function() describe('reverse_text', function()
local reverse_text = function(str) local reverse_text = function(str)
return helpers.internalize(strings.reverse_text(to_cstr(str))) return helpers.internalize(strings.reverse_text(to_cstr(str)))
end end
itp("handles empty string", function() itp('handles empty string', function()
eq("", reverse_text("")) eq('', reverse_text(''))
end) end)
itp("handles simple cases", function() itp('handles simple cases', function()
eq("a", reverse_text("a")) eq('a', reverse_text('a'))
eq("ba", reverse_text("ab")) eq('ba', reverse_text('ab'))
end) end)
itp("handles multibyte characters", function() itp('handles multibyte characters', function()
eq("bα", reverse_text("αb")) eq('bα', reverse_text('αb'))
eq("Yötön yö", reverse_text("öy nötöY")) eq('Yötön yö', reverse_text('öy nötöY'))
end) end)
itp("handles combining chars", function() itp('handles combining chars', function()
local utf8_COMBINING_RING_ABOVE = "\204\138" local utf8_COMBINING_RING_ABOVE = '\204\138'
local utf8_COMBINING_RING_BELOW = "\204\165" local utf8_COMBINING_RING_BELOW = '\204\165'
eq("bba" .. utf8_COMBINING_RING_ABOVE .. utf8_COMBINING_RING_BELOW .. "aa", eq(
reverse_text("aaa" .. utf8_COMBINING_RING_ABOVE .. utf8_COMBINING_RING_BELOW .. "bb")) 'bba' .. utf8_COMBINING_RING_ABOVE .. utf8_COMBINING_RING_BELOW .. 'aa',
reverse_text('aaa' .. utf8_COMBINING_RING_ABOVE .. utf8_COMBINING_RING_BELOW .. 'bb')
)
end) end)
itp("treats invalid utf as separate characters", function() itp('treats invalid utf as separate characters', function()
eq("\192ba", reverse_text("ab\192")) eq('\192ba', reverse_text('ab\192'))
end) end)
itp("treats an incomplete utf continuation sequence as valid", function() itp('treats an incomplete utf continuation sequence as valid', function()
eq("\194ba", reverse_text("ab\194")) eq('\194ba', reverse_text('ab\194'))
end) end)
end) end)

View File

@ -23,7 +23,7 @@ local buffer_hash = nil
child_call_once(function() child_call_once(function()
if old_p_udir == nil then if old_p_udir == nil then
old_p_udir = options.p_udir -- save the old value of p_udir (undodir) old_p_udir = options.p_udir -- save the old value of p_udir (undodir)
end end
-- create a new buffer -- create a new buffer
@ -39,21 +39,20 @@ child_call_once(function()
undo.u_compute_hash(file_buffer, buffer_hash) undo.u_compute_hash(file_buffer, buffer_hash)
end) end)
describe('u_write_undo', function() describe('u_write_undo', function()
setup(function() setup(function()
mkdir('unit-test-directory') mkdir('unit-test-directory')
luv.chdir('unit-test-directory') luv.chdir('unit-test-directory')
options.p_udir = to_cstr(luv.cwd()) -- set p_udir to be the test dir options.p_udir = to_cstr(luv.cwd()) -- set p_udir to be the test dir
end) end)
teardown(function() teardown(function()
luv.chdir('..') luv.chdir('..')
local success, err = luv.fs_rmdir('unit-test-directory') local success, err = luv.fs_rmdir('unit-test-directory')
if not success then if not success then
print(err) -- inform tester if directory fails to delete print(err) -- inform tester if directory fails to delete
end end
options.p_udir = old_p_udir --restore old p_udir options.p_udir = old_p_udir --restore old p_udir
end) end)
-- Lua wrapper for u_write_undo -- Lua wrapper for u_write_undo
@ -68,24 +67,24 @@ describe('u_write_undo', function()
itp('writes an undo file to undodir given a buffer and hash', function() itp('writes an undo file to undodir given a buffer and hash', function()
u_write_undo(nil, false, file_buffer, buffer_hash) u_write_undo(nil, false, file_buffer, buffer_hash)
local correct_name = ffi.string(undo.u_get_undo_file_name(file_buffer.b_ffname, false)) local correct_name = ffi.string(undo.u_get_undo_file_name(file_buffer.b_ffname, false))
local undo_file = io.open(correct_name, "r") local undo_file = io.open(correct_name, 'r')
neq(undo_file, nil) neq(undo_file, nil)
local success, err = os.remove(correct_name) -- delete the file now that we're done with it. local success, err = os.remove(correct_name) -- delete the file now that we're done with it.
if not success then if not success then
print(err) -- inform tester if undofile fails to delete print(err) -- inform tester if undofile fails to delete
end end
end) end)
itp('writes a correctly-named undo file to undodir given a name, buffer, and hash', function() itp('writes a correctly-named undo file to undodir given a name, buffer, and hash', function()
local correct_name = "undofile.test" local correct_name = 'undofile.test'
u_write_undo(correct_name, false, file_buffer, buffer_hash) u_write_undo(correct_name, false, file_buffer, buffer_hash)
local undo_file = io.open(correct_name, "r") local undo_file = io.open(correct_name, 'r')
neq(undo_file, nil) neq(undo_file, nil)
local success, err = os.remove(correct_name) -- delete the file now that we're done with it. local success, err = os.remove(correct_name) -- delete the file now that we're done with it.
if not success then if not success then
print(err) -- inform tester if undofile fails to delete print(err) -- inform tester if undofile fails to delete
end end
end) end)
@ -96,9 +95,9 @@ describe('u_write_undo', function()
itp('writes the undofile with the same permissions as the original file', function() itp('writes the undofile with the same permissions as the original file', function()
-- Create Test file and set permissions -- Create Test file and set permissions
local test_file_name = "./test.file" local test_file_name = './test.file'
local test_permission_file = io.open(test_file_name, "w") local test_permission_file = io.open(test_file_name, 'w')
test_permission_file:write("testing permissions") test_permission_file:write('testing permissions')
test_permission_file:close() test_permission_file:close()
local test_permissions = luv.fs_stat(test_file_name).mode local test_permissions = luv.fs_stat(test_file_name).mode
@ -119,17 +118,17 @@ describe('u_write_undo', function()
-- delete the file now that we're done with it. -- delete the file now that we're done with it.
local success, err = os.remove(test_file_name) local success, err = os.remove(test_file_name)
if not success then if not success then
print(err) -- inform tester if undofile fails to delete print(err) -- inform tester if undofile fails to delete
end end
success, err = os.remove(undo_file_name) success, err = os.remove(undo_file_name)
if not success then if not success then
print(err) -- inform tester if undofile fails to delete print(err) -- inform tester if undofile fails to delete
end end
end) end)
itp('writes an undofile only readable by the user if the buffer is unnamed', function() itp('writes an undofile only readable by the user if the buffer is unnamed', function()
local correct_permissions = 33152 local correct_permissions = 33152
local undo_file_name = "test.undo" local undo_file_name = 'test.undo'
-- Create vim buffer -- Create vim buffer
file_buffer = buffer.buflist_new(nil, nil, 1, buffer.BLN_LISTED) file_buffer = buffer.buflist_new(nil, nil, 1, buffer.BLN_LISTED)
@ -144,12 +143,12 @@ describe('u_write_undo', function()
-- delete the file now that we're done with it. -- delete the file now that we're done with it.
local success, err = os.remove(undo_file_name) local success, err = os.remove(undo_file_name)
if not success then if not success then
print(err) -- inform tester if undofile fails to delete print(err) -- inform tester if undofile fails to delete
end end
end) end)
itp('forces writing undo file for :wundo! command', function() itp('forces writing undo file for :wundo! command', function()
local file_contents = "testing permissions" local file_contents = 'testing permissions'
-- Write a text file where the undofile should go -- Write a text file where the undofile should go
local correct_name = ffi.string(undo.u_get_undo_file_name(file_buffer.b_ffname, false)) local correct_name = ffi.string(undo.u_get_undo_file_name(file_buffer.b_ffname, false))
helpers.write_file(correct_name, file_contents, true, false) helpers.write_file(correct_name, file_contents, true, false)
@ -160,9 +159,9 @@ describe('u_write_undo', function()
local undo_file_contents = helpers.read_file(correct_name) local undo_file_contents = helpers.read_file(correct_name)
neq(file_contents, undo_file_contents) neq(file_contents, undo_file_contents)
local success, deletion_err = os.remove(correct_name) -- delete the file now that we're done with it. local success, deletion_err = os.remove(correct_name) -- delete the file now that we're done with it.
if not success then if not success then
print(deletion_err) -- inform tester if undofile fails to delete print(deletion_err) -- inform tester if undofile fails to delete
end end
end) end)
@ -172,17 +171,17 @@ describe('u_write_undo', function()
local file_last_modified = luv.fs_stat(correct_name).mtime.sec local file_last_modified = luv.fs_stat(correct_name).mtime.sec
sleep(1000) -- Ensure difference in timestamps. sleep(1000) -- Ensure difference in timestamps.
file_buffer.b_u_numhead = 1 -- Mark it as if there are changes file_buffer.b_u_numhead = 1 -- Mark it as if there are changes
u_write_undo(nil, false, file_buffer, buffer_hash) u_write_undo(nil, false, file_buffer, buffer_hash)
local file_last_modified_2 = luv.fs_stat(correct_name).mtime.sec local file_last_modified_2 = luv.fs_stat(correct_name).mtime.sec
-- print(file_last_modified, file_last_modified_2) -- print(file_last_modified, file_last_modified_2)
neq(file_last_modified, file_last_modified_2) neq(file_last_modified, file_last_modified_2)
local success, err = os.remove(correct_name) -- delete the file now that we're done with it. local success, err = os.remove(correct_name) -- delete the file now that we're done with it.
if not success then if not success then
print(err) -- inform tester if undofile fails to delete print(err) -- inform tester if undofile fails to delete
end end
end) end)
@ -195,16 +194,16 @@ describe('u_write_undo', function()
end) end)
itp('does not write an undo file if there is no undo information for the buffer', function() itp('does not write an undo file if there is no undo information for the buffer', function()
file_buffer.b_u_numhead = 0 -- Mark it as if there is no undo information file_buffer.b_u_numhead = 0 -- Mark it as if there is no undo information
local correct_name = ffi.string(undo.u_get_undo_file_name(file_buffer.b_ffname, false)) local correct_name = ffi.string(undo.u_get_undo_file_name(file_buffer.b_ffname, false))
local existing_file = io.open(correct_name,"r") local existing_file = io.open(correct_name, 'r')
if existing_file then if existing_file then
existing_file:close() existing_file:close()
os.remove(correct_name) os.remove(correct_name)
end end
u_write_undo(nil, false, file_buffer, buffer_hash) u_write_undo(nil, false, file_buffer, buffer_hash)
local undo_file = io.open(correct_name, "r") local undo_file = io.open(correct_name, 'r')
eq(undo_file, nil) eq(undo_file, nil)
end) end)

View File

@ -83,24 +83,23 @@ local function eltkn2lua(pstate, tkn)
type = conv_eltkn_type(tkn.type), type = conv_eltkn_type(tkn.type),
} }
pstate_set_str(pstate, tkn.start, tkn.len, ret) pstate_set_str(pstate, tkn.start, tkn.len, ret)
if not ret.error and (#(ret.str) ~= ret.len) then if not ret.error and (#ret.str ~= ret.len) then
ret.error = '#str /= len' ret.error = '#str /= len'
end end
if ret.type == 'Comparison' then if ret.type == 'Comparison' then
ret.data = { ret.data = {
type = conv_cmp_type(tkn.data.cmp.type), type = conv_cmp_type(tkn.data.cmp.type),
ccs = conv_ccs(tkn.data.cmp.ccs), ccs = conv_ccs(tkn.data.cmp.ccs),
inv = (not not tkn.data.cmp.inv), inv = not not tkn.data.cmp.inv,
} }
elseif ret.type == 'Multiplication' then elseif ret.type == 'Multiplication' then
ret.data = { type = conv_enum(eltkn_mul_type_tab, tkn.data.mul.type) } ret.data = { type = conv_enum(eltkn_mul_type_tab, tkn.data.mul.type) }
elseif bracket_types[ret.type] then elseif bracket_types[ret.type] then
ret.data = { closing = (not not tkn.data.brc.closing) } ret.data = { closing = not not tkn.data.brc.closing }
elseif ret.type == 'Register' then elseif ret.type == 'Register' then
ret.data = { name = intchar2lua(tkn.data.reg.name) } ret.data = { name = intchar2lua(tkn.data.reg.name) }
elseif (ret.type == 'SingleQuotedString' elseif ret.type == 'SingleQuotedString' or ret.type == 'DoubleQuotedString' then
or ret.type == 'DoubleQuotedString') then ret.data = { closed = not not tkn.data.str.closed }
ret.data = { closed = (not not tkn.data.str.closed) }
elseif ret.type == 'Option' then elseif ret.type == 'Option' then
ret.data = { ret.data = {
scope = conv_enum(eltkn_opt_scope_tab, tkn.data.opt.scope), scope = conv_enum(eltkn_opt_scope_tab, tkn.data.opt.scope),
@ -109,16 +108,15 @@ local function eltkn2lua(pstate, tkn)
elseif ret.type == 'PlainIdentifier' then elseif ret.type == 'PlainIdentifier' then
ret.data = { ret.data = {
scope = intchar2lua(tkn.data.var.scope), scope = intchar2lua(tkn.data.var.scope),
autoload = (not not tkn.data.var.autoload), autoload = not not tkn.data.var.autoload,
} }
elseif ret.type == 'Number' then elseif ret.type == 'Number' then
ret.data = { ret.data = {
is_float = (not not tkn.data.num.is_float), is_float = not not tkn.data.num.is_float,
base = tonumber(tkn.data.num.base), base = tonumber(tkn.data.num.base),
} }
ret.data.val = tonumber(tkn.data.num.is_float ret.data.val =
and tkn.data.num.val.floating tonumber(tkn.data.num.is_float and tkn.data.num.val.floating or tkn.data.num.val.integer)
or tkn.data.num.val.integer)
elseif ret.type == 'Assignment' then elseif ret.type == 'Assignment' then
ret.data = { type = conv_expr_asgn_type(tkn.data.ass.type) } ret.data = { type = conv_expr_asgn_type(tkn.data.ass.type) }
elseif ret.type == 'Invalid' then elseif ret.type == 'Invalid' then
@ -150,156 +148,263 @@ describe('Expressions lexer', function()
end end
end end
local function singl_eltkn_test(typ, str, data) local function singl_eltkn_test(typ, str, data)
local pstate = new_pstate({str}) local pstate = new_pstate({ str })
eq({data=data, len=#str, start={col=0, line=0}, str=str, type=typ}, eq(
next_eltkn(pstate, flags)) { data = data, len = #str, start = { col = 0, line = 0 }, str = str, type = typ },
next_eltkn(pstate, flags)
)
check_advance(pstate, #str, 0) check_advance(pstate, #str, 0)
if not ( if
not (
typ == 'Spacing' typ == 'Spacing'
or (typ == 'Register' and str == '@') or (typ == 'Register' and str == '@')
or ((typ == 'SingleQuotedString' or typ == 'DoubleQuotedString') or ((typ == 'SingleQuotedString' or typ == 'DoubleQuotedString') and not data.closed)
and not data.closed) )
) then then
pstate = new_pstate({str .. ' '}) pstate = new_pstate({ str .. ' ' })
eq({data=data, len=#str, start={col=0, line=0}, str=str, type=typ}, eq(
next_eltkn(pstate, flags)) { data = data, len = #str, start = { col = 0, line = 0 }, str = str, type = typ },
next_eltkn(pstate, flags)
)
check_advance(pstate, #str, 0) check_advance(pstate, #str, 0)
end end
pstate = new_pstate({'x' .. str}) pstate = new_pstate({ 'x' .. str })
pstate.pos.col = 1 pstate.pos.col = 1
eq({data=data, len=#str, start={col=1, line=0}, str=str, type=typ}, eq(
next_eltkn(pstate, flags)) { data = data, len = #str, start = { col = 1, line = 0 }, str = str, type = typ },
next_eltkn(pstate, flags)
)
check_advance(pstate, #str, 1) check_advance(pstate, #str, 1)
end end
local function scope_test(scope) local function scope_test(scope)
singl_eltkn_test('PlainIdentifier', scope .. ':test#var', {autoload=true, scope=scope}) singl_eltkn_test('PlainIdentifier', scope .. ':test#var', { autoload = true, scope = scope })
singl_eltkn_test('PlainIdentifier', scope .. ':', {autoload=false, scope=scope}) singl_eltkn_test('PlainIdentifier', scope .. ':', { autoload = false, scope = scope })
end end
local function comparison_test(op, inv_op, cmp_type) local function comparison_test(op, inv_op, cmp_type)
singl_eltkn_test('Comparison', op, {type=cmp_type, inv=false, ccs='UseOption'}) singl_eltkn_test('Comparison', op, { type = cmp_type, inv = false, ccs = 'UseOption' })
singl_eltkn_test('Comparison', inv_op, {type=cmp_type, inv=true, ccs='UseOption'}) singl_eltkn_test('Comparison', inv_op, { type = cmp_type, inv = true, ccs = 'UseOption' })
singl_eltkn_test('Comparison', op .. '#', {type=cmp_type, inv=false, ccs='MatchCase'}) singl_eltkn_test('Comparison', op .. '#', { type = cmp_type, inv = false, ccs = 'MatchCase' })
singl_eltkn_test('Comparison', inv_op .. '#', {type=cmp_type, inv=true, ccs='MatchCase'}) singl_eltkn_test(
singl_eltkn_test('Comparison', op .. '?', {type=cmp_type, inv=false, ccs='IgnoreCase'}) 'Comparison',
singl_eltkn_test('Comparison', inv_op .. '?', {type=cmp_type, inv=true, ccs='IgnoreCase'}) inv_op .. '#',
{ type = cmp_type, inv = true, ccs = 'MatchCase' }
)
singl_eltkn_test('Comparison', op .. '?', { type = cmp_type, inv = false, ccs = 'IgnoreCase' })
singl_eltkn_test(
'Comparison',
inv_op .. '?',
{ type = cmp_type, inv = true, ccs = 'IgnoreCase' }
)
end end
local function simple_test(pstate_arg, exp_type, exp_len, exp) local function simple_test(pstate_arg, exp_type, exp_len, exp)
local pstate = new_pstate(pstate_arg) local pstate = new_pstate(pstate_arg)
exp = shallowcopy(exp) exp = shallowcopy(exp)
exp.type = exp_type exp.type = exp_type
exp.len = exp_len or #(pstate_arg[0]) exp.len = exp_len or #pstate_arg[0]
exp.start = { col = 0, line = 0 } exp.start = { col = 0, line = 0 }
eq(exp, next_eltkn(pstate, flags)) eq(exp, next_eltkn(pstate, flags))
end end
local function stable_tests() local function stable_tests()
singl_eltkn_test('Parenthesis', '(', {closing=false}) singl_eltkn_test('Parenthesis', '(', { closing = false })
singl_eltkn_test('Parenthesis', ')', {closing=true}) singl_eltkn_test('Parenthesis', ')', { closing = true })
singl_eltkn_test('Bracket', '[', {closing=false}) singl_eltkn_test('Bracket', '[', { closing = false })
singl_eltkn_test('Bracket', ']', {closing=true}) singl_eltkn_test('Bracket', ']', { closing = true })
singl_eltkn_test('FigureBrace', '{', {closing=false}) singl_eltkn_test('FigureBrace', '{', { closing = false })
singl_eltkn_test('FigureBrace', '}', {closing=true}) singl_eltkn_test('FigureBrace', '}', { closing = true })
singl_eltkn_test('Question', '?') singl_eltkn_test('Question', '?')
singl_eltkn_test('Colon', ':') singl_eltkn_test('Colon', ':')
singl_eltkn_test('Dot', '.') singl_eltkn_test('Dot', '.')
singl_eltkn_test('Assignment', '.=', {type='Concat'}) singl_eltkn_test('Assignment', '.=', { type = 'Concat' })
singl_eltkn_test('Plus', '+') singl_eltkn_test('Plus', '+')
singl_eltkn_test('Assignment', '+=', {type='Add'}) singl_eltkn_test('Assignment', '+=', { type = 'Add' })
singl_eltkn_test('Comma', ',') singl_eltkn_test('Comma', ',')
singl_eltkn_test('Multiplication', '*', {type='Mul'}) singl_eltkn_test('Multiplication', '*', { type = 'Mul' })
singl_eltkn_test('Multiplication', '/', {type='Div'}) singl_eltkn_test('Multiplication', '/', { type = 'Div' })
singl_eltkn_test('Multiplication', '%', {type='Mod'}) singl_eltkn_test('Multiplication', '%', { type = 'Mod' })
singl_eltkn_test('Spacing', ' \t\t \t\t') singl_eltkn_test('Spacing', ' \t\t \t\t')
singl_eltkn_test('Spacing', ' ') singl_eltkn_test('Spacing', ' ')
singl_eltkn_test('Spacing', '\t') singl_eltkn_test('Spacing', '\t')
singl_eltkn_test('Invalid', '\x01\x02\x03', {error='E15: Invalid control character present in input: %.*s'}) singl_eltkn_test(
singl_eltkn_test('Number', '0123', {is_float=false, base=8, val=83}) 'Invalid',
singl_eltkn_test('Number', '01234567', {is_float=false, base=8, val=342391}) '\x01\x02\x03',
singl_eltkn_test('Number', '012345678', {is_float=false, base=10, val=12345678}) { error = 'E15: Invalid control character present in input: %.*s' }
singl_eltkn_test('Number', '0x123', {is_float=false, base=16, val=291}) )
singl_eltkn_test('Number', '0x56FF', {is_float=false, base=16, val=22271}) singl_eltkn_test('Number', '0123', { is_float = false, base = 8, val = 83 })
singl_eltkn_test('Number', '0xabcdef', {is_float=false, base=16, val=11259375}) singl_eltkn_test('Number', '01234567', { is_float = false, base = 8, val = 342391 })
singl_eltkn_test('Number', '0xABCDEF', {is_float=false, base=16, val=11259375}) singl_eltkn_test('Number', '012345678', { is_float = false, base = 10, val = 12345678 })
singl_eltkn_test('Number', '0x0', {is_float=false, base=16, val=0}) singl_eltkn_test('Number', '0x123', { is_float = false, base = 16, val = 291 })
singl_eltkn_test('Number', '00', {is_float=false, base=8, val=0}) singl_eltkn_test('Number', '0x56FF', { is_float = false, base = 16, val = 22271 })
singl_eltkn_test('Number', '0b0', {is_float=false, base=2, val=0}) singl_eltkn_test('Number', '0xabcdef', { is_float = false, base = 16, val = 11259375 })
singl_eltkn_test('Number', '0b010111', {is_float=false, base=2, val=23}) singl_eltkn_test('Number', '0xABCDEF', { is_float = false, base = 16, val = 11259375 })
singl_eltkn_test('Number', '0b100111', {is_float=false, base=2, val=39}) singl_eltkn_test('Number', '0x0', { is_float = false, base = 16, val = 0 })
singl_eltkn_test('Number', '0', {is_float=false, base=10, val=0}) singl_eltkn_test('Number', '00', { is_float = false, base = 8, val = 0 })
singl_eltkn_test('Number', '9', {is_float=false, base=10, val=9}) singl_eltkn_test('Number', '0b0', { is_float = false, base = 2, val = 0 })
singl_eltkn_test('Number', '0b010111', { is_float = false, base = 2, val = 23 })
singl_eltkn_test('Number', '0b100111', { is_float = false, base = 2, val = 39 })
singl_eltkn_test('Number', '0', { is_float = false, base = 10, val = 0 })
singl_eltkn_test('Number', '9', { is_float = false, base = 10, val = 9 })
singl_eltkn_test('Env', '$abc') singl_eltkn_test('Env', '$abc')
singl_eltkn_test('Env', '$') singl_eltkn_test('Env', '$')
singl_eltkn_test('PlainIdentifier', 'test', {autoload=false, scope=0}) singl_eltkn_test('PlainIdentifier', 'test', { autoload = false, scope = 0 })
singl_eltkn_test('PlainIdentifier', '_test', {autoload=false, scope=0}) singl_eltkn_test('PlainIdentifier', '_test', { autoload = false, scope = 0 })
singl_eltkn_test('PlainIdentifier', '_test_foo', {autoload=false, scope=0}) singl_eltkn_test('PlainIdentifier', '_test_foo', { autoload = false, scope = 0 })
singl_eltkn_test('PlainIdentifier', 't', {autoload=false, scope=0}) singl_eltkn_test('PlainIdentifier', 't', { autoload = false, scope = 0 })
singl_eltkn_test('PlainIdentifier', 'test5', {autoload=false, scope=0}) singl_eltkn_test('PlainIdentifier', 'test5', { autoload = false, scope = 0 })
singl_eltkn_test('PlainIdentifier', 't0', {autoload=false, scope=0}) singl_eltkn_test('PlainIdentifier', 't0', { autoload = false, scope = 0 })
singl_eltkn_test('PlainIdentifier', 'test#var', {autoload=true, scope=0}) singl_eltkn_test('PlainIdentifier', 'test#var', { autoload = true, scope = 0 })
singl_eltkn_test('PlainIdentifier', 'test#var#val###', {autoload=true, scope=0}) singl_eltkn_test('PlainIdentifier', 'test#var#val###', { autoload = true, scope = 0 })
singl_eltkn_test('PlainIdentifier', 't#####', {autoload=true, scope=0}) singl_eltkn_test('PlainIdentifier', 't#####', { autoload = true, scope = 0 })
singl_eltkn_test('And', '&&') singl_eltkn_test('And', '&&')
singl_eltkn_test('Or', '||') singl_eltkn_test('Or', '||')
singl_eltkn_test('Invalid', '&', {error='E112: Option name missing: %.*s'}) singl_eltkn_test('Invalid', '&', { error = 'E112: Option name missing: %.*s' })
singl_eltkn_test('Option', '&opt', {scope='Unspecified', name='opt'}) singl_eltkn_test('Option', '&opt', { scope = 'Unspecified', name = 'opt' })
singl_eltkn_test('Option', '&t_xx', {scope='Unspecified', name='t_xx'}) singl_eltkn_test('Option', '&t_xx', { scope = 'Unspecified', name = 't_xx' })
singl_eltkn_test('Option', '&t_\r\r', {scope='Unspecified', name='t_\r\r'}) singl_eltkn_test('Option', '&t_\r\r', { scope = 'Unspecified', name = 't_\r\r' })
singl_eltkn_test('Option', '&t_\t\t', {scope='Unspecified', name='t_\t\t'}) singl_eltkn_test('Option', '&t_\t\t', { scope = 'Unspecified', name = 't_\t\t' })
singl_eltkn_test('Option', '&t_ ', {scope='Unspecified', name='t_ '}) singl_eltkn_test('Option', '&t_ ', { scope = 'Unspecified', name = 't_ ' })
singl_eltkn_test('Option', '&g:opt', {scope='Global', name='opt'}) singl_eltkn_test('Option', '&g:opt', { scope = 'Global', name = 'opt' })
singl_eltkn_test('Option', '&l:opt', {scope='Local', name='opt'}) singl_eltkn_test('Option', '&l:opt', { scope = 'Local', name = 'opt' })
singl_eltkn_test('Invalid', '&l:', {error='E112: Option name missing: %.*s'}) singl_eltkn_test('Invalid', '&l:', { error = 'E112: Option name missing: %.*s' })
singl_eltkn_test('Invalid', '&g:', {error='E112: Option name missing: %.*s'}) singl_eltkn_test('Invalid', '&g:', { error = 'E112: Option name missing: %.*s' })
singl_eltkn_test('Register', '@', {name=-1}) singl_eltkn_test('Register', '@', { name = -1 })
singl_eltkn_test('Register', '@a', {name='a'}) singl_eltkn_test('Register', '@a', { name = 'a' })
singl_eltkn_test('Register', '@\r', {name=13}) singl_eltkn_test('Register', '@\r', { name = 13 })
singl_eltkn_test('Register', '@ ', {name=' '}) singl_eltkn_test('Register', '@ ', { name = ' ' })
singl_eltkn_test('Register', '@\t', {name=9}) singl_eltkn_test('Register', '@\t', { name = 9 })
singl_eltkn_test('SingleQuotedString', '\'test', {closed=false}) singl_eltkn_test('SingleQuotedString', "'test", { closed = false })
singl_eltkn_test('SingleQuotedString', '\'test\'', {closed=true}) singl_eltkn_test('SingleQuotedString', "'test'", { closed = true })
singl_eltkn_test('SingleQuotedString', '\'\'\'\'', {closed=true}) singl_eltkn_test('SingleQuotedString', "''''", { closed = true })
singl_eltkn_test('SingleQuotedString', '\'x\'\'\'', {closed=true}) singl_eltkn_test('SingleQuotedString', "'x'''", { closed = true })
singl_eltkn_test('SingleQuotedString', '\'\'\'x\'', {closed=true}) singl_eltkn_test('SingleQuotedString', "'''x'", { closed = true })
singl_eltkn_test('SingleQuotedString', '\'\'\'', {closed=false}) singl_eltkn_test('SingleQuotedString', "'''", { closed = false })
singl_eltkn_test('SingleQuotedString', '\'x\'\'', {closed=false}) singl_eltkn_test('SingleQuotedString', "'x''", { closed = false })
singl_eltkn_test('SingleQuotedString', '\'\'\'x', {closed=false}) singl_eltkn_test('SingleQuotedString', "'''x", { closed = false })
singl_eltkn_test('DoubleQuotedString', '"test', {closed=false}) singl_eltkn_test('DoubleQuotedString', '"test', { closed = false })
singl_eltkn_test('DoubleQuotedString', '"test"', {closed=true}) singl_eltkn_test('DoubleQuotedString', '"test"', { closed = true })
singl_eltkn_test('DoubleQuotedString', '"\\""', {closed=true}) singl_eltkn_test('DoubleQuotedString', '"\\""', { closed = true })
singl_eltkn_test('DoubleQuotedString', '"x\\""', {closed=true}) singl_eltkn_test('DoubleQuotedString', '"x\\""', { closed = true })
singl_eltkn_test('DoubleQuotedString', '"\\"x"', {closed=true}) singl_eltkn_test('DoubleQuotedString', '"\\"x"', { closed = true })
singl_eltkn_test('DoubleQuotedString', '"\\"', {closed=false}) singl_eltkn_test('DoubleQuotedString', '"\\"', { closed = false })
singl_eltkn_test('DoubleQuotedString', '"x\\"', {closed=false}) singl_eltkn_test('DoubleQuotedString', '"x\\"', { closed = false })
singl_eltkn_test('DoubleQuotedString', '"\\"x', {closed=false}) singl_eltkn_test('DoubleQuotedString', '"\\"x', { closed = false })
singl_eltkn_test('Not', '!') singl_eltkn_test('Not', '!')
singl_eltkn_test('Assignment', '=', {type='Plain'}) singl_eltkn_test('Assignment', '=', { type = 'Plain' })
comparison_test('==', '!=', 'Equal') comparison_test('==', '!=', 'Equal')
comparison_test('=~', '!~', 'Matches') comparison_test('=~', '!~', 'Matches')
comparison_test('>', '<=', 'Greater') comparison_test('>', '<=', 'Greater')
comparison_test('>=', '<', 'GreaterOrEqual') comparison_test('>=', '<', 'GreaterOrEqual')
singl_eltkn_test('Minus', '-') singl_eltkn_test('Minus', '-')
singl_eltkn_test('Assignment', '-=', {type='Subtract'}) singl_eltkn_test('Assignment', '-=', { type = 'Subtract' })
singl_eltkn_test('Arrow', '->') singl_eltkn_test('Arrow', '->')
singl_eltkn_test('Invalid', '~', {error='E15: Unidentified character: %.*s'}) singl_eltkn_test('Invalid', '~', { error = 'E15: Unidentified character: %.*s' })
simple_test({{data=nil, size=0}}, 'EOC', 0, {error='start.col >= #pstr'}) simple_test({ { data = nil, size = 0 } }, 'EOC', 0, { error = 'start.col >= #pstr' })
simple_test({''}, 'EOC', 0, {error='start.col >= #pstr'}) simple_test({ '' }, 'EOC', 0, { error = 'start.col >= #pstr' })
simple_test({'2.'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'}) simple_test(
simple_test({'2e5'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'}) { '2.' },
simple_test({'2.x'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'}) 'Number',
simple_test({'2.2.'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'}) 1,
simple_test({'2.0x'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'}) { data = { is_float = false, base = 10, val = 2 }, str = '2' }
simple_test({'2.0e'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'}) )
simple_test({'2.0e+'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'}) simple_test(
simple_test({'2.0e-'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'}) { '2e5' },
simple_test({'2.0e+x'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'}) 'Number',
simple_test({'2.0e-x'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'}) 1,
simple_test({'2.0e+1a'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'}) { data = { is_float = false, base = 10, val = 2 }, str = '2' }
simple_test({'2.0e-1a'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'}) )
simple_test({'0b102'}, 'Number', 4, {data={is_float=false, base=2, val=2}, str='0b10'}) simple_test(
simple_test({'10F'}, 'Number', 2, {data={is_float=false, base=10, val=10}, str='10'}) { '2.x' },
simple_test({'0x0123456789ABCDEFG'}, 'Number', 18, {data={is_float=false, base=16, val=81985529216486895}, str='0x0123456789ABCDEF'}) 'Number',
simple_test({{data='00', size=2}}, 'Number', 2, {data={is_float=false, base=8, val=0}, str='00'}) 1,
simple_test({{data='009', size=2}}, 'Number', 2, {data={is_float=false, base=8, val=0}, str='00'}) { data = { is_float = false, base = 10, val = 2 }, str = '2' }
simple_test({{data='01', size=1}}, 'Number', 1, {data={is_float=false, base=10, val=0}, str='0'}) )
simple_test(
{ '2.2.' },
'Number',
1,
{ data = { is_float = false, base = 10, val = 2 }, str = '2' }
)
simple_test(
{ '2.0x' },
'Number',
1,
{ data = { is_float = false, base = 10, val = 2 }, str = '2' }
)
simple_test(
{ '2.0e' },
'Number',
1,
{ data = { is_float = false, base = 10, val = 2 }, str = '2' }
)
simple_test(
{ '2.0e+' },
'Number',
1,
{ data = { is_float = false, base = 10, val = 2 }, str = '2' }
)
simple_test(
{ '2.0e-' },
'Number',
1,
{ data = { is_float = false, base = 10, val = 2 }, str = '2' }
)
simple_test(
{ '2.0e+x' },
'Number',
1,
{ data = { is_float = false, base = 10, val = 2 }, str = '2' }
)
simple_test(
{ '2.0e-x' },
'Number',
1,
{ data = { is_float = false, base = 10, val = 2 }, str = '2' }
)
simple_test(
{ '2.0e+1a' },
'Number',
1,
{ data = { is_float = false, base = 10, val = 2 }, str = '2' }
)
simple_test(
{ '2.0e-1a' },
'Number',
1,
{ data = { is_float = false, base = 10, val = 2 }, str = '2' }
)
simple_test(
{ '0b102' },
'Number',
4,
{ data = { is_float = false, base = 2, val = 2 }, str = '0b10' }
)
simple_test(
{ '10F' },
'Number',
2,
{ data = { is_float = false, base = 10, val = 10 }, str = '10' }
)
simple_test({ '0x0123456789ABCDEFG' }, 'Number', 18, {
data = { is_float = false, base = 16, val = 81985529216486895 },
str = '0x0123456789ABCDEF',
})
simple_test(
{ { data = '00', size = 2 } },
'Number',
2,
{ data = { is_float = false, base = 8, val = 0 }, str = '00' }
)
simple_test(
{ { data = '009', size = 2 } },
'Number',
2,
{ data = { is_float = false, base = 8, val = 0 }, str = '00' }
)
simple_test(
{ { data = '01', size = 1 } },
'Number',
1,
{ data = { is_float = false, base = 10, val = 0 }, str = '0' }
)
end end
local function regular_scope_tests() local function regular_scope_tests()
@ -312,29 +417,104 @@ describe('Expressions lexer', function()
scope_test('l') scope_test('l')
scope_test('a') scope_test('a')
simple_test({'g:'}, 'PlainIdentifier', 2, {data={scope='g', autoload=false}, str='g:'}) simple_test(
simple_test({'g:is#foo'}, 'PlainIdentifier', 8, {data={scope='g', autoload=true}, str='g:is#foo'}) { 'g:' },
simple_test({'g:isnot#foo'}, 'PlainIdentifier', 11, {data={scope='g', autoload=true}, str='g:isnot#foo'}) 'PlainIdentifier',
2,
{ data = { scope = 'g', autoload = false }, str = 'g:' }
)
simple_test(
{ 'g:is#foo' },
'PlainIdentifier',
8,
{ data = { scope = 'g', autoload = true }, str = 'g:is#foo' }
)
simple_test(
{ 'g:isnot#foo' },
'PlainIdentifier',
11,
{ data = { scope = 'g', autoload = true }, str = 'g:isnot#foo' }
)
end end
local function regular_is_tests() local function regular_is_tests()
comparison_test('is', 'isnot', 'Identical') comparison_test('is', 'isnot', 'Identical')
simple_test({'is'}, 'Comparison', 2, {data={type='Identical', inv=false, ccs='UseOption'}, str='is'}) simple_test(
simple_test({'isnot'}, 'Comparison', 5, {data={type='Identical', inv=true, ccs='UseOption'}, str='isnot'}) { 'is' },
simple_test({'is?'}, 'Comparison', 3, {data={type='Identical', inv=false, ccs='IgnoreCase'}, str='is?'}) 'Comparison',
simple_test({'isnot?'}, 'Comparison', 6, {data={type='Identical', inv=true, ccs='IgnoreCase'}, str='isnot?'}) 2,
simple_test({'is#'}, 'Comparison', 3, {data={type='Identical', inv=false, ccs='MatchCase'}, str='is#'}) { data = { type = 'Identical', inv = false, ccs = 'UseOption' }, str = 'is' }
simple_test({'isnot#'}, 'Comparison', 6, {data={type='Identical', inv=true, ccs='MatchCase'}, str='isnot#'}) )
simple_test({'is#foo'}, 'Comparison', 3, {data={type='Identical', inv=false, ccs='MatchCase'}, str='is#'}) simple_test(
simple_test({'isnot#foo'}, 'Comparison', 6, {data={type='Identical', inv=true, ccs='MatchCase'}, str='isnot#'}) { 'isnot' },
'Comparison',
5,
{ data = { type = 'Identical', inv = true, ccs = 'UseOption' }, str = 'isnot' }
)
simple_test(
{ 'is?' },
'Comparison',
3,
{ data = { type = 'Identical', inv = false, ccs = 'IgnoreCase' }, str = 'is?' }
)
simple_test(
{ 'isnot?' },
'Comparison',
6,
{ data = { type = 'Identical', inv = true, ccs = 'IgnoreCase' }, str = 'isnot?' }
)
simple_test(
{ 'is#' },
'Comparison',
3,
{ data = { type = 'Identical', inv = false, ccs = 'MatchCase' }, str = 'is#' }
)
simple_test(
{ 'isnot#' },
'Comparison',
6,
{ data = { type = 'Identical', inv = true, ccs = 'MatchCase' }, str = 'isnot#' }
)
simple_test(
{ 'is#foo' },
'Comparison',
3,
{ data = { type = 'Identical', inv = false, ccs = 'MatchCase' }, str = 'is#' }
)
simple_test(
{ 'isnot#foo' },
'Comparison',
6,
{ data = { type = 'Identical', inv = true, ccs = 'MatchCase' }, str = 'isnot#' }
)
end end
local function regular_number_tests() local function regular_number_tests()
simple_test({'2.0'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'}) simple_test(
simple_test({'2.0e5'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'}) { '2.0' },
simple_test({'2.0e+5'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'}) 'Number',
simple_test({'2.0e-5'}, 'Number', 1, {data={is_float=false, base=10, val=2}, str='2'}) 1,
{ data = { is_float = false, base = 10, val = 2 }, str = '2' }
)
simple_test(
{ '2.0e5' },
'Number',
1,
{ data = { is_float = false, base = 10, val = 2 }, str = '2' }
)
simple_test(
{ '2.0e+5' },
'Number',
1,
{ data = { is_float = false, base = 10, val = 2 }, str = '2' }
)
simple_test(
{ '2.0e-5' },
'Number',
1,
{ data = { is_float = false, base = 10, val = 2 }, str = '2' }
)
end end
local function regular_eoc_tests() local function regular_eoc_tests()
@ -369,7 +549,12 @@ describe('Expressions lexer', function()
regular_is_tests() regular_is_tests()
regular_number_tests() regular_number_tests()
simple_test({'g:'}, 'PlainIdentifier', 1, {data={scope=0, autoload=false}, str='g'}) simple_test(
{ 'g:' },
'PlainIdentifier',
1,
{ data = { scope = 0, autoload = false }, str = 'g' }
)
end) end)
itp('allows floats', function() itp('allows floats', function()
flags = tonumber(lib.kELFlagAllowFloat) flags = tonumber(lib.kELFlagAllowFloat)
@ -379,20 +564,72 @@ describe('Expressions lexer', function()
regular_scope_tests() regular_scope_tests()
regular_is_tests() regular_is_tests()
simple_test({'2.2'}, 'Number', 3, {data={is_float=true, base=10, val=2.2}, str='2.2'}) simple_test(
simple_test({'2.0e5'}, 'Number', 5, {data={is_float=true, base=10, val=2e5}, str='2.0e5'}) { '2.2' },
simple_test({'2.0e+5'}, 'Number', 6, {data={is_float=true, base=10, val=2e5}, str='2.0e+5'}) 'Number',
simple_test({'2.0e-5'}, 'Number', 6, {data={is_float=true, base=10, val=2e-5}, str='2.0e-5'}) 3,
simple_test({'2.500000e-5'}, 'Number', 11, {data={is_float=true, base=10, val=2.5e-5}, str='2.500000e-5'}) { data = { is_float = true, base = 10, val = 2.2 }, str = '2.2' }
simple_test({'2.5555e2'}, 'Number', 8, {data={is_float=true, base=10, val=2.5555e2}, str='2.5555e2'}) )
simple_test({'2.5555e+2'}, 'Number', 9, {data={is_float=true, base=10, val=2.5555e2}, str='2.5555e+2'}) simple_test(
simple_test({'2.5555e-2'}, 'Number', 9, {data={is_float=true, base=10, val=2.5555e-2}, str='2.5555e-2'}) { '2.0e5' },
simple_test({{data='2.5e-5', size=3}}, 'Number',
'Number', 3, {data={is_float=true, base=10, val=2.5}, str='2.5'}) 5,
simple_test({{data='2.5e5', size=4}}, { data = { is_float = true, base = 10, val = 2e5 }, str = '2.0e5' }
'Number', 1, {data={is_float=false, base=10, val=2}, str='2'}) )
simple_test({{data='2.5e-50', size=6}}, simple_test(
'Number', 6, {data={is_float=true, base=10, val=2.5e-5}, str='2.5e-5'}) { '2.0e+5' },
'Number',
6,
{ data = { is_float = true, base = 10, val = 2e5 }, str = '2.0e+5' }
)
simple_test(
{ '2.0e-5' },
'Number',
6,
{ data = { is_float = true, base = 10, val = 2e-5 }, str = '2.0e-5' }
)
simple_test(
{ '2.500000e-5' },
'Number',
11,
{ data = { is_float = true, base = 10, val = 2.5e-5 }, str = '2.500000e-5' }
)
simple_test(
{ '2.5555e2' },
'Number',
8,
{ data = { is_float = true, base = 10, val = 2.5555e2 }, str = '2.5555e2' }
)
simple_test(
{ '2.5555e+2' },
'Number',
9,
{ data = { is_float = true, base = 10, val = 2.5555e2 }, str = '2.5555e+2' }
)
simple_test(
{ '2.5555e-2' },
'Number',
9,
{ data = { is_float = true, base = 10, val = 2.5555e-2 }, str = '2.5555e-2' }
)
simple_test(
{ { data = '2.5e-5', size = 3 } },
'Number',
3,
{ data = { is_float = true, base = 10, val = 2.5 }, str = '2.5' }
)
simple_test(
{ { data = '2.5e5', size = 4 } },
'Number',
1,
{ data = { is_float = false, base = 10, val = 2 }, str = '2' }
)
simple_test(
{ { data = '2.5e-50', size = 6 } },
'Number',
6,
{ data = { is_float = true, base = 10, val = 2.5e-5 }, str = '2.5e-5' }
)
end) end)
itp('treats `is` as an identifier', function() itp('treats `is` as an identifier', function()
flags = tonumber(lib.kELFlagIsNotCmp) flags = tonumber(lib.kELFlagIsNotCmp)
@ -402,14 +639,54 @@ describe('Expressions lexer', function()
regular_scope_tests() regular_scope_tests()
regular_number_tests() regular_number_tests()
simple_test({'is'}, 'PlainIdentifier', 2, {data={scope=0, autoload=false}, str='is'}) simple_test(
simple_test({'isnot'}, 'PlainIdentifier', 5, {data={scope=0, autoload=false}, str='isnot'}) { 'is' },
simple_test({'is?'}, 'PlainIdentifier', 2, {data={scope=0, autoload=false}, str='is'}) 'PlainIdentifier',
simple_test({'isnot?'}, 'PlainIdentifier', 5, {data={scope=0, autoload=false}, str='isnot'}) 2,
simple_test({'is#'}, 'PlainIdentifier', 3, {data={scope=0, autoload=true}, str='is#'}) { data = { scope = 0, autoload = false }, str = 'is' }
simple_test({'isnot#'}, 'PlainIdentifier', 6, {data={scope=0, autoload=true}, str='isnot#'}) )
simple_test({'is#foo'}, 'PlainIdentifier', 6, {data={scope=0, autoload=true}, str='is#foo'}) simple_test(
simple_test({'isnot#foo'}, 'PlainIdentifier', 9, {data={scope=0, autoload=true}, str='isnot#foo'}) { 'isnot' },
'PlainIdentifier',
5,
{ data = { scope = 0, autoload = false }, str = 'isnot' }
)
simple_test(
{ 'is?' },
'PlainIdentifier',
2,
{ data = { scope = 0, autoload = false }, str = 'is' }
)
simple_test(
{ 'isnot?' },
'PlainIdentifier',
5,
{ data = { scope = 0, autoload = false }, str = 'isnot' }
)
simple_test(
{ 'is#' },
'PlainIdentifier',
3,
{ data = { scope = 0, autoload = true }, str = 'is#' }
)
simple_test(
{ 'isnot#' },
'PlainIdentifier',
6,
{ data = { scope = 0, autoload = true }, str = 'isnot#' }
)
simple_test(
{ 'is#foo' },
'PlainIdentifier',
6,
{ data = { scope = 0, autoload = true }, str = 'is#foo' }
)
simple_test(
{ 'isnot#foo' },
'PlainIdentifier',
9,
{ data = { scope = 0, autoload = true }, str = 'isnot#foo' }
)
end) end)
itp('forbids EOC', function() itp('forbids EOC', function()
flags = tonumber(lib.kELFlagForbidEOC) flags = tonumber(lib.kELFlagForbidEOC)
@ -419,8 +696,8 @@ describe('Expressions lexer', function()
regular_is_tests() regular_is_tests()
regular_number_tests() regular_number_tests()
singl_eltkn_test('Invalid', '|', {error='E15: Unexpected EOC character: %.*s'}) singl_eltkn_test('Invalid', '|', { error = 'E15: Unexpected EOC character: %.*s' })
singl_eltkn_test('Invalid', '\0', {error='E15: Unexpected EOC character: %.*s'}) singl_eltkn_test('Invalid', '\0', { error = 'E15: Unexpected EOC character: %.*s' })
singl_eltkn_test('Invalid', '\n', {error='E15: Unexpected EOC character: %.*s'}) singl_eltkn_test('Invalid', '\n', { error = 'E15: Unexpected EOC character: %.*s' })
end) end)
end) end)

View File

@ -25,100 +25,99 @@ local conv_cmp_type = viml_helpers.conv_cmp_type
local pstate_set_str = viml_helpers.pstate_set_str local pstate_set_str = viml_helpers.pstate_set_str
local conv_expr_asgn_type = viml_helpers.conv_expr_asgn_type local conv_expr_asgn_type = viml_helpers.conv_expr_asgn_type
local lib = cimport('./src/nvim/viml/parser/expressions.h', local lib = cimport('./src/nvim/viml/parser/expressions.h', './src/nvim/syntax.h')
'./src/nvim/syntax.h')
local alloc_log = alloc_log_new() local alloc_log = alloc_log_new()
local predefined_hl_defs = { local predefined_hl_defs = {
-- From highlight_init_both -- From highlight_init_both
Conceal=true, Conceal = true,
Cursor=true, Cursor = true,
lCursor=true, lCursor = true,
DiffText=true, DiffText = true,
ErrorMsg=true, ErrorMsg = true,
IncSearch=true, IncSearch = true,
ModeMsg=true, ModeMsg = true,
NonText=true, NonText = true,
PmenuSbar=true, PmenuSbar = true,
StatusLine=true, StatusLine = true,
StatusLineNC=true, StatusLineNC = true,
TabLineFill=true, TabLineFill = true,
TabLineSel=true, TabLineSel = true,
TermCursor=true, TermCursor = true,
VertSplit=true, VertSplit = true,
WildMenu=true, WildMenu = true,
WinSeparator=true, WinSeparator = true,
EndOfBuffer=true, EndOfBuffer = true,
QuickFixLine=true, QuickFixLine = true,
Substitute=true, Substitute = true,
Whitespace=true, Whitespace = true,
Error=true, Error = true,
Todo=true, Todo = true,
String=true, String = true,
Character=true, Character = true,
Number=true, Number = true,
Boolean=true, Boolean = true,
Float=true, Float = true,
Function=true, Function = true,
Conditional=true, Conditional = true,
Repeat=true, Repeat = true,
Label=true, Label = true,
Operator=true, Operator = true,
Keyword=true, Keyword = true,
Exception=true, Exception = true,
Include=true, Include = true,
Define=true, Define = true,
Macro=true, Macro = true,
PreCondit=true, PreCondit = true,
StorageClass=true, StorageClass = true,
Structure=true, Structure = true,
Typedef=true, Typedef = true,
Tag=true, Tag = true,
SpecialChar=true, SpecialChar = true,
Delimiter=true, Delimiter = true,
SpecialComment=true, SpecialComment = true,
Debug=true, Debug = true,
-- From highlight_init_(dark|light) -- From highlight_init_(dark|light)
ColorColumn=true, ColorColumn = true,
CursorColumn=true, CursorColumn = true,
CursorLine=true, CursorLine = true,
CursorLineNr=true, CursorLineNr = true,
DiffAdd=true, DiffAdd = true,
DiffChange=true, DiffChange = true,
DiffDelete=true, DiffDelete = true,
Directory=true, Directory = true,
FoldColumn=true, FoldColumn = true,
Folded=true, Folded = true,
LineNr=true, LineNr = true,
MatchParen=true, MatchParen = true,
MoreMsg=true, MoreMsg = true,
Pmenu=true, Pmenu = true,
PmenuSel=true, PmenuSel = true,
PmenuThumb=true, PmenuThumb = true,
Question=true, Question = true,
Search=true, Search = true,
SignColumn=true, SignColumn = true,
SpecialKey=true, SpecialKey = true,
SpellBad=true, SpellBad = true,
SpellCap=true, SpellCap = true,
SpellLocal=true, SpellLocal = true,
SpellRare=true, SpellRare = true,
TabLine=true, TabLine = true,
Title=true, Title = true,
Visual=true, Visual = true,
WarningMsg=true, WarningMsg = true,
Normal=true, Normal = true,
Comment=true, Comment = true,
Constant=true, Constant = true,
Special=true, Special = true,
Identifier=true, Identifier = true,
Statement=true, Statement = true,
PreProc=true, PreProc = true,
Type=true, Type = true,
Underlined=true, Underlined = true,
Ignore=true, Ignore = true,
} }
local nvim_hl_defs = {} local nvim_hl_defs = {}
@ -136,22 +135,18 @@ child_call_once(function()
-- linking, otherwise it will be created as cleared. So existence -- linking, otherwise it will be created as cleared. So existence
-- of the group is checked here and not in the next pass over -- of the group is checked here and not in the next pass over
-- nvim_hl_defs. -- nvim_hl_defs.
eq(true, not not (nvim_hl_defs[grp_link] eq(true, not not (nvim_hl_defs[grp_link] or predefined_hl_defs[grp_link]))
or predefined_hl_defs[grp_link])) eq(false, not not (nvim_hl_defs[new_grp] or predefined_hl_defs[new_grp]))
eq(false, not not (nvim_hl_defs[new_grp] nvim_hl_defs[new_grp] = { 'link', grp_link }
or predefined_hl_defs[new_grp]))
nvim_hl_defs[new_grp] = {'link', grp_link}
else else
local new_grp, grp_args = s:match('^(%w+) (.*)') local new_grp, grp_args = s:match('^(%w+) (.*)')
neq(nil, new_grp) neq(nil, new_grp)
eq(false, not not (nvim_hl_defs[new_grp] eq(false, not not (nvim_hl_defs[new_grp] or predefined_hl_defs[new_grp]))
or predefined_hl_defs[new_grp])) nvim_hl_defs[new_grp] = { 'definition', grp_args }
nvim_hl_defs[new_grp] = {'definition', grp_args}
end end
end) end)
if not err then if not err then
msg = format_string( msg = format_string('Error while processing string %s at position %u:\n%s', s, i, msg)
'Error while processing string %s at position %u:\n%s', s, i, msg)
error(msg) error(msg)
end end
i = i + 1 i = i + 1
@ -185,12 +180,12 @@ local function hls_to_hl_fs(hls)
local col_shift = col - next_col local col_shift = col - next_col
assert(col_shift >= 0) assert(col_shift >= 0)
next_col = col + #str next_col = col + #str
ret[i] = format_string('hl(%r, %r%s)', ret[i] = format_string(
group, 'hl(%r, %r%s)',
str, group,
(col_shift == 0 str,
and '' (col_shift == 0 and '' or (', %u'):format(col_shift))
or (', %u'):format(col_shift))) )
end end
return ret return ret
end end
@ -205,9 +200,9 @@ local function format_check(expr, format_check_data, opts)
dig_len = #opts.funcname + 2 dig_len = #opts.funcname + 2
else else
print(format_string('\n_check_parsing(%r, %r, {', opts, expr)) print(format_string('\n_check_parsing(%r, %r, {', opts, expr))
dig_len = #('_check_parsing(, \'') + #(format_string('%r', opts)) dig_len = #"_check_parsing(, '" + #(format_string('%r', opts))
end end
local digits = ' --' .. (' '):rep(dig_len - #(' --')) local digits = ' --' .. (' '):rep(dig_len - #' --')
local digits2 = digits:sub(1, -10) local digits2 = digits:sub(1, -10)
for i = 0, #expr - 1 do for i = 0, #expr - 1 do
if i % 10 == 0 then if i % 10 == 0 then
@ -240,10 +235,9 @@ local function format_check(expr, format_check_data, opts)
diffs[flags] = dictdiff(zdata, v) diffs[flags] = dictdiff(zdata, v)
if diffs[flags] then if diffs[flags] then
if flags == 3 + zflags then if flags == 3 + zflags then
if (dictdiff(format_check_data[1 + zflags], if
format_check_data[3 + zflags]) == nil dictdiff(format_check_data[1 + zflags], format_check_data[3 + zflags]) == nil
or dictdiff(format_check_data[2 + zflags], or dictdiff(format_check_data[2 + zflags], format_check_data[3 + zflags]) == nil
format_check_data[3 + zflags]) == nil)
then then
diffs[flags] = nil diffs[flags] = nil
else else
@ -268,7 +262,7 @@ local function format_check(expr, format_check_data, opts)
end end
if diff.hl_fs then if diff.hl_fs then
print(' hl_fs = ' .. format_luav(diff.hl_fs, ' ', { print(' hl_fs = ' .. format_luav(diff.hl_fs, ' ', {
literal_strings=true literal_strings = true,
}) .. ',') }) .. ',')
end end
print(' },') print(' },')
@ -280,47 +274,54 @@ local function format_check(expr, format_check_data, opts)
end end
local east_node_type_tab local east_node_type_tab
make_enum_conv_tab(lib, { make_enum_conv_tab(
'kExprNodeMissing', lib,
'kExprNodeOpMissing', {
'kExprNodeTernary', 'kExprNodeMissing',
'kExprNodeTernaryValue', 'kExprNodeOpMissing',
'kExprNodeRegister', 'kExprNodeTernary',
'kExprNodeSubscript', 'kExprNodeTernaryValue',
'kExprNodeListLiteral', 'kExprNodeRegister',
'kExprNodeUnaryPlus', 'kExprNodeSubscript',
'kExprNodeBinaryPlus', 'kExprNodeListLiteral',
'kExprNodeNested', 'kExprNodeUnaryPlus',
'kExprNodeCall', 'kExprNodeBinaryPlus',
'kExprNodePlainIdentifier', 'kExprNodeNested',
'kExprNodePlainKey', 'kExprNodeCall',
'kExprNodeComplexIdentifier', 'kExprNodePlainIdentifier',
'kExprNodeUnknownFigure', 'kExprNodePlainKey',
'kExprNodeLambda', 'kExprNodeComplexIdentifier',
'kExprNodeDictLiteral', 'kExprNodeUnknownFigure',
'kExprNodeCurlyBracesIdentifier', 'kExprNodeLambda',
'kExprNodeComma', 'kExprNodeDictLiteral',
'kExprNodeColon', 'kExprNodeCurlyBracesIdentifier',
'kExprNodeArrow', 'kExprNodeComma',
'kExprNodeComparison', 'kExprNodeColon',
'kExprNodeConcat', 'kExprNodeArrow',
'kExprNodeConcatOrSubscript', 'kExprNodeComparison',
'kExprNodeInteger', 'kExprNodeConcat',
'kExprNodeFloat', 'kExprNodeConcatOrSubscript',
'kExprNodeSingleQuotedString', 'kExprNodeInteger',
'kExprNodeDoubleQuotedString', 'kExprNodeFloat',
'kExprNodeOr', 'kExprNodeSingleQuotedString',
'kExprNodeAnd', 'kExprNodeDoubleQuotedString',
'kExprNodeUnaryMinus', 'kExprNodeOr',
'kExprNodeBinaryMinus', 'kExprNodeAnd',
'kExprNodeNot', 'kExprNodeUnaryMinus',
'kExprNodeMultiplication', 'kExprNodeBinaryMinus',
'kExprNodeDivision', 'kExprNodeNot',
'kExprNodeMod', 'kExprNodeMultiplication',
'kExprNodeOption', 'kExprNodeDivision',
'kExprNodeEnvironment', 'kExprNodeMod',
'kExprNodeAssignment', 'kExprNodeOption',
}, 'kExprNode', function(ret) east_node_type_tab = ret end) 'kExprNodeEnvironment',
'kExprNodeAssignment',
},
'kExprNode',
function(ret)
east_node_type_tab = ret
end
)
local function conv_east_node_type(typ) local function conv_east_node_type(typ)
return conv_enum(east_node_type_tab, typ) return conv_enum(east_node_type_tab, typ)
@ -346,25 +347,35 @@ local function eastnode2lua(pstate, eastnode, checked_nodes)
ret_str = ('%u:%u:%s'):format(str.start.line, str.start.col, str.str) ret_str = ('%u:%u:%s'):format(str.start.line, str.start.col, str.str)
end end
if typ == 'Register' then if typ == 'Register' then
typ = typ .. ('(name=%s)'):format( typ = typ .. ('(name=%s)'):format(tostring(intchar2lua(eastnode.data.reg.name)))
tostring(intchar2lua(eastnode.data.reg.name)))
elseif typ == 'PlainIdentifier' then elseif typ == 'PlainIdentifier' then
typ = typ .. ('(scope=%s,ident=%s)'):format( typ = typ
tostring(intchar2lua(eastnode.data.var.scope)), .. ('(scope=%s,ident=%s)'):format(
ffi.string(eastnode.data.var.ident, eastnode.data.var.ident_len)) tostring(intchar2lua(eastnode.data.var.scope)),
ffi.string(eastnode.data.var.ident, eastnode.data.var.ident_len)
)
elseif typ == 'PlainKey' then elseif typ == 'PlainKey' then
typ = typ .. ('(key=%s)'):format( typ = typ
ffi.string(eastnode.data.var.ident, eastnode.data.var.ident_len)) .. ('(key=%s)'):format(ffi.string(eastnode.data.var.ident, eastnode.data.var.ident_len))
elseif (typ == 'UnknownFigure' or typ == 'DictLiteral' elseif
or typ == 'CurlyBracesIdentifier' or typ == 'Lambda') then typ == 'UnknownFigure'
typ = typ .. ('(%s)'):format( or typ == 'DictLiteral'
(eastnode.data.fig.type_guesses.allow_lambda and '\\' or '-') or typ == 'CurlyBracesIdentifier'
.. (eastnode.data.fig.type_guesses.allow_dict and 'd' or '-') or typ == 'Lambda'
.. (eastnode.data.fig.type_guesses.allow_ident and 'i' or '-')) then
typ = typ
.. ('(%s)'):format(
(eastnode.data.fig.type_guesses.allow_lambda and '\\' or '-')
.. (eastnode.data.fig.type_guesses.allow_dict and 'd' or '-')
.. (eastnode.data.fig.type_guesses.allow_ident and 'i' or '-')
)
elseif typ == 'Comparison' then elseif typ == 'Comparison' then
typ = typ .. ('(type=%s,inv=%u,ccs=%s)'):format( typ = typ
conv_cmp_type(eastnode.data.cmp.type), eastnode.data.cmp.inv and 1 or 0, .. ('(type=%s,inv=%u,ccs=%s)'):format(
conv_ccs(eastnode.data.cmp.ccs)) conv_cmp_type(eastnode.data.cmp.type),
eastnode.data.cmp.inv and 1 or 0,
conv_ccs(eastnode.data.cmp.ccs)
)
elseif typ == 'Integer' then elseif typ == 'Integer' then
typ = typ .. ('(val=%u)'):format(tonumber(eastnode.data.num.value)) typ = typ .. ('(val=%u)'):format(tonumber(eastnode.data.num.value))
elseif typ == 'Float' then elseif typ == 'Float' then
@ -380,11 +391,13 @@ local function eastnode2lua(pstate, eastnode, checked_nodes)
typ = ('%s(scope=%s,ident=%s)'):format( typ = ('%s(scope=%s,ident=%s)'):format(
typ, typ,
tostring(intchar2lua(eastnode.data.opt.scope)), tostring(intchar2lua(eastnode.data.opt.scope)),
ffi.string(eastnode.data.opt.ident, eastnode.data.opt.ident_len)) ffi.string(eastnode.data.opt.ident, eastnode.data.opt.ident_len)
)
elseif typ == 'Environment' then elseif typ == 'Environment' then
typ = ('%s(ident=%s)'):format( typ = ('%s(ident=%s)'):format(
typ, typ,
ffi.string(eastnode.data.env.ident, eastnode.data.env.ident_len)) ffi.string(eastnode.data.env.ident, eastnode.data.env.ident_len)
)
elseif typ == 'Assignment' then elseif typ == 'Assignment' then
typ = ('%s(%s)'):format(typ, conv_expr_asgn_type(eastnode.data.ass.type)) typ = ('%s(%s)'):format(typ, conv_expr_asgn_type(eastnode.data.ass.type))
end end
@ -433,22 +446,21 @@ local function phl2lua(pstate)
local ret = {} local ret = {}
for i = 0, (tonumber(pstate.colors.size) - 1) do for i = 0, (tonumber(pstate.colors.size) - 1) do
local chunk = pstate.colors.items[i] local chunk = pstate.colors.items[i]
local chunk_tbl = pstate_set_str( local chunk_tbl = pstate_set_str(pstate, chunk.start, chunk.end_col - chunk.start.col, {
pstate, chunk.start, chunk.end_col - chunk.start.col, { group = ffi.string(chunk.group),
group = ffi.string(chunk.group), })
})
ret[i + 1] = ('%s:%u:%u:%s'):format( ret[i + 1] = ('%s:%u:%u:%s'):format(
chunk_tbl.group, chunk_tbl.group,
chunk_tbl.start.line, chunk_tbl.start.line,
chunk_tbl.start.col, chunk_tbl.start.col,
chunk_tbl.str) chunk_tbl.str
)
end end
return ret return ret
end end
describe('Expressions parser', function() describe('Expressions parser', function()
local function _check_parsing(opts, str, exp_ast, exp_highlighting_fs, local function _check_parsing(opts, str, exp_ast, exp_highlighting_fs, nz_flags_exps)
nz_flags_exps)
local zflags = opts.flags[1] local zflags = opts.flags[1]
nz_flags_exps = nz_flags_exps or {} nz_flags_exps = nz_flags_exps or {}
local format_check_data = {} local format_check_data = {}
@ -460,12 +472,12 @@ describe('Expressions parser', function()
end end
alloc_log:check({}) alloc_log:check({})
local pstate = new_pstate({str}) local pstate = new_pstate({ str })
local east = lib.viml_pexpr_parse(pstate, flags) local east = lib.viml_pexpr_parse(pstate, flags)
local ast = east2lua(str, pstate, east) local ast = east2lua(str, pstate, east)
local hls = phl2lua(pstate) local hls = phl2lua(pstate)
if exp_ast == nil then if exp_ast == nil then
format_check_data[flags] = {ast=ast, hl_fs=hls_to_hl_fs(hls)} format_check_data[flags] = { ast = ast, hl_fs = hls_to_hl_fs(hls) }
else else
local exps = { local exps = {
ast = exp_ast, ast = exp_ast,
@ -499,8 +511,7 @@ describe('Expressions parser', function()
alloc_log:check({}) alloc_log:check({})
end) end)
if not err then if not err then
msg = format_string('Error while processing test (%r, %u):\n%s', msg = format_string('Error while processing test (%r, %u):\n%s', str, flags, msg)
str, flags, msg)
error(msg) error(msg)
end end
end end
@ -514,16 +525,11 @@ describe('Expressions parser', function()
error(('Unknown group: Nvim%s'):format(group)) error(('Unknown group: Nvim%s'):format(group))
end end
local col = next_col + (shift or 0) local col = next_col + (shift or 0)
return (('%s:%u:%u:%s'):format( return (('%s:%u:%u:%s'):format('Nvim' .. group, 0, col, str)), (col + #str)
'Nvim' .. group,
0,
col,
str)), (col + #str)
end end
end end
local function fmtn(typ, args, rest) local function fmtn(typ, args, rest)
return ('%s(%s)%s'):format(typ, args, rest) return ('%s(%s)%s'):format(typ, args, rest)
end end
require('test.unit.viml.expressions.parser_tests')( require('test.unit.viml.expressions.parser_tests')(itp, _check_parsing, hl, fmtn)
itp, _check_parsing, hl, fmtn)
end) end)

View File

@ -4,12 +4,12 @@ local REMOVE_THIS = global_helpers.REMOVE_THIS
return function(itp, _check_parsing, hl, fmtn) return function(itp, _check_parsing, hl, fmtn)
local function check_parsing(...) local function check_parsing(...)
return _check_parsing({flags={0, 1, 2, 3}, funcname='check_parsing'}, ...) return _check_parsing({ flags = { 0, 1, 2, 3 }, funcname = 'check_parsing' }, ...)
end end
local function check_asgn_parsing(...) local function check_asgn_parsing(...)
return _check_parsing({ return _check_parsing({
flags={4, 5, 6, 7}, flags = { 4, 5, 6, 7 },
funcname='check_asgn_parsing', funcname = 'check_asgn_parsing',
}, ...) }, ...)
end end
itp('works with + and @a', function() itp('works with + and @a', function()
@ -142,7 +142,7 @@ return function(itp, _check_parsing, hl, fmtn)
len = 2, len = 2,
err = REMOVE_THIS, err = REMOVE_THIS,
ast = { ast = {
'Register(name=a):0:0:@a' 'Register(name=a):0:0:@a',
}, },
}, },
hl_fs = { hl_fs = {
@ -174,7 +174,7 @@ return function(itp, _check_parsing, hl, fmtn)
len = 6, len = 6,
err = REMOVE_THIS, err = REMOVE_THIS,
ast = { ast = {
'Register(name=a):0:0: @a' 'Register(name=a):0:0: @a',
}, },
}, },
hl_fs = { hl_fs = {
@ -541,7 +541,7 @@ return function(itp, _check_parsing, hl, fmtn)
children = { children = {
{ {
'Nested:0:4:(', 'Nested:0:4:(',
children = { 'Register(name=b):0:5:@b' } children = { 'Register(name=b):0:5:@b' },
}, },
}, },
}, },
@ -579,7 +579,7 @@ return function(itp, _check_parsing, hl, fmtn)
children = { children = {
{ {
'Nested:0:4:(', 'Nested:0:4:(',
children = { 'Register(name=b):0:5:@b' } children = { 'Register(name=b):0:5:@b' },
}, },
}, },
}, },
@ -600,13 +600,13 @@ return function(itp, _check_parsing, hl, fmtn)
hl('BinaryPlus', '+'), hl('BinaryPlus', '+'),
hl('Register', '@c'), hl('Register', '@c'),
}) })
check_parsing( check_parsing('@a + (@b + @c) + @d(@e) + (+@f) + ((+@g(@h))(@j)(@k))(@l)', {--[[
'@a + (@b + @c) + @d(@e) + (+@f) + ((+@g(@h))(@j)(@k))(@l)', {--[[
| | | | | | | | || | | || | | ||| || || || || | | | | | | | | || | | || | | ||| || || || ||
000000000011111111112222222222333333333344444444445555555 000000000011111111112222222222333333333344444444445555555
012345678901234567890123456789012345678901234567890123456 012345678901234567890123456789012345678901234567890123456
]] ]]
ast = {{ ast = {
{
'BinaryPlus:0:31: +', 'BinaryPlus:0:31: +',
children = { children = {
{ {
@ -696,45 +696,46 @@ return function(itp, _check_parsing, hl, fmtn)
}, },
}, },
}, },
}}, },
}, { },
hl('Register', '@a'), }, {
hl('BinaryPlus', '+', 1), hl('Register', '@a'),
hl('NestingParenthesis', '(', 1), hl('BinaryPlus', '+', 1),
hl('Register', '@b'), hl('NestingParenthesis', '(', 1),
hl('BinaryPlus', '+', 1), hl('Register', '@b'),
hl('Register', '@c', 1), hl('BinaryPlus', '+', 1),
hl('NestingParenthesis', ')'), hl('Register', '@c', 1),
hl('BinaryPlus', '+', 1), hl('NestingParenthesis', ')'),
hl('Register', '@d', 1), hl('BinaryPlus', '+', 1),
hl('CallingParenthesis', '('), hl('Register', '@d', 1),
hl('Register', '@e'), hl('CallingParenthesis', '('),
hl('CallingParenthesis', ')'), hl('Register', '@e'),
hl('BinaryPlus', '+', 1), hl('CallingParenthesis', ')'),
hl('NestingParenthesis', '(', 1), hl('BinaryPlus', '+', 1),
hl('UnaryPlus', '+'), hl('NestingParenthesis', '(', 1),
hl('Register', '@f'), hl('UnaryPlus', '+'),
hl('NestingParenthesis', ')'), hl('Register', '@f'),
hl('BinaryPlus', '+', 1), hl('NestingParenthesis', ')'),
hl('NestingParenthesis', '(', 1), hl('BinaryPlus', '+', 1),
hl('NestingParenthesis', '('), hl('NestingParenthesis', '(', 1),
hl('UnaryPlus', '+'), hl('NestingParenthesis', '('),
hl('Register', '@g'), hl('UnaryPlus', '+'),
hl('CallingParenthesis', '('), hl('Register', '@g'),
hl('Register', '@h'), hl('CallingParenthesis', '('),
hl('CallingParenthesis', ')'), hl('Register', '@h'),
hl('NestingParenthesis', ')'), hl('CallingParenthesis', ')'),
hl('CallingParenthesis', '('), hl('NestingParenthesis', ')'),
hl('Register', '@j'), hl('CallingParenthesis', '('),
hl('CallingParenthesis', ')'), hl('Register', '@j'),
hl('CallingParenthesis', '('), hl('CallingParenthesis', ')'),
hl('Register', '@k'), hl('CallingParenthesis', '('),
hl('CallingParenthesis', ')'), hl('Register', '@k'),
hl('NestingParenthesis', ')'), hl('CallingParenthesis', ')'),
hl('CallingParenthesis', '('), hl('NestingParenthesis', ')'),
hl('Register', '@l'), hl('CallingParenthesis', '('),
hl('CallingParenthesis', ')'), hl('Register', '@l'),
}) hl('CallingParenthesis', ')'),
})
check_parsing('@a)', { check_parsing('@a)', {
-- 012 -- 012
ast = { ast = {
@ -1078,25 +1079,25 @@ return function(itp, _check_parsing, hl, fmtn)
end) end)
itp('works with variable names, including curly braces ones', function() itp('works with variable names, including curly braces ones', function()
check_parsing('var', { check_parsing('var', {
ast = { ast = {
'PlainIdentifier(scope=0,ident=var):0:0:var', 'PlainIdentifier(scope=0,ident=var):0:0:var',
}, },
}, { }, {
hl('IdentifierName', 'var'), hl('IdentifierName', 'var'),
}) })
check_parsing('g:var', { check_parsing('g:var', {
ast = { ast = {
'PlainIdentifier(scope=g,ident=var):0:0:g:var', 'PlainIdentifier(scope=g,ident=var):0:0:g:var',
}, },
}, { }, {
hl('IdentifierScope', 'g'), hl('IdentifierScope', 'g'),
hl('IdentifierScopeDelimiter', ':'), hl('IdentifierScopeDelimiter', ':'),
hl('IdentifierName', 'var'), hl('IdentifierName', 'var'),
}) })
check_parsing('g:', { check_parsing('g:', {
ast = { ast = {
'PlainIdentifier(scope=g,ident=):0:0:g:', 'PlainIdentifier(scope=g,ident=):0:0:g:',
}, },
}, { }, {
hl('IdentifierScope', 'g'), hl('IdentifierScope', 'g'),
hl('IdentifierScopeDelimiter', ':'), hl('IdentifierScopeDelimiter', ':'),
@ -1141,7 +1142,7 @@ return function(itp, _check_parsing, hl, fmtn)
children = { children = {
{ {
'OpMissing:0:3:', 'OpMissing:0:3:',
children={ children = {
'PlainIdentifier(scope=a,ident=):0:1:a:', 'PlainIdentifier(scope=a,ident=):0:1:a:',
'Register(name=b):0:3:@b', 'Register(name=b):0:3:@b',
}, },
@ -1783,7 +1784,7 @@ return function(itp, _check_parsing, hl, fmtn)
{ {
'Comma:0:3:,', 'Comma:0:3:,',
children = { children = {
'PlainIdentifier(scope=0,ident=b):0:2:b', 'PlainIdentifier(scope=0,ident=b):0:2:b',
{ {
'Comma:0:5:,', 'Comma:0:5:,',
children = { children = {
@ -1819,7 +1820,7 @@ return function(itp, _check_parsing, hl, fmtn)
{ {
'Comma:0:3:,', 'Comma:0:3:,',
children = { children = {
'PlainIdentifier(scope=0,ident=b):0:2:b', 'PlainIdentifier(scope=0,ident=b):0:2:b',
{ {
'Comma:0:5:,', 'Comma:0:5:,',
children = { children = {
@ -2851,7 +2852,7 @@ return function(itp, _check_parsing, hl, fmtn)
}, },
err = { err = {
arg = '{a : b', arg = '{a : b',
msg = 'E723: Missing end of Dictionary \'}\': %.*s', msg = "E723: Missing end of Dictionary '}': %.*s",
}, },
}, { }, {
hl('Dict', '{'), hl('Dict', '{'),
@ -3182,7 +3183,7 @@ return function(itp, _check_parsing, hl, fmtn)
}, },
err = { err = {
arg = '?b', arg = '?b',
msg = 'E109: Missing \':\' after \'?\': %.*s', msg = "E109: Missing ':' after '?': %.*s",
}, },
}, { }, {
hl('IdentifierName', 'a'), hl('IdentifierName', 'a'),
@ -3207,7 +3208,7 @@ return function(itp, _check_parsing, hl, fmtn)
}, },
err = { err = {
arg = '?b:', arg = '?b:',
msg = 'E109: Missing \':\' after \'?\': %.*s', msg = "E109: Missing ':' after '?': %.*s",
}, },
}, { }, {
hl('IdentifierName', 'a'), hl('IdentifierName', 'a'),
@ -4840,7 +4841,7 @@ return function(itp, _check_parsing, hl, fmtn)
}, },
err = { err = {
arg = '[1', arg = '[1',
msg = 'E697: Missing end of List \']\': %.*s', msg = "E697: Missing end of List ']': %.*s",
}, },
}, { }, {
hl('List', '['), hl('List', '['),
@ -4848,15 +4849,15 @@ return function(itp, _check_parsing, hl, fmtn)
}) })
end) end)
itp('works with strings', function() itp('works with strings', function()
check_parsing('\'abc\'', { check_parsing("'abc'", {
-- 01234 -- 01234
ast = { ast = {
fmtn('SingleQuotedString', 'val="abc"', ':0:0:\'abc\''), fmtn('SingleQuotedString', 'val="abc"', ":0:0:'abc'"),
}, },
}, { }, {
hl('SingleQuote', '\''), hl('SingleQuote', "'"),
hl('SingleQuotedBody', 'abc'), hl('SingleQuotedBody', 'abc'),
hl('SingleQuote', '\''), hl('SingleQuote', "'"),
}) })
check_parsing('"abc"', { check_parsing('"abc"', {
-- 01234 -- 01234
@ -4868,14 +4869,14 @@ return function(itp, _check_parsing, hl, fmtn)
hl('DoubleQuotedBody', 'abc'), hl('DoubleQuotedBody', 'abc'),
hl('DoubleQuote', '"'), hl('DoubleQuote', '"'),
}) })
check_parsing('\'\'', { check_parsing("''", {
-- 01 -- 01
ast = { ast = {
fmtn('SingleQuotedString', 'val=NULL', ':0:0:\'\''), fmtn('SingleQuotedString', 'val=NULL', ":0:0:''"),
}, },
}, { }, {
hl('SingleQuote', '\''), hl('SingleQuote', "'"),
hl('SingleQuote', '\''), hl('SingleQuote', "'"),
}) })
check_parsing('""', { check_parsing('""', {
-- 01 -- 01
@ -4898,17 +4899,17 @@ return function(itp, _check_parsing, hl, fmtn)
}, { }, {
hl('InvalidDoubleQuote', '"'), hl('InvalidDoubleQuote', '"'),
}) })
check_parsing('\'', { check_parsing("'", {
-- 0 -- 0
ast = { ast = {
fmtn('SingleQuotedString', 'val=NULL', ':0:0:\''), fmtn('SingleQuotedString', 'val=NULL', ":0:0:'"),
}, },
err = { err = {
arg = '\'', arg = "'",
msg = 'E115: Missing single quote: %.*s', msg = 'E115: Missing single quote: %.*s',
}, },
}, { }, {
hl('InvalidSingleQuote', '\''), hl('InvalidSingleQuote', "'"),
}) })
check_parsing('"a', { check_parsing('"a', {
-- 01 -- 01
@ -4923,71 +4924,71 @@ return function(itp, _check_parsing, hl, fmtn)
hl('InvalidDoubleQuote', '"'), hl('InvalidDoubleQuote', '"'),
hl('InvalidDoubleQuotedBody', 'a'), hl('InvalidDoubleQuotedBody', 'a'),
}) })
check_parsing('\'a', { check_parsing("'a", {
-- 01 -- 01
ast = { ast = {
fmtn('SingleQuotedString', 'val="a"', ':0:0:\'a'), fmtn('SingleQuotedString', 'val="a"', ":0:0:'a"),
}, },
err = { err = {
arg = '\'a', arg = "'a",
msg = 'E115: Missing single quote: %.*s', msg = 'E115: Missing single quote: %.*s',
}, },
}, { }, {
hl('InvalidSingleQuote', '\''), hl('InvalidSingleQuote', "'"),
hl('InvalidSingleQuotedBody', 'a'), hl('InvalidSingleQuotedBody', 'a'),
}) })
check_parsing('\'abc\'\'def\'', { check_parsing("'abc''def'", {
-- 0123456789 -- 0123456789
ast = { ast = {
fmtn('SingleQuotedString', 'val="abc\'def"', ':0:0:\'abc\'\'def\''), fmtn('SingleQuotedString', 'val="abc\'def"', ":0:0:'abc''def'"),
}, },
}, { }, {
hl('SingleQuote', '\''), hl('SingleQuote', "'"),
hl('SingleQuotedBody', 'abc'), hl('SingleQuotedBody', 'abc'),
hl('SingleQuotedQuote', '\'\''), hl('SingleQuotedQuote', "''"),
hl('SingleQuotedBody', 'def'), hl('SingleQuotedBody', 'def'),
hl('SingleQuote', '\''), hl('SingleQuote', "'"),
}) })
check_parsing('\'abc\'\'', { check_parsing("'abc''", {
-- 012345 -- 012345
ast = { ast = {
fmtn('SingleQuotedString', 'val="abc\'"', ':0:0:\'abc\'\''), fmtn('SingleQuotedString', 'val="abc\'"', ":0:0:'abc''"),
}, },
err = { err = {
arg = '\'abc\'\'', arg = "'abc''",
msg = 'E115: Missing single quote: %.*s', msg = 'E115: Missing single quote: %.*s',
}, },
}, { }, {
hl('InvalidSingleQuote', '\''), hl('InvalidSingleQuote', "'"),
hl('InvalidSingleQuotedBody', 'abc'), hl('InvalidSingleQuotedBody', 'abc'),
hl('InvalidSingleQuotedQuote', '\'\''), hl('InvalidSingleQuotedQuote', "''"),
}) })
check_parsing('\'\'\'\'\'\'\'\'', { check_parsing("''''''''", {
-- 01234567 -- 01234567
ast = { ast = {
fmtn('SingleQuotedString', 'val="\'\'\'"', ':0:0:\'\'\'\'\'\'\'\''), fmtn('SingleQuotedString', "val=\"'''\"", ":0:0:''''''''"),
}, },
}, { }, {
hl('SingleQuote', '\''), hl('SingleQuote', "'"),
hl('SingleQuotedQuote', '\'\''), hl('SingleQuotedQuote', "''"),
hl('SingleQuotedQuote', '\'\''), hl('SingleQuotedQuote', "''"),
hl('SingleQuotedQuote', '\'\''), hl('SingleQuotedQuote', "''"),
hl('SingleQuote', '\''), hl('SingleQuote', "'"),
}) })
check_parsing('\'\'\'a\'\'\'\'bc\'', { check_parsing("'''a''''bc'", {
-- 01234567890 -- 01234567890
-- 0 1 -- 0 1
ast = { ast = {
fmtn('SingleQuotedString', 'val="\'a\'\'bc"', ':0:0:\'\'\'a\'\'\'\'bc\''), fmtn('SingleQuotedString', "val=\"'a''bc\"", ":0:0:'''a''''bc'"),
}, },
}, { }, {
hl('SingleQuote', '\''), hl('SingleQuote', "'"),
hl('SingleQuotedQuote', '\'\''), hl('SingleQuotedQuote', "''"),
hl('SingleQuotedBody', 'a'), hl('SingleQuotedBody', 'a'),
hl('SingleQuotedQuote', '\'\''), hl('SingleQuotedQuote', "''"),
hl('SingleQuotedQuote', '\'\''), hl('SingleQuotedQuote', "''"),
hl('SingleQuotedBody', 'bc'), hl('SingleQuotedBody', 'bc'),
hl('SingleQuote', '\''), hl('SingleQuote', "'"),
}) })
check_parsing('"\\"\\"\\"\\""', { check_parsing('"\\"\\"\\"\\""', {
-- 0123456789 -- 0123456789
@ -5006,7 +5007,11 @@ return function(itp, _check_parsing, hl, fmtn)
-- 0123456789012345678901234 -- 0123456789012345678901234
-- 0 1 2 -- 0 1 2
ast = { ast = {
fmtn('DoubleQuotedString', 'val="abc\\"def\\"ghi\\"jkl\\"mno"', ':0:0:"abc\\"def\\"ghi\\"jkl\\"mno"'), fmtn(
'DoubleQuotedString',
'val="abc\\"def\\"ghi\\"jkl\\"mno"',
':0:0:"abc\\"def\\"ghi\\"jkl\\"mno"'
),
}, },
}, { }, {
hl('DoubleQuote', '"'), hl('DoubleQuote', '"'),
@ -6977,8 +6982,7 @@ return function(itp, _check_parsing, hl, fmtn)
arg = '\0002&A:\000', arg = '\0002&A:\000',
msg = 'E15: Expected value, got EOC: %.*s', msg = 'E15: Expected value, got EOC: %.*s',
}, },
}, { }, {}, {
}, {
[2] = { [2] = {
ast = { ast = {
len = REMOVE_THIS, len = REMOVE_THIS,
@ -7025,7 +7029,7 @@ return function(itp, _check_parsing, hl, fmtn)
}, },
}, },
}) })
check_parsing({data='01', size=1}, { check_parsing({ data = '01', size = 1 }, {
len = 1, len = 1,
ast = { ast = {
'Integer(val=0):0:0:0', 'Integer(val=0):0:0:0',
@ -7033,7 +7037,7 @@ return function(itp, _check_parsing, hl, fmtn)
}, { }, {
hl('Number', '0'), hl('Number', '0'),
}) })
check_parsing({data='001', size=2}, { check_parsing({ data = '001', size = 2 }, {
len = 2, len = 2,
ast = { ast = {
'Integer(val=0):0:0:00', 'Integer(val=0):0:0:00',
@ -7076,8 +7080,7 @@ return function(itp, _check_parsing, hl, fmtn)
arg = '|"\\U\\', arg = '|"\\U\\',
msg = 'E15: Expected value, got EOC: %.*s', msg = 'E15: Expected value, got EOC: %.*s',
}, },
}, { }, {}, {
}, {
[2] = { [2] = {
ast = { ast = {
len = REMOVE_THIS, len = REMOVE_THIS,
@ -7109,8 +7112,7 @@ return function(itp, _check_parsing, hl, fmtn)
arg = '|"\\e"', arg = '|"\\e"',
msg = 'E15: Expected value, got EOC: %.*s', msg = 'E15: Expected value, got EOC: %.*s',
}, },
}, { }, {}, {
}, {
[2] = { [2] = {
ast = { ast = {
len = REMOVE_THIS, len = REMOVE_THIS,
@ -7142,8 +7144,7 @@ return function(itp, _check_parsing, hl, fmtn)
arg = '|\029', arg = '|\029',
msg = 'E15: Expected value, got EOC: %.*s', msg = 'E15: Expected value, got EOC: %.*s',
}, },
}, { }, {}, {
}, {
[2] = { [2] = {
ast = { ast = {
len = REMOVE_THIS, len = REMOVE_THIS,
@ -7373,7 +7374,7 @@ return function(itp, _check_parsing, hl, fmtn)
hl_fs = { hl_fs = {
[2] = REMOVE_THIS, [2] = REMOVE_THIS,
[3] = REMOVE_THIS, [3] = REMOVE_THIS,
} },
}, },
}) })

View File

@ -76,7 +76,7 @@ local function pstate_set_str(pstate, start, len, ret)
ret = ret or {} ret = ret or {}
ret.start = { ret.start = {
line = tonumber(start.line), line = tonumber(start.line),
col = tonumber(start.col) col = tonumber(start.col),
} }
ret.len = tonumber(len) ret.len = tonumber(len)
ret.str, ret.error = pstate_str(pstate, start, len) ret.str, ret.error = pstate_str(pstate, start, len)
@ -84,36 +84,57 @@ local function pstate_set_str(pstate, start, len, ret)
end end
local eltkn_cmp_type_tab local eltkn_cmp_type_tab
make_enum_conv_tab(lib, { make_enum_conv_tab(
'kExprCmpEqual', lib,
'kExprCmpMatches', {
'kExprCmpGreater', 'kExprCmpEqual',
'kExprCmpGreaterOrEqual', 'kExprCmpMatches',
'kExprCmpIdentical', 'kExprCmpGreater',
}, 'kExprCmp', function(ret) eltkn_cmp_type_tab = ret end) 'kExprCmpGreaterOrEqual',
'kExprCmpIdentical',
},
'kExprCmp',
function(ret)
eltkn_cmp_type_tab = ret
end
)
local function conv_cmp_type(typ) local function conv_cmp_type(typ)
return conv_enum(eltkn_cmp_type_tab, typ) return conv_enum(eltkn_cmp_type_tab, typ)
end end
local ccs_tab local ccs_tab
make_enum_conv_tab(lib, { make_enum_conv_tab(
'kCCStrategyUseOption', lib,
'kCCStrategyMatchCase', {
'kCCStrategyIgnoreCase', 'kCCStrategyUseOption',
}, 'kCCStrategy', function(ret) ccs_tab = ret end) 'kCCStrategyMatchCase',
'kCCStrategyIgnoreCase',
},
'kCCStrategy',
function(ret)
ccs_tab = ret
end
)
local function conv_ccs(ccs) local function conv_ccs(ccs)
return conv_enum(ccs_tab, ccs) return conv_enum(ccs_tab, ccs)
end end
local expr_asgn_type_tab local expr_asgn_type_tab
make_enum_conv_tab(lib, { make_enum_conv_tab(
'kExprAsgnPlain', lib,
'kExprAsgnAdd', {
'kExprAsgnSubtract', 'kExprAsgnPlain',
'kExprAsgnConcat', 'kExprAsgnAdd',
}, 'kExprAsgn', function(ret) expr_asgn_type_tab = ret end) 'kExprAsgnSubtract',
'kExprAsgnConcat',
},
'kExprAsgn',
function(ret)
expr_asgn_type_tab = ret
end
)
local function conv_expr_asgn_type(expr_asgn_type) local function conv_expr_asgn_type(expr_asgn_type)
return conv_enum(expr_asgn_type_tab, expr_asgn_type) return conv_enum(expr_asgn_type_tab, expr_asgn_type)