void QueueTest::maxQueueSize() {
  // Create a queue with a maximum size of 5, and fill it up

  for (int n = 0; n < 5; ++n) {
    queue.tryPutMessage(n);
  }

  // Calling tryPutMessage() now should fail
  EXPECT_THROW(queue.tryPutMessage(5), std::overflow_error);

  EXPECT_FALSE(queue.tryPutMessageNoThrow(5));
  int val = 5;
  EXPECT_FALSE(queue.tryPutMessageNoThrow(std::move(val)));

  // Pop a message from the queue
  int result = -1;
  EXPECT_TRUE(queue.tryConsume(result));
  EXPECT_EQ(0, result);

  // We should be able to write another message now that we popped one off.
  queue.tryPutMessage(5);
  // But now we are full again.
  EXPECT_THROW(queue.tryPutMessage(6), std::overflow_error);
  // putMessage() should let us exceed the maximum
  queue.putMessage(6);

  // Pull another mesage off
  EXPECT_TRUE(queue.tryConsume(result));
  EXPECT_EQ(1, result);

  // tryPutMessage() should still fail since putMessage() actually put us over
  // the max.
  EXPECT_THROW(queue.tryPutMessage(7), std::overflow_error);

  // Pull another message off and try again
  EXPECT_TRUE(queue.tryConsume(result));
  EXPECT_EQ(2, result);
  queue.tryPutMessage(7);

  // Now pull all the remaining messages off
  EXPECT_TRUE(queue.tryConsume(result));
  EXPECT_EQ(3, result);
  EXPECT_TRUE(queue.tryConsume(result));
  EXPECT_EQ(4, result);
  EXPECT_TRUE(queue.tryConsume(result));
  EXPECT_EQ(5, result);
  EXPECT_TRUE(queue.tryConsume(result));
  EXPECT_EQ(6, result);
  EXPECT_TRUE(queue.tryConsume(result));
  EXPECT_EQ(7, result);

  // There should be no messages left
  result = -1;
  EXPECT_TRUE(!queue.tryConsume(result));
  EXPECT_EQ(-1, result);
}
void QueueTest::maxQueueSize() {
  // Create a queue with a maximum size of 5, and fill it up

  for (int n = 0; n < 5; ++n) {
    queue.tryPutMessage(n);
  }

  // Calling tryPutMessage() now should fail
  BOOST_CHECK_THROW(queue.tryPutMessage(5), TQueueFullException);

  BOOST_CHECK_EQUAL(queue.tryPutMessageNoThrow(5), false);
  int val = 5;
  BOOST_CHECK_EQUAL(queue.tryPutMessageNoThrow(std::move(val)), false);

  // Pop a message from the queue
  int result = -1;
  BOOST_CHECK(queue.tryConsume(result));
  BOOST_CHECK_EQUAL(result, 0);

  // We should be able to write another message now that we popped one off.
  queue.tryPutMessage(5);
  // But now we are full again.
  BOOST_CHECK_THROW(queue.tryPutMessage(6), TQueueFullException);
  // putMessage() should let us exceed the maximum
  queue.putMessage(6);

  // Pull another mesage off
  BOOST_CHECK(queue.tryConsume(result));
  BOOST_CHECK_EQUAL(result, 1);

  // tryPutMessage() should still fail since putMessage() actually put us over
  // the max.
  BOOST_CHECK_THROW(queue.tryPutMessage(7), TQueueFullException);

  // Pull another message off and try again
  BOOST_CHECK(queue.tryConsume(result));
  BOOST_CHECK_EQUAL(result, 2);
  queue.tryPutMessage(7);

  // Now pull all the remaining messages off
  BOOST_CHECK(queue.tryConsume(result));
  BOOST_CHECK_EQUAL(result, 3);
  BOOST_CHECK(queue.tryConsume(result));
  BOOST_CHECK_EQUAL(result, 4);
  BOOST_CHECK(queue.tryConsume(result));
  BOOST_CHECK_EQUAL(result, 5);
  BOOST_CHECK(queue.tryConsume(result));
  BOOST_CHECK_EQUAL(result, 6);
  BOOST_CHECK(queue.tryConsume(result));
  BOOST_CHECK_EQUAL(result, 7);

  // There should be no messages left
  result = -1;
  BOOST_CHECK(!queue.tryConsume(result));
  BOOST_CHECK_EQUAL(result, -1);
}
TEST(NotificationQueueTest, ConsumeUntilDrainedStress) {
  for (size_t i = 0; i < 1 << 8; ++i) {
    // Basic tests: make sure we
    // - drain all the messages
    // - ignore any maxReadAtOnce
    // - can't add messages during draining
    EventBase eventBase;
    IntQueue queue;
    QueueConsumer consumer;
    consumer.fn = [&](int j) {
      EXPECT_THROW(queue.tryPutMessage(j), std::runtime_error);
      EXPECT_FALSE(queue.tryPutMessageNoThrow(j));
      EXPECT_THROW(queue.putMessage(j), std::runtime_error);
      std::vector<int> ints{1, 2, 3};
      EXPECT_THROW(
          queue.putMessages(ints.begin(), ints.end()),
          std::runtime_error);
    };
    consumer.setMaxReadAtOnce(10); // We should ignore this
    consumer.startConsuming(&eventBase, &queue);
    for (int j = 0; j < 20; j++) {
      queue.putMessage(j);
    }
    EXPECT_TRUE(consumer.consumeUntilDrained());
    EXPECT_EQ(20, consumer.messages.size());

    // Make sure there can only be one drainer at once
    folly::Baton<> callbackBaton, threadStartBaton;
    consumer.fn = [&](int /* i */) { callbackBaton.wait(); };
    QueueConsumer competingConsumer;
    competingConsumer.startConsuming(&eventBase, &queue);
    queue.putMessage(1);
    atomic<bool> raceA {false};
    atomic<bool> raceB {false};
    size_t numConsA = 0;
    size_t numConsB = 0;
    auto thread = std::thread([&]{
      threadStartBaton.post();
      raceB = consumer.consumeUntilDrained(&numConsB) && numConsB;
    });
    threadStartBaton.wait();
    raceA = competingConsumer.consumeUntilDrained(&numConsA) && numConsA;
    callbackBaton.post();
    thread.join();
    EXPECT_FALSE(raceA && raceB);
    EXPECT_TRUE(raceA || raceB);
    EXPECT_TRUE(raceA ^ raceB);
  }
}