string_spec.lua (9659B)
1 local t = require('test.testutil') 2 local n = require('test.functional.testnvim')() 3 4 local clear = n.clear 5 local eq = t.eq 6 local command = n.command 7 local api = n.api 8 local eval = n.eval 9 local exc_exec = n.exc_exec 10 local pcall_err = t.pcall_err 11 local fn = n.fn 12 local NIL = vim.NIL 13 local source = n.source 14 15 describe('string() function', function() 16 setup(clear) 17 18 describe('used to represent floating-point values', function() 19 it('dumps NaN values', function() 20 eq("str2float('nan')", eval("string(str2float('nan'))")) 21 end) 22 23 it('dumps infinite values', function() 24 eq("str2float('inf')", eval("string(str2float('inf'))")) 25 eq("-str2float('inf')", eval("string(str2float('-inf'))")) 26 end) 27 28 it('dumps regular values', function() 29 eq('1.5', fn.string(1.5)) 30 eq('1.56e-20', fn.string(1.56000e-020)) 31 eq('0.0', eval('string(0.0)')) 32 end) 33 34 it('dumps special v: values', function() 35 eq('v:true', eval('string(v:true)')) 36 eq('v:false', eval('string(v:false)')) 37 eq('v:null', eval('string(v:null)')) 38 eq('v:true', fn.string(true)) 39 eq('v:false', fn.string(false)) 40 eq('v:null', fn.string(NIL)) 41 end) 42 43 it('dumps values with at most six digits after the decimal point', function() 44 eq('1.234568e-20', fn.string(1.23456789123456789123456789e-020)) 45 eq('1.234568', fn.string(1.23456789123456789123456789)) 46 end) 47 48 it('dumps values with at most seven digits before the decimal point', function() 49 eq('1234567.891235', fn.string(1234567.89123456789123456789)) 50 eq('1.234568e7', fn.string(12345678.9123456789123456789)) 51 end) 52 53 it('dumps negative values', function() 54 eq('-1.5', fn.string(-1.5)) 55 eq('-1.56e-20', fn.string(-1.56000e-020)) 56 eq('-1.234568e-20', fn.string(-1.23456789123456789123456789e-020)) 57 eq('-1.234568', fn.string(-1.23456789123456789123456789)) 58 eq('-1234567.891235', fn.string(-1234567.89123456789123456789)) 59 eq('-1.234568e7', fn.string(-12345678.9123456789123456789)) 60 end) 61 end) 62 63 describe('used to represent numbers', function() 64 it('dumps regular values', function() 65 eq('0', fn.string(0)) 66 eq('-1', fn.string(-1)) 67 eq('1', fn.string(1)) 68 end) 69 70 it('dumps large values', function() 71 eq('2147483647', fn.string(2 ^ 31 - 1)) 72 eq('-2147483648', fn.string(-2 ^ 31)) 73 end) 74 end) 75 76 describe('used to represent strings', function() 77 it('dumps regular strings', function() 78 eq("'test'", fn.string('test')) 79 end) 80 81 it('dumps empty strings', function() 82 eq("''", fn.string('')) 83 end) 84 85 it("dumps strings with ' inside", function() 86 eq("''''''''", fn.string("'''")) 87 eq("'a''b'''''", fn.string("a'b''")) 88 eq("'''b''''d'", fn.string("'b''d")) 89 eq("'a''b''c''d'", fn.string("a'b'c'd")) 90 end) 91 92 it('dumps NULL strings', function() 93 eq("''", eval('string($XXX_UNEXISTENT_VAR_XXX)')) 94 end) 95 96 it('dumps NULL lists', function() 97 eq('[]', eval('string(v:_null_list)')) 98 end) 99 100 it('dumps NULL dictionaries', function() 101 eq('{}', eval('string(v:_null_dict)')) 102 end) 103 end) 104 105 describe('used to represent funcrefs', function() 106 setup(function() 107 source([[ 108 function Test1() 109 endfunction 110 111 function s:Test2() dict 112 endfunction 113 114 function g:Test3() dict 115 endfunction 116 117 let g:Test2_f = function('s:Test2') 118 ]]) 119 end) 120 121 it('dumps references to built-in functions', function() 122 eq("function('function')", eval('string(function("function"))')) 123 end) 124 125 it('dumps references to user functions', function() 126 eq("function('Test1')", eval('string(function("Test1"))')) 127 eq("function('g:Test3')", eval('string(function("g:Test3"))')) 128 end) 129 130 it('dumps references to script functions', function() 131 eq("function('<SNR>1_Test2')", eval('string(Test2_f)')) 132 end) 133 134 it('dumps partials with self referencing a partial', function() 135 source([[ 136 function TestDict() dict 137 endfunction 138 let d = {} 139 let TestDictRef = function('TestDict', d) 140 let d.tdr = TestDictRef 141 ]]) 142 eq( 143 'Vim(echo):E724: unable to correctly dump variable with self-referencing container', 144 pcall_err(command, 'echo string(d.tdr)') 145 ) 146 end) 147 148 it('dumps automatically created partials', function() 149 eq( 150 "function('<SNR>1_Test2', {'f': function('<SNR>1_Test2')})", 151 eval('string({"f": Test2_f}.f)') 152 ) 153 eq( 154 "function('<SNR>1_Test2', [1], {'f': function('<SNR>1_Test2', [1])})", 155 eval('string({"f": function(Test2_f, [1])}.f)') 156 ) 157 end) 158 159 it('dumps manually created partials', function() 160 eq("function('Test3', [1, 2], {})", eval('string(function("Test3", [1, 2], {}))')) 161 eq("function('Test3', {})", eval('string(function("Test3", {}))')) 162 eq("function('Test3', [1, 2])", eval('string(function("Test3", [1, 2]))')) 163 end) 164 165 it('does not crash or halt when dumping partials with reference cycles in self', function() 166 api.nvim_set_var('d', { v = true }) 167 eq( 168 [[Vim(echo):E724: unable to correctly dump variable with self-referencing container]], 169 pcall_err(command, 'echo string(extend(extend(g:d, {"f": g:Test2_f}), {"p": g:d.f}))') 170 ) 171 end) 172 173 it('does not show errors when dumping partials referencing the same dict', function() 174 command('let d = {}') 175 -- Regression for “eval/typval_encode: Dump empty dict before 176 -- checking for refcycle”, results in error. 177 eq( 178 "[function('tr', {}), function('tr', {})]", 179 eval('string([function("tr", d), function("tr", d)])') 180 ) 181 -- Regression for “eval: Work with reference cycles in partials (self) 182 -- properly”, results in crash. 183 eval('extend(d, {"a": 1})') 184 eq( 185 "[function('tr', {'a': 1}), function('tr', {'a': 1})]", 186 eval('string([function("tr", d), function("tr", d)])') 187 ) 188 end) 189 190 it('does not crash or halt when dumping partials with reference cycles in arguments', function() 191 api.nvim_set_var('l', {}) 192 eval('add(l, l)') 193 -- Regression: the below line used to crash (add returns original list and 194 -- there was error in dumping partials). Tested explicitly in 195 -- test/unit/api/private_helpers_spec.lua. 196 eval('add(l, function("Test1", l))') 197 eq( 198 [=[Vim(echo):E724: unable to correctly dump variable with self-referencing container]=], 199 pcall_err(command, 'echo string(function("Test1", l))') 200 ) 201 end) 202 203 it( 204 'does not crash or halt when dumping partials with reference cycles in self and arguments', 205 function() 206 api.nvim_set_var('d', { v = true }) 207 api.nvim_set_var('l', {}) 208 eval('add(l, l)') 209 eval('add(l, function("Test1", l))') 210 eval('add(l, function("Test1", d))') 211 eq( 212 [=[Vim(echo):E724: unable to correctly dump variable with self-referencing container]=], 213 pcall_err( 214 command, 215 'echo string(extend(extend(g:d, {"f": g:Test2_f}), {"p": function(g:d.f, l)}))' 216 ) 217 ) 218 end 219 ) 220 end) 221 222 describe('used to represent lists', function() 223 it('dumps empty list', function() 224 eq('[]', fn.string({})) 225 end) 226 227 it('dumps nested lists', function() 228 eq('[[[[[]]]]]', fn.string({ { { { {} } } } })) 229 end) 230 231 it('dumps nested non-empty lists', function() 232 eq('[1, [[3, [[5], 4]], 2]]', fn.string({ 1, { { 3, { { 5 }, 4 } }, 2 } })) 233 end) 234 235 it('errors when dumping recursive lists', function() 236 api.nvim_set_var('l', {}) 237 eval('add(l, l)') 238 eq( 239 'Vim(echo):E724: unable to correctly dump variable with self-referencing container', 240 exc_exec('echo string(l)') 241 ) 242 end) 243 244 it('dumps recursive lists despite the error', function() 245 api.nvim_set_var('l', {}) 246 eval('add(l, l)') 247 eq( 248 'Vim(echo):E724: unable to correctly dump variable with self-referencing container', 249 pcall_err(command, 'echo string(l)') 250 ) 251 eq( 252 'Vim(echo):E724: unable to correctly dump variable with self-referencing container', 253 pcall_err(command, 'echo string([l])') 254 ) 255 end) 256 end) 257 258 describe('used to represent dictionaries', function() 259 it('dumps empty dict', function() 260 eq('{}', eval('string({})')) 261 end) 262 263 it('dumps list with two same empty dictionaries, also in partials', function() 264 command('let d = {}') 265 eq('[{}, {}]', eval('string([d, d])')) 266 eq("[function('tr', {}), {}]", eval('string([function("tr", d), d])')) 267 eq("[{}, function('tr', {})]", eval('string([d, function("tr", d)])')) 268 end) 269 270 it('dumps non-empty dict', function() 271 eq("{'t''est': 1}", fn.string({ ["t'est"] = 1 })) 272 end) 273 274 it('errors when dumping recursive dictionaries', function() 275 api.nvim_set_var('d', { d = 1 }) 276 eval('extend(d, {"d": d})') 277 eq( 278 'Vim(echo):E724: unable to correctly dump variable with self-referencing container', 279 exc_exec('echo string(d)') 280 ) 281 end) 282 283 it('dumps recursive dictionaries despite the error', function() 284 api.nvim_set_var('d', { d = 1 }) 285 eval('extend(d, {"d": d})') 286 eq( 287 'Vim(echo):E724: unable to correctly dump variable with self-referencing container', 288 pcall_err(command, 'echo string(d)') 289 ) 290 eq( 291 'Vim(echo):E724: unable to correctly dump variable with self-referencing container', 292 pcall_err(command, 'echo string({"out": d})') 293 ) 294 end) 295 end) 296 end)