crash_collector_test.cc revision 129bea543b7572bfdf09c6a7b3cebbe3b57ce723
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_collector_test.h"
6
7#include <unistd.h>
8#include <utility>
9
10#include <base/files/file_util.h>
11#include <base/strings/string_util.h>
12#include <base/strings/stringprintf.h>
13#include <chromeos/syslog_logging.h>
14#include <gtest/gtest.h>
15
16#include "crash_collector.h"
17
18using base::FilePath;
19using base::StringPrintf;
20using chromeos::FindLog;
21using ::testing::Invoke;
22using ::testing::Return;
23
24namespace {
25
26void CountCrash() {
27  ADD_FAILURE();
28}
29
30bool IsMetrics() {
31  ADD_FAILURE();
32  return false;
33}
34
35bool GetActiveUserSessionsImpl(std::map<std::string, std::string> *sessions) {
36  char kUser[] = "chicken@butt.com";
37  char kHash[] = "hashcakes";
38  sessions->insert(std::pair<std::string, std::string>(kUser, kHash));
39  return true;
40}
41
42}  // namespace
43
44class CrashCollectorTest : public ::testing::Test {
45 public:
46  void SetUp() {
47    EXPECT_CALL(collector_, SetUpDBus()).WillRepeatedly(Return());
48
49    collector_.Initialize(CountCrash, IsMetrics);
50    test_dir_ = FilePath("test");
51    base::CreateDirectory(test_dir_);
52    chromeos::ClearLog();
53  }
54
55  void TearDown() {
56    base::DeleteFile(test_dir_, true);
57  }
58
59  bool CheckHasCapacity();
60
61 protected:
62  CrashCollectorMock collector_;
63  FilePath test_dir_;
64};
65
66TEST_F(CrashCollectorTest, Initialize) {
67  ASSERT_TRUE(CountCrash == collector_.count_crash_function_);
68  ASSERT_TRUE(IsMetrics == collector_.is_feedback_allowed_function_);
69}
70
71TEST_F(CrashCollectorTest, WriteNewFile) {
72  FilePath test_file = test_dir_.Append("test_new");
73  const char kBuffer[] = "buffer";
74  EXPECT_EQ(strlen(kBuffer),
75            collector_.WriteNewFile(test_file,
76                                    kBuffer,
77                                    strlen(kBuffer)));
78  EXPECT_LT(collector_.WriteNewFile(test_file,
79                                    kBuffer,
80                                    strlen(kBuffer)), 0);
81}
82
83TEST_F(CrashCollectorTest, Sanitize) {
84  EXPECT_EQ("chrome", collector_.Sanitize("chrome"));
85  EXPECT_EQ("CHROME", collector_.Sanitize("CHROME"));
86  EXPECT_EQ("1chrome2", collector_.Sanitize("1chrome2"));
87  EXPECT_EQ("chrome__deleted_", collector_.Sanitize("chrome (deleted)"));
88  EXPECT_EQ("foo_bar", collector_.Sanitize("foo.bar"));
89  EXPECT_EQ("", collector_.Sanitize(""));
90  EXPECT_EQ("_", collector_.Sanitize(" "));
91}
92
93TEST_F(CrashCollectorTest, GetCrashDirectoryInfo) {
94  FilePath path;
95  const int kRootUid = 0;
96  const int kRootGid = 0;
97  const int kNtpUid = 5;
98  const int kChronosUid = 1000;
99  const int kChronosGid = 1001;
100  const mode_t kExpectedSystemMode = 01755;
101  const mode_t kExpectedUserMode = 0755;
102
103  mode_t directory_mode;
104  uid_t directory_owner;
105  gid_t directory_group;
106
107  path = collector_.GetCrashDirectoryInfo(kRootUid,
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  path = collector_.GetCrashDirectoryInfo(kNtpUid,
119                                          kChronosUid,
120                                          kChronosGid,
121                                          &directory_mode,
122                                          &directory_owner,
123                                          &directory_group);
124  EXPECT_EQ("/var/spool/crash", path.value());
125  EXPECT_EQ(kExpectedSystemMode, directory_mode);
126  EXPECT_EQ(kRootUid, directory_owner);
127  EXPECT_EQ(kRootGid, directory_group);
128
129  EXPECT_CALL(collector_, GetActiveUserSessions(testing::_))
130      .WillOnce(Invoke(&GetActiveUserSessionsImpl));
131
132  EXPECT_EQ(collector_.IsUserSpecificDirectoryEnabled(), true);
133
134  path = collector_.GetCrashDirectoryInfo(kChronosUid,
135                                          kChronosUid,
136                                          kChronosGid,
137                                          &directory_mode,
138                                          &directory_owner,
139                                          &directory_group);
140  EXPECT_EQ("/home/user/hashcakes/crash", path.value());
141  EXPECT_EQ(kExpectedUserMode, directory_mode);
142  EXPECT_EQ(kChronosUid, directory_owner);
143  EXPECT_EQ(kChronosGid, directory_group);
144}
145
146TEST_F(CrashCollectorTest, FormatDumpBasename) {
147  struct tm tm = {0};
148  tm.tm_sec = 15;
149  tm.tm_min = 50;
150  tm.tm_hour = 13;
151  tm.tm_mday = 23;
152  tm.tm_mon = 4;
153  tm.tm_year = 110;
154  tm.tm_isdst = -1;
155  std::string basename =
156      collector_.FormatDumpBasename("foo", mktime(&tm), 100);
157  ASSERT_EQ("foo.20100523.135015.100", basename);
158}
159
160TEST_F(CrashCollectorTest, GetCrashPath) {
161  EXPECT_EQ("/var/spool/crash/myprog.20100101.1200.1234.core",
162            collector_.GetCrashPath(FilePath("/var/spool/crash"),
163                                    "myprog.20100101.1200.1234",
164                                    "core").value());
165  EXPECT_EQ("/home/chronos/user/crash/chrome.20100101.1200.1234.dmp",
166            collector_.GetCrashPath(FilePath("/home/chronos/user/crash"),
167                                    "chrome.20100101.1200.1234",
168                                    "dmp").value());
169}
170
171
172bool CrashCollectorTest::CheckHasCapacity() {
173  static const char kFullMessage[] = "Crash directory test already full";
174  bool has_capacity = collector_.CheckHasCapacity(test_dir_);
175  bool has_message = FindLog(kFullMessage);
176  EXPECT_EQ(has_message, !has_capacity);
177  return has_capacity;
178}
179
180TEST_F(CrashCollectorTest, CheckHasCapacityUsual) {
181  // Test kMaxCrashDirectorySize - 1 non-meta files can be added.
182  for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) {
183    base::WriteFile(test_dir_.Append(StringPrintf("file%d.core", i)), "", 0);
184    EXPECT_TRUE(CheckHasCapacity());
185  }
186
187  // Test an additional kMaxCrashDirectorySize - 1 meta files fit.
188  for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) {
189    base::WriteFile(test_dir_.Append(StringPrintf("file%d.meta", i)), "", 0);
190    EXPECT_TRUE(CheckHasCapacity());
191  }
192
193  // Test an additional kMaxCrashDirectorySize meta files don't fit.
194  for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize; ++i) {
195    base::WriteFile(test_dir_.Append(StringPrintf("overage%d.meta", i)), "", 0);
196    EXPECT_FALSE(CheckHasCapacity());
197  }
198}
199
200TEST_F(CrashCollectorTest, CheckHasCapacityCorrectBasename) {
201  // Test kMaxCrashDirectorySize - 1 files can be added.
202  for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) {
203    base::WriteFile(test_dir_.Append(StringPrintf("file.%d.core", i)), "", 0);
204    EXPECT_TRUE(CheckHasCapacity());
205  }
206  base::WriteFile(test_dir_.Append("file.last.core"), "", 0);
207  EXPECT_FALSE(CheckHasCapacity());
208}
209
210TEST_F(CrashCollectorTest, CheckHasCapacityStrangeNames) {
211  // Test many files with different extensions and same base fit.
212  for (int i = 0; i < 5 * CrashCollector::kMaxCrashDirectorySize; ++i) {
213    base::WriteFile(test_dir_.Append(StringPrintf("a.%d", i)), "", 0);
214    EXPECT_TRUE(CheckHasCapacity());
215  }
216  // Test dot files are treated as individual files.
217  for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 2; ++i) {
218    base::WriteFile(test_dir_.Append(StringPrintf(".file%d", i)), "", 0);
219    EXPECT_TRUE(CheckHasCapacity());
220  }
221  base::WriteFile(test_dir_.Append("normal.meta"), "", 0);
222  EXPECT_FALSE(CheckHasCapacity());
223}
224
225TEST_F(CrashCollectorTest, MetaData) {
226  const char kMetaFileBasename[] = "generated.meta";
227  FilePath meta_file = test_dir_.Append(kMetaFileBasename);
228  FilePath lsb_release = test_dir_.Append("lsb-release");
229  FilePath payload_file = test_dir_.Append("payload-file");
230  std::string contents;
231  collector_.lsb_release_ = lsb_release.value();
232  const char kLsbContents[] =
233      "CHROMEOS_RELEASE_BOARD=lumpy\n"
234      "CHROMEOS_RELEASE_VERSION=6727.0.2015_01_26_0853\n"
235      "CHROMEOS_RELEASE_NAME=Chromium OS\n";
236  ASSERT_TRUE(base::WriteFile(lsb_release, kLsbContents, strlen(kLsbContents)));
237  const char kPayload[] = "foo";
238  ASSERT_TRUE(base::WriteFile(payload_file, kPayload, strlen(kPayload)));
239  collector_.AddCrashMetaData("foo", "bar");
240  collector_.WriteCrashMetaData(meta_file, "kernel", payload_file.value());
241  EXPECT_TRUE(base::ReadFileToString(meta_file, &contents));
242  const char kExpectedMeta[] =
243      "foo=bar\n"
244      "exec_name=kernel\n"
245      "ver=6727.0.2015_01_26_0853\n"
246      "payload=test/payload-file\n"
247      "payload_size=3\n"
248      "done=1\n";
249  EXPECT_EQ(kExpectedMeta, contents);
250
251  // Test target of symlink is not overwritten.
252  payload_file = test_dir_.Append("payload2-file");
253  ASSERT_TRUE(base::WriteFile(payload_file, kPayload, strlen(kPayload)));
254  FilePath meta_symlink_path = test_dir_.Append("symlink.meta");
255  ASSERT_EQ(0,
256            symlink(kMetaFileBasename,
257                    meta_symlink_path.value().c_str()));
258  ASSERT_TRUE(base::PathExists(meta_symlink_path));
259  chromeos::ClearLog();
260  collector_.WriteCrashMetaData(meta_symlink_path,
261                                "kernel",
262                                payload_file.value());
263  // Target metadata contents should have stayed the same.
264  contents.clear();
265  EXPECT_TRUE(base::ReadFileToString(meta_file, &contents));
266  EXPECT_EQ(kExpectedMeta, contents);
267  EXPECT_TRUE(FindLog("Unable to write"));
268
269  // Test target of dangling symlink is not created.
270  base::DeleteFile(meta_file, false);
271  ASSERT_FALSE(base::PathExists(meta_file));
272  chromeos::ClearLog();
273  collector_.WriteCrashMetaData(meta_symlink_path, "kernel",
274                                payload_file.value());
275  EXPECT_FALSE(base::PathExists(meta_file));
276  EXPECT_TRUE(FindLog("Unable to write"));
277}
278
279TEST_F(CrashCollectorTest, GetLogContents) {
280  FilePath config_file = test_dir_.Append("crash_config");
281  FilePath output_file = test_dir_.Append("crash_log");
282  const char kConfigContents[] =
283      "foobar=echo hello there | \\\n  sed -e \"s/there/world/\"";
284  ASSERT_TRUE(
285      base::WriteFile(config_file, kConfigContents, strlen(kConfigContents)));
286  base::DeleteFile(FilePath(output_file), false);
287  EXPECT_FALSE(collector_.GetLogContents(config_file,
288                                         "barfoo",
289                                         output_file));
290  EXPECT_FALSE(base::PathExists(output_file));
291  base::DeleteFile(FilePath(output_file), false);
292  EXPECT_TRUE(collector_.GetLogContents(config_file,
293                                        "foobar",
294                                        output_file));
295  ASSERT_TRUE(base::PathExists(output_file));
296  std::string contents;
297  EXPECT_TRUE(base::ReadFileToString(output_file, &contents));
298  EXPECT_EQ("hello world\n", contents);
299}
300