Files
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

601 lines
14 KiB
C++

// Copyright © Advanced Micro Devices, Inc., or its affiliates.
// SPDX-License-Identifier: MIT
#include <gtest/gtest.h>
#include <algorithm>
#include <hipdnn_data_sdk/utilities/Tensor.hpp>
#include <hipdnn_data_sdk/utilities/TensorView.hpp>
#include <numeric>
using namespace hipdnn_data_sdk::utilities;
// ============================================================================
// Basic Typed Iteration Tests
// ============================================================================
TEST(TestTensorView, BasicIteration)
{
Tensor<float> tensor({2, 3});
tensor.fillWithValue(1.0f);
ITensor* iTensor = &tensor;
TensorView<float> view(*iTensor);
int count = 0;
for(auto it = view.begin(); it != view.end(); ++it)
{
// No cast needed! Direct typed reference
const float& value = *it;
EXPECT_EQ(value, 1.0f);
++count;
}
EXPECT_EQ(count, 6);
}
TEST(TestTensorView, ModifyValues)
{
Tensor<float> tensor({2, 3});
tensor.fillWithValue(1.0f);
ITensor* iTensor = &tensor;
TensorView<float> view(*iTensor);
// Modify all values through typed iterator
for(auto it = view.begin(); it != view.end(); ++it)
{
float& value = *it;
value = 5.0f;
}
// Verify modifications
for(auto it = view.begin(); it != view.end(); ++it)
{
EXPECT_EQ(*it, 5.0f);
}
}
TEST(TestTensorView, RangeBasedForLoop)
{
Tensor<float> tensor({2, 2});
tensor.fillWithValue(3.14f);
ITensor* iTensor = &tensor;
TensorView<float> view(*iTensor);
int count = 0;
// Clean range-based for loop without casts
for(float& value : view)
{
EXPECT_FLOAT_EQ(value, 3.14f);
value = 2.71f;
++count;
}
EXPECT_EQ(count, 4);
// Verify modifications
for(const float& value : view)
{
EXPECT_FLOAT_EQ(value, 2.71f);
}
}
// ============================================================================
// Const Correctness Tests
// ============================================================================
TEST(TestTensorView, ConstIteration)
{
Tensor<double> tensor({2, 2});
tensor.fillWithValue(3.14);
const ITensor* iTensor = &tensor;
const ConstTensorView<double> view(*iTensor);
int count = 0;
for(auto it = view.cbegin(); it != view.cend(); ++it)
{
const double& value = *it;
EXPECT_DOUBLE_EQ(value, 3.14);
++count;
}
EXPECT_EQ(count, 4);
}
TEST(TestTensorView, ConstViewFromConstTensor)
{
Tensor<float> tensor({2, 3});
tensor.fillWithValue(1.5f);
const ITensor& iTensor = tensor;
ConstTensorView<float> view(iTensor); // NOLINT(misc-const-correctness) begin() not const
// Should be able to read through const view
for(const float& value : view)
{
EXPECT_FLOAT_EQ(value, 1.5f);
}
}
TEST(TestTensorView, ConstRangeBasedForLoop)
{
Tensor<int> tensor({3, 3});
tensor.fillWithValue(42.0f);
ITensor* iTensor = &tensor;
ConstTensorView<int> view(*iTensor); // NOLINT(misc-const-correctness) begin() not const
int count = 0; // NOLINT(misc-const-correctness) mutated in loop
for(const int& value : view)
{
EXPECT_EQ(value, 42);
++count;
}
EXPECT_EQ(count, 9);
}
// ============================================================================
// Iterator Comparison Tests
// ============================================================================
TEST(TestTensorView, EqualityComparison)
{
Tensor<float> tensor({2, 2});
ITensor* iTensor = &tensor;
TensorView<float> view(*iTensor);
auto it1 = view.begin();
auto it2 = view.begin();
EXPECT_TRUE(it1 == it2);
EXPECT_FALSE(it1 != it2);
++it1;
EXPECT_FALSE(it1 == it2);
EXPECT_TRUE(it1 != it2);
++it2;
EXPECT_TRUE(it1 == it2);
}
TEST(TestTensorView, EndComparison)
{
Tensor<float> tensor({2, 2});
ITensor* iTensor = &tensor;
TensorView<float> view(*iTensor);
auto it = view.begin();
auto end = view.end();
EXPECT_NE(it, end);
// Advance to end
for(int i = 0; i < 4; ++i)
{
++it;
}
EXPECT_EQ(it, end);
}
// ============================================================================
// Copy and Move Semantics Tests
// ============================================================================
TEST(TestTensorView, CopyConstructor)
{
Tensor<float> tensor({2, 2});
tensor.fillWithValue(2.0f);
ITensor* iTensor = &tensor;
TensorView<float> view(*iTensor);
auto it1 = view.begin();
// NOLINTNEXTLINE(performance-unnecessary-copy-initialization)
auto it2 = it1; // Copy
const float& val1 = *it1;
const float& val2 = *it2;
EXPECT_EQ(&val1, &val2);
EXPECT_EQ(val1, 2.0f);
}
TEST(TestTensorView, CopyAssignment)
{
Tensor<float> tensor({2, 2});
tensor.fillWithValue(3.0f);
ITensor* iTensor = &tensor;
TensorView<float> view(*iTensor);
auto it1 = view.begin();
auto it2 = view.end();
it2 = it1; // Copy assignment
EXPECT_EQ(it1, it2);
}
TEST(TestTensorView, MoveConstructor)
{
Tensor<float> tensor({2, 2});
tensor.fillWithValue(4.0f);
ITensor* iTensor = &tensor;
TensorView<float> view(*iTensor);
auto it1 = view.begin();
auto it2 = std::move(it1); // Move
const float& val = *it2;
EXPECT_EQ(val, 4.0f);
}
TEST(TestTensorView, ViewCopy)
{
Tensor<float> tensor({2, 2});
tensor.fillWithValue(1.0f);
ITensor* iTensor = &tensor;
TensorView<float> view1(*iTensor);
TensorView<float> view2(view1); // Copy view
// Both views should iterate the same tensor
auto it1 = view1.begin();
auto it2 = view2.begin();
EXPECT_EQ(*it1, *it2);
}
// ============================================================================
// Type Safety Tests
// ============================================================================
TEST(TestTensorView, AutoTypeDeduction)
{
Tensor<float> tensor({2, 2});
tensor.fillWithValue(1.5f);
ITensor* iTensor = &tensor;
TensorView<float> view(*iTensor);
// auto should correctly deduce float&
for(auto& value : view)
{
static_assert(std::is_same_v<decltype(value), float&>, "Type should be float&");
}
}
// ============================================================================
// Different Data Types
// ============================================================================
TEST(TestTensorViewDouble, BasicIteration)
{
Tensor<double> tensor({3, 3});
tensor.fillWithValue(2.718);
ITensor* iTensor = &tensor;
TensorView<double> view(*iTensor);
for(const double& value : view)
{
EXPECT_DOUBLE_EQ(value, 2.718);
}
}
TEST(TestTensorViewInt, BasicIteration)
{
Tensor<int> tensor({4, 4});
tensor.fillWithValue(7.0f);
ITensor* iTensor = &tensor;
TensorView<int> view(*iTensor);
for(const int& value : view)
{
EXPECT_EQ(value, 7);
}
}
// ============================================================================
// Strided Tensor Tests
// ============================================================================
TEST(TestTensorView, StridedTensor)
{
const std::vector<int64_t> dims = {2, 2};
const std::vector<int64_t> strides = {3, 1}; // Non-standard strides
Tensor<float> tensor(dims, strides);
ITensor* iTensor = &tensor;
TensorView<float> view(*iTensor);
// Set values using view
int counter = 0;
for(float& value : view)
{
value = static_cast<float>(counter++);
}
// Verify using indices
EXPECT_EQ(tensor.getHostValue(0, 0), 0.0f);
EXPECT_EQ(tensor.getHostValue(0, 1), 1.0f);
EXPECT_EQ(tensor.getHostValue(1, 0), 2.0f);
EXPECT_EQ(tensor.getHostValue(1, 1), 3.0f);
}
// ============================================================================
// Multi-Dimensional Tests
// ============================================================================
TEST(TestTensorView, TwoDimensionalTensor)
{
Tensor<float> tensor({3, 4});
ITensor* iTensor = &tensor;
TensorView<float> view(*iTensor);
// Fill with sequence
int counter = 0;
for(float& value : view)
{
value = static_cast<float>(counter++);
}
EXPECT_EQ(counter, 12);
// Verify a few values
EXPECT_EQ(tensor.getHostValue(0, 0), 0.0f);
EXPECT_EQ(tensor.getHostValue(2, 3), 11.0f);
}
TEST(TestTensorView, ThreeDimensionalTensor)
{
Tensor<float> tensor({2, 3, 4});
ITensor* iTensor = &tensor;
TensorView<float> view(*iTensor);
// Fill with index-based values
int counter = 0;
for(float& value : view)
{
value = static_cast<float>(counter++);
}
EXPECT_EQ(counter, 24);
// Verify first and last elements
EXPECT_EQ(tensor.getHostValue(0, 0, 0), 0.0f);
EXPECT_EQ(tensor.getHostValue(1, 2, 3), 23.0f);
}
TEST(TestTensorView, FourDimensionalTensor)
{
Tensor<int> tensor({2, 2, 2, 2});
ITensor* iTensor = &tensor;
TensorView<int> view(*iTensor);
int count = 0;
for(int& value : view)
{
value = count++;
}
EXPECT_EQ(count, 16);
EXPECT_EQ(tensor.getHostValue(0, 0, 0, 0), 0.0f);
EXPECT_EQ(tensor.getHostValue(1, 1, 1, 1), 15.0f);
}
// ============================================================================
// Edge Cases
// ============================================================================
TEST(TestTensorView, EmptyTensor)
{
const std::vector<int64_t> dims = {}; // Zero dimensions
Tensor<float> tensor(dims);
ITensor* iTensor = &tensor;
TensorView<float> view(*iTensor);
auto it = view.begin();
EXPECT_EQ(it, view.end());
EXPECT_THROW(it++, std::out_of_range);
}
TEST(TestTensorView, SingleElement)
{
Tensor<int> tensor({1});
tensor.setHostValue(42, 0);
TensorView<int> view(tensor);
int count = 0;
for(const int& value : view)
{
EXPECT_EQ(value, 42);
++count;
}
EXPECT_EQ(count, 1);
}
// ============================================================================
// Interoperability Tests
// ============================================================================
TEST(TestTensorView, FromITensorReference)
{
Tensor<float> tensor({2, 3});
tensor.fillWithValue(1.0f);
ITensor& iTensor = tensor;
TensorView<float> view(iTensor);
for(const float& value : view)
{
EXPECT_EQ(value, 1.0f);
}
}
TEST(TestTensorView, FromConstITensorReference)
{
Tensor<float> tensor({2, 3});
tensor.fillWithValue(2.0f);
const ITensor& iTensor = tensor;
ConstTensorView<float> view(iTensor); // NOLINT(misc-const-correctness) begin() not const
for(const float& value : view)
{
EXPECT_EQ(value, 2.0f);
}
}
TEST(TestTensorView, ViewAndDirectAccess)
{
Tensor<float> tensor({2, 2});
TensorView<float> view(tensor);
// Set values through view
int counter = 0;
for(float& value : view)
{
value = static_cast<float>(counter++);
}
// Verify through direct tensor access
EXPECT_EQ(tensor.getHostValue(0, 0), 0.0f);
EXPECT_EQ(tensor.getHostValue(0, 1), 1.0f);
EXPECT_EQ(tensor.getHostValue(1, 0), 2.0f);
EXPECT_EQ(tensor.getHostValue(1, 1), 3.0f);
}
// ============================================================================
// STL Algorithm Compatibility
// ============================================================================
TEST(TestTensorView, StdCount)
{
Tensor<int> tensor({5});
TensorView<int> view(tensor);
// Fill with values
int counter = 0;
for(int& value : view)
{
value = (counter++ % 2 == 0) ? 1 : 2;
}
// Count occurrences of 1
auto count = std::count(view.begin(), view.end(), 1);
EXPECT_EQ(count, 3); // Indices 0, 2, 4
}
TEST(TestTensorView, StdAccumulate)
{
Tensor<int> tensor({5});
TensorView<int> view(tensor);
// Fill with sequence 1, 2, 3, 4, 5
std::iota(view.begin(), view.end(), 1);
// Sum all values
const int sum = std::accumulate(view.begin(), view.end(), 0);
EXPECT_EQ(sum, 15); // 1+2+3+4+5
}
TEST(TestTensorView, StdTransform)
{
Tensor<float> tensor({4});
TensorView<float> view(tensor);
// Fill with initial values
std::iota(view.begin(), view.end(), 1.0f);
// Double all values
std::transform(view.begin(), view.end(), view.begin(), [](float val) { return val * 2.0f; });
// Verify
int idx = 0;
for(const float& value : view)
{
EXPECT_FLOAT_EQ(value, static_cast<float>((idx + 1) * 2));
++idx;
}
}
TEST(TestTensorView, StdForEach)
{
Tensor<int> tensor({3, 3});
TensorView<int> view(tensor);
// Initialize
std::iota(view.begin(), view.end(), 1);
// Multiply each by 3
std::for_each(view.begin(), view.end(), [](int& val) { val *= 3; });
// Verify
int expected = 3;
for(const int& value : view)
{
EXPECT_EQ(value, expected);
expected += 3;
}
}
// ============================================================================
// Prefix vs Postfix Increment
// ============================================================================
TEST(TestTensorView, PrefixIncrement)
{
Tensor<int> tensor({3});
tensor.setHostValue(10, 0);
tensor.setHostValue(20, 1);
tensor.setHostValue(30, 2);
TensorView<int> view(tensor);
auto it = view.begin();
auto it2 = ++it; // Prefix increment
// Both should point to same element
const int& val1 = *it;
const int& val2 = *it2;
EXPECT_EQ(&val1, &val2);
EXPECT_EQ(val1, 20);
}
TEST(TestTensorView, PostfixIncrement)
{
Tensor<int> tensor({3});
tensor.setHostValue(10, 0);
tensor.setHostValue(20, 1);
tensor.setHostValue(30, 2);
TensorView<int> view(tensor);
auto it = view.begin();
auto it2 = it++; // Postfix increment
// it2 should point to old position
const int& val2 = *it2;
EXPECT_EQ(val2, 10);
// it should point to new position
const int& val1 = *it;
EXPECT_EQ(val1, 20);
}