Newer
Older
minerva / Tests / AK / TestFloatingPointStringConversions.cpp
@minerva minerva on 13 Jul 3 KB Initial commit
/*
 * Copyright (c) 2025, Tomás Simões <tomasprsimoes@tecnico.ulisboa.pt>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#include <AK/FloatingPointStringConversions.h>
#include <AK/UFixedBigInt.h>
#include <LibTest/TestCase.h>

using namespace AK;

enum class ParserType {
    Regular,
    Hexfloat,
    Complete
};

struct FloatingPointTestCase {
    StringView input;
    double expected_value;
    FloatingPointError expected_error;
    int expected_end_offset;
    ParserType parser_type;
};

static void floating_point_parsing_helper(FloatingPointTestCase const& test_case)
{
    auto const* input = test_case.input.characters_without_null_termination();
    size_t const input_length = test_case.input.length();

    switch (test_case.parser_type) {
    case ParserType::Regular: {
        auto result = parse_first_floating_point<double>(input, input + input_length);
        EXPECT_EQ(result.error, test_case.expected_error);
        // If the error is NoOrInvalidInput, parse_first_floating_point might return NaN/null
        if (test_case.expected_error != FloatingPointError::NoOrInvalidInput) {
            EXPECT_EQ(result.value, test_case.expected_value);
            EXPECT_EQ(result.end_ptr, input + test_case.expected_end_offset);
        }
        break;
    }
    case ParserType::Hexfloat: {
        auto result = parse_first_hexfloat_until_zero_character<double>(input);
        EXPECT_EQ(result.error, test_case.expected_error);
        EXPECT_EQ(result.value, test_case.expected_value);
        EXPECT_EQ(result.end_ptr, input + test_case.expected_end_offset);
        break;
    }
    case ParserType::Complete: {
        auto result = parse_floating_point_completely<double>(input, input + input_length);
        if (test_case.expected_error == FloatingPointError::None) {
            EXPECT(result.has_value());
            EXPECT_EQ(result.value(), test_case.expected_value);
        } else {
            EXPECT(!result.has_value());
        }
        break;
    }
    }
}

TEST_CASE(basic_integer)
{
    floating_point_parsing_helper({ "123"sv, 123.0, FloatingPointError::None, 3, ParserType::Regular });
}

TEST_CASE(decimal_number)
{
    floating_point_parsing_helper({ "123.45"sv, 123.45, FloatingPointError::None, 6, ParserType::Regular });
}

TEST_CASE(exponent_notation)
{
    floating_point_parsing_helper({ "1.5e3"sv, 1500.0, FloatingPointError::None, 5, ParserType::Regular });
}

TEST_CASE(negative_number)
{
    floating_point_parsing_helper({ "-67.89"sv, -67.89, FloatingPointError::None, 6, ParserType::Regular });
}

TEST_CASE(zero)
{
    floating_point_parsing_helper({ "0"sv, 0.0, FloatingPointError::None, 1, ParserType::Regular });
}

TEST_CASE(negative_zero)
{
    floating_point_parsing_helper({ "-0.0"sv, -0.0, FloatingPointError::None, 4, ParserType::Regular });
}

TEST_CASE(invalid_input)
{
    floating_point_parsing_helper({ "abc"sv, 0.0, FloatingPointError::NoOrInvalidInput, 0, ParserType::Regular });
}

TEST_CASE(partial_parse)
{
    floating_point_parsing_helper({ "123.45abc"sv, 123.45, FloatingPointError::None, 6, ParserType::Regular });
}

TEST_CASE(hex_float)
{
    floating_point_parsing_helper({ "0x1.8p1"sv, 3.0, FloatingPointError::None, 7, ParserType::Hexfloat });
}

TEST_CASE(out_of_range)
{
    floating_point_parsing_helper({ "1e309"sv, __builtin_huge_val(), FloatingPointError::OutOfRange, 5, ParserType::Regular });
}

TEST_CASE(rounded_down_to_zero)
{
    floating_point_parsing_helper({ "1e-400"sv, 0.0, FloatingPointError::RoundedDownToZero, 6, ParserType::Regular });
}

TEST_CASE(parse_completely_valid)
{
    floating_point_parsing_helper({ "123.45"sv, 123.45, FloatingPointError::None, 0, ParserType::Complete });
}

TEST_CASE(parse_completely_invalid)
{
    floating_point_parsing_helper({ "123.45a"sv, 0.0, FloatingPointError::NoOrInvalidInput, 0, ParserType::Complete });
}