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