2020-12-11 00:38:45 +01:00
|
|
|
/*
|
|
|
|
* 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::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; };
|
2021-11-18 01:33:35 +01:00
|
|
|
constexpr auto kFeatureLevel = nn::kVersionFeatureLevel1;
|
2020-12-11 00:38:45 +01:00
|
|
|
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();
|
2021-11-18 01:33:35 +01:00
|
|
|
constexpr auto kFeatureLevel = nn::kVersionFeatureLevel1;
|
2020-12-11 00:38:45 +01:00
|
|
|
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)
|
2021-11-18 01:33:35 +01:00
|
|
|
.WillOnce(Return(nn::kVersionFeatureLevel2));
|
2020-12-11 00:38:45 +01:00
|
|
|
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
|