blob: 3abd724c8c19fc1436fc8b285d71a77833c53fb1 [file] [log] [blame]
/*
* 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 <gmock/gmock.h>
#include <nnapi/TypeUtils.h>
#include <nnapi/Types.h>
#include <nnapi/hal/ResilientDevice.h>
#include <tuple>
#include <utility>
#include "MockBuffer.h"
#include "MockDevice.h"
#include "MockPreparedModel.h"
namespace android::hardware::neuralnetworks::utils {
namespace {
using ::testing::_;
using ::testing::InvokeWithoutArgs;
using ::testing::Return;
using SharedMockDevice = std::shared_ptr<const nn::MockDevice>;
using MockDeviceFactory = ::testing::MockFunction<nn::GeneralResult<nn::SharedDevice>(bool)>;
const std::string kName = "Google-MockV1";
const std::string kVersionString = "version1";
const auto kExtensions = std::vector<nn::Extension>{};
constexpr auto kNoInfo = std::numeric_limits<float>::max();
constexpr auto kNoPerformanceInfo =
nn::Capabilities::PerformanceInfo{.execTime = kNoInfo, .powerUsage = kNoInfo};
const auto kCapabilities = nn::Capabilities{
.relaxedFloat32toFloat16PerformanceScalar = kNoPerformanceInfo,
.relaxedFloat32toFloat16PerformanceTensor = kNoPerformanceInfo,
.operandPerformance = nn::Capabilities::OperandPerformanceTable::create({}).value(),
.ifPerformance = kNoPerformanceInfo,
.whilePerformance = kNoPerformanceInfo};
constexpr auto kNumberOfCacheFilesNeeded = std::pair<uint32_t, uint32_t>(5, 3);
SharedMockDevice createConfiguredMockDevice() {
auto mockDevice = std::make_shared<const nn::MockDevice>();
// Setup default actions for each relevant call.
constexpr auto getName_ret = []() -> const std::string& { return kName; };
constexpr auto getVersionString_ret = []() -> const std::string& { return kVersionString; };
constexpr auto kFeatureLevel = nn::Version::ANDROID_OC_MR1;
constexpr auto kDeviceType = nn::DeviceType::ACCELERATOR;
constexpr auto getSupportedExtensions_ret = []() -> const std::vector<nn::Extension>& {
return kExtensions;
};
constexpr auto getCapabilities_ret = []() -> const nn::Capabilities& { return kCapabilities; };
// Setup default actions for each relevant call.
ON_CALL(*mockDevice, getName()).WillByDefault(getName_ret);
ON_CALL(*mockDevice, getVersionString()).WillByDefault(getVersionString_ret);
ON_CALL(*mockDevice, getFeatureLevel()).WillByDefault(Return(kFeatureLevel));
ON_CALL(*mockDevice, getType()).WillByDefault(Return(kDeviceType));
ON_CALL(*mockDevice, getSupportedExtensions()).WillByDefault(getSupportedExtensions_ret);
ON_CALL(*mockDevice, getCapabilities()).WillByDefault(getCapabilities_ret);
ON_CALL(*mockDevice, getNumberOfCacheFilesNeeded())
.WillByDefault(Return(kNumberOfCacheFilesNeeded));
// These EXPECT_CALL(...).Times(testing::AnyNumber()) calls are to suppress warnings on the
// uninteresting methods calls.
EXPECT_CALL(*mockDevice, getName()).Times(testing::AnyNumber());
EXPECT_CALL(*mockDevice, getVersionString()).Times(testing::AnyNumber());
EXPECT_CALL(*mockDevice, getFeatureLevel()).Times(testing::AnyNumber());
EXPECT_CALL(*mockDevice, getType()).Times(testing::AnyNumber());
EXPECT_CALL(*mockDevice, getSupportedExtensions()).Times(testing::AnyNumber());
EXPECT_CALL(*mockDevice, getCapabilities()).Times(testing::AnyNumber());
EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded()).Times(testing::AnyNumber());
return mockDevice;
}
std::tuple<SharedMockDevice, std::unique_ptr<MockDeviceFactory>,
std::shared_ptr<const ResilientDevice>>
setup() {
auto mockDevice = createConfiguredMockDevice();
auto mockDeviceFactory = std::make_unique<MockDeviceFactory>();
EXPECT_CALL(*mockDeviceFactory, Call(true)).Times(1).WillOnce(Return(mockDevice));
auto device = ResilientDevice::create(mockDeviceFactory->AsStdFunction()).value();
return std::make_tuple(std::move(mockDevice), std::move(mockDeviceFactory), std::move(device));
}
constexpr auto makeError = [](nn::ErrorStatus status) {
return [status](const auto&... /*args*/) { return nn::error(status); };
};
const auto kReturnGeneralFailure = makeError(nn::ErrorStatus::GENERAL_FAILURE);
const auto kReturnDeadObject = makeError(nn::ErrorStatus::DEAD_OBJECT);
} // namespace
TEST(ResilientDeviceTest, invalidDeviceFactory) {
// setup call
const auto invalidDeviceFactory = ResilientDevice::Factory{};
// run test
const auto result = ResilientDevice::create(invalidDeviceFactory);
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::INVALID_ARGUMENT);
}
TEST(ResilientDeviceTest, preparedModelFactoryFailure) {
// setup call
const auto invalidDeviceFactory = kReturnGeneralFailure;
// run test
const auto result = ResilientDevice::create(invalidDeviceFactory);
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
TEST(ResilientDeviceTest, cachedData) {
// setup call
const auto [mockDevice, mockDeviceFactory, device] = setup();
// run test and verify results
EXPECT_EQ(device->getName(), kName);
EXPECT_EQ(device->getVersionString(), kVersionString);
EXPECT_EQ(device->getSupportedExtensions(), kExtensions);
EXPECT_EQ(device->getCapabilities(), kCapabilities);
}
TEST(ResilientDeviceTest, getFeatureLevel) {
// setup call
const auto [mockDevice, mockDeviceFactory, device] = setup();
constexpr auto kFeatureLevel = nn::Version::ANDROID_OC_MR1;
EXPECT_CALL(*mockDevice, getFeatureLevel()).Times(1).WillOnce(Return(kFeatureLevel));
// run test
const auto featureLevel = device->getFeatureLevel();
// verify results
EXPECT_EQ(featureLevel, kFeatureLevel);
}
TEST(ResilientDeviceTest, getType) {
// setup call
const auto [mockDevice, mockDeviceFactory, device] = setup();
constexpr auto kDeviceType = nn::DeviceType::ACCELERATOR;
EXPECT_CALL(*mockDevice, getType()).Times(1).WillOnce(Return(kDeviceType));
// run test
const auto type = device->getType();
// verify results
EXPECT_EQ(type, kDeviceType);
}
TEST(ResilientDeviceTest, getNumberOfCacheFilesNeeded) {
// setup call
const auto [mockDevice, mockDeviceFactory, device] = setup();
EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded())
.Times(1)
.WillOnce(Return(kNumberOfCacheFilesNeeded));
// run test
const auto numberOfCacheFilesNeeded = device->getNumberOfCacheFilesNeeded();
// verify results
EXPECT_EQ(numberOfCacheFilesNeeded, kNumberOfCacheFilesNeeded);
}
TEST(ResilientDeviceTest, getDevice) {
// setup call
const auto [mockDevice, mockDeviceFactory, device] = setup();
// run test
const auto result = device->getDevice();
// verify result
EXPECT_TRUE(result == mockDevice);
}
TEST(ResilientDeviceTest, wait) {
// setup call
const auto [mockDevice, mockDeviceFactory, device] = setup();
EXPECT_CALL(*mockDevice, wait()).Times(1).WillOnce(Return(nn::GeneralResult<void>{}));
// run test
const auto result = device->wait();
// verify result
ASSERT_TRUE(result.has_value())
<< "Failed with " << result.error().code << ": " << result.error().message;
}
TEST(ResilientDeviceTest, waitError) {
// setup call
const auto [mockDevice, mockDeviceFactory, device] = setup();
EXPECT_CALL(*mockDevice, wait()).Times(1).WillOnce(kReturnGeneralFailure);
// run test
const auto result = device->wait();
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
TEST(ResilientDeviceTest, waitDeadObjectFailedRecovery) {
// setup call
const auto [mockDevice, mockDeviceFactory, device] = setup();
EXPECT_CALL(*mockDevice, wait()).Times(1).WillOnce(kReturnDeadObject);
EXPECT_CALL(*mockDeviceFactory, Call(true)).Times(1).WillOnce(kReturnGeneralFailure);
// run test
const auto result = device->wait();
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
}
TEST(ResilientDeviceTest, waitDeadObjectSuccessfulRecovery) {
// setup call
const auto [mockDevice, mockDeviceFactory, device] = setup();
EXPECT_CALL(*mockDevice, wait()).Times(1).WillOnce(kReturnDeadObject);
const auto recoveredMockDevice = createConfiguredMockDevice();
EXPECT_CALL(*recoveredMockDevice, wait()).Times(1).WillOnce(Return(nn::GeneralResult<void>{}));
EXPECT_CALL(*mockDeviceFactory, Call(true)).Times(1).WillOnce(Return(recoveredMockDevice));
// run test
const auto result = device->wait();
// verify result
ASSERT_TRUE(result.has_value())
<< "Failed with " << result.error().code << ": " << result.error().message;
}
TEST(ResilientDeviceTest, getSupportedOperations) {
// setup call
const auto [mockDevice, mockDeviceFactory, device] = setup();
EXPECT_CALL(*mockDevice, getSupportedOperations(_))
.Times(1)
.WillOnce(Return(nn::GeneralResult<std::vector<bool>>{}));
// run test
const auto result = device->getSupportedOperations({});
// verify result
ASSERT_TRUE(result.has_value())
<< "Failed with " << result.error().code << ": " << result.error().message;
}
TEST(ResilientDeviceTest, getSupportedOperationsError) {
// setup call
const auto [mockDevice, mockDeviceFactory, device] = setup();
EXPECT_CALL(*mockDevice, getSupportedOperations(_)).Times(1).WillOnce(kReturnGeneralFailure);
// run test
const auto result = device->getSupportedOperations({});
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
TEST(ResilientDeviceTest, getSupportedOperationsDeadObjectFailedRecovery) {
// setup call
const auto [mockDevice, mockDeviceFactory, device] = setup();
EXPECT_CALL(*mockDevice, getSupportedOperations(_)).Times(1).WillOnce(kReturnDeadObject);
EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(kReturnGeneralFailure);
// run test
const auto result = device->getSupportedOperations({});
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
}
TEST(ResilientDeviceTest, getSupportedOperationsDeadObjectSuccessfulRecovery) {
// setup call
const auto [mockDevice, mockDeviceFactory, device] = setup();
EXPECT_CALL(*mockDevice, getSupportedOperations(_)).Times(1).WillOnce(kReturnDeadObject);
const auto recoveredMockDevice = createConfiguredMockDevice();
EXPECT_CALL(*recoveredMockDevice, getSupportedOperations(_))
.Times(1)
.WillOnce(Return(nn::GeneralResult<std::vector<bool>>{}));
EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
// run test
const auto result = device->getSupportedOperations({});
// verify result
ASSERT_TRUE(result.has_value())
<< "Failed with " << result.error().code << ": " << result.error().message;
}
TEST(ResilientDeviceTest, prepareModel) {
// setup call
const auto [mockDevice, mockDeviceFactory, device] = setup();
const auto mockPreparedModel = std::make_shared<const nn::MockPreparedModel>();
EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _))
.Times(1)
.WillOnce(Return(mockPreparedModel));
// run test
const auto result = device->prepareModel({}, {}, {}, {}, {}, {}, {});
// verify result
ASSERT_TRUE(result.has_value())
<< "Failed with " << result.error().code << ": " << result.error().message;
}
TEST(ResilientDeviceTest, prepareModelError) {
// setup call
const auto [mockDevice, mockDeviceFactory, device] = setup();
EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _))
.Times(1)
.WillOnce(kReturnGeneralFailure);
// run test
const auto result = device->prepareModel({}, {}, {}, {}, {}, {}, {});
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
TEST(ResilientDeviceTest, prepareModelDeadObjectFailedRecovery) {
// setup call
const auto [mockDevice, mockDeviceFactory, device] = setup();
EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _))
.Times(1)
.WillOnce(kReturnDeadObject);
EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(kReturnGeneralFailure);
// run test
const auto result = device->prepareModel({}, {}, {}, {}, {}, {}, {});
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
}
TEST(ResilientDeviceTest, prepareModelDeadObjectSuccessfulRecovery) {
// setup call
const auto [mockDevice, mockDeviceFactory, device] = setup();
EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _))
.Times(1)
.WillOnce(kReturnDeadObject);
const auto recoveredMockDevice = createConfiguredMockDevice();
const auto mockPreparedModel = std::make_shared<const nn::MockPreparedModel>();
EXPECT_CALL(*recoveredMockDevice, prepareModel(_, _, _, _, _, _, _))
.Times(1)
.WillOnce(Return(mockPreparedModel));
EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
// run test
const auto result = device->prepareModel({}, {}, {}, {}, {}, {}, {});
// verify result
ASSERT_TRUE(result.has_value())
<< "Failed with " << result.error().code << ": " << result.error().message;
}
TEST(ResilientDeviceTest, prepareModelFromCache) {
// setup call
const auto [mockDevice, mockDeviceFactory, device] = setup();
const auto mockPreparedModel = std::make_shared<const nn::MockPreparedModel>();
EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _))
.Times(1)
.WillOnce(Return(mockPreparedModel));
// run test
const auto result = device->prepareModelFromCache({}, {}, {}, {});
// verify result
ASSERT_TRUE(result.has_value())
<< "Failed with " << result.error().code << ": " << result.error().message;
}
TEST(ResilientDeviceTest, prepareModelFromCacheError) {
// setup call
const auto [mockDevice, mockDeviceFactory, device] = setup();
EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _))
.Times(1)
.WillOnce(kReturnGeneralFailure);
// run test
const auto result = device->prepareModelFromCache({}, {}, {}, {});
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
TEST(ResilientDeviceTest, prepareModelFromCacheDeadObjectFailedRecovery) {
// setup call
const auto [mockDevice, mockDeviceFactory, device] = setup();
EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _))
.Times(1)
.WillOnce(kReturnDeadObject);
EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(kReturnGeneralFailure);
// run test
const auto result = device->prepareModelFromCache({}, {}, {}, {});
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
}
TEST(ResilientDeviceTest, prepareModelFromCacheDeadObjectSuccessfulRecovery) {
// setup call
const auto [mockDevice, mockDeviceFactory, device] = setup();
EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _))
.Times(1)
.WillOnce(kReturnDeadObject);
const auto recoveredMockDevice = createConfiguredMockDevice();
const auto mockPreparedModel = std::make_shared<const nn::MockPreparedModel>();
EXPECT_CALL(*recoveredMockDevice, prepareModelFromCache(_, _, _, _))
.Times(1)
.WillOnce(Return(mockPreparedModel));
EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
// run test
const auto result = device->prepareModelFromCache({}, {}, {}, {});
// verify result
ASSERT_TRUE(result.has_value())
<< "Failed with " << result.error().code << ": " << result.error().message;
}
TEST(ResilientDeviceTest, allocate) {
// setup call
const auto [mockDevice, mockDeviceFactory, device] = setup();
const auto mockBuffer = std::make_shared<const nn::MockBuffer>();
EXPECT_CALL(*mockDevice, allocate(_, _, _, _)).Times(1).WillOnce(Return(mockBuffer));
// run test
const auto result = device->allocate({}, {}, {}, {});
// verify result
ASSERT_TRUE(result.has_value())
<< "Failed with " << result.error().code << ": " << result.error().message;
}
TEST(ResilientDeviceTest, allocateError) {
// setup call
const auto [mockDevice, mockDeviceFactory, device] = setup();
EXPECT_CALL(*mockDevice, allocate(_, _, _, _)).Times(1).WillOnce(kReturnGeneralFailure);
// run test
const auto result = device->allocate({}, {}, {}, {});
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
TEST(ResilientDeviceTest, allocateDeadObjectFailedRecovery) {
// setup call
const auto [mockDevice, mockDeviceFactory, device] = setup();
EXPECT_CALL(*mockDevice, allocate(_, _, _, _)).Times(1).WillOnce(kReturnDeadObject);
EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(kReturnGeneralFailure);
// run test
const auto result = device->allocate({}, {}, {}, {});
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
}
TEST(ResilientDeviceTest, allocateDeadObjectSuccessfulRecovery) {
// setup call
const auto [mockDevice, mockDeviceFactory, device] = setup();
EXPECT_CALL(*mockDevice, allocate(_, _, _, _)).Times(1).WillOnce(kReturnDeadObject);
const auto recoveredMockDevice = createConfiguredMockDevice();
const auto mockBuffer = std::make_shared<const nn::MockBuffer>();
EXPECT_CALL(*recoveredMockDevice, allocate(_, _, _, _)).Times(1).WillOnce(Return(mockBuffer));
EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
// run test
const auto result = device->allocate({}, {}, {}, {});
// verify result
ASSERT_TRUE(result.has_value())
<< "Failed with " << result.error().code << ": " << result.error().message;
}
TEST(ResilientDeviceTest, recover) {
// setup call
const auto [mockDevice, mockDeviceFactory, device] = setup();
const auto recoveredMockDevice = createConfiguredMockDevice();
EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
// run test
const auto result = device->recover(mockDevice.get(), /*blocking=*/false);
// verify result
ASSERT_TRUE(result.has_value())
<< "Failed with " << result.error().code << ": " << result.error().message;
EXPECT_TRUE(result.value() == recoveredMockDevice);
}
TEST(ResilientDeviceTest, recoverFailure) {
// setup call
const auto [mockDevice, mockDeviceFactory, device] = setup();
const auto recoveredMockDevice = createConfiguredMockDevice();
EXPECT_CALL(*mockDeviceFactory, Call(_)).Times(1).WillOnce(kReturnGeneralFailure);
// run test
const auto result = device->recover(mockDevice.get(), /*blocking=*/false);
// verify result
EXPECT_FALSE(result.has_value());
}
TEST(ResilientDeviceTest, someoneElseRecovered) {
// setup call
const auto [mockDevice, mockDeviceFactory, device] = setup();
const auto recoveredMockDevice = createConfiguredMockDevice();
EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
device->recover(mockDevice.get(), /*blocking=*/false);
// run test
const auto result = device->recover(mockDevice.get(), /*blocking=*/false);
// verify result
ASSERT_TRUE(result.has_value())
<< "Failed with " << result.error().code << ": " << result.error().message;
EXPECT_TRUE(result.value() == recoveredMockDevice);
}
TEST(ResilientDeviceTest, recoverCacheMismatchGetName) {
// setup call
const auto [mockDevice, mockDeviceFactory, device] = setup();
const auto recoveredMockDevice = createConfiguredMockDevice();
const std::string kDifferentName = "Google-DifferentName";
const auto ret = [&kDifferentName]() -> const std::string& { return kDifferentName; };
EXPECT_CALL(*recoveredMockDevice, getName()).Times(1).WillOnce(ret);
EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
// run test
const auto result = device->recover(mockDevice.get(), /*blocking=*/false);
// verify result
ASSERT_TRUE(result.has_value())
<< "Failed with " << result.error().code << ": " << result.error().message;
EXPECT_TRUE(result.value() != nullptr);
EXPECT_TRUE(result.value() != mockDevice);
EXPECT_TRUE(result.value() != recoveredMockDevice);
}
TEST(ResilientDeviceTest, recoverCacheMismatchGetVersionString) {
// setup call
const auto [mockDevice, mockDeviceFactory, device] = setup();
const auto recoveredMockDevice = createConfiguredMockDevice();
const std::string kDifferentVersionString = "differentversion";
const auto ret = [&kDifferentVersionString]() -> const std::string& {
return kDifferentVersionString;
};
EXPECT_CALL(*recoveredMockDevice, getVersionString()).Times(1).WillOnce(ret);
EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
// run test
const auto result = device->recover(mockDevice.get(), /*blocking=*/false);
// verify result
ASSERT_TRUE(result.has_value())
<< "Failed with " << result.error().code << ": " << result.error().message;
EXPECT_TRUE(result.value() != nullptr);
EXPECT_TRUE(result.value() != mockDevice);
EXPECT_TRUE(result.value() != recoveredMockDevice);
}
TEST(ResilientDeviceTest, recoverCacheMismatchGetFeatureLevel) {
// setup call
const auto [mockDevice, mockDeviceFactory, device] = setup();
const auto recoveredMockDevice = createConfiguredMockDevice();
EXPECT_CALL(*recoveredMockDevice, getFeatureLevel())
.Times(1)
.WillOnce(Return(nn::Version::ANDROID_P));
EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
// run test
const auto result = device->recover(mockDevice.get(), /*blocking=*/false);
// verify result
ASSERT_TRUE(result.has_value())
<< "Failed with " << result.error().code << ": " << result.error().message;
EXPECT_TRUE(result.value() != nullptr);
EXPECT_TRUE(result.value() != mockDevice);
EXPECT_TRUE(result.value() != recoveredMockDevice);
}
TEST(ResilientDeviceTest, recoverCacheMismatchGetType) {
// setup call
const auto [mockDevice, mockDeviceFactory, device] = setup();
const auto recoveredMockDevice = createConfiguredMockDevice();
EXPECT_CALL(*recoveredMockDevice, getType()).Times(1).WillOnce(Return(nn::DeviceType::GPU));
EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
// run test
const auto result = device->recover(mockDevice.get(), /*blocking=*/false);
// verify result
ASSERT_TRUE(result.has_value())
<< "Failed with " << result.error().code << ": " << result.error().message;
EXPECT_TRUE(result.value() != nullptr);
EXPECT_TRUE(result.value() != mockDevice);
EXPECT_TRUE(result.value() != recoveredMockDevice);
}
TEST(ResilientDeviceTest, recoverCacheMismatchGetSupportedExtensions) {
// setup call
const auto [mockDevice, mockDeviceFactory, device] = setup();
const auto recoveredMockDevice = createConfiguredMockDevice();
const auto kDifferentExtensions =
std::vector<nn::Extension>{nn::Extension{.name = "", .operandTypes = {}}};
const auto ret = [&kDifferentExtensions]() -> const std::vector<nn::Extension>& {
return kDifferentExtensions;
};
EXPECT_CALL(*recoveredMockDevice, getSupportedExtensions()).Times(1).WillOnce(ret);
EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
// run test
const auto result = device->recover(mockDevice.get(), /*blocking=*/false);
// verify result
ASSERT_TRUE(result.has_value())
<< "Failed with " << result.error().code << ": " << result.error().message;
EXPECT_TRUE(result.value() != nullptr);
EXPECT_TRUE(result.value() != mockDevice);
EXPECT_TRUE(result.value() != recoveredMockDevice);
}
TEST(ResilientDeviceTest, recoverCacheMismatchGetCapabilities) {
// setup call
const auto [mockDevice, mockDeviceFactory, device] = setup();
const auto recoveredMockDevice = createConfiguredMockDevice();
const auto kDifferentCapabilities = nn::Capabilities{
.relaxedFloat32toFloat16PerformanceTensor = {.execTime = 0.5f, .powerUsage = 0.5f},
.operandPerformance = nn::Capabilities::OperandPerformanceTable::create({}).value()};
const auto ret = [&kDifferentCapabilities]() -> const nn::Capabilities& {
return kDifferentCapabilities;
};
EXPECT_CALL(*recoveredMockDevice, getCapabilities()).Times(1).WillOnce(ret);
EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
// run test
const auto result = device->recover(mockDevice.get(), /*blocking=*/false);
// verify result
ASSERT_TRUE(result.has_value())
<< "Failed with " << result.error().code << ": " << result.error().message;
EXPECT_TRUE(result.value() != nullptr);
EXPECT_TRUE(result.value() != mockDevice);
EXPECT_TRUE(result.value() != recoveredMockDevice);
}
TEST(ResilientDeviceTest, recoverCacheMismatchInvalidPrepareModel) {
// setup call
const auto [mockDevice, mockDeviceFactory, device] = setup();
const auto recoveredMockDevice = createConfiguredMockDevice();
EXPECT_CALL(*recoveredMockDevice, getType()).Times(1).WillOnce(Return(nn::DeviceType::GPU));
EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
device->recover(mockDevice.get(), /*blocking=*/false);
// run test
auto result = device->prepareModel({}, {}, {}, {}, {}, {}, {});
// verify result
ASSERT_TRUE(result.has_value())
<< "Failed with " << result.error().code << ": " << result.error().message;
EXPECT_TRUE(result.value() != nullptr);
}
TEST(ResilientDeviceTest, recoverCacheMismatchInvalidPrepareModelFromCache) {
// setup call
const auto [mockDevice, mockDeviceFactory, device] = setup();
const auto recoveredMockDevice = createConfiguredMockDevice();
EXPECT_CALL(*recoveredMockDevice, getType()).Times(1).WillOnce(Return(nn::DeviceType::GPU));
EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
device->recover(mockDevice.get(), /*blocking=*/false);
// run test
auto result = device->prepareModelFromCache({}, {}, {}, {});
// verify result
ASSERT_TRUE(result.has_value())
<< "Failed with " << result.error().code << ": " << result.error().message;
EXPECT_TRUE(result.value() != nullptr);
}
TEST(ResilientDeviceTest, recoverCacheMismatchInvalidAllocate) {
// setup call
const auto [mockDevice, mockDeviceFactory, device] = setup();
const auto recoveredMockDevice = createConfiguredMockDevice();
EXPECT_CALL(*recoveredMockDevice, getType()).Times(1).WillOnce(Return(nn::DeviceType::GPU));
EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
device->recover(mockDevice.get(), /*blocking=*/false);
// run test
auto result = device->allocate({}, {}, {}, {});
// verify result
ASSERT_TRUE(result.has_value())
<< "Failed with " << result.error().code << ": " << result.error().message;
EXPECT_TRUE(result.value() != nullptr);
}
} // namespace android::hardware::neuralnetworks::utils