blob: 11570a39974b6fb1b8f739dad0db679160b79e11 [file] [log] [blame]
// Copyright 2018 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/feedback/crashpad_agent/crashpad_agent.h"
#include <fuchsia/feedback/cpp/fidl.h>
#include <fuchsia/mem/cpp/fidl.h>
#include <lib/fit/result.h>
#include <lib/gtest/test_loop_fixture.h>
#include <lib/inspect/cpp/hierarchy.h>
#include <lib/inspect/cpp/inspect.h>
#include <lib/inspect/cpp/reader.h>
#include <lib/sys/cpp/testing/service_directory_provider.h>
#include <lib/timekeeper/test_clock.h>
#include <lib/zx/time.h>
#include <zircon/errors.h>
#include <zircon/types.h>
#include <algorithm>
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "sdk/lib/inspect/testing/cpp/inspect.h"
#include "src/developer/feedback/crashpad_agent/config.h"
#include "src/developer/feedback/crashpad_agent/constants.h"
#include "src/developer/feedback/crashpad_agent/settings.h"
#include "src/developer/feedback/crashpad_agent/tests/stub_crash_server.h"
#include "src/developer/feedback/crashpad_agent/tests/stub_feedback_data_provider.h"
#include "src/lib/files/directory.h"
#include "src/lib/files/file.h"
#include "src/lib/files/path.h"
#include "src/lib/fsl/vmo/strings.h"
#include "src/lib/fxl/logging.h"
#include "src/lib/fxl/test/test_settings.h"
#include "src/lib/syslog/cpp/logger.h"
#include "third_party/googletest/googlemock/include/gmock/gmock.h"
#include "third_party/googletest/googletest/include/gtest/gtest.h"
namespace feedback {
namespace {
using fuchsia::feedback::Annotation;
using fuchsia::feedback::Attachment;
using fuchsia::feedback::CrashReport;
using fuchsia::feedback::GenericCrashReport;
using fuchsia::feedback::NativeCrashReport;
using fuchsia::feedback::RuntimeCrashReport;
using fuchsia::feedback::SpecificCrashReport;
using inspect::testing::ChildrenMatch;
using inspect::testing::NameMatches;
using inspect::testing::NodeMatches;
using inspect::testing::PropertyList;
using inspect::testing::StringIs;
using inspect::testing::UintIs;
using testing::Contains;
using testing::ElementsAre;
using testing::IsEmpty;
using testing::Not;
using testing::UnorderedElementsAreArray;
// We keep the local Crashpad database size under a certain value. As we want to check the produced
// attachments in the database, we should set the size to be at least the total size for a single
// report so that it does not get cleaned up before we are able to inspect its attachments. For now,
// a single report should take up to 1MB.
constexpr uint64_t kMaxTotalReportSizeInKb = 1024u;
constexpr bool kUploadSuccessful = true;
constexpr bool kUploadFailed = false;
constexpr char kCrashpadDatabasePath[] = "/tmp/crashes";
// "attachments" should be kept in sync with the value defined in
// //crashpad/client/crash_report_database_generic.cc
constexpr char kCrashpadAttachmentsDir[] = "attachments";
constexpr char kProgramName[] = "crashing_program";
constexpr char kSingleAttachmentKey[] = "attachment.key";
constexpr char kSingleAttachmentValue[] = "attachment.value";
Attachment BuildAttachment(const std::string& key, const std::string& value) {
Attachment attachment;
attachment.key = key;
FXL_CHECK(fsl::VmoFromString(value, &attachment.value));
return attachment;
}
// Unit-tests the implementation of the fuchsia.feedback.CrashReporter FIDL interface.
//
// This does not test the environment service. It directly instantiates the class, without
// connecting through FIDL.
class CrashpadAgentTest : public gtest::TestLoopFixture {
void TearDown() override {
ASSERT_TRUE(files::DeletePath(kCrashpadDatabasePath, /*recursive=*/true));
}
protected:
// Sets up the underlying agent using the given |config| and |crash_server|.
void SetUpAgent(Config config, std::unique_ptr<StubCrashServer> crash_server) {
FXL_CHECK((config.crash_server.url && crash_server) ||
(!config.crash_server.url && !crash_server));
crash_server_ = crash_server.get();
attachments_dir_ = files::JoinPath(kCrashpadDatabasePath, kCrashpadAttachmentsDir);
inspector_ = std::make_unique<inspect::Inspector>();
clock_ = std::make_unique<timekeeper::TestClock>();
inspect_manager_ = std::make_unique<InspectManager>(&inspector_->GetRoot(), clock_.get());
agent_ = CrashpadAgent::TryCreate(dispatcher(), service_directory_provider_.service_directory(),
std::move(config), std::move(crash_server),
inspect_manager_.get());
FXL_CHECK(agent_);
}
// Sets up the underlying agent using the given |config|.
void SetUpAgent(Config config) {
FXL_CHECK(!config.crash_server.url);
return SetUpAgent(std::move(config), /*crash_server=*/nullptr);
}
// Sets up the underlying agent using a default config.
void SetUpAgentDefaultConfig(const std::vector<bool>& upload_attempt_results = {}) {
SetUpAgent(
Config{/*crashpad_database=*/
{
/*max_size_in_kb=*/kMaxTotalReportSizeInKb,
},
/*crash_server=*/
{
/*upload_policy=*/CrashServerConfig::UploadPolicy::ENABLED,
/*url=*/std::make_unique<std::string>(kStubCrashServerUrl),
}},
std::make_unique<StubCrashServer>(upload_attempt_results));
}
// Sets up the underlying stub feedback data provider and registers it in the
// |service_directory_provider_|.
void SetUpFeedbackDataProvider(std::unique_ptr<StubFeedbackDataProvider> feedback_data_provider) {
feedback_data_provider_ = std::move(feedback_data_provider);
if (feedback_data_provider_) {
FXL_CHECK(service_directory_provider_.AddService(feedback_data_provider_->GetHandler()) ==
ZX_OK);
}
}
// Checks that in the local Crashpad database there is:
// * only one set of attachments
// * the set of attachment filenames matches the concatenation of
// |expected_extra_attachments| and feedback_data_provider_->attachment_bundle_key()
// * no attachment is empty
void CheckAttachmentsInDatabase(
const std::vector<std::string>& expected_extra_attachment_filenames = {}) {
const std::vector<std::string> subdirs = GetAttachmentSubdirsInDatabase();
// We expect a single crash report to have been generated.
ASSERT_EQ(subdirs.size(), 1u);
// We expect as attachments the ones returned by the feedback::DataProvider and the extra ones
// specific to the crash analysis flow under test.
std::vector<std::string> expected_attachments = expected_extra_attachment_filenames;
if (feedback_data_provider_ && feedback_data_provider_->has_attachment_bundle_key()) {
expected_attachments.push_back(feedback_data_provider_->attachment_bundle_key());
}
std::vector<std::string> attachments;
const std::string report_attachments_dir = files::JoinPath(attachments_dir_, subdirs[0]);
ASSERT_TRUE(files::ReadDirContents(report_attachments_dir, &attachments));
RemoveCurrentDirectory(&attachments);
EXPECT_THAT(attachments, UnorderedElementsAreArray(expected_attachments));
for (const std::string& attachment : attachments) {
uint64_t size;
ASSERT_TRUE(files::GetFileSize(files::JoinPath(report_attachments_dir, attachment), &size));
EXPECT_GT(size, 0u) << "attachment file '" << attachment << "' shouldn't be empty";
}
}
// Checks that on the stub crash server the annotations received match the concatenation of:
// * |expected_extra_annotations|
// * feedback_data_provider_->annotations()
// * default annotations
//
// In case of duplicate keys, the value from |expected_extra_annotations| is picked.
void CheckAnnotationsOnServer(
const std::map<std::string, testing::Matcher<std::string>>& expected_extra_annotations = {}) {
FXL_CHECK(crash_server_);
std::map<std::string, testing::Matcher<std::string>> expected_annotations = {
{"product", "Fuchsia"},
{"version", Not(IsEmpty())},
{"ptype", testing::StartsWith("crashing_program")},
{"osName", "Fuchsia"},
{"osVersion", "0.0.0"},
{"should_process", "false"},
};
if (feedback_data_provider_) {
for (const auto& [key, value] : feedback_data_provider_->annotations()) {
expected_annotations[key] = value;
}
}
for (const auto& [key, value] : expected_extra_annotations) {
expected_annotations[key] = value;
}
EXPECT_EQ(crash_server_->latest_annotations().size(), expected_annotations.size());
for (const auto& [key, value] : expected_annotations) {
EXPECT_THAT(crash_server_->latest_annotations(),
testing::Contains(testing::Pair(key, value)));
}
}
// Checks that on the stub crash server the keys for the attachments received match the
// concatenation of:
// * |expected_extra_attachment_keys|
// * feedback_data_provider_->attachment_bundle_key()
void CheckAttachmentsOnServer(
const std::vector<std::string>& expected_extra_attachment_keys = {}) {
FXL_CHECK(crash_server_);
std::vector<std::string> expected_attachment_keys = expected_extra_attachment_keys;
if (feedback_data_provider_ && feedback_data_provider_->has_attachment_bundle_key()) {
expected_attachment_keys.push_back(feedback_data_provider_->attachment_bundle_key());
}
EXPECT_EQ(crash_server_->latest_attachment_keys().size(), expected_attachment_keys.size());
for (const auto& key : expected_attachment_keys) {
EXPECT_THAT(crash_server_->latest_attachment_keys(), testing::Contains(key));
}
}
// Files one crash report.
fit::result<void, zx_status_t> FileOneCrashReport(CrashReport report) {
FXL_CHECK(agent_ != nullptr) << "agent_ is nullptr. Call SetUpAgent() or one of its variants "
"at the beginning of a test case.";
fit::result<void, zx_status_t> out_result;
agent_->File(std::move(report), [&out_result](fit::result<void, zx_status_t> result) {
out_result = std::move(result);
});
FXL_CHECK(RunLoopUntilIdle());
return out_result;
}
// Files one crash report.
fit::result<void, zx_status_t> FileOneCrashReport(const std::vector<Annotation>& annotations = {},
std::vector<Attachment> attachments = {}) {
CrashReport report;
report.set_program_name(kProgramName);
if (!annotations.empty()) {
report.set_annotations(annotations);
}
if (!attachments.empty()) {
report.set_attachments(std::move(attachments));
}
return FileOneCrashReport(std::move(report));
}
// Files one crash report.
//
// |attachment| is useful to control the lower bound of the size of the report by controlling the
// size of some of the attachment(s). This comes in handy when testing the database size limit
// enforcement logic for instance.
fit::result<void, zx_status_t> FileOneCrashReportWithSingleAttachment(
const std::string& attachment = kSingleAttachmentValue) {
std::vector<Attachment> attachments;
attachments.emplace_back(BuildAttachment(kSingleAttachmentKey, attachment));
return FileOneCrashReport(/*annotations=*/{},
/*attachments=*/std::move(attachments));
}
// Files one generic crash report.
fit::result<void, zx_status_t> FileOneGenericCrashReport(
const std::optional<std::string>& crash_signature) {
GenericCrashReport generic_report;
if (crash_signature.has_value()) {
generic_report.set_crash_signature(crash_signature.value());
}
SpecificCrashReport specific_report;
specific_report.set_generic(std::move(generic_report));
CrashReport report;
report.set_program_name("crashing_program_generic");
report.set_specific_report(std::move(specific_report));
return FileOneCrashReport(std::move(report));
}
// Files one native crash report.
fit::result<void, zx_status_t> FileOneNativeCrashReport(
std::optional<fuchsia::mem::Buffer> minidump) {
NativeCrashReport native_report;
if (minidump.has_value()) {
native_report.set_minidump(std::move(minidump.value()));
}
SpecificCrashReport specific_report;
specific_report.set_native(std::move(native_report));
CrashReport report;
report.set_program_name("crashing_program_native");
report.set_specific_report(std::move(specific_report));
return FileOneCrashReport(std::move(report));
}
// Files one Dart crash report.
fit::result<void, zx_status_t> FileOneDartCrashReport(
const std::optional<std::string>& exception_type,
const std::optional<std::string>& exception_message,
std::optional<fuchsia::mem::Buffer> exception_stack_trace) {
RuntimeCrashReport dart_report;
if (exception_type.has_value()) {
dart_report.set_exception_type(exception_type.value());
}
if (exception_message.has_value()) {
dart_report.set_exception_message(exception_message.value());
}
if (exception_stack_trace.has_value()) {
dart_report.set_exception_stack_trace(std::move(exception_stack_trace.value()));
}
SpecificCrashReport specific_report;
specific_report.set_dart(std::move(dart_report));
CrashReport report;
report.set_program_name("crashing_program_dart");
report.set_specific_report(std::move(specific_report));
return FileOneCrashReport(std::move(report));
}
inspect::Hierarchy InspectTree() {
auto result = inspect::ReadFromVmo(inspector_->DuplicateVmo());
FXL_CHECK(result.is_ok());
return result.take_value();
}
uint64_t total_num_feedback_data_provider_bindings() {
if (!feedback_data_provider_) {
return 0u;
}
return feedback_data_provider_->total_num_bindings();
}
size_t current_num_feedback_data_provider_bindings() {
if (!feedback_data_provider_) {
return 0u;
}
return feedback_data_provider_->current_num_bindings();
}
private:
// Returns all the attachment subdirectories under the over-arching attachment directory in the
// database.
//
// Each subdirectory corresponds to one local crash report.
std::vector<std::string> GetAttachmentSubdirsInDatabase() {
std::vector<std::string> subdirs;
FXL_CHECK(files::ReadDirContents(attachments_dir_, &subdirs));
RemoveCurrentDirectory(&subdirs);
return subdirs;
}
void RemoveCurrentDirectory(std::vector<std::string>* dirs) {
dirs->erase(std::remove(dirs->begin(), dirs->end(), "."), dirs->end());
}
protected:
std::unique_ptr<CrashpadAgent> agent_;
private:
sys::testing::ServiceDirectoryProvider service_directory_provider_;
std::unique_ptr<StubFeedbackDataProvider> feedback_data_provider_;
StubCrashServer* crash_server_;
std::string attachments_dir_;
std::unique_ptr<inspect::Inspector> inspector_;
std::unique_ptr<timekeeper::TestClock> clock_;
std::unique_ptr<InspectManager> inspect_manager_;
};
TEST_F(CrashpadAgentTest, Succeed_OnInputCrashReport) {
SetUpAgentDefaultConfig({kUploadSuccessful});
SetUpFeedbackDataProvider(std::make_unique<StubFeedbackDataProvider>());
ASSERT_TRUE(FileOneCrashReport().is_ok());
CheckAttachmentsInDatabase();
CheckAnnotationsOnServer();
CheckAttachmentsOnServer();
}
TEST_F(CrashpadAgentTest, Succeed_OnInputCrashReportWithAdditionalData) {
SetUpAgentDefaultConfig({kUploadSuccessful});
SetUpFeedbackDataProvider(std::make_unique<StubFeedbackDataProvider>());
std::vector<Attachment> attachments;
attachments.emplace_back(BuildAttachment(kSingleAttachmentKey, kSingleAttachmentValue));
ASSERT_TRUE(FileOneCrashReport(
/*annotations=*/
{
{"annotation.key", "annotation.value"},
},
/*attachments=*/std::move(attachments))
.is_ok());
CheckAttachmentsInDatabase({kSingleAttachmentKey});
CheckAnnotationsOnServer({
{"annotation.key", "annotation.value"},
});
CheckAttachmentsOnServer({kSingleAttachmentKey});
}
TEST_F(CrashpadAgentTest, Succeed_OnInputCrashReportWithEventId) {
SetUpAgentDefaultConfig({kUploadSuccessful});
SetUpFeedbackDataProvider(std::make_unique<StubFeedbackDataProvider>());
CrashReport report;
report.set_program_name(kProgramName);
report.set_event_id("some-event-id");
ASSERT_TRUE(FileOneCrashReport(std::move(report)).is_ok());
CheckAttachmentsInDatabase();
CheckAnnotationsOnServer({
{"comments", "some-event-id"},
});
CheckAttachmentsOnServer();
}
TEST_F(CrashpadAgentTest, Succeed_OnGenericInputCrashReport) {
SetUpAgentDefaultConfig({kUploadSuccessful});
SetUpFeedbackDataProvider(std::make_unique<StubFeedbackDataProvider>());
ASSERT_TRUE(FileOneGenericCrashReport(std::nullopt).is_ok());
CheckAttachmentsInDatabase();
CheckAnnotationsOnServer();
CheckAttachmentsOnServer();
}
TEST_F(CrashpadAgentTest, Succeed_OnGenericInputCrashReportWithSignature) {
SetUpAgentDefaultConfig({kUploadSuccessful});
SetUpFeedbackDataProvider(std::make_unique<StubFeedbackDataProvider>());
ASSERT_TRUE(FileOneGenericCrashReport("some-signature").is_ok());
CheckAttachmentsInDatabase();
CheckAnnotationsOnServer({
{"signature", "some-signature"},
});
CheckAttachmentsOnServer();
}
TEST_F(CrashpadAgentTest, Succeed_OnNativeInputCrashReport) {
SetUpAgentDefaultConfig({kUploadSuccessful});
SetUpFeedbackDataProvider(std::make_unique<StubFeedbackDataProvider>());
fuchsia::mem::Buffer minidump;
fsl::VmoFromString("minidump", &minidump);
ASSERT_TRUE(FileOneNativeCrashReport(std::move(minidump)).is_ok());
CheckAttachmentsInDatabase();
CheckAnnotationsOnServer({
{"should_process", "true"},
});
CheckAttachmentsOnServer({"uploadFileMinidump"});
}
TEST_F(CrashpadAgentTest, Succeed_OnNativeInputCrashReportWithoutMinidump) {
SetUpAgentDefaultConfig({kUploadSuccessful});
SetUpFeedbackDataProvider(std::make_unique<StubFeedbackDataProvider>());
ASSERT_TRUE(FileOneNativeCrashReport(std::nullopt).is_ok());
CheckAttachmentsInDatabase();
CheckAnnotationsOnServer({
{"signature", "fuchsia-no-minidump"},
});
CheckAttachmentsOnServer();
}
TEST_F(CrashpadAgentTest, Succeed_OnDartInputCrashReport) {
SetUpAgentDefaultConfig({kUploadSuccessful});
SetUpFeedbackDataProvider(std::make_unique<StubFeedbackDataProvider>());
fuchsia::mem::Buffer stack_trace;
fsl::VmoFromString("#0", &stack_trace);
ASSERT_TRUE(
FileOneDartCrashReport("FileSystemException", "cannot open file", std::move(stack_trace))
.is_ok());
CheckAttachmentsInDatabase({"DartError"});
CheckAnnotationsOnServer({
{"error_runtime_type", "FileSystemException"},
{"error_message", "cannot open file"},
{"type", "DartError"},
{"should_process", "true"},
});
CheckAttachmentsOnServer({"DartError"});
}
TEST_F(CrashpadAgentTest, Succeed_OnDartInputCrashReportWithoutExceptionData) {
SetUpAgentDefaultConfig({kUploadSuccessful});
SetUpFeedbackDataProvider(std::make_unique<StubFeedbackDataProvider>());
ASSERT_TRUE(FileOneDartCrashReport(std::nullopt, std::nullopt, std::nullopt).is_ok());
CheckAttachmentsInDatabase();
CheckAnnotationsOnServer({
{"type", "DartError"},
{"signature", "fuchsia-no-dart-stack-trace"},
});
CheckAttachmentsOnServer();
}
TEST_F(CrashpadAgentTest, Fail_OnInvalidInputCrashReport) {
SetUpAgentDefaultConfig();
CrashReport report;
fit::result<void, zx_status_t> out_result;
agent_->File(std::move(report), [&out_result](fit::result<void, zx_status_t> result) {
out_result = std::move(result);
});
ASSERT_TRUE(out_result.is_error());
}
TEST_F(CrashpadAgentTest, Succeed_OnConcurrentReports) {
SetUpAgentDefaultConfig(std::vector<bool>(10, kUploadSuccessful));
// We generate ten crash reports before runnning the loop to make sure that one crash
// report filing doesn't clean up the concurrent crash reports being filed.
const size_t kNumReports = 10;
std::vector<fit::result<void, zx_status_t>> results;
for (size_t i = 0; i < kNumReports; ++i) {
CrashReport report;
report.set_program_name(kProgramName);
agent_->File(std::move(report),
[&results](fit::result<void, zx_status_t> result) { results.push_back(result); });
}
ASSERT_TRUE(RunLoopUntilIdle());
EXPECT_EQ(results.size(), kNumReports);
for (const auto result : results) {
EXPECT_TRUE(result.is_ok());
}
}
TEST_F(CrashpadAgentTest, Succeed_OnFailedUpload) {
SetUpAgent(
Config{/*crashpad_database=*/
{
/*max_size_in_kb=*/kMaxTotalReportSizeInKb,
},
/*crash_server=*/
{
/*upload_policy=*/CrashServerConfig::UploadPolicy::ENABLED,
/*url=*/std::make_unique<std::string>(kStubCrashServerUrl),
}},
std::make_unique<StubCrashServer>(std::vector<bool>({kUploadFailed})));
EXPECT_TRUE(FileOneCrashReport().is_ok());
}
TEST_F(CrashpadAgentTest, Succeed_OnDisabledUpload) {
SetUpFeedbackDataProvider(std::make_unique<StubFeedbackDataProvider>());
SetUpAgent(Config{/*crashpad_database=*/
{
/*max_size_in_kb=*/kMaxTotalReportSizeInKb,
},
/*crash_server=*/
{
/*upload_policy=*/CrashServerConfig::UploadPolicy::DISABLED,
/*url=*/nullptr,
}});
EXPECT_TRUE(FileOneCrashReport().is_ok());
}
TEST_F(CrashpadAgentTest, Succeed_OnNoFeedbackAttachments) {
SetUpAgentDefaultConfig({kUploadSuccessful});
SetUpFeedbackDataProvider(std::make_unique<StubFeedbackDataProviderReturnsNoAttachment>());
EXPECT_TRUE(FileOneCrashReportWithSingleAttachment().is_ok());
CheckAttachmentsInDatabase({kSingleAttachmentKey});
CheckAnnotationsOnServer();
CheckAttachmentsOnServer({kSingleAttachmentKey});
}
TEST_F(CrashpadAgentTest, Succeed_OnNoFeedbackAnnotations) {
SetUpAgentDefaultConfig({kUploadSuccessful});
SetUpFeedbackDataProvider(std::make_unique<StubFeedbackDataProviderReturnsNoAnnotation>());
EXPECT_TRUE(FileOneCrashReportWithSingleAttachment().is_ok());
CheckAttachmentsInDatabase({kSingleAttachmentKey});
CheckAnnotationsOnServer();
CheckAttachmentsOnServer({kSingleAttachmentKey});
}
TEST_F(CrashpadAgentTest, Succeed_OnNoFeedbackData) {
SetUpAgentDefaultConfig({kUploadSuccessful});
SetUpFeedbackDataProvider(std::make_unique<StubFeedbackDataProviderReturnsNoData>());
EXPECT_TRUE(FileOneCrashReportWithSingleAttachment().is_ok());
CheckAttachmentsInDatabase({kSingleAttachmentKey});
CheckAnnotationsOnServer();
CheckAttachmentsOnServer({kSingleAttachmentKey});
}
TEST_F(CrashpadAgentTest, Succeed_OnNoFeedbackDataProvider) {
SetUpAgentDefaultConfig({kUploadSuccessful});
// We pass a nullptr stub so there will be no fuchsia.feedback.DataProvider service to connect to.
SetUpFeedbackDataProvider(nullptr);
EXPECT_TRUE(FileOneCrashReportWithSingleAttachment().is_ok());
CheckAttachmentsInDatabase({kSingleAttachmentKey});
CheckAnnotationsOnServer();
CheckAttachmentsOnServer({kSingleAttachmentKey});
}
TEST_F(CrashpadAgentTest, Succeed_OnFeedbackDataProviderTakingTooLong) {
SetUpAgentDefaultConfig({kUploadSuccessful});
SetUpFeedbackDataProvider(std::make_unique<StubFeedbackDataProviderNeverReturning>());
fit::result<void, zx_status_t> result = FileOneCrashReportWithSingleAttachment();
RunLoopFor(zx::sec(10) + zx::sec(1));
EXPECT_TRUE(result.is_ok());
CheckAttachmentsInDatabase({kSingleAttachmentKey});
CheckAnnotationsOnServer();
CheckAttachmentsOnServer({kSingleAttachmentKey});
}
TEST_F(CrashpadAgentTest, Check_OneFeedbackDataProviderConnectionPerAnalysis) {
const size_t num_calls = 5u;
SetUpAgentDefaultConfig(std::vector<bool>(num_calls, true));
// We use a stub that returns no data as we are not interested in the payload, just the number of
// different connections to the stub.
SetUpFeedbackDataProvider(std::make_unique<StubFeedbackDataProviderReturnsNoData>());
for (size_t i = 0; i < num_calls; i++) {
FileOneCrashReportWithSingleAttachment();
}
EXPECT_EQ(total_num_feedback_data_provider_bindings(), num_calls);
EXPECT_EQ(current_num_feedback_data_provider_bindings(), 0u);
}
TEST_F(CrashpadAgentTest, Check_InitialInspectTree) {
SetUpAgentDefaultConfig();
EXPECT_THAT(
InspectTree(),
ChildrenMatch(UnorderedElementsAre(
AllOf(NodeMatches(NameMatches(kInspectConfigName)),
ChildrenMatch(UnorderedElementsAreArray({
NodeMatches(
AllOf(NameMatches(kCrashpadDatabaseKey),
PropertyList(UnorderedElementsAreArray({
UintIs(kCrashpadDatabaseMaxSizeInKbKey, kMaxTotalReportSizeInKb),
})))),
NodeMatches(
AllOf(NameMatches(kCrashServerKey),
PropertyList(UnorderedElementsAreArray({
StringIs(kCrashServerUploadPolicyKey,
ToString(CrashServerConfig::UploadPolicy::ENABLED)),
StringIs(kCrashServerUrlKey, kStubCrashServerUrl),
})))),
}))),
NodeMatches(AllOf(NameMatches(kInspectSettingsName),
PropertyList(ElementsAre(StringIs(
"upload_policy", ToString(Settings::UploadPolicy::ENABLED)))))),
NodeMatches(NameMatches(kInspectReportsName)))));
}
TEST_F(CrashpadAgentTest, Check_InspectTreeAfterSuccessfulUpload) {
SetUpAgentDefaultConfig({kUploadSuccessful});
EXPECT_TRUE(FileOneCrashReport().is_ok());
EXPECT_THAT(
InspectTree(),
ChildrenMatch(Contains(AllOf(
NodeMatches(NameMatches(kInspectReportsName)),
ChildrenMatch(ElementsAre(AllOf(
NodeMatches(NameMatches(kProgramName)),
ChildrenMatch(ElementsAre(AllOf(
NodeMatches(PropertyList(ElementsAre(StringIs("creation_time", Not(IsEmpty()))))),
ChildrenMatch(ElementsAre(NodeMatches(AllOf(
NameMatches("crash_server"), PropertyList(UnorderedElementsAreArray({
StringIs("creation_time", Not(IsEmpty())),
StringIs("id", kStubServerReportId),
}))))))))))))))));
}
} // namespace
} // namespace feedback
int main(int argc, char** argv) {
if (!fxl::SetTestSettings(argc, argv)) {
return EXIT_FAILURE;
}
testing::InitGoogleTest(&argc, argv);
syslog::InitLogger({"feedback", "test"});
return RUN_ALL_TESTS();
}