/* * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package nsk.share.jdi; import java.io.PrintStream; import java.lang.reflect.*; import java.util.*; import com.sun.jdi.*; import com.sun.jdi.event.*; import com.sun.jdi.request.*; import nsk.share.TestBug; /* * Class is used as base debugger in tests for following events and event requests: * - MonitorContendedEnterRequest / MonitorContendedEnterEvent * - MonitorContendedEnteredRequest / MonitorContendedEnteredEvent * - MonitorWaitRequest / MonitorWaitEvent * - MonitorWaitedRequest / MonitorWaitedEvent * * In all these tests similar scenario is used: * - debugger VM forces debuggee VM to create number of objects which should generate events during test * - if any event filters are used each generating event object is checked is this object accepted by all filters, * if object was accepted it should save information about all generated events and this information is available for debugger * - debuggee performs event generation and stop at breakpoint * - debugger reads data saved by event generators and checks is only expected events was generated */ public class JDIEventsDebugger extends TestDebuggerType2 { // types of tested events static public enum EventType { MONITOR_CONTENTED_ENTER, MONITOR_CONTENTED_ENTERED, MONITOR_WAIT, MONITOR_WAITED } /* * Class contains information required for event testing */ static class TestedEventData { // event class public Class eventClass; // class representing event data on debuggee's side public Class eventDataMirrorClass; // class representing event data on debugger's side public Class eventDataClass; public TestedEventData(Class eventClass, Class eventDataMirrorClass, Class eventDataClass) { this.eventClass = eventClass; this.eventDataMirrorClass = eventDataMirrorClass; this.eventDataClass = eventDataClass; } } static public Map testedEventData = new HashMap(); static { testedEventData.put(EventType.MONITOR_CONTENTED_ENTER, new TestedEventData(MonitorContendedEnterEvent.class, DebuggeeEventData.DebugMonitorEnterEventData.class, DebuggerEventData.DebugMonitorEnterEventData.class)); testedEventData.put(EventType.MONITOR_CONTENTED_ENTERED, new TestedEventData(MonitorContendedEnteredEvent.class, DebuggeeEventData.DebugMonitorEnteredEventData.class, DebuggerEventData.DebugMonitorEnteredEventData.class)); testedEventData.put(EventType.MONITOR_WAIT, new TestedEventData(MonitorWaitEvent.class, DebuggeeEventData.DebugMonitorWaitEventData.class, DebuggerEventData.DebugMonitorWaitEventData.class)); testedEventData.put(EventType.MONITOR_WAITED, new TestedEventData(MonitorWaitedEvent.class, DebuggeeEventData.DebugMonitorWaitedEventData.class, DebuggerEventData.DebugMonitorWaitedEventData.class)); } static public TestedEventData[] eventDataByEventTypes(EventType[] eventTypes) { TestedEventData[] result = new TestedEventData[eventTypes.length]; int i = 0; for (EventType eventType : eventTypes) { TestedEventData eventData = testedEventData.get(eventType); if (eventData == null) throw new TestBug("Unsupported event type: " + eventType); result[i++] = eventData; } return result; } /* * Dummy event listener, just accepts all events */ public class DummyEventListener extends EventHandler.EventListener { private volatile boolean breakpointEventReceived; public boolean eventReceived(Event event) { if (event instanceof BreakpointEvent) { breakpointEventReceived = true; vm.resume(); } return true; } public void waitBreakpoint() { while (!breakpointEventReceived) Thread.yield(); } } /* * Parse common for event tests parameters */ protected String[] doInit(String[] args, PrintStream out) { args = super.doInit(args, out); ArrayList standardArgs = new ArrayList(); for (int i = 0; i < args.length; i++) { if (args[i].equals("-allowExtraEvents")) { extraEventClasses = createEventClassArray(args[i + 1]); i++; } else if (args[i].equals("-allowMissedEvents")) { missedEventClasses = createEventClassArray(args[i + 1]); i++; } else standardArgs.add(args[i]); } return standardArgs.toArray(new String[standardArgs.size()]); } // can't control some kinds of events (events from system libraries) and // not all events should be saved for analysis // (should be implemented in subclasses) protected boolean shouldSaveEvent(Event event) { return true; } public Class findEventDataClass(TestedEventData[] testedEventData, Event event) { for (TestedEventData eventData : testedEventData) { if (eventData.eventClass.isAssignableFrom(event.getClass())) return eventData.eventClass; } return null; } /* * This event listener stores received monitor events until BreakpointEvent * is not received, after getting of BreakpointEvent checks only expected * events were received */ public class EventListener extends EventHandler.EventListener { private TestedEventData[] testedEventData; public EventListener(TestedEventData[] testedEventData) { this.testedEventData = testedEventData; } private boolean shouldHandleEvent(Event event) { return findEventDataClass(testedEventData, event) == null ? false : true; } volatile boolean breakpointWasReceived; // execution was interrupted because of timeout volatile boolean executionWasInterrupted; public boolean eventReceived(Event event) { if (shouldHandleEvent(event)) { if (shouldSaveEvent(event)) { Class eventClass; eventClass = findEventDataClass(testedEventData, event); List events = allReceivedEvents.get(eventClass); if (events == null) { events = new LinkedList(); allReceivedEvents.put(eventClass, events); } events.add(event); } return true; } // debuggee should stop at the end of test else if (event instanceof BreakpointEvent) { breakpointWasReceived = true; try { // if execution was interrupted because of timeout don't check received // events because it can consume too much time if (!executionWasInterrupted) { // get data from debuggee about all generated events initExpectedEvents(testedEventData); checkEvents(); } else log.complain("WARNING: execution was interrupted because of timeout, test doesn't check received events"); } catch (Throwable t) { unexpectedException(t); } vm.resume(); return true; } return false; } } protected Class extraEventClasses[]; protected Class missedEventClasses[]; /* * If test can't strictly control event generation it may allow generation * of extra events and unexpected events aren't treated as error * (subclasses should specify what kinds of extra events are allowed) */ private Class[] allowedExtraEvents() { return extraEventClasses; } /* * If test can't strictly control event generation case when debugger doesn't * receive expected event may be not treated as failure * (subclasses should specify what kinds of expected events can be not received) */ private Class[] allowedMissedEvents() { return missedEventClasses; } private boolean isExtraEventAllowed(Class eventClass) { return checkEvent(eventClass, allowedExtraEvents()); } private boolean isMissedEventAllowed(Class eventClass) { return checkEvent(eventClass, allowedMissedEvents()); } private boolean checkEvent(Class eventClass, Class classes[]) { if (classes == null) return false; for (Class klass : classes) { if (klass.isAssignableFrom(eventClass)) return true; } return false; } // flag is modified from the event listener thread private volatile boolean eventsNotGenerated; /* * Method returns true if test expects event generation, but events weren't * generated. If test can't strictly control event generation such case isn't * necessarily treated as test failure (sublasses of JDIEventsDebugger can * for example try to rerun test several times). */ protected boolean eventsNotGenerated() { return eventsNotGenerated; } /* * Print debug information about expected and received events(this data * should be stored in lists 'allExpectedEvents' and 'allReceivedEvents') * and check that only expected events were received */ private void checkEvents() { if (getAllExpectedEvents().size() > 0 && getAllReceivedEvents().size() == 0 && allowedMissedEvents() != null) { log.display("WARNING: didn't receive any event"); eventsNotGenerated = true; } log.display("ALL RECEIVED EVENTS: "); for (Event event : getAllReceivedEvents()) log.display("received event: " + eventToString(event)); log.display("ALL EXPECTED EVENTS: "); for (DebuggerEventData.DebugEventData eventData : getAllExpectedEvents()) log.display("expected event: " + eventData); // try to find received event in the list of expected events, if this event // was found remove data about events from both lists for (Class eventClass : allReceivedEvents.keySet()) { List receivedEvents = allReceivedEvents.get(eventClass); List expectedEvents = allExpectedEvents.get(eventClass); for (Iterator allReceivedEventsIterator = receivedEvents.iterator(); allReceivedEventsIterator.hasNext();) { Event event = allReceivedEventsIterator.next(); for (Iterator allExpectedEventsIterator = expectedEvents.iterator(); allExpectedEventsIterator.hasNext();) { DebuggerEventData.DebugEventData debugEventData = allExpectedEventsIterator.next(); if (debugEventData.shouldCheckEvent(event)) { if (debugEventData.checkEvent(event)) { allExpectedEventsIterator.remove(); allReceivedEventsIterator.remove(); break; } } } } } List receivedEventsLeft = getAllReceivedEvents(); // check is all received events were found in expected if (receivedEventsLeft.size() > 0) { // if allowExtraEvents = true extra events are not treated as error for (Event event : receivedEventsLeft) { if (!isExtraEventAllowed(event.getClass())) { setSuccess(false); log.complain("Unexpected event " + eventToString(event)); } } } List expectedEventsLeft = getAllExpectedEvents(); // check is all expected events were received if (expectedEventsLeft.size() > 0) { for (DebuggerEventData.DebugEventData eventData : expectedEventsLeft) { if (!isMissedEventAllowed(eventData.eventClass)) { setSuccess(false); log.complain("Expected event was not generated: " + eventData); } } } } private String eventToString(Event event) { try { if (event instanceof MonitorContendedEnterEvent) return event + ". Details(MonitorContendedEnterEvent):" + " Monitor: " + ((MonitorContendedEnterEvent) event).monitor() + " Thread: " + ((MonitorContendedEnterEvent) event).thread(); else if (event instanceof MonitorContendedEnteredEvent) return event + ". Details(MonitorContendedEnteredEvent):" + " Monitor: " + ((MonitorContendedEnteredEvent) event).monitor() + " Thread: " + ((MonitorContendedEnteredEvent) event).thread(); else if (event instanceof MonitorWaitEvent) return event + ". Details(MonitorWaitEvent):" + " Monitor: " + ((MonitorWaitEvent) event).monitor() + " Thread: " + ((MonitorWaitEvent) event).thread() + " Timeout: " + ((MonitorWaitEvent) event).timeout(); else if (event instanceof MonitorWaitedEvent) return event + ". Details(MonitorWaitedEvent):" + " Monitor: " + ((MonitorWaitedEvent) event).monitor() + " Thread: " + ((MonitorWaitedEvent) event).thread() + " Timedout: " + ((MonitorWaitedEvent) event).timedout(); return event.toString(); } // this exception can occur when unexpected event was received catch (ObjectCollectedException e) { // allowExtraEvents=true extra events are not treated as error if (!isExtraEventAllowed(event.getClass())) { setSuccess(false); e.printStackTrace(log.getOutStream()); log.complain("Unexpected ObjectCollectedException was caught, possible unexpected event was received"); } return event.getClass().getName() + " [ Can't get full description, ObjectCollectedException was thrown ]"; } } // events received during test execution are stored here private Map, List> allReceivedEvents = new HashMap, List>(); private List getAllReceivedEvents() { List result = new LinkedList(); for (Class eventClass : allReceivedEvents.keySet()) { result.addAll(allReceivedEvents.get(eventClass)); } return result; } protected Map, List> allExpectedEvents = new HashMap, List>(); private List getAllExpectedEvents() { List result = new LinkedList(); for (Class eventClass : allExpectedEvents.keySet()) { result.addAll(allExpectedEvents.get(eventClass)); } return result; } // find in debuggee VM and add to the list 'allExpectedEvents' instances // of classes representing generated events protected void initExpectedEvents(TestedEventData testedEventData[]) { List events; ReferenceType referenceType = debuggee.classByName(debuggeeClassNameWithoutArgs()); ArrayReference generatedEvents = (ArrayReference) referenceType.getValue(referenceType.fieldByName("generatedEvents")); for (TestedEventData eventData : testedEventData) { events = new LinkedList(); allExpectedEvents.put(eventData.eventClass, events); } for (int i = 0; i < generatedEvents.length(); i++) { ObjectReference debuggeeMirror = (ObjectReference) generatedEvents.getValue(i); for (TestedEventData eventData : testedEventData) { if (debuggeeMirror.referenceType().name().equals(eventData.eventDataMirrorClass.getName())) { events = allExpectedEvents.get(eventData.eventClass); /* * Use reflection to create object representing generated * event Event data class should has constructor with single * parameter of type com.sun.jdi.ObjectReference */ Constructor constructor; try { constructor = eventData.eventDataClass.getConstructor(new Class[] { ObjectReference.class }); } catch (NoSuchMethodException e) { TestBug testBug = new TestBug( "Class representing debug event data should implement constructor with single parameter of type com.sun.jdi.ObjectReference"); testBug.initCause(e); throw testBug; } DebuggerEventData.DebugEventData expectedEvent; try { expectedEvent = (DebuggerEventData.DebugEventData) constructor.newInstance(new Object[] { debuggeeMirror }); } catch (Exception e) { TestBug testBug = new TestBug("Error when create debug event data: " + e); testBug.initCause(e); throw testBug; } events.add(expectedEvent); } } } } private void printFiltersInfo() { if (eventFilters.size() > 0) { log.display("Use following filters: "); for (EventFilters.DebugEventFilter filter : eventFilters) log.display("" + filter); } else { log.display("Don't use event filters"); } } // filters used in test protected List eventFilters = new LinkedList(); // Check is object generating events matches all filters, // if object was accepted by all filters set this object's field // 'saveEventData' to 'true', otherwise to 'false', private void checkEventGenerator(ThreadReference eventThread, ObjectReference executor) { boolean acceptedByFilters = true; for (EventFilters.DebugEventFilter eventFilter : eventFilters) { if (!eventFilter.isObjectMatch(executor, eventThread)) { acceptedByFilters = false; break; } } try { executor.setValue(executor.referenceType().fieldByName("saveEventData"), vm.mirrorOf(acceptedByFilters)); } catch (Exception e) { throw new TestBug("Unexpected exception when change object field in debugee VM: " + e, e); } } /* * Find all event generating threads in debuggee VM (instances of * nsk.share.jdi.MonitorEventsDebuggee$MonitorActionsThread) all these * threads have special field - 'executor', this is object which generates * events. If event generating thread and event generating object was * accepted by all filters generating object should store information about * all generated events and this information will be available for debugger */ protected void initializeEventGenerators() { printFiltersInfo(); List eventThreads = getEventThreads(); for (ThreadReference eventThread : eventThreads) { ObjectReference executor = (ObjectReference) eventThread.getValue(eventThread.referenceType().fieldByName("executor")); checkEventGenerator(eventThread, executor); } // debuggee's main thread also can generate events, need to filter it in // the same way as other threads checkEventGenerator(debuggee.threadByName(JDIEventsDebuggee.MAIN_THREAD_NAME), findSingleObjectReference(debuggeeClassNameWithoutArgs())); } // find instances of nsk.share.jdi.MonitorEventsDebuggee$MonitorActionsThread protected List getEventThreads() { ReferenceType referenceType = debuggee.classByName(JDIEventsDebuggee.EventActionsThread.class.getName()); List debuggeeEventThreads = referenceType.instances(0); List result = new LinkedList(); for (ObjectReference threadReference : debuggeeEventThreads) result.add((ThreadReference) threadReference); return result; } // find instances of nsk.share.jdi.MonitorEventsDebuggee$MonitorActionsThread, // and get value of this object's field with name 'executor' protected List getEventObjects() { List eventObjects = new LinkedList(); List eventThreads = getEventThreads(); for (ThreadReference eventThread : eventThreads) { eventObjects.add((ObjectReference) eventThread.getValue(eventThread.referenceType().fieldByName("executor"))); } return eventObjects; } // remove all filters, received and expected events private void clearTestData() { allExpectedEvents.clear(); allReceivedEvents.clear(); eventFilters.clear(); eventsNotGenerated = false; } private boolean isEventSupported(EventType eventType) { switch (eventType) { case MONITOR_CONTENTED_ENTER: case MONITOR_CONTENTED_ENTERED: case MONITOR_WAIT: case MONITOR_WAITED: return vm.canRequestMonitorEvents(); default: throw new TestBug("Invalid tested event type: " + eventType); } } // create instance of EventRequest depending on given eventType private EventRequest createTestRequest(EventType eventType) { switch (eventType) { case MONITOR_CONTENTED_ENTER: return debuggee.getEventRequestManager().createMonitorContendedEnterRequest(); case MONITOR_CONTENTED_ENTERED: return debuggee.getEventRequestManager().createMonitorContendedEnteredRequest(); case MONITOR_WAIT: return debuggee.getEventRequestManager().createMonitorWaitRequest(); case MONITOR_WAITED: return debuggee.getEventRequestManager().createMonitorWaitedRequest(); default: throw new TestBug("Invalid tested event type: " + eventType); } } // create command depending on given eventType private String createCommand(EventType eventTypes[], int eventsNumber) { String command = JDIEventsDebuggee.COMMAND_CREATE_ACTIONS_EXECUTORS + ":" + eventsNumber + ":"; for (EventType eventType : eventTypes) { switch (eventType) { case MONITOR_CONTENTED_ENTER: case MONITOR_CONTENTED_ENTERED: case MONITOR_WAIT: case MONITOR_WAITED: command += " " + eventType.name(); break; default: throw new TestBug("Invalid tested event type: " + eventType); } } return command; } // get list of event requests from EventRequestManager depending on the given eventType private List getEventRequestsFromManager(EventType eventType) { switch (eventType) { case MONITOR_CONTENTED_ENTER: return debuggee.getEventRequestManager().monitorContendedEnterRequests(); case MONITOR_CONTENTED_ENTERED: return debuggee.getEventRequestManager().monitorContendedEnteredRequests(); case MONITOR_WAIT: return debuggee.getEventRequestManager().monitorWaitRequests(); case MONITOR_WAITED: return debuggee.getEventRequestManager().monitorWaitedRequests(); default: throw new TestBug("Invalid tested event type: " + eventType); } } protected EventHandler eventHandler; private EventListener eventListener; // perform event generation before test begins to load all using classes // and avoid unexpected events related to classloading protected void prepareDebuggee(EventType[] eventTypes) { initDefaultBreakpoint(); eventHandler = new EventHandler(debuggee, log); eventHandler.startListening(); // use event listener which just skip all received events DummyEventListener dummyEventListener = new DummyEventListener(); eventHandler.addListener(dummyEventListener); EventRequest eventRequests[] = new EventRequest[eventTypes.length]; for (int i = 0; i < eventRequests.length; i++) { eventRequests[i] = createTestRequest(eventTypes[i]); eventRequests[i].setSuspendPolicy(EventRequest.SUSPEND_NONE); eventRequests[i].enable(); } // debuggee should create event generators pipe.println(createCommand(eventTypes, 1)); if (!isDebuggeeReady()) return; // start event generation pipe.println(JDIEventsDebuggee.COMMAND_START_EXECUTION); if (!isDebuggeeReady()) return; for (int i = 0; i < eventRequests.length; i++) eventRequests[i].disable(); dummyEventListener.waitBreakpoint(); eventHandler.removeListener(dummyEventListener); pipe.println(JDIEventsDebuggee.COMMAND_WAIT_EXECUTION_COMPLETION); if (!isDebuggeeReady()) return; eventListener = new EventListener(eventDataByEventTypes(eventTypes)); eventHandler.addListener(eventListener); } /* * Method for stress testing, allows specify requests for several event * types, number of events which should be generated during test and number * of threads which simultaneously generate events */ protected void stressTestTemplate(EventType[] eventTypes, int eventsNumber, int threadsNumber) { for (EventType eventType : eventTypes) { if (!isEventSupported(eventType)) { log.complain("Can't test event because of it isn't supported: " + eventType); return; } } // Used framework is intended for testing event filters and debuggee // creates 3 threads performing event generation and there is possibility // to filter events from some threads for (int i = 0; i < threadsNumber; i++) { pipe.println(createCommand(eventTypes, eventsNumber)); if (!isDebuggeeReady()) return; } // clear data(if this method is executed several times) clearTestData(); initializeEventGenerators(); EventRequest eventRequests[] = new EventRequest[eventTypes.length]; // create event requests for (int i = 0; i < eventTypes.length; i++) { eventRequests[i] = createTestRequest(eventTypes[i]); eventRequests[i].setSuspendPolicy(EventRequest.SUSPEND_NONE); eventRequests[i].enable(); log.display("Use following event request: " + eventRequests[i]); } // stressTestTemplate can control only execution time, so ignore iteration count stresser.start(0); try { // start event generation pipe.println(JDIEventsDebuggee.COMMAND_START_EXECUTION); if (!isDebuggeeReady()) return; // check is stressTime exceeded while (stresser.continueExecution()) { try { Thread.sleep(100); } catch (InterruptedException e) { unexpectedException(e); } // periodically check is test completed if (eventListener.breakpointWasReceived) break; } } finally { stresser.finish(); } // debugger should interrupt test because of timeout if (!eventListener.breakpointWasReceived) { eventListener.executionWasInterrupted = true; log.complain("WARNING: time is exceeded, interrupt test"); pipe.println(JDIEventsDebuggee.COMMAND_STOP_EXECUTION); if (!isDebuggeeReady()) return; } pipe.println(JDIEventsDebuggee.COMMAND_WAIT_EXECUTION_COMPLETION); if (!isDebuggeeReady()) return; for (int i = 0; i < eventRequests.length; i++) eventRequests[i].disable(); } /* * Hook method for subclasses implementing tests against event filters (it is called from eventFilterTestTemplate) */ protected EventFilters.DebugEventFilter[] createTestFilters(int testedFilterIndex) { throw new TestBug("Not implemented"); } /* * Test event request with filter * * Also this method check following: * - InvalidRequestStateException is thrown if add filter for deleted or enabled request * - EventRequestManager.xxxRequests() returns created event request */ protected void eventFilterTestTemplate(EventType eventType, int testedFilterIndex) { if (!isEventSupported(eventType)) { log.complain("Can't test event because of it isn't supported: " + eventType); return; } // debuggee create event generators pipe.println(createCommand(new EventType[] { eventType }, 1)); if (!isDebuggeeReady()) return; clearTestData(); EventFilters.DebugEventFilter[] filters = createTestFilters(testedFilterIndex); for (EventFilters.DebugEventFilter filter : filters) { if (filter.isSupported(vm)) eventFilters.add(filter); else { log.complain("Can't test filter because of it isn't supported: " + filter); return; } } initializeEventGenerators(); // create event request EventRequest request = createTestRequest(eventType); request.setSuspendPolicy(EventRequest.SUSPEND_NONE); // try add filter to enabled request, expect // 'InvalidRequestStateException' request.enable(); try { for (EventFilters.DebugEventFilter filter : filters) filter.addFilter(request); setSuccess(false); log.complain("Expected 'InvalidRequestStateException' was not thrown"); } catch (InvalidRequestStateException e) { // expected exception } catch (Throwable e) { setSuccess(false); log.complain("Unexpected exception: " + e); e.printStackTrace(log.getOutStream()); } // add event filter request.disable(); for (EventFilters.DebugEventFilter filter : filters) addFilter(filter, request); request.enable(); log.display("Use following event request: " + request); // start event generation pipe.println(JDIEventsDebuggee.COMMAND_START_EXECUTION); if (!isDebuggeeReady()) return; // wait execution completion pipe.println(JDIEventsDebuggee.COMMAND_WAIT_EXECUTION_COMPLETION); if (!isDebuggeeReady()) return; // check that method EventRequestManager.xxxRequests() return created // request if (!getEventRequestsFromManager(eventType).contains(request)) { setSuccess(false); log.complain("EventRequestManager doesn't return request: " + request); } // delete event request debuggee.getEventRequestManager().deleteEventRequest(request); // try add filter to removed request, expect // 'InvalidRequestStateException' try { for (EventFilters.DebugEventFilter filter : filters) filter.addFilter(request); setSuccess(false); log.complain("Expected 'InvalidRequestStateException' was not thrown"); } catch (InvalidRequestStateException e) { // expected exception } catch (Throwable t) { unexpectedException(t); } } private void addFilter(EventFilters.DebugEventFilter filter, EventRequest request) { try { filter.addFilter(request); } catch (Throwable t) { unexpectedException(t); } } // used to parse parameters -allowExtraEvents and -allowMissedEvents private Class[] createEventClassArray(String string) { String eventTypesNames[] = string.split(":"); EventType eventTypes[] = new EventType[eventTypesNames.length]; try { for (int i = 0; i < eventTypesNames.length; i++) { eventTypes[i] = EventType.valueOf(eventTypesNames[i]); } } catch (IllegalArgumentException e) { throw new TestBug("Invalid event type", e); } if (eventTypesNames.length == 0) throw new TestBug("Event types weren't specified"); Class[] result = new Class[eventTypesNames.length]; for (int i = 0; i < result.length; i++) result[i] = testedEventData.get(eventTypes[i]).eventClass; return result; } }