Skip to content

Commit

Permalink
23: Breaks up nearest tests to not be singular test
Browse files Browse the repository at this point in the history
Tests modified:
- floor_test.cpp
- trunc_test.cpp

Both adopts parameterized tests. These help reduce the expect calls and
code duplication, also making it easier to add additional test cases in
the future. NaN tests are still inside of the original TEST function.

Since the tests seemed to be directly comparing the function under test
with the standard library equivalent, adjusted the `std::signbit` and
`std::isnan` comparisons to directly compare equality in the NaN tests.
  • Loading branch information
khurd21 committed Jul 5, 2024
1 parent 6f41601 commit fba76e5
Show file tree
Hide file tree
Showing 2 changed files with 230 additions and 120 deletions.
112 changes: 72 additions & 40 deletions test/nearest/floor_test.cpp
Original file line number Diff line number Diff line change
@@ -1,50 +1,82 @@
/*
* Copyright (c) 2024-Present Ian Pike
* Copyright (c) 2024-Present ccmath contributors
*
* This library is provided under the MIT License.
* See LICENSE for more information.
*/
* Copyright (c) 2024-Present Ian Pike
* Copyright (c) 2024-Present ccmath contributors
*
* This library is provided under the MIT License.
* See LICENSE for more information.
*/

#include <gtest/gtest.h>

#include "ccmath/ccmath.hpp"
#include <cmath>
#include <limits>
#include "ccmath/ccmath.hpp"

TEST(CcmathNearestTests, Floor)
namespace
{
EXPECT_EQ(ccm::floor(1.0), std::floor(1.0));
EXPECT_EQ(ccm::floor(1.0000000000000000000000000000000000000000000001), std::floor(1.0000000000000000000000000000000000000000000001));
EXPECT_EQ(ccm::floor(1.5), std::floor(1.5));
EXPECT_EQ(ccm::floor(1.9), std::floor(1.9));
EXPECT_EQ(ccm::floor(-1.0), std::floor(-1.0));
EXPECT_EQ(ccm::floor(-1.5), std::floor(-1.5));
EXPECT_EQ(ccm::floor(-1.9), std::floor(-1.9));
EXPECT_EQ(ccm::floor(std::numeric_limits<double>::infinity()), std::floor(std::numeric_limits<double>::infinity()));
EXPECT_EQ(ccm::floor(-std::numeric_limits<double>::infinity()), std::floor(-std::numeric_limits<double>::infinity()));

bool isCcmPositiveNanPositive = (std::signbit(ccm::floor(std::numeric_limits<double>::quiet_NaN())) == false && std::isnan(ccm::floor(std::numeric_limits<double>::quiet_NaN())) == true); // NOLINT
bool isStdPositiveNanPositive = (std::signbit(std::floor(std::numeric_limits<double>::quiet_NaN())) == false && std::isnan(std::floor(std::numeric_limits<double>::quiet_NaN())) == true); // NOLINT
EXPECT_EQ(isCcmPositiveNanPositive, isStdPositiveNanPositive);

bool isCcmNegativeNanNegative = (std::signbit(ccm::floor(-std::numeric_limits<double>::quiet_NaN())) == true && std::isnan(ccm::floor(-std::numeric_limits<double>::quiet_NaN())) == true); // NOLINT
bool isStdNegativeNanNegative = (std::signbit(std::floor(-std::numeric_limits<double>::quiet_NaN())) == true && std::isnan(std::floor(-std::numeric_limits<double>::quiet_NaN())) == true); // NOLINT
EXPECT_EQ(isCcmNegativeNanNegative, isStdNegativeNanNegative);

bool testThatCcmFloorThatNanReturnsNan = (std::isnan(ccm::floor(std::numeric_limits<double>::quiet_NaN())));
bool testThatCcmFloorThatNegativeNanReturnsNegativeNan = (std::isnan(ccm::floor(-std::numeric_limits<double>::quiet_NaN())));
EXPECT_EQ(testThatCcmFloorThatNanReturnsNan, testThatCcmFloorThatNegativeNanReturnsNegativeNan);

EXPECT_EQ(ccm::floor(0.0), std::floor(0.0));
EXPECT_EQ(ccm::floor(-0.0), std::floor(-0.0));
EXPECT_EQ(ccm::floor(0.5), std::floor(0.5));
EXPECT_EQ(ccm::floor(-0.5), std::floor(-0.5));
EXPECT_EQ(ccm::floor(0.9), std::floor(0.9));
EXPECT_EQ(ccm::floor(-0.9), std::floor(-0.9));
EXPECT_EQ(ccm::floor(0.9999999999999999), std::floor(0.9999999999999999));
EXPECT_EQ(ccm::floor(-0.9999999999999999), std::floor(-0.9999999999999999));
EXPECT_EQ(ccm::floor(1.0000000000000001), std::floor(1.0000000000000001));
EXPECT_EQ(ccm::floor(-1.0000000000000001), std::floor(-1.0000000000000001));

using testing::TestWithParam;
using testing::ValuesIn;

struct FloorTestParams
{
double input{};
double expected{};
};

const std::vector<FloorTestParams> kFloorTestsParams{
// Basic double values
{1.0, std::floor(1.0)},
{1.5, std::floor(1.5)},
{1.9, std::floor(1.9)},
{-1.0, std::floor(-1.0)},
{-1.5, std::floor(-1.5)},
{-1.9, std::floor(-1.9)},

// Zero values
{0.0, std::floor(0.0)},
{-0.0, std::floor(-0.0)},

// Fractional values
{0.5, std::floor(0.5)},
{-0.5, std::floor(-0.5)},
{0.9, std::floor(0.9)},
{-0.9, std::floor(-0.9)},

// Very close to whole numbers
{0.9999999999999999, std::floor(0.9999999999999999)},
{-0.9999999999999999, std::floor(-0.9999999999999999)},
{1.0000000000000001, std::floor(1.0000000000000001)},
{-1.0000000000000001, std::floor(-1.0000000000000001)},
{1.0000000000000000000000000000000000000000000001, std::floor(1.0000000000000000000000000000000000000000000001)},
};

} // namespace

