Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0639471d authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge changes from topic "lshal_improve"

* changes:
  lshal: arch unknown => ?
  lshal: Add VINTF column
  lshal: refactor: Use vintf::Arch instead of enum Architecture
  lshal: refactor: Use vintf::Transport instead of string
  lshal: Released column is now Y/N/?
parents 381ee9c8 430f8980
Loading
Loading
Loading
Loading
+90 −30
Original line number Diff line number Diff line
@@ -126,6 +126,67 @@ Partition ListCommand::resolvePartition(Partition process, const FqInstance& fqI
    return process;
}

bool match(const vintf::ManifestInstance& instance, const FqInstance& fqInstance,
           vintf::TransportArch ta) {
    // For hwbinder libs, allow missing arch in manifest.
    // For passthrough libs, allow missing interface/instance in table.
    return (ta.transport == instance.transport()) &&
            (ta.transport == vintf::Transport::HWBINDER ||
             vintf::contains(instance.arch(), ta.arch)) &&
            (!fqInstance.hasInterface() || fqInstance.getInterface() == instance.interface()) &&
            (!fqInstance.hasInstance() || fqInstance.getInstance() == instance.instance());
}

bool match(const vintf::MatrixInstance& instance, const FqInstance& fqInstance,
           vintf::TransportArch /* ta */) {
    return (!fqInstance.hasInterface() || fqInstance.getInterface() == instance.interface()) &&
            (!fqInstance.hasInstance() || instance.matchInstance(fqInstance.getInstance()));
}

template <typename ObjectType>
VintfInfo getVintfInfo(const std::shared_ptr<const ObjectType>& object,
                       const FqInstance& fqInstance, vintf::TransportArch ta, VintfInfo value) {
    bool found = false;
    (void)object->forEachInstanceOfVersion(fqInstance.getPackage(), fqInstance.getVersion(),
                                           [&](const auto& instance) {
                                               found = match(instance, fqInstance, ta);
                                               return !found; // continue if not found
                                           });
    return found ? value : VINTF_INFO_EMPTY;
}

std::shared_ptr<const vintf::HalManifest> ListCommand::getDeviceManifest() const {
    return vintf::VintfObject::GetDeviceHalManifest();
}

std::shared_ptr<const vintf::CompatibilityMatrix> ListCommand::getDeviceMatrix() const {
    return vintf::VintfObject::GetDeviceCompatibilityMatrix();
}

std::shared_ptr<const vintf::HalManifest> ListCommand::getFrameworkManifest() const {
    return vintf::VintfObject::GetFrameworkHalManifest();
}

std::shared_ptr<const vintf::CompatibilityMatrix> ListCommand::getFrameworkMatrix() const {
    return vintf::VintfObject::GetFrameworkCompatibilityMatrix();
}

VintfInfo ListCommand::getVintfInfo(const std::string& fqInstanceName,
                                    vintf::TransportArch ta) const {
    FqInstance fqInstance;
    if (!fqInstance.setTo(fqInstanceName) &&
        // Ignore interface / instance for passthrough libs
        !fqInstance.setTo(splitFirst(fqInstanceName, ':').first)) {
        err() << "Warning: Cannot parse '" << fqInstanceName << "'; no VINTF info." << std::endl;
        return VINTF_INFO_EMPTY;
    }

    return lshal::getVintfInfo(getDeviceManifest(), fqInstance, ta, DEVICE_MANIFEST) |
            lshal::getVintfInfo(getFrameworkManifest(), fqInstance, ta, FRAMEWORK_MANIFEST) |
            lshal::getVintfInfo(getDeviceMatrix(), fqInstance, ta, DEVICE_MATRIX) |
            lshal::getVintfInfo(getFrameworkMatrix(), fqInstance, ta, FRAMEWORK_MATRIX);
}

static bool scanBinderContext(pid_t pid,
        const std::string &contextName,
        std::function<void(const std::string&)> eachLine) {
@@ -269,6 +330,7 @@ void ListCommand::postprocess() {
        }
        for (TableEntry& entry : table) {
            entry.partition = getPartition(entry.serverPid);
            entry.vintfInfo = getVintfInfo(entry.interfaceName, {entry.transport, entry.arch});
        }
    });
    // use a double for loop here because lshal doesn't care about efficiency.
