/* Copyright(c) 2015 - 2018 Denis Blank Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions : The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. **/ #include #include #include #include #include #include #include #include #include #include #include #include #include "test-continuable.hpp" using std::get; using std::make_tuple; using std::tuple; using cti::async_traverse_complete_tag; using cti::async_traverse_detach_tag; using cti::async_traverse_in_place_tag; using cti::async_traverse_visit_tag; using cti::detail::util::unused; using cti::traverse_pack_async; /// A tag which isn't accepted by any mapper struct not_accepted_tag {}; template class async_counter_base : public std::enable_shared_from_this { std::size_t counter_ = 0; public: async_counter_base() = default; virtual ~async_counter_base() { } std::size_t const& counter() const noexcept { return counter_; } std::size_t& counter() noexcept { return counter_; } }; template struct async_increasing_int_sync_visitor : async_counter_base> { bool operator()(async_traverse_visit_tag, std::size_t i) { EXPECT_EQ(i, this->counter()); ++this->counter(); return true; } template void operator()(async_traverse_detach_tag, std::size_t i, N&& next) { unused(i); unused(next); // Should never be called! EXPECT_TRUE(false); } template void operator()(async_traverse_complete_tag, T&& pack) { unused(pack); EXPECT_EQ(this->counter(), ArgCount); ++this->counter(); } }; template struct async_increasing_int_visitor : async_counter_base> { bool operator()(async_traverse_visit_tag, std::size_t i) const { EXPECT_EQ(i, this->counter()); return false; } template void operator()(async_traverse_detach_tag, std::size_t i, N&& next) { unused(i); ++this->counter(); std::forward(next)(); } template void operator()(async_traverse_complete_tag, T&& pack) { unused(pack); EXPECT_EQ(this->counter(), ArgCount); ++this->counter(); } }; template void test_async_traversal_base(Args&&... args) { // Test that every element is traversed in the correct order // when we detach the control flow on every visit. { auto result = traverse_pack_async( async_increasing_int_sync_visitor{}, args...); EXPECT_EQ(result->counter(), ArgCount + 1U); } // Test that every element is traversed in the correct order // when we detach the control flow on every visit. { auto result = traverse_pack_async(async_increasing_int_visitor{}, args...); EXPECT_EQ(result->counter(), ArgCount + 1U); } } TEST(async_traversal, base) { // Just test everything using a casual int pack test_async_traversal_base<4U>(not_accepted_tag{}, 0U, 1U, not_accepted_tag{}, 2U, 3U, not_accepted_tag{}); } template void test_async_container_traversal_impl(ContainerFactory&& container_of) { // Test by passing a containers in the middle test_async_traversal_base<4U>(0U, container_of(1U, 2U), 3U); // Test by splitting the pack in two containers test_async_traversal_base<4U>(container_of(0U, 1U), container_of(2U, 3U)); // Test by passing a huge containers to the traversal test_async_traversal_base<4U>(container_of(0U, 1U, 2U, 3U)); } template struct common_container_factory { template T operator()(Args&&... args) { return T{std::forward(args)...}; } }; template struct array_container_factory { template > Array operator()(Args&&... args) { return Array{{std::forward(args)...}}; } }; TEST(async_traversal_container, visit_vector) { common_container_factory> factory; test_async_container_traversal_impl(factory); } TEST(async_traversal_container, visit_list) { common_container_factory> factory; test_async_container_traversal_impl(factory); } TEST(async_traversal_container, visit_set) { common_container_factory> factory; test_async_container_traversal_impl(factory); } TEST(async_traversal_container, visit_array) { array_container_factory factory; test_async_container_traversal_impl(factory); } // Test by passing a tuple in the middle TEST(async_traversal_tuple_like, visit_tuple) { test_async_traversal_base<4U>(not_accepted_tag{}, 0U, make_tuple(1U, not_accepted_tag{}, 2U), 3U); } // Test by splitting the pack in two tuples TEST(async_traversal_tuple_like, visit_tuple_nested) { test_async_traversal_base<4U>(make_tuple(0U, not_accepted_tag{}, 1U), make_tuple(2U, 3U)); } // Test by passing a huge tuple to the traversal TEST(async_traversal_tuple_like, visit_tuple_huge) { test_async_traversal_base<4U>(make_tuple(0U, 1U, 2U, 3U)); } template ::type>> Vector vector_of(T&& first, Args&&... args) { return Vector{std::forward(first), std::forward(args)...}; } // Test hierarchies where container and tuple like types are mixed TEST(async_traversal_mixed_traversal, visit_tuple_container) { test_async_traversal_base<4U>( 0U, make_tuple(std::vector{1U, 2U}), 3U); } TEST(async_traversal_mixed_traversal, visit_mixed_non_accepted) { test_async_traversal_base<4U>( make_tuple(0U, vector_of(not_accepted_tag{}), vector_of(vector_of(1U))), make_tuple(2U, 3U)); } TEST(async_traversal_mixed_traversal, visit_vector_vector_tuple) { test_async_traversal_base<4U>( vector_of(vector_of(make_tuple(0U, 1U, 2U, 3U)))); } template struct async_unique_sync_visitor : async_counter_base> { explicit async_unique_sync_visitor(not_accepted_tag) { } bool operator()(async_traverse_visit_tag, std::unique_ptr& i) { EXPECT_EQ(*i, this->counter()); ++this->counter(); return true; } template void operator()(async_traverse_detach_tag, std::unique_ptr& i, N&& next) { unused(i); unused(next); // Should never be called! EXPECT_TRUE(false); } template void operator()(async_traverse_complete_tag, T&& pack) { unused(pack); EXPECT_EQ(this->counter(), ArgCount); ++this->counter(); } }; template struct async_unique_visitor : async_counter_base> { explicit async_unique_visitor(not_accepted_tag) { } bool operator()(async_traverse_visit_tag, std::unique_ptr& i) const { EXPECT_EQ(*i, this->counter()); return false; } template void operator()(async_traverse_detach_tag, std::unique_ptr& i, N&& next) { unused(i); ++this->counter(); std::forward(next)(); } template void operator()(async_traverse_complete_tag, T&& pack) { unused(pack); EXPECT_EQ(this->counter(), ArgCount); ++this->counter(); } }; inline auto of(std::size_t i) { return std::make_unique(i); } TEST(async_traverse_in_place, construct_inplace_sync) { auto result = traverse_pack_async( async_traverse_in_place_tag>{}, not_accepted_tag{}, of(0), of(1), of(2), of(3)); EXPECT_EQ(result->counter(), 5U); } TEST(async_traverse_in_place, construct_inplace_async) { auto result = traverse_pack_async( async_traverse_in_place_tag>{}, not_accepted_tag{}, of(0), of(1), of(2), of(3)); EXPECT_EQ(result->counter(), 5U); } struct invalidate_visitor : async_counter_base { bool operator()(async_traverse_visit_tag, std::shared_ptr& i) const { EXPECT_EQ(*i, 22); return false; } template void operator()(async_traverse_detach_tag, std::shared_ptr& i, N&& next) { unused(i); std::forward(next)(); } // Test whether the passed pack was passed as r-value reference void operator()(async_traverse_complete_tag, tuple>&& pack) const { // Invalidate the moved object tuple> moved = std::move(pack); unused(moved); } }; // Check whether the arguments are invalidated (moved out) when called TEST(async_complete_invalidation, check_whether_frame_released) { auto value = std::make_shared(22); auto frame = traverse_pack_async(invalidate_visitor{}, value); EXPECT_EQ(value.use_count(), 1L); }