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