From c769c02c55e63a82e3a8a2fcec10c95308c6901f Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Sat, 4 Apr 2020 20:23:50 +0200 Subject: [PATCH] added unit tests for readdir operation (adapter) --- cmake/unit_tests.cmake | 1 + lib/webfuse/adapter/impl/filesystem.c | 1 + lib/webfuse/adapter/impl/operation/readdir.c | 53 ++-- lib/webfuse/adapter/impl/operation/readdir.h | 35 +++ .../tests/adapter/operation/test_readdir.cc | 266 ++++++++++++++++++ 5 files changed, 331 insertions(+), 25 deletions(-) create mode 100644 lib/webfuse/adapter/impl/operation/readdir.h create mode 100644 test/webfuse/tests/adapter/operation/test_readdir.cc diff --git a/cmake/unit_tests.cmake b/cmake/unit_tests.cmake index 2b2d445..1444ee5 100644 --- a/cmake/unit_tests.cmake +++ b/cmake/unit_tests.cmake @@ -51,6 +51,7 @@ add_executable(alltests test/webfuse/tests/adapter/operation/test_open.cc 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/provider/test_url.cc test/webfuse/tests/provider/test_client_protocol.cc test/webfuse/tests/provider/operation/test_close.cc diff --git a/lib/webfuse/adapter/impl/filesystem.c b/lib/webfuse/adapter/impl/filesystem.c index 66ee76c..2573753 100644 --- a/lib/webfuse/adapter/impl/filesystem.c +++ b/lib/webfuse/adapter/impl/filesystem.c @@ -3,6 +3,7 @@ #include "webfuse/adapter/impl/operation/open.h" #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/session.h" #include "webfuse/adapter/impl/mountpoint.h" diff --git a/lib/webfuse/adapter/impl/operation/readdir.c b/lib/webfuse/adapter/impl/operation/readdir.c index 804a80e..d82fc62 100644 --- a/lib/webfuse/adapter/impl/operation/readdir.c +++ b/lib/webfuse/adapter/impl/operation/readdir.c @@ -1,3 +1,4 @@ +#include "webfuse/adapter/impl/operation/readdir.h" #include "webfuse/adapter/impl/operations.h" #include @@ -15,13 +16,6 @@ #define WF_DIRBUFFER_INITIAL_SIZE 1024 -struct wf_impl_operation_readdir_context -{ - fuse_req_t request; - size_t size; - off_t offset; -}; - struct wf_impl_dirbuffer { char * data; @@ -72,7 +66,7 @@ static size_t wf_impl_min(size_t a, size_t b) return (a < b) ? a : b; } -static void wf_impl_operation_readdir_finished( +void wf_impl_operation_readdir_finished( void * user_data, json_t const * result, json_t const * error) @@ -83,31 +77,40 @@ static void wf_impl_operation_readdir_finished( struct wf_impl_dirbuffer buffer; wf_impl_dirbuffer_init(&buffer); - if (NULL != result) + if (json_is_array(result)) { - if (json_is_array(result)) + size_t const count = json_array_size(result); + for(size_t i = 0; i < count; i++) { - bool buffer_full = false; - size_t const count = json_array_size(result); - for(size_t i = 0; (!buffer_full) && (i < count); i++) + json_t * entry =json_array_get(result, i); + if (json_is_object(entry)) { - json_t * entry =json_array_get(result, i); - if (json_is_object(entry)) + json_t * name_holder = json_object_get(entry, "name"); + json_t * inode_holder = json_object_get(entry, "inode"); + + if ((json_is_string(name_holder)) && (json_is_integer(inode_holder))) + { + char const * name = json_string_value(name_holder); + fuse_ino_t entry_inode = (fuse_ino_t) json_integer_value(inode_holder); + wf_impl_dirbuffer_add(context->request, &buffer, name, entry_inode); + } + else { - json_t * name_holder = json_object_get(entry, "name"); - json_t * inode_holder = json_object_get(entry, "inode"); - - if ((NULL != name_holder) && (json_is_string(name_holder)) && - (NULL != inode_holder) && (json_is_integer(inode_holder))) - { - char const * name = json_string_value(name_holder); - fuse_ino_t entry_inode = (fuse_ino_t) json_integer_value(inode_holder); - wf_impl_dirbuffer_add(context->request, &buffer, name, entry_inode); - } + status = WF_BAD_FORMAT; + break; } } + else + { + status = WF_BAD_FORMAT; + break; + } } } + else if (WF_GOOD == status) + { + status = WF_BAD_FORMAT; + } if (WF_GOOD == status) { diff --git a/lib/webfuse/adapter/impl/operation/readdir.h b/lib/webfuse/adapter/impl/operation/readdir.h new file mode 100644 index 0000000..40eb09e --- /dev/null +++ b/lib/webfuse/adapter/impl/operation/readdir.h @@ -0,0 +1,35 @@ +#ifndef WF_ADAPTER_IMPL_OPERATION_READDIR_H +#define WF_ADAPTER_IMPL_OPERATION_READDIR_H + +#include "webfuse/adapter/impl/fuse_wrapper.h" +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wf_impl_operation_readdir_context +{ + fuse_req_t request; + size_t size; + off_t offset; +}; + +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 void wf_impl_operation_readdir_finished( + void * user_data, + json_t const * result, + json_t const * error); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/test/webfuse/tests/adapter/operation/test_readdir.cc b/test/webfuse/tests/adapter/operation/test_readdir.cc new file mode 100644 index 0000000..3ae9df2 --- /dev/null +++ b/test/webfuse/tests/adapter/operation/test_readdir.cc @@ -0,0 +1,266 @@ +#include "webfuse/adapter/impl/operation/readdir.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_readdir, invoke_proxy) +{ + MockJsonRpcProxy proxy; + EXPECT_CALL(proxy, wf_jsonrpc_proxy_vinvoke(_,_,_,StrEq("readdir"),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; + FuseMock fuse; + EXPECT_CALL(fuse, fuse_req_userdata(_)).Times(1).WillOnce(Return(&op_context)); + + + fuse_req_t request = nullptr; + fuse_ino_t inode = 1; + size_t size = 10; + size_t offset = 0; + fuse_file_info file_info; + file_info.flags = 0; + wf_impl_operation_readdir(request, inode, size, offset, &file_info); +} + +TEST(wf_impl_operation_readdir, 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_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; + size_t size = 10; + size_t offset = 0; + fuse_file_info file_info; + file_info.flags = 0; + wf_impl_operation_readdir(request, inode, size, offset, &file_info); +} + +TEST(wf_impl_operation_readdir, finished) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_buf(_,_,_)).Times(1).WillOnce(Return(0)); + + json_t * result = json_array(); + json_t * item = json_object(); + json_object_set_new(item, "name", json_string("a.file")); + json_object_set_new(item, "inode", json_integer(42)); + json_array_append_new(result, item); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_readdir_context))); + context->request = nullptr; + context->size = 1; + context->offset = 0; + wf_impl_operation_readdir_finished(reinterpret_cast(context), result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_readdir, finished_many_items) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_buf(_,_,_)).Times(1).WillOnce(Return(0)); + + json_t * result = json_array(); + for(int i = 0; i < 100; i++) + { + json_t * item = json_object(); + json_object_set_new(item, "name", json_sprintf("file_%d.txt", i)); + json_object_set_new(item, "inode", json_integer(1 + i)); + json_array_append_new(result, item); + } + + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_readdir_context))); + context->request = nullptr; + context->size = 100; + context->offset = 0; + wf_impl_operation_readdir_finished(reinterpret_cast(context), result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_readdir, finished_read_after_end) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_buf(_,_,_)).Times(1).WillOnce(Return(0)); + + json_t * result = json_array(); + json_t * item = json_object(); + json_object_set_new(item, "name", json_string("a.file")); + json_object_set_new(item, "inode", json_integer(42)); + json_array_append_new(result, item); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_readdir_context))); + context->request = nullptr; + context->size = 10; + context->offset = 2; + wf_impl_operation_readdir_finished(reinterpret_cast(context), result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_readdir, finished_fail_error) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_buf(_,_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, ENOENT)).Times(1).WillOnce(Return(0)); + + json_t * error = json_array(); + json_object_set_new(error, "code", json_integer(WF_BAD)); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_readdir_context))); + context->request = nullptr; + context->size = 1; + context->offset = 0; + wf_impl_operation_readdir_finished(reinterpret_cast(context), nullptr, error); + json_decref(error); +} + +TEST(wf_impl_operation_readdir, finished_fail_invalid_result_type) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_buf(_,_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, ENOENT)).Times(1).WillOnce(Return(0)); + + json_t * result = json_object(); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_readdir_context))); + context->request = nullptr; + context->size = 1; + context->offset = 0; + wf_impl_operation_readdir_finished(reinterpret_cast(context), result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_readdir, finished_fail_missing_name) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_buf(_,_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, ENOENT)).Times(1).WillOnce(Return(0)); + + json_t * result = json_array(); + json_t * item = json_object(); + json_object_set_new(item, "inode", json_integer(42)); + json_array_append_new(result, item); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_readdir_context))); + context->request = nullptr; + context->size = 1; + context->offset = 0; + wf_impl_operation_readdir_finished(reinterpret_cast(context), result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_readdir, finished_fail_invalid_name_type) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_buf(_,_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, ENOENT)).Times(1).WillOnce(Return(0)); + + json_t * result = json_array(); + json_t * item = json_object(); + json_object_set_new(item, "name", json_integer(42)); + json_object_set_new(item, "inode", json_integer(42)); + json_array_append_new(result, item); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_readdir_context))); + context->request = nullptr; + context->size = 1; + context->offset = 0; + wf_impl_operation_readdir_finished(reinterpret_cast(context), result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_readdir, finished_fail_missing_inode) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_buf(_,_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, ENOENT)).Times(1).WillOnce(Return(0)); + + json_t * result = json_array(); + json_t * item = json_object(); + json_object_set_new(item, "name", json_string("a.file")); + json_array_append_new(result, item); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_readdir_context))); + context->request = nullptr; + context->size = 1; + context->offset = 0; + wf_impl_operation_readdir_finished(reinterpret_cast(context), result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_readdir, finished_fail_invalid_inode_type) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_buf(_,_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, ENOENT)).Times(1).WillOnce(Return(0)); + + json_t * result = json_array(); + json_t * item = json_object(); + json_object_set_new(item, "name", json_string("a.file")); + json_object_set_new(item, "inode", json_string("42")); + json_array_append_new(result, item); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_readdir_context))); + context->request = nullptr; + context->size = 1; + context->offset = 0; + wf_impl_operation_readdir_finished(reinterpret_cast(context), result, nullptr); + json_decref(result); +} + +TEST(wf_impl_operation_readdir, finished_fail_invalid_item_type) +{ + FuseMock fuse; + EXPECT_CALL(fuse, fuse_reply_buf(_,_,_)).Times(0); + EXPECT_CALL(fuse, fuse_reply_err(_, ENOENT)).Times(1).WillOnce(Return(0)); + + json_t * result = json_array(); + json_array_append_new(result, json_string("item")); + + auto * context = reinterpret_cast(malloc(sizeof(wf_impl_operation_readdir_context))); + context->request = nullptr; + context->size = 1; + context->offset = 0; + wf_impl_operation_readdir_finished(reinterpret_cast(context), result, nullptr); + json_decref(result); +}