class CcmathFloorTests : public TestWithParam<FloorTestParams>
{
};

INSTANTIATE_TEST_SUITE_P(FloorTests, CcmathFloorTests, ValuesIn(kFloorTestsParams));

TEST_P(CcmathFloorTests, Floor)
{
const auto param{GetParam()};
const auto actual{ccm::floor(param.input)};
EXPECT_EQ(actual, param.expected) << "ccm::floor(" << param.input << ") expected to equal " << param.expected << ". Instead got " << actual << ".";
}

TEST(CcmathNearestTests, CcmFloorTestNanValues)
{
// Check if ccm::floor and std::floor return NaN for positive NaN
EXPECT_EQ(std::isnan(ccm::floor(std::numeric_limits<double>::quiet_NaN())), std::isnan(std::floor(std::numeric_limits<double>::quiet_NaN())));

// Check if ccm::floor and std::floor have the same sign bit for positive NaN
EXPECT_EQ(std::signbit(ccm::floor(std::numeric_limits<double>::quiet_NaN())), std::signbit(std::floor(std::numeric_limits<double>::quiet_NaN())));

// Check if ccm::floor and std::floor return NaN for negative NaN
EXPECT_EQ(std::isnan(ccm::floor(-std::numeric_limits<double>::quiet_NaN())), std::isnan(std::floor(-std::numeric_limits<double>::quiet_NaN())));

// Check if ccm::floor and std::floor have the same sign bit for negative NaN
EXPECT_EQ(std::signbit(ccm::floor(-std::numeric_limits<double>::quiet_NaN())), std::signbit(std::floor(-std::numeric_limits<double>::quiet_NaN())));
}
238 changes: 158 additions & 80 deletions test/nearest/trunc_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,87 +13,165 @@
#include <cmath>
#include <limits>

