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