/*
 * Copyright (C) 2017 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 <android-base/logging.h>
#include <android-base/scopeguard.h>
// android/log.h contains __INTRODUCED_IN() macro and must be included before
// sharedmem.h
#include <android/log.h>
#include <android/sharedmem.h>
#include <gtest/gtest.h>
#include <sys/mman.h>

#include <algorithm>
#include <functional>
#include <future>
#include <limits>
#include <set>
#include <string>
#include <utility>
#include <vector>

#include "AndroidVersionUtil.h"
#include "NeuralNetworks.h"
#include "NeuralNetworksOEM.h"
#include "TmpDirectoryUtils.h"

#ifdef __ANDROID__
#include <android/hardware_buffer.h>
#else  // __ANDROID__
#include <android-base/file.h>
#endif  // __ANDROID__

#ifndef NNTEST_ONLY_PUBLIC_API
#include "NeuralNetworksExtensions.h"
#include "TypeManager.h"
#endif

// This file tests all the validations done by the Neural Networks API.

namespace {

constexpr uint64_t kShortWaitInNanoseconds = 1'000'000'000;  // 1 second

class ValidationTest : public ::testing::Test {
   protected:
    virtual void SetUp() {}
};

class ValidationTestModel : public ValidationTest {
   protected:
    virtual void SetUp() {
        ValidationTest::SetUp();
        ASSERT_EQ(ANeuralNetworksModel_create(&mModel), ANEURALNETWORKS_NO_ERROR);
    }
    virtual void TearDown() {
        ANeuralNetworksModel_free(mModel);
        ValidationTest::TearDown();
    }

    uint32_t addScalarOperand(int32_t type = ANEURALNETWORKS_INT32) {
        ANeuralNetworksOperandType operandType = {
                .type = type, .dimensionCount = 0, .dimensions = nullptr};
        EXPECT_EQ(ANeuralNetworksModel_addOperand(mModel, &operandType), ANEURALNETWORKS_NO_ERROR);
        return mNumOperands++;
    }

    uint32_t addOperand(const ANeuralNetworksOperandType& operandType) {
        EXPECT_EQ(ANeuralNetworksModel_addOperand(mModel, &operandType), ANEURALNETWORKS_NO_ERROR);
        return mNumOperands++;
    }

    uint32_t addTensorOperand(int32_t type = ANEURALNETWORKS_TENSOR_FLOAT32) {
        return addTensorOperand(type, {2});
    }

    uint32_t addTensorOperand(int32_t type, const std::vector<uint32_t>& dimensions) {
        ANeuralNetworksOperandType operandType = {
                .type = type,
                .dimensionCount = static_cast<uint32_t>(dimensions.size()),
                .dimensions = dimensions.data(),
        };
        return addOperand(operandType);
    }

    int addOperation(ANeuralNetworksOperationType type, const std::vector<uint32_t>& inputs,
                     const std::vector<uint32_t>& outputs) {
        ++mNumOperations;
        return ANeuralNetworksModel_addOperation(mModel, type, inputs.size(), inputs.data(),
                                                 outputs.size(), outputs.data());
    }
    int identifyInputsAndOutputs(const std::vector<uint32_t>& inputs,
                                 const std::vector<uint32_t>& outputs) {
        return ANeuralNetworksModel_identifyInputsAndOutputs(mModel, inputs.size(), inputs.data(),
                                                             outputs.size(), outputs.data());
    }
    int modelFinish() { return ANeuralNetworksModel_finish(mModel); }

    virtual void createModel() {
        addTensorOperand();
        addTensorOperand();
        addScalarOperand();
        addTensorOperand();
        const std::vector<uint32_t> inList = {0, 1, 2};
        const std::vector<uint32_t> outList = {3};
        ASSERT_EQ(addOperation(ANEURALNETWORKS_ADD, inList, outList), ANEURALNETWORKS_NO_ERROR);
        ASSERT_EQ(identifyInputsAndOutputs(inList, outList), ANEURALNETWORKS_NO_ERROR);
        ASSERT_EQ(modelFinish(), ANEURALNETWORKS_NO_ERROR);
    }

    uint32_t mNumOperands = 0;
    uint32_t mNumOperations = 0;
    ANeuralNetworksModel* mModel = nullptr;

    const uint32_t kDummyDimensionValue = 1;
    const ANeuralNetworksOperandType kInvalidTensorType1{
            .type = ANEURALNETWORKS_TENSOR_FLOAT32,
            // dimensionCount must be consistent with dimensions.
            .dimensionCount = 1,
            .dimensions = nullptr,
    };
    const ANeuralNetworksOperandType kInvalidTensorType2{
            .type = ANEURALNETWORKS_TENSOR_FLOAT32,
            // dimensionCount must be consistent with dimensions.
            .dimensionCount = 0,
            .dimensions = &kDummyDimensionValue,
    };
};

#ifndef NNTEST_ONLY_PUBLIC_API
constexpr const char* kTestExtensionName = "com.android.test_extension";
constexpr int32_t kTestExtensionTensorType = ANEURALNETWORKS_TENSOR_QUANT8_SYMM_PER_CHANNEL;

class ValidationTestModelExtensions : public ValidationTestModel {
   protected:
    virtual void SetUp() {
        ValidationTestModel::SetUp();
        EXPECT_TRUE(::android::nn::TypeManager::get()->forTest_registerExtension({
                .name = kTestExtensionName,
                .operandTypes =
                        {
                                {
                                        .type = kTestExtensionTensorType,
                                        .isTensor = true,
                                        .byteSize = 1,
                                },
                        },
        }));
    }

    virtual void TearDown() {
        ::android::nn::TypeManager::get()->forTest_reset();
        ValidationTestModel::TearDown();
    }

    int32_t getExtensionOperandType(uint16_t typeWithinExtension) {
        int32_t result;
        EXPECT_EQ(ANeuralNetworksModel_getExtensionOperandType(mModel, kTestExtensionName,
                                                               typeWithinExtension, &result),
                  ANEURALNETWORKS_NO_ERROR);
        return result;
    }
};
#endif

class ValidationTestIdentify : public ValidationTestModel {
    virtual void SetUp() {
        ValidationTestModel::SetUp();

        uint32_t dimensions[]{1};
        ANeuralNetworksOperandType tensorType{.type = ANEURALNETWORKS_TENSOR_FLOAT32,
                                              .dimensionCount = 1,
                                              .dimensions = dimensions};
        ANeuralNetworksOperandType scalarType{
                .type = ANEURALNETWORKS_INT32, .dimensionCount = 0, .dimensions = nullptr};
        ASSERT_EQ(ANeuralNetworksModel_addOperand(mModel, &tensorType), ANEURALNETWORKS_NO_ERROR);
        ASSERT_EQ(ANeuralNetworksModel_addOperand(mModel, &tensorType), ANEURALNETWORKS_NO_ERROR);
        ASSERT_EQ(ANeuralNetworksModel_addOperand(mModel, &scalarType), ANEURALNETWORKS_NO_ERROR);
        ASSERT_EQ(ANeuralNetworksModel_addOperand(mModel, &tensorType), ANEURALNETWORKS_NO_ERROR);
        ASSERT_EQ(addOperation(ANEURALNETWORKS_ADD, {0, 1, 2}, {3}), ANEURALNETWORKS_NO_ERROR);
    }
    virtual void TearDown() { ValidationTestModel::TearDown(); }
};

class ValidationTestCompilation : public ValidationTestModel {
   protected:
    virtual void SetUp() {
        ValidationTestModel::SetUp();
        createModel();
        ASSERT_EQ(ANeuralNetworksCompilation_create(mModel, &mCompilation),
                  ANEURALNETWORKS_NO_ERROR);
    }

    virtual void TearDown() {
        ANeuralNetworksCompilation_free(mCompilation);
        ValidationTestModel::TearDown();
    }

    ANeuralNetworksCompilation* mCompilation = nullptr;
};

class ValidationTestExecution : public ValidationTestCompilation {
   protected:
    virtual void SetUp() {
        ValidationTestCompilation::SetUp();

        ASSERT_EQ(ANeuralNetworksCompilation_finish(mCompilation), ANEURALNETWORKS_NO_ERROR);

        ASSERT_EQ(ANeuralNetworksExecution_create(mCompilation, &mExecution),
                  ANEURALNETWORKS_NO_ERROR);
    }
    virtual void TearDown() {
        ANeuralNetworksExecution_free(mExecution);
        ValidationTestCompilation::TearDown();
    }
    ANeuralNetworksExecution* mExecution = nullptr;
};

class ValidationTestBurst : public ValidationTestExecution {
   protected:
    virtual void SetUp() {
        ValidationTestExecution::SetUp();

        ASSERT_EQ(ANeuralNetworksBurst_create(mCompilation, &mBurst), ANEURALNETWORKS_NO_ERROR);
    }
    virtual void TearDown() {
        ANeuralNetworksBurst_free(mBurst);
        ValidationTestExecution::TearDown();
    }
    ANeuralNetworksBurst* mBurst = nullptr;
};

class ValidationTestMemoryDesc : public ValidationTestCompilation {
   protected:
    virtual void SetUp() {
        ValidationTestCompilation::SetUp();
        ASSERT_EQ(ANeuralNetworksMemoryDesc_create(&mDesc), ANEURALNETWORKS_NO_ERROR);
    }
    virtual void TearDown() {
        ANeuralNetworksMemoryDesc_free(mDesc);
        for (auto* memory : mMemories) ANeuralNetworksMemory_free(memory);
        for (int fd : mFds) close(fd);
        ValidationTestCompilation::TearDown();
    }

    ANeuralNetworksMemory* createAshmem(uint32_t size) {
#ifdef __ANDROID__
        int fd = ASharedMemory_create("nnMemory", size);
#else   // __ANDROID__
        TemporaryFile tmpFile;
        int fd = tmpFile.release();
        CHECK_EQ(ftruncate(fd, size), 0);
#endif  // __ANDROID__
        EXPECT_GT(fd, 0);
        mFds.push_back(fd);
        ANeuralNetworksMemory* ashmem = nullptr;
        EXPECT_EQ(ANeuralNetworksMemory_createFromFd(size, PROT_READ | PROT_WRITE, fd, 0, &ashmem),
                  ANEURALNETWORKS_NO_ERROR);
        mMemories.push_back(ashmem);
        return ashmem;
    }

    ANeuralNetworksMemoryDesc* mDesc = nullptr;
    std::vector<ANeuralNetworksMemory*> mMemories;
    std::vector<int> mFds;
};

class ValidationTestExecutionDeviceMemory : public ValidationTest {
   protected:
    virtual void SetUp() {
        ValidationTest::SetUp();
        ASSERT_EQ(ANeuralNetworksModel_create(&mModel), ANEURALNETWORKS_NO_ERROR);
        createModel(mModel, /*dimensionsUnspecified=*/false, /*isValid=*/true);
        ASSERT_EQ(ANeuralNetworksCompilation_create(mModel, &mCompilation),
                  ANEURALNETWORKS_NO_ERROR);
        ASSERT_EQ(ANeuralNetworksCompilation_finish(mCompilation), ANEURALNETWORKS_NO_ERROR);
        ASSERT_EQ(ANeuralNetworksExecution_create(mCompilation, &mExecution),
                  ANEURALNETWORKS_NO_ERROR);

        ASSERT_EQ(ANeuralNetworksModel_create(&mModelDynamic), ANEURALNETWORKS_NO_ERROR);
        createModel(mModelDynamic, /*dimensionsUnspecified=*/true, /*isValid=*/true);
        ASSERT_EQ(ANeuralNetworksCompilation_create(mModelDynamic, &mCompilationDynamic),
                  ANEURALNETWORKS_NO_ERROR);
        ASSERT_EQ(ANeuralNetworksCompilation_finish(mCompilationDynamic), ANEURALNETWORKS_NO_ERROR);
        ASSERT_EQ(ANeuralNetworksExecution_create(mCompilationDynamic, &mExecutionDynamic),
                  ANEURALNETWORKS_NO_ERROR);

        ASSERT_EQ(ANeuralNetworksModel_create(&mInitModel), ANEURALNETWORKS_NO_ERROR);
        createModel(mInitModel, /*dimensionsUnspecified=*/false, /*isValid=*/true);
        ASSERT_EQ(ANeuralNetworksCompilation_create(mInitModel, &mInitCompilation),
                  ANEURALNETWORKS_NO_ERROR);
        ASSERT_EQ(ANeuralNetworksCompilation_finish(mInitCompilation), ANEURALNETWORKS_NO_ERROR);

        ASSERT_EQ(ANeuralNetworksModel_create(&mDeinitModel), ANEURALNETWORKS_NO_ERROR);
        createModel(mDeinitModel, /*dimensionsUnspecified=*/false, /*isValid=*/false);
        ASSERT_EQ(ANeuralNetworksCompilation_create(mDeinitModel, &mDeinitCompilation),
                  ANEURALNETWORKS_NO_ERROR);
        ASSERT_EQ(ANeuralNetworksCompilation_finish(mDeinitCompilation), ANEURALNETWORKS_NO_ERROR);
    }
    virtual void TearDown() {
        ANeuralNetworksExecution_free(mExecution);
        ANeuralNetworksCompilation_free(mCompilation);
        ANeuralNetworksModel_free(mModel);
        ANeuralNetworksExecution_free(mExecutionDynamic);
        ANeuralNetworksCompilation_free(mCompilationDynamic);
        ANeuralNetworksModel_free(mModelDynamic);

        ANeuralNetworksCompilation_free(mInitCompilation);
        ANeuralNetworksModel_free(mInitModel);
        ANeuralNetworksCompilation_free(mDeinitCompilation);
        ANeuralNetworksModel_free(mDeinitModel);

        ValidationTest::TearDown();
    }

    void addScalarOperand(ANeuralNetworksModel* model) {
        ANeuralNetworksOperandType operandType = {
                .type = ANEURALNETWORKS_INT32, .dimensionCount = 0, .dimensions = nullptr};
        EXPECT_EQ(ANeuralNetworksModel_addOperand(model, &operandType), ANEURALNETWORKS_NO_ERROR);
    }

    void addTensorOperand(ANeuralNetworksModel* model, bool dimensionsUnspecified) {
        uint32_t dimension = dimensionsUnspecified ? 0 : 1;
        ANeuralNetworksOperandType operandType = {
                .type = ANEURALNETWORKS_TENSOR_FLOAT32,
                .dimensionCount = 1,
                .dimensions = &dimension,
        };
        EXPECT_EQ(ANeuralNetworksModel_addOperand(model, &operandType), ANEURALNETWORKS_NO_ERROR);
    }

    void createModel(ANeuralNetworksModel* model, bool dimensionsUnspecified, bool isValid) {
        const float constData = 0;
        const uint32_t actData = isValid ? 0 : 999;

        addTensorOperand(model, dimensionsUnspecified);
        addTensorOperand(model, /*dimensionsUnspecified=*/false);
        addScalarOperand(model);
        addTensorOperand(model, dimensionsUnspecified);

        ASSERT_EQ(ANeuralNetworksModel_setOperandValue(model, 1, &constData, sizeof(float)),
                  ANEURALNETWORKS_NO_ERROR);
        ASSERT_EQ(ANeuralNetworksModel_setOperandValue(model, 2, &actData, sizeof(uint32_t)),
                  ANEURALNETWORKS_NO_ERROR);

        uint32_t inList[] = {0, 1, 2}, outList[] = {3};
        ASSERT_EQ(ANeuralNetworksModel_addOperation(model, ANEURALNETWORKS_ADD, 3, inList, 1,
                                                    outList),
                  ANEURALNETWORKS_NO_ERROR);
        uint32_t inputList[] = {0}, outputList[] = {3};
        ASSERT_EQ(ANeuralNetworksModel_identifyInputsAndOutputs(model, 1, inputList, 1, outputList),
                  ANEURALNETWORKS_NO_ERROR);
        ASSERT_EQ(ANeuralNetworksModel_finish(model), ANEURALNETWORKS_NO_ERROR);
    }

    void executeWithMemoryAsInput(ANeuralNetworksCompilation* compilation,
                                  ANeuralNetworksMemory* memory, int expectedResult) {
        float data = 0;
        ANeuralNetworksExecution* execution = nullptr;
        ASSERT_EQ(ANeuralNetworksExecution_create(compilation, &execution),
                  ANEURALNETWORKS_NO_ERROR);
        ASSERT_EQ(ANeuralNetworksExecution_setInputFromMemory(execution, 0, nullptr, memory, 0, 0),
                  ANEURALNETWORKS_NO_ERROR);
        ASSERT_EQ(ANeuralNetworksExecution_setOutput(execution, 0, nullptr, &data, sizeof(float)),
                  ANEURALNETWORKS_NO_ERROR);
        ASSERT_EQ(ANeuralNetworksExecution_compute(execution), expectedResult);
        ANeuralNetworksExecution_free(execution);
    }

    void executeWithMemoryAsOutput(ANeuralNetworksCompilation* compilation,
                                   ANeuralNetworksMemory* memory, int expectedResult) {
        const float data = 0;
        ANeuralNetworksExecution* execution = nullptr;
        ASSERT_EQ(ANeuralNetworksExecution_create(compilation, &execution),
                  ANEURALNETWORKS_NO_ERROR);
        ASSERT_EQ(ANeuralNetworksExecution_setInput(execution, 0, nullptr, &data, sizeof(float)),
                  ANEURALNETWORKS_NO_ERROR);
        ASSERT_EQ(ANeuralNetworksExecution_setOutputFromMemory(execution, 0, nullptr, memory, 0, 0),
                  ANEURALNETWORKS_NO_ERROR);
        ASSERT_EQ(ANeuralNetworksExecution_compute(execution), expectedResult);
        ANeuralNetworksExecution_free(execution);
    }

    ANeuralNetworksModel* mModel = nullptr;
    ANeuralNetworksCompilation* mCompilation = nullptr;
    ANeuralNetworksExecution* mExecution = nullptr;

    ANeuralNetworksModel* mModelDynamic = nullptr;
    ANeuralNetworksCompilation* mCompilationDynamic = nullptr;
    ANeuralNetworksExecution* mExecutionDynamic = nullptr;

    ANeuralNetworksModel* mInitModel = nullptr;
    ANeuralNetworksCompilation* mInitCompilation = nullptr;
    ANeuralNetworksModel* mDeinitModel = nullptr;
    ANeuralNetworksCompilation* mDeinitCompilation = nullptr;
};

TEST_F(ValidationTest, CreateModel) {
    EXPECT_EQ(ANeuralNetworksModel_create(nullptr), ANEURALNETWORKS_UNEXPECTED_NULL);
}

TEST_F(ValidationTestModel, AddOperand) {
    ANeuralNetworksOperandType floatType{
            .type = ANEURALNETWORKS_FLOAT32, .dimensionCount = 0, .dimensions = nullptr};
    EXPECT_EQ(ANeuralNetworksModel_addOperand(nullptr, &floatType),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksModel_addOperand(mModel, nullptr), ANEURALNETWORKS_UNEXPECTED_NULL);

    ANeuralNetworksOperandType quant8TypeInvalidScale{
            .type = ANEURALNETWORKS_TENSOR_QUANT8_ASYMM,
            .dimensionCount = 0,
            .dimensions = nullptr,
            // Scale has to be non-negative
            .scale = -1.0f,
            .zeroPoint = 0,
    };
    EXPECT_EQ(ANeuralNetworksModel_addOperand(mModel, &quant8TypeInvalidScale),
              ANEURALNETWORKS_BAD_DATA);

    ANeuralNetworksOperandType quant8TypeInvalidZeroPoint{
            .type = ANEURALNETWORKS_TENSOR_QUANT8_ASYMM,
            .dimensionCount = 0,
            .dimensions = nullptr,
            .scale = 1.0f,
            // zeroPoint has to be in [0, 255]
            .zeroPoint = -1,
    };
    EXPECT_EQ(ANeuralNetworksModel_addOperand(mModel, &quant8TypeInvalidZeroPoint),
              ANEURALNETWORKS_BAD_DATA);

    const uint32_t dim = 2;
    ANeuralNetworksOperandType invalidScalarType{
            .type = ANEURALNETWORKS_INT32,
            // a scalar type must have 0 dimensions.
            .dimensionCount = 1,
            .dimensions = &dim,
    };
    EXPECT_EQ(ANeuralNetworksModel_addOperand(mModel, &invalidScalarType),
              ANEURALNETWORKS_BAD_DATA);

    EXPECT_EQ(ANeuralNetworksModel_addOperand(mModel, &kInvalidTensorType1),
              ANEURALNETWORKS_BAD_DATA);
    EXPECT_EQ(ANeuralNetworksModel_addOperand(mModel, &kInvalidTensorType2),
              ANEURALNETWORKS_BAD_DATA);

    modelFinish();
    // This should fail, as the model is already finished.
    EXPECT_EQ(ANeuralNetworksModel_addOperand(mModel, &floatType), ANEURALNETWORKS_BAD_STATE);
}

