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