/* * 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.*; import java.util.*; import com.sun.jdi.*; import com.sun.jdi.event.*; import nsk.share.Consts; import nsk.share.TestBug; /* * Class contains debugger classes based on JDIEventsDebugger intended for JDI events testing */ public class EventTestTemplates { // how many times rerun test in case when tested events aren't generated static final int MAX_TEST_RUN_NUMBER = 10; /* * Method contains common code used by EventFilterTests and StressTestTemplate */ static void runTestWithRerunPossibilty(JDIEventsDebugger debugger, Runnable testRunner) { for (int i = 0; i < MAX_TEST_RUN_NUMBER; i++) { testRunner.run(); /* * If events weren't generated at all but test accepts missing events * try to rerun test (rerun test only if it didn't fail yet) */ if (debugger.eventsNotGenerated() && debugger.getSuccess()) { if (i < MAX_TEST_RUN_NUMBER - 1) { debugger.log.display("\nWARNING: tested events weren't generated at all, trying to rerun test (rerun attempt " + (i + 1) + ")\n"); continue; } else { debugger.setSuccess(false); debugger.log.complain("Tested events weren't generated after " + MAX_TEST_RUN_NUMBER + " runs, test FAILED"); } } else break; } } /* * Debugger class for testing event filters, subclasses should parse command * line and call method JDIEventsDebugger.eventFilterTestTemplate with * required arguments. * * Class handles common for event filter tests parameters: * - debuggee class name (e.g. -debuggeeClassName nsk.share.jdi.MonitorEventsDebuggee) * - tested event type (e.g. -eventType MONITOR_CONTENTED_ENTER) */ public static abstract class EventFilterTest extends JDIEventsDebugger { protected String debuggeeClassName; protected EventType eventType; 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("-debuggeeClassName") && (i < args.length - 1)) { debuggeeClassName = args[i + 1]; i++; } else if (args[i].equals("-eventType") && (i < args.length - 1)) { try { eventType = EventType.valueOf(args[i + 1]); } catch (IllegalArgumentException e) { TestBug testBug = new TestBug("Invalid event type : " + args[i + 1], e); throw testBug; } i++; } else standardArgs.add(args[i]); } if (eventType == null) throw new TestBug("Test requires 'eventType' parameter"); if (debuggeeClassName == null) throw new TestBug("Test requires 'debuggeeClassName' parameter"); return standardArgs.toArray(new String[] {}); } abstract protected int getTestFiltersNumber(); // This class is required to call runTestWithRerunPossibilty() class FilterTestRunner implements Runnable { private int filterIndex; FilterTestRunner(int filterIndex) { this.filterIndex = filterIndex; } public void run() { eventFilterTestTemplate(eventType, filterIndex); } } public final void doTest() { prepareDebuggee(new EventType[] {eventType}); int filtersNumber = getTestFiltersNumber(); if (filtersNumber <= 0) throw new TestBug("Invalid filtersNumber: " + filtersNumber); for (int filterIndex = 0; filterIndex < getTestFiltersNumber(); filterIndex++) { runTestWithRerunPossibilty(this, new FilterTestRunner(filterIndex)); } eventHandler.stopEventHandler(); removeDefaultBreakpoint(); } // can't control events from system libraries, so save events only from nsk packages protected boolean shouldSaveEvent(Event event) { return isEventFromNSK(event); } protected String debuggeeClassName() { return debuggeeClassName; } } /* * Class for testing class exclusion filter, this filter is added by * request's method addClassExclusionFilter(String classPattern) Expected * command line parameter: * - class patterns which should be passed to adding filter method (e.g. -classPatterns java.*:*.Foo) */ public static class ClassExclusionFilterTest extends EventFilterTest { protected String classPatterns[]; public static void main(String argv[]) { System.exit(run(argv, System.out) + Consts.JCK_STATUS_BASE); } public static int run(String argv[], PrintStream out) { return new ClassExclusionFilterTest().runIt(argv, out); } 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("-classPatterns") && (i < args.length - 1)) { classPatterns = args[i + 1].split(":"); i++; } else standardArgs.add(args[i]); } if (classPatterns == null || classPatterns.length == 0) throw new TestBug("Test requires at least one class name pattern"); return standardArgs.toArray(new String[] {}); } protected int getTestFiltersNumber() { return classPatterns.length; } protected EventFilters.DebugEventFilter[] createTestFilters(int testedFilterIndex) { if (testedFilterIndex < 0 || testedFilterIndex >= classPatterns.length) throw new TestBug("Invalid testedFilterIndex: " + testedFilterIndex); return new EventFilters.DebugEventFilter[]{new EventFilters.ClassExclusionFilter(classPatterns[testedFilterIndex])}; } } /* * Subclass of ClassExclusionFilterTest, create ClassFilter instead of * ClassExclusionFilter, this filter is added by request's method * addClassFilter(String classPattern) */ public static class ClassFilterTest_ClassName extends ClassExclusionFilterTest { public static void main(String argv[]) { System.exit(run(argv, System.out) + Consts.JCK_STATUS_BASE); } public static int run(String argv[], PrintStream out) { return new ClassFilterTest_ClassName().runIt(argv, out); } protected EventFilters.DebugEventFilter[] createTestFilters(int testedFilterIndex) { if (testedFilterIndex < 0 || testedFilterIndex >= classPatterns.length) throw new TestBug("Invalid testedFilterIndex: " + testedFilterIndex); return new EventFilters.DebugEventFilter[]{new EventFilters.ClassFilter(classPatterns[testedFilterIndex])}; } } /* * Subclass of ClassExclusionFilterTest, create ClassReferenceFilter instead * of ClassExclusionFilter, this filter is added by request's method * addClassFilter(ReferenceType referenceType) */ public static class ClassFilterTest_ReferenceType extends ClassExclusionFilterTest { public static void main(String argv[]) { System.exit(run(argv, System.out) + Consts.JCK_STATUS_BASE); } public static int run(String argv[], PrintStream out) { return new ClassFilterTest_ReferenceType().runIt(argv, out); } protected int getTestFiltersNumber() { return classPatterns.length; } protected EventFilters.DebugEventFilter[] createTestFilters(int testedFilterIndex) { if (testedFilterIndex < 0 || testedFilterIndex >= classPatterns.length) throw new TestBug("Invalid testedFilterIndex: " + testedFilterIndex); ReferenceType referenceType = debuggee.classByName(classPatterns[testedFilterIndex]); if (referenceType == null) throw new TestBug("Invalid class name is passed: " + classPatterns[testedFilterIndex]); return new EventFilters.DebugEventFilter[]{new EventFilters.ClassReferenceFilter(referenceType)}; } } /* * Class for testing instance filter, this filter is added by request's * method addInstanceFilter(ObjectReference instance). Class tests 3 following * cases: * - add filter for single object * - add filter for the same object 2 times, expect behavior such as in previous case * - add filter for 2 different objects, so events shouldn't be received */ public static class InstanceFilterTest extends EventFilterTest { public static void main(String argv[]) { System.exit(run(argv, System.out) + Consts.JCK_STATUS_BASE); } public static int run(String argv[], PrintStream out) { return new InstanceFilterTest().runIt(argv, out); } protected int getTestFiltersNumber() { return 3; } protected EventFilters.DebugEventFilter[] createTestFilters(int testedFilterIndex) { List objects = getEventObjects(); if (objects.size() < 2) { throw new TestBug("Debuggee didn't create event generating objects"); } switch (testedFilterIndex) { case 0: // filter for 1 object return new EventFilters.DebugEventFilter[]{new EventFilters.ObjectReferenceFilter(objects.get(0))}; case 1: // add 2 filters for the same object return new EventFilters.DebugEventFilter[]{new EventFilters.ObjectReferenceFilter(objects.get(0)), new EventFilters.ObjectReferenceFilter(objects.get(0))}; case 2: // add 2 filters for 2 different objects, so don't expect any event return new EventFilters.DebugEventFilter[]{new EventFilters.ObjectReferenceFilter(objects.get(0)), new EventFilters.ObjectReferenceFilter(objects.get(1))}; default: throw new TestBug("Invalid testedFilterIndex: " + testedFilterIndex); } } } /* * Class for testing thread filter, this filter is added by request's method * addThreadFilter(ThreadReference thread). Class test 3 follows cases: * - add filter for single thread * - add filter for the same thread 2 times, expect behavior such as in previous case * - add filter for 2 different threads, so events shouldn't be received */ public static class ThreadFilterTest extends EventFilterTest { public static void main(String argv[]) { System.exit(run(argv, System.out) + Consts.JCK_STATUS_BASE); } public static int run(String argv[], PrintStream out) { return new ThreadFilterTest().runIt(argv, out); } protected int getTestFiltersNumber() { return 3; } protected EventFilters.DebugEventFilter[] createTestFilters(int testedFilterIndex) { List threads = getEventThreads(); if (threads.size() < 2) { throw new TestBug("Debuggee didn't create event generating threads"); } switch (testedFilterIndex) { case 0: // filter for 1 thread return new EventFilters.DebugEventFilter[]{new EventFilters.ThreadFilter(threads.get(0))}; case 1: // add 2 filters for the same thread return new EventFilters.DebugEventFilter[]{new EventFilters.ThreadFilter(threads.get(0)), new EventFilters.ThreadFilter(threads.get(0))}; case 2: // add 2 filters for 2 different threads, so don't expect any event return new EventFilters.DebugEventFilter[]{new EventFilters.ThreadFilter(threads.get(0)), new EventFilters.ThreadFilter(threads.get(1))}; default: throw new TestBug("Invalid testedFilterIndex: " + testedFilterIndex); } } } /* * Class is intended for stress testing, expected command line parameters: * - debuggee class name (e.g.: -debuggeeClassName nsk.share.jdi.MonitorEventsDebuggee) * - one or more tested event types (e.g.: -eventTypes MONITOR_CONTENTED_ENTER:MONITOR_CONTENTED_ENTERED) * - number of events which should be generated during test execution (e.g.: -eventsNumber 500) * - number of threads which simultaneously generate events (e.g.: -threadsNumber 10) * * Class parses command line and calls method JDIEventsDebugger.stressTestTemplate. */ public static class StressTestTemplate extends JDIEventsDebugger { protected String debuggeeClassName; protected EventType testedEventTypes[]; protected int eventsNumber = 1; protected int threadsNumber = 1; public static void main(String argv[]) { System.exit(run(argv, System.out) + Consts.JCK_STATUS_BASE); } public static int run(String argv[], PrintStream out) { return new StressTestTemplate().runIt(argv, out); } 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("-eventsNumber") && (i < args.length - 1)) { eventsNumber = Integer.parseInt(args[i + 1]); // for this stress test events number is equivalent of iterations // (don't support 0 value of IterationsFactor) if (stressOptions.getIterationsFactor() != 0) eventsNumber *= stressOptions.getIterationsFactor(); i++; } else if (args[i].equals("-threadsNumber") && (i < args.length - 1)) { threadsNumber = Integer.parseInt(args[i + 1]); // if 'threadsNumber' is specified test should take in account stress options threadsNumber *= stressOptions.getThreadsFactor(); i++; } else if (args[i].equals("-debuggeeClassName") && (i < args.length - 1)) { debuggeeClassName = args[i + 1]; i++; } else if (args[i].equals("-eventTypes") && (i < args.length - 1)) { String eventTypesNames[] = args[i + 1].split(":"); testedEventTypes = new EventType[eventTypesNames.length]; try { for (int j = 0; j < eventTypesNames.length; j++) testedEventTypes[j] = EventType.valueOf(eventTypesNames[j]); } catch (IllegalArgumentException e) { throw new TestBug("Invalid event type", e); } i++; } else standardArgs.add(args[i]); } if (testedEventTypes == null || testedEventTypes.length == 0) throw new TestBug("Test requires 'eventTypes' parameter"); if (debuggeeClassName == null) throw new TestBug("Test requires 'debuggeeClassName' parameter"); return standardArgs.toArray(new String[] {}); } // can't control events from system libraries, so save events only from nsk packages protected boolean shouldSaveEvent(Event event) { return isEventFromNSK(event); } protected String debuggeeClassName() { return debuggeeClassName; } public void doTest() { prepareDebuggee(testedEventTypes); runTestWithRerunPossibilty(this, new Runnable() { public void run() { stressTestTemplate(testedEventTypes, eventsNumber, threadsNumber); } }); eventHandler.stopEventHandler(); removeDefaultBreakpoint(); } } static public boolean isEventFromNSK(Event event) { if (event instanceof MonitorContendedEnterEvent) { return ((MonitorContendedEnterEvent) event).location() != null && ((MonitorContendedEnterEvent) event).monitor().type().name().startsWith("nsk."); } if (event instanceof MonitorContendedEnteredEvent) { return ((MonitorContendedEnteredEvent) event).location() != null && ((MonitorContendedEnteredEvent) event).monitor().type().name().startsWith("nsk."); } if (event instanceof MonitorWaitEvent) { return ((MonitorWaitEvent) event).monitor().type().name().startsWith("nsk."); } if (event instanceof MonitorWaitedEvent) { return ((MonitorWaitedEvent) event).monitor().type().name().startsWith("nsk."); } // don't filter other events return true; } }