2017-07-03 16:06:04 -07:00
|
|
|
local helpers = require('test.functional.helpers')(after_each)
|
2017-07-03 16:22:26 -07:00
|
|
|
|
2017-07-03 16:06:04 -07:00
|
|
|
local eq = helpers.eq
|
2017-07-03 16:22:26 -07:00
|
|
|
local NIL = helpers.NIL
|
2017-07-03 16:06:04 -07:00
|
|
|
local eval = helpers.eval
|
2017-07-03 16:22:26 -07:00
|
|
|
local clear = helpers.clear
|
|
|
|
local meths = helpers.meths
|
2017-07-03 16:06:04 -07:00
|
|
|
local funcs = helpers.funcs
|
|
|
|
local source = helpers.source
|
|
|
|
local dedent = helpers.dedent
|
2017-07-03 16:22:26 -07:00
|
|
|
local command = helpers.command
|
|
|
|
local exc_exec = helpers.exc_exec
|
|
|
|
local redir_exec = helpers.redir_exec
|
2017-07-03 16:06:04 -07:00
|
|
|
|
2017-07-03 16:22:26 -07:00
|
|
|
describe(':echo', function()
|
|
|
|
before_each(function()
|
|
|
|
clear()
|
|
|
|
source([[
|
|
|
|
function String(s)
|
|
|
|
return execute('echo a:s')[1:]
|
|
|
|
endfunction
|
|
|
|
]])
|
|
|
|
end)
|
2017-07-03 16:06:04 -07:00
|
|
|
|
|
|
|
describe('used to represent floating-point values', function()
|
|
|
|
it('dumps NaN values', function()
|
2017-07-03 16:22:26 -07:00
|
|
|
eq('str2float(\'nan\')', eval('String(str2float(\'nan\'))'))
|
2017-07-03 16:06:04 -07:00
|
|
|
end)
|
|
|
|
|
|
|
|
it('dumps infinite values', function()
|
2017-07-03 16:22:26 -07:00
|
|
|
eq('str2float(\'inf\')', eval('String(str2float(\'inf\'))'))
|
|
|
|
eq('-str2float(\'inf\')', eval('String(str2float(\'-inf\'))'))
|
2017-07-03 16:06:04 -07:00
|
|
|
end)
|
|
|
|
|
|
|
|
it('dumps regular values', function()
|
2017-07-03 16:22:26 -07:00
|
|
|
eq('1.5', funcs.String(1.5))
|
|
|
|
eq('1.56e-20', funcs.String(1.56000e-020))
|
|
|
|
eq('0.0', eval('String(0.0)'))
|
2017-07-03 16:06:04 -07:00
|
|
|
end)
|
|
|
|
|
|
|
|
it('dumps special v: values', function()
|
2017-07-03 16:22:26 -07:00
|
|
|
eq('v:true', eval('String(v:true)'))
|
|
|
|
eq('v:false', eval('String(v:false)'))
|
|
|
|
eq('v:null', eval('String(v:null)'))
|
|
|
|
eq('v:true', funcs.String(true))
|
|
|
|
eq('v:false', funcs.String(false))
|
|
|
|
eq('v:null', funcs.String(NIL))
|
2017-07-03 16:06:04 -07:00
|
|
|
end)
|
|
|
|
|
|
|
|
it('dumps values with at most six digits after the decimal point',
|
|
|
|
function()
|
2017-07-03 16:22:26 -07:00
|
|
|
eq('1.234568e-20', funcs.String(1.23456789123456789123456789e-020))
|
|
|
|
eq('1.234568', funcs.String(1.23456789123456789123456789))
|
2017-07-03 16:06:04 -07:00
|
|
|
end)
|
|
|
|
|
|
|
|
it('dumps values with at most seven digits before the decimal point',
|
|
|
|
function()
|
2017-07-03 16:22:26 -07:00
|
|
|
eq('1234567.891235', funcs.String(1234567.89123456789123456789))
|
|
|
|
eq('1.234568e7', funcs.String(12345678.9123456789123456789))
|
2017-07-03 16:06:04 -07:00
|
|
|
end)
|
|
|
|
|
|
|
|
it('dumps negative values', function()
|
2017-07-03 16:22:26 -07:00
|
|
|
eq('-1.5', funcs.String(-1.5))
|
|
|
|
eq('-1.56e-20', funcs.String(-1.56000e-020))
|
|
|
|
eq('-1.234568e-20', funcs.String(-1.23456789123456789123456789e-020))
|
|
|
|
eq('-1.234568', funcs.String(-1.23456789123456789123456789))
|
|
|
|
eq('-1234567.891235', funcs.String(-1234567.89123456789123456789))
|
|
|
|
eq('-1.234568e7', funcs.String(-12345678.9123456789123456789))
|
2017-07-03 16:06:04 -07:00
|
|
|
end)
|
|
|
|
end)
|
|
|
|
|
|
|
|
describe('used to represent numbers', function()
|
|
|
|
it('dumps regular values', function()
|
2017-07-03 16:22:26 -07:00
|
|
|
eq('0', funcs.String(0))
|
|
|
|
eq('-1', funcs.String(-1))
|
|
|
|
eq('1', funcs.String(1))
|
2017-07-03 16:06:04 -07:00
|
|
|
end)
|
|
|
|
|
|
|
|
it('dumps large values', function()
|
2017-07-03 16:22:26 -07:00
|
|
|
eq('2147483647', funcs.String(2^31-1))
|
|
|
|
eq('-2147483648', funcs.String(-2^31))
|
2017-07-03 16:06:04 -07:00
|
|
|
end)
|
|
|
|
end)
|
|
|
|
|
|
|
|
describe('used to represent strings', function()
|
|
|
|
it('dumps regular strings', function()
|
2017-07-03 16:22:26 -07:00
|
|
|
eq('test', funcs.String('test'))
|
2017-07-03 16:06:04 -07:00
|
|
|
end)
|
|
|
|
|
|
|
|
it('dumps empty strings', function()
|
2017-07-03 16:22:26 -07:00
|
|
|
eq('', funcs.String(''))
|
2017-07-03 16:06:04 -07:00
|
|
|
end)
|
|
|
|
|
|
|
|
it('dumps strings with \' inside', function()
|
2017-07-03 16:22:26 -07:00
|
|
|
eq('\'\'\'', funcs.String('\'\'\''))
|
|
|
|
eq('a\'b\'\'', funcs.String('a\'b\'\''))
|
|
|
|
eq('\'b\'\'d', funcs.String('\'b\'\'d'))
|
|
|
|
eq('a\'b\'c\'d', funcs.String('a\'b\'c\'d'))
|
2017-07-03 16:06:04 -07:00
|
|
|
end)
|
|
|
|
|
|
|
|
it('dumps NULL strings', function()
|
2017-07-03 16:22:26 -07:00
|
|
|
eq('', eval('String($XXX_UNEXISTENT_VAR_XXX)'))
|
2017-07-03 16:06:04 -07:00
|
|
|
end)
|
|
|
|
|
|
|
|
it('dumps NULL lists', function()
|
2017-07-03 16:22:26 -07:00
|
|
|
eq('[]', eval('String(v:_null_list)'))
|
2017-07-03 16:06:04 -07:00
|
|
|
end)
|
|
|
|
|
|
|
|
it('dumps NULL dictionaries', function()
|
2017-07-03 16:22:26 -07:00
|
|
|
eq('{}', eval('String(v:_null_dict)'))
|
2017-07-03 16:06:04 -07:00
|
|
|
end)
|
|
|
|
end)
|
|
|
|
|
|
|
|
describe('used to represent funcrefs', function()
|
|
|
|
before_each(function()
|
|
|
|
source([[
|
|
|
|
function Test1()
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function s:Test2() dict
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function g:Test3() dict
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
let g:Test2_f = function('s:Test2')
|
|
|
|
]])
|
|
|
|
end)
|
|
|
|
|
|
|
|
it('dumps references to built-in functions', function()
|
2017-07-03 16:22:26 -07:00
|
|
|
eq('function', eval('String(function("function"))'))
|
2017-07-03 16:06:04 -07:00
|
|
|
end)
|
|
|
|
|
|
|
|
it('dumps references to user functions', function()
|
2017-07-03 16:22:26 -07:00
|
|
|
eq('Test1', eval('String(function("Test1"))'))
|
|
|
|
eq('g:Test3', eval('String(function("g:Test3"))'))
|
2017-07-03 16:06:04 -07:00
|
|
|
end)
|
|
|
|
|
|
|
|
it('dumps references to script functions', function()
|
2017-07-03 16:22:26 -07:00
|
|
|
eq('<SNR>1_Test2', eval('String(Test2_f)'))
|
2017-07-03 16:06:04 -07:00
|
|
|
end)
|
|
|
|
|
|
|
|
it('dumps partials with self referencing a partial', function()
|
|
|
|
source([[
|
|
|
|
function TestDict() dict
|
|
|
|
endfunction
|
|
|
|
let d = {}
|
|
|
|
let TestDictRef = function('TestDict', d)
|
|
|
|
let d.tdr = TestDictRef
|
|
|
|
]])
|
2017-07-03 16:22:26 -07:00
|
|
|
eq(dedent([[
|
|
|
|
|
|
|
|
function('TestDict', {'tdr': function('TestDict', {...@1})})
|
|
|
|
function('TestDict', {'tdr': function('TestDict', {...@1})})]]),
|
|
|
|
redir_exec('echo String(d.tdr)'))
|
2017-07-03 16:06:04 -07:00
|
|
|
end)
|
|
|
|
|
|
|
|
it('dumps automatically created partials', function()
|
|
|
|
eq('function(\'<SNR>1_Test2\', {\'f\': function(\'<SNR>1_Test2\')})',
|
2017-07-03 16:22:26 -07:00
|
|
|
eval('String({"f": Test2_f}.f)'))
|
2017-07-03 16:06:04 -07:00
|
|
|
eq('function(\'<SNR>1_Test2\', [1], {\'f\': function(\'<SNR>1_Test2\', [1])})',
|
2017-07-03 16:22:26 -07:00
|
|
|
eval('String({"f": function(Test2_f, [1])}.f)'))
|
2017-07-03 16:06:04 -07:00
|
|
|
end)
|
|
|
|
|
|
|
|
it('dumps manually created partials', function()
|
|
|
|
eq('function(\'Test3\', [1, 2], {})',
|
2017-07-03 16:22:26 -07:00
|
|
|
eval('String(function("Test3", [1, 2], {}))'))
|
2017-07-03 16:06:04 -07:00
|
|
|
eq('function(\'Test3\', {})',
|
2017-07-03 16:22:26 -07:00
|
|
|
eval('String(function("Test3", {}))'))
|
2017-07-03 16:06:04 -07:00
|
|
|
eq('function(\'Test3\', [1, 2])',
|
2017-07-03 16:22:26 -07:00
|
|
|
eval('String(function("Test3", [1, 2]))'))
|
2017-07-03 16:06:04 -07:00
|
|
|
end)
|
|
|
|
|
|
|
|
it('does not crash or halt when dumping partials with reference cycles in self',
|
|
|
|
function()
|
|
|
|
meths.set_var('d', {v=true})
|
|
|
|
eq(dedent([[
|
|
|
|
|
2017-07-03 16:22:26 -07:00
|
|
|
{'p': function('<SNR>1_Test2', {...@0}), 'f': function('<SNR>1_Test2'), 'v': v:true}
|
|
|
|
{'p': function('<SNR>1_Test2', {...@0}), 'f': function('<SNR>1_Test2'), 'v': v:true}]]),
|
|
|
|
redir_exec('echo String(extend(extend(g:d, {"f": g:Test2_f}), {"p": g:d.f}))'))
|
2017-07-03 16:06:04 -07:00
|
|
|
end)
|
|
|
|
|
|
|
|
it('does not show errors when dumping partials referencing the same dictionary',
|
|
|
|
function()
|
|
|
|
command('let d = {}')
|
|
|
|
-- Regression for “eval/typval_encode: Dump empty dictionary before
|
|
|
|
-- checking for refcycle”, results in error.
|
2017-07-03 16:22:26 -07:00
|
|
|
eq('[function(\'tr\', {}), function(\'tr\', {})]', eval('String([function("tr", d), function("tr", d)])'))
|
2017-07-03 16:06:04 -07:00
|
|
|
-- Regression for “eval: Work with reference cycles in partials (self)
|
|
|
|
-- properly”, results in crash.
|
|
|
|
eval('extend(d, {"a": 1})')
|
2017-07-03 16:22:26 -07:00
|
|
|
eq('[function(\'tr\', {\'a\': 1}), function(\'tr\', {\'a\': 1})]', eval('String([function("tr", d), function("tr", d)])'))
|
2017-07-03 16:06:04 -07:00
|
|
|
end)
|
|
|
|
|
|
|
|
it('does not crash or halt when dumping partials with reference cycles in arguments',
|
|
|
|
function()
|
|
|
|
meths.set_var('l', {})
|
|
|
|
eval('add(l, l)')
|
|
|
|
-- Regression: the below line used to crash (add returns original list and
|
|
|
|
-- there was error in dumping partials). Tested explicitly in
|
|
|
|
-- test/unit/api/private_helpers_spec.lua.
|
|
|
|
eval('add(l, function("Test1", l))')
|
|
|
|
eq(dedent([=[
|
|
|
|
|
2017-07-03 16:22:26 -07:00
|
|
|
function('Test1', [[[...@2], function('Test1', [[...@2]])], function('Test1', [[[...@4], function('Test1', [[...@4]])]])])
|
|
|
|
function('Test1', [[[...@2], function('Test1', [[...@2]])], function('Test1', [[[...@4], function('Test1', [[...@4]])]])])]=]),
|
|
|
|
redir_exec('echo String(function("Test1", l))'))
|
2017-07-03 16:06:04 -07:00
|
|
|
end)
|
|
|
|
|
|
|
|
it('does not crash or halt when dumping partials with reference cycles in self and arguments',
|
|
|
|
function()
|
|
|
|
meths.set_var('d', {v=true})
|
|
|
|
meths.set_var('l', {})
|
|
|
|
eval('add(l, l)')
|
|
|
|
eval('add(l, function("Test1", l))')
|
|
|
|
eval('add(l, function("Test1", d))')
|
|
|
|
eq(dedent([=[
|
|
|
|
|
2017-07-03 16:22:26 -07:00
|
|
|
{'p': function('<SNR>1_Test2', [[[...@3], function('Test1', [[...@3]]), function('Test1', {...@0})], function('Test1', [[[...@5], function('Test1', [[...@5]]), function('Test1', {...@0})]]), function('Test1', {...@0})], {...@0}), 'f': function('<SNR>1_Test2'), 'v': v:true}
|
|
|
|
{'p': function('<SNR>1_Test2', [[[...@3], function('Test1', [[...@3]]), function('Test1', {...@0})], function('Test1', [[[...@5], function('Test1', [[...@5]]), function('Test1', {...@0})]]), function('Test1', {...@0})], {...@0}), 'f': function('<SNR>1_Test2'), 'v': v:true}]=]),
|
|
|
|
redir_exec('echo String(extend(extend(g:d, {"f": g:Test2_f}), {"p": function(g:d.f, l)}))'))
|
2017-07-03 16:06:04 -07:00
|
|
|
end)
|
|
|
|
end)
|
|
|
|
|
|
|
|
describe('used to represent lists', function()
|
|
|
|
it('dumps empty list', function()
|
2017-07-03 16:22:26 -07:00
|
|
|
eq('[]', funcs.String({}))
|
2017-07-03 16:06:04 -07:00
|
|
|
end)
|
|
|
|
|
|
|
|
it('dumps nested lists', function()
|
2017-07-03 16:22:26 -07:00
|
|
|
eq('[[[[[]]]]]', funcs.String({{{{{}}}}}))
|
2017-07-03 16:06:04 -07:00
|
|
|
end)
|
|
|
|
|
|
|
|
it('dumps nested non-empty lists', function()
|
2017-07-03 16:22:26 -07:00
|
|
|
eq('[1, [[3, [[5], 4]], 2]]', funcs.String({1, {{3, {{5}, 4}}, 2}}))
|
2017-07-03 16:06:04 -07:00
|
|
|
end)
|
|
|
|
|
2017-07-03 16:22:26 -07:00
|
|
|
it('does not error when dumping recursive lists', function()
|
2017-07-03 16:06:04 -07:00
|
|
|
meths.set_var('l', {})
|
|
|
|
eval('add(l, l)')
|
2017-07-03 16:22:26 -07:00
|
|
|
eq(0, exc_exec('echo String(l)'))
|
2017-07-03 16:06:04 -07:00
|
|
|
end)
|
|
|
|
|
2017-07-03 16:22:26 -07:00
|
|
|
it('dumps recursive lists without error', function()
|
2017-07-03 16:06:04 -07:00
|
|
|
meths.set_var('l', {})
|
|
|
|
eval('add(l, l)')
|
2017-07-03 16:22:26 -07:00
|
|
|
eq('\n[[...@0]]\n[[...@0]]', redir_exec('echo String(l)'))
|
|
|
|
eq('\n[[[...@1]]]\n[[[...@1]]]', redir_exec('echo String([l])'))
|
2017-07-03 16:06:04 -07:00
|
|
|
end)
|
|
|
|
end)
|
|
|
|
|
|
|
|
describe('used to represent dictionaries', function()
|
|
|
|
it('dumps empty dictionary', function()
|
2017-07-03 16:22:26 -07:00
|
|
|
eq('{}', eval('String({})'))
|
2017-07-03 16:06:04 -07:00
|
|
|
end)
|
|
|
|
|
|
|
|
it('dumps list with two same empty dictionaries, also in partials', function()
|
|
|
|
command('let d = {}')
|
2017-07-03 16:22:26 -07:00
|
|
|
eq('[{}, {}]', eval('String([d, d])'))
|
|
|
|
eq('[function(\'tr\', {}), {}]', eval('String([function("tr", d), d])'))
|
|
|
|
eq('[{}, function(\'tr\', {})]', eval('String([d, function("tr", d)])'))
|
2017-07-03 16:06:04 -07:00
|
|
|
end)
|
|
|
|
|
|
|
|
it('dumps non-empty dictionary', function()
|
2017-07-03 16:22:26 -07:00
|
|
|
eq('{\'t\'\'est\': 1}', funcs.String({['t\'est']=1}))
|
2017-07-03 16:06:04 -07:00
|
|
|
end)
|
|
|
|
|
2017-07-03 16:22:26 -07:00
|
|
|
it('does not error when dumping recursive dictionaries', function()
|
2017-07-03 16:06:04 -07:00
|
|
|
meths.set_var('d', {d=1})
|
|
|
|
eval('extend(d, {"d": d})')
|
2017-07-03 16:22:26 -07:00
|
|
|
eq(0, exc_exec('echo String(d)'))
|
2017-07-03 16:06:04 -07:00
|
|
|
end)
|
|
|
|
|
2017-07-03 16:22:26 -07:00
|
|
|
it('dumps recursive dictionaries without the error', function()
|
2017-07-03 16:06:04 -07:00
|
|
|
meths.set_var('d', {d=1})
|
|
|
|
eval('extend(d, {"d": d})')
|
2017-07-03 16:22:26 -07:00
|
|
|
eq('\n{\'d\': {...@0}}\n{\'d\': {...@0}}',
|
|
|
|
redir_exec('echo String(d)'))
|
|
|
|
eq('\n{\'out\': {\'d\': {...@1}}}\n{\'out\': {\'d\': {...@1}}}',
|
|
|
|
redir_exec('echo String({"out": d})'))
|
2017-07-03 16:06:04 -07:00
|
|
|
end)
|
|
|
|
end)
|
|
|
|
end)
|