blob: 26affc47071056cd68b4c10c8d7c4b8739b5dcc5 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/developer/forensics/utils/fidl/oneshot_ptr.h"
#include <fuchsia/update/channelcontrol/cpp/fidl.h>
#include <lib/async/cpp/executor.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "src/developer/forensics/testing/stubs/channel_control.h"
#include "src/developer/forensics/testing/unit_test_fixture.h"
#include "src/developer/forensics/utils/errors.h"
#include "src/lib/fxl/strings/string_printf.h"
namespace forensics {
namespace fidl {
namespace {
constexpr char kChannel[] = "my-channel";
constexpr zx::duration kTimeout = zx::sec(1);
// We need to use an actual FIDL interface to test OneShotPtr, so we use
// fuchsia::update::channelcontrol::ChannelControl and stubs::ChannelControl in our test cases.
class OneShotPtrTest : public UnitTestFixture {
public:
OneShotPtrTest() : executor_(dispatcher()), channel_provider_server_() {}
protected:
template <typename V, typename E>
::fpromise::result<V, E> ExecutePromise(::fpromise::promise<V, E> promise) {
::fpromise::result<V, E> out_result;
executor_.schedule_task(std::move(promise).then(
[&](::fpromise::result<V, E>& result) { out_result = std::move(result); }));
RunLoopFor(kTimeout);
return out_result;
}
void SetUpChannelProviderServer(
std::unique_ptr<stubs::ChannelControlBase> channel_provider_server) {
channel_provider_server_ = std::move(channel_provider_server);
if (channel_provider_server_) {
InjectServiceProvider(channel_provider_server_.get());
}
}
private:
async::Executor executor_;
std::unique_ptr<stubs::ChannelControlBase> channel_provider_server_;
};
TEST_F(OneShotPtrTest, Check_Success) {
auto channel_provider = std::make_unique<stubs::ChannelControl>(stubs::ChannelControlBase::Params{
.current = kChannel,
.target = std::nullopt,
});
SetUpChannelProviderServer(std::move(channel_provider));
OneShotPtr<fuchsia::update::channelcontrol::ChannelControl, std::string> channel_ptr(dispatcher(),
services());
channel_ptr->GetCurrent([&](std::string channel) {
if (channel_ptr.IsAlreadyDone()) {
return;
}
channel_ptr.CompleteOk(channel);
});
auto result = ExecutePromise(channel_ptr.WaitForDone());
ASSERT_TRUE(result.is_ok());
EXPECT_EQ(result.value(), kChannel);
}
TEST_F(OneShotPtrTest, Fail_NoServer) {
SetUpChannelProviderServer(nullptr);
OneShotPtr<fuchsia::update::channelcontrol::ChannelControl> channel_ptr(dispatcher(), services());
// Make a call to ensure we connect to the server.
channel_ptr->GetCurrent([&](std::string channel) {});
const auto result = ExecutePromise(channel_ptr.WaitForDone());
EXPECT_TRUE(result.is_error());
}
TEST_F(OneShotPtrTest, Fail_ClosedChannel) {
SetUpChannelProviderServer(std::make_unique<stubs::ChannelControlClosesConnection>());
OneShotPtr<fuchsia::update::channelcontrol::ChannelControl> channel_ptr(dispatcher(), services());
// Make a call to ensure we connect to the server.
channel_ptr->GetCurrent([&](std::string channel) {});
const auto result = ExecutePromise(channel_ptr.WaitForDone());
EXPECT_TRUE(result.is_error());
EXPECT_EQ(result.error(), Error::kConnectionError);
}
TEST_F(OneShotPtrTest, Fail_Timeout) {
SetUpChannelProviderServer(std::make_unique<stubs::ChannelControlNeverReturns>());
OneShotPtr<fuchsia::update::channelcontrol::ChannelControl> channel_ptr(dispatcher(), services());
bool did_timeout = false;
const auto result = ExecutePromise(
channel_ptr.WaitForDone(fit::Timeout(kTimeout, [&did_timeout]() { did_timeout = true; })));
ASSERT_TRUE(result.is_error());
EXPECT_TRUE(did_timeout);
}
TEST_F(OneShotPtrTest, Crash_MultipleUses) {
auto channel_provider = std::make_unique<stubs::ChannelControl>(stubs::ChannelControlBase::Params{
.current = kChannel,
.target = std::nullopt,
});
SetUpChannelProviderServer(std::move(channel_provider));
OneShotPtr<fuchsia::update::channelcontrol::ChannelControl> channel_ptr(dispatcher(), services());
channel_ptr->GetCurrent([&](std::string channel) {});
ASSERT_DEATH({ channel_ptr->GetCurrent([](std::string channel) {}); },
testing::HasSubstr("You've only got one shot to use -> on a OneShotPtr"));
}
} // namespace
} // namespace fidl
} // namespace forensics