Extract tests-common module without any actual tests
The main reasoning for the module is to avoid running any compiler tests while executing run configuration that searches tests across module dependencies.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.asJava;
|
||||
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collections;
|
||||
@@ -53,12 +54,12 @@ public class JavaElementFinderTest extends KotlinAsJavaTestBase {
|
||||
|
||||
private void assertClass(String qualifiedName) {
|
||||
PsiClass psiClass = finder.findClass(qualifiedName, GlobalSearchScope.allScope(getProject()));
|
||||
assertNotNull(String.format("Class with fqn='%s' wasn't found.", qualifiedName), psiClass);
|
||||
assertTrue(String.format("Class with fqn='%s' is not valid.", qualifiedName), psiClass.isValid());
|
||||
TestCase.assertNotNull(String.format("Class with fqn='%s' wasn't found.", qualifiedName), psiClass);
|
||||
TestCase.assertTrue(String.format("Class with fqn='%s' is not valid.", qualifiedName), psiClass.isValid());
|
||||
}
|
||||
|
||||
private void assertNoClass(String qualifiedName) {
|
||||
assertNull(String.format("Class with fqn='%s' isn't expected to be found.", qualifiedName),
|
||||
finder.findClass(qualifiedName, GlobalSearchScope.allScope(getProject())));
|
||||
TestCase.assertNull(String.format("Class with fqn='%s' isn't expected to be found.", qualifiedName),
|
||||
finder.findClass(qualifiedName, GlobalSearchScope.allScope(getProject())));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.asJava
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.psi.PsiClass
|
||||
import com.intellij.psi.impl.compiled.ClsElementImpl
|
||||
import junit.framework.TestCase
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import java.io.File
|
||||
import java.util.regex.Pattern
|
||||
|
||||
object LightClassTestCommon {
|
||||
private val SUBJECT_FQ_NAME_PATTERN = Pattern.compile("^//\\s*(.*)$", Pattern.MULTILINE)
|
||||
|
||||
@JvmOverloads
|
||||
fun testLightClass(
|
||||
testDataFile: File,
|
||||
findLightClass: (String) -> PsiClass?,
|
||||
normalizeText: (String) -> String = { it }
|
||||
) {
|
||||
val text = FileUtil.loadFile(testDataFile, true)
|
||||
val matcher = SUBJECT_FQ_NAME_PATTERN.matcher(text)
|
||||
TestCase.assertTrue("No FqName specified. First line of the form '// f.q.Name' expected", matcher.find())
|
||||
val fqName = matcher.group(1)
|
||||
|
||||
val lightClass = findLightClass(fqName)
|
||||
|
||||
val actual = actualText(fqName, lightClass, normalizeText)
|
||||
KotlinTestUtils.assertEqualsToFile(KotlinTestUtils.replaceExtension(testDataFile, "java"), actual)
|
||||
}
|
||||
|
||||
private fun actualText(fqName: String?, lightClass: PsiClass?, normalizeText: (String) -> String): String {
|
||||
if (lightClass == null) {
|
||||
return "<not generated>"
|
||||
}
|
||||
TestCase.assertTrue("Not a light class: $lightClass ($fqName)", lightClass is KtLightClass)
|
||||
|
||||
val delegate = (lightClass as KtLightClass).clsDelegate
|
||||
TestCase.assertTrue("Not a CLS element: $delegate", delegate is ClsElementImpl)
|
||||
|
||||
val buffer = StringBuilder()
|
||||
(delegate as ClsElementImpl).appendMirrorText(0, buffer)
|
||||
val actual = normalizeText(buffer.toString())
|
||||
return actual
|
||||
}
|
||||
|
||||
// Actual text for light class is generated with ClsElementImpl.appendMirrorText() that can find empty DefaultImpl inner class in stubs
|
||||
// for all interfaces. This inner class can't be used in Java as it generally is not seen from light classes built from Kotlin sources.
|
||||
// It is also omitted during classes generation in backend so it also absent in light classes built from compiled code.
|
||||
fun removeEmptyDefaultImpls(text: String) : String = text.replace("\n final class DefaultImpls {\n }\n", "")
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.cfg
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.PseudoValue
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.PseudocodeImpl
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.TypePredicate
|
||||
|
||||
@@ -271,7 +271,7 @@ public abstract class BaseDiagnosticsTest
|
||||
this.expectedText = textWithMarkers;
|
||||
String textWithExtras = addExtras(expectedText);
|
||||
this.clearText = CheckerTestUtil.parseDiagnosedRanges(textWithExtras, diagnosedRanges);
|
||||
this.jetFile = CheckerTestUtilTest.createCheckAndReturnPsiFile(fileName, clearText, getProject());
|
||||
this.jetFile = TestCheckerUtil.createCheckAndReturnPsiFile(fileName, clearText, getProject());
|
||||
for (CheckerTestUtil.DiagnosedRange diagnosedRange : diagnosedRanges) {
|
||||
diagnosedRange.setFile(jetFile);
|
||||
}
|
||||
|
||||
@@ -17,13 +17,8 @@
|
||||
package org.jetbrains.kotlin.checkers;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.intellij.openapi.fileEditor.impl.LoadTextUtil;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiElementVisitor;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.testFramework.LightVirtualFile;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.checkers.CheckerTestUtil.DiagnosedRange;
|
||||
@@ -53,28 +48,7 @@ public class CheckerTestUtilTest extends KotlinTestWithEnvironment {
|
||||
|
||||
protected void doTest(TheTest theTest) throws Exception {
|
||||
String text = KotlinTestUtils.doLoadFile(getTestDataPath(), "test.kt");
|
||||
theTest.test(createCheckAndReturnPsiFile("test.kt", text, getProject()));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static KtFile createCheckAndReturnPsiFile(@NotNull String fileName, @NotNull String text, @NotNull Project project) {
|
||||
KtFile myFile = KotlinTestUtils.createFile(fileName, text, project);
|
||||
ensureParsed(myFile);
|
||||
assertEquals("light virtual file text mismatch", text, ((LightVirtualFile) myFile.getVirtualFile()).getContent().toString());
|
||||
assertEquals("virtual file text mismatch", text, LoadTextUtil.loadText(myFile.getVirtualFile()));
|
||||
//noinspection ConstantConditions
|
||||
assertEquals("doc text mismatch", text, myFile.getViewProvider().getDocument().getText());
|
||||
assertEquals("psi text mismatch", text, myFile.getText());
|
||||
return myFile;
|
||||
}
|
||||
|
||||
private static void ensureParsed(PsiFile file) {
|
||||
file.accept(new PsiElementVisitor() {
|
||||
@Override
|
||||
public void visitElement(@NotNull PsiElement element) {
|
||||
element.acceptChildren(this);
|
||||
}
|
||||
});
|
||||
theTest.test(TestCheckerUtil.createCheckAndReturnPsiFile("test.kt", text, getProject()));
|
||||
}
|
||||
|
||||
public void testEquals() throws Exception {
|
||||
|
||||
@@ -1,194 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen;
|
||||
|
||||
import com.intellij.ide.highlighter.JavaFileType;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.util.Processor;
|
||||
import kotlin.collections.ArraysKt;
|
||||
import kotlin.io.FilesKt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.cli.common.output.outputUtils.OutputUtilsKt;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.JvmPackagePartProvider;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
|
||||
import org.jetbrains.kotlin.cli.jvm.config.JvmContentRootsKt;
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration;
|
||||
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil;
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration;
|
||||
import org.jetbrains.kotlin.psi.KtFile;
|
||||
import org.jetbrains.kotlin.psi.KtNamedFunction;
|
||||
import org.jetbrains.kotlin.psi.KtProperty;
|
||||
import org.jetbrains.kotlin.test.ConfigurationKind;
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.test.TestJdkKind;
|
||||
import org.jetbrains.kotlin.utils.ExceptionUtilsKt;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.jetbrains.kotlin.test.KotlinTestUtils.getAnnotationsJar;
|
||||
|
||||
public abstract class AbstractBlackBoxCodegenTest extends CodegenTestCase {
|
||||
private boolean addRuntime = false;
|
||||
private boolean addReflect = false;
|
||||
|
||||
@Override
|
||||
protected void doMultiFileTest(@NotNull File wholeFile, @NotNull List<TestFile> files, @Nullable File javaFilesDir) throws Exception {
|
||||
TestJdkKind jdkKind = getJdkKind(files);
|
||||
|
||||
List<String> javacOptions = new ArrayList<String>(0);
|
||||
for (TestFile file : files) {
|
||||
if (InTextDirectivesUtils.isDirectiveDefined(file.content, "WITH_RUNTIME")) {
|
||||
addRuntime = true;
|
||||
}
|
||||
if (InTextDirectivesUtils.isDirectiveDefined(file.content, "WITH_REFLECT")) {
|
||||
addReflect = true;
|
||||
}
|
||||
|
||||
javacOptions.addAll(InTextDirectivesUtils.findListWithPrefixes(file.content, "// JAVAC_OPTIONS:"));
|
||||
}
|
||||
|
||||
configurationKind =
|
||||
addReflect ? ConfigurationKind.ALL :
|
||||
addRuntime ? ConfigurationKind.NO_KOTLIN_REFLECT :
|
||||
ConfigurationKind.JDK_ONLY;
|
||||
|
||||
compileAndRun(files, javaFilesDir, jdkKind, javacOptions);
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
protected void compileAndRun(
|
||||
@NotNull List<TestFile> files,
|
||||
@Nullable File javaSourceDir,
|
||||
@NotNull TestJdkKind jdkKind,
|
||||
@NotNull List<String> javacOptions
|
||||
) {
|
||||
CompilerConfiguration configuration = createCompilerConfigurationForTests(
|
||||
configurationKind, jdkKind,
|
||||
Collections.singletonList(getAnnotationsJar()),
|
||||
ArraysKt.filterNotNull(new File[] {javaSourceDir}),
|
||||
files
|
||||
);
|
||||
|
||||
myEnvironment = KotlinCoreEnvironment.createForTests(
|
||||
getTestRootDisposable(), configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES
|
||||
);
|
||||
|
||||
loadMultiFiles(files);
|
||||
|
||||
classFileFactory = GenerationUtils.compileManyFilesGetGenerationStateForTest(
|
||||
myEnvironment.getProject(), myFiles.getPsiFiles(), new JvmPackagePartProvider(myEnvironment),
|
||||
myEnvironment.getConfiguration()
|
||||
).getFactory();
|
||||
|
||||
if (javaSourceDir != null) {
|
||||
// If there are Java files, they should be compiled against the class files produced by Kotlin, so we dump them to the disk
|
||||
File kotlinOut;
|
||||
try {
|
||||
kotlinOut = KotlinTestUtils.tmpDir(toString());
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw ExceptionUtilsKt.rethrow(e);
|
||||
}
|
||||
|
||||
OutputUtilsKt.writeAllTo(classFileFactory, kotlinOut);
|
||||
|
||||
File output = CodegenTestUtil.compileJava(
|
||||
findJavaSourcesInDirectory(javaSourceDir), Collections.singletonList(kotlinOut.getPath()), javacOptions
|
||||
);
|
||||
// Add javac output to classpath so that the created class loader can find generated Java classes
|
||||
JvmContentRootsKt.addJvmClasspathRoot(configuration, output);
|
||||
}
|
||||
|
||||
blackBox();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected static List<String> findJavaSourcesInDirectory(@NotNull File directory) {
|
||||
final List<String> javaFilePaths = new ArrayList<String>(1);
|
||||
|
||||
FileUtil.processFilesRecursively(directory, new Processor<File>() {
|
||||
@Override
|
||||
public boolean process(File file) {
|
||||
if (file.isFile() && FilesKt.getExtension(file).equals(JavaFileType.DEFAULT_EXTENSION)) {
|
||||
javaFilePaths.add(file.getPath());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
return javaFilePaths;
|
||||
}
|
||||
|
||||
protected void blackBox() {
|
||||
// If there are many files, the first 'box(): String' function will be executed.
|
||||
GeneratedClassLoader generatedClassLoader = generateAndCreateClassLoader();
|
||||
for (KtFile firstFile : myFiles.getPsiFiles()) {
|
||||
String className = getFacadeFqName(firstFile);
|
||||
if (className == null) continue;
|
||||
Class<?> aClass = getGeneratedClass(generatedClassLoader, className);
|
||||
try {
|
||||
Method method = getBoxMethodOrNull(aClass);
|
||||
if (method != null) {
|
||||
String r = (String) method.invoke(null);
|
||||
assertEquals("OK", r);
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (Throwable e) {
|
||||
System.out.println(generateToText());
|
||||
throw ExceptionUtilsKt.rethrow(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getFacadeFqName(@NotNull KtFile firstFile) {
|
||||
for (KtDeclaration declaration : firstFile.getDeclarations()) {
|
||||
if (declaration instanceof KtProperty || declaration instanceof KtNamedFunction) {
|
||||
return JvmFileClassUtil.getFileClassInfoNoResolve(firstFile).getFacadeClassFqName().asString();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Class<?> getGeneratedClass(GeneratedClassLoader generatedClassLoader, String className) {
|
||||
try {
|
||||
return generatedClassLoader.loadClass(className);
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
fail("No class file was generated for: " + className);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Method getBoxMethodOrNull(Class<?> aClass) {
|
||||
try {
|
||||
return aClass.getMethod("box");
|
||||
}
|
||||
catch (NoSuchMethodException e){
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen
|
||||
|
||||
import org.jetbrains.kotlin.test.ConfigurationKind
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.jetbrains.org.objectweb.asm.*
|
||||
import java.io.File
|
||||
|
||||
abstract class AbstractBytecodeListingTest : CodegenTestCase() {
|
||||
override fun doTest(filename: String) {
|
||||
createEnvironmentWithMockJdkAndIdeaAnnotations(ConfigurationKind.ALL)
|
||||
loadFileByFullPath(filename)
|
||||
val ktFile = File(filename)
|
||||
val txtFile = File(ktFile.parentFile, ktFile.nameWithoutExtension + ".txt")
|
||||
val generatedFiles = CodegenTestUtil.generateFiles(myEnvironment, myFiles)
|
||||
.getClassFiles()
|
||||
.sortedBy { it.relativePath }
|
||||
.map {
|
||||
val cr = ClassReader(it.asByteArray())
|
||||
val visitor = TextCollectingVisitor()
|
||||
cr.accept(visitor, ClassReader.SKIP_CODE)
|
||||
KotlinTestUtils.replaceHash(visitor.text, "HASH")
|
||||
}.joinToString("\n\n")
|
||||
|
||||
KotlinTestUtils.assertEqualsToFile(txtFile, generatedFiles)
|
||||
}
|
||||
|
||||
private class TextCollectingVisitor : ClassVisitor(Opcodes.ASM5) {
|
||||
private class Declaration(val text: String, val annotations: MutableList<String> = arrayListOf())
|
||||
|
||||
private val declarationsInsideClass = arrayListOf<Declaration>()
|
||||
private val classAnnotations = arrayListOf<String>()
|
||||
private var className = ""
|
||||
private var classAccess = 0
|
||||
|
||||
private fun addAnnotation(desc: String, list: MutableList<String> = declarationsInsideClass.last().annotations) {
|
||||
val name = Type.getType(desc).className
|
||||
list.add("@$name ")
|
||||
}
|
||||
|
||||
private fun addModifier(text: String, list: MutableList<String>) {
|
||||
list.add("$text ")
|
||||
}
|
||||
|
||||
private fun handleModifiers(access: Int, list: MutableList<String> = declarationsInsideClass.last().annotations) {
|
||||
if ((access and Opcodes.ACC_PUBLIC) != 0) addModifier("public", list)
|
||||
if ((access and Opcodes.ACC_PROTECTED) != 0) addModifier("protected", list)
|
||||
if ((access and Opcodes.ACC_PRIVATE) != 0) addModifier("private", list)
|
||||
|
||||
if ((access and Opcodes.ACC_SYNTHETIC) != 0) addModifier("synthetic", list)
|
||||
if ((access and Opcodes.ACC_DEPRECATED) != 0) addModifier("deprecated", list)
|
||||
if ((access and Opcodes.ACC_FINAL) != 0) addModifier("final", list)
|
||||
if ((access and Opcodes.ACC_ABSTRACT) != 0) addModifier("abstract", list)
|
||||
if ((access and Opcodes.ACC_STATIC) != 0) addModifier("static", list)
|
||||
}
|
||||
|
||||
val text: String
|
||||
get() = StringBuilder().apply {
|
||||
append(classAnnotations.joinToString("\n", postfix = "\n"))
|
||||
arrayListOf<String>().apply { handleModifiers(classAccess, this) }.forEach { append(it) }
|
||||
append("class ")
|
||||
append(className)
|
||||
if (declarationsInsideClass.isNotEmpty()) {
|
||||
append(" {\n")
|
||||
for (declaration in declarationsInsideClass.sortedBy { it.text }) {
|
||||
append(" ").append(declaration.annotations.joinToString("")).append(declaration.text).append("\n")
|
||||
}
|
||||
append("}")
|
||||
}
|
||||
}.toString()
|
||||
|
||||
override fun visitMethod(
|
||||
access: Int,
|
||||
name: String,
|
||||
desc: String,
|
||||
signature: String?,
|
||||
exceptions: Array<out String>?
|
||||
): MethodVisitor? {
|
||||
val returnType = Type.getReturnType(desc).className
|
||||
val parameterTypes = Type.getArgumentTypes(desc).map { it.className }
|
||||
val methodAnnotations = arrayListOf<String>()
|
||||
val parameterAnnotations = hashMapOf<Int, MutableList<String>>()
|
||||
|
||||
handleModifiers(access, methodAnnotations)
|
||||
|
||||
return object : MethodVisitor(Opcodes.ASM5) {
|
||||
override fun visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor? {
|
||||
val type = Type.getType(desc).className
|
||||
methodAnnotations += "@$type "
|
||||
return super.visitAnnotation(desc, visible)
|
||||
}
|
||||
|
||||
override fun visitParameterAnnotation(parameter: Int, desc: String, visible: Boolean): AnnotationVisitor? {
|
||||
val type = Type.getType(desc).className
|
||||
parameterAnnotations.getOrPut(parameter, { arrayListOf() }).add("@$type ")
|
||||
return super.visitParameterAnnotation(parameter, desc, visible)
|
||||
}
|
||||
|
||||
override fun visitEnd() {
|
||||
val parameterWithAnnotations = parameterTypes.mapIndexed { index, parameter ->
|
||||
val annotations = parameterAnnotations.getOrElse(index, { emptyList<String>() }).joinToString("")
|
||||
"${annotations}p$index: $parameter"
|
||||
}.joinToString()
|
||||
declarationsInsideClass.add(Declaration("method $name($parameterWithAnnotations): $returnType", methodAnnotations))
|
||||
super.visitEnd()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitField(access: Int, name: String, desc: String, signature: String?, value: Any?): FieldVisitor? {
|
||||
val type = Type.getType(desc).className
|
||||
declarationsInsideClass.add(Declaration("field $name: $type"))
|
||||
handleModifiers(access)
|
||||
|
||||
return object : FieldVisitor(Opcodes.ASM5) {
|
||||
override fun visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor? {
|
||||
addAnnotation(desc)
|
||||
return super.visitAnnotation(desc, visible)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor? {
|
||||
val name = Type.getType(desc).className
|
||||
classAnnotations.add("@$name")
|
||||
return super.visitAnnotation(desc, visible)
|
||||
}
|
||||
|
||||
override fun visit(
|
||||
version: Int,
|
||||
access: Int,
|
||||
name: String,
|
||||
signature: String?,
|
||||
superName: String?,
|
||||
interfaces: Array<out String>?
|
||||
) {
|
||||
className = name
|
||||
classAccess = access
|
||||
}
|
||||
|
||||
override fun visitInnerClass(name: String, outerName: String?, innerName: String?, access: Int) {
|
||||
declarationsInsideClass.add(Declaration("inner class $name"))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen;
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import kotlin.text.Charsets;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.test.ConfigurationKind;
|
||||
import org.jetbrains.kotlin.utils.ExceptionUtilsKt;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public abstract class AbstractBytecodeTextTest extends CodegenTestCase {
|
||||
private static final Pattern AT_OUTPUT_FILE_PATTERN = Pattern.compile("^\\s*//\\s*@(.*):$");
|
||||
private static final Pattern EXPECTED_OCCURRENCES_PATTERN = Pattern.compile("^\\s*//\\s*(\\d+)\\s*(.*)$");
|
||||
|
||||
@Override
|
||||
protected void doMultiFileTest(@NotNull File wholeFile, @NotNull List<TestFile> files, @Nullable File javaFilesDir) throws Exception {
|
||||
createEnvironmentWithMockJdkAndIdeaAnnotations(ConfigurationKind.ALL, files, javaFilesDir);
|
||||
loadMultiFiles(files);
|
||||
|
||||
if (isMultiFileTest(files)) {
|
||||
doTestMultiFile(files);
|
||||
}
|
||||
else {
|
||||
List<OccurrenceInfo> expected = readExpectedOccurrences(wholeFile.getPath());
|
||||
String actual = generateToText();
|
||||
checkGeneratedTextAgainstExpectedOccurrences(actual, expected);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isMultiFileTest(@NotNull List<TestFile> files) {
|
||||
int kotlinFiles = 0;
|
||||
for (TestFile file : files) {
|
||||
if (file.name.endsWith(".kt")) {
|
||||
kotlinFiles++;
|
||||
}
|
||||
}
|
||||
return kotlinFiles > 1;
|
||||
}
|
||||
|
||||
protected static void checkGeneratedTextAgainstExpectedOccurrences(
|
||||
@NotNull String text,
|
||||
@NotNull List<OccurrenceInfo> expectedOccurrences
|
||||
) {
|
||||
StringBuilder expected = new StringBuilder();
|
||||
StringBuilder actual = new StringBuilder();
|
||||
|
||||
for (OccurrenceInfo info : expectedOccurrences) {
|
||||
expected.append(info.numberOfOccurrences).append(" ").append(info.needle).append("\n");
|
||||
int actualCount = StringUtil.findMatches(text, Pattern.compile("(" + info.needle + ")")).size();
|
||||
actual.append(actualCount).append(" ").append(info.needle).append("\n");
|
||||
}
|
||||
|
||||
try {
|
||||
assertEquals(text, expected.toString(), actual.toString());
|
||||
}
|
||||
catch (Throwable e) {
|
||||
System.out.println(text);
|
||||
throw ExceptionUtilsKt.rethrow(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void doTestMultiFile(@NotNull List<TestFile> files) throws Exception {
|
||||
Map<String, List<OccurrenceInfo>> expectedOccurrencesByOutputFile = new LinkedHashMap<String, List<OccurrenceInfo>>();
|
||||
for (TestFile file : files) {
|
||||
readExpectedOccurrencesForMultiFileTest(file, expectedOccurrencesByOutputFile);
|
||||
}
|
||||
|
||||
Map<String, String> generated = generateEachFileToText();
|
||||
for (String expectedOutputFile : expectedOccurrencesByOutputFile.keySet()) {
|
||||
assertTextWasGenerated(expectedOutputFile, generated);
|
||||
String generatedText = generated.get(expectedOutputFile);
|
||||
List<OccurrenceInfo> expectedOccurrences = expectedOccurrencesByOutputFile.get(expectedOutputFile);
|
||||
checkGeneratedTextAgainstExpectedOccurrences(generatedText, expectedOccurrences);
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertTextWasGenerated(String expectedOutputFile, Map<String, String> generated) {
|
||||
if (!generated.containsKey(expectedOutputFile)) {
|
||||
StringBuilder failMessage = new StringBuilder();
|
||||
failMessage.append("Missing output file ").append(expectedOutputFile).append(", got ").append(generated.size()).append(": ");
|
||||
for (String generatedFile : generated.keySet()) {
|
||||
failMessage.append(generatedFile).append(" ");
|
||||
}
|
||||
fail(failMessage.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected List<OccurrenceInfo> readExpectedOccurrences(@NotNull String filename) throws Exception {
|
||||
List<OccurrenceInfo> result = new ArrayList<OccurrenceInfo>();
|
||||
String[] lines = FileUtil.loadFile(new File(filename), Charsets.UTF_8.name(), true).split("\n");
|
||||
|
||||
for (String line : lines) {
|
||||
Matcher matcher = EXPECTED_OCCURRENCES_PATTERN.matcher(line);
|
||||
if (matcher.matches()) {
|
||||
result.add(parseOccurrenceInfo(matcher));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void readExpectedOccurrencesForMultiFileTest(
|
||||
@NotNull TestFile file,
|
||||
@NotNull Map<String, List<OccurrenceInfo>> occurrenceMap
|
||||
) {
|
||||
List<OccurrenceInfo> currentOccurrenceInfos = null;
|
||||
for (String line : file.content.split("\n")) {
|
||||
Matcher atOutputFileMatcher = AT_OUTPUT_FILE_PATTERN.matcher(line);
|
||||
if (atOutputFileMatcher.matches()) {
|
||||
String outputFileName = atOutputFileMatcher.group(1);
|
||||
if (occurrenceMap.containsKey(outputFileName)) {
|
||||
throw new AssertionError(
|
||||
file.name + ": Expected occurrences for output file " + outputFileName + " were already provided"
|
||||
);
|
||||
}
|
||||
currentOccurrenceInfos = new ArrayList<OccurrenceInfo>();
|
||||
occurrenceMap.put(outputFileName, currentOccurrenceInfos);
|
||||
}
|
||||
|
||||
Matcher expectedOccurrencesMatcher = EXPECTED_OCCURRENCES_PATTERN.matcher(line);
|
||||
if (expectedOccurrencesMatcher.matches()) {
|
||||
if (currentOccurrenceInfos == null) {
|
||||
throw new AssertionError(
|
||||
file.name + ": Should specify output file with '// @<OUTPUT_FILE_NAME>:' before expectations"
|
||||
);
|
||||
}
|
||||
OccurrenceInfo occurrenceInfo = parseOccurrenceInfo(expectedOccurrencesMatcher);
|
||||
currentOccurrenceInfos.add(occurrenceInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static OccurrenceInfo parseOccurrenceInfo(Matcher matcher) {
|
||||
int numberOfOccurrences = Integer.parseInt(matcher.group(1));
|
||||
String needle = matcher.group(2);
|
||||
return new OccurrenceInfo(numberOfOccurrences, needle);
|
||||
}
|
||||
|
||||
protected static class OccurrenceInfo {
|
||||
private final int numberOfOccurrences;
|
||||
private final String needle;
|
||||
|
||||
private OccurrenceInfo(int numberOfOccurrences, @NotNull String needle) {
|
||||
this.numberOfOccurrences = numberOfOccurrences;
|
||||
this.needle = needle;
|
||||
}
|
||||
}
|
||||
}
|
||||
-184
@@ -1,184 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen;
|
||||
|
||||
import com.google.common.io.Files;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.backend.common.output.OutputFile;
|
||||
import org.jetbrains.kotlin.backend.common.output.OutputFileCollection;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
|
||||
import org.jetbrains.kotlin.psi.KtFile;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.test.TestCaseWithTmpdir;
|
||||
import org.jetbrains.org.objectweb.asm.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Test correctness of written local variables in class file for specified method
|
||||
*/
|
||||
|
||||
public abstract class AbstractCheckLocalVariablesTableTest extends TestCaseWithTmpdir {
|
||||
|
||||
private File ktFile;
|
||||
private KotlinCoreEnvironment jetCoreEnvironment;
|
||||
|
||||
public AbstractCheckLocalVariablesTableTest() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
jetCoreEnvironment = KotlinTestUtils.createEnvironmentWithMockJdkAndIdeaAnnotations(myTestRootDisposable);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
jetCoreEnvironment = null;
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
protected void doTest(@NotNull String ktFileName) throws Exception {
|
||||
ktFile = new File(ktFileName);
|
||||
String text = FileUtil.loadFile(ktFile, true);
|
||||
|
||||
KtFile psiFile = KotlinTestUtils.createFile(ktFile.getName(), text, jetCoreEnvironment.getProject());
|
||||
|
||||
OutputFileCollection outputFiles = GenerationUtils.compileFileGetClassFileFactoryForTest(psiFile, jetCoreEnvironment);
|
||||
|
||||
String classAndMethod = parseClassAndMethodSignature();
|
||||
String[] split = classAndMethod.split("\\.");
|
||||
assert split.length == 2 : "Exactly one dot is expected: " + classAndMethod;
|
||||
final String classFileRegex = StringUtil.escapeToRegexp(split[0] + ".class").replace("\\*", ".+");
|
||||
String methodName = split[1];
|
||||
|
||||
OutputFile outputFile = ContainerUtil.find(outputFiles.asList(), new Condition<OutputFile>() {
|
||||
@Override
|
||||
public boolean value(OutputFile outputFile) {
|
||||
return outputFile.getRelativePath().matches(classFileRegex);
|
||||
}
|
||||
});
|
||||
|
||||
String pathsString = StringUtil.join(outputFiles.asList(), new Function<OutputFile, String>() {
|
||||
@Override
|
||||
public String fun(OutputFile file) {
|
||||
return file.getRelativePath();
|
||||
}
|
||||
}, ", ");
|
||||
assertNotNull("Couldn't find class file for pattern " + classFileRegex + " in: " + pathsString, outputFile);
|
||||
|
||||
ClassReader cr = new ClassReader(outputFile.asByteArray());
|
||||
List<LocalVariable> actualLocalVariables = readLocalVariable(cr, methodName);
|
||||
|
||||
KotlinTestUtils.assertEqualsToFile(ktFile, text.substring(0, text.indexOf("// VARIABLE : ")) + getActualVariablesAsString(actualLocalVariables));
|
||||
}
|
||||
|
||||
private static String getActualVariablesAsString(List<LocalVariable> list) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (LocalVariable variable : list) {
|
||||
builder.append(variable.toString()).append("\n");
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
|
||||
private static class LocalVariable {
|
||||
private final String name;
|
||||
private final String type;
|
||||
private final int index;
|
||||
|
||||
private LocalVariable(
|
||||
@NotNull String name,
|
||||
@NotNull String type,
|
||||
int index
|
||||
) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "// VARIABLE : NAME=" + name + " TYPE=" + type + " INDEX=" + index;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static final Pattern methodPattern = Pattern.compile("^// METHOD : *(.*)");
|
||||
|
||||
@NotNull
|
||||
private String parseClassAndMethodSignature() throws IOException {
|
||||
List<String> lines = Files.readLines(ktFile, Charset.forName("utf-8"));
|
||||
for (String line : lines) {
|
||||
Matcher methodMatcher = methodPattern.matcher(line);
|
||||
if (methodMatcher.matches()) {
|
||||
return methodMatcher.group(1);
|
||||
}
|
||||
}
|
||||
|
||||
throw new AssertionError("method instructions not found");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<LocalVariable> readLocalVariable(ClassReader cr, final String methodName) throws Exception {
|
||||
class Visitor extends ClassVisitor {
|
||||
List<LocalVariable> readVariables = new ArrayList<LocalVariable>();
|
||||
|
||||
public Visitor() {
|
||||
super(Opcodes.ASM5);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodVisitor visitMethod(
|
||||
int access, @NotNull String name, @NotNull String desc, String signature, String[] exceptions
|
||||
) {
|
||||
if (methodName.equals(name + desc)) {
|
||||
return new MethodVisitor(Opcodes.ASM5) {
|
||||
@Override
|
||||
public void visitLocalVariable(
|
||||
@NotNull String name, @NotNull String desc, String signature, @NotNull Label start, @NotNull Label end, int index
|
||||
) {
|
||||
readVariables.add(new LocalVariable(name, desc, index));
|
||||
}
|
||||
};
|
||||
}
|
||||
else {
|
||||
return super.visitMethod(access, name, desc, signature, exceptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
Visitor visitor = new Visitor();
|
||||
|
||||
cr.accept(visitor, ClassReader.SKIP_FRAMES);
|
||||
|
||||
assertFalse("method not found: " + methodName, visitor.readVariables.size() == 0);
|
||||
|
||||
return visitor.readVariables;
|
||||
}
|
||||
}
|
||||
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen
|
||||
|
||||
import java.io.File
|
||||
|
||||
abstract class AbstractCompileKotlinAgainstInlineKotlinTest : AbstractCompileKotlinAgainstKotlinTest() {
|
||||
override fun doMultiFileTest(wholeFile: File, files: List<TestFile>, javaFilesDir: File?) {
|
||||
val (factory1, factory2) = doTwoFileTest(files.filter { it.name.endsWith(".kt") })
|
||||
try {
|
||||
val allGeneratedFiles = factory1.asList() + factory2.asList()
|
||||
val sourceFiles = factory1.inputFiles + factory2.inputFiles
|
||||
InlineTestUtil.checkNoCallsToInline(allGeneratedFiles.filterClassFiles(), sourceFiles)
|
||||
SMAPTestUtil.checkSMAP(files, allGeneratedFiles.filterClassFiles())
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
println("FIRST:\n\n${factory1.createText()}\n\nSECOND:\n\n${factory2.createText()}")
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
-146
@@ -1,146 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen;
|
||||
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import kotlin.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.cli.common.modules.ModuleBuilder;
|
||||
import org.jetbrains.kotlin.cli.common.output.outputUtils.OutputUtilsKt;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
|
||||
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime;
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration;
|
||||
import org.jetbrains.kotlin.load.kotlin.ModuleVisibilityManager;
|
||||
import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils;
|
||||
import org.jetbrains.kotlin.psi.KtFile;
|
||||
import org.jetbrains.kotlin.test.ConfigurationKind;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.test.TestJdkKind;
|
||||
import org.jetbrains.kotlin.utils.ExceptionUtilsKt;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class AbstractCompileKotlinAgainstKotlinTest extends CodegenTestCase {
|
||||
private File tmpdir;
|
||||
private File aDir;
|
||||
private File bDir;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
tmpdir = KotlinTestUtils.tmpDirForTest(this);
|
||||
aDir = new File(tmpdir, "a");
|
||||
bDir = new File(tmpdir, "b");
|
||||
KotlinTestUtils.mkdirs(aDir);
|
||||
KotlinTestUtils.mkdirs(bDir);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doMultiFileTest(@NotNull File wholeFile, @NotNull List<TestFile> files, @Nullable File javaFilesDir) throws Exception {
|
||||
assert javaFilesDir == null : ".java files are not supported yet in this test";
|
||||
doTwoFileTest(files);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected Pair<ClassFileFactory, ClassFileFactory> doTwoFileTest(@NotNull List<TestFile> files) throws Exception {
|
||||
// Note that it may be beneficial to improve this test to handle many files, compiling them successively against all previous
|
||||
assert files.size() == 2 : "There should be exactly two files in this test";
|
||||
TestFile fileA = files.get(0);
|
||||
TestFile fileB = files.get(1);
|
||||
ClassFileFactory factoryA = compileA(fileA.name, fileA.content, files);
|
||||
ClassFileFactory factoryB = null;
|
||||
try {
|
||||
factoryB = compileB(fileB.name, fileB.content, files);
|
||||
invokeBox(PackagePartClassUtils.getFilePartShortName(new File(fileB.name).getName()));
|
||||
}
|
||||
catch (Throwable e) {
|
||||
String result = "FIRST: \n\n" + factoryA.createText();
|
||||
if (factoryB != null) {
|
||||
result += "\n\nSECOND: \n\n" + factoryB.createText();
|
||||
}
|
||||
System.out.println(result);
|
||||
throw ExceptionUtilsKt.rethrow(e);
|
||||
}
|
||||
return new Pair<ClassFileFactory, ClassFileFactory>(factoryA, factoryB);
|
||||
}
|
||||
|
||||
protected void invokeBox(@NotNull String className) throws Exception {
|
||||
Method box = createGeneratedClassLoader().loadClass(className).getMethod("box");
|
||||
String result = (String) box.invoke(null);
|
||||
assertEquals("OK", result);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private URLClassLoader createGeneratedClassLoader() throws Exception {
|
||||
return new URLClassLoader(
|
||||
new URL[]{ bDir.toURI().toURL(), aDir.toURI().toURL() },
|
||||
ForTestCompileRuntime.runtimeAndReflectJarClassLoader()
|
||||
);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected ClassFileFactory compileA(@NotNull String fileName, @NotNull String content, List<TestFile> files) throws IOException {
|
||||
Disposable compileDisposable = createDisposable("compileA");
|
||||
KotlinCoreEnvironment environment =
|
||||
KotlinTestUtils.createEnvironmentWithJdkAndNullabilityAnnotationsFromIdea(compileDisposable, ConfigurationKind.ALL, getJdkKind(files));
|
||||
return compileKotlin(fileName, content, aDir, environment, compileDisposable);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected ClassFileFactory compileB(@NotNull String fileName, @NotNull String content, List<TestFile> files) throws IOException {
|
||||
CompilerConfiguration configurationWithADirInClasspath = KotlinTestUtils
|
||||
.compilerConfigurationForTests(ConfigurationKind.ALL, getJdkKind(files), KotlinTestUtils.getAnnotationsJar(), aDir);
|
||||
|
||||
Disposable compileDisposable = createDisposable("compileB");
|
||||
KotlinCoreEnvironment environment = KotlinCoreEnvironment.createForTests(
|
||||
compileDisposable, configurationWithADirInClasspath, EnvironmentConfigFiles.JVM_CONFIG_FILES);
|
||||
|
||||
return compileKotlin(fileName, content, bDir, environment, compileDisposable);
|
||||
}
|
||||
|
||||
private Disposable createDisposable(String debugName) {
|
||||
Disposable disposable = Disposer.newDisposable("CompileDisposable" + debugName);
|
||||
Disposer.register(getTestRootDisposable(), disposable);
|
||||
return disposable;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private ClassFileFactory compileKotlin(
|
||||
@NotNull String fileName, @NotNull String content, @NotNull File outputDir, @NotNull KotlinCoreEnvironment environment,
|
||||
@NotNull Disposable disposable
|
||||
) throws IOException {
|
||||
KtFile psiFile = KotlinTestUtils.createFile(fileName, content, environment.getProject());
|
||||
|
||||
ModuleVisibilityManager.SERVICE.getInstance(environment.getProject()).addModule(new ModuleBuilder("module for test", tmpdir.getAbsolutePath(), "test"));
|
||||
|
||||
ClassFileFactory outputFiles = GenerationUtils.compileFileGetClassFileFactoryForTest(psiFile, environment);
|
||||
|
||||
OutputUtilsKt.writeAllTo(outputFiles, outputDir);
|
||||
|
||||
Disposer.dispose(disposable);
|
||||
return outputFiles;
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen
|
||||
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.JvmPackagePartProvider
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil
|
||||
import org.jetbrains.kotlin.test.ConfigurationKind
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.jetbrains.kotlin.test.TestJdkKind
|
||||
import org.jetbrains.kotlin.utils.*
|
||||
import java.io.File
|
||||
|
||||
abstract class AbstractDumpDeclarationsTest : CodegenTestCase() {
|
||||
|
||||
override fun doMultiFileTest(wholeFile: File, files: List<TestFile>, javaFilesDir: File?) {
|
||||
val expectedResult = KotlinTestUtils.replaceExtension(wholeFile, "json")
|
||||
compileAndCompareDump(files, expectedResult)
|
||||
}
|
||||
|
||||
|
||||
private fun compileAndCompareDump(files: List<TestFile>, expectedResult: File) {
|
||||
configurationKind = ConfigurationKind.NO_KOTLIN_REFLECT
|
||||
|
||||
val configuration = KotlinTestUtils.compilerConfigurationForTests(
|
||||
configurationKind, TestJdkKind.MOCK_JDK, listOf(KotlinTestUtils.getAnnotationsJar()), emptyList())
|
||||
|
||||
myEnvironment = KotlinCoreEnvironment.createForTests(
|
||||
testRootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
|
||||
|
||||
loadMultiFiles(files)
|
||||
|
||||
val declarationsFile = compileManyFilesGetDeclarationsDump(myFiles.psiFiles)
|
||||
KotlinTestUtils.assertEqualsToFile(expectedResult, declarationsFile.readText())
|
||||
}
|
||||
|
||||
private fun compileManyFilesGetDeclarationsDump(files: List<KtFile>): File {
|
||||
val project = myEnvironment.project
|
||||
val packagePartProvider = JvmPackagePartProvider(myEnvironment)
|
||||
|
||||
val analysisResult = JvmResolveUtil.analyzeFilesWithJavaIntegrationAndCheckForErrors(
|
||||
project, files, packagePartProvider)
|
||||
|
||||
analysisResult.throwIfError()
|
||||
|
||||
val dumpToFile = KotlinTestUtils.tmpDirForTest(this).resolve(this.name + ".json")
|
||||
|
||||
val state = GenerationState(
|
||||
project, ClassBuilderFactories.TEST,
|
||||
analysisResult.moduleDescriptor, analysisResult.bindingContext,
|
||||
files,
|
||||
disableCallAssertions = false,
|
||||
disableParamAssertions = false,
|
||||
dumpBinarySignatureMappingTo = dumpToFile)
|
||||
KotlinCodegenFacade.compileCorrectFiles(state, org.jetbrains.kotlin.codegen.CompilationErrorHandler.THROW_EXCEPTION)
|
||||
|
||||
state.destroy()
|
||||
|
||||
return dumpToFile
|
||||
}
|
||||
}
|
||||
@@ -1,232 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import kotlin.collections.CollectionsKt;
|
||||
import kotlin.Pair;
|
||||
import kotlin.jvm.functions.Function1;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.backend.common.output.OutputFile;
|
||||
import org.jetbrains.kotlin.backend.common.output.OutputFileCollection;
|
||||
import org.jetbrains.kotlin.cli.common.output.outputUtils.OutputUtilsKt;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||
import org.jetbrains.kotlin.psi.KtFile;
|
||||
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.utils.ExceptionUtilsKt;
|
||||
import org.jetbrains.org.objectweb.asm.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public abstract class AbstractLineNumberTest extends TestCaseWithTmpdir {
|
||||
|
||||
private static final String LINE_NUMBER_FUN = "lineNumber";
|
||||
private static final Pattern TEST_LINE_NUMBER_PATTERN = Pattern.compile("^.*test." + LINE_NUMBER_FUN + "\\(\\).*$");
|
||||
|
||||
@NotNull
|
||||
private static String getTestDataPath() {
|
||||
return KotlinTestUtils.getTestDataPathBase() + "/lineNumber";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private KotlinCoreEnvironment createEnvironment() {
|
||||
return KotlinCoreEnvironment.createForTests(
|
||||
myTestRootDisposable,
|
||||
KotlinTestUtils.compilerConfigurationForTests(ConfigurationKind.JDK_ONLY, TestJdkKind.MOCK_JDK,
|
||||
KotlinTestUtils.getAnnotationsJar(), tmpdir),
|
||||
EnvironmentConfigFiles.JVM_CONFIG_FILES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
KotlinCoreEnvironment environment = createEnvironment();
|
||||
KtFile psiFile = KotlinTestUtils.createFile(LINE_NUMBER_FUN + ".kt",
|
||||
"package test;\n\npublic fun " + LINE_NUMBER_FUN + "(): Int = 0\n",
|
||||
environment.getProject());
|
||||
|
||||
OutputFileCollection outputFiles =
|
||||
GenerationUtils.compileFileGetClassFileFactoryForTest(psiFile, environment);
|
||||
OutputUtilsKt.writeAllTo(outputFiles, tmpdir);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Pair<KtFile, KotlinCoreEnvironment> createPsiFile(@NotNull String filename) {
|
||||
File file = new File(filename);
|
||||
KotlinCoreEnvironment environment = createEnvironment();
|
||||
|
||||
String text;
|
||||
try {
|
||||
text = FileUtil.loadFile(file, true);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw ExceptionUtilsKt.rethrow(e);
|
||||
}
|
||||
|
||||
return new Pair(KotlinTestUtils.createFile(file.getName(), text, environment.getProject()), environment);
|
||||
}
|
||||
|
||||
private void doTest(@NotNull String filename, boolean custom) {
|
||||
Pair<KtFile, KotlinCoreEnvironment> fileAndEnv = createPsiFile(filename);
|
||||
KtFile psiFile = fileAndEnv.getFirst();
|
||||
KotlinCoreEnvironment environment = fileAndEnv.getSecond();
|
||||
|
||||
GenerationState state = GenerationUtils.compileFileGetGenerationStateForTest(psiFile, environment);
|
||||
|
||||
if (custom) {
|
||||
List<Integer> actualLineNumbers = extractActualLineNumbersFromBytecode(state, false);
|
||||
String text = psiFile.getText();
|
||||
String newFileText = text.substring(0, text.indexOf("// ")) + getActualLineNumbersAsString(actualLineNumbers);
|
||||
KotlinTestUtils.assertEqualsToFile(new File(filename), newFileText);
|
||||
}
|
||||
else {
|
||||
List<Integer> expectedLineNumbers = extractSelectedLineNumbersFromSource(psiFile);
|
||||
List<Integer> actualLineNumbers = extractActualLineNumbersFromBytecode(state, true);
|
||||
assertSameElements(actualLineNumbers, expectedLineNumbers);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static String getActualLineNumbersAsString(List<Integer> list) {
|
||||
return CollectionsKt.joinToString(list, " ", "// ", "", -1, "...", new Function1<Integer, CharSequence>() {
|
||||
@Override
|
||||
public CharSequence invoke(Integer integer) {
|
||||
return integer.toString();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<Integer> extractActualLineNumbersFromBytecode(@NotNull GenerationState state, boolean testFunInvoke) {
|
||||
ClassFileFactory factory = state.getFactory();
|
||||
List<Integer> actualLineNumbers = Lists.newArrayList();
|
||||
for (OutputFile outputFile : ClassFileUtilsKt.getClassFiles(factory)) {
|
||||
ClassReader cr = new ClassReader(outputFile.asByteArray());
|
||||
try {
|
||||
List<Integer> lineNumbers = testFunInvoke ? readTestFunLineNumbers(cr) : readAllLineNumbers(cr);
|
||||
actualLineNumbers.addAll(lineNumbers);
|
||||
}
|
||||
catch (Throwable e) {
|
||||
System.out.println(factory.createText());
|
||||
throw ExceptionUtilsKt.rethrow(e);
|
||||
}
|
||||
}
|
||||
|
||||
return actualLineNumbers;
|
||||
}
|
||||
|
||||
protected void doTest(String path) {
|
||||
doTest(path, false);
|
||||
}
|
||||
|
||||
protected void doTestCustom(String path) {
|
||||
doTest(path, true);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<Integer> extractSelectedLineNumbersFromSource(@NotNull KtFile file) {
|
||||
String fileContent = file.getText();
|
||||
List<Integer> lineNumbers = Lists.newArrayList();
|
||||
String[] lines = StringUtil.convertLineSeparators(fileContent).split("\n");
|
||||
|
||||
for (int i = 0; i < lines.length; i++) {
|
||||
Matcher matcher = TEST_LINE_NUMBER_PATTERN.matcher(lines[i]);
|
||||
if (matcher.matches()) {
|
||||
lineNumbers.add(i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
return lineNumbers;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<Integer> readTestFunLineNumbers(@NotNull ClassReader cr) {
|
||||
final List<Label> labels = Lists.newArrayList();
|
||||
final Map<Label, Integer> labels2LineNumbers = Maps.newHashMap();
|
||||
|
||||
ClassVisitor visitor = new ClassVisitor(Opcodes.ASM5) {
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, @NotNull String name, @NotNull String desc, String signature, String[] exceptions) {
|
||||
return new MethodVisitor(Opcodes.ASM5) {
|
||||
private Label lastLabel;
|
||||
|
||||
@Override
|
||||
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
|
||||
if (LINE_NUMBER_FUN.equals(name)) {
|
||||
assert lastLabel != null : "A function call with no preceding label";
|
||||
labels.add(lastLabel);
|
||||
}
|
||||
lastLabel = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLabel(@NotNull Label label) {
|
||||
lastLabel = label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLineNumber(int line, @NotNull Label start) {
|
||||
labels2LineNumbers.put(start, line);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
cr.accept(visitor, ClassReader.SKIP_FRAMES);
|
||||
|
||||
List<Integer> lineNumbers = Lists.newArrayList();
|
||||
for (Label label : labels) {
|
||||
Integer lineNumber = labels2LineNumbers.get(label);
|
||||
assert lineNumber != null : "No line number found for a label";
|
||||
lineNumbers.add(lineNumber);
|
||||
}
|
||||
|
||||
return lineNumbers;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<Integer> readAllLineNumbers(@NotNull ClassReader reader) {
|
||||
final List<Integer> result = new ArrayList<Integer>();
|
||||
reader.accept(new ClassVisitor(Opcodes.ASM5) {
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, @NotNull String name, @NotNull String desc, String signature, String[] exceptions) {
|
||||
return new MethodVisitor(Opcodes.ASM5) {
|
||||
@Override
|
||||
public void visitLineNumber(int line, @NotNull Label label) {
|
||||
result.add(line);
|
||||
}
|
||||
};
|
||||
}
|
||||
}, ClassReader.SKIP_FRAMES);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.name.FqName;
|
||||
import org.jetbrains.kotlin.test.ConfigurationKind;
|
||||
import org.jetbrains.kotlin.utils.ExceptionUtilsKt;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public abstract class AbstractScriptCodegenTest extends CodegenTestCase {
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
createEnvironmentWithMockJdkAndIdeaAnnotations(ConfigurationKind.JDK_ONLY);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doTest(@NotNull String filename) {
|
||||
loadFileByFullPath(filename);
|
||||
|
||||
try {
|
||||
//noinspection ConstantConditions
|
||||
FqName fqName = myFiles.getPsiFile().getScript().getFqName();
|
||||
Class<?> scriptClass = generateClass(fqName.asString());
|
||||
|
||||
Constructor constructor = getTheOnlyConstructor(scriptClass);
|
||||
Object scriptInstance = constructor.newInstance(myFiles.getScriptParameterValues().toArray());
|
||||
|
||||
assertFalse("expecting at least one expectation", myFiles.getExpectedValues().isEmpty());
|
||||
|
||||
for (Pair<String, String> nameValue : myFiles.getExpectedValues()) {
|
||||
String fieldName = nameValue.first;
|
||||
String expectedValue = nameValue.second;
|
||||
|
||||
if (expectedValue.equals("<nofield>")) {
|
||||
try {
|
||||
scriptClass.getDeclaredField(fieldName);
|
||||
fail("must have no field " + fieldName);
|
||||
}
|
||||
catch (NoSuchFieldException e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Field field = scriptClass.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
Object result = field.get(scriptInstance);
|
||||
String resultString = result != null ? result.toString() : "null";
|
||||
assertEquals("comparing field " + fieldName, expectedValue, resultString);
|
||||
}
|
||||
}
|
||||
catch (Throwable e) {
|
||||
System.out.println(generateToText());
|
||||
throw ExceptionUtilsKt.rethrow(e);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Constructor getTheOnlyConstructor(@NotNull Class<?> clazz) {
|
||||
Constructor[] constructors = clazz.getConstructors();
|
||||
if (constructors.length != 1) {
|
||||
throw new IllegalArgumentException("Script class should have one constructor: " + clazz);
|
||||
}
|
||||
return constructors[0];
|
||||
}
|
||||
}
|
||||
-81
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen;
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.Processor;
|
||||
import kotlin.collections.CollectionsKt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
|
||||
import org.jetbrains.kotlin.test.ConfigurationKind;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.test.MockLibraryUtil;
|
||||
import org.jetbrains.kotlin.test.TestJdkKind;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class AbstractTopLevelMembersInvocationTest extends AbstractBytecodeTextTest {
|
||||
|
||||
private static final String LIBRARY = "library";
|
||||
|
||||
@Override
|
||||
public void doTest(@NotNull String filename) throws Exception {
|
||||
File root = new File(filename);
|
||||
final List<String> sourceFiles = new ArrayList<String>(2);
|
||||
|
||||
FileUtil.processFilesRecursively(root, new Processor<File>() {
|
||||
@Override
|
||||
public boolean process(File file) {
|
||||
if (file.getName().endsWith(".kt")) {
|
||||
sourceFiles.add(relativePath(file));
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}, new Processor<File>() {
|
||||
@Override
|
||||
public boolean process(File file) {
|
||||
return !LIBRARY.equals(file.getName());
|
||||
}
|
||||
});
|
||||
|
||||
File library = new File(root, LIBRARY);
|
||||
List<File> classPath = library.exists() ?
|
||||
Collections.singletonList(MockLibraryUtil.compileLibraryToJar(library.getPath(), LIBRARY, false, false)) :
|
||||
Collections.<File>emptyList();
|
||||
|
||||
assert !sourceFiles.isEmpty() : getTestName(true) + " should contain at least one .kt file";
|
||||
Collections.sort(sourceFiles);
|
||||
|
||||
myEnvironment = KotlinCoreEnvironment.createForTests(
|
||||
getTestRootDisposable(),
|
||||
KotlinTestUtils.compilerConfigurationForTests(ConfigurationKind.JDK_ONLY, TestJdkKind.MOCK_JDK,
|
||||
CollectionsKt.plus(classPath, KotlinTestUtils.getAnnotationsJar()), classPath),
|
||||
EnvironmentConfigFiles.JVM_CONFIG_FILES);
|
||||
|
||||
loadFiles(ArrayUtil.toStringArray(sourceFiles));
|
||||
|
||||
List<OccurrenceInfo> expected = readExpectedOccurrences(KotlinTestUtils.getTestDataPathBase() + "/codegen/" + sourceFiles.get(0));
|
||||
String actual = generateToText();
|
||||
checkGeneratedTextAgainstExpectedOccurrences(actual, expected);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,7 +21,7 @@ import org.jetbrains.kotlin.backend.common.bridges.Bridge
|
||||
import org.jetbrains.kotlin.backend.common.bridges.FunctionHandle
|
||||
import org.jetbrains.kotlin.backend.common.bridges.generateBridges
|
||||
import org.jetbrains.kotlin.utils.DFS
|
||||
import java.util.HashSet
|
||||
import java.util.*
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class BridgeTest : TestCase() {
|
||||
|
||||
@@ -1,537 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.testFramework.TestDataFile;
|
||||
import kotlin.collections.ArraysKt;
|
||||
import kotlin.io.FilesKt;
|
||||
import kotlin.text.Charsets;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.backend.common.output.OutputFile;
|
||||
import org.jetbrains.kotlin.checkers.CheckerTestUtil;
|
||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
|
||||
import org.jetbrains.kotlin.cli.jvm.config.JVMConfigurationKeys;
|
||||
import org.jetbrains.kotlin.cli.jvm.config.JvmContentRootsKt;
|
||||
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime;
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration;
|
||||
import org.jetbrains.kotlin.config.CompilerConfigurationKey;
|
||||
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil;
|
||||
import org.jetbrains.kotlin.name.FqName;
|
||||
import org.jetbrains.kotlin.psi.KtFile;
|
||||
import org.jetbrains.kotlin.test.ConfigurationKind;
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.test.TestJdkKind;
|
||||
import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase;
|
||||
import org.jetbrains.kotlin.utils.ExceptionUtilsKt;
|
||||
import org.jetbrains.org.objectweb.asm.ClassReader;
|
||||
import org.jetbrains.org.objectweb.asm.tree.ClassNode;
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode;
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.Analyzer;
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException;
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue;
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.SimpleVerifier;
|
||||
import org.jetbrains.org.objectweb.asm.util.Textifier;
|
||||
import org.jetbrains.org.objectweb.asm.util.TraceMethodVisitor;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.jetbrains.kotlin.codegen.CodegenTestUtil.*;
|
||||
import static org.jetbrains.kotlin.test.KotlinTestUtils.compilerConfigurationForTests;
|
||||
import static org.jetbrains.kotlin.test.KotlinTestUtils.getAnnotationsJar;
|
||||
|
||||
public abstract class CodegenTestCase extends KtUsefulTestCase {
|
||||
private static final String DEFAULT_TEST_FILE_NAME = "a_test";
|
||||
|
||||
protected KotlinCoreEnvironment myEnvironment;
|
||||
protected CodegenTestFiles myFiles;
|
||||
protected ClassFileFactory classFileFactory;
|
||||
protected GeneratedClassLoader initializedClassLoader;
|
||||
protected ConfigurationKind configurationKind = ConfigurationKind.JDK_ONLY;
|
||||
|
||||
protected final void createEnvironmentWithMockJdkAndIdeaAnnotations(
|
||||
@NotNull ConfigurationKind configurationKind,
|
||||
@Nullable File... javaSourceRoots
|
||||
) {
|
||||
createEnvironmentWithMockJdkAndIdeaAnnotations(configurationKind, Collections.<TestFile>emptyList(), javaSourceRoots);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected static TestJdkKind getJdkKind(@NotNull List<TestFile> files) {
|
||||
for (TestFile file : files) {
|
||||
if (InTextDirectivesUtils.isDirectiveDefined(file.content, "FULL_JDK")) {
|
||||
return TestJdkKind.FULL_JDK;
|
||||
}
|
||||
}
|
||||
return TestJdkKind.MOCK_JDK;
|
||||
}
|
||||
|
||||
protected final void createEnvironmentWithMockJdkAndIdeaAnnotations(
|
||||
@NotNull ConfigurationKind configurationKind,
|
||||
@NotNull List<TestFile> testFilesWithConfigurationDirectives,
|
||||
@Nullable File... javaSourceRoots
|
||||
) {
|
||||
if (myEnvironment != null) {
|
||||
throw new IllegalStateException("must not set up myEnvironment twice");
|
||||
}
|
||||
|
||||
CompilerConfiguration configuration = createCompilerConfigurationForTests(
|
||||
configurationKind,
|
||||
TestJdkKind.MOCK_JDK,
|
||||
Collections.singletonList(getAnnotationsJar()),
|
||||
ArraysKt.filterNotNull(javaSourceRoots),
|
||||
testFilesWithConfigurationDirectives
|
||||
);
|
||||
|
||||
myEnvironment = KotlinCoreEnvironment.createForTests(
|
||||
getTestRootDisposable(), configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES
|
||||
);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected static CompilerConfiguration createCompilerConfigurationForTests(
|
||||
@NotNull ConfigurationKind kind,
|
||||
@NotNull TestJdkKind jdkKind,
|
||||
@NotNull List<File> classpath,
|
||||
@NotNull List<File> javaSource,
|
||||
@NotNull List<TestFile> testFilesWithConfigurationDirectives
|
||||
) {
|
||||
CompilerConfiguration configuration = compilerConfigurationForTests(kind, jdkKind, classpath, javaSource);
|
||||
|
||||
updateConfigurationByDirectivesInTestFiles(testFilesWithConfigurationDirectives, configuration);
|
||||
|
||||
return configuration;
|
||||
}
|
||||
|
||||
private static void updateConfigurationByDirectivesInTestFiles(
|
||||
@NotNull List<TestFile> testFilesWithConfigurationDirectives,
|
||||
@NotNull CompilerConfiguration configuration
|
||||
) {
|
||||
List<String> kotlinConfigurationFlags = new ArrayList<String>(0);
|
||||
for (TestFile testFile : testFilesWithConfigurationDirectives) {
|
||||
kotlinConfigurationFlags.addAll(InTextDirectivesUtils.findListWithPrefixes(testFile.content, "// KOTLIN_CONFIGURATION_FLAGS:"));
|
||||
}
|
||||
|
||||
updateConfigurationWithFlags(configuration, kotlinConfigurationFlags);
|
||||
}
|
||||
|
||||
private static final Map<String, Class<?>> FLAG_NAMESPACE_TO_CLASS = ImmutableMap.of(
|
||||
"CLI", CLIConfigurationKeys.class,
|
||||
"JVM", JVMConfigurationKeys.class
|
||||
);
|
||||
|
||||
private static final List<Class<?>> FLAG_CLASSES = ImmutableList.of(CLIConfigurationKeys.class, JVMConfigurationKeys.class);
|
||||
|
||||
private static final Pattern BOOLEAN_FLAG_PATTERN = Pattern.compile("([+-])(([a-zA-Z_0-9]*)\\.)?([a-zA-Z_0-9]*)");
|
||||
|
||||
private static void updateConfigurationWithFlags(@NotNull CompilerConfiguration configuration, @NotNull List<String> flags) {
|
||||
for (String flag : flags) {
|
||||
Matcher m = BOOLEAN_FLAG_PATTERN.matcher(flag);
|
||||
if (m.matches()) {
|
||||
boolean flagEnabled = !"-".equals(m.group(1));
|
||||
String flagNamespace = m.group(3);
|
||||
String flagName = m.group(4);
|
||||
|
||||
tryApplyBooleanFlag(configuration, flag, flagEnabled, flagNamespace, flagName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void tryApplyBooleanFlag(
|
||||
@NotNull CompilerConfiguration configuration,
|
||||
@NotNull String flag,
|
||||
boolean flagEnabled,
|
||||
@Nullable String flagNamespace,
|
||||
@NotNull String flagName
|
||||
) {
|
||||
Class<?> configurationKeysClass;
|
||||
Field configurationKeyField = null;
|
||||
if (flagNamespace == null) {
|
||||
for (Class<?> flagClass : FLAG_CLASSES) {
|
||||
try {
|
||||
configurationKeyField = flagClass.getField(flagName);
|
||||
break;
|
||||
}
|
||||
catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
configurationKeysClass = FLAG_NAMESPACE_TO_CLASS.get(flagNamespace);
|
||||
assert configurationKeysClass != null : "Expected [+|-][namespace.]configurationKey, got: " + flag;
|
||||
try {
|
||||
configurationKeyField = configurationKeysClass.getField(flagName);
|
||||
}
|
||||
catch (Exception e) {
|
||||
configurationKeyField = null;
|
||||
}
|
||||
}
|
||||
assert configurationKeyField != null : "Expected [+|-][namespace.]configurationKey, got: " + flag;
|
||||
|
||||
try {
|
||||
//noinspection unchecked
|
||||
CompilerConfigurationKey<Boolean> configurationKey = (CompilerConfigurationKey<Boolean>) configurationKeyField.get(null);
|
||||
configuration.put(configurationKey, flagEnabled);
|
||||
}
|
||||
catch (Exception e) {
|
||||
assert false : "Expected [+|-][namespace.]configurationKey, got: " + flag;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
myFiles = null;
|
||||
myEnvironment = null;
|
||||
classFileFactory = null;
|
||||
|
||||
if (initializedClassLoader != null) {
|
||||
initializedClassLoader.dispose();
|
||||
initializedClassLoader = null;
|
||||
}
|
||||
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
protected void loadText(@NotNull String text) {
|
||||
myFiles = CodegenTestFiles.create(DEFAULT_TEST_FILE_NAME + ".kt", text, myEnvironment.getProject());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected String loadFile(@NotNull @TestDataFile String name) {
|
||||
return loadFileByFullPath(KotlinTestUtils.getTestDataPathBase() + "/codegen/" + name);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected String loadFileByFullPath(@NotNull String fullPath) {
|
||||
try {
|
||||
File file = new File(fullPath);
|
||||
String content = FileUtil.loadFile(file, Charsets.UTF_8.name(), true);
|
||||
myFiles = CodegenTestFiles.create(file.getName(), content, myEnvironment.getProject());
|
||||
return content;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected void loadFiles(@NotNull String... names) {
|
||||
myFiles = CodegenTestFiles.create(myEnvironment.getProject(), names);
|
||||
}
|
||||
|
||||
protected void loadFile() {
|
||||
loadFile(getPrefix() + "/" + getTestName(true) + ".kt");
|
||||
}
|
||||
|
||||
protected void loadMultiFiles(@NotNull List<TestFile> files) {
|
||||
Collections.sort(files);
|
||||
|
||||
List<KtFile> ktFiles = new ArrayList<KtFile>(files.size());
|
||||
for (TestFile file : files) {
|
||||
if (file.name.endsWith(".kt")) {
|
||||
String content = CheckerTestUtil.parseDiagnosedRanges(file.content, new ArrayList<CheckerTestUtil.DiagnosedRange>(0));
|
||||
ktFiles.add(KotlinTestUtils.createFile(file.name, content, myEnvironment.getProject()));
|
||||
}
|
||||
}
|
||||
|
||||
myFiles = CodegenTestFiles.create(ktFiles);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected String codegenTestBasePath() {
|
||||
return "compiler/testData/codegen/";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected String relativePath(@NotNull File file) {
|
||||
String stringToCut = codegenTestBasePath();
|
||||
String systemIndependentPath = file.getPath().replace(File.separatorChar, '/');
|
||||
assert systemIndependentPath.startsWith(stringToCut) : "File path is not absolute: " + file;
|
||||
return systemIndependentPath.substring(stringToCut.length());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected String getPrefix() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected GeneratedClassLoader generateAndCreateClassLoader() {
|
||||
if (initializedClassLoader != null) {
|
||||
fail("Double initialization of class loader in same test");
|
||||
}
|
||||
|
||||
initializedClassLoader = createClassLoader();
|
||||
|
||||
if (!verifyAllFilesWithAsm(generateClassesInFile(), initializedClassLoader)) {
|
||||
fail("Verification failed: see exceptions above");
|
||||
}
|
||||
|
||||
return initializedClassLoader;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected GeneratedClassLoader createClassLoader() {
|
||||
return new GeneratedClassLoader(
|
||||
generateClassesInFile(),
|
||||
configurationKind.getWithReflection() ? ForTestCompileRuntime.runtimeAndReflectJarClassLoader()
|
||||
: ForTestCompileRuntime.runtimeJarClassLoader(),
|
||||
getClassPathURLs()
|
||||
);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private URL[] getClassPathURLs() {
|
||||
List<URL> urls = Lists.newArrayList();
|
||||
for (File file : JvmContentRootsKt.getJvmClasspathRoots(myEnvironment.getConfiguration())) {
|
||||
try {
|
||||
urls.add(file.toURI().toURL());
|
||||
}
|
||||
catch (MalformedURLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
return urls.toArray(new URL[urls.size()]);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected String generateToText() {
|
||||
if (classFileFactory == null) {
|
||||
classFileFactory = generateFiles(myEnvironment, myFiles);
|
||||
}
|
||||
return classFileFactory.createText();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected Map<String, String> generateEachFileToText() {
|
||||
if (classFileFactory == null) {
|
||||
classFileFactory = generateFiles(myEnvironment, myFiles);
|
||||
}
|
||||
return classFileFactory.createTextForEachFile();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected Class<?> generateFacadeClass() {
|
||||
FqName facadeClassFqName = JvmFileClassUtil.getFileClassInfoNoResolve(myFiles.getPsiFile()).getFacadeClassFqName();
|
||||
return generateClass(facadeClassFqName.asString());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected Class<?> generateClass(@NotNull String name) {
|
||||
try {
|
||||
return generateAndCreateClassLoader().loadClass(name);
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
fail("No class file was generated for: " + name);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected ClassFileFactory generateClassesInFile() {
|
||||
if (classFileFactory == null) {
|
||||
try {
|
||||
classFileFactory = generateFiles(myEnvironment, myFiles);
|
||||
|
||||
if (DxChecker.RUN_DX_CHECKER) {
|
||||
DxChecker.check(classFileFactory);
|
||||
}
|
||||
}
|
||||
catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
System.err.println("Generating instructions as text...");
|
||||
try {
|
||||
if (classFileFactory == null) {
|
||||
System.out.println("Cannot generate text: exception was thrown during generation");
|
||||
}
|
||||
else {
|
||||
System.out.println(classFileFactory.createText());
|
||||
}
|
||||
}
|
||||
catch (Throwable e1) {
|
||||
System.err.println("Exception thrown while trying to generate text, the actual exception follows:");
|
||||
e1.printStackTrace();
|
||||
System.err.println("-----------------------------------------------------------------------------");
|
||||
}
|
||||
fail("See exceptions above");
|
||||
}
|
||||
}
|
||||
return classFileFactory;
|
||||
}
|
||||
|
||||
private static boolean verifyAllFilesWithAsm(ClassFileFactory factory, ClassLoader loader) {
|
||||
boolean noErrors = true;
|
||||
for (OutputFile file : ClassFileUtilsKt.getClassFiles(factory)) {
|
||||
noErrors &= verifyWithAsm(file, loader);
|
||||
}
|
||||
return noErrors;
|
||||
}
|
||||
|
||||
private static boolean verifyWithAsm(@NotNull OutputFile file, ClassLoader loader) {
|
||||
ClassNode classNode = new ClassNode();
|
||||
new ClassReader(file.asByteArray()).accept(classNode, 0);
|
||||
|
||||
SimpleVerifier verifier = new SimpleVerifier();
|
||||
verifier.setClassLoader(loader);
|
||||
Analyzer<BasicValue> analyzer = new Analyzer<BasicValue>(verifier);
|
||||
|
||||
boolean noErrors = true;
|
||||
for (MethodNode method : classNode.methods) {
|
||||
try {
|
||||
analyzer.analyze(classNode.name, method);
|
||||
}
|
||||
catch (Throwable e) {
|
||||
System.err.println(file.asText());
|
||||
System.err.println(classNode.name + "::" + method.name + method.desc);
|
||||
|
||||
//noinspection InstanceofCatchParameter
|
||||
if (e instanceof AnalyzerException) {
|
||||
// Print the erroneous instruction
|
||||
TraceMethodVisitor tmv = new TraceMethodVisitor(new Textifier());
|
||||
((AnalyzerException) e).node.accept(tmv);
|
||||
PrintWriter pw = new PrintWriter(System.err);
|
||||
tmv.p.print(pw);
|
||||
pw.flush();
|
||||
}
|
||||
|
||||
e.printStackTrace();
|
||||
noErrors = false;
|
||||
}
|
||||
}
|
||||
return noErrors;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected Method generateFunction() {
|
||||
Class<?> aClass = generateFacadeClass();
|
||||
try {
|
||||
return findTheOnlyMethod(aClass);
|
||||
} catch (Error e) {
|
||||
System.out.println(generateToText());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected Method generateFunction(@NotNull String name) {
|
||||
return findDeclaredMethodByName(generateFacadeClass(), name);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Class<? extends Annotation> loadAnnotationClassQuietly(@NotNull String fqName) {
|
||||
try {
|
||||
//noinspection unchecked
|
||||
return (Class<? extends Annotation>) initializedClassLoader.loadClass(fqName);
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
throw ExceptionUtilsKt.rethrow(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static class TestFile implements Comparable<TestFile> {
|
||||
public final String name;
|
||||
public final String content;
|
||||
|
||||
public TestFile(@NotNull String name, @NotNull String content) {
|
||||
this.name = name;
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull TestFile o) {
|
||||
return name.compareTo(o.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return name.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof TestFile && ((TestFile) obj).name.equals(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
protected void doTest(String filePath) throws Exception {
|
||||
File file = new File(filePath);
|
||||
String expectedText = KotlinTestUtils.doLoadFile(file);
|
||||
final Ref<File> javaFilesDir = Ref.create();
|
||||
|
||||
List<TestFile> testFiles = createTestFiles(file, expectedText, javaFilesDir);
|
||||
|
||||
doMultiFileTest(file, testFiles, javaFilesDir.get());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private List<TestFile> createTestFiles(File file, String expectedText, final Ref<File> javaFilesDir) {
|
||||
return KotlinTestUtils.createTestFiles(file.getName(), expectedText, new KotlinTestUtils.TestFileFactoryNoModules<TestFile>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public TestFile create(@NotNull String fileName, @NotNull String text, @NotNull Map<String, String> directives) {
|
||||
if (fileName.endsWith(".java")) {
|
||||
if (javaFilesDir.isNull()) {
|
||||
try {
|
||||
javaFilesDir.set(KotlinTestUtils.tmpDir("java-files"));
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw ExceptionUtilsKt.rethrow(e);
|
||||
}
|
||||
}
|
||||
writeSourceFile(fileName, text, javaFilesDir.get());
|
||||
}
|
||||
|
||||
return new TestFile(fileName, text);
|
||||
}
|
||||
|
||||
private void writeSourceFile(@NotNull String fileName, @NotNull String content, @NotNull File targetDir) {
|
||||
File file = new File(targetDir, fileName);
|
||||
KotlinTestUtils.mkdirs(file.getParentFile());
|
||||
FilesKt.writeText(file, content, Charsets.UTF_8);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void doMultiFileTest(@NotNull File wholeFile, @NotNull List<TestFile> files, @Nullable File javaFilesDir) throws Exception {
|
||||
throw new UnsupportedOperationException("Multi-file test cases are not supported in this test");
|
||||
}
|
||||
}
|
||||
@@ -1,175 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.psi.PsiErrorElement;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||
import org.jetbrains.kotlin.checkers.CheckerTestUtil;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.psi.KtFile;
|
||||
import org.jetbrains.kotlin.resolve.AnalyzingUtils;
|
||||
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform;
|
||||
import org.jetbrains.kotlin.script.KotlinScriptDefinitionProvider;
|
||||
import org.jetbrains.kotlin.script.ScriptParameter;
|
||||
import org.jetbrains.kotlin.scripts.TestScriptDefinition;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.types.KotlinType;
|
||||
import org.jetbrains.kotlin.types.Variance;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class CodegenTestFiles {
|
||||
|
||||
@NotNull
|
||||
private final List<KtFile> psiFiles;
|
||||
@NotNull
|
||||
private final List<Pair<String, String>> expectedValues;
|
||||
@NotNull
|
||||
private final List<Object> scriptParameterValues;
|
||||
|
||||
private CodegenTestFiles(
|
||||
@NotNull List<KtFile> psiFiles,
|
||||
@NotNull List<Pair<String, String>> expectedValues,
|
||||
@NotNull List<Object> scriptParameterValues
|
||||
) {
|
||||
this.psiFiles = psiFiles;
|
||||
this.expectedValues = expectedValues;
|
||||
this.scriptParameterValues = scriptParameterValues;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public KtFile getPsiFile() {
|
||||
assert psiFiles.size() == 1;
|
||||
return psiFiles.get(0);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<Pair<String, String>> getExpectedValues() {
|
||||
return expectedValues;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<Object> getScriptParameterValues() {
|
||||
return scriptParameterValues;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<KtFile> getPsiFiles() {
|
||||
return psiFiles;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static CodegenTestFiles create(@NotNull List<KtFile> ktFiles) {
|
||||
assert !ktFiles.isEmpty() : "List should have at least one file";
|
||||
return new CodegenTestFiles(ktFiles, Collections.<Pair<String, String>>emptyList(), Collections.emptyList());
|
||||
}
|
||||
|
||||
public static CodegenTestFiles create(Project project, String[] names) {
|
||||
return create(project, names, KotlinTestUtils.getTestDataPathBase());
|
||||
}
|
||||
|
||||
public static CodegenTestFiles create(Project project, String[] names, String testDataPath) {
|
||||
List<KtFile> files = new ArrayList<KtFile>(names.length);
|
||||
for (String name : names) {
|
||||
try {
|
||||
String content = KotlinTestUtils.doLoadFile(testDataPath + "/codegen/", name);
|
||||
KtFile file = KotlinTestUtils.createFile(name, content, project);
|
||||
files.add(file);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return create(files);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static CodegenTestFiles create(@NotNull String fileName, @NotNull String contentWithDiagnosticMarkup, @NotNull Project project) {
|
||||
String content = CheckerTestUtil.parseDiagnosedRanges(contentWithDiagnosticMarkup, new ArrayList<CheckerTestUtil.DiagnosedRange>());
|
||||
KtFile file = KotlinTestUtils.createFile(fileName, content, project);
|
||||
List<PsiErrorElement> ranges = AnalyzingUtils.getSyntaxErrorRanges(file);
|
||||
assert ranges.isEmpty() : "Syntax errors found in " + file + ": " + ranges;
|
||||
|
||||
List<Pair<String, String>> expectedValues = Lists.newArrayList();
|
||||
|
||||
Matcher matcher = Pattern.compile("// expected: (\\S+): (.*)").matcher(content);
|
||||
while (matcher.find()) {
|
||||
String fieldName = matcher.group(1);
|
||||
String expectedValue = matcher.group(2);
|
||||
expectedValues.add(Pair.create(fieldName, expectedValue));
|
||||
}
|
||||
|
||||
List<ScriptParameter> scriptParameterTypes = Lists.newArrayList();
|
||||
List<Object> scriptParameterValues = Lists.newArrayList();
|
||||
|
||||
if (file.isScript()) {
|
||||
Pattern scriptParametersPattern = Pattern.compile("param: (\\S+): (\\S+): (\\S.*)");
|
||||
Matcher scriptParametersMatcher = scriptParametersPattern.matcher(file.getText());
|
||||
|
||||
while (scriptParametersMatcher.find()) {
|
||||
String name = scriptParametersMatcher.group(1);
|
||||
String type = scriptParametersMatcher.group(2);
|
||||
String valueString = scriptParametersMatcher.group(3);
|
||||
Object value;
|
||||
|
||||
KotlinType jetType;
|
||||
KotlinBuiltIns builtIns = JvmPlatform.INSTANCE.getBuiltIns();
|
||||
if (type.equals("kotlin.String")) {
|
||||
value = valueString;
|
||||
jetType = builtIns.getStringType();
|
||||
}
|
||||
else if (type.equals("kotlin.Long")) {
|
||||
value = Long.parseLong(valueString);
|
||||
jetType = builtIns.getLongType();
|
||||
}
|
||||
else if (type.equals("kotlin.Int")) {
|
||||
value = Integer.parseInt(valueString);
|
||||
jetType = builtIns.getIntType();
|
||||
}
|
||||
else if (type.equals("kotlin.Array<kotlin.String>")) {
|
||||
value = valueString.split(" ");
|
||||
jetType = builtIns.getArrayType(Variance.INVARIANT, builtIns.getStringType());
|
||||
}
|
||||
else {
|
||||
throw new AssertionError("TODO: " + type);
|
||||
}
|
||||
|
||||
scriptParameterTypes.add(new ScriptParameter(Name.identifier(name), jetType));
|
||||
scriptParameterValues.add(value);
|
||||
}
|
||||
|
||||
KotlinScriptDefinitionProvider definitionProvider = KotlinScriptDefinitionProvider.getInstance(project);
|
||||
definitionProvider.addScriptDefinition(
|
||||
new TestScriptDefinition(
|
||||
".kts",
|
||||
scriptParameterTypes
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return new CodegenTestFiles(Collections.singletonList(file), expectedValues, scriptParameterValues);
|
||||
}
|
||||
}
|
||||
@@ -1,176 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen;
|
||||
|
||||
import kotlin.collections.CollectionsKt;
|
||||
import kotlin.jvm.functions.Function1;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.analyzer.AnalysisResult;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.JvmPackagePartProvider;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
|
||||
import org.jetbrains.kotlin.cli.jvm.config.JVMConfigurationKeys;
|
||||
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime;
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration;
|
||||
import org.jetbrains.kotlin.resolve.AnalyzingUtils;
|
||||
import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.utils.ExceptionUtilsKt;
|
||||
import org.jetbrains.kotlin.utils.StringsKt;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class CodegenTestUtil {
|
||||
private CodegenTestUtil() {}
|
||||
|
||||
@NotNull
|
||||
public static ClassFileFactory generateFiles(@NotNull KotlinCoreEnvironment environment, @NotNull CodegenTestFiles files) {
|
||||
AnalysisResult analysisResult = JvmResolveUtil.analyzeFilesWithJavaIntegrationAndCheckForErrors(
|
||||
environment.getProject(),
|
||||
files.getPsiFiles(),
|
||||
new JvmPackagePartProvider(environment)
|
||||
);
|
||||
analysisResult.throwIfError();
|
||||
AnalyzingUtils.throwExceptionOnErrors(analysisResult.getBindingContext());
|
||||
CompilerConfiguration configuration = environment.getConfiguration();
|
||||
GenerationState state = new GenerationState(
|
||||
environment.getProject(),
|
||||
ClassBuilderFactories.TEST,
|
||||
analysisResult.getModuleDescriptor(),
|
||||
analysisResult.getBindingContext(),
|
||||
files.getPsiFiles(),
|
||||
configuration.get(JVMConfigurationKeys.DISABLE_CALL_ASSERTIONS, false),
|
||||
configuration.get(JVMConfigurationKeys.DISABLE_PARAM_ASSERTIONS, false),
|
||||
GenerationState.GenerateClassFilter.GENERATE_ALL,
|
||||
configuration.get(JVMConfigurationKeys.DISABLE_INLINE, false),
|
||||
configuration.get(JVMConfigurationKeys.DISABLE_OPTIMIZATION, false),
|
||||
/* useTypeTableInSerializer = */ false,
|
||||
configuration.get(JVMConfigurationKeys.INHERIT_MULTIFILE_PARTS, false)
|
||||
);
|
||||
KotlinCodegenFacade.compileCorrectFiles(state, CompilationErrorHandler.THROW_EXCEPTION);
|
||||
|
||||
// For JVM-specific errors
|
||||
AnalyzingUtils.throwExceptionOnErrors(state.getCollectedExtraJvmDiagnostics());
|
||||
|
||||
return state.getFactory();
|
||||
}
|
||||
|
||||
public static void assertThrows(@NotNull Method foo, @NotNull Class<? extends Throwable> exceptionClass,
|
||||
@Nullable Object instance, @NotNull Object... args) throws IllegalAccessException {
|
||||
boolean caught = false;
|
||||
try {
|
||||
foo.invoke(instance, args);
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
caught = exceptionClass.isInstance(ex.getTargetException());
|
||||
}
|
||||
assertTrue(caught);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Method findDeclaredMethodByName(@NotNull Class<?> aClass, @NotNull String name) {
|
||||
Method result = findDeclaredMethodByNameOrNull(aClass, name);
|
||||
if (result == null) {
|
||||
throw new AssertionError("Method " + name + " is not found in " + aClass);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Method findDeclaredMethodByNameOrNull(@NotNull Class<?> aClass, @NotNull String name) {
|
||||
for (Method method : aClass.getDeclaredMethods()) {
|
||||
if (method.getName().equals(name)) {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static File compileJava(
|
||||
@NotNull List<String> fileNames,
|
||||
@NotNull List<String> additionalClasspath,
|
||||
@NotNull List<String> additionalOptions
|
||||
) {
|
||||
try {
|
||||
File javaClassesTempDirectory = KotlinTestUtils.tmpDir("java-classes");
|
||||
List<String> classpath = new ArrayList<String>();
|
||||
classpath.add(ForTestCompileRuntime.runtimeJarForTests().getPath());
|
||||
classpath.add(ForTestCompileRuntime.reflectJarForTests().getPath());
|
||||
classpath.add(KotlinTestUtils.getAnnotationsJar().getPath());
|
||||
classpath.addAll(additionalClasspath);
|
||||
|
||||
List<String> options = new ArrayList<String>(Arrays.asList(
|
||||
"-classpath", StringsKt.join(classpath, File.pathSeparator),
|
||||
"-d", javaClassesTempDirectory.getPath()
|
||||
));
|
||||
options.addAll(additionalOptions);
|
||||
|
||||
List<File> fileList = CollectionsKt.map(fileNames, new Function1<String, File>() {
|
||||
@Override
|
||||
public File invoke(String input) {
|
||||
return new File(input);
|
||||
}
|
||||
});
|
||||
|
||||
KotlinTestUtils.compileJavaFiles(fileList, options);
|
||||
|
||||
return javaClassesTempDirectory;
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw ExceptionUtilsKt.rethrow(e);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Method findTheOnlyMethod(@NotNull Class<?> aClass) {
|
||||
Method r = null;
|
||||
for (Method method : aClass.getMethods()) {
|
||||
if (method.getDeclaringClass().equals(Object.class)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (r != null) {
|
||||
throw new AssertionError("More than one public method in class " + aClass);
|
||||
}
|
||||
|
||||
r = method;
|
||||
}
|
||||
if (r == null) {
|
||||
throw new AssertionError("No public methods in class " + aClass);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Object getAnnotationAttribute(@NotNull Object annotation, @NotNull String name) {
|
||||
try {
|
||||
return annotation.getClass().getMethod(name).invoke(annotation);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw ExceptionUtilsKt.rethrow(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen;
|
||||
|
||||
import com.android.dx.cf.direct.DirectClassFile;
|
||||
import com.android.dx.cf.direct.StdAttributeFactory;
|
||||
import com.android.dx.command.dexer.Main;
|
||||
import com.android.dx.dex.cf.CfTranslator;
|
||||
import com.android.dx.dex.file.DexFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.backend.common.output.OutputFile;
|
||||
import org.junit.Assert;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class DxChecker {
|
||||
|
||||
public static final boolean RUN_DX_CHECKER = true;
|
||||
private static final Pattern STACK_TRACE_PATTERN = Pattern.compile("[\\s]+at .*");
|
||||
|
||||
private DxChecker() {
|
||||
}
|
||||
|
||||
public static void check(ClassFileFactory outputFiles) {
|
||||
Main.Arguments arguments = new Main.Arguments();
|
||||
String[] array = new String[1];
|
||||
array[0] = "testArgs";
|
||||
arguments.parse(array);
|
||||
|
||||
for (OutputFile file : ClassFileUtilsKt.getClassFiles(outputFiles)) {
|
||||
try {
|
||||
byte[] bytes = file.asByteArray();
|
||||
checkFileWithDx(bytes, file.getRelativePath(), arguments);
|
||||
}
|
||||
catch (Throwable e) {
|
||||
Assert.fail(generateExceptionMessage(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkFileWithDx(byte[] bytes, @NotNull String relativePath) {
|
||||
Main.Arguments arguments = new Main.Arguments();
|
||||
String[] array = new String[1];
|
||||
array[0] = "testArgs";
|
||||
arguments.parse(array);
|
||||
checkFileWithDx(bytes, relativePath, arguments);
|
||||
}
|
||||
|
||||
private static void checkFileWithDx(byte[] bytes, @NotNull String relativePath, @NotNull Main.Arguments arguments) {
|
||||
DirectClassFile cf = new DirectClassFile(bytes, relativePath, true);
|
||||
cf.setAttributeFactory(StdAttributeFactory.THE_ONE);
|
||||
CfTranslator.translate(
|
||||
cf,
|
||||
bytes,
|
||||
arguments.cfOptions,
|
||||
arguments.dexOptions,
|
||||
new DexFile(arguments.dexOptions)
|
||||
);
|
||||
}
|
||||
|
||||
private static String generateExceptionMessage(Throwable e) {
|
||||
StringWriter writer = new StringWriter();
|
||||
PrintWriter printWriter = new PrintWriter(writer);
|
||||
try {
|
||||
e.printStackTrace(printWriter);
|
||||
String stackTrace = writer.toString();
|
||||
Matcher matcher = STACK_TRACE_PATTERN.matcher(stackTrace);
|
||||
return matcher.replaceAll("");
|
||||
}
|
||||
finally {
|
||||
printWriter.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.analyzer.AnalysisResult;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.JvmPackagePartProvider;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
|
||||
import org.jetbrains.kotlin.cli.jvm.config.JVMConfigurationKeys;
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration;
|
||||
import org.jetbrains.kotlin.config.CompilerConfigurationKey;
|
||||
import org.jetbrains.kotlin.descriptors.PackagePartProvider;
|
||||
import org.jetbrains.kotlin.psi.KtFile;
|
||||
import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class GenerationUtils {
|
||||
|
||||
private GenerationUtils() {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static ClassFileFactory compileFileGetClassFileFactoryForTest(
|
||||
@NotNull KtFile psiFile,
|
||||
@NotNull KotlinCoreEnvironment environment
|
||||
) {
|
||||
return compileFileGetGenerationStateForTest(psiFile, environment).getFactory();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static GenerationState compileFileGetGenerationStateForTest(
|
||||
@NotNull KtFile psiFile,
|
||||
@NotNull KotlinCoreEnvironment environment
|
||||
) {
|
||||
AnalysisResult analysisResult =
|
||||
JvmResolveUtil.analyzeOneFileWithJavaIntegrationAndCheckForErrors(psiFile, new JvmPackagePartProvider(environment));
|
||||
return compileFilesGetGenerationState(psiFile.getProject(), analysisResult, Collections.singletonList(psiFile), false, null);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static GenerationState compileManyFilesGetGenerationStateForTest(@NotNull Project project, @NotNull List<KtFile> files) {
|
||||
return compileManyFilesGetGenerationStateForTest(project, files, PackagePartProvider.Companion.getEMPTY(), null);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static GenerationState compileManyFilesGetGenerationStateForTest(
|
||||
@NotNull Project project,
|
||||
@NotNull List<KtFile> files,
|
||||
@NotNull PackagePartProvider packagePartProvider,
|
||||
@Nullable CompilerConfiguration configuration
|
||||
) {
|
||||
AnalysisResult analysisResult = JvmResolveUtil.analyzeFilesWithJavaIntegrationAndCheckForErrors(
|
||||
project, files, packagePartProvider);
|
||||
return compileFilesGetGenerationState(project, analysisResult, files, false, configuration);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static GenerationState compileFilesGetGenerationState(
|
||||
@NotNull Project project,
|
||||
@NotNull AnalysisResult analysisResult,
|
||||
@NotNull List<KtFile> files,
|
||||
boolean useTypeTableInSerializer
|
||||
) {
|
||||
return compileFilesGetGenerationState(project, analysisResult, files, useTypeTableInSerializer, null);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static GenerationState compileFilesGetGenerationState(
|
||||
@NotNull Project project,
|
||||
@NotNull AnalysisResult analysisResult,
|
||||
@NotNull List<KtFile> files,
|
||||
boolean useTypeTableInSerializer,
|
||||
@Nullable CompilerConfiguration configuration
|
||||
) {
|
||||
analysisResult.throwIfError();
|
||||
GenerationState state = new GenerationState(
|
||||
project, ClassBuilderFactories.TEST,
|
||||
analysisResult.getModuleDescriptor(), analysisResult.getBindingContext(),
|
||||
files,
|
||||
getConfigurationValueOrDefault(configuration, JVMConfigurationKeys.DISABLE_CALL_ASSERTIONS, false),
|
||||
getConfigurationValueOrDefault(configuration, JVMConfigurationKeys.DISABLE_PARAM_ASSERTIONS, false),
|
||||
GenerationState.GenerateClassFilter.GENERATE_ALL,
|
||||
getConfigurationValueOrDefault(configuration, JVMConfigurationKeys.DISABLE_INLINE, false),
|
||||
getConfigurationValueOrDefault(configuration, JVMConfigurationKeys.DISABLE_OPTIMIZATION, false),
|
||||
useTypeTableInSerializer,
|
||||
getConfigurationValueOrDefault(configuration, JVMConfigurationKeys.INHERIT_MULTIFILE_PARTS, false)
|
||||
);
|
||||
KotlinCodegenFacade.compileCorrectFiles(state, CompilationErrorHandler.THROW_EXCEPTION);
|
||||
return state;
|
||||
}
|
||||
|
||||
private static <T> T getConfigurationValueOrDefault(
|
||||
@Nullable CompilerConfiguration configuration,
|
||||
@NotNull CompilerConfigurationKey<T> key,
|
||||
T defaultValue
|
||||
) {
|
||||
if (configuration == null) return defaultValue;
|
||||
return configuration.get(key, defaultValue);
|
||||
}
|
||||
}
|
||||
@@ -1,211 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen;
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.output.OutputFile
|
||||
import org.jetbrains.kotlin.inline.inlineFunctionsJvmNames
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
|
||||
import org.jetbrains.kotlin.load.kotlin.FileBasedKotlinClass
|
||||
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass
|
||||
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||
import org.jetbrains.org.objectweb.asm.*
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
import java.util.*
|
||||
|
||||
object InlineTestUtil {
|
||||
fun checkNoCallsToInline(files: Iterable<OutputFile>, sourceFiles: List<KtFile>) {
|
||||
val inlineInfo = obtainInlineInfo(files)
|
||||
val inlineMethods = inlineInfo.inlineMethods
|
||||
assert(!inlineMethods.isEmpty()) { "There are no inline methods" }
|
||||
|
||||
val notInlinedCalls = checkInlineMethodNotInvoked(files, inlineMethods)
|
||||
assert(notInlinedCalls.isEmpty()) { "All inline methods should be inlined but:\n" + notInlinedCalls.joinToString("\n") }
|
||||
|
||||
val skipParameterChecking = sourceFiles.any {
|
||||
InTextDirectivesUtils.isDirectiveDefined(it.text, "NO_CHECK_LAMBDA_INLINING")
|
||||
}
|
||||
|
||||
if (!skipParameterChecking) {
|
||||
val notInlinedParameters = checkParametersInlined(files, inlineInfo)
|
||||
assert(notInlinedParameters.isEmpty()) {
|
||||
"All inline parameters should be inlined but:\n${notInlinedParameters.joinToString("\n")}\n" +
|
||||
"but if you have not inlined lambdas or anonymous objects enable NO_CHECK_LAMBDA_INLINING directive"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun obtainInlineInfo(files: Iterable<OutputFile>): InlineInfo {
|
||||
val inlineMethods = HashSet<MethodInfo>()
|
||||
val binaryClasses = hashMapOf<String, KotlinJvmBinaryClass>()
|
||||
|
||||
for (file in files) {
|
||||
val bytes = file.asByteArray()
|
||||
val cr = ClassReader(bytes)
|
||||
|
||||
val inlineFunctions = inlineFunctionsJvmNames(bytes)
|
||||
|
||||
val classVisitor = object : ClassVisitorWithName() {
|
||||
override fun visitMethod(access: Int, name: String, desc: String, signature: String?, exceptions: Array<String>?): MethodVisitor {
|
||||
return object : MethodNode(Opcodes.ASM5, access, name, desc, signature, exceptions) {
|
||||
override fun visitEnd() {
|
||||
if (name + desc in inlineFunctions) {
|
||||
inlineMethods.add(MethodInfo(className, name, this.desc))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cr.accept(classVisitor, 0)
|
||||
binaryClasses.put(classVisitor.className, loadBinaryClass(file))
|
||||
}
|
||||
|
||||
return InlineInfo(inlineMethods, binaryClasses)
|
||||
}
|
||||
|
||||
private fun checkInlineMethodNotInvoked(files: Iterable<OutputFile>, inlinedMethods: Set<MethodInfo>): List<NotInlinedCall> {
|
||||
val notInlined = ArrayList<NotInlinedCall>()
|
||||
|
||||
files.forEach { file ->
|
||||
ClassReader(file.asByteArray()).accept(object : ClassVisitorWithName() {
|
||||
private var skipMethodsOfThisClass = false
|
||||
|
||||
override fun visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor? {
|
||||
if (desc == JvmAnnotationNames.METADATA_DESC) {
|
||||
return object : AnnotationVisitor(Opcodes.ASM5) {
|
||||
override fun visit(name: String?, value: Any) {
|
||||
if (name == JvmAnnotationNames.KIND_FIELD_NAME && value == KotlinClassHeader.Kind.MULTIFILE_CLASS.id) {
|
||||
skipMethodsOfThisClass = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
override fun visitMethod(access: Int, name: String, desc: String, signature: String, exceptions: Array<String>): MethodVisitor? {
|
||||
if (skipMethodsOfThisClass) {
|
||||
return null
|
||||
}
|
||||
|
||||
return object : MethodNode(Opcodes.ASM5, access, name, desc, signature, exceptions) {
|
||||
override fun visitMethodInsn(opcode: Int, owner: String, name: String, desc: String, itf: Boolean) {
|
||||
val methodCall = MethodInfo(owner, name, desc)
|
||||
if (inlinedMethods.contains(methodCall)) {
|
||||
val fromCall = MethodInfo(className, this.name, this.desc)
|
||||
|
||||
//skip delegation to interface DefaultImpls from child class
|
||||
if (methodCall.owner.endsWith(JvmAbi.DEFAULT_IMPLS_SUFFIX) && fromCall.owner != methodCall.owner) {
|
||||
return
|
||||
}
|
||||
notInlined.add(NotInlinedCall(fromCall, methodCall))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 0)
|
||||
}
|
||||
|
||||
return notInlined
|
||||
}
|
||||
|
||||
private fun checkParametersInlined(files: Iterable<OutputFile>, inlineInfo: InlineInfo): ArrayList<NotInlinedParameter> {
|
||||
val inlinedMethods = inlineInfo.inlineMethods
|
||||
val notInlinedParameters = ArrayList<NotInlinedParameter>()
|
||||
for (file in files) {
|
||||
if (!isClassOrPackagePartKind(loadBinaryClass(file))) continue
|
||||
|
||||
ClassReader(file.asByteArray()).accept(object : ClassVisitorWithName() {
|
||||
override fun visitMethod(access: Int, name: String, desc: String, signature: String?, exceptions: Array<String>?): MethodVisitor? {
|
||||
val declaration = MethodInfo(className, name, desc)
|
||||
//do not check anonymous object creation in inline functions and in package facades
|
||||
if (declaration in inlinedMethods) {
|
||||
return null
|
||||
}
|
||||
|
||||
return object : MethodNode(Opcodes.ASM5, access, name, desc, signature, exceptions) {
|
||||
private fun isInlineParameterLikeOwner(owner: String) =
|
||||
"$" in owner && !isTopLevelOrInnerOrPackageClass(owner, inlineInfo)
|
||||
|
||||
override fun visitMethodInsn(opcode: Int, owner: String, name: String, desc: String, itf: Boolean) {
|
||||
if ("<init>".equals(name) && isInlineParameterLikeOwner(owner)) {
|
||||
val fromCall = MethodInfo(className, this.name, this.desc)
|
||||
notInlinedParameters.add(NotInlinedParameter(owner, fromCall))
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitFieldInsn(opcode: Int, owner: String, name: String, desc: String) {
|
||||
if (opcode == Opcodes.GETSTATIC && isInlineParameterLikeOwner(owner)) {
|
||||
val fromCall = MethodInfo(className, this.name, this.desc)
|
||||
notInlinedParameters.add(NotInlinedParameter(owner, fromCall))
|
||||
}
|
||||
super.visitFieldInsn(opcode, owner, name, desc)
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 0)
|
||||
}
|
||||
|
||||
return notInlinedParameters
|
||||
}
|
||||
|
||||
private fun isTopLevelOrInnerOrPackageClass(classInternalName: String, inlineInfo: InlineInfo): Boolean {
|
||||
if (classInternalName.startsWith("kotlin/jvm/internal/"))
|
||||
return true
|
||||
|
||||
return isClassOrPackagePartKind(inlineInfo.binaryClasses[classInternalName]!!)
|
||||
}
|
||||
|
||||
private fun isClassOrPackagePartKind(klass: KotlinJvmBinaryClass): Boolean {
|
||||
return klass.classHeader.kind == KotlinClassHeader.Kind.CLASS && !klass.classId.isLocal
|
||||
}
|
||||
|
||||
private fun loadBinaryClass(file: OutputFile): KotlinJvmBinaryClass {
|
||||
val klass = FileBasedKotlinClass.create(file.asByteArray()) {
|
||||
className, classHeader, innerClasses ->
|
||||
object : FileBasedKotlinClass(className, classHeader, innerClasses) {
|
||||
override fun getLocation(): String = throw UnsupportedOperationException()
|
||||
override fun getFileContents(): ByteArray = throw UnsupportedOperationException()
|
||||
override fun hashCode(): Int = throw UnsupportedOperationException()
|
||||
override fun equals(other: Any?): Boolean = throw UnsupportedOperationException()
|
||||
override fun toString(): String = throw UnsupportedOperationException()
|
||||
}
|
||||
}!!
|
||||
return klass
|
||||
}
|
||||
|
||||
private class InlineInfo(val inlineMethods: Set<MethodInfo>, val binaryClasses: Map<String, KotlinJvmBinaryClass>)
|
||||
|
||||
private data class NotInlinedCall(val fromCall: MethodInfo, val inlineMethod: MethodInfo)
|
||||
|
||||
private data class NotInlinedParameter(val parameterClassName: String, val fromCall: MethodInfo)
|
||||
|
||||
private data class MethodInfo(val owner: String, val name: String, val desc: String)
|
||||
|
||||
private open class ClassVisitorWithName : ClassVisitor(Opcodes.ASM5) {
|
||||
lateinit var className: String
|
||||
|
||||
override fun visit(version: Int, access: Int, name: String, signature: String?, superName: String?, interfaces: Array<String>?) {
|
||||
className = name
|
||||
super.visit(version, access, name, signature, superName, interfaces)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,11 +17,11 @@
|
||||
package org.jetbrains.kotlin.codegen
|
||||
|
||||
import junit.framework.TestCase
|
||||
import org.jetbrains.kotlin.test.ConfigurationKind
|
||||
import org.jetbrains.org.objectweb.asm.ClassReader
|
||||
import org.jetbrains.org.objectweb.asm.ClassVisitor
|
||||
import org.jetbrains.org.objectweb.asm.MethodVisitor
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.kotlin.test.ConfigurationKind
|
||||
import java.util.*
|
||||
|
||||
class MethodOrderTest: CodegenTestCase() {
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import org.jetbrains.kotlin.backend.common.output.OutputFile
|
||||
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil
|
||||
import org.jetbrains.org.objectweb.asm.ClassReader
|
||||
import org.jetbrains.org.objectweb.asm.ClassVisitor
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.junit.Assert
|
||||
import java.io.File
|
||||
import java.io.StringReader
|
||||
|
||||
object SMAPTestUtil {
|
||||
private fun extractSMAPFromClasses(outputFiles: Iterable<OutputFile>): List<SMAPAndFile> {
|
||||
return outputFiles.mapNotNull { outputFile ->
|
||||
var debugInfo: String? = null
|
||||
ClassReader(outputFile.asByteArray()).accept(object : ClassVisitor(Opcodes.ASM5) {
|
||||
override fun visitSource(source: String?, debug: String?) {
|
||||
debugInfo = debug
|
||||
}
|
||||
}, 0)
|
||||
|
||||
SMAPAndFile.SMAPAndFile(debugInfo, outputFile.sourceFiles.single())
|
||||
}
|
||||
}
|
||||
|
||||
private fun extractSmapFromTestDataFile(file: CodegenTestCase.TestFile): SMAPAndFile? {
|
||||
if (!file.name.endsWith(".smap")) return null
|
||||
|
||||
val content = buildString {
|
||||
StringReader(file.content).forEachLine { line ->
|
||||
// Strip comments
|
||||
if (!line.startsWith("//")) {
|
||||
appendln(line.trim())
|
||||
}
|
||||
}
|
||||
}.trim()
|
||||
|
||||
return SMAPAndFile(if (content.isNotEmpty()) content else null, SMAPAndFile.getPath(file.name))
|
||||
}
|
||||
|
||||
fun checkSMAP(inputFiles: List<CodegenTestCase.TestFile>, outputFiles: Iterable<OutputFile>) {
|
||||
if (!InlineCodegenUtil.GENERATE_SMAP) return
|
||||
|
||||
val sourceData = inputFiles.mapNotNull { extractSmapFromTestDataFile(it) }
|
||||
val compiledData = extractSMAPFromClasses(outputFiles).groupBy {
|
||||
it.sourceFile
|
||||
}.map {
|
||||
val smap = it.value.mapNotNull { it.smap }.joinToString("\n")
|
||||
SMAPAndFile(if (smap.isNotEmpty()) smap else null, it.key)
|
||||
}.associateBy { it.sourceFile }
|
||||
|
||||
for (source in sourceData) {
|
||||
val ktFileName = "/" + source.sourceFile.replace(".smap", ".kt")
|
||||
val data = compiledData[ktFileName]
|
||||
Assert.assertEquals("Smap data differs for $ktFileName", normalize(source.smap), normalize(data?.smap))
|
||||
}
|
||||
}
|
||||
|
||||
private fun normalize(text: String?) =
|
||||
text?.let { StringUtil.convertLineSeparators(it.trim()) }
|
||||
|
||||
private class SMAPAndFile(val smap: String?, val sourceFile: String) {
|
||||
companion object {
|
||||
fun SMAPAndFile(smap: String?, sourceFile: File) = SMAPAndFile(smap, getPath(sourceFile))
|
||||
|
||||
fun getPath(file: File): String {
|
||||
return getPath(file.canonicalPath)
|
||||
}
|
||||
|
||||
fun getPath(canonicalPath: String): String {
|
||||
//There are some problems with disk name on windows cause LightVirtualFile return it without disk name
|
||||
return FileUtil.toSystemIndependentName(canonicalPath).substringAfter(":")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-72
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.defaultConstructor;
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import org.jetbrains.kotlin.codegen.CodegenTestCase;
|
||||
import org.jetbrains.kotlin.test.ConfigurationKind;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.List;
|
||||
|
||||
import static org.jetbrains.kotlin.test.InTextDirectivesUtils.findListWithPrefixes;
|
||||
|
||||
public abstract class AbstractDefaultArgumentsReflectionTest extends CodegenTestCase {
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
createEnvironmentWithMockJdkAndIdeaAnnotations(ConfigurationKind.JDK_ONLY);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doTest(String path) throws IOException {
|
||||
loadFileByFullPath(path);
|
||||
|
||||
String fileText = FileUtil.loadFile(new File(path), true);
|
||||
String className = loadInstructionValue(fileText, "CLASS");
|
||||
boolean hasDefaultConstructor = loadInstructionValue(fileText, "HAS_DEFAULT_CONSTRUCTOR").equals("true");
|
||||
|
||||
Class<?> aClass = generateClass(className);
|
||||
assertNotNull("Cannot find class with name " + className, aClass);
|
||||
try {
|
||||
Constructor constructor = aClass.getDeclaredConstructor();
|
||||
if (!hasDefaultConstructor) {
|
||||
System.out.println(generateToText());
|
||||
throw new AssertionError("Default constructor was found but it wasn't expected: " + constructor);
|
||||
}
|
||||
}
|
||||
catch (NoSuchMethodException e) {
|
||||
if (hasDefaultConstructor) {
|
||||
System.out.println(generateToText());
|
||||
throw new AssertionError("Cannot find default constructor");
|
||||
}
|
||||
}
|
||||
catch (Throwable e) {
|
||||
System.out.println(generateToText());
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static String loadInstructionValue(String fileContent, String instructionName) {
|
||||
List<String> testedClass = findListWithPrefixes(fileContent, "// " + instructionName + ": ");
|
||||
assertTrue("Cannot find " + instructionName + " instruction", !testedClass.isEmpty());
|
||||
assertTrue(instructionName + " instruction must have only one element", testedClass.size() == 1);
|
||||
return testedClass.get(0);
|
||||
}
|
||||
}
|
||||
@@ -1,302 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.flags;
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.backend.common.output.OutputFile;
|
||||
import org.jetbrains.kotlin.backend.common.output.OutputFileCollection;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
|
||||
import org.jetbrains.kotlin.codegen.GenerationUtils;
|
||||
import org.jetbrains.kotlin.psi.KtFile;
|
||||
import org.jetbrains.kotlin.test.ConfigurationKind;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase;
|
||||
import org.jetbrains.org.objectweb.asm.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.jetbrains.kotlin.test.InTextDirectivesUtils.findListWithPrefixes;
|
||||
import static org.jetbrains.kotlin.test.InTextDirectivesUtils.findStringWithPrefixes;
|
||||
|
||||
/*
|
||||
* Test correctness of written flags in class file
|
||||
*
|
||||
* TESTED_OBJECT_KIND - maybe class, function or property
|
||||
* TESTED_OBJECTS - className, [function/property name]
|
||||
* FLAGS - only flags which must be true (could be skipped if ABSENT is TRUE)
|
||||
* ABSENT - true or false, optional (false by default)
|
||||
*
|
||||
* There is could be specified several tested objects separated by empty line, e.g:
|
||||
* TESTED_OBJECT_KIND: property
|
||||
* TESTED_OBJECTS: Test$object, prop
|
||||
* ABSENT: TRUE
|
||||
*
|
||||
* TESTED_OBJECT_KIND: property
|
||||
* TESTED_OBJECTS: Test, prop$delegate
|
||||
* FLAGS: ACC_STATIC, ACC_FINAL, ACC_PRIVATE
|
||||
*/
|
||||
public abstract class AbstractWriteFlagsTest extends KtUsefulTestCase {
|
||||
|
||||
private KotlinCoreEnvironment jetCoreEnvironment;
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
jetCoreEnvironment = KotlinTestUtils.createEnvironmentWithMockJdkAndIdeaAnnotations(myTestRootDisposable, ConfigurationKind.JDK_ONLY);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
jetCoreEnvironment = null;
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
protected void doTest(String path) throws Exception {
|
||||
File ktFile = new File(path);
|
||||
assertTrue("Cannot find a file " + ktFile.getAbsolutePath(), ktFile.exists());
|
||||
|
||||
String fileText = FileUtil.loadFile(ktFile, true);
|
||||
|
||||
KtFile psiFile = KotlinTestUtils.createFile(ktFile.getName(), fileText, jetCoreEnvironment.getProject());
|
||||
|
||||
OutputFileCollection outputFiles = GenerationUtils.compileFileGetClassFileFactoryForTest(psiFile, jetCoreEnvironment);
|
||||
|
||||
List<TestedObject> testedObjects = parseExpectedTestedObject(fileText);
|
||||
for (TestedObject testedObject : testedObjects) {
|
||||
String className = null;
|
||||
for (OutputFile outputFile : outputFiles.asList()) {
|
||||
String filePath = outputFile.getRelativePath();
|
||||
if (testedObject.isFullContainingClassName && filePath.equals(testedObject.containingClass + ".class")) {
|
||||
className = filePath;
|
||||
}
|
||||
else if (!testedObject.isFullContainingClassName && filePath.startsWith(testedObject.containingClass)) {
|
||||
className = filePath;
|
||||
}
|
||||
}
|
||||
|
||||
assertNotNull("Couldn't find a class file with name " + testedObject.containingClass, className);
|
||||
|
||||
OutputFile outputFile = outputFiles.get(className);
|
||||
assertNotNull(outputFile);
|
||||
|
||||
ClassReader cr = new ClassReader(outputFile.asByteArray());
|
||||
TestClassVisitor classVisitor = getClassVisitor(testedObject.kind, testedObject.name, false);
|
||||
cr.accept(classVisitor, ClassReader.SKIP_CODE);
|
||||
|
||||
if (!classVisitor.isExists()) {
|
||||
classVisitor = getClassVisitor(testedObject.kind, testedObject.name, true);
|
||||
cr.accept(classVisitor, ClassReader.SKIP_CODE);
|
||||
}
|
||||
|
||||
boolean isObjectExists = !Boolean.valueOf(findStringWithPrefixes(testedObject.textData, "// ABSENT: "));
|
||||
assertEquals("Wrong object existence state: " + testedObject, isObjectExists, classVisitor.isExists());
|
||||
|
||||
if (isObjectExists) {
|
||||
assertEquals("Wrong access flag for " + testedObject + " \n" + outputFile.asText(),
|
||||
getExpectedFlags(testedObject.textData), classVisitor.getAccess());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static List<TestedObject> parseExpectedTestedObject(String testDescription) {
|
||||
String[] testObjectData = testDescription.substring(testDescription.indexOf("// TESTED_OBJECT_KIND")).split("\n\n");
|
||||
ArrayList<TestedObject> objects = new ArrayList<TestedObject>();
|
||||
|
||||
for (String testData : testObjectData) {
|
||||
if (testData.isEmpty()) continue;
|
||||
|
||||
TestedObject testObject = new TestedObject();
|
||||
testObject.textData = testData;
|
||||
List<String> testedObjects = findListWithPrefixes(testData, "// TESTED_OBJECTS: ");
|
||||
assertTrue("Cannot find TESTED_OBJECTS instruction", !testedObjects.isEmpty());
|
||||
testObject.containingClass = testedObjects.get(0);
|
||||
if (testedObjects.size() == 1) {
|
||||
testObject.name = testedObjects.get(0);
|
||||
}
|
||||
else if (testedObjects.size() == 2) {
|
||||
testObject.name = testedObjects.get(1);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException(
|
||||
"TESTED_OBJECTS instruction must contain one (for class) or two (for function and property) values");
|
||||
}
|
||||
|
||||
testObject.kind = findStringWithPrefixes(testData, "// TESTED_OBJECT_KIND: ");
|
||||
List<String> isFullName = findListWithPrefixes(testData, "// IS_FULL_CONTAINING_CLASS_NAME: ");
|
||||
if (isFullName.size() == 1) {
|
||||
testObject.isFullContainingClassName = Boolean.parseBoolean(isFullName.get(0));
|
||||
}
|
||||
objects.add(testObject);
|
||||
}
|
||||
assertTrue("Test description not present!", !objects.isEmpty());
|
||||
return objects;
|
||||
}
|
||||
|
||||
private static class TestedObject {
|
||||
public String name;
|
||||
public String containingClass = "";
|
||||
public boolean isFullContainingClassName = true;
|
||||
public String kind;
|
||||
public String textData;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Class = " + containingClass + ", name = " + name + ", kind = " + kind;
|
||||
}
|
||||
}
|
||||
|
||||
private static TestClassVisitor getClassVisitor(String visitorKind, String testedObjectName, boolean allowSynthetic) {
|
||||
if (visitorKind.equals("class")) {
|
||||
return new ClassFlagsVisitor();
|
||||
}
|
||||
else if (visitorKind.equals("function")) {
|
||||
return new FunctionFlagsVisitor(testedObjectName, allowSynthetic);
|
||||
}
|
||||
else if (visitorKind.equals("property")) {
|
||||
return new PropertyFlagsVisitor(testedObjectName);
|
||||
}
|
||||
else if (visitorKind.equals("innerClass")) {
|
||||
return new InnerClassFlagsVisitor(testedObjectName);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Value of TESTED_OBJECT_KIND is incorrect: " + visitorKind);
|
||||
}
|
||||
|
||||
protected static abstract class TestClassVisitor extends ClassVisitor {
|
||||
|
||||
protected boolean isExists;
|
||||
|
||||
public TestClassVisitor() {
|
||||
super(Opcodes.ASM5);
|
||||
}
|
||||
|
||||
abstract public int getAccess();
|
||||
|
||||
public boolean isExists() {
|
||||
return isExists;
|
||||
}
|
||||
}
|
||||
|
||||
private static int getExpectedFlags(String text) {
|
||||
int expectedAccess = 0;
|
||||
Class klass = Opcodes.class;
|
||||
List<String> flags = findListWithPrefixes(text, "// FLAGS: ");
|
||||
for (String flag : flags) {
|
||||
try {
|
||||
Field field = klass.getDeclaredField(flag);
|
||||
expectedAccess |= field.getInt(klass);
|
||||
}
|
||||
catch (NoSuchFieldException e) {
|
||||
throw new IllegalArgumentException("Cannot find " + flag + " field in Opcodes class", e);
|
||||
}
|
||||
catch (IllegalAccessException e) {
|
||||
throw new IllegalArgumentException("Cannot find " + flag + " field in Opcodes class", e);
|
||||
}
|
||||
}
|
||||
return expectedAccess;
|
||||
}
|
||||
|
||||
private static class ClassFlagsVisitor extends TestClassVisitor {
|
||||
private int access = 0;
|
||||
|
||||
@Override
|
||||
public void visit(int version, int access, @NotNull String name, String signature, String superName, String[] interfaces) {
|
||||
this.access = access;
|
||||
isExists = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAccess() {
|
||||
return access;
|
||||
}
|
||||
}
|
||||
|
||||
private static class FunctionFlagsVisitor extends TestClassVisitor {
|
||||
private int access = 0;
|
||||
private final String funName;
|
||||
private final boolean allowSynthetic;
|
||||
|
||||
public FunctionFlagsVisitor(String name, boolean allowSynthetic) {
|
||||
funName = name;
|
||||
this.allowSynthetic = allowSynthetic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, @NotNull String name, @NotNull String desc, String signature, String[] exceptions) {
|
||||
if (name.equals(funName)) {
|
||||
if (!allowSynthetic && (access & Opcodes.ACC_SYNTHETIC) != 0) return null;
|
||||
this.access = access;
|
||||
isExists = true;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAccess() {
|
||||
return access;
|
||||
}
|
||||
}
|
||||
|
||||
private static class PropertyFlagsVisitor extends TestClassVisitor {
|
||||
private int access = 0;
|
||||
private final String propertyName;
|
||||
|
||||
public PropertyFlagsVisitor(String name) {
|
||||
propertyName = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldVisitor visitField(int access, @NotNull String name, @NotNull String desc, String signature, Object value) {
|
||||
if (name.equals(propertyName)) {
|
||||
this.access = access;
|
||||
isExists = true;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAccess() {
|
||||
return access;
|
||||
}
|
||||
}
|
||||
|
||||
private static class InnerClassFlagsVisitor extends TestClassVisitor {
|
||||
private int access = 0;
|
||||
private final String innerClassName;
|
||||
|
||||
public InnerClassFlagsVisitor(String name) {
|
||||
innerClassName = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInnerClass(@NotNull String innerClassInternalName, String outerClassInternalName, String name, int access) {
|
||||
if (name.equals(innerClassName)) {
|
||||
this.access = access;
|
||||
isExists = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAccess() {
|
||||
return access;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.forTestCompile;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.utils.ExceptionUtilsKt;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ForTestCompileRuntime {
|
||||
private static volatile SoftReference<ClassLoader> reflectJarClassLoader = new SoftReference<ClassLoader>(null);
|
||||
private static volatile SoftReference<ClassLoader> runtimeJarClassLoader = new SoftReference<ClassLoader>(null);
|
||||
|
||||
@NotNull
|
||||
public static File runtimeJarForTests() {
|
||||
return assertExists(new File("dist/kotlinc/lib/kotlin-runtime.jar"));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static File mockRuntimeJarForTests() {
|
||||
return assertExists(new File("dist/kotlin-mock-runtime-for-test.jar"));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static File kotlinTestJarForTests() {
|
||||
return assertExists(new File("dist/kotlinc/lib/kotlin-test.jar"));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static File reflectJarForTests() {
|
||||
return assertExists(new File("dist/kotlinc/lib/kotlin-reflect.jar"));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static File assertExists(@NotNull File file) {
|
||||
if (!file.exists()) {
|
||||
throw new IllegalStateException(file + " does not exist. Run 'ant dist'");
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static synchronized ClassLoader runtimeAndReflectJarClassLoader() {
|
||||
ClassLoader loader = reflectJarClassLoader.get();
|
||||
if (loader == null) {
|
||||
loader = createClassLoader(runtimeJarForTests(), reflectJarForTests(), kotlinTestJarForTests());
|
||||
reflectJarClassLoader = new SoftReference<ClassLoader>(loader);
|
||||
}
|
||||
return loader;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static synchronized ClassLoader runtimeJarClassLoader() {
|
||||
ClassLoader loader = runtimeJarClassLoader.get();
|
||||
if (loader == null) {
|
||||
loader = createClassLoader(runtimeJarForTests());
|
||||
runtimeJarClassLoader = new SoftReference<ClassLoader>(loader);
|
||||
}
|
||||
return loader;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static ClassLoader createClassLoader(@NotNull File... files) {
|
||||
try {
|
||||
List<URL> urls = new ArrayList<URL>(2);
|
||||
for (File file : files) {
|
||||
urls.add(file.toURI().toURL());
|
||||
}
|
||||
return new URLClassLoader(urls.toArray(new URL[urls.size()]), null);
|
||||
}
|
||||
catch (MalformedURLException e) {
|
||||
throw ExceptionUtilsKt.rethrow(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.daemon
|
||||
|
||||
import kotlin.text.MatchResult
|
||||
import kotlin.text.Regex
|
||||
|
||||
/**
|
||||
* holder of a [regex] and optional [matchCheck] for additional checks on match result
|
||||
*/
|
||||
class LinePattern(val regex: Regex, val matchCheck: (MatchResult) -> Boolean = { true })
|
||||
fun LinePattern(regex: String, matchCheck: (MatchResult) -> Boolean = { true }) = LinePattern(regex.toRegex(), matchCheck)
|
||||
|
||||
/**
|
||||
* calls [body] if receiver does not contain complete sequence of lines matched by [patternsIter], separated by any number of other lines
|
||||
* [body] receives first unmatched pattern and index of last matched line in the sequence
|
||||
*/
|
||||
fun Sequence<String>.ifNotContainsSequence(patternsIter: Iterator<LinePattern>,
|
||||
body: (LinePattern, Int) -> Unit) : Unit {
|
||||
class Accumulator(it: Iterator<LinePattern>) {
|
||||
val iter = EndBoundIteratorWithValue(it)
|
||||
var lineNo = 1
|
||||
var lastMatchedLineNo = 0
|
||||
fun nextLineAndPattern(): Accumulator { iter.traverseNext(); lastMatchedLineNo = lineNo; return nextLine() }
|
||||
fun nextLine(): Accumulator { lineNo++; return this }
|
||||
}
|
||||
val res = fold(Accumulator(patternsIter))
|
||||
{ acc, line ->
|
||||
when {
|
||||
!acc.iter.isValid() -> return@fold acc
|
||||
acc.iter.value.regex.find(line)?.let { acc.iter.value.matchCheck(it) } ?: false -> acc.nextLineAndPattern()
|
||||
else -> acc.nextLine()
|
||||
}
|
||||
}
|
||||
if (res.iter.isValid()) {
|
||||
body(res.iter.value, res.lastMatchedLineNo)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* calls [body] if receiver does not contain complete sequence of lines matched by [patterns], separated by any number of other lines
|
||||
* [body] receives first unmatched pattern and index of last matched line in the sequence
|
||||
*/
|
||||
fun Sequence<String>.ifNotContainsSequence(patterns: List<LinePattern>,
|
||||
body: (LinePattern, Int) -> Unit): Unit {
|
||||
ifNotContainsSequence(patterns.iterator(), body)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* calls [body] if receiver does not contain complete sequence of lines matched by [patterns], separated by any number of other lines
|
||||
* [body] receives first unmatched pattern and index of last matched line in the sequence
|
||||
*/
|
||||
fun Sequence<String>.ifNotContainsSequence(vararg patterns: LinePattern,
|
||||
body: (LinePattern, Int) -> Unit): Unit {
|
||||
ifNotContainsSequence(patterns.iterator(), body)
|
||||
}
|
||||
|
||||
|
||||
// emulates Stepanov's / STL iterator, but with "embedded" end check via isValid:
|
||||
// iterator points to a current value and upon init points to the first element or is invalid
|
||||
// allows to express some algorithms more concisely
|
||||
private class EndBoundIteratorWithValue<T: Any, Iter: Iterator<T>>(val base: Iter) {
|
||||
private var _value: T? = base.nextOrNull()
|
||||
|
||||
val value: T get() = _value ?: throw Exception("Dereferencing invalid iterator")
|
||||
|
||||
fun isValid(): Boolean = _value != null
|
||||
|
||||
fun traverseNext(): EndBoundIteratorWithValue<T, Iter> {
|
||||
_value = base.nextOrNull()
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
private fun<T: Any> Iterator<T>.nextOrNull(): T? = if (hasNext()) next() else null
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.integration;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public abstract class AbstractAntTaskTest extends KotlinIntegrationTestBase {
|
||||
protected void doTest(String testFile) throws Exception {
|
||||
String testDataDir = new File(testFile).getAbsolutePath();
|
||||
|
||||
runJava(
|
||||
testDataDir,
|
||||
"build.log",
|
||||
"-jar", getAntHome() + File.separator + "lib" + File.separator + "ant-launcher.jar",
|
||||
"-Dkotlin.lib=" + getCompilerLib(),
|
||||
"-Dkotlin.runtime.jar=" + ForTestCompileRuntime.runtimeJarForTests().getAbsolutePath(),
|
||||
"-Dkotlin.reflect.jar=" + ForTestCompileRuntime.reflectJarForTests().getAbsolutePath(),
|
||||
"-Dtest.data=" + testDataDir,
|
||||
"-Dtemp=" + tmpdir,
|
||||
"-f", "build.xml"
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
protected String normalizeOutput(@NotNull File testDataDir, @NotNull String content) {
|
||||
return super.normalizeOutput(testDataDir, content)
|
||||
.replaceAll("Total time: .+\n", "Total time: [time]\n");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String getAntHome() {
|
||||
return getKotlinProjectHome().getAbsolutePath() + File.separator + "dependencies" + File.separator + "ant-1.8";
|
||||
}
|
||||
}
|
||||
@@ -1,173 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.integration;
|
||||
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.execution.configurations.GeneralCommandLine;
|
||||
import com.intellij.execution.process.OSProcessHandler;
|
||||
import com.intellij.execution.process.ProcessAdapter;
|
||||
import com.intellij.execution.process.ProcessEvent;
|
||||
import com.intellij.execution.process.ProcessOutputTypes;
|
||||
import com.intellij.openapi.application.PathManager;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import kotlin.jvm.functions.Function1;
|
||||
import kotlin.text.MatchResult;
|
||||
import kotlin.text.Regex;
|
||||
import org.intellij.lang.annotations.Language;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.cli.common.KotlinVersion;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.test.TestCaseWithTmpdir;
|
||||
import org.jetbrains.kotlin.utils.PathUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public abstract class KotlinIntegrationTestBase extends TestCaseWithTmpdir {
|
||||
static {
|
||||
System.setProperty("java.awt.headless", "true");
|
||||
}
|
||||
|
||||
protected int runJava(@NotNull String testDataDir, @Nullable String logName, @NotNull String... arguments) throws Exception {
|
||||
GeneralCommandLine commandLine = new GeneralCommandLine().withWorkDirectory(testDataDir);
|
||||
commandLine.setExePath(getJavaRuntime().getAbsolutePath());
|
||||
commandLine.addParameters(arguments);
|
||||
|
||||
StringBuilder executionLog = new StringBuilder();
|
||||
int exitCode = runProcess(commandLine, executionLog);
|
||||
|
||||
if (logName == null) {
|
||||
assertEquals("Non-zero exit code", 0, exitCode);
|
||||
}
|
||||
else {
|
||||
check(testDataDir, logName, executionLog.toString());
|
||||
}
|
||||
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
private static String normalizePath(String content, File baseDir, String pathId) {
|
||||
String contentWithRelativePaths = content.replace(baseDir.getAbsolutePath(), pathId);
|
||||
|
||||
@Language("RegExp")
|
||||
String RELATIVE_PATH_WITH_MIXED_SEPARATOR = Regex.Companion.escape(pathId) + "[-.\\w/\\\\]*";
|
||||
|
||||
return new Regex(RELATIVE_PATH_WITH_MIXED_SEPARATOR).replace(contentWithRelativePaths, new Function1<MatchResult, String>() {
|
||||
@Override
|
||||
public String invoke(MatchResult mr) {
|
||||
return FileUtil.toSystemIndependentName(mr.getValue());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected String normalizeOutput(@NotNull File testDataDir, @NotNull String content) {
|
||||
content = normalizePath(content, testDataDir, "[TestData]");
|
||||
content = normalizePath(content, tmpdir, "[Temp]");
|
||||
content = normalizePath(content, getCompilerLib(), "[CompilerLib]");
|
||||
content = normalizePath(content, getKotlinProjectHome(), "[KotlinProjectHome]");
|
||||
content = content.replaceAll(Pattern.quote(KotlinVersion.VERSION), "[KotlinVersion]");
|
||||
content = StringUtil.convertLineSeparators(content);
|
||||
return content;
|
||||
}
|
||||
|
||||
private void check(String testDataDir, String baseName, String content) throws IOException {
|
||||
File expectedFile = new File(testDataDir, baseName + ".expected");
|
||||
String normalizedContent = normalizeOutput(new File(testDataDir), content);
|
||||
|
||||
KotlinTestUtils.assertEqualsToFile(expectedFile, normalizedContent);
|
||||
}
|
||||
|
||||
private static int runProcess(GeneralCommandLine commandLine, StringBuilder executionLog) throws ExecutionException {
|
||||
OSProcessHandler handler =
|
||||
new OSProcessHandler(commandLine.createProcess(), commandLine.getCommandLineString(), commandLine.getCharset());
|
||||
|
||||
StringBuilder outContent = new StringBuilder();
|
||||
StringBuilder errContent = new StringBuilder();
|
||||
|
||||
handler.addProcessListener(new OutputListener(outContent, errContent));
|
||||
|
||||
handler.startNotify();
|
||||
handler.waitFor();
|
||||
int exitCode = handler.getProcess().exitValue();
|
||||
|
||||
appendIfNotEmpty(executionLog, "OUT:\n", outContent.toString());
|
||||
appendIfNotEmpty(executionLog, "\nERR:\n", errContent.toString());
|
||||
|
||||
executionLog.append("\nReturn code: ").append(exitCode).append("\n");
|
||||
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
private static void appendIfNotEmpty(StringBuilder executionLog, String prefix, String content) {
|
||||
if (content.length() > 0) {
|
||||
executionLog.append(prefix);
|
||||
executionLog.append(content);
|
||||
}
|
||||
}
|
||||
|
||||
private static File getJavaRuntime() {
|
||||
File javaHome = new File(System.getProperty("java.home"));
|
||||
String javaExe = SystemInfo.isWindows ? "java.exe" : "java";
|
||||
|
||||
File runtime = new File(javaHome, "bin" + File.separator + javaExe);
|
||||
assertTrue("No java runtime at " + runtime, runtime.isFile());
|
||||
|
||||
return runtime;
|
||||
}
|
||||
|
||||
public static File getCompilerLib() {
|
||||
File file = PathUtil.getKotlinPathsForDistDirectory().getLibPath().getAbsoluteFile();
|
||||
assertTrue("Lib directory doesn't exist. Run 'ant dist'", file.isDirectory());
|
||||
return file;
|
||||
}
|
||||
|
||||
protected static File getKotlinProjectHome() {
|
||||
return new File(PathManager.getHomePath()).getParentFile();
|
||||
}
|
||||
|
||||
private static class OutputListener extends ProcessAdapter {
|
||||
private final StringBuilder out;
|
||||
private final StringBuilder err;
|
||||
|
||||
public OutputListener(@NotNull StringBuilder out, @NotNull StringBuilder err) {
|
||||
this.out = out;
|
||||
this.err = err;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextAvailable(ProcessEvent event, Key outputType) {
|
||||
if (outputType == ProcessOutputTypes.STDERR) {
|
||||
err.append(event.getText());
|
||||
}
|
||||
else if (outputType == ProcessOutputTypes.SYSTEM) {
|
||||
// skip
|
||||
}
|
||||
else {
|
||||
out.append(event.getText());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processTerminated(ProcessEvent event) {}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.jvm.compiler;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.test.TestJdkKind;
|
||||
|
||||
public abstract class AbstractLoadJava8Test extends AbstractLoadJavaTest {
|
||||
@NotNull
|
||||
@Override
|
||||
protected TestJdkKind getJdkKind() {
|
||||
return TestJdkKind.FULL_JDK;
|
||||
}
|
||||
}
|
||||
@@ -1,299 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.jvm.compiler;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import junit.framework.ComparisonFailure;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.analyzer.AnalysisResult;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.CliLightClassGenerationSupport;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.JvmPackagePartProvider;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
|
||||
import org.jetbrains.kotlin.cli.jvm.config.JvmContentRootsKt;
|
||||
import org.jetbrains.kotlin.cli.jvm.config.ModuleNameKt;
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration;
|
||||
import org.jetbrains.kotlin.config.ContentRootsKt;
|
||||
import org.jetbrains.kotlin.context.ModuleContext;
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.PackageViewDescriptor;
|
||||
import org.jetbrains.kotlin.psi.KtFile;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||
import org.jetbrains.kotlin.resolve.TopDownAnalysisMode;
|
||||
import org.jetbrains.kotlin.resolve.jvm.TopDownAnalyzerFacadeForJVM;
|
||||
import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil;
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor;
|
||||
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.RecursiveDescriptorComparator;
|
||||
import org.junit.Assert;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.jetbrains.kotlin.jvm.compiler.LoadDescriptorUtil.*;
|
||||
import static org.jetbrains.kotlin.test.KotlinTestUtils.*;
|
||||
import static org.jetbrains.kotlin.test.util.DescriptorValidator.ValidationVisitor.errorTypesAllowed;
|
||||
import static org.jetbrains.kotlin.test.util.DescriptorValidator.ValidationVisitor.errorTypesForbidden;
|
||||
import static org.jetbrains.kotlin.test.util.RecursiveDescriptorComparator.*;
|
||||
|
||||
/*
|
||||
The generated test compares package descriptors loaded from kotlin sources and read from compiled java.
|
||||
*/
|
||||
public abstract class AbstractLoadJavaTest extends TestCaseWithTmpdir {
|
||||
protected void doTestCompiledJava(@NotNull String javaFileName) throws Exception {
|
||||
doTestCompiledJava(javaFileName, DONT_INCLUDE_METHODS_OF_OBJECT);
|
||||
}
|
||||
|
||||
// Java-Kotlin dependencies are not supported in this method for simplicity
|
||||
protected void doTestCompiledJavaAndKotlin(@NotNull String expectedFileName) throws Exception {
|
||||
File expectedFile = new File(expectedFileName);
|
||||
File sourcesDir = new File(expectedFileName.replaceFirst("\\.txt$", ""));
|
||||
|
||||
List<File> kotlinSources = FileUtil.findFilesByMask(Pattern.compile(".+\\.kt"), sourcesDir);
|
||||
compileKotlinToDirAndGetAnalysisResult(kotlinSources, tmpdir, myTestRootDisposable, ConfigurationKind.JDK_ONLY, false);
|
||||
|
||||
List<File> javaSources = FileUtil.findFilesByMask(Pattern.compile(".+\\.java"), sourcesDir);
|
||||
Pair<PackageViewDescriptor, BindingContext> binaryPackageAndContext = compileJavaAndLoadTestPackageAndBindingContextFromBinary(
|
||||
javaSources, tmpdir, ConfigurationKind.JDK_ONLY
|
||||
);
|
||||
|
||||
checkJavaPackage(expectedFile, binaryPackageAndContext.first, binaryPackageAndContext.second, DONT_INCLUDE_METHODS_OF_OBJECT);
|
||||
}
|
||||
|
||||
protected void doTestCompiledJavaIncludeObjectMethods(@NotNull String javaFileName) throws Exception {
|
||||
doTestCompiledJava(javaFileName, RECURSIVE);
|
||||
}
|
||||
|
||||
protected void doTestCompiledKotlin(@NotNull String ktFileName) throws Exception {
|
||||
doTestCompiledKotlin(ktFileName, ConfigurationKind.JDK_ONLY, false);
|
||||
}
|
||||
|
||||
protected void doTestCompiledKotlinWithTypeTable(@NotNull String ktFileName) throws Exception {
|
||||
doTestCompiledKotlin(ktFileName, ConfigurationKind.JDK_ONLY, true);
|
||||
}
|
||||
|
||||
protected void doTestCompiledKotlinWithStdlib(@NotNull String ktFileName) throws Exception {
|
||||
doTestCompiledKotlin(ktFileName, ConfigurationKind.ALL, false);
|
||||
}
|
||||
|
||||
private void doTestCompiledKotlin(
|
||||
@NotNull String ktFileName, @NotNull ConfigurationKind configurationKind, boolean useTypeTableInSerializer
|
||||
) throws Exception {
|
||||
File ktFile = new File(ktFileName);
|
||||
File txtFile = new File(ktFileName.replaceFirst("\\.kt$", ".txt"));
|
||||
AnalysisResult result = compileKotlinToDirAndGetAnalysisResult(
|
||||
Collections.singletonList(ktFile), tmpdir, getTestRootDisposable(), configurationKind, useTypeTableInSerializer
|
||||
);
|
||||
|
||||
PackageViewDescriptor packageFromSource = result.getModuleDescriptor().getPackage(TEST_PACKAGE_FQNAME);
|
||||
Assert.assertEquals("test", packageFromSource.getName().asString());
|
||||
|
||||
PackageViewDescriptor packageFromBinary = LoadDescriptorUtil.loadTestPackageAndBindingContextFromJavaRoot(
|
||||
tmpdir, getTestRootDisposable(), getJdkKind(), configurationKind, true
|
||||
).first;
|
||||
|
||||
for (DeclarationDescriptor descriptor : DescriptorUtils.getAllDescriptors(packageFromBinary.getMemberScope())) {
|
||||
if (descriptor instanceof ClassDescriptor) {
|
||||
assert descriptor instanceof DeserializedClassDescriptor : DescriptorUtils.getFqName(descriptor) + " is loaded as " + descriptor.getClass();
|
||||
}
|
||||
}
|
||||
|
||||
DescriptorValidator.validate(errorTypesForbidden(), packageFromSource);
|
||||
DescriptorValidator.validate(new DeserializedScopeValidationVisitor(), packageFromBinary);
|
||||
Configuration configuration = RecursiveDescriptorComparator.DONT_INCLUDE_METHODS_OF_OBJECT
|
||||
.checkPrimaryConstructors(true)
|
||||
.checkPropertyAccessors(true);
|
||||
compareDescriptors(packageFromSource, packageFromBinary, configuration, txtFile);
|
||||
}
|
||||
|
||||
protected void doTestJavaAgainstKotlin(String expectedFileName) throws Exception {
|
||||
File expectedFile = new File(expectedFileName);
|
||||
File sourcesDir = new File(expectedFileName.replaceFirst("\\.txt$", ""));
|
||||
|
||||
FileUtil.copyDir(sourcesDir, new File(tmpdir, "test"), new FileFilter() {
|
||||
@Override
|
||||
public boolean accept(@NotNull File pathname) {
|
||||
return pathname.getName().endsWith(".java");
|
||||
}
|
||||
});
|
||||
|
||||
CompilerConfiguration configuration = KotlinTestUtils.compilerConfigurationForTests(
|
||||
ConfigurationKind.JDK_ONLY, getJdkKind());
|
||||
ContentRootsKt.addKotlinSourceRoot(configuration, sourcesDir.getAbsolutePath());
|
||||
JvmContentRootsKt.addJavaSourceRoot(configuration, new File("compiler/testData/loadJava/include"));
|
||||
JvmContentRootsKt.addJavaSourceRoot(configuration, tmpdir);
|
||||
|
||||
KotlinCoreEnvironment environment =
|
||||
KotlinCoreEnvironment.createForTests(getTestRootDisposable(), configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES);
|
||||
|
||||
BindingTrace trace = new CliLightClassGenerationSupport.NoScopeRecordCliBindingTrace();
|
||||
ModuleContext moduleContext = TopDownAnalyzerFacadeForJVM.createContextWithSealedModule(environment.getProject(),
|
||||
ModuleNameKt.getModuleName(environment));
|
||||
|
||||
TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegrationNoIncremental(
|
||||
moduleContext,
|
||||
environment.getSourceFiles(),
|
||||
trace,
|
||||
TopDownAnalysisMode.TopLevelDeclarations,
|
||||
new JvmPackagePartProvider(environment)
|
||||
);
|
||||
|
||||
PackageViewDescriptor packageView = moduleContext.getModule().getPackage(TEST_PACKAGE_FQNAME);
|
||||
checkJavaPackage(expectedFile, packageView, trace.getBindingContext(), DONT_INCLUDE_METHODS_OF_OBJECT);
|
||||
}
|
||||
|
||||
// TODO: add more tests on inherited parameter names, but currently impossible because of KT-4509
|
||||
protected void doTestKotlinAgainstCompiledJavaWithKotlin(@NotNull String expectedFileName) throws Exception {
|
||||
File kotlinSrc = new File(expectedFileName);
|
||||
File librarySrc = new File(expectedFileName.replaceFirst("\\.kt$", ""));
|
||||
File expectedFile = new File(expectedFileName.replaceFirst("\\.kt$", ".txt"));
|
||||
|
||||
File libraryOut = new File(tmpdir, "libraryOut");
|
||||
compileKotlinWithJava(
|
||||
FileUtil.findFilesByMask(Pattern.compile(".+\\.java$"), librarySrc),
|
||||
FileUtil.findFilesByMask(Pattern.compile(".+\\.kt$"), librarySrc),
|
||||
libraryOut,
|
||||
getTestRootDisposable(),
|
||||
null
|
||||
);
|
||||
|
||||
KotlinCoreEnvironment environment = KotlinCoreEnvironment.createForTests(
|
||||
getTestRootDisposable(),
|
||||
compilerConfigurationForTests(ConfigurationKind.JDK_ONLY, getJdkKind(), getAnnotationsJar(), libraryOut),
|
||||
EnvironmentConfigFiles.JVM_CONFIG_FILES);
|
||||
|
||||
KtFile jetFile = KotlinTestUtils.createFile(kotlinSrc.getPath(), FileUtil.loadFile(kotlinSrc, true), environment.getProject());
|
||||
|
||||
AnalysisResult result = JvmResolveUtil.analyzeFilesWithJavaIntegrationAndCheckForErrors(
|
||||
environment.getProject(), Collections.singleton(jetFile)
|
||||
);
|
||||
PackageViewDescriptor packageView = result.getModuleDescriptor().getPackage(TEST_PACKAGE_FQNAME);
|
||||
assertFalse(packageView.isEmpty());
|
||||
|
||||
validateAndCompareDescriptorWithFile(packageView, DONT_INCLUDE_METHODS_OF_OBJECT.withValidationStrategy(
|
||||
new DeserializedScopeValidationVisitor()
|
||||
), expectedFile);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected TestJdkKind getJdkKind() {
|
||||
return TestJdkKind.MOCK_JDK;
|
||||
}
|
||||
|
||||
protected void doTestSourceJava(@NotNull String javaFileName) throws Exception {
|
||||
File originalJavaFile = new File(javaFileName);
|
||||
File expectedFile = getTxtFile(javaFileName);
|
||||
|
||||
File testPackageDir = new File(tmpdir, "test");
|
||||
assertTrue(testPackageDir.mkdir());
|
||||
FileUtil.copy(originalJavaFile, new File(testPackageDir, originalJavaFile.getName()));
|
||||
|
||||
Pair<PackageViewDescriptor, BindingContext> javaPackageAndContext = loadTestPackageAndBindingContextFromJavaRoot(
|
||||
tmpdir, getTestRootDisposable(), getJdkKind(), ConfigurationKind.JDK_ONLY, false
|
||||
);
|
||||
|
||||
checkJavaPackage(expectedFile, javaPackageAndContext.first, javaPackageAndContext.second,
|
||||
DONT_INCLUDE_METHODS_OF_OBJECT.withValidationStrategy(errorTypesAllowed()));
|
||||
}
|
||||
|
||||
private void doTestCompiledJava(@NotNull String javaFileName, Configuration configuration) throws Exception {
|
||||
final File srcDir = new File(tmpdir, "src");
|
||||
File compiledDir = new File(tmpdir, "compiled");
|
||||
assertTrue(srcDir.mkdir());
|
||||
assertTrue(compiledDir.mkdir());
|
||||
|
||||
List<File> srcFiles = KotlinTestUtils.createTestFiles(
|
||||
new File(javaFileName).getName(), FileUtil.loadFile(new File(javaFileName), true),
|
||||
new TestFileFactoryNoModules<File>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public File create(@NotNull String fileName, @NotNull String text, @NotNull Map<String, String> directives) {
|
||||
File targetFile = new File(srcDir, fileName);
|
||||
try {
|
||||
FileUtil.writeToFile(targetFile, text);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
return targetFile;
|
||||
}
|
||||
});
|
||||
|
||||
Pair<PackageViewDescriptor, BindingContext> javaPackageAndContext = compileJavaAndLoadTestPackageAndBindingContextFromBinary(
|
||||
srcFiles, compiledDir, ConfigurationKind.ALL
|
||||
);
|
||||
|
||||
checkJavaPackage(getTxtFile(javaFileName), javaPackageAndContext.first, javaPackageAndContext.second, configuration);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Pair<PackageViewDescriptor, BindingContext> compileJavaAndLoadTestPackageAndBindingContextFromBinary(
|
||||
@NotNull Collection<File> javaFiles,
|
||||
@NotNull File outDir,
|
||||
@NotNull ConfigurationKind configurationKind
|
||||
) throws IOException {
|
||||
compileJavaWithAnnotationsJar(javaFiles, outDir);
|
||||
return loadTestPackageAndBindingContextFromJavaRoot(outDir, myTestRootDisposable, getJdkKind(), configurationKind, true);
|
||||
}
|
||||
|
||||
private static void checkJavaPackage(
|
||||
File txtFile,
|
||||
PackageViewDescriptor javaPackage,
|
||||
BindingContext bindingContext,
|
||||
Configuration configuration
|
||||
) {
|
||||
boolean fail = false;
|
||||
try {
|
||||
ExpectedLoadErrorsUtil.checkForLoadErrors(javaPackage, bindingContext);
|
||||
}
|
||||
catch (ComparisonFailure e) {
|
||||
// to let the next check run even if this one failed
|
||||
System.err.println("Expected: " + e.getExpected());
|
||||
System.err.println("Actual : " + e.getActual());
|
||||
e.printStackTrace();
|
||||
fail = true;
|
||||
}
|
||||
catch (AssertionError e) {
|
||||
e.printStackTrace();
|
||||
fail = true;
|
||||
}
|
||||
|
||||
validateAndCompareDescriptorWithFile(javaPackage, configuration, txtFile);
|
||||
|
||||
if (fail) {
|
||||
fail("See error above");
|
||||
}
|
||||
}
|
||||
|
||||
private static File getTxtFile(String javaFileName) {
|
||||
return new File(javaFileName.replaceFirst("\\.java$", ".txt"));
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,10 +23,8 @@ import com.intellij.openapi.util.io.FileUtil
|
||||
import org.jetbrains.kotlin.cli.common.output.outputUtils.writeAllTo
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.codegen.GenerationUtils
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.JvmPackagePartProvider
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.jetbrains.kotlin.test.TestCaseWithTmpdir
|
||||
import org.jetbrains.kotlin.utils.join
|
||||
import org.jetbrains.org.objectweb.asm.*
|
||||
import org.junit.Assert
|
||||
import java.io.File
|
||||
@@ -34,8 +32,6 @@ import java.io.FileInputStream
|
||||
import java.nio.charset.Charset
|
||||
import java.util.*
|
||||
import java.util.regex.MatchResult
|
||||
import java.util.regex.Pattern
|
||||
import kotlin.text.Regex
|
||||
|
||||
|
||||
abstract class AbstractWriteSignatureTest : TestCaseWithTmpdir() {
|
||||
|
||||
-53
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.jvm.compiler
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PackageViewDescriptor
|
||||
import org.jetbrains.kotlin.resolve.MemberComparator
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedMemberScope
|
||||
import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase
|
||||
import org.jetbrains.kotlin.test.util.DescriptorValidator
|
||||
import org.jetbrains.kotlin.test.util.DescriptorValidator.ValidationVisitor
|
||||
|
||||
class DeserializedScopeValidationVisitor : ValidationVisitor() {
|
||||
override fun validateScope(scopeOwner: DeclarationDescriptor, scope: MemberScope, collector: DescriptorValidator.DiagnosticCollector) {
|
||||
super.validateScope(scopeOwner, scope, collector)
|
||||
validateDeserializedScope(scopeOwner, scope)
|
||||
}
|
||||
}
|
||||
|
||||
private fun validateDeserializedScope(scopeOwner: DeclarationDescriptor, scope: MemberScope) {
|
||||
val isPackageViewScope = scopeOwner is PackageViewDescriptor
|
||||
if (scope is DeserializedMemberScope || isPackageViewScope) {
|
||||
val relevantDescriptors = scope.getContributedDescriptors().filter { member ->
|
||||
member is CallableMemberDescriptor && member.kind.isReal || (!isPackageViewScope && member is ClassDescriptor)
|
||||
}
|
||||
checkSorted(relevantDescriptors, scopeOwner)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkSorted(descriptors: Collection<DeclarationDescriptor>, declaration: DeclarationDescriptor) {
|
||||
KtUsefulTestCase.assertOrderedEquals(
|
||||
"Members of $declaration should be sorted by serialization.",
|
||||
descriptors,
|
||||
descriptors.sortedWith(MemberComparator.INSTANCE)
|
||||
)
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.jvm.compiler;
|
||||
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.impl.DeclarationDescriptorVisitorEmptyBodies;
|
||||
import org.jetbrains.kotlin.load.java.JavaBindingContext;
|
||||
import org.jetbrains.kotlin.name.FqName;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||
import org.jetbrains.kotlin.resolve.constants.ConstantValue;
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase.assertNotNull;
|
||||
import static org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase.assertSameElements;
|
||||
|
||||
public class ExpectedLoadErrorsUtil {
|
||||
public static final String ANNOTATION_CLASS_NAME = "org.jetbrains.kotlin.jvm.compiler.annotation.ExpectLoadError";
|
||||
|
||||
public static void checkForLoadErrors(
|
||||
@NotNull PackageViewDescriptor packageFromJava,
|
||||
@NotNull BindingContext bindingContext
|
||||
) {
|
||||
Map<SourceElement, List<String>> expectedErrors = getExpectedLoadErrors(packageFromJava);
|
||||
Map<SourceElement, List<String>> actualErrors = getActualLoadErrors(bindingContext);
|
||||
|
||||
for (SourceElement source : ContainerUtil.union(expectedErrors.keySet(), actualErrors.keySet())) {
|
||||
List<String> actual = actualErrors.get(source);
|
||||
List<String> expected = expectedErrors.get(source);
|
||||
|
||||
assertNotNull("Unexpected load error(s):\n" + actual + "\ncontainer:" + source, expected);
|
||||
assertNotNull("Missing load error(s):\n" + expected + "\ncontainer:" + source, actual);
|
||||
|
||||
assertSameElements("Unexpected/missing load error(s)\ncontainer:" + source, actual, expected);
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<SourceElement, List<String>> getExpectedLoadErrors(@NotNull PackageViewDescriptor packageFromJava) {
|
||||
final Map<SourceElement, List<String>> map = new HashMap<SourceElement, List<String>>();
|
||||
|
||||
packageFromJava.acceptVoid(new DeclarationDescriptorVisitorEmptyBodies<Void, Void>() {
|
||||
@Override
|
||||
public Void visitPackageViewDescriptor(PackageViewDescriptor descriptor, Void data) {
|
||||
return visitDeclarationRecursively(descriptor, descriptor.getMemberScope());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitClassDescriptor(ClassDescriptor descriptor, Void data) {
|
||||
return visitDeclarationRecursively(descriptor, descriptor.getDefaultType().getMemberScope());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitFunctionDescriptor(FunctionDescriptor descriptor, Void data) {
|
||||
return visitDeclaration(descriptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitPropertyDescriptor(PropertyDescriptor descriptor, Void data) {
|
||||
return visitDeclaration(descriptor);
|
||||
}
|
||||
|
||||
private Void visitDeclaration(@NotNull DeclarationDescriptor descriptor) {
|
||||
AnnotationDescriptor annotation = descriptor.getAnnotations().findAnnotation(new FqName(ANNOTATION_CLASS_NAME));
|
||||
if (annotation == null) return null;
|
||||
|
||||
// we expect exactly one annotation argument
|
||||
ConstantValue<?> argument = annotation.getAllValueArguments().values().iterator().next();
|
||||
|
||||
String error = (String) argument.getValue();
|
||||
//noinspection ConstantConditions
|
||||
List<String> errors = Arrays.asList(error.split("\\|"));
|
||||
|
||||
putError(map, descriptor, errors);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Void visitDeclarationRecursively(@NotNull DeclarationDescriptor descriptor, @NotNull MemberScope memberScope) {
|
||||
for (DeclarationDescriptor member : DescriptorUtils.getAllDescriptors(memberScope)) {
|
||||
member.acceptVoid(this);
|
||||
}
|
||||
|
||||
return visitDeclaration(descriptor);
|
||||
}
|
||||
});
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
private static Map<SourceElement, List<String>> getActualLoadErrors(@NotNull BindingContext bindingContext) {
|
||||
Map<SourceElement, List<String>> result = new HashMap<SourceElement, List<String>>();
|
||||
|
||||
Collection<DeclarationDescriptor> descriptors = bindingContext.getKeys(JavaBindingContext.LOAD_FROM_JAVA_SIGNATURE_ERRORS);
|
||||
for (DeclarationDescriptor descriptor : descriptors) {
|
||||
List<String> errors = bindingContext.get(JavaBindingContext.LOAD_FROM_JAVA_SIGNATURE_ERRORS, descriptor);
|
||||
if (errors == null) continue;
|
||||
|
||||
putError(result, descriptor, errors);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void putError(
|
||||
@NotNull Map<SourceElement, List<String>> result,
|
||||
@NotNull DeclarationDescriptor descriptor,
|
||||
@NotNull List<String> errors
|
||||
) {
|
||||
assert descriptor.getOriginal() instanceof DeclarationDescriptorWithSource
|
||||
: "Signature errors should be reported only on declarations with source, but " + descriptor + " found";
|
||||
result.put(((DeclarationDescriptorWithSource) descriptor.getOriginal()).getSource(), errors);
|
||||
}
|
||||
|
||||
private ExpectedLoadErrorsUtil() {
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -29,7 +29,6 @@ import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.jetbrains.kotlin.test.KotlinTestWithEnvironmentManagement
|
||||
import org.jetbrains.kotlin.test.TestJdkKind
|
||||
import java.io.File
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
import kotlin.test.assertNull
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
@@ -1,170 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.jvm.compiler;
|
||||
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.analyzer.AnalysisResult;
|
||||
import org.jetbrains.kotlin.cli.common.output.outputUtils.OutputUtilsKt;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.CliLightClassGenerationSupport;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.JvmPackagePartProvider;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
|
||||
import org.jetbrains.kotlin.codegen.GenerationUtils;
|
||||
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime;
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration;
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.PackageViewDescriptor;
|
||||
import org.jetbrains.kotlin.name.FqName;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.psi.KtFile;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace;
|
||||
import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil;
|
||||
import org.jetbrains.kotlin.resolve.lazy.LazyResolveTestUtil;
|
||||
import org.jetbrains.kotlin.test.ConfigurationKind;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.test.TestJdkKind;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
import static org.jetbrains.kotlin.test.KotlinTestUtils.createEnvironmentWithMockJdkAndIdeaAnnotations;
|
||||
|
||||
public final class LoadDescriptorUtil {
|
||||
|
||||
@NotNull
|
||||
public static final FqName TEST_PACKAGE_FQNAME = FqName.topLevel(Name.identifier("test"));
|
||||
|
||||
private LoadDescriptorUtil() {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static AnalysisResult compileKotlinToDirAndGetAnalysisResult(
|
||||
@NotNull List<File> kotlinFiles,
|
||||
@NotNull File outDir,
|
||||
@NotNull Disposable disposable,
|
||||
@NotNull ConfigurationKind configurationKind,
|
||||
boolean useTypeTableInSerializer
|
||||
) {
|
||||
KtFilesAndAnalysisResult
|
||||
filesAndResult = KtFilesAndAnalysisResult.createJetFilesAndAnalyze(kotlinFiles, disposable, configurationKind);
|
||||
AnalysisResult result = filesAndResult.getAnalysisResult();
|
||||
List<KtFile> files = filesAndResult.getKtFiles();
|
||||
GenerationState state = GenerationUtils.compileFilesGetGenerationState(
|
||||
files.get(0).getProject(), result, files, useTypeTableInSerializer
|
||||
);
|
||||
OutputUtilsKt.writeAllTo(state.getFactory(), outDir);
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Pair<PackageViewDescriptor, BindingContext> loadTestPackageAndBindingContextFromJavaRoot(
|
||||
@NotNull File javaRoot,
|
||||
@NotNull Disposable disposable,
|
||||
@NotNull TestJdkKind testJdkKind,
|
||||
@NotNull ConfigurationKind configurationKind,
|
||||
boolean isBinaryRoot
|
||||
) {
|
||||
List<File> javaBinaryRoots = new ArrayList<File>();
|
||||
javaBinaryRoots.add(KotlinTestUtils.getAnnotationsJar());
|
||||
|
||||
List<File> javaSourceRoots = new ArrayList<File>();
|
||||
javaSourceRoots.add(new File("compiler/testData/loadJava/include"));
|
||||
if (isBinaryRoot) {
|
||||
javaBinaryRoots.add(javaRoot);
|
||||
}
|
||||
else {
|
||||
javaSourceRoots.add(javaRoot);
|
||||
}
|
||||
CompilerConfiguration configuration = KotlinTestUtils.compilerConfigurationForTests(
|
||||
configurationKind,
|
||||
testJdkKind,
|
||||
javaBinaryRoots,
|
||||
javaSourceRoots
|
||||
);
|
||||
KotlinCoreEnvironment environment =
|
||||
KotlinCoreEnvironment.createForTests(disposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES);
|
||||
|
||||
BindingTrace trace = new CliLightClassGenerationSupport.NoScopeRecordCliBindingTrace();
|
||||
ModuleDescriptor module = LazyResolveTestUtil
|
||||
.resolve(environment.getProject(), trace, Collections.<KtFile>emptyList(), environment);
|
||||
|
||||
PackageViewDescriptor packageView = module.getPackage(TEST_PACKAGE_FQNAME);
|
||||
return Pair.create(packageView, trace.getBindingContext());
|
||||
}
|
||||
|
||||
public static void compileJavaWithAnnotationsJar(@NotNull Collection<File> javaFiles, @NotNull File outDir) throws IOException {
|
||||
String classPath = ForTestCompileRuntime.runtimeJarForTests() + File.pathSeparator +
|
||||
KotlinTestUtils.getAnnotationsJar().getPath();
|
||||
KotlinTestUtils.compileJavaFiles(javaFiles, Arrays.asList(
|
||||
"-classpath", classPath,
|
||||
"-sourcepath", "compiler/testData/loadJava/include",
|
||||
"-d", outDir.getPath()
|
||||
));
|
||||
}
|
||||
|
||||
private static class KtFilesAndAnalysisResult {
|
||||
@NotNull
|
||||
public static KtFilesAndAnalysisResult createJetFilesAndAnalyze(
|
||||
@NotNull List<File> kotlinFiles,
|
||||
@NotNull Disposable disposable,
|
||||
@NotNull ConfigurationKind configurationKind
|
||||
) {
|
||||
final KotlinCoreEnvironment jetCoreEnvironment = createEnvironmentWithMockJdkAndIdeaAnnotations(disposable, configurationKind);
|
||||
List<KtFile> jetFiles = ContainerUtil.map(kotlinFiles, new Function<File, KtFile>() {
|
||||
@Override
|
||||
public KtFile fun(File kotlinFile) {
|
||||
try {
|
||||
return KotlinTestUtils.createFile(
|
||||
kotlinFile.getName(), FileUtil.loadFile(kotlinFile, true), jetCoreEnvironment.getProject());
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
AnalysisResult result = JvmResolveUtil.analyzeFilesWithJavaIntegrationAndCheckForErrors(
|
||||
jetCoreEnvironment.getProject(), jetFiles, new JvmPackagePartProvider(jetCoreEnvironment));
|
||||
return new KtFilesAndAnalysisResult(jetFiles, result);
|
||||
}
|
||||
|
||||
private final List<KtFile> ktFiles;
|
||||
private final AnalysisResult result;
|
||||
|
||||
private KtFilesAndAnalysisResult(@NotNull List<KtFile> ktFiles, @NotNull AnalysisResult result) {
|
||||
this.ktFiles = ktFiles;
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<KtFile> getKtFiles() {
|
||||
return ktFiles;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public AnalysisResult getAnalysisResult() {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -38,8 +38,8 @@ import org.jetbrains.kotlin.resolve.jvm.JvmAnalyzerFacade
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmPlatformParameters
|
||||
import org.jetbrains.kotlin.test.ConfigurationKind
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase
|
||||
import org.jetbrains.kotlin.test.TestJdkKind
|
||||
import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase
|
||||
import org.jetbrains.kotlin.types.ErrorUtils
|
||||
import org.junit.Assert
|
||||
import java.io.File
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.jvm.runtime
|
||||
|
||||
import org.jetbrains.kotlin.test.TestJdkKind
|
||||
|
||||
abstract class AbstractJvm8RuntimeDescriptorLoaderTest : AbstractJvmRuntimeDescriptorLoaderTest() {
|
||||
override val defaultJdkKind: TestJdkKind = TestJdkKind.FULL_JDK
|
||||
}
|
||||
-245
@@ -1,245 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.jvm.runtime
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import org.jetbrains.kotlin.cli.common.output.outputUtils.writeAllTo
|
||||
import org.jetbrains.kotlin.codegen.GenerationUtils
|
||||
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.incremental.components.LookupLocation
|
||||
import org.jetbrains.kotlin.jvm.compiler.ExpectedLoadErrorsUtil
|
||||
import org.jetbrains.kotlin.jvm.compiler.LoadDescriptorUtil
|
||||
import org.jetbrains.kotlin.load.java.ANNOTATIONS_COPIED_TO_TYPES
|
||||
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
|
||||
import org.jetbrains.kotlin.load.java.structure.reflect.classId
|
||||
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
||||
import org.jetbrains.kotlin.load.kotlin.reflect.ReflectKotlinClass
|
||||
import org.jetbrains.kotlin.load.kotlin.reflect.RuntimeModuleData
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRendererModifier
|
||||
import org.jetbrains.kotlin.renderer.OverrideRenderingPolicy
|
||||
import org.jetbrains.kotlin.renderer.ParameterNameRenderingPolicy
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil
|
||||
import org.jetbrains.kotlin.resolve.scopes.ChainedMemberScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScopeImpl
|
||||
import org.jetbrains.kotlin.serialization.deserialization.findClassAcrossModuleDependencies
|
||||
import org.jetbrains.kotlin.test.*
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils.TestFileFactoryNoModules
|
||||
import org.jetbrains.kotlin.test.util.DescriptorValidator.ValidationVisitor.errorTypesForbidden
|
||||
import org.jetbrains.kotlin.test.util.RecursiveDescriptorComparator
|
||||
import org.jetbrains.kotlin.test.util.RecursiveDescriptorComparator.Configuration
|
||||
import org.jetbrains.kotlin.types.TypeSubstitutor
|
||||
import org.jetbrains.kotlin.utils.Printer
|
||||
import org.jetbrains.kotlin.utils.sure
|
||||
import java.io.File
|
||||
import java.net.URLClassLoader
|
||||
import java.util.*
|
||||
import java.util.regex.Pattern
|
||||
|
||||
abstract class AbstractJvmRuntimeDescriptorLoaderTest : TestCaseWithTmpdir() {
|
||||
companion object {
|
||||
private val renderer = DescriptorRenderer.withOptions {
|
||||
withDefinedIn = false
|
||||
excludedAnnotationClasses = (listOf(
|
||||
FqName(ExpectedLoadErrorsUtil.ANNOTATION_CLASS_NAME)
|
||||
) + ANNOTATIONS_COPIED_TO_TYPES).toSet()
|
||||
overrideRenderingPolicy = OverrideRenderingPolicy.RENDER_OPEN_OVERRIDE
|
||||
parameterNameRenderingPolicy = ParameterNameRenderingPolicy.NONE
|
||||
includePropertyConstant = false
|
||||
verbose = true
|
||||
renderDefaultAnnotationArguments = true
|
||||
modifiers = DescriptorRendererModifier.ALL
|
||||
}
|
||||
}
|
||||
|
||||
protected open val defaultJdkKind: TestJdkKind = TestJdkKind.MOCK_JDK
|
||||
|
||||
// NOTE: this test does a dirty hack of text substitution to make all annotations defined in source code retain at runtime.
|
||||
// Specifically each @interface in Java sources is extended by @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
|
||||
// Also type related annotations are removed from Java because they are invisible at runtime
|
||||
protected fun doTest(fileName: String) {
|
||||
val file = File(fileName)
|
||||
val text = FileUtil.loadFile(file, true)
|
||||
|
||||
if (InTextDirectivesUtils.isDirectiveDefined(text, "SKIP_IN_RUNTIME_TEST")) return
|
||||
|
||||
val jdkKind =
|
||||
if (InTextDirectivesUtils.isDirectiveDefined(text, "FULL_JDK")) TestJdkKind.FULL_JDK
|
||||
else defaultJdkKind
|
||||
|
||||
compileFile(file, text, jdkKind)
|
||||
|
||||
val classLoader = URLClassLoader(arrayOf(tmpdir.toURI().toURL()), ForTestCompileRuntime.runtimeAndReflectJarClassLoader())
|
||||
|
||||
val actual = createReflectedPackageView(classLoader, JvmResolveUtil.TEST_MODULE_NAME)
|
||||
|
||||
val comparatorConfiguration = Configuration(
|
||||
/* checkPrimaryConstructors = */ fileName.endsWith(".kt"),
|
||||
/* checkPropertyAccessors = */ true,
|
||||
/* includeMethodsOfKotlinAny = */ false,
|
||||
// Skip Java annotation constructors because order of their parameters is not retained at runtime
|
||||
{ descriptor -> !descriptor!!.isJavaAnnotationConstructor() },
|
||||
errorTypesForbidden(), renderer
|
||||
)
|
||||
|
||||
val differentResultFile = KotlinTestUtils.replaceExtension(file, "runtime.txt")
|
||||
if (differentResultFile.exists()) {
|
||||
RecursiveDescriptorComparator.validateAndCompareDescriptorWithFile(actual, comparatorConfiguration, differentResultFile)
|
||||
return
|
||||
}
|
||||
|
||||
val expected = LoadDescriptorUtil.loadTestPackageAndBindingContextFromJavaRoot(
|
||||
tmpdir, testRootDisposable, jdkKind, ConfigurationKind.ALL, true
|
||||
).first
|
||||
|
||||
RecursiveDescriptorComparator.validateAndCompareDescriptors(expected, actual, comparatorConfiguration, null)
|
||||
}
|
||||
|
||||
private fun DeclarationDescriptor.isJavaAnnotationConstructor() =
|
||||
this is ConstructorDescriptor &&
|
||||
containingDeclaration is JavaClassDescriptor &&
|
||||
containingDeclaration.kind == ClassKind.ANNOTATION_CLASS
|
||||
|
||||
private fun compileFile(file: File, text: String, jdkKind: TestJdkKind) {
|
||||
val fileName = file.name
|
||||
when {
|
||||
fileName.endsWith(".java") -> {
|
||||
val sources = KotlinTestUtils.createTestFiles(fileName, text, object : TestFileFactoryNoModules<File>() {
|
||||
override fun create(fileName: String, text: String, directives: Map<String, String>): File {
|
||||
val targetFile = File(tmpdir, fileName)
|
||||
targetFile.writeText(adaptJavaSource(text))
|
||||
return targetFile
|
||||
}
|
||||
})
|
||||
LoadDescriptorUtil.compileJavaWithAnnotationsJar(sources, tmpdir)
|
||||
}
|
||||
fileName.endsWith(".kt") -> {
|
||||
val environment = KotlinTestUtils.createEnvironmentWithJdkAndNullabilityAnnotationsFromIdea(
|
||||
myTestRootDisposable, ConfigurationKind.ALL, jdkKind
|
||||
)
|
||||
val jetFile = KotlinTestUtils.createFile(file.path, text, environment.project)
|
||||
GenerationUtils.compileFileGetClassFileFactoryForTest(jetFile, environment).writeAllTo(tmpdir)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createReflectedPackageView(classLoader: URLClassLoader, moduleName: String): SyntheticPackageViewForTest {
|
||||
val moduleData = RuntimeModuleData.create(classLoader)
|
||||
moduleData.packageFacadeProvider.registerModule(moduleName)
|
||||
val module = moduleData.module
|
||||
|
||||
val generatedPackageDir = File(tmpdir, LoadDescriptorUtil.TEST_PACKAGE_FQNAME.pathSegments().single().asString())
|
||||
val allClassFiles = FileUtil.findFilesByMask(Pattern.compile(".*\\.class"), generatedPackageDir)
|
||||
|
||||
val packageScopes = arrayListOf<MemberScope>()
|
||||
val classes = arrayListOf<ClassDescriptor>()
|
||||
for (classFile in allClassFiles) {
|
||||
val className = classFile.toRelativeString(tmpdir).substringBeforeLast(".class").replace('/', '.').replace('\\', '.')
|
||||
|
||||
val klass = classLoader.loadClass(className).sure { "Couldn't load class $className" }
|
||||
val binaryClass = ReflectKotlinClass.create(klass)
|
||||
val header = binaryClass?.classHeader
|
||||
|
||||
if (header?.kind == KotlinClassHeader.Kind.FILE_FACADE || header?.kind == KotlinClassHeader.Kind.MULTIFILE_CLASS) {
|
||||
val packageView = module.getPackage(LoadDescriptorUtil.TEST_PACKAGE_FQNAME)
|
||||
if (!packageScopes.contains(packageView.memberScope)) {
|
||||
packageScopes.add(packageView.memberScope)
|
||||
}
|
||||
}
|
||||
else if (header == null || header.kind == KotlinClassHeader.Kind.CLASS) {
|
||||
// Either a normal Kotlin class or a Java class
|
||||
val classId = klass.classId
|
||||
if (!classId.isLocal) {
|
||||
val classDescriptor = module.findClassAcrossModuleDependencies(classId).sure { "Couldn't resolve class $className" }
|
||||
if (DescriptorUtils.isTopLevelDeclaration(classDescriptor)) {
|
||||
classes.add(classDescriptor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Since runtime package view descriptor doesn't support getAllDescriptors(), we construct a synthetic package view here.
|
||||
// It has in its scope descriptors for all the classes and top level members generated by the compiler
|
||||
return SyntheticPackageViewForTest(module, packageScopes, classes)
|
||||
}
|
||||
|
||||
private fun adaptJavaSource(text: String): String {
|
||||
val typeAnnotations = arrayOf("NotNull", "Nullable", "ReadOnly", "Mutable")
|
||||
return typeAnnotations.fold(text) { text, annotation -> text.replace("@$annotation", "") }.replace(
|
||||
"@interface",
|
||||
"@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @interface"
|
||||
)
|
||||
}
|
||||
|
||||
private class SyntheticPackageViewForTest(override val module: ModuleDescriptor,
|
||||
packageScopes: List<MemberScope>,
|
||||
classes: List<ClassifierDescriptor>) : PackageViewDescriptor {
|
||||
private val scope: MemberScope
|
||||
|
||||
init {
|
||||
val list = ArrayList<MemberScope>(packageScopes.size + 1)
|
||||
list.add(ScopeWithClassifiers(classes))
|
||||
list.addAll(packageScopes)
|
||||
scope = ChainedMemberScope("synthetic package view for test", list)
|
||||
}
|
||||
|
||||
override val fqName: FqName
|
||||
get() = LoadDescriptorUtil.TEST_PACKAGE_FQNAME
|
||||
override val memberScope: MemberScope
|
||||
get() = scope
|
||||
override fun <R, D> accept(visitor: DeclarationDescriptorVisitor<R, D>, data: D): R =
|
||||
visitor.visitPackageViewDescriptor(this, data)
|
||||
|
||||
override fun getContainingDeclaration() = null
|
||||
override fun getOriginal() = throw UnsupportedOperationException()
|
||||
override fun substitute(substitutor: TypeSubstitutor) = throw UnsupportedOperationException()
|
||||
override fun acceptVoid(visitor: DeclarationDescriptorVisitor<Void, Void>?) = throw UnsupportedOperationException()
|
||||
override fun getAnnotations() = throw UnsupportedOperationException()
|
||||
override fun getName() = throw UnsupportedOperationException()
|
||||
override val fragments: Nothing
|
||||
get() = throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
private class ScopeWithClassifiers(classifiers: List<ClassifierDescriptor>) : MemberScopeImpl() {
|
||||
private val classifierMap = HashMap<Name, ClassifierDescriptor>()
|
||||
|
||||
init {
|
||||
for (classifier in classifiers) {
|
||||
classifierMap.put(classifier.name, classifier)?.let {
|
||||
throw IllegalStateException(String.format("Redeclaration: %s (%s) and %s (%s) (no line info available)",
|
||||
DescriptorUtils.getFqName(it), it,
|
||||
DescriptorUtils.getFqName(classifier), classifier))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getContributedClassifier(name: Name, location: LookupLocation): ClassifierDescriptor? = classifierMap[name]
|
||||
|
||||
override fun getContributedDescriptors(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean): Collection<DeclarationDescriptor> = classifierMap.values
|
||||
|
||||
override fun printScopeStructure(p: Printer) {
|
||||
p.println("runtime descriptor loader test")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,9 +23,9 @@ import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation;
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity;
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector;
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer;
|
||||
import org.jetbrains.kotlin.modules.Module;
|
||||
import org.jetbrains.kotlin.cli.common.modules.ModuleScriptData;
|
||||
import org.jetbrains.kotlin.cli.common.modules.ModuleXmlParser;
|
||||
import org.jetbrains.kotlin.modules.Module;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,158 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.renderer
|
||||
|
||||
import com.intellij.openapi.editor.impl.DocumentImpl
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.CliLightClassGenerationSupport
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.cli.jvm.config.getModuleName
|
||||
import org.jetbrains.kotlin.container.ComponentProvider
|
||||
import org.jetbrains.kotlin.container.get
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.frontend.di.createContainerForLazyResolve
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.CompilerEnvironment
|
||||
import org.jetbrains.kotlin.resolve.TargetEnvironment
|
||||
import org.jetbrains.kotlin.resolve.jvm.TopDownAnalyzerFacadeForJVM
|
||||
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
|
||||
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
|
||||
import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory
|
||||
import org.jetbrains.kotlin.test.ConfigurationKind
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.jetbrains.kotlin.test.KotlinTestWithEnvironment
|
||||
import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
abstract class AbstractDescriptorRendererTest : KotlinTestWithEnvironment() {
|
||||
protected open fun getDescriptor(declaration: KtDeclaration, container: ComponentProvider): DeclarationDescriptor {
|
||||
return container.get<ResolveSession>().resolveToDescriptor(declaration)
|
||||
}
|
||||
|
||||
protected open val targetEnvironment: TargetEnvironment
|
||||
get() = CompilerEnvironment
|
||||
|
||||
fun doTest(path: String) {
|
||||
val fileText = FileUtil.loadFile(File(path), true)
|
||||
val psiFile = KtPsiFactory(project).createFile(fileText)
|
||||
|
||||
val context = TopDownAnalyzerFacadeForJVM.createContextWithSealedModule(project, environment.getModuleName())
|
||||
|
||||
|
||||
val container = createContainerForLazyResolve(
|
||||
context,
|
||||
FileBasedDeclarationProviderFactory(context.storageManager, listOf(psiFile)),
|
||||
CliLightClassGenerationSupport.NoScopeRecordCliBindingTrace(),
|
||||
JvmPlatform,
|
||||
targetEnvironment
|
||||
)
|
||||
|
||||
val resolveSession = container.get<ResolveSession>()
|
||||
|
||||
context.initializeModuleContents(resolveSession.packageFragmentProvider)
|
||||
|
||||
val descriptors = ArrayList<DeclarationDescriptor>()
|
||||
|
||||
psiFile.accept(object : KtVisitorVoid() {
|
||||
override fun visitKtFile(file: KtFile) {
|
||||
val fqName = file.packageFqName
|
||||
if (!fqName.isRoot) {
|
||||
val packageDescriptor = context.module.getPackage(fqName)
|
||||
descriptors.add(packageDescriptor)
|
||||
}
|
||||
file.acceptChildren(this)
|
||||
}
|
||||
|
||||
override fun visitParameter(parameter: KtParameter) {
|
||||
val declaringElement = parameter.parent.parent
|
||||
when (declaringElement) {
|
||||
is KtFunctionType -> return
|
||||
is KtNamedFunction ->
|
||||
addCorrespondingParameterDescriptor(getDescriptor(declaringElement, container) as FunctionDescriptor, parameter)
|
||||
is KtPrimaryConstructor -> {
|
||||
val ktClassOrObject: KtClassOrObject = declaringElement.getContainingClassOrObject()
|
||||
val classDescriptor = getDescriptor(ktClassOrObject, container) as ClassDescriptor
|
||||
addCorrespondingParameterDescriptor(classDescriptor.unsubstitutedPrimaryConstructor!!, parameter)
|
||||
}
|
||||
else -> super.visitParameter(parameter)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitPropertyAccessor(accessor: KtPropertyAccessor) {
|
||||
val property = accessor.property
|
||||
val propertyDescriptor = getDescriptor(property, container) as PropertyDescriptor
|
||||
if (accessor.isGetter) {
|
||||
descriptors.add(propertyDescriptor.getter!!)
|
||||
}
|
||||
else {
|
||||
descriptors.add(propertyDescriptor.setter!!)
|
||||
}
|
||||
accessor.acceptChildren(this)
|
||||
}
|
||||
|
||||
override fun visitAnonymousInitializer(initializer: KtAnonymousInitializer) {
|
||||
initializer.acceptChildren(this)
|
||||
}
|
||||
|
||||
override fun visitDeclaration(element: KtDeclaration) {
|
||||
val descriptor = getDescriptor(element, container)
|
||||
descriptors.add(descriptor)
|
||||
if (descriptor is ClassDescriptor) {
|
||||
// if class has primary constructor then we visit it later, otherwise add it artificially
|
||||
if (element !is KtClassOrObject || !element.hasExplicitPrimaryConstructor()) {
|
||||
if (descriptor.unsubstitutedPrimaryConstructor != null) {
|
||||
descriptors.add(descriptor.unsubstitutedPrimaryConstructor!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
element.acceptChildren(this)
|
||||
}
|
||||
|
||||
override fun visitKtElement(element: KtElement) {
|
||||
element.acceptChildren(this)
|
||||
}
|
||||
|
||||
private fun addCorrespondingParameterDescriptor(functionDescriptor: FunctionDescriptor, parameter: KtParameter) {
|
||||
for (valueParameterDescriptor in functionDescriptor.valueParameters) {
|
||||
if (valueParameterDescriptor.name == parameter.nameAsName) {
|
||||
descriptors.add(valueParameterDescriptor)
|
||||
}
|
||||
}
|
||||
parameter.acceptChildren(this)
|
||||
}
|
||||
})
|
||||
|
||||
val renderer = DescriptorRenderer.withOptions {
|
||||
classifierNamePolicy = ClassifierNamePolicy.FULLY_QUALIFIED
|
||||
modifiers = DescriptorRendererModifier.ALL
|
||||
}
|
||||
val renderedDescriptors = descriptors.map { renderer.render(it) }.joinToString(separator = "\n")
|
||||
|
||||
val document = DocumentImpl(psiFile.text)
|
||||
KtUsefulTestCase.assertSameLines(KotlinTestUtils.getLastCommentedLines(document), renderedDescriptors.toString())
|
||||
}
|
||||
|
||||
override fun createEnvironment(): KotlinCoreEnvironment {
|
||||
return createEnvironmentWithMockJdk(ConfigurationKind.JDK_ONLY)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import org.jetbrains.kotlin.checkers.CheckerTestUtilTest;
|
||||
import org.jetbrains.kotlin.psi.KtFile;
|
||||
|
||||
public abstract class AbstractResolveTest extends ExtensibleResolveTestCase {
|
||||
@Override
|
||||
protected ExpectedResolveData getExpectedResolveData() {
|
||||
final Project project = getProject();
|
||||
|
||||
return new ExpectedResolveData(
|
||||
ExpectedResolveDataUtil.prepareDefaultNameToDescriptors(project, getEnvironment()),
|
||||
ExpectedResolveDataUtil.prepareDefaultNameToDeclaration(project, getEnvironment())
|
||||
) {
|
||||
@Override
|
||||
protected KtFile createKtFile(String fileName, String text) {
|
||||
return CheckerTestUtilTest.createCheckAndReturnPsiFile(fileName, text, project);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,376 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiQualifiedNamedElement;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.analyzer.AnalysisResult;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic;
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils;
|
||||
import org.jetbrains.kotlin.diagnostics.Errors;
|
||||
import org.jetbrains.kotlin.name.FqName;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer;
|
||||
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform;
|
||||
import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil;
|
||||
import org.jetbrains.kotlin.types.ErrorUtils;
|
||||
import org.jetbrains.kotlin.types.KotlinType;
|
||||
import org.jetbrains.kotlin.types.TypeConstructor;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.jetbrains.kotlin.resolve.BindingContext.AMBIGUOUS_REFERENCE_TARGET;
|
||||
import static org.jetbrains.kotlin.resolve.BindingContext.REFERENCE_TARGET;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public abstract class ExpectedResolveData {
|
||||
|
||||
protected static final String STANDARD_PREFIX = "kotlin::";
|
||||
|
||||
private static class Position {
|
||||
private final PsiElement element;
|
||||
|
||||
private Position(KtFile file, int offset) {
|
||||
this.element = file.findElementAt(offset);
|
||||
}
|
||||
|
||||
public PsiElement getElement() {
|
||||
return element;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return DiagnosticUtils.atLocation(element);
|
||||
}
|
||||
}
|
||||
|
||||
private final Map<String, Position> declarationToPosition = Maps.newHashMap();
|
||||
private final Map<Position, String> positionToReference = Maps.newHashMap();
|
||||
private final Map<Position, String> positionToType = Maps.newHashMap();
|
||||
|
||||
private final Map<String, DeclarationDescriptor> nameToDescriptor;
|
||||
private final Map<String, PsiElement> nameToPsiElement;
|
||||
|
||||
public ExpectedResolveData(Map<String, DeclarationDescriptor> nameToDescriptor, Map<String, PsiElement> nameToPsiElement) {
|
||||
this.nameToDescriptor = nameToDescriptor;
|
||||
this.nameToPsiElement = nameToPsiElement;
|
||||
}
|
||||
|
||||
public final KtFile createFileFromMarkedUpText(String fileName, String text) {
|
||||
Map<String, Integer> declarationToIntPosition = Maps.newHashMap();
|
||||
Map<Integer, String> intPositionToReference = Maps.newHashMap();
|
||||
Map<Integer, String> intPositionToType = Maps.newHashMap();
|
||||
|
||||
Pattern pattern = Pattern.compile("(~[^~]+~)|(`[^`]+`)");
|
||||
while (true) {
|
||||
Matcher matcher = pattern.matcher(text);
|
||||
if (!matcher.find()) break;
|
||||
|
||||
String group = matcher.group();
|
||||
String name = group.substring(1, group.length() - 1);
|
||||
int start = matcher.start();
|
||||
if (group.startsWith("~")) {
|
||||
if (declarationToIntPosition.put(name, start) != null) {
|
||||
throw new IllegalArgumentException("Redeclaration: " + name);
|
||||
}
|
||||
}
|
||||
else if (group.startsWith("`")) {
|
||||
if (name.startsWith(":")) {
|
||||
intPositionToType.put(start - 1, name.substring(1));
|
||||
}
|
||||
else {
|
||||
intPositionToReference.put(start, name);
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
text = text.substring(0, start) + text.substring(matcher.end());
|
||||
}
|
||||
|
||||
KtFile ktFile = createKtFile(fileName, text);
|
||||
|
||||
for (Map.Entry<Integer, String> entry : intPositionToType.entrySet()) {
|
||||
positionToType.put(new Position(ktFile, entry.getKey()), entry.getValue());
|
||||
}
|
||||
for (Map.Entry<String, Integer> entry : declarationToIntPosition.entrySet()) {
|
||||
declarationToPosition.put(entry.getKey(), new Position(ktFile, entry.getValue()));
|
||||
}
|
||||
for (Map.Entry<Integer, String> entry : intPositionToReference.entrySet()) {
|
||||
positionToReference.put(new Position(ktFile, entry.getKey()), entry.getValue());
|
||||
}
|
||||
return ktFile;
|
||||
}
|
||||
|
||||
protected abstract KtFile createKtFile(String fileName, String text);
|
||||
|
||||
protected static BindingContext analyze(List<KtFile> files, KotlinCoreEnvironment environment) {
|
||||
if (files.isEmpty()) {
|
||||
System.err.println("Suspicious: no files");
|
||||
return BindingContext.EMPTY;
|
||||
}
|
||||
|
||||
Project project = files.iterator().next().getProject();
|
||||
AnalysisResult analysisResult = JvmResolveUtil.analyzeFilesWithJavaIntegration(project, files, environment);
|
||||
return analysisResult.getBindingContext();
|
||||
}
|
||||
|
||||
public final void checkResult(BindingContext bindingContext) {
|
||||
Set<PsiElement> unresolvedReferences = Sets.newHashSet();
|
||||
for (Diagnostic diagnostic : bindingContext.getDiagnostics()) {
|
||||
if (Errors.UNRESOLVED_REFERENCE_DIAGNOSTICS.contains(diagnostic.getFactory())) {
|
||||
unresolvedReferences.add(diagnostic.getPsiElement());
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, PsiElement> nameToDeclaration = Maps.newHashMap();
|
||||
|
||||
Map<PsiElement, String> declarationToName = Maps.newHashMap();
|
||||
for (Map.Entry<String, Position> entry : declarationToPosition.entrySet()) {
|
||||
String name = entry.getKey();
|
||||
Position position = entry.getValue();
|
||||
PsiElement element = position.getElement();
|
||||
|
||||
PsiElement ancestorOfType;
|
||||
|
||||
if (name.equals("file")) {
|
||||
ancestorOfType = element.getContainingFile();
|
||||
}
|
||||
else {
|
||||
ancestorOfType = getAncestorOfType(KtDeclaration.class, element);
|
||||
if (ancestorOfType == null) {
|
||||
KtPackageDirective directive = getAncestorOfType(KtPackageDirective.class, element);
|
||||
assert directive != null : "Not a declaration: " + name;
|
||||
ancestorOfType = element;
|
||||
}
|
||||
}
|
||||
nameToDeclaration.put(name, ancestorOfType);
|
||||
declarationToName.put(ancestorOfType, name);
|
||||
}
|
||||
|
||||
for (Map.Entry<Position, String> entry : positionToReference.entrySet()) {
|
||||
Position position = entry.getKey();
|
||||
String name = entry.getValue();
|
||||
PsiElement element = position.getElement();
|
||||
|
||||
KtReferenceExpression referenceExpression = PsiTreeUtil.getParentOfType(element, KtReferenceExpression.class);
|
||||
DeclarationDescriptor referenceTarget = bindingContext.get(REFERENCE_TARGET, referenceExpression);
|
||||
if ("!".equals(name)) {
|
||||
assertTrue(
|
||||
"Must have been unresolved: " +
|
||||
renderReferenceInContext(referenceExpression) +
|
||||
" but was resolved to " + renderNullableDescriptor(referenceTarget),
|
||||
unresolvedReferences.contains(referenceExpression));
|
||||
|
||||
assertTrue(
|
||||
String.format("Reference =%s= has a reference target =%s= but expected to be unresolved",
|
||||
renderReferenceInContext(referenceExpression), renderNullableDescriptor(referenceTarget)),
|
||||
referenceTarget == null);
|
||||
|
||||
continue;
|
||||
}
|
||||
if ("!!".equals(name)) {
|
||||
assertTrue(
|
||||
"Must have been resolved to multiple descriptors: " +
|
||||
renderReferenceInContext(referenceExpression) +
|
||||
" but was resolved to " + renderNullableDescriptor(referenceTarget),
|
||||
bindingContext.get(AMBIGUOUS_REFERENCE_TARGET, referenceExpression) != null);
|
||||
continue;
|
||||
}
|
||||
else if ("!null".equals(name)) {
|
||||
assertTrue(
|
||||
"Must have been resolved to null: " +
|
||||
renderReferenceInContext(referenceExpression) +
|
||||
" but was resolved to " + renderNullableDescriptor(referenceTarget),
|
||||
referenceTarget == null
|
||||
);
|
||||
continue;
|
||||
}
|
||||
else if ("!error".equals(name)) {
|
||||
assertTrue(
|
||||
"Must have been resolved to error: " +
|
||||
renderReferenceInContext(referenceExpression) +
|
||||
" but was resolved to " + renderNullableDescriptor(referenceTarget),
|
||||
ErrorUtils.isError(referenceTarget)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
PsiElement expected = nameToDeclaration.get(name);
|
||||
if (expected == null) {
|
||||
expected = nameToPsiElement.get(name);
|
||||
}
|
||||
|
||||
KtReferenceExpression reference = getAncestorOfType(KtReferenceExpression.class, element);
|
||||
if (expected == null && name.startsWith(STANDARD_PREFIX)) {
|
||||
DeclarationDescriptor expectedDescriptor = nameToDescriptor.get(name);
|
||||
KtTypeReference typeReference = getAncestorOfType(KtTypeReference.class, element);
|
||||
if (expectedDescriptor != null) {
|
||||
DeclarationDescriptor actual = bindingContext.get(REFERENCE_TARGET, reference);
|
||||
assertSame("Expected: " + name, expectedDescriptor.getOriginal(), actual == null
|
||||
? null
|
||||
: actual.getOriginal());
|
||||
continue;
|
||||
}
|
||||
|
||||
KotlinType actualType = bindingContext.get(BindingContext.TYPE, typeReference);
|
||||
assertNotNull("Type " + name + " not resolved for reference " + name, actualType);
|
||||
ClassifierDescriptor expectedClass = getBuiltinClass(name.substring(STANDARD_PREFIX.length()));
|
||||
assertSame("Type resolution mismatch: ", expectedClass.getTypeConstructor(), actualType.getConstructor());
|
||||
continue;
|
||||
}
|
||||
assert expected != null : "No declaration for " + name;
|
||||
|
||||
if (referenceTarget instanceof PackageViewDescriptor) {
|
||||
KtPackageDirective expectedDirective = PsiTreeUtil.getParentOfType(expected, KtPackageDirective.class);
|
||||
FqName expectedFqName;
|
||||
if (expectedDirective != null) {
|
||||
expectedFqName = expectedDirective.getFqName();
|
||||
}
|
||||
else if (expected instanceof PsiQualifiedNamedElement) {
|
||||
String qualifiedName = ((PsiQualifiedNamedElement) expected).getQualifiedName();
|
||||
assert qualifiedName != null : "No qualified name for " + name;
|
||||
expectedFqName = new FqName(qualifiedName);
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException(expected.getClass().getName() + " name=" + name);
|
||||
}
|
||||
assertEquals(expectedFqName, ((PackageViewDescriptor) referenceTarget).getFqName());
|
||||
continue;
|
||||
}
|
||||
|
||||
PsiElement actual = referenceTarget == null
|
||||
? bindingContext.get(BindingContext.LABEL_TARGET, referenceExpression)
|
||||
: DescriptorToSourceUtils.descriptorToDeclaration(referenceTarget);
|
||||
if (actual instanceof KtSimpleNameExpression) {
|
||||
actual = ((KtSimpleNameExpression)actual).getIdentifier();
|
||||
}
|
||||
|
||||
String actualName = null;
|
||||
if (actual != null) {
|
||||
actualName = declarationToName.get(actual);
|
||||
if (actualName == null) {
|
||||
actualName = actual.toString();
|
||||
}
|
||||
}
|
||||
assertNotNull(element.getText(), reference);
|
||||
|
||||
assertEquals(
|
||||
"Reference `" + name + "`" + renderReferenceInContext(reference) + " is resolved into " + actualName + ".",
|
||||
expected, actual);
|
||||
}
|
||||
|
||||
for (Map.Entry<Position, String> entry : positionToType.entrySet()) {
|
||||
Position position = entry.getKey();
|
||||
String typeName = entry.getValue();
|
||||
|
||||
PsiElement element = position.getElement();
|
||||
KtExpression expression = getAncestorOfType(KtExpression.class, element);
|
||||
|
||||
KotlinType expressionType = bindingContext.getType(expression);
|
||||
TypeConstructor expectedTypeConstructor;
|
||||
if (typeName.startsWith(STANDARD_PREFIX)) {
|
||||
String name = typeName.substring(STANDARD_PREFIX.length());
|
||||
ClassifierDescriptor expectedClass = getBuiltinClass(name);
|
||||
expectedTypeConstructor = expectedClass.getTypeConstructor();
|
||||
}
|
||||
else {
|
||||
Position declarationPosition = declarationToPosition.get(typeName);
|
||||
assertNotNull("Undeclared: " + typeName, declarationPosition);
|
||||
PsiElement declElement = declarationPosition.getElement();
|
||||
assertNotNull(declarationPosition);
|
||||
KtDeclaration declaration = getAncestorOfType(KtDeclaration.class, declElement);
|
||||
assertNotNull(declaration);
|
||||
if (declaration instanceof KtClass) {
|
||||
ClassDescriptor classDescriptor = bindingContext.get(BindingContext.CLASS, declaration);
|
||||
expectedTypeConstructor = classDescriptor.getTypeConstructor();
|
||||
}
|
||||
else if (declaration instanceof KtTypeParameter) {
|
||||
TypeParameterDescriptor typeParameterDescriptor = bindingContext.get(BindingContext.TYPE_PARAMETER, (KtTypeParameter) declaration);
|
||||
expectedTypeConstructor = typeParameterDescriptor.getTypeConstructor();
|
||||
}
|
||||
else {
|
||||
fail("Unsupported declaration: " + declaration);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
assertNotNull(expression.getText() + " type is null", expressionType);
|
||||
assertSame("At " + position + ": ", expectedTypeConstructor, expressionType.getConstructor());
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static ClassifierDescriptor getBuiltinClass(String nameOrFqName) {
|
||||
ClassifierDescriptor expectedClass;
|
||||
|
||||
if (nameOrFqName.indexOf('.') >= 0) {
|
||||
expectedClass = JvmPlatform.INSTANCE.getBuiltIns().getBuiltInClassByFqNameNullable(FqName.fromSegments(Arrays.asList(nameOrFqName.split("\\."))));
|
||||
}
|
||||
else {
|
||||
expectedClass = JvmPlatform.INSTANCE.getBuiltIns().getBuiltInClassByNameNullable(Name.identifier(nameOrFqName));
|
||||
}
|
||||
assertNotNull("Expected class not found: " + nameOrFqName, expectedClass);
|
||||
|
||||
return expectedClass;
|
||||
}
|
||||
|
||||
private static String renderReferenceInContext(KtReferenceExpression referenceExpression) {
|
||||
KtExpression statement = referenceExpression;
|
||||
while (true) {
|
||||
PsiElement parent = statement.getParent();
|
||||
if (!(parent instanceof KtExpression)) break;
|
||||
if (parent instanceof KtBlockExpression) break;
|
||||
statement = (KtExpression) parent;
|
||||
}
|
||||
KtDeclaration declaration = PsiTreeUtil.getParentOfType(referenceExpression, KtDeclaration.class);
|
||||
|
||||
|
||||
|
||||
return referenceExpression.getText() + " at " + DiagnosticUtils.atLocation(referenceExpression) +
|
||||
" in " + statement.getText() + (declaration == null ? "" : " in " + declaration.getText());
|
||||
}
|
||||
|
||||
private static <T> T getAncestorOfType(Class<T> type, PsiElement element) {
|
||||
while (element != null && !type.isInstance(element)) {
|
||||
element = element.getParent();
|
||||
}
|
||||
@SuppressWarnings({"unchecked", "UnnecessaryLocalVariable"})
|
||||
T result = (T) element;
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String renderNullableDescriptor(@Nullable DeclarationDescriptor d) {
|
||||
return d == null ? "<null>" : DescriptorRenderer.FQ_NAMES_IN_TYPES.render(d);
|
||||
}
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl;
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
|
||||
import org.jetbrains.kotlin.name.FqName;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
||||
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults;
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfoFactory;
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
|
||||
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform;
|
||||
import org.jetbrains.kotlin.resolve.lazy.LazyResolveTestUtil;
|
||||
import org.jetbrains.kotlin.resolve.scopes.ImportingScope;
|
||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScopeImpl;
|
||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScopeKind;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.tests.di.ContainerForTests;
|
||||
import org.jetbrains.kotlin.tests.di.InjectionKt;
|
||||
import org.jetbrains.kotlin.types.KotlinType;
|
||||
import org.jetbrains.kotlin.types.TypeUtils;
|
||||
import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext;
|
||||
import org.jetbrains.kotlin.types.expressions.FakeCallKind;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
public class ExpectedResolveDataUtil {
|
||||
private ExpectedResolveDataUtil() {
|
||||
}
|
||||
|
||||
public static Map<String, DeclarationDescriptor> prepareDefaultNameToDescriptors(Project project, KotlinCoreEnvironment environment) {
|
||||
KotlinBuiltIns builtIns = JvmPlatform.INSTANCE.getBuiltIns();
|
||||
|
||||
Map<String, DeclarationDescriptor> nameToDescriptor = new HashMap<String, DeclarationDescriptor>();
|
||||
nameToDescriptor.put("kotlin::Int.plus(Int)", standardFunction(builtIns.getInt(), "plus", project, builtIns.getIntType()));
|
||||
FunctionDescriptor descriptorForGet = standardFunction(builtIns.getArray(), "get", project, builtIns.getIntType());
|
||||
nameToDescriptor.put("kotlin::Array.get(Int)", descriptorForGet.getOriginal());
|
||||
nameToDescriptor.put("kotlin::Int.compareTo(Double)", standardFunction(builtIns.getInt(), "compareTo", project, builtIns.getDoubleType()));
|
||||
@NotNull
|
||||
FunctionDescriptor descriptorForSet = standardFunction(builtIns.getArray(), "set", project, builtIns.getIntType(), builtIns.getIntType());
|
||||
nameToDescriptor.put("kotlin::Array.set(Int, Int)", descriptorForSet.getOriginal());
|
||||
|
||||
return nameToDescriptor;
|
||||
}
|
||||
|
||||
public static Map<String, PsiElement> prepareDefaultNameToDeclaration(Project project, KotlinCoreEnvironment environment) {
|
||||
Map<String, PsiElement> nameToDeclaration = new HashMap<String, PsiElement>();
|
||||
|
||||
PsiClass java_util_Collections = findClass("java.util.Collections", project, environment);
|
||||
nameToDeclaration.put("java::java.util.Collections.emptyList()", findMethod(java_util_Collections, "emptyList"));
|
||||
nameToDeclaration.put("java::java.util.Collections", java_util_Collections);
|
||||
PsiClass java_util_List = findClass("java.util.ArrayList", project, environment);
|
||||
nameToDeclaration.put("java::java.util.List", findClass("java.util.List", project, environment));
|
||||
nameToDeclaration.put("java::java.util.ArrayList", java_util_List);
|
||||
nameToDeclaration.put("java::java.util.ArrayList.set()", java_util_List.findMethodsByName("set", true)[0]);
|
||||
nameToDeclaration.put("java::java.util.ArrayList.get()", java_util_List.findMethodsByName("get", true)[0]);
|
||||
nameToDeclaration.put("java::java", findPackage("java", project));
|
||||
nameToDeclaration.put("java::java.util", findPackage("java.util", project));
|
||||
nameToDeclaration.put("java::java.lang", findPackage("java.lang", project));
|
||||
nameToDeclaration.put("java::java.lang.Object", findClass("java.lang.Object", project, environment));
|
||||
nameToDeclaration.put("java::java.lang.Comparable", findClass("java.lang.Comparable", project, environment));
|
||||
PsiClass java_lang_System = findClass("java.lang.System", project, environment);
|
||||
nameToDeclaration.put("java::java.lang.System", java_lang_System);
|
||||
PsiMethod[] methods = findClass("java.io.PrintStream", project, environment).findMethodsByName("print", true);
|
||||
nameToDeclaration.put("java::java.io.PrintStream.print(Object)", methods[8]);
|
||||
nameToDeclaration.put("java::java.io.PrintStream.print(Int)", methods[2]);
|
||||
nameToDeclaration.put("java::java.io.PrintStream.print(char[])", methods[6]);
|
||||
nameToDeclaration.put("java::java.io.PrintStream.print(Double)", methods[5]);
|
||||
PsiField outField = java_lang_System.findFieldByName("out", true);
|
||||
assertNotNull("'out' property wasn't found", outField);
|
||||
nameToDeclaration.put("java::java.lang.System.out", outField);
|
||||
PsiClass java_lang_Number = findClass("java.lang.Number", project, environment);
|
||||
nameToDeclaration.put("java::java.lang.Number", java_lang_Number);
|
||||
nameToDeclaration.put("java::java.lang.Number.intValue()", java_lang_Number.findMethodsByName("intValue", true)[0]);
|
||||
|
||||
return nameToDeclaration;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static PsiElement findPackage(String qualifiedName, Project project) {
|
||||
JavaPsiFacade javaFacade = JavaPsiFacade.getInstance(project);
|
||||
PsiPackage javaFacadePackage = javaFacade.findPackage(qualifiedName);
|
||||
assertNotNull("Package wasn't found: " + qualifiedName, javaFacadePackage);
|
||||
return javaFacadePackage;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static PsiMethod findMethod(PsiClass psiClass, String name) {
|
||||
PsiMethod[] emptyLists = psiClass.findMethodsByName(name, true);
|
||||
return emptyLists[0];
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static PsiClass findClass(String qualifiedName, Project project, KotlinCoreEnvironment environment) {
|
||||
ModuleDescriptor module = LazyResolveTestUtil.resolveProject(project, environment);
|
||||
ClassDescriptor classDescriptor = DescriptorUtilsKt.resolveTopLevelClass(module, new FqName(qualifiedName), NoLookupLocation.FROM_TEST);
|
||||
assertNotNull("Class descriptor wasn't resolved: " + qualifiedName, classDescriptor);
|
||||
PsiClass psiClass = (PsiClass) DescriptorToSourceUtils.getSourceFromDescriptor(classDescriptor);
|
||||
assertNotNull("Class declaration wasn't found: " + classDescriptor, psiClass);
|
||||
return psiClass;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static FunctionDescriptor standardFunction(
|
||||
ClassDescriptor classDescriptor,
|
||||
String name,
|
||||
Project project,
|
||||
KotlinType... parameterTypes
|
||||
) {
|
||||
ModuleDescriptorImpl emptyModule = KotlinTestUtils.createEmptyModule();
|
||||
ContainerForTests container = InjectionKt.createContainerForTests(project, emptyModule);
|
||||
emptyModule.setDependencies(emptyModule);
|
||||
emptyModule.initialize(PackageFragmentProvider.Empty.INSTANCE);
|
||||
|
||||
LexicalScopeImpl lexicalScope = new LexicalScopeImpl(ImportingScope.Empty.INSTANCE, classDescriptor, false,
|
||||
classDescriptor.getThisAsReceiverParameter(),
|
||||
LexicalScopeKind.SYNTHETIC);
|
||||
|
||||
ExpressionTypingContext context = ExpressionTypingContext.newContext(
|
||||
new BindingTraceContext(), lexicalScope,
|
||||
DataFlowInfoFactory.EMPTY, TypeUtils.NO_EXPECTED_TYPE);
|
||||
|
||||
OverloadResolutionResults<FunctionDescriptor> functions = container.getFakeCallResolver().resolveFakeCall(
|
||||
context, null, Name.identifier(name), null, null, FakeCallKind.OTHER, parameterTypes);
|
||||
|
||||
for (ResolvedCall<? extends FunctionDescriptor> resolvedCall : functions.getResultingCalls()) {
|
||||
List<ValueParameterDescriptor> unsubstitutedValueParameters = resolvedCall.getResultingDescriptor().getValueParameters();
|
||||
for (int i = 0, unsubstitutedValueParametersSize = unsubstitutedValueParameters.size(); i < unsubstitutedValueParametersSize; i++) {
|
||||
ValueParameterDescriptor unsubstitutedValueParameter = unsubstitutedValueParameters.get(i);
|
||||
if (unsubstitutedValueParameter.getType().equals(parameterTypes[i])) {
|
||||
return resolvedCall.getResultingDescriptor();
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Not found: kotlin::" + classDescriptor.getName() + "." + name + "(" +
|
||||
Arrays.toString(parameterTypes) + ")");
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve;
|
||||
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
|
||||
import org.jetbrains.kotlin.psi.KtFile;
|
||||
import org.jetbrains.kotlin.test.ConfigurationKind;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.test.KotlinTestWithEnvironment;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class ExtensibleResolveTestCase extends KotlinTestWithEnvironment {
|
||||
private ExpectedResolveData expectedResolveData;
|
||||
|
||||
@Override
|
||||
protected KotlinCoreEnvironment createEnvironment() {
|
||||
return createEnvironmentWithMockJdk(ConfigurationKind.JDK_ONLY);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
expectedResolveData = getExpectedResolveData();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
expectedResolveData = null;
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
protected abstract ExpectedResolveData getExpectedResolveData();
|
||||
|
||||
protected void doTest(@NonNls String filePath) throws Exception {
|
||||
File file = new File(filePath);
|
||||
String text = KotlinTestUtils.doLoadFile(file);
|
||||
List<KtFile> files = KotlinTestUtils.createTestFiles("file.kt", text, new KotlinTestUtils.TestFileFactoryNoModules<KtFile>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public KtFile create(@NotNull String fileName, @NotNull String text, @NotNull Map<String, String> directives) {
|
||||
return expectedResolveData.createFileFromMarkedUpText(fileName, text);
|
||||
}
|
||||
});
|
||||
expectedResolveData.checkResult(ExpectedResolveData.analyze(files, getEnvironment()));
|
||||
}
|
||||
}
|
||||
-399
@@ -1,399 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.annotation;
|
||||
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.Function;
|
||||
import kotlin.Unit;
|
||||
import kotlin.jvm.functions.Function1;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.analyzer.AnalysisResult;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget;
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationWithTarget;
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
|
||||
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor;
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
|
||||
import org.jetbrains.kotlin.name.FqName;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.psi.KtAnnotationEntry;
|
||||
import org.jetbrains.kotlin.psi.KtAnnotationUseSiteTarget;
|
||||
import org.jetbrains.kotlin.psi.KtFile;
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer;
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRendererModifier;
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRendererOptions;
|
||||
import org.jetbrains.kotlin.renderer.ClassifierNamePolicy;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.test.KotlinTestWithEnvironment;
|
||||
import org.jetbrains.kotlin.types.TypeProjection;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.jetbrains.kotlin.resolve.DescriptorUtils.isNonCompanionObject;
|
||||
|
||||
public abstract class AbstractAnnotationDescriptorResolveTest extends KotlinTestWithEnvironment {
|
||||
private static final DescriptorRenderer WITH_ANNOTATION_ARGUMENT_TYPES = DescriptorRenderer.Companion.withOptions(
|
||||
new Function1<DescriptorRendererOptions, Unit>() {
|
||||
@Override
|
||||
public Unit invoke(DescriptorRendererOptions options) {
|
||||
options.setVerbose(true);
|
||||
options.setClassifierNamePolicy(ClassifierNamePolicy.SHORT.INSTANCE);
|
||||
options.setModifiers(DescriptorRendererModifier.ALL);
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
private static final String PATH = "compiler/testData/resolveAnnotations/testFile.kt";
|
||||
|
||||
private static final FqName PACKAGE = new FqName("test");
|
||||
|
||||
protected BindingContext context;
|
||||
|
||||
@Override
|
||||
protected KotlinCoreEnvironment createEnvironment() {
|
||||
return KotlinTestUtils.createEnvironmentWithMockJdkAndIdeaAnnotations(getTestRootDisposable());
|
||||
}
|
||||
|
||||
protected void doTest(@NotNull String content, @NotNull String expectedAnnotation) {
|
||||
checkAnnotationOnAllExceptLocalDeclarations(content, expectedAnnotation);
|
||||
checkAnnotationOnLocalDeclarations(expectedAnnotation);
|
||||
}
|
||||
|
||||
protected void checkAnnotationOnAllExceptLocalDeclarations(String content, String expectedAnnotation) {
|
||||
KtFile testFile = getFile(content);
|
||||
PackageFragmentDescriptor testPackage = getPackage(testFile);
|
||||
|
||||
checkAnnotationsOnFile(expectedAnnotation, testFile);
|
||||
|
||||
ClassDescriptor myClass = getClassDescriptor(testPackage, "MyClass");
|
||||
checkDescriptor(expectedAnnotation, myClass);
|
||||
ClassDescriptor companionObjectDescriptor = myClass.getCompanionObjectDescriptor();
|
||||
assert companionObjectDescriptor != null : "Cannot find companion object for class " + myClass.getName();
|
||||
checkDescriptor(expectedAnnotation, companionObjectDescriptor);
|
||||
checkDescriptor(expectedAnnotation, getInnerClassDescriptor(myClass, "InnerClass"));
|
||||
|
||||
FunctionDescriptor foo = getFunctionDescriptor(myClass, "foo");
|
||||
checkAnnotationsOnFunction(expectedAnnotation, foo);
|
||||
|
||||
SimpleFunctionDescriptor anonymousFun = getAnonymousFunDescriptor();
|
||||
if (anonymousFun instanceof AnonymousFunctionDescriptor) {
|
||||
for (ValueParameterDescriptor descriptor : anonymousFun.getValueParameters()) {
|
||||
checkDescriptor(expectedAnnotation, descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
PropertyDescriptor prop = getPropertyDescriptor(myClass, "prop");
|
||||
checkAnnotationsOnProperty(expectedAnnotation, prop);
|
||||
|
||||
FunctionDescriptor topFoo = getFunctionDescriptor(testPackage, "topFoo");
|
||||
checkAnnotationsOnFunction(expectedAnnotation, topFoo);
|
||||
|
||||
PropertyDescriptor topProp = getPropertyDescriptor(testPackage, "topProp", true);
|
||||
checkAnnotationsOnProperty(expectedAnnotation, topProp);
|
||||
|
||||
checkDescriptor(expectedAnnotation, getClassDescriptor(testPackage, "MyObject"));
|
||||
|
||||
checkDescriptor(expectedAnnotation, getConstructorParameterDescriptor(myClass, "consProp"));
|
||||
checkDescriptor(expectedAnnotation, getConstructorParameterDescriptor(myClass, "param"));
|
||||
}
|
||||
|
||||
private void checkAnnotationsOnFile(String expectedAnnotation, KtFile file) {
|
||||
String actualAnnotation = StringUtil.join(file.getAnnotationEntries(), new Function<KtAnnotationEntry, String>() {
|
||||
@Override
|
||||
public String fun(KtAnnotationEntry annotationEntry) {
|
||||
AnnotationDescriptor annotationDescriptor = context.get(BindingContext.ANNOTATION, annotationEntry);
|
||||
assertNotNull(annotationDescriptor);
|
||||
|
||||
KtAnnotationUseSiteTarget target = annotationEntry.getUseSiteTarget();
|
||||
|
||||
if (target != null) {
|
||||
return WITH_ANNOTATION_ARGUMENT_TYPES.renderAnnotation(
|
||||
annotationDescriptor, target.getAnnotationUseSiteTarget());
|
||||
}
|
||||
|
||||
return WITH_ANNOTATION_ARGUMENT_TYPES.renderAnnotation(annotationDescriptor, null);
|
||||
}
|
||||
}, " ");
|
||||
|
||||
String expectedAnnotationWithTarget = "@" + AnnotationUseSiteTarget.FILE.getRenderName() + ":" + expectedAnnotation.substring(1);
|
||||
|
||||
assertEquals(expectedAnnotationWithTarget, actualAnnotation);
|
||||
}
|
||||
|
||||
private void checkAnnotationOnLocalDeclarations(String expectedAnnotation) {
|
||||
checkDescriptor(expectedAnnotation, getLocalClassDescriptor("LocalClass"));
|
||||
checkDescriptor(expectedAnnotation, getLocalObjectDescriptor("LocalObject"));
|
||||
checkDescriptor(expectedAnnotation, getLocalFunDescriptor("localFun"));
|
||||
checkDescriptor(expectedAnnotation, getLocalVarDescriptor(context, "localVar"));
|
||||
}
|
||||
|
||||
private static void checkAnnotationsOnProperty(String expectedAnnotation, PropertyDescriptor prop) {
|
||||
checkDescriptorWithTarget(expectedAnnotation, prop, AnnotationUseSiteTarget.FIELD);
|
||||
checkDescriptor(expectedAnnotation, prop.getGetter());
|
||||
PropertySetterDescriptor propSetter = prop.getSetter();
|
||||
assertNotNull(propSetter);
|
||||
checkAnnotationsOnFunction(expectedAnnotation, propSetter);
|
||||
}
|
||||
|
||||
private static void checkAnnotationsOnFunction(String expectedAnnotation, FunctionDescriptor foo) {
|
||||
checkDescriptor(expectedAnnotation, foo);
|
||||
checkDescriptor(expectedAnnotation, getFunctionParameterDescriptor(foo, "param"));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected static FunctionDescriptor getFunctionDescriptor(@NotNull PackageFragmentDescriptor packageView, @NotNull String name) {
|
||||
Name functionName = Name.identifier(name);
|
||||
MemberScope memberScope = packageView.getMemberScope();
|
||||
Collection<SimpleFunctionDescriptor> functions = memberScope.getContributedFunctions(functionName, NoLookupLocation.FROM_TEST);
|
||||
assert functions.size() == 1 : "Failed to find function " + functionName + " in class" + "." + packageView.getName();
|
||||
return functions.iterator().next();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static FunctionDescriptor getFunctionDescriptor(@NotNull ClassDescriptor classDescriptor, @NotNull String name) {
|
||||
Name functionName = Name.identifier(name);
|
||||
MemberScope memberScope = classDescriptor.getMemberScope(Collections.<TypeProjection>emptyList());
|
||||
Collection<SimpleFunctionDescriptor> functions = memberScope.getContributedFunctions(functionName, NoLookupLocation.FROM_TEST);
|
||||
assert functions.size() == 1 : "Failed to find function " + functionName + " in class" + "." + classDescriptor.getName();
|
||||
return functions.iterator().next();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected static PropertyDescriptor getPropertyDescriptor(@NotNull PackageFragmentDescriptor packageView, @NotNull String name, boolean failOnMissing) {
|
||||
Name propertyName = Name.identifier(name);
|
||||
MemberScope memberScope = packageView.getMemberScope();
|
||||
Collection<PropertyDescriptor> properties = memberScope.getContributedVariables(propertyName, NoLookupLocation.FROM_TEST);
|
||||
if (properties.isEmpty()) {
|
||||
for (DeclarationDescriptor descriptor : DescriptorUtils.getAllDescriptors(memberScope)) {
|
||||
if (descriptor instanceof ClassDescriptor) {
|
||||
Collection<PropertyDescriptor> classProperties =
|
||||
((ClassDescriptor) descriptor).getMemberScope(Collections.<TypeProjection>emptyList())
|
||||
.getContributedVariables(propertyName, NoLookupLocation.FROM_TEST);
|
||||
if (!classProperties.isEmpty()) {
|
||||
properties = classProperties;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (failOnMissing) {
|
||||
assert properties.size() == 1 : "Failed to find property " + propertyName + " in class " + packageView.getName();
|
||||
}
|
||||
else if (properties.size() != 1) {
|
||||
return null;
|
||||
}
|
||||
return properties.iterator().next();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static PropertyDescriptor getPropertyDescriptor(@NotNull ClassDescriptor classDescriptor, @NotNull String name) {
|
||||
Name propertyName = Name.identifier(name);
|
||||
MemberScope memberScope = classDescriptor.getMemberScope(Collections.<TypeProjection>emptyList());
|
||||
Collection<PropertyDescriptor> properties = memberScope.getContributedVariables(propertyName, NoLookupLocation.FROM_TEST);
|
||||
assert properties.size() == 1 : "Failed to find property " + propertyName + " in class " + classDescriptor.getName();
|
||||
return properties.iterator().next();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected static ClassDescriptor getClassDescriptor(@NotNull PackageFragmentDescriptor packageView, @NotNull String name) {
|
||||
Name className = Name.identifier(name);
|
||||
ClassifierDescriptor aClass = packageView.getMemberScope().getContributedClassifier(className, NoLookupLocation.FROM_TEST);
|
||||
assertNotNull("Failed to find class: " + packageView.getName() + "." + className, aClass);
|
||||
assert aClass instanceof ClassDescriptor : "Not a class: " + aClass;
|
||||
return (ClassDescriptor) aClass;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static ClassDescriptor getInnerClassDescriptor(@NotNull ClassDescriptor classDescriptor, @NotNull String name) {
|
||||
Name propertyName = Name.identifier(name);
|
||||
MemberScope memberScope = classDescriptor.getMemberScope(Collections.<TypeProjection>emptyList());
|
||||
ClassifierDescriptor innerClass = memberScope.getContributedClassifier(propertyName, NoLookupLocation.FROM_TEST);
|
||||
assert innerClass instanceof ClassDescriptor : "Failed to find inner class " +
|
||||
propertyName +
|
||||
" in class " +
|
||||
classDescriptor.getName();
|
||||
return (ClassDescriptor) innerClass;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private ClassDescriptor getLocalClassDescriptor(@NotNull String name) {
|
||||
for (ClassDescriptor descriptor : context.getSliceContents(BindingContext.CLASS).values()) {
|
||||
if (descriptor.getName().asString().equals(name)) {
|
||||
return descriptor;
|
||||
}
|
||||
}
|
||||
|
||||
fail("Failed to find local class " + name);
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private ClassDescriptor getLocalObjectDescriptor(@NotNull String name) {
|
||||
ClassDescriptor localClassDescriptor = getLocalClassDescriptor(name);
|
||||
if (isNonCompanionObject(localClassDescriptor)) {
|
||||
return localClassDescriptor;
|
||||
}
|
||||
|
||||
fail("Failed to find local object " + name);
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private SimpleFunctionDescriptor getLocalFunDescriptor(@NotNull String name) {
|
||||
for (SimpleFunctionDescriptor descriptor : context.getSliceContents(BindingContext.FUNCTION).values()) {
|
||||
if (descriptor.getName().asString().equals(name)) {
|
||||
return descriptor;
|
||||
}
|
||||
}
|
||||
|
||||
fail("Failed to find local fun " + name);
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected static VariableDescriptor getLocalVarDescriptor(@NotNull BindingContext context, @NotNull String name) {
|
||||
for (VariableDescriptor descriptor : context.getSliceContents(BindingContext.VARIABLE).values()) {
|
||||
if (descriptor.getName().asString().equals(name)) {
|
||||
return descriptor;
|
||||
}
|
||||
}
|
||||
|
||||
fail("Failed to find local variable " + name);
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private SimpleFunctionDescriptor getAnonymousFunDescriptor() {
|
||||
for (SimpleFunctionDescriptor descriptor : context.getSliceContents(BindingContext.FUNCTION).values()) {
|
||||
if (descriptor instanceof AnonymousFunctionDescriptor) {
|
||||
return descriptor;
|
||||
}
|
||||
}
|
||||
|
||||
fail("Failed to find anonymous fun");
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static ValueParameterDescriptor getConstructorParameterDescriptor(
|
||||
@NotNull ClassDescriptor classDescriptor,
|
||||
@NotNull String name
|
||||
) {
|
||||
ConstructorDescriptor constructorDescriptor = getConstructorDescriptor(classDescriptor);
|
||||
ValueParameterDescriptor parameter = findValueParameter(constructorDescriptor.getValueParameters(), name);
|
||||
assertNotNull("Cannot find constructor parameter with name " + name, parameter);
|
||||
return parameter;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static ConstructorDescriptor getConstructorDescriptor(@NotNull ClassDescriptor classDescriptor) {
|
||||
Collection<ConstructorDescriptor> constructors = classDescriptor.getConstructors();
|
||||
assert constructors.size() == 1;
|
||||
return constructors.iterator().next();
|
||||
}
|
||||
|
||||
private static ValueParameterDescriptor findValueParameter(List<ValueParameterDescriptor> parameters, String name) {
|
||||
for (ValueParameterDescriptor parameter : parameters) {
|
||||
if (parameter.getName().asString().equals(name)) {
|
||||
return parameter;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static ValueParameterDescriptor getFunctionParameterDescriptor(
|
||||
@NotNull FunctionDescriptor functionDescriptor,
|
||||
@NotNull String name
|
||||
) {
|
||||
ValueParameterDescriptor parameter = findValueParameter(functionDescriptor.getValueParameters(), name);
|
||||
assertNotNull("Cannot find function parameter with name " + name, parameter);
|
||||
return parameter;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected KtFile getFile(@NotNull String content) {
|
||||
KtFile ktFile = KotlinTestUtils.createFile("dummy.kt", content, getProject());
|
||||
AnalysisResult analysisResult = KotlinTestUtils.analyzeFile(ktFile, getEnvironment());
|
||||
context = analysisResult.getBindingContext();
|
||||
|
||||
return ktFile;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected PackageFragmentDescriptor getPackage(@NotNull KtFile ktFile) {
|
||||
PackageFragmentDescriptor packageFragment = context.get(BindingContext.FILE_TO_PACKAGE_FRAGMENT, ktFile);
|
||||
assertNotNull("Failed to find package: " + PACKAGE, packageFragment);
|
||||
return packageFragment;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected PackageFragmentDescriptor getPackage(@NotNull String content) {
|
||||
return getPackage(getFile(content));
|
||||
}
|
||||
|
||||
protected static String getContent(@NotNull String annotationText) throws IOException {
|
||||
File file = new File(PATH);
|
||||
return KotlinTestUtils.doLoadFile(file).replaceAll("ANNOTATION", annotationText);
|
||||
}
|
||||
|
||||
private static String renderAnnotations(Annotations annotations, @Nullable final AnnotationUseSiteTarget defaultTarget) {
|
||||
return StringUtil.join(annotations.getAllAnnotations(), new Function<AnnotationWithTarget, String>() {
|
||||
@Override
|
||||
public String fun(AnnotationWithTarget annotationWithTarget) {
|
||||
AnnotationUseSiteTarget targetToRender = annotationWithTarget.getTarget();
|
||||
if (targetToRender == defaultTarget) {
|
||||
targetToRender = null;
|
||||
}
|
||||
|
||||
return WITH_ANNOTATION_ARGUMENT_TYPES.renderAnnotation(annotationWithTarget.getAnnotation(), targetToRender);
|
||||
}
|
||||
}, " ");
|
||||
}
|
||||
|
||||
protected static void checkDescriptor(String expectedAnnotation, DeclarationDescriptor member) {
|
||||
String actual = getAnnotations(member);
|
||||
assertEquals("Failed to resolve annotation descriptor for " + member.toString(), expectedAnnotation, actual);
|
||||
}
|
||||
|
||||
private static void checkDescriptorWithTarget(String expectedAnnotation, DeclarationDescriptor member, AnnotationUseSiteTarget target) {
|
||||
String actual = renderAnnotations(member.getAnnotations(), target);
|
||||
assertEquals("Failed to resolve annotation descriptor for " + member.toString(), expectedAnnotation, actual);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected static String getAnnotations(DeclarationDescriptor member) {
|
||||
return renderAnnotations(member.getAnnotations(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tearDown() throws Exception {
|
||||
context = null;
|
||||
super.tearDown();
|
||||
}
|
||||
}
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.annotation
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||
import java.io.File
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
|
||||
abstract class AbstractAnnotationParameterTest : AbstractAnnotationDescriptorResolveTest() {
|
||||
fun doTest(path: String) {
|
||||
val fileText = FileUtil.loadFile(File(path), true)
|
||||
val packageView = getPackage(fileText)
|
||||
val classDescriptor = AbstractAnnotationDescriptorResolveTest.getClassDescriptor(packageView, "MyClass")
|
||||
|
||||
val expected = InTextDirectivesUtils.findListWithPrefixes(fileText, "// EXPECTED: ").joinToString(", ")
|
||||
val actual = AbstractAnnotationDescriptorResolveTest.getAnnotations(classDescriptor)
|
||||
|
||||
KotlinTestUtils.assertEqualsToFile(File(path), fileText.replace(expected, actual))
|
||||
}
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||
import org.jetbrains.kotlin.psi.ValueArgument
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getParentResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ArgumentMapping
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.Receiver
|
||||
import org.jetbrains.kotlin.test.ConfigurationKind
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.jetbrains.kotlin.test.KotlinTestWithEnvironment
|
||||
import java.io.File
|
||||
|
||||
abstract class AbstractResolvedCallsTest : KotlinTestWithEnvironment() {
|
||||
override fun createEnvironment(): KotlinCoreEnvironment = createEnvironmentWithMockJdk(ConfigurationKind.ALL)
|
||||
|
||||
fun doTest(filePath: String) {
|
||||
val text = KotlinTestUtils.doLoadFile(File(filePath))!!
|
||||
|
||||
val jetFile = KtPsiFactory(project).createFile(text.replace("<caret>", ""))
|
||||
val bindingContext = JvmResolveUtil.analyzeOneFileWithJavaIntegration(jetFile, environment).bindingContext
|
||||
|
||||
val (element, cachedCall) = buildCachedCall(bindingContext, jetFile, text)
|
||||
|
||||
val resolvedCall = if (cachedCall !is VariableAsFunctionResolvedCall) cachedCall
|
||||
else if ("(" == element?.text) cachedCall.functionCall
|
||||
else cachedCall.variableCall
|
||||
|
||||
val resolvedCallInfoFileName = FileUtil.getNameWithoutExtension(filePath) + ".txt"
|
||||
KotlinTestUtils.assertEqualsToFile(File(resolvedCallInfoFileName), "$text\n\n\n${resolvedCall?.renderToText()}")
|
||||
}
|
||||
|
||||
open protected fun buildCachedCall(
|
||||
bindingContext: BindingContext, jetFile: KtFile, text: String
|
||||
): Pair<PsiElement?, ResolvedCall<out CallableDescriptor>?> {
|
||||
val element = jetFile.findElementAt(text.indexOf("<caret>"))!!
|
||||
val expression = element.getStrictParentOfType<KtExpression>()
|
||||
|
||||
val cachedCall = expression?.getParentResolvedCall(bindingContext, strict = false)
|
||||
return Pair(element, cachedCall)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun Receiver?.getText() = when (this) {
|
||||
is ExpressionReceiver -> "${expression.text} {${type}}"
|
||||
is ImplicitClassReceiver -> "Class{${type}}"
|
||||
is ExtensionReceiver -> "${type}Ext{${declarationDescriptor.getText()}}"
|
||||
null -> "NO_RECEIVER"
|
||||
else -> toString()
|
||||
}
|
||||
|
||||
private fun ValueArgument.getText() = this.getArgumentExpression()?.text?.replace("\n", " ") ?: ""
|
||||
|
||||
private fun ArgumentMapping.getText() = when (this) {
|
||||
is ArgumentMatch -> {
|
||||
val parameterType = DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(valueParameter.type)
|
||||
"${status.name} ${valueParameter.name} : ${parameterType} ="
|
||||
}
|
||||
else -> "ARGUMENT UNMAPPED: "
|
||||
}
|
||||
|
||||
private fun DeclarationDescriptor.getText(): String = when (this) {
|
||||
is ReceiverParameterDescriptor -> "${value.getText()}::this"
|
||||
else -> DescriptorRenderer.COMPACT_WITH_SHORT_TYPES.render(this)
|
||||
}
|
||||
|
||||
private fun ResolvedCall<*>.renderToText(): String {
|
||||
return buildString {
|
||||
appendln("Resolved call:")
|
||||
appendln()
|
||||
|
||||
if (candidateDescriptor != resultingDescriptor) {
|
||||
appendln("Candidate descriptor: ${candidateDescriptor!!.getText()}")
|
||||
}
|
||||
appendln("Resulting descriptor: ${resultingDescriptor!!.getText()}")
|
||||
appendln()
|
||||
|
||||
appendln("Explicit receiver kind = ${explicitReceiverKind}")
|
||||
appendln("Dispatch receiver = ${dispatchReceiver.getText()}")
|
||||
appendln("Extension receiver = ${extensionReceiver.getText()}")
|
||||
|
||||
val valueArguments = call.valueArguments
|
||||
if (!valueArguments.isEmpty()) {
|
||||
appendln()
|
||||
appendln("Value arguments mapping:")
|
||||
appendln()
|
||||
|
||||
for (valueArgument in valueArguments) {
|
||||
val argumentText = valueArgument!!.getText()
|
||||
val argumentMappingText = getArgumentMapping(valueArgument).getText()
|
||||
|
||||
appendln("$argumentMappingText $argumentText")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls
|
||||
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||
import org.jetbrains.kotlin.psi.KtSecondaryConstructor
|
||||
import org.jetbrains.kotlin.psi.debugText.getDebugText
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getParentResolvedCall
|
||||
|
||||
|
||||
abstract class AbstractResolvedConstructorDelegationCallsTests : AbstractResolvedCallsTest() {
|
||||
override fun buildCachedCall(
|
||||
bindingContext: BindingContext, jetFile: KtFile, text: String
|
||||
): Pair<PsiElement?, ResolvedCall<out CallableDescriptor>?> {
|
||||
val element = jetFile.findElementAt(text.indexOf("<caret>"))
|
||||
val constructor = element?.getNonStrictParentOfType<KtSecondaryConstructor>()!!
|
||||
val delegationCall = constructor.getDelegationCall()
|
||||
|
||||
val cachedCall = delegationCall.getParentResolvedCall(bindingContext, strict = false)
|
||||
return Pair(delegationCall, cachedCall)
|
||||
}
|
||||
}
|
||||
-117
@@ -1,117 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.constants.evaluate
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtProperty
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DelegatingBindingTrace
|
||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||
import org.jetbrains.kotlin.resolve.annotation.AbstractAnnotationDescriptorResolveTest
|
||||
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant
|
||||
import org.jetbrains.kotlin.resolve.constants.StringValue
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import java.io.File
|
||||
import java.util.regex.Pattern
|
||||
|
||||
abstract class AbstractCompileTimeConstantEvaluatorTest : AbstractAnnotationDescriptorResolveTest() {
|
||||
|
||||
// Test directives should look like [// val testedPropertyName: expectedValue]
|
||||
fun doConstantTest(path: String) {
|
||||
doTest(path) {
|
||||
property, context ->
|
||||
val compileTimeConstant = property.compileTimeInitializer
|
||||
if (compileTimeConstant is StringValue) {
|
||||
"\\\"${compileTimeConstant.value}\\\""
|
||||
} else {
|
||||
"$compileTimeConstant"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test directives should look like [// val testedPropertyName: expectedValue]
|
||||
fun doIsPureTest(path: String) {
|
||||
doTest(path) {
|
||||
property, context ->
|
||||
evaluateInitializer(context, property)?.isPure.toString()
|
||||
}
|
||||
}
|
||||
|
||||
// Test directives should look like [// val testedPropertyName: expectedValue]
|
||||
fun doUsesVariableAsConstantTest(path: String) {
|
||||
doTest(path) {
|
||||
property, context ->
|
||||
evaluateInitializer(context, property)?.usesVariableAsConstant.toString()
|
||||
}
|
||||
}
|
||||
|
||||
private fun evaluateInitializer(context: BindingContext, property: VariableDescriptor): CompileTimeConstant<*>? {
|
||||
val propertyDeclaration = DescriptorToSourceUtils.descriptorToDeclaration(property) as KtProperty
|
||||
val compileTimeConstant = ConstantExpressionEvaluator(property.builtIns).evaluateExpression(
|
||||
propertyDeclaration.initializer!!,
|
||||
DelegatingBindingTrace(context, "trace for evaluating compile time constant"),
|
||||
property.type
|
||||
)
|
||||
return compileTimeConstant
|
||||
}
|
||||
|
||||
private fun doTest(path: String, getValueToTest: (VariableDescriptor, BindingContext) -> String) {
|
||||
val myFile = File(path)
|
||||
val fileText = FileUtil.loadFile(myFile, true)
|
||||
val packageView = getPackage(fileText)
|
||||
|
||||
val propertiesForTest = getObjectsToTest(fileText)
|
||||
|
||||
val expectedActual = arrayListOf<Pair<String, String>>()
|
||||
|
||||
for (propertyName in propertiesForTest) {
|
||||
val expectedPropertyPrefix = "// val ${propertyName}: "
|
||||
val expected = InTextDirectivesUtils.findStringWithPrefixes(fileText, expectedPropertyPrefix)
|
||||
assertNotNull(expected, "Failed to find expected directive: $expectedPropertyPrefix")
|
||||
|
||||
val property = AbstractAnnotationDescriptorResolveTest.getPropertyDescriptor(packageView, propertyName, false)
|
||||
?: AbstractAnnotationDescriptorResolveTest.getLocalVarDescriptor(context!!, propertyName)
|
||||
|
||||
val testedObject = getValueToTest(property, context!!)
|
||||
expectedActual.add(expectedPropertyPrefix + expected!! to expectedPropertyPrefix + testedObject)
|
||||
}
|
||||
|
||||
var actualFileText = fileText
|
||||
for ((expected, actual) in expectedActual) {
|
||||
assert(actualFileText.contains(expected)) { "File text should contain $expected" }
|
||||
actualFileText = actualFileText.replace(expected, actual)
|
||||
}
|
||||
|
||||
KotlinTestUtils.assertEqualsToFile(myFile, actualFileText)
|
||||
}
|
||||
|
||||
fun getObjectsToTest(fileText: String): List<String> {
|
||||
return InTextDirectivesUtils.findListWithPrefixes(fileText, "// val").map {
|
||||
val matcher = pattern.matcher(it)
|
||||
if (matcher.find()) {
|
||||
matcher.group(0) ?: "Couldn't match tested object $it"
|
||||
} else "Couldn't match tested object $it"
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val pattern = Pattern.compile(".+(?=:)")
|
||||
}
|
||||
}
|
||||
-149
@@ -1,149 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.constraintSystem
|
||||
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.Renderers
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||
import org.jetbrains.kotlin.resolve.TypeResolver
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.CallHandle
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintContext
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilderImpl
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.SPECIAL
|
||||
import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil
|
||||
import org.jetbrains.kotlin.test.ConfigurationKind
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.jetbrains.kotlin.test.KotlinTestWithEnvironment
|
||||
import org.jetbrains.kotlin.tests.di.createContainerForTests
|
||||
import org.jetbrains.kotlin.types.ErrorUtils
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import java.io.File
|
||||
|
||||
abstract class AbstractConstraintSystemTest : KotlinTestWithEnvironment() {
|
||||
private var _typeResolver: TypeResolver? = null
|
||||
private val typeResolver: TypeResolver
|
||||
get() = _typeResolver!!
|
||||
|
||||
private var _testDeclarations: ConstraintSystemTestData? = null
|
||||
private val testDeclarations: ConstraintSystemTestData
|
||||
get() = _testDeclarations!!
|
||||
|
||||
override fun createEnvironment(): KotlinCoreEnvironment {
|
||||
return createEnvironmentWithMockJdk(ConfigurationKind.ALL)
|
||||
}
|
||||
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
|
||||
_typeResolver = createContainerForTests(project, KotlinTestUtils.createEmptyModule()).typeResolver
|
||||
_testDeclarations = analyzeDeclarations()
|
||||
}
|
||||
|
||||
override fun tearDown() {
|
||||
_typeResolver = null
|
||||
_testDeclarations = null
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
private val testDataPath: String
|
||||
get() = KotlinTestUtils.getTestDataPathBase() + "/constraintSystem/"
|
||||
|
||||
private fun analyzeDeclarations(): ConstraintSystemTestData {
|
||||
val fileName = "declarations.kt"
|
||||
|
||||
val psiFile = KotlinTestUtils.createFile(fileName, KotlinTestUtils.doLoadFile(testDataPath, fileName), project)
|
||||
val bindingContext = JvmResolveUtil.analyzeOneFileWithJavaIntegrationAndCheckForErrors(psiFile).bindingContext
|
||||
return ConstraintSystemTestData(bindingContext, project, typeResolver)
|
||||
}
|
||||
|
||||
fun doTest(filePath: String) {
|
||||
val constraintsFile = File(filePath)
|
||||
val constraintsFileText = constraintsFile.readLines()
|
||||
|
||||
val builder = ConstraintSystemBuilderImpl()
|
||||
|
||||
val variables = parseVariables(constraintsFileText)
|
||||
val fixVariables = constraintsFileText.contains("FIX_VARIABLES")
|
||||
val typeParameterDescriptors = variables.map { testDeclarations.getParameterDescriptor(it) }
|
||||
val substitutor = builder.registerTypeVariables(CallHandle.NONE, typeParameterDescriptors)
|
||||
|
||||
val constraints = parseConstraints(constraintsFileText)
|
||||
|
||||
fun getType(typeString: String): KotlinType {
|
||||
val type = testDeclarations.getType(typeString).apply {
|
||||
assert(!ErrorUtils.containsErrorType(this)) { "Type $this is resolved to or contains error type" }
|
||||
}
|
||||
return substitutor.substitute(type, Variance.INVARIANT) ?: error("Failed to substitute $type")
|
||||
}
|
||||
|
||||
for (constraint in constraints) {
|
||||
val firstType = getType(constraint.firstType)
|
||||
val secondType = getType(constraint.secondType)
|
||||
val context = ConstraintContext(SPECIAL.position(), initial = true)
|
||||
when (constraint.kind) {
|
||||
MyConstraintKind.SUBTYPE -> builder.addSubtypeConstraint(firstType, secondType, context.position)
|
||||
MyConstraintKind.SUPERTYPE -> builder.addSubtypeConstraint(secondType, firstType, context.position)
|
||||
MyConstraintKind.EQUAL -> builder.addConstraint(
|
||||
ConstraintSystemBuilderImpl.ConstraintKind.EQUAL, firstType, secondType, context
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (fixVariables) builder.fixVariables()
|
||||
|
||||
val system = builder.build()
|
||||
|
||||
val resultingStatus = Renderers.renderConstraintSystem(system, shortTypeBounds = true)
|
||||
|
||||
val resultingSubstitutor = system.resultingSubstitutor
|
||||
val result = typeParameterDescriptors.map {
|
||||
val parameterType = testDeclarations.getType(it.name.asString())
|
||||
val resultType = resultingSubstitutor.substitute(parameterType, Variance.INVARIANT)
|
||||
"${it.name}=${resultType?.let { DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(it) }}"
|
||||
}.joinToString("\n", prefix = "result:\n")
|
||||
|
||||
val boundsFile = File(filePath.replace("constraints", "bounds"))
|
||||
KotlinTestUtils.assertEqualsToFile(boundsFile, "${constraintsFileText.joinToString("\n")}\n\n$resultingStatus\n\n$result")
|
||||
}
|
||||
|
||||
class MyConstraint(val kind: MyConstraintKind, val firstType: String, val secondType: String)
|
||||
enum class MyConstraintKind(val token: String) {
|
||||
SUBTYPE("<:"), SUPERTYPE(">:"), EQUAL(":=")
|
||||
}
|
||||
|
||||
private fun parseVariables(lines: List<String>): List<String> {
|
||||
val first = lines.first()
|
||||
val variablesString = "VARIABLES "
|
||||
assert (first.startsWith(variablesString)) { "The first line should contain variables: $first"}
|
||||
val variables = first.substringAfter(variablesString).split(' ')
|
||||
return variables.toList()
|
||||
}
|
||||
|
||||
private fun parseConstraints(lines: List<String>): List<MyConstraint> {
|
||||
val kindsMap = MyConstraintKind.values().map { it.token to it }.toMap()
|
||||
val kinds = kindsMap.keys
|
||||
val linesWithConstraints = lines.filter { line -> kinds.any { kind -> line.contains(kind) } }
|
||||
return linesWithConstraints.map {
|
||||
line ->
|
||||
val kind = kinds.first { line.contains(it) }
|
||||
val firstType = line.substringBefore(kind).trim()
|
||||
val secondType = line.substringAfter(kind).trim()
|
||||
MyConstraint(kindsMap[kind]!!, firstType, secondType)
|
||||
}
|
||||
}
|
||||
}
|
||||
-78
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.constraintSystem
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.psi.KtFunction
|
||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||
import org.jetbrains.kotlin.resolve.TypeResolver
|
||||
import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstructor
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.KotlinTypeImpl
|
||||
import java.util.regex.Pattern
|
||||
|
||||
class ConstraintSystemTestData(
|
||||
context: BindingContext,
|
||||
private val project: Project,
|
||||
private val typeResolver: TypeResolver
|
||||
) {
|
||||
private val functionFoo: FunctionDescriptor
|
||||
private val scopeToResolveTypeParameters: LexicalScope
|
||||
|
||||
init {
|
||||
val functions = context.getSliceContents(BindingContext.FUNCTION)
|
||||
functionFoo = findFunctionByName(functions.values, "foo")
|
||||
val function = DescriptorToSourceUtils.descriptorToDeclaration(functionFoo) as KtFunction
|
||||
val fooBody = function.bodyExpression
|
||||
scopeToResolveTypeParameters = context.get(BindingContext.LEXICAL_SCOPE, fooBody)!!
|
||||
}
|
||||
|
||||
private fun findFunctionByName(functions: Collection<FunctionDescriptor>, name: String): FunctionDescriptor {
|
||||
return functions.firstOrNull { it.name.asString() == name } ?:
|
||||
throw AssertionError("Function ${name} is not declared")
|
||||
}
|
||||
|
||||
fun getParameterDescriptor(name: String): TypeParameterDescriptor {
|
||||
return functionFoo.typeParameters.firstOrNull { it.name.asString() == name } ?:
|
||||
throw AssertionError("Unsupported type parameter name: $name. You may add it to constraintSystem/declarations.kt")
|
||||
}
|
||||
|
||||
fun getType(name: String): KotlinType {
|
||||
val matcher = INTEGER_VALUE_TYPE_PATTERN.matcher(name)
|
||||
if (matcher.find()) {
|
||||
val number = matcher.group(1)!!
|
||||
return KotlinTypeImpl.create(
|
||||
Annotations.EMPTY, IntegerValueTypeConstructor(number.toLong(), functionFoo.builtIns), false, listOf(),
|
||||
MemberScope.Empty
|
||||
)
|
||||
}
|
||||
return typeResolver.resolveType(
|
||||
scopeToResolveTypeParameters, KtPsiFactory(project).createType(name),
|
||||
KotlinTestUtils.DUMMY_TRACE, true)
|
||||
}
|
||||
}
|
||||
|
||||
private val INTEGER_VALUE_TYPE_PATTERN = Pattern.compile("""IntegerValueType\((\d*)\)""")
|
||||
@@ -1,117 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.lazy;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.analyzer.AnalysisResult;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.CliLightClassGenerationSupport;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.JvmPackagePartProvider;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
|
||||
import org.jetbrains.kotlin.context.ModuleContext;
|
||||
import org.jetbrains.kotlin.descriptors.PackagePartProvider;
|
||||
import org.jetbrains.kotlin.psi.KtFile;
|
||||
import org.jetbrains.kotlin.resolve.AnalyzingUtils;
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace;
|
||||
import org.jetbrains.kotlin.resolve.jvm.TopDownAnalyzerFacadeForJVM;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
public class JvmResolveUtil {
|
||||
|
||||
public static String TEST_MODULE_NAME = "java-integration-test";
|
||||
@NotNull
|
||||
public static AnalysisResult analyzeOneFileWithJavaIntegrationAndCheckForErrors(@NotNull KtFile file) {
|
||||
return analyzeOneFileWithJavaIntegrationAndCheckForErrors(file, PackagePartProvider.Companion.getEMPTY());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static AnalysisResult analyzeOneFileWithJavaIntegrationAndCheckForErrors(@NotNull KtFile file, @NotNull PackagePartProvider provider) {
|
||||
AnalyzingUtils.checkForSyntacticErrors(file);
|
||||
|
||||
AnalysisResult analysisResult = analyzeOneFileWithJavaIntegration(file, provider);
|
||||
|
||||
AnalyzingUtils.throwExceptionOnErrors(analysisResult.getBindingContext());
|
||||
|
||||
return analysisResult;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static AnalysisResult analyzeOneFileWithJavaIntegration(@NotNull KtFile file, @NotNull KotlinCoreEnvironment environment) {
|
||||
return analyzeOneFileWithJavaIntegration(file, new JvmPackagePartProvider(environment));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static AnalysisResult analyzeOneFileWithJavaIntegration(@NotNull KtFile file, @NotNull PackagePartProvider provider) {
|
||||
return analyzeFilesWithJavaIntegration(file.getProject(), Collections.singleton(file), provider);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static AnalysisResult analyzeOneFileWithJavaIntegration(@NotNull KtFile file) {
|
||||
return analyzeOneFileWithJavaIntegration(file, PackagePartProvider.Companion.getEMPTY());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static AnalysisResult analyzeFilesWithJavaIntegrationAndCheckForErrors(
|
||||
@NotNull Project project,
|
||||
@NotNull Collection<KtFile> files
|
||||
) {
|
||||
return analyzeFilesWithJavaIntegrationAndCheckForErrors(project, files, PackagePartProvider.Companion.getEMPTY());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static AnalysisResult analyzeFilesWithJavaIntegrationAndCheckForErrors(
|
||||
@NotNull Project project,
|
||||
@NotNull Collection<KtFile> files,
|
||||
@NotNull PackagePartProvider packagePartProvider
|
||||
) {
|
||||
for (KtFile file : files) {
|
||||
AnalyzingUtils.checkForSyntacticErrors(file);
|
||||
}
|
||||
|
||||
AnalysisResult analysisResult = analyzeFilesWithJavaIntegration(project, files, packagePartProvider);
|
||||
|
||||
AnalyzingUtils.throwExceptionOnErrors(analysisResult.getBindingContext());
|
||||
|
||||
return analysisResult;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static AnalysisResult analyzeFilesWithJavaIntegration(
|
||||
@NotNull Project project,
|
||||
@NotNull Collection<KtFile> files,
|
||||
@NotNull KotlinCoreEnvironment environment
|
||||
) {
|
||||
return analyzeFilesWithJavaIntegration(project, files, new JvmPackagePartProvider(environment));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static AnalysisResult analyzeFilesWithJavaIntegration(
|
||||
@NotNull Project project,
|
||||
@NotNull Collection<KtFile> files,
|
||||
@NotNull PackagePartProvider packagePartProvider
|
||||
) {
|
||||
|
||||
ModuleContext moduleContext = TopDownAnalyzerFacadeForJVM.createContextWithSealedModule(project, TEST_MODULE_NAME);
|
||||
|
||||
BindingTrace trace = new CliLightClassGenerationSupport.CliBindingTrace();
|
||||
|
||||
return TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegrationWithCustomContext(moduleContext, files, trace, null, null,
|
||||
packagePartProvider);
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.lazy;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.CliLightClassGenerationSupport;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.JvmPackagePartProvider;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
|
||||
import org.jetbrains.kotlin.context.ModuleContext;
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.name.SpecialNames;
|
||||
import org.jetbrains.kotlin.psi.KtFile;
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace;
|
||||
import org.jetbrains.kotlin.resolve.TopDownAnalysisMode;
|
||||
import org.jetbrains.kotlin.resolve.jvm.TopDownAnalyzerFacadeForJVM;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class LazyResolveTestUtil {
|
||||
private LazyResolveTestUtil() {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static ModuleDescriptor resolveProject(@NotNull Project project, @NotNull KotlinCoreEnvironment environment) {
|
||||
return resolve(project, Collections.<KtFile>emptyList(), environment);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static ModuleDescriptor resolve(@NotNull Project project, @NotNull List<KtFile> sourceFiles, @NotNull KotlinCoreEnvironment environment) {
|
||||
return resolve(project, new CliLightClassGenerationSupport.NoScopeRecordCliBindingTrace(), sourceFiles, environment);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static ModuleDescriptor resolve(
|
||||
@NotNull Project project,
|
||||
@NotNull BindingTrace trace,
|
||||
@NotNull List<KtFile> sourceFiles,
|
||||
@NotNull KotlinCoreEnvironment environment
|
||||
) {
|
||||
ModuleContext moduleContext = TopDownAnalyzerFacadeForJVM.createContextWithSealedModule(project, JvmResolveUtil.TEST_MODULE_NAME);
|
||||
|
||||
TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegrationNoIncremental(
|
||||
moduleContext, sourceFiles, trace, TopDownAnalysisMode.TopLevelDeclarations,
|
||||
new JvmPackagePartProvider(environment)
|
||||
);
|
||||
|
||||
return moduleContext.getModule();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static KotlinCodeAnalyzer resolveLazilyWithSession(
|
||||
@NotNull List<KtFile> files,
|
||||
@NotNull KotlinCoreEnvironment environment,
|
||||
boolean addBuiltIns
|
||||
) {
|
||||
return LazyResolveTestUtilsKt.createResolveSessionForFiles(environment.getProject(), files, addBuiltIns);
|
||||
}
|
||||
|
||||
public static ModuleDescriptor resolveLazily(List<KtFile> files, KotlinCoreEnvironment environment) {
|
||||
return resolveLazily(files, environment, true);
|
||||
}
|
||||
|
||||
public static ModuleDescriptor resolveLazily(List<KtFile> files, KotlinCoreEnvironment environment, boolean addBuiltIns) {
|
||||
return resolveLazilyWithSession(files, environment, addBuiltIns).getModuleDescriptor();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Set<Name> getTopLevelPackagesFromFileList(@NotNull List<KtFile> files) {
|
||||
Set<Name> shortNames = Sets.newLinkedHashSet();
|
||||
for (KtFile file : files) {
|
||||
List<Name> packageFqNameSegments = file.getPackageFqName().pathSegments();
|
||||
Name name = packageFqNameSegments.isEmpty() ? SpecialNames.ROOT_PACKAGE : packageFqNameSegments.get(0);
|
||||
shortNames.add(name);
|
||||
}
|
||||
return shortNames;
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.lazy
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.analyzer.ModuleContent
|
||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||
import org.jetbrains.kotlin.container.get
|
||||
import org.jetbrains.kotlin.context.ProjectContext
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.CompilerEnvironment
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmAnalyzerFacade
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmPlatformParameters
|
||||
|
||||
fun createResolveSessionForFiles(
|
||||
project: Project,
|
||||
syntheticFiles: Collection<KtFile>,
|
||||
addBuiltIns: Boolean
|
||||
): ResolveSession {
|
||||
val projectContext = ProjectContext(project)
|
||||
val testModule = TestModule(addBuiltIns)
|
||||
val resolverForProject = JvmAnalyzerFacade.setupResolverForProject(
|
||||
"test",
|
||||
projectContext, listOf(testModule),
|
||||
{ ModuleContent(syntheticFiles, GlobalSearchScope.allScope(project)) },
|
||||
JvmPlatformParameters { testModule },
|
||||
CompilerEnvironment
|
||||
)
|
||||
return resolverForProject.resolverForModule(testModule).componentProvider.get<ResolveSession>()
|
||||
}
|
||||
|
||||
private class TestModule(val dependsOnBuiltIns: Boolean) : ModuleInfo {
|
||||
override val name: Name = Name.special("<Test module for lazy resolve>")
|
||||
override fun dependencies() = listOf(this)
|
||||
override fun dependencyOnBuiltIns() =
|
||||
if (dependsOnBuiltIns)
|
||||
ModuleInfo.DependenciesOnBuiltIns.LAST
|
||||
else
|
||||
ModuleInfo.DependenciesOnBuiltIns.NONE
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.scripts
|
||||
|
||||
import com.intellij.psi.PsiFile
|
||||
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtScript
|
||||
import org.jetbrains.kotlin.script.KotlinScriptDefinition
|
||||
import org.jetbrains.kotlin.script.ScriptNameUtil
|
||||
import org.jetbrains.kotlin.script.ScriptParameter
|
||||
|
||||
class TestScriptDefinition(
|
||||
val extension: String,
|
||||
val parameters: List<ScriptParameter>
|
||||
) : KotlinScriptDefinition {
|
||||
override fun getScriptParameters(scriptDescriptor: ScriptDescriptor) = parameters
|
||||
override fun isScript(file: PsiFile): Boolean = file.name.endsWith(extension)
|
||||
override fun getScriptName(script: KtScript): Name = ScriptNameUtil.fileNameWithExtensionStripped(script, extension)
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test
|
||||
|
||||
enum class ConfigurationKind(
|
||||
val withRuntime: Boolean = false,
|
||||
val withMockRuntime: Boolean = false,
|
||||
val withReflection: Boolean = false
|
||||
) {
|
||||
JDK_ONLY(),
|
||||
MOCK_RUNTIME(withMockRuntime = true),
|
||||
NO_KOTLIN_REFLECT(withRuntime = true),
|
||||
ALL(withRuntime = true, withReflection = true),
|
||||
}
|
||||
@@ -1,200 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.junit.Assert;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.*;
|
||||
|
||||
public final class InTextDirectivesUtils {
|
||||
|
||||
private InTextDirectivesUtils() {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Integer getPrefixedInt(String fileText, String prefix) {
|
||||
String[] strings = findArrayWithPrefixes(fileText, prefix);
|
||||
if (strings.length > 0) {
|
||||
assert strings.length == 1;
|
||||
return Integer.parseInt(strings[0]);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Boolean getPrefixedBoolean(String fileText, String prefix) {
|
||||
String[] strings = findArrayWithPrefixes(fileText, prefix);
|
||||
if (strings.length > 0) {
|
||||
assert strings.length == 1;
|
||||
return Boolean.parseBoolean(strings[0]);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String[] findArrayWithPrefixes(@NotNull String fileText, @NotNull String... prefixes) {
|
||||
return ArrayUtil.toStringArray(findListWithPrefixes(fileText, prefixes));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<String> findListWithPrefixes(@NotNull String fileText, @NotNull String... prefixes) {
|
||||
List<String> result = new ArrayList<String>();
|
||||
|
||||
for (String line : findLinesWithPrefixesRemoved(fileText, prefixes)) {
|
||||
String unquoted = StringUtil.unquoteString(line);
|
||||
if (!unquoted.equals(line)) {
|
||||
result.add(unquoted);
|
||||
}
|
||||
else{
|
||||
String[] variants = line.split(",");
|
||||
for (String variant : variants) {
|
||||
result.add(variant.trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static boolean isDirectiveDefined(String fileText, String directive) {
|
||||
return !findListWithPrefixes(fileText, directive).isEmpty();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String findStringWithPrefixes(String fileText, String... prefixes) {
|
||||
List<String> strings = findListWithPrefixes(fileText, prefixes);
|
||||
if (strings.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (strings.size() != 1) {
|
||||
throw new IllegalStateException("There is more than one string with given prefixes " +
|
||||
Arrays.toString(prefixes) + ":\n" +
|
||||
StringUtil.join(strings, "\n") + "\n" +
|
||||
"Use findListWithPrefixes() instead.");
|
||||
}
|
||||
|
||||
return strings.get(0);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<String> findLinesWithPrefixesRemoved(String fileText, String... prefixes) {
|
||||
List<String> result = new ArrayList<String>();
|
||||
List<String> cleanedPrefixes = cleanDirectivesFromComments(Arrays.asList(prefixes));
|
||||
|
||||
for (String line : fileNonEmptyCommentedLines(fileText)) {
|
||||
for (String prefix : cleanedPrefixes) {
|
||||
if (line.startsWith(prefix)) {
|
||||
String noPrefixLine = line.substring(prefix.length());
|
||||
|
||||
if (noPrefixLine.isEmpty() ||
|
||||
Character.isWhitespace(noPrefixLine.charAt(0)) ||
|
||||
Character.isWhitespace(prefix.charAt(prefix.length() - 1))) {
|
||||
result.add(noPrefixLine.trim());
|
||||
break;
|
||||
} else {
|
||||
throw new AssertionError(
|
||||
"Line starts with prefix \"" + prefix + "\", but doesn't have space symbol after it: " + line);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void assertHasUnknownPrefixes(String fileText, Collection<String> knownPrefixes) {
|
||||
Set<String> prefixes = Sets.newHashSet();
|
||||
|
||||
for (String line : fileNonEmptyCommentedLines(fileText)) {
|
||||
String prefix = probableDirective(line);
|
||||
if (prefix != null) {
|
||||
prefixes.add(prefix);
|
||||
}
|
||||
}
|
||||
|
||||
prefixes.removeAll(cleanDirectivesFromComments(knownPrefixes));
|
||||
|
||||
Assert.assertTrue("File contains some unexpected directives" + prefixes, prefixes.isEmpty());
|
||||
}
|
||||
|
||||
private static String probableDirective(String line) {
|
||||
String[] arr = line.split(" ", 2);
|
||||
String firstWord = arr[0];
|
||||
|
||||
if (firstWord.length() > 1 && StringUtil.toUpperCase(firstWord).equals(firstWord)) {
|
||||
return firstWord;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static List<String> cleanDirectivesFromComments(Collection<String> prefixes) {
|
||||
List<String> resultPrefixes = Lists.newArrayList();
|
||||
|
||||
for (String prefix : prefixes) {
|
||||
if (prefix.startsWith("//") || prefix.startsWith("##")) {
|
||||
resultPrefixes.add(StringUtil.trimLeading(prefix.substring(2)));
|
||||
}
|
||||
else {
|
||||
resultPrefixes.add(prefix);
|
||||
}
|
||||
}
|
||||
|
||||
return resultPrefixes;
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
private static List<String> fileNonEmptyCommentedLines(String fileText) {
|
||||
List<String> result = new ArrayList<String>();
|
||||
|
||||
try {
|
||||
BufferedReader reader = new BufferedReader(new StringReader(fileText));
|
||||
try {
|
||||
String line;
|
||||
|
||||
while ((line = reader.readLine()) != null) {
|
||||
line = line.trim();
|
||||
if (line.startsWith("//") || line.startsWith("##")) {
|
||||
String uncommentedLine = line.substring(2).trim();
|
||||
if (!uncommentedLine.isEmpty()) {
|
||||
result.add(uncommentedLine);
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
reader.close();
|
||||
}
|
||||
} catch(IOException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,185 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test;
|
||||
|
||||
import junit.framework.Test;
|
||||
import junit.framework.TestCase;
|
||||
import junit.framework.TestResult;
|
||||
import junit.framework.TestSuite;
|
||||
import org.junit.internal.MethodSorter;
|
||||
import org.junit.internal.runners.JUnit38ClassRunner;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runner.Runner;
|
||||
import org.junit.runner.manipulation.*;
|
||||
import org.junit.runner.notification.RunNotifier;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* This runner runs class with all inners test classes, but monitors situation when those classes are planned to be executed
|
||||
* with IDEA package test runner.
|
||||
*/
|
||||
public class JUnit3RunnerWithInners extends Runner implements Filterable, Sortable {
|
||||
private static final Set<Class> requestedRunners = new HashSet<Class>();
|
||||
|
||||
private JUnit38ClassRunner delegateRunner;
|
||||
private final Class<?> klass;
|
||||
private boolean isFakeTest = false;
|
||||
|
||||
private static class FakeEmptyClassTest implements Test {
|
||||
private final Class<?> klass;
|
||||
|
||||
public FakeEmptyClassTest(Class<?> klass) {
|
||||
this.klass = klass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int countTestCases() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(TestResult result) {
|
||||
result.startTest(this);
|
||||
result.endTest(this);
|
||||
}
|
||||
|
||||
public Class<?> getTestClass() {
|
||||
return klass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Empty class with inners";
|
||||
}
|
||||
}
|
||||
|
||||
public JUnit3RunnerWithInners(Class<?> klass) {
|
||||
this.klass = klass;
|
||||
requestedRunners.add(klass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(RunNotifier notifier) {
|
||||
initialize();
|
||||
delegateRunner.run(notifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Description getDescription() {
|
||||
initialize();
|
||||
return isFakeTest ? Description.EMPTY : delegateRunner.getDescription();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void filter(Filter filter) throws NoTestsRemainException {
|
||||
delegateRunner = new JUnit38ClassRunner(klass);
|
||||
delegateRunner.filter(filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sort(Sorter sorter) {
|
||||
initialize();
|
||||
delegateRunner.sort(sorter);
|
||||
}
|
||||
|
||||
protected void initialize() {
|
||||
if (delegateRunner != null) return;
|
||||
delegateRunner = new JUnit38ClassRunner(getCollectedTests());
|
||||
}
|
||||
|
||||
protected Test getCollectedTests() {
|
||||
List<Class> innerClasses = collectDeclaredClasses(klass, false);
|
||||
Set<Class> unprocessedInnerClasses = unprocessedClasses(innerClasses);
|
||||
|
||||
if (unprocessedInnerClasses.isEmpty()) {
|
||||
if (!innerClasses.isEmpty() && !hasTestMethods(klass)) {
|
||||
isFakeTest = true;
|
||||
return new FakeEmptyClassTest(klass);
|
||||
}
|
||||
else {
|
||||
return new TestSuite(klass.asSubclass(TestCase.class));
|
||||
}
|
||||
}
|
||||
else if (unprocessedInnerClasses.size() == innerClasses.size()) {
|
||||
return createTreeTestSuite(klass);
|
||||
}
|
||||
else {
|
||||
return new TestSuite(klass.asSubclass(TestCase.class));
|
||||
}
|
||||
}
|
||||
|
||||
private static Test createTreeTestSuite(Class root) {
|
||||
Set<Class> classes = new LinkedHashSet<Class>(collectDeclaredClasses(root, true));
|
||||
Map<Class, TestSuite> classSuites = new HashMap<Class, TestSuite>();
|
||||
|
||||
for (Class aClass : classes) {
|
||||
classSuites.put(aClass, hasTestMethods(aClass) ? new TestSuite(aClass) : new TestSuite(aClass.getCanonicalName()));
|
||||
}
|
||||
|
||||
for (Class aClass : classes) {
|
||||
if (aClass.getEnclosingClass() != null && classes.contains(aClass.getEnclosingClass())) {
|
||||
classSuites.get(aClass.getEnclosingClass()).addTest(classSuites.get(aClass));
|
||||
}
|
||||
}
|
||||
|
||||
return classSuites.get(root);
|
||||
}
|
||||
|
||||
private static Set<Class> unprocessedClasses(Collection<Class> classes) {
|
||||
Set<Class> result = new LinkedHashSet<Class>();
|
||||
for (Class aClass : classes) {
|
||||
if (!requestedRunners.contains(aClass)) {
|
||||
result.add(aClass);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static List<Class> collectDeclaredClasses(Class klass, boolean withItself) {
|
||||
List<Class> result = new ArrayList<Class>();
|
||||
if (withItself) {
|
||||
result.add(klass);
|
||||
}
|
||||
|
||||
for (Class aClass : klass.getDeclaredClasses()) {
|
||||
result.addAll(collectDeclaredClasses(aClass, true));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static boolean hasTestMethods(Class klass) {
|
||||
for (Class currentClass = klass; Test.class.isAssignableFrom(currentClass); currentClass = currentClass.getSuperclass()) {
|
||||
for (Method each : MethodSorter.getDeclaredMethods(currentClass)) {
|
||||
if (isTestMethod(each)) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isTestMethod(Method method) {
|
||||
return method.getParameterTypes().length == 0 &&
|
||||
method.getName().startsWith("test") &&
|
||||
method.getReturnType().equals(Void.TYPE) &&
|
||||
Modifier.isPublic(method.getModifiers());
|
||||
}
|
||||
}
|
||||
@@ -1,969 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.ShutDownTracker;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.CharsetToolkit;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFileFactory;
|
||||
import com.intellij.psi.impl.PsiFileFactoryImpl;
|
||||
import com.intellij.rt.execution.junit.FileComparisonFailure;
|
||||
import com.intellij.testFramework.LightVirtualFile;
|
||||
import com.intellij.testFramework.TestDataFile;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import junit.framework.TestCase;
|
||||
import kotlin.collections.CollectionsKt;
|
||||
import kotlin.collections.SetsKt;
|
||||
import kotlin.jvm.functions.Function1;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
import org.jetbrains.kotlin.analyzer.AnalysisResult;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
|
||||
import org.jetbrains.kotlin.cli.jvm.config.JvmContentRootsKt;
|
||||
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime;
|
||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys;
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration;
|
||||
import org.jetbrains.kotlin.config.ContentRoot;
|
||||
import org.jetbrains.kotlin.config.KotlinSourceRoot;
|
||||
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl;
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic;
|
||||
import org.jetbrains.kotlin.diagnostics.Errors;
|
||||
import org.jetbrains.kotlin.diagnostics.Severity;
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages;
|
||||
import org.jetbrains.kotlin.idea.KotlinLanguage;
|
||||
import org.jetbrains.kotlin.lexer.KtTokens;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.psi.KtExpression;
|
||||
import org.jetbrains.kotlin.psi.KtFile;
|
||||
import org.jetbrains.kotlin.psi.KtPsiFactoryKt;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace;
|
||||
import org.jetbrains.kotlin.resolve.TargetPlatform;
|
||||
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics;
|
||||
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform;
|
||||
import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil;
|
||||
import org.jetbrains.kotlin.resolve.lazy.LazyResolveTestUtil;
|
||||
import org.jetbrains.kotlin.storage.LockBasedStorageManager;
|
||||
import org.jetbrains.kotlin.test.util.JetTestUtilsKt;
|
||||
import org.jetbrains.kotlin.types.KotlinType;
|
||||
import org.jetbrains.kotlin.types.expressions.KotlinTypeInfo;
|
||||
import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice;
|
||||
import org.jetbrains.kotlin.util.slicedMap.SlicedMap;
|
||||
import org.jetbrains.kotlin.util.slicedMap.WritableSlice;
|
||||
import org.jetbrains.kotlin.utils.ExceptionUtilsKt;
|
||||
import org.jetbrains.kotlin.utils.PathUtil;
|
||||
import org.junit.Assert;
|
||||
|
||||
import javax.tools.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.jetbrains.kotlin.cli.jvm.config.JVMConfigurationKeys.MODULE_NAME;
|
||||
import static org.jetbrains.kotlin.jvm.compiler.LoadDescriptorUtil.compileKotlinToDirAndGetAnalysisResult;
|
||||
import static org.jetbrains.kotlin.test.ConfigurationKind.ALL;
|
||||
|
||||
public class KotlinTestUtils {
|
||||
public static final String TEST_GENERATOR_NAME = "org.jetbrains.kotlin.generators.tests.TestsPackage";
|
||||
public static final String PLEASE_REGENERATE_TESTS = "Please regenerate tests (GenerateTests.kt)";
|
||||
|
||||
private static final List<File> filesToDelete = new ArrayList<File>();
|
||||
|
||||
/**
|
||||
* Syntax:
|
||||
*
|
||||
* // MODULE: name(dependency1, dependency2, ...)
|
||||
*
|
||||
* // FILE: name
|
||||
*
|
||||
* Several files may follow one module
|
||||
*/
|
||||
public static final Pattern FILE_OR_MODULE_PATTERN = Pattern.compile("(?://\\s*MODULE:\\s*(\\w+)(\\(\\w+(?:, \\w+)*\\))?\\s*)?" +
|
||||
"//\\s*FILE:\\s*(.*)$", Pattern.MULTILINE);
|
||||
public static final Pattern DIRECTIVE_PATTERN = Pattern.compile("^//\\s*!(\\w+)(:\\s*(.*)$)?", Pattern.MULTILINE);
|
||||
|
||||
public static final BindingTrace DUMMY_TRACE = new BindingTrace() {
|
||||
@NotNull
|
||||
@Override
|
||||
public BindingContext getBindingContext() {
|
||||
return new BindingContext() {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Diagnostics getDiagnostics() {
|
||||
return Diagnostics.Companion.getEMPTY();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <K, V> V get(ReadOnlySlice<K, V> slice, K key) {
|
||||
return DUMMY_TRACE.get(slice, key);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public <K, V> Collection<K> getKeys(WritableSlice<K, V> slice) {
|
||||
return DUMMY_TRACE.getKeys(slice);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@TestOnly
|
||||
@Override
|
||||
public <K, V> ImmutableMap<K, V> getSliceContents(@NotNull ReadOnlySlice<K, V> slice) {
|
||||
return ImmutableMap.of();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public KotlinType getType(@NotNull KtExpression expression) {
|
||||
return DUMMY_TRACE.getType(expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addOwnDataTo(@NotNull BindingTrace trace, boolean commitDiagnostics) {
|
||||
// do nothing
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public <K, V> void record(WritableSlice<K, V> slice, K key, V value) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <K> void record(WritableSlice<K, Boolean> slice, K key) {
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <K, V> V get(ReadOnlySlice<K, V> slice, K key) {
|
||||
if (slice == BindingContext.PROCESSED) return (V) Boolean.FALSE;
|
||||
return SlicedMap.DO_NOTHING.get(slice, key);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public <K, V> Collection<K> getKeys(WritableSlice<K, V> slice) {
|
||||
assert slice.isCollective();
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public KotlinType getType(@NotNull KtExpression expression) {
|
||||
KotlinTypeInfo typeInfo = get(BindingContext.EXPRESSION_TYPE_INFO, expression);
|
||||
return typeInfo != null ? typeInfo.getType() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recordType(@NotNull KtExpression expression, @Nullable KotlinType type) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void report(@NotNull Diagnostic diagnostic) {
|
||||
if (Errors.UNRESOLVED_REFERENCE_DIAGNOSTICS.contains(diagnostic.getFactory())) {
|
||||
throw new IllegalStateException("Unresolved: " + diagnostic.getPsiElement().getText());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public static BindingTrace DUMMY_EXCEPTION_ON_ERROR_TRACE = new BindingTrace() {
|
||||
@NotNull
|
||||
@Override
|
||||
public BindingContext getBindingContext() {
|
||||
return new BindingContext() {
|
||||
@NotNull
|
||||
@Override
|
||||
public Diagnostics getDiagnostics() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <K, V> V get(ReadOnlySlice<K, V> slice, K key) {
|
||||
return DUMMY_EXCEPTION_ON_ERROR_TRACE.get(slice, key);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public <K, V> Collection<K> getKeys(WritableSlice<K, V> slice) {
|
||||
return DUMMY_EXCEPTION_ON_ERROR_TRACE.getKeys(slice);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@TestOnly
|
||||
@Override
|
||||
public <K, V> ImmutableMap<K, V> getSliceContents(@NotNull ReadOnlySlice<K, V> slice) {
|
||||
return ImmutableMap.of();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public KotlinType getType(@NotNull KtExpression expression) {
|
||||
return DUMMY_EXCEPTION_ON_ERROR_TRACE.getType(expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addOwnDataTo(@NotNull BindingTrace trace, boolean commitDiagnostics) {
|
||||
// do nothing
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public <K, V> void record(WritableSlice<K, V> slice, K key, V value) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <K> void record(WritableSlice<K, Boolean> slice, K key) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <K, V> V get(ReadOnlySlice<K, V> slice, K key) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public <K, V> Collection<K> getKeys(WritableSlice<K, V> slice) {
|
||||
assert slice.isCollective();
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public KotlinType getType(@NotNull KtExpression expression) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recordType(@NotNull KtExpression expression, @Nullable KotlinType type) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void report(@NotNull Diagnostic diagnostic) {
|
||||
if (diagnostic.getSeverity() == Severity.ERROR) {
|
||||
throw new IllegalStateException(DefaultErrorMessages.render(diagnostic));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// We suspect sequences of eight consecutive hexadecimal digits to be a package part hash code
|
||||
private static final Pattern STRIP_PACKAGE_PART_HASH_PATTERN = Pattern.compile("\\$([0-9a-f]{8})");
|
||||
|
||||
private KotlinTestUtils() {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static AnalysisResult analyzeFile(@NotNull KtFile file, @NotNull KotlinCoreEnvironment environment) {
|
||||
return JvmResolveUtil.analyzeOneFileWithJavaIntegration(file, environment);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static KotlinCoreEnvironment createEnvironmentWithMockJdkAndIdeaAnnotations(Disposable disposable) {
|
||||
return createEnvironmentWithMockJdkAndIdeaAnnotations(disposable, ConfigurationKind.ALL);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static KotlinCoreEnvironment createEnvironmentWithMockJdkAndIdeaAnnotations(Disposable disposable, @NotNull ConfigurationKind configurationKind) {
|
||||
return createEnvironmentWithJdkAndNullabilityAnnotationsFromIdea(disposable, configurationKind, TestJdkKind.MOCK_JDK);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static KotlinCoreEnvironment createEnvironmentWithJdkAndNullabilityAnnotationsFromIdea(
|
||||
@NotNull Disposable disposable,
|
||||
@NotNull ConfigurationKind configurationKind,
|
||||
@NotNull TestJdkKind jdkKind
|
||||
) {
|
||||
return KotlinCoreEnvironment.createForTests(
|
||||
disposable,
|
||||
compilerConfigurationForTests(configurationKind, jdkKind, getAnnotationsJar()),
|
||||
EnvironmentConfigFiles.JVM_CONFIG_FILES);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String getTestDataPathBase() {
|
||||
return getHomeDirectory() + "/compiler/testData";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String getHomeDirectory() {
|
||||
File resourceRoot = PathUtil.getResourcePathForClass(KotlinTestUtils.class);
|
||||
return FileUtil.toSystemIndependentName(resourceRoot.getParentFile().getParentFile().getParent());
|
||||
}
|
||||
|
||||
public static File findMockJdkRtJar() {
|
||||
return new File(getHomeDirectory(), "compiler/testData/mockJDK/jre/lib/rt.jar");
|
||||
}
|
||||
|
||||
public static File findAndroidApiJar() {
|
||||
return new File(getHomeDirectory(), "dependencies/android.jar");
|
||||
}
|
||||
|
||||
public static File getAnnotationsJar() {
|
||||
return new File(getHomeDirectory(), "compiler/testData/mockJDK/jre/lib/annotations.jar");
|
||||
}
|
||||
|
||||
public static void mkdirs(@NotNull File file) {
|
||||
if (file.isDirectory()) {
|
||||
return;
|
||||
}
|
||||
if (!file.mkdirs()) {
|
||||
if (file.exists()) {
|
||||
throw new IllegalStateException("Failed to create " + file + ": file exists and not a directory");
|
||||
}
|
||||
throw new IllegalStateException("Failed to create " + file);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static File tmpDirForTest(TestCase test) throws IOException {
|
||||
File answer = FileUtil.createTempDirectory(test.getClass().getSimpleName(), test.getName());
|
||||
deleteOnShutdown(answer);
|
||||
return answer;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static File tmpDir(String name) throws IOException {
|
||||
// we should use this form. otherwise directory will be deleted on each test
|
||||
File answer = FileUtil.createTempDirectory(new File(System.getProperty("java.io.tmpdir")), name, "");
|
||||
deleteOnShutdown(answer);
|
||||
return answer;
|
||||
}
|
||||
|
||||
public static void deleteOnShutdown(File file) {
|
||||
if (filesToDelete.isEmpty()) {
|
||||
ShutDownTracker.getInstance().registerShutdownTask(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ShutDownTracker.invokeAndWait(true, true, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (File victim : filesToDelete) {
|
||||
FileUtil.delete(victim);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
filesToDelete.add(file);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static KtFile createFile(@NotNull @NonNls final String name, @NotNull String text, @NotNull Project project) {
|
||||
String shortName = name.substring(name.lastIndexOf('/') + 1);
|
||||
shortName = shortName.substring(shortName.lastIndexOf('\\') + 1);
|
||||
LightVirtualFile virtualFile = new LightVirtualFile(shortName, KotlinLanguage.INSTANCE, text) {
|
||||
@NotNull
|
||||
@Override
|
||||
public String getPath() {
|
||||
//TODO: patch LightVirtualFile
|
||||
return "/" + name;
|
||||
}
|
||||
};
|
||||
|
||||
virtualFile.setCharset(CharsetToolkit.UTF8_CHARSET);
|
||||
PsiFileFactoryImpl factory = (PsiFileFactoryImpl) PsiFileFactory.getInstance(project);
|
||||
//noinspection ConstantConditions
|
||||
return (KtFile) factory.trySetupPsiForFile(virtualFile, KotlinLanguage.INSTANCE, true, false);
|
||||
}
|
||||
|
||||
public static String doLoadFile(String myFullDataPath, String name) throws IOException {
|
||||
String fullName = myFullDataPath + File.separatorChar + name;
|
||||
return doLoadFile(new File(fullName));
|
||||
}
|
||||
|
||||
public static String doLoadFile(@NotNull File file) throws IOException {
|
||||
return FileUtil.loadFile(file, CharsetToolkit.UTF8, true);
|
||||
}
|
||||
|
||||
public static String getFilePath(File file) {
|
||||
return FileUtil.toSystemIndependentName(file.getPath());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static CompilerConfiguration compilerConfigurationForTests(
|
||||
@NotNull ConfigurationKind configurationKind,
|
||||
@NotNull TestJdkKind jdkKind,
|
||||
@NotNull File... extraClasspath
|
||||
) {
|
||||
return compilerConfigurationForTests(configurationKind, jdkKind, Arrays.asList(extraClasspath), Collections.<File>emptyList());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static CompilerConfiguration compilerConfigurationForTests(
|
||||
@NotNull ConfigurationKind configurationKind,
|
||||
@NotNull TestJdkKind jdkKind,
|
||||
@NotNull List<File> classpath,
|
||||
@NotNull List<File> javaSource
|
||||
) {
|
||||
CompilerConfiguration configuration = new CompilerConfiguration();
|
||||
JvmContentRootsKt.addJavaSourceRoots(configuration, javaSource);
|
||||
if (jdkKind == TestJdkKind.MOCK_JDK) {
|
||||
JvmContentRootsKt.addJvmClasspathRoot(configuration, findMockJdkRtJar());
|
||||
}
|
||||
else if (jdkKind == TestJdkKind.ANDROID_API) {
|
||||
JvmContentRootsKt.addJvmClasspathRoot(configuration, findAndroidApiJar());
|
||||
}
|
||||
else {
|
||||
JvmContentRootsKt.addJvmClasspathRoots(configuration, PathUtil.getJdkClassesRoots());
|
||||
}
|
||||
|
||||
if (configurationKind.getWithRuntime()) {
|
||||
JvmContentRootsKt.addJvmClasspathRoot(configuration, ForTestCompileRuntime.runtimeJarForTests());
|
||||
JvmContentRootsKt.addJvmClasspathRoot(configuration, ForTestCompileRuntime.kotlinTestJarForTests());
|
||||
}
|
||||
else if (configurationKind.getWithMockRuntime()) {
|
||||
JvmContentRootsKt.addJvmClasspathRoot(configuration, ForTestCompileRuntime.mockRuntimeJarForTests());
|
||||
}
|
||||
if (configurationKind.getWithReflection()) {
|
||||
JvmContentRootsKt.addJvmClasspathRoot(configuration, ForTestCompileRuntime.reflectJarForTests());
|
||||
}
|
||||
|
||||
JvmContentRootsKt.addJvmClasspathRoots(configuration, classpath);
|
||||
|
||||
configuration.put(MODULE_NAME, "compilerConfigurationForTests");
|
||||
|
||||
return configuration;
|
||||
}
|
||||
|
||||
public static void resolveAllKotlinFiles(KotlinCoreEnvironment environment) throws IOException {
|
||||
List<ContentRoot> paths = environment.getConfiguration().get(CommonConfigurationKeys.CONTENT_ROOTS);
|
||||
if (paths == null) return;
|
||||
List<KtFile> jetFiles = Lists.newArrayList();
|
||||
for (ContentRoot root : paths) {
|
||||
if (!(root instanceof KotlinSourceRoot)) continue;
|
||||
|
||||
String path = ((KotlinSourceRoot) root).getPath();
|
||||
File file = new File(path);
|
||||
if (file.isFile()) {
|
||||
jetFiles.add(loadJetFile(environment.getProject(), file));
|
||||
}
|
||||
else {
|
||||
//noinspection ConstantConditions
|
||||
for (File childFile : file.listFiles()) {
|
||||
if (childFile.getName().endsWith(".kt")) {
|
||||
jetFiles.add(loadJetFile(environment.getProject(), childFile));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
LazyResolveTestUtil.resolve(environment.getProject(), jetFiles, environment);
|
||||
}
|
||||
|
||||
public static void assertEqualsToFile(@NotNull File expectedFile, @NotNull Editor editor) {
|
||||
String actualText = editor.getDocument().getText();
|
||||
String afterText = new StringBuilder(actualText).insert(editor.getCaretModel().getOffset(), "<caret>").toString();
|
||||
|
||||
assertEqualsToFile(expectedFile, afterText);
|
||||
}
|
||||
|
||||
public static void assertEqualsToFile(@NotNull File expectedFile, @NotNull String actual) {
|
||||
assertEqualsToFile(expectedFile, actual, new Function1<String, String>() {
|
||||
@Override
|
||||
public String invoke(String s) {
|
||||
return s;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void assertEqualsToFile(@NotNull File expectedFile, @NotNull String actual, @NotNull Function1<String, String> sanitizer) {
|
||||
try {
|
||||
String actualText = JetTestUtilsKt.trimTrailingWhitespacesAndAddNewlineAtEOF(StringUtil.convertLineSeparators(actual.trim()));
|
||||
|
||||
if (!expectedFile.exists()) {
|
||||
FileUtil.writeToFile(expectedFile, actualText);
|
||||
Assert.fail("Expected data file did not exist. Generating: " + expectedFile);
|
||||
}
|
||||
String expected = FileUtil.loadFile(expectedFile, CharsetToolkit.UTF8, true);
|
||||
|
||||
String expectedText = JetTestUtilsKt.trimTrailingWhitespacesAndAddNewlineAtEOF(StringUtil.convertLineSeparators(expected.trim()));
|
||||
|
||||
if (!Comparing.equal(sanitizer.invoke(expectedText), sanitizer.invoke(actualText))) {
|
||||
throw new FileComparisonFailure("Actual data differs from file content: " + expectedFile.getName(),
|
||||
expected, actual, expectedFile.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw ExceptionUtilsKt.rethrow(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void compileKotlinWithJava(
|
||||
@NotNull List<File> javaFiles,
|
||||
@NotNull List<File> ktFiles,
|
||||
@NotNull File outDir,
|
||||
@NotNull Disposable disposable,
|
||||
@Nullable File javaErrorFile
|
||||
) throws IOException {
|
||||
if (!ktFiles.isEmpty()) {
|
||||
compileKotlinToDirAndGetAnalysisResult(ktFiles, outDir, disposable, ALL, false);
|
||||
}
|
||||
else {
|
||||
boolean mkdirs = outDir.mkdirs();
|
||||
assert mkdirs : "Not created: " + outDir;
|
||||
}
|
||||
if (!javaFiles.isEmpty()) {
|
||||
compileJavaFiles(javaFiles, Arrays.asList(
|
||||
"-classpath", outDir.getPath() + File.pathSeparator + ForTestCompileRuntime.runtimeJarForTests(),
|
||||
"-d", outDir.getPath()
|
||||
), javaErrorFile);
|
||||
}
|
||||
}
|
||||
|
||||
public interface TestFileFactory<M, F> {
|
||||
F createFile(@Nullable M module, @NotNull String fileName, @NotNull String text, @NotNull Map<String, String> directives);
|
||||
M createModule(@NotNull String name, @NotNull List<String> dependencies);
|
||||
}
|
||||
|
||||
public static abstract class TestFileFactoryNoModules<F> implements TestFileFactory<Void, F> {
|
||||
@Override
|
||||
public final F createFile(
|
||||
@Nullable Void module,
|
||||
@NotNull String fileName,
|
||||
@NotNull String text,
|
||||
@NotNull Map<String, String> directives
|
||||
) {
|
||||
return create(fileName, text, directives);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public abstract F create(@NotNull String fileName, @NotNull String text, @NotNull Map<String, String> directives);
|
||||
|
||||
@Override
|
||||
public Void createModule(@NotNull String name, @NotNull List<String> dependencies) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static <M, F> List<F> createTestFiles(String testFileName, String expectedText, TestFileFactory<M, F> factory) {
|
||||
Map<String, String> directives = parseDirectives(expectedText);
|
||||
|
||||
List<F> testFiles = Lists.newArrayList();
|
||||
Matcher matcher = FILE_OR_MODULE_PATTERN.matcher(expectedText);
|
||||
if (!matcher.find()) {
|
||||
// One file
|
||||
testFiles.add(factory.createFile(null, testFileName, expectedText, directives));
|
||||
}
|
||||
else {
|
||||
int processedChars = 0;
|
||||
M module = null;
|
||||
// Many files
|
||||
while (true) {
|
||||
String moduleName = matcher.group(1);
|
||||
String moduleDependencies = matcher.group(2);
|
||||
if (moduleName != null) {
|
||||
module = factory.createModule(moduleName, parseDependencies(moduleDependencies));
|
||||
}
|
||||
|
||||
String fileName = matcher.group(3);
|
||||
int start = processedChars;
|
||||
|
||||
boolean nextFileExists = matcher.find();
|
||||
int end;
|
||||
if (nextFileExists) {
|
||||
end = matcher.start();
|
||||
}
|
||||
else {
|
||||
end = expectedText.length();
|
||||
}
|
||||
String fileText = expectedText.substring(start, end);
|
||||
processedChars = end;
|
||||
|
||||
testFiles.add(factory.createFile(module, fileName, fileText, directives));
|
||||
|
||||
if (!nextFileExists) break;
|
||||
}
|
||||
assert processedChars == expectedText.length() : "Characters skipped from " +
|
||||
processedChars +
|
||||
" to " +
|
||||
(expectedText.length() - 1);
|
||||
}
|
||||
return testFiles;
|
||||
}
|
||||
|
||||
private static List<String> parseDependencies(@Nullable String dependencies) {
|
||||
if (dependencies == null) return Collections.emptyList();
|
||||
|
||||
Matcher matcher = Pattern.compile("\\w+").matcher(dependencies);
|
||||
List<String> result = new ArrayList<String>();
|
||||
while (matcher.find()) {
|
||||
result.add(matcher.group());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Map<String, String> parseDirectives(String expectedText) {
|
||||
Map<String, String> directives = Maps.newHashMap();
|
||||
Matcher directiveMatcher = DIRECTIVE_PATTERN.matcher(expectedText);
|
||||
int start = 0;
|
||||
while (directiveMatcher.find()) {
|
||||
if (directiveMatcher.start() != start) {
|
||||
Assert.fail("Directives should only occur at the beginning of a file: " + directiveMatcher.group());
|
||||
}
|
||||
String name = directiveMatcher.group(1);
|
||||
String value = directiveMatcher.group(3);
|
||||
String oldValue = directives.put(name, value);
|
||||
Assert.assertNull("Directive overwritten: " + name + " old value: " + oldValue + " new value: " + value, oldValue);
|
||||
start = directiveMatcher.end() + 1;
|
||||
}
|
||||
return directives;
|
||||
}
|
||||
|
||||
public static List<String> loadBeforeAfterText(String filePath) {
|
||||
String content;
|
||||
|
||||
try {
|
||||
content = FileUtil.loadFile(new File(filePath), true);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
List<String> files = createTestFiles("", content, new TestFileFactoryNoModules<String>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public String create(@NotNull String fileName, @NotNull String text, @NotNull Map<String, String> directives) {
|
||||
int firstLineEnd = text.indexOf('\n');
|
||||
return StringUtil.trimTrailing(text.substring(firstLineEnd + 1));
|
||||
}
|
||||
});
|
||||
|
||||
Assert.assertTrue("Exactly two files expected: ", files.size() == 2);
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
public static String getLastCommentedLines(@NotNull Document document) {
|
||||
List<CharSequence> resultLines = new ArrayList<CharSequence>();
|
||||
for (int i = document.getLineCount() - 1; i >= 0; i--) {
|
||||
int lineStart = document.getLineStartOffset(i);
|
||||
int lineEnd = document.getLineEndOffset(i);
|
||||
if (document.getCharsSequence().subSequence(lineStart, lineEnd).toString().trim().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ("//".equals(document.getCharsSequence().subSequence(lineStart, lineStart + 2).toString())) {
|
||||
resultLines.add(document.getCharsSequence().subSequence(lineStart + 2, lineEnd));
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Collections.reverse(resultLines);
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (CharSequence line : resultLines) {
|
||||
result.append(line).append("\n");
|
||||
}
|
||||
result.delete(result.length() - 1, result.length());
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
public enum CommentType {
|
||||
ALL,
|
||||
LINE_COMMENT,
|
||||
BLOCK_COMMENT
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String getLastCommentInFile(@NotNull KtFile file) {
|
||||
return CollectionsKt.first(getLastCommentsInFile(file, CommentType.ALL, true));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<String> getLastCommentsInFile(@NotNull KtFile file, CommentType commentType, boolean assertMustExist) {
|
||||
PsiElement lastChild = file.getLastChild();
|
||||
if (lastChild != null && lastChild.getNode().getElementType().equals(KtTokens.WHITE_SPACE)) {
|
||||
lastChild = lastChild.getPrevSibling();
|
||||
}
|
||||
assert lastChild != null;
|
||||
|
||||
List<String> comments = ContainerUtil.newArrayList();
|
||||
|
||||
while (true) {
|
||||
if (lastChild.getNode().getElementType().equals(KtTokens.BLOCK_COMMENT)) {
|
||||
if (commentType == CommentType.ALL || commentType == CommentType.BLOCK_COMMENT) {
|
||||
String lastChildText = lastChild.getText();
|
||||
comments.add(lastChildText.substring(2, lastChildText.length() - 2).trim());
|
||||
}
|
||||
}
|
||||
else if (lastChild.getNode().getElementType().equals(KtTokens.EOL_COMMENT)) {
|
||||
if (commentType == CommentType.ALL || commentType == CommentType.LINE_COMMENT) {
|
||||
comments.add(lastChild.getText().substring(2).trim());
|
||||
}
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
|
||||
lastChild = lastChild.getPrevSibling();
|
||||
}
|
||||
|
||||
if (comments.isEmpty() && assertMustExist) {
|
||||
throw new AssertionError(String.format(
|
||||
"Test file '%s' should end in a comment of type %s; last node was: %s", file.getName(), commentType, lastChild));
|
||||
}
|
||||
|
||||
return comments;
|
||||
}
|
||||
|
||||
public static void compileJavaFiles(@NotNull Collection<File> files, List<String> options) throws IOException {
|
||||
compileJavaFiles(files, options, null);
|
||||
}
|
||||
|
||||
public static void compileJavaFiles(@NotNull Collection<File> files, List<String> options, @Nullable File javaErrorFile) throws IOException {
|
||||
JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
|
||||
DiagnosticCollector<JavaFileObject> diagnosticCollector = new DiagnosticCollector<JavaFileObject>();
|
||||
StandardJavaFileManager fileManager = javaCompiler.getStandardFileManager(diagnosticCollector, Locale.ENGLISH, Charset.forName("utf-8"));
|
||||
try {
|
||||
Iterable<? extends JavaFileObject> javaFileObjectsFromFiles = fileManager.getJavaFileObjectsFromFiles(files);
|
||||
|
||||
JavaCompiler.CompilationTask task = javaCompiler.getTask(
|
||||
new StringWriter(), // do not write to System.err
|
||||
fileManager,
|
||||
diagnosticCollector,
|
||||
options,
|
||||
null,
|
||||
javaFileObjectsFromFiles);
|
||||
|
||||
Boolean success = task.call(); // do NOT inline this variable, call() should complete before errorsToString()
|
||||
if (javaErrorFile == null || !javaErrorFile.exists()) {
|
||||
Assert.assertTrue(errorsToString(diagnosticCollector, true), success);
|
||||
}
|
||||
else {
|
||||
assertEqualsToFile(javaErrorFile, errorsToString(diagnosticCollector, false));
|
||||
}
|
||||
} finally {
|
||||
fileManager.close();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String errorsToString(@NotNull DiagnosticCollector<JavaFileObject> diagnosticCollector, boolean humanReadable) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (javax.tools.Diagnostic<? extends JavaFileObject> diagnostic : diagnosticCollector.getDiagnostics()) {
|
||||
if (diagnostic.getKind() != javax.tools.Diagnostic.Kind.ERROR) continue;
|
||||
|
||||
if (humanReadable) {
|
||||
builder.append(diagnostic).append("\n");
|
||||
}
|
||||
else {
|
||||
builder.append(diagnostic.getSource().getName()).append(":")
|
||||
.append(diagnostic.getLineNumber()).append(":")
|
||||
.append(diagnostic.getColumnNumber()).append(":")
|
||||
.append(diagnostic.getCode()).append("\n");
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public static String navigationMetadata(@TestDataFile String testFile) {
|
||||
return testFile;
|
||||
}
|
||||
|
||||
public static String getTestsRoot(@NotNull Class<?> testCaseClass) {
|
||||
TestMetadata testClassMetadata = testCaseClass.getAnnotation(TestMetadata.class);
|
||||
Assert.assertNotNull("No metadata for class: " + testCaseClass, testClassMetadata);
|
||||
return testClassMetadata.value();
|
||||
}
|
||||
|
||||
public static void assertAllTestsPresentByMetadata(
|
||||
@NotNull Class<?> testCaseClass,
|
||||
@NotNull File testDataDir,
|
||||
@NotNull Pattern filenamePattern,
|
||||
boolean recursive,
|
||||
@NotNull String... excludeDirs
|
||||
) {
|
||||
File rootFile = new File(getTestsRoot(testCaseClass));
|
||||
|
||||
Set<String> filePaths = collectPathsMetadata(testCaseClass);
|
||||
Set<String> exclude = SetsKt.setOf(excludeDirs);
|
||||
|
||||
File[] files = testDataDir.listFiles();
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
if (file.isDirectory()) {
|
||||
if (recursive && containsTestData(file, filenamePattern) && !exclude.contains(file.getName())) {
|
||||
assertTestClassPresentByMetadata(testCaseClass, file);
|
||||
}
|
||||
}
|
||||
else if (filenamePattern.matcher(file.getName()).matches()) {
|
||||
assertFilePathPresent(file, rootFile, filePaths);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertAllTestsPresentInSingleGeneratedClass(
|
||||
@NotNull Class<?> testCaseClass,
|
||||
@NotNull File testDataDir,
|
||||
@NotNull final Pattern filenamePattern
|
||||
) {
|
||||
final File rootFile = new File(getTestsRoot(testCaseClass));
|
||||
|
||||
final Set<String> filePaths = collectPathsMetadata(testCaseClass);
|
||||
|
||||
FileUtil.processFilesRecursively(testDataDir, new Processor<File>() {
|
||||
@Override
|
||||
public boolean process(File file) {
|
||||
if (file.isFile() && filenamePattern.matcher(file.getName()).matches()) {
|
||||
assertFilePathPresent(file, rootFile, filePaths);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void assertFilePathPresent(File file, File rootFile, Set<String> filePaths) {
|
||||
String path = FileUtil.getRelativePath(rootFile, file);
|
||||
if (path != null) {
|
||||
String relativePath = FileUtil.nameToCompare(path);
|
||||
if (!filePaths.contains(relativePath)) {
|
||||
Assert.fail("Test data file missing from the generated test class: " + file + "\n" + PLEASE_REGENERATE_TESTS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Set<String> collectPathsMetadata(Class<?> testCaseClass) {
|
||||
return ContainerUtil.newHashSet(
|
||||
ContainerUtil.map(collectMethodsMetadata(testCaseClass), new Function<String, String>() {
|
||||
@Override
|
||||
public String fun(String pathData) {
|
||||
return FileUtil.nameToCompare(pathData);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private static Set<String> collectMethodsMetadata(Class<?> testCaseClass) {
|
||||
Set<String> filePaths = Sets.newHashSet();
|
||||
for (Method method : testCaseClass.getDeclaredMethods()) {
|
||||
TestMetadata testMetadata = method.getAnnotation(TestMetadata.class);
|
||||
if (testMetadata != null) {
|
||||
filePaths.add(testMetadata.value());
|
||||
}
|
||||
}
|
||||
return filePaths;
|
||||
}
|
||||
|
||||
private static boolean containsTestData(File dir, Pattern filenamePattern) {
|
||||
File[] files = dir.listFiles();
|
||||
assert files != null;
|
||||
for (File file : files) {
|
||||
if (file.isDirectory()) {
|
||||
if (containsTestData(file, filenamePattern)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (filenamePattern.matcher(file.getName()).matches()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void assertTestClassPresentByMetadata(@NotNull Class<?> outerClass, @NotNull File testDataDir) {
|
||||
for (Class<?> nestedClass : outerClass.getDeclaredClasses()) {
|
||||
TestMetadata testMetadata = nestedClass.getAnnotation(TestMetadata.class);
|
||||
if (testMetadata != null && testMetadata.value().equals(getFilePath(testDataDir))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
Assert.fail("Test data directory missing from the generated test class: " + testDataDir + "\n" + PLEASE_REGENERATE_TESTS);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static KtFile loadJetFile(@NotNull Project project, @NotNull File ioFile) throws IOException {
|
||||
String text = FileUtil.loadFile(ioFile, true);
|
||||
return KtPsiFactoryKt.KtPsiFactory(project).createPhysicalFile(ioFile.getName(), text);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<KtFile> loadToJetFiles(@NotNull KotlinCoreEnvironment environment, @NotNull List<File> files) throws IOException {
|
||||
List<KtFile> jetFiles = Lists.newArrayList();
|
||||
for (File file : files) {
|
||||
jetFiles.add(loadJetFile(environment.getProject(), file));
|
||||
}
|
||||
return jetFiles;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static ModuleDescriptorImpl createEmptyModule() {
|
||||
return createEmptyModule("<empty-for-test>");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static ModuleDescriptorImpl createEmptyModule(@NotNull String name) {
|
||||
return createEmptyModule(name, JvmPlatform.INSTANCE);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static ModuleDescriptorImpl createEmptyModule(@NotNull String name, @NotNull TargetPlatform platform) {
|
||||
return new ModuleDescriptorImpl(
|
||||
Name.special(name), LockBasedStorageManager.NO_LOCKS, platform.getDefaultModuleParameters(), platform.getBuiltIns()
|
||||
);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static File replaceExtension(@NotNull File file, @Nullable String newExtension) {
|
||||
return new File(file.getParentFile(), FileUtil.getNameWithoutExtension(file) + (newExtension == null ? "" : "." + newExtension));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String replaceHashWithStar(@NotNull String string) {
|
||||
return replaceHash(string, "*");
|
||||
}
|
||||
|
||||
public static String replaceHash(@NotNull String string, @NotNull String replacement) {
|
||||
//TODO: hashes are still used in SamWrapperCodegen
|
||||
Matcher matcher = STRIP_PACKAGE_PART_HASH_PATTERN.matcher(string);
|
||||
if (matcher.find()) {
|
||||
return matcher.replaceAll("\\$" + replacement);
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
public static boolean isAllFilesPresentTest(String testName) {
|
||||
//noinspection SpellCheckingInspection
|
||||
return testName.toLowerCase().startsWith("allfilespresentin");
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test;
|
||||
|
||||
import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
|
||||
import org.jetbrains.kotlin.utils.ExceptionUtilsKt;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public abstract class KotlinTestWithEnvironment extends KotlinTestWithEnvironmentManagement {
|
||||
private KotlinCoreEnvironment environment;
|
||||
private Application application;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
application = ApplicationManager.getApplication();
|
||||
|
||||
super.setUp();
|
||||
environment = createEnvironment();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
removeEnvironment();
|
||||
environment = null;
|
||||
super.tearDown();
|
||||
|
||||
if (application == null) {
|
||||
resetApplicationToNull();
|
||||
}
|
||||
|
||||
application = null;
|
||||
}
|
||||
|
||||
protected void resetApplicationToNull() {
|
||||
try {
|
||||
Field ourApplicationField = ApplicationManager.class.getDeclaredField("ourApplication");
|
||||
ourApplicationField.setAccessible(true);
|
||||
ourApplicationField.set(null, null);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw ExceptionUtilsKt.rethrow(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract KotlinCoreEnvironment createEnvironment() throws Exception;
|
||||
protected void removeEnvironment() throws Exception {}
|
||||
|
||||
@NotNull
|
||||
public KotlinCoreEnvironment getEnvironment() {
|
||||
return environment;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Project getProject() {
|
||||
return getEnvironment().getProject();
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
|
||||
import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase;
|
||||
import org.jetbrains.kotlin.types.DelegatingFlexibleType;
|
||||
|
||||
public abstract class KotlinTestWithEnvironmentManagement extends KtUsefulTestCase {
|
||||
static {
|
||||
System.setProperty("java.awt.headless", "true");
|
||||
DelegatingFlexibleType.RUN_SLOW_ASSERTIONS = true;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected KotlinCoreEnvironment createEnvironmentWithMockJdk(@NotNull ConfigurationKind configurationKind) {
|
||||
return createEnvironmentWithJdk(configurationKind, TestJdkKind.MOCK_JDK);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected KotlinCoreEnvironment createEnvironmentWithJdk(@NotNull ConfigurationKind configurationKind, @NotNull TestJdkKind jdkKind) {
|
||||
return KotlinTestUtils.createEnvironmentWithJdkAndNullabilityAnnotationsFromIdea(getTestRootDisposable(), configurationKind, jdkKind);
|
||||
}
|
||||
}
|
||||
@@ -1,263 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test;
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.io.ZipUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.cli.common.ExitCode;
|
||||
import org.jetbrains.kotlin.cli.js.K2JSCompiler;
|
||||
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler;
|
||||
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime;
|
||||
import org.jetbrains.kotlin.preloading.ClassPreloadingUtils;
|
||||
import org.jetbrains.kotlin.preloading.Preloader;
|
||||
import org.jetbrains.kotlin.utils.ExceptionUtilsKt;
|
||||
import org.jetbrains.kotlin.utils.PathUtil;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class MockLibraryUtil {
|
||||
|
||||
private static SoftReference<ClassLoader> compilerClassLoader = new SoftReference<ClassLoader>(null);
|
||||
|
||||
@NotNull
|
||||
public static File compileLibraryToJar(
|
||||
@NotNull String sourcesPath,
|
||||
@NotNull String jarName,
|
||||
boolean addSources,
|
||||
boolean isJsLibrary,
|
||||
boolean allowKotlinPackage,
|
||||
@NotNull String... extraClasspath
|
||||
) {
|
||||
if (isJsLibrary) {
|
||||
return compileJsLibraryToJar(sourcesPath, jarName, addSources);
|
||||
}
|
||||
else {
|
||||
return compileLibraryToJar(sourcesPath, jarName, addSources, allowKotlinPackage, extraClasspath);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static File compileLibraryToJar(
|
||||
@NotNull String sourcesPath,
|
||||
@NotNull String jarName,
|
||||
boolean addSources,
|
||||
boolean allowKotlinPackage,
|
||||
@NotNull String... extraClasspath
|
||||
) {
|
||||
try {
|
||||
File contentDir = KotlinTestUtils.tmpDir("testLibrary-" + jarName);
|
||||
|
||||
File classesDir = new File(contentDir, "classes");
|
||||
|
||||
File srcFile = new File(sourcesPath);
|
||||
List<File> kotlinFiles = FileUtil.findFilesByMask(Pattern.compile(".*\\.kt"), srcFile);
|
||||
if (srcFile.isFile() || !kotlinFiles.isEmpty()) {
|
||||
compileKotlin(sourcesPath, classesDir, allowKotlinPackage, extraClasspath);
|
||||
}
|
||||
|
||||
List<File> javaFiles = FileUtil.findFilesByMask(Pattern.compile(".*\\.java"), srcFile);
|
||||
if (!javaFiles.isEmpty()) {
|
||||
List<String> classpath = new ArrayList<String>();
|
||||
classpath.add(ForTestCompileRuntime.runtimeJarForTests().getPath());
|
||||
classpath.add(KotlinTestUtils.getAnnotationsJar().getPath());
|
||||
Collections.addAll(classpath, extraClasspath);
|
||||
|
||||
// Probably no kotlin files were present, so dir might not have been created after kotlin compiler
|
||||
if (classesDir.exists()) {
|
||||
classpath.add(classesDir.getPath());
|
||||
}
|
||||
else {
|
||||
FileUtil.createDirectory(classesDir);
|
||||
}
|
||||
|
||||
List<String> options = Arrays.asList(
|
||||
"-classpath", StringUtil.join(classpath, File.pathSeparator),
|
||||
"-d", classesDir.getPath()
|
||||
);
|
||||
|
||||
KotlinTestUtils.compileJavaFiles(javaFiles, options);
|
||||
}
|
||||
|
||||
return createJarFile(contentDir, classesDir, sourcesPath, jarName, addSources);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw ExceptionUtilsKt.rethrow(e);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static File compileJsLibraryToJar(
|
||||
@NotNull String sourcesPath,
|
||||
@NotNull String jarName,
|
||||
boolean addSources
|
||||
) {
|
||||
try {
|
||||
File contentDir = KotlinTestUtils.tmpDir("testLibrary-" + jarName);
|
||||
|
||||
File outDir = new File(contentDir, "out");
|
||||
File outputFile = new File(outDir, jarName + ".js");
|
||||
File outputMetaFile = new File(outDir, jarName + ".meta.js");
|
||||
compileKotlin2JS(sourcesPath, outputFile, outputMetaFile, true);
|
||||
|
||||
return createJarFile(contentDir, outDir, sourcesPath, jarName, addSources);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw ExceptionUtilsKt.rethrow(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static File createJarFile(File contentDir, File dirToAdd, String sourcesPath, String jarName, boolean addSources) throws IOException {
|
||||
File jarFile = new File(contentDir, jarName + ".jar");
|
||||
|
||||
ZipOutputStream zip = new ZipOutputStream(new FileOutputStream(jarFile));
|
||||
ZipUtil.addDirToZipRecursively(zip, jarFile, dirToAdd, "", null, null);
|
||||
if (addSources) {
|
||||
ZipUtil.addDirToZipRecursively(zip, jarFile, new File(sourcesPath), "src", null, null);
|
||||
}
|
||||
zip.close();
|
||||
|
||||
return jarFile;
|
||||
}
|
||||
|
||||
private static void runJvmCompiler(@NotNull List<String> args) {
|
||||
runCompiler(getCompiler2JVMClass(), args);
|
||||
}
|
||||
|
||||
private static void runJsCompiler(@NotNull List<String> args) {
|
||||
runCompiler(getCompiler2JSClass(), args);
|
||||
}
|
||||
|
||||
// Runs compiler in custom class loader to avoid effects caused by replacing Application with another one created in compiler.
|
||||
private static void runCompiler(@NotNull Class<?> compilerClass, @NotNull List<String> args) {
|
||||
try {
|
||||
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
|
||||
Object compiler = compilerClass.newInstance();
|
||||
Method execMethod = compilerClass.getMethod("exec", PrintStream.class, String[].class);
|
||||
|
||||
Enum<?> invocationResult = (Enum<?>) execMethod.invoke(compiler, new PrintStream(outStream), ArrayUtil.toStringArray(args));
|
||||
|
||||
assertEquals(new String(outStream.toByteArray()), ExitCode.OK.name(), invocationResult.name());
|
||||
}
|
||||
catch (Throwable e) {
|
||||
throw ExceptionUtilsKt.rethrow(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void compileKotlin(@NotNull String sourcesPath, @NotNull File outDir, @NotNull String... extraClasspath) {
|
||||
compileKotlin(sourcesPath, outDir, false, extraClasspath);
|
||||
}
|
||||
|
||||
public static void compileKotlin(
|
||||
@NotNull String sourcesPath,
|
||||
@NotNull File outDir,
|
||||
boolean allowKotlinPackage,
|
||||
@NotNull String... extraClasspath
|
||||
) {
|
||||
List<String> classpath = new ArrayList<String>();
|
||||
if (new File(sourcesPath).isDirectory()) {
|
||||
classpath.add(sourcesPath);
|
||||
}
|
||||
Collections.addAll(classpath, extraClasspath);
|
||||
|
||||
List<String> args = new ArrayList<String>();
|
||||
args.add(sourcesPath);
|
||||
args.add("-d");
|
||||
args.add(outDir.getAbsolutePath());
|
||||
args.add("-classpath");
|
||||
args.add(StringUtil.join(classpath, File.pathSeparator));
|
||||
if (allowKotlinPackage) {
|
||||
args.add("-Xallow-kotlin-package");
|
||||
}
|
||||
|
||||
runJvmCompiler(args);
|
||||
}
|
||||
|
||||
public static void compileKotlin2JS(@NotNull String sourcesPath, @NotNull File outputFile, @Nullable File metaFile, boolean kjsm) {
|
||||
List<String> args = new ArrayList<String>();
|
||||
if (metaFile != null) {
|
||||
args.add("-meta-info");
|
||||
}
|
||||
if (kjsm) {
|
||||
args.add("-kjsm");
|
||||
}
|
||||
|
||||
args.add("-output");
|
||||
args.add(outputFile.getAbsolutePath());
|
||||
|
||||
args.add(sourcesPath);
|
||||
|
||||
runJsCompiler(args);
|
||||
}
|
||||
|
||||
public static void compileKotlinModule(@NotNull String modulePath) {
|
||||
runJvmCompiler(Arrays.asList("-no-stdlib", "-module", modulePath));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static synchronized Class<?> getCompiler2JVMClass() {
|
||||
return loadCompilerClass(K2JVMCompiler.class.getName());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static synchronized Class<?> getCompiler2JSClass() {
|
||||
return loadCompilerClass(K2JSCompiler.class.getName());
|
||||
}
|
||||
|
||||
private static synchronized Class<?> loadCompilerClass(String compilerClassName) {
|
||||
try {
|
||||
ClassLoader classLoader = compilerClassLoader.get();
|
||||
if (classLoader == null) {
|
||||
classLoader = createCompilerClassLoader();
|
||||
compilerClassLoader = new SoftReference<ClassLoader>(classLoader);
|
||||
}
|
||||
return classLoader.loadClass(compilerClassName);
|
||||
}
|
||||
catch (Throwable e) {
|
||||
throw ExceptionUtilsKt.rethrow(e);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static synchronized ClassLoader createCompilerClassLoader() {
|
||||
try {
|
||||
File kotlinCompilerJar = new File(PathUtil.getKotlinPathsForDistDirectory().getLibPath(), "kotlin-compiler.jar");
|
||||
return ClassPreloadingUtils.preloadClasses(
|
||||
Collections.singletonList(kotlinCompilerJar), Preloader.DEFAULT_CLASS_NUMBER_ESTIMATE, null, null, null
|
||||
);
|
||||
}
|
||||
catch (Throwable e) {
|
||||
throw ExceptionUtilsKt.rethrow(e);
|
||||
}
|
||||
}
|
||||
|
||||
private MockLibraryUtil() {
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test;
|
||||
|
||||
import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public abstract class TestCaseWithTmpdir extends KtUsefulTestCase {
|
||||
protected File tmpdir;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
tmpdir = KotlinTestUtils.tmpDirForTest(this);
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test;
|
||||
|
||||
public enum TestJdkKind {
|
||||
MOCK_JDK,
|
||||
FULL_JDK,
|
||||
ANDROID_API,
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||
public @interface TestMetadata {
|
||||
String value();
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test.testFramework
|
||||
|
||||
import com.intellij.util.ThrowableRunnable
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import java.lang.reflect.InvocationTargetException
|
||||
import javax.swing.SwingUtilities
|
||||
|
||||
class EdtTestUtil {
|
||||
companion object {
|
||||
@TestOnly @JvmStatic fun runInEdtAndWait(runnable: Runnable) {
|
||||
runInEdtAndWait { runnable.run() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Test only because in production you must use Application.invokeAndWait(Runnable, ModalityState).
|
||||
// The problem is - Application logs errors, but not throws. But in tests must be thrown.
|
||||
// In any case name "runInEdtAndWait" is better than "invokeAndWait".
|
||||
@TestOnly
|
||||
fun runInEdtAndWait(runnable: () -> Unit) {
|
||||
if (SwingUtilities.isEventDispatchThread()) {
|
||||
runnable()
|
||||
}
|
||||
else {
|
||||
try {
|
||||
SwingUtilities.invokeAndWait(runnable)
|
||||
}
|
||||
catch (e: InvocationTargetException) {
|
||||
throw e.cause ?: e
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,340 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test.testFramework;
|
||||
|
||||
import com.intellij.core.CoreASTFactory;
|
||||
import com.intellij.lang.*;
|
||||
import com.intellij.lang.impl.PsiBuilderFactoryImpl;
|
||||
import com.intellij.mock.MockFileDocumentManagerImpl;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.application.PathManager;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.EditorFactory;
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.extensions.Extensions;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.fileEditor.impl.LoadTextUtil;
|
||||
import com.intellij.openapi.fileTypes.FileTypeFactory;
|
||||
import com.intellij.openapi.fileTypes.FileTypeManager;
|
||||
import com.intellij.openapi.options.SchemesManagerFactory;
|
||||
import com.intellij.openapi.progress.EmptyProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.progress.impl.CoreProgressManager;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vfs.CharsetToolkit;
|
||||
import com.intellij.pom.PomModel;
|
||||
import com.intellij.pom.core.impl.PomModelImpl;
|
||||
import com.intellij.pom.tree.TreeAspect;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.DebugUtil;
|
||||
import com.intellij.psi.impl.PsiCachedValuesFactory;
|
||||
import com.intellij.psi.impl.PsiFileFactoryImpl;
|
||||
import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry;
|
||||
import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistryImpl;
|
||||
import com.intellij.psi.impl.source.text.BlockSupportImpl;
|
||||
import com.intellij.psi.impl.source.text.DiffLog;
|
||||
import com.intellij.psi.util.CachedValuesManager;
|
||||
import com.intellij.testFramework.LightVirtualFile;
|
||||
import com.intellij.testFramework.TestDataFile;
|
||||
import com.intellij.util.CachedValuesManagerImpl;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.messages.MessageBus;
|
||||
import com.intellij.util.messages.MessageBusFactory;
|
||||
import junit.framework.TestCase;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.test.testFramework.mock.*;
|
||||
import org.picocontainer.ComponentAdapter;
|
||||
import org.picocontainer.MutablePicoContainer;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
|
||||
@SuppressWarnings("ALL")
|
||||
public abstract class KtParsingTestCase extends KtPlatformLiteFixture {
|
||||
public static final Key<Document> HARD_REF_TO_DOCUMENT_KEY = Key.create("HARD_REF_TO_DOCUMENT_KEY");
|
||||
protected String myFilePrefix = "";
|
||||
protected String myFileExt;
|
||||
protected final String myFullDataPath;
|
||||
protected PsiFile myFile;
|
||||
private MockPsiManager myPsiManager;
|
||||
private PsiFileFactoryImpl myFileFactory;
|
||||
protected Language myLanguage;
|
||||
private final ParserDefinition[] myDefinitions;
|
||||
private final boolean myLowercaseFirstLetter;
|
||||
|
||||
protected KtParsingTestCase(@NonNls @NotNull String dataPath, @NotNull String fileExt, @NotNull ParserDefinition... definitions) {
|
||||
this(dataPath, fileExt, false, definitions);
|
||||
}
|
||||
|
||||
protected KtParsingTestCase(@NonNls @NotNull String dataPath, @NotNull String fileExt, boolean lowercaseFirstLetter, @NotNull ParserDefinition... definitions) {
|
||||
myDefinitions = definitions;
|
||||
myFullDataPath = getTestDataPath() + "/" + dataPath;
|
||||
myFileExt = fileExt;
|
||||
myLowercaseFirstLetter = lowercaseFirstLetter;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
initApplication();
|
||||
ComponentAdapter component = getApplication().getPicoContainer().getComponentAdapter(ProgressManager.class.getName());
|
||||
|
||||
Extensions.registerAreaClass("IDEA_PROJECT", null);
|
||||
myProject = new MockProjectEx(getTestRootDisposable());
|
||||
myPsiManager = new MockPsiManager(myProject);
|
||||
myFileFactory = new PsiFileFactoryImpl(myPsiManager);
|
||||
MutablePicoContainer appContainer = getApplication().getPicoContainer();
|
||||
registerComponentInstance(appContainer, MessageBus.class, MessageBusFactory.newMessageBus(getApplication()));
|
||||
registerComponentInstance(appContainer, SchemesManagerFactory.class, new MockSchemesManagerFactory());
|
||||
final MockEditorFactory editorFactory = new MockEditorFactory();
|
||||
registerComponentInstance(appContainer, EditorFactory.class, editorFactory);
|
||||
registerComponentInstance(appContainer, FileDocumentManager.class, new MockFileDocumentManagerImpl(new Function<CharSequence, Document>() {
|
||||
@Override
|
||||
public Document fun(CharSequence charSequence) {
|
||||
return editorFactory.createDocument(charSequence);
|
||||
}
|
||||
}, HARD_REF_TO_DOCUMENT_KEY));
|
||||
registerComponentInstance(appContainer, PsiDocumentManager.class, new MockPsiDocumentManager());
|
||||
registerApplicationService(PsiBuilderFactory.class, new PsiBuilderFactoryImpl());
|
||||
registerApplicationService(DefaultASTFactory.class, new CoreASTFactory());
|
||||
registerApplicationService(ReferenceProvidersRegistry.class, new ReferenceProvidersRegistryImpl());
|
||||
|
||||
registerApplicationService(ProgressManager.class, new CoreProgressManager());
|
||||
|
||||
myProject.registerService(CachedValuesManager.class, new CachedValuesManagerImpl(myProject, new PsiCachedValuesFactory(myPsiManager)));
|
||||
myProject.registerService(PsiManager.class, myPsiManager);
|
||||
|
||||
this.registerExtensionPoint(FileTypeFactory.FILE_TYPE_FACTORY_EP, FileTypeFactory.class);
|
||||
|
||||
for (ParserDefinition definition : myDefinitions) {
|
||||
addExplicitExtension(LanguageParserDefinitions.INSTANCE, definition.getFileNodeType().getLanguage(), definition);
|
||||
}
|
||||
if (myDefinitions.length > 0) {
|
||||
configureFromParserDefinition(myDefinitions[0], myFileExt);
|
||||
}
|
||||
|
||||
// That's for reparse routines
|
||||
final PomModelImpl pomModel = new PomModelImpl(myProject);
|
||||
myProject.registerService(PomModel.class, pomModel);
|
||||
new TreeAspect(pomModel);
|
||||
}
|
||||
|
||||
public void configureFromParserDefinition(ParserDefinition definition, String extension) {
|
||||
myLanguage = definition.getFileNodeType().getLanguage();
|
||||
myFileExt = extension;
|
||||
addExplicitExtension(LanguageParserDefinitions.INSTANCE, this.myLanguage, definition);
|
||||
registerComponentInstance(
|
||||
getApplication().getPicoContainer(), FileTypeManager.class,
|
||||
new KtMockFileTypeManager(new KtMockLanguageFileType(myLanguage, myFileExt)));
|
||||
}
|
||||
|
||||
protected <T> void addExplicitExtension(final LanguageExtension<T> instance, final Language language, final T object) {
|
||||
instance.addExplicitExtension(language, object);
|
||||
Disposer.register(myProject, new Disposable() {
|
||||
@Override
|
||||
public void dispose() {
|
||||
instance.removeExplicitExtension(language, object);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> void registerExtensionPoint(final ExtensionPointName<T> extensionPointName, Class<T> aClass) {
|
||||
super.registerExtensionPoint(extensionPointName, aClass);
|
||||
Disposer.register(myProject, new Disposable() {
|
||||
@Override
|
||||
public void dispose() {
|
||||
Extensions.getRootArea().unregisterExtensionPoint(extensionPointName.getName());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected <T> void registerApplicationService(final Class<T> aClass, T object) {
|
||||
getApplication().registerService(aClass, object);
|
||||
Disposer.register(myProject, new Disposable() {
|
||||
@Override
|
||||
public void dispose() {
|
||||
getApplication().getPicoContainer().unregisterComponent(aClass.getName());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public MockProjectEx getProject() {
|
||||
return myProject;
|
||||
}
|
||||
|
||||
public MockPsiManager getPsiManager() {
|
||||
return myPsiManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
super.tearDown();
|
||||
myFile = null;
|
||||
myProject = null;
|
||||
myPsiManager = null;
|
||||
}
|
||||
|
||||
protected String getTestDataPath() {
|
||||
return PathManager.getHomePath();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public final String getTestName() {
|
||||
return getTestName(myLowercaseFirstLetter);
|
||||
}
|
||||
|
||||
protected boolean includeRanges() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean skipSpaces() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean checkAllPsiRoots() {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void doTest(boolean checkResult) {
|
||||
String name = getTestName();
|
||||
try {
|
||||
String text = loadFile(name + "." + myFileExt);
|
||||
myFile = createPsiFile(name, text);
|
||||
ensureParsed(myFile);
|
||||
assertEquals("light virtual file text mismatch", text, ((LightVirtualFile)myFile.getVirtualFile()).getContent().toString());
|
||||
assertEquals("virtual file text mismatch", text, LoadTextUtil.loadText(myFile.getVirtualFile()));
|
||||
assertEquals("doc text mismatch", text, myFile.getViewProvider().getDocument().getText());
|
||||
assertEquals("psi text mismatch", text, myFile.getText());
|
||||
ensureCorrectReparse(myFile);
|
||||
if (checkResult){
|
||||
checkResult(name, myFile);
|
||||
}
|
||||
else{
|
||||
toParseTreeText(myFile, skipSpaces(), includeRanges());
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected void doTest(String suffix) throws IOException {
|
||||
String name = getTestName();
|
||||
String text = loadFile(name + "." + myFileExt);
|
||||
myFile = createPsiFile(name, text);
|
||||
ensureParsed(myFile);
|
||||
assertEquals(text, myFile.getText());
|
||||
checkResult(name + suffix, myFile);
|
||||
}
|
||||
|
||||
protected void doCodeTest(String code) throws IOException {
|
||||
String name = getTestName();
|
||||
myFile = createPsiFile("a", code);
|
||||
ensureParsed(myFile);
|
||||
assertEquals(code, myFile.getText());
|
||||
checkResult(myFilePrefix + name, myFile);
|
||||
}
|
||||
|
||||
protected PsiFile createPsiFile(String name, String text) {
|
||||
return createFile(name + "." + myFileExt, text);
|
||||
}
|
||||
|
||||
protected PsiFile createFile(@NonNls String name, String text) {
|
||||
LightVirtualFile virtualFile = new LightVirtualFile(name, myLanguage, text);
|
||||
virtualFile.setCharset(CharsetToolkit.UTF8_CHARSET);
|
||||
return createFile(virtualFile);
|
||||
}
|
||||
|
||||
protected PsiFile createFile(LightVirtualFile virtualFile) {
|
||||
return myFileFactory.trySetupPsiForFile(virtualFile, myLanguage, true, false);
|
||||
}
|
||||
|
||||
protected void checkResult(@NonNls @TestDataFile String targetDataName, final PsiFile file) throws IOException {
|
||||
doCheckResult(myFullDataPath, file, checkAllPsiRoots(), targetDataName, skipSpaces(), includeRanges());
|
||||
}
|
||||
|
||||
public static void doCheckResult(String testDataDir,
|
||||
PsiFile file,
|
||||
boolean checkAllPsiRoots,
|
||||
String targetDataName,
|
||||
boolean skipSpaces,
|
||||
boolean printRanges) throws IOException {
|
||||
FileViewProvider provider = file.getViewProvider();
|
||||
Set<Language> languages = provider.getLanguages();
|
||||
|
||||
if (!checkAllPsiRoots || languages.size() == 1) {
|
||||
doCheckResult(testDataDir, targetDataName + ".txt", toParseTreeText(file, skipSpaces, printRanges).trim());
|
||||
return;
|
||||
}
|
||||
|
||||
for (Language language : languages) {
|
||||
PsiFile root = provider.getPsi(language);
|
||||
String expectedName = targetDataName + "." + language.getID() + ".txt";
|
||||
doCheckResult(testDataDir, expectedName, toParseTreeText(root, skipSpaces, printRanges).trim());
|
||||
}
|
||||
}
|
||||
|
||||
protected void checkResult(String actual) throws IOException {
|
||||
String name = getTestName();
|
||||
doCheckResult(myFullDataPath, myFilePrefix + name + ".txt", actual);
|
||||
}
|
||||
|
||||
protected void checkResult(@TestDataFile @NonNls String targetDataName, String actual) throws IOException {
|
||||
doCheckResult(myFullDataPath, targetDataName, actual);
|
||||
}
|
||||
|
||||
public static void doCheckResult(String fullPath, String targetDataName, String actual) throws IOException {
|
||||
String expectedFileName = fullPath + File.separatorChar + targetDataName;
|
||||
KtUsefulTestCase.assertSameLinesWithFile(expectedFileName, actual);
|
||||
}
|
||||
|
||||
protected static String toParseTreeText(PsiElement file, boolean skipSpaces, boolean printRanges) {
|
||||
return DebugUtil.psiToString(file, skipSpaces, printRanges);
|
||||
}
|
||||
|
||||
protected String loadFile(@NonNls @TestDataFile String name) throws IOException {
|
||||
return loadFileDefault(myFullDataPath, name);
|
||||
}
|
||||
|
||||
public static String loadFileDefault(String dir, String name) throws IOException {
|
||||
return FileUtil.loadFile(new File(dir, name), CharsetToolkit.UTF8, true).trim();
|
||||
}
|
||||
|
||||
public static void ensureParsed(PsiFile file) {
|
||||
file.accept(new PsiElementVisitor() {
|
||||
@Override
|
||||
public void visitElement(PsiElement element) {
|
||||
element.acceptChildren(this);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void ensureCorrectReparse(@NotNull PsiFile file) {
|
||||
String psiToStringDefault = DebugUtil.psiToString(file, false, false);
|
||||
String fileText = file.getText();
|
||||
DiffLog diffLog = (new BlockSupportImpl(file.getProject())).reparseRange(file, TextRange.allOf(fileText), fileText, new EmptyProgressIndicator(), fileText);
|
||||
diffLog.performActualPsiChange(file);
|
||||
|
||||
TestCase.assertEquals(psiToStringDefault, DebugUtil.psiToString(file, false, false));
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test.testFramework;
|
||||
|
||||
import com.intellij.core.CoreEncodingProjectManager;
|
||||
import com.intellij.mock.MockApplicationEx;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.extensions.ExtensionPoint;
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.extensions.Extensions;
|
||||
import com.intellij.openapi.extensions.ExtensionsArea;
|
||||
import com.intellij.openapi.fileTypes.FileTypeManager;
|
||||
import com.intellij.openapi.fileTypes.FileTypeRegistry;
|
||||
import com.intellij.openapi.util.Getter;
|
||||
import com.intellij.openapi.vfs.encoding.EncodingManager;
|
||||
import org.picocontainer.MutablePicoContainer;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
public abstract class KtPlatformLiteFixture extends KtUsefulTestCase {
|
||||
protected MockProjectEx myProject;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
Extensions.cleanRootArea(getTestRootDisposable());
|
||||
}
|
||||
|
||||
public static MockApplicationEx getApplication() {
|
||||
return (MockApplicationEx)ApplicationManager.getApplication();
|
||||
}
|
||||
|
||||
public void initApplication() {
|
||||
MockApplicationEx instance = new MockApplicationEx(getTestRootDisposable());
|
||||
ApplicationManager.setApplication(instance,
|
||||
new Getter<FileTypeRegistry>() {
|
||||
@Override
|
||||
public FileTypeRegistry get() {
|
||||
return FileTypeManager.getInstance();
|
||||
}
|
||||
},
|
||||
getTestRootDisposable());
|
||||
getApplication().registerService(EncodingManager.class, CoreEncodingProjectManager.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
super.tearDown();
|
||||
clearFields(this);
|
||||
myProject = null;
|
||||
}
|
||||
|
||||
protected <T> void registerExtensionPoint(ExtensionPointName<T> extensionPointName, Class<T> aClass) {
|
||||
registerExtensionPoint(Extensions.getRootArea(), extensionPointName, aClass);
|
||||
}
|
||||
|
||||
private static <T> void registerExtensionPoint(
|
||||
ExtensionsArea area, ExtensionPointName<T> extensionPointName,
|
||||
Class<? extends T> aClass
|
||||
) {
|
||||
String name = extensionPointName.getName();
|
||||
if (!area.hasExtensionPoint(name)) {
|
||||
ExtensionPoint.Kind kind = aClass.isInterface() || (aClass.getModifiers() & Modifier.ABSTRACT) != 0 ? ExtensionPoint.Kind.INTERFACE : ExtensionPoint.Kind.BEAN_CLASS;
|
||||
area.registerExtensionPoint(name, aClass.getName(), kind);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T registerComponentInstance(MutablePicoContainer container, Class<T> key, T implementation) {
|
||||
Object old = container.getComponentInstance(key);
|
||||
container.unregisterComponent(key);
|
||||
container.registerComponentInstance(key, implementation);
|
||||
//noinspection unchecked
|
||||
return (T)old;
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test.testFramework;
|
||||
|
||||
import com.intellij.ide.util.treeView.AbstractTreeNode;
|
||||
import com.intellij.openapi.ui.Queryable;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
// Based on com.intellij.testFramework.PlatformTestUtil
|
||||
public class KtPlatformTestUtil {
|
||||
@NotNull
|
||||
public static String getTestName(@NotNull String name, boolean lowercaseFirstLetter) {
|
||||
name = StringUtil.trimStart(name, "test");
|
||||
return StringUtil.isEmpty(name) ? "" : lowercaseFirstLetter(name, lowercaseFirstLetter);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String lowercaseFirstLetter(@NotNull String name, boolean lowercaseFirstLetter) {
|
||||
if (lowercaseFirstLetter && !isAllUppercaseName(name)) {
|
||||
name = Character.toLowerCase(name.charAt(0)) + name.substring(1);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
public static boolean isAllUppercaseName(@NotNull String name) {
|
||||
int uppercaseChars = 0;
|
||||
for (int i = 0; i < name.length(); i++) {
|
||||
if (Character.isLowerCase(name.charAt(i))) {
|
||||
return false;
|
||||
}
|
||||
if (Character.isUpperCase(name.charAt(i))) {
|
||||
uppercaseChars++;
|
||||
}
|
||||
}
|
||||
return uppercaseChars >= 3;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected static String toString(@Nullable Object node, @Nullable Queryable.PrintInfo printInfo) {
|
||||
if (node instanceof AbstractTreeNode) {
|
||||
if (printInfo != null) {
|
||||
return ((AbstractTreeNode) node).toTestString(printInfo);
|
||||
}
|
||||
else {
|
||||
@SuppressWarnings({"deprecation", "UnnecessaryLocalVariable"}) String presentation =
|
||||
((AbstractTreeNode) node).getTestPresentation();
|
||||
return presentation;
|
||||
}
|
||||
}
|
||||
if (node == null) {
|
||||
return "NULL";
|
||||
}
|
||||
return node.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,441 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test.testFramework;
|
||||
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.application.impl.ApplicationInfoImpl;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.io.FileSystemUtil;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.CharsetToolkit;
|
||||
import com.intellij.rt.execution.junit.FileComparisonFailure;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.ReflectionUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.hash.HashMap;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import gnu.trove.THashSet;
|
||||
import junit.framework.AssertionFailedError;
|
||||
import junit.framework.TestCase;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.Assert;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
|
||||
@SuppressWarnings("UseOfSystemOutOrSystemErr")
|
||||
public abstract class KtUsefulTestCase extends TestCase {
|
||||
private static final String TEMP_DIR_MARKER = "unitTest_";
|
||||
|
||||
private static final String ORIGINAL_TEMP_DIR = FileUtil.getTempDirectory();
|
||||
|
||||
private static final Map<String, Long> TOTAL_SETUP_COST_MILLIS = new HashMap<String, Long>();
|
||||
private static final Map<String, Long> TOTAL_TEARDOWN_COST_MILLIS = new HashMap<String, Long>();
|
||||
|
||||
@NotNull
|
||||
protected final Disposable myTestRootDisposable = new Disposable() {
|
||||
@Override
|
||||
public void dispose() { }
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String testName = getTestName(false);
|
||||
return KtUsefulTestCase.this.getClass() + (StringUtil.isEmpty(testName) ? "" : ".test" + testName);
|
||||
}
|
||||
};
|
||||
|
||||
private static final String ourPathToKeep = null;
|
||||
private final List<String> myPathsToKeep = new ArrayList<String>();
|
||||
|
||||
private String myTempDir;
|
||||
|
||||
static {
|
||||
// Radar #5755208: Command line Java applications need a way to launch without a Dock icon.
|
||||
System.setProperty("apple.awt.UIElement", "true");
|
||||
}
|
||||
|
||||
private boolean oldDisposerDebug;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
String testName = FileUtil.sanitizeFileName(getTestName(true));
|
||||
if (StringUtil.isEmptyOrSpaces(testName)) testName = "";
|
||||
testName = new File(testName).getName(); // in case the test name contains file separators
|
||||
myTempDir = new File(ORIGINAL_TEMP_DIR, TEMP_DIR_MARKER + testName).getPath();
|
||||
FileUtil.resetCanonicalTempPathCache(myTempDir);
|
||||
boolean isPerformanceTest = isPerformanceTest();
|
||||
ApplicationInfoImpl.setInPerformanceTest(isPerformanceTest);
|
||||
// turn off Disposer debugging for performance tests
|
||||
oldDisposerDebug = Disposer.setDebugMode(Disposer.isDebugMode() && !isPerformanceTest);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
try {
|
||||
Disposer.dispose(myTestRootDisposable);
|
||||
cleanupSwingDataStructures();
|
||||
cleanupDeleteOnExitHookList();
|
||||
}
|
||||
finally {
|
||||
Disposer.setDebugMode(oldDisposerDebug);
|
||||
FileUtil.resetCanonicalTempPathCache(ORIGINAL_TEMP_DIR);
|
||||
if (hasTmpFilesToKeep()) {
|
||||
File[] files = new File(myTempDir).listFiles();
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
if (!shouldKeepTmpFile(file)) {
|
||||
FileUtil.delete(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
FileUtil.delete(new File(myTempDir));
|
||||
}
|
||||
}
|
||||
|
||||
UIUtil.removeLeakingAppleListeners();
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
private boolean hasTmpFilesToKeep() {
|
||||
return !myPathsToKeep.isEmpty();
|
||||
}
|
||||
|
||||
private boolean shouldKeepTmpFile(File file) {
|
||||
String path = file.getPath();
|
||||
if (FileUtil.pathsEqual(path, ourPathToKeep)) return true;
|
||||
for (String pathToKeep : myPathsToKeep) {
|
||||
if (FileUtil.pathsEqual(path, pathToKeep)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static final Set<String> DELETE_ON_EXIT_HOOK_DOT_FILES;
|
||||
private static final Class DELETE_ON_EXIT_HOOK_CLASS;
|
||||
static {
|
||||
Class<?> aClass;
|
||||
try {
|
||||
aClass = Class.forName("java.io.DeleteOnExitHook");
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
Set<String> files = ReflectionUtil.getStaticFieldValue(aClass, Set.class, "files");
|
||||
DELETE_ON_EXIT_HOOK_CLASS = aClass;
|
||||
DELETE_ON_EXIT_HOOK_DOT_FILES = files;
|
||||
}
|
||||
|
||||
private static void cleanupDeleteOnExitHookList() throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
|
||||
// try to reduce file set retained by java.io.DeleteOnExitHook
|
||||
List<String> list;
|
||||
synchronized (DELETE_ON_EXIT_HOOK_CLASS) {
|
||||
if (DELETE_ON_EXIT_HOOK_DOT_FILES.isEmpty()) return;
|
||||
list = new ArrayList<String>(DELETE_ON_EXIT_HOOK_DOT_FILES);
|
||||
}
|
||||
for (int i = list.size() - 1; i >= 0; i--) {
|
||||
String path = list.get(i);
|
||||
if (FileSystemUtil.getAttributes(path) == null || new File(path).delete()) {
|
||||
synchronized (DELETE_ON_EXIT_HOOK_CLASS) {
|
||||
DELETE_ON_EXIT_HOOK_DOT_FILES.remove(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void cleanupSwingDataStructures() throws Exception {
|
||||
Object manager = ReflectionUtil.getDeclaredMethod(Class.forName("javax.swing.KeyboardManager"), "getCurrentManager").invoke(null);
|
||||
Map componentKeyStrokeMap = ReflectionUtil.getField(manager.getClass(), manager, Hashtable.class, "componentKeyStrokeMap");
|
||||
componentKeyStrokeMap.clear();
|
||||
Map containerMap = ReflectionUtil.getField(manager.getClass(), manager, Hashtable.class, "containerMap");
|
||||
containerMap.clear();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public final Disposable getTestRootDisposable() {
|
||||
return myTestRootDisposable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void runTest() throws Throwable {
|
||||
final Throwable[] throwables = new Throwable[1];
|
||||
|
||||
Runnable runnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
KtUsefulTestCase.super.runTest();
|
||||
}
|
||||
catch (InvocationTargetException e) {
|
||||
e.fillInStackTrace();
|
||||
throwables[0] = e.getTargetException();
|
||||
}
|
||||
catch (IllegalAccessException e) {
|
||||
e.fillInStackTrace();
|
||||
throwables[0] = e;
|
||||
}
|
||||
catch (Throwable e) {
|
||||
throwables[0] = e;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
invokeTestRunnable(runnable);
|
||||
|
||||
if (throwables[0] != null) {
|
||||
throw throwables[0];
|
||||
}
|
||||
}
|
||||
|
||||
private static void invokeTestRunnable(@NotNull Runnable runnable) throws Exception {
|
||||
EdtTestUtil.runInEdtAndWait(runnable);
|
||||
}
|
||||
|
||||
private void defaultRunBare() throws Throwable {
|
||||
Throwable exception = null;
|
||||
try {
|
||||
long setupStart = System.nanoTime();
|
||||
setUp();
|
||||
long setupCost = (System.nanoTime() - setupStart) / 1000000;
|
||||
logPerClassCost(setupCost, TOTAL_SETUP_COST_MILLIS);
|
||||
|
||||
runTest();
|
||||
}
|
||||
catch (Throwable running) {
|
||||
exception = running;
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
long teardownStart = System.nanoTime();
|
||||
tearDown();
|
||||
long teardownCost = (System.nanoTime() - teardownStart) / 1000000;
|
||||
logPerClassCost(teardownCost, TOTAL_TEARDOWN_COST_MILLIS);
|
||||
}
|
||||
catch (Throwable tearingDown) {
|
||||
if (exception == null) exception = tearingDown;
|
||||
}
|
||||
}
|
||||
if (exception != null) throw exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the setup cost grouped by test fixture class (superclass of the current test class).
|
||||
*
|
||||
* @param cost setup cost in milliseconds
|
||||
*/
|
||||
private void logPerClassCost(long cost, Map<String, Long> costMap) {
|
||||
Class<?> superclass = getClass().getSuperclass();
|
||||
Long oldCost = costMap.get(superclass.getName());
|
||||
long newCost = oldCost == null ? cost : oldCost + cost;
|
||||
costMap.put(superclass.getName(), newCost);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runBare() throws Throwable {
|
||||
this.defaultRunBare();
|
||||
}
|
||||
|
||||
@NonNls
|
||||
public static String toString(Iterable<?> collection) {
|
||||
if (!collection.iterator().hasNext()) {
|
||||
return "<empty>";
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (Object o : collection) {
|
||||
if (o instanceof THashSet) {
|
||||
builder.append(new TreeSet<Object>((THashSet)o));
|
||||
}
|
||||
else {
|
||||
builder.append(o);
|
||||
}
|
||||
builder.append("\n");
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private static <T> void assertOrderedEquals(String errorMsg, @NotNull Iterable<T> actual, @NotNull T... expected) {
|
||||
Assert.assertNotNull(actual);
|
||||
Assert.assertNotNull(expected);
|
||||
assertOrderedEquals(errorMsg, actual, Arrays.asList(expected));
|
||||
}
|
||||
|
||||
public static <T> void assertOrderedEquals(
|
||||
String erroMsg,
|
||||
Iterable<? extends T> actual,
|
||||
Collection<? extends T> expected) {
|
||||
ArrayList<T> list = new ArrayList<T>();
|
||||
for (T t : actual) {
|
||||
list.add(t);
|
||||
}
|
||||
if (!list.equals(new ArrayList<T>(expected))) {
|
||||
String expectedString = toString(expected);
|
||||
String actualString = toString(actual);
|
||||
Assert.assertEquals(erroMsg, expectedString, actualString);
|
||||
Assert.fail("Warning! 'toString' does not reflect the difference.\nExpected: " + expectedString + "\nActual: " + actualString);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> void assertSameElements(T[] collection, T... expected) {
|
||||
assertSameElements(Arrays.asList(collection), expected);
|
||||
}
|
||||
|
||||
public static <T> void assertSameElements(Collection<? extends T> collection, T... expected) {
|
||||
assertSameElements(collection, Arrays.asList(expected));
|
||||
}
|
||||
|
||||
public static <T> void assertSameElements(Collection<? extends T> collection, Collection<T> expected) {
|
||||
assertSameElements(null, collection, expected);
|
||||
}
|
||||
|
||||
public static <T> void assertSameElements(String message, Collection<? extends T> collection, Collection<T> expected) {
|
||||
assertNotNull(collection);
|
||||
assertNotNull(expected);
|
||||
if (collection.size() != expected.size() || !new HashSet<T>(expected).equals(new HashSet<T>(collection))) {
|
||||
Assert.assertEquals(message, toString(expected, "\n"), toString(collection, "\n"));
|
||||
Assert.assertEquals(message, new HashSet<T>(expected), new HashSet<T>(collection));
|
||||
}
|
||||
}
|
||||
|
||||
public static String toString(Object[] collection, String separator) {
|
||||
return toString(Arrays.asList(collection), separator);
|
||||
}
|
||||
|
||||
public static String toString(Collection<?> collection, String separator) {
|
||||
List<String> list = ContainerUtil.map2List(collection, new Function<Object, String>() {
|
||||
@Override
|
||||
public String fun(Object o) {
|
||||
return String.valueOf(o);
|
||||
}
|
||||
});
|
||||
Collections.sort(list);
|
||||
StringBuilder builder = new StringBuilder();
|
||||
boolean flag = false;
|
||||
for (String o : list) {
|
||||
if (flag) {
|
||||
builder.append(separator);
|
||||
}
|
||||
builder.append(o);
|
||||
flag = true;
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Contract("null, _ -> fail")
|
||||
public static <T> T assertInstanceOf(Object o, Class<T> aClass) {
|
||||
Assert.assertNotNull("Expected instance of: " + aClass.getName() + " actual: " + null, o);
|
||||
Assert.assertTrue("Expected instance of: " + aClass.getName() + " actual: " + o.getClass().getName(), aClass.isInstance(o));
|
||||
@SuppressWarnings("unchecked") T t = (T)o;
|
||||
return t;
|
||||
}
|
||||
|
||||
protected static <T> void assertEmpty(String errorMsg, Collection<T> collection) {
|
||||
//noinspection unchecked
|
||||
assertOrderedEquals(errorMsg, collection);
|
||||
}
|
||||
|
||||
protected static void assertSize(int expectedSize, Object[] array) {
|
||||
assertEquals(toString(Arrays.asList(array)), expectedSize, array.length);
|
||||
}
|
||||
|
||||
public static void assertSameLines(String expected, String actual) {
|
||||
String expectedText = StringUtil.convertLineSeparators(expected.trim());
|
||||
String actualText = StringUtil.convertLineSeparators(actual.trim());
|
||||
Assert.assertEquals(expectedText, actualText);
|
||||
}
|
||||
|
||||
protected String getTestName(boolean lowercaseFirstLetter) {
|
||||
return getTestName(getName(), lowercaseFirstLetter);
|
||||
}
|
||||
|
||||
public static String getTestName(String name, boolean lowercaseFirstLetter) {
|
||||
return name == null ? "" : KtPlatformTestUtil.getTestName(name, lowercaseFirstLetter);
|
||||
}
|
||||
|
||||
/** @deprecated use {@link KtPlatformTestUtil#lowercaseFirstLetter(String, boolean)} (to be removed in IDEA 17) */
|
||||
@SuppressWarnings("unused")
|
||||
public static String lowercaseFirstLetter(String name, boolean lowercaseFirstLetter) {
|
||||
return KtPlatformTestUtil.lowercaseFirstLetter(name, lowercaseFirstLetter);
|
||||
}
|
||||
|
||||
/** @deprecated use {@link KtPlatformTestUtil#isAllUppercaseName(String)} (to be removed in IDEA 17) */
|
||||
@SuppressWarnings("unused")
|
||||
public static boolean isAllUppercaseName(String name) {
|
||||
return KtPlatformTestUtil.isAllUppercaseName(name);
|
||||
}
|
||||
|
||||
public static void assertSameLinesWithFile(String filePath, String actualText) {
|
||||
assertSameLinesWithFile(filePath, actualText, true);
|
||||
}
|
||||
|
||||
public static void assertSameLinesWithFile(String filePath, String actualText, boolean trimBeforeComparing) {
|
||||
String fileText;
|
||||
try {
|
||||
fileText = FileUtil.loadFile(new File(filePath), CharsetToolkit.UTF8_CHARSET);
|
||||
}
|
||||
catch (FileNotFoundException e) {
|
||||
VfsTestUtil.overwriteTestData(filePath, actualText);
|
||||
throw new AssertionFailedError("No output text found. File " + filePath + " created.");
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
String expected = StringUtil.convertLineSeparators(trimBeforeComparing ? fileText.trim() : fileText);
|
||||
String actual = StringUtil.convertLineSeparators(trimBeforeComparing ? actualText.trim() : actualText);
|
||||
if (!Comparing.equal(expected, actual)) {
|
||||
throw new FileComparisonFailure(null, expected, actual, filePath);
|
||||
}
|
||||
}
|
||||
|
||||
public static void clearFields(Object test) throws IllegalAccessException {
|
||||
Class aClass = test.getClass();
|
||||
while (aClass != null) {
|
||||
clearDeclaredFields(test, aClass);
|
||||
aClass = aClass.getSuperclass();
|
||||
}
|
||||
}
|
||||
|
||||
private static void clearDeclaredFields(Object test, Class aClass) throws IllegalAccessException {
|
||||
if (aClass == null) return;
|
||||
for (Field field : aClass.getDeclaredFields()) {
|
||||
@NonNls String name = field.getDeclaringClass().getName();
|
||||
if (!name.startsWith("junit.framework.") && !name.startsWith("com.intellij.testFramework.")) {
|
||||
int modifiers = field.getModifiers();
|
||||
if ((modifiers & Modifier.FINAL) == 0 && (modifiers & Modifier.STATIC) == 0 && !field.getType().isPrimitive()) {
|
||||
field.setAccessible(true);
|
||||
field.set(test, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isPerformanceTest() {
|
||||
String name = getName();
|
||||
return name != null && name.contains("Performance") || getClass().getName().contains("Performance");
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test.testFramework
|
||||
|
||||
import com.intellij.mock.MockProject
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.project.Project
|
||||
|
||||
interface ProjectEx : Project {
|
||||
fun init()
|
||||
|
||||
fun setProjectName(name: String)
|
||||
}
|
||||
|
||||
class MockProjectEx(parentDisposable: Disposable) : MockProject(if (ApplicationManager.getApplication() != null) ApplicationManager.getApplication().picoContainer else null, parentDisposable), ProjectEx {
|
||||
override fun setProjectName(name: String) {
|
||||
}
|
||||
|
||||
override fun init() {
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.kotlin.test.testFramework;
|
||||
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
public class TestRunnerUtil {
|
||||
private TestRunnerUtil() {
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
public static boolean isJUnit4TestClass(Class aClass) {
|
||||
int modifiers = aClass.getModifiers();
|
||||
if ((modifiers & Modifier.ABSTRACT) != 0) return false;
|
||||
if ((modifiers & Modifier.PUBLIC) == 0) return false;
|
||||
if (aClass.getAnnotation(RunWith.class) != null) return true;
|
||||
for (Method method : aClass.getMethods()) {
|
||||
if (method.getAnnotation(Test.class) != null) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test.testFramework;
|
||||
|
||||
import com.intellij.openapi.application.AccessToken;
|
||||
import com.intellij.openapi.application.WriteAction;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vfs.VfsUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.PathUtil;
|
||||
import com.intellij.util.text.StringTokenizer;
|
||||
import org.junit.Assert;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public class VfsTestUtil {
|
||||
private VfsTestUtil() {
|
||||
}
|
||||
|
||||
public static VirtualFile createFile(VirtualFile root, String relativePath) {
|
||||
return createFile(root, relativePath, "");
|
||||
}
|
||||
|
||||
public static VirtualFile createFile(VirtualFile root, String relativePath, String text) {
|
||||
return createFileOrDir(root, relativePath, text, false);
|
||||
}
|
||||
|
||||
private static VirtualFile createFileOrDir(
|
||||
VirtualFile root,
|
||||
String relativePath,
|
||||
String text,
|
||||
boolean dir) {
|
||||
try {
|
||||
AccessToken token = WriteAction.start();
|
||||
try {
|
||||
VirtualFile parent = root;
|
||||
Assert.assertNotNull(parent);
|
||||
StringTokenizer parents = new StringTokenizer(PathUtil.getParentPath(relativePath), "/");
|
||||
while (parents.hasMoreTokens()) {
|
||||
String name = parents.nextToken();
|
||||
VirtualFile child = parent.findChild(name);
|
||||
if (child == null || !child.isValid()) {
|
||||
child = parent.createChildDirectory(VfsTestUtil.class, name);
|
||||
}
|
||||
parent = child;
|
||||
}
|
||||
|
||||
VirtualFile file;
|
||||
parent.getChildren();//need this to ensure that fileCreated event is fired
|
||||
if (dir) {
|
||||
file = parent.createChildDirectory(VfsTestUtil.class, PathUtil.getFileName(relativePath));
|
||||
}
|
||||
else {
|
||||
file = parent.findFileByRelativePath(relativePath);
|
||||
if (file == null) {
|
||||
file = parent.createChildData(VfsTestUtil.class, PathUtil.getFileName(relativePath));
|
||||
}
|
||||
VfsUtil.saveText(file, text);
|
||||
}
|
||||
return file;
|
||||
}
|
||||
finally {
|
||||
token.finish();
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
public static void overwriteTestData(String filePath, String actual) {
|
||||
try {
|
||||
FileUtil.writeToFile(new File(filePath), actual);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
-167
@@ -1,167 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test.testFramework.mock;
|
||||
|
||||
import com.intellij.openapi.fileTypes.*;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class KtMockFileTypeManager extends FileTypeManager {
|
||||
private final FileType fileType;
|
||||
|
||||
public KtMockFileTypeManager(FileType fileType) {
|
||||
this.fileType = fileType;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getIgnoredFilesList() {
|
||||
throw new IncorrectOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIgnoredFilesList(@NotNull String list) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerFileType(@NotNull FileType type, @NotNull List<FileNameMatcher> defaultAssociations) {
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public FileType getFileTypeByFileName(@NotNull String fileName) {
|
||||
return fileType;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public FileType getFileTypeByFile(@NotNull VirtualFile file) {
|
||||
return fileType;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public FileType getFileTypeByExtension(@NotNull String extension) {
|
||||
return fileType;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public FileType[] getRegisteredFileTypes() {
|
||||
return FileType.EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFileIgnored(@NotNull String name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFileIgnored(@NotNull VirtualFile file) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String[] getAssociatedExtensions(@NotNull FileType type) {
|
||||
return ArrayUtil.EMPTY_STRING_ARRAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addFileTypeListener(@NotNull FileTypeListener listener) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeFileTypeListener(@NotNull FileTypeListener listener) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileType getKnownFileTypeOrAssociate(@NotNull VirtualFile file) {
|
||||
return file.getFileType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileType getKnownFileTypeOrAssociate(@NotNull VirtualFile file, @NotNull Project project) {
|
||||
return getKnownFileTypeOrAssociate(file);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public List<FileNameMatcher> getAssociations(@NotNull FileType type) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void associate(@NotNull FileType type, @NotNull FileNameMatcher matcher) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAssociation(@NotNull FileType type, @NotNull FileNameMatcher matcher) {
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public FileType getStdFileType(@NotNull @NonNls String fileTypeName) {
|
||||
if ("ARCHIVE".equals(fileTypeName) || "CLASS".equals(fileTypeName)) return UnknownFileType.INSTANCE;
|
||||
if ("PLAIN_TEXT".equals(fileTypeName)) return PlainTextFileType.INSTANCE;
|
||||
if ("JAVA".equals(fileTypeName)) return loadFileTypeSafe("com.intellij.ide.highlighter.JavaFileType", fileTypeName);
|
||||
if ("XML".equals(fileTypeName)) return loadFileTypeSafe("com.intellij.ide.highlighter.XmlFileType", fileTypeName);
|
||||
if ("DTD".equals(fileTypeName)) return loadFileTypeSafe("com.intellij.ide.highlighter.DTDFileType", fileTypeName);
|
||||
if ("JSP".equals(fileTypeName)) return loadFileTypeSafe("com.intellij.ide.highlighter.NewJspFileType", fileTypeName);
|
||||
if ("JSPX".equals(fileTypeName)) return loadFileTypeSafe("com.intellij.ide.highlighter.JspxFileType", fileTypeName);
|
||||
if ("HTML".equals(fileTypeName)) return loadFileTypeSafe("com.intellij.ide.highlighter.HtmlFileType", fileTypeName);
|
||||
if ("XHTML".equals(fileTypeName)) return loadFileTypeSafe("com.intellij.ide.highlighter.XHtmlFileType", fileTypeName);
|
||||
if ("JavaScript".equals(fileTypeName)) return loadFileTypeSafe("com.intellij.lang.javascript.JavaScriptFileType", fileTypeName);
|
||||
if ("Properties".equals(fileTypeName)) return loadFileTypeSafe("com.intellij.lang.properties.PropertiesFileType", fileTypeName);
|
||||
return new KtMockLanguageFileType(PlainTextLanguage.INSTANCE, fileTypeName.toLowerCase());
|
||||
}
|
||||
|
||||
private static FileType loadFileTypeSafe(String className, String fileTypeName) {
|
||||
try {
|
||||
return (FileType)Class.forName(className).getField("INSTANCE").get(null);
|
||||
}
|
||||
catch (Exception ignored) {
|
||||
return new KtMockLanguageFileType(PlainTextLanguage.INSTANCE, fileTypeName.toLowerCase(Locale.ENGLISH));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFileOfType(@NotNull VirtualFile file, @NotNull FileType type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public FileType detectFileTypeFromContent(@NotNull VirtualFile file) {
|
||||
return UnknownFileType.INSTANCE;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public FileType findFileTypeByName(@NotNull String fileTypeName) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test.testFramework.mock;
|
||||
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.openapi.fileTypes.LanguageFileType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
public class KtMockLanguageFileType extends LanguageFileType {
|
||||
private final String myExtension;
|
||||
|
||||
public KtMockLanguageFileType(@NotNull Language language, String extension) {
|
||||
super(language);
|
||||
this.myExtension = extension;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getName() {
|
||||
return this.getLanguage().getID();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getDescription() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getDefaultExtension() {
|
||||
String var10000 = this.myExtension;
|
||||
if(this.myExtension == null) {
|
||||
throw new IllegalStateException(String.format("@NotNull method %s.%s must not return null", new Object[]{"com/intellij/mock/KtMockLanguageFileType", "getDefaultExtension"}));
|
||||
} else {
|
||||
return var10000;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Icon getIcon() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof LanguageFileType && this.getLanguage().equals(((LanguageFileType) obj).getLanguage());
|
||||
}
|
||||
}
|
||||
-94
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.kotlin.test.testFramework.mock;
|
||||
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.editor.event.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class MockEditorEventMulticaster implements EditorEventMulticaster {
|
||||
public MockEditorEventMulticaster() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDocumentListener(@NotNull DocumentListener listener) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDocumentListener(@NotNull DocumentListener listener, @NotNull Disposable parentDisposable) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeDocumentListener(@NotNull DocumentListener listener) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addEditorMouseListener(@NotNull EditorMouseListener listener) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addEditorMouseListener(@NotNull EditorMouseListener listener, @NotNull Disposable parentDisposable) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeEditorMouseListener(@NotNull EditorMouseListener listener) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addEditorMouseMotionListener(@NotNull EditorMouseMotionListener listener) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addEditorMouseMotionListener(@NotNull EditorMouseMotionListener listener, @NotNull Disposable parentDisposable) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeEditorMouseMotionListener(@NotNull EditorMouseMotionListener listener) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCaretListener(@NotNull CaretListener listener) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCaretListener(@NotNull CaretListener listener, @NotNull Disposable parentDisposable) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeCaretListener(@NotNull CaretListener listener) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSelectionListener(@NotNull SelectionListener listener) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSelectionListener(@NotNull SelectionListener listener, @NotNull Disposable parentDisposable) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeSelectionListener(@NotNull SelectionListener listener) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addVisibleAreaListener(@NotNull VisibleAreaListener listener) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeVisibleAreaListener(@NotNull VisibleAreaListener listener) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.kotlin.test.testFramework.mock;
|
||||
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.EditorFactory;
|
||||
import com.intellij.openapi.editor.event.EditorEventMulticaster;
|
||||
import com.intellij.openapi.editor.event.EditorFactoryListener;
|
||||
import com.intellij.openapi.editor.impl.DocumentImpl;
|
||||
import com.intellij.openapi.fileTypes.FileType;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.text.CharArrayCharSequence;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class MockEditorFactory extends EditorFactory {
|
||||
@Override
|
||||
public Editor createEditor(@NotNull Document document) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editor createViewer(@NotNull Document document) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editor createEditor(@NotNull Document document, Project project) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editor createEditor(@NotNull Document document, Project project, @NotNull VirtualFile file, boolean isViewer) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editor createEditor(@NotNull Document document, Project project, @NotNull FileType fileType, boolean isViewer) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editor createViewer(@NotNull Document document, Project project) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void releaseEditor(@NotNull Editor editor) {
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Editor[] getEditors(@NotNull Document document, Project project) {
|
||||
return Editor.EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Editor[] getEditors(@NotNull Document document) {
|
||||
return getEditors(document, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Editor[] getAllEditors() {
|
||||
return Editor.EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addEditorFactoryListener(@NotNull EditorFactoryListener listener) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addEditorFactoryListener(@NotNull EditorFactoryListener listener, @NotNull Disposable parentDisposable) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeEditorFactoryListener(@NotNull EditorFactoryListener listener) {
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public EditorEventMulticaster getEventMulticaster() {
|
||||
return new MockEditorEventMulticaster();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Document createDocument(@NotNull CharSequence text) {
|
||||
return new DocumentImpl(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Document createDocument(@NotNull char[] text) {
|
||||
return createDocument(new CharArrayCharSequence(text));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshAllEditors() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.kotlin.test.testFramework.mock;
|
||||
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.FileViewProvider;
|
||||
import com.intellij.psi.PsiDirectory;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.SingleRootFileViewProvider;
|
||||
import com.intellij.psi.impl.PsiManagerEx;
|
||||
import com.intellij.psi.impl.file.impl.FileManager;
|
||||
import com.intellij.util.containers.WeakFactoryMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class MockFileManager implements FileManager {
|
||||
private final PsiManagerEx myManager;
|
||||
// in mock tests it's LightVirtualFile, they're only alive when they're referenced,
|
||||
// and there can not be several instances representing the same file
|
||||
private final WeakFactoryMap<VirtualFile, FileViewProvider> myViewProviders = new WeakFactoryMap<VirtualFile, FileViewProvider>() {
|
||||
@Override
|
||||
protected FileViewProvider create(VirtualFile key) {
|
||||
return new SingleRootFileViewProvider(myManager, key);
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public FileViewProvider createFileViewProvider(@NotNull VirtualFile file, boolean eventSystemEnabled) {
|
||||
return new SingleRootFileViewProvider(myManager, file, eventSystemEnabled);
|
||||
}
|
||||
|
||||
public MockFileManager(PsiManagerEx manager) {
|
||||
myManager = manager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
throw new UnsupportedOperationException("Method dispose is not yet implemented in " + getClass().getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public PsiFile findFile(@NotNull VirtualFile vFile) {
|
||||
return getCachedPsiFile(vFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public PsiDirectory findDirectory(@NotNull VirtualFile vFile) {
|
||||
throw new UnsupportedOperationException("Method findDirectory is not yet implemented in " + getClass().getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reloadFromDisk(@NotNull PsiFile file) //Q: move to PsiFile(Impl)?
|
||||
{
|
||||
throw new UnsupportedOperationException("Method reloadFromDisk is not yet implemented in " + getClass().getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public PsiFile getCachedPsiFile(@NotNull VirtualFile vFile) {
|
||||
FileViewProvider provider = findCachedViewProvider(vFile);
|
||||
return provider.getPsi(provider.getBaseLanguage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanupForNextTest() {
|
||||
myViewProviders.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileViewProvider findViewProvider(@NotNull VirtualFile file) {
|
||||
throw new UnsupportedOperationException("Method findViewProvider is not yet implemented in " + getClass().getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileViewProvider findCachedViewProvider(@NotNull VirtualFile file) {
|
||||
return myViewProviders.get(file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setViewProvider(@NotNull VirtualFile virtualFile, FileViewProvider fileViewProvider) {
|
||||
myViewProviders.put(virtualFile, fileViewProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public List<PsiFile> getAllCachedFiles() {
|
||||
throw new UnsupportedOperationException("Method getAllCachedFiles is not yet implemented in " + getClass().getName());
|
||||
}
|
||||
}
|
||||
-146
@@ -1,146 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.kotlin.test.testFramework.mock;
|
||||
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class MockPsiDocumentManager extends PsiDocumentManager {
|
||||
@Override
|
||||
@Nullable
|
||||
public PsiFile getPsiFile(@NotNull Document document) {
|
||||
throw new UnsupportedOperationException("Method getPsiFile is not yet implemented in " + getClass().getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public PsiFile getCachedPsiFile(@NotNull Document document) {
|
||||
throw new UnsupportedOperationException("Method getCachedPsiFile is not yet implemented in " + getClass().getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Document getDocument(@NotNull PsiFile file) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Document getCachedDocument(@NotNull PsiFile file) {
|
||||
VirtualFile vFile = file.getViewProvider().getVirtualFile();
|
||||
return FileDocumentManager.getInstance().getCachedDocument(vFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commitAllDocuments() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performForCommittedDocument(@NotNull Document document, @NotNull Runnable action) {
|
||||
action.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commitDocument(@NotNull Document document) {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CharSequence getLastCommittedText(@NotNull Document document) {
|
||||
return document.getImmutableCharSequence();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLastCommittedStamp(@NotNull Document document) {
|
||||
return document.getModificationStamp();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Document getLastCommittedDocument(@NotNull PsiFile file) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Document[] getUncommittedDocuments() {
|
||||
throw new UnsupportedOperationException("Method getUncommittedDocuments is not yet implemented in " + getClass().getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUncommited(@NotNull Document document) {
|
||||
throw new UnsupportedOperationException("Method isUncommited is not yet implemented in " + getClass().getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCommitted(@NotNull Document document) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasUncommitedDocuments() {
|
||||
throw new UnsupportedOperationException("Method hasUncommitedDocuments is not yet implemented in " + getClass().getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commitAndRunReadAction(@NotNull Runnable runnable) {
|
||||
throw new UnsupportedOperationException("Method commitAndRunReadAction is not yet implemented in " + getClass().getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T commitAndRunReadAction(@NotNull Computable<T> computation) {
|
||||
throw new UnsupportedOperationException("Method commitAndRunReadAction is not yet implemented in " + getClass().getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(@NotNull Listener listener) {
|
||||
throw new UnsupportedOperationException("Method addListener is not yet implemented in " + getClass().getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener(@NotNull Listener listener) {
|
||||
throw new UnsupportedOperationException("Method removeListener is not yet implemented in " + getClass().getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDocumentBlockedByPsi(@NotNull Document doc) {
|
||||
throw new UnsupportedOperationException("Method isDocumentBlockedByPsi is not yet implemented in " + getClass().getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doPostponedOperationsAndUnblockDocument(@NotNull Document doc) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Method doPostponedOperationsAndUnblockDocument is not yet implemented in " + getClass().getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performWhenAllCommitted(@NotNull Runnable action) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reparseFiles(@NotNull Collection<VirtualFile> files, boolean includeOpenFiles) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
@@ -1,189 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test.testFramework.mock;
|
||||
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.PsiManagerEx;
|
||||
import com.intellij.psi.impl.PsiModificationTrackerImpl;
|
||||
import com.intellij.psi.impl.PsiTreeChangeEventImpl;
|
||||
import com.intellij.psi.impl.file.impl.FileManager;
|
||||
import com.intellij.psi.util.PsiModificationTracker;
|
||||
import gnu.trove.THashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class MockPsiManager extends PsiManagerEx {
|
||||
private final Project myProject;
|
||||
private final Map<VirtualFile,PsiDirectory> myDirectories = new THashMap<VirtualFile, PsiDirectory>();
|
||||
private MockFileManager myMockFileManager;
|
||||
private PsiModificationTrackerImpl myPsiModificationTracker;
|
||||
|
||||
public MockPsiManager(@NotNull Project project) {
|
||||
myProject = project;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void addPsiDirectory(VirtualFile file, PsiDirectory psiDirectory) {
|
||||
myDirectories.put(file, psiDirectory);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Project getProject() {
|
||||
return myProject;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsiFile findFile(@NotNull VirtualFile file) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public
|
||||
FileViewProvider findViewProvider(@NotNull VirtualFile file) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsiDirectory findDirectory(@NotNull VirtualFile file) {
|
||||
return myDirectories.get(file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areElementsEquivalent(PsiElement element1, PsiElement element2) {
|
||||
return Comparing.equal(element1, element2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reloadFromDisk(@NotNull PsiFile file) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPsiTreeChangeListener(@NotNull PsiTreeChangeListener listener) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPsiTreeChangeListener(@NotNull PsiTreeChangeListener listener, @NotNull Disposable parentDisposable) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePsiTreeChangeListener(@NotNull PsiTreeChangeListener listener) {
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public PsiModificationTracker getModificationTracker() {
|
||||
if (myPsiModificationTracker == null) {
|
||||
myPsiModificationTracker = new PsiModificationTrackerImpl(myProject);
|
||||
}
|
||||
return myPsiModificationTracker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startBatchFilesProcessingMode() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finishBatchFilesProcessingMode() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getUserData(@NotNull Key<T> key) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void putUserData(@NotNull Key<T> key, T value) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDisposed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dropResolveCaches() {
|
||||
getFileManager().cleanupForNextTest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInProject(@NotNull PsiElement element) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBatchFilesProcessingMode() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAssertOnFileLoading(@NotNull VirtualFile file) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeChange(boolean isPhysical) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterChange(boolean isPhysical) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerRunnableToRunOnChange(@NotNull Runnable runnable) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerRunnableToRunOnAnyChange(@NotNull Runnable runnable) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerRunnableToRunAfterAnyChange(@NotNull Runnable runnable) {
|
||||
throw new UnsupportedOperationException("Method registerRunnableToRunAfterAnyChange is not yet implemented in " + getClass().getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public FileManager getFileManager() {
|
||||
if (myMockFileManager == null) {
|
||||
myMockFileManager = new MockFileManager(this);
|
||||
}
|
||||
return myMockFileManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeChildRemoval(@NotNull PsiTreeChangeEventImpl event) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeChildReplacement(@NotNull PsiTreeChangeEventImpl event) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeChildAddition(@NotNull PsiTreeChangeEventImpl event) {
|
||||
}
|
||||
}
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.kotlin.test.testFramework.mock;
|
||||
|
||||
import com.intellij.openapi.components.RoamingType;
|
||||
import com.intellij.openapi.options.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class MockSchemesManagerFactory extends SchemesManagerFactory {
|
||||
private static final SchemesManager EMPTY = new EmptySchemesManager();
|
||||
|
||||
@Override
|
||||
protected <T extends Scheme, E extends ExternalizableScheme> SchemesManager<T, E> create(@NotNull String directoryName,
|
||||
@NotNull SchemeProcessor<E> processor,
|
||||
@NotNull RoamingType roamingType,
|
||||
@Nullable String presentableName) {
|
||||
//noinspection unchecked
|
||||
return EMPTY;
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test.testFramework
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.components.ComponentManager
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.picocontainer.MutablePicoContainer
|
||||
|
||||
fun <T> runWriteAction(action: () -> T): T {
|
||||
return ApplicationManager.getApplication().runWriteAction<T>(action)
|
||||
}
|
||||
@@ -1,558 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test.util;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope;
|
||||
import org.jetbrains.kotlin.types.KotlinType;
|
||||
import org.junit.Assert;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class DescriptorValidator {
|
||||
|
||||
public static void validate(@NotNull ValidationVisitor validationStrategy, DeclarationDescriptor descriptor) {
|
||||
DiagnosticCollectorForTests collector = new DiagnosticCollectorForTests();
|
||||
validate(validationStrategy, descriptor, collector);
|
||||
collector.done();
|
||||
}
|
||||
|
||||
public static void validate(
|
||||
@NotNull ValidationVisitor validator,
|
||||
@NotNull DeclarationDescriptor descriptor,
|
||||
@NotNull DiagnosticCollector collector
|
||||
) {
|
||||
RecursiveDescriptorProcessor.process(descriptor, collector, validator);
|
||||
}
|
||||
|
||||
private static void report(@NotNull DiagnosticCollector collector, @NotNull DeclarationDescriptor descriptor, @NotNull String message) {
|
||||
collector.report(new ValidationDiagnostic(descriptor, message));
|
||||
}
|
||||
|
||||
public interface DiagnosticCollector {
|
||||
void report(@NotNull ValidationDiagnostic diagnostic);
|
||||
}
|
||||
|
||||
public static class ValidationVisitor implements DeclarationDescriptorVisitor<Boolean, DiagnosticCollector> {
|
||||
public static ValidationVisitor errorTypesForbidden() {
|
||||
return new ValidationVisitor();
|
||||
}
|
||||
|
||||
public static ValidationVisitor errorTypesAllowed() {
|
||||
return new ValidationVisitor().allowErrorTypes();
|
||||
}
|
||||
|
||||
private boolean allowErrorTypes = false;
|
||||
private Predicate<DeclarationDescriptor> recursiveFilter = Predicates.alwaysTrue();
|
||||
|
||||
protected ValidationVisitor() {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ValidationVisitor withStepIntoFilter(@NotNull Predicate<DeclarationDescriptor> filter) {
|
||||
this.recursiveFilter = filter;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ValidationVisitor allowErrorTypes() {
|
||||
this.allowErrorTypes = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
protected void validateScope(DeclarationDescriptor scopeOwner, @NotNull MemberScope scope, @NotNull DiagnosticCollector collector) {
|
||||
for (DeclarationDescriptor descriptor : DescriptorUtils.getAllDescriptors(scope)) {
|
||||
if (recursiveFilter.apply(descriptor)) {
|
||||
descriptor.accept(new ScopeValidatorVisitor(collector), scope);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void validateType(
|
||||
@NotNull DeclarationDescriptor descriptor,
|
||||
@Nullable KotlinType type,
|
||||
@NotNull DiagnosticCollector collector
|
||||
) {
|
||||
if (type == null) {
|
||||
report(collector, descriptor, "No type");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!allowErrorTypes && type.isError()) {
|
||||
report(collector, descriptor, "Error type: " + type);
|
||||
return;
|
||||
}
|
||||
|
||||
validateScope(descriptor, type.getMemberScope(), collector);
|
||||
}
|
||||
|
||||
private void validateReturnType(CallableDescriptor descriptor, DiagnosticCollector collector) {
|
||||
validateType(descriptor, descriptor.getReturnType(), collector);
|
||||
}
|
||||
|
||||
private static void validateTypeParameters(DiagnosticCollector collector, List<TypeParameterDescriptor> parameters) {
|
||||
for (int i = 0; i < parameters.size(); i++) {
|
||||
TypeParameterDescriptor typeParameterDescriptor = parameters.get(i);
|
||||
if (typeParameterDescriptor.getIndex() != i) {
|
||||
report(collector, typeParameterDescriptor, "Incorrect index: " + typeParameterDescriptor.getIndex() + " but must be " + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void validateValueParameters(DiagnosticCollector collector, List<ValueParameterDescriptor> parameters) {
|
||||
for (int i = 0; i < parameters.size(); i++) {
|
||||
ValueParameterDescriptor valueParameterDescriptor = parameters.get(i);
|
||||
if (valueParameterDescriptor.getIndex() != i) {
|
||||
report(collector, valueParameterDescriptor, "Incorrect index: " + valueParameterDescriptor.getIndex() + " but must be " + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void validateTypes(
|
||||
DeclarationDescriptor descriptor,
|
||||
DiagnosticCollector collector,
|
||||
Collection<KotlinType> types
|
||||
) {
|
||||
for (KotlinType type : types) {
|
||||
validateType(descriptor, type, collector);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateCallable(CallableDescriptor descriptor, DiagnosticCollector collector) {
|
||||
validateReturnType(descriptor, collector);
|
||||
validateTypeParameters(collector, descriptor.getTypeParameters());
|
||||
validateValueParameters(collector, descriptor.getValueParameters());
|
||||
}
|
||||
|
||||
private static <T> void assertEquals(
|
||||
DeclarationDescriptor descriptor,
|
||||
DiagnosticCollector collector,
|
||||
String name,
|
||||
T expected,
|
||||
T actual
|
||||
) {
|
||||
if (!expected.equals(actual)) {
|
||||
report(collector, descriptor, "Wrong " + name + ": " + actual + " must be " + expected);
|
||||
}
|
||||
}
|
||||
|
||||
private static <T> void assertEqualTypes(
|
||||
DeclarationDescriptor descriptor,
|
||||
DiagnosticCollector collector,
|
||||
String name,
|
||||
KotlinType expected,
|
||||
KotlinType actual
|
||||
) {
|
||||
if (expected.isError() && actual.isError()) {
|
||||
assertEquals(descriptor, collector, name, expected.toString(), actual.toString());
|
||||
}
|
||||
else if (!expected.equals(actual)) {
|
||||
report(collector, descriptor, "Wrong " + name + ": " + actual + " must be " + expected);
|
||||
}
|
||||
}
|
||||
|
||||
private static void validateAccessor(
|
||||
PropertyDescriptor descriptor,
|
||||
DiagnosticCollector collector,
|
||||
PropertyAccessorDescriptor accessor,
|
||||
String name
|
||||
) {
|
||||
// TODO: fix the discrepancies in descriptor construction and enable these checks
|
||||
//assertEquals(accessor, collector, name + " visibility", descriptor.getVisibility(), accessor.getVisibility());
|
||||
//assertEquals(accessor, collector, name + " modality", descriptor.getModality(), accessor.getModality());
|
||||
assertEquals(accessor, collector, "corresponding property", descriptor, accessor.getCorrespondingProperty());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitPackageFragmentDescriptor(
|
||||
PackageFragmentDescriptor descriptor, DiagnosticCollector collector
|
||||
) {
|
||||
validateScope(descriptor, descriptor.getMemberScope(), collector);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitPackageViewDescriptor(PackageViewDescriptor descriptor, DiagnosticCollector collector) {
|
||||
if (!recursiveFilter.apply(descriptor)) return false;
|
||||
|
||||
validateScope(descriptor, descriptor.getMemberScope(), collector);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitVariableDescriptor(
|
||||
VariableDescriptor descriptor, DiagnosticCollector collector
|
||||
) {
|
||||
validateReturnType(descriptor, collector);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitFunctionDescriptor(
|
||||
FunctionDescriptor descriptor, DiagnosticCollector collector
|
||||
) {
|
||||
validateCallable(descriptor, collector);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitTypeParameterDescriptor(
|
||||
TypeParameterDescriptor descriptor, DiagnosticCollector collector
|
||||
) {
|
||||
validateTypes(descriptor, collector, descriptor.getUpperBounds());
|
||||
|
||||
validateType(descriptor, descriptor.getDefaultType(), collector);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitClassDescriptor(
|
||||
ClassDescriptor descriptor, DiagnosticCollector collector
|
||||
) {
|
||||
validateTypeParameters(collector, descriptor.getDeclaredTypeParameters());
|
||||
|
||||
Collection<KotlinType> supertypes = descriptor.getTypeConstructor().getSupertypes();
|
||||
if (supertypes.isEmpty() && descriptor.getKind() != ClassKind.INTERFACE
|
||||
&& !KotlinBuiltIns.isSpecialClassWithNoSupertypes(descriptor)) {
|
||||
report(collector, descriptor, "No supertypes for non-trait");
|
||||
}
|
||||
validateTypes(descriptor, collector, supertypes);
|
||||
|
||||
validateType(descriptor, descriptor.getDefaultType(), collector);
|
||||
|
||||
validateScope(descriptor, descriptor.getUnsubstitutedInnerClassesScope(), collector);
|
||||
|
||||
List<ConstructorDescriptor> primary = Lists.newArrayList();
|
||||
for (ConstructorDescriptor constructorDescriptor : descriptor.getConstructors()) {
|
||||
if (constructorDescriptor.isPrimary()) {
|
||||
primary.add(constructorDescriptor);
|
||||
}
|
||||
}
|
||||
if (primary.size() > 1) {
|
||||
report(collector, descriptor, "Many primary constructors: " + primary);
|
||||
}
|
||||
|
||||
ConstructorDescriptor primaryConstructor = descriptor.getUnsubstitutedPrimaryConstructor();
|
||||
if (primaryConstructor != null) {
|
||||
if (!descriptor.getConstructors().contains(primaryConstructor)) {
|
||||
report(collector, primaryConstructor,
|
||||
"Primary constructor not in getConstructors() result: " + descriptor.getConstructors());
|
||||
}
|
||||
}
|
||||
|
||||
ClassDescriptor companionObjectDescriptor = descriptor.getCompanionObjectDescriptor();
|
||||
if (companionObjectDescriptor != null && !companionObjectDescriptor.isCompanionObject()) {
|
||||
report(collector, companionObjectDescriptor, "Companion object should be marked as such");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitModuleDeclaration(
|
||||
ModuleDescriptor descriptor, DiagnosticCollector collector
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitConstructorDescriptor(
|
||||
ConstructorDescriptor constructorDescriptor, DiagnosticCollector collector
|
||||
) {
|
||||
visitFunctionDescriptor(constructorDescriptor, collector);
|
||||
|
||||
assertEqualTypes(constructorDescriptor, collector,
|
||||
"return type",
|
||||
constructorDescriptor.getContainingDeclaration().getDefaultType(),
|
||||
constructorDescriptor.getReturnType());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitScriptDescriptor(
|
||||
ScriptDescriptor scriptDescriptor, DiagnosticCollector collector
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitPropertyDescriptor(
|
||||
PropertyDescriptor descriptor, DiagnosticCollector collector
|
||||
) {
|
||||
validateCallable(descriptor, collector);
|
||||
|
||||
PropertyGetterDescriptor getter = descriptor.getGetter();
|
||||
if (getter != null) {
|
||||
assertEqualTypes(getter, collector, "getter return type", descriptor.getType(), getter.getReturnType());
|
||||
validateAccessor(descriptor, collector, getter, "getter");
|
||||
}
|
||||
|
||||
PropertySetterDescriptor setter = descriptor.getSetter();
|
||||
if (setter != null) {
|
||||
assertEquals(setter, collector, "setter parameter count", 1, setter.getValueParameters().size());
|
||||
assertEqualTypes(setter, collector, "setter parameter type", descriptor.getType(), setter.getValueParameters().get(0).getType());
|
||||
assertEquals(setter, collector, "corresponding property", descriptor, setter.getCorrespondingProperty());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitValueParameterDescriptor(
|
||||
ValueParameterDescriptor descriptor, DiagnosticCollector collector
|
||||
) {
|
||||
return visitVariableDescriptor(descriptor, collector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitPropertyGetterDescriptor(
|
||||
PropertyGetterDescriptor descriptor, DiagnosticCollector collector
|
||||
) {
|
||||
return visitFunctionDescriptor(descriptor, collector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitPropertySetterDescriptor(
|
||||
PropertySetterDescriptor descriptor, DiagnosticCollector collector
|
||||
) {
|
||||
return visitFunctionDescriptor(descriptor, collector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitReceiverParameterDescriptor(
|
||||
ReceiverParameterDescriptor descriptor, DiagnosticCollector collector
|
||||
) {
|
||||
validateType(descriptor, descriptor.getType(), collector);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class ScopeValidatorVisitor implements DeclarationDescriptorVisitor<Void, MemberScope> {
|
||||
private final DiagnosticCollector collector;
|
||||
|
||||
public ScopeValidatorVisitor(DiagnosticCollector collector) {
|
||||
this.collector = collector;
|
||||
}
|
||||
|
||||
private void report(DeclarationDescriptor expected, String message) {
|
||||
DescriptorValidator.report(collector, expected, message);
|
||||
}
|
||||
|
||||
private void assertFound(
|
||||
@NotNull MemberScope scope,
|
||||
@NotNull DeclarationDescriptor expected,
|
||||
@Nullable DeclarationDescriptor found,
|
||||
boolean shouldBeSame
|
||||
) {
|
||||
if (found == null) {
|
||||
report(expected, "Not found in " + scope);
|
||||
}
|
||||
if (shouldBeSame ? expected != found : !expected.equals(found)) {
|
||||
report(expected, "Lookup error in " + scope + ": " + found);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertFound(
|
||||
@NotNull MemberScope scope,
|
||||
@NotNull DeclarationDescriptor expected,
|
||||
@NotNull Collection<? extends DeclarationDescriptor> found
|
||||
) {
|
||||
if (!found.contains(expected)) {
|
||||
report(expected, "Not found in " + scope + ": " + found);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitPackageFragmentDescriptor(
|
||||
PackageFragmentDescriptor descriptor, MemberScope scope
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitPackageViewDescriptor(
|
||||
PackageViewDescriptor descriptor, MemberScope scope
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitVariableDescriptor(
|
||||
VariableDescriptor descriptor, MemberScope scope
|
||||
) {
|
||||
assertFound(scope, descriptor, scope.getContributedVariables(descriptor.getName(), NoLookupLocation.FROM_TEST));
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitFunctionDescriptor(
|
||||
FunctionDescriptor descriptor, MemberScope scope
|
||||
) {
|
||||
assertFound(scope, descriptor, scope.getContributedFunctions(descriptor.getName(), NoLookupLocation.FROM_TEST));
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitTypeParameterDescriptor(
|
||||
TypeParameterDescriptor descriptor, MemberScope scope
|
||||
) {
|
||||
assertFound(scope, descriptor, scope.getContributedClassifier(descriptor.getName(), NoLookupLocation.FROM_TEST), true);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitClassDescriptor(
|
||||
ClassDescriptor descriptor, MemberScope scope
|
||||
) {
|
||||
assertFound(scope, descriptor, scope.getContributedClassifier(descriptor.getName(), NoLookupLocation.FROM_TEST), true);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitModuleDeclaration(
|
||||
ModuleDescriptor descriptor, MemberScope scope
|
||||
) {
|
||||
report(descriptor, "Module found in scope: " + scope);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitConstructorDescriptor(
|
||||
ConstructorDescriptor descriptor, MemberScope scope
|
||||
) {
|
||||
report(descriptor, "Constructor found in scope: " + scope);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitScriptDescriptor(
|
||||
ScriptDescriptor descriptor, MemberScope scope
|
||||
) {
|
||||
report(descriptor, "Script found in scope: " + scope);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitPropertyDescriptor(
|
||||
PropertyDescriptor descriptor, MemberScope scope
|
||||
) {
|
||||
return visitVariableDescriptor(descriptor, scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitValueParameterDescriptor(
|
||||
ValueParameterDescriptor descriptor, MemberScope scope
|
||||
) {
|
||||
return visitVariableDescriptor(descriptor, scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitPropertyGetterDescriptor(
|
||||
PropertyGetterDescriptor descriptor, MemberScope scope
|
||||
) {
|
||||
report(descriptor, "Getter found in scope: " + scope);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitPropertySetterDescriptor(
|
||||
PropertySetterDescriptor descriptor, MemberScope scope
|
||||
) {
|
||||
report(descriptor, "Setter found in scope: " + scope);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitReceiverParameterDescriptor(
|
||||
ReceiverParameterDescriptor descriptor, MemberScope scope
|
||||
) {
|
||||
report(descriptor, "Receiver parameter found in scope: " + scope);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ValidationDiagnostic {
|
||||
|
||||
private final DeclarationDescriptor descriptor;
|
||||
private final String message;
|
||||
private final Throwable stackTrace;
|
||||
|
||||
private ValidationDiagnostic(@NotNull DeclarationDescriptor descriptor, @NotNull String message) {
|
||||
this.descriptor = descriptor;
|
||||
this.message = message;
|
||||
this.stackTrace = new Throwable();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DeclarationDescriptor getDescriptor() {
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Throwable getStackTrace() {
|
||||
return stackTrace;
|
||||
}
|
||||
|
||||
public void printStackTrace(@NotNull PrintStream out) {
|
||||
out.println(descriptor);
|
||||
out.println(message);
|
||||
stackTrace.printStackTrace(out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return descriptor + " > " + message;
|
||||
}
|
||||
}
|
||||
|
||||
private static class DiagnosticCollectorForTests implements DiagnosticCollector {
|
||||
private boolean errorsFound = false;
|
||||
|
||||
@Override
|
||||
public void report(@NotNull ValidationDiagnostic diagnostic) {
|
||||
diagnostic.printStackTrace(System.err);
|
||||
errorsFound = true;
|
||||
}
|
||||
|
||||
public void done() {
|
||||
if (errorsFound) {
|
||||
Assert.fail("Descriptor validation failed (see messages above)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private DescriptorValidator() {}
|
||||
}
|
||||
@@ -1,328 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test.util;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Lists;
|
||||
import kotlin.Unit;
|
||||
import kotlin.jvm.functions.Function1;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.jvm.compiler.ExpectedLoadErrorsUtil;
|
||||
import org.jetbrains.kotlin.name.FqName;
|
||||
import org.jetbrains.kotlin.renderer.*;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||
import org.jetbrains.kotlin.resolve.MemberComparator;
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.utils.Printer;
|
||||
import org.junit.Assert;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.jetbrains.kotlin.resolve.DescriptorUtils.isEnumEntry;
|
||||
import static org.jetbrains.kotlin.test.util.DescriptorValidator.ValidationVisitor.errorTypesForbidden;
|
||||
|
||||
public class RecursiveDescriptorComparator {
|
||||
|
||||
private static final DescriptorRenderer DEFAULT_RENDERER = DescriptorRenderer.Companion.withOptions(
|
||||
new Function1<DescriptorRendererOptions, Unit>() {
|
||||
@Override
|
||||
public Unit invoke(DescriptorRendererOptions options) {
|
||||
options.setWithDefinedIn(false);
|
||||
options.setExcludedAnnotationClasses(Collections.singleton(new FqName(ExpectedLoadErrorsUtil.ANNOTATION_CLASS_NAME)));
|
||||
options.setOverrideRenderingPolicy(OverrideRenderingPolicy.RENDER_OPEN_OVERRIDE);
|
||||
options.setIncludePropertyConstant(true);
|
||||
options.setClassifierNamePolicy(ClassifierNamePolicy.FULLY_QUALIFIED.INSTANCE);
|
||||
options.setVerbose(true);
|
||||
options.setModifiers(DescriptorRendererModifier.ALL);
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
public static final Configuration DONT_INCLUDE_METHODS_OF_OBJECT = new Configuration(false, false, false,
|
||||
Predicates.<DeclarationDescriptor>alwaysTrue(),
|
||||
errorTypesForbidden(), DEFAULT_RENDERER);
|
||||
public static final Configuration RECURSIVE = new Configuration(false, false, true,
|
||||
Predicates.<DeclarationDescriptor>alwaysTrue(),
|
||||
errorTypesForbidden(), DEFAULT_RENDERER);
|
||||
|
||||
public static final Configuration RECURSIVE_ALL = new Configuration(true, true, true,
|
||||
Predicates.<DeclarationDescriptor>alwaysTrue(),
|
||||
errorTypesForbidden(), DEFAULT_RENDERER);
|
||||
|
||||
public static final Predicate<DeclarationDescriptor> SKIP_BUILT_INS_PACKAGES = new Predicate<DeclarationDescriptor>() {
|
||||
@Override
|
||||
public boolean apply(DeclarationDescriptor descriptor) {
|
||||
if (descriptor instanceof PackageViewDescriptor) {
|
||||
return !KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME.equals(((PackageViewDescriptor) descriptor).getFqName());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
private static final ImmutableSet<String> KOTLIN_ANY_METHOD_NAMES = ImmutableSet.of("equals", "hashCode", "toString");
|
||||
|
||||
private final Configuration conf;
|
||||
|
||||
public RecursiveDescriptorComparator(@NotNull Configuration conf) {
|
||||
this.conf = conf;
|
||||
}
|
||||
|
||||
public String serializeRecursively(@NotNull DeclarationDescriptor declarationDescriptor) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
appendDeclarationRecursively(declarationDescriptor, DescriptorUtils.getContainingModule(declarationDescriptor),
|
||||
new Printer(result, 1), true);
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private void appendDeclarationRecursively(
|
||||
@NotNull DeclarationDescriptor descriptor,
|
||||
@NotNull ModuleDescriptor module,
|
||||
@NotNull Printer printer,
|
||||
boolean topLevel
|
||||
) {
|
||||
boolean isEnumEntry = isEnumEntry(descriptor);
|
||||
boolean isClassOrPackage =
|
||||
(descriptor instanceof ClassOrPackageFragmentDescriptor || descriptor instanceof PackageViewDescriptor) && !isEnumEntry;
|
||||
|
||||
if (isClassOrPackage && !topLevel) {
|
||||
printer.println();
|
||||
}
|
||||
|
||||
boolean isPrimaryConstructor = descriptor instanceof ConstructorDescriptor && ((ConstructorDescriptor) descriptor).isPrimary();
|
||||
printer.print(isPrimaryConstructor && conf.checkPrimaryConstructors ? "/*primary*/ " : "", conf.renderer.render(descriptor));
|
||||
|
||||
if (isClassOrPackage) {
|
||||
if (!topLevel) {
|
||||
printer.printlnWithNoIndent(" {").pushIndent();
|
||||
}
|
||||
else {
|
||||
printer.println();
|
||||
printer.println();
|
||||
}
|
||||
|
||||
if (descriptor instanceof ClassDescriptor) {
|
||||
ClassDescriptor klass = (ClassDescriptor) descriptor;
|
||||
appendSubDescriptors(descriptor, module,
|
||||
klass.getDefaultType().getMemberScope(), klass.getConstructors(), printer);
|
||||
MemberScope staticScope = klass.getStaticScope();
|
||||
if (!DescriptorUtils.getAllDescriptors(staticScope).isEmpty()) {
|
||||
printer.println();
|
||||
printer.println("// Static members");
|
||||
appendSubDescriptors(descriptor, module, staticScope, Collections.<DeclarationDescriptor>emptyList(), printer);
|
||||
}
|
||||
}
|
||||
else if (descriptor instanceof PackageFragmentDescriptor) {
|
||||
appendSubDescriptors(descriptor, module,
|
||||
((PackageFragmentDescriptor) descriptor).getMemberScope(),
|
||||
Collections.<DeclarationDescriptor>emptyList(), printer);
|
||||
}
|
||||
else if (descriptor instanceof PackageViewDescriptor) {
|
||||
appendSubDescriptors(descriptor, module,
|
||||
((PackageViewDescriptor) descriptor).getMemberScope(),
|
||||
Collections.<DeclarationDescriptor>emptyList(), printer);
|
||||
}
|
||||
|
||||
if (!topLevel) {
|
||||
printer.popIndent().println("}");
|
||||
}
|
||||
}
|
||||
else if (conf.checkPropertyAccessors && descriptor instanceof PropertyDescriptor) {
|
||||
printer.printlnWithNoIndent();
|
||||
printer.pushIndent();
|
||||
PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor;
|
||||
PropertyGetterDescriptor getter = propertyDescriptor.getGetter();
|
||||
if (getter != null) {
|
||||
printer.println(conf.renderer.render(getter));
|
||||
}
|
||||
|
||||
PropertySetterDescriptor setter = propertyDescriptor.getSetter();
|
||||
if (setter != null) {
|
||||
printer.println(conf.renderer.render(setter));
|
||||
}
|
||||
|
||||
printer.popIndent();
|
||||
}
|
||||
else {
|
||||
printer.printlnWithNoIndent();
|
||||
}
|
||||
|
||||
if (isEnumEntry) {
|
||||
printer.println();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldSkip(@NotNull DeclarationDescriptor subDescriptor) {
|
||||
boolean isFunctionFromAny = subDescriptor.getContainingDeclaration() instanceof ClassDescriptor
|
||||
&& subDescriptor instanceof FunctionDescriptor
|
||||
&& KOTLIN_ANY_METHOD_NAMES.contains(subDescriptor.getName().asString());
|
||||
return (isFunctionFromAny && !conf.includeMethodsOfKotlinAny) || !conf.recursiveFilter.apply(subDescriptor);
|
||||
}
|
||||
|
||||
private void appendSubDescriptors(
|
||||
@NotNull DeclarationDescriptor descriptor,
|
||||
@NotNull ModuleDescriptor module,
|
||||
@NotNull MemberScope memberScope,
|
||||
@NotNull Collection<? extends DeclarationDescriptor> extraSubDescriptors,
|
||||
@NotNull Printer printer
|
||||
) {
|
||||
if (!module.equals(DescriptorUtils.getContainingModule(descriptor))) {
|
||||
printer.println(String.format("// -- Module: %s --", DescriptorUtils.getContainingModule(descriptor).getName()));
|
||||
return;
|
||||
}
|
||||
|
||||
List<DeclarationDescriptor> subDescriptors = Lists.newArrayList();
|
||||
|
||||
subDescriptors.addAll(DescriptorUtils.getAllDescriptors(memberScope));
|
||||
subDescriptors.addAll(extraSubDescriptors);
|
||||
|
||||
Collections.sort(subDescriptors, MemberComparator.INSTANCE);
|
||||
|
||||
for (DeclarationDescriptor subDescriptor : subDescriptors) {
|
||||
if (!shouldSkip(subDescriptor)) {
|
||||
appendDeclarationRecursively(subDescriptor, module, printer, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void compareDescriptorWithFile(
|
||||
@NotNull DeclarationDescriptor actual,
|
||||
@NotNull Configuration configuration,
|
||||
@NotNull File txtFile
|
||||
) {
|
||||
doCompareDescriptors(null, actual, configuration, txtFile);
|
||||
}
|
||||
|
||||
public static void compareDescriptors(
|
||||
@NotNull DeclarationDescriptor expected,
|
||||
@NotNull DeclarationDescriptor actual,
|
||||
@NotNull Configuration configuration,
|
||||
@Nullable File txtFile
|
||||
) {
|
||||
if (expected == actual) {
|
||||
throw new IllegalArgumentException("Don't invoke this method with expected == actual." +
|
||||
"Invoke compareDescriptorWithFile() instead.");
|
||||
}
|
||||
doCompareDescriptors(expected, actual, configuration, txtFile);
|
||||
}
|
||||
|
||||
public static void validateAndCompareDescriptorWithFile(
|
||||
@NotNull DeclarationDescriptor actual,
|
||||
@NotNull Configuration configuration,
|
||||
@NotNull File txtFile
|
||||
) {
|
||||
DescriptorValidator.validate(configuration.validationStrategy, actual);
|
||||
compareDescriptorWithFile(actual, configuration, txtFile);
|
||||
}
|
||||
|
||||
public static void validateAndCompareDescriptors(
|
||||
@NotNull DeclarationDescriptor expected,
|
||||
@NotNull DeclarationDescriptor actual,
|
||||
@NotNull Configuration configuration,
|
||||
@Nullable File txtFile
|
||||
) {
|
||||
DescriptorValidator.validate(configuration.validationStrategy, expected);
|
||||
DescriptorValidator.validate(configuration.validationStrategy, actual);
|
||||
compareDescriptors(expected, actual, configuration, txtFile);
|
||||
}
|
||||
|
||||
private static void doCompareDescriptors(
|
||||
@Nullable DeclarationDescriptor expected,
|
||||
@NotNull DeclarationDescriptor actual,
|
||||
@NotNull Configuration configuration,
|
||||
@Nullable File txtFile
|
||||
) {
|
||||
RecursiveDescriptorComparator comparator = new RecursiveDescriptorComparator(configuration);
|
||||
|
||||
String actualSerialized = comparator.serializeRecursively(actual);
|
||||
|
||||
if (expected != null) {
|
||||
String expectedSerialized = comparator.serializeRecursively(expected);
|
||||
|
||||
Assert.assertEquals("Expected and actual descriptors differ", expectedSerialized, actualSerialized);
|
||||
}
|
||||
|
||||
if (txtFile != null) {
|
||||
KotlinTestUtils.assertEqualsToFile(txtFile, actualSerialized);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Configuration {
|
||||
private final boolean checkPrimaryConstructors;
|
||||
private final boolean checkPropertyAccessors;
|
||||
private final boolean includeMethodsOfKotlinAny;
|
||||
private final Predicate<DeclarationDescriptor> recursiveFilter;
|
||||
private final DescriptorRenderer renderer;
|
||||
|
||||
private final DescriptorValidator.ValidationVisitor validationStrategy;
|
||||
|
||||
public Configuration(
|
||||
boolean checkPrimaryConstructors,
|
||||
boolean checkPropertyAccessors,
|
||||
boolean includeMethodsOfKotlinAny,
|
||||
Predicate<DeclarationDescriptor> recursiveFilter,
|
||||
DescriptorValidator.ValidationVisitor validationStrategy,
|
||||
DescriptorRenderer renderer
|
||||
) {
|
||||
this.checkPrimaryConstructors = checkPrimaryConstructors;
|
||||
this.checkPropertyAccessors = checkPropertyAccessors;
|
||||
this.includeMethodsOfKotlinAny = includeMethodsOfKotlinAny;
|
||||
this.recursiveFilter = recursiveFilter;
|
||||
this.validationStrategy = validationStrategy;
|
||||
this.renderer = renderer;
|
||||
}
|
||||
|
||||
public Configuration filterRecursion(@NotNull Predicate<DeclarationDescriptor> stepIntoFilter) {
|
||||
return new Configuration(checkPrimaryConstructors, checkPropertyAccessors, includeMethodsOfKotlinAny, stepIntoFilter,
|
||||
validationStrategy.withStepIntoFilter(stepIntoFilter), renderer);
|
||||
}
|
||||
|
||||
public Configuration checkPrimaryConstructors(boolean checkPrimaryConstructors) {
|
||||
return new Configuration(checkPrimaryConstructors, checkPropertyAccessors, includeMethodsOfKotlinAny, recursiveFilter,
|
||||
validationStrategy, renderer);
|
||||
}
|
||||
|
||||
public Configuration checkPropertyAccessors(boolean checkPropertyAccessors) {
|
||||
return new Configuration(checkPrimaryConstructors, checkPropertyAccessors, includeMethodsOfKotlinAny, recursiveFilter,
|
||||
validationStrategy, renderer);
|
||||
}
|
||||
|
||||
public Configuration includeMethodsOfKotlinAny(boolean includeMethodsOfKotlinAny) {
|
||||
return new Configuration(checkPrimaryConstructors, checkPropertyAccessors, includeMethodsOfKotlinAny, recursiveFilter,
|
||||
validationStrategy, renderer);
|
||||
}
|
||||
|
||||
public Configuration withValidationStrategy(@NotNull DescriptorValidator.ValidationVisitor validationStrategy) {
|
||||
return new Configuration(checkPrimaryConstructors, checkPropertyAccessors, includeMethodsOfKotlinAny, recursiveFilter,
|
||||
validationStrategy, renderer);
|
||||
}
|
||||
|
||||
public Configuration withRenderer(@NotNull DescriptorRenderer renderer) {
|
||||
return new Configuration(checkPrimaryConstructors, checkPropertyAccessors, includeMethodsOfKotlinAny, recursiveFilter,
|
||||
validationStrategy, renderer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,148 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test.util;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.name.FqName;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class RecursiveDescriptorProcessor {
|
||||
|
||||
public static <D> boolean process(
|
||||
@NotNull DeclarationDescriptor descriptor,
|
||||
D data,
|
||||
@NotNull DeclarationDescriptorVisitor<Boolean, D> visitor
|
||||
) {
|
||||
return descriptor.accept(new RecursiveVisitor<D>(visitor), data);
|
||||
}
|
||||
|
||||
private static class RecursiveVisitor<D> implements DeclarationDescriptorVisitor<Boolean, D> {
|
||||
|
||||
private final DeclarationDescriptorVisitor<Boolean, D> worker;
|
||||
|
||||
private RecursiveVisitor(@NotNull DeclarationDescriptorVisitor<Boolean, D> worker) {
|
||||
this.worker = worker;
|
||||
}
|
||||
|
||||
private boolean visitChildren(Collection<? extends DeclarationDescriptor> descriptors, D data) {
|
||||
for (DeclarationDescriptor descriptor : descriptors) {
|
||||
if (!descriptor.accept(this, data)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean visitChildren(@Nullable DeclarationDescriptor descriptor, D data) {
|
||||
if (descriptor == null) return true;
|
||||
|
||||
return descriptor.accept(this, data);
|
||||
}
|
||||
|
||||
private boolean applyWorker(@NotNull DeclarationDescriptor descriptor, D data) {
|
||||
return descriptor.accept(worker, data);
|
||||
}
|
||||
|
||||
private boolean processCallable(CallableDescriptor descriptor, D data) {
|
||||
return applyWorker(descriptor, data)
|
||||
&& visitChildren(descriptor.getTypeParameters(), data)
|
||||
&& visitChildren(descriptor.getExtensionReceiverParameter(), data)
|
||||
&& visitChildren(descriptor.getValueParameters(), data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitPackageFragmentDescriptor(PackageFragmentDescriptor descriptor, D data) {
|
||||
return applyWorker(descriptor, data)
|
||||
&& visitChildren(DescriptorUtils.getAllDescriptors(descriptor.getMemberScope()), data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitPackageViewDescriptor(PackageViewDescriptor descriptor, D data) {
|
||||
return applyWorker(descriptor, data)
|
||||
&& visitChildren(DescriptorUtils.getAllDescriptors(descriptor.getMemberScope()), data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitVariableDescriptor(VariableDescriptor descriptor, D data) {
|
||||
return processCallable(descriptor, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitPropertyDescriptor(PropertyDescriptor descriptor, D data) {
|
||||
return processCallable(descriptor, data)
|
||||
&& visitChildren(descriptor.getGetter(), data)
|
||||
&& visitChildren(descriptor.getSetter(), data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitFunctionDescriptor(FunctionDescriptor descriptor, D data) {
|
||||
return processCallable(descriptor, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitTypeParameterDescriptor(TypeParameterDescriptor descriptor, D data) {
|
||||
return applyWorker(descriptor, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitClassDescriptor(ClassDescriptor descriptor, D data) {
|
||||
return applyWorker(descriptor, data)
|
||||
&& visitChildren(descriptor.getThisAsReceiverParameter(), data)
|
||||
&& visitChildren(descriptor.getConstructors(), data)
|
||||
&& visitChildren(descriptor.getTypeConstructor().getParameters(), data)
|
||||
&& visitChildren(DescriptorUtils.getAllDescriptors(descriptor.getDefaultType().getMemberScope()), data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitModuleDeclaration(ModuleDescriptor descriptor, D data) {
|
||||
return applyWorker(descriptor, data)
|
||||
&& visitChildren(descriptor.getPackage(FqName.ROOT), data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitConstructorDescriptor(ConstructorDescriptor constructorDescriptor, D data) {
|
||||
return visitFunctionDescriptor(constructorDescriptor, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitScriptDescriptor(ScriptDescriptor scriptDescriptor, D data) {
|
||||
return visitClassDescriptor(scriptDescriptor, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitValueParameterDescriptor(ValueParameterDescriptor descriptor, D data) {
|
||||
return visitVariableDescriptor(descriptor, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitPropertyGetterDescriptor(PropertyGetterDescriptor descriptor, D data) {
|
||||
return visitFunctionDescriptor(descriptor, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitPropertySetterDescriptor(PropertySetterDescriptor descriptor, D data) {
|
||||
return visitFunctionDescriptor(descriptor, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitReceiverParameterDescriptor(ReceiverParameterDescriptor descriptor, D data) {
|
||||
return applyWorker(descriptor, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test.util
|
||||
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.intellij.util.SmartFMap
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtPackageDirective
|
||||
import org.jetbrains.kotlin.psi.KtTreeVisitorVoid
|
||||
|
||||
fun String.trimTrailingWhitespacesAndAddNewlineAtEOF(): String =
|
||||
this.split('\n').map { it.trimEnd() }.joinToString(separator = "\n").let {
|
||||
result -> if (result.endsWith("\n")) result else result + "\n"
|
||||
}
|
||||
|
||||
fun PsiFile.findElementByCommentPrefix(commentText: String): PsiElement? =
|
||||
findElementsByCommentPrefix(commentText).keys.singleOrNull()
|
||||
|
||||
fun PsiFile.findElementsByCommentPrefix(prefix: String): Map<PsiElement, String> {
|
||||
var result = SmartFMap.emptyMap<PsiElement, String>()
|
||||
accept(
|
||||
object : KtTreeVisitorVoid() {
|
||||
override fun visitComment(comment: PsiComment) {
|
||||
val commentText = comment.text
|
||||
if (commentText.startsWith(prefix)) {
|
||||
val parent = comment.parent
|
||||
val elementToAdd = when (parent) {
|
||||
is KtDeclaration -> parent
|
||||
is PsiMember -> parent
|
||||
else -> PsiTreeUtil.skipSiblingsForward(
|
||||
comment,
|
||||
PsiWhiteSpace::class.java, PsiComment::class.java, KtPackageDirective::class.java
|
||||
)
|
||||
} as? PsiElement ?: return
|
||||
|
||||
result = result.plus(elementToAdd, commentText.substring(prefix.length).trim())
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
return result
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.tests.di
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.container.*
|
||||
import org.jetbrains.kotlin.context.ModuleContext
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.frontend.di.configureModule
|
||||
import org.jetbrains.kotlin.incremental.components.LookupTracker
|
||||
import org.jetbrains.kotlin.resolve.DescriptorResolver
|
||||
import org.jetbrains.kotlin.resolve.FunctionDescriptorResolver
|
||||
import org.jetbrains.kotlin.resolve.TypeResolver
|
||||
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
|
||||
import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices
|
||||
import org.jetbrains.kotlin.types.expressions.FakeCallResolver
|
||||
|
||||
fun createContainerForTests(project: Project, module: ModuleDescriptor): ContainerForTests {
|
||||
return ContainerForTests(createContainer("Tests") {
|
||||
configureModule(ModuleContext(module, project), JvmPlatform)
|
||||
useInstance(LookupTracker.DO_NOTHING)
|
||||
useImpl<ExpressionTypingServices>()
|
||||
})
|
||||
}
|
||||
|
||||
class ContainerForTests(container: StorageComponentContainer) {
|
||||
val descriptorResolver: DescriptorResolver by container
|
||||
val functionDescriptorResolver: FunctionDescriptorResolver by container
|
||||
val typeResolver: TypeResolver by container
|
||||
val fakeCallResolver: FakeCallResolver by container
|
||||
val expressionTypingServices: ExpressionTypingServices by container
|
||||
}
|
||||
@@ -17,11 +17,8 @@
|
||||
package org.jetbrains.kotlin.types
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.resolve.scopes.BaseLexicalScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.memberScopeAsImportingScope
|
||||
import org.jetbrains.kotlin.utils.Printer
|
||||
|
||||
fun KotlinBuiltIns.builtInPackageAsLexicalScope()
|
||||
= LexicalScope.empty(builtInsPackageScope.memberScopeAsImportingScope(), builtInsModule)
|
||||
Reference in New Issue
Block a user