blob: a26be99006771af6b3a8bfecbd60d60eab4e533e [file] [log] [blame]
// 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 <aidl/android/automotive/computepipe/registry/BnClientInfo.h>
#include <aidl/android/automotive/computepipe/registry/IPipeQuery.h>
#include <aidl/android/automotive/computepipe/registry/IPipeRegistration.h>
#include <aidl/android/automotive/computepipe/runner/BnPipeStateCallback.h>
#include <aidl/android/automotive/computepipe/runner/BnPipeStream.h>
#include <aidl/android/automotive/computepipe/runner/PipeState.h>
#include <android/binder_manager.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <utility>
#include "ConfigurationCommand.pb.h"
#include "ControlCommand.pb.h"
#include "MemHandle.h"
#include "MockMemHandle.h"
#include "Options.pb.h"
#include "runner/utils/RunnerInterface.h"
#include "runner/utils/RunnerInterfaceCallbacks.h"
#include "types/Status.h"
namespace android {
namespace automotive {
namespace computepipe {
namespace {
using ::aidl::android::automotive::computepipe::registry::BnClientInfo;
using ::aidl::android::automotive::computepipe::registry::IPipeQuery;
using ::aidl::android::automotive::computepipe::runner::BnPipeStateCallback;
using ::aidl::android::automotive::computepipe::runner::BnPipeStream;
using ::aidl::android::automotive::computepipe::runner::IPipeRunner;
using ::aidl::android::automotive::computepipe::runner::IPipeStateCallback;
using ::aidl::android::automotive::computepipe::runner::PacketDescriptor;
using ::aidl::android::automotive::computepipe::runner::PipeState;
using ::ndk::ScopedAStatus;
using ::ndk::SharedRefBase;
using ::testing::AtLeast;
using ::testing::Return;
const char kRegistryInterfaceName[] = "router";
class RunnerCallbacks {
public:
Status ControlCommandCallback(const proto::ControlCommand& command) {
mLastControlCommand = command;
return mStatus;
}
Status ConfigurationCommandCallback(const proto::ConfigurationCommand& command) {
mLastConfigurationCommand = command;
return mStatus;
}
Status ReleasePacketNotification(const std::shared_ptr<MemHandle>& packet) {
mLastPacket = packet;
return mStatus;
}
runner_utils::RunnerInterfaceCallbacks GetCallbackObject() {
using std::placeholders::_1;
std::function<Status(const proto::ControlCommand&)> controlCb =
std::bind(&RunnerCallbacks::ControlCommandCallback, this, _1);
std::function<Status(const proto::ConfigurationCommand&)> configCb =
std::bind(&RunnerCallbacks::ConfigurationCommandCallback, this, _1);
std::function<Status(const std::shared_ptr<MemHandle>&)> packetCb =
std::bind(&RunnerCallbacks::ReleasePacketNotification, this, _1);
return runner_utils::RunnerInterfaceCallbacks(controlCb, configCb, packetCb);
}
void SetReturnStatus(Status status) {
mStatus = status;
}
proto::ControlCommand mLastControlCommand;
proto::ConfigurationCommand mLastConfigurationCommand;
std::shared_ptr<MemHandle> mLastPacket = nullptr;
Status mStatus = Status::SUCCESS;
};
class StateChangeCallback : public BnPipeStateCallback {
public:
ScopedAStatus handleState(PipeState state) {
mState = state;
return ScopedAStatus::ok();
}
PipeState mState = PipeState::RESET;
};
class StreamCallback : public BnPipeStream {
public:
ScopedAStatus deliverPacket(const PacketDescriptor& in_packet) override {
data = in_packet.data;
timestamp = in_packet.sourceTimeStampMillis;
return ScopedAStatus::ok();
}
std::string data;
uint64_t timestamp;
};
class ClientInfo : public BnClientInfo {
public:
ScopedAStatus getClientId(int32_t* _aidl_return) {
if (_aidl_return) {
*_aidl_return = 0;
return ScopedAStatus::ok();
}
return ScopedAStatus::fromExceptionCode(EX_TRANSACTION_FAILED);
}
};
class ClientInterface : public ::testing::Test {
protected:
void SetUp() override {
const std::string graphName = "graph1";
proto::Options options;
options.set_graph_name(graphName);
mRunnerInterface = std::make_unique<runner_utils::RunnerInterface>(
options, mCallbacks.GetCallbackObject());
// Register the instance with router.
EXPECT_EQ(mRunnerInterface->init(), Status::SUCCESS);
// Init is not a blocking call, so sleep for 3 seconds to allow the runner to register with
// router.
sleep(3);
// Retrieve router query instance from service manager.
std::string instanceName =
std::string() + IPipeQuery::descriptor + "/" + kRegistryInterfaceName;
ndk::SpAIBinder binder(AServiceManager_getService(instanceName.c_str()));
ASSERT_TRUE(binder.get() != nullptr);
std::shared_ptr<IPipeQuery> queryService = IPipeQuery::fromBinder(binder);
// Retrieve pipe runner instance from the router.
std::shared_ptr<ClientInfo> clientInfo = ndk::SharedRefBase::make<ClientInfo>();
ASSERT_TRUE(queryService->getPipeRunner(graphName, clientInfo, &mPipeRunner).isOk());
}
RunnerCallbacks mCallbacks;
std::shared_ptr<runner_utils::RunnerInterface> mRunnerInterface = nullptr;
std::shared_ptr<IPipeRunner> mPipeRunner = nullptr;
};
TEST_F(ClientInterface, TestSetConfiguration) {
// Configure runner to return success.
mCallbacks.SetReturnStatus(Status::SUCCESS);
// Initialize pipe runner.
std::shared_ptr<StateChangeCallback> stateCallback =
ndk::SharedRefBase::make<StateChangeCallback>();
EXPECT_TRUE(mPipeRunner->init(stateCallback).isOk());
// Test that set input source returns ok status.
EXPECT_TRUE(mPipeRunner->setPipeInputSource(1).isOk());
EXPECT_EQ(mCallbacks.mLastConfigurationCommand.has_set_input_source(), true);
EXPECT_EQ(mCallbacks.mLastConfigurationCommand.set_input_source().source_id(), 1);
// Test that set offload option returns ok status.
EXPECT_TRUE(mPipeRunner->setPipeOffloadOptions(5).isOk());
EXPECT_EQ(mCallbacks.mLastConfigurationCommand.has_set_offload_offload(), true);
EXPECT_EQ(mCallbacks.mLastConfigurationCommand.set_offload_offload().offload_option_id(), 5);
// Test that set termination option returns ok status.
EXPECT_TRUE(mPipeRunner->setPipeTermination(3).isOk());
EXPECT_EQ(mCallbacks.mLastConfigurationCommand.has_set_termination_option(), true);
EXPECT_EQ(mCallbacks.mLastConfigurationCommand.set_termination_option().termination_option_id(),
3);
// Test that set output callback returns ok status.
std::shared_ptr<StreamCallback> streamCb = ndk::SharedRefBase::make<StreamCallback>();
EXPECT_TRUE(mPipeRunner->setPipeOutputConfig(0, 10, streamCb).isOk());
EXPECT_EQ(mCallbacks.mLastConfigurationCommand.has_set_output_stream(), true);
EXPECT_EQ(mCallbacks.mLastConfigurationCommand.set_output_stream().stream_id(), 0);
EXPECT_EQ(mCallbacks.mLastConfigurationCommand.set_output_stream().max_inflight_packets_count(),
10);
// Release runner here. This should remove registry entry from router registry.
mRunnerInterface.reset();
}
TEST_F(ClientInterface, TestSetConfigurationError) {
ScopedAStatus status;
// Configure runner to return error.
mCallbacks.SetReturnStatus(Status::INTERNAL_ERROR);
// Initialize pipe runner.
std::shared_ptr<StateChangeCallback> stateCallback =
ndk::SharedRefBase::make<StateChangeCallback>();
EXPECT_TRUE(mPipeRunner->init(stateCallback).isOk());
// Test that set input source returns error status.
status = mPipeRunner->setPipeInputSource(1);
EXPECT_EQ(status.getExceptionCode(), EX_TRANSACTION_FAILED);
EXPECT_EQ(mCallbacks.mLastConfigurationCommand.has_set_input_source(), true);
EXPECT_EQ(mCallbacks.mLastConfigurationCommand.set_input_source().source_id(), 1);
// Test that set offload option returns error status.
status = mPipeRunner->setPipeOffloadOptions(5);
EXPECT_EQ(status.getExceptionCode(), EX_TRANSACTION_FAILED);
EXPECT_EQ(mCallbacks.mLastConfigurationCommand.has_set_offload_offload(), true);
EXPECT_EQ(mCallbacks.mLastConfigurationCommand.set_offload_offload().offload_option_id(), 5);
// Test that set termination option returns error status.
status = mPipeRunner->setPipeTermination(3);
EXPECT_EQ(status.getExceptionCode(), EX_TRANSACTION_FAILED);
EXPECT_EQ(mCallbacks.mLastConfigurationCommand.has_set_termination_option(), true);
EXPECT_EQ(mCallbacks.mLastConfigurationCommand.set_termination_option().termination_option_id(),
3);
// Test that set output callback returns error status.
std::shared_ptr<StreamCallback> streamCb = ndk::SharedRefBase::make<StreamCallback>();
status = mPipeRunner->setPipeOutputConfig(0, 10, streamCb);
EXPECT_EQ(status.getExceptionCode(), EX_TRANSACTION_FAILED);
EXPECT_EQ(mCallbacks.mLastConfigurationCommand.has_set_output_stream(), true);
EXPECT_EQ(mCallbacks.mLastConfigurationCommand.set_output_stream().stream_id(), 0);
EXPECT_EQ(mCallbacks.mLastConfigurationCommand.set_output_stream().max_inflight_packets_count(),
10);
// Release runner here. This should remove registry entry from router registry.
mRunnerInterface.reset();
}
TEST_F(ClientInterface, TestControlCommands) {
// Configure runner to return success.
mCallbacks.SetReturnStatus(Status::SUCCESS);
// Initialize pipe runner.
std::shared_ptr<StateChangeCallback> stateCallback =
ndk::SharedRefBase::make<StateChangeCallback>();
EXPECT_TRUE(mPipeRunner->init(stateCallback).isOk());
// Test that apply-configs api returns ok status.
EXPECT_TRUE(mPipeRunner->applyPipeConfigs().isOk());
EXPECT_EQ(mCallbacks.mLastControlCommand.has_apply_configs(), true);
// Test that set start graph api returns ok status.
EXPECT_TRUE(mPipeRunner->startPipe().isOk());
EXPECT_EQ(mCallbacks.mLastControlCommand.has_start_graph(), true);
// Test that set stop graph api returns ok status.
EXPECT_TRUE(mPipeRunner->stopPipe().isOk());
EXPECT_EQ(mCallbacks.mLastControlCommand.has_stop_graph(), true);
// Release runner here. This should remove registry entry from router registry.
mRunnerInterface.reset();
}
TEST_F(ClientInterface, TestControlCommandsFailure) {
ScopedAStatus status;
// Configure runner to return error status.
mCallbacks.SetReturnStatus(Status::INTERNAL_ERROR);
// Initialize pipe runner.
std::shared_ptr<StateChangeCallback> stateCallback =
ndk::SharedRefBase::make<StateChangeCallback>();
EXPECT_TRUE(mPipeRunner->init(stateCallback).isOk());
// Test that apply-configs api returns error status.
status = mPipeRunner->applyPipeConfigs();
EXPECT_EQ(status.getExceptionCode(), EX_TRANSACTION_FAILED);
EXPECT_EQ(mCallbacks.mLastControlCommand.has_apply_configs(), true);
// Test that start graph api returns error status.
status = mPipeRunner->startPipe();
EXPECT_EQ(status.getExceptionCode(), EX_TRANSACTION_FAILED);
EXPECT_EQ(mCallbacks.mLastControlCommand.has_start_graph(), true);
// Test that stop graph api returns error status.
status = mPipeRunner->stopPipe();
EXPECT_EQ(status.getExceptionCode(), EX_TRANSACTION_FAILED);
EXPECT_EQ(mCallbacks.mLastControlCommand.has_stop_graph(), true);
// Release runner here. This should remove registry entry from router registry.
mRunnerInterface.reset();
}
TEST_F(ClientInterface, TestFailureWithoutInit) {
mCallbacks.SetReturnStatus(Status::SUCCESS);
// Pipe runner is not initalized here, test that a configuration command returns error status.
ScopedAStatus status;
status = mPipeRunner->setPipeInputSource(1);
EXPECT_EQ(status.getExceptionCode(), EX_ILLEGAL_STATE);
EXPECT_EQ(mCallbacks.mLastConfigurationCommand.has_set_input_source(), false);
// Test that a control command returns error status.
status = mPipeRunner->applyPipeConfigs();
EXPECT_EQ(status.getExceptionCode(), EX_ILLEGAL_STATE);
EXPECT_EQ(mCallbacks.mLastControlCommand.has_apply_configs(), false);
}
TEST_F(ClientInterface, TestStateChangeNotification) {
// Create RunnerInterface instance.
mCallbacks.SetReturnStatus(Status::SUCCESS);
// Initialize pipe runner.
std::shared_ptr<StateChangeCallback> stateCallback =
ndk::SharedRefBase::make<StateChangeCallback>();
EXPECT_TRUE(mPipeRunner->init(stateCallback).isOk());
// Test that when runner interface is notified of a new state, client callback is invoked with
// correct state.
GraphState state = RUNNING;
EXPECT_EQ(mRunnerInterface->stateUpdateNotification(state), Status::SUCCESS);
EXPECT_EQ(stateCallback->mState, PipeState::RUNNING);
}
TEST_F(ClientInterface, TestPacketDelivery) {
// Configure runner to return success state.
mCallbacks.SetReturnStatus(Status::SUCCESS);
// Initialize pipe runner.
std::shared_ptr<StateChangeCallback> stateCallback =
ndk::SharedRefBase::make<StateChangeCallback>();
EXPECT_TRUE(mPipeRunner->init(stateCallback).isOk());
// Set callback for stream id 0.
std::shared_ptr<StreamCallback> streamCb = ndk::SharedRefBase::make<StreamCallback>();
EXPECT_TRUE(mPipeRunner->setPipeOutputConfig(0, 10, streamCb).isOk());
EXPECT_EQ(mCallbacks.mLastConfigurationCommand.has_set_output_stream(), true);
EXPECT_EQ(mCallbacks.mLastConfigurationCommand.set_output_stream().stream_id(), 0);
EXPECT_EQ(mCallbacks.mLastConfigurationCommand.set_output_stream().max_inflight_packets_count(),
10);
// Send a packet to client and verify the packet.
std::shared_ptr<tests::MockMemHandle> packet = std::make_unique<tests::MockMemHandle>();
uint64_t timestamp = 100;
const std::string testData = "Test String.";
EXPECT_CALL(*packet, getType()).Times(AtLeast(1))
.WillRepeatedly(Return(proto::PacketType::SEMANTIC_DATA));
EXPECT_CALL(*packet, getTimeStamp()).Times(AtLeast(1))
.WillRepeatedly(Return(timestamp));
EXPECT_CALL(*packet, getSize()).Times(AtLeast(1))
.WillRepeatedly(Return(testData.size()));
EXPECT_CALL(*packet, getData()).Times(AtLeast(1))
.WillRepeatedly(Return(testData.c_str()));
EXPECT_EQ(mRunnerInterface->newPacketNotification(
0, static_cast<std::shared_ptr<MemHandle>>(packet)), Status::SUCCESS);
EXPECT_EQ(streamCb->data, packet->getData());
EXPECT_EQ(streamCb->timestamp, packet->getTimeStamp());
}
} // namespace
} // namespace computepipe
} // namespace automotive
} // namespace android