// Copyright 2016 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/base/parse_number.h" #include #include #include "base/strings/string_number_conversions.h" #include "testing/gtest/include/gtest/gtest.h" namespace net { namespace { // Returns a decimal string that is one larger than the maximum value that type // T can represent. template std::string CreateOverflowString() { const T value = std::numeric_limits::max(); std::string result = base::NumberToString(value); EXPECT_NE('9', result.back()); result.back()++; return result; } // Returns a decimal string that is one less than the minimum value that // (signed) type T can represent. template std::string CreateUnderflowString() { EXPECT_TRUE(std::numeric_limits::is_signed); const T value = std::numeric_limits::min(); std::string result = base::NumberToString(value); EXPECT_EQ('-', result.front()); EXPECT_NE('9', result.back()); result.back()++; return result; } // These are potentially valid inputs, along with whether they're non-negative // or "strict" (minimal representations). const struct { const char* input; int expected_output; bool is_non_negative; bool is_strict; } kAnnotatedTests[] = { {"0", 0, /*is_non_negative=*/true, /*is_strict=*/true}, {"10", 10, /*is_non_negative=*/true, /*is_strict=*/true}, {"1234566", 1234566, /*is_non_negative=*/true, /*is_strict=*/true}, {"00", 0, /*is_non_negative=*/true, /*is_strict=*/false}, {"010", 10, /*is_non_negative=*/true, /*is_strict=*/false}, {"0010", 10, /*is_non_negative=*/true, /*is_strict=*/false}, {"-10", -10, /*is_non_negative=*/false, /*is_strict=*/true}, {"-1234566", -1234566, /*is_non_negative=*/false, /*is_strict=*/true}, {"-0", 0, /*is_non_negative=*/false, /*is_strict=*/false}, {"-00", 0, /*is_non_negative=*/false, /*is_strict=*/false}, {"-010", -10, /*is_non_negative=*/false, /*is_strict=*/false}, {"-0000000000000000000000000000000000001234566", -1234566, /*is_non_negative=*/false, /*is_strict=*/false}, }; // These are invalid inputs that can not be parsed regardless of the format // used (they are neither valid negative or non-negative values). const char* kInvalidParseTests[] = { "", "-", "--", "23-", "134-34", "- ", " ", "+42", " 123", "123 ", "123\n", "0xFF", "-0xFF", "0x11", "-0x11", "x11", "-x11", "F11", "-F11", "AF", "-AF", "0AF", "0.0", "13.", "13,000", "13.000", "13/5", "Inf", "NaN", "null", "dog", }; // This wrapper calls func() and expects the result to match |expected_output|. template void ExpectParseIntSuccess(ParseFunc func, std::string_view input, ParseIntFormat format, ExpectationType expected_output) { // Try parsing without specifying an error output - expecting success. OutputType parsed_number1; EXPECT_TRUE(func(input, format, &parsed_number1, nullptr)) << "Failed to parse: " << input; EXPECT_EQ(static_cast(expected_output), parsed_number1); // Try parsing with an error output - expecting success. ParseIntError kBogusError = static_cast(19); ParseIntError error = kBogusError; OutputType parsed_number2; EXPECT_TRUE(func(input, format, &parsed_number2, &error)) << "Failed to parse: " << input; EXPECT_EQ(static_cast(expected_output), parsed_number2); // Check that the error output was not written to. EXPECT_EQ(kBogusError, error); } // This wrapper calls func() and expects the failure to match |expected_error|. template void ExpectParseIntFailure(ParseFunc func, std::string_view input, ParseIntFormat format, ParseIntError expected_error) { const OutputType kBogusOutput(23614); // Try parsing without specifying an error output - expecting failure. OutputType parsed_number1 = kBogusOutput; EXPECT_FALSE(func(input, format, &parsed_number1, nullptr)) << "Succeded parsing: " << input; EXPECT_EQ(kBogusOutput, parsed_number1) << "Modified output when failed parsing"; // Try parsing with an error output - expecting failure. OutputType parsed_number2 = kBogusOutput; ParseIntError error; EXPECT_FALSE(func(input, format, &parsed_number2, &error)) << "Succeded parsing: " << input; EXPECT_EQ(kBogusOutput, parsed_number2) << "Modified output when failed parsing"; EXPECT_EQ(expected_error, error); } // Common tests for both ParseInt*() and ParseUint*() // // When testing ParseUint*() the |format| parameter is not applicable and // should be passed as NON_NEGATIVE. template void TestParseIntUsingFormat(ParseFunc func, ParseIntFormat format) { bool is_format_non_negative = format == ParseIntFormat::NON_NEGATIVE || format == ParseIntFormat::STRICT_NON_NEGATIVE; bool is_format_strict = format == ParseIntFormat::STRICT_NON_NEGATIVE || format == ParseIntFormat::STRICT_OPTIONALLY_NEGATIVE; // Test annotated inputs, some of which may not be valid inputs when parsed // using `format`. for (const auto& test : kAnnotatedTests) { SCOPED_TRACE(test.input); if ((test.is_non_negative || !is_format_non_negative) && (test.is_strict || !is_format_strict)) { ExpectParseIntSuccess(func, test.input, format, test.expected_output); } else { ExpectParseIntFailure(func, test.input, format, ParseIntError::FAILED_PARSE); } } // Test invalid inputs (invalid regardless of parsing format) for (auto* input : kInvalidParseTests) { ExpectParseIntFailure(func, input, format, ParseIntError::FAILED_PARSE); } // Test parsing the largest possible value for output type. { const T value = std::numeric_limits::max(); ExpectParseIntSuccess(func, base::NumberToString(value), format, value); } // Test parsing a number one larger than the output type can accomodate // (overflow). ExpectParseIntFailure(func, CreateOverflowString(), format, ParseIntError::FAILED_OVERFLOW); // Test parsing a number at least as large as the output allows AND contains // garbage at the end. This exercises an interesting internal quirk of // base::StringToInt*(), in that its result cannot distinguish this case // from overflow. ExpectParseIntFailure( func, base::NumberToString(std::numeric_limits::max()) + " ", format, ParseIntError::FAILED_PARSE); ExpectParseIntFailure(func, CreateOverflowString() + " ", format, ParseIntError::FAILED_PARSE); // Test parsing the smallest possible value for output type. Don't do the // test for unsigned types since the smallest number 0 is tested elsewhere. if (std::numeric_limits::is_signed) { const T value = std::numeric_limits::min(); std::string str_value = base::NumberToString(value); // The minimal value is necessarily negative, since this function is // testing only signed output types. if (is_format_non_negative) { ExpectParseIntFailure(func, str_value, format, ParseIntError::FAILED_PARSE); } else { ExpectParseIntSuccess(func, str_value, format, value); } } // Test parsing a number one less than the output type can accomodate // (underflow). if (!is_format_non_negative) { ExpectParseIntFailure(func, CreateUnderflowString(), format, ParseIntError::FAILED_UNDERFLOW); } // Test parsing a string that contains a valid number followed by a NUL // character. ExpectParseIntFailure(func, std::string_view("123\0", 4), format, ParseIntError::FAILED_PARSE); } // Common tests to run for each of the versions of ParseInt*(). // // The `func` parameter should be a function pointer to the particular // ParseInt*() function to test. template void TestParseInt(ParseFunc func) { // Test using each of the possible formats. ParseIntFormat kFormats[] = {ParseIntFormat::NON_NEGATIVE, ParseIntFormat::OPTIONALLY_NEGATIVE, ParseIntFormat::STRICT_NON_NEGATIVE, ParseIntFormat::STRICT_OPTIONALLY_NEGATIVE}; for (const auto& format : kFormats) { TestParseIntUsingFormat(func, format); } } // Common tests to run for each of the versions of ParseUint*(). // // The `func` parameter should be a function pointer to the particular // ParseUint*() function to test. template void TestParseUint(ParseFunc func) { // Test using each of the possible formats. ParseIntFormat kFormats[] = { ParseIntFormat::NON_NEGATIVE, ParseIntFormat::STRICT_NON_NEGATIVE, }; for (const auto& format : kFormats) { TestParseIntUsingFormat(func, format); } } TEST(ParseNumberTest, ParseInt32) { TestParseInt(ParseInt32); } TEST(ParseNumberTest, ParseInt64) { TestParseInt(ParseInt64); } TEST(ParseNumberTest, ParseUint32) { TestParseUint(ParseUint32); } TEST(ParseNumberTest, ParseUint64) { TestParseUint(ParseUint64); } } // namespace } // namespace net