blob: 7e3724a3d3c4bb50000047dba39eacd7fd85daff [file] [edit]
// Copyright 2026 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// 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 "pw_async2/transform.h"
#include "pw_async2/channel.h"
#include "pw_async2/dispatcher.h"
#include "pw_async2/dispatcher_for_test.h"
#include "pw_async2/future.h"
#include "pw_async2/poll.h"
#include "pw_async2/value_future.h"
#include "pw_unit_test/framework.h"
namespace {
using ::pw::async2::ChannelStorage;
using ::pw::async2::CreateSpscChannel;
using ::pw::async2::DispatcherForTest;
using ::pw::async2::Ready;
using ::pw::async2::ValueFuture;
using ::pw::async2::ValueProvider;
using ::pw::async2::experimental::Map;
using ::pw::async2::experimental::Then;
struct MoveOnly {
MoveOnly(int val) : value(val) {}
MoveOnly(const MoveOnly&) = delete;
MoveOnly& operator=(const MoveOnly&) = delete;
MoveOnly(MoveOnly&&) = default;
MoveOnly& operator=(MoveOnly&&) = default;
int value;
};
TEST(FuturePipe, Map) {
DispatcherForTest dispatcher;
ValueProvider<int> provider;
auto future = provider.Get() | Map([](int x) { return x * 2; });
static_assert(std::is_same_v<decltype(future)::value_type, int>);
EXPECT_TRUE(future.is_pendable());
EXPECT_FALSE(future.is_complete());
provider.Resolve(10);
EXPECT_EQ(dispatcher.RunInTaskUntilStalled(future), Ready(20));
EXPECT_FALSE(future.is_pendable());
EXPECT_TRUE(future.is_complete());
}
TEST(FuturePipe, MapMoveOnly) {
DispatcherForTest dispatcher;
ValueProvider<MoveOnly> provider;
auto future = provider.Get() | Map([](MoveOnly x) { return x.value * 2; });
static_assert(std::is_same_v<decltype(future)::value_type, int>);
EXPECT_TRUE(future.is_pendable());
provider.Resolve(MoveOnly(10));
EXPECT_EQ(dispatcher.RunInTaskUntilStalled(future), Ready(20));
}
TEST(FuturePipe, MapChain) {
DispatcherForTest dispatcher;
ValueProvider<int> provider;
// clang-format off
auto future = provider.Get()
| Map([](int x) { return x + 5; })
| Map([](int x) { return static_cast<char>(x * 5); });
// clang-format on
static_assert(std::is_same_v<decltype(future)::value_type, char>);
EXPECT_TRUE(future.is_pendable());
EXPECT_FALSE(future.is_complete());
provider.Resolve(10);
EXPECT_EQ(dispatcher.RunInTaskUntilStalled(future), Ready('K'));
}
TEST(FuturePipe, Then) {
DispatcherForTest dispatcher;
ValueProvider<int> provider;
ChannelStorage<int, 1> storage;
auto [handle, sender, receiver] = CreateSpscChannel(storage);
auto future = provider.Get() | Then([s = std::move(sender)](int x) mutable {
return s.Send(x * 10);
});
static_assert(std::is_same_v<decltype(future)::value_type, bool>);
EXPECT_TRUE(future.is_pendable());
EXPECT_FALSE(future.is_complete());
provider.Resolve(5);
EXPECT_EQ(dispatcher.RunInTaskUntilStalled(future), Ready(true));
pw::Result<int> received = receiver.TryReceive();
EXPECT_TRUE(received.ok());
EXPECT_EQ(*received, 50);
EXPECT_FALSE(future.is_pendable());
EXPECT_TRUE(future.is_complete());
}
TEST(FuturePipe, ThenMoveOnly) {
DispatcherForTest dispatcher;
ValueProvider<MoveOnly> provider;
ChannelStorage<int, 1> storage;
auto [handle, sender, receiver] = CreateSpscChannel(storage);
auto future =
provider.Get() | Then([s = std::move(sender)](MoveOnly x) mutable {
return s.Send(x.value * 10);
});
static_assert(std::is_same_v<decltype(future)::value_type, bool>);
provider.Resolve(MoveOnly(5));
EXPECT_EQ(dispatcher.RunInTaskUntilStalled(future), Ready(true));
pw::Result<int> received = receiver.TryReceive();
EXPECT_TRUE(received.ok());
EXPECT_EQ(*received, 50);
}
TEST(FuturePipe, MapVoidFuture) {
DispatcherForTest dispatcher;
ValueProvider<void> provider;
auto future = provider.Get() | Map([]() { return 42; });
static_assert(std::is_same_v<typename decltype(future)::value_type, int>);
EXPECT_TRUE(future.is_pendable());
EXPECT_FALSE(future.is_complete());
provider.Resolve();
EXPECT_EQ(dispatcher.RunInTaskUntilStalled(future), Ready(42));
EXPECT_FALSE(future.is_pendable());
EXPECT_TRUE(future.is_complete());
}
TEST(FuturePipe, MapToVoid) {
DispatcherForTest dispatcher;
ValueProvider<int> provider;
int output = 0;
auto future = provider.Get() | Map([&output](int x) { output = x; });
static_assert(std::is_same_v<typename decltype(future)::value_type, void>);
EXPECT_TRUE(future.is_pendable());
EXPECT_FALSE(future.is_complete());
provider.Resolve(42);
EXPECT_EQ(dispatcher.RunInTaskUntilStalled(future), Ready());
EXPECT_EQ(output, 42);
EXPECT_FALSE(future.is_pendable());
EXPECT_TRUE(future.is_complete());
}
TEST(FuturePipe, MapVoidToVoid) {
DispatcherForTest dispatcher;
ValueProvider<void> provider;
bool executed = false;
auto future = provider.Get() | Map([&executed]() { executed = true; });
static_assert(std::is_same_v<typename decltype(future)::value_type, void>);
EXPECT_TRUE(future.is_pendable());
EXPECT_FALSE(future.is_complete());
provider.Resolve();
EXPECT_EQ(dispatcher.RunInTaskUntilStalled(future), Ready());
EXPECT_TRUE(executed);
EXPECT_FALSE(future.is_pendable());
EXPECT_TRUE(future.is_complete());
}
TEST(FuturePipe, ThenVoidFuture) {
DispatcherForTest dispatcher;
ValueProvider<void> provider1;
ValueProvider<int> provider2;
auto future = provider1.Get() | Then([&]() { return provider2.Get(); });
static_assert(std::is_same_v<typename decltype(future)::value_type, int>);
EXPECT_TRUE(future.is_pendable());
EXPECT_FALSE(future.is_complete());
provider1.Resolve();
EXPECT_EQ(dispatcher.RunInTaskUntilStalled(future), ::pw::async2::Pending());
provider2.Resolve(123);
EXPECT_EQ(dispatcher.RunInTaskUntilStalled(future), Ready(123));
EXPECT_FALSE(future.is_pendable());
EXPECT_TRUE(future.is_complete());
}
TEST(FuturePipe, ThenToVoid) {
DispatcherForTest dispatcher;
ValueProvider<int> provider1;
ValueProvider<void> provider2;
int output = 0;
auto future = provider1.Get() | Then([&](int x) {
output = x;
return provider2.Get();
});
static_assert(std::is_same_v<typename decltype(future)::value_type, void>);
EXPECT_TRUE(future.is_pendable());
EXPECT_FALSE(future.is_complete());
provider1.Resolve(42);
EXPECT_EQ(dispatcher.RunInTaskUntilStalled(future), ::pw::async2::Pending());
EXPECT_EQ(output, 42);
provider2.Resolve();
EXPECT_EQ(dispatcher.RunInTaskUntilStalled(future), Ready());
EXPECT_FALSE(future.is_pendable());
EXPECT_TRUE(future.is_complete());
}
TEST(FuturePipe, ThenVoidToVoid) {
DispatcherForTest dispatcher;
ValueProvider<void> provider1;
ValueProvider<void> provider2;
bool executed = false;
auto future = provider1.Get() | Then([&]() {
executed = true;
return provider2.Get();
});
static_assert(std::is_same_v<typename decltype(future)::value_type, void>);
EXPECT_TRUE(future.is_pendable());
EXPECT_FALSE(future.is_complete());
provider1.Resolve();
EXPECT_EQ(dispatcher.RunInTaskUntilStalled(future), ::pw::async2::Pending());
EXPECT_TRUE(executed);
provider2.Resolve();
EXPECT_EQ(dispatcher.RunInTaskUntilStalled(future), Ready());
EXPECT_FALSE(future.is_pendable());
EXPECT_TRUE(future.is_complete());
}
TEST(FuturePipe, DefaultConstruct) {
auto map_func = [](int x) { return x; };
using MapFutureType =
decltype(std::declval<ValueFuture<int>>() | Map(map_func));
MapFutureType map_future;
EXPECT_FALSE(map_future.is_pendable());
EXPECT_FALSE(map_future.is_complete());
auto then_func = [](int) { return ValueFuture<int>(); };
using ThenFutureType =
decltype(std::declval<ValueFuture<int>>() | Then(then_func));
ThenFutureType then_future;
EXPECT_FALSE(then_future.is_pendable());
EXPECT_FALSE(then_future.is_complete());
}
} // namespace