Rework and prettify CompileKotlinAgainstCustomBinariesTest
Do not use MockLibraryUtil to compile Kotlin code: this allows to avoid rebuilding kotlin-compiler.jar every time before running a test. Also extract a few utilities, use Kotlin stdlib where possible
This commit is contained in:
+98
-107
@@ -21,6 +21,8 @@ import com.intellij.openapi.project.Project;
|
|||||||
import com.intellij.openapi.util.Ref;
|
import com.intellij.openapi.util.Ref;
|
||||||
import com.intellij.openapi.util.io.FileUtil;
|
import com.intellij.openapi.util.io.FileUtil;
|
||||||
import kotlin.Pair;
|
import kotlin.Pair;
|
||||||
|
import kotlin.collections.SetsKt;
|
||||||
|
import kotlin.io.FilesKt;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.kotlin.analyzer.AnalysisResult;
|
import org.jetbrains.kotlin.analyzer.AnalysisResult;
|
||||||
import org.jetbrains.kotlin.cli.AbstractCliTest;
|
import org.jetbrains.kotlin.cli.AbstractCliTest;
|
||||||
@@ -38,19 +40,27 @@ import org.jetbrains.kotlin.load.kotlin.JvmMetadataVersion;
|
|||||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||||
import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil;
|
import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil;
|
||||||
import org.jetbrains.kotlin.test.*;
|
import org.jetbrains.kotlin.test.ConfigurationKind;
|
||||||
|
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||||
|
import org.jetbrains.kotlin.test.TestCaseWithTmpdir;
|
||||||
|
import org.jetbrains.kotlin.test.TestJdkKind;
|
||||||
import org.jetbrains.kotlin.test.util.DescriptorValidator;
|
import org.jetbrains.kotlin.test.util.DescriptorValidator;
|
||||||
import org.jetbrains.kotlin.test.util.RecursiveDescriptorComparator;
|
import org.jetbrains.kotlin.test.util.RecursiveDescriptorComparator;
|
||||||
import org.jetbrains.kotlin.utils.ExceptionUtilsKt;
|
import org.jetbrains.kotlin.utils.ExceptionUtilsKt;
|
||||||
|
import org.jetbrains.kotlin.utils.StringsKt;
|
||||||
import org.jetbrains.org.objectweb.asm.ClassReader;
|
import org.jetbrains.org.objectweb.asm.ClassReader;
|
||||||
import org.jetbrains.org.objectweb.asm.ClassVisitor;
|
import org.jetbrains.org.objectweb.asm.ClassVisitor;
|
||||||
import org.jetbrains.org.objectweb.asm.ClassWriter;
|
import org.jetbrains.org.objectweb.asm.ClassWriter;
|
||||||
import org.jetbrains.org.objectweb.asm.Opcodes;
|
import org.jetbrains.org.objectweb.asm.Opcodes;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.jar.JarEntry;
|
import java.util.jar.JarEntry;
|
||||||
import java.util.jar.JarFile;
|
import java.util.jar.JarFile;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
import java.util.zip.ZipOutputStream;
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
import static org.jetbrains.kotlin.resolve.DescriptorUtils.isObject;
|
import static org.jetbrains.kotlin.resolve.DescriptorUtils.isObject;
|
||||||
@@ -58,6 +68,7 @@ import static org.jetbrains.kotlin.test.util.RecursiveDescriptorComparator.valid
|
|||||||
|
|
||||||
public class CompileKotlinAgainstCustomBinariesTest extends TestCaseWithTmpdir {
|
public class CompileKotlinAgainstCustomBinariesTest extends TestCaseWithTmpdir {
|
||||||
public static final String TEST_DATA_PATH = "compiler/testData/compileKotlinAgainstCustomBinaries/";
|
public static final String TEST_DATA_PATH = "compiler/testData/compileKotlinAgainstCustomBinaries/";
|
||||||
|
private static final Pattern JAVA_FILES = Pattern.compile(".*\\.java$");
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private File getTestDataDirectory() {
|
private File getTestDataDirectory() {
|
||||||
@@ -70,10 +81,10 @@ public class CompileKotlinAgainstCustomBinariesTest extends TestCaseWithTmpdir {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private File compileLibrary(@NotNull String sourcePath, @NotNull String... extraClassPath) {
|
private File compileLibrary(@NotNull String sourcePath, @NotNull File... extraClassPath) {
|
||||||
return MockLibraryUtil.compileLibraryToJar(
|
File result = new File(tmpdir, sourcePath + ".jar");
|
||||||
new File(getTestDataDirectory(), sourcePath).getPath(), "customKotlinLib", false, false, extraClassPath
|
compileKotlin(sourcePath, result, extraClassPath);
|
||||||
);
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -122,9 +133,10 @@ public class CompileKotlinAgainstCustomBinariesTest extends TestCaseWithTmpdir {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private static File copyJarFileWithoutEntry(@NotNull File jarPath, @NotNull String entryToDelete) {
|
private static File copyJarFileWithoutEntry(@NotNull File jarPath, @NotNull String... entriesToDelete) {
|
||||||
try {
|
try {
|
||||||
File outputFile = new File(jarPath.getParentFile(), FileUtil.getNameWithoutExtension(jarPath) + "-after.jar");
|
File outputFile = new File(jarPath.getParentFile(), FileUtil.getNameWithoutExtension(jarPath) + "-after.jar");
|
||||||
|
Set<String> toDelete = SetsKt.setOf(entriesToDelete);
|
||||||
|
|
||||||
@SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
|
@SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
|
||||||
JarFile jar = new JarFile(jarPath);
|
JarFile jar = new JarFile(jarPath);
|
||||||
@@ -132,7 +144,7 @@ public class CompileKotlinAgainstCustomBinariesTest extends TestCaseWithTmpdir {
|
|||||||
try {
|
try {
|
||||||
for (Enumeration<JarEntry> enumeration = jar.entries(); enumeration.hasMoreElements(); ) {
|
for (Enumeration<JarEntry> enumeration = jar.entries(); enumeration.hasMoreElements(); ) {
|
||||||
JarEntry jarEntry = enumeration.nextElement();
|
JarEntry jarEntry = enumeration.nextElement();
|
||||||
if (entryToDelete.equals(jarEntry.getName())) {
|
if (toDelete.contains(jarEntry.getName())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
output.putNextEntry(jarEntry);
|
output.putNextEntry(jarEntry);
|
||||||
@@ -152,6 +164,62 @@ public class CompileKotlinAgainstCustomBinariesTest extends TestCaseWithTmpdir {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private File compileJava(@NotNull String libraryDir) throws Exception {
|
||||||
|
List<File> allJavaFiles = FileUtil.findFilesByMask(JAVA_FILES, new File(getTestDataDirectory(), libraryDir));
|
||||||
|
File result = new File(tmpdir, libraryDir);
|
||||||
|
assert result.mkdirs() : "Could not create directory: " + result;
|
||||||
|
KotlinTestUtils.compileJavaFiles(allJavaFiles, Arrays.asList("-d", result.getPath()));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private static File deletePaths(@NotNull File library, @NotNull String... pathsToDelete) {
|
||||||
|
for (String pathToDelete : pathsToDelete) {
|
||||||
|
File fileToDelete = new File(library, pathToDelete);
|
||||||
|
assert fileToDelete.delete() : "Can't delete " + fileToDelete;
|
||||||
|
}
|
||||||
|
return library;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private Pair<String, ExitCode> compileKotlin(
|
||||||
|
@NotNull String fileName,
|
||||||
|
@NotNull File output,
|
||||||
|
@NotNull File... classpath
|
||||||
|
) {
|
||||||
|
List<String> args = new ArrayList<String>();
|
||||||
|
File sourceFile = new File(getTestDataDirectory(), fileName);
|
||||||
|
assert sourceFile.exists() : "Source file does not exist: " + sourceFile.getAbsolutePath();
|
||||||
|
args.add(sourceFile.getPath());
|
||||||
|
if (classpath.length > 0) {
|
||||||
|
args.add("-classpath");
|
||||||
|
args.add(StringsKt.join(Arrays.asList(classpath), File.pathSeparator));
|
||||||
|
}
|
||||||
|
args.add("-d");
|
||||||
|
args.add(output.getPath());
|
||||||
|
|
||||||
|
return AbstractCliTest.executeCompilerGrabOutput(new K2JVMCompiler(), args);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doTestBrokenJavaLibrary(@NotNull String libraryName, @NotNull String... pathsToDelete) throws Exception {
|
||||||
|
// This function compiles a Java library, then deletes one class file and attempts to compile a Kotlin source against
|
||||||
|
// this broken library. The expected result is an error message from the compiler
|
||||||
|
File library = deletePaths(compileJava(libraryName), pathsToDelete);
|
||||||
|
|
||||||
|
Pair<String, ExitCode> output = compileKotlin("source.kt", tmpdir, library);
|
||||||
|
KotlinTestUtils.assertEqualsToFile(new File(getTestDataDirectory(), "output.txt"), normalizeOutput(output));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doTestBrokenKotlinLibrary(@NotNull String libraryName, @NotNull String... pathsToDelete) throws Exception {
|
||||||
|
// Analogous to doTestBrokenJavaLibrary, but with a Kotlin library compiled to a JAR file
|
||||||
|
File library = copyJarFileWithoutEntry(compileLibrary(libraryName), pathsToDelete);
|
||||||
|
Pair<String, ExitCode> output = compileKotlin("source.kt", tmpdir, library);
|
||||||
|
KotlinTestUtils.assertEqualsToFile(new File(getTestDataDirectory(), "output.txt"), normalizeOutput(output));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------
|
||||||
|
|
||||||
public void testRawTypes() throws Exception {
|
public void testRawTypes() throws Exception {
|
||||||
KotlinTestUtils.compileJavaFiles(
|
KotlinTestUtils.compileJavaFiles(
|
||||||
Collections.singletonList(
|
Collections.singletonList(
|
||||||
@@ -160,21 +228,9 @@ public class CompileKotlinAgainstCustomBinariesTest extends TestCaseWithTmpdir {
|
|||||||
Arrays.asList("-d", tmpdir.getPath())
|
Arrays.asList("-d", tmpdir.getPath())
|
||||||
);
|
);
|
||||||
|
|
||||||
File libSrc = new File(getTestDataDirectory(), "library/test/lib.kt");
|
Pair<String, ExitCode> outputLib = compileKotlin("library/test/lib.kt", tmpdir, tmpdir);
|
||||||
|
|
||||||
Pair<String, ExitCode> outputLib = AbstractCliTest.executeCompilerGrabOutput(new K2JVMCompiler(), Arrays.asList(
|
Pair<String, ExitCode> outputMain = compileKotlin("main.kt", tmpdir, tmpdir);
|
||||||
libSrc.getPath(),
|
|
||||||
"-classpath", tmpdir.getPath(),
|
|
||||||
"-d", tmpdir.getPath()
|
|
||||||
));
|
|
||||||
|
|
||||||
File mainSrc = new File(getTestDataDirectory(), "main.kt");
|
|
||||||
|
|
||||||
Pair<String, ExitCode> outputMain = AbstractCliTest.executeCompilerGrabOutput(new K2JVMCompiler(), Arrays.asList(
|
|
||||||
mainSrc.getPath(),
|
|
||||||
"-classpath", tmpdir.getPath(),
|
|
||||||
"-d", tmpdir.getPath()
|
|
||||||
));
|
|
||||||
|
|
||||||
KotlinTestUtils.assertEqualsToFile(
|
KotlinTestUtils.assertEqualsToFile(
|
||||||
new File(getTestDataDirectory(), "output.txt"), normalizeOutput(outputLib) + "\n" + normalizeOutput(outputMain)
|
new File(getTestDataDirectory(), "output.txt"), normalizeOutput(outputLib) + "\n" + normalizeOutput(outputMain)
|
||||||
@@ -227,107 +283,42 @@ public class CompileKotlinAgainstCustomBinariesTest extends TestCaseWithTmpdir {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testIncompleteHierarchyInJava() throws Exception {
|
public void testIncompleteHierarchyInJava() throws Exception {
|
||||||
// This test compiles a Java library of two classes (Super and Sub), then deletes Super.class and attempts to compile a Kotlin
|
doTestBrokenJavaLibrary("library", "test/Super.class");
|
||||||
// source against this broken library. The expected result is an "incomplete hierarchy" error message from the compiler
|
|
||||||
|
|
||||||
KotlinTestUtils.compileJavaFiles(
|
|
||||||
Arrays.asList(
|
|
||||||
new File(getTestDataDirectory() + "/library/test/Super.java"),
|
|
||||||
new File(getTestDataDirectory() + "/library/test/Sub.java")
|
|
||||||
),
|
|
||||||
Arrays.asList("-d", tmpdir.getPath())
|
|
||||||
);
|
|
||||||
|
|
||||||
File superClassFile = new File(tmpdir + "/test/Super.class");
|
|
||||||
assert superClassFile.delete() : "Can't delete " + superClassFile;
|
|
||||||
|
|
||||||
File source = new File(getTestDataDirectory(), "source.kt");
|
|
||||||
|
|
||||||
Pair<String, ExitCode> output = AbstractCliTest.executeCompilerGrabOutput(new K2JVMCompiler(), Arrays.asList(
|
|
||||||
source.getPath(),
|
|
||||||
"-classpath", tmpdir.getPath(),
|
|
||||||
"-d", tmpdir.getPath()
|
|
||||||
));
|
|
||||||
|
|
||||||
KotlinTestUtils.assertEqualsToFile(new File(getTestDataDirectory(), "output.txt"), normalizeOutput(output));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testIncompleteHierarchyInKotlin() throws Exception {
|
public void testIncompleteHierarchyInKotlin() throws Exception {
|
||||||
// Analogous to testIncompleteHierarchyInJava, but with a Kotlin library
|
doTestBrokenKotlinLibrary("library", "test/Super.class");
|
||||||
|
|
||||||
File library = copyJarFileWithoutEntry(compileLibrary("library"), "test/Super.class");
|
|
||||||
|
|
||||||
File source = new File(getTestDataDirectory(), "source.kt");
|
|
||||||
|
|
||||||
Pair<String, ExitCode> output = AbstractCliTest.executeCompilerGrabOutput(new K2JVMCompiler(), Arrays.asList(
|
|
||||||
source.getPath(),
|
|
||||||
"-classpath", library.getPath(),
|
|
||||||
"-d", tmpdir.getPath()
|
|
||||||
));
|
|
||||||
|
|
||||||
KotlinTestUtils.assertEqualsToFile(new File(getTestDataDirectory(), "output.txt"), normalizeOutput(output));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*test source mapping generation when source info is absent*/
|
/*test source mapping generation when source info is absent*/
|
||||||
public void testInlineFunWithoutDebugInfo() throws Exception {
|
public void testInlineFunWithoutDebugInfo() throws Exception {
|
||||||
File inlineSource = new File(getTestDataDirectory(), "sourceInline.kt");
|
compileKotlin("sourceInline.kt", tmpdir);
|
||||||
|
|
||||||
AbstractCliTest.executeCompilerGrabOutput(new K2JVMCompiler(), Arrays.asList(
|
|
||||||
inlineSource.getPath(),
|
|
||||||
"-d", tmpdir.getPath()
|
|
||||||
));
|
|
||||||
|
|
||||||
ClassWriter cw;
|
|
||||||
File inlineFunClass = new File(tmpdir.getAbsolutePath(), "test/A.class");
|
File inlineFunClass = new File(tmpdir.getAbsolutePath(), "test/A.class");
|
||||||
FileInputStream is = new FileInputStream(inlineFunClass);
|
ClassWriter cw = new ClassWriter(Opcodes.ASM5);
|
||||||
try {
|
new ClassReader(FilesKt.readBytes(inlineFunClass)).accept(new ClassVisitor(Opcodes.ASM5, cw) {
|
||||||
ClassReader reader = new ClassReader(is);
|
@Override
|
||||||
cw = new ClassWriter(Opcodes.ASM5);
|
public void visitSource(String source, String debug) {
|
||||||
reader.accept(new ClassVisitor(Opcodes.ASM5, cw) {
|
//skip debug info
|
||||||
@Override
|
}
|
||||||
public void visitSource(String source, String debug) {
|
}, 0);
|
||||||
//skip debug info
|
|
||||||
}
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
is.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
assert inlineFunClass.delete();
|
assert inlineFunClass.delete();
|
||||||
assert !inlineFunClass.exists();
|
assert !inlineFunClass.exists();
|
||||||
|
|
||||||
FileOutputStream stream = new FileOutputStream(inlineFunClass);
|
FilesKt.writeBytes(inlineFunClass, cw.toByteArray());
|
||||||
try {
|
|
||||||
stream.write(cw.toByteArray());
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
stream.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
File resultSource = new File(getTestDataDirectory(), "source.kt");
|
compileKotlin("source.kt", tmpdir, tmpdir);
|
||||||
AbstractCliTest.executeCompilerGrabOutput(new K2JVMCompiler(), Arrays.asList(
|
|
||||||
resultSource.getPath(),
|
|
||||||
"-classpath", tmpdir.getPath(),
|
|
||||||
"-d", tmpdir.getPath()
|
|
||||||
));
|
|
||||||
|
|
||||||
final Ref<String> debugInfo = new Ref<String>();
|
final Ref<String> debugInfo = new Ref<String>();
|
||||||
File resultFile = new File(tmpdir.getAbsolutePath(), "test/B.class");
|
File resultFile = new File(tmpdir.getAbsolutePath(), "test/B.class");
|
||||||
FileInputStream resultStream = new FileInputStream(resultFile);
|
new ClassReader(FilesKt.readBytes(resultFile)).accept(new ClassVisitor(Opcodes.ASM5) {
|
||||||
try {
|
@Override
|
||||||
ClassReader reader = new ClassReader(resultStream);
|
public void visitSource(String source, String debug) {
|
||||||
reader.accept(new ClassVisitor(Opcodes.ASM5) {
|
//skip debug info
|
||||||
@Override
|
debugInfo.set(debug);
|
||||||
public void visitSource(String source, String debug) {
|
}
|
||||||
//skip debug info
|
}, 0);
|
||||||
debugInfo.set(debug);
|
|
||||||
}
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
resultStream.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
String expected = "SMAP\n" +
|
String expected = "SMAP\n" +
|
||||||
"source.kt\n" +
|
"source.kt\n" +
|
||||||
@@ -350,7 +341,7 @@ public class CompileKotlinAgainstCustomBinariesTest extends TestCaseWithTmpdir {
|
|||||||
|
|
||||||
public void testReplaceAnnotationClassWithInterface() throws Exception {
|
public void testReplaceAnnotationClassWithInterface() throws Exception {
|
||||||
File library1 = compileLibrary("library-1");
|
File library1 = compileLibrary("library-1");
|
||||||
File usage = compileLibrary("usage", library1.getPath());
|
File usage = compileLibrary("usage", library1);
|
||||||
File library2 = compileLibrary("library-2");
|
File library2 = compileLibrary("library-2");
|
||||||
doTestWithTxt(usage, library2);
|
doTestWithTxt(usage, library2);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user