8369232: testlibrary_tests/ir_framework/tests/TestScenariosCrossProduct.java timed out

Reviewed-by: dfenacci, epeter
This commit is contained in:
Christian Hagedorn 2025-10-16 16:02:26 +00:00
parent 95380e1ea5
commit e56db37734
2 changed files with 305 additions and 106 deletions

View File

@ -327,8 +327,10 @@ public class TestFramework {
for (Scenario scenario : scenarios) {
int scenarioIndex = scenario.getIndex();
TestFormat.checkNoThrow(scenarioIndices.add(scenarioIndex),
"Cannot define two scenarios with the same index " + scenarioIndex);
if (!scenarioIndices.add(scenarioIndex)) {
TestFormat.failNoThrow("Cannot define two scenarios with the same index " + scenarioIndex);
continue;
}
this.scenarios.add(scenario);
}
TestFormat.throwIfAnyFailures();
@ -336,9 +338,12 @@ public class TestFramework {
}
/**
* Add the cross-product (cartesian product) of sets of flags as Scenarios. Unlike when when constructing
* Add the cross-product (cartesian product) of sets of flags as Scenarios. Unlike when constructing
* scenarios directly a string can contain multiple flags separated with a space. This allows grouping
* flags that have to be specified togeher. Further, an empty string in a set stands in for "no flag".
* flags that have to be specified together. Further, an empty string in a set stands in for "no flag".
* <p>
* Passing a single set will create a scenario for each of the provided flags in the set (i.e. the same as
* passing an additional set with an empty string only).
* <p>
* Example:
* <pre>
@ -355,7 +360,7 @@ public class TestFramework {
* Scenario(5, "-Xbatch -XX:-TieredCompilation", "-XX:+UseNewCode2")
* </pre>
*
* @param sets sets of flags to generate the cross product for.
* @param flagSets sets of flags to generate the cross product for.
* @return the same framework instance.
*/
@SafeVarargs
@ -376,7 +381,7 @@ public class TestFramework {
Stream<List<String>> crossProduct = Arrays.stream(flagSets)
.reduce(
Stream.of(Collections.<String>emptyList()), // Initialize Stream<List<String>> acc with a Stream containing an empty list of Strings.
Stream.of(Collections.emptyList()), // Initialize Stream<List<String>> acc with a Stream containing an empty list of Strings.
(Stream<List<String>> acc, Set<String> set) ->
acc.flatMap(lAcc -> // For each List<String>> lAcc in acc...
set.stream().map(flag -> { // ...and each flag in the current set...
@ -384,19 +389,19 @@ public class TestFramework {
newList.add(flag); // ...and append the flag.
return newList;
}) // This results in one List<List<String>> for each lAcc...
), // ...that get flattend into one big List<List<String>>.
(a, b) -> Stream.concat(a, b)); // combiner; if any reduction steps are executed in parallel, just concat two streams.
), // ...that get flattened into one big List<List<String>>.
Stream::concat); // combiner; if any reduction steps are executed in parallel, just concat two streams.
Scenario[] newScenarios = crossProduct
.map(flags -> new Scenario( // For each List<String> flags in crossProduct create a new Scenario.
idx.getAndIncrement(),
flags.stream() // Process flags
.map(s -> Set.of(s.split("[ ]"))) // Split muliple flags in the same string into separate strings.
.map(s -> Set.of(s.split("[ ]"))) // Split multiple flags in the same string into separate strings.
.flatMap(Collection::stream) // Flatten the Stream<List<String>> into Stream<String>>.
.filter(s -> !s.isEmpty()) // Remove empty string flags.
.collect(Collectors.toList())
.toList()
.toArray(new String[0])))
.collect(Collectors.toList()).toArray(new Scenario[0]);
.toList().toArray(new Scenario[0]);
return addScenarios(newScenarios);
}

View File

