fix build when sanitizers are enabled and jemalloc is disabled
[folly.git] / folly / test / FBStringTest.cpp
index ddc8a646ea7d6be82de4a87d9816bae14c0b3097..9f51947af6c855bc88f92f749818712523b18cbf 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2013 Facebook, Inc.
+ * Copyright 2017 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 //
 // Author: andrei.alexandrescu@fb.com
 
-#include "folly/FBString.h"
+#include <folly/FBString.h>
 
+#include <atomic>
 #include <cstdlib>
-
+#include <iomanip>
 #include <list>
-#include <fstream>
+#include <sstream>
+
 #include <boost/algorithm/string.hpp>
 #include <boost/random.hpp>
-#include <gtest/gtest.h>
-
-#include <gflags/gflags.h>
 
-#include "folly/Foreach.h"
-#include "folly/Portability.h"
-#include "folly/Random.h"
-#include "folly/Conv.h"
+#include <folly/Conv.h>
+#include <folly/Portability.h>
+#include <folly/Random.h>
+#include <folly/container/Foreach.h>
+#include <folly/portability/GTest.h>
 
 using namespace std;
 using namespace folly;
 
+namespace {
+
 static const int seed = folly::randomNumberSeed();
 typedef boost::mt19937 RandomT;
 static RandomT rng(seed);
@@ -73,6 +75,7 @@ std::list<char> RandomList(unsigned int maxSize) {
  }
   return lst;
 }
+} // namespace
 
 ////////////////////////////////////////////////////////////////////////////////
 // Tests begin here