namespace
{

using testing::TestWithParam;
using testing::ValuesIn;

template <typename T>
struct TruncTestParams
{
T input{};
T expected{};
};

const std::vector<TruncTestParams<double>> kTruncDoubleTestsParams{

// Basic double values
{1.0, std::trunc(1.0)},
{1.5, std::trunc(1.5)},
{1.9, std::trunc(1.9)},
{-1.0, std::trunc(-1.0)},
{-1.5, std::trunc(-1.5)},
{-1.9, std::trunc(-1.9)},

// Infinity values
{std::numeric_limits<double>::infinity(), std::trunc(std::numeric_limits<double>::infinity())},
{-std::numeric_limits<double>::infinity(), std::trunc(-std::numeric_limits<double>::infinity())},

// Zero values
{0.0, std::trunc(0.0)},
{-0.0, std::trunc(-0.0)},

// Fractional values
{0.5, std::trunc(0.5)},
{-0.5, std::trunc(-0.5)},
{0.9, std::trunc(0.9)},
{-0.9, std::trunc(-0.9)},

// Very close to whole numbers
{0.9999999999999999, std::trunc(0.9999999999999999)},
{-0.9999999999999999, std::trunc(-0.9999999999999999)},
{1.0000000000000001, std::trunc(1.0000000000000001)},
{-1.0000000000000001, std::trunc(-1.0000000000000001)}};

const std::vector<TruncTestParams<float>> kTruncFloatTestParams{

// Basic float values
{1.0f, std::truncf(1.0f)},
{1.5f, std::truncf(1.5f)},
{1.9f, std::truncf(1.9f)},
{-1.0f, std::truncf(-1.0f)},
{-1.5f, std::truncf(-1.5f)},
{-1.9f, std::truncf(-1.9f)},

// Infinity values
{std::numeric_limits<float>::infinity(), std::truncf(std::numeric_limits<float>::infinity())},
{-std::numeric_limits<float>::infinity(), std::truncf(-std::numeric_limits<float>::infinity())},

// Zero values
{0.0f, std::truncf(0.0f)},
{-0.0f, std::truncf(-0.0f)},

// Fractional values
{0.5f, std::truncf(0.5f)},
{-0.5f, std::truncf(-0.5f)},
{0.9f, std::truncf(0.9f)},
{-0.9f, std::truncf(-0.9f)},

// Very close to whole numbers
{0.9999999999999999f, std::truncf(0.9999999999999999f)},
{-0.9999999999999999f, std::truncf(-0.9999999999999999f)},

{30.508474576271183309F, std::truncf(30.508474576271183309F)},
};

const std::vector<TruncTestParams<long double>> kTruncLongDoubleTestParams{

// Basic long double values
{1.0L, std::truncl(1.0L)},
{1.5L, std::truncl(1.5L)},
{1.9L, std::truncl(1.9L)},
{-1.0L, std::truncl(-1.0L)},
{-1.5L, std::truncl(-1.5L)},
{-1.9L, std::truncl(-1.9L)},

// Infinity values
{std::numeric_limits<long double>::infinity(), std::truncl(std::numeric_limits<long double>::infinity())},
{-std::numeric_limits<long double>::infinity(), std::truncl(-std::numeric_limits<long double>::infinity())},

// Fractional values
{0.0L, std::truncl(0.0L)},
{-0.0L, std::truncl(-0.0L)},
{0.5L, std::truncl(0.5L)},
{-0.5L, std::truncl(-0.5L)},
{0.9L, std::truncl(0.9L)},
{-0.9L, std::truncl(-0.9L)},

// Very close to whole numbers
{0.9999999999999999L, std::truncl(0.9999999999999999L)},
{-0.9999999999999999L, std::truncl(-0.9999999999999999L)},
};

} // namespace

