blob: cd57087507ec13af88bdd93cf622d48d9bfcdfbc [file] [log] [blame]
/*
* The MIT License (MIT)
*
* Copyright (c) 2015 Microsoft Corporation
*
* -=- Robust Distributed System Nucleus (rDSN) -=-
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stddef.h>
#include <list>
#include <map>
#include <set>
#include <string>
#include <tuple>
#include <type_traits>
#include <unordered_set>
#include <utility>
#include <vector>
#include "gtest/gtest.h"
#include "utils/autoref_ptr.h"
#include "utils/binary_reader.h"
#include "utils/binary_writer.h"
#include "utils/crc.h"
#include "utils/link.h"
#include "utils/rand.h"
#include "utils/strings.h"
#include "utils/utils.h"
namespace dsn {
namespace utils {
TEST(core, get_last_component)
{
ASSERT_EQ("a", get_last_component("a", "/"));
ASSERT_EQ("b", get_last_component("a/b", "/"));
ASSERT_EQ("b", get_last_component("a//b", "/"));
ASSERT_EQ("", get_last_component("a/", "/"));
ASSERT_EQ("c", get_last_component("a/b_c", "/_"));
}
TEST(core, crc)
{
char buffer[24];
for (int i = 0; i < sizeof(buffer) / sizeof(char); i++) {
buffer[i] = rand::next_u32(0, 200);
}
auto c1 = dsn::utils::crc32_calc(buffer, 12, 0);
auto c2 = dsn::utils::crc32_calc(buffer + 12, 12, c1);
auto c3 = dsn::utils::crc32_calc(buffer, 24, 0);
auto c4 = dsn::utils::crc32_concat(0, 0, c1, 12, c1, c2, 12);
EXPECT_TRUE(c3 == c4);
}
TEST(core, binary_io)
{
int value = 0xdeadbeef;
binary_writer writer;
writer.write(value);
auto buf = writer.get_buffer();
binary_reader reader(buf);
int value3;
reader.read(value3);
EXPECT_TRUE(value3 == value);
}
void check_empty(const char *str) { EXPECT_TRUE(dsn::utils::is_empty(str)); }
void check_nonempty(const char *str) { EXPECT_FALSE(dsn::utils::is_empty(str)); }
TEST(core, check_c_string_empty)
{
const char *empty_strings[] = {nullptr, "", "\0", "\0\0", "\0\0\0", "\0a", "\0ab", "\0abc"};
for (const auto &p : empty_strings) {
check_empty(p);
}
const char *nonempty_strings[] = {"\\",
"\\\\",
"0",
"00",
"\\0",
"\\0a",
"\\\\00",
"a",
"a\0",
"a\\0",
"a\0b",
"ab\0c",
"abc\0",
"abc"};
for (const auto &p : nonempty_strings) {
check_nonempty(p);
}
}
using c_string_equality = std::tuple<const char *, const char *, bool, bool>;
class CStringEqualityTest : public testing::TestWithParam<c_string_equality>
{
};
TEST_P(CStringEqualityTest, CStringEquals)
{
const char *lhs;
const char *rhs;
bool is_equal;
bool is_equal_ignore_case;
std::tie(lhs, rhs, is_equal, is_equal_ignore_case) = GetParam();
EXPECT_EQ(is_equal, dsn::utils::equals(lhs, rhs));
EXPECT_EQ(is_equal_ignore_case, dsn::utils::iequals(lhs, rhs));
if (rhs != nullptr) {
// Since NULL pointer cannot be used to construct std::string, related test cases
// are neglected.
std::string rhs_str(rhs);
EXPECT_EQ(is_equal_ignore_case, dsn::utils::iequals(lhs, rhs_str));
}
if (lhs != nullptr) {
// Since NULL pointer cannot be used to construct std::string, related test cases
// are neglected.
std::string lhs_str(lhs);
EXPECT_EQ(is_equal_ignore_case, dsn::utils::iequals(lhs_str, rhs));
}
}
const std::vector<c_string_equality> c_string_equality_tests = {
{nullptr, nullptr, true, true}, {nullptr, "", false, false}, {nullptr, "a", false, false},
{nullptr, "abc", false, false}, {"", nullptr, false, false}, {"a", nullptr, false, false},
{"abc", nullptr, false, false}, {"", "", true, true}, {"", "a", false, false},
{"", "abc", false, false}, {"a", "", false, false}, {"abc", "", false, false},
{"a", "a", true, true}, {"a", "A", false, true}, {"A", "A", true, true},
{"abc", "abc", true, true}, {"aBc", "abc", false, true}, {"abc", "ABC", false, true},
{"a", "abc", false, false}, {"A", "abc", false, false}, {"abc", "a", false, false},
{"Abc", "a", false, false},
};
INSTANTIATE_TEST_SUITE_P(StringTest,
CStringEqualityTest,
testing::ValuesIn(c_string_equality_tests));
using c_string_n_bytes_equality = std::tuple<const char *, const char *, size_t, bool, bool, bool>;
class CStringNBytesEqualityTest : public testing::TestWithParam<c_string_n_bytes_equality>
{
};
TEST_P(CStringNBytesEqualityTest, CStringNBytesEquals)
{
const char *lhs;
const char *rhs;
size_t n;
bool is_equal;
bool is_equal_ignore_case;
bool is_equal_memory;
std::tie(lhs, rhs, n, is_equal, is_equal_ignore_case, is_equal_memory) = GetParam();
EXPECT_EQ(is_equal, dsn::utils::equals(lhs, rhs, n));
EXPECT_EQ(is_equal_ignore_case, dsn::utils::iequals(lhs, rhs, n));
if (rhs != nullptr) {
// Since NULL pointer cannot be used to construct std::string, related test cases
// are neglected.
std::string rhs_str(rhs);
EXPECT_EQ(is_equal_ignore_case, dsn::utils::iequals(lhs, rhs_str, n));
}
if (lhs != nullptr) {
// Since NULL pointer cannot be used to construct std::string, related test cases
// are neglected.
std::string lhs_str(lhs);
EXPECT_EQ(is_equal_ignore_case, dsn::utils::iequals(lhs_str, rhs, n));
}
EXPECT_EQ(is_equal_memory, dsn::utils::mequals(lhs, rhs, n));
}
const std::vector<c_string_n_bytes_equality> c_string_n_bytes_equality_tests = {
{nullptr, nullptr, 0, true, true, true},
{nullptr, nullptr, 1, true, true, true},
{nullptr, "", 0, false, false, false},
{nullptr, "", 1, false, false, false},
{nullptr, "a", 0, false, false, false},
{nullptr, "a", 1, false, false, false},
{nullptr, "abc", 0, false, false, false},
{nullptr, "abc", 1, false, false, false},
{"", nullptr, 0, false, false, false},
{"", nullptr, 1, false, false, false},
{"a", nullptr, 0, false, false, false},
{"a", nullptr, 1, false, false, false},
{"abc", nullptr, 0, false, false, false},
{"abc", nullptr, 1, false, false, false},
{"", "", 0, true, true, true},
{"", "", 1, true, true, true},
{"\0", "a", 1, false, false, false},
{"\0\0\0", "abc", 3, false, false, false},
{"a", "\0", 1, false, false, false},
{"abc", "\0\0\0", 3, false, false, false},
{"a", "a", 1, true, true, true},
{"a", "A", 1, false, true, false},
{"A", "A", 1, true, true, true},
{"abc", "abc", 3, true, true, true},
{"aBc", "abc", 3, false, true, false},
{"abc", "ABC", 3, false, true, false},
{"a\0\0", "abc", 3, false, false, false},
{"A\0\0", "abc", 3, false, false, false},
{"abc", "a\0\0", 3, false, false, false},
{"abc", "xyz", 0, true, true, true},
{"Abc", "a\0\0", 3, false, false, false},
{"a", "abc", 1, true, true, true},
{"a", "Abc", 1, false, true, false},
{"abc", "a", 1, true, true, true},
{"Abc", "a", 1, false, true, false},
{"abc", "abd", 2, true, true, true},
{"abc", "ABd", 2, false, true, false},
{"abc\0opq", "abc\0xyz", 7, true, true, false},
{"abc\0opq", "ABC\0xyz", 7, false, true, false},
{"abc\0xyz", "abc\0xyz", 7, true, true, true},
};
INSTANTIATE_TEST_SUITE_P(StringTest,
CStringNBytesEqualityTest,
testing::ValuesIn(c_string_n_bytes_equality_tests));
// For containers such as std::unordered_set, the expected result will be deduplicated
// at initialization. Therefore, it can be used to compare with actual result safely.
template <typename Container>
void test_split_args()
{
// Test cases:
// - split empty string by ' ' without place holder
// - split empty string by ' ' with place holder
// - split empty string by ',' without place holder
// - split empty string by ',' with place holder
// - split a space (' ') by ' ' without place holder
// - split a space (' ') by ' ' with place holder
// - split a space (' ') by ',' without place holder
// - split a space (' ') by ',' with place holder
// - split a comma (',') by ' ' without place holder
// - split a comma (',') by ' ' with place holder
// - split a comma (',') by ',' without place holder
// - split a comma (',') by ',' with place holder
// - split 2 leading spaces by ' ' without place holder
// - split 2 leading spaces by ' ' with place holder
// - split 2 leading spaces by ',' without place holder
// - split 2 leading spaces by ',' with place holder
// - split 3 leading spaces by ' ' without place holder
// - split 3 leading spaces by ' ' with place holder
// - split 3 leading spaces by ',' without place holder
// - split 3 leading spaces by ',' with place holder
// - split 2 trailing spaces by ' ' without place holder
// - split 2 trailing spaces by ' ' with place holder
// - split 2 trailing spaces by ',' without place holder
// - split 2 trailing spaces by ',' with place holder
// - split 3 trailing spaces by ' ' without place holder
// - split 3 trailing spaces by ' ' with place holder
// - split 3 trailing spaces by ',' without place holder
// - split 3 trailing spaces by ',' with place holder
// - split a string including "\\t", "\\r" and "\\n" by ' ' without place holder
// - split a string including "\\t", "\\r" and "\\n" by ' ' with place holder
// - split a string including "\\t", "\\r" and "\\n" by ',' without place holder
// - split a string including "\\t", "\\r" and "\\n" by ',' with place holder
// - split a single letter by ' ' without place holder
// - split a single letter by ' ' with place holder
// - split a single letter by ',' without place holder
// - split a single letter by ',' with place holder
// - split a single word by ' ' without place holder
// - split a single word by ' ' with place holder
// - split a single word by ',' without place holder
// - split a single word by ',' with place holder
// - split a string including letters and words by ' ' without place holder
// - split a string including letters and words by ' ' with place holder
// - split a string including letters and words by ',' without place holder
// - split a string including letters and words by ',' with place holder
// - split a string that includes multiple letters by ' ' without place holder
// - split a string that includes multiple letters by ' ' with place holder
// - split a string that includes multiple letters by ',' without place holder
// - split a string that includes multiple letters by ',' with place holder
// - split a string that includes multiple words by ' ' without place holder
// - split a string that includes multiple words by ' ' with place holder
// - split a string that includes multiple words by ',' without place holder
// - split a string that includes multiple words by ',' with place holder
struct test_case
{
const char *input;
char separator;
bool keep_place_holder;
Container expected_output;
} tests[] = {{"", ' ', false, {}},
{"", ' ', true, {""}},
{"", ',', false, {}},
{"", ',', true, {""}},
{" ", ' ', false, {}},
{" ", ' ', true, {"", ""}},
{" ", ',', false, {}},
{" ", ',', true, {""}},
{",", ' ', false, {","}},
{",", ' ', true, {","}},
{",", ',', false, {}},
{",", ',', true, {"", ""}},
{" ", ' ', false, {}},
{"\t ", ' ', true, {"", ""}},
{" \t", ',', false, {}},
{"\t\t", ',', true, {""}},
{" \t ", ' ', false, {}},
{"\t ", ' ', true, {"", "", ""}},
{"\t\t ", ',', false, {}},
{" \t", ',', true, {""}},
{"\r ", ' ', false, {}},
{"\t\n", ' ', true, {""}},
{"\r\t", ',', false, {}},
{" \n", ',', true, {""}},
{"\n\t\r", ' ', false, {}},
{" \r ", ' ', true, {"", "", ""}},
{"\r \n", ',', false, {}},
{"\t\n\r", ',', true, {""}},
{" \\n,,\\t \\r ", ' ', false, {"\\n,,\\t", "\\r"}},
{" \\n,,\\t \\r ", ' ', true, {"", "\\n,,\\t", "\\r", ""}},
{" \\n,,\\t \\r ", ',', false, {"\\n", "\\t \\r"}},
{" \\n,,\\t \\r ", ',', true, {"\\n", "", "\\t \\r"}},
{"a", ' ', false, {"a"}},
{"a", ' ', true, {"a"}},
{"a", ',', false, {"a"}},
{"a", ',', true, {"a"}},
{"dinner", ' ', false, {"dinner"}},
{"dinner", ' ', true, {"dinner"}},
{"dinner", ',', false, {"dinner"}},
{"dinner", ',', true, {"dinner"}},
{"\t\r\na\t\tdog,\t\r\n \t\r\nand\t\r\n \t\ta\t\tcat",
' ',
false,
{"\r\na\t\tdog,", "\r\nand", "a\t\tcat"}},
{"\t\r\na\t\tdog,\t\r\n \t\r\nand\t\r\n \t\ta\t\tcat",
' ',
true,
{"\r\na\t\tdog,", "", "\r\nand", "a\t\tcat"}},
{"\t\r\na\t\tdog,\t\r\n \t\r\nand\t\r\n \t\ta\t\tcat",
',',
false,
{"\r\na\t\tdog", "\r\n \t\r\nand\t\r\n \t\ta\t\tcat"}},
{"\t\r\na\t\tdog,\t\r\n \t\r\nand\t\r\n \t\ta\t\tcat",
',',
true,
{"\r\na\t\tdog", "\r\n \t\r\nand\t\r\n \t\ta\t\tcat"}},
{"a ,b, ,c ", ' ', false, {"a", ",b,", ",c"}},
{"a ,b, ,c ", ' ', true, {"a", ",b,", ",c", ""}},
{"a ,b, ,c ", ',', false, {"a", "b", "c"}},
{"a ,b, ,c ", ',', true, {"a", "b", "", "c"}},
{" in early 2000s , too, ", ' ', false, {"in", "early", "2000s", ",", "too,"}},
{" in early 2000s , too, ",
' ',
true,
{"", "in", "", "early", "2000s", ",", "", "too,", ""}},
{" in early 2000s , too, ", ',', false, {"in early 2000s", "too"}},
{" in early 2000s , too, ", ',', true, {"in early 2000s", "too", ""}}};
for (const auto &test : tests) {
Container actual_output;
split_args(test.input, actual_output, test.separator, test.keep_place_holder);
EXPECT_EQ(actual_output, test.expected_output);
// Test default value (i.e. false) for keep_place_holder.
if (!test.keep_place_holder) {
split_args(test.input, actual_output, test.separator);
EXPECT_EQ(actual_output, test.expected_output);
// Test default value (i.e. ' ') for separator.
if (test.separator == ' ') {
split_args(test.input, actual_output);
EXPECT_EQ(actual_output, test.expected_output);
}
}
}
}
TEST(core, split_args)
{
test_split_args<std::vector<std::string>>();
test_split_args<std::list<std::string>>();
test_split_args<std::unordered_set<std::string>>();
}
TEST(core, trim_string)
{
std::string value = " x x x x ";
auto r = trim_string((char *)value.c_str());
EXPECT_EQ(std::string(r), "x x x x");
}
TEST(core, dlink)
{
dlink links[10];
dlink hdr;
for (int i = 0; i < 10; i++)
links[i].insert_before(&hdr);
int count = 0;
dlink *p = hdr.next();
while (p != &hdr) {
count++;
p = p->next();
}
EXPECT_EQ(count, 10);
p = hdr.next();
while (p != &hdr) {
auto p1 = p;
p = p->next();
p1->remove();
count--;
}
EXPECT_TRUE(hdr.is_alone());
EXPECT_TRUE(count == 0);
}
TEST(core, find_string_prefix)
{
struct test_case
{
std::string input;
char separator;
std::string expected_prefix;
} tests[] = {{"", ' ', ""},
{"abc.def", ' ', ""},
{"abc.def", '.', "abc"},
{"ab.cd.ef", '.', "ab"},
{"abc...def", '.', "abc"},
{".abc.def", '.', ""},
{" ", ' ', ""},
{"..", '.', ""},
{". ", ' ', "."}};
for (const auto &test : tests) {
auto actual_output = find_string_prefix(test.input, test.separator);
EXPECT_EQ(actual_output, test.expected_prefix);
}
}
class foo : public ::dsn::ref_counter
{
public:
foo(int &count) : _count(count) { _count++; }
~foo() { _count--; }
private:
int &_count;
};
typedef ::dsn::ref_ptr<foo> foo_ptr;
TEST(core, ref_ptr)
{
int count = 0;
foo_ptr x = nullptr;
auto y = new foo(count);
x = y;
EXPECT_TRUE(x->get_count() == 1);
EXPECT_TRUE(count == 1);
x = new foo(count);
EXPECT_TRUE(x->get_count() == 1);
EXPECT_TRUE(count == 1);
x = nullptr;
EXPECT_TRUE(count == 0);
std::map<int, foo_ptr> xs;
x = new foo(count);
EXPECT_TRUE(x->get_count() == 1);
EXPECT_TRUE(count == 1);
xs.insert(std::make_pair(1, x));
EXPECT_TRUE(x->get_count() == 2);
EXPECT_TRUE(count == 1);
x = nullptr;
EXPECT_TRUE(count == 1);
xs.clear();
EXPECT_TRUE(count == 0);
x = new foo(count);
EXPECT_TRUE(count == 1);
xs[2] = x;
EXPECT_TRUE(x->get_count() == 2);
x = nullptr;
EXPECT_TRUE(count == 1);
xs.clear();
EXPECT_TRUE(count == 0);
y = new foo(count);
EXPECT_TRUE(count == 1);
xs.insert(std::make_pair(1, y));
EXPECT_TRUE(count == 1);
EXPECT_TRUE(y->get_count() == 1);
xs.clear();
EXPECT_TRUE(count == 0);
y = new foo(count);
EXPECT_TRUE(count == 1);
xs[2] = y;
EXPECT_TRUE(count == 1);
EXPECT_TRUE(y->get_count() == 1);
xs.clear();
EXPECT_TRUE(count == 0);
foo_ptr z = new foo(count);
EXPECT_TRUE(count == 1);
z = foo_ptr();
EXPECT_TRUE(count == 0);
}
TEST(core, flip_map)
{
std::map<int, int> source;
source.emplace(3, 1);
source.emplace(2, 1);
source.emplace(1, 1);
auto target = flip_map(source);
ASSERT_EQ(target.size(), 3);
ASSERT_EQ(target.count(1), 3);
ASSERT_EQ(target.count(2), 0);
ASSERT_EQ(target.count(3), 0);
std::string values;
for (auto it = target.equal_range(1); it.first != it.second; it.first++) {
values += std::to_string(it.first->second);
}
ASSERT_EQ(values, "123");
}
TEST(core, get_intersection)
{
std::set<int> set1;
set1.insert(1);
set1.insert(2);
set1.insert(3);
std::set<int> set2;
set2.insert(3);
set2.insert(4);
set2.insert(5);
auto intersection = utils::get_intersection(set1, set2);
ASSERT_EQ(intersection.size(), 1);
ASSERT_EQ(*intersection.begin(), 3);
}
struct has_space_case
{
std::string str;
bool expected_has_space;
};
class HasSpaceTest : public testing::TestWithParam<has_space_case>
{
};
TEST_P(HasSpaceTest, HasSpace)
{
const auto &space_case = GetParam();
EXPECT_EQ(space_case.expected_has_space, has_space(space_case.str));
}
const std::vector<has_space_case> has_space_tests = {
{"abc xyz", true},
{" abcxyz", true},
{"abcxyz ", true},
{"abc xyz", true},
{"abc\r\nxyz", true},
{"abc\r\nxyz", true},
{"abc\txyz", true},
{"abc\txyz", true},
{"abcxyz", false},
{"abc_xyz", false},
};
INSTANTIATE_TEST_SUITE_P(StringTest, HasSpaceTest, testing::ValuesIn(has_space_tests));
} // namespace utils
} // namespace dsn