TEST_F(ValidationTestModel, SetOperandSymmPerChannelQuantParams) {
    const int32_t operandIndex = addTensorOperand(ANEURALNETWORKS_TENSOR_QUANT8_SYMM_PER_CHANNEL);

    float scales[2] = {1.0, 2.0};
    ANeuralNetworksSymmPerChannelQuantParams channelQuant = {
            .channelDim = 0,
            .scaleCount = 2,
            .scales = scales,
    };

    EXPECT_EQ(ANeuralNetworksModel_setOperandSymmPerChannelQuantParams(nullptr, operandIndex,
                                                                       &channelQuant),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(
            ANeuralNetworksModel_setOperandSymmPerChannelQuantParams(mModel, operandIndex, nullptr),
            ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksModel_setOperandSymmPerChannelQuantParams(mModel, operandIndex + 1,
                                                                       &channelQuant),
              ANEURALNETWORKS_BAD_DATA);
    EXPECT_EQ(ANeuralNetworksModel_setOperandSymmPerChannelQuantParams(mModel, operandIndex,
                                                                       &channelQuant),
              ANEURALNETWORKS_NO_ERROR);
}

#ifndef NNTEST_ONLY_PUBLIC_API
TEST_F(ValidationTestModelExtensions, AddOperand_UnknownPrefix) {
    ANeuralNetworksOperandType type = {.type = -1};
    ASSERT_EQ(ANeuralNetworksModel_addOperand(mModel, &type), ANEURALNETWORKS_BAD_DATA);
}

TEST_F(ValidationTestModelExtensions, SetOperandSymmPerChannelQuantParams_ExtensionOperand) {
    const int32_t operandIndex =
            addTensorOperand(getExtensionOperandType(kTestExtensionTensorType));

    float scales[2] = {1.0, 2.0};
    ANeuralNetworksSymmPerChannelQuantParams channelQuant = {
            .channelDim = 0,
            .scaleCount = 2,
            .scales = scales,
    };

    EXPECT_EQ(ANeuralNetworksModel_setOperandSymmPerChannelQuantParams(mModel, operandIndex,
                                                                       &channelQuant),
              ANEURALNETWORKS_BAD_DATA);
}

TEST_F(ValidationTestModelExtensions, SetOperandExtensionData) {
    const int32_t operandIndex =
            addTensorOperand(getExtensionOperandType(kTestExtensionTensorType));
    const int32_t data = 42;
    const size_t dataLength = sizeof(data);
    EXPECT_EQ(
            ANeuralNetworksModel_setOperandExtensionData(nullptr, operandIndex, &data, dataLength),
            ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(
            ANeuralNetworksModel_setOperandExtensionData(mModel, operandIndex, nullptr, dataLength),
            ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksModel_setOperandExtensionData(mModel, operandIndex, &data, 0),
              ANEURALNETWORKS_BAD_DATA);
    EXPECT_EQ(ANeuralNetworksModel_setOperandExtensionData(mModel, operandIndex + 1, &data,
                                                           dataLength),
              ANEURALNETWORKS_BAD_DATA);
    EXPECT_EQ(ANeuralNetworksModel_setOperandExtensionData(mModel, operandIndex, &data, dataLength),
              ANEURALNETWORKS_NO_ERROR);
}

TEST_F(ValidationTestModelExtensions, SetOperandExtensionData_Empty) {
    const int32_t operandIndex =
            addTensorOperand(getExtensionOperandType(kTestExtensionTensorType));
    EXPECT_EQ(ANeuralNetworksModel_setOperandExtensionData(mModel, operandIndex, nullptr, 0),
              ANEURALNETWORKS_NO_ERROR);
}

TEST_F(ValidationTestModelExtensions, SetOperandExtensionData_NonExtensionOperand) {
    const int32_t operandIndex = addTensorOperand();
    const int32_t data = 42;
    const size_t dataLength = sizeof(data);
    EXPECT_EQ(ANeuralNetworksModel_setOperandExtensionData(mModel, operandIndex, &data, dataLength),
              ANEURALNETWORKS_BAD_DATA);
}

TEST_F(ValidationTestModelExtensions, SetOperandValue_UnspecifiedDimension) {
    const uint32_t dimensions[2] = {3, 0};
    ANeuralNetworksOperandType type = {
            .type = getExtensionOperandType(kTestExtensionTensorType),
            .dimensionCount = 2,
            .dimensions = dimensions,
    };
    const int32_t operandIndex = addOperand(type);
    char buffer[20];
    EXPECT_EQ(ANeuralNetworksModel_setOperandValue(mModel, operandIndex, buffer, sizeof(buffer)),
              ANEURALNETWORKS_BAD_DATA);
}

TEST_F(ValidationTestModelExtensions, SetOperandValue_UnspecifiedRank) {
    ANeuralNetworksOperandType type = {
            .type = getExtensionOperandType(kTestExtensionTensorType),
            .dimensionCount = 0,
            .dimensions = nullptr,
    };
    const int32_t operandIndex = addOperand(type);
    char buffer[20];
    EXPECT_EQ(ANeuralNetworksModel_setOperandValue(mModel, operandIndex, buffer, sizeof(buffer)),
              ANEURALNETWORKS_BAD_DATA);
}

TEST_F(ValidationTestModelExtensions, AddOperandDimensionProductOverflow) {
    uint32_t dimensions[] = {5, 4, 4, 786433, 5, 3, 16777216, 4, 5};
    ANeuralNetworksOperandType operandType = {
            .type = getExtensionOperandType(kTestExtensionTensorType),
            .dimensionCount = std::size(dimensions),
            .dimensions = dimensions,
    };
    // This should fail, as the operand type's dimension product overflows uint32_t.
    ASSERT_EQ(ANeuralNetworksModel_addOperand(mModel, &operandType), ANEURALNETWORKS_BAD_DATA);
}
#endif

TEST_F(ValidationTestModel, SetOptionalOperand) {
    ANeuralNetworksOperandType floatType{
            .type = ANEURALNETWORKS_FLOAT32, .dimensionCount = 0, .dimensions = nullptr};
    EXPECT_EQ(ANeuralNetworksModel_addOperand(mModel, &floatType), ANEURALNETWORKS_NO_ERROR);

    EXPECT_EQ(ANeuralNetworksModel_setOperandValue(mModel, 0, nullptr, 0),
              ANEURALNETWORKS_NO_ERROR);
}

TEST_F(ValidationTestModel, SetOperandValue) {
    ANeuralNetworksOperandType floatType{
            .type = ANEURALNETWORKS_FLOAT32, .dimensionCount = 0, .dimensions = nullptr};
    EXPECT_EQ(ANeuralNetworksModel_addOperand(mModel, &floatType), ANEURALNETWORKS_NO_ERROR);

    char buffer[20];
    EXPECT_EQ(ANeuralNetworksModel_setOperandValue(nullptr, 0, buffer, sizeof(buffer)),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksModel_setOperandValue(mModel, 0, nullptr, sizeof(buffer)),
              ANEURALNETWORKS_UNEXPECTED_NULL);

    // This should fail, because buffer is not the size of a float32.
    EXPECT_EQ(ANeuralNetworksModel_setOperandValue(mModel, 0, buffer, sizeof(buffer)),
              ANEURALNETWORKS_BAD_DATA);

    // This should succeed.
    EXPECT_EQ(ANeuralNetworksModel_setOperandValue(mModel, 0, buffer, sizeof(float)),
              ANEURALNETWORKS_NO_ERROR);

    // This should fail, as this operand does not exist.
    EXPECT_EQ(ANeuralNetworksModel_setOperandValue(mModel, 1, buffer, sizeof(float)),
              ANEURALNETWORKS_BAD_DATA);

    modelFinish();
    // This should fail, as the model is already finished.
    EXPECT_EQ(ANeuralNetworksModel_setOperandValue(mModel, 0, buffer, sizeof(float)),
              ANEURALNETWORKS_BAD_STATE);
}

TEST_F(ValidationTestModel, SetOperandValueFromMemory) {
    uint32_t dimensions[]{1};
    ANeuralNetworksOperandType floatType{
            .type = ANEURALNETWORKS_TENSOR_FLOAT32, .dimensionCount = 1, .dimensions = dimensions};
    EXPECT_EQ(ANeuralNetworksModel_addOperand(mModel, &floatType), ANEURALNETWORKS_NO_ERROR);

    const size_t memorySize = 20;
#ifdef __ANDROID__
    int memoryFd = ASharedMemory_create("nnMemory", memorySize);
#else   // __ANDROID__
    TemporaryFile tmpFile;
    int memoryFd = tmpFile.release();
    CHECK_EQ(ftruncate(memoryFd, memorySize), 0);
#endif  // __ANDROID__
    ASSERT_GT(memoryFd, 0);

    ANeuralNetworksMemory* memory;
    EXPECT_EQ(ANeuralNetworksMemory_createFromFd(memorySize, PROT_READ | PROT_WRITE, memoryFd, 0,
                                                 &memory),
              ANEURALNETWORKS_NO_ERROR);

    EXPECT_EQ(ANeuralNetworksModel_setOperandValueFromMemory(nullptr, 0, memory, 0, sizeof(float)),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksModel_setOperandValueFromMemory(mModel, 0, nullptr, 0, sizeof(float)),
              ANEURALNETWORKS_UNEXPECTED_NULL);

    // This should fail, because the operand does not exist.
    EXPECT_EQ(ANeuralNetworksModel_setOperandValueFromMemory(mModel, -1, memory, 0, sizeof(float)),
              ANEURALNETWORKS_BAD_DATA);

    // This should fail, because memory is not the size of a float32.
    EXPECT_EQ(ANeuralNetworksModel_setOperandValueFromMemory(mModel, 0, memory, 0, memorySize),
              ANEURALNETWORKS_BAD_DATA);

    // This should fail, as this operand does not exist.
    EXPECT_EQ(ANeuralNetworksModel_setOperandValueFromMemory(mModel, 1, memory, 0, sizeof(float)),
              ANEURALNETWORKS_BAD_DATA);

    // This should fail, because offset is larger than memorySize.
    EXPECT_EQ(ANeuralNetworksModel_setOperandValueFromMemory(mModel, 0, memory, memorySize + 1,
                                                             sizeof(float)),
              ANEURALNETWORKS_BAD_DATA);

    // This should fail, because requested size is larger than the memory.
    EXPECT_EQ(ANeuralNetworksModel_setOperandValueFromMemory(mModel, 0, memory, memorySize - 3,
                                                             sizeof(float)),
              ANEURALNETWORKS_BAD_DATA);

    modelFinish();
    // This should fail, as the model is already finished.
    EXPECT_EQ(ANeuralNetworksModel_setOperandValueFromMemory(mModel, 0, memory, 0, sizeof(float)),
              ANEURALNETWORKS_BAD_STATE);

    // close memory
    ANeuralNetworksMemory_free(memory);
    close(memoryFd);
}

#ifdef __ANDROID__
TEST_F(ValidationTestModel, SetOperandValueFromAHardwareBuffer) {
    uint32_t dimensions[]{1};
    ANeuralNetworksOperandType quant8Type{.type = ANEURALNETWORKS_TENSOR_QUANT8_ASYMM,
                                          .dimensionCount = 1,
                                          .dimensions = dimensions,
                                          .scale = 1.0,
                                          .zeroPoint = 0};
    EXPECT_EQ(ANeuralNetworksModel_addOperand(mModel, &quant8Type), ANEURALNETWORKS_NO_ERROR);

    AHardwareBuffer_Desc desc{
            .width = 16,
            .height = 16,
            .layers = 1,
            .format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
            .usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
    };

    AHardwareBuffer* buffer = nullptr;
    ASSERT_EQ(AHardwareBuffer_allocate(&desc, &buffer), 0);

    ANeuralNetworksMemory* memory;
    EXPECT_EQ(ANeuralNetworksMemory_createFromAHardwareBuffer(buffer, &memory),
              ANEURALNETWORKS_NO_ERROR);

    // This should fail, because non-BLOB AHardwareBuffer is not allowed.
    EXPECT_EQ(ANeuralNetworksModel_setOperandValueFromMemory(mModel, 0, memory, 0, sizeof(uint8_t)),
              ANEURALNETWORKS_BAD_DATA);

    // close memory
    ANeuralNetworksMemory_free(memory);
    AHardwareBuffer_release(buffer);
}

TEST_F(ValidationTestModel, SetOperandValueFromAHardwareBufferBlob) {
    uint32_t dimensions[]{1};
    ANeuralNetworksOperandType floatType{
            .type = ANEURALNETWORKS_TENSOR_FLOAT32, .dimensionCount = 1, .dimensions = dimensions};
    EXPECT_EQ(ANeuralNetworksModel_addOperand(mModel, &floatType), ANEURALNETWORKS_NO_ERROR);

    const size_t memorySize = 20;
    AHardwareBuffer_Desc desc{
            .width = memorySize,
            .height = 1,
            .layers = 1,
            .format = AHARDWAREBUFFER_FORMAT_BLOB,
            .usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
    };

    AHardwareBuffer* buffer = nullptr;
    ASSERT_EQ(AHardwareBuffer_allocate(&desc, &buffer), 0);

    ANeuralNetworksMemory* memory;
    EXPECT_EQ(ANeuralNetworksMemory_createFromAHardwareBuffer(buffer, &memory),
              ANEURALNETWORKS_NO_ERROR);

    // This should fail, because offset is larger than memorySize.
    EXPECT_EQ(ANeuralNetworksModel_setOperandValueFromMemory(mModel, 0, memory, memorySize + 1,
                                                             sizeof(float)),
              ANEURALNETWORKS_BAD_DATA);

    // This should fail, because requested size is larger than the memory.
    EXPECT_EQ(ANeuralNetworksModel_setOperandValueFromMemory(mModel, 0, memory, memorySize - 3,
                                                             sizeof(float)),
              ANEURALNETWORKS_BAD_DATA);

    // close memory
    ANeuralNetworksMemory_free(memory);
    AHardwareBuffer_release(buffer);
}
#endif  // __ANDROID__

TEST_F(ValidationTestModel, SetOperandValueFromModel) {
    uint32_t dimensions[] = {2};
    ANeuralNetworksOperandType tensorType = {
            .type = ANEURALNETWORKS_TENSOR_FLOAT32,
            .dimensionCount = std::size(dimensions),
            .dimensions = dimensions,
    };
    ANeuralNetworksOperandType scalarType = {.type = ANEURALNETWORKS_INT32};
    ANeuralNetworksOperandType modelType = {.type = ANEURALNETWORKS_MODEL};

    ANeuralNetworksModel* valueModel = nullptr;
    ASSERT_EQ(ANeuralNetworksModel_create(&valueModel), ANEURALNETWORKS_NO_ERROR);
    ASSERT_EQ(ANeuralNetworksModel_addOperand(valueModel, &tensorType), ANEURALNETWORKS_NO_ERROR);
    ASSERT_EQ(ANeuralNetworksModel_addOperand(valueModel, &tensorType), ANEURALNETWORKS_NO_ERROR);
    ASSERT_EQ(ANeuralNetworksModel_addOperand(valueModel, &scalarType), ANEURALNETWORKS_NO_ERROR);
    ASSERT_EQ(ANeuralNetworksModel_addOperand(valueModel, &tensorType), ANEURALNETWORKS_NO_ERROR);
    uint32_t inList[3] = {0, 1, 2};
    uint32_t outList[1] = {3};
    ASSERT_EQ(ANeuralNetworksModel_addOperation(valueModel, ANEURALNETWORKS_ADD, 3, inList, 1,
                                                outList),
              ANEURALNETWORKS_NO_ERROR);
    ASSERT_EQ(ANeuralNetworksModel_identifyInputsAndOutputs(valueModel, 3, inList, 1, outList),
              ANEURALNETWORKS_NO_ERROR);

    EXPECT_EQ(ANeuralNetworksModel_addOperand(mModel, &modelType), ANEURALNETWORKS_NO_ERROR);

    // This should fail, as the value model is not finished.
    EXPECT_EQ(ANeuralNetworksModel_setOperandValueFromModel(mModel, 0, valueModel),
              ANEURALNETWORKS_BAD_STATE);
    ANeuralNetworksModel_finish(valueModel);

    EXPECT_EQ(ANeuralNetworksModel_setOperandValueFromModel(nullptr, 0, valueModel),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksModel_setOperandValueFromModel(mModel, 0, nullptr),
              ANEURALNETWORKS_UNEXPECTED_NULL);

    // This should fail, because the operand does not exist.
    EXPECT_EQ(ANeuralNetworksModel_setOperandValueFromModel(mModel, -1, valueModel),
              ANEURALNETWORKS_BAD_DATA);

    // This should fail, as this operand does not exist.
    EXPECT_EQ(ANeuralNetworksModel_setOperandValueFromModel(mModel, 1, valueModel),
              ANEURALNETWORKS_BAD_DATA);

    modelFinish();
    // This should fail, as the model is already finished.
    EXPECT_EQ(ANeuralNetworksModel_setOperandValueFromModel(mModel, 0, valueModel),
              ANEURALNETWORKS_BAD_STATE);

    ANeuralNetworksModel_free(valueModel);
}

TEST_F(ValidationTestModel, AddOEMOperand) {
    ANeuralNetworksOperandType OEMScalarType{
            .type = ANEURALNETWORKS_OEM_SCALAR, .dimensionCount = 0, .dimensions = nullptr};
    EXPECT_EQ(ANeuralNetworksModel_addOperand(mModel, &OEMScalarType), ANEURALNETWORKS_NO_ERROR);
    char buffer[20];
    EXPECT_EQ(ANeuralNetworksModel_setOperandValue(mModel, 0, buffer, sizeof(buffer)),
              ANEURALNETWORKS_NO_ERROR);

    const size_t kByteSizeOfOEMTensor = 4;
    uint32_t dimensions[]{kByteSizeOfOEMTensor};
    ANeuralNetworksOperandType OEMTensorType{
            .type = ANEURALNETWORKS_TENSOR_OEM_BYTE, .dimensionCount = 1, .dimensions = dimensions};
    EXPECT_EQ(ANeuralNetworksModel_addOperand(mModel, &OEMTensorType), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksModel_setOperandValue(mModel, 1, buffer, kByteSizeOfOEMTensor),
              ANEURALNETWORKS_NO_ERROR);

    modelFinish();
    // This should fail, as the model is already finished.
    EXPECT_EQ(ANeuralNetworksModel_addOperand(mModel, &OEMTensorType), ANEURALNETWORKS_BAD_STATE);
}

TEST_F(ValidationTestModel, AddOperation) {
    uint32_t input = 0;
    uint32_t output = 0;
    EXPECT_EQ(ANeuralNetworksModel_addOperation(nullptr, ANEURALNETWORKS_AVERAGE_POOL_2D, 1, &input,
                                                1, &output),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksModel_addOperation(mModel, ANEURALNETWORKS_AVERAGE_POOL_2D, 0, nullptr,
                                                1, &output),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksModel_addOperation(mModel, ANEURALNETWORKS_AVERAGE_POOL_2D, 1, &input,
                                                0, nullptr),
              ANEURALNETWORKS_UNEXPECTED_NULL);

    ANeuralNetworksOperationType invalidOp = -1;
    EXPECT_EQ(addOperation(invalidOp, {input}, {output}), ANEURALNETWORKS_BAD_DATA);

    modelFinish();
    // This should fail, as the model is already finished.
    EXPECT_EQ(addOperation(ANEURALNETWORKS_AVERAGE_POOL_2D, {input}, {output}),
              ANEURALNETWORKS_BAD_STATE);
}

TEST_F(ValidationTestModel, IdentifyInputsAndOutputs) {
    uint32_t input = 0;
    uint32_t output = 0;
    EXPECT_EQ(ANeuralNetworksModel_identifyInputsAndOutputs(nullptr, 1, &input, 1, &output),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksModel_identifyInputsAndOutputs(mModel, 0, nullptr, 1, &output),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksModel_identifyInputsAndOutputs(mModel, 1, &input, 0, nullptr),
              ANEURALNETWORKS_UNEXPECTED_NULL);

    createModel();
    // This should fail, as the model is already finished.
    EXPECT_EQ(identifyInputsAndOutputs({input}, {output}), ANEURALNETWORKS_BAD_STATE);
}

TEST_F(ValidationTestModel, RelaxComputationFloat32toFloat16) {
    EXPECT_EQ(ANeuralNetworksModel_relaxComputationFloat32toFloat16(nullptr, true),
              ANEURALNETWORKS_UNEXPECTED_NULL);

    createModel();
    // This should fail, as the model is already finished.
    EXPECT_EQ(ANeuralNetworksModel_relaxComputationFloat32toFloat16(mModel, true),
              ANEURALNETWORKS_BAD_STATE);
    EXPECT_EQ(ANeuralNetworksModel_relaxComputationFloat32toFloat16(mModel, false),
              ANEURALNETWORKS_BAD_STATE);
}

TEST_F(ValidationTestModel, Finish) {
    EXPECT_EQ(ANeuralNetworksModel_finish(nullptr), ANEURALNETWORKS_UNEXPECTED_NULL);
    createModel();
    EXPECT_EQ(modelFinish(), ANEURALNETWORKS_BAD_STATE);
}

TEST_F(ValidationTestModel, EmptyModel) {
    // An empty model is invalid
    EXPECT_EQ(modelFinish(), ANEURALNETWORKS_BAD_DATA);
}

TEST_F(ValidationTestModel, CreateCompilation) {
    ANeuralNetworksCompilation* compilation = nullptr;
    EXPECT_EQ(ANeuralNetworksCompilation_create(nullptr, &compilation),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksCompilation_create(mModel, nullptr), ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksCompilation_create(mModel, &compilation), ANEURALNETWORKS_BAD_STATE);
}

TEST_F(ValidationTestModel, CreateCompilationForDevices) {
    createModel();
    uint32_t numDevices = 0;
    EXPECT_EQ(ANeuralNetworks_getDeviceCount(&numDevices), ANEURALNETWORKS_NO_ERROR);

    if (numDevices > 0) {
        ANeuralNetworksDevice* device;
        EXPECT_EQ(ANeuralNetworks_getDevice(0, &device), ANEURALNETWORKS_NO_ERROR);
        ANeuralNetworksCompilation* compilation = nullptr;
        EXPECT_EQ(ANeuralNetworksCompilation_createForDevices(nullptr, &device, 1, &compilation),
                  ANEURALNETWORKS_UNEXPECTED_NULL);
        EXPECT_EQ(ANeuralNetworksCompilation_createForDevices(mModel, &device, 1, nullptr),
                  ANEURALNETWORKS_UNEXPECTED_NULL);

        // empty device list
        EXPECT_EQ(ANeuralNetworksCompilation_createForDevices(mModel, &device, 0, &compilation),
                  ANEURALNETWORKS_BAD_DATA);

        // duplicate devices in the list.
        ANeuralNetworksDevice* invalidDevices[2] = {device, device};
        EXPECT_EQ(ANeuralNetworksCompilation_createForDevices(mModel, invalidDevices, 2,
                                                              &compilation),
                  ANEURALNETWORKS_BAD_DATA);
        // nullptr in the list.
        invalidDevices[1] = nullptr;
        EXPECT_EQ(ANeuralNetworksCompilation_createForDevices(mModel, invalidDevices, 2,
                                                              &compilation),
                  ANEURALNETWORKS_UNEXPECTED_NULL);
    }

    ANeuralNetworksCompilation* compilation = nullptr;
    EXPECT_EQ(ANeuralNetworksCompilation_createForDevices(nullptr, nullptr, 1, &compilation),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksCompilation_createForDevices(mModel, nullptr, 1, nullptr),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksCompilation_createForDevices(mModel, nullptr, 1, &compilation),
              ANEURALNETWORKS_UNEXPECTED_NULL);
}

TEST_F(ValidationTestModel, GetSupportedOperationsForDevices) {
    createModel();
    uint32_t numDevices = 0;
    EXPECT_EQ(ANeuralNetworks_getDeviceCount(&numDevices), ANEURALNETWORKS_NO_ERROR);

    bool supportedOps[20];
    ASSERT_LE(mNumOperations, sizeof(supportedOps) / sizeof(supportedOps[0]));
    if (numDevices > 0) {
        ANeuralNetworksDevice* device;
        EXPECT_EQ(ANeuralNetworks_getDevice(0, &device), ANEURALNETWORKS_NO_ERROR);
        EXPECT_EQ(ANeuralNetworksModel_getSupportedOperationsForDevices(nullptr, &device, 1,
                                                                        supportedOps),
                  ANEURALNETWORKS_UNEXPECTED_NULL);
        EXPECT_EQ(
                ANeuralNetworksModel_getSupportedOperationsForDevices(mModel, &device, 1, nullptr),
                ANEURALNETWORKS_UNEXPECTED_NULL);

        // empty device list
        EXPECT_EQ(ANeuralNetworksModel_getSupportedOperationsForDevices(mModel, &device, 0,
                                                                        supportedOps),
                  ANEURALNETWORKS_BAD_DATA);

        // duplicate devices in the list.
        ANeuralNetworksDevice* invalidDevices[2] = {device, device};
        EXPECT_EQ(ANeuralNetworksModel_getSupportedOperationsForDevices(mModel, invalidDevices, 2,
                                                                        supportedOps),
                  ANEURALNETWORKS_BAD_DATA);
        // nullptr in the list.
        invalidDevices[1] = nullptr;
        EXPECT_EQ(ANeuralNetworksModel_getSupportedOperationsForDevices(mModel, invalidDevices, 2,
                                                                        supportedOps),
                  ANEURALNETWORKS_UNEXPECTED_NULL);
    }

    EXPECT_EQ(ANeuralNetworksModel_getSupportedOperationsForDevices(nullptr, nullptr, 1,
                                                                    supportedOps),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksModel_getSupportedOperationsForDevices(mModel, nullptr, 1, nullptr),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(
            ANeuralNetworksModel_getSupportedOperationsForDevices(mModel, nullptr, 1, supportedOps),
            ANEURALNETWORKS_UNEXPECTED_NULL);
}

TEST_F(ValidationTestModel, Cycle) {
    uint32_t dimensions[]{1};
    ANeuralNetworksOperandType tensorType{
            .type = ANEURALNETWORKS_TENSOR_FLOAT32, .dimensionCount = 1, .dimensions = dimensions};
    ANeuralNetworksOperandType scalarType{
            .type = ANEURALNETWORKS_INT32, .dimensionCount = 0, .dimensions = nullptr};

    // opnd0 = model input TENSOR_FLOAT32
    // opnd1 = model input TENSOR_FLOAT32
    // opnd2 = model input INT32
    // opnd3 = ADD(opnd0, opnd4, opnd2)
    // opnd4 = ADD(opnd1, opnd3, opnd2)
    // opnd5 = ADD(opnd4, opnd0, opnd2)  // model output
    //
    //            +-----+
    //            |     |
    //            v     |
    // 3 = ADD(0, 4, 2) |
    // |                |
    // +----------+     |
    //            |     |
    //            v     |
    // 4 = ADD(1, 3, 2) |
    // |                |
    // +----------------+
    // |
    // |
    // +-------+
    //         |
    //         v
    // 5 = ADD(4, 0, 2)

    ASSERT_EQ(ANeuralNetworksModel_addOperand(mModel, &tensorType), ANEURALNETWORKS_NO_ERROR);
    ASSERT_EQ(ANeuralNetworksModel_addOperand(mModel, &tensorType), ANEURALNETWORKS_NO_ERROR);
    ASSERT_EQ(ANeuralNetworksModel_addOperand(mModel, &scalarType), ANEURALNETWORKS_NO_ERROR);
    ASSERT_EQ(ANeuralNetworksModel_addOperand(mModel, &tensorType), ANEURALNETWORKS_NO_ERROR);
    ASSERT_EQ(ANeuralNetworksModel_addOperand(mModel, &tensorType), ANEURALNETWORKS_NO_ERROR);
    ASSERT_EQ(ANeuralNetworksModel_addOperand(mModel, &tensorType), ANEURALNETWORKS_NO_ERROR);

    ASSERT_EQ(addOperation(ANEURALNETWORKS_ADD, {0, 4, 2}, {3}), ANEURALNETWORKS_NO_ERROR);
    ASSERT_EQ(addOperation(ANEURALNETWORKS_ADD, {1, 3, 2}, {4}), ANEURALNETWORKS_NO_ERROR);
    ASSERT_EQ(addOperation(ANEURALNETWORKS_ADD, {4, 0, 2}, {5}), ANEURALNETWORKS_NO_ERROR);

    ASSERT_EQ(identifyInputsAndOutputs({0, 1, 2}, {5}), ANEURALNETWORKS_NO_ERROR);
    ASSERT_EQ(modelFinish(), ANEURALNETWORKS_BAD_DATA);
}

TEST_F(ValidationTestModel, AcyclicReadBeforeWrite) {
    uint32_t dimensions[]{1};
    ANeuralNetworksOperandType tensorType{
            .type = ANEURALNETWORKS_TENSOR_FLOAT32, .dimensionCount = 1, .dimensions = dimensions};

    // opnd0 = TENSOR_FLOAT32   // model input
    // opnd1 = LOGISTIC(opnd2)  // model output
    // opnd2 = LOGISTIC(opnd0)
    ASSERT_EQ(ANeuralNetworksModel_addOperand(mModel, &tensorType), ANEURALNETWORKS_NO_ERROR);
    ASSERT_EQ(ANeuralNetworksModel_addOperand(mModel, &tensorType), ANEURALNETWORKS_NO_ERROR);
    ASSERT_EQ(ANeuralNetworksModel_addOperand(mModel, &tensorType), ANEURALNETWORKS_NO_ERROR);

    ASSERT_EQ(addOperation(ANEURALNETWORKS_LOGISTIC, {2}, {1}), ANEURALNETWORKS_NO_ERROR);
    ASSERT_EQ(addOperation(ANEURALNETWORKS_LOGISTIC, {0}, {2}), ANEURALNETWORKS_NO_ERROR);
    ASSERT_EQ(identifyInputsAndOutputs({0}, {1}), ANEURALNETWORKS_NO_ERROR);

    // This should succeed, because NN API doesn't require that operations be sorted.
    ASSERT_EQ(modelFinish(), ANEURALNETWORKS_NO_ERROR);
}

TEST_F(ValidationTestModel, MissingWrite) {
    uint32_t dimensions[]{1};
    ANeuralNetworksOperandType tensorType{
            .type = ANEURALNETWORKS_TENSOR_FLOAT32, .dimensionCount = 1, .dimensions = dimensions};

    // opnd0 = TENSOR_FLOAT32  // model input
    // opnd1 = TENSOR_FLOAT32  // never written
    // opnd2 = LOGISTIC(opnd1) // model output
    // opnd3 = LOGISTIC(opnd0) // model output
    ASSERT_EQ(ANeuralNetworksModel_addOperand(mModel, &tensorType), ANEURALNETWORKS_NO_ERROR);
    ASSERT_EQ(ANeuralNetworksModel_addOperand(mModel, &tensorType), ANEURALNETWORKS_NO_ERROR);
    ASSERT_EQ(ANeuralNetworksModel_addOperand(mModel, &tensorType), ANEURALNETWORKS_NO_ERROR);
    ASSERT_EQ(ANeuralNetworksModel_addOperand(mModel, &tensorType), ANEURALNETWORKS_NO_ERROR);

    ASSERT_EQ(addOperation(ANEURALNETWORKS_LOGISTIC, {1}, {2}), ANEURALNETWORKS_NO_ERROR);
    ASSERT_EQ(addOperation(ANEURALNETWORKS_LOGISTIC, {0}, {3}), ANEURALNETWORKS_NO_ERROR);
    ASSERT_EQ(identifyInputsAndOutputs({0}, {2, 3}), ANEURALNETWORKS_NO_ERROR);

    ASSERT_EQ(modelFinish(), ANEURALNETWORKS_BAD_DATA);
}

TEST_F(ValidationTestModel, UnwrittenOperand) {
    uint32_t dimensions[]{1};
    ANeuralNetworksOperandType tensorType{
            .type = ANEURALNETWORKS_TENSOR_FLOAT32, .dimensionCount = 1, .dimensions = dimensions};

    // opnd0 = TENSOR_FLOAT32  // model input
    // opnd1 = TENSOR_FLOAT32  // never written
    // opnd2 = LOGISTIC(opnd0) // model output
    ASSERT_EQ(ANeuralNetworksModel_addOperand(mModel, &tensorType), ANEURALNETWORKS_NO_ERROR);
    ASSERT_EQ(ANeuralNetworksModel_addOperand(mModel, &tensorType), ANEURALNETWORKS_NO_ERROR);
    ASSERT_EQ(ANeuralNetworksModel_addOperand(mModel, &tensorType), ANEURALNETWORKS_NO_ERROR);

    ASSERT_EQ(addOperation(ANEURALNETWORKS_LOGISTIC, {0}, {2}), ANEURALNETWORKS_NO_ERROR);
    ASSERT_EQ(identifyInputsAndOutputs({0}, {2}), ANEURALNETWORKS_NO_ERROR);

    ASSERT_EQ(modelFinish(), ANEURALNETWORKS_BAD_DATA);
}

TEST_F(ValidationTestModel, MultipleWrite) {
    uint32_t dimensions[]{1};
    ANeuralNetworksOperandType tensorType{
            .type = ANEURALNETWORKS_TENSOR_FLOAT32, .dimensionCount = 1, .dimensions = dimensions};
    ANeuralNetworksOperandType scalarType{
            .type = ANEURALNETWORKS_INT32, .dimensionCount = 0, .dimensions = nullptr};

    // opnd0 = TENSOR_FLOAT32            // model input
    // opnd1 = INT32                     // model input
    // opnd2 = ADD(opnd0, opnd0, opnd1)  // model output; do this twice
    ASSERT_EQ(ANeuralNetworksModel_addOperand(mModel, &tensorType), ANEURALNETWORKS_NO_ERROR);
    ASSERT_EQ(ANeuralNetworksModel_addOperand(mModel, &scalarType), ANEURALNETWORKS_NO_ERROR);
    ASSERT_EQ(ANeuralNetworksModel_addOperand(mModel, &tensorType), ANEURALNETWORKS_NO_ERROR);

    for (int i = 0; i < 2; ++i) {
        SCOPED_TRACE(i);
        ASSERT_EQ(addOperation(ANEURALNETWORKS_ADD, {0, 0, 1}, {2}), ANEURALNETWORKS_NO_ERROR);
    }

    ASSERT_EQ(identifyInputsAndOutputs({0, 1}, {2}), ANEURALNETWORKS_NO_ERROR);
    ASSERT_EQ(modelFinish(), ANEURALNETWORKS_BAD_DATA);
}

TEST_F(ValidationTestIdentify, Ok) {
    ASSERT_EQ(identifyInputsAndOutputs({0, 1, 2}, {3}), ANEURALNETWORKS_NO_ERROR);
    ASSERT_EQ(modelFinish(), ANEURALNETWORKS_NO_ERROR);
}

TEST_F(ValidationTestIdentify, InputIsOutput) {
    ASSERT_EQ(identifyInputsAndOutputs({0, 1, 2}, {3, 0}), ANEURALNETWORKS_BAD_DATA);
}

TEST_F(ValidationTestIdentify, OutputIsInput) {
    ASSERT_EQ(identifyInputsAndOutputs({0, 1, 2, 3}, {3}), ANEURALNETWORKS_BAD_DATA);
}

TEST_F(ValidationTestIdentify, DuplicateInputs) {
    ASSERT_EQ(identifyInputsAndOutputs({0, 1, 2, 0}, {3}), ANEURALNETWORKS_BAD_DATA);
}

TEST_F(ValidationTestIdentify, DuplicateOutputs) {
    ASSERT_EQ(identifyInputsAndOutputs({0, 1, 2}, {3, 3}), ANEURALNETWORKS_BAD_DATA);
}

// Also see TEST_F(ValidationTestCompilationForDevices_1, SetPreference)
TEST_F(ValidationTestCompilation, SetPreference) {
    EXPECT_EQ(ANeuralNetworksCompilation_setPreference(nullptr, ANEURALNETWORKS_PREFER_LOW_POWER),
              ANEURALNETWORKS_UNEXPECTED_NULL);

    EXPECT_EQ(ANeuralNetworksCompilation_setPreference(mCompilation, 40), ANEURALNETWORKS_BAD_DATA);
}

// Also see TEST_F(ValidationTestCompilationForDevices_1, SetCaching)
TEST_F(ValidationTestCompilation, SetCaching) {
    std::vector<uint8_t> token(ANEURALNETWORKS_BYTE_SIZE_OF_CACHE_TOKEN, 0);
    EXPECT_EQ(ANeuralNetworksCompilation_setCaching(nullptr, NN_TMP_DIR, token.data()),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksCompilation_setCaching(mCompilation, nullptr, token.data()),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksCompilation_setCaching(mCompilation, NN_TMP_DIR, nullptr),
              ANEURALNETWORKS_UNEXPECTED_NULL);
}

TEST_F(ValidationTestCompilation, SetPriority) {
    EXPECT_EQ(ANeuralNetworksCompilation_setPriority(nullptr, ANEURALNETWORKS_PRIORITY_DEFAULT),
              ANEURALNETWORKS_UNEXPECTED_NULL);

    // Test invalid values of priority.
    constexpr int kInvalidPriorities[] = {0,
                                          ANEURALNETWORKS_PRIORITY_LOW - 1,
                                          ANEURALNETWORKS_PRIORITY_LOW + 1,
                                          ANEURALNETWORKS_PRIORITY_MEDIUM - 1,
                                          ANEURALNETWORKS_PRIORITY_MEDIUM + 1,
                                          ANEURALNETWORKS_PRIORITY_HIGH - 1,
                                          ANEURALNETWORKS_PRIORITY_HIGH + 1};
    for (int invalidPriority : kInvalidPriorities) {
        EXPECT_EQ(ANeuralNetworksCompilation_setPriority(mCompilation, invalidPriority),
                  ANEURALNETWORKS_BAD_DATA);
    }
}

// Also see TEST_F(ValidationTestCompilationForDevices_1, SetTimeout)
// Also see TEST_F(ValidationTestCompilationForDevices_2, SetTimeout)
TEST_F(ValidationTestCompilation, SetTimeout) {
    EXPECT_EQ(ANeuralNetworksCompilation_setTimeout(nullptr, kShortWaitInNanoseconds),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    // Timeout can only be set on Compilations created from CompilationForDevices with one device
    // specified.
    EXPECT_EQ(ANeuralNetworksCompilation_setTimeout(mCompilation, kShortWaitInNanoseconds),
              ANEURALNETWORKS_BAD_DATA);
}

TEST_F(ValidationTestCompilation, GetPreferredMemoryAlignmentAndPadding) {
    if (__builtin_available(android __NNAPI_FL5_MIN_ANDROID_API__, *)) {
        uint32_t result;

        // The following calls should fail, because the compilation has not been finished.
        EXPECT_EQ(ANeuralNetworksCompilation_getPreferredMemoryAlignmentForInput(mCompilation, 0,
                                                                                 &result),
                  ANEURALNETWORKS_BAD_STATE);
        EXPECT_EQ(ANeuralNetworksCompilation_getPreferredMemoryPaddingForInput(mCompilation, 0,
                                                                               &result),
                  ANEURALNETWORKS_BAD_STATE);
        EXPECT_EQ(ANeuralNetworksCompilation_getPreferredMemoryAlignmentForOutput(mCompilation, 0,
                                                                                  &result),
                  ANEURALNETWORKS_BAD_STATE);
        EXPECT_EQ(ANeuralNetworksCompilation_getPreferredMemoryPaddingForOutput(mCompilation, 0,
                                                                                &result),
                  ANEURALNETWORKS_BAD_STATE);

        EXPECT_EQ(ANeuralNetworksCompilation_finish(mCompilation), ANEURALNETWORKS_NO_ERROR);

        // The following calls should fail because of unexpected nullptr.
        EXPECT_EQ(
                ANeuralNetworksCompilation_getPreferredMemoryAlignmentForInput(nullptr, 0, &result),
                ANEURALNETWORKS_UNEXPECTED_NULL);
        EXPECT_EQ(ANeuralNetworksCompilation_getPreferredMemoryAlignmentForInput(mCompilation, 0,
                                                                                 nullptr),
                  ANEURALNETWORKS_UNEXPECTED_NULL);
        EXPECT_EQ(ANeuralNetworksCompilation_getPreferredMemoryPaddingForInput(nullptr, 0, &result),
                  ANEURALNETWORKS_UNEXPECTED_NULL);
        EXPECT_EQ(ANeuralNetworksCompilation_getPreferredMemoryPaddingForInput(mCompilation, 0,
                                                                               nullptr),
                  ANEURALNETWORKS_UNEXPECTED_NULL);
        EXPECT_EQ(ANeuralNetworksCompilation_getPreferredMemoryAlignmentForOutput(nullptr, 0,
                                                                                  &result),
                  ANEURALNETWORKS_UNEXPECTED_NULL);
        EXPECT_EQ(ANeuralNetworksCompilation_getPreferredMemoryAlignmentForOutput(mCompilation, 0,
                                                                                  nullptr),
                  ANEURALNETWORKS_UNEXPECTED_NULL);
        EXPECT_EQ(
                ANeuralNetworksCompilation_getPreferredMemoryPaddingForOutput(nullptr, 0, &result),
                ANEURALNETWORKS_UNEXPECTED_NULL);
        EXPECT_EQ(ANeuralNetworksCompilation_getPreferredMemoryPaddingForOutput(mCompilation, 0,
                                                                                nullptr),
                  ANEURALNETWORKS_UNEXPECTED_NULL);

        // The following calls should fail, because the index is out of range.
        const uint32_t invalidIndex = 1000;
        EXPECT_EQ(ANeuralNetworksCompilation_getPreferredMemoryAlignmentForInput(
                          mCompilation, invalidIndex, &result),
                  ANEURALNETWORKS_BAD_DATA);
        EXPECT_EQ(ANeuralNetworksCompilation_getPreferredMemoryPaddingForInput(
                          mCompilation, invalidIndex, &result),
                  ANEURALNETWORKS_BAD_DATA);
        EXPECT_EQ(ANeuralNetworksCompilation_getPreferredMemoryAlignmentForOutput(
                          mCompilation, invalidIndex, &result),
                  ANEURALNETWORKS_BAD_DATA);
        EXPECT_EQ(ANeuralNetworksCompilation_getPreferredMemoryPaddingForOutput(
                          mCompilation, invalidIndex, &result),
                  ANEURALNETWORKS_BAD_DATA);

    } else {
        GTEST_SKIP();
    }
}

// Also see TEST_F(ValidationTestCompilationForDevices_1, CreateExecution)
TEST_F(ValidationTestCompilation, CreateExecution) {
    ANeuralNetworksExecution* execution = nullptr;
    EXPECT_EQ(ANeuralNetworksExecution_create(nullptr, &execution),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksExecution_create(mCompilation, nullptr),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksExecution_create(mCompilation, &execution), ANEURALNETWORKS_BAD_STATE);
}

// Also see TEST_F(ValidationTestCompilationForDevices_1, Finish)
TEST_F(ValidationTestCompilation, Finish) {
    EXPECT_EQ(ANeuralNetworksCompilation_finish(nullptr), ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksCompilation_finish(mCompilation), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksCompilation_setPreference(mCompilation,
                                                       ANEURALNETWORKS_PREFER_FAST_SINGLE_ANSWER),
              ANEURALNETWORKS_BAD_STATE);
    EXPECT_EQ(
            ANeuralNetworksCompilation_setPriority(mCompilation, ANEURALNETWORKS_PRIORITY_DEFAULT),
            ANEURALNETWORKS_BAD_STATE);
    EXPECT_EQ(ANeuralNetworksCompilation_setTimeout(mCompilation, kShortWaitInNanoseconds),
              ANEURALNETWORKS_BAD_STATE);
    std::vector<uint8_t> token(ANEURALNETWORKS_BYTE_SIZE_OF_CACHE_TOKEN, 0);
    EXPECT_EQ(ANeuralNetworksCompilation_setCaching(mCompilation, NN_TMP_DIR, token.data()),
              ANEURALNETWORKS_BAD_STATE);
    EXPECT_EQ(ANeuralNetworksCompilation_finish(mCompilation), ANEURALNETWORKS_BAD_STATE);
}

// Also see TEST_F(ValidationTestCompilationForDevices_1, ExecutionSetTimeout)
// Also see TEST_F(ValidationTestCompilationForDevices_2, ExecutionSetTimeout)
TEST_F(ValidationTestCompilation, ExecutionSetTimeout) {
    EXPECT_EQ(ANeuralNetworksExecution_setTimeout(nullptr, kShortWaitInNanoseconds),
              ANEURALNETWORKS_UNEXPECTED_NULL);

    ASSERT_EQ(ANeuralNetworksCompilation_finish(mCompilation), ANEURALNETWORKS_NO_ERROR);
    ANeuralNetworksExecution* execution;
    ASSERT_EQ(ANeuralNetworksExecution_create(mCompilation, &execution), ANEURALNETWORKS_NO_ERROR);
    // Timeout can only be set on Compilations created from CompilationForDevices with one device
    // specified.
    EXPECT_EQ(ANeuralNetworksExecution_setTimeout(execution, kShortWaitInNanoseconds),
              ANEURALNETWORKS_BAD_DATA);
    ANeuralNetworksExecution_free(execution);
}

// Also see TEST_F(ValidationTestCompilationForDevices_1, ExecutionTiming)
// Also see TEST_F(ValidationTestCompilationForDevices_2, ExecutionTiming)
TEST_F(ValidationTestCompilation, ExecutionTiming) {
    ASSERT_EQ(ANeuralNetworksCompilation_finish(mCompilation), ANEURALNETWORKS_NO_ERROR);
    ANeuralNetworksExecution* execution;
    ASSERT_EQ(ANeuralNetworksExecution_create(mCompilation, &execution), ANEURALNETWORKS_NO_ERROR);
    // Cannot setMeasureTiming() with Compilation rather than CompilationForDevices.
    EXPECT_EQ(ANeuralNetworksExecution_setMeasureTiming(execution, false),
              ANEURALNETWORKS_BAD_DATA);
    EXPECT_EQ(ANeuralNetworksExecution_setMeasureTiming(execution, true), ANEURALNETWORKS_BAD_DATA);

    // close memory
    ANeuralNetworksExecution_free(execution);
}

// Also see TEST_F(ValidationTestCompilationForDevices_1, ExecutionTiming)
TEST_F(ValidationTestCompilation, ExecutionUsability) {
    ASSERT_EQ(ANeuralNetworksCompilation_finish(mCompilation), ANEURALNETWORKS_NO_ERROR);

    enum class ExecutionType : uint32_t { ASYNC, SYNC, BURST, FENCED };
    for (auto executionType :
         {ExecutionType::ASYNC, ExecutionType::SYNC, ExecutionType::BURST, ExecutionType::FENCED}) {
        for (bool explicitlyDisableReusablility : {false, true}) {
            SCOPED_TRACE(static_cast<uint32_t>(executionType));
            SCOPED_TRACE(explicitlyDisableReusablility);

            ANeuralNetworksExecution* execution;
            ASSERT_EQ(ANeuralNetworksExecution_create(mCompilation, &execution),
                      ANEURALNETWORKS_NO_ERROR);

            if (explicitlyDisableReusablility) {
                if (__builtin_available(android __NNAPI_FL5_MIN_ANDROID_API__, *)) {
                    ASSERT_EQ(ANeuralNetworksExecution_setReusable(execution, false),
                              ANEURALNETWORKS_NO_ERROR);
                } else {
                    ANeuralNetworksExecution_free(execution);
                    continue;
                }
            }

            // Set inputs and outputs.
            float in0[] = {0.0f, 0.0f}, in1[] = {1.0f, 1.0f}, out0[2];
            int in2 = 0;
            ASSERT_EQ(ANeuralNetworksExecution_setInput(execution, 0, nullptr, &in0, sizeof(in0)),
                      ANEURALNETWORKS_NO_ERROR);
            ASSERT_EQ(ANeuralNetworksExecution_setInput(execution, 1, nullptr, &in1, sizeof(in1)),
                      ANEURALNETWORKS_NO_ERROR);
            ASSERT_EQ(ANeuralNetworksExecution_setInput(execution, 2, nullptr, &in2, sizeof(in2)),
                      ANEURALNETWORKS_NO_ERROR);
            ASSERT_EQ(
                    ANeuralNetworksExecution_setOutput(execution, 0, nullptr, &out0, sizeof(out0)),
                    ANEURALNETWORKS_NO_ERROR);

            const size_t memorySize = std::max(sizeof(in0), sizeof(out0));
#ifdef __ANDROID__
            int memoryFd = ASharedMemory_create("nnMemory", memorySize);
#else   // __ANDROID__
            TemporaryFile tmpFile;
            int memoryFd = tmpFile.release();
            CHECK_EQ(ftruncate(memoryFd, memorySize), 0);
#endif  // __ANDROID__
            ASSERT_GT(memoryFd, 0);
            ANeuralNetworksMemory* memory;
            EXPECT_EQ(ANeuralNetworksMemory_createFromFd(memorySize, PROT_READ | PROT_WRITE,
                                                         memoryFd, 0, &memory),
                      ANEURALNETWORKS_NO_ERROR);

            auto testTooLate = [this, execution, &in0, &out0, memory] {
                // Try a bunch of things that are impermissible if the execution has started.

                // Set loop timeout.
                ASSERT_EQ(
                        ANeuralNetworksExecution_setLoopTimeout(execution, kShortWaitInNanoseconds),
                        ANEURALNETWORKS_BAD_STATE);

                // Enable/Disable input and output padding.
                if (__builtin_available(android __NNAPI_FL5_MIN_ANDROID_API__, *)) {
                    ASSERT_EQ(ANeuralNetworksExecution_enableInputAndOutputPadding(execution, true),
                              ANEURALNETWORKS_BAD_STATE);
                    ASSERT_EQ(
                            ANeuralNetworksExecution_enableInputAndOutputPadding(execution, false),
                            ANEURALNETWORKS_BAD_STATE);
                }

                // Set inputs and outputs.
                ASSERT_EQ(
                        ANeuralNetworksExecution_setInput(execution, 0, nullptr, &in0, sizeof(in0)),
                        ANEURALNETWORKS_BAD_STATE);
                ASSERT_EQ(ANeuralNetworksExecution_setOutput(execution, 0, nullptr, &out0,
                                                             sizeof(out0)),
                          ANEURALNETWORKS_BAD_STATE);
                ASSERT_EQ(ANeuralNetworksExecution_setInputFromMemory(execution, 0, nullptr, memory,
                                                                      0, sizeof(in0)),
                          ANEURALNETWORKS_BAD_STATE);
                ASSERT_EQ(ANeuralNetworksExecution_setOutputFromMemory(execution, 0, nullptr,
                                                                       memory, 0, sizeof(out0)),
                          ANEURALNETWORKS_BAD_STATE);

                // Set reusable.
                if (__builtin_available(android __NNAPI_FL5_MIN_ANDROID_API__, *)) {
                    ASSERT_EQ(ANeuralNetworksExecution_setReusable(execution, true),
                              ANEURALNETWORKS_BAD_STATE);
                    ASSERT_EQ(ANeuralNetworksExecution_setReusable(execution, false),
                              ANEURALNETWORKS_BAD_STATE);
                }

                // Reuse for asynchronous execution.
                {
                    ANeuralNetworksEvent* event;
                    ASSERT_EQ(ANeuralNetworksExecution_startCompute(execution, &event),
                              ANEURALNETWORKS_BAD_STATE);
                }

                // Reuse for synchronous execution.
                ASSERT_EQ(ANeuralNetworksExecution_compute(execution), ANEURALNETWORKS_BAD_STATE);

                // Reuse for burst execution.
                {
                    ANeuralNetworksBurst* burst;
                    ASSERT_EQ(ANeuralNetworksBurst_create(mCompilation, &burst),
                              ANEURALNETWORKS_NO_ERROR);
                    ASSERT_EQ(ANeuralNetworksExecution_burstCompute(execution, burst),
                              ANEURALNETWORKS_BAD_STATE);
                    ANeuralNetworksBurst_free(burst);
                }

                // Reuse for fenced execution.
                {
                    ANeuralNetworksEvent* event;
                    ASSERT_EQ(ANeuralNetworksExecution_startComputeWithDependencies(
                                      execution, nullptr, 0, 0, &event),
                              ANEURALNETWORKS_BAD_STATE);
                }
            };

            // Compute.
            switch (executionType) {
                case ExecutionType::ASYNC: {
                    ANeuralNetworksEvent* event;
                    ASSERT_EQ(ANeuralNetworksExecution_startCompute(execution, &event),
                              ANEURALNETWORKS_NO_ERROR);
                    testTooLate();
                    ASSERT_EQ(ANeuralNetworksEvent_wait(event), ANEURALNETWORKS_NO_ERROR);
                    testTooLate();
                    ANeuralNetworksEvent_free(event);
                    break;
                }
                case ExecutionType::SYNC: {
                    ASSERT_EQ(ANeuralNetworksExecution_compute(execution),
                              ANEURALNETWORKS_NO_ERROR);
                    testTooLate();
                    break;
                }
                case ExecutionType::BURST: {
                    ANeuralNetworksBurst* burst;
                    ASSERT_EQ(ANeuralNetworksBurst_create(mCompilation, &burst),
                              ANEURALNETWORKS_NO_ERROR);
                    ASSERT_EQ(ANeuralNetworksExecution_burstCompute(execution, burst),
                              ANEURALNETWORKS_NO_ERROR);
                    testTooLate();
                    ANeuralNetworksBurst_free(burst);
                    break;
                }
                case ExecutionType::FENCED: {
                    ANeuralNetworksEvent* event;
                    ASSERT_EQ(ANeuralNetworksExecution_startComputeWithDependencies(
                                      execution, nullptr, 0, 0, &event),
                              ANEURALNETWORKS_NO_ERROR);
                    testTooLate();
                    ASSERT_EQ(ANeuralNetworksEvent_wait(event), ANEURALNETWORKS_NO_ERROR);
                    testTooLate();
                    ANeuralNetworksEvent_free(event);
                    break;
                }
                default:
                    FAIL() << "Unreachable";
            }

            // close memory
            ANeuralNetworksExecution_free(execution);
            ANeuralNetworksMemory_free(memory);
            close(memoryFd);
        }
    }
}

static void testConcurrentExecution(bool reusable, ANeuralNetworksCompilation* compilation) {
    ASSERT_EQ(ANeuralNetworksCompilation_finish(compilation), ANEURALNETWORKS_NO_ERROR);

    enum class ExecutionType : uint32_t { ASYNC, SYNC, BURST, FENCED };
    const auto compute = [compilation](ExecutionType executionType,
                                       ANeuralNetworksExecution* execution) -> int {
        switch (executionType) {
            case ExecutionType::ASYNC: {
                ANeuralNetworksEvent* event;
                int result = ANeuralNetworksExecution_startCompute(execution, &event);
                if (result == ANEURALNETWORKS_NO_ERROR) {
                    result = ANeuralNetworksEvent_wait(event);
                }
                ANeuralNetworksEvent_free(event);
                return result;
            }
            case ExecutionType::SYNC: {
                return ANeuralNetworksExecution_compute(execution);
            }
            case ExecutionType::BURST: {
                ANeuralNetworksBurst* burst;
                int result = ANeuralNetworksBurst_create(compilation, &burst);
                if (result == ANEURALNETWORKS_NO_ERROR) {
                    result = ANeuralNetworksExecution_burstCompute(execution, burst);
                }
                ANeuralNetworksBurst_free(burst);
                return result;
            }
            case ExecutionType::FENCED: {
                ANeuralNetworksEvent* event;
                int result = ANeuralNetworksExecution_startComputeWithDependencies(
                        execution, nullptr, 0, 0, &event);
                if (result == ANEURALNETWORKS_NO_ERROR) {
                    result = ANeuralNetworksEvent_wait(event);
                }
                ANeuralNetworksEvent_free(event);
                return result;
            }
        }
    };

    const std::vector<ExecutionType> kExecutionTypes = {
            ExecutionType::ASYNC, ExecutionType::SYNC, ExecutionType::BURST, ExecutionType::FENCED};
    for (auto executionType1 : kExecutionTypes) {
        for (auto executionType2 : kExecutionTypes) {
            SCOPED_TRACE(static_cast<uint32_t>(executionType1));
            SCOPED_TRACE(static_cast<uint32_t>(executionType2));

            ANeuralNetworksExecution* execution;
            ASSERT_EQ(ANeuralNetworksExecution_create(compilation, &execution),
                      ANEURALNETWORKS_NO_ERROR);

            float in0[] = {0.0f, 0.0f}, in1[] = {1.0f, 1.0f}, out0[2];
            int in2 = 0;
            ASSERT_EQ(ANeuralNetworksExecution_setInput(execution, 0, nullptr, &in0, sizeof(in0)),
                      ANEURALNETWORKS_NO_ERROR);
            ASSERT_EQ(ANeuralNetworksExecution_setInput(execution, 1, nullptr, &in1, sizeof(in1)),
                      ANEURALNETWORKS_NO_ERROR);
            ASSERT_EQ(ANeuralNetworksExecution_setInput(execution, 2, nullptr, &in2, sizeof(in2)),
                      ANEURALNETWORKS_NO_ERROR);
            ASSERT_EQ(
                    ANeuralNetworksExecution_setOutput(execution, 0, nullptr, &out0, sizeof(out0)),
                    ANEURALNETWORKS_NO_ERROR);
            if (__builtin_available(android __NNAPI_FL5_MIN_ANDROID_API__, *)) {
                ASSERT_EQ(ANeuralNetworksExecution_setReusable(execution, reusable),
                          ANEURALNETWORKS_NO_ERROR);
            } else {
                if (reusable) {
                    ANeuralNetworksExecution_free(execution);
                    return;
                }
            }

            // Compute on the same execution concurrently.
            auto first = std::async(std::launch::async, [compute, executionType1, execution] {
                return compute(executionType1, execution);
            });
            auto second = std::async(std::launch::async, [compute, executionType2, execution] {
                return compute(executionType2, execution);
            });
            const int result1 = first.get();
            const int result2 = second.get();

            // At least one result must be ANEURALNETWORKS_NO_ERROR. One may return
            // ANEURALNETWORKS_BAD_STATE if the other is already executing.
            EXPECT_TRUE(result1 == ANEURALNETWORKS_BAD_STATE ||
                        result1 == ANEURALNETWORKS_NO_ERROR);
            EXPECT_TRUE(result2 == ANEURALNETWORKS_BAD_STATE ||
                        result2 == ANEURALNETWORKS_NO_ERROR);
            EXPECT_TRUE(result1 == ANEURALNETWORKS_NO_ERROR || result2 == ANEURALNETWORKS_NO_ERROR);

            // If the execution is not reusable, one result must be ANEURALNETWORKS_BAD_STATE.
            if (!reusable) {
                EXPECT_TRUE(result1 == ANEURALNETWORKS_BAD_STATE ||
                            result2 == ANEURALNETWORKS_BAD_STATE);
            }

            ANeuralNetworksExecution_free(execution);
        }
    }
}

// Also see TEST_F(ValidationTestBurst, BurstComputeConcurrent)
TEST_F(ValidationTestCompilation, ReusableExecutionConcurrent) {
    testConcurrentExecution(/*reusable=*/true, mCompilation);
}
TEST_F(ValidationTestCompilation, NonReusableExecutionConcurrent) {
    testConcurrentExecution(/*reusable=*/false, mCompilation);
}

TEST_F(ValidationTestExecution, SetLoopTimeout) {
    EXPECT_EQ(ANeuralNetworksExecution_setLoopTimeout(nullptr, kShortWaitInNanoseconds),
              ANEURALNETWORKS_UNEXPECTED_NULL);
}

TEST_F(ValidationTestExecution, EnableInputAndOutputPadding) {
    if (__builtin_available(android __NNAPI_FL5_MIN_ANDROID_API__, *)) {
        EXPECT_EQ(ANeuralNetworksExecution_enableInputAndOutputPadding(nullptr, true),
                  ANEURALNETWORKS_UNEXPECTED_NULL);
        EXPECT_EQ(ANeuralNetworksExecution_enableInputAndOutputPadding(nullptr, false),
                  ANEURALNETWORKS_UNEXPECTED_NULL);
    } else {
        GTEST_SKIP();
    }
}

TEST_F(ValidationTestExecution, ExecutionSetReusable) {
    if (__builtin_available(android __NNAPI_FL5_MIN_ANDROID_API__, *)) {
        EXPECT_EQ(ANeuralNetworksExecution_setReusable(nullptr, true),
                  ANEURALNETWORKS_UNEXPECTED_NULL);
        EXPECT_EQ(ANeuralNetworksExecution_setReusable(nullptr, false),
                  ANEURALNETWORKS_UNEXPECTED_NULL);
    } else {
        GTEST_SKIP();
    }
}

TEST_F(ValidationTestExecution, SetInput) {
    char buffer[20];
    EXPECT_EQ(ANeuralNetworksExecution_setInput(nullptr, 0, nullptr, buffer, sizeof(float)),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksExecution_setInput(mExecution, 0, nullptr, nullptr, sizeof(float)),
              ANEURALNETWORKS_UNEXPECTED_NULL);

    // This should fail, because memory is not the size of a float32.
    EXPECT_EQ(ANeuralNetworksExecution_setInput(mExecution, 0, nullptr, buffer, 20),
              ANEURALNETWORKS_BAD_DATA);

    // This should fail, as this operand does not exist.
    EXPECT_EQ(ANeuralNetworksExecution_setInput(mExecution, 999, nullptr, buffer, sizeof(float)),
              ANEURALNETWORKS_BAD_DATA);

    // This should fail, as this operand does not exist.
    EXPECT_EQ(ANeuralNetworksExecution_setInput(mExecution, -1, nullptr, buffer, sizeof(float)),
              ANEURALNETWORKS_BAD_DATA);

    // These should fail, because the tensor types are invalid.
    EXPECT_EQ(ANeuralNetworksExecution_setInput(mExecution, 0, &kInvalidTensorType1, buffer,
                                                sizeof(float)),
              ANEURALNETWORKS_BAD_DATA);
    EXPECT_EQ(ANeuralNetworksExecution_setInput(mExecution, 0, &kInvalidTensorType2, buffer,
                                                sizeof(float)),
              ANEURALNETWORKS_BAD_DATA);

    // Cannot do this twice.
    EXPECT_EQ(ANeuralNetworksExecution_setInput(mExecution, 0, nullptr, buffer, 8),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksExecution_setInput(mExecution, 0, nullptr, buffer, 8),
              ANEURALNETWORKS_BAD_STATE);
}

TEST_F(ValidationTestExecution, SetInputEnablePadding) {
    if (__builtin_available(android __NNAPI_FL5_MIN_ANDROID_API__, *)) {
        EXPECT_EQ(ANeuralNetworksExecution_enableInputAndOutputPadding(mExecution, true),
                  ANEURALNETWORKS_NO_ERROR);

        // This should fail, because length is less than the size of a float32.
        char buffer[20];
        EXPECT_EQ(ANeuralNetworksExecution_setInput(mExecution, 0, nullptr, buffer,
                                                    sizeof(float) - 1),
                  ANEURALNETWORKS_BAD_DATA);
    } else {
        GTEST_SKIP();
    }
}

TEST_F(ValidationTestExecution, SetOutput) {
    char buffer[20];
    EXPECT_EQ(ANeuralNetworksExecution_setOutput(nullptr, 0, nullptr, buffer, sizeof(float)),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksExecution_setOutput(mExecution, 0, nullptr, nullptr, sizeof(float)),
              ANEURALNETWORKS_UNEXPECTED_NULL);

    // This should fail, because memory is not the size of a float32.
    EXPECT_EQ(ANeuralNetworksExecution_setOutput(mExecution, 0, nullptr, buffer, 20),
              ANEURALNETWORKS_BAD_DATA);

    // This should fail, as this operand does not exist.
    EXPECT_EQ(ANeuralNetworksExecution_setOutput(mExecution, 999, nullptr, buffer, sizeof(float)),
              ANEURALNETWORKS_BAD_DATA);

    // This should fail, as this operand does not exist.
    EXPECT_EQ(ANeuralNetworksExecution_setOutput(mExecution, -1, nullptr, buffer, sizeof(float)),
              ANEURALNETWORKS_BAD_DATA);

    // These should fail, because the tensor types are invalid.
    EXPECT_EQ(ANeuralNetworksExecution_setOutput(mExecution, 0, &kInvalidTensorType1, buffer,
                                                 sizeof(float)),
              ANEURALNETWORKS_BAD_DATA);
    EXPECT_EQ(ANeuralNetworksExecution_setOutput(mExecution, 0, &kInvalidTensorType2, buffer,
                                                 sizeof(float)),
              ANEURALNETWORKS_BAD_DATA);

    // Cannot do this twice.
    EXPECT_EQ(ANeuralNetworksExecution_setOutput(mExecution, 0, nullptr, buffer, 8),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksExecution_setOutput(mExecution, 0, nullptr, buffer, 8),
              ANEURALNETWORKS_BAD_STATE);
}

TEST_F(ValidationTestExecution, SetOutputEnablePadding) {
    if (__builtin_available(android __NNAPI_FL5_MIN_ANDROID_API__, *)) {
        EXPECT_EQ(ANeuralNetworksExecution_enableInputAndOutputPadding(mExecution, true),
                  ANEURALNETWORKS_NO_ERROR);

        // This should fail, because length is less than the size of a float32.
        char buffer[20];
        EXPECT_EQ(ANeuralNetworksExecution_setOutput(mExecution, 0, nullptr, buffer,
                                                     sizeof(float) - 1),
                  ANEURALNETWORKS_BAD_DATA);
    } else {
        GTEST_SKIP();
    }
}

TEST_F(ValidationTestExecution, SetInputFromMemory) {
    const size_t memorySize = 20;
#ifdef __ANDROID__
    int memoryFd = ASharedMemory_create("nnMemory", memorySize);
#else   // __ANDROID__
    TemporaryFile tmpFile;
    int memoryFd = tmpFile.release();
    CHECK_EQ(ftruncate(memoryFd, memorySize), 0);
#endif  // __ANDROID__
    ASSERT_GT(memoryFd, 0);

    ANeuralNetworksMemory* memory;
    EXPECT_EQ(ANeuralNetworksMemory_createFromFd(memorySize, PROT_READ | PROT_WRITE, memoryFd, 0,
                                                 &memory),
              ANEURALNETWORKS_NO_ERROR);

    EXPECT_EQ(ANeuralNetworksExecution_setInputFromMemory(nullptr, 0, nullptr, memory, 0,
                                                          sizeof(float)),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksExecution_setInputFromMemory(mExecution, 0, nullptr, nullptr, 0,
                                                          sizeof(float)),
              ANEURALNETWORKS_UNEXPECTED_NULL);

    // This should fail, because the operand does not exist.
    EXPECT_EQ(ANeuralNetworksExecution_setInputFromMemory(mExecution, 999, nullptr, memory, 0,
                                                          sizeof(float)),
              ANEURALNETWORKS_BAD_DATA);

    // This should fail, because the operand does not exist.
    EXPECT_EQ(ANeuralNetworksExecution_setInputFromMemory(mExecution, -1, nullptr, memory, 0,
                                                          sizeof(float)),
              ANEURALNETWORKS_BAD_DATA);

    // This should fail, because memory is not the size of a float32.
    EXPECT_EQ(ANeuralNetworksExecution_setInputFromMemory(mExecution, 0, nullptr, memory, 0,
                                                          memorySize),
              ANEURALNETWORKS_BAD_DATA);

    // This should fail, because offset is larger than memorySize.
    EXPECT_EQ(ANeuralNetworksExecution_setInputFromMemory(mExecution, 0, nullptr, memory,
                                                          memorySize + 1, sizeof(float)),
              ANEURALNETWORKS_BAD_DATA);

    // This should fail, because requested size is larger than the memory.
    EXPECT_EQ(ANeuralNetworksExecution_setInputFromMemory(mExecution, 0, nullptr, memory,
                                                          memorySize - 3, sizeof(float)),
              ANEURALNETWORKS_BAD_DATA);

    // These should fail, because the tensor types are invalid.
    EXPECT_EQ(ANeuralNetworksExecution_setInputFromMemory(mExecution, 0, &kInvalidTensorType1,
                                                          memory, 0, sizeof(float)),
              ANEURALNETWORKS_BAD_DATA);
    EXPECT_EQ(ANeuralNetworksExecution_setInputFromMemory(mExecution, 0, &kInvalidTensorType2,
                                                          memory, 0, sizeof(float)),
              ANEURALNETWORKS_BAD_DATA);

    // Cannot do this twice.
    EXPECT_EQ(ANeuralNetworksExecution_setInputFromMemory(mExecution, 0, nullptr, memory, 0, 8),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksExecution_setInputFromMemory(mExecution, 0, nullptr, memory, 0, 8),
              ANEURALNETWORKS_BAD_STATE);
    char buffer[memorySize];
    EXPECT_EQ(ANeuralNetworksExecution_setInput(mExecution, 0, nullptr, buffer, 8),
              ANEURALNETWORKS_BAD_STATE);

    // close memory
    ANeuralNetworksMemory_free(memory);
    close(memoryFd);
}

TEST_F(ValidationTestExecution, SetInputFromMemoryEnablePadding) {
    if (__builtin_available(android __NNAPI_FL5_MIN_ANDROID_API__, *)) {
        const size_t memorySize = 20;
#ifdef __ANDROID__
        int memoryFd = ASharedMemory_create("nnMemory", memorySize);
#else   // __ANDROID__
        TemporaryFile tmpFile;
        int memoryFd = tmpFile.release();
        CHECK_EQ(ftruncate(memoryFd, memorySize), 0);
#endif  // __ANDROID__
        ASSERT_GT(memoryFd, 0);

        ANeuralNetworksMemory* memory;
        EXPECT_EQ(ANeuralNetworksMemory_createFromFd(memorySize, PROT_READ | PROT_WRITE, memoryFd,
                                                     0, &memory),
                  ANEURALNETWORKS_NO_ERROR);

        EXPECT_EQ(ANeuralNetworksExecution_enableInputAndOutputPadding(mExecution, true),
                  ANEURALNETWORKS_NO_ERROR);

        // This should fail, because length is less than the size of a float32.
        EXPECT_EQ(ANeuralNetworksExecution_setInputFromMemory(mExecution, 0, nullptr, memory, 0,
                                                              sizeof(float) - 1),
                  ANEURALNETWORKS_BAD_DATA);

        // close memory
        ANeuralNetworksMemory_free(memory);
        close(memoryFd);
    } else {
        GTEST_SKIP();
    }
}

#ifdef __ANDROID__
TEST_F(ValidationTestExecution, SetInputFromAHardwareBufferBlob) {
    const size_t memorySize = 20;

    AHardwareBuffer_Desc desc{
            .width = memorySize,
            .height = 1,
            .layers = 1,
            .format = AHARDWAREBUFFER_FORMAT_BLOB,
            .usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
    };

    AHardwareBuffer* buffer = nullptr;
    ASSERT_EQ(AHardwareBuffer_allocate(&desc, &buffer), 0);

    ANeuralNetworksMemory* memory;
    EXPECT_EQ(ANeuralNetworksMemory_createFromAHardwareBuffer(buffer, &memory),
              ANEURALNETWORKS_NO_ERROR);

    // This should fail, because memory is not the size of a float32.
    EXPECT_EQ(ANeuralNetworksExecution_setInputFromMemory(mExecution, 0, nullptr, memory, 0,
                                                          memorySize),
              ANEURALNETWORKS_BAD_DATA);

    // This should fail, because offset is larger than memorySize.
    EXPECT_EQ(ANeuralNetworksExecution_setInputFromMemory(mExecution, 0, nullptr, memory,
                                                          memorySize + 1, sizeof(float)),
              ANEURALNETWORKS_BAD_DATA);
    // This should fail, because requested size is larger than the memory.
    EXPECT_EQ(ANeuralNetworksExecution_setInputFromMemory(mExecution, 0, nullptr, memory,
                                                          memorySize - 3, sizeof(float)),
              ANEURALNETWORKS_BAD_DATA);

    // These should fail, because the tensor types are invalid.
    EXPECT_EQ(ANeuralNetworksExecution_setInputFromMemory(mExecution, 0, &kInvalidTensorType1,
                                                          memory, 0, sizeof(float)),
              ANEURALNETWORKS_BAD_DATA);
    EXPECT_EQ(ANeuralNetworksExecution_setInputFromMemory(mExecution, 0, &kInvalidTensorType2,
                                                          memory, 0, sizeof(float)),
              ANEURALNETWORKS_BAD_DATA);

    // close memory
    ANeuralNetworksMemory_free(memory);
    AHardwareBuffer_release(buffer);
}

TEST_F(ValidationTestExecution, SetInputFromAHardwareBufferBlobEnablePadding) {
    if (__builtin_available(android __NNAPI_FL5_MIN_ANDROID_API__, *)) {
        const size_t memorySize = 20;

        AHardwareBuffer_Desc desc{
                .width = memorySize,
                .height = 1,
                .layers = 1,
                .format = AHARDWAREBUFFER_FORMAT_BLOB,
                .usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
                         AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
        };

        AHardwareBuffer* buffer = nullptr;
        ASSERT_EQ(AHardwareBuffer_allocate(&desc, &buffer), 0);

        ANeuralNetworksMemory* memory;
        EXPECT_EQ(ANeuralNetworksMemory_createFromAHardwareBuffer(buffer, &memory),
                  ANEURALNETWORKS_NO_ERROR);

        EXPECT_EQ(ANeuralNetworksExecution_enableInputAndOutputPadding(mExecution, true),
                  ANEURALNETWORKS_NO_ERROR);

        // This should fail, because length is less than the size of a float32.
        EXPECT_EQ(ANeuralNetworksExecution_setInputFromMemory(mExecution, 0, nullptr, memory, 0,
                                                              sizeof(float) - 1),
                  ANEURALNETWORKS_BAD_DATA);

        // close memory
        ANeuralNetworksMemory_free(memory);
        AHardwareBuffer_release(buffer);
    } else {
        GTEST_SKIP();
    }
}
#endif  // __ANDROID__

TEST_F(ValidationTestExecution, SetOutputFromMemory) {
    ANeuralNetworksExecution* execution;
    EXPECT_EQ(ANeuralNetworksExecution_create(mCompilation, &execution), ANEURALNETWORKS_NO_ERROR);

    const size_t memorySize = 20;
#ifdef __ANDROID__
    int memoryFd = ASharedMemory_create("nnMemory", memorySize);
#else   // __ANDROID__
    TemporaryFile tmpFile;
    int memoryFd = tmpFile.release();
    CHECK_EQ(ftruncate(memoryFd, memorySize), 0);
#endif  // __ANDROID__
    ASSERT_GT(memoryFd, 0);

    ANeuralNetworksMemory* memory;
    EXPECT_EQ(ANeuralNetworksMemory_createFromFd(memorySize, PROT_READ | PROT_WRITE, memoryFd, 0,
                                                 &memory),
              ANEURALNETWORKS_NO_ERROR);

    EXPECT_EQ(ANeuralNetworksExecution_setOutputFromMemory(nullptr, 0, nullptr, memory, 0,
                                                           sizeof(float)),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksExecution_setOutputFromMemory(execution, 0, nullptr, nullptr, 0,
                                                           sizeof(float)),
              ANEURALNETWORKS_UNEXPECTED_NULL);

    // This should fail, because the operand does not exist.
    EXPECT_EQ(ANeuralNetworksExecution_setOutputFromMemory(execution, 999, nullptr, memory, 0,
                                                           sizeof(float)),
              ANEURALNETWORKS_BAD_DATA);

    // This should fail, because the operand does not exist.
    EXPECT_EQ(ANeuralNetworksExecution_setOutputFromMemory(execution, -1, nullptr, memory, 0,
                                                           sizeof(float)),
              ANEURALNETWORKS_BAD_DATA);

    // This should fail, because memory is not the size of a float32.
    EXPECT_EQ(ANeuralNetworksExecution_setOutputFromMemory(execution, 0, nullptr, memory, 0,
                                                           memorySize),
              ANEURALNETWORKS_BAD_DATA);

    // This should fail, because offset is larger than memorySize.
    EXPECT_EQ(ANeuralNetworksExecution_setOutputFromMemory(execution, 0, nullptr, memory,
                                                           memorySize + 1, sizeof(float)),
              ANEURALNETWORKS_BAD_DATA);

    // This should fail, because requested size is larger than the memory.
    EXPECT_EQ(ANeuralNetworksExecution_setOutputFromMemory(execution, 0, nullptr, memory,
                                                           memorySize - 3, sizeof(float)),
              ANEURALNETWORKS_BAD_DATA);

    // These should fail, because the tensor types are invalid.
    EXPECT_EQ(ANeuralNetworksExecution_setOutputFromMemory(execution, 0, &kInvalidTensorType1,
                                                           memory, 0, sizeof(float)),
              ANEURALNETWORKS_BAD_DATA);
    EXPECT_EQ(ANeuralNetworksExecution_setOutputFromMemory(execution, 0, &kInvalidTensorType2,
                                                           memory, 0, sizeof(float)),
              ANEURALNETWORKS_BAD_DATA);

    // Cannot do this twice.
    EXPECT_EQ(ANeuralNetworksExecution_setOutputFromMemory(execution, 0, nullptr, memory, 0, 8),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksExecution_setOutputFromMemory(execution, 0, nullptr, memory, 0, 8),
              ANEURALNETWORKS_BAD_STATE);
    char buffer[memorySize];
    EXPECT_EQ(ANeuralNetworksExecution_setOutput(execution, 0, nullptr, buffer, 8),
              ANEURALNETWORKS_BAD_STATE);

    // close memory
    ANeuralNetworksMemory_free(memory);
    ANeuralNetworksExecution_free(execution);
    close(memoryFd);
}

TEST_F(ValidationTestExecution, SetOutputFromMemoryEnablePadding) {
    if (__builtin_available(android __NNAPI_FL5_MIN_ANDROID_API__, *)) {
        ANeuralNetworksExecution* execution;
        EXPECT_EQ(ANeuralNetworksExecution_create(mCompilation, &execution),
                  ANEURALNETWORKS_NO_ERROR);

        const size_t memorySize = 20;
#ifdef __ANDROID__
        int memoryFd = ASharedMemory_create("nnMemory", memorySize);
#else   // __ANDROID__
        TemporaryFile tmpFile;
        int memoryFd = tmpFile.release();
        CHECK_EQ(ftruncate(memoryFd, memorySize), 0);
#endif  // __ANDROID__
        ASSERT_GT(memoryFd, 0);

        ANeuralNetworksMemory* memory;
        EXPECT_EQ(ANeuralNetworksMemory_createFromFd(memorySize, PROT_READ | PROT_WRITE, memoryFd,
                                                     0, &memory),
                  ANEURALNETWORKS_NO_ERROR);

        EXPECT_EQ(ANeuralNetworksExecution_enableInputAndOutputPadding(mExecution, true),
                  ANEURALNETWORKS_NO_ERROR);

        // This should fail, because length is less than the size of a float32.
        EXPECT_EQ(ANeuralNetworksExecution_setOutputFromMemory(execution, 0, nullptr, memory, 0,
                                                               sizeof(float) - 1),
                  ANEURALNETWORKS_BAD_DATA);

        // close memory
        ANeuralNetworksMemory_free(memory);
        ANeuralNetworksExecution_free(execution);
        close(memoryFd);
    } else {
        GTEST_SKIP();
    }
}

#ifdef __ANDROID__
TEST_F(ValidationTestExecution, SetOutputFromAHardwareBufferBlob) {
    const size_t memorySize = 20;

    AHardwareBuffer_Desc desc{
            .width = memorySize,
            .height = 1,
            .layers = 1,
            .format = AHARDWAREBUFFER_FORMAT_BLOB,
            .usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
    };

    AHardwareBuffer* buffer = nullptr;
    ASSERT_EQ(AHardwareBuffer_allocate(&desc, &buffer), 0);

    ANeuralNetworksMemory* memory;
    EXPECT_EQ(ANeuralNetworksMemory_createFromAHardwareBuffer(buffer, &memory),
              ANEURALNETWORKS_NO_ERROR);

    // This should fail, because memory is not the size of a float32.
    EXPECT_EQ(ANeuralNetworksExecution_setOutputFromMemory(mExecution, 0, nullptr, memory, 0,
                                                           memorySize),
              ANEURALNETWORKS_BAD_DATA);

    // This should fail, because offset is larger than memorySize.
    EXPECT_EQ(ANeuralNetworksExecution_setOutputFromMemory(mExecution, 0, nullptr, memory,
                                                           memorySize + 1, sizeof(float)),
              ANEURALNETWORKS_BAD_DATA);

    // This should fail, because requested size is larger than the memory.
    EXPECT_EQ(ANeuralNetworksExecution_setOutputFromMemory(mExecution, 0, nullptr, memory,
                                                           memorySize - 3, sizeof(float)),
              ANEURALNETWORKS_BAD_DATA);

    // These should fail, because the tensor types are invalid.
    EXPECT_EQ(ANeuralNetworksExecution_setOutputFromMemory(mExecution, 0, &kInvalidTensorType1,
                                                           memory, 0, sizeof(float)),
              ANEURALNETWORKS_BAD_DATA);
    EXPECT_EQ(ANeuralNetworksExecution_setOutputFromMemory(mExecution, 0, &kInvalidTensorType2,
                                                           memory, 0, sizeof(float)),
              ANEURALNETWORKS_BAD_DATA);

    // close memory
    ANeuralNetworksMemory_free(memory);
    AHardwareBuffer_release(buffer);
}

TEST_F(ValidationTestExecution, SetOutputFromAHardwareBufferBlobEnablePadding) {
    if (__builtin_available(android __NNAPI_FL5_MIN_ANDROID_API__, *)) {
        const size_t memorySize = 20;

        AHardwareBuffer_Desc desc{
                .width = memorySize,
                .height = 1,
                .layers = 1,
                .format = AHARDWAREBUFFER_FORMAT_BLOB,
                .usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
                         AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
        };

        AHardwareBuffer* buffer = nullptr;
        ASSERT_EQ(AHardwareBuffer_allocate(&desc, &buffer), 0);

        ANeuralNetworksMemory* memory;
        EXPECT_EQ(ANeuralNetworksMemory_createFromAHardwareBuffer(buffer, &memory),
                  ANEURALNETWORKS_NO_ERROR);

        EXPECT_EQ(ANeuralNetworksExecution_enableInputAndOutputPadding(mExecution, true),
                  ANEURALNETWORKS_NO_ERROR);

        // This should fail, because length is less than the size of a float32.
        EXPECT_EQ(ANeuralNetworksExecution_setOutputFromMemory(mExecution, 0, nullptr, memory, 0,
                                                               sizeof(float) - 1),
                  ANEURALNETWORKS_BAD_DATA);

        // close memory
        ANeuralNetworksMemory_free(memory);
        AHardwareBuffer_release(buffer);
    } else {
        GTEST_SKIP();
    }
}
#endif  // __ANDROID__

TEST_F(ValidationTestExecution, EnablePaddingAfterSetInputOutput) {
    if (__builtin_available(android __NNAPI_FL5_MIN_ANDROID_API__, *)) {
        ANeuralNetworksExecution* execution;
        char buffer[20];
        const size_t memorySize = 20;
#ifdef __ANDROID__
        int memoryFd = ASharedMemory_create("nnMemory", memorySize);
#else   // __ANDROID__
        TemporaryFile tmpFile;
        int memoryFd = tmpFile.release();
        CHECK_EQ(ftruncate(memoryFd, memorySize), 0);
#endif  // __ANDROID__
        ASSERT_GT(memoryFd, 0);

        ANeuralNetworksMemory* memory;
        EXPECT_EQ(ANeuralNetworksMemory_createFromFd(memorySize, PROT_READ | PROT_WRITE, memoryFd,
                                                     0, &memory),
                  ANEURALNETWORKS_NO_ERROR);

        // Enable padding after setInput.
        EXPECT_EQ(ANeuralNetworksExecution_create(mCompilation, &execution),
                  ANEURALNETWORKS_NO_ERROR);
        EXPECT_EQ(ANeuralNetworksExecution_setInput(execution, 0, nullptr, buffer, 8),
                  ANEURALNETWORKS_NO_ERROR);
        EXPECT_EQ(ANeuralNetworksExecution_enableInputAndOutputPadding(execution, true),
                  ANEURALNETWORKS_BAD_STATE);
        ANeuralNetworksExecution_free(execution);

        // Enable padding after setInputFromMemory.
        EXPECT_EQ(ANeuralNetworksExecution_create(mCompilation, &execution),
                  ANEURALNETWORKS_NO_ERROR);
        EXPECT_EQ(ANeuralNetworksExecution_setInputFromMemory(execution, 0, nullptr, memory, 0, 8),
                  ANEURALNETWORKS_NO_ERROR);
        EXPECT_EQ(ANeuralNetworksExecution_enableInputAndOutputPadding(execution, true),
                  ANEURALNETWORKS_BAD_STATE);
        ANeuralNetworksExecution_free(execution);

        // Enable padding after setOutput.
        EXPECT_EQ(ANeuralNetworksExecution_create(mCompilation, &execution),
                  ANEURALNETWORKS_NO_ERROR);
        EXPECT_EQ(ANeuralNetworksExecution_setOutput(execution, 0, nullptr, buffer, 8),
                  ANEURALNETWORKS_NO_ERROR);
        EXPECT_EQ(ANeuralNetworksExecution_enableInputAndOutputPadding(execution, true),
                  ANEURALNETWORKS_BAD_STATE);
        ANeuralNetworksExecution_free(execution);

        // Enable padding after setOutputFromMemory.
        EXPECT_EQ(ANeuralNetworksExecution_create(mCompilation, &execution),
                  ANEURALNETWORKS_NO_ERROR);
        EXPECT_EQ(ANeuralNetworksExecution_setOutputFromMemory(execution, 0, nullptr, memory, 0, 8),
                  ANEURALNETWORKS_NO_ERROR);
        EXPECT_EQ(ANeuralNetworksExecution_enableInputAndOutputPadding(execution, true),
                  ANEURALNETWORKS_BAD_STATE);
        ANeuralNetworksExecution_free(execution);

        // close memory
        ANeuralNetworksMemory_free(memory);
        close(memoryFd);
    } else {
        GTEST_SKIP();
    }
}

TEST_F(ValidationTestExecutionDeviceMemory, SetInputFromMemory) {
    ANeuralNetworksMemoryDesc* desc;
    ASSERT_EQ(ANeuralNetworksMemoryDesc_create(&desc), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksMemoryDesc_addInputRole(desc, mCompilation, 0, 1.0f),
              ANEURALNETWORKS_NO_ERROR);

    // The following output roles are for init/deinit of the device memory.
    EXPECT_EQ(ANeuralNetworksMemoryDesc_addOutputRole(desc, mInitCompilation, 0, 1.0f),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksMemoryDesc_addOutputRole(desc, mDeinitCompilation, 0, 1.0f),
              ANEURALNETWORKS_NO_ERROR);

    EXPECT_EQ(ANeuralNetworksMemoryDesc_finish(desc), ANEURALNETWORKS_NO_ERROR);

    ANeuralNetworksMemory* memory;
    EXPECT_EQ(ANeuralNetworksMemory_createFromDesc(desc, &memory), ANEURALNETWORKS_NO_ERROR);
    ANeuralNetworksMemoryDesc_free(desc);

    // Uninitialized memory as input.
    executeWithMemoryAsInput(mCompilation, memory, ANEURALNETWORKS_OP_FAILED);

    // The memory is deinitialized between setInputFromMemory and compute.
    {
        // Initialize device memory.
        executeWithMemoryAsOutput(mInitCompilation, memory, ANEURALNETWORKS_NO_ERROR);

        float data = 0;
        ANeuralNetworksExecution* execution = nullptr;
        ASSERT_EQ(ANeuralNetworksExecution_create(mCompilation, &execution),
                  ANEURALNETWORKS_NO_ERROR);
        ASSERT_EQ(ANeuralNetworksExecution_setInputFromMemory(execution, 0, nullptr, memory, 0, 0),
                  ANEURALNETWORKS_NO_ERROR);
        ASSERT_EQ(ANeuralNetworksExecution_setOutput(execution, 0, nullptr, &data, sizeof(float)),
                  ANEURALNETWORKS_NO_ERROR);

        // Deinitialize device memory.
        executeWithMemoryAsOutput(mDeinitCompilation, memory, ANEURALNETWORKS_OP_FAILED);

        // Uninitialized memory as input at compute time.
        ASSERT_EQ(ANeuralNetworksExecution_compute(execution), ANEURALNETWORKS_OP_FAILED);
        ANeuralNetworksExecution_free(execution);
    }

    // Initialize device memory.
    executeWithMemoryAsOutput(mInitCompilation, memory, ANEURALNETWORKS_NO_ERROR);

    // Bad offset and length.
    EXPECT_EQ(ANeuralNetworksExecution_setInputFromMemory(mExecution, 0, nullptr, memory, 1, 0),
              ANEURALNETWORKS_BAD_DATA);
    EXPECT_EQ(ANeuralNetworksExecution_setInputFromMemory(mExecution, 0, nullptr, memory, 0,
                                                          sizeof(float)),
              ANEURALNETWORKS_BAD_DATA);

    // Bad usage -- not configured for this role.
    EXPECT_EQ(ANeuralNetworksExecution_setOutputFromMemory(mExecution, 0, nullptr, memory, 0, 0),
              ANEURALNETWORKS_BAD_DATA);

    // Deinitialize device memory.
    executeWithMemoryAsOutput(mDeinitCompilation, memory, ANEURALNETWORKS_OP_FAILED);

    // Uninitialized memory as input.
    executeWithMemoryAsInput(mCompilation, memory, ANEURALNETWORKS_OP_FAILED);

    ANeuralNetworksMemory_free(memory);
}

TEST_F(ValidationTestExecutionDeviceMemory, SetOutputFromMemory) {
    ANeuralNetworksMemoryDesc* desc;
    ASSERT_EQ(ANeuralNetworksMemoryDesc_create(&desc), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksMemoryDesc_addOutputRole(desc, mCompilation, 0, 1.0f),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksMemoryDesc_finish(desc), ANEURALNETWORKS_NO_ERROR);

    ANeuralNetworksMemory* memory;
    EXPECT_EQ(ANeuralNetworksMemory_createFromDesc(desc, &memory), ANEURALNETWORKS_NO_ERROR);
    ANeuralNetworksMemoryDesc_free(desc);

    // Bad offset and length.
    EXPECT_EQ(ANeuralNetworksExecution_setOutputFromMemory(mExecution, 0, nullptr, memory, 1, 0),
              ANEURALNETWORKS_BAD_DATA);
    EXPECT_EQ(ANeuralNetworksExecution_setOutputFromMemory(mExecution, 0, nullptr, memory, 0,
                                                           sizeof(float)),
              ANEURALNETWORKS_BAD_DATA);

    // Bad usage -- not configured for this role.
    EXPECT_EQ(ANeuralNetworksExecution_setInputFromMemory(mExecution, 0, nullptr, memory, 0, 0),
              ANEURALNETWORKS_BAD_DATA);

    ANeuralNetworksMemory_free(memory);
}

TEST_F(ValidationTestExecutionDeviceMemory, SetInputFromMemory_DynamicShape) {
    uint32_t dimension = 1, badDimension = 2;
    ANeuralNetworksOperandType badType = {
            .type = ANEURALNETWORKS_TENSOR_FLOAT32,
            .dimensionCount = 1,
            .dimensions = &badDimension,
    };

    ANeuralNetworksMemoryDesc* desc;
    ASSERT_EQ(ANeuralNetworksMemoryDesc_create(&desc), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksMemoryDesc_addInputRole(desc, mCompilationDynamic, 0, 1.0f),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksMemoryDesc_setDimensions(desc, 1, &dimension),
              ANEURALNETWORKS_NO_ERROR);

    // The following output role is for init of the device memory.
    EXPECT_EQ(ANeuralNetworksMemoryDesc_addOutputRole(desc, mInitCompilation, 0, 1.0f),
              ANEURALNETWORKS_NO_ERROR);

    EXPECT_EQ(ANeuralNetworksMemoryDesc_finish(desc), ANEURALNETWORKS_NO_ERROR);

    ANeuralNetworksMemory* memory;
    EXPECT_EQ(ANeuralNetworksMemory_createFromDesc(desc, &memory), ANEURALNETWORKS_NO_ERROR);
    ANeuralNetworksMemoryDesc_free(desc);

    // Initialize device memory.
    executeWithMemoryAsOutput(mInitCompilation, memory, ANEURALNETWORKS_NO_ERROR);

    // Incompatible dimensions between updated type and memory.
    EXPECT_EQ(ANeuralNetworksExecution_setInputFromMemory(mExecutionDynamic, 0, &badType, memory, 0,
                                                          0),
              ANEURALNETWORKS_BAD_DATA);

    ANeuralNetworksMemory_free(memory);
}

TEST_F(ValidationTestExecutionDeviceMemory, SetOutputFromMemory_DynamicShape) {
    uint32_t dimension = 1, badDimension = 2;
    ANeuralNetworksOperandType badType = {
            .type = ANEURALNETWORKS_TENSOR_FLOAT32,
            .dimensionCount = 1,
            .dimensions = &badDimension,
    };

    ANeuralNetworksMemoryDesc* desc;
    ASSERT_EQ(ANeuralNetworksMemoryDesc_create(&desc), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksMemoryDesc_addOutputRole(desc, mCompilationDynamic, 0, 1.0f),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksMemoryDesc_setDimensions(desc, 1, &dimension),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksMemoryDesc_finish(desc), ANEURALNETWORKS_NO_ERROR);

    ANeuralNetworksMemory* memory;
    EXPECT_EQ(ANeuralNetworksMemory_createFromDesc(desc, &memory), ANEURALNETWORKS_NO_ERROR);
    ANeuralNetworksMemoryDesc_free(desc);

    // Incompatible dimensions between updated type and memory.
    EXPECT_EQ(ANeuralNetworksExecution_setOutputFromMemory(mExecutionDynamic, 0, &badType, memory,
                                                           0, 0),
              ANEURALNETWORKS_BAD_DATA);

    ANeuralNetworksMemory_free(memory);
}

TEST_F(ValidationTestExecution, Compute) {
    EXPECT_EQ(ANeuralNetworksExecution_compute(nullptr), ANEURALNETWORKS_UNEXPECTED_NULL);
}

TEST_F(ValidationTestExecution, StartCompute) {
    ANeuralNetworksExecution* execution;
    EXPECT_EQ(ANeuralNetworksExecution_create(mCompilation, &execution), ANEURALNETWORKS_NO_ERROR);

    ANeuralNetworksEvent* event;
    EXPECT_EQ(ANeuralNetworksExecution_startCompute(nullptr, &event),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksExecution_startCompute(execution, nullptr),
              ANEURALNETWORKS_UNEXPECTED_NULL);

    // close memory
    ANeuralNetworksExecution_free(execution);
}

TEST_F(ValidationTestExecution, EventWait) {
    EXPECT_EQ(ANeuralNetworksEvent_wait(nullptr), ANEURALNETWORKS_UNEXPECTED_NULL);
}

TEST_F(ValidationTest, EventCreateFromSyncFenceFd) {
    ANeuralNetworksEvent* event;
    EXPECT_EQ(ANeuralNetworksEvent_createFromSyncFenceFd(-1, &event), ANEURALNETWORKS_BAD_DATA);
    EXPECT_EQ(ANeuralNetworksEvent_createFromSyncFenceFd(1, nullptr),
              ANEURALNETWORKS_UNEXPECTED_NULL);
}

TEST_F(ValidationTest, EventGetSyncFenceFd) {
    int syncFd = -100;
    EXPECT_EQ(ANeuralNetworksEvent_getSyncFenceFd(nullptr, &syncFd),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(syncFd, -1);
}

TEST_F(ValidationTestExecution, EventGetSyncFenceFdFromStartCompute) {
    // Create a valid execution and event first.
    ANeuralNetworksExecution* execution;
    EXPECT_EQ(ANeuralNetworksExecution_create(mCompilation, &execution), ANEURALNETWORKS_NO_ERROR);
    float input0[] = {1.0f, 1.0f}, input1[] = {2.0f, 2.0f}, output0[2];
    int32_t input2[] = {0};
    EXPECT_EQ(ANeuralNetworksExecution_setInput(execution, 0, nullptr, input0, sizeof(input0)),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksExecution_setInput(execution, 1, nullptr, input1, sizeof(input1)),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksExecution_setInput(execution, 2, nullptr, input2, sizeof(input2)),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksExecution_setOutput(execution, 0, nullptr, output0, sizeof(output0)),
              ANEURALNETWORKS_NO_ERROR);
    ANeuralNetworksEvent* event = nullptr;
    EXPECT_EQ(ANeuralNetworksExecution_startCompute(execution, &event), ANEURALNETWORKS_NO_ERROR);

    // The event from startCompute is not backed by sync fence.
    int syncFd = -100;
    EXPECT_EQ(ANeuralNetworksEvent_getSyncFenceFd(event, &syncFd), ANEURALNETWORKS_BAD_DATA);
    EXPECT_EQ(syncFd, -1);

    ANeuralNetworksEvent_free(event);
    ANeuralNetworksExecution_free(execution);
}

TEST_F(ValidationTestExecution, FencedExecution) {
    // Create a valid execution and event first.
    ANeuralNetworksExecution* execution1;
    EXPECT_EQ(ANeuralNetworksExecution_create(mCompilation, &execution1), ANEURALNETWORKS_NO_ERROR);
    float input0[] = {1.0f, 1.0f}, input1[] = {2.0f, 2.0f}, output0[2];
    int32_t input2[] = {0};
    EXPECT_EQ(ANeuralNetworksExecution_setInput(execution1, 0, nullptr, input0, sizeof(input0)),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksExecution_setInput(execution1, 1, nullptr, input1, sizeof(input1)),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksExecution_setInput(execution1, 2, nullptr, input2, sizeof(input2)),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksExecution_setOutput(execution1, 0, nullptr, output0, sizeof(output0)),
              ANEURALNETWORKS_NO_ERROR);
    ANeuralNetworksEvent* event1 = nullptr;
    EXPECT_EQ(ANeuralNetworksExecution_startComputeWithDependencies(execution1, nullptr, 0, 0,
                                                                    &event1),
              ANEURALNETWORKS_NO_ERROR);

    EXPECT_EQ(ANeuralNetworksEvent_getSyncFenceFd(event1, nullptr),
              ANEURALNETWORKS_UNEXPECTED_NULL);

    // The event from startComputeWithDependencie may or may not be backed by a sync fence depending
    // on the driver implementation.
    int syncFd = -100;
    int getSyncFdResult = ANeuralNetworksEvent_getSyncFenceFd(event1, &syncFd);
    if (getSyncFdResult == ANEURALNETWORKS_NO_ERROR) {
        EXPECT_GE(syncFd, 0);
        close(syncFd);
    } else {
        EXPECT_EQ(getSyncFdResult, ANEURALNETWORKS_BAD_DATA);
        EXPECT_EQ(syncFd, -1);
    }

    // The subsequent execution will wait for the first execution to finish.
    ANeuralNetworksExecution* execution2;
    ANeuralNetworksEvent* event2 = nullptr;
    EXPECT_EQ(ANeuralNetworksExecution_create(mCompilation, &execution2), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(
            ANeuralNetworksExecution_startComputeWithDependencies(nullptr, &event1, 1, 0, &event2),
            ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksExecution_startComputeWithDependencies(execution2, nullptr, 1, 0,
                                                                    &event2),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksExecution_startComputeWithDependencies(execution2, &event1, 1, 0,
                                                                    nullptr),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    ANeuralNetworksEvent* wait_for_list[] = {event1, nullptr};
    EXPECT_EQ(ANeuralNetworksExecution_startComputeWithDependencies(execution2, wait_for_list, 2, 0,
                                                                    &event2),
              ANEURALNETWORKS_UNEXPECTED_NULL);

    ANeuralNetworksEvent_free(event1);
    ANeuralNetworksExecution_free(execution1);
    ANeuralNetworksExecution_free(execution2);
}

TEST_F(ValidationTestExecution, GetOutputOperandRankAndDimensions) {
    ANeuralNetworksExecution* execution;
    EXPECT_EQ(ANeuralNetworksExecution_create(mCompilation, &execution), ANEURALNETWORKS_NO_ERROR);

    float input0[] = {1.0f, 1.0f}, input1[] = {2.0f, 2.0f}, output0[2];
    int32_t input2[] = {0};
    EXPECT_EQ(ANeuralNetworksExecution_setInput(execution, 0, nullptr, input0, sizeof(input0)),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksExecution_setInput(execution, 1, nullptr, input1, sizeof(input1)),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksExecution_setInput(execution, 2, nullptr, input2, sizeof(input2)),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksExecution_setOutput(execution, 0, nullptr, output0, sizeof(output0)),
              ANEURALNETWORKS_NO_ERROR);

    uint32_t rank, dims[4], expectedRank = 1, expectedDims = 2;
    // This should fail, because the execution has not yet started to compute.
    EXPECT_EQ(ANeuralNetworksExecution_getOutputOperandRank(execution, 0, &rank),
              ANEURALNETWORKS_BAD_STATE);
    EXPECT_EQ(ANeuralNetworksExecution_getOutputOperandDimensions(execution, 0, dims),
              ANEURALNETWORKS_BAD_STATE);

    ANeuralNetworksEvent* event;
    EXPECT_EQ(ANeuralNetworksExecution_startCompute(execution, &event), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksEvent_wait(event), ANEURALNETWORKS_NO_ERROR);

    // This should fail, because unexpected nullptr.
    EXPECT_EQ(ANeuralNetworksExecution_getOutputOperandRank(nullptr, 0, &rank),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksExecution_getOutputOperandDimensions(nullptr, 0, dims),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksExecution_getOutputOperandRank(execution, 0, nullptr),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksExecution_getOutputOperandDimensions(execution, 0, nullptr),
              ANEURALNETWORKS_UNEXPECTED_NULL);

    // This should fail, because the operand does not exist.
    EXPECT_EQ(ANeuralNetworksExecution_getOutputOperandRank(execution, -1, &rank),
              ANEURALNETWORKS_BAD_DATA);
    EXPECT_EQ(ANeuralNetworksExecution_getOutputOperandRank(execution, 999, &rank),
              ANEURALNETWORKS_BAD_DATA);
    EXPECT_EQ(ANeuralNetworksExecution_getOutputOperandDimensions(execution, -1, dims),
              ANEURALNETWORKS_BAD_DATA);
    EXPECT_EQ(ANeuralNetworksExecution_getOutputOperandDimensions(execution, 999, dims),
              ANEURALNETWORKS_BAD_DATA);

    EXPECT_EQ(ANeuralNetworksExecution_getOutputOperandRank(execution, 0, &rank),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksExecution_getOutputOperandDimensions(execution, 0, dims),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(rank, expectedRank);
    EXPECT_EQ(dims[0], expectedDims);

    // close memory
    ANeuralNetworksEvent_free(event);
    ANeuralNetworksExecution_free(execution);
}

// Regression test for b/146044137.
class ValidationTestDimensionProductOverflow : public ValidationTestExecution {
   protected:
    void createModel() override {
        uint32_t dimensions[] = {5, 4, 4, 0, 5, 3, 0, 4, 5};
        ANeuralNetworksOperandType operandType = {
                .type = ANEURALNETWORKS_TENSOR_FLOAT32,
                .dimensionCount = std::size(dimensions),
                .dimensions = dimensions,
        };
        addOperand(operandType);
        addOperand(operandType);
        ASSERT_EQ(addOperation(ANEURALNETWORKS_ABS, {0}, {1}), ANEURALNETWORKS_NO_ERROR);
        ASSERT_EQ(identifyInputsAndOutputs({0}, {1}), ANEURALNETWORKS_NO_ERROR);
        ASSERT_EQ(modelFinish(), ANEURALNETWORKS_NO_ERROR);
    }
};

TEST_F(ValidationTestDimensionProductOverflow, SetInputOrOutput) {
    uint32_t dimensions[] = {5, 4, 4, 786433, 5, 3, 16777216, 4, 5};
    ANeuralNetworksOperandType operandType = {
            .type = ANEURALNETWORKS_TENSOR_FLOAT32,
            .dimensionCount = std::size(dimensions),
            .dimensions = dimensions,
    };
    uint8_t buffer[20];
    // This should fail, as the new operand type's dimension product overflows
    // uint32_t.
    EXPECT_EQ(
            ANeuralNetworksExecution_setInput(mExecution, 0, &operandType, buffer, sizeof(buffer)),
            ANEURALNETWORKS_BAD_DATA);
    EXPECT_EQ(
            ANeuralNetworksExecution_setOutput(mExecution, 0, &operandType, buffer, sizeof(buffer)),
            ANEURALNETWORKS_BAD_DATA);
}

TEST_F(ValidationTestModel, AddOperandDimensionProductOverflow) {
    uint32_t dimensions[] = {5, 4, 4, 786433, 5, 3, 16777216, 4, 5};
    ANeuralNetworksOperandType operandType = {
            .type = ANEURALNETWORKS_TENSOR_FLOAT32,
            .dimensionCount = std::size(dimensions),
            .dimensions = dimensions,
    };
    // This should fail, as the operand type's dimension product overflows uint32_t.
    ASSERT_EQ(ANeuralNetworksModel_addOperand(mModel, &operandType), ANEURALNETWORKS_BAD_DATA);
}

class ValidationTestDimensionProductOverflow2 : public ValidationTestExecution {
   protected:
    void createModel() override {
        addTensorOperand(ANEURALNETWORKS_TENSOR_FLOAT32, {0, 1});
        addTensorOperand(ANEURALNETWORKS_TENSOR_FLOAT32, {0, 1});
        addTensorOperand(ANEURALNETWORKS_TENSOR_FLOAT32, {0});
        addScalarOperand(ANEURALNETWORKS_INT32);
        int32_t activation = 0;
        ASSERT_EQ(ANeuralNetworksModel_setOperandValue(mModel, 3, &activation, sizeof(activation)),
                  ANEURALNETWORKS_NO_ERROR);
        addTensorOperand(ANEURALNETWORKS_TENSOR_FLOAT32, {0, 0});
        ASSERT_EQ(addOperation(ANEURALNETWORKS_FULLY_CONNECTED, {0, 1, 2, 3}, {4}),
                  ANEURALNETWORKS_NO_ERROR);
        ASSERT_EQ(identifyInputsAndOutputs({0, 1, 2}, {4}), ANEURALNETWORKS_NO_ERROR);
        ASSERT_EQ(modelFinish(), ANEURALNETWORKS_NO_ERROR);
    }
};

TEST_F(ValidationTestDimensionProductOverflow2, DynamicOutputShapeOverflow) {
    constexpr uint32_t kLargeDim = 1 << 16;
    std::vector<float> inputData(kLargeDim), outputData(kLargeDim);
    const uint32_t inputDims[] = {kLargeDim, 1};
    const uint32_t biasDims[] = {kLargeDim};
    const ANeuralNetworksOperandType inputType = {
            .type = ANEURALNETWORKS_TENSOR_FLOAT32,
            .dimensionCount = std::size(inputDims),
            .dimensions = inputDims,
    };
    const ANeuralNetworksOperandType biasType = {
            .type = ANEURALNETWORKS_TENSOR_FLOAT32,
            .dimensionCount = std::size(biasDims),
            .dimensions = biasDims,
    };
    EXPECT_EQ(ANeuralNetworksExecution_setInput(mExecution, 0, &inputType, inputData.data(),
                                                inputData.size() * sizeof(float)),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksExecution_setInput(mExecution, 1, &inputType, inputData.data(),
                                                inputData.size() * sizeof(float)),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksExecution_setInput(mExecution, 2, &biasType, inputData.data(),
                                                inputData.size() * sizeof(float)),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksExecution_setOutput(mExecution, 0, nullptr, outputData.data(),
                                                 outputData.size() * sizeof(float)),
              ANEURALNETWORKS_NO_ERROR);

    // This should fail, because the deduced output data size overflows uint32_t.
    EXPECT_NE(ANeuralNetworksExecution_compute(mExecution), ANEURALNETWORKS_NO_ERROR);
}

TEST_F(ValidationTestBurst, BurstComputeNull) {
    EXPECT_EQ(ANeuralNetworksExecution_burstCompute(mExecution, nullptr),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksExecution_burstCompute(nullptr, mBurst),
              ANEURALNETWORKS_UNEXPECTED_NULL);
}

TEST_F(ValidationTestBurst, BurstComputeBadCompilation) {
    ANeuralNetworksCompilation* compilation;
    ASSERT_EQ(ANeuralNetworksCompilation_create(mModel, &compilation), ANEURALNETWORKS_NO_ERROR);
    // NOTE: ANeuralNetworksCompilation_finish not called

    ANeuralNetworksBurst* burst;
    EXPECT_EQ(ANeuralNetworksBurst_create(compilation, &burst), ANEURALNETWORKS_BAD_STATE);

    // close memory
    ANeuralNetworksBurst_free(burst);
    ANeuralNetworksCompilation_free(compilation);
}

TEST_F(ValidationTestBurst, BurstComputeDifferentCompilations) {
    ANeuralNetworksCompilation* secondCompilation;
    ASSERT_EQ(ANeuralNetworksCompilation_create(mModel, &secondCompilation),
              ANEURALNETWORKS_NO_ERROR);
    ASSERT_EQ(ANeuralNetworksCompilation_finish(secondCompilation), ANEURALNETWORKS_NO_ERROR);

    ANeuralNetworksExecution* execution;
    EXPECT_EQ(ANeuralNetworksExecution_create(secondCompilation, &execution),
              ANEURALNETWORKS_NO_ERROR);

    EXPECT_EQ(ANeuralNetworksExecution_burstCompute(execution, mBurst), ANEURALNETWORKS_BAD_DATA);

    ANeuralNetworksExecution_free(execution);
    ANeuralNetworksCompilation_free(secondCompilation);
}

TEST_F(ValidationTestBurst, BurstComputeConcurrent) {
    ANeuralNetworksExecution* secondExecution;
    EXPECT_EQ(ANeuralNetworksExecution_create(mCompilation, &secondExecution),
              ANEURALNETWORKS_NO_ERROR);

    // set inputs of first execution
    float inputA0[] = {1.0f, 1.0f}, inputA1[] = {2.0f, 2.0f}, outputA0[2];
    int32_t inputA2[] = {0};
    EXPECT_EQ(ANeuralNetworksExecution_setInput(mExecution, 0, nullptr, inputA0, sizeof(inputA0)),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksExecution_setInput(mExecution, 1, nullptr, inputA1, sizeof(inputA1)),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksExecution_setInput(mExecution, 2, nullptr, inputA2, sizeof(inputA2)),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(
            ANeuralNetworksExecution_setOutput(mExecution, 0, nullptr, outputA0, sizeof(outputA0)),
            ANEURALNETWORKS_NO_ERROR);

    // set inputs of second execution
    float inputB0[] = {1.0f, 1.0f}, inputB1[] = {2.0f, 2.0f}, outputB0[2];
    int32_t inputB2[] = {0};
    EXPECT_EQ(ANeuralNetworksExecution_setInput(secondExecution, 0, nullptr, inputB0,
                                                sizeof(inputB0)),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksExecution_setInput(secondExecution, 1, nullptr, inputB1,
                                                sizeof(inputB1)),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksExecution_setInput(secondExecution, 2, nullptr, inputB2,
                                                sizeof(inputB2)),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksExecution_setOutput(secondExecution, 0, nullptr, outputB0,
                                                 sizeof(outputB0)),
              ANEURALNETWORKS_NO_ERROR);

    // Execute on the same burst concurrently. At least one result must be
    // ANEURALNETWORKS_NO_ERROR. One may return ANEURALNETWORKS_BAD_STATE if the
    // other is already executing on the burst.
    auto first = std::async(std::launch::async, [this] {
        return ANeuralNetworksExecution_burstCompute(mExecution, mBurst);
    });
    auto second = std::async(std::launch::async, [this, secondExecution] {
        return ANeuralNetworksExecution_burstCompute(secondExecution, mBurst);
    });

    const int result1 = first.get();
    const int result2 = second.get();
    EXPECT_TRUE(result1 == ANEURALNETWORKS_BAD_STATE || result1 == ANEURALNETWORKS_NO_ERROR);
    EXPECT_TRUE(result2 == ANEURALNETWORKS_BAD_STATE || result2 == ANEURALNETWORKS_NO_ERROR);
    EXPECT_TRUE(result1 == ANEURALNETWORKS_NO_ERROR || result2 == ANEURALNETWORKS_NO_ERROR);

    ANeuralNetworksExecution_free(secondExecution);
}

// The burst object maintains a local cache of memory objects. Because the burst
// is intended to live for multiple executions, and because memory might be
// created and freed for each execution, burst includes internal mechanisms to
// purge memory objects from its cache that have been freed by the NNAPI client.
// The following two test cases (FreeMemoryBeforeBurst and
// FreeBurstBeforeMemory) ensure that this internal cleanup is tested in both
// freeing orders.
//
// These two test cases explicitly create a new burst object and a new execution
// object so that the order of freeing can be specified. If these tests instead
// relied on the provided mExecution and mBurst, mBurst would always be freed
// before mExecution.

TEST_F(ValidationTestBurst, FreeMemoryBeforeBurst) {
    ANeuralNetworksBurst* burst;
    EXPECT_EQ(ANeuralNetworksBurst_create(mCompilation, &burst), ANEURALNETWORKS_NO_ERROR);

    // prepare data for execution
    float input0[] = {1.0f, 1.0f}, input1[] = {2.0f, 2.0f}, output0[2];
    int32_t input2[] = {0};

    const size_t memorySize = sizeof(output0);
#ifdef __ANDROID__
    int memoryFd = ASharedMemory_create("nnMemory", memorySize);
#else   // __ANDROID__
    TemporaryFile tmpFile;
    int memoryFd = tmpFile.release();
    CHECK_EQ(ftruncate(memoryFd, memorySize), 0);
#endif  // __ANDROID__
    ASSERT_GT(memoryFd, 0);

    ANeuralNetworksMemory* memory;
    EXPECT_EQ(ANeuralNetworksMemory_createFromFd(memorySize, PROT_READ | PROT_WRITE, memoryFd, 0,
                                                 &memory),
              ANEURALNETWORKS_NO_ERROR);

    // create and configure execution
    ANeuralNetworksExecution* execution;
    EXPECT_EQ(ANeuralNetworksExecution_create(mCompilation, &execution), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksExecution_setInput(execution, 0, nullptr, input0, sizeof(input0)),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksExecution_setInput(execution, 1, nullptr, input1, sizeof(input1)),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksExecution_setInput(execution, 2, nullptr, input2, sizeof(input2)),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksExecution_setOutputFromMemory(execution, 0, nullptr, memory, 0,
                                                           sizeof(output0)),
              ANEURALNETWORKS_NO_ERROR);

    // preform execution to cache memory into burst
    EXPECT_EQ(ANeuralNetworksExecution_burstCompute(execution, burst), ANEURALNETWORKS_NO_ERROR);
    ANeuralNetworksExecution_free(execution);

    // free memory before burst
    ANeuralNetworksMemory_free(memory);
    ANeuralNetworksBurst_free(burst);

    // close memory
    close(memoryFd);
}

TEST_F(ValidationTestBurst, FreeBurstBeforeMemory) {
    ANeuralNetworksBurst* burst;
    EXPECT_EQ(ANeuralNetworksBurst_create(mCompilation, &burst), ANEURALNETWORKS_NO_ERROR);

    // prepare data for execution
    float input0[] = {1.0f, 1.0f}, input1[] = {2.0f, 2.0f}, output0[2];
    int32_t input2[] = {0};
    const size_t memorySize = sizeof(output0);
#ifdef __ANDROID__
    int memoryFd = ASharedMemory_create("nnMemory", memorySize);
#else   // __ANDROID__
    TemporaryFile tmpFile;
    int memoryFd = tmpFile.release();
    CHECK_EQ(ftruncate(memoryFd, memorySize), 0);
#endif  // __ANDROID__
    ASSERT_GT(memoryFd, 0);

    ANeuralNetworksMemory* memory;
    EXPECT_EQ(ANeuralNetworksMemory_createFromFd(memorySize, PROT_READ | PROT_WRITE, memoryFd, 0,
                                                 &memory),
              ANEURALNETWORKS_NO_ERROR);

    // create and configure execution
    ANeuralNetworksExecution* execution;
    EXPECT_EQ(ANeuralNetworksExecution_create(mCompilation, &execution), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksExecution_setInput(execution, 0, nullptr, input0, sizeof(input0)),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksExecution_setInput(execution, 1, nullptr, input1, sizeof(input1)),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksExecution_setInput(execution, 2, nullptr, input2, sizeof(input2)),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksExecution_setOutputFromMemory(execution, 0, nullptr, memory, 0,
                                                           sizeof(output0)),
              ANEURALNETWORKS_NO_ERROR);

    // preform execution to cache memory into burst
    EXPECT_EQ(ANeuralNetworksExecution_burstCompute(execution, burst), ANEURALNETWORKS_NO_ERROR);
    ANeuralNetworksExecution_free(execution);

    // free burst before memory
    ANeuralNetworksBurst_free(burst);
    ANeuralNetworksMemory_free(memory);

    // close memory
    close(memoryFd);
}

TEST(ValidationTestIntrospection, GetNumDevices) {
    uint32_t numDevices = 0;
    EXPECT_EQ(ANeuralNetworks_getDeviceCount(&numDevices), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworks_getDeviceCount(nullptr), ANEURALNETWORKS_UNEXPECTED_NULL);
}

TEST(ValidationTestIntrospection, GetDevice) {
    uint32_t numDevices = 0;
    EXPECT_EQ(ANeuralNetworks_getDeviceCount(&numDevices), ANEURALNETWORKS_NO_ERROR);

    ANeuralNetworksDevice* device = nullptr;
    for (uint32_t i = 0; i < numDevices; i++) {
        SCOPED_TRACE(i);
        EXPECT_EQ(ANeuralNetworks_getDevice(i, &device), ANEURALNETWORKS_NO_ERROR);
        EXPECT_NE(device, nullptr);
    }
    EXPECT_EQ(ANeuralNetworks_getDevice(0, nullptr), ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworks_getDevice(numDevices, &device), ANEURALNETWORKS_BAD_DATA);
}

static void deviceStringCheck(std::function<int(const ANeuralNetworksDevice*, const char**)> func) {
    uint32_t numDevices = 0;
    EXPECT_EQ(ANeuralNetworks_getDeviceCount(&numDevices), ANEURALNETWORKS_NO_ERROR);

    const char* buffer;
    for (uint32_t i = 0; i < numDevices; i++) {
        SCOPED_TRACE(i);
        ANeuralNetworksDevice* device;
        EXPECT_EQ(ANeuralNetworks_getDevice(i, &device), ANEURALNETWORKS_NO_ERROR);
        EXPECT_EQ(func(device, &buffer), ANEURALNETWORKS_NO_ERROR);
        EXPECT_EQ(func(device, nullptr), ANEURALNETWORKS_UNEXPECTED_NULL);
    }
    EXPECT_EQ(func(nullptr, &buffer), ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(func(nullptr, nullptr), ANEURALNETWORKS_UNEXPECTED_NULL);
}

TEST(ValidationTestIntrospection, DeviceGetName) {
    deviceStringCheck(ANeuralNetworksDevice_getName);
}

TEST(ValidationTestIntrospection, DeviceGetNameUnique) {
    uint32_t numDevices = 0;
    EXPECT_EQ(ANeuralNetworks_getDeviceCount(&numDevices), ANEURALNETWORKS_NO_ERROR);

    std::set<std::string> deviceNames;
    for (uint32_t i = 0; i < numDevices; i++) {
        ANeuralNetworksDevice* device = nullptr;
        EXPECT_EQ(ANeuralNetworks_getDevice(i, &device), ANEURALNETWORKS_NO_ERROR);
        const char* buffer = nullptr;
        EXPECT_EQ(ANeuralNetworksDevice_getName(device, &buffer), ANEURALNETWORKS_NO_ERROR);
        std::string name(buffer);
        EXPECT_EQ(deviceNames.count(name), (uint32_t)0);
        deviceNames.insert(name);
    }
}

TEST(ValidationTestIntrospection, DeviceGetVersion) {
    deviceStringCheck(ANeuralNetworksDevice_getVersion);
}

TEST(ValidationTestIntrospection, DeviceGetFeatureLevel) {
    uint32_t numDevices = 0;
    EXPECT_EQ(ANeuralNetworks_getDeviceCount(&numDevices), ANEURALNETWORKS_NO_ERROR);

    int64_t featureLevel;
    for (uint32_t i = 0; i < numDevices; i++) {
        SCOPED_TRACE(i);
        ANeuralNetworksDevice* device;
        EXPECT_EQ(ANeuralNetworks_getDevice(i, &device), ANEURALNETWORKS_NO_ERROR);
        EXPECT_EQ(ANeuralNetworksDevice_getFeatureLevel(device, &featureLevel),
                  ANEURALNETWORKS_NO_ERROR);
        EXPECT_EQ(ANeuralNetworksDevice_getFeatureLevel(device, nullptr),
                  ANEURALNETWORKS_UNEXPECTED_NULL);
    }
    EXPECT_EQ(ANeuralNetworksDevice_getFeatureLevel(nullptr, &featureLevel),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksDevice_getFeatureLevel(nullptr, nullptr),
              ANEURALNETWORKS_UNEXPECTED_NULL);
}

TEST(ValidationTestIntrospection, DeviceGetType) {
    uint32_t numDevices = 0;
    EXPECT_EQ(ANeuralNetworks_getDeviceCount(&numDevices), ANEURALNETWORKS_NO_ERROR);

    int32_t validTypes[] = {ANEURALNETWORKS_DEVICE_UNKNOWN, ANEURALNETWORKS_DEVICE_OTHER,
                            ANEURALNETWORKS_DEVICE_CPU, ANEURALNETWORKS_DEVICE_GPU,
                            ANEURALNETWORKS_DEVICE_ACCELERATOR};
    int32_t deviceType;
    for (uint32_t i = 0; i < numDevices; i++) {
        SCOPED_TRACE(i);
        // Initialize the deviceType to be an invalid type.
        deviceType = -1;
        ANeuralNetworksDevice* device;
        EXPECT_EQ(ANeuralNetworks_getDevice(i, &device), ANEURALNETWORKS_NO_ERROR);
        EXPECT_EQ(ANeuralNetworksDevice_getType(device, &deviceType), ANEURALNETWORKS_NO_ERROR);
        EXPECT_TRUE(std::find(std::begin(validTypes), std::end(validTypes), deviceType) !=
                    std::end(validTypes));
        EXPECT_EQ(ANeuralNetworksDevice_getType(device, nullptr), ANEURALNETWORKS_UNEXPECTED_NULL);
    }
    EXPECT_EQ(ANeuralNetworksDevice_getType(nullptr, &deviceType), ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksDevice_getType(nullptr, nullptr), ANEURALNETWORKS_UNEXPECTED_NULL);
}

TEST(ValidationTestIntrospection, DeviceWait) {
    uint32_t numDevices = 0;
    EXPECT_EQ(ANeuralNetworks_getDeviceCount(&numDevices), ANEURALNETWORKS_NO_ERROR);

    for (uint32_t i = 0; i < numDevices; i++) {
        SCOPED_TRACE(i);
        ANeuralNetworksDevice* device;
        EXPECT_EQ(ANeuralNetworks_getDevice(i, &device), ANEURALNETWORKS_NO_ERROR);
        EXPECT_EQ(ANeuralNetworksDevice_wait(device), ANEURALNETWORKS_NO_ERROR);
    }
    EXPECT_EQ(ANeuralNetworksDevice_wait(nullptr), ANEURALNETWORKS_UNEXPECTED_NULL);
}

class ValidationTestCompilationForDevices_1 : public ValidationTestModel {
   protected:
    virtual void SetUp() override {
        ValidationTestModel::SetUp();
        createModel();

        uint32_t numDevices = 0;
        EXPECT_EQ(ANeuralNetworks_getDeviceCount(&numDevices), ANEURALNETWORKS_NO_ERROR);

        if (numDevices > 0) {
            EXPECT_EQ(ANeuralNetworks_getDevice(0, &mDevice), ANEURALNETWORKS_NO_ERROR);
            bool supported = false;
            ASSERT_EQ(mNumOperations, static_cast<uint32_t>(1));
            EXPECT_EQ(ANeuralNetworksModel_getSupportedOperationsForDevices(mModel, &mDevice, 1,
                                                                            &supported),
                      ANEURALNETWORKS_NO_ERROR);
            if (supported) {
                ASSERT_EQ(ANeuralNetworksCompilation_createForDevices(mModel, &mDevice, 1,
                                                                      &mCompilation),
                          ANEURALNETWORKS_NO_ERROR);
            }
        }
    }

    virtual void TearDown() {
        ANeuralNetworksCompilation_free(mCompilation);
        ValidationTestModel::TearDown();
    }

    ANeuralNetworksDevice* mDevice = nullptr;
    ANeuralNetworksCompilation* mCompilation = nullptr;
};

// Also see TEST_F(ValidationTestCompilation, SetPreference)
TEST_F(ValidationTestCompilationForDevices_1, SetPreference) {
    EXPECT_EQ(ANeuralNetworksCompilation_setPreference(nullptr, ANEURALNETWORKS_PREFER_LOW_POWER),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    if (!mCompilation) {
        return;
    }
    EXPECT_EQ(ANeuralNetworksCompilation_setPreference(mCompilation, 40), ANEURALNETWORKS_BAD_DATA);
}

// Also see TEST_F(ValidationTestCompilation, SetCaching)
TEST_F(ValidationTestCompilationForDevices_1, SetCaching) {
    std::vector<uint8_t> token(ANEURALNETWORKS_BYTE_SIZE_OF_CACHE_TOKEN, 0);
    EXPECT_EQ(ANeuralNetworksCompilation_setCaching(nullptr, NN_TMP_DIR, token.data()),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    if (!mCompilation) {
        return;
    }
    EXPECT_EQ(ANeuralNetworksCompilation_setCaching(mCompilation, nullptr, token.data()),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksCompilation_setCaching(mCompilation, NN_TMP_DIR, nullptr),
              ANEURALNETWORKS_UNEXPECTED_NULL);
}

// Also see TEST_F(ValidationTestCompilation, CreateExecution)
TEST_F(ValidationTestCompilationForDevices_1, CreateExecution) {
    ANeuralNetworksExecution* execution = nullptr;
    EXPECT_EQ(ANeuralNetworksExecution_create(nullptr, &execution),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    if (!mCompilation) {
        return;
    }
    EXPECT_EQ(ANeuralNetworksExecution_create(mCompilation, nullptr),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksExecution_create(mCompilation, &execution), ANEURALNETWORKS_BAD_STATE);
}

// Also see TEST_F(ValidationTestCompilation, Finish)
TEST_F(ValidationTestCompilationForDevices_1, Finish) {
    EXPECT_EQ(ANeuralNetworksCompilation_finish(nullptr), ANEURALNETWORKS_UNEXPECTED_NULL);
    if (!mCompilation) {
        return;
    }
    EXPECT_EQ(ANeuralNetworksCompilation_finish(mCompilation), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksCompilation_setPreference(mCompilation,
                                                       ANEURALNETWORKS_PREFER_FAST_SINGLE_ANSWER),
              ANEURALNETWORKS_BAD_STATE);
    EXPECT_EQ(
            ANeuralNetworksCompilation_setPriority(mCompilation, ANEURALNETWORKS_PRIORITY_DEFAULT),
            ANEURALNETWORKS_BAD_STATE);
    EXPECT_EQ(ANeuralNetworksCompilation_setTimeout(mCompilation, kShortWaitInNanoseconds),
              ANEURALNETWORKS_BAD_STATE);
    std::vector<uint8_t> token(ANEURALNETWORKS_BYTE_SIZE_OF_CACHE_TOKEN, 0);
    EXPECT_EQ(ANeuralNetworksCompilation_setCaching(mCompilation, NN_TMP_DIR, token.data()),
              ANEURALNETWORKS_BAD_STATE);
    EXPECT_EQ(ANeuralNetworksCompilation_finish(mCompilation), ANEURALNETWORKS_BAD_STATE);
}

// Also see TEST_F(ValidationTestCompilation, SetTimeout)
// Also see TEST_F(ValidationTestCompilationForDevices_2, SetTimeout)
TEST_F(ValidationTestCompilationForDevices_1, SetTimeout) {
    if (!mCompilation) {
        return;
    }

    EXPECT_EQ(ANeuralNetworksCompilation_setTimeout(mCompilation, kShortWaitInNanoseconds),
              ANEURALNETWORKS_NO_ERROR);

    // Attempt to finish
    const int n = ANeuralNetworksCompilation_finish(mCompilation);
    EXPECT_TRUE(n == ANEURALNETWORKS_NO_ERROR || n == ANEURALNETWORKS_MISSED_DEADLINE_TRANSIENT ||
                n == ANEURALNETWORKS_MISSED_DEADLINE_PERSISTENT);
}

TEST_F(ValidationTestCompilationForDevices_1, SetTimeoutMaximum) {
    if (!mCompilation) {
        return;
    }

    constexpr uint64_t duration = std::numeric_limits<uint64_t>::max();
    EXPECT_EQ(ANeuralNetworksCompilation_setTimeout(mCompilation, duration),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksCompilation_finish(mCompilation), ANEURALNETWORKS_NO_ERROR);
}

class ValidationTestCompilationForDevices_2 : public ValidationTestModel {
   protected:
    virtual void SetUp() override {
        ValidationTestModel::SetUp();
        createModel();

        uint32_t numDevices = 0;
        EXPECT_EQ(ANeuralNetworks_getDeviceCount(&numDevices), ANEURALNETWORKS_NO_ERROR);

        if (numDevices > 1) {
            EXPECT_EQ(ANeuralNetworks_getDevice(0, &mDevices[0]), ANEURALNETWORKS_NO_ERROR);
            EXPECT_EQ(ANeuralNetworks_getDevice(1, &mDevices[1]), ANEURALNETWORKS_NO_ERROR);
            bool supported = false;
            ASSERT_EQ(mNumOperations, static_cast<uint32_t>(1));
            EXPECT_EQ(ANeuralNetworksModel_getSupportedOperationsForDevices(mModel, mDevices, 2,
                                                                            &supported),
                      ANEURALNETWORKS_NO_ERROR);
            if (supported) {
                ASSERT_EQ(ANeuralNetworksCompilation_createForDevices(mModel, mDevices, 2,
                                                                      &mCompilation),
                          ANEURALNETWORKS_NO_ERROR);
            }
        }
    }

    virtual void TearDown() {
        ANeuralNetworksCompilation_free(mCompilation);
        ValidationTestModel::TearDown();
    }

    ANeuralNetworksDevice* mDevices[2] = {nullptr, nullptr};
    ANeuralNetworksCompilation* mCompilation = nullptr;
};

// Also see TEST_F(ValidationTestCompilation, SetTimeout)
// Also see TEST_F(ValidationTestCompilationForDevices_1, SetTimeout)
TEST_F(ValidationTestCompilationForDevices_2, SetTimeout) {
    EXPECT_EQ(ANeuralNetworksCompilation_setTimeout(nullptr, kShortWaitInNanoseconds),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    if (!mCompilation) {
        return;
    }
    // Timeouts can only be set on Compilations created from CompilationForDevices with one device
    // specified.
    EXPECT_EQ(ANeuralNetworksCompilation_setTimeout(mCompilation, kShortWaitInNanoseconds),
              ANEURALNETWORKS_BAD_DATA);
}

// Also see TEST_F(ValidationTestCompilation, ExecutionSetTimeout)
// Also see TEST_F(ValidationTestCompilationForDevices_1, ExecutionSetTimeout)
TEST_F(ValidationTestCompilationForDevices_2, ExecutionSetTimeout) {
    EXPECT_EQ(ANeuralNetworksExecution_setTimeout(nullptr, kShortWaitInNanoseconds),
              ANEURALNETWORKS_UNEXPECTED_NULL);

    if (!mCompilation) {
        return;
    }
    ASSERT_EQ(ANeuralNetworksCompilation_finish(mCompilation), ANEURALNETWORKS_NO_ERROR);
    ANeuralNetworksExecution* execution;
    ASSERT_EQ(ANeuralNetworksExecution_create(mCompilation, &execution), ANEURALNETWORKS_NO_ERROR);
    // Timeouts can only be set on Compilations created from CompilationForDevices with one device
    // specified.
    EXPECT_EQ(ANeuralNetworksExecution_setTimeout(execution, kShortWaitInNanoseconds),
              ANEURALNETWORKS_BAD_DATA);
    ANeuralNetworksExecution_free(execution);
}

// Also see TEST_F(ValidationTestCompilation, ExecutionTiming)
// Also see TEST_F(ValidationTestCompilationForDevices_1, ExecutionTiming)
TEST_F(ValidationTestCompilationForDevices_2, ExecutionTiming) {
    if (!mCompilation) {
        return;
    }
    ASSERT_EQ(ANeuralNetworksCompilation_finish(mCompilation), ANEURALNETWORKS_NO_ERROR);
    ANeuralNetworksExecution* execution;
    ASSERT_EQ(ANeuralNetworksExecution_create(mCompilation, &execution), ANEURALNETWORKS_NO_ERROR);
    // Cannot setMeasureTiming() if there are two or more devices.
    EXPECT_EQ(ANeuralNetworksExecution_setMeasureTiming(execution, false),
              ANEURALNETWORKS_BAD_DATA);
    EXPECT_EQ(ANeuralNetworksExecution_setMeasureTiming(execution, true), ANEURALNETWORKS_BAD_DATA);

    // close memory
    ANeuralNetworksExecution_free(execution);
}

class ValidationTestInvalidCompilation : public ValidationTestModel {
   protected:
    virtual void SetUp() override {
        ValidationTestModel::SetUp();

        // Create a model with an OEM operation
        uint32_t dimensions[]{1};
        ANeuralNetworksOperandType OEMTensorType{.type = ANEURALNETWORKS_TENSOR_OEM_BYTE,
                                                 .dimensionCount = 1,
                                                 .dimensions = dimensions};
        EXPECT_EQ(ANeuralNetworksModel_addOperand(mModel, &OEMTensorType),
                  ANEURALNETWORKS_NO_ERROR);
        EXPECT_EQ(ANeuralNetworksModel_addOperand(mModel, &OEMTensorType),
                  ANEURALNETWORKS_NO_ERROR);
        ASSERT_EQ(addOperation(ANEURALNETWORKS_OEM_OPERATION, {0}, {1}), ANEURALNETWORKS_NO_ERROR);
        ASSERT_EQ(identifyInputsAndOutputs({0}, {1}), ANEURALNETWORKS_NO_ERROR);
        ASSERT_EQ(modelFinish(), ANEURALNETWORKS_NO_ERROR);

        // Find a device that cannot handle OEM operation and create compilation on that
        uint32_t numDevices = 0;
        EXPECT_EQ(ANeuralNetworks_getDeviceCount(&numDevices), ANEURALNETWORKS_NO_ERROR);
        for (uint32_t i = 0; i < numDevices; i++) {
            ANeuralNetworksDevice* device;
            EXPECT_EQ(ANeuralNetworks_getDevice(i, &device), ANEURALNETWORKS_NO_ERROR);
            bool supported = false;
            EXPECT_EQ(ANeuralNetworksModel_getSupportedOperationsForDevices(mModel, &device, 1,
                                                                            &supported),
                      ANEURALNETWORKS_NO_ERROR);
            if (!supported) {
                ASSERT_EQ(ANeuralNetworksCompilation_createForDevices(mModel, &device, 1,
                                                                      &mInvalidCompilation),
                          ANEURALNETWORKS_NO_ERROR);
                break;
            }
        }
        if (mInvalidCompilation) {
            ASSERT_EQ(ANeuralNetworksCompilation_finish(mInvalidCompilation),
                      ANEURALNETWORKS_BAD_DATA);
        }
    }

    virtual void TearDown() {
        ANeuralNetworksCompilation_free(mInvalidCompilation);
        ValidationTestModel::TearDown();
    }

    ANeuralNetworksCompilation* mInvalidCompilation = nullptr;
};

TEST_F(ValidationTestInvalidCompilation, GetPreferredMemoryAlignmentAndPadding) {
    if (__builtin_available(android __NNAPI_FL5_MIN_ANDROID_API__, *)) {
        if (!mInvalidCompilation) {
            return;
        }
        uint32_t result;
        EXPECT_EQ(ANeuralNetworksCompilation_getPreferredMemoryAlignmentForInput(
                          mInvalidCompilation, 0, &result),
                  ANEURALNETWORKS_BAD_STATE);
        EXPECT_EQ(ANeuralNetworksCompilation_getPreferredMemoryPaddingForInput(mInvalidCompilation,
                                                                               0, &result),
                  ANEURALNETWORKS_BAD_STATE);
        EXPECT_EQ(ANeuralNetworksCompilation_getPreferredMemoryAlignmentForOutput(
                          mInvalidCompilation, 0, &result),
                  ANEURALNETWORKS_BAD_STATE);
        EXPECT_EQ(ANeuralNetworksCompilation_getPreferredMemoryPaddingForOutput(mInvalidCompilation,
                                                                                0, &result),
                  ANEURALNETWORKS_BAD_STATE);
    } else {
        GTEST_SKIP();
    }
}

TEST_F(ValidationTestInvalidCompilation, CreateExecution) {
    if (!mInvalidCompilation) {
        return;
    }
    ANeuralNetworksExecution* execution = nullptr;
    EXPECT_EQ(ANeuralNetworksExecution_create(mInvalidCompilation, &execution),
              ANEURALNETWORKS_BAD_STATE);
    ANeuralNetworksExecution_free(execution);
}

TEST_F(ValidationTestInvalidCompilation, MemoryDescAddRole) {
    if (!mInvalidCompilation) {
        return;
    }
    ANeuralNetworksMemoryDesc* desc = nullptr;
    ASSERT_EQ(ANeuralNetworksMemoryDesc_create(&desc), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksMemoryDesc_addInputRole(desc, mInvalidCompilation, 0, 1.0f),
              ANEURALNETWORKS_BAD_DATA);
    EXPECT_EQ(ANeuralNetworksMemoryDesc_addOutputRole(desc, mInvalidCompilation, 0, 1.0f),
              ANEURALNETWORKS_BAD_DATA);
    ANeuralNetworksMemoryDesc_free(desc);
}

// Also see TEST_F(ValidationTestCompilation, ExecutionTiming)
// Also see TEST_F(ValidationTestCompilationForDevices_2, ExecutionTiming)
// Also see TEST_F(ValidationTestCompilation, ExecutionUsability)
TEST_F(ValidationTestCompilationForDevices_1, ExecutionTiming) {
    if (!mCompilation) {
        return;
    }
    ASSERT_EQ(ANeuralNetworksCompilation_finish(mCompilation), ANEURALNETWORKS_NO_ERROR);

    enum class ExecutionType : uint32_t { ASYNC, SYNC, BURST, FENCED };
    for (auto executionType :
         {ExecutionType::ASYNC, ExecutionType::SYNC, ExecutionType::BURST, ExecutionType::FENCED}) {
        SCOPED_TRACE(static_cast<uint32_t>(executionType));

        ANeuralNetworksExecution* execution;
        ASSERT_EQ(ANeuralNetworksExecution_create(mCompilation, &execution),
                  ANEURALNETWORKS_NO_ERROR);

        EXPECT_EQ(ANeuralNetworksExecution_setMeasureTiming(nullptr, false),
                  ANEURALNETWORKS_UNEXPECTED_NULL);
        EXPECT_EQ(ANeuralNetworksExecution_setMeasureTiming(nullptr, true),
                  ANEURALNETWORKS_UNEXPECTED_NULL);
        EXPECT_EQ(ANeuralNetworksExecution_setMeasureTiming(execution, false),
                  ANEURALNETWORKS_NO_ERROR);
        EXPECT_EQ(ANeuralNetworksExecution_setMeasureTiming(execution, true),
                  ANEURALNETWORKS_NO_ERROR);

        float in0[] = {0.0f, 0.0f}, in1[] = {1.0f, 1.0f}, out0[2];
        int in2 = 0;
        ASSERT_EQ(ANeuralNetworksExecution_setInput(execution, 0, nullptr, &in0, sizeof(in0)),
                  ANEURALNETWORKS_NO_ERROR);
        ASSERT_EQ(ANeuralNetworksExecution_setInput(execution, 1, nullptr, &in1, sizeof(in1)),
                  ANEURALNETWORKS_NO_ERROR);
        ASSERT_EQ(ANeuralNetworksExecution_setInput(execution, 2, nullptr, &in2, sizeof(in2)),
                  ANEURALNETWORKS_NO_ERROR);
        ASSERT_EQ(ANeuralNetworksExecution_setOutput(execution, 0, nullptr, &out0, sizeof(out0)),
                  ANEURALNETWORKS_NO_ERROR);

        // Cannot getDuration until the execution has finished.
        uint64_t duration;
        EXPECT_EQ(ANeuralNetworksExecution_getDuration(
                          execution, ANEURALNETWORKS_DURATION_ON_HARDWARE, &duration),
                  ANEURALNETWORKS_BAD_STATE);
        EXPECT_EQ(ANeuralNetworksExecution_getDuration(
                          execution, ANEURALNETWORKS_DURATION_IN_DRIVER, &duration),
                  ANEURALNETWORKS_BAD_STATE);

        auto testSetTimeoutTooLate = [execution] {
            // Cannot setTimeout if the execution has started.
            EXPECT_EQ(ANeuralNetworksExecution_setTimeout(execution, kShortWaitInNanoseconds),
                      ANEURALNETWORKS_BAD_STATE);
        };

        auto testMeasureTooLate = [execution] {
            // Cannot setMeasureTiming if the execution has started.
            EXPECT_EQ(ANeuralNetworksExecution_setMeasureTiming(execution, false),
                      ANEURALNETWORKS_BAD_STATE);
            EXPECT_EQ(ANeuralNetworksExecution_setMeasureTiming(execution, true),
                      ANEURALNETWORKS_BAD_STATE);
        };

        // Compute.
        switch (executionType) {
            case ExecutionType::ASYNC: {
                ANeuralNetworksEvent* event;
                ASSERT_EQ(ANeuralNetworksExecution_startCompute(execution, &event),
                          ANEURALNETWORKS_NO_ERROR);
                testMeasureTooLate();
                ASSERT_EQ(ANeuralNetworksEvent_wait(event), ANEURALNETWORKS_NO_ERROR);
                testSetTimeoutTooLate();
                testMeasureTooLate();
                ANeuralNetworksEvent_free(event);
                break;
            }
            case ExecutionType::SYNC: {
                ASSERT_EQ(ANeuralNetworksExecution_compute(execution), ANEURALNETWORKS_NO_ERROR);
                testSetTimeoutTooLate();
                testMeasureTooLate();
                break;
            }
            case ExecutionType::BURST: {
                ANeuralNetworksBurst* burst;
                ASSERT_EQ(ANeuralNetworksBurst_create(mCompilation, &burst),
                          ANEURALNETWORKS_NO_ERROR);
                ASSERT_EQ(ANeuralNetworksExecution_burstCompute(execution, burst),
                          ANEURALNETWORKS_NO_ERROR);
                testSetTimeoutTooLate();
                testMeasureTooLate();
                ANeuralNetworksBurst_free(burst);
                break;
            }
            case ExecutionType::FENCED: {
                ANeuralNetworksEvent* event = nullptr;
                ASSERT_EQ(ANeuralNetworksExecution_startComputeWithDependencies(execution, nullptr,
                                                                                0, 0, &event),
                          ANEURALNETWORKS_NO_ERROR);
                testMeasureTooLate();
                ASSERT_EQ(ANeuralNetworksEvent_wait(event), ANEURALNETWORKS_NO_ERROR);
                testSetTimeoutTooLate();
                testMeasureTooLate();
                ANeuralNetworksEvent_free(event);
                break;
            }
            default:
                FAIL() << "Unreachable";
        }

        auto testDuration = [](ANeuralNetworksExecution* e, int32_t durationCode,
                               bool nullDuration) {
            SCOPED_TRACE(e);
            SCOPED_TRACE(durationCode);
            SCOPED_TRACE(nullDuration);

            // Strictly speaking, a duration COULD have this value, but it is
            // exceedingly unlikely. We'll use it as an initial value that we expect
            // to be modified by getDuration().
            const uint64_t kBogusDuration = UINT64_MAX - 1;

            uint64_t duration = kBogusDuration;
            uint64_t* durationPtr = nullDuration ? nullptr : &duration;

            int expectedResultCode = ANEURALNETWORKS_NO_ERROR;
            if (e == nullptr | durationPtr == nullptr) {
                expectedResultCode = ANEURALNETWORKS_UNEXPECTED_NULL;
            } else if (durationCode < 0 ||
                       durationCode > ANEURALNETWORKS_FENCED_DURATION_IN_DRIVER) {
                expectedResultCode = ANEURALNETWORKS_BAD_DATA;
            }

            EXPECT_EQ(ANeuralNetworksExecution_getDuration(e, durationCode, durationPtr),
                      expectedResultCode);
            if (expectedResultCode == ANEURALNETWORKS_NO_ERROR) {
                EXPECT_NE(duration, kBogusDuration);
            }
        };

        std::vector<ANeuralNetworksExecution*> executions = {nullptr, execution};
        std::vector<int32_t> durationCodes = {-1,
                                              ANEURALNETWORKS_DURATION_ON_HARDWARE,
                                              ANEURALNETWORKS_DURATION_IN_DRIVER,
                                              ANEURALNETWORKS_FENCED_DURATION_ON_HARDWARE,
                                              ANEURALNETWORKS_FENCED_DURATION_IN_DRIVER,
                                              ANEURALNETWORKS_FENCED_DURATION_IN_DRIVER + 1};
        std::vector<bool> nullDurations = {false, true};
        for (auto e : executions) {
            for (auto d : durationCodes) {
                for (auto n : nullDurations) {
                    testDuration(e, d, n);
                }
            }
        }

        // close memory
        ANeuralNetworksExecution_free(execution);
    }
}

enum class TimeoutDurationType { SHORT, MAXIMUM };
uint64_t createTimeoutDuration(TimeoutDurationType type) {
    switch (type) {
        case TimeoutDurationType::SHORT:
            return kShortWaitInNanoseconds;
        case TimeoutDurationType::MAXIMUM:
            return std::numeric_limits<uint64_t>::max();
    }
    LOG(FATAL) << "Invalid TimeoutDurationType: " << static_cast<int>(type);
    return 0;
}

void runExecutionSetTimeoutTest(ANeuralNetworksCompilation* compilation,
                                TimeoutDurationType timeoutDurationType) {
    if (!compilation) {
        return;
    }
    ASSERT_EQ(ANeuralNetworksCompilation_finish(compilation), ANEURALNETWORKS_NO_ERROR);

    enum class ExecutionType : uint32_t { ASYNC, SYNC, BURST, FENCED };
    for (auto executionType :
         {ExecutionType::ASYNC, ExecutionType::SYNC, ExecutionType::BURST, ExecutionType::FENCED}) {
        SCOPED_TRACE(static_cast<uint32_t>(executionType));

        ANeuralNetworksExecution* execution;
        ASSERT_EQ(ANeuralNetworksExecution_create(compilation, &execution),
                  ANEURALNETWORKS_NO_ERROR);
        const auto scoped = android::base::make_scope_guard(
                [execution] { ANeuralNetworksExecution_free(execution); });

        float in0[] = {0.0f, 0.0f}, in1[] = {1.0f, 1.0f}, out0[2];
        int in2 = 0;
        ASSERT_EQ(ANeuralNetworksExecution_setInput(execution, 0, nullptr, &in0, sizeof(in0)),
                  ANEURALNETWORKS_NO_ERROR);
        ASSERT_EQ(ANeuralNetworksExecution_setInput(execution, 1, nullptr, &in1, sizeof(in1)),
                  ANEURALNETWORKS_NO_ERROR);
        ASSERT_EQ(ANeuralNetworksExecution_setInput(execution, 2, nullptr, &in2, sizeof(in2)),
                  ANEURALNETWORKS_NO_ERROR);
        ASSERT_EQ(ANeuralNetworksExecution_setOutput(execution, 0, nullptr, &out0, sizeof(out0)),
                  ANEURALNETWORKS_NO_ERROR);

        const uint64_t timeoutDuration = createTimeoutDuration(timeoutDurationType);
        EXPECT_EQ(ANeuralNetworksExecution_setTimeout(execution, timeoutDuration),
                  ANEURALNETWORKS_NO_ERROR);

        const auto checkResult = [timeoutDurationType](int n) {
            switch (timeoutDurationType) {
                case TimeoutDurationType::SHORT:
                    EXPECT_TRUE(n == ANEURALNETWORKS_NO_ERROR ||
                                n == ANEURALNETWORKS_MISSED_DEADLINE_TRANSIENT ||
                                n == ANEURALNETWORKS_MISSED_DEADLINE_PERSISTENT);
                    return;
                case TimeoutDurationType::MAXIMUM:
                    EXPECT_EQ(n, ANEURALNETWORKS_NO_ERROR);
                    return;
            }
            LOG(FATAL) << "Invalid TimeoutDurationType: " << static_cast<int>(timeoutDurationType);
        };

        // Compute.
        switch (executionType) {
            case ExecutionType::ASYNC: {
                ANeuralNetworksEvent* event = nullptr;
                EXPECT_EQ(ANeuralNetworksExecution_startCompute(execution, &event),
                          ANEURALNETWORKS_NO_ERROR);
                checkResult(ANeuralNetworksEvent_wait(event));
                ANeuralNetworksEvent_free(event);
                break;
            }
            case ExecutionType::SYNC: {
                checkResult(ANeuralNetworksExecution_compute(execution));
                break;
            }
            case ExecutionType::BURST: {
                ANeuralNetworksBurst* burst;
                ASSERT_EQ(ANeuralNetworksBurst_create(compilation, &burst),
                          ANEURALNETWORKS_NO_ERROR);
                checkResult(ANeuralNetworksExecution_burstCompute(execution, burst));
                ANeuralNetworksBurst_free(burst);
                break;
            }
            case ExecutionType::FENCED: {
                ANeuralNetworksEvent* event = nullptr;
                EXPECT_EQ(ANeuralNetworksExecution_startComputeWithDependencies(execution, nullptr,
                                                                                0, 0, &event),
                          ANEURALNETWORKS_NO_ERROR);
                checkResult(ANeuralNetworksEvent_wait(event));
                ANeuralNetworksEvent_free(event);
                break;
            }
            default:
                FAIL() << "Unreachable";
        }
    }
}

// Also see TEST_F(ValidationTestCompilation, ExecutionSetTimeout)
// Also see TEST_F(ValidationTestCompilationForDevices_2, ExecutionSetTimeout)
TEST_F(ValidationTestCompilationForDevices_1, ExecutionSetTimeout) {
    runExecutionSetTimeoutTest(mCompilation, TimeoutDurationType::SHORT);
}

TEST_F(ValidationTestCompilationForDevices_1, ExecutionSetTimeoutMaximum) {
    runExecutionSetTimeoutTest(mCompilation, TimeoutDurationType::MAXIMUM);
}

TEST_F(ValidationTest, CreateMemoryDesc) {
    EXPECT_EQ(ANeuralNetworksMemoryDesc_create(nullptr), ANEURALNETWORKS_UNEXPECTED_NULL);
}

TEST_F(ValidationTestMemoryDesc, AddInputRole) {
    EXPECT_EQ(ANeuralNetworksMemoryDesc_addInputRole(nullptr, mCompilation, 0, 1.0f),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksMemoryDesc_addInputRole(mDesc, nullptr, 0, 1.0f),
              ANEURALNETWORKS_UNEXPECTED_NULL);

    // Unfinished compilation.
    EXPECT_EQ(ANeuralNetworksMemoryDesc_addInputRole(mDesc, mCompilation, 0, 1.0f),
              ANEURALNETWORKS_BAD_DATA);

    ASSERT_EQ(ANeuralNetworksCompilation_finish(mCompilation), ANEURALNETWORKS_NO_ERROR);

    // Index out of range.
    EXPECT_EQ(ANeuralNetworksMemoryDesc_addInputRole(mDesc, mCompilation, 999, 1.0f),
              ANEURALNETWORKS_BAD_DATA);

    // Invalid frequency.
    EXPECT_EQ(ANeuralNetworksMemoryDesc_addInputRole(mDesc, mCompilation, 0, 10.0f),
              ANEURALNETWORKS_BAD_DATA);
    EXPECT_EQ(ANeuralNetworksMemoryDesc_addInputRole(mDesc, mCompilation, 0, 0.0f),
              ANEURALNETWORKS_BAD_DATA);
    EXPECT_EQ(ANeuralNetworksMemoryDesc_addInputRole(mDesc, mCompilation, 0, -1.0f),
              ANEURALNETWORKS_BAD_DATA);

    // Specify the same operand twice.
    EXPECT_EQ(ANeuralNetworksMemoryDesc_addInputRole(mDesc, mCompilation, 0, 1.0f),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksMemoryDesc_addInputRole(mDesc, mCompilation, 0, 1.0f),
              ANEURALNETWORKS_BAD_DATA);

    // Attempting to modify a finished descriptor.
    EXPECT_EQ(ANeuralNetworksMemoryDesc_finish(mDesc), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksMemoryDesc_addInputRole(mDesc, mCompilation, 0, 1.0f),
              ANEURALNETWORKS_BAD_STATE);
}

TEST_F(ValidationTestMemoryDesc, AddOutputRole) {
    EXPECT_EQ(ANeuralNetworksMemoryDesc_addOutputRole(nullptr, mCompilation, 0, 1.0f),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksMemoryDesc_addOutputRole(mDesc, nullptr, 0, 1.0f),
              ANEURALNETWORKS_UNEXPECTED_NULL);

    // Unfinished compilation.
    EXPECT_EQ(ANeuralNetworksMemoryDesc_addOutputRole(mDesc, mCompilation, 0, 1.0f),
              ANEURALNETWORKS_BAD_DATA);

    ASSERT_EQ(ANeuralNetworksCompilation_finish(mCompilation), ANEURALNETWORKS_NO_ERROR);

    // Index out of range.
    EXPECT_EQ(ANeuralNetworksMemoryDesc_addOutputRole(mDesc, mCompilation, 999, 1.0f),
              ANEURALNETWORKS_BAD_DATA);

    // Invalid frequency.
    EXPECT_EQ(ANeuralNetworksMemoryDesc_addOutputRole(mDesc, mCompilation, 0, 10.0f),
              ANEURALNETWORKS_BAD_DATA);
    EXPECT_EQ(ANeuralNetworksMemoryDesc_addOutputRole(mDesc, mCompilation, 0, 0.0f),
              ANEURALNETWORKS_BAD_DATA);
    EXPECT_EQ(ANeuralNetworksMemoryDesc_addOutputRole(mDesc, mCompilation, 0, -1.0f),
              ANEURALNETWORKS_BAD_DATA);

    // Specify the same operand twice.
    EXPECT_EQ(ANeuralNetworksMemoryDesc_addOutputRole(mDesc, mCompilation, 0, 1.0f),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksMemoryDesc_addOutputRole(mDesc, mCompilation, 0, 1.0f),
              ANEURALNETWORKS_BAD_DATA);

    // Attempting to modify a finished descriptor.
    EXPECT_EQ(ANeuralNetworksMemoryDesc_finish(mDesc), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksMemoryDesc_addOutputRole(mDesc, mCompilation, 0, 1.0f),
              ANEURALNETWORKS_BAD_STATE);
}

// Creates and compiles a single-operation ADD model with the given operand type.
// The caller is responsible to free the returned model and compilation.
static std::pair<ANeuralNetworksModel*, ANeuralNetworksCompilation*>
createAndCompileAddModelWithType(const ANeuralNetworksOperandType& type) {
    // OperandType for activation scalar.
    const ANeuralNetworksOperandType actType = {
            .type = ANEURALNETWORKS_INT32, .dimensionCount = 0, .dimensions = nullptr};

    ANeuralNetworksModel* model;
    EXPECT_EQ(ANeuralNetworksModel_create(&model), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksModel_addOperand(model, &type), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksModel_addOperand(model, &type), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksModel_addOperand(model, &actType), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksModel_addOperand(model, &type), ANEURALNETWORKS_NO_ERROR);

    const uint32_t inList[] = {0, 1, 2};
    const uint32_t outList[] = {3};
    EXPECT_EQ(ANeuralNetworksModel_addOperation(model, ANEURALNETWORKS_ADD, 3, inList, 1, outList),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksModel_identifyInputsAndOutputs(model, 3, inList, 1, outList),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksModel_finish(model), ANEURALNETWORKS_NO_ERROR);

    ANeuralNetworksCompilation* compilation;
    EXPECT_EQ(ANeuralNetworksCompilation_create(model, &compilation), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksCompilation_finish(compilation), ANEURALNETWORKS_NO_ERROR);
    return {model, compilation};
}

static void testIncompatibleOperands(const ANeuralNetworksCompilation* compilation,
                                     const ANeuralNetworksOperandType& badType) {
    const auto [badModel, badCompilation] = createAndCompileAddModelWithType(badType);
    {
        ANeuralNetworksMemoryDesc* desc = nullptr;
        EXPECT_EQ(ANeuralNetworksMemoryDesc_create(&desc), ANEURALNETWORKS_NO_ERROR);
        EXPECT_EQ(ANeuralNetworksMemoryDesc_addInputRole(desc, compilation, 0, 1.0f),
                  ANEURALNETWORKS_NO_ERROR);
        EXPECT_EQ(ANeuralNetworksMemoryDesc_addInputRole(desc, badCompilation, 0, 1.0f),
                  ANEURALNETWORKS_BAD_DATA);
        EXPECT_EQ(ANeuralNetworksMemoryDesc_addOutputRole(desc, badCompilation, 0, 1.0f),
                  ANEURALNETWORKS_BAD_DATA);
        ANeuralNetworksMemoryDesc_free(desc);
    }
    {
        ANeuralNetworksMemoryDesc* desc = nullptr;
        EXPECT_EQ(ANeuralNetworksMemoryDesc_create(&desc), ANEURALNETWORKS_NO_ERROR);
        EXPECT_EQ(ANeuralNetworksMemoryDesc_addOutputRole(desc, compilation, 0, 1.0f),
                  ANEURALNETWORKS_NO_ERROR);
        EXPECT_EQ(ANeuralNetworksMemoryDesc_addInputRole(desc, badCompilation, 0, 1.0f),
                  ANEURALNETWORKS_BAD_DATA);
        EXPECT_EQ(ANeuralNetworksMemoryDesc_addOutputRole(desc, badCompilation, 0, 1.0f),
                  ANEURALNETWORKS_BAD_DATA);
        ANeuralNetworksMemoryDesc_free(desc);
    }
    ANeuralNetworksCompilation_free(badCompilation);
    ANeuralNetworksModel_free(badModel);
}

TEST_F(ValidationTestMemoryDesc, OperandMetadata) {
    const uint32_t dimensions[] = {2};
    const uint32_t rank = std::size(dimensions);
    const ANeuralNetworksOperandType floatBase = {.type = ANEURALNETWORKS_TENSOR_FLOAT32,
                                                  .dimensionCount = rank,
                                                  .dimensions = dimensions,
                                                  .scale = 0.0f,
                                                  .zeroPoint = 0};
    const ANeuralNetworksOperandType quantBase = {.type = ANEURALNETWORKS_TENSOR_QUANT8_ASYMM,
                                                  .dimensionCount = rank,
                                                  .dimensions = dimensions,
                                                  .scale = 1.0f,
                                                  .zeroPoint = 0};
    const auto [floatModel, floatCompilation] = createAndCompileAddModelWithType(floatBase);
    const auto [quantModel, quantCompilation] = createAndCompileAddModelWithType(quantBase);

    // Different data type.
    {
        SCOPED_TRACE("Data type");
        ANeuralNetworksOperandType wrongType = floatBase;
        wrongType.type = ANEURALNETWORKS_TENSOR_FLOAT16;
        testIncompatibleOperands(floatCompilation, wrongType);
    }

    // Different scale.
    {
        SCOPED_TRACE("Scale");
        ANeuralNetworksOperandType wrongScale = quantBase;
        wrongScale.scale = 0.5f;
        testIncompatibleOperands(quantCompilation, wrongScale);
    }

    // Different zero point.
    {
        SCOPED_TRACE("Zero point");
        ANeuralNetworksOperandType wrongZeroPoint = quantBase;
        wrongZeroPoint.zeroPoint = 128;
        testIncompatibleOperands(quantCompilation, wrongZeroPoint);
    }

    // Different rank.
    {
        SCOPED_TRACE("Rank");
        const uint32_t badDimensions[] = {2, 1};
        const uint32_t badRank = std::size(badDimensions);
        ANeuralNetworksOperandType wrongRank = quantBase;
        wrongRank.dimensionCount = badRank;
        wrongRank.dimensions = badDimensions;
        testIncompatibleOperands(quantCompilation, wrongRank);
    }

    // Different dimensions.
    {
        SCOPED_TRACE("Dimensions");
        const uint32_t badDimensions[] = {1};
        ANeuralNetworksOperandType wrongDims = quantBase;
        wrongDims.dimensions = badDimensions;
        testIncompatibleOperands(quantCompilation, wrongDims);
    }

    ANeuralNetworksCompilation_free(floatCompilation);
    ANeuralNetworksCompilation_free(quantCompilation);
    ANeuralNetworksModel_free(floatModel);
    ANeuralNetworksModel_free(quantModel);
}

// Creates and compiles a single-operation CONV_2D model with channel quant data type of the given
// scales. The caller is responsible to free the returned model and compilation.
static std::pair<ANeuralNetworksModel*, ANeuralNetworksCompilation*>
createAndCompileChannelQuantConvModel(const std::vector<float>& scales) {
    const uint32_t numChannels = scales.size();

    // OperandType for input and output.
    const uint32_t inoutDimensions[] = {1, 16, 16, numChannels};
    const ANeuralNetworksOperandType inoutType = {
            .type = ANEURALNETWORKS_TENSOR_QUANT8_ASYMM,
            .dimensionCount = std::size(inoutDimensions),
            .dimensions = inoutDimensions,
            .scale = 1.0f,
            .zeroPoint = 0,
    };

    // OperandType for filter.
    const uint32_t filterDimensions[] = {numChannels, 3, 3, numChannels};
    const ANeuralNetworksOperandType filterType = {
            .type = ANEURALNETWORKS_TENSOR_QUANT8_SYMM_PER_CHANNEL,
            .dimensionCount = std::size(filterDimensions),
            .dimensions = filterDimensions,
            .scale = 0.0f,
            .zeroPoint = 0,
    };

    // OperandType for bias.
    const uint32_t biasDimensions[] = {numChannels};
    const ANeuralNetworksOperandType biasType = {
            .type = ANEURALNETWORKS_TENSOR_INT32,
            .dimensionCount = std::size(biasDimensions),
            .dimensions = biasDimensions,
            .scale = 0.0f,
            .zeroPoint = 0,
    };

    // OperandType for scalars: implicit padding code, strides, activation.
    const ANeuralNetworksOperandType scalarType = {
            .type = ANEURALNETWORKS_INT32, .dimensionCount = 0, .dimensions = nullptr};

    ANeuralNetworksModel* model;
    EXPECT_EQ(ANeuralNetworksModel_create(&model), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksModel_addOperand(model, &inoutType), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksModel_addOperand(model, &filterType), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksModel_addOperand(model, &biasType), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksModel_addOperand(model, &scalarType), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksModel_addOperand(model, &scalarType), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksModel_addOperand(model, &scalarType), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksModel_addOperand(model, &scalarType), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksModel_addOperand(model, &inoutType), ANEURALNETWORKS_NO_ERROR);

    // Set channel quant parameters for the filter tensor.
    const ANeuralNetworksSymmPerChannelQuantParams channelQuant = {
            .channelDim = 0,
            .scaleCount = numChannels,
            .scales = scales.data(),
    };
    EXPECT_EQ(ANeuralNetworksModel_setOperandSymmPerChannelQuantParams(model, 1, &channelQuant),
              ANEURALNETWORKS_NO_ERROR);

    const uint32_t inList[] = {0, 1, 2, 3, 4, 5, 6};
    const uint32_t outList[] = {7};
    EXPECT_EQ(ANeuralNetworksModel_addOperation(model, ANEURALNETWORKS_CONV_2D, std::size(inList),
                                                inList, std::size(outList), outList),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksModel_identifyInputsAndOutputs(model, std::size(inList), inList,
                                                            std::size(outList), outList),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksModel_finish(model), ANEURALNETWORKS_NO_ERROR);

    ANeuralNetworksCompilation* compilation;
    EXPECT_EQ(ANeuralNetworksCompilation_create(model, &compilation), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksCompilation_finish(compilation), ANEURALNETWORKS_NO_ERROR);
    return {model, compilation};
}

TEST_F(ValidationTestMemoryDesc, ExtraParams) {
    // Create two compilations with conflict channel quant scales.
    const auto [model1, compilation1] = createAndCompileChannelQuantConvModel({1.0f, 1.0f});
    const auto [model2, compilation2] = createAndCompileChannelQuantConvModel({0.5f, 0.5f});

    ANeuralNetworksMemoryDesc* desc = nullptr;
    EXPECT_EQ(ANeuralNetworksMemoryDesc_create(&desc), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksMemoryDesc_addInputRole(desc, compilation1, 1, 1.0f),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksMemoryDesc_addInputRole(desc, compilation2, 1, 1.0f),
              ANEURALNETWORKS_BAD_DATA);
    ANeuralNetworksMemoryDesc_free(desc);

    ANeuralNetworksCompilation_free(compilation1);
    ANeuralNetworksCompilation_free(compilation2);
    ANeuralNetworksModel_free(model1);
    ANeuralNetworksModel_free(model2);
}

TEST_F(ValidationTestMemoryDesc, SetDimensions) {
    const uint32_t dimensions[] = {2};
    const uint32_t badDimensions[] = {3};
    const uint32_t rank = std::size(dimensions);
    const uint32_t badRankDimensions[] = {2, 1};
    const uint32_t badRank = std::size(badRankDimensions);

    EXPECT_EQ(ANeuralNetworksMemoryDesc_setDimensions(nullptr, rank, dimensions),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksMemoryDesc_setDimensions(mDesc, rank, nullptr),
              ANEURALNETWORKS_UNEXPECTED_NULL);

    // Incompatible dimensions.
    EXPECT_EQ(ANeuralNetworksMemoryDesc_setDimensions(mDesc, rank, dimensions),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksMemoryDesc_setDimensions(mDesc, rank, badDimensions),
              ANEURALNETWORKS_BAD_DATA);
    EXPECT_EQ(ANeuralNetworksMemoryDesc_setDimensions(mDesc, badRank, badRankDimensions),
              ANEURALNETWORKS_BAD_DATA);

    // Attempting to modify a finished descriptor.
    EXPECT_EQ(ANeuralNetworksCompilation_finish(mCompilation), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksMemoryDesc_addInputRole(mDesc, mCompilation, 0, 1.0f),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksMemoryDesc_finish(mDesc), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksMemoryDesc_setDimensions(mDesc, rank, dimensions),
              ANEURALNETWORKS_BAD_STATE);
}

TEST_F(ValidationTestMemoryDesc, SetScalarDimensionsBeforeAddRole) {
    const uint32_t badDimensions[] = {2};
    const uint32_t badRank = std::size(badDimensions);

    // Set non-zero rank.
    EXPECT_EQ(ANeuralNetworksMemoryDesc_setDimensions(mDesc, badRank, badDimensions),
              ANEURALNETWORKS_NO_ERROR);

    // This should fail because input2 is a scalar.
    EXPECT_EQ(ANeuralNetworksCompilation_finish(mCompilation), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksMemoryDesc_addInputRole(mDesc, mCompilation, 2, 1.0f),
              ANEURALNETWORKS_BAD_DATA);
}

TEST_F(ValidationTestMemoryDesc, SetScalarDimensionsAfterAddRole) {
    const uint32_t badDimensions[] = {2};
    const uint32_t badRank = std::size(badDimensions);

    // Input2 is a scalar.
    EXPECT_EQ(ANeuralNetworksCompilation_finish(mCompilation), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksMemoryDesc_addInputRole(mDesc, mCompilation, 2, 1.0f),
              ANEURALNETWORKS_NO_ERROR);

    // This should fail because the rank is not zero.
    EXPECT_EQ(ANeuralNetworksMemoryDesc_setDimensions(mDesc, 0, nullptr), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksMemoryDesc_setDimensions(mDesc, badRank, badDimensions),
              ANEURALNETWORKS_BAD_DATA);
}

TEST_F(ValidationTestMemoryDesc, Finish) {
    EXPECT_EQ(ANeuralNetworksMemoryDesc_finish(nullptr), ANEURALNETWORKS_UNEXPECTED_NULL);

    // No usage is specified.
    EXPECT_EQ(ANeuralNetworksMemoryDesc_finish(mDesc), ANEURALNETWORKS_BAD_DATA);

    // Finish an already finished descriptor.
    EXPECT_EQ(ANeuralNetworksCompilation_finish(mCompilation), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksMemoryDesc_addInputRole(mDesc, mCompilation, 0, 1.0f),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksMemoryDesc_finish(mDesc), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksMemoryDesc_finish(mDesc), ANEURALNETWORKS_BAD_STATE);
}

TEST_F(ValidationTestMemoryDesc, CreateMemory) {
    ANeuralNetworksMemory* memory = nullptr;
    EXPECT_EQ(ANeuralNetworksMemory_createFromDesc(nullptr, &memory),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksMemory_createFromDesc(mDesc, nullptr),
              ANEURALNETWORKS_UNEXPECTED_NULL);

    // Unfinished descriptor.
    EXPECT_EQ(ANeuralNetworksMemory_createFromDesc(mDesc, &memory), ANEURALNETWORKS_BAD_STATE);

    ANeuralNetworksMemory_free(memory);
}

TEST(ValidationTestMemory, CreateFromFd) {
    const size_t memorySize = 20;
#ifdef __ANDROID__
    int memoryFd = ASharedMemory_create("nnMemory", memorySize);
#else   // __ANDROID__
    TemporaryFile tmpFile;
    int memoryFd = tmpFile.release();
    CHECK_EQ(ftruncate(memoryFd, memorySize), 0);
#endif  // __ANDROID__
    ASSERT_GT(memoryFd, 0);

    EXPECT_EQ(ANeuralNetworksMemory_createFromFd(memorySize, PROT_READ | PROT_WRITE, memoryFd, 0,
                                                 nullptr),
              ANEURALNETWORKS_UNEXPECTED_NULL);

    close(memoryFd);
}

#ifdef __ANDROID__
TEST(ValidationTestMemory, CreateFromAHardwareBuffer) {
    const size_t memorySize = 20;
    AHardwareBuffer_Desc desc{
            .width = memorySize,
            .height = 1,
            .layers = 1,
            .format = AHARDWAREBUFFER_FORMAT_BLOB,
            .usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
    };
    AHardwareBuffer* buffer = nullptr;
    ASSERT_EQ(AHardwareBuffer_allocate(&desc, &buffer), 0);
    EXPECT_EQ(ANeuralNetworksMemory_createFromAHardwareBuffer(buffer, nullptr),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    AHardwareBuffer_release(buffer);

    ANeuralNetworksMemory* memory = nullptr;
    EXPECT_EQ(ANeuralNetworksMemory_createFromAHardwareBuffer(nullptr, &memory),
              ANEURALNETWORKS_UNEXPECTED_NULL);
}
#endif  // __ANDROID__

TEST_F(ValidationTestMemoryDesc, MemoryCopying) {
    uint32_t goodSize = sizeof(float) * 2, badSize1 = sizeof(float), badSize2 = sizeof(float) * 4;
    ANeuralNetworksMemory* goodAshmem = createAshmem(goodSize);
    ANeuralNetworksMemory* badAshmem1 = createAshmem(badSize1);
    ANeuralNetworksMemory* badAshmem2 = createAshmem(badSize2);

    const uint32_t goodDimensions[] = {1, 2};
    const uint32_t badDimensions1[] = {2};
    const uint32_t badDimensions2[] = {2, 1};
    const ANeuralNetworksOperandType goodType = {.type = ANEURALNETWORKS_TENSOR_FLOAT32,
                                                 .dimensionCount = std::size(goodDimensions),
                                                 .dimensions = goodDimensions,
                                                 .scale = 0.0f,
                                                 .zeroPoint = 0};
    const ANeuralNetworksOperandType badType1 = {.type = ANEURALNETWORKS_TENSOR_FLOAT32,
                                                 .dimensionCount = std::size(badDimensions1),
                                                 .dimensions = badDimensions1,
                                                 .scale = 0.0f,
                                                 .zeroPoint = 0};
    const ANeuralNetworksOperandType badType2 = {.type = ANEURALNETWORKS_TENSOR_FLOAT32,
                                                 .dimensionCount = std::size(badDimensions2),
                                                 .dimensions = badDimensions2,
                                                 .scale = 0.0f,
                                                 .zeroPoint = 0};
    const auto [goodModel, goodCompilation] = createAndCompileAddModelWithType(goodType);
    const auto [badModel1, badCompilation1] = createAndCompileAddModelWithType(badType1);
    const auto [badModel2, badCompilation2] = createAndCompileAddModelWithType(badType2);

    ANeuralNetworksMemoryDesc* desc = nullptr;
    ANeuralNetworksMemory *goodDeviceMemory1 = nullptr, *goodDeviceMemory2 = nullptr;
    EXPECT_EQ(ANeuralNetworksMemoryDesc_create(&desc), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksMemoryDesc_addInputRole(desc, goodCompilation, 0, 1.0f),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksMemoryDesc_finish(desc), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksMemory_createFromDesc(desc, &goodDeviceMemory1),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksMemory_createFromDesc(desc, &goodDeviceMemory2),
              ANEURALNETWORKS_NO_ERROR);
    ANeuralNetworksMemoryDesc_free(desc);

    ANeuralNetworksMemory* badDeviceMemory1 = nullptr;
    EXPECT_EQ(ANeuralNetworksMemoryDesc_create(&desc), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksMemoryDesc_addInputRole(desc, badCompilation1, 0, 1.0f),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksMemoryDesc_finish(desc), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksMemory_createFromDesc(desc, &badDeviceMemory1),
              ANEURALNETWORKS_NO_ERROR);
    ANeuralNetworksMemoryDesc_free(desc);

    ANeuralNetworksMemory* badDeviceMemory2 = nullptr;
    EXPECT_EQ(ANeuralNetworksMemoryDesc_create(&desc), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksMemoryDesc_addInputRole(desc, badCompilation2, 0, 1.0f),
              ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksMemoryDesc_finish(desc), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksMemory_createFromDesc(desc, &badDeviceMemory2),
              ANEURALNETWORKS_NO_ERROR);
    ANeuralNetworksMemoryDesc_free(desc);

    EXPECT_EQ(ANeuralNetworksMemory_copy(nullptr, goodDeviceMemory1),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksMemory_copy(goodDeviceMemory1, nullptr),
              ANEURALNETWORKS_UNEXPECTED_NULL);

    // Ashmem -> Ashmem
    // Bad memory size.
    EXPECT_EQ(ANeuralNetworksMemory_copy(goodAshmem, badAshmem1), ANEURALNETWORKS_BAD_DATA);
    EXPECT_EQ(ANeuralNetworksMemory_copy(goodAshmem, badAshmem2), ANEURALNETWORKS_BAD_DATA);
    EXPECT_EQ(ANeuralNetworksMemory_copy(badAshmem1, goodAshmem), ANEURALNETWORKS_BAD_DATA);
    EXPECT_EQ(ANeuralNetworksMemory_copy(badAshmem2, goodAshmem), ANEURALNETWORKS_BAD_DATA);

    // Ashmem -> Device Memory
    // Bad memory size.
    EXPECT_EQ(ANeuralNetworksMemory_copy(badAshmem1, goodDeviceMemory1), ANEURALNETWORKS_BAD_DATA);
    EXPECT_EQ(ANeuralNetworksMemory_copy(badAshmem2, goodDeviceMemory1), ANEURALNETWORKS_BAD_DATA);

    // Device Memory -> Ashmem
    // Uninitialized source device memory.
    EXPECT_EQ(ANeuralNetworksMemory_copy(goodDeviceMemory1, goodAshmem), ANEURALNETWORKS_BAD_DATA);
    // Bad memory size.
    EXPECT_EQ(ANeuralNetworksMemory_copy(goodAshmem, goodDeviceMemory1), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksMemory_copy(goodDeviceMemory1, badAshmem1), ANEURALNETWORKS_BAD_DATA);
    // Uninitialized source device memory (after a failed copy).
    EXPECT_EQ(ANeuralNetworksMemory_copy(badAshmem1, goodDeviceMemory1), ANEURALNETWORKS_BAD_DATA);
    EXPECT_EQ(ANeuralNetworksMemory_copy(goodDeviceMemory1, goodAshmem), ANEURALNETWORKS_BAD_DATA);
    // Bad memory size.
    EXPECT_EQ(ANeuralNetworksMemory_copy(goodAshmem, goodDeviceMemory1), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksMemory_copy(goodDeviceMemory1, badAshmem2), ANEURALNETWORKS_BAD_DATA);

    // Device Memory -> Device Memory
    // Uninitialized source device memory.
    EXPECT_EQ(ANeuralNetworksMemory_copy(goodDeviceMemory2, goodDeviceMemory1),
              ANEURALNETWORKS_BAD_DATA);
    // Incompatible rank.
    EXPECT_EQ(ANeuralNetworksMemory_copy(goodAshmem, badDeviceMemory1), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksMemory_copy(badDeviceMemory1, goodDeviceMemory1),
              ANEURALNETWORKS_BAD_DATA);
    // Incompatible dimensions.
    EXPECT_EQ(ANeuralNetworksMemory_copy(goodAshmem, badDeviceMemory2), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksMemory_copy(badDeviceMemory2, goodDeviceMemory1),
              ANEURALNETWORKS_BAD_DATA);
    // Deinitialized source device memory.
    EXPECT_EQ(ANeuralNetworksMemory_copy(goodAshmem, goodDeviceMemory2), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksMemory_copy(badAshmem1, goodDeviceMemory2), ANEURALNETWORKS_BAD_DATA);
    EXPECT_EQ(ANeuralNetworksMemory_copy(goodDeviceMemory2, goodDeviceMemory1),
              ANEURALNETWORKS_BAD_DATA);

    ANeuralNetworksMemory_free(goodDeviceMemory1);
    ANeuralNetworksMemory_free(goodDeviceMemory2);
    ANeuralNetworksMemory_free(badDeviceMemory1);
    ANeuralNetworksMemory_free(badDeviceMemory2);
    ANeuralNetworksCompilation_free(goodCompilation);
    ANeuralNetworksCompilation_free(badCompilation1);
    ANeuralNetworksCompilation_free(badCompilation2);
    ANeuralNetworksModel_free(goodModel);
    ANeuralNetworksModel_free(badModel1);
    ANeuralNetworksModel_free(badModel2);
}

#ifndef NNTEST_ONLY_PUBLIC_API
TEST(ValidationTestDevice, GetExtensionSupport) {
    bool result;
    EXPECT_EQ(ANeuralNetworksDevice_getExtensionSupport(nullptr, kTestExtensionName, &result),
              ANEURALNETWORKS_UNEXPECTED_NULL);

    uint32_t numDevices = 0;
    EXPECT_EQ(ANeuralNetworks_getDeviceCount(&numDevices), ANEURALNETWORKS_NO_ERROR);

    for (uint32_t i = 0; i < numDevices; i++) {
        SCOPED_TRACE(i);
        ANeuralNetworksDevice* device;
        EXPECT_EQ(ANeuralNetworks_getDevice(i, &device), ANEURALNETWORKS_NO_ERROR);
        EXPECT_EQ(ANeuralNetworksDevice_getExtensionSupport(device, kTestExtensionName, nullptr),
                  ANEURALNETWORKS_UNEXPECTED_NULL);
        EXPECT_EQ(ANeuralNetworksDevice_getExtensionSupport(device, nullptr, &result),
                  ANEURALNETWORKS_UNEXPECTED_NULL);
        EXPECT_EQ(ANeuralNetworksDevice_getExtensionSupport(device, kTestExtensionName, &result),
                  ANEURALNETWORKS_NO_ERROR);
    }
}

constexpr const char* kTestAttributeExtensionName = "com.android.test_attribute_extension";
const uint16_t kAttributeCode = 0;
const uint16_t kAttributeCode2 = 2;
const uint8_t kAttributeValue = 0;

class ValidationTestCompilationExtension : public ValidationTestCompilation {
   protected:
    virtual void SetUp() {
        ValidationTestCompilation::SetUp();
        EXPECT_TRUE(::android::nn::TypeManager::get()->forTest_registerExtension({
                .name = kTestAttributeExtensionName,
                .operandTypes = {},
        }));
    }

    virtual void TearDown() {
        ::android::nn::TypeManager::get()->forTest_reset();
        ValidationTestCompilation::TearDown();
    }
};

// Also see TEST_F(ValidationTestCompilationExtensionForDevices_1, AddExtensionAttribute)
// Also see TEST_F(ValidationTestCompilationExtensionForDevices_2, AddExtensionAttribute)
TEST_F(ValidationTestCompilationExtension, AddExtensionAttribute) {
    EXPECT_EQ(ANeuralNetworksCompilation_addExtensionAttribute(nullptr, kTestAttributeExtensionName,
                                                               kAttributeCode, &kAttributeValue,
                                                               sizeof(uint8_t)),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksCompilation_addExtensionAttribute(
                      mCompilation, nullptr, kAttributeCode, &kAttributeValue, sizeof(uint8_t)),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksCompilation_addExtensionAttribute(
                      mCompilation, kTestAttributeExtensionName, kAttributeCode, nullptr,
                      sizeof(uint8_t)),
              ANEURALNETWORKS_UNEXPECTED_NULL);

    // ExtensionAttribute can only be added to Compilations created from CompilationForDevices with
    // one device specified.
    EXPECT_EQ(ANeuralNetworksCompilation_addExtensionAttribute(
                      mCompilation, kTestAttributeExtensionName, kAttributeCode, &kAttributeValue,
                      sizeof(uint8_t)),
              ANEURALNETWORKS_BAD_DATA);
}

// Also see TEST_F(ValidationTestCompilationExtensionForDevices_1, ExecutionAddExtensionAttribute)
// Also see TEST_F(ValidationTestCompilationExtensionForDevices_2, ExecutionAddExtensionAttribute)
TEST_F(ValidationTestCompilationExtension, ExecutionAddExtensionAttribute) {
    EXPECT_EQ(ANeuralNetworksExecution_addExtensionAttribute(nullptr, kTestAttributeExtensionName,
                                                             kAttributeCode, &kAttributeValue,
                                                             sizeof(uint8_t)),
              ANEURALNETWORKS_UNEXPECTED_NULL);

    ASSERT_EQ(ANeuralNetworksCompilation_finish(mCompilation), ANEURALNETWORKS_NO_ERROR);
    ANeuralNetworksExecution* execution;
    ASSERT_EQ(ANeuralNetworksExecution_create(mCompilation, &execution), ANEURALNETWORKS_NO_ERROR);
    EXPECT_EQ(ANeuralNetworksExecution_addExtensionAttribute(execution, nullptr, kAttributeCode,
                                                             &kAttributeValue, sizeof(uint8_t)),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    EXPECT_EQ(ANeuralNetworksExecution_addExtensionAttribute(execution, kTestAttributeExtensionName,
                                                             kAttributeCode, nullptr,
                                                             sizeof(uint8_t)),
              ANEURALNETWORKS_UNEXPECTED_NULL);
    // ExtensionAttribute can only be added to Executions created from CompilationForDevices with
    // one device specified.
    EXPECT_EQ(ANeuralNetworksExecution_addExtensionAttribute(execution, kTestAttributeExtensionName,
                                                             kAttributeCode, &kAttributeValue,
                                                             sizeof(uint8_t)),
              ANEURALNETWORKS_BAD_DATA);
    ANeuralNetworksExecution_free(execution);
}

class ValidationTestCompilationExtensionForDevices_1
    : public ValidationTestCompilationForDevices_1 {
   protected:
    virtual void SetUp() {
        ValidationTestCompilationForDevices_1::SetUp();
        EXPECT_TRUE(::android::nn::TypeManager::get()->forTest_registerExtension({
                .name = kTestAttributeExtensionName,
                .operandTypes = {},
        }));
    }

    virtual void TearDown() {
        ::android::nn::TypeManager::get()->forTest_reset();
        ValidationTestCompilationForDevices_1::TearDown();
    }
};

// Also see TEST_F(ValidationTestCompilationExtension, AddExtensionAttribute)
// Also see TEST_F(ValidationTestCompilationExtensionForDevices_2, AddExtensionAttribute)
TEST_F(ValidationTestCompilationExtensionForDevices_1, AddExtensionAttribute) {
    if (!mCompilation) {
        return;
    }
    EXPECT_EQ(ANeuralNetworksCompilation_addExtensionAttribute(
                      mCompilation, kTestAttributeExtensionName, kAttributeCode, &kAttributeValue,
                      sizeof(uint8_t)),
              ANEURALNETWORKS_NO_ERROR);
    // Adding another attribute.
    EXPECT_EQ(ANeuralNetworksCompilation_addExtensionAttribute(
                      mCompilation, kTestAttributeExtensionName, kAttributeCode2, &kAttributeValue,
                      sizeof(uint8_t)),
              ANEURALNETWORKS_NO_ERROR);
    // Adding the same attribute twice is illegal.
    EXPECT_EQ(ANeuralNetworksCompilation_addExtensionAttribute(
                      mCompilation, kTestAttributeExtensionName, kAttributeCode, &kAttributeValue,
                      sizeof(uint8_t)),
              ANEURALNETWORKS_BAD_DATA);
    // Attempt to finish
    const int n = ANeuralNetworksCompilation_finish(mCompilation);
    EXPECT_TRUE(n == ANEURALNETWORKS_NO_ERROR);

    EXPECT_EQ(ANeuralNetworksCompilation_addExtensionAttribute(
                      mCompilation, kTestAttributeExtensionName, kAttributeCode, &kAttributeValue,
                      sizeof(uint8_t)),
              ANEURALNETWORKS_BAD_STATE);
}

// Also see TEST_F(ValidationTestCompilationExtension, ExecutionAddExtensionAttribute)
// Also see TEST_F(ValidationTestCompilationExtensionForDevices_2, ExecutionAddExtensionAttribute)
TEST_F(ValidationTestCompilationExtensionForDevices_1, ExecutionAddExtensionAttribute) {
    if (!mCompilation) {
        return;
    }
    ASSERT_EQ(ANeuralNetworksCompilation_finish(mCompilation), ANEURALNETWORKS_NO_ERROR);
    ANeuralNetworksExecution* execution;
    ASSERT_EQ(ANeuralNetworksExecution_create(mCompilation, &execution), ANEURALNETWORKS_NO_ERROR);

    EXPECT_EQ(ANeuralNetworksExecution_addExtensionAttribute(execution, kTestAttributeExtensionName,
                                                             kAttributeCode, &kAttributeValue,
                                                             sizeof(uint8_t)),
              ANEURALNETWORKS_NO_ERROR);
    // Adding another attribute.
    EXPECT_EQ(ANeuralNetworksExecution_addExtensionAttribute(execution, kTestAttributeExtensionName,
                                                             kAttributeCode2, &kAttributeValue,
                                                             sizeof(uint8_t)),
              ANEURALNETWORKS_NO_ERROR);
    // Adding the same attribute twice is illegal.
    EXPECT_EQ(ANeuralNetworksExecution_addExtensionAttribute(execution, kTestAttributeExtensionName,
                                                             kAttributeCode, &kAttributeValue,
                                                             sizeof(uint8_t)),
              ANEURALNETWORKS_BAD_DATA);

    // start the execution
    float in0[] = {0.0f, 0.0f}, in1[] = {1.0f, 1.0f}, out0[2];
    int in2 = 0;
    ASSERT_EQ(ANeuralNetworksExecution_setInput(execution, 0, nullptr, &in0, sizeof(in0)),
              ANEURALNETWORKS_NO_ERROR);
    ASSERT_EQ(ANeuralNetworksExecution_setInput(execution, 1, nullptr, &in1, sizeof(in1)),
              ANEURALNETWORKS_NO_ERROR);
    ASSERT_EQ(ANeuralNetworksExecution_setInput(execution, 2, nullptr, &in2, sizeof(in2)),
              ANEURALNETWORKS_NO_ERROR);
    ASSERT_EQ(ANeuralNetworksExecution_setOutput(execution, 0, nullptr, &out0, sizeof(out0)),
              ANEURALNETWORKS_NO_ERROR);
    ASSERT_EQ(ANeuralNetworksExecution_compute(execution), ANEURALNETWORKS_NO_ERROR);

    EXPECT_EQ(ANeuralNetworksExecution_addExtensionAttribute(execution, kTestAttributeExtensionName,
                                                             kAttributeCode, &kAttributeValue,
                                                             sizeof(uint8_t)),
              ANEURALNETWORKS_BAD_STATE);

    ANeuralNetworksExecution_free(execution);
}

class ValidationTestCompilationExtensionForDevices_2
    : public ValidationTestCompilationForDevices_2 {
   protected:
    virtual void SetUp() {
        ValidationTestCompilationForDevices_2::SetUp();
        EXPECT_TRUE(::android::nn::TypeManager::get()->forTest_registerExtension({
                .name = kTestAttributeExtensionName,
                .operandTypes = {},
        }));
    }

    virtual void TearDown() {
        ::android::nn::TypeManager::get()->forTest_reset();
        ValidationTestCompilationForDevices_2::TearDown();
    }
};

// Also see TEST_F(ValidationTestCompilationExtension, AddExtensionAttribute)
// Also see TEST_F(ValidationTestCompilationExtensionForDevices_1, AddExtensionAttribute)
TEST_F(ValidationTestCompilationExtensionForDevices_2, AddExtensionAttribute) {
    if (!mCompilation) {
        return;
    }
    // ExtensionAttribute can only be added to Compilations created from CompilationForDevices with
    // one device specified.
    EXPECT_EQ(ANeuralNetworksCompilation_addExtensionAttribute(
                      mCompilation, kTestAttributeExtensionName, kAttributeCode, &kAttributeValue,
                      sizeof(uint8_t)),
              ANEURALNETWORKS_BAD_DATA);
}

// Also see TEST_F(ValidationTestCompilationExtension, ExecutionAddExtensionAttribute)
// Also see TEST_F(ValidationTestCompilationExtensionForDevices_1, ExecutionAddExtensionAttribute)
TEST_F(ValidationTestCompilationExtensionForDevices_2, ExecutionAddExtensionAttribute) {
    if (!mCompilation) {
        return;
    }
    ASSERT_EQ(ANeuralNetworksCompilation_finish(mCompilation), ANEURALNETWORKS_NO_ERROR);
    ANeuralNetworksExecution* execution;
    ASSERT_EQ(ANeuralNetworksExecution_create(mCompilation, &execution), ANEURALNETWORKS_NO_ERROR);

    // ExtensionAttribute can only be added to Executions created from CompilationForDevices with
    // one device specified.
    EXPECT_EQ(ANeuralNetworksExecution_addExtensionAttribute(execution, kTestAttributeExtensionName,
                                                             kAttributeCode, &kAttributeValue,
                                                             sizeof(uint8_t)),
              ANEURALNETWORKS_BAD_DATA);
    ANeuralNetworksExecution_free(execution);
}
#endif  // NNTEST_ONLY_PUBLIC_API

}  // namespace
