| /* |
| * 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 nsk.share.Consts; |
| import nsk.share.TestBug; |
| import nsk.share.jpda.AbstractDebuggeeTest; |
| import java.io.*; |
| import java.util.*; |
| |
| /* |
| * This class serial executes several JDI tests based on nsk.share.jdi.TestDebuggerType2 in single VM |
| * SerialExecutionDebugger is used together with SerialExecutionDebuggee, execution process is following: |
| * |
| * - SerialExecutionDebugger reads tests to execute from input file, test description is debugger class name and test's parameters, |
| * if 'shuffle' option is specified in input file debugger executes tests in random order (input file should contain line "OPTIONS:shuffle"). |
| * SerialExecutionDebugger can execute tests several times in loop, number of iterations should be specified in input file in following manner: |
| * OPTIONS:iterations <iterations_number>. |
| * |
| * - SerialExecutionDebugger starts debuggee VM with main class SerialExecutionDebuggee, |
| * initializes IOPipe and 'debuggee' object which represents debuggee VM |
| * |
| * - for each test from input file: |
| * |
| * - SerialExecutionDebugger creates object of current debugger and initializes it with already created pipe and debuggee |
| * - SerialExecutionDebugger sends command to SerialExecutionDebuggee: 'COMMAND_EXECUTE_DEBUGGEE <CurrentDebuggeeName>' |
| * (CurrentDebuggeeName name should provide current debugger), and waits READY signal from debuggee |
| * - SerialExecutionDebuggee parses received command, extracts debugee name, creates object of current debuggee, which should be |
| * subclass of nsk.share.jpda.AbstractDebuggeeTestName |
| * - SerialExecutionDebuggee executes current debuggee's method 'doTest()', in this method debuggee sends signal READY |
| * and waits debugger command |
| * - SerialExecutionDebugger receives signal READY and executes current debugger's method 'doTest()', in |
| * this method debugger should perform test |
| * - when debugger method doTest() finishes SerialExecutionDebugger checks is this test passed or failed and |
| * sends command QUIT to the current debuggee, and when current debuggee finishes sends command 'COMMAND_CLEAR_DEBUGGEE' to the SerialExecutionDebuggee, |
| * after this command SerialExecutionDebugger and SerialExecutionDebuggee ready to execute next test |
| * |
| * - when all tests was executed SerialExecutionDebugger sends command QUIT to the SerialExecutionDebuggee and exits |
| * |
| * SerialExecutionDebugger requires "-configFile <ini-file>" parameter, <ini-file> - file with list of tests for execution |
| */ |
| public class SerialExecutionDebugger extends TestDebuggerType2 { |
| static public void main(String[] args) { |
| System.exit(Consts.JCK_STATUS_BASE + new SerialExecutionDebugger().runIt(args, System.out)); |
| } |
| |
| public String debuggeeClassName() { |
| return SerialExecutionDebuggee.class.getName(); |
| } |
| |
| // contains test's debugger class name and test parameters |
| static class Test { |
| public Test(String debuggerClassName, String[] arguments) { |
| this.debuggerClassName = debuggerClassName; |
| this.arguments = arguments; |
| } |
| |
| public String argumentsToString() { |
| String result = ""; |
| |
| for (String argument : arguments) |
| result += argument + " "; |
| |
| return result; |
| } |
| |
| String debuggerClassName; |
| |
| String arguments[]; |
| } |
| |
| private Test tests[]; |
| |
| // how many times execute tests |
| private int iterations = 1; |
| |
| // requires "-configFile <ini-file>" parameter, <ini-file> - file with list |
| // of tests for execution |
| protected String[] doInit(String args[], PrintStream out) { |
| args = super.doInit(args, out); |
| |
| String configFileName = null; |
| |
| ArrayList<String> standardArgs = new ArrayList<String>(); |
| |
| for (int i = 0; i < args.length; i++) { |
| if (args[i].equals("-configFile") && (i < args.length - 1)) { |
| configFileName = args[i + 1]; |
| i++; |
| } else |
| standardArgs.add(args[i]); |
| } |
| |
| if (configFileName == null) { |
| throw new TestBug("Config file wasn't specified (use option -configFile <file name>)"); |
| } |
| |
| tests = parseConfigFile(configFileName); |
| |
| if (tests.length == 0) |
| throw new TestBug("Tests to run were not specified"); |
| |
| return standardArgs.toArray(new String[] {}); |
| } |
| |
| // read test names and test parameters from ini-file |
| private Test[] parseConfigFile(String fileName) { |
| List<Test> result = new ArrayList<Test>(); |
| |
| boolean shuffle = false; |
| |
| try { |
| File file = new File(fileName); |
| |
| LineNumberReader lineReader = new LineNumberReader(new FileReader(file)); |
| |
| String line = null; |
| |
| while ((line = lineReader.readLine()) != null) { |
| // skip empty lines and comments started with '#" |
| if (line.length() == 0 || line.startsWith("#")) |
| continue; |
| |
| if (line.startsWith("OPTIONS:")) { |
| String arguments[] = line.substring(8).split(" "); |
| |
| for (int i = 0; i < arguments.length; i++) { |
| if (arguments[i].equalsIgnoreCase("shuffle")) |
| shuffle = true; |
| else if (arguments[i].equalsIgnoreCase("iterations") && (i < (arguments.length - 1))) { |
| iterations = Integer.parseInt(arguments[i + 1]); |
| i++; |
| } |
| } |
| |
| continue; |
| } |
| |
| StreamTokenizer tokenizer = new StreamTokenizer(new StringReader(line)); |
| tokenizer.resetSyntax(); |
| tokenizer.wordChars(Integer.MIN_VALUE, Integer.MAX_VALUE); |
| tokenizer.whitespaceChars(' ', ' '); |
| |
| if (tokenizer.nextToken() != StreamTokenizer.TT_WORD) |
| throw new TestBug("Invalid ini file format"); |
| |
| String testClassName = tokenizer.sval; |
| List<String> parameters = new ArrayList<String>(); |
| |
| int token; |
| while ((token = tokenizer.nextToken()) != StreamTokenizer.TT_EOF) { |
| if (token == StreamTokenizer.TT_WORD) { |
| if (tokenizer.sval.equals("$CLASSPATH")) |
| parameters.add(classpath); |
| else |
| parameters.add(tokenizer.sval); |
| } |
| |
| if (token == StreamTokenizer.TT_NUMBER) { |
| parameters.add("" + tokenizer.nval); |
| } |
| } |
| |
| result.add(new Test(testClassName, parameters.toArray(new String[] {}))); |
| } |
| |
| } catch (IOException e) { |
| throw new TestBug("Exception during config file parsing: " + e); |
| } |
| |
| if (shuffle) { |
| if (testWorkDir == null) |
| throw new TestBug("Debugger requires -testWorkDir parameter"); |
| |
| Collections.shuffle(result); |
| |
| // save resulted tests sequence in file (to simplify reproducing) |
| try { |
| File file = new File(testWorkDir + File.separator + "run.tests"); |
| file.createNewFile(); |
| |
| PrintWriter writer = new PrintWriter(new FileWriter(file)); |
| |
| for (Test test : result) { |
| writer.println(test.debuggerClassName + " " + test.argumentsToString()); |
| } |
| |
| writer.close(); |
| } catch (IOException e) { |
| throw new TestBug("Unexpected IOException: " + e); |
| } |
| } |
| |
| System.out.println("Tests execution order: "); |
| for (Test test : result) { |
| System.out.println(test.debuggerClassName + " " + test.argumentsToString()); |
| } |
| |
| return result.toArray(new Test[] {}); |
| } |
| |
| public void doTest() { |
| |
| stresser.start(iterations); |
| |
| try { |
| if (iterations == 1) { |
| /* |
| * Since many test couldn't be run in single VM twice and test config specifies only 1 iteration don't |
| * multiple iterations by iterations factor and execute tests once (not depending on iterations factor) |
| */ |
| executeTests(); |
| } else { |
| while (stresser.continueExecution()) { |
| if (!executeTests()) { |
| // if error occured stop execution |
| break; |
| } |
| } |
| } |
| } finally { |
| stresser.finish(); |
| } |
| } |
| |
| boolean executeTests() { |
| // maximum execution time of single test |
| long maxExecutionTime = 0; |
| |
| for (Test test : tests) { |
| long testStartTime = System.currentTimeMillis(); |
| |
| TestDebuggerType2 debugger = null; |
| |
| try { |
| // create debugger object |
| Class debuggerClass = Class.forName(test.debuggerClassName); |
| |
| if (!TestDebuggerType2.class.isAssignableFrom(debuggerClass)) { |
| setSuccess(false); |
| log.complain("Invalid debugger class: " + debuggerClass); |
| return false; |
| } |
| |
| // init test debugger, pass to the debugger already created |
| // objects: argHandler, log, pipe, debuggee, vm |
| debugger = (TestDebuggerType2) debuggerClass.newInstance(); |
| debugger.initDebugger(argHandler, log, pipe, debuggee, vm); |
| debugger.doInit(test.arguments, System.out); |
| } catch (Exception e) { |
| setSuccess(false); |
| log.complain("Unexpected exception during debugger initialization: " + e); |
| e.printStackTrace(log.getOutStream()); |
| |
| return false; |
| } |
| |
| log.display("Execute debugger: " + debugger); |
| |
| // send command to the SerialExecutionDebuggee (create debuggee |
| // object) |
| pipe.println(SerialExecutionDebuggee.COMMAND_EXECUTE_DEBUGGEE + ":" + debugger.debuggeeClassName()); |
| |
| // wait first READY from AbstractDebuggeeTest.doTest() (debuggee |
| // sends this command when it was initialized and ready for |
| // test) |
| if (!isDebuggeeReady()) |
| return false; |
| |
| try { |
| // here debuggee should be ready for test and current |
| // debugger may perform test |
| debugger.doTest(); |
| |
| if (debugger.getSuccess()) { |
| log.display("Debugger " + debugger + " finished successfully"); |
| } else { |
| setSuccess(false); |
| log.complain("Debugger " + debugger + " finished with errors"); |
| } |
| } catch (TestBug testBug) { |
| setSuccess(false); |
| log.complain("Test bug in " + debugger + ": " + testBug); |
| testBug.printStackTrace(log.getOutStream()); |
| } catch (Throwable t) { |
| setSuccess(false); |
| log.complain("Unexpected exception during test execution(debugger: " + debugger + "): " + t); |
| t.printStackTrace(log.getOutStream()); |
| } |
| |
| // send QUIT command to the current debuggee |
| pipe.println(AbstractDebuggeeTest.COMMAND_QUIT); |
| |
| if (!isDebuggeeReady()) |
| return false; |
| |
| // send command to the SerialExecutionDebuggee |
| pipe.println(SerialExecutionDebuggee.COMMAND_CLEAR_DEBUGGEE); |
| |
| if (!isDebuggeeReady()) |
| return false; |
| |
| long testExecutionTime = System.currentTimeMillis() - testStartTime; |
| |
| if (testExecutionTime > maxExecutionTime) |
| maxExecutionTime = testExecutionTime; |
| |
| if (maxExecutionTime > stresser.getTimeLeft()) { |
| log.display("WARNING: stop test execution because of timeout " + |
| "(max execution time for single test: " + maxExecutionTime + ", time left: " + stresser.getTimeLeft() + ")"); |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| } |