Skip to content

Commit 624b75a

Browse files
zzzyxwvutchrisbra
authored andcommitted
CI(screendump): Support iterative filtering for screendump comparison
Before two screendumps are compared for equality by calling "VerifyScreenDump()", parts of their contents can be omitted from comparison by executing arbitrary Vim commands written in a filter file that shares its basename with screendumps. Sometimes, such filtering can only be too general, as more context is required in order to decide what parts to touch. Two new arbitrary functions are therefore hooked in the body of "VerifyScreenDump()" for the purpose of probing into the current context and applying iterative filtering as needed. A paired-up public implementation of each function is also provided to expedite a workaround for #16559: ------------------------------------------------------------ source util/screendump.vim let opts = { \ 'FileComparisonPreAction': \ function('g:ScreenDumpDiscardFFFDChars'), \ 'NonEqualLineComparisonPostAction': \ function('g:ScreenDumpLookForFFFDChars'), \ } call g:VerifyScreenDump(buf, basename, opts) ------------------------------------------------------------ related: #17704 Signed-off-by: Aliaksei Budavei <[email protected]> Signed-off-by: Christian Brabandt <[email protected]>
1 parent 6432971 commit 624b75a

1 file changed

Lines changed: 101 additions & 7 deletions

File tree

src/testdir/util/screendump.vim

Lines changed: 101 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,83 @@ def ReadAndFilter(fname: string, filter: string): list<string>
2929
return contents
3030
enddef
3131

32+
" Accommodate rendering idiosyncrasies (see #16559). For details, refer to
33+
" "VerifyScreenDump()" and the "options" dictionary passed to it: this is
34+
" an implementation of its "FileComparisonPreAction" entry. (This function
35+
" runs in couples with "g:ScreenDumpLookForFFFDChars()".)
36+
def g:ScreenDumpDiscardFFFDChars(
37+
state: dict<number>,
38+
testdump: list<string>,
39+
refdump: list<string>)
40+
if empty(state) || len(testdump) != len(refdump)
41+
return
42+
endif
43+
for lstr: string in keys(state)
44+
const lnum: number = str2nr(lstr)
45+
const fst_fffd_idx: number = stridx(testdump[lnum], "\xef\xbf\xbd")
46+
# Retroactively discard non-equal line suffixes. It is assumed that no
47+
# runs of U+EFU+BFU+BD and no U+FFFDs are present in "refdump".
48+
if fst_fffd_idx >= 0
49+
# Mask the "||" character cells and the cursor cell ">.".
50+
const masked_part: string = substitute(
51+
substitute(
52+
strpart(testdump[lnum], 0, (fst_fffd_idx - 1)),
53+
'[>|]|', '|.', 'g'),
54+
'|\@<!>', '|', 'g')
55+
const prev_cell_idx: number = strridx(masked_part, '|')
56+
# A series of repeated characters will be found recorded in shorthand;
57+
# e.g. "|α@3" stands for a cell of four "α"s. Replacing any repeated
58+
# multibyte character of a series with a U+FFFD character will split the
59+
# series and its shorthand record will reflect this fact: "|α@2|�".
60+
# Therefore, a common prefix to share for two corresponding lines can
61+
# extend to either an ASCII character(s) cell before the leftmost U+FFFD
62+
# character cell; or, a last-but-one arbitrary cell before the leftmost
63+
# U+FFFD character cell; or, an empty string.
64+
const prefix: number = (prev_cell_idx >= 0)
65+
? (char2nr(strpart(masked_part, (prev_cell_idx + 1), 1), true) < 128)
66+
? fst_fffd_idx - 1
67+
: (strridx(masked_part, '|', (prev_cell_idx - 1)) >= 0)
68+
? prev_cell_idx
69+
: 0
70+
: 0
71+
refdump[lnum] = strpart(refdump[lnum], 0, prefix)
72+
testdump[lnum] = strpart(testdump[lnum], 0, prefix)
73+
endif
74+
endfor
75+
enddef
76+
77+
" Accommodate rendering idiosyncrasies (see #16559). For details, refer to
78+
" "VerifyScreenDump()" and the "options" dictionary passed to it: this is
79+
" an implementation of its "NonEqualLineComparisonPostAction" entry. (This
80+
" function runs in couples with "g:ScreenDumpDiscardFFFDChars()".)
81+
def g:ScreenDumpLookForFFFDChars(
82+
state: dict<number>,
83+
testdump: list<string>,
84+
lnum: number)
85+
if stridx(testdump[lnum], "\xef\xbf\xbd") >= 0
86+
state[string(lnum)] = 1
87+
endif
88+
enddef
3289

