diff --git a/.gitignore b/.gitignore index e33b0ba01e..7e079cd84a 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,9 @@ /src/testdir/X* /src/testdir/valgrind.* +# Folder generated by the unit tests +/test/includes/post/ + # luarocks, not added as a subtree because of the large number of blobs /third-party/luarocks diff --git a/Makefile b/Makefile index cc6ebe1c79..27646711c0 100644 --- a/Makefile +++ b/Makefile @@ -36,6 +36,7 @@ cmake: clean deps clean: rm -rf build $(MAKE) -C src/testdir clean + $(MAKE) -C test/includes clean install: build/bin/nvim $(MAKE) -C build install diff --git a/scripts/unittest.sh b/scripts/unittest.sh index 600d6d5b3c..539fae6401 100644 --- a/scripts/unittest.sh +++ b/scripts/unittest.sh @@ -6,4 +6,5 @@ eval "$(luarocks path)" if [ -z "$BUSTED_OUTPUT_TYPE" ]; then export BUSTED_OUTPUT_TYPE="utf_terminal" fi +make -C ./test/includes busted --pattern=.moon -o $BUSTED_OUTPUT_TYPE ./test diff --git a/test/includes/Makefile b/test/includes/Makefile new file mode 100644 index 0000000000..740d571504 --- /dev/null +++ b/test/includes/Makefile @@ -0,0 +1,11 @@ +POST = $(shell find pre -name '*.h' | sed 's/^pre/post/') + +all: $(POST) + +$(POST): post/%: pre/% + @echo GEN $* + @mkdir -p $(@D) + @cc -std=c99 -E -P $< -o $@ + +clean: + rm -rf post diff --git a/test/includes/pre/sys/stat.h b/test/includes/pre/sys/stat.h new file mode 100644 index 0000000000..c6cac80913 --- /dev/null +++ b/test/includes/pre/sys/stat.h @@ -0,0 +1,26 @@ +#define _GNU_SOURCE +#include + +static const mode_t kS_IFMT = S_IFMT; +static const mode_t kS_IFSOCK = S_IFSOCK; +static const mode_t kS_IFLNK = S_IFLNK; +static const mode_t kS_IFREG = S_IFREG; +static const mode_t kS_IFBLK = S_IFBLK; +static const mode_t kS_IFDIR = S_IFDIR; +static const mode_t kS_IFCHR = S_IFCHR; +static const mode_t kS_IFIFO = S_IFIFO; +static const mode_t kS_ISUID = S_ISUID; +static const mode_t kS_ISGID = S_ISGID; +static const mode_t kS_ISVTX = S_ISVTX; +static const mode_t kS_IRWXU = S_IRWXU; +static const mode_t kS_IRUSR = S_IRUSR; +static const mode_t kS_IWUSR = S_IWUSR; +static const mode_t kS_IXUSR = S_IXUSR; +static const mode_t kS_IRWXG = S_IRWXG; +static const mode_t kS_IRGRP = S_IRGRP; +static const mode_t kS_IWGRP = S_IWGRP; +static const mode_t kS_IXGRP = S_IXGRP; +static const mode_t kS_IRWXO = S_IRWXO; +static const mode_t kS_IROTH = S_IROTH; +static const mode_t kS_IWOTH = S_IWOTH; +static const mode_t kS_IXOTH = S_IXOTH; diff --git a/test/unit/helpers.moon b/test/unit/helpers.moon index b1670d01ea..d41a7465d6 100644 --- a/test/unit/helpers.moon +++ b/test/unit/helpers.moon @@ -22,6 +22,9 @@ cimport = (path) -> return libnvim +cppimport = (path) -> + return cimport './test/includes/post/' .. path + cimport './src/types.h' -- take a pointer to a C-allocated string and return an interned @@ -37,8 +40,10 @@ to_cstr = (string) -> return { cimport: cimport + cppimport: cppimport internalize: internalize eq: (expected, actual) -> assert.are.same expected, actual + neq: (expected, actual) -> assert.are_not.same expected, actual ffi: ffi lib: libnvim cstr: cstr diff --git a/test/unit/os/fs.moon b/test/unit/os/fs.moon index 8f141d689a..c111c89049 100644 --- a/test/unit/os/fs.moon +++ b/test/unit/os/fs.moon @@ -1,5 +1,6 @@ -{:cimport, :internalize, :eq, :ffi, :lib, :cstr, :to_cstr} = require 'test.unit.helpers' +{:cimport, :cppimport, :internalize, :eq, :neq, :ffi, :lib, :cstr, :to_cstr} = require 'test.unit.helpers' require 'lfs' +require 'bit' -- fs = cimport './src/os/os.h' -- remove these statements once 'cimport' is working properly for misc1.h @@ -15,16 +16,20 @@ int mch_dirname(char_u *buf, int len); int mch_isdir(char_u * name); int is_executable(char_u *name); int mch_can_exe(char_u *name); +long mch_getperm(char_u *name); +int mch_setperm(char_u *name, long perm); ]] -- import constants parsed by ffi {:OK, :FAIL} = lib {:TRUE, :FALSE} = lib +cppimport 'sys/stat.h' + describe 'fs function', -> setup -> lfs.mkdir 'unit-test-directory' - lfs.touch 'unit-test-directory/test.file' + (io.open 'unit-test-directory/test.file', 'w').close! -- Since the tests are executed, they are called by an executable. We use -- that executable for several asserts. @@ -35,6 +40,7 @@ describe 'fs function', -> export directory, executable_name = string.match(absolute_executable, '^(.*)/(.*)$') teardown -> + os.remove 'unit-test-directory/test.file' lfs.rmdir 'unit-test-directory' describe 'mch_dirname', -> @@ -266,3 +272,51 @@ describe 'fs function', -> relative_executable = './' .. executable_name eq TRUE, (mch_can_exe relative_executable) lfs.chdir old_dir + + describe 'file permissions', -> + mch_getperm = (filename) -> + perm = fs.mch_getperm (to_cstr filename) + tonumber perm + + mch_setperm = (filename, perm) -> + fs.mch_setperm (to_cstr filename), perm + + bit_set = (number, check_bit) -> + if 0 == (bit.band number, check_bit) then false else true + + set_bit = (number, to_set) -> + return bit.bor number, to_set + + unset_bit = (number, to_unset) -> + return bit.band number, (bit.bnot to_unset) + + describe 'mch_getperm', -> + it 'returns -1 when the given file does not exist', -> + eq -1, (mch_getperm 'non-existing-file') + + it 'returns a perm > 0 when given an existing file', -> + assert.is_true (mch_getperm 'unit-test-directory') > 0 + + it 'returns S_IRUSR when the file is readable', -> + perm = mch_getperm 'unit-test-directory' + assert.is_true (bit_set perm, ffi.C.kS_IRUSR) + + describe 'mch_setperm', -> + it 'can set and unset the executable bit of a file', -> + perm = mch_getperm 'unit-test-directory/test.file' + + perm = unset_bit perm, ffi.C.kS_IXUSR + eq OK, (mch_setperm 'unit-test-directory/test.file', perm) + + perm = mch_getperm 'unit-test-directory/test.file' + assert.is_false (bit_set perm, ffi.C.kS_IXUSR) + + perm = set_bit perm, ffi.C.kS_IXUSR + eq OK, mch_setperm 'unit-test-directory/test.file', perm + + perm = mch_getperm 'unit-test-directory/test.file' + assert.is_true (bit_set perm, ffi.C.kS_IXUSR) + + it 'fails if given file does not exist', -> + perm = ffi.C.kS_IXUSR + eq FAIL, (mch_setperm 'non-existing-file', perm)