From b199194a2cde4e296d26c07e5c6d88cee477487c Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 4 Jul 2017 02:06:04 +0300 Subject: functests: Copy eval/string_spec.lua to ex_cmds/echo_spec.lua --- test/functional/ex_cmds/echo_spec.lua | 277 ++++++++++++++++++++++++++++++++++ 1 file changed, 277 insertions(+) create mode 100644 test/functional/ex_cmds/echo_spec.lua diff --git a/test/functional/ex_cmds/echo_spec.lua b/test/functional/ex_cmds/echo_spec.lua new file mode 100644 index 0000000000..adc1af9b8e --- /dev/null +++ b/test/functional/ex_cmds/echo_spec.lua @@ -0,0 +1,277 @@ +local helpers = require('test.functional.helpers')(after_each) +local clear = helpers.clear +local eq = helpers.eq +local command = helpers.command +local meths = helpers.meths +local eval = helpers.eval +local exc_exec = helpers.exc_exec +local redir_exec = helpers.redir_exec +local funcs = helpers.funcs +local NIL = helpers.NIL +local source = helpers.source +local dedent = helpers.dedent + +describe('string() function', function() + before_each(clear) + + describe('used to represent floating-point values', function() + it('dumps NaN values', function() + eq('str2float(\'nan\')', eval('string(str2float(\'nan\'))')) + end) + + it('dumps infinite values', function() + eq('str2float(\'inf\')', eval('string(str2float(\'inf\'))')) + eq('-str2float(\'inf\')', eval('string(str2float(\'-inf\'))')) + end) + + it('dumps regular values', function() + eq('1.5', funcs.string(1.5)) + eq('1.56e-20', funcs.string(1.56000e-020)) + eq('0.0', eval('string(0.0)')) + end) + + it('dumps special v: values', function() + 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)) + end) + + it('dumps values with at most six digits after the decimal point', + function() + eq('1.234568e-20', funcs.string(1.23456789123456789123456789e-020)) + eq('1.234568', funcs.string(1.23456789123456789123456789)) + end) + + it('dumps values with at most seven digits before the decimal point', + function() + eq('1234567.891235', funcs.string(1234567.89123456789123456789)) + eq('1.234568e7', funcs.string(12345678.9123456789123456789)) + end) + + it('dumps negative values', function() + 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)) + end) + end) + + describe('used to represent numbers', function() + it('dumps regular values', function() + eq('0', funcs.string(0)) + eq('-1', funcs.string(-1)) + eq('1', funcs.string(1)) + end) + + it('dumps large values', function() + eq('2147483647', funcs.string(2^31-1)) + eq('-2147483648', funcs.string(-2^31)) + end) + end) + + describe('used to represent strings', function() + it('dumps regular strings', function() + eq('\'test\'', funcs.string('test')) + end) + + it('dumps empty strings', function() + eq('\'\'', funcs.string('')) + end) + + it('dumps strings with \' inside', function() + 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')) + end) + + it('dumps NULL strings', function() + eq('\'\'', eval('string($XXX_UNEXISTENT_VAR_XXX)')) + end) + + it('dumps NULL lists', function() + eq('[]', eval('string(v:_null_list)')) + end) + + it('dumps NULL dictionaries', function() + eq('{}', eval('string(v:_null_dict)')) + 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() + eq('function(\'function\')', eval('string(function("function"))')) + end) + + it('dumps references to user functions', function() + eq('function(\'Test1\')', eval('string(function("Test1"))')) + eq('function(\'g:Test3\')', eval('string(function("g:Test3"))')) + end) + + it('dumps references to script functions', function() + eq('function(\'1_Test2\')', eval('string(Test2_f)')) + 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 + ]]) + eq("\nE724: unable to correctly dump variable with self-referencing container\nfunction('TestDict', {'tdr': function('TestDict', {E724@1})})", + redir_exec('echo string(d.tdr)')) + end) + + it('dumps automatically created partials', function() + eq('function(\'1_Test2\', {\'f\': function(\'1_Test2\')})', + eval('string({"f": Test2_f}.f)')) + eq('function(\'1_Test2\', [1], {\'f\': function(\'1_Test2\', [1])})', + eval('string({"f": function(Test2_f, [1])}.f)')) + end) + + it('dumps manually created partials', function() + eq('function(\'Test3\', [1, 2], {})', + eval('string(function("Test3", [1, 2], {}))')) + eq('function(\'Test3\', {})', + eval('string(function("Test3", {}))')) + eq('function(\'Test3\', [1, 2])', + eval('string(function("Test3", [1, 2]))')) + end) + + it('does not crash or halt when dumping partials with reference cycles in self', + function() + meths.set_var('d', {v=true}) + eq(dedent([[ + + E724: unable to correctly dump variable with self-referencing container + {'p': function('1_Test2', {E724@0}), 'f': function('1_Test2'), 'v': v:true}]]), + redir_exec('echo string(extend(extend(g:d, {"f": g:Test2_f}), {"p": g:d.f}))')) + 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. + eq('[function(\'tr\', {}), function(\'tr\', {})]', eval('string([function("tr", d), function("tr", d)])')) + -- Regression for “eval: Work with reference cycles in partials (self) + -- properly”, results in crash. + eval('extend(d, {"a": 1})') + eq('[function(\'tr\', {\'a\': 1}), function(\'tr\', {\'a\': 1})]', eval('string([function("tr", d), function("tr", d)])')) + 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([=[ + + E724: unable to correctly dump variable with self-referencing container + function('Test1', [[{E724@2}, function('Test1', [{E724@2}])], function('Test1', [[{E724@4}, function('Test1', [{E724@4}])]])])]=]), + redir_exec('echo string(function("Test1", l))')) + 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([=[ + + E724: unable to correctly dump variable with self-referencing container + {'p': function('1_Test2', [[{E724@3}, function('Test1', [{E724@3}]), function('Test1', {E724@0})], function('Test1', [[{E724@5}, function('Test1', [{E724@5}]), function('Test1', {E724@0})]]), function('Test1', {E724@0})], {E724@0}), 'f': function('1_Test2'), 'v': v:true}]=]), + redir_exec('echo string(extend(extend(g:d, {"f": g:Test2_f}), {"p": function(g:d.f, l)}))')) + end) + end) + + describe('used to represent lists', function() + it('dumps empty list', function() + eq('[]', funcs.string({})) + end) + + it('dumps nested lists', function() + eq('[[[[[]]]]]', funcs.string({{{{{}}}}})) + end) + + it('dumps nested non-empty lists', function() + eq('[1, [[3, [[5], 4]], 2]]', funcs.string({1, {{3, {{5}, 4}}, 2}})) + end) + + it('errors when dumping recursive lists', function() + meths.set_var('l', {}) + eval('add(l, l)') + eq('Vim(echo):E724: unable to correctly dump variable with self-referencing container', + exc_exec('echo string(l)')) + end) + + it('dumps recursive lists despite the error', function() + meths.set_var('l', {}) + eval('add(l, l)') + eq('\nE724: unable to correctly dump variable with self-referencing container\n[{E724@0}]', + redir_exec('echo string(l)')) + eq('\nE724: unable to correctly dump variable with self-referencing container\n[[{E724@1}]]', + redir_exec('echo string([l])')) + end) + end) + + describe('used to represent dictionaries', function() + it('dumps empty dictionary', function() + eq('{}', eval('string({})')) + end) + + it('dumps list with two same empty dictionaries, also in partials', function() + command('let d = {}') + eq('[{}, {}]', eval('string([d, d])')) + eq('[function(\'tr\', {}), {}]', eval('string([function("tr", d), d])')) + eq('[{}, function(\'tr\', {})]', eval('string([d, function("tr", d)])')) + end) + + it('dumps non-empty dictionary', function() + eq('{\'t\'\'est\': 1}', funcs.string({['t\'est']=1})) + end) + + it('errors when dumping recursive dictionaries', function() + meths.set_var('d', {d=1}) + eval('extend(d, {"d": d})') + eq('Vim(echo):E724: unable to correctly dump variable with self-referencing container', + exc_exec('echo string(d)')) + end) + + it('dumps recursive dictionaries despite the error', function() + meths.set_var('d', {d=1}) + eval('extend(d, {"d": d})') + eq('\nE724: unable to correctly dump variable with self-referencing container\n{\'d\': {E724@0}}', + redir_exec('echo string(d)')) + eq('\nE724: unable to correctly dump variable with self-referencing container\n{\'out\': {\'d\': {E724@1}}}', + redir_exec('echo string({"out": d})')) + end) + end) +end) -- cgit From e07e46f53921aa75bc826290098491e4b5622448 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 4 Jul 2017 02:06:40 +0300 Subject: message: Fix `:echo "\x80"` printing `~@<80>` --- src/nvim/message.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nvim/message.c b/src/nvim/message.c index 8a9d8e1bc6..36f9ca84ed 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1196,7 +1196,7 @@ int msg_outtrans_len_attr(char_u *msgstr, int len, int attr) len -= mb_l - 1; str += mb_l; } else { - s = transchar_byte(*str); + s = transchar_byte((uint8_t)(*str)); if (s[1] != NUL) { // Unprintable char: print the printable chars so far and the // translation of the unprintable char. -- cgit From d113d3d737641d7668361b2e08cea53411308146 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 4 Jul 2017 02:22:26 +0300 Subject: functests: Make ex_cmds/echo actually use :echo --- test/functional/ex_cmds/echo_spec.lua | 187 ++++++++++++++++++---------------- 1 file changed, 97 insertions(+), 90 deletions(-) diff --git a/test/functional/ex_cmds/echo_spec.lua b/test/functional/ex_cmds/echo_spec.lua index adc1af9b8e..06436fb87e 100644 --- a/test/functional/ex_cmds/echo_spec.lua +++ b/test/functional/ex_cmds/echo_spec.lua @@ -1,105 +1,113 @@ local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear + local eq = helpers.eq -local command = helpers.command -local meths = helpers.meths +local NIL = helpers.NIL local eval = helpers.eval -local exc_exec = helpers.exc_exec -local redir_exec = helpers.redir_exec +local clear = helpers.clear +local meths = helpers.meths local funcs = helpers.funcs -local NIL = helpers.NIL local source = helpers.source local dedent = helpers.dedent +local command = helpers.command +local exc_exec = helpers.exc_exec +local redir_exec = helpers.redir_exec -describe('string() function', function() - before_each(clear) +describe(':echo', function() + before_each(function() + clear() + source([[ + function String(s) + return execute('echo a:s')[1:] + endfunction + ]]) + end) describe('used to represent floating-point values', function() it('dumps NaN values', function() - eq('str2float(\'nan\')', eval('string(str2float(\'nan\'))')) + eq('str2float(\'nan\')', eval('String(str2float(\'nan\'))')) end) it('dumps infinite values', function() - eq('str2float(\'inf\')', eval('string(str2float(\'inf\'))')) - eq('-str2float(\'inf\')', eval('string(str2float(\'-inf\'))')) + eq('str2float(\'inf\')', eval('String(str2float(\'inf\'))')) + eq('-str2float(\'inf\')', eval('String(str2float(\'-inf\'))')) end) it('dumps regular values', function() - eq('1.5', funcs.string(1.5)) - eq('1.56e-20', funcs.string(1.56000e-020)) - eq('0.0', eval('string(0.0)')) + eq('1.5', funcs.String(1.5)) + eq('1.56e-20', funcs.String(1.56000e-020)) + eq('0.0', eval('String(0.0)')) end) it('dumps special v: values', function() - 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)) + 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)) end) it('dumps values with at most six digits after the decimal point', function() - eq('1.234568e-20', funcs.string(1.23456789123456789123456789e-020)) - eq('1.234568', funcs.string(1.23456789123456789123456789)) + eq('1.234568e-20', funcs.String(1.23456789123456789123456789e-020)) + eq('1.234568', funcs.String(1.23456789123456789123456789)) end) it('dumps values with at most seven digits before the decimal point', function() - eq('1234567.891235', funcs.string(1234567.89123456789123456789)) - eq('1.234568e7', funcs.string(12345678.9123456789123456789)) + eq('1234567.891235', funcs.String(1234567.89123456789123456789)) + eq('1.234568e7', funcs.String(12345678.9123456789123456789)) end) it('dumps negative values', function() - 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)) + 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)) end) end) describe('used to represent numbers', function() it('dumps regular values', function() - eq('0', funcs.string(0)) - eq('-1', funcs.string(-1)) - eq('1', funcs.string(1)) + eq('0', funcs.String(0)) + eq('-1', funcs.String(-1)) + eq('1', funcs.String(1)) end) it('dumps large values', function() - eq('2147483647', funcs.string(2^31-1)) - eq('-2147483648', funcs.string(-2^31)) + eq('2147483647', funcs.String(2^31-1)) + eq('-2147483648', funcs.String(-2^31)) end) end) describe('used to represent strings', function() it('dumps regular strings', function() - eq('\'test\'', funcs.string('test')) + eq('test', funcs.String('test')) end) it('dumps empty strings', function() - eq('\'\'', funcs.string('')) + eq('', funcs.String('')) end) it('dumps strings with \' inside', function() - 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')) + 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')) end) it('dumps NULL strings', function() - eq('\'\'', eval('string($XXX_UNEXISTENT_VAR_XXX)')) + eq('', eval('String($XXX_UNEXISTENT_VAR_XXX)')) end) it('dumps NULL lists', function() - eq('[]', eval('string(v:_null_list)')) + eq('[]', eval('String(v:_null_list)')) end) it('dumps NULL dictionaries', function() - eq('{}', eval('string(v:_null_dict)')) + eq('{}', eval('String(v:_null_dict)')) end) end) @@ -120,16 +128,16 @@ describe('string() function', function() end) it('dumps references to built-in functions', function() - eq('function(\'function\')', eval('string(function("function"))')) + eq('function', eval('String(function("function"))')) end) it('dumps references to user functions', function() - eq('function(\'Test1\')', eval('string(function("Test1"))')) - eq('function(\'g:Test3\')', eval('string(function("g:Test3"))')) + eq('Test1', eval('String(function("Test1"))')) + eq('g:Test3', eval('String(function("g:Test3"))')) end) it('dumps references to script functions', function() - eq('function(\'1_Test2\')', eval('string(Test2_f)')) + eq('1_Test2', eval('String(Test2_f)')) end) it('dumps partials with self referencing a partial', function() @@ -140,24 +148,27 @@ describe('string() function', function() let TestDictRef = function('TestDict', d) let d.tdr = TestDictRef ]]) - eq("\nE724: unable to correctly dump variable with self-referencing container\nfunction('TestDict', {'tdr': function('TestDict', {E724@1})})", - redir_exec('echo string(d.tdr)')) + eq(dedent([[ + + function('TestDict', {'tdr': function('TestDict', {...@1})}) + function('TestDict', {'tdr': function('TestDict', {...@1})})]]), + redir_exec('echo String(d.tdr)')) end) it('dumps automatically created partials', function() eq('function(\'1_Test2\', {\'f\': function(\'1_Test2\')})', - eval('string({"f": Test2_f}.f)')) + eval('String({"f": Test2_f}.f)')) eq('function(\'1_Test2\', [1], {\'f\': function(\'1_Test2\', [1])})', - eval('string({"f": function(Test2_f, [1])}.f)')) + eval('String({"f": function(Test2_f, [1])}.f)')) end) it('dumps manually created partials', function() eq('function(\'Test3\', [1, 2], {})', - eval('string(function("Test3", [1, 2], {}))')) + eval('String(function("Test3", [1, 2], {}))')) eq('function(\'Test3\', {})', - eval('string(function("Test3", {}))')) + eval('String(function("Test3", {}))')) eq('function(\'Test3\', [1, 2])', - eval('string(function("Test3", [1, 2]))')) + eval('String(function("Test3", [1, 2]))')) end) it('does not crash or halt when dumping partials with reference cycles in self', @@ -165,9 +176,9 @@ describe('string() function', function() meths.set_var('d', {v=true}) eq(dedent([[ - E724: unable to correctly dump variable with self-referencing container - {'p': function('1_Test2', {E724@0}), 'f': function('1_Test2'), 'v': v:true}]]), - redir_exec('echo string(extend(extend(g:d, {"f": g:Test2_f}), {"p": g:d.f}))')) + {'p': function('1_Test2', {...@0}), 'f': function('1_Test2'), 'v': v:true} + {'p': function('1_Test2', {...@0}), 'f': function('1_Test2'), 'v': v:true}]]), + redir_exec('echo String(extend(extend(g:d, {"f": g:Test2_f}), {"p": g:d.f}))')) end) it('does not show errors when dumping partials referencing the same dictionary', @@ -175,11 +186,11 @@ describe('string() function', function() command('let d = {}') -- Regression for “eval/typval_encode: Dump empty dictionary before -- checking for refcycle”, results in error. - eq('[function(\'tr\', {}), function(\'tr\', {})]', eval('string([function("tr", d), function("tr", d)])')) + eq('[function(\'tr\', {}), function(\'tr\', {})]', eval('String([function("tr", d), function("tr", d)])')) -- Regression for “eval: Work with reference cycles in partials (self) -- properly”, results in crash. eval('extend(d, {"a": 1})') - eq('[function(\'tr\', {\'a\': 1}), function(\'tr\', {\'a\': 1})]', eval('string([function("tr", d), function("tr", d)])')) + eq('[function(\'tr\', {\'a\': 1}), function(\'tr\', {\'a\': 1})]', eval('String([function("tr", d), function("tr", d)])')) end) it('does not crash or halt when dumping partials with reference cycles in arguments', @@ -192,9 +203,9 @@ describe('string() function', function() eval('add(l, function("Test1", l))') eq(dedent([=[ - E724: unable to correctly dump variable with self-referencing container - function('Test1', [[{E724@2}, function('Test1', [{E724@2}])], function('Test1', [[{E724@4}, function('Test1', [{E724@4}])]])])]=]), - redir_exec('echo string(function("Test1", l))')) + 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))')) end) it('does not crash or halt when dumping partials with reference cycles in self and arguments', @@ -206,72 +217,68 @@ describe('string() function', function() eval('add(l, function("Test1", d))') eq(dedent([=[ - E724: unable to correctly dump variable with self-referencing container - {'p': function('1_Test2', [[{E724@3}, function('Test1', [{E724@3}]), function('Test1', {E724@0})], function('Test1', [[{E724@5}, function('Test1', [{E724@5}]), function('Test1', {E724@0})]]), function('Test1', {E724@0})], {E724@0}), 'f': function('1_Test2'), 'v': v:true}]=]), - redir_exec('echo string(extend(extend(g:d, {"f": g:Test2_f}), {"p": function(g:d.f, l)}))')) + {'p': function('1_Test2', [[[...@3], function('Test1', [[...@3]]), function('Test1', {...@0})], function('Test1', [[[...@5], function('Test1', [[...@5]]), function('Test1', {...@0})]]), function('Test1', {...@0})], {...@0}), 'f': function('1_Test2'), 'v': v:true} + {'p': function('1_Test2', [[[...@3], function('Test1', [[...@3]]), function('Test1', {...@0})], function('Test1', [[[...@5], function('Test1', [[...@5]]), function('Test1', {...@0})]]), function('Test1', {...@0})], {...@0}), 'f': function('1_Test2'), 'v': v:true}]=]), + redir_exec('echo String(extend(extend(g:d, {"f": g:Test2_f}), {"p": function(g:d.f, l)}))')) end) end) describe('used to represent lists', function() it('dumps empty list', function() - eq('[]', funcs.string({})) + eq('[]', funcs.String({})) end) it('dumps nested lists', function() - eq('[[[[[]]]]]', funcs.string({{{{{}}}}})) + eq('[[[[[]]]]]', funcs.String({{{{{}}}}})) end) it('dumps nested non-empty lists', function() - eq('[1, [[3, [[5], 4]], 2]]', funcs.string({1, {{3, {{5}, 4}}, 2}})) + eq('[1, [[3, [[5], 4]], 2]]', funcs.String({1, {{3, {{5}, 4}}, 2}})) end) - it('errors when dumping recursive lists', function() + it('does not error when dumping recursive lists', function() meths.set_var('l', {}) eval('add(l, l)') - eq('Vim(echo):E724: unable to correctly dump variable with self-referencing container', - exc_exec('echo string(l)')) + eq(0, exc_exec('echo String(l)')) end) - it('dumps recursive lists despite the error', function() + it('dumps recursive lists without error', function() meths.set_var('l', {}) eval('add(l, l)') - eq('\nE724: unable to correctly dump variable with self-referencing container\n[{E724@0}]', - redir_exec('echo string(l)')) - eq('\nE724: unable to correctly dump variable with self-referencing container\n[[{E724@1}]]', - redir_exec('echo string([l])')) + eq('\n[[...@0]]\n[[...@0]]', redir_exec('echo String(l)')) + eq('\n[[[...@1]]]\n[[[...@1]]]', redir_exec('echo String([l])')) end) end) describe('used to represent dictionaries', function() it('dumps empty dictionary', function() - eq('{}', eval('string({})')) + eq('{}', eval('String({})')) end) it('dumps list with two same empty dictionaries, also in partials', function() command('let d = {}') - eq('[{}, {}]', eval('string([d, d])')) - eq('[function(\'tr\', {}), {}]', eval('string([function("tr", d), d])')) - eq('[{}, function(\'tr\', {})]', eval('string([d, function("tr", d)])')) + eq('[{}, {}]', eval('String([d, d])')) + eq('[function(\'tr\', {}), {}]', eval('String([function("tr", d), d])')) + eq('[{}, function(\'tr\', {})]', eval('String([d, function("tr", d)])')) end) it('dumps non-empty dictionary', function() - eq('{\'t\'\'est\': 1}', funcs.string({['t\'est']=1})) + eq('{\'t\'\'est\': 1}', funcs.String({['t\'est']=1})) end) - it('errors when dumping recursive dictionaries', function() + it('does not error when dumping recursive dictionaries', function() meths.set_var('d', {d=1}) eval('extend(d, {"d": d})') - eq('Vim(echo):E724: unable to correctly dump variable with self-referencing container', - exc_exec('echo string(d)')) + eq(0, exc_exec('echo String(d)')) end) - it('dumps recursive dictionaries despite the error', function() + it('dumps recursive dictionaries without the error', function() meths.set_var('d', {d=1}) eval('extend(d, {"d": d})') - eq('\nE724: unable to correctly dump variable with self-referencing container\n{\'d\': {E724@0}}', - redir_exec('echo string(d)')) - eq('\nE724: unable to correctly dump variable with self-referencing container\n{\'out\': {\'d\': {E724@1}}}', - redir_exec('echo string({"out": d})')) + 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})')) end) end) end) -- cgit From 480598dcda990fd19900b9fe3a01f519cd9b50d0 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 4 Jul 2017 02:38:30 +0300 Subject: functests: Add some more :echo tests which also check for regression Fixes #6954 --- test/functional/ex_cmds/echo_spec.lua | 37 +++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/test/functional/ex_cmds/echo_spec.lua b/test/functional/ex_cmds/echo_spec.lua index 06436fb87e..4936c5af8c 100644 --- a/test/functional/ex_cmds/echo_spec.lua +++ b/test/functional/ex_cmds/echo_spec.lua @@ -281,4 +281,41 @@ describe(':echo', function() redir_exec('echo String({"out": d})')) end) end) + + describe('used to represent special values', function() + local function chr(n) + return ('%c'):format(n) + end + local function ctrl(c) + return ('%c'):format(c:upper():byte() - 0x40) + end + it('displays hex as hex', function() + -- Regression: due to missing (uint8_t) cast \x80 was represented as + -- ~@<80>. + eq('<80>', funcs.String(chr(0x80))) + eq('<81>', funcs.String(chr(0x81))) + eq('<8e>', funcs.String(chr(0x8e))) + eq('', funcs.String(('«'):sub(1, 1))) + eq('«', funcs.String(('«'):sub(1, 2))) + end) + it('displays ASCII control characters using ^X notation', function() + eq('^C', funcs.String(ctrl('c'))) + eq('^A', funcs.String(ctrl('a'))) + eq('^F', funcs.String(ctrl('f'))) + end) + it('prints CR, NL and tab as-is', function() + eq('\n', funcs.String('\n')) + eq('\r', funcs.String('\r')) + eq('\t', funcs.String('\t')) + end) + it('prints non-printable UTF-8 in <> notation', function() + -- SINGLE SHIFT TWO, unicode control + eq('<8e>', funcs.String(funcs.nr2char(0x8E))) + -- Surrogate pair: U+1F0A0 PLAYING CARD BACK is represented in UTF-16 as + -- 0xD83C 0xDCA0. This is not valid in UTF-8. + eq('', funcs.String(funcs.nr2char(0xD83C))) + eq('', funcs.String(funcs.nr2char(0xDCA0))) + eq('', funcs.String(funcs.nr2char(0xD83C) .. funcs.nr2char(0xDCA0))) + end) + end) end) -- cgit From 2208b64891d57a4ab79143183888149be9ee228d Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 4 Jul 2017 15:15:23 +0300 Subject: functests: Ensure different SIDs on successive source() calls --- test/functional/ex_cmds/echo_spec.lua | 14 +++++++------- test/functional/helpers.lua | 17 ++++++++++++++++- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/test/functional/ex_cmds/echo_spec.lua b/test/functional/ex_cmds/echo_spec.lua index 4936c5af8c..10c7230896 100644 --- a/test/functional/ex_cmds/echo_spec.lua +++ b/test/functional/ex_cmds/echo_spec.lua @@ -137,7 +137,7 @@ describe(':echo', function() end) it('dumps references to script functions', function() - eq('1_Test2', eval('String(Test2_f)')) + eq('2_Test2', eval('String(Test2_f)')) end) it('dumps partials with self referencing a partial', function() @@ -156,9 +156,9 @@ describe(':echo', function() end) it('dumps automatically created partials', function() - eq('function(\'1_Test2\', {\'f\': function(\'1_Test2\')})', + eq('function(\'2_Test2\', {\'f\': function(\'2_Test2\')})', eval('String({"f": Test2_f}.f)')) - eq('function(\'1_Test2\', [1], {\'f\': function(\'1_Test2\', [1])})', + eq('function(\'2_Test2\', [1], {\'f\': function(\'2_Test2\', [1])})', eval('String({"f": function(Test2_f, [1])}.f)')) end) @@ -176,8 +176,8 @@ describe(':echo', function() meths.set_var('d', {v=true}) eq(dedent([[ - {'p': function('1_Test2', {...@0}), 'f': function('1_Test2'), 'v': v:true} - {'p': function('1_Test2', {...@0}), 'f': function('1_Test2'), 'v': v:true}]]), + {'p': function('2_Test2', {...@0}), 'f': function('2_Test2'), 'v': v:true} + {'p': function('2_Test2', {...@0}), 'f': function('2_Test2'), 'v': v:true}]]), redir_exec('echo String(extend(extend(g:d, {"f": g:Test2_f}), {"p": g:d.f}))')) end) @@ -217,8 +217,8 @@ describe(':echo', function() eval('add(l, function("Test1", d))') eq(dedent([=[ - {'p': function('1_Test2', [[[...@3], function('Test1', [[...@3]]), function('Test1', {...@0})], function('Test1', [[[...@5], function('Test1', [[...@5]]), function('Test1', {...@0})]]), function('Test1', {...@0})], {...@0}), 'f': function('1_Test2'), 'v': v:true} - {'p': function('1_Test2', [[[...@3], function('Test1', [[...@3]]), function('Test1', {...@0})], function('Test1', [[[...@5], function('Test1', [[...@5]]), function('Test1', {...@0})]]), function('Test1', {...@0})], {...@0}), 'f': function('1_Test2'), 'v': v:true}]=]), + {'p': function('2_Test2', [[[...@3], function('Test1', [[...@3]]), function('Test1', {...@0})], function('Test1', [[[...@5], function('Test1', [[...@5]]), function('Test1', {...@0})]]), function('Test1', {...@0})], {...@0}), 'f': function('2_Test2'), 'v': v:true} + {'p': function('2_Test2', [[[...@3], function('Test1', [[...@3]]), function('Test1', {...@0})], function('Test1', [[[...@5], function('Test1', [[...@5]]), function('Test1', {...@0})]]), function('Test1', {...@0})], {...@0}), 'f': function('2_Test2'), 'v': v:true}]=]), redir_exec('echo String(extend(extend(g:d, {"f": g:Test2_f}), {"p": function(g:d.f, l)}))')) end) end) diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index 4a170d993b..d7858cacd5 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -337,11 +337,23 @@ local function read_file(name) return ret end +local sourced_fnames = {} local function source(code) local fname = tmpname() write_file(fname, code) nvim_command('source '..fname) - os.remove(fname) + -- DO NOT REMOVE FILE HERE. + -- do_source() has a habit of checking whether files are “same” by using inode + -- and device IDs. If you run two source() calls in quick succession there is + -- a good chance that underlying filesystem will reuse the inode, making files + -- appear as “symlinks” to do_source when it checks FileIDs. With current + -- setup linux machines (both QB, travis and mine(ZyX-I) with XFS) do reuse + -- inodes, Mac OS machines (again, both QB and travis) do not. + -- + -- Files appearing as “symlinks” mean that both the first and the second + -- source() calls will use same SID, which may fail some tests which check for + -- exact numbers after `` in e.g. function names. + sourced_fnames[#sourced_fnames + 1] = fname return fname end @@ -673,6 +685,9 @@ local module = { return function(after_each) if after_each then after_each(function() + for _, fname in ipairs(sourced_fnames) do + os.remove(fname) + end check_logs() check_cores('build/bin/nvim') end) -- cgit