1 // Copyright (C) 2017 The Android Open Source Project 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 #define DEBUG false 15 #include "Log.h" 16 17 #include "Reporter.h" 18 19 #include <android/os/BnIncidentReportStatusListener.h> 20 #include <frameworks/base/core/proto/android/os/header.pb.h> 21 22 #include <dirent.h> 23 #include <string.h> 24 25 #include <android-base/file.h> 26 #include <gmock/gmock.h> 27 #include <gtest/gtest.h> 28 29 using namespace android; 30 using namespace android::base; 31 using namespace android::binder; 32 using namespace android::os; 33 using namespace android::os::incidentd; 34 using namespace std; 35 using ::testing::StrEq; 36 using ::testing::Test; 37 38 namespace { 39 /* 40 void getHeaderData(const IncidentHeaderProto& headerProto, vector<uint8_t>* out) { 41 out->clear(); 42 auto serialized = headerProto.SerializeAsString(); 43 if (serialized.empty()) return; 44 out->resize(serialized.length()); 45 std::copy(serialized.begin(), serialized.end(), out->begin()); 46 } 47 */ 48 } 49 50 class TestListener : public IIncidentReportStatusListener { 51 public: 52 int startInvoked; 53 int finishInvoked; 54 int failedInvoked; 55 map<int, int> startSections; 56 map<int, int> finishSections; 57 58 TestListener() : startInvoked(0), finishInvoked(0), failedInvoked(0){}; 59 virtual ~TestListener(){}; 60 61 virtual Status onReportStarted() { 62 startInvoked++; 63 return Status::ok(); 64 }; 65 virtual Status onReportSectionStatus(int section, int status) { 66 switch (status) { 67 case IIncidentReportStatusListener::STATUS_STARTING: 68 if (startSections.count(section) == 0) startSections[section] = 0; 69 startSections[section] = startSections[section] + 1; 70 break; 71 case IIncidentReportStatusListener::STATUS_FINISHED: 72 if (finishSections.count(section) == 0) finishSections[section] = 0; 73 finishSections[section] = finishSections[section] + 1; 74 break; 75 } 76 return Status::ok(); 77 }; 78 virtual Status onReportFinished() { 79 finishInvoked++; 80 return Status::ok(); 81 }; 82 virtual Status onReportFailed() { 83 failedInvoked++; 84 return Status::ok(); 85 }; 86 87 int sectionStarted(int sectionId) const { 88 map<int, int>::const_iterator found = startSections.find(sectionId); 89 if (found != startSections.end()) { 90 return found->second; 91 } else { 92 return 0; 93 } 94 }; 95 96 int sectionFinished(int sectionId) const { 97 map<int, int>::const_iterator found = finishSections.find(sectionId); 98 if (found != finishSections.end()) { 99 return found->second; 100 } else { 101 return 0; 102 } 103 }; 104 105 protected: 106 virtual IBinder* onAsBinder() override { return nullptr; }; 107 }; 108 109 class ReporterTest : public Test { 110 public: 111 virtual void SetUp() { 112 listener = new TestListener(); 113 } 114 115 vector<string> InspectFiles() { 116 DIR* dir; 117 struct dirent* entry; 118 vector<string> results; 119 120 string dirbase = string(td.path) + "/"; 121 dir = opendir(td.path); 122 123 while ((entry = readdir(dir)) != NULL) { 124 if (entry->d_name[0] == '.') { 125 continue; 126 } 127 string filename = dirbase + entry->d_name; 128 string content; 129 ReadFileToString(filename, &content); 130 results.push_back(content); 131 } 132 return results; 133 } 134 135 protected: 136 TemporaryDir td; 137 sp<TestListener> listener; 138 size_t size; 139 }; 140 141 TEST_F(ReporterTest, IncidentReportArgs) { 142 IncidentReportArgs args1, args2; 143 args1.addSection(1); 144 args2.addSection(3); 145 146 args1.merge(args2); 147 ASSERT_TRUE(args1.containsSection(1, false)); 148 ASSERT_FALSE(args1.containsSection(2, false)); 149 ASSERT_TRUE(args1.containsSection(3, false)); 150 } 151 152 /* 153 TEST_F(ReporterTest, RunReportEmpty) { 154 vector<sp<ReportRequest>> requests; 155 sp<Reporter> reporter = new Reporter(requests, td.path); 156 157 ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport(&size)); 158 EXPECT_EQ(0, listener->startInvoked); 159 EXPECT_EQ(0, listener->finishInvoked); 160 EXPECT_TRUE(listener->startSections.empty()); 161 EXPECT_TRUE(listener->finishSections.empty()); 162 EXPECT_EQ(0, listener->failedInvoked); 163 } 164 165 TEST_F(ReporterTest, RunReportWithHeaders) { 166 TemporaryFile tf; 167 IncidentReportArgs args1, args2; 168 args1.addSection(1); 169 args2.addSection(2); 170 IncidentHeaderProto header; 171 header.set_alert_id(12); 172 173 vector<uint8_t> out; 174 getHeaderData(header, &out); 175 args2.addHeader(out); 176 177 sp<WorkDirectory> workDirectory = new WorkDirectory(td.path); 178 sp<ReportBatch> batch = new ReportBatch(); 179 batch->addStreamingReport(args1, listener, tf.fd); 180 sp<Reporter> reporter = new Reporter(workDirectory, batch); 181 182 ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport(&size)); 183 184 string result; 185 ReadFileToString(tf.path, &result); 186 EXPECT_THAT(result, StrEq("\n\x2" 187 "\b\f")); 188 189 EXPECT_EQ(listener->startInvoked, 1); 190 EXPECT_EQ(listener->finishInvoked, 1); 191 EXPECT_FALSE(listener->startSections.empty()); 192 EXPECT_FALSE(listener->finishSections.empty()); 193 EXPECT_EQ(listener->failedInvoked, 0); 194 } 195 196 TEST_F(ReporterTest, RunReportToGivenDirectory) { 197 IncidentReportArgs args; 198 IncidentHeaderProto header1, header2; 199 header1.set_alert_id(12); 200 header2.set_reason("abcd"); 201 202 vector<uint8_t> out; 203 getHeaderData(header1, &out); 204 args.addHeader(out); 205 getHeaderData(header2, &out); 206 args.addHeader(out); 207 208 vector<sp<ReportRequest>> requests; 209 requests.push_back(new ReportRequest(args, listener, -1)); 210 sp<Reporter> reporter = new Reporter(requests, td.path); 211 212 ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport(&size)); 213 vector<string> results = InspectFiles(); 214 ASSERT_EQ(results.size(), 1UL); 215 EXPECT_EQ(results[0], 216 "\n\x2" 217 "\b\f\n\x6" 218 "\x12\x4" 219 "abcd"); 220 } 221 222 TEST_F(ReporterTest, ReportMetadata) { 223 IncidentReportArgs args; 224 args.addSection(1); 225 args.setPrivacyPolicy(android::os::PRIVACY_POLICY_EXPLICIT); 226 vector<sp<ReportRequest>> requests; 227 requests.push_back(new ReportRequest(args, listener, -1)); 228 sp<Reporter> reporter = new Reporter(requests, td.path); 229 230 ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport(&size)); 231 IncidentMetadata metadata = reporter->metadata(); 232 EXPECT_EQ(IncidentMetadata_Destination_EXPLICIT, metadata.dest()); 233 EXPECT_EQ(1, metadata.request_size()); 234 EXPECT_TRUE(metadata.use_dropbox()); 235 EXPECT_EQ(0, metadata.sections_size()); 236 } 237 238 TEST_F(ReporterTest, RunReportLocal_1_2) { 239 IncidentReportArgs args; 240 args.addSection(1); 241 args.addSection(2); 242 args.setPrivacyPolicy(android::os::PRIVACY_POLICY_LOCAL); 243 244 vector<sp<ReportRequest>> requests; 245 requests.push_back(new ReportRequest(args, listener, -1)); 246 sp<Reporter> reporter = new Reporter(requests, td.path); 247 248 ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport(&size)); 249 250 EXPECT_EQ(1, listener->sectionStarted(1)); 251 EXPECT_EQ(1, listener->sectionFinished(1)); 252 EXPECT_EQ(1, listener->sectionStarted(2)); 253 EXPECT_EQ(1, listener->sectionFinished(2)); 254 255 // TODO: validate that a file was created in the directory 256 } 257 */ 258