* 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()); | |||
} |