| // 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 |