blob: c54972887e9dfddaa6bd42d11ff59596a16a3028 [file] [log] [blame]
Michael Butler7ed61352018-03-22 16:37:57 -07001/*
2 * Copyright (C) 2018 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 "neuralnetworks_hidl_hal_test"
18
Michael Butler7ed61352018-03-22 16:37:57 -070019#include <android-base/logging.h>
20#include <android/hidl/memory/1.0/IMemory.h>
21#include <hidlmemory/mapping.h>
22
Slava Shklyaev73ee79d2019-05-14 14:15:14 +010023#include "1.0/Callbacks.h"
24#include "1.0/Utils.h"
25#include "MemoryUtils.h"
26#include "TestHarness.h"
27#include "VtsHalNeuralnetworks.h"
28
Michael Butler7ed61352018-03-22 16:37:57 -070029namespace android {
30namespace hardware {
31namespace neuralnetworks {
32namespace V1_1 {
33namespace vts {
34namespace functional {
35
Slava Shklyaev73ee79d2019-05-14 14:15:14 +010036using ::android::hardware::neuralnetworks::V1_0::ErrorStatus;
37using ::android::hardware::neuralnetworks::V1_0::Request;
38using ::android::hardware::neuralnetworks::V1_0::RequestArgument;
39using ::android::hardware::neuralnetworks::V1_0::implementation::ExecutionCallback;
40using ::android::hardware::neuralnetworks::V1_1::IPreparedModel;
Michael Butler7ed61352018-03-22 16:37:57 -070041using ::android::hidl::memory::V1_0::IMemory;
Slava Shklyaev73ee79d2019-05-14 14:15:14 +010042using ::test_helper::for_all;
43using ::test_helper::MixedTyped;
44using ::test_helper::MixedTypedExample;
Michael Butler7ed61352018-03-22 16:37:57 -070045
46///////////////////////// UTILITY FUNCTIONS /////////////////////////
47
Michael Butler7ed61352018-03-22 16:37:57 -070048// Primary validation function. This function will take a valid request, apply a
49// mutation to it to invalidate the request, then pass it to interface calls
50// that use the request. Note that the request here is passed by value, and any
51// mutation to the request does not leave this function.
52static void validate(const sp<IPreparedModel>& preparedModel, const std::string& message,
53 Request request, const std::function<void(Request*)>& mutation) {
54 mutation(&request);
55 SCOPED_TRACE(message + " [execute]");
56
57 sp<ExecutionCallback> executionCallback = new ExecutionCallback();
58 ASSERT_NE(nullptr, executionCallback.get());
59 Return<ErrorStatus> executeLaunchStatus = preparedModel->execute(request, executionCallback);
60 ASSERT_TRUE(executeLaunchStatus.isOk());
61 ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, static_cast<ErrorStatus>(executeLaunchStatus));
62
63 executionCallback->wait();
64 ErrorStatus executionReturnStatus = executionCallback->getStatus();
65 ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, executionReturnStatus);
66}
67
Michael Butler7ed61352018-03-22 16:37:57 -070068///////////////////////// REMOVE INPUT ////////////////////////////////////
69
70static void removeInputTest(const sp<IPreparedModel>& preparedModel, const Request& request) {
71 for (size_t input = 0; input < request.inputs.size(); ++input) {
72 const std::string message = "removeInput: removed input " + std::to_string(input);
73 validate(preparedModel, message, request,
74 [input](Request* request) { hidl_vec_removeAt(&request->inputs, input); });
75 }
76}
77
78///////////////////////// REMOVE OUTPUT ////////////////////////////////////
79
80static void removeOutputTest(const sp<IPreparedModel>& preparedModel, const Request& request) {
81 for (size_t output = 0; output < request.outputs.size(); ++output) {
82 const std::string message = "removeOutput: removed Output " + std::to_string(output);
83 validate(preparedModel, message, request,
84 [output](Request* request) { hidl_vec_removeAt(&request->outputs, output); });
85 }
86}
87
88///////////////////////////// ENTRY POINT //////////////////////////////////
89
Michael K. Sandersda3bdbc2018-10-19 14:39:09 +010090std::vector<Request> createRequests(const std::vector<MixedTypedExample>& examples) {
Michael Butler7ed61352018-03-22 16:37:57 -070091 const uint32_t INPUT = 0;
92 const uint32_t OUTPUT = 1;
93
94 std::vector<Request> requests;
95
96 for (auto& example : examples) {
Michael K. Sandersda3bdbc2018-10-19 14:39:09 +010097 const MixedTyped& inputs = example.operands.first;
98 const MixedTyped& outputs = example.operands.second;
Michael Butler7ed61352018-03-22 16:37:57 -070099
100 std::vector<RequestArgument> inputs_info, outputs_info;
101 uint32_t inputSize = 0, outputSize = 0;
102
103 // This function only partially specifies the metadata (vector of RequestArguments).
104 // The contents are copied over below.
105 for_all(inputs, [&inputs_info, &inputSize](int index, auto, auto s) {
106 if (inputs_info.size() <= static_cast<size_t>(index)) inputs_info.resize(index + 1);
107 RequestArgument arg = {
Slava Shklyaev73ee79d2019-05-14 14:15:14 +0100108 .location = {.poolIndex = INPUT,
109 .offset = 0,
110 .length = static_cast<uint32_t>(s)},
111 .dimensions = {},
Michael Butler7ed61352018-03-22 16:37:57 -0700112 };
113 RequestArgument arg_empty = {
Slava Shklyaev73ee79d2019-05-14 14:15:14 +0100114 .hasNoValue = true,
Michael Butler7ed61352018-03-22 16:37:57 -0700115 };
116 inputs_info[index] = s ? arg : arg_empty;
117 inputSize += s;
118 });
119 // Compute offset for inputs 1 and so on
120 {
121 size_t offset = 0;
122 for (auto& i : inputs_info) {
123 if (!i.hasNoValue) i.location.offset = offset;
124 offset += i.location.length;
125 }
126 }
127
128 // Go through all outputs, initialize RequestArgument descriptors
129 for_all(outputs, [&outputs_info, &outputSize](int index, auto, auto s) {
130 if (outputs_info.size() <= static_cast<size_t>(index)) outputs_info.resize(index + 1);
131 RequestArgument arg = {
Slava Shklyaev73ee79d2019-05-14 14:15:14 +0100132 .location = {.poolIndex = OUTPUT,
133 .offset = 0,
134 .length = static_cast<uint32_t>(s)},
135 .dimensions = {},
Michael Butler7ed61352018-03-22 16:37:57 -0700136 };
137 outputs_info[index] = arg;
138 outputSize += s;
139 });
140 // Compute offset for outputs 1 and so on
141 {
142 size_t offset = 0;
143 for (auto& i : outputs_info) {
144 i.location.offset = offset;
145 offset += i.location.length;
146 }
147 }
148 std::vector<hidl_memory> pools = {nn::allocateSharedMemory(inputSize),
149 nn::allocateSharedMemory(outputSize)};
150 if (pools[INPUT].size() == 0 || pools[OUTPUT].size() == 0) {
151 return {};
152 }
153
154 // map pool
155 sp<IMemory> inputMemory = mapMemory(pools[INPUT]);
156 if (inputMemory == nullptr) {
157 return {};
158 }
159 char* inputPtr = reinterpret_cast<char*>(static_cast<void*>(inputMemory->getPointer()));
160 if (inputPtr == nullptr) {
161 return {};
162 }
163
164 // initialize pool
165 inputMemory->update();
166 for_all(inputs, [&inputs_info, inputPtr](int index, auto p, auto s) {
167 char* begin = (char*)p;
168 char* end = begin + s;
169 // TODO: handle more than one input
170 std::copy(begin, end, inputPtr + inputs_info[index].location.offset);
171 });
172 inputMemory->commit();
173
174 requests.push_back({.inputs = inputs_info, .outputs = outputs_info, .pools = pools});
175 }
176
177 return requests;
178}
179
Michael Butler353a6242019-04-30 13:51:24 -0700180void ValidationTest::validateRequests(const sp<IPreparedModel>& preparedModel,
Michael Butler7ed61352018-03-22 16:37:57 -0700181 const std::vector<Request>& requests) {
Michael Butler7ed61352018-03-22 16:37:57 -0700182 // validate each request
183 for (const Request& request : requests) {
184 removeInputTest(preparedModel, request);
185 removeOutputTest(preparedModel, request);
186 }
187}
188
189} // namespace functional
190} // namespace vts
191} // namespace V1_1
192} // namespace neuralnetworks
193} // namespace hardware
194} // namespace android