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