com.espertech.esper.regression.support.PatternTestHarness.java Source code

Java tutorial

Introduction

Here is the source code for com.espertech.esper.regression.support.PatternTestHarness.java

Source

/*
 * *************************************************************************************
 *  Copyright (C) 2008 EsperTech, Inc. All rights reserved.                            *
 *  http://esper.codehaus.org                                                          *
 *  http://www.espertech.com                                                           *
 *  ---------------------------------------------------------------------------------- *
 *  The software in this package is published under the terms of the GPL license       *
 *  a copy of which has been included with this distribution in the license.txt file.  *
 * *************************************************************************************
 */

package com.espertech.esper.regression.support;

import com.espertech.esper.client.*;
import com.espertech.esper.client.scopetest.SupportUpdateListener;
import com.espertech.esper.client.soda.EPStatementObjectModel;
import com.espertech.esper.client.time.CurrentTimeEvent;
import com.espertech.esper.client.time.TimerEvent;
import com.espertech.esper.event.EventBeanUtility;
import com.espertech.esper.support.bean.*;
import com.espertech.esper.support.client.SupportConfigFactory;
import junit.framework.Assert;
import junit.framework.TestCase;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map;

/**
 * Test harness for testing expressions and comparing received MatchedEventMap instances against against expected results.
 */
public class PatternTestHarness implements SupportBeanConstants {
    private final EventCollection sendEventCollection;
    private final CaseList caseList;

    // Array of expressions and match listeners for listening to events for each test descriptor
    private EPStatement expressions[];
    private SupportUpdateListener listeners[];

    public PatternTestHarness(EventCollection sendEventCollection, CaseList caseList) {
        this.sendEventCollection = sendEventCollection;
        this.caseList = caseList;

        // Create a listener for each test descriptor
        this.listeners = new SupportUpdateListener[caseList.getNumTests()];
        for (int i = 0; i < listeners.length; i++) {
            listeners[i] = new SupportUpdateListener();
        }
        expressions = new EPStatement[listeners.length];
    }

    public void runTest() throws Exception {
        runTest(PatternTestStyle.USE_PATTERN_LANGUAGE);
        runTest(PatternTestStyle.USE_EPL);
        runTest(PatternTestStyle.COMPILE_TO_MODEL);
        runTest(PatternTestStyle.COMPILE_TO_EPL);
    }

