Flesh out Optional members swap, reset, emplace, has_value
[folly.git] / folly / test / FBStringTest.cpp
index 3bc4723cb40ddba2dd575ea5646aaff423212214..9f51947af6c855bc88f92f749818712523b18cbf 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2012 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/Random.h"
-#include "folly/Benchmark.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);
@@ -59,9 +62,9 @@ void randomString(String* toFill, unsigned int maxSize = 1000) {
 
 template <class String, class Integral>
 void Num2String(String& str, Integral n) {
-  str.resize(30, '\0');
-  sprintf(&str[0], "%lu", static_cast<unsigned long>(n));
-  str.resize(strlen(str.c_str()));
+
+  std::string tmp = folly::to<std::string>(n);
+  str = String(tmp.begin(), tmp.end());
 }
 
 std::list<char> RandomList(unsigned int maxSize) {
@@ -72,64 +75,102 @@ std::list<char> RandomList(unsigned int maxSize) {
  }
   return lst;
 }
-
-// void preventOptimization(void * p) {
-//   return folly::preventOptimization((int)(long) p);
-// }
+} // namespace
 
 ////////////////////////////////////////////////////////////////////////////////
 // Tests begin here
 ////////////////////////////////////////////////////////////////////////////////
 
-template <class String> void clause_21_3_1_a(String & test) {
+template <class String> void clause11_21_4_2_a(String & test) {
   test.String::~String();
   new(&test) String();
 }
-template <class String> void clause_21_3_1_b(String & test) {
-  // Copy constructor
+template <class String> void clause11_21_4_2_b(String & test) {
+  String test2(test);
+  assert(test2 == test);
+}
+template <class String> void clause11_21_4_2_c(String & test) {
+  // Test move constructor. There is a more specialized test, see
+  // TEST(FBString, testMoveCtor)
+  String donor(test);
+  String test2(std::move(donor));
+  EXPECT_EQ(test2, test);
+  // Technically not required, but all implementations that actually
+  // support move will move large strings. Make a guess for 128 as the
+  // maximum small string optimization that's reasonable.
+  EXPECT_LE(donor.size(), 128);
+}
+template <class String> void clause11_21_4_2_d(String & test) {
+  // Copy constructor with position and length
   const size_t pos = random(0, test.size());
-  String s(test, pos, random(0, (size_t)(test.size() - pos)));
+  String s(test, pos, random(0, 9)
+           ? random(0, (size_t)(test.size() - pos))
+           : String::npos); // test for npos, too, in 10% of the cases
   test = s;
 }
-template <class String> void clause_21_3_1_c(String & test) {
+template <class String> void clause11_21_4_2_e(String & test) {
   // Constructor from char*, size_t
   const size_t
     pos = random(0, test.size()),
     n = random(0, test.size() - pos);
-  std::string before(test.data(), test.size());
+  String before(test.data(), test.size());
   String s(test.c_str() + pos, n);
-  std::string after(test.data(), test.size());
+  String after(test.data(), test.size());
   EXPECT_EQ(before, after);
-
+  test.swap(s);
+}
+template <class String> void clause11_21_4_2_f(String & test) {
+  // Constructor from char*
+  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());
+  EXPECT_EQ(before, after);
+  test.swap(s);
+}
+template <class String> void clause11_21_4_2_g(String & test) {
+  // Constructor from size_t, char
+  const size_t n = random(0, test.size());
+  const auto c = test.front();
+  test = String(n, c);
+}
+template <class String> void clause11_21_4_2_h(String & test) {
+  // Constructors from various iterator pairs
   // Constructor from char*, char*
   String s1(test.begin(), test.end());
   EXPECT_EQ(test, s1);
   String s2(test.data(), test.data() + test.size());
   EXPECT_EQ(test, s2);
-
-  // Constructor from iterators
+  // 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
   wchar_t t[20];
   t[0] = 'a';
   t[1] = 'b';
-  String s5(t, t + 2);;
+  fbstring s5(t, t + 2);;
   EXPECT_EQ("ab", s5);
-
-  test = s;
 }
-template <class String> void clause_21_3_1_d(String & test) {
-  // Assignment
+template <class String> void clause11_21_4_2_i(String & test) {
+  // From initializer_list<char>
+  std::initializer_list<typename String::value_type>
+    il = { 'h', 'e', 'l', 'l', 'o' };
+  String s(il);
+  test.swap(s);
+}
+template <class String> void clause11_21_4_2_j(String & test) {
+  // Assignment from const String&
   auto size = random(0, 2000);
   String s(size, '\0');
   EXPECT_EQ(s.size(), size);
@@ -138,7 +179,20 @@ template <class String> void clause_21_3_1_d(String & test) {
   }
   test = s;
 }
-template <class String> void clause_21_3_1_e(String & test) {
+template <class String> void clause11_21_4_2_k(String & test) {
+  // Assignment from String&&
+  auto size = random(0, 2000);
+  String s(size, '\0');
+  EXPECT_EQ(s.size(), size);
+  FOR_EACH_RANGE (i, 0, s.size()) {
+    s[i] = random('a', 'z');
+  }
+  test = std::move(s);
+  if (typeid(String) == typeid(fbstring)) {
+    EXPECT_LE(s.size(), 128);
+  }
+}
+template <class String> void clause11_21_4_2_l(String & test) {
   // Assignment from char*
   String s(random(0, 1000), '\0');
   size_t i = 0;
@@ -147,7 +201,7 @@ template <class String> void clause_21_3_1_e(String & test) {
   }
   test = s.c_str();
 }
-template <class String> void clause_21_3_1_f(String & test) {
+template <class String> void clause11_21_4_2_lprime(String & test) {
   // Aliased assign
   const size_t pos = random(0, test.size());
   if (avoidAliasing) {
@@ -156,15 +210,24 @@ template <class String> void clause_21_3_1_f(String & test) {
     test = test.c_str() + pos;
   }
 }
-template <class String> void clause_21_3_1_g(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>
+  initializer_list<typename String::value_type>
+    il = { 'h', 'e', 'l', 'l', 'o' };
+  test = il;
 }
 
-template <class String> void clause_21_3_2(String & test) {
+template <class String> void clause11_21_4_3(String & test) {
   // Iterators. The code below should leave test unchanged
   EXPECT_EQ(test.size(), test.end() - test.begin());
   EXPECT_EQ(test.size(), test.rend() - test.rbegin());
+  EXPECT_EQ(test.size(), test.cend() - test.cbegin());
+  EXPECT_EQ(test.size(), test.crend() - test.crbegin());
 
   auto s = test.size();
   test.resize(test.end() - test.begin());
@@ -173,31 +236,47 @@ template <class String> void clause_21_3_2(String & test) {
   EXPECT_EQ(s, test.size());
 }
 
-template <class String> void clause_21_3_3(String & test) {
+template <class String> void clause11_21_4_4(String & test) {
   // exercise capacity, size, max_size
   EXPECT_EQ(test.size(), test.length());
   EXPECT_LE(test.size(), test.max_size());
   EXPECT_LE(test.capacity(), test.max_size());
   EXPECT_LE(test.size(), test.capacity());
+
+  // exercise shrink_to_fit. Nonbinding request so we can't really do
+  // much beyond calling it.
+  auto copy = test;
+  copy.reserve(copy.capacity() * 3);
+  copy.shrink_to_fit();
+  EXPECT_EQ(copy, test);
+
   // exercise empty
-  if (test.empty()) test = "empty";
-  else test = "not empty";
+  string empty("empty");
+  string notempty("not empty");
+  if (test.empty()) {
+    test = String(empty.begin(), empty.end());
+  } else {
+    test = String(notempty.begin(), notempty.end());
+  }
 }
 
-template <class String> void clause_21_3_4(String & test) {
-  // exercise element access 21.3.4
+template <class String> void clause11_21_4_5(String & test) {
+  // exercise element access
   if (!test.empty()) {
+    EXPECT_EQ(test[0], test.front());
+    EXPECT_EQ(test[test.size() - 1], test.back());
     auto const i = random(0, test.size() - 1);
     EXPECT_EQ(test[i], test.at(i));
     test = test[i];
   }
 }
 
-template <class String> void clause_21_3_5_a(String & test) {
+template <class String> void clause11_21_4_6_1(String & test) {
   // 21.3.5 modifiers (+=)
   String test1;
   randomString(&test1);
-  assert(test1.size() == strlen(test1.c_str()));
+  assert(test1.size() == char_traits
+      <typename String::value_type>::length(test1.c_str()));
   auto len = test.size();
   test += test1;
   EXPECT_EQ(test.size(), test1.size() + len);
@@ -211,20 +290,24 @@ template <class String> void clause_21_3_5_a(String & test) {
   len = test.size();
   EXPECT_EQ(memcmp(sz, dt, len), 0);
   String copy(test.data(), test.size());
-  EXPECT_EQ(strlen(test.c_str()), len);
+  EXPECT_EQ(char_traits
+      <typename String::value_type>::length(test.c_str()), len);
   test += test;
   //test.append(test);
   EXPECT_EQ(test.size(), 2 * len);
-  EXPECT_EQ(strlen(test.c_str()), 2 * len);
+  EXPECT_EQ(char_traits
+      <typename String::value_type>::length(test.c_str()), 2 * len);
   FOR_EACH_RANGE (i, 0, len) {
     EXPECT_EQ(test[i], copy[i]);
     EXPECT_EQ(test[i], test[len + i]);
   }
   len = test.size();
-  EXPECT_EQ(strlen(test.c_str()), len);
+  EXPECT_EQ(char_traits
+      <typename String::value_type>::length(test.c_str()), len);
   // more aliasing
   auto const pos = random(0, test.size());
-  EXPECT_EQ(strlen(test.c_str() + pos), len - pos);
+  EXPECT_EQ(char_traits
+      <typename String::value_type>::length(test.c_str() + pos), len - pos);
   if (avoidAliasing) {
     String addMe(test.c_str() + pos);
     EXPECT_EQ(addMe.size(), len - pos);
@@ -237,9 +320,12 @@ template <class String> void clause_21_3_5_a(String & test) {
   len = test.size();
   test += random('a', 'z');
   EXPECT_EQ(test.size(), len + 1);
+  // initializer_list
+  initializer_list<typename String::value_type> il { 'a', 'b', 'c' };
+  test += il;
 }
 
-template <class String> void clause_21_3_5_b(String & test) {
+template <class String> void clause11_21_4_6_2(String & test) {
   // 21.3.5 modifiers (append, push_back)
   String s;
 
@@ -264,70 +350,85 @@ template <class String> void clause_21_3_5_b(String & test) {
   c = random('a', 'z');
   test.push_back(c);
   EXPECT_EQ(test[test.size() - 1], c);
+  // initializer_list
+  initializer_list<typename String::value_type> il { 'a', 'b', 'c' };
+  test.append(il);
 }
 
-template <class String> void clause_21_3_5_c(String & test) {
+template <class String> void clause11_21_4_6_3_a(String & test) {
   // assign
   String s;
   randomString(&s);
   test.assign(s);
+  EXPECT_EQ(test, s);
+  // move assign
+  test.assign(std::move(s));
+  if (typeid(String) == typeid(fbstring)) {
+    EXPECT_LE(s.size(), 128);
+  }
 }
 
-template <class String> void clause_21_3_5_d(String & test) {
+template <class String> void clause11_21_4_6_3_b(String & test) {
   // assign
   String s;
   randomString(&s, maxString);
   test.assign(s, random(0, s.size()), random(0, maxString));
 }
 
-template <class String> void clause_21_3_5_e(String & test) {
+template <class String> void clause11_21_4_6_3_c(String & test) {
   // assign
   String s;
   randomString(&s, maxString);
   test.assign(s.c_str(), random(0, s.size()));
 }
 
-template <class String> void clause_21_3_5_f(String & test) {
+template <class String> void clause11_21_4_6_3_d(String & test) {
   // assign
   String s;
   randomString(&s, maxString);
   test.assign(s.c_str());
 }
 
-template <class String> void clause_21_3_5_g(String & test) {
+template <class String> void clause11_21_4_6_3_e(String & test) {
   // assign
   String s;
   randomString(&s, maxString);
   test.assign(random(0, maxString), random('a', 'z'));
 }
 
-template <class String> void clause_21_3_5_h(String & test) {
+template <class String> void clause11_21_4_6_3_f(String & test) {
   // assign from bidirectional iterator
   std::list<char> lst(RandomList(maxString));
   test.assign(lst.begin(), lst.end());
 }
 
-template <class String> void clause_21_3_5_i(String & test) {
+template <class String> void clause11_21_4_6_3_g(String & test) {
   // assign from aliased source
   test.assign(test);
 }
 
-template <class String> void clause_21_3_5_j(String & test) {
+template <class String> void clause11_21_4_6_3_h(String & test) {
   // assign from aliased source
   test.assign(test, random(0, test.size()), random(0, maxString));
 }
 
-template <class String> void clause_21_3_5_k(String & test) {
+template <class String> void clause11_21_4_6_3_i(String & test) {
   // assign from aliased source
   test.assign(test.c_str(), random(0, test.size()));
 }
 
-template <class String> void clause_21_3_5_l(String & test) {
+template <class String> void clause11_21_4_6_3_j(String & test) {
   // assign from aliased source
   test.assign(test.c_str());
 }
 
-template <class String> void clause_21_3_5_m(String & test) {
+template <class String> void clause11_21_4_6_3_k(String & test) {
+  // assign from initializer_list
+  initializer_list<typename String::value_type> il { 'a', 'b', 'c' };
+  test.assign(il);
+}
+
+template <class String> void clause11_21_4_6_4(String & test) {
   // insert
   String s;
   randomString(&s, maxString);
@@ -343,15 +444,29 @@ template <class String> void clause_21_3_5_m(String & test) {
   test.insert(random(0, test.size()), s.c_str());
   test.insert(random(0, test.size()),
               random(0, maxString), random('a', 'z'));
-  test.insert(test.begin() + random(0, test.size()),
-              random('a', 'z'));
+  typename String::size_type pos = random(0, test.size());
+  typename String::iterator res =
+    test.insert(test.begin() + pos, random('a', 'z'));
+  EXPECT_EQ(res - test.begin(), pos);
   std::list<char> lst(RandomList(maxString));
-  test.insert(test.begin() + random(0, test.size()),
-              lst.begin(), lst.end());
+  pos = random(0, test.size());
+  // Uncomment below to see a bug in gcc
+  /*res = */test.insert(test.begin() + pos, lst.begin(), lst.end());
+  // insert from initializer_list
+  initializer_list<typename String::value_type> il { 'a', 'b', 'c' };
+  pos = random(0, test.size());
+  // Uncomment below to see a bug in gcc
+  /*res = */test.insert(test.begin() + pos, il);
+
+  // Test with actual input iterators
+  stringstream ss;
+  ss << "hello cruel world";
+  auto i = istream_iterator<char>(ss);
+  test.insert(test.begin(), i, istream_iterator<char>());
 }
 
-template <class String> void clause_21_3_5_n(String & test) {
-  // erase
+template <class String> void clause11_21_4_6_5(String & test) {
+  // erase and pop_back
   if (!test.empty()) {
     test.erase(random(0, test.size()), random(0, maxString));
   }
@@ -365,9 +480,13 @@ template <class String> void clause_21_3_5_n(String & test) {
       test.erase(i, i + random(0, size_t(test.end() - i)));
     }
   }
+  if (!test.empty()) {
+    // Can't test pop_back with std::string, doesn't support it yet.
+    //test.pop_back();
+  }
 }
 
-template <class String> void clause_21_3_5_o(String & test) {
+template <class String> void clause11_21_4_6_6(String & test) {
   auto pos = random(0, test.size());
   if (avoidAliasing) {
     test.replace(pos, random(0, test.size() - pos),
@@ -416,10 +535,11 @@ template <class String> void clause_21_3_5_o(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,
@@ -428,10 +548,11 @@ template <class String> void clause_21_3_5_o(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(
@@ -458,39 +579,54 @@ template <class String> void clause_21_3_5_o(String & test) {
     random(0, maxString), random('a', 'z'));
 }
 
-template <class String> void clause_21_3_5_p(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 clause_21_3_5_q(String & test) {
+template <class String> void clause11_21_4_6_8(String & test) {
   String s;
   randomString(&s, maxString);
   s.swap(test);
 }
 
-template <class String> void clause_21_3_6_a(String & test) {
+template <class String> void clause11_21_4_7_1(String & test) {
   // 21.3.6 string operations
   // exercise c_str() and data()
   assert(test.c_str() == test.data());
   // 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 clause_21_3_6_b(String & test) {
+template <class String> void clause11_21_4_7_2_a(String & test) {
   String str = test.substr(
     random(0, test.size()),
     random(0, test.size()));
   Num2String(test, test.find(str, random(0, test.size())));
 }
 
-template <class String> void clause_21_3_6_c(String & test) {
+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);
   String str = test.substr(from, length);
@@ -499,7 +635,26 @@ template <class String> void clause_21_3_6_c(String & test) {
                              random(0, str.size())));
 }
 
-template <class String> void clause_21_3_6_d(String & test) {
+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()),
     random(0, test.size()));
@@ -507,20 +662,37 @@ template <class String> void clause_21_3_6_d(String & test) {
                              random(0, test.size())));
 }
 
-template <class String> void clause_21_3_6_e(String & test) {
+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'),
                random(0, test.size())));
 }
 
-template <class String> void clause_21_3_6_f(String & test) {
+template <class String> void clause11_21_4_7_3_a(String & test) {
   String str = test.substr(
     random(0, test.size()),
     random(0, test.size()));
   Num2String(test, test.rfind(str, random(0, test.size())));
 }
 
-template <class String> void clause_21_3_6_g(String & test) {
+template <class String> void clause11_21_4_7_3_b(String & test) {
   String str = test.substr(
     random(0, test.size()),
     random(0, test.size()));
@@ -529,7 +701,7 @@ template <class String> void clause_21_3_6_g(String & test) {
                               random(0, str.size())));
 }
 
-template <class String> void clause_21_3_6_h(String & test) {
+template <class String> void clause11_21_4_7_3_c(String & test) {
   String str = test.substr(
     random(0, test.size()),
     random(0, test.size()));
@@ -537,20 +709,20 @@ template <class String> void clause_21_3_6_h(String & test) {
                               random(0, test.size())));
 }
 
-template <class String> void clause_21_3_6_i(String & test) {
+template <class String> void clause11_21_4_7_3_d(String & test) {
   Num2String(test, test.rfind(
                random('a', 'z'),
                random(0, test.size())));
 }
 
-template <class String> void clause_21_3_6_j(String & test) {
+template <class String> void clause11_21_4_7_4_a(String & test) {
   String str;
   randomString(&str, maxString);
   Num2String(test, test.find_first_of(str,
                                       random(0, test.size())));
 }
 
-template <class String> void clause_21_3_6_k(String & test) {
+template <class String> void clause11_21_4_7_4_b(String & test) {
   String str;
   randomString(&str, maxString);
   Num2String(test, test.find_first_of(str.c_str(),
@@ -558,27 +730,27 @@ template <class String> void clause_21_3_6_k(String & test) {
                                       random(0, str.size())));
 }
 
-template <class String> void clause_21_3_6_l(String & test) {
+template <class String> void clause11_21_4_7_4_c(String & test) {
   String str;
   randomString(&str, maxString);
   Num2String(test, test.find_first_of(str.c_str(),
                                       random(0, test.size())));
 }
 
-template <class String> void clause_21_3_6_m(String & test) {
+template <class String> void clause11_21_4_7_4_d(String & test) {
   Num2String(test, test.find_first_of(
                random('a', 'z'),
                random(0, test.size())));
 }
 
-template <class String> void clause_21_3_6_n(String & test) {
+template <class String> void clause11_21_4_7_5_a(String & test) {
   String str;
   randomString(&str, maxString);
   Num2String(test, test.find_last_of(str,
                                      random(0, test.size())));
 }
 
-template <class String> void clause_21_3_6_o(String & test) {
+template <class String> void clause11_21_4_7_5_b(String & test) {
   String str;
   randomString(&str, maxString);
   Num2String(test, test.find_last_of(str.c_str(),
@@ -586,27 +758,27 @@ template <class String> void clause_21_3_6_o(String & test) {
                                      random(0, str.size())));
 }
 
-template <class String> void clause_21_3_6_p(String & test) {
+template <class String> void clause11_21_4_7_5_c(String & test) {
   String str;
   randomString(&str, maxString);
   Num2String(test, test.find_last_of(str.c_str(),
                                      random(0, test.size())));
 }
 
-template <class String> void clause_21_3_6_q(String & test) {
+template <class String> void clause11_21_4_7_5_d(String & test) {
   Num2String(test, test.find_last_of(
                random('a', 'z'),
                random(0, test.size())));
 }
 
-template <class String> void clause_21_3_6_r(String & test) {
+template <class String> void clause11_21_4_7_6_a(String & test) {
   String str;
   randomString(&str, maxString);
   Num2String(test, test.find_first_not_of(str,
                                           random(0, test.size())));
 }
 
-template <class String> void clause_21_3_6_s(String & test) {
+template <class String> void clause11_21_4_7_6_b(String & test) {
   String str;
   randomString(&str, maxString);
   Num2String(test, test.find_first_not_of(str.c_str(),
@@ -614,27 +786,27 @@ template <class String> void clause_21_3_6_s(String & test) {
                                           random(0, str.size())));
 }
 
-template <class String> void clause_21_3_6_t(String & test) {
+template <class String> void clause11_21_4_7_6_c(String & test) {
   String str;
   randomString(&str, maxString);
   Num2String(test, test.find_first_not_of(str.c_str(),
                                           random(0, test.size())));
 }
 
-template <class String> void clause_21_3_6_u(String & test) {
+template <class String> void clause11_21_4_7_6_d(String & test) {
   Num2String(test, test.find_first_not_of(
                random('a', 'z'),
                random(0, test.size())));
 }
 
-template <class String> void clause_21_3_6_v(String & test) {
+template <class String> void clause11_21_4_7_7_a(String & test) {
   String str;
   randomString(&str, maxString);
   Num2String(test, test.find_last_not_of(str,
                                          random(0, test.size())));
 }
 
-template <class String> void clause_21_3_6_w(String & test) {
+template <class String> void clause11_21_4_7_7_b(String & test) {
   String str;
   randomString(&str, maxString);
   Num2String(test, test.find_last_not_of(str.c_str(),
@@ -642,45 +814,51 @@ template <class String> void clause_21_3_6_w(String & test) {
                                          random(0, str.size())));
 }
 
-template <class String> void clause_21_3_6_x(String & test) {
+template <class String> void clause11_21_4_7_7_c(String & test) {
   String str;
   randomString(&str, maxString);
   Num2String(test, test.find_last_not_of(str.c_str(),
                                          random(0, test.size())));
 }
 
-template <class String> void clause_21_3_6_y(String & test) {
+template <class String> void clause11_21_4_7_7_d(String & test) {
   Num2String(test, test.find_last_not_of(
                random('a', 'z'),
                random(0, test.size())));
 }
 
-template <class String> void clause_21_3_6_z(String & test) {
+template <class String> void clause11_21_4_7_8(String & test) {
   test = test.substr(random(0, test.size()), random(0, test.size()));
 }
 
-template <class String> void clause_21_3_7_a(String & test) {
+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);
 }
 
-template <class String> void clause_21_3_7_b(String & test) {
+template <class String> void clause11_21_4_7_9_b(String & test) {
   String s;
   randomString(&s, maxString);
   int tristate = test.compare(
     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);
 }
 
-template <class String> void clause_21_3_7_c(String & test) {
+template <class String> void clause11_21_4_7_9_c(String & test) {
   String str;
   randomString(&str, maxString);
   int tristate = test.compare(
@@ -689,21 +867,27 @@ template <class String> void clause_21_3_7_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);
 }
 
-template <class String> void clause_21_3_7_d(String & test) {
+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 clause_21_3_7_e(String & test) {
+template <class String> void clause11_21_4_7_9_e(String & test) {
   String str;
   randomString(&str, maxString);
   int tristate = test.compare(
@@ -711,12 +895,15 @@ template <class String> void clause_21_3_7_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);
 }
 
-template <class String> void clause_21_3_7_f(String & test) {
+template <class String> void clause11_21_4_8_1_a(String & test) {
   String s1;
   randomString(&s1, maxString);
   String s2;
@@ -724,7 +911,31 @@ template <class String> void clause_21_3_7_f(String & test) {
   test = s1 + s2;
 }
 
-template <class String> void clause_21_3_7_g(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;
@@ -732,13 +943,27 @@ template <class String> void clause_21_3_7_g(String & test) {
   test = s.c_str() + s1;
 }
 
-template <class String> void clause_21_3_7_h(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 clause_21_3_7_i(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;
@@ -746,26 +971,34 @@ template <class String> void clause_21_3_7_i(String & test) {
   test = s + s1.c_str();
 }
 
-template <class String> void clause_21_3_7_j(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 clause_21_3_7_k(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 clause_21_4_8_9_a(String & test) {
-  stringstream s("asd asdfjhuhdf    asdfasdf\tasdsdf");
+template <class String> void clause11_21_4_8_9_a(String & test) {
+  basic_stringstream<typename String::value_type> stst(test.c_str());
   String str;
-  while (s) {
-    s >> str;
+  while (stst) {
+    stst >> str;
     test += str + test;
   }
 }
@@ -774,96 +1007,152 @@ TEST(FBString, testAllClauses) {
   EXPECT_TRUE(1) << "Starting with seed: " << seed;
   std::string r;
   folly::fbstring c;
-#define TEST_CLAUSE(x)                                              \
-  do {                                                              \
-      if (1) {} else EXPECT_TRUE(1) << "Testing clause " << #x;     \
-      randomString(&r);                                             \
-      c = r;                                                        \
-      EXPECT_EQ(c, r);                                              \
-      auto localSeed = seed + count;                                \
-      rng = RandomT(localSeed);                                     \
-      clause_##x(r);                                                \
-      rng = RandomT(localSeed);                                     \
-      clause_##x(c);                                                \
-      EXPECT_EQ(r, c)                                               \
-        << "Lengths: " << r.size() << " vs. " << c.size()           \
-        << "\nReference: '" << r << "'"                             \
-        << "\nActual:    '" << c.data()[0] << "'";                  \
-    } while (++count % 100 != 0)
-
+#if FOLLY_HAVE_WCHAR_SUPPORT
+  std::wstring wr;
+  folly::basic_fbstring<wchar_t> wc;
+#endif
   int count = 0;
-  TEST_CLAUSE(21_3_1_a);
-  TEST_CLAUSE(21_3_1_b);
-  TEST_CLAUSE(21_3_1_c);
-  TEST_CLAUSE(21_3_1_d);
-  TEST_CLAUSE(21_3_1_e);
-  TEST_CLAUSE(21_3_1_f);
-  TEST_CLAUSE(21_3_1_g);
-
-  TEST_CLAUSE(21_3_2);
-  TEST_CLAUSE(21_3_3);
-  TEST_CLAUSE(21_3_4);
-  TEST_CLAUSE(21_3_5_a);
-  TEST_CLAUSE(21_3_5_b);
-  TEST_CLAUSE(21_3_5_c);
-  TEST_CLAUSE(21_3_5_d);
-  TEST_CLAUSE(21_3_5_e);
-  TEST_CLAUSE(21_3_5_f);
-  TEST_CLAUSE(21_3_5_g);
-  TEST_CLAUSE(21_3_5_h);
-  TEST_CLAUSE(21_3_5_i);
-  TEST_CLAUSE(21_3_5_j);
-  TEST_CLAUSE(21_3_5_k);
-  TEST_CLAUSE(21_3_5_l);
-  TEST_CLAUSE(21_3_5_m);
-  TEST_CLAUSE(21_3_5_n);
-  TEST_CLAUSE(21_3_5_o);
-  TEST_CLAUSE(21_3_5_p);
-
-  TEST_CLAUSE(21_3_6_a);
-  TEST_CLAUSE(21_3_6_b);
-  TEST_CLAUSE(21_3_6_c);
-  TEST_CLAUSE(21_3_6_d);
-  TEST_CLAUSE(21_3_6_e);
-  TEST_CLAUSE(21_3_6_f);
-  TEST_CLAUSE(21_3_6_g);
-  TEST_CLAUSE(21_3_6_h);
-  TEST_CLAUSE(21_3_6_i);
-  TEST_CLAUSE(21_3_6_j);
-  TEST_CLAUSE(21_3_6_k);
-  TEST_CLAUSE(21_3_6_l);
-  TEST_CLAUSE(21_3_6_m);
-  TEST_CLAUSE(21_3_6_n);
-  TEST_CLAUSE(21_3_6_o);
-  TEST_CLAUSE(21_3_6_p);
-  TEST_CLAUSE(21_3_6_q);
-  TEST_CLAUSE(21_3_6_r);
-  TEST_CLAUSE(21_3_6_s);
-  TEST_CLAUSE(21_3_6_t);
-  TEST_CLAUSE(21_3_6_u);
-  TEST_CLAUSE(21_3_6_v);
-  TEST_CLAUSE(21_3_6_w);
-  TEST_CLAUSE(21_3_6_x);
-  TEST_CLAUSE(21_3_6_y);
-  TEST_CLAUSE(21_3_6_z);
-
-  TEST_CLAUSE(21_3_7_a);
-  TEST_CLAUSE(21_3_7_b);
-  TEST_CLAUSE(21_3_7_c);
-  TEST_CLAUSE(21_3_7_d);
-  TEST_CLAUSE(21_3_7_e);
-  TEST_CLAUSE(21_3_7_f);
-  TEST_CLAUSE(21_3_7_g);
-  TEST_CLAUSE(21_3_7_h);
-  TEST_CLAUSE(21_3_7_i);
-  TEST_CLAUSE(21_3_7_j);
-  TEST_CLAUSE(21_3_7_k);
 
+  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);
+  TEST_CLAUSE(21_4_2_d);
+  TEST_CLAUSE(21_4_2_e);
+  TEST_CLAUSE(21_4_2_f);
+  TEST_CLAUSE(21_4_2_g);
+  TEST_CLAUSE(21_4_2_h);
+  TEST_CLAUSE(21_4_2_i);
+  TEST_CLAUSE(21_4_2_j);
+  TEST_CLAUSE(21_4_2_k);
+  TEST_CLAUSE(21_4_2_l);
+  TEST_CLAUSE(21_4_2_lprime);
+  TEST_CLAUSE(21_4_2_m);
+  TEST_CLAUSE(21_4_2_n);
+  TEST_CLAUSE(21_4_3);
+  TEST_CLAUSE(21_4_4);
+  TEST_CLAUSE(21_4_5);
+  TEST_CLAUSE(21_4_6_1);
+  TEST_CLAUSE(21_4_6_2);
+  TEST_CLAUSE(21_4_6_3_a);
+  TEST_CLAUSE(21_4_6_3_b);
+  TEST_CLAUSE(21_4_6_3_c);
+  TEST_CLAUSE(21_4_6_3_d);
+  TEST_CLAUSE(21_4_6_3_e);
+  TEST_CLAUSE(21_4_6_3_f);
+  TEST_CLAUSE(21_4_6_3_g);
+  TEST_CLAUSE(21_4_6_3_h);
+  TEST_CLAUSE(21_4_6_3_i);
+  TEST_CLAUSE(21_4_6_3_j);
+  TEST_CLAUSE(21_4_6_3_k);
+  TEST_CLAUSE(21_4_6_4);
+  TEST_CLAUSE(21_4_6_5);
+  TEST_CLAUSE(21_4_6_6);
+  TEST_CLAUSE(21_4_6_7);
+  TEST_CLAUSE(21_4_6_8);
+  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);
+  TEST_CLAUSE(21_4_7_3_c);
+  TEST_CLAUSE(21_4_7_3_d);
+  TEST_CLAUSE(21_4_7_4_a);
+  TEST_CLAUSE(21_4_7_4_b);
+  TEST_CLAUSE(21_4_7_4_c);
+  TEST_CLAUSE(21_4_7_4_d);
+  TEST_CLAUSE(21_4_7_5_a);
+  TEST_CLAUSE(21_4_7_5_b);
+  TEST_CLAUSE(21_4_7_5_c);
+  TEST_CLAUSE(21_4_7_5_d);
+  TEST_CLAUSE(21_4_7_6_a);
+  TEST_CLAUSE(21_4_7_6_b);
+  TEST_CLAUSE(21_4_7_6_c);
+  TEST_CLAUSE(21_4_7_6_d);
+  TEST_CLAUSE(21_4_7_7_a);
+  TEST_CLAUSE(21_4_7_7_b);
+  TEST_CLAUSE(21_4_7_7_c);
+  TEST_CLAUSE(21_4_7_7_d);
+  TEST_CLAUSE(21_4_7_8);
+  TEST_CLAUSE(21_4_7_9_a);
+  TEST_CLAUSE(21_4_7_9_b);
+  TEST_CLAUSE(21_4_7_9_c);
+  TEST_CLAUSE(21_4_7_9_d);
+  TEST_CLAUSE(21_4_7_9_e);
+  TEST_CLAUSE(21_4_8_1_a);
+  TEST_CLAUSE(21_4_8_1_b);
+  TEST_CLAUSE(21_4_8_1_c);
+  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\
@@ -881,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));
+      EXPECT_TRUE(!getline(input, line).fail());
       EXPECT_EQ(line, *i);
     }
   }
-  unlink(f);
 }
 
 TEST(FBString, testMoveCtor) {
@@ -951,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) {
@@ -977,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;
@@ -986,80 +1256,422 @@ TEST(FBString, testFixedBugs) {
       cp.push_back('?');
     }
   }
+  { // D580267
+    {
+      fbstring str(1337, 'f');
+      fbstring cp = str;
+      cp.push_back('f');
+    }
+    {
+      fbstring str(1337, 'f');
+      fbstring cp = str;
+      cp += "bb";
+    }
+  }
+  { // D661622
+    folly::basic_fbstring<wchar_t> s;
+    EXPECT_EQ(0, s.size());
+  }
+  { // D785057
+    fbstring str(1337, 'f');
+    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));
+  }
 }
 
-#define CONCAT(A, B) CONCAT_HELPER(A, B)
-#define CONCAT_HELPER(A, B) A##B
-#define BENCHFUN(F) CONCAT(CONCAT(BM_, F), CONCAT(_, STRING))
+TEST(FBString, findWithNpos) {
+  fbstring fbstr("localhost:80");
+  EXPECT_EQ(fbstring::npos, fbstr.find(":", fbstring::npos));
+}
+
+TEST(FBString, testHash) {
+  fbstring a;
+  fbstring b;
+  a.push_back(0);
+  a.push_back(1);
+  b.push_back(0);
+  b.push_back(2);
+  std::hash<fbstring> hashfunc;
+  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');
+  EXPECT_EQ(str.back(), 'o');
+  str.front() = 'H';
+  EXPECT_EQ(str.front(), 'H');
+  str.back() = 'O';
+  EXPECT_EQ(str.back(), 'O');
+  EXPECT_EQ(str, "HellO");
+}
+
+TEST(FBString, noexcept) {
+  EXPECT_TRUE(noexcept(fbstring()));
+  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)));
+}
+
+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;
+ */
 
-#define STRING string
-#include "folly/test/FBStringTestBenchmarks.cpp.h"
-#undef STRING
-#define STRING fbstring
-#include "folly/test/FBStringTestBenchmarks.cpp.h"
-#undef STRING
+struct TestStructDefaultAllocator {
+  folly::basic_fbstring<char> stringMember;
+};
 
-int main(int argc, char** argv) {
-  testing::InitGoogleTest(&argc, argv);
-  google::ParseCommandLineFlags(&argc, &argv, true);
-  auto ret = RUN_ALL_TESTS();
-  if (!ret && FLAGS_benchmark) {
-    folly::runBenchmarks();
-  }
+template <class A>
+struct TestStructWithAllocator {
+  folly::basic_fbstring<char, std::char_traits<char>, A> stringMember;
+};
 
-  return ret;
-}
-
-/*
-malloc
-
-BENCHFUN(defaultCtor)                  100000  1.426 s   14.26 us  68.47 k
-BM_copyCtor_string/32k                 100000  63.48 ms  634.8 ns  1.502 M
-BM_ctorFromArray_string/32k            100000  303.3 ms  3.033 us  321.9 k
-BM_ctorFromChar_string/1M              100000  9.915 ms  99.15 ns  9.619 M
-BM_assignmentOp_string/256             100000  69.09 ms  690.9 ns   1.38 M
-BENCHFUN(assignmentFill)               100000  1.775 ms  17.75 ns  53.73 M
-BM_resize_string/512k                  100000  1.667 s   16.67 us  58.58 k
-BM_findSuccessful_string/512k          100000  287.3 ms  2.873 us  339.9 k
-BM_findUnsuccessful_string/512k        100000  320.3 ms  3.203 us  304.9 k
-BM_replace_string/256                  100000  69.68 ms  696.8 ns  1.369 M
-BM_push_back_string/1k                 100000  433.1 ms  4.331 us  225.5 k
-
-BENCHFUN(defaultCtor)                  100000  1.086 s   10.86 us  89.91 k
-BM_copyCtor_fbstring/32k               100000  4.218 ms  42.18 ns  22.61 M
-BM_ctorFromArray_fbstring/32k          100000  145.2 ms  1.452 us  672.7 k
-BM_ctorFromChar_fbstring/1M            100000   9.21 ms   92.1 ns  10.35 M
-BM_assignmentOp_fbstring/256           100000  61.95 ms  619.5 ns   1.54 M
-BENCHFUN(assignmentFill)               100000   1.41 ms   14.1 ns  67.64 M
-BM_resize_fbstring/512k                100000  1.668 s   16.68 us  58.56 k
-BM_findSuccessful_fbstring/512k        100000   20.6 ms    206 ns  4.629 M
-BM_findUnsuccessful_fbstring/512k      100000  141.3 ms  1.413 us  691.1 k
-BM_replace_fbstring/256                100000  77.12 ms  771.2 ns  1.237 M
-BM_push_back_fbstring/1k               100000  1.745 s   17.45 us  55.95 k
-
-jemalloc
-
-BENCHFUN(defaultCtor)                  100000  1.426 s   14.26 us   68.5 k
-BM_copyCtor_string/32k                 100000  275.7 ms  2.757 us  354.2 k
-BM_ctorFromArray_string/32k            100000    270 ms    2.7 us  361.7 k
-BM_ctorFromChar_string/1M              100000  10.36 ms  103.6 ns  9.206 M
-BM_assignmentOp_string/256             100000  70.44 ms  704.3 ns  1.354 M
-BENCHFUN(assignmentFill)               100000  1.766 ms  17.66 ns     54 M
-BM_resize_string/512k                  100000  1.675 s   16.75 us  58.29 k
-BM_findSuccessful_string/512k          100000  90.89 ms  908.9 ns  1.049 M
-BM_findUnsuccessful_string/512k        100000  315.1 ms  3.151 us  309.9 k
-BM_replace_string/256                  100000  71.14 ms  711.4 ns  1.341 M
-BM_push_back_string/1k                 100000  425.1 ms  4.251 us  229.7 k
-
-BENCHFUN(defaultCtor)                  100000  1.082 s   10.82 us  90.23 k
-BM_copyCtor_fbstring/32k               100000  4.213 ms  42.13 ns  22.64 M
-BM_ctorFromArray_fbstring/32k          100000  113.2 ms  1.132 us    863 k
-BM_ctorFromChar_fbstring/1M            100000  9.162 ms  91.62 ns  10.41 M
-BM_assignmentOp_fbstring/256           100000  61.34 ms  613.4 ns  1.555 M
-BENCHFUN(assignmentFill)               100000  1.408 ms  14.08 ns  67.73 M
-BM_resize_fbstring/512k                100000  1.671 s   16.71 us  58.43 k
-BM_findSuccessful_fbstring/512k        100000  8.723 ms  87.23 ns  10.93 M
-BM_findUnsuccessful_fbstring/512k      100000  141.3 ms  1.413 us  691.2 k
-BM_replace_fbstring/256                100000  77.83 ms  778.3 ns  1.225 M
-BM_push_back_fbstring/1k               100000  1.744 s   17.44 us  55.99 k
-*/
+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