@@ -118,9 +121,7 @@ template <class String> void clause11_21_4_2_e(String & test) {
 }
 template <class String> void clause11_21_4_2_f(String & test) {
   // Constructor from char*
-  const size_t
-    pos = random(0, test.size()),
-    n = random(0, test.size() - pos);
+  const size_t pos = random(0, test.size());
   String before(test.data(), test.size());
   String s(test.c_str() + pos);
   String after(test.data(), test.size());
@@ -142,12 +143,16 @@ template <class String> void clause11_21_4_2_h(String & test) {
   EXPECT_EQ(test, s2);
   // Constructor from other iterators
   std::list<char> lst;
-  for (auto c : test) lst.push_back(c);
+  for (auto c : test) {
+    lst.push_back(c);
+  }
   String s3(lst.begin(), lst.end());
   EXPECT_EQ(test, s3);
   // Constructor from wchar_t iterators
   std::list<wchar_t> lst1;
-  for (auto c : test) lst1.push_back(c);
+  for (auto c : test) {
+    lst1.push_back(c);
+  }
   String s4(lst1.begin(), lst1.end());
   EXPECT_EQ(test, s4);
   // Constructor from wchar_t pointers
@@ -207,7 +212,8 @@ template <class String> void clause11_21_4_2_lprime(String & test) {
 }
 template <class String> void clause11_21_4_2_m(String & test) {
   // Assignment from char
-  test = random('a', 'z');
+  using value_type = typename String::value_type;
+  test = random(static_cast<value_type>('a'), static_cast<value_type>('z'));
 }
 template <class String> void clause11_21_4_2_n(String & test) {
   // Assignment from initializer_list<char>
@@ -247,8 +253,11 @@ template <class String> void clause11_21_4_4(String & test) {
   // exercise empty
   string empty("empty");
   string notempty("not empty");
-  if (test.empty()) test = String(empty.begin(), empty.end());
-  else test = String(notempty.begin(), notempty.end());
+  if (test.empty()) {
+    test = String(empty.begin(), empty.end());
+  } else {
+    test = String(notempty.begin(), notempty.end());
+  }
 }
 
 template <class String> void clause11_21_4_5(String & test) {
@@ -526,10 +535,11 @@ template <class String> void clause11_21_4_6_6(String & test) {
                random(0, maxString), random('a', 'z'));
   pos = random(0, test.size());
   if (avoidAliasing) {
+    auto newString = String(test);
     test.replace(
       test.begin() + pos,
       test.begin() + pos + random(0, test.size() - pos),
-      String(test));
+      newString);
   } else {
     test.replace(
       test.begin() + pos,
@@ -538,10 +548,11 @@ template <class String> void clause11_21_4_6_6(String & test) {
   }
   pos = random(0, test.size());
   if (avoidAliasing) {
+    auto newString = String(test);
     test.replace(
       test.begin() + pos,
       test.begin() + pos + random(0, test.size() - pos),
-      String(test).c_str(),
+      newString.c_str(),
       test.size() - random(0, test.size()));
   } else {
     test.replace(
@@ -571,10 +582,10 @@ template <class String> void clause11_21_4_6_6(String & test) {
 template <class String> void clause11_21_4_6_7(String & test) {
   std::vector<typename String::value_type>
     vec(random(0, maxString));
-  test.copy(
-    &vec[0],
-    vec.size(),
-    random(0, test.size()));
+  if (vec.empty()) {
+    return;
+  }
+  test.copy(vec.data(), vec.size(), random(0, test.size()));
 }
 
 template <class String> void clause11_21_4_6_8(String & test) {
@@ -590,7 +601,7 @@ template <class String> void clause11_21_4_7_1(String & test) {
   // exercise get_allocator()
   String s;
   randomString(&s, maxString);
-  assert(test.get_allocator() == s.get_allocator());
+  DCHECK(test.get_allocator() == s.get_allocator());
 }
 
 template <class String> void clause11_21_4_7_2_a(String & test) {
@@ -600,6 +611,21 @@ template <class String> void clause11_21_4_7_2_a(String & test) {
   Num2String(test, test.find(str, random(0, test.size())));
 }
 
+template <class String> void clause11_21_4_7_2_a1(String & test) {
+  String str = String(test).substr(
+    random(0, test.size()),
+    random(0, test.size()));
+  Num2String(test, test.find(str, random(0, test.size())));
+}
+
+template <class String> void clause11_21_4_7_2_a2(String & test) {
+  auto const& cTest = test;
+  String str = cTest.substr(
+    random(0, test.size()),
+    random(0, test.size()));
+  Num2String(test, test.find(str, random(0, test.size())));
+}
+
 template <class String> void clause11_21_4_7_2_b(String & test) {
   auto from = random(0, test.size());
   auto length = random(0, test.size() - from);
@@ -609,6 +635,25 @@ template <class String> void clause11_21_4_7_2_b(String & test) {
                              random(0, str.size())));
 }
 
+template <class String> void clause11_21_4_7_2_b1(String & test) {
+  auto from = random(0, test.size());
+  auto length = random(0, test.size() - from);
+  String str = String(test).substr(from, length);
+  Num2String(test, test.find(str.c_str(),
+                             random(0, test.size()),
+                             random(0, str.size())));
+}
+
+template <class String> void clause11_21_4_7_2_b2(String & test) {
+  auto from = random(0, test.size());
+  auto length = random(0, test.size() - from);
+  const auto& cTest = test;
+  String str = cTest.substr(from, length);
+  Num2String(test, test.find(str.c_str(),
+                             random(0, test.size()),
+                             random(0, str.size())));
+}
+
 template <class String> void clause11_21_4_7_2_c(String & test) {
   String str = test.substr(
     random(0, test.size()),
@@ -617,6 +662,23 @@ template <class String> void clause11_21_4_7_2_c(String & test) {
                              random(0, test.size())));
 }
 
+template <class String> void clause11_21_4_7_2_c1(String & test) {
+  String str = String(test).substr(
+    random(0, test.size()),
+    random(0, test.size()));
+  Num2String(test, test.find(str.c_str(),
+                             random(0, test.size())));
+}
+
+template <class String> void clause11_21_4_7_2_c2(String & test) {
+  const auto& cTest = test;
+  String str = cTest.substr(
+    random(0, test.size()),
+    random(0, test.size()));
+  Num2String(test, test.find(str.c_str(),
+                             random(0, test.size())));
+}
+
 template <class String> void clause11_21_4_7_2_d(String & test) {
   Num2String(test, test.find(
                random('a', 'z'),
@@ -773,8 +835,11 @@ template <class String> void clause11_21_4_7_9_a(String & test) {
   String s;
   randomString(&s, maxString);
   int tristate = test.compare(s);
-  if (tristate > 0) tristate = 1;
-  else if (tristate < 0) tristate = 2;
+  if (tristate > 0) {
+    tristate = 1;
+  } else if (tristate < 0) {
+    tristate = 2;
+  }
   Num2String(test, tristate);
 }
 
@@ -785,8 +850,11 @@ template <class String> void clause11_21_4_7_9_b(String & test) {
     random(0, test.size()),
     random(0, test.size()),
     s);
-  if (tristate > 0) tristate = 1;
-  else if (tristate < 0) tristate = 2;
+  if (tristate > 0) {
+    tristate = 1;
+  } else if (tristate < 0) {
+    tristate = 2;
+  }
   Num2String(test, tristate);
 }
 
@@ -799,8 +867,11 @@ template <class String> void clause11_21_4_7_9_c(String & test) {
     str,
     random(0, str.size()),
     random(0, str.size()));
-  if (tristate > 0) tristate = 1;
-  else if (tristate < 0) tristate = 2;
+  if (tristate > 0) {
+    tristate = 1;
+  } else if (tristate < 0) {
+    tristate = 2;
+  }
   Num2String(test, tristate);
 }
 
@@ -808,9 +879,12 @@ template <class String> void clause11_21_4_7_9_d(String & test) {
   String s;
   randomString(&s, maxString);
   int tristate = test.compare(s.c_str());
-  if (tristate > 0) tristate = 1;
-  else if (tristate < 0) tristate = 2;
-                Num2String(test, tristate);
+  if (tristate > 0) {
+    tristate = 1;
+  } else if (tristate < 0) {
+    tristate = 2;
+  }
+  Num2String(test, tristate);
 }
 
 template <class String> void clause11_21_4_7_9_e(String & test) {
@@ -821,8 +895,11 @@ template <class String> void clause11_21_4_7_9_e(String & test) {
     random(0, test.size()),
     str.c_str(),
     random(0, str.size()));
-  if (tristate > 0) tristate = 1;
-  else if (tristate < 0) tristate = 2;
+  if (tristate > 0) {
+    tristate = 1;
+  } else if (tristate < 0) {
+    tristate = 2;
+  }
   Num2String(test, tristate);
 }
 
@@ -835,6 +912,30 @@ template <class String> void clause11_21_4_8_1_a(String & test) {
 }
 
 template <class String> void clause11_21_4_8_1_b(String & test) {
+  String s1;
+  randomString(&s1, maxString);
+  String s2;
+  randomString(&s2, maxString);
+  test = move(s1) + s2;
+}
+
+template <class String> void clause11_21_4_8_1_c(String & test) {
+  String s1;
+  randomString(&s1, maxString);
+  String s2;
+  randomString(&s2, maxString);
+  test = s1 + move(s2);
+}
+
+template <class String> void clause11_21_4_8_1_d(String & test) {
+  String s1;
+  randomString(&s1, maxString);
+  String s2;
+  randomString(&s2, maxString);
+  test = move(s1) + move(s2);
+}
+
+template <class String> void clause11_21_4_8_1_e(String & test) {
   String s;
   randomString(&s, maxString);
   String s1;
@@ -842,13 +943,27 @@ template <class String> void clause11_21_4_8_1_b(String & test) {
   test = s.c_str() + s1;
 }
 
-template <class String> void clause11_21_4_8_1_c(String & test) {
+template <class String> void clause11_21_4_8_1_f(String & test) {
+  String s;
+  randomString(&s, maxString);
+  String s1;
+  randomString(&s1, maxString);
+  test = s.c_str() + move(s1);
+}
+
+template <class String> void clause11_21_4_8_1_g(String & test) {
   String s;
   randomString(&s, maxString);
   test = typename String::value_type(random('a', 'z')) + s;
 }
 
-template <class String> void clause11_21_4_8_1_d(String & test) {
+template <class String> void clause11_21_4_8_1_h(String & test) {
+  String s;
+  randomString(&s, maxString);
+  test = typename String::value_type(random('a', 'z')) + move(s);
+}
+
+template <class String> void clause11_21_4_8_1_i(String & test) {
   String s;
   randomString(&s, maxString);
   String s1;
@@ -856,20 +971,28 @@ template <class String> void clause11_21_4_8_1_d(String & test) {
   test = s + s1.c_str();
 }
 
-template <class String> void clause11_21_4_8_1_e(String & test) {
+template <class String> void clause11_21_4_8_1_j(String & test) {
   String s;
   randomString(&s, maxString);
   String s1;
   randomString(&s1, maxString);
-  test = s + s1.c_str();
+  test = move(s) + s1.c_str();
 }
 
-template <class String> void clause11_21_4_8_1_f(String & test) {
+template <class String> void clause11_21_4_8_1_k(String & test) {
   String s;
   randomString(&s, maxString);
   test = s + typename String::value_type(random('a', 'z'));
 }
 
+template <class String> void clause11_21_4_8_1_l(String & test) {
+  String s;
+  randomString(&s, maxString);
+  String s1;
+  randomString(&s1, maxString);
+  test = move(s) + s1.c_str();
+}
+
 // Numbering here is from C++11
 template <class String> void clause11_21_4_8_9_a(String & test) {
   basic_stringstream<typename String::value_type> stst(test.c_str());
@@ -883,39 +1006,62 @@ template <class String> void clause11_21_4_8_9_a(String & test) {
 TEST(FBString, testAllClauses) {
   EXPECT_TRUE(1) << "Starting with seed: " << seed;
   std::string r;
-  std::wstring wr;
   folly::fbstring c;
+#if FOLLY_HAVE_WCHAR_SUPPORT
+  std::wstring wr;
   folly::basic_fbstring<wchar_t> wc;
-#define TEST_CLAUSE(x)                                              \
-  do {                                                              \
-      if (1) {} else EXPECT_TRUE(1) << "Testing clause " << #x;     \
-      randomString(&r);                                             \
-      c = r;                                                        \
-      EXPECT_EQ(c, r);                                              \
-      wr = std::wstring(r.begin(), r.end());                        \
-      wc = folly::basic_fbstring<wchar_t>(wr.c_str());              \
-      auto localSeed = seed + count;                                \
-      rng = RandomT(localSeed);                                     \
-      clause11_##x(r);                                                \
-      rng = RandomT(localSeed);                                     \
-      clause11_##x(c);                                                \
-      EXPECT_EQ(r, c)                                               \
-        << "Lengths: " << r.size() << " vs. " << c.size()           \
-        << "\nReference: '" << r << "'"                             \
-        << "\nActual:    '" << c.data()[0] << "'";                  \
-      rng = RandomT(localSeed);                                     \
-      clause11_##x(wc);                                               \
-      int wret = wcslen(wc.c_str());                                \
-      char mb[wret+1];                                              \
-      int ret = wcstombs(mb, wc.c_str(), sizeof(mb));               \
-      if (ret == wret) mb[wret] = '\0';                             \
-      const char *mc = c.c_str();                                   \
-      std::string one(mb);                                          \
-      std::string two(mc);                                          \
-      EXPECT_EQ(one, two);                                          \
-    } while (++count % 100 != 0)
-
+#endif
   int count = 0;
+
+  auto l = [&](const char * const clause,
+               void(*f_string)(std::string&),
+               void(*f_fbstring)(folly::fbstring&),
+               void(*f_wfbstring)(folly::basic_fbstring<wchar_t>&)) {
+    do {
+      if (true) {
+      } else {
+        EXPECT_TRUE(1) << "Testing clause " << clause;
+      }
+      randomString(&r);
+      c = r;
+      EXPECT_EQ(c, r);
+#if FOLLY_HAVE_WCHAR_SUPPORT
+      wr = std::wstring(r.begin(), r.end());
+      wc = folly::basic_fbstring<wchar_t>(wr.c_str());
+#endif
+      auto localSeed = seed + count;
+      rng = RandomT(localSeed);
+      f_string(r);
+      rng = RandomT(localSeed);
+      f_fbstring(c);
+      EXPECT_EQ(r, c)
+        << "Lengths: " << r.size() << " vs. " << c.size()
+        << "\nReference: '" << r << "'"
+        << "\nActual:    '" << c.data()[0] << "'";
+#if FOLLY_HAVE_WCHAR_SUPPORT
+      rng = RandomT(localSeed);
+      f_wfbstring(wc);
+      int wret = wcslen(wc.c_str());
+      auto mbv = std::vector<char>(wret + 1);
+      auto mb = mbv.data();
+      int ret = wcstombs(mb, wc.c_str(), wret + 1);
+      if (ret == wret) {
+        mb[wret] = '\0';
+      }
+      const char *mc = c.c_str();
+      std::string one(mb);
+      std::string two(mc);
+      EXPECT_EQ(one, two);
+#endif
+    } while (++count % 100 != 0);
+  };
+
+#define TEST_CLAUSE(x) \
+  l(#x, \
+    clause11_##x<std::string>, \
+    clause11_##x<folly::fbstring>, \
+    clause11_##x<folly::basic_fbstring<wchar_t>>);
+
   TEST_CLAUSE(21_4_2_a);
   TEST_CLAUSE(21_4_2_b);
   TEST_CLAUSE(21_4_2_c);
@@ -955,8 +1101,14 @@ TEST(FBString, testAllClauses) {
   TEST_CLAUSE(21_4_7_1);
 
   TEST_CLAUSE(21_4_7_2_a);
+  TEST_CLAUSE(21_4_7_2_a1);
+  TEST_CLAUSE(21_4_7_2_a2);
   TEST_CLAUSE(21_4_7_2_b);
+  TEST_CLAUSE(21_4_7_2_b1);
+  TEST_CLAUSE(21_4_7_2_b2);
   TEST_CLAUSE(21_4_7_2_c);
+  TEST_CLAUSE(21_4_7_2_c1);
+  TEST_CLAUSE(21_4_7_2_c2);
   TEST_CLAUSE(21_4_7_2_d);
   TEST_CLAUSE(21_4_7_3_a);
   TEST_CLAUSE(21_4_7_3_b);
@@ -990,11 +1142,17 @@ TEST(FBString, testAllClauses) {
   TEST_CLAUSE(21_4_8_1_d);
   TEST_CLAUSE(21_4_8_1_e);
   TEST_CLAUSE(21_4_8_1_f);
+  TEST_CLAUSE(21_4_8_1_g);
+  TEST_CLAUSE(21_4_8_1_h);
+  TEST_CLAUSE(21_4_8_1_i);
+  TEST_CLAUSE(21_4_8_1_j);
+  TEST_CLAUSE(21_4_8_1_k);
+  TEST_CLAUSE(21_4_8_1_l);
   TEST_CLAUSE(21_4_8_9_a);
 }
 
 TEST(FBString, testGetline) {
-  fbstring s1 = "\
+  string s1 = "\
 Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras accumsan \n\
 elit ut urna consectetur in sagittis mi auctor. Nulla facilisi. In nec \n\
 dolor leo, vitae imperdiet neque. Donec ut erat mauris, a faucibus \n\
@@ -1012,28 +1170,17 @@ massa, ut accumsan magna. Donec imperdiet tempor nisi et \n\
 laoreet. Phasellus lectus quam, ultricies ut tincidunt in, dignissim \n\
 id eros. Mauris vulputate tortor nec neque pellentesque sagittis quis \n\
 sed nisl. In diam lacus, lobortis ut posuere nec, ornare id quam.";
-  char f[] = "/tmp/fbstring_testing.XXXXXX";
-  int fd = mkstemp(f);
-  EXPECT_TRUE(fd > 0);
-  if (fd > 0) {
-    close(fd);  // Yeah
-    std::ofstream out(f);
-    if (!(out << s1)) {
-      EXPECT_TRUE(0) << "Couldn't write to temp file.";
-      return;
-    }
-  }
+
   vector<fbstring> v;
   boost::split(v, s1, boost::is_any_of("\n"));
   {
-    ifstream input(f);
+    istringstream input(s1);
     fbstring line;
     FOR_EACH (i, v) {
       EXPECT_TRUE(!getline(input, line).fail());
       EXPECT_EQ(line, *i);
     }
   }
-  unlink(f);
 }
 
 TEST(FBString, testMoveCtor) {
@@ -1082,22 +1229,14 @@ TEST(FBString, testMoveOperatorPlusRhs) {
   EXPECT_EQ(size1 + size2, test.size());
 }
 
+// The GNU C++ standard library throws an std::logic_error when an std::string
+// is constructed with a null pointer. Verify that we mirror this behavior.
+//
+// N.B. We behave this way even if the C++ library being used is something
+//      other than libstdc++. Someday if we deem it important to present
+//      identical undefined behavior for other platforms, we can re-visit this.
 TEST(FBString, testConstructionFromLiteralZero) {
-  try {
-    std::string s(0);
-    EXPECT_TRUE(false);
-  } catch (const std::logic_error&) {
-  } catch (...) {
-    EXPECT_TRUE(false);
-  }
-
-  try {
-    fbstring s(0);
-    EXPECT_TRUE(false);
-  } catch (const std::logic_error& e) {
-  } catch (...) {
-    EXPECT_TRUE(false);
-  }
+  EXPECT_THROW(fbstring s(nullptr), std::logic_error);
 }
 
 TEST(FBString, testFixedBugs) {
@@ -1108,7 +1247,7 @@ TEST(FBString, testFixedBugs) {
     cp.c_str();
     EXPECT_EQ(str.front(), 'f');
   }
-  { // D481173, --extra-cxxflags=-DFBSTRING_CONSERVATIVE
+  { // D481173
     fbstring str(1337, 'f');
     for (int i = 0; i < 2; ++i) {
       fbstring cp = str;
@@ -1138,8 +1277,45 @@ TEST(FBString, testFixedBugs) {
     std::swap(str, str);
     EXPECT_EQ(1337, str.size());
   }
+  { // D1012196, --allocator=malloc
+    fbstring str(128, 'f');
+    str.clear();  // Empty medium string.
+    fbstring copy(str);  // Medium string of 0 capacity.
+    copy.push_back('b');
+    EXPECT_GE(copy.capacity(), 1);
+  }
+  { // D2813713
+    fbstring s1("a");
+    s1.reserve(8); // Trigger the optimized code path.
+    auto test1 = '\0' + std::move(s1);
+    EXPECT_EQ(2, test1.size());
+
+    fbstring s2(1, '\0');
+    s2.reserve(8);
+    auto test2 = "a" + std::move(s2);
+    EXPECT_EQ(2, test2.size());
+  }
+  { // D3698862
+    EXPECT_EQ(fbstring().find(fbstring(), 4), fbstring::npos);
+  }
+  if (usingJEMalloc()) { // D4355440
+    fbstring str(1337, 'f');
+    str.reserve(3840);
+    EXPECT_NE(str.capacity(), 3840);
+
+    struct {
+      std::atomic<size_t> refCount_;
+    } dummyRefCounted;
+    EXPECT_EQ(
+        str.capacity(),
+        goodMallocSize(3840) - sizeof(dummyRefCounted) - sizeof(char));
+  }
 }
 
+TEST(FBString, findWithNpos) {
+  fbstring fbstr("localhost:80");
+  EXPECT_EQ(fbstring::npos, fbstr.find(":", fbstring::npos));
+}
 
 TEST(FBString, testHash) {
   fbstring a;
@@ -1152,6 +1328,20 @@ TEST(FBString, testHash) {
   EXPECT_NE(hashfunc(a), hashfunc(b));
 }
 
+#if FOLLY_HAVE_WCHAR_SUPPORT
+TEST(FBString, testHashChar16) {
+  using u16fbstring = folly::basic_fbstring<char16_t>;
+  u16fbstring a;
+  u16fbstring b;
+  a.push_back(0);
+  a.push_back(1);
+  b.push_back(0);
+  b.push_back(2);
+  std::hash<u16fbstring> hashfunc;
+  EXPECT_NE(hashfunc(a), hashfunc(b));
+}
+#endif
+
 TEST(FBString, testFrontBack) {
   fbstring str("hello");
   EXPECT_EQ(str.front(), 'h');
@@ -1165,19 +1355,323 @@ TEST(FBString, testFrontBack) {
 
 TEST(FBString, noexcept) {
   EXPECT_TRUE(noexcept(fbstring()));
-  // std::move is not marked noexcept in gcc 4.6, sigh
-#if __GNUC_PREREQ(4, 7)
   fbstring x;
   EXPECT_FALSE(noexcept(fbstring(x)));
   EXPECT_TRUE(noexcept(fbstring(std::move(x))));
   fbstring y;
   EXPECT_FALSE(noexcept(y = x));
   EXPECT_TRUE(noexcept(y = std::move(x)));
-#endif
 }
 
-int main(int argc, char** argv) {
-  testing::InitGoogleTest(&argc, argv);
-  google::ParseCommandLineFlags(&argc, &argv, true);
-  return RUN_ALL_TESTS();
+TEST(FBString, iomanip) {
+  stringstream ss;
+  fbstring fbstr("Hello");
+
+  ss << setw(6) << fbstr;
+  EXPECT_EQ(ss.str(), " Hello");
+  ss.str("");
+
+  ss << left << setw(6) << fbstr;
+  EXPECT_EQ(ss.str(), "Hello ");
+  ss.str("");
+
+  ss << right << setw(6) << fbstr;
+  EXPECT_EQ(ss.str(), " Hello");
+  ss.str("");
+
+  ss << setw(4) << fbstr;
+  EXPECT_EQ(ss.str(), "Hello");
+  ss.str("");
+
+  ss << setfill('^') << setw(6) << fbstr;
+  EXPECT_EQ(ss.str(), "^Hello");
+  ss.str("");
+}
+
+TEST(FBString, rvalueIterators) {
+  // you cannot take &* of a move-iterator, so use that for testing
+  fbstring s = "base";
+  fbstring r = "hello";
+  r.replace(r.begin(), r.end(),
+      make_move_iterator(s.begin()), make_move_iterator(s.end()));
+  EXPECT_EQ("base", r);
+
+  // The following test is probably not required by the standard.
+  // i.e. this could be in the realm of undefined behavior.
+  fbstring b = "123abcXYZ";
+  auto ait = b.begin() + 3;
+  auto Xit = b.begin() + 6;
+  b.replace(ait, b.end(), b.begin(), Xit);
+  EXPECT_EQ("123123abc", b); // if things go wrong, you'd get "123123123"
+}
+
+TEST(FBString, moveTerminator) {
+  // The source of a move must remain in a valid state
+  fbstring s(100, 'x'); // too big to be in-situ
+  fbstring k;
+  k = std::move(s);
+
+  EXPECT_EQ(0, s.size());
+  EXPECT_EQ('\0', *s.c_str());
 }
+
+namespace {
+/*
+ * t8968589: Clang 3.7 refused to compile w/ certain constructors (specifically
+ * those that were "explicit" and had a defaulted parameter, if they were used
+ * in structs which were default-initialized).  Exercise these just to ensure
+ * they compile.
+ *
+ * In diff D2632953 the old constructor:
+ *   explicit basic_fbstring(const A& a = A()) noexcept;
+ *
+ * was split into these two, as a workaround:
+ *   basic_fbstring() noexcept;
+ *   explicit basic_fbstring(const A& a) noexcept;
+ */
+
+struct TestStructDefaultAllocator {
+  folly::basic_fbstring<char> stringMember;
+};
+
+template <class A>
+struct TestStructWithAllocator {
+  folly::basic_fbstring<char, std::char_traits<char>, A> stringMember;
+};
+
+std::atomic<size_t> allocatorConstructedCount(0);
+struct TestStructStringAllocator : std::allocator<char> {
+  TestStructStringAllocator() {
+    ++ allocatorConstructedCount;
+  }
+};
+
+} // namespace
+
+TEST(FBStringCtorTest, DefaultInitStructDefaultAlloc) {
+  TestStructDefaultAllocator t1 { };
+  EXPECT_TRUE(t1.stringMember.empty());
+}
+
+TEST(FBStringCtorTest, DefaultInitStructAlloc) {
+  EXPECT_EQ(allocatorConstructedCount.load(), 0);
+  TestStructWithAllocator<TestStructStringAllocator> t2;
+  EXPECT_TRUE(t2.stringMember.empty());
+  EXPECT_EQ(allocatorConstructedCount.load(), 1);
+}
+
+TEST(FBStringCtorTest, NullZeroConstruction) {
+  char* p = nullptr;
+  int n = 0;
+  folly::fbstring f(p, n);
+  EXPECT_EQ(f.size(), 0);
+}
+
+// Tests for the comparison operators. I use EXPECT_TRUE rather than EXPECT_LE
+// because what's under test is the operator rather than the relation between
+// the objects.
+
+TEST(FBString, compareToStdString) {
+  using folly::fbstring;
+  using namespace std::string_literals;
+  auto stdA = "a"s;
+  auto stdB = "b"s;
+  fbstring fbA("a");
+  fbstring fbB("b");
+  EXPECT_TRUE(stdA == fbA);
+  EXPECT_TRUE(fbB == stdB);
+  EXPECT_TRUE(stdA != fbB);
+  EXPECT_TRUE(fbA != stdB);
+  EXPECT_TRUE(stdA < fbB);
+  EXPECT_TRUE(fbA < stdB);
+  EXPECT_TRUE(stdB > fbA);
+  EXPECT_TRUE(fbB > stdA);
+  EXPECT_TRUE(stdA <= fbB);
+  EXPECT_TRUE(fbA <= stdB);
+  EXPECT_TRUE(stdA <= fbA);
+  EXPECT_TRUE(fbA <= stdA);
+  EXPECT_TRUE(stdB >= fbA);
+  EXPECT_TRUE(fbB >= stdA);
+  EXPECT_TRUE(stdB >= fbB);
+  EXPECT_TRUE(fbB >= stdB);
+}
+
+TEST(U16FBString, compareToStdU16String) {
+  using folly::basic_fbstring;
+  using namespace std::string_literals;
+  auto stdA = u"a"s;
+  auto stdB = u"b"s;
+  basic_fbstring<char16_t> fbA(u"a");
+  basic_fbstring<char16_t> fbB(u"b");
+  EXPECT_TRUE(stdA == fbA);
+  EXPECT_TRUE(fbB == stdB);
+  EXPECT_TRUE(stdA != fbB);
+  EXPECT_TRUE(fbA != stdB);
+  EXPECT_TRUE(stdA < fbB);
+  EXPECT_TRUE(fbA < stdB);
+  EXPECT_TRUE(stdB > fbA);
+  EXPECT_TRUE(fbB > stdA);
+  EXPECT_TRUE(stdA <= fbB);
+  EXPECT_TRUE(fbA <= stdB);
+  EXPECT_TRUE(stdA <= fbA);
+  EXPECT_TRUE(fbA <= stdA);
+  EXPECT_TRUE(stdB >= fbA);
+  EXPECT_TRUE(fbB >= stdA);
+  EXPECT_TRUE(stdB >= fbB);
+  EXPECT_TRUE(fbB >= stdB);
+}
+
+TEST(U32FBString, compareToStdU32String) {
+  using folly::basic_fbstring;
+  using namespace std::string_literals;
+  auto stdA = U"a"s;
+  auto stdB = U"b"s;
+  basic_fbstring<char32_t> fbA(U"a");
+  basic_fbstring<char32_t> fbB(U"b");
+  EXPECT_TRUE(stdA == fbA);
+  EXPECT_TRUE(fbB == stdB);
+  EXPECT_TRUE(stdA != fbB);
+  EXPECT_TRUE(fbA != stdB);
+  EXPECT_TRUE(stdA < fbB);
+  EXPECT_TRUE(fbA < stdB);
+  EXPECT_TRUE(stdB > fbA);
+  EXPECT_TRUE(fbB > stdA);
+  EXPECT_TRUE(stdA <= fbB);
+  EXPECT_TRUE(fbA <= stdB);
+  EXPECT_TRUE(stdA <= fbA);
+  EXPECT_TRUE(fbA <= stdA);
+  EXPECT_TRUE(stdB >= fbA);
+  EXPECT_TRUE(fbB >= stdA);
+  EXPECT_TRUE(stdB >= fbB);
+  EXPECT_TRUE(fbB >= stdB);
+}
+
+TEST(WFBString, compareToStdWString) {
+  using folly::basic_fbstring;
+  using namespace std::string_literals;
+  auto stdA = L"a"s;
+  auto stdB = L"b"s;
+  basic_fbstring<wchar_t> fbA(L"a");
+  basic_fbstring<wchar_t> fbB(L"b");
+  EXPECT_TRUE(stdA == fbA);
+  EXPECT_TRUE(fbB == stdB);
+  EXPECT_TRUE(stdA != fbB);
+  EXPECT_TRUE(fbA != stdB);
+  EXPECT_TRUE(stdA < fbB);
+  EXPECT_TRUE(fbA < stdB);
+  EXPECT_TRUE(stdB > fbA);
+  EXPECT_TRUE(fbB > stdA);
+  EXPECT_TRUE(stdA <= fbB);
+  EXPECT_TRUE(fbA <= stdB);
+  EXPECT_TRUE(stdA <= fbA);
+  EXPECT_TRUE(fbA <= stdA);
+  EXPECT_TRUE(stdB >= fbA);
+  EXPECT_TRUE(fbB >= stdA);
+  EXPECT_TRUE(stdB >= fbB);
+  EXPECT_TRUE(fbB >= stdB);
+}
+
+// Same again, but with a more challenging input - a common prefix and different
+// lengths.
+
+TEST(FBString, compareToStdStringLong) {
+  using folly::fbstring;
+  using namespace std::string_literals;
+  auto stdA = "1234567890a"s;
+  auto stdB = "1234567890ab"s;
+  fbstring fbA("1234567890a");
+  fbstring fbB("1234567890ab");
+  EXPECT_TRUE(stdA == fbA);
+  EXPECT_TRUE(fbB == stdB);
+  EXPECT_TRUE(stdA != fbB);
+  EXPECT_TRUE(fbA != stdB);
+  EXPECT_TRUE(stdA < fbB);
+  EXPECT_TRUE(fbA < stdB);
+  EXPECT_TRUE(stdB > fbA);
+  EXPECT_TRUE(fbB > stdA);
+  EXPECT_TRUE(stdA <= fbB);
+  EXPECT_TRUE(fbA <= stdB);
+  EXPECT_TRUE(stdA <= fbA);
+  EXPECT_TRUE(fbA <= stdA);
+  EXPECT_TRUE(stdB >= fbA);
+  EXPECT_TRUE(fbB >= stdA);
+  EXPECT_TRUE(stdB >= fbB);
+  EXPECT_TRUE(fbB >= stdB);
+}
+
+TEST(U16FBString, compareToStdU16StringLong) {
+  using folly::basic_fbstring;
+  using namespace std::string_literals;
+  auto stdA = u"1234567890a"s;
+  auto stdB = u"1234567890ab"s;
+  basic_fbstring<char16_t> fbA(u"1234567890a");
+  basic_fbstring<char16_t> fbB(u"1234567890ab");
+  EXPECT_TRUE(stdA == fbA);
+  EXPECT_TRUE(fbB == stdB);
+  EXPECT_TRUE(stdA != fbB);
+  EXPECT_TRUE(fbA != stdB);
+  EXPECT_TRUE(stdA < fbB);
+  EXPECT_TRUE(fbA < stdB);
+  EXPECT_TRUE(stdB > fbA);
+  EXPECT_TRUE(fbB > stdA);
+  EXPECT_TRUE(stdA <= fbB);
+  EXPECT_TRUE(fbA <= stdB);
+  EXPECT_TRUE(stdA <= fbA);
+  EXPECT_TRUE(fbA <= stdA);
+  EXPECT_TRUE(stdB >= fbA);
+  EXPECT_TRUE(fbB >= stdA);
+  EXPECT_TRUE(stdB >= fbB);
+  EXPECT_TRUE(fbB >= stdB);
+}
+
+#if FOLLY_HAVE_WCHAR_SUPPORT
+TEST(U32FBString, compareToStdU32StringLong) {
+  using folly::basic_fbstring;
+  using namespace std::string_literals;
+  auto stdA = U"1234567890a"s;
+  auto stdB = U"1234567890ab"s;
+  basic_fbstring<char32_t> fbA(U"1234567890a");
+  basic_fbstring<char32_t> fbB(U"1234567890ab");
+  EXPECT_TRUE(stdA == fbA);
+  EXPECT_TRUE(fbB == stdB);
+  EXPECT_TRUE(stdA != fbB);
+  EXPECT_TRUE(fbA != stdB);
+  EXPECT_TRUE(stdA < fbB);
+  EXPECT_TRUE(fbA < stdB);
+  EXPECT_TRUE(stdB > fbA);
+  EXPECT_TRUE(fbB > stdA);
+  EXPECT_TRUE(stdA <= fbB);
+  EXPECT_TRUE(fbA <= stdB);
+  EXPECT_TRUE(stdA <= fbA);
+  EXPECT_TRUE(fbA <= stdA);
+  EXPECT_TRUE(stdB >= fbA);
+  EXPECT_TRUE(fbB >= stdA);
+  EXPECT_TRUE(stdB >= fbB);
+  EXPECT_TRUE(fbB >= stdB);
+}
+
+TEST(WFBString, compareToStdWStringLong) {
+  using folly::basic_fbstring;
+  using namespace std::string_literals;
+  auto stdA = L"1234567890a"s;
+  auto stdB = L"1234567890ab"s;
+  basic_fbstring<wchar_t> fbA(L"1234567890a");
+  basic_fbstring<wchar_t> fbB(L"1234567890ab");
+  EXPECT_TRUE(stdA == fbA);
+  EXPECT_TRUE(fbB == stdB);
+  EXPECT_TRUE(stdA != fbB);
+  EXPECT_TRUE(fbA != stdB);
+  EXPECT_TRUE(stdA < fbB);
+  EXPECT_TRUE(fbA < stdB);
+  EXPECT_TRUE(stdB > fbA);
+  EXPECT_TRUE(fbB > stdA);
+  EXPECT_TRUE(stdA <= fbB);
+  EXPECT_TRUE(fbA <= stdB);
+  EXPECT_TRUE(stdA <= fbA);
+  EXPECT_TRUE(fbA <= stdA);
+  EXPECT_TRUE(stdB >= fbA);
+  EXPECT_TRUE(fbB >= stdA);
+  EXPECT_TRUE(stdB >= fbB);
+  EXPECT_TRUE(fbB >= stdB);
+}
+#endif