diff options
author | Michael Butler <butlermichael@google.com> | 2020-05-01 04:05:15 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2020-05-01 04:05:15 +0000 |
commit | e6ed0fc145d449239ad753d8b268a96b928dc830 (patch) | |
tree | 304f9ef1871d5579d62f91a3753118f446630432 /nn | |
parent | bd71fcc8c1959c0b5b0d3529169c51e94f666953 (diff) | |
parent | 56443eb92c61db057948394493d6a5a237d68de7 (diff) | |
download | ml-e6ed0fc145d449239ad753d8b268a96b928dc830.tar.gz |
Merge changes I3e1eb543,I012eb4df into rvc-dev
* changes:
Create tests for VersionedInterfaces errors
Simplify IDevice reboot logic
Diffstat (limited to 'nn')
-rw-r--r-- | nn/common/include/HalInterfaces.h | 1 | ||||
-rw-r--r-- | nn/runtime/Manager.cpp | 37 | ||||
-rw-r--r-- | nn/runtime/Manager.h | 5 | ||||
-rw-r--r-- | nn/runtime/Memory.cpp | 2 | ||||
-rw-r--r-- | nn/runtime/VersionedInterfaces.cpp | 22 | ||||
-rw-r--r-- | nn/runtime/VersionedInterfaces.h | 13 | ||||
-rw-r--r-- | nn/runtime/test/Android.bp | 4 | ||||
-rw-r--r-- | nn/runtime/test/TestVersionedInterfaces.cpp | 2916 |
8 files changed, 2969 insertions, 31 deletions
diff --git a/nn/common/include/HalInterfaces.h b/nn/common/include/HalInterfaces.h index fe1ff563e..4e3a3800b 100644 --- a/nn/common/include/HalInterfaces.h +++ b/nn/common/include/HalInterfaces.h @@ -103,6 +103,7 @@ using OperandExtraParams = V1_2::Operand::ExtraParams; using CacheToken = hardware::hidl_array<uint8_t, static_cast<uint32_t>(Constant::BYTE_SIZE_OF_CACHE_TOKEN)>; +using DeviceFactory = std::function<sp<V1_0::IDevice>(bool blocking)>; using ModelFactory = std::function<Model()>; inline constexpr Priority kDefaultPriority = Priority::MEDIUM; diff --git a/nn/runtime/Manager.cpp b/nn/runtime/Manager.cpp index 7be8419ad..310710e3c 100644 --- a/nn/runtime/Manager.cpp +++ b/nn/runtime/Manager.cpp @@ -55,9 +55,10 @@ const Timing kNoTiming = {.timeOnDevice = UINT64_MAX, .timeInDriver = UINT64_MAX // A Device with actual underlying driver class DriverDevice : public Device { public: - // Create a DriverDevice from a name and an IDevice. + // Create a DriverDevice from a name and a DeviceFactory function. // Returns nullptr on failure. - static std::shared_ptr<DriverDevice> create(std::string name, sp<V1_0::IDevice> device); + static std::shared_ptr<DriverDevice> create(const std::string& name, + const DeviceFactory& makeDevice); // Prefer using DriverDevice::create DriverDevice(std::shared_ptr<VersionedIDevice> device); @@ -159,6 +160,7 @@ class DriverPreparedModel : public PreparedModel { DriverDevice::DriverDevice(std::shared_ptr<VersionedIDevice> device) : kInterface(std::move(device)) { + CHECK(kInterface != nullptr); #ifdef NN_DEBUGGABLE static const char samplePrefix[] = "sample"; if (getName().substr(0, sizeof(samplePrefix) - 1) == samplePrefix) { @@ -167,17 +169,17 @@ DriverDevice::DriverDevice(std::shared_ptr<VersionedIDevice> device) #endif // NN_DEBUGGABLE } -std::shared_ptr<DriverDevice> DriverDevice::create(std::string name, sp<V1_0::IDevice> device) { - CHECK(device != nullptr); - std::shared_ptr<VersionedIDevice> versionedDevice = - VersionedIDevice::create(name, std::move(device)); - if (versionedDevice == nullptr) { +std::shared_ptr<DriverDevice> DriverDevice::create(const std::string& name, + const DeviceFactory& makeDevice) { + CHECK(makeDevice != nullptr); + std::shared_ptr<VersionedIDevice> device = VersionedIDevice::create(name, makeDevice); + if (device == nullptr) { LOG(ERROR) << "DriverDevice::create failed to create VersionedIDevice object for service " << name; return nullptr; } - return std::make_shared<DriverDevice>(std::move(versionedDevice)); + return std::make_shared<DriverDevice>(std::move(device)); } std::vector<bool> DriverDevice::getSupportedOperations(const MetaModel& metaModel) const { @@ -817,7 +819,8 @@ std::shared_ptr<Device> DeviceManager::getCpuDevice() { std::shared_ptr<Device> DeviceManager::forTest_makeDriverDevice(const std::string& name, const sp<V1_0::IDevice>& device) { - const auto driverDevice = DriverDevice::create(name, device); + const DeviceFactory makeDevice = [device](bool /*blocking*/) { return device; }; + const auto driverDevice = DriverDevice::create(name, makeDevice); CHECK(driverDevice != nullptr); return driverDevice; } @@ -829,12 +832,10 @@ void DeviceManager::findAvailableDevices() { const auto names = hardware::getAllHalInstanceNames(V1_0::IDevice::descriptor); for (const auto& name : names) { VLOG(MANAGER) << "Found interface " << name; - sp<V1_0::IDevice> device = V1_0::IDevice::getService(name); - if (device == nullptr) { - LOG(ERROR) << "Got a null IDEVICE for " << name; - continue; - } - registerDevice(name, device); + const DeviceFactory makeDevice = [name](bool blocking) { + return blocking ? V1_0::IDevice::getService(name) : V1_0::IDevice::tryGetService(name); + }; + registerDevice(name, makeDevice); } // register CPU fallback device @@ -842,9 +843,9 @@ void DeviceManager::findAvailableDevices() { mDevicesCpuOnly.push_back(CpuDevice::get()); } -void DeviceManager::registerDevice(const std::string& name, const sp<V1_0::IDevice>& device) { - if (const auto d = DriverDevice::create(name, device)) { - mDevices.push_back(d); +void DeviceManager::registerDevice(const std::string& name, const DeviceFactory& makeDevice) { + if (auto device = DriverDevice::create(name, makeDevice)) { + mDevices.push_back(std::move(device)); } } diff --git a/nn/runtime/Manager.h b/nn/runtime/Manager.h index c28ee49a6..d6d483576 100644 --- a/nn/runtime/Manager.h +++ b/nn/runtime/Manager.h @@ -169,7 +169,8 @@ class DeviceManager { // Register a test device. void forTest_registerDevice(const std::string& name, const sp<hal::V1_0::IDevice>& device) { - registerDevice(name, device); + const hal::DeviceFactory makeDevice = [device](bool /*blocking*/) { return device; }; + registerDevice(name, makeDevice); } // Re-initialize the list of available devices. @@ -192,7 +193,7 @@ class DeviceManager { DeviceManager(); // Adds a device for the manager to use. - void registerDevice(const std::string& name, const sp<hal::V1_0::IDevice>& device); + void registerDevice(const std::string& name, const hal::DeviceFactory& makeDevice); void findAvailableDevices(); diff --git a/nn/runtime/Memory.cpp b/nn/runtime/Memory.cpp index 7bfaf5562..e0bd6b953 100644 --- a/nn/runtime/Memory.cpp +++ b/nn/runtime/Memory.cpp @@ -194,7 +194,7 @@ Memory::Memory(sp<hal::IBuffer> buffer, uint32_t token) : kBuffer(std::move(buffer)), kToken(token) {} Memory::~Memory() { - for (const auto [ptr, weakBurst] : mUsedBy) { + for (const auto& [ptr, weakBurst] : mUsedBy) { if (const std::shared_ptr<ExecutionBurstController> burst = weakBurst.lock()) { burst->freeMemory(getKey()); } diff --git a/nn/runtime/VersionedInterfaces.cpp b/nn/runtime/VersionedInterfaces.cpp index cd39a52a7..3ae950eac 100644 --- a/nn/runtime/VersionedInterfaces.cpp +++ b/nn/runtime/VersionedInterfaces.cpp @@ -703,8 +703,16 @@ std::optional<InitialData> initialize(const Core& core) { } std::shared_ptr<VersionedIDevice> VersionedIDevice::create(std::string serviceName, - sp<V1_0::IDevice> device) { - CHECK(device != nullptr) << "VersionedIDevice::create passed invalid device object."; + const DeviceFactory& makeDevice) { + CHECK(makeDevice != nullptr) + << "VersionedIDevice::create passed invalid device factory object."; + + // get handle to IDevice object + sp<V1_0::IDevice> device = makeDevice(/*blocking=*/true); + if (device == nullptr) { + VLOG(DRIVER) << "VersionedIDevice::create got a null IDevice for " << serviceName; + return nullptr; + } auto core = Core::create(std::move(device)); if (!core.has_value()) { @@ -722,20 +730,22 @@ std::shared_ptr<VersionedIDevice> VersionedIDevice::create(std::string serviceNa std::move(*initialData); return std::make_shared<VersionedIDevice>( std::move(capabilities), std::move(supportedExtensions), type, std::move(versionString), - numberOfCacheFilesNeeded, std::move(serviceName), std::move(core.value())); + numberOfCacheFilesNeeded, std::move(serviceName), makeDevice, std::move(core.value())); } VersionedIDevice::VersionedIDevice(hal::Capabilities capabilities, std::vector<hal::Extension> supportedExtensions, int32_t type, std::string versionString, std::pair<uint32_t, uint32_t> numberOfCacheFilesNeeded, - std::string serviceName, Core core) + std::string serviceName, const DeviceFactory& makeDevice, + Core core) : kCapabilities(std::move(capabilities)), kSupportedExtensions(std::move(supportedExtensions)), kType(type), kVersionString(std::move(versionString)), kNumberOfCacheFilesNeeded(numberOfCacheFilesNeeded), kServiceName(std::move(serviceName)), + kMakeDevice(makeDevice), mCore(std::move(core)) {} std::optional<VersionedIDevice::Core> VersionedIDevice::Core::create(sp<V1_0::IDevice> device) { @@ -874,7 +884,7 @@ Return<T_Return> VersionedIDevice::recoverable( if (pingReturn.isDeadObject()) { VLOG(DRIVER) << "VersionedIDevice::recoverable(" << context << ") -- Recovering " << kServiceName; - sp<V1_0::IDevice> recoveredDevice = V1_0::IDevice::tryGetService(kServiceName); + sp<V1_0::IDevice> recoveredDevice = kMakeDevice(/*blocking=*/false); if (recoveredDevice == nullptr) { VLOG(DRIVER) << "VersionedIDevice::recoverable got a null IDEVICE for " << kServiceName; @@ -911,7 +921,7 @@ int VersionedIDevice::wait() const { auto pingReturn = mCore.getDevice<V1_0::IDevice>()->ping(); if (pingReturn.isDeadObject()) { VLOG(DRIVER) << "VersionedIDevice::wait -- Recovering " << kServiceName; - sp<V1_0::IDevice> recoveredDevice = V1_0::IDevice::getService(kServiceName); + sp<V1_0::IDevice> recoveredDevice = kMakeDevice(/*blocking=*/true); if (recoveredDevice == nullptr) { LOG(ERROR) << "VersionedIDevice::wait got a null IDevice for " << kServiceName; return ANEURALNETWORKS_OP_FAILED; diff --git a/nn/runtime/VersionedInterfaces.h b/nn/runtime/VersionedInterfaces.h index 94ca3fe49..efde0bdf5 100644 --- a/nn/runtime/VersionedInterfaces.h +++ b/nn/runtime/VersionedInterfaces.h @@ -72,12 +72,12 @@ class VersionedIDevice { * protections. * * @param serviceName The name of the service that provides "device". - * @param device A device object that is at least version 1.0 of the IDevice - * interface. + * @param makeDevice A device factory function that returns a device object + * that is at least version 1.0 of the IDevice interface. * @return A valid VersionedIDevice object, otherwise nullptr. */ static std::shared_ptr<VersionedIDevice> create(std::string serviceName, - sp<hal::V1_0::IDevice> device); + const hal::DeviceFactory& makeDevice); /** * Constructor for the VersionedIDevice object. @@ -92,6 +92,8 @@ class VersionedIDevice { * @param numberOfCacheFilesNeeded Number of model cache and data cache * files needed by the driver. * @param serviceName The name of the service that provides core.getDevice<V1_0::IDevice>(). + * @param makeDevice A device factory function that returns a device object + * that is at least version 1.0 of the IDevice interface. * @param core An object that encapsulates a V1_0::IDevice, any appropriate downcasts to * newer interfaces, and a hidl_death_recipient that will proactively handle * the case when the service containing the IDevice object crashes. @@ -100,7 +102,7 @@ class VersionedIDevice { std::vector<hal::Extension> supportedExtensions, int32_t type, std::string versionString, std::pair<uint32_t, uint32_t> numberOfCacheFilesNeeded, - std::string serviceName, Core core); + std::string serviceName, const hal::DeviceFactory& makeDevice, Core core); /** * Gets the capabilities of a driver. @@ -554,6 +556,9 @@ class VersionedIDevice { // The name of the service that implements the driver. const std::string kServiceName; + // Factory function object to generate an IDevice object. + const hal::DeviceFactory kMakeDevice; + // Guards access to mCore. mutable std::shared_mutex mMutex; diff --git a/nn/runtime/test/Android.bp b/nn/runtime/test/Android.bp index f832d90f5..aed0c4e60 100644 --- a/nn/runtime/test/Android.bp +++ b/nn/runtime/test/Android.bp @@ -130,6 +130,7 @@ cc_defaults { "TestPartitioning.cpp", "TestPartitioningRandom.cpp", "TestRemoveDefaultArguments.cpp", + "TestVersionedInterfaces.cpp", "fibonacci_extension/FibonacciDriver.cpp", "fibonacci_extension/FibonacciExtensionTest.cpp", @@ -139,7 +140,9 @@ cc_defaults { ], static_libs: [ "android.hardware.neuralnetworks@1.0-adapter-helper", + "android.hardware.neuralnetworks@1.1-adapter-helper", "android.hardware.neuralnetworks@1.2-adapter-helper", + "android.hardware.neuralnetworks@1.3-adapter-helper", "libSampleDriver", "libgmock", "libhidladapter", @@ -152,6 +155,7 @@ cc_defaults { ], header_libs: [ "libneuralnetworks_private_headers", + "libutils_headers", "neuralnetworks_example_fibonacci_extension", ], } diff --git a/nn/runtime/test/TestVersionedInterfaces.cpp b/nn/runtime/test/TestVersionedInterfaces.cpp new file mode 100644 index 000000000..6d1306d57 --- /dev/null +++ b/nn/runtime/test/TestVersionedInterfaces.cpp @@ -0,0 +1,2916 @@ +/* + * Copyright (C) 2020 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/hardware/neuralnetworks/1.0/ADevice.h> +#include <android/hardware/neuralnetworks/1.1/ADevice.h> +#include <android/hardware/neuralnetworks/1.2/ADevice.h> +#include <android/hardware/neuralnetworks/1.3/ADevice.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <hidl/Status.h> +#include <utils/Errors.h> + +#include <limits> +#include <memory> +#include <utility> +#include <vector> + +#include "HalInterfaces.h" +#include "MemoryUtils.h" +#include "MetaModel.h" +#include "VersionedInterfaces.h" + +namespace android::nn { +namespace { + +using namespace hal; +using testing::_; +using testing::Invoke; +using testing::InvokeWithoutArgs; +using testing::MockFunction; +using MockDeviceFactory = MockFunction<sp<V1_0::IDevice>(bool blocking)>; + +constexpr uint32_t kNoCacheFilesNeeded = 0; +constexpr uint32_t kMaxNumberOfCacheFiles = + static_cast<uint32_t>(Constant::MAX_NUMBER_OF_CACHE_FILES); +constexpr Timing kNoTiming = {.timeOnDevice = std::numeric_limits<uint64_t>::max(), + .timeInDriver = std::numeric_limits<uint64_t>::max()}; + +template <typename... Args> +auto makeCallbackReturn(Args&&... args) { + return [argPack = std::make_tuple(std::forward<Args>(args)...)](const auto& cb) { + std::apply(cb, argPack); + return Void(); + }; +}; + +class MockDevice : public IDevice { + public: + static sp<MockDevice> create() { + const sp<MockDevice> mockDevice = new MockDevice(); + + const auto linkToDeathRet_ret = []() -> Return<bool> { return true; }; + const auto getCapabilities_ret = + makeCallbackReturn(V1_0::ErrorStatus::NONE, V1_0::Capabilities{}); + const auto getCapabilities_1_1_ret = + makeCallbackReturn(V1_0::ErrorStatus::NONE, V1_1::Capabilities{}); + const auto getVersionString_ret = + makeCallbackReturn(V1_0::ErrorStatus::NONE, "Google-MockV1"); + const auto getType_ret = makeCallbackReturn(V1_0::ErrorStatus::NONE, DeviceType::OTHER); + const auto getCapabilities_1_2_ret = + makeCallbackReturn(V1_0::ErrorStatus::NONE, V1_2::Capabilities{}); + const auto getSupportedExtensions_ret = + makeCallbackReturn(V1_0::ErrorStatus::NONE, hidl_vec<Extension>{}); + const auto getNumberOfCacheFilesNeeded_ret = makeCallbackReturn( + V1_0::ErrorStatus::NONE, kMaxNumberOfCacheFiles, kMaxNumberOfCacheFiles); + const auto getCapabilities_1_3_ret = + makeCallbackReturn(V1_3::ErrorStatus::NONE, V1_3::Capabilities{}); + + ON_CALL(*mockDevice, linkToDeathRet()).WillByDefault(Invoke(linkToDeathRet_ret)); + ON_CALL(*mockDevice, getCapabilities(_)).WillByDefault(Invoke(getCapabilities_ret)); + ON_CALL(*mockDevice, getCapabilities_1_1(_)).WillByDefault(Invoke(getCapabilities_1_1_ret)); + ON_CALL(*mockDevice, getVersionString(_)).WillByDefault(Invoke(getVersionString_ret)); + ON_CALL(*mockDevice, getType(_)).WillByDefault(Invoke(getType_ret)); + ON_CALL(*mockDevice, getCapabilities_1_2(_)).WillByDefault(Invoke(getCapabilities_1_2_ret)); + ON_CALL(*mockDevice, getSupportedExtensions(_)) + .WillByDefault(Invoke(getSupportedExtensions_ret)); + ON_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_)) + .WillByDefault(Invoke(getNumberOfCacheFilesNeeded_ret)); + ON_CALL(*mockDevice, getCapabilities_1_3(_)).WillByDefault(Invoke(getCapabilities_1_3_ret)); + + // These EXPECT_CALL(...).Times(testing::AnyNumber()) calls are to + // suppress warnings on the uninteresting methods calls. + EXPECT_CALL(*mockDevice, linkToDeathRet()).Times(testing::AnyNumber()); + EXPECT_CALL(*mockDevice, getCapabilities(_)).Times(testing::AnyNumber()); + EXPECT_CALL(*mockDevice, getCapabilities_1_1(_)).Times(testing::AnyNumber()); + EXPECT_CALL(*mockDevice, getVersionString(_)).Times(testing::AnyNumber()); + EXPECT_CALL(*mockDevice, getType(_)).Times(testing::AnyNumber()); + EXPECT_CALL(*mockDevice, getCapabilities_1_2(_)).Times(testing::AnyNumber()); + EXPECT_CALL(*mockDevice, getSupportedExtensions(_)).Times(testing::AnyNumber()); + EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_)).Times(testing::AnyNumber()); + EXPECT_CALL(*mockDevice, getCapabilities_1_3(_)).Times(testing::AnyNumber()); + + return mockDevice; + } + + // IBase methods below. + Return<bool> linkToDeath(const sp<hidl_death_recipient>& recipient, + uint64_t /*cookie*/) override { + mDeathRecipient = recipient; + return linkToDeathRet(); + } + MOCK_METHOD(Return<void>, ping, (), (override)); + + // V1_0 methods below. + MOCK_METHOD(Return<void>, getCapabilities, (getCapabilities_cb cb), (override)); + MOCK_METHOD(Return<void>, getSupportedOperations, + (const V1_0::Model& model, getSupportedOperations_cb cb), (override)); + MOCK_METHOD(Return<V1_0::ErrorStatus>, prepareModel, + (const V1_0::Model& model, const sp<V1_0::IPreparedModelCallback>& callback), + (override)); + MOCK_METHOD(Return<DeviceStatus>, getStatus, (), (override)); + + // V1_1 methods below. + MOCK_METHOD(Return<void>, getCapabilities_1_1, (getCapabilities_1_1_cb cb), (override)); + MOCK_METHOD(Return<void>, getSupportedOperations_1_1, + (const V1_1::Model& model, getSupportedOperations_1_1_cb cb), (override)); + MOCK_METHOD(Return<V1_0::ErrorStatus>, prepareModel_1_1, + (const V1_1::Model& model, ExecutionPreference preference, + const sp<V1_0::IPreparedModelCallback>& callback), + (override)); + + // V1_2 methods below. + MOCK_METHOD(Return<void>, getVersionString, (getVersionString_cb cb), (override)); + MOCK_METHOD(Return<void>, getType, (getType_cb cb), (override)); + MOCK_METHOD(Return<void>, getCapabilities_1_2, (getCapabilities_1_2_cb cb), (override)); + MOCK_METHOD(Return<void>, getSupportedExtensions, (getSupportedExtensions_cb cb), (override)); + MOCK_METHOD(Return<void>, getSupportedOperations_1_2, + (const V1_2::Model& model, getSupportedOperations_1_2_cb cb), (override)); + MOCK_METHOD(Return<void>, getNumberOfCacheFilesNeeded, (getNumberOfCacheFilesNeeded_cb cb), + (override)); + MOCK_METHOD(Return<V1_0::ErrorStatus>, prepareModel_1_2, + (const V1_2::Model& model, ExecutionPreference preference, + const hidl_vec<hidl_handle>& modelCache, const hidl_vec<hidl_handle>& dataCache, + const CacheToken& token, const sp<V1_2::IPreparedModelCallback>& callback), + (override)); + MOCK_METHOD(Return<V1_0::ErrorStatus>, prepareModelFromCache, + (const hidl_vec<hidl_handle>& modelCache, const hidl_vec<hidl_handle>& dataCache, + const CacheToken& token, const sp<V1_2::IPreparedModelCallback>& callback), + (override)); + + // V1_3 methods below. + MOCK_METHOD(Return<void>, getCapabilities_1_3, (getCapabilities_1_3_cb cb), (override)); + MOCK_METHOD(Return<void>, getSupportedOperations_1_3, + (const V1_3::Model& model, getSupportedOperations_1_3_cb cb), (override)); + MOCK_METHOD(Return<V1_3::ErrorStatus>, prepareModel_1_3, + (const V1_3::Model& model, ExecutionPreference preference, Priority priority, + const OptionalTimePoint& deadline, const hidl_vec<hidl_handle>& modelCache, + const hidl_vec<hidl_handle>& dataCache, const CacheToken& token, + const sp<V1_3::IPreparedModelCallback>& callback), + (override)); + MOCK_METHOD(Return<V1_3::ErrorStatus>, prepareModelFromCache_1_3, + (const OptionalTimePoint& deadline, const hidl_vec<hidl_handle>& modelCache, + const hidl_vec<hidl_handle>& dataCache, const CacheToken& token, + const sp<V1_3::IPreparedModelCallback>& callback), + (override)); + MOCK_METHOD(Return<void>, allocate, + (const BufferDesc& desc, const hidl_vec<sp<V1_3::IPreparedModel>>& preparedModels, + const hidl_vec<BufferRole>& inputRoles, const hidl_vec<BufferRole>& outputRoles, + allocate_cb cb), + (override)); + + // Helper methods. + MOCK_METHOD(Return<bool>, linkToDeathRet, ()); + void simulateCrash() { + ASSERT_NE(nullptr, mDeathRecipient.get()); + + // Currently, the VersionedInterfaces code will not use the `cookie` or + // `who` arguments, so we pass in 0 and nullptr for these arguments + // instead. Normally, they are used by the hidl_death_recipient to + // determine which object is dead. However, the VersionedInterfaces + // code only pairs a single death recipient with a single HIDL + // interface object, so these arguments are redundant. + mDeathRecipient->serviceDied(0, nullptr); + } + + private: + // Members. + sp<hidl_death_recipient> mDeathRecipient; +}; + +class MockPreparedModel : public IPreparedModel { + public: + static sp<MockPreparedModel> create() { + const sp<MockPreparedModel> mockPreparedModel = new MockPreparedModel(); + + const auto linkToDeathRet_ret = []() -> Return<bool> { return true; }; + ON_CALL(*mockPreparedModel, linkToDeathRet()).WillByDefault(Invoke(linkToDeathRet_ret)); + + // This EXPECT_CALL(...).Times(testing::AnyNumber()) calls are to + // suppress warnings on the uninteresting methods calls. + EXPECT_CALL(*mockPreparedModel, linkToDeathRet()).Times(testing::AnyNumber()); + + return mockPreparedModel; + } + + // IBase methods below. + Return<bool> linkToDeath(const sp<hidl_death_recipient>& recipient, + uint64_t /*cookie*/) override { + mDeathRecipient = recipient; + return linkToDeathRet(); + } + MOCK_METHOD(Return<void>, ping, (), (override)); + + // V1_0 methods below. + MOCK_METHOD(Return<V1_0::ErrorStatus>, execute, + (const V1_0::Request& request, const sp<V1_0::IExecutionCallback>& callback), + (override)); + + // V1_2 methods below. + MOCK_METHOD(Return<V1_0::ErrorStatus>, execute_1_2, + (const V1_0::Request& request, MeasureTiming measure, + const sp<V1_2::IExecutionCallback>& callback), + (override)); + MOCK_METHOD(Return<void>, executeSynchronously, + (const V1_0::Request& request, MeasureTiming measure, executeSynchronously_cb cb), + (override)); + MOCK_METHOD(Return<void>, configureExecutionBurst, + (const sp<V1_2::IBurstCallback>& callback, + const hardware::MQDescriptorSync<V1_2::FmqRequestDatum>& requestChannel, + const hardware::MQDescriptorSync<V1_2::FmqResultDatum>& resultChannel, + configureExecutionBurst_cb cb), + (override)); + + // V1_3 methods below. + MOCK_METHOD(Return<ErrorStatus>, execute_1_3, + (const V1_3::Request& request, MeasureTiming measure, + const OptionalTimePoint& deadline, + const OptionalTimeoutDuration& loopTimeoutDuration, + const sp<IExecutionCallback>& callback), + (override)); + MOCK_METHOD(Return<void>, executeSynchronously_1_3, + (const V1_3::Request& request, MeasureTiming measure, + const OptionalTimePoint& deadline, + const OptionalTimeoutDuration& loopTimeoutDuration, + executeSynchronously_1_3_cb cb), + (override)); + MOCK_METHOD(Return<void>, executeFenced, + (const V1_3::Request& request, const hidl_vec<hidl_handle>& waitFor, + MeasureTiming measure, const OptionalTimePoint& deadline, + const OptionalTimeoutDuration& loopTimeoutDuration, + const OptionalTimeoutDuration& duration, executeFenced_cb cb), + (override)); + + // Helper methods. + MOCK_METHOD(Return<bool>, linkToDeathRet, ()); + void simulateCrash() { + ASSERT_NE(nullptr, mDeathRecipient.get()); + + // Currently, the VersionedInterfaces code will not use the `cookie` or + // `who` arguments, so we pass in 0 and nullptr for these arguments + // instead. Normally, they are used by the hidl_death_recipient to + // determine which object is dead. However, the VersionedInterfaces + // code only pairs a single death recipient with a single HIDL + // interface object, so these arguments are redundant. + mDeathRecipient->serviceDied(0, nullptr); + } + + private: + // Members. + sp<hidl_death_recipient> mDeathRecipient; +}; + +class MockBurstContext : public V1_2::IBurstContext { + public: + // V1_2 methods below. + MOCK_METHOD(Return<void>, freeMemory, (int32_t slot), (override)); +}; + +class MockFencedExecutionCallback : public IFencedExecutionCallback { + public: + // V1_3 methods below. + MOCK_METHOD(Return<void>, getExecutionInfo, (getExecutionInfo_cb cb), (override)); +}; + +class MockBuffer : public IBuffer { + public: + // V1_3 methods below. + MOCK_METHOD(Return<ErrorStatus>, copyTo, (const hidl_memory& dst), (override)); + MOCK_METHOD(Return<ErrorStatus>, copyFrom, + (const hidl_memory& src, const hidl_vec<uint32_t>& dimensions), (override)); +}; + +enum class Version { V1_0, V1_1, V1_2, V1_3, MOCK }; + +sp<V1_0::IDevice> adaptAs(const sp<MockDevice>& mockDevice, Version version) { + switch (version) { + case Version::V1_0: + return new V1_0::ADevice(mockDevice); + case Version::V1_1: + return new V1_1::ADevice(mockDevice); + case Version::V1_2: + return new V1_2::ADevice(mockDevice); + case Version::V1_3: + return new V1_3::ADevice(mockDevice); + case Version::MOCK: + return mockDevice; + } + LOG(FATAL) << "unrecognized version: " << static_cast<int>(version); + return nullptr; +} + +auto makePreparedModelReturn(V1_0::ErrorStatus launchStatus, V1_0::ErrorStatus returnStatus, + const sp<MockPreparedModel>& preparedModel) { + return [launchStatus, returnStatus, preparedModel]( + const V1_0::Model& /*model*/, + const sp<V1_0::IPreparedModelCallback>& cb) -> Return<V1_0::ErrorStatus> { + cb->notify(returnStatus, preparedModel).isOk(); + return launchStatus; + }; +} +auto makePreparedModel_1_1Return(V1_0::ErrorStatus launchStatus, V1_0::ErrorStatus returnStatus, + const sp<MockPreparedModel>& preparedModel) { + return [launchStatus, returnStatus, preparedModel]( + const V1_1::Model& /*model*/, ExecutionPreference /*preference*/, + const sp<V1_0::IPreparedModelCallback>& cb) -> Return<V1_0::ErrorStatus> { + cb->notify(returnStatus, preparedModel).isOk(); + return launchStatus; + }; +} +auto makePreparedModel_1_2Return(V1_0::ErrorStatus launchStatus, V1_0::ErrorStatus returnStatus, + const sp<MockPreparedModel>& preparedModel) { + return [launchStatus, returnStatus, preparedModel]( + const V1_2::Model& /*model*/, ExecutionPreference /*preference*/, + const auto& /*modelCache*/, const auto& /*dataCache*/, const auto& /*token*/, + const sp<V1_2::IPreparedModelCallback>& cb) -> Return<V1_0::ErrorStatus> { + cb->notify_1_2(returnStatus, preparedModel).isOk(); + return launchStatus; + }; +} +auto makePreparedModel_1_3Return(V1_3::ErrorStatus launchStatus, V1_3::ErrorStatus returnStatus, + const sp<MockPreparedModel>& preparedModel) { + return [launchStatus, returnStatus, preparedModel]( + const V1_3::Model& /*model*/, ExecutionPreference /*preference*/, + Priority /*priority*/, const OptionalTimePoint& /*deadline*/, + const hidl_vec<hidl_handle>& /*modelCache*/, + const hidl_vec<hidl_handle>& /*dataCache*/, const CacheToken& /*token*/, + const sp<V1_3::IPreparedModelCallback>& cb) -> Return<V1_3::ErrorStatus> { + cb->notify_1_3(returnStatus, preparedModel).isOk(); + return launchStatus; + }; +} + +auto makeExecuteReturn(V1_0::ErrorStatus launchStatus, V1_0::ErrorStatus returnStatus) { + return [launchStatus, returnStatus]( + const V1_0::Request& /*request*/, + const sp<V1_0::IExecutionCallback>& cb) -> Return<V1_0::ErrorStatus> { + cb->notify(returnStatus); + return launchStatus; + }; +} +auto makeExecute_1_2Return(V1_0::ErrorStatus launchStatus, V1_0::ErrorStatus returnStatus, + const std::vector<OutputShape>& outputShapes, const Timing& timing) { + return [launchStatus, returnStatus, outputShapes, timing]( + const V1_0::Request& /*request*/, MeasureTiming /*measureTiming*/, + const sp<V1_2::IExecutionCallback>& cb) -> Return<V1_0::ErrorStatus> { + cb->notify_1_2(returnStatus, outputShapes, timing); + return launchStatus; + }; +} +auto makeExecute_1_3Return(V1_3::ErrorStatus launchStatus, V1_3::ErrorStatus returnStatus, + const std::vector<OutputShape>& outputShapes, const Timing& timing) { + return [launchStatus, returnStatus, outputShapes, timing]( + const V1_3::Request& /*request*/, MeasureTiming /*measureTiming*/, + const OptionalTimePoint& /*deadline*/, + const OptionalTimeoutDuration& /*loopTimeoutDuration*/, + const sp<V1_3::IExecutionCallback>& cb) -> Return<V1_3::ErrorStatus> { + cb->notify_1_3(returnStatus, outputShapes, timing); + return launchStatus; + }; +} +auto makeExecuteSynchronouslyReturn(V1_0::ErrorStatus status, + const std::vector<OutputShape>& outputShapes, + const Timing& timing) { + return [status, outputShapes, timing](const V1_0::Request& /*request*/, + MeasureTiming /*measureTiming*/, + const V1_2::IPreparedModel::executeSynchronously_cb& cb) { + cb(status, outputShapes, timing); + return Void(); + }; +} +auto makeExecuteSynchronously_1_3Return(V1_3::ErrorStatus status, + const std::vector<OutputShape>& outputShapes, + const Timing& timing) { + return [status, outputShapes, timing]( + const V1_3::Request& /*request*/, MeasureTiming /*measureTiming*/, + const OptionalTimePoint& /*deadline*/, + const OptionalTimeoutDuration& /*loopTimeoutDuration*/, + const V1_3::IPreparedModel::executeSynchronously_1_3_cb& cb) { + cb(status, outputShapes, timing); + return Void(); + }; +} +auto makeConfigureExecutionBurst(V1_0::ErrorStatus status, + const sp<MockBurstContext>& burstContext) { + return [status, burstContext]( + const sp<V1_2::IBurstCallback>& /*callback*/, + const hardware::MQDescriptorSync<V1_2::FmqRequestDatum>& /*requestChannel*/, + const hardware::MQDescriptorSync<V1_2::FmqResultDatum>& /*resultChannel*/, + V1_2::IPreparedModel::configureExecutionBurst_cb cb) { + cb(status, burstContext); + return Void(); + }; +} +auto makeExecuteFencedReturn(V1_3::ErrorStatus status, const hidl_handle& syncFence, + const sp<IFencedExecutionCallback>& dispatchCallback) { + return [status, syncFence, dispatchCallback]( + const V1_3::Request& /*request*/, const hidl_vec<hidl_handle>& /*waitFor*/, + MeasureTiming /*measure*/, const OptionalTimePoint& /*deadline*/, + const OptionalTimeoutDuration& /*loopTimeoutDuration*/, + const OptionalTimeoutDuration& /*duration*/, + V1_3::IPreparedModel::executeFenced_cb cb) { + cb(status, syncFence, dispatchCallback); + return Void(); + }; +} + +// TODO: The "setupInitializationExpectation*" calls below re-specify the +// number of expected times each initialization method is called. Because +// this was originally set to `testing::AnyNumber()` when the object was +// created, do these calls act as no-ops, do they override the previous +// expectations, or are both expectations still active? + +void setupInitializationExpectationsV1_0(const sp<MockDevice>& mockDevice) { + EXPECT_CALL(*mockDevice, getCapabilities_1_1(_)).Times(0); + EXPECT_CALL(*mockDevice, getCapabilities_1_2(_)).Times(0); + EXPECT_CALL(*mockDevice, getCapabilities_1_3(_)).Times(0); + EXPECT_CALL(*mockDevice, getVersionString(_)).Times(0); + EXPECT_CALL(*mockDevice, getType(_)).Times(0); + EXPECT_CALL(*mockDevice, getSupportedExtensions(_)).Times(0); + EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_)).Times(0); +} + +void setupInitializationExpectationsV1_1(const sp<MockDevice>& mockDevice) { + EXPECT_CALL(*mockDevice, getCapabilities(_)).Times(0); + EXPECT_CALL(*mockDevice, getCapabilities_1_2(_)).Times(0); + EXPECT_CALL(*mockDevice, getCapabilities_1_3(_)).Times(0); + EXPECT_CALL(*mockDevice, getVersionString(_)).Times(0); + EXPECT_CALL(*mockDevice, getType(_)).Times(0); + EXPECT_CALL(*mockDevice, getSupportedExtensions(_)).Times(0); + EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_)).Times(0); +} + +void setupInitializationExpectationsV1_2(const sp<MockDevice>& mockDevice) { + EXPECT_CALL(*mockDevice, getCapabilities(_)).Times(0); + EXPECT_CALL(*mockDevice, getCapabilities_1_1(_)).Times(0); + EXPECT_CALL(*mockDevice, getCapabilities_1_3(_)).Times(0); +} + +void setupInitializationExpectationsV1_3(const sp<MockDevice>& mockDevice) { + EXPECT_CALL(*mockDevice, getCapabilities(_)).Times(0); + EXPECT_CALL(*mockDevice, getCapabilities_1_1(_)).Times(0); + EXPECT_CALL(*mockDevice, getCapabilities_1_2(_)).Times(0); +} + +void setupInitializationExpectations(const sp<MockDevice>& mockDevice, Version version) { + switch (version) { + case Version::V1_0: + setupInitializationExpectationsV1_0(mockDevice); + return; + case Version::V1_1: + setupInitializationExpectationsV1_1(mockDevice); + return; + case Version::V1_2: + setupInitializationExpectationsV1_2(mockDevice); + return; + case Version::V1_3: + setupInitializationExpectationsV1_3(mockDevice); + return; + case Version::MOCK: + setupInitializationExpectationsV1_3(mockDevice); + return; + } + LOG(FATAL) << "unrecognized version: " << static_cast<int>(version); +} + +void setupSuccessfulInitializationExpectations(const sp<MockDevice>& mockDevice, Version version) { + EXPECT_CALL(*mockDevice, linkToDeathRet()).Times(testing::AnyNumber()); + + const int numCallsForV1_0 = (version == Version::V1_0 ? 1 : 0); + EXPECT_CALL(*mockDevice, getCapabilities(_)).Times(numCallsForV1_0); + + const int numCallsForV1_1 = (version == Version::V1_1 ? 1 : 0); + EXPECT_CALL(*mockDevice, getCapabilities_1_1(_)).Times(numCallsForV1_1); + + const int numCallsForV1_2 = (version == Version::V1_2 ? 1 : 0); + EXPECT_CALL(*mockDevice, getCapabilities_1_2(_)).Times(numCallsForV1_2); + + const int numCallsForAtLeastV1_3 = (version >= Version::V1_3 ? 1 : 0); + EXPECT_CALL(*mockDevice, getCapabilities_1_3(_)).Times(numCallsForAtLeastV1_3); + + const int numCallsForAtLeastV1_2 = (version >= Version::V1_2 ? 1 : 0); + EXPECT_CALL(*mockDevice, getVersionString(_)).Times(numCallsForAtLeastV1_2); + EXPECT_CALL(*mockDevice, getType(_)).Times(numCallsForAtLeastV1_2); + EXPECT_CALL(*mockDevice, getSupportedExtensions(_)).Times(numCallsForAtLeastV1_2); + EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_)).Times(numCallsForAtLeastV1_2); +} + +std::shared_ptr<VersionedIDevice> makeVersionedIDeviceFrom(const sp<MockDevice>& mockDevice, + MockDeviceFactory* mockDeviceFactory, + Version version) { + setupInitializationExpectations(mockDevice, version); + const auto device = adaptAs(mockDevice, version); + ON_CALL(*mockDeviceFactory, Call(_)).WillByDefault(testing::Return(device)); + EXPECT_CALL(*mockDeviceFactory, Call(/*blocking=*/true)).Times(testing::AtLeast(1)); + const DeviceFactory makeDevice = mockDeviceFactory->AsStdFunction(); + return VersionedIDevice::create("MockDevice", makeDevice); +} + +std::shared_ptr<VersionedIDevice> makeVersionedIDeviceSuccessfulInitializationFrom( + const sp<MockDevice>& device, MockDeviceFactory* mockDeviceFactory, Version version) { + setupSuccessfulInitializationExpectations(device, version); + return makeVersionedIDeviceFrom(device, mockDeviceFactory, version); +} + +std::function<hardware::Status()> makeTransportFailure(status_t status) { + return [status] { return hardware::Status::fromStatusT(status); }; +} + +const auto makeGeneralTransportFailure = makeTransportFailure(NO_MEMORY); +const auto makeDeadObjectFailure = makeTransportFailure(DEAD_OBJECT); + +class VersionedIDeviceTest : public testing::Test { + protected: + const sp<MockDevice> kMockDevice = MockDevice::create(); + const std::unique_ptr<MockDeviceFactory> kMockMakeDevice = + std::make_unique<MockDeviceFactory>(); +}; + +class VersionedIDeviceInitializationTest : public VersionedIDeviceTest {}; + +template <Version version> +class VersionedIDeviceInitializedTest : public VersionedIDeviceTest { + protected: + void SetUp() override { + VersionedIDeviceTest::SetUp(); + ASSERT_NE(nullptr, kDevice.get()); + } + + const std::shared_ptr<VersionedIDevice> kDevice = + makeVersionedIDeviceSuccessfulInitializationFrom(kMockDevice, kMockMakeDevice.get(), + version); +}; + +class VersionedIDeviceV1_0Test : public VersionedIDeviceInitializedTest<Version::V1_0> {}; +class VersionedIDeviceV1_1Test : public VersionedIDeviceInitializedTest<Version::V1_1> {}; +class VersionedIDeviceV1_2Test : public VersionedIDeviceInitializedTest<Version::V1_2> {}; +class VersionedIDeviceV1_3Test : public VersionedIDeviceInitializedTest<Version::V1_3> {}; +class VersionedIDeviceMockTest : public VersionedIDeviceInitializedTest<Version::MOCK> {}; + +// Simulate initialization/link error + +TEST_F(VersionedIDeviceInitializationTest, creationFailure) { + // setup failure + EXPECT_CALL(*kMockMakeDevice, Call(_)).Times(1).WillOnce(testing::Return(nullptr)); + const DeviceFactory makeDevice = kMockMakeDevice->AsStdFunction(); + + // run test + const auto device = VersionedIDevice::create("MockDevice", makeDevice); + + // verify failure + EXPECT_EQ(nullptr, device.get()); +} + +TEST_F(VersionedIDeviceInitializationTest, linkToDeathTransportFailure) { + // setup failure + EXPECT_CALL(*kMockDevice, linkToDeathRet()) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); + EXPECT_CALL(*kMockMakeDevice, Call(_)).Times(1).WillOnce(testing::Return(kMockDevice)); + const DeviceFactory makeDevice = kMockMakeDevice->AsStdFunction(); + + // run test + const auto device = VersionedIDevice::create("MockDevice", makeDevice); + + // verify failure + EXPECT_EQ(nullptr, device.get()); +} + +TEST_F(VersionedIDeviceInitializationTest, linkToDeathReturnError) { + // setup failure + const auto ret = []() -> Return<bool> { return false; }; + EXPECT_CALL(*kMockMakeDevice, Call(_)).Times(1).WillOnce(testing::Return(kMockDevice)); + EXPECT_CALL(*kMockDevice, linkToDeathRet()).Times(1).WillOnce(InvokeWithoutArgs(ret)); + const DeviceFactory makeDevice = kMockMakeDevice->AsStdFunction(); + + // run test + const auto device = VersionedIDevice::create("MockDevice", makeDevice); + + // verify failure + EXPECT_EQ(nullptr, device.get()); +} + +TEST_F(VersionedIDeviceInitializationTest, getCapabilitiesFailure) { + // setup failure + const auto ret = makeCallbackReturn(V1_0::ErrorStatus::GENERAL_FAILURE, V1_0::Capabilities{}); + EXPECT_CALL(*kMockDevice, getCapabilities(_)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto device = makeVersionedIDeviceFrom(kMockDevice, kMockMakeDevice.get(), Version::V1_0); + + // verify failure + EXPECT_EQ(nullptr, device.get()); +} + +TEST_F(VersionedIDeviceInitializationTest, getCapabilities_1_1Failure) { + // setup failure + const auto ret = makeCallbackReturn(V1_0::ErrorStatus::GENERAL_FAILURE, V1_1::Capabilities{}); + EXPECT_CALL(*kMockDevice, getCapabilities_1_1(_)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto device = makeVersionedIDeviceFrom(kMockDevice, kMockMakeDevice.get(), Version::V1_1); + + // verify failure + EXPECT_EQ(nullptr, device.get()); +} + +TEST_F(VersionedIDeviceInitializationTest, getCapabilities_1_2Failure) { + // setup failure + const auto ret = makeCallbackReturn(V1_0::ErrorStatus::GENERAL_FAILURE, V1_2::Capabilities{}); + EXPECT_CALL(*kMockDevice, getCapabilities_1_2(_)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto device = makeVersionedIDeviceFrom(kMockDevice, kMockMakeDevice.get(), Version::V1_2); + + // verify failure + EXPECT_EQ(nullptr, device.get()); +} + +TEST_F(VersionedIDeviceInitializationTest, getCapabilities_1_3Failure) { + // setup failure + const auto ret = makeCallbackReturn(V1_3::ErrorStatus::GENERAL_FAILURE, V1_3::Capabilities{}); + EXPECT_CALL(*kMockDevice, getCapabilities_1_3(_)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto device = makeVersionedIDeviceFrom(kMockDevice, kMockMakeDevice.get(), Version::V1_3); + + // verify failure + EXPECT_EQ(nullptr, device.get()); +} + +TEST_F(VersionedIDeviceInitializationTest, getVersionStringFailure) { + // setup failure + const auto ret = makeCallbackReturn(V1_0::ErrorStatus::GENERAL_FAILURE, ""); + EXPECT_CALL(*kMockDevice, getVersionString(_)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto device = makeVersionedIDeviceFrom(kMockDevice, kMockMakeDevice.get(), Version::V1_2); + + // verify failure + EXPECT_EQ(nullptr, device.get()); +} + +TEST_F(VersionedIDeviceInitializationTest, getTypeFailure) { + // setup failure + const auto ret = makeCallbackReturn(V1_0::ErrorStatus::GENERAL_FAILURE, DeviceType::OTHER); + EXPECT_CALL(*kMockDevice, getType(_)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto device = makeVersionedIDeviceFrom(kMockDevice, kMockMakeDevice.get(), Version::V1_2); + + // verify failure + EXPECT_EQ(nullptr, device.get()); +} + +TEST_F(VersionedIDeviceInitializationTest, getSupportedExtensionsFailure) { + // setup failure + const auto ret = makeCallbackReturn(V1_0::ErrorStatus::GENERAL_FAILURE, hidl_vec<Extension>{}); + EXPECT_CALL(*kMockDevice, getSupportedExtensions(_)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto device = makeVersionedIDeviceFrom(kMockDevice, kMockMakeDevice.get(), Version::V1_2); + + // verify failure + EXPECT_EQ(nullptr, device.get()); +} + +TEST_F(VersionedIDeviceInitializationTest, getNumberOfCacheFilesNeededFailure) { + // setup failure + const auto ret = makeCallbackReturn(V1_0::ErrorStatus::GENERAL_FAILURE, kMaxNumberOfCacheFiles, + kMaxNumberOfCacheFiles); + EXPECT_CALL(*kMockDevice, getNumberOfCacheFilesNeeded(_)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto device = makeVersionedIDeviceFrom(kMockDevice, kMockMakeDevice.get(), Version::V1_2); + + // verify failure + EXPECT_EQ(nullptr, device.get()); +} + +TEST_F(VersionedIDeviceInitializationTest, dataCacheFilesExceedsSpecifiedMax) { + // setup failure + const auto ret = makeCallbackReturn(V1_0::ErrorStatus::NONE, kMaxNumberOfCacheFiles + 1, + kMaxNumberOfCacheFiles); + EXPECT_CALL(*kMockDevice, getNumberOfCacheFilesNeeded(_)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto device = makeVersionedIDeviceFrom(kMockDevice, kMockMakeDevice.get(), Version::V1_2); + + // verify failure + EXPECT_EQ(nullptr, device.get()); +} + +TEST_F(VersionedIDeviceInitializationTest, modelCacheFilesExceedsSpecifiedMax) { + // setup failure + const auto ret = makeCallbackReturn(V1_0::ErrorStatus::NONE, kMaxNumberOfCacheFiles, + kMaxNumberOfCacheFiles + 1); + EXPECT_CALL(*kMockDevice, getNumberOfCacheFilesNeeded(_)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto device = makeVersionedIDeviceFrom(kMockDevice, kMockMakeDevice.get(), Version::V1_2); + + // verify failure + EXPECT_EQ(nullptr, device.get()); +} + +TEST_F(VersionedIDeviceInitializationTest, getCapabilitiesTransportFailure) { + // setup failure + EXPECT_CALL(*kMockDevice, getCapabilities(_)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); + + // run test + const auto device = makeVersionedIDeviceFrom(kMockDevice, kMockMakeDevice.get(), Version::V1_0); + + // verify failure + EXPECT_EQ(nullptr, device.get()); +} + +TEST_F(VersionedIDeviceInitializationTest, getCapabilities_1_1TransportFailure) { + // setup failure + EXPECT_CALL(*kMockDevice, getCapabilities_1_1(_)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); + + // run test + const auto device = makeVersionedIDeviceFrom(kMockDevice, kMockMakeDevice.get(), Version::V1_1); + + // verify failure + EXPECT_EQ(nullptr, device.get()); +} + +TEST_F(VersionedIDeviceInitializationTest, getCapabilities_1_2TransportFailure) { + // setup failure + EXPECT_CALL(*kMockDevice, getCapabilities_1_2(_)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); + + // run test + const auto device = makeVersionedIDeviceFrom(kMockDevice, kMockMakeDevice.get(), Version::V1_2); + + // verify failure + EXPECT_EQ(nullptr, device.get()); +} + +TEST_F(VersionedIDeviceInitializationTest, getCapabilities_1_3TransportFailure) { + // setup failure + EXPECT_CALL(*kMockDevice, getCapabilities_1_3(_)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); + + // run test + const auto device = makeVersionedIDeviceFrom(kMockDevice, kMockMakeDevice.get(), Version::V1_3); + + // verify failure + EXPECT_EQ(nullptr, device.get()); +} + +TEST_F(VersionedIDeviceInitializationTest, getVersionStringTransportFailure) { + // setup failure + EXPECT_CALL(*kMockDevice, getVersionString(_)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); + + // run test + const auto device = makeVersionedIDeviceFrom(kMockDevice, kMockMakeDevice.get(), Version::V1_2); + + // verify failure + EXPECT_EQ(nullptr, device.get()); +} + +TEST_F(VersionedIDeviceInitializationTest, getTypeTransportFailure) { + // setup failure + EXPECT_CALL(*kMockDevice, getType(_)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); + + // run test + const auto device = makeVersionedIDeviceFrom(kMockDevice, kMockMakeDevice.get(), Version::V1_2); + + // verify failure + EXPECT_EQ(nullptr, device.get()); +} + +TEST_F(VersionedIDeviceInitializationTest, getSupportedExtensionsTransportFailure) { + // setup failure + EXPECT_CALL(*kMockDevice, getSupportedExtensions(_)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); + + // run test + const auto device = makeVersionedIDeviceFrom(kMockDevice, kMockMakeDevice.get(), Version::V1_2); + + // verify failure + EXPECT_EQ(nullptr, device.get()); +} + +TEST_F(VersionedIDeviceInitializationTest, getNumberOfCacheFilesNeededTransportFailure) { + // setup failure + EXPECT_CALL(*kMockDevice, getNumberOfCacheFilesNeeded(_)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); + + // run test + const auto device = makeVersionedIDeviceFrom(kMockDevice, kMockMakeDevice.get(), Version::V1_2); + + // verify failure + EXPECT_EQ(nullptr, device.get()); +} + +// Ensure device has cached metadata + +TEST_F(VersionedIDeviceV1_0Test, getCapabilities) { + // run test + const auto capabilities = kDevice->getCapabilities(); + const auto cached = kDevice->getCapabilities(); + + // verify success + EXPECT_EQ(PerformanceInfo{}, capabilities.relaxedFloat32toFloat16PerformanceScalar); + EXPECT_EQ(PerformanceInfo{}, capabilities.relaxedFloat32toFloat16PerformanceTensor); + EXPECT_LT(0u, capabilities.operandPerformance.size()); + EXPECT_EQ(cached, capabilities); +} + +TEST_F(VersionedIDeviceV1_1Test, getCapabilities) { + // run test + const auto capabilities = kDevice->getCapabilities(); + const auto cached = kDevice->getCapabilities(); + + // verify success + EXPECT_EQ(PerformanceInfo{}, capabilities.relaxedFloat32toFloat16PerformanceScalar); + EXPECT_EQ(PerformanceInfo{}, capabilities.relaxedFloat32toFloat16PerformanceTensor); + EXPECT_LT(0u, capabilities.operandPerformance.size()); + EXPECT_EQ(cached, capabilities); +} + +TEST_F(VersionedIDeviceV1_2Test, getCapabilities) { + // run test + const auto capabilities = kDevice->getCapabilities(); + const auto cached = kDevice->getCapabilities(); + + // verify success + EXPECT_EQ(PerformanceInfo{}, capabilities.relaxedFloat32toFloat16PerformanceScalar); + EXPECT_EQ(PerformanceInfo{}, capabilities.relaxedFloat32toFloat16PerformanceTensor); + EXPECT_EQ(0u, capabilities.operandPerformance.size()); + EXPECT_EQ(cached, capabilities); +} + +TEST_F(VersionedIDeviceV1_3Test, getCapabilities) { + // run test + const auto capabilities = kDevice->getCapabilities(); + const auto cached = kDevice->getCapabilities(); + + // verify success + EXPECT_EQ(PerformanceInfo{}, capabilities.relaxedFloat32toFloat16PerformanceScalar); + EXPECT_EQ(PerformanceInfo{}, capabilities.relaxedFloat32toFloat16PerformanceTensor); + EXPECT_EQ(0u, capabilities.operandPerformance.size()); + EXPECT_EQ(cached, capabilities); +} + +TEST_F(VersionedIDeviceV1_0Test, getVersionString) { + // run test + const auto versionString = kDevice->getVersionString(); + const auto cached = kDevice->getVersionString(); + + // verify success + EXPECT_EQ("UNKNOWN", versionString); + EXPECT_EQ(cached, versionString); +} + +TEST_F(VersionedIDeviceV1_1Test, getVersionString) { + // run test + const auto versionString = kDevice->getVersionString(); + const auto cached = kDevice->getVersionString(); + + // verify success + EXPECT_EQ("UNKNOWN", versionString); + EXPECT_EQ(cached, versionString); +} + +TEST_F(VersionedIDeviceV1_2Test, getVersionString) { + // run test + const auto versionString = kDevice->getVersionString(); + const auto cached = kDevice->getVersionString(); + + // verify success + EXPECT_EQ("Google-MockV1", versionString); + EXPECT_EQ(cached, versionString); +} + +TEST_F(VersionedIDeviceV1_3Test, getVersionString) { + // run test + const auto versionString = kDevice->getVersionString(); + const auto cached = kDevice->getVersionString(); + + // verify success + EXPECT_EQ("Google-MockV1", versionString); + EXPECT_EQ(cached, versionString); +} + +TEST_F(VersionedIDeviceV1_0Test, getType) { + // run test + const auto type = kDevice->getType(); + const auto cached = kDevice->getType(); + + // verify success + EXPECT_EQ(ANEURALNETWORKS_DEVICE_UNKNOWN, type); + EXPECT_EQ(cached, type); +} + +TEST_F(VersionedIDeviceV1_1Test, getType) { + // run test + const auto type = kDevice->getType(); + const auto cached = kDevice->getType(); + + // verify success + EXPECT_EQ(ANEURALNETWORKS_DEVICE_UNKNOWN, type); + EXPECT_EQ(cached, type); +} + +TEST_F(VersionedIDeviceV1_2Test, getType) { + // run test + const auto type = kDevice->getType(); + const auto cached = kDevice->getType(); + + // verify success + EXPECT_EQ(ANEURALNETWORKS_DEVICE_OTHER, type); + EXPECT_EQ(cached, type); +} + +TEST_F(VersionedIDeviceV1_3Test, getType) { + // run test + const auto type = kDevice->getType(); + const auto cached = kDevice->getType(); + + // verify success + EXPECT_EQ(ANEURALNETWORKS_DEVICE_OTHER, type); + EXPECT_EQ(cached, type); +} + +TEST_F(VersionedIDeviceV1_0Test, getSupportedExtensions) { + // run test + const auto supportedExtensions = kDevice->getSupportedExtensions(); + const auto cached = kDevice->getSupportedExtensions(); + + // verify success + EXPECT_EQ(0u, supportedExtensions.size()); + EXPECT_EQ(cached, supportedExtensions); +} + +TEST_F(VersionedIDeviceV1_1Test, getSupportedExtensions) { + // run test + const auto supportedExtensions = kDevice->getSupportedExtensions(); + const auto cached = kDevice->getSupportedExtensions(); + + // verify success + EXPECT_EQ(0u, supportedExtensions.size()); + EXPECT_EQ(cached, supportedExtensions); +} + +TEST_F(VersionedIDeviceV1_2Test, getSupportedExtensions) { + // run test + const auto supportedExtensions = kDevice->getSupportedExtensions(); + const auto cached = kDevice->getSupportedExtensions(); + + // verify success + EXPECT_EQ(0u, supportedExtensions.size()); + EXPECT_EQ(cached, supportedExtensions); +} + +TEST_F(VersionedIDeviceV1_3Test, getSupportedExtensions) { + // run test + const auto supportedExtensions = kDevice->getSupportedExtensions(); + const auto cached = kDevice->getSupportedExtensions(); + + // verify success + EXPECT_EQ(0u, supportedExtensions.size()); + EXPECT_EQ(cached, supportedExtensions); +} + +TEST_F(VersionedIDeviceV1_0Test, getNumberOfCacheFilesNeeded) { + // run test + const auto [dataCacheFilesNeeded, modelCacheFilesNeeded] = + kDevice->getNumberOfCacheFilesNeeded(); + const auto [cachedDataCacheFilesNeeded, cachedModelCacheFilesNeeded] = + kDevice->getNumberOfCacheFilesNeeded(); + + // verify success + EXPECT_EQ(kNoCacheFilesNeeded, dataCacheFilesNeeded); + EXPECT_EQ(kNoCacheFilesNeeded, modelCacheFilesNeeded); + EXPECT_EQ(cachedDataCacheFilesNeeded, dataCacheFilesNeeded); + EXPECT_EQ(cachedModelCacheFilesNeeded, modelCacheFilesNeeded); +} + +TEST_F(VersionedIDeviceV1_1Test, getNumberOfCacheFilesNeeded) { + // run test + const auto [dataCacheFilesNeeded, modelCacheFilesNeeded] = + kDevice->getNumberOfCacheFilesNeeded(); + const auto [cachedDataCacheFilesNeeded, cachedModelCacheFilesNeeded] = + kDevice->getNumberOfCacheFilesNeeded(); + + // verify success + EXPECT_EQ(kNoCacheFilesNeeded, dataCacheFilesNeeded); + EXPECT_EQ(kNoCacheFilesNeeded, modelCacheFilesNeeded); + EXPECT_EQ(cachedDataCacheFilesNeeded, dataCacheFilesNeeded); + EXPECT_EQ(cachedModelCacheFilesNeeded, modelCacheFilesNeeded); +} + +TEST_F(VersionedIDeviceV1_2Test, getNumberOfCacheFilesNeeded) { + // run test + const auto [dataCacheFilesNeeded, modelCacheFilesNeeded] = + kDevice->getNumberOfCacheFilesNeeded(); + const auto [cachedDataCacheFilesNeeded, cachedModelCacheFilesNeeded] = + kDevice->getNumberOfCacheFilesNeeded(); + + // verify success + EXPECT_EQ(kMaxNumberOfCacheFiles, dataCacheFilesNeeded); + EXPECT_EQ(kMaxNumberOfCacheFiles, modelCacheFilesNeeded); + EXPECT_EQ(cachedDataCacheFilesNeeded, dataCacheFilesNeeded); + EXPECT_EQ(cachedModelCacheFilesNeeded, modelCacheFilesNeeded); +} + +TEST_F(VersionedIDeviceV1_3Test, getNumberOfCacheFilesNeeded) { + // run test + const auto [dataCacheFilesNeeded, modelCacheFilesNeeded] = + kDevice->getNumberOfCacheFilesNeeded(); + const auto [cachedDataCacheFilesNeeded, cachedModelCacheFilesNeeded] = + kDevice->getNumberOfCacheFilesNeeded(); + + // verify success + EXPECT_EQ(kMaxNumberOfCacheFiles, dataCacheFilesNeeded); + EXPECT_EQ(kMaxNumberOfCacheFiles, modelCacheFilesNeeded); + EXPECT_EQ(cachedDataCacheFilesNeeded, dataCacheFilesNeeded); + EXPECT_EQ(cachedModelCacheFilesNeeded, modelCacheFilesNeeded); +} + +TEST_F(VersionedIDeviceV1_0Test, getFeatureLevel) { + // run test + const auto featureLevel = kDevice->getFeatureLevel(); + const auto cached = kDevice->getFeatureLevel(); + + // verify success + constexpr int64_t expectedFeatureLevel = __ANDROID_API_O_MR1__; + EXPECT_EQ(expectedFeatureLevel, featureLevel); + EXPECT_EQ(cached, featureLevel); +} + +TEST_F(VersionedIDeviceV1_1Test, getFeatureLevel) { + // run test + const auto featureLevel = kDevice->getFeatureLevel(); + const auto cached = kDevice->getFeatureLevel(); + + // verify success + constexpr int64_t expectedFeatureLevel = __ANDROID_API_P__; + EXPECT_EQ(expectedFeatureLevel, featureLevel); + EXPECT_EQ(cached, featureLevel); +} + +TEST_F(VersionedIDeviceV1_2Test, getFeatureLevel) { + // run test + const auto featureLevel = kDevice->getFeatureLevel(); + const auto cached = kDevice->getFeatureLevel(); + + // verify success + constexpr int64_t expectedFeatureLevel = __ANDROID_API_Q__; + EXPECT_EQ(expectedFeatureLevel, featureLevel); + EXPECT_EQ(cached, featureLevel); +} + +TEST_F(VersionedIDeviceV1_3Test, getFeatureLevel) { + // run test + const auto featureLevel = kDevice->getFeatureLevel(); + const auto cached = kDevice->getFeatureLevel(); + + // verify success + constexpr int64_t expectedFeatureLevel = __ANDROID_API_R__; + EXPECT_EQ(expectedFeatureLevel, featureLevel); + EXPECT_EQ(cached, featureLevel); +} + +// Simulate successful test + +TEST_F(VersionedIDeviceV1_0Test, getSupportedOperations) { + // setup call + const auto ret = [](const auto& /*model*/, const auto cb) { + cb(V1_0::ErrorStatus::NONE, {}); + return Void(); + }; + EXPECT_CALL(*kMockDevice, getSupportedOperations(_, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto metaModel = MetaModel({}, /*strictSlicing=*/true); + const auto [resultCode, supportedOperations] = kDevice->getSupportedOperations(metaModel); + + // verify success + EXPECT_EQ(V1_3::ErrorStatus::NONE, resultCode); + EXPECT_EQ(0u, supportedOperations.size()); +} + +TEST_F(VersionedIDeviceV1_1Test, getSupportedOperations) { + // setup call + const auto ret = [](const auto& /*model*/, const auto cb) { + cb(V1_0::ErrorStatus::NONE, {}); + return Void(); + }; + EXPECT_CALL(*kMockDevice, getSupportedOperations_1_1(_, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto metaModel = MetaModel({}, /*strictSlicing=*/true); + const auto [resultCode, supportedOperations] = kDevice->getSupportedOperations(metaModel); + + // verify success + EXPECT_EQ(V1_3::ErrorStatus::NONE, resultCode); + EXPECT_EQ(0u, supportedOperations.size()); +} + +TEST_F(VersionedIDeviceV1_2Test, getSupportedOperations) { + // setup call + const auto ret = [](const auto& /*model*/, const auto cb) { + cb(V1_0::ErrorStatus::NONE, {}); + return Void(); + }; + EXPECT_CALL(*kMockDevice, getSupportedOperations_1_2(_, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto metaModel = MetaModel({}, /*strictSlicing=*/true); + const auto [resultCode, supportedOperations] = kDevice->getSupportedOperations(metaModel); + + // verify success + EXPECT_EQ(V1_3::ErrorStatus::NONE, resultCode); + EXPECT_EQ(0u, supportedOperations.size()); +} + +TEST_F(VersionedIDeviceV1_3Test, getSupportedOperations) { + // setup call + const auto ret = [](const auto& /*model*/, const auto cb) { + cb(V1_3::ErrorStatus::NONE, {}); + return Void(); + }; + EXPECT_CALL(*kMockDevice, getSupportedOperations_1_3(_, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto metaModel = MetaModel({}, /*strictSlicing=*/true); + const auto [resultCode, supportedOperations] = kDevice->getSupportedOperations(metaModel); + + // verify success + EXPECT_EQ(V1_3::ErrorStatus::NONE, resultCode); + EXPECT_EQ(0u, supportedOperations.size()); +} + +TEST_F(VersionedIDeviceV1_0Test, prepareModel) { + // setup call + const sp<MockPreparedModel> mockPreparedModel = MockPreparedModel::create(); + const auto ret = makePreparedModelReturn(V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::NONE, + mockPreparedModel); + EXPECT_CALL(*kMockDevice, prepareModel(_, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const ModelFactory makeModel = [] { return V1_3::Model{}; }; + const auto [resultCode, preparedModel] = kDevice->prepareModel(makeModel, {}, {}, {}, {}, {}); + + // verify success + EXPECT_EQ(ANEURALNETWORKS_NO_ERROR, resultCode); + EXPECT_NE(nullptr, preparedModel.get()); +} + +TEST_F(VersionedIDeviceV1_1Test, prepareModel) { + // setup call + const sp<MockPreparedModel> mockPreparedModel = MockPreparedModel::create(); + const auto ret = makePreparedModel_1_1Return(V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::NONE, + mockPreparedModel); + EXPECT_CALL(*kMockDevice, prepareModel_1_1(_, _, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const ModelFactory makeModel = [] { return V1_3::Model{}; }; + const auto [resultCode, preparedModel] = kDevice->prepareModel(makeModel, {}, {}, {}, {}, {}); + + // verify success + EXPECT_EQ(ANEURALNETWORKS_NO_ERROR, resultCode); + EXPECT_NE(nullptr, preparedModel.get()); +} + +TEST_F(VersionedIDeviceV1_2Test, prepareModel) { + // setup call + const sp<MockPreparedModel> mockPreparedModel = MockPreparedModel::create(); + const auto ret = makePreparedModel_1_2Return(V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::NONE, + mockPreparedModel); + EXPECT_CALL(*kMockDevice, prepareModel_1_2(_, _, _, _, _, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const ModelFactory makeModel = [] { return V1_3::Model{}; }; + const auto [resultCode, preparedModel] = kDevice->prepareModel(makeModel, {}, {}, {}, {}, {}); + + // verify success + EXPECT_EQ(ANEURALNETWORKS_NO_ERROR, resultCode); + EXPECT_NE(nullptr, preparedModel.get()); +} + +TEST_F(VersionedIDeviceV1_3Test, prepareModel) { + // setup call + const sp<MockPreparedModel> mockPreparedModel = MockPreparedModel::create(); + const auto ret = makePreparedModel_1_3Return(V1_3::ErrorStatus::NONE, V1_3::ErrorStatus::NONE, + mockPreparedModel); + EXPECT_CALL(*kMockDevice, prepareModel_1_3(_, _, _, _, _, _, _, _)) + .Times(1) + .WillOnce(Invoke(ret)); + + // run test + const ModelFactory makeModel = [] { return V1_3::Model{}; }; + const auto [resultCode, preparedModel] = kDevice->prepareModel(makeModel, {}, {}, {}, {}, {}); + + // verify success + EXPECT_EQ(ANEURALNETWORKS_NO_ERROR, resultCode); + EXPECT_NE(nullptr, preparedModel.get()); +} + +TEST_F(VersionedIDeviceV1_0Test, allocate) { + // run test + const auto [status, buffer, token] = kDevice->allocate({}, {}, {}, {}); + + // verify success + EXPECT_EQ(V1_3::ErrorStatus::GENERAL_FAILURE, status); + EXPECT_EQ(nullptr, buffer.get()); + EXPECT_EQ(0u, token); +} + +TEST_F(VersionedIDeviceV1_1Test, allocate) { + // run test + const auto [status, buffer, token] = kDevice->allocate({}, {}, {}, {}); + + // verify success + EXPECT_EQ(V1_3::ErrorStatus::GENERAL_FAILURE, status); + EXPECT_EQ(nullptr, buffer.get()); + EXPECT_EQ(0u, token); +} + +TEST_F(VersionedIDeviceV1_2Test, allocate) { + // run test + const auto [status, buffer, token] = kDevice->allocate({}, {}, {}, {}); + + // verify success + EXPECT_EQ(V1_3::ErrorStatus::GENERAL_FAILURE, status); + EXPECT_EQ(nullptr, buffer.get()); + EXPECT_EQ(0u, token); +} + +TEST_F(VersionedIDeviceV1_3Test, allocate) { + // setup call + const sp<MockBuffer> mockBuffer = new MockBuffer(); + constexpr uint32_t mockToken = 1; + const auto ret = [mockBuffer](const BufferDesc& /*desc*/, + const hidl_vec<sp<V1_3::IPreparedModel>>& /*preparedModels*/, + const hidl_vec<BufferRole>& /*inputRoles*/, + const hidl_vec<BufferRole>& /*outputRoles*/, + V1_3::IDevice::allocate_cb cb) -> Return<void> { + cb(V1_3::ErrorStatus::NONE, mockBuffer, mockToken); + return Void(); + }; + EXPECT_CALL(*kMockDevice, allocate(_, _, _, _, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto [status, buffer, token] = kDevice->allocate({}, {}, {}, {}); + + // verify success + EXPECT_EQ(V1_3::ErrorStatus::NONE, status); + EXPECT_NE(nullptr, buffer.get()); + EXPECT_NE(0u, token); +} + +TEST_F(VersionedIDeviceMockTest, wait) { + // setup call + const auto ret = []() -> Return<void> { return {}; }; + EXPECT_CALL(*kMockDevice, ping()).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto resultCode = kDevice->wait(); + + // verify success + EXPECT_EQ(ANEURALNETWORKS_NO_ERROR, resultCode); +} + +// Simulate general failure + +TEST_F(VersionedIDeviceV1_0Test, getSupportedOperationsFailure) { + // setup failure + const auto ret = [](const auto& /*model*/, const auto cb) { + cb(V1_0::ErrorStatus::GENERAL_FAILURE, {}); + return Void(); + }; + EXPECT_CALL(*kMockDevice, getSupportedOperations(_, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto metaModel = MetaModel({}, /*strictSlicing=*/true); + const auto [resultCode, supportedOperations] = kDevice->getSupportedOperations(metaModel); + + // verify failure + EXPECT_EQ(V1_3::ErrorStatus::GENERAL_FAILURE, resultCode); + EXPECT_EQ(0u, supportedOperations.size()); +} + +TEST_F(VersionedIDeviceV1_1Test, getSupportedOperationsFailure) { + // setup failure + const auto ret = [](const auto& /*model*/, const auto cb) { + cb(V1_0::ErrorStatus::GENERAL_FAILURE, {}); + return Void(); + }; + EXPECT_CALL(*kMockDevice, getSupportedOperations_1_1(_, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto metaModel = MetaModel({}, /*strictSlicing=*/true); + const auto [resultCode, supportedOperations] = kDevice->getSupportedOperations(metaModel); + + // verify failure + EXPECT_EQ(V1_3::ErrorStatus::GENERAL_FAILURE, resultCode); + EXPECT_EQ(0u, supportedOperations.size()); +} + +TEST_F(VersionedIDeviceV1_2Test, getSupportedOperationsFailure) { + // setup failure + const auto ret = [](const auto& /*model*/, const auto cb) { + cb(V1_0::ErrorStatus::GENERAL_FAILURE, {}); + return Void(); + }; + EXPECT_CALL(*kMockDevice, getSupportedOperations_1_2(_, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto metaModel = MetaModel({}, /*strictSlicing=*/true); + const auto [resultCode, supportedOperations] = kDevice->getSupportedOperations(metaModel); + + // verify failure + EXPECT_EQ(V1_3::ErrorStatus::GENERAL_FAILURE, resultCode); + EXPECT_EQ(0u, supportedOperations.size()); +} + +TEST_F(VersionedIDeviceV1_3Test, getSupportedOperationsFailure) { + // setup failure + const auto ret = [](const auto& /*model*/, const auto cb) { + cb(V1_3::ErrorStatus::GENERAL_FAILURE, {}); + return Void(); + }; + EXPECT_CALL(*kMockDevice, getSupportedOperations_1_3(_, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto metaModel = MetaModel({}, /*strictSlicing=*/true); + const auto [resultCode, supportedOperations] = kDevice->getSupportedOperations(metaModel); + + // verify failure + EXPECT_EQ(V1_3::ErrorStatus::GENERAL_FAILURE, resultCode); + EXPECT_EQ(0u, supportedOperations.size()); +} + +TEST_F(VersionedIDeviceV1_0Test, prepareModelLaunchFailure) { + // setup failure + const sp<MockPreparedModel> mockPreparedModel = MockPreparedModel::create(); + const auto ret = makePreparedModelReturn(V1_0::ErrorStatus::GENERAL_FAILURE, + V1_0::ErrorStatus::NONE, mockPreparedModel); + EXPECT_CALL(*kMockDevice, prepareModel(_, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const ModelFactory makeModel = [] { return V1_3::Model{}; }; + const auto [resultCode, preparedModel] = kDevice->prepareModel(makeModel, {}, {}, {}, {}, {}); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(nullptr, preparedModel.get()); +} + +TEST_F(VersionedIDeviceV1_1Test, prepareModelLaunchFailure) { + // setup failure + const sp<MockPreparedModel> mockPreparedModel = MockPreparedModel::create(); + const auto ret = makePreparedModel_1_1Return(V1_0::ErrorStatus::GENERAL_FAILURE, + V1_0::ErrorStatus::NONE, mockPreparedModel); + EXPECT_CALL(*kMockDevice, prepareModel_1_1(_, _, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const ModelFactory makeModel = [] { return V1_3::Model{}; }; + const auto [resultCode, preparedModel] = kDevice->prepareModel(makeModel, {}, {}, {}, {}, {}); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(nullptr, preparedModel.get()); +} + +TEST_F(VersionedIDeviceV1_2Test, prepareModelLaunchFailure) { + // setup failure + const sp<MockPreparedModel> mockPreparedModel = MockPreparedModel::create(); + const auto ret = makePreparedModel_1_2Return(V1_0::ErrorStatus::GENERAL_FAILURE, + V1_0::ErrorStatus::NONE, mockPreparedModel); + EXPECT_CALL(*kMockDevice, prepareModel_1_2(_, _, _, _, _, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const ModelFactory makeModel = [] { return V1_3::Model{}; }; + const auto [resultCode, preparedModel] = kDevice->prepareModel(makeModel, {}, {}, {}, {}, {}); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(nullptr, preparedModel.get()); +} + +TEST_F(VersionedIDeviceV1_3Test, prepareModelLaunchFailure) { + // setup failure + const sp<MockPreparedModel> mockPreparedModel = MockPreparedModel::create(); + const auto ret = makePreparedModel_1_3Return(V1_3::ErrorStatus::GENERAL_FAILURE, + V1_3::ErrorStatus::NONE, mockPreparedModel); + EXPECT_CALL(*kMockDevice, prepareModel_1_3(_, _, _, _, _, _, _, _)) + .Times(1) + .WillOnce(Invoke(ret)); + + // run test + const ModelFactory makeModel = [] { return V1_3::Model{}; }; + const auto [resultCode, preparedModel] = kDevice->prepareModel(makeModel, {}, {}, {}, {}, {}); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(nullptr, preparedModel.get()); +} + +TEST_F(VersionedIDeviceV1_0Test, prepareModelReturnFailure) { + // setup failure + const sp<MockPreparedModel> mockPreparedModel = MockPreparedModel::create(); + const auto ret = makePreparedModelReturn(V1_0::ErrorStatus::NONE, + V1_0::ErrorStatus::GENERAL_FAILURE, mockPreparedModel); + EXPECT_CALL(*kMockDevice, prepareModel(_, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const ModelFactory makeModel = [] { return V1_3::Model{}; }; + const auto [resultCode, preparedModel] = kDevice->prepareModel(makeModel, {}, {}, {}, {}, {}); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(nullptr, preparedModel.get()); +} + +TEST_F(VersionedIDeviceV1_1Test, prepareModelReturnFailure) { + // setup failure + const sp<MockPreparedModel> mockPreparedModel = MockPreparedModel::create(); + const auto ret = makePreparedModel_1_1Return( + V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::GENERAL_FAILURE, mockPreparedModel); + EXPECT_CALL(*kMockDevice, prepareModel_1_1(_, _, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const ModelFactory makeModel = [] { return V1_3::Model{}; }; + const auto [resultCode, preparedModel] = kDevice->prepareModel(makeModel, {}, {}, {}, {}, {}); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(nullptr, preparedModel.get()); +} + +TEST_F(VersionedIDeviceV1_2Test, prepareModelReturnFailure) { + // setup failure + const sp<MockPreparedModel> mockPreparedModel = MockPreparedModel::create(); + const auto ret = makePreparedModel_1_2Return( + V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::GENERAL_FAILURE, mockPreparedModel); + EXPECT_CALL(*kMockDevice, prepareModel_1_2(_, _, _, _, _, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const ModelFactory makeModel = [] { return V1_3::Model{}; }; + const auto [resultCode, preparedModel] = kDevice->prepareModel(makeModel, {}, {}, {}, {}, {}); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(nullptr, preparedModel.get()); +} + +TEST_F(VersionedIDeviceV1_3Test, prepareModelReturnFailure) { + // setup failure + const sp<MockPreparedModel> mockPreparedModel = MockPreparedModel::create(); + const auto ret = makePreparedModel_1_3Return( + V1_3::ErrorStatus::NONE, V1_3::ErrorStatus::GENERAL_FAILURE, mockPreparedModel); + EXPECT_CALL(*kMockDevice, prepareModel_1_3(_, _, _, _, _, _, _, _)) + .Times(1) + .WillOnce(Invoke(ret)); + + // run test + const ModelFactory makeModel = [] { return V1_3::Model{}; }; + const auto [resultCode, preparedModel] = kDevice->prepareModel(makeModel, {}, {}, {}, {}, {}); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(nullptr, preparedModel.get()); +} + +TEST_F(VersionedIDeviceV1_0Test, prepareModelNullptrError) { + // setup failure + const sp<MockPreparedModel> mockPreparedModel = nullptr; + const auto ret = makePreparedModelReturn(V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::NONE, + mockPreparedModel); + EXPECT_CALL(*kMockDevice, prepareModel(_, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const ModelFactory makeModel = [] { return V1_3::Model{}; }; + const auto [resultCode, preparedModel] = kDevice->prepareModel(makeModel, {}, {}, {}, {}, {}); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(nullptr, preparedModel.get()); +} + +TEST_F(VersionedIDeviceV1_1Test, prepareModelNullptrError) { + // setup failure + const sp<MockPreparedModel> mockPreparedModel = nullptr; + const auto ret = makePreparedModel_1_1Return(V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::NONE, + mockPreparedModel); + EXPECT_CALL(*kMockDevice, prepareModel_1_1(_, _, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const ModelFactory makeModel = [] { return V1_3::Model{}; }; + const auto [resultCode, preparedModel] = kDevice->prepareModel(makeModel, {}, {}, {}, {}, {}); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(nullptr, preparedModel.get()); +} + +TEST_F(VersionedIDeviceV1_2Test, prepareModelNullptrError) { + // setup failure + const sp<MockPreparedModel> mockPreparedModel = nullptr; + const auto ret = makePreparedModel_1_2Return(V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::NONE, + mockPreparedModel); + EXPECT_CALL(*kMockDevice, prepareModel_1_2(_, _, _, _, _, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const ModelFactory makeModel = [] { return V1_3::Model{}; }; + const auto [resultCode, preparedModel] = kDevice->prepareModel(makeModel, {}, {}, {}, {}, {}); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(nullptr, preparedModel.get()); +} + +TEST_F(VersionedIDeviceV1_3Test, prepareModelNullptrError) { + // setup failure + const sp<MockPreparedModel> mockPreparedModel = nullptr; + const auto ret = makePreparedModel_1_3Return(V1_3::ErrorStatus::NONE, V1_3::ErrorStatus::NONE, + mockPreparedModel); + EXPECT_CALL(*kMockDevice, prepareModel_1_3(_, _, _, _, _, _, _, _)) + .Times(1) + .WillOnce(Invoke(ret)); + + // run test + const ModelFactory makeModel = [] { return V1_3::Model{}; }; + const auto [resultCode, preparedModel] = kDevice->prepareModel(makeModel, {}, {}, {}, {}, {}); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(nullptr, preparedModel.get()); +} + +TEST_F(VersionedIDeviceV1_3Test, allocateFailure) { + // setup failure + const auto ret = [](const BufferDesc& /*desc*/, + const hidl_vec<sp<V1_3::IPreparedModel>>& /*preparedModels*/, + const hidl_vec<BufferRole>& /*inputRoles*/, + const hidl_vec<BufferRole>& /*outputRoles*/, + V1_3::IDevice::allocate_cb cb) -> Return<void> { + cb(V1_3::ErrorStatus::GENERAL_FAILURE, nullptr, 0); + return Void(); + }; + EXPECT_CALL(*kMockDevice, allocate(_, _, _, _, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto [status, buffer, token] = kDevice->allocate({}, {}, {}, {}); + + // verify failure + EXPECT_EQ(V1_3::ErrorStatus::GENERAL_FAILURE, status); + EXPECT_EQ(nullptr, buffer.get()); + EXPECT_EQ(0u, token); +} + +// Simulate transport failure + +TEST_F(VersionedIDeviceV1_0Test, getSupportedOperationsTransportFailure) { + // setup failure + EXPECT_CALL(*kMockDevice, getSupportedOperations(_, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); + + // run test + const auto metaModel = MetaModel({}, /*strictSlicing=*/true); + const auto [resultCode, supportedOperations] = kDevice->getSupportedOperations(metaModel); + + // verify failure + EXPECT_EQ(V1_3::ErrorStatus::GENERAL_FAILURE, resultCode); + EXPECT_EQ(0u, supportedOperations.size()); +} + +TEST_F(VersionedIDeviceV1_1Test, getSupportedOperationsTransportFailure) { + // setup failure + EXPECT_CALL(*kMockDevice, getSupportedOperations_1_1(_, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); + + // run test + const auto metaModel = MetaModel({}, /*strictSlicing=*/true); + const auto [resultCode, supportedOperations] = kDevice->getSupportedOperations(metaModel); + + // verify failure + EXPECT_EQ(V1_3::ErrorStatus::GENERAL_FAILURE, resultCode); + EXPECT_EQ(0u, supportedOperations.size()); +} + +TEST_F(VersionedIDeviceV1_2Test, getSupportedOperationsTransportFailure) { + // setup failure + EXPECT_CALL(*kMockDevice, getSupportedOperations_1_2(_, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); + + // run test + const auto metaModel = MetaModel({}, /*strictSlicing=*/true); + const auto [resultCode, supportedOperations] = kDevice->getSupportedOperations(metaModel); + + // verify failure + EXPECT_EQ(V1_3::ErrorStatus::GENERAL_FAILURE, resultCode); + EXPECT_EQ(0u, supportedOperations.size()); +} + +TEST_F(VersionedIDeviceV1_3Test, getSupportedOperationsTransportFailure) { + // setup failure + EXPECT_CALL(*kMockDevice, getSupportedOperations_1_3(_, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); + + // run test + const auto metaModel = MetaModel({}, /*strictSlicing=*/true); + const auto [resultCode, supportedOperations] = kDevice->getSupportedOperations(metaModel); + + // verify failure + EXPECT_EQ(V1_3::ErrorStatus::GENERAL_FAILURE, resultCode); + EXPECT_EQ(0u, supportedOperations.size()); +} + +TEST_F(VersionedIDeviceV1_0Test, prepareModelTransportFailure) { + // setup failure + EXPECT_CALL(*kMockDevice, prepareModel(_, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); + + // run test + const ModelFactory makeModel = [] { return V1_3::Model{}; }; + const auto [resultCode, preparedModel] = kDevice->prepareModel(makeModel, {}, {}, {}, {}, {}); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(nullptr, preparedModel.get()); +} + +TEST_F(VersionedIDeviceV1_1Test, prepareModelTransportFailure) { + // setup failure + EXPECT_CALL(*kMockDevice, prepareModel_1_1(_, _, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); + + // run test + const ModelFactory makeModel = [] { return V1_3::Model{}; }; + const auto [resultCode, preparedModel] = kDevice->prepareModel(makeModel, {}, {}, {}, {}, {}); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(nullptr, preparedModel.get()); +} + +TEST_F(VersionedIDeviceV1_2Test, prepareModelTransportFailure) { + // setup failure + EXPECT_CALL(*kMockDevice, prepareModel_1_2(_, _, _, _, _, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); + + // run test + const ModelFactory makeModel = [] { return V1_3::Model{}; }; + const auto [resultCode, preparedModel] = kDevice->prepareModel(makeModel, {}, {}, {}, {}, {}); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(nullptr, preparedModel.get()); +} + +TEST_F(VersionedIDeviceV1_3Test, prepareModelTransportFailure) { + // setup failure + EXPECT_CALL(*kMockDevice, prepareModel_1_3(_, _, _, _, _, _, _, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); + + // run test + const ModelFactory makeModel = [] { return V1_3::Model{}; }; + const auto [resultCode, preparedModel] = kDevice->prepareModel(makeModel, {}, {}, {}, {}, {}); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(nullptr, preparedModel.get()); +} + +TEST_F(VersionedIDeviceV1_3Test, allocateTransportFailure) { + // setup failure + EXPECT_CALL(*kMockDevice, allocate(_, _, _, _, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); + + // run test + const auto [status, buffer, token] = kDevice->allocate({}, {}, {}, {}); + + // verify failure + EXPECT_EQ(V1_3::ErrorStatus::GENERAL_FAILURE, status); + EXPECT_EQ(nullptr, buffer.get()); + EXPECT_EQ(0u, token); +} + +TEST_F(VersionedIDeviceMockTest, waitTransportFailure) { + // setup call + EXPECT_CALL(*kMockDevice, ping()) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); + + // run test + const auto resultCode = kDevice->wait(); + + // verify success + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); +} + +// Simulate service crash + +// TODO: enable this test once b/154183300 is fixed. +TEST_F(VersionedIDeviceMockTest, DISABLED_prepareModelRecoverCrash) { + // setup original device calls + EXPECT_CALL(*kMockDevice, prepareModel_1_3(_, _, _, _, _, _, _, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure)); + EXPECT_CALL(*kMockDevice, ping()).Times(1).WillOnce(InvokeWithoutArgs(makeDeadObjectFailure)); + + // setup recovery call + const sp<MockDevice> mockRecoveredDevice = MockDevice::create(); + EXPECT_CALL(*kMockMakeDevice, Call(/*blocking=*/false)) + .Times(1) + .WillOnce(testing::Return(mockRecoveredDevice)); + + // setup recovered device calls + const sp<MockPreparedModel> mockPreparedModel = MockPreparedModel::create(); + const auto ret = makePreparedModel_1_3Return(V1_3::ErrorStatus::NONE, V1_3::ErrorStatus::NONE, + mockPreparedModel); + EXPECT_CALL(*mockRecoveredDevice, linkToDeathRet()).Times(1); + EXPECT_CALL(*mockRecoveredDevice, prepareModel_1_3(_, _, _, _, _, _, _, _)) + .Times(1) + .WillOnce(Invoke(ret)); + + // run test + const ModelFactory makeModel = [] { return V1_3::Model{}; }; + const auto [resultCode, preparedModel] = kDevice->prepareModel(makeModel, {}, {}, {}, {}, {}); + + // verify success + EXPECT_EQ(ANEURALNETWORKS_NO_ERROR, resultCode); + EXPECT_NE(nullptr, preparedModel.get()); +} + +TEST_F(VersionedIDeviceMockTest, prepareModelFullCrash) { + // setup failure + EXPECT_CALL(*kMockDevice, prepareModel_1_3(_, _, _, _, _, _, _, _)) + .Times(1) + .WillRepeatedly(InvokeWithoutArgs(makeDeadObjectFailure)); + EXPECT_CALL(*kMockDevice, ping()) + .Times(1) + .WillRepeatedly(InvokeWithoutArgs(makeDeadObjectFailure)); + EXPECT_CALL(*kMockMakeDevice, Call(/*blocking=*/false)) + .Times(1) + .WillOnce(testing::Return(nullptr)); + + // run test + const ModelFactory makeModel = [] { return V1_3::Model{}; }; + const auto [resultCode, preparedModel] = kDevice->prepareModel(makeModel, {}, {}, {}, {}, {}); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_DEAD_OBJECT, resultCode); + EXPECT_EQ(nullptr, preparedModel.get()); +} + +TEST_F(VersionedIDeviceMockTest, prepareModelAsyncCrash) { + // setup failure + const auto ret = [this]() -> Return<V1_3::ErrorStatus> { + kMockDevice->simulateCrash(); + return V1_3::ErrorStatus::NONE; + }; + EXPECT_CALL(*kMockDevice, prepareModel_1_3(_, _, _, _, _, _, _, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(ret)); + + // run test + const ModelFactory makeModel = [] { return V1_3::Model{}; }; + const auto [resultCode, preparedModel] = kDevice->prepareModel(makeModel, {}, {}, {}, {}, {}); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_DEAD_OBJECT, resultCode); + EXPECT_EQ(nullptr, preparedModel.get()); +} + +TEST_F(VersionedIDeviceMockTest, waitCrash) { + // setup failure + EXPECT_CALL(*kMockDevice, ping()) + .Times(1) + .WillRepeatedly(InvokeWithoutArgs(makeDeadObjectFailure)); + EXPECT_CALL(*kMockMakeDevice, Call(/*blocking=*/true)) + .Times(1) + .WillOnce(testing::Return(nullptr)); + + // run test + const auto resultCode = kDevice->wait(); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); +} + +TEST_F(VersionedIDeviceMockTest, waitRecoverCrash) { + // setup original device calls + EXPECT_CALL(*kMockDevice, ping()).Times(1).WillOnce(InvokeWithoutArgs(makeDeadObjectFailure)); + + // setup recovery call + const sp<MockDevice> mockRecoveredDevice = MockDevice::create(); + EXPECT_CALL(*kMockMakeDevice, Call(/*blocking=*/true)) + .Times(1) + .WillOnce(testing::Return(mockRecoveredDevice)); + + // setup recovered device calls + const auto ret = []() -> Return<bool> { return true; }; + EXPECT_CALL(*mockRecoveredDevice, linkToDeathRet()).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto resultCode = kDevice->wait(); + + // verify success + EXPECT_EQ(ANEURALNETWORKS_NO_ERROR, resultCode); +} + +TEST_F(VersionedIDeviceMockTest, waitFailedRecoverCrash) { + // setup original device calls + EXPECT_CALL(*kMockDevice, ping()).Times(1).WillOnce(InvokeWithoutArgs(makeDeadObjectFailure)); + + // setup recovery call + const sp<MockDevice> mockRecoveredDevice = MockDevice::create(); + EXPECT_CALL(*kMockMakeDevice, Call(/*blocking=*/true)) + .Times(1) + .WillOnce(testing::Return(mockRecoveredDevice)); + + // setup recovered device calls + EXPECT_CALL(*mockRecoveredDevice, linkToDeathRet()) + .Times(1) + .WillOnce(makeGeneralTransportFailure); + + // run test + const auto resultCode = kDevice->wait(); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); +} + +// Harness for VersionedIPreparedModel failures. + +class VersionedIPreparedModelInitializationTest : public VersionedIDeviceMockTest { + protected: + const sp<MockPreparedModel> kMockPreparedModel = MockPreparedModel::create(); +}; + +std::shared_ptr<VersionedIPreparedModel> makeVersionedIPreparedModelSuccessfulInitializationFrom( + const sp<MockDevice>& mockDevice, const sp<MockPreparedModel>& mockPreparedModel, + const VersionedIDevice& device) { + const auto retV1_0 = makePreparedModelReturn(V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::NONE, + mockPreparedModel); + const auto retV1_1 = makePreparedModel_1_1Return(V1_0::ErrorStatus::NONE, + V1_0::ErrorStatus::NONE, mockPreparedModel); + const auto retV1_2 = makePreparedModel_1_2Return(V1_0::ErrorStatus::NONE, + V1_0::ErrorStatus::NONE, mockPreparedModel); + const auto retV1_3 = makePreparedModel_1_3Return(V1_3::ErrorStatus::NONE, + V1_3::ErrorStatus::NONE, mockPreparedModel); + + ON_CALL(*mockDevice, prepareModel(_, _)).WillByDefault(Invoke(retV1_0)); + ON_CALL(*mockDevice, prepareModel_1_1(_, _, _)).WillByDefault(Invoke(retV1_1)); + ON_CALL(*mockDevice, prepareModel_1_2(_, _, _, _, _, _)).WillByDefault(Invoke(retV1_2)); + ON_CALL(*mockDevice, prepareModel_1_3(_, _, _, _, _, _, _, _)).WillByDefault(Invoke(retV1_3)); + + EXPECT_CALL(*mockDevice, prepareModel(_, _)).Times(testing::AnyNumber()); + EXPECT_CALL(*mockDevice, prepareModel_1_1(_, _, _)).Times(testing::AnyNumber()); + EXPECT_CALL(*mockDevice, prepareModel_1_2(_, _, _, _, _, _)).Times(testing::AnyNumber()); + EXPECT_CALL(*mockDevice, prepareModel_1_3(_, _, _, _, _, _, _, _)).Times(testing::AnyNumber()); + + const ModelFactory makeModel = [] { return V1_3::Model{}; }; + const auto [resultCode, preparedModel] = device.prepareModel(makeModel, {}, {}, {}, {}, {}); + + CHECK_EQ(ANEURALNETWORKS_NO_ERROR, resultCode); + CHECK(preparedModel != nullptr); + + return preparedModel; +} + +template <Version version> +class VersionedIPreparedModelTest : public VersionedIDeviceInitializedTest<version> { + using Base = VersionedIDeviceInitializedTest<version>; + + protected: + void SetUp() override { + VersionedIDeviceInitializedTest<version>::SetUp(); + ASSERT_NE(nullptr, kPreparedModel.get()); + } + + const sp<MockPreparedModel> kMockPreparedModel = MockPreparedModel::create(); + const std::shared_ptr<VersionedIPreparedModel> kPreparedModel = + makeVersionedIPreparedModelSuccessfulInitializationFrom( + Base::kMockDevice, kMockPreparedModel, *Base::kDevice); +}; + +class VersionedIPreparedModelV1_0Test : public VersionedIPreparedModelTest<Version::V1_0> {}; +class VersionedIPreparedModelV1_1Test : public VersionedIPreparedModelTest<Version::V1_1> {}; +class VersionedIPreparedModelV1_2Test : public VersionedIPreparedModelTest<Version::V1_2> {}; +class VersionedIPreparedModelV1_3Test : public VersionedIPreparedModelTest<Version::V1_3> {}; +class VersionedIPreparedModelMockTest : public VersionedIPreparedModelTest<Version::MOCK> {}; + +// Simulate initialization/link error + +TEST_F(VersionedIPreparedModelInitializationTest, linkToDeathTransportFailure) { + // setup failure + EXPECT_CALL(*kMockPreparedModel, linkToDeathRet()) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); + const auto ret = makePreparedModel_1_3Return(V1_3::ErrorStatus::NONE, V1_3::ErrorStatus::NONE, + kMockPreparedModel); + EXPECT_CALL(*kMockDevice, prepareModel_1_3(_, _, _, _, _, _, _, _)) + .Times(1) + .WillOnce(Invoke(ret)); + + // run test + const ModelFactory makeModel = [] { return V1_3::Model{}; }; + const auto [resultCode, preparedModel] = kDevice->prepareModel(makeModel, {}, {}, {}, {}, {}); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(nullptr, preparedModel.get()); +} + +TEST_F(VersionedIPreparedModelInitializationTest, linkToDeathDeadObject) { + // setup failure + EXPECT_CALL(*kMockPreparedModel, linkToDeathRet()) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure)); + const auto ret = makePreparedModel_1_3Return(V1_3::ErrorStatus::NONE, V1_3::ErrorStatus::NONE, + kMockPreparedModel); + EXPECT_CALL(*kMockDevice, prepareModel_1_3(_, _, _, _, _, _, _, _)) + .Times(1) + .WillOnce(Invoke(ret)); + + // run test + const ModelFactory makeModel = [] { return V1_3::Model{}; }; + const auto [resultCode, preparedModel] = kDevice->prepareModel(makeModel, {}, {}, {}, {}, {}); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_DEAD_OBJECT, resultCode); + EXPECT_EQ(nullptr, preparedModel.get()); +} + +TEST_F(VersionedIPreparedModelInitializationTest, linkToDeathReturnError) { + // setup failure + EXPECT_CALL(*kMockPreparedModel, linkToDeathRet()) + .Times(1) + .WillOnce(InvokeWithoutArgs([]() -> Return<bool> { return false; })); + const auto ret = makePreparedModel_1_3Return(V1_3::ErrorStatus::NONE, V1_3::ErrorStatus::NONE, + kMockPreparedModel); + EXPECT_CALL(*kMockDevice, prepareModel_1_3(_, _, _, _, _, _, _, _)) + .Times(1) + .WillOnce(Invoke(ret)); + + // run test + const ModelFactory makeModel = [] { return V1_3::Model{}; }; + const auto [resultCode, preparedModel] = kDevice->prepareModel(makeModel, {}, {}, {}, {}, {}); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(nullptr, preparedModel.get()); +} + +// Simulate successful test + +TEST_F(VersionedIPreparedModelV1_0Test, executeAsync) { + // setup call + const auto ret = makeExecuteReturn(V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::NONE); + EXPECT_CALL(*kMockPreparedModel, execute(_, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto [resultCode, outputShapes, timing] = + kPreparedModel->execute({}, {}, {}, {}, /*preferSynchronous=*/false); + + // verify success + EXPECT_EQ(ANEURALNETWORKS_NO_ERROR, resultCode); + EXPECT_EQ(0u, outputShapes.size()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_1Test, executeAsync) { + // setup call + const auto ret = makeExecuteReturn(V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::NONE); + EXPECT_CALL(*kMockPreparedModel, execute(_, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto [resultCode, outputShapes, timing] = + kPreparedModel->execute({}, {}, {}, {}, /*preferSynchronous=*/false); + + // verify success + EXPECT_EQ(ANEURALNETWORKS_NO_ERROR, resultCode); + EXPECT_EQ(0u, outputShapes.size()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_2Test, executeAsync) { + // setup call + const auto ret = + makeExecute_1_2Return(V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::NONE, {}, kNoTiming); + EXPECT_CALL(*kMockPreparedModel, execute_1_2(_, _, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto [resultCode, outputShapes, timing] = + kPreparedModel->execute({}, {}, {}, {}, /*preferSynchronous=*/false); + + // verify success + EXPECT_EQ(ANEURALNETWORKS_NO_ERROR, resultCode); + EXPECT_EQ(0u, outputShapes.size()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_3Test, executeAsync) { + // setup call + const auto ret = + makeExecute_1_3Return(V1_3::ErrorStatus::NONE, V1_3::ErrorStatus::NONE, {}, kNoTiming); + EXPECT_CALL(*kMockPreparedModel, execute_1_3(_, _, _, _, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto [resultCode, outputShapes, timing] = + kPreparedModel->execute({}, {}, {}, {}, /*preferSynchronous=*/false); + + // verify success + EXPECT_EQ(ANEURALNETWORKS_NO_ERROR, resultCode); + EXPECT_EQ(0u, outputShapes.size()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_0Test, executePreferSync) { + // setup call + const auto ret = makeExecuteReturn(V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::NONE); + EXPECT_CALL(*kMockPreparedModel, execute(_, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto [resultCode, outputShapes, timing] = + kPreparedModel->execute({}, {}, {}, {}, /*preferSynchronous=*/true); + + // verify success + EXPECT_EQ(ANEURALNETWORKS_NO_ERROR, resultCode); + EXPECT_EQ(0u, outputShapes.size()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_1Test, executePreferSync) { + // setup call + const auto ret = makeExecuteReturn(V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::NONE); + EXPECT_CALL(*kMockPreparedModel, execute(_, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto [resultCode, outputShapes, timing] = + kPreparedModel->execute({}, {}, {}, {}, /*preferSynchronous=*/true); + + // verify success + EXPECT_EQ(ANEURALNETWORKS_NO_ERROR, resultCode); + EXPECT_EQ(0u, outputShapes.size()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_2Test, executePreferSync) { + // setup call + const auto ret = makeExecuteSynchronouslyReturn(V1_0::ErrorStatus::NONE, {}, kNoTiming); + EXPECT_CALL(*kMockPreparedModel, executeSynchronously(_, _, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto [resultCode, outputShapes, timing] = + kPreparedModel->execute({}, {}, {}, {}, /*preferSynchronous=*/true); + + // verify success + EXPECT_EQ(ANEURALNETWORKS_NO_ERROR, resultCode); + EXPECT_EQ(0u, outputShapes.size()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_3Test, executePreferSync) { + // setup call + const auto ret = makeExecuteSynchronously_1_3Return(V1_3::ErrorStatus::NONE, {}, kNoTiming); + EXPECT_CALL(*kMockPreparedModel, executeSynchronously_1_3(_, _, _, _, _)) + .Times(1) + .WillOnce(Invoke(ret)); + + // run test + const auto [resultCode, outputShapes, timing] = + kPreparedModel->execute({}, {}, {}, {}, /*preferSynchronous=*/true); + + // verify success + EXPECT_EQ(ANEURALNETWORKS_NO_ERROR, resultCode); + EXPECT_EQ(0u, outputShapes.size()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_0Test, executeFenced) { + // setup call + const auto ret = makeExecuteReturn(V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::NONE); + EXPECT_CALL(*kMockPreparedModel, execute(_, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto [resultCode, syncFence, dispatchCallback, timing] = + kPreparedModel->executeFenced({}, {}, {}, {}, {}, {}); + + // verify success + EXPECT_EQ(ANEURALNETWORKS_NO_ERROR, resultCode); + EXPECT_EQ(nullptr, syncFence.getNativeHandle()); + EXPECT_EQ(nullptr, dispatchCallback.get()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_1Test, executeFenced) { + // setup call + const auto ret = makeExecuteReturn(V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::NONE); + EXPECT_CALL(*kMockPreparedModel, execute(_, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto [resultCode, syncFence, dispatchCallback, timing] = + kPreparedModel->executeFenced({}, {}, {}, {}, {}, {}); + + // verify success + EXPECT_EQ(ANEURALNETWORKS_NO_ERROR, resultCode); + EXPECT_EQ(nullptr, syncFence.getNativeHandle()); + EXPECT_EQ(nullptr, dispatchCallback.get()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_2Test, executeFenced) { + // setup call + const auto ret = makeExecuteSynchronouslyReturn(V1_0::ErrorStatus::NONE, {}, kNoTiming); + EXPECT_CALL(*kMockPreparedModel, executeSynchronously(_, _, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto [resultCode, syncFence, dispatchCallback, timing] = + kPreparedModel->executeFenced({}, {}, {}, {}, {}, {}); + + // verify success + EXPECT_EQ(ANEURALNETWORKS_NO_ERROR, resultCode); + EXPECT_EQ(nullptr, syncFence.getNativeHandle()); + EXPECT_EQ(nullptr, dispatchCallback.get()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_3Test, executeFenced) { + // setup call + auto memory = allocateSharedMemory(4); + hidl_handle fakeSyncFence(memory.handle()); + const sp<IFencedExecutionCallback> callback = new MockFencedExecutionCallback(); + const auto ret = makeExecuteFencedReturn(V1_3::ErrorStatus::NONE, fakeSyncFence, callback); + EXPECT_CALL(*kMockPreparedModel, executeFenced(_, _, _, _, _, _, _)) + .Times(1) + .WillOnce(Invoke(ret)); + + // run test + const auto [resultCode, syncFence, dispatchCallback, timing] = + kPreparedModel->executeFenced({}, {}, {}, {}, {}, {}); + + // verify success + EXPECT_EQ(ANEURALNETWORKS_NO_ERROR, resultCode); + EXPECT_NE(nullptr, syncFence.getNativeHandle()); + EXPECT_NE(nullptr, dispatchCallback.get()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_0Test, configureExecutionBurst) { + // run test + const auto executionBurstController = + kPreparedModel->configureExecutionBurst(/*preferPowerOverLatency=*/false); + + // verify success + EXPECT_EQ(nullptr, executionBurstController); +} + +TEST_F(VersionedIPreparedModelV1_1Test, configureExecutionBurst) { + // run test + const auto executionBurstController = + kPreparedModel->configureExecutionBurst(/*preferPowerOverLatency=*/false); + + // verify success + EXPECT_EQ(nullptr, executionBurstController); +} + +TEST_F(VersionedIPreparedModelV1_2Test, configureExecutionBurst) { + // setup call + const sp<MockBurstContext> burstContext = new MockBurstContext(); + const auto ret = makeConfigureExecutionBurst(V1_0::ErrorStatus::NONE, burstContext); + EXPECT_CALL(*kMockPreparedModel, configureExecutionBurst(_, _, _, _)) + .Times(1) + .WillOnce(Invoke(ret)); + + // run test + const auto executionBurstController = + kPreparedModel->configureExecutionBurst(/*preferPowerOverLatency=*/false); + + // verify success + EXPECT_NE(nullptr, executionBurstController); +} + +TEST_F(VersionedIPreparedModelV1_3Test, configureExecutionBurst) { + // setup call + const sp<MockBurstContext> burstContext = new MockBurstContext(); + const auto ret = makeConfigureExecutionBurst(V1_0::ErrorStatus::NONE, burstContext); + EXPECT_CALL(*kMockPreparedModel, configureExecutionBurst(_, _, _, _)) + .Times(1) + .WillOnce(Invoke(ret)); + + // run test + const auto executionBurstController = + kPreparedModel->configureExecutionBurst(/*preferPowerOverLatency=*/false); + + // verify success + EXPECT_NE(nullptr, executionBurstController); +} + +// Simulate general failure + +TEST_F(VersionedIPreparedModelV1_0Test, executeAsyncLaunchFailure) { + // setup failure + const auto ret = makeExecuteReturn(V1_0::ErrorStatus::GENERAL_FAILURE, V1_0::ErrorStatus::NONE); + EXPECT_CALL(*kMockPreparedModel, execute(_, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto [resultCode, outputShapes, timing] = + kPreparedModel->execute({}, {}, {}, {}, /*preferSynchronous=*/false); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(0u, outputShapes.size()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_1Test, executeAsyncLaunchFailure) { + // setup failure + const auto ret = makeExecuteReturn(V1_0::ErrorStatus::GENERAL_FAILURE, V1_0::ErrorStatus::NONE); + EXPECT_CALL(*kMockPreparedModel, execute(_, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto [resultCode, outputShapes, timing] = + kPreparedModel->execute({}, {}, {}, {}, /*preferSynchronous=*/false); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(0u, outputShapes.size()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_2Test, executeAsyncLaunchFailure) { + // setup failure + const auto ret = makeExecute_1_2Return(V1_0::ErrorStatus::GENERAL_FAILURE, + V1_0::ErrorStatus::NONE, {}, kNoTiming); + EXPECT_CALL(*kMockPreparedModel, execute_1_2(_, _, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto [resultCode, outputShapes, timing] = + kPreparedModel->execute({}, {}, {}, {}, /*preferSynchronous=*/false); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(0u, outputShapes.size()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_3Test, executeAsyncLaunchFailure) { + // setup failure + const auto ret = makeExecute_1_3Return(V1_3::ErrorStatus::GENERAL_FAILURE, + V1_3::ErrorStatus::NONE, {}, kNoTiming); + EXPECT_CALL(*kMockPreparedModel, execute_1_3(_, _, _, _, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto [resultCode, outputShapes, timing] = + kPreparedModel->execute({}, {}, {}, {}, /*preferSynchronous=*/false); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(0u, outputShapes.size()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_0Test, executeAsyncReturnFailure) { + // setup failure + const auto ret = makeExecuteReturn(V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::GENERAL_FAILURE); + EXPECT_CALL(*kMockPreparedModel, execute(_, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto [resultCode, outputShapes, timing] = + kPreparedModel->execute({}, {}, {}, {}, /*preferSynchronous=*/false); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(0u, outputShapes.size()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_1Test, executeAsyncReturnFailure) { + // setup failure + const auto ret = makeExecuteReturn(V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::GENERAL_FAILURE); + EXPECT_CALL(*kMockPreparedModel, execute(_, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto [resultCode, outputShapes, timing] = + kPreparedModel->execute({}, {}, {}, {}, /*preferSynchronous=*/false); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(0u, outputShapes.size()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_2Test, executeAsyncReturnFailure) { + // setup failure + const auto ret = makeExecute_1_2Return(V1_0::ErrorStatus::NONE, + V1_0::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming); + EXPECT_CALL(*kMockPreparedModel, execute_1_2(_, _, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto [resultCode, outputShapes, timing] = + kPreparedModel->execute({}, {}, {}, {}, /*preferSynchronous=*/false); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(0u, outputShapes.size()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_3Test, executeAsyncReturnFailure) { + // setup failure + const auto ret = makeExecute_1_3Return(V1_3::ErrorStatus::NONE, + V1_3::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming); + EXPECT_CALL(*kMockPreparedModel, execute_1_3(_, _, _, _, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto [resultCode, outputShapes, timing] = + kPreparedModel->execute({}, {}, {}, {}, /*preferSynchronous=*/false); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(0u, outputShapes.size()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_0Test, executePreferSyncFailure) { + // setup failure + const auto ret = makeExecuteReturn(V1_0::ErrorStatus::GENERAL_FAILURE, + V1_0::ErrorStatus::GENERAL_FAILURE); + EXPECT_CALL(*kMockPreparedModel, execute(_, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto [resultCode, outputShapes, timing] = + kPreparedModel->execute({}, {}, {}, {}, /*preferSynchronous=*/true); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(0u, outputShapes.size()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_1Test, executePreferSyncFailure) { + // setup failure + const auto ret = makeExecuteReturn(V1_0::ErrorStatus::GENERAL_FAILURE, + V1_0::ErrorStatus::GENERAL_FAILURE); + EXPECT_CALL(*kMockPreparedModel, execute(_, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto [resultCode, outputShapes, timing] = + kPreparedModel->execute({}, {}, {}, {}, /*preferSynchronous=*/true); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(0u, outputShapes.size()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_2Test, executePreferSyncFailure) { + // setup failure + const auto ret = + makeExecuteSynchronouslyReturn(V1_0::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming); + EXPECT_CALL(*kMockPreparedModel, executeSynchronously(_, _, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto [resultCode, outputShapes, timing] = + kPreparedModel->execute({}, {}, {}, {}, /*preferSynchronous=*/true); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(0u, outputShapes.size()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_3Test, executePreferSyncFailure) { + // setup failure + const auto ret = + makeExecuteSynchronously_1_3Return(V1_3::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming); + EXPECT_CALL(*kMockPreparedModel, executeSynchronously_1_3(_, _, _, _, _)) + .Times(1) + .WillOnce(Invoke(ret)); + + // run test + const auto [resultCode, outputShapes, timing] = + kPreparedModel->execute({}, {}, {}, {}, /*preferSynchronous=*/true); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(0u, outputShapes.size()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_0Test, executeFencedFailure) { + // setup failure + const auto ret = makeExecuteReturn(V1_0::ErrorStatus::GENERAL_FAILURE, + V1_0::ErrorStatus::GENERAL_FAILURE); + EXPECT_CALL(*kMockPreparedModel, execute(_, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto [resultCode, syncFence, dispatchCallback, timing] = + kPreparedModel->executeFenced({}, {}, {}, {}, {}, {}); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(nullptr, syncFence.getNativeHandle()); + EXPECT_EQ(nullptr, dispatchCallback.get()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_1Test, executeFencedFailure) { + // setup failure + const auto ret = makeExecuteReturn(V1_0::ErrorStatus::GENERAL_FAILURE, + V1_0::ErrorStatus::GENERAL_FAILURE); + EXPECT_CALL(*kMockPreparedModel, execute(_, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto [resultCode, syncFence, dispatchCallback, timing] = + kPreparedModel->executeFenced({}, {}, {}, {}, {}, {}); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(nullptr, syncFence.getNativeHandle()); + EXPECT_EQ(nullptr, dispatchCallback.get()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_2Test, executeFencedFailure) { + // setup failure + const auto ret = + makeExecuteSynchronouslyReturn(V1_0::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming); + EXPECT_CALL(*kMockPreparedModel, executeSynchronously(_, _, _)).Times(1).WillOnce(Invoke(ret)); + + // run test + const auto [resultCode, syncFence, dispatchCallback, timing] = + kPreparedModel->executeFenced({}, {}, {}, {}, {}, {}); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(nullptr, syncFence.getNativeHandle()); + EXPECT_EQ(nullptr, dispatchCallback.get()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_3Test, executeFencedFailure) { + // setup failure + auto memory = allocateSharedMemory(4); + hidl_handle fakeSyncFence(memory.handle()); + const sp<IFencedExecutionCallback> callback = new MockFencedExecutionCallback(); + const auto ret = + makeExecuteFencedReturn(V1_3::ErrorStatus::GENERAL_FAILURE, fakeSyncFence, callback); + EXPECT_CALL(*kMockPreparedModel, executeFenced(_, _, _, _, _, _, _)) + .Times(1) + .WillOnce(Invoke(ret)); + + // run test + const auto [resultCode, syncFence, dispatchCallback, timing] = + kPreparedModel->executeFenced({}, {}, {}, {}, {}, {}); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(nullptr, syncFence.getNativeHandle()); + EXPECT_EQ(nullptr, dispatchCallback.get()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_2Test, configureExecutionBurstFailure) { + // setup failure + const sp<MockBurstContext> burstContext = new MockBurstContext(); + const auto ret = makeConfigureExecutionBurst(V1_0::ErrorStatus::GENERAL_FAILURE, burstContext); + EXPECT_CALL(*kMockPreparedModel, configureExecutionBurst(_, _, _, _)) + .Times(1) + .WillOnce(Invoke(ret)); + + // run test + const auto executionBurstController = + kPreparedModel->configureExecutionBurst(/*preferPowerOverLatency=*/false); + + // verify failure + EXPECT_EQ(nullptr, executionBurstController); +} + +TEST_F(VersionedIPreparedModelV1_3Test, configureExecutionBurstFailure) { + // setup failure + const sp<MockBurstContext> burstContext = new MockBurstContext(); + const auto ret = makeConfigureExecutionBurst(V1_0::ErrorStatus::GENERAL_FAILURE, burstContext); + EXPECT_CALL(*kMockPreparedModel, configureExecutionBurst(_, _, _, _)) + .Times(1) + .WillOnce(Invoke(ret)); + + // run test + const auto executionBurstController = + kPreparedModel->configureExecutionBurst(/*preferPowerOverLatency=*/false); + + // verify failure + EXPECT_EQ(nullptr, executionBurstController); +} + +TEST_F(VersionedIPreparedModelV1_2Test, configureExecutionBurstNullptrError) { + // setup failure + const auto ret = makeConfigureExecutionBurst(V1_0::ErrorStatus::NONE, nullptr); + EXPECT_CALL(*kMockPreparedModel, configureExecutionBurst(_, _, _, _)) + .Times(1) + .WillOnce(Invoke(ret)); + + // run test + const auto executionBurstController = + kPreparedModel->configureExecutionBurst(/*preferPowerOverLatency=*/false); + + // verify failure + EXPECT_EQ(nullptr, executionBurstController); +} + +TEST_F(VersionedIPreparedModelV1_3Test, configureExecutionBurstNullptrError) { + // setup failure + const auto ret = makeConfigureExecutionBurst(V1_0::ErrorStatus::NONE, nullptr); + EXPECT_CALL(*kMockPreparedModel, configureExecutionBurst(_, _, _, _)) + .Times(1) + .WillOnce(Invoke(ret)); + + // run test + const auto executionBurstController = + kPreparedModel->configureExecutionBurst(/*preferPowerOverLatency=*/false); + + // verify failure + EXPECT_EQ(nullptr, executionBurstController); +} + +// Simulate transport failure + +TEST_F(VersionedIPreparedModelV1_0Test, executeAsyncTransportFailure) { + // setup failure + EXPECT_CALL(*kMockPreparedModel, execute(_, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); + + // run test + const auto [resultCode, outputShapes, timing] = + kPreparedModel->execute({}, {}, {}, {}, /*preferSynchronous=*/false); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(0u, outputShapes.size()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_1Test, executeAsyncTransportFailure) { + // setup failure + EXPECT_CALL(*kMockPreparedModel, execute(_, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); + + // run test + const auto [resultCode, outputShapes, timing] = + kPreparedModel->execute({}, {}, {}, {}, /*preferSynchronous=*/false); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(0u, outputShapes.size()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_2Test, executeAsyncTransportFailure) { + // setup failure + EXPECT_CALL(*kMockPreparedModel, execute_1_2(_, _, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); + + // run test + const auto [resultCode, outputShapes, timing] = + kPreparedModel->execute({}, {}, {}, {}, /*preferSynchronous=*/false); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(0u, outputShapes.size()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_3Test, executeAsyncTransportFailure) { + // setup failure + EXPECT_CALL(*kMockPreparedModel, execute_1_3(_, _, _, _, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); + + // run test + const auto [resultCode, outputShapes, timing] = + kPreparedModel->execute({}, {}, {}, {}, /*preferSynchronous=*/false); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(0u, outputShapes.size()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_0Test, executePreferSyncTransportFailure) { + // setup failure + EXPECT_CALL(*kMockPreparedModel, execute(_, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); + + // run test + const auto [resultCode, outputShapes, timing] = + kPreparedModel->execute({}, {}, {}, {}, /*preferSynchronous=*/true); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(0u, outputShapes.size()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_1Test, executePreferSyncTransportFailure) { + // setup failure + EXPECT_CALL(*kMockPreparedModel, execute(_, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); + + // run test + const auto [resultCode, outputShapes, timing] = + kPreparedModel->execute({}, {}, {}, {}, /*preferSynchronous=*/true); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(0u, outputShapes.size()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_2Test, executePreferSyncTransportFailure) { + // setup failure + EXPECT_CALL(*kMockPreparedModel, executeSynchronously(_, _, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); + + // run test + const auto [resultCode, outputShapes, timing] = + kPreparedModel->execute({}, {}, {}, {}, /*preferSynchronous=*/true); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(0u, outputShapes.size()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_3Test, executePreferSyncTransportFailure) { + // setup failure + EXPECT_CALL(*kMockPreparedModel, executeSynchronously_1_3(_, _, _, _, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); + + // run test + const auto [resultCode, outputShapes, timing] = + kPreparedModel->execute({}, {}, {}, {}, /*preferSynchronous=*/true); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(0u, outputShapes.size()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_0Test, executeFencedTransportFailure) { + // setup failure + EXPECT_CALL(*kMockPreparedModel, execute(_, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); + + // run test + const auto [resultCode, syncFence, dispatchCallback, timing] = + kPreparedModel->executeFenced({}, {}, {}, {}, {}, {}); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(nullptr, syncFence.getNativeHandle()); + EXPECT_EQ(nullptr, dispatchCallback.get()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_1Test, executeFencedTransportFailure) { + // setup failure + EXPECT_CALL(*kMockPreparedModel, execute(_, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); + + // run test + const auto [resultCode, syncFence, dispatchCallback, timing] = + kPreparedModel->executeFenced({}, {}, {}, {}, {}, {}); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(nullptr, syncFence.getNativeHandle()); + EXPECT_EQ(nullptr, dispatchCallback.get()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_2Test, executeFencedTransportFailure) { + // setup failure + EXPECT_CALL(*kMockPreparedModel, executeSynchronously(_, _, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); + + // run test + const auto [resultCode, syncFence, dispatchCallback, timing] = + kPreparedModel->executeFenced({}, {}, {}, {}, {}, {}); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(nullptr, syncFence.getNativeHandle()); + EXPECT_EQ(nullptr, dispatchCallback.get()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_3Test, executeFencedTransportFailure) { + // setup failure + EXPECT_CALL(*kMockPreparedModel, executeFenced(_, _, _, _, _, _, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); + + // run test + const auto [resultCode, syncFence, dispatchCallback, timing] = + kPreparedModel->executeFenced({}, {}, {}, {}, {}, {}); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_OP_FAILED, resultCode); + EXPECT_EQ(nullptr, syncFence.getNativeHandle()); + EXPECT_EQ(nullptr, dispatchCallback.get()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_2Test, configureExecutionBurstTransportFailure) { + // setup failure + EXPECT_CALL(*kMockPreparedModel, configureExecutionBurst(_, _, _, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); + + // run test + const auto executionBurstController = + kPreparedModel->configureExecutionBurst(/*preferPowerOverLatency=*/false); + + // verify failure + EXPECT_EQ(nullptr, executionBurstController); +} + +TEST_F(VersionedIPreparedModelV1_3Test, configureExecutionBurstTransportFailure) { + // setup failure + EXPECT_CALL(*kMockPreparedModel, configureExecutionBurst(_, _, _, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); + + // run test + const auto executionBurstController = + kPreparedModel->configureExecutionBurst(/*preferPowerOverLatency=*/false); + + // verify failure + EXPECT_EQ(nullptr, executionBurstController); +} + +// Simulate service crash + +TEST_F(VersionedIPreparedModelV1_0Test, executeAsyncLaunchCrash) { + // setup failure + EXPECT_CALL(*kMockPreparedModel, execute(_, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure)); + + // run test + const auto [resultCode, outputShapes, timing] = + kPreparedModel->execute({}, {}, {}, {}, /*preferSynchronous=*/false); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_DEAD_OBJECT, resultCode); + EXPECT_EQ(0u, outputShapes.size()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_1Test, executeAsyncLaunchCrash) { + // setup failure + EXPECT_CALL(*kMockPreparedModel, execute(_, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure)); + + // run test + const auto [resultCode, outputShapes, timing] = + kPreparedModel->execute({}, {}, {}, {}, /*preferSynchronous=*/false); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_DEAD_OBJECT, resultCode); + EXPECT_EQ(0u, outputShapes.size()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_2Test, executeAsyncLaunchCrash) { + // setup failure + EXPECT_CALL(*kMockPreparedModel, execute_1_2(_, _, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure)); + + // run test + const auto [resultCode, outputShapes, timing] = + kPreparedModel->execute({}, {}, {}, {}, /*preferSynchronous=*/false); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_DEAD_OBJECT, resultCode); + EXPECT_EQ(0u, outputShapes.size()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_3Test, executeAsyncLaunchCrash) { + // setup failure + EXPECT_CALL(*kMockPreparedModel, execute_1_3(_, _, _, _, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure)); + + // run test + const auto [resultCode, outputShapes, timing] = + kPreparedModel->execute({}, {}, {}, {}, /*preferSynchronous=*/false); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_DEAD_OBJECT, resultCode); + EXPECT_EQ(0u, outputShapes.size()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_2Test, executePreferSyncCrash) { + // setup failure + EXPECT_CALL(*kMockPreparedModel, executeSynchronously(_, _, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure)); + + // run test + const auto [resultCode, outputShapes, timing] = + kPreparedModel->execute({}, {}, {}, {}, /*preferSynchronous=*/true); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_DEAD_OBJECT, resultCode); + EXPECT_EQ(0u, outputShapes.size()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelV1_3Test, executePreferSyncCrash) { + // setup failure + EXPECT_CALL(*kMockPreparedModel, executeSynchronously_1_3(_, _, _, _, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure)); + + // run test + const auto [resultCode, outputShapes, timing] = + kPreparedModel->execute({}, {}, {}, {}, /*preferSynchronous=*/true); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_DEAD_OBJECT, resultCode); + EXPECT_EQ(0u, outputShapes.size()); + EXPECT_EQ(kNoTiming, timing); +} + +TEST_F(VersionedIPreparedModelMockTest, executeAsyncReturnCrash) { + // setup failure + const auto ret = [this]() -> Return<V1_3::ErrorStatus> { + kMockPreparedModel->simulateCrash(); + return V1_3::ErrorStatus::NONE; + }; + EXPECT_CALL(*kMockPreparedModel, execute_1_3(_, _, _, _, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(ret)); + + // run test + const auto [resultCode, outputShapes, timing] = + kPreparedModel->execute({}, {}, {}, {}, /*preferSynchronous=*/false); + + // verify failure + EXPECT_EQ(ANEURALNETWORKS_DEAD_OBJECT, resultCode); + EXPECT_EQ(0u, outputShapes.size()); + EXPECT_EQ(kNoTiming, timing); +} + +} // namespace +} // namespace android::nn |