view src/testdir/test_vim9_script.vim @ 19316:17dc6282f370 v8.2.0216

patch 8.2.0216: several Vim9 instructions are not tested Commit: https://github.com/vim/vim/commit/ff80cb6807d99db35cc928f151b87503b2928e19 Author: Bram Moolenaar <Bram@vim.org> Date: Wed Feb 5 22:10:05 2020 +0100 patch 8.2.0216: several Vim9 instructions are not tested Problem: Several Vim9 instructions are not tested. Solution: Add more tests. Fix :disassamble output. Make catch with pattern work.
author Bram Moolenaar <Bram@vim.org>
date Wed, 05 Feb 2020 22:15:03 +0100
parents 84703c85a583
children 6e199d1d11d0
line wrap: on
line source

" Test various aspects of the Vim9 script language.

source check.vim

" Check that "lines" inside ":def" results in an "error" message.
func CheckDefFailure(lines, error)
  call writefile(['def Func()'] + a:lines + ['enddef'], 'Xdef')
  call assert_fails('so Xdef', a:error, a:lines)
  call delete('Xdef')
endfunc

func CheckScriptFailure(lines, error)
  call writefile(a:lines, 'Xdef')
  call assert_fails('so Xdef', a:error, a:lines)
  call delete('Xdef')
endfunc

def Test_syntax()
  let var = 234
  let other: list<string> = ['asdf']
enddef

func Test_def_basic()
  def SomeFunc(): string
    return 'yes'
  enddef
  call assert_equal('yes', SomeFunc())
endfunc

def Test_assignment()
  let bool1: bool = true
  assert_equal(v:true, bool1)
  let bool2: bool = false
  assert_equal(v:false, bool2)

  let list1: list<string> = ['sdf', 'asdf']
  let list2: list<number> = [1, 2, 3]

  " TODO: does not work yet
  " let listS: list<string> = []
  " let listN: list<number> = []

  let dict1: dict<string> = #{key: 'value'}
  let dict2: dict<number> = #{one: 1, two: 2}

  v:char = 'abc'
  call assert_equal('abc', v:char)

  $ENVVAR = 'foobar'
  call assert_equal('foobar', $ENVVAR)
  $ENVVAR = ''
enddef

func Test_assignment_failure()
  call CheckDefFailure(['let var=234'], 'E1004:')
  call CheckDefFailure(['let var =234'], 'E1004:')
  call CheckDefFailure(['let var= 234'], 'E1004:')

  call CheckDefFailure(['let true = 1'], 'E1034:')
  call CheckDefFailure(['let false = 1'], 'E1034:')

  call CheckDefFailure(['let var: list<string> = [123]'], 'expected list<string> but got list<number>')
  call CheckDefFailure(['let var: list<number> = ["xx"]'], 'expected list<number> but got list<string>')

  call CheckDefFailure(['let var: dict<string> = #{key: 123}'], 'expected dict<string> but got dict<number>')
  call CheckDefFailure(['let var: dict<number> = #{key: "xx"}'], 'expected dict<number> but got dict<string>')

  call CheckDefFailure(['let var = feedkeys("0")'], 'E1031:')
  call CheckDefFailure(['let var: number = feedkeys("0")'], 'expected number but got void')
endfunc

func Test_const()
  call CheckDefFailure(['const var = 234', 'var = 99'], 'E1018:')
  call CheckDefFailure(['const one = 234', 'let one = 99'], 'E1017:')
  call CheckDefFailure(['const two'], 'E1021:')
endfunc

def Test_block()
  let outer = 1
  {
    let inner = 2
    assert_equal(1, outer)
    assert_equal(2, inner)
  }
  assert_equal(1, outer)
enddef

func Test_block_failure()
  call CheckDefFailure(['{', 'let inner = 1', '}', 'echo inner'], 'E1001:')
endfunc

def ReturnString(): string
  return 'string'
enddef

def ReturnNumber(): number
  return 123
enddef

def Test_return_string()
  assert_equal('string', ReturnString())
  assert_equal(123, ReturnNumber())
enddef

func Increment()
  let g:counter += 1
endfunc

def Test_call_ufunc_count()
  g:counter = 1
  Increment()
  Increment()
  Increment()
  " works with and without :call
  assert_equal(4, g:counter)
  call assert_equal(4, g:counter)
  unlet g:counter
enddef

def MyVarargs(arg: string, ...rest: list<string>): string
  let res = arg
  for s in rest
    res ..= ',' .. s
  endfor
  return res
enddef

def Test_call_varargs()
  assert_equal('one', MyVarargs('one'))
  assert_equal('one,two', MyVarargs('one', 'two'))
  assert_equal('one,two,three', MyVarargs('one', 'two', 'three'))
