crash_collector_test.cc revision ab6cc90503ca2db976a3cb9c9382a9da85c4b5a2
1// Copyright (c) 2012 The Chromium OS Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "crash-reporter/crash_collector_test.h" 6 7#include <unistd.h> 8 9#include <dbus/dbus-glib-lowlevel.h> 10#include <glib.h> 11 12#include <base/files/file_util.h> 13#include <base/strings/string_util.h> 14#include <base/strings/stringprintf.h> 15#include <chromeos/syslog_logging.h> 16#include <chromeos/test_helpers.h> 17#include <gtest/gtest.h> 18 19#include "crash-reporter/crash_collector.h" 20 21using base::FilePath; 22using base::StringPrintf; 23using chromeos::FindLog; 24using ::testing::Return; 25 26void CountCrash() { 27 ADD_FAILURE(); 28} 29 30bool IsMetrics() { 31 ADD_FAILURE(); 32 return false; 33} 34 35class CrashCollectorTest : public ::testing::Test { 36 public: 37 void SetUp() { 38 collector_.Initialize(CountCrash, IsMetrics); 39 test_dir_ = FilePath("test"); 40 base::CreateDirectory(test_dir_); 41 chromeos::ClearLog(); 42 } 43 44 void TearDown() { 45 base::DeleteFile(test_dir_, true); 46 } 47 48 bool CheckHasCapacity(); 49 50 protected: 51 CrashCollectorMock collector_; 52 FilePath test_dir_; 53}; 54 55TEST_F(CrashCollectorTest, Initialize) { 56 ASSERT_TRUE(CountCrash == collector_.count_crash_function_); 57 ASSERT_TRUE(IsMetrics == collector_.is_feedback_allowed_function_); 58} 59 60TEST_F(CrashCollectorTest, WriteNewFile) { 61 FilePath test_file = test_dir_.Append("test_new"); 62 const char kBuffer[] = "buffer"; 63 EXPECT_EQ(strlen(kBuffer), 64 collector_.WriteNewFile(test_file, 65 kBuffer, 66 strlen(kBuffer))); 67 EXPECT_LT(collector_.WriteNewFile(test_file, 68 kBuffer, 69 strlen(kBuffer)), 0); 70} 71 72TEST_F(CrashCollectorTest, Sanitize) { 73 EXPECT_EQ("chrome", collector_.Sanitize("chrome")); 74 EXPECT_EQ("CHROME", collector_.Sanitize("CHROME")); 75 EXPECT_EQ("1chrome2", collector_.Sanitize("1chrome2")); 76 EXPECT_EQ("chrome__deleted_", collector_.Sanitize("chrome (deleted)")); 77 EXPECT_EQ("foo_bar", collector_.Sanitize("foo.bar")); 78 EXPECT_EQ("", collector_.Sanitize("")); 79 EXPECT_EQ("_", collector_.Sanitize(" ")); 80} 81 82TEST_F(CrashCollectorTest, GetCrashDirectoryInfo) { 83 FilePath path; 84 const int kRootUid = 0; 85 const int kRootGid = 0; 86 const int kNtpUid = 5; 87 const int kChronosUid = 1000; 88 const int kChronosGid = 1001; 89 const mode_t kExpectedSystemMode = 01755; 90 const mode_t kExpectedUserMode = 0755; 91 92 mode_t directory_mode; 93 uid_t directory_owner; 94 gid_t directory_group; 95 96 path = collector_.GetCrashDirectoryInfo(kRootUid, 97 kChronosUid, 98 kChronosGid, 99 &directory_mode, 100 &directory_owner, 101 &directory_group); 102 EXPECT_EQ("/var/spool/crash", path.value()); 103 EXPECT_EQ(kExpectedSystemMode, directory_mode); 104 EXPECT_EQ(kRootUid, directory_owner); 105 EXPECT_EQ(kRootGid, directory_group); 106 107 path = collector_.GetCrashDirectoryInfo(kNtpUid, 108 kChronosUid, 109 kChronosGid, 110 &directory_mode, 111 &directory_owner, 112 &directory_group); 113 EXPECT_EQ("/var/spool/crash", path.value()); 114 EXPECT_EQ(kExpectedSystemMode, directory_mode); 115 EXPECT_EQ(kRootUid, directory_owner); 116 EXPECT_EQ(kRootGid, directory_group); 117 118 // No need to destroy the hash as GetCrashDirectoryInfo() will do it for us. 119 GHashTable *active_sessions = g_hash_table_new(g_str_hash, g_str_equal); 120 char kUser[] = "chicken@butt.com"; 121 char kHash[] = "hashcakes"; 122 g_hash_table_insert(active_sessions, 123 static_cast<gpointer>(kUser), 124 static_cast<gpointer>(kHash)); 125 EXPECT_CALL(collector_, GetActiveUserSessions()) 126 .WillOnce(Return(active_sessions)); 127 128 EXPECT_EQ(collector_.IsUserSpecificDirectoryEnabled(), true); 129 130 path = collector_.GetCrashDirectoryInfo(kChronosUid, 131 kChronosUid, 132 kChronosGid, 133 &directory_mode, 134 &directory_owner, 135 &directory_group); 136 EXPECT_EQ("/home/user/hashcakes/crash", path.value()); 137 EXPECT_EQ(kExpectedUserMode, directory_mode); 138 EXPECT_EQ(kChronosUid, directory_owner); 139 EXPECT_EQ(kChronosGid, directory_group); 140} 141 142TEST_F(CrashCollectorTest, FormatDumpBasename) { 143 struct tm tm = {0}; 144 tm.tm_sec = 15; 145 tm.tm_min = 50; 146 tm.tm_hour = 13; 147 tm.tm_mday = 23; 148 tm.tm_mon = 4; 149 tm.tm_year = 110; 150 tm.tm_isdst = -1; 151 std::string basename = 152 collector_.FormatDumpBasename("foo", mktime(&tm), 100); 153 ASSERT_EQ("foo.20100523.135015.100", basename); 154} 155 156TEST_F(CrashCollectorTest, GetCrashPath) { 157 EXPECT_EQ("/var/spool/crash/myprog.20100101.1200.1234.core", 158 collector_.GetCrashPath(FilePath("/var/spool/crash"), 159 "myprog.20100101.1200.1234", 160 "core").value()); 161 EXPECT_EQ("/home/chronos/user/crash/chrome.20100101.1200.1234.dmp", 162 collector_.GetCrashPath(FilePath("/home/chronos/user/crash"), 163 "chrome.20100101.1200.1234", 164 "dmp").value()); 165} 166 167 168bool CrashCollectorTest::CheckHasCapacity() { 169 static const char kFullMessage[] = "Crash directory test already full"; 170 bool has_capacity = collector_.CheckHasCapacity(test_dir_); 171 bool has_message = FindLog(kFullMessage); 172 EXPECT_EQ(has_message, !has_capacity); 173 return has_capacity; 174} 175 176TEST_F(CrashCollectorTest, CheckHasCapacityUsual) { 177 // Test kMaxCrashDirectorySize - 1 non-meta files can be added. 178 for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) { 179 base::WriteFile(test_dir_.Append(StringPrintf("file%d.core", i)), "", 0); 180 EXPECT_TRUE(CheckHasCapacity()); 181 } 182 183 // Test an additional kMaxCrashDirectorySize - 1 meta files fit. 184 for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) { 185 base::WriteFile(test_dir_.Append(StringPrintf("file%d.meta", i)), "", 0); 186 EXPECT_TRUE(CheckHasCapacity()); 187 } 188 189 // Test an additional kMaxCrashDirectorySize meta files don't fit. 190 for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize; ++i) { 191 base::WriteFile(test_dir_.Append(StringPrintf("overage%d.meta", i)), "", 0); 192 EXPECT_FALSE(CheckHasCapacity()); 193 } 194} 195 196TEST_F(CrashCollectorTest, CheckHasCapacityCorrectBasename) { 197 // Test kMaxCrashDirectorySize - 1 files can be added. 198 for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) { 199 base::WriteFile(test_dir_.Append(StringPrintf("file.%d.core", i)), "", 0); 200 EXPECT_TRUE(CheckHasCapacity()); 201 } 202 base::WriteFile(test_dir_.Append("file.last.core"), "", 0); 203 EXPECT_FALSE(CheckHasCapacity()); 204} 205 206TEST_F(CrashCollectorTest, CheckHasCapacityStrangeNames) { 207 // Test many files with different extensions and same base fit. 208 for (int i = 0; i < 5 * CrashCollector::kMaxCrashDirectorySize; ++i) { 209 base::WriteFile(test_dir_.Append(StringPrintf("a.%d", i)), "", 0); 210 EXPECT_TRUE(CheckHasCapacity()); 211 } 212 // Test dot files are treated as individual files. 213 for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 2; ++i) { 214 base::WriteFile(test_dir_.Append(StringPrintf(".file%d", i)), "", 0); 215 EXPECT_TRUE(CheckHasCapacity()); 216 } 217 base::WriteFile(test_dir_.Append("normal.meta"), "", 0); 218 EXPECT_FALSE(CheckHasCapacity()); 219} 220 221TEST_F(CrashCollectorTest, IsCommentLine) { 222 EXPECT_FALSE(CrashCollector::IsCommentLine("")); 223 EXPECT_TRUE(CrashCollector::IsCommentLine("#")); 224 EXPECT_TRUE(CrashCollector::IsCommentLine("#real comment")); 225 EXPECT_TRUE(CrashCollector::IsCommentLine(" # real comment")); 226 EXPECT_FALSE(CrashCollector::IsCommentLine("not comment")); 227 EXPECT_FALSE(CrashCollector::IsCommentLine(" not comment")); 228} 229 230TEST_F(CrashCollectorTest, ReadKeyValueFile) { 231 const char *contents = ("a=b\n" 232 "\n" 233 " c=d \n"); 234 FilePath path(test_dir_.Append("keyval")); 235 std::map<std::string, std::string> dictionary; 236 std::map<std::string, std::string>::iterator i; 237 238 base::WriteFile(path, contents, strlen(contents)); 239 240 EXPECT_TRUE(collector_.ReadKeyValueFile(path, '=', &dictionary)); 241 i = dictionary.find("a"); 242 EXPECT_TRUE(i != dictionary.end() && i->second == "b"); 243 i = dictionary.find("c"); 244 EXPECT_TRUE(i != dictionary.end() && i->second == "d"); 245 246 dictionary.clear(); 247 248 contents = ("a=b c d\n" 249 "e\n" 250 " f g = h\n" 251 "i=j\n" 252 "=k\n" 253 "#comment=0\n" 254 "l=\n"); 255 base::WriteFile(path, contents, strlen(contents)); 256 257 EXPECT_FALSE(collector_.ReadKeyValueFile(path, '=', &dictionary)); 258 EXPECT_EQ(5, dictionary.size()); 259 260 i = dictionary.find("a"); 261 EXPECT_TRUE(i != dictionary.end() && i->second == "b c d"); 262 i = dictionary.find("e"); 263 EXPECT_TRUE(i == dictionary.end()); 264 i = dictionary.find("f g"); 265 EXPECT_TRUE(i != dictionary.end() && i->second == "h"); 266 i = dictionary.find("i"); 267 EXPECT_TRUE(i != dictionary.end() && i->second == "j"); 268 i = dictionary.find(""); 269 EXPECT_TRUE(i != dictionary.end() && i->second == "k"); 270 i = dictionary.find("l"); 271 EXPECT_TRUE(i != dictionary.end() && i->second == ""); 272} 273 274TEST_F(CrashCollectorTest, MetaData) { 275 const char kMetaFileBasename[] = "generated.meta"; 276 FilePath meta_file = test_dir_.Append(kMetaFileBasename); 277 FilePath lsb_release = test_dir_.Append("lsb-release"); 278 FilePath payload_file = test_dir_.Append("payload-file"); 279 std::string contents; 280 collector_.lsb_release_ = lsb_release.value(); 281 const char kLsbContents[] = "CHROMEOS_RELEASE_VERSION=version\n"; 282 ASSERT_TRUE(base::WriteFile(lsb_release, kLsbContents, strlen(kLsbContents))); 283 const char kPayload[] = "foo"; 284 ASSERT_TRUE(base::WriteFile(payload_file, kPayload, strlen(kPayload))); 285 collector_.AddCrashMetaData("foo", "bar"); 286 collector_.WriteCrashMetaData(meta_file, "kernel", payload_file.value()); 287 EXPECT_TRUE(base::ReadFileToString(meta_file, &contents)); 288 const char kExpectedMeta[] = 289 "foo=bar\n" 290 "exec_name=kernel\n" 291 "ver=version\n" 292 "payload=test/payload-file\n" 293 "payload_size=3\n" 294 "done=1\n"; 295 EXPECT_EQ(kExpectedMeta, contents); 296 297 // Test target of symlink is not overwritten. 298 payload_file = test_dir_.Append("payload2-file"); 299 ASSERT_TRUE(base::WriteFile(payload_file, kPayload, strlen(kPayload))); 300 FilePath meta_symlink_path = test_dir_.Append("symlink.meta"); 301 ASSERT_EQ(0, 302 symlink(kMetaFileBasename, 303 meta_symlink_path.value().c_str())); 304 ASSERT_TRUE(base::PathExists(meta_symlink_path)); 305 chromeos::ClearLog(); 306 collector_.WriteCrashMetaData(meta_symlink_path, 307 "kernel", 308 payload_file.value()); 309 // Target metadata contents should have stayed the same. 310 contents.clear(); 311 EXPECT_TRUE(base::ReadFileToString(meta_file, &contents)); 312 EXPECT_EQ(kExpectedMeta, contents); 313 EXPECT_TRUE(FindLog("Unable to write")); 314 315 // Test target of dangling symlink is not created. 316 base::DeleteFile(meta_file, false); 317 ASSERT_FALSE(base::PathExists(meta_file)); 318 chromeos::ClearLog(); 319 collector_.WriteCrashMetaData(meta_symlink_path, "kernel", 320 payload_file.value()); 321 EXPECT_FALSE(base::PathExists(meta_file)); 322 EXPECT_TRUE(FindLog("Unable to write")); 323} 324 325TEST_F(CrashCollectorTest, GetLogContents) { 326 FilePath config_file = test_dir_.Append("crash_config"); 327 FilePath output_file = test_dir_.Append("crash_log"); 328 const char kConfigContents[] = 329 "foobar:echo hello there | sed -e \"s/there/world/\""; 330 ASSERT_TRUE( 331 base::WriteFile(config_file, kConfigContents, strlen(kConfigContents))); 332 base::DeleteFile(FilePath(output_file), false); 333 EXPECT_FALSE(collector_.GetLogContents(config_file, 334 "barfoo", 335 output_file)); 336 EXPECT_FALSE(base::PathExists(output_file)); 337 base::DeleteFile(FilePath(output_file), false); 338 EXPECT_TRUE(collector_.GetLogContents(config_file, 339 "foobar", 340 output_file)); 341 ASSERT_TRUE(base::PathExists(output_file)); 342 std::string contents; 343 EXPECT_TRUE(base::ReadFileToString(output_file, &contents)); 344 EXPECT_EQ("hello world\n", contents); 345} 346 347int main(int argc, char **argv) { 348 ::g_type_init(); 349 SetUpTests(&argc, argv, false); 350 return RUN_ALL_TESTS(); 351} 352