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/SharedMemory.h>
19 #include <nnapi/TypeUtils.h>
20 #include <nnapi/Types.h>
21 #include <nnapi/hal/ResilientBuffer.h>
22 #include <memory>
23 #include <tuple>
24 #include <utility>
25 #include "MockBuffer.h"
26
27 namespace android::hardware::neuralnetworks::utils {
28 namespace {
29
30 using ::testing::_;
31 using ::testing::InvokeWithoutArgs;
32 using ::testing::Return;
33
34 constexpr auto kToken = nn::Request::MemoryDomainToken{1};
35
36 using SharedMockBuffer = std::shared_ptr<const nn::MockBuffer>;
37 using MockBufferFactory = ::testing::MockFunction<nn::GeneralResult<nn::SharedBuffer>()>;
38
createConfiguredMockBuffer()39 SharedMockBuffer createConfiguredMockBuffer() {
40 return std::make_shared<const nn::MockBuffer>();
41 }
42
43 std::tuple<std::shared_ptr<const nn::MockBuffer>, std::unique_ptr<MockBufferFactory>,
44 std::shared_ptr<const ResilientBuffer>>
setup()45 setup() {
46 auto mockBuffer = std::make_shared<const nn::MockBuffer>();
47
48 auto mockBufferFactory = std::make_unique<MockBufferFactory>();
49 EXPECT_CALL(*mockBufferFactory, Call()).Times(1).WillOnce(Return(mockBuffer));
50
51 auto buffer = ResilientBuffer::create(mockBufferFactory->AsStdFunction()).value();
52 return std::make_tuple(std::move(mockBuffer), std::move(mockBufferFactory), std::move(buffer));
53 }
54
__anon35fa07630202(nn::ErrorStatus status) 55 constexpr auto makeError = [](nn::ErrorStatus status) {
56 return [status](const auto&... /*args*/) { return nn::error(status); };
57 };
58 const auto kReturnGeneralFailure = makeError(nn::ErrorStatus::GENERAL_FAILURE);
59 const auto kReturnDeadObject = makeError(nn::ErrorStatus::DEAD_OBJECT);
60
61 const auto kNoError = nn::GeneralResult<void>{};
62
63 } // namespace
64
TEST(ResilientBufferTest,invalidBufferFactory)65 TEST(ResilientBufferTest, invalidBufferFactory) {
66 // setup call
67 const auto invalidBufferFactory = ResilientBuffer::Factory{};
68
69 // run test
70 const auto result = ResilientBuffer::create(invalidBufferFactory);
71
72 // verify result
73 ASSERT_FALSE(result.has_value());
74 EXPECT_EQ(result.error().code, nn::ErrorStatus::INVALID_ARGUMENT);
75 }
76
TEST(ResilientBufferTest,bufferFactoryFailure)77 TEST(ResilientBufferTest, bufferFactoryFailure) {
78 // setup call
79 const auto invalidBufferFactory = kReturnGeneralFailure;
80
81 // run test
82 const auto result = ResilientBuffer::create(invalidBufferFactory);
83
84 // verify result
85 ASSERT_FALSE(result.has_value());
86 EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
87 }
88
TEST(ResilientBufferTest,getBuffer)89 TEST(ResilientBufferTest, getBuffer) {
90 // setup call
91 const auto [mockBuffer, mockBufferFactory, buffer] = setup();
92
93 // run test
94 const auto result = buffer->getBuffer();
95
96 // verify result
97 EXPECT_TRUE(result == mockBuffer);
98 }
99
TEST(ResilientBufferTest,getToken)100 TEST(ResilientBufferTest, getToken) {
101 // setup call
102 const auto [mockBuffer, mockBufferFactory, buffer] = setup();
103 EXPECT_CALL(*mockBuffer, getToken()).Times(1).WillOnce(Return(kToken));
104
105 // run test
106 const auto token = buffer->getToken();
107
108 // verify result
109 EXPECT_EQ(token, kToken);
110 }
111
TEST(ResilientBufferTest,copyTo)112 TEST(ResilientBufferTest, copyTo) {
113 // setup call
114 const auto [mockBuffer, mockBufferFactory, buffer] = setup();
115 EXPECT_CALL(*mockBuffer, copyTo(_)).Times(1).WillOnce(Return(kNoError));
116
117 // run test
118 const nn::SharedMemory memory = std::make_shared<const nn::Memory>();
119 const auto result = buffer->copyTo(memory);
120
121 // verify result
122 ASSERT_TRUE(result.has_value())
123 << "Failed with " << result.error().code << ": " << result.error().message;
124 }
125
TEST(ResilientBufferTest,copyToError)126 TEST(ResilientBufferTest, copyToError) {
127 // setup call
128 const auto [mockBuffer, mockBufferFactory, buffer] = setup();
129 EXPECT_CALL(*mockBuffer, copyTo(_)).Times(1).WillOnce(kReturnGeneralFailure);
130
131 // run test
132 const nn::SharedMemory memory = std::make_shared<const nn::Memory>();
133 const auto result = buffer->copyTo(memory);
134
135 // verify result
136 ASSERT_FALSE(result.has_value());
137 EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
138 }
139
TEST(ResilientBufferTest,copyToDeadObjectFailedRecovery)140 TEST(ResilientBufferTest, copyToDeadObjectFailedRecovery) {
141 // setup call
142 const auto [mockBuffer, mockBufferFactory, buffer] = setup();
143 EXPECT_CALL(*mockBuffer, copyTo(_)).Times(1).WillOnce(kReturnDeadObject);
144 EXPECT_CALL(*mockBufferFactory, Call()).Times(1).WillOnce(kReturnGeneralFailure);
145
146 // run test
147 const nn::SharedMemory memory = std::make_shared<const nn::Memory>();
148 const auto result = buffer->copyTo(memory);
149
150 // verify result
151 ASSERT_FALSE(result.has_value());
152 EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
153 }
154
TEST(ResilientBufferTest,copyToDeadObjectSuccessfulRecovery)155 TEST(ResilientBufferTest, copyToDeadObjectSuccessfulRecovery) {
156 // setup call
157 const auto [mockBuffer, mockBufferFactory, buffer] = setup();
158 EXPECT_CALL(*mockBuffer, copyTo(_)).Times(1).WillOnce(kReturnDeadObject);
159 const auto recoveredMockBuffer = createConfiguredMockBuffer();
160 EXPECT_CALL(*recoveredMockBuffer, copyTo(_)).Times(1).WillOnce(Return(kNoError));
161 EXPECT_CALL(*mockBufferFactory, Call()).Times(1).WillOnce(Return(recoveredMockBuffer));
162
163 // run test
164 const nn::SharedMemory memory = std::make_shared<const nn::Memory>();
165 const auto result = buffer->copyTo(memory);
166
167 // verify result
168 ASSERT_TRUE(result.has_value())
169 << "Failed with " << result.error().code << ": " << result.error().message;
170 }
171
TEST(ResilientBufferTest,copyFrom)172 TEST(ResilientBufferTest, copyFrom) {
173 // setup call
174 const auto [mockBuffer, mockBufferFactory, buffer] = setup();
175 EXPECT_CALL(*mockBuffer, copyFrom(_, _)).Times(1).WillOnce(Return(kNoError));
176
177 // run test
178 const nn::SharedMemory memory = std::make_shared<const nn::Memory>();
179 const auto result = buffer->copyFrom(memory, {});
180
181 // verify result
182 ASSERT_TRUE(result.has_value())
183 << "Failed with " << result.error().code << ": " << result.error().message;
184 }
185
TEST(ResilientBufferTest,copyFromError)186 TEST(ResilientBufferTest, copyFromError) {
187 // setup call
188 const auto [mockBuffer, mockBufferFactory, buffer] = setup();
189 EXPECT_CALL(*mockBuffer, copyFrom(_, _)).Times(1).WillOnce(kReturnGeneralFailure);
190
191 // run test
192 const nn::SharedMemory memory = std::make_shared<const nn::Memory>();
193 const auto result = buffer->copyFrom(memory, {});
194
195 // verify result
196 ASSERT_FALSE(result.has_value());
197 EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
198 }
199
TEST(ResilientBufferTest,copyFromDeadObjectFailedRecovery)200 TEST(ResilientBufferTest, copyFromDeadObjectFailedRecovery) {
201 // setup call
202 const auto [mockBuffer, mockBufferFactory, buffer] = setup();
203 EXPECT_CALL(*mockBuffer, copyFrom(_, _)).Times(1).WillOnce(kReturnDeadObject);
204 EXPECT_CALL(*mockBufferFactory, Call()).Times(1).WillOnce(kReturnGeneralFailure);
205
206 // run test
207 const nn::SharedMemory memory = std::make_shared<const nn::Memory>();
208 const auto result = buffer->copyFrom(memory, {});
209
210 // verify result
211 ASSERT_FALSE(result.has_value());
212 EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
213 }
214
TEST(ResilientBufferTest,copyFromDeadObjectSuccessfulRecovery)215 TEST(ResilientBufferTest, copyFromDeadObjectSuccessfulRecovery) {
216 // setup call
217 const auto [mockBuffer, mockBufferFactory, buffer] = setup();
218 EXPECT_CALL(*mockBuffer, copyFrom(_, _)).Times(1).WillOnce(kReturnDeadObject);
219 const auto recoveredMockBuffer = createConfiguredMockBuffer();
220 EXPECT_CALL(*recoveredMockBuffer, copyFrom(_, _)).Times(1).WillOnce(Return(kNoError));
221 EXPECT_CALL(*mockBufferFactory, Call()).Times(1).WillOnce(Return(recoveredMockBuffer));
222
223 // run test
224 const nn::SharedMemory memory = std::make_shared<const nn::Memory>();
225 const auto result = buffer->copyFrom(memory, {});
226
227 // verify result
228 ASSERT_TRUE(result.has_value())
229 << "Failed with " << result.error().code << ": " << result.error().message;
230 }
231
TEST(ResilientBufferTest,recover)232 TEST(ResilientBufferTest, recover) {
233 // setup call
234 const auto [mockBuffer, mockBufferFactory, buffer] = setup();
235 const auto recoveredMockBuffer = createConfiguredMockBuffer();
236 EXPECT_CALL(*mockBufferFactory, Call()).Times(1).WillOnce(Return(recoveredMockBuffer));
237
238 // run test
239 const auto result = buffer->recover(mockBuffer.get());
240
241 // verify result
242 ASSERT_TRUE(result.has_value())
243 << "Failed with " << result.error().code << ": " << result.error().message;
244 EXPECT_TRUE(result.value() == recoveredMockBuffer);
245 }
246
TEST(ResilientBufferTest,recoverFailure)247 TEST(ResilientBufferTest, recoverFailure) {
248 // setup call
249 const auto [mockBuffer, mockBufferFactory, buffer] = setup();
250 const auto recoveredMockBuffer = createConfiguredMockBuffer();
251 EXPECT_CALL(*mockBufferFactory, Call()).Times(1).WillOnce(kReturnGeneralFailure);
252
253 // run test
254 const auto result = buffer->recover(mockBuffer.get());
255
256 // verify result
257 EXPECT_FALSE(result.has_value());
258 }
259
TEST(ResilientBufferTest,someoneElseRecovered)260 TEST(ResilientBufferTest, someoneElseRecovered) {
261 // setup call
262 const auto [mockBuffer, mockBufferFactory, buffer] = setup();
263 const auto recoveredMockBuffer = createConfiguredMockBuffer();
264 EXPECT_CALL(*mockBufferFactory, Call()).Times(1).WillOnce(Return(recoveredMockBuffer));
265 buffer->recover(mockBuffer.get());
266
267 // run test
268 const auto result = buffer->recover(mockBuffer.get());
269
270 // verify result
271 ASSERT_TRUE(result.has_value())
272 << "Failed with " << result.error().code << ": " << result.error().message;
273 EXPECT_TRUE(result.value() == recoveredMockBuffer);
274 }
275
276 } // namespace android::hardware::neuralnetworks::utils
277