    private void runTest(PatternTestStyle testStyle) throws Exception {
        Configuration config = SupportConfigFactory.getConfiguration();
        config.addEventType("A", SupportBean_A.class);
        config.addEventType("B", SupportBean_B.class);
        config.addEventType("C", SupportBean_C.class);
        config.addEventType("D", SupportBean_D.class);
        config.addEventType("E", SupportBean_E.class);
        config.addEventType("F", SupportBean_F.class);
        config.addEventType("G", SupportBean_G.class);
        EPServiceProvider serviceProvider = EPServiceProviderManager.getDefaultProvider(config);
        serviceProvider.initialize();

        EPRuntime runtime = serviceProvider.getEPRuntime();

        // Send the start time to the runtime
        if (sendEventCollection.getTime(EventCollection.ON_START_EVENT_ID) != null) {
            TimerEvent startTime = new CurrentTimeEvent(
                    sendEventCollection.getTime(EventCollection.ON_START_EVENT_ID));
            runtime.sendEvent(startTime);
            log.debug(".runTest Start time is " + startTime);
        }

        // Set up expression filters and match listeners

        int index = 0;
        for (EventExpressionCase descriptor : caseList.getResults()) {
            String expressionText = descriptor.getExpressionText();
            EPStatementObjectModel model = descriptor.getObjectModel();

            EPStatement statement = null;

            try {
                if (model != null) {
                    statement = serviceProvider.getEPAdministrator().create(model, "name--" + expressionText);
                } else {
                    if (testStyle == PatternTestStyle.USE_PATTERN_LANGUAGE) {
                        statement = serviceProvider.getEPAdministrator().createPattern(expressionText,
                                "name--" + expressionText);
                    } else if (testStyle == PatternTestStyle.USE_EPL) {
                        String text = "@Audit('pattern') @Audit('pattern-instances') select * from pattern ["
                                + expressionText + "]";
                        statement = serviceProvider.getEPAdministrator().createEPL(text);
                        expressionText = text;
                    } else if (testStyle == PatternTestStyle.COMPILE_TO_MODEL) {
                        String text = "select * from pattern [" + expressionText + "]";
                        EPStatementObjectModel mymodel = serviceProvider.getEPAdministrator().compileEPL(text);
                        statement = serviceProvider.getEPAdministrator().create(mymodel);
                        expressionText = text;
                    } else if (testStyle == PatternTestStyle.COMPILE_TO_EPL) {
                        String text = "select * from pattern [" + expressionText + "]";
                        EPStatementObjectModel mymodel = serviceProvider.getEPAdministrator().compileEPL(text);
                        String reverse = mymodel.toEPL();
                        statement = serviceProvider.getEPAdministrator().createEPL(reverse);
                        expressionText = reverse;
                    } else {
                        throw new IllegalArgumentException("Unknown test style");
                    }
                }
            } catch (Exception ex) {
                String text = expressionText;
                if (model != null) {
                    text = "Model: " + model.toEPL();
                }
                log.fatal(".runTest Failed to create statement for style " + testStyle + " pattern expression="
                        + text, ex);
                TestCase.fail();
            }

            // We stop the statement again and start after the first listener was added.
            // Thus we can handle patterns that fireStatementStopped on startup.
            statement.stop();

            expressions[index] = statement;
            expressions[index].addListener(listeners[index]);

            // Start the statement again: listeners now got called for on-start events such as for a "not"
            statement.start();

            index++;
        }

        // Some expressions may fireStatementStopped as soon as they are started, such as a "not b()" expression, for example.
        // Check results for any such listeners/expressions.
        // NOTE: For EPL statements we do not support calling listeners when a pattern that fires upon start.
        // Reason is that this should not be a relevant functionality of a pattern, the start pattern
        // event itself cannot carry any information and is thus ignore. Note subsequent events
        // generated by the same pattern are fine.
        int totalEventsReceived = 0;
        if (testStyle != PatternTestStyle.USE_PATTERN_LANGUAGE) {
            clearListenerEvents();
            totalEventsReceived += countExpectedEvents(EventCollection.ON_START_EVENT_ID);
        } else // Patterns do need to handle event publishing upon pattern expression start (patterns that turn true right away)
        {
            checkResults(testStyle, EventCollection.ON_START_EVENT_ID);
            totalEventsReceived += countListenerEvents();
            clearListenerEvents();
        }

        // Send actual test events
        for (Map.Entry<String, Object> entry : sendEventCollection.entrySet()) {
            String eventId = entry.getKey();

            // Manipulate the time when this event was send
            if (sendEventCollection.getTime(eventId) != null) {
                TimerEvent currentTimeEvent = new CurrentTimeEvent(sendEventCollection.getTime(eventId));
                runtime.sendEvent(currentTimeEvent);
                log.debug(".runTest Sending event " + entry.getKey() + " = " + entry.getValue() + "  timed "
                        + currentTimeEvent);
            }

            // Send event itself
            runtime.sendEvent(entry.getValue());

            // Check expected results for this event
            checkResults(testStyle, eventId);

            // Count and clear the list of events that each listener has received
            totalEventsReceived += countListenerEvents();
            clearListenerEvents();
        }

        // Count number of expected matches
        int totalExpected = 0;
        for (EventExpressionCase descriptor : caseList.getResults()) {
            for (LinkedList<EventDescriptor> events : descriptor.getExpectedResults().values()) {
                totalExpected += events.size();
            }
        }

        if (totalExpected != totalEventsReceived) {
            log.debug(".test Count expected does not match count received, expected=" + totalExpected + " received="
                    + totalEventsReceived);
            TestCase.assertTrue(false);
        }

        // Kill all expressions
        for (EPStatement expression : expressions) {
            expression.removeAllListeners();
        }

        // Send test events again to also test that all were indeed killed
        for (Map.Entry<String, Object> entry : sendEventCollection.entrySet()) {
            runtime.sendEvent(entry.getValue());
        }

        // Make sure all listeners are still at zero
        for (SupportUpdateListener listener : listeners) {
            if (listener.getNewDataList().size() > 0) {
                log.debug(".test A match was received after stopping all expressions");
                TestCase.assertTrue(false);
            }
        }
    }

