diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/TestFrameworkClass.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/TestFrameworkClass.java new file mode 100644 index 00000000000..5194b75af43 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/TestFrameworkClass.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2025, 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 compiler.lib.template_framework.library; + +import java.util.List; +import java.util.Set; + +import compiler.lib.ir_framework.TestFramework; +import compiler.lib.compile_framework.CompileFramework; +import compiler.lib.template_framework.Template; +import compiler.lib.template_framework.TemplateToken; +import static compiler.lib.template_framework.Template.body; +import static compiler.lib.template_framework.Template.let; + +/** + * This class provides a {@link #render} method that can be used to simplify generating + * source code when using the {@link TestFramework} (also known as IR Framework) to run + * a list of tests. + * + *
+ * The idea is that the user only has to generate the code for the individual tests, + * and can then pass the corresponding list of {@link TemplateToken}s to this + * provided {@link #render} method which generates the surrounding class and the main + * method that invokes the {@link TestFramework}, so that all the generated tests + * are run. + */ +public final class TestFrameworkClass { + + // Ensure there can be no instance, and we do not have to document the constructor. + private TestFrameworkClass() {} + + /** + * This method renders a list of {@code testTemplateTokens} into the body of a class + * and generates a {@code main} method which launches the {@link TestFramework} + * to run the generated tests. + * + *
+ * The generated {@code main} method is to be invoked with a {@code vmFlags} argument, + * which must be a {@link String[]}, specifying the VM flags for the Test VM, in which + * the tests will be run. Thus, one can generate the test class once, and invoke its + * {@code main} method multiple times, each time with a different set of VM flags. + * + *
+ * The internal {@link Template} sets the {@link Hooks#CLASS_HOOK} for the scope of
+ * all test methods.
+ *
+ * @param packageName The package name of the test class.
+ * @param className The name of the test class.
+ * @param imports A set of imports.
+ * @param classpath The classpath from {@link CompileFramework#getEscapedClassPathOfCompiledClasses},
+ * so that the Test VM has access to the class files that are compiled from the
+ * generated source code.
+ * @param testTemplateTokens The list of tests to be generated into the test class.
+ * Every test must be annotated with {@code @Test}, so that
+ * the {@link TestFramework} can later find and run them.
+ * @return The generated source code of the test class as a {@link String}.
+ */
+ public static String render(final String packageName,
+ final String className,
+ final Set
+ * The "@compile" command for JTREG is required so that the frameworks used in the Template code
+ * are compiled and available for the Test-VM.
+ *
+ * Additionally, we must set the classpath for the Test VM, so that it has access to all compiled
+ * classes (see {@link CompileFramework#getEscapedClassPathOfCompiledClasses}).
+ */
+public class TestWithTestFrameworkClass {
+
+ public static void main(String[] args) {
+ // Create a new CompileFramework instance.
+ CompileFramework comp = new CompileFramework();
+
+ // Add a java source file.
+ comp.addJavaSourceCode("p.xyz.InnerTest", generate(comp));
+
+ // Compile the source file.
+ comp.compile();
+
+ // p.xyz.InnterTest.main(new String[] {});
+ comp.invoke("p.xyz.InnerTest", "main", new Object[] {new String[] {}});
+
+ // We can also pass VM flags for the Test VM.
+ // p.xyz.InnterTest.main(new String[] {"-Xbatch"});
+ comp.invoke("p.xyz.InnerTest", "main", new Object[] {new String[] {"-Xbatch"}});
+ }
+
+ // Generate a source Java file as String
+ public static String generate(CompileFramework comp) {
+ // A simple template that adds a comment.
+ var commentTemplate = Template.make(() -> body(
+ """
+ // Comment inserted from test method to class hook.
+ """
+ ));
+
+ // We define a Test-Template:
+ // - static fields for inputs: INPUT_A and INPUT_B
+ // - Data generated with Generators and hashtag replacement #con1.
+ // - GOLD value precomputed with dedicated call to test.
+ // - This ensures that the GOLD value is computed in the interpreter
+ // most likely, since the test method is not yet compiled.
+ // This allows us later to compare to the results of the compiled
+ // code.
+ // The input data is cloned, so that the original INPUT_A is never
+ // modified and can serve as identical input in later calls to test.
+ // - In the Setup method, we clone the input data, since the input data
+ // could be modified inside the test method.
+ // - The test method makes use of hashtag replacements (#con2 and #op).
+ // - The Check method verifies the results of the test method with the
+ // GOLD value.
+ var testTemplate = Template.make("op", (String op) -> body(
+ let("size", Generators.G.safeRestrict(Generators.G.ints(), 10_000, 20_000).next()),
+ let("con1", Generators.G.ints().next()),
+ let("con2", Generators.G.safeRestrict(Generators.G.ints(), 1, Integer.MAX_VALUE).next()),
+ """
+ // --- $test start ---
+ // $test with size=#size and op=#op
+ private static int[] $INPUT_A = new int[#size];
+ static {
+ Generators.G.fill(Generators.G.ints(), $INPUT_A);
+ }
+ private static int $INPUT_B = #con1;
+ private static Object $GOLD = $test($INPUT_A.clone(), $INPUT_B);
+
+ @Setup
+ public static Object[] $setup() {
+ // Must make sure to clone input arrays, if it is mutated in the test.
+ return new Object[] {$INPUT_A.clone(), $INPUT_B};
+ }
+
+ @Test
+ @Arguments(setup = "$setup")
+ public static Object $test(int[] a, int b) {
+ for (int i = 0; i < a.length; i++) {
+ int con = #con2;
+ a[i] = (a[i] * con) #op b;
+ }
+ return a;
+ }
+
+ @Check(test = "$test")
+ public static void $check(Object result) {
+ Verify.checkEQ(result, $GOLD);
+ }
+ // --- $test end ---
+ """,
+ // Good to know: we can insert to the class hook, which is set for the
+ // TestFrameworkClass scope:
+ Hooks.CLASS_HOOK.insert(commentTemplate.asToken())
+ ));
+
+ // Create a test for each operator.
+ List