/* * libfud * Copyright 2024 Dominick Allen * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "fud_array.hpp" #include "fud_string.hpp" #include "gtest/gtest.h" namespace fud { template auto testStringToIntegerInvalid(Func&& func) { Array backing{"7 13 42 0 08 010 0x08"}; FudString extString{FudString::withBacking(backing)}; StringView view{extString}; StringView invalidView{extString}; T number = 0; ASSERT_EQ(std::forward(func)(view, nullptr, 0, nullptr), ExtNullPointer); invalidView.data = nullptr; ASSERT_EQ(std::forward(func)(invalidView, &number, 1, nullptr), ExtNullPointer); invalidView.data = view.data; invalidView.length = 0; ASSERT_EQ(std::forward(func)(invalidView, &number, 0, nullptr), ExtInvalidInput); ASSERT_EQ(std::forward(func)(view, &number, 1, nullptr), ExtInvalidInput); ASSERT_EQ(std::forward(func)(view, &number, UINT8_MAX, nullptr), ExtInvalidInput); } template auto testStringToNumber( Array& backing, const Array& wordSizes, const Array& statuses, const Array& expectedValues, Func&& func, uint8_t radix) { FudString extString{FudString::withBacking(backing)}; StringView view{extString}; T number = 0; ASSERT_EQ(std::forward(func)(view, &number, radix, nullptr), statuses[0]); ASSERT_EQ(number, expectedValues[0]); size_t index = 0; for (size_t idx = 0; idx < WordCount; ++idx) { T tempNumber{}; if (std::forward(func)(view, &tempNumber, radix, &index) != statuses[idx]) { printf( "どうして? \"%s\" %ld %zu %ld\n", view.data, static_cast(expectedValues[idx]), idx, static_cast(tempNumber)); if (idx > 0) { printf("Test: \"%s\"\n", view.data - index); } } EXPECT_EQ(std::forward(func)(view, &number, radix, &index), statuses[idx]); if (statuses[idx] == ExtSuccess) { ASSERT_EQ(number, expectedValues[idx]); ASSERT_EQ(index, wordSizes[idx]); } view.data += wordSizes[idx]; view.length -= wordSizes[idx]; } } TEST(FudString, StringToUint8Invalid) { testStringToIntegerInvalid(ext_string_to_uint8); } TEST(FudString, StringToUint8RadixUnspec) { Array backing{"7 13 42 0 018 010 0xFF 0xFF0 256"}; constexpr Array wordSizes{ sizeof("7") - 1, sizeof(" 13") - 1, sizeof(" 42") - 1, sizeof(" 0") - 1, sizeof(" 01") - 1, sizeof("8") - 1, sizeof(" 010") - 1, sizeof(" 0xFF") - 1, sizeof(" 0xFF0") - 1, sizeof(" 256") - 1}; constexpr Array expectedValues{7, 13, 42, 0, 1, 8, 8, 0xFF, 0, 0}; constexpr Array statuses{ ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtInvalidInput, ExtInvalidInput}; testStringToNumber(backing, wordSizes, statuses, expectedValues, ext_string_to_uint8, 0); } TEST(FudString, StringToUint8Octal) { Array backing{"7 13 42 0 018 010 377 0xFF0"}; constexpr Array expectedValues{7, 8 + 3, 8 * 4 + 2, 0, 1, 0, 8, 255, 0, 0}; constexpr Array wordSizes{ sizeof("7") - 1, sizeof(" 13") - 1, sizeof(" 42") - 1, sizeof(" 0") - 1, sizeof(" 01") - 1, sizeof("8") - 1, sizeof(" 010") - 1, sizeof(" 377") - 1, sizeof(" 0") - 1, sizeof("xFF0") - 1}; constexpr Array statuses{ ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtInvalidInput, ExtSuccess, ExtSuccess, ExtSuccess, ExtInvalidInput}; testStringToNumber(backing, wordSizes, statuses, expectedValues, ext_string_to_uint8, ExtRadixOctal); } TEST(FudString, StringToUint8Hex) { Array backing{"7 13 0x42 0 018 010 FF 0xFF0"}; constexpr Array expectedValues{7, 16 + 3, 16 * 4 + 2, 0, 16 + 8, 16, 255, 0}; constexpr Array wordSizes{ sizeof("7") - 1, sizeof(" 13") - 1, sizeof(" 0x42") - 1, sizeof(" 0") - 1, sizeof(" 018") - 1, sizeof(" 010") - 1, sizeof(" FF") - 1, sizeof(" 0xFF0") - 1}; constexpr Array statuses{ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtInvalidInput}; testStringToNumber(backing, wordSizes, statuses, expectedValues, ext_string_to_uint8, ExtRadixHexadecimal); } TEST(FudString, StringToUint16Invalid) { testStringToIntegerInvalid(ext_string_to_uint16); } TEST(FudString, StringToUint16RadixUnspec) { Array backing{ "7 65535 0x42 0 010 0xFFFF 0xFFFF0"}; constexpr Array expectedValues{7, 0xFFFF, 0x42, 0, 8, 0xFFFF, 0}; constexpr Array wordSizes{ sizeof("7") - 1, sizeof(" 65535") - 1, sizeof(" 0x42") - 1, sizeof(" 0") - 1, sizeof(" 010") - 1, sizeof(" 0xFFFF") - 1, sizeof(" 0xFFFF0") - 1}; constexpr Array statuses{ ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtInvalidInput}; testStringToNumber(backing, wordSizes, statuses, expectedValues, ext_string_to_uint16, 0); } TEST(FudString, StringToUint16Octal) { Array backing{"7 13 42 0 010 177777 200000"}; constexpr Array expectedValues{7, 8 + 3, 8 * 4 + 2, 0, 8, 0xFFFF, 0}; constexpr Array wordSizes{ sizeof("7") - 1, sizeof(" 13") - 1, sizeof(" 42") - 1, sizeof(" 0") - 1, sizeof(" 010") - 1, sizeof(" 177777") - 1, sizeof(" 200000") - 1}; constexpr Array statuses{ ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtInvalidInput}; testStringToNumber(backing, wordSizes, statuses, expectedValues, ext_string_to_uint16, ExtRadixOctal); } TEST(FudString, StringToUint16Hex) { Array backing{"7 13 0x42 0 018 010 FFFF 0x10000"}; constexpr Array expectedValues{7, 16 + 3, 16 * 4 + 2, 0, 16 + 8, 16, 0xFFFF, 0}; constexpr Array wordSizes{ sizeof("7") - 1, sizeof(" 13") - 1, sizeof(" 0x42") - 1, sizeof(" 0") - 1, sizeof(" 018") - 1, sizeof(" 010") - 1, sizeof(" FFFF") - 1, sizeof(" 200000") - 1}; constexpr Array statuses{ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtInvalidInput}; testStringToNumber(backing, wordSizes, statuses, expectedValues, ext_string_to_uint16, ExtRadixHexadecimal); } TEST(FudString, StringToUint64Hex) { Array backing{"7 13 0x7FFFFFFFFFFFFFFF 0 018 010 -8000000000000000 0xFFFFFFFFFFFFFFFF -8000000000000001"}; constexpr Array expectedValues{ 7, 16 + 3, std::numeric_limits::max(), 0, 16 + 8, 16, 0, std::numeric_limits::max(), 0}; constexpr Array wordSizes{ sizeof("7") - 1, sizeof(" 13") - 1, sizeof(" 0x7FFFFFFFFFFFFFFF") - 1, sizeof(" 0") - 1, sizeof(" 018") - 1, sizeof(" 010") - 1, sizeof(" -8000000000000000") - 1, sizeof(" 0xFFFFFFFFFFFFFFFF") - 1, sizeof(" -8000000000000001") - 1}; constexpr Array statuses{ ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtInvalidInput, ExtSuccess, ExtInvalidInput}; testStringToNumber(backing, wordSizes, statuses, expectedValues, ext_string_to_uint64, ExtRadixHexadecimal); } TEST(FudString, StringToInt8Invalid) { testStringToIntegerInvalid(ext_string_to_int8); } TEST(FudString, StringToInt8RadixUnspec) { Array backing{"7 13 127 0 018 010 -0x80 0x80"}; constexpr Array expectedValues{7, 13, 127, 0, 0, 8, -0x80, 0}; constexpr Array wordSizes{ sizeof("7") - 1, sizeof(" 13") - 1, sizeof(" 127") - 1, sizeof(" 0") - 1, sizeof(" 018") - 1, sizeof(" 010") - 1, sizeof(" -0x80") - 1, sizeof(" 0x80") - 1}; constexpr Array statuses{ ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtInvalidInput, ExtSuccess, ExtSuccess, ExtInvalidInput}; testStringToNumber(backing, wordSizes, statuses, expectedValues, ext_string_to_int8, 0); } TEST(FudString, StringToInt8Octal) { Array backing{"7 13 177 0 018 010 -200 0xFF0 -201"}; constexpr Array expectedValues{7, 8 + 3, 127, 0, 0, 8, -128, 0}; constexpr Array wordSizes{ sizeof("7") - 1, sizeof(" 13") - 1, sizeof(" 177") - 1, sizeof(" 0") - 1, sizeof(" 018") - 1, sizeof(" 010") - 1, sizeof(" -200") - 1, sizeof(" 0xFF0") - 1, sizeof(" -201") - 1}; constexpr Array statuses{ ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtInvalidInput, ExtSuccess, ExtSuccess, ExtInvalidInput, ExtInvalidInput}; testStringToNumber(backing, wordSizes, statuses, expectedValues, ext_string_to_int8, ExtRadixOctal); } TEST(FudString, StringToInt8Hex) { Array backing{"7 13 0x7F 0 018 010 -80 0xFF0"}; constexpr Array expectedValues{7, 16 + 3, 127, 0, 16 + 8, 16, -128, 0}; constexpr Array wordSizes{ sizeof("7") - 1, sizeof(" 13") - 1, sizeof(" 0x7F") - 1, sizeof(" 0") - 1, sizeof(" 018") - 1, sizeof(" 010") - 1, sizeof(" -80") - 1, sizeof(" 0xFF0") - 1}; constexpr Array statuses{ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtInvalidInput}; testStringToNumber(backing, wordSizes, statuses, expectedValues, ext_string_to_int8, ExtRadixHexadecimal); } TEST(FudString, StringToInt32Decimal) { Array backing{"01 13 0x7F 0 018 010 -80 0xFF0"}; constexpr Array expectedValues{1, 13, 0, 0, 18, 10, -80, 0}; constexpr Array wordSizes{ sizeof("01") - 1, sizeof(" 13") - 1, sizeof(" 0x7F") - 1, sizeof(" 0") - 1, sizeof(" 018") - 1, sizeof(" 010") - 1, sizeof(" -80") - 1, sizeof(" 0xFF0") - 1}; constexpr Array statuses{ExtSuccess, ExtSuccess, ExtInvalidInput, ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtInvalidInput}; testStringToNumber(backing, wordSizes, statuses, expectedValues, ext_string_to_int8, ExtRadixDecimal); } TEST(FudString, StringToInt64Hex) { Array backing{"+7 13 0x7FFFFFFFFFFFFFFF 0 018 010 -8000000000000000 0x8000000000000000 -8000000000000001"}; constexpr Array expectedValues{ 7, 16 + 3, std::numeric_limits::max(), 0, 16 + 8, 16, std::numeric_limits::min(), 0, 0}; constexpr Array wordSizes{ sizeof("+7") - 1, sizeof(" 13") - 1, sizeof(" 0x7FFFFFFFFFFFFFFF") - 1, sizeof(" 0") - 1, sizeof(" 018") - 1, sizeof(" 010") - 1, sizeof(" -8000000000000000") - 1, sizeof(" 0x8000000000000000") - 1, sizeof(" -8000000000000001") - 1}; constexpr Array statuses{ ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtSuccess, ExtInvalidInput, ExtInvalidInput}; testStringToNumber(backing, wordSizes, statuses, expectedValues, ext_string_to_int64, ExtRadixHexadecimal); } template struct TestStringToFloatParams { T value{0.0}; size_t wordSize{0}; ExtStatus status{ExtInvalidInput}; bool isNaN{false}; }; template auto testStringToFloat( Array& backing, const Array, WordCount>& testParams, Func&& func) { FudString extString{FudString::withBacking(backing)}; StringView view{extString}; T number = 0; ASSERT_EQ(std::forward(func)(view, &number, nullptr), testParams[0].status); ASSERT_EQ(number, testParams[0].value); size_t index = 0; for (size_t idx = 0; idx < WordCount; ++idx) { ASSERT_EQ(std::forward(func)(view, &number, &index), testParams[idx].status); if (testParams[idx].status == ExtSuccess) { if (!testParams[idx].isNaN) { ASSERT_EQ(number, testParams[idx].value); } else { ASSERT_NE(number, number); } ASSERT_EQ(index, testParams[idx].wordSize); } view.data += testParams[idx].wordSize; view.length -= testParams[idx].wordSize; } } TEST(FudString, StringToFloat) { Array backing{ "123 -7 123.5 .25 0.25 -.125 -2.0625 NaN -inf inF"}; /* N.B. In the following expressions, sizeof includes null, but excludes one leading space. */ constexpr Array, 10> testParams{ {{123.0, sizeof("123") - 1, ExtSuccess}, {-7.0, sizeof("-7"), ExtSuccess}, {123.5, sizeof("123.5"), ExtSuccess}, {.25, sizeof(".25"), ExtSuccess}, {.25, sizeof("0.25"), ExtSuccess}, {-.125, sizeof("-.125"), ExtSuccess}, {-2.0625, sizeof("-2.0625"), ExtSuccess}, {std::numeric_limits::quiet_NaN(), sizeof("NaN"), ExtSuccess, true}, {-1.0F * std::numeric_limits::infinity(), sizeof("-inf"), ExtSuccess}, {std::numeric_limits::infinity(), sizeof("inF"), ExtSuccess}}}; testStringToFloat(backing, testParams, ext_string_to_float); } TEST(FudString, StringToDouble) { Array backing{ "123 -7 123.5 .25 0.25 -.125 -2.0625 NaN -inf inF"}; /* N.B. In the following expressions, sizeof includes null, but excludes one leading space. */ constexpr Array, 10> testParams{ {{123.0, sizeof("123") - 1, ExtSuccess}, {-7.0, sizeof("-7"), ExtSuccess}, {123.5, sizeof("123.5"), ExtSuccess}, {.25, sizeof(".25"), ExtSuccess}, {.25, sizeof("0.25"), ExtSuccess}, {-.125, sizeof("-.125"), ExtSuccess}, {-2.0625, sizeof("-2.0625"), ExtSuccess}, {std::numeric_limits::quiet_NaN(), sizeof("NaN"), ExtSuccess, true}, {-1.0 * std::numeric_limits::infinity(), sizeof("-inf"), ExtSuccess}, {std::numeric_limits::infinity(), sizeof("inF"), ExtSuccess}}}; testStringToFloat(backing, testParams, ext_string_to_double); } TEST(FudString, StringToDoubleExpt) { Array backing{ "1.25e01 1.25e+01 0.125e2 12.5e0 125.e-1 125e-1 1250.e-02 1250.0e-02"}; /* N.B. In the following expressions, sizeof includes null, but excludes one leading space. */ constexpr Array, 8> testParams{ {{12.5, sizeof("1.25e01") - 1, ExtSuccess}, {12.5, sizeof("1.25e+01"), ExtSuccess}, {12.5, sizeof("0.125e2"), ExtSuccess}, {12.5, sizeof("12.5e0"), ExtSuccess}, {12.5, sizeof("125.e-1"), ExtSuccess}, {12.5, sizeof("125e-1"), ExtSuccess}, {12.5, sizeof("1250.e-02"), ExtSuccess}, {12.5, sizeof("1250.0e-02"), ExtSuccess} }}; testStringToFloat(backing, testParams, ext_string_to_double); } } // namespace fud