blob: 61a5ef5b7dd9f29cf78974ecd0eb92159a3ebf48 [file] [log] [blame]
Vishnu Naire97d6122018-01-18 13:58:56 -08001/*
2 * Copyright (C) 2018 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 <gmock/gmock.h>
18#include <gtest/gtest.h>
19
20#include <fcntl.h>
21#include <libgen.h>
22
23#include <android-base/file.h>
24#include <cutils/properties.h>
25#include <ziparchive/zip_archive.h>
26
27#include "dumpstate.h"
28
29#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
30
31namespace android {
32namespace os {
33namespace dumpstate {
34
35using ::testing::Test;
36using ::std::literals::chrono_literals::operator""s;
37
38struct SectionInfo {
39 std::string name;
40 status_t status;
41 int32_t size_bytes;
42 int32_t duration_ms;
43};
44
45/**
46 * Listens to bugreport progress and updates the user by writing the progress to STDOUT. All the
47 * section details generated by dumpstate are added to a vector to be used by Tests later.
48 */
49class DumpstateListener : public IDumpstateListener {
50 public:
51 int outFd_, max_progress_;
52 std::shared_ptr<std::vector<SectionInfo>> sections_;
53 DumpstateListener(int fd, std::shared_ptr<std::vector<SectionInfo>> sections)
54 : outFd_(fd), max_progress_(5000), sections_(sections) {
55 }
56 binder::Status onProgressUpdated(int32_t progress) override {
57 dprintf(outFd_, "\rIn progress %d/%d", progress, max_progress_);
58 return binder::Status::ok();
59 }
60 binder::Status onMaxProgressUpdated(int32_t max_progress) override {
61 max_progress_ = max_progress;
62 return binder::Status::ok();
63 }
64 binder::Status onSectionComplete(const ::std::string& name, int32_t status, int32_t size_bytes,
65 int32_t duration_ms) override {
66 sections_->push_back({name, status, size_bytes, duration_ms});
67 return binder::Status::ok();
68 }
69 IBinder* onAsBinder() override {
70 return nullptr;
71 }
72};
73
74/**
75 * Generates bug report and provide access to the bug report file and other info for other tests.
76 * Since bug report generation is slow, the bugreport is only generated once.
77 */
78class ZippedBugreportGenerationTest : public Test {
79 public:
80 static std::shared_ptr<std::vector<SectionInfo>> sections;
81 static Dumpstate& ds;
82 static std::chrono::milliseconds duration;
83 static void SetUpTestCase() {
84 property_set("dumpstate.options", "bugreportplus");
85 // clang-format off
86 char* argv[] = {
87 (char*)"dumpstate",
88 (char*)"-d",
89 (char*)"-z",
90 (char*)"-B",
91 (char*)"-o",
92 (char*)dirname(android::base::GetExecutablePath().c_str())
93 };
94 // clang-format on
95 sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout)), sections));
96 ds.listener_ = listener;
97 ds.listener_name_ = "Smokey";
98 ds.report_section_ = true;
99 auto start = std::chrono::steady_clock::now();
100 run_main(ARRAY_SIZE(argv), argv);
101 auto end = std::chrono::steady_clock::now();
102 duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
103 }
104
105 static const char* getZipFilePath() {
106 return ds.GetPath(".zip").c_str();
107 }
108};
109std::shared_ptr<std::vector<SectionInfo>> ZippedBugreportGenerationTest::sections =
110 std::make_shared<std::vector<SectionInfo>>();
111Dumpstate& ZippedBugreportGenerationTest::ds = Dumpstate::GetInstance();
112std::chrono::milliseconds ZippedBugreportGenerationTest::duration = 0s;
113
114TEST_F(ZippedBugreportGenerationTest, IsGeneratedWithoutErrors) {
115 EXPECT_EQ(access(getZipFilePath(), F_OK), 0);
116}
117
118TEST_F(ZippedBugreportGenerationTest, Is3MBto30MBinSize) {
119 struct stat st;
120 EXPECT_EQ(stat(getZipFilePath(), &st), 0);
121 EXPECT_GE(st.st_size, 3000000 /* 3MB */);
122 EXPECT_LE(st.st_size, 30000000 /* 30MB */);
123}
124
125TEST_F(ZippedBugreportGenerationTest, TakesBetween30And150Seconds) {
126 EXPECT_GE(duration, 30s) << "Expected completion in more than 30s. Actual time "
127 << duration.count() << " s.";
128 EXPECT_LE(duration, 150s) << "Expected completion in less than 150s. Actual time "
129 << duration.count() << " s.";
130}
131
132/**
133 * Run tests on contents of zipped bug report.
134 */
135class ZippedBugReportContentsTest : public Test {
136 public:
137 ZipArchiveHandle handle;
138 void SetUp() {
139 ASSERT_EQ(OpenArchive(ZippedBugreportGenerationTest::getZipFilePath(), &handle), 0);
140 }
141 void TearDown() {
142 CloseArchive(handle);
143 }
144
145 void FileExists(const char* filename, uint32_t minsize, uint32_t maxsize) {
146 ZipEntry entry;
147 EXPECT_EQ(FindEntry(handle, ZipString(filename), &entry), 0);
148 EXPECT_GT(entry.uncompressed_length, minsize);
149 EXPECT_LT(entry.uncompressed_length, maxsize);
150 }
151};
152
153TEST_F(ZippedBugReportContentsTest, ContainsMainEntry) {
154 ZipEntry mainEntryLoc;
155 // contains main entry name file
156 EXPECT_EQ(FindEntry(handle, ZipString("main_entry.txt"), &mainEntryLoc), 0);
157
158 char* buf = new char[mainEntryLoc.uncompressed_length];
159 ExtractToMemory(handle, &mainEntryLoc, (uint8_t*)buf, mainEntryLoc.uncompressed_length);
160 delete[] buf;
161
162 // contains main entry file
163 FileExists(buf, 1000000U, 50000000U);
164}
165
166TEST_F(ZippedBugReportContentsTest, ContainsVersion) {
167 ZipEntry entry;
168 // contains main entry name file
169 EXPECT_EQ(FindEntry(handle, ZipString("version.txt"), &entry), 0);
170
171 char* buf = new char[entry.uncompressed_length + 1];
172 ExtractToMemory(handle, &entry, (uint8_t*)buf, entry.uncompressed_length);
173 buf[entry.uncompressed_length] = 0;
174 EXPECT_STREQ(buf, ZippedBugreportGenerationTest::ds.version_.c_str());
175 delete[] buf;
176}
177
178TEST_F(ZippedBugReportContentsTest, ContainsBoardSpecificFiles) {
179 FileExists("dumpstate_board.bin", 1000000U, 80000000U);
180 FileExists("dumpstate_board.txt", 100000U, 1000000U);
181}
182
183// Spot check on some files pulled from the file system
184TEST_F(ZippedBugReportContentsTest, ContainsSomeFileSystemFiles) {
185 // FS/proc/*/mountinfo size > 0
186 FileExists("FS/proc/1/mountinfo", 0U, 100000U);
187
188 // FS/data/misc/profiles/cur/0/*/primary.prof size > 0
189 FileExists("FS/data/misc/profiles/cur/0/com.android.phone/primary.prof", 0U, 100000U);
190}
191
192/**
193 * Runs tests on section data generated by dumpstate and captured by DumpstateListener.
194 */
195class BugreportSectionTest : public Test {
196 public:
197 int numMatches(const std::string& substring) {
198 int matches = 0;
199 for (auto const& section : *ZippedBugreportGenerationTest::sections) {
200 if (section.name.find(substring) != std::string::npos) {
201 matches++;
202 }
203 }
204 return matches;
205 }
206 void SectionExists(const std::string& sectionName, int minsize) {
207 for (auto const& section : *ZippedBugreportGenerationTest::sections) {
208 if (sectionName == section.name) {
209 EXPECT_GE(section.size_bytes, minsize);
210 return;
211 }
212 }
213 FAIL() << sectionName << " not found.";
214 }
215};
216
217// Test all sections are generated without timeouts or errors
218TEST_F(BugreportSectionTest, GeneratedWithoutErrors) {
219 for (auto const& section : *ZippedBugreportGenerationTest::sections) {
220 EXPECT_EQ(section.status, 0) << section.name << " failed with status " << section.status;
221 }
222}
223
224TEST_F(BugreportSectionTest, Atleast3CriticalDumpsysSectionsGenerated) {
225 int numSections = numMatches("DUMPSYS CRITICAL");
226 EXPECT_GE(numSections, 3);
227}
228
229TEST_F(BugreportSectionTest, Atleast2HighDumpsysSectionsGenerated) {
230 int numSections = numMatches("DUMPSYS HIGH");
231 EXPECT_GE(numSections, 2);
232}
233
234TEST_F(BugreportSectionTest, Atleast50NormalDumpsysSectionsGenerated) {
235 int allSections = numMatches("DUMPSYS");
236 int criticalSections = numMatches("DUMPSYS CRITICAL");
237 int highSections = numMatches("DUMPSYS HIGH");
238 int normalSections = allSections - criticalSections - highSections;
239
240 EXPECT_GE(normalSections, 50) << "Total sections less than 50 (Critical:" << criticalSections
241 << "High:" << highSections << "Normal:" << normalSections << ")";
242}
243
244TEST_F(BugreportSectionTest, Atleast1ProtoDumpsysSectionGenerated) {
245 int numSections = numMatches("proto/");
246 EXPECT_GE(numSections, 1);
247}
248
249// Test if some critical sections are being generated.
250TEST_F(BugreportSectionTest, CriticalSurfaceFlingerSectionGenerated) {
251 SectionExists("DUMPSYS CRITICAL - SurfaceFlinger", /* bytes= */ 10000);
252}
253
254TEST_F(BugreportSectionTest, ActivitySectionsGenerated) {
255 SectionExists("DUMPSYS CRITICAL - activity", /* bytes= */ 5000);
256 SectionExists("DUMPSYS - activity", /* bytes= */ 10000);
257}
258
259TEST_F(BugreportSectionTest, CpuinfoSectionGenerated) {
260 SectionExists("DUMPSYS CRITICAL - cpuinfo", /* bytes= */ 1000);
261}
262
263TEST_F(BugreportSectionTest, WindowSectionGenerated) {
264 SectionExists("DUMPSYS CRITICAL - window", /* bytes= */ 20000);
265}
266
267TEST_F(BugreportSectionTest, ConnectivitySectionsGenerated) {
268 SectionExists("DUMPSYS HIGH - connectivity", /* bytes= */ 5000);
269 SectionExists("DUMPSYS - connectivity", /* bytes= */ 5000);
270}
271
272TEST_F(BugreportSectionTest, MeminfoSectionGenerated) {
273 SectionExists("DUMPSYS HIGH - meminfo", /* bytes= */ 100000);
274}
275
276TEST_F(BugreportSectionTest, BatteryStatsSectionGenerated) {
277 SectionExists("DUMPSYS - batterystats", /* bytes= */ 1000);
278}
279
280TEST_F(BugreportSectionTest, WifiSectionGenerated) {
281 SectionExists("DUMPSYS - wifi", /* bytes= */ 100000);
282}
283
284} // namespace dumpstate
285} // namespace os
286} // namespace android