08855df46a
Reviewed-by: vlivanov, erikj, mseledtsov, gthornbr
338 lines
13 KiB
Java
338 lines
13 KiB
Java
/*
|
|
* 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;
|
|
}
|
|
}
|