blob: 7d87d6d4cc0b0a657e90f360ebadf122788571ca [file] [log] [blame]
Yifan Hong9881df92017-05-10 14:33:05 -07001/*
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>
Yifan Hong8bf73162017-09-07 18:06:13 -070029#include <vintf/parse_xml.h>
Yifan Hong9881df92017-05-10 14:33:05 -070030
Yifan Hongb2a2ecb2017-09-07 15:08:22 -070031#include "ListCommand.h"
Yifan Hong9881df92017-05-10 14:33:05 -070032#include "Lshal.h"
33
34#define NELEMS(array) static_cast<int>(sizeof(array) / sizeof(array[0]))
35
36using namespace testing;
37
Yifan Hong8bf73162017-09-07 18:06:13 -070038using ::android::hidl::base::V1_0::DebugInfo;
Yifan Hong9881df92017-05-10 14:33:05 -070039using ::android::hidl::base::V1_0::IBase;
40using ::android::hidl::manager::V1_0::IServiceManager;
41using ::android::hidl::manager::V1_0::IServiceNotification;
42using ::android::hardware::hidl_death_recipient;
43using ::android::hardware::hidl_handle;
44using ::android::hardware::hidl_string;
45using ::android::hardware::hidl_vec;
46
Yifan Hong8bf73162017-09-07 18:06:13 -070047using InstanceDebugInfo = IServiceManager::InstanceDebugInfo;
48
Yifan Hong9881df92017-05-10 14:33:05 -070049namespace android {
50namespace hardware {
51namespace tests {
52namespace baz {
53namespace V1_0 {
54namespace implementation {
55struct Quux : android::hardware::tests::baz::V1_0::IQuux {
56 ::android::hardware::Return<void> debug(const hidl_handle& hh, const hidl_vec<hidl_string>& options) override {
57 const native_handle_t *handle = hh.getNativeHandle();
58 if (handle->numFds < 1) {
59 return Void();
60 }
61 int fd = handle->data[0];
62 std::string content{descriptor};
63 for (const auto &option : options) {
64 content += "\n";
65 content += option.c_str();
66 }
67 ssize_t written = write(fd, content.c_str(), content.size());
68 if (written != (ssize_t)content.size()) {
69 LOG(WARNING) << "SERVER(Quux) debug writes " << written << " bytes < "
70 << content.size() << " bytes, errno = " << errno;
71 }
72 return Void();
73 }
74};
75
76} // namespace implementation
77} // namespace V1_0
78} // namespace baz
79} // namespace tests
80} // namespace hardware
81
82namespace lshal {
83
Yifan Hong9881df92017-05-10 14:33:05 -070084class MockServiceManager : public IServiceManager {
85public:
86 template<typename T>
87 using R = ::android::hardware::Return<T>;
88 using String = const hidl_string&;
89 ~MockServiceManager() = default;
90
91#define MOCK_METHOD_CB(name) MOCK_METHOD1(name, R<void>(IServiceManager::name##_cb))
92
93 MOCK_METHOD2(get, R<sp<IBase>>(String, String));
94 MOCK_METHOD2(add, R<bool>(String, const sp<IBase>&));
95 MOCK_METHOD2(getTransport, R<IServiceManager::Transport>(String, String));
96 MOCK_METHOD_CB(list);
97 MOCK_METHOD2(listByInterface, R<void>(String, listByInterface_cb));
98 MOCK_METHOD3(registerForNotifications, R<bool>(String, String, const sp<IServiceNotification>&));
99 MOCK_METHOD_CB(debugDump);
100 MOCK_METHOD2(registerPassthroughClient, R<void>(String, String));
101 MOCK_METHOD_CB(interfaceChain);
102 MOCK_METHOD2(debug, R<void>(const hidl_handle&, const hidl_vec<hidl_string>&));
103 MOCK_METHOD_CB(interfaceDescriptor);
104 MOCK_METHOD_CB(getHashChain);
105 MOCK_METHOD0(setHalInstrumentation, R<void>());
106 MOCK_METHOD2(linkToDeath, R<bool>(const sp<hidl_death_recipient>&, uint64_t));
107 MOCK_METHOD0(ping, R<void>());
108 MOCK_METHOD_CB(getDebugInfo);
109 MOCK_METHOD0(notifySyspropsChanged, R<void>());
110 MOCK_METHOD1(unlinkToDeath, R<bool>(const sp<hidl_death_recipient>&));
111
112};
113
Yifan Hongbf20a262017-09-07 11:10:58 -0700114class DebugTest : public ::testing::Test {
Yifan Hong9881df92017-05-10 14:33:05 -0700115public:
116 void SetUp() override {
117 using ::android::hardware::tests::baz::V1_0::IQuux;
118 using ::android::hardware::tests::baz::V1_0::implementation::Quux;
119
120 err.str("");
121 out.str("");
122 serviceManager = new testing::NiceMock<MockServiceManager>();
123 ON_CALL(*serviceManager, get(_, _)).WillByDefault(Invoke(
124 [](const auto &iface, const auto &inst) -> ::android::hardware::Return<sp<IBase>> {
125 if (iface == IQuux::descriptor && inst == "default")
126 return new Quux();
127 return nullptr;
128 }));
Yifan Hongbf20a262017-09-07 11:10:58 -0700129
130 lshal = std::make_unique<Lshal>(out, err, serviceManager, serviceManager);
Yifan Hong9881df92017-05-10 14:33:05 -0700131 }
132 void TearDown() override {}
133
134 std::stringstream err;
135 std::stringstream out;
136 sp<MockServiceManager> serviceManager;
Yifan Hongbf20a262017-09-07 11:10:58 -0700137
138 std::unique_ptr<Lshal> lshal;
Yifan Hong9881df92017-05-10 14:33:05 -0700139};
140
Yifan Hongbf20a262017-09-07 11:10:58 -0700141static Arg createArg(const std::vector<const char*>& args) {
142 return Arg{static_cast<int>(args.size()), const_cast<char**>(args.data())};
143}
144
145template<typename T>
146static Status callMain(const std::unique_ptr<T>& lshal, const std::vector<const char*>& args) {
147 return lshal->main(createArg(args));
148}
149
150TEST_F(DebugTest, Debug) {
151 EXPECT_EQ(0u, callMain(lshal, {
Yifan Hong9881df92017-05-10 14:33:05 -0700152 "lshal", "debug", "android.hardware.tests.baz@1.0::IQuux/default", "foo", "bar"
Yifan Hongbf20a262017-09-07 11:10:58 -0700153 }));
Yifan Hong9881df92017-05-10 14:33:05 -0700154 EXPECT_THAT(out.str(), StrEq("android.hardware.tests.baz@1.0::IQuux\nfoo\nbar"));
155 EXPECT_THAT(err.str(), IsEmpty());
156}
157
Yifan Hongbf20a262017-09-07 11:10:58 -0700158TEST_F(DebugTest, Debug2) {
159 EXPECT_EQ(0u, callMain(lshal, {
Yifan Hong9881df92017-05-10 14:33:05 -0700160 "lshal", "debug", "android.hardware.tests.baz@1.0::IQuux", "baz", "quux"
Yifan Hongbf20a262017-09-07 11:10:58 -0700161 }));
Yifan Hong9881df92017-05-10 14:33:05 -0700162 EXPECT_THAT(out.str(), StrEq("android.hardware.tests.baz@1.0::IQuux\nbaz\nquux"));
163 EXPECT_THAT(err.str(), IsEmpty());
164}
165
Yifan Hongbf20a262017-09-07 11:10:58 -0700166TEST_F(DebugTest, Debug3) {
167 EXPECT_NE(0u, callMain(lshal, {
Yifan Hong9881df92017-05-10 14:33:05 -0700168 "lshal", "debug", "android.hardware.tests.doesnotexist@1.0::IDoesNotExist",
Yifan Hongbf20a262017-09-07 11:10:58 -0700169 }));
Yifan Hong9881df92017-05-10 14:33:05 -0700170 EXPECT_THAT(err.str(), HasSubstr("does not exist"));
171}
172
Yifan Hongb2a2ecb2017-09-07 15:08:22 -0700173class MockLshal : public Lshal {
174public:
175 MockLshal() {}
176 ~MockLshal() = default;
177 MOCK_CONST_METHOD0(out, NullableOStream<std::ostream>());
178 MOCK_CONST_METHOD0(err, NullableOStream<std::ostream>());
179};
180
181// expose protected fields and methods for ListCommand
182class MockListCommand : public ListCommand {
183public:
184 MockListCommand(Lshal* lshal) : ListCommand(*lshal) {}
185
Yifan Honga8bedc62017-09-08 18:00:31 -0700186 Status parseArgs(const Arg& arg) { return ListCommand::parseArgs(arg); }
187 Status main(const Arg& arg) { return ListCommand::main(arg); }
Yifan Hong93b8bff2017-09-14 16:02:52 -0700188 void forEachTable(const std::function<void(Table &)> &f) {
189 return ListCommand::forEachTable(f);
190 }
Yifan Hongb2a2ecb2017-09-07 15:08:22 -0700191 void forEachTable(const std::function<void(const Table &)> &f) const {
192 return ListCommand::forEachTable(f);
193 }
Yifan Hong8bf73162017-09-07 18:06:13 -0700194 Status fetch() { return ListCommand::fetch(); }
195 void dumpVintf(const NullableOStream<std::ostream>& out) {
196 return ListCommand::dumpVintf(out);
197 }
Yifan Hong93b8bff2017-09-14 16:02:52 -0700198 void internalPostprocess() { ListCommand::postprocess(); }
Yifan Hong1243dde2017-09-14 17:49:30 -0700199 const PidInfo* getPidInfoCached(pid_t serverPid) {
200 return ListCommand::getPidInfoCached(serverPid);
201 }
Yifan Hong8bf73162017-09-07 18:06:13 -0700202
Yifan Hong93b8bff2017-09-14 16:02:52 -0700203 MOCK_METHOD0(postprocess, void());
Yifan Hong8bf73162017-09-07 18:06:13 -0700204 MOCK_CONST_METHOD2(getPidInfo, bool(pid_t, PidInfo*));
205 MOCK_CONST_METHOD1(parseCmdline, std::string(pid_t));
Yifan Hongb2a2ecb2017-09-07 15:08:22 -0700206};
207
208class ListParseArgsTest : public ::testing::Test {
209public:
210 void SetUp() override {
211 mockLshal = std::make_unique<NiceMock<MockLshal>>();
212 mockList = std::make_unique<MockListCommand>(mockLshal.get());
213 // ListCommand::parseArgs should parse arguments from the second element
214 optind = 1;
215 }
216 std::unique_ptr<MockLshal> mockLshal;
217 std::unique_ptr<MockListCommand> mockList;
218 std::stringstream output;
219};
220
Yifan Hongb2a2ecb2017-09-07 15:08:22 -0700221TEST_F(ListParseArgsTest, Args) {
222 EXPECT_EQ(0u, mockList->parseArgs(createArg({"lshal", "-p", "-i", "-a", "-c"})));
223 mockList->forEachTable([](const Table& table) {
224 EXPECT_EQ(SelectedColumns({TableColumnType::SERVER_PID, TableColumnType::INTERFACE_NAME,
225 TableColumnType::SERVER_ADDR, TableColumnType::CLIENT_PIDS}),
226 table.getSelectedColumns());
227 });
228}
229
230TEST_F(ListParseArgsTest, Cmds) {
231 EXPECT_EQ(0u, mockList->parseArgs(createArg({"lshal", "-m"})));
232 mockList->forEachTable([](const Table& table) {
Yifan Hong7a3b46c2017-09-14 18:18:53 -0700233 EXPECT_THAT(table.getSelectedColumns(), Not(Contains(TableColumnType::SERVER_PID)))
234 << "should not print server PID with -m";
235 EXPECT_THAT(table.getSelectedColumns(), Not(Contains(TableColumnType::CLIENT_PIDS)))
236 << "should not print client PIDs with -m";
237 EXPECT_THAT(table.getSelectedColumns(), Contains(TableColumnType::SERVER_CMD))
238 << "should print server cmd with -m";
239 EXPECT_THAT(table.getSelectedColumns(), Contains(TableColumnType::CLIENT_CMDS))
240 << "should print client cmds with -m";
Yifan Hongb2a2ecb2017-09-07 15:08:22 -0700241 });
242}
243
244TEST_F(ListParseArgsTest, DebugAndNeat) {
245 ON_CALL(*mockLshal, err()).WillByDefault(Return(NullableOStream<std::ostream>(output)));
246 EXPECT_NE(0u, mockList->parseArgs(createArg({"lshal", "--neat", "-d"})));
247 EXPECT_THAT(output.str(), StrNe(""));
248}
249
Yifan Hong8bf73162017-09-07 18:06:13 -0700250/// Fetch Test
251
252// A set of deterministic functions to generate fake debug infos.
253static uint64_t getPtr(pid_t serverId) { return 10000 + serverId; }
254static std::vector<pid_t> getClients(pid_t serverId) {
255 return {serverId + 1, serverId + 3};
256}
257static PidInfo getPidInfoFromId(pid_t serverId) {
258 PidInfo info;
259 info.refPids[getPtr(serverId)] = getClients(serverId);
260 info.threadUsage = 10 + serverId;
261 info.threadCount = 20 + serverId;
262 return info;
263}
264static std::string getInterfaceName(pid_t serverId) {
265 return "a.h.foo" + std::to_string(serverId) + "@" + std::to_string(serverId) + ".0::IFoo";
266}
267static std::string getInstanceName(pid_t serverId) {
268 return std::to_string(serverId);
269}
270static pid_t getIdFromInstanceName(const hidl_string& instance) {
271 return atoi(instance.c_str());
272}
273static std::string getFqInstanceName(pid_t serverId) {
274 return getInterfaceName(serverId) + "/" + getInstanceName(serverId);
275}
276static std::string getCmdlineFromId(pid_t serverId) {
277 if (serverId == NO_PID) return "";
278 return "command_line_" + std::to_string(serverId);
279}
280
281// Fake service returned by mocked IServiceManager::get.
282class TestService : public IBase {
283public:
284 TestService(DebugInfo&& info) : mInfo(std::move(info)) {}
285 hardware::Return<void> getDebugInfo(getDebugInfo_cb cb) override {
286 cb(mInfo);
287 return hardware::Void();
288 }
289private:
290 DebugInfo mInfo;
291};
292
293class ListTest : public ::testing::Test {
294public:
295 void SetUp() override {
296 initMockServiceManager();
297 lshal = std::make_unique<Lshal>(out, err, serviceManager, passthruManager);
298 initMockList();
299 }
300
301 void initMockList() {
302 mockList = std::make_unique<NiceMock<MockListCommand>>(lshal.get());
303 ON_CALL(*mockList, getPidInfo(_,_)).WillByDefault(Invoke(
304 [](pid_t serverPid, PidInfo* info) {
305 *info = getPidInfoFromId(serverPid);
306 return true;
307 }));
308 ON_CALL(*mockList, parseCmdline(_)).WillByDefault(Invoke(&getCmdlineFromId));
Yifan Hong93b8bff2017-09-14 16:02:52 -0700309 ON_CALL(*mockList, postprocess()).WillByDefault(Invoke([&]() {
310 mockList->internalPostprocess();
311 size_t i = 0;
312 mockList->forEachTable([&](Table& table) {
313 table.setDescription("[fake description " + std::to_string(i++) + "]");
314 });
315 }));
Yifan Hong8bf73162017-09-07 18:06:13 -0700316 }
317
318 void initMockServiceManager() {
319 serviceManager = new testing::NiceMock<MockServiceManager>();
320 passthruManager = new testing::NiceMock<MockServiceManager>();
321 using A = DebugInfo::Architecture;
322 ON_CALL(*serviceManager, list(_)).WillByDefault(Invoke(
323 [] (IServiceManager::list_cb cb) {
324 cb({ getFqInstanceName(1), getFqInstanceName(2) });
325 return hardware::Void();
326 }));
327
328 ON_CALL(*serviceManager, get(_, _)).WillByDefault(Invoke(
329 [&](const hidl_string&, const hidl_string& instance) {
330 int id = getIdFromInstanceName(instance);
331 return sp<IBase>(new TestService({ id /* pid */, getPtr(id), A::IS_64BIT }));
332 }));
333
334 ON_CALL(*serviceManager, debugDump(_)).WillByDefault(Invoke(
335 [] (IServiceManager::debugDump_cb cb) {
336 cb({InstanceDebugInfo{getInterfaceName(3), getInstanceName(3), 3,
337 getClients(3), A::IS_32BIT},
338 InstanceDebugInfo{getInterfaceName(4), getInstanceName(4), 4,
339 getClients(4), A::IS_32BIT}});
340 return hardware::Void();
341 }));
342
343 ON_CALL(*passthruManager, debugDump(_)).WillByDefault(Invoke(
344 [] (IServiceManager::debugDump_cb cb) {
345 cb({InstanceDebugInfo{getInterfaceName(5), getInstanceName(5), 5,
346 getClients(5), A::IS_32BIT},
347 InstanceDebugInfo{getInterfaceName(6), getInstanceName(6), 6,
348 getClients(6), A::IS_32BIT}});
349 return hardware::Void();
350 }));
351 }
352
353 std::stringstream err;
354 std::stringstream out;
355 std::unique_ptr<Lshal> lshal;
356 std::unique_ptr<MockListCommand> mockList;
357 sp<MockServiceManager> serviceManager;
358 sp<MockServiceManager> passthruManager;
359};
360
Yifan Hong1243dde2017-09-14 17:49:30 -0700361TEST_F(ListTest, GetPidInfoCached) {
362 EXPECT_CALL(*mockList, getPidInfo(5, _)).Times(1);
363
364 EXPECT_NE(nullptr, mockList->getPidInfoCached(5));
365 EXPECT_NE(nullptr, mockList->getPidInfoCached(5));
366}
367
Yifan Hong8bf73162017-09-07 18:06:13 -0700368TEST_F(ListTest, Fetch) {
369 EXPECT_EQ(0u, mockList->fetch());
370 std::array<std::string, 6> transports{{"hwbinder", "hwbinder", "passthrough",
371 "passthrough", "passthrough", "passthrough"}};
372 std::array<Architecture, 6> archs{{ARCH64, ARCH64, ARCH32, ARCH32, ARCH32, ARCH32}};
373 int id = 1;
374 mockList->forEachTable([&](const Table& table) {
375 ASSERT_EQ(2u, table.size());
376 for (const auto& entry : table) {
377 const auto& transport = transports[id - 1];
378 TableEntry expected{
379 .interfaceName = getFqInstanceName(id),
380 .transport = transport,
381 .serverPid = transport == "hwbinder" ? id : NO_PID,
382 .threadUsage = transport == "hwbinder" ? getPidInfoFromId(id).threadUsage : 0,
383 .threadCount = transport == "hwbinder" ? getPidInfoFromId(id).threadCount : 0,
384 .serverCmdline = {},
385 .serverObjectAddress = transport == "hwbinder" ? getPtr(id) : NO_PTR,
386 .clientPids = getClients(id),
387 .clientCmdlines = {},
388 .arch = archs[id - 1],
389 };
390 EXPECT_EQ(expected, entry) << expected.to_string() << " vs. " << entry.to_string();
391
392 ++id;
393 }
394 });
395
396}
397
398TEST_F(ListTest, DumpVintf) {
399 const std::string expected =
400 "<!-- \n"
401 " This is a skeleton device manifest. Notes: \n"
402 " 1. android.hidl.*, android.frameworks.*, android.system.* are not included.\n"
403 " 2. If a HAL is supported in both hwbinder and passthrough transport, \n"
404 " only hwbinder is shown.\n"
405 " 3. It is likely that HALs in passthrough transport does not have\n"
406 " <interface> declared; users will have to write them by hand.\n"
407 " 4. A HAL with lower minor version can be overridden by a HAL with\n"
408 " higher minor version if they have the same name and major version.\n"
409 " 5. sepolicy version is set to 0.0. It is recommended that the entry\n"
410 " is removed from the manifest file and written by assemble_vintf\n"
411 " at build time.\n"
412 "-->\n"
413 "<manifest version=\"1.0\" type=\"device\">\n"
414 " <hal format=\"hidl\">\n"
415 " <name>a.h.foo1</name>\n"
416 " <transport>hwbinder</transport>\n"
417 " <version>1.0</version>\n"
418 " <interface>\n"
419 " <name>IFoo</name>\n"
420 " <instance>1</instance>\n"
421 " </interface>\n"
422 " </hal>\n"
423 " <hal format=\"hidl\">\n"
424 " <name>a.h.foo2</name>\n"
425 " <transport>hwbinder</transport>\n"
426 " <version>2.0</version>\n"
427 " <interface>\n"
428 " <name>IFoo</name>\n"
429 " <instance>2</instance>\n"
430 " </interface>\n"
431 " </hal>\n"
432 " <hal format=\"hidl\">\n"
433 " <name>a.h.foo3</name>\n"
434 " <transport arch=\"32\">passthrough</transport>\n"
435 " <version>3.0</version>\n"
436 " <interface>\n"
437 " <name>IFoo</name>\n"
438 " <instance>3</instance>\n"
439 " </interface>\n"
440 " </hal>\n"
441 " <hal format=\"hidl\">\n"
442 " <name>a.h.foo4</name>\n"
443 " <transport arch=\"32\">passthrough</transport>\n"
444 " <version>4.0</version>\n"
445 " <interface>\n"
446 " <name>IFoo</name>\n"
447 " <instance>4</instance>\n"
448 " </interface>\n"
449 " </hal>\n"
450 " <hal format=\"hidl\">\n"
451 " <name>a.h.foo5</name>\n"
452 " <transport arch=\"32\">passthrough</transport>\n"
453 " <version>5.0</version>\n"
454 " </hal>\n"
455 " <hal format=\"hidl\">\n"
456 " <name>a.h.foo6</name>\n"
457 " <transport arch=\"32\">passthrough</transport>\n"
458 " <version>6.0</version>\n"
459 " </hal>\n"
460 " <sepolicy>\n"
461 " <version>0.0</version>\n"
462 " </sepolicy>\n"
463 "</manifest>\n";
464
465 optind = 1; // mimic Lshal::parseArg()
466 EXPECT_EQ(0u, mockList->main(createArg({"lshal", "--init-vintf"})));
467 EXPECT_EQ(expected, out.str());
468 EXPECT_EQ("", err.str());
469
470 vintf::HalManifest m;
471 EXPECT_EQ(true, vintf::gHalManifestConverter(&m, out.str()))
472 << "--init-vintf does not emit valid HAL manifest: "
473 << vintf::gHalManifestConverter.lastError();
474}
475
476TEST_F(ListTest, Dump) {
477 const std::string expected =
Yifan Hong93b8bff2017-09-14 16:02:52 -0700478 "[fake description 0]\n"
Yifan Hong8bf73162017-09-07 18:06:13 -0700479 "Interface Transport Arch Thread Use Server PTR Clients\n"
480 "a.h.foo1@1.0::IFoo/1 hwbinder 64 11/21 1 0000000000002711 2 4\n"
481 "a.h.foo2@2.0::IFoo/2 hwbinder 64 12/22 2 0000000000002712 3 5\n"
482 "\n"
Yifan Hong93b8bff2017-09-14 16:02:52 -0700483 "[fake description 1]\n"
Yifan Hong8bf73162017-09-07 18:06:13 -0700484 "Interface Transport Arch Thread Use Server PTR Clients\n"
485 "a.h.foo3@3.0::IFoo/3 passthrough 32 N/A N/A N/A 4 6\n"
486 "a.h.foo4@4.0::IFoo/4 passthrough 32 N/A N/A N/A 5 7\n"
487 "\n"
Yifan Hong93b8bff2017-09-14 16:02:52 -0700488 "[fake description 2]\n"
Yifan Hong8bf73162017-09-07 18:06:13 -0700489 "Interface Transport Arch Thread Use Server PTR Clients\n"
490 "a.h.foo5@5.0::IFoo/5 passthrough 32 N/A N/A N/A 6 8\n"
491 "a.h.foo6@6.0::IFoo/6 passthrough 32 N/A N/A N/A 7 9\n"
492 "\n";
493
494 optind = 1; // mimic Lshal::parseArg()
495 EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-itrepac"})));
496 EXPECT_EQ(expected, out.str());
497 EXPECT_EQ("", err.str());
498}
499
500TEST_F(ListTest, DumpCmdline) {
501 const std::string expected =
Yifan Hong93b8bff2017-09-14 16:02:52 -0700502 "[fake description 0]\n"
Yifan Hong8bf73162017-09-07 18:06:13 -0700503 "Interface Transport Arch Thread Use Server CMD PTR Clients CMD\n"
504 "a.h.foo1@1.0::IFoo/1 hwbinder 64 11/21 command_line_1 0000000000002711 command_line_2;command_line_4\n"
505 "a.h.foo2@2.0::IFoo/2 hwbinder 64 12/22 command_line_2 0000000000002712 command_line_3;command_line_5\n"
506 "\n"
Yifan Hong93b8bff2017-09-14 16:02:52 -0700507 "[fake description 1]\n"
Yifan Hong8bf73162017-09-07 18:06:13 -0700508 "Interface Transport Arch Thread Use Server CMD PTR Clients CMD\n"
509 "a.h.foo3@3.0::IFoo/3 passthrough 32 N/A N/A command_line_4;command_line_6\n"
510 "a.h.foo4@4.0::IFoo/4 passthrough 32 N/A N/A command_line_5;command_line_7\n"
511 "\n"
Yifan Hong93b8bff2017-09-14 16:02:52 -0700512 "[fake description 2]\n"
Yifan Hong8bf73162017-09-07 18:06:13 -0700513 "Interface Transport Arch Thread Use Server CMD PTR Clients CMD\n"
514 "a.h.foo5@5.0::IFoo/5 passthrough 32 N/A N/A command_line_6;command_line_8\n"
515 "a.h.foo6@6.0::IFoo/6 passthrough 32 N/A N/A command_line_7;command_line_9\n"
516 "\n";
517
518 optind = 1; // mimic Lshal::parseArg()
519 EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-itrepacm"})));
520 EXPECT_EQ(expected, out.str());
521 EXPECT_EQ("", err.str());
522}
523
524TEST_F(ListTest, DumpNeat) {
525 const std::string expected =
526 "a.h.foo1@1.0::IFoo/1 11/21 1 2 4\n"
527 "a.h.foo2@2.0::IFoo/2 12/22 2 3 5\n"
528 "a.h.foo3@3.0::IFoo/3 N/A N/A 4 6\n"
529 "a.h.foo4@4.0::IFoo/4 N/A N/A 5 7\n"
530 "a.h.foo5@5.0::IFoo/5 N/A N/A 6 8\n"
531 "a.h.foo6@6.0::IFoo/6 N/A N/A 7 9\n";
532
533 optind = 1; // mimic Lshal::parseArg()
Yifan Hong7a3b46c2017-09-14 18:18:53 -0700534 EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-iepc", "--neat"})));
Yifan Hong8bf73162017-09-07 18:06:13 -0700535 EXPECT_EQ(expected, out.str());
536 EXPECT_EQ("", err.str());
537}
Yifan Honga8bedc62017-09-08 18:00:31 -0700538
539class HelpTest : public ::testing::Test {
540public:
541 void SetUp() override {
542 lshal = std::make_unique<Lshal>(out, err, new MockServiceManager() /* serviceManager */,
543 new MockServiceManager() /* passthruManager */);
544 }
545
546 std::stringstream err;
547 std::stringstream out;
548 std::unique_ptr<Lshal> lshal;
549};
550
551TEST_F(HelpTest, GlobalUsage) {
552 (void)callMain(lshal, {"lshal", "--help"}); // ignore return
553 std::string errStr = err.str();
554 EXPECT_THAT(errStr, ContainsRegex("(^|\n)commands:($|\n)"))
555 << "`lshal --help` does not contain global usage";
556 EXPECT_THAT(errStr, ContainsRegex("(^|\n)list:($|\n)"))
557 << "`lshal --help` does not contain usage for 'list' command";
558 EXPECT_THAT(errStr, ContainsRegex("(^|\n)debug:($|\n)"))
559 << "`lshal --help` does not contain usage for 'debug' command";
560 EXPECT_THAT(errStr, ContainsRegex("(^|\n)help:($|\n)"))
561 << "`lshal --help` does not contain usage for 'help' command";
562
563 err.str("");
564 (void)callMain(lshal, {"lshal", "help"}); // ignore return
565 EXPECT_EQ(errStr, err.str()) << "`lshal help` should have the same output as `lshal --help`";
566
567 err.str("");
568 EXPECT_NE(0u, callMain(lshal, {"lshal", "--unknown-option"}));
569 EXPECT_THAT(err.str(), ContainsRegex("unrecognized option"));
570 EXPECT_THAT(err.str(), EndsWith(errStr))
571 << "`lshal --unknown-option` should have the same output as `lshal --help`";
572 EXPECT_EQ("", out.str());
573}
574
575TEST_F(HelpTest, UnknownOptionList1) {
576 (void)callMain(lshal, {"lshal", "help", "list"});
577 EXPECT_THAT(err.str(), ContainsRegex("(^|\n)list:($|\n)"))
578 << "`lshal help list` does not contain usage for 'list' command";
579}
580
581TEST_F(HelpTest, UnknownOptionList2) {
582 EXPECT_NE(0u, callMain(lshal, {"lshal", "list", "--unknown-option"}));
583 EXPECT_THAT(err.str(), ContainsRegex("unrecognized option"));
584 EXPECT_THAT(err.str(), ContainsRegex("(^|\n)list:($|\n)"))
585 << "`lshal list --unknown-option` does not contain usage for 'list' command";
586 EXPECT_EQ("", out.str());
587}
588
589TEST_F(HelpTest, UnknownOptionHelp1) {
590 (void)callMain(lshal, {"lshal", "help", "help"});
591 EXPECT_THAT(err.str(), ContainsRegex("(^|\n)help:($|\n)"))
592 << "`lshal help help` does not contain usage for 'help' command";
593}
594
595TEST_F(HelpTest, UnknownOptionHelp2) {
596 (void)callMain(lshal, {"lshal", "help", "--unknown-option"});
597 EXPECT_THAT(err.str(), ContainsRegex("(^|\n)help:($|\n)"))
598 << "`lshal help --unknown-option` does not contain usage for 'help' command";
599 EXPECT_EQ("", out.str());
600}
601
Yifan Hong9881df92017-05-10 14:33:05 -0700602} // namespace lshal
603} // namespace android
604
605int main(int argc, char **argv) {
606 ::testing::InitGoogleMock(&argc, argv);
607 return RUN_ALL_TESTS();
608}