// TODO: Find a way to test all of the different ways the function may behave internally to work based on the provided compiler.

TEST(CcmathNearestTests, Trunc)
class CcmathTruncDoubleTests : public TestWithParam<TruncTestParams<double>>
{
};
class CcmathTruncFloatTests : public TestWithParam<TruncTestParams<float>>
{
EXPECT_EQ(ccm::trunc(1.0), std::trunc(1.0));
EXPECT_EQ(ccm::trunc(1.5), std::trunc(1.5));
EXPECT_EQ(ccm::trunc(1.9), std::trunc(1.9));
EXPECT_EQ(ccm::trunc(-1.0), std::trunc(-1.0));
EXPECT_EQ(ccm::trunc(-1.5), std::trunc(-1.5));
EXPECT_EQ(ccm::trunc(-1.9), std::trunc(-1.9));
EXPECT_EQ(ccm::trunc(std::numeric_limits<double>::infinity()), std::trunc(std::numeric_limits<double>::infinity()));
EXPECT_EQ(ccm::trunc(-std::numeric_limits<double>::infinity()), std::trunc(-std::numeric_limits<double>::infinity()));
// Google Test is apparently incapable of comparing NaNs or I do not know enough about gtest to find a solution. I've though personally validated that ccm::signbit handles NaNs correctly
//EXPECT_EQ(ccm::trunc(std::numeric_limits<double>::quiet_NaN()), std::trunc(std::numeric_limits<double>::quiet_NaN()));
//EXPECT_EQ(ccm::trunc(-std::numeric_limits<double>::quiet_NaN()), std::trunc(-std::numeric_limits<double>::quiet_NaN()));

// The standard when passed +NaN returns +NaN, and when passed -NaN returns -NaN
bool isCcmPositiveNanPositive = (std::signbit(ccm::trunc(std::numeric_limits<double>::quiet_NaN())) == false && std::isnan(ccm::trunc(std::numeric_limits<double>::quiet_NaN())) == true); // NOLINT
bool isStdPositiveNanPositive = (std::signbit(std::trunc(std::numeric_limits<double>::quiet_NaN())) == false && std::isnan(std::trunc(std::numeric_limits<double>::quiet_NaN())) == true); // NOLINT
EXPECT_EQ(isCcmPositiveNanPositive, isStdPositiveNanPositive);


bool isCcmNegativeNanNegative = (std::signbit(ccm::trunc(-std::numeric_limits<double>::quiet_NaN())) == true && std::isnan(ccm::trunc(-std::numeric_limits<double>::quiet_NaN())) == true); // NOLINT
bool isStdNegativeNanNegative = (std::signbit(std::trunc(-std::numeric_limits<double>::quiet_NaN())) == true && std::isnan(std::trunc(-std::numeric_limits<double>::quiet_NaN())) == true); // NOLINT
EXPECT_EQ(isCcmNegativeNanNegative, isStdNegativeNanNegative);


EXPECT_TRUE(std::isnan(ccm::trunc(std::nan(""))));
EXPECT_TRUE(std::isnan(ccm::trunc(-std::nan(""))));
EXPECT_EQ(ccm::trunc(0.0), std::trunc(0.0));
EXPECT_EQ(ccm::trunc(-0.0), std::trunc(-0.0));
EXPECT_EQ(ccm::trunc(0.5), std::trunc(0.5));
EXPECT_EQ(ccm::trunc(-0.5), std::trunc(-0.5));
EXPECT_EQ(ccm::trunc(0.9), std::trunc(0.9));
EXPECT_EQ(ccm::trunc(-0.9), std::trunc(-0.9));
EXPECT_EQ(ccm::trunc(0.9999999999999999), std::trunc(0.9999999999999999));
EXPECT_EQ(ccm::trunc(-0.9999999999999999), std::trunc(-0.9999999999999999));
EXPECT_EQ(ccm::trunc(1.0000000000000001), std::trunc(1.0000000000000001));
EXPECT_EQ(ccm::trunc(-1.0000000000000001), std::trunc(-1.0000000000000001));

// Test with float using std::truncf and ccm::truncf
EXPECT_EQ(ccm::truncf(1.0F), std::truncf(1.0F));
EXPECT_EQ(ccm::truncf(1.5F), std::truncf(1.5F));
EXPECT_EQ(ccm::truncf(1.9F), std::truncf(1.9F));
EXPECT_EQ(ccm::truncf(-1.0F), std::truncf(-1.0F));
EXPECT_EQ(ccm::truncf(-1.5F), std::truncf(-1.5F));
EXPECT_EQ(ccm::truncf(-1.9F), std::truncf(-1.9F));
EXPECT_EQ(ccm::truncf(std::numeric_limits<float>::infinity()), std::truncf(std::numeric_limits<float>::infinity()));
EXPECT_EQ(ccm::truncf(-std::numeric_limits<float>::infinity()), std::truncf(-std::numeric_limits<float>::infinity()));
EXPECT_TRUE(std::isnan(ccm::truncf(std::nanf(""))));
EXPECT_TRUE(std::isnan(ccm::truncf(-std::nanf(""))));
EXPECT_EQ(ccm::truncf(0.0F), std::truncf(0.0F));
EXPECT_EQ(ccm::truncf(-0.0F), std::truncf(-0.0F));
EXPECT_EQ(ccm::truncf(0.5F), std::truncf(0.5F));
EXPECT_EQ(ccm::truncf(-0.5F), std::truncf(-0.5F));
EXPECT_EQ(ccm::truncf(0.9F), std::truncf(0.9F));
EXPECT_EQ(ccm::truncf(-0.9F), std::truncf(-0.9F));
EXPECT_EQ(ccm::truncf(0.9999999999999999F), std::truncf(0.9999999999999999F));
EXPECT_EQ(ccm::truncf(-0.9999999999999999F), std::truncf(-0.9999999999999999F));

//EXPECT_FLOAT_EQ(ccm::fmod(30.508474576271183309f, 6.1016949152542370172F), std::fmod(30.508474576271183309f, 6.1016949152542370172F));
EXPECT_EQ(ccm::truncf(30.508474576271183309F), std::truncf(30.508474576271183309F));


// Test with long double using std::truncl and ccm::truncl
EXPECT_EQ(ccm::truncl(1.0L), std::truncl(1.0L));
EXPECT_EQ(ccm::truncl(1.5L), std::truncl(1.5L));
EXPECT_EQ(ccm::truncl(1.9L), std::truncl(1.9L));
EXPECT_EQ(ccm::truncl(-1.0L), std::truncl(-1.0L));
EXPECT_EQ(ccm::truncl(-1.5L), std::truncl(-1.5L));
EXPECT_EQ(ccm::truncl(-1.9L), std::truncl(-1.9L));
EXPECT_EQ(ccm::truncl(std::numeric_limits<long double>::infinity()), std::truncl(std::numeric_limits<long double>::infinity()));
EXPECT_EQ(ccm::truncl(-std::numeric_limits<long double>::infinity()), std::truncl(-std::numeric_limits<long double>::infinity()));
EXPECT_TRUE(std::isnan(ccm::truncl(std::nanl(""))));
EXPECT_TRUE(std::isnan(ccm::truncl(-std::nanl(""))));
EXPECT_EQ(ccm::truncl(0.0L), std::truncl(0.0L));
EXPECT_EQ(ccm::truncl(-0.0L), std::truncl(-0.0L));
EXPECT_EQ(ccm::truncl(0.5L), std::truncl(0.5L));
EXPECT_EQ(ccm::truncl(-0.5L), std::truncl(-0.5L));
EXPECT_EQ(ccm::truncl(0.9L), std::truncl(0.9L));
EXPECT_EQ(ccm::truncl(-0.9L), std::truncl(-0.9L));
EXPECT_EQ(ccm::truncl(0.9999999999999999L), std::truncl(0.9999999999999999L));
EXPECT_EQ(ccm::truncl(-0.9999999999999999L), std::truncl(-0.9999999999999999L));
};
class CcmathTruncLongDoubleTests : public TestWithParam<TruncTestParams<long double>>
{
};

