/** 
 * 
 * Copyright 2004 Protique Ltd
 * 
 * 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. 
 * 
 **/

package org.activemq.io.util;
import java.util.ArrayList;
import java.util.List;

import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;

import org.activemq.message.ActiveMQMessage;

import EDU.oswego.cs.dl.util.concurrent.Semaphore;
import EDU.oswego.cs.dl.util.concurrent.SynchronizedBoolean;

/**
 * MemoryBoundedQueueTest
 * 
 * @version $Revision: 1.1.1.1 $
 */
public class MemoryBoundedQueueTest extends TestCase {
    private static final int TEST_INSTANCE_SIZE = 2048;
    private static final int TEST_ENQUEUE_SIZE = TEST_INSTANCE_SIZE / 2;
    private static final String QUEUE_NAME = "TestQueue";
    private final int TOTAL_LOAD = 100000;
    private final int NUMBER_CONSUMERS = 20;
    private Semaphore stoppedSemaphore = new Semaphore(1-NUMBER_CONSUMERS);
    
    private final MemoryBoundedObjectManager memoryManager = new MemoryBoundedObjectManager("testmanager", 1024 * 1024);
    private final MemoryBoundedQueueManager queueManager = new MemoryBoundedQueueManager(memoryManager);
    
    private class Dequeue implements Runnable {
        private MemoryBoundedQueue queue;
        private int num = 0;
        private int localCount;

        Dequeue(MemoryBoundedQueue q, int num, int localCount) {
            this.queue = q;
            this.num = num;
            this.localCount = localCount;
        }

        public void run() {
            try {
            int internalCount = 0;
                while (internalCount < localCount) {
                    Thread.yield();
                    MemoryManageable obj = queue.dequeue();
                    if (obj == null) {
                        break;
                    }
                    internalCount++;
                }
            } catch (InterruptedException ie) {
                ie.printStackTrace();
            } finally {
                stoppedSemaphore.release();
            }
        }
    }

    public void testLoad() throws Exception {

        final MemoryBoundedQueue queue = queueManager.getMemoryBoundedQueue(QUEUE_NAME);
        memoryManager.setValueLimit(TEST_INSTANCE_SIZE * NUMBER_CONSUMERS);
        final List list = new ArrayList(NUMBER_CONSUMERS);
        int numberOfMessages = TOTAL_LOAD / NUMBER_CONSUMERS;
        for (int i = 0;i < NUMBER_CONSUMERS;i++) {
            Dequeue dq = new Dequeue(queue, i, numberOfMessages);
            list.add(dq);
            Thread t = new Thread(dq);
            t.setPriority(Thread.NORM_PRIORITY - 1);
            t.start();
        }
        for (int i = 0;i < TOTAL_LOAD;i++) {
            ActiveMQMessage msg = new ActiveMQMessage();
            msg.setMemoryUsage(TEST_INSTANCE_SIZE);
            queue.enqueue(msg);
        }
        try {
            // Assert that all the consumers stopped.
            assertTrue(stoppedSemaphore.attempt(1000*30));
        }
        catch (InterruptedException ie) {
            ie.printStackTrace();
        }
        
        //Thread.sleep(250);
        assertEquals(0, memoryManager.getTotalMemoryUsedSize());
        queue.close();
    }

    public void testClear() {
        final MemoryBoundedQueue queue = queueManager.getMemoryBoundedQueue(QUEUE_NAME);
        memoryManager.setValueLimit(TEST_INSTANCE_SIZE);
        ActiveMQMessage msg = new ActiveMQMessage();
        queue.enqueue(msg);
        queue.clear();
        assertTrue(queue.size() == 0);
        queue.close();
    }

    public void testDequeue() throws Exception {
        final MemoryBoundedQueue queue = queueManager.getMemoryBoundedQueue(QUEUE_NAME);
        memoryManager.setValueLimit(TEST_INSTANCE_SIZE * 100);
        ActiveMQMessage msg = new ActiveMQMessage();
        queue.enqueue(msg);
        Object result = queue.dequeue();
        assertTrue(result == msg);
        queue.close();
    }

