From bf20a261f0ff11dad5f55009de04895953cca8ff Mon Sep 17 00:00:00 2001 From: Yifan Hong Date: Thu, 7 Sep 2017 11:10:58 -0700 Subject: lshal: clean up tests Refactored duplicated code. Test: lshal_test Bug: 35389839 Change-Id: I181fa2122b88e2b7932581f1c63b30decd7c3ddf --- cmds/lshal/test.cpp | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) (limited to 'cmds/lshal/test.cpp') diff --git a/cmds/lshal/test.cpp b/cmds/lshal/test.cpp index 972d508768..b9ed538749 100644 --- a/cmds/lshal/test.cpp +++ b/cmds/lshal/test.cpp @@ -107,7 +107,7 @@ public: }; -class LshalTest : public ::testing::Test { +class DebugTest : public ::testing::Test { public: void SetUp() override { using ::android::hardware::tests::baz::V1_0::IQuux; @@ -122,40 +122,47 @@ public: return new Quux(); return nullptr; })); + + lshal = std::make_unique(out, err, serviceManager, serviceManager); } void TearDown() override {} std::stringstream err; std::stringstream out; sp serviceManager; + + std::unique_ptr lshal; }; -TEST_F(LshalTest, Debug) { - const char *args[] = { +static Arg createArg(const std::vector& args) { + return Arg{static_cast(args.size()), const_cast(args.data())}; +} + +template +static Status callMain(const std::unique_ptr& lshal, const std::vector& args) { + return lshal->main(createArg(args)); +} + +TEST_F(DebugTest, Debug) { + EXPECT_EQ(0u, callMain(lshal, { "lshal", "debug", "android.hardware.tests.baz@1.0::IQuux/default", "foo", "bar" - }; - EXPECT_EQ(0u, Lshal(out, err, serviceManager, serviceManager) - .main({NELEMS(args), const_cast(args)})); + })); EXPECT_THAT(out.str(), StrEq("android.hardware.tests.baz@1.0::IQuux\nfoo\nbar")); EXPECT_THAT(err.str(), IsEmpty()); } -TEST_F(LshalTest, Debug2) { - const char *args[] = { +TEST_F(DebugTest, Debug2) { + EXPECT_EQ(0u, callMain(lshal, { "lshal", "debug", "android.hardware.tests.baz@1.0::IQuux", "baz", "quux" - }; - EXPECT_EQ(0u, Lshal(out, err, serviceManager, serviceManager) - .main({NELEMS(args), const_cast(args)})); + })); EXPECT_THAT(out.str(), StrEq("android.hardware.tests.baz@1.0::IQuux\nbaz\nquux")); EXPECT_THAT(err.str(), IsEmpty()); } -TEST_F(LshalTest, Debug3) { - const char *args[] = { +TEST_F(DebugTest, Debug3) { + EXPECT_NE(0u, callMain(lshal, { "lshal", "debug", "android.hardware.tests.doesnotexist@1.0::IDoesNotExist", - }; - EXPECT_NE(0u, Lshal(out, err, serviceManager, serviceManager) - .main({NELEMS(args), const_cast(args)})); + })); EXPECT_THAT(err.str(), HasSubstr("does not exist")); } -- cgit v1.2.3-59-g8ed1b From b2a2ecb642d1dad620bd91f6b875521a55b08224 Mon Sep 17 00:00:00 2001 From: Yifan Hong Date: Thu, 7 Sep 2017 15:08:22 -0700 Subject: lshal: Add tests for ListCommand::parseArgs Test: lshal_test Bug: 35389839 Change-Id: Iaef39b048b0e7c06e70e21c96448c2f2e24d3128 --- cmds/lshal/ListCommand.h | 2 +- cmds/lshal/Lshal.h | 5 ++-- cmds/lshal/test.cpp | 67 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 3 deletions(-) (limited to 'cmds/lshal/test.cpp') diff --git a/cmds/lshal/ListCommand.h b/cmds/lshal/ListCommand.h index 51f1ca580e..64d33ca0fe 100644 --- a/cmds/lshal/ListCommand.h +++ b/cmds/lshal/ListCommand.h @@ -40,7 +40,7 @@ class ListCommand { public: ListCommand(Lshal &lshal); Status main(const std::string &command, const Arg &arg); -private: +protected: Status parseArgs(const std::string &command, const Arg &arg); Status fetch(); void postprocess(); diff --git a/cmds/lshal/Lshal.h b/cmds/lshal/Lshal.h index 00db5d090a..d3cc4e20a7 100644 --- a/cmds/lshal/Lshal.h +++ b/cmds/lshal/Lshal.h @@ -33,13 +33,14 @@ namespace lshal { class Lshal { public: Lshal(); + virtual ~Lshal() {} Lshal(std::ostream &out, std::ostream &err, sp serviceManager, sp passthroughManager); Status main(const Arg &arg); void usage(const std::string &command = "") const; - NullableOStream err() const; - NullableOStream out() const; + virtual NullableOStream err() const; + virtual NullableOStream out() const; const sp &serviceManager() const; const sp &passthroughManager() const; diff --git a/cmds/lshal/test.cpp b/cmds/lshal/test.cpp index b9ed538749..6cdec29ad3 100644 --- a/cmds/lshal/test.cpp +++ b/cmds/lshal/test.cpp @@ -27,6 +27,7 @@ #include #include +#include "ListCommand.h" #include "Lshal.h" #define NELEMS(array) static_cast(sizeof(array) / sizeof(array[0])) @@ -166,6 +167,72 @@ TEST_F(DebugTest, Debug3) { EXPECT_THAT(err.str(), HasSubstr("does not exist")); } +class MockLshal : public Lshal { +public: + MockLshal() {} + ~MockLshal() = default; + MOCK_CONST_METHOD0(out, NullableOStream()); + MOCK_CONST_METHOD0(err, NullableOStream()); +}; + +// expose protected fields and methods for ListCommand +class MockListCommand : public ListCommand { +public: + MockListCommand(Lshal* lshal) : ListCommand(*lshal) {} + + Status parseArgs(const Arg& arg) { return ListCommand::parseArgs("", arg); } + void forEachTable(const std::function &f) const { + return ListCommand::forEachTable(f); + } +}; + +class ListParseArgsTest : public ::testing::Test { +public: + void SetUp() override { + mockLshal = std::make_unique>(); + mockList = std::make_unique(mockLshal.get()); + // ListCommand::parseArgs should parse arguments from the second element + optind = 1; + } + std::unique_ptr mockLshal; + std::unique_ptr mockList; + std::stringstream output; +}; + +TEST_F(ListParseArgsTest, Default) { + // default args + EXPECT_EQ(0u, mockList->parseArgs(createArg({}))); + mockList->forEachTable([](const Table& table) { + EXPECT_EQ(SelectedColumns({TableColumnType::INTERFACE_NAME, TableColumnType::THREADS, + TableColumnType::SERVER_PID, TableColumnType::CLIENT_PIDS}), + table.getSelectedColumns()); + }); +} + +TEST_F(ListParseArgsTest, Args) { + EXPECT_EQ(0u, mockList->parseArgs(createArg({"lshal", "-p", "-i", "-a", "-c"}))); + mockList->forEachTable([](const Table& table) { + EXPECT_EQ(SelectedColumns({TableColumnType::SERVER_PID, TableColumnType::INTERFACE_NAME, + TableColumnType::SERVER_ADDR, TableColumnType::CLIENT_PIDS}), + table.getSelectedColumns()); + }); +} + +TEST_F(ListParseArgsTest, Cmds) { + EXPECT_EQ(0u, mockList->parseArgs(createArg({"lshal", "-m"}))); + mockList->forEachTable([](const Table& table) { + EXPECT_EQ(SelectedColumns({TableColumnType::INTERFACE_NAME, TableColumnType::THREADS, + TableColumnType::SERVER_CMD, TableColumnType::CLIENT_CMDS}), + table.getSelectedColumns()); + }); +} + +TEST_F(ListParseArgsTest, DebugAndNeat) { + ON_CALL(*mockLshal, err()).WillByDefault(Return(NullableOStream(output))); + EXPECT_NE(0u, mockList->parseArgs(createArg({"lshal", "--neat", "-d"}))); + EXPECT_THAT(output.str(), StrNe("")); +} + } // namespace lshal } // namespace android -- cgit v1.2.3-59-g8ed1b From 8bf7316ebc75f9e5b3a6349bbc9c7140e6e90234 Mon Sep 17 00:00:00 2001 From: Yifan Hong Date: Thu, 7 Sep 2017 18:06:13 -0700 Subject: lshal: add tests for ListCommand::fetch* and dumpVintf Test: lshal_test Change-Id: I9e519ec93709ba4dfa7f95e4c5fff60cbda36134 --- cmds/lshal/Android.bp | 1 + cmds/lshal/ListCommand.cpp | 4 +- cmds/lshal/ListCommand.h | 16 ++- cmds/lshal/TableEntry.cpp | 22 ++++ cmds/lshal/TableEntry.h | 4 + cmds/lshal/test.cpp | 297 ++++++++++++++++++++++++++++++++++++++++++++- 6 files changed, 335 insertions(+), 9 deletions(-) (limited to 'cmds/lshal/test.cpp') diff --git a/cmds/lshal/Android.bp b/cmds/lshal/Android.bp index fab7bfd0e8..47814688a9 100644 --- a/cmds/lshal/Android.bp +++ b/cmds/lshal/Android.bp @@ -61,6 +61,7 @@ cc_test { "libgmock" ], shared_libs: [ + "libvintf", "android.hardware.tests.baz@1.0" ], srcs: [ diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp index 99048afe1c..4550e410a0 100644 --- a/cmds/lshal/ListCommand.cpp +++ b/cmds/lshal/ListCommand.cpp @@ -55,7 +55,7 @@ NullableOStream ListCommand::err() const { return mLshal.err(); } -std::string getCmdline(pid_t pid) { +std::string ListCommand::parseCmdline(pid_t pid) const { std::ifstream ifs("/proc/" + std::to_string(pid) + "/cmdline"); std::string cmdline; if (!ifs.is_open()) { @@ -70,7 +70,7 @@ const std::string &ListCommand::getCmdline(pid_t pid) { if (pair != mCmdlines.end()) { return pair->second; } - mCmdlines[pid] = ::android::lshal::getCmdline(pid); + mCmdlines[pid] = parseCmdline(pid); return mCmdlines[pid]; } diff --git a/cmds/lshal/ListCommand.h b/cmds/lshal/ListCommand.h index 64d33ca0fe..9833d43de5 100644 --- a/cmds/lshal/ListCommand.h +++ b/cmds/lshal/ListCommand.h @@ -36,9 +36,16 @@ namespace lshal { class Lshal; +struct PidInfo { + std::map refPids; // pids that are referenced + uint32_t threadUsage; // number of threads in use + uint32_t threadCount; // number of threads total +}; + class ListCommand { public: ListCommand(Lshal &lshal); + virtual ~ListCommand() = default; Status main(const std::string &command, const Arg &arg); protected: Status parseArgs(const std::string &command, const Arg &arg); @@ -50,12 +57,7 @@ protected: Status fetchBinderized(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager); Status fetchAllLibraries(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager); - struct PidInfo { - std::map refPids; // pids that are referenced - uint32_t threadUsage; // number of threads in use - uint32_t threadCount; // number of threads total - }; - bool getPidInfo(pid_t serverPid, PidInfo *info) const; + virtual bool getPidInfo(pid_t serverPid, PidInfo *info) const; void dumpTable(const NullableOStream& out) const; void dumpVintf(const NullableOStream& out) const; @@ -64,6 +66,8 @@ protected: const std::string &serverCmdline, const std::string &address, const std::string &clients, const std::string &clientCmdlines) const; void addLine(TextTable *table, const TableEntry &entry); + // Read and return /proc/{pid}/cmdline. + virtual std::string parseCmdline(pid_t pid) const; // Return /proc/{pid}/cmdline if it exists, else empty string. const std::string &getCmdline(pid_t pid); // Call getCmdline on all pid in pids. If it returns empty string, the process might diff --git a/cmds/lshal/TableEntry.cpp b/cmds/lshal/TableEntry.cpp index dc6ccaeda5..eac0f2144a 100644 --- a/cmds/lshal/TableEntry.cpp +++ b/cmds/lshal/TableEntry.cpp @@ -152,5 +152,27 @@ TextTable MergedTable::createTextTable() { return textTable; } +bool TableEntry::operator==(const TableEntry& other) const { + if (this == &other) { + return true; + } + return interfaceName == other.interfaceName && transport == other.transport && + serverPid == other.serverPid && threadUsage == other.threadUsage && + threadCount == other.threadCount && serverCmdline == other.serverCmdline && + serverObjectAddress == other.serverObjectAddress && clientPids == other.clientPids && + clientCmdlines == other.clientCmdlines && arch == other.arch; +} + +std::string TableEntry::to_string() const { + std::stringstream ss; + ss << "name=" << interfaceName << ";transport=" << transport << ";thread=" << getThreadUsage() + << ";server=" << serverPid + << "(" << serverObjectAddress << ";" << serverCmdline << ");clients=[" + << join(clientPids, ";") << "](" << join(clientCmdlines, ";") << ");arch=" + << getArchString(arch); + return ss.str(); + +} + } // namespace lshal } // namespace android diff --git a/cmds/lshal/TableEntry.h b/cmds/lshal/TableEntry.h index dac8b5e3b7..7a3b22ea67 100644 --- a/cmds/lshal/TableEntry.h +++ b/cmds/lshal/TableEntry.h @@ -85,6 +85,9 @@ struct TableEntry { } std::string getField(TableColumnType type) const; + + bool operator==(const TableEntry& other) const; + std::string to_string() const; }; using SelectedColumns = std::vector; @@ -97,6 +100,7 @@ public: Entries::const_iterator begin() const { return mEntries.begin(); } Entries::iterator end() { return mEntries.end(); } Entries::const_iterator end() const { return mEntries.end(); } + size_t size() const { return mEntries.size(); } void add(TableEntry&& entry) { mEntries.push_back(std::move(entry)); } diff --git a/cmds/lshal/test.cpp b/cmds/lshal/test.cpp index 6cdec29ad3..44b196e11f 100644 --- a/cmds/lshal/test.cpp +++ b/cmds/lshal/test.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include "ListCommand.h" #include "Lshal.h" @@ -34,6 +35,7 @@ using namespace testing; +using ::android::hidl::base::V1_0::DebugInfo; using ::android::hidl::base::V1_0::IBase; using ::android::hidl::manager::V1_0::IServiceManager; using ::android::hidl::manager::V1_0::IServiceNotification; @@ -42,6 +44,8 @@ using ::android::hardware::hidl_handle; using ::android::hardware::hidl_string; using ::android::hardware::hidl_vec; +using InstanceDebugInfo = IServiceManager::InstanceDebugInfo; + namespace android { namespace hardware { namespace tests { @@ -77,7 +81,6 @@ struct Quux : android::hardware::tests::baz::V1_0::IQuux { namespace lshal { - class MockServiceManager : public IServiceManager { public: template @@ -181,9 +184,17 @@ public: MockListCommand(Lshal* lshal) : ListCommand(*lshal) {} Status parseArgs(const Arg& arg) { return ListCommand::parseArgs("", arg); } + Status main(const Arg& arg) { return ListCommand::main("", arg); } void forEachTable(const std::function &f) const { return ListCommand::forEachTable(f); } + Status fetch() { return ListCommand::fetch(); } + void dumpVintf(const NullableOStream& out) { + return ListCommand::dumpVintf(out); + } + + MOCK_CONST_METHOD2(getPidInfo, bool(pid_t, PidInfo*)); + MOCK_CONST_METHOD1(parseCmdline, std::string(pid_t)); }; class ListParseArgsTest : public ::testing::Test { @@ -233,6 +244,290 @@ TEST_F(ListParseArgsTest, DebugAndNeat) { EXPECT_THAT(output.str(), StrNe("")); } +/// Fetch Test + +// A set of deterministic functions to generate fake debug infos. +static uint64_t getPtr(pid_t serverId) { return 10000 + serverId; } +static std::vector getClients(pid_t serverId) { + return {serverId + 1, serverId + 3}; +} +static PidInfo getPidInfoFromId(pid_t serverId) { + PidInfo info; + info.refPids[getPtr(serverId)] = getClients(serverId); + info.threadUsage = 10 + serverId; + info.threadCount = 20 + serverId; + return info; +} +static std::string getInterfaceName(pid_t serverId) { + return "a.h.foo" + std::to_string(serverId) + "@" + std::to_string(serverId) + ".0::IFoo"; +} +static std::string getInstanceName(pid_t serverId) { + return std::to_string(serverId); +} +static pid_t getIdFromInstanceName(const hidl_string& instance) { + return atoi(instance.c_str()); +} +static std::string getFqInstanceName(pid_t serverId) { + return getInterfaceName(serverId) + "/" + getInstanceName(serverId); +} +static std::string getCmdlineFromId(pid_t serverId) { + if (serverId == NO_PID) return ""; + return "command_line_" + std::to_string(serverId); +} + +// Fake service returned by mocked IServiceManager::get. +class TestService : public IBase { +public: + TestService(DebugInfo&& info) : mInfo(std::move(info)) {} + hardware::Return getDebugInfo(getDebugInfo_cb cb) override { + cb(mInfo); + return hardware::Void(); + } +private: + DebugInfo mInfo; +}; + +class ListTest : public ::testing::Test { +public: + void SetUp() override { + initMockServiceManager(); + lshal = std::make_unique(out, err, serviceManager, passthruManager); + initMockList(); + } + + void initMockList() { + mockList = std::make_unique>(lshal.get()); + ON_CALL(*mockList, getPidInfo(_,_)).WillByDefault(Invoke( + [](pid_t serverPid, PidInfo* info) { + *info = getPidInfoFromId(serverPid); + return true; + })); + ON_CALL(*mockList, parseCmdline(_)).WillByDefault(Invoke(&getCmdlineFromId)); + } + + void initMockServiceManager() { + serviceManager = new testing::NiceMock(); + passthruManager = new testing::NiceMock(); + using A = DebugInfo::Architecture; + ON_CALL(*serviceManager, list(_)).WillByDefault(Invoke( + [] (IServiceManager::list_cb cb) { + cb({ getFqInstanceName(1), getFqInstanceName(2) }); + return hardware::Void(); + })); + + ON_CALL(*serviceManager, get(_, _)).WillByDefault(Invoke( + [&](const hidl_string&, const hidl_string& instance) { + int id = getIdFromInstanceName(instance); + return sp(new TestService({ id /* pid */, getPtr(id), A::IS_64BIT })); + })); + + ON_CALL(*serviceManager, debugDump(_)).WillByDefault(Invoke( + [] (IServiceManager::debugDump_cb cb) { + cb({InstanceDebugInfo{getInterfaceName(3), getInstanceName(3), 3, + getClients(3), A::IS_32BIT}, + InstanceDebugInfo{getInterfaceName(4), getInstanceName(4), 4, + getClients(4), A::IS_32BIT}}); + return hardware::Void(); + })); + + ON_CALL(*passthruManager, debugDump(_)).WillByDefault(Invoke( + [] (IServiceManager::debugDump_cb cb) { + cb({InstanceDebugInfo{getInterfaceName(5), getInstanceName(5), 5, + getClients(5), A::IS_32BIT}, + InstanceDebugInfo{getInterfaceName(6), getInstanceName(6), 6, + getClients(6), A::IS_32BIT}}); + return hardware::Void(); + })); + } + + std::stringstream err; + std::stringstream out; + std::unique_ptr lshal; + std::unique_ptr mockList; + sp serviceManager; + sp passthruManager; +}; + +TEST_F(ListTest, Fetch) { + EXPECT_EQ(0u, mockList->fetch()); + std::array transports{{"hwbinder", "hwbinder", "passthrough", + "passthrough", "passthrough", "passthrough"}}; + std::array archs{{ARCH64, ARCH64, ARCH32, ARCH32, ARCH32, ARCH32}}; + int id = 1; + mockList->forEachTable([&](const Table& table) { + ASSERT_EQ(2u, table.size()); + for (const auto& entry : table) { + const auto& transport = transports[id - 1]; + TableEntry expected{ + .interfaceName = getFqInstanceName(id), + .transport = transport, + .serverPid = transport == "hwbinder" ? id : NO_PID, + .threadUsage = transport == "hwbinder" ? getPidInfoFromId(id).threadUsage : 0, + .threadCount = transport == "hwbinder" ? getPidInfoFromId(id).threadCount : 0, + .serverCmdline = {}, + .serverObjectAddress = transport == "hwbinder" ? getPtr(id) : NO_PTR, + .clientPids = getClients(id), + .clientCmdlines = {}, + .arch = archs[id - 1], + }; + EXPECT_EQ(expected, entry) << expected.to_string() << " vs. " << entry.to_string(); + + ++id; + } + }); + +} + +TEST_F(ListTest, DumpVintf) { + const std::string expected = + "\n" + "\n" + " \n" + " a.h.foo1\n" + " hwbinder\n" + " 1.0\n" + " \n" + " IFoo\n" + " 1\n" + " \n" + " \n" + " \n" + " a.h.foo2\n" + " hwbinder\n" + " 2.0\n" + " \n" + " IFoo\n" + " 2\n" + " \n" + " \n" + " \n" + " a.h.foo3\n" + " passthrough\n" + " 3.0\n" + " \n" + " IFoo\n" + " 3\n" + " \n" + " \n" + " \n" + " a.h.foo4\n" + " passthrough\n" + " 4.0\n" + " \n" + " IFoo\n" + " 4\n" + " \n" + " \n" + " \n" + " a.h.foo5\n" + " passthrough\n" + " 5.0\n" + " \n" + " \n" + " a.h.foo6\n" + " passthrough\n" + " 6.0\n" + " \n" + " \n" + " 0.0\n" + " \n" + "\n"; + + optind = 1; // mimic Lshal::parseArg() + EXPECT_EQ(0u, mockList->main(createArg({"lshal", "--init-vintf"}))); + EXPECT_EQ(expected, out.str()); + EXPECT_EQ("", err.str()); + + vintf::HalManifest m; + EXPECT_EQ(true, vintf::gHalManifestConverter(&m, out.str())) + << "--init-vintf does not emit valid HAL manifest: " + << vintf::gHalManifestConverter.lastError(); +} + +TEST_F(ListTest, Dump) { + const std::string expected = + "All binderized services (registered services through hwservicemanager)\n" + "Interface Transport Arch Thread Use Server PTR Clients\n" + "a.h.foo1@1.0::IFoo/1 hwbinder 64 11/21 1 0000000000002711 2 4\n" + "a.h.foo2@2.0::IFoo/2 hwbinder 64 12/22 2 0000000000002712 3 5\n" + "\n" + "All interfaces that getService() has ever return as a passthrough interface;\n" + "PIDs / processes shown below might be inaccurate because the process\n" + "might have relinquished the interface or might have died.\n" + "The Server / Server CMD column can be ignored.\n" + "The Clients / Clients CMD column shows all process that have ever dlopen'ed \n" + "the library and successfully fetched the passthrough implementation.\n" + "Interface Transport Arch Thread Use Server PTR Clients\n" + "a.h.foo3@3.0::IFoo/3 passthrough 32 N/A N/A N/A 4 6\n" + "a.h.foo4@4.0::IFoo/4 passthrough 32 N/A N/A N/A 5 7\n" + "\n" + "All available passthrough implementations (all -impl.so files)\n" + "Interface Transport Arch Thread Use Server PTR Clients\n" + "a.h.foo5@5.0::IFoo/5 passthrough 32 N/A N/A N/A 6 8\n" + "a.h.foo6@6.0::IFoo/6 passthrough 32 N/A N/A N/A 7 9\n" + "\n"; + + optind = 1; // mimic Lshal::parseArg() + EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-itrepac"}))); + EXPECT_EQ(expected, out.str()); + EXPECT_EQ("", err.str()); +} + +TEST_F(ListTest, DumpCmdline) { + const std::string expected = + "All binderized services (registered services through hwservicemanager)\n" + "Interface Transport Arch Thread Use Server CMD PTR Clients CMD\n" + "a.h.foo1@1.0::IFoo/1 hwbinder 64 11/21 command_line_1 0000000000002711 command_line_2;command_line_4\n" + "a.h.foo2@2.0::IFoo/2 hwbinder 64 12/22 command_line_2 0000000000002712 command_line_3;command_line_5\n" + "\n" + "All interfaces that getService() has ever return as a passthrough interface;\n" + "PIDs / processes shown below might be inaccurate because the process\n" + "might have relinquished the interface or might have died.\n" + "The Server / Server CMD column can be ignored.\n" + "The Clients / Clients CMD column shows all process that have ever dlopen'ed \n" + "the library and successfully fetched the passthrough implementation.\n" + "Interface Transport Arch Thread Use Server CMD PTR Clients CMD\n" + "a.h.foo3@3.0::IFoo/3 passthrough 32 N/A N/A command_line_4;command_line_6\n" + "a.h.foo4@4.0::IFoo/4 passthrough 32 N/A N/A command_line_5;command_line_7\n" + "\n" + "All available passthrough implementations (all -impl.so files)\n" + "Interface Transport Arch Thread Use Server CMD PTR Clients CMD\n" + "a.h.foo5@5.0::IFoo/5 passthrough 32 N/A N/A command_line_6;command_line_8\n" + "a.h.foo6@6.0::IFoo/6 passthrough 32 N/A N/A command_line_7;command_line_9\n" + "\n"; + + optind = 1; // mimic Lshal::parseArg() + EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-itrepacm"}))); + EXPECT_EQ(expected, out.str()); + EXPECT_EQ("", err.str()); +} + +TEST_F(ListTest, DumpNeat) { + const std::string expected = + "a.h.foo1@1.0::IFoo/1 11/21 1 2 4\n" + "a.h.foo2@2.0::IFoo/2 12/22 2 3 5\n" + "a.h.foo3@3.0::IFoo/3 N/A N/A 4 6\n" + "a.h.foo4@4.0::IFoo/4 N/A N/A 5 7\n" + "a.h.foo5@5.0::IFoo/5 N/A N/A 6 8\n" + "a.h.foo6@6.0::IFoo/6 N/A N/A 7 9\n"; + + optind = 1; // mimic Lshal::parseArg() + EXPECT_EQ(0u, mockList->main(createArg({"lshal", "--neat"}))); + EXPECT_EQ(expected, out.str()); + EXPECT_EQ("", err.str()); +} } // namespace lshal } // namespace android -- cgit v1.2.3-59-g8ed1b