test: add generic testing framework

This commit is contained in:
Lewis Russell 2024-07-04 10:48:10 +01:00 committed by Lewis Russell
parent 02eefe50e0
commit 1ac390aded
7 changed files with 397 additions and 206 deletions

View file

@ -41,5 +41,21 @@ if-statement spans multiple lines.
A pull request for supporting a new language requires:
1. Adding `queries/[LANG]/context.scm` as explained in the previous section.
2. Adding `test/test.[LANG EXT]` with code examples the `context.scm` is designed to support.
2. Adding `test/lang/test.[LANG]` or `test/lang/test.[LANG].[EXT]` with code examples the `context.scm` is designed to support.
- These test files use custom comment directives to annotate what lines should be a context. It has the format.
```c
// {{TEST}} -- mark start of test
int main() { // {{CONTEXT}} -- mark line as a context
// {{CURSOR}} -- where cursor needs to be for contexts to be shown.
}
```
See `test/lang/test.c` for examples.
3. Updating `README.md` to mark `[LANG]` as supported.

View file

@ -1,96 +1,79 @@
#!/bin/bash
foo() {
if [ 1 -eq 1 ]; then
# {{TEST}}
foo() { # {{CONTEXT}}
if [ 1 -eq 1 ]; then # {{CONTEXT}}
echo 1
# {{CURSOR}}
fi
case "$i" in
1) echo 1
}
# {{TEST}}
bar() { # {{CONTEXT}}
case "$i" in # {{CONTEXT}}
1) echo 1 # {{CONTEXT}}
# {{CURSOR}}
;;
2|3) echo 2 or 3
;;
*) echo default
;;
esac
while [ $x -le 5 ]
do
echo "Welcome $x times"
x=$(( $x + 1 ))
done
# until is also a while loop
until [ $x -gt 5 ]
do
echo Counter: $x
((x++))
done
# select is a for statement
select character in Sheldon Leonard Penny Howard Raj
do
echo "Selected character: $character"
echo "Selected number: $REPLY"
done
for ((i=0; i<=10000; i++)); do
echo "$i"
done
}
# {{TEST}}
baz() { # {{CONTEXT}}
while [ $x -le 5 ] # {{CONTEXT}}
do
echo "Welcome $x times"
x=$(( $x + 1 ))
# {{CURSOR}}
done
}
# {{TEST}}
baz2() { # {{CONTEXT}}
# until is also a while loop
until [ $x -gt 5 ] # {{CONTEXT}}
do
echo Counter: $x
((x++))
# {{CURSOR}}
done
}
# {{TEST}}
# select is a for statement
select character in Sheldon Leonard Penny Howard Raj # {{CONTEXT}}
do
echo "Selected character: $character"
# {{CURSOR}}
done
# {{POPCONTEXT}}
for ((i=0; i<=10000; i++)); do # {{CONTEXT}}
# {{CURSOR}}
echo "$i"
done

View file

