* Implemented new future features: 'lazy' * Implemented new module 'fs' with directory operationsmaster
| @@ -6,6 +6,7 @@ | |||
| #include "future.pre.h" | |||
| #include "future/map.h" | |||
| #include "future/lazy.h" | |||
| #include "future/and_then.h" | |||
| namespace asyncpp | |||
| @@ -3,6 +3,7 @@ | |||
| #include "future.h" | |||
| #include "future/map.inl" | |||
| #include "future/lazy.inl" | |||
| #include "future/and_then.inl" | |||
| #ifdef asyncpp_timing | |||
| @@ -27,10 +27,10 @@ namespace __future { | |||
| using base_future_type = base_future<value_type, this_type>; | |||
| using result_type = typename base_future_type::result_type; | |||
| public: | |||
| lambda_type lambda; | |||
| first_future_type first; | |||
| second_future_type_ptr second; | |||
| private: | |||
| lambda_type _lambda; | |||
| first_future_type _first; | |||
| second_future_type_ptr _second; | |||
| public: | |||
| /** | |||
| @@ -17,8 +17,8 @@ namespace __future { | |||
| ::and_then_impl( | |||
| X_future&& p_first, | |||
| X_lambda&& p_lambda) | |||
| : first (std::forward<X_future>(p_first)) | |||
| , lambda(std::forward<X_lambda>(p_lambda)) | |||
| : _first (std::forward<X_future>(p_first)) | |||
| , _lambda(std::forward<X_lambda>(p_lambda)) | |||
| { } | |||
| template< | |||
| @@ -30,17 +30,17 @@ namespace __future { | |||
| { | |||
| while (true) | |||
| { | |||
| if (second) | |||
| if (_second) | |||
| { | |||
| return second->poll(); | |||
| return _second->poll(); | |||
| } | |||
| else | |||
| { | |||
| auto r = first.poll(); | |||
| auto r = _first.poll(); | |||
| if (!r) | |||
| return result_type::not_ready(); | |||
| second = std::make_unique<second_future_type>(r.call(lambda)); | |||
| _second = std::make_unique<second_future_type>(r.call(_lambda)); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,64 @@ | |||
| #pragma once | |||
| #include "../future.pre.h" | |||
| namespace asyncpp | |||
| { | |||
| namespace __future | |||
| { | |||
| template<typename T_lambda> | |||
| struct lazy_impl final : | |||
| public base_future< | |||
| decltype(std::declval<T_lambda>()()), | |||
| lazy_impl<T_lambda> | |||
| > | |||
| { | |||
| public: | |||
| using lambda_type = T_lambda; | |||
| using value_type = decltype(std::declval<lambda_type>()()); | |||
| using this_type = lazy_impl<lambda_type>; | |||
| using base_future_type = base_future<value_type, this_type>; | |||
| using result_type = typename base_future_type::result_type; | |||
| private: | |||
| lambda_type _lambda; | |||
| public: | |||
| /** | |||
| * @brief Constructor. | |||
| */ | |||
| template<typename X_lambda> | |||
| inline lazy_impl( | |||
| X_lambda&& p_lambda); | |||
| public: /* future */ | |||
| /** | |||
| * @brief Poll the result from the future. | |||
| */ | |||
| template<typename X = value_type> | |||
| inline auto poll() | |||
| -> std::enable_if_t< | |||
| std::is_void_v<X>, | |||
| result_type>; | |||
| /** | |||
| * @brief Poll the result from the future. | |||
| */ | |||
| template<typename X = value_type> | |||
| inline auto poll() | |||
| -> std::enable_if_t< | |||
| !std::is_void_v<X>, | |||
| result_type>; | |||
| }; | |||
| } | |||
| /** | |||
| * @brief Create a lazy evaluated future. | |||
| */ | |||
| template<typename X_lambda> | |||
| inline auto lazy(X_lambda&& p_lambda); | |||
| } | |||
| @@ -0,0 +1,53 @@ | |||
| #pragma once | |||
| #include "lazy.h" | |||
| namespace asyncpp | |||
| { | |||
| namespace __future | |||
| { | |||
| /* lazy_impl */ | |||
| template<typename T_lambda> | |||
| template<typename X_lambda> | |||
| lazy_impl<T_lambda>::lazy_impl( | |||
| X_lambda&& p_lambda) | |||
| : _lambda(std::forward<X_lambda>(p_lambda)) | |||
| { } | |||
| template<typename T_lambda> | |||
| template<typename X> | |||
| inline auto lazy_impl<T_lambda> | |||
| ::poll() | |||
| -> std::enable_if_t< | |||
| std::is_void_v<X>, | |||
| result_type> | |||
| { | |||
| _lambda(); | |||
| return result_type::ready(); | |||
| } | |||
| template<typename T_lambda> | |||
| template<typename X> | |||
| inline auto lazy_impl<T_lambda> | |||
| ::poll() | |||
| -> std::enable_if_t< | |||
| !std::is_void_v<X>, | |||
| result_type> | |||
| { | |||
| return result_type::ready(_lambda()); | |||
| } | |||
| } | |||
| template<typename X_lambda> | |||
| inline auto lazy(X_lambda&& p_lambda) | |||
| { | |||
| using lambda_type = X_lambda; | |||
| using lazy_type = __future::lazy_impl<lambda_type>; | |||
| return lazy_type(std::forward<X_lambda>(p_lambda)); | |||
| } | |||
| } | |||
| @@ -23,9 +23,9 @@ namespace __future { | |||
| using base_future_type = base_future<value_type, this_type>; | |||
| using result_type = typename base_future_type::result_type; | |||
| public: | |||
| future_type future; | |||
| lambda_type lambda; | |||
| private: | |||
| future_type _future; | |||
| lambda_type _lambda; | |||
| public: | |||
| /** | |||
| @@ -17,8 +17,8 @@ namespace __future { | |||
| ::map_impl( | |||
| X_future&& p_future, | |||
| X_lambda&& p_lambda) | |||
| : future(std::forward<X_future>(p_future)) | |||
| , lambda(std::forward<X_lambda>(p_lambda)) | |||
| : _future(std::forward<X_future>(p_future)) | |||
| , _lambda(std::forward<X_lambda>(p_lambda)) | |||
| { } | |||
| template< | |||
| @@ -28,9 +28,9 @@ namespace __future { | |||
| map_impl<T_future, T_lambda> | |||
| ::poll() | |||
| { | |||
| auto r = future.poll(); | |||
| auto r = _future.poll(); | |||
| return r | |||
| ? result_type::ready(r.call(lambda)) | |||
| ? result_type::ready(r.call(_lambda)) | |||
| : result_type::not_ready(); | |||
| } | |||
| @@ -5,8 +5,6 @@ | |||
| #include "result.h" | |||
| #include "stream.pre.h" | |||
| #include "stream/for_each.h" | |||
| namespace asyncpp | |||
| { | |||
| @@ -47,6 +45,44 @@ namespace asyncpp | |||
| template<typename X_lambda> | |||
| inline auto for_each(X_lambda&& p_lambda) &&; | |||
| public: | |||
| /** | |||
| * @brief Transforms the stream from one type to another type | |||
| * by executing the passed lambda for each value. | |||
| */ | |||
| template<typename X_lambda> | |||
| inline auto map(X_lambda&& p_lambda) const &; | |||
| /** | |||
| * @brief Transforms the stream from one type to another type | |||
| * by executing the passed lambda for each value. | |||
| */ | |||
| template<typename X_lambda> | |||
| inline auto map(X_lambda&& p_lambda) &; | |||
| /** | |||
| * @brief Transforms the stream from one type to another type | |||
| * by executing the passed lambda for each value. | |||
| */ | |||
| template<typename X_lambda> | |||
| inline auto map(X_lambda&& p_lambda) &&; | |||
| public: | |||
| /** | |||
| * @brief Flatten the stream of streams to a single stream. | |||
| */ | |||
| inline auto flatten() const &; | |||
| /** | |||
| * @brief Flatten the stream of streams to a single stream. | |||
| */ | |||
| inline auto flatten() &; | |||
| /** | |||
| * @brief Flatten the stream of streams to a single stream. | |||
| */ | |||
| inline auto flatten() &&; | |||
| #ifdef asyncpp_timing | |||
| public: | |||
| /** | |||
| @@ -2,6 +2,8 @@ | |||
| #include "stream.h" | |||
| #include "stream/map.inl" | |||
| #include "stream/flatten.inl" | |||
| #include "stream/for_each.inl" | |||
| #ifdef asyncpp_timing | |||
| @@ -67,6 +69,106 @@ namespace asyncpp | |||
| std::forward<X_lambda>(p_lambda)); | |||
| } | |||
| /* base_stream::map */ | |||
| template< | |||
| typename T_value, | |||
| typename T_derived> | |||
| template< | |||
| typename X_lambda> | |||
| auto base_stream<T_value, T_derived> | |||
| ::map(X_lambda&& p_lambda) const & | |||
| { | |||
| using lambda_type = X_lambda; | |||
| using map_type = __stream::map_impl<derived_type, lambda_type>; | |||
| auto& self = static_cast<derived_type const &>(*this); | |||
| return map_type( | |||
| std::forward<derived_type const &>(self), | |||
| std::forward<X_lambda>(p_lambda)); | |||
| } | |||
| template< | |||
| typename T_value, | |||
| typename T_derived> | |||
| template< | |||
| typename X_lambda> | |||
| auto base_stream<T_value, T_derived> | |||
| ::map(X_lambda&& p_lambda) & | |||
| { | |||
| using lambda_type = X_lambda; | |||
| using map_type = __stream::map_impl<derived_type, lambda_type>; | |||
| auto& self = static_cast<derived_type &>(*this); | |||
| return map_type( | |||
| std::forward<derived_type &>(self), | |||
| std::forward<X_lambda>(p_lambda)); | |||
| } | |||
| template< | |||
| typename T_value, | |||
| typename T_derived> | |||
| template< | |||
| typename X_lambda> | |||
| auto base_stream<T_value, T_derived> | |||
| ::map(X_lambda&& p_lambda) && | |||
| { | |||
| using lambda_type = X_lambda; | |||
| using map_type = __stream::map_impl<derived_type, lambda_type>; | |||
| auto& self = static_cast<derived_type &>(*this); | |||
| return map_type( | |||
| std::forward<derived_type &&>(self), | |||
| std::forward<X_lambda>(p_lambda)); | |||
| } | |||
| /* base_stream::flatten */ | |||
| template< | |||
| typename T_value, | |||
| typename T_derived> | |||
| auto base_stream<T_value, T_derived> | |||
| ::flatten() const & | |||
| { | |||
| using flatten_type = __stream::flatten_impl<derived_type>; | |||
| auto& self = static_cast<derived_type const &>(*this); | |||
| return flatten_type( | |||
| std::forward<derived_type const &>(self)); | |||
| } | |||
| template< | |||
| typename T_value, | |||
| typename T_derived> | |||
| auto base_stream<T_value, T_derived> | |||
| ::flatten() & | |||
| { | |||
| using flatten_type = __stream::flatten_impl<derived_type>; | |||
| auto& self = static_cast<derived_type &>(*this); | |||
| return flatten_type( | |||
| std::forward<derived_type &>(self)); | |||
| } | |||
| template< | |||
| typename T_value, | |||
| typename T_derived> | |||
| auto base_stream<T_value, T_derived> | |||
| ::flatten() && | |||
| { | |||
| using flatten_type = __stream::flatten_impl<derived_type>; | |||
| auto& self = static_cast<derived_type &>(*this); | |||
| return flatten_type( | |||
| std::forward<derived_type &&>(self)); | |||
| } | |||
| /* base_stream::timeout */ | |||
| #ifdef asyncpp_timing | |||
| @@ -0,0 +1,47 @@ | |||
| #pragma once | |||
| #include <memory> | |||
| #include <asyncpp/core/stream.pre.h> | |||
| namespace asyncpp { | |||
| namespace __stream { | |||
| template< | |||
| typename T_stream> | |||
| struct flatten_impl final : | |||
| public base_stream< | |||
| typename T_stream::value_type::value_type, | |||
| flatten_impl<T_stream> | |||
| > | |||
| { | |||
| public: | |||
| using stream_type = T_stream; | |||
| using inner_stream_type = typename stream_type::value_type; | |||
| using inner_stream_ptr_u = std::unique_ptr<inner_stream_type>; | |||
| using value_type = typename inner_stream_type::value_type; | |||
| using this_type = flatten_impl<stream_type>; | |||
| using base_stream_type = base_stream<value_type, this_type>; | |||
| using result_type = typename base_stream_type::result_type; | |||
| private: | |||
| stream_type _stream; | |||
| inner_stream_ptr_u _inner; | |||
| public: | |||
| /** | |||
| * @brief Constructor. | |||
| */ | |||
| template< | |||
| typename X_stream> | |||
| inline flatten_impl( | |||
| X_stream&& p_stream); | |||
| public: /* future */ | |||
| /** | |||
| * @brief Poll the result from the future. | |||
| */ | |||
| inline result_type poll(); | |||
| }; | |||
| } } | |||
| @@ -0,0 +1,49 @@ | |||
| #pragma once | |||
| #include "flatten.h" | |||
| namespace asyncpp { | |||
| namespace __stream { | |||
| /* flatten_impl */ | |||
| template< | |||
| typename T_stream> | |||
| template< | |||
| typename X_stream> | |||
| flatten_impl<T_stream>::flatten_impl( | |||
| X_stream&& p_stream) | |||
| : _stream (std::forward<X_stream>(p_stream)) | |||
| , _inner () | |||
| { } | |||
| template< | |||
| typename T_stream> | |||
| typename flatten_impl<T_stream>::result_type | |||
| flatten_impl<T_stream> | |||
| ::poll() | |||
| { | |||
| while (true) | |||
| { | |||
| if (_inner) | |||
| { | |||
| auto r = _inner->poll(); | |||
| if (!r.is_done()) | |||
| return r; | |||
| _inner.reset(); | |||
| } | |||
| auto r = _stream.poll(); | |||
| if (r.is_done()) | |||
| return result_type::done(); | |||
| if (r.is_not_ready()) | |||
| return result_type::not_ready(); | |||
| _inner = std::make_unique<inner_stream_type>(std::move(*r)); | |||
| } | |||
| } | |||
| } } | |||
| @@ -22,9 +22,9 @@ namespace __stream { | |||
| using base_future_type = base_future<value_type, this_type>; | |||
| using result_type = typename base_future_type::result_type; | |||
| public: | |||
| stream_type stream; | |||
| lambda_type lambda; | |||
| private: | |||
| stream_type _stream; | |||
| lambda_type _lambda; | |||
| public: | |||
| /** | |||
| @@ -17,8 +17,8 @@ namespace __stream { | |||
| ::for_each_impl( | |||
| X_stream&& p_stream, | |||
| X_lambda&& p_lambda) | |||
| : stream(std::forward<X_stream>(p_stream)) | |||
| , lambda(std::forward<X_lambda>(p_lambda)) | |||
| : _stream(std::forward<X_stream>(p_stream)) | |||
| , _lambda(std::forward<X_lambda>(p_lambda)) | |||
| { } | |||
| template< | |||
| @@ -30,14 +30,14 @@ namespace __stream { | |||
| { | |||
| while (true) | |||
| { | |||
| auto r = stream.poll(); | |||
| auto r = _stream.poll(); | |||
| if (r.is_done()) | |||
| return result_type::ready(); | |||
| if (r.is_not_ready()) | |||
| return result_type::not_ready(); | |||
| r.call(lambda); | |||
| r.call(_lambda); | |||
| } | |||
| } | |||
| @@ -0,0 +1,48 @@ | |||
| #pragma once | |||
| #include <asyncpp/core/stream.pre.h> | |||
| namespace asyncpp { | |||
| namespace __stream { | |||
| template< | |||
| typename T_stream, | |||
| typename T_lambda> | |||
| struct map_impl final : | |||
| public base_stream< | |||
| decltype(std::declval<T_lambda>()(std::declval<typename T_stream::value_type>())), | |||
| map_impl<T_stream, T_lambda> | |||
| > | |||
| { | |||
| public: | |||
| using stream_type = T_stream; | |||
| using lambda_type = T_lambda; | |||
| using inner_value_type = typename stream_type::value_type; | |||
| using value_type = decltype(std::declval<lambda_type>()(std::declval<inner_value_type>())); | |||
| using this_type = map_impl<stream_type, lambda_type>; | |||
| using base_stream_type = base_stream<value_type, this_type>; | |||
| using result_type = typename base_stream_type::result_type; | |||
| private: | |||
| stream_type _stream; | |||
| lambda_type _lambda; | |||
| public: | |||
| /** | |||
| * @brief Constructor. | |||
| */ | |||
| template< | |||
| typename X_stream, | |||
| typename X_lambda> | |||
| inline map_impl( | |||
| X_stream&& p_stream, | |||
| X_lambda&& p_lambda); | |||
| public: /* future */ | |||
| /** | |||
| * @brief Poll the result from the future. | |||
| */ | |||
| inline result_type poll(); | |||
| }; | |||
| } } | |||
| @@ -0,0 +1,40 @@ | |||
| #pragma once | |||
| #include "map.h" | |||
| namespace asyncpp { | |||
| namespace __stream { | |||
| /* map_impl */ | |||
| template< | |||
| typename T_stream, | |||
| typename T_lambda> | |||
| template< | |||
| typename X_stream, | |||
| typename X_lambda> | |||
| map_impl<T_stream, T_lambda>::map_impl( | |||
| X_stream&& p_stream, | |||
| X_lambda&& p_lambda) | |||
| : _stream(std::forward<X_stream>(p_stream)) | |||
| , _lambda(std::forward<X_lambda>(p_lambda)) | |||
| { } | |||
| template< | |||
| typename T_stream, | |||
| typename T_lambda> | |||
| typename map_impl<T_stream, T_lambda>::result_type | |||
| map_impl<T_stream, T_lambda> | |||
| ::poll() | |||
| { | |||
| auto r = _stream.poll(); | |||
| if (r.is_done()) | |||
| return result_type::done(); | |||
| if (r.is_ready()) | |||
| return result_type::ready(r.call(_lambda)); | |||
| return result_type::not_ready(); | |||
| } | |||
| } } | |||
| @@ -0,0 +1,5 @@ | |||
| #pragma once | |||
| #include "fs/directory.h" | |||
| #include "fs/directory.inl" | |||
| @@ -0,0 +1,43 @@ | |||
| #pragma once | |||
| #include <string> | |||
| #include <cppfs/path.h> | |||
| #include <asyncpp/core/stream.pre.h> | |||
| #include <asyncpp/core/future/lazy.h> | |||
| namespace asyncpp { | |||
| namespace fs { | |||
| /** | |||
| * @brief Create future to read the content of a directory. | |||
| */ | |||
| inline auto read_dir(const cppfs::path& p_path); | |||
| /** | |||
| * @brief Create future to read the content of a directory including the subdirectories. | |||
| */ | |||
| inline auto read_dir_all(const cppfs::path& p_path); | |||
| /** | |||
| * @brief Create a new future to create a directory. | |||
| */ | |||
| inline auto create_dir(const cppfs::path& p_path); | |||
| /** | |||
| * @brief Create a new future to create a directory and all it's parent directories. | |||
| */ | |||
| inline auto create_dir_all(const cppfs::path& p_path); | |||
| /** | |||
| * @brief Create a new future to remove an empty directory. | |||
| */ | |||
| inline auto remove_dir(const cppfs::path& p_path); | |||
| /** | |||
| * @brief Create a new future to remove a directory and all it's content. | |||
| */ | |||
| inline auto remove_dir_all(const cppfs::path& p_path); | |||
| } } | |||
| @@ -0,0 +1,167 @@ | |||
| #pragma once | |||
| #include <vector> | |||
| #include <cppfs/directory.h> | |||
| #include "directory.h" | |||
| namespace asyncpp { | |||
| namespace fs { | |||
| namespace __impl | |||
| { | |||
| struct read_dir_impl | |||
| : public base_stream< | |||
| cppfs::directory::entry, | |||
| read_dir_impl> | |||
| { | |||
| public: | |||
| using value_type = cppfs::directory::entry; | |||
| using this_type = read_dir_impl; | |||
| using base_future_type = base_stream<value_type, this_type>; | |||
| using result_type = typename base_future_type::result_type; | |||
| private: | |||
| cppfs::directory::iterator _it; | |||
| cppfs::directory::iterator _end; | |||
| public: | |||
| inline read_dir_impl( | |||
| const cppfs::path& p_path) | |||
| { | |||
| cppfs::directory d(p_path); | |||
| if (d.exists()) | |||
| { | |||
| _it = d.begin(); | |||
| _end = d.end(); | |||
| } | |||
| } | |||
| public: | |||
| inline result_type poll() | |||
| { | |||
| if (_it == _end) | |||
| return result_type::done(); | |||
| auto ret = result_type::ready(*_it); | |||
| ++_it; | |||
| return ret; | |||
| } | |||
| }; | |||
| struct read_dir_all_impl | |||
| : public base_stream< | |||
| cppfs::directory::entry, | |||
| read_dir_all_impl> | |||
| { | |||
| public: | |||
| using value_type = cppfs::directory::entry; | |||
| using this_type = read_dir_impl; | |||
| using base_future_type = base_stream<value_type, this_type>; | |||
| using result_type = typename base_future_type::result_type; | |||
| private: | |||
| struct entry | |||
| { | |||
| cppfs::directory::iterator it; | |||
| cppfs::directory::iterator end; | |||
| }; | |||
| using entry_stack = std::vector<entry>; | |||
| entry_stack _stack; | |||
| public: | |||
| inline read_dir_all_impl( | |||
| const cppfs::path& p_path) | |||
| { | |||
| cppfs::directory d(p_path); | |||
| if (d.exists()) | |||
| { | |||
| _stack.emplace_back(entry { | |||
| d.begin(), | |||
| d.end() | |||
| }); | |||
| } | |||
| } | |||
| public: | |||
| inline result_type poll() | |||
| { | |||
| while (true) | |||
| { | |||
| if (_stack.empty()) | |||
| return result_type::done(); | |||
| auto& e = _stack.back(); | |||
| if (e.it == e.end) | |||
| { | |||
| _stack.pop_back(); | |||
| continue; | |||
| } | |||
| auto ret = result_type::ready(std::move(*e.it)); | |||
| cppfs::directory d(e.it->path); | |||
| ++e.it; | |||
| if (d.exists()) | |||
| { | |||
| _stack.emplace_back(entry { | |||
| d.begin(), | |||
| d.end(), | |||
| }); | |||
| } | |||
| return ret; | |||
| } | |||
| } | |||
| }; | |||
| } | |||
| auto read_dir(const cppfs::path& p_path) | |||
| { | |||
| return __impl::read_dir_impl(p_path); | |||
| } | |||
| auto read_dir_all(const cppfs::path& p_path) | |||
| { | |||
| return __impl::read_dir_all_impl(p_path); | |||
| } | |||
| auto create_dir(const cppfs::path& p_path) | |||
| { | |||
| return lazy([d = cppfs::directory(p_path)]{ | |||
| if (!d.exists()) | |||
| d.create(false); | |||
| }); | |||
| } | |||
| auto create_dir_all(const cppfs::path& p_path) | |||
| { | |||
| return lazy([d = cppfs::directory(p_path)]{ | |||
| if (!d.exists()) | |||
| d.create(true); | |||
| }); | |||
| } | |||
| auto remove_dir(const cppfs::path& p_path) | |||
| { | |||
| return lazy([d = cppfs::directory(p_path)]{ | |||
| if (d.exists()) | |||
| d.remove(false); | |||
| }); | |||
| } | |||
| auto remove_dir_all(const cppfs::path& p_path) | |||
| { | |||
| return lazy([d = cppfs::directory(p_path)]{ | |||
| if (d.exists()) | |||
| d.remove(true); | |||
| }); | |||
| } | |||
| } } | |||
| @@ -4,6 +4,7 @@ Include ( cotire OPTIONAL RESULT_VARIABLE HAS | |||
| Include ( pedantic OPTIONAL RESULT_VARIABLE HAS_PEDANTIC ) | |||
| Include ( strip_symbols OPTIONAL RESULT_VARIABLE HAS_STRIP_SYMBOLS ) | |||
| Find_Package ( cppfs REQUIRED ) | |||
| Find_Package ( cppcore REQUIRED ) | |||
| # Interface Library ############################################################################### | |||
| @@ -16,6 +17,7 @@ Target_Include_Directories ( asyncpp | |||
| $<INSTALL_INTERFACE:${ASYNCPP_INSTALL_DIR_INCLUDE}> ) | |||
| Target_Link_Libraries ( asyncpp | |||
| INTERFACE | |||
| cppfs::cppfs | |||
| cppcore::cppcore ) | |||
| # Install ######################################################################################### | |||
| @@ -72,5 +72,21 @@ TEST(future_tests, and_then) | |||
| auto r = f.poll(); | |||
| ASSERT_TRUE(r); | |||
| ASSERT_EQ (5, *r); | |||
| ASSERT_EQ (2, f.first._count); | |||
| } | |||
| TEST(future_tests, lazy) | |||
| { | |||
| int i = 1; | |||
| auto f = lazy([&]{ | |||
| ++i; | |||
| return i + 1; | |||
| }); | |||
| ASSERT_EQ(i, 1); | |||
| auto r = f.poll(); | |||
| ASSERT_TRUE(r); | |||
| ASSERT_EQ (2, i); | |||
| ASSERT_EQ (3, *r); | |||
| } | |||
| @@ -73,3 +73,72 @@ TEST(stream_tests, for_each) | |||
| auto r = f.poll(); | |||
| ASSERT_TRUE(r); | |||
| } | |||
| TEST(stream_tests, map) | |||
| { | |||
| delay d { 5, 0, 0 }; | |||
| auto s = d | |||
| .map([](int i){ | |||
| return std::to_string(i); | |||
| }); | |||
| auto r = s.poll(); | |||
| ASSERT_TRUE(r); | |||
| ASSERT_EQ (*r, "1"); | |||
| r = s.poll(); | |||
| ASSERT_TRUE(r); | |||
| ASSERT_EQ (*r, "2"); | |||
| r = s.poll(); | |||
| ASSERT_TRUE(r); | |||
| ASSERT_EQ (*r, "3"); | |||
| r = s.poll(); | |||
| ASSERT_TRUE(r); | |||
| ASSERT_EQ (*r, "4"); | |||
| r = s.poll(); | |||
| ASSERT_TRUE(r); | |||
| ASSERT_EQ (*r, "5"); | |||
| r = s.poll(); | |||
| ASSERT_TRUE(r.is_done()); | |||
| } | |||
| TEST(stream_tests, flatten) | |||
| { | |||
| delay d { 3, 0, 0 }; | |||
| auto s = d | |||
| .map([](int i){ | |||
| return delay { i * 100 + 2, 0, i * 100 }; | |||
| }) | |||
| .flatten(); | |||
| auto r = s.poll(); | |||
| ASSERT_TRUE(r); | |||
| ASSERT_EQ (*r, 101); | |||
| r = s.poll(); | |||
| ASSERT_TRUE(r); | |||
| ASSERT_EQ (*r, 102); | |||
| r = s.poll(); | |||
| ASSERT_TRUE(r); | |||
| ASSERT_EQ (*r, 201); | |||
| r = s.poll(); | |||
| ASSERT_TRUE(r); | |||
| ASSERT_EQ (*r, 202); | |||
| r = s.poll(); | |||
| ASSERT_TRUE(r); | |||
| ASSERT_EQ (*r, 301); | |||
| r = s.poll(); | |||
| ASSERT_TRUE(r); | |||
| ASSERT_EQ (*r, 302); | |||
| r = s.poll(); | |||
| ASSERT_TRUE(r.is_done()); | |||
| } | |||
| @@ -0,0 +1,210 @@ | |||
| #include <gtest/gtest.h> | |||
| #include <asyncpp.h> | |||
| #include <asyncpp/fs.h> | |||
| using namespace ::testing; | |||
| using namespace ::asyncpp; | |||
| struct auto_remove_dir | |||
| : public cppfs::directory | |||
| { | |||
| public: | |||
| using cppfs::directory::directory; | |||
| inline ~auto_remove_dir() | |||
| { | |||
| if (exists()) | |||
| remove(true); | |||
| } | |||
| }; | |||
| class directory_tests | |||
| : public ::testing::Test | |||
| { | |||
| protected: | |||
| static const std::string& path_test_dir() | |||
| { | |||
| static const std::string value("/tmp/asyncpp/test"); | |||
| return value; | |||
| } | |||
| static const std::string& path_test_dir_single() | |||
| { | |||
| static const std::string value("/tmp/asyncpp/test/single"); | |||
| return value; | |||
| } | |||
| static const std::string& path_test_dir_parent() | |||
| { | |||
| static const std::string value("/tmp/asyncpp/test/parent"); | |||
| return value; | |||
| } | |||
| static const std::string& path_test_dir_child() | |||
| { | |||
| static const std::string value("/tmp/asyncpp/test/parent/child"); | |||
| return value; | |||
| } | |||
| void SetUp() override | |||
| { | |||
| cppfs::directory d(path_test_dir()); | |||
| if (!d.exists()) | |||
| d.create(true); | |||
| } | |||
| void TearDown() override | |||
| { | |||
| cppfs::directory d(path_test_dir()); | |||
| if (d.exists()) | |||
| d.remove(true); | |||
| } | |||
| }; | |||
| TEST_F(directory_tests, read_dir) | |||
| { | |||
| using namespace cppfs; | |||
| using namespace std::string_literals; | |||
| auto s = fs::read_dir("."s); | |||
| auto v = std::vector<std::string>(); | |||
| auto r = decltype(s)::result_type::not_ready(); | |||
| while ((r = s.poll()).is_ready()) | |||
| v.emplace_back(r->path.parts().back()); | |||
| ASSERT_TRUE(r.is_done()); | |||
| std::sort(v.begin(), v.end(), cppcore::op_invariant_string_less { }); | |||
| EXPECT_EQ(v, std::vector<std::string>({ | |||
| "asyncpp", | |||
| "CMakeLists.txt", | |||
| "helper", | |||
| })); | |||
| } | |||
| TEST_F(directory_tests, read_dir_all) | |||
| { | |||
| using namespace cppfs; | |||
| using namespace std::string_literals; | |||
| auto s = fs::read_dir_all("."s); | |||
| auto r = decltype(s)::result_type::not_ready(); | |||
| auto entries = std::vector<cppfs::directory::entry>(); | |||
| while ((r = s.poll()).is_ready()) | |||
| entries.emplace_back(*r); | |||
| ASSERT_TRUE(r.is_done()); | |||
| std::sort(entries.begin(), entries.end()); | |||
| std::vector<std::string> paths; | |||
| paths.reserve(entries.size()); | |||
| std::transform( | |||
| entries.begin(), | |||
| entries.end(), | |||
| std::back_inserter(paths), | |||
| [](auto& e) { | |||
| auto s = std::to_string(static_cast<int>(e.type)) + e.path.str(); | |||
| return s; | |||
| }); | |||
| EXPECT_EQ(paths, std::vector<std::string>({ | |||
| "1./asyncpp", | |||
| "1./asyncpp/core", | |||
| "2./asyncpp/core/future_tests.cpp", | |||
| "2./asyncpp/core/result_tests.cpp", | |||
| "2./asyncpp/core/stream_tests.cpp", | |||
| "2./asyncpp/core/task_tests.cpp", | |||
| "1./asyncpp/executor", | |||
| "2./asyncpp/executor/current_thread_tests.cpp", | |||
| "1./asyncpp/fs", | |||
| "2./asyncpp/fs/directory_tests.cpp", | |||
| "1./asyncpp/timing", | |||
| "2./asyncpp/timing/delay_tests.cpp", | |||
| "2./asyncpp/timing/interval_tests.cpp", | |||
| "2./asyncpp/timing/timeout_tests.cpp", | |||
| "2./asyncpp/timing/timer_tests.cpp", | |||
| "1./helper", | |||
| "2./helper/now_mock.h", | |||
| "2./helper/runtime_mock.h", | |||
| "2./helper/test_delay.h", | |||
| "2./CMakeLists.txt", | |||
| })); | |||
| } | |||
| TEST_F(directory_tests, create_dir) | |||
| { | |||
| auto f = | |||
| lazy([path=path_test_dir_single()]{ | |||
| return path; | |||
| }) | |||
| .and_then(fs::create_dir); | |||
| auto_remove_dir d(path_test_dir_single()); | |||
| ASSERT_FALSE(d.exists()); | |||
| auto r = f.poll(); | |||
| ASSERT_TRUE (r); | |||
| ASSERT_TRUE (d.exists()); | |||
| } | |||
| TEST_F(directory_tests, create_dir_all) | |||
| { | |||
| auto f = | |||
| lazy([path=path_test_dir_child()]{ | |||
| return path; | |||
| }) | |||
| .and_then(fs::create_dir_all); | |||
| auto_remove_dir d(path_test_dir_child()); | |||
| ASSERT_FALSE(d.exists()); | |||
| auto r = f.poll(); | |||
| ASSERT_TRUE (r); | |||
| ASSERT_TRUE (d.exists()); | |||
| } | |||
| TEST_F(directory_tests, remove_dir) | |||
| { | |||
| auto f = | |||
| lazy([path=path_test_dir_single()]{ | |||
| return path; | |||
| }) | |||
| .and_then(fs::remove_dir); | |||
| auto_remove_dir d(path_test_dir_single()); | |||
| d.create(true); | |||
| ASSERT_TRUE (d.exists()); | |||
| auto r = f.poll(); | |||
| ASSERT_TRUE (r); | |||
| ASSERT_FALSE(d.exists()); | |||
| } | |||
| TEST_F(directory_tests, remove_dir_all) | |||
| { | |||
| auto f = | |||
| lazy([path=path_test_dir_parent()]{ | |||
| return path; | |||
| }) | |||
| .and_then(fs::remove_dir_all); | |||
| auto_remove_dir d(path_test_dir_child()); | |||
| d.create(true); | |||
| ASSERT_TRUE (d.exists()); | |||
| auto r = f.poll(); | |||
| ASSERT_TRUE (r); | |||
| ASSERT_FALSE(d.exists()); | |||
| } | |||