blob: f999c98825cd018286cef7384a4a3868122e72d6 [file] [log] [blame]
/*
* Copyright (c) 2019 The Regents of the University of California
* All rights reserved
*
* The license below extends only to copyright in the software and shall
* not be construed as granting a license to any other intellectual
* property including but not limited to intellectual property relating
* to a hardware implementation of the functionality of the software
* licensed hereunder. You may use the software subject to the license
* terms below provided that you ensure that this notice is replicated
* unmodified and in its entirety in all distributions of the software,
* modified or unmodified, in source code or in binary form.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer;
* redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution;
* neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <gtest/gtest.h>
#include <cstdint>
#include "base/str.hh"
using namespace gem5;
/*
* str.cc has "eat_lead_white", "eat_end_white", and "eat_white" fucntions to
* remove leading and trailing whitespace. The following tests verify this
* behavior.
*/
TEST(StrTest, EatLeadWhite)
{
std::string val = " hello there ";
eat_lead_white(val);
EXPECT_EQ("hello there ", val);
}
TEST(StrTest, EatLeadWhiteNoLeadingWhitespace)
{
std::string val = "hello there ";
eat_lead_white(val);
EXPECT_EQ("hello there ", val);
}
TEST(StrTest, EatEndWhite)
{
std::string val = " hello there ";
eat_end_white(val);
EXPECT_EQ(" hello there", val);
}
TEST(StrTest, EatEndWhiteNoTrailingWhitespace)
{
std::string val = " hello there";
eat_end_white(val);
EXPECT_EQ(" hello there", val);
}
TEST(StrTest, EatWhite)
{
std::string val = " hello there ";
eat_white(val);
EXPECT_EQ("hello there", val);
}
TEST(StrTest, EatWhiteNoWhitespace)
{
std::string val = "hello there";
eat_lead_white(val);
EXPECT_EQ("hello there", val);
}
/*
* This tests checks that str.cc's "to_lower" function converts a string to
* lowercase.
*/
TEST(StrTest, ToLower)
{
std::string val = "gOoDbYe FOO@barr!";
EXPECT_EQ("goodbye foo@barr!", to_lower(val));
}
/*
* str.cc's "split_first" and "split_last" fucntions split a string on a
* character into two parts. "split_first" splits on the first instance of
* this character and "split_last" splits on the last instance of this
* character. The character itself is not included in either of the output
* right-hand side and left-hand side strings. If the character cannot be
* found in the string then the left-hand side string is equal to the input
* string and the right-hand side string is empty.
*/
TEST(StrTest, SplitFirst)
{
std::string val = "abcdefg abcdefg";
std::string lhs;
std::string rhs;
split_first(val , lhs, rhs, 'f');
EXPECT_EQ("abcdefg abcdefg", val);
EXPECT_EQ("abcde", lhs);
EXPECT_EQ("g abcdefg", rhs);
}
TEST(StrTest, SplitFirstNoChar)
{
std::string val = "abcdefg abcdefg";
std::string lhs;
std::string rhs;
split_first(val , lhs, rhs, 'h');
EXPECT_EQ("abcdefg abcdefg", val);
EXPECT_EQ("abcdefg abcdefg", lhs);
EXPECT_EQ("", rhs);
}
TEST(StrTest, SplitFirstOnFirstChar)
{
std::string val = "abcdefg abcdefg";
std::string lhs;
std::string rhs;
split_first(val , lhs, rhs, 'a');
EXPECT_EQ("abcdefg abcdefg", val);
EXPECT_EQ("", lhs);
EXPECT_EQ("bcdefg abcdefg", rhs);
}
TEST(StrTest, SplitLast)
{
std::string val = "abcdefg abcdefg";
std::string lhs;
std::string rhs;
split_last(val , lhs, rhs, 'f');
EXPECT_EQ("abcdefg abcdefg", val);
EXPECT_EQ("abcdefg abcde", lhs);
EXPECT_EQ("g", rhs);
}
TEST(StrTest, SplitLastNoChar)
{
std::string val = "abcdefg abcdefg";
std::string lhs;
std::string rhs;
split_last(val , lhs, rhs, 'h');
EXPECT_EQ("abcdefg abcdefg", val);
EXPECT_EQ("abcdefg abcdefg", lhs);
EXPECT_EQ("", rhs);
}
TEST(StrTest, SplitLastOnLastChar)
{
std::string val = "abcdefg abcdefg";
std::string lhs;
std::string rhs;
split_last(val , lhs, rhs, 'g');
EXPECT_EQ("abcdefg abcdefg", val);
EXPECT_EQ("abcdefg abcdef", lhs);
EXPECT_EQ("", rhs);
}
/*
* str.cc's "tokenize" function splits a string into its constituent tokens.
* It splits based on an input character.
*/
TEST(StrTest, TokenizeOnSpace)
{
/*
* val has a double space between each token with trailing and leading
* whitespace.
*/
std::string val = " Hello, this is a sentence. ";
std::vector<std::string> tokens;
/*
* By default 'ign' is true. This means empty tokens are not included in
* the output list.
*/
tokenize(tokens, val, ' ');
EXPECT_EQ(" Hello, this is a sentence. ", val);
EXPECT_EQ(5, tokens.size());
EXPECT_EQ("Hello,", tokens[0]);
EXPECT_EQ("this", tokens[1]);
EXPECT_EQ("is", tokens[2]);
EXPECT_EQ("a", tokens[3]);
EXPECT_EQ("sentence.", tokens[4]);
}
TEST(StrTest, TokenizeOnSpaceIgnFalse)
{
/*
* val has a double space between each token with trailing and leading
* whitespace.
*/
std::string val = " Hello, this is a sentence. ";
std::vector<std::string> tokens;
tokenize(tokens, val, ' ', false);
EXPECT_EQ(" Hello, this is a sentence. ", val);
EXPECT_EQ(11, tokens.size());
EXPECT_EQ("", tokens[0]);
EXPECT_EQ("Hello,", tokens[1]);
EXPECT_EQ("", tokens[2]);
EXPECT_EQ("this", tokens[3]);
EXPECT_EQ("", tokens[4]);
EXPECT_EQ("is", tokens[5]);
EXPECT_EQ("", tokens[6]);
EXPECT_EQ("a", tokens[7]);
EXPECT_EQ("", tokens[8]);
EXPECT_EQ("sentence.", tokens[9]);
EXPECT_EQ("", tokens[10]);
}
TEST(StrTest, TokenizedTokenDoesNotExist)
{
std::string val = "abcdefg";
std::vector<std::string> tokens;
tokenize(tokens, val, 'h');
EXPECT_EQ("abcdefg", val);
EXPECT_EQ(1, tokens.size());
EXPECT_EQ("abcdefg", tokens[0]);
}
/*
* str.cc's "to_number" function converts a string to a number. The function
* will return false if this is not possible either because the string
* represents a number out-of-range, or because the string cannot be parsed.
*/
TEST(StrTest, ToNumber8BitInt)
{
int8_t output;
std::string input = "-128";
EXPECT_TRUE(to_number(input, output));
EXPECT_EQ(-128, output);
}
TEST(StrTest, ToNumber8BitIntStringOutOfRange)
{
int8_t output;
std::string input = "-129";
EXPECT_FALSE(to_number(input, output));
}
TEST(StrTest, ToNumber8BitIntInvalidString)
{
int8_t output;
std::string input = "onetwoeight";
EXPECT_FALSE(to_number(input, output));
}
TEST(StrTest, ToNumberUnsigned8BitInt)
{
uint8_t output;
std::string input = "255";
EXPECT_TRUE(to_number(input, output));
EXPECT_EQ(255, output);
}
TEST(StrTest, ToNumberUnsigned8BitIntNegative)
{
uint8_t output;
std::string input = "-1";
EXPECT_FALSE(to_number(input, output));
}
/** Test that a double that can be converted to int is always rounded down. */
TEST(StrTest, ToNumberUnsigned8BitIntRoundDown)
{
uint8_t output;
std::string input_1 = "2.99";
EXPECT_TRUE(to_number(input_1, output));
EXPECT_EQ(2, output);
std::string input_2 = "3.99";
EXPECT_TRUE(to_number(input_2, output));
EXPECT_EQ(3, output);
}
/**
* Test that a double can still be converted to int as long as it is below
* the numerical limit + 1.
*/
TEST(StrTest, ToNumber8BitUnsignedLimit)
{
uint8_t output;
std::string input = "255.99";
EXPECT_TRUE(to_number(input, output));
EXPECT_EQ(255, output);
}
/**
* Test that a double cannot be converted to int when it passes the numerical
* limit.
*/
TEST(StrTest, ToNumber8BitUnsignedOutOfRange)
{
uint8_t output;
std::string input = "256.99";
EXPECT_FALSE(to_number(input, output));
}
/** Test that a scientific number cannot be converted to int. */
TEST(StrTest, ToNumberUnsignedScientific)
{
uint32_t output;
std::string input = "8.234e+08";
EXPECT_FALSE(to_number(input, output));
}
/** Test that a negative scientific number cannot be converted to int. */
TEST(StrTest, ToNumberIntScientificNegative)
{
int32_t output;
std::string input = "-8.234e+08";
EXPECT_FALSE(to_number(input, output));
}
TEST(StrTest, ToNumber64BitInt)
{
int64_t output;
int64_t input_number = 0xFFFFFFFFFFFFFFFF;
std::string input = std::to_string(input_number);
EXPECT_TRUE(to_number(input, output));
EXPECT_EQ(input_number, output);
}
TEST(StrTest, ToNumber64BitIntInvalidString)
{
int64_t output;
std::string input = " ";
EXPECT_FALSE(to_number(input, output));
}
TEST(StrTest, ToNumberEnum)
{
enum Number
{
TWO=2,
};
Number output;
std::string input = "2";
EXPECT_TRUE(to_number(input, output));
EXPECT_EQ(TWO, output);
}
/** Test that trying to convert a number to an enum that is not valid fails. */
TEST(StrTest, DISABLED_ToNumberEnumInvalid)
{
enum Number
{
TWO=2,
};
Number output;
std::string input = "3";
EXPECT_FALSE(to_number(input, output));
}
TEST(StrTest, ToNumberFloat)
{
float output;
std::string input = "0.1";
float expected_output = 0.1;
EXPECT_TRUE(to_number(input, output));
EXPECT_EQ(expected_output, output);
}
TEST(StrTest, ToNumberFloatIntegerString)
{
float output;
std::string input = "10";
float expected_output = 10.0;
EXPECT_TRUE(to_number(input, output));
EXPECT_EQ(expected_output, output);
}
TEST(StrTest, ToNumberFloatNegative)
{
float output;
std::string input = "-0.1";
float expected_output = -0.1;
EXPECT_TRUE(to_number(input, output));
EXPECT_EQ(expected_output, output);
}
TEST(StrTest, ToNumberDouble)
{
double output;
std::string input = "0.0001";
double expected_output = 0.0001;
EXPECT_TRUE(to_number(input, output));
EXPECT_EQ(expected_output, output);
}
TEST(StrTest, ToNumberDoubleIntegerString)
{
double output;
std::string input = "12345";
double expected_output = 12345.0;
EXPECT_TRUE(to_number(input, output));
EXPECT_EQ(expected_output, output);
}
TEST(StrTest, ToNumberDoubleNegative)
{
double output;
std::string input = "-1.2345";
double expected_output = -1.2345;
EXPECT_TRUE(to_number(input, output));
EXPECT_EQ(expected_output, output);
}
/** Test that a scientific number is converted properly to double. */
TEST(StrTest, ToNumberScientific)
{
double output;
std::string input = "8.234e+08";
double expected_output = 823400000;
EXPECT_TRUE(to_number(input, output));
EXPECT_EQ(expected_output, output);
}
/*
* The "to_bool" function takes a string, "true" or "false"
* (case-insenstive), and sets the second argument to the bool equivilent.
* The function will return false if it cannot parse the string.
*/
TEST(StrTest, ToBoolTrue)
{
bool output = false;
EXPECT_TRUE(to_bool("TrUe", output));
EXPECT_TRUE(output);
}
TEST(StrTest, ToBoolFalse){
bool output = true;
EXPECT_TRUE(to_bool("fAlSe", output));
EXPECT_FALSE(output);
}
TEST(StrTest, ToBoolInvalidInput)
{
bool output;
EXPECT_FALSE(to_bool("falsify", output));
}
/*
* The "quote" function take a string and returns that string quoted (i.e.,
* between double-quotes) if the string contains a space.
*/
TEST(StrTest, QuoteStringNoSpace)
{
EXPECT_EQ("hello", quote("hello"));
}
TEST(StrTest, QuoteStringWithSpace)
{
EXPECT_EQ("\"hello world\"", quote("hello world"));
}
TEST(StrTest, QuoteQuotedString)
{
/*
* At present, a quoted string can be quoted again.
*/
EXPECT_EQ("\"\"hello world\"\"", quote("\"hello world\""));
}
TEST(StrTest, QuoteStringWithTab)
{
/*
* The "quote" function only works with standard space, not any
* whitepsace.
*/
EXPECT_EQ("hello\tworld", quote("hello\tworld"));
}
/*
* str.hh has three implementations of "startswith"; a function that takes
* string and a prefix and returns true if the string starts with the prefix.
* One implementation takes two strings, another takes two char*, and the
* third takes a string and a char* as a prefix.
*/
TEST(StrTest, StartswithDoubleStringDoesStartWith)
{
std::string s = "Hello, how are you?";
std::string prefix = "Hello";
EXPECT_TRUE(startswith(s, prefix));
}
TEST(StrTest, StartswithDoubleStringDoesNotStartWith)
{
std::string s = "Hello, how are you?";
std::string prefix = "ello";
EXPECT_FALSE(startswith(s, prefix));
}
TEST(StrTest, StartswithDoubleCharArrayDoesStartWith)
{
const char* s = "abcdefg";
const char* prefix = "ab";
EXPECT_TRUE(startswith(s, prefix));
}
TEST(StrTest, StartswithDoubleCharArrayDoesNotStartWith)
{
const char* s = " abcdefg";
const char* prefix = "a";
EXPECT_FALSE(startswith(s, prefix));
}
TEST(StrTest, StartswithStringCharArrayDoesStartWith)
{
std::string s = "foobarr";
const char* prefix = "f";
EXPECT_TRUE(startswith(s, prefix));
}
TEST(StrTest, StartswithStringCharArrayDoesNotStartWith)
{
std::string s = "foobarr";
const char* prefix = "barr";
EXPECT_FALSE(startswith(s, prefix));
}