Index: client/prune_crash_reports_test.cc |
diff --git a/client/prune_crash_reports_test.cc b/client/prune_crash_reports_test.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..2da7a88dc8f44da72397b853cc7c4335d48ccca5 |
--- /dev/null |
+++ b/client/prune_crash_reports_test.cc |
@@ -0,0 +1,264 @@ |
+// 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/prune_crash_reports.h" |
+ |
+#include <stdlib.h> |
+ |
+#include <algorithm> |
+#include <string> |
+#include <vector> |
+ |
+#include "gtest/gtest.h" |
+#include "test/scoped_temp_dir.h" |
+#include "util/file/file_io.h" |
+ |
+namespace crashpad { |
+namespace test { |
+namespace { |
+ |
+class TestDatabase : public CrashReportDatabase { |
Mark Mentovai
2015/10/07 03:54:28
I don’t know if gmock would make the TestDatabase
Robert Sesek
2015/10/07 16:24:35
Gmock is never easy to deal with. But it is less c
|
+ public: |
+ TestDatabase() |
+ : pending_reports_(), completed_reports_(), deleted_reports_() {} |
+ ~TestDatabase() override {} |
+ |
+ std::vector<Report>* pending_reports() { return &pending_reports_; } |
+ std::vector<Report>* completed_reports() { return &completed_reports_; } |
+ |
+ const std::vector<UUID>& deleted_reports() { return deleted_reports_; } |
+ |
+ // CrashReportDatabase: |
+ Settings* GetSettings() override { return nullptr; } |
+ OperationStatus PrepareNewCrashReport(NewReport** report) override { |
+ return kDatabaseError; |
+ } |
+ |
+ OperationStatus FinishedWritingCrashReport(NewReport* report, |
+ UUID* uuid) override { |
+ return kDatabaseError; |
+ } |
+ OperationStatus ErrorWritingCrashReport(NewReport* report) override { |
+ return kDatabaseError; |
+ } |
+ OperationStatus LookUpCrashReport(const UUID& uuid, |
+ Report* report) override { |
+ return kDatabaseError; |
+ } |
+ OperationStatus GetPendingReports(std::vector<Report>* reports) override { |
+ *reports = pending_reports_; |
+ return kNoError; |
+ } |
+ OperationStatus GetCompletedReports(std::vector<Report>* reports) override { |
+ *reports = completed_reports_; |
+ return kNoError; |
+ } |
+ OperationStatus GetReportForUploading(const UUID& uuid, |
+ const Report** report) override { |
+ return kDatabaseError; |
+ } |
+ OperationStatus RecordUploadAttempt(const Report* report, |
+ bool successful, |
+ const std::string& id) override { |
+ return kDatabaseError; |
+ } |
+ OperationStatus SkipReportUpload(const UUID& uuid) override { |
+ return kDatabaseError; |
+ } |
+ OperationStatus DeleteReport(const UUID& uuid) override { |
+ auto condition = [uuid](const CrashReportDatabase::Report& report) { |
+ return report.uuid == uuid; |
+ }; |
+ |
+ auto it = std::remove_if(pending_reports_.begin(), pending_reports_.end(), |
+ condition); |
+ if (it != pending_reports_.end()) |
+ pending_reports_.erase(it); |
+ |
+ it = std::remove_if(completed_reports_.begin(), completed_reports_.end(), |
+ condition); |
+ if (it != completed_reports_.end()) |
+ completed_reports_.erase(it); |
+ |
+ deleted_reports_.push_back(uuid); |
+ return kNoError; |
+ } |
+ |
+ private: |
+ std::vector<Report> pending_reports_; |
+ std::vector<Report> completed_reports_; |
+ std::vector<UUID> deleted_reports_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(TestDatabase); |
+}; |
+ |
+time_t NDaysAgo(int num_days) { |
+ return time(nullptr) - (num_days * 60 * 60 * 24); |
+} |
+ |
+TEST(PruneCrashReports, AgeCondition) { |
+ CrashReportDatabase::Report report_80_days; |
+ report_80_days.creation_time = NDaysAgo(80); |
+ |
+ CrashReportDatabase::Report report_10_days; |
+ report_10_days.creation_time = NDaysAgo(10); |
+ |
+ CrashReportDatabase::Report report_30_days; |
+ report_30_days.creation_time = NDaysAgo(30); |
+ |
+ AgePruneCondition condition(30); |
+ EXPECT_TRUE(condition.ShouldPruneReport(report_80_days)); |
+ EXPECT_FALSE(condition.ShouldPruneReport(report_10_days)); |
+ EXPECT_FALSE(condition.ShouldPruneReport(report_30_days)); |
+} |
+ |
+TEST(PruneCrashReports, SizeCondition) { |
+ ScopedTempDir temp_dir; |
+ |
+ CrashReportDatabase::Report report_128; |
+ report_128.file_path = temp_dir.path().Append(FILE_PATH_LITERAL("file128")); |
+ CrashReportDatabase::Report report_1024; |
+ report_1024.file_path = temp_dir.path().Append(FILE_PATH_LITERAL("file1024")); |
+ |
+ { |
+ ScopedFileHandle scoped_file_128( |
+ LoggingOpenFileForWrite(report_128.file_path, |
+ FileWriteMode::kCreateOrFail, |
+ FilePermissions::kOwnerOnly)); |
+ ASSERT_TRUE(scoped_file_128.is_valid()); |
+ |
+ std::string string; |
+ for (int i = 0; i < 128; ++i) |
+ string.push_back(i); |
scottmg
2015/10/06 22:22:07
warning converting int to char here on Windows.
Robert Sesek
2015/10/07 16:24:35
Done.
|
+ ASSERT_TRUE(LoggingWriteFile(scoped_file_128.get(), |
+ string.c_str(), string.length())); |
+ |
+ ScopedFileHandle scoped_file_1024( |
+ LoggingOpenFileForWrite(report_1024.file_path, |
+ FileWriteMode::kCreateOrFail, |
+ FilePermissions::kOwnerOnly)); |
+ ASSERT_TRUE(scoped_file_1024.is_valid()); |
+ |
+ for (int i = 0; i < 1024; i += string.size()) { |
+ ASSERT_TRUE(LoggingWriteFile(scoped_file_1024.get(), |
+ string.c_str(), string.length())); |
+ } |
+ } |
+ |
+ { |
+ DatabaseSizePruneCondition condition(512); |
+ EXPECT_FALSE(condition.ShouldPruneReport(report_128)); |
+ EXPECT_FALSE(condition.ShouldPruneReport(report_128)); |
+ EXPECT_FALSE(condition.ShouldPruneReport(report_128)); |
+ EXPECT_FALSE(condition.ShouldPruneReport(report_128)); |
+ EXPECT_TRUE(condition.ShouldPruneReport(report_128)); |
+ } |
+ |
+ { |
+ DatabaseSizePruneCondition condition(512); |
+ EXPECT_TRUE(condition.ShouldPruneReport(report_1024)); |
+ } |
+ |
+ { |
+ DatabaseSizePruneCondition condition(2100); |
+ EXPECT_FALSE(condition.ShouldPruneReport(report_1024)); |
+ EXPECT_FALSE(condition.ShouldPruneReport(report_1024)); |
+ EXPECT_TRUE(condition.ShouldPruneReport(report_128)); |
+ } |
+} |
+ |
+class StaticCondition : public PruneCondition { |
+ public: |
+ explicit StaticCondition(bool value) : value_(value) {} |
+ ~StaticCondition() {} |
+ |
+ bool ShouldPruneReport(const CrashReportDatabase::Report& report) override { |
+ return value_; |
+ } |
+ |
+ private: |
+ bool value_; |
+}; |
+ |
+TEST(PruneCrashReports, BinaryAND) { |
+ BinaryPruneCondition false_false(BinaryPruneCondition::AND, |
+ new StaticCondition(false), new StaticCondition(false)); |
+ BinaryPruneCondition false_true(BinaryPruneCondition::AND, |
+ new StaticCondition(false), new StaticCondition(true)); |
+ BinaryPruneCondition true_false(BinaryPruneCondition::AND, |
+ new StaticCondition(true), new StaticCondition(false)); |
+ BinaryPruneCondition true_true(BinaryPruneCondition::AND, |
+ new StaticCondition(true), new StaticCondition(true)); |
+ |
+ CrashReportDatabase::Report report; |
+ |
+ EXPECT_FALSE(false_false.ShouldPruneReport(report)); |
+ EXPECT_FALSE(false_true.ShouldPruneReport(report)); |
+ EXPECT_FALSE(true_false.ShouldPruneReport(report)); |
+ EXPECT_TRUE(true_true.ShouldPruneReport(report)); |
+} |
+ |
+TEST(PruneCrashReports, BinaryOR) { |
+ BinaryPruneCondition false_false(BinaryPruneCondition::OR, |
+ new StaticCondition(false), new StaticCondition(false)); |
+ BinaryPruneCondition false_true(BinaryPruneCondition::OR, |
+ new StaticCondition(false), new StaticCondition(true)); |
+ BinaryPruneCondition true_false(BinaryPruneCondition::OR, |
+ new StaticCondition(true), new StaticCondition(false)); |
+ BinaryPruneCondition true_true(BinaryPruneCondition::OR, |
+ new StaticCondition(true), new StaticCondition(true)); |
+ |
+ CrashReportDatabase::Report report; |
+ |
+ EXPECT_FALSE(false_false.ShouldPruneReport(report)); |
+ EXPECT_TRUE(false_true.ShouldPruneReport(report)); |
+ EXPECT_TRUE(true_false.ShouldPruneReport(report)); |
+ EXPECT_TRUE(true_true.ShouldPruneReport(report)); |
+} |
+ |
+TEST(PruneCrashReports, PruneOrder) { |
+ TestDatabase db; |
+ |
+ std::vector<CrashReportDatabase::Report> reports; |
+ for (int i = 0; i < 10; ++i) { |
+ CrashReportDatabase::Report temp; |
+ temp.uuid.data_1 = i; |
+ temp.creation_time = NDaysAgo(i * 10); |
+ reports.push_back(temp); |
+ } |
+ |
+ // The randomness from std::rand() is not, so use arc4random instead. |
+ std::random_shuffle(reports.begin(), reports.end(), [](int rand_max) { |
+ return arc4random() % rand_max; |
scottmg
2015/10/06 22:22:07
arc4random doesn't exist on Windows.
scottmg
2015/10/06 22:35:29
(Should have mentioned we have base/rand_util.h wh
Robert Sesek
2015/10/07 16:24:35
Done.
|
+ }); |
+ db.pending_reports()->insert(db.pending_reports()->end(), |
+ reports.begin(), reports.begin() + 5); |
+ db.completed_reports()->insert(db.completed_reports()->end(), |
+ reports.begin() + 5, reports.end()); |
+ |
+ StaticCondition delete_all(true); |
+ PruneCrashReportDatabase(&db, &delete_all); |
+ |
+ ASSERT_EQ(reports.size(), db.deleted_reports().size()); |
+ for (size_t i = 0; i < reports.size(); ++i) { |
+ EXPECT_EQ(i, db.deleted_reports()[i].data_1); |
+ } |
+ |
+ EXPECT_TRUE(db.pending_reports()->empty()); |
+ EXPECT_TRUE(db.completed_reports()->empty()); |
+} |
+ |
+} // namespace |
+} // namespace test |
+} // namespace crashpad |