From 52389e724366ebb2fb58f08c657f580900dd09ee Mon Sep 17 00:00:00 2001 From: dundargoc Date: Sat, 25 May 2024 13:33:59 +0200 Subject: test(unit): skip flaky 'typval.c dict extend() works' test --- test/unit/eval/typval_spec.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test/unit') diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index c69c9b0fae..4e3b461396 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -2326,7 +2326,7 @@ describe('typval.c', function() return lib.tv_dict_extend(d1, d2, action) end, emsg) end - itp('works', function() + pending('works (skip due to flakiness)', function() local d1 = dict() alloc_log:check({ a.dict(d1) }) eq({}, dct2tbl(d1)) -- cgit From 78d21593a35cf89692224f1000a04d3c9fff8add Mon Sep 17 00:00:00 2001 From: bfredl Date: Fri, 31 May 2024 14:40:53 +0200 Subject: refactor(io): make rstream use a linear buffer If you like it you shouldn't put a ring on it. This is what _every_ consumer of RStream used anyway, either by calling rbuffer_reset, or rbuffer_consumed_compact (same as rbuffer_reset without needing a scratch buffer), or by consuming everything in each stream_read_cb call directly. --- test/unit/fixtures/rbuffer.c | 28 ---- test/unit/fixtures/rbuffer.h | 9 -- test/unit/rbuffer_spec.lua | 340 ------------------------------------------- 3 files changed, 377 deletions(-) delete mode 100644 test/unit/fixtures/rbuffer.c delete mode 100644 test/unit/fixtures/rbuffer.h delete mode 100644 test/unit/rbuffer_spec.lua (limited to 'test/unit') diff --git a/test/unit/fixtures/rbuffer.c b/test/unit/fixtures/rbuffer.c deleted file mode 100644 index d587d6b054..0000000000 --- a/test/unit/fixtures/rbuffer.c +++ /dev/null @@ -1,28 +0,0 @@ -#include "nvim/rbuffer.h" -#include "rbuffer.h" - - -void ut_rbuffer_each_read_chunk(RBuffer *buf, each_ptr_cb cb) -{ - RBUFFER_UNTIL_EMPTY(buf, rptr, rcnt) { - cb(rptr, rcnt); - rbuffer_consumed(buf, rcnt); - } -} - -void ut_rbuffer_each_write_chunk(RBuffer *buf, each_ptr_cb cb) -{ - RBUFFER_UNTIL_FULL(buf, wptr, wcnt) { - cb(wptr, wcnt); - rbuffer_produced(buf, wcnt); - } -} -void ut_rbuffer_each(RBuffer *buf, each_cb cb) -{ - RBUFFER_EACH(buf, c, i) cb(c, i); -} - -void ut_rbuffer_each_reverse(RBuffer *buf, each_cb cb) -{ - RBUFFER_EACH_REVERSE(buf, c, i) cb(c, i); -} diff --git a/test/unit/fixtures/rbuffer.h b/test/unit/fixtures/rbuffer.h deleted file mode 100644 index 640092c627..0000000000 --- a/test/unit/fixtures/rbuffer.h +++ /dev/null @@ -1,9 +0,0 @@ -#include "nvim/rbuffer.h" - -typedef void(*each_ptr_cb)(char *ptr, size_t cnt); -typedef void(*each_cb)(char c, size_t i); - -void ut_rbuffer_each_read_chunk(RBuffer *buf, each_ptr_cb cb); -void ut_rbuffer_each_write_chunk(RBuffer *buf, each_ptr_cb cb); -void ut_rbuffer_each(RBuffer *buf, each_cb cb); -void ut_rbuffer_each_reverse(RBuffer *buf, each_cb cb); diff --git a/test/unit/rbuffer_spec.lua b/test/unit/rbuffer_spec.lua deleted file mode 100644 index ad18ea2ddc..0000000000 --- a/test/unit/rbuffer_spec.lua +++ /dev/null @@ -1,340 +0,0 @@ -local t = require('test.unit.testutil') -local itp = t.gen_itp(it) - -local eq = t.eq -local ffi = t.ffi -local cstr = t.cstr -local to_cstr = t.to_cstr -local child_call_once = t.child_call_once - -local rbuffer = t.cimport('./test/unit/fixtures/rbuffer.h') - -describe('rbuffer functions', function() - local capacity = 16 - local rbuf - - local function inspect() - return ffi.string(rbuf.start_ptr, capacity) - end - - local function write(str) - local buf = to_cstr(str) - return rbuffer.rbuffer_write(rbuf, buf, #str) - end - - local function read(len) - local buf = cstr(len) - len = rbuffer.rbuffer_read(rbuf, buf, len) - return ffi.string(buf, len) - end - - local function get(idx) - return ffi.string(rbuffer.rbuffer_get(rbuf, idx), 1) - end - - before_each(function() - child_call_once(function() - rbuf = ffi.gc(rbuffer.rbuffer_new(capacity), rbuffer.rbuffer_free) - -- fill the internal buffer with the character '0' to simplify inspecting - ffi.C.memset(rbuf.start_ptr, string.byte('0'), capacity) - end) - end) - - describe('RBUFFER_UNTIL_FULL', function() - local chunks - - local function collect_write_chunks() - rbuffer.ut_rbuffer_each_write_chunk(rbuf, function(wptr, wcnt) - table.insert(chunks, ffi.string(wptr, wcnt)) - end) - end - - before_each(function() - chunks = {} - end) - - describe('with empty buffer in one contiguous chunk', function() - itp('is called once with the empty chunk', function() - collect_write_chunks() - eq({ '0000000000000000' }, chunks) - end) - end) - - describe('with partially empty buffer in one contiguous chunk', function() - itp('is called once with the empty chunk', function() - write('string') - collect_write_chunks() - eq({ '0000000000' }, chunks) - end) - end) - - describe('with filled buffer in one contiguous chunk', function() - itp('is not called', function() - write('abcdefghijklmnopq') - collect_write_chunks() - eq({}, chunks) - end) - end) - - describe('with buffer partially empty in two contiguous chunks', function() - itp('is called twice with each filled chunk', function() - write('1234567890') - read(8) - collect_write_chunks() - eq({ '000000', '12345678' }, chunks) - end) - end) - - describe('with buffer empty in two contiguous chunks', function() - itp('is called twice with each filled chunk', function() - write('12345678') - read(8) - collect_write_chunks() - eq({ '00000000', '12345678' }, chunks) - end) - end) - - describe('with buffer filled in two contiguous chunks', function() - itp('is not called', function() - write('12345678') - read(8) - write('abcdefghijklmnopq') - collect_write_chunks() - eq({}, chunks) - end) - end) - end) - - describe('RBUFFER_UNTIL_EMPTY', function() - local chunks - - local function collect_read_chunks() - rbuffer.ut_rbuffer_each_read_chunk(rbuf, function(rptr, rcnt) - table.insert(chunks, ffi.string(rptr, rcnt)) - end) - end - - before_each(function() - chunks = {} - end) - - describe('with empty buffer', function() - itp('is not called', function() - collect_read_chunks() - eq({}, chunks) - end) - end) - - describe('with partially filled buffer in one contiguous chunk', function() - itp('is called once with the filled chunk', function() - write('string') - collect_read_chunks() - eq({ 'string' }, chunks) - end) - end) - - describe('with filled buffer in one contiguous chunk', function() - itp('is called once with the filled chunk', function() - write('abcdefghijklmnopq') - collect_read_chunks() - eq({ 'abcdefghijklmnop' }, chunks) - end) - end) - - describe('with buffer partially filled in two contiguous chunks', function() - itp('is called twice with each filled chunk', function() - write('1234567890') - read(10) - write('long string') - collect_read_chunks() - eq({ 'long s', 'tring' }, chunks) - end) - end) - - describe('with buffer filled in two contiguous chunks', function() - itp('is called twice with each filled chunk', function() - write('12345678') - read(8) - write('abcdefghijklmnopq') - collect_read_chunks() - eq({ 'abcdefgh', 'ijklmnop' }, chunks) - end) - end) - end) - - describe('RBUFFER_EACH', function() - local chars - - local function collect_chars() - rbuffer.ut_rbuffer_each(rbuf, function(c, i) - table.insert(chars, { string.char(c), tonumber(i) }) - end) - end - before_each(function() - chars = {} - end) - - describe('with empty buffer', function() - itp('is not called', function() - collect_chars() - eq({}, chars) - end) - end) - - describe('with buffer filled in two contiguous chunks', function() - itp('collects each character and index', function() - write('1234567890') - read(10) - write('long string') - collect_chars() - eq({ - { 'l', 0 }, - { 'o', 1 }, - { 'n', 2 }, - { 'g', 3 }, - { ' ', 4 }, - { 's', 5 }, - { 't', 6 }, - { 'r', 7 }, - { 'i', 8 }, - { 'n', 9 }, - { 'g', 10 }, - }, chars) - end) - end) - end) - - describe('RBUFFER_EACH_REVERSE', function() - local chars - - local function collect_chars() - rbuffer.ut_rbuffer_each_reverse(rbuf, function(c, i) - table.insert(chars, { string.char(c), tonumber(i) }) - end) - end - before_each(function() - chars = {} - end) - - describe('with empty buffer', function() - itp('is not called', function() - collect_chars() - eq({}, chars) - end) - end) - - describe('with buffer filled in two contiguous chunks', function() - itp('collects each character and index', function() - write('1234567890') - read(10) - write('long string') - collect_chars() - eq({ - { 'g', 10 }, - { 'n', 9 }, - { 'i', 8 }, - { 'r', 7 }, - { 't', 6 }, - { 's', 5 }, - { ' ', 4 }, - { 'g', 3 }, - { 'n', 2 }, - { 'o', 1 }, - { 'l', 0 }, - }, chars) - end) - end) - end) - - describe('rbuffer_cmp', function() - local function cmp(str) - local rv = rbuffer.rbuffer_cmp(rbuf, to_cstr(str), #str) - if rv == 0 then - return 0 - else - return rv / math.abs(rv) - end - end - - describe('with buffer filled in two contiguous chunks', function() - itp('compares the common longest sequence', function() - write('1234567890') - read(10) - write('long string') - eq(0, cmp('long string')) - eq(0, cmp('long strin')) - eq(-1, cmp('long striM')) - eq(1, cmp('long strio')) - eq(0, cmp('long')) - eq(-1, cmp('lonG')) - eq(1, cmp('lonh')) - end) - end) - - describe('with empty buffer', function() - itp('returns 0 since no characters are compared', function() - eq(0, cmp('')) - end) - end) - end) - - describe('rbuffer_write', function() - itp('fills the internal buffer and returns the write count', function() - eq(12, write('short string')) - eq('short string0000', inspect()) - end) - - itp('wont write beyond capacity', function() - eq(16, write('very very long string')) - eq('very very long s', inspect()) - end) - end) - - describe('rbuffer_read', function() - itp('reads what was previously written', function() - write('to read') - eq('to read', read(20)) - end) - - itp('reads nothing if the buffer is empty', function() - eq('', read(20)) - write('empty') - eq('empty', read(20)) - eq('', read(20)) - end) - end) - - describe('rbuffer_get', function() - itp('fetch the pointer at offset, wrapping if required', function() - write('1234567890') - read(10) - write('long string') - eq('l', get(0)) - eq('o', get(1)) - eq('n', get(2)) - eq('g', get(3)) - eq(' ', get(4)) - eq('s', get(5)) - eq('t', get(6)) - eq('r', get(7)) - eq('i', get(8)) - eq('n', get(9)) - eq('g', get(10)) - end) - end) - - describe('wrapping behavior', function() - itp('writing/reading wraps across the end of the internal buffer', function() - write('1234567890') - eq('1234', read(4)) - eq('5678', read(4)) - write('987654321') - eq('3214567890987654', inspect()) - eq('90987654321', read(20)) - eq('', read(4)) - write('abcdefghijklmnopqrs') - eq('nopabcdefghijklm', inspect()) - eq('abcdefghijklmnop', read(20)) - end) - end) -end) -- cgit From 2a883d9c597e70d25ffc53373731d05d18a89b91 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 5 Jul 2024 15:20:02 +0800 Subject: vim-patch:9.1.0524: the recursive parameter in the *_equal functions can be removed (#29572) Problem: the recursive parameter in the *_equal functions can be removed Solution: Remove the recursive parameter in dict_equal(), list_equal() object_equal and tv_equal(). Use a comparison of the static var recursive_cnt == 0 to determine whether or not tv_equal() has been called recursively (Yinzuo Jiang). closes: vim/vim#15070 https://github.com/vim/vim/commit/7ccd1a2e858dbb2ac7fb09971dfcbfad62baa677 Co-authored-by: Yinzuo Jiang --- test/unit/eval/typval_spec.lua | 134 ++++++++++++++++++----------------------- 1 file changed, 59 insertions(+), 75 deletions(-) (limited to 'test/unit') diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 4e3b461396..14fd7986e7 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -1267,26 +1267,19 @@ describe('typval.c', function() local l2 = list() -- NULL lists are equal to empty lists - eq(true, lib.tv_list_equal(l, nil, true, false)) - eq(true, lib.tv_list_equal(nil, l, false, false)) - eq(true, lib.tv_list_equal(nil, l, false, true)) - eq(true, lib.tv_list_equal(l, nil, true, true)) + eq(true, lib.tv_list_equal(l, nil, true)) + eq(true, lib.tv_list_equal(nil, l, false)) -- NULL lists are equal themselves - eq(true, lib.tv_list_equal(nil, nil, true, false)) - eq(true, lib.tv_list_equal(nil, nil, false, false)) - eq(true, lib.tv_list_equal(nil, nil, false, true)) - eq(true, lib.tv_list_equal(nil, nil, true, true)) + eq(true, lib.tv_list_equal(nil, nil, true)) + eq(true, lib.tv_list_equal(nil, nil, false)) -- As well as empty lists - eq(true, lib.tv_list_equal(l, l, true, false)) - eq(true, lib.tv_list_equal(l, l2, false, false)) - eq(true, lib.tv_list_equal(l2, l, false, true)) - eq(true, lib.tv_list_equal(l2, l2, true, true)) - end) - -- Must not use recursive=true argument in the following tests because it - -- indicates that tv_equal_recurse_limit and recursive_cnt were set which - -- is essential. This argument will be set when comparing inner lists. + eq(true, lib.tv_list_equal(l, l, true)) + eq(true, lib.tv_list_equal(l, l2, false)) + eq(true, lib.tv_list_equal(l2, l, false)) + eq(true, lib.tv_list_equal(l2, l2, true)) + end) itp('compares lists correctly when case is not ignored', function() local l1 = list('abc', { 1, 2, 'Abc' }, 'def') local l2 = list('abc', { 1, 2, 'Abc' }) @@ -1298,15 +1291,15 @@ describe('typval.c', function() local l8 = list('abc', nil, 'def') local l9 = list('abc', { 1, 2, nil }, 'def') - eq(true, lib.tv_list_equal(l1, l1, false, false)) - eq(false, lib.tv_list_equal(l1, l2, false, false)) - eq(false, lib.tv_list_equal(l1, l3, false, false)) - eq(false, lib.tv_list_equal(l1, l4, false, false)) - eq(false, lib.tv_list_equal(l1, l5, false, false)) - eq(true, lib.tv_list_equal(l1, l6, false, false)) - eq(false, lib.tv_list_equal(l1, l7, false, false)) - eq(false, lib.tv_list_equal(l1, l8, false, false)) - eq(false, lib.tv_list_equal(l1, l9, false, false)) + eq(true, lib.tv_list_equal(l1, l1, false)) + eq(false, lib.tv_list_equal(l1, l2, false)) + eq(false, lib.tv_list_equal(l1, l3, false)) + eq(false, lib.tv_list_equal(l1, l4, false)) + eq(false, lib.tv_list_equal(l1, l5, false)) + eq(true, lib.tv_list_equal(l1, l6, false)) + eq(false, lib.tv_list_equal(l1, l7, false)) + eq(false, lib.tv_list_equal(l1, l8, false)) + eq(false, lib.tv_list_equal(l1, l9, false)) end) itp('compares lists correctly when case is ignored', function() local l1 = list('abc', { 1, 2, 'Abc' }, 'def') @@ -1319,15 +1312,15 @@ describe('typval.c', function() local l8 = list('abc', nil, 'def') local l9 = list('abc', { 1, 2, nil }, 'def') - eq(true, lib.tv_list_equal(l1, l1, true, false)) - eq(false, lib.tv_list_equal(l1, l2, true, false)) - eq(true, lib.tv_list_equal(l1, l3, true, false)) - eq(false, lib.tv_list_equal(l1, l4, true, false)) - eq(true, lib.tv_list_equal(l1, l5, true, false)) - eq(true, lib.tv_list_equal(l1, l6, true, false)) - eq(true, lib.tv_list_equal(l1, l7, true, false)) - eq(false, lib.tv_list_equal(l1, l8, true, false)) - eq(false, lib.tv_list_equal(l1, l9, true, false)) + eq(true, lib.tv_list_equal(l1, l1, true)) + eq(false, lib.tv_list_equal(l1, l2, true)) + eq(true, lib.tv_list_equal(l1, l3, true)) + eq(false, lib.tv_list_equal(l1, l4, true)) + eq(true, lib.tv_list_equal(l1, l5, true)) + eq(true, lib.tv_list_equal(l1, l6, true)) + eq(true, lib.tv_list_equal(l1, l7, true)) + eq(false, lib.tv_list_equal(l1, l8, true)) + eq(false, lib.tv_list_equal(l1, l9, true)) end) end) describe('find', function() @@ -2448,8 +2441,8 @@ describe('typval.c', function() end) end) describe('equal()', function() - local function tv_dict_equal(d1, d2, ic, recursive) - return lib.tv_dict_equal(d1, d2, ic or false, recursive or false) + local function tv_dict_equal(d1, d2, ic) + return lib.tv_dict_equal(d1, d2, ic or false) end itp('works', function() eq(true, tv_dict_equal(nil, nil)) @@ -2494,7 +2487,6 @@ describe('typval.c', function() eq(true, tv_dict_equal(d_kupper_upper, d_kupper_lower, true)) eq(false, tv_dict_equal(d_kupper_upper, d_lower, true)) eq(false, tv_dict_equal(d_kupper_upper, d_upper, true)) - eq(true, tv_dict_equal(d_upper, d_upper, true, true)) alloc_log:check({}) end) end) @@ -2923,26 +2915,19 @@ describe('typval.c', function() local nl = lua2typvalt(null_list) -- NULL lists are equal to empty lists - eq(true, lib.tv_equal(l, nl, true, false)) - eq(true, lib.tv_equal(nl, l, false, false)) - eq(true, lib.tv_equal(nl, l, false, true)) - eq(true, lib.tv_equal(l, nl, true, true)) + eq(true, lib.tv_equal(l, nl, true)) + eq(true, lib.tv_equal(nl, l, false)) -- NULL lists are equal themselves - eq(true, lib.tv_equal(nl, nl, true, false)) - eq(true, lib.tv_equal(nl, nl, false, false)) - eq(true, lib.tv_equal(nl, nl, false, true)) - eq(true, lib.tv_equal(nl, nl, true, true)) + eq(true, lib.tv_equal(nl, nl, true)) + eq(true, lib.tv_equal(nl, nl, false)) -- As well as empty lists - eq(true, lib.tv_equal(l, l, true, false)) - eq(true, lib.tv_equal(l, l2, false, false)) - eq(true, lib.tv_equal(l2, l, false, true)) - eq(true, lib.tv_equal(l2, l2, true, true)) - end) - -- Must not use recursive=true argument in the following tests because it - -- indicates that tv_equal_recurse_limit and recursive_cnt were set which - -- is essential. This argument will be set when comparing inner lists. + eq(true, lib.tv_equal(l, l, true)) + eq(true, lib.tv_equal(l, l2, false)) + eq(true, lib.tv_equal(l2, l, false)) + eq(true, lib.tv_equal(l2, l2, true)) + end) itp('compares lists correctly when case is not ignored', function() local l1 = lua2typvalt({ 'abc', { 1, 2, 'Abc' }, 'def' }) local l2 = lua2typvalt({ 'abc', { 1, 2, 'Abc' } }) @@ -2954,15 +2939,15 @@ describe('typval.c', function() local l8 = lua2typvalt({ 'abc', nil, 'def' }) local l9 = lua2typvalt({ 'abc', { 1, 2, nil }, 'def' }) - eq(true, lib.tv_equal(l1, l1, false, false)) - eq(false, lib.tv_equal(l1, l2, false, false)) - eq(false, lib.tv_equal(l1, l3, false, false)) - eq(false, lib.tv_equal(l1, l4, false, false)) - eq(false, lib.tv_equal(l1, l5, false, false)) - eq(true, lib.tv_equal(l1, l6, false, false)) - eq(false, lib.tv_equal(l1, l7, false, false)) - eq(false, lib.tv_equal(l1, l8, false, false)) - eq(false, lib.tv_equal(l1, l9, false, false)) + eq(true, lib.tv_equal(l1, l1, false)) + eq(false, lib.tv_equal(l1, l2, false)) + eq(false, lib.tv_equal(l1, l3, false)) + eq(false, lib.tv_equal(l1, l4, false)) + eq(false, lib.tv_equal(l1, l5, false)) + eq(true, lib.tv_equal(l1, l6, false)) + eq(false, lib.tv_equal(l1, l7, false)) + eq(false, lib.tv_equal(l1, l8, false)) + eq(false, lib.tv_equal(l1, l9, false)) end) itp('compares lists correctly when case is ignored', function() local l1 = lua2typvalt({ 'abc', { 1, 2, 'Abc' }, 'def' }) @@ -2975,18 +2960,18 @@ describe('typval.c', function() local l8 = lua2typvalt({ 'abc', nil, 'def' }) local l9 = lua2typvalt({ 'abc', { 1, 2, nil }, 'def' }) - eq(true, lib.tv_equal(l1, l1, true, false)) - eq(false, lib.tv_equal(l1, l2, true, false)) - eq(true, lib.tv_equal(l1, l3, true, false)) - eq(false, lib.tv_equal(l1, l4, true, false)) - eq(true, lib.tv_equal(l1, l5, true, false)) - eq(true, lib.tv_equal(l1, l6, true, false)) - eq(true, lib.tv_equal(l1, l7, true, false)) - eq(false, lib.tv_equal(l1, l8, true, false)) - eq(false, lib.tv_equal(l1, l9, true, false)) - end) - local function tv_equal(d1, d2, ic, recursive) - return lib.tv_equal(d1, d2, ic or false, recursive or false) + eq(true, lib.tv_equal(l1, l1, true)) + eq(false, lib.tv_equal(l1, l2, true)) + eq(true, lib.tv_equal(l1, l3, true)) + eq(false, lib.tv_equal(l1, l4, true)) + eq(true, lib.tv_equal(l1, l5, true)) + eq(true, lib.tv_equal(l1, l6, true)) + eq(true, lib.tv_equal(l1, l7, true)) + eq(false, lib.tv_equal(l1, l8, true)) + eq(false, lib.tv_equal(l1, l9, true)) + end) + local function tv_equal(d1, d2, ic) + return lib.tv_equal(d1, d2, ic or false) end itp('works with dictionaries', function() local nd = lua2typvalt(null_dict) @@ -3033,7 +3018,6 @@ describe('typval.c', function() eq(true, tv_equal(d_kupper_upper, d_kupper_lower, true)) eq(false, tv_equal(d_kupper_upper, d_lower, true)) eq(false, tv_equal(d_kupper_upper, d_upper, true)) - eq(true, tv_equal(d_upper, d_upper, true, true)) alloc_log:check({}) end) end) -- cgit From 028dd3c5c4d1828eec64c099d3372ffb90572dc0 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 12 Jul 2024 14:30:49 +0800 Subject: vim-patch:9.1.0569: fnamemodify() treats ".." and "../" differently (#29673) Problem: fnamemodify() treats ".." and "../" differently. Solution: Expand ".." properly like how "/.." is treated in 8.2.3388. (zeertzjq) closes: vim/vim#15218 https://github.com/vim/vim/commit/1ee7420460768df67ea4bc73467f2d4f8b1555bd --- test/unit/path_spec.lua | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) (limited to 'test/unit') diff --git a/test/unit/path_spec.lua b/test/unit/path_spec.lua index 6f6a80f44e..ffad552a8a 100644 --- a/test/unit/path_spec.lua +++ b/test/unit/path_spec.lua @@ -468,8 +468,11 @@ describe('path.c', function() eq(OK, result) end) - itp('concatenates directory name if it does not contain a slash', function() - local expected = uv.cwd() .. '/..' + itp('produces absolute path for .. without a slash', function() + local old_dir = uv.cwd() + uv.chdir('..') + local expected = uv.cwd() + uv.chdir(old_dir) local filename = '..' local buflen = get_buf_len(expected, filename) local do_expand = 1 @@ -478,21 +481,18 @@ describe('path.c', function() eq(OK, result) end) - itp( - 'enters given directory (instead of just concatenating the strings) if possible and if path contains a slash', - function() - local old_dir = uv.cwd() - uv.chdir('..') - local expected = uv.cwd() .. '/test.file' - uv.chdir(old_dir) - local filename = '../test.file' - local buflen = get_buf_len(expected, filename) - local do_expand = 1 - local buf, result = vim_FullName(filename, buflen, do_expand) - eq(expected, ffi.string(buf)) - eq(OK, result) - end - ) + itp('produces absolute path if possible and if path contains a slash', function() + local old_dir = uv.cwd() + uv.chdir('..') + local expected = uv.cwd() .. '/test.file' + uv.chdir(old_dir) + local filename = '../test.file' + local buflen = get_buf_len(expected, filename) + local do_expand = 1 + local buf, result = vim_FullName(filename, buflen, do_expand) + eq(expected, ffi.string(buf)) + eq(OK, result) + end) itp('just copies the path if it is already absolute and force=0', function() local absolute_path = '/absolute/path' -- cgit From 7dffc36e61c46e6adc92cff5944e876446f3c40e Mon Sep 17 00:00:00 2001 From: bfredl Date: Thu, 13 Jun 2024 12:00:58 +0200 Subject: refactor(declarations): also generate prototypes for functions in headers Before this change, "static inline" functions in headers needed to have their function attributes specified in a completely different way. The prototype had to be duplicated, and REAL_FATTR_ had to be used instead of the public FUNC_ATTR_ names. TODO: need a check that a "header.h.inline.generated.h" file is not forgotten when the first "static inline" function with attributes is added to a header (they would just be silently missing). --- test/unit/formatc.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'test/unit') diff --git a/test/unit/formatc.lua b/test/unit/formatc.lua index ce9cb81f4a..04a8b4009f 100644 --- a/test/unit/formatc.lua +++ b/test/unit/formatc.lua @@ -264,6 +264,7 @@ local function formatc(str) -- and ';' indicates we're at the end of a statement, so we put end -- it with a newline. token[1] = ';\n' + end_at_brace = false end elseif typ == 'whitespace' then -- replace all whitespace by one space -- cgit From f926cc32c9262b6254e2843276b951cef9da1afe Mon Sep 17 00:00:00 2001 From: bfredl Date: Tue, 2 Jul 2024 13:45:50 +0200 Subject: refactor(shada): rework msgpack decoding without msgpack-c This also makes shada reading slightly faster due to avoiding some copying and allocation. Use keysets to drive decoding of msgpack maps for shada entries. --- test/unit/eval/encode_spec.lua | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'test/unit') diff --git a/test/unit/eval/encode_spec.lua b/test/unit/eval/encode_spec.lua index 5b9188163e..9f193bc2f9 100644 --- a/test/unit/eval/encode_spec.lua +++ b/test/unit/eval/encode_spec.lua @@ -21,81 +21,81 @@ describe('encode_list_write()', function() itp('writes empty string', function() local l = list() - eq(0, encode_list_write(l, '')) + encode_list_write(l, '') eq({ [type_key] = list_type }, lst2tbl(l)) end) itp('writes ASCII string literal with printable characters', function() local l = list() - eq(0, encode_list_write(l, 'abc')) + encode_list_write(l, 'abc') eq({ 'abc' }, lst2tbl(l)) end) itp('writes string starting with NL', function() local l = list() - eq(0, encode_list_write(l, '\nabc')) + encode_list_write(l, '\nabc') eq({ null_string, 'abc' }, lst2tbl(l)) end) itp('writes string starting with NL twice', function() local l = list() - eq(0, encode_list_write(l, '\nabc')) + encode_list_write(l, '\nabc') eq({ null_string, 'abc' }, lst2tbl(l)) - eq(0, encode_list_write(l, '\nabc')) + encode_list_write(l, '\nabc') eq({ null_string, 'abc', 'abc' }, lst2tbl(l)) end) itp('writes string ending with NL', function() local l = list() - eq(0, encode_list_write(l, 'abc\n')) + encode_list_write(l, 'abc\n') eq({ 'abc', null_string }, lst2tbl(l)) end) itp('writes string ending with NL twice', function() local l = list() - eq(0, encode_list_write(l, 'abc\n')) + encode_list_write(l, 'abc\n') eq({ 'abc', null_string }, lst2tbl(l)) - eq(0, encode_list_write(l, 'abc\n')) + encode_list_write(l, 'abc\n') eq({ 'abc', 'abc', null_string }, lst2tbl(l)) end) itp('writes string starting, ending and containing NL twice', function() local l = list() - eq(0, encode_list_write(l, '\na\nb\n')) + encode_list_write(l, '\na\nb\n') eq({ null_string, 'a', 'b', null_string }, lst2tbl(l)) - eq(0, encode_list_write(l, '\na\nb\n')) + encode_list_write(l, '\na\nb\n') eq({ null_string, 'a', 'b', null_string, 'a', 'b', null_string }, lst2tbl(l)) end) itp('writes string starting, ending and containing NUL with NL between twice', function() local l = list() - eq(0, encode_list_write(l, '\0\n\0\n\0')) + encode_list_write(l, '\0\n\0\n\0') eq({ '\n', '\n', '\n' }, lst2tbl(l)) - eq(0, encode_list_write(l, '\0\n\0\n\0')) + encode_list_write(l, '\0\n\0\n\0') eq({ '\n', '\n', '\n\n', '\n', '\n' }, lst2tbl(l)) end) itp('writes string starting, ending and containing NL with NUL between twice', function() local l = list() - eq(0, encode_list_write(l, '\n\0\n\0\n')) + encode_list_write(l, '\n\0\n\0\n') eq({ null_string, '\n', '\n', null_string }, lst2tbl(l)) - eq(0, encode_list_write(l, '\n\0\n\0\n')) + encode_list_write(l, '\n\0\n\0\n') eq({ null_string, '\n', '\n', null_string, '\n', '\n', null_string }, lst2tbl(l)) end) itp('writes string containing a single NL twice', function() local l = list() - eq(0, encode_list_write(l, '\n')) + encode_list_write(l, '\n') eq({ null_string, null_string }, lst2tbl(l)) - eq(0, encode_list_write(l, '\n')) + encode_list_write(l, '\n') eq({ null_string, null_string, null_string }, lst2tbl(l)) end) itp('writes string containing a few NLs twice', function() local l = list() - eq(0, encode_list_write(l, '\n\n\n')) + encode_list_write(l, '\n\n\n') eq({ null_string, null_string, null_string, null_string }, lst2tbl(l)) - eq(0, encode_list_write(l, '\n\n\n')) + encode_list_write(l, '\n\n\n') eq( { null_string, null_string, null_string, null_string, null_string, null_string, null_string }, lst2tbl(l) -- cgit From cfdf68a7acde16597fbd896674af68c42361102c Mon Sep 17 00:00:00 2001 From: bfredl Date: Thu, 8 Aug 2024 10:42:08 +0200 Subject: feat(mbyte): support extended grapheme clusters including more emoji Use the grapheme break algorithm from utf8proc to support grapheme clusters from recent unicode versions. Handle variant selector VS16 turning some codepoints into double-width emoji. This means we need to use ptr2cells rather than char2cells when possible. --- test/unit/mbyte_spec.lua | 119 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 93 insertions(+), 26 deletions(-) (limited to 'test/unit') diff --git a/test/unit/mbyte_spec.lua b/test/unit/mbyte_spec.lua index 8fcc67d20b..787a8862ae 100644 --- a/test/unit/mbyte_spec.lua +++ b/test/unit/mbyte_spec.lua @@ -3,8 +3,15 @@ local itp = t.gen_itp(it) local ffi = t.ffi local eq = t.eq +local to_cstr = t.to_cstr +local ok = t.ok -local lib = t.cimport('./src/nvim/mbyte.h', './src/nvim/charset.h', './src/nvim/grid.h') +local lib = t.cimport( + './src/nvim/mbyte.h', + './src/nvim/charset.h', + './src/nvim/grid.h', + './src/nvim/option_vars.h' +) describe('mbyte', function() -- Convert from bytes to string @@ -45,12 +52,21 @@ describe('mbyte', function() end) end - describe('utfc_ptr2schar_len', function() + describe('utfc_ptr2schar', function() local function test_seq(seq) local firstc = ffi.new('int[1]') local buf = ffi.new('char[32]') - lib.schar_get(buf, lib.utfc_ptr2schar_len(to_string(seq), #seq, firstc)) - return { ffi.string(buf), firstc[0] } + lib.schar_get(buf, lib.utfc_ptr2schar(to_string(seq), firstc)) + local str = ffi.string(buf) + if 1 > 2 then -- for debugging + local tabel = {} + for i = 1, #str do + table.insert(tabel, string.format('0x%02x', string.byte(str, i))) + end + print('{ ' .. table.concat(tabel, ', ') .. ' }') + io.stdout:flush() + end + return { str, firstc[0] } end local function byte(val) @@ -88,7 +104,9 @@ describe('mbyte', function() eq(byte(0x7f), test_seq { 0x7f, 0xc2, 0x80 }) -- Combining character is U+0300 - eq({ '\x7f\xcc\x80', 0x7f }, test_seq { 0x7f, 0xcc, 0x80 }) + eq({ '\x29\xcc\x80', 0x29 }, test_seq { 0x29, 0xcc, 0x80 }) + -- invalid start byte for combining + eq({ '\x7f', 0x7f }, test_seq { 0x7f, 0xcc, 0x80 }) -- No UTF-8 sequence eq({ '', 0xc2 }, test_seq { 0xc2, 0x7f, 0xcc }) @@ -102,18 +120,21 @@ describe('mbyte', function() itp('4-byte sequences', function() -- No following combining character eq(byte(0x7f), test_seq { 0x7f, 0x7f, 0xcc, 0x80 }) + eq(byte(0x29), test_seq { 0x29, 0x29, 0xcc, 0x80 }) -- No second UTF-8 character eq(byte(0x7f), test_seq { 0x7f, 0xc2, 0xcc, 0x80 }) -- Combining character U+0300 - eq({ '\x7f\xcc\x80', 0x7f }, test_seq { 0x7f, 0xcc, 0x80, 0xcc }) + eq({ '\x29\xcc\x80', 0x29 }, test_seq { 0x29, 0xcc, 0x80, 0xcc }) -- No UTF-8 sequence eq({ '', 0xc2 }, test_seq { 0xc2, 0x7f, 0xcc, 0x80 }) -- No following UTF-8 character eq({ '\xc2\x80', 0x80 }, test_seq { 0xc2, 0x80, 0xcc, 0xcc }) -- Combining character U+0301 - eq({ '\xc2\x80\xcc\x81', 0x80 }, test_seq { 0xc2, 0x80, 0xcc, 0x81 }) + eq({ '\xc2\xbc\xcc\x81', 0xbc }, test_seq { 0xc2, 0xbc, 0xcc, 0x81 }) + -- U+0080 : not a valid start char + eq({ '\xc2\x80', 0x80 }, test_seq { 0xc2, 0x80, 0xcc, 0x81 }) -- One UTF-8 character eq({ '\xf4\x80\x80\x80', 0x100000 }, test_seq { 0xf4, 0x80, 0x80, 0x80 }) @@ -126,36 +147,36 @@ describe('mbyte', function() eq(byte(0x7f), test_seq { 0x7f, 0xc2, 0xcc, 0x80, 0x80 }) -- Combining character U+0300 - eq({ '\x7f\xcc\x80', 0x7f }, test_seq { 0x7f, 0xcc, 0x80, 0xcc, 0x00 }) + eq({ '\x29\xcc\x80', 0x29 }, test_seq { 0x29, 0xcc, 0x80, 0xcc, 0x00 }) -- Combining characters U+0300 and U+0301 - eq({ '\x7f\xcc\x80\xcc\x81', 0x7f }, test_seq { 0x7f, 0xcc, 0x80, 0xcc, 0x81 }) + eq({ '\x29\xcc\x80\xcc\x81', 0x29 }, test_seq { 0x29, 0xcc, 0x80, 0xcc, 0x81 }) -- Combining characters U+0300, U+0301, U+0302 eq( - { '\x7f\xcc\x80\xcc\x81\xcc\x82', 0x7f }, - test_seq { 0x7f, 0xcc, 0x80, 0xcc, 0x81, 0xcc, 0x82 } + { '\x29\xcc\x80\xcc\x81\xcc\x82', 0x29 }, + test_seq { 0x29, 0xcc, 0x80, 0xcc, 0x81, 0xcc, 0x82 } ) -- Combining characters U+0300, U+0301, U+0302, U+0303 eq( - { '\x7f\xcc\x80\xcc\x81\xcc\x82\xcc\x83', 0x7f }, - test_seq { 0x7f, 0xcc, 0x80, 0xcc, 0x81, 0xcc, 0x82, 0xcc, 0x83 } + { '\x29\xcc\x80\xcc\x81\xcc\x82\xcc\x83', 0x29 }, + test_seq { 0x29, 0xcc, 0x80, 0xcc, 0x81, 0xcc, 0x82, 0xcc, 0x83 } ) -- Combining characters U+0300, U+0301, U+0302, U+0303, U+0304 eq( - { '\x7f\xcc\x80\xcc\x81\xcc\x82\xcc\x83\xcc\x84', 0x7f }, - test_seq { 0x7f, 0xcc, 0x80, 0xcc, 0x81, 0xcc, 0x82, 0xcc, 0x83, 0xcc, 0x84 } + { '\x29\xcc\x80\xcc\x81\xcc\x82\xcc\x83\xcc\x84', 0x29 }, + test_seq { 0x29, 0xcc, 0x80, 0xcc, 0x81, 0xcc, 0x82, 0xcc, 0x83, 0xcc, 0x84 } ) -- Combining characters U+0300, U+0301, U+0302, U+0303, U+0304, U+0305 eq( - { '\x7f\xcc\x80\xcc\x81\xcc\x82\xcc\x83\xcc\x84\xcc\x85', 0x7f }, - test_seq { 0x7f, 0xcc, 0x80, 0xcc, 0x81, 0xcc, 0x82, 0xcc, 0x83, 0xcc, 0x84, 0xcc, 0x85 } + { '\x29\xcc\x80\xcc\x81\xcc\x82\xcc\x83\xcc\x84\xcc\x85', 0x29 }, + test_seq { 0x29, 0xcc, 0x80, 0xcc, 0x81, 0xcc, 0x82, 0xcc, 0x83, 0xcc, 0x84, 0xcc, 0x85 } ) -- Combining characters U+0300, U+0301, U+0302, U+0303, U+0304, U+0305, U+0306 eq( - { '\x7f\xcc\x80\xcc\x81\xcc\x82\xcc\x83\xcc\x84\xcc\x85\xcc\x86', 0x7f }, + { '\x29\xcc\x80\xcc\x81\xcc\x82\xcc\x83\xcc\x84\xcc\x85\xcc\x86', 0x29 }, test_seq { - 0x7f, + 0x29, 0xcc, 0x80, 0xcc, @@ -175,18 +196,18 @@ describe('mbyte', function() -- Only three following combining characters U+0300, U+0301, U+0302 eq( - { '\x7f\xcc\x80\xcc\x81\xcc\x82', 0x7f }, - test_seq { 0x7f, 0xcc, 0x80, 0xcc, 0x81, 0xcc, 0x82, 0xc2, 0x80, 0xcc, 0x84, 0xcc, 0x85 } + { '\x29\xcc\x80\xcc\x81\xcc\x82', 0x29 }, + test_seq { 0x29, 0xcc, 0x80, 0xcc, 0x81, 0xcc, 0x82, 0xc2, 0x80, 0xcc, 0x84, 0xcc, 0x85 } ) -- No UTF-8 sequence eq({ '', 0xc2 }, test_seq { 0xc2, 0x7f, 0xcc, 0x80, 0x80 }) -- No following UTF-8 character - eq({ '\xc2\x80', 0x80 }, test_seq { 0xc2, 0x80, 0xcc, 0xcc, 0x80 }) + eq({ '\xc2\xbc', 0xbc }, test_seq { 0xc2, 0xbc, 0xcc, 0xcc, 0x80 }) -- Combining character U+0301 - eq({ '\xc2\x80\xcc\x81', 0x80 }, test_seq { 0xc2, 0x80, 0xcc, 0x81, 0x7f }) + eq({ '\xc2\xbc\xcc\x81', 0xbc }, test_seq { 0xc2, 0xbc, 0xcc, 0x81, 0x7f }) -- Combining character U+0301 - eq({ '\xc2\x80\xcc\x81', 0x80 }, test_seq { 0xc2, 0x80, 0xcc, 0x81, 0xcc }) + eq({ '\xc2\xbc\xcc\x81', 0xbc }, test_seq { 0xc2, 0xbc, 0xcc, 0x81, 0xcc }) -- One UTF-8 character eq({ '\xf4\x80\x80\x80', 0x100000 }, test_seq { 0xf4, 0x80, 0x80, 0x80, 0x7f }) @@ -205,8 +226,6 @@ describe('mbyte', function() end) describe('utf_cp_bounds_len', function() - local to_cstr = t.to_cstr - local tests = { { name = 'for valid string', @@ -273,4 +292,52 @@ describe('mbyte', function() eq(expected_offsets, { b = b_offsets, e = e_offsets }) end) end) + + itp('utf_head_off', function() + local function check(str, expected_glyphs) + local len = #str + local cstr = to_cstr(str) + local breaks = { 0 } -- SOT + local pos = 0 + local mb_glyphs = {} + while pos < len do + local clen = lib.utfc_ptr2len(cstr + pos) + ok(clen > 0) -- otherwise we get stuck + if clen > 1 then + table.insert(mb_glyphs, string.sub(str, pos + 1, pos + clen)) + end + pos = pos + clen + table.insert(breaks, pos) + end + eq(breaks[#breaks], len) -- include EOT as break + -- we could also send in breaks, but this is more human readable + eq(mb_glyphs, expected_glyphs) + + for i = 1, #breaks - 1 do + local start, next = breaks[i], breaks[i + 1] + + for p = start, next - 1 do + eq(p - start, lib.utf_head_off(cstr, cstr + p)) + end + end + eq(0, lib.utf_head_off(cstr, cstr + len)) -- NUL byte is safe + end + -- stylua doesn't like ZWJ chars.. + -- stylua: ignore start + check('hej och hΓ₯ πŸ§‘β€πŸŒΎ!', { 'Γ₯', 'πŸ§‘β€πŸŒΎ' }) + -- emoji only (various kinds of combinations, use g8 to see them) + check("πŸ³οΈβ€βš§οΈπŸ§‘β€πŸŒΎβ€οΈπŸ˜‚πŸ΄β€β˜ οΈ", {"πŸ³οΈβ€βš§οΈ", "πŸ§‘β€πŸŒΎ", "❀️", "πŸ˜‚", "πŸ΄β€β˜ οΈ"}) + check('πŸ³οΈβ€βš§οΈxyπŸ§‘β€πŸŒΎ\rβ€οΈπŸ˜‚Γ₯πŸ΄β€β˜ οΈΒ€', { 'πŸ³οΈβ€βš§οΈ', 'πŸ§‘β€πŸŒΎ', '❀️', 'πŸ˜‚', 'Γ₯', 'πŸ΄β€β˜ οΈ', 'Β€' }) + + check('πŸ‡¦πŸ…±οΈ πŸ‡¦πŸ‡½ πŸ‡¦πŸ‡¨πŸ‡¦ πŸ‡²πŸ‡½πŸ‡ΉπŸ‡±',{'πŸ‡¦', 'πŸ…±οΈ', 'πŸ‡¦πŸ‡½', 'πŸ‡¦πŸ‡¨', 'πŸ‡¦', 'πŸ‡²πŸ‡½', 'πŸ‡ΉπŸ‡±'}) + check('🏴󠁧󠁒󠁳󠁣󠁴󠁿🏴󠁧󠁒󠁷󠁬󠁳󠁿', {'🏴󠁧󠁒󠁳󠁣󠁴󠁿', '🏴󠁧󠁒󠁷󠁬󠁳󠁿'}) + + lib.p_arshape = true -- default + check('Ψ³Ω„Ψ§Ω…', { 'Ψ³', 'Ω„Ψ§', 'Ω…' }) + lib.p_arshape = false + check('Ψ³Ω„Ψ§Ω…', { 'Ψ³', 'Ω„', 'Ψ§', 'Ω…' }) + + check('LΜ“Μ‰Μ‘Μ’ΜŒΜšoΜŒΜ’Μ—Μ„Μ›Μ€rΜΜˆΜ•ΜˆΜŽΜè̇̅̄̄̐mΜ…Μ–ΜŸΜ„ΜŸΜš', {'LΜ“Μ‰Μ‘Μ’ΜŒΜš', 'oΜŒΜ’Μ—Μ„Μ›Μ€', 'rΜΜˆΜ•ΜˆΜŽΜ', 'è̇̅̄̄̐', 'mΜ…Μ–ΜŸΜ„ΜŸΜš'}) + -- stylua: ignore end + end) end) -- cgit From fa99afe35eb5d8cf01d875e12b53165bf1104a60 Mon Sep 17 00:00:00 2001 From: bfredl Date: Wed, 4 Sep 2024 12:09:42 +0200 Subject: fix(multibyte): handle backspace of wide clusters in replace mode Make utf_head_off more robust against invalid sequences and embedded NUL chars --- test/unit/mbyte_spec.lua | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'test/unit') diff --git a/test/unit/mbyte_spec.lua b/test/unit/mbyte_spec.lua index 787a8862ae..62390c8794 100644 --- a/test/unit/mbyte_spec.lua +++ b/test/unit/mbyte_spec.lua @@ -4,7 +4,6 @@ local itp = t.gen_itp(it) local ffi = t.ffi local eq = t.eq local to_cstr = t.to_cstr -local ok = t.ok local lib = t.cimport( './src/nvim/mbyte.h', @@ -302,7 +301,10 @@ describe('mbyte', function() local mb_glyphs = {} while pos < len do local clen = lib.utfc_ptr2len(cstr + pos) - ok(clen > 0) -- otherwise we get stuck + if clen == 0 then + eq(0, string.byte(str, pos + 1)) -- only NUL bytes can has length zery + clen = 1 -- but skip it, otherwise we get stuck + end if clen > 1 then table.insert(mb_glyphs, string.sub(str, pos + 1, pos + clen)) end @@ -325,13 +327,18 @@ describe('mbyte', function() -- stylua doesn't like ZWJ chars.. -- stylua: ignore start check('hej och hΓ₯ πŸ§‘β€πŸŒΎ!', { 'Γ₯', 'πŸ§‘β€πŸŒΎ' }) - -- emoji only (various kinds of combinations, use g8 to see them) + + -- emoji (various kinds of combinations, use g8 to see them) check("πŸ³οΈβ€βš§οΈπŸ§‘β€πŸŒΎβ€οΈπŸ˜‚πŸ΄β€β˜ οΈ", {"πŸ³οΈβ€βš§οΈ", "πŸ§‘β€πŸŒΎ", "❀️", "πŸ˜‚", "πŸ΄β€β˜ οΈ"}) check('πŸ³οΈβ€βš§οΈxyπŸ§‘β€πŸŒΎ\rβ€οΈπŸ˜‚Γ₯πŸ΄β€β˜ οΈΒ€', { 'πŸ³οΈβ€βš§οΈ', 'πŸ§‘β€πŸŒΎ', '❀️', 'πŸ˜‚', 'Γ₯', 'πŸ΄β€β˜ οΈ', 'Β€' }) + check('πŸ³οΈβ€βš§οΈ\000πŸ§‘β€πŸŒΎ\000❀️\000πŸ˜‚\000Γ₯\000πŸ΄β€β˜ οΈ\000Β€', { 'πŸ³οΈβ€βš§οΈ', 'πŸ§‘β€πŸŒΎ', '❀️', 'πŸ˜‚', 'Γ₯', 'πŸ΄β€β˜ οΈ', 'Β€' }) + check('\195πŸ³οΈβ€βš§οΈ\198πŸ§‘β€πŸŒΎ\165❀️\168\195πŸ˜‚\255πŸ΄β€β˜ οΈ\129Β€\165', { 'πŸ³οΈβ€βš§οΈ', 'πŸ§‘β€πŸŒΎ', '❀️', 'πŸ˜‚', 'πŸ΄β€β˜ οΈ', 'Β€' }) check('πŸ‡¦πŸ…±οΈ πŸ‡¦πŸ‡½ πŸ‡¦πŸ‡¨πŸ‡¦ πŸ‡²πŸ‡½πŸ‡ΉπŸ‡±',{'πŸ‡¦', 'πŸ…±οΈ', 'πŸ‡¦πŸ‡½', 'πŸ‡¦πŸ‡¨', 'πŸ‡¦', 'πŸ‡²πŸ‡½', 'πŸ‡ΉπŸ‡±'}) check('🏴󠁧󠁒󠁳󠁣󠁴󠁿🏴󠁧󠁒󠁷󠁬󠁳󠁿', {'🏴󠁧󠁒󠁳󠁣󠁴󠁿', '🏴󠁧󠁒󠁷󠁬󠁳󠁿'}) + check('Γ₯\165ΓΌ\195aΓ«q\168Ξ²\000\169本\255', {'Γ₯', 'ΓΌ', 'Γ«', 'Ξ²', '本'}) + lib.p_arshape = true -- default check('Ψ³Ω„Ψ§Ω…', { 'Ψ³', 'Ω„Ψ§', 'Ω…' }) lib.p_arshape = false -- cgit From f289161c3cf19222e7ee40fabe0b3935f8ad6bdf Mon Sep 17 00:00:00 2001 From: dundargoc Date: Sat, 7 Sep 2024 12:35:54 +0200 Subject: test: add termkey unit tests Skipped importing the following unit tests from libtermkey as they'd require introducing a lot of unused code or require more effort to port than is probably worth: - 05read - 12strpkey - 20canon - 40ti-override --- test/unit/termkey_spec.lua | 975 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 975 insertions(+) create mode 100644 test/unit/termkey_spec.lua (limited to 'test/unit') diff --git a/test/unit/termkey_spec.lua b/test/unit/termkey_spec.lua new file mode 100644 index 0000000000..ffb83d5852 --- /dev/null +++ b/test/unit/termkey_spec.lua @@ -0,0 +1,975 @@ +local t = require('test.unit.testutil') +local itp = t.gen_itp(it) +local bit = require('bit') + +--- @alias TermKeyKey {utf8: string, type: integer, modifiers: integer, code: {codepoint: integer, sym: any, number: integer}} + +--- @class termkey +--- @field TERMKEY_CANON_SPACESYMBOL integer +--- @field TERMKEY_FLAG_SPACESYMBOL integer +--- @field TERMKEY_FLAG_UTF8 integer +--- @field TERMKEY_FORMAT_ALTISMETA integer +--- @field TERMKEY_FORMAT_CARETCTRL integer +--- @field TERMKEY_FORMAT_LONGMOD integer +--- @field TERMKEY_FORMAT_LOWERMOD integer +--- @field TERMKEY_FORMAT_LOWERSPACE integer +--- @field TERMKEY_FORMAT_MOUSE_POS integer +--- @field TERMKEY_FORMAT_SPACEMOD integer +--- @field TERMKEY_FORMAT_WRAPBRACKET integer +--- @field TERMKEY_KEYMOD_ALT integer +--- @field TERMKEY_KEYMOD_CTRL integer +--- @field TERMKEY_MOUSE_DRAG integer +--- @field TERMKEY_MOUSE_PRESS integer +--- @field TERMKEY_MOUSE_RELEASE integer +--- @field TERMKEY_RES_AGAIN integer +--- @field TERMKEY_RES_KEY integer +--- @field TERMKEY_RES_NONE integer +--- @field TERMKEY_SYM_DOWN integer +--- @field TERMKEY_SYM_PAGEUP integer +--- @field TERMKEY_SYM_SPACE integer +--- @field TERMKEY_SYM_UNKNOWN integer +--- @field TERMKEY_SYM_UP integer +--- @field TERMKEY_TYPE_DCS integer +--- @field TERMKEY_TYPE_FUNCTION integer +--- @field TERMKEY_TYPE_KEYSYM integer +--- @field TERMKEY_TYPE_MODEREPORT integer +--- @field TERMKEY_TYPE_MOUSE integer +--- @field TERMKEY_TYPE_OSC integer +--- @field TERMKEY_TYPE_POSITION integer +--- @field TERMKEY_TYPE_UNICODE integer +--- @field TERMKEY_TYPE_UNKNOWN_CSI integer +--- @field termkey_canonicalise fun(any, any):any +--- @field termkey_destroy fun(any) +--- @field termkey_get_buffer_remaining fun(any):integer +--- @field termkey_get_buffer_size fun(any):integer +--- @field termkey_get_canonflags fun(any):any +--- @field termkey_get_keyname fun(any, any):any +--- @field termkey_getkey fun(any, any):any +--- @field termkey_getkey_force fun(any, any):any +--- @field termkey_interpret_csi fun(any, any, any, any, any):any +--- @field termkey_interpret_modereport fun(any, any, any, any, any):any +--- @field termkey_interpret_mouse fun(any, any, TermKeyKey, integer, integer, integer):any +--- @field termkey_interpret_position fun(any, any, any, any):any +--- @field termkey_interpret_string fun(any, TermKeyKey, any):any +--- @field termkey_lookup_keyname fun(any, any, any):any +--- @field termkey_new_abstract fun(string, integer):any +--- @field termkey_push_bytes fun(any, string, integer):integer +--- @field termkey_set_buffer_size fun(any, integer):integer +--- @field termkey_set_canonflags fun(any, any):any +--- @field termkey_set_flags fun(any, integer) +--- @field termkey_start fun(any):integer +--- @field termkey_stop fun(any):integer +--- @field termkey_strfkey fun(any, string, integer, any, any):integer +local termkey = t.cimport( + './src/nvim/tui/termkey/termkey.h', + './src/nvim/tui/termkey/termkey-internal.h', + './src/nvim/tui/termkey/termkey_defs.h', + './src/nvim/tui/termkey/driver-csi.h' +) + +describe('termkey', function() + itp('01base', function() + local tk = termkey.termkey_new_abstract('vt100', 0) + t.neq(tk, nil) + + t.eq(termkey.termkey_get_buffer_size(tk), 256) + t.eq(tk.is_started, 1) -- tk->is_started true after construction + + termkey.termkey_stop(tk) + t.neq(tk.is_started, 1) -- tk->is_started false after termkey_stop() + + termkey.termkey_start(tk) + t.eq(tk.is_started, 1) -- tk->is_started true after termkey_start() + + termkey.termkey_destroy(tk) + end) + + itp('02getkey', function() + local tk = termkey.termkey_new_abstract('vt100', 0) + local key = t.ffi.new('TermKeyKey') ---@type TermKeyKey + + t.eq(termkey.termkey_get_buffer_remaining(tk), 256) -- buffer free initially 256 + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_NONE) -- getkey yields RES_NONE when empty + + t.eq(termkey.termkey_push_bytes(tk, 'h', 1), 1) -- push_bytes returns 1 + + t.eq(termkey.termkey_get_buffer_remaining(tk), 255) -- buffer free 255 after push_bytes + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY after h + + t.eq(key.type, termkey.TERMKEY_TYPE_UNICODE) -- key.type after h + t.eq(key.code.codepoint, string.byte('h')) -- key.code.codepoint after h + t.eq(key.modifiers, 0) -- key.modifiers after h + t.eq(t.ffi.string(key.utf8), 'h') -- key.utf8 after h + + t.eq(termkey.termkey_get_buffer_remaining(tk), 256) -- buffer free 256 after getkey + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_NONE) -- getkey yields RES_NONE a second time + + termkey.termkey_push_bytes(tk, '\x01', 1) + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY after C-a + + t.eq(key.type, termkey.TERMKEY_TYPE_UNICODE) -- key.type after C-a + t.eq(key.code.codepoint, string.byte('a')) -- key.code.codepoint after C-a + t.eq(key.modifiers, termkey.TERMKEY_KEYMOD_CTRL) -- key.modifiers after C-a + + termkey.termkey_push_bytes(tk, '\033OA', 3) + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY after Up + + -- is_int(key.type, TERMKEY_TYPE_KEYSYM, "key.type after Up"); + -- is_int(key.code.sym, TERMKEY_SYM_UP, "key.code.sym after Up"); + t.eq(key.modifiers, 0) -- key.modifiers after Up + + t.eq(termkey.termkey_push_bytes(tk, '\033O', 2), 2) -- push_bytes returns 2 + + -- is_int(termkey_get_buffer_remaining(tk), 254, "buffer free 254 after partial write"); + + -- is_int(termkey_getkey(tk, &key), TERMKEY_RES_AGAIN, "getkey yields RES_AGAIN after partial write"); + + termkey.termkey_push_bytes(tk, 'C', 1) + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY after Right completion + + -- is_int(key.type, TERMKEY_TYPE_KEYSYM, "key.type after Right"); + -- is_int(key.code.sym, TERMKEY_SYM_RIGHT, "key.code.sym after Right"); + -- is_int(key.modifiers, 0, "key.modifiers after Right"); + + -- is_int(termkey_get_buffer_remaining(tk), 256, "buffer free 256 after completion"); + + termkey.termkey_push_bytes(tk, '\033[27;5u', 7) + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY after Ctrl-Escape + + -- is_int(key.type, TERMKEY_TYPE_KEYSYM, "key.type after Ctrl-Escape"); + -- is_int(key.code.sym, TERMKEY_SYM_ESCAPE, "key.code.sym after Ctrl-Escape"); + -- is_int(key.modifiers, TERMKEY_KEYMOD_CTRL, "key.modifiers after Ctrl-Escape"); + + termkey.termkey_push_bytes(tk, '\0', 1) + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY after Ctrl-Space + + t.eq(key.type, termkey.TERMKEY_TYPE_UNICODE) -- key.type after Ctrl-Space + -- t.eq(key.code.codepoint, string.byte(' ')) -- key.code.codepoint after Ctrl-Space + -- is_int(key.modifiers, TERMKEY_KEYMOD_CTRL, "key.modifiers after Ctrl-Space"); + + termkey.termkey_destroy(tk) + end) + + itp('03utf8', function() + local tk = termkey.termkey_new_abstract('vt100', termkey.TERMKEY_FLAG_UTF8) + local key = t.ffi.new('TermKeyKey') ---@type TermKeyKey + + termkey.termkey_push_bytes(tk, 'a', 1) + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY low ASCII + t.eq(key.type, termkey.TERMKEY_TYPE_UNICODE) -- key.type low ASCII + t.eq(key.code.codepoint, string.byte('a')) -- key.code.codepoint low ASCII + + -- 2-byte UTF-8 range is U+0080 to U+07FF (0xDF 0xBF) + -- However, we'd best avoid the C1 range, so we'll start at U+00A0 (0xC2 0xA0) + + termkey.termkey_push_bytes(tk, '\xC2\xA0', 2) + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 2 low + t.eq(key.type, termkey.TERMKEY_TYPE_UNICODE) -- key.type UTF-8 2 low + t.eq(key.code.codepoint, 0x00A0) -- key.code.codepoint UTF-8 2 low + + termkey.termkey_push_bytes(tk, '\xDF\xBF', 2) + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 2 high + t.eq(key.type, termkey.TERMKEY_TYPE_UNICODE) -- key.type UTF-8 2 high + t.eq(key.code.codepoint, 0x07FF) -- key.code.codepoint UTF-8 2 high + + -- 3-byte UTF-8 range is U+0800 (0xE0 0xA0 0x80) to U+FFFD (0xEF 0xBF 0xBD) + + termkey.termkey_push_bytes(tk, '\xE0\xA0\x80', 3) + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 3 low + t.eq(key.type, termkey.TERMKEY_TYPE_UNICODE) -- key.type UTF-8 3 low + t.eq(key.code.codepoint, 0x0800) -- key.code.codepoint UTF-8 3 low + + termkey.termkey_push_bytes(tk, '\xEF\xBF\xBD', 3) + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 3 high + t.eq(key.type, termkey.TERMKEY_TYPE_UNICODE) -- key.type UTF-8 3 high + t.eq(key.code.codepoint, 0xFFFD) -- key.code.codepoint UTF-8 3 high + + -- 4-byte UTF-8 range is U+10000 (0xF0 0x90 0x80 0x80) to U+10FFFF (0xF4 0x8F 0xBF 0xBF) + + termkey.termkey_push_bytes(tk, '\xF0\x90\x80\x80', 4) + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 4 low + t.eq(key.type, termkey.TERMKEY_TYPE_UNICODE) -- key.type UTF-8 4 low + t.eq(key.code.codepoint, 0x10000) -- key.code.codepoint UTF-8 4 low + + termkey.termkey_push_bytes(tk, '\xF4\x8F\xBF\xBF', 4) + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 4 high + t.eq(key.type, termkey.TERMKEY_TYPE_UNICODE) -- key.type UTF-8 4 high + t.eq(key.code.codepoint, 0x10FFFF) -- key.code.codepoint UTF-8 4 high + + -- Invalid continuations + + termkey.termkey_push_bytes(tk, '\xC2!', 2) + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 2 invalid cont + t.eq(key.code.codepoint, 0xFFFD) -- key.code.codepoint UTF-8 2 invalid cont + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 2 invalid after + t.eq(key.code.codepoint, string.byte('!')) -- key.code.codepoint UTF-8 2 invalid after + + termkey.termkey_push_bytes(tk, '\xE0!', 2) + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 3 invalid cont + t.eq(key.code.codepoint, 0xFFFD) -- key.code.codepoint UTF-8 3 invalid cont + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 3 invalid after + t.eq(key.code.codepoint, string.byte('!')) -- key.code.codepoint UTF-8 3 invalid after + + termkey.termkey_push_bytes(tk, '\xE0\xA0!', 3) + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 3 invalid cont 2 + t.eq(key.code.codepoint, 0xFFFD) -- key.code.codepoint UTF-8 3 invalid cont 2 + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 3 invalid after + t.eq(key.code.codepoint, string.byte('!')) -- key.code.codepoint UTF-8 3 invalid after + + termkey.termkey_push_bytes(tk, '\xF0!', 2) + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 4 invalid cont + t.eq(key.code.codepoint, 0xFFFD) -- key.code.codepoint UTF-8 4 invalid cont + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 4 invalid after + t.eq(key.code.codepoint, string.byte('!')) -- key.code.codepoint UTF-8 4 invalid after + + termkey.termkey_push_bytes(tk, '\xF0\x90!', 3) + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 4 invalid cont 2 + t.eq(key.code.codepoint, 0xFFFD) -- key.code.codepoint UTF-8 4 invalid cont 2 + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 4 invalid after + t.eq(key.code.codepoint, string.byte('!')) -- key.code.codepoint UTF-8 4 invalid after + + termkey.termkey_push_bytes(tk, '\xF0\x90\x80!', 4) + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 4 invalid cont 3 + t.eq(key.code.codepoint, 0xFFFD) -- key.code.codepoint UTF-8 4 invalid cont 3 + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 4 invalid after + t.eq(key.code.codepoint, string.byte('!')) -- key.code.codepoint UTF-8 4 invalid after + + -- Partials + + termkey.termkey_push_bytes(tk, '\xC2', 1) + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_AGAIN) -- getkey yields RES_AGAIN UTF-8 2 partial + + termkey.termkey_push_bytes(tk, '\xA0', 1) + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 2 partial + t.eq(key.code.codepoint, 0x00A0) -- key.code.codepoint UTF-8 2 partial + + termkey.termkey_push_bytes(tk, '\xE0', 1) + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_AGAIN) -- getkey yields RES_AGAIN UTF-8 3 partial + + termkey.termkey_push_bytes(tk, '\xA0', 1) + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_AGAIN) -- getkey yields RES_AGAIN UTF-8 3 partial + + termkey.termkey_push_bytes(tk, '\x80', 1) + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 3 partial + t.eq(key.code.codepoint, 0x0800) -- key.code.codepoint UTF-8 3 partial + + termkey.termkey_push_bytes(tk, '\xF0', 1) + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_AGAIN) -- getkey yields RES_AGAIN UTF-8 4 partial + + termkey.termkey_push_bytes(tk, '\x90', 1) + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_AGAIN) -- getkey yields RES_AGAIN UTF-8 4 partial + + termkey.termkey_push_bytes(tk, '\x80', 1) + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_AGAIN) -- getkey yields RES_AGAIN UTF-8 4 partial + + termkey.termkey_push_bytes(tk, '\x80', 1) + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 4 partial + t.eq(key.code.codepoint, 0x10000) -- key.code.codepoint UTF-8 4 partial + + termkey.termkey_destroy(tk) + end) + + itp('04flags', function() + local tk = termkey.termkey_new_abstract('vt100', 0) + local key = t.ffi.new('TermKeyKey') ---@type TermKeyKey + + termkey.termkey_push_bytes(tk, ' ', 1) + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY after space + + t.eq(key.type, termkey.TERMKEY_TYPE_UNICODE) -- key.type after space + t.eq(key.code.codepoint, string.byte(' ')) -- key.code.codepoint after space + t.eq(key.modifiers, 0) -- key.modifiers after space + + termkey.termkey_set_flags(tk, termkey.TERMKEY_FLAG_SPACESYMBOL) + + termkey.termkey_push_bytes(tk, ' ', 1) + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY after space + + t.eq(key.type, termkey.TERMKEY_TYPE_KEYSYM) -- key.type after space with FLAG_SPACESYMBOL + t.eq(key.code.sym, termkey.TERMKEY_SYM_SPACE) -- key.code.sym after space with FLAG_SPACESYMBOL + t.eq(key.modifiers, 0) -- key.modifiers after space with FLAG_SPACESYMBOL + + termkey.termkey_destroy(tk) + end) + + itp('06buffer', function() + local tk = termkey.termkey_new_abstract('vt100', 0) + local key = t.ffi.new('TermKeyKey') ---@type TermKeyKey + + t.eq(termkey.termkey_get_buffer_remaining(tk), 256) -- buffer free initially 256 + t.eq(termkey.termkey_get_buffer_size(tk), 256) -- buffer size initially 256 + + t.eq(termkey.termkey_push_bytes(tk, 'h', 1), 1) -- push_bytes returns 1 + + t.eq(termkey.termkey_get_buffer_remaining(tk), 255) -- buffer free 255 after push_bytes + t.eq(termkey.termkey_get_buffer_size(tk), 256) -- buffer size 256 after push_bytes + + t.eq(not not termkey.termkey_set_buffer_size(tk, 512), true) -- buffer set size OK + + t.eq(termkey.termkey_get_buffer_remaining(tk), 511) -- buffer free 511 after push_bytes + t.eq(termkey.termkey_get_buffer_size(tk), 512) -- buffer size 512 after push_bytes + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- buffered key still useable after resize + + termkey.termkey_destroy(tk) + end) + + local function termkey_keyname2sym(tk, keyname) + local sym = t.ffi.new('TermKeySym[1]') + local endp = termkey.termkey_lookup_keyname(tk, keyname, sym) + if endp == nil then + return termkey.TERMKEY_SYM_UNKNOWN + end + return sym + end + + itp('10keyname', function() + local tk = termkey.termkey_new_abstract('vt100', 0) + + local sym = termkey_keyname2sym(tk, 'SomeUnknownKey') + t.eq(sym, termkey.TERMKEY_SYM_UNKNOWN) -- keyname2sym SomeUnknownKey + + sym = termkey_keyname2sym(tk, 'Space') + t.eq(sym[0], termkey.TERMKEY_SYM_SPACE) -- keyname2sym Space + + local _end = termkey.termkey_lookup_keyname(tk, 'Up', sym) + t.neq(_end, nil) -- termkey_get_keyname Up returns non-NULL + t.eq(t.ffi.string(_end), '') -- termkey_get_keyname Up return points at endofstring + t.eq(sym[0], termkey.TERMKEY_SYM_UP) -- termkey_get_keyname Up yields Up symbol + + _end = termkey.termkey_lookup_keyname(tk, 'DownMore', sym) + t.neq(_end, nil) -- termkey_get_keyname DownMore returns non-NULL + t.eq(t.ffi.string(_end), 'More') -- termkey_get_keyname DownMore return points at More + t.eq(sym[0], termkey.TERMKEY_SYM_DOWN) -- termkey_get_keyname DownMore yields Down symbol + + _end = termkey.termkey_lookup_keyname(tk, 'SomeUnknownKey', sym) + t.eq(_end, nil) -- termkey_get_keyname SomeUnknownKey returns NULL + + t.eq(t.ffi.string(termkey.termkey_get_keyname(tk, termkey.TERMKEY_SYM_SPACE)), 'Space') -- "get_keyname SPACE"); + + termkey.termkey_destroy(tk) + end) + + itp('11strfkey', function() + local tk = termkey.termkey_new_abstract('vt100', 0) + ---@type TermKeyKey + local key = t.ffi.new( + 'TermKeyKey', + { type = termkey.TERMKEY_TYPE_UNICODE, code = { codepoint = string.byte('A') } } + ) + local buffer = t.ffi.new('char[16]') + + local len = termkey.termkey_strfkey(tk, buffer, t.ffi.sizeof(buffer), key, 0) + t.eq(len, 1) -- length for unicode/A/0 + t.eq(t.ffi.string(buffer), 'A') -- buffer for unicode/A/0 + + len = termkey.termkey_strfkey( + tk, + buffer, + t.ffi.sizeof(buffer), + key, + termkey.TERMKEY_FORMAT_WRAPBRACKET + ) + t.eq(len, 1) -- length for unicode/A/0 wrapbracket + t.eq(t.ffi.string(buffer), 'A') -- buffer for unicode/A/0 wrapbracket + + ---@type TermKeyKey + key = t.ffi.new('TermKeyKey', { + type = termkey.TERMKEY_TYPE_UNICODE, + code = { codepoint = string.byte('b') }, + modifiers = termkey.TERMKEY_KEYMOD_CTRL, + }) + + len = termkey.termkey_strfkey(tk, buffer, t.ffi.sizeof(buffer), key, 0) + t.eq(len, 3) -- length for unicode/b/CTRL + t.eq(t.ffi.string(buffer), 'C-b') -- buffer for unicode/b/CTRL + + len = + termkey.termkey_strfkey(tk, buffer, t.ffi.sizeof(buffer), key, termkey.TERMKEY_FORMAT_LONGMOD) + t.eq(len, 6) -- length for unicode/b/CTRL longmod + t.eq(t.ffi.string(buffer), 'Ctrl-b') -- buffer for unicode/b/CTRL longmod + + len = termkey.termkey_strfkey( + tk, + buffer, + t.ffi.sizeof(buffer), + key, + bit.bor(termkey.TERMKEY_FORMAT_LONGMOD, termkey.TERMKEY_FORMAT_SPACEMOD) + ) + t.eq(len, 6) -- length for unicode/b/CTRL longmod|spacemod + t.eq(t.ffi.string(buffer), 'Ctrl b') -- buffer for unicode/b/CTRL longmod|spacemod + + len = termkey.termkey_strfkey( + tk, + buffer, + t.ffi.sizeof(buffer), + key, + bit.bor(termkey.TERMKEY_FORMAT_LONGMOD, termkey.TERMKEY_FORMAT_LOWERMOD) + ) + t.eq(len, 6) -- length for unicode/b/CTRL longmod|lowermod + t.eq(t.ffi.string(buffer), 'ctrl-b') -- buffer for unicode/b/CTRL longmod|lowermod + + len = termkey.termkey_strfkey( + tk, + buffer, + t.ffi.sizeof(buffer), + key, + bit.bor( + termkey.TERMKEY_FORMAT_LONGMOD, + termkey.TERMKEY_FORMAT_SPACEMOD, + termkey.TERMKEY_FORMAT_LOWERMOD + ) + ) + t.eq(len, 6) -- length for unicode/b/CTRL longmod|spacemod|lowermode + t.eq(t.ffi.string(buffer), 'ctrl b') -- buffer for unicode/b/CTRL longmod|spacemod|lowermode + + len = termkey.termkey_strfkey( + tk, + buffer, + t.ffi.sizeof(buffer), + key, + termkey.TERMKEY_FORMAT_CARETCTRL + ) + t.eq(len, 2) -- length for unicode/b/CTRL caretctrl + t.eq(t.ffi.string(buffer), '^B') -- buffer for unicode/b/CTRL caretctrl + + len = termkey.termkey_strfkey( + tk, + buffer, + t.ffi.sizeof(buffer), + key, + termkey.TERMKEY_FORMAT_WRAPBRACKET + ) + t.eq(len, 5) -- length for unicode/b/CTRL wrapbracket + t.eq(t.ffi.string(buffer), '') -- buffer for unicode/b/CTRL wrapbracket + + ---@type TermKeyKey + key = t.ffi.new('TermKeyKey', { + type = termkey.TERMKEY_TYPE_UNICODE, + code = { codepoint = string.byte('c') }, + modifiers = termkey.TERMKEY_KEYMOD_ALT, + }) + + len = termkey.termkey_strfkey(tk, buffer, t.ffi.sizeof(buffer), key, 0) + t.eq(len, 3) -- length for unicode/c/ALT + t.eq(t.ffi.string(buffer), 'A-c') -- buffer for unicode/c/ALT + + len = + termkey.termkey_strfkey(tk, buffer, t.ffi.sizeof(buffer), key, termkey.TERMKEY_FORMAT_LONGMOD) + t.eq(len, 5) -- length for unicode/c/ALT longmod + t.eq(t.ffi.string(buffer), 'Alt-c') -- buffer for unicode/c/ALT longmod + + len = termkey.termkey_strfkey( + tk, + buffer, + t.ffi.sizeof(buffer), + key, + termkey.TERMKEY_FORMAT_ALTISMETA + ) + t.eq(len, 3) -- length for unicode/c/ALT altismeta + t.eq(t.ffi.string(buffer), 'M-c') -- buffer for unicode/c/ALT altismeta + + len = termkey.termkey_strfkey( + tk, + buffer, + t.ffi.sizeof(buffer), + key, + bit.bor(termkey.TERMKEY_FORMAT_LONGMOD, termkey.TERMKEY_FORMAT_ALTISMETA) + ) + t.eq(len, 6) -- length for unicode/c/ALT longmod|altismeta + t.eq(t.ffi.string(buffer), 'Meta-c') -- buffer for unicode/c/ALT longmod|altismeta + + ---@type TermKeyKey + key = t.ffi.new( + 'TermKeyKey', + { type = termkey.TERMKEY_TYPE_KEYSYM, code = { sym = termkey.TERMKEY_SYM_UP } } + ) + + len = termkey.termkey_strfkey(tk, buffer, t.ffi.sizeof(buffer), key, 0) + t.eq(len, 2) -- length for sym/Up/0 + t.eq(t.ffi.string(buffer), 'Up') -- buffer for sym/Up/0 + + len = termkey.termkey_strfkey( + tk, + buffer, + t.ffi.sizeof(buffer), + key, + termkey.TERMKEY_FORMAT_WRAPBRACKET + ) + t.eq(len, 4) -- length for sym/Up/0 wrapbracket + t.eq(t.ffi.string(buffer), '') -- buffer for sym/Up/0 wrapbracket + + ---@type TermKeyKey + key = t.ffi.new( + 'TermKeyKey', + { type = termkey.TERMKEY_TYPE_KEYSYM, code = { sym = termkey.TERMKEY_SYM_PAGEUP } } + ) + + len = termkey.termkey_strfkey(tk, buffer, t.ffi.sizeof(buffer), key, 0) + t.eq(len, 6) -- length for sym/PageUp/0 + t.eq(t.ffi.string(buffer), 'PageUp') -- buffer for sym/PageUp/0 + + len = termkey.termkey_strfkey( + tk, + buffer, + t.ffi.sizeof(buffer), + key, + termkey.TERMKEY_FORMAT_LOWERSPACE + ) + t.eq(len, 7) -- length for sym/PageUp/0 lowerspace + t.eq(t.ffi.string(buffer), 'page up') -- buffer for sym/PageUp/0 lowerspace + + -- If size of buffer is too small, strfkey should return something consistent + len = termkey.termkey_strfkey(tk, buffer, 4, key, 0) + t.eq(len, 6) -- length for sym/PageUp/0 + t.eq(t.ffi.string(buffer), 'Pag') -- buffer of len 4 for sym/PageUp/0 + + len = termkey.termkey_strfkey(tk, buffer, 4, key, termkey.TERMKEY_FORMAT_LOWERSPACE) + t.eq(len, 7) -- length for sym/PageUp/0 lowerspace + t.eq(t.ffi.string(buffer), 'pag') -- buffer of len 4 for sym/PageUp/0 lowerspace + + key = t.ffi.new('TermKeyKey', { type = termkey.TERMKEY_TYPE_FUNCTION, code = { number = 5 } }) ---@type TermKeyKey + + len = termkey.termkey_strfkey(tk, buffer, t.ffi.sizeof(buffer), key, 0) + t.eq(len, 2) -- length for func/5/0 + t.eq(t.ffi.string(buffer), 'F5') -- buffer for func/5/0 + + len = termkey.termkey_strfkey( + tk, + buffer, + t.ffi.sizeof(buffer), + key, + termkey.TERMKEY_FORMAT_WRAPBRACKET + ) + t.eq(len, 4) -- length for func/5/0 wrapbracket + t.eq(t.ffi.string(buffer), '') -- buffer for func/5/0 wrapbracket + + len = termkey.termkey_strfkey( + tk, + buffer, + t.ffi.sizeof(buffer), + key, + termkey.TERMKEY_FORMAT_LOWERSPACE + ) + t.eq(len, 2) -- length for func/5/0 lowerspace + t.eq(t.ffi.string(buffer), 'f5') -- buffer for func/5/0 lowerspace + + termkey.termkey_destroy(tk) + end) + + itp('13cmpkey', function() + local function termkey_keycmp(tk, key1, key2) + termkey.termkey_canonicalise(tk, key1) + termkey.termkey_canonicalise(tk, key2) + + if key1.type ~= key2.type then + return key1.type - key2.type + end + + if key1.type == termkey.TERMKEY_TYPE_UNICODE then + if key1.code.codepoint ~= key2.code.codepoint then + return key1.code.codepoint - key2.code.codepoint + end + end + + return key1.modifiers - key2.modifiers + end + + local tk = termkey.termkey_new_abstract('vt100', 0) + ---@type TermKeyKey + local key1 = t.ffi.new('TermKeyKey', { + type = termkey.TERMKEY_TYPE_UNICODE, + code = { codepoint = string.byte('A') }, + modifiers = 0, + }) + ---@type TermKeyKey + local key2 = t.ffi.new('TermKeyKey', { + type = termkey.TERMKEY_TYPE_UNICODE, + code = { codepoint = string.byte('A') }, + modifiers = 0, + }) + + t.eq(termkey_keycmp(tk, key1, key1), 0) -- cmpkey same structure + t.eq(termkey_keycmp(tk, key1, key2), 0) -- cmpkey identical structure + + key2.modifiers = termkey.TERMKEY_KEYMOD_CTRL + + t.eq(termkey_keycmp(tk, key1, key2) < 0, true) -- cmpkey orders CTRL after nomod + t.eq(termkey_keycmp(tk, key2, key1) > 0, true) -- cmpkey orders nomod before CTRL + + key2.code.codepoint = string.byte('B') + key2.modifiers = 0 + + t.eq(termkey_keycmp(tk, key1, key2) < 0, true) -- cmpkey orders 'B' after 'A' + t.eq(termkey_keycmp(tk, key2, key1) > 0, true) -- cmpkey orders 'A' before 'B' + + key1.modifiers = termkey.TERMKEY_KEYMOD_CTRL + + t.eq(termkey_keycmp(tk, key1, key2) < 0, true) -- cmpkey orders nomod 'B' after CTRL 'A' + t.eq(termkey_keycmp(tk, key2, key1) > 0, true) -- cmpkey orders CTRL 'A' before nomod 'B' + + key2.type = termkey.TERMKEY_TYPE_KEYSYM + key2.code.sym = termkey.TERMKEY_SYM_UP + + t.eq(termkey_keycmp(tk, key1, key2) < 0, true) -- cmpkey orders KEYSYM after UNICODE + t.eq(termkey_keycmp(tk, key2, key1) > 0, true) -- cmpkey orders UNICODE before KEYSYM + + key1.type = termkey.TERMKEY_TYPE_KEYSYM + key1.code.sym = termkey.TERMKEY_SYM_SPACE + key1.modifiers = 0 + key2.type = termkey.TERMKEY_TYPE_UNICODE + key2.code.codepoint = string.byte(' ') + key2.modifiers = 0 + + t.eq(termkey_keycmp(tk, key1, key2), 0) -- cmpkey considers KEYSYM/SPACE and UNICODE/SP identical + + termkey.termkey_set_canonflags( + tk, + bit.bor(termkey.termkey_get_canonflags(tk), termkey.TERMKEY_CANON_SPACESYMBOL) + ) + t.eq(termkey_keycmp(tk, key1, key2), 0) -- "cmpkey considers KEYSYM/SPACE and UNICODE/SP identical under SPACESYMBOL"); + + termkey.termkey_destroy(tk) + end) + + itp('30mouse', function() + local tk = termkey.termkey_new_abstract('vt100', 0) + local key = t.ffi.new('TermKeyKey', { type = -1 }) ---@type TermKeyKey + local ev = t.ffi.new('TermKeyMouseEvent[1]') + local button = t.ffi.new('int[1]') + local line = t.ffi.new('int[1]') + local col = t.ffi.new('int[1]') + local buffer = t.ffi.new('char[32]') + + termkey.termkey_push_bytes(tk, '\x1b[M !!', 6) + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY for mouse press + + t.eq(key.type, termkey.TERMKEY_TYPE_MOUSE) -- key.type for mouse press + + t.eq(termkey.termkey_interpret_mouse(tk, key, ev, button, line, col), termkey.TERMKEY_RES_KEY) -- interpret_mouse yields RES_KEY + + t.eq(ev[0], termkey.TERMKEY_MOUSE_PRESS) -- mouse event for press + t.eq(button[0], 1) -- mouse button for press + t.eq(line[0], 1) -- mouse line for press + t.eq(col[0], 1) -- mouse column for press + t.eq(key.modifiers, 0) -- modifiers for press + + local len = termkey.termkey_strfkey(tk, buffer, t.ffi.sizeof(buffer), key, 0) + t.eq(len, 13) -- string length for press + t.eq(t.ffi.string(buffer), 'MousePress(1)') -- string buffer for press + + len = termkey.termkey_strfkey( + tk, + buffer, + t.ffi.sizeof(buffer), + key, + termkey.TERMKEY_FORMAT_MOUSE_POS + ) + t.eq(len, 21) -- string length for press + t.eq(t.ffi.string(buffer), 'MousePress(1) @ (1,1)') -- string buffer for press + + termkey.termkey_push_bytes(tk, '\x1b[M@"!', 6) + + termkey.termkey_getkey(tk, key) + t.eq(termkey.termkey_interpret_mouse(tk, key, ev, button, line, col), termkey.TERMKEY_RES_KEY) -- interpret_mouse yields RES_KEY + + t.eq(ev[0], termkey.TERMKEY_MOUSE_DRAG) -- mouse event for drag + t.eq(button[0], 1) -- mouse button for drag + t.eq(line[0], 1) -- mouse line for drag + t.eq(col[0], 2) -- mouse column for drag + t.eq(key.modifiers, 0) -- modifiers for press + + termkey.termkey_push_bytes(tk, '\x1b[M##!', 6) + + termkey.termkey_getkey(tk, key) + t.eq(termkey.termkey_interpret_mouse(tk, key, ev, button, line, col), termkey.TERMKEY_RES_KEY) -- interpret_mouse yields RES_KEY + + t.eq(ev[0], termkey.TERMKEY_MOUSE_RELEASE) -- mouse event for release + t.eq(line[0], 1) -- mouse line for release + t.eq(col[0], 3) -- mouse column for release + t.eq(key.modifiers, 0) -- modifiers for press + + termkey.termkey_push_bytes(tk, '\x1b[M0++', 6) + + termkey.termkey_getkey(tk, key) + t.eq(termkey.termkey_interpret_mouse(tk, key, ev, button, line, col), termkey.TERMKEY_RES_KEY) -- interpret_mouse yields RES_KEY + + t.eq(ev[0], termkey.TERMKEY_MOUSE_PRESS) -- mouse event for Ctrl-press + t.eq(button[0], 1) -- mouse button for Ctrl-press + t.eq(line[0], 11) -- mouse line for Ctrl-press + t.eq(col[0], 11) -- mouse column for Ctrl-press + t.eq(key.modifiers, termkey.TERMKEY_KEYMOD_CTRL) -- modifiers for Ctrl-press + + len = termkey.termkey_strfkey(tk, buffer, t.ffi.sizeof(buffer), key, 0) + t.eq(len, 15) -- string length for Ctrl-press + t.eq(t.ffi.string(buffer), 'C-MousePress(1)') -- string buffer for Ctrl-press + + termkey.termkey_push_bytes(tk, '\x1b[M`!!', 6) + + termkey.termkey_getkey(tk, key) + t.eq(termkey.termkey_interpret_mouse(tk, key, ev, button, line, col), termkey.TERMKEY_RES_KEY) -- interpret_mouse yields RES_KEY + + t.eq(ev[0], termkey.TERMKEY_MOUSE_PRESS) -- mouse event for wheel down + t.eq(button[0], 4) -- mouse button for wheel down + + termkey.termkey_push_bytes(tk, '\x1b[Mb!!', 6) + + termkey.termkey_getkey(tk, key) + t.eq(termkey.termkey_interpret_mouse(tk, key, ev, button, line, col), termkey.TERMKEY_RES_KEY) -- interpret_mouse yields RES_KEY + + t.eq(ev[0], termkey.TERMKEY_MOUSE_PRESS) -- mouse event for wheel left + t.eq(button[0], 6) -- mouse button for wheel left + + -- rxvt protocol + termkey.termkey_push_bytes(tk, '\x1b[0;20;20M', 10) + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY for mouse press rxvt protocol + + t.eq(key.type, termkey.TERMKEY_TYPE_MOUSE) -- key.type for mouse press rxvt protocol + + t.eq(termkey.termkey_interpret_mouse(tk, key, ev, button, line, col), termkey.TERMKEY_RES_KEY) -- interpret_mouse yields RES_KEY + + t.eq(ev[0], termkey.TERMKEY_MOUSE_PRESS) -- mouse event for press rxvt protocol + t.eq(button[0], 1) -- mouse button for press rxvt protocol + t.eq(line[0], 20) -- mouse line for press rxvt protocol + t.eq(col[0], 20) -- mouse column for press rxvt protocol + t.eq(key.modifiers, 0) -- modifiers for press rxvt protocol + + termkey.termkey_push_bytes(tk, '\x1b[3;20;20M', 10) + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY for mouse release rxvt protocol + + t.eq(key.type, termkey.TERMKEY_TYPE_MOUSE) -- key.type for mouse release rxvt protocol + + t.eq(termkey.termkey_interpret_mouse(tk, key, ev, button, line, col), termkey.TERMKEY_RES_KEY) -- interpret_mouse yields RES_KEY + + t.eq(ev[0], termkey.TERMKEY_MOUSE_RELEASE) -- mouse event for release rxvt protocol + t.eq(line[0], 20) -- mouse line for release rxvt protocol + t.eq(col[0], 20) -- mouse column for release rxvt protocol + t.eq(key.modifiers, 0) -- modifiers for release rxvt protocol + + -- SGR protocol + termkey.termkey_push_bytes(tk, '\x1b[<0;30;30M', 11) + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY for mouse press SGR encoding + + t.eq(key.type, termkey.TERMKEY_TYPE_MOUSE) -- key.type for mouse press SGR encoding + + t.eq(termkey.termkey_interpret_mouse(tk, key, ev, button, line, col), termkey.TERMKEY_RES_KEY) -- interpret_mouse yields RES_KEY + + t.eq(ev[0], termkey.TERMKEY_MOUSE_PRESS) -- mouse event for press SGR + t.eq(button[0], 1) -- mouse button for press SGR + t.eq(line[0], 30) -- mouse line for press SGR + t.eq(col[0], 30) -- mouse column for press SGR + t.eq(key.modifiers, 0) -- modifiers for press SGR + + termkey.termkey_push_bytes(tk, '\x1b[<0;30;30m', 11) + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY for mouse release SGR encoding + + t.eq(key.type, termkey.TERMKEY_TYPE_MOUSE) -- key.type for mouse release SGR encoding + + t.eq(termkey.termkey_interpret_mouse(tk, key, ev, button, line, col), termkey.TERMKEY_RES_KEY) -- interpret_mouse yields RES_KEY + + t.eq(ev[0], termkey.TERMKEY_MOUSE_RELEASE) -- mouse event for release SGR + + termkey.termkey_push_bytes(tk, '\x1b[<0;500;300M', 13) + + termkey.termkey_getkey(tk, key) + termkey.termkey_interpret_mouse(tk, key, ev, button, line, col) + + t.eq(line[0], 300) -- mouse line for press SGR wide + t.eq(col[0], 500) -- mouse column for press SGR wide + + termkey.termkey_destroy(tk) + end) + + itp('31position', function() + local tk = termkey.termkey_new_abstract('vt100', 0) + local key = t.ffi.new('TermKeyKey') ---@type TermKeyKey + local line = t.ffi.new('int[1]') + local col = t.ffi.new('int[1]') + + termkey.termkey_push_bytes(tk, '\x1b[?15;7R', 8) + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY for position report + + t.eq(key.type, termkey.TERMKEY_TYPE_POSITION) -- key.type for position report + + t.eq(termkey.termkey_interpret_position(tk, key, line, col), termkey.TERMKEY_RES_KEY) -- interpret_position yields RES_KEY + + t.eq(line[0], 15) -- line for position report + t.eq(col[0], 7) -- column for position report + + -- A plain CSI R is likely to be though. + -- This is tricky :/ + + termkey.termkey_push_bytes(tk, '\x1b[R', 3) + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY for + + t.eq(key.type, termkey.TERMKEY_TYPE_FUNCTION) -- key.type for + t.eq(key.code.number, 3) -- key.code.number for + + termkey.termkey_destroy(tk) + end) + + itp('32modereport', function() + local tk = termkey.termkey_new_abstract('vt100', 0) + local key = t.ffi.new('TermKeyKey') ---@type TermKeyKey + local initial = t.ffi.new('int[1]') + local mode = t.ffi.new('int[1]') + local value = t.ffi.new('int[1]') + + termkey.termkey_push_bytes(tk, '\x1b[?1;2$y', 8) + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY for mode report + + t.eq(key.type, termkey.TERMKEY_TYPE_MODEREPORT) -- key.type for mode report + + t.eq( + termkey.termkey_interpret_modereport(tk, key, initial, mode, value), + termkey.TERMKEY_RES_KEY + ) -- interpret_modereoprt yields RES_KEY + + t.eq(initial[0], 63) -- initial indicator from mode report + t.eq(mode[0], 1) -- mode number from mode report + t.eq(value[0], 2) -- mode value from mode report + + termkey.termkey_push_bytes(tk, '\x1b[4;1$y', 7) + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY for mode report + + t.eq(key.type, termkey.TERMKEY_TYPE_MODEREPORT) -- key.type for mode report + + t.eq( + termkey.termkey_interpret_modereport(tk, key, initial, mode, value), + termkey.TERMKEY_RES_KEY + ) -- interpret_modereoprt yields RES_KEY + + t.eq(initial[0], 0) -- initial indicator from mode report + t.eq(mode[0], 4) -- mode number from mode report + t.eq(value[0], 1) -- mode value from mode report + + termkey.termkey_destroy(tk) + end) + + itp('38csi', function() + local tk = termkey.termkey_new_abstract('vt100', 0) + local key = t.ffi.new('TermKeyKey') ---@type TermKeyKey + local args = t.ffi.new('TermKeyCsiParam[16]') + local nargs = t.ffi.new('size_t[1]') + local command = t.ffi.new('unsigned[1]') + + termkey.termkey_push_bytes(tk, '\x1b[5;25v', 7) + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY for CSI v + + t.eq(key.type, termkey.TERMKEY_TYPE_UNKNOWN_CSI) -- key.type for unknown CSI + + t.eq(termkey.termkey_interpret_csi(tk, key, args, nargs, command), termkey.TERMKEY_RES_KEY) -- interpret_csi yields RES_KEY + + t.eq(nargs[0], 2) -- nargs for unknown CSI + -- t.eq(args[0], 5) -- args[0] for unknown CSI + -- t.eq(args[1], 25) -- args[1] for unknown CSI + t.eq(command[0], 118) -- command for unknown CSI + + termkey.termkey_push_bytes(tk, '\x1b[?w', 4) + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY for CSI ? w + t.eq(key.type, termkey.TERMKEY_TYPE_UNKNOWN_CSI) -- key.type for unknown CSI + t.eq(termkey.termkey_interpret_csi(tk, key, args, nargs, command), termkey.TERMKEY_RES_KEY) -- interpret_csi yields RES_KEY + t.eq(command[0], bit.bor(bit.lshift(63, 8), 119)) -- command for unknown CSI + + termkey.termkey_push_bytes(tk, '\x1b[?$x', 5) + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY for CSI ? $x + t.eq(key.type, termkey.TERMKEY_TYPE_UNKNOWN_CSI) -- key.type for unknown CSI + t.eq(termkey.termkey_interpret_csi(tk, key, args, nargs, command), termkey.TERMKEY_RES_KEY) -- interpret_csi yields RES_KEY + t.eq(command[0], bit.bor(bit.lshift(36, 16), bit.lshift(63, 8), 120)) -- command for unknown CSI + + termkey.termkey_destroy(tk) + end) + + itp('39dcs', function() + local tk = termkey.termkey_new_abstract('xterm', 0) + local key = t.ffi.new('TermKeyKey') ---@type TermKeyKey + + -- 7bit DCS + termkey.termkey_push_bytes(tk, '\x1bP1$r1 q\x1b\\', 10) + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY for DCS + + t.eq(key.type, termkey.TERMKEY_TYPE_DCS) -- key.type for DCS + t.eq(key.modifiers, 0) -- key.modifiers for DCS + + local str = t.ffi.new('const char*[1]') + t.eq(termkey.termkey_interpret_string(tk, key, str), termkey.TERMKEY_RES_KEY) -- termkey_interpret_string() gives string + t.eq(t.ffi.string(str[0]), '1$r1 q') -- termkey_interpret_string() yields correct string + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_NONE) -- getkey again yields RES_NONE + + -- 8bit DCS + termkey.termkey_push_bytes(tk, '\x901$r2 q\x9c', 8) + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY for DCS + + t.eq(key.type, termkey.TERMKEY_TYPE_DCS) -- key.type for DCS + t.eq(key.modifiers, 0) -- key.modifiers for DCS + + t.eq(termkey.termkey_interpret_string(tk, key, str), termkey.TERMKEY_RES_KEY) -- "termkey_interpret_string() gives string"); + t.eq(t.ffi.string(str[0]), '1$r2 q') -- "termkey_interpret_string() yields correct string"); + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_NONE) -- "getkey again yields RES_NONE"); + + -- 7bit OSC + termkey.termkey_push_bytes(tk, '\x1b]15;abc\x1b\\', 10) + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY for OSC + + t.eq(key.type, termkey.TERMKEY_TYPE_OSC) -- key.type for OSC + t.eq(key.modifiers, 0) -- key.modifiers for OSC + + t.eq(termkey.termkey_interpret_string(tk, key, str), termkey.TERMKEY_RES_KEY) -- "termkey_interpret_string() gives string"); + t.eq(t.ffi.string(str[0]), '15;abc') -- "termkey_interpret_string() yields correct string"); + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_NONE) -- getkey again yields RES_NONE + + -- False alarm + termkey.termkey_push_bytes(tk, '\x1bP', 2) + + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_AGAIN) -- getkey yields RES_AGAIN for false alarm + + t.eq(termkey.termkey_getkey_force(tk, key), termkey.TERMKEY_RES_KEY) -- getkey_force yields RES_KEY for false alarm + + t.eq(key.type, termkey.TERMKEY_TYPE_UNICODE) -- key.type for false alarm + t.eq(key.code.codepoint, string.byte('P')) -- key.code.codepoint for false alarm + t.eq(key.modifiers, termkey.TERMKEY_KEYMOD_ALT) -- key.modifiers for false alarm + + termkey.termkey_destroy(tk) + end) +end) -- cgit From 737f58e23230ea14f1648ac1fc7f442ea0f8563c Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Fri, 20 Sep 2024 07:34:50 +0200 Subject: refactor(api)!: rename Dictionary => Dict In the api_info() output: :new|put =map(filter(api_info().functions, '!has_key(v:val,''deprecated_since'')'), 'v:val') ... {'return_type': 'ArrayOf(Integer, 2)', 'name': 'nvim_win_get_position', 'method': v:true, 'parameters': [['Window', 'window']], 'since': 1} The `ArrayOf(Integer, 2)` return type didn't break clients when we added it, which is evidence that clients don't use the `return_type` field, thus renaming Dictionary => Dict in api_info() is not (in practice) a breaking change. --- test/unit/api/private_helpers_spec.lua | 6 +++--- test/unit/api/testutil.lua | 12 ++++++------ test/unit/statusline_spec.lua | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) (limited to 'test/unit') diff --git a/test/unit/api/private_helpers_spec.lua b/test/unit/api/private_helpers_spec.lua index a31374bd70..bdfc83a031 100644 --- a/test/unit/api/private_helpers_spec.lua +++ b/test/unit/api/private_helpers_spec.lua @@ -43,9 +43,9 @@ describe('vim_to_object', function() simple_test('converts empty string', '') simple_test('converts non-empty string', 'foobar') simple_test('converts integer 10', { [type_key] = int_type, value = 10 }) - simple_test('converts empty dictionary', {}) - simple_test('converts dictionary with scalar values', { test = 10, test2 = true, test3 = 'test' }) - simple_test('converts dictionary with containers inside', { test = {}, test2 = { 1, 2 } }) + simple_test('converts empty dict', {}) + simple_test('converts dict with scalar values', { test = 10, test2 = true, test3 = 'test' }) + simple_test('converts dict with containers inside', { test = {}, test2 = { 1, 2 } }) simple_test('converts empty list', { [type_key] = list_type }) simple_test('converts list with scalar values', { 1, 2, 'test', 'foo' }) simple_test( diff --git a/test/unit/api/testutil.lua b/test/unit/api/testutil.lua index 0946ef194c..bb387ae0e1 100644 --- a/test/unit/api/testutil.lua +++ b/test/unit/api/testutil.lua @@ -35,10 +35,10 @@ local function init_obj2lua_tab() end return ret end, - [tonumber(api.kObjectTypeDictionary)] = function(obj) + [tonumber(api.kObjectTypeDict)] = function(obj) local ret = {} - for i = 1, tonumber(obj.data.dictionary.size) do - local kv_pair = obj.data.dictionary.items[i - 1] + for i = 1, tonumber(obj.data.dict.size) do + local kv_pair = obj.data.dict.items[i - 1] ret[ffi.string(kv_pair.key.data, kv_pair.key.size)] = obj2lua(kv_pair.value) end return ret @@ -112,8 +112,8 @@ local lua2obj_type_tab = { end end local len = #kvs - local dct = obj(api.kObjectTypeDictionary, { - dictionary = { + local dct = obj(api.kObjectTypeDict, { + dict = { size = len, capacity = len, items = ffi.cast('KeyValuePair *', api.xmalloc(len * ffi.sizeof('KeyValuePair'))), @@ -121,7 +121,7 @@ local lua2obj_type_tab = { }) for i = 1, len do local key, val = unpack(kvs[i]) - dct.data.dictionary.items[i - 1] = ffi.new( + dct.data.dict.items[i - 1] = ffi.new( 'KeyValuePair', { key = ffi.gc(lua2obj(key), nil).data.string, value = ffi.gc(lua2obj(val), nil) } ) diff --git a/test/unit/statusline_spec.lua b/test/unit/statusline_spec.lua index 973d9ec992..a97a4f41d7 100644 --- a/test/unit/statusline_spec.lua +++ b/test/unit/statusline_spec.lua @@ -56,14 +56,14 @@ describe('build_stl_str_hl', function() -- @param input_stl The format string for the statusline -- @param expected_stl The expected result string for the statusline -- - -- @param arg Options can be placed in an optional dictionary as the last parameter + -- @param arg Options can be placed in an optional dict as the last parameter -- .expected_cell_count The expected number of cells build_stl_str_hl will return -- .expected_byte_length The expected byte length of the string (defaults to byte length of expected_stl) -- .file_name The name of the file to be tested (useful in %f type tests) -- .fillchar The character that will be used to fill any 'extra' space in the stl local function statusline_test(description, statusline_cell_count, input_stl, expected_stl, arg) -- arg is the optional parameter - -- so we either fill in option with arg or an empty dictionary + -- so we either fill in option with arg or an empty dict local option = arg or {} local fillchar = option.fillchar or ' ' -- cgit From a9287dd882e082a17fc7dcf004d3f991ed29001b Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 27 Sep 2024 07:40:46 +0800 Subject: fix(mbyte): check for utf8proc_map() failure (#30531) --- test/unit/mbyte_spec.lua | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'test/unit') diff --git a/test/unit/mbyte_spec.lua b/test/unit/mbyte_spec.lua index 62390c8794..e0c0244989 100644 --- a/test/unit/mbyte_spec.lua +++ b/test/unit/mbyte_spec.lua @@ -347,4 +347,10 @@ describe('mbyte', function() check('LΜ“Μ‰Μ‘Μ’ΜŒΜšoΜŒΜ’Μ—Μ„Μ›Μ€rΜΜˆΜ•ΜˆΜŽΜè̇̅̄̄̐mΜ…Μ–ΜŸΜ„ΜŸΜš', {'LΜ“Μ‰Μ‘Μ’ΜŒΜš', 'oΜŒΜ’Μ—Μ„Μ›Μ€', 'rΜΜˆΜ•ΜˆΜŽΜ', 'è̇̅̄̄̐', 'mΜ…Μ–ΜŸΜ„ΜŸΜš'}) -- stylua: ignore end end) + + describe('utf_fold', function() + itp('does not crash with surrogates #30527', function() + eq(0xDDFB, lib.utf_fold(0xDDFB)) + end) + end) end) -- cgit From 2dfa6f6033f762243f776025aeb7dbc2b383342b Mon Sep 17 00:00:00 2001 From: bfredl Date: Mon, 16 Sep 2024 19:28:37 +0200 Subject: refactor(multibyte): neo-casefolding without allocation fixes #30400 --- test/unit/mbyte_spec.lua | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'test/unit') diff --git a/test/unit/mbyte_spec.lua b/test/unit/mbyte_spec.lua index e0c0244989..0a322ce651 100644 --- a/test/unit/mbyte_spec.lua +++ b/test/unit/mbyte_spec.lua @@ -351,6 +351,12 @@ describe('mbyte', function() describe('utf_fold', function() itp('does not crash with surrogates #30527', function() eq(0xDDFB, lib.utf_fold(0xDDFB)) + eq(0xd800, lib.utf_fold(0xd800)) -- high surrogate, invalid as a character + end) + + itp("doesn't crash on invalid codepoints", function() + eq(9000000, lib.utf_fold(9000000)) + eq(0, lib.utf_fold(0)) end) end) end) -- cgit From 2c937d723dd6f64705bf4d901254683ba32b2846 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Sun, 29 Sep 2024 11:54:12 +0200 Subject: docs: misc (#30177) Co-authored-by: Christian Clason Co-authored-by: Riley Bruins Co-authored-by: zeertzjq --- test/unit/mbyte_spec.lua | 2 +- test/unit/termkey_spec.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'test/unit') diff --git a/test/unit/mbyte_spec.lua b/test/unit/mbyte_spec.lua index 0a322ce651..bdc111de2c 100644 --- a/test/unit/mbyte_spec.lua +++ b/test/unit/mbyte_spec.lua @@ -350,7 +350,7 @@ describe('mbyte', function() describe('utf_fold', function() itp('does not crash with surrogates #30527', function() - eq(0xDDFB, lib.utf_fold(0xDDFB)) + eq(0xddfb, lib.utf_fold(0xddfb)) -- low surrogate, invalid as a character eq(0xd800, lib.utf_fold(0xd800)) -- high surrogate, invalid as a character end) diff --git a/test/unit/termkey_spec.lua b/test/unit/termkey_spec.lua index ffb83d5852..0381cfd15a 100644 --- a/test/unit/termkey_spec.lua +++ b/test/unit/termkey_spec.lua @@ -332,7 +332,7 @@ describe('termkey', function() t.eq(termkey.termkey_get_buffer_remaining(tk), 511) -- buffer free 511 after push_bytes t.eq(termkey.termkey_get_buffer_size(tk), 512) -- buffer size 512 after push_bytes - t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- buffered key still useable after resize + t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- buffered key still usable after resize termkey.termkey_destroy(tk) end) -- cgit