| /* |
| * Copyright (c) 2021 Arm Limited. |
| * |
| * SPDX-License-Identifier: MIT |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a copy |
| * of this software and associated documentation files (the "Software"), to |
| * deal in the Software without restriction, including without limitation the |
| * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
| * sell copies of the Software, and to permit persons to whom the Software is |
| * furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included in all |
| * copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| * SOFTWARE. |
| */ |
| #ifndef ARM_COMPUTE_TEST_UNIT_TENSOR_FIXTURE |
| #define ARM_COMPUTE_TEST_UNIT_TENSOR_FIXTURE |
| |
| #include "arm_compute/Acl.hpp" |
| #include "tests/framework/Asserts.h" |
| #include "tests/framework/Fixture.h" |
| #include "tests/framework/Macros.h" |
| #include "tests/validation/Validation.h" |
| |
| namespace arm_compute |
| { |
| namespace test |
| { |
| namespace validation |
| { |
| /** Test case for AclCreateTensor |
| * |
| * Validate that AclCreateTensor behaves as expected with invalid context |
| * |
| * Test Steps: |
| * - Call AclCreateTensor with an invalid context |
| * - Confirm that AclInvalidArgument is reported |
| * - Confirm that the tensor is still nullptr |
| */ |
| class CreateTensorWithInvalidContextFixture : public framework::Fixture |
| { |
| public: |
| void setup() |
| { |
| AclTensor tensor = nullptr; |
| ARM_COMPUTE_ASSERT(AclCreateTensor(&tensor, nullptr, nullptr, false) == AclStatus::AclInvalidArgument); |
| ARM_COMPUTE_ASSERT(tensor == nullptr); |
| }; |
| }; |
| |
| /** Test-case for AclCreateTensor |
| * |
| * Validate that AclCreateTensor behaves as expected on invalid descriptor |
| * |
| * Test Steps: |
| * - Call AclCreateTensor with valid context but invalid descriptor |
| * - Confirm that AclInvalidArgument is reported |
| * - Confirm that tensor is still nullptr |
| */ |
| template <acl::Target Target> |
| class CreateTensorWithInvalidDescriptorFixture : public framework::Fixture |
| { |
| public: |
| void setup() |
| { |
| acl::Context ctx(Target); |
| AclTensor tensor = nullptr; |
| ARM_COMPUTE_ASSERT(AclCreateTensor(&tensor, ctx.get(), nullptr, false) == AclStatus::AclInvalidArgument); |
| ARM_COMPUTE_ASSERT(tensor == nullptr); |
| |
| // Check invalid data type |
| AclTensorDescriptor invalid_desc; |
| invalid_desc.ndims = 4; |
| invalid_desc.data_type = static_cast<AclDataType>(-1); |
| ARM_COMPUTE_ASSERT(AclCreateTensor(&tensor, ctx.get(), &invalid_desc, false) == AclStatus::AclInvalidArgument); |
| ARM_COMPUTE_ASSERT(tensor == nullptr); |
| |
| // Check invalid number of dimensions |
| invalid_desc.data_type = AclDataType::AclFloat32; |
| invalid_desc.ndims = 15; |
| ARM_COMPUTE_ASSERT(AclCreateTensor(&tensor, ctx.get(), &invalid_desc, false) == AclStatus::AclInvalidArgument); |
| ARM_COMPUTE_ASSERT(tensor == nullptr); |
| }; |
| }; |
| |
| /** Test case for AclDestroyTensor |
| * |
| * Validate that AclDestroyTensor behaves as expected when an invalid tensor is given |
| * |
| * Test Steps: |
| * - Call AclDestroyTensor with null tensor |
| * - Confirm that AclInvalidArgument is reported |
| * - Call AclDestroyTensor on empty array |
| * - Confirm that AclInvalidArgument is reported |
| * - Call AclDestroyTensor on an ACL object other than AclTensor |
| * - Confirm that AclInvalidArgument is reported |
| * - Confirm that tensor is still nullptr |
| */ |
| template <acl::Target Target> |
| class DestroyInvalidTensorFixture : public framework::Fixture |
| { |
| public: |
| void setup() |
| { |
| acl::Context ctx(Target); |
| |
| std::array<char, 256> empty_array{}; |
| AclTensor tensor = nullptr; |
| |
| ARM_COMPUTE_ASSERT(AclDestroyTensor(tensor) == AclStatus::AclInvalidArgument); |
| ARM_COMPUTE_ASSERT(AclDestroyTensor(reinterpret_cast<AclTensor>(ctx.get())) == AclStatus::AclInvalidArgument); |
| ARM_COMPUTE_ASSERT(AclDestroyTensor(reinterpret_cast<AclTensor>(empty_array.data())) == AclStatus::AclInvalidArgument); |
| ARM_COMPUTE_ASSERT(tensor == nullptr); |
| }; |
| }; |
| |
| /** Test case for AclCreateTensor |
| * |
| * Validate that a tensor can be created successfully |
| * |
| * Test Steps: |
| * - Create a valid context |
| * - Create a valid tensor |
| * - Confirm that AclSuccess is returned |
| */ |
| template <acl::Target Target> |
| class SimpleTensorFixture : public framework::Fixture |
| { |
| public: |
| void setup() |
| { |
| acl::StatusCode err = acl::StatusCode::Success; |
| acl::Context ctx(Target, &err); |
| |
| ARM_COMPUTE_ASSERT(err == acl::StatusCode::Success); |
| acl::Tensor tensor(ctx, acl::TensorDescriptor({ 2, 3 }, acl::DataType::Float32), &err); |
| ARM_COMPUTE_ASSERT(err == acl::StatusCode::Success); |
| }; |
| }; |
| |
| /** Test case for AclTensor |
| * |
| * Validate that multiple tensors can be created successfully |
| * Stress the possibility of memory leaks |
| * |
| * Test Steps: |
| * - Create a valid context |
| * - Create a lot of tensors |
| * - Confirm that AclSuccess is returned |
| */ |
| template <acl::Target Target> |
| class TensorStressFixture : public framework::Fixture |
| { |
| public: |
| void setup() |
| { |
| acl::StatusCode err = acl::StatusCode::Success; |
| |
| acl::Context ctx(Target, &err); |
| ARM_COMPUTE_ASSERT(err == acl::StatusCode::Success); |
| |
| const unsigned int num_tensors = 1024; |
| for(unsigned int i = 0; i < num_tensors; ++i) |
| { |
| acl::Tensor tensor(ctx, acl::TensorDescriptor({ 1024, 1024 }, acl::DataType::Float32), &err); |
| ARM_COMPUTE_ASSERT(err == acl::StatusCode::Success); |
| } |
| }; |
| }; |
| |
| /** Test case for AclMapTensor |
| * |
| * Validate that map on an invalid object fails |
| * |
| * Test Steps: |
| * - Create a valid context |
| * - Pass and invalid object for mapping |
| * - Confirm that AclInvalidArgument is returned |
| */ |
| template <acl::Target Target> |
| class MapInvalidTensorFixture : public framework::Fixture |
| { |
| public: |
| void setup() |
| { |
| acl::StatusCode err = acl::StatusCode::Success; |
| |
| acl::Context ctx(Target, &err); |
| ARM_COMPUTE_ASSERT(err == acl::StatusCode::Success); |
| |
| void *handle = nullptr; |
| ARM_COMPUTE_ASSERT(AclMapTensor(reinterpret_cast<AclTensor>(ctx.get()), &handle) == AclStatus::AclInvalidArgument); |
| }; |
| }; |
| |
| /** Test case for AclMapTensor |
| * |
| * Validate that map of an unallocated pointer is nullptr |
| * |
| * Test Steps: |
| * - Create a valid context |
| * - Create a valid tensor without allocating |
| * - Map tensor |
| * - Check that mapping is nullptr |
| */ |
| template <acl::Target Target> |
| class MapNotAllocatedTensorFixture : public framework::Fixture |
| { |
| public: |
| void setup() |
| { |
| acl::StatusCode err = acl::StatusCode::Success; |
| |
| acl::Context ctx(Target, &err); |
| ARM_COMPUTE_ASSERT(err == acl::StatusCode::Success); |
| |
| acl::Tensor tensor(ctx, acl::TensorDescriptor({ 8, 8 }, acl::DataType::Float32), false /* allocate */, &err); |
| ARM_COMPUTE_ASSERT(err == acl::StatusCode::Success); |
| ARM_COMPUTE_ASSERT(tensor.map() == nullptr); |
| }; |
| }; |
| |
| /** Test case for AclMapTensor |
| * |
| * Validate that map of a valid tensor return a non-nullptr value |
| * |
| * Test Steps: |
| * - Create a valid context |
| * - Create a valid tensor while allocating |
| * - Map tensor |
| * - Check that mapping is not nullptr |
| */ |
| template <acl::Target Target> |
| class MapAllocatedTensorFixture : public framework::Fixture |
| { |
| public: |
| void setup() |
| { |
| acl::StatusCode err = acl::StatusCode::Success; |
| |
| acl::Context ctx(Target, &err); |
| ARM_COMPUTE_ASSERT(err == acl::StatusCode::Success); |
| |
| acl::Tensor tensor(ctx, acl::TensorDescriptor({ 8, 8 }, acl::DataType::Float32), &err); |
| ARM_COMPUTE_ASSERT(err == acl::StatusCode::Success); |
| |
| void *handle = tensor.map(); |
| ARM_COMPUTE_ASSERT(handle != nullptr); |
| ARM_COMPUTE_ASSERT(tensor.unmap(handle) == acl::StatusCode::Success); |
| }; |
| }; |
| |
| /** Test case for AclTensorImport |
| * |
| * Validate that an externally memory can be successfully imported |
| * |
| * Test Steps: |
| * - Create a valid context |
| * - Create a valid tensor without allocating |
| * - Allocate external memory |
| * - Import memory to the tensor |
| * - Check that imported pointer matches |
| */ |
| template <acl::Target Target> |
| class ImportMemoryFixture : public framework::Fixture |
| { |
| public: |
| void setup() |
| { |
| acl::StatusCode err = acl::StatusCode::Success; |
| |
| acl::Context ctx(Target, &err); |
| ARM_COMPUTE_ASSERT(err == acl::StatusCode::Success); |
| |
| const int32_t size = 8; |
| acl::Tensor tensor(ctx, acl::TensorDescriptor({ size }, acl::DataType::Float32), false /* allocate */, &err); |
| ARM_COMPUTE_ASSERT(err == acl::StatusCode::Success); |
| |
| std::vector<float> data(size); |
| err = tensor.import(data.data(), acl::ImportType::Host); |
| |
| void *handle = tensor.map(); |
| ARM_COMPUTE_ASSERT(handle == data.data()); |
| ARM_COMPUTE_ASSERT(tensor.unmap(handle) == acl::StatusCode::Success); |
| } |
| }; |
| /** Test case for get_size() interface of Tensor |
| * |
| * |
| * Test Steps: |
| * - Create a valid context |
| * - Create a valid tensor |
| * - Compare the size value returned with the expected value |
| */ |
| template <acl::Target Target> |
| class TensorSizeFixture : public framework::Fixture |
| { |
| public: |
| void setup() |
| { |
| acl::StatusCode err = acl::StatusCode::Success; |
| acl::Context ctx(Target, &err); |
| |
| ARM_COMPUTE_ASSERT(err == acl::StatusCode::Success); |
| acl::Tensor tensor(ctx, acl::TensorDescriptor({ 2, 3 }, acl::DataType::Float32), &err); |
| |
| // size should be 6 elements (2x3) times 4 bytes (float32) = 24 bytes |
| constexpr size_t expected_size = 24; |
| ARM_COMPUTE_ASSERT(tensor.get_size() == expected_size); |
| }; |
| }; |
| /** Test case for get_size() dealing with invalid arguments |
| * |
| * Test Steps: |
| * - Test nullptr tensor can return a correct error |
| * - Create a valid tensor |
| * - Test C interface with null size argument can return a correct error |
| */ |
| template <acl::Target Target> |
| class InvalidTensorSizeFixture : public framework::Fixture |
| { |
| public: |
| void setup() |
| { |
| // Null tensor |
| AclTensor null_tensor = nullptr; |
| uint64_t size{ 0 }; |
| ARM_COMPUTE_ASSERT(AclGetTensorSize(null_tensor, &size) == AclStatus::AclInvalidArgument); |
| |
| // Create valid tensor |
| acl::StatusCode err = acl::StatusCode::Success; |
| acl::Context ctx(Target, &err); |
| ARM_COMPUTE_ASSERT(err == acl::StatusCode::Success); |
| acl::Tensor tensor(ctx, acl::TensorDescriptor({ 2, 3 }, acl::DataType::Float32), &err); |
| |
| // Null size argument |
| ARM_COMPUTE_ASSERT(AclGetTensorSize(tensor.get(), nullptr) == AclStatus::AclInvalidArgument); |
| }; |
| }; |
| |
| template <acl::Target Target> |
| class DescriptorConversionFixture : public framework::Fixture |
| { |
| bool compare_descriptor(const AclTensorDescriptor &desc_a, const AclTensorDescriptor &desc_b) |
| { |
| auto are_descriptors_same = true; |
| |
| are_descriptors_same &= desc_a.ndims == desc_b.ndims; |
| are_descriptors_same &= desc_a.data_type == desc_b.data_type; |
| are_descriptors_same &= desc_a.shape != nullptr && desc_b.shape != nullptr; |
| |
| for(int32_t d = 0; d < desc_a.ndims; ++d) |
| { |
| are_descriptors_same &= desc_a.shape[d] == desc_b.shape[d]; |
| } |
| |
| // other attributes should be added here |
| |
| return are_descriptors_same; |
| } |
| |
| public: |
| void setup() |
| { |
| auto err{ acl::StatusCode::Success }; |
| auto ctx{ acl::Context(Target, &err) }; |
| ARM_COMPUTE_ASSERT(err == acl::StatusCode::Success); |
| |
| auto desc{ acl::TensorDescriptor({ 2, 3 }, acl::DataType::Float32) }; |
| acl::Tensor tensor(ctx, desc, &err); |
| |
| auto desc_from_tensor = tensor.get_descriptor(); |
| |
| ARM_COMPUTE_ASSERT(compare_descriptor(*desc.get(), *desc_from_tensor.get())); |
| ARM_COMPUTE_ASSERT(desc == desc_from_tensor); |
| |
| // Test c interface with "prepopulated" descriptor |
| // Note: When c interface used, there are possibility of memory leak |
| // if members are not correctly deleted (e.g., shape). |
| // Since that is considered user's responsibility, we don't test here. |
| AclTensorDescriptor prepopulated_descriptor |
| { |
| 3, nullptr, AclDataType::AclBFloat16, nullptr, 0 |
| }; |
| |
| ARM_COMPUTE_ASSERT(AclGetTensorDescriptor(tensor.get(), &prepopulated_descriptor) == AclStatus::AclSuccess); |
| ARM_COMPUTE_ASSERT(compare_descriptor(*desc.get(), prepopulated_descriptor)); |
| ARM_COMPUTE_ASSERT(desc == acl::TensorDescriptor(prepopulated_descriptor)); |
| }; |
| }; |
| |
| template <acl::Target Target> |
| class InvalidDescriptorConversionFixture : public framework::Fixture |
| { |
| public: |
| void setup() |
| { |
| // Null tensor |
| AclTensor null_tensor = nullptr; |
| AclTensorDescriptor desc{}; |
| ARM_COMPUTE_ASSERT(AclGetTensorDescriptor(null_tensor, &desc) == AclStatus::AclInvalidArgument); |
| |
| // Create valid tensor |
| acl::StatusCode err = acl::StatusCode::Success; |
| acl::Context ctx(Target, &err); |
| ARM_COMPUTE_ASSERT(err == acl::StatusCode::Success); |
| acl::Tensor tensor(ctx, acl::TensorDescriptor({ 2, 3 }, acl::DataType::Float32), &err); |
| |
| // Null size argument |
| ARM_COMPUTE_ASSERT(AclGetTensorDescriptor(tensor.get(), nullptr) == AclStatus::AclInvalidArgument); |
| }; |
| }; |
| } // namespace validation |
| } // namespace test |
| } // namespace arm_compute |
| #endif /* ARM_COMPUTE_TEST_UNIT_TENSOR_FIXTURE */ |