INSTANTIATE_TEST_SUITE_P(TruncDoubleTests, CcmathTruncDoubleTests, ValuesIn(kTruncDoubleTestsParams));
INSTANTIATE_TEST_SUITE_P(TruncFloatTests, CcmathTruncFloatTests, ValuesIn(kTruncFloatTestParams));
INSTANTIATE_TEST_SUITE_P(TruncLongDoubleTests, CcmathTruncLongDoubleTests, ValuesIn(kTruncLongDoubleTestParams));

TEST_P(CcmathTruncDoubleTests, Trunc)
{
const auto param{GetParam()};
const auto actual{ccm::trunc(param.input)};
EXPECT_EQ(actual, param.expected) << "ccm::trunc(" << param.input << ") expected to equal " << param.expected << ". Instead got " << actual << ".";
}

TEST_P(CcmathTruncFloatTests, TruncF)
{
const auto param{GetParam()};
const auto actual{ccm::truncf(param.input)};
EXPECT_EQ(actual, param.expected) << "ccm::truncf(" << param.input << ") expected to equal " << param.expected << ". Instead got " << actual << ".";
}

TEST_P(CcmathTruncLongDoubleTests, TruncL)
{
const auto param{GetParam()};
const auto actual{ccm::truncl(param.input)};
EXPECT_EQ(actual, param.expected) << "ccm::truncl(" << param.input << ") expected to equal " << param.expected << ". Instead got " << actual << ".";
}

