1 /*
2 * Copyright (C) 2019 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-base/file.h>
18 #include <android-base/logging.h>
19 #include <android-base/macros.h>
20 #include <android-base/unique_fd.h>
21 #include <android/sharedmem.h>
22 #include <gtest/gtest.h>
23 #include <sys/mman.h>
24
25 #include <memory>
26 #include <string>
27 #include <tuple>
28 #include <utility>
29 #include <vector>
30
31 #include "TestNeuralNetworksWrapper.h"
32
33 using namespace android::nn::test_wrapper;
34
35 namespace {
36
37 // We try the following model:
38 //
39 // op2 = ADD(op0, op1)
40 // op4 = TRANSPOSE(op2, op3)
41 //
42 // where op0 is a required model input, should be of dimension (A, B).
43 // op1 is a required constant, should be of dimension (A, 1).
44 // op2 is an internal operand, should be of dimension (A, B).
45 // op3 is an omitted optional constant / model input, should be of dimension (2).
46 // op4 is a model output, should be of dimension (B, A).
47 //
48 // For each operand, we test combinations of dimensions specification level during model
49 // construction time and execution time (if any). All other relevant combinations of the
50 // basic scenarios are then iterated over in TestAll. Note that we don't want to just use
51 // googletest's parametrized tests (TEST_P) as the 16k combinations generated too many
52 // lines of output for the test infrastructure to handle correctly.
53
54 // Which operand to test
55 enum class UnspecifiedOperand {
56 INPUT_MANDATORY,
57 CONST_MANDATORY,
58 TEMPORARY_VARIABLE,
59 INPUT_OPTIONAL,
60 CONST_OPTIONAL,
61 OUTPUT
62 };
63 // How well the dimensional information is specified
64 enum class SpecificationLevel {
65 FULLY_SPECIFIED, // all dimensions are clearly specified without any ambiguity
66 UNSPECIFIED_DIM, // certain dimension is set to 0 as unknown, but rank is well-specified
67 UNSPECIFIED_RANK, // rank is set to 0 as unknown, passing an empty vector for dims
68 UNSPECIFIED_TYPE // only during execution time, passing nullptr for operand type
69 };
70 using UnspecifiedDimensionsTestParam = std::tuple<UnspecifiedOperand,
71 SpecificationLevel, // model construction time
72 SpecificationLevel>; // execution time
73
74 // Indexing
75 constexpr uint32_t kIndex0_Model = 0; // op0, model
76 constexpr uint32_t kIndex1_Model = 1; // op1, model
77 constexpr uint32_t kIndex2_Model = 2; // op2, model
78 constexpr uint32_t kIndex3_Model = 3; // op3, model
79 constexpr uint32_t kIndex4_Model = 4; // op4, model
80 constexpr uint32_t kIndex0_Execution = 5; // op0, execution
81 constexpr uint32_t kIndex3_Execution = 6; // op3, execution
82 constexpr uint32_t kIndex4_Execution = 7; // op4, execution
83 constexpr uint32_t kIndexCount = 8; // count
84
85 constexpr int32_t kValueA = 0;
86 constexpr int32_t kValueB = 2;
87 constexpr uint32_t kDimAGood = 2;
88 constexpr uint32_t kDimABad = 3;
89
90 class UnspecifiedDimensionsTest : public ::testing::TestWithParam<UnspecifiedDimensionsTestParam> {
91 enum class OptionalType { CONST, INPUT }; // omitted operand op3 is an input or const
92 enum class BufferSize { LESS, EQUAL, MORE }; // only used for output buffer size
93 enum class OperandLocation { BUFFER, MEMORY }; // where the operand reside
94 enum class InOutType { INPUT, OUTPUT }; // parameter for setInOut()
95 // Whether input/output padding is implicitly disabled, enabled, or explicitly disabled
96 enum class PaddingEnabled { DEFAULT, ENABLED, DISABLED };
97
98 class SharedMemoryForTest {
99 public:
SharedMemoryForTest()100 SharedMemoryForTest() : memory(nullptr), fd(-1), buffer(nullptr) {}
~SharedMemoryForTest()101 ~SharedMemoryForTest() {
102 if (buffer != nullptr) {
103 munmap(buffer, kLength);
104 }
105 if (fd > -1) {
106 close(fd);
107 }
108 }
initialize(size_t size,const void * data)109 void initialize(size_t size, const void* data) {
110 #ifdef __ANDROID__
111 fd = ASharedMemory_create(nullptr, kLength);
112 #else // __ANDROID__
113 TemporaryFile tmpFile;
114 fd = tmpFile.release();
115 CHECK_EQ(ftruncate(fd, kLength), 0);
116 #endif // __ANDROID__
117 ASSERT_GT(fd, -1);
118 buffer = (uint8_t*)mmap(nullptr, kLength, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
119 ASSERT_NE(buffer, nullptr);
120 memcpy(buffer, data, size);
121 memory = std::make_shared<Memory>(kLength, PROT_READ | PROT_WRITE, fd, 0);
122 ASSERT_TRUE(memory->isValid());
123 }
getMemory() const124 const Memory* getMemory() const { return memory.get(); }
getBuffer() const125 const uint8_t* getBuffer() const { return buffer; }
126
127 private:
128 DISALLOW_COPY_AND_ASSIGN(SharedMemoryForTest);
129 std::shared_ptr<Memory> memory;
130 int fd;
131 uint8_t* buffer;
132 // Always allocate an ashmem of 64 bytes. This is large enough for all use cases.
133 static constexpr size_t kLength = 64;
134 };
135
toString(SpecificationLevel level)136 std::string toString(SpecificationLevel level) {
137 switch (level) {
138 case SpecificationLevel::FULLY_SPECIFIED:
139 return "FULLY_SPECIFIED";
140 case SpecificationLevel::UNSPECIFIED_DIM:
141 return "UNSPECIFIED_DIM";
142 case SpecificationLevel::UNSPECIFIED_RANK:
143 return "UNSPECIFIED_RANK";
144 case SpecificationLevel::UNSPECIFIED_TYPE:
145 return "UNSPECIFIED_TYPE";
146 default:
147 return "UNKNOWN";
148 }
149 }
150
toString(BufferSize b)151 std::string toString(BufferSize b) {
152 switch (b) {
153 case BufferSize::LESS:
154 return "LESS";
155 case BufferSize::EQUAL:
156 return "EQUAL";
157 case BufferSize::MORE:
158 return "MORE";
159 default:
160 return "UNKNOWN";
161 }
162 }
163
toString(OperandLocation loc)164 std::string toString(OperandLocation loc) {
165 switch (loc) {
166 case OperandLocation::BUFFER:
167 return "BUFFER";
168 case OperandLocation::MEMORY:
169 return "MEMORY";
170 default:
171 return "UNKNOWN";
172 }
173 }
174
toString(PaddingEnabled enabled)175 std::string toString(PaddingEnabled enabled) {
176 switch (enabled) {
177 case PaddingEnabled::DEFAULT:
178 return "DEFAULT";
179 case PaddingEnabled::ENABLED:
180 return "ENABLED";
181 case PaddingEnabled::DISABLED:
182 return "DISABLED";
183 default:
184 return "UNKNOWN";
185 }
186 }
187
188 protected:
SetUp()189 virtual void SetUp() {
190 uint32_t modelIndex, executionIndex;
191 switch (kUnspecifiedOperand) {
192 case UnspecifiedOperand::INPUT_MANDATORY:
193 modelIndex = kIndex0_Model;
194 executionIndex = kIndex0_Execution;
195 mBadIndexChoices = {kIndexCount, modelIndex, executionIndex};
196 mOperandLocationChoices = {OperandLocation::BUFFER, OperandLocation::MEMORY};
197 mBufferSizeChoices = {BufferSize::LESS, BufferSize::EQUAL, BufferSize::MORE};
198 mEnablePaddingChoices = {PaddingEnabled::DEFAULT, PaddingEnabled::ENABLED,
199 PaddingEnabled::DISABLED};
200 break;
201 case UnspecifiedOperand::CONST_MANDATORY:
202 modelIndex = kIndex1_Model;
203 executionIndex = kIndexCount;
204 mBadIndexChoices = {kIndexCount, modelIndex};
205 mOperandLocationChoices = {OperandLocation::BUFFER, OperandLocation::MEMORY};
206 break;
207 case UnspecifiedOperand::TEMPORARY_VARIABLE:
208 modelIndex = kIndex2_Model;
209 executionIndex = kIndexCount;
210 mBadIndexChoices = {kIndexCount, modelIndex};
211 mOperandLocationChoices = {OperandLocation::BUFFER};
212 break;
213 case UnspecifiedOperand::INPUT_OPTIONAL:
214 modelIndex = kIndex3_Model;
215 executionIndex = kIndex3_Execution;
216 mBadIndexChoices = {kIndexCount};
217 mOptionalType = OptionalType::INPUT;
218 mOperandLocationChoices = {OperandLocation::BUFFER};
219 break;
220 case UnspecifiedOperand::CONST_OPTIONAL:
221 modelIndex = kIndex3_Model;
222 executionIndex = kIndexCount;
223 mBadIndexChoices = {kIndexCount};
224 mOperandLocationChoices = {OperandLocation::BUFFER};
225 break;
226 case UnspecifiedOperand::OUTPUT:
227 modelIndex = kIndex4_Model;
228 executionIndex = kIndex4_Execution;
229 mBadIndexChoices = {kIndexCount, modelIndex, executionIndex};
230 mOperandLocationChoices = {OperandLocation::BUFFER, OperandLocation::MEMORY};
231 mBufferSizeChoices = {BufferSize::LESS, BufferSize::EQUAL, BufferSize::MORE};
232 mEnablePaddingChoices = {PaddingEnabled::DEFAULT, PaddingEnabled::ENABLED,
233 PaddingEnabled::DISABLED};
234 break;
235 default:
236 break;
237 }
238 std::vector<SpecificationLevel> levels{
239 SpecificationLevel::UNSPECIFIED_DIM, SpecificationLevel::FULLY_SPECIFIED,
240 SpecificationLevel::UNSPECIFIED_DIM, SpecificationLevel::FULLY_SPECIFIED,
241 SpecificationLevel::UNSPECIFIED_DIM, SpecificationLevel::FULLY_SPECIFIED,
242 SpecificationLevel::FULLY_SPECIFIED, SpecificationLevel::FULLY_SPECIFIED};
243 levels[modelIndex] = kSpecificationLevelModel;
244 if (executionIndex < kIndexCount) {
245 levels[executionIndex] = kSpecificationLevelExecution;
246 }
247 mSpecificationLevels = std::move(levels);
248 }
249
getType(uint32_t index,const std::vector<uint32_t> & dim)250 OperandType getType(uint32_t index, const std::vector<uint32_t>& dim) {
251 const SpecificationLevel l = mSpecificationLevels[index];
252 std::vector<uint32_t> setDim;
253 if (l != SpecificationLevel::UNSPECIFIED_RANK) {
254 for (auto d : dim) {
255 if (d == 0) {
256 setDim.push_back(mBadIndex != index ? kDimAGood : kDimABad);
257 } else {
258 setDim.push_back(l == SpecificationLevel::FULLY_SPECIFIED ? d : 0);
259 }
260 }
261 }
262 float scale = mOperandTypes[index] == Type::TENSOR_QUANT8_ASYMM ? 1.0 : 0.0;
263 return OperandType(mOperandTypes[index], setDim, scale, 0);
264 }
265
getSize(uint32_t index,const std::vector<uint32_t> & dim,BufferSize s=BufferSize::EQUAL)266 uint32_t getSize(uint32_t index, const std::vector<uint32_t>& dim,
267 BufferSize s = BufferSize::EQUAL) {
268 uint32_t n = 1;
269 for (auto d : dim) {
270 n *= (d == 0 ? (mBadIndex != index ? kDimAGood : kDimABad) : d);
271 }
272 if (s == BufferSize::LESS) {
273 n /= 2;
274 } else if (s == BufferSize::MORE) {
275 n *= 2;
276 }
277 return n;
278 };
279
280 template <typename T>
setInOut(Execution * execution,uint32_t index,uint32_t opIndex,const std::vector<uint32_t> & dim,void * buffer,const SharedMemoryForTest * memory,InOutType inOutType,BufferSize bufferSize=BufferSize::EQUAL)281 Result setInOut(Execution* execution, uint32_t index, uint32_t opIndex,
282 const std::vector<uint32_t>& dim, void* buffer,
283 const SharedMemoryForTest* memory, InOutType inOutType,
284 BufferSize bufferSize = BufferSize::EQUAL) {
285 const auto kLevel = mSpecificationLevels[index];
286 size_t size = (buffer == nullptr) ? 0 : getSize(index, dim, bufferSize) * sizeof(T);
287 auto type = getType(index, dim);
288 ANeuralNetworksOperandType* t =
289 (kLevel == SpecificationLevel::UNSPECIFIED_TYPE) ? nullptr : &type.operandType;
290 if (mOperandLocation == OperandLocation::MEMORY && memory != nullptr) {
291 if (inOutType == InOutType::INPUT) {
292 return execution->setInputFromMemory(opIndex, memory->getMemory(), 0, size, t);
293 } else {
294 return execution->setOutputFromMemory(opIndex, memory->getMemory(), 0, size, t);
295 }
296 } else {
297 if (inOutType == InOutType::INPUT) {
298 return execution->setInput(opIndex, buffer, size, t);
299 } else {
300 return execution->setOutput(opIndex, buffer, size, t);
301 }
302 }
303 return Result::NO_ERROR;
304 }
305
306 template <typename T, Type TensorType>
TestOne()307 void TestOne() {
308 // Phase 1: Build Model
309 Model model;
310 auto type0 = getType(kIndex0_Model, {kValueA, kValueB});
311 auto type1 = getType(kIndex1_Model, {kValueA, 1});
312 auto type2 = getType(kIndex2_Model, {kValueA, kValueB});
313 auto type3 = getType(kIndex3_Model, {2});
314 auto type4 = getType(kIndex4_Model, {kValueB, kValueA});
315 OperandType typeActivation(Type::INT32, {}); // activation
316
317 auto op0 = model.addOperand(&type0);
318 auto op1 = model.addOperand(&type1);
319 auto op2 = model.addOperand(&type2);
320 auto op3 = model.addOperand(&type3);
321 auto op4 = model.addOperand(&type4);
322 auto act = model.addOperand(&typeActivation);
323
324 T bufferOp1[2] = {1, 2};
325 SharedMemoryForTest memoryOp1;
326 memoryOp1.initialize(sizeof(bufferOp1), bufferOp1);
327 if (mOperandLocation == OperandLocation::BUFFER) {
328 model.setOperandValue(op1, bufferOp1, sizeof(bufferOp1));
329 } else {
330 model.setOperandValueFromMemory(op1, memoryOp1.getMemory(), 0, sizeof(bufferOp1));
331 }
332 int32_t kActivation = 0;
333 model.setOperandValue(act, &kActivation, sizeof(int32_t));
334 if (mOptionalType == OptionalType::CONST) {
335 model.setOperandValue(op3, nullptr, 0);
336 }
337
338 model.addOperation(ANEURALNETWORKS_ADD, {op0, op1, act}, {op2});
339 model.addOperation(ANEURALNETWORKS_TRANSPOSE, {op2, op3}, {op4});
340 if (mOptionalType == OptionalType::CONST) {
341 model.identifyInputsAndOutputs({op0}, {op4});
342 } else {
343 model.identifyInputsAndOutputs({op0, op3}, {op4});
344 }
345
346 bool expected = expectModelIsValid();
347 ASSERT_EQ(model.isValid(), expected);
348 Result result = model.finish();
349 if (expected) {
350 ASSERT_EQ(result, Result::NO_ERROR);
351 } else {
352 // There is no contract (yet) for specific errors in NeuralNetworks.h,
353 // so we just assert on not being successful.
354 ASSERT_NE(result, Result::NO_ERROR);
355 return;
356 }
357
358 // Phase 2: Compile Model, should always pass
359 Compilation compilation(&model);
360 ASSERT_EQ(compilation.finish(), Result::NO_ERROR);
361
362 std::vector<uint32_t> valueBChoices = {1, 2};
363 for (const auto valueB : valueBChoices) {
364 SCOPED_TRACE("ValueB: " + std::to_string(valueB));
365 if (valueB != kValueB &&
366 (mSpecificationLevels[kIndex0_Model] == SpecificationLevel::FULLY_SPECIFIED ||
367 mSpecificationLevels[kIndex2_Model] == SpecificationLevel::FULLY_SPECIFIED ||
368 mSpecificationLevels[kIndex4_Model] == SpecificationLevel::FULLY_SPECIFIED)) {
369 continue;
370 }
371
372 // Phase 3: Set Execution Input/Output
373 Execution execution(&compilation);
374
375 // Enable padding
376 if (mEnablePadding == PaddingEnabled::ENABLED) {
377 ASSERT_EQ(execution.enableInputAndOutputPadding(true), Result::NO_ERROR);
378 } else if (mEnablePadding == PaddingEnabled::DISABLED) {
379 ASSERT_EQ(execution.enableInputAndOutputPadding(false), Result::NO_ERROR);
380 }
381
382 // Set input0
383 Result result;
384 T bufferOp0[6] = {1, 2, 3, 4, 5, 6};
385 SharedMemoryForTest memoryOp0;
386 memoryOp0.initialize(sizeof(bufferOp0), bufferOp0);
387 result = setInOut<T>(&execution, kIndex0_Execution, 0, {kValueA, valueB}, bufferOp0,
388 &memoryOp0, InOutType::INPUT, mBufferSize);
389 ASSERT_EQ(result, expectSetInput0());
390 if (result != Result::NO_ERROR) continue;
391
392 // Set input1, omitted
393 if (mOptionalType == OptionalType::INPUT) {
394 result = setInOut<T>(&execution, kIndex3_Execution, 1, {2}, nullptr, nullptr,
395 InOutType::INPUT);
396 ASSERT_EQ(result, expectSetInput1());
397 if (result != Result::NO_ERROR) continue;
398 }
399
400 // Set output0
401 T bufferOp4[16];
402 SharedMemoryForTest memoryOp4;
403 memoryOp4.initialize(sizeof(bufferOp4), bufferOp4);
404 result = setInOut<T>(&execution, kIndex4_Execution, 0, {valueB, kValueA}, bufferOp4,
405 &memoryOp4, InOutType::OUTPUT, mBufferSize);
406 ASSERT_EQ(result, expectSetOutput0());
407 if (result != Result::NO_ERROR) continue;
408
409 // Phase 4: Compute and Compare Results
410 result = execution.compute();
411 ASSERT_EQ(result, expectCompute());
412 if (result == Result::OP_FAILED) continue;
413
414 std::vector<uint32_t> outputShape;
415 ASSERT_EQ(execution.getOutputOperandDimensions(0, &outputShape), result);
416 std::vector<uint32_t> expectedOutputShape = {valueB, kDimAGood};
417 ASSERT_EQ(outputShape, expectedOutputShape);
418 if (result == Result::OUTPUT_INSUFFICIENT_SIZE) continue;
419
420 const T* outputBuffer = mOperandLocation == OperandLocation::MEMORY
421 ? reinterpret_cast<const T*>(memoryOp4.getBuffer())
422 : bufferOp4;
423 T expected_1x2[2] = {2, 4};
424 T expected_2x2[4] = {2, 5, 3, 6};
425 for (uint32_t i = 0; i < kDimAGood * valueB; i++) {
426 ASSERT_EQ(outputBuffer[i], valueB == 1 ? expected_1x2[i] : expected_2x2[i]);
427 }
428 }
429 }
430
431 // Expect invalid model for the following cases
432 // - op1 is not fully specified (const operand must be fully specified)
433 // - op1 has bad dimension value (const operand size is checked with buffer size)
expectModelIsValid()434 bool expectModelIsValid() {
435 const auto kLevel1_Model = mSpecificationLevels[kIndex1_Model];
436 if (kLevel1_Model != SpecificationLevel::FULLY_SPECIFIED || mBadIndex == kIndex1_Model) {
437 return false;
438 }
439 return true;
440 }
441
442 // Expect BAD_DATA on input0 for the following cases
443 // - the provided type is not fully specified
444 // - the provided type does not agree with the type set at model construction time
445 // - no type is provided and the type is not fully specified at model construction time
446 // - the buffer size (length) is less than needed
447 // - the buffer size (length) is more than needed and padding is not enabled
expectSetInput0()448 Result expectSetInput0() {
449 const auto kLevel0_Model = mSpecificationLevels[kIndex0_Model];
450 const auto kLevel0_Execution = mSpecificationLevels[kIndex0_Execution];
451 switch (kLevel0_Execution) {
452 case SpecificationLevel::UNSPECIFIED_DIM:
453 case SpecificationLevel::UNSPECIFIED_RANK:
454 return Result::BAD_DATA;
455 case SpecificationLevel::FULLY_SPECIFIED:
456 if ((mBadIndex == kIndex0_Execution || mBadIndex == kIndex0_Model) &&
457 kLevel0_Model != SpecificationLevel::UNSPECIFIED_RANK) {
458 return Result::BAD_DATA;
459 }
460 if (mBufferSize == BufferSize::LESS) {
461 return Result::BAD_DATA;
462 }
463 if (mEnablePadding != PaddingEnabled::ENABLED && mBufferSize == BufferSize::MORE) {
464 return Result::BAD_DATA;
465 }
466 break;
467 case SpecificationLevel::UNSPECIFIED_TYPE:
468 if (kLevel0_Model == SpecificationLevel::UNSPECIFIED_DIM ||
469 kLevel0_Model == SpecificationLevel::UNSPECIFIED_RANK) {
470 return Result::BAD_DATA;
471 }
472 if (mBufferSize == BufferSize::LESS) {
473 return Result::BAD_DATA;
474 }
475 if (mEnablePadding != PaddingEnabled::ENABLED && mBufferSize == BufferSize::MORE) {
476 return Result::BAD_DATA;
477 }
478 // This is the case when the dimension is incorrectly specified in the model.
479 // With incorrect dimension, the needed size is 2 * 3 = 6 data type size.
480 // BufferSize::EQUAL (2 * 2 = 4 data type size) cannot provide enough length.
481 if (mBadIndex == kIndex0_Model && mBufferSize == BufferSize::EQUAL) {
482 return Result::BAD_DATA;
483 }
484 break;
485 default:
486 break;
487 }
488 return Result::NO_ERROR;
489 }
490
491 // Expect BAD_DATA on input1 for the following cases
492 // - the provided type is less detailed as the type set at model construction time
expectSetInput1()493 Result expectSetInput1() {
494 const auto kLevel3_Model = mSpecificationLevels[kIndex3_Model];
495 const auto kLevel3_Execution = mSpecificationLevels[kIndex3_Execution];
496 switch (kLevel3_Execution) {
497 case SpecificationLevel::UNSPECIFIED_DIM:
498 if (kLevel3_Model == SpecificationLevel::FULLY_SPECIFIED) {
499 return Result::BAD_DATA;
500 }
501 break;
502 case SpecificationLevel::UNSPECIFIED_RANK:
503 if (kLevel3_Model != SpecificationLevel::UNSPECIFIED_RANK) {
504 return Result::BAD_DATA;
505 }
506 break;
507 default:
508 break;
509 }
510 return Result::NO_ERROR;
511 }
512
513 // Expect BAD_DATA on output0 for the following cases
514 // - the provided type is less detailed as the type set at model construction time
515 // - the provided type does not agree with the type set at model construction time
516 // - the buffer size (length) is less than needed
517 // - the buffer size (length) is more than needed and padding is not enabled
expectSetOutput0()518 Result expectSetOutput0() {
519 const auto kLevel4_Model = mSpecificationLevels[kIndex4_Model];
520 const auto kLevel4_Execution = mSpecificationLevels[kIndex4_Execution];
521 switch (kLevel4_Execution) {
522 case SpecificationLevel::UNSPECIFIED_DIM:
523 if (kLevel4_Model == SpecificationLevel::FULLY_SPECIFIED ||
524 (kLevel4_Model == SpecificationLevel::UNSPECIFIED_DIM &&
525 (mBadIndex == kIndex4_Model || mBadIndex == kIndex4_Execution))) {
526 return Result::BAD_DATA;
527 }
528 break;
529 case SpecificationLevel::UNSPECIFIED_RANK:
530 if (kLevel4_Model != SpecificationLevel::UNSPECIFIED_RANK) {
531 return Result::BAD_DATA;
532 }
533 break;
534 case SpecificationLevel::FULLY_SPECIFIED:
535 if ((mBadIndex == kIndex4_Model || mBadIndex == kIndex4_Execution) &&
536 kLevel4_Model != SpecificationLevel::UNSPECIFIED_RANK) {
537 return Result::BAD_DATA;
538 }
539 if (mBufferSize == BufferSize::LESS) {
540 return Result::BAD_DATA;
541 }
542 if (mEnablePadding != PaddingEnabled::ENABLED && mBufferSize == BufferSize::MORE) {
543 return Result::BAD_DATA;
544 }
545 break;
546 case SpecificationLevel::UNSPECIFIED_TYPE:
547 if (kLevel4_Model == SpecificationLevel::FULLY_SPECIFIED) {
548 if (mBufferSize == BufferSize::LESS) {
549 return Result::BAD_DATA;
550 }
551 if (mEnablePadding != PaddingEnabled::ENABLED &&
552 mBufferSize == BufferSize::MORE) {
553 return Result::BAD_DATA;
554 }
555 // This is the case when the dimension is incorrectly specified in the model.
556 // With incorrect dimension, the needed size is 2 * 3 = 6 data type size.
557 // BufferSize::EQUAL (2 * 2 = 4 data type size) cannot provide enough length.
558 if (mBadIndex == kIndex4_Model && mBufferSize == BufferSize::EQUAL) {
559 return Result::BAD_DATA;
560 }
561 }
562 break;
563 default:
564 break;
565 }
566 return Result::NO_ERROR;
567 }
568
569 // Expect failure for the following cases
570 // - one of the operands has bad dimension -> OP_FAILED
571 // - insufficient output buffer -> OUTPUT_INSUFFICIENT_SIZE
expectCompute()572 Result expectCompute() {
573 if (mBadIndex < 8) {
574 return Result::OP_FAILED;
575 } else if (mBufferSize == BufferSize::LESS) {
576 return Result::OUTPUT_INSUFFICIENT_SIZE;
577 }
578 return Result::NO_ERROR;
579 }
580
581 // Iterate over combinations of
582 // - mBadIndexChoices: which operand has incorrect dimension
583 // - mOperandLocationChoices: where the operand reside, buffer or shared memory
584 // - mBufferSizeChoices: whether the provided buffer/memory size is sufficient
585 // - mEnablePaddingChoices: whether input/output memory padding is enabled
586 template <typename T, Type TensorType>
TestAll()587 void TestAll() {
588 SCOPED_TRACE("Model: " + toString(kSpecificationLevelModel));
589 SCOPED_TRACE("Execution: " + toString(kSpecificationLevelExecution));
590 mOperandTypes = {TensorType, TensorType, TensorType, Type::TENSOR_INT32,
591 TensorType, TensorType, Type::TENSOR_INT32, TensorType};
592 for (const auto kBadIndex : mBadIndexChoices) {
593 mBadIndex = kBadIndex;
594 SCOPED_TRACE("Bad Index: " + std::to_string(mBadIndex));
595 if (mBadIndex < 8 &&
596 (mSpecificationLevels[mBadIndex] == SpecificationLevel::UNSPECIFIED_RANK ||
597 mSpecificationLevels[mBadIndex] == SpecificationLevel::UNSPECIFIED_TYPE)) {
598 continue;
599 }
600 for (const auto kOperandLocation : mOperandLocationChoices) {
601 mOperandLocation = kOperandLocation;
602 SCOPED_TRACE("Operand Location: " + toString(mOperandLocation));
603 for (const auto kBufferSize : mBufferSizeChoices) {
604 mBufferSize = kBufferSize;
605 SCOPED_TRACE("Buffer Size: " + toString(mBufferSize));
606 for (const auto kEnablePadding : mEnablePaddingChoices) {
607 mEnablePadding = kEnablePadding;
608 SCOPED_TRACE("Enable Padding: " + toString(mEnablePadding));
609 TestOne<T, TensorType>();
610 }
611 }
612 }
613 }
614 }
615
616 const UnspecifiedOperand kUnspecifiedOperand = std::get<0>(GetParam());
617 const SpecificationLevel kSpecificationLevelModel = std::get<1>(GetParam());
618 const SpecificationLevel kSpecificationLevelExecution = std::get<2>(GetParam());
619
620 std::vector<SpecificationLevel> mSpecificationLevels;
621 std::vector<Type> mOperandTypes;
622 OptionalType mOptionalType = OptionalType::CONST;
623
624 // Iterate all combinations in TestAll()
625 std::vector<uint32_t> mBadIndexChoices;
626 std::vector<OperandLocation> mOperandLocationChoices;
627 std::vector<BufferSize> mBufferSizeChoices = {BufferSize::EQUAL};
628 std::vector<PaddingEnabled> mEnablePaddingChoices = {PaddingEnabled::DEFAULT};
629
630 uint32_t mBadIndex;
631 OperandLocation mOperandLocation;
632 BufferSize mBufferSize;
633 PaddingEnabled mEnablePadding;
634 };
635
TEST_P(UnspecifiedDimensionsTest,Float32)636 TEST_P(UnspecifiedDimensionsTest, Float32) {
637 TestAll<float, Type::TENSOR_FLOAT32>();
638 }
639
TEST_P(UnspecifiedDimensionsTest,Quant8)640 TEST_P(UnspecifiedDimensionsTest, Quant8) {
641 TestAll<uint8_t, Type::TENSOR_QUANT8_ASYMM>();
642 }
643
TEST_P(UnspecifiedDimensionsTest,Float16)644 TEST_P(UnspecifiedDimensionsTest, Float16) {
645 TestAll<_Float16, Type::TENSOR_FLOAT16>();
646 }
647
648 static const auto kAllSpecificationLevelsModel =
649 testing::Values(SpecificationLevel::FULLY_SPECIFIED, SpecificationLevel::UNSPECIFIED_DIM,
650 SpecificationLevel::UNSPECIFIED_RANK);
651 static const auto kAllSpecificationLevelsExecution =
652 testing::Values(SpecificationLevel::FULLY_SPECIFIED, SpecificationLevel::UNSPECIFIED_DIM,
653 SpecificationLevel::UNSPECIFIED_RANK, SpecificationLevel::UNSPECIFIED_TYPE);
654 static const auto kFullySpecified = testing::Values(SpecificationLevel::FULLY_SPECIFIED);
655
656 INSTANTIATE_TEST_SUITE_P(ModelInputTest, UnspecifiedDimensionsTest,
657 testing::Combine(testing::Values(UnspecifiedOperand::INPUT_MANDATORY),
658 kAllSpecificationLevelsModel,
659 kAllSpecificationLevelsExecution));
660
661 INSTANTIATE_TEST_SUITE_P(ConstantParameterTest, UnspecifiedDimensionsTest,
662 testing::Combine(testing::Values(UnspecifiedOperand::CONST_MANDATORY),
663 kAllSpecificationLevelsModel, kFullySpecified));
664
665 INSTANTIATE_TEST_SUITE_P(TemporaryVariableTest, UnspecifiedDimensionsTest,
666 testing::Combine(testing::Values(UnspecifiedOperand::TEMPORARY_VARIABLE),
667 kAllSpecificationLevelsModel, kFullySpecified));
668
669 INSTANTIATE_TEST_SUITE_P(OptionalConstantTest, UnspecifiedDimensionsTest,
670 testing::Combine(testing::Values(UnspecifiedOperand::CONST_OPTIONAL),
671 kAllSpecificationLevelsModel, kFullySpecified));
672
673 INSTANTIATE_TEST_SUITE_P(OptionalInputTest, UnspecifiedDimensionsTest,
674 testing::Combine(testing::Values(UnspecifiedOperand::INPUT_OPTIONAL),
675 kAllSpecificationLevelsModel,
676 kAllSpecificationLevelsExecution));
677
678 INSTANTIATE_TEST_SUITE_P(ModelOutputTest, UnspecifiedDimensionsTest,
679 testing::Combine(testing::Values(UnspecifiedOperand::OUTPUT),
680 kAllSpecificationLevelsModel,
681 kAllSpecificationLevelsExecution));
682
683 } // end namespace
684