jai1 closed pull request #322: More than one connection per broker in C++ client
URL: https://github.com/apache/incubator-pulsar/pull/322
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/pulsar-client-cpp/include/pulsar/Client.h 
b/pulsar-client-cpp/include/pulsar/Client.h
index 70123f609a..9178ff1b02 100644
--- a/pulsar-client-cpp/include/pulsar/Client.h
+++ b/pulsar-client-cpp/include/pulsar/Client.h
@@ -163,6 +163,18 @@ class ClientConfiguration {
     ClientConfiguration& setTlsAllowInsecureConnection(bool allowInsecure);
     bool isTlsAllowInsecureConnection() const;
 
+    /*
+     * Set max number of of connections per broker.
+     * @param connectionsPerBroker - number of connections per brokers
+     * @note - if connections per broker < 1 then the value will be set as to 
1.
+     */
+    ClientConfiguration& setConnectionsPerBroker(size_t connectionsPerBroker);
+
+    /*
+     * Get number of connections per broker.
+     */
+    size_t getConnectionsPerBroker() const;
+
  private:
     const AuthenticationPtr& getAuthenticationPtr() const;
 
diff --git a/pulsar-client-cpp/lib/Client.cc b/pulsar-client-cpp/lib/Client.cc
index 27033a39b3..0a00f20c1b 100644
--- a/pulsar-client-cpp/lib/Client.cc
+++ b/pulsar-client-cpp/lib/Client.cc
@@ -41,6 +41,7 @@ struct ClientConfiguration::Impl {
     bool useTls;
     std::string tlsTrustCertsFilePath;
     bool tlsAllowInsecureConnection;
+    size_t connectionsPerBroker;
     Impl() : authenticationPtr(AuthFactory::Disabled()),
              ioThreads(1),
              operationTimeoutSeconds(30),
@@ -48,6 +49,7 @@ struct ClientConfiguration::Impl {
              concurrentLookupRequest(5000),
              logConfFilePath(),
              useTls(false),
+             connectionsPerBroker(1),
              tlsAllowInsecureConnection(true) {}
 };
 
@@ -163,6 +165,18 @@ const std::string& 
ClientConfiguration::getLogConfFilePath() const {
     return impl_->logConfFilePath;
 }
 
+ClientConfiguration& ClientConfiguration::setConnectionsPerBroker(size_t 
connectionsPerBroker) {
+    if (connectionsPerBroker < 1) {
+        LOG_ERROR("connectionsPerBroker set to 1");
+    }
+    impl_->connectionsPerBroker = std::max(1uL, connectionsPerBroker);
+    return *this;
+}
+
+size_t ClientConfiguration::getConnectionsPerBroker() const {
+    return impl_->connectionsPerBroker;
+}
+
 /////////////////////////////////////////////////////////////////
 
 Client::Client(const std::string& serviceUrl)
diff --git a/pulsar-client-cpp/lib/ClientImpl.cc 
b/pulsar-client-cpp/lib/ClientImpl.cc
index 85086509c5..866ace7ff9 100644
--- a/pulsar-client-cpp/lib/ClientImpl.cc
+++ b/pulsar-client-cpp/lib/ClientImpl.cc
@@ -63,7 +63,7 @@ namespace pulsar {
           
ioExecutorProvider_(boost::make_shared<ExecutorServiceProvider>(clientConfiguration.getIOThreads())),
           
listenerExecutorProvider_(boost::make_shared<ExecutorServiceProvider>(clientConfiguration.getMessageListenerThreads())),
           
partitionListenerExecutorProvider_(boost::make_shared<ExecutorServiceProvider>(clientConfiguration.getMessageListenerThreads())),
-          pool_(clientConfiguration, ioExecutorProvider_, 
clientConfiguration.getAuthenticationPtr(), poolConnections),
+          pool_(clientConfiguration, ioExecutorProvider_, 
clientConfiguration.getAuthenticationPtr(), poolConnections, 
clientConfiguration.getConnectionsPerBroker()),
           producerIdGenerator_(0),
           consumerIdGenerator_(0),
           requestIdGenerator_(0) {
diff --git a/pulsar-client-cpp/lib/ConnectionPool.cc 
b/pulsar-client-cpp/lib/ConnectionPool.cc
index 1fd07d0fb5..2d42668ebc 100644
--- a/pulsar-client-cpp/lib/ConnectionPool.cc
+++ b/pulsar-client-cpp/lib/ConnectionPool.cc
@@ -15,7 +15,6 @@
  */
 
 #include "ConnectionPool.h"
-
 #include "LogUtils.h"
 
 DECLARE_LOG_OBJECT()
@@ -24,34 +23,45 @@ namespace pulsar {
 
 ConnectionPool::ConnectionPool(const ClientConfiguration& conf,
                                ExecutorServiceProviderPtr executorProvider,
-                               const AuthenticationPtr& authentication, bool 
poolConnections)
+                               const AuthenticationPtr& authentication,
+                               bool poolConnections, size_t 
connectionsPerBroker)
         : clientConfiguration_(conf),
           executorProvider_(executorProvider),
           authentication_(authentication),
           pool_(),
           poolConnections_(poolConnections),
-          mutex_() {
+          mutex_(),
+          connectionsPerBroker(connectionsPerBroker) {
 }
 
 Future<Result, ClientConnectionWeakPtr> ConnectionPool::getConnectionAsync(
         const std::string& endpoint) {
     boost::unique_lock<boost::mutex> lock(mutex_);
-
+    PoolMap::iterator cnxIt = pool_.end();
     if (poolConnections_) {
-        PoolMap::iterator cnxIt = pool_.find(endpoint);
+        cnxIt = pool_.find(endpoint);
         if (cnxIt != pool_.end()) {
-            ClientConnectionPtr cnx = cnxIt->second.lock();
-
-            if (cnx && !cnx->isClosed()) {
-                // Found a valid or pending connection in the pool
-                LOG_DEBUG("Got connection from pool for " << endpoint << " 
use_count: "  //
-                        << (cnx.use_count() - 1) << " @ " << cnx.get());
-                return cnx->getConnectFuture();
-            } else {
-                // Deleting stale connection
-                LOG_INFO("Deleting stale connection from pool for " << 
endpoint << " use_count: "
-                        << (cnx.use_count() - 1) << " @ " << cnx.get());
-                pool_.erase(endpoint);
+            // endpoint exists in the map
+                ClientConnectionContainerPtr containerPtr = cnxIt->second;
+                if (containerPtr && containerPtr->full()) {
+                    // container is full - can start reusing connections
+                    ClientConnectionWeakPtr weakCnx;
+                    if (containerPtr->getNext(weakCnx)) {
+                        ClientConnectionPtr cnx = weakCnx.lock();
+                        if (cnx && !cnx->isClosed()) {
+                            // Found a valid or pending connection in the pool
+                            LOG_DEBUG("Got connection from pool for " << 
endpoint << " use_count: "//
+                                    << (cnx.use_count() - 1) << " @ " << 
cnx.get()
+                                    << " " << *containerPtr);
+                            return cnx->getConnectFuture();
+                        } else {
+                            // Deleting stale connection
+                            LOG_INFO("Deleting stale connection from pool for 
" << endpoint << " use_count: "
+                                    << (cnx.use_count() - 1) << " @ " << 
cnx.get()
+                                    << " " << *containerPtr);
+                            containerPtr->remove();
+                        }
+                    }
             }
         }
     }
@@ -62,8 +72,20 @@ Future<Result, ClientConnectionWeakPtr> 
ConnectionPool::getConnectionAsync(
     LOG_INFO("Created connection for " << endpoint);
 
     Future<Result, ClientConnectionWeakPtr> future = cnx->getConnectFuture();
-    pool_.insert(std::make_pair(endpoint, cnx));
-
+    if (poolConnections_) {
+        if (cnxIt == pool_.end()) {
+            // Need to insert a container in the map
+            ClientConnectionContainerPtr containerPtr = 
boost::make_shared<RoundRobinArray<ClientConnectionWeakPtr> 
>(connectionsPerBroker);
+            LOG_DEBUG("Adding Connection to a new Container " << 
*containerPtr);
+            ClientConnectionWeakPtr temp = cnx; // can't typecast and bind 
lvalue at same time
+            containerPtr->add(temp);
+            pool_.insert(std::make_pair(endpoint, containerPtr));
+        } else {
+            LOG_DEBUG("Adding Connection to an existing Container " << 
*(cnxIt->second));
+            ClientConnectionWeakPtr temp = cnx;
+            (cnxIt->second)->add(temp);
+        }
+    }
     lock.unlock();
 
     cnx->tcpConnectAsync();
diff --git a/pulsar-client-cpp/lib/ConnectionPool.h 
b/pulsar-client-cpp/lib/ConnectionPool.h
index b24c8b10e4..2fc17f16d4 100644
--- a/pulsar-client-cpp/lib/ConnectionPool.h
+++ b/pulsar-client-cpp/lib/ConnectionPool.h
@@ -19,20 +19,20 @@
 
 #include <pulsar/Result.h>
 
-#include "ClientConnection.h"
-
 #include <string>
 #include <map>
 #include <boost/thread/mutex.hpp>
+#include <lib/ClientConnection.h>
+#include <lib/RoundRobinArray.h>
 
 namespace pulsar {
 
 class ExecutorService;
-
+typedef boost::shared_ptr<RoundRobinArray<ClientConnectionWeakPtr> > 
ClientConnectionContainerPtr;
 class ConnectionPool {
  public:
     ConnectionPool(const ClientConfiguration& conf, ExecutorServiceProviderPtr 
executorProvider,
-                   const AuthenticationPtr& authentication, bool 
poolConnections = true);
+                   const AuthenticationPtr& authentication, bool 
poolConnections = true, size_t connectionsPerBroker = 1);
 
     Future<Result, ClientConnectionWeakPtr> getConnectionAsync(const 
std::string& endpoint);
 
@@ -40,9 +40,10 @@ class ConnectionPool {
     ClientConfiguration clientConfiguration_;
     ExecutorServiceProviderPtr executorProvider_;
     AuthenticationPtr authentication_;
-    typedef std::map<std::string, ClientConnectionWeakPtr> PoolMap;
+    typedef std::map<std::string, ClientConnectionContainerPtr> PoolMap;
     PoolMap pool_;
     bool poolConnections_;
+    size_t connectionsPerBroker;
     boost::mutex mutex_;
 
     friend class ConnectionPoolTest;
diff --git a/pulsar-client-cpp/lib/RoundRobinArray.h 
b/pulsar-client-cpp/lib/RoundRobinArray.h
new file mode 100644
index 0000000000..bae5db17c7
--- /dev/null
+++ b/pulsar-client-cpp/lib/RoundRobinArray.h
@@ -0,0 +1,147 @@
+/**
+ * Copyright 2016 Yahoo Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef PULSAR_CPP_CLIENTCONNECTIONCONTAINER_H
+#define PULSAR_CPP_CLIENTCONNECTIONCONTAINER_H
+
+#include <vector>
+#include <iostream>
+#include <algorithm>    // std::min
+namespace pulsar {
+/* @brief - This class uses a vector to store elements and provides a wrap 
around getNext() function to retrieve elements in a round robin fashion.
+ * @note - this class is not thread safe.
+ */
+template<class T>
+class RoundRobinArray {
+ private:
+    size_t capacity_;
+    size_t currentIndex_;
+    std::vector<T> array_;
+ public:
+    /*
+     * @param - the capacity of the container (capacity > 0)
+     * @note - A container of capacity 1 is created if given capacity is 0.
+     */
+    RoundRobinArray(size_t);
+
+    /*
+     * @returns - true if the container has reached it's max capacity.
+     */
+    inline bool full() const;
+
+    /*
+     * @brief - gets the next element in the container - wraps around after 
the last element.
+     * @returns - false if the list is empty.
+     */
+    bool getNext(T&);
+
+    /*
+     * @brief - Adds the element to the end of the list.
+     * @returns - false if the list is full.
+     */
+    bool add(T&);
+
+    /*
+     * @brief - removes element in reverse order starting from the one 
returned by last getNext() call
+     *                   - removes the oldest element if getNext() never called
+     * @return - true if an element was removed
+     */
+    bool remove();
+
+    /*
+     * @returns - the size of the container
+     */
+    inline size_t size() const;
+
+    /*
+     * @returns - the capacity of the container
+     */
+    inline size_t capacity() const;
+
+    /*
+     * @returns - true if the list is empty
+     */
+    inline bool empty() const;
+
+    // http://web.mst.edu/~nmjxv3/articles/templates.html
+    friend std::ostream& operator<<(std::ostream& os, const 
RoundRobinArray<T>& obj) {
+        os << "ClientConnectionContainer [ size_ = " << obj.size() << ", 
currentIndex_ = "
+                << obj.currentIndex_ << ", capacity = " << obj.capacity_ << 
"]";
+        return os;
+    }
+};
+
+template<class T> RoundRobinArray<T>::RoundRobinArray(size_t capacity)
+        : capacity_(std::max(capacity, 1uL)),
+          currentIndex_(-1) {
+}
+
+template<class T> bool RoundRobinArray<T>::full() const {
+    return array_.size() >= capacity_;
+}
+
+template<class T> bool RoundRobinArray<T>::empty() const {
+    return array_.empty();
+}
+
+template<class T> bool RoundRobinArray<T>::getNext(T& element) {
+    if (array_.empty()) {
+        return false;
+    }
+    if (++currentIndex_ >= array_.size()) {
+        currentIndex_ = 0;
+    }
+    element = array_[currentIndex_];
+    return true;
+}
+
+template<class T> bool RoundRobinArray<T>::add(T& element) {
+    if (full()) {
+        return false;
+    }
+    array_.push_back(element);
+    return true;
+}
+
+template<class T> bool RoundRobinArray<T>::remove() {
+    if (array_.empty()) {
+        return false;
+    } else if (array_.size() == 1) {
+        array_.clear();
+        currentIndex_ = -1;
+        return true;
+    } else if (currentIndex_ == -1) {
+        array_.erase(array_.begin());
+        return true;
+    }
+    // array size >= 2
+    array_.erase(array_.begin() + currentIndex_);
+    // array size >= 1
+    if (--currentIndex_ >= array_.size() ) { // unsigned
+        currentIndex_ = array_.size() - 1;
+    }
+    return true;
+}
+
+template<class T> size_t RoundRobinArray<T>::size() const {
+    return array_.size();
+}
+
+template<class T> size_t RoundRobinArray<T>::capacity() const {
+    return capacity_;
+}
+}
+
+#endif //PULSAR_CPP_CLIENTCONNECTIONCONTAINER_H
diff --git a/pulsar-client-cpp/tests/BasicEndToEndTest.cc 
b/pulsar-client-cpp/tests/BasicEndToEndTest.cc
index fa71f8a49c..4126033a68 100644
--- a/pulsar-client-cpp/tests/BasicEndToEndTest.cc
+++ b/pulsar-client-cpp/tests/BasicEndToEndTest.cc
@@ -167,7 +167,7 @@ void resendMessage(Result r, const Message& msg, Producer 
&producer) {
     ASSERT_EQ(ResultOk, result);
 
     Message receivedMsg;
-    consumer.receive(receivedMsg);
+    ASSERT_EQ(ResultOk, consumer.receive(receivedMsg));
     ASSERT_EQ(content, receivedMsg.getDataAsString());
     ASSERT_EQ(ResultOk, consumer.unsubscribe());
     ASSERT_EQ(ResultAlreadyClosed, consumer.close());
@@ -287,7 +287,7 @@ TEST(BasicEndToEndTest, testLookupThrottling) {
     LOG_INFO("Trying to receive 10 messages");
     Message msgReceived;
     for (int i = 0; i < 10; i++) {
-        consumer.receive(msgReceived, 1000);
+        ASSERT_EQ(ResultOk, consumer.receive(msgReceived, 1000));
         LOG_INFO("Received message :" << msgReceived.getMessageId());
         ASSERT_EQ(msgContent, msgReceived.getDataAsString());
         ASSERT_EQ(boost::lexical_cast<std::string>(i), 
msgReceived.getProperty("msgIndex"));
@@ -370,12 +370,14 @@ TEST(BasicEndToEndTest, testLookupThrottling) {
 
 TEST(BasicEndToEndTest, testPartitionedProducerConsumer)
 {
+    ClientConfiguration config;
+    config.setConnectionsPerBroker(4);
     Client client(lookupUrl);
     std::string topicName = "persistent://prop/unit/ns/partition-test";
 
     // call admin api to make it partitioned
     std::string url = adminUrl + 
"admin/persistent/prop/unit/ns/partition-test/partitions";
-    int res = makePutRequest(url, "3");
+    int res = makePutRequest(url, "13");
 
     LOG_INFO("res = "<<res);
     ASSERT_FALSE(res != 204 && res != 409);
@@ -402,8 +404,8 @@ TEST(BasicEndToEndTest, testPartitionedProducerConsumer)
     ASSERT_EQ(consumer.getSubscriptionName(), "subscription-A");
     for (int i = 0; i < 10; i++) {
         Message m;
-        consumer.receive(m, 10000);
-        consumer.acknowledge(m);
+        ASSERT_EQ(ResultOk, consumer.receive(m, 10000));
+        ASSERT_EQ(ResultOk, consumer.acknowledge(m));
     }
     client.shutdown();
 }
@@ -459,10 +461,10 @@ TEST(BasicEndToEndTest, testMessageTooBig)
     ASSERT_EQ(ResultOk, result);
 
     Message receivedMsg;
-    consumer.receive(receivedMsg);
+    ASSERT_EQ(ResultOk, consumer.receive(receivedMsg));
     ASSERT_EQ(content1, receivedMsg.getDataAsString());
 
-    consumer.receive(receivedMsg);
+    ASSERT_EQ(ResultOk, consumer.receive(receivedMsg));
     ASSERT_EQ(content2, receivedMsg.getDataAsString());
 
     ASSERT_EQ(ResultOk, consumer.unsubscribe());
@@ -498,10 +500,10 @@ TEST(BasicEndToEndTest, testMessageTooBig)
     ASSERT_EQ(ResultOk, result);
 
     Message receivedMsg;
-    consumer.receive(receivedMsg);
+    ASSERT_EQ(ResultOk, consumer.receive(receivedMsg));
     ASSERT_EQ(content1, receivedMsg.getDataAsString());
 
-    consumer.receive(receivedMsg);
+    ASSERT_EQ(ResultOk, consumer.receive(receivedMsg));
     ASSERT_EQ(content2, receivedMsg.getDataAsString());
 
     ASSERT_EQ(ResultOk, consumer.unsubscribe());
@@ -559,9 +561,10 @@ TEST(BasicEndToEndTest, testSinglePartitionRoutingPolicy)
         ASSERT_EQ(ResultOk, producer.send(msg));
     }
 
+    // Since we are getting all messages in order that means we are using a 
single producer
     for (int i = 0; i < 10; i++) {
         Message m;
-        consumer.receive(m);
+        ASSERT_EQ(ResultOk, consumer.receive(m));
         consumer.acknowledgeCumulative(m);
     }
     consumer.close();
diff --git a/pulsar-client-cpp/tests/BatchMessageTest.cc 
b/pulsar-client-cpp/tests/BatchMessageTest.cc
index 4dfa290493..cbe1d89008 100644
--- a/pulsar-client-cpp/tests/BatchMessageTest.cc
+++ b/pulsar-client-cpp/tests/BatchMessageTest.cc
@@ -760,7 +760,9 @@ TEST(BatchMessageTest, testPermits) {
 }
 
 TEST(BatchMessageTest, testPartitionedTopics) {
-    Client client(lookupUrl);
+    ClientConfiguration clientConfig;
+    clientConfig.setConnectionsPerBroker(3);
+    Client client(lookupUrl, clientConfig);
     std::string topicName = 
"persistent://property/cluster/namespace/test-partitioned-batch-messages-" + 
boost::lexical_cast<std::string>(epochTime) ;
 
     // call admin api to make it partitioned
diff --git a/pulsar-client-cpp/tests/RoundRobinArrayTest.cc 
b/pulsar-client-cpp/tests/RoundRobinArrayTest.cc
new file mode 100644
index 0000000000..8014e3e74c
--- /dev/null
+++ b/pulsar-client-cpp/tests/RoundRobinArrayTest.cc
@@ -0,0 +1,200 @@
+/**
+ * Copyright 2016 Yahoo Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>     /* srand, rand */
+#include <time.h>       /* time */
+#include <gtest/gtest.h>
+#include <lib/LogUtils.h>
+#include <RoundRobinArray.h>
+DECLARE_LOG_OBJECT();
+
+using namespace pulsar;
+
+TEST(RoundRobinArrayTest, basicWorking) {
+    int nextValue;
+    // Initializing a random variable
+    srand(time(NULL));
+
+    // Initialize a container of size 3
+    RoundRobinArray<int> container(3);
+    // Successfully add 3 elements
+    int obj = 0;
+    ASSERT_TRUE(container.add(obj));
+    obj = 1;
+    ASSERT_TRUE(container.add(obj));
+    ASSERT_FALSE(container.full());
+    obj = 2;
+    ASSERT_TRUE(container.add(obj));
+    ASSERT_TRUE(container.full());
+
+    // Fail on trying to add the fourth element
+    obj = 3;
+    ASSERT_FALSE(container.add(obj));
+    ASSERT_EQ(container.size(), 3);
+    ASSERT_TRUE(container.full());
+
+    // random number [1 100]
+    int rnumber = rand() % 100 + 1;
+    LOG_DEBUG("rnumber = " << rnumber);
+
+    // Test wrap around functionality
+    for (int i = 0; i < rnumber; i++) {
+        ASSERT_TRUE(container.getNext(nextValue));
+        ASSERT_EQ(nextValue, i % 3);
+    }
+
+    // Test remove functionality
+    int sum = 0;
+    for (int i = 0; i < 3; i++) {
+        ASSERT_TRUE(container.getNext(nextValue));
+        sum += nextValue;
+        ASSERT_TRUE(container.remove());
+        ASSERT_EQ(container.size(), 3 - (i + 1));
+        ASSERT_FALSE(container.full());
+    }
+    // 3 = 0 + 1 + 2
+    ASSERT_EQ(sum, 3);
+    ASSERT_FALSE(container.remove());
+    ASSERT_FALSE(container.getNext(nextValue));
+}
+
+TEST(RoundRobinArrayTest, removeOperation) {
+    int nextValue;
+    // Initialize a container of size 3
+    RoundRobinArray<int> container(5);
+    ASSERT_EQ(container.capacity(), 5);
+
+    // Successfully add 2 elements
+    int obj = 0;
+    ASSERT_TRUE(container.add(obj));
+    obj = 1;
+    ASSERT_TRUE(container.add(obj));
+
+    // [0 1] - remove before getNext() - hence oldest(0) removed
+    ASSERT_TRUE(container.remove());
+    ASSERT_EQ(container.size(), 1);
+    ASSERT_TRUE(container.getNext(nextValue));
+    ASSERT_EQ(nextValue, 1);
+    ASSERT_TRUE(container.getNext(nextValue));
+    ASSERT_EQ(nextValue, 1);
+
+    // [1 2 3] - adding
+    obj = 2;
+    ASSERT_TRUE(container.add(obj));
+    obj = 3;
+    ASSERT_TRUE(container.add(obj));
+
+    // [1 2 3] - last getNext returned 1 hence 1 deleted
+    ASSERT_TRUE(container.remove());
+
+    // [2 3]
+    // jumps over an element if the element returned by previous call is 
deleted
+    ASSERT_TRUE(container.getNext(nextValue));
+    ASSERT_EQ(nextValue, 2);
+    ASSERT_TRUE(container.getNext(nextValue));
+    ASSERT_EQ(nextValue, 3);
+    ASSERT_TRUE(container.getNext(nextValue));
+    ASSERT_EQ(nextValue, 2);
+
+    // [2 3] - last getNext returned 2 hence 2 deleted
+    ASSERT_TRUE(container.remove());
+    ASSERT_TRUE(container.getNext(nextValue));
+    ASSERT_EQ(nextValue, 3);
+    ASSERT_TRUE(container.getNext(nextValue));
+    ASSERT_EQ(nextValue, 3);
+    ASSERT_TRUE(container.remove());
+
+    // empty list
+    ASSERT_FALSE(container.remove());
+
+    // [4 5] - adding
+    obj = 4;
+    ASSERT_TRUE(container.add(obj));
+    obj = 5;
+    ASSERT_TRUE(container.add(obj));
+
+    // since list became empty - we start deleting from start (4)
+    ASSERT_TRUE(container.remove());
+
+    // [5]
+    ASSERT_TRUE(container.getNext(nextValue));
+    ASSERT_EQ(nextValue, 5);
+    obj = 6;
+    ASSERT_TRUE(container.add(obj));
+    obj = 7;
+    ASSERT_TRUE(container.add(obj));
+    obj = 8;
+    ASSERT_TRUE(container.add(obj));
+    obj = 9;
+    ASSERT_TRUE(container.add(obj));
+
+    //  v
+    // [5 6 7 8 9]
+    ASSERT_TRUE(container.remove());  // 5 removed
+    // 9 removed - removes element in reverse order starting from the one 
returned by last getNext() call
+    //        v
+    // [6 7 8 9]
+    ASSERT_TRUE(container.remove());
+    // 8 removed - removes element in reverse order starting from the one 
returned by last getNext() call
+    //      v
+    // [6 7 8]
+    ASSERT_TRUE(container.remove());
+    //    v
+    // [6 7]
+    ASSERT_TRUE(container.getNext(nextValue));
+    ASSERT_EQ(nextValue, 6);
+    ASSERT_TRUE(container.getNext(nextValue));
+    ASSERT_EQ(nextValue, 7);
+    ASSERT_TRUE(container.remove());  // 7 removed
+    ASSERT_TRUE(container.getNext(nextValue));
+    ASSERT_EQ(nextValue, 6);
+    ASSERT_TRUE(container.remove());  // 6 removed
+
+    ASSERT_FALSE(container.getNext(nextValue));
+}
+
+TEST(RoundRobinArrayTest, negativeTests) {
+    int nextValue;
+    RoundRobinArray<int> container1(3);
+    ASSERT_FALSE(container1.getNext(nextValue));
+
+    RoundRobinArray<int> container2(0);
+    ASSERT_EQ(container2.size(), 0);
+    ASSERT_EQ(container2.capacity(), 1);
+    ASSERT_TRUE(container2.empty());
+    ASSERT_FALSE(container2.full());
+}
+
+TEST(RoundRobinArrayTest, addOperation) {
+    int nextValue;
+    // Initialize a container of size 3
+    RoundRobinArray<int> container(1);
+    // Successfully add 2 elements
+    int obj = 0;
+    ASSERT_TRUE(container.add(obj));
+    obj = 1;
+    ASSERT_FALSE(container.add(obj));
+
+    ASSERT_TRUE(container.getNext(nextValue));
+    ASSERT_EQ(nextValue, 0);
+    ASSERT_TRUE(container.getNext(nextValue));
+    ASSERT_EQ(nextValue, 0);
+
+    ASSERT_TRUE(container.remove());
+    ASSERT_FALSE(container.full());
+    ASSERT_EQ(container.size(), 0);
+}
+


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
[email protected]


With regards,
Apache Git Services

Reply via email to