Override for include-guard
[folly.git] / folly / test / RangeTest.cpp
index faec886660dc15748403aaf86644ceefef7fa7fe..afc8ec8a8fa4cd130cc1d03f181a34d3db988de5 100644 (file)
 // @author Kristina Holst (kholst@fb.com)
 // @author Andrei Alexandrescu (andrei.alexandrescu@fb.com)
 
-#include "folly/Range.h"
+#include <folly/Range.h>
 
-#include <limits>
+#include <sys/mman.h>
+#include <array>
 #include <cstdlib>
-#include <string>
 #include <iterator>
-#include <sys/mman.h>
+#include <limits>
+#include <random>
+#include <string>
+#include <type_traits>
+#include <vector>
 #include <boost/range/concepts.hpp>
 #include <gtest/gtest.h>
 
 namespace folly { namespace detail {
 
 // declaration of functions in Range.cpp
-size_t qfind_first_byte_of_memchr(const StringPiece& haystack,
-                                  const StringPiece& needles);
+size_t qfind_first_byte_of_memchr(const StringPiece haystack,
+                                  const StringPiece needles);
 
-size_t qfind_first_byte_of_byteset(const StringPiece& haystack,
-                                   const StringPiece& needles);
+size_t qfind_first_byte_of_byteset(const StringPiece haystack,
+                                   const StringPiece needles);
 
 }}  // namespaces
 
 using namespace folly;
 using namespace std;
 
