/* * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License Version 2.0 with LLVM Exceptions * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * https://llvm.org/LICENSE.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include "mock_receiver.hpp" #include #include #include #include #include using namespace unifex; using namespace unifex_test; using namespace testing; namespace { // We need to validate the following contract: // - any_sender_of is a typed_sender // - sender_traits>::value_types // is std::variant> // - sender_traits>::error_types is // std::variant // - any_sender_of is constructible from just(declval()...) // - connect(any_sender_of{}, mock_receiver{}): // - invokes nothing on the receiver immediately, but // - returns an operation state that, when started, acts on the receiver // as if the wrapped sender and receiver were directly connected // - there is no confusion when an any_sender_of is connect with a // receiver that can receive more than one kind of tuple // // Stretch: // - all operations that would be noexcept with directly connected sender and // receiver pairs are also noexcept when the same pair is connected through // an any_sender_of/any_receiver_of pair template struct AnySenderOfTestImpl : Test { using any_sender = any_sender_of; static constexpr size_t value_count = sizeof...(T); static_assert(typed_sender); static_assert(sender_to>); static_assert(std::is_same_v>, sender_value_types_t>); static_assert(std::is_same_v, sender_error_types_t>); static auto default_just() { if constexpr (value_count == 0) { return just(); } else if constexpr (value_count == 1) { return just(42); } else if constexpr (value_count == 2) { return just(42, std::string{"hello"}); } } template static auto& expect_set_value_call(U& receiver) noexcept { if constexpr (value_count == 0) { return EXPECT_CALL(receiver, set_value()); } else if constexpr (value_count == 1) { return EXPECT_CALL(receiver, set_value(_)); } else if constexpr (value_count == 2) { return EXPECT_CALL(receiver, set_value(_, _)); } } }; template struct AnySenderOfTest; template struct AnySenderOfTest : AnySenderOfTestImpl {}; template struct AnySenderOfTest : AnySenderOfTestImpl {}; using AnySenderOfTestTypes = Types< void(), void() noexcept, void(int), void(int) noexcept, void(int, std::string), void(int, std::string) noexcept>; TYPED_TEST_SUITE(AnySenderOfTest, AnySenderOfTestTypes, ); template void testWrappingAJust() noexcept { using test_t = AnySenderOfTest; using any_sender = typename test_t::any_sender; any_sender sender = test_t::default_just(); mock_receiver receiver; auto op = connect(std::move(sender), receiver); test_t::expect_set_value_call(*receiver) .WillOnce(Invoke([](auto... values) noexcept { if constexpr (test_t::value_count == 0) { EXPECT_EQ(std::tuple{}, std::tie(values...)); } else if constexpr (test_t::value_count == 1) { EXPECT_EQ(std::tuple{42}, std::tie(values...)); } else { static_assert(test_t::value_count == 2, "Unimplemented"); EXPECT_EQ((std::tuple{42, std::string{"hello"}}), std::tie(values...)); } })); start(op); } } // TYPED_TEST(AnySenderOfTest, AnySenderOfCanWrapAJust) { testWrappingAJust(); } TYPED_TEST(AnySenderOfTest, AnySenderOfCanConnectToAMultiReceiver) { testWrappingAJust(); } #if !defined(_MSC_VER) // TODO: Investigate why MSVC can't compile these tests. TYPED_TEST(AnySenderOfTest, AnySenderOfCanBeCancelled) { using test_t = AnySenderOfTest; using any_sender = typename test_t::any_sender; any_sender sender = finally(test_t::default_just(), ready_done_sender{}); mock_receiver receiver; auto op = connect(std::move(sender), receiver); EXPECT_CALL(*receiver, set_done()).Times(1); start(op); } #if !UNIFEX_NO_EXCEPTIONS TYPED_TEST(AnySenderOfTest, AnySenderOfCanError) { using test_t = AnySenderOfTest; using any_sender = typename test_t::any_sender; any_sender sender = finally(test_t::default_just(), then(just(), [] { throw std::runtime_error("uh oh"); })); mock_receiver receiver; auto op = connect(std::move(sender), receiver); EXPECT_CALL(*receiver, set_error(_)) .WillOnce(Invoke([](std::exception_ptr eptr) noexcept { ASSERT_TRUE(eptr); EXPECT_NO_THROW({ try { std::rethrow_exception(eptr); } catch (std::runtime_error& e) { EXPECT_STREQ("uh oh", e.what()); } }); })); start(op); } #endif // !UNIFEX_NO_EXCEPTIONS #endif // !defined(_MSC_VER) TEST(AnySenderOfTest, SchedulerProvider) { // Build a list of required receiver queries; in this case, just get_scheduler: using Queries = with_receiver_queries(get_scheduler)>; // From that list of receiver queries, generate a type-erased sender: using Sender = Queries::any_sender_of; // Type-erase a sender. This sender only connects to receivers that have // implemented the required receiver queries. Sender j = just(42, std::string{"hello"}); // Wrap the sender such that all passed-in receivers are wrapped in a // wrapper that implements the get_scheduler query to return an // inline_scheduler auto sender = with_query_value(std::move(j), get_scheduler, inline_scheduler{}); mock_receiver receiver; auto op = connect(std::move(sender), receiver); EXPECT_CALL(*receiver, set_value(_, _)) .WillOnce(Invoke([](auto... values) noexcept { EXPECT_EQ((std::tuple{42, std::string{"hello"}}), std::tie(values...)); })); start(op); }