blob: 40f740c421d5a2ff194fae0f2fb81e61205905ab [file] [log] [blame]
// Copyright 2022 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/feedback/annotations/fidl_provider.h"
#include <fuchsia/feedback/cpp/fidl.h>
#include <fuchsia/update/channelcontrol/cpp/fidl.h>
#include <lib/zx/time.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "src/developer/forensics/feedback/annotations/fidl_provider.h"
#include "src/developer/forensics/feedback/annotations/types.h"
#include "src/developer/forensics/testing/stubs/channel_control.h"
#include "src/developer/forensics/testing/stubs/device_id_provider.h"
#include "src/developer/forensics/testing/unit_test_fixture.h"
#include "src/lib/backoff/backoff.h"
namespace forensics::feedback {
namespace {
using ::testing::IsEmpty;
using ::testing::Pair;
using ::testing::UnorderedElementsAreArray;
constexpr char kChannelKey[] = "current_channel";
constexpr char kChannelValue[] = "channel";
class MonotonicBackoff : public backoff::Backoff {
public:
zx::duration GetNext() override {
const auto backoff = backoff_;
backoff_ = backoff + zx::sec(1);
return backoff;
}
void Reset() override { backoff_ = zx::sec(1); }
private:
zx::duration backoff_{zx::sec(1)};
};
using StaticSingleFidlMethodAnnotationProviderTest = UnitTestFixture;
struct ConvertChannel {
Annotations operator()(const ErrorOr<std::string>& channel) { return {{kChannelKey, channel}}; }
};
class StaticCurrentChannelProvider
: public StaticSingleFidlMethodAnnotationProvider<
fuchsia::update::channelcontrol::ChannelControl,
&fuchsia::update::channelcontrol::ChannelControl::GetCurrent, ConvertChannel> {
public:
using StaticSingleFidlMethodAnnotationProvider::StaticSingleFidlMethodAnnotationProvider;
std::set<std::string> GetKeys() const override { return {kChannelKey}; }
};
TEST_F(StaticSingleFidlMethodAnnotationProviderTest, GetAll) {
StaticCurrentChannelProvider provider(dispatcher(), services(),
std::make_unique<MonotonicBackoff>());
auto channel_server = std::make_unique<stubs::ChannelControl>(stubs::ChannelControlBase::Params{
.current = kChannelValue,
});
InjectServiceProvider(channel_server.get());
RunLoopUntilIdle();
Annotations annotations;
provider.GetOnce([&annotations](Annotations a) { annotations = std::move(a); });
RunLoopUntilIdle();
EXPECT_THAT(annotations, UnorderedElementsAreArray({Pair(kChannelKey, kChannelValue)}));
EXPECT_EQ(channel_server->NumConnections(), 0u);
}
TEST_F(StaticSingleFidlMethodAnnotationProviderTest, Reconnects) {
StaticCurrentChannelProvider provider(dispatcher(), services(),
std::make_unique<MonotonicBackoff>());
auto channel_server = std::make_unique<stubs::ChannelControlClosesFirstConnection>(
stubs::ChannelControlBase::Params{
.current = kChannelValue,
});
InjectServiceProvider(channel_server.get());
RunLoopUntilIdle();
ASSERT_EQ(channel_server->NumConnections(), 0u);
Annotations annotations;
provider.GetOnce([&annotations](Annotations a) { annotations = std::move(a); });
RunLoopUntilIdle();
EXPECT_THAT(annotations, IsEmpty());
RunLoopFor(zx::sec(1));
EXPECT_THAT(annotations, UnorderedElementsAreArray({Pair(kChannelKey, kChannelValue)}));
EXPECT_EQ(channel_server->NumConnections(), 0u);
}
using DynamicSingleFidlMethodAnnotationProviderTest = UnitTestFixture;
class DynamicCurrentChannelProvider
: public DynamicSingleFidlMethodAnnotationProvider<
fuchsia::update::channelcontrol::ChannelControl,
&fuchsia::update::channelcontrol::ChannelControl::GetCurrent, ConvertChannel> {
public:
using DynamicSingleFidlMethodAnnotationProvider::DynamicSingleFidlMethodAnnotationProvider;
std::set<std::string> GetKeys() const override { return {kChannelKey}; }
};
TEST_F(DynamicSingleFidlMethodAnnotationProviderTest, Get) {
DynamicCurrentChannelProvider provider(dispatcher(), services(),
std::make_unique<MonotonicBackoff>());
auto channel_server = std::make_unique<stubs::ChannelControl>(stubs::ChannelControlBase::Params{
.current = kChannelValue,
});
InjectServiceProvider(channel_server.get());
RunLoopUntilIdle();
Annotations annotations;
provider.Get([&annotations](Annotations a) { annotations = std::move(a); });
RunLoopUntilIdle();
EXPECT_THAT(annotations, UnorderedElementsAreArray({Pair(kChannelKey, kChannelValue)}));
EXPECT_EQ(channel_server->NumConnections(), 1u);
}
TEST_F(DynamicSingleFidlMethodAnnotationProviderTest, Reconnects) {
DynamicCurrentChannelProvider provider(dispatcher(), services(),
std::make_unique<MonotonicBackoff>());
auto channel_server = std::make_unique<stubs::ChannelControl>(stubs::ChannelControlBase::Params{
.current = kChannelValue,
});
InjectServiceProvider(channel_server.get());
RunLoopUntilIdle();
ASSERT_EQ(channel_server->NumConnections(), 1u);
channel_server->CloseAllConnections();
RunLoopUntilIdle();
ASSERT_EQ(channel_server->NumConnections(), 0u);
Annotations annotations;
provider.Get([&annotations](Annotations a) { annotations = std::move(a); });
RunLoopUntilIdle();
EXPECT_THAT(annotations, UnorderedElementsAreArray({Pair(kChannelKey, Error::kConnectionError)}));
RunLoopFor(zx::sec(1));
EXPECT_EQ(channel_server->NumConnections(), 1u);
provider.Get([&annotations](Annotations a) { annotations = std::move(a); });
RunLoopUntilIdle();
EXPECT_THAT(annotations, UnorderedElementsAreArray({Pair(kChannelKey, kChannelValue)}));
channel_server->CloseAllConnections();
RunLoopUntilIdle();
EXPECT_EQ(channel_server->NumConnections(), 0u);
}
constexpr char kDeviceIdKey[] = "current_device_id";
constexpr std::array<const char*, 2> kDeviceIdValues = {
"device_id_1",
"device_id_2",
};
struct ConvertDeviceId {
Annotations operator()(const ErrorOr<std::string>& device_id) {
return {{kDeviceIdKey, device_id}};
}
};
class HangingGetDeviceIdProvider
: public HangingGetSingleFidlMethodAnnotationProvider<
fuchsia::feedback::DeviceIdProvider, &fuchsia::feedback::DeviceIdProvider::GetId,
ConvertDeviceId> {
public:
using HangingGetSingleFidlMethodAnnotationProvider::HangingGetSingleFidlMethodAnnotationProvider;
std::set<std::string> GetKeys() const override { return {kDeviceIdKey}; }
};
class HangingGetSingleFidlMethodAnnotationProviderTest : public UnitTestFixture {
protected:
void SetUpDeviceIdProviderServer(
std::unique_ptr<stubs::DeviceIdProviderBase> device_id_provider_server) {
device_id_provider_server_ = std::move(device_id_provider_server);
if (device_id_provider_server_) {
InjectServiceProvider(device_id_provider_server_.get());
}
}
std::unique_ptr<stubs::DeviceIdProviderBase> device_id_provider_server_;
};
TEST_F(HangingGetSingleFidlMethodAnnotationProviderTest, Get) {
SetUpDeviceIdProviderServer(std::make_unique<stubs::DeviceIdProvider>(kDeviceIdValues[0]));
HangingGetDeviceIdProvider device_id_provider(dispatcher(), services(),
std::make_unique<MonotonicBackoff>());
Annotations annotations;
device_id_provider.GetOnUpdate(
[&annotations](Annotations result) { annotations = std::move(result); });
// |annotations| should be empty because the call hasn't completed.
EXPECT_THAT(annotations, IsEmpty());
RunLoopUntilIdle();
EXPECT_THAT(annotations, UnorderedElementsAreArray({
Pair(kDeviceIdKey, kDeviceIdValues[0]),
}));
device_id_provider_server_->SetDeviceId(kDeviceIdValues[1]);
// |annotations| should the old value because the change hasn't propagated yet.
EXPECT_THAT(annotations, UnorderedElementsAreArray({
Pair(kDeviceIdKey, kDeviceIdValues[0]),
}));
RunLoopUntilIdle();
EXPECT_THAT(annotations, UnorderedElementsAreArray({
Pair(kDeviceIdKey, kDeviceIdValues[1]),
}));
device_id_provider_server_->CloseConnection();
// |annotations| should contain the old value because the disconnection hasn't propagated.
EXPECT_THAT(annotations, UnorderedElementsAreArray({
Pair(kDeviceIdKey, kDeviceIdValues[1]),
}));
}
TEST_F(HangingGetSingleFidlMethodAnnotationProviderTest, Reconnects) {
SetUpDeviceIdProviderServer(std::make_unique<stubs::DeviceIdProviderNeverReturns>());
HangingGetDeviceIdProvider device_id_provider(dispatcher(), services(),
std::make_unique<MonotonicBackoff>());
RunLoopUntilIdle();
ASSERT_TRUE(device_id_provider_server_->IsBound());
Annotations annotations;
device_id_provider.GetOnUpdate(
[&annotations](Annotations result) { annotations = std::move(result); });
device_id_provider_server_->CloseConnection();
ASSERT_FALSE(device_id_provider_server_->IsBound());
RunLoopUntilIdle();
// The outstanding request should complete with a connection error.
EXPECT_THAT(annotations, IsEmpty());
RunLoopFor(zx::sec(1));
ASSERT_TRUE(device_id_provider_server_->IsBound());
}
} // namespace
} // namespace forensics::feedback