Vec3/tests/unit_tests/vec_test.cpp

342 lines
11 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "vec3.h"
#include <cmath>
#include <gtest/gtest.h>
#include <limits>
// Test fixture for Vec3 tests
class Vec3Test : public ::testing::Test {
protected:
// Test vectors that will be used across multiple tests
Vec3<int> int_vec1, int_vec2;
Vec3<float> float_vec1, float_vec2;
Vec3<double> double_vec1, double_vec2;
void SetUp() override {
// Initialize test vectors
int_vec1 = {1, 2, 3};
int_vec2 = {4, 5, 6};
float_vec1 = {1.5f, 2.5f, 3.5f};
float_vec2 = {4.5f, 5.5f, 6.5f};
double_vec1 = {1.5, 2.5, 3.5};
double_vec2 = {4.5, 5.5, 6.5};
}
};
// Test vector addition
TEST_F(Vec3Test, Addition) {
// Test integer vectors
auto int_result = int_vec1 + int_vec2;
EXPECT_EQ(int_result.x, 5);
EXPECT_EQ(int_result.y, 7);
EXPECT_EQ(int_result.z, 9);
// Test float vectors
auto float_result = float_vec1 + float_vec2;
EXPECT_FLOAT_EQ(float_result.x, 6.0f);
EXPECT_FLOAT_EQ(float_result.y, 8.0f);
EXPECT_FLOAT_EQ(float_result.z, 10.0f);
// Test double vectors
auto double_result = double_vec1 + double_vec2;
EXPECT_DOUBLE_EQ(double_result.x, 6.0);
EXPECT_DOUBLE_EQ(double_result.y, 8.0);
EXPECT_DOUBLE_EQ(double_result.z, 10.0);
}
// Test vector subtraction
TEST_F(Vec3Test, Subtraction) {
// Test integer vectors
auto int_result = int_vec2 - int_vec1;
EXPECT_EQ(int_result.x, 3);
EXPECT_EQ(int_result.y, 3);
EXPECT_EQ(int_result.z, 3);
// Test float vectors
auto float_result = float_vec2 - float_vec1;
EXPECT_FLOAT_EQ(float_result.x, 3.0f);
EXPECT_FLOAT_EQ(float_result.y, 3.0f);
EXPECT_FLOAT_EQ(float_result.z, 3.0f);
// Test double vectors
auto double_result = double_vec2 - double_vec1;
EXPECT_DOUBLE_EQ(double_result.x, 3.0);
EXPECT_DOUBLE_EQ(double_result.y, 3.0);
EXPECT_DOUBLE_EQ(double_result.z, 3.0);
}
// Test vector scaling
TEST_F(Vec3Test, Scale) {
// Test integer scaling
Vec3<int> int_vec_scaled = int_vec1.scale(2);
EXPECT_EQ(int_vec_scaled.x, 2);
EXPECT_EQ(int_vec_scaled.y, 4);
EXPECT_EQ(int_vec_scaled.z, 6);
// Test float scaling
Vec3<float> float_vec_scaled = float_vec1.scale(2.0f);
EXPECT_FLOAT_EQ(float_vec_scaled.x, 3.0f);
EXPECT_FLOAT_EQ(float_vec_scaled.y, 5.0f);
EXPECT_FLOAT_EQ(float_vec_scaled.z, 7.0f);
// Test double scaling
Vec3<double> double_vec_scaled = double_vec1.scale(2.0);
EXPECT_DOUBLE_EQ(double_vec_scaled.x, 3.0);
EXPECT_DOUBLE_EQ(double_vec_scaled.y, 5.0);
EXPECT_DOUBLE_EQ(double_vec_scaled.z, 7.0);
// Test scaling by zero
Vec3<float> zero_scaled = float_vec1.scale(0.0f);
EXPECT_FLOAT_EQ(zero_scaled.x, 0.0f);
EXPECT_FLOAT_EQ(zero_scaled.y, 0.0f);
EXPECT_FLOAT_EQ(zero_scaled.z, 0.0f);
// Test scaling by negative number
Vec3<int> neg_scaled = int_vec1.scale(-1);
EXPECT_EQ(neg_scaled.x, -1);
EXPECT_EQ(neg_scaled.y, -2);
EXPECT_EQ(neg_scaled.z, -3);
}
// Test dot product
TEST_F(Vec3Test, DotProduct) {
// Test integer dot product
int int_dot = int_vec1.dot(int_vec2);
EXPECT_EQ(int_dot, 32); // 1*4 + 2*5 + 3*6 = 4 + 10 + 18 = 32
// Test float dot product
float float_dot = float_vec1.dot(float_vec2);
EXPECT_FLOAT_EQ(
float_dot,
43.25f); // 1.5*4.5 + 2.5*5.5 + 3.5*6.5 = 6.75 + 13.75 + 22.75 = 43.25
// Test double dot product
double double_dot = double_vec1.dot(double_vec2);
EXPECT_DOUBLE_EQ(double_dot, 43.25); // Same calculation as float
// Test dot product with self (should equal squared length)
int self_dot = int_vec1.dot(int_vec1);
EXPECT_EQ(self_dot, 14); // 1*1 + 2*2 + 3*3 = 1 + 4 + 9 = 14
// Test dot product with zero vector
Vec3<int> zero_vec = {0, 0, 0};
EXPECT_EQ(int_vec1.dot(zero_vec), 0);
}
// Test cross product
TEST_F(Vec3Test, CrossProduct) {
// Test integer cross product
auto int_cross = int_vec1.cross(int_vec2);
EXPECT_EQ(int_cross.x, -3); // (2*6 - 3*5) = 12 - 15 = -3
EXPECT_EQ(int_cross.y, 6); // (3*4 - 1*6) = 12 - 6 = 6
EXPECT_EQ(int_cross.z, -3); // (1*5 - 2*4) = 5 - 8 = -3
// Test float cross product
auto float_cross = float_vec1.cross(float_vec2);
EXPECT_FLOAT_EQ(float_cross.x,
-3.0f); // (2.5*6.5 - 3.5*5.5) = 16.25 - 19.25 = -3
EXPECT_FLOAT_EQ(float_cross.y,
6.0f); // (3.5*4.5 - 1.5*6.5) = 15.75 - 9.75 = 6
EXPECT_FLOAT_EQ(float_cross.z,
-3.0f); // (1.5*5.5 - 2.5*4.5) = 8.25 - 11.25 = -3
// Test double cross product
auto double_cross = double_vec1.cross(double_vec2);
EXPECT_DOUBLE_EQ(double_cross.x, -3.0);
EXPECT_DOUBLE_EQ(double_cross.y, 6.0);
EXPECT_DOUBLE_EQ(double_cross.z, -3.0);
// Test cross product with self (should be zero)
auto self_cross = int_vec1.cross(int_vec1);
EXPECT_EQ(self_cross.x, 0);
EXPECT_EQ(self_cross.y, 0);
EXPECT_EQ(self_cross.z, 0);
// Test cross product of standard basis vectors (i × j = k)
Vec3<int> vec_i = {1, 0, 0};
Vec3<int> vec_j = {0, 1, 0};
Vec3<int> vec_k = {0, 0, 1};
auto i_cross_j = vec_i.cross(vec_j);
EXPECT_EQ(i_cross_j.x, 0);
EXPECT_EQ(i_cross_j.y, 0);
EXPECT_EQ(i_cross_j.z, 1);
auto j_cross_k = vec_j.cross(vec_k);
EXPECT_EQ(j_cross_k.x, 1);
EXPECT_EQ(j_cross_k.y, 0);
EXPECT_EQ(j_cross_k.z, 0);
auto k_cross_i = vec_k.cross(vec_i);
EXPECT_EQ(k_cross_i.x, 0);
EXPECT_EQ(k_cross_i.y, 1);
EXPECT_EQ(k_cross_i.z, 0);
}
// Test with edge cases
TEST_F(Vec3Test, EdgeCases) {
// Test with max values
Vec3<int> max_vec = {std::numeric_limits<int>::max(),
std::numeric_limits<int>::max(),
std::numeric_limits<int>::max()};
// Addition with max values may overflow, but we want to test the operation
// works
max_vec + int_vec1;
// Test with min values
Vec3<int> min_vec = {std::numeric_limits<int>::min(),
std::numeric_limits<int>::min(),
std::numeric_limits<int>::min()};
// Subtraction with min values may underflow, but we want to test the
// operation works
min_vec - int_vec1;
// Test with mixed values
Vec3<double> mixed_vec1 = {0.0, -1.0,
std::numeric_limits<double>::infinity()};
Vec3<double> mixed_vec2 = {-0.0, 1.0,
-std::numeric_limits<double>::infinity()};
auto mixed_addition = mixed_vec1 + mixed_vec2;
// 0.0 + (-0.0) = 0.0
EXPECT_DOUBLE_EQ(mixed_addition.x, 0.0);
// -1.0 + 1.0 = 0.0
EXPECT_DOUBLE_EQ(mixed_addition.y, 0.0);
// inf + (-inf) = NaN
EXPECT_TRUE(std::isnan(mixed_addition.z));
// Test with NaN
Vec3<double> nan_vec = {std::numeric_limits<double>::quiet_NaN(),
std::numeric_limits<double>::quiet_NaN(),
std::numeric_limits<double>::quiet_NaN()};
// Any operation with NaN should result in NaN
auto nan_result = double_vec1 + nan_vec;
EXPECT_TRUE(std::isnan(nan_result.x));
EXPECT_TRUE(std::isnan(nan_result.y));
EXPECT_TRUE(std::isnan(nan_result.z));
}
// Test squared_norm2 and norm2 methods
TEST_F(Vec3Test, NormMethods) {
// Test squared_norm2 for integer vector
EXPECT_DOUBLE_EQ(int_vec1.squared_norm2(), 14.0); // 1*1 + 2*2 + 3*3 = 14
// Test norm2 for integer vector
EXPECT_EQ(int_vec1.norm2(), (int)std::sqrt(14.0)); // √14 ≈ 3.741657...
// Test squared_norm2 for float vector
EXPECT_FLOAT_EQ(
float_vec1.squared_norm2(),
20.75); // 1.5*1.5 + 2.5*2.5 + 3.5*3.5 = 2.25 + 6.25 + 12.25 = 20.75
// Test norm2 for float vector
EXPECT_FLOAT_EQ(float_vec1.norm2(), std::sqrt(20.75)); // √20.75 ≈ 4.5552...
// Test squared_norm2 for double vector
EXPECT_DOUBLE_EQ(double_vec1.squared_norm2(),
20.75); // Same as float calculation
// Test norm2 for double vector
EXPECT_DOUBLE_EQ(double_vec1.norm2(), std::sqrt(20.75));
// Test with zero vector
Vec3<int> zero_vec = {0, 0, 0};
EXPECT_DOUBLE_EQ(zero_vec.squared_norm2(), 0.0);
EXPECT_DOUBLE_EQ(zero_vec.norm2(), 0.0);
// Test with unit vectors
Vec3<double> unit_x = {1.0, 0.0, 0.0};
Vec3<double> unit_y = {0.0, 1.0, 0.0};
Vec3<double> unit_z = {0.0, 0.0, 1.0};
EXPECT_DOUBLE_EQ(unit_x.squared_norm2(), 1.0);
EXPECT_DOUBLE_EQ(unit_x.norm2(), 1.0);
EXPECT_DOUBLE_EQ(unit_y.squared_norm2(), 1.0);
EXPECT_DOUBLE_EQ(unit_y.norm2(), 1.0);
EXPECT_DOUBLE_EQ(unit_z.squared_norm2(), 1.0);
EXPECT_DOUBLE_EQ(unit_z.norm2(), 1.0);
// Test with negative components
Vec3<int> neg_vec = {-1, -2, -3};
EXPECT_DOUBLE_EQ(neg_vec.squared_norm2(), 14.0); // (-1)² + (-2)² + (-3)² = 14
EXPECT_DOUBLE_EQ(neg_vec.norm2(), (int)std::sqrt(14.0));
}
// Test norm methods with special values
TEST_F(Vec3Test, NormEdgeCases) {
// Test with infinity
Vec3<double> inf_vec = {std::numeric_limits<double>::infinity(), 0.0, 0.0};
EXPECT_TRUE(std::isinf(inf_vec.squared_norm2()));
EXPECT_TRUE(std::isinf(inf_vec.norm2()));
// Test with large values that might cause overflow in intermediate
// calculations
double large_val = std::sqrt(std::numeric_limits<double>::max()) / 2.0;
Vec3<double> large_vec = {large_val, large_val, large_val};
// The squared norm should be approximately 3 * large_val²
double expected_squared = 3.0 * large_val * large_val;
EXPECT_DOUBLE_EQ(large_vec.squared_norm2(), expected_squared);
// The norm should be √(3) * large_val
double expected_norm = std::sqrt(3.0) * large_val;
EXPECT_DOUBLE_EQ(large_vec.norm2(), expected_norm);
// Test with NaN values
Vec3<double> nan_vec = {std::numeric_limits<double>::quiet_NaN(), 0.0, 0.0};
EXPECT_TRUE(std::isnan(nan_vec.squared_norm2()));
EXPECT_TRUE(std::isnan(nan_vec.norm2()));
}
// Test vector normalization
TEST_F(Vec3Test, Normalized) {
// Test with integer vector
auto int_normalized = int_vec1.normalized();
int int_norm = std::sqrt(14.0); // sqrt(1^2 + 2^2 + 3^2) = sqrt(14)
EXPECT_EQ(int_normalized.x, (1 / int_norm));
EXPECT_EQ(int_normalized.y, (2 / int_norm));
EXPECT_EQ(int_normalized.z, (3 / int_norm));
// Verify that the normalized vector has length 1.0
EXPECT_NEAR(int_normalized.norm2(), 1.0, 1e-10);
// Test with float vector
auto float_normalized = float_vec1.normalized();
float float_norm = std::sqrt(1.5f * 1.5f + 2.5f * 2.5f + 3.5f * 3.5f);
EXPECT_FLOAT_EQ(float_normalized.x, 1.5f / float_norm);
EXPECT_FLOAT_EQ(float_normalized.y, 2.5f / float_norm);
EXPECT_FLOAT_EQ(float_normalized.z, 3.5f / float_norm);
// Verify that the normalized vector has length 1.0
EXPECT_NEAR(float_normalized.norm2(), 1.0, 1e-6);
// Test with double vector
auto double_normalized = double_vec1.normalized();
double double_norm = std::sqrt(1.5 * 1.5 + 2.5 * 2.5 + 3.5 * 3.5);
EXPECT_DOUBLE_EQ(double_normalized.x, 1.5 / double_norm);
EXPECT_DOUBLE_EQ(double_normalized.y, 2.5 / double_norm);
EXPECT_DOUBLE_EQ(double_normalized.z, 3.5 / double_norm);
// Verify that the normalized vector has length 1.0
EXPECT_DOUBLE_EQ(double_normalized.norm2(), 1.0);
// Test with unit vectors (should remain unchanged)
Vec3<double> unit_x = {1.0, 0.0, 0.0};
auto normalized_unit_x = unit_x.normalized();
EXPECT_DOUBLE_EQ(normalized_unit_x.x, 1.0);
EXPECT_DOUBLE_EQ(normalized_unit_x.y, 0.0);
EXPECT_DOUBLE_EQ(normalized_unit_x.z, 0.0);
}
// Main function that runs the tests
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}