blob: 45d0adffb6345cae30f8825662aa48b469792fb2 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// 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 <limits>
#include <sstream>
#include "base/strings/string_number_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
namespace {
template <typename T>
std::string ToString(T number) {
// TODO(eroman): Just use std::to_string() instead (Currently chromium's
// C++11 guide hasn't taken a stance on it).
std::stringstream s;
s << number;
return s.str();
}
// Returns a decimal string that is one larger than the maximum value that type
// T can represent.
template <typename T>
std::string CreateOverflowString() {
const T value = std::numeric_limits<T>::max();
std::string result = ToString(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 <typename T>
std::string CreateUnderflowString() {
EXPECT_TRUE(std::numeric_limits<T>::is_signed);
const T value = std::numeric_limits<T>::min();
std::string result = ToString(value);
EXPECT_EQ('-', result.front());
EXPECT_NE('9', result.back());
result.back()++;
return result;
}
// These are valid inputs representing non-negative integers. Note that these
// test inputs are re-used when constructing negative test cases, by simply
// prepending a '-'.
const struct {
const char* input;
int expected_output;
} kValidNonNegativeTests[] = {
{"0", 0}, {"00000", 0}, {"003", 3}, {"003", 3}, {"1234566", 1234566},
{"987", 987}, {"010", 10},
};
// 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 <typename OutputType, typename ParseFunc, typename ExpectationType>
void ExpectParseIntSuccess(ParseFunc func,
const base::StringPiece& 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<OutputType>(expected_output), parsed_number1);
// Try parsing with an error output - expecting success.
ParseIntError kBogusError = static_cast<ParseIntError>(19);
ParseIntError error = kBogusError;
OutputType parsed_number2;
EXPECT_TRUE(func(input, format, &parsed_number2, &error))
<< "Failed to parse: " << input;
EXPECT_EQ(static_cast<OutputType>(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 <typename OutputType, typename ParseFunc>
void ExpectParseIntFailure(ParseFunc func,
const base::StringPiece& 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 <typename T, typename ParseFunc>
void TestParseIntUsingFormat(ParseFunc func, ParseIntFormat format) {
// Test valid non-negative inputs
for (const auto& test : kValidNonNegativeTests) {
ExpectParseIntSuccess<T>(func, test.input, format, test.expected_output);
}
// Test invalid inputs (invalid regardless of parsing format)
for (auto* input : kInvalidParseTests) {
ExpectParseIntFailure<T>(func, input, format, ParseIntError::FAILED_PARSE);
}
// Test valid negative inputs (constructed from the valid non-negative test
// cases).
for (const auto& test : kValidNonNegativeTests) {
std::string negative_input = std::string("-") + test.input;
int expected_negative_output = -test.expected_output;
// The result depends on the format.
if (format == ParseIntFormat::NON_NEGATIVE) {
ExpectParseIntFailure<T>(func, negative_input, format,
ParseIntError::FAILED_PARSE);
} else {
ExpectParseIntSuccess<T>(func, negative_input, format,
expected_negative_output);
}
}
// Test parsing the largest possible value for output type.
{
const T value = std::numeric_limits<T>::max();
ExpectParseIntSuccess<T>(func, ToString(value), format, value);
}
// Test parsing a number one larger than the output type can accomodate
// (overflow).
ExpectParseIntFailure<T>(func, CreateOverflowString<T>(), 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<T>(func, ToString(std::numeric_limits<T>::max()) + " ",
format, ParseIntError::FAILED_PARSE);
ExpectParseIntFailure<T>(func, CreateOverflowString<T>() + " ", 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<T>::is_signed) {
const T value = std::numeric_limits<T>::min();
std::string str_value = ToString(value);
// The minimal value is necessarily negative, since this function is
// testing only signed output types.
if (format == ParseIntFormat::NON_NEGATIVE) {
ExpectParseIntFailure<T>(func, str_value, format,
ParseIntError::FAILED_PARSE);
} else {
ExpectParseIntSuccess<T>(func, str_value, format, value);
}
}
// Test parsing a number one less than the output type can accomodate
// (underflow).
if (format == ParseIntFormat::OPTIONALLY_NEGATIVE) {
ExpectParseIntFailure<T>(func, CreateUnderflowString<T>(),
ParseIntFormat::OPTIONALLY_NEGATIVE,
ParseIntError::FAILED_UNDERFLOW);
}
// Test parsing a string that contains a valid number followed by a NUL
// character.
ExpectParseIntFailure<T>(func, base::StringPiece("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 <typename T, typename ParseFunc>
void TestParseInt(ParseFunc func) {
// Test using each of the possible formats.
ParseIntFormat kFormats[] = {ParseIntFormat::NON_NEGATIVE,
ParseIntFormat::OPTIONALLY_NEGATIVE};
for (const auto& format : kFormats) {
TestParseIntUsingFormat<T>(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 <typename T, typename ParseFunc>
void TestParseUint(ParseFunc func) {
// TestParseIntUsingFormat() expects a functor that has a |format|
// parameter. For ParseUint*() there is no such parameter. For all intents
// and purposes can just fix it to NON_NEGATIVE and re-use that test driver.
auto func_adapter = [&func](const base::StringPiece& input,
ParseIntFormat format, T* output,
ParseIntError* optional_error) {
EXPECT_EQ(ParseIntFormat::NON_NEGATIVE, format);
return func(input, output, optional_error);
};
TestParseIntUsingFormat<T>(func_adapter, ParseIntFormat::NON_NEGATIVE);
}
TEST(ParseNumberTest, ParseInt32) {
TestParseInt<int32_t>(ParseInt32);
}
TEST(ParseNumberTest, ParseInt64) {
TestParseInt<int64_t>(ParseInt64);
}
TEST(ParseNumberTest, ParseUint32) {
TestParseUint<uint32_t>(ParseUint32);
}
TEST(ParseNumberTest, ParseUint64) {
TestParseUint<uint64_t>(ParseUint64);
}
} // namespace
} // namespace net