@ -1,148 +1,90 @@
struct Bert {
// {{TEST}}
struct Bert { // {{CONTEXT}}
int *f1;
// comment
int *f2;
// comment
// comment
// comment
// comment
// comment
// {{CURSOR}}
};
typedef enum {
// {{TEST}}
typedef enum { // {{CONTEXT}}
E1,
E2,
E3
// comment
// comment
// comment
// comment
// comment
// comment
// {{CURSOR}}
} Myenum;
int main(int arg1,
// {{TEST}}
int main(int arg1, // {{CONTEXT}}
char **arg2,
char **arg3
)
char **arg3)
{
if (arg1 == 4
if (arg1 == 4 // {{CONTEXT}}
&& arg2 == arg3) {
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
for (int i = 0; i < arg1; i++) {
// comment
// comment
// comment
// comment
while (1) {
// comment
// comment
// comment
// comment
// comment
// {{CURSOR}}
for (int i = 0; i < arg1; i++) { // {{CONTEXT}}
// {{CURSOR}}
while (1) { // {{CONTEXT}}
// {{CURSOR}}
}
do {
// comment
// comment
// comment
// comment
// comment
} while (1);
// comment
// comment
// comment
// comment
// comment
}
} else if (arg1 == 4) {
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
} else {
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
}
}
switch (arg1) {
// comment
// {{TEST}}
void foo(int a) { // {{CONTEXT}}
if (a) { // {{CONTEXT}}
do { // {{CONTEXT}}
// {{CURSOR}}
} while (1);
}
}
// {{TEST}}
void bar(int a) { // {{CONTEXT}}
if (a) { // {{CONTEXT}}
} else if (a == 4) { // {{CONTEXT}}
// comment
} else { // {{CONTEXT}}
// {{CURSOR}}
}
}
// {{TEST}}
void baz(int a) { // {{CONTEXT}}
switch (a) { // {{CONTEXT}}
case 0:
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
break;
case 1: {
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
case 1: { // {{CONTEXT}}
// {{CURSOR}}
} break;
}
}

13
test/lang/test.lua Normal file
View file

@ -0,0 +1,13 @@
-- {{TEST}}
local function foo() -- {{CONTEXT}}
local function bar() -- {{CONTEXT}}
-- {{CURSOR}}
end
end

148
test/test.c Normal file
View file

@ -0,0 +1,148 @@
struct Bert {
int *f1;
// comment
int *f2;
// comment
// comment
// comment
// comment
// comment
};
typedef enum {
E1,
E2,
E3
// comment
// comment
// comment
// comment
// comment
// comment
} Myenum;
int main(int arg1,
char **arg2,
char **arg3
)
{
if (arg1 == 4
&& arg2 == arg3) {
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
for (int i = 0; i < arg1; i++) {
// comment
// comment
// comment
// comment
while (1) {
// comment
// comment
// comment
// comment
// comment
}
do {
// comment
// comment
// comment
// comment
// comment
} while (1);
// comment
// comment
// comment
// comment
// comment
}
} else if (arg1 == 4) {
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
} else {
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
}
switch (arg1) {
// comment
// comment
case 0:
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
break;
case 1: {
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
// comment
} break;
}
}

View file

@ -5,6 +5,7 @@ local clear = helpers.clear
local exec_lua = helpers.exec_lua
local cmd = helpers.api.nvim_command
local feed = helpers.feed
local api = helpers.api
local function install_langs(langs)
if type(langs) == 'string' then
@ -22,7 +23,52 @@ local function install_langs(langs)
]], langs)
end
local function get_langs()
---@param line string
---@return string?
local function parse_directive(line)
--- @type string?
local directive = line:match('{{([A-Z]+)}}')
return directive
end
--- @param filename string
--- @return table<integer, integer[]>? contexts
local function parse_directives(filename)
local f = io.open(filename, 'r')
if not f then
return
end
local context = {} --- @type table<integer,integer[]>
local contexts = {} --- @type table<integer,integer[]>
local i = 0
for l in f:lines() do
local directive = parse_directive(l)
if directive then
if directive == 'TEST' then
context = {}
elseif directive == 'CURSOR' then
contexts[i] = vim.deepcopy(context)
elseif directive == 'CONTEXT' then
table.insert(context, i)
elseif directive == 'POPCONTEXT' then
table.remove(context, #context)
end
end
i = i + 1
end
f:close()
for _, c in pairs(contexts) do
table.sort(c)
end
return contexts
end
local langs = {} --- @type string[]
do
local f = assert(io.open('README.md', 'r'))
local readme_langs = {} --- @type table<string,true>
for l in f:lines() do
@ -35,18 +81,16 @@ local function get_langs()
f:close()
f = assert(io.open('nvim-treesitter/lockfile.json', 'r'))
local txt = f:read('*a')
local j = vim.json.decode(txt)
local langs = {} --- @type string[]
for k in pairs(j) do
for k in pairs(vim.json.decode(f:read('*a'))) do
if readme_langs[k] then
langs[#langs+1] = k
readme_langs[k] = nil
end
end
print('Invalid languages:', table.concat(vim.tbl_keys(readme_langs), ', '))
return langs
if next(readme_langs) then
print('Invalid languages:', table.concat(vim.tbl_keys(readme_langs), ', '))
end
end
describe('ts_context', function()
@ -91,7 +135,7 @@ describe('ts_context', function()
it('edit a file', function()
install_langs('lua')
exec_lua[[require'treesitter-context'.setup{}]]
cmd('edit test/lang/test_file.lua')
cmd('edit test/test_file.lua')
exec_lua [[vim.treesitter.start()]]
feed'<C-e>'
feed'jj'
@ -141,7 +185,7 @@ describe('ts_context', function()
f:close()
end)
for _, lang in ipairs(get_langs()) do
for _, lang in ipairs(langs) do
it(lang, function()
install_langs(lang)
@ -182,6 +226,51 @@ describe('ts_context', function()
end)
describe('contexts:', function()
for _, lang in ipairs(langs) do
it(lang, function()
install_langs(lang)
local test_file = 'test/lang/test.'..lang
if not vim.uv.fs_stat(test_file) then
pending('No test file')
return
end
local contexts = parse_directives(test_file)
if not contexts or not next(contexts) then
pending('No tests')
return
end
cmd('edit '..test_file)
for cursor_row, context_rows in pairs(contexts) do
local bufnr = api.nvim_get_current_buf()
local winid = api.nvim_get_current_win()
api.nvim_win_set_cursor(winid, {cursor_row + 1, 0})
assert(helpers.fn.getline('.'):match('{{CURSOR}}'))
feed(string.format('zt%d<C-y>', #context_rows + 2))
--- @type [integer,integer,integer,integer][]
local ranges = exec_lua([[
return require('treesitter-context.context').get(...)
]], bufnr, winid)
local act_context_rows = {} --- @type integer[]
for _, r in ipairs(ranges) do
table.insert(act_context_rows, r[1])
end
helpers.eq(context_rows, act_context_rows, string.format('test for cursor %d failed', cursor_row))
end
end)
end
end)
describe('language:', function()
before_each(function()
exec_lua[[require'treesitter-context'.setup{
@ -240,7 +329,7 @@ describe('ts_context', function()
it('c', function()
install_langs('c')
cmd('edit test/lang/test.c')
cmd('edit test/test.c')
exec_lua [[vim.treesitter.start()]]
feed'<C-e>'