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

Commit 0ec8d63d authored by Tomasz Wasilczyk's avatar Tomasz Wasilczyk
Browse files

Test CAN bus HAL

Bug: 135918744
Test: VTS
Change-Id: I7b45a24b6a1142cf74c1d215658792835553d169
parent 1b2c6efe
Loading
Loading
Loading
Loading
+47 −0
Original line number Diff line number Diff line
//
// Copyright (C) 2019 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

cc_defaults {
    name: "android.hardware.automotive.can@vts-defaults",
    defaults: ["VtsHalTargetTestDefaults", "android.hardware.automotive.can@defaults"],
    header_libs: [
        "android.hardware.automotive.can@hidl-utils-lib",
        "android.hardware.automotive.can@vts-utils-lib",
    ],
    static_libs: [
        "android.hardware.automotive.can@1.0",
        "libgmock",
    ],
    test_suites: ["general-tests"],
}

cc_test {
    name: "VtsHalCanControllerV1_0TargetTest",
    defaults: ["android.hardware.automotive.can@vts-defaults"],
    srcs: ["VtsHalCanControllerV1_0TargetTest.cpp"],
}

cc_test {
    name: "VtsHalCanBusV1_0TargetTest",
    defaults: ["android.hardware.automotive.can@vts-defaults"],
    srcs: ["VtsHalCanBusV1_0TargetTest.cpp"],
}

cc_test {
    name: "VtsHalCanBusVirtualV1_0TargetTest",
    defaults: ["android.hardware.automotive.can@vts-defaults"],
    srcs: ["VtsHalCanBusVirtualV1_0TargetTest.cpp"],
}
+168 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <VtsHalHidlTargetTestBase.h>
#include <android-base/logging.h>
#include <android-base/strings.h>
#include <android/hardware/automotive/can/1.0/ICanBus.h>
#include <android/hardware/automotive/can/1.0/types.h>
#include <can-vts-utils/can-hal-printers.h>
#include <can-vts-utils/environment-utils.h>
#include <gmock/gmock.h>
#include <hidl-utils/hidl-utils.h>

namespace android {
namespace hardware {
namespace automotive {
namespace can {
namespace V1_0 {
namespace vts {

using hardware::hidl_vec;

static utils::SimpleHidlEnvironment<ICanBus>* gEnv = nullptr;

struct CanMessageListener : public can::V1_0::ICanMessageListener {
    virtual Return<void> onReceive(const can::V1_0::CanMessage&) { return {}; }
    virtual Return<void> onError(can::V1_0::ErrorEvent) { return {}; }
};

class CanBusHalTest : public ::testing::VtsHalHidlTargetTestBase {
  protected:
    virtual void SetUp() override;
    virtual void TearDown() override;

    std::tuple<Result, sp<ICloseHandle>> listen(const hidl_vec<CanMessageFilter>& filter,
                                                const sp<ICanMessageListener>& listener);

