/** 
 * 
 * Copyright 2004 Hiram Chirino
 * 
 * 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.filter;

import junit.framework.TestCase;
import org.activemq.filter.MultiExpressionEvaluator.ExpressionListener;
import org.activemq.message.ActiveMQMessage;
import org.activemq.selector.SelectorParser;

import javax.jms.JMSException;
import javax.jms.Message;
import java.util.ArrayList;
import java.util.Iterator;

/**
 * Test cases for the MultiExpressionEvaluator class.
 */
public class MultiExpressionEvaluatorTest extends TestCase {
    MultiExpressionEvaluator evaluator = new MultiExpressionEvaluator();
    ArrayList expressions = new ArrayList();

    /**
     * @see junit.framework.TestCase#setUp()
     */
    protected void setUp() throws Exception {
        // Add a few expressions that have matching nested expressions.
        // Make sure that there are 3 more true expressions than false ones.
        expressions.add(parseTrue("(3+4=7 AND 3+4=7) OR 1=10"));// true
        expressions.add(parseTrue("(3+4=7 AND 3+4=7) AND 3+4=7"));// true
        expressions.add(parseTrue("(3+4)*10=70"));// true

        expressions.add(parseTrue("((3+4=7 AND 3+4=7) OR 1=10) AND 10 BETWEEN 0 AND 100"));// true
        expressions.add(parseFalse("(3+4)*5=70"));// false

        expressions.add(parseTrue("10 BETWEEN 0 AND 100"));// true
        expressions.add(parseFalse("((3+4=7 OR 3+4=7) OR 1=10) AND 1=10"));// false
    }

    /**
     * @param string
     * @return
     */
    private Expression parseTrue(String string) throws Exception {
        Expression rc = parse(string);
        assertEquals(rc.evaluate(new ActiveMQMessage()), Boolean.TRUE);
        return rc;
    }

    /**
     * @param string
     * @return
     */
    private Expression parseFalse(String string) throws Exception {
        Expression rc = parse(string);
        assertEquals(rc.evaluate(new ActiveMQMessage()), Boolean.FALSE);
        return rc;
    }

    protected Expression parse(String text) throws Exception {
        ExpressionFilter filter = (ExpressionFilter) new SelectorParser()
                .parse(text);
        return filter.getExpression();
    }

    /**
     * There are 3 expression that are true in the expressions list.
     * test them all and see if the count is right.
     */
    public void testSimpleEvaluation() {

        final int hits[] = new int[]{0};
        final boolean test[] = new boolean[]{true};

        for (Iterator i = expressions.iterator(); i.hasNext();) {
            Expression expr = (Expression) i.next();
            evaluator.addExpressionListner(expr, new ExpressionListener() {
                public void evaluateResultEvent(Expression selector,
                                                Message message, Object result) {
                    if (result.equals(Boolean.TRUE)) {
                        hits[0]++;
                    }
                    else {
                        hits[0]--;
                    }
                }
            });
        }

        evaluator.evaluate(new ActiveMQMessage());
        assertTrue(hits[0] == 3);
    }

    static final public int SPEED_TEST_ITERATIONS = 100000;

    public void testSpeedUsingMultiExpressionEvaluator() {
        // setup
        // Add all the expressions to the MultiExpressionEvaluator
        for (Iterator i = expressions.iterator(); i.hasNext();) {
            Expression expr = (Expression) i.next();
            evaluator.addExpressionListner(expr, new ExpressionListener() {
                public void evaluateResultEvent(Expression selector, Message message, Object result) {
                }
            });
        }
        ActiveMQMessage message = new ActiveMQMessage();

        // Now evaluate:
        long start = System.currentTimeMillis();
        for (int i = 0; i < SPEED_TEST_ITERATIONS; i++) {
            evaluator.evaluate(message);
        }
        long end = System.currentTimeMillis();
        printSpeedReport("Using MultiExpressionEvaluator", start, end);
    }

    public void testSpeedNotUsingMultiExpressionEvaluator() {

        // setup
        Expression expr[] = new Expression[expressions.size()];
        expressions.toArray(expr);
        ActiveMQMessage message = new ActiveMQMessage();

        // Now evaluate:
        long start = System.currentTimeMillis();
        for (int i = 0; i < SPEED_TEST_ITERATIONS; i++) {

            for (int j = 0; j < expr.length; j++) {
                try {
                    expr[j].evaluate(message);
                }
                catch (JMSException e) {
                }
            }
        }

        long end = System.currentTimeMillis();
        printSpeedReport("Not Using MultiExpressionEvaluator", start, end);
    }

    /**
     * @param string
     * @param start
     * @param end
     */
    private void printSpeedReport(String testName, long start, long end) {
        System.out.println("Duration: " + testName + ": " + ((float) (end - start) / 1000.0) + " seconds.");
        System.out.println("Evaluation Rate: " + testName + ": " + ((float) (SPEED_TEST_ITERATIONS * expressions.size()) / (float) (end - start)) + " evaluations/second.");
    }
}