    private void checkResults(PatternTestStyle testStyle, String eventId) {
        // For each test descriptor, make sure the listener has received exactly the events expected
        int index = 0;
        log.debug(".checkResults Checking results for event " + eventId);

        for (EventExpressionCase descriptor : caseList.getResults()) {
            String expressionText = expressions[index].getText();

            LinkedHashMap<String, LinkedList<EventDescriptor>> allExpectedResults = descriptor.getExpectedResults();
            EventBean[] receivedResults = listeners[index].getLastNewData();
            index++;

            // If nothing at all was expected for this event, make sure nothing was received
            if (!(allExpectedResults.containsKey(eventId))) {
                if ((receivedResults != null) && (receivedResults.length > 0)) {
                    log.debug(".checkResults Incorrect result for style " + testStyle + " expression : "
                            + expressionText);
                    log.debug(".checkResults Expected no results for event " + eventId + ", but received "
                            + receivedResults.length + " events");
                    log.debug(".checkResults Received, have " + receivedResults.length + " entries");
                    printList(receivedResults);
                    TestCase.assertFalse(true);
                }
                continue;
            }

            LinkedList<EventDescriptor> expectedResults = allExpectedResults.get(eventId);

            // Compare the result lists, not caring about the order of the elements
            try {
                if (!(compareLists(receivedResults, expectedResults))) {
                    log.debug(".checkResults Incorrect result for style " + testStyle + " expression : "
                            + expressionText);
                    log.debug(".checkResults Expected size=" + expectedResults.size() + " received size="
                            + (receivedResults == null ? 0 : receivedResults.length));

                    log.debug(".checkResults Expected, have " + expectedResults.size() + " entries");
                    printList(expectedResults);
                    log.debug(".checkResults Received, have "
                            + (receivedResults == null ? 0 : receivedResults.length) + " entries");
                    printList(receivedResults);

                    TestCase.assertFalse(true);
                }
            } catch (Exception ex) {
                ex.printStackTrace();
                Assert.fail("For statement '" + expressionText + "' failed to assert: " + ex.getMessage());
            }
        }
    }

    private boolean compareLists(EventBean[] receivedResults, LinkedList<EventDescriptor> expectedResults) {
        int receivedSize = (receivedResults == null) ? 0 : receivedResults.length;
        if (expectedResults.size() != receivedSize) {
            return false;
        }

        // To make sure all received events have been expected
        LinkedList<EventDescriptor> expectedResultsClone = new LinkedList<EventDescriptor>(expectedResults);

        // Go through the list of expected results and remove from received result list if found
        for (EventDescriptor desc : expectedResults) {
            EventDescriptor foundMatch = null;

            for (EventBean received : receivedResults) {
                if (compareEvents(desc, received)) {
                    foundMatch = desc;
                    break;
                }
            }

            // No match between expected and received
            if (foundMatch == null) {
                return false;
            }

            expectedResultsClone.remove(foundMatch);
        }

        // Any left over received results also invalidate the test
        if (expectedResultsClone.size() > 0) {
            return false;
        }
        return true;
    }

    private static boolean compareEvents(EventDescriptor eventDesc, EventBean eventBean) {
        for (Map.Entry<String, Object> entry : eventDesc.getEventProperties().entrySet()) {
            Object left = eventBean.get(entry.getKey());
            Object right = entry.getValue();
            if (left != right) {
                return false;
            }
        }
        return true;
    }

    /**
     * Clear the event list of all listeners
     */
    private void clearListenerEvents() {
        for (SupportUpdateListener listener : listeners) {
            listener.reset();
        }
    }

    /**
     * Clear the event list of all listeners
     */
    private int countListenerEvents() {
        int count = 0;
        for (SupportUpdateListener listener : listeners) {
            for (EventBean[] events : listener.getNewDataList()) {
                count += events.length;
            }
        }
        return count;
    }

    private void printList(LinkedList<EventDescriptor> events) {
        int index = 0;
        for (EventDescriptor desc : events) {
            StringBuilder buffer = new StringBuilder();
            int count = 0;

            for (Map.Entry<String, Object> entry : desc.getEventProperties().entrySet()) {
                buffer.append(" (" + (count++) + ") ");
                buffer.append("tag=" + entry.getKey());

                String id = findValue(entry.getValue());
                buffer.append("  eventId=" + id);
            }

            log.debug(".printList (" + index + ") : " + buffer.toString());
            index++;
        }
    }

    private void printList(EventBean[] events) {
        if (events == null) {
            log.debug(".printList : null-value events array");
            return;
        }

        log.debug(".printList : " + events.length + " elements...");
        for (int i = 0; i < events.length; i++) {
            log.debug("  " + EventBeanUtility.printEvent(events[i]));
        }
    }

    /**
     * Find the value object in the map of object names and values
     */
    private String findValue(Object value) {
        for (Map.Entry<String, Object> entry : sendEventCollection.entrySet()) {
            if (value == entry.getValue()) {
                return entry.getKey();
            }
        }
        return null;
    }

    private int countExpectedEvents(String eventId) {
        int result = 0;
        for (EventExpressionCase descriptor : caseList.getResults()) {
            LinkedHashMap<String, LinkedList<EventDescriptor>> allExpectedResults = descriptor.getExpectedResults();

            // If nothing at all was expected for this event, make sure nothing was received
            if (allExpectedResults.containsKey(eventId)) {
                result++;
            }
        }
        return result;
    }

    private enum PatternTestStyle {
        USE_PATTERN_LANGUAGE, USE_EPL, COMPILE_TO_MODEL, COMPILE_TO_EPL;
    }

    private static final Log log = LogFactory.getLog(PatternTestHarness.class);
}