| /* |
| * Copyright (C) 2012 The Android Open Source Project |
| * |
| * 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 "crash_collector_test.h" |
| |
| #include <unistd.h> |
| #include <utility> |
| |
| #include <base/files/file_util.h> |
| #include <base/files/scoped_temp_dir.h> |
| #include <base/strings/string_util.h> |
| #include <base/strings/stringprintf.h> |
| #include <brillo/syslog_logging.h> |
| #include <gtest/gtest.h> |
| |
| #include "crash_collector.h" |
| |
| using base::FilePath; |
| using base::StringPrintf; |
| using brillo::FindLog; |
| using ::testing::Invoke; |
| using ::testing::Return; |
| |
| namespace { |
| |
| void CountCrash() { |
| ADD_FAILURE(); |
| } |
| |
| bool IsMetrics() { |
| ADD_FAILURE(); |
| return false; |
| } |
| |
| } // namespace |
| |
| class CrashCollectorTest : public ::testing::Test { |
| public: |
| void SetUp() { |
| EXPECT_CALL(collector_, SetUpDBus()).WillRepeatedly(Return()); |
| |
| collector_.Initialize(CountCrash, IsMetrics); |
| EXPECT_TRUE(test_dir_.CreateUniqueTempDir()); |
| brillo::ClearLog(); |
| } |
| |
| bool CheckHasCapacity(); |
| |
| protected: |
| CrashCollectorMock collector_; |
| |
| // Temporary directory used for tests. |
| base::ScopedTempDir test_dir_; |
| }; |
| |
| TEST_F(CrashCollectorTest, Initialize) { |
| ASSERT_TRUE(CountCrash == collector_.count_crash_function_); |
| ASSERT_TRUE(IsMetrics == collector_.is_feedback_allowed_function_); |
| } |
| |
| TEST_F(CrashCollectorTest, WriteNewFile) { |
| FilePath test_file = test_dir_.path().Append("test_new"); |
| const char kBuffer[] = "buffer"; |
| EXPECT_EQ(strlen(kBuffer), |
| collector_.WriteNewFile(test_file, |
| kBuffer, |
| strlen(kBuffer))); |
| EXPECT_LT(collector_.WriteNewFile(test_file, |
| kBuffer, |
| strlen(kBuffer)), 0); |
| } |
| |
| TEST_F(CrashCollectorTest, Sanitize) { |
| EXPECT_EQ("chrome", collector_.Sanitize("chrome")); |
| EXPECT_EQ("CHROME", collector_.Sanitize("CHROME")); |
| EXPECT_EQ("1chrome2", collector_.Sanitize("1chrome2")); |
| EXPECT_EQ("chrome__deleted_", collector_.Sanitize("chrome (deleted)")); |
| EXPECT_EQ("foo_bar", collector_.Sanitize("foo.bar")); |
| EXPECT_EQ("", collector_.Sanitize("")); |
| EXPECT_EQ("_", collector_.Sanitize(" ")); |
| } |
| |
| TEST_F(CrashCollectorTest, FormatDumpBasename) { |
| struct tm tm = {0}; |
| tm.tm_sec = 15; |
| tm.tm_min = 50; |
| tm.tm_hour = 13; |
| tm.tm_mday = 23; |
| tm.tm_mon = 4; |
| tm.tm_year = 110; |
| tm.tm_isdst = -1; |
| std::string basename = |
| collector_.FormatDumpBasename("foo", mktime(&tm), 100); |
| ASSERT_EQ("foo.20100523.135015.100", basename); |
| } |
| |
| TEST_F(CrashCollectorTest, GetCrashPath) { |
| EXPECT_EQ("/var/spool/crash/myprog.20100101.1200.1234.core", |
| collector_.GetCrashPath(FilePath("/var/spool/crash"), |
| "myprog.20100101.1200.1234", |
| "core").value()); |
| EXPECT_EQ("/home/chronos/user/crash/chrome.20100101.1200.1234.dmp", |
| collector_.GetCrashPath(FilePath("/home/chronos/user/crash"), |
| "chrome.20100101.1200.1234", |
| "dmp").value()); |
| } |
| |
| |
| bool CrashCollectorTest::CheckHasCapacity() { |
| const char* kFullMessage = |
| StringPrintf("Crash directory %s already full", |
| test_dir_.path().value().c_str()).c_str(); |
| bool has_capacity = collector_.CheckHasCapacity(test_dir_.path()); |
| bool has_message = FindLog(kFullMessage); |
| EXPECT_EQ(has_message, !has_capacity); |
| return has_capacity; |
| } |
| |
| TEST_F(CrashCollectorTest, CheckHasCapacityUsual) { |
| // Test kMaxCrashDirectorySize - 1 non-meta files can be added. |
| for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) { |
| base::WriteFile(test_dir_.path().Append(StringPrintf("file%d.core", i)), |
| "", 0); |
| EXPECT_TRUE(CheckHasCapacity()); |
| } |
| |
| // Test an additional kMaxCrashDirectorySize - 1 meta files fit. |
| for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) { |
| base::WriteFile(test_dir_.path().Append(StringPrintf("file%d.meta", i)), |
| "", 0); |
| EXPECT_TRUE(CheckHasCapacity()); |
| } |
| |
| // Test an additional kMaxCrashDirectorySize meta files don't fit. |
| for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize; ++i) { |
| base::WriteFile(test_dir_.path().Append(StringPrintf("overage%d.meta", i)), |
| "", 0); |
| EXPECT_FALSE(CheckHasCapacity()); |
| } |
| } |
| |
| TEST_F(CrashCollectorTest, CheckHasCapacityCorrectBasename) { |
| // Test kMaxCrashDirectorySize - 1 files can be added. |
| for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) { |
| base::WriteFile(test_dir_.path().Append(StringPrintf("file.%d.core", i)), |
| "", 0); |
| EXPECT_TRUE(CheckHasCapacity()); |
| } |
| base::WriteFile(test_dir_.path().Append("file.last.core"), "", 0); |
| EXPECT_FALSE(CheckHasCapacity()); |
| } |
| |
| TEST_F(CrashCollectorTest, CheckHasCapacityStrangeNames) { |
| // Test many files with different extensions and same base fit. |
| for (int i = 0; i < 5 * CrashCollector::kMaxCrashDirectorySize; ++i) { |
| base::WriteFile(test_dir_.path().Append(StringPrintf("a.%d", i)), "", 0); |
| EXPECT_TRUE(CheckHasCapacity()); |
| } |
| // Test dot files are treated as individual files. |
| for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 2; ++i) { |
| base::WriteFile(test_dir_.path().Append(StringPrintf(".file%d", i)), "", 0); |
| EXPECT_TRUE(CheckHasCapacity()); |
| } |
| base::WriteFile(test_dir_.path().Append("normal.meta"), "", 0); |
| EXPECT_FALSE(CheckHasCapacity()); |
| } |
| |
| TEST_F(CrashCollectorTest, MetaData) { |
| const char kMetaFileBasename[] = "generated.meta"; |
| FilePath meta_file = test_dir_.path().Append(kMetaFileBasename); |
| FilePath payload_file = test_dir_.path().Append("payload-file"); |
| std::string contents; |
| const char kPayload[] = "foo"; |
| ASSERT_TRUE(base::WriteFile(payload_file, kPayload, strlen(kPayload))); |
| collector_.AddCrashMetaData("foo", "bar"); |
| collector_.WriteCrashMetaData(meta_file, "kernel", payload_file.value()); |
| EXPECT_TRUE(base::ReadFileToString(meta_file, &contents)); |
| const std::string kExpectedMeta = |
| StringPrintf("foo=bar\n" |
| "exec_name=kernel\n" |
| "payload=%s\n" |
| "payload_size=3\n" |
| "done=1\n", |
| test_dir_.path().Append("payload-file").value().c_str()); |
| EXPECT_EQ(kExpectedMeta, contents); |
| |
| // Test target of symlink is not overwritten. |
| payload_file = test_dir_.path().Append("payload2-file"); |
| ASSERT_TRUE(base::WriteFile(payload_file, kPayload, strlen(kPayload))); |
| FilePath meta_symlink_path = test_dir_.path().Append("symlink.meta"); |
| ASSERT_EQ(0, |
| symlink(kMetaFileBasename, |
| meta_symlink_path.value().c_str())); |
| ASSERT_TRUE(base::PathExists(meta_symlink_path)); |
| brillo::ClearLog(); |
| collector_.WriteCrashMetaData(meta_symlink_path, |
| "kernel", |
| payload_file.value()); |
| // Target metadata contents should have stayed the same. |
| contents.clear(); |
| EXPECT_TRUE(base::ReadFileToString(meta_file, &contents)); |
| EXPECT_EQ(kExpectedMeta, contents); |
| EXPECT_TRUE(FindLog("Unable to write")); |
| |
| // Test target of dangling symlink is not created. |
| base::DeleteFile(meta_file, false); |
| ASSERT_FALSE(base::PathExists(meta_file)); |
| brillo::ClearLog(); |
| collector_.WriteCrashMetaData(meta_symlink_path, "kernel", |
| payload_file.value()); |
| EXPECT_FALSE(base::PathExists(meta_file)); |
| EXPECT_TRUE(FindLog("Unable to write")); |
| } |
| |
| TEST_F(CrashCollectorTest, GetLogContents) { |
| FilePath config_file = test_dir_.path().Append("crash_config"); |
| FilePath output_file = test_dir_.path().Append("crash_log"); |
| const char kConfigContents[] = |
| "foobar=echo hello there | \\\n sed -e \"s/there/world/\""; |
| ASSERT_TRUE( |
| base::WriteFile(config_file, kConfigContents, strlen(kConfigContents))); |
| base::DeleteFile(FilePath(output_file), false); |
| EXPECT_FALSE(collector_.GetLogContents(config_file, |
| "barfoo", |
| output_file)); |
| EXPECT_FALSE(base::PathExists(output_file)); |
| base::DeleteFile(FilePath(output_file), false); |
| EXPECT_TRUE(collector_.GetLogContents(config_file, |
| "foobar", |
| output_file)); |
| ASSERT_TRUE(base::PathExists(output_file)); |
| std::string contents; |
| EXPECT_TRUE(base::ReadFileToString(output_file, &contents)); |
| EXPECT_EQ("hello world\n", contents); |
| } |