// Copyright 2015 The Crashpad Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "client/crash_report_database.h" #include "build/build_config.h" #include "client/settings.h" #include "gtest/gtest.h" #include "test/errors.h" #include "test/file.h" #include "test/filesystem.h" #include "test/gtest_disabled.h" #include "test/scoped_temp_dir.h" #include "util/file/file_io.h" #include "util/file/filesystem.h" namespace crashpad { namespace test { namespace { class CrashReportDatabaseTest : public testing::Test { public: CrashReportDatabaseTest() { } protected: // testing::Test: void SetUp() override { db_ = CrashReportDatabase::Initialize(path()); ASSERT_TRUE(db_); } void ResetDatabase() { db_.reset(); } CrashReportDatabase* db() { return db_.get(); } base::FilePath path() const { return temp_dir_.path().Append(FILE_PATH_LITERAL("crashpad_test_database")); } void CreateCrashReport(CrashReportDatabase::Report* report) { std::unique_ptr new_report; ASSERT_EQ(db_->PrepareNewCrashReport(&new_report), CrashReportDatabase::kNoError); static constexpr char kTest[] = "test"; ASSERT_TRUE(new_report->Writer()->Write(kTest, sizeof(kTest))); UUID uuid; EXPECT_EQ(db_->FinishedWritingCrashReport(std::move(new_report), &uuid), CrashReportDatabase::kNoError); EXPECT_EQ(db_->LookUpCrashReport(uuid, report), CrashReportDatabase::kNoError); ExpectPreparedCrashReport(*report); } void UploadReport(const UUID& uuid, bool successful, const std::string& id) { Settings* const settings = db_->GetSettings(); ASSERT_TRUE(settings); time_t times[2]; ASSERT_TRUE(settings->GetLastUploadAttemptTime(×[0])); std::unique_ptr report; ASSERT_EQ(db_->GetReportForUploading(uuid, &report), CrashReportDatabase::kNoError); EXPECT_NE(report->uuid, UUID()); EXPECT_FALSE(report->file_path.empty()); EXPECT_TRUE(FileExists(report->file_path)) << report->file_path.value(); EXPECT_GT(report->creation_time, 0); if (successful) { EXPECT_EQ(db_->RecordUploadComplete(std::move(report), id), CrashReportDatabase::kNoError); } else { report.reset(); } ASSERT_TRUE(settings->GetLastUploadAttemptTime(×[1])); EXPECT_NE(times[1], 0); EXPECT_GE(times[1], times[0]); } void ExpectPreparedCrashReport(const CrashReportDatabase::Report& report) { EXPECT_NE(report.uuid, UUID()); EXPECT_FALSE(report.file_path.empty()); EXPECT_TRUE(FileExists(report.file_path)) << report.file_path.value(); EXPECT_TRUE(report.id.empty()); EXPECT_GT(report.creation_time, 0); EXPECT_FALSE(report.uploaded); EXPECT_EQ(report.last_upload_attempt_time, 0); EXPECT_EQ(report.upload_attempts, 0); EXPECT_FALSE(report.upload_explicitly_requested); } void RelocateDatabase() { ResetDatabase(); temp_dir_.Rename(); SetUp(); } CrashReportDatabase::OperationStatus RequestUpload(const UUID& uuid) { CrashReportDatabase::OperationStatus os = db()->RequestUpload(uuid); CrashReportDatabase::Report report; EXPECT_EQ(db_->LookUpCrashReport(uuid, &report), CrashReportDatabase::kNoError); return os; } private: ScopedTempDir temp_dir_; std::unique_ptr db_; DISALLOW_COPY_AND_ASSIGN(CrashReportDatabaseTest); }; TEST_F(CrashReportDatabaseTest, Initialize) { // Initialize the database for the first time, creating it. ASSERT_TRUE(db()); Settings* settings = db()->GetSettings(); UUID client_ids[3]; ASSERT_TRUE(settings->GetClientID(&client_ids[0])); EXPECT_NE(client_ids[0], UUID()); time_t last_upload_attempt_time; ASSERT_TRUE(settings->GetLastUploadAttemptTime(&last_upload_attempt_time)); EXPECT_EQ(last_upload_attempt_time, 0); // Close and reopen the database at the same path. ResetDatabase(); EXPECT_FALSE(db()); auto db = CrashReportDatabase::InitializeWithoutCreating(path()); ASSERT_TRUE(db); settings = db->GetSettings(); ASSERT_TRUE(settings->GetClientID(&client_ids[1])); EXPECT_EQ(client_ids[1], client_ids[0]); ASSERT_TRUE(settings->GetLastUploadAttemptTime(&last_upload_attempt_time)); EXPECT_EQ(last_upload_attempt_time, 0); // Check that the database can also be opened by the method that is permitted // to create it. db = CrashReportDatabase::Initialize(path()); ASSERT_TRUE(db); settings = db->GetSettings(); ASSERT_TRUE(settings->GetClientID(&client_ids[2])); EXPECT_EQ(client_ids[2], client_ids[0]); ASSERT_TRUE(settings->GetLastUploadAttemptTime(&last_upload_attempt_time)); EXPECT_EQ(last_upload_attempt_time, 0); std::vector reports; EXPECT_EQ(db->GetPendingReports(&reports), CrashReportDatabase::kNoError); EXPECT_TRUE(reports.empty()); reports.clear(); EXPECT_EQ(db->GetCompletedReports(&reports), CrashReportDatabase::kNoError); EXPECT_TRUE(reports.empty()); // InitializeWithoutCreating() shouldn’t create a nonexistent database. base::FilePath non_database_path = path().DirName().Append(FILE_PATH_LITERAL("not_a_database")); db = CrashReportDatabase::InitializeWithoutCreating(non_database_path); EXPECT_FALSE(db); } TEST_F(CrashReportDatabaseTest, NewCrashReport) { std::unique_ptr new_report; EXPECT_EQ(db()->PrepareNewCrashReport(&new_report), CrashReportDatabase::kNoError); UUID expect_uuid = new_report->ReportID(); UUID uuid; EXPECT_EQ(db()->FinishedWritingCrashReport(std::move(new_report), &uuid), CrashReportDatabase::kNoError); EXPECT_EQ(uuid, expect_uuid); CrashReportDatabase::Report report; EXPECT_EQ(db()->LookUpCrashReport(uuid, &report), CrashReportDatabase::kNoError); ExpectPreparedCrashReport(report); std::vector reports; EXPECT_EQ(db()->GetPendingReports(&reports), CrashReportDatabase::kNoError); ASSERT_EQ(reports.size(), 1u); EXPECT_EQ(reports[0].uuid, report.uuid); reports.clear(); EXPECT_EQ(db()->GetCompletedReports(&reports), CrashReportDatabase::kNoError); EXPECT_TRUE(reports.empty()); } TEST_F(CrashReportDatabaseTest, LookUpCrashReport) { UUID uuid; { CrashReportDatabase::Report report; CreateCrashReport(&report); uuid = report.uuid; } { CrashReportDatabase::Report report; EXPECT_EQ(db()->LookUpCrashReport(uuid, &report), CrashReportDatabase::kNoError); EXPECT_EQ(report.uuid, uuid); EXPECT_NE(report.file_path.value().find(path().value()), std::string::npos); EXPECT_EQ(report.id, std::string()); EXPECT_FALSE(report.uploaded); EXPECT_EQ(report.last_upload_attempt_time, 0); EXPECT_EQ(report.upload_attempts, 0); EXPECT_FALSE(report.upload_explicitly_requested); } UploadReport(uuid, true, "test"); { CrashReportDatabase::Report report; EXPECT_EQ(db()->LookUpCrashReport(uuid, &report), CrashReportDatabase::kNoError); EXPECT_EQ(report.uuid, uuid); EXPECT_NE(report.file_path.value().find(path().value()), std::string::npos); EXPECT_EQ(report.id, "test"); EXPECT_TRUE(report.uploaded); EXPECT_NE(report.last_upload_attempt_time, 0); EXPECT_EQ(report.upload_attempts, 1); EXPECT_FALSE(report.upload_explicitly_requested); } } TEST_F(CrashReportDatabaseTest, RecordUploadAttempt) { std::vector reports(3); CreateCrashReport(&reports[0]); CreateCrashReport(&reports[1]); CreateCrashReport(&reports[2]); // Record two attempts: one successful, one not. UploadReport(reports[1].uuid, false, std::string()); UploadReport(reports[2].uuid, true, "abc123"); std::vector query(3); EXPECT_EQ(db()->LookUpCrashReport(reports[0].uuid, &query[0]), CrashReportDatabase::kNoError); EXPECT_EQ(db()->LookUpCrashReport(reports[1].uuid, &query[1]), CrashReportDatabase::kNoError); EXPECT_EQ(db()->LookUpCrashReport(reports[2].uuid, &query[2]), CrashReportDatabase::kNoError); EXPECT_EQ(query[0].id, std::string()); EXPECT_EQ(query[1].id, std::string()); EXPECT_EQ(query[2].id, "abc123"); EXPECT_FALSE(query[0].uploaded); EXPECT_FALSE(query[1].uploaded); EXPECT_TRUE(query[2].uploaded); EXPECT_EQ(query[0].last_upload_attempt_time, 0); EXPECT_NE(query[1].last_upload_attempt_time, 0); EXPECT_NE(query[2].last_upload_attempt_time, 0); EXPECT_EQ(query[0].upload_attempts, 0); EXPECT_EQ(query[1].upload_attempts, 1); EXPECT_EQ(query[2].upload_attempts, 1); // Attempt to upload and fail again. UploadReport(reports[1].uuid, false, std::string()); time_t report_2_upload_time = query[2].last_upload_attempt_time; EXPECT_EQ(db()->LookUpCrashReport(reports[0].uuid, &query[0]), CrashReportDatabase::kNoError); EXPECT_EQ(db()->LookUpCrashReport(reports[1].uuid, &query[1]), CrashReportDatabase::kNoError); EXPECT_EQ(db()->LookUpCrashReport(reports[2].uuid, &query[2]), CrashReportDatabase::kNoError); EXPECT_FALSE(query[0].uploaded); EXPECT_FALSE(query[1].uploaded); EXPECT_TRUE(query[2].uploaded); EXPECT_EQ(query[0].last_upload_attempt_time, 0); EXPECT_GE(query[1].last_upload_attempt_time, report_2_upload_time); EXPECT_EQ(query[2].last_upload_attempt_time, report_2_upload_time); EXPECT_EQ(query[0].upload_attempts, 0); EXPECT_EQ(query[1].upload_attempts, 2); EXPECT_EQ(query[2].upload_attempts, 1); // Third time's the charm: upload and succeed. UploadReport(reports[1].uuid, true, "666hahaha"); time_t report_1_upload_time = query[1].last_upload_attempt_time; EXPECT_EQ(db()->LookUpCrashReport(reports[0].uuid, &query[0]), CrashReportDatabase::kNoError); EXPECT_EQ(db()->LookUpCrashReport(reports[1].uuid, &query[1]), CrashReportDatabase::kNoError); EXPECT_EQ(db()->LookUpCrashReport(reports[2].uuid, &query[2]), CrashReportDatabase::kNoError); EXPECT_FALSE(query[0].uploaded); EXPECT_TRUE(query[1].uploaded); EXPECT_TRUE(query[2].uploaded); EXPECT_EQ(query[0].last_upload_attempt_time, 0); EXPECT_GE(query[1].last_upload_attempt_time, report_1_upload_time); EXPECT_EQ(query[2].last_upload_attempt_time, report_2_upload_time); EXPECT_EQ(query[0].upload_attempts, 0); EXPECT_EQ(query[1].upload_attempts, 3); EXPECT_EQ(query[2].upload_attempts, 1); } // This test covers both query functions since they are related. TEST_F(CrashReportDatabaseTest, GetCompletedAndNotUploadedReports) { std::vector reports(5); CreateCrashReport(&reports[0]); CreateCrashReport(&reports[1]); CreateCrashReport(&reports[2]); CreateCrashReport(&reports[3]); CreateCrashReport(&reports[4]); const UUID& report_0_uuid = reports[0].uuid; const UUID& report_1_uuid = reports[1].uuid; const UUID& report_2_uuid = reports[2].uuid; const UUID& report_3_uuid = reports[3].uuid; const UUID& report_4_uuid = reports[4].uuid; std::vector pending; EXPECT_EQ(db()->GetPendingReports(&pending), CrashReportDatabase::kNoError); std::vector completed; EXPECT_EQ(db()->GetCompletedReports(&completed), CrashReportDatabase::kNoError); EXPECT_EQ(pending.size(), reports.size()); EXPECT_EQ(completed.size(), 0u); // Upload one report successfully. UploadReport(report_1_uuid, true, "report1"); pending.clear(); EXPECT_EQ(db()->GetPendingReports(&pending), CrashReportDatabase::kNoError); completed.clear(); EXPECT_EQ(db()->GetCompletedReports(&completed), CrashReportDatabase::kNoError); EXPECT_EQ(pending.size(), 4u); ASSERT_EQ(completed.size(), 1u); for (const auto& report : pending) { EXPECT_NE(report.uuid, report_1_uuid); EXPECT_FALSE(report.file_path.empty()); } EXPECT_EQ(completed[0].uuid, report_1_uuid); EXPECT_EQ(completed[0].id, "report1"); EXPECT_EQ(completed[0].uploaded, true); EXPECT_GT(completed[0].last_upload_attempt_time, 0); EXPECT_EQ(completed[0].upload_attempts, 1); // Fail to upload one report. UploadReport(report_2_uuid, false, std::string()); pending.clear(); EXPECT_EQ(db()->GetPendingReports(&pending), CrashReportDatabase::kNoError); completed.clear(); EXPECT_EQ(db()->GetCompletedReports(&completed), CrashReportDatabase::kNoError); EXPECT_EQ(pending.size(), 4u); ASSERT_EQ(completed.size(), 1u); for (const auto& report : pending) { if (report.upload_attempts != 0) { EXPECT_EQ(report.uuid, report_2_uuid); EXPECT_GT(report.last_upload_attempt_time, 0); EXPECT_FALSE(report.uploaded); EXPECT_TRUE(report.id.empty()); } EXPECT_FALSE(report.file_path.empty()); } // Upload a second report. UploadReport(report_4_uuid, true, "report_4"); pending.clear(); EXPECT_EQ(db()->GetPendingReports(&pending), CrashReportDatabase::kNoError); completed.clear(); EXPECT_EQ(db()->GetCompletedReports(&completed), CrashReportDatabase::kNoError); EXPECT_EQ(pending.size(), 3u); ASSERT_EQ(completed.size(), 2u); // Succeed the failed report. UploadReport(report_2_uuid, true, "report 2"); pending.clear(); EXPECT_EQ(db()->GetPendingReports(&pending), CrashReportDatabase::kNoError); completed.clear(); EXPECT_EQ(db()->GetCompletedReports(&completed), CrashReportDatabase::kNoError); EXPECT_EQ(pending.size(), 2u); ASSERT_EQ(completed.size(), 3u); for (const auto& report : pending) { EXPECT_TRUE(report.uuid == report_0_uuid || report.uuid == report_3_uuid); EXPECT_FALSE(report.file_path.empty()); } // Skip upload for one report. EXPECT_EQ(db()->SkipReportUpload( report_3_uuid, Metrics::CrashSkippedReason::kUploadsDisabled), CrashReportDatabase::kNoError); pending.clear(); EXPECT_EQ(db()->GetPendingReports(&pending), CrashReportDatabase::kNoError); completed.clear(); EXPECT_EQ(db()->GetCompletedReports(&completed), CrashReportDatabase::kNoError); ASSERT_EQ(pending.size(), 1u); ASSERT_EQ(completed.size(), 4u); EXPECT_EQ(pending[0].uuid, report_0_uuid); for (const auto& report : completed) { if (report.uuid == report_3_uuid) { EXPECT_FALSE(report.uploaded); EXPECT_EQ(report.upload_attempts, 0); EXPECT_EQ(report.last_upload_attempt_time, 0); } else { EXPECT_TRUE(report.uploaded); EXPECT_GT(report.upload_attempts, 0); EXPECT_GT(report.last_upload_attempt_time, 0); } EXPECT_FALSE(report.file_path.empty()); } } TEST_F(CrashReportDatabaseTest, DuelingUploads) { CrashReportDatabase::Report report; CreateCrashReport(&report); std::unique_ptr upload_report; EXPECT_EQ(db()->GetReportForUploading(report.uuid, &upload_report), CrashReportDatabase::kNoError); std::unique_ptr upload_report_2; EXPECT_EQ(db()->GetReportForUploading(report.uuid, &upload_report_2), CrashReportDatabase::kBusyError); EXPECT_FALSE(upload_report_2); EXPECT_EQ(db()->RecordUploadComplete(std::move(upload_report), std::string()), CrashReportDatabase::kNoError); } TEST_F(CrashReportDatabaseTest, UploadAlreadyUploaded) { CrashReportDatabase::Report report; CreateCrashReport(&report); std::unique_ptr upload_report; EXPECT_EQ(db()->GetReportForUploading(report.uuid, &upload_report), CrashReportDatabase::kNoError); EXPECT_EQ(db()->RecordUploadComplete(std::move(upload_report), std::string()), CrashReportDatabase::kNoError); std::unique_ptr upload_report_2; EXPECT_EQ(db()->GetReportForUploading(report.uuid, &upload_report_2), CrashReportDatabase::kReportNotFound); EXPECT_FALSE(upload_report_2.get()); } TEST_F(CrashReportDatabaseTest, MoveDatabase) { std::unique_ptr new_report; EXPECT_EQ(db()->PrepareNewCrashReport(&new_report), CrashReportDatabase::kNoError); UUID uuid; EXPECT_EQ(db()->FinishedWritingCrashReport(std::move(new_report), &uuid), CrashReportDatabase::kNoError); RelocateDatabase(); CrashReportDatabase::Report report; EXPECT_EQ(db()->LookUpCrashReport(uuid, &report), CrashReportDatabase::kNoError); ExpectPreparedCrashReport(report); } TEST_F(CrashReportDatabaseTest, ReportRemoved) { std::unique_ptr new_report; EXPECT_EQ(db()->PrepareNewCrashReport(&new_report), CrashReportDatabase::kNoError); UUID uuid; EXPECT_EQ(db()->FinishedWritingCrashReport(std::move(new_report), &uuid), CrashReportDatabase::kNoError); CrashReportDatabase::Report report; EXPECT_EQ(db()->LookUpCrashReport(uuid, &report), CrashReportDatabase::kNoError); EXPECT_TRUE(LoggingRemoveFile(report.file_path)); EXPECT_EQ(db()->LookUpCrashReport(uuid, &report), CrashReportDatabase::kReportNotFound); } TEST_F(CrashReportDatabaseTest, DeleteReport) { CrashReportDatabase::Report keep_pending; CrashReportDatabase::Report delete_pending; CrashReportDatabase::Report keep_completed; CrashReportDatabase::Report delete_completed; CreateCrashReport(&keep_pending); CreateCrashReport(&delete_pending); CreateCrashReport(&keep_completed); CreateCrashReport(&delete_completed); EXPECT_TRUE(FileExists(keep_pending.file_path)); EXPECT_TRUE(FileExists(delete_pending.file_path)); EXPECT_TRUE(FileExists(keep_completed.file_path)); EXPECT_TRUE(FileExists(delete_completed.file_path)); UploadReport(keep_completed.uuid, true, "1"); UploadReport(delete_completed.uuid, true, "2"); EXPECT_EQ(db()->LookUpCrashReport(keep_completed.uuid, &keep_completed), CrashReportDatabase::kNoError); EXPECT_EQ(db()->LookUpCrashReport(delete_completed.uuid, &delete_completed), CrashReportDatabase::kNoError); EXPECT_TRUE(FileExists(keep_completed.file_path)); EXPECT_TRUE(FileExists(delete_completed.file_path)); EXPECT_EQ(db()->DeleteReport(delete_pending.uuid), CrashReportDatabase::kNoError); EXPECT_FALSE(FileExists(delete_pending.file_path)); EXPECT_EQ(db()->LookUpCrashReport(delete_pending.uuid, &delete_pending), CrashReportDatabase::kReportNotFound); EXPECT_EQ(db()->DeleteReport(delete_pending.uuid), CrashReportDatabase::kReportNotFound); EXPECT_EQ(db()->DeleteReport(delete_completed.uuid), CrashReportDatabase::kNoError); EXPECT_FALSE(FileExists(delete_completed.file_path)); EXPECT_EQ(db()->LookUpCrashReport(delete_completed.uuid, &delete_completed), CrashReportDatabase::kReportNotFound); EXPECT_EQ(db()->DeleteReport(delete_completed.uuid), CrashReportDatabase::kReportNotFound); EXPECT_EQ(db()->LookUpCrashReport(keep_pending.uuid, &keep_pending), CrashReportDatabase::kNoError); EXPECT_EQ(db()->LookUpCrashReport(keep_completed.uuid, &keep_completed), CrashReportDatabase::kNoError); } TEST_F(CrashReportDatabaseTest, DeleteReportEmptyingDatabase) { CrashReportDatabase::Report report; CreateCrashReport(&report); EXPECT_TRUE(FileExists(report.file_path)); UploadReport(report.uuid, true, "1"); EXPECT_EQ(db()->LookUpCrashReport(report.uuid, &report), CrashReportDatabase::kNoError); EXPECT_TRUE(FileExists(report.file_path)); // This causes an empty database to be written, make sure this is handled. EXPECT_EQ(db()->DeleteReport(report.uuid), CrashReportDatabase::kNoError); EXPECT_FALSE(FileExists(report.file_path)); } TEST_F(CrashReportDatabaseTest, ReadEmptyDatabase) { CrashReportDatabase::Report report; CreateCrashReport(&report); EXPECT_EQ(db()->DeleteReport(report.uuid), CrashReportDatabase::kNoError); // Deleting and the creating another report causes an empty database to be // loaded. Make sure this is handled. CrashReportDatabase::Report report2; CreateCrashReport(&report2); } TEST_F(CrashReportDatabaseTest, RequestUpload) { std::vector reports(2); CreateCrashReport(&reports[0]); CreateCrashReport(&reports[1]); const UUID& report_0_uuid = reports[0].uuid; const UUID& report_1_uuid = reports[1].uuid; // Skipped report gets back to pending state after RequestUpload is called. EXPECT_EQ(db()->SkipReportUpload( report_1_uuid, Metrics::CrashSkippedReason::kUploadsDisabled), CrashReportDatabase::kNoError); std::vector pending_reports; CrashReportDatabase::OperationStatus os = db()->GetPendingReports(&pending_reports); EXPECT_EQ(os, CrashReportDatabase::kNoError); ASSERT_EQ(pending_reports.size(), 1u); EXPECT_EQ(report_0_uuid, pending_reports[0].uuid); pending_reports.clear(); EXPECT_EQ(RequestUpload(report_1_uuid), CrashReportDatabase::kNoError); os = db()->GetPendingReports(&pending_reports); EXPECT_EQ(os, CrashReportDatabase::kNoError); ASSERT_EQ(pending_reports.size(), 2u); // Check individual reports. const CrashReportDatabase::Report* explicitly_requested_report; const CrashReportDatabase::Report* pending_report; if (pending_reports[0].uuid == report_0_uuid) { pending_report = &pending_reports[0]; explicitly_requested_report = &pending_reports[1]; } else { pending_report = &pending_reports[1]; explicitly_requested_report = &pending_reports[0]; } EXPECT_EQ(pending_report->uuid, report_0_uuid); EXPECT_FALSE(pending_report->upload_explicitly_requested); EXPECT_EQ(explicitly_requested_report->uuid, report_1_uuid); EXPECT_TRUE(explicitly_requested_report->upload_explicitly_requested); // Explicitly requested reports will not have upload_explicitly_requested bit // after getting skipped. EXPECT_EQ(db()->SkipReportUpload( report_1_uuid, Metrics::CrashSkippedReason::kUploadsDisabled), CrashReportDatabase::kNoError); CrashReportDatabase::Report report; EXPECT_EQ(db()->LookUpCrashReport(report_1_uuid, &report), CrashReportDatabase::kNoError); EXPECT_FALSE(report.upload_explicitly_requested); // Pending report gets correctly affected after RequestUpload is called. pending_reports.clear(); EXPECT_EQ(RequestUpload(report_0_uuid), CrashReportDatabase::kNoError); os = db()->GetPendingReports(&pending_reports); EXPECT_EQ(os, CrashReportDatabase::kNoError); EXPECT_EQ(pending_reports.size(), 1u); EXPECT_EQ(report_0_uuid, pending_reports[0].uuid); EXPECT_TRUE(pending_reports[0].upload_explicitly_requested); // Already uploaded report cannot be requested for the new upload. UploadReport(report_0_uuid, true, "1"); EXPECT_EQ(RequestUpload(report_0_uuid), CrashReportDatabase::kCannotRequestUpload); } TEST_F(CrashReportDatabaseTest, Attachments) { #if defined(OS_MACOSX) || defined(OS_WIN) // Attachments aren't supported on Mac and Windows yet. DISABLED_TEST(); #else std::unique_ptr new_report; ASSERT_EQ(db()->PrepareNewCrashReport(&new_report), CrashReportDatabase::kNoError); FileWriter* attach_some_file = new_report->AddAttachment("some_file"); ASSERT_NE(attach_some_file, nullptr); static constexpr char test_data[] = "test data"; attach_some_file->Write(test_data, sizeof(test_data)); FileWriter* failed_attach = new_report->AddAttachment("not/a valid fi!e"); EXPECT_EQ(failed_attach, nullptr); UUID expect_uuid = new_report->ReportID(); UUID uuid; ASSERT_EQ(db()->FinishedWritingCrashReport(std::move(new_report), &uuid), CrashReportDatabase::kNoError); EXPECT_EQ(uuid, expect_uuid); CrashReportDatabase::Report report; EXPECT_EQ(db()->LookUpCrashReport(uuid, &report), CrashReportDatabase::kNoError); ExpectPreparedCrashReport(report); std::vector reports; EXPECT_EQ(db()->GetPendingReports(&reports), CrashReportDatabase::kNoError); ASSERT_EQ(reports.size(), 1u); EXPECT_EQ(reports[0].uuid, report.uuid); std::unique_ptr upload_report; ASSERT_EQ(db()->GetReportForUploading(reports[0].uuid, &upload_report), CrashReportDatabase::kNoError); std::map result_attachments = upload_report->GetAttachments(); EXPECT_EQ(result_attachments.size(), 1u); EXPECT_NE(result_attachments.find("some_file"), result_attachments.end()); char result_buffer[sizeof(test_data)]; result_attachments["some_file"]->Read(result_buffer, sizeof(result_buffer)); EXPECT_EQ(memcmp(test_data, result_buffer, sizeof(test_data)), 0); #endif } TEST_F(CrashReportDatabaseTest, OrphanedAttachments) { #if defined(OS_MACOSX) || defined(OS_WIN) // Attachments aren't supported on Mac and Windows yet. DISABLED_TEST(); #else // TODO: This is using paths that are specific to the generic implementation // and will need to be generalized for Mac and Windows. std::unique_ptr new_report; ASSERT_EQ(db()->PrepareNewCrashReport(&new_report), CrashReportDatabase::kNoError); FileWriter* file1 = new_report->AddAttachment("file1"); ASSERT_NE(file1, nullptr); FileWriter* file2 = new_report->AddAttachment("file2"); ASSERT_NE(file2, nullptr); UUID expect_uuid = new_report->ReportID(); UUID uuid; ASSERT_EQ(db()->FinishedWritingCrashReport(std::move(new_report), &uuid), CrashReportDatabase::kNoError); EXPECT_EQ(uuid, expect_uuid); CrashReportDatabase::Report report; ASSERT_EQ(db()->LookUpCrashReport(uuid, &report), CrashReportDatabase::kNoError); ASSERT_TRUE(LoggingRemoveFile(report.file_path)); ASSERT_TRUE(LoggingRemoveFile(base::FilePath( report.file_path.RemoveFinalExtension().value() + ".meta"))); ASSERT_EQ(db()->LookUpCrashReport(uuid, &report), CrashReportDatabase::kReportNotFound); base::FilePath report_attachments_dir( path().Append("attachments").Append(uuid.ToString())); base::FilePath file_path1(report_attachments_dir.Append("file1")); base::FilePath file_path2(report_attachments_dir.Append("file2")); EXPECT_TRUE(FileExists(file_path1)); EXPECT_TRUE(FileExists(file_path1)); EXPECT_EQ(db()->CleanDatabase(0), 0); EXPECT_FALSE(FileExists(file_path1)); EXPECT_FALSE(FileExists(file_path2)); EXPECT_FALSE(FileExists(report_attachments_dir)); #endif } // This test uses knowledge of the database format to break it, so it only // applies to the unfified database implementation. #if !defined(OS_MACOSX) && !defined(OS_WIN) TEST_F(CrashReportDatabaseTest, CleanBrokenDatabase) { // Remove report files if metadata goes missing. CrashReportDatabase::Report report; ASSERT_NO_FATAL_FAILURE(CreateCrashReport(&report)); const base::FilePath metadata( report.file_path.RemoveFinalExtension().value() + FILE_PATH_LITERAL(".meta")); ASSERT_TRUE(PathExists(report.file_path)); ASSERT_TRUE(PathExists(metadata)); ASSERT_TRUE(LoggingRemoveFile(metadata)); EXPECT_EQ(db()->CleanDatabase(0), 1); EXPECT_FALSE(PathExists(report.file_path)); EXPECT_FALSE(PathExists(metadata)); // Remove metadata files if reports go missing. ASSERT_NO_FATAL_FAILURE(CreateCrashReport(&report)); const base::FilePath metadata2( report.file_path.RemoveFinalExtension().value() + FILE_PATH_LITERAL(".meta")); ASSERT_TRUE(PathExists(report.file_path)); ASSERT_TRUE(PathExists(metadata2)); ASSERT_TRUE(LoggingRemoveFile(report.file_path)); EXPECT_EQ(db()->CleanDatabase(0), 1); EXPECT_FALSE(PathExists(report.file_path)); EXPECT_FALSE(PathExists(metadata2)); // Remove stale new files. std::unique_ptr new_report; EXPECT_EQ(db()->PrepareNewCrashReport(&new_report), CrashReportDatabase::kNoError); new_report->Writer()->Close(); EXPECT_EQ(db()->CleanDatabase(0), 1); // Remove stale lock files and their associated reports. ASSERT_NO_FATAL_FAILURE(CreateCrashReport(&report)); const base::FilePath metadata3( report.file_path.RemoveFinalExtension().value() + FILE_PATH_LITERAL(".meta")); ASSERT_TRUE(PathExists(report.file_path)); ASSERT_TRUE(PathExists(metadata3)); const base::FilePath lockpath( report.file_path.RemoveFinalExtension().value() + FILE_PATH_LITERAL(".lock")); ScopedFileHandle handle(LoggingOpenFileForWrite( lockpath, FileWriteMode::kCreateOrFail, FilePermissions::kOwnerOnly)); ASSERT_TRUE(handle.is_valid()); time_t expired_timestamp = time(nullptr) - 60 * 60 * 24 * 3; ASSERT_TRUE(LoggingWriteFile( handle.get(), &expired_timestamp, sizeof(expired_timestamp))); ASSERT_TRUE(LoggingCloseFile(handle.get())); ignore_result(handle.release()); EXPECT_EQ(db()->CleanDatabase(0), 1); EXPECT_FALSE(PathExists(report.file_path)); EXPECT_FALSE(PathExists(metadata3)); } #endif // !OS_MACOSX && !OS_WIN } // namespace } // namespace test } // namespace crashpad