diff --git a/cmake/unit_tests.cmake b/cmake/unit_tests.cmake index 1444ee5..0e3f8dd 100644 --- a/cmake/unit_tests.cmake +++ b/cmake/unit_tests.cmake @@ -52,6 +52,7 @@ add_executable(alltests test/webfuse/tests/adapter/operation/test_close.cc test/webfuse/tests/adapter/operation/test_read.cc test/webfuse/tests/adapter/operation/test_readdir.cc + test/webfuse/tests/adapter/operation/test_getattr.cc test/webfuse/tests/provider/test_url.cc test/webfuse/tests/provider/test_client_protocol.cc test/webfuse/tests/provider/operation/test_close.cc @@ -81,6 +82,8 @@ target_link_libraries(alltests PUBLIC -Wl,--wrap=fuse_reply_open -Wl,--wrap=fuse_reply_err -Wl,--wrap=fuse_reply_buf + -Wl,--wrap=fuse_reply_attr + -Wl,--wrap=fuse_req_ctx webfuse-adapter-static webfuse-provider-static diff --git a/lib/webfuse/adapter/impl/filesystem.c b/lib/webfuse/adapter/impl/filesystem.c index 2573753..d614cd4 100644 --- a/lib/webfuse/adapter/impl/filesystem.c +++ b/lib/webfuse/adapter/impl/filesystem.c @@ -4,6 +4,7 @@ #include "webfuse/adapter/impl/operation/close.h" #include "webfuse/adapter/impl/operation/read.h" #include "webfuse/adapter/impl/operation/readdir.h" +#include "webfuse/adapter/impl/operation/getattr.h" #include "webfuse/adapter/impl/session.h" #include "webfuse/adapter/impl/mountpoint.h" diff --git a/lib/webfuse/adapter/impl/operation/getattr.c b/lib/webfuse/adapter/impl/operation/getattr.c index ae64a25..6185044 100644 --- a/lib/webfuse/adapter/impl/operation/getattr.c +++ b/lib/webfuse/adapter/impl/operation/getattr.c @@ -1,3 +1,4 @@ +#include "webfuse/adapter/impl/operation/getattr.h" #include "webfuse/adapter/impl/operations.h" #include @@ -11,16 +12,7 @@ #include "webfuse/core/json_util.h" #include "webfuse/core/util.h" -struct wf_impl_operation_getattr_context -{ - fuse_req_t request; - fuse_ino_t inode; - double timeout; - uid_t uid; - gid_t gid; -}; - -static void wf_impl_operation_getattr_finished( +void wf_impl_operation_getattr_finished( void * user_data, json_t const * result, json_t const * error) @@ -33,8 +25,7 @@ static void wf_impl_operation_getattr_finished( { json_t * mode_holder = json_object_get(result, "mode"); json_t * type_holder = json_object_get(result, "type"); - if ((NULL != mode_holder) && (json_is_integer(mode_holder)) && - (NULL != type_holder) && (json_is_string(type_holder))) + if ((json_is_integer(mode_holder)) && (json_is_string(type_holder))) { memset(&buffer, 0, sizeof(struct stat)); diff --git a/lib/webfuse/adapter/impl/operation/getattr.h b/lib/webfuse/adapter/impl/operation/getattr.h new file mode 100644 index 0000000..3908922 --- /dev/null +++ b/lib/webfuse/adapter/impl/operation/getattr.h @@ -0,0 +1,37 @@ +#ifndef WF_ADAPTER_IMPL_OPERATION_GETATTR_H +#define WF_ADAPTER_IMPL_OPERATION_GETATTR_H + +#include "webfuse/adapter/impl/fuse_wrapper.h" + +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wf_impl_operation_getattr_context +{ + fuse_req_t request; + fuse_ino_t inode; + double timeout; + uid_t uid; + gid_t gid; +}; + +extern void wf_impl_operation_getattr_finished( + void * user_data, + json_t const * result, + json_t const * error); + +extern void wf_impl_operation_getattr ( + fuse_req_t request, + fuse_ino_t inode, + struct fuse_file_info *file_info); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/webfuse/adapter/impl/operations.h b/lib/webfuse/adapter/impl/operations.h index 6217de7..e52d09e 100644 --- a/lib/webfuse/adapter/impl/operations.h +++ b/lib/webfuse/adapter/impl/operations.h @@ -22,18 +22,6 @@ extern void wf_impl_operation_lookup ( fuse_ino_t parent, char const * name); -extern void wf_impl_operation_getattr ( - fuse_req_t request, - fuse_ino_t inode, - struct fuse_file_info *file_info); - -extern void wf_impl_operation_readdir ( - fuse_req_t request, - fuse_ino_t inode, - size_t size, - off_t offset, - struct fuse_file_info *file_info); - extern struct wf_jsonrpc_proxy * wf_impl_operations_context_get_proxy( struct wf_impl_operations_context * context); diff --git a/test/webfuse/mocks/mock_fuse.cc b/test/webfuse/mocks/mock_fuse.cc index 1cf6aa1..96eb6fa 100644 --- a/test/webfuse/mocks/mock_fuse.cc +++ b/test/webfuse/mocks/mock_fuse.cc @@ -9,6 +9,8 @@ WF_WRAP_FUNC1(webfuse_test_FuseMock, void*, fuse_req_userdata, fuse_req_t); WF_WRAP_FUNC2(webfuse_test_FuseMock, int, fuse_reply_open, fuse_req_t, const struct fuse_file_info *); WF_WRAP_FUNC2(webfuse_test_FuseMock, int, fuse_reply_err, fuse_req_t, int); WF_WRAP_FUNC3(webfuse_test_FuseMock, int, fuse_reply_buf, fuse_req_t, const char *, size_t); +WF_WRAP_FUNC3(webfuse_test_FuseMock, int, fuse_reply_attr, fuse_req_t, const struct stat *, double); +WF_WRAP_FUNC1(webfuse_test_FuseMock, const struct fuse_ctx *, fuse_req_ctx, fuse_req_t); } diff --git a/test/webfuse/mocks/mock_fuse.hpp b/test/webfuse/mocks/mock_fuse.hpp index 1b81e71..02f6e75 100644 --- a/test/webfuse/mocks/mock_fuse.hpp +++ b/test/webfuse/mocks/mock_fuse.hpp @@ -17,6 +17,8 @@ public: MOCK_METHOD2(fuse_reply_open, int (fuse_req_t req, const struct fuse_file_info *fi)); MOCK_METHOD2(fuse_reply_err, int (fuse_req_t req, int err)); MOCK_METHOD3(fuse_reply_buf, int (fuse_req_t req, const char *buf, size_t size)); + MOCK_METHOD3(fuse_reply_attr, int (fuse_req_t req, const struct stat *attr, double attr_timeout)); + MOCK_METHOD1(fuse_req_ctx, const struct fuse_ctx *(fuse_req_t req)); }; } diff --git a/test/webfuse/tests/adapter/operation/test_getattr.cc b/test/webfuse/tests/adapter/operation/test_getattr.cc new file mode 100644 index 0000000..cced583 --- /dev/null +++ b/test/webfuse/tests/adapter/operation/test_getattr.cc @@ -0,0 +1,214 @@ +#include "webfuse/adapter/impl/operation/getattr.h" +#include "webfuse/adapter/impl/operations.h" + +#include "webfuse/core/status.h" + +#include "webfuse/mocks/mock_fuse.hpp" +#include "webfuse/mocks/mock_operations_context.hpp" +#include "webfuse/mocks/mock_jsonrpc_proxy.hpp" + +#include + +using webfuse_test::MockJsonRpcProxy; +using webfuse_test::MockOperationsContext; +using webfuse_test::FuseMock; +using testing::_; +using testing::Return; +using testing::Invoke; +using testing::StrEq; + +namespace +{ + +void free_context( + struct wf_jsonrpc_proxy * , + wf_jsonrpc_proxy_finished_fn * , + void * user_data, + char const * , + char const *) +{ + free(user_data); +} + +} + +TEST(wf_impl_operation_getattr, invoke_proxy) +{ + MockJsonRpcProxy proxy; + EXPECT_CALL(proxy, wf_jsonrpc_proxy_vinvoke(_,_,_,StrEq("getattr"),StrEq("si"))).Times(1) + .WillOnce(Invoke(free_context)); + + MockOperationsContext context; + EXPECT_CALL(context, wf_impl_operations_context_get_proxy(_)).Times(1) + .WillOnce(Return(reinterpret_cast(&proxy))); + + wf_impl_operations_context op_context; + op_context.name = nullptr; + fuse_ctx fuse_context; + fuse_context.gid = 0; + fuse_context.uid = 0; + FuseMock fuse; + EXPECT_CALL(fuse, fuse_req_ctx(_)).Times(1).WillOnce(Return(&fuse_context)); + EXPECT_CALL(fuse, fuse_req_userdata(_)).Times(1).WillOnce(Return(&op_context)); + + + fuse_req_t request = nullptr; + fuse_ino_t inode = 1; + fuse_file_info * file_info = nullptr; + wf_impl_operation_getattr(request, inode, file_info); +} + +TEST(wf_impl_operation_getattr, fail_rpc_null) +{ + MockOperationsContext context; + EXPECT_CALL(context, wf_impl_operations_context_get_proxy(_)).Times(1) + .WillOnce(Return(nullptr)); + + FuseMock fuse; + EXPECT_CALL(fuse, fuse_req_ctx(_)).Times(1).WillOnce(Return(nullptr)); + EXPECT_CALL(fuse, fuse_req_userdata(_)).Times(1).WillOnce(Return(nullptr)); + EXPECT_CALL(fuse, fuse_reply_err(_, ENOENT)).Times(1).WillOnce(Return(0)); + + fuse_req_t request = nullptr; + fuse_ino_t inode = 1; + fuse_file_info * file_info = nullptr; + wf_impl_operation_getattr(request, inode, file_info); +} + +TEST(wf_impl_operation_getattr, finished_file) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_attr(_,_,_)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + json_object_set_new(result, "mode", json_integer(0755)); + json_object_set_new(result, "type", json_string("file")); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_getattr_context))); + context->inode = 1; + context->gid = 0; + context->uid = 0; + wf_impl_operation_getattr_finished(context, result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_getattr, finished_dir) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_attr(_,_,_)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + json_object_set_new(result, "mode", json_integer(0755)); + json_object_set_new(result, "type", json_string("dir")); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_getattr_context))); + context->inode = 1; + context->gid = 0; + context->uid = 0; + wf_impl_operation_getattr_finished(context, result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_getattr, finished_unknown_type) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_attr(_,_,_)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + json_object_set_new(result, "mode", json_integer(0755)); + json_object_set_new(result, "type", json_string("unknown")); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_getattr_context))); + context->inode = 1; + context->gid = 0; + context->uid = 0; + wf_impl_operation_getattr_finished(context, result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_getattr, finished_fail_missing_mode) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_open(_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, ENOENT)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + json_object_set_new(result, "type", json_string("file")); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_getattr_context))); + context->inode = 1; + context->gid = 0; + context->uid = 0; + wf_impl_operation_getattr_finished(context, result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_getattr, finished_fail_invalid_mode_type) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_open(_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, ENOENT)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + json_object_set_new(result, "mode", json_string("0755")); + json_object_set_new(result, "type", json_string("file")); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_getattr_context))); + context->inode = 1; + context->gid = 0; + context->uid = 0; + wf_impl_operation_getattr_finished(context, result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_getattr, finished_fail_type_mode) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_open(_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, ENOENT)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + json_object_set_new(result, "mode", json_integer(0755)); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_getattr_context))); + context->inode = 1; + context->gid = 0; + context->uid = 0; + wf_impl_operation_getattr_finished(context, result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_getattr, finished_fail_invalid_type_type) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_open(_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, ENOENT)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + json_object_set_new(result, "mode", json_integer(0755)); + json_object_set_new(result, "type", json_integer(42)); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_getattr_context))); + context->inode = 1; + context->gid = 0; + context->uid = 0; + wf_impl_operation_getattr_finished(context, result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_getattr, finished_error) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_open(_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, ENOENT)).Times(1).WillOnce(Return(0)); + + json_t * error = json_object(); + json_object_set_new(error, "code", json_integer(WF_BAD)); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_getattr_context))); + context->inode = 1; + context->gid = 0; + context->uid = 0; + wf_impl_operation_getattr_finished(context, nullptr, error); + json_decref(error); +}