| // 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/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<CrashReportDatabase::NewReport> new_report; |
| ASSERT_EQ(db_->PrepareNewCrashReport(&new_report), |
| CrashReportDatabase::kNoError); |
| static constexpr char kTest[] = "test"; |
| ASSERT_TRUE(new_report->Writer()->Write(kTest, sizeof(kTest))); |
| |
| char contents[sizeof(kTest)]; |
| FileReaderInterface* reader = new_report->Reader(); |
| ASSERT_TRUE(reader->ReadExactly(contents, sizeof(contents))); |
| EXPECT_EQ(memcmp(contents, kTest, sizeof(contents)), 0); |
| EXPECT_EQ(reader->ReadExactly(contents, 1), 0); |
| |
| 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<const CrashReportDatabase::UploadReport> 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); |
| EXPECT_GE(report.total_size, 0u); |
| } |
| |
| 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<CrashReportDatabase> 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<CrashReportDatabase::Report> 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<CrashReportDatabase::NewReport> 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<CrashReportDatabase::Report> 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<CrashReportDatabase::Report> 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<CrashReportDatabase::Report> 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<CrashReportDatabase::Report> 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<CrashReportDatabase::Report> pending; |
| EXPECT_EQ(db()->GetPendingReports(&pending), CrashReportDatabase::kNoError); |
| |
| std::vector<CrashReportDatabase::Report> 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<const CrashReportDatabase::UploadReport> upload_report; |
| EXPECT_EQ(db()->GetReportForUploading(report.uuid, &upload_report), |
| CrashReportDatabase::kNoError); |
| |
| std::unique_ptr<const CrashReportDatabase::UploadReport> 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<const CrashReportDatabase::UploadReport> 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<const CrashReportDatabase::UploadReport> 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<CrashReportDatabase::NewReport> 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<CrashReportDatabase::NewReport> 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<CrashReportDatabase::Report> 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<CrashReportDatabase::Report> 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. |
| GTEST_SKIP(); |
| #else |
| std::unique_ptr<CrashReportDatabase::NewReport> 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<CrashReportDatabase::Report> reports; |
| EXPECT_EQ(db()->GetPendingReports(&reports), CrashReportDatabase::kNoError); |
| ASSERT_EQ(reports.size(), 1u); |
| EXPECT_EQ(reports[0].uuid, report.uuid); |
| |
| std::unique_ptr<const CrashReportDatabase::UploadReport> upload_report; |
| ASSERT_EQ(db()->GetReportForUploading(reports[0].uuid, &upload_report), |
| CrashReportDatabase::kNoError); |
| std::map<std::string, FileReader*> 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. |
| GTEST_SKIP(); |
| #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<CrashReportDatabase::NewReport> 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<CrashReportDatabase::NewReport> 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 |
| |
| TEST_F(CrashReportDatabaseTest, TotalSize_MainReportOnly) { |
| std::unique_ptr<CrashReportDatabase::NewReport> new_report; |
| ASSERT_EQ(db()->PrepareNewCrashReport(&new_report), |
| CrashReportDatabase::kNoError); |
| |
| // Main report. |
| static constexpr char main_report_data[] = "dlbvandslhb"; |
| ASSERT_TRUE( |
| new_report->Writer()->Write(main_report_data, sizeof(main_report_data))); |
| |
| UUID uuid; |
| ASSERT_EQ(db()->FinishedWritingCrashReport(std::move(new_report), &uuid), |
| CrashReportDatabase::kNoError); |
| |
| CrashReportDatabase::Report report; |
| ASSERT_EQ(db()->LookUpCrashReport(uuid, &report), |
| CrashReportDatabase::kNoError); |
| |
| EXPECT_EQ(report.total_size, sizeof(main_report_data)); |
| } |
| |
| TEST_F(CrashReportDatabaseTest, GetReportSize_RightSizeWithAttachments) { |
| #if defined(OS_MACOSX) || defined(OS_WIN) |
| // Attachments aren't supported on Mac and Windows yet. |
| return; |
| #else |
| std::unique_ptr<CrashReportDatabase::NewReport> new_report; |
| ASSERT_EQ(db()->PrepareNewCrashReport(&new_report), |
| CrashReportDatabase::kNoError); |
| |
| // Main report. |
| static constexpr char main_report_data[] = "dlbvandslhb"; |
| ASSERT_TRUE( |
| new_report->Writer()->Write(main_report_data, sizeof(main_report_data))); |
| |
| // First attachment. |
| FileWriter* attachment_1 = new_report->AddAttachment("my_attachment_1"); |
| ASSERT_NE(attachment_1, nullptr); |
| static constexpr char attachment_1_data[] = "vKDnidhvbiudshoihbvdsoiuh nhh"; |
| attachment_1->Write(attachment_1_data, sizeof(attachment_1_data)); |
| |
| // Second attachment. |
| FileWriter* attachment_2 = new_report->AddAttachment("my_attachment_2"); |
| ASSERT_NE(attachment_2, nullptr); |
| static constexpr char attachment_2_data[] = "sgvsvgusiyguysigfkhpmo-["; |
| attachment_2->Write(attachment_2_data, sizeof(attachment_2_data)); |
| |
| UUID uuid; |
| ASSERT_EQ(db()->FinishedWritingCrashReport(std::move(new_report), &uuid), |
| CrashReportDatabase::kNoError); |
| |
| CrashReportDatabase::Report report; |
| ASSERT_EQ(db()->LookUpCrashReport(uuid, &report), |
| CrashReportDatabase::kNoError); |
| EXPECT_EQ(report.total_size, |
| sizeof(main_report_data) + sizeof(attachment_1_data) + |
| sizeof(attachment_2_data)); |
| #endif |
| } |
| |
| } // namespace |
| } // namespace test |
| } // namespace crashpad |