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