+static_assert(std::is_literal_type<StringPiece>::value, "");
+
 BOOST_CONCEPT_ASSERT((boost::RandomAccessRangeConcept<StringPiece>));
 
 TEST(StringPiece, All) {
@@ -114,6 +120,7 @@ TEST(StringPiece, All) {
   EXPECT_EQ(s.find('y'), StringPiece::npos);
   EXPECT_EQ(s.find('y', 1), StringPiece::npos);
   EXPECT_EQ(s.find('o', 4), StringPiece::npos);  // starting position too far
+  EXPECT_TRUE(s.contains('z'));
   // starting pos that is obviously past the end -- This works for std::string
   EXPECT_EQ(s.toString().find('y', 55), StringPiece::npos);
   EXPECT_EQ(s.find('z', s.size()), StringPiece::npos);
@@ -121,6 +128,7 @@ TEST(StringPiece, All) {
   // null char
   EXPECT_EQ(s.find('\0'), std::string().find('\0'));
   EXPECT_EQ(s.find('\0'), StringPiece::npos);
+  EXPECT_FALSE(s.contains('\0'));
 
   // single char rfinds
   EXPECT_EQ(s.rfind('b'), 6);
@@ -137,8 +145,10 @@ TEST(StringPiece, All) {
   EXPECT_EQ(s.find_first_of("bar"), 3);
   EXPECT_EQ(s.find_first_of("ba", 3), 3);
   EXPECT_EQ(s.find_first_of("ba", 4), 4);
+  EXPECT_TRUE(s.contains("bar"));
   EXPECT_EQ(s.find_first_of("xyxy"), StringPiece::npos);
   EXPECT_EQ(s.find_first_of("xyxy", 1), StringPiece::npos);
+  EXPECT_FALSE(s.contains("xyxy"));
   // starting position too far
   EXPECT_EQ(s.find_first_of("foo", 4), StringPiece::npos);
   // starting pos that is obviously past the end -- This works for std::string
@@ -433,6 +443,7 @@ TEST(StringPiece, split_step_char_delimiter) {
   folly::StringPiece p(s);
   EXPECT_EQ(s, p.begin());
   EXPECT_EQ(e, p.end());
+  EXPECT_EQ(s, p);
 
   auto x = p.split_step(' ');
   EXPECT_EQ(std::next(s, 5), p.begin());
@@ -490,6 +501,7 @@ TEST(StringPiece, split_step_range_delimiter) {
   folly::StringPiece p(s);
   EXPECT_EQ(s, p.begin());
   EXPECT_EQ(e, p.end());
+  EXPECT_EQ(s, p);
 
   auto x = p.split_step("  ");
   EXPECT_EQ(std::next(s, 6), p.begin());
@@ -554,6 +566,7 @@ TEST(StringPiece, split_step_with_process_char_delimiter) {
   folly::StringPiece p(s);
   EXPECT_EQ(s, p.begin());
   EXPECT_EQ(e, p.end());
+  EXPECT_EQ(s, p);
 
   EXPECT_EQ(1, (p.split_step(' ', [&](folly::StringPiece x) {
     EXPECT_EQ(std::next(s, 5), p.begin());
@@ -636,6 +649,7 @@ TEST(StringPiece, split_step_with_process_range_delimiter) {
   folly::StringPiece p(s);
   EXPECT_EQ(s, p.begin());
   EXPECT_EQ(e, p.end());
+  EXPECT_EQ(s, p);
 
   EXPECT_EQ(1, (p.split_step("  ", [&](folly::StringPiece x) {
     EXPECT_EQ(std::next(s, 6), p.begin());
@@ -715,6 +729,82 @@ TEST(StringPiece, split_step_with_process_range_delimiter) {
   EXPECT_NO_THROW(p.split_step(' ', split_step_with_process_noop));
 }
 
+TEST(StringPiece, split_step_with_process_char_delimiter_additional_args) {
+  //              0         1         2
+  //              012345678901234567890123456
+  auto const s = "this is just  a test string";
+  auto const e = std::next(s, std::strlen(s));
+  auto const delimiter = ' ';
+  EXPECT_EQ('\0', *e);
+
+  folly::StringPiece p(s);
+  EXPECT_EQ(s, p.begin());
+  EXPECT_EQ(e, p.end());
+  EXPECT_EQ(s, p);
+
+  auto const functor = [](
+    folly::StringPiece s,
+    folly::StringPiece expected
+  ) {
+    EXPECT_EQ(expected, s);
+    return expected;
+  };
+
+  auto const checker = [&](folly::StringPiece expected) {
+    EXPECT_EQ(expected, p.split_step(delimiter, functor, expected));
+  };
+
+  checker("this");
+  checker("is");
+  checker("just");
+  checker("");
+  checker("a");
+  checker("test");
+  checker("string");
+  checker("");
+  checker("");
+
+  EXPECT_TRUE(p.empty());
+}
+
+TEST(StringPiece, split_step_with_process_range_delimiter_additional_args) {
+  //              0         1         2         3
+  //              0123456789012345678901234567890123
+  auto const s = "this  is  just    a   test  string";
+  auto const e = std::next(s, std::strlen(s));
+  auto const delimiter = "  ";
+  EXPECT_EQ('\0', *e);
+
+  folly::StringPiece p(s);
+  EXPECT_EQ(s, p.begin());
+  EXPECT_EQ(e, p.end());
+  EXPECT_EQ(s, p);
+
+  auto const functor = [](
+    folly::StringPiece s,
+    folly::StringPiece expected
+  ) {
+    EXPECT_EQ(expected, s);
+    return expected;
+  };
+
+  auto const checker = [&](folly::StringPiece expected) {
+    EXPECT_EQ(expected, p.split_step(delimiter, functor, expected));
+  };
+
+  checker("this");
+  checker("is");
+  checker("just");
+  checker("");
+  checker("a");
+  checker(" test");
+  checker("string");
+  checker("");
+  checker("");
+
+  EXPECT_TRUE(p.empty());
+}
+
 TEST(qfind, UInt32_Ranges) {
   vector<uint32_t> a({1, 2, 3, 260, 5});
   vector<uint32_t> b({2, 3, 4});
@@ -922,3 +1012,157 @@ TEST(NonConstTest, StringPiece) {
     MutableByteRange r2(sp);
   }
 }
+
+template<class C>
+void testRangeFunc(C&& x, size_t n) {
+  const auto& cx = x;
+  // type, conversion checks
+  Range<int*> r1 = range(std::forward<C>(x));
+  Range<const int*> r2 = range(std::forward<C>(x));
+  Range<const int*> r3 = range(cx);
+  Range<const int*> r5 = range(std::move(cx));
+  EXPECT_EQ(r1.begin(), &x[0]);
+  EXPECT_EQ(r1.end(), &x[n]);
+  EXPECT_EQ(n, r1.size());
+  EXPECT_EQ(n, r2.size());
+  EXPECT_EQ(n, r3.size());
+  EXPECT_EQ(n, r5.size());
+}
+
+TEST(RangeFunc, Vector) {
+  std::vector<int> x;
+  testRangeFunc(x, 0);
+  x.push_back(2);
+  testRangeFunc(x, 1);
+  testRangeFunc(std::vector<int>{1, 2}, 2);
+}
+
+TEST(RangeFunc, Array) {
+  std::array<int, 3> x;
+  testRangeFunc(x, 3);
+}
+
+TEST(RangeFunc, CArray) {
+  int x[] {1, 2, 3, 4};
+  testRangeFunc(x, 4);
+}
+
+std::string get_rand_str(
+    int size, std::uniform_int_distribution<>& dist, std::mt19937& gen) {
+  std::string ret(size, '\0');
+  for (int i=0; i<size; ++i) {
+    ret[i] = static_cast<char>(dist(gen));
+  }
+
+  return ret;
+}
+
+namespace folly {
+bool operator==(MutableStringPiece mp, StringPiece sp) {
+  return mp.compare(sp) == 0;
+}
+
+bool operator==(StringPiece sp, MutableStringPiece mp) {
+  return mp.compare(sp) == 0;
+}
+}
+
+TEST(ReplaceAt, exhaustiveTest) {
+  char input[] = "this is nice and long input";
+  auto msp = MutableStringPiece(input);
+  auto str = std::string(input);
+  std::random_device rd;
+  std::mt19937 gen(rd());
+  std::uniform_int_distribution<> dist('a', 'z');
+
+  for (int i=0; i < 100; ++i) {
+    for (int j = 1; j <= msp.size(); ++j) {
+      auto replacement = get_rand_str(j, dist, gen);
+      for (int pos=0; pos < msp.size() - j; ++pos) {
+        msp.replaceAt(pos, replacement);
+        str.replace(pos, replacement.size(), replacement);
+        EXPECT_EQ(msp.compare(str), 0);
+      }
+    }
+  }
+
+  // too far
+  EXPECT_EQ(msp.replaceAt(msp.size() - 2, StringPiece("meh")), false);
+}
+
+TEST(ReplaceAll, basicTest) {
+  char input[] = "this is nice and long input";
+  auto orig = std::string(input);
+  auto msp = MutableStringPiece(input);
+
+  EXPECT_EQ(msp.replaceAll("is", "si"), 2);
+  EXPECT_EQ("thsi si nice and long input", msp);
+  EXPECT_EQ(msp.replaceAll("si", "is"), 2);
+  EXPECT_EQ(msp, orig);
+
+  EXPECT_EQ(msp.replaceAll("abcd", "efgh"), 0); // nothing to replace
+  EXPECT_EQ(msp, orig);
+
+  // at the very beginning
+  EXPECT_EQ(msp.replaceAll("this", "siht"), 1);
+  EXPECT_EQ("siht is nice and long input", msp);
+  EXPECT_EQ(msp.replaceAll("siht", "this"), 1);
+  EXPECT_EQ(msp, orig);
+
+  // at the very end
+  EXPECT_EQ(msp.replaceAll("input", "soput"), 1);
+  EXPECT_EQ("this is nice and long soput", msp);
+  EXPECT_EQ(msp.replaceAll("soput", "input"), 1);
+  EXPECT_EQ(msp, orig);
+
+  // all spaces
+  EXPECT_EQ(msp.replaceAll(" ", "@"), 5);
+  EXPECT_EQ("this@is@nice@and@long@input", msp);
+  EXPECT_EQ(msp.replaceAll("@", " "), 5);
+  EXPECT_EQ(msp, orig);
+}
+
+TEST(ReplaceAll, randomTest) {
+  char input[] = "abcdefghijklmnoprstuwqz"; // no pattern repeata inside
+  auto orig = std::string(input);
+  auto msp = MutableStringPiece(input);
+
+  std::random_device rd;
+  std::mt19937 gen(rd());
+  std::uniform_int_distribution<> dist('A', 'Z');
+
+  for (int i=0; i < 100; ++i) {
+    for (int j = 1; j <= orig.size(); ++j) {
+      auto replacement = get_rand_str(j, dist, gen);
+      for (int pos=0; pos < msp.size() - j; ++pos) {
+        auto piece = orig.substr(pos, j);
+        EXPECT_EQ(msp.replaceAll(piece, replacement), 1);
+        EXPECT_EQ(msp.find(replacement), pos);
+        EXPECT_EQ(msp.replaceAll(replacement, piece), 1);
+        EXPECT_EQ(msp, orig);
+      }
+    }
+  }
+}
+
+TEST(ReplaceAll, BadArg) {
+  int count = 0;
+  auto fst = "longer";
+  auto snd = "small";
+  char input[] = "meh meh meh";
+  auto all =  MutableStringPiece(input);
+
+  try {
+    all.replaceAll(fst, snd);
+  } catch (std::invalid_argument&) {
+    ++count;
+  }
+
+  try {
+    all.replaceAll(snd, fst);
+  } catch (std::invalid_argument&) {
+    ++count;
+  }
+
+  EXPECT_EQ(count, 2);
+}