8339366: [jittester] Make it possible to generate tests without execution
Reviewed-by: lmesnik
This commit is contained in:
parent
6b5958d661
commit
559fc711e0
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2024, 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
|
||||
@ -23,13 +23,6 @@
|
||||
|
||||
package jdk.test.lib.jittester;
|
||||
|
||||
import jdk.test.lib.util.Pair;
|
||||
import jdk.test.lib.jittester.factories.IRNodeBuilder;
|
||||
import jdk.test.lib.jittester.types.TypeKlass;
|
||||
import jdk.test.lib.jittester.utils.FixedTrees;
|
||||
import jdk.test.lib.jittester.utils.OptionResolver;
|
||||
import jdk.test.lib.jittester.utils.OptionResolver.Option;
|
||||
import jdk.test.lib.jittester.utils.PseudoRandom;
|
||||
import java.time.LocalTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -39,57 +32,6 @@ import java.util.function.Function;
|
||||
public class Automatic {
|
||||
public static final int MINUTES_TO_WAIT = Integer.getInteger("jdk.test.lib.jittester", 3);
|
||||
|
||||
private static Pair<IRNode, IRNode> generateIRTree(String name) {
|
||||
ProductionLimiter.resetTimer();
|
||||
SymbolTable.removeAll();
|
||||
TypeList.removeAll();
|
||||
|
||||
IRNodeBuilder builder = new IRNodeBuilder()
|
||||
.setPrefix(name)
|
||||
.setName(name)
|
||||
.setLevel(0);
|
||||
|
||||
Long complexityLimit = ProductionParams.complexityLimit.value();
|
||||
IRNode privateClasses = null;
|
||||
if (!ProductionParams.disableClasses.value()) {
|
||||
long privateClassComlexity = (long) (complexityLimit * PseudoRandom.random());
|
||||
try {
|
||||
privateClasses = builder.setComplexityLimit(privateClassComlexity)
|
||||
.getClassDefinitionBlockFactory()
|
||||
.produce();
|
||||
} catch (ProductionFailedException ex) {
|
||||
ex.printStackTrace(System.out);
|
||||
}
|
||||
}
|
||||
long mainClassComplexity = (long) (complexityLimit * PseudoRandom.random());
|
||||
IRNode mainClass = null;
|
||||
try {
|
||||
mainClass = builder.setComplexityLimit(mainClassComplexity)
|
||||
.getMainKlassFactory()
|
||||
.produce();
|
||||
TypeKlass aClass = new TypeKlass(name);
|
||||
mainClass.getChild(1).addChild(FixedTrees.generateMainOrExecuteMethod(aClass, true));
|
||||
mainClass.getChild(1).addChild(FixedTrees.generateMainOrExecuteMethod(aClass, false));
|
||||
} catch (ProductionFailedException ex) {
|
||||
ex.printStackTrace(System.out);
|
||||
}
|
||||
return new Pair<>(mainClass, privateClasses);
|
||||
}
|
||||
|
||||
private static void initializeTestGenerator(String[] params) {
|
||||
OptionResolver parser = new OptionResolver();
|
||||
Option<String> propertyFileOpt = parser.addStringOption('p', "property-file",
|
||||
"conf/default.properties", "File to read properties from");
|
||||
ProductionParams.register(parser);
|
||||
parser.parse(params, propertyFileOpt);
|
||||
PseudoRandom.reset(ProductionParams.seed.value());
|
||||
TypesParser.parseTypesAndMethods(ProductionParams.classesFile.value(),
|
||||
ProductionParams.excludeMethodsFile.value());
|
||||
if (ProductionParams.specificSeed.isSet()) {
|
||||
PseudoRandom.setCurrentSeed(ProductionParams.specificSeed.value());
|
||||
}
|
||||
}
|
||||
|
||||
private static List<TestsGenerator> getTestGenerators() {
|
||||
List<TestsGenerator> result = new ArrayList<>();
|
||||
Class<?> factoryClass;
|
||||
@ -109,7 +51,9 @@ public class Automatic {
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
initializeTestGenerator(args);
|
||||
ProductionParams.initializeFromCmdline(args);
|
||||
IRTreeGenerator.initializeWithProductionParams();
|
||||
|
||||
int counter = 0;
|
||||
System.out.printf("Generating %d tests...%n", ProductionParams.numberOfTests.value());
|
||||
System.out.printf(" %13s | %8s | %8s | %8s |%n", "start time", "count", "generat",
|
||||
@ -121,7 +65,7 @@ public class Automatic {
|
||||
try {
|
||||
System.out.print("[" + LocalTime.now() + "] |");
|
||||
String name = "Test_" + counter;
|
||||
Pair<IRNode, IRNode> irTree = generateIRTree(name);
|
||||
var test = IRTreeGenerator.generateIRTree(name);
|
||||
System.out.printf(" %8d |", counter);
|
||||
long maxWaitTime = TimeUnit.MINUTES.toMillis(MINUTES_TO_WAIT);
|
||||
double generationTime = System.currentTimeMillis() - start;
|
||||
@ -129,7 +73,7 @@ public class Automatic {
|
||||
start = System.currentTimeMillis();
|
||||
Thread generatorThread = new Thread(() -> {
|
||||
for (TestsGenerator generator : generators) {
|
||||
generator.accept(irTree.first, irTree.second);
|
||||
generator.accept(test);
|
||||
}
|
||||
});
|
||||
generatorThread.start();
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2024, 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
|
||||
@ -47,16 +47,17 @@ class ByteCodeGenerator extends TestsGenerator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(IRNode mainClass, IRNode privateClasses) {
|
||||
generateClassFiles(mainClass, privateClasses);
|
||||
generateSeparateJtregHeader(mainClass);
|
||||
public void accept(IRTreeGenerator.Test test) {
|
||||
IRNode mainClass = test.mainClass();
|
||||
generateClassFiles(mainClass, test.privateClasses());
|
||||
generateSeparateJtregHeader(test.seed(), mainClass);
|
||||
compilePrinter();
|
||||
generateGoldenOut(mainClass.getName());
|
||||
}
|
||||
|
||||
private void generateSeparateJtregHeader(IRNode mainClass) {
|
||||
private void generateSeparateJtregHeader(long seed, IRNode mainClass) {
|
||||
String mainClassName = mainClass.getName();
|
||||
writeFile(generatorDir, mainClassName + ".java", getJtregHeader(mainClassName));
|
||||
writeFile(generatorDir, mainClassName + ".java", getJtregHeader(mainClassName, seed));
|
||||
}
|
||||
|
||||
private void generateClassFiles(IRNode mainClass, IRNode privateClasses) {
|
||||
@ -94,4 +95,17 @@ class ByteCodeGenerator extends TestsGenerator {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
ProductionParams.initializeFromCmdline(args);
|
||||
IRTreeGenerator.initializeWithProductionParams();
|
||||
|
||||
ByteCodeGenerator generator = new ByteCodeGenerator();
|
||||
|
||||
for (String mainClass : ProductionParams.mainClassNames.value()) {
|
||||
var test = IRTreeGenerator.generateIRTree(mainClass);
|
||||
generator.generateClassFiles(test.mainClass(), test.privateClasses());
|
||||
generator.generateSeparateJtregHeader(test.seed(), test.mainClass());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 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 jdk.test.lib.jittester;
|
||||
|
||||
import jdk.test.lib.jittester.factories.IRNodeBuilder;
|
||||
import jdk.test.lib.jittester.types.TypeKlass;
|
||||
import jdk.test.lib.jittester.utils.FixedTrees;
|
||||
import jdk.test.lib.jittester.utils.PseudoRandom;
|
||||
|
||||
/**
|
||||
* Generates IR trees for fuzzy test classes.
|
||||
*/
|
||||
public class IRTreeGenerator {
|
||||
|
||||
/**
|
||||
* Generated Test - main and private classes trees along with random seed used for generation.
|
||||
*/
|
||||
public record Test (long seed, IRNode mainClass, IRNode privateClasses) {};
|
||||
|
||||
/**
|
||||
* Generates IR trees for main and private classes.
|
||||
*
|
||||
* @param name main class name
|
||||
* @return a pair (main class; private classes)
|
||||
*/
|
||||
public static Test generateIRTree(String name) {
|
||||
long seed = PseudoRandom.getCurrentSeed();
|
||||
ProductionLimiter.resetTimer();
|
||||
//NB: SymbolTable is a widely-used singleton, hence all the locking.
|
||||
SymbolTable.removeAll();
|
||||
TypeList.removeAll();
|
||||
|
||||
IRNodeBuilder builder = new IRNodeBuilder()
|
||||
.setPrefix(name)
|
||||
.setName(name)
|
||||
.setLevel(0);
|
||||
|
||||
Long complexityLimit = ProductionParams.complexityLimit.value();
|
||||
IRNode privateClasses = null;
|
||||
if (!ProductionParams.disableClasses.value()) {
|
||||
long privateClassComlexity = (long) (complexityLimit * PseudoRandom.random());
|
||||
try {
|
||||
privateClasses = builder.setComplexityLimit(privateClassComlexity)
|
||||
.getClassDefinitionBlockFactory()
|
||||
.produce();
|
||||
} catch (ProductionFailedException ex) {
|
||||
ex.printStackTrace(System.out);
|
||||
}
|
||||
}
|
||||
long mainClassComplexity = (long) (complexityLimit * PseudoRandom.random());
|
||||
IRNode mainClass = null;
|
||||
try {
|
||||
mainClass = builder.setComplexityLimit(mainClassComplexity)
|
||||
.getMainKlassFactory()
|
||||
.produce();
|
||||
TypeKlass aClass = new TypeKlass(name);
|
||||
mainClass.getChild(1).addChild(FixedTrees.generateMainOrExecuteMethod(aClass, true));
|
||||
mainClass.getChild(1).addChild(FixedTrees.generateMainOrExecuteMethod(aClass, false));
|
||||
} catch (ProductionFailedException ex) {
|
||||
ex.printStackTrace(System.out);
|
||||
}
|
||||
return new Test(seed, mainClass, privateClasses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the generator from ProductionParams static class.
|
||||
*/
|
||||
public static void initializeWithProductionParams() {
|
||||
TypesParser.parseTypesAndMethods(ProductionParams.classesFile.value(),
|
||||
ProductionParams.excludeMethodsFile.value());
|
||||
if (ProductionParams.specificSeed.isSet()) {
|
||||
PseudoRandom.setCurrentSeed(ProductionParams.specificSeed.value());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -42,19 +42,20 @@ public class JavaCodeGenerator extends TestsGenerator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(IRNode mainClass, IRNode privateClasses) {
|
||||
public void accept(IRTreeGenerator.Test test) {
|
||||
IRNode mainClass = test.mainClass();
|
||||
String mainClassName = mainClass.getName();
|
||||
generateSources(mainClass, privateClasses);
|
||||
generateSources(test.seed(), mainClass, test.privateClasses());
|
||||
compilePrinter();
|
||||
compileJavaFile(mainClassName);
|
||||
generateGoldenOut(mainClassName);
|
||||
}
|
||||
|
||||
private void generateSources(IRNode mainClass, IRNode privateClasses) {
|
||||
private void generateSources(long seed, IRNode mainClass, IRNode privateClasses) {
|
||||
String mainClassName = mainClass.getName();
|
||||
StringBuilder code = new StringBuilder();
|
||||
JavaCodeVisitor vis = new JavaCodeVisitor();
|
||||
code.append(getJtregHeader(mainClassName));
|
||||
code.append(getJtregHeader(mainClassName, seed));
|
||||
if (privateClasses != null) {
|
||||
code.append(privateClasses.accept(vis));
|
||||
}
|
||||
@ -79,7 +80,19 @@ public class JavaCodeGenerator extends TestsGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
private static String[] generatePrerunAction(String mainClassName) {
|
||||
protected static String[] generatePrerunAction(String mainClassName) {
|
||||
return new String[] {"@compile " + mainClassName + ".java"};
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
ProductionParams.initializeFromCmdline(args);
|
||||
IRTreeGenerator.initializeWithProductionParams();
|
||||
|
||||
JavaCodeGenerator generator = new JavaCodeGenerator();
|
||||
|
||||
for (String mainClass : ProductionParams.mainClassNames.value()) {
|
||||
var test = IRTreeGenerator.generateIRTree(mainClass);
|
||||
generator.generateSources(test.seed(), test.mainClass(), test.privateClasses());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2024, 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
|
||||
@ -23,11 +23,15 @@
|
||||
|
||||
package jdk.test.lib.jittester;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import jdk.test.lib.jittester.utils.OptionResolver;
|
||||
import jdk.test.lib.jittester.utils.OptionResolver.Option;
|
||||
import jdk.test.lib.jittester.utils.PseudoRandom;
|
||||
|
||||
public class ProductionParams {
|
||||
|
||||
public static Option<List<String>> mainClassNames = null;
|
||||
public static Option<Integer> productionLimit = null;
|
||||
public static Option<Integer> productionLimitSeconds = null;
|
||||
public static Option<Integer> dataMemberLimit = null;
|
||||
@ -72,6 +76,7 @@ public class ProductionParams {
|
||||
// workaraound: to reduce chance throwing ArrayIndexOutOfBoundsException
|
||||
public static Option<Integer> chanceExpressionIndex = null;
|
||||
public static Option<String> testbaseDir = null;
|
||||
public static Option<String> tempDir = null;
|
||||
public static Option<Integer> numberOfTests = null;
|
||||
public static Option<String> seed = null;
|
||||
public static Option<Long> specificSeed = null;
|
||||
@ -81,6 +86,7 @@ public class ProductionParams {
|
||||
public static Option<String> generatorsFactories = null;
|
||||
|
||||
public static void register(OptionResolver optionResolver) {
|
||||
mainClassNames = optionResolver.addRepeatingOption('k', "main-class", "", "Main class name");
|
||||
productionLimit = optionResolver.addIntegerOption('l', "production-limit", 100, "Limit on steps in the production of an expression");
|
||||
productionLimitSeconds = optionResolver.addIntegerOption("production-limit-seconds", 600, "Limit the time a test generation may take");
|
||||
dataMemberLimit = optionResolver.addIntegerOption('v', "data-member-limit", 10, "Upper limit on data members");
|
||||
@ -124,6 +130,7 @@ public class ProductionParams {
|
||||
enableFinalizers = optionResolver.addBooleanOption("enable-finalizers", "Enable finalizers (for stress testing)");
|
||||
chanceExpressionIndex = optionResolver.addIntegerOption("chance-expression-index", 0, "A non negative decimal integer used to restrict chane of generating expression in array index while creating or accessing by index");
|
||||
testbaseDir = optionResolver.addStringOption("testbase-dir", ".", "Testbase dir");
|
||||
tempDir = optionResolver.addStringOption("temp-dir", ".", "Temp dir path");
|
||||
numberOfTests = optionResolver.addIntegerOption('n', "number-of-tests", 0, "Number of test classes to generate");
|
||||
seed = optionResolver.addStringOption("seed", "", "Random seed");
|
||||
specificSeed = optionResolver.addLongOption('z', "specificSeed", 0L, "A seed to be set for specific test generation(regular seed still needed for initialization)");
|
||||
@ -132,4 +139,18 @@ public class ProductionParams {
|
||||
generators = optionResolver.addStringOption("generators", "", "Comma-separated list of generator names");
|
||||
generatorsFactories = optionResolver.addStringOption("generatorsFactories", "", "Comma-separated list of generators factories class names");
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes from the given command-line args
|
||||
*
|
||||
* @param args command-line arguments to use for initialization
|
||||
*/
|
||||
public static void initializeFromCmdline(String[] args) {
|
||||
OptionResolver parser = new OptionResolver();
|
||||
Option<String> propertyFileOpt = parser.addStringOption('p', "property-file",
|
||||
"conf/default.properties", "File to read properties from");
|
||||
ProductionParams.register(parser);
|
||||
parser.parse(args, propertyFileOpt);
|
||||
PseudoRandom.reset(ProductionParams.seed.value());
|
||||
}
|
||||
}
|
||||
|
@ -30,13 +30,12 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import jdk.test.lib.jittester.types.TypeKlass;
|
||||
import jdk.test.lib.jittester.utils.PseudoRandom;
|
||||
|
||||
public abstract class TestsGenerator implements BiConsumer<IRNode, IRNode> {
|
||||
public abstract class TestsGenerator implements Consumer<IRTreeGenerator.Test> {
|
||||
private static final int DEFAULT_JTREG_TIMEOUT = 120;
|
||||
protected static final String JAVA_BIN = getJavaPath();
|
||||
protected static final String JAVAC = Paths.get(JAVA_BIN, "javac").toString();
|
||||
@ -121,9 +120,9 @@ public abstract class TestsGenerator implements BiConsumer<IRNode, IRNode> {
|
||||
}
|
||||
}
|
||||
|
||||
protected String getJtregHeader(String mainClassName) {
|
||||
protected String getJtregHeader(String mainClassName, long seed) {
|
||||
String synopsis = "seed = '" + ProductionParams.seed.value() + "'"
|
||||
+ ", specificSeed = '" + PseudoRandom.getCurrentSeed() + "'";
|
||||
+ ", specificSeed = '" + seed + "'";
|
||||
StringBuilder header = new StringBuilder();
|
||||
header.append("/*\n * @test\n * @summary ")
|
||||
.append(synopsis)
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2024, 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
|
||||
@ -156,6 +156,12 @@ public class OptionResolver {
|
||||
return addBooleanOption(null, name, false, description);
|
||||
}
|
||||
|
||||
public Option<List<String>> addRepeatingOption(Character key, String name, String defaultValue, String description) {
|
||||
final Option<List<String>> option = new RepeatingOption(key, name, defaultValue, description);
|
||||
register(option);
|
||||
return option;
|
||||
}
|
||||
|
||||
private void register(Option<?> option) {
|
||||
if (options.put("--" + option.longName, option) != null) {
|
||||
throw new RuntimeException("Option is already registered for key " + option.longName);
|
||||
@ -264,6 +270,20 @@ public class OptionResolver {
|
||||
}
|
||||
}
|
||||
|
||||
private class RepeatingOption extends Option<List<String>> {
|
||||
List<String> accumulated = new ArrayList<String>();
|
||||
|
||||
RepeatingOption(Character s, String l, String v, String d) {
|
||||
super(s, l, List.of(v), d);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> parseFromString(String arg) {
|
||||
accumulated.add(arg);
|
||||
return accumulated;
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<Option<?>> getRegisteredOptions() {
|
||||
return Collections.unmodifiableSet(new LinkedHashSet<>(options.values()));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user