    sp<ICanBus> mCanBus;
};

void CanBusHalTest::SetUp() {
    const auto serviceName = gEnv->getServiceName<ICanBus>();
    mCanBus = getService<ICanBus>(serviceName);
    ASSERT_TRUE(mCanBus) << "Couldn't open CAN Bus: " << serviceName;
}

void CanBusHalTest::TearDown() {
    mCanBus.clear();
}

std::tuple<Result, sp<ICloseHandle>> CanBusHalTest::listen(
        const hidl_vec<CanMessageFilter>& filter, const sp<ICanMessageListener>& listener) {
    Result halResult;
    sp<ICloseHandle> closeHandle;
    mCanBus->listen(filter, listener, hidl_utils::fill(&halResult, &closeHandle)).assertOk();

    return {halResult, closeHandle};
}

TEST_F(CanBusHalTest, SendNoPayload) {
    CanMessage msg = {};
    msg.id = 0x123;

    const auto result = mCanBus->send(msg);
    ASSERT_EQ(Result::OK, result);
}

TEST_F(CanBusHalTest, Send8B) {
    CanMessage msg = {};
    msg.id = 0x234;
    msg.payload = {1, 2, 3, 4, 5, 6, 7, 8};

    const auto result = mCanBus->send(msg);
    ASSERT_EQ(Result::OK, result);
}

TEST_F(CanBusHalTest, SendZeroId) {
    CanMessage msg = {};
    msg.payload = {1, 2, 3};

    const auto result = mCanBus->send(msg);
    ASSERT_EQ(Result::OK, result);
}

TEST_F(CanBusHalTest, SendTooLong) {
    CanMessage msg = {};
    msg.id = 0x123;
    msg.payload = hidl_vec<uint8_t>(102400);  // 100kiB

    const auto result = mCanBus->send(msg);
    ASSERT_EQ(Result::PAYLOAD_TOO_LONG, result);
}

TEST_F(CanBusHalTest, ListenNoFilter) {
    const auto [result, closeHandle] = listen({}, new CanMessageListener());
    ASSERT_EQ(Result::OK, result);

    closeHandle->close().assertOk();
}

TEST_F(CanBusHalTest, ListenSomeFilter) {
    hidl_vec<CanMessageFilter> filters = {
            {0x123, 0x1FF, false},
            {0x001, 0x00F, true},
            {0x200, 0x100, false},
    };

    const auto [result, closeHandle] = listen(filters, new CanMessageListener());
    ASSERT_EQ(Result::OK, result);

    closeHandle->close().assertOk();
}

TEST_F(CanBusHalTest, ListenNull) {
    const auto [result, closeHandle] = listen({}, nullptr);
    ASSERT_EQ(Result::INVALID_ARGUMENTS, result);
}

TEST_F(CanBusHalTest, DoubleCloseListen) {
    const auto [result, closeHandle] = listen({}, new CanMessageListener());
    ASSERT_EQ(Result::OK, result);

    closeHandle->close().assertOk();
    closeHandle->close().assertOk();
}

TEST_F(CanBusHalTest, DontCloseListen) {
    const auto [result, closeHandle] = listen({}, new CanMessageListener());
    ASSERT_EQ(Result::OK, result);
}

}  // namespace vts
}  // namespace V1_0
}  // namespace can
}  // namespace automotive
}  // namespace hardware
}  // namespace android

/**
 * Example manual invocation:
 * adb shell /data/nativetest64/VtsHalCanBusV1_0TargetTest/VtsHalCanBusV1_0TargetTest \
 *     --hal_service_instance=android.hardware.automotive.can@1.0::ICanBus/test
 */
int main(int argc, char** argv) {
    using android::hardware::automotive::can::V1_0::ICanBus;
    using android::hardware::automotive::can::V1_0::vts::gEnv;
    using android::hardware::automotive::can::V1_0::vts::utils::SimpleHidlEnvironment;
    android::base::SetDefaultTag("CanBusVts");
    android::base::SetMinimumLogSeverity(android::base::VERBOSE);
    gEnv = new SimpleHidlEnvironment<ICanBus>;
    ::testing::AddGlobalTestEnvironment(gEnv);
    ::testing::InitGoogleTest(&argc, argv);
    gEnv->init(&argc, argv);
    return RUN_ALL_TESTS();
}
+322 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <VtsHalHidlTargetTestBase.h>
#include <android-base/logging.h>
#include <android-base/strings.h>
#include <android/hardware/automotive/can/1.0/ICanBus.h>
#include <android/hardware/automotive/can/1.0/ICanController.h>
#include <android/hardware/automotive/can/1.0/types.h>
#include <android/hidl/manager/1.2/IServiceManager.h>
#include <can-vts-utils/can-hal-printers.h>
#include <can-vts-utils/environment-utils.h>
#include <gmock/gmock.h>
#include <hidl-utils/hidl-utils.h>
#include <utils/Mutex.h>
#include <utils/SystemClock.h>

#include <chrono>
#include <thread>

namespace android {
namespace hardware {
namespace automotive {
namespace can {
namespace V1_0 {
namespace vts {

using namespace std::chrono_literals;

using hardware::hidl_vec;
using InterfaceType = ICanController::InterfaceType;

static utils::SimpleHidlEnvironment<ICanController>* gEnv = nullptr;

struct CanMessageListener : public can::V1_0::ICanMessageListener {
    DISALLOW_COPY_AND_ASSIGN(CanMessageListener);

    CanMessageListener() {}

    virtual Return<void> onReceive(const can::V1_0::CanMessage& msg) {
        std::unique_lock<std::mutex> lk(mMessagesGuard);
        mMessages.push_back(msg);
        mMessagesUpdated.notify_one();
        return {};
    }

    virtual Return<void> onError(can::V1_0::ErrorEvent event) {
        EXPECT_TRUE(false) << "Got error: " << event;
        return {};
    }

    virtual ~CanMessageListener() { mCloseHandle->close(); }

    void assignCloseHandle(sp<ICloseHandle> closeHandle) {
        EXPECT_TRUE(closeHandle);
        EXPECT_FALSE(mCloseHandle);
        mCloseHandle = closeHandle;
    }