TEST(CcmathNearestTests, CcmTruncTestNanValues)
{
// Check if ccm::trunc and std::trunc return NaN for positive NaN
EXPECT_EQ(std::isnan(ccm::trunc(std::numeric_limits<double>::quiet_NaN())), std::isnan(std::trunc(std::numeric_limits<double>::quiet_NaN())));

// Check if ccm::trunc and std::trunc have the same sign bit for positive NaN
EXPECT_EQ(std::signbit(ccm::trunc(std::numeric_limits<double>::quiet_NaN())), std::signbit(std::trunc(std::numeric_limits<double>::quiet_NaN())));

// Check if ccm::trunc and std::trunc return NaN for negative NaN
EXPECT_EQ(std::isnan(ccm::trunc(-std::numeric_limits<double>::quiet_NaN())), std::isnan(std::trunc(-std::numeric_limits<double>::quiet_NaN())));

// Check if ccm::trunc and std::trunc have the same sign bit for negative NaN
EXPECT_EQ(std::signbit(ccm::trunc(-std::numeric_limits<double>::quiet_NaN())), std::signbit(std::trunc(-std::numeric_limits<double>::quiet_NaN())));

// Google Test is apparently incapable of comparing NaNs or I do not know enough about gtest to find a solution. I've though personally validated that
// ccm::signbit handles NaNs correctly
// EXPECT_EQ(ccm::trunc(std::numeric_limits<double>::quiet_NaN()), std::trunc(std::numeric_limits<double>::quiet_NaN()));
// EXPECT_EQ(ccm::trunc(-std::numeric_limits<double>::quiet_NaN()), std::trunc(-std::numeric_limits<double>::quiet_NaN()));

EXPECT_TRUE(std::isnan(ccm::trunc(std::nan(""))));
EXPECT_TRUE(std::isnan(ccm::trunc(-std::nan(""))));
}

0 comments on commit fba76e5

Please sign in to comment.