neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

json_functions_spec.lua (33626B)


      1 local t = require('test.testutil')
      2 local n = require('test.functional.testnvim')()
      3 
      4 local clear = n.clear
      5 local fn = n.fn
      6 local api = n.api
      7 local eq = t.eq
      8 local eval = n.eval
      9 local command = n.command
     10 local exc_exec = n.exc_exec
     11 local pcall_err = t.pcall_err
     12 local NIL = vim.NIL
     13 local source = n.source
     14 
     15 describe('json_decode() function', function()
     16  setup(function()
     17    clear()
     18    source([[
     19      language C
     20      function Eq(exp, act)
     21        let act = a:act
     22        let exp = a:exp
     23        if type(exp) != type(act)
     24          return 0
     25        endif
     26        if type(exp) == type({})
     27          if sort(keys(exp)) !=# sort(keys(act))
     28            return 0
     29          endif
     30          if sort(keys(exp)) ==# ['_TYPE', '_VAL']
     31            let exp_typ = v:msgpack_types[exp._TYPE]
     32            let act_typ = act._TYPE
     33            if exp_typ isnot act_typ
     34              return 0
     35            endif
     36            return Eq(exp._VAL, act._VAL)
     37          else
     38            return empty(filter(copy(exp), '!Eq(v:val, act[v:key])'))
     39          endif
     40        else
     41          if type(exp) == type([])
     42            if len(exp) != len(act)
     43              return 0
     44            endif
     45            return empty(filter(copy(exp), '!Eq(v:val, act[v:key])'))
     46          endif
     47          return exp ==# act
     48        endif
     49        return 1
     50      endfunction
     51      function EvalEq(exp, act_expr)
     52        let act = eval(a:act_expr)
     53        if Eq(a:exp, act)
     54          return 1
     55        else
     56          return string(act)
     57        endif
     58      endfunction
     59    ]])
     60  end)
     61 
     62  local speq = function(expected, actual_expr)
     63    eq(1, fn.EvalEq(expected, actual_expr))
     64  end
     65 
     66  it('accepts readfile()-style list', function()
     67    eq(
     68      { Test = 1 },
     69      fn.json_decode({
     70        '{',
     71        '\t"Test": 1',
     72        '}',
     73      })
     74    )
     75  end)
     76 
     77  it('accepts strings with newlines', function()
     78    eq(
     79      { Test = 1 },
     80      fn.json_decode([[
     81      {
     82        "Test": 1
     83      }
     84    ]])
     85    )
     86  end)
     87 
     88  it('parses null, true, false', function()
     89    eq(NIL, fn.json_decode('null'))
     90    eq(true, fn.json_decode('true'))
     91    eq(false, fn.json_decode('false'))
     92  end)
     93 
     94  it('fails to parse incomplete null, true, false', function()
     95    eq('Vim(call):E474: Expected null: n', exc_exec('call json_decode("n")'))
     96    eq('Vim(call):E474: Expected null: nu', exc_exec('call json_decode("nu")'))
     97    eq('Vim(call):E474: Expected null: nul', exc_exec('call json_decode("nul")'))
     98    eq('Vim(call):E474: Expected null: nul\n\t', exc_exec('call json_decode("nul\\n\\t")'))
     99 
    100    eq('Vim(call):E474: Expected true: t', exc_exec('call json_decode("t")'))
    101    eq('Vim(call):E474: Expected true: tr', exc_exec('call json_decode("tr")'))
    102    eq('Vim(call):E474: Expected true: tru', exc_exec('call json_decode("tru")'))
    103    eq('Vim(call):E474: Expected true: tru\t\n', exc_exec('call json_decode("tru\\t\\n")'))
    104 
    105    eq('Vim(call):E474: Expected false: f', exc_exec('call json_decode("f")'))
    106    eq('Vim(call):E474: Expected false: fa', exc_exec('call json_decode("fa")'))
    107    eq('Vim(call):E474: Expected false: fal', exc_exec('call json_decode("fal")'))
    108    eq('Vim(call):E474: Expected false: fal   <', exc_exec('call json_decode("   fal   <")'))
    109    eq('Vim(call):E474: Expected false: fals', exc_exec('call json_decode("fals")'))
    110  end)
    111 
    112  it('parses integer numbers', function()
    113    eq(100000, fn.json_decode('100000'))
    114    eq(-100000, fn.json_decode('-100000'))
    115    eq(100000, fn.json_decode('  100000  '))
    116    eq(-100000, fn.json_decode('  -100000  '))
    117    eq(0, fn.json_decode('0'))
    118    eq(0, fn.json_decode('-0'))
    119  end)
    120 
    121  it('fails to parse +numbers and .number', function()
    122    eq('Vim(call):E474: Unidentified byte: +1000', exc_exec('call json_decode("+1000")'))
    123    eq('Vim(call):E474: Unidentified byte: .1000', exc_exec('call json_decode(".1000")'))
    124  end)
    125 
    126  it('fails to parse numbers with leading zeroes', function()
    127    eq('Vim(call):E474: Leading zeroes are not allowed: 00.1', exc_exec('call json_decode("00.1")'))
    128    eq('Vim(call):E474: Leading zeroes are not allowed: 01', exc_exec('call json_decode("01")'))
    129    eq('Vim(call):E474: Leading zeroes are not allowed: -01', exc_exec('call json_decode("-01")'))
    130    eq(
    131      'Vim(call):E474: Leading zeroes are not allowed: -001.0',
    132      exc_exec('call json_decode("-001.0")')
    133    )
    134  end)
    135 
    136  it('fails to parse incomplete numbers', function()
    137    eq('Vim(call):E474: Missing number after minus sign: -.1', exc_exec('call json_decode("-.1")'))
    138    eq('Vim(call):E474: Missing number after minus sign: -', exc_exec('call json_decode("-")'))
    139    eq('Vim(call):E474: Missing number after decimal dot: -1.', exc_exec('call json_decode("-1.")'))
    140    eq('Vim(call):E474: Missing number after decimal dot: 0.', exc_exec('call json_decode("0.")'))
    141    eq('Vim(call):E474: Missing exponent: 0.0e', exc_exec('call json_decode("0.0e")'))
    142    eq('Vim(call):E474: Missing exponent: 0.0e+', exc_exec('call json_decode("0.0e+")'))
    143    eq('Vim(call):E474: Missing exponent: 0.0e-', exc_exec('call json_decode("0.0e-")'))
    144    eq('Vim(call):E474: Missing exponent: 0.0e-', exc_exec('call json_decode("0.0e-")'))
    145    eq(
    146      'Vim(call):E474: Missing number after decimal dot: 1.e5',
    147      exc_exec('call json_decode("1.e5")')
    148    )
    149    eq(
    150      'Vim(call):E474: Missing number after decimal dot: 1.e+5',
    151      exc_exec('call json_decode("1.e+5")')
    152    )
    153    eq(
    154      'Vim(call):E474: Missing number after decimal dot: 1.e+',
    155      exc_exec('call json_decode("1.e+")')
    156    )
    157  end)
    158 
    159  it('parses floating-point numbers', function()
    160    -- Also test method call (->) syntax
    161    eq('100000.0', eval('"100000.0"->json_decode()->string()'))
    162    eq(100000.5, fn.json_decode('100000.5'))
    163    eq(-100000.5, fn.json_decode('-100000.5'))
    164    eq(-100000.5e50, fn.json_decode('-100000.5e50'))
    165    eq(100000.5e50, fn.json_decode('100000.5e50'))
    166    eq(100000.5e50, fn.json_decode('100000.5e+50'))
    167    eq(-100000.5e-50, fn.json_decode('-100000.5e-50'))
    168    eq(100000.5e-50, fn.json_decode('100000.5e-50'))
    169    eq(100000e-50, fn.json_decode('100000e-50'))
    170    eq(0.5, fn.json_decode('0.5'))
    171    eq(0.005, fn.json_decode('0.005'))
    172    eq(0.005, fn.json_decode('0.00500'))
    173    eq(0.5, fn.json_decode('0.00500e+002'))
    174    eq(0.00005, fn.json_decode('0.00500e-002'))
    175 
    176    eq(-0.0, fn.json_decode('-0.0'))
    177    eq(-0.0, fn.json_decode('-0.0e0'))
    178    eq(-0.0, fn.json_decode('-0.0e+0'))
    179    eq(-0.0, fn.json_decode('-0.0e-0'))
    180    eq(-0.0, fn.json_decode('-0e-0'))
    181    eq(-0.0, fn.json_decode('-0e-2'))
    182    eq(-0.0, fn.json_decode('-0e+2'))
    183 
    184    eq(0.0, fn.json_decode('0.0'))
    185    eq(0.0, fn.json_decode('0.0e0'))
    186    eq(0.0, fn.json_decode('0.0e+0'))
    187    eq(0.0, fn.json_decode('0.0e-0'))
    188    eq(0.0, fn.json_decode('0e-0'))
    189    eq(0.0, fn.json_decode('0e-2'))
    190    eq(0.0, fn.json_decode('0e+2'))
    191  end)
    192 
    193  it('fails to parse numbers with spaces inside', function()
    194    eq(
    195      'Vim(call):E474: Missing number after minus sign: - 1000',
    196      exc_exec('call json_decode("- 1000")')
    197    )
    198    eq('Vim(call):E474: Missing number after decimal dot: 0. ', exc_exec('call json_decode("0. ")'))
    199    eq(
    200      'Vim(call):E474: Missing number after decimal dot: 0. 0',
    201      exc_exec('call json_decode("0. 0")')
    202    )
    203    eq('Vim(call):E474: Missing exponent: 0.0e 1', exc_exec('call json_decode("0.0e 1")'))
    204    eq('Vim(call):E474: Missing exponent: 0.0e+ 1', exc_exec('call json_decode("0.0e+ 1")'))
    205    eq('Vim(call):E474: Missing exponent: 0.0e- 1', exc_exec('call json_decode("0.0e- 1")'))
    206  end)
    207 
    208  it('fails to parse "," and ":"', function()
    209    eq('Vim(call):E474: Comma not inside container: ,  ', exc_exec('call json_decode("  ,  ")'))
    210    eq('Vim(call):E474: Colon not inside container: :  ', exc_exec('call json_decode("  :  ")'))
    211  end)
    212 
    213  it('parses empty containers', function()
    214    eq({}, fn.json_decode('[]'))
    215    eq('[]', eval('string(json_decode("[]"))'))
    216  end)
    217 
    218  it('fails to parse "[" and "{"', function()
    219    eq('Vim(call):E474: Unexpected end of input: {', exc_exec('call json_decode("{")'))
    220    eq('Vim(call):E474: Unexpected end of input: [', exc_exec('call json_decode("[")'))
    221  end)
    222 
    223  it('fails to parse "}" and "]"', function()
    224    eq('Vim(call):E474: No container to close: ]', exc_exec('call json_decode("]")'))
    225    eq('Vim(call):E474: No container to close: }', exc_exec('call json_decode("}")'))
    226  end)
    227 
    228  it('fails to parse containers which are closed by different brackets', function()
    229    eq(
    230      'Vim(call):E474: Closing dictionary with square bracket: ]',
    231      exc_exec('call json_decode("{]")')
    232    )
    233    eq('Vim(call):E474: Closing list with curly bracket: }', exc_exec('call json_decode("[}")'))
    234  end)
    235 
    236  it('fails to parse concat inside container', function()
    237    eq(
    238      'Vim(call):E474: Expected comma before list item: []]',
    239      exc_exec('call json_decode("[[][]]")')
    240    )
    241    eq(
    242      'Vim(call):E474: Expected comma before list item: {}]',
    243      exc_exec('call json_decode("[{}{}]")')
    244    )
    245    eq('Vim(call):E474: Expected comma before list item: ]', exc_exec('call json_decode("[1 2]")'))
    246    eq(
    247      'Vim(call):E474: Expected comma before dictionary key: ": 4}',
    248      exc_exec('call json_decode("{\\"1\\": 2 \\"3\\": 4}")')
    249    )
    250    eq(
    251      'Vim(call):E474: Expected colon before dictionary value: , "3" 4}',
    252      exc_exec('call json_decode("{\\"1\\" 2, \\"3\\" 4}")')
    253    )
    254  end)
    255 
    256  it('fails to parse containers with leading comma or colon', function()
    257    eq('Vim(call):E474: Leading comma: ,}', exc_exec('call json_decode("{,}")'))
    258    eq('Vim(call):E474: Leading comma: ,]', exc_exec('call json_decode("[,]")'))
    259    eq('Vim(call):E474: Using colon not in dictionary: :]', exc_exec('call json_decode("[:]")'))
    260    eq('Vim(call):E474: Unexpected colon: :}', exc_exec('call json_decode("{:}")'))
    261  end)
    262 
    263  it('fails to parse containers with trailing comma', function()
    264    eq('Vim(call):E474: Trailing comma: ]', exc_exec('call json_decode("[1,]")'))
    265    eq('Vim(call):E474: Trailing comma: }', exc_exec('call json_decode("{\\"1\\": 2,}")'))
    266  end)
    267 
    268  it('fails to parse dictionaries with missing value', function()
    269    eq('Vim(call):E474: Expected value after colon: }', exc_exec('call json_decode("{\\"1\\":}")'))
    270    eq('Vim(call):E474: Expected value: }', exc_exec('call json_decode("{\\"1\\"}")'))
    271  end)
    272 
    273  it('fails to parse containers with two commas or colons', function()
    274    eq(
    275      'Vim(call):E474: Duplicate comma: , "2": 2}',
    276      exc_exec('call json_decode("{\\"1\\": 1,, \\"2\\": 2}")')
    277    )
    278    eq(
    279      'Vim(call):E474: Duplicate comma: , "2", 2]',
    280      exc_exec('call json_decode("[\\"1\\", 1,, \\"2\\", 2]")')
    281    )
    282    eq(
    283      'Vim(call):E474: Duplicate colon: : 2}',
    284      exc_exec('call json_decode("{\\"1\\": 1, \\"2\\":: 2}")')
    285    )
    286    eq(
    287      'Vim(call):E474: Comma after colon: , 2}',
    288      exc_exec('call json_decode("{\\"1\\": 1, \\"2\\":, 2}")')
    289    )
    290    eq(
    291      'Vim(call):E474: Unexpected colon: : "2": 2}',
    292      exc_exec('call json_decode("{\\"1\\": 1,: \\"2\\": 2}")')
    293    )
    294    eq(
    295      'Vim(call):E474: Unexpected colon: :, "2": 2}',
    296      exc_exec('call json_decode("{\\"1\\": 1:, \\"2\\": 2}")')
    297    )
    298  end)
    299 
    300  it('fails to parse concat of two values', function()
    301    eq('Vim(call):E474: Trailing characters: []', exc_exec('call json_decode("{}[]")'))
    302  end)
    303 
    304  it('parses containers', function()
    305    eq({ 1 }, fn.json_decode('[1]'))
    306    eq({ NIL, 1 }, fn.json_decode('[null, 1]'))
    307    eq({ ['1'] = 2 }, fn.json_decode('{"1": 2}'))
    308    eq(
    309      { ['1'] = 2, ['3'] = { { ['4'] = { ['5'] = { {}, 1 } } } } },
    310      fn.json_decode('{"1": 2, "3": [{"4": {"5": [[], 1]}}]}')
    311    )
    312  end)
    313 
    314  it('fails to parse incomplete strings', function()
    315    eq('Vim(call):E474: Expected string end: \t"', exc_exec('call json_decode("\\t\\"")'))
    316    eq('Vim(call):E474: Expected string end: \t"abc', exc_exec('call json_decode("\\t\\"abc")'))
    317    eq(
    318      'Vim(call):E474: Unfinished escape sequence: \t"abc\\',
    319      exc_exec('call json_decode("\\t\\"abc\\\\")')
    320    )
    321    eq(
    322      'Vim(call):E474: Unfinished unicode escape sequence: \t"abc\\u',
    323      exc_exec('call json_decode("\\t\\"abc\\\\u")')
    324    )
    325    eq(
    326      'Vim(call):E474: Unfinished unicode escape sequence: \t"abc\\u0',
    327      exc_exec('call json_decode("\\t\\"abc\\\\u0")')
    328    )
    329    eq(
    330      'Vim(call):E474: Unfinished unicode escape sequence: \t"abc\\u00',
    331      exc_exec('call json_decode("\\t\\"abc\\\\u00")')
    332    )
    333    eq(
    334      'Vim(call):E474: Unfinished unicode escape sequence: \t"abc\\u000',
    335      exc_exec('call json_decode("\\t\\"abc\\\\u000")')
    336    )
    337    eq(
    338      'Vim(call):E474: Expected four hex digits after \\u: \\u"    ',
    339      exc_exec('call json_decode("\\t\\"abc\\\\u\\"    ")')
    340    )
    341    eq(
    342      'Vim(call):E474: Expected four hex digits after \\u: \\u0"    ',
    343      exc_exec('call json_decode("\\t\\"abc\\\\u0\\"    ")')
    344    )
    345    eq(
    346      'Vim(call):E474: Expected four hex digits after \\u: \\u00"    ',
    347      exc_exec('call json_decode("\\t\\"abc\\\\u00\\"    ")')
    348    )
    349    eq(
    350      'Vim(call):E474: Expected four hex digits after \\u: \\u000"    ',
    351      exc_exec('call json_decode("\\t\\"abc\\\\u000\\"    ")')
    352    )
    353    eq(
    354      'Vim(call):E474: Expected string end: \t"abc\\u0000',
    355      exc_exec('call json_decode("\\t\\"abc\\\\u0000")')
    356    )
    357  end)
    358 
    359  it('fails to parse unknown escape sequences', function()
    360    eq(
    361      'Vim(call):E474: Unknown escape sequence: \\a"',
    362      exc_exec('call json_decode("\\t\\"\\\\a\\"")')
    363    )
    364  end)
    365 
    366  it('parses strings properly', function()
    367    eq('\n', fn.json_decode('"\\n"'))
    368    eq('', fn.json_decode('""'))
    369    eq('\\/"\t\b\n\r\f', fn.json_decode([["\\\/\"\t\b\n\r\f"]]))
    370    eq('/a', fn.json_decode([["\/a"]]))
    371    -- Unicode characters: 2-byte, 3-byte, 4-byte
    372    eq(
    373      {
    374        '«',
    375        'ફ',
    376        '\240\144\128\128',
    377      },
    378      fn.json_decode({
    379        '[',
    380        '"«",',
    381        '"ફ",',
    382        '"\240\144\128\128"',
    383        ']',
    384      })
    385    )
    386  end)
    387 
    388  it('fails on strings with invalid bytes', function()
    389    eq(
    390      'Vim(call):E474: Only UTF-8 strings allowed: \255"',
    391      exc_exec('call json_decode("\\t\\"\\xFF\\"")')
    392    )
    393    eq(
    394      'Vim(call):E474: ASCII control characters cannot be present inside string: ',
    395      exc_exec('call json_decode(["\\"\\n\\""])')
    396    )
    397    -- 0xC2 starts 2-byte unicode character
    398    eq(
    399      'Vim(call):E474: Only UTF-8 strings allowed: \194"',
    400      exc_exec('call json_decode("\\t\\"\\xC2\\"")')
    401    )
    402    -- 0xE0 0xAA starts 3-byte unicode character
    403    eq(
    404      'Vim(call):E474: Only UTF-8 strings allowed: \224"',
    405      exc_exec('call json_decode("\\t\\"\\xE0\\"")')
    406    )
    407    eq(
    408      'Vim(call):E474: Only UTF-8 strings allowed: \224\170"',
    409      exc_exec('call json_decode("\\t\\"\\xE0\\xAA\\"")')
    410    )
    411    -- 0xF0 0x90 0x80 starts 4-byte unicode character
    412    eq(
    413      'Vim(call):E474: Only UTF-8 strings allowed: \240"',
    414      exc_exec('call json_decode("\\t\\"\\xF0\\"")')
    415    )
    416    eq(
    417      'Vim(call):E474: Only UTF-8 strings allowed: \240\144"',
    418      exc_exec('call json_decode("\\t\\"\\xF0\\x90\\"")')
    419    )
    420    eq(
    421      'Vim(call):E474: Only UTF-8 strings allowed: \240\144\128"',
    422      exc_exec('call json_decode("\\t\\"\\xF0\\x90\\x80\\"")')
    423    )
    424    -- 0xF9 0x80 0x80 0x80 starts 5-byte unicode character
    425    eq(
    426      'Vim(call):E474: Only UTF-8 strings allowed: \249"',
    427      exc_exec('call json_decode("\\t\\"\\xF9\\"")')
    428    )
    429    eq(
    430      'Vim(call):E474: Only UTF-8 strings allowed: \249\128"',
    431      exc_exec('call json_decode("\\t\\"\\xF9\\x80\\"")')
    432    )
    433    eq(
    434      'Vim(call):E474: Only UTF-8 strings allowed: \249\128\128"',
    435      exc_exec('call json_decode("\\t\\"\\xF9\\x80\\x80\\"")')
    436    )
    437    eq(
    438      'Vim(call):E474: Only UTF-8 strings allowed: \249\128\128\128"',
    439      exc_exec('call json_decode("\\t\\"\\xF9\\x80\\x80\\x80\\"")')
    440    )
    441    -- 0xFC 0x90 0x80 0x80 0x80 starts 6-byte unicode character
    442    eq(
    443      'Vim(call):E474: Only UTF-8 strings allowed: \252"',
    444      exc_exec('call json_decode("\\t\\"\\xFC\\"")')
    445    )
    446    eq(
    447      'Vim(call):E474: Only UTF-8 strings allowed: \252\144"',
    448      exc_exec('call json_decode("\\t\\"\\xFC\\x90\\"")')
    449    )
    450    eq(
    451      'Vim(call):E474: Only UTF-8 strings allowed: \252\144\128"',
    452      exc_exec('call json_decode("\\t\\"\\xFC\\x90\\x80\\"")')
    453    )
    454    eq(
    455      'Vim(call):E474: Only UTF-8 strings allowed: \252\144\128\128"',
    456      exc_exec('call json_decode("\\t\\"\\xFC\\x90\\x80\\x80\\"")')
    457    )
    458    eq(
    459      'Vim(call):E474: Only UTF-8 strings allowed: \252\144\128\128\128"',
    460      exc_exec('call json_decode("\\t\\"\\xFC\\x90\\x80\\x80\\x80\\"")')
    461    )
    462    -- Specification does not allow unquoted characters above 0x10FFFF
    463    eq(
    464      'Vim(call):E474: Only UTF-8 code points up to U+10FFFF are allowed to appear unescaped: \249\128\128\128\128"',
    465      exc_exec('call json_decode("\\t\\"\\xF9\\x80\\x80\\x80\\x80\\"")')
    466    )
    467    eq(
    468      'Vim(call):E474: Only UTF-8 code points up to U+10FFFF are allowed to appear unescaped: \252\144\128\128\128\128"',
    469      exc_exec('call json_decode("\\t\\"\\xFC\\x90\\x80\\x80\\x80\\x80\\"")')
    470    )
    471    -- '"\249\128\128\128\128"',
    472    -- '"\252\144\128\128\128\128"',
    473  end)
    474 
    475  it('parses surrogate pairs properly', function()
    476    eq('\240\144\128\128', fn.json_decode('"\\uD800\\uDC00"'))
    477    eq('\237\160\128a\237\176\128', fn.json_decode('"\\uD800a\\uDC00"'))
    478    eq('\237\160\128\t\237\176\128', fn.json_decode('"\\uD800\\t\\uDC00"'))
    479 
    480    eq('\237\160\128', fn.json_decode('"\\uD800"'))
    481    eq('\237\160\128a', fn.json_decode('"\\uD800a"'))
    482    eq('\237\160\128\t', fn.json_decode('"\\uD800\\t"'))
    483 
    484    eq('\237\176\128', fn.json_decode('"\\uDC00"'))
    485    eq('\237\176\128a', fn.json_decode('"\\uDC00a"'))
    486    eq('\237\176\128\t', fn.json_decode('"\\uDC00\\t"'))
    487 
    488    eq('\237\176\128', fn.json_decode('"\\uDC00"'))
    489    eq('a\237\176\128', fn.json_decode('"a\\uDC00"'))
    490    eq('\t\237\176\128', fn.json_decode('"\\t\\uDC00"'))
    491 
    492    eq('\237\160\128¬', fn.json_decode('"\\uD800\\u00AC"'))
    493 
    494    eq('\237\160\128\237\160\128', fn.json_decode('"\\uD800\\uD800"'))
    495  end)
    496 
    497  local sp_decode_eq = function(expected, json)
    498    api.nvim_set_var('__json', json)
    499    speq(expected, 'json_decode(g:__json)')
    500    command('unlet! g:__json')
    501  end
    502 
    503  it('parses strings with NUL properly', function()
    504    sp_decode_eq('\000', '"\\u0000"')
    505    sp_decode_eq('\000\n\000', '"\\u0000\\n\\u0000"')
    506    sp_decode_eq('\000«\000', '"\\u0000\\u00AB\\u0000"')
    507  end)
    508 
    509  it('parses dictionaries with duplicate keys to special maps', function()
    510    sp_decode_eq({ _TYPE = 'map', _VAL = { { 'a', 1 }, { 'a', 2 } } }, '{"a": 1, "a": 2}')
    511    sp_decode_eq(
    512      { _TYPE = 'map', _VAL = { { 'b', 3 }, { 'a', 1 }, { 'a', 2 } } },
    513      '{"b": 3, "a": 1, "a": 2}'
    514    )
    515    sp_decode_eq(
    516      { _TYPE = 'map', _VAL = { { 'b', 3 }, { 'a', 1 }, { 'c', 4 }, { 'a', 2 } } },
    517      '{"b": 3, "a": 1, "c": 4, "a": 2}'
    518    )
    519    sp_decode_eq(
    520      { _TYPE = 'map', _VAL = { { 'b', 3 }, { 'a', 1 }, { 'c', 4 }, { 'a', 2 }, { 'c', 4 } } },
    521      '{"b": 3, "a": 1, "c": 4, "a": 2, "c": 4}'
    522    )
    523    sp_decode_eq(
    524      { { _TYPE = 'map', _VAL = { { 'b', 3 }, { 'a', 1 }, { 'c', 4 }, { 'a', 2 }, { 'c', 4 } } } },
    525      '[{"b": 3, "a": 1, "c": 4, "a": 2, "c": 4}]'
    526    )
    527    sp_decode_eq({
    528      {
    529        d = {
    530          _TYPE = 'map',
    531          _VAL = { { 'b', 3 }, { 'a', 1 }, { 'c', 4 }, { 'a', 2 }, { 'c', 4 } },
    532        },
    533      },
    534    }, '[{"d": {"b": 3, "a": 1, "c": 4, "a": 2, "c": 4}}]')
    535    sp_decode_eq({
    536      1,
    537      {
    538        d = {
    539          _TYPE = 'map',
    540          _VAL = { { 'b', 3 }, { 'a', 1 }, { 'c', 4 }, { 'a', 2 }, { 'c', 4 } },
    541        },
    542      },
    543    }, '[1, {"d": {"b": 3, "a": 1, "c": 4, "a": 2, "c": 4}}]')
    544    sp_decode_eq({
    545      1,
    546      {
    547        a = {},
    548        d = {
    549          _TYPE = 'map',
    550          _VAL = {
    551            { 'b', 3 },
    552            { 'a', 1 },
    553            { 'c', 4 },
    554            { 'a', 2 },
    555            {
    556              'c',
    557              4,
    558            },
    559          },
    560        },
    561      },
    562    }, '[1, {"a": [], "d": {"b": 3, "a": 1, "c": 4, "a": 2, "c": 4}}]')
    563    sp_decode_eq(
    564      { _TYPE = 'map', _VAL = { { '', 3 }, { 'a', 1 }, { 'c', 4 }, { 'd', 2 }, { '', 4 } } },
    565      '{"": 3, "a": 1, "c": 4, "d": 2, "": 4}'
    566    )
    567    sp_decode_eq(
    568      { { _TYPE = 'map', _VAL = { { '', 3 }, { 'a', 1 }, { 'c', 4 }, { 'd', 2 }, { '', 4 } } } },
    569      '[{"": 3, "a": 1, "c": 4, "d": 2, "": 4}]'
    570    )
    571  end)
    572 
    573  it('parses dictionaries with empty keys', function()
    574    eq({ [''] = 4 }, fn.json_decode('{"": 4}'))
    575    eq(
    576      { b = 3, a = 1, c = 4, d = 2, [''] = 4 },
    577      fn.json_decode('{"b": 3, "a": 1, "c": 4, "d": 2, "": 4}')
    578    )
    579  end)
    580 
    581  it('parses dictionaries with keys with NUL bytes to special maps', function()
    582    sp_decode_eq({ _TYPE = 'map', _VAL = { { 'a\000\nb', 4 } } }, '{"a\\u0000\\nb": 4}')
    583    sp_decode_eq({ _TYPE = 'map', _VAL = { { 'a\000\nb\n', 4 } } }, '{"a\\u0000\\nb\\n": 4}')
    584    sp_decode_eq({
    585      _TYPE = 'map',
    586      _VAL = {
    587        { 'b', 3 },
    588        { 'a', 1 },
    589        { 'c', 4 },
    590        { 'd', 2 },
    591        { '\000', 4 },
    592      },
    593    }, '{"b": 3, "a": 1, "c": 4, "d": 2, "\\u0000": 4}')
    594  end)
    595 
    596  it('parses U+00C3 correctly', function()
    597    eq('\195\131', fn.json_decode('"\195\131"'))
    598  end)
    599 
    600  it('fails to parse empty string', function()
    601    eq('Vim(call):E474: Attempt to decode a blank string', exc_exec('call json_decode("")'))
    602    eq('Vim(call):E474: Attempt to decode a blank string', exc_exec('call json_decode([])'))
    603    eq('Vim(call):E474: Attempt to decode a blank string', exc_exec('call json_decode([""])'))
    604    eq('Vim(call):E474: Attempt to decode a blank string', exc_exec('call json_decode(" ")'))
    605    eq('Vim(call):E474: Attempt to decode a blank string', exc_exec('call json_decode("\\t")'))
    606    eq('Vim(call):E474: Attempt to decode a blank string', exc_exec('call json_decode("\\n")'))
    607    eq(
    608      'Vim(call):E474: Attempt to decode a blank string',
    609      exc_exec('call json_decode(" \\t\\n \\n\\t\\t \\n\\t\\n \\n \\t\\n\\t ")')
    610    )
    611  end)
    612 
    613  it('accepts all spaces in every position where space may be put', function()
    614    local s =
    615      ' \t\n\r \t\r\n \n\t\r \n\r\t \r\t\n \r\n\t\t \n\r\t \r\n\t\n \r\t\n\r \t\r \n\t\r\n \n \t\r\n \r\t\n\t \r\n\t\r \n\r \t\n\r\t \r \t\n\r \n\t\r\t \n\r\t\n \r\n \t\r\n\t'
    616    local str = ('%s{%s"key"%s:%s[%s"val"%s,%s"val2"%s]%s,%s"key2"%s:%s1%s}%s'):gsub('%%s', s)
    617    eq({ key = { 'val', 'val2' }, key2 = 1 }, fn.json_decode(str))
    618  end)
    619 
    620  it('does not overflow when writing error message about decoding ["", ""]', function()
    621    eq(
    622      'Vim(call):E474: Attempt to decode a blank string',
    623      pcall_err(command, 'call json_decode(["", ""])')
    624    )
    625  end)
    626 end)
    627 
    628 describe('json_encode() function', function()
    629  setup(function()
    630    clear()
    631    command('language C')
    632  end)
    633 
    634  it('dumps strings', function()
    635    eq('"Test"', fn.json_encode('Test'))
    636    eq('""', fn.json_encode(''))
    637    eq('"\\t"', fn.json_encode('\t'))
    638    eq('"\\n"', fn.json_encode('\n'))
    639    eq('"\\u001B"', fn.json_encode('\27'))
    640    eq('"þÿþ"', fn.json_encode('þÿþ'))
    641  end)
    642 
    643  it('dumps blobs', function()
    644    eq('[]', eval('json_encode(0z)'))
    645    eq('[222, 173, 190, 239]', eval('json_encode(0zDEADBEEF)'))
    646  end)
    647 
    648  it('dumps numbers', function()
    649    eq('0', fn.json_encode(0))
    650    eq('10', fn.json_encode(10))
    651    eq('-10', fn.json_encode(-10))
    652  end)
    653 
    654  it('dumps floats', function()
    655    -- Also test method call (->) syntax
    656    eq('0.0', eval('0.0->json_encode()'))
    657    eq('10.5', fn.json_encode(10.5))
    658    eq('-10.5', fn.json_encode(-10.5))
    659    eq('-1.0e-5', fn.json_encode(-1e-5))
    660    eq('1.0e50', eval('1.0e50->json_encode()'))
    661  end)
    662 
    663  it('fails to dump NaN and infinite values', function()
    664    eq(
    665      'Vim(call):E474: Unable to represent NaN value in JSON',
    666      exc_exec('call json_encode(str2float("nan"))')
    667    )
    668    eq(
    669      'Vim(call):E474: Unable to represent infinity in JSON',
    670      exc_exec('call json_encode(str2float("inf"))')
    671    )
    672    eq(
    673      'Vim(call):E474: Unable to represent infinity in JSON',
    674      exc_exec('call json_encode(-str2float("inf"))')
    675    )
    676  end)
    677 
    678  it('dumps lists', function()
    679    eq('[]', fn.json_encode({}))
    680    eq('[[]]', fn.json_encode({ {} }))
    681    eq('[[], []]', fn.json_encode({ {}, {} }))
    682  end)
    683 
    684  it('dumps dictionaries', function()
    685    eq('{}', eval('json_encode({})'))
    686    eq('{"d": []}', fn.json_encode({ d = {} }))
    687    eq('{"d": [], "e": []}', fn.json_encode({ d = {}, e = {} }))
    688    -- Empty keys are allowed per JSON spec (and Vim dicts, and msgpack).
    689    eq('{"": []}', fn.json_encode({ [''] = {} }))
    690  end)
    691 
    692  it('cannot dump generic mapping with generic mapping keys and values', function()
    693    command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}')
    694    command('let todumpv1 = {"_TYPE": v:msgpack_types.map, "_VAL": []}')
    695    command('let todumpv2 = {"_TYPE": v:msgpack_types.map, "_VAL": []}')
    696    command('call add(todump._VAL, [todumpv1, todumpv2])')
    697    eq('Vim(call):E474: Invalid key in special dictionary', exc_exec('call json_encode(todump)'))
    698  end)
    699 
    700  it('cannot dump generic mapping with ext key', function()
    701    command('let todump = {"_TYPE": v:msgpack_types.ext, "_VAL": [5, ["",""]]}')
    702    command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}')
    703    eq('Vim(call):E474: Invalid key in special dictionary', exc_exec('call json_encode(todump)'))
    704  end)
    705 
    706  it('cannot dump generic mapping with array key', function()
    707    command('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": [5, [""]]}')
    708    command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}')
    709    eq('Vim(call):E474: Invalid key in special dictionary', exc_exec('call json_encode(todump)'))
    710  end)
    711 
    712  it('cannot dump generic mapping with UINT64_MAX key', function()
    713    command('let todump = {"_TYPE": v:msgpack_types.integer}')
    714    command('let todump._VAL = [1, 3, 0x7FFFFFFF, 0x7FFFFFFF]')
    715    command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}')
    716    eq('Vim(call):E474: Invalid key in special dictionary', exc_exec('call json_encode(todump)'))
    717  end)
    718 
    719  it('cannot dump generic mapping with floating-point key', function()
    720    command('let todump = {"_TYPE": v:msgpack_types.float, "_VAL": 0.125}')
    721    command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}')
    722    eq('Vim(call):E474: Invalid key in special dictionary', exc_exec('call json_encode(todump)'))
    723  end)
    724 
    725  it('can dump generic mapping with STR special key and NUL', function()
    726    command('let todump = {"_TYPE": v:msgpack_types.string, "_VAL": ["\\n"]}')
    727    command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}')
    728    eq('{"\\u0000": 1}', eval('json_encode(todump)'))
    729  end)
    730 
    731  it('can dump STR special mapping with NUL and NL', function()
    732    command('let todump = {"_TYPE": v:msgpack_types.string, "_VAL": ["\\n", ""]}')
    733    eq('"\\u0000\\n"', eval('json_encode(todump)'))
    734  end)
    735 
    736  it('cannot dump special ext mapping', function()
    737    command('let todump = {"_TYPE": v:msgpack_types.ext, "_VAL": [5, ["",""]]}')
    738    eq('Vim(call):E474: Unable to convert EXT string to JSON', exc_exec('call json_encode(todump)'))
    739  end)
    740 
    741  it('can dump special array mapping', function()
    742    command('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": [5, [""]]}')
    743    eq('[5, [""]]', eval('json_encode(todump)'))
    744  end)
    745 
    746  it('can dump special UINT64_MAX mapping', function()
    747    command('let todump = {"_TYPE": v:msgpack_types.integer}')
    748    command('let todump._VAL = [1, 3, 0x7FFFFFFF, 0x7FFFFFFF]')
    749    eq('18446744073709551615', eval('json_encode(todump)'))
    750  end)
    751 
    752  it('can dump special INT64_MIN mapping', function()
    753    command('let todump = {"_TYPE": v:msgpack_types.integer}')
    754    command('let todump._VAL = [-1, 2, 0, 0]')
    755    eq('-9223372036854775808', eval('json_encode(todump)'))
    756  end)
    757 
    758  it('can dump special BOOLEAN true mapping', function()
    759    command('let todump = {"_TYPE": v:msgpack_types.boolean, "_VAL": 1}')
    760    eq('true', eval('json_encode(todump)'))
    761  end)
    762 
    763  it('can dump special BOOLEAN false mapping', function()
    764    command('let todump = {"_TYPE": v:msgpack_types.boolean, "_VAL": 0}')
    765    eq('false', eval('json_encode(todump)'))
    766  end)
    767 
    768  it('can dump special NIL mapping', function()
    769    command('let todump = {"_TYPE": v:msgpack_types.nil, "_VAL": 0}')
    770    eq('null', eval('json_encode(todump)'))
    771  end)
    772 
    773  it('fails to dump a function reference', function()
    774    eq(
    775      'Vim(call):E474: Error while dumping encode_tv2json() argument, itself: attempt to dump function reference',
    776      exc_exec('call json_encode(function("tr"))')
    777    )
    778  end)
    779 
    780  it('fails to dump a partial', function()
    781    command('function T() dict\nendfunction')
    782    eq(
    783      'Vim(call):E474: Error while dumping encode_tv2json() argument, itself: attempt to dump function reference',
    784      exc_exec('call json_encode(function("T", [1, 2], {}))')
    785    )
    786  end)
    787 
    788  it('fails to dump a function reference in a list', function()
    789    eq(
    790      'Vim(call):E474: Error while dumping encode_tv2json() argument, index 0: attempt to dump function reference',
    791      exc_exec('call json_encode([function("tr")])')
    792    )
    793  end)
    794 
    795  it('fails to dump a recursive list', function()
    796    command('let todump = [[[]]]')
    797    command('call add(todump[0][0], todump)')
    798    eq(
    799      'Vim(call):E724: unable to correctly dump variable with self-referencing container',
    800      exc_exec('call json_encode(todump)')
    801    )
    802  end)
    803 
    804  it('fails to dump a recursive dict', function()
    805    command('let todump = {"d": {"d": {}}}')
    806    command('call extend(todump.d.d, {"d": todump})')
    807    eq(
    808      'Vim(call):E724: unable to correctly dump variable with self-referencing container',
    809      exc_exec('call json_encode([todump])')
    810    )
    811  end)
    812 
    813  it('can dump dict with two same dicts inside', function()
    814    command('let inter = {}')
    815    command('let todump = {"a": inter, "b": inter}')
    816    eq('{"a": {}, "b": {}}', eval('json_encode(todump)'))
    817  end)
    818 
    819  it('can dump list with two same lists inside', function()
    820    command('let inter = []')
    821    command('let todump = [inter, inter]')
    822    eq('[[], []]', eval('json_encode(todump)'))
    823  end)
    824 
    825  it('fails to dump a recursive list in a special dict', function()
    826    command('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}')
    827    command('call add(todump._VAL, todump)')
    828    eq(
    829      'Vim(call):E724: unable to correctly dump variable with self-referencing container',
    830      exc_exec('call json_encode(todump)')
    831    )
    832  end)
    833 
    834  it('fails to dump a recursive (val) map in a special dict', function()
    835    command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}')
    836    command('call add(todump._VAL, ["", todump])')
    837    eq(
    838      'Vim(call):E724: unable to correctly dump variable with self-referencing container',
    839      exc_exec('call json_encode([todump])')
    840    )
    841  end)
    842 
    843  it('fails to dump a recursive (val) map in a special dict, _VAL reference', function()
    844    command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [["", []]]}')
    845    command('call add(todump._VAL[0][1], todump._VAL)')
    846    eq(
    847      'Vim(call):E724: unable to correctly dump variable with self-referencing container',
    848      exc_exec('call json_encode(todump)')
    849    )
    850  end)
    851 
    852  it('fails to dump a recursive (val) special list in a special dict', function()
    853    command('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}')
    854    command('call add(todump._VAL, ["", todump._VAL])')
    855    eq(
    856      'Vim(call):E724: unable to correctly dump variable with self-referencing container',
    857      exc_exec('call json_encode(todump)')
    858    )
    859  end)
    860 
    861  it('fails when called with no arguments', function()
    862    eq(
    863      'Vim(call):E119: Not enough arguments for function: json_encode',
    864      exc_exec('call json_encode()')
    865    )
    866  end)
    867 
    868  it('fails when called with two arguments', function()
    869    eq(
    870      'Vim(call):E118: Too many arguments for function: json_encode',
    871      exc_exec('call json_encode(["", ""], 1)')
    872    )
    873  end)
    874 
    875  it('ignores improper values in &isprint', function()
    876    api.nvim_set_option_value('isprint', '1', {})
    877    eq(1, eval('"\1" =~# "\\\\p"'))
    878    eq('"\\u0001"', fn.json_encode('\1'))
    879  end)
    880 
    881  it('fails when using surrogate character in a UTF-8 string', function()
    882    eq(
    883      'Vim(call):E474: UTF-8 string contains code point which belongs to a surrogate pair: \237\160\128',
    884      exc_exec('call json_encode("\237\160\128")')
    885    )
    886    eq(
    887      'Vim(call):E474: UTF-8 string contains code point which belongs to a surrogate pair: \237\175\191',
    888      exc_exec('call json_encode("\237\175\191")')
    889    )
    890  end)
    891 
    892  it('dumps control characters as expected', function()
    893    eq(
    894      [["\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000B\f\r\u000E\u000F\u0010\u0011\u0012\u0013"]],
    895      eval(
    896        'json_encode({"_TYPE": v:msgpack_types.string, "_VAL": ["\n\1\2\3\4\5\6\7\8\9", "\11\12\13\14\15\16\17\18\19"]})'
    897      )
    898    )
    899  end)
    900 
    901  it('can dump NULL string', function()
    902    eq('""', eval('json_encode($XXX_UNEXISTENT_VAR_XXX)'))
    903  end)
    904 
    905  it('can dump NULL blob', function()
    906    eq('[]', eval('json_encode(v:_null_blob)'))
    907  end)
    908 
    909  it('can dump NULL list', function()
    910    eq('[]', eval('json_encode(v:_null_list)'))
    911  end)
    912 
    913  it('can dump NULL dict', function()
    914    eq('{}', eval('json_encode(v:_null_dict)'))
    915  end)
    916 
    917  it('fails to parse NULL strings and lists', function()
    918    eq(
    919      'Vim(call):E474: Attempt to decode a blank string',
    920      exc_exec('call json_decode($XXX_UNEXISTENT_VAR_XXX)')
    921    )
    922    eq(
    923      'Vim(call):E474: Attempt to decode a blank string',
    924      exc_exec('call json_decode(v:_null_list)')
    925    )
    926  end)
    927 end)