    std::vector<can::V1_0::CanMessage> fetchMessages(std::chrono::milliseconds timeout,
                                                     unsigned atLeast = 1) {
        std::unique_lock<std::mutex> lk(mMessagesGuard);
        mMessagesUpdated.wait_for(lk, timeout, [&] { return mMessages.size() >= atLeast; });
        const auto messages = mMessages;
        mMessages.clear();
        return messages;
    }

  private:
    sp<ICloseHandle> mCloseHandle;

    std::mutex mMessagesGuard;
    std::condition_variable mMessagesUpdated GUARDED_BY(mMessagesGuard);
    std::vector<can::V1_0::CanMessage> mMessages GUARDED_BY(mMessagesGuard);
};

struct Bus {
    DISALLOW_COPY_AND_ASSIGN(Bus);

    Bus(sp<ICanController> controller, const ICanController::BusConfiguration& config)
        : mIfname(config.name), mController(controller) {
        const auto result = controller->upInterface(config);
        EXPECT_EQ(ICanController::Result::OK, result);

        /* Not using ICanBus::getService here, since it ignores interfaces not in the manifest
         * file -- this is a test, so we don't want to add dummy services to a device manifest.
         */
        auto manager = hidl::manager::V1_2::IServiceManager::getService();
        auto service = manager->get(ICanBus::descriptor, config.name);
        mBus = ICanBus::castFrom(service);
        EXPECT_TRUE(mBus) << "ICanBus/" << config.name << " failed to register";
    }

    virtual ~Bus() { reset(); }

    void reset() {
        mBus.clear();
        if (mController) {
            const auto res = mController->downInterface(mIfname);
            EXPECT_TRUE(res);
            mController.clear();
        }
    }

    ICanBus* operator->() const { return mBus.get(); }
    sp<ICanBus> get() { return mBus; }

    sp<CanMessageListener> listen(const hidl_vec<CanMessageFilter>& filter) {
        sp<CanMessageListener> listener = new CanMessageListener();

        Result result;
        sp<ICloseHandle> closeHandle;
        mBus->listen(filter, listener, hidl_utils::fill(&result, &closeHandle)).assertOk();
        EXPECT_EQ(Result::OK, result);
        listener->assignCloseHandle(closeHandle);

        return listener;
    }

    void send(const CanMessage& msg) {
        const auto result = mBus->send(msg);
        EXPECT_EQ(Result::OK, result);
    }

  private:
    const std::string mIfname;
    sp<ICanController> mController;
    sp<ICanBus> mBus;
};

class CanBusVirtualHalTest : public ::testing::VtsHalHidlTargetTestBase {
  protected:
    virtual void SetUp() override;

    static void SetUpTestCase();
    static void TearDownTestCase();

    Bus makeBus();

