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