Home | History | Annotate | Download | only in adb
      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