1/*
2 * Copyright (C) 2017 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#define LOG_TAG "Lshal"
18#include <android-base/logging.h>
19
20#include <sstream>
21#include <string>
22#include <thread>
23#include <vector>
24
25#include <gtest/gtest.h>
26#include <gmock/gmock.h>
27#include <android/hardware/tests/baz/1.0/IQuux.h>
28#include <hidl/HidlTransportSupport.h>
29#include <vintf/parse_xml.h>
30
31#include "ListCommand.h"
32#include "Lshal.h"
33
34#define NELEMS(array)   static_cast<int>(sizeof(array) / sizeof(array[0]))
35
36using namespace testing;
37
38using ::android::hidl::base::V1_0::DebugInfo;
39using ::android::hidl::base::V1_0::IBase;
40using ::android::hidl::manager::V1_0::IServiceManager;
41using ::android::hidl::manager::V1_0::IServiceNotification;
42using ::android::hardware::hidl_array;
43using ::android::hardware::hidl_death_recipient;
44using ::android::hardware::hidl_handle;
45using ::android::hardware::hidl_string;
46using ::android::hardware::hidl_vec;
47
48using InstanceDebugInfo = IServiceManager::InstanceDebugInfo;
49
50using hidl_hash = hidl_array<uint8_t, 32>;
51
52namespace android {
53namespace hardware {
54namespace tests {
55namespace baz {
56namespace V1_0 {
57namespace implementation {
58struct Quux : android::hardware::tests::baz::V1_0::IQuux {
59    ::android::hardware::Return<void> debug(const hidl_handle& hh, const hidl_vec<hidl_string>& options) override {
60        const native_handle_t *handle = hh.getNativeHandle();
61        if (handle->numFds < 1) {
62            return Void();
63        }
64        int fd = handle->data[0];
65        std::string content{descriptor};
66        for (const auto &option : options) {
67            content += "\n";
68            content += option.c_str();
69        }
70        ssize_t written = write(fd, content.c_str(), content.size());
71        if (written != (ssize_t)content.size()) {
72            LOG(WARNING) << "SERVER(Quux) debug writes " << written << " bytes < "
73                    << content.size() << " bytes, errno = " << errno;
74        }
75        return Void();
76    }
77};
78
79} // namespace implementation
80} // namespace V1_0
81} // namespace baz
82} // namespace tests
83} // namespace hardware
84
85namespace lshal {
86
87class MockServiceManager : public IServiceManager {
88public:
89    template<typename T>
90    using R = ::android::hardware::Return<T>;
91    using String = const hidl_string&;
92    ~MockServiceManager() = default;
93
94#define MOCK_METHOD_CB(name) MOCK_METHOD1(name, R<void>(IServiceManager::name##_cb))
95
96    MOCK_METHOD2(get, R<sp<IBase>>(String, String));
97    MOCK_METHOD2(add, R<bool>(String, const sp<IBase>&));
98    MOCK_METHOD2(getTransport, R<IServiceManager::Transport>(String, String));
99    MOCK_METHOD_CB(list);
100    MOCK_METHOD2(listByInterface, R<void>(String, listByInterface_cb));
101    MOCK_METHOD3(registerForNotifications, R<bool>(String, String, const sp<IServiceNotification>&));
102    MOCK_METHOD_CB(debugDump);
103    MOCK_METHOD2(registerPassthroughClient, R<void>(String, String));
104    MOCK_METHOD_CB(interfaceChain);
105    MOCK_METHOD2(debug, R<void>(const hidl_handle&, const hidl_vec<hidl_string>&));
106    MOCK_METHOD_CB(interfaceDescriptor);
107    MOCK_METHOD_CB(getHashChain);
108    MOCK_METHOD0(setHalInstrumentation, R<void>());
109    MOCK_METHOD2(linkToDeath, R<bool>(const sp<hidl_death_recipient>&, uint64_t));
110    MOCK_METHOD0(ping, R<void>());
111    MOCK_METHOD_CB(getDebugInfo);
112    MOCK_METHOD0(notifySyspropsChanged, R<void>());
113    MOCK_METHOD1(unlinkToDeath, R<bool>(const sp<hidl_death_recipient>&));
114
115};
116
117class DebugTest : public ::testing::Test {
118public:
119    void SetUp() override {
120        using ::android::hardware::tests::baz::V1_0::IQuux;
121        using ::android::hardware::tests::baz::V1_0::implementation::Quux;
122
123        err.str("");
124        out.str("");
125        serviceManager = new testing::NiceMock<MockServiceManager>();
126        ON_CALL(*serviceManager, get(_, _)).WillByDefault(Invoke(
127            [](const auto &iface, const auto &inst) -> ::android::hardware::Return<sp<IBase>> {
128                if (iface == IQuux::descriptor && inst == "default")
129                    return new Quux();
130                return nullptr;
131            }));
132
133        lshal = std::make_unique<Lshal>(out, err, serviceManager, serviceManager);
134    }
135    void TearDown() override {}
136
137    std::stringstream err;
138    std::stringstream out;
139    sp<MockServiceManager> serviceManager;
140
141    std::unique_ptr<Lshal> lshal;
142};
143
144static Arg createArg(const std::vector<const char*>& args) {
145    return Arg{static_cast<int>(args.size()), const_cast<char**>(args.data())};
146}
147
148template<typename T>
149static Status callMain(const std::unique_ptr<T>& lshal, const std::vector<const char*>& args) {
150    return lshal->main(createArg(args));
151}
152
153TEST_F(DebugTest, Debug) {
154    EXPECT_EQ(0u, callMain(lshal, {
155        "lshal", "debug", "android.hardware.tests.baz@1.0::IQuux/default", "foo", "bar"
156    }));
157    EXPECT_THAT(out.str(), StrEq("android.hardware.tests.baz@1.0::IQuux\nfoo\nbar"));
158    EXPECT_THAT(err.str(), IsEmpty());
159}
160
161TEST_F(DebugTest, Debug2) {
162    EXPECT_EQ(0u, callMain(lshal, {
163        "lshal", "debug", "android.hardware.tests.baz@1.0::IQuux", "baz", "quux"
164    }));
165    EXPECT_THAT(out.str(), StrEq("android.hardware.tests.baz@1.0::IQuux\nbaz\nquux"));
166    EXPECT_THAT(err.str(), IsEmpty());
167}
168
169TEST_F(DebugTest, Debug3) {
170    EXPECT_NE(0u, callMain(lshal, {
171        "lshal", "debug", "android.hardware.tests.doesnotexist@1.0::IDoesNotExist",
172    }));
173    EXPECT_THAT(err.str(), HasSubstr("does not exist"));
174}
175
176class MockLshal : public Lshal {
177public:
178    MockLshal() {}
179    ~MockLshal() = default;
180    MOCK_CONST_METHOD0(out, NullableOStream<std::ostream>());
181    MOCK_CONST_METHOD0(err, NullableOStream<std::ostream>());
182};
183
184// expose protected fields and methods for ListCommand
185class MockListCommand : public ListCommand {
186public:
187    MockListCommand(Lshal* lshal) : ListCommand(*lshal) {}
188
189    Status parseArgs(const Arg& arg) { return ListCommand::parseArgs(arg); }
190    Status main(const Arg& arg) { return ListCommand::main(arg); }
191    void forEachTable(const std::function<void(Table &)> &f) {
192        return ListCommand::forEachTable(f);
193    }
194    void forEachTable(const std::function<void(const Table &)> &f) const {
195        return ListCommand::forEachTable(f);
196    }
197    Status fetch() { return ListCommand::fetch(); }
198    void dumpVintf(const NullableOStream<std::ostream>& out) {
199        return ListCommand::dumpVintf(out);
200    }
201    void internalPostprocess() { ListCommand::postprocess(); }
202    const PidInfo* getPidInfoCached(pid_t serverPid) {
203        return ListCommand::getPidInfoCached(serverPid);
204    }
205
206    MOCK_METHOD0(postprocess, void());
207    MOCK_CONST_METHOD2(getPidInfo, bool(pid_t, PidInfo*));
208    MOCK_CONST_METHOD1(parseCmdline, std::string(pid_t));
209    MOCK_METHOD1(getPartition, Partition(pid_t));
210};
211
212class ListParseArgsTest : public ::testing::Test {
213public:
214    void SetUp() override {
215        mockLshal = std::make_unique<NiceMock<MockLshal>>();
216        mockList = std::make_unique<MockListCommand>(mockLshal.get());
217        // ListCommand::parseArgs should parse arguments from the second element
218        optind = 1;
219    }
220    std::unique_ptr<MockLshal> mockLshal;
221    std::unique_ptr<MockListCommand> mockList;
222    std::stringstream output;
223};
224
225TEST_F(ListParseArgsTest, Args) {
226    EXPECT_EQ(0u, mockList->parseArgs(createArg({"lshal", "-p", "-i", "-a", "-c"})));
227    mockList->forEachTable([](const Table& table) {
228        EXPECT_EQ(SelectedColumns({TableColumnType::SERVER_PID, TableColumnType::INTERFACE_NAME,
229                                   TableColumnType::SERVER_ADDR, TableColumnType::CLIENT_PIDS}),
230                  table.getSelectedColumns());
231    });
232}
233
234TEST_F(ListParseArgsTest, Cmds) {
235    EXPECT_EQ(0u, mockList->parseArgs(createArg({"lshal", "-m"})));
236    mockList->forEachTable([](const Table& table) {
237        EXPECT_THAT(table.getSelectedColumns(), Not(Contains(TableColumnType::SERVER_PID)))
238                << "should not print server PID with -m";
239        EXPECT_THAT(table.getSelectedColumns(), Not(Contains(TableColumnType::CLIENT_PIDS)))
240                << "should not print client PIDs with -m";
241        EXPECT_THAT(table.getSelectedColumns(), Contains(TableColumnType::SERVER_CMD))
242                << "should print server cmd with -m";
243        EXPECT_THAT(table.getSelectedColumns(), Contains(TableColumnType::CLIENT_CMDS))
244                << "should print client cmds with -m";
245    });
246}
247
248TEST_F(ListParseArgsTest, DebugAndNeat) {
249    ON_CALL(*mockLshal, err()).WillByDefault(Return(NullableOStream<std::ostream>(output)));
250    EXPECT_NE(0u, mockList->parseArgs(createArg({"lshal", "--neat", "-d"})));
251    EXPECT_THAT(output.str(), StrNe(""));
252}
253
254/// Fetch Test
255
256// A set of deterministic functions to generate fake debug infos.
257static uint64_t getPtr(pid_t serverId) { return 10000 + serverId; }
258static std::vector<pid_t> getClients(pid_t serverId) {
259    return {serverId + 1, serverId + 3};
260}
261static PidInfo getPidInfoFromId(pid_t serverId) {
262    PidInfo info;
263    info.refPids[getPtr(serverId)] = getClients(serverId);
264    info.threadUsage = 10 + serverId;
265    info.threadCount = 20 + serverId;
266    return info;
267}
268static std::string getInterfaceName(pid_t serverId) {
269    return "a.h.foo" + std::to_string(serverId) + "@" + std::to_string(serverId) + ".0::IFoo";
270}
271static std::string getInstanceName(pid_t serverId) {
272    return std::to_string(serverId);
273}
274static pid_t getIdFromInstanceName(const hidl_string& instance) {
275    return atoi(instance.c_str());
276}
277static std::string getFqInstanceName(pid_t serverId) {
278    return getInterfaceName(serverId) + "/" + getInstanceName(serverId);
279}
280static std::string getCmdlineFromId(pid_t serverId) {
281    if (serverId == NO_PID) return "";
282    return "command_line_" + std::to_string(serverId);
283}
284static bool getIsReleasedFromId(pid_t p) { return p % 2 == 0; }
285static hidl_hash getHashFromId(pid_t serverId) {
286    hidl_hash hash;
287    bool isReleased = getIsReleasedFromId(serverId);
288    for (size_t i = 0; i < hash.size(); ++i) {
289        hash[i] = isReleased ? static_cast<uint8_t>(serverId) : 0u;
290    }
291    return hash;
292}
293
294// Fake service returned by mocked IServiceManager::get.
295class TestService : public IBase {
296public:
297    TestService(pid_t id) : mId(id) {}
298    hardware::Return<void> getDebugInfo(getDebugInfo_cb cb) override {
299        cb({ mId /* pid */, getPtr(mId), DebugInfo::Architecture::IS_64BIT });
300        return hardware::Void();
301    }
302    hardware::Return<void> interfaceChain(interfaceChain_cb cb) override {
303        cb({getInterfaceName(mId), IBase::descriptor});
304        return hardware::Void();
305    }
306    hardware::Return<void> getHashChain(getHashChain_cb cb) override {
307        cb({getHashFromId(mId), getHashFromId(0xff)});
308        return hardware::Void();
309    }
310private:
311    pid_t mId;
312};
313
314class ListTest : public ::testing::Test {
315public:
316    void SetUp() override {
317        initMockServiceManager();
318        lshal = std::make_unique<Lshal>(out, err, serviceManager, passthruManager);
319        initMockList();
320    }
321
322    void initMockList() {
323        mockList = std::make_unique<NiceMock<MockListCommand>>(lshal.get());
324        ON_CALL(*mockList, getPidInfo(_,_)).WillByDefault(Invoke(
325            [](pid_t serverPid, PidInfo* info) {
326                *info = getPidInfoFromId(serverPid);
327                return true;
328            }));
329        ON_CALL(*mockList, parseCmdline(_)).WillByDefault(Invoke(&getCmdlineFromId));
330        ON_CALL(*mockList, postprocess()).WillByDefault(Invoke([&]() {
331            mockList->internalPostprocess();
332            size_t i = 0;
333            mockList->forEachTable([&](Table& table) {
334                table.setDescription("[fake description " + std::to_string(i++) + "]");
335            });
336        }));
337        ON_CALL(*mockList, getPartition(_)).WillByDefault(Return(Partition::VENDOR));
338    }
339
340    void initMockServiceManager() {
341        serviceManager = new testing::NiceMock<MockServiceManager>();
342        passthruManager = new testing::NiceMock<MockServiceManager>();
343        using A = DebugInfo::Architecture;
344        ON_CALL(*serviceManager, list(_)).WillByDefault(Invoke(
345            [] (IServiceManager::list_cb cb) {
346                cb({ getFqInstanceName(1), getFqInstanceName(2) });
347                return hardware::Void();
348            }));
349
350        ON_CALL(*serviceManager, get(_, _)).WillByDefault(Invoke(
351            [&](const hidl_string&, const hidl_string& instance) {
352                int id = getIdFromInstanceName(instance);
353                return sp<IBase>(new TestService(id));
354            }));
355
356        ON_CALL(*serviceManager, debugDump(_)).WillByDefault(Invoke(
357            [] (IServiceManager::debugDump_cb cb) {
358                cb({InstanceDebugInfo{getInterfaceName(3), getInstanceName(3), 3,
359                                      getClients(3), A::IS_32BIT},
360                    InstanceDebugInfo{getInterfaceName(4), getInstanceName(4), 4,
361                                      getClients(4), A::IS_32BIT}});
362                return hardware::Void();
363            }));
364
365        ON_CALL(*passthruManager, debugDump(_)).WillByDefault(Invoke(
366            [] (IServiceManager::debugDump_cb cb) {
367                cb({InstanceDebugInfo{getInterfaceName(5), getInstanceName(5), 5,
368                                      getClients(5), A::IS_32BIT},
369                    InstanceDebugInfo{getInterfaceName(6), getInstanceName(6), 6,
370                                      getClients(6), A::IS_32BIT}});
371                return hardware::Void();
372            }));
373    }
374
375    std::stringstream err;
376    std::stringstream out;
377    std::unique_ptr<Lshal> lshal;
378    std::unique_ptr<MockListCommand> mockList;
379    sp<MockServiceManager> serviceManager;
380    sp<MockServiceManager> passthruManager;
381};
382
383TEST_F(ListTest, GetPidInfoCached) {
384    EXPECT_CALL(*mockList, getPidInfo(5, _)).Times(1);
385
386    EXPECT_NE(nullptr, mockList->getPidInfoCached(5));
387    EXPECT_NE(nullptr, mockList->getPidInfoCached(5));
388}
389
390TEST_F(ListTest, Fetch) {
391    EXPECT_EQ(0u, mockList->fetch());
392    std::array<std::string, 6> transports{{"hwbinder", "hwbinder", "passthrough",
393                                          "passthrough", "passthrough", "passthrough"}};
394    std::array<Architecture, 6> archs{{ARCH64, ARCH64, ARCH32, ARCH32, ARCH32, ARCH32}};
395    int id = 1;
396    mockList->forEachTable([&](const Table& table) {
397        ASSERT_EQ(2u, table.size());
398        for (const auto& entry : table) {
399            const auto& transport = transports[id - 1];
400            TableEntry expected{
401                .interfaceName = getFqInstanceName(id),
402                .transport = transport,
403                .serverPid = transport == "hwbinder" ? id : NO_PID,
404                .threadUsage = transport == "hwbinder" ? getPidInfoFromId(id).threadUsage : 0,
405                .threadCount = transport == "hwbinder" ? getPidInfoFromId(id).threadCount : 0,
406                .serverCmdline = {},
407                .serverObjectAddress = transport == "hwbinder" ? getPtr(id) : NO_PTR,
408                .clientPids = getClients(id),
409                .clientCmdlines = {},
410                .arch = archs[id - 1],
411            };
412            EXPECT_EQ(expected, entry) << expected.to_string() << " vs. " << entry.to_string();
413
414            ++id;
415        }
416    });
417
418}
419
420TEST_F(ListTest, DumpVintf) {
421    const std::string expected =
422        "<!-- \n"
423        "    This is a skeleton device manifest. Notes: \n" + ListCommand::INIT_VINTF_NOTES +
424        "-->\n"
425        "<manifest version=\"1.0\" type=\"device\">\n"
426        "    <hal format=\"hidl\">\n"
427        "        <name>a.h.foo1</name>\n"
428        "        <transport>hwbinder</transport>\n"
429        "        <version>1.0</version>\n"
430        "        <interface>\n"
431        "            <name>IFoo</name>\n"
432        "            <instance>1</instance>\n"
433        "        </interface>\n"
434        "    </hal>\n"
435        "    <hal format=\"hidl\">\n"
436        "        <name>a.h.foo2</name>\n"
437        "        <transport>hwbinder</transport>\n"
438        "        <version>2.0</version>\n"
439        "        <interface>\n"
440        "            <name>IFoo</name>\n"
441        "            <instance>2</instance>\n"
442        "        </interface>\n"
443        "    </hal>\n"
444        "    <hal format=\"hidl\">\n"
445        "        <name>a.h.foo3</name>\n"
446        "        <transport arch=\"32\">passthrough</transport>\n"
447        "        <version>3.0</version>\n"
448        "        <interface>\n"
449        "            <name>IFoo</name>\n"
450        "            <instance>3</instance>\n"
451        "        </interface>\n"
452        "    </hal>\n"
453        "    <hal format=\"hidl\">\n"
454        "        <name>a.h.foo4</name>\n"
455        "        <transport arch=\"32\">passthrough</transport>\n"
456        "        <version>4.0</version>\n"
457        "        <interface>\n"
458        "            <name>IFoo</name>\n"
459        "            <instance>4</instance>\n"
460        "        </interface>\n"
461        "    </hal>\n"
462        "    <hal format=\"hidl\">\n"
463        "        <name>a.h.foo5</name>\n"
464        "        <transport arch=\"32\">passthrough</transport>\n"
465        "        <version>5.0</version>\n"
466        "    </hal>\n"
467        "    <hal format=\"hidl\">\n"
468        "        <name>a.h.foo6</name>\n"
469        "        <transport arch=\"32\">passthrough</transport>\n"
470        "        <version>6.0</version>\n"
471        "    </hal>\n"
472        "</manifest>\n";
473
474    optind = 1; // mimic Lshal::parseArg()
475    EXPECT_EQ(0u, mockList->main(createArg({"lshal", "--init-vintf"})));
476    EXPECT_EQ(expected, out.str());
477    EXPECT_EQ("", err.str());
478
479    vintf::HalManifest m;
480    EXPECT_EQ(true, vintf::gHalManifestConverter(&m, out.str()))
481        << "--init-vintf does not emit valid HAL manifest: "
482        << vintf::gHalManifestConverter.lastError();
483}
484
485// test default columns
486TEST_F(ListTest, DumpDefault) {
487    const std::string expected =
488        "[fake description 0]\n"
489        "R Interface            Thread Use Server Clients\n"
490        "  a.h.foo1@1.0::IFoo/1 11/21      1      2 4\n"
491        "Y a.h.foo2@2.0::IFoo/2 12/22      2      3 5\n"
492        "\n"
493        "[fake description 1]\n"
494        "R Interface            Thread Use Server Clients\n"
495        "  a.h.foo3@3.0::IFoo/3 N/A        N/A    4 6\n"
496        "  a.h.foo4@4.0::IFoo/4 N/A        N/A    5 7\n"
497        "\n"
498        "[fake description 2]\n"
499        "R Interface            Thread Use Server Clients\n"
500        "  a.h.foo5@5.0::IFoo/5 N/A        N/A    6 8\n"
501        "  a.h.foo6@6.0::IFoo/6 N/A        N/A    7 9\n"
502        "\n";
503
504    optind = 1; // mimic Lshal::parseArg()
505    EXPECT_EQ(0u, mockList->main(createArg({"lshal"})));
506    EXPECT_EQ(expected, out.str());
507    EXPECT_EQ("", err.str());
508}
509
510TEST_F(ListTest, DumpHash) {
511    const std::string expected =
512        "[fake description 0]\n"
513        "Interface            R Hash\n"
514        "a.h.foo1@1.0::IFoo/1   0000000000000000000000000000000000000000000000000000000000000000\n"
515        "a.h.foo2@2.0::IFoo/2 Y 0202020202020202020202020202020202020202020202020202020202020202\n"
516        "\n"
517        "[fake description 1]\n"
518        "Interface            R Hash\n"
519        "a.h.foo3@3.0::IFoo/3   \n"
520        "a.h.foo4@4.0::IFoo/4   \n"
521        "\n"
522        "[fake description 2]\n"
523        "Interface            R Hash\n"
524        "a.h.foo5@5.0::IFoo/5   \n"
525        "a.h.foo6@6.0::IFoo/6   \n"
526        "\n";
527
528    optind = 1; // mimic Lshal::parseArg()
529    EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-ils"})));
530    EXPECT_EQ(expected, out.str());
531    EXPECT_EQ("", err.str());
532}
533
534TEST_F(ListTest, Dump) {
535    const std::string expected =
536        "[fake description 0]\n"
537        "Interface            Transport Arch Thread Use Server PTR              Clients\n"
538        "a.h.foo1@1.0::IFoo/1 hwbinder  64   11/21      1      0000000000002711 2 4\n"
539        "a.h.foo2@2.0::IFoo/2 hwbinder  64   12/22      2      0000000000002712 3 5\n"
540        "\n"
541        "[fake description 1]\n"
542        "Interface            Transport   Arch Thread Use Server PTR Clients\n"
543        "a.h.foo3@3.0::IFoo/3 passthrough 32   N/A        N/A    N/A 4 6\n"
544        "a.h.foo4@4.0::IFoo/4 passthrough 32   N/A        N/A    N/A 5 7\n"
545        "\n"
546        "[fake description 2]\n"
547        "Interface            Transport   Arch Thread Use Server PTR Clients\n"
548        "a.h.foo5@5.0::IFoo/5 passthrough 32   N/A        N/A    N/A 6 8\n"
549        "a.h.foo6@6.0::IFoo/6 passthrough 32   N/A        N/A    N/A 7 9\n"
550        "\n";
551
552    optind = 1; // mimic Lshal::parseArg()
553    EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-itrepac"})));
554    EXPECT_EQ(expected, out.str());
555    EXPECT_EQ("", err.str());
556}
557
558TEST_F(ListTest, DumpCmdline) {
559    const std::string expected =
560        "[fake description 0]\n"
561        "Interface            Transport Arch Thread Use Server CMD     PTR              Clients CMD\n"
562        "a.h.foo1@1.0::IFoo/1 hwbinder  64   11/21      command_line_1 0000000000002711 command_line_2;command_line_4\n"
563        "a.h.foo2@2.0::IFoo/2 hwbinder  64   12/22      command_line_2 0000000000002712 command_line_3;command_line_5\n"
564        "\n"
565        "[fake description 1]\n"
566        "Interface            Transport   Arch Thread Use Server CMD PTR Clients CMD\n"
567        "a.h.foo3@3.0::IFoo/3 passthrough 32   N/A                   N/A command_line_4;command_line_6\n"
568        "a.h.foo4@4.0::IFoo/4 passthrough 32   N/A                   N/A command_line_5;command_line_7\n"
569        "\n"
570        "[fake description 2]\n"
571        "Interface            Transport   Arch Thread Use Server CMD PTR Clients CMD\n"
572        "a.h.foo5@5.0::IFoo/5 passthrough 32   N/A                   N/A command_line_6;command_line_8\n"
573        "a.h.foo6@6.0::IFoo/6 passthrough 32   N/A                   N/A command_line_7;command_line_9\n"
574        "\n";
575
576    optind = 1; // mimic Lshal::parseArg()
577    EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-itrepacm"})));
578    EXPECT_EQ(expected, out.str());
579    EXPECT_EQ("", err.str());
580}
581
582TEST_F(ListTest, DumpNeat) {
583    const std::string expected =
584        "a.h.foo1@1.0::IFoo/1 11/21 1   2 4\n"
585        "a.h.foo2@2.0::IFoo/2 12/22 2   3 5\n"
586        "a.h.foo3@3.0::IFoo/3 N/A   N/A 4 6\n"
587        "a.h.foo4@4.0::IFoo/4 N/A   N/A 5 7\n"
588        "a.h.foo5@5.0::IFoo/5 N/A   N/A 6 8\n"
589        "a.h.foo6@6.0::IFoo/6 N/A   N/A 7 9\n";
590
591    optind = 1; // mimic Lshal::parseArg()
592    EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-iepc", "--neat"})));
593    EXPECT_EQ(expected, out.str());
594    EXPECT_EQ("", err.str());
595}
596
597class HelpTest : public ::testing::Test {
598public:
599    void SetUp() override {
600        lshal = std::make_unique<Lshal>(out, err, new MockServiceManager() /* serviceManager */,
601                                        new MockServiceManager() /* passthruManager */);
602    }
603
604    std::stringstream err;
605    std::stringstream out;
606    std::unique_ptr<Lshal> lshal;
607};
608
609TEST_F(HelpTest, GlobalUsage) {
610    (void)callMain(lshal, {"lshal", "--help"}); // ignore return
611    std::string errStr = err.str();
612    EXPECT_THAT(errStr, ContainsRegex("(^|\n)commands:($|\n)"))
613        << "`lshal --help` does not contain global usage";
614    EXPECT_THAT(errStr, ContainsRegex("(^|\n)list:($|\n)"))
615        << "`lshal --help` does not contain usage for 'list' command";
616    EXPECT_THAT(errStr, ContainsRegex("(^|\n)debug:($|\n)"))
617        << "`lshal --help` does not contain usage for 'debug' command";
618    EXPECT_THAT(errStr, ContainsRegex("(^|\n)help:($|\n)"))
619        << "`lshal --help` does not contain usage for 'help' command";
620
621    err.str("");
622    (void)callMain(lshal, {"lshal", "help"}); // ignore return
623    EXPECT_EQ(errStr, err.str()) << "`lshal help` should have the same output as `lshal --help`";
624
625    err.str("");
626    EXPECT_NE(0u, callMain(lshal, {"lshal", "--unknown-option"}));
627    EXPECT_THAT(err.str(), ContainsRegex("unrecognized option"));
628    EXPECT_THAT(err.str(), EndsWith(errStr))
629            << "`lshal --unknown-option` should have the same output as `lshal --help`";
630    EXPECT_EQ("", out.str());
631}
632
633TEST_F(HelpTest, UnknownOptionList1) {
634    (void)callMain(lshal, {"lshal", "help", "list"});
635    EXPECT_THAT(err.str(), ContainsRegex("(^|\n)list:($|\n)"))
636        << "`lshal help list` does not contain usage for 'list' command";
637}
638
639TEST_F(HelpTest, UnknownOptionList2) {
640    EXPECT_NE(0u, callMain(lshal, {"lshal", "list", "--unknown-option"}));
641    EXPECT_THAT(err.str(), ContainsRegex("unrecognized option"));
642    EXPECT_THAT(err.str(), ContainsRegex("(^|\n)list:($|\n)"))
643        << "`lshal list --unknown-option` does not contain usage for 'list' command";
644    EXPECT_EQ("", out.str());
645}
646
647TEST_F(HelpTest, UnknownOptionHelp1) {
648    (void)callMain(lshal, {"lshal", "help", "help"});
649    EXPECT_THAT(err.str(), ContainsRegex("(^|\n)help:($|\n)"))
650        << "`lshal help help` does not contain usage for 'help' command";
651}
652
653TEST_F(HelpTest, UnknownOptionHelp2) {
654    (void)callMain(lshal, {"lshal", "help", "--unknown-option"});
655    EXPECT_THAT(err.str(), ContainsRegex("(^|\n)help:($|\n)"))
656        << "`lshal help --unknown-option` does not contain usage for 'help' command";
657    EXPECT_EQ("", out.str());
658}
659
660} // namespace lshal
661} // namespace android
662
663int main(int argc, char **argv) {
664    ::testing::InitGoogleMock(&argc, argv);
665    return RUN_ALL_TESTS();
666}
667