aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--test/unit/garray_spec.lua391
-rw-r--r--test/unit/garray_spec.moon271
2 files changed, 391 insertions, 271 deletions
diff --git a/test/unit/garray_spec.lua b/test/unit/garray_spec.lua
new file mode 100644
index 0000000000..ab38176c41
--- /dev/null
+++ b/test/unit/garray_spec.lua
@@ -0,0 +1,391 @@
+local helpers = require("test.unit.helpers")
+
+local cimport = helpers.cimport
+local internalize = helpers.internalize
+local eq = helpers.eq
+local neq = helpers.neq
+local ffi = helpers.ffi
+local lib = helpers.lib
+local cstr = helpers.cstr
+local to_cstr = helpers.to_cstr
+local NULL = helpers.NULL
+
+local garray = cimport('./src/nvim/garray.h')
+
+-- define a basic interface to garray. We could make it a lot nicer by
+-- constructing a moonscript class wrapper around garray. It could for
+-- example associate ga_clear_strings to the underlying garray cdata if the
+-- garray is a string array. But for now I estimate that that kind of magic
+-- might make testing less "transparant" (i.e.: the interface would become
+-- quite different as to how one would use it from C.
+
+-- accessors
+function ga_len(garr)
+ return garr[0].ga_len
+end
+
+function ga_maxlen(garr)
+ return garr[0].ga_maxlen
+end
+
+function ga_itemsize(garr)
+ return garr[0].ga_itemsize
+end
+
+function ga_growsize(garr)
+ return garr[0].ga_growsize
+end
+
+function ga_data(garr)
+ return garr[0].ga_data
+end
+
+-- derived accessors
+function ga_size(garr)
+ return ga_len(garr) * ga_itemsize(garr)
+end
+
+function ga_maxsize(garr)
+ return ga_maxlen(garr) * ga_itemsize(garr)
+end
+
+function ga_data_as_bytes(garr)
+ return ffi.cast('uint8_t *', ga_data(garr))
+end
+
+function ga_data_as_strings(garr)
+ return ffi.cast('char **', ga_data(garr))
+end
+
+function ga_data_as_ints(garr)
+ return ffi.cast('int *', ga_data(garr))
+end
+
+-- garray manipulation
+function ga_init(garr, itemsize, growsize)
+ return garray.ga_init(garr, itemsize, growsize)
+end
+
+function ga_clear(garr)
+ return garray.ga_clear(garr)
+end
+
+function ga_clear_strings(garr)
+ assert.is_true(ga_itemsize(garr) == ffi.sizeof('char *'))
+ return garray.ga_clear_strings(garr)
+end
+
+function ga_grow(garr, n)
+ return garray.ga_grow(garr, n)
+end
+
+function ga_concat(garr, str)
+ return garray.ga_concat(garr, to_cstr(str))
+end
+
+function ga_append(garr, b)
+ if type(b) == 'string' then
+ return garray.ga_append(garr, string.byte(b))
+ else
+ return garray.ga_append(garr, b)
+ end
+end
+
+function ga_concat_strings(garr)
+ return internalize(garray.ga_concat_strings(garr))
+end
+
+function ga_concat_strings_sep(garr, sep)
+ return internalize(garray.ga_concat_strings_sep(garr, to_cstr(sep)))
+end
+
+function ga_remove_duplicate_strings(garr)
+ return garray.ga_remove_duplicate_strings(garr)
+end
+
+-- derived manipulators
+function ga_set_len(garr, len)
+ assert.is_true(len <= ga_maxlen(garr))
+ garr[0].ga_len = len
+end
+
+function ga_inc_len(garr, by)
+ return ga_set_len(garr, ga_len(garr) + 1)
+end
+
+-- custom append functions
+-- not the C ga_append, which only works for bytes
+function ga_append_int(garr, it)
+ assert.is_true(ga_itemsize(garr) == ffi.sizeof('int'))
+ ga_grow(garr, 1)
+ local data = ga_data_as_ints(garr)
+ data[ga_len(garr)] = it
+ return ga_inc_len(garr, 1)
+end
+
+function ga_append_string(garr, it)
+ assert.is_true(ga_itemsize(garr) == ffi.sizeof('char *'))
+ -- make a non-garbage collected string and copy the lua string into it,
+ -- TODO(aktau): we should probably call xmalloc here, though as long as
+ -- xmalloc is based on malloc it should work.
+ local mem = ffi.C.malloc(string.len(it) + 1)
+ ffi.copy(mem, it)
+ ga_grow(garr, 1)
+ local data = ga_data_as_strings(garr)
+ data[ga_len(garr)] = mem
+ return ga_inc_len(garr, 1)
+end
+
+function ga_append_strings(garr, ...)
+ local prevlen = ga_len(garr)
+ local len = select('#', ...)
+ for i = 1, len do
+ ga_append_string(garr, select(i, ...))
+ end
+ return eq(prevlen + len, ga_len(garr))
+end
+
+function ga_append_ints(garr, ...)
+ local prevlen = ga_len(garr)
+ local len = select('#', ...)
+ for i = 1, len do
+ ga_append_int(garr, select(i, ...))
+ end
+ return eq(prevlen + len, ga_len(garr))
+end
+
+-- enhanced constructors
+local garray_ctype = ffi.typeof('garray_T[1]')
+function new_garray()
+ local garr = garray_ctype()
+ return ffi.gc(garr, ga_clear)
+end
+
+function new_string_garray()
+ local garr = garray_ctype()
+ ga_init(garr, ffi.sizeof("char_u *"), 1)
+ return ffi.gc(garr, ga_clear_strings)
+end
+
+function randomByte()
+ return ffi.cast('uint8_t', math.random(0, 255))
+end
+
+-- scramble the data in a garray
+function ga_scramble(garr)
+ local size, bytes = ga_size(garr), ga_data_as_bytes(garr)
+ for i = 0, size - 1 do
+ bytes[i] = randomByte()
+ end
+end
+
+describe('garray', function()
+ local itemsize = 14
+ local growsize = 95
+
+ describe('ga_init', function()
+ it('initializes the values of the garray', function()
+ local garr = new_garray()
+ ga_init(garr, itemsize, growsize)
+ eq(0, ga_len(garr))
+ eq(0, ga_maxlen(garr))
+ eq(growsize, ga_growsize(garr))
+ eq(itemsize, ga_itemsize(garr))
+ eq(NULL, ga_data(garr))
+ end)
+ end)
+
+ describe('ga_grow', function()
+ local new_and_grow
+ function new_and_grow(itemsize, growsize, req)
+ local garr = new_garray()
+ ga_init(garr, itemsize, growsize)
+ eq(0, ga_size(garr)) -- should be 0 at first
+ eq(NULL, ga_data(garr)) -- should be NULL
+ ga_grow(garr, req) -- add space for `req` items
+ return garr
+ end
+
+ it('grows by growsize items if num < growsize', function()
+ itemsize = 16
+ growsize = 4
+ local grow_by = growsize - 1
+ local garr = new_and_grow(itemsize, growsize, grow_by)
+ neq(NULL, ga_data(garr)) -- data should be a ptr to memory
+ eq(growsize, ga_maxlen(garr)) -- we requested LESS than growsize, so...
+ end)
+
+ it('grows by num items if num > growsize', function()
+ itemsize = 16
+ growsize = 4
+ local grow_by = growsize + 1
+ local garr = new_and_grow(itemsize, growsize, grow_by)
+ neq(NULL, ga_data(garr)) -- data should be a ptr to memory
+ eq(grow_by, ga_maxlen(garr)) -- we requested MORE than growsize, so...
+ end)
+
+ it('does not grow when nothing is requested', function()
+ local garr = new_and_grow(16, 4, 0)
+ eq(NULL, ga_data(garr))
+ eq(0, ga_maxlen(garr))
+ end)
+ end)
+
+ describe('ga_clear', function()
+ it('clears an already allocated array', function()
+ -- allocate and scramble an array
+ local garr = garray_ctype()
+ ga_init(garr, itemsize, growsize)
+ ga_grow(garr, 4)
+ ga_set_len(garr, 4)
+ ga_scramble(garr)
+
+ -- clear it and check
+ ga_clear(garr)
+ eq(NULL, ga_data(garr))
+ eq(0, ga_maxlen(garr))
+ eq(0, ga_len(garr))
+ end)
+ end)
+
+ describe('ga_append', function()
+ it('can append bytes', function()
+ -- this is the actual ga_append, the others are just emulated lua
+ -- versions
+ local garr = new_garray()
+ ga_init(garr, ffi.sizeof("uint8_t"), 1)
+ ga_append(garr, 'h')
+ ga_append(garr, 'e')
+ ga_append(garr, 'l')
+ ga_append(garr, 'l')
+ ga_append(garr, 'o')
+ ga_append(garr, 0)
+ local bytes = ga_data_as_bytes(garr)
+ eq('hello', ffi.string(bytes))
+ end)
+
+ it('can append integers', function()
+ local garr = new_garray()
+ ga_init(garr, ffi.sizeof("int"), 1)
+ local input = {
+ -20,
+ 94,
+ 867615,
+ 90927,
+ 86
+ }
+ ga_append_ints(garr, unpack(input))
+ local ints = ga_data_as_ints(garr)
+ for i = 0, #input - 1 do
+ eq(input[i + 1], ints[i])
+ end
+ end)
+
+ it('can append strings to a growing array of strings', function()
+ local garr = new_string_garray()
+ local input = {
+ "some",
+ "str",
+ "\r\n\r●●●●●●,,,",
+ "hmm",
+ "got it"
+ }
+ ga_append_strings(garr, unpack(input))
+ -- check that we can get the same strings out of the array
+ local strings = ga_data_as_strings(garr)
+ for i = 0, #input - 1 do
+ eq(input[i + 1], ffi.string(strings[i]))
+ end
+ end)
+ end)
+
+ describe('ga_concat', function()
+ it('concatenates the parameter to the growing byte array', function()
+ local garr = new_garray()
+ ga_init(garr, ffi.sizeof("char"), 1)
+ local str = "ohwell●●"
+ local loop = 5
+ for i = 1, loop do
+ ga_concat(garr, str)
+ end
+
+ -- ga_concat does NOT append the NUL in the src string to the
+ -- destination, you have to do that manually by calling something like
+ -- ga_append(gar, '\0'). I'ts always used like that in the vim
+ -- codebase. I feel that this is a bit of an unnecesesary
+ -- micro-optimization.
+ ga_append(garr, 0)
+ local result = ffi.string(ga_data_as_bytes(garr))
+ eq(string.rep(str, loop), result)
+ end)
+ end)
+
+ function test_concat_fn(input, fn, sep)
+ local garr = new_string_garray()
+ ga_append_strings(garr, unpack(input))
+ if sep == nil then
+ eq(table.concat(input, ','), fn(garr))
+ else
+ eq(table.concat(input, sep), fn(garr, sep))
+ end
+ end
+
+ describe('ga_concat_strings', function()
+ it('returns an empty string when concatenating an empty array', function()
+ test_concat_fn({ }, ga_concat_strings)
+ end)
+
+ it('can concatenate a non-empty array', function()
+ test_concat_fn({
+ 'oh',
+ 'my',
+ 'neovim'
+ }, ga_concat_strings)
+ end)
+ end)
+
+ describe('ga_concat_strings_sep', function()
+ it('returns an empty string when concatenating an empty array', function()
+ test_concat_fn({ }, ga_concat_strings_sep, '---')
+ end)
+
+ it('can concatenate a non-empty array', function()
+ local sep = '-●●-'
+ test_concat_fn({
+ 'oh',
+ 'my',
+ 'neovim'
+ }, ga_concat_strings_sep, sep)
+ end)
+ end)
+
+ describe('ga_remove_duplicate_strings', function()
+ it('sorts and removes duplicate strings', function()
+ local garr = new_string_garray()
+ local input = {
+ 'ccc',
+ 'aaa',
+ 'bbb',
+ 'ddd●●',
+ 'aaa',
+ 'bbb',
+ 'ccc',
+ 'ccc',
+ 'ddd●●'
+ }
+ local sorted_dedup_input = {
+ 'aaa',
+ 'bbb',
+ 'ccc',
+ 'ddd●●'
+ }
+ ga_append_strings(garr, unpack(input))
+ ga_remove_duplicate_strings(garr)
+ eq(#sorted_dedup_input, ga_len(garr))
+ local strings = ga_data_as_strings(garr)
+ for i = 0, #sorted_dedup_input - 1 do
+ eq(sorted_dedup_input[i + 1], ffi.string(strings[i]))
+ end
+ end)
+ end)
+end)
diff --git a/test/unit/garray_spec.moon b/test/unit/garray_spec.moon
deleted file mode 100644
index 5d4dbe690c..0000000000
--- a/test/unit/garray_spec.moon
+++ /dev/null
@@ -1,271 +0,0 @@
-{:cimport, :internalize, :eq, :neq, :ffi, :lib, :cstr, :to_cstr, :NULL} = require 'test.unit.helpers'
-
-garray = cimport './src/nvim/garray.h'
-
--- define a basic interface to garray. We could make it a lot nicer by
--- constructing a moonscript class wrapper around garray. It could for
--- example associate ga_clear_strings to the underlying garray cdata if the
--- garray is a string array. But for now I estimate that that kind of magic
--- might make testing less "transparant" (i.e.: the interface would become
--- quite different as to how one would use it from C.
-
--- accessors
-ga_len = (garr) ->
- garr[0].ga_len
-ga_maxlen = (garr) ->
- garr[0].ga_maxlen
-ga_itemsize = (garr) ->
- garr[0].ga_itemsize
-ga_growsize = (garr) ->
- garr[0].ga_growsize
-ga_data = (garr) ->
- garr[0].ga_data
-
--- derived accessors
-ga_size = (garr) ->
- ga_len(garr) * ga_itemsize(garr)
-ga_maxsize = (garr) ->
- ga_maxlen(garr) * ga_itemsize(garr)
-ga_data_as_bytes = (garr) ->
- ffi.cast('uint8_t *', ga_data(garr))
-ga_data_as_strings = (garr) ->
- ffi.cast('char **', ga_data(garr))
-ga_data_as_ints = (garr) ->
- ffi.cast('int *', ga_data(garr))
-
--- garray manipulation
-ga_init = (garr, itemsize, growsize) ->
- garray.ga_init(garr, itemsize, growsize)
-ga_clear = (garr) ->
- garray.ga_clear(garr)
-ga_clear_strings = (garr) ->
- assert.is_true(ga_itemsize(garr) == ffi.sizeof('char *'))
- garray.ga_clear_strings(garr)
-ga_grow = (garr, n) ->
- garray.ga_grow(garr, n)
-ga_concat = (garr, str) ->
- garray.ga_concat(garr, to_cstr(str))
-ga_append = (garr, b) ->
- if type(b) == 'string'
- garray.ga_append(garr, string.byte(b))
- else
- garray.ga_append(garr, b)
-ga_concat_strings = (garr) ->
- internalize(garray.ga_concat_strings(garr))
-ga_concat_strings_sep = (garr, sep) ->
- internalize(garray.ga_concat_strings_sep(garr, to_cstr(sep)))
-ga_remove_duplicate_strings = (garr) ->
- garray.ga_remove_duplicate_strings(garr)
-
--- derived manipulators
-ga_set_len = (garr, len) ->
- assert.is_true(len <= ga_maxlen(garr))
- garr[0].ga_len = len
-ga_inc_len = (garr, by) ->
- ga_set_len(garr, ga_len(garr) + 1)
-
--- custom append functions
--- not the C ga_append, which only works for bytes
-ga_append_int = (garr, it) ->
- assert.is_true(ga_itemsize(garr) == ffi.sizeof('int'))
-
- ga_grow(garr, 1)
- data = ga_data_as_ints(garr)
- data[ga_len(garr)] = it
- ga_inc_len(garr, 1)
-ga_append_string = (garr, it) ->
- assert.is_true(ga_itemsize(garr) == ffi.sizeof('char *'))
-
- -- make a non-garbage collected string and copy the lua string into it,
- -- TODO(aktau): we should probably call xmalloc here, though as long as
- -- xmalloc is based on malloc it should work.
- mem = ffi.C.malloc(string.len(it) + 1)
- ffi.copy(mem, it)
-
- ga_grow(garr, 1)
- data = ga_data_as_strings(garr)
- data[ga_len(garr)] = mem
- ga_inc_len(garr, 1)
-ga_append_strings = (garr, ...) ->
- prevlen = ga_len(garr)
- len = select('#', ...)
- for i = 1, len
- ga_append_string(garr, select(i, ...))
- eq prevlen + len, ga_len(garr)
-ga_append_ints = (garr, ...) ->
- prevlen = ga_len(garr)
- len = select('#', ...)
- for i = 1, len
- ga_append_int(garr, select(i, ...))
- eq prevlen + len, ga_len(garr)
-
--- enhanced constructors
-garray_ctype = ffi.typeof('garray_T[1]')
-new_garray = ->
- garr = garray_ctype()
- ffi.gc(garr, ga_clear)
-new_string_garray = ->
- garr = garray_ctype()
- ga_init(garr, ffi.sizeof("char_u *"), 1)
- ffi.gc(garr, ga_clear_strings)
-
-randomByte = ->
- ffi.cast('uint8_t', math.random(0, 255))
-
--- scramble the data in a garray
-ga_scramble = (garr) ->
- size, bytes = ga_size(garr), ga_data_as_bytes(garr)
-
- for i = 0, size - 1
- bytes[i] = randomByte()
-
-describe 'garray', ->
- itemsize = 14
- growsize = 95
-
- describe 'ga_init', ->
- it 'initializes the values of the garray', ->
- garr = new_garray()
- ga_init(garr, itemsize, growsize)
- eq 0, ga_len(garr)
- eq 0, ga_maxlen(garr)
- eq growsize, ga_growsize(garr)
- eq itemsize, ga_itemsize(garr)
- eq NULL, ga_data(garr)
-
- describe 'ga_grow', ->
- new_and_grow = (itemsize, growsize, req) ->
- garr = new_garray()
- ga_init(garr, itemsize, growsize)
-
- eq 0, ga_size(garr) -- should be 0 at first
- eq NULL, ga_data(garr) -- should be NULL
- ga_grow(garr, req) -- add space for `req` items
-
- garr
-
- it 'grows by growsize items if num < growsize', ->
- itemsize = 16
- growsize = 4
- grow_by = growsize - 1
- garr = new_and_grow(itemsize, growsize, grow_by)
- neq NULL, ga_data(garr) -- data should be a ptr to memory
- eq growsize, ga_maxlen(garr) -- we requested LESS than growsize, so...
-
- it 'grows by num items if num > growsize', ->
- itemsize = 16
- growsize = 4
- grow_by = growsize + 1
- garr = new_and_grow(itemsize, growsize, grow_by)
- neq NULL, ga_data(garr) -- data should be a ptr to memory
- eq grow_by, ga_maxlen(garr) -- we requested MORE than growsize, so...
-
- it 'does not grow when nothing is requested', ->
- garr = new_and_grow(16, 4, 0)
- eq NULL, ga_data(garr)
- eq 0, ga_maxlen(garr)
-
- describe 'ga_clear', ->
- it 'clears an already allocated array', ->
- -- allocate and scramble an array
- garr = garray_ctype()
- ga_init(garr, itemsize, growsize)
- ga_grow(garr, 4)
- ga_set_len(garr, 4)
- ga_scramble(garr)
-
- -- clear it and check
- ga_clear(garr)
- eq NULL, ga_data(garr)
- eq 0, ga_maxlen(garr)
- eq 0, ga_len(garr)
-
- describe 'ga_append', ->
- it 'can append bytes', ->
- -- this is the actual ga_append, the others are just emulated lua
- -- versions
- garr = new_garray()
- ga_init(garr, ffi.sizeof("uint8_t"), 1)
- ga_append(garr, 'h')
- ga_append(garr, 'e')
- ga_append(garr, 'l')
- ga_append(garr, 'l')
- ga_append(garr, 'o')
- ga_append(garr, 0)
- bytes = ga_data_as_bytes(garr)
- eq 'hello', ffi.string(bytes)
-
- it 'can append integers', ->
- garr = new_garray()
- ga_init(garr, ffi.sizeof("int"), 1)
- input = {-20, 94, 867615, 90927, 86}
- ga_append_ints(garr, unpack(input))
-
- ints = ga_data_as_ints(garr)
- for i = 0, #input - 1
- eq input[i+1], ints[i]
-
- it 'can append strings to a growing array of strings', ->
- garr = new_string_garray()
- input = {"some", "str", "\r\n\r●●●●●●,,,", "hmm", "got it"}
- ga_append_strings(garr, unpack(input))
-
- -- check that we can get the same strings out of the array
- strings = ga_data_as_strings(garr)
- for i = 0, #input - 1
- eq input[i+1], ffi.string(strings[i])
-
- describe 'ga_concat', ->
- it 'concatenates the parameter to the growing byte array', ->
- garr = new_garray()
- ga_init(garr, ffi.sizeof("char"), 1)
-
- str = "ohwell●●"
- loop = 5
- for i = 1, loop
- ga_concat(garr, str)
-
- -- ga_concat does NOT append the NUL in the src string to the
- -- destination, you have to do that manually by calling something like
- -- ga_append(gar, '\0'). I'ts always used like that in the vim
- -- codebase. I feel that this is a bit of an unnecesesary
- -- micro-optimization.
- ga_append(garr, 0)
-
- result = ffi.string(ga_data_as_bytes(garr))
- eq string.rep(str, loop), result
-
- test_concat_fn = (input, fn, sep) ->
- garr = new_string_garray()
- ga_append_strings(garr, unpack(input))
- if sep == nil
- eq table.concat(input, ','), fn(garr)
- else
- eq table.concat(input, sep), fn(garr, sep)
-
- describe 'ga_concat_strings', ->
- it 'returns an empty string when concatenating an empty array', ->
- test_concat_fn({}, ga_concat_strings)
- it 'can concatenate a non-empty array', ->
- test_concat_fn({'oh', 'my', 'neovim'}, ga_concat_strings)
-
- describe 'ga_concat_strings_sep', ->
- it 'returns an empty string when concatenating an empty array', ->
- test_concat_fn({}, ga_concat_strings_sep, '---')
- it 'can concatenate a non-empty array', ->
- sep = '-●●-'
- test_concat_fn({'oh', 'my', 'neovim'}, ga_concat_strings_sep, sep)
-
- describe 'ga_remove_duplicate_strings', ->
- it 'sorts and removes duplicate strings', ->
- garr = new_string_garray()
- input = {'ccc', 'aaa', 'bbb', 'ddd●●', 'aaa', 'bbb', 'ccc', 'ccc', 'ddd●●'}
- sorted_dedup_input = {'aaa', 'bbb', 'ccc', 'ddd●●'}
-
- ga_append_strings(garr, unpack(input))
- ga_remove_duplicate_strings(garr)
- eq #sorted_dedup_input, ga_len(garr)
-
- strings = ga_data_as_strings(garr)
- for i = 0, #sorted_dedup_input - 1
- eq sorted_dedup_input[i+1], ffi.string(strings[i])