  private:
    unsigned mLastIface = 0;
    static sp<ICanController> mCanController;
    static bool mVirtualSupported;
};

sp<ICanController> CanBusVirtualHalTest::mCanController = nullptr;
bool CanBusVirtualHalTest::mVirtualSupported;

static CanMessage makeMessage(CanMessageId id) {
    CanMessage msg = {};
    msg.id = id;
    return msg;
}

static void clearTimestamps(std::vector<CanMessage>& messages) {
    std::for_each(messages.begin(), messages.end(), [](auto& msg) { msg.timestamp = 0; });
}

void CanBusVirtualHalTest::SetUp() {
    if (!mVirtualSupported) GTEST_SKIP();
}

void CanBusVirtualHalTest::SetUpTestCase() {
    const auto serviceName = gEnv->getServiceName<ICanController>();
    mCanController = getService<ICanController>(serviceName);
    ASSERT_TRUE(mCanController) << "Couldn't open CAN Controller: " << serviceName;

    hidl_vec<InterfaceType> supported;
    mCanController->getSupportedInterfaceTypes(hidl_utils::fill(&supported)).assertOk();
    mVirtualSupported = supported.contains(InterfaceType::VIRTUAL);
}

void CanBusVirtualHalTest::TearDownTestCase() {
    mCanController.clear();
}

Bus CanBusVirtualHalTest::makeBus() {
    const auto idx = ++mLastIface;

    ICanController::BusConfiguration config = {};
    config.name = "test" + std::to_string(idx);
    config.iftype = InterfaceType::VIRTUAL;
    config.interfaceId.address("vcan50");

    return Bus(mCanController, config);
}

TEST_F(CanBusVirtualHalTest, Send) {
    auto bus = makeBus();

    CanMessage msg = {};
    msg.id = 0x123;
    msg.payload = {1, 2, 3};

    bus.send(msg);
}

TEST_F(CanBusVirtualHalTest, SendAfterClose) {
    auto bus = makeBus();
    auto zombie = bus.get();
    bus.reset();

    const auto result = zombie->send({});
    ASSERT_EQ(Result::INTERFACE_DOWN, result);
}

TEST_F(CanBusVirtualHalTest, SendAndRecv) {
    auto bus1 = makeBus();
    auto bus2 = makeBus();

    auto listener = bus2.listen({});

    CanMessage msg = {};
    msg.id = 0x123;
    msg.payload = {1, 2, 3};
    bus1.send(msg);

    auto messages = listener->fetchMessages(100ms);
    ASSERT_EQ(1u, messages.size());
    ASSERT_NEAR(uint64_t(elapsedRealtimeNano()), messages[0].timestamp,
                std::chrono::nanoseconds(100ms).count());
    clearTimestamps(messages);
    ASSERT_EQ(msg, messages[0]);
}

TEST_F(CanBusVirtualHalTest, DownOneOfTwo) {
    auto bus1 = makeBus();
    auto bus2 = makeBus();

    bus2.reset();

    bus1.send({});
}

TEST_F(CanBusVirtualHalTest, Filter) {
    auto bus1 = makeBus();
    auto bus2 = makeBus();

    hidl_vec<CanMessageFilter> filterPositive = {
            {0x101, 0x100, false},
            {0x010, 0x0F0, false},
    };
    auto listenerPositive = bus2.listen(filterPositive);

    hidl_vec<CanMessageFilter> filterNegative = {
            {0x123, 0x0FF, true},
            {0x004, 0x00F, true},
    };
    auto listenerNegative = bus2.listen(filterNegative);

    bus1.send(makeMessage(0));
    bus1.send(makeMessage(0x1A0));
    bus1.send(makeMessage(0x1A1));
    bus1.send(makeMessage(0x2A0));
    bus1.send(makeMessage(0x3A0));
    bus1.send(makeMessage(0x010));
    bus1.send(makeMessage(0x123));
    bus1.send(makeMessage(0x023));
    bus1.send(makeMessage(0x124));

    std::vector<can::V1_0::CanMessage> expectedPositive{
            makeMessage(0x1A0),  //
            makeMessage(0x1A1),  //
            makeMessage(0x3A0),  //
            makeMessage(0x010),  //
            makeMessage(0x123),  //
            makeMessage(0x124),  //
    };
    std::vector<can::V1_0::CanMessage> expectedNegative{
            makeMessage(0),      //
            makeMessage(0x1A0),  //
            makeMessage(0x1A1),  //
            makeMessage(0x2A0),  //
            makeMessage(0x3A0),  //
            makeMessage(0x010),  //
    };

    auto messagesNegative = listenerNegative->fetchMessages(100ms, expectedNegative.size());
    auto messagesPositive = listenerPositive->fetchMessages(100ms, expectedPositive.size());
    clearTimestamps(messagesNegative);
    clearTimestamps(messagesPositive);
    ASSERT_EQ(expectedNegative, messagesNegative);
    ASSERT_EQ(expectedPositive, messagesPositive);
}

}  // namespace vts
}  // namespace V1_0
}  // namespace can
}  // namespace automotive
}  // namespace hardware
}  // namespace android

/**
 * Example manual invocation:
 * adb shell /data/nativetest64/VtsHalCanBusVirtualV1_0TargetTest/VtsHalCanBusVirtualV1_0TargetTest\
 *     --hal_service_instance=android.hardware.automotive.can@1.0::ICanController/socketcan
 */
int main(int argc, char** argv) {
    using android::hardware::automotive::can::V1_0::ICanController;
    using android::hardware::automotive::can::V1_0::vts::gEnv;
    using android::hardware::automotive::can::V1_0::vts::utils::SimpleHidlEnvironment;
    android::base::SetDefaultTag("CanBusVirtualVts");
    android::base::SetMinimumLogSeverity(android::base::VERBOSE);
    gEnv = new SimpleHidlEnvironment<ICanController>;
    ::testing::AddGlobalTestEnvironment(gEnv);
    ::testing::InitGoogleTest(&argc, argv);
    gEnv->init(&argc, argv);
    return RUN_ALL_TESTS();
}
+260 −0

File added.

Preview size limit exceeded, changes collapsed.

+20 −0
Original line number Diff line number Diff line
//
// Copyright (C) 2019 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

cc_library_headers {
    name: "android.hardware.automotive.can@vts-utils-lib",
    export_include_dirs: ["include"],
}
Loading