1 /*
2  * Copyright (C) 2010 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 "unclean_shutdown_collector.h"
18 
19 #include <unistd.h>
20 
21 #include <base/files/file_util.h>
22 #include <base/files/scoped_temp_dir.h>
23 #include <base/strings/string_util.h>
24 #include <brillo/syslog_logging.h>
25 #include <gmock/gmock.h>
26 #include <gtest/gtest.h>
27 
28 using base::FilePath;
29 using ::brillo::FindLog;
30 
31 namespace {
32 
33 int s_crashes = 0;
34 bool s_metrics = true;
35 
CountCrash()36 void CountCrash() {
37   ++s_crashes;
38 }
39 
IsMetrics()40 bool IsMetrics() {
41   return s_metrics;
42 }
43 
44 }  // namespace
45 
46 class UncleanShutdownCollectorMock : public UncleanShutdownCollector {
47  public:
48   MOCK_METHOD0(SetUpDBus, void());
49 };
50 
51 class UncleanShutdownCollectorTest : public ::testing::Test {
SetUp()52   void SetUp() {
53     s_crashes = 0;
54 
55     EXPECT_CALL(collector_, SetUpDBus()).WillRepeatedly(testing::Return());
56 
57     collector_.Initialize(CountCrash,
58                           IsMetrics);
59 
60     EXPECT_TRUE(test_dir_.CreateUniqueTempDir());
61 
62     test_directory_ = test_dir_.path().Append("test");
63     test_unclean_ = test_dir_.path().Append("test/unclean");
64 
65     collector_.unclean_shutdown_file_ = test_unclean_.value().c_str();
66     base::DeleteFile(test_unclean_, true);
67     // Set up an alternate power manager state file as well
68     collector_.powerd_suspended_file_ =
69         test_dir_.path().Append("test/suspended");
70     brillo::ClearLog();
71   }
72 
73  protected:
WriteStringToFile(const FilePath & file_path,const char * data)74   void WriteStringToFile(const FilePath &file_path,
75                          const char *data) {
76     ASSERT_EQ(strlen(data), base::WriteFile(file_path, data, strlen(data)));
77   }
78 
79   UncleanShutdownCollectorMock collector_;
80 
81   // Temporary directory used for tests.
82   base::ScopedTempDir test_dir_;
83   FilePath test_directory_;
84   FilePath test_unclean_;
85 };
86 
TEST_F(UncleanShutdownCollectorTest,EnableWithoutParent)87 TEST_F(UncleanShutdownCollectorTest, EnableWithoutParent) {
88   ASSERT_TRUE(collector_.Enable());
89   ASSERT_TRUE(base::PathExists(test_unclean_));
90 }
91 
TEST_F(UncleanShutdownCollectorTest,EnableWithParent)92 TEST_F(UncleanShutdownCollectorTest, EnableWithParent) {
93   mkdir(test_directory_.value().c_str(), 0777);
94   ASSERT_TRUE(collector_.Enable());
95   ASSERT_TRUE(base::PathExists(test_unclean_));
96 }
97 
TEST_F(UncleanShutdownCollectorTest,EnableCannotWrite)98 TEST_F(UncleanShutdownCollectorTest, EnableCannotWrite) {
99   collector_.unclean_shutdown_file_ = "/bad/path";
100   ASSERT_FALSE(collector_.Enable());
101   ASSERT_TRUE(FindLog("Unable to create shutdown check file"));
102 }
103 
TEST_F(UncleanShutdownCollectorTest,CollectTrue)104 TEST_F(UncleanShutdownCollectorTest, CollectTrue) {
105   ASSERT_TRUE(collector_.Enable());
106   ASSERT_TRUE(base::PathExists(test_unclean_));
107   ASSERT_TRUE(collector_.Collect());
108   ASSERT_FALSE(base::PathExists(test_unclean_));
109   ASSERT_EQ(1, s_crashes);
110   ASSERT_TRUE(FindLog("Last shutdown was not clean"));
111 }
112 
TEST_F(UncleanShutdownCollectorTest,CollectFalse)113 TEST_F(UncleanShutdownCollectorTest, CollectFalse) {
114   ASSERT_FALSE(collector_.Collect());
115   ASSERT_EQ(0, s_crashes);
116 }
117 
TEST_F(UncleanShutdownCollectorTest,CollectDeadBatterySuspended)118 TEST_F(UncleanShutdownCollectorTest, CollectDeadBatterySuspended) {
119   ASSERT_TRUE(collector_.Enable());
120   ASSERT_TRUE(base::PathExists(test_unclean_));
121   base::WriteFile(collector_.powerd_suspended_file_, "", 0);
122   ASSERT_FALSE(collector_.Collect());
123   ASSERT_FALSE(base::PathExists(test_unclean_));
124   ASSERT_FALSE(base::PathExists(collector_.powerd_suspended_file_));
125   ASSERT_EQ(0, s_crashes);
126   ASSERT_TRUE(FindLog("Unclean shutdown occurred while suspended."));
127 }
128 
TEST_F(UncleanShutdownCollectorTest,Disable)129 TEST_F(UncleanShutdownCollectorTest, Disable) {
130   ASSERT_TRUE(collector_.Enable());
131   ASSERT_TRUE(base::PathExists(test_unclean_));
132   ASSERT_TRUE(collector_.Disable());
133   ASSERT_FALSE(base::PathExists(test_unclean_));
134   ASSERT_FALSE(collector_.Collect());
135 }
136 
TEST_F(UncleanShutdownCollectorTest,DisableWhenNotEnabled)137 TEST_F(UncleanShutdownCollectorTest, DisableWhenNotEnabled) {
138   ASSERT_TRUE(collector_.Disable());
139 }
140 
TEST_F(UncleanShutdownCollectorTest,CantDisable)141 TEST_F(UncleanShutdownCollectorTest, CantDisable) {
142   mkdir(test_directory_.value().c_str(), 0700);
143   if (mkdir(test_unclean_.value().c_str(), 0700)) {
144     ASSERT_EQ(EEXIST, errno)
145         << "Error while creating directory '" << test_unclean_.value()
146         << "': " << strerror(errno);
147   }
148   ASSERT_EQ(0, base::WriteFile(test_unclean_.Append("foo"), "", 0))
149       << "Error while creating empty file '"
150       << test_unclean_.Append("foo").value() << "': " << strerror(errno);
151   ASSERT_FALSE(collector_.Disable());
152   rmdir(test_unclean_.value().c_str());
153 }
154