jdk-24/test/langtools/tools/javac/launcher/MultiFileSourceLauncherTests.java
2024-05-02 11:13:41 +00:00

337 lines
12 KiB
Java

/*
* Copyright (c) 2023, 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.
*/
/*
* @test
* @bug 8304400
* @summary Test source launcher running Java programs spanning multiple files
* @modules jdk.compiler/com.sun.tools.javac.launcher
* @run junit MultiFileSourceLauncherTests
*/
import static org.junit.jupiter.api.Assertions.*;
import com.sun.tools.javac.launcher.Fault;
import java.nio.file.*;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.io.*;
class MultiFileSourceLauncherTests {
@Test
void testHelloWorldInTwoCompilationUnits(@TempDir Path base) throws Exception {
var hello = Files.writeString(base.resolve("Hello.java"),
"""
public class Hello {
public static void main(String... args) {
System.out.println("Hello " + new World("Terra"));
System.out.println(Hello.class.getResource("Hello.java"));
System.out.println(Hello.class.getResource("World.java"));
}
}
""");
Files.writeString(base.resolve("World.java"),
"""
record World(String name) {}
""");
var run = Run.of(hello);
assertLinesMatch(
"""
Hello World[name=Terra]
\\Qfile:\\E.+\\QHello.java\\E
\\Qfile:\\E.+\\QWorld.java\\E
""".lines(),
run.stdOut().lines());
assertTrue(run.stdErr().isEmpty(), run.toString());
assertNull(run.exception(), run.toString());
}
@Test
void testLoadingOfEnclosedTypes(@TempDir Path base) throws Exception {
var hello = Files.writeString(base.resolve("Hello.java"),
"""
public class Hello {
public static void main(String... args) throws Exception {
System.out.println(Class.forName("World$Core"));
System.out.println(Class.forName("p.q.Unit$Fir$t"));
System.out.println(Class.forName("p.q.Unit$123$Fir$t$$econd"));
System.out.println(Class.forName("$.$.$"));
}
}
""");
Files.writeString(base.resolve("World.java"),
"""
record World(String name) {
record Core() {}
}
""");
var pq = Files.createDirectories(base.resolve("p/q"));
Files.writeString(pq.resolve("Unit.java"),
"""
package p.q;
record Unit() {
record Fir$t() {
record $econd() {}
}
}
""");
Files.writeString(pq.resolve("Unit$123.java"),
"""
package p.q;
record Unit$123() {
record Fir$t() {
record $econd() {}
}
}
""");
var $$ = Files.createDirectories(base.resolve("$/$"));
Files.writeString($$.resolve("$.java"),
"""
package $.$;
record $($ $) {}
""");
var run = Run.of(hello);
assertAll("Run -> " + run,
() -> assertLinesMatch(
"""
class World$Core
class p.q.Unit$Fir$t
class p.q.Unit$123$Fir$t$$econd
class $.$.$
""".lines(),
run.stdOut().lines()),
() -> assertTrue(run.stdErr().isEmpty()),
() -> assertNull(run.exception())
);
}
@Test
void testMultiplePackages(@TempDir Path base) throws Exception {
var packageA = Files.createDirectories(base.resolve("a"));
var hello = Files.writeString(packageA.resolve("Hello.java"),
"""
package a;
import b.World;
public class Hello {
public static void main(String... args) {
System.out.println("Hello " + new World("in package b"));
}
}
""");
var packageB = Files.createDirectories(base.resolve("b"));
Files.writeString(packageB.resolve("World.java"),
"""
package b;
public record World(String name) {}
""");
var run = Run.of(hello);
assertLinesMatch(
"""
Hello World[name=in package b]
""".lines(),
run.stdOut().lines());
assertTrue(run.stdErr().isEmpty(), run.toString());
assertNull(run.exception(), run.toString());
}
@Test
void testMissingSecondUnit(@TempDir Path base) throws Exception {
var program = Files.writeString(base.resolve("Program.java"),
"""
public class Program {
public static void main(String... args) {
System.out.println("Hello " + new MissingSecondUnit());
}
}
""");
var run = Run.of(program);
assertTrue(run.stdOut().isEmpty(), run.toString());
assertLinesMatch(
"""
%s:3: error: cannot find symbol
System.out.println("Hello " + new MissingSecondUnit());
^
symbol: class MissingSecondUnit
location: class Program
1 error
""".formatted(program.toString())
.lines(),
run.stdErr().lines(),
run.toString());
assertTrue(run.exception() instanceof Fault);
}
@Test
void testSecondUnitWithSyntaxError(@TempDir Path base) throws Exception {
var program = Files.writeString(base.resolve("Program.java"),
"""
public class Program {
public static void main(String... args) {
System.out.println("Hello " + new BrokenSecondUnit());
}
}
""");
var broken = Files.writeString(base.resolve("BrokenSecondUnit.java"),
"""
record BrokenSecondUnit {}
""");
var run = Run.of(program);
assertTrue(run.stdOut().isEmpty(), run.toString());
assertLinesMatch(
"""
%s:1: error: '(' expected
>> MORE LINES >>
""".formatted(broken.toString())
.lines(),
run.stdErr().lines(),
run.toString());
assertTrue(run.exception() instanceof Fault);
}
@Test
void onlyJavaFilesReferencedByTheProgramAreCompiled(@TempDir Path base) throws Exception {
var prog = Files.writeString(base.resolve("Prog.java"),
"""
class Prog {
public static void main(String... args) {
Helper.run();
}
}
""");
Files.writeString(base.resolve("Helper.java"),
"""
class Helper {
static void run() {
System.out.println("Hello!");
}
}
""");
var old = Files.writeString(base.resolve("OldProg.java"),
"""
class OldProg {
public static void main(String... args) {
Helper.run()
}
}
""");
var run = Run.of(prog);
assertAll("Run := " + run,
() -> assertLinesMatch(
"""
Hello!
""".lines(), run.stdOut().lines()),
() -> assertTrue(run.stdErr().isEmpty()),
() -> assertNull(run.exception()));
var fail = Run.of(old);
assertAll("Run := " + fail,
() -> assertTrue(fail.stdOut().isEmpty()),
() -> assertLinesMatch(
"""
%s:3: error: ';' expected
Helper.run()
^
1 error
""".formatted(old).lines(), fail.stdErr().lines()),
() -> assertNotNull(fail.exception()));
}
@Test
void classesDeclaredInSameFileArePreferredToClassesInOtherFiles(@TempDir Path base) throws Exception {
var prog = Files.writeString(base.resolve("Prog.java"),
"""
class Helper {
static void run() {
System.out.println("Same file.");
}
}
public class Prog {
public static void main(String... args) {
Helper.run();
}
}
""");
Files.writeString(base.resolve("Helper.java"),
"""
class Helper {
static void run() {
System.out.println("Other file.");
}
}
""");
var run = Run.of(prog);
assertAll("Run := " + run,
() -> assertLinesMatch(
"""
Same file.
""".lines(), run.stdOut().lines()),
() -> assertTrue(run.stdErr().isEmpty()),
() -> assertNull(run.exception()));
}
@Test
void duplicateDeclarationOfClassFails(@TempDir Path base) throws Exception {
var prog = Files.writeString(base.resolve("Prog.java"),
"""
class Prog {
public static void main(String... args) {
Helper.run();
Aux.cleanup();
}
}
class Aux {
static void cleanup() {}
}
""");
var helper = Files.writeString(base.resolve("Helper.java"),
"""
class Helper {
static void run() {}
}
class Aux {
static void cleanup() {}
}
""");
var fail = Run.of(prog);
assertAll("Run := " + fail,
() -> assertTrue(fail.stdOut().isEmpty()),
() -> assertLinesMatch(
"""
%s:4: error: duplicate class: Aux
class Aux {
^
1 error
""".formatted(helper).lines(), fail.stdErr().lines()),
() -> assertNotNull(fail.exception()));
}
}