blob: 58993ec791d48464ffe863c239fb8496fa14c163 [file] [log] [blame]
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*
*/
package org.apache.qpid.server.queue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.apache.qpid.server.message.MessageInstance;
import org.apache.qpid.test.utils.QpidTestCase;
public class ConsumerListTest extends QpidTestCase
{
private QueueConsumerList _subList;
private QueueConsumer _sub1;
private QueueConsumer _sub2;
private QueueConsumer _sub3;
private ConsumerNode _node;
protected void setUp()
{
_subList = new QueueConsumerList();
_sub1 = newMockConsumer();
_sub2 = newMockConsumer();
_sub3 = newMockConsumer();
_subList.add(_sub1);
_subList.add(_sub2);
_subList.add(_sub3);
_node = _subList.getHead();
}
private QueueConsumer newMockConsumer()
{
QueueConsumer consumer = mock(QueueConsumer.class);
MessageInstance.StealableConsumerAcquiredState
owningState = new MessageInstance.StealableConsumerAcquiredState(consumer);
when(consumer.getOwningState()).thenReturn(owningState);
return consumer;
}
/**
* Test that if the first (non-head) node in the list is deleted (but is still present),
* it is not returned when searching through the list for the next viable node, and the
* subsequent viable node is returned instead.
*/
public void testFindNextSkipsFirstDeletedNode()
{
assertTrue("Deleting consumer node should have succeeded",
getNodeForConsumer(_subList, _sub1).delete());
assertNotNull("Returned node should not be null", _node = _node.findNext());
assertEquals("Should have returned node for 2nd consumer", _sub2, _node.getConsumer());
assertNotNull("Returned node should not be null", _node = _node.findNext());
assertEquals("Should have returned node for 3rd consumer", _sub3, _node.getConsumer());
}
/**
* Test that if a central node in the list is deleted (but is still present),
* it is not returned when searching through the list for the next viable node,
* and the subsequent viable node is returned instead.
*/
public void testFindNextSkipsCentralDeletedNode()
{
assertNotNull("Returned node should not be null", _node = _node.findNext());
assertTrue("Deleting consumer node should have succeeded",
getNodeForConsumer(_subList, _sub2).delete());
assertNotNull("Returned node should not be null", _node = _node.findNext());
assertEquals("Should have returned node for 3rd consumer", _sub3, _node.getConsumer());
}
/**
* Test that if the last node in the list is deleted (but is still present),
* it is not returned when searching through the list for the next viable node,
* and null is returned instead.
*/
public void testFindNextSkipsLastDeletedNode()
{
assertNotNull("Returned node should not be null", _node = _node.findNext());
assertEquals("Should have returned node for 1st consumer", _sub1, _node.getConsumer());
assertNotNull("Returned node should not be null", _node = _node.findNext());
assertEquals("Should have returned node for 2nd consumer", _sub2, _node.getConsumer());
assertTrue("Deleting consumer node should have succeeded",
getNodeForConsumer(_subList, _sub3).delete());
assertNull("Returned node should be null", _node = _node.findNext());
}
/**
* Test that if multiple nodes in the list are deleted (but still present), they
* are not returned when searching through the list for the next viable node,
* and the subsequent viable node is returned instead.
*/
public void testFindNextSkipsMultipleDeletedNode()
{
assertTrue("Deleting consumer node should have succeeded",
getNodeForConsumer(_subList, _sub1).delete());
assertTrue("Deleting consumer node should have succeeded",
getNodeForConsumer(_subList, _sub2).delete());
assertNotNull("Returned node should not be null", _node = _node.findNext());
assertEquals("Should have returned node for 3rd consumer", _sub3, _node.getConsumer());
}
/**
* Test that if a node in the list is marked 'deleted' it is still present in the list
* until actually removed. counter-test to verify above testing of getNext() method.
*/
public void testDeletedNodeStillPresent()
{
assertTrue("Deleting consumer node should have succeeded",
getNodeForConsumer(_subList, _sub1).delete());
assertNotNull("Node marked deleted should still be present", getNodeForConsumer(_subList, _sub1));
assertEquals("All 3 nodes are still expected to be present", 3, countNodes(_subList));
}
/**
* Traverses the list nodes in a non-mutating fashion, returning the first node which matches the given
* Consumer, or null if none is found.
*/
private ConsumerNode getNodeForConsumer(final QueueConsumerList list, final QueueConsumer sub)
{
ConsumerNode node = list.getHead();
while (node != null && node.getConsumer() != sub)
{
node = node.nextNode();
}
return node;
}
/**
* Counts the number of (non-head) nodes in the list.
*/
private int countNodes(final QueueConsumerList list)
{
ConsumerNode node = list.getHead();
int count;
for(count = -1; node != null; count++)
{
node = node.nextNode();
}
return count;
}
/**
* Tests that the head is returned as expected, and isn't the node for the first consumer.
*/
public void testGetHead()
{
assertNotNull("List head should be non null", _node);
assertNotSame("Head should not be node for first consumer",
_node, getNodeForConsumer(_subList, _sub1));
}
/**
* Tests that the size is returned correctly in the face of additions and removals.
*/
public void testGetSize()
{
QueueConsumerList subList = new QueueConsumerList();
assertEquals("Unexpected size result", 0, subList.size());
QueueConsumer sub1 = newMockConsumer();
QueueConsumer sub2 = newMockConsumer();
QueueConsumer sub3 = newMockConsumer();
subList.add(sub1);
assertEquals("Unexpected size result", 1, subList.size());
subList.add(sub2);
assertEquals("Unexpected size result", 2, subList.size());
subList.add(sub3);
assertEquals("Unexpected size result", 3, subList.size());
assertTrue("Removing consumer from list should have succeeded", subList.remove(sub1));
assertEquals("Unexpected size result", 2, subList.size());
assertTrue("Removing consumer from list should have succeeded", subList.remove(sub2));
assertEquals("Unexpected size result", 1, subList.size());
assertTrue("Removing consumer from list should have succeeded", subList.remove(sub3));
assertEquals("Unexpected size result", 0, subList.size());
}
/**
* Test that if the first (non-head) node in the list is removed it is no longer
* present in the node structure of the list at all.
*/
public void testRemoveFirstNode()
{
assertNotNull("Should have been a node present for the consumer", getNodeForConsumer(_subList, _sub1));
assertTrue("Removing consumer node should have succeeded", _subList.remove(_sub1));
assertNull("Should not have been a node present for the removed consumer",
getNodeForConsumer(_subList, _sub1));
assertEquals("Unexpected number of nodes", 2, countNodes(_subList));
assertNotNull("Should have been a node present for the consumer", getNodeForConsumer(_subList, _sub2));
assertNotNull("Should have been a node present for the consumer", getNodeForConsumer(_subList, _sub3));
}
/**
* Test that if a central node in the list is removed it is no longer
* present in the node structure of the list at all.
*/
public void testRemoveCentralNode()
{
assertNotNull("Should have been a node present for the consumer", getNodeForConsumer(_subList, _sub2));
assertTrue("Removing consumer node should have succeeded", _subList.remove(_sub2));
assertNull("Should not have been a node present for the removed consumer",
getNodeForConsumer(_subList, _sub2));
assertEquals("Unexpected number of nodes", 2, countNodes(_subList));
assertNotNull("Should have been a node present for the consumer", getNodeForConsumer(_subList, _sub1));
assertNotNull("Should have been a node present for the consumer", getNodeForConsumer(_subList, _sub3));
}
/**
* Test that if the consumer contained in the last node of the list is removed
* it is no longer present in the node structure of the list at all. However,
* as the last node in the structure can't actually be removed a dummy will instead
* be present.
*/
public void testRemoveLastNode()
{
assertNotNull("Should have been a node present for the consumer", getNodeForConsumer(_subList, _sub3));
assertTrue("Removing consumer node should have succeeded", _subList.remove(_sub3));
assertNull("Should not have been a node present for the removed consumer",
getNodeForConsumer(_subList, _sub3));
//We actually expect 3 nodes to remain this time, because the last node cant be removed for thread safety reasons,
//however a dummy final node can be used as substitute to allow removal of the consumer node.
assertEquals("Unexpected number of nodes", 2 + 1, countNodes(_subList));
assertNotNull("Should have been a node present for the consumer", getNodeForConsumer(_subList, _sub1));
assertNotNull("Should have been a node present for the consumer", getNodeForConsumer(_subList, _sub2));
}
/**
* Test that if the consumer not contained in the list is requested to be removed
* that the removal fails
*/
public void testRemoveNonexistentNode()
{
QueueConsumer sub4 = newMockConsumer();
assertNull("Should not have been a node present for the consumer", getNodeForConsumer(_subList, sub4));
assertFalse("Removing consumer node should not have succeeded", _subList.remove(sub4));
assertEquals("Unexpected number of nodes", 3, countNodes(_subList));
}
/**
* Test that if a consumer node which occurs later in the main list than the marked node is
* removed from the list after the marked node is also removed, then the marker node doesn't
* serve to retain the subsequent nodes in the list structure (and thus memory) despite their
* removal.
*/
public void testDeletedMarkedNodeDoesntLeakSubsequentlyDeletedNodes()
{
//get the nodes out the list for the 1st and 3rd consumers
ConsumerNode sub1Node = getNodeForConsumer(_subList, _sub1);
assertNotNull("Should have been a node present for the consumer", sub1Node);
ConsumerNode sub3Node = getNodeForConsumer(_subList, _sub3);
assertNotNull("Should have been a node present for the consumer", sub3Node);
//mark the first consumer node
assertTrue("should have succeeded in updating the marked node",
_subList.updateMarkedNode(_subList.getMarkedNode(), sub1Node));
//remove the 1st consumer from the list
assertTrue("Removing consumer node should have succeeded", _subList.remove(_sub1));
//verify the 1st consumer is no longer the marker node (replaced by a dummy), or in the main list structure
assertNotSame("Unexpected marker node", sub1Node, _subList.getMarkedNode());
assertNull("Should not have been a node present in the list structure for the marked-but-removed sub1 node",
getNodeForConsumer(_subList, _sub1));
//remove the 2nd consumer from the list
assertTrue("Removing consumer node should have succeeded", _subList.remove(_sub2));
//verify the marker node isn't leaking subsequently removed nodes, by ensuring the very next node
//in its list structure is now the 3rd consumer (since the 2nd was removed too)
assertEquals("Unexpected next node", sub3Node, _subList.getMarkedNode().nextNode());
//remove the 3rd and final/tail consumer
assertTrue("Removing consumer node should have succeeded", _subList.remove(_sub3));
//verify the marker node isn't leaking subsequently removed nodes, by ensuring the very next node
//in its list structure is now the dummy tail (since the 3rd consumer was removed, and a dummy
//tail was inserted) and NOT the 3rd sub node.
assertNotSame("Unexpected next node", sub3Node, _subList.getMarkedNode().nextNode());
assertTrue("Unexpected next node", _subList.getMarkedNode().nextNode().isDeleted());
assertNull("Next non-deleted node from the marker should now be the list end, i.e. null", _subList.getMarkedNode().findNext());
}
/**
* Test that the marked node 'findNext' behaviour is as expected after a consumer is added
* to the list following the tail consumer node being removed while it is the marked node.
* That is, that the new consumers node is returned by getMarkedNode().findNext().
*/
public void testMarkedNodeFindsNewConsumerAfterRemovingTailWhilstMarked()
{
//get the node out the list for the 3rd consumer
ConsumerNode sub3Node = getNodeForConsumer(_subList, _sub3);
assertNotNull("Should have been a node present for the consumer", sub3Node);
//mark the 3rd consumer node
assertTrue("should have succeeded in updating the marked node",
_subList.updateMarkedNode(_subList.getMarkedNode(), sub3Node));
//verify calling findNext on the marked node returns null, i.e. the end of the list has been reached
assertEquals("Unexpected node after marked node", null, _subList.getMarkedNode().findNext());
//remove the 3rd(marked) consumer from the list
assertTrue("Removing consumer node should have succeeded", _subList.remove(_sub3));
//add a new 4th consumer to the list
QueueConsumer sub4 = newMockConsumer();
_subList.add(sub4);
//get the node out the list for the 4th consumer
ConsumerNode sub4Node = getNodeForConsumer(_subList, sub4);
assertNotNull("Should have been a node present for the consumer", sub4Node);
//verify the marked node (which is now a dummy substitute for the 3rd consumer) returns
//the 4th consumers node as the next non-deleted node.
assertEquals("Unexpected next node", sub4Node, _subList.getMarkedNode().findNext());
}
/**
* Test that setting the marked node to null doesn't cause problems during remove operations
*/
public void testRemoveWithNullMarkedNode()
{
//set the marker to null
assertTrue("should have succeeded in updating the marked node",
_subList.updateMarkedNode(_subList.getMarkedNode(), null));
//remove the 1st consumer from the main list
assertTrue("Removing consumer node should have succeeded", _subList.remove(_sub1));
//verify the 1st consumer is no longer in the main list structure
assertNull("Should not have been a node present in the main list structure for sub1",
getNodeForConsumer(_subList, _sub1));
assertEquals("Unexpected number of nodes", 2, countNodes(_subList));
}
/**
* Tests that after the first (non-head) node of the list is marked deleted but has not
* yet been removed, the iterator still skips it.
*/
public void testIteratorSkipsFirstDeletedNode()
{
//'delete' but don't remove the node for the 1st consumer
assertTrue("Deleting consumer node should have succeeded",
getNodeForConsumer(_subList, _sub1).delete());
assertNotNull("Should still have been a node present for the deleted consumer",
getNodeForConsumer(_subList, _sub1));
ConsumerNodeIterator iter = _subList.iterator();
//verify the iterator returns the 2nd consumers node
assertTrue("Iterator should have been able to advance", iter.advance());
assertEquals("Iterator returned unexpected ConsumerNode", _sub2, iter.getNode().getConsumer());
//verify the iterator returns the 3rd consumers node and not the 2nd.
assertTrue("Iterator should have been able to advance", iter.advance());
assertEquals("Iterator returned unexpected ConsumerNode", _sub3, iter.getNode().getConsumer());
}
/**
* Tests that after a central node of the list is marked deleted but has not yet been removed,
* the iterator still skips it.
*/
public void testIteratorSkipsCentralDeletedNode()
{
//'delete' but don't remove the node for the 2nd consumer
assertTrue("Deleting consumer node should have succeeded",
getNodeForConsumer(_subList, _sub2).delete());
assertNotNull("Should still have been a node present for the deleted consumer",
getNodeForConsumer(_subList, _sub2));
ConsumerNodeIterator iter = _subList.iterator();
//verify the iterator returns the 1st consumers node
assertTrue("Iterator should have been able to advance", iter.advance());
assertEquals("Iterator returned unexpected ConsumerNode", _sub1, iter.getNode().getConsumer());
//verify the iterator returns the 3rd consumers node and not the 2nd.
assertTrue("Iterator should have been able to advance", iter.advance());
assertEquals("Iterator returned unexpected ConsumerNode", _sub3, iter.getNode().getConsumer());
}
/**
* Tests that after the last node of the list is marked deleted but has not yet been removed,
* the iterator still skips it.
*/
public void testIteratorSkipsDeletedFinalNode()
{
//'delete' but don't remove the node for the 3rd consumer
assertTrue("Deleting consumer node should have succeeded",
getNodeForConsumer(_subList, _sub3).delete());
assertNotNull("Should still have been a node present for the deleted 3rd consumer",
getNodeForConsumer(_subList, _sub3));
ConsumerNodeIterator iter = _subList.iterator();
//verify the iterator returns the 1st consumers node
assertTrue("Iterator should have been able to advance", iter.advance());
assertEquals("Iterator returned unexpected ConsumerNode", _sub1, iter.getNode().getConsumer());
//verify the iterator returns the 2nd consumers node
assertTrue("Iterator should have been able to advance", iter.advance());
assertEquals("Iterator returned unexpected ConsumerNode", _sub2, iter.getNode().getConsumer());
//verify the iterator can no longer advance and does not return a consumer node
assertFalse("Iterator should not have been able to advance", iter.advance());
assertEquals("Iterator returned unexpected ConsumerNode", null, iter.getNode());
}
}