blob: 58db98f37407cec320cb77218ecb8520bbd91ad7 [file] [log] [blame]
Lev Proleevc185e882020-12-15 19:25:32 +00001/*
2 * Copyright (C) 2021 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#include <android/binder_enums.h>
18#include <android/binder_interface_utils.h>
19#include <android/binder_status.h>
20
21#include <nnapi/hal/aidl/Conversions.h>
22
23#include "Callbacks.h"
24#include "GeneratedTestHarness.h"
25#include "Utils.h"
26
27namespace aidl::android::hardware::neuralnetworks::vts::functional {
28
29using implementation::PreparedModelCallback;
30using test_helper::TestBuffer;
31using test_helper::TestModel;
32
33enum class DeadlineBoundType { NOW, UNLIMITED, SHORT };
34constexpr std::array<DeadlineBoundType, 3> deadlineBounds = {
35 DeadlineBoundType::NOW, DeadlineBoundType::UNLIMITED, DeadlineBoundType::SHORT};
36std::string toString(DeadlineBoundType type) {
37 switch (type) {
38 case DeadlineBoundType::NOW:
39 return "NOW";
40 case DeadlineBoundType::UNLIMITED:
41 return "UNLIMITED";
42 case DeadlineBoundType::SHORT:
43 return "SHORT";
44 }
45 LOG(FATAL) << "Unrecognized DeadlineBoundType: " << static_cast<int>(type);
46 return {};
47}
48
49constexpr auto kShortDuration = std::chrono::milliseconds{5};
50
51using Results = std::tuple<ErrorStatus, std::vector<OutputShape>, Timing>;
52using MaybeResults = std::optional<Results>;
53
54static int64_t makeDeadline(DeadlineBoundType deadlineBoundType) {
55 const auto getNanosecondsSinceEpoch = [](const auto& time) -> int64_t {
56 const auto timeSinceEpoch = time.time_since_epoch();
57 return std::chrono::duration_cast<std::chrono::nanoseconds>(timeSinceEpoch).count();
58 };
59
60 std::chrono::steady_clock::time_point timePoint;
61 switch (deadlineBoundType) {
62 case DeadlineBoundType::NOW:
63 timePoint = std::chrono::steady_clock::now();
64 break;
65 case DeadlineBoundType::UNLIMITED:
66 timePoint = std::chrono::steady_clock::time_point::max();
67 break;
68 case DeadlineBoundType::SHORT:
69 timePoint = std::chrono::steady_clock::now() + kShortDuration;
70 break;
71 }
72
73 return getNanosecondsSinceEpoch(timePoint);
74}
75
76void runPrepareModelTest(const std::shared_ptr<IDevice>& device, const Model& model,
77 Priority priority, std::optional<DeadlineBoundType> deadlineBound) {
78 int64_t deadline = kNoDeadline;
79 if (deadlineBound.has_value()) {
80 deadline = makeDeadline(deadlineBound.value());
81 }
82
83 // see if service can handle model
84 std::vector<bool> supportedOps;
85 const auto supportedCallStatus = device->getSupportedOperations(model, &supportedOps);
86 ASSERT_TRUE(supportedCallStatus.isOk());
87 ASSERT_NE(0ul, supportedOps.size());
88 const bool fullySupportsModel =
89 std::all_of(supportedOps.begin(), supportedOps.end(), [](bool valid) { return valid; });
90
91 // launch prepare model
92 const std::shared_ptr<PreparedModelCallback> preparedModelCallback =
93 ndk::SharedRefBase::make<PreparedModelCallback>();
94 const auto prepareLaunchStatus =
95 device->prepareModel(model, ExecutionPreference::FAST_SINGLE_ANSWER, priority, deadline,
96 {}, {}, kEmptyCacheToken, preparedModelCallback);
97 ASSERT_TRUE(prepareLaunchStatus.isOk())
98 << "prepareLaunchStatus: " << prepareLaunchStatus.getDescription();
99
100 // retrieve prepared model
101 preparedModelCallback->wait();
102 const ErrorStatus prepareReturnStatus = preparedModelCallback->getStatus();
103 const std::shared_ptr<IPreparedModel> preparedModel = preparedModelCallback->getPreparedModel();
104
105 // The getSupportedOperations call returns a list of operations that are guaranteed not to fail
106 // if prepareModel is called, and 'fullySupportsModel' is true i.f.f. the entire model is
107 // guaranteed. If a driver has any doubt that it can prepare an operation, it must return false.
108 // So here, if a driver isn't sure if it can support an operation, but reports that it
109 // successfully prepared the model, the test can continue.
110 if (!fullySupportsModel && prepareReturnStatus != ErrorStatus::NONE) {
111 ASSERT_EQ(nullptr, preparedModel.get());
112 return;
113 }
114
115 // verify return status
116 if (!deadlineBound.has_value()) {
117 EXPECT_EQ(ErrorStatus::NONE, prepareReturnStatus);
118 } else {
119 switch (deadlineBound.value()) {
120 case DeadlineBoundType::NOW:
121 case DeadlineBoundType::SHORT:
122 // Either the driver successfully completed the task or it
123 // aborted and returned MISSED_DEADLINE_*.
124 EXPECT_TRUE(prepareReturnStatus == ErrorStatus::NONE ||
125 prepareReturnStatus == ErrorStatus::MISSED_DEADLINE_TRANSIENT ||
126 prepareReturnStatus == ErrorStatus::MISSED_DEADLINE_PERSISTENT);
127 break;
128 case DeadlineBoundType::UNLIMITED:
129 // If an unlimited deadline is supplied, we expect the execution to
130 // proceed normally. In this case, check it normally by breaking out
131 // of the switch statement.
132 EXPECT_EQ(ErrorStatus::NONE, prepareReturnStatus);
133 break;
134 }
135 }
136 ASSERT_EQ(prepareReturnStatus == ErrorStatus::NONE, preparedModel.get() != nullptr);
137}
138
139void runPrepareModelTests(const std::shared_ptr<IDevice>& device, const Model& model) {
140 // test priority
141 for (auto priority : ndk::enum_range<Priority>{}) {
142 SCOPED_TRACE("priority: " + toString(priority));
143 if (priority == kDefaultPriority) continue;
144 runPrepareModelTest(device, model, priority, {});
145 }
146
147 // test deadline
148 for (auto deadlineBound : deadlineBounds) {
149 SCOPED_TRACE("deadlineBound: " + toString(deadlineBound));
150 runPrepareModelTest(device, model, kDefaultPriority, deadlineBound);
151 }
152}
153
154static MaybeResults executeSynchronously(const std::shared_ptr<IPreparedModel>& preparedModel,
155 const Request& request, int64_t deadline) {
156 SCOPED_TRACE("synchronous");
157 const bool measure = false;
158
159 // run execution
160 ExecutionResult executionResult;
161 const auto ret = preparedModel->executeSynchronously(request, measure, deadline,
162 kOmittedTimeoutDuration, &executionResult);
163 EXPECT_TRUE(ret.isOk() || ret.getExceptionCode() == EX_SERVICE_SPECIFIC)
164 << ret.getDescription();
165 if (!ret.isOk()) {
166 if (ret.getExceptionCode() != EX_SERVICE_SPECIFIC) {
167 return std::nullopt;
168 }
169 return MaybeResults(
170 {static_cast<ErrorStatus>(ret.getServiceSpecificError()), {}, kNoTiming});
171 }
172
173 // return results
174 return MaybeResults({executionResult.outputSufficientSize
175 ? ErrorStatus::NONE
176 : ErrorStatus::OUTPUT_INSUFFICIENT_SIZE,
177 std::move(executionResult.outputShapes), executionResult.timing});
178}
179
180void runExecutionTest(const std::shared_ptr<IPreparedModel>& preparedModel,
181 const TestModel& testModel, const Request& request,
182 const ExecutionContext& context, DeadlineBoundType deadlineBound) {
183 const auto deadline = makeDeadline(deadlineBound);
184
185 // Perform execution and unpack results.
186 const auto results = executeSynchronously(preparedModel, request, deadline);
187 if (!results.has_value()) return;
188 const auto& [status, outputShapes, timing] = results.value();
189
190 // Verify no timing information was returned
191 EXPECT_EQ(timing, kNoTiming);
192
193 // Validate deadline information if applicable.
194 switch (deadlineBound) {
195 case DeadlineBoundType::NOW:
196 case DeadlineBoundType::SHORT:
197 // Either the driver successfully completed the task or it
198 // aborted and returned MISSED_DEADLINE_*.
199 ASSERT_TRUE(status == ErrorStatus::NONE ||
200 status == ErrorStatus::MISSED_DEADLINE_TRANSIENT ||
201 status == ErrorStatus::MISSED_DEADLINE_PERSISTENT);
202 break;
203 case DeadlineBoundType::UNLIMITED:
204 // If an unlimited deadline is supplied, we expect the execution to
205 // proceed normally. In this case, check it normally by breaking out
206 // of the switch statement.
207 ASSERT_EQ(ErrorStatus::NONE, status);
208 break;
209 }
210
211 // If the model output operands are fully specified, outputShapes must be either
212 // either empty, or have the same number of elements as the number of outputs.
213 ASSERT_TRUE(outputShapes.size() == 0 ||
214 outputShapes.size() == testModel.main.outputIndexes.size());
215
216 // Go through all outputs, check returned output shapes.
217 for (uint32_t i = 0; i < outputShapes.size(); i++) {
218 EXPECT_TRUE(outputShapes[i].isSufficient);
219 const auto expect =
220 utils::toSigned(testModel.main.operands[testModel.main.outputIndexes[i]].dimensions)
221 .value();
222 const std::vector<int32_t>& actual = outputShapes[i].dimensions;
223 EXPECT_EQ(expect, actual);
224 }
225
226 // Retrieve execution results.
227 const std::vector<TestBuffer> outputs = context.getOutputBuffers(request);
228
229 // We want "close-enough" results.
230 if (status == ErrorStatus::NONE) {
231 checkResults(testModel, outputs);
232 }
233}
234
235void runExecutionTests(const std::shared_ptr<IPreparedModel>& preparedModel,
236 const TestModel& testModel, const Request& request,
237 const ExecutionContext& context) {
238 for (auto deadlineBound : deadlineBounds) {
239 runExecutionTest(preparedModel, testModel, request, context, deadlineBound);
240 }
241}
242
243void runTests(const std::shared_ptr<IDevice>& device, const TestModel& testModel) {
244 // setup
245 const Model model = createModel(testModel);
246
247 // run prepare model tests
248 runPrepareModelTests(device, model);
249
250 // prepare model
251 std::shared_ptr<IPreparedModel> preparedModel;
252 createPreparedModel(device, model, &preparedModel);
253 if (preparedModel == nullptr) return;
254
255 // run execution tests
256 ExecutionContext context;
257 const Request request = context.createRequest(testModel);
258 runExecutionTests(preparedModel, testModel, request, context);
259}
260
261class DeadlineTest : public GeneratedTestBase {};
262
263TEST_P(DeadlineTest, Test) {
264 runTests(kDevice, kTestModel);
265}
266
267INSTANTIATE_GENERATED_TEST(DeadlineTest,
268 [](const TestModel& testModel) { return !testModel.expectFailure; });
269
270} // namespace aidl::android::hardware::neuralnetworks::vts::functional