| /* |
| * 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)); |
| } |