1 /* 2 * Copyright (c) 2021 Arm Limited. 3 * 4 * SPDX-License-Identifier: MIT 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to 8 * deal in the Software without restriction, including without limitation the 9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 * sell copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in all 14 * copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 * SOFTWARE. 23 */ 24 #ifndef ARM_COMPUTE_TEST_UNIT_TENSOR_FIXTURE 25 #define ARM_COMPUTE_TEST_UNIT_TENSOR_FIXTURE 26 27 #include "arm_compute/Acl.hpp" 28 #include "tests/framework/Asserts.h" 29 #include "tests/framework/Fixture.h" 30 #include "tests/framework/Macros.h" 31 #include "tests/validation/Validation.h" 32 33 namespace arm_compute 34 { 35 namespace test 36 { 37 namespace validation 38 { 39 /** Test case for AclCreateTensor 40 * 41 * Validate that AclCreateTensor behaves as expected with invalid context 42 * 43 * Test Steps: 44 * - Call AclCreateTensor with an invalid context 45 * - Confirm that AclInvalidArgument is reported 46 * - Confirm that the tensor is still nullptr 47 */ 48 class CreateTensorWithInvalidContextFixture : public framework::Fixture 49 { 50 public: setup()51 void setup() 52 { 53 AclTensor tensor = nullptr; 54 ARM_COMPUTE_ASSERT(AclCreateTensor(&tensor, nullptr, nullptr, false) == AclStatus::AclInvalidArgument); 55 ARM_COMPUTE_ASSERT(tensor == nullptr); 56 }; 57 }; 58 59 /** Test-case for AclCreateTensor 60 * 61 * Validate that AclCreateTensor behaves as expected on invalid descriptor 62 * 63 * Test Steps: 64 * - Call AclCreateTensor with valid context but invalid descriptor 65 * - Confirm that AclInvalidArgument is reported 66 * - Confirm that tensor is still nullptr 67 */ 68 template <acl::Target Target> 69 class CreateTensorWithInvalidDescriptorFixture : public framework::Fixture 70 { 71 public: setup()72 void setup() 73 { 74 acl::Context ctx(Target); 75 AclTensor tensor = nullptr; 76 ARM_COMPUTE_ASSERT(AclCreateTensor(&tensor, ctx.get(), nullptr, false) == AclStatus::AclInvalidArgument); 77 ARM_COMPUTE_ASSERT(tensor == nullptr); 78 79 // Check invalid data type 80 AclTensorDescriptor invalid_desc; 81 invalid_desc.ndims = 4; 82 invalid_desc.data_type = static_cast<AclDataType>(-1); 83 ARM_COMPUTE_ASSERT(AclCreateTensor(&tensor, ctx.get(), &invalid_desc, false) == AclStatus::AclInvalidArgument); 84 ARM_COMPUTE_ASSERT(tensor == nullptr); 85 86 // Check invalid number of dimensions 87 invalid_desc.data_type = AclDataType::AclFloat32; 88 invalid_desc.ndims = 15; 89 ARM_COMPUTE_ASSERT(AclCreateTensor(&tensor, ctx.get(), &invalid_desc, false) == AclStatus::AclInvalidArgument); 90 ARM_COMPUTE_ASSERT(tensor == nullptr); 91 }; 92 }; 93 94 /** Test case for AclDestroyTensor 95 * 96 * Validate that AclDestroyTensor behaves as expected when an invalid tensor is given 97 * 98 * Test Steps: 99 * - Call AclDestroyTensor with null tensor 100 * - Confirm that AclInvalidArgument is reported 101 * - Call AclDestroyTensor on empty array 102 * - Confirm that AclInvalidArgument is reported 103 * - Call AclDestroyTensor on an ACL object other than AclTensor 104 * - Confirm that AclInvalidArgument is reported 105 * - Confirm that tensor is still nullptr 106 */ 107 template <acl::Target Target> 108 class DestroyInvalidTensorFixture : public framework::Fixture 109 { 110 public: setup()111 void setup() 112 { 113 acl::Context ctx(Target); 114 115 std::array<char, 256> empty_array{}; 116 AclTensor tensor = nullptr; 117 118 ARM_COMPUTE_ASSERT(AclDestroyTensor(tensor) == AclStatus::AclInvalidArgument); 119 ARM_COMPUTE_ASSERT(AclDestroyTensor(reinterpret_cast<AclTensor>(ctx.get())) == AclStatus::AclInvalidArgument); 120 ARM_COMPUTE_ASSERT(AclDestroyTensor(reinterpret_cast<AclTensor>(empty_array.data())) == AclStatus::AclInvalidArgument); 121 ARM_COMPUTE_ASSERT(tensor == nullptr); 122 }; 123 }; 124 125 /** Test case for AclCreateTensor 126 * 127 * Validate that a tensor can be created successfully 128 * 129 * Test Steps: 130 * - Create a valid context 131 * - Create a valid tensor 132 * - Confirm that AclSuccess is returned 133 */ 134 template <acl::Target Target> 135 class SimpleTensorFixture : public framework::Fixture 136 { 137 public: setup()138 void setup() 139 { 140 acl::StatusCode err = acl::StatusCode::Success; 141 acl::Context ctx(Target, &err); 142 143 ARM_COMPUTE_ASSERT(err == acl::StatusCode::Success); 144 acl::Tensor tensor(ctx, acl::TensorDescriptor({ 2, 3 }, acl::DataType::Float32), &err); 145 ARM_COMPUTE_ASSERT(err == acl::StatusCode::Success); 146 }; 147 }; 148 149 /** Test case for AclTensor 150 * 151 * Validate that multiple tensors can be created successfully 152 * Stress the possibility of memory leaks 153 * 154 * Test Steps: 155 * - Create a valid context 156 * - Create a lot of tensors 157 * - Confirm that AclSuccess is returned 158 */ 159 template <acl::Target Target> 160 class TensorStressFixture : public framework::Fixture 161 { 162 public: setup()163 void setup() 164 { 165 acl::StatusCode err = acl::StatusCode::Success; 166 167 acl::Context ctx(Target, &err); 168 ARM_COMPUTE_ASSERT(err == acl::StatusCode::Success); 169 170 const unsigned int num_tensors = 1024; 171 for(unsigned int i = 0; i < num_tensors; ++i) 172 { 173 acl::Tensor tensor(ctx, acl::TensorDescriptor({ 1024, 1024 }, acl::DataType::Float32), &err); 174 ARM_COMPUTE_ASSERT(err == acl::StatusCode::Success); 175 } 176 }; 177 }; 178 179 /** Test case for AclMapTensor 180 * 181 * Validate that map on an invalid object fails 182 * 183 * Test Steps: 184 * - Create a valid context 185 * - Pass and invalid object for mapping 186 * - Confirm that AclInvalidArgument is returned 187 */ 188 template <acl::Target Target> 189 class MapInvalidTensorFixture : public framework::Fixture 190 { 191 public: setup()192 void setup() 193 { 194 acl::StatusCode err = acl::StatusCode::Success; 195 196 acl::Context ctx(Target, &err); 197 ARM_COMPUTE_ASSERT(err == acl::StatusCode::Success); 198 199 void *handle = nullptr; 200 ARM_COMPUTE_ASSERT(AclMapTensor(reinterpret_cast<AclTensor>(ctx.get()), &handle) == AclStatus::AclInvalidArgument); 201 }; 202 }; 203 204 /** Test case for AclMapTensor 205 * 206 * Validate that map of an unallocated pointer is nullptr 207 * 208 * Test Steps: 209 * - Create a valid context 210 * - Create a valid tensor without allocating 211 * - Map tensor 212 * - Check that mapping is nullptr 213 */ 214 template <acl::Target Target> 215 class MapNotAllocatedTensorFixture : public framework::Fixture 216 { 217 public: setup()218 void setup() 219 { 220 acl::StatusCode err = acl::StatusCode::Success; 221 222 acl::Context ctx(Target, &err); 223 ARM_COMPUTE_ASSERT(err == acl::StatusCode::Success); 224 225 acl::Tensor tensor(ctx, acl::TensorDescriptor({ 8, 8 }, acl::DataType::Float32), false /* allocate */, &err); 226 ARM_COMPUTE_ASSERT(err == acl::StatusCode::Success); 227 ARM_COMPUTE_ASSERT(tensor.map() == nullptr); 228 }; 229 }; 230 231 /** Test case for AclMapTensor 232 * 233 * Validate that map of a valid tensor return a non-nullptr value 234 * 235 * Test Steps: 236 * - Create a valid context 237 * - Create a valid tensor while allocating 238 * - Map tensor 239 * - Check that mapping is not nullptr 240 */ 241 template <acl::Target Target> 242 class MapAllocatedTensorFixture : public framework::Fixture 243 { 244 public: setup()245 void setup() 246 { 247 acl::StatusCode err = acl::StatusCode::Success; 248 249 acl::Context ctx(Target, &err); 250 ARM_COMPUTE_ASSERT(err == acl::StatusCode::Success); 251 252 acl::Tensor tensor(ctx, acl::TensorDescriptor({ 8, 8 }, acl::DataType::Float32), &err); 253 ARM_COMPUTE_ASSERT(err == acl::StatusCode::Success); 254 255 void *handle = tensor.map(); 256 ARM_COMPUTE_ASSERT(handle != nullptr); 257 ARM_COMPUTE_ASSERT(tensor.unmap(handle) == acl::StatusCode::Success); 258 }; 259 }; 260 261 /** Test case for AclTensorImport 262 * 263 * Validate that an externally memory can be successfully imported 264 * 265 * Test Steps: 266 * - Create a valid context 267 * - Create a valid tensor without allocating 268 * - Allocate external memory 269 * - Import memory to the tensor 270 * - Check that imported pointer matches 271 */ 272 template <acl::Target Target> 273 class ImportMemoryFixture : public framework::Fixture 274 { 275 public: setup()276 void setup() 277 { 278 acl::StatusCode err = acl::StatusCode::Success; 279 280 acl::Context ctx(Target, &err); 281 ARM_COMPUTE_ASSERT(err == acl::StatusCode::Success); 282 283 const int32_t size = 8; 284 acl::Tensor tensor(ctx, acl::TensorDescriptor({ size }, acl::DataType::Float32), false /* allocate */, &err); 285 ARM_COMPUTE_ASSERT(err == acl::StatusCode::Success); 286 287 std::vector<float> data(size); 288 err = tensor.import(data.data(), acl::ImportType::Host); 289 290 void *handle = tensor.map(); 291 ARM_COMPUTE_ASSERT(handle == data.data()); 292 ARM_COMPUTE_ASSERT(tensor.unmap(handle) == acl::StatusCode::Success); 293 } 294 }; 295 /** Test case for get_size() interface of Tensor 296 * 297 * 298 * Test Steps: 299 * - Create a valid context 300 * - Create a valid tensor 301 * - Compare the size value returned with the expected value 302 */ 303 template <acl::Target Target> 304 class TensorSizeFixture : public framework::Fixture 305 { 306 public: setup()307 void setup() 308 { 309 acl::StatusCode err = acl::StatusCode::Success; 310 acl::Context ctx(Target, &err); 311 312 ARM_COMPUTE_ASSERT(err == acl::StatusCode::Success); 313 acl::Tensor tensor(ctx, acl::TensorDescriptor({ 2, 3 }, acl::DataType::Float32), &err); 314 315 // size should be 6 elements (2x3) times 4 bytes (float32) = 24 bytes 316 constexpr size_t expected_size = 24; 317 ARM_COMPUTE_ASSERT(tensor.get_size() == expected_size); 318 }; 319 }; 320 /** Test case for get_size() dealing with invalid arguments 321 * 322 * Test Steps: 323 * - Test nullptr tensor can return a correct error 324 * - Create a valid tensor 325 * - Test C interface with null size argument can return a correct error 326 */ 327 template <acl::Target Target> 328 class InvalidTensorSizeFixture : public framework::Fixture 329 { 330 public: setup()331 void setup() 332 { 333 // Null tensor 334 AclTensor null_tensor = nullptr; 335 uint64_t size{ 0 }; 336 ARM_COMPUTE_ASSERT(AclGetTensorSize(null_tensor, &size) == AclStatus::AclInvalidArgument); 337 338 // Create valid tensor 339 acl::StatusCode err = acl::StatusCode::Success; 340 acl::Context ctx(Target, &err); 341 ARM_COMPUTE_ASSERT(err == acl::StatusCode::Success); 342 acl::Tensor tensor(ctx, acl::TensorDescriptor({ 2, 3 }, acl::DataType::Float32), &err); 343 344 // Null size argument 345 ARM_COMPUTE_ASSERT(AclGetTensorSize(tensor.get(), nullptr) == AclStatus::AclInvalidArgument); 346 }; 347 }; 348 349 template <acl::Target Target> 350 class DescriptorConversionFixture : public framework::Fixture 351 { compare_descriptor(const AclTensorDescriptor & desc_a,const AclTensorDescriptor & desc_b)352 bool compare_descriptor(const AclTensorDescriptor &desc_a, const AclTensorDescriptor &desc_b) 353 { 354 auto are_descriptors_same = true; 355 356 are_descriptors_same &= desc_a.ndims == desc_b.ndims; 357 are_descriptors_same &= desc_a.data_type == desc_b.data_type; 358 are_descriptors_same &= desc_a.shape != nullptr && desc_b.shape != nullptr; 359 360 for(int32_t d = 0; d < desc_a.ndims; ++d) 361 { 362 are_descriptors_same &= desc_a.shape[d] == desc_b.shape[d]; 363 } 364 365 // other attributes should be added here 366 367 return are_descriptors_same; 368 } 369 370 public: setup()371 void setup() 372 { 373 auto err{ acl::StatusCode::Success }; 374 auto ctx{ acl::Context(Target, &err) }; 375 ARM_COMPUTE_ASSERT(err == acl::StatusCode::Success); 376 377 auto desc{ acl::TensorDescriptor({ 2, 3 }, acl::DataType::Float32) }; 378 acl::Tensor tensor(ctx, desc, &err); 379 380 auto desc_from_tensor = tensor.get_descriptor(); 381 382 ARM_COMPUTE_ASSERT(compare_descriptor(*desc.get(), *desc_from_tensor.get())); 383 ARM_COMPUTE_ASSERT(desc == desc_from_tensor); 384 385 // Test c interface with "prepopulated" descriptor 386 // Note: When c interface used, there are possibility of memory leak 387 // if members are not correctly deleted (e.g., shape). 388 // Since that is considered user's responsibility, we don't test here. 389 AclTensorDescriptor prepopulated_descriptor 390 { 391 3, nullptr, AclDataType::AclBFloat16, nullptr, 0 392 }; 393 394 ARM_COMPUTE_ASSERT(AclGetTensorDescriptor(tensor.get(), &prepopulated_descriptor) == AclStatus::AclSuccess); 395 ARM_COMPUTE_ASSERT(compare_descriptor(*desc.get(), prepopulated_descriptor)); 396 ARM_COMPUTE_ASSERT(desc == acl::TensorDescriptor(prepopulated_descriptor)); 397 }; 398 }; 399 400 template <acl::Target Target> 401 class InvalidDescriptorConversionFixture : public framework::Fixture 402 { 403 public: setup()404 void setup() 405 { 406 // Null tensor 407 AclTensor null_tensor = nullptr; 408 AclTensorDescriptor desc{}; 409 ARM_COMPUTE_ASSERT(AclGetTensorDescriptor(null_tensor, &desc) == AclStatus::AclInvalidArgument); 410 411 // Create valid tensor 412 acl::StatusCode err = acl::StatusCode::Success; 413 acl::Context ctx(Target, &err); 414 ARM_COMPUTE_ASSERT(err == acl::StatusCode::Success); 415 acl::Tensor tensor(ctx, acl::TensorDescriptor({ 2, 3 }, acl::DataType::Float32), &err); 416 417 // Null size argument 418 ARM_COMPUTE_ASSERT(AclGetTensorDescriptor(tensor.get(), nullptr) == AclStatus::AclInvalidArgument); 419 }; 420 }; 421 } // namespace validation 422 } // namespace test 423 } // namespace arm_compute 424 #endif /* ARM_COMPUTE_TEST_UNIT_TENSOR_FIXTURE */ 425