1 /* 2 * Copyright (C) 2016 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 "bugreport.h" 18 19 #include <gmock/gmock.h> 20 #include <gtest/gtest.h> 21 22 #include <android-base/strings.h> 23 #include <android-base/test_utils.h> 24 25 #include "sysdeps.h" 26 #include "adb_utils.h" 27 28 using ::testing::_; 29 using ::testing::Action; 30 using ::testing::ActionInterface; 31 using ::testing::DoAll; 32 using ::testing::ElementsAre; 33 using ::testing::HasSubstr; 34 using ::testing::MakeAction; 35 using ::testing::Return; 36 using ::testing::StrEq; 37 using ::testing::WithArg; 38 using ::testing::internal::CaptureStderr; 39 using ::testing::internal::CaptureStdout; 40 using ::testing::internal::GetCapturedStderr; 41 using ::testing::internal::GetCapturedStdout; 42 43 // Empty function so tests don't need to be linked against file_sync_service.cpp, which requires 44 // SELinux and its transitive dependencies... 45 bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs, 46 const char* name) { 47 ADD_FAILURE() << "do_sync_pull() should have been mocked"; 48 return false; 49 } 50 51 // Empty functions so tests don't need to be linked against commandline.cpp 52 DefaultStandardStreamsCallback DEFAULT_STANDARD_STREAMS_CALLBACK(nullptr, nullptr); 53 int usage() { 54 return -42; 55 } 56 int send_shell_command(TransportType transport_type, const char* serial, const std::string& command, 57 bool disable_shell_protocol, StandardStreamsCallbackInterface* callback) { 58 ADD_FAILURE() << "send_shell_command() should have been mocked"; 59 return -42; 60 } 61 62 enum StreamType { 63 kStreamStdout, 64 kStreamStderr, 65 }; 66 67 // gmock black magic to provide a WithArg<4>(WriteOnStdout(output)) matcher 68 typedef void OnStandardStreamsCallbackFunction(StandardStreamsCallbackInterface*); 69 70 class OnStandardStreamsCallbackAction : public ActionInterface<OnStandardStreamsCallbackFunction> { 71 public: 72 explicit OnStandardStreamsCallbackAction(StreamType type, const std::string& output) 73 : type_(type), output_(output) { 74 } 75 virtual Result Perform(const ArgumentTuple& args) { 76 if (type_ == kStreamStdout) { 77 ::std::tr1::get<0>(args)->OnStdout(output_.c_str(), output_.size()); 78 } 79 if (type_ == kStreamStderr) { 80 ::std::tr1::get<0>(args)->OnStderr(output_.c_str(), output_.size()); 81 } 82 } 83 84 private: 85 StreamType type_; 86 std::string output_; 87 }; 88 89 // Matcher used to emulated StandardStreamsCallbackInterface.OnStdout(buffer, 90 // length) 91 Action<OnStandardStreamsCallbackFunction> WriteOnStdout(const std::string& output) { 92 return MakeAction(new OnStandardStreamsCallbackAction(kStreamStdout, output)); 93 } 94 95 // Matcher used to emulated StandardStreamsCallbackInterface.OnStderr(buffer, 96 // length) 97 Action<OnStandardStreamsCallbackFunction> WriteOnStderr(const std::string& output) { 98 return MakeAction(new OnStandardStreamsCallbackAction(kStreamStderr, output)); 99 } 100 101 typedef int CallbackDoneFunction(StandardStreamsCallbackInterface*); 102 103 class CallbackDoneAction : public ActionInterface<CallbackDoneFunction> { 104 public: 105 explicit CallbackDoneAction(int status) : status_(status) { 106 } 107 virtual Result Perform(const ArgumentTuple& args) { 108 int status = ::std::tr1::get<0>(args)->Done(status_); 109 return status; 110 } 111 112 private: 113 int status_; 114 }; 115 116 // Matcher used to emulated StandardStreamsCallbackInterface.Done(status) 117 Action<CallbackDoneFunction> ReturnCallbackDone(int status = -1337) { 118 return MakeAction(new CallbackDoneAction(status)); 119 } 120 121 class BugreportMock : public Bugreport { 122 public: 123 MOCK_METHOD5(SendShellCommand, 124 int(TransportType transport_type, const char* serial, const std::string& command, 125 bool disable_shell_protocol, StandardStreamsCallbackInterface* callback)); 126 MOCK_METHOD4(DoSyncPull, bool(const std::vector<const char*>& srcs, const char* dst, 127 bool copy_attrs, const char* name)); 128 MOCK_METHOD3(UpdateProgress, void(const std::string&, int, int)); 129 }; 130 131 class BugreportTest : public ::testing::Test { 132 public: 133 void SetUp() { 134 if (!getcwd(&cwd_)) { 135 ADD_FAILURE() << "getcwd failed: " << strerror(errno); 136 return; 137 } 138 } 139 140 void ExpectBugreportzVersion(const std::string& version) { 141 EXPECT_CALL(br_, 142 SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -v", false, _)) 143 .WillOnce(DoAll(WithArg<4>(WriteOnStderr(version.c_str())), 144 WithArg<4>(ReturnCallbackDone(0)))); 145 } 146 147 void ExpectProgress(int progress, int total, const std::string& file = "file.zip") { 148 EXPECT_CALL(br_, UpdateProgress(StrEq("generating " + file), progress, total)); 149 } 150 151 BugreportMock br_; 152 std::string cwd_; // TODO: make it static 153 }; 154 155 // Tests when called with invalid number of arguments 156 TEST_F(BugreportTest, InvalidNumberArgs) { 157 const char* args[] = {"bugreport", "to", "principal"}; 158 ASSERT_EQ(-42, br_.DoIt(kTransportLocal, "HannibalLecter", 3, args)); 159 } 160 161 // Tests the 'adb bugreport' option when the device does not support 'bugreportz' - it falls back 162 // to the flat-file format ('bugreport' binary on device) 163 TEST_F(BugreportTest, NoArgumentsPreNDevice) { 164 // clang-format off 165 EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -v", false, _)) 166 .WillOnce(DoAll(WithArg<4>(WriteOnStderr("")), 167 // Write some bogus output on stdout to make sure it's ignored 168 WithArg<4>(WriteOnStdout("Dude, where is my bugreportz?")), 169 WithArg<4>(ReturnCallbackDone(0)))); 170 // clang-format on 171 std::string bugreport = "Reported the bug was."; 172 CaptureStdout(); 173 EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreport", false, _)) 174 .WillOnce(DoAll(WithArg<4>(WriteOnStdout(bugreport)), Return(0))); 175 176 const char* args[] = {"bugreport"}; 177 ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 1, args)); 178 ASSERT_THAT(GetCapturedStdout(), StrEq(bugreport)); 179 } 180 181 // Tests the 'adb bugreport' option when the device supports 'bugreportz' version 1.0 - it will 182 // save the bugreport in the current directory with the name provided by the device. 183 TEST_F(BugreportTest, NoArgumentsNDevice) { 184 ExpectBugreportzVersion("1.0"); 185 186 std::string dest_file = 187 android::base::StringPrintf("%s%cda_bugreport.zip", cwd_.c_str(), OS_PATH_SEPARATOR); 188 EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _)) 189 .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip")), 190 WithArg<4>(ReturnCallbackDone()))); 191 EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file), 192 true, StrEq("generating da_bugreport.zip"))) 193 .WillOnce(Return(true)); 194 195 const char* args[] = {"bugreport"}; 196 ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 1, args)); 197 } 198 199 // Tests the 'adb bugreport' option when the device supports 'bugreportz' version 1.1 - it will 200 // save the bugreport in the current directory with the name provided by the device. 201 TEST_F(BugreportTest, NoArgumentsPostNDevice) { 202 ExpectBugreportzVersion("1.1"); 203 std::string dest_file = 204 android::base::StringPrintf("%s%cda_bugreport.zip", cwd_.c_str(), OS_PATH_SEPARATOR); 205 ExpectProgress(50, 100, "da_bugreport.zip"); 206 EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _)) 207 .WillOnce(DoAll(WithArg<4>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")), 208 WithArg<4>(WriteOnStdout("PROGRESS:50/100\n")), 209 WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip\n")), 210 WithArg<4>(ReturnCallbackDone()))); 211 EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file), 212 true, StrEq("generating da_bugreport.zip"))) 213 .WillOnce(Return(true)); 214 215 const char* args[] = {"bugreport"}; 216 ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 1, args)); 217 } 218 219 // Tests 'adb bugreport file.zip' when it succeeds and device does not support progress. 220 TEST_F(BugreportTest, OkNDevice) { 221 ExpectBugreportzVersion("1.0"); 222 EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _)) 223 .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip")), 224 WithArg<4>(ReturnCallbackDone()))); 225 EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"), 226 true, StrEq("generating file.zip"))) 227 .WillOnce(Return(true)); 228 229 const char* args[] = {"bugreport", "file.zip"}; 230 ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args)); 231 } 232 233 // Tests 'adb bugreport file.zip' when it succeeds but response was sent in 234 // multiple buffer writers and without progress updates. 235 TEST_F(BugreportTest, OkNDeviceSplitBuffer) { 236 ExpectBugreportzVersion("1.0"); 237 EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _)) 238 .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device")), 239 WithArg<4>(WriteOnStdout("/bugreport.zip")), 240 WithArg<4>(ReturnCallbackDone()))); 241 EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"), 242 true, StrEq("generating file.zip"))) 243 .WillOnce(Return(true)); 244 245 const char* args[] = {"bugreport", "file.zip"}; 246 ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args)); 247 } 248 249 // Tests 'adb bugreport file.zip' when it succeeds and displays progress. 250 TEST_F(BugreportTest, OkProgress) { 251 ExpectBugreportzVersion("1.1"); 252 ExpectProgress(1, 100); 253 ExpectProgress(10, 100); 254 ExpectProgress(50, 100); 255 ExpectProgress(99, 100); 256 // clang-format off 257 EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _)) 258 // NOTE: DoAll accepts at most 10 arguments, and we're almost reached that limit... 259 .WillOnce(DoAll( 260 // Name might change on OK, so make sure the right one is picked. 261 WithArg<4>(WriteOnStdout("BEGIN:/device/bugreport___NOT.zip\n")), 262 // Progress line in one write 263 WithArg<4>(WriteOnStdout("PROGRESS:1/100\n")), 264 // Add some bogus lines 265 WithArg<4>(WriteOnStdout("\nDUDE:SWEET\n\nBLA\n\nBLA\nBLA\n\n")), 266 // Multiple progress lines in one write 267 WithArg<4>(WriteOnStdout("PROGRESS:10/100\nPROGRESS:50/100\n")), 268 // Progress line in multiple writes 269 WithArg<4>(WriteOnStdout("PROG")), 270 WithArg<4>(WriteOnStdout("RESS:99")), 271 WithArg<4>(WriteOnStdout("/100\n")), 272 // Split last message as well, just in case 273 WithArg<4>(WriteOnStdout("OK:/device/bugreport")), 274 WithArg<4>(WriteOnStdout(".zip")), 275 WithArg<4>(ReturnCallbackDone()))); 276 // clang-format on 277 EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"), 278 true, StrEq("generating file.zip"))) 279 .WillOnce(Return(true)); 280 281 const char* args[] = {"bugreport", "file.zip"}; 282 ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args)); 283 } 284 285 // Tests 'adb bugreport dir' when it succeeds and destination is a directory. 286 TEST_F(BugreportTest, OkDirectory) { 287 ExpectBugreportzVersion("1.1"); 288 TemporaryDir td; 289 std::string dest_file = 290 android::base::StringPrintf("%s%cda_bugreport.zip", td.path, OS_PATH_SEPARATOR); 291 292 EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _)) 293 .WillOnce(DoAll(WithArg<4>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")), 294 WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip")), 295 WithArg<4>(ReturnCallbackDone()))); 296 EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file), 297 true, StrEq("generating da_bugreport.zip"))) 298 .WillOnce(Return(true)); 299 300 const char* args[] = {"bugreport", td.path}; 301 ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args)); 302 } 303 304 // Tests 'adb bugreport file' when it succeeds 305 TEST_F(BugreportTest, OkNoExtension) { 306 ExpectBugreportzVersion("1.1"); 307 EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _)) 308 .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip\n")), 309 WithArg<4>(ReturnCallbackDone()))); 310 EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"), 311 true, StrEq("generating file.zip"))) 312 .WillOnce(Return(true)); 313 314 const char* args[] = {"bugreport", "file"}; 315 ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args)); 316 } 317 318 // Tests 'adb bugreport dir' when it succeeds and destination is a directory and device runs N. 319 TEST_F(BugreportTest, OkNDeviceDirectory) { 320 ExpectBugreportzVersion("1.0"); 321 TemporaryDir td; 322 std::string dest_file = 323 android::base::StringPrintf("%s%cda_bugreport.zip", td.path, OS_PATH_SEPARATOR); 324 325 EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _)) 326 .WillOnce(DoAll(WithArg<4>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")), 327 WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip")), 328 WithArg<4>(ReturnCallbackDone()))); 329 EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file), 330 true, StrEq("generating da_bugreport.zip"))) 331 .WillOnce(Return(true)); 332 333 const char* args[] = {"bugreport", td.path}; 334 ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args)); 335 } 336 337 // Tests 'adb bugreport file.zip' when the bugreport itself failed 338 TEST_F(BugreportTest, BugreportzReturnedFail) { 339 ExpectBugreportzVersion("1.1"); 340 EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _)) 341 .WillOnce( 342 DoAll(WithArg<4>(WriteOnStdout("FAIL:D'OH!\n")), WithArg<4>(ReturnCallbackDone()))); 343 344 CaptureStderr(); 345 const char* args[] = {"bugreport", "file.zip"}; 346 ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args)); 347 ASSERT_THAT(GetCapturedStderr(), HasSubstr("D'OH!")); 348 } 349 350 // Tests 'adb bugreport file.zip' when the bugreport itself failed but response 351 // was sent in 352 // multiple buffer writes 353 TEST_F(BugreportTest, BugreportzReturnedFailSplitBuffer) { 354 ExpectBugreportzVersion("1.1"); 355 EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _)) 356 .WillOnce(DoAll(WithArg<4>(WriteOnStdout("FAIL")), WithArg<4>(WriteOnStdout(":D'OH!\n")), 357 WithArg<4>(ReturnCallbackDone()))); 358 359 CaptureStderr(); 360 const char* args[] = {"bugreport", "file.zip"}; 361 ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args)); 362 ASSERT_THAT(GetCapturedStderr(), HasSubstr("D'OH!")); 363 } 364 365 // Tests 'adb bugreport file.zip' when the bugreportz returned an unsupported 366 // response. 367 TEST_F(BugreportTest, BugreportzReturnedUnsupported) { 368 ExpectBugreportzVersion("1.1"); 369 EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _)) 370 .WillOnce(DoAll(WithArg<4>(WriteOnStdout("bugreportz? What am I, a zombie?")), 371 WithArg<4>(ReturnCallbackDone()))); 372 373 CaptureStderr(); 374 const char* args[] = {"bugreport", "file.zip"}; 375 ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args)); 376 ASSERT_THAT(GetCapturedStderr(), HasSubstr("bugreportz? What am I, a zombie?")); 377 } 378 379 // Tests 'adb bugreport file.zip' when the bugreportz -v command failed 380 TEST_F(BugreportTest, BugreportzVersionFailed) { 381 EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -v", false, _)) 382 .WillOnce(Return(666)); 383 384 const char* args[] = {"bugreport", "file.zip"}; 385 ASSERT_EQ(666, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args)); 386 } 387 388 // Tests 'adb bugreport file.zip' when the bugreportz -v returns status 0 but with no output. 389 TEST_F(BugreportTest, BugreportzVersionEmpty) { 390 ExpectBugreportzVersion(""); 391 392 const char* args[] = {"bugreport", "file.zip"}; 393 ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args)); 394 } 395 396 // Tests 'adb bugreport file.zip' when the main bugreportz command failed 397 TEST_F(BugreportTest, BugreportzFailed) { 398 ExpectBugreportzVersion("1.1"); 399 EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _)) 400 .WillOnce(Return(666)); 401 402 const char* args[] = {"bugreport", "file.zip"}; 403 ASSERT_EQ(666, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args)); 404 } 405 406 // Tests 'adb bugreport file.zip' when the bugreport could not be pulled 407 TEST_F(BugreportTest, PullFails) { 408 ExpectBugreportzVersion("1.1"); 409 EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _)) 410 .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip")), 411 WithArg<4>(ReturnCallbackDone()))); 412 EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"), 413 true, HasSubstr("file.zip"))) 414 .WillOnce(Return(false)); 415 416 const char* args[] = {"bugreport", "file.zip"}; 417 ASSERT_EQ(1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args)); 418 } 419