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