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::CaptureStdout;
40using ::testing::internal::GetCapturedStderr;
41using ::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...
45bool 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
52DefaultStandardStreamsCallback DEFAULT_STANDARD_STREAMS_CALLBACK(nullptr, nullptr);
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_METHOD2(UpdateProgress, void(const std::string&, 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_percentage, const std::string& file = "file.zip") {
146        EXPECT_CALL(br_, UpdateProgress(StrEq("generating " + file), progress_percentage));
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[] = {"bugreport", "to", "principal"};
156    ASSERT_EQ(1, 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    // clang-format off
163    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -v", false, _))
164        .WillOnce(DoAll(WithArg<4>(WriteOnStderr("")),
165                        // Write some bogus output on stdout to make sure it's ignored
166                        WithArg<4>(WriteOnStdout("Dude, where is my bugreportz?")),
167                        WithArg<4>(ReturnCallbackDone(0))));
168    // clang-format on
169    std::string bugreport = "Reported the bug was.";
170    CaptureStdout();
171    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreport", false, _))
172        .WillOnce(DoAll(WithArg<4>(WriteOnStdout(bugreport)), Return(0)));
173
174    const char* args[] = {"bugreport"};
175    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 1, args));
176    ASSERT_THAT(GetCapturedStdout(), StrEq(bugreport));
177}
178
179// Tests the 'adb bugreport' option when the device supports 'bugreportz' version 1.0 - it will
180// save the bugreport in the current directory with the name provided by the device.
181TEST_F(BugreportTest, NoArgumentsNDevice) {
182    ExpectBugreportzVersion("1.0");
183
184    std::string dest_file =
185        android::base::StringPrintf("%s%cda_bugreport.zip", cwd_.c_str(), OS_PATH_SEPARATOR);
186    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _))
187        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip")),
188                        WithArg<4>(ReturnCallbackDone())));
189    EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
190                                true, StrEq("pulling da_bugreport.zip")))
191        .WillOnce(Return(true));
192
193    const char* args[] = {"bugreport"};
194    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 1, args));
195}
196
197// Tests the 'adb bugreport' option when the device supports 'bugreportz' version 1.1 - it will
198// save the bugreport in the current directory with the name provided by the device.
199TEST_F(BugreportTest, NoArgumentsPostNDevice) {
200    ExpectBugreportzVersion("1.1");
201    std::string dest_file =
202        android::base::StringPrintf("%s%cda_bugreport.zip", cwd_.c_str(), OS_PATH_SEPARATOR);
203    ExpectProgress(50, "da_bugreport.zip");
204    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
205        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
206                        WithArg<4>(WriteOnStdout("PROGRESS:50/100\n")),
207                        WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip\n")),
208                        WithArg<4>(ReturnCallbackDone())));
209    EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
210                                true, StrEq("pulling da_bugreport.zip")))
211        .WillOnce(Return(true));
212
213    const char* args[] = {"bugreport"};
214    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 1, args));
215}
216
217// Tests 'adb bugreport file.zip' when it succeeds and device does not support progress.
218TEST_F(BugreportTest, OkNDevice) {
219    ExpectBugreportzVersion("1.0");
220    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _))
221        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip")),
222                        WithArg<4>(ReturnCallbackDone())));
223    EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
224                                true, StrEq("pulling file.zip")))
225        .WillOnce(Return(true));
226
227    const char* args[] = {"bugreport", "file.zip"};
228    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
229}
230
231// Tests 'adb bugreport file.zip' when it succeeds but response was sent in
232// multiple buffer writers and without progress updates.
233TEST_F(BugreportTest, OkNDeviceSplitBuffer) {
234    ExpectBugreportzVersion("1.0");
235    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _))
236        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device")),
237                        WithArg<4>(WriteOnStdout("/bugreport.zip")),
238                        WithArg<4>(ReturnCallbackDone())));
239    EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
240                                true, StrEq("pulling file.zip")))
241        .WillOnce(Return(true));
242
243    const char* args[] = {"bugreport", "file.zip"};
244    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
245}
246
247// Tests 'adb bugreport file.zip' when it succeeds and displays progress.
248TEST_F(BugreportTest, OkProgress) {
249    ExpectBugreportzVersion("1.1");
250    ExpectProgress(1);
251    ExpectProgress(10);
252    ExpectProgress(50);
253    ExpectProgress(99);
254    // clang-format off
255    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
256        // NOTE: DoAll accepts at most 10 arguments, and we're almost reached that limit...
257        .WillOnce(DoAll(
258            // Name might change on OK, so make sure the right one is picked.
259            WithArg<4>(WriteOnStdout("BEGIN:/device/bugreport___NOT.zip\n")),
260            // Progress line in one write
261            WithArg<4>(WriteOnStdout("PROGRESS:1/100\n")),
262            // Add some bogus lines
263            WithArg<4>(WriteOnStdout("\nDUDE:SWEET\n\nBLA\n\nBLA\nBLA\n\n")),
264            // Multiple progress lines in one write
265            WithArg<4>(WriteOnStdout("PROGRESS:10/100\nPROGRESS:50/100\n")),
266            // Progress line in multiple writes
267            WithArg<4>(WriteOnStdout("PROG")),
268            WithArg<4>(WriteOnStdout("RESS:99")),
269            WithArg<4>(WriteOnStdout("/100\n")),
270            // Split last message as well, just in case
271            WithArg<4>(WriteOnStdout("OK:/device/bugreport")),
272            WithArg<4>(WriteOnStdout(".zip")),
273            WithArg<4>(ReturnCallbackDone())));
274    // clang-format on
275    EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
276                                true, StrEq("pulling file.zip")))
277        .WillOnce(Return(true));
278
279    const char* args[] = {"bugreport", "file.zip"};
280    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
281}
282
283// Tests 'adb bugreport file.zip' when it succeeds and displays progress, even if progress recedes.
284TEST_F(BugreportTest, OkProgressAlwaysForward) {
285    ExpectBugreportzVersion("1.1");
286    ExpectProgress(1);
287    ExpectProgress(50);
288    ExpectProgress(75);
289    // clang-format off
290    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
291        // NOTE: DoAll accepts at most 10 arguments, and we're almost reached that limit...
292        .WillOnce(DoAll(
293            WithArg<4>(WriteOnStdout("BEGIN:/device/bugreport.zip\n")),
294            WithArg<4>(WriteOnStdout("PROGRESS:1/100\n")), // 1%
295            WithArg<4>(WriteOnStdout("PROGRESS:50/100\n")), // 50%
296            // 25% should be ignored becaused it receded.
297            WithArg<4>(WriteOnStdout("PROGRESS:25/100\n")), // 25%
298            WithArg<4>(WriteOnStdout("PROGRESS:75/100\n")), // 75%
299            // 75% should be ignored becaused it didn't change.
300            WithArg<4>(WriteOnStdout("PROGRESS:75/100\n")), // 75%
301            // Try a receeding percentage with a different max progress
302            WithArg<4>(WriteOnStdout("PROGRESS:700/1000\n")), // 70%
303            WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip")),
304            WithArg<4>(ReturnCallbackDone())));
305    // clang-format on
306    EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
307                                true, StrEq("pulling file.zip")))
308        .WillOnce(Return(true));
309
310    const char* args[] = {"bugreport", "file.zip"};
311    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
312}
313
314// Tests 'adb bugreport file.zip' when it succeeds and displays the initial progress of 0%
315TEST_F(BugreportTest, OkProgressZeroPercentIsNotIgnored) {
316    ExpectBugreportzVersion("1.1");
317    ExpectProgress(0);
318    ExpectProgress(1);
319    // clang-format off
320    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
321        // NOTE: DoAll accepts at most 10 arguments, and we're almost reached that limit...
322        .WillOnce(DoAll(
323            WithArg<4>(WriteOnStdout("BEGIN:/device/bugreport.zip\n")),
324            WithArg<4>(WriteOnStdout("PROGRESS:1/100000\n")),
325            WithArg<4>(WriteOnStdout("PROGRESS:1/100\n")), // 1%
326            WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip")),
327            WithArg<4>(ReturnCallbackDone())));
328    // clang-format on
329    EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
330                                true, StrEq("pulling file.zip")))
331        .WillOnce(Return(true));
332
333    const char* args[] = {"bugreport", "file.zip"};
334    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
335}
336
337// Tests 'adb bugreport dir' when it succeeds and destination is a directory.
338TEST_F(BugreportTest, OkDirectory) {
339    ExpectBugreportzVersion("1.1");
340    TemporaryDir td;
341    std::string dest_file =
342        android::base::StringPrintf("%s%cda_bugreport.zip", td.path, OS_PATH_SEPARATOR);
343
344    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
345        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
346                        WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip")),
347                        WithArg<4>(ReturnCallbackDone())));
348    EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
349                                true, StrEq("pulling da_bugreport.zip")))
350        .WillOnce(Return(true));
351
352    const char* args[] = {"bugreport", td.path};
353    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
354}
355
356// Tests 'adb bugreport file' when it succeeds
357TEST_F(BugreportTest, OkNoExtension) {
358    ExpectBugreportzVersion("1.1");
359    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
360        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip\n")),
361                        WithArg<4>(ReturnCallbackDone())));
362    EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
363                                true, StrEq("pulling file.zip")))
364        .WillOnce(Return(true));
365
366    const char* args[] = {"bugreport", "file"};
367    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
368}
369
370// Tests 'adb bugreport dir' when it succeeds and destination is a directory and device runs N.
371TEST_F(BugreportTest, OkNDeviceDirectory) {
372    ExpectBugreportzVersion("1.0");
373    TemporaryDir td;
374    std::string dest_file =
375        android::base::StringPrintf("%s%cda_bugreport.zip", td.path, OS_PATH_SEPARATOR);
376
377    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _))
378        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
379                        WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip")),
380                        WithArg<4>(ReturnCallbackDone())));
381    EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
382                                true, StrEq("pulling da_bugreport.zip")))
383        .WillOnce(Return(true));
384
385    const char* args[] = {"bugreport", td.path};
386    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
387}
388
389// Tests 'adb bugreport file.zip' when the bugreport itself failed
390TEST_F(BugreportTest, BugreportzReturnedFail) {
391    ExpectBugreportzVersion("1.1");
392    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
393        .WillOnce(
394            DoAll(WithArg<4>(WriteOnStdout("FAIL:D'OH!\n")), WithArg<4>(ReturnCallbackDone())));
395
396    CaptureStderr();
397    const char* args[] = {"bugreport", "file.zip"};
398    ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
399    ASSERT_THAT(GetCapturedStderr(), HasSubstr("D'OH!"));
400}
401
402// Tests 'adb bugreport file.zip' when the bugreport itself failed but response
403// was sent in
404// multiple buffer writes
405TEST_F(BugreportTest, BugreportzReturnedFailSplitBuffer) {
406    ExpectBugreportzVersion("1.1");
407    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
408        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("FAIL")), WithArg<4>(WriteOnStdout(":D'OH!\n")),
409                        WithArg<4>(ReturnCallbackDone())));
410
411    CaptureStderr();
412    const char* args[] = {"bugreport", "file.zip"};
413    ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
414    ASSERT_THAT(GetCapturedStderr(), HasSubstr("D'OH!"));
415}
416
417// Tests 'adb bugreport file.zip' when the bugreportz returned an unsupported
418// response.
419TEST_F(BugreportTest, BugreportzReturnedUnsupported) {
420    ExpectBugreportzVersion("1.1");
421    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
422        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("bugreportz? What am I, a zombie?")),
423                        WithArg<4>(ReturnCallbackDone())));
424
425    CaptureStderr();
426    const char* args[] = {"bugreport", "file.zip"};
427    ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
428    ASSERT_THAT(GetCapturedStderr(), HasSubstr("bugreportz? What am I, a zombie?"));
429}
430
431// Tests 'adb bugreport file.zip' when the bugreportz -v command failed
432TEST_F(BugreportTest, BugreportzVersionFailed) {
433    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -v", false, _))
434        .WillOnce(Return(666));
435
436    const char* args[] = {"bugreport", "file.zip"};
437    ASSERT_EQ(666, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
438}
439
440// Tests 'adb bugreport file.zip' when the bugreportz -v returns status 0 but with no output.
441TEST_F(BugreportTest, BugreportzVersionEmpty) {
442    ExpectBugreportzVersion("");
443
444    const char* args[] = {"bugreport", "file.zip"};
445    ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
446}
447
448// Tests 'adb bugreport file.zip' when the main bugreportz command failed
449TEST_F(BugreportTest, BugreportzFailed) {
450    ExpectBugreportzVersion("1.1");
451    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
452        .WillOnce(Return(666));
453
454    const char* args[] = {"bugreport", "file.zip"};
455    ASSERT_EQ(666, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
456}
457
458// Tests 'adb bugreport file.zip' when the bugreport could not be pulled
459TEST_F(BugreportTest, PullFails) {
460    ExpectBugreportzVersion("1.1");
461    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
462        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip")),
463                        WithArg<4>(ReturnCallbackDone())));
464    EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
465                                true, HasSubstr("file.zip")))
466        .WillOnce(Return(false));
467
468    const char* args[] = {"bugreport", "file.zip"};
469    ASSERT_EQ(1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
470}
471