string_spec.lua 9.4 KB

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