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 
36 using namespace testing;
37 
38 using ::android::hidl::base::V1_0::DebugInfo;
39 using ::android::hidl::base::V1_0::IBase;
40 using ::android::hidl::manager::V1_0::IServiceManager;
41 using ::android::hidl::manager::V1_0::IServiceNotification;
42 using ::android::hardware::hidl_array;
43 using ::android::hardware::hidl_death_recipient;
44 using ::android::hardware::hidl_handle;
45 using ::android::hardware::hidl_string;
46 using ::android::hardware::hidl_vec;
47 
48 using InstanceDebugInfo = IServiceManager::InstanceDebugInfo;
49 
50 using hidl_hash = hidl_array<uint8_t, 32>;
51 
52 namespace android {
53 namespace hardware {
54 namespace tests {
55 namespace baz {
56 namespace V1_0 {
57 namespace implementation {
58 struct Quux : android::hardware::tests::baz::V1_0::IQuux {
debugandroid::hardware::tests::baz::V1_0::implementation::Quux59     ::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 
85 namespace lshal {
86 
87 class MockServiceManager : public IServiceManager {
88 public:
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 
117 class DebugTest : public ::testing::Test {
118 public:
SetUp()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     }
TearDown()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 
createArg(const std::vector<const char * > & args)144 static Arg createArg(const std::vector<const char*>& args) {
145     return Arg{static_cast<int>(args.size()), const_cast<char**>(args.data())};
146 }
147 
148 template<typename T>
callMain(const std::unique_ptr<T> & lshal,const std::vector<const char * > & args)149 static Status callMain(const std::unique_ptr<T>& lshal, const std::vector<const char*>& args) {
150     return lshal->main(createArg(args));
151 }
152 
TEST_F(DebugTest,Debug)153 TEST_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 
TEST_F(DebugTest,Debug2)161 TEST_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 
TEST_F(DebugTest,Debug3)169 TEST_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 
176 class MockLshal : public Lshal {
177 public:
MockLshal()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
185 class MockListCommand : public ListCommand {
186 public:
MockListCommand(Lshal * lshal)187     MockListCommand(Lshal* lshal) : ListCommand(*lshal) {}
188 
parseArgs(const Arg & arg)189     Status parseArgs(const Arg& arg) { return ListCommand::parseArgs(arg); }
main(const Arg & arg)190     Status main(const Arg& arg) { return ListCommand::main(arg); }
forEachTable(const std::function<void (Table &)> & f)191     void forEachTable(const std::function<void(Table &)> &f) {
192         return ListCommand::forEachTable(f);
193     }
forEachTable(const std::function<void (const Table &)> & f) const194     void forEachTable(const std::function<void(const Table &)> &f) const {
195         return ListCommand::forEachTable(f);
196     }
fetch()197     Status fetch() { return ListCommand::fetch(); }
dumpVintf(const NullableOStream<std::ostream> & out)198     void dumpVintf(const NullableOStream<std::ostream>& out) {
199         return ListCommand::dumpVintf(out);
200     }
internalPostprocess()201     void internalPostprocess() { ListCommand::postprocess(); }
getPidInfoCached(pid_t serverPid)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 
212 class ListParseArgsTest : public ::testing::Test {
213 public:
SetUp()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 
TEST_F(ListParseArgsTest,Args)225 TEST_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 
TEST_F(ListParseArgsTest,Cmds)234 TEST_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 
TEST_F(ListParseArgsTest,DebugAndNeat)248 TEST_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.
getPtr(pid_t serverId)257 static uint64_t getPtr(pid_t serverId) { return 10000 + serverId; }
getClients(pid_t serverId)258 static std::vector<pid_t> getClients(pid_t serverId) {
259     return {serverId + 1, serverId + 3};
260 }
getPidInfoFromId(pid_t serverId)261 static 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 }
getInterfaceName(pid_t serverId)268 static std::string getInterfaceName(pid_t serverId) {
269     return "a.h.foo" + std::to_string(serverId) + "@" + std::to_string(serverId) + ".0::IFoo";
270 }
getInstanceName(pid_t serverId)271 static std::string getInstanceName(pid_t serverId) {
272     return std::to_string(serverId);
273 }
getIdFromInstanceName(const hidl_string & instance)274 static pid_t getIdFromInstanceName(const hidl_string& instance) {
275     return atoi(instance.c_str());
276 }
getFqInstanceName(pid_t serverId)277 static std::string getFqInstanceName(pid_t serverId) {
278     return getInterfaceName(serverId) + "/" + getInstanceName(serverId);
279 }
getCmdlineFromId(pid_t serverId)280 static std::string getCmdlineFromId(pid_t serverId) {
281     if (serverId == NO_PID) return "";
282     return "command_line_" + std::to_string(serverId);
283 }
getIsReleasedFromId(pid_t p)284 static bool getIsReleasedFromId(pid_t p) { return p % 2 == 0; }
getHashFromId(pid_t serverId)285 static 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.
295 class TestService : public IBase {
296 public:
TestService(pid_t id)297     TestService(pid_t id) : mId(id) {}
getDebugInfo(getDebugInfo_cb cb)298     hardware::Return<void> getDebugInfo(getDebugInfo_cb cb) override {
299         cb({ mId /* pid */, getPtr(mId), DebugInfo::Architecture::IS_64BIT });
300         return hardware::Void();
301     }
interfaceChain(interfaceChain_cb cb)302     hardware::Return<void> interfaceChain(interfaceChain_cb cb) override {
303         cb({getInterfaceName(mId), IBase::descriptor});
304         return hardware::Void();
305     }
getHashChain(getHashChain_cb cb)306     hardware::Return<void> getHashChain(getHashChain_cb cb) override {
307         cb({getHashFromId(mId), getHashFromId(0xff)});
308         return hardware::Void();
309     }
310 private:
311     pid_t mId;
312 };
313 
314 class ListTest : public ::testing::Test {
315 public:
SetUp()316     void SetUp() override {
317         initMockServiceManager();
318         lshal = std::make_unique<Lshal>(out, err, serviceManager, passthruManager);
319         initMockList();
320     }
321 
initMockList()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 
initMockServiceManager()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 
TEST_F(ListTest,GetPidInfoCached)383 TEST_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 
TEST_F(ListTest,Fetch)390 TEST_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 
TEST_F(ListTest,DumpVintf)420 TEST_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
TEST_F(ListTest,DumpDefault)486 TEST_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 
TEST_F(ListTest,DumpHash)510 TEST_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 
TEST_F(ListTest,Dump)534 TEST_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 
TEST_F(ListTest,DumpCmdline)558 TEST_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 
TEST_F(ListTest,DumpNeat)582 TEST_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 
597 class HelpTest : public ::testing::Test {
598 public:
SetUp()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 
TEST_F(HelpTest,GlobalUsage)609 TEST_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 
TEST_F(HelpTest,UnknownOptionList1)633 TEST_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 
TEST_F(HelpTest,UnknownOptionList2)639 TEST_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 
TEST_F(HelpTest,UnknownOptionHelp1)647 TEST_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 
TEST_F(HelpTest,UnknownOptionHelp2)653 TEST_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 
main(int argc,char ** argv)663 int main(int argc, char **argv) {
664     ::testing::InitGoogleMock(&argc, argv);
665     return RUN_ALL_TESTS();
666 }
667