1 /*
2 * Copyright (C) 2020 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 <gmock/gmock.h>
18 #include <nnapi/TypeUtils.h>
19 #include <nnapi/Types.h>
20 #include <nnapi/hal/ResilientPreparedModel.h>
21 #include <utility>
22 #include "MockPreparedModel.h"
23
24 namespace android::hardware::neuralnetworks::utils {
25 namespace {
26
27 using ::testing::_;
28 using ::testing::InvokeWithoutArgs;
29 using ::testing::Return;
30
31 using SharedMockPreparedModel = std::shared_ptr<const nn::MockPreparedModel>;
32 using MockPreparedModelFactory =
33 ::testing::MockFunction<nn::GeneralResult<nn::SharedPreparedModel>()>;
34
createConfiguredMockPreparedModel()35 SharedMockPreparedModel createConfiguredMockPreparedModel() {
36 return std::make_shared<const nn::MockPreparedModel>();
37 }
38
39 std::tuple<std::shared_ptr<const nn::MockPreparedModel>, std::unique_ptr<MockPreparedModelFactory>,
40 std::shared_ptr<const ResilientPreparedModel>>
setup()41 setup() {
42 auto mockPreparedModel = std::make_shared<const nn::MockPreparedModel>();
43
44 auto mockPreparedModelFactory = std::make_unique<MockPreparedModelFactory>();
45 EXPECT_CALL(*mockPreparedModelFactory, Call()).Times(1).WillOnce(Return(mockPreparedModel));
46
47 auto buffer = ResilientPreparedModel::create(mockPreparedModelFactory->AsStdFunction()).value();
48 return std::make_tuple(std::move(mockPreparedModel), std::move(mockPreparedModelFactory),
49 std::move(buffer));
50 }
51
__anond1466ecd0202(nn::ErrorStatus status) 52 constexpr auto makeError = [](nn::ErrorStatus status) {
53 return [status](const auto&... /*args*/) { return nn::error(status); };
54 };
55 const auto kReturnGeneralFailure = makeError(nn::ErrorStatus::GENERAL_FAILURE);
56 const auto kReturnDeadObject = makeError(nn::ErrorStatus::DEAD_OBJECT);
57
58 const auto kNoCreateReusableExecutionError = nn::GeneralResult<nn::SharedExecution>{};
59 const auto kNoExecutionError =
60 nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>{};
61 const auto kNoFencedExecutionError =
62 nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>(
63 std::make_pair(nn::SyncFence::createAsSignaled(), nullptr));
64
65 struct FakeResource {};
66
67 } // namespace
68
TEST(ResilientPreparedModelTest,invalidPreparedModelFactory)69 TEST(ResilientPreparedModelTest, invalidPreparedModelFactory) {
70 // setup call
71 const auto invalidPreparedModelFactory = ResilientPreparedModel::Factory{};
72
73 // run test
74 const auto result = ResilientPreparedModel::create(invalidPreparedModelFactory);
75
76 // verify result
77 ASSERT_FALSE(result.has_value());
78 EXPECT_EQ(result.error().code, nn::ErrorStatus::INVALID_ARGUMENT);
79 }
80
TEST(ResilientPreparedModelTest,preparedModelFactoryFailure)81 TEST(ResilientPreparedModelTest, preparedModelFactoryFailure) {
82 // setup call
83 const auto invalidPreparedModelFactory = kReturnGeneralFailure;
84
85 // run test
86 const auto result = ResilientPreparedModel::create(invalidPreparedModelFactory);
87
88 // verify result
89 ASSERT_FALSE(result.has_value());
90 EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
91 }
92
TEST(ResilientPreparedModelTest,getPreparedModel)93 TEST(ResilientPreparedModelTest, getPreparedModel) {
94 // setup call
95 const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
96
97 // run test
98 const auto result = preparedModel->getPreparedModel();
99
100 // verify result
101 EXPECT_TRUE(result == mockPreparedModel);
102 }
103
TEST(ResilientPreparedModelTest,execute)104 TEST(ResilientPreparedModelTest, execute) {
105 // setup call
106 const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
107 EXPECT_CALL(*mockPreparedModel, execute(_, _, _, _))
108 .Times(1)
109 .WillOnce(Return(kNoExecutionError));
110
111 // run test
112 const auto result = preparedModel->execute({}, {}, {}, {});
113
114 // verify result
115 ASSERT_TRUE(result.has_value())
116 << "Failed with " << result.error().code << ": " << result.error().message;
117 }
118
TEST(ResilientPreparedModelTest,executeError)119 TEST(ResilientPreparedModelTest, executeError) {
120 // setup call
121 const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
122 EXPECT_CALL(*mockPreparedModel, execute(_, _, _, _)).Times(1).WillOnce(kReturnGeneralFailure);
123
124 // run test
125 const auto result = preparedModel->execute({}, {}, {}, {});
126
127 // verify result
128 ASSERT_FALSE(result.has_value());
129 EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
130 }
131
TEST(ResilientPreparedModelTest,executeDeadObjectFailedRecovery)132 TEST(ResilientPreparedModelTest, executeDeadObjectFailedRecovery) {
133 // setup call
134 const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
135 EXPECT_CALL(*mockPreparedModel, execute(_, _, _, _)).Times(1).WillOnce(kReturnDeadObject);
136 constexpr auto ret = [] { return nn::error(nn::ErrorStatus::GENERAL_FAILURE); };
137 EXPECT_CALL(*mockPreparedModelFactory, Call()).Times(1).WillOnce(ret);
138
139 // run test
140 const auto result = preparedModel->execute({}, {}, {}, {});
141
142 // verify result
143 ASSERT_FALSE(result.has_value());
144 EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
145 }
146
TEST(ResilientPreparedModelTest,executeDeadObjectSuccessfulRecovery)147 TEST(ResilientPreparedModelTest, executeDeadObjectSuccessfulRecovery) {
148 // setup call
149 const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
150 EXPECT_CALL(*mockPreparedModel, execute(_, _, _, _)).Times(1).WillOnce(kReturnDeadObject);
151 const auto recoveredMockPreparedModel = createConfiguredMockPreparedModel();
152 EXPECT_CALL(*recoveredMockPreparedModel, execute(_, _, _, _))
153 .Times(1)
154 .WillOnce(Return(kNoExecutionError));
155 EXPECT_CALL(*mockPreparedModelFactory, Call())
156 .Times(1)
157 .WillOnce(Return(recoveredMockPreparedModel));
158
159 // run test
160 const auto result = preparedModel->execute({}, {}, {}, {});
161
162 // verify result
163 ASSERT_TRUE(result.has_value())
164 << "Failed with " << result.error().code << ": " << result.error().message;
165 }
166
TEST(ResilientPreparedModelTest,executeFenced)167 TEST(ResilientPreparedModelTest, executeFenced) {
168 // setup call
169 const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
170 EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _))
171 .Times(1)
172 .WillOnce(Return(kNoFencedExecutionError));
173
174 // run test
175 const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {});
176
177 // verify result
178 ASSERT_TRUE(result.has_value())
179 << "Failed with " << result.error().code << ": " << result.error().message;
180 }
181
TEST(ResilientPreparedModelTest,executeFencedError)182 TEST(ResilientPreparedModelTest, executeFencedError) {
183 // setup call
184 const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
185 EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _))
186 .Times(1)
187 .WillOnce(kReturnGeneralFailure);
188
189 // run test
190 const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {});
191
192 // verify result
193 ASSERT_FALSE(result.has_value());
194 EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
195 }
196
TEST(ResilientPreparedModelTest,executeFencedDeadObjectFailedRecovery)197 TEST(ResilientPreparedModelTest, executeFencedDeadObjectFailedRecovery) {
198 // setup call
199 const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
200 EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _))
201 .Times(1)
202 .WillOnce(kReturnDeadObject);
203 EXPECT_CALL(*mockPreparedModelFactory, Call()).Times(1).WillOnce(kReturnGeneralFailure);
204
205 // run test
206 const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {});
207
208 // verify result
209 ASSERT_FALSE(result.has_value());
210 EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
211 }
212
TEST(ResilientPreparedModelTest,executeFencedDeadObjectSuccessfulRecovery)213 TEST(ResilientPreparedModelTest, executeFencedDeadObjectSuccessfulRecovery) {
214 // setup call
215 const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
216 EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _))
217 .Times(1)
218 .WillOnce(kReturnDeadObject);
219 const auto recoveredMockPreparedModel = createConfiguredMockPreparedModel();
220 EXPECT_CALL(*recoveredMockPreparedModel, executeFenced(_, _, _, _, _, _))
221 .Times(1)
222 .WillOnce(Return(kNoFencedExecutionError));
223 EXPECT_CALL(*mockPreparedModelFactory, Call())
224 .Times(1)
225 .WillOnce(Return(recoveredMockPreparedModel));
226
227 // run test
228 const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {});
229
230 // verify result
231 ASSERT_TRUE(result.has_value())
232 << "Failed with " << result.error().code << ": " << result.error().message;
233 }
234
TEST(ResilientPreparedModelTest,createReusableExecution)235 TEST(ResilientPreparedModelTest, createReusableExecution) {
236 // setup call
237 const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
238 EXPECT_CALL(*mockPreparedModel, createReusableExecution(_, _, _))
239 .Times(1)
240 .WillOnce(Return(kNoCreateReusableExecutionError));
241
242 // run test
243 const auto result = preparedModel->createReusableExecution({}, {}, {});
244
245 // verify result
246 ASSERT_TRUE(result.has_value())
247 << "Failed with " << result.error().code << ": " << result.error().message;
248 }
249
TEST(ResilientPreparedModelTest,createReusableExecutionError)250 TEST(ResilientPreparedModelTest, createReusableExecutionError) {
251 // setup call
252 const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
253 EXPECT_CALL(*mockPreparedModel, createReusableExecution(_, _, _))
254 .Times(1)
255 .WillOnce(kReturnGeneralFailure);
256
257 // run test
258 const auto result = preparedModel->createReusableExecution({}, {}, {});
259
260 // verify result
261 ASSERT_FALSE(result.has_value());
262 EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
263 }
264
TEST(ResilientPreparedModelTest,getUnderlyingResource)265 TEST(ResilientPreparedModelTest, getUnderlyingResource) {
266 // setup call
267 const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
268 EXPECT_CALL(*mockPreparedModel, getUnderlyingResource())
269 .Times(1)
270 .WillOnce(Return(FakeResource{}));
271
272 // run test
273 const auto resource = preparedModel->getUnderlyingResource();
274
275 // verify resource
276 const FakeResource* maybeFakeResource = std::any_cast<FakeResource>(&resource);
277 EXPECT_NE(maybeFakeResource, nullptr);
278 }
279
TEST(ResilientPreparedModelTest,recover)280 TEST(ResilientPreparedModelTest, recover) {
281 // setup call
282 const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
283 const auto recoveredMockPreparedModel = createConfiguredMockPreparedModel();
284 EXPECT_CALL(*mockPreparedModelFactory, Call())
285 .Times(1)
286 .WillOnce(Return(recoveredMockPreparedModel));
287
288 // run test
289 const auto result = preparedModel->recover(mockPreparedModel.get());
290
291 // verify result
292 ASSERT_TRUE(result.has_value())
293 << "Failed with " << result.error().code << ": " << result.error().message;
294 EXPECT_TRUE(result.value() == recoveredMockPreparedModel);
295 }
296
TEST(ResilientPreparedModelTest,recoverFailure)297 TEST(ResilientPreparedModelTest, recoverFailure) {
298 // setup call
299 const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
300 const auto recoveredMockPreparedModel = createConfiguredMockPreparedModel();
301 EXPECT_CALL(*mockPreparedModelFactory, Call()).Times(1).WillOnce(kReturnGeneralFailure);
302
303 // run test
304 const auto result = preparedModel->recover(mockPreparedModel.get());
305
306 // verify result
307 EXPECT_FALSE(result.has_value());
308 }
309
TEST(ResilientPreparedModelTest,someoneElseRecovered)310 TEST(ResilientPreparedModelTest, someoneElseRecovered) {
311 // setup call
312 const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
313 const auto recoveredMockPreparedModel = createConfiguredMockPreparedModel();
314 EXPECT_CALL(*mockPreparedModelFactory, Call())
315 .Times(1)
316 .WillOnce(Return(recoveredMockPreparedModel));
317 preparedModel->recover(mockPreparedModel.get());
318
319 // run test
320 const auto result = preparedModel->recover(mockPreparedModel.get());
321
322 // verify result
323 ASSERT_TRUE(result.has_value())
324 << "Failed with " << result.error().code << ": " << result.error().message;
325 EXPECT_TRUE(result.value() == recoveredMockPreparedModel);
326 }
327
328 } // namespace android::hardware::neuralnetworks::utils
329