blob: fbfa887fab6325fba94bdec246a06f589b414f0b [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/attachments/inspect.h"
#include <fuchsia/mem/cpp/fidl.h>
#include <lib/async/cpp/executor.h>
#include <lib/fpromise/result.h>
#include <lib/sys/cpp/service_directory.h>
#include <lib/zx/time.h>
#include <memory>
#include <string>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "src/developer/forensics/feedback/attachments/types.h"
#include "src/developer/forensics/feedback_data/constants.h"
#include "src/developer/forensics/testing/gpretty_printers.h"
#include "src/developer/forensics/testing/stubs/diagnostics_archive.h"
#include "src/developer/forensics/testing/stubs/diagnostics_batch_iterator.h"
#include "src/developer/forensics/testing/unit_test_fixture.h"
#include "src/developer/forensics/utils/cobalt/logger.h"
#include "src/developer/forensics/utils/errors.h"
#include "src/lib/timekeeper/async_test_clock.h"
namespace forensics::feedback {
namespace {
class MonotonicBackoff : public backoff::Backoff {
public:
static std::unique_ptr<backoff::Backoff> Make() { return std::make_unique<MonotonicBackoff>(); }
zx::duration GetNext() override { return zx::sec(delay_++); }
void Reset() override {}
private:
size_t delay_{1u};
};
class InspectTest : public UnitTestFixture {
public:
InspectTest()
: executor_(dispatcher()),
clock_(dispatcher()),
cobalt_(dispatcher(), services(), &clock_),
inspect_node_manager_(&InspectRoot()),
inspect_data_budget_(std::make_unique<feedback_data::InspectDataBudget>(
true, &inspect_node_manager_, &cobalt_)) {}
protected:
void SetUpInspectServer(std::unique_ptr<stubs::DiagnosticsArchiveBase> server) {
inspect_server_ = std::move(server);
if (inspect_server_) {
InjectServiceProvider(inspect_server_.get(), feedback_data::kArchiveAccessorName);
}
}
void DisableDataBudget() {
inspect_data_budget_ =
std::make_unique<feedback_data::InspectDataBudget>(false, &inspect_node_manager_, &cobalt_);
}
AttachmentValue Run(::fpromise::promise<AttachmentValue> promise,
const std::optional<zx::duration> run_loop_for = std::nullopt) {
AttachmentValue attachment(Error::kLogicError);
executor_.schedule_task(
promise.and_then([&attachment](AttachmentValue& result) { attachment = std::move(result); })
.or_else([]() { FX_LOGS(FATAL) << "Unexpected branch"; }));
if (run_loop_for.has_value()) {
RunLoopFor(*run_loop_for);
} else {
RunLoopUntilIdle();
}
return attachment;
}
feedback_data::InspectDataBudget* DataBudget() { return inspect_data_budget_.get(); }
private:
async::Executor executor_;
timekeeper::AsyncTestClock clock_;
cobalt::Logger cobalt_;
InspectNodeManager inspect_node_manager_;
std::unique_ptr<feedback_data::InspectDataBudget> inspect_data_budget_;
std::unique_ptr<stubs::DiagnosticsArchiveBase> inspect_server_;
};
TEST_F(InspectTest, DataBudget) {
fuchsia::diagnostics::StreamParameters parameters;
SetUpInspectServer(std::make_unique<stubs::DiagnosticsArchiveCaptureParameters>(&parameters));
const size_t kBudget = DataBudget()->SizeInBytes().value();
Inspect inspect(dispatcher(), services(), MonotonicBackoff::Make(), DataBudget());
inspect.Get(zx::duration::infinite());
RunLoopUntilIdle();
ASSERT_TRUE(parameters.has_performance_configuration());
const auto& performance = parameters.performance_configuration();
ASSERT_TRUE(performance.has_max_aggregate_content_size_bytes());
ASSERT_EQ(performance.max_aggregate_content_size_bytes(), kBudget);
}
TEST_F(InspectTest, NoDataBudget) {
fuchsia::diagnostics::StreamParameters parameters;
SetUpInspectServer(std::make_unique<stubs::DiagnosticsArchiveCaptureParameters>(&parameters));
DisableDataBudget();
Inspect inspect(dispatcher(), services(), MonotonicBackoff::Make(), DataBudget());
inspect.Get(zx::duration::infinite());
RunLoopUntilIdle();
EXPECT_FALSE(parameters.has_performance_configuration());
}
TEST_F(InspectTest, Get) {
SetUpInspectServer(std::make_unique<stubs::DiagnosticsArchive>(
std::make_unique<stubs::DiagnosticsBatchIterator>(std::vector<std::vector<std::string>>({
{"foo1", "foo2"},
{"bar1"},
{},
}))));
Inspect inspect(dispatcher(), services(), MonotonicBackoff::Make(), DataBudget());
const auto attachment = Run(inspect.Get(zx::duration::infinite()));
EXPECT_FALSE(attachment.HasError());
ASSERT_TRUE(attachment.HasValue());
EXPECT_EQ(attachment.Value(), R"([
foo1,
foo2,
bar1
])");
}
TEST_F(InspectTest, GetTimeout) {
SetUpInspectServer(std::make_unique<stubs::DiagnosticsArchive>(
std::make_unique<stubs::DiagnosticsBatchIteratorNeverRespondsAfterOneBatch>(
std::vector<std::string>({"foo1", "foo2"}))));
Inspect inspect(dispatcher(), services(), MonotonicBackoff::Make(), DataBudget());
const auto attachment = Run(inspect.Get(zx::sec(10)), zx::sec(10));
ASSERT_TRUE(attachment.HasError());
EXPECT_EQ(attachment.Error(), Error::kTimeout);
ASSERT_TRUE(attachment.HasValue());
EXPECT_EQ(attachment.Value(), R"([
foo1,
foo2
])");
}
TEST_F(InspectTest, GetConnectionError) {
SetUpInspectServer(std::make_unique<stubs::DiagnosticsArchiveClosesIteratorConnection>());
Inspect inspect(dispatcher(), services(), MonotonicBackoff::Make(), DataBudget());
const auto attachment = Run(inspect.Get(zx::duration::infinite()));
ASSERT_TRUE(attachment.HasError());
EXPECT_EQ(attachment.Error(), Error::kConnectionError);
EXPECT_FALSE(attachment.HasValue());
}
TEST_F(InspectTest, GetIteratorReturnsError) {
SetUpInspectServer(std::make_unique<stubs::DiagnosticsArchive>(
std::make_unique<stubs::DiagnosticsBatchIteratorReturnsError>()));
Inspect inspect(dispatcher(), services(), MonotonicBackoff::Make(), DataBudget());
const auto attachment = Run(inspect.Get(zx::duration::infinite()));
ASSERT_TRUE(attachment.HasError());
EXPECT_EQ(attachment.Error(), Error::kMissingValue);
EXPECT_FALSE(attachment.HasValue());
}
TEST_F(InspectTest, Reconnects) {
fuchsia::diagnostics::StreamParameters parameters;
auto archive = std::make_unique<stubs::DiagnosticsArchiveCaptureParameters>(&parameters);
InjectServiceProvider(archive.get(), feedback_data::kArchiveAccessorName);
Inspect inspect(dispatcher(), services(), MonotonicBackoff::Make(), DataBudget());
RunLoopUntilIdle();
EXPECT_TRUE(archive->IsBound());
archive->CloseConnection();
RunLoopUntilIdle();
EXPECT_FALSE(archive->IsBound());
RunLoopFor(zx::sec(1));
EXPECT_TRUE(archive->IsBound());
}
} // namespace
} // namespace forensics::feedback