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 "../dumpsys.h"
18
19#include <vector>
20
21#include <gmock/gmock.h>
22#include <gtest/gtest.h>
23
24#include <android-base/file.h>
25#include <utils/String16.h>
26#include <utils/String8.h>
27#include <utils/Vector.h>
28
29using namespace android;
30
31using ::testing::_;
32using ::testing::Action;
33using ::testing::ActionInterface;
34using ::testing::DoAll;
35using ::testing::Eq;
36using ::testing::HasSubstr;
37using ::testing::MakeAction;
38using ::testing::Not;
39using ::testing::Return;
40using ::testing::StrEq;
41using ::testing::Test;
42using ::testing::WithArg;
43using ::testing::internal::CaptureStderr;
44using ::testing::internal::CaptureStdout;
45using ::testing::internal::GetCapturedStderr;
46using ::testing::internal::GetCapturedStdout;
47
48class ServiceManagerMock : public IServiceManager {
49  public:
50    MOCK_CONST_METHOD1(getService, sp<IBinder>(const String16&));
51    MOCK_CONST_METHOD1(checkService, sp<IBinder>(const String16&));
52    MOCK_METHOD3(addService, status_t(const String16&, const sp<IBinder>&, bool));
53    MOCK_METHOD0(listServices, Vector<String16>());
54
55  protected:
56    MOCK_METHOD0(onAsBinder, IBinder*());
57};
58
59class BinderMock : public BBinder {
60  public:
61    BinderMock() {
62    }
63
64    MOCK_METHOD2(dump, status_t(int, const Vector<String16>&));
65};
66
67// gmock black magic to provide a WithArg<0>(WriteOnFd(output)) matcher
68typedef void WriteOnFdFunction(int);
69
70class WriteOnFdAction : public ActionInterface<WriteOnFdFunction> {
71  public:
72    explicit WriteOnFdAction(const std::string& output) : output_(output) {
73    }
74    virtual Result Perform(const ArgumentTuple& args) {
75        int fd = ::std::tr1::get<0>(args);
76        android::base::WriteStringToFd(output_, fd);
77    }
78
79  private:
80    std::string output_;
81};
82
83// Matcher used to emulate dump() by writing on its file descriptor.
84Action<WriteOnFdFunction> WriteOnFd(const std::string& output) {
85    return MakeAction(new WriteOnFdAction(output));
86}
87
88// Matcher for args using Android's Vector<String16> format
89// TODO: move it to some common testing library
90MATCHER_P(AndroidElementsAre, expected, "") {
91    std::ostringstream errors;
92    if (arg.size() != expected.size()) {
93        errors << " sizes do not match (expected " << expected.size() << ", got " << arg.size()
94               << ")\n";
95    }
96    int i = 0;
97    std::ostringstream actual_stream, expected_stream;
98    for (String16 actual : arg) {
99        std::string actual_str = String8(actual).c_str();
100        std::string expected_str = expected[i];
101        actual_stream << "'" << actual_str << "' ";
102        expected_stream << "'" << expected_str << "' ";
103        if (actual_str != expected_str) {
104            errors << " element mismatch at index " << i << "\n";
105        }
106        i++;
107    }
108
109    if (!errors.str().empty()) {
110        errors << "\nExpected args: " << expected_stream.str()
111               << "\nActual args: " << actual_stream.str();
112        *result_listener << errors.str();
113        return false;
114    }
115    return true;
116}
117
118// Custom action to sleep for timeout seconds
119ACTION_P(Sleep, timeout) {
120    sleep(timeout);
121}
122
123class DumpsysTest : public Test {
124  public:
125    DumpsysTest() : sm_(), dump_(&sm_), stdout_(), stderr_() {
126    }
127
128    void ExpectListServices(std::vector<std::string> services) {
129        Vector<String16> services16;
130        for (auto& service : services) {
131            services16.add(String16(service.c_str()));
132        }
133        EXPECT_CALL(sm_, listServices()).WillRepeatedly(Return(services16));
134    }
135
136    sp<BinderMock> ExpectCheckService(const char* name, bool running = true) {
137        sp<BinderMock> binder_mock;
138        if (running) {
139            binder_mock = new BinderMock;
140        }
141        EXPECT_CALL(sm_, checkService(String16(name))).WillRepeatedly(Return(binder_mock));
142        return binder_mock;
143    }
144
145    void ExpectDump(const char* name, const std::string& output) {
146        sp<BinderMock> binder_mock = ExpectCheckService(name);
147        EXPECT_CALL(*binder_mock, dump(_, _))
148            .WillRepeatedly(DoAll(WithArg<0>(WriteOnFd(output)), Return(0)));
149    }
150
151    void ExpectDumpWithArgs(const char* name, std::vector<std::string> args,
152                            const std::string& output) {
153        sp<BinderMock> binder_mock = ExpectCheckService(name);
154        EXPECT_CALL(*binder_mock, dump(_, AndroidElementsAre(args)))
155            .WillRepeatedly(DoAll(WithArg<0>(WriteOnFd(output)), Return(0)));
156    }
157
158    void ExpectDumpAndHang(const char* name, int timeout_s, const std::string& output) {
159        sp<BinderMock> binder_mock = ExpectCheckService(name);
160        EXPECT_CALL(*binder_mock, dump(_, _))
161            .WillRepeatedly(DoAll(Sleep(timeout_s), WithArg<0>(WriteOnFd(output)), Return(0)));
162    }
163
164    void CallMain(const std::vector<std::string>& args) {
165        const char* argv[1024] = {"/some/virtual/dir/dumpsys"};
166        int argc = (int)args.size() + 1;
167        int i = 1;
168        for (const std::string& arg : args) {
169            argv[i++] = arg.c_str();
170        }
171        CaptureStdout();
172        CaptureStderr();
173        int status = dump_.main(argc, const_cast<char**>(argv));
174        stdout_ = GetCapturedStdout();
175        stderr_ = GetCapturedStderr();
176        EXPECT_THAT(status, Eq(0));
177    }
178
179    void AssertRunningServices(const std::vector<std::string>& services) {
180        std::string expected("Currently running services:\n");
181        for (const std::string& service : services) {
182            expected.append("  ").append(service).append("\n");
183        }
184        EXPECT_THAT(stdout_, HasSubstr(expected));
185    }
186
187    void AssertOutput(const std::string& expected) {
188        EXPECT_THAT(stdout_, StrEq(expected));
189    }
190
191    void AssertOutputContains(const std::string& expected) {
192        EXPECT_THAT(stdout_, HasSubstr(expected));
193    }
194
195    void AssertDumped(const std::string& service, const std::string& dump) {
196        EXPECT_THAT(stdout_, HasSubstr("DUMP OF SERVICE " + service + ":\n" + dump));
197    }
198
199    void AssertNotDumped(const std::string& dump) {
200        EXPECT_THAT(stdout_, Not(HasSubstr(dump)));
201    }
202
203    void AssertStopped(const std::string& service) {
204        EXPECT_THAT(stderr_, HasSubstr("Can't find service: " + service + "\n"));
205    }
206
207    ServiceManagerMock sm_;
208    Dumpsys dump_;
209
210  private:
211    std::string stdout_, stderr_;
212};
213
214// Tests 'dumpsys -l' when all services are running
215TEST_F(DumpsysTest, ListAllServices) {
216    ExpectListServices({"Locksmith", "Valet"});
217    ExpectCheckService("Locksmith");
218    ExpectCheckService("Valet");
219
220    CallMain({"-l"});
221
222    AssertRunningServices({"Locksmith", "Valet"});
223}
224
225// Tests 'dumpsys -l' when a service is not running
226TEST_F(DumpsysTest, ListRunningServices) {
227    ExpectListServices({"Locksmith", "Valet"});
228    ExpectCheckService("Locksmith");
229    ExpectCheckService("Valet", false);
230
231    CallMain({"-l"});
232
233    AssertRunningServices({"Locksmith"});
234    AssertNotDumped({"Valet"});
235}
236
237// Tests 'dumpsys service_name' on a service is running
238TEST_F(DumpsysTest, DumpRunningService) {
239    ExpectDump("Valet", "Here's your car");
240
241    CallMain({"Valet"});
242
243    AssertOutput("Here's your car");
244}
245
246// Tests 'dumpsys -t 1 service_name' on a service that times out after 2s
247TEST_F(DumpsysTest, DumpRunningServiceTimeout) {
248    ExpectDumpAndHang("Valet", 2, "Here's your car");
249
250    CallMain({"-t", "1", "Valet"});
251
252    AssertOutputContains("SERVICE 'Valet' DUMP TIMEOUT (1s) EXPIRED");
253    AssertNotDumped("Here's your car");
254
255    // Must wait so binder mock is deleted, otherwise test will fail with a leaked object
256    sleep(1);
257}
258
259// Tests 'dumpsys service_name Y U NO HAVE ARGS' on a service that is running
260TEST_F(DumpsysTest, DumpWithArgsRunningService) {
261    ExpectDumpWithArgs("SERVICE", {"Y", "U", "NO", "HANDLE", "ARGS"}, "I DO!");
262
263    CallMain({"SERVICE", "Y", "U", "NO", "HANDLE", "ARGS"});
264
265    AssertOutput("I DO!");
266}
267
268// Tests 'dumpsys' with no arguments
269TEST_F(DumpsysTest, DumpMultipleServices) {
270    ExpectListServices({"running1", "stopped2", "running3"});
271    ExpectDump("running1", "dump1");
272    ExpectCheckService("stopped2", false);
273    ExpectDump("running3", "dump3");
274
275    CallMain({});
276
277    AssertRunningServices({"running1", "running3"});
278    AssertDumped("running1", "dump1");
279    AssertStopped("stopped2");
280    AssertDumped("running3", "dump3");
281}
282
283// Tests 'dumpsys --skip skipped3 skipped5', which should skip these services
284TEST_F(DumpsysTest, DumpWithSkip) {
285    ExpectListServices({"running1", "stopped2", "skipped3", "running4", "skipped5"});
286    ExpectDump("running1", "dump1");
287    ExpectCheckService("stopped2", false);
288    ExpectDump("skipped3", "dump3");
289    ExpectDump("running4", "dump4");
290    ExpectDump("skipped5", "dump5");
291
292    CallMain({"--skip", "skipped3", "skipped5"});
293
294    AssertRunningServices({"running1", "running4", "skipped3 (skipped)", "skipped5 (skipped)"});
295    AssertDumped("running1", "dump1");
296    AssertDumped("running4", "dump4");
297    AssertStopped("stopped2");
298    AssertNotDumped("dump3");
299    AssertNotDumped("dump5");
300}
301