view src/testdir/test_functions.vim @ 33591:288da62613ba v9.0.2040

patch 9.0.2040: trim(): hard to use default mask Commit: https://github.com/vim/vim/commit/6e6386716f9494ae86027c6d34f657fd03dfec42 Author: Illia Bobyr <illia.bobyr@gmail.com> Date: Tue Oct 17 11:09:45 2023 +0200 patch 9.0.2040: trim(): hard to use default mask Problem: trim(): hard to use default mask Solution: Use default 'mask' when it is v:none The default 'mask' value is pretty complex, as it includes many characters. Yet, if one needs to specify the trimming direction, the third argument, 'trim()' currently requires the 'mask' value to be provided explicitly. 'v:none' is already used to mean "use the default argument value" in user defined functions. See |none-function_argument| in help. closes: #13363 Signed-off-by: Christian Brabandt <cb@256bit.org> Co-authored-by: Illia Bobyr <illia.bobyr@gmail.com>
author Christian Brabandt <cb@256bit.org>
date Tue, 17 Oct 2023 11:15:09 +0200
parents 8fc442c731ca
children e9c70470fe94
line wrap: on
line source

" Tests for various functions.

source shared.vim
source check.vim
source term_util.vim
source screendump.vim
import './vim9.vim' as v9

" Must be done first, since the alternate buffer must be unset.
func Test_00_bufexists()
  call assert_equal(0, bufexists('does_not_exist'))
  call assert_equal(1, bufexists(bufnr('%')))
  call assert_equal(0, bufexists(0))
  new Xfoo
  let bn = bufnr('%')
  call assert_equal(1, bufexists(bn))
  call assert_equal(1, bufexists('Xfoo'))
  call assert_equal(1, bufexists(getcwd() . '/Xfoo'))
  call assert_equal(1, bufexists(0))
  bw
  call assert_equal(0, bufexists(bn))
  call assert_equal(0, bufexists('Xfoo'))
endfunc

func Test_has()
  call assert_equal(1, has('eval'))
  call assert_equal(1, has('eval', 1))

  if has('unix')
    call assert_equal(1, or(has('ttyin'), 1))
    call assert_equal(0, and(has('ttyout'), 0))
    call assert_equal(1, has('multi_byte_encoding'))
    call assert_equal(0, has(':tearoff'))
  endif
  call assert_equal(1, has('vcon', 1))
  call assert_equal(1, has('mouse_gpm_enabled', 1))

  call assert_equal(has('gui_win32') && has('menu'), has(':tearoff'))

  call assert_equal(0, has('nonexistent'))
  call assert_equal(0, has('nonexistent', 1))

  " Will we ever have patch 9999?
  let ver = 'patch-' .. v:version / 100 .. '.' .. v:version % 100 .. '.9999'
  call assert_equal(0, has(ver))

  " There actually isn't a patch 9.0.0, but this is more consistent.
  call assert_equal(1, has('patch-9.0.0'))
endfunc

func Test_empty()
  call assert_equal(1, empty(''))
  call assert_equal(0, empty('a'))

  call assert_equal(1, empty(0))
  call assert_equal(1, empty(-0))
  call assert_equal(0, empty(1))
  call assert_equal(0, empty(-1))

  call assert_equal(1, empty(0.0))
  call assert_equal(1, empty(-0.0))
  call assert_equal(0, empty(1.0))
  call assert_equal(0, empty(-1.0))
  call assert_equal(0, empty(1.0/0.0))
  call assert_equal(0, empty(0.0/0.0))

  call assert_equal(1, empty([]))
  call assert_equal(0, empty(['a']))

  call assert_equal(1, empty({}))
  call assert_equal(0, empty({'a':1}))

  call assert_equal(1, empty(v:null))
  call assert_equal(1, empty(v:none))
  call assert_equal(1, empty(v:false))
  call assert_equal(0, empty(v:true))

  if has('channel')
    call assert_equal(1, empty(test_null_channel()))
  endif
  if has('job')
    call assert_equal(1, empty(test_null_job()))
  endif

  call assert_equal(0, empty(function('Test_empty')))
  call assert_equal(0, empty(function('Test_empty', [0])))

  call assert_fails("call empty(test_void())", ['E340:', 'E685:'])
  call assert_fails("call empty(test_unknown())", ['E340:', 'E685:'])
endfunc

func Test_err_teapot()
  call assert_fails('call err_teapot()', "E418: I'm a teapot")
  call assert_fails('call err_teapot(0)', "E418: I'm a teapot")
  call assert_fails('call err_teapot(v:false)', "E418: I'm a teapot")

  call assert_fails('call err_teapot("1")', "E503: Coffee is currently not available")
  call assert_fails('call err_teapot(v:true)', "E503: Coffee is currently not available")
  let expr = 1
  call assert_fails('call err_teapot(expr)', "E503: Coffee is currently not available")
endfunc

func Test_test_void()
  call assert_fails('echo 1 == test_void()', 'E1031:')
  call assert_fails('echo 1.0 == test_void()', 'E1031:')
  call assert_fails('let x = json_encode(test_void())', ['E340:', 'E685:'])
  call assert_fails('let x = copy(test_void())', ['E340:', 'E685:'])
  call assert_fails('let x = copy([test_void()])', 'E1031:')
endfunc

func Test_islocked()
  call assert_fails('call islocked(99)', 'E475:')
  call assert_fails('call islocked("s: x")', 'E488:')
endfunc

func Test_len()
  call assert_equal(1, len(0))
  call assert_equal(2, len(12))

  call assert_equal(0, len(''))
  call assert_equal(2, len('ab'))

  call assert_equal(0, len([]))
  call assert_equal(0, len(test_null_list()))
  call assert_equal(2, len([2, 1]))

  call assert_equal(0, len({}))
  call assert_equal(0, len(test_null_dict()))
  call assert_equal(2, len({'a': 1, 'b': 2}))

  call assert_fails('call len(v:none)', 'E701:')
  call assert_fails('call len({-> 0})', 'E701:')
endfunc

func Test_max()
  call assert_equal(0, max([]))
  call assert_equal(2, max([2]))
  call assert_equal(2, max([1, 2]))
  call assert_equal(2, max([1, 2, v:null]))

  call assert_equal(0, max({}))
  call assert_equal(2, max({'a':1, 'b':2}))

  call assert_fails('call max(1)', 'E712:')
  call assert_fails('call max(v:none)', 'E712:')

  " check we only get one error
  call assert_fails('call max([#{}, [1]])', ['E728:', 'E728:'])
  call assert_fails('call max(#{a: {}, b: [1]})', ['E728:', 'E728:'])
endfunc

func Test_min()
  call assert_equal(0, min([]))
  call assert_equal(2, min([2]))
  call assert_equal(1, min([1, 2]))
  call assert_equal(0, min([1, 2, v:null]))

  call assert_equal(0, min({}))
  call assert_equal(1, min({'a':1, 'b':2}))

  call assert_fails('call min(1)', 'E712:')
  call assert_fails('call min(v:none)', 'E712:')
  call assert_fails('call min([1, {}])', 'E728:')

  " check we only get one error
  call assert_fails('call min([[1], #{}])', ['E745:', 'E745:'])
  call assert_fails('call min(#{a: [1], b: #{}})', ['E745:', 'E745:'])
endfunc

func Test_strwidth()
  for aw in ['single', 'double']
    exe 'set ambiwidth=' . aw
    call assert_equal(0, strwidth(''))
    call assert_equal(1, strwidth("\t"))
    call assert_equal(3, strwidth('Vim'))
    call assert_equal(4, strwidth(1234))
    call assert_equal(5, strwidth(-1234))

    call assert_equal(2, strwidth('😉'))
    call assert_equal(17, strwidth('Eĥoŝanĝo ĉiuĵaŭde'))
    call assert_equal((aw == 'single') ? 6 : 7, strwidth('Straße'))

    call assert_fails('call strwidth({->0})', 'E729:')
    call assert_fails('call strwidth([])', 'E730:')
    call assert_fails('call strwidth({})', 'E731:')
  endfor

  call assert_equal(3, strwidth(1.2))
  call v9.CheckDefAndScriptFailure(['echo strwidth(1.2)'], ['E1013: Argument 1: type mismatch, expected string but got float', 'E1174: String required for argument 1'])

  set ambiwidth&
endfunc

func Test_str2nr()
  call assert_equal(0, str2nr(''))
  call assert_equal(1, str2nr('1'))
  call assert_equal(1, str2nr(' 1 '))

  call assert_equal(1, str2nr('+1'))
  call assert_equal(1, str2nr('+ 1'))
  call assert_equal(1, str2nr(' + 1 '))

  call assert_equal(-1, str2nr('-1'))
  call assert_equal(-1, str2nr('- 1'))
  call assert_equal(-1, str2nr(' - 1 '))

  call assert_equal(123456789, str2nr('123456789'))
  call assert_equal(-123456789, str2nr('-123456789'))

  call assert_equal(5, str2nr('101', 2))
  call assert_equal(5, '0b101'->str2nr(2))
  call assert_equal(5, str2nr('0B101', 2))
  call assert_equal(-5, str2nr('-101', 2))
  call assert_equal(-5, str2nr('-0b101', 2))
  call assert_equal(-5, str2nr('-0B101', 2))

  call assert_equal(65, str2nr('101', 8))
  call assert_equal(65, str2nr('0101', 8))
  call assert_equal(-65, str2nr('-101', 8))
  call assert_equal(-65, str2nr('-0101', 8))
  call assert_equal(65, str2nr('0o101', 8))
  call assert_equal(65, str2nr('0O0101', 8))
  call assert_equal(-65, str2nr('-0O101', 8))
  call assert_equal(-65, str2nr('-0o0101', 8))

  call assert_equal(11259375, str2nr('abcdef', 16))
  call assert_equal(11259375, str2nr('ABCDEF', 16))
  call assert_equal(-11259375, str2nr('-ABCDEF', 16))
  call assert_equal(11259375, str2nr('0xabcdef', 16))
  call assert_equal(11259375, str2nr('0Xabcdef', 16))
  call assert_equal(11259375, str2nr('0XABCDEF', 16))
  call assert_equal(-11259375, str2nr('-0xABCDEF', 16))

  call assert_equal(1, str2nr("1'000'000", 10, 0))
  call assert_equal(256, str2nr("1'0000'0000", 2, 1))
  call assert_equal(262144, str2nr("1'000'000", 8, 1))
  call assert_equal(1000000, str2nr("1'000'000", 10, 1))
  call assert_equal(1000, str2nr("1'000''000", 10, 1))
  call assert_equal(65536, str2nr("1'00'00", 16, 1))

  call assert_equal(0, str2nr('0x10'))
  call assert_equal(0, str2nr('0b10'))
  call assert_equal(0, str2nr('0o10'))
  call assert_equal(1, str2nr('12', 2))
  call assert_equal(1, str2nr('18', 8))
  call assert_equal(1, str2nr('1g', 16))

  call assert_equal(0, str2nr(v:null))
  call assert_equal(0, str2nr(v:none))

  call assert_fails('call str2nr([])', 'E730:')
  call assert_fails('call str2nr({->2})', 'E729:')
  call assert_equal(1, str2nr(1.2))
  call v9.CheckDefAndScriptFailure(['echo str2nr(1.2)'], ['E1013: Argument 1: type mismatch, expected string but got float', 'E1174: String required for argument 1'])
  call assert_fails('call str2nr(10, [])', 'E745:')
endfunc

func Test_strftime()
  CheckFunction strftime

  " Format of strftime() depends on system. We assume
  " that basic formats tested here are available and
  " identical on all systems which support strftime().
  "
  " The 2nd parameter of strftime() is a local time, so the output day
  " of strftime() can be 17 or 18, depending on timezone.
  call assert_match('^2017-01-1[78]$', strftime('%Y-%m-%d', 1484695512))
  "
  call assert_match('^\d\d\d\d-\(0\d\|1[012]\)-\([012]\d\|3[01]\) \([01]\d\|2[0-3]\):[0-5]\d:\([0-5]\d\|60\)$', '%Y-%m-%d %H:%M:%S'->strftime())

  call assert_fails('call strftime([])', 'E730:')
  call assert_fails('call strftime("%Y", [])', 'E745:')

  " Check that the time changes after we change the timezone
  " Save previous timezone value, if any
  if exists('$TZ')
    let tz = $TZ
  endif

  " Force EST and then UTC, save the current hour (24-hour clock) for each
  let $TZ = 'EST' | let est = strftime('%H')
  let $TZ = 'UTC' | let utc = strftime('%H')

  " Those hours should be two bytes long, and should not be the same; if they
  " are, a tzset(3) call may have failed somewhere
  call assert_equal(strlen(est), 2)
  call assert_equal(strlen(utc), 2)
  " TODO: this fails on MS-Windows
  if has('unix')
    call assert_notequal(est, utc)
  endif

  " If we cached a timezone value, put it back, otherwise clear it
  if exists('tz')
    let $TZ = tz
  else
    unlet $TZ
  endif
endfunc

func Test_strptime()
  CheckFunction strptime
  CheckNotBSD

  if exists('$TZ')
    let tz = $TZ
  endif
  let $TZ = 'UTC'

  call assert_equal(1484653763, strptime('%Y-%m-%d %T', '2017-01-17 11:49:23'))

  " Force DST and check that it's considered
  let $TZ = 'WINTER0SUMMER,J1,J365'
  call assert_equal(1484653763 - 3600, strptime('%Y-%m-%d %T', '2017-01-17 11:49:23'))

  call assert_fails('call strptime()', 'E119:')
  call assert_fails('call strptime("xxx")', 'E119:')
  " This fails on BSD 14 and returns
  " -2209078800 instead of 0
  call assert_equal(0, strptime("%Y", ''))
  call assert_equal(0, strptime("%Y", "xxx"))

  if exists('tz')
    let $TZ = tz
  else
    unlet $TZ
  endif
endfunc

func Test_resolve_unix()
  CheckUnix

  " Xlink1 -> Xlink2
  " Xlink2 -> Xlink3
  silent !ln -s -f Xlink2 Xlink1
  silent !ln -s -f Xlink3 Xlink2
  call assert_equal('Xlink3', resolve('Xlink1'))
  call assert_equal('./Xlink3', resolve('./Xlink1'))
  call assert_equal('Xlink3/', resolve('Xlink2/'))
  " FIXME: these tests result in things like "Xlink2/" instead of "Xlink3/"?!
  "call assert_equal('Xlink3/', resolve('Xlink1/'))
  "call assert_equal('./Xlink3/', resolve('./Xlink1/'))
  "call assert_equal(getcwd() . '/Xlink3/', resolve(getcwd() . '/Xlink1/'))
  call assert_equal(getcwd() . '/Xlink3', resolve(getcwd() . '/Xlink1'))

  " Test resolve() with a symlink cycle.
  " Xlink1 -> Xlink2
  " Xlink2 -> Xlink3
  " Xlink3 -> Xlink1
  silent !ln -s -f Xlink1 Xlink3
  call assert_fails('call resolve("Xlink1")',   'E655:')
  call assert_fails('call resolve("./Xlink1")', 'E655:')
  call assert_fails('call resolve("Xlink2")',   'E655:')
  call assert_fails('call resolve("Xlink3")',   'E655:')
  call delete('Xlink1')
  call delete('Xlink2')
  call delete('Xlink3')

  silent !ln -s -f Xresolvedir//Xfile Xresolvelink
  call assert_equal('Xresolvedir/Xfile', resolve('Xresolvelink'))
  call delete('Xresolvelink')

  silent !ln -s -f Xlink2/ Xlink1
  call assert_equal('Xlink2', 'Xlink1'->resolve())
  call assert_equal('Xlink2/', resolve('Xlink1/'))
  call delete('Xlink1')

  silent !ln -s -f ./Xlink2 Xlink1
  call assert_equal('Xlink2', resolve('Xlink1'))
  call assert_equal('./Xlink2', resolve('./Xlink1'))
  call delete('Xlink1')

  call assert_equal('/', resolve('/'))
endfunc