@@ -279,7 +341,7 @@ void ListCommand::postprocess() {
            continue;
        }
        for (TableEntry &interfaceEntry : mPassthroughRefTable) {
            if (interfaceEntry.arch != ARCH_UNKNOWN) {
            if (interfaceEntry.arch != vintf::Arch::ARCH_EMPTY) {
                continue;
            }
            FQName interfaceName;
@@ -330,35 +392,22 @@ bool ListCommand::addEntryWithInstance(const TableEntry& entry,
        return true; // strip out instances that is in a different partition.
    }

    vintf::Transport transport;
    vintf::Arch arch;
    if (entry.transport == "hwbinder") {
        transport = vintf::Transport::HWBINDER;
        arch = vintf::Arch::ARCH_EMPTY;
    } else if (entry.transport == "passthrough") {
        transport = vintf::Transport::PASSTHROUGH;
        switch (entry.arch) {
            case lshal::ARCH32:
                arch = vintf::Arch::ARCH_32;
                break;
            case lshal::ARCH64:
                arch = vintf::Arch::ARCH_64;
                break;
            case lshal::ARCH_BOTH:
                arch = vintf::Arch::ARCH_32_64;
                break;
            case lshal::ARCH_UNKNOWN: // fallthrough
            default:
    if (entry.transport == vintf::Transport::HWBINDER) {
        arch = vintf::Arch::ARCH_EMPTY; // no need to specify arch in manifest
    } else if (entry.transport == vintf::Transport::PASSTHROUGH) {
        if (entry.arch == vintf::Arch::ARCH_EMPTY) {
            err() << "Warning: '" << entry.interfaceName << "' doesn't have bitness info.";
            return false;
        }
        arch = entry.arch;
    } else {
        err() << "Warning: '" << entry.transport << "' is not a valid transport." << std::endl;
        return false;
    }

    std::string e;
    if (!manifest->insertInstance(fqInstance, transport, arch, vintf::HalFormat::HIDL, &e)) {
    if (!manifest->insertInstance(fqInstance, entry.transport, arch, vintf::HalFormat::HIDL, &e)) {
        err() << "Warning: Cannot insert '" << fqInstance.string() << ": " << e << std::endl;
        return false;
    }
@@ -440,15 +489,15 @@ std::string ListCommand::INIT_VINTF_NOTES{
    "       until they are updated.\n"
};

static Architecture fromBaseArchitecture(::android::hidl::base::V1_0::DebugInfo::Architecture a) {
static vintf::Arch fromBaseArchitecture(::android::hidl::base::V1_0::DebugInfo::Architecture a) {
    switch (a) {
        case ::android::hidl::base::V1_0::DebugInfo::Architecture::IS_64BIT:
            return ARCH64;
            return vintf::Arch::ARCH_64;
        case ::android::hidl::base::V1_0::DebugInfo::Architecture::IS_32BIT:
            return ARCH32;
            return vintf::Arch::ARCH_32;
        case ::android::hidl::base::V1_0::DebugInfo::Architecture::UNKNOWN: // fallthrough
        default:
            return ARCH_UNKNOWN;
            return vintf::Arch::ARCH_EMPTY;
    }
}

@@ -534,7 +583,7 @@ Status ListCommand::fetchAllLibraries(const sp<IServiceManager> &manager) {
                    std::string{info.instanceName.c_str()};
            entries.emplace(interfaceName, TableEntry{
                .interfaceName = interfaceName,
                .transport = "passthrough",
                .transport = vintf::Transport::PASSTHROUGH,
                .clientPids = info.clientPids,
            }).first->second.arch |= fromBaseArchitecture(info.arch);
        }
@@ -566,7 +615,7 @@ Status ListCommand::fetchPassthrough(const sp<IServiceManager> &manager) {
                .interfaceName =
                        std::string{info.interfaceName.c_str()} + "/" +
                        std::string{info.instanceName.c_str()},
                .transport = "passthrough",
                .transport = vintf::Transport::PASSTHROUGH,
                .serverPid = info.clientPids.size() == 1 ? info.clientPids[0] : NO_PID,
                .clientPids = info.clientPids,
                .arch = fromBaseArchitecture(info.arch)
@@ -582,9 +631,11 @@ Status ListCommand::fetchPassthrough(const sp<IServiceManager> &manager) {
}

Status ListCommand::fetchBinderized(const sp<IServiceManager> &manager) {
    using vintf::operator<<;

    if (!shouldReportHalType(HalType::BINDERIZED_SERVICES)) { return OK; }

    const std::string mode = "hwbinder";
    const vintf::Transport mode = vintf::Transport::HWBINDER;
    hidl_vec<hidl_string> fqInstanceNames;
    // copying out for timeoutIPC
    auto listRet = timeoutIPC(manager, &IServiceManager::list, [&] (const auto &names) {
@@ -748,7 +799,7 @@ void ListCommand::registerAllOptions() {
    mOptions.push_back({'l', "released", no_argument, v++, [](ListCommand* thiz, const char*) {
        thiz->mSelectedColumns.push_back(TableColumnType::RELEASED);
        return OK;
    }, "print the 'is released?' column\n(Y=released, empty=unreleased or unknown)"});
    }, "print the 'is released?' column\n(Y=released, N=unreleased, ?=unknown)"});
    mOptions.push_back({'t', "transport", no_argument, v++, [](ListCommand* thiz, const char*) {
        thiz->mSelectedColumns.push_back(TableColumnType::TRANSPORT);
        return OK;
@@ -788,6 +839,15 @@ void ListCommand::registerAllOptions() {
    }, "Emit debug info from\nIBase::debug with empty options. Cannot be used with --neat.\n"
        "Writes to specified file if 'arg' is provided, otherwise stdout."});

    mOptions.push_back({'V', "vintf", no_argument, v++, [](ListCommand* thiz, const char*) {
        thiz->mSelectedColumns.push_back(TableColumnType::VINTF);
        return OK;
    }, "print VINTF info. This column contains a comma-separated list of:\n"
       "    - DM: device manifest\n"
       "    - DC: device compatibility matrix\n"
       "    - FM: framework manifest\n"
       "    - FC: framework compatibility matrix"});

    // long options without short alternatives
    mOptions.push_back({'\0', "init-vintf", no_argument, v++, [](ListCommand* thiz, const char* arg) {
        thiz->mVintf = true;
+10 −0
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@
#include <android/hidl/manager/1.0/IServiceManager.h>
#include <hidl-util/FqInstance.h>
#include <vintf/HalManifest.h>
#include <vintf/VintfObject.h>

#include "Command.h"
#include "NullableOStream.h"
@@ -87,7 +88,9 @@ public:

protected:
    Status parseArgs(const Arg &arg);
    // Retrieve first-hand information
    Status fetch();
    // Retrieve derived information base on existing table
    virtual void postprocess();
    Status dump();
    void putEntry(TableEntrySource source, TableEntry &&entry);
@@ -122,6 +125,13 @@ protected:
    virtual Partition getPartition(pid_t pid);
    Partition resolvePartition(Partition processPartition, const FqInstance &fqInstance) const;

    VintfInfo getVintfInfo(const std::string &fqInstanceName, vintf::TransportArch ta) const;
    // Allow to mock these functions for testing.
    virtual std::shared_ptr<const vintf::HalManifest> getDeviceManifest() const;
    virtual std::shared_ptr<const vintf::CompatibilityMatrix> getDeviceMatrix() const;
    virtual std::shared_ptr<const vintf::HalManifest> getFrameworkManifest() const;
    virtual std::shared_ptr<const vintf::CompatibilityMatrix> getFrameworkMatrix() const;

    void forEachTable(const std::function<void(Table &)> &f);
    void forEachTable(const std::function<void(const Table &)> &f) const;

+37 −9
Original line number Diff line number Diff line
@@ -16,7 +16,11 @@
#define LOG_TAG "lshal"
#include <android-base/logging.h>

#include <map>

#include <android-base/strings.h>
#include <hidl-hash/Hash.h>
#include <vintf/parse_string.h>

#include "TableEntry.h"

@@ -26,19 +30,19 @@
namespace android {
namespace lshal {

static const std::string &getArchString(Architecture arch) {
static const std::string &getArchString(vintf::Arch arch) {
    static const std::string sStr64 = "64";
    static const std::string sStr32 = "32";
    static const std::string sStrBoth = "32+64";
    static const std::string sStrUnknown = "";
    static const std::string sStrUnknown = "?";
    switch (arch) {
        case ARCH64:
        case vintf::Arch::ARCH_64:
            return sStr64;
        case ARCH32:
        case vintf::Arch::ARCH_32:
            return sStr32;
        case ARCH_BOTH:
        case vintf::Arch::ARCH_32_64:
            return sStrBoth;
        case ARCH_UNKNOWN: // fall through
        case vintf::Arch::ARCH_EMPTY: // fall through
        default:
            return sStrUnknown;
    }
@@ -57,6 +61,7 @@ static std::string getTitle(TableColumnType type) {
        case TableColumnType::THREADS:          return "Thread Use";
        case TableColumnType::RELEASED:         return "R";
        case TableColumnType::HASH:             return "Hash";
        case TableColumnType::VINTF:            return "VINTF";
        default:
            LOG(FATAL) << __func__ << "Should not reach here. " << static_cast<int>(type);
            return "";
@@ -68,7 +73,7 @@ std::string TableEntry::getField(TableColumnType type) const {
        case TableColumnType::INTERFACE_NAME:
            return interfaceName;
        case TableColumnType::TRANSPORT:
            return transport;
            return vintf::to_string(transport);
        case TableColumnType::SERVER_PID:
            return serverPid == NO_PID ? "N/A" : std::to_string(serverPid);
        case TableColumnType::SERVER_CMD:
@@ -87,6 +92,8 @@ std::string TableEntry::getField(TableColumnType type) const {
            return isReleased();
        case TableColumnType::HASH:
            return hash;
        case TableColumnType::VINTF:
            return getVintfInfo();
        default:
            LOG(FATAL) << __func__ << "Should not reach here. " << static_cast<int>(type);
            return "";
@@ -96,12 +103,32 @@ std::string TableEntry::getField(TableColumnType type) const {
std::string TableEntry::isReleased() const {
    static const std::string unreleased = Hash::hexString(Hash::kEmptyHash);

    if (hash.empty() || hash == unreleased) {
        return " "; // unknown or unreleased
    if (hash.empty()) {
        return "?";
    }
    if (hash == unreleased) {
        return "N"; // unknown or unreleased
    }
    return "Y"; // released
}

std::string TableEntry::getVintfInfo() const {
    static const std::map<VintfInfo, std::string> values{
            {DEVICE_MANIFEST, "DM"},
            {DEVICE_MATRIX, "DC"},
            {FRAMEWORK_MANIFEST, "FM"},
            {FRAMEWORK_MATRIX, "FC"},
    };
    std::vector<std::string> ret;
    for (const auto& pair : values) {
        if (vintfInfo & pair.first) {
            ret.push_back(pair.second);
        }
    }
    auto joined = base::Join(ret, ',');
    return joined.empty() ? "X" : joined;
}

TextTable Table::createTextTable(bool neat,
    const std::function<std::string(const std::string&)>& emitDebugInfo) const {

@@ -152,6 +179,7 @@ bool TableEntry::operator==(const TableEntry& other) const {
}

std::string TableEntry::to_string() const {
    using vintf::operator<<;
    std::stringstream ss;
    ss << "name=" << interfaceName << ";transport=" << transport << ";thread=" << getThreadUsage()
       << ";server=" << serverPid
+17 −10
Original line number Diff line number Diff line
@@ -24,6 +24,8 @@
#include <iostream>

#include <procpartition/procpartition.h>
#include <vintf/Arch.h>
#include <vintf/Transport.h>

#include "TextTable.h"

@@ -40,14 +42,6 @@ enum : unsigned int {
};
using TableEntrySource = unsigned int;

enum : unsigned int {
    ARCH_UNKNOWN = 0,
    ARCH32       = 1 << 0,
    ARCH64       = 1 << 1,
    ARCH_BOTH    = ARCH32 | ARCH64
};
using Architecture = unsigned int;

enum class TableColumnType : unsigned int {
    INTERFACE_NAME,
    TRANSPORT,
@@ -60,8 +54,18 @@ enum class TableColumnType : unsigned int {
    THREADS,
    RELEASED,
    HASH,
    VINTF,
};

enum : unsigned int {
    VINTF_INFO_EMPTY = 0,
    DEVICE_MANIFEST = 1 << 0,
    DEVICE_MATRIX = 1 << 1,
    FRAMEWORK_MANIFEST = 1 << 2,
    FRAMEWORK_MATRIX = 1 << 3,
};
using VintfInfo = unsigned int;

enum {
    NO_PID = -1,
    NO_PTR = 0
@@ -69,7 +73,7 @@ enum {

struct TableEntry {
    std::string interfaceName{};
    std::string transport{};
    vintf::Transport transport{vintf::Transport::EMPTY};
    int32_t serverPid{NO_PID};
    uint32_t threadUsage{0};
    uint32_t threadCount{0};
@@ -77,10 +81,11 @@ struct TableEntry {
    uint64_t serverObjectAddress{NO_PTR};
    Pids clientPids{};
    std::vector<std::string> clientCmdlines{};
    Architecture arch{ARCH_UNKNOWN};
    vintf::Arch arch{vintf::Arch::ARCH_EMPTY};
    // empty: unknown, all zeros: unreleased, otherwise: released
    std::string hash{};
    Partition partition{Partition::UNKNOWN};
    VintfInfo vintfInfo{VINTF_INFO_EMPTY};

    static bool sortByInterfaceName(const TableEntry &a, const TableEntry &b) {
        return a.interfaceName < b.interfaceName;
@@ -99,6 +104,8 @@ struct TableEntry {

    std::string isReleased() const;

    std::string getVintfInfo() const;

    std::string getField(TableColumnType type) const;

    bool operator==(const TableEntry& other) const;
+124 −19
Original line number Diff line number Diff line
@@ -44,6 +44,13 @@ using ::android::hardware::hidl_death_recipient;
using ::android::hardware::hidl_handle;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
using android::vintf::Arch;
using android::vintf::CompatibilityMatrix;
using android::vintf::gCompatibilityMatrixConverter;
using android::vintf::gHalManifestConverter;
using android::vintf::HalManifest;
using android::vintf::Transport;
using android::vintf::VintfObject;

using InstanceDebugInfo = IServiceManager::InstanceDebugInfo;

@@ -207,6 +214,11 @@ public:
    MOCK_CONST_METHOD2(getPidInfo, bool(pid_t, PidInfo*));
    MOCK_CONST_METHOD1(parseCmdline, std::string(pid_t));
    MOCK_METHOD1(getPartition, Partition(pid_t));

    MOCK_CONST_METHOD0(getDeviceManifest, std::shared_ptr<const vintf::HalManifest>());
    MOCK_CONST_METHOD0(getDeviceMatrix, std::shared_ptr<const vintf::CompatibilityMatrix>());
    MOCK_CONST_METHOD0(getFrameworkManifest, std::shared_ptr<const vintf::HalManifest>());
    MOCK_CONST_METHOD0(getFrameworkMatrix, std::shared_ptr<const vintf::CompatibilityMatrix>());
};

class ListParseArgsTest : public ::testing::Test {
@@ -335,6 +347,15 @@ public:
            });
        }));
        ON_CALL(*mockList, getPartition(_)).WillByDefault(Return(Partition::VENDOR));

        ON_CALL(*mockList, getDeviceManifest())
                .WillByDefault(Return(VintfObject::GetDeviceHalManifest()));
        ON_CALL(*mockList, getDeviceMatrix())
                .WillByDefault(Return(VintfObject::GetDeviceCompatibilityMatrix()));
        ON_CALL(*mockList, getFrameworkManifest())
                .WillByDefault(Return(VintfObject::GetFrameworkHalManifest()));
        ON_CALL(*mockList, getFrameworkMatrix())
                .WillByDefault(Return(VintfObject::GetFrameworkCompatibilityMatrix()));
    }

    void initMockServiceManager() {
@@ -389,25 +410,28 @@ TEST_F(ListTest, GetPidInfoCached) {

TEST_F(ListTest, Fetch) {
    EXPECT_EQ(0u, mockList->fetch());
    std::array<std::string, 6> transports{{"hwbinder", "hwbinder", "passthrough",
                                          "passthrough", "passthrough", "passthrough"}};
    std::array<Architecture, 6> archs{{ARCH64, ARCH64, ARCH32, ARCH32, ARCH32, ARCH32}};
    vintf::TransportArch hwbinder{Transport::HWBINDER, Arch::ARCH_64};
    vintf::TransportArch passthrough{Transport::PASSTHROUGH, Arch::ARCH_32};
    std::array<vintf::TransportArch, 6> transportArchs{{hwbinder, hwbinder, passthrough,
                                                        passthrough, passthrough, passthrough}};
    int id = 1;
    mockList->forEachTable([&](const Table& table) {
        ASSERT_EQ(2u, table.size());
        for (const auto& entry : table) {
            const auto& transport = transports[id - 1];
            auto transport = transportArchs.at(id - 1).transport;
            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,
                .serverPid = transport == Transport::HWBINDER ? id : NO_PID,
                .threadUsage =
                        transport == Transport::HWBINDER ? getPidInfoFromId(id).threadUsage : 0,
                .threadCount =
                        transport == Transport::HWBINDER ? getPidInfoFromId(id).threadCount : 0,
                .serverCmdline = {},
                .serverObjectAddress = transport == "hwbinder" ? getPtr(id) : NO_PTR,
                .serverObjectAddress = transport == Transport::HWBINDER ? getPtr(id) : NO_PTR,
                .clientPids = getClients(id),
                .clientCmdlines = {},
                .arch = archs[id - 1],
                .arch = transportArchs.at(id - 1).arch,
            };
            EXPECT_EQ(expected, entry) << expected.to_string() << " vs. " << entry.to_string();

@@ -460,18 +484,18 @@ TEST_F(ListTest, DumpDefault) {
    const std::string expected =
        "[fake description 0]\n"
        "R Interface            Thread Use Server Clients\n"
        "  a.h.foo1@1.0::IFoo/1 11/21      1      2 4\n"
        "N a.h.foo1@1.0::IFoo/1 11/21      1      2 4\n"
        "Y a.h.foo2@2.0::IFoo/2 12/22      2      3 5\n"
        "\n"
        "[fake description 1]\n"
        "R Interface            Thread Use Server Clients\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.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"
        "\n"
        "[fake description 2]\n"
        "R Interface            Thread Use Server Clients\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"
        "? 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"
        "\n";

    optind = 1; // mimic Lshal::parseArg()
@@ -484,18 +508,18 @@ TEST_F(ListTest, DumpHash) {
    const std::string expected =
        "[fake description 0]\n"
        "Interface            R Hash\n"
        "a.h.foo1@1.0::IFoo/1   0000000000000000000000000000000000000000000000000000000000000000\n"
        "a.h.foo1@1.0::IFoo/1 N 0000000000000000000000000000000000000000000000000000000000000000\n"
        "a.h.foo2@2.0::IFoo/2 Y 0202020202020202020202020202020202020202020202020202020202020202\n"
        "\n"
        "[fake description 1]\n"
        "Interface            R Hash\n"
        "a.h.foo3@3.0::IFoo/3   \n"
        "a.h.foo4@4.0::IFoo/4   \n"
        "a.h.foo3@3.0::IFoo/3 ? \n"
        "a.h.foo4@4.0::IFoo/4 ? \n"
        "\n"
        "[fake description 2]\n"
        "Interface            R Hash\n"
        "a.h.foo5@5.0::IFoo/5   \n"
        "a.h.foo6@6.0::IFoo/6   \n"
        "a.h.foo5@5.0::IFoo/5 ? \n"
        "a.h.foo6@6.0::IFoo/6 ? \n"
        "\n";

    optind = 1; // mimic Lshal::parseArg()
@@ -651,6 +675,87 @@ TEST_F(ListTest, UnknownHalType) {
    EXPECT_THAT(err.str(), HasSubstr("Unrecognized HAL type: a"));
}

TEST_F(ListTest, Vintf) {
    std::string deviceManifestXml =
            "<manifest version=\"1.0\" type=\"device\">\n"
            "    <hal>\n"
            "        <name>a.h.foo1</name>\n"
            "        <transport>hwbinder</transport>\n"
            "        <fqname>@1.0::IFoo/1</fqname>\n"
            "    </hal>\n"
            "    <hal>\n"
            "        <name>a.h.foo3</name>\n"
            "        <transport arch=\"32+64\">passthrough</transport>\n"
            "        <fqname>@3.0::IFoo/3</fqname>\n"
            "    </hal>\n"
            "</manifest>\n";
    std::string frameworkManifestXml =
            "<manifest version=\"1.0\" type=\"framework\">\n"
            "    <hal>\n"
            "        <name>a.h.foo5</name>\n"
            "        <transport arch=\"32\">passthrough</transport>\n"
            "        <fqname>@5.0::IFoo/5</fqname>\n"
            "    </hal>\n"
            "</manifest>\n";
    std::string deviceMatrixXml =
            "<compatibility-matrix version=\"1.0\" type=\"device\">\n"
            "    <hal>\n"
            "        <name>a.h.foo5</name>\n"
            "        <version>5.0</version>\n"
            "        <interface>\n"
            "            <name>IFoo</name>\n"
            "            <instance>5</instance>\n"
            "        </interface>\n"
            "    </hal>\n"
            "</compatibility-matrix>\n";
    std::string frameworkMatrixXml =
            "<compatibility-matrix version=\"1.0\" type=\"framework\">\n"
            "    <hal>\n"
            "        <name>a.h.foo1</name>\n"
            "        <version>1.0</version>\n"
            "        <interface>\n"
            "            <name>IFoo</name>\n"
            "            <instance>1</instance>\n"
            "        </interface>\n"
            "    </hal>\n"
            "    <hal>\n"
            "        <name>a.h.foo3</name>\n"
            "        <version>3.0</version>\n"
            "        <interface>\n"
            "            <name>IFoo</name>\n"
            "            <instance>3</instance>\n"
            "        </interface>\n"
            "    </hal>\n"
            "</compatibility-matrix>\n";

    std::string expected = "DM,FC a.h.foo1@1.0::IFoo/1\n"
                           "X     a.h.foo2@2.0::IFoo/2\n"
                           "DM,FC a.h.foo3@3.0::IFoo/3\n"
                           "X     a.h.foo4@4.0::IFoo/4\n"
                           "DC,FM a.h.foo5@5.0::IFoo/5\n"
                           "X     a.h.foo6@6.0::IFoo/6\n";

    auto deviceManifest = std::make_shared<HalManifest>();
    auto frameworkManifest = std::make_shared<HalManifest>();
    auto deviceMatrix = std::make_shared<CompatibilityMatrix>();
    auto frameworkMatrix = std::make_shared<CompatibilityMatrix>();

    ASSERT_TRUE(gHalManifestConverter(deviceManifest.get(), deviceManifestXml));
    ASSERT_TRUE(gHalManifestConverter(frameworkManifest.get(), frameworkManifestXml));
    ASSERT_TRUE(gCompatibilityMatrixConverter(deviceMatrix.get(), deviceMatrixXml));
    ASSERT_TRUE(gCompatibilityMatrixConverter(frameworkMatrix.get(), frameworkMatrixXml));

    ON_CALL(*mockList, getDeviceManifest()).WillByDefault(Return(deviceManifest));
    ON_CALL(*mockList, getDeviceMatrix()).WillByDefault(Return(deviceMatrix));
    ON_CALL(*mockList, getFrameworkManifest()).WillByDefault(Return(frameworkManifest));
    ON_CALL(*mockList, getFrameworkMatrix()).WillByDefault(Return(frameworkMatrix));

    optind = 1; // mimic Lshal::parseArg()
    EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-Vi", "--neat"})));
    EXPECT_THAT(out.str(), HasSubstr(expected));
    EXPECT_EQ("", err.str());
}

class HelpTest : public ::testing::Test {
public:
    void SetUp() override {
+1 −1

File changed.

Contains only whitespace changes.

Loading