@ -23,15 +23,22 @@
package ir_framework.tests;
import java.util.Set;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Field;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import compiler.lib.ir_framework.*;
import compiler.lib.ir_framework.shared.TestRunException;
import compiler.lib.ir_framework.shared.TestFormatException;
import compiler.lib.ir_framework.shared.TestRunException;
import jdk.test.lib.Asserts;
/*
* @test
* @bug 8365262 8369232
* @requires vm.debug == true & vm.compMode != "Xint" & vm.compiler2.enabled & vm.flagless
* @summary Test cross product scenarios with the framework.
* @library /test/lib /testlibrary_tests /
@ -39,29 +46,20 @@ import jdk.test.lib.Asserts;
*/
public class TestScenariosCrossProduct {
static void hasNFailures(String s, int count) {
if (!s.matches("The following scenarios have failed: (#[0-9](, )?){" + count + "}. Please check stderr for more information.")) {
throw new RuntimeException("Expected " + count + " failures in \"" + s + "\"");
}
}
public static void main(String[] args) {
// Test argument handling
try {
TestFramework t = new TestFramework();
t.addCrossProductScenarios((Set<String>[]) null);
Asserts.fail("Should have thrown exception");
} catch (TestFormatException e) {}
try {
TestFramework t = new TestFramework();
t.addCrossProductScenarios(Set.of("foo", "bar"), null);
Asserts.fail("Should have thrown exception");
} catch (TestFormatException e) {}
expectFormatFailure((Set<String>[]) null);
expectFormatFailure(Set.of("foo", "bar"), null);
try {
TestFramework t = new TestFramework();
t.addCrossProductScenarios(Set.of("blub"), Set.of("foo", null));
Asserts.fail("Should have thrown exception");
} catch (NullPointerException e) {} // Set.of prevents null elements
shouldHaveThrown();
} catch (NullPointerException _) {
// Expected: Set.of prevents null elements
}
try {
TestFramework t = new TestFramework();
t.addCrossProductScenarios();
@ -70,95 +68,291 @@ public class TestScenariosCrossProduct {
}
// Single set should test all flags in the set by themselves.
try {
TestFramework t1 = new TestFramework();
t1.addCrossProductScenarios(Set.of("-XX:TLABRefillWasteFraction=51",
"-XX:TLABRefillWasteFraction=53",
"-XX:TLABRefillWasteFraction=64"));
t1.start();
Asserts.fail("Should have thrown exception");
} catch (TestRunException e) {
hasNFailures(e.getMessage(), 3);
}
new TestCase()
.inputFlags(Set.of(
Set.of("-XX:TLABRefillWasteFraction=51",
"-XX:TLABRefillWasteFraction=53",
"-XX:TLABRefillWasteFraction=64")
)
)
.expectedScenariosWithFlags(Set.of(
Set.of("-XX:TLABRefillWasteFraction=51"),
Set.of("-XX:TLABRefillWasteFraction=53"),
Set.of("-XX:TLABRefillWasteFraction=64")
))
.run();
// The cross product of a set with one element and a set with three elements is three sets.
try {
TestFramework t2 = new TestFramework();
t2.addCrossProductScenarios(Set.of("-XX:TLABRefillWasteFraction=53"),
Set.of("-XX:+UseNewCode", "-XX:+UseNewCode2", "-XX:+UseNewCode3"));
t2.start();
Asserts.fail("Should have thrown exception");
} catch (TestRunException e) {
hasNFailures(e.getMessage(), 3);
}
new TestCase()
.inputFlags(Set.of(
Set.of("-XX:TLABRefillWasteFraction=53"),
Set.of("-XX:+UseNewCode", "-XX:+UseNewCode2", "-XX:+UseNewCode3")
)
)
.expectedScenariosWithFlags(Set.of(
Set.of("-XX:TLABRefillWasteFraction=53", "-XX:+UseNewCode"),
Set.of("-XX:TLABRefillWasteFraction=53", "-XX:+UseNewCode2"),
Set.of("-XX:TLABRefillWasteFraction=53", "-XX:+UseNewCode3")
))
.run();
// The cross product of two sets with two elements is four sets.
try {
TestFramework t3 = new TestFramework();
t3.addCrossProductScenarios(Set.of("-XX:TLABRefillWasteFraction=53", "-XX:TLABRefillWasteFraction=64"),
Set.of("-XX:+UseNewCode", "-XX:-UseNewCode"));
t3.start();
Asserts.fail("Should have thrown exception");
} catch (TestRunException e) {
hasNFailures(e.getMessage(), 4);
}
new TestCase()
.inputFlags(Set.of(
Set.of("-XX:TLABRefillWasteFraction=53", "-XX:TLABRefillWasteFraction=64"),
Set.of("-XX:+UseNewCode", "-XX:-UseNewCode")
)
)
.expectedScenariosWithFlags(Set.of(
Set.of("-XX:TLABRefillWasteFraction=53", "-XX:+UseNewCode"),
Set.of("-XX:TLABRefillWasteFraction=53", "-XX:-UseNewCode"),
Set.of("-XX:TLABRefillWasteFraction=64", "-XX:+UseNewCode"),
Set.of("-XX:TLABRefillWasteFraction=64", "-XX:-UseNewCode")
))
.run();
// Test with a pair of flags.
try {
TestFramework t4 = new TestFramework();
t4.addCrossProductScenarios(Set.of("-XX:TLABRefillWasteFraction=50 -XX:+UseNewCode", "-XX:TLABRefillWasteFraction=40"),
Set.of("-XX:+UseNewCode2"));
t4.start();
Asserts.fail("Should have thrown exception");
} catch (TestRunException e) {
hasNFailures(e.getMessage(), 1);
}
new TestCase()
.inputFlags(Set.of(
Set.of("-XX:TLABRefillWasteFraction=50 -XX:+UseNewCode", "-XX:TLABRefillWasteFraction=40"),
Set.of("-XX:+UseNewCode2")
)
)
.expectedScenariosWithFlags(Set.of(
Set.of("-XX:TLABRefillWasteFraction=50", "-XX:+UseNewCode", "-XX:+UseNewCode2"),
Set.of("-XX:TLABRefillWasteFraction=40", "-XX:+UseNewCode2")
))
.run();
// Test with an empty string. All 6 scenarios fail because 64 is the default value for TLABRefillWasteFraction.
try {
TestFramework t5 = new TestFramework();
t5.addCrossProductScenarios(Set.of("", "-XX:TLABRefillWasteFraction=51", "-XX:TLABRefillWasteFraction=53"),
Set.of("-XX:+UseNewCode", "-XX:+UseNewCode2"));
t5.start();
Asserts.fail("Should have thrown exception");
} catch (TestRunException e) {
hasNFailures(e.getMessage(), 6);
}
// Test with an empty string, resulting in 6 scenarios.
new TestCase()
.inputFlags(Set.of(
Set.of("", "-XX:TLABRefillWasteFraction=51", "-XX:TLABRefillWasteFraction=53"),
Set.of("-XX:+UseNewCode", "-XX:+UseNewCode2")
)
)
.expectedScenariosWithFlags(Set.of(
Set.of("-XX:+UseNewCode"),
Set.of("-XX:+UseNewCode2"),
Set.of("-XX:TLABRefillWasteFraction=51", "-XX:+UseNewCode"),
Set.of("-XX:TLABRefillWasteFraction=51", "-XX:+UseNewCode2"),
Set.of("-XX:TLABRefillWasteFraction=53", "-XX:+UseNewCode"),
Set.of("-XX:TLABRefillWasteFraction=53", "-XX:+UseNewCode2")
))
.run();
// Test with 3 input sets which equals to 2x2x2 = 8 scenarios.
new TestCase()
.inputFlags(Set.of(
Set.of("-XX:TLABRefillWasteFraction=51",
"-XX:TLABRefillWasteFraction=53"),
Set.of("-XX:+UseNewCode",
"-XX:-UseNewCode"),
Set.of("-XX:+UseNewCode2",
"-XX:-UseNewCode2")
)
)
.expectedScenariosWithFlags(Set.of(
Set.of("-XX:TLABRefillWasteFraction=51", "-XX:+UseNewCode", "-XX:+UseNewCode2"),
Set.of("-XX:TLABRefillWasteFraction=53", "-XX:+UseNewCode", "-XX:+UseNewCode2"),
Set.of("-XX:TLABRefillWasteFraction=51", "-XX:-UseNewCode", "-XX:+UseNewCode2"),
Set.of("-XX:TLABRefillWasteFraction=53", "-XX:-UseNewCode", "-XX:+UseNewCode2"),
Set.of("-XX:TLABRefillWasteFraction=51", "-XX:+UseNewCode", "-XX:-UseNewCode2"),
Set.of("-XX:TLABRefillWasteFraction=53", "-XX:+UseNewCode", "-XX:-UseNewCode2"),
Set.of("-XX:TLABRefillWasteFraction=51", "-XX:-UseNewCode", "-XX:-UseNewCode2"),
Set.of("-XX:TLABRefillWasteFraction=53", "-XX:-UseNewCode", "-XX:-UseNewCode2")
))
.run();
TestFramework testFramework = new TestFramework();
testFramework.addScenarios(new Scenario(0, "-XX:TLABRefillWasteFraction=50", "-XX:+UseNewCode"));
testFramework.addCrossProductScenarios(Set.of("-XX:TLABRefillWasteFraction=51", "-XX:TLABRefillWasteFraction=53"),
Set.of("-XX:+UseNewCode", "-XX:+UseNewCode2"));
try {
TestFramework t6 = new TestFramework();
t6.addScenarios(new Scenario(0, "-XX:TLABRefillWasteFraction=50", "-XX:+UseNewCode")); // failPair
t6.addCrossProductScenarios(Set.of("-XX:TLABRefillWasteFraction=51", "-XX:TLABRefillWasteFraction=53"),
Set.of("-XX:+UseNewCode", "-XX:+UseNewCode2"));
try {
t6.addScenarios(new Scenario(4, "-XX:+UseNewCode3")); // fails because index 4 is already used
Asserts.fail("Should have thrown exception");
} catch (TestFormatException e) {}
t6.addScenarios(new Scenario(5, "-XX:+UseNewCode3")); // fail default
t6.start();
Asserts.fail("Should have thrown exception");
} catch (TestRunException e) {
hasNFailures(e.getMessage(), 6);
testFramework.addScenarios(new Scenario(4, "-XX:+UseNewCode3")); // fails because index 4 is already used
shouldHaveThrown();
} catch (TestFormatException _) {
// Expected.
}
testFramework.addScenarios(new Scenario(5, "-XX:+UseNewCode3"));
new TestCase()
.expectedScenariosWithFlags(Set.of(
Set.of("-XX:TLABRefillWasteFraction=50", "-XX:+UseNewCode"),
Set.of("-XX:TLABRefillWasteFraction=51", "-XX:+UseNewCode"),
Set.of("-XX:TLABRefillWasteFraction=51", "-XX:+UseNewCode2"),
Set.of("-XX:TLABRefillWasteFraction=53", "-XX:+UseNewCode"),
Set.of("-XX:TLABRefillWasteFraction=53", "-XX:+UseNewCode2"),
Set.of("-XX:+UseNewCode3")
))
.runWithPreAddedScenarios(testFramework);
runEndToEndTest();
}
private static void expectFormatFailure(Set<String>... flagSets) {
TestFramework testFramework = new TestFramework();
try {
testFramework.addCrossProductScenarios(flagSets);
shouldHaveThrown();
} catch (TestFormatException _) {
// Expected.
}
}
@Test
@IR(applyIf = {"TLABRefillWasteFraction", "64"}, counts = {IRNode.CALL, "1"})
public void failDefault() {
private static void shouldHaveThrown() {
Asserts.fail("Should have thrown exception");
}
static class TestCase {
private Set<Set<String>> inputFlags;
private Set<Set<String>> expectedScenariosWithFlags;
public TestCase inputFlags(Set<Set<String>> inputFlags) {
this.inputFlags = inputFlags;
return this;
}
public TestCase expectedScenariosWithFlags(Set<Set<String>> expectedScenariosWithFlags) {
this.expectedScenariosWithFlags = expectedScenariosWithFlags;
return this;
}
public void run() {
TestFramework testFramework = new TestFramework();
testFramework.addCrossProductScenarios(inputFlags.toArray(new Set[0]));
runWithPreAddedScenarios(testFramework);
}
public void runWithPreAddedScenarios(TestFramework testFramework) {
List<Scenario> scenariosFromCrossProduct = getScenarios(testFramework);
assertScenarioCount(expectedScenariosWithFlags.size(), scenariosFromCrossProduct);
assertScenariosWithFlags(scenariosFromCrossProduct, expectedScenariosWithFlags);
assertSameResultWhenManuallyAdding(scenariosFromCrossProduct, expectedScenariosWithFlags);
}
private static void assertScenarioCount(int expectedCount, List<Scenario> scenarios) {
Asserts.assertEQ(expectedCount, scenarios.size(), "Scenario count is off");
}
/**
* Check that the added scenarios to the IR framework with TestFramework.addCrossProductScenarios()
* (i.e. 'scenariosFromCrossProduct') match the expected flag combos (i.e. 'expectedScenariosWithFlags').
*/
private static void assertScenariosWithFlags(List<Scenario> scenariosFromCrossProduct,
Set<Set<String>> expectedScenariosWithFlags) {
for (Set<String> expectedScenarioFlags : expectedScenariosWithFlags) {
if (scenariosFromCrossProduct.stream()
.map(Scenario::getFlags)
.map(Set::copyOf)
.anyMatch(flags -> flags.equals(expectedScenarioFlags))) {
continue;
}
System.err.println("Scenarios from cross product:");
for (Scenario s : scenariosFromCrossProduct) {
System.err.println(Arrays.toString(s.getFlags().toArray()));
}
throw new RuntimeException("Could not find a scenario with the provided flags: " + Arrays.toString(expectedScenarioFlags.toArray()));
}
}
/**
* Add scenarios for the provided flag sets in 'expectedScenariosWithFlags' by using TestFramework.addScenarios().
* We should end up with the same scenarios as if we added them with TestFramework.addCrossProductScenarios().
* This is verified by this method by comparing the flags of the scenarios, ignoring scenario indices.
*/
private static void assertSameResultWhenManuallyAdding(List<Scenario> scenariosFromCrossProduct,
Set<Set<String>> expectedScenariosWithFlags) {
List<Scenario> expectedScenarios = getScenariosWithFlags(expectedScenariosWithFlags);
List<Scenario> fetchedScenarios = addScenariosAndFetchFromFramework(expectedScenarios);
assertSameScenarios(scenariosFromCrossProduct, fetchedScenarios);
}
private static List<Scenario> getScenariosWithFlags(Set<Set<String>> expectedScenariosWithFlags) {
List<Scenario> expecedScenarioList = new ArrayList<>();
int index = -1; // Use some different indices - should not matter what we choose.
for (Set<String> expectedScenarioFlags : expectedScenariosWithFlags) {
expecedScenarioList.add(new Scenario(index--, expectedScenarioFlags.toArray(new String[0])));
}
return expecedScenarioList;
}
private static List<Scenario> addScenariosAndFetchFromFramework(List<Scenario> expecedScenarioList) {
TestFramework testFramework = new TestFramework();
testFramework.addScenarios(expecedScenarioList.toArray(new Scenario[0]));
return getScenarios(testFramework);
}
private static void assertSameScenarios(List<Scenario> scenariosFromCrossProduct,
List<Scenario> expectedScenarios) {
assertScenariosWithFlags(scenariosFromCrossProduct, fetchFlags(expectedScenarios));
}
private static Set<Set<String>> fetchFlags(List<Scenario> scenarios) {
return scenarios.stream()
.map(scenario -> new HashSet<>(scenario.getFlags()))
.collect(Collectors.toSet());
}
}
private static List<Scenario> getScenarios(TestFramework testFramework) {
Field field;
try {
field = TestFramework.class.getDeclaredField("scenarios");
field.setAccessible(true);
return (List<Scenario>)field.get(testFramework);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
/**
* Also run a simple end-to-end test to sanity check the API method. We capture the stderr to fetch the
* scenario flags.
*/
private static void runEndToEndTest() {
TestFramework testFramework = new TestFramework();
// Capture stderr
PrintStream originalErr = System.err;
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
PrintStream printStream = new PrintStream(outputStream);
System.setErr(printStream);
try {
testFramework
.addCrossProductScenarios(Set.of("-XX:+UseNewCode", "-XX:-UseNewCode"),
Set.of("-XX:+UseNewCode2", "-XX:-UseNewCode2"))
.addFlags()
.start();
shouldHaveThrown();
} catch (TestRunException e) {
// Expected.
System.setErr(originalErr);
Asserts.assertTrue(e.getMessage().contains("The following scenarios have failed: #0, #1, #2, #3."));
String stdErr = outputStream.toString();
Asserts.assertTrue(stdErr.contains("Scenario flags: [-XX:+UseNewCode, -XX:+UseNewCode2]"));
Asserts.assertTrue(stdErr.contains("Scenario flags: [-XX:-UseNewCode, -XX:-UseNewCode2]"));
Asserts.assertTrue(stdErr.contains("Scenario flags: [-XX:+UseNewCode, -XX:-UseNewCode2]"));
Asserts.assertTrue(stdErr.contains("Scenario flags: [-XX:-UseNewCode, -XX:+UseNewCode2]"));
Asserts.assertEQ(4, scenarioCount(stdErr));
}
}
public static int scenarioCount(String stdErr) {
Pattern pattern = Pattern.compile("Scenario flags");
Matcher matcher = pattern.matcher(stdErr);
int count = 0;
while (matcher.find()) {
count++;
}
return count;
}
@Test
@IR(applyIf = {"TLABRefillWasteFraction", "51"}, counts = {IRNode.CALL, "1"})
public void fail1() {
}
@Test
@IR(applyIf = {"TLABRefillWasteFraction", "53"}, counts = {IRNode.CALL, "1"})
public void fail2() {
}
@Test
@IR(applyIfAnd = {"TLABRefillWasteFraction", "50", "UseNewCode", "true"}, counts = {IRNode.CALL, "1"})
public void failPair() {
public void endToEndTest() {
throw new RuntimeException("executed test");
}
}