/* * Copyright (c) 2023, 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. */ import java.nio.file.Files; import java.nio.file.Path; import java.util.List; import java.util.Objects; import java.util.function.Consumer; /** * @test * @bug 8329420 * @summary test execution priority and behavior of main methods * @run main InstanceMainTest */ public class InstanceMainTest extends TestHelper { private static final String[] SOURCES = new String[] { // static dominating with args """ class MainClass { static void main() { throw new AssertionError(); } static void main(String[] args) { } } """, // instance dominating static """ class MainClass { void main(String[] args) { } static void main() { throw new AssertionError(); } } """, // instance dominating with args """ class MainClass { void main() { throw new AssertionError(); } void main(String[] args) { } } """, // instance no args """ class MainClass { void main() { } } """, // unnamed class static dominating with args """ static void main() { throw new AssertionError(); } static void main(String[] args) { } """, // main with args dominating main without args """ void main(String[] args) { } static void main() { throw new AssertionError(); } """, // unnamed class instance dominating with args """ void main() { throw new AssertionError(); } void main(String[] args) { } """, // unnamed class instance main no args """ void main() { } """, // instance main with args dominating super """ class MainClass extends SuperClass { void main() { throw new AssertionError(); } } class SuperClass { void main(String[] args) { } } """, // super instance main with args dominating """ public class MainClass extends Super { } class Super { public void main(String... args) { } public void main() { throw new AssertionError(); } } """, // ignore super instance main """ public class MainClass extends Super { public static void main(String... args) { } } class Super { public static void main(String... args) { throw new AssertionError(); } } """, // enum main """ enum MainClass { A; public static void main() { } } """, // record main """ record MainClass() { static void main() { System.out.println("Done!"); } } """, // interface main """ interface MainClass { static void main() { System.out.println("Done!"); } } """ }; private static void testMethodOrder() throws Exception { for (String source : SOURCES) { performTest(source, true, tr -> { if (!tr.isOK()) { System.err.println(source); System.err.println(tr); throw new AssertionError(); } }); } } record TestCase(String sourceCode, boolean enablePreview, List expectedOutput) { public TestCase(String sourceCode, List expectedOutput) { this(sourceCode, true, expectedOutput); } } private static final TestCase[] EXECUTION_ORDER = new TestCase[] { new TestCase(""" public class MainClass { public MainClass() { System.out.println("Constructor called!"); } public static void main() { System.out.println("main called!"); } } """, List.of("main called!")), new TestCase(""" public class MainClass { public MainClass() { System.out.println("Constructor called!"); } public void main() { System.out.println("main called!"); } } """, List.of("Constructor called!", "main called!")) }; private static void testExecutionOrder() throws Exception { for (TestCase testCase : EXECUTION_ORDER) { performTest(testCase.sourceCode, testCase.enablePreview(), tr -> { if (!Objects.equals(testCase.expectedOutput, tr.testOutput)) { throw new AssertionError("Unexpected output, " + "expected: " + testCase.expectedOutput + ", actual: " + tr.testOutput); } }); } } private static final TestCase[] EXECUTION_ERRORS = new TestCase[] { new TestCase(""" public class MainClass { public MainClass() { System.out.println("Constructor called!"); if (true) throw new Error(); } public void main(String... args) { System.out.println("main called!"); } } """, List.of("Constructor called!", "Exception in thread \"main\" java.lang.Error", "\tat MainClass.(MainClass.java:4)")), new TestCase(""" public class MainClass { public MainClass() { System.out.println("Constructor called!"); if (true) throw new Error(); } public void main() { System.out.println("main called!"); } } """, List.of("Constructor called!", "Exception in thread \"main\" java.lang.Error", "\tat MainClass.(MainClass.java:4)")), new TestCase(""" public class MainClass { static int idx; public MainClass() { System.out.println("Constructor called!"); if (idx++ == 0) throw new Error(); } public void main(String... args) { System.out.println("main called!"); } public void main() { System.out.println("main called!"); } } """, List.of("Constructor called!", "Exception in thread \"main\" java.lang.Error", "\tat MainClass.(MainClass.java:5)")), new TestCase(""" public class MainClass { static { System.out.println("static init called!"); if (true) throw new Error(); } public static void main(String... args) { System.out.println("main called!"); } } """, false, List.of("static init called!", "Exception in thread \"main\" java.lang.Error", "\tat MainClass.(MainClass.java:4)")), new TestCase(""" public class MainClass { static { System.out.println("static init called!"); if (true) throw new Error(); } public static void main(String... args) { System.out.println("main called!"); } } """, true, List.of("static init called!", "Exception in thread \"main\" java.lang.Error", "\tat MainClass.(MainClass.java:4)")), new TestCase(""" public class MainClass { static { System.out.println("static init called!"); if (true) throw new Error(); } public void main(String... args) { System.out.println("main called!"); } } """, true, List.of("static init called!", "Exception in thread \"main\" java.lang.Error", "\tat MainClass.(MainClass.java:4)")), }; private static void testExecutionErrors() throws Exception { for (TestCase testCase : EXECUTION_ERRORS) { performTest(testCase.sourceCode, testCase.enablePreview(), tr -> { for (int i = 0; i < testCase.expectedOutput.size(); i++) { if (i >= tr.testOutput.size() || !Objects.equals(testCase.expectedOutput.get(i), tr.testOutput.get(i))) { throw new AssertionError("Unexpected output, " + "expected: " + testCase.expectedOutput + ", actual: " + tr.testOutput + ", failed comparison at index: " + i); } } }); } } private static void performTest(String source, boolean enablePreview, Consumer validator) throws Exception { Path mainClass = Path.of("MainClass.java"); Files.writeString(mainClass, source); var version = System.getProperty("java.specification.version"); var previewRuntime = enablePreview ? "--enable-preview" : "-DtestNoPreview"; var previewCompile = enablePreview ? "--enable-preview" : "-XDtestNoPreview"; var trSource = doExec(javaCmd, previewRuntime, "--source", version, "MainClass.java"); validator.accept(trSource); compile(previewCompile, "--source", version, "MainClass.java"); String cp = mainClass.toAbsolutePath().getParent().toString(); var trCompile = doExec(javaCmd, previewRuntime, "--class-path", cp, "MainClass"); validator.accept(trCompile); } public static void main(String... args) throws Exception { testMethodOrder(); testExecutionOrder(); testExecutionErrors(); } }