blob: b2f4507c2248d4d3ca64027867c69b3b7de282fa [file] [log] [blame]
/*
* Copyright (C) 2021 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.
*/
#define LOG_TAG "neuralnetworks_aidl_hal_test"
#include <aidl/android/hardware/neuralnetworks/Capabilities.h>
#include <aidl/android/hardware/neuralnetworks/IDevice.h>
#include <aidl/android/hardware/neuralnetworks/Operand.h>
#include <aidl/android/hardware/neuralnetworks/OperandType.h>
#include <aidl/android/hardware/neuralnetworks/Priority.h>
#include <android/binder_interface_utils.h>
#include "Utils.h"
#include "VtsHalNeuralnetworks.h"
namespace aidl::android::hardware::neuralnetworks::vts::functional {
using implementation::PreparedModelCallback;
// create device test
TEST_P(NeuralNetworksAidlTest, CreateDevice) {}
// initialization
TEST_P(NeuralNetworksAidlTest, GetCapabilitiesTest) {
Capabilities capabilities;
const auto retStatus = kDevice->getCapabilities(&capabilities);
ASSERT_TRUE(retStatus.isOk());
auto isPositive = [](const PerformanceInfo& perf) {
return perf.execTime > 0.0f && perf.powerUsage > 0.0f;
};
EXPECT_TRUE(isPositive(capabilities.relaxedFloat32toFloat16PerformanceScalar));
EXPECT_TRUE(isPositive(capabilities.relaxedFloat32toFloat16PerformanceTensor));
const auto& opPerf = capabilities.operandPerformance;
EXPECT_TRUE(
std::all_of(opPerf.begin(), opPerf.end(),
[isPositive](const OperandPerformance& a) { return isPositive(a.info); }));
EXPECT_TRUE(std::is_sorted(opPerf.begin(), opPerf.end(),
[](const OperandPerformance& a, const OperandPerformance& b) {
return a.type < b.type;
}));
EXPECT_TRUE(std::all_of(opPerf.begin(), opPerf.end(), [](const OperandPerformance& a) {
return a.type != OperandType::SUBGRAPH;
}));
EXPECT_TRUE(isPositive(capabilities.ifPerformance));
EXPECT_TRUE(isPositive(capabilities.whilePerformance));
}
// detect cycle
TEST_P(NeuralNetworksAidlTest, CycleTest) {
// opnd0 = TENSOR_FLOAT32 // model input
// opnd1 = TENSOR_FLOAT32 // model input
// opnd2 = INT32 // model input
// opnd3 = ADD(opnd0, opnd4, opnd2)
// opnd4 = ADD(opnd1, opnd3, opnd2)
// opnd5 = ADD(opnd4, opnd0, opnd2) // model output
//
// +-----+
// | |
// v |
// 3 = ADD(0, 4, 2) |
// | |
// +----------+ |
// | |
// v |
// 4 = ADD(1, 3, 2) |
// | |
// +----------------+
// |
// |
// +-------+
// |
// v
// 5 = ADD(4, 0, 2)
const std::vector<Operand> operands = {
{
// operands[0]
.type = OperandType::TENSOR_FLOAT32,
.dimensions = {1},
.scale = 0.0f,
.zeroPoint = 0,
.lifetime = OperandLifeTime::SUBGRAPH_INPUT,
.location = {.poolIndex = 0, .offset = 0, .length = 0},
},
{
// operands[1]
.type = OperandType::TENSOR_FLOAT32,
.dimensions = {1},
.scale = 0.0f,
.zeroPoint = 0,
.lifetime = OperandLifeTime::SUBGRAPH_INPUT,
.location = {.poolIndex = 0, .offset = 0, .length = 0},
},
{
// operands[2]
.type = OperandType::INT32,
.dimensions = {},
.scale = 0.0f,
.zeroPoint = 0,
.lifetime = OperandLifeTime::SUBGRAPH_INPUT,
.location = {.poolIndex = 0, .offset = 0, .length = 0},
},
{
// operands[3]
.type = OperandType::TENSOR_FLOAT32,
.dimensions = {1},
.scale = 0.0f,
.zeroPoint = 0,
.lifetime = OperandLifeTime::TEMPORARY_VARIABLE,
.location = {.poolIndex = 0, .offset = 0, .length = 0},
},
{
// operands[4]
.type = OperandType::TENSOR_FLOAT32,
.dimensions = {1},
.scale = 0.0f,
.zeroPoint = 0,
.lifetime = OperandLifeTime::TEMPORARY_VARIABLE,
.location = {.poolIndex = 0, .offset = 0, .length = 0},
},
{
// operands[5]
.type = OperandType::TENSOR_FLOAT32,
.dimensions = {1},
.scale = 0.0f,
.zeroPoint = 0,
.lifetime = OperandLifeTime::SUBGRAPH_OUTPUT,
.location = {.poolIndex = 0, .offset = 0, .length = 0},
},
};
const std::vector<Operation> operations = {
{.type = OperationType::ADD, .inputs = {0, 4, 2}, .outputs = {3}},
{.type = OperationType::ADD, .inputs = {1, 3, 2}, .outputs = {4}},
{.type = OperationType::ADD, .inputs = {4, 0, 2}, .outputs = {5}},
};
Subgraph subgraph = {
.operands = operands,
.operations = operations,
.inputIndexes = {0, 1, 2},
.outputIndexes = {5},
};
const Model model = {
.main = std::move(subgraph),
.referenced = {},
.operandValues = {},
.pools = {},
};
// ensure that getSupportedOperations() checks model validity
std::vector<bool> supportedOps;
const auto supportedOpsStatus = kDevice->getSupportedOperations(model, &supportedOps);
ASSERT_FALSE(supportedOpsStatus.isOk());
ASSERT_EQ(supportedOpsStatus.getExceptionCode(), EX_SERVICE_SPECIFIC);
ASSERT_EQ(static_cast<ErrorStatus>(supportedOpsStatus.getServiceSpecificError()),
ErrorStatus::INVALID_ARGUMENT);
// ensure that prepareModel() checks model validity
auto preparedModelCallback = ndk::SharedRefBase::make<PreparedModelCallback>();
auto prepareLaunchStatus =
kDevice->prepareModel(model, ExecutionPreference::FAST_SINGLE_ANSWER, kDefaultPriority,
kNoDeadline, {}, {}, kEmptyCacheToken, preparedModelCallback);
// Note that preparation can fail for reasons other than an
// invalid model (invalid model should result in
// INVALID_ARGUMENT) -- for example, perhaps not all
// operations are supported, or perhaps the device hit some
// kind of capacity limit.
ASSERT_FALSE(prepareLaunchStatus.isOk());
EXPECT_EQ(prepareLaunchStatus.getExceptionCode(), EX_SERVICE_SPECIFIC);
EXPECT_NE(static_cast<ErrorStatus>(prepareLaunchStatus.getServiceSpecificError()),
ErrorStatus::NONE);
EXPECT_NE(preparedModelCallback->getStatus(), ErrorStatus::NONE);
EXPECT_EQ(preparedModelCallback->getPreparedModel(), nullptr);
}
} // namespace aidl::android::hardware::neuralnetworks::vts::functional