blob: 11c8c0d8257af949d1c7117f04516cc4ab53520b [file] [log] [blame]
/*
* 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);
}