func s:normalize_fname(fname)
  let ret = substitute(a:fname, '\', '/', 'g')
  let ret = substitute(ret, '//', '/', 'g')
  return ret->tolower()
endfunc

func Test_resolve_win32()
  CheckMSWindows

  " test for shortcut file
  if executable('cscript')
    new Xresfile
    wq
    let lines =<< trim END
	Set fs = CreateObject("Scripting.FileSystemObject")
	Set ws = WScript.CreateObject("WScript.Shell")
	Set shortcut = ws.CreateShortcut("Xlink.lnk")
	shortcut.TargetPath = fs.BuildPath(ws.CurrentDirectory, "Xresfile")
	shortcut.Save
    END
    call writefile(lines, 'link.vbs')
    silent !cscript link.vbs
    call delete('link.vbs')
    call assert_equal(s:normalize_fname(getcwd() . '\Xresfile'), s:normalize_fname(resolve('./Xlink.lnk')))
    call delete('Xresfile')

    call assert_equal(s:normalize_fname(getcwd() . '\Xresfile'), s:normalize_fname(resolve('./Xlink.lnk')))
    call delete('Xlink.lnk')
  else
    echomsg 'skipped test for shortcut file'
  endif

  " remove files
  call delete('Xlink')
  call delete('Xdir', 'd')
  call delete('Xresfile')

  " test for symbolic link to a file
  new Xresfile
  wq
  call assert_equal('Xresfile', resolve('Xresfile'))
  silent !mklink Xlink Xresfile
  if !v:shell_error
    call assert_equal(s:normalize_fname(getcwd() . '\Xresfile'), s:normalize_fname(resolve('./Xlink')))
    call delete('Xlink')
  else
    echomsg 'skipped test for symbolic link to a file'
  endif
  call delete('Xresfile')

  " test for junction to a directory
  call mkdir('Xdir')
  silent !mklink /J Xlink Xdir
  if !v:shell_error
    call assert_equal(s:normalize_fname(getcwd() . '\Xdir'), s:normalize_fname(resolve(getcwd() . '/Xlink')))

    call delete('Xdir', 'd')

    " test for junction already removed
    call assert_equal(s:normalize_fname(getcwd() . '\Xlink'), s:normalize_fname(resolve(getcwd() . '/Xlink')))
    call delete('Xlink')
  else
    echomsg 'skipped test for junction to a directory'
    call delete('Xdir', 'd')
  endif

  " test for symbolic link to a directory
  call mkdir('Xdir')
  silent !mklink /D Xlink Xdir
  if !v:shell_error
    call assert_equal(s:normalize_fname(getcwd() . '\Xdir'), s:normalize_fname(resolve(getcwd() . '/Xlink')))

    call delete('Xdir', 'd')

    " test for symbolic link already removed
    call assert_equal(s:normalize_fname(getcwd() . '\Xlink'), s:normalize_fname(resolve(getcwd() . '/Xlink')))
    call delete('Xlink')
  else
    echomsg 'skipped test for symbolic link to a directory'
    call delete('Xdir', 'd')
  endif

  " test for buffer name
  new Xbuffile
  wq
  silent !mklink Xlink Xbuffile
  if !v:shell_error
    edit Xlink
    call assert_equal('Xlink', bufname('%'))
    call delete('Xlink')
    bw!
  else
    echomsg 'skipped test for buffer name'
  endif
  call delete('Xbuffile')

  " test for reparse point
  call mkdir('Xdir')
  call assert_equal('Xdir', resolve('Xdir'))
  silent !mklink /D Xdirlink Xdir
  if !v:shell_error
    w Xdir/text.txt
    call assert_equal('Xdir/text.txt', resolve('Xdir/text.txt'))
    call assert_equal(s:normalize_fname(getcwd() . '\Xdir\text.txt'), s:normalize_fname(resolve('Xdirlink\text.txt')))
    call assert_equal(s:normalize_fname(getcwd() . '\Xdir'), s:normalize_fname(resolve('Xdirlink')))
    call delete('Xdirlink')
  else
    echomsg 'skipped test for reparse point'
  endif

  call delete('Xdir', 'rf')
endfunc

func Test_simplify()
  call assert_equal('',            simplify(''))
  call assert_equal('/',           simplify('/'))
  call assert_equal('/',           simplify('/.'))
  call assert_equal('/',           simplify('/..'))
  call assert_equal('/...',        simplify('/...'))
  call assert_equal('//path',      simplify('//path'))
  if has('unix')
    call assert_equal('/path',       simplify('///path'))
    call assert_equal('/path',       simplify('////path'))
  endif

  call assert_equal('./dir/file',  './dir/file'->simplify())
  call assert_equal('./dir/file',  simplify('.///dir//file'))
  call assert_equal('./dir/file',  simplify('./dir/./file'))
  call assert_equal('./file',      simplify('./dir/../file'))
  call assert_equal('../dir/file', simplify('dir/../../dir/file'))
  call assert_equal('./file',      simplify('dir/.././file'))
  call assert_equal('../dir',      simplify('./../dir'))
  call assert_equal('..',          simplify('../testdir/..'))
  call mkdir('Xsimpdir')
  call assert_equal('.',           simplify('Xsimpdir/../.'))
  call delete('Xsimpdir', 'd')

  call assert_fails('call simplify({->0})', 'E729:')
  call assert_fails('call simplify([])', 'E730:')
  call assert_fails('call simplify({})', 'E731:')
  call assert_equal('1.2', simplify(1.2))
  call v9.CheckDefAndScriptFailure(['echo simplify(1.2)'], ['E1013: Argument 1: type mismatch, expected string but got float', 'E1174: String required for argument 1'])
endfunc

func Test_pathshorten()
  call assert_equal('', pathshorten(''))
  call assert_equal('foo', pathshorten('foo'))
  call assert_equal('/foo', '/foo'->pathshorten())
  call assert_equal('f/', pathshorten('foo/'))
  call assert_equal('f/bar', pathshorten('foo/bar'))
  call assert_equal('f/b/foobar', 'foo/bar/foobar'->pathshorten())
  call assert_equal('/f/b/foobar', pathshorten('/foo/bar/foobar'))
  call assert_equal('.f/bar', pathshorten('.foo/bar'))
  call assert_equal('~f/bar', pathshorten('~foo/bar'))
  call assert_equal('~.f/bar', pathshorten('~.foo/bar'))
  call assert_equal('.~f/bar', pathshorten('.~foo/bar'))
  call assert_equal('~/f/bar', pathshorten('~/foo/bar'))
  call assert_fails('call pathshorten([])', 'E730:')

  " test pathshorten with optional variable to set preferred size of shortening
  call assert_equal('', pathshorten('', 2))
  call assert_equal('foo', pathshorten('foo', 2))
  call assert_equal('/foo', pathshorten('/foo', 2))
  call assert_equal('fo/', pathshorten('foo/', 2))
  call assert_equal('fo/bar', pathshorten('foo/bar', 2))
  call assert_equal('fo/ba/foobar', pathshorten('foo/bar/foobar', 2))
  call assert_equal('/fo/ba/foobar', pathshorten('/foo/bar/foobar', 2))
  call assert_equal('.fo/bar', pathshorten('.foo/bar', 2))
  call assert_equal('~fo/bar', pathshorten('~foo/bar', 2))
  call assert_equal('~.fo/bar', pathshorten('~.foo/bar', 2))
  call assert_equal('.~fo/bar', pathshorten('.~foo/bar', 2))
  call assert_equal('~/fo/bar', pathshorten('~/foo/bar', 2))
  call assert_fails('call pathshorten([],2)', 'E730:')
  call assert_notequal('~/fo/bar', pathshorten('~/foo/bar', 3))
  call assert_equal('~/foo/bar', pathshorten('~/foo/bar', 3))
  call assert_equal('~/f/bar', pathshorten('~/foo/bar', 0))
endfunc

func Test_strpart()
  call assert_equal('de', strpart('abcdefg', 3, 2))
  call assert_equal('ab', strpart('abcdefg', -2, 4))
  call assert_equal('abcdefg', 'abcdefg'->strpart(-2))
  call assert_equal('fg', strpart('abcdefg', 5, 4))
  call assert_equal('defg', strpart('abcdefg', 3))
  call assert_equal('', strpart('abcdefg', 10))
  call assert_fails("let s=strpart('abcdef', [])", 'E745:')

  call assert_equal('lép', strpart('éléphant', 2, 4))
  call assert_equal('léphant', strpart('éléphant', 2))

  call assert_equal('é', strpart('éléphant', 0, 1, 1))
  call assert_equal('ép', strpart('éléphant', 3, 2, v:true))
  call assert_equal('ó', strpart('cómposed', 1, 1, 1))
endfunc

func Test_tolower()
  call assert_equal("", tolower(""))

  " Test with all printable ASCII characters.
  call assert_equal(' !"#$%&''()*+,-./0123456789:;<=>?@abcdefghijklmnopqrstuvwxyz[\]^_`abcdefghijklmnopqrstuvwxyz{|}~',
          \ tolower(' !"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~'))

  " Test with a few uppercase diacritics.
  call assert_equal("aàáâãäåāăąǎǟǡả", tolower("AÀÁÂÃÄÅĀĂĄǍǞǠẢ"))
  call assert_equal("bḃḇ", tolower("BḂḆ"))
  call assert_equal("cçćĉċč", tolower("CÇĆĈĊČ"))
  call assert_equal("dďđḋḏḑ", tolower("DĎĐḊḎḐ"))
  call assert_equal("eèéêëēĕėęěẻẽ", tolower("EÈÉÊËĒĔĖĘĚẺẼ"))
  call assert_equal("fḟ ", tolower("FḞ "))
  call assert_equal("gĝğġģǥǧǵḡ", tolower("GĜĞĠĢǤǦǴḠ"))
  call assert_equal("hĥħḣḧḩ", tolower("HĤĦḢḦḨ"))
  call assert_equal("iìíîïĩīĭįiǐỉ", tolower("IÌÍÎÏĨĪĬĮİǏỈ"))
  call assert_equal("jĵ", tolower("JĴ"))
  call assert_equal("kķǩḱḵ", tolower("KĶǨḰḴ"))
  call assert_equal("lĺļľŀłḻ", tolower("LĹĻĽĿŁḺ"))
  call assert_equal("mḿṁ", tolower("MḾṀ"))
  call assert_equal("nñńņňṅṉ", tolower("NÑŃŅŇṄṈ"))
  call assert_equal("oòóôõöøōŏőơǒǫǭỏ", tolower("OÒÓÔÕÖØŌŎŐƠǑǪǬỎ"))
  call assert_equal("pṕṗ", tolower("PṔṖ"))
  call assert_equal("q", tolower("Q"))
  call assert_equal("rŕŗřṙṟ", tolower("RŔŖŘṘṞ"))
  call assert_equal("sśŝşšṡ", tolower("SŚŜŞŠṠ"))
  call assert_equal("tţťŧṫṯ", tolower("TŢŤŦṪṮ"))
  call assert_equal("uùúûüũūŭůűųưǔủ", tolower("UÙÚÛÜŨŪŬŮŰŲƯǓỦ"))
  call assert_equal("vṽ", tolower("VṼ"))
  call assert_equal("wŵẁẃẅẇ", tolower("WŴẀẂẄẆ"))
  call assert_equal("xẋẍ", tolower("XẊẌ"))
  call assert_equal("yýŷÿẏỳỷỹ", tolower("YÝŶŸẎỲỶỸ"))
  call assert_equal("zźżžƶẑẕ", tolower("ZŹŻŽƵẐẔ"))

  " Test with a few lowercase diacritics, which should remain unchanged.
  call assert_equal("aàáâãäåāăąǎǟǡả", tolower("aàáâãäåāăąǎǟǡả"))
  call assert_equal("bḃḇ", tolower("bḃḇ"))
  call assert_equal("cçćĉċč", tolower("cçćĉċč"))
  call assert_equal("dďđḋḏḑ", tolower("dďđḋḏḑ"))
  call assert_equal("eèéêëēĕėęěẻẽ", tolower("eèéêëēĕėęěẻẽ"))
  call assert_equal("fḟ", tolower("fḟ"))
  call assert_equal("gĝğġģǥǧǵḡ", tolower("gĝğġģǥǧǵḡ"))
  call assert_equal("hĥħḣḧḩẖ", tolower("hĥħḣḧḩẖ"))
  call assert_equal("iìíîïĩīĭįǐỉ", tolower("iìíîïĩīĭįǐỉ"))
  call assert_equal("jĵǰ", tolower("jĵǰ"))
  call assert_equal("kķǩḱḵ", tolower("kķǩḱḵ"))
  call assert_equal("lĺļľŀłḻ", tolower("lĺļľŀłḻ"))
  call assert_equal("mḿṁ ", tolower("mḿṁ "))
  call assert_equal("nñńņňʼnṅṉ", tolower("nñńņňʼnṅṉ"))
  call assert_equal("oòóôõöøōŏőơǒǫǭỏ", tolower("oòóôõöøōŏőơǒǫǭỏ"))
  call assert_equal("pṕṗ", tolower("pṕṗ"))
  call assert_equal("q", tolower("q"))
  call assert_equal("rŕŗřṙṟ", tolower("rŕŗřṙṟ"))
  call assert_equal("sśŝşšṡ", tolower("sśŝşšṡ"))
  call assert_equal("tţťŧṫṯẗ", tolower("tţťŧṫṯẗ"))
  call assert_equal("uùúûüũūŭůűųưǔủ", tolower("uùúûüũūŭůűųưǔủ"))
  call assert_equal("vṽ", tolower("vṽ"))
  call assert_equal("wŵẁẃẅẇẘ", tolower("wŵẁẃẅẇẘ"))
  call assert_equal("ẋẍ", tolower("ẋẍ"))
  call assert_equal("yýÿŷẏẙỳỷỹ", tolower("yýÿŷẏẙỳỷỹ"))
  call assert_equal("zźżžƶẑẕ", tolower("zźżžƶẑẕ"))

  " According to https://twitter.com/jifa/status/625776454479970304
  " Ⱥ (U+023A) and Ⱦ (U+023E) are the *only* code points to increase
  " in length (2 to 3 bytes) when lowercased. So let's test them.
  call assert_equal("ⱥ ⱦ", tolower("Ⱥ Ⱦ"))

  " This call to tolower with invalid utf8 sequence used to cause access to
  " invalid memory.
  call tolower("\xC0\x80\xC0")
  call tolower("123\xC0\x80\xC0")

  " Test in latin1 encoding
  let save_enc = &encoding
  set encoding=latin1
  call assert_equal("abc", tolower("ABC"))
  let &encoding = save_enc
endfunc

func Test_toupper()
  call assert_equal("", toupper(""))

  " Test with all printable ASCII characters.
  call assert_equal(' !"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~',
          \ toupper(' !"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~'))

  " Test with a few lowercase diacritics.
  call assert_equal("AÀÁÂÃÄÅĀĂĄǍǞǠẢ", "aàáâãäåāăąǎǟǡả"->toupper())
  call assert_equal("BḂḆ", toupper("bḃḇ"))
  call assert_equal("CÇĆĈĊČ", toupper("cçćĉċč"))
  call assert_equal("DĎĐḊḎḐ", toupper("dďđḋḏḑ"))
  call assert_equal("EÈÉÊËĒĔĖĘĚẺẼ", toupper("eèéêëēĕėęěẻẽ"))
  call assert_equal("FḞ", toupper("fḟ"))
  call assert_equal("GĜĞĠĢǤǦǴḠ", toupper("gĝğġģǥǧǵḡ"))
  call assert_equal("HĤĦḢḦḨẖ", toupper("hĥħḣḧḩẖ"))
  call assert_equal("IÌÍÎÏĨĪĬĮǏỈ", toupper("iìíîïĩīĭįǐỉ"))
  call assert_equal("JĴǰ", toupper("jĵǰ"))
  call assert_equal("KĶǨḰḴ", toupper("kķǩḱḵ"))
  call assert_equal("LĹĻĽĿŁḺ", toupper("lĺļľŀłḻ"))
  call assert_equal("MḾṀ ", toupper("mḿṁ "))
  call assert_equal("NÑŃŅŇʼnṄṈ", toupper("nñńņňʼnṅṉ"))
  call assert_equal("OÒÓÔÕÖØŌŎŐƠǑǪǬỎ", toupper("oòóôõöøōŏőơǒǫǭỏ"))
  call assert_equal("PṔṖ", toupper("pṕṗ"))
  call assert_equal("Q", toupper("q"))
  call assert_equal("RŔŖŘṘṞ", toupper("rŕŗřṙṟ"))
  call assert_equal("SŚŜŞŠṠ", toupper("sśŝşšṡ"))
  call assert_equal("TŢŤŦṪṮẗ", toupper("tţťŧṫṯẗ"))
  call assert_equal("UÙÚÛÜŨŪŬŮŰŲƯǓỦ", toupper("uùúûüũūŭůűųưǔủ"))
  call assert_equal("VṼ", toupper("vṽ"))
  call assert_equal("WŴẀẂẄẆẘ", toupper("wŵẁẃẅẇẘ"))
  call assert_equal("ẊẌ", toupper("ẋẍ"))
  call assert_equal("YÝŸŶẎẙỲỶỸ", toupper("yýÿŷẏẙỳỷỹ"))
  call assert_equal("ZŹŻŽƵẐẔ", toupper("zźżžƶẑẕ"))

  " Test that uppercase diacritics, which should remain unchanged.
  call assert_equal("AÀÁÂÃÄÅĀĂĄǍǞǠẢ", toupper("AÀÁÂÃÄÅĀĂĄǍǞǠẢ"))
  call assert_equal("BḂḆ", toupper("BḂḆ"))
  call assert_equal("CÇĆĈĊČ", toupper("CÇĆĈĊČ"))
  call assert_equal("DĎĐḊḎḐ", toupper("DĎĐḊḎḐ"))
  call assert_equal("EÈÉÊËĒĔĖĘĚẺẼ", toupper("EÈÉÊËĒĔĖĘĚẺẼ"))
  call assert_equal("FḞ ", toupper("FḞ "))
  call assert_equal("GĜĞĠĢǤǦǴḠ", toupper("GĜĞĠĢǤǦǴḠ"))
  call assert_equal("HĤĦḢḦḨ", toupper("HĤĦḢḦḨ"))
  call assert_equal("IÌÍÎÏĨĪĬĮİǏỈ", toupper("IÌÍÎÏĨĪĬĮİǏỈ"))
  call assert_equal("JĴ", toupper("JĴ"))
  call assert_equal("KĶǨḰḴ", toupper("KĶǨḰḴ"))
  call assert_equal("LĹĻĽĿŁḺ", toupper("LĹĻĽĿŁḺ"))
  call assert_equal("MḾṀ", toupper("MḾṀ"))
  call assert_equal("NÑŃŅŇṄṈ", toupper("NÑŃŅŇṄṈ"))
  call assert_equal("OÒÓÔÕÖØŌŎŐƠǑǪǬỎ", toupper("OÒÓÔÕÖØŌŎŐƠǑǪǬỎ"))
  call assert_equal("PṔṖ", toupper("PṔṖ"))
  call assert_equal("Q", toupper("Q"))
  call assert_equal("RŔŖŘṘṞ", toupper("RŔŖŘṘṞ"))
  call assert_equal("SŚŜŞŠṠ", toupper("SŚŜŞŠṠ"))
  call assert_equal("TŢŤŦṪṮ", toupper("TŢŤŦṪṮ"))
  call assert_equal("UÙÚÛÜŨŪŬŮŰŲƯǓỦ", toupper("UÙÚÛÜŨŪŬŮŰŲƯǓỦ"))
  call assert_equal("VṼ", toupper("VṼ"))
  call assert_equal("WŴẀẂẄẆ", toupper("WŴẀẂẄẆ"))
  call assert_equal("XẊẌ", toupper("XẊẌ"))
  call assert_equal("YÝŶŸẎỲỶỸ", toupper("YÝŶŸẎỲỶỸ"))
  call assert_equal("ZŹŻŽƵẐẔ", toupper("ZŹŻŽƵẐẔ"))

  call assert_equal("Ⱥ Ⱦ", toupper("ⱥ ⱦ"))

  " This call to toupper with invalid utf8 sequence used to cause access to
  " invalid memory.
  call toupper("\xC0\x80\xC0")
  call toupper("123\xC0\x80\xC0")

  " Test in latin1 encoding
  let save_enc = &encoding
  set encoding=latin1
  call assert_equal("ABC", toupper("abc"))
  let &encoding = save_enc
endfunc

func Test_tr()
  call assert_equal('foo', tr('bar', 'bar', 'foo'))
  call assert_equal('zxy', 'cab'->tr('abc', 'xyz'))
  call assert_fails("let s=tr([], 'abc', 'def')", 'E730:')
  call assert_fails("let s=tr('abc', [], 'def')", 'E730:')
  call assert_fails("let s=tr('abc', 'abc', [])", 'E730:')
  call assert_fails("let s=tr('abcd', 'abcd', 'def')", 'E475:')
  set encoding=latin1
  call assert_fails("let s=tr('abcd', 'abcd', 'def')", 'E475:')
  call assert_equal('hEllO', tr('hello', 'eo', 'EO'))
  call assert_equal('hello', tr('hello', 'xy', 'ab'))
  call assert_fails('call tr("abc", "123", "₁₂")', 'E475:')
  set encoding=utf8
endfunc

" Tests for the mode() function
let current_modes = ''
func Save_mode()
  let g:current_modes = mode(0) . '-' . mode(1)
  return ''
endfunc

" Test for the mode() function
func Test_mode()
  new
  call append(0, ["Blue Ball Black", "Brown Band Bowl", ""])

  " Only complete from the current buffer.
  set complete=.

  inoremap <F2> <C-R>=Save_mode()<CR>
  xnoremap <F2> <Cmd>call Save_mode()<CR>

  normal! 3G
  exe "normal i\<F2>\<Esc>"
  call assert_equal('i-i', g:current_modes)
  " i_CTRL-P: Multiple matches
  exe "normal i\<C-G>uBa\<C-P>\<F2>\<Esc>u"
  call assert_equal('i-ic', g:current_modes)
  " i_CTRL-P: Single match
  exe "normal iBro\<C-P>\<F2>\<Esc>u"
  call assert_equal('i-ic', g:current_modes)
  " i_CTRL-X
  exe "normal iBa\<C-X>\<F2>\<Esc>u"
  call assert_equal('i-ix', g:current_modes)
  " i_CTRL-X CTRL-P: Multiple matches
  exe "normal iBa\<C-X>\<C-P>\<F2>\<Esc>u"
  call assert_equal('i-ic', g:current_modes)
  " i_CTRL-X CTRL-P: Single match
  exe "normal iBro\<C-X>\<C-P>\<F2>\<Esc>u"
  call assert_equal('i-ic', g:current_modes)
  " i_CTRL-X CTRL-P + CTRL-P: Single match
  exe "normal iBro\<C-X>\<C-P>\<C-P>\<F2>\<Esc>u"
  call assert_equal('i-ic', g:current_modes)
  " i_CTRL-X CTRL-L: Multiple matches
  exe "normal i\<C-X>\<C-L>\<F2>\<Esc>u"
  call assert_equal('i-ic', g:current_modes)
  " i_CTRL-X CTRL-L: Single match
  exe "normal iBlu\<C-X>\<C-L>\<F2>\<Esc>u"
  call assert_equal('i-ic', g:current_modes)
  " i_CTRL-P: No match
  exe "normal iCom\<C-P>\<F2>\<Esc>u"
  call assert_equal('i-ic', g:current_modes)
  " i_CTRL-X CTRL-P: No match
  exe "normal iCom\<C-X>\<C-P>\<F2>\<Esc>u"
  call assert_equal('i-ic', g:current_modes)
  " i_CTRL-X CTRL-L: No match
  exe "normal iabc\<C-X>\<C-L>\<F2>\<Esc>u"
  call assert_equal('i-ic', g:current_modes)

  exe "normal R\<F2>\<Esc>"
  call assert_equal('R-R', g:current_modes)
  " R_CTRL-P: Multiple matches
  exe "normal RBa\<C-P>\<F2>\<Esc>u"
  call assert_equal('R-Rc', g:current_modes)
  " R_CTRL-P: Single match
  exe "normal RBro\<C-P>\<F2>\<Esc>u"
  call assert_equal('R-Rc', g:current_modes)
  " R_CTRL-X
  exe "normal RBa\<C-X>\<F2>\<Esc>u"
  call assert_equal('R-Rx', g:current_modes)
  " R_CTRL-X CTRL-P: Multiple matches
  exe "normal RBa\<C-X>\<C-P>\<F2>\<Esc>u"
  call assert_equal('R-Rc', g:current_modes)
  " R_CTRL-X CTRL-P: Single match
  exe "normal RBro\<C-X>\<C-P>\<F2>\<Esc>u"
  call assert_equal('R-Rc', g:current_modes)
  " R_CTRL-X CTRL-P + CTRL-P: Single match
  exe "normal RBro\<C-X>\<C-P>\<C-P>\<F2>\<Esc>u"
  call assert_equal('R-Rc', g:current_modes)
  " R_CTRL-X CTRL-L: Multiple matches
  exe "normal R\<C-X>\<C-L>\<F2>\<Esc>u"
  call assert_equal('R-Rc', g:current_modes)
  " R_CTRL-X CTRL-L: Single match
  exe "normal RBlu\<C-X>\<C-L>\<F2>\<Esc>u"
  call assert_equal('R-Rc', g:current_modes)
  " R_CTRL-P: No match
  exe "normal RCom\<C-P>\<F2>\<Esc>u"
  call assert_equal('R-Rc', g:current_modes)
  " R_CTRL-X CTRL-P: No match
  exe "normal RCom\<C-X>\<C-P>\<F2>\<Esc>u"
  call assert_equal('R-Rc', g:current_modes)
  " R_CTRL-X CTRL-L: No match
  exe "normal Rabc\<C-X>\<C-L>\<F2>\<Esc>u"
  call assert_equal('R-Rc', g:current_modes)

  exe "normal gR\<F2>\<Esc>"
  call assert_equal('R-Rv', g:current_modes)
  " gR_CTRL-P: Multiple matches
  exe "normal gRBa\<C-P>\<F2>\<Esc>u"
  call assert_equal('R-Rvc', g:current_modes)
  " gR_CTRL-P: Single match
  exe "normal gRBro\<C-P>\<F2>\<Esc>u"
  call assert_equal('R-Rvc', g:current_modes)
  " gR_CTRL-X
  exe "normal gRBa\<C-X>\<F2>\<Esc>u"
  call assert_equal('R-Rvx', g:current_modes)
  " gR_CTRL-X CTRL-P: Multiple matches
  exe "normal gRBa\<C-X>\<C-P>\<F2>\<Esc>u"
  call assert_equal('R-Rvc', g:current_modes)
  " gR_CTRL-X CTRL-P: Single match
  exe "normal gRBro\<C-X>\<C-P>\<F2>\<Esc>u"
  call assert_equal('R-Rvc', g:current_modes)
  " gR_CTRL-X CTRL-P + CTRL-P: Single match
  exe "normal gRBro\<C-X>\<C-P>\<C-P>\<F2>\<Esc>u"
  call assert_equal('R-Rvc', g:current_modes)
  " gR_CTRL-X CTRL-L: Multiple matches
  exe "normal gR\<C-X>\<C-L>\<F2>\<Esc>u"
  call assert_equal('R-Rvc', g:current_modes)
  " gR_CTRL-X CTRL-L: Single match
  exe "normal gRBlu\<C-X>\<C-L>\<F2>\<Esc>u"
  call assert_equal('R-Rvc', g:current_modes)
  " gR_CTRL-P: No match
  exe "normal gRCom\<C-P>\<F2>\<Esc>u"
  call assert_equal('R-Rvc', g:current_modes)
  " gR_CTRL-X CTRL-P: No match
  exe "normal gRCom\<C-X>\<C-P>\<F2>\<Esc>u"
  call assert_equal('R-Rvc', g:current_modes)
  " gR_CTRL-X CTRL-L: No match
  exe "normal gRabc\<C-X>\<C-L>\<F2>\<Esc>u"
  call assert_equal('R-Rvc', g:current_modes)

  call assert_equal('n', 0->mode())
  call assert_equal('n', 1->mode())

  " i_CTRL-O
  exe "normal i\<C-O>:call Save_mode()\<Cr>\<Esc>"
  call assert_equal("n-niI", g:current_modes)

  " R_CTRL-O
  exe "normal R\<C-O>:call Save_mode()\<Cr>\<Esc>"
  call assert_equal("n-niR", g:current_modes)

  " gR_CTRL-O
  exe "normal gR\<C-O>:call Save_mode()\<Cr>\<Esc>"
  call assert_equal("n-niV", g:current_modes)

  " How to test operator-pending mode?

  call feedkeys("v", 'xt')
  call assert_equal('v', mode())
  call assert_equal('v', mode(1))
  call feedkeys("\<Esc>V", 'xt')
  call assert_equal('V', mode())
  call assert_equal('V', mode(1))
  call feedkeys("\<Esc>\<C-V>", 'xt')
  call assert_equal("\<C-V>", mode())
  call assert_equal("\<C-V>", mode(1))
  call feedkeys("\<Esc>", 'xt')

  call feedkeys("gh", 'xt')
  call assert_equal('s', mode())
  call assert_equal('s', mode(1))
  call feedkeys("\<Esc>gH", 'xt')
  call assert_equal('S', mode())
  call assert_equal('S', mode(1))
  call feedkeys("\<Esc>g\<C-H>", 'xt')
  call assert_equal("\<C-S>", mode())
  call assert_equal("\<C-S>", mode(1))
  call feedkeys("\<Esc>", 'xt')

  " v_CTRL-O
  exe "normal gh\<C-O>\<F2>\<Esc>"
  call assert_equal("v-vs", g:current_modes)
  exe "normal gH\<C-O>\<F2>\<Esc>"
  call assert_equal("V-Vs", g:current_modes)
  exe "normal g\<C-H>\<C-O>\<F2>\<Esc>"
  call assert_equal("\<C-V>-\<C-V>s", g:current_modes)

  call feedkeys(":echo \<C-R>=Save_mode()\<C-U>\<CR>", 'xt')
  call assert_equal('c-c', g:current_modes)
  call feedkeys("gQecho \<C-R>=Save_mode()\<CR>\<CR>vi\<CR>", 'xt')
  call assert_equal('c-cv', g:current_modes)
  call feedkeys("Qcall Save_mode()\<CR>vi\<CR>", 'xt')
  call assert_equal('c-ce', g:current_modes)
  " How to test Ex mode?

  " Test mode in operatorfunc (it used to be Operator-pending).
  set operatorfunc=OperatorFunc
  function OperatorFunc(_)
    call Save_mode()
  endfunction
  execute "normal! g@l\<Esc>"
  call assert_equal('n-n', g:current_modes)
  execute "normal! i\<C-o>g@l\<Esc>"
  call assert_equal('n-niI', g:current_modes)
  execute "normal! R\<C-o>g@l\<Esc>"
  call assert_equal('n-niR', g:current_modes)
  execute "normal! gR\<C-o>g@l\<Esc>"
  call assert_equal('n-niV', g:current_modes)

  if has('terminal')
    term
    " Terminal-Job mode
    call assert_equal('t', mode())
    call assert_equal('t', mode(1))
    call feedkeys("\<C-W>:echo \<C-R>=Save_mode()\<C-U>\<CR>", 'xt')
    call assert_equal("c-ct", g:current_modes)
    call feedkeys("\<Esc>", 'xt')

    " Terminal-Normal mode
    call feedkeys("\<C-W>N", 'xt')
    call assert_equal('n', mode())
    call assert_equal('nt', mode(1))
    call feedkeys(":echo \<C-R>=Save_mode()\<C-U>\<CR>", 'xt')
    call assert_equal("c-c", g:current_modes)
    call feedkeys("aexit\<CR>", 'xt')
  endif

  bwipe!
  iunmap <F2>
  xunmap <F2>
  set complete&
  set operatorfunc&
  delfunction OperatorFunc
endfunc

" Test for append()
func Test_append()
  enew!
  split
  call assert_equal(0, append(1, []))
  call assert_equal(0, append(1, test_null_list()))
  call assert_equal(0, append(0, ["foo"]))
  call assert_equal(0, append(1, []))
  call assert_equal(0, append(1, test_null_list()))
  call assert_equal(0, append(8, []))
  call assert_equal(0, append(9, test_null_list()))
  call assert_equal(['foo', ''], getline(1, '$'))
  split
  only
  undo
  undo

  " Using $ instead of '$' must give an error
  call assert_fails("call append($, 'foobar')", 'E116:')

  call assert_fails("call append({}, '')", ['E728:', 'E728:'])
endfunc

" Test for setline()
func Test_setline()
  new
  call setline(0, ["foo"])
  call setline(0, [])
  call setline(0, test_null_list())
  call setline(1, ["bar"])
  call setline(1, [])
  call setline(1, test_null_list())
  call setline(2, [])
  call setline(2, test_null_list())
  call setline(3, [])
  call setline(3, test_null_list())
  call setline(2, ["baz"])
  call assert_equal(['bar', 'baz'], getline(1, '$'))
  close!
endfunc

func Test_getbufvar()
  let bnr = bufnr('%')
  let b:var_num = '1234'
  let def_num = '5678'
  call assert_equal('1234', getbufvar(bnr, 'var_num'))
  call assert_equal('1234', getbufvar(bnr, 'var_num', def_num))

  let bd = getbufvar(bnr, '')
  call assert_equal('1234', bd['var_num'])
  call assert_true(exists("bd['changedtick']"))
  call assert_equal(2, len(bd))

  let bd2 = getbufvar(bnr, '', def_num)
  call assert_equal(bd, bd2)

  unlet b:var_num
  call assert_equal(def_num, getbufvar(bnr, 'var_num', def_num))
  call assert_equal('', getbufvar(bnr, 'var_num'))

  let bd = getbufvar(bnr, '')
  call assert_equal(1, len(bd))
  let bd = getbufvar(bnr, '',def_num)
  call assert_equal(1, len(bd))

  call assert_equal('', getbufvar(9999, ''))
  call assert_equal(def_num, getbufvar(9999, '', def_num))
  unlet def_num

  call assert_equal(0, getbufvar(bnr, '&autoindent'))
  call assert_equal(0, getbufvar(bnr, '&autoindent', 1))

  " Set and get a buffer-local variable
  call setbufvar(bnr, 'bufvar_test', ['one', 'two'])
  call assert_equal(['one', 'two'], getbufvar(bnr, 'bufvar_test'))

  " Open new window with forced option values
  set fileformats=unix,dos
  new ++ff=dos ++bin ++enc=iso-8859-2
  call assert_equal('dos', getbufvar(bufnr('%'), '&fileformat'))
  call assert_equal(1, getbufvar(bufnr('%'), '&bin'))
  call assert_equal('iso-8859-2', getbufvar(bufnr('%'), '&fenc'))
  close

  " Get the b: dict.
  let b:testvar = 'one'
  new
  let b:testvar = 'two'
  let thebuf = bufnr()
  wincmd w
  call assert_equal('two', getbufvar(thebuf, 'testvar'))
  call assert_equal('two', getbufvar(thebuf, '').testvar)
  bwipe!

  set fileformats&
endfunc

func Test_last_buffer_nr()
  call assert_equal(bufnr('$'), last_buffer_nr())
endfunc

func Test_stridx()
  call assert_equal(-1, stridx('', 'l'))
  call assert_equal(0,  stridx('', ''))
  call assert_equal(0,  'hello'->stridx(''))
  call assert_equal(-1, stridx('hello', 'L'))
  call assert_equal(2,  stridx('hello', 'l', -1))
  call assert_equal(2,  stridx('hello', 'l', 0))
  call assert_equal(2,  'hello'->stridx('l', 1))
  call assert_equal(3,  stridx('hello', 'l', 3))
  call assert_equal(-1, stridx('hello', 'l', 4))
  call assert_equal(-1, stridx('hello', 'l', 10))
  call assert_equal(2,  stridx('hello', 'll'))
  call assert_equal(-1, stridx('hello', 'hello world'))
  call assert_fails("let n=stridx('hello', [])", 'E730:')
  call assert_fails("let n=stridx([], 'l')", 'E730:')
endfunc

func Test_strridx()
  call assert_equal(-1, strridx('', 'l'))
  call assert_equal(0,  strridx('', ''))
  call assert_equal(5,  strridx('hello', ''))
  call assert_equal(-1, strridx('hello', 'L'))
  call assert_equal(3,  'hello'->strridx('l'))
  call assert_equal(3,  strridx('hello', 'l', 10))
  call assert_equal(3,  strridx('hello', 'l', 3))
  call assert_equal(2,  strridx('hello', 'l', 2))
  call assert_equal(-1, strridx('hello', 'l', 1))
  call assert_equal(-1, strridx('hello', 'l', 0))
  call assert_equal(-1, strridx('hello', 'l', -1))
  call assert_equal(2,  strridx('hello', 'll'))
  call assert_equal(-1, strridx('hello', 'hello world'))
  call assert_fails("let n=strridx('hello', [])", 'E730:')
  call assert_fails("let n=strridx([], 'l')", 'E730:')
endfunc

func Test_match_func()
  call assert_equal(4,  match('testing', 'ing'))
  call assert_equal(4,  'testing'->match('ing', 2))
  call assert_equal(-1, match('testing', 'ing', 5))
  call assert_equal(-1, match('testing', 'ing', 8))
  call assert_equal(1, match(['vim', 'testing', 'execute'], 'ing'))
  call assert_equal(-1, match(['vim', 'testing', 'execute'], 'img'))
  call assert_fails("let x=match('vim', [])", 'E730:')
  call assert_equal(3, match(['a', 'b', 'c', 'a'], 'a', 1))
  call assert_equal(-1, match(['a', 'b', 'c', 'a'], 'a', 5))
  call assert_equal(4,  match('testing', 'ing', -1))
  call assert_fails("let x=match('testing', 'ing', 0, [])", 'E745:')
  call assert_equal(-1, match(test_null_list(), 2))
  call assert_equal(-1, match('abc', '\\%('))
endfunc

func Test_matchend()
  call assert_equal(7,  matchend('testing', 'ing'))
  call assert_equal(7,  'testing'->matchend('ing', 2))
  call assert_equal(-1, matchend('testing', 'ing', 5))
  call assert_equal(-1, matchend('testing', 'ing', 8))
  call assert_equal(match(['vim', 'testing', 'execute'], 'ing'), matchend(['vim', 'testing', 'execute'], 'ing'))
  call assert_equal(match(['vim', 'testing', 'execute'], 'img'), matchend(['vim', 'testing', 'execute'], 'img'))
endfunc

func Test_matchlist()
  call assert_equal(['acd', 'a', '', 'c', 'd', '', '', '', '', ''],  matchlist('acd', '\(a\)\?\(b\)\?\(c\)\?\(.*\)'))
  call assert_equal(['d', '', '', '', 'd', '', '', '', '', ''],  'acd'->matchlist('\(a\)\?\(b\)\?\(c\)\?\(.*\)', 2))
  call assert_equal([],  matchlist('acd', '\(a\)\?\(b\)\?\(c\)\?\(.*\)', 4))
endfunc

func Test_matchstr()
  call assert_equal('ing',  matchstr('testing', 'ing'))
  call assert_equal('ing',  'testing'->matchstr('ing', 2))
  call assert_equal('', matchstr('testing', 'ing', 5))
  call assert_equal('', matchstr('testing', 'ing', 8))
  call assert_equal('testing', matchstr(['vim', 'testing', 'execute'], 'ing'))
  call assert_equal('', matchstr(['vim', 'testing', 'execute'], 'img'))
endfunc

func Test_matchstrpos()
  call assert_equal(['ing', 4, 7], matchstrpos('testing', 'ing'))
  call assert_equal(['ing', 4, 7], 'testing'->matchstrpos('ing', 2))
  call assert_equal(['', -1, -1], matchstrpos('testing', 'ing', 5))
  call assert_equal(['', -1, -1], matchstrpos('testing', 'ing', 8))
  call assert_equal(['ing', 1, 4, 7], matchstrpos(['vim', 'testing', 'execute'], 'ing'))
  call assert_equal(['', -1, -1, -1], matchstrpos(['vim', 'testing', 'execute'], 'img'))
  call assert_equal(['', -1, -1], matchstrpos(test_null_list(), '\a'))
endfunc

func Test_nextnonblank_prevnonblank()
  new
insert
This


is

a
Test
.
  call assert_equal(0, nextnonblank(-1))
  call assert_equal(0, nextnonblank(0))
  call assert_equal(1, nextnonblank(1))
  call assert_equal(4, 2->nextnonblank())
  call assert_equal(4, nextnonblank(3))
  call assert_equal(4, nextnonblank(4))
  call assert_equal(6, nextnonblank(5))
  call assert_equal(6, nextnonblank(6))
  call assert_equal(7, nextnonblank(7))
  call assert_equal(0, 8->nextnonblank())

  call assert_equal(0, prevnonblank(-1))
  call assert_equal(0, prevnonblank(0))
  call assert_equal(1, 1->prevnonblank())
  call assert_equal(1, prevnonblank(2))
  call assert_equal(1, prevnonblank(3))
  call assert_equal(4, prevnonblank(4))
  call assert_equal(4, 5->prevnonblank())
  call assert_equal(6, prevnonblank(6))
  call assert_equal(7, prevnonblank(7))
  call assert_equal(0, prevnonblank(8))
  bw!
endfunc

func Test_byte2line_line2byte()
  new
  set endofline
  call setline(1, ['a', 'bc', 'd'])

  set fileformat=unix
  call assert_equal([-1, -1, 1, 1, 2, 2, 2, 3, 3, -1],
  \                 map(range(-1, 8), 'byte2line(v:val)'))
  call assert_equal([-1, -1, 1, 3, 6, 8, -1],
  \                 map(range(-1, 5), 'line2byte(v:val)'))

  set fileformat=mac
  call assert_equal([-1, -1, 1, 1, 2, 2, 2, 3, 3, -1],
  \                 map(range(-1, 8), 'v:val->byte2line()'))
  call assert_equal([-1, -1, 1, 3, 6, 8, -1],
  \                 map(range(-1, 5), 'v:val->line2byte()'))

  set fileformat=dos
  call assert_equal([-1, -1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, -1],
  \                 map(range(-1, 11), 'byte2line(v:val)'))
  call assert_equal([-1, -1, 1, 4, 8, 11, -1],
  \                 map(range(-1, 5), 'line2byte(v:val)'))

  bw!
  set noendofline nofixendofline
  normal a-
  for ff in ["unix", "mac", "dos"]
    let &fileformat = ff
    call assert_equal(1, line2byte(1))
    call assert_equal(2, line2byte(2))  " line2byte(line("$") + 1) is the buffer size plus one (as per :help line2byte).
  endfor

  set endofline& fixendofline& fileformat&
  bw!
endfunc

" Test for byteidx() using a character index
func Test_byteidx()
  let a = '.é.' " one char of two bytes
  call assert_equal(0, byteidx(a, 0))
  call assert_equal(1, byteidx(a, 1))
  call assert_equal(3, byteidx(a, 2))
  call assert_equal(4, byteidx(a, 3))
  call assert_equal(-1, byteidx(a, 4))

  let b = '.é.' " normal e with composing char
  call assert_equal(0, b->byteidx(0))
  call assert_equal(1, b->byteidx(1))
  call assert_equal(4, b->byteidx(2))
  call assert_equal(5, b->byteidx(3))
  call assert_equal(-1, b->byteidx(4))

  " string with multiple composing characters
  let str = '-ą́-ą́'
  call assert_equal(0, byteidx(str, 0))
  call assert_equal(1, byteidx(str, 1))
  call assert_equal(6, byteidx(str, 2))
  call assert_equal(7, byteidx(str, 3))
  call assert_equal(12, byteidx(str, 4))
  call assert_equal(-1, byteidx(str, 5))

  " empty string
  call assert_equal(0, byteidx('', 0))
  call assert_equal(-1, byteidx('', 1))

  " error cases
  call assert_fails("call byteidx([], 0)", 'E730:')
  call assert_fails("call byteidx('abc', [])", 'E745:')
  call assert_fails("call byteidx('abc', 0, {})", ['E728:', 'E728:'])
  call assert_fails("call byteidx('abc', 0, -1)", ['E1023:', 'E1023:'])
endfunc

" Test for byteidxcomp() using a character index
func Test_byteidxcomp()
  let a = '.é.' " one char of two bytes
  call assert_equal(0, byteidxcomp(a, 0))
  call assert_equal(1, byteidxcomp(a, 1))
  call assert_equal(3, byteidxcomp(a, 2))
  call assert_equal(4, byteidxcomp(a, 3))
  call assert_equal(-1, byteidxcomp(a, 4))

  let b = '.é.' " normal e with composing char
  call assert_equal(0, b->byteidxcomp(0))
  call assert_equal(1, b->byteidxcomp(1))
  call assert_equal(2, b->byteidxcomp(2))
  call assert_equal(4, b->byteidxcomp(3))
  call assert_equal(5, b->byteidxcomp(4))
  call assert_equal(-1, b->byteidxcomp(5))

  " string with multiple composing characters
  let str = '-ą́-ą́'
  call assert_equal(0, byteidxcomp(str, 0))
  call assert_equal(1, byteidxcomp(str, 1))
  call assert_equal(2, byteidxcomp(str, 2))
  call assert_equal(4, byteidxcomp(str, 3))
  call assert_equal(6, byteidxcomp(str, 4))
  call assert_equal(7, byteidxcomp(str, 5))
  call assert_equal(8, byteidxcomp(str, 6))
  call assert_equal(10, byteidxcomp(str, 7))
  call assert_equal(12, byteidxcomp(str, 8))
  call assert_equal(-1, byteidxcomp(str, 9))

  " empty string
  call assert_equal(0, byteidxcomp('', 0))
  call assert_equal(-1, byteidxcomp('', 1))

  " error cases
  call assert_fails("call byteidxcomp([], 0)", 'E730:')
  call assert_fails("call byteidxcomp('abc', [])", 'E745:')
  call assert_fails("call byteidxcomp('abc', 0, {})", ['E728:', 'E728:'])
  call assert_fails("call byteidxcomp('abc', 0, -1)", ['E1023:', 'E1023:'])
endfunc

" Test for byteidx() using a UTF-16 index
func Test_byteidx_from_utf16_index()
  " string with single byte characters
  let str = "abc"
  for i in range(3)
    call assert_equal(i, byteidx(str, i, v:true))
  endfor
  call assert_equal(3, byteidx(str, 3, v:true))
  call assert_equal(-1, byteidx(str, 4, v:true))

  " string with two byte characters
  let str = "a©©b"
  call assert_equal(0, byteidx(str, 0, v:true))
  call assert_equal(1, byteidx(str, 1, v:true))
  call assert_equal(3, byteidx(str, 2, v:true))
  call assert_equal(5, byteidx(str, 3, v:true))
  call assert_equal(6, byteidx(str, 4, v:true))
  call assert_equal(-1, byteidx(str, 5, v:true))

  " string with two byte characters
  let str = "a😊😊b"
  call assert_equal(0, byteidx(str, 0, v:true))
  call assert_equal(1, byteidx(str, 1, v:true))
  call assert_equal(1, byteidx(str, 2, v:true))
  call assert_equal(5, byteidx(str, 3, v:true))
  call assert_equal(5, byteidx(str, 4, v:true))
  call assert_equal(9, byteidx(str, 5, v:true))
  call assert_equal(10, byteidx(str, 6, v:true))
  call assert_equal(-1, byteidx(str, 7, v:true))

  " string with composing characters
  let str = '-á-b́'
  call assert_equal(0, byteidx(str, 0, v:true))
  call assert_equal(1, byteidx(str, 1, v:true))
  call assert_equal(4, byteidx(str, 2, v:true))
  call assert_equal(5, byteidx(str, 3, v:true))
  call assert_equal(8, byteidx(str, 4, v:true))
  call assert_equal(-1, byteidx(str, 5, v:true))

  " string with multiple composing characters
  let str = '-ą́-ą́'
  call assert_equal(0, byteidx(str, 0, v:true))
  call assert_equal(1, byteidx(str, 1, v:true))
  call assert_equal(6, byteidx(str, 2, v:true))
  call assert_equal(7, byteidx(str, 3, v:true))
  call assert_equal(12, byteidx(str, 4, v:true))
  call assert_equal(-1, byteidx(str, 5, v:true))

  " empty string
  call assert_equal(0, byteidx('', 0, v:true))
  call assert_equal(-1, byteidx('', 1, v:true))

  " error cases
  call assert_fails('call byteidx(str, 0, [])', 'E745:')
endfunc

" Test for byteidxcomp() using a UTF-16 index
func Test_byteidxcomp_from_utf16_index()
  " string with single byte characters
  let str = "abc"
  for i in range(3)
    call assert_equal(i, byteidxcomp(str, i, v:true))
  endfor
  call assert_equal(3, byteidxcomp(str, 3, v:true))
  call assert_equal(-1, byteidxcomp(str, 4, v:true))

  " string with two byte characters
  let str = "a©©b"
  call assert_equal(0, byteidxcomp(str, 0, v:true))
  call assert_equal(1, byteidxcomp(str, 1, v:true))
  call assert_equal(3, byteidxcomp(str, 2, v:true))
  call assert_equal(5, byteidxcomp(str, 3, v:true))
  call assert_equal(6, byteidxcomp(str, 4, v:true))
  call assert_equal(-1, byteidxcomp(str, 5, v:true))

  " string with two byte characters
  let str = "a😊😊b"
  call assert_equal(0, byteidxcomp(str, 0, v:true))
  call assert_equal(1, byteidxcomp(str, 1, v:true))
  call assert_equal(1, byteidxcomp(str, 2, v:true))
  call assert_equal(5, byteidxcomp(str, 3, v:true))
  call assert_equal(5, byteidxcomp(str, 4, v:true))
  call assert_equal(9, byteidxcomp(str, 5, v:true))
  call assert_equal(10, byteidxcomp(str, 6, v:true))
  call assert_equal(-1, byteidxcomp(str, 7, v:true))

  " string with composing characters
  let str = '-á-b́'
  call assert_equal(0, byteidxcomp(str, 0, v:true))
  call assert_equal(1, byteidxcomp(str, 1, v:true))
  call assert_equal(2, byteidxcomp(str, 2, v:true))
  call assert_equal(4, byteidxcomp(str, 3, v:true))
  call assert_equal(5, byteidxcomp(str, 4, v:true))
  call assert_equal(6, byteidxcomp(str, 5, v:true))
  call assert_equal(8, byteidxcomp(str, 6, v:true))
  call assert_equal(-1, byteidxcomp(str, 7, v:true))
  call assert_fails('call byteidxcomp(str, 0, [])', 'E745:')

  " string with multiple composing characters
  let str = '-ą́-ą́'
  call assert_equal(0, byteidxcomp(str, 0, v:true))
  call assert_equal(1, byteidxcomp(str, 1, v:true))
  call assert_equal(2, byteidxcomp(str, 2, v:true))
  call assert_equal(4, byteidxcomp(str, 3, v:true))
  call assert_equal(6, byteidxcomp(str, 4, v:true))
  call assert_equal(7, byteidxcomp(str, 5, v:true))
  call assert_equal(8, byteidxcomp(str, 6, v:true))
  call assert_equal(10, byteidxcomp(str, 7, v:true))
  call assert_equal(12, byteidxcomp(str, 8, v:true))
  call assert_equal(-1, byteidxcomp(str, 9, v:true))

  " empty string
  call assert_equal(0, byteidxcomp('', 0, v:true))
  call assert_equal(-1, byteidxcomp('', 1, v:true))

  " error cases
  call assert_fails('call byteidxcomp(str, 0, [])', 'E745:')
endfunc

" Test for charidx() using a byte index
func Test_charidx()
  let a = 'xáb́y'
  call assert_equal(0, charidx(a, 0))
  call assert_equal(1, charidx(a, 3))
  call assert_equal(2, charidx(a, 4))
  call assert_equal(3, charidx(a, 7))
  call assert_equal(4, charidx(a, 8))
  call assert_equal(-1, charidx(a, 9))
  call assert_equal(-1, charidx(a, -1))

  " count composing characters
  call assert_equal(0, a->charidx(0, 1))
  call assert_equal(2, a->charidx(2, 1))
  call assert_equal(3, a->charidx(4, 1))
  call assert_equal(5, a->charidx(7, 1))
  call assert_equal(6, a->charidx(8, 1))
  call assert_equal(-1, a->charidx(9, 1))

  " empty string
  call assert_equal(0, charidx('', 0))
  call assert_equal(-1, charidx('', 1))
  call assert_equal(0, charidx('', 0, 1))
  call assert_equal(-1, charidx('', 1, 1))

  " error cases
  call assert_equal(0, charidx(test_null_string(), 0))
  call assert_equal(-1, charidx(test_null_string(), 1))
  call assert_fails('let x = charidx([], 1)', 'E1174:')
  call assert_fails('let x = charidx("abc", [])', 'E1210:')
  call assert_fails('let x = charidx("abc", 1, [])', 'E1212:')
  call assert_fails('let x = charidx("abc", 1, -1)', 'E1212:')
  call assert_fails('let x = charidx("abc", 1, 2)', 'E1212:')
endfunc

" Test for charidx() using a UTF-16 index
func Test_charidx_from_utf16_index()
  " string with single byte characters
  let str = "abc"
  for i in range(4)
    call assert_equal(i, charidx(str, i, v:false, v:true))
  endfor
  call assert_equal(-1, charidx(str, 4, v:false, v:true))

  " string with two byte characters
  let str = "a©©b"
  call assert_equal(0, charidx(str, 0, v:false, v:true))
  call assert_equal(1, charidx(str, 1, v:false, v:true))
  call assert_equal(2, charidx(str, 2, v:false, v:true))
  call assert_equal(3, charidx(str, 3, v:false, v:true))
  call assert_equal(4, charidx(str, 4, v:false, v:true))
  call assert_equal(-1, charidx(str, 5, v:false, v:true))

  " string with four byte characters
  let str = "a😊😊b"
  call assert_equal(0, charidx(str, 0, v:false, v:true))
  call assert_equal(1, charidx(str, 1, v:false, v:true))
  call assert_equal(1, charidx(str, 2, v:false, v:true))
  call assert_equal(2, charidx(str, 3, v:false, v:true))
  call assert_equal(2, charidx(str, 4, v:false, v:true))
  call assert_equal(3, charidx(str, 5, v:false, v:true))
  call assert_equal(4, charidx(str, 6, v:false, v:true))
  call assert_equal(-1, charidx(str, 7, v:false, v:true))

  " string with composing characters
  let str = '-á-b́'
  for i in str->strcharlen()->range()
    call assert_equal(i, charidx(str, i, v:false, v:true))
  endfor
  call assert_equal(4, charidx(str, 4, v:false, v:true))
  call assert_equal(-1, charidx(str, 5, v:false, v:true))
  for i in str->strchars()->range()
    call assert_equal(i, charidx(str, i, v:true, v:true))
  endfor
  call assert_equal(6, charidx(str, 6, v:true, v:true))
  call assert_equal(-1, charidx(str, 7, v:true, v:true))

  " string with multiple composing characters
  let str = '-ą́-ą́'
  for i in str->strcharlen()->range()
    call assert_equal(i, charidx(str, i, v:false, v:true))
  endfor
  call assert_equal(4, charidx(str, 4, v:false, v:true))
  call assert_equal(-1, charidx(str, 5, v:false, v:true))
  for i in str->strchars()->range()
    call assert_equal(i, charidx(str, i, v:true, v:true))
  endfor
  call assert_equal(8, charidx(str, 8, v:true, v:true))
  call assert_equal(-1, charidx(str, 9, v:true, v:true))

  " empty string
  call assert_equal(0, charidx('', 0, v:false, v:true))
  call assert_equal(-1, charidx('', 1, v:false, v:true))
  call assert_equal(0, charidx('', 0, v:true, v:true))
  call assert_equal(-1, charidx('', 1, v:true, v:true))

  " error cases
  call assert_equal(0, charidx('', 0, v:false, v:true))
  call assert_equal(-1, charidx('', 1, v:false, v:true))
  call assert_equal(0, charidx('', 0, v:true, v:true))
  call assert_equal(-1, charidx('', 1, v:true, v:true))
  call assert_equal(0, charidx(test_null_string(), 0, v:false, v:true))
  call assert_equal(-1, charidx(test_null_string(), 1, v:false, v:true))
  call assert_fails('let x = charidx("abc", 1, v:false, [])', 'E1212:')
  call assert_fails('let x = charidx("abc", 1, v:true, [])', 'E1212:')
endfunc

" Test for utf16idx() using a byte index
func Test_utf16idx_from_byteidx()
  " UTF-16 index of a string with single byte characters
  let str = "abc"
  for i in range(4)
    call assert_equal(i, utf16idx(str, i))
  endfor
  call assert_equal(-1, utf16idx(str, 4))

  " UTF-16 index of a string with two byte characters
  let str = 'a©©b'
  call assert_equal(0, str->utf16idx(0))
  call assert_equal(1, str->utf16idx(1))
  call assert_equal(1, str->utf16idx(2))
  call assert_equal(2, str->utf16idx(3))
  call assert_equal(2, str->utf16idx(4))
  call assert_equal(3, str->utf16idx(5))
  call assert_equal(4, str->utf16idx(6))
  call assert_equal(-1, str->utf16idx(7))

  " UTF-16 index of a string with four byte characters
  let str = 'a😊😊b'
  call assert_equal(0, utf16idx(str, 0))
  call assert_equal(1, utf16idx(str, 1))
  call assert_equal(1, utf16idx(str, 2))
  call assert_equal(1, utf16idx(str, 3))
  call assert_equal(1, utf16idx(str, 4))
  call assert_equal(3, utf16idx(str, 5))
  call assert_equal(3, utf16idx(str, 6))
  call assert_equal(3, utf16idx(str, 7))
  call assert_equal(3, utf16idx(str, 8))
  call assert_equal(5, utf16idx(str, 9))
  call assert_equal(6, utf16idx(str, 10))
  call assert_equal(-1, utf16idx(str, 11))

  " UTF-16 index of a string with composing characters
  let str = '-á-b́'
  call assert_equal(0, utf16idx(str, 0))
  call assert_equal(1, utf16idx(str, 1))
  call assert_equal(1, utf16idx(str, 2))
  call assert_equal(1, utf16idx(str, 3))
  call assert_equal(2, utf16idx(str, 4))
  call assert_equal(3, utf16idx(str, 5))
  call assert_equal(3, utf16idx(str, 6))
  call assert_equal(3, utf16idx(str, 7))
  call assert_equal(4, utf16idx(str, 8))
  call assert_equal(-1, utf16idx(str, 9))
  call assert_equal(0, utf16idx(str, 0, v:true))
  call assert_equal(1, utf16idx(str, 1, v:true))
  call assert_equal(2, utf16idx(str, 2, v:true))
  call assert_equal(2, utf16idx(str, 3, v:true))
  call assert_equal(3, utf16idx(str, 4, v:true))
  call assert_equal(4, utf16idx(str, 5, v:true))
  call assert_equal(5, utf16idx(str, 6, v:true))
  call assert_equal(5, utf16idx(str, 7, v:true))
  call assert_equal(6, utf16idx(str, 8, v:true))
  call assert_equal(-1, utf16idx(str, 9, v:true))

  " string with multiple composing characters
  let str = '-ą́-ą́'
  call assert_equal(0, utf16idx(str, 0))
  call assert_equal(1, utf16idx(str, 1))
  call assert_equal(1, utf16idx(str, 2))
  call assert_equal(1, utf16idx(str, 3))
  call assert_equal(1, utf16idx(str, 4))
  call assert_equal(1, utf16idx(str, 5))
  call assert_equal(2, utf16idx(str, 6))
  call assert_equal(3, utf16idx(str, 7))
  call assert_equal(3, utf16idx(str, 8))
  call assert_equal(3, utf16idx(str, 9))
  call assert_equal(3, utf16idx(str, 10))
  call assert_equal(3, utf16idx(str, 11))
  call assert_equal(4, utf16idx(str, 12))
  call assert_equal(-1, utf16idx(str, 13))
  call assert_equal(0, utf16idx(str, 0, v:true))
  call assert_equal(1, utf16idx(str, 1, v:true))
  call assert_equal(2, utf16idx(str, 2, v:true))
  call assert_equal(2, utf16idx(str, 3, v:true))
  call assert_equal(3, utf16idx(str, 4, v:true))
  call assert_equal(3, utf16idx(str, 5, v:true))
  call assert_equal(4, utf16idx(str, 6, v:true))
  call assert_equal(5, utf16idx(str, 7, v:true))
  call assert_equal(6, utf16idx(str, 8, v:true))
  call assert_equal(6, utf16idx(str, 9, v:true))
  call assert_equal(7, utf16idx(str, 10, v:true))
  call assert_equal(7, utf16idx(str, 11, v:true))
  call assert_equal(8, utf16idx(str, 12, v:true))
  call assert_equal(-1, utf16idx(str, 13, v:true))

  " empty string
  call assert_equal(0, utf16idx('', 0))
  call assert_equal(-1, utf16idx('', 1))
  call assert_equal(0, utf16idx('', 0, v:true))
  call assert_equal(-1, utf16idx('', 1, v:true))

  " error cases
  call assert_equal(0, utf16idx("", 0))
  call assert_equal(-1, utf16idx("", 1))
  call assert_equal(-1, utf16idx("abc", -1))
  call assert_equal(0, utf16idx(test_null_string(), 0))
  call assert_equal(-1, utf16idx(test_null_string(), 1))
  call assert_fails('let l = utf16idx([], 0)', 'E1174:')
  call assert_fails('let l = utf16idx("ab", [])', 'E1210:')
  call assert_fails('let l = utf16idx("ab", 0, [])', 'E1212:')
endfunc

" Test for utf16idx() using a character index
func Test_utf16idx_from_charidx()
  let str = "abc"
  for i in str->strcharlen()->range()
    call assert_equal(i, utf16idx(str, i, v:false, v:true))
  endfor
  call assert_equal(3, utf16idx(str, 3, v:false, v:true))
  call assert_equal(-1, utf16idx(str, 4, v:false, v:true))

  " UTF-16 index of a string with two byte characters
  let str = "a©©b"
  for i in str->strcharlen()->range()
    call assert_equal(i, utf16idx(str, i, v:false, v:true))
  endfor
  call assert_equal(4, utf16idx(str, 4, v:false, v:true))
  call assert_equal(-1, utf16idx(str, 5, v:false, v:true))

  " UTF-16 index of a string with four byte characters
  let str = "a😊😊b"
  call assert_equal(0, utf16idx(str, 0, v:false, v:true))
  call assert_equal(1, utf16idx(str, 1, v:false, v:true))
  call assert_equal(3, utf16idx(str, 2, v:false, v:true))
  call assert_equal(5, utf16idx(str, 3, v:false, v:true))
  call assert_equal(6, utf16idx(str, 4, v:false, v:true))
  call assert_equal(-1, utf16idx(str, 5, v:false, v:true))

  " UTF-16 index of a string with composing characters
  let str = '-á-b́'
  for i in str->strcharlen()->range()
    call assert_equal(i, utf16idx(str, i, v:false, v:true))
  endfor
  call assert_equal(4, utf16idx(str, 4, v:false, v:true))
  call assert_equal(-1, utf16idx(str, 5, v:false, v:true))
  for i in str->strchars()->range()
    call assert_equal(i, utf16idx(str, i, v:true, v:true))
  endfor
  call assert_equal(6, utf16idx(str, 6, v:true, v:true))
  call assert_equal(-1, utf16idx(str, 7, v:true, v:true))

  " string with multiple composing characters
  let str = '-ą́-ą́'
  for i in str->strcharlen()->range()
    call assert_equal(i, utf16idx(str, i, v:false, v:true))
  endfor
  call assert_equal(4, utf16idx(str, 4, v:false, v:true))
  call assert_equal(-1, utf16idx(str, 5, v:false, v:true))
  for i in str->strchars()->range()
    call assert_equal(i, utf16idx(str, i, v:true, v:true))
  endfor
  call assert_equal(8, utf16idx(str, 8, v:true, v:true))
  call assert_equal(-1, utf16idx(str, 9, v:true, v:true))

  " empty string
  call assert_equal(0, utf16idx('', 0, v:false, v:true))
  call assert_equal(-1, utf16idx('', 1, v:false, v:true))
  call assert_equal(0, utf16idx('', 0, v:true, v:true))
  call assert_equal(-1, utf16idx('', 1, v:true, v:true))

  " error cases
  call assert_equal(0, utf16idx(test_null_string(), 0, v:true, v:true))
  call assert_equal(-1, utf16idx(test_null_string(), 1, v:true, v:true))
  call assert_fails('let l = utf16idx("ab", 0, v:false, [])', 'E1212:')
endfunc

" Test for strutf16len()
func Test_strutf16len()
  call assert_equal(3, strutf16len('abc'))
  call assert_equal(3, 'abc'->strutf16len(v:true))
  call assert_equal(4, strutf16len('a©©b'))
  call assert_equal(4, strutf16len('a©©b', v:true))
  call assert_equal(6, strutf16len('a😊😊b'))
  call assert_equal(6, strutf16len('a😊😊b', v:true))
  call assert_equal(4, strutf16len('-á-b́'))
  call assert_equal(6, strutf16len('-á-b́', v:true))
  call assert_equal(4, strutf16len('-ą́-ą́'))
  call assert_equal(8, strutf16len('-ą́-ą́', v:true))
  call assert_equal(0, strutf16len(''))

  " error cases
  call assert_fails('let l = strutf16len([])', 'E1174:')
  call assert_fails('let l = strutf16len("a", [])', 'E1212:')
  call assert_equal(0, strutf16len(test_null_string()))
endfunc

func Test_count()
  let l = ['a', 'a', 'A', 'b']
  call assert_equal(2, count(l, 'a'))
  call assert_equal(1, count(l, 'A'))
  call assert_equal(1, count(l, 'b'))
  call assert_equal(0, count(l, 'B'))

  call assert_equal(2, count(l, 'a', 0))
  call assert_equal(1, count(l, 'A', 0))
  call assert_equal(1, count(l, 'b', 0))
  call assert_equal(0, count(l, 'B', 0))

  call assert_equal(3, count(l, 'a', 1))
  call assert_equal(3, count(l, 'A', 1))
  call assert_equal(1, count(l, 'b', 1))
  call assert_equal(1, count(l, 'B', 1))
  call assert_equal(0, count(l, 'c', 1))

  call assert_equal(1, count(l, 'a', 0, 1))
  call assert_equal(2, count(l, 'a', 1, 1))
  call assert_fails('call count(l, "a", 0, 10)', 'E684:')
  call assert_fails('call count(l, "a", [])', 'E745:')

  let d = {1: 'a', 2: 'a', 3: 'A', 4: 'b'}
  call assert_equal(2, count(d, 'a'))
  call assert_equal(1, count(d, 'A'))
  call assert_equal(1, count(d, 'b'))
  call assert_equal(0, count(d, 'B'))

  call assert_equal(2, count(d, 'a', 0))
  call assert_equal(1, count(d, 'A', 0))
  call assert_equal(1, count(d, 'b', 0))
  call assert_equal(0, count(d, 'B', 0))

  call assert_equal(3, count(d, 'a', 1))
  call assert_equal(3, count(d, 'A', 1))
  call assert_equal(1, count(d, 'b', 1))
  call assert_equal(1, count(d, 'B', 1))
  call assert_equal(0, count(d, 'c', 1))

  call assert_fails('call count(d, "a", 0, 1)', 'E474:')

  call assert_equal(0, count("foo", "bar"))
  call assert_equal(1, count("foo", "oo"))
  call assert_equal(2, count("foo", "o"))
  call assert_equal(0, count("foo", "O"))
  call assert_equal(2, count("foo", "O", 1))
  call assert_equal(2, count("fooooo", "oo"))
  call assert_equal(0, count("foo", ""))

  call assert_fails('call count(0, 0)', 'E706:')
  call assert_fails('call count("", "", {})', ['E728:', 'E728:'])
endfunc

func Test_changenr()
  new Xchangenr
  call assert_equal(0, changenr())
  norm ifoo
  call assert_equal(1, changenr())
  set undolevels=10
  norm Sbar
  call assert_equal(2, changenr())
  undo
  call assert_equal(1, changenr())
  redo
  call assert_equal(2, changenr())
  bw!
  set undolevels&
endfunc

func Test_filewritable()
  new Xfilewritable
  write!
  call assert_equal(1, filewritable('Xfilewritable'))

  call assert_notequal(0, setfperm('Xfilewritable', 'r--r-----'))
  call assert_equal(0, filewritable('Xfilewritable'))

  call assert_notequal(0, setfperm('Xfilewritable', 'rw-r-----'))
  call assert_equal(1, 'Xfilewritable'->filewritable())

  call assert_equal(0, filewritable('doesnotexist'))

  call mkdir('Xwritedir', 'D')
  call assert_equal(2, filewritable('Xwritedir'))

  call delete('Xfilewritable')
  bw!
endfunc

func Test_Executable()
  if has('win32')
    call assert_equal(1, executable('notepad'))
    call assert_equal(1, 'notepad.exe'->executable())
    call assert_equal(0, executable('notepad.exe.exe'))
    call assert_equal(0, executable('shell32.dll'))
    call assert_equal(0, executable('win.ini'))

    " get "notepad" path and remove the leading drive and sep. (ex. 'C:\')
    let notepadcmd = exepath('notepad.exe')
    let driveroot = notepadcmd[:2]
    let notepadcmd = notepadcmd[3:]
    new
    " check that the relative path works in /
    execute 'lcd' driveroot
    call assert_equal(1, executable(notepadcmd))
    call assert_equal(driveroot .. notepadcmd, notepadcmd->exepath())
    bwipe

    " create "notepad.bat"
    call mkdir('Xnotedir')
    let notepadbat = fnamemodify('Xnotedir/notepad.bat', ':p')
    call writefile([], notepadbat)
    new
    " check that the path and the pathext order is valid
    lcd Xnotedir
    let [pathext, $PATHEXT] = [$PATHEXT, '.com;.exe;.bat;.cmd']
    call assert_equal(notepadbat, exepath('notepad'))
    let $PATHEXT = pathext
    " check for symbolic link
    execute 'silent !mklink np.bat "' .. notepadbat .. '"'
    call assert_equal(1, executable('./np.bat'))
    call assert_equal(1, executable('./np'))
    bwipe
    eval 'Xnotedir'->delete('rf')
  elseif has('unix')
    call assert_equal(1, 'cat'->executable())
    call assert_equal(0, executable('nodogshere'))

    " get "cat" path and remove the leading /
    let catcmd = exepath('cat')[1:]
    new
    " check that the relative path works in /
    lcd /
    call assert_equal(1, executable(catcmd))
    let result = catcmd->exepath()
    " when using chroot looking for sbin/cat can return bin/cat, that is OK
    if catcmd =~ '\<sbin\>' && result =~ '\<bin\>'
      call assert_equal('/' .. substitute(catcmd, '\<sbin\>', 'bin', ''), result)
    else
      " /bin/cat and /usr/bin/cat may be hard linked, we could get either
      let result = substitute(result, '/usr/bin/cat', '/bin/cat', '')
      let catcmd = substitute(catcmd, 'usr/bin/cat', 'bin/cat', '')
      call assert_equal('/' .. catcmd, result)
    endif
    bwipe
  else
    throw 'Skipped: does not work on this platform'
  endif
endfunc

func Test_executable_windows_store_apps()
  CheckMSWindows

  " Windows Store apps install some 'decoy' .exe that require some careful
  " handling as they behave similarly to symlinks.
  let app_dir = expand("$LOCALAPPDATA\\Microsoft\\WindowsApps")
  if !isdirectory(app_dir)
    return
  endif

  let save_path = $PATH
  let $PATH = app_dir
  " Ensure executable() finds all the app .exes
  for entry in readdir(app_dir)
    if entry =~ '\.exe$'
      call assert_true(executable(entry))
    endif
  endfor

  let $PATH = save_path
endfunc

func Test_executable_longname()
  CheckMSWindows

  " Create a temporary .bat file with 205 characters in the name.
  " Maximum length of a filename (including the path) on MS-Windows is 259
  " characters.
  " See https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation
  let len = 259 - getcwd()->len() - 6
  if len > 200
    let len = 200
  endif

  let fname = 'X' . repeat('あ', len) . '.bat'
  call writefile([], fname)
  call assert_equal(1, executable(fname))
  call delete(fname)
endfunc

func Test_hostname()
  let hostname_vim = hostname()
  if has('unix')
    let hostname_system = systemlist('uname -n')[0]
    call assert_equal(hostname_vim, hostname_system)
  endif
endfunc

func Test_getpid()
  " getpid() always returns the same value within a vim instance.
  call assert_equal(getpid(), getpid())
  if has('unix')
    call assert_equal(systemlist('echo $PPID')[0], string(getpid()))
  endif
endfunc

func Test_hlexists()
  call assert_equal(0, hlexists('does_not_exist'))
  call assert_equal(0, 'Number'->hlexists())
  call assert_equal(0, highlight_exists('does_not_exist'))
  call assert_equal(0, highlight_exists('Number'))
  syntax on
  call assert_equal(0, hlexists('does_not_exist'))
  call assert_equal(1, hlexists('Number'))
  call assert_equal(0, highlight_exists('does_not_exist'))
  call assert_equal(1, highlight_exists('Number'))
  syntax off
endfunc

" Test for the col() function
func Test_col()
  new
  call setline(1, 'abcdef')
  norm gg4|mx6|mY2|
  call assert_equal(2, col('.'))
  call assert_equal(7, col('$'))
  call assert_equal(2, col('v'))
  call assert_equal(4, col("'x"))
  call assert_equal(6, col("'Y"))
  call assert_equal(2, [1, 2]->col())
  call assert_equal(7, col([1, '$']))

  call assert_equal(0, col(''))
  call assert_equal(0, col('x'))
  call assert_equal(0, col([2, '$']))
  call assert_equal(0, col([1, 100]))
  call assert_equal(0, col([1]))
  call assert_equal(0, col(test_null_list()))
  call assert_fails('let c = col({})', 'E1222:')
  call assert_fails('let c = col(".", [])', 'E1210:')

  " test for getting the visual start column
  func T()
    let g:Vcol = col('v')
    return ''
  endfunc
  let g:Vcol = 0
  xmap <expr> <F2> T()
  exe "normal gg3|ve\<F2>"
  call assert_equal(3, g:Vcol)
  xunmap <F2>
  delfunc T

  " Test for the visual line start and end marks '< and '>
  call setline(1, ['one', 'one two', 'one two three'])
  "normal! ggVG
  call feedkeys("ggVG\<Esc>", 'xt')
  call assert_equal(1, col("'<"))
  call assert_equal(14, col("'>"))
  " Delete the last line of the visually selected region
  $d
  call assert_notequal(14, col("'>"))

  " Test with 'virtualedit'
  set virtualedit=all
  call cursor(1, 10)
  call assert_equal(4, col('.'))
  set virtualedit&

  " Test for getting the column number in another window
  let winid = win_getid()
  new
  call win_execute(winid, 'normal 1G$')
  call assert_equal(3, col('.', winid))
  call win_execute(winid, 'normal 2G')
  call assert_equal(8, col('$', winid))
  call assert_equal(0, col('.', 5001))

  bw!
endfunc

" Test for input()
func Test_input_func()
  " Test for prompt with multiple lines
  redir => v
  call feedkeys(":let c = input(\"A\\nB\\nC\\n? \")\<CR>B\<CR>", 'xt')
  redir END
  call assert_equal("B", c)
  call assert_equal(['A', 'B', 'C'], split(v, "\n"))

  " Test for default value
  call feedkeys(":let c = input('color? ', 'red')\<CR>\<CR>", 'xt')
  call assert_equal('red', c)

  " Test for completion at the input prompt
  func! Tcomplete(arglead, cmdline, pos)
    return "item1\nitem2\nitem3"
  endfunc
  call feedkeys(":let c = input('Q? ', '', 'custom,Tcomplete')\<CR>"
        \ .. "\<C-A>\<CR>", 'xt')
  delfunc Tcomplete
  call assert_equal('item1 item2 item3', c)

  " Test for using special characters as default input
  call feedkeys(":let c = input('name? ', \"x\\<BS>y\")\<CR>\<CR>", 'xt')
  call assert_equal('y', c)

  " Test for using text with composing characters as default input
  call feedkeys(":let c = input('name? ', \"ã̳\")\<CR>\<CR>", 'xt')
  call assert_equal('ã̳', c)

  " Test for using <CR> as default input
  call feedkeys(":let c = input('name? ', \"\\<CR>\")\<CR>x\<CR>", 'xt')
  call assert_equal(' x', c)

  call assert_fails("call input('F:', '', 'invalid')", 'E180:')
  call assert_fails("call input('F:', '', [])", 'E730:')
endfunc

" Test for the inputdialog() function
func Test_inputdialog()
  set timeout timeoutlen=10
  if has('gui_running')
    call assert_fails('let v=inputdialog([], "xx")', 'E730:')
    call assert_fails('let v=inputdialog("Q", [])', 'E730:')
  else
    call feedkeys(":let v=inputdialog('Q:', 'xx', 'yy')\<CR>\<CR>", 'xt')
    call assert_equal('xx', v)
    call feedkeys(":let v=inputdialog('Q:', 'xx', 'yy')\<CR>\<Esc>", 'xt')
    call assert_equal('yy', v)
  endif
  set timeout& timeoutlen&
endfunc

" Test for inputlist()
func Test_inputlist()
  call feedkeys(":let c = inputlist(['Select color:', '1. red', '2. green', '3. blue'])\<cr>1\<cr>", 'tx')
  call assert_equal(1, c)
  call feedkeys(":let c = ['Select color:', '1. red', '2. green', '3. blue']->inputlist()\<cr>2\<cr>", 'tx')
  call assert_equal(2, c)
  call feedkeys(":let c = inputlist(['Select color:', '1. red', '2. green', '3. blue'])\<cr>3\<cr>", 'tx')
  call assert_equal(3, c)

  " CR to cancel
  call feedkeys(":let c = inputlist(['Select color:', '1. red', '2. green', '3. blue'])\<cr>\<cr>", 'tx')
  call assert_equal(0, c)

  " Esc to cancel
  call feedkeys(":let c = inputlist(['Select color:', '1. red', '2. green', '3. blue'])\<cr>\<Esc>", 'tx')
  call assert_equal(0, c)

  " q to cancel
  call feedkeys(":let c = inputlist(['Select color:', '1. red', '2. green', '3. blue'])\<cr>q", 'tx')
  call assert_equal(0, c)

  " Cancel after inputting a number
  call feedkeys(":let c = inputlist(['Select color:', '1. red', '2. green', '3. blue'])\<cr>5q", 'tx')
  call assert_equal(0, c)

  " Use backspace to delete characters in the prompt
  call feedkeys(":let c = inputlist(['Select color:', '1. red', '2. green', '3. blue'])\<cr>1\<BS>3\<BS>2\<cr>", 'tx')
  call assert_equal(2, c)

  " Use mouse to make a selection
  call test_setmouse(&lines - 3, 2)
  call feedkeys(":let c = inputlist(['Select color:', '1. red', '2. green', '3. blue'])\<cr>\<LeftMouse>", 'tx')
  call assert_equal(1, c)
  " Mouse click outside of the list
  call test_setmouse(&lines - 6, 2)
  call feedkeys(":let c = inputlist(['Select color:', '1. red', '2. green', '3. blue'])\<cr>\<LeftMouse>", 'tx')
  call assert_equal(-2, c)

  call assert_fails('call inputlist("")', 'E686:')
  call assert_fails('call inputlist(test_null_list())', 'E686:')
endfunc

func Test_range_inputlist()
  " flush out any garbage left in the buffer
  while getchar(0)
  endwhile

  call feedkeys(":let result = inputlist(range(10))\<CR>1\<CR>", 'x')
  call assert_equal(1, result)
  call feedkeys(":let result = inputlist(range(3, 10))\<CR>1\<CR>", 'x')
  call assert_equal(1, result)

  unlet result
endfunc

func Test_balloon_show()
  CheckFeature balloon_eval

  " This won't do anything but must not crash either.
  call balloon_show('hi!')
  if !has('gui_running')
    call balloon_show(range(3))
    call balloon_show([])
  endif
endfunc

func Test_setbufvar_options()
  " This tests that aucmd_prepbuf() and aucmd_restbuf() properly restore the
  " window layout and cursor position.
  call assert_equal(1, winnr('$'))
  split dummy_preview
  resize 2
  set winfixheight winfixwidth
  let prev_id = win_getid()

  wincmd j
  let wh = winheight(0)
  let dummy_buf = bufnr('dummy_buf1', v:true)
  call setbufvar(dummy_buf, '&buftype', 'nofile')
  execute 'belowright vertical split #' . dummy_buf
  call assert_equal(wh, winheight(0))
  let dum1_id = win_getid()
  call setline(1, 'foo')
  normal! V$
  call assert_equal(4, col('.'))
  call setbufvar('dummy_preview', '&buftype', 'nofile')
  call assert_equal(4, col('.'))

  wincmd h
  let wh = winheight(0)
  call setline(1, 'foo')
  normal! V$
  call assert_equal(4, col('.'))
  let dummy_buf = bufnr('dummy_buf2', v:true)
  eval 'nofile'->setbufvar(dummy_buf, '&buftype')
  call assert_equal(4, col('.'))
  execute 'belowright vertical split #' . dummy_buf
  call assert_equal(wh, winheight(0))

  bwipe!
  call win_gotoid(prev_id)
  bwipe!
  call win_gotoid(dum1_id)
  bwipe!
endfunc

func Test_setbufvar_keep_window_title()
  CheckRunVimInTerminal
  if !has('title') || empty(&t_ts)
    throw "Skipped: can't get/set title"
  endif

  let lines =<< trim END
      set title
      edit Xa.txt
      let g:buf = bufadd('Xb.txt')
      inoremap <F2> <C-R>=setbufvar(g:buf, '&autoindent', 1) ?? ''<CR>
  END
  call writefile(lines, 'Xsetbufvar', 'D')
  let buf = RunVimInTerminal('-S Xsetbufvar', {})
  call WaitForAssert({-> assert_match('Xa.txt', term_gettitle(buf))}, 1000)

  call term_sendkeys(buf, "i\<F2>")
  call TermWait(buf)
  call term_sendkeys(buf, "\<Esc>")
  call TermWait(buf)
  call assert_match('Xa.txt', term_gettitle(buf))

  call StopVimInTerminal(buf)
endfunc

func Test_redo_in_nested_functions()
  nnoremap g. :set opfunc=Operator<CR>g@
  function Operator( type, ... )
     let @x = 'XXX'
     execute 'normal! g`[' . (a:type ==# 'line' ? 'V' : 'v') . 'g`]' . '"xp'
  endfunction

  function! Apply()
      5,6normal! .
  endfunction

  new
  call setline(1, repeat(['some "quoted" text', 'more "quoted" text'], 3))
  1normal g.i"
  call assert_equal('some "XXX" text', getline(1))
  3,4normal .
  call assert_equal('some "XXX" text', getline(3))
  call assert_equal('more "XXX" text', getline(4))
  call Apply()
  call assert_equal('some "XXX" text', getline(5))
  call assert_equal('more "XXX" text', getline(6))
  bwipe!

  nunmap g.
  delfunc Operator
  delfunc Apply
endfunc

func Test_trim()
  call assert_equal("Testing", trim("  \t\r\r\x0BTesting  \t\n\r\n\t\x0B\x0B"))
  call assert_equal("Testing", "  \t  \r\r\n\n\x0BTesting  \t\n\r\n\t\x0B\x0B"->trim())
  call assert_equal("RESERVE", trim("xyz \twwRESERVEzyww \t\t", " wxyz\t"))
  call assert_equal("wRE    \tSERVEzyww", trim("wRE    \tSERVEzyww"))
  call assert_equal("abcd\t     xxxx   tail", trim(" \tabcd\t     xxxx   tail"))
  call assert_equal("\tabcd\t     xxxx   tail", trim(" \tabcd\t     xxxx   tail", " "))
  call assert_equal(" \tabcd\t     xxxx   tail", trim(" \tabcd\t     xxxx   tail", "abx"))
  call assert_equal("RESERVE", trim("你RESERVE好", "你好"))
  call assert_equal("您R E SER V E早", trim("你好您R E SER V E早好你你", "你好"))
  call assert_equal("你好您R E SER V E早好你你", trim(" \n\r\r   你好您R E SER V E早好你你    \t  \x0B", ))
  call assert_equal("您R E SER V E早好你你    \t  \x0B", trim("    你好您R E SER V E早好你你    \t  \x0B", " 你好"))
  call assert_equal("您R E SER V E早好你你    \t  \x0B", trim("    tteesstttt你好您R E SER V E早好你你    \t  \x0B ttestt", " 你好tes"))
  call assert_equal("您R E SER V E早好你你    \t  \x0B", trim("    tteesstttt你好您R E SER V E早好你你    \t  \x0B ttestt", "   你你你好好好tttsses"))
  call assert_equal("留下", trim("这些些不要这些留下这些", "这些不要"))
  call assert_equal("", trim("", ""))
  call assert_equal("a", trim("a", ""))
  call assert_equal("", trim("", "a"))

  call assert_equal("vim", trim("  vim  ", " ", 0))
  call assert_equal("vim  ", trim("  vim  ", " ", 1))
  call assert_equal("  vim", trim("  vim  ", " ", 2))
  call assert_fails('eval trim("  vim  ", " ", [])', 'E745:')
  call assert_fails('eval trim("  vim  ", " ", -1)', 'E475:')
  call assert_fails('eval trim("  vim  ", " ", 3)', 'E475:')
  call assert_fails('eval trim("  vim  ", 0)', 'E1393:')

  let chars = join(map(range(1, 0x20) + [0xa0], {n -> n->nr2char()}), '')
  call assert_equal("x", trim(chars . "x" . chars))

  call assert_equal("x", trim(chars . "x" . chars, v:none, 0))
  call assert_equal("x" . chars, trim(chars . "x" . chars, v:none, 1))
  call assert_equal(chars . "x", trim(chars . "x" . chars, v:none, 2))

  call assert_fails('let c=trim([])', 'E730:')
endfunc

" Test for reg_recording() and reg_executing()
func Test_reg_executing_and_recording()
  let s:reg_stat = ''
  func s:save_reg_stat()
    let s:reg_stat = reg_recording() . ':' . reg_executing()
    return ''
  endfunc

  new
  call s:save_reg_stat()
  call assert_equal(':', s:reg_stat)
  call feedkeys("qa\"=s:save_reg_stat()\<CR>pq", 'xt')
  call assert_equal('a:', s:reg_stat)
  call feedkeys("@a", 'xt')
  call assert_equal(':a', s:reg_stat)
  call feedkeys("qb@aq", 'xt')
  call assert_equal('b:a', s:reg_stat)
  call feedkeys("q\"\"=s:save_reg_stat()\<CR>pq", 'xt')
  call assert_equal('":', s:reg_stat)

  " :normal command saves and restores reg_executing
  let s:reg_stat = ''
  let @q = ":call TestFunc()\<CR>:call s:save_reg_stat()\<CR>"
  func TestFunc() abort
    normal! ia
  endfunc
  call feedkeys("@q", 'xt')
  call assert_equal(':q', s:reg_stat)
  delfunc TestFunc

  " getchar() command saves and restores reg_executing
  map W :call TestFunc()<CR>
  let @q = "W"
  let g:typed = ''
  let g:regs = []
  func TestFunc() abort
    let g:regs += [reg_executing()]
    let g:typed = getchar(0)
    let g:regs += [reg_executing()]
  endfunc
  call feedkeys("@qy", 'xt')
  call assert_equal(char2nr("y"), g:typed)
  call assert_equal(['q', 'q'], g:regs)
  delfunc TestFunc
  unmap W
  unlet g:typed
  unlet g:regs

  " input() command saves and restores reg_executing
  map W :call TestFunc()<CR>
  let @q = "W"
  let g:typed = ''
  let g:regs = []
  func TestFunc() abort
    let g:regs += [reg_executing()]
    let g:typed = '?'->input()
    let g:regs += [reg_executing()]
  endfunc
  call feedkeys("@qy\<CR>", 'xt')
  call assert_equal("y", g:typed)
  call assert_equal(['q', 'q'], g:regs)
  delfunc TestFunc
  unmap W
  unlet g:typed
  unlet g:regs

  bwipe!
  delfunc s:save_reg_stat
  unlet s:reg_stat
endfunc

func Test_inputsecret()
  map W :call TestFunc()<CR>
  let @q = "W"
  let g:typed1 = ''
  let g:typed2 = ''
  let g:regs = []
  func TestFunc() abort
    let g:typed1 = '?'->inputsecret()
    let g:typed2 = inputsecret('password: ')
  endfunc
  call feedkeys("@qsomething\<CR>else\<CR>", 'xt')
  call assert_equal("something", g:typed1)
  call assert_equal("else", g:typed2)
  delfunc TestFunc
  unmap W
  unlet g:typed1
  unlet g:typed2
endfunc

func Test_getchar()
  call feedkeys('a', '')
  call assert_equal(char2nr('a'), getchar())
  call assert_equal(0, getchar(0))
  call assert_equal(0, getchar(1))

  call feedkeys('a', '')
  call assert_equal('a', getcharstr())
  call assert_equal('', getcharstr(0))
  call assert_equal('', getcharstr(1))

  call feedkeys("\<M-F2>", '')
  call assert_equal("\<M-F2>", getchar(0))
  call assert_equal(0, getchar(0))

  call setline(1, 'xxxx')
  call test_setmouse(1, 3)
  let v:mouse_win = 9
  let v:mouse_winid = 9
  let v:mouse_lnum = 9
  let v:mouse_col = 9
  call feedkeys("\<S-LeftMouse>", '')
  call assert_equal("\<S-LeftMouse>", getchar())
  call assert_equal(1, v:mouse_win)
  call assert_equal(win_getid(1), v:mouse_winid)
  call assert_equal(1, v:mouse_lnum)
  call assert_equal(3, v:mouse_col)
  enew!
endfunc

func Test_libcall_libcallnr()
  CheckFeature libcall

  if has('win32')
    let libc = 'msvcrt.dll'
  elseif has('mac')
    let libc = 'libSystem.B.dylib'
  elseif executable('ldd')
    let libc = matchstr(split(system('ldd ' . GetVimProg())), '/libc\.so\>')
  endif
  if get(l:, 'libc', '') ==# ''
    " On Unix, libc.so can be in various places.
    if has('linux')
      " There is not documented but regarding the 1st argument of glibc's
      " dlopen an empty string and nullptr are equivalent, so using an empty
      " string for the 1st argument of libcall allows to call functions.
      let libc = ''
    elseif has('sun')
      " Set the path to libc.so according to the architecture.
      let test_bits = system('file ' . GetVimProg())
      let test_arch = system('uname -p')
      if test_bits =~ '64-bit' && test_arch =~ 'sparc'
        let libc = '/usr/lib/sparcv9/libc.so'
      elseif test_bits =~ '64-bit' && test_arch =~ 'i386'
        let libc = '/usr/lib/amd64/libc.so'
      else
        let libc = '/usr/lib/libc.so'
      endif
    else
      " Unfortunately skip this test until a good way is found.
      return
    endif
  endif

  if has('win32')
    call assert_equal($USERPROFILE, 'USERPROFILE'->libcall(libc, 'getenv'))
  else
    call assert_equal($HOME, 'HOME'->libcall(libc, 'getenv'))
  endif

  " If function returns NULL, libcall() should return an empty string.
  call assert_equal('', libcall(libc, 'getenv', 'X_ENV_DOES_NOT_EXIT'))

  " Test libcallnr() with string and integer argument.
  call assert_equal(4, 'abcd'->libcallnr(libc, 'strlen'))
  call assert_equal(char2nr('A'), char2nr('a')->libcallnr(libc, 'toupper'))

  call assert_fails("call libcall(libc, 'Xdoesnotexist_', '')", ['', 'E364:'])
  call assert_fails("call libcallnr(libc, 'Xdoesnotexist_', '')", ['', 'E364:'])

  call assert_fails("call libcall('Xdoesnotexist_', 'getenv', 'HOME')", ['', 'E364:'])
  call assert_fails("call libcallnr('Xdoesnotexist_', 'strlen', 'abcd')", ['', 'E364:'])
endfunc

sandbox function Fsandbox()
  normal ix
endfunc

func Test_func_sandbox()
  sandbox let F = {-> 'hello'}
  call assert_equal('hello', F())

  sandbox let F = {-> "normal ix\<Esc>"->execute()}
  call assert_fails('call F()', 'E48:')
  unlet F

  call assert_fails('call Fsandbox()', 'E48:')
  delfunc Fsandbox

  " From a sandbox try to set a predefined variable (which cannot be modified
  " from a sandbox)
  call assert_fails('sandbox let v:lnum = 10', 'E794:')
endfunc

func EditAnotherFile()
  let word = expand('<cword>')
  edit Xfuncrange2
endfunc

func Test_func_range_with_edit()
  " Define a function that edits another buffer, then call it with a range that
  " is invalid in that buffer.
  call writefile(['just one line'], 'Xfuncrange2', 'D')
  new
  eval 10->range()->setline(1)
  write Xfuncrange1
  call assert_fails('5,8call EditAnotherFile()', 'E16:')

  call delete('Xfuncrange1')
  bwipe!
endfunc

func Test_func_exists_on_reload()
  call writefile(['func ExistingFunction()', 'echo "yes"', 'endfunc'], 'Xfuncexists', 'D')
  call assert_equal(0, exists('*ExistingFunction'))
  source Xfuncexists
  call assert_equal(1, '*ExistingFunction'->exists())
  " Redefining a function when reloading a script is OK.
  source Xfuncexists
  call assert_equal(1, exists('*ExistingFunction'))

  " But redefining in another script is not OK.
  call writefile(['func ExistingFunction()', 'echo "yes"', 'endfunc'], 'Xfuncexists2', 'D')
  call assert_fails('source Xfuncexists2', 'E122:')

  " Defining a new function from the cmdline should fail if the function is
  " already defined
  call assert_fails('call feedkeys(":func ExistingFunction()\<CR>", "xt")', 'E122:')

  delfunc ExistingFunction
  call assert_equal(0, exists('*ExistingFunction'))
  call writefile([
	\ 'func ExistingFunction()', 'echo "yes"', 'endfunc',
	\ 'func ExistingFunction()', 'echo "no"', 'endfunc',
	\ ], 'Xfuncexists')
  call assert_fails('source Xfuncexists', 'E122:')
  call assert_equal(1, exists('*ExistingFunction'))

  delfunc ExistingFunction
endfunc

" Test confirm({msg} [, {choices} [, {default} [, {type}]]])
func Test_confirm()
  CheckUnix
  CheckNotGui

  call feedkeys('o', 'L')
  let a = confirm('Press O to proceed')
  call assert_equal(1, a)

  call feedkeys('y', 'L')
  let a = 'Are you sure?'->confirm("&Yes\n&No")
  call assert_equal(1, a)

  call feedkeys('n', 'L')
  let a = confirm('Are you sure?', "&Yes\n&No")
  call assert_equal(2, a)

  " confirm() should return 0 when pressing CTRL-C.
  call feedkeys("\<C-C>", 'L')
  let a = confirm('Are you sure?', "&Yes\n&No")
  call assert_equal(0, a)

  " <Esc> requires another character to avoid it being seen as the start of an
  " escape sequence.  Zero should be harmless.
  eval "\<Esc>0"->feedkeys('L')
  let a = confirm('Are you sure?', "&Yes\n&No")
  call assert_equal(0, a)

  " Default choice is returned when pressing <CR>.
  call feedkeys("\<CR>", 'L')
  let a = confirm('Are you sure?', "&Yes\n&No")
  call assert_equal(1, a)

  call feedkeys("\<CR>", 'L')
  let a = confirm('Are you sure?', "&Yes\n&No", 2)
  call assert_equal(2, a)

  call feedkeys("\<CR>", 'L')
  let a = confirm('Are you sure?', "&Yes\n&No", 0)
  call assert_equal(0, a)

  " Test with the {type} 4th argument
  for type in ['Error', 'Question', 'Info', 'Warning', 'Generic']
    call feedkeys('y', 'L')
    let a = confirm('Are you sure?', "&Yes\n&No\n", 1, type)
    call assert_equal(1, a)
  endfor

  call assert_fails('call confirm([])', 'E730:')
  call assert_fails('call confirm("Are you sure?", [])', 'E730:')
  call assert_fails('call confirm("Are you sure?", "&Yes\n&No\n", [])', 'E745:')
  call assert_fails('call confirm("Are you sure?", "&Yes\n&No\n", 0, [])', 'E730:')
endfunc

func Test_platform_name()
  " The system matches at most only one name.
  let names = ['amiga', 'bsd', 'hpux', 'linux', 'mac', 'qnx', 'sun', 'vms', 'win32', 'win32unix']
  call assert_inrange(0, 1, len(filter(copy(names), 'has(v:val)')))

  " Is Unix?
  call assert_equal(has('bsd'), has('bsd') && has('unix'))
  call assert_equal(has('hpux'), has('hpux') && has('unix'))
  call assert_equal(has('linux'), has('linux') && has('unix'))
  call assert_equal(has('mac'), has('mac') && has('unix'))
  call assert_equal(has('qnx'), has('qnx') && has('unix'))
  call assert_equal(has('sun'), has('sun') && has('unix'))
  call assert_equal(has('win32'), has('win32') && !has('unix'))
  call assert_equal(has('win32unix'), has('win32unix') && has('unix'))

  if has('unix') && executable('uname')
    let uname = system('uname')
    " GNU userland on BSD kernels (e.g., GNU/kFreeBSD) don't have BSD defined
    call assert_equal(uname =~? '\%(GNU/k\w\+\)\@<!BSD\|DragonFly', has('bsd'))
    call assert_equal(uname =~? 'HP-UX', has('hpux'))
    call assert_equal(uname =~? 'Linux', has('linux'))
    call assert_equal(uname =~? 'Darwin', has('mac'))
    call assert_equal(uname =~? 'QNX', has('qnx'))
    call assert_equal(uname =~? 'SunOS', has('sun'))
    call assert_equal(uname =~? 'CYGWIN\|MSYS', has('win32unix'))
  endif
endfunc

func Test_readdir()
  call mkdir('Xreaddir', 'R')
  call writefile([], 'Xreaddir/foo.txt')
  call writefile([], 'Xreaddir/bar.txt')
  call mkdir('Xreaddir/dir')

  " All results
  let files = readdir('Xreaddir')
  call assert_equal(['bar.txt', 'dir', 'foo.txt'], sort(files))

  " Only results containing "f"
  let files = 'Xreaddir'->readdir({ x -> stridx(x, 'f') != -1 })
  call assert_equal(['foo.txt'], sort(files))

  " Only .txt files
  let files = readdir('Xreaddir', { x -> x =~ '.txt$' })
  call assert_equal(['bar.txt', 'foo.txt'], sort(files))

  " Only .txt files with string
  let files = readdir('Xreaddir', 'v:val =~ ".txt$"')
  call assert_equal(['bar.txt', 'foo.txt'], sort(files))

  " Limit to 1 result.
  let l = []
  let files = readdir('Xreaddir', {x -> len(add(l, x)) == 2 ? -1 : 1})
  call assert_equal(1, len(files))

  " Nested readdir() must not crash
  let files = readdir('Xreaddir', 'readdir("Xreaddir", "1") != []')
  call sort(files)->assert_equal(['bar.txt', 'dir', 'foo.txt'])
endfunc

func Test_readdirex()
  call mkdir('Xexdir', 'R')
  call writefile(['foo'], 'Xexdir/foo.txt')
  call writefile(['barbar'], 'Xexdir/bar.txt')
  call mkdir('Xexdir/dir')

  " All results
  let files = readdirex('Xexdir')->map({-> v:val.name})
  call assert_equal(['bar.txt', 'dir', 'foo.txt'], sort(files))
  let sizes = readdirex('Xexdir')->map({-> v:val.size})
  call assert_equal([0, 4, 7], sort(sizes))

  " Only results containing "f"
  let files = 'Xexdir'->readdirex({ e -> stridx(e.name, 'f') != -1 })
			  \ ->map({-> v:val.name})
  call assert_equal(['foo.txt'], sort(files))

  " Only .txt files
  let files = readdirex('Xexdir', { e -> e.name =~ '.txt$' })
			  \ ->map({-> v:val.name})
  call assert_equal(['bar.txt', 'foo.txt'], sort(files))

  " Only .txt files with string
  let files = readdirex('Xexdir', 'v:val.name =~ ".txt$"')
			  \ ->map({-> v:val.name})
  call assert_equal(['bar.txt', 'foo.txt'], sort(files))

  " Limit to 1 result.
  let l = []
  let files = readdirex('Xexdir', {e -> len(add(l, e.name)) == 2 ? -1 : 1})
			  \ ->map({-> v:val.name})
  call assert_equal(1, len(files))

  " Nested readdirex() must not crash
  let files = readdirex('Xexdir', 'readdirex("Xexdir", "1") != []')
			  \ ->map({-> v:val.name})
  call sort(files)->assert_equal(['bar.txt', 'dir', 'foo.txt'])

  " report broken link correctly
  if has("unix")
    call writefile([], 'Xexdir/abc.txt')
    call system("ln -s Xexdir/abc.txt Xexdir/link")
    call delete('Xexdir/abc.txt')
    let files = readdirex('Xexdir', 'readdirex("Xexdir", "1") != []')
			  \ ->map({-> v:val.name .. '_' .. v:val.type})
    call sort(files)->assert_equal(
        \ ['bar.txt_file', 'dir_dir', 'foo.txt_file', 'link_link'])
  endif

  call assert_fails('call readdirex("doesnotexist")', 'E484:')
endfunc

func Test_readdirex_sort()
  CheckUnix
  " Skip tests on Mac OS X and Cygwin (does not allow several files with different casing)
  if has("osxdarwin") || has("osx") || has("macunix") || has("win32unix")
    throw 'Skipped: Test_readdirex_sort on systems that do not allow this using the default filesystem'
  endif
  let _collate = v:collate
  call mkdir('Xsortdir2', 'R')
  call writefile(['1'], 'Xsortdir2/README.txt')
  call writefile(['2'], 'Xsortdir2/Readme.txt')
  call writefile(['3'], 'Xsortdir2/readme.txt')

  " 1) default
  let files = readdirex('Xsortdir2')->map({-> v:val.name})
  let default = copy(files)
  call assert_equal(['README.txt', 'Readme.txt', 'readme.txt'], files, 'sort using default')

  " 2) no sorting
  let files = readdirex('Xsortdir2', 1, #{sort: 'none'})->map({-> v:val.name})
  let unsorted = copy(files)
  call assert_equal(['README.txt', 'Readme.txt', 'readme.txt'], sort(files), 'unsorted')
  call assert_fails("call readdirex('Xsortdir2', 1, #{slort: 'none'})", 'E857: Dictionary key "sort" required')

  " 3) sort by case (same as default)
  let files = readdirex('Xsortdir2', 1, #{sort: 'case'})->map({-> v:val.name})
  call assert_equal(default, files, 'sort by case')

  " 4) sort by ignoring case
  let files = readdirex('Xsortdir2', 1, #{sort: 'icase'})->map({-> v:val.name})
  call assert_equal(unsorted->sort('i'), files, 'sort by icase')

  " 5) Default Collation
  let collate = v:collate
  lang collate C
  let files = readdirex('Xsortdir2', 1, #{sort: 'collate'})->map({-> v:val.name})
  call assert_equal(['README.txt', 'Readme.txt', 'readme.txt'], files, 'sort by C collation')

  " 6) Collation de_DE
  " Switch locale, this may not work on the CI system, if the locale isn't
  " available
  try
    lang collate de_DE
    let files = readdirex('Xsortdir2', 1, #{sort: 'collate'})->map({-> v:val.name})
    call assert_equal(['readme.txt', 'Readme.txt', 'README.txt'], files, 'sort by de_DE collation')
  catch
    throw 'Skipped: de_DE collation is not available'

  finally
    exe 'lang collate' collate
  endtry
endfunc

func Test_readdir_sort()
  " some more cases for testing sorting for readdirex
  let dir = 'Xsortdir3'
  call mkdir(dir, 'R')
  call writefile(['1'], dir .. '/README.txt')
  call writefile(['2'], dir .. '/Readm.txt')
  call writefile(['3'], dir .. '/read.txt')
  call writefile(['4'], dir .. '/Z.txt')
  call writefile(['5'], dir .. '/a.txt')
  call writefile(['6'], dir .. '/b.txt')

  " 1) default
  let files = readdir(dir)
  let default = copy(files)
  call assert_equal(default->sort(), files, 'sort using default')

  " 2) sort by case (same as default)
  let files = readdir(dir, '1', #{sort: 'case'})
  call assert_equal(default, files, 'sort using default')

  " 3) sort by ignoring case
  let files = readdir(dir, '1', #{sort: 'icase'})
  call assert_equal(default->sort('i'), files, 'sort by ignoring case')

  " 4) collation
  let collate = v:collate
  lang collate C
  let files = readdir(dir, 1, #{sort: 'collate'})
  call assert_equal(default->sort(), files, 'sort by C collation')
  exe "lang collate" collate

  " 5) Errors
  call assert_fails('call readdir(dir, 1, 1)', 'E1206:')
  call assert_fails('call readdir(dir, 1, #{sorta: 1})')
  call assert_fails('call readdir(dir, 1, test_null_dict())', 'E1297:')
  call assert_fails('call readdirex(dir, 1, 1)', 'E1206:')
  call assert_fails('call readdirex(dir, 1, #{sorta: 1})')
  call assert_fails('call readdirex(dir, 1, test_null_dict())', 'E1297:')

  " 6) ignore other values in dict
  let files = readdir(dir, '1', #{sort: 'c'})
  call assert_equal(default, files, 'sort using default2')

  " Cleanup
  exe "lang collate" collate
endfunc

func Test_delete_rf()
  call mkdir('Xrfdir')
  call writefile([], 'Xrfdir/foo.txt')
  call writefile([], 'Xrfdir/bar.txt')
  call mkdir('Xrfdir/[a-1]')  " issue #696
  call writefile([], 'Xrfdir/[a-1]/foo.txt')
  call writefile([], 'Xrfdir/[a-1]/bar.txt')
  call assert_true(filereadable('Xrfdir/foo.txt'))
  call assert_true('Xrfdir/[a-1]/foo.txt'->filereadable())

  call assert_equal(0, delete('Xrfdir', 'rf'))
  call assert_false(filereadable('Xrfdir/foo.txt'))
  call assert_false(filereadable('Xrfdir/[a-1]/foo.txt'))

  if has('unix')
    call mkdir('Xrfdir/Xdir2', 'p')
    silent !chmod 555 Xrfdir
    call assert_equal(-1, delete('Xrfdir/Xdir2', 'rf'))
    call assert_equal(-1, delete('Xrfdir', 'rf'))
    silent !chmod 755 Xrfdir
    call assert_equal(0, delete('Xrfdir', 'rf'))
  endif
endfunc

func Test_call()
  call assert_equal(3, call('len', [123]))
  call assert_equal(3, 'len'->call([123]))
  call assert_fails("call call('len', 123)", 'E1211:')
  call assert_equal(0, call('', []))
  call assert_equal(0, call('len', test_null_list()))

  function Mylen() dict
     return len(self.data)
  endfunction
  let mydict = {'data': [0, 1, 2, 3], 'len': function("Mylen")}
  eval mydict.len->call([], mydict)->assert_equal(4)
  call assert_fails("call call('Mylen', [], 0)", 'E1206:')
  call assert_fails('call foo', 'E107:')

  " These once caused a crash.
  call call(test_null_function(), [])
  call call(test_null_partial(), [])
  call assert_fails('call test_null_function()()', 'E1192:')
  call assert_fails('call test_null_partial()()', 'E117:')

  let lines =<< trim END
      let Time = 'localtime'
      call Time()
  END
  call v9.CheckScriptFailure(lines, 'E1085:')
endfunc

func Test_char2nr()
  call assert_equal(12354, char2nr('あ', 1))
  call assert_equal(120, 'x'->char2nr())
  set encoding=latin1
  call assert_equal(120, 'x'->char2nr())
  set encoding=utf-8
endfunc

func Test_charclass()
  call assert_equal(0, charclass(' '))
  call assert_equal(1, charclass('.'))
  call assert_equal(2, charclass('x'))
  call assert_equal(3, charclass("\u203c"))
  " this used to crash vim
  call assert_equal(0, "xxx"[-1]->charclass())
endfunc

func Test_eventhandler()
  call assert_equal(0, eventhandler())
endfunc

func Test_bufadd_bufload()
  call assert_equal(0, bufexists('someName'))
  let buf = bufadd('someName')
  call assert_notequal(0, buf)
  call assert_equal(1, bufexists('someName'))
  call assert_equal(0, getbufvar(buf, '&buflisted'))
  call assert_equal(0, bufloaded(buf))
  call bufload(buf)
  call assert_equal(1, bufloaded(buf))
  call assert_equal([''], getbufline(buf, 1, '$'))

  let curbuf = bufnr('')
  eval ['some', 'text']->writefile('XotherName')
  let buf = 'XotherName'->bufadd()
  call assert_notequal(0, buf)
  eval 'XotherName'->bufexists()->assert_equal(1)
  call assert_equal(0, getbufvar(buf, '&buflisted'))
  call assert_equal(0, bufloaded(buf))
  eval buf->bufload()
  call assert_equal(1, bufloaded(buf))
  call assert_equal(['some', 'text'], getbufline(buf, 1, '$'))
  call assert_equal(curbuf, bufnr(''))

  let buf1 = bufadd('')
  let buf2 = bufadd('')
  call assert_notequal(0, buf1)
  call assert_notequal(0, buf2)
  call assert_notequal(buf1, buf2)
  call assert_equal(1, bufexists(buf1))
  call assert_equal(1, bufexists(buf2))
  call assert_equal(0, bufloaded(buf1))
  exe 'bwipe ' .. buf1
  call assert_equal(0, bufexists(buf1))
  call assert_equal(1, bufexists(buf2))
  exe 'bwipe ' .. buf2
  call assert_equal(0, bufexists(buf2))

  " When 'buftype' is "nofile" then bufload() does not read the file.
  " Other values too.
  for val in [['nofile', 0],
            \ ['nowrite', 1],
            \ ['acwrite', 1],
            \ ['quickfix', 0],
            \ ['help', 1],
            \ ['terminal', 0],
            \ ['prompt', 0],
            \ ['popup', 0],
            \ ]
    bwipe! XotherName
    let buf = bufadd('XotherName')
    call setbufvar(buf, '&bt', val[0])
    call bufload(buf)
    call assert_equal(val[1] ? ['some', 'text'] : [''], getbufline(buf, 1, '$'), val[0])
  endfor

  bwipe someName
  bwipe XotherName
  call assert_equal(0, bufexists('someName'))
  call delete('XotherName')
endfunc

func Test_state()
  CheckRunVimInTerminal

  let getstate = ":echo 'state: ' .. g:state .. '; mode: ' .. g:mode\<CR>"

  let lines =<< trim END
	call setline(1, ['one', 'two', 'three'])
	map ;; gg
	set complete=.
	func RunTimer()
	  call timer_start(10, {id -> execute('let g:state = state()') .. execute('let g:mode = mode()')})
	endfunc
	au Filetype foobar let g:state = state()|let g:mode = mode()
  END
  call writefile(lines, 'XState')
  let buf = RunVimInTerminal('-S XState', #{rows: 6})

  " Using a ":" command Vim is busy, thus "S" is returned
  call term_sendkeys(buf, ":echo 'state: ' .. state() .. '; mode: ' .. mode()\<CR>")
  call WaitForAssert({-> assert_match('state: S; mode: n', term_getline(buf, 6))}, 1000)
  call term_sendkeys(buf, ":\<CR>")

  " Using a timer callback
  call term_sendkeys(buf, ":call RunTimer()\<CR>")
  call TermWait(buf, 25)
  call term_sendkeys(buf, getstate)
  call WaitForAssert({-> assert_match('state: c; mode: n', term_getline(buf, 6))}, 1000)

  " Halfway a mapping
  call term_sendkeys(buf, ":call RunTimer()\<CR>;")
  call TermWait(buf, 25)
  call term_sendkeys(buf, ";")
  call term_sendkeys(buf, getstate)
  call WaitForAssert({-> assert_match('state: mSc; mode: n', term_getline(buf, 6))}, 1000)

  " An operator is pending
  call term_sendkeys(buf, ":call RunTimer()\<CR>y")
  call TermWait(buf, 25)
  call term_sendkeys(buf, "y")
  call term_sendkeys(buf, getstate)
  call WaitForAssert({-> assert_match('state: oSc; mode: n', term_getline(buf, 6))}, 1000)

  " A register was specified
  call term_sendkeys(buf, ":call RunTimer()\<CR>\"r")
  call TermWait(buf, 25)
  call term_sendkeys(buf, "yy")
  call term_sendkeys(buf, getstate)
  call WaitForAssert({-> assert_match('state: oSc; mode: n', term_getline(buf, 6))}, 1000)

  " Insert mode completion (bit slower on Mac)
  call term_sendkeys(buf, ":call RunTimer()\<CR>Got\<C-N>")
  call TermWait(buf, 25)
  call term_sendkeys(buf, "\<Esc>")
  call term_sendkeys(buf, getstate)
  call WaitForAssert({-> assert_match('state: aSc; mode: i', term_getline(buf, 6))}, 1000)

  " Autocommand executing
  call term_sendkeys(buf, ":set filetype=foobar\<CR>")
  call TermWait(buf, 25)
  call term_sendkeys(buf, getstate)
  call WaitForAssert({-> assert_match('state: xS; mode: n', term_getline(buf, 6))}, 1000)

  " Todo: "w" - waiting for ch_evalexpr()

  " messages scrolled
  call term_sendkeys(buf, ":call RunTimer()\<CR>:echo \"one\\ntwo\\nthree\"\<CR>")
  call TermWait(buf, 25)
  call term_sendkeys(buf, "\<CR>")
  call term_sendkeys(buf, getstate)
  call WaitForAssert({-> assert_match('state: Scs; mode: r', term_getline(buf, 6))}, 1000)

  call StopVimInTerminal(buf)
  call delete('XState')
endfunc

func Test_range()
  " destructuring
  let [x, y] = range(2)
  call assert_equal([0, 1], [x, y])

  " index
  call assert_equal(4, range(1, 10)[3])

  " add()
  call assert_equal([0, 1, 2, 3], add(range(3), 3))
  call assert_equal([0, 1, 2, [0, 1, 2]], add([0, 1, 2], range(3)))
  call assert_equal([0, 1, 2, [0, 1, 2]], add(range(3), range(3)))

  " append()
  new
  call append('.', range(5))
  call assert_equal(['', '0', '1', '2', '3', '4'], getline(1, '$'))
  bwipe!

  " appendbufline()
  new
  call appendbufline(bufnr(''), '.', range(5))
  call assert_equal(['0', '1', '2', '3', '4', ''], getline(1, '$'))
  bwipe!

  " call()
  func TwoArgs(a, b)
    return [a:a, a:b]
  endfunc
  call assert_equal([0, 1], call('TwoArgs', range(2)))

  " col()
  new
  call setline(1, ['foo', 'bar'])
  call assert_equal(2, col(range(1, 2)))
  bwipe!

  " complete()
  execute "normal! a\<C-r>=[complete(col('.'), range(10)), ''][1]\<CR>"
  " complete_info()
  execute "normal! a\<C-r>=[complete(col('.'), range(10)), ''][1]\<CR>\<C-r>=[complete_info(range(5)), ''][1]\<CR>"

  " copy()
  call assert_equal([1, 2, 3], copy(range(1, 3)))

  " count()
  call assert_equal(0, count(range(0), 3))
  call assert_equal(0, count(range(2), 3))
  call assert_equal(1, count(range(5), 3))

  " cursor()
  new
  call setline(1, ['aaa', 'bbb', 'ccc'])
  call cursor(range(1, 2))
  call assert_equal([2, 1], [col('.'), line('.')])
  bwipe!

  " deepcopy()
  call assert_equal([1, 2, 3], deepcopy(range(1, 3)))

  " empty()
  call assert_true(empty(range(0)))
  call assert_false(empty(range(2)))

  " execute()
  new
  call setline(1, ['aaa', 'bbb', 'ccc'])
  call execute(range(3))
  call assert_equal(2, line('.'))
  bwipe!

  " extend()
  call assert_equal([1, 2, 3, 4], extend([1], range(2, 4)))
  call assert_equal([1, 2, 3, 4], extend(range(1, 1), range(2, 4)))
  call assert_equal([1, 2, 3, 4], extend(range(1, 1), [2, 3, 4]))

  " filter()
  call assert_equal([1, 3], filter(range(5), 'v:val % 2'))
  call assert_equal([1, 5, 7, 11, 13], filter(filter(range(15), 'v:val % 2'), 'v:val % 3'))

  " funcref()
  call assert_equal([0, 1], funcref('TwoArgs', range(2))())

  " function()
  call assert_equal([0, 1], function('TwoArgs', range(2))())

  " garbagecollect()
  let thelist = [1, range(2), 3]
  let otherlist = range(3)
  call test_garbagecollect_now()

  " get()
  call assert_equal(4, get(range(1, 10), 3))
  call assert_equal(-1, get(range(1, 10), 42, -1))

  " index()
  call assert_equal(1, index(range(1, 5), 2))
  call assert_fails("echo index([1, 2], 1, [])", 'E745:')

  " insert()
  call assert_equal([42, 1, 2, 3, 4, 5], insert(range(1, 5), 42))
  call assert_equal([42, 1, 2, 3, 4, 5], insert(range(1, 5), 42, 0))
  call assert_equal([1, 42, 2, 3, 4, 5], insert(range(1, 5), 42, 1))
  call assert_equal([1, 2, 3, 4, 42, 5], insert(range(1, 5), 42, 4))
  call assert_equal([1, 2, 3, 4, 42, 5], insert(range(1, 5), 42, -1))
  call assert_equal([1, 2, 3, 4, 5, 42], insert(range(1, 5), 42, 5))

  " join()
  call assert_equal('0 1 2 3 4', join(range(5)))

  " json_encode()
  call assert_equal('[0,1,2,3]', json_encode(range(4)))

  " len()
  call assert_equal(0, len(range(0)))
  call assert_equal(2, len(range(2)))
  call assert_equal(5, len(range(0, 12, 3)))
  call assert_equal(4, len(range(3, 0, -1)))

  " list2str()
  call assert_equal('ABC', list2str(range(65, 67)))
  call assert_fails('let s = list2str(5)', 'E1211:')

  " lock()
  let thelist = range(5)
  lockvar thelist

  " map()
  call assert_equal([0, 2, 4, 6, 8], map(range(5), 'v:val * 2'))
  call assert_equal([3, 5, 7, 9, 11], map(map(range(5), 'v:val * 2'), 'v:val + 3'))
  call assert_equal([2, 6], map(filter(range(5), 'v:val % 2'), 'v:val * 2'))
  call assert_equal([2, 4, 8], filter(map(range(5), 'v:val * 2'), 'v:val % 3'))

  " match()
  call assert_equal(3, match(range(5), 3))

  " matchaddpos()
  highlight MyGreenGroup ctermbg=green guibg=green
  call matchaddpos('MyGreenGroup', range(line('.'), line('.')))

  " matchend()
  call assert_equal(4, matchend(range(5), '4'))
  call assert_equal(3, matchend(range(1, 5), '4'))
  call assert_equal(-1, matchend(range(1, 5), '42'))

  " matchstrpos()
  call assert_equal(['4', 4, 0, 1], matchstrpos(range(5), '4'))
  call assert_equal(['4', 3, 0, 1], matchstrpos(range(1, 5), '4'))
  call assert_equal(['', -1, -1, -1], matchstrpos(range(1, 5), '42'))

  " max() reverse()
  call assert_equal(0, max(range(0)))
  call assert_equal(0, max(range(10, 9)))
  call assert_equal(9, max(range(10)))
  call assert_equal(18, max(range(0, 20, 3)))
  call assert_equal(20, max(range(20, 0, -3)))
  call assert_equal(99999, max(range(100000)))
  call assert_equal(99999, max(range(99999, 0, -1)))
  call assert_equal(99999, max(reverse(range(100000))))
  call assert_equal(99999, max(reverse(range(99999, 0, -1))))

  " min() reverse()
  call assert_equal(0, min(range(0)))
  call assert_equal(0, min(range(10, 9)))
  call assert_equal(5, min(range(5, 10)))
  call assert_equal(5, min(range(5, 10, 3)))
  call assert_equal(2, min(range(20, 0, -3)))
  call assert_equal(0, min(range(100000)))
  call assert_equal(0, min(range(99999, 0, -1)))
  call assert_equal(0, min(reverse(range(100000))))
  call assert_equal(0, min(reverse(range(99999, 0, -1))))

  " remove()
  call assert_equal(1, remove(range(1, 10), 0))
  call assert_equal(2, remove(range(1, 10), 1))
  call assert_equal(9, remove(range(1, 10), 8))
  call assert_equal(10, remove(range(1, 10), 9))
  call assert_equal(10, remove(range(1, 10), -1))
  call assert_equal([3, 4, 5], remove(range(1, 10), 2, 4))

  " repeat()
  call assert_equal([0, 1, 2, 0, 1, 2], repeat(range(3), 2))
  call assert_equal([0, 1, 2], repeat(range(3), 1))
  call assert_equal([], repeat(range(3), 0))
  call assert_equal([], repeat(range(5, 4), 2))
  call assert_equal([], repeat(range(5, 4), 0))

  " reverse()
  call assert_equal([2, 1, 0], reverse(range(3)))
  call assert_equal([0, 1, 2, 3], reverse(range(3, 0, -1)))
  call assert_equal([9, 8, 7, 6, 5, 4, 3, 2, 1, 0], reverse(range(10)))
  call assert_equal([20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10], reverse(range(10, 20)))
  call assert_equal([16, 13, 10], reverse(range(10, 18, 3)))
  call assert_equal([19, 16, 13, 10], reverse(range(10, 19, 3)))
  call assert_equal([19, 16, 13, 10], reverse(range(10, 20, 3)))
  call assert_equal([11, 14, 17, 20], reverse(range(20, 10, -3)))
  call assert_equal([], reverse(range(0)))

  " TODO: setpos()
  " new
  " call setline(1, repeat([''], bufnr('')))
  " call setline(bufnr('') + 1, repeat('x', bufnr('') * 2 + 6))
  " call setpos('x', range(bufnr(''), bufnr('') + 3))
  " bwipe!

  " setreg()
  call setreg('a', range(3))
  call assert_equal("0\n1\n2\n", getreg('a'))

  " settagstack()
  call settagstack(1, #{items : range(4)})

  " sign_define()
  call assert_fails("call sign_define(range(5))", "E715:")
  call assert_fails("call sign_placelist(range(5))", "E715:")

  " sign_undefine()
  call assert_fails("call sign_undefine(range(5))", "E908:")

  " sign_unplacelist()
  call assert_fails("call sign_unplacelist(range(5))", "E715:")

  " sort()
  call assert_equal([0, 1, 2, 3, 4, 5], sort(range(5, 0, -1)))

  " string()
  call assert_equal('[0, 1, 2, 3, 4]', string(range(5)))

  " taglist() with 'tagfunc'
  func TagFunc(pattern, flags, info)
    return range(10)
  endfunc
  set tagfunc=TagFunc
  call assert_fails("call taglist('asdf')", 'E987:')
  set tagfunc=

  " term_start()
  if has('terminal') && has('termguicolors')
    call assert_fails('call term_start(range(3, 4))', 'E474:')
    let g:terminal_ansi_colors = range(16)
    if has('win32')
      let cmd = "cmd /c dir"
    else
      let cmd = "ls"
    endif
    call assert_fails('call term_start("' .. cmd .. '", #{term_finish: "close"'
        \ .. ', ansi_colors: range(16)})', 'E475:')
    unlet g:terminal_ansi_colors
  endif

  " type()
  call assert_equal(v:t_list, type(range(5)))

  " uniq()
  call assert_equal([0, 1, 2, 3, 4], uniq(range(5)))

  " errors
  call assert_fails('let x=range(2, 8, 0)', 'E726:')
  call assert_fails('let x=range(3, 1)', 'E727:')
  call assert_fails('let x=range(1, 3, -2)', 'E727:')
  call assert_fails('let x=range([])', 'E745:')
  call assert_fails('let x=range(1, [])', 'E745:')
  call assert_fails('let x=range(1, 4, [])', 'E745:')
endfunc

func Test_garbagecollect_now_fails()
  let v:testing = 0
  call assert_fails('call test_garbagecollect_now()', 'E1142:')
  let v:testing = 1
endfunc

func Test_echoraw()
  CheckScreendump

  " Normally used for escape codes, but let's test with a CR.
  let lines =<< trim END
    call echoraw("hello\<CR>x")
  END
  call writefile(lines, 'XTest_echoraw')
  let buf = RunVimInTerminal('-S XTest_echoraw', {'rows': 5, 'cols': 40})
  call VerifyScreenDump(buf, 'Test_functions_echoraw', {})

  " clean up
  call StopVimInTerminal(buf)
  call delete('XTest_echoraw')
endfunc

" Test for echo highlighting
func Test_echohl()
  echohl Search
  echo 'Vim'
  call assert_equal('Vim', Screenline(&lines))
  " TODO: How to check the highlight group used by echohl?
  " ScreenAttrs() returns all zeros.
  echohl None
endfunc

" Test for the eval() function
func Test_eval()
  call assert_fails("call eval('5 a')", 'E488:')
endfunc

" Test for the keytrans() function
func Test_keytrans()
  call assert_equal('<Space>', keytrans(' '))
  call assert_equal('<lt>', keytrans('<'))
  call assert_equal('<lt>Tab>', keytrans('<Tab>'))
  call assert_equal('<Tab>', keytrans("\<Tab>"))
  call assert_equal('<C-V>', keytrans("\<C-V>"))
  call assert_equal('<BS>', keytrans("\<BS>"))
  call assert_equal('<Home>', keytrans("\<Home>"))
  call assert_equal('<C-Home>', keytrans("\<C-Home>"))
  call assert_equal('<M-Home>', keytrans("\<M-Home>"))
  call assert_equal('<C-Space>', keytrans("\<C-Space>"))
  call assert_equal('<M-Space>', keytrans("\<*M-Space>"))
  call assert_equal('<M-x>', "\<*M-x>"->keytrans())
  call assert_equal('<C-I>', "\<*C-I>"->keytrans())
  call assert_equal('<S-3>', "\<*S-3>"->keytrans())
  call assert_equal('π', 'π'->keytrans())
  call assert_equal('<M-π>', "\<M-π>"->keytrans())
  call assert_equal('ě', 'ě'->keytrans())
  call assert_equal('<M-ě>', "\<M-ě>"->keytrans())
  call assert_equal('', ''->keytrans())
  call assert_equal('', test_null_string()->keytrans())
  call assert_fails('call keytrans(1)', 'E1174:')
  call assert_fails('call keytrans()', 'E119:')
endfunc

" Test for the nr2char() function
func Test_nr2char()
  set encoding=latin1
  call assert_equal('@', nr2char(64))
  set encoding=utf8
  call assert_equal('a', nr2char(97, 1))
  call assert_equal('a', nr2char(97, 0))

  call assert_equal("\x80\xfc\b" .. nr2char(0x100000), eval('"\<M-' .. nr2char(0x100000) .. '>"'))
  call assert_equal("\x80\xfc\b" .. nr2char(0x40000000), eval('"\<M-' .. nr2char(0x40000000) .. '>"'))
endfunc

" Test for screenattr(), screenchar() and screenchars() functions
func Test_screen_functions()
  call assert_equal(-1, screenattr(-1, -1))
  call assert_equal(-1, screenchar(-1, -1))
  call assert_equal([], screenchars(-1, -1))

  " Run this in a separate Vim instance to avoid messing up.
  let after =<< trim [CODE]
    scriptencoding utf-8
    call setline(1, '口')
    redraw
    call assert_equal(0, screenattr(1, 1))
    call assert_equal(char2nr('口'), screenchar(1, 1))
    call assert_equal([char2nr('口')], screenchars(1, 1))
    call assert_equal('口', screenstring(1, 1))
    call writefile(v:errors, 'Xresult')
    qall!
  [CODE]

  let encodings = ['utf-8', 'cp932', 'cp936', 'cp949', 'cp950']
  if !has('win32')
    let encodings += ['euc-jp']
  endif
  for enc in encodings
    let msg = 'enc=' .. enc
    if RunVim([], after, $'--clean --cmd "set encoding={enc}"')
      call assert_equal([], readfile('Xresult'), msg)
    endif
    call delete('Xresult')
  endfor
endfunc

" Test for getcurpos() and setpos()
func Test_getcurpos_setpos()
  new
  call setline(1, ['012345678', '012345678'])
  normal gg6l
  let sp = getcurpos()
  normal 0
  call setpos('.', sp)
  normal jyl
  call assert_equal('6', @")
  call assert_equal(-1, setpos('.', test_null_list()))
  call assert_equal(-1, setpos('.', {}))

  let winid = win_getid()
  normal G$
  let pos = getcurpos()
  wincmd w
  call assert_equal(pos, getcurpos(winid))

  wincmd w
  close!

  call assert_equal(getcurpos(), getcurpos(0))
  call assert_equal([0, 0, 0, 0, 0], getcurpos(-1))
  call assert_equal([0, 0, 0, 0, 0], getcurpos(1999))
endfunc

func Test_getmousepos()
  enew!
  call setline(1, "\t\t\t1234")
  call test_setmouse(1, 1)
  call assert_equal(#{
        \ screenrow: 1,
        \ screencol: 1,
        \ winid: win_getid(),
        \ winrow: 1,
        \ wincol: 1,
        \ line: 1,
        \ column: 1,
        \ coladd: 0,
        \ }, getmousepos())
  call test_setmouse(1, 2)
  call assert_equal(#{
        \ screenrow: 1,
        \ screencol: 2,
        \ winid: win_getid(),
        \ winrow: 1,
        \ wincol: 2,
        \ line: 1,
        \ column: 1,
        \ coladd: 1,
        \ }, getmousepos())
  call test_setmouse(1, 8)
  call assert_equal(#{
        \ screenrow: 1,
        \ screencol: 8,
        \ winid: win_getid(),
        \ winrow: 1,
        \ wincol: 8,
        \ line: 1,
        \ column: 1,
        \ coladd: 7,
        \ }, getmousepos())
  call test_setmouse(1, 9)
  call assert_equal(#{
        \ screenrow: 1,
        \ screencol: 9,
        \ winid: win_getid(),
        \ winrow: 1,
        \ wincol: 9,
        \ line: 1,
        \ column: 2,
        \ coladd: 0,
        \ }, getmousepos())
  call test_setmouse(1, 12)
  call assert_equal(#{
        \ screenrow: 1,
        \ screencol: 12,
        \ winid: win_getid(),
        \ winrow: 1,
        \ wincol: 12,
        \ line: 1,
        \ column: 2,
        \ coladd: 3,
        \ }, getmousepos())
  call test_setmouse(1, 25)
  call assert_equal(#{
        \ screenrow: 1,
        \ screencol: 25,
        \ winid: win_getid(),
        \ winrow: 1,
        \ wincol: 25,
        \ line: 1,
        \ column: 4,
        \ coladd: 0,
        \ }, getmousepos())
  call test_setmouse(1, 28)
  call assert_equal(#{
        \ screenrow: 1,
        \ screencol: 28,
        \ winid: win_getid(),
        \ winrow: 1,
        \ wincol: 28,
        \ line: 1,
        \ column: 7,
        \ coladd: 0,
        \ }, getmousepos())
  call test_setmouse(1, 29)
  call assert_equal(#{
        \ screenrow: 1,
        \ screencol: 29,
        \ winid: win_getid(),
        \ winrow: 1,
        \ wincol: 29,
        \ line: 1,
        \ column: 8,
        \ coladd: 0,
        \ }, getmousepos())
  call test_setmouse(1, 50)
  call assert_equal(#{
        \ screenrow: 1,
        \ screencol: 50,
        \ winid: win_getid(),
        \ winrow: 1,
        \ wincol: 50,
        \ line: 1,
        \ column: 8,
        \ coladd: 21,
        \ }, getmousepos())

  " If the mouse is positioned past the last buffer line, "line" and "column"
  " should act like it's positioned on the last buffer line.
  call test_setmouse(2, 25)
  call assert_equal(#{
        \ screenrow: 2,
        \ screencol: 25,
        \ winid: win_getid(),
        \ winrow: 2,
        \ wincol: 25,
        \ line: 1,
        \ column: 4,
        \ coladd: 0,
        \ }, getmousepos())
  call test_setmouse(2, 50)
  call assert_equal(#{
        \ screenrow: 2,
        \ screencol: 50,
        \ winid: win_getid(),
        \ winrow: 2,
        \ wincol: 50,
        \ line: 1,
        \ column: 8,
        \ coladd: 21,
        \ }, getmousepos())
  bwipe!
endfunc

func Test_getmouseshape()
  CheckFeature mouseshape

  call assert_equal('arrow', getmouseshape())
endfunc

" Test for glob()
func Test_glob()
  call assert_equal('', glob(test_null_string()))
  call assert_equal('', globpath(test_null_string(), test_null_string()))
  call assert_fails("let x = globpath(&rtp, 'syntax/c.vim', [])", 'E745:')

  call writefile([], 'Xglob1')
  call writefile([], 'XGLOB2')
  set wildignorecase
  " Sort output of glob() otherwise we end up with different
  " ordering depending on whether file system is case-sensitive.
  call assert_equal(['XGLOB2', 'Xglob1'], sort(glob('Xglob[12]', 0, 1)))
  " wildignorecase shall be applied even when the pattern contains no wildcards.
  call assert_equal('XGLOB2', glob('xglob2'))
  set wildignorecase&

  call delete('Xglob1')
  call delete('XGLOB2')

  call assert_fails("call glob('*', 0, {})", 'E728:')
endfunc

" Test for browse()
func Test_browse()
  CheckFeature browse
  call assert_fails('call browse([], "open", "x", "a.c")', 'E745:')
endfunc

" Test for browsedir()
func Test_browsedir()
  CheckFeature browse
  call assert_fails('call browsedir("open", [])', 'E730:')
endfunc

func HasDefault(msg = 'msg')
  return a:msg
endfunc

func Test_default_arg_value()
  call assert_equal('msg', HasDefault())
endfunc

" Test for gettext()
func Test_gettext()
  call assert_fails('call gettext(1)', 'E1174:')
endfunc

func Test_builtin_check()
  call assert_fails('let g:["trim"] = {x -> " " .. x}', 'E704:')
  call assert_fails('let g:.trim = {x -> " " .. x}', 'E704:')
  call assert_fails('let l:["trim"] = {x -> " " .. x}', 'E704:')
  call assert_fails('let l:.trim = {x -> " " .. x}', 'E704:')
  let lines =<< trim END
    vim9script
    var trim = (x) => " " .. x
  END
  call v9.CheckScriptFailure(lines, 'E704:')

  call assert_fails('call extend(g:, #{foo: { -> "foo" }})', 'E704:')
  let g:bar = 123
  call extend(g:, #{bar: { -> "foo" }}, "keep")
  call assert_fails('call extend(g:, #{bar: { -> "foo" }}, "force")', 'E704:')
  unlet g:bar

  call assert_fails('call extend(l:, #{foo: { -> "foo" }})', 'E704:')
  let bar = 123
  call extend(l:, #{bar: { -> "foo" }}, "keep")
  call assert_fails('call extend(l:, #{bar: { -> "foo" }}, "force")', 'E704:')
  unlet bar

  call assert_fails('call extend(g:, #{foo: function("extend")})', 'E704:')
  let g:bar = 123
  call extend(g:, #{bar: function("extend")}, "keep")
  call assert_fails('call extend(g:, #{bar: function("extend")}, "force")', 'E704:')
  unlet g:bar

  call assert_fails('call extend(l:, #{foo: function("extend")})', 'E704:')
  let bar = 123
  call extend(l:, #{bar: function("extend")}, "keep")
  call assert_fails('call extend(l:, #{bar: function("extend")}, "force")', 'E704:')
  unlet bar
endfunc

func Test_funcref_to_string()
  let Fn = funcref('g:Test_funcref_to_string')
  call assert_equal("function('g:Test_funcref_to_string')", string(Fn))
endfunc

" Test for isabsolutepath()
func Test_isabsolutepath()
  call assert_false(isabsolutepath(''))
  call assert_false(isabsolutepath('.'))
  call assert_false(isabsolutepath('../Foo'))
  call assert_false(isabsolutepath('Foo/'))
  if has('win32')
    call assert_true(isabsolutepath('A:\'))
    call assert_true(isabsolutepath('A:\Foo'))
    call assert_true(isabsolutepath('A:/Foo'))
    call assert_false(isabsolutepath('A:Foo'))
    call assert_false(isabsolutepath('\Windows'))
    call assert_true(isabsolutepath('\\Server2\Share\Test\Foo.txt'))
  else
    call assert_true(isabsolutepath('/'))
    call assert_true(isabsolutepath('/usr/share/'))
  endif
endfunc

" Test for exepath()
func Test_exepath()
  if has('win32')
    call assert_notequal(exepath('cmd'), '')

    let oldNoDefaultCurrentDirectoryInExePath = $NoDefaultCurrentDirectoryInExePath
    call writefile(['@echo off', 'echo Evil'], 'vim-test-evil.bat')
    let $NoDefaultCurrentDirectoryInExePath = ''
    call assert_notequal(exepath("vim-test-evil.bat"), '')
    let $NoDefaultCurrentDirectoryInExePath = '1'
    call assert_equal(exepath("vim-test-evil.bat"), '')
    let $NoDefaultCurrentDirectoryInExePath = oldNoDefaultCurrentDirectoryInExePath
    call delete('vim-test-evil.bat')
  else
    call assert_notequal(exepath('sh'), '')
  endif
endfunc

" Test for virtcol()
func Test_virtcol()
  new
  call setline(1, "the\tquick\tbrown\tfox")
  norm! 4|
  call assert_equal(8, virtcol('.'))
  call assert_equal(8, virtcol('.', v:false))
  call assert_equal([4, 8], virtcol('.', v:true))

  let w = winwidth(0)
  call setline(2, repeat('a', w + 2))
  let win_nosbr = win_getid()
  split
  setlocal showbreak=!!
  let win_sbr = win_getid()
  call assert_equal([w, w], virtcol([2, w], v:true, win_nosbr))
  call assert_equal([w + 1, w + 1], virtcol([2, w + 1], v:true, win_nosbr))
  call assert_equal([w + 2, w + 2], virtcol([2, w + 2], v:true, win_nosbr))
  call assert_equal([w, w], virtcol([2, w], v:true, win_sbr))
  call assert_equal([w + 3, w + 3], virtcol([2, w + 1], v:true, win_sbr))
  call assert_equal([w + 4, w + 4], virtcol([2, w + 2], v:true, win_sbr))
  close

  call assert_equal(0, virtcol(''))
  call assert_equal([0, 0], virtcol('', v:true))
  call assert_equal(0, virtcol('.', v:false, 5001))
  call assert_equal([0, 0], virtcol('.', v:true, 5001))

  bwipe!
endfunc

func Test_delfunc_while_listing()
  CheckRunVimInTerminal

  let lines =<< trim END
      set nocompatible
      for i in range(1, 999)
        exe 'func ' .. 'MyFunc' .. i .. '()'
        endfunc
      endfor
      au CmdlineLeave : call timer_start(0, {-> execute('delfunc MyFunc622')})
  END
  call writefile(lines, 'Xfunctionclear', 'D')
  let buf = RunVimInTerminal('-S Xfunctionclear', {'rows': 12})

  " This was using freed memory.  The height of the terminal must be so that
  " the next function to be listed with "j" is the one that is deleted in the
  " timer callback, tricky!
  call term_sendkeys(buf, ":func /MyFunc\<CR>")
  call TermWait(buf, 50)
  call term_sendkeys(buf, "j")
  call TermWait(buf, 50)
  call term_sendkeys(buf, "\<CR>")

  call StopVimInTerminal(buf)
endfunc

" Test for the reverse() function with a string
func Test_string_reverse()
  let lines =<< trim END
    call assert_equal('', reverse(test_null_string()))
    for [s1, s2] in [['', ''], ['a', 'a'], ['ab', 'ba'], ['abc', 'cba'],
                   \ ['abcd', 'dcba'], ['«-«-»-»', '»-»-«-«'],
                   \ ['🇦', '🇦'], ['🇦🇧', '🇧🇦'], ['🇦🇧🇨', '🇨🇧🇦'],
                   \ ['🇦«🇧-🇨»🇩', '🇩»🇨-🇧«🇦']]
      call assert_equal(s2, reverse(s1))
    endfor
  END
  call v9.CheckLegacyAndVim9Success(lines)

  " test in latin1 encoding
  let save_enc = &encoding
  set encoding=latin1
  call assert_equal('dcba', reverse('abcd'))
  let &encoding = save_enc
endfunc

func Test_fullcommand()
  " this used to crash vim
  call assert_equal('', fullcommand(10))
endfunc

" Test for glob() with shell special patterns
func Test_glob_extended_bash()
  CheckExecutable bash
  CheckNotMSWindows
  CheckNotMac   " The default version of bash is old on macOS.

  let _shell = &shell
  set shell=bash

  call mkdir('Xtestglob/foo/bar/src', 'p')
  call writefile([], 'Xtestglob/foo/bar/src/foo.sh')
  call writefile([], 'Xtestglob/foo/bar/src/foo.h')
  call writefile([], 'Xtestglob/foo/bar/src/foo.cpp')

  " Sort output of glob() otherwise we end up with different
  " ordering depending on whether file system is case-sensitive.
  let expected = ['Xtestglob/foo/bar/src/foo.cpp', 'Xtestglob/foo/bar/src/foo.h']
  call assert_equal(expected, sort(glob('Xtestglob/**/foo.{h,cpp}', 0, 1)))
  call delete('Xtestglob', 'rf')
  let &shell=_shell
endfunc

" Test for glob() with extended patterns (MS-Windows)
" Vim doesn't use 'shell' to expand wildcards on MS-Windows.
" Unlike bash, it doesn't support {,} expansion.
func Test_glob_extended_mswin()
  CheckMSWindows

  call mkdir('Xtestglob/foo/bar/src', 'p')
  call writefile([], 'Xtestglob/foo/bar/src/foo.sh')
  call writefile([], 'Xtestglob/foo/bar/src/foo.h')
  call writefile([], 'Xtestglob/foo/bar/src/foo.cpp')

  " Sort output of glob() otherwise we end up with different
  " ordering depending on whether file system is case-sensitive.
  let expected = ['Xtestglob/foo/bar/src/foo.cpp', 'Xtestglob/foo/bar/src/foo.h', 'Xtestglob/foo/bar/src/foo.sh']
  call assert_equal(expected, sort(glob('Xtestglob/**/foo.*', 0, 1)))
  call delete('Xtestglob', 'rf')
endfunc

" vim: shiftwidth=2 sts=2 expandtab