/******************************************************************************* * Copyright (c) 2009, 2010 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.wst.jsdt.debug.rhino.tests; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import org.eclipse.wst.jsdt.debug.core.jsdi.VirtualMachine; import org.eclipse.wst.jsdt.debug.internal.rhino.transport.EventPacket; import org.eclipse.wst.jsdt.debug.internal.rhino.transport.JSONConstants; import org.eclipse.wst.jsdt.debug.internal.rhino.transport.JSONUtil; import org.eclipse.wst.jsdt.debug.internal.rhino.transport.RhinoRequest; import org.eclipse.wst.jsdt.debug.transport.DebugSession; import org.eclipse.wst.jsdt.debug.transport.exception.DisconnectedException; import org.eclipse.wst.jsdt.debug.transport.exception.TimeoutException; /** * Event handler for testing purposes * * @since 1.0 */ public class TestEventHandler implements Runnable { /** * Sub-handler for test to implement and add to the root * {@link TestEventHandler} */ public interface Subhandler { boolean handleEvent(DebugSession debugSession, EventPacket event); String testName(); } /** * Sub-handler for breakpoint specific operations * * @since 1.1 */ public interface BreakpointSubHandler extends Subhandler { /** * Array of line numbers to add breakpoints to * * @param lines */ void linesToAddBreakpoints(int[] lines); /** * Array of lines to remove breakpoints from * * @param lines */ void linesToRemoveBreakpoints(int[] lines); } private DebugSession debugSession; private Thread thread; private final List subhandlers = new ArrayList(); private final ArrayList expectedEvents = new ArrayList(); private final ArrayList actualEvents = new ArrayList(); private volatile boolean shutdown = false; private int eventCount = 0; private String testname = null; /** * Constructor * * @param debugSession * the current session to handle * @param testname * the name of the test using this handler */ public TestEventHandler(DebugSession debugSession, String testname) { this.debugSession = debugSession; this.thread = new Thread(this, "TestEventHandler"); //$NON-NLS-1$ this.testname = testname; } /** * start the test handler */ public void start() { thread.start(); } /** * stop the test handler */ public void stop() { shutdown = true; thread.interrupt(); try { thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (this) { this.expectedEvents.clear(); this.actualEvents.clear(); this.subhandlers.clear(); this.eventCount = 0; } } /** * Sets the events that the handler is expected to receive * * @param events */ public synchronized void setExpectedEvents(EventPacket[] events) { this.expectedEvents.clear(); for (int i = 0; i < events.length; i++) { this.expectedEvents.add(events[i]); } } /* * (non-Javadoc) * * @see java.lang.Runnable#run() */ public void run() { while (!shutdown) { try { EventPacket event = (EventPacket) debugSession.receive(JSONConstants.EVENT, VirtualMachine.DEFAULT_TIMEOUT); handleEvent(event); } catch (TimeoutException e) { // ignore } catch (DisconnectedException e) { e.printStackTrace(); } } } /** * Add a sub handler * * @param subhandler */ public synchronized void addSubhandler(Subhandler subhandler) { subhandlers.add(subhandler); } /** * Remove a sub handler * * @param subhandler */ public synchronized void removeSubhandler(Subhandler subhandler) { subhandlers.remove(subhandler); } /** * Handles the given event packet * * @param event */ private synchronized void handleEvent(EventPacket event) { String estring = null; if (RequestTest.isTracing()) { estring = JSONUtil.write(event.toJSON()); System.out.println(this.testname + " got event: " + estring); //$NON-NLS-1$ } for (ListIterator iterator = subhandlers.listIterator(); iterator.hasNext();) { Subhandler subhandler = (Subhandler) iterator.next(); try { if (subhandler.handleEvent(debugSession, event)) { if (RequestTest.isTracing()) { System.out.println("\t" + this.testname + " handled event: " + estring); //$NON-NLS-1$ //$NON-NLS-2$ } this.expectedEvents.remove(event); actualEvents.add(event); eventCount++; notifyAll(); } } catch (Throwable t) { t.printStackTrace(); } } if (!event.getEvent().equals("thread")) { //$NON-NLS-1$ sendContinue(event, null); } } /** * Continues the handler * * @param event * @param step */ protected void sendContinue(EventPacket event, String step) { Number threadId = (Number) event.getBody().get("threadId"); //$NON-NLS-1$ RhinoRequest request = new RhinoRequest("continue"); //$NON-NLS-1$ request.getArguments().put("threadId", threadId); //$NON-NLS-1$ request.getArguments().put("step", step); //$NON-NLS-1$ try { debugSession.send(request); debugSession.receiveResponse(request.getSequence(), VirtualMachine.DEFAULT_TIMEOUT); } catch (DisconnectedException e) { if (!shutdown) e.printStackTrace(); } catch (TimeoutException e) { if (!shutdown) e.printStackTrace(); } } /** * Waits for all of the events specified from * {@link #setExpectedEvents(EventPacket[])} * * @param count */ public synchronized void waitForEvents(int count) { long timeout = System.currentTimeMillis() + 5000; while (eventCount < count && System.currentTimeMillis() < timeout) { try { wait(500); } catch (InterruptedException e) { e.printStackTrace(); } } try { if (eventCount != count) { for (Iterator it = actualEvents.iterator(); it.hasNext();) { System.err.println(it.next().toString()); } throw new IllegalStateException("eventcount was: " + eventCount + " expected: " + count); //$NON-NLS-1$ //$NON-NLS-2$ } } finally { this.eventCount = 0; } } /** * Waits for the given collection of events to be handled, or a default * timeout to occur * * @param events */ public synchronized void waitForEvents(EventPacket[] events) { long timeout = System.currentTimeMillis() + 5000; while (!this.expectedEvents.isEmpty() && System.currentTimeMillis() < timeout) { try { wait(500); } catch (InterruptedException e) { e.printStackTrace(); } } if (!this.expectedEvents.isEmpty()) { try { throw new IllegalStateException("Some expected events were not received"); //$NON-NLS-1$ } finally { this.eventCount = 0; for (int i = 0; i < this.expectedEvents.size(); i++) { System.out.println(this.testname + " missed expected event: " + JSONUtil.write(this.expectedEvents.get(i))); //$NON-NLS-1$ } } } if (this.eventCount > events.length) { try { throw new IllegalStateException(this.testname + " got too many events - expected [" + events.length + "] got [" + eventCount + "]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } finally { this.eventCount = 0; } } } }