| Index: client/crash_report_database_test.cc
|
| diff --git a/client/crash_report_database_test.cc b/client/crash_report_database_test.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..67f52b555b1a013485446d7592395d64f2dbc838
|
| --- /dev/null
|
| +++ b/client/crash_report_database_test.cc
|
| @@ -0,0 +1,392 @@
|
| +// 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 <sys/stat.h>
|
| +
|
| +#include "gtest/gtest.h"
|
| +#include "util/file/file_io.h"
|
| +#include "util/test/scoped_temp_dir.h"
|
| +
|
| +namespace crashpad {
|
| +namespace test {
|
| +namespace {
|
| +
|
| +bool FileExistsAtPath(const base::FilePath& path) {
|
| +#if defined(OS_POSIX)
|
| + struct stat st;
|
| + return lstat(path.value().c_str(), &st) == 0;
|
| +#else
|
| +#error "Not implemented"
|
| +#endif
|
| +}
|
| +
|
| +void CreateFile(const base::FilePath& path) {
|
| + FileHandle handle = LoggingOpenFileForWrite(path,
|
| + FileWriteMode::kCreateOrFail,
|
| + FilePermissions::kWorldReadable);
|
| + ASSERT_GE(handle, 0);
|
| + ASSERT_TRUE(
|
| + LoggingWriteFile(handle, path.value().c_str(), path.value().length()));
|
| + ASSERT_TRUE(LoggingCloseFile(handle));
|
| +}
|
| +
|
| +class CrashReportDatabaseTest : public testing::Test {
|
| + protected:
|
| + // testing::Test:
|
| + void SetUp() override {
|
| + db_ = CrashReportDatabase::Initialize(path());
|
| + ASSERT_TRUE(db_.get());
|
| + }
|
| +
|
| + void ResetDatabase() {
|
| + db_.reset();
|
| + }
|
| +
|
| + CrashReportDatabase* db() const { return db_.get(); }
|
| + const base::FilePath& path() const { return temp_dir_.path(); }
|
| +
|
| + void CreateCrashReport(CrashReportDatabase::Report* report) {
|
| + FileHandle handle;
|
| + EXPECT_EQ(CrashReportDatabase::kNoError,
|
| + db_->PrepareNewCrashReport(&handle));
|
| + const char kTest[] = "test";
|
| + ASSERT_TRUE(LoggingWriteFile(handle, kTest, sizeof(kTest)));
|
| +
|
| + UUID uuid;
|
| + EXPECT_EQ(CrashReportDatabase::kNoError,
|
| + db_->FinishedWritingCrashReport(handle, &uuid));
|
| +
|
| + EXPECT_EQ(CrashReportDatabase::kNoError,
|
| + db_->LookUpCrashReport(uuid, report));
|
| + ExpectPreparedCrashReport(*report);
|
| + ASSERT_TRUE(FileExistsAtPath(report->file_path));
|
| + }
|
| +
|
| + void UploadReport(const UUID& uuid, bool successful, const std::string& id) {
|
| + const CrashReportDatabase::Report* report = nullptr;
|
| + EXPECT_EQ(CrashReportDatabase::kNoError,
|
| + db_->GetReportForUploading(uuid, &report));
|
| + EXPECT_TRUE(report);
|
| + EXPECT_NE(UUID(), report->uuid);
|
| + EXPECT_FALSE(report->file_path.empty());
|
| + EXPECT_TRUE(FileExistsAtPath(report->file_path))
|
| + << report->file_path.value();
|
| + EXPECT_GT(report->creation_time, 0);
|
| + EXPECT_EQ(CrashReportDatabase::kNoError,
|
| + db_->RecordUploadAttempt(report, successful, id));
|
| + }
|
| +
|
| + void ExpectPreparedCrashReport(const CrashReportDatabase::Report& report) {
|
| + EXPECT_NE(UUID(), report.uuid);
|
| + EXPECT_FALSE(report.file_path.empty());
|
| + EXPECT_TRUE(FileExistsAtPath(report.file_path)) << report.file_path.value();
|
| + EXPECT_TRUE(report.id.empty());
|
| + EXPECT_GT(report.creation_time, 0);
|
| + EXPECT_FALSE(report.uploaded);
|
| + EXPECT_EQ(0, report.last_upload_attempt_time);
|
| + EXPECT_EQ(0, report.upload_attempts);
|
| + }
|
| +
|
| + private:
|
| + ScopedTempDir temp_dir_;
|
| + scoped_ptr<CrashReportDatabase> db_;
|
| +};
|
| +
|
| +TEST_F(CrashReportDatabaseTest, Initialize) {
|
| + // Initialize the database for the first time, creating it.
|
| + EXPECT_TRUE(db());
|
| +
|
| + // Close and reopen the database at the same path.
|
| + ResetDatabase();
|
| + EXPECT_FALSE(db());
|
| + auto db = CrashReportDatabase::Initialize(path());
|
| + EXPECT_TRUE(db.get());
|
| +
|
| + std::vector<const CrashReportDatabase::Report> reports;
|
| + EXPECT_EQ(CrashReportDatabase::kNoError, db->GetNotUploadedReports(&reports));
|
| + EXPECT_TRUE(reports.empty());
|
| + EXPECT_EQ(CrashReportDatabase::kNoError, db->GetUploadedReports(&reports));
|
| + EXPECT_TRUE(reports.empty());
|
| +}
|
| +
|
| +TEST_F(CrashReportDatabaseTest, NewCrashReport) {
|
| + FileHandle handle;
|
| + EXPECT_EQ(CrashReportDatabase::kNoError,
|
| + db()->PrepareNewCrashReport(&handle));
|
| + UUID uuid;
|
| + EXPECT_EQ(CrashReportDatabase::kNoError,
|
| + db()->FinishedWritingCrashReport(handle, &uuid));
|
| +
|
| + CrashReportDatabase::Report report;
|
| + EXPECT_EQ(CrashReportDatabase::kNoError,
|
| + db()->LookUpCrashReport(uuid, &report));
|
| + ExpectPreparedCrashReport(report);
|
| +
|
| + std::vector<const CrashReportDatabase::Report> reports;
|
| + EXPECT_EQ(CrashReportDatabase::kNoError,
|
| + db()->GetNotUploadedReports(&reports));
|
| + ASSERT_EQ(1u, reports.size());
|
| + EXPECT_EQ(report.uuid, reports[0].uuid);
|
| +
|
| + reports.clear();
|
| + EXPECT_EQ(CrashReportDatabase::kNoError,
|
| + db()->GetUploadedReports(&reports));
|
| + EXPECT_TRUE(reports.empty());
|
| +}
|
| +
|
| +TEST_F(CrashReportDatabaseTest, LookUpCrashReport) {
|
| + UUID uuid;
|
| +
|
| + {
|
| + CrashReportDatabase::Report report;
|
| + CreateCrashReport(&report);
|
| + uuid = report.uuid;
|
| + }
|
| +
|
| + {
|
| + CrashReportDatabase::Report report;
|
| + EXPECT_EQ(CrashReportDatabase::kNoError,
|
| + db()->LookUpCrashReport(uuid, &report));
|
| + EXPECT_EQ(uuid, report.uuid);
|
| + EXPECT_NE(std::string::npos, report.file_path.value().find(path().value()));
|
| + EXPECT_EQ("", report.id);
|
| + EXPECT_FALSE(report.uploaded);
|
| + EXPECT_EQ(0, report.last_upload_attempt_time);
|
| + EXPECT_EQ(0, report.upload_attempts);
|
| + }
|
| +
|
| + UploadReport(uuid, true, "test");
|
| +
|
| + {
|
| + CrashReportDatabase::Report report;
|
| + EXPECT_EQ(CrashReportDatabase::kNoError,
|
| + db()->LookUpCrashReport(uuid, &report));
|
| + EXPECT_EQ(uuid, report.uuid);
|
| + EXPECT_NE(std::string::npos, report.file_path.value().find(path().value()));
|
| + EXPECT_EQ("test", report.id);
|
| + EXPECT_TRUE(report.uploaded);
|
| + EXPECT_NE(0, report.last_upload_attempt_time);
|
| + EXPECT_EQ(1, report.upload_attempts);
|
| + }
|
| +}
|
| +
|
| +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, "");
|
| + UploadReport(reports[2].uuid, true, "abc123");
|
| +
|
| + std::vector<CrashReportDatabase::Report> query(3);
|
| +
|
| + EXPECT_EQ(CrashReportDatabase::kNoError,
|
| + db()->LookUpCrashReport(reports[0].uuid, &query[0]));
|
| + EXPECT_EQ(CrashReportDatabase::kNoError,
|
| + db()->LookUpCrashReport(reports[1].uuid, &query[1]));
|
| + EXPECT_EQ(CrashReportDatabase::kNoError,
|
| + db()->LookUpCrashReport(reports[2].uuid, &query[2]));
|
| +
|
| + EXPECT_EQ("", query[0].id);
|
| + EXPECT_EQ("", query[1].id);
|
| + EXPECT_EQ("abc123", query[2].id);
|
| +
|
| + EXPECT_FALSE(query[0].uploaded);
|
| + EXPECT_FALSE(query[1].uploaded);
|
| + EXPECT_TRUE(query[2].uploaded);
|
| +
|
| + EXPECT_EQ(0, query[0].last_upload_attempt_time);
|
| + EXPECT_NE(0, query[1].last_upload_attempt_time);
|
| + EXPECT_NE(0, query[2].last_upload_attempt_time);
|
| +
|
| + EXPECT_EQ(0, query[0].upload_attempts);
|
| + EXPECT_EQ(1, query[1].upload_attempts);
|
| + EXPECT_EQ(1, query[2].upload_attempts);
|
| +
|
| + // Attempt to upload and fail again.
|
| + UploadReport(reports[1].uuid, false, "");
|
| +
|
| + time_t report_2_upload_time = query[2].last_upload_attempt_time;
|
| +
|
| + EXPECT_EQ(CrashReportDatabase::kNoError,
|
| + db()->LookUpCrashReport(reports[0].uuid, &query[0]));
|
| + EXPECT_EQ(CrashReportDatabase::kNoError,
|
| + db()->LookUpCrashReport(reports[1].uuid, &query[1]));
|
| + EXPECT_EQ(CrashReportDatabase::kNoError,
|
| + db()->LookUpCrashReport(reports[2].uuid, &query[2]));
|
| +
|
| + EXPECT_FALSE(query[0].uploaded);
|
| + EXPECT_FALSE(query[1].uploaded);
|
| + EXPECT_TRUE(query[2].uploaded);
|
| +
|
| + EXPECT_EQ(0, query[0].last_upload_attempt_time);
|
| + EXPECT_GE(query[1].last_upload_attempt_time, report_2_upload_time);
|
| + EXPECT_EQ(report_2_upload_time, query[2].last_upload_attempt_time);
|
| +
|
| + EXPECT_EQ(0, query[0].upload_attempts);
|
| + EXPECT_EQ(2, query[1].upload_attempts);
|
| + EXPECT_EQ(1, query[2].upload_attempts);
|
| +
|
| + // 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(CrashReportDatabase::kNoError,
|
| + db()->LookUpCrashReport(reports[0].uuid, &query[0]));
|
| + EXPECT_EQ(CrashReportDatabase::kNoError,
|
| + db()->LookUpCrashReport(reports[1].uuid, &query[1]));
|
| + EXPECT_EQ(CrashReportDatabase::kNoError,
|
| + db()->LookUpCrashReport(reports[2].uuid, &query[2]));
|
| +
|
| + EXPECT_FALSE(query[0].uploaded);
|
| + EXPECT_TRUE(query[1].uploaded);
|
| + EXPECT_TRUE(query[2].uploaded);
|
| +
|
| + EXPECT_EQ(0, query[0].last_upload_attempt_time);
|
| + EXPECT_GE(query[1].last_upload_attempt_time, report_1_upload_time);
|
| + EXPECT_EQ(report_2_upload_time, query[2].last_upload_attempt_time);
|
| +
|
| + EXPECT_EQ(0, query[0].upload_attempts);
|
| + EXPECT_EQ(3, query[1].upload_attempts);
|
| + EXPECT_EQ(1, query[2].upload_attempts);
|
| +}
|
| +
|
| +// This test covers both query functions since they are related.
|
| +TEST_F(CrashReportDatabaseTest, GetUploadedAndNotUploadedReports) {
|
| + 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<const CrashReportDatabase::Report> not_uploaded;
|
| + EXPECT_EQ(CrashReportDatabase::kNoError,
|
| + db()->GetNotUploadedReports(¬_uploaded));
|
| +
|
| + std::vector<const CrashReportDatabase::Report> uploaded;
|
| + EXPECT_EQ(CrashReportDatabase::kNoError,
|
| + db()->GetUploadedReports(&uploaded));
|
| +
|
| + EXPECT_EQ(reports.size(), not_uploaded.size());
|
| + EXPECT_EQ(0u, uploaded.size());
|
| +
|
| + // Upload one report successfully.
|
| + UploadReport(report_1_uuid, true, "report1");
|
| +
|
| + not_uploaded.clear();
|
| + EXPECT_EQ(CrashReportDatabase::kNoError,
|
| + db()->GetNotUploadedReports(¬_uploaded));
|
| + uploaded.clear();
|
| + EXPECT_EQ(CrashReportDatabase::kNoError,
|
| + db()->GetUploadedReports(&uploaded));
|
| +
|
| + EXPECT_EQ(4u, not_uploaded.size());
|
| + ASSERT_EQ(1u, uploaded.size());
|
| +
|
| + for (const auto& report : not_uploaded)
|
| + EXPECT_NE(report_1_uuid, report.uuid);
|
| + EXPECT_EQ(report_1_uuid, uploaded[0].uuid);
|
| + EXPECT_EQ("report1", uploaded[0].id);
|
| + EXPECT_EQ(true, uploaded[0].uploaded);
|
| + EXPECT_GT(uploaded[0].last_upload_attempt_time, 0);
|
| + EXPECT_EQ(1, uploaded[0].upload_attempts);
|
| +
|
| + const CrashReportDatabase::Report uploaded_report_1 = uploaded[0];
|
| +
|
| + // Fail to upload one report.
|
| + UploadReport(report_2_uuid, false, "");
|
| +
|
| + not_uploaded.clear();
|
| + EXPECT_EQ(CrashReportDatabase::kNoError,
|
| + db()->GetNotUploadedReports(¬_uploaded));
|
| + uploaded.clear();
|
| + EXPECT_EQ(CrashReportDatabase::kNoError,
|
| + db()->GetUploadedReports(&uploaded));
|
| +
|
| + EXPECT_EQ(4u, not_uploaded.size());
|
| + ASSERT_EQ(1u, uploaded.size());
|
| +
|
| + for (const auto& report : not_uploaded) {
|
| + if (report.upload_attempts != 0) {
|
| + EXPECT_EQ(report_2_uuid, report.uuid);
|
| + EXPECT_GT(report.last_upload_attempt_time, 0);
|
| + EXPECT_FALSE(report.uploaded);
|
| + EXPECT_TRUE(report.id.empty());
|
| + }
|
| + }
|
| +
|
| + // Upload a second report.
|
| + UploadReport(report_4_uuid, true, "report_4");
|
| +
|
| + not_uploaded.clear();
|
| + EXPECT_EQ(CrashReportDatabase::kNoError,
|
| + db()->GetNotUploadedReports(¬_uploaded));
|
| + uploaded.clear();
|
| + EXPECT_EQ(CrashReportDatabase::kNoError,
|
| + db()->GetUploadedReports(&uploaded));
|
| +
|
| + EXPECT_EQ(3u, not_uploaded.size());
|
| + ASSERT_EQ(2u, uploaded.size());
|
| +
|
| + // Succeed the failed report.
|
| + UploadReport(report_2_uuid, true, "report 2");
|
| +
|
| + not_uploaded.clear();
|
| + EXPECT_EQ(CrashReportDatabase::kNoError,
|
| + db()->GetNotUploadedReports(¬_uploaded));
|
| + uploaded.clear();
|
| + EXPECT_EQ(CrashReportDatabase::kNoError,
|
| + db()->GetUploadedReports(&uploaded));
|
| +
|
| + EXPECT_EQ(2u, not_uploaded.size());
|
| + ASSERT_EQ(3u, uploaded.size());
|
| +
|
| + for (const auto& report : not_uploaded) {
|
| + EXPECT_TRUE(report.uuid == report_0_uuid ||
|
| + report.uuid == report_3_uuid);
|
| + }
|
| +}
|
| +
|
| +TEST_F(CrashReportDatabaseTest, DuelingUploads) {
|
| + CrashReportDatabase::Report report;
|
| + CreateCrashReport(&report);
|
| +
|
| + const CrashReportDatabase::Report* upload_report;
|
| + EXPECT_EQ(CrashReportDatabase::kNoError,
|
| + db()->GetReportForUploading(report.uuid, &upload_report));
|
| +
|
| + const CrashReportDatabase::Report* upload_report_2 = nullptr;
|
| + EXPECT_EQ(CrashReportDatabase::kBusyError,
|
| + db()->GetReportForUploading(report.uuid, &upload_report_2));
|
| + EXPECT_FALSE(upload_report_2);
|
| +
|
| + EXPECT_EQ(CrashReportDatabase::kNoError,
|
| + db()->RecordUploadAttempt(upload_report, true, ""));
|
| +}
|
| +
|
| +} // namespace
|
| +} // namespace test
|
| +} // namespace crashpad
|
|
|