blob: d210d7c03117cc18cc0644fbb502737b564ad1c2 [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/feedback_data/archive_accessor_ptr.h"
#include <fuchsia/mem/cpp/fidl.h>
#include <lib/async/cpp/executor.h>
#include <lib/fit/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_data/attachments/types.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/errors.h"
#include "src/developer/forensics/utils/fit/promise.h"
namespace forensics {
namespace feedback_data {
namespace {
// Collects Archive data in snapshot mode; concatenates data in the output string "contents" and
// returns a promise of the done signal. After every Collect(), "." is also appended.
::fit::promise<void, Error> CollectArchiveData(async_dispatcher_t* dispatcher,
std::shared_ptr<sys::ServiceDirectory> services,
fit::Timeout timeout,
std::shared_ptr<std::string> content,
std::optional<size_t> data_budget) {
std::unique_ptr<ArchiveAccessor> inspect = std::make_unique<ArchiveAccessor>(
dispatcher, services, fuchsia::diagnostics::DataType::INSPECT,
fuchsia::diagnostics::StreamMode::SNAPSHOT, data_budget);
// Collect data.
inspect->Collect([content](fuchsia::diagnostics::FormattedContent chunk) {
std::string json;
fsl::StringFromVmo(chunk.json(), &json);
*content.get() += json + ".";
});
// Wait for done signal.
::fit::promise<void, Error> inspect_data = inspect->WaitForDone(std::move(timeout));
return fit::ExtendArgsLifetimeBeyondPromise(/*promise=*/std::move(inspect_data),
/*args=*/std::move(inspect));
}
using testing::IsEmpty;
using testing::UnorderedElementsAreArray;
class ArchiveAccessorTest : public UnitTestFixture {
public:
ArchiveAccessorTest() : executor_(dispatcher()) {}
protected:
void SetUpInspectServer(std::unique_ptr<stubs::DiagnosticsArchiveBase> server) {
inspect_server_ = std::move(server);
if (inspect_server_) {
InjectServiceProvider(inspect_server_.get(), kArchiveAccessorName);
}
}
::fit::result<void, Error> CollectData(std::shared_ptr<std::string> content,
std::optional<size_t> data_budget = {}) {
::fit::result<void, Error> status;
const zx::duration timeout = zx::sec(1);
executor_.schedule_task(
feedback_data::CollectArchiveData(
dispatcher(), services(), fit::Timeout(timeout, /*action=*/[] {}), content, data_budget)
.then([&status](::fit::result<void, Error>& res) { status = std::move(res); }));
RunLoopFor(timeout);
return status;
}
async::Executor executor_;
private:
std::unique_ptr<stubs::DiagnosticsArchiveBase> inspect_server_;
};
TEST_F(ArchiveAccessorTest, TestLimitedDataBudget) {
fuchsia::diagnostics::StreamParameters parameters;
SetUpInspectServer(std::make_unique<stubs::DiagnosticsArchiveCaptureParameters>(&parameters));
auto budget = std::make_optional<size_t>(1024);
CollectData(std::make_shared<std::string>(), budget);
// read parameters
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(), budget.value());
}
TEST_F(ArchiveAccessorTest, TestUnlimitedDataBudget) {
fuchsia::diagnostics::StreamParameters parameters;
SetUpInspectServer(std::make_unique<stubs::DiagnosticsArchiveCaptureParameters>(&parameters));
CollectData(std::make_shared<std::string>(), {});
// read parameters
ASSERT_FALSE(parameters.has_performance_configuration());
}
TEST_F(ArchiveAccessorTest, Succeed_AllInspectData) {
SetUpInspectServer(std::make_unique<stubs::DiagnosticsArchive>(
std::make_unique<stubs::DiagnosticsBatchIterator>(std::vector<std::vector<std::string>>({
{"foo1", "foo2"},
{"bar1"},
{},
}))));
auto content = std::make_shared<std::string>();
auto status = CollectData(content);
ASSERT_TRUE(status.is_ok());
ASSERT_STREQ(content->c_str(), "foo1.foo2.bar1.");
}
TEST_F(ArchiveAccessorTest, Succeed_PartialInspectData) {
SetUpInspectServer(std::make_unique<stubs::DiagnosticsArchive>(
std::make_unique<stubs::DiagnosticsBatchIteratorNeverRespondsAfterOneBatch>(
std::vector<std::string>({"foo1", "foo2"}))));
auto content = std::make_shared<std::string>();
auto status = CollectData(content);
ASSERT_FALSE(status.is_ok());
EXPECT_STREQ(content->c_str(), "foo1.foo2.");
EXPECT_EQ(status.error(), Error::kTimeout);
}
TEST_F(ArchiveAccessorTest, Fail_BatchIteratorReturnsError) {
SetUpInspectServer(std::make_unique<stubs::DiagnosticsArchive>(
std::make_unique<stubs::DiagnosticsBatchIteratorReturnsError>()));
auto content = std::make_shared<std::string>();
auto status = CollectData(content);
ASSERT_FALSE(status.is_ok());
EXPECT_EQ(status.error(), Error::kBadValue);
}
TEST_F(ArchiveAccessorTest, Fail_BatchIteratorNeverResponds) {
SetUpInspectServer(std::make_unique<stubs::DiagnosticsArchive>(
std::make_unique<stubs::DiagnosticsBatchIteratorNeverResponds>()));
auto content = std::make_shared<std::string>();
auto status = CollectData(content);
ASSERT_FALSE(status.is_ok());
EXPECT_EQ(status.error(), Error::kTimeout);
}
TEST_F(ArchiveAccessorTest, Fail_ArchiveClosesIteratorClosesConnection) {
SetUpInspectServer(std::make_unique<stubs::DiagnosticsArchiveClosesIteratorConnection>());
auto content = std::make_shared<std::string>();
auto status = CollectData(content);
ASSERT_FALSE(status.is_ok());
EXPECT_EQ(status.error(), Error::kConnectionError);
}
} // namespace
} // namespace feedback_data
} // namespace forensics