From 7f7e3874339e9ff4fe3bdb24e627f4e0f7edcf97 Mon Sep 17 00:00:00 2001 From: Xusong Wang Date: Tue, 28 Apr 2020 16:05:48 -0700 Subject: Add internal NNT_static tests for buffer copying. This helps cover the code path of data copying between hidl memory and IBuffer, as well as data copying between IBuffers. These code paths may not be covered by CTS because of the lack of driver support. This CL additionally extracts TestAshmem class from TestGenerated to a common TestUtils. Bug: 152209365 Test: NNT_static Change-Id: I617bfbc391c7d0ada0c32b31ee2e6e493d5dc6a2 --- nn/runtime/test/TestGenerated.cpp | 47 +-------------- nn/runtime/test/TestMemoryDomain.cpp | 107 +++++++++++++++++++++++++---------- nn/runtime/test/TestUtils.h | 82 +++++++++++++++++++++++++++ 3 files changed, 161 insertions(+), 75 deletions(-) create mode 100644 nn/runtime/test/TestUtils.h (limited to 'nn/runtime/test') diff --git a/nn/runtime/test/TestGenerated.cpp b/nn/runtime/test/TestGenerated.cpp index 60f866cab..cc0b61983 100644 --- a/nn/runtime/test/TestGenerated.cpp +++ b/nn/runtime/test/TestGenerated.cpp @@ -15,9 +15,6 @@ */ #include -#include -#include -#include #include #include #include @@ -37,6 +34,7 @@ #include "GeneratedTestUtils.h" #include "TestHarness.h" #include "TestNeuralNetworksWrapper.h" +#include "TestUtils.h" // Systrace is not available from CTS tests due to platform layering // constraints. We reuse the NNTEST_ONLY_PUBLIC_API flag, as that should also be @@ -140,49 +138,6 @@ static void computeWithPtrs(const TestModel& testModel, Execution* execution, Re *result = execution->compute(); } -// Convenience class to manage the file, mapping, and memory object. -class TestAshmem { - public: - TestAshmem(::android::base::unique_fd fd, std::unique_ptr<::android::base::MappedFile> mapped, - Memory memory) - : mFd(std::move(fd)), mMapped(std::move(mapped)), mMemory(std::move(memory)) {} - - // Factory function for TestAshmem; prefer this over the raw constructor - static std::unique_ptr createFrom(const TestBuffer& buffer) { - // Create ashmem-based fd. - int fd = ASharedMemory_create(nullptr, buffer.size()); - if (fd <= 0) return nullptr; - ::android::base::unique_fd managedFd(fd); - - // Map and populate ashmem. - auto mappedFile = - ::android::base::MappedFile::FromFd(fd, 0, buffer.size(), PROT_READ | PROT_WRITE); - if (!mappedFile) return nullptr; - memcpy(mappedFile->data(), buffer.get(), buffer.size()); - - // Create NNAPI memory object. - Memory memory(buffer.size(), PROT_READ | PROT_WRITE, fd, 0); - if (!memory.isValid()) return nullptr; - - // Store everything in managing class. - return std::make_unique(std::move(managedFd), std::move(mappedFile), - std::move(memory)); - } - - size_t size() { return mMapped->size(); } - Memory* get() { return &mMemory; } - - template - Type* dataAs() { - return static_cast(static_cast(mMapped->data())); - } - - private: - ::android::base::unique_fd mFd; - std::unique_ptr<::android::base::MappedFile> mMapped; - Memory mMemory; -}; - static ANeuralNetworksMemory* createDeviceMemoryForInput(const Compilation& compilation, uint32_t index) { ANeuralNetworksMemoryDesc* desc = nullptr; diff --git a/nn/runtime/test/TestMemoryDomain.cpp b/nn/runtime/test/TestMemoryDomain.cpp index 88ca2f4d8..4a9c394ad 100644 --- a/nn/runtime/test/TestMemoryDomain.cpp +++ b/nn/runtime/test/TestMemoryDomain.cpp @@ -29,7 +29,9 @@ #include "Manager.h" #include "Memory.h" #include "SampleDriver.h" +#include "SampleDriverFull.h" #include "TestNeuralNetworksWrapper.h" +#include "TestUtils.h" using namespace android::nn; using namespace hal; @@ -163,16 +165,10 @@ test_wrapper::Model createTestModel() { return model; } -// Test memory domain with the following parameters -// - If true, use a V1_2 driver, otherwise, use the latest version; -// - If true, compile with explicit device list, otherwise, compile in the default way; -// - The return of the allocate function. -using MemoryDomainTestParam = std::tuple; - -class MemoryDomainTest : public ::testing::TestWithParam { +class MemoryDomainTestBase : public ::testing::Test { protected: void SetUp() override { - ::testing::TestWithParam::SetUp(); + ::testing::Test::SetUp(); if (DeviceManager::get()->getUseCpuOnly()) { GTEST_SKIP(); } @@ -182,28 +178,14 @@ class MemoryDomainTest : public ::testing::TestWithParam void TearDown() override { DeviceManager::get()->forTest_reInitializeDeviceList(); - ::testing::TestWithParam::TearDown(); - } - - // If kUseV1_2Driver, allocateReturn must be AllocateReturn::NOT_SUPPORTED. - void createAndRegisterDriver(const char* name, std::set supportedOperations, - AllocateReturn allocateReturn) { - sp driver; - if (kUseV1_2Driver) { - CHECK(allocateReturn == AllocateReturn::NOT_SUPPORTED); - const sp testDriver = - new TestDriverLatest(name, supportedOperations, AllocateReturn::NOT_SUPPORTED); - driver = new V1_2::ADevice(testDriver); - } else { - driver = new TestDriverLatest(name, std::move(supportedOperations), allocateReturn); - } - DeviceManager::get()->forTest_registerDevice(name, driver); + ::testing::Test::TearDown(); } - // If not kCompileWithExplicitDeviceList, the input argument "deviceNames" is ignored. + // If "deviceNames" is not empty, the compilation is created with explicit device list; + // otherwise, it is created normally. test_wrapper::Compilation createCompilation(const std::vector& deviceNames) { test_wrapper::Compilation compilation; - if (kCompileWithExplicitDeviceList) { + if (!deviceNames.empty()) { // Map device names to ANeuralNetworksDevice. std::map deviceMap; uint32_t numDevices = 0; @@ -251,14 +233,49 @@ class MemoryDomainTest : public ::testing::TestWithParam return {n, test_wrapper::Memory(memory)}; } + static const test_wrapper::Model kModel; +}; + +const test_wrapper::Model MemoryDomainTestBase::kModel = createTestModel(); + +// Test memory domain with the following parameters +// - If true, use a V1_2 driver, otherwise, use the latest version; +// - If true, compile with explicit device list, otherwise, compile in the default way; +// - The return of the allocate function. +using MemoryDomainTestParam = std::tuple; + +class MemoryDomainTest : public MemoryDomainTestBase, + public ::testing::WithParamInterface { + protected: + // If kUseV1_2Driver, allocateReturn must be AllocateReturn::NOT_SUPPORTED. + void createAndRegisterDriver(const char* name, std::set supportedOperations, + AllocateReturn allocateReturn) { + sp driver; + if (kUseV1_2Driver) { + CHECK(allocateReturn == AllocateReturn::NOT_SUPPORTED); + const sp testDriver = + new TestDriverLatest(name, supportedOperations, AllocateReturn::NOT_SUPPORTED); + driver = new V1_2::ADevice(testDriver); + } else { + driver = new TestDriverLatest(name, std::move(supportedOperations), allocateReturn); + } + DeviceManager::get()->forTest_registerDevice(name, driver); + } + + // If not kCompileWithExplicitDeviceList, the input argument "deviceNames" is ignored. + test_wrapper::Compilation createCompilation(const std::vector& deviceNames) { + if (kCompileWithExplicitDeviceList) { + return MemoryDomainTestBase::createCompilation(deviceNames); + } else { + return MemoryDomainTestBase::createCompilation({}); + } + } + const bool kUseV1_2Driver = std::get<0>(GetParam()); const bool kCompileWithExplicitDeviceList = std::get<1>(GetParam()); const AllocateReturn kAllocateReturn = std::get<2>(GetParam()); - static const test_wrapper::Model kModel; }; -const test_wrapper::Model MemoryDomainTest::kModel = createTestModel(); - // Test device memory allocation on a compilation with only a single partition. TEST_P(MemoryDomainTest, SinglePartition) { createAndRegisterDriver("test_driver", @@ -412,4 +429,36 @@ INSTANTIATE_TEST_CASE_P(DeviceVersionV1_2, MemoryDomainTest, testing::Combine(testing::Values(true), testing::Bool(), testing::Values(AllocateReturn::NOT_SUPPORTED))); +class MemoryDomainCopyTest : public MemoryDomainTestBase {}; + +TEST_F(MemoryDomainCopyTest, MemoryCopyTest) { + sp driver(new sample_driver::SampleDriverFull( + "test_driver", {.execTime = 0.1f, .powerUsage = 0.1f})); + DeviceManager::get()->forTest_registerDevice("test_driver", driver); + auto compilation = createCompilation({"test_driver"}); + ASSERT_NE(compilation.getHandle(), nullptr); + + // Allocate ashmem. + const float initValue1 = 3.14f, initValue2 = 2.72f; + auto ashmem1 = TestAshmem::createFrom(&initValue1, sizeof(float)); + auto ashmem2 = TestAshmem::createFrom(&initValue2, sizeof(float)); + ASSERT_NE(ashmem1, nullptr); + ASSERT_NE(ashmem2, nullptr); + + // Allocate device memories. + auto [n1, memory1] = allocateDeviceMemory(compilation, {0}, {}); + auto [n2, memory2] = allocateDeviceMemory(compilation, {0}, {}); + ASSERT_EQ(n1, ANEURALNETWORKS_NO_ERROR); + ASSERT_EQ(n2, ANEURALNETWORKS_NO_ERROR); + + // Test memory copying: ashmem1 -> memory1 -> memory2 -> ashmem2 + ASSERT_EQ(ANeuralNetworksMemory_copy(ashmem1->get()->get(), memory1.get()), + ANEURALNETWORKS_NO_ERROR); + ASSERT_EQ(ANeuralNetworksMemory_copy(memory1.get(), memory2.get()), ANEURALNETWORKS_NO_ERROR); + ASSERT_EQ(ANeuralNetworksMemory_copy(memory2.get(), ashmem2->get()->get()), + ANEURALNETWORKS_NO_ERROR); + + EXPECT_EQ(ashmem2->dataAs()[0], initValue1); +} + } // namespace diff --git a/nn/runtime/test/TestUtils.h b/nn/runtime/test/TestUtils.h new file mode 100644 index 000000000..61f7ef8f5 --- /dev/null +++ b/nn/runtime/test/TestUtils.h @@ -0,0 +1,82 @@ +/* + * 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. + */ + +#ifndef ANDROID_FRAMEWORKS_ML_NN_RUNTIME_TEST_TEST_UTILS_H +#define ANDROID_FRAMEWORKS_ML_NN_RUNTIME_TEST_TEST_UTILS_H + +#include +#include +#include + +#include +#include + +#include "TestHarness.h" +#include "TestNeuralNetworksWrapper.h" + +namespace android::nn { + +// Convenience class to manage the file, mapping, and memory object. +class TestAshmem { + public: + TestAshmem(::android::base::unique_fd fd, std::unique_ptr<::android::base::MappedFile> mapped, + test_wrapper::Memory memory) + : mFd(std::move(fd)), mMapped(std::move(mapped)), mMemory(std::move(memory)) {} + + // Factory function for TestAshmem; prefer this over the raw constructor + static std::unique_ptr createFrom(const test_helper::TestBuffer& buffer) { + return createFrom(buffer.get(), buffer.size()); + } + + // Factory function for TestAshmem; prefer this over the raw constructor + static std::unique_ptr createFrom(const void* data, uint32_t length) { + // Create ashmem-based fd. + int fd = ASharedMemory_create(nullptr, length); + if (fd <= 0) return nullptr; + ::android::base::unique_fd managedFd(fd); + + // Map and populate ashmem. + auto mappedFile = + ::android::base::MappedFile::FromFd(fd, 0, length, PROT_READ | PROT_WRITE); + if (!mappedFile) return nullptr; + memcpy(mappedFile->data(), data, length); + + // Create NNAPI memory object. + test_wrapper::Memory memory(length, PROT_READ | PROT_WRITE, fd, 0); + if (!memory.isValid()) return nullptr; + + // Store everything in managing class. + return std::make_unique(std::move(managedFd), std::move(mappedFile), + std::move(memory)); + } + + size_t size() { return mMapped->size(); } + test_wrapper::Memory* get() { return &mMemory; } + + template + Type* dataAs() { + return static_cast(static_cast(mMapped->data())); + } + + private: + ::android::base::unique_fd mFd; + std::unique_ptr<::android::base::MappedFile> mMapped; + test_wrapper::Memory mMemory; +}; + +} // namespace android::nn + +#endif // ANDROID_FRAMEWORKS_ML_NN_RUNTIME_TEST_TEST_UTILS_H -- cgit v1.2.3