Use the GTest portability headers
[folly.git] / folly / test / ProducerConsumerQueueTest.cpp
index d322f0327f4902a15f490b32a09eb915d803c519..4fe0a6653bf5fc4dee61713f7093748ca5951393 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2012 Facebook, Inc.
+ * Copyright 2016 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * limitations under the License.
  */
 
-#include "folly/ProducerConsumerQueue.h"
+#include <folly/ProducerConsumerQueue.h>
 
-#include <gtest/gtest.h>
-#include <vector>
 #include <atomic>
 #include <chrono>
 #include <memory>
 #include <thread>
+#include <vector>
+
 #include <glog/logging.h>
 
+#include <folly/portability/GTest.h>
+
 //////////////////////////////////////////////////////////////////////
 
 namespace {
@@ -34,11 +36,11 @@ template<class T> struct TestTraits {
 };
 
 template<> struct TestTraits<std::string> {
-  int limit() const { return 1 << 21; }
+  unsigned int limit() const { return 1 << 22; }
   std::string generate() const { return std::string(12, ' '); }
 };
 
-template<class QueueType, size_t Size>
+template<class QueueType, size_t Size, bool Pop = false>
 struct PerfTest {
   typedef typename QueueType::value_type T;
 
@@ -61,16 +63,27 @@ struct PerfTest {
   }
 
   void producer() {
-    for (int i = 0; i < traits_.limit(); ++i) {
+    // This is written differently than you might expect so that
+    // it does not run afoul of -Wsign-compare, regardless of the
+    // signedness of this loop's upper bound.
+    for (auto i = traits_.limit(); i > 0; --i) {
       while (!queue_.write(traits_.generate())) {
       }
     }
   }
 
   void consumer() {
-    while (!done_) {
-      T data;
-      queue_.read(data);
+    /*static*/ if (Pop) {
+      while (!done_) {
+        if (queue_.frontPtr()) {
+          queue_.popFront();
+        }
+      }
+    } else {
+      while (!done_) {
+        T data;
+        queue_.read(data);
+      }
     }
   }
 
@@ -85,15 +98,16 @@ template<class TestType> void doTest(const char* name) {
   (*t)();
 }
 
-template<class T> void perfTestType(const char* type) {
+template<class T, bool Pop = false>
+void perfTestType(const char* type) {
   const size_t size = 0xfffe;
 
   LOG(INFO) << "Type: " << type;
-  doTest<PerfTest<folly::ProducerConsumerQueue<T>,size> >(
+  doTest<PerfTest<folly::ProducerConsumerQueue<T>,size,Pop> >(
     "ProducerConsumerQueue");
 }
 
-template<class QueueType, size_t Size>
+template<class QueueType, size_t Size, bool Pop>
 struct CorrectnessTest {
   typedef typename QueueType::value_type T;
 
@@ -103,7 +117,7 @@ struct CorrectnessTest {
   {
     const size_t testSize = traits_.limit();
     testData_.reserve(testSize);
-    for (int i = 0; i < testSize; ++i) {
+    for (size_t i = 0; i < testSize; ++i) {
       testData_.push_back(traits_.generate());
     }
   }
@@ -125,7 +139,39 @@ struct CorrectnessTest {
   }
 
   void consumer() {
-    for (auto& expect : testData_) {
+    if (Pop) {
+      consumerPop();
+    } else {
+      consumerRead();
+    }
+  }
+
+  void consumerPop() {
+    for (auto expect : testData_) {
+    again:
+      T* data;
+      if (!(data = queue_.frontPtr())) {
+        if (done_) {
+          // Try one more read; unless there's a bug in the queue class
+          // there should still be more data sitting in the queue even
+          // though the producer thread exited.
+          if (!(data = queue_.frontPtr())) {
+            EXPECT_TRUE(0 && "Finished too early ...");
+            return;
+          }
+        } else {
+          goto again;
+        }
+        EXPECT_EQ(*data, expect);
+      } else {
+        EXPECT_EQ(*data, expect);
+        queue_.popFront();
+      }
+    }
+  }
+
+  void consumerRead() {
+    for (auto expect : testData_) {
     again:
       T data;
       if (!queue_.read(data)) {
@@ -151,32 +197,35 @@ struct CorrectnessTest {
   std::atomic<bool> done_;
 };
 
-template<class T> void correctnessTestType(const std::string& type) {
+template<class T, bool Pop = false>
+void correctnessTestType(const std::string& type) {
   LOG(INFO) << "Type: " << type;
-  doTest<CorrectnessTest<folly::ProducerConsumerQueue<T>,0xfffe> >(
+  doTest<CorrectnessTest<folly::ProducerConsumerQueue<T>,0xfffe,Pop> >(
     "ProducerConsumerQueue");
 }
 
 struct DtorChecker {
-  static int numInstances;
+  static unsigned int numInstances;
   DtorChecker() { ++numInstances; }
-  DtorChecker(const DtorChecker& o) { ++numInstances; }
+  DtorChecker(const DtorChecker& /* o */) { ++numInstances; }
   ~DtorChecker() { --numInstances; }
 };
 
-int DtorChecker::numInstances = 0;
+unsigned int DtorChecker::numInstances = 0;
 
 }
 
 //////////////////////////////////////////////////////////////////////
 
 TEST(PCQ, QueueCorrectness) {
+  correctnessTestType<std::string,true>("string (front+pop)");
   correctnessTestType<std::string>("string");
   correctnessTestType<int>("int");
   correctnessTestType<unsigned long long>("unsigned long long");
 }
 
 TEST(PCQ, PerfTest) {
+  perfTestType<std::string,true>("string (front+pop)");
   perfTestType<std::string>("string");
   perfTestType<int>("int");
   perfTestType<unsigned long long>("unsigned long long");
@@ -222,3 +271,20 @@ TEST(PCQ, Destructor) {
   }
   EXPECT_EQ(DtorChecker::numInstances, 0);
 }
+
+TEST(PCQ, EmptyFull) {
+  folly::ProducerConsumerQueue<int> queue(3);
+  EXPECT_TRUE(queue.isEmpty());
+  EXPECT_FALSE(queue.isFull());
+
+  EXPECT_TRUE(queue.write(1));
+  EXPECT_FALSE(queue.isEmpty());
+  EXPECT_FALSE(queue.isFull());
+
+  EXPECT_TRUE(queue.write(2));
+  EXPECT_FALSE(queue.isEmpty());
+  EXPECT_TRUE(queue.isFull());  // Tricky: full after 2 writes, not 3.
+
+  EXPECT_FALSE(queue.write(3));
+  EXPECT_EQ(queue.sizeGuess(), 2);
+}