1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "crash_collector_test.h"
18 
19 #include <unistd.h>
20 #include <utility>
21 
22 #include <base/files/file_util.h>
23 #include <base/files/scoped_temp_dir.h>
24 #include <base/strings/string_util.h>
25 #include <base/strings/stringprintf.h>
26 #include <brillo/syslog_logging.h>
27 #include <gtest/gtest.h>
28 
29 #include "crash_collector.h"
30 
31 using base::FilePath;
32 using base::StringPrintf;
33 using brillo::FindLog;
34 using ::testing::Invoke;
35 using ::testing::Return;
36 
37 namespace {
38 
CountCrash()39 void CountCrash() {
40   ADD_FAILURE();
41 }
42 
IsMetrics()43 bool IsMetrics() {
44   ADD_FAILURE();
45   return false;
46 }
47 
48 }  // namespace
49 
50 class CrashCollectorTest : public ::testing::Test {
51  public:
SetUp()52   void SetUp() {
53     EXPECT_CALL(collector_, SetUpDBus()).WillRepeatedly(Return());
54 
55     collector_.Initialize(CountCrash, IsMetrics);
56     EXPECT_TRUE(test_dir_.CreateUniqueTempDir());
57     brillo::ClearLog();
58   }
59 
60   bool CheckHasCapacity();
61 
62  protected:
63   CrashCollectorMock collector_;
64 
65   // Temporary directory used for tests.
66   base::ScopedTempDir test_dir_;
67 };
68 
TEST_F(CrashCollectorTest,Initialize)69 TEST_F(CrashCollectorTest, Initialize) {
70   ASSERT_TRUE(CountCrash == collector_.count_crash_function_);
71   ASSERT_TRUE(IsMetrics == collector_.is_feedback_allowed_function_);
72 }
73 
TEST_F(CrashCollectorTest,WriteNewFile)74 TEST_F(CrashCollectorTest, WriteNewFile) {
75   FilePath test_file = test_dir_.path().Append("test_new");
76   const char kBuffer[] = "buffer";
77   EXPECT_EQ(strlen(kBuffer),
78             collector_.WriteNewFile(test_file,
79                                     kBuffer,
80                                     strlen(kBuffer)));
81   EXPECT_LT(collector_.WriteNewFile(test_file,
82                                     kBuffer,
83                                     strlen(kBuffer)), 0);
84 }
85 
TEST_F(CrashCollectorTest,Sanitize)86 TEST_F(CrashCollectorTest, Sanitize) {
87   EXPECT_EQ("chrome", collector_.Sanitize("chrome"));
88   EXPECT_EQ("CHROME", collector_.Sanitize("CHROME"));
89   EXPECT_EQ("1chrome2", collector_.Sanitize("1chrome2"));
90   EXPECT_EQ("chrome__deleted_", collector_.Sanitize("chrome (deleted)"));
91   EXPECT_EQ("foo_bar", collector_.Sanitize("foo.bar"));
92   EXPECT_EQ("", collector_.Sanitize(""));
93   EXPECT_EQ("_", collector_.Sanitize(" "));
94 }
95 
TEST_F(CrashCollectorTest,FormatDumpBasename)96 TEST_F(CrashCollectorTest, FormatDumpBasename) {
97   struct tm tm = {0};
98   tm.tm_sec = 15;
99   tm.tm_min = 50;
100   tm.tm_hour = 13;
101   tm.tm_mday = 23;
102   tm.tm_mon = 4;
103   tm.tm_year = 110;
104   tm.tm_isdst = -1;
105   std::string basename =
106       collector_.FormatDumpBasename("foo", mktime(&tm), 100);
107   ASSERT_EQ("foo.20100523.135015.100", basename);
108 }
109 
TEST_F(CrashCollectorTest,GetCrashPath)110 TEST_F(CrashCollectorTest, GetCrashPath) {
111   EXPECT_EQ("/var/spool/crash/myprog.20100101.1200.1234.core",
112             collector_.GetCrashPath(FilePath("/var/spool/crash"),
113                                     "myprog.20100101.1200.1234",
114                                     "core").value());
115   EXPECT_EQ("/home/chronos/user/crash/chrome.20100101.1200.1234.dmp",
116             collector_.GetCrashPath(FilePath("/home/chronos/user/crash"),
117                                     "chrome.20100101.1200.1234",
118                                     "dmp").value());
119 }
120 
121 
CheckHasCapacity()122 bool CrashCollectorTest::CheckHasCapacity() {
123   const char* kFullMessage =
124       StringPrintf("Crash directory %s already full",
125                    test_dir_.path().value().c_str()).c_str();
126   bool has_capacity = collector_.CheckHasCapacity(test_dir_.path());
127   bool has_message = FindLog(kFullMessage);
128   EXPECT_EQ(has_message, !has_capacity);
129   return has_capacity;
130 }
131 
TEST_F(CrashCollectorTest,CheckHasCapacityUsual)132 TEST_F(CrashCollectorTest, CheckHasCapacityUsual) {
133   // Test kMaxCrashDirectorySize - 1 non-meta files can be added.
134   for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) {
135     base::WriteFile(test_dir_.path().Append(StringPrintf("file%d.core", i)),
136                     "", 0);
137     EXPECT_TRUE(CheckHasCapacity());
138   }
139 
140   // Test an additional kMaxCrashDirectorySize - 1 meta files fit.
141   for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) {
142     base::WriteFile(test_dir_.path().Append(StringPrintf("file%d.meta", i)),
143                     "", 0);
144     EXPECT_TRUE(CheckHasCapacity());
145   }
146 
147   // Test an additional kMaxCrashDirectorySize meta files don't fit.
148   for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize; ++i) {
149     base::WriteFile(test_dir_.path().Append(StringPrintf("overage%d.meta", i)),
150                     "", 0);
151     EXPECT_FALSE(CheckHasCapacity());
152   }
153 }
154 
TEST_F(CrashCollectorTest,CheckHasCapacityCorrectBasename)155 TEST_F(CrashCollectorTest, CheckHasCapacityCorrectBasename) {
156   // Test kMaxCrashDirectorySize - 1 files can be added.
157   for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) {
158     base::WriteFile(test_dir_.path().Append(StringPrintf("file.%d.core", i)),
159                     "", 0);
160     EXPECT_TRUE(CheckHasCapacity());
161   }
162   base::WriteFile(test_dir_.path().Append("file.last.core"), "", 0);
163   EXPECT_FALSE(CheckHasCapacity());
164 }
165 
TEST_F(CrashCollectorTest,CheckHasCapacityStrangeNames)166 TEST_F(CrashCollectorTest, CheckHasCapacityStrangeNames) {
167   // Test many files with different extensions and same base fit.
168   for (int i = 0; i < 5 * CrashCollector::kMaxCrashDirectorySize; ++i) {
169     base::WriteFile(test_dir_.path().Append(StringPrintf("a.%d", i)), "", 0);
170     EXPECT_TRUE(CheckHasCapacity());
171   }
172   // Test dot files are treated as individual files.
173   for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 2; ++i) {
174     base::WriteFile(test_dir_.path().Append(StringPrintf(".file%d", i)), "", 0);
175     EXPECT_TRUE(CheckHasCapacity());
176   }
177   base::WriteFile(test_dir_.path().Append("normal.meta"), "", 0);
178   EXPECT_FALSE(CheckHasCapacity());
179 }
180 
TEST_F(CrashCollectorTest,MetaData)181 TEST_F(CrashCollectorTest, MetaData) {
182   const char kMetaFileBasename[] = "generated.meta";
183   FilePath meta_file = test_dir_.path().Append(kMetaFileBasename);
184   FilePath payload_file = test_dir_.path().Append("payload-file");
185   std::string contents;
186   const char kPayload[] = "foo";
187   ASSERT_TRUE(base::WriteFile(payload_file, kPayload, strlen(kPayload)));
188   collector_.AddCrashMetaData("foo", "bar");
189   collector_.WriteCrashMetaData(meta_file, "kernel", payload_file.value());
190   EXPECT_TRUE(base::ReadFileToString(meta_file, &contents));
191   const std::string kExpectedMeta =
192       StringPrintf("foo=bar\n"
193           "exec_name=kernel\n"
194           "payload=%s\n"
195           "payload_size=3\n"
196           "done=1\n",
197           test_dir_.path().Append("payload-file").value().c_str());
198   EXPECT_EQ(kExpectedMeta, contents);
199 
200   // Test target of symlink is not overwritten.
201   payload_file = test_dir_.path().Append("payload2-file");
202   ASSERT_TRUE(base::WriteFile(payload_file, kPayload, strlen(kPayload)));
203   FilePath meta_symlink_path = test_dir_.path().Append("symlink.meta");
204   ASSERT_EQ(0,
205             symlink(kMetaFileBasename,
206                     meta_symlink_path.value().c_str()));
207   ASSERT_TRUE(base::PathExists(meta_symlink_path));
208   brillo::ClearLog();
209   collector_.WriteCrashMetaData(meta_symlink_path,
210                                 "kernel",
211                                 payload_file.value());
212   // Target metadata contents should have stayed the same.
213   contents.clear();
214   EXPECT_TRUE(base::ReadFileToString(meta_file, &contents));
215   EXPECT_EQ(kExpectedMeta, contents);
216   EXPECT_TRUE(FindLog("Unable to write"));
217 
218   // Test target of dangling symlink is not created.
219   base::DeleteFile(meta_file, false);
220   ASSERT_FALSE(base::PathExists(meta_file));
221   brillo::ClearLog();
222   collector_.WriteCrashMetaData(meta_symlink_path, "kernel",
223                                 payload_file.value());
224   EXPECT_FALSE(base::PathExists(meta_file));
225   EXPECT_TRUE(FindLog("Unable to write"));
226 }
227 
TEST_F(CrashCollectorTest,GetLogContents)228 TEST_F(CrashCollectorTest, GetLogContents) {
229   FilePath config_file = test_dir_.path().Append("crash_config");
230   FilePath output_file = test_dir_.path().Append("crash_log");
231   const char kConfigContents[] =
232       "foobar=echo hello there | \\\n  sed -e \"s/there/world/\"";
233   ASSERT_TRUE(
234       base::WriteFile(config_file, kConfigContents, strlen(kConfigContents)));
235   base::DeleteFile(FilePath(output_file), false);
236   EXPECT_FALSE(collector_.GetLogContents(config_file,
237                                          "barfoo",
238                                          output_file));
239   EXPECT_FALSE(base::PathExists(output_file));
240   base::DeleteFile(FilePath(output_file), false);
241   EXPECT_TRUE(collector_.GetLogContents(config_file,
242                                         "foobar",
243                                         output_file));
244   ASSERT_TRUE(base::PathExists(output_file));
245   std::string contents;
246   EXPECT_TRUE(base::ReadFileToString(output_file, &contents));
247   EXPECT_EQ("hello world\n", contents);
248 }
249