summaryrefslogtreecommitdiff
path: root/mnv/runtime/syntax/testdir/runtest.mnv
diff options
context:
space:
mode:
authorMehmet Samet Duman <yongdohyun@projecttick.org>2026-04-04 12:41:27 +0300
committerMehmet Samet Duman <yongdohyun@projecttick.org>2026-04-04 12:41:27 +0300
commit4f2d36194b4f299aa7509d815c07121039ea833b (patch)
treef3ded014bad3a4c76ff6a22b8726ebaab68c3d13 /mnv/runtime/syntax/testdir/runtest.mnv
parent5b578e70c314723a3cde5c9bfc2be0bf1dadc93b (diff)
downloadProject-Tick-4f2d36194b4f299aa7509d815c07121039ea833b.tar.gz
Project-Tick-4f2d36194b4f299aa7509d815c07121039ea833b.zip
NOISSUE change uvim folder name to mnv
Signed-off-by: Mehmet Samet Duman <yongdohyun@projecttick.org>
Diffstat (limited to 'mnv/runtime/syntax/testdir/runtest.mnv')
-rw-r--r--mnv/runtime/syntax/testdir/runtest.mnv623
1 files changed, 623 insertions, 0 deletions
diff --git a/mnv/runtime/syntax/testdir/runtest.mnv b/mnv/runtime/syntax/testdir/runtest.mnv
new file mode 100644
index 0000000000..a09c6f1e1d
--- /dev/null
+++ b/mnv/runtime/syntax/testdir/runtest.mnv
@@ -0,0 +1,623 @@
+" Runs all the syntax tests for which there is no "done/name" file.
+"
+" Current directory must be runtime/syntax.
+
+" needed because of line-continuation lines
+set cpo&mnv
+
+" Only do this with the +eval feature
+if 1
+
+" Remember the directory where we started. Will change to "testdir" below.
+let syntaxDir = getcwd()
+
+let s:messagesFname = fnameescape(syntaxDir .. '/testdir/messages')
+
+let s:messages = []
+
+" Erase the cursor line and do not advance the cursor. (Call the function
+" after each passing test report.)
+def EraseLineAndReturnCarriage(line: string)
+ const full_width: number = winwidth(0)
+ const half_width: number = full_width - (full_width + 1) / 2
+ if strlen(line) > half_width
+ echon "\r" .. repeat("\x20", full_width) .. "\r"
+ else
+ echon repeat("\x20", half_width) .. "\r"
+ endif
+enddef
+
+" Add one message to the list of messages
+func Message(msg)
+ echomsg a:msg
+ call add(s:messages, a:msg)
+endfunc
+
+" Report a fatal message and exit
+func Fatal(msg)
+ echoerr a:msg
+ call AppendMessages(a:msg)
+ qall!
+endfunc
+
+" Append s:messages to the messages file and make it empty.
+func AppendMessages(header)
+ silent exe 'split ' .. s:messagesFname
+ call append(line('$'), '')
+ call append(line('$'), a:header)
+ call append(line('$'), s:messages)
+ let s:messages = []
+ silent wq
+endfunc
+
+" Relevant messages are written to the "messages" file.
+" If the file already exists it is appended to.
+silent exe 'split ' .. s:messagesFname
+call append(line('$'), repeat('=-', 70))
+call append(line('$'), '')
+let s:test_run_message = 'Test run on ' .. strftime("%Y %b %d %H:%M:%S")
+call append(line('$'), s:test_run_message)
+silent wq
+
+if syntaxDir !~ '[/\\]runtime[/\\]syntax\>'
+ call Fatal('Current directory must be "runtime/syntax"')
+endif
+if !isdirectory('testdir')
+ call Fatal('"testdir" directory not found')
+endif
+
+" Use the script for source code screendump testing. It sources other scripts,
+" therefore we must "cd" there.
+cd ../../src/testdir
+
+let s:mnvcmdSyntaxFname = fnameescape(syntaxDir .. '/testdir/mnvcmd')
+
+" Adapt "runtime/syntax/testdir/mnvcmd" for "src/testdir/util/shared.mnv".
+if filereadable(s:mnvcmdSyntaxFname)
+ call delete('mnvcmd')
+ call filecopy(s:mnvcmdSyntaxFname, 'mnvcmd')
+ exe 'au ExitPre <buffer> call delete("' .. fnameescape(getcwd() .. '/mnvcmd') .. '")'
+endif
+
+source util/screendump.mnv
+source util/term_util.mnv
+exe 'cd ' .. fnameescape(syntaxDir)
+
+" For these tests we need to be able to run terminal MNV with 256 colors. On
+" MS-Windows the console only has 16 colors and the GUI can't run in a
+" terminal.
+if !CanRunMNVInTerminal()
+ call Fatal('Cannot make screendumps, aborting')
+endif
+
+cd testdir
+
+if !isdirectory('done')
+ call mkdir('done')
+endif
+
+if !isdirectory('failed')
+ call mkdir('failed')
+endif
+
+set nocp
+set nowrapscan
+set report=9999
+set modeline
+set debug=throw
+set nomore
+
+au! SwapExists * call HandleSwapExists()
+func HandleSwapExists()
+ " Ignore finding a swap file for the test input, the user might be editing
+ " it and that's OK.
+ if expand('<afile>') =~ 'input[/\\].*\..*'
+ let v:swapchoice = 'e'
+ endif
+endfunc
+
+" Trace ruler liveness on demand.
+if !empty($MNV_SYNTAX_TEST_LOG) && filewritable($MNV_SYNTAX_TEST_LOG)
+ def s:TraceRulerLiveness(context: string, times: number, tail: string)
+ writefile([printf('%s: %4d: %s', context, times, tail)],
+ $MNV_SYNTAX_TEST_LOG,
+ 'a')
+ enddef
+else
+ def s:TraceRulerLiveness(_: string, _: number, _: string)
+ enddef
+endif
+
+" See ":help 'ruler'".
+def s:CannotSeeLastLine(ruler: list<string>): bool
+ return !(get(ruler, -1, '') ==# 'All' || get(ruler, -1, '') ==# 'Bot')
+enddef
+
+def s:CannotDumpNextPage(buf: number, prev_ruler: list<string>, ruler: list<string>): bool
+ return !(ruler !=# prev_ruler &&
+ len(ruler) == 2 &&
+ ruler[1] =~# '\%(\d%\|\<Bot\)$' &&
+ get(term_getcursor(buf), 0) != 20)
+enddef
+
+def s:CannotDumpFirstPage(buf: number, _: list<string>, ruler: list<string>): bool
+ return !(len(ruler) == 2 &&
+ ruler[1] =~# '\%(\<All\|\<Top\)$' &&
+ get(term_getcursor(buf), 0) != 20)
+enddef
+
+def s:CannotDumpShellFirstPage(buf: number, _: list<string>, ruler: list<string>): bool
+ return !(len(ruler) > 3 &&
+ get(ruler, -1, '') =~# '\%(\<All\|\<Top\)$' &&
+ get(term_getcursor(buf), 0) != 20)
+enddef
+
+" Poll for updates of the cursor position in the terminal buffer occupying the
+" first window. (ALWAYS call the function or its equivalent before calling
+" "VerifyScreenDump()" *and* after calling any number of "term_sendkeys()".)
+def s:TermPollRuler(
+ CannotDumpPage: func, # (TYPE FOR LEGACY CONTEXT CALL SITES.)
+ buf: number,
+ in_name_and_out_name: string): list<string>
+ # Expect defaults from "term_util#RunMNVInTerminal()".
+ if winwidth(1) != 75 || winheight(1) != 20
+ ch_log(printf('Aborting for %s: (75 x 20) != (%d x %d)',
+ in_name_and_out_name,
+ winwidth(1),
+ winheight(1)))
+ return ['0,0-1', 'All']
+ endif
+ # A two-fold role for redrawing:
+ # (*) in case the terminal buffer cannot redraw itself just yet;
+ # (*) to avoid extra "real estate" checks.
+ redraw
+ # The contents of "ruler".
+ var ruler: list<string> = []
+ # Attempts at most, targeting ASan-instrumented MNV builds.
+ var times: number = 2048
+ # Check "real estate" of the terminal buffer. Read and compare its ruler
+ # line and let "Xtestscript#s:AssertCursorForwardProgress()" do the rest.
+ # Note that the cursor ought to be advanced after each successive call of
+ # this function yet its relative position need not be changed (e.g. "0%").
+ while CannotDumpPage(ruler) && times > 0
+ ruler = split(term_getline(buf, 20))
+ sleep 1m
+ times -= 1
+ if times % 8 == 0
+ redraw
+ endif
+ endwhile
+ TraceRulerLiveness('P', (2048 - times), in_name_and_out_name)
+ return ruler
+enddef
+
+" Prevent "s:TermPollRuler()" from prematurely reading the cursor position,
+" which is available at ":edit", after outracing the loading of syntax etc. in
+" the terminal buffer. (Call the function before calling "VerifyScreenDump()"
+" for the first time.)
+def s:TermWaitAndPollRuler(buf: number, in_name_and_out_name: string): list<string>
+ # Expect defaults from "term_util#RunMNVInTerminal()".
+ if winwidth(1) != 75 || winheight(1) != 20
+ ch_log(printf('Aborting for %s: (75 x 20) != (%d x %d)',
+ in_name_and_out_name,
+ winwidth(1),
+ winheight(1)))
+ return ['0,0-1', 'All']
+ endif
+ # The contents of "ruler".
+ var ruler: string = ''
+ # Attempts at most, targeting ASan-instrumented MNV builds.
+ var times: number = 32768
+ # Check "real estate" of the terminal buffer. Expect a known token to be
+ # rendered in the terminal buffer; its prefix must be "is_" so that buffer
+ # variables from "sh.mnv" can be matched (see "Xtestscript#ShellInfo()").
+ # Verify that the whole line is available!
+ while ruler !~# '^is_.\+\s\%(All\|Top\)$' && times > 0
+ ruler = term_getline(buf, 20)
+ sleep 1m
+ times -= 1
+ if times % 16 == 0
+ redraw
+ endif
+ endwhile
+ TraceRulerLiveness('W', (32768 - times), in_name_and_out_name)
+ if strpart(ruler, 0, 8) !=# 'is_nonce'
+ # Retain any of "b:is_(bash|dash|kornshell|posix|sh)" entries and let
+ # "CannotDumpShellFirstPage()" win the cursor race.
+ return TermPollRuler(
+ function(CannotDumpShellFirstPage, [buf, []]),
+ buf,
+ in_name_and_out_name)
+ else
+ # Clear the "is_nonce" token and let "CannotDumpFirstPage()" win any
+ # race.
+ term_sendkeys(buf, ":redraw!\<CR>")
+ endif
+ return TermPollRuler(
+ function(CannotDumpFirstPage, [buf, []]),
+ buf,
+ in_name_and_out_name)
+enddef
+
+func RunTest()
+ let XTESTSCRIPT =<< trim END
+ " Track the cursor progress through a syntax test file so that any
+ " degenerate input can be reported. Each file will have its own cursor.
+ let s:cursor = 1
+
+ " extra info for shell variables
+ func ShellInfo()
+ let msg = ''
+ for [key, val] in items(b:)
+ if key =~ '^is_'
+ let msg ..= key .. ': ' .. val .. ', '
+ endif
+ endfor
+ if msg != ''
+ echomsg msg
+ endif
+ endfunc
+
+ au! SwapExists * call HandleSwapExists()
+ func HandleSwapExists()
+ " Ignore finding a swap file for the test input, the user might be
+ " editing it and that's OK.
+ if expand('<afile>') =~ 'input[/\\].*\..*'
+ let v:swapchoice = 'e'
+ endif
+ endfunc
+
+ func LoadFiletype(type)
+ for file in glob("ftplugin/" .. a:type .. "*.mnv", 1, 1)
+ exe "source " .. file
+ endfor
+ redraw!
+ endfunc
+
+ func SetUpMNV()
+ call cursor(1, 1)
+ " Defend against rogue MNV_TEST_SETUP commands.
+ for _ in range(20)
+ let lnum = search('\C\<MNV_TEST_SETUP\>', 'eW', 20)
+ if lnum < 1
+ break
+ endif
+ exe substitute(getline(lnum), '\C.*\<MNV_TEST_SETUP\>', '', '')
+ endfor
+ call cursor(1, 1)
+ " BEGIN [runtime/defaults.mnv]
+ " Also, disable italic highlighting to avoid issues on some terminals.
+ set display=lastline ruler scrolloff=5 t_ZH= t_ZR=
+ syntax on
+ " END [runtime/defaults.mnv]
+ redraw!
+ endfunc
+
+ def s:AssertCursorForwardProgress(): bool
+ const curnum: number = line('.')
+ if curnum <= cursor
+ # Use "actions/upload-artifact@v4" of ci.yml for delivery.
+ writefile([printf('No cursor progress: %d <= %d (%s). Please file an issue.',
+ curnum,
+ cursor,
+ bufname('%'))],
+ 'failed/00-FIXME',
+ 'a')
+ bwipeout!
+ endif
+ cursor = curnum
+ return true
+ enddef
+
+ def ScrollToSecondPage(estate: number, op_wh: number, op_so: number): bool
+ if line('.') != 1 || line('w$') >= line('$')
+ return AssertCursorForwardProgress()
+ endif
+ try
+ set scrolloff=0
+ # Advance mark "c"[ursor] along with the cursor.
+ norm! Lmc
+ if foldclosed('.') < 0 &&
+ (strdisplaywidth(getline('.')) + &l:fdc * winheight(1)) >= estate
+ # Make for an exit for a screenful long line.
+ norm! j^
+ return AssertCursorForwardProgress()
+ else
+ # Place the cursor on the actually last visible line.
+ while winline() < op_wh
+ const lastnum: number = winline()
+ norm! gjmc
+ if lastnum > winline()
+ break
+ endif
+ endwhile
+ norm! zt
+ endif
+ finally
+ # COMPATIBILITY: Scroll up around "scrolloff" lines.
+ &scrolloff = max([1, op_so])
+ endtry
+ norm! ^
+ return AssertCursorForwardProgress()
+ enddef
+
+ def ScrollToNextPage(estate: number, op_wh: number, op_so: number): bool
+ if line('.') == 1 || line('w$') >= line('$')
+ return AssertCursorForwardProgress()
+ endif
+ try
+ set scrolloff=0
+ # Advance mark "c"[ursor] along with the cursor.
+ norm! Lmc
+ if foldclosed('.') < 0 &&
+ (strdisplaywidth(getline('.')) + &l:fdc * winheight(1)) >= estate
+ # Make for an exit for a screenful long line.
+ norm! j^
+ return AssertCursorForwardProgress()
+ else
+ # Place the cursor on the actually last visible line.
+ while winline() < op_wh
+ const lastnum: number = winline()
+ norm! gjmc
+ if lastnum > winline()
+ break
+ endif
+ endwhile
+ endif
+ finally
+ # COMPATIBILITY: Scroll up/down around "scrolloff" lines.
+ &scrolloff = max([1, op_so])
+ endtry
+ norm! zt
+ const marknum: number = line("'c")
+ # Eschew &smoothscroll since line("`c") is not supported.
+ # Remember that "w0" can point to the first line of a _closed_ fold
+ # whereas the last line of a _closed_ fold can be marked.
+ if line('w0') > marknum
+ while line('w0') > marknum
+ exe "norm! \<C-y>"
+ endwhile
+ if line('w0') != marknum
+ exe "norm! \<C-e>H"
+ endif
+ # Handle non-wrapped lines.
+ elseif line('w0') < marknum
+ while line('w0') < marknum
+ exe "norm! \<C-e>"
+ endwhile
+ if line('w0') != marknum
+ exe "norm! \<C-y>H"
+ endif
+ endif
+ norm! ^
+ return AssertCursorForwardProgress()
+ enddef
+ END
+ let MAX_FAILED_COUNT = 5
+ let DUMP_OPTS = exists("$MNV_SYNTAX_TEST_WAIT_TIME") &&
+ \ !empty($MNV_SYNTAX_TEST_WAIT_TIME)
+ \ ? {'wait': max([1, str2nr($MNV_SYNTAX_TEST_WAIT_TIME)])}
+ \ : {}
+ lockvar DUMP_OPTS MAX_FAILED_COUNT XTESTSCRIPT
+ let ok_count = 0
+ let disused_pages = []
+ let failed_tests = []
+ let skipped_count = 0
+ let last_test_status = 'invalid'
+ let filter = ''
+ " Create a map of setup configuration filenames with their basenames as keys.
+ let setup = glob('input/setup/*.mnv', 1, 1)
+ \ ->reduce({d, f -> extend(d, {fnamemodify(f, ':t:r'): f})}, {})
+ " Turn a subset of filenames etc. requested for testing into a pattern.
+ if filereadable('../testdir/Xfilter')
+ let filter = readfile('../testdir/Xfilter')
+ \ ->map({_, v -> '^' .. escape(substitute(v, '_$', '', ''), '.')})
+ \ ->join('\|')
+ call delete('../testdir/Xfilter')
+ endif
+
+ " Treat "^self-testing" as a string NOT as a regexp.
+ if filter ==# '^self-testing'
+ let dirpath = 'input/selftestdir/'
+ let fnames = readdir(dirpath, {fname -> fname !~ '^README\.txt$'})
+ else
+ let dirpath = 'input/'
+ let filter ..= exists("$MNV_SYNTAX_TEST_FILTER") &&
+ \ !empty($MNV_SYNTAX_TEST_FILTER)
+ \ ? (empty(filter) ? '' : '\|') .. $MNV_SYNTAX_TEST_FILTER
+ \ : ''
+ let fnames = readdir(dirpath,
+ \ {subset -> {fname -> fname !~ '\~$' && fname =~# subset}}(
+ \ empty(filter) ? '^.\+\..\+$' : filter))
+ endif
+
+ for fname in fnames
+ let root = fnamemodify(fname, ':r')
+ let fname = dirpath .. fname
+
+ " Execute the test if the "done" file does not exist or when the input file
+ " is newer.
+ let in_time = getftime(fname)
+ let out_time = getftime('done/' .. root)
+ if out_time < 0 || in_time > out_time
+ call ch_log('running tests for: ' .. fname)
+ let filetype = substitute(root, '\([^_.]*\)[_.].*', '\1', '')
+ let failed_root = 'failed/' .. root
+
+ for pagename in glob(failed_root .. '_\d*\.dump', 1, 1)
+ call delete(pagename)
+ endfor
+ call delete('done/' .. root)
+ call writefile(XTESTSCRIPT, 'Xtestscript')
+
+ " close all but the last window
+ while winnr('$') > 1
+ close
+ endwhile
+
+ " Redraw to make sure that messages are cleared and there is enough space
+ " for the terminal window.
+ redraw
+
+ " Let "Xtestscript#SetUpMNV()" turn the syntax on.
+ let prefix = '-Nu NONE -S Xtestscript'
+ let path = get(setup, root, '')
+ " Source the found setup configuration file.
+ let args = !empty(path)
+ \ ? prefix .. ' -S ' .. path
+ \ : prefix
+ let buf = RunMNVInTerminal(args, {})
+ " edit the file only after catching the SwapExists event
+ call term_sendkeys(buf, ":edit " .. fname .. "\<CR>")
+ " set up the testing environment
+ call term_sendkeys(buf, ":call SetUpMNV()\<CR>")
+ " load filetype specific settings
+ call term_sendkeys(buf, ":call LoadFiletype('" .. filetype .. "')\<CR>")
+
+ " Make a synchronisation point between buffers by requesting to echo
+ " a known token in the terminal buffer and asserting its availability
+ " with "s:TermWaitAndPollRuler()".
+ if filetype == 'sh'
+ call term_sendkeys(buf, ":call ShellInfo()\<CR>")
+ else
+ call term_sendkeys(buf, ":echo 'is_nonce'\<CR>")
+ endif
+
+ let root_00 = root .. '_00'
+ let in_name_and_out_name = fname .. ': failed/' .. root_00 .. '.dump'
+ " Queue up all "term_sendkeys()"es and let them finish before returning
+ " from "s:TermWaitAndPollRuler()".
+ let ruler = s:TermWaitAndPollRuler(buf, in_name_and_out_name)
+ call ch_log('First screendump for ' .. in_name_and_out_name)
+ " Make a screendump at the start of the file: failed/root_00.dump
+ let fail = VerifyScreenDump(buf, root_00, DUMP_OPTS)
+ let page_nr = 0
+
+ " Accommodate the next code block to "buf"'s contingency for self
+ " wipe-out.
+ try
+ let keys_a = ":call ScrollToSecondPage((18 * 75 + 1), 19, 5) | redraw!\<CR>"
+ let keys_b = ":call ScrollToNextPage((18 * 75 + 1), 19, 5) | redraw!\<CR>"
+ while s:CannotSeeLastLine(ruler)
+ call term_sendkeys(buf, keys_a)
+ let keys_a = keys_b
+ let page_nr += 1
+ let root_next = printf('%s_%02d', root, page_nr)
+ let in_name_and_out_name = fname .. ': failed/' .. root_next .. '.dump'
+ let ruler = s:TermPollRuler(
+ \ function('s:CannotDumpNextPage', [buf, ruler]),
+ \ buf,
+ \ in_name_and_out_name)
+ call ch_log('Next screendump for ' .. in_name_and_out_name)
+ " Make a screendump of every 18 lines of the file: failed/root_NN.dump
+ let fail += VerifyScreenDump(buf, root_next, DUMP_OPTS)
+ endwhile
+ call StopMNVInTerminal(buf)
+ finally
+ call delete('Xtestscript')
+ endtry
+
+ let page_nr += 1
+ let pagename = printf('dumps/%s_%02d.dump', root, page_nr)
+
+ while filereadable(pagename)
+ call add(disused_pages, pagename)
+ let page_nr += 1
+ let pagename = printf('dumps/%s_%02d.dump', root, page_nr)
+ endwhile
+
+ " redraw here to avoid the following messages to get mixed up with screen
+ " output.
+ redraw
+
+ " Add any assert errors to s:messages.
+ if len(v:errors) > 0
+ call extend(s:messages, v:errors)
+ if last_test_status == 'passed'
+ call EraseLineAndReturnCarriage('Test ' .. root .. ' OK')
+ else
+ echon "\n"
+ endif
+ " Echo the errors here, in case the script aborts or the "messages" file
+ " is not displayed later.
+ echomsg v:errors
+ let v:errors = []
+ let fail += 1
+ endif
+
+ if fail == 0
+ if last_test_status == 'skipped'
+ echon "\n"
+ endif
+ let last_test_status = 'passed'
+ let msg = "Test " .. root .. " OK"
+ call Message(msg)
+ call EraseLineAndReturnCarriage(msg)
+
+ call writefile(['OK'], 'done/' .. root)
+
+ let ok_count += 1
+ else
+ let last_test_status = 'failed'
+ call Message("Test " .. root .. " FAILED")
+ echon "\n"
+
+ call delete('done/' .. root)
+
+ eval failed_tests->add(root)
+ if len(failed_tests) > MAX_FAILED_COUNT
+ call Message('')
+ call Message('Too many errors, aborting')
+ endif
+ endif
+ else
+ if last_test_status == 'passed'
+ call EraseLineAndReturnCarriage('Test ' .. root .. ' OK')
+ endif
+ let last_test_status = 'skipped'
+ call Message("Test " .. root .. " skipped")
+ let skipped_count += 1
+ endif
+
+ " Append messages to the file "testdir/messages"
+ call AppendMessages('Input file ' .. fname .. ':')
+
+ if len(failed_tests) > MAX_FAILED_COUNT
+ break
+ endif
+ endfor
+
+ if last_test_status == 'passed' && exists('root')
+ call EraseLineAndReturnCarriage('Test ' .. root .. ' OK')
+ endif
+
+ call Message(s:test_run_message)
+ call Message('OK: ' .. ok_count)
+ call Message('FAILED: ' .. len(failed_tests) .. ': ' .. string(failed_tests))
+ call Message('skipped: ' .. skipped_count)
+
+ for pagename in disused_pages
+ call Message(printf('No input page found for "%s"', pagename))
+ endfor
+
+ if !empty(failed_tests)
+ call Message('')
+ call Message('View generated screendumps with "../../src/mnv --clean -S testdir/viewdumps.mnv"')
+ endif
+
+ call AppendMessages('== SUMMARY SYNTAX TESTS ==')
+
+ if len(failed_tests) > 0
+ " have make report an error
+ cquit
+ endif
+endfunc
+
+call RunTest()
+
+" Matching "if 1" at the start.
+endif
+
+qall!
+
+" mnv:sw=2:ts=8:noet: