C++程序  |  339行  |  14.96 KB

/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "HalInterfaces.h"
#include "Manager.h"
#include "NeuralNetworks.h"
#include "NeuralNetworksExtensions.h"
#include "NeuralNetworksWrapperExtensions.h"
#include "TestNeuralNetworksWrapper.h"
#include "TypeManager.h"
#include "Utils.h"
#include "ValidateHal.h"

#include <gtest/gtest.h>

#include "FibonacciDriver.h"
#include "FibonacciExtension.h"

namespace android {
namespace nn {
namespace {

using ::android::nn::test_wrapper::ExtensionModel;
using ::android::nn::test_wrapper::ExtensionOperandParams;
using ::android::nn::test_wrapper::ExtensionOperandType;
using ::android::nn::test_wrapper::Type;

class FibonacciExtensionTest : public ::testing::Test {
   protected:
    virtual void SetUp() {
        if (DeviceManager::get()->getUseCpuOnly()) {
            // This test requires the use a custom driver.
            GTEST_SKIP();
        }

        // Real world extension tests should run against actual hardware
        // implementations, but there is no hardware supporting the test
        // extension. Hence the sample software driver.
        DeviceManager::get()->forTest_registerDevice(sample_driver::FibonacciDriver::kDriverName,
                                                     new sample_driver::FibonacciDriver());
        // Discover extensions provided by registered devices.
        TypeManager::get()->forTest_reset();

        uint32_t numDevices = 0;
        ASSERT_EQ(ANeuralNetworks_getDeviceCount(&numDevices), ANEURALNETWORKS_NO_ERROR);
        ANeuralNetworksDevice* fibonacciDevice = nullptr;
        ANeuralNetworksDevice* cpuDevice = nullptr;
        for (uint32_t i = 0; i < numDevices; i++) {
            ANeuralNetworksDevice* device = nullptr;
            EXPECT_EQ(ANeuralNetworks_getDevice(i, &device), ANEURALNETWORKS_NO_ERROR);
            bool supportsFibonacciExtension;
            ASSERT_EQ(ANeuralNetworksDevice_getExtensionSupport(
                              device, TEST_VENDOR_FIBONACCI_EXTENSION_NAME,
                              &supportsFibonacciExtension),
                      ANEURALNETWORKS_NO_ERROR);
            if (supportsFibonacciExtension) {
                ASSERT_EQ(fibonacciDevice, nullptr) << "Found multiple Fibonacci drivers";
                fibonacciDevice = device;
            } else if (DeviceManager::get()->forTest_isCpuDevice(device)) {
                ASSERT_EQ(cpuDevice, nullptr) << "Found multiple CPU drivers";
                cpuDevice = device;
            }
        }
        ASSERT_NE(fibonacciDevice, nullptr) << "Expecting Fibonacci driver to be available";
        ASSERT_NE(cpuDevice, nullptr) << "Expecting CPU driver to be available";
        mDevices = {fibonacciDevice, cpuDevice};
    }

    virtual void TearDown() {
        if (mExecution) {
            ANeuralNetworksExecution_free(mExecution);
        }
        if (mCompilation) {
            ANeuralNetworksCompilation_free(mCompilation);
        }
        DeviceManager::get()->forTest_reInitializeDeviceList();
        TypeManager::get()->forTest_reset();
    }

    void checkSupportedOperations(const std::vector<bool>& expected) {
        const uint32_t kMaxNumberOperations = 256;
        EXPECT_LE(expected.size(), kMaxNumberOperations);
        bool supported[kMaxNumberOperations] = {false};
        EXPECT_EQ(ANeuralNetworksModel_getSupportedOperationsForDevices(
                          mModel.getHandle(), mDevices.data(), mDevices.size(), supported),
                  ANEURALNETWORKS_NO_ERROR);
        for (size_t i = 0; i < expected.size(); ++i) {
            SCOPED_TRACE(::testing::Message() << "i = " << i);
            EXPECT_EQ(supported[i], expected[i]);
        }
    }

    void prepareForExecution() {
        ASSERT_EQ(ANeuralNetworksCompilation_createForDevices(mModel.getHandle(), mDevices.data(),
                                                              mDevices.size(), &mCompilation),
                  ANEURALNETWORKS_NO_ERROR);
        ASSERT_EQ(ANeuralNetworksCompilation_finish(mCompilation), ANEURALNETWORKS_NO_ERROR);
        ASSERT_EQ(ANeuralNetworksExecution_create(mCompilation, &mExecution),
                  ANEURALNETWORKS_NO_ERROR);
    }

    std::vector<ANeuralNetworksDevice*> mDevices;
    ANeuralNetworksExecution* mExecution = nullptr;
    ANeuralNetworksCompilation* mCompilation = nullptr;
    ExtensionModel mModel;
};

void addNopOperation(ExtensionModel* model, ExtensionOperandType inputType, uint32_t input,
                     uint32_t output) {
    // Our NOP operation is ADD, which has no extension type support.
    ASSERT_EQ(inputType.operandType.type, ANEURALNETWORKS_TENSOR_FLOAT32);
    ASSERT_EQ(inputType.dimensions.size(), 1u);

    uint32_t inputZeros = model->addOperand(&inputType);
    uint32_t inputSize = inputType.dimensions[0];
    uint32_t inputLength = sizeof(float) * inputSize;
    const float kZeros[100] = {};
    ASSERT_GE(sizeof(kZeros), inputLength);
    model->setOperandValue(inputZeros, &kZeros, inputLength);

    ExtensionOperandType scalarType(Type::INT32, {});
    uint32_t activation = model->addOperand(&scalarType);
    int32_t kNoActivation = ANEURALNETWORKS_FUSED_NONE;
    model->setOperandValue(activation, &kNoActivation, sizeof(kNoActivation));

    model->addOperation(ANEURALNETWORKS_ADD, {input, inputZeros, activation}, {output});
}

void createModel(ExtensionModel* model, ExtensionOperandType inputType,
                 ExtensionOperandType outputType, bool addNopOperations) {
    uint32_t fibonacciInput = model->addOperand(&inputType);
    uint32_t fibonacciOutput = model->addOperand(&outputType);

    uint32_t modelInput = addNopOperations ? model->addOperand(&inputType) : fibonacciInput;
    uint32_t modelOutput = addNopOperations ? model->addOperand(&outputType) : fibonacciOutput;

    if (addNopOperations) {
        addNopOperation(model, inputType, modelInput, fibonacciInput);
    }
    model->addOperation(model->getExtensionOperationType(TEST_VENDOR_FIBONACCI_EXTENSION_NAME,
                                                         TEST_VENDOR_FIBONACCI),
                        {fibonacciInput}, {fibonacciOutput});
    if (addNopOperations) {
        addNopOperation(model, outputType, fibonacciOutput, modelOutput);
    }

    model->identifyInputsAndOutputs({modelInput}, {modelOutput});
    model->finish();
    ASSERT_TRUE(model->isValid());
}

TEST_F(FibonacciExtensionTest, ModelWithExtensionOperandTypes) {
    constexpr uint32_t N = 10;
    constexpr double scale = 0.5;
    constexpr int64_t zeroPoint = 10;

    ExtensionOperandType inputType(
            static_cast<Type>(mModel.getExtensionOperandType(TEST_VENDOR_FIBONACCI_EXTENSION_NAME,
                                                             TEST_VENDOR_INT64)),
            {});
    ExtensionOperandType outputType(
            static_cast<Type>(mModel.getExtensionOperandType(TEST_VENDOR_FIBONACCI_EXTENSION_NAME,
                                                             TEST_VENDOR_TENSOR_QUANT64_ASYMM)),
            {N},
            ExtensionOperandParams(TestVendorQuant64AsymmParams{
                    .scale = scale,
                    .zeroPoint = zeroPoint,
            }));
    createModel(&mModel, inputType, outputType, /*addNopOperations=*/false);
    checkSupportedOperations({true});
    prepareForExecution();

    int64_t input = N;
    EXPECT_EQ(ANeuralNetworksExecution_setInput(mExecution, 0, nullptr, &input, sizeof(input)),
              ANEURALNETWORKS_NO_ERROR);

    int64_t output[N] = {};
    EXPECT_EQ(ANeuralNetworksExecution_setOutput(mExecution, 0, nullptr, &output, sizeof(output)),
              ANEURALNETWORKS_NO_ERROR);

    ASSERT_EQ(ANeuralNetworksExecution_compute(mExecution), ANEURALNETWORKS_NO_ERROR);

    EXPECT_EQ(output[0], 1 / scale + zeroPoint);
    EXPECT_EQ(output[1], 1 / scale + zeroPoint);
    EXPECT_EQ(output[2], 2 / scale + zeroPoint);
    EXPECT_EQ(output[3], 3 / scale + zeroPoint);
    EXPECT_EQ(output[4], 5 / scale + zeroPoint);
    EXPECT_EQ(output[5], 8 / scale + zeroPoint);
    EXPECT_EQ(output[6], 13 / scale + zeroPoint);
    EXPECT_EQ(output[7], 21 / scale + zeroPoint);
    EXPECT_EQ(output[8], 34 / scale + zeroPoint);
    EXPECT_EQ(output[9], 55 / scale + zeroPoint);
}

TEST_F(FibonacciExtensionTest, ModelWithTemporaries) {
    constexpr uint32_t N = 10;

    ExtensionOperandType inputType(Type::TENSOR_FLOAT32, {1});
    ExtensionOperandType outputType(Type::TENSOR_FLOAT32, {N});
    createModel(&mModel, inputType, outputType, /*addNopOperations=*/true);
    checkSupportedOperations({true, true, true});
    prepareForExecution();

    float input[] = {N};
    EXPECT_EQ(ANeuralNetworksExecution_setInput(mExecution, 0, nullptr, &input, sizeof(input)),
              ANEURALNETWORKS_NO_ERROR);

    float output[N] = {};
    EXPECT_EQ(ANeuralNetworksExecution_setOutput(mExecution, 0, nullptr, &output, sizeof(output)),
              ANEURALNETWORKS_NO_ERROR);

    ASSERT_EQ(ANeuralNetworksExecution_compute(mExecution), ANEURALNETWORKS_NO_ERROR);

    EXPECT_EQ(output[0], 1);
    EXPECT_EQ(output[1], 1);
    EXPECT_EQ(output[2], 2);
    EXPECT_EQ(output[3], 3);
    EXPECT_EQ(output[4], 5);
    EXPECT_EQ(output[5], 8);
    EXPECT_EQ(output[6], 13);
    EXPECT_EQ(output[7], 21);
    EXPECT_EQ(output[8], 34);
    EXPECT_EQ(output[9], 55);
}

TEST_F(FibonacciExtensionTest, InvalidInputType) {
    ExtensionOperandType inputType(Type::TENSOR_INT32, {1});  // Unsupported type.
    ExtensionOperandType outputType(Type::TENSOR_FLOAT32, {1});
    createModel(&mModel, inputType, outputType, /*addNopOperations=*/false);
    checkSupportedOperations({false});  // The driver reports that it doesn't support the operation.
    ASSERT_EQ(ANeuralNetworksCompilation_createForDevices(mModel.getHandle(), mDevices.data(),
                                                          mDevices.size(), &mCompilation),
              ANEURALNETWORKS_NO_ERROR);
    ASSERT_EQ(ANeuralNetworksCompilation_finish(mCompilation), ANEURALNETWORKS_BAD_DATA);
}

TEST_F(FibonacciExtensionTest, InvalidOutputType) {
    ExtensionOperandType inputType(Type::TENSOR_FLOAT32, {1});
    ExtensionOperandType outputType(Type::TENSOR_INT32, {1});  // Unsupported type.
    createModel(&mModel, inputType, outputType, /*addNopOperations=*/false);
    checkSupportedOperations({false});  // The driver reports that it doesn't support the operation.
    ASSERT_EQ(ANeuralNetworksCompilation_createForDevices(mModel.getHandle(), mDevices.data(),
                                                          mDevices.size(), &mCompilation),
              ANEURALNETWORKS_NO_ERROR);
    ASSERT_EQ(ANeuralNetworksCompilation_finish(mCompilation), ANEURALNETWORKS_BAD_DATA);
}

TEST_F(FibonacciExtensionTest, InvalidInputValue) {
    ExtensionOperandType inputType(Type::TENSOR_FLOAT32, {1});
    ExtensionOperandType outputType(Type::TENSOR_FLOAT32, {1});
    createModel(&mModel, inputType, outputType, /*addNopOperations=*/false);
    checkSupportedOperations({true});
    prepareForExecution();

    float input[] = {-1};  // Invalid input value.
    EXPECT_EQ(ANeuralNetworksExecution_setInput(mExecution, 0, nullptr, &input, sizeof(input)),
              ANEURALNETWORKS_NO_ERROR);

    float output[1] = {};
    EXPECT_EQ(ANeuralNetworksExecution_setOutput(mExecution, 0, nullptr, &output, sizeof(output)),
              ANEURALNETWORKS_NO_ERROR);

    ASSERT_EQ(ANeuralNetworksExecution_compute(mExecution), ANEURALNETWORKS_OP_FAILED);
}

TEST_F(FibonacciExtensionTest, InvalidNumInputs) {
    ExtensionOperandType inputType(Type::TENSOR_FLOAT32, {1});
    ExtensionOperandType outputType(Type::TENSOR_FLOAT32, {1});
    uint32_t input1 = mModel.addOperand(&inputType);
    uint32_t input2 = mModel.addOperand(&inputType);  // Extra input.
    uint32_t output = mModel.addOperand(&outputType);
    mModel.addOperation(mModel.getExtensionOperationType(TEST_VENDOR_FIBONACCI_EXTENSION_NAME,
                                                         TEST_VENDOR_FIBONACCI),
                        {input1, input2}, {output});
    mModel.identifyInputsAndOutputs({input1, input2}, {output});
    mModel.finish();
    ASSERT_TRUE(mModel.isValid());
    checkSupportedOperations({false});
    ASSERT_EQ(ANeuralNetworksCompilation_createForDevices(mModel.getHandle(), mDevices.data(),
                                                          mDevices.size(), &mCompilation),
              ANEURALNETWORKS_NO_ERROR);
    ASSERT_EQ(ANeuralNetworksCompilation_finish(mCompilation), ANEURALNETWORKS_BAD_DATA);
}

TEST_F(FibonacciExtensionTest, InvalidNumOutputs) {
    ExtensionOperandType inputType(Type::TENSOR_FLOAT32, {1});
    ExtensionOperandType outputType(Type::TENSOR_FLOAT32, {1});
    uint32_t input = mModel.addOperand(&inputType);
    uint32_t output1 = mModel.addOperand(&outputType);
    uint32_t output2 = mModel.addOperand(&outputType);  // Extra output.
    mModel.addOperation(mModel.getExtensionOperationType(TEST_VENDOR_FIBONACCI_EXTENSION_NAME,
                                                         TEST_VENDOR_FIBONACCI),
                        {input}, {output1, output2});
    mModel.identifyInputsAndOutputs({input}, {output1, output2});
    mModel.finish();
    ASSERT_TRUE(mModel.isValid());
    checkSupportedOperations({false});
    ASSERT_EQ(ANeuralNetworksCompilation_createForDevices(mModel.getHandle(), mDevices.data(),
                                                          mDevices.size(), &mCompilation),
              ANEURALNETWORKS_NO_ERROR);
    ASSERT_EQ(ANeuralNetworksCompilation_finish(mCompilation), ANEURALNETWORKS_BAD_DATA);
}

TEST_F(FibonacciExtensionTest, InvalidOperation) {
    ExtensionOperandType inputType(Type::TENSOR_FLOAT32, {1});
    ExtensionOperandType outputType(Type::TENSOR_FLOAT32, {1});
    uint32_t input = mModel.addOperand(&inputType);
    uint32_t output = mModel.addOperand(&outputType);
    mModel.addOperation(mModel.getExtensionOperationType(
                                TEST_VENDOR_FIBONACCI_EXTENSION_NAME,
                                TEST_VENDOR_FIBONACCI + 1),  // This operation should not exist.
                        {input}, {output});
    mModel.identifyInputsAndOutputs({input}, {output});
    mModel.finish();
    ASSERT_TRUE(mModel.isValid());
    checkSupportedOperations({false});
    ASSERT_EQ(ANeuralNetworksCompilation_createForDevices(mModel.getHandle(), mDevices.data(),
                                                          mDevices.size(), &mCompilation),
              ANEURALNETWORKS_NO_ERROR);
    ASSERT_EQ(ANeuralNetworksCompilation_finish(mCompilation), ANEURALNETWORKS_BAD_DATA);
}

}  // namespace
}  // namespace nn
}  // namespace android