diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f10ab485a891fa517596ab4d689c61bca12e1c1d..4c5f83afe70ed8cce03254da75aa75ce2c64ce0b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -49,6 +49,12 @@ gkfs: interruptible: true needs: [] script: + # Change config.hpp with sed to enable extra features + - sed -i 's/constexpr auto use_atime = false;/constexpr auto use_atime = true;/g' "${CI_PROJECT_DIR}/include/config.hpp" + - sed -i 's/constexpr auto use_ctime = false;/constexpr auto use_ctime = true;/g' "${CI_PROJECT_DIR}/include/config.hpp" + - sed -i 's/constexpr auto use_mtime = false;/constexpr auto use_mtime = true;/g' "${CI_PROJECT_DIR}/include/config.hpp" + - sed -i 's/constexpr auto use_link_cnt = false;/constexpr auto use_link_cnt = true;/g' "${CI_PROJECT_DIR}/include/config.hpp" + - sed -i 's/constexpr auto use_blocks = false;/constexpr auto use_blocks = true;/g' "${CI_PROJECT_DIR}/include/config.hpp" - mkdir -p ${BUILD_PATH} && cd ${BUILD_PATH} - cmake -Wdev @@ -131,7 +137,7 @@ gkfs:integration: needs: ['gkfs'] parallel: matrix: - - SUBTEST: [ data, directories, operations, position, shell, status ] + - SUBTEST: [ data, status, syscalls, directories, operations, position, shell ] script: ## run tests @@ -227,6 +233,7 @@ gkfs:unit: - ctest -j $(nproc) -L unit::all --output-junit report.xml ## capture coverage information + - cd ${BUILD_PATH} - ${CI_SCRIPTS_DIR}/coverage.sh --verbose --capture unit diff --git a/CHANGELOG.md b/CHANGELOG.md index 19c3d3960e57edc5417976e528f977e1e03e8416..2c93c2d1793b1be563d31caa64c0d016ec3176f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] ### New +- Additional tests to increase code coverage ([!141](https://storage.bsc.es/gitlab/hpc/gekkofs/-/merge_requests/141)). +- GKFS_ENABLE_UNUSED_FUNCTIONS added to disable code to increase code coverage. ### Changed - Support parallelism for path resolution tests ([!145](https://storage.bsc.es/gitlab/hpc/gekkofs/-/merge_requests/145)). - Support parallelism for symlink tests ([!147](https://storage.bsc.es/gitlab/hpc/gekkofs/-/merge_requests/147)). @@ -14,7 +16,11 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ### Fixed - Using `unlink` now fails if it is a directory unless the `AT_REMOVEDIR` flag is used (POSIX compliance) ([!139](https://storage.bsc.es/gitlab/hpc/gekkofs/-/merge_requests/139)). - Support glibc-2.34 or newer with syscall_intercept [!146](https://storage.bsc.es/gitlab/hpc/gekkofs/-/merge_requests/146)). - +- Additional `#ifdef` to remove unused code ([!141](https://storage.bsc.es/gitlab/hpc/gekkofs/-/merge_requests/141)) +### Removed +### Fixed +- Using `unlink` now fails if it is a directory unless the `AT_REMOVEDIR` flag is used (POSIX compliance) ([!139](https://storage.bsc.es/gitlab/hpc/gekkofs/-/merge_requests/139)). +- fchdir generate a SIGSEV in debug mode (due to log) ([!141](https://storage.bsc.es/gitlab/hpc/gekkofs/-/merge_requests/141)) ## [0.9.1] - 2022-04-29 ### New diff --git a/CMakeLists.txt b/CMakeLists.txt index 60cfe42ce384111e92e71c755c1cf53d0d011b45..9a41ef5e4184d8f721aabf2a8cae8fefeb41cb12 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -178,6 +178,8 @@ if (GKFS_ENABLE_AGIOS) find_package(AGIOS REQUIRED) endif () +option(GKFS_ENABLE_UNUSED_FUNCTIONS "Enable unused functions compilation" OFF) + option(GKFS_ENABLE_PARALLAX "Enable Parallax db backend" OFF) option(GKFS_ENABLE_ROCKSDB "Enable ROCKSDB backend" ON) diff --git a/include/client/open_file_map.hpp b/include/client/open_file_map.hpp index 2da1d0806de08de8c5a19fef9de17b3a58299917..cc67913b5f4ed66cc0b7be96338cab9dc26714ad 100644 --- a/include/client/open_file_map.hpp +++ b/include/client/open_file_map.hpp @@ -34,6 +34,7 @@ #include #include #include +#include namespace gkfs::filemap { diff --git a/include/common/rpc/rpc_util.hpp b/include/common/rpc/rpc_util.hpp index 0cc26e6999f4d3f12015201465e63c4c5a73ab8f..eb595596c4974b6e0aa6810794c056f52cd7b8fc 100644 --- a/include/common/rpc/rpc_util.hpp +++ b/include/common/rpc/rpc_util.hpp @@ -44,8 +44,10 @@ bool_to_merc_bool(bool state); std::string get_my_hostname(bool short_hostname = false); +#ifdef GKFS_ENABLE_UNUSED_FUNCTIONS std::string get_host_by_name(const std::string& hostname); +#endif } // namespace gkfs::rpc diff --git a/src/client/gkfs_functions.cpp b/src/client/gkfs_functions.cpp index a9cb5069fc50cb4a5ee49f4c28cbe52ddc30a888..23db5e0f0a055530bcff78b607e12a5b6662f74f 100644 --- a/src/client/gkfs_functions.cpp +++ b/src/client/gkfs_functions.cpp @@ -411,6 +411,7 @@ gkfs_statfs(struct statfs* buf) { return 0; } +#ifdef GKFS_ENABLE_UNUSED_FUNCTIONS /** * gkfs wrapper for statvfs() system calls * errno may be set @@ -444,6 +445,7 @@ gkfs_statvfs(struct statvfs* buf) { ST_NOATIME | ST_NODIRATIME | ST_NOSUID | ST_NODEV | ST_SYNCHRONOUS; return 0; } +#endif /** * gkfs wrapper for lseek() system calls with available file descriptor @@ -1094,7 +1096,7 @@ gkfs_getdents64(unsigned int fd, struct linux_dirent64* dirp, #ifdef HAS_SYMLINKS - +#ifdef GKFS_ENABLE_UNUSED_FUNCTIONS /** * gkfs wrapper for make symlink() system calls * errno may be set @@ -1177,7 +1179,7 @@ gkfs_readlink(const std::string& path, char* buf, int bufsize) { std::strcpy(buf + CTX->mountdir().size(), md->target_path().c_str()); return path_size; } - +#endif #endif } // namespace gkfs::syscall diff --git a/src/client/hooks.cpp b/src/client/hooks.cpp index 037d6c8eec7b826b975df3f451d4116843a78f2a..5917b80a7b6813bdba5b8602881f15b6c9068856 100644 --- a/src/client/hooks.cpp +++ b/src/client/hooks.cpp @@ -712,8 +712,8 @@ hook_fchdir(unsigned int fd) { auto open_dir = CTX->file_map()->get_dir(fd); if(open_dir == nullptr) { // Cast did not succeeded: open_file is a regular file - LOG(ERROR, "{}() file descriptor refers to a normal file: '{}'", - __func__, open_dir->path()); + LOG(ERROR, "{}() file descriptor refers to a normal file", + __func__); return -EBADF; } diff --git a/src/common/rpc/rpc_util.cpp b/src/common/rpc/rpc_util.cpp index 604749b6d0358a2aa30f8503b1c1786eb29c9639..46970b82fdc0d793093092c8fc59f34acbbaa3a1 100644 --- a/src/common/rpc/rpc_util.cpp +++ b/src/common/rpc/rpc_util.cpp @@ -73,7 +73,7 @@ get_my_hostname(bool short_hostname) { return ""s; } - +#ifdef GKFS_ENABLE_UNUSED_FUNCTIONS string get_host_by_name(const string& hostname) { int err = 0; @@ -102,5 +102,6 @@ get_host_by_name(const string& hostname) { freeaddrinfo(addr); return addr_str; } +#endif } // namespace gkfs::rpc \ No newline at end of file diff --git a/tests/integration/CMakeLists.txt b/tests/integration/CMakeLists.txt index 497a8e558cfd5110e22166b3d87892898bd2d29a..775cfe4f3d720b0336ff70b14989e87a66818200 100644 --- a/tests/integration/CMakeLists.txt +++ b/tests/integration/CMakeLists.txt @@ -120,6 +120,13 @@ gkfs_add_python_test( SOURCE data/ ) +gkfs_add_python_test( + NAME test_syscalls + PYTHON_VERSION 3.6 + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/tests/integration + SOURCE syscalls/ +) + if(GKFS_INSTALL_TESTS) install(DIRECTORY harness DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/gkfs/tests/integration @@ -182,6 +189,14 @@ if(GKFS_INSTALL_TESTS) PATTERN ".pytest_cache" EXCLUDE ) + install(DIRECTORY syscalls + DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/gkfs/tests/integration + FILES_MATCHING + REGEX ".*\\.py" + PATTERN "__pycache__" EXCLUDE + PATTERN ".pytest_cache" EXCLUDE +) + install(DIRECTORY shell DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/gkfs/tests/integration FILES_MATCHING diff --git a/tests/integration/data/test_truncate.py b/tests/integration/data/test_truncate.py index 58887a799711572942ffcd95df23d4d026ce9a49..4d6fc40da3d758defd848bce889f46a33f9809dd 100644 --- a/tests/integration/data/test_truncate.py +++ b/tests/integration/data/test_truncate.py @@ -30,6 +30,7 @@ # from pathlib import Path import os import stat +import errno # @pytest.mark.xfail(reason="invalid errno returned on success") @@ -121,3 +122,35 @@ def test_truncate(gkfs_daemon, gkfs_client): ret = gkfs_client.file_compare(truncfile, truncfile_verify_2, trunc_size) assert ret.retval == 0 + + +# Tests failure cases +def test_fail_truncate(gkfs_daemon, gkfs_client): + truncfile = gkfs_daemon.mountdir / "trunc_file2" + # open and create test file + ret = gkfs_client.open(truncfile, os.O_CREAT | os.O_WRONLY, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) + + assert ret.retval != -1 + + buf_length = 256 + ret = gkfs_client.write_random(truncfile, buf_length) + assert ret.retval == buf_length + + ret = gkfs_client.truncate(truncfile, buf_length) + assert ret.retval == 0 + + # Size should not change + ret = gkfs_client.stat(truncfile) + assert ret.statbuf.st_size == buf_length + + # Truncate to a negative size + ret = gkfs_client.truncate(truncfile, -1) + assert ret.retval == -1 + assert ret.errno == errno.EINVAL + + # Truncate to a size greater than the file size + ret = gkfs_client.truncate(truncfile, buf_length + 1) + assert ret.retval == -1 + assert ret.errno == errno.EINVAL + + diff --git a/tests/integration/directories/test_directories.py b/tests/integration/directories/test_directories.py index 066b36962f9c77b5fdc59365dedce2b1a72e1711..52d036ba56f52a852628af785aeb89cbe1401e76 100644 --- a/tests/integration/directories/test_directories.py +++ b/tests/integration/directories/test_directories.py @@ -189,6 +189,15 @@ def test_mkdir(gkfs_daemon, gkfs_client): assert d.d_name == e[0] assert d.d_type == e[1] + # Try to open a file as a dir + ret = gkfs_client.opendir(file_a) + assert ret.dirp is None + assert ret.errno == errno.ENOTDIR + + # Try to remove a non empty dir + ret = gkfs_client.rmdir(topdir) + assert ret.retval == -1 + assert ret.errno == errno.ENOTEMPTY return diff --git a/tests/integration/harness/CMakeLists.txt b/tests/integration/harness/CMakeLists.txt index 30bae73e64f32b97161238955029ed951b11e89d..39bd95bb26b8965d85d4dea44327e096fbb943d7 100644 --- a/tests/integration/harness/CMakeLists.txt +++ b/tests/integration/harness/CMakeLists.txt @@ -63,6 +63,10 @@ add_executable(gkfs.io gkfs.io/symlink.cpp gkfs.io/directory_validate.cpp gkfs.io/unlink.cpp + gkfs.io/access.cpp + gkfs.io/statfs.cpp + gkfs.io/dup_validate.cpp + gkfs.io/syscall_coverage.cpp ) include(FetchContent) diff --git a/tests/integration/harness/gkfs.io/access.cpp b/tests/integration/harness/gkfs.io/access.cpp new file mode 100644 index 0000000000000000000000000000000000000000..49464d7cedec4522c8890dbfdae9f9e6c29316d3 --- /dev/null +++ b/tests/integration/harness/gkfs.io/access.cpp @@ -0,0 +1,105 @@ +/* + Copyright 2018-2022, Barcelona Supercomputing Center (BSC), Spain + Copyright 2015-2022, Johannes Gutenberg Universitaet Mainz, Germany + + This software was partially supported by the + EC H2020 funded project NEXTGenIO (Project ID: 671951, www.nextgenio.eu). + + This software was partially supported by the + ADA-FS project under the SPPEXA project funded by the DFG. + + This file is part of GekkoFS. + + GekkoFS is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + GekkoFS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GekkoFS. If not, see . + + SPDX-License-Identifier: GPL-3.0-or-later +*/ + +/* C++ includes */ +#include +#include +#include +#include +#include +#include + +/* C includes */ +#include +#include + +using json = nlohmann::json; + +struct access_options { + bool verbose{}; + std::string path{}; + int mask{}; + + REFL_DECL_STRUCT(access_options, REFL_DECL_MEMBER(bool, verbose), + REFL_DECL_MEMBER(std::string, path), + REFL_DECL_MEMBER(int, mask)); +}; + +struct access_output { + int retval; + int errnum; + + REFL_DECL_STRUCT(access_output, REFL_DECL_MEMBER(int, retval), + REFL_DECL_MEMBER(int, errnum)); +}; + +void +to_json(json& record, const access_output& out) { + record = serialize(out); +} + +void +access_exec(const access_options& opts) { + + auto rv = ::access(opts.path.c_str(), opts.mask); + if(rv == -1) { + if(opts.verbose) { + fmt::print( + "access(path=\"{}\", mask={}) = {}, errno: {} [{}]\n", + opts.path, opts.mask, rv, errno, ::strerror(errno)); + return; + } + } + + json out = access_output{rv, errno}; + fmt::print("{}\n", out.dump(2)); +} + +void +access_init(CLI::App& app) { + + // Create the option and subcommand objects + auto opts = std::make_shared(); + auto* cmd = app.add_subcommand("access", + "Execute the access() system call"); + + // Add options to cmd, binding them to opts + cmd->add_flag("-v,--verbose", opts->verbose, + "Produce human writeable output"); + + cmd->add_option("path", opts->path, "Path to file") + ->required() + ->type_name(""); + + cmd->add_option("mask", opts->mask, + "Mask to apply to the access check") + ->required() + ->type_name(""); + + cmd->callback([opts]() { access_exec(*opts); }); +} diff --git a/tests/integration/harness/gkfs.io/commands.hpp b/tests/integration/harness/gkfs.io/commands.hpp index e489a44b6b49fa9ae685889967151e4cfc1e06c4..c57ef5c40dcc3a6ba5d95c0f7c3895d476ab8df4 100644 --- a/tests/integration/harness/gkfs.io/commands.hpp +++ b/tests/integration/harness/gkfs.io/commands.hpp @@ -96,6 +96,12 @@ write_random_init(CLI::App& app); void truncate_init(CLI::App& app); +void +access_init(CLI::App& app); + +void +statfs_init(CLI::App& app); + // UTIL void file_compare_init(CLI::App& app); @@ -111,4 +117,10 @@ symlink_init(CLI::App& app); void unlink_init(CLI::App& app); +void +dup_validate_init(CLI::App& app); + +void +syscall_coverage_init(CLI::App& app); + #endif // IO_COMMANDS_HPP diff --git a/tests/integration/harness/gkfs.io/dup_validate.cpp b/tests/integration/harness/gkfs.io/dup_validate.cpp new file mode 100644 index 0000000000000000000000000000000000000000..acc084a37fe45ffc51eada3c7a6f54d7e706c4fd --- /dev/null +++ b/tests/integration/harness/gkfs.io/dup_validate.cpp @@ -0,0 +1,132 @@ +/* + Copyright 2018-2022, Barcelona Supercomputing Center (BSC), Spain + Copyright 2015-2022, Johannes Gutenberg Universitaet Mainz, Germany + + This software was partially supported by the + EC H2020 funded project NEXTGenIO (Project ID: 671951, www.nextgenio.eu). + + This software was partially supported by the + ADA-FS project under the SPPEXA project funded by the DFG. + + This file is part of GekkoFS. + + GekkoFS is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + GekkoFS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GekkoFS. If not, see . + + SPDX-License-Identifier: GPL-3.0-or-later +*/ + +/* C++ includes */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* C includes */ +#include +#include +#include +#include + +using json = nlohmann::json; + +struct dup_validate_options { + bool verbose{}; + std::string pathname; + + + REFL_DECL_STRUCT(dup_validate_options, REFL_DECL_MEMBER(bool, verbose), + REFL_DECL_MEMBER(std::string, pathname)); +}; + +struct dup_validate_output { + int retval; + int errnum; + + REFL_DECL_STRUCT(dup_validate_output, REFL_DECL_MEMBER(int, retval), + REFL_DECL_MEMBER(int, errnum)); +}; + +void +to_json(json& record, const dup_validate_output& out) { + record = serialize(out); +} + +void +dup_validate_exec(const dup_validate_options& opts) { + + int fd = ::open(opts.pathname.c_str(), O_WRONLY); + + if(fd == -1) { + if(opts.verbose) { + fmt::print( + "dup_validate(pathname=\"{}\") = {}, errno: {} [{}]\n", + opts.pathname, fd, errno, ::strerror(errno)); + return; + } + + json out = dup_validate_output{fd, errno}; + fmt::print("{}\n", out.dump(2)); + + return; + } + + + auto rv = ::dup(fd); + auto rv2 = ::dup2(fd, rv); + + + + if(opts.verbose) { + fmt::print( + "dup_validate(pathname=\"{}\") = {}, errno: {} [{}]\n", + opts.pathname, rv, errno, ::strerror(errno)); + return; + } + + if(rv < 0 || rv2 < 0) { + json out = dup_validate_output{(int) rv, errno}; + fmt::print("{}\n", out.dump(2)); + return; + } + + rv = 0; + errno = 0; + json out = dup_validate_output{(int) rv, errno}; + fmt::print("{}\n", out.dump(2)); + return; +} + +void +dup_validate_init(CLI::App& app) { + + // Create the option and subcommand objects + auto opts = std::make_shared(); + auto* cmd = app.add_subcommand( + "dup_validate", + "Execute dup, dup2, and dup3 returns 0 if the file descriptor is valid"); + + // Add options to cmd, binding them to opts + cmd->add_flag("-v,--verbose", opts->verbose, + "Produce human writeable output"); + + cmd->add_option("pathname", opts->pathname, "File name") + ->required() + ->type_name(""); + + cmd->callback([opts]() { dup_validate_exec(*opts); }); +} diff --git a/tests/integration/harness/gkfs.io/main.cpp b/tests/integration/harness/gkfs.io/main.cpp index c737722a9e180a63455b01bb1534d6b211501995..91514bba3e2ae7d042ca2607f2d113d68ce4857d 100644 --- a/tests/integration/harness/gkfs.io/main.cpp +++ b/tests/integration/harness/gkfs.io/main.cpp @@ -57,12 +57,16 @@ init_commands(CLI::App& app) { directory_validate_init(app); write_random_init(app); truncate_init(app); + access_init(app); + statfs_init(app); // utils file_compare_init(app); chdir_init(app); getcwd_validate_init(app); symlink_init(app); unlink_init(app); + dup_validate_init(app); + syscall_coverage_init(app); } diff --git a/tests/integration/harness/gkfs.io/serialize.hpp b/tests/integration/harness/gkfs.io/serialize.hpp index 5786c32eea54017fcdfcf2bfbb95819fe525fdc5..cb740b4abc452c20d0d73c508cb8bfb051bcda15 100644 --- a/tests/integration/harness/gkfs.io/serialize.hpp +++ b/tests/integration/harness/gkfs.io/serialize.hpp @@ -34,6 +34,8 @@ extern "C" { #include +#include +#include } template @@ -171,6 +173,21 @@ struct adl_serializer { } }; +// ADL specialization for struct ::statfs (not exhaustive) type +template <> +struct adl_serializer { + static void + to_json(json& j, const struct ::statfs opt) { + j = json{{"f_type", opt.f_type}, + {"f_bsize", opt.f_bsize}, + {"f_blocks", opt.f_blocks}, + {"f_bfree", opt.f_bfree}, + {"f_bavail", opt.f_bavail}, + {"f_files", opt.f_files}, + {"f_ffree", opt.f_ffree}}; + } +}; + #ifdef STATX_TYPE // ADL specialization for struct ::statx type template <> diff --git a/tests/integration/harness/gkfs.io/statfs.cpp b/tests/integration/harness/gkfs.io/statfs.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3ef6675d37688275d94504c1acd69ad89396f5a5 --- /dev/null +++ b/tests/integration/harness/gkfs.io/statfs.cpp @@ -0,0 +1,101 @@ +/* + Copyright 2018-2022, Barcelona Supercomputing Center (BSC), Spain + Copyright 2015-2022, Johannes Gutenberg Universitaet Mainz, Germany + + This software was partially supported by the + EC H2020 funded project NEXTGenIO (Project ID: 671951, www.nextgenio.eu). + + This software was partially supported by the + ADA-FS project under the SPPEXA project funded by the DFG. + + This file is part of GekkoFS. + + GekkoFS is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + GekkoFS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GekkoFS. If not, see . + + SPDX-License-Identifier: GPL-3.0-or-later +*/ + +/* C++ includes */ +#include +#include +#include +#include +#include +#include +#include + +/* C includes */ +#include +#include +#include + +using json = nlohmann::json; + +struct statfs_options { + bool verbose{}; + std::string pathname; + + REFL_DECL_STRUCT(statfs_options, REFL_DECL_MEMBER(bool, verbose), + REFL_DECL_MEMBER(std::string, pathname)); +}; + +struct statfs_output { + int retval; + int errnum; + struct ::statfs statfsbuf; + + REFL_DECL_STRUCT(statfs_output, REFL_DECL_MEMBER(int, retval), + REFL_DECL_MEMBER(int, errnum), + REFL_DECL_MEMBER(struct ::statfs, statfsbuf)); +}; + +void +to_json(json& record, const statfs_output& out) { + record = serialize(out); +} + +void +statfs_exec(const statfs_options& opts) { + + struct ::statfs statfsbuf; + + auto rv = ::statfs(opts.pathname.c_str(), &statfsbuf); + + if(opts.verbose) { + fmt::print("statfs(pathname=\"{}\") = {}, errno: {} [{}]\n", + opts.pathname, rv, errno, ::strerror(errno)); + return; + } + + json out = statfs_output{rv, errno, statfsbuf}; + fmt::print("{}\n", out.dump(2)); +} + +void +statfs_init(CLI::App& app) { + + // Create the option and subcommand objects + auto opts = std::make_shared(); + auto* cmd = app.add_subcommand("statfs", "Execute the statfs() system call"); + + // Add options to cmd, binding them to opts + cmd->add_flag("-v,--verbose", opts->verbose, + "Produce human readable output"); + + cmd->add_option("pathname", opts->pathname, "Directory name") + ->required() + ->type_name(""); + + cmd->callback([opts]() { statfs_exec(*opts); }); +} diff --git a/tests/integration/harness/gkfs.io/syscall_coverage.cpp b/tests/integration/harness/gkfs.io/syscall_coverage.cpp new file mode 100644 index 0000000000000000000000000000000000000000..257311f1461bd8b9a853c149572bdde57b04b7cd --- /dev/null +++ b/tests/integration/harness/gkfs.io/syscall_coverage.cpp @@ -0,0 +1,461 @@ +/* + Copyright 2018-2022, Barcelona Supercomputing Center (BSC), Spain + Copyright 2015-2022, Johannes Gutenberg Universitaet Mainz, Germany + + This software was partially supported by the + EC H2020 funded project NEXTGenIO (Project ID: 671951, www.nextgenio.eu). + + This software was partially supported by the + ADA-FS project under the SPPEXA project funded by the DFG. + + This file is part of GekkoFS. + + GekkoFS is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + GekkoFS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GekkoFS. If not, see . + + SPDX-License-Identifier: GPL-3.0-or-later +*/ + +/* C++ includes */ +#include +#include +#include +#include +#include +#include +#include +#include + + +/* C includes */ +#include +#include +#include +#include +#include + +// Syscall numbers +#include + +using json = nlohmann::json; + +struct syscall_coverage_options { + bool verbose{}; + std::string pathname; + + + REFL_DECL_STRUCT(syscall_coverage_options, REFL_DECL_MEMBER(bool, verbose), + REFL_DECL_MEMBER(std::string, pathname)); +}; + +struct syscall_coverage_output { + int retval; + int errnum; + std::string syscall; + + REFL_DECL_STRUCT(syscall_coverage_output, REFL_DECL_MEMBER(int, retval), + REFL_DECL_MEMBER(int, errnum), + REFL_DECL_MEMBER(std::string, syscall)); +}; + +void +to_json(json& record, const syscall_coverage_output& out) { + record = serialize(out); +} + +void +output(const std::string syscall, const int ret, + const syscall_coverage_options& opts) { + if(opts.verbose) { + fmt::print( + "syscall_coverage[{}](pathname=\"{}\") = {}, errno: {} [{}]\n", + syscall, opts.pathname, ret, errno, ::strerror(errno)); + } + + json out = syscall_coverage_output{ret, errno, syscall}; + fmt::print("{}\n", out.dump(2)); +} + +/* + * system calls + * that are being tested. + */ +void +syscall_coverage_exec(const syscall_coverage_options& opts) { + + int fd = ::open(opts.pathname.c_str(), O_RDWR); + + if(fd == -1) { + output("open", fd, opts); + return; + } + + // create external + int fdext = ::open("tmpfile", O_RDWR | O_CREAT, 0666); + + // faccessat Internal + + auto rv = ::faccessat(AT_FDCWD, opts.pathname.c_str(), F_OK, 0); + + if(rv < 0) { + output("faccessat", rv, opts); + return; + } + + // faccessat External + + rv = ::faccessat(AT_FDCWD, "/tmp", F_OK, 0); + + if(rv < 0) { + output("faccessat, external", rv, opts); + return; + } + + // lstat + struct stat st; + rv = ::lstat(opts.pathname.c_str(), &st); + if(rv < 0) { + output("lstat", rv, opts); + return; + } + + rv = ::lstat("tmpfile", &st); + if(rv < 0) { + output("lstat", rv, opts); + return; + } + + // pwrite external + rv = ::pwrite(fdext, "test", 4, 0); + if(rv < 0) { + output("pwrite", rv, opts); + return; + } + + // pread external + char bufext[4]; + rv = ::pread(fdext, bufext, 4, 0); + if(rv < 0) { + output("pread", rv, opts); + return; + } + + // lssek external + rv = ::lseek(fdext, 0, SEEK_SET); + if(rv < 0) { + output("lseek", rv, opts); + return; + } + + // ftruncate external + rv = ::ftruncate(fdext, 0); + if(rv < 0) { + output("ftruncate", rv, opts); + return; + } + + // truncate exterlan + rv = ::truncate("tmpfile", 0); + if(rv < 0) { + output("truncate", rv, opts); + return; + } + + + // dup external + int fdext2 = ::dup(fdext); + if(fdext2 < 0) { + output("dup", fdext2, opts); + return; + } + + // dup2 external + int ffdext3 = 0; + rv = ::dup2(fdext, ffdext3); + if(rv < 0) { + output("dup2", rv, opts); + return; + } + + + // fchmod internal + rv = ::fchmod(fd, 0777); + if(errno != ENOTSUP) { + output("fchmod", rv, opts); + return; + } + + // fchmod external + rv = ::fchmod(fdext, 0777); + if(rv < 0) { + output("fchmod", rv, opts); + return; + } + + // fchmodat internal + rv = ::fchmodat(AT_FDCWD, opts.pathname.c_str(), 0777, 0); + if(errno != ENOTSUP) { + output("fchmodat", rv, opts); + return; + } + // fchmodat external + rv = ::fchmodat(AT_FDCWD, "tmpfile", 0777, 0); + if(rv < 0) { + output("fchmodat, external", rv, opts); + return; + } + + // dup3 internal + rv = ::dup3(fd, 0, 0); + if(errno != ENOTSUP) { + output("dup3", rv, opts); + return; + } + + // dup3 external + int ffdext4 = 0; + rv = ::dup3(fdext, ffdext4, 0); + if(rv < 0) { + output("dup3", rv, opts); + return; + } + + + // fcntl + rv = ::fcntl(fd, F_GETFD); + if(rv < 0) { + output("fcntl, F_GETFD", rv, opts); + return; + } + + rv = ::fcntl(fd, F_GETFL); + if(rv < 0 || rv != O_RDWR) { + output("fcntl, F_GETFL", rv, opts); + return; + } + + + rv = ::fcntl(fd, F_SETFD, 0); + if(rv < 0) { + output("fcntl, F_SETFD", rv, opts); + return; + } + + rv = ::fcntl(fd, F_SETFL, 0); + if(errno != ENOTSUP) { + output("fcntl, F_SETFL", rv, opts); + return; + } + + rv = ::fcntl(fd, F_DUPFD, 0); + if(rv < 0) { + output("fcntl, F_DUPFD", rv, opts); + return; + } + + rv = ::fcntl(fd, F_DUPFD_CLOEXEC, 0); + if(rv < 0) { + output("fcntl, F_DUPFD_CLOEXEC", rv, opts); + return; + } + + // Fstatfs internal + + struct statfs stfs; + rv = ::fstatfs(fd, &stfs); + if(rv < 0) { + output("fstatfs", rv, opts); + return; + } + + // Fstatfs external + rv = ::fstatfs(fdext, &stfs); + if(rv < 0) { + output("fstatfs", rv, opts); + return; + } + + // fsync + + rv = ::fsync(fd); + if(rv < 0) { + output("fsync", rv, opts); + return; + } + + // getxattr + + char buf[1024]; + rv = ::getxattr(opts.pathname.c_str(), "user.test", buf, sizeof(buf)); + if(errno != ENOTSUP) { + output("getxattr", rv, opts); + return; + } + + // readlinkat + rv = ::readlinkat(AT_FDCWD, opts.pathname.c_str(), buf, sizeof(buf)); + if(errno != ENOTSUP) { + output("readlinkat", rv, opts); + return; + } + + // chdir internal error + rv = ::chdir(opts.pathname.c_str()); + if(errno != ENOTDIR) { + output("chdir", rv, opts); + return; + } + + // chdir internal error + std::string nonexist = opts.pathname+"x2"; + rv = ::chdir(nonexist.c_str()); + if(rv >= 0) { + output("chdir", rv, opts); + return; + } + + // fchdir + auto fddir = ::open(".", O_RDONLY); + if(fddir < 0) { + output("fchdir", fddir, opts); + return; + } + + rv = ::fchdir(fddir); + if(rv < 0) { + output("fchdir", rv, opts); + return; + } + + // ftruncate + rv = ::ftruncate(fd, 0); + if(rv < 0) { + output("ftruncate", rv, opts); + return; + } + + // fchdir internal file + rv = ::fchdir(fd); + if(errno != EBADF) { + output("fchdir", rv, opts); + return; + } + + + // fchdir directory from opts.pathname + auto fd2 = ::open( + opts.pathname.substr(0, opts.pathname.find_last_of("/")).c_str(), + O_RDONLY); + if(fd2 < 0) { + output("fchdir", fd2, opts); + return; + } + rv = ::fchdir(fd2); + if(rv < 0) { + output("fchdir", rv, opts); + return; + } + + // renameat external + auto fdtmp = ::open("/tmp/test_rename", O_CREAT | O_WRONLY); + ::close(fdtmp); + + rv = ::renameat(AT_FDCWD, "/tmp/test_rename", AT_FDCWD, + opts.pathname.c_str()); + if(errno != ENOTSUP) { + output("renameat", rv, opts); + return; + } + + rv = ::renameat(AT_FDCWD, "/tmp/test_rename", AT_FDCWD, + "/tmp/test_rename2"); + if(rv < 0) { + output("renameat", rv, opts); + return; + } + // sys_open + rv = ::syscall(SYS_open, opts.pathname.c_str(), O_RDONLY, 0); + if(rv < 0) { + output("sys_open", rv, opts); + return; + } + + // sys_creat + rv = ::syscall(SYS_creat, opts.pathname.c_str(), 0777); + if(rv < 0) { + output("sys_creat", rv, opts); + return; + } + + // sys_unlinkat + rv = ::syscall(SYS_unlinkat, AT_FDCWD, opts.pathname.c_str(), 0); + if(errno != ENOTSUP) { + output("sys_unlinkat", rv, opts); + return; + } + + // sys_mkdirat + std::string path = opts.pathname + "path"; + rv = ::syscall(SYS_mkdirat, AT_FDCWD, opts.pathname.c_str(), 0777); + if(rv < 0) { + output("sys_mkdirat", rv, opts); + return; + } + + // SYS_chmod + rv = ::syscall(SYS_chmod, opts.pathname.c_str(), 0777); + if(errno != ENOTSUP) { + output("sys_chmod", rv, opts); + return; + } + + // hook_faccessat coverage + rv = ::syscall(SYS_faccessat, AT_FDCWD, opts.pathname.c_str(), F_OK, 0); + if(rv < 0) { + output("sys_faccessat", rv, opts); + return; + } + + rv = ::syscall(SYS_faccessat, AT_FDCWD, "/tmp", F_OK, 0); + if(rv < 0) { + output("sys_faccessat", rv, opts); + return; + } + + + rv = 0; + errno = 0; + auto syscall = "ALLOK"; + json out = syscall_coverage_output{(int) rv, errno, syscall}; + fmt::print("{}\n", out.dump(2)); + return; +} + +void +syscall_coverage_init(CLI::App& app) { + + // Create the option and subcommand objects + auto opts = std::make_shared(); + auto* cmd = + app.add_subcommand("syscall_coverage", "Execute severals syscalls"); + + // Add options to cmd, binding them to opts + cmd->add_flag("-v,--verbose", opts->verbose, + "Produce human writeable output"); + + cmd->add_option("pathname", opts->pathname, "File name") + ->required() + ->type_name(""); + + cmd->callback([opts]() { syscall_coverage_exec(*opts); }); +} diff --git a/tests/integration/harness/gkfs.py b/tests/integration/harness/gkfs.py index 26db3b309df6ae62b88aaa48aeea5d3932c2a3b1..2ba4ae5939938ba9ce17c77a1415d973bac8f55c 100644 --- a/tests/integration/harness/gkfs.py +++ b/tests/integration/harness/gkfs.py @@ -254,7 +254,8 @@ class Daemon: '--dbbackend', self._database, '--output-stats', self.logdir / 'stats.log', '--enable-collection', - '--enable-chunkstats' ] + '--enable-chunkstats', + '--enable-prometheus' ] if self._database == "parallaxdb" : args.append('--clean-rootdir-finish') diff --git a/tests/integration/harness/io.py b/tests/integration/harness/io.py index 7b65adc23ea6dcc621313035244781d10078f50b..834174a41a3addeeb291122f3de6b60a6fb6786c 100644 --- a/tests/integration/harness/io.py +++ b/tests/integration/harness/io.py @@ -83,6 +83,29 @@ class StructStatSchema(Schema): 'st_atim', 'st_mtim', 'st_ctim'])(**data) +class StructStatfsSchema(Schema): + """Schema that deserializes a struct statfs""" + + f_type = fields.Integer(required=True) + f_bsize = fields.Integer(required=True) + f_blocks = fields.Integer(required=True) + f_bfree = fields.Integer(required=True) + f_bavail = fields.Integer(required=True) + f_files = fields.Integer(required=True) + f_ffree = fields.Integer(required=True) + # f_fsid = fields.Integer(required=True) + # f_namelen = fields.Integer(required=True) + # f_frsize = fields.Integer(required=True) + # f_flags = fields.Integer(required=True) + + @post_load + def make_object(self, data, **kwargs): + return namedtuple('StructStatfs', + ['f_type', 'f_bsize', 'f_blocks', 'f_bfree', + 'f_bavail', 'f_files', 'f_ffree'])(**data) + + + class StructStatxTimestampSchema(Schema): """Schema that deserializes a struct timespec""" tv_sec = fields.Integer(required=True) @@ -298,6 +321,16 @@ class StatxOutputSchema(Schema): def make_object(self, data, **kwargs): return namedtuple('StatxReturn', ['retval', 'statbuf', 'errno'])(**data) +class StatfsOutputSchema(Schema): + """Schema to deserialize the results of a statfs() execution""" + + retval = fields.Integer(required=True) + statfsbuf = fields.Nested(StructStatfsSchema, required=True) + errno = Errno(data_key='errnum', required=True) + + @post_load + def make_object(self, data, **kwargs): + return namedtuple('StatfsReturn', ['retval', 'statfsbuf', 'errno'])(**data) class LseekOutputSchema(Schema): """Schema to deserialize the results of an lseek() execution""" @@ -349,6 +382,16 @@ class TruncateOutputSchema(Schema): def make_object(self, data, **kwargs): return namedtuple('TruncateReturn', ['retval', 'errno'])(**data) +class AccessOutputSchema(Schema): + """Schema to deserialize the results of an access() execution""" + retval = fields.Integer(required=True) + errno = Errno(data_key='errnum', required=True) + + @post_load + def make_object(self, data, **kwargs): + return namedtuple('AccessReturn', ['retval', 'errno'])(**data) + + class ChdirOutputSchema(Schema): """Schema to deserialize the results of an chdir() execution""" @@ -371,7 +414,26 @@ class GetcwdvalidateOutputSchema(Schema): def make_object(self, data, **kwargs): return namedtuple('GetcwdvalidateReturn', ['retval', 'path', 'errno'])(**data) +class DupValidateOutputSchema(Schema): + """Schema to deserialize the results of an dup, dup2, dup3 execution""" + + retval = fields.Integer(required=True) + errno = Errno(data_key='errnum', required=True) + @post_load + def make_object(self, data, **kwargs): + return namedtuple('DupValidateReturn', ['retval', 'errno'])(**data) + +class SyscallCoverageOutputSchema(Schema): + """Schema to deserialize the results of a syscall coverage execution""" + + retval = fields.Integer(required=True) + errno = Errno(data_key='errnum', required=True) + syscall = fields.String(required=True) + + @post_load + def make_object(self, data, **kwargs): + return namedtuple('SyscallCoverageReturn', ['retval', 'errno', 'syscall'])(**data) class SymlinkOutputSchema(Schema): """Schema to deserialize the results of an symlink execution""" @@ -429,11 +491,15 @@ class IOParser: 'truncate': TruncateOutputSchema(), 'directory_validate' : DirectoryValidateOutputSchema(), 'unlink' : UnlinkOutputSchema(), + 'access' : AccessOutputSchema(), + 'statfs' : StatfsOutputSchema(), # UTIL 'file_compare': FileCompareOutputSchema(), 'chdir' : ChdirOutputSchema(), 'getcwd_validate' : GetcwdvalidateOutputSchema(), 'symlink' : SymlinkOutputSchema(), + 'dup_validate' : DupValidateOutputSchema(), + 'syscall_coverage' : SyscallCoverageOutputSchema(), } def parse(self, command, output): diff --git a/tests/integration/position/test_lseek.py b/tests/integration/position/test_lseek.py index dd96ca9ec3c30cef070fd5e96dfe17f33535f8fa..733b3988ce541483b7ac36c36ebd0c56919b91ef 100644 --- a/tests/integration/position/test_lseek.py +++ b/tests/integration/position/test_lseek.py @@ -94,6 +94,9 @@ def test_lseek(gkfs_daemon, gkfs_client): ret = gkfs_client.lseek(file_a, 5, os.SEEK_SET) assert ret.retval == 5 + ret = gkfs_client.lseek(file_a, 2, os.SEEK_CUR) + assert ret.retval == 2 + # Size needs to be 0 ret = gkfs_client.stat(file_a) @@ -126,7 +129,17 @@ def test_lseek(gkfs_daemon, gkfs_client): assert ret.retval == -1 #FAILS assert ret.errno == 22 + ret = gkfs_client.lseek(file_a, 0, os.SEEK_DATA) + assert ret.retval == -1 + assert ret.errno == errno.EINVAL + ret = gkfs_client.lseek(file_a, 0, os.SEEK_HOLE) + assert ret.retval == -1 + assert ret.errno == errno.EINVAL + + ret = gkfs_client.lseek(file_a, 0, 666) + assert ret.retval == -1 + assert ret.errno == errno.EINVAL diff --git a/tests/integration/syscalls/test_error_operations.py b/tests/integration/syscalls/test_error_operations.py new file mode 100644 index 0000000000000000000000000000000000000000..a5ce6bd948f308ca89696c837becdcac7dbb4b3d --- /dev/null +++ b/tests/integration/syscalls/test_error_operations.py @@ -0,0 +1,170 @@ +################################################################################ +# Copyright 2018-2022, Barcelona Supercomputing Center (BSC), Spain # +# Copyright 2015-2022, Johannes Gutenberg Universitaet Mainz, Germany # +# # +# This software was partially supported by the # +# EC H2020 funded project NEXTGenIO (Project ID: 671951, www.nextgenio.eu). # +# # +# This software was partially supported by the # +# ADA-FS project under the SPPEXA project funded by the DFG. # +# # +# This file is part of GekkoFS. # +# # +# GekkoFS is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# GekkoFS is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with GekkoFS. If not, see . # +# # +# SPDX-License-Identifier: GPL-3.0-or-later # +################################################################################ + +from sre_parse import State +import harness +from pathlib import Path +import errno +import stat +import os +import ctypes +import sh +import sys +import pytest +from harness.logger import logger + +nonexisting = "nonexisting" + + +def test_open_error(gkfs_daemon, gkfs_client): + + file = gkfs_daemon.mountdir / "file" + file2 = gkfs_daemon.mountdir / "file2" + file3 = gkfs_daemon.mountdir / "file3" + + flags = [os.O_PATH, os.O_APPEND, os.O_CREAT | os.O_DIRECTORY] + # create a file in gekkofs + + for flag in flags: + ret = gkfs_client.open(file, + flag, + stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) + + assert ret.retval == -1 + assert ret.errno == errno.ENOTSUP + + + # Create file and recreate + ret = gkfs_client.open(file, os.O_CREAT | os.O_EXCL | os.O_WRONLY) + assert ret.retval == 10000 + + ret = gkfs_client.open(file, os.O_CREAT | os.O_EXCL | os.O_WRONLY) + assert ret.retval == -1 + assert ret.errno == errno.EEXIST + + + # Create file and recreate + ret = gkfs_client.open(file2, os.O_CREAT | os.O_WRONLY) + assert ret.retval == 10000 + # Undefined in man + ret = gkfs_client.open(file2, os.O_CREAT | os.O_WRONLY) + assert ret.retval == 10000 + + # RDWR + ret = gkfs_client.open(file2, os.O_RDWR) + assert ret.retval == 10000 + + # RD + ret = gkfs_client.open(file2, os.O_RDONLY) + assert ret.retval == 10000 + + # Truncate the file + ret = gkfs_client.open(file2, os.O_TRUNC | os.O_WRONLY) + assert ret.retval == 10000 + + # Open unexistent file + ret = gkfs_client.open(file3, os.O_WRONLY) + assert ret.retval == -1 + assert ret.errno == errno.ENOENT + + ret = gkfs_client.open(file3, os.O_CREAT | stat.S_IFSOCK | os.O_EXCL | os.O_WRONLY) + assert ret.retval == 10000 + +def test_access_error(gkfs_daemon, gkfs_client): + + file = gkfs_daemon.mountdir / "file" + file2 = gkfs_daemon.mountdir / "file2" + + ret = gkfs_client.open(file, os.O_CREAT | os.O_WRONLY) + assert ret.retval == 10000 + + # Access, flags are not being used + ret = gkfs_client.access(file2, os.R_OK) + assert ret.retval == -1 + assert ret.errno == errno.ENOENT + + ret = gkfs_client.access(file, os.R_OK) + assert ret.retval != -1 + +def test_stat_error(gkfs_daemon, gkfs_client): + # Stat non existing file + file = gkfs_daemon.mountdir / "file" + + ret = gkfs_client.stat(file) + assert ret.retval == -1 + assert ret.errno == errno.ENOENT + + # test statx on existing file + ret = gkfs_client.statx(0, file, 0, 0) + assert ret.retval == -1 + assert ret.errno == errno.ENOENT + +def test_statfs(gkfs_daemon, gkfs_client): + # Statfs check most of the outputs + + ret = gkfs_client.statfs(gkfs_daemon.mountdir) + assert ret.retval == 0 + assert ret.statfsbuf.f_type == 0 + assert ret.statfsbuf.f_bsize != 0 + assert ret.statfsbuf.f_blocks != 0 + assert ret.statfsbuf.f_bfree != 0 + assert ret.statfsbuf.f_bavail != 0 + assert ret.statfsbuf.f_files == 0 + assert ret.statfsbuf.f_ffree == 0 + + +def test_check_parents(gkfs_daemon, gkfs_client): + file = gkfs_daemon.mountdir / "dir" / "file" + file2 = gkfs_daemon.mountdir / "file2" + file3 = gkfs_daemon.mountdir / "file2" / "file3" + + # Parent directory does not exist + ret = gkfs_client.open(file, os.O_CREAT | os.O_WRONLY) + assert ret.retval == -1 + assert ret.errno == errno.ENOENT + + # Create file + ret = gkfs_client.open(file2, os.O_CREAT | os.O_WRONLY) + assert ret.retval == 10000 + + # Create file over a file + ret = gkfs_client.open(file3, os.O_CREAT | os.O_WRONLY) + assert ret.retval == -1 + assert ret.errno == errno.ENOTDIR + + +def test_dup(gkfs_daemon, gkfs_client): + file = gkfs_daemon.mountdir / "file" + + ret = gkfs_client.open(file, os.O_CREAT | os.O_WRONLY) + assert ret.retval == 10000 + + ret = gkfs_client.dup_validate(file) + assert ret.retval == 0 + assert ret.errno == 0 + diff --git a/tests/integration/syscalls/test_syscalls.py b/tests/integration/syscalls/test_syscalls.py new file mode 100644 index 0000000000000000000000000000000000000000..5395938388251ed67e3eb67dfdbf74dcd1e45cba --- /dev/null +++ b/tests/integration/syscalls/test_syscalls.py @@ -0,0 +1,58 @@ +################################################################################ +# Copyright 2018-2022, Barcelona Supercomputing Center (BSC), Spain # +# Copyright 2015-2022, Johannes Gutenberg Universitaet Mainz, Germany # +# # +# This software was partially supported by the # +# EC H2020 funded project NEXTGenIO (Project ID: 671951, www.nextgenio.eu). # +# # +# This software was partially supported by the # +# ADA-FS project under the SPPEXA project funded by the DFG. # +# # +# This file is part of GekkoFS. # +# # +# GekkoFS is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# GekkoFS is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with GekkoFS. If not, see . # +# # +# SPDX-License-Identifier: GPL-3.0-or-later # +################################################################################ + +from sre_parse import State +import harness +from pathlib import Path +import errno +import stat +import os +import ctypes +import sys +import pytest +from harness.logger import logger +import ctypes +nonexisting = "nonexisting" + + +def test_syscalls(gkfs_daemon, gkfs_client): + + file = gkfs_daemon.mountdir / "file" + + ret = gkfs_client.open(file, os.O_CREAT | os.O_WRONLY) + assert ret.retval == 10000 + + + ret = gkfs_client.syscall_coverage(file) + assert ret.syscall == "ALLOK" + assert ret.retval == 0 + assert ret.errno == 0 + + + + diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 5d00c8b2701b15f2a67260bc9f0e0bb514048ccf..1bcff84ef8fc1f07a704a603b1dc7681a2a96832 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -62,8 +62,6 @@ target_link_libraries(catch2_main add_executable(tests) target_sources(tests PRIVATE - ${CMAKE_CURRENT_LIST_DIR}/test_example_00.cpp - ${CMAKE_CURRENT_LIST_DIR}/test_example_01.cpp ${CMAKE_CURRENT_LIST_DIR}/test_utils_arithmetic.cpp ${CMAKE_CURRENT_LIST_DIR}/test_helpers.cpp)