blob: aa97315a9eb78a6773896ce6c1d5d1bb9985c06b [file] [log] [blame]
// Copyright 2019 The Fuchsia 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 <variant>
#include "gtest/gtest.h"
#include "src/developer/debug/zxdb/expr/expr_value.h"
#include "src/developer/debug/zxdb/expr/number_parser.h"
#include "src/lib/fxl/arraysize.h"
#include "src/developer/debug/zxdb/symbols/base_type.h"
namespace zxdb {
TEST(NumberParser, ExtractIntegerPrefix) {
struct PrefixCase {
const char* input; // Input with prefix.
const char* num; // Number without prefix.
IntegerPrefix::Sign sign;
IntegerPrefix::Base base;
IntegerPrefix::OctalType oct;
} kCases[] = {
// clang-format off
// input num sign base oct
{"", "", IntegerPrefix::kPositive, IntegerPrefix::kDec, IntegerPrefix::OctalType::kC},
{"-", "", IntegerPrefix::kNegative, IntegerPrefix::kDec, IntegerPrefix::OctalType::kC},
{"76", "76", IntegerPrefix::kPositive, IntegerPrefix::kDec, IntegerPrefix::OctalType::kC},
{"- 76", "76", IntegerPrefix::kNegative, IntegerPrefix::kDec, IntegerPrefix::OctalType::kC},
{"0b101", "101", IntegerPrefix::kPositive, IntegerPrefix::kBin, IntegerPrefix::OctalType::kC},
{"-0b101", "101", IntegerPrefix::kNegative, IntegerPrefix::kBin, IntegerPrefix::OctalType::kC},
{"0xabc", "abc", IntegerPrefix::kPositive, IntegerPrefix::kHex, IntegerPrefix::OctalType::kC},
{"0o123", "123", IntegerPrefix::kPositive, IntegerPrefix::kOct, IntegerPrefix::OctalType::kRust},
{"0", "0", IntegerPrefix::kPositive, IntegerPrefix::kDec, IntegerPrefix::OctalType::kC},
{"-\t0", "0", IntegerPrefix::kNegative, IntegerPrefix::kDec, IntegerPrefix::OctalType::kC},
{"hello", "hello", IntegerPrefix::kPositive, IntegerPrefix::kDec, IntegerPrefix::OctalType::kC},
// clang-format on
};
for (const auto& cur : kCases) {
std::string_view view(cur.input);
IntegerPrefix prefix = ExtractIntegerPrefix(&view);
EXPECT_EQ(0, view.compare(cur.num)) << "Input " << cur.input;
EXPECT_EQ(cur.sign, prefix.sign) << "Input " << cur.input;
EXPECT_EQ(cur.base, prefix.base) << "Input " << cur.input;
if (cur.base == IntegerPrefix::kOct)
EXPECT_EQ(cur.oct, prefix.octal_type) << "Input " << cur.input;
}
}
TEST(NumberParser, ExtractIntegerSuffix) {
struct SuffixCase {
const char* input; // Input with suffix.
const char* err_msg; // Null for no error.
const char* num; // Number without suffix (when no error).
IntegerSuffix::Signed sign;
IntegerSuffix::Length length;
} kCases[] = {
// clang-format off
// input err_msg num sign length
{"", nullptr, "", IntegerSuffix::kSigned, IntegerSuffix::Length::kInteger},
{"1234", nullptr, "1234", IntegerSuffix::kSigned, IntegerSuffix::Length::kInteger},
{"12l", nullptr, "12", IntegerSuffix::kSigned, IntegerSuffix::Length::kLong},
{"12LL", nullptr, "12", IntegerSuffix::kSigned, IntegerSuffix::Length::kLongLong},
{"13u", nullptr, "13", IntegerSuffix::kUnsigned, IntegerSuffix::Length::kInteger},
{"14ul", nullptr, "14", IntegerSuffix::kUnsigned, IntegerSuffix::Length::kLong},
{"15LU", nullptr, "15", IntegerSuffix::kUnsigned, IntegerSuffix::Length::kLong},
{"16Llu", nullptr, "16", IntegerSuffix::kUnsigned, IntegerSuffix::Length::kLongLong},
{"17ulL", nullptr, "17", IntegerSuffix::kUnsigned, IntegerSuffix::Length::kLongLong},
// Bad number (still extract suffix).
{"taLl", nullptr, "ta", IntegerSuffix::kSigned, IntegerSuffix::Length::kLongLong},
// Error cases.
{"12lul", "Duplicate 'l' or 'll' in number suffix.", "", IntegerSuffix::kSigned, IntegerSuffix::Length::kLong},
{"12uu", "Duplicate 'u' in number suffix.", "", IntegerSuffix::kSigned, IntegerSuffix::Length::kLong},
// clang-format on
};
for (const auto& cur : kCases) {
std::string_view view(cur.input);
IntegerSuffix suffix;
Err err = ExtractIntegerSuffix(&view, &suffix);
if (cur.err_msg) {
// Expected error.
EXPECT_TRUE(err.has_error());
EXPECT_EQ(cur.err_msg, err.msg());
} else {
// No expected error.
EXPECT_FALSE(err.has_error()) << err.msg();
EXPECT_EQ(0, view.compare(cur.num)) << "For input " << cur.input;
EXPECT_EQ(cur.sign, suffix.type_signed) << "For input " << cur.input;
EXPECT_EQ(cur.length, suffix.length) << "For input " << cur.input;
}
}
}
TEST(NumberParser, StringToNumber) {
using ExpectedType = std::variant<int32_t, uint32_t, int64_t, uint64_t>;
struct Case {
const char* input; // Input with suffix.
const char* err_msg; // Null for no error.
ExpectedType expected;
const char* expected_type_name;
} kCases[] = {
// Normal positive input.
{"0", nullptr, int32_t(0), "int"},
{"23", nullptr, int32_t(23), "int"},
{"23u", nullptr, uint32_t(23), "unsigned"},
{"23l", nullptr, int64_t(23), "long"},
{"23ul", nullptr, uint64_t(23), "unsigned long"},
{"23ll", nullptr, int64_t(23), "long long"},
{"23ull", nullptr, uint64_t(23), "unsigned long long"},
// Normal negative input.
{"-0", nullptr, int32_t(0), "int"},
{"-23", nullptr, int32_t(-23), "int"},
{"-23u", nullptr, uint32_t(-23), "unsigned"},
{"-23l", nullptr, int64_t(-23), "long"},
{"-23lu", nullptr, uint64_t(-23), "unsigned long"},
{"-23ll", nullptr, int64_t(-23), "long long"},
{"-23llu", nullptr, uint64_t(-23), "unsigned long long"},
// Hex input.
{"0xabcd", nullptr, int32_t(0xabcd), "int"},
{"- 0x614u", nullptr, uint32_t(-0x614), "unsigned"},
{"0xabcdull", nullptr, uint64_t(0xabcd), "unsigned long long"},
{"0xffffFFFFffffFFFF", nullptr, uint64_t(0xffffFFFFffffFFFF),
"unsigned long"},
// This overflow case gets promoted to "long long" because it's "bigger".
// C++ would put this in a long.
{"-0xffffFFFFffffFFFF", nullptr, uint64_t(-0xffffFFFFffffFFFF),
"unsigned long long"},
// Octal input ("0" prefix disallowed).
{"0o567", nullptr, int32_t(0567), "int"},
{"-0o567", nullptr, int32_t(-0567), "int"},
{"-0o0567llu", nullptr, uint64_t(-0567), "unsigned long long"},
{"0567", "Octal numbers must be prefixed with '0o'.", int32_t(0567),
"int"},
{"-0567llu", "Octal numbers must be prefixed with '0o'.", uint64_t(-0567),
"unsigned long long"},
// Binary input.
{"0b0", nullptr, int32_t(0), "int"},
{"0b110010", nullptr, int32_t(0b110010), "int"},
{"0b110010l", nullptr, int64_t(0b110010), "long"},
{"-0b110010l", nullptr, int64_t(-0b110010), "long"},
// This number is one too large to put in an signed 32-bit type. C++
// would expand this to a "long" unless it was in a non-decimal base, but
// our simpler rules put it in an unsigned since it fits.
{"2147483648", nullptr, uint32_t(2147483648), "unsigned"},
// Long override makes it more clear.
{"2147483648l", nullptr, int64_t(2147483648), "long"},
// Largest 32-bit negative number. Forcing it to unsigned gives the same
// bit pattern and same size type, but in an unsigned type.
{"-2147483648", nullptr, int32_t(-2147483648), "int"},
{"-2147483648u", nullptr, uint32_t(-2147483648u), "unsigned"},
// Some error cases.
{"", "Expected a number.", 0, nullptr},
{"0x56g", "Invalid character in number.", 0, nullptr},
{"0x56 56", "Invalid character in number.", 0, nullptr},
{"0b5", "Invalid character in number.", 0, nullptr},
{"0x0x34", "Invalid character in number.", 0, nullptr},
{"--45", "Invalid character in number.", 0, nullptr},
{"67lll", "Duplicate 'l' or 'll' in number suffix.", 0, nullptr},
};
for (const auto& cur : kCases) {
ExprValue expr_value;
Err err = StringToNumber(cur.input, &expr_value);
if (cur.err_msg) {
// Expected failure.
EXPECT_TRUE(err.has_error()) << " Input = " << cur.input;
EXPECT_EQ(cur.err_msg, err.msg()) << "Input = " << cur.input;
} else {
// Expected success.
EXPECT_FALSE(err.has_error())
<< err.msg() << " for input = " << cur.input;
// This craziness calls the lambda with the given expected type and
// value. We can compare the type of the expected parameter based on the
// test case with the type generated by our code in the expr_value.
std::visit(
[&expr_value, cur](auto expected) {
using ExpectedType = std::decay_t<decltype(expected)>;
const BaseType* expr_type = expr_value.type()->AsBaseType();
ASSERT_TRUE(expr_type) << "Input = " << cur.input;
// Size of types should match.
EXPECT_EQ(sizeof(ExpectedType), expr_value.data().size())
<< "Input = " << cur.input;
// Sign on types should match.
if (std::is_signed_v<ExpectedType>) {
EXPECT_EQ(expr_type->base_type(), BaseType::kBaseTypeSigned)
<< "Input = " << cur.input;
} else {
EXPECT_EQ(expr_type->base_type(), BaseType::kBaseTypeUnsigned)
<< "Input = " << cur.input;
}
// Names of types should match.
EXPECT_EQ(cur.expected_type_name, expr_type->GetFullName())
<< "Input = " << cur.input;
// Values should match.
EXPECT_EQ(expected, expr_value.GetAs<ExpectedType>())
<< "Input = " << cur.input;
},
cur.expected);
}
}
}
} // namespace zxdb