    public void testClose() {
        /** @todo: Insert test code here. Use assertEquals(), for example. */
        final MemoryBoundedQueue queue = queueManager.getMemoryBoundedQueue(QUEUE_NAME);
        memoryManager.setValueLimit(TEST_ENQUEUE_SIZE);
        final SynchronizedBoolean success = new SynchronizedBoolean(false);
        final MemoryBoundedQueue q1 = queueManager.getMemoryBoundedQueue(QUEUE_NAME);
        assertTrue(queue == q1);
        Thread t = new Thread(new Runnable() {
            public void run() {
                try {
                    Thread.sleep(250);
                    queue.dequeue();
                }
                catch (Exception e) {
                    e.printStackTrace();
                }
                synchronized (success) {
                    success.set(true);
                    success.notify();
                }
            }
        });
        t.start();
        queue.close();
        try {
            synchronized (success) {
                if (!success.get()) {
                    success.wait(2000);
                }
            }
        }
        catch (Throwable e) {
            e.printStackTrace();
        }
        assertTrue(success.get());
       
        MemoryBoundedQueue q2 = queueManager.getMemoryBoundedQueue(QUEUE_NAME);
        assertTrue(queue != q2);
    }

    public void testDequeueNoWait() throws Exception {
        final MemoryBoundedQueue queue = queueManager.getMemoryBoundedQueue(QUEUE_NAME);
        Object obj = queue.dequeueNoWait();
        assertTrue(obj == null);
        queue.close();
    }

    public void testEnqueueFirst() throws Exception {
        final MemoryBoundedQueue queue = queueManager.getMemoryBoundedQueue(QUEUE_NAME);
        assertTrue(memoryManager.getTotalMemoryUsedSize() == 0);
        Object mutex = new Object();
        memoryManager.setValueLimit(TEST_INSTANCE_SIZE * 100);
        for (int i = 0;i < 10;i++) {
            queue.enqueue(new ActiveMQMessage());
        }
        ActiveMQMessage test = new ActiveMQMessage();
        test.setJMSMessageID("FIRST");
        queue.enqueueFirst(test);
        Object obj = queue.dequeue();
        assertTrue(obj == test);
        queue.close();
    }

    public void testEnqueueNoBlock() {
        MemoryBoundedQueue queue = queueManager.getMemoryBoundedQueue(QUEUE_NAME);
        memoryManager.setValueLimit(TEST_ENQUEUE_SIZE);
        ActiveMQMessage msg = new ActiveMQMessage();
        queue.enqueueNoBlock(msg);
        assertTrue(true);
        queue.close();
    }

    public void testIsEmpty() {
        int size = 10;
        MemoryBoundedQueue queue = queueManager.getMemoryBoundedQueue(QUEUE_NAME);
        for (int i = 0;i < size;i++) {
            queue.enqueue(new ActiveMQMessage());
        }
        queue.clear();
        assertTrue(queue.isEmpty());
        queue.close();
    }

    public void testRemove() {
        MemoryBoundedQueue queue = queueManager.getMemoryBoundedQueue(QUEUE_NAME);
        ActiveMQMessage msg = new ActiveMQMessage();
        queue.enqueue(msg);
        assertTrue(queue.remove(msg));
        queue.close();
    }

    public void testSize() {
        int size = 10;
        MemoryBoundedQueue queue = queueManager.getMemoryBoundedQueue(QUEUE_NAME);
        for (int i = 0;i < size;i++) {
            queue.enqueue(new ActiveMQMessage());
        }
        assertTrue(queue.size() == size);
        queue.close();
    }
    
    public void testRemovePacket(){
        int size = 100;
        MemoryBoundedQueue queue = queueManager.getMemoryBoundedQueue(QUEUE_NAME);
        List list = new ArrayList(size);
        for (int i = 0;i < size;i++) {
            ActiveMQMessage msg = new ActiveMQMessage();
            msg.setJMSMessageID(""+i);
            list.add(msg);
            queue.enqueue(msg);
        }
        for (int i =0; i < size; i++){
            queue.remove((ActiveMQMessage)list.get(i));
        }
        assertTrue(queue.size() == 0);
        queue.close();
    }
    
    public void testRemovePacketById(){
        int size = 100;
        MemoryBoundedQueue queue = queueManager.getMemoryBoundedQueue(QUEUE_NAME);
        List list = new ArrayList(size);
        for (int i = 0;i < size;i++) {
            ActiveMQMessage msg = new ActiveMQMessage();
            msg.setJMSMessageID(""+i);
            list.add(msg);
            queue.enqueue(msg);
        }
        for (int i =0; i < size; i++){
            ActiveMQMessage p = (ActiveMQMessage)list.get(i);
            MemoryManageable removed = queue.remove(p.getJMSMessageID());
            assertTrue(removed != null);
            assertTrue(removed == p);
        }
        assertTrue(queue.size() == 0);
        queue.close();
    }

    public static Test suite() {
        return new TestSuite(MemoryBoundedQueueTest.class);
    }

    public static void main(String[] args) {
        MemoryBoundedQueueTest test = new MemoryBoundedQueueTest();
        test.setName("test");
        test.testClose();
    }
}