Home | History | Annotate | Download | only in functional
      1 /*
      2  * Copyright (C) 2018 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 #define LOG_TAG "neuralnetworks_hidl_hal_test"
     18 
     19 #include "VtsHalNeuralnetworks.h"
     20 
     21 #include "Callbacks.h"
     22 #include "TestHarness.h"
     23 #include "Utils.h"
     24 
     25 #include <android-base/logging.h>
     26 #include <android/hidl/memory/1.0/IMemory.h>
     27 #include <hidlmemory/mapping.h>
     28 
     29 namespace android {
     30 namespace hardware {
     31 namespace neuralnetworks {
     32 namespace V1_0 {
     33 namespace vts {
     34 namespace functional {
     35 
     36 using ::android::hardware::neuralnetworks::V1_2::implementation::ExecutionCallback;
     37 using ::android::hidl::memory::V1_0::IMemory;
     38 using test_helper::for_all;
     39 using test_helper::MixedTyped;
     40 using test_helper::MixedTypedExample;
     41 
     42 ///////////////////////// UTILITY FUNCTIONS /////////////////////////
     43 
     44 // Primary validation function. This function will take a valid request, apply a
     45 // mutation to it to invalidate the request, then pass it to interface calls
     46 // that use the request. Note that the request here is passed by value, and any
     47 // mutation to the request does not leave this function.
     48 static void validate(const sp<IPreparedModel>& preparedModel, const std::string& message,
     49                      Request request, const std::function<void(Request*)>& mutation) {
     50     mutation(&request);
     51     SCOPED_TRACE(message + " [execute]");
     52 
     53     sp<ExecutionCallback> executionCallback = new ExecutionCallback();
     54     ASSERT_NE(nullptr, executionCallback.get());
     55     Return<ErrorStatus> executeLaunchStatus = preparedModel->execute(request, executionCallback);
     56     ASSERT_TRUE(executeLaunchStatus.isOk());
     57     ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, static_cast<ErrorStatus>(executeLaunchStatus));
     58 
     59     executionCallback->wait();
     60     ErrorStatus executionReturnStatus = executionCallback->getStatus();
     61     ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, executionReturnStatus);
     62 }
     63 
     64 // Delete element from hidl_vec. hidl_vec doesn't support a "remove" operation,
     65 // so this is efficiently accomplished by moving the element to the end and
     66 // resizing the hidl_vec to one less.
     67 template <typename Type>
     68 static void hidl_vec_removeAt(hidl_vec<Type>* vec, uint32_t index) {
     69     if (vec) {
     70         std::rotate(vec->begin() + index, vec->begin() + index + 1, vec->end());
     71         vec->resize(vec->size() - 1);
     72     }
     73 }
     74 
     75 template <typename Type>
     76 static uint32_t hidl_vec_push_back(hidl_vec<Type>* vec, const Type& value) {
     77     // assume vec is valid
     78     const uint32_t index = vec->size();
     79     vec->resize(index + 1);
     80     (*vec)[index] = value;
     81     return index;
     82 }
     83 
     84 ///////////////////////// REMOVE INPUT ////////////////////////////////////
     85 
     86 static void removeInputTest(const sp<IPreparedModel>& preparedModel, const Request& request) {
     87     for (size_t input = 0; input < request.inputs.size(); ++input) {
     88         const std::string message = "removeInput: removed input " + std::to_string(input);
     89         validate(preparedModel, message, request,
     90                  [input](Request* request) { hidl_vec_removeAt(&request->inputs, input); });
     91     }
     92 }
     93 
     94 ///////////////////////// REMOVE OUTPUT ////////////////////////////////////
     95 
     96 static void removeOutputTest(const sp<IPreparedModel>& preparedModel, const Request& request) {
     97     for (size_t output = 0; output < request.outputs.size(); ++output) {
     98         const std::string message = "removeOutput: removed Output " + std::to_string(output);
     99         validate(preparedModel, message, request,
    100                  [output](Request* request) { hidl_vec_removeAt(&request->outputs, output); });
    101     }
    102 }
    103 
    104 ///////////////////////////// ENTRY POINT //////////////////////////////////
    105 
    106 std::vector<Request> createRequests(const std::vector<MixedTypedExample>& examples) {
    107     const uint32_t INPUT = 0;
    108     const uint32_t OUTPUT = 1;
    109 
    110     std::vector<Request> requests;
    111 
    112     for (const MixedTypedExample& example : examples) {
    113         const MixedTyped& inputs = example.operands.first;
    114         const MixedTyped& outputs = example.operands.second;
    115 
    116         std::vector<RequestArgument> inputs_info, outputs_info;
    117         uint32_t inputSize = 0, outputSize = 0;
    118 
    119         // This function only partially specifies the metadata (vector of RequestArguments).
    120         // The contents are copied over below.
    121         for_all(inputs, [&inputs_info, &inputSize](int index, auto, auto s) {
    122             if (inputs_info.size() <= static_cast<size_t>(index)) inputs_info.resize(index + 1);
    123             RequestArgument arg = {
    124                 .location = {.poolIndex = INPUT, .offset = 0, .length = static_cast<uint32_t>(s)},
    125                 .dimensions = {},
    126             };
    127             RequestArgument arg_empty = {
    128                 .hasNoValue = true,
    129             };
    130             inputs_info[index] = s ? arg : arg_empty;
    131             inputSize += s;
    132         });
    133         // Compute offset for inputs 1 and so on
    134         {
    135             size_t offset = 0;
    136             for (auto& i : inputs_info) {
    137                 if (!i.hasNoValue) i.location.offset = offset;
    138                 offset += i.location.length;
    139             }
    140         }
    141 
    142         // Go through all outputs, initialize RequestArgument descriptors
    143         for_all(outputs, [&outputs_info, &outputSize](int index, auto, auto s) {
    144             if (outputs_info.size() <= static_cast<size_t>(index)) outputs_info.resize(index + 1);
    145             RequestArgument arg = {
    146                 .location = {.poolIndex = OUTPUT, .offset = 0, .length = static_cast<uint32_t>(s)},
    147                 .dimensions = {},
    148             };
    149             outputs_info[index] = arg;
    150             outputSize += s;
    151         });
    152         // Compute offset for outputs 1 and so on
    153         {
    154             size_t offset = 0;
    155             for (auto& i : outputs_info) {
    156                 i.location.offset = offset;
    157                 offset += i.location.length;
    158             }
    159         }
    160         std::vector<hidl_memory> pools = {nn::allocateSharedMemory(inputSize),
    161                                           nn::allocateSharedMemory(outputSize)};
    162         if (pools[INPUT].size() == 0 || pools[OUTPUT].size() == 0) {
    163             return {};
    164         }
    165 
    166         // map pool
    167         sp<IMemory> inputMemory = mapMemory(pools[INPUT]);
    168         if (inputMemory == nullptr) {
    169             return {};
    170         }
    171         char* inputPtr = reinterpret_cast<char*>(static_cast<void*>(inputMemory->getPointer()));
    172         if (inputPtr == nullptr) {
    173             return {};
    174         }
    175 
    176         // initialize pool
    177         inputMemory->update();
    178         for_all(inputs, [&inputs_info, inputPtr](int index, auto p, auto s) {
    179             char* begin = (char*)p;
    180             char* end = begin + s;
    181             // TODO: handle more than one input
    182             std::copy(begin, end, inputPtr + inputs_info[index].location.offset);
    183         });
    184         inputMemory->commit();
    185 
    186         requests.push_back({.inputs = inputs_info, .outputs = outputs_info, .pools = pools});
    187     }
    188 
    189     return requests;
    190 }
    191 
    192 void ValidationTest::validateRequests(const sp<IPreparedModel>& preparedModel,
    193                                       const std::vector<Request>& requests) {
    194     // validate each request
    195     for (const Request& request : requests) {
    196         removeInputTest(preparedModel, request);
    197         removeOutputTest(preparedModel, request);
    198     }
    199 }
    200 
    201 }  // namespace functional
    202 }  // namespace vts
    203 }  // namespace V1_0
    204 }  // namespace neuralnetworks
    205 }  // namespace hardware
    206 }  // namespace android
    207