3390
" Verify that Vim running in terminal buffer "buf" matches the screen dump.
34-
" "options" is passed to term_dumpwrite().
35-
" Additionally, the "wait" entry can specify the maximum time to wait for the
36-
" screen dump to match in msec (default 1000 msec).
91+
"
92+
" A copy of "options" is passed to "term_dumpwrite()". For convenience, this
93+
" dictionary supports other optional entries:
94+
" "wait", (default to 1000 msec at least)
95+
" the maximum time to wait for the screen dump to match in msec.
96+
" "FileComparisonPreAction", (default to a no-op action)
97+
" some Funcref to call, passing the following three arguments, each time
98+
" before the file contents of two screen dumps are compared:
99+
" some dictionary with some state entries;
100+
" the file contents of the newly generated screen dump;
101+
" the file contents of the reference screen dump.
102+
" "NonEqualLineComparisonPostAction", (default to a no-op action)
103+
" some Funcref to call, passing the following three arguments, each time
104+
" after a corresponding pair of lines is found not equal:
105+
" some dictionary with some state entries;
106+
" the file contents of the newly generated screen dump;
107+
" the zero-based number of the line whose copies are not equal.
108+
"
37109
" The file name used is "dumps/{filename}.dump".
38110
"
39111
" To ignore part of the dump, provide a "dumps/{filename}.vim" file with
@@ -53,7 +125,24 @@ func VerifyScreenDump(buf, filename, options, ...)
53125
let filter = 'dumps/' . a:filename . '.vim'
54126
let testfile = 'failed/' . a:filename . '.dump'
55127

56-
let max_loops = get(a:options, 'wait', 1000) / 1
128+
let options_copy = copy(a:options)
129+
if has_key(options_copy, 'wait')
130+
let max_loops = max([0, remove(options_copy, 'wait')])
131+
else
132+
let max_loops = 1000
133+
endif
134+
if has_key(options_copy, 'FileComparisonPreAction')
135+
let FileComparisonPreAction = remove(options_copy, 'FileComparisonPreAction')
136+
let CopyStringList = {_refdump -> copy(_refdump)}
137+
else
138+
let FileComparisonPreAction = {_state, _testdump, _refdump -> 0}
139+
let CopyStringList = {_refdump -> _refdump}
140+
endif
141+
if has_key(options_copy, 'NonEqualLineComparisonPostAction')
142+
let NonEqualLineComparisonPostAction = remove(options_copy, 'NonEqualLineComparisonPostAction')
143+
else
144+
let NonEqualLineComparisonPostAction = {_state, _testdump, _lnum -> 0}
145+
endif
57146

58147
" Starting a terminal to make a screendump is always considered flaky.
59148
let g:test_is_flaky = 1
@@ -76,21 +165,25 @@ func VerifyScreenDump(buf, filename, options, ...)
76165
" Leave a bit of time for updating the original window while we spin wait.
77166
sleep 10m
78167
call delete(testfile)
79-
call term_dumpwrite(a:buf, testfile, a:options)
168+
call term_dumpwrite(a:buf, testfile, options_copy)
80169
call assert_report('See new dump file: call term_dumpload("testdir/' .. testfile .. '")')
81170
" No point in retrying.
82171
let g:run_nr = 10
83172
return 1
84173
endif
85174

86-
let refdump = ReadAndFilter(reference, filter)
175+
let refdump_orig = ReadAndFilter(reference, filter)
176+
let state = {}
87177
let i = 0
88178
while 1
89179
" Leave a bit of time for updating the original window while we spin wait.
90180
sleep 1m
91181
call delete(testfile)
92-
call term_dumpwrite(a:buf, testfile, a:options)
182+
call term_dumpwrite(a:buf, testfile, options_copy)
183+
" Filtering done with "FileComparisonPreAction()" may change "refdump*".
184+
let refdump = CopyStringList(refdump_orig)
93185
let testdump = ReadAndFilter(testfile, filter)
186+
call FileComparisonPreAction(state, testdump, refdump)
94187
if refdump == testdump
95188
call delete(testfile)
96189
if did_mkdir
@@ -116,6 +209,7 @@ func VerifyScreenDump(buf, filename, options, ...)
116209
endif
117210
if testdump[j] != refdump[j]
118211
let msg = msg . '; difference in line ' . (j + 1) . ': "' . testdump[j] . '"'
212+
call NonEqualLineComparisonPostAction(state, testdump, j)
119213
endif
120214
endfor
121215

0 commit comments

Comments
 (0)