blob: 4b9c926a711051c116fd2997ad65c443fafa353c [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#include <string>
#include "runtime/string-value.inline.h"
#include "testutil/gtest-util.h"
#include "util/cpu-info.h"
#include "common/names.h"
namespace impala {
StringValue FromStdString(const string& str) {
char* ptr = const_cast<char*>(str.c_str());
int len = str.size();
return StringValue(ptr, len);
}
void TestCompareImpl(StringValue* svs, int NUM_STRINGS) {
for (int i = 0; i < NUM_STRINGS; ++i) {
for (int j = 0; j < NUM_STRINGS; ++j) {
if (i == j) {
// Same string
EXPECT_TRUE(svs[i].Eq(svs[j])) << "i=" << i << " j=" << j;
EXPECT_FALSE(svs[i].Ne(svs[j])) << "i=" << i << " j=" << j;
EXPECT_FALSE(svs[i].Lt(svs[j])) << "i=" << i << " j=" << j;
EXPECT_FALSE(svs[i].Gt(svs[j])) << "i=" << i << " j=" << j;
EXPECT_TRUE(svs[i].Le(svs[j])) << "i=" << i << " j=" << j;
EXPECT_TRUE(svs[i].Ge(svs[j])) << "i=" << i << " j=" << j;
EXPECT_TRUE(svs[i].Compare(svs[j]) == 0) << "i=" << i << " j=" << j;
} else if (i < j) {
// svs[i] < svs[j]
EXPECT_FALSE(svs[i].Eq(svs[j])) << "i=" << i << " j=" << j;
EXPECT_TRUE(svs[i].Ne(svs[j])) << "i=" << i << " j=" << j;
EXPECT_TRUE(svs[i].Lt(svs[j])) << "i=" << i << " j=" << j;
EXPECT_FALSE(svs[i].Gt(svs[j])) << "i=" << i << " j=" << j;
EXPECT_TRUE(svs[i].Le(svs[j])) << "i=" << i << " j=" << j;
EXPECT_FALSE(svs[i].Gt(svs[j])) << "i=" << i << " j=" << j;
EXPECT_TRUE(svs[i].Compare(svs[j]) < 0) << "i=" << i << " j=" << j;
} else {
// svs[i] > svs[j]
EXPECT_FALSE(svs[i].Eq(svs[j])) << "i=" << i << " j=" << j;
EXPECT_TRUE(svs[i].Ne(svs[j])) << "i=" << i << " j=" << j;
EXPECT_FALSE(svs[i].Lt(svs[j])) << "i=" << i << " j=" << j;
EXPECT_TRUE(svs[i].Gt(svs[j])) << "i=" << i << " j=" << j;
EXPECT_FALSE(svs[i].Le(svs[j])) << "i=" << i << " j=" << j;
EXPECT_TRUE(svs[i].Gt(svs[j])) << "i=" << i << " j=" << j;
EXPECT_TRUE(svs[i].Compare(svs[j]) > 0) << "i=" << i << " j=" << j;
}
}
}
}
class StringValueTest : public ::testing::Test {
protected:
void SmallifySV(StringValue* sv) { sv->Smallify(); }
void SmallifySVExpect(StringValue* sv, bool expect_to_succeed) {
if (expect_to_succeed) {
EXPECT_TRUE(sv->Smallify());
} else {
EXPECT_FALSE(sv->Smallify());
}
}
void TestLargestSmallerString(StringValue& sv, const string& expected) {
EXPECT_EQ(sv.LargestSmallerString(), expected);
sv.Smallify();
EXPECT_EQ(sv.LargestSmallerString(), expected);
}
void TestLeastLargerString(StringValue& sv, const string& expected) {
EXPECT_EQ(sv.LeastLargerString(), expected);
sv.Smallify();
EXPECT_EQ(sv.LeastLargerString(), expected);
}
};
TEST_F(StringValueTest, TestCompare) {
string empty_str = "";
string str1_str("\0", 1);
string str2_str("\0xy", 3);
string str3_str = "abc";
string str4_str("abc\0def", 7);
string str5_str = "abcdef";
string str6_str = "xyz";
string str7_str("xyz\0", 4);
// Include a few long strings so we test the SSE path
string str8_str("yyyyyyyyyyyyyyyy\0yyyyyyyyyyyyyyyyyy", 35);
string str9_str("yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy", 34);
const int NUM_STRINGS = 10;
// Must be in lexical order
StringValue svs[NUM_STRINGS];
svs[0] = FromStdString(empty_str);
svs[1] = FromStdString(str1_str);
svs[2] = FromStdString(str2_str);
svs[3] = FromStdString(str3_str);
svs[4] = FromStdString(str4_str);
svs[5] = FromStdString(str5_str);
svs[6] = FromStdString(str6_str);
svs[7] = FromStdString(str7_str);
svs[8] = FromStdString(str8_str);
svs[9] = FromStdString(str9_str);
TestCompareImpl(svs, NUM_STRINGS);
for (int i = 0; i < NUM_STRINGS; ++i) {
SmallifySV(&svs[i]);
}
TestCompareImpl(svs, NUM_STRINGS);
}
void TestUnpaddedCharLength(StringValue* chars) {
EXPECT_EQ(StringValue::UnpaddedCharLength(chars[0].Ptr(), 2), 2);
EXPECT_EQ(StringValue::UnpaddedCharLength(chars[1].Ptr(), 4), 2);
EXPECT_EQ(StringValue::UnpaddedCharLength(chars[2].Ptr(), 5), 3);
EXPECT_EQ(StringValue::UnpaddedCharLength(chars[3].Ptr(), 5), 5);
EXPECT_EQ(StringValue::UnpaddedCharLength(chars[4].Ptr(), 1), 0);
EXPECT_EQ(StringValue::UnpaddedCharLength(chars[5].Ptr(), 0), 0);
EXPECT_EQ(StringValue::UnpaddedCharLength(chars[6].Ptr(), 20), 17);
}
TEST_F(StringValueTest, TestCharFunctions) {
string char0_str("hi", 2);
string char1_str("hi ", 4);
string char2_str(" hi ", 5);
string char3_str("12345", 5);
string char4_str(" ", 1);
string char5_str("", 0);
string char6_str(" 0123456789ABCD ");
const int NUM_CHARS = 7;
StringValue chars[NUM_CHARS];
chars[0] = FromStdString(char0_str);
chars[1] = FromStdString(char1_str);
chars[2] = FromStdString(char2_str);
chars[3] = FromStdString(char3_str);
chars[4] = FromStdString(char4_str);
chars[5] = FromStdString(char5_str);
chars[6] = FromStdString(char6_str);
TestUnpaddedCharLength(chars);
for (int i = 0; i < NUM_CHARS; ++i) {
SmallifySV(&chars[i]);
}
TestUnpaddedCharLength(chars);
StringValue::PadWithSpaces(chars[3].Ptr(), 5, 4);
EXPECT_EQ(chars[3].Ptr()[4], ' ');
EXPECT_EQ(chars[3].Ptr()[3], '4');
StringValue::PadWithSpaces(chars[6].Ptr(), 20, 10);
EXPECT_EQ(chars[6].Ptr()[10], ' ');
EXPECT_EQ(chars[6].Ptr()[9], '6');
}
void TestConvertToUInt64Impl(StringValue* svs) {
EXPECT_EQ(svs[0].ToUInt64(), 0);
EXPECT_EQ(svs[1].ToUInt64(), 0x100000000000000);
EXPECT_EQ(svs[2].ToUInt64(), 0x102000000000000);
EXPECT_EQ(svs[3].ToUInt64(), 0x102030000000000);
// extra character(s) does not change the result
EXPECT_EQ(svs[4].ToUInt64(), 0x102030405060707);
EXPECT_EQ(svs[5].ToUInt64(), 0x102030405060707);
EXPECT_EQ(svs[6].ToUInt64(), 0x102030405060707);
}
TEST_F(StringValueTest, TestConvertToUInt64) {
// Test converting StringValues to uint64_t which utilizes up to first 8 bytes.
const int NUM_STRINGS = 7;
string strings[NUM_STRINGS];
strings[0] = "";
strings[1] = "\1";
strings[2] = "\1\2";
strings[3] = "\1\2\3";
strings[4] = "\1\2\3\4\5\6\7\7";
strings[5] = "\1\2\3\4\5\6\7\7\7";
strings[6] = "\1\2\3\4\5\6\7\7\7\7\7\7\7\7\7";
// Must be in lexical order
StringValue svs[NUM_STRINGS];
for (int i = 0; i < NUM_STRINGS; ++i) {
svs[i] = FromStdString(strings[i]);
}
TestConvertToUInt64Impl(svs);
for (int i = 0; i < NUM_STRINGS; ++i) {
SmallifySV(&svs[i]);
}
TestConvertToUInt64Impl(svs);
}
// Test finding the largest smaller strings.
TEST_F(StringValueTest, TestLargestSmallerString) {
string oneKbNullStr(1024, 0x00);
string a1023NullStr(1023, 0x00);
EXPECT_EQ(StringValue(oneKbNullStr).LargestSmallerString(), a1023NullStr);
StringValue asv(const_cast<char*>("\x12\xef"), 2);
TestLargestSmallerString(asv, "\x12\xee");
StringValue bsv(const_cast<char*>("\x12\x00"), 2);
TestLargestSmallerString(bsv, "\x12");
// "0x00" is the smallest non-empty string.
string oneNullStr("\00", 1);
StringValue oneNullStrSv(oneNullStr);
TestLargestSmallerString(oneNullStrSv, "");
// The empty string is the absolute smallest string.
StringValue emptySv(const_cast<char*>(""));
TestLargestSmallerString(emptySv, "");
}
// Test finding the least larger strings.
TEST_F(StringValueTest, TestLeastLargerString) {
string nullStr(const_cast<char*>("\x00"), 1);
StringValue nullStrSv(nullStr);
TestLeastLargerString(nullStrSv, string("\x01", 1));
string a10230xFFStr(1023, 0xff);
string oneKbStr(1023, 0xff);
oneKbStr.append(1, 0x00);
StringValue a10230xFFStrSv(a10230xFFStr);
TestLeastLargerString(a10230xFFStrSv, oneKbStr);
StringValue asv(const_cast<char*>("\x12\xef"), 2);
TestLeastLargerString(asv, "\x12\xf0");
StringValue bsv(const_cast<char*>("\x12\xff"), 2);
TestLeastLargerString(bsv, "\x13");
string smallLimit(11, 0xff);
StringValue smallLimitSv(smallLimit);
string smallLimitLeastLarger = smallLimit + '\0';
TestLeastLargerString(smallLimitSv, smallLimitLeastLarger);
StringValue emptySv(const_cast<char*>(""));
TestLeastLargerString(emptySv, string("\00", 1));
}
TEST_F(StringValueTest, TestConstructors) {
// Test that all strings are non-small initially.
StringValue def_ctor;
EXPECT_FALSE(def_ctor.IsSmall());
StringValue copy_ctor(def_ctor);
EXPECT_FALSE(copy_ctor.IsSmall());
// Modify 'copy_ctor' to make Clang Tidy happy.
SmallifySVExpect(&copy_ctor, true);
StringValue char_ctor(const_cast<char*>("small"));
EXPECT_FALSE(char_ctor.IsSmall());
StringValue char_n(const_cast<char*>("small"), 5);
EXPECT_FALSE(char_n.IsSmall());
string small_str("small");
StringValue string_ctor(small_str);
EXPECT_FALSE(string_ctor.IsSmall());
}
TEST_F(StringValueTest, TestSmallify) {
StringValue nullstr;
StringValue empty(const_cast<char*>(""), 0);
StringValue one_char(const_cast<char*>("a"), 1);
StringValue limit(const_cast<char*>("0123456789A"), 11);
StringValue over_the_limit(const_cast<char*>("0123456789AB"), 12);
StringValue nullstr_clone(nullstr);
StringValue empty_clone(empty);
StringValue one_char_clone(one_char);
StringValue limit_clone(limit);
StringValue over_the_limit_clone(over_the_limit);
SmallifySVExpect(&nullstr, true);
SmallifySVExpect(&empty, true);
SmallifySVExpect(&one_char, true);
SmallifySVExpect(&limit, true);
SmallifySVExpect(&over_the_limit, false);
EXPECT_EQ(nullstr, nullstr_clone);
EXPECT_NE(nullstr.Ptr(), nullstr_clone.Ptr());
EXPECT_EQ(empty, empty_clone);
EXPECT_NE(empty.Ptr(), empty_clone.Ptr());
EXPECT_EQ(one_char, one_char_clone);
EXPECT_NE(one_char.Ptr(), one_char_clone.Ptr());
EXPECT_EQ(limit, limit_clone);
EXPECT_NE(limit.Ptr(), limit_clone.Ptr());
EXPECT_EQ(over_the_limit, over_the_limit_clone);
EXPECT_EQ(over_the_limit.Ptr(), over_the_limit_clone.Ptr());
}
}