blob: ba96e319ffc10c9db24a6bb9901b0c4398372123 [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/crash_reports/crash_reporter.h"
#include <fuchsia/feedback/cpp/fidl.h>
#include <fuchsia/mem/cpp/fidl.h>
#include <fuchsia/settings/cpp/fidl.h>
#include <lib/fpromise/result.h>
#include <lib/syslog/cpp/macros.h>
#include <lib/zx/time.h>
#include <zircon/errors.h>
#include <zircon/types.h>
#include <zircon/utc.h>
#include <algorithm>
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "src/developer/forensics/crash_reports/config.h"
#include "src/developer/forensics/crash_reports/constants.h"
#include "src/developer/forensics/crash_reports/info/info_context.h"
#include "src/developer/forensics/crash_reports/tests/stub_crash_server.h"
#include "src/developer/forensics/testing/fakes/privacy_settings.h"
#include "src/developer/forensics/testing/stubs/channel_control.h"
#include "src/developer/forensics/testing/stubs/cobalt_logger_factory.h"
#include "src/developer/forensics/testing/stubs/data_provider.h"
#include "src/developer/forensics/testing/stubs/device_id_provider.h"
#include "src/developer/forensics/testing/stubs/network_reachability_provider.h"
#include "src/developer/forensics/testing/unit_test_fixture.h"
#include "src/developer/forensics/utils/cobalt/event.h"
#include "src/developer/forensics/utils/cobalt/metrics.h"
#include "src/lib/files/directory.h"
#include "src/lib/files/file.h"
#include "src/lib/files/path.h"
#include "src/lib/files/scoped_temp_dir.h"
#include "src/lib/fsl/vmo/strings.h"
#include "src/lib/timekeeper/test_clock.h"
namespace forensics {
namespace crash_reports {
namespace {
using fuchsia::feedback::Annotation;
using fuchsia::feedback::Attachment;
using fuchsia::feedback::CrashReport;
using fuchsia::feedback::NativeCrashReport;
using fuchsia::feedback::RuntimeCrashReport;
using fuchsia::feedback::SpecificCrashReport;
using fuchsia::settings::PrivacySettings;
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::ByRef;
using testing::Contains;
using testing::ElementsAre;
using testing::IsEmpty;
using testing::IsSupersetOf;
using testing::Not;
using testing::UnorderedElementsAre;
using testing::UnorderedElementsAreArray;
constexpr CrashServer::UploadStatus kUploadSuccessful = CrashServer::UploadStatus::kSuccess;
constexpr CrashServer::UploadStatus kUploadFailed = CrashServer::UploadStatus::kFailure;
constexpr CrashServer::UploadStatus kUploadThrottled = CrashServer::UploadStatus::kThrottled;
constexpr char kProgramName[] = "crashing_program";
constexpr char kBuildVersion[] = "some-version";
constexpr char kDefaultChannel[] = "some-channel";
constexpr char kDefaultDeviceId[] = "some-device-id";
constexpr char kSingleAttachmentKey[] = "attachment.key";
constexpr char kSingleAttachmentValue[] = "attachment.value";
constexpr bool kUserOptInDataSharing = true;
constexpr bool kUserOptOutDataSharing = false;
constexpr size_t kDailyPerProductQuota = 100u;
const std::map<std::string, std::string> kDefaultAnnotations = {
{"feedback.annotation.1.key", "feedback.annotation.1.value"},
{"feedback.annotation.2.key", "feedback.annotation.2.value"},
};
const std::map<std::string, std::string> kEmptyAnnotations = {};
constexpr char kDefaultAttachmentBundleKey[] = "feedback.attachment.bundle.key";
constexpr char kEmptyAttachmentBundleKey[] = "empty.attachment.key";
constexpr zx::duration kSnapshotSharedRequestWindow = zx::sec(5);
Attachment BuildAttachment(const std::string& key, const std::string& value) {
Attachment attachment;
attachment.key = key;
FX_CHECK(fsl::VmoFromString(value, &attachment.value));
return attachment;
}
PrivacySettings MakePrivacySettings(const std::optional<bool> user_data_sharing_consent) {
PrivacySettings settings;
if (user_data_sharing_consent.has_value()) {
settings.set_user_data_sharing_consent(user_data_sharing_consent.value());
}
return settings;
}
template <typename K, typename V>
std::vector<testing::internal::PairMatcher<K, V>> Linearize(const std::map<K, V>& annotations) {
std::vector<testing::internal::PairMatcher<K, V>> linearized;
for (const auto& [k, v] : annotations) {
linearized.push_back(testing::Pair(k, v));
}
return linearized;
}
// 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 CrashReporterTest : public UnitTestFixture {
public:
void SetUp() override {
clock_.Set(zx::time(0u));
info_context_ =
std::make_shared<InfoContext>(&InspectRoot(), &clock_, dispatcher(), services());
crash_register_ = std::make_unique<CrashRegister>(
dispatcher(), services(), info_context_, std::string(kBuildVersion), RegisterJsonPath());
SetUpCobaltServer(std::make_unique<stubs::CobaltLoggerFactory>());
SetUpNetworkReachabilityProviderServer();
RunLoopUntilIdle();
}
void TearDown() override {
ASSERT_TRUE(files::DeletePath(kStoreTmpPath, /*recursive=*/true));
ASSERT_TRUE(files::DeletePath(kStoreCachePath, /*recursive=*/true));
}
protected:
// Sets up the underlying crash reporter using the given |config| and |crash_server|.
void SetUpCrashReporter(
Config config, const std::vector<CrashServer::UploadStatus>& upload_attempt_results = {}) {
FX_CHECK(data_provider_server_);
snapshot_manager_ = std::make_unique<SnapshotManager>(
dispatcher(), &clock_, data_provider_server_.get(), kSnapshotSharedRequestWindow,
kGarbageCollectedSnapshotsPath, StorageSize::Gigabytes(1u), StorageSize::Gigabytes(1u)),
crash_server_ =
std::make_unique<StubCrashServer>(dispatcher(), services(), upload_attempt_results);
crash_reporter_ = std::make_unique<CrashReporter>(
dispatcher(), services(), &clock_, info_context_, config,
AnnotationMap({{"osName", "Fuchsia"}, {"osVersion", kBuildVersion}}), crash_register_.get(),
&tags_, snapshot_manager_.get(), crash_server_.get());
FX_CHECK(crash_reporter_);
}
// Sets up the underlying crash reporter using a default config.
void SetUpCrashReporterDefaultConfig(
const std::vector<CrashServer::UploadStatus>& upload_attempt_results = {}) {
SetUpCrashReporter(
Config{/*crash_server=*/
{
/*upload_policy=*/CrashServerConfig::UploadPolicy::ENABLED,
},
/*daily_per_product_quota=*/kDailyPerProductQuota,
/*houry_snapshot=*/true},
upload_attempt_results);
}
void SetUpChannelProviderServer(std::unique_ptr<stubs::ChannelControlBase> server) {
channel_provider_server_ = std::move(server);
if (channel_provider_server_) {
InjectServiceProvider(channel_provider_server_.get());
}
}
void SetUpDataProviderServer(std::unique_ptr<stubs::DataProviderBase> server) {
data_provider_server_ = std::move(server);
}
void SetUpDeviceIdProviderServer(std::unique_ptr<stubs::DeviceIdProviderBase> server) {
device_id_provider_server_ = std::move(server);
if (device_id_provider_server_) {
InjectServiceProvider(device_id_provider_server_.get());
}
}
void SetUpNetworkReachabilityProviderServer() {
network_reachability_provider_server_ = std::make_unique<stubs::NetworkReachabilityProvider>();
InjectServiceProvider(network_reachability_provider_server_.get());
}
void SetUpPrivacySettingsServer(std::unique_ptr<fakes::PrivacySettings> server) {
privacy_settings_server_ = std::move(server);
if (privacy_settings_server_) {
InjectServiceProvider(privacy_settings_server_.get());
}
}
std::string RegisterJsonPath() { return files::JoinPath(tmp_dir_.path(), "register.json"); }
// Checks that on the crash server the annotations received match the concatenation of:
// * |expected_extra_annotations|
// * default annotations
//
// In case of duplicate keys, the value from |expected_extra_annotations| is picked.
void CheckAnnotationsOnServer(
const std::map<std::string, std::string>& expected_extra_annotations = {}) {
ASSERT_TRUE(crash_server_);
std::map<std::string, testing::Matcher<std::string>> expected_annotations = {
{"product", "Fuchsia"},
{"version", kBuildVersion},
{"program", testing::StartsWith("crashing_program")},
{"ptype", testing::StartsWith("crashing_program")},
{"osName", "Fuchsia"},
{"osVersion", kBuildVersion},
{"reportTimeMillis", Not(IsEmpty())},
{"guid", kDefaultDeviceId},
{"channel", kDefaultChannel},
{"should_process", "false"},
{"debug.snapshot.shared-request.num-clients", Not(IsEmpty())},
{"debug.snapshot.shared-request.uuid", Not(IsEmpty())},
};
for (const auto& [key, value] : expected_extra_annotations) {
expected_annotations[key] = value;
}
EXPECT_THAT(crash_server_->latest_annotations().Raw(),
testing::UnorderedElementsAreArray(Linearize(expected_annotations)));
}
// Checks that on the crash server the keys for the attachments received match the
// concatenation of:
// * |expected_extra_attachment_keys|
// * data_provider_->attachment_bundle_key()
void CheckAttachmentsOnServer(
const std::vector<std::string>& expected_extra_attachment_keys = {}) {
ASSERT_TRUE(crash_server_);
std::vector<std::string> expected_attachment_keys = expected_extra_attachment_keys;
EXPECT_THAT(crash_server_->latest_attachment_keys(),
testing::UnorderedElementsAreArray(expected_attachment_keys));
}
// Checks that the crash server is still expecting at least one more request.
//
// This is useful to check that an upload request hasn't been made as we are using a strict stub.
void CheckServerStillExpectRequests() {
ASSERT_TRUE(crash_server_);
EXPECT_TRUE(crash_server_->ExpectRequest());
}
// Files one crash report.
::fpromise::result<void, zx_status_t> FileOneCrashReport(CrashReport report) {
FX_CHECK(crash_reporter_ != nullptr)
<< "crash_reporter_ is nullptr. Call SetUpCrashReporter() or one of its variants "
"at the beginning of a test case.";
std::optional<::fpromise::result<void, zx_status_t>> out_result{std::nullopt};
crash_reporter_->File(std::move(report),
[&out_result](::fpromise::result<void, zx_status_t> result) {
out_result = std::move(result);
});
RunLoopFor(kSnapshotSharedRequestWindow);
FX_CHECK(out_result.has_value());
return out_result.value();
}
// Files one crash report.
::fpromise::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).
::fpromise::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 native crash report.
::fpromise::result<void, zx_status_t> FileOneNativeCrashReport(
std::optional<fuchsia::mem::Buffer> minidump,
const std::optional<std::string>& crash_signature) {
NativeCrashReport native_report;
if (minidump.has_value()) {
native_report.set_minidump(std::move(minidump.value()));
}
native_report.set_process_name("crashing_process");
native_report.set_process_koid(123u);
native_report.set_thread_name("crashing_thread");
native_report.set_thread_koid(1234u);
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));
if (crash_signature.has_value()) {
report.set_crash_signature(crash_signature.value());
}
return FileOneCrashReport(std::move(report));
}
// Files one Dart crash report.
::fpromise::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));
}
// Files one empty crash report.
::fpromise::result<void, zx_status_t> FileOneEmptyCrashReport() {
CrashReport report;
return FileOneCrashReport(std::move(report));
}
// Files one crash report with the provided crash signature.
::fpromise::result<void, zx_status_t> FileOneCrashReportWithSignature(
const std::string& signature) {
CrashReport report;
report.set_program_name("crashing_program_generic");
report.set_crash_signature(signature);
return FileOneCrashReport(std::move(report));
}
// Files one crash report with the provided is fatal value.
::fpromise::result<void, zx_status_t> FileOneCrashReportWithIsFatal(const bool is_fatal) {
CrashReport report;
report.set_program_name("crashing_program_generic");
report.set_is_fatal(is_fatal);
return FileOneCrashReport(std::move(report));
}
void SetPrivacySettings(std::optional<bool> user_data_sharing_consent) {
FX_CHECK(privacy_settings_server_);
::fpromise::result<void, fuchsia::settings::Error> set_result;
privacy_settings_server_->Set(
MakePrivacySettings(user_data_sharing_consent),
[&set_result](::fpromise::result<void, fuchsia::settings::Error> result) {
set_result = std::move(result);
});
EXPECT_TRUE(set_result.is_ok());
}
private:
files::ScopedTempDir tmp_dir_;
LogTags tags_;
// Stubs and fake servers.
std::unique_ptr<stubs::ChannelControlBase> channel_provider_server_;
std::unique_ptr<stubs::DataProviderBase> data_provider_server_;
std::unique_ptr<stubs::DeviceIdProviderBase> device_id_provider_server_;
std::unique_ptr<stubs::NetworkReachabilityProvider> network_reachability_provider_server_;
std::unique_ptr<fakes::PrivacySettings> privacy_settings_server_;
protected:
std::unique_ptr<StubCrashServer> crash_server_;
private:
timekeeper::TestClock clock_;
std::shared_ptr<InfoContext> info_context_;
protected:
std::unique_ptr<SnapshotManager> snapshot_manager_;
std::unique_ptr<CrashRegister> crash_register_;
std::unique_ptr<CrashReporter> crash_reporter_;
};
TEST_F(CrashReporterTest, Succeed_OnInputCrashReport) {
SetUpDataProviderServer(
std::make_unique<stubs::DataProvider>(kDefaultAnnotations, kDefaultAttachmentBundleKey));
SetUpCrashReporterDefaultConfig({kUploadSuccessful});
SetUpChannelProviderServer(
std::make_unique<stubs::ChannelControl>(stubs::ChannelControlBase::Params{
.current = kDefaultChannel,
.target = std::nullopt,
}));
SetUpDeviceIdProviderServer(std::make_unique<stubs::DeviceIdProvider>(kDefaultDeviceId));
ASSERT_TRUE(FileOneCrashReport().is_ok());
CheckAnnotationsOnServer(kDefaultAnnotations);
CheckAttachmentsOnServer({kDefaultAttachmentBundleKey});
}
TEST_F(CrashReporterTest, EnforcesQuota) {
SetUpDataProviderServer(
std::make_unique<stubs::DataProvider>(kDefaultAnnotations, kDefaultAttachmentBundleKey));
SetUpCrashReporterDefaultConfig(
std::vector<CrashServer::UploadStatus>(kDailyPerProductQuota, kUploadSuccessful));
SetUpChannelProviderServer(
std::make_unique<stubs::ChannelControl>(stubs::ChannelControlBase::Params{
.current = kDefaultChannel,
.target = std::nullopt,
}));
SetUpDeviceIdProviderServer(std::make_unique<stubs::DeviceIdProvider>(kDefaultDeviceId));
for (size_t i = 0; i < kDailyPerProductQuota + 1; ++i) {
ASSERT_TRUE(FileOneCrashReport().is_ok());
}
}
TEST_F(CrashReporterTest, ResetsQuota) {
SetUpDataProviderServer(
std::make_unique<stubs::DataProvider>(kDefaultAnnotations, kDefaultAttachmentBundleKey));
SetUpCrashReporterDefaultConfig(
std::vector<CrashServer::UploadStatus>(kDailyPerProductQuota * 2, kUploadSuccessful));
SetUpChannelProviderServer(
std::make_unique<stubs::ChannelControl>(stubs::ChannelControlBase::Params{
.current = kDefaultChannel,
.target = std::nullopt,
}));
SetUpDeviceIdProviderServer(std::make_unique<stubs::DeviceIdProvider>(kDefaultDeviceId));
for (size_t i = 0; i < kDailyPerProductQuota; ++i) {
ASSERT_TRUE(FileOneCrashReport().is_ok());
}
RunLoopFor(zx::hour(24));
for (size_t i = 0; i < kDailyPerProductQuota; ++i) {
ASSERT_TRUE(FileOneCrashReport().is_ok());
}
}
TEST_F(CrashReporterTest, NoQuota) {
SetUpDataProviderServer(
std::make_unique<stubs::DataProvider>(kDefaultAnnotations, kDefaultAttachmentBundleKey));
SetUpCrashReporter(
Config{/*crash_server=*/
{
/*upload_policy=*/CrashServerConfig::UploadPolicy::ENABLED,
},
/*daily_per_product_quota=*/std::nullopt,
/*houry_snapshot=*/true},
std::vector<CrashServer::UploadStatus>(kDailyPerProductQuota + 1 /*first hourly snapshot*/,
kUploadSuccessful));
SetUpChannelProviderServer(
std::make_unique<stubs::ChannelControl>(stubs::ChannelControlBase::Params{
.current = kDefaultChannel,
.target = std::nullopt,
}));
SetUpDeviceIdProviderServer(std::make_unique<stubs::DeviceIdProvider>(kDefaultDeviceId));
for (size_t i = 0; i < kDailyPerProductQuota; ++i) {
ASSERT_TRUE(FileOneCrashReport().is_ok());
}
}
TEST_F(CrashReporterTest, Check_UnknownChannel) {
SetUpDataProviderServer(
std::make_unique<stubs::DataProvider>(kEmptyAnnotations, kDefaultAttachmentBundleKey));
SetUpCrashReporterDefaultConfig({kUploadSuccessful});
SetUpChannelProviderServer(std::make_unique<stubs::ChannelControlClosesConnection>());
SetUpDeviceIdProviderServer(std::make_unique<stubs::DeviceIdProvider>(kDefaultDeviceId));
ASSERT_TRUE(FileOneCrashReport().is_ok());
CheckAttachmentsOnServer({kDefaultAttachmentBundleKey});
ASSERT_TRUE(crash_server_->latest_annotations().Contains("channel"));
EXPECT_EQ(crash_server_->latest_annotations().Get("channel"), "unknown");
ASSERT_TRUE(crash_server_->latest_annotations().Contains("debug.channel.error"));
EXPECT_EQ(crash_server_->latest_annotations().Get("debug.channel.error"),
"FIDL connection error");
}
TEST_F(CrashReporterTest, Check_RegisteredProduct) {
SetUpDataProviderServer(
std::make_unique<stubs::DataProvider>(kEmptyAnnotations, kDefaultAttachmentBundleKey));
SetUpCrashReporterDefaultConfig({kUploadSuccessful});
SetUpDeviceIdProviderServer(std::make_unique<stubs::DeviceIdProvider>(kDefaultDeviceId));
fuchsia::feedback::CrashReportingProduct product;
product.set_name("some name");
product.set_version("some version");
product.set_channel("some channel");
crash_register_->Upsert(kProgramName, std::move(product));
ASSERT_TRUE(FileOneCrashReport().is_ok());
ASSERT_TRUE(crash_server_->latest_annotations().Contains("product"));
EXPECT_EQ(crash_server_->latest_annotations().Get("product"), "some name");
ASSERT_TRUE(crash_server_->latest_annotations().Contains("version"));
EXPECT_EQ(crash_server_->latest_annotations().Get("version"), "some version");
ASSERT_TRUE(crash_server_->latest_annotations().Contains("channel"));
EXPECT_EQ(crash_server_->latest_annotations().Get("channel"), "some channel");
}
TEST_F(CrashReporterTest, Succeed_OnInputCrashReportWithAdditionalData) {
SetUpDataProviderServer(
std::make_unique<stubs::DataProvider>(kEmptyAnnotations, kEmptyAttachmentBundleKey));
SetUpCrashReporterDefaultConfig({kUploadSuccessful});
SetUpChannelProviderServer(
std::make_unique<stubs::ChannelControl>(stubs::ChannelControlBase::Params{
.current = kDefaultChannel,
.target = std::nullopt,
}));
SetUpDeviceIdProviderServer(std::make_unique<stubs::DeviceIdProvider>(kDefaultDeviceId));
std::vector<Attachment> attachments;
attachments.emplace_back(BuildAttachment(kSingleAttachmentKey, kSingleAttachmentValue));
ASSERT_TRUE(FileOneCrashReport(
/*annotations=*/
{
{"annotation.key", "annotation.value"},
},
/*attachments=*/std::move(attachments))
.is_ok());
CheckAnnotationsOnServer({
{"annotation.key", "annotation.value"},
});
CheckAttachmentsOnServer({kSingleAttachmentKey, kEmptyAttachmentBundleKey});
}
TEST_F(CrashReporterTest, Succeed_OnInputCrashReportWithEventId) {
SetUpDataProviderServer(
std::make_unique<stubs::DataProvider>(kEmptyAnnotations, kDefaultAttachmentBundleKey));
SetUpCrashReporterDefaultConfig({kUploadSuccessful});
SetUpChannelProviderServer(
std::make_unique<stubs::ChannelControl>(stubs::ChannelControlBase::Params{
.current = kDefaultChannel,
.target = std::nullopt,
}));
SetUpDeviceIdProviderServer(std::make_unique<stubs::DeviceIdProvider>(kDefaultDeviceId));
CrashReport report;
report.set_program_name(kProgramName);
report.set_event_id("some-event-id");
ASSERT_TRUE(FileOneCrashReport(std::move(report)).is_ok());
CheckAnnotationsOnServer({
{"comments", "some-event-id"},
});
CheckAttachmentsOnServer({kDefaultAttachmentBundleKey});
}
TEST_F(CrashReporterTest, Succeed_OnInputCrashReportWithProgramUptime) {
SetUpDataProviderServer(
std::make_unique<stubs::DataProvider>(kEmptyAnnotations, kDefaultAttachmentBundleKey));
SetUpCrashReporterDefaultConfig({kUploadSuccessful});
SetUpChannelProviderServer(
std::make_unique<stubs::ChannelControl>(stubs::ChannelControlBase::Params{
.current = kDefaultChannel,
.target = std::nullopt,
}));
SetUpDeviceIdProviderServer(std::make_unique<stubs::DeviceIdProvider>(kDefaultDeviceId));
CrashReport report;
report.set_program_name(kProgramName);
const zx::duration uptime =
zx::hour(3) * 24 + zx::hour(15) + zx::min(33) + zx::sec(17) + zx::msec(54);
report.set_program_uptime(uptime.get());
ASSERT_TRUE(FileOneCrashReport(std::move(report)).is_ok());
CheckAnnotationsOnServer({
{"ptime", std::to_string(uptime.to_msecs())},
});
CheckAttachmentsOnServer({kDefaultAttachmentBundleKey});
}
TEST_F(CrashReporterTest, Succeed_OnNativeInputCrashReport) {
SetUpDataProviderServer(
std::make_unique<stubs::DataProvider>(kEmptyAnnotations, kDefaultAttachmentBundleKey));
SetUpCrashReporterDefaultConfig({kUploadSuccessful});
SetUpChannelProviderServer(
std::make_unique<stubs::ChannelControl>(stubs::ChannelControlBase::Params{
.current = kDefaultChannel,
.target = std::nullopt,
}));
SetUpDeviceIdProviderServer(std::make_unique<stubs::DeviceIdProvider>(kDefaultDeviceId));
fuchsia::mem::Buffer minidump;
fsl::VmoFromString("minidump", &minidump);
ASSERT_TRUE(FileOneNativeCrashReport(std::move(minidump), std::nullopt).is_ok());
CheckAnnotationsOnServer({
{"crash.process.name", "crashing_process"},
{"crash.process.koid", "123"},
{"crash.thread.name", "crashing_thread"},
{"crash.thread.koid", "1234"},
{"should_process", "true"},
});
CheckAttachmentsOnServer({"uploadFileMinidump", kDefaultAttachmentBundleKey});
}
TEST_F(CrashReporterTest, Succeed_OnNativeInputCrashReportWithoutMinidump) {
SetUpDataProviderServer(
std::make_unique<stubs::DataProvider>(kEmptyAnnotations, kDefaultAttachmentBundleKey));
SetUpCrashReporterDefaultConfig({kUploadSuccessful});
SetUpChannelProviderServer(
std::make_unique<stubs::ChannelControl>(stubs::ChannelControlBase::Params{
.current = kDefaultChannel,
.target = std::nullopt,
}));
SetUpDeviceIdProviderServer(std::make_unique<stubs::DeviceIdProvider>(kDefaultDeviceId));
ASSERT_TRUE(FileOneNativeCrashReport(std::nullopt, std::nullopt).is_ok());
CheckAnnotationsOnServer({
{"crash.process.name", "crashing_process"},
{"crash.process.koid", "123"},
{"crash.thread.name", "crashing_thread"},
{"crash.thread.koid", "1234"},
{"signature", "fuchsia-no-minidump"},
});
CheckAttachmentsOnServer({kDefaultAttachmentBundleKey});
}
TEST_F(CrashReporterTest, Succeed_OnNativeInputCrashReportWithoutMinidumpButCrashSignature) {
SetUpDataProviderServer(
std::make_unique<stubs::DataProvider>(kEmptyAnnotations, kDefaultAttachmentBundleKey));
SetUpCrashReporterDefaultConfig({kUploadSuccessful});
SetUpChannelProviderServer(
std::make_unique<stubs::ChannelControl>(stubs::ChannelControlBase::Params{
.current = kDefaultChannel,
.target = std::nullopt,
}));
SetUpDeviceIdProviderServer(std::make_unique<stubs::DeviceIdProvider>(kDefaultDeviceId));
ASSERT_TRUE(FileOneNativeCrashReport(std::nullopt, "some-signature").is_ok());
CheckAnnotationsOnServer({
{"crash.process.name", "crashing_process"},
{"crash.process.koid", "123"},
{"crash.thread.name", "crashing_thread"},
{"crash.thread.koid", "1234"},
{"signature", "some-signature"},
});
CheckAttachmentsOnServer({kDefaultAttachmentBundleKey});
}
TEST_F(CrashReporterTest, Succeed_OnDartInputCrashReport) {
SetUpDataProviderServer(
std::make_unique<stubs::DataProvider>(kEmptyAnnotations, kEmptyAttachmentBundleKey));
SetUpCrashReporterDefaultConfig({kUploadSuccessful});
SetUpChannelProviderServer(
std::make_unique<stubs::ChannelControl>(stubs::ChannelControlBase::Params{
.current = kDefaultChannel,
.target = std::nullopt,
}));
SetUpDeviceIdProviderServer(std::make_unique<stubs::DeviceIdProvider>(kDefaultDeviceId));
fuchsia::mem::Buffer stack_trace;
fsl::VmoFromString("#0", &stack_trace);
ASSERT_TRUE(
FileOneDartCrashReport("FileSystemException", "cannot open file", std::move(stack_trace))
.is_ok());
CheckAnnotationsOnServer({
{"error_runtime_type", "FileSystemException"},
{"error_message", "cannot open file"},
{"type", "DartError"},
{"should_process", "true"},
});
CheckAttachmentsOnServer({"DartError", kEmptyAttachmentBundleKey});
}
TEST_F(CrashReporterTest, Succeed_OnDartInputCrashReportWithoutExceptionData) {
SetUpDataProviderServer(
std::make_unique<stubs::DataProvider>(kEmptyAnnotations, kDefaultAttachmentBundleKey));
SetUpCrashReporterDefaultConfig({kUploadSuccessful});
SetUpChannelProviderServer(
std::make_unique<stubs::ChannelControl>(stubs::ChannelControlBase::Params{
.current = kDefaultChannel,
.target = std::nullopt,
}));
SetUpDeviceIdProviderServer(std::make_unique<stubs::DeviceIdProvider>(kDefaultDeviceId));
ASSERT_TRUE(FileOneDartCrashReport(std::nullopt, std::nullopt, std::nullopt).is_ok());
CheckAnnotationsOnServer({
{"type", "DartError"},
{"signature", "fuchsia-no-dart-stack-trace"},
});
CheckAttachmentsOnServer({kDefaultAttachmentBundleKey});
}
TEST_F(CrashReporterTest, Succeed_OnInputCrashReportWithSignature) {
SetUpDataProviderServer(
std::make_unique<stubs::DataProvider>(kEmptyAnnotations, kDefaultAttachmentBundleKey));
SetUpCrashReporterDefaultConfig({kUploadSuccessful});
SetUpChannelProviderServer(
std::make_unique<stubs::ChannelControl>(stubs::ChannelControlBase::Params{
.current = kDefaultChannel,
.target = std::nullopt,
}));
SetUpDeviceIdProviderServer(std::make_unique<stubs::DeviceIdProvider>(kDefaultDeviceId));
ASSERT_TRUE(FileOneCrashReportWithSignature("some-signature").is_ok());
CheckAnnotationsOnServer({
{"signature", "some-signature"},
});
CheckAttachmentsOnServer({kDefaultAttachmentBundleKey});
}
TEST_F(CrashReporterTest, Fail_OnInvalidInputCrashReport) {
SetUpDataProviderServer(std::make_unique<stubs::DataProviderReturnsEmptySnapshot>());
SetUpCrashReporterDefaultConfig();
SetUpDeviceIdProviderServer(std::make_unique<stubs::DeviceIdProvider>(kDefaultDeviceId));
EXPECT_TRUE(FileOneEmptyCrashReport().is_error());
}
TEST_F(CrashReporterTest, Succeed_OnInputCrashReportWithIsFatalTrue) {
SetUpDataProviderServer(
std::make_unique<stubs::DataProvider>(kEmptyAnnotations, kDefaultAttachmentBundleKey));
SetUpCrashReporterDefaultConfig({kUploadSuccessful});
SetUpChannelProviderServer(
std::make_unique<stubs::ChannelControl>(stubs::ChannelControlBase::Params{
.current = kDefaultChannel,
.target = std::nullopt,
}));
SetUpDeviceIdProviderServer(std::make_unique<stubs::DeviceIdProvider>(kDefaultDeviceId));
ASSERT_TRUE(FileOneCrashReportWithIsFatal(true).is_ok());
CheckAnnotationsOnServer({
{"isFatal", "true"},
});
CheckAttachmentsOnServer({kDefaultAttachmentBundleKey});
}
TEST_F(CrashReporterTest, Succeed_OnInputCrashReportWithIsFatalFalse) {
SetUpDataProviderServer(
std::make_unique<stubs::DataProvider>(kEmptyAnnotations, kDefaultAttachmentBundleKey));
SetUpCrashReporterDefaultConfig({kUploadSuccessful});
SetUpChannelProviderServer(
std::make_unique<stubs::ChannelControl>(stubs::ChannelControlBase::Params{
.current = kDefaultChannel,
.target = std::nullopt,
}));
SetUpDeviceIdProviderServer(std::make_unique<stubs::DeviceIdProvider>(kDefaultDeviceId));
ASSERT_TRUE(FileOneCrashReportWithIsFatal(false).is_ok());
CheckAnnotationsOnServer({
{"isFatal", "false"},
});
CheckAttachmentsOnServer({kDefaultAttachmentBundleKey});
}
TEST_F(CrashReporterTest, Upload_OnUserAlreadyOptedInDataSharing) {
SetUpDataProviderServer(
std::make_unique<stubs::DataProvider>(kDefaultAnnotations, kDefaultAttachmentBundleKey));
SetUpCrashReporter(
Config{/*crash_server=*/
{
/*upload_policy=*/CrashServerConfig::UploadPolicy::READ_FROM_PRIVACY_SETTINGS,
},
/*daily_per_product_quota=*/kDailyPerProductQuota,
/*houry_snapshot=*/true},
std::vector({kUploadSuccessful}));
SetUpChannelProviderServer(
std::make_unique<stubs::ChannelControl>(stubs::ChannelControlBase::Params{
.current = kDefaultChannel,
.target = std::nullopt,
}));
SetUpDeviceIdProviderServer(std::make_unique<stubs::DeviceIdProvider>(kDefaultDeviceId));
SetUpPrivacySettingsServer(std::make_unique<fakes::PrivacySettings>());
SetPrivacySettings(kUserOptInDataSharing);
ASSERT_TRUE(FileOneCrashReport().is_ok());
CheckAnnotationsOnServer(kDefaultAnnotations);
CheckAttachmentsOnServer({kDefaultAttachmentBundleKey});
}
TEST_F(CrashReporterTest, Archive_OnUserAlreadyOptedOutDataSharing) {
SetUpDataProviderServer(std::make_unique<stubs::DataProviderTracksNumCalls>(0u));
SetUpCrashReporter(Config{
/*crash_server=*/
{
/*upload_policy=*/CrashServerConfig::UploadPolicy::READ_FROM_PRIVACY_SETTINGS,
},
/*daily_per_product_quota=*/kDailyPerProductQuota,
/*houry_snapshot=*/true});
SetUpChannelProviderServer(
std::make_unique<stubs::ChannelControl>(stubs::ChannelControlBase::Params{
.current = kDefaultChannel,
.target = std::nullopt,
}));
SetUpDeviceIdProviderServer(std::make_unique<stubs::DeviceIdProvider>(kDefaultDeviceId));
SetUpPrivacySettingsServer(std::make_unique<fakes::PrivacySettings>());
SetPrivacySettings(kUserOptOutDataSharing);
RunLoopUntilIdle();
ASSERT_TRUE(FileOneCrashReport().is_ok());
}
TEST_F(CrashReporterTest, Upload_OnceUserOptInDataSharing) {
SetUpDataProviderServer(
std::make_unique<stubs::DataProvider>(kDefaultAnnotations, kDefaultAttachmentBundleKey));
SetUpCrashReporter(
Config{/*crash_server=*/
{
/*upload_policy=*/CrashServerConfig::UploadPolicy::READ_FROM_PRIVACY_SETTINGS,
},
/*daily_per_product_quota=*/kDailyPerProductQuota,
/*hourly_snapshot=*/true},
std::vector({kUploadSuccessful}));
SetUpChannelProviderServer(
std::make_unique<stubs::ChannelControl>(stubs::ChannelControlBase::Params{
.current = kDefaultChannel,
.target = std::nullopt,
}));
SetUpDeviceIdProviderServer(std::make_unique<stubs::DeviceIdProvider>(kDefaultDeviceId));
SetUpPrivacySettingsServer(std::make_unique<fakes::PrivacySettings>());
ASSERT_TRUE(FileOneCrashReport().is_ok());
CheckServerStillExpectRequests();
SetPrivacySettings(kUserOptInDataSharing);
ASSERT_TRUE(RunLoopUntilIdle());
CheckAnnotationsOnServer(kDefaultAnnotations);
CheckAttachmentsOnServer({kDefaultAttachmentBundleKey});
}
TEST_F(CrashReporterTest, Succeed_OnFailedUpload) {
SetUpDataProviderServer(
std::make_unique<stubs::DataProvider>(kEmptyAnnotations, kEmptyAttachmentBundleKey));
SetUpCrashReporter(
Config{/*crash_server=*/
{
/*upload_policy=*/CrashServerConfig::UploadPolicy::ENABLED,
},
/*daily_per_product_quota=*/kDailyPerProductQuota,
/*hourly_snapshot=*/true},
std::vector({kUploadFailed}));
SetUpChannelProviderServer(
std::make_unique<stubs::ChannelControl>(stubs::ChannelControlBase::Params{
.current = kDefaultChannel,
.target = std::nullopt,
}));
SetUpDeviceIdProviderServer(std::make_unique<stubs::DeviceIdProvider>(kDefaultDeviceId));
EXPECT_TRUE(FileOneCrashReport().is_ok());
}
TEST_F(CrashReporterTest, Succeed_OnThrottledUpload) {
SetUpDataProviderServer(
std::make_unique<stubs::DataProvider>(kEmptyAnnotations, kEmptyAttachmentBundleKey));
SetUpCrashReporter(
Config{/*crash_server=*/
{
/*upload_policy=*/CrashServerConfig::UploadPolicy::ENABLED,
},
/*daily_per_product_quota=*/kDailyPerProductQuota,
/*hourly_snapshot=*/true},
std::vector({kUploadThrottled}));
SetUpChannelProviderServer(
std::make_unique<stubs::ChannelControl>(stubs::ChannelControlBase::Params{
.current = kDefaultChannel,
.target = std::nullopt,
}));
SetUpDeviceIdProviderServer(std::make_unique<stubs::DeviceIdProvider>(kDefaultDeviceId));
EXPECT_TRUE(FileOneCrashReport().is_ok());
}
TEST_F(CrashReporterTest, Succeed_OnDisabledUpload) {
SetUpDataProviderServer(
std::make_unique<stubs::DataProvider>(kEmptyAnnotations, kEmptyAttachmentBundleKey));
SetUpCrashReporter(Config{/*crash_server=*/
{
/*upload_policy=*/CrashServerConfig::UploadPolicy::DISABLED,
},
/*daily_per_product_quota=*/kDailyPerProductQuota,
/*hourly_snapshot=*/true});
SetUpChannelProviderServer(
std::make_unique<stubs::ChannelControl>(stubs::ChannelControlBase::Params{
.current = kDefaultChannel,
.target = std::nullopt,
}));
SetUpDeviceIdProviderServer(std::make_unique<stubs::DeviceIdProvider>(kDefaultDeviceId));
EXPECT_TRUE(FileOneCrashReport().is_ok());
}
TEST_F(CrashReporterTest, Succeed_OnNoFeedbackAttachments) {
SetUpDataProviderServer(
std::make_unique<stubs::DataProviderReturnsNoAttachment>(kDefaultAnnotations));
SetUpCrashReporterDefaultConfig({kUploadSuccessful});
SetUpChannelProviderServer(
std::make_unique<stubs::ChannelControl>(stubs::ChannelControlBase::Params{
.current = kDefaultChannel,
.target = std::nullopt,
}));
SetUpDeviceIdProviderServer(std::make_unique<stubs::DeviceIdProvider>(kDefaultDeviceId));
EXPECT_TRUE(FileOneCrashReportWithSingleAttachment().is_ok());
CheckAnnotationsOnServer(kDefaultAnnotations);
CheckAttachmentsOnServer({kSingleAttachmentKey});
}
TEST_F(CrashReporterTest, Succeed_OnNoFeedbackAnnotations) {
SetUpDataProviderServer(
std::make_unique<stubs::DataProviderReturnsNoAnnotation>(kEmptyAttachmentBundleKey));
SetUpCrashReporterDefaultConfig({kUploadSuccessful});
SetUpChannelProviderServer(
std::make_unique<stubs::ChannelControl>(stubs::ChannelControlBase::Params{
.current = kDefaultChannel,
.target = std::nullopt,
}));
SetUpDeviceIdProviderServer(std::make_unique<stubs::DeviceIdProvider>(kDefaultDeviceId));
EXPECT_TRUE(FileOneCrashReportWithSingleAttachment().is_ok());
CheckAnnotationsOnServer();
CheckAttachmentsOnServer({kSingleAttachmentKey, kEmptyAttachmentBundleKey});
}
TEST_F(CrashReporterTest, Succeed_OnNoFeedbackData) {
SetUpDataProviderServer(std::make_unique<stubs::DataProviderReturnsEmptySnapshot>());
SetUpCrashReporterDefaultConfig({kUploadSuccessful});
SetUpChannelProviderServer(
std::make_unique<stubs::ChannelControl>(stubs::ChannelControlBase::Params{
.current = kDefaultChannel,
.target = std::nullopt,
}));
SetUpDeviceIdProviderServer(std::make_unique<stubs::DeviceIdProvider>(kDefaultDeviceId));
EXPECT_TRUE(FileOneCrashReportWithSingleAttachment().is_ok());
CheckAnnotationsOnServer({
{"debug.snapshot.present", "false"},
});
CheckAttachmentsOnServer({kSingleAttachmentKey});
}
TEST_F(CrashReporterTest, Upload_HourlySnapshot) {
SetUpDataProviderServer(
std::make_unique<stubs::DataProvider>(kDefaultAnnotations, kDefaultAttachmentBundleKey));
SetUpCrashReporterDefaultConfig({kUploadSuccessful, kUploadSuccessful});
SetUpChannelProviderServer(
std::make_unique<stubs::ChannelControl>(stubs::ChannelControlBase::Params{
.current = kDefaultChannel,
.target = std::nullopt,
}));
SetUpDeviceIdProviderServer(std::make_unique<stubs::DeviceIdProvider>(kDefaultDeviceId));
RunLoopFor(zx::min(5));
EXPECT_THAT(crash_server_->latest_annotations().Raw(),
IsSupersetOf(Linearize(std::map<std::string, testing::Matcher<std::string>>({
{"ptime", Not(IsEmpty())},
{"signature", kHourlySnapshotSignature},
}))));
CheckAttachmentsOnServer({kDefaultAttachmentBundleKey});
RunLoopFor(zx::hour(1));
EXPECT_THAT(crash_server_->latest_annotations().Raw(),
IsSupersetOf(Linearize(std::map<std::string, testing::Matcher<std::string>>({
{"ptime", Not(IsEmpty())},
{"signature", kHourlySnapshotSignature},
}))));
CheckAttachmentsOnServer({kDefaultAttachmentBundleKey});
}
TEST_F(CrashReporterTest, Skip_HourlySnapshotIfPending) {
SetUpDataProviderServer(
std::make_unique<stubs::DataProvider>(kDefaultAnnotations, kDefaultAttachmentBundleKey));
SetUpCrashReporterDefaultConfig({
// Initial upload attempt.
kUploadFailed,
// 4 failed periodic uploads by the queue.
kUploadFailed,
kUploadFailed,
kUploadFailed,
kUploadFailed,
});
SetUpChannelProviderServer(
std::make_unique<stubs::ChannelControl>(stubs::ChannelControlBase::Params{
.current = kDefaultChannel,
.target = std::nullopt,
}));
SetUpDeviceIdProviderServer(std::make_unique<stubs::DeviceIdProvider>(kDefaultDeviceId));
RunLoopFor(zx::min(5));
RunLoopFor(zx::hour(1));
EXPECT_THAT(crash_server_->latest_annotations().Raw(),
IsSupersetOf(Linearize(std::map<std::string, testing::Matcher<std::string>>({
{"ptime", Not(IsEmpty())},
{"signature", kHourlySnapshotSignature},
}))));
CheckAttachmentsOnServer({kDefaultAttachmentBundleKey});
}
TEST_F(CrashReporterTest, Skip_HourlySnapshotIfNegativeConsent) {
SetUpDataProviderServer(
std::make_unique<stubs::DataProvider>(kDefaultAnnotations, kDefaultAttachmentBundleKey));
SetUpCrashReporter(
Config{/*crash_server=*/
{
/*upload_policy=*/CrashServerConfig::UploadPolicy::READ_FROM_PRIVACY_SETTINGS,
},
/*daily_per_product_quota=*/kDailyPerProductQuota,
/*houry_snapshot=*/true},
std::vector<CrashServer::UploadStatus>({}));
SetUpChannelProviderServer(
std::make_unique<stubs::ChannelControl>(stubs::ChannelControlBase::Params{
.current = kDefaultChannel,
.target = std::nullopt,
}));
SetUpDeviceIdProviderServer(std::make_unique<stubs::DeviceIdProvider>(kDefaultDeviceId));
SetUpPrivacySettingsServer(std::make_unique<fakes::PrivacySettings>());
SetPrivacySettings(kUserOptOutDataSharing);
RunLoopFor(zx::min(5));
}
TEST_F(CrashReporterTest, Check_CobaltAfterSuccessfulUpload) {
SetUpDataProviderServer(
std::make_unique<stubs::DataProvider>(kEmptyAnnotations, kEmptyAttachmentBundleKey));
SetUpCrashReporterDefaultConfig({kUploadSuccessful});
SetUpChannelProviderServer(
std::make_unique<stubs::ChannelControl>(stubs::ChannelControlBase::Params{
.current = kDefaultChannel,
.target = std::nullopt,
}));
SetUpDeviceIdProviderServer(std::make_unique<stubs::DeviceIdProvider>(kDefaultDeviceId));
EXPECT_TRUE(FileOneCrashReport().is_ok());
EXPECT_THAT(ReceivedCobaltEvents(),
UnorderedElementsAreArray({
cobalt::Event(cobalt::CrashState::kFiled),
cobalt::Event(cobalt::CrashState::kUploaded),
cobalt::Event(cobalt::UploadAttemptState::kUploadAttempt, 1u),
cobalt::Event(cobalt::UploadAttemptState::kUploaded, 1u),
}));
}
TEST_F(CrashReporterTest, Check_CobaltAfterQuotaReached) {
SetUpDataProviderServer(
std::make_unique<stubs::DataProvider>(kEmptyAnnotations, kEmptyAttachmentBundleKey));
SetUpCrashReporter(Config{/*crash_server=*/
{
/*upload_policy=*/CrashServerConfig::UploadPolicy::ENABLED,
},
/*daily_per_product_quota=*/0u,
/*hourly_snapshot=*/true});
SetUpChannelProviderServer(
std::make_unique<stubs::ChannelControl>(stubs::ChannelControlBase::Params{
.current = kDefaultChannel,
.target = std::nullopt,
}));
SetUpDeviceIdProviderServer(std::make_unique<stubs::DeviceIdProvider>(kDefaultDeviceId));
EXPECT_TRUE(FileOneCrashReport().is_ok());
EXPECT_THAT(ReceivedCobaltEvents(), UnorderedElementsAreArray({
cobalt::Event(cobalt::CrashState::kOnDeviceQuotaReached),
}));
}
TEST_F(CrashReporterTest, Check_CobaltAfterInvalidInputCrashReport) {
SetUpDataProviderServer(std::make_unique<stubs::DataProviderReturnsEmptySnapshot>());
SetUpCrashReporterDefaultConfig();
SetUpChannelProviderServer(
std::make_unique<stubs::ChannelControl>(stubs::ChannelControlBase::Params{
.current = kDefaultChannel,
.target = std::nullopt,
}));
SetUpDeviceIdProviderServer(std::make_unique<stubs::DeviceIdProvider>(kDefaultDeviceId));
EXPECT_TRUE(FileOneEmptyCrashReport().is_error());
EXPECT_THAT(ReceivedCobaltEvents(), UnorderedElementsAreArray({
cobalt::Event(cobalt::CrashState::kDropped),
}));
}
// Test fixture that replaces the runtime clock before starting.
class CrashReporterTestWithClock : public CrashReporterTest {
public:
CrashReporterTestWithClock() {
// Create a |test_clock_|.
zx_clock_create_args_v1_t clock_args{.backstop_time = 0};
FX_CHECK(zx::clock::create(0u, &clock_args, &test_clock_) == ZX_OK);
// Duplicate |test_clock| into |tmp_clock|.
zx::clock tmp_clock;
zx_info_handle_basic_t clock_info;
FX_CHECK(test_clock_.get_info(ZX_INFO_HANDLE_BASIC, &clock_info, sizeof(clock_info), nullptr,
nullptr) == ZX_OK);
FX_CHECK(test_clock_.duplicate(clock_info.rights, &tmp_clock) == ZX_OK);
// Install |tmp_clock_| and save the old clock in |old_clock_|.
FX_CHECK(zx_utc_reference_swap(tmp_clock.release(), old_clock_.reset_and_get_address()) ==
ZX_OK);
}
~CrashReporterTestWithClock() {
// Swapping clocks while |crash_reporter_| is waiting on |test_clock_| causes a crash.
crash_reporter_.reset();
// Reinstall the old clock.
zx::clock tmp_clock;
FX_CHECK(zx_utc_reference_swap(old_clock_.release(), tmp_clock.reset_and_get_address()) ==
ZX_OK);
}
private:
zx::clock test_clock_;
zx::clock old_clock_;
};
TEST_F(CrashReporterTestWithClock, Check_UtcTimeIsNotReady) {
SetUpDataProviderServer(
std::make_unique<stubs::DataProvider>(kDefaultAnnotations, kDefaultAttachmentBundleKey));
SetUpCrashReporterDefaultConfig({kUploadSuccessful});
SetUpChannelProviderServer(
std::make_unique<stubs::ChannelControl>(stubs::ChannelControlBase::Params{
.current = kDefaultChannel,
.target = std::nullopt,
}));
SetUpDeviceIdProviderServer(std::make_unique<stubs::DeviceIdProvider>(kDefaultDeviceId));
ASSERT_TRUE(FileOneCrashReport().is_ok());
CheckAttachmentsOnServer({kDefaultAttachmentBundleKey});
EXPECT_FALSE(crash_server_->latest_annotations().Contains("reportTimeMillis"));
ASSERT_TRUE(crash_server_->latest_annotations().Contains("debug.report-time.set"));
EXPECT_EQ(crash_server_->latest_annotations().Get("debug.report-time.set"), "false");
}
} // namespace
} // namespace crash_reports
} // namespace forensics