| // Copyright 2020 The Chromium OS 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 "crash-reporter/crash_sender_base.h" |
| |
| #include <string> |
| |
| #include <base/files/file_util.h> |
| #include <base/files/scoped_temp_dir.h> |
| #include <base/guid.h> |
| #include <brillo/key_value_store.h> |
| #include <gtest/gtest.h> |
| |
| #include "crash-reporter/crash_sender_paths.h" |
| #include "crash-reporter/paths.h" |
| #include "crash-reporter/test_util.h" |
| |
| namespace util { |
| namespace { |
| |
| constexpr char kFakeClientId[] = "00112233445566778899aabbccddeeff"; |
| |
| // Creates the client ID file and stores the fake client ID in it. |
| bool CreateClientIdFile() { |
| return test_util::CreateFile( |
| paths::GetAt(paths::kCrashSenderStateDirectory, paths::kClientId), |
| kFakeClientId); |
| } |
| |
| // Set the file flag which indicates we are mocking crash sending, either |
| // successfully or as a a failure. This also creates the directory where |
| // uploads.log is written to since Chrome would normally be doing that. |
| bool SetMockCrashSending(bool success) { |
| return test_util::CreateFile(paths::GetAt(paths::kSystemRunStateDirectory, |
| paths::kMockCrashSending), |
| success ? "" : "0") && |
| base::CreateDirectory(paths::Get(paths::kChromeCrashLog).DirName()); |
| } |
| |
| class CrashSenderBaseTest : public testing::Test { |
| protected: |
| void SetUp() override { |
| ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |
| test_dir_ = temp_dir_.GetPath(); |
| paths::SetPrefixForTesting(test_dir_); |
| } |
| |
| void TearDown() override { paths::SetPrefixForTesting(base::FilePath()); } |
| |
| base::ScopedTempDir temp_dir_; |
| base::FilePath test_dir_; |
| }; |
| |
| TEST_F(CrashSenderBaseTest, GetBaseNameFromMetadata) { |
| brillo::KeyValueStore metadata; |
| metadata.LoadFromString(""); |
| EXPECT_EQ("", GetBaseNameFromMetadata(metadata, "payload").value()); |
| |
| metadata.LoadFromString("payload=test.log\n"); |
| EXPECT_EQ("test.log", GetBaseNameFromMetadata(metadata, "payload").value()); |
| |
| metadata.LoadFromString("payload=/foo/test.log\n"); |
| EXPECT_EQ("test.log", GetBaseNameFromMetadata(metadata, "payload").value()); |
| } |
| |
| TEST_F(CrashSenderBaseTest, GetKindFromPayloadPath) { |
| EXPECT_EQ("", GetKindFromPayloadPath(base::FilePath())); |
| EXPECT_EQ("", GetKindFromPayloadPath(base::FilePath("foo"))); |
| EXPECT_EQ("log", GetKindFromPayloadPath(base::FilePath("foo.log"))); |
| // "dmp" is a special case. |
| EXPECT_EQ("minidump", GetKindFromPayloadPath(base::FilePath("foo.dmp"))); |
| |
| // ".gz" should be ignored. |
| EXPECT_EQ("log", GetKindFromPayloadPath(base::FilePath("foo.log.gz"))); |
| EXPECT_EQ("minidump", GetKindFromPayloadPath(base::FilePath("foo.dmp.gz"))); |
| EXPECT_EQ("", GetKindFromPayloadPath(base::FilePath("foo.gz"))); |
| |
| // The directory name should not affect the function. |
| EXPECT_EQ("minidump", |
| GetKindFromPayloadPath(base::FilePath("/1.2.3/foo.dmp.gz"))); |
| } |
| |
| TEST_F(CrashSenderBaseTest, ParseMetadata) { |
| brillo::KeyValueStore metadata; |
| std::string value; |
| EXPECT_TRUE(ParseMetadata("", &metadata)); |
| EXPECT_TRUE(ParseMetadata("log=test.log\n", &metadata)); |
| EXPECT_TRUE(ParseMetadata("#comment\nlog=test.log\n", &metadata)); |
| |
| EXPECT_TRUE(metadata.GetString("log", &value)); |
| // This will clear the previously parsed data. |
| EXPECT_TRUE(ParseMetadata("payload=test.dmp\n", &metadata)); |
| EXPECT_FALSE(metadata.GetString("log", &value)); |
| |
| // Underscores, dashes, and periods should allowed, as Chrome uses them. |
| // https://crbug.com/821530. |
| EXPECT_TRUE(ParseMetadata("abcABC012_.-=test.log\n", &metadata)); |
| EXPECT_TRUE(metadata.GetString("abcABC012_.-", &value)); |
| EXPECT_EQ("test.log", value); |
| |
| // Invalid metadata should be detected. |
| EXPECT_FALSE(ParseMetadata("=test.log\n", &metadata)); |
| EXPECT_FALSE(ParseMetadata("***\n", &metadata)); |
| EXPECT_FALSE(ParseMetadata("***=test.log\n", &metadata)); |
| EXPECT_FALSE(ParseMetadata("log\n", &metadata)); |
| } |
| |
| TEST_F(CrashSenderBaseTest, IsCompleteMetadata) { |
| brillo::KeyValueStore metadata; |
| metadata.LoadFromString(""); |
| EXPECT_FALSE(IsCompleteMetadata(metadata)); |
| |
| metadata.LoadFromString("log=test.log\n"); |
| EXPECT_FALSE(IsCompleteMetadata(metadata)); |
| |
| metadata.LoadFromString("log=test.log\ndone=1\n"); |
| EXPECT_TRUE(IsCompleteMetadata(metadata)); |
| |
| metadata.LoadFromString("done=1\n"); |
| EXPECT_TRUE(IsCompleteMetadata(metadata)); |
| } |
| |
| TEST_F(CrashSenderBaseTest, CreateClientId) { |
| std::string client_id = GetClientId(); |
| EXPECT_EQ(client_id.length(), 32); |
| // Make sure it returns the same one multiple times. |
| EXPECT_EQ(client_id, GetClientId()); |
| } |
| |
| TEST_F(CrashSenderBaseTest, RetrieveClientId) { |
| CreateClientIdFile(); |
| EXPECT_EQ(kFakeClientId, GetClientId()); |
| } |
| |
| TEST_F(CrashSenderBaseTest, GetSleepTime) { |
| const base::FilePath meta_file = test_dir_.Append("test.meta"); |
| base::TimeDelta max_spread_time = base::TimeDelta::FromSeconds(0); |
| |
| // This should fail since meta_file does not exist. |
| base::TimeDelta sleep_time; |
| EXPECT_FALSE( |
| GetSleepTime(meta_file, max_spread_time, kMaxHoldOffTime, &sleep_time)); |
| |
| ASSERT_TRUE(test_util::CreateFile(meta_file, "")); |
| |
| // sleep_time should be close enough to kMaxHoldOffTime since the meta file |
| // was just created, but 10% error is allowed just in case. |
| EXPECT_TRUE( |
| GetSleepTime(meta_file, max_spread_time, kMaxHoldOffTime, &sleep_time)); |
| EXPECT_NEAR(kMaxHoldOffTime.InSecondsF(), sleep_time.InSecondsF(), |
| kMaxHoldOffTime.InSecondsF() * 0.1); |
| |
| // Zero hold-off time and zero sleep time should always give zero sleep time. |
| EXPECT_TRUE(GetSleepTime(meta_file, max_spread_time, |
| base::TimeDelta::FromSeconds(0) /*hold_off_time*/, |
| &sleep_time)); |
| EXPECT_EQ(base::TimeDelta::FromSeconds(0), sleep_time); |
| |
| // Even if file is new, a zero hold-off time means we choose a time between |
| // 0 and max_spread_time. |
| ASSERT_TRUE(test_util::TouchFileHelper(meta_file, base::Time::Now())); |
| EXPECT_TRUE(GetSleepTime( |
| meta_file, base::TimeDelta::FromSeconds(60) /*max_spread_time*/, |
| base::TimeDelta::FromSeconds(0) /*hold_off_time*/, &sleep_time)); |
| EXPECT_LE(base::TimeDelta::FromSeconds(0), sleep_time); |
| EXPECT_GE(base::TimeDelta::FromSeconds(60), sleep_time); |
| |
| // Make the meta file old enough so hold-off time is not necessary. |
| const base::Time now = base::Time::Now(); |
| ASSERT_TRUE(test_util::TouchFileHelper(meta_file, now - kMaxHoldOffTime)); |
| |
| // sleep_time should always be 0, since max_spread_time is set to 0. |
| EXPECT_TRUE( |
| GetSleepTime(meta_file, max_spread_time, kMaxHoldOffTime, &sleep_time)); |
| EXPECT_EQ(base::TimeDelta::FromSeconds(0), sleep_time); |
| |
| // sleep_time should be in range [0, 10]. |
| max_spread_time = base::TimeDelta::FromSeconds(10); |
| EXPECT_TRUE( |
| GetSleepTime(meta_file, max_spread_time, kMaxHoldOffTime, &sleep_time)); |
| EXPECT_LE(base::TimeDelta::FromSeconds(0), sleep_time); |
| EXPECT_GE(base::TimeDelta::FromSeconds(10), sleep_time); |
| |
| // If the meta file is current, the minimum sleep time should be |
| // kMaxHoldOffTime but the maximum is still max_spread_time. |
| max_spread_time = base::TimeDelta::FromSeconds(60); |
| ASSERT_TRUE(test_util::TouchFileHelper(meta_file, base::Time::Now())); |
| EXPECT_TRUE( |
| GetSleepTime(meta_file, max_spread_time, kMaxHoldOffTime, &sleep_time)); |
| // 0.9 in case we got preempted for 3 seconds between the file touch and the |
| // GetSleepTime(). |
| EXPECT_LE(kMaxHoldOffTime * 0.9, sleep_time); |
| EXPECT_GE(base::TimeDelta::FromSeconds(60), sleep_time); |
| } |
| |
| TEST_F(CrashSenderBaseTest, IsMock) { |
| EXPECT_FALSE(IsMock()); |
| ASSERT_TRUE(SetMockCrashSending(false)); |
| EXPECT_TRUE(IsMock()); |
| EXPECT_FALSE(IsMockSuccessful()); |
| ASSERT_TRUE(SetMockCrashSending(true)); |
| EXPECT_TRUE(IsMock()); |
| EXPECT_TRUE(IsMockSuccessful()); |
| } |
| |
| TEST_F(CrashSenderBaseTest, GetImageType) { |
| EXPECT_EQ("", GetImageType()); |
| ASSERT_TRUE(SetMockCrashSending(false)); |
| EXPECT_EQ("mock-fail", GetImageType()); |
| ASSERT_TRUE(test_util::CreateFile(paths::Get(paths::kLeaveCoreFile), "")); |
| EXPECT_EQ("dev", GetImageType()); |
| ASSERT_TRUE(test_util::CreateFile( |
| paths::GetAt(paths::kEtcDirectory, paths::kLsbRelease), |
| "CHROMEOS_RELEASE_TRACK=testimage-channel")); |
| EXPECT_EQ("test", GetImageType()); |
| } |
| |
| } // namespace |
| } // namespace util |