enddef

"def Test_call_func_defined_later()
"  call assert_equal('one', DefineLater('one'))
"  call assert_fails('call NotDefined("one")', 'E99:')
"enddef

func DefineLater(arg)
  return a:arg
endfunc

def MyDefaultArgs(name = 'string'): string
  return name
enddef

func Test_call_default_args_from_func()
  " TODO: implement using default value for optional argument
  "call assert_equal('string', MyDefaultArgs())
  call assert_fails('call MyDefaultArgs()', 'optional arguments not implemented yet')
  call assert_equal('one', MyDefaultArgs('one'))
  call assert_fails('call MyDefaultArgs("one", "two")', 'E118:')
endfunc

def Test_call_default_args()
  " TODO: implement using default value for optional argument
  "assert_equal('string', MyDefaultArgs())
  assert_equal('one', MyDefaultArgs('one'))
  assert_fails('call MyDefaultArgs("one", "two")', 'E118:')
enddef

def Test_return_type_wrong()
  CheckScriptFailure(['def Func(): number', 'return "a"', 'enddef'], 'expected number but got string')
  CheckScriptFailure(['def Func(): string', 'return 1', 'enddef'], 'expected string but got number')
  CheckScriptFailure(['def Func(): void', 'return "a"', 'enddef'], 'expected void but got string')
  CheckScriptFailure(['def Func()', 'return "a"', 'enddef'], 'expected void but got string')
enddef

def Test_arg_type_wrong()
  CheckScriptFailure(['def Func3(items: list)', 'echo "a"', 'enddef'], 'E1008: Missing <type>')
enddef

def Test_try_catch()
  let l = []
  try
    add(l, '1')
    throw 'wrong'
    add(l, '2')
  catch
    add(l, v:exception)
  finally
    add(l, '3')
  endtry
  assert_equal(['1', 'wrong', '3'], l)
enddef

let s:export_script_lines =<< trim END
  vim9script
  let name: string = 'bob'
  def Concat(arg: string): string
    return name .. arg
  enddef
  let g:result = Concat('bie')
  let g:localname = name

  export const CONST = 1234
  export let exported = 9876
  export def Exported(): string
    return 'Exported'
  enddef
END

def Test_vim9script()
  let import_script_lines =<< trim END
    vim9script
    import {exported, Exported} from './Xexport.vim'
    g:imported = exported
    g:imported_func = Exported()
  END

  writefile(import_script_lines, 'Ximport.vim')
  writefile(s:export_script_lines, 'Xexport.vim')

  source Ximport.vim

  assert_equal('bobbie', g:result)
  assert_equal('bob', g:localname)
  assert_equal(9876, g:imported)
  assert_equal('Exported', g:imported_func)
  assert_false(exists('g:name'))

  unlet g:result
  unlet g:localname
  unlet g:imported
  unlet g:imported_func
  delete('Ximport.vim')
  delete('Xexport.vim')

  CheckScriptFailure(['scriptversion 2', 'vim9script'], 'E1039:')
  CheckScriptFailure(['vim9script', 'scriptversion 2'], 'E1040:')
enddef

def Test_vim9script_call()
  let lines =<< trim END
    vim9script
    let var = ''
    def MyFunc(arg: string)
       var = arg
    enddef
    MyFunc('foobar')
    assert_equal('foobar', var)

    let str = 'barfoo'
    str->MyFunc()
    assert_equal('barfoo', var)

    let g:value = 'value'
    g:value->MyFunc()
    assert_equal('value', var)

    let listvar = []
    def ListFunc(arg: list<number>)
       listvar = arg
    enddef
    [1, 2, 3]->ListFunc()
    assert_equal([1, 2, 3], listvar)

    let dictvar = {}
    def DictFunc(arg: dict<number>)
       dictvar = arg
    enddef
    {'a': 1, 'b': 2}->DictFunc()
    assert_equal(#{a: 1, b: 2}, dictvar)
    #{a: 3, b: 4}->DictFunc()
    assert_equal(#{a: 3, b: 4}, dictvar)
  END
  writefile(lines, 'Xcall.vim')
  source Xcall.vim
  delete('Xcall.vim')
enddef

def Test_vim9script_call_fail_decl()
  let lines =<< trim END
    vim9script
    let var = ''
    def MyFunc(arg: string)
       let var = 123
    enddef
  END
  writefile(lines, 'Xcall_decl.vim')
  assert_fails('source Xcall_decl.vim', 'E1054:')
  delete('Xcall_decl.vim')
enddef

def Test_vim9script_call_fail_const()
  let lines =<< trim END
    vim9script
    const var = ''
    def MyFunc(arg: string)
       var = 'asdf'
    enddef
  END
  writefile(lines, 'Xcall_const.vim')
  assert_fails('source Xcall_const.vim', 'E46:')
  delete('Xcall_const.vim')
enddef

def Test_vim9script_reload()
  let lines =<< trim END
    vim9script
    const var = ''
    let valone = 1234
    def MyFunc(arg: string)
       valone = 5678
    enddef
  END
  let morelines =<< trim END
    let valtwo = 222
    export def GetValtwo(): number
      return valtwo
    enddef
  END
  writefile(lines + morelines, 'Xreload.vim')
  source Xreload.vim
  source Xreload.vim
  source Xreload.vim

  let testlines =<< trim END
    vim9script
    def TheFunc()
      import GetValtwo from './Xreload.vim'
      assert_equal(222, GetValtwo())
    enddef
    TheFunc()
  END
  writefile(testlines, 'Ximport.vim')
  source Ximport.vim

  " test that when not using "morelines" valtwo is still defined
  " need to source Xreload.vim again, import doesn't reload a script
  writefile(lines, 'Xreload.vim')
  source Xreload.vim
  source Ximport.vim

  " cannot declare a var twice
  lines =<< trim END
    vim9script
    let valone = 1234
    let valone = 5678
  END
  writefile(lines, 'Xreload.vim')
  assert_fails('source Xreload.vim', 'E1041:')

  delete('Xreload.vim')
  delete('Ximport.vim')
enddef

def Test_import_absolute()
  let import_lines = [
        \ 'vim9script',
        \ 'import exported from "' .. escape(getcwd(), '\') .. '/Xexport_abs.vim"',
        \ 'def UseExported()',
        \ '  g:imported_abs = exported',
        \ '  exported = 8888',
        \ '  g:imported_after = exported',
        \ 'enddef',
        \ 'UseExported()',
        \ 'g:import_disassembled = execute("disass UseExported")',
        \ ]
  writefile(import_lines, 'Ximport_abs.vim')
  writefile(s:export_script_lines, 'Xexport_abs.vim')

  source Ximport_abs.vim

  assert_equal(9876, g:imported_abs)
  assert_equal(8888, g:imported_after)
  assert_match('<SNR>\d\+_UseExported.*'
        \ .. 'g:imported_abs = exported.*'
        \ .. '0 LOADSCRIPT exported from .*Xexport_abs.vim.*'
        \ .. '1 STOREG g:imported_abs.*'
        \ .. 'exported = 8888.*'
        \ .. '3 STORESCRIPT exported in .*Xexport_abs.vim.*'
        \ .. 'g:imported_after = exported.*'
        \ .. '4 LOADSCRIPT exported from .*Xexport_abs.vim.*'
        \ .. '5 STOREG g:imported_after.*'
        \, g:import_disassembled)
  unlet g:imported_abs
  unlet g:import_disassembled

  delete('Ximport_abs.vim')
  delete('Xexport_abs.vim')
enddef

def Test_import_rtp()
  let import_lines = [
        \ 'vim9script',
        \ 'import exported from "Xexport_rtp.vim"',
        \ 'g:imported_rtp = exported',
        \ ]
  writefile(import_lines, 'Ximport_rtp.vim')
  mkdir('import')
  writefile(s:export_script_lines, 'import/Xexport_rtp.vim')

  let save_rtp = &rtp
  &rtp = getcwd()
  source Ximport_rtp.vim
  &rtp = save_rtp

  assert_equal(9876, g:imported_rtp)
  unlet g:imported_rtp

  delete('Ximport_rtp.vim')
  delete('import/Xexport_rtp.vim')
  delete('import', 'd')
enddef

def Test_fixed_size_list()
  " will be allocated as one piece of memory, check that changes work
  let l = [1, 2, 3, 4]
  l->remove(0)
  l->add(5)
  l->insert(99, 1)
  assert_equal([2, 99, 3, 4, 5], l)
enddef

" Test that inside :function a Python function can be defined, :def is not
" recognized.
func Test_function_python()
  CheckFeature python3
  let py = 'python3'
  execute py "<< EOF"
def do_something():
  return 1
EOF
endfunc

def HasEval()
  if has('eval')
    echo 'yes'
  else
    echo 'no'
  endif
enddef

def HasNothing()
  if has('nothing')
    echo 'yes'
  else
    echo 'no'
  endif
enddef

def Test_compile_const_expr()
  assert_equal("\nyes", execute('call HasEval()'))
  let instr = execute('disassemble HasEval')
  assert_match('PUSHS "yes"', instr)
  assert_notmatch('PUSHS "no"', instr)
  assert_notmatch('JUMP', instr)

  assert_equal("\nno", execute('call HasNothing()'))
  instr = execute('disassemble HasNothing')
  assert_notmatch('PUSHS "yes"', instr)
  assert_match('PUSHS "no"', instr)
  assert_notmatch('JUMP', instr)
enddef

func NotCompiled()
  echo "not"
endfunc

let s:scriptvar = 4
let g:globalvar = 'g'

def s:ScriptFuncLoad(arg: string)
  let local = 1
  buffers
  echo arg
  echo local
  echo v:version
  echo s:scriptvar
  echo g:globalvar
  echo &tabstop
  echo $ENVVAR
  echo @z
enddef

def s:ScriptFuncPush()
  let localbool = true
  let localspec = v:none
  let localblob = 0z1234
  if has('float')
    let localfloat = 1.234
  endif
enddef

def s:ScriptFuncStore()
  let localnr = 1
  localnr = 2
  let localstr = 'abc'
  localstr = 'xyz'
  v:char = 'abc'
  s:scriptvar = 'sv'
  g:globalvar = 'gv'
  &tabstop = 8
  $ENVVAR = 'ev'
  @z = 'rv'
enddef

def s:ScriptFuncTry()
  try
    echo 'yes'
  catch /fail/
    echo 'no'
  finally
    echo 'end'
  endtry
enddef

def Test_disassemble()
  assert_fails('disass NoFunc', 'E1061:')
  assert_fails('disass NotCompiled', 'E1062:')

  let res = execute('disass s:ScriptFuncLoad')
  assert_match('<SNR>\d*_ScriptFuncLoad.*'
        \ .. 'buffers.*'
        \ .. ' EXEC \+buffers.*'
        \ .. ' LOAD arg\[-1\].*'
        \ .. ' LOAD $0.*'
        \ .. ' LOADV v:version.*'
        \ .. ' LOADS s:scriptvar from .*test_vim9_script.vim.*'
        \ .. ' LOADG g:globalvar.*'
        \ .. ' LOADENV $ENVVAR.*'
        \ .. ' LOADREG @z.*'
        \, res)

  res = execute('disass s:ScriptFuncPush')
  assert_match('<SNR>\d*_ScriptFuncPush.*'
        \ .. 'localbool = true.*'
        \ .. ' PUSH v:true.*'
        \ .. 'localspec = v:none.*'
        \ .. ' PUSH v:none.*'
        \ .. 'localblob = 0z1234.*'
        \ .. ' PUSHBLOB 0z1234.*'
        \, res)
  if has('float')
  assert_match('<SNR>\d*_ScriptFuncPush.*'
        \ .. 'localfloat = 1.234.*'
        \ .. ' PUSHF 1.234.*'
        \, res)
  endif

  res = execute('disass s:ScriptFuncStore')
  assert_match('<SNR>\d*_ScriptFuncStore.*'
        \ .. 'localnr = 2.*'
        \ .. ' STORE 2 in $0.*'
        \ .. 'localstr = ''xyz''.*'
        \ .. ' STORE $1.*'
        \ .. 'v:char = ''abc''.*'
        \ .. 'STOREV v:char.*'
        \ .. 's:scriptvar = ''sv''.*'
        \ .. ' STORES s:scriptvar in .*test_vim9_script.vim.*'
        \ .. 'g:globalvar = ''gv''.*'
        \ .. ' STOREG g:globalvar.*'
        \ .. '&tabstop = 8.*'
        \ .. ' STOREOPT &tabstop.*'
        \ .. '$ENVVAR = ''ev''.*'
        \ .. ' STOREENV $ENVVAR.*'
        \ .. '@z = ''rv''.*'
        \ .. ' STOREREG @z.*'
        \, res)

  res = execute('disass s:ScriptFuncTry')
  assert_match('<SNR>\d*_ScriptFuncTry.*'
        \ .. 'try.*'
        \ .. 'TRY catch -> \d\+, finally -> \d\+.*'
        \ .. 'catch /fail/.*'
        \ .. ' JUMP -> \d\+.*'
        \ .. ' PUSH v:exception.*'
        \ .. ' PUSHS "fail".*'
        \ .. ' COMPARESTRING =\~.*'
        \ .. ' JUMP_IF_FALSE -> \d\+.*'
        \ .. ' CATCH.*'
        \ .. 'finally.*'
        \ .. ' PUSHS "end".*'
        \ .. 'endtry.*'
        \ .. ' ENDTRY.*'
        \, res)
enddef


" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker