mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	garray: add unit tests
Only append_ga_line() was not tested because it relies on global vim state.
This commit is contained in:
		 Nicolas Hillegeer
					Nicolas Hillegeer
				
			
				
					committed by
					
						 Thiago de Arruda
						Thiago de Arruda
					
				
			
			
				
	
			
			
			 Thiago de Arruda
						Thiago de Arruda
					
				
			
						parent
						
							42efbfd2fd
						
					
				
				
					commit
					ce9c49f222
				
			
							
								
								
									
										262
									
								
								test/unit/garray.moon
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										262
									
								
								test/unit/garray.moon
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,262 @@ | |||||||
|  | {:cimport, :internalize, :eq, :neq, :ffi, :lib, :cstr, :to_cstr} = require 'test.unit.helpers' | ||||||
|  |  | ||||||
|  | garray = cimport './src/garray.h' | ||||||
|  |  | ||||||
|  | -- handy constants | ||||||
|  | NULL = ffi.cast 'void*', 0 | ||||||
|  |  | ||||||
|  | -- 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_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 | ||||||
|  |  | ||||||
|  |   describe 'ga_concat_strings', -> | ||||||
|  |     it 'returns an empty string when concatenating an empty array', -> | ||||||
|  |       garr = new_string_garray() | ||||||
|  |       eq '', ga_concat_strings(garr) | ||||||
|  |  | ||||||
|  |     it 'can concatenate a non-empty array', -> | ||||||
|  |       garr = new_string_garray() | ||||||
|  |       input = {'oh', 'my', 'neovim'} | ||||||
|  |       ga_append_strings(garr, unpack(input)) | ||||||
|  |       eq table.concat(input, ','), ga_concat_strings(garr) | ||||||
|  |  | ||||||
|  |   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]) | ||||||
		Reference in New Issue
	
	Block a user