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 { 59 ::android::hardware::Return<void> debug(const hidl_handle& hh, const hidl_vec<hidl_string>& options) override { 60 const native_handle_t *handle = hh.getNativeHandle(); 61 if (handle->numFds < 1) { 62 return Void(); 63 } 64 int fd = handle->data[0]; 65 std::string content{descriptor}; 66 for (const auto &option : options) { 67 content += "\n"; 68 content += option.c_str(); 69 } 70 ssize_t written = write(fd, content.c_str(), content.size()); 71 if (written != (ssize_t)content.size()) { 72 LOG(WARNING) << "SERVER(Quux) debug writes " << written << " bytes < " 73 << content.size() << " bytes, errno = " << errno; 74 } 75 return Void(); 76 } 77 }; 78 79 } // namespace implementation 80 } // namespace V1_0 81 } // namespace baz 82 } // namespace tests 83 } // namespace hardware 84 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: 119 void SetUp() override { 120 using ::android::hardware::tests::baz::V1_0::IQuux; 121 using ::android::hardware::tests::baz::V1_0::implementation::Quux; 122 123 err.str(""); 124 out.str(""); 125 serviceManager = new testing::NiceMock<MockServiceManager>(); 126 ON_CALL(*serviceManager, get(_, _)).WillByDefault(Invoke( 127 [](const auto &iface, const auto &inst) -> ::android::hardware::Return<sp<IBase>> { 128 if (iface == IQuux::descriptor && inst == "default") 129 return new Quux(); 130 return nullptr; 131 })); 132 133 lshal = std::make_unique<Lshal>(out, err, serviceManager, serviceManager); 134 } 135 void TearDown() override {} 136 137 std::stringstream err; 138 std::stringstream out; 139 sp<MockServiceManager> serviceManager; 140 141 std::unique_ptr<Lshal> lshal; 142 }; 143 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> 149 static Status callMain(const std::unique_ptr<T>& lshal, const std::vector<const char*>& args) { 150 return lshal->main(createArg(args)); 151 } 152 153 TEST_F(DebugTest, Debug) { 154 EXPECT_EQ(0u, callMain(lshal, { 155 "lshal", "debug", "android.hardware.tests.baz (at) 1.0::IQuux/default", "foo", "bar" 156 })); 157 EXPECT_THAT(out.str(), StrEq("android.hardware.tests.baz (at) 1.0::IQuux\nfoo\nbar")); 158 EXPECT_THAT(err.str(), IsEmpty()); 159 } 160 161 TEST_F(DebugTest, Debug2) { 162 EXPECT_EQ(0u, callMain(lshal, { 163 "lshal", "debug", "android.hardware.tests.baz (at) 1.0::IQuux", "baz", "quux" 164 })); 165 EXPECT_THAT(out.str(), StrEq("android.hardware.tests.baz (at) 1.0::IQuux\nbaz\nquux")); 166 EXPECT_THAT(err.str(), IsEmpty()); 167 } 168 169 TEST_F(DebugTest, Debug3) { 170 EXPECT_NE(0u, callMain(lshal, { 171 "lshal", "debug", "android.hardware.tests.doesnotexist (at) 1.0::IDoesNotExist", 172 })); 173 EXPECT_THAT(err.str(), HasSubstr("does not exist")); 174 } 175 176 class MockLshal : public Lshal { 177 public: 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: 187 MockListCommand(Lshal* lshal) : ListCommand(*lshal) {} 188 189 Status parseArgs(const Arg& arg) { return ListCommand::parseArgs(arg); } 190 Status main(const Arg& arg) { return ListCommand::main(arg); } 191 void forEachTable(const std::function<void(Table &)> &f) { 192 return ListCommand::forEachTable(f); 193 } 194 void forEachTable(const std::function<void(const Table &)> &f) const { 195 return ListCommand::forEachTable(f); 196 } 197 Status fetch() { return ListCommand::fetch(); } 198 void dumpVintf(const NullableOStream<std::ostream>& out) { 199 return ListCommand::dumpVintf(out); 200 } 201 void internalPostprocess() { ListCommand::postprocess(); } 202 const PidInfo* getPidInfoCached(pid_t serverPid) { 203 return ListCommand::getPidInfoCached(serverPid); 204 } 205 206 MOCK_METHOD0(postprocess, void()); 207 MOCK_CONST_METHOD2(getPidInfo, bool(pid_t, PidInfo*)); 208 MOCK_CONST_METHOD1(parseCmdline, std::string(pid_t)); 209 MOCK_METHOD1(getPartition, Partition(pid_t)); 210 }; 211 212 class ListParseArgsTest : public ::testing::Test { 213 public: 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 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 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 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. 257 static uint64_t getPtr(pid_t serverId) { return 10000 + serverId; } 258 static std::vector<pid_t> getClients(pid_t serverId) { 259 return {serverId + 1, serverId + 3}; 260 } 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 } 268 static std::string getInterfaceName(pid_t serverId) { 269 return "a.h.foo" + std::to_string(serverId) + "@" + std::to_string(serverId) + ".0::IFoo"; 270 } 271 static std::string getInstanceName(pid_t serverId) { 272 return std::to_string(serverId); 273 } 274 static pid_t getIdFromInstanceName(const hidl_string& instance) { 275 return atoi(instance.c_str()); 276 } 277 static std::string getFqInstanceName(pid_t serverId) { 278 return getInterfaceName(serverId) + "/" + getInstanceName(serverId); 279 } 280 static std::string getCmdlineFromId(pid_t serverId) { 281 if (serverId == NO_PID) return ""; 282 return "command_line_" + std::to_string(serverId); 283 } 284 static bool getIsReleasedFromId(pid_t p) { return p % 2 == 0; } 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: 297 TestService(pid_t id) : mId(id) {} 298 hardware::Return<void> getDebugInfo(getDebugInfo_cb cb) override { 299 cb({ mId /* pid */, getPtr(mId), DebugInfo::Architecture::IS_64BIT }); 300 return hardware::Void(); 301 } 302 hardware::Return<void> interfaceChain(interfaceChain_cb cb) override { 303 cb({getInterfaceName(mId), IBase::descriptor}); 304 return hardware::Void(); 305 } 306 hardware::Return<void> getHashChain(getHashChain_cb cb) override { 307 cb({getHashFromId(mId), getHashFromId(0xff)}); 308 return hardware::Void(); 309 } 310 private: 311 pid_t mId; 312 }; 313 314 class ListTest : public ::testing::Test { 315 public: 316 void SetUp() override { 317 initMockServiceManager(); 318 lshal = std::make_unique<Lshal>(out, err, serviceManager, passthruManager); 319 initMockList(); 320 } 321 322 void initMockList() { 323 mockList = std::make_unique<NiceMock<MockListCommand>>(lshal.get()); 324 ON_CALL(*mockList, getPidInfo(_,_)).WillByDefault(Invoke( 325 [](pid_t serverPid, PidInfo* info) { 326 *info = getPidInfoFromId(serverPid); 327 return true; 328 })); 329 ON_CALL(*mockList, parseCmdline(_)).WillByDefault(Invoke(&getCmdlineFromId)); 330 ON_CALL(*mockList, postprocess()).WillByDefault(Invoke([&]() { 331 mockList->internalPostprocess(); 332 size_t i = 0; 333 mockList->forEachTable([&](Table& table) { 334 table.setDescription("[fake description " + std::to_string(i++) + "]"); 335 }); 336 })); 337 ON_CALL(*mockList, getPartition(_)).WillByDefault(Return(Partition::VENDOR)); 338 } 339 340 void initMockServiceManager() { 341 serviceManager = new testing::NiceMock<MockServiceManager>(); 342 passthruManager = new testing::NiceMock<MockServiceManager>(); 343 using A = DebugInfo::Architecture; 344 ON_CALL(*serviceManager, list(_)).WillByDefault(Invoke( 345 [] (IServiceManager::list_cb cb) { 346 cb({ getFqInstanceName(1), getFqInstanceName(2) }); 347 return hardware::Void(); 348 })); 349 350 ON_CALL(*serviceManager, get(_, _)).WillByDefault(Invoke( 351 [&](const hidl_string&, const hidl_string& instance) { 352 int id = getIdFromInstanceName(instance); 353 return sp<IBase>(new TestService(id)); 354 })); 355 356 ON_CALL(*serviceManager, debugDump(_)).WillByDefault(Invoke( 357 [] (IServiceManager::debugDump_cb cb) { 358 cb({InstanceDebugInfo{getInterfaceName(3), getInstanceName(3), 3, 359 getClients(3), A::IS_32BIT}, 360 InstanceDebugInfo{getInterfaceName(4), getInstanceName(4), 4, 361 getClients(4), A::IS_32BIT}}); 362 return hardware::Void(); 363 })); 364 365 ON_CALL(*passthruManager, debugDump(_)).WillByDefault(Invoke( 366 [] (IServiceManager::debugDump_cb cb) { 367 cb({InstanceDebugInfo{getInterfaceName(5), getInstanceName(5), 5, 368 getClients(5), A::IS_32BIT}, 369 InstanceDebugInfo{getInterfaceName(6), getInstanceName(6), 6, 370 getClients(6), A::IS_32BIT}}); 371 return hardware::Void(); 372 })); 373 } 374 375 std::stringstream err; 376 std::stringstream out; 377 std::unique_ptr<Lshal> lshal; 378 std::unique_ptr<MockListCommand> mockList; 379 sp<MockServiceManager> serviceManager; 380 sp<MockServiceManager> passthruManager; 381 }; 382 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 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 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 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 (at) 1.0::IFoo/1 11/21 1 2 4\n" 491 "Y a.h.foo2 (at) 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 (at) 3.0::IFoo/3 N/A N/A 4 6\n" 496 " a.h.foo4 (at) 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 (at) 5.0::IFoo/5 N/A N/A 6 8\n" 501 " a.h.foo6 (at) 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 510 TEST_F(ListTest, DumpHash) { 511 const std::string expected = 512 "[fake description 0]\n" 513 "Interface R Hash\n" 514 "a.h.foo1 (at) 1.0::IFoo/1 0000000000000000000000000000000000000000000000000000000000000000\n" 515 "a.h.foo2 (at) 2.0::IFoo/2 Y 0202020202020202020202020202020202020202020202020202020202020202\n" 516 "\n" 517 "[fake description 1]\n" 518 "Interface R Hash\n" 519 "a.h.foo3 (at) 3.0::IFoo/3 \n" 520 "a.h.foo4 (at) 4.0::IFoo/4 \n" 521 "\n" 522 "[fake description 2]\n" 523 "Interface R Hash\n" 524 "a.h.foo5 (at) 5.0::IFoo/5 \n" 525 "a.h.foo6 (at) 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 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 (at) 1.0::IFoo/1 hwbinder 64 11/21 1 0000000000002711 2 4\n" 539 "a.h.foo2 (at) 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 (at) 3.0::IFoo/3 passthrough 32 N/A N/A N/A 4 6\n" 544 "a.h.foo4 (at) 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 (at) 5.0::IFoo/5 passthrough 32 N/A N/A N/A 6 8\n" 549 "a.h.foo6 (at) 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 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 (at) 1.0::IFoo/1 hwbinder 64 11/21 command_line_1 0000000000002711 command_line_2;command_line_4\n" 563 "a.h.foo2 (at) 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 (at) 3.0::IFoo/3 passthrough 32 N/A N/A command_line_4;command_line_6\n" 568 "a.h.foo4 (at) 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 (at) 5.0::IFoo/5 passthrough 32 N/A N/A command_line_6;command_line_8\n" 573 "a.h.foo6 (at) 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 582 TEST_F(ListTest, DumpNeat) { 583 const std::string expected = 584 "a.h.foo1 (at) 1.0::IFoo/1 11/21 1 2 4\n" 585 "a.h.foo2 (at) 2.0::IFoo/2 12/22 2 3 5\n" 586 "a.h.foo3 (at) 3.0::IFoo/3 N/A N/A 4 6\n" 587 "a.h.foo4 (at) 4.0::IFoo/4 N/A N/A 5 7\n" 588 "a.h.foo5 (at) 5.0::IFoo/5 N/A N/A 6 8\n" 589 "a.h.foo6 (at) 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: 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 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 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 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 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 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 663 int main(int argc, char **argv) { 664 ::testing::InitGoogleMock(&argc, argv); 665 return RUN_ALL_TESTS(); 666 } 667