Move common test parts to tests-common. Minify test jar dependencies

This commit is contained in:
Mikhael Bogdanov
2017-11-27 16:23:13 +01:00
parent acbf53c42e
commit f529069077
43 changed files with 16 additions and 21 deletions
@@ -0,0 +1,63 @@
/*
* Copyright 2010-2017 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.psi.PsiClass
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.asJava.finder.JavaElementFinder
import org.jetbrains.kotlin.checkers.KotlinMultiFileTestWithJava
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.test.ConfigurationKind
import org.jetbrains.kotlin.test.KotlinTestUtils
import java.io.File
abstract class AbstractCompilerLightClassTest : KotlinMultiFileTestWithJava<Void, Void>() {
override fun getConfigurationKind(): ConfigurationKind = ConfigurationKind.ALL
override fun isKotlinSourceRootNeeded(): Boolean = true
override fun doMultiFileTest(file: File, modules: MutableMap<String, ModuleAndDependencies>, files: MutableList<Void>) {
val environment = createEnvironment(file)
val expectedFile = KotlinTestUtils.replaceExtension(file, "java")
LightClassTestCommon.testLightClass(
expectedFile,
file,
{ fqname -> findLightClass(environment, fqname) },
LightClassTestCommon::removeEmptyDefaultImpls
)
}
override fun createTestModule(name: String): Void? = null
override fun createTestFile(module: Void?, fileName: String, text: String, directives: Map<String, String>): Void? = null
companion object {
fun findLightClass(environment: KotlinCoreEnvironment, fqname: String): PsiClass? {
KotlinTestUtils.resolveAllKotlinFiles(environment)
val lightCLassForScript = LightClassGenerationSupport
.getInstance(environment.project)
.getScriptClasses(FqName(fqname), GlobalSearchScope.allScope(environment.project))
.firstOrNull()
return lightCLassForScript ?: JavaElementFinder
.getInstance(environment.project)
.findClass(fqname, GlobalSearchScope.allScope(environment.project))
}
}
}
@@ -0,0 +1,120 @@
/*
* 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.cfg;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.cfg.pseudocode.PseudocodeImpl;
import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction;
import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionImpl;
import org.jetbrains.kotlin.resolve.BindingContext;
import java.util.*;
public abstract class AbstractControlFlowTest extends AbstractPseudocodeTest {
@Override
protected void dumpInstructions(
@NotNull PseudocodeImpl pseudocode,
@NotNull StringBuilder out,
@NotNull BindingContext bindingContext
) {
int nextInstructionsColumnWidth = countNextInstructionsColumnWidth(pseudocode.getInstructionsIncludingDeadCode());
dumpInstructions(pseudocode, out, (instruction, next, prev) -> {
StringBuilder result = new StringBuilder();
Collection<Instruction> nextInstructions = instruction.getNextInstructions();
if (!sameContents(next, nextInstructions)) {
result.append(" NEXT:").append(
String.format("%1$-" + nextInstructionsColumnWidth + "s", formatInstructionList(nextInstructions)));
}
Collection<Instruction> previousInstructions = instruction.getPreviousInstructions();
if (!sameContents(prev, previousInstructions)) {
result.append(" PREV:").append(formatInstructionList(previousInstructions));
}
return result.toString();
});
}
private static String formatInstructionList(Collection<Instruction> instructions) {
StringBuilder sb = new StringBuilder();
sb.append('[');
for (Iterator<Instruction> iterator = instructions.iterator(); iterator.hasNext(); ) {
Instruction instruction = iterator.next();
String instructionText = instruction.toString();
String[] parts = instructionText.split("\n");
if (parts.length > 1) {
StringBuilder instructionSb = new StringBuilder();
for (String part : parts) {
instructionSb.append(part.trim()).append(' ');
}
if (instructionSb.toString().length() > 30) {
sb.append(instructionSb.substring(0, 28)).append("..)");
}
else {
sb.append(instructionSb);
}
}
else {
sb.append(instruction);
}
if (iterator.hasNext()) {
sb.append(", ");
}
}
sb.append(']');
return sb.toString();
}
private static int countNextInstructionsColumnWidth(List<Instruction> instructions) {
int maxWidth = 0;
for (Instruction instruction : instructions) {
String instructionListText = formatInstructionList(instruction.getNextInstructions());
if (instructionListText.length() > maxWidth) {
maxWidth = instructionListText.length();
}
}
return maxWidth;
}
private static boolean sameContents(@Nullable Instruction natural, Collection<Instruction> actual) {
if (natural == null) {
return actual.isEmpty();
}
return Collections.singleton(natural).equals(new HashSet<>(actual));
}
@Override
protected void checkPseudocode(PseudocodeImpl pseudocode) {
//check edges directions
Collection<Instruction> instructions = pseudocode.getInstructionsIncludingDeadCode();
for (Instruction instruction : instructions) {
if (!((InstructionImpl)instruction).getMarkedAsDead()) {
for (Instruction nextInstruction : instruction.getNextInstructions()) {
assertTrue("instruction '" + instruction + "' has '" + nextInstruction + "' among next instructions list, but not vice versa",
nextInstruction.getPreviousInstructions().contains(instruction));
}
for (Instruction prevInstruction : instruction.getPreviousInstructions()) {
assertTrue("instruction '" + instruction + "' has '" + prevInstruction + "' among previous instructions list, but not vice versa",
prevInstruction.getNextInstructions().contains(instruction));
}
}
}
}
}
@@ -0,0 +1,116 @@
/*
* 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.cfg;
import com.google.common.collect.Lists;
import com.intellij.openapi.util.text.StringUtil;
import javaslang.Tuple2;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.cfg.pseudocode.PseudocodeImpl;
import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction;
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.Edges;
import org.jetbrains.kotlin.descriptors.VariableDescriptor;
import org.jetbrains.kotlin.resolve.BindingContext;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public abstract class AbstractDataFlowTest extends AbstractPseudocodeTest {
@Override
public void dumpInstructions(
@NotNull PseudocodeImpl pseudocode,
@NotNull StringBuilder out,
@NotNull BindingContext bindingContext
) {
PseudocodeVariablesData pseudocodeVariablesData = new PseudocodeVariablesData(pseudocode.getRootPseudocode(), bindingContext);
Map<Instruction, Edges<ReadOnlyInitControlFlowInfo>> variableInitializers =
pseudocodeVariablesData.getVariableInitializers();
Map<Instruction, Edges<ReadOnlyControlFlowInfo<VariableUseState>>> useStatusData =
pseudocodeVariablesData.getVariableUseStatusData();
String initPrefix = " INIT:";
String usePrefix = " USE:";
int initializersColumnWidth = countDataColumnWidth(initPrefix, pseudocode.getInstructionsIncludingDeadCode(), variableInitializers,
pseudocodeVariablesData);
dumpInstructions(pseudocode, out, (instruction, next, prev) -> {
StringBuilder result = new StringBuilder();
Edges<ReadOnlyInitControlFlowInfo> initializersEdges = variableInitializers.get(instruction);
Edges<ReadOnlyInitControlFlowInfo> previousInitializersEdges = variableInitializers.get(prev);
String initializersData = "";
if (initializersEdges != null && !initializersEdges.equals(previousInitializersEdges)) {
initializersData = dumpEdgesData(initPrefix, initializersEdges, pseudocodeVariablesData);
}
result.append(String.format("%1$-" + initializersColumnWidth + "s", initializersData));
Edges<ReadOnlyControlFlowInfo<VariableUseState>> useStatusEdges = useStatusData.get(instruction);
Edges<ReadOnlyControlFlowInfo<VariableUseState>> nextUseStatusEdges = useStatusData.get(next);
if (useStatusEdges != null && !useStatusEdges.equals(nextUseStatusEdges)) {
result.append(dumpEdgesData(usePrefix, useStatusEdges, pseudocodeVariablesData));
}
return result.toString();
});
}
private static int countDataColumnWidth(
@NotNull String prefix,
@NotNull List<Instruction> instructions,
@NotNull Map<Instruction, Edges<ReadOnlyInitControlFlowInfo>> data,
@NotNull PseudocodeVariablesData variablesData
) {
int maxWidth = 0;
for (Instruction instruction : instructions) {
Edges<ReadOnlyInitControlFlowInfo> edges = data.get(instruction);
if (edges == null) continue;
int length = dumpEdgesData(prefix, edges, variablesData).length();
if (maxWidth < length) {
maxWidth = length;
}
}
return maxWidth;
}
@NotNull
private static <S, I extends ReadOnlyControlFlowInfo<S>> String dumpEdgesData(
String prefix,
@NotNull Edges<I> edges,
@NotNull PseudocodeVariablesData variablesData
) {
return prefix +
" in: " + renderVariableMap(edges.getIncoming().asMap(), variablesData) +
" out: " + renderVariableMap(edges.getOutgoing().asMap(), variablesData);
}
private static <S> String renderVariableMap(
javaslang.collection.Map<VariableDescriptor, S> map,
PseudocodeVariablesData variablesData
) {
List<String> result = Lists.newArrayList();
for (Tuple2<VariableDescriptor, S> entry : map) {
VariableDescriptor variable = entry._1;
S state = entry._2;
if (variablesData.isVariableWithTrivialInitializer(variable)) continue;
result.add(variable.getName() + "=" + state);
}
Collections.sort(result);
return "{" + StringUtil.join(result, ", ") + "}";
}
}
@@ -0,0 +1,38 @@
/*
* 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.cfg;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.checkers.AbstractDiagnosticsTest;
import org.jetbrains.kotlin.test.ConfigurationKind;
import org.jetbrains.kotlin.test.TestJdkKind;
import java.io.File;
public abstract class AbstractDiagnosticsWithModifiedMockJdkTest extends AbstractDiagnosticsTest {
@NotNull
@Override
protected ConfigurationKind getConfigurationKind() {
return ConfigurationKind.ALL;
}
@NotNull
@Override
protected TestJdkKind getTestJdkKind(@NotNull File file) {
return TestJdkKind.MODIFIED_MOCK_JDK;
}
}
@@ -0,0 +1,99 @@
/*
* 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.cfg
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.cfg.pseudocode.PseudoValue
import org.jetbrains.kotlin.cfg.pseudocode.PseudocodeImpl
import org.jetbrains.kotlin.cfg.pseudocode.TypePredicate
import org.jetbrains.kotlin.cfg.pseudocode.getExpectedTypePredicate
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.InstructionWithValue
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtTreeVisitorVoid
import org.jetbrains.kotlin.resolve.BindingContext
import java.util.*
abstract class AbstractPseudoValueTest : AbstractPseudocodeTest() {
override fun dumpInstructions(pseudocode: PseudocodeImpl, out: StringBuilder, bindingContext: BindingContext) {
val expectedTypePredicateMap = HashMap<PseudoValue, TypePredicate>()
fun getElementToValueMap(pseudocode: PseudocodeImpl): Map<KtElement, PseudoValue> {
val elementToValues = LinkedHashMap<KtElement, PseudoValue>()
pseudocode.correspondingElement.accept(object : KtTreeVisitorVoid() {
override fun visitKtElement(element: KtElement) {
super.visitKtElement(element)
val value = pseudocode.getElementValue(element)
if (value != null) {
elementToValues.put(element, value)
}
}
})
return elementToValues
}
fun elementText(element: KtElement?): String =
element?.text?.replace("\\s+".toRegex(), " ") ?: ""
fun valueDecl(value: PseudoValue): String {
val typePredicate = expectedTypePredicateMap.getOrPut(value) {
getExpectedTypePredicate(value, bindingContext, DefaultBuiltIns.Instance)
}
return "${value.debugName}: $typePredicate"
}
fun valueDescription(element: KtElement?, value: PseudoValue): String {
return when {
value.element != element -> "COPY"
else -> value.createdAt?.let { "NEW: $it" } ?: ""
}
}
val elementToValues = getElementToValueMap(pseudocode)
val unboundValues = pseudocode.instructions
.mapNotNull { (it as? InstructionWithValue)?.outputValue }
.filter { it.element == null }
.sortedBy { it.debugName }
val allValues = elementToValues.values + unboundValues
if (allValues.isEmpty()) return
val valueDescriptions = LinkedHashMap<Pair<PseudoValue, KtElement?>, String>()
for (value in unboundValues) {
valueDescriptions[value to null] = valueDescription(null, value)
}
for ((element, value) in elementToValues.entries) {
valueDescriptions[value to element] = valueDescription(element, value)
}
val elementColumnWidth = elementToValues.keys.map { elementText(it).length }.max() ?: 1
val valueColumnWidth = allValues.map { valueDecl(it).length }.max()!!
val valueDescColumnWidth = valueDescriptions.values.map { it.length }.max()!!
for ((ve, description) in valueDescriptions.entries) {
val (value, element) = ve
val line =
"%1$-${elementColumnWidth}s".format(elementText(element)) +
" " +
"%1$-${valueColumnWidth}s".format(valueDecl(value)) +
" " +
"%1$-${valueDescColumnWidth}s".format(description)
out.appendln(line.trimEnd())
}
}
override fun getDataFileExtension(): String? = "values"
}
@@ -0,0 +1,249 @@
/*
* Copyright 2010-2017 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.cfg;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.SetMultimap;
import com.google.common.collect.Sets;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import kotlin.jvm.functions.Function3;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.analyzer.AnalysisResult;
import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode;
import org.jetbrains.kotlin.cfg.pseudocode.PseudocodeImpl;
import org.jetbrains.kotlin.cfg.pseudocode.PseudocodeLabel;
import org.jetbrains.kotlin.cfg.pseudocode.PseudocodeUtil;
import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction;
import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionImpl;
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.LocalFunctionDeclarationInstruction;
import org.jetbrains.kotlin.checkers.CompilerTestLanguageVersionSettingsKt;
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.test.ConfigurationKind;
import org.jetbrains.kotlin.test.KotlinTestUtils;
import org.jetbrains.kotlin.test.KotlinTestWithEnvironmentManagement;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Set;
public abstract class AbstractPseudocodeTest extends KotlinTestWithEnvironmentManagement {
protected void doTestWithStdLib(String fileName) throws Exception {
doTestWithEnvironment(fileName, createEnvironmentWithMockJdk(ConfigurationKind.NO_KOTLIN_REFLECT));
}
protected void doTest(String fileName) throws Exception {
doTestWithEnvironment(fileName, createEnvironmentWithMockJdk(ConfigurationKind.JDK_ONLY));
}
private void doTestWithEnvironment(String fileName, KotlinCoreEnvironment environment) throws Exception {
File file = new File(fileName);
CompilerTestLanguageVersionSettingsKt.setupLanguageVersionSettingsForCompilerTests(FileUtil.loadFile(file, true), environment);
KtFile ktFile = KotlinTestUtils.loadJetFile(environment.getProject(), file);
SetMultimap<KtElement, Pseudocode> data = LinkedHashMultimap.create();
AnalysisResult analysisResult = KotlinTestUtils.analyzeFile(ktFile, environment);
List<KtDeclaration> declarations = ktFile.getDeclarations();
BindingContext bindingContext = analysisResult.getBindingContext();
for (KtDeclaration declaration : declarations) {
addDeclaration(data, bindingContext, declaration);
if (declaration instanceof KtDeclarationContainer) {
for (KtDeclaration member : ((KtDeclarationContainer) declaration).getDeclarations()) {
// Properties and initializers are processed elsewhere
if (member instanceof KtNamedFunction || member instanceof KtSecondaryConstructor) {
addDeclaration(data, bindingContext, member);
}
}
}
}
try {
processCFData(file, data, bindingContext);
}
catch (IOException e) {
throw new RuntimeException(e);
}
finally {
if ("true".equals(System.getProperty("kotlin.control.flow.test.dump.graphs"))) {
CFGraphToDotFilePrinter.dumpDot(file, data.values());
}
}
}
private static void addDeclaration(SetMultimap<KtElement, Pseudocode> data, BindingContext bindingContext, KtDeclaration declaration) {
Pseudocode pseudocode = PseudocodeUtil.generatePseudocode(declaration, bindingContext);
data.put(declaration, pseudocode);
for (LocalFunctionDeclarationInstruction instruction : pseudocode.getLocalDeclarations()) {
Pseudocode localPseudocode = instruction.getBody();
data.put(localPseudocode.getCorrespondingElement(), localPseudocode);
}
}
private void processCFData(File file, SetMultimap<KtElement, Pseudocode> data, BindingContext bindingContext) throws IOException {
Collection<Pseudocode> pseudocodes = data.values();
StringBuilder instructionDump = new StringBuilder();
int i = 0;
for (Pseudocode pseudocode : pseudocodes) {
KtElement correspondingElement = pseudocode.getCorrespondingElement();
String label;
assert (correspondingElement instanceof KtNamedDeclaration || correspondingElement instanceof KtPropertyAccessor) :
"Unexpected element class is pseudocode: " + correspondingElement.getClass();
boolean isAnonymousFunction =
correspondingElement instanceof KtFunctionLiteral
|| (correspondingElement instanceof KtNamedFunction && correspondingElement.getName() == null);
if (isAnonymousFunction) {
label = "anonymous_" + i++;
}
else if (correspondingElement instanceof KtNamedDeclaration) {
KtNamedDeclaration namedDeclaration = (KtNamedDeclaration) correspondingElement;
label = namedDeclaration.getName();
}
else {
String propertyName = ((KtProperty) correspondingElement.getParent()).getName();
label = (((KtPropertyAccessor) correspondingElement).isGetter() ? "get" : "set") + "_" + propertyName;
}
if (pseudocode.isInlined()) {
label = "inlined " + label;
}
instructionDump.append("== ").append(label).append(" ==\n");
instructionDump.append(correspondingElement.getText());
instructionDump.append("\n---------------------\n");
dumpInstructions((PseudocodeImpl) pseudocode, instructionDump, bindingContext);
instructionDump.append("=====================\n");
checkPseudocode((PseudocodeImpl) pseudocode);
}
File expectedInstructionsFile = KotlinTestUtils.replaceExtension(file, getDataFileExtension());
KotlinTestUtils.assertEqualsToFile(expectedInstructionsFile, instructionDump.toString());
}
protected String getDataFileExtension() {
return "instructions";
}
protected void checkPseudocode(PseudocodeImpl pseudocode) {
}
private static String getIsDeadInstructionPrefix(
@NotNull Instruction instruction,
@NotNull Set<Instruction> remainedAfterPostProcessInstructions
) {
boolean isRemovedThroughPostProcess = !remainedAfterPostProcessInstructions.contains(instruction);
assert isRemovedThroughPostProcess == ((InstructionImpl)instruction).getMarkedAsDead();
return isRemovedThroughPostProcess ? "-" : " ";
}
private static String getDepthInstructionPrefix(@NotNull Instruction instruction, @Nullable Instruction previous) {
Integer prevDepth = previous != null ? previous.getBlockScope().getDepth() : null;
int depth = instruction.getBlockScope().getDepth();
if (prevDepth == null || depth != prevDepth) {
return String.format("%2d ", depth);
}
return " ";
}
private static String formatInstruction(Instruction instruction, int maxLength, String prefix) {
String[] parts = instruction.toString().split("\n");
if (parts.length == 1) {
return prefix + String.format("%1$-" + maxLength + "s", instruction);
}
StringBuilder sb = new StringBuilder();
for (int i = 0, partsLength = parts.length; i < partsLength; i++) {
String part = parts[i];
sb.append(prefix).append(String.format("%1$-" + maxLength + "s", part));
if (i < partsLength - 1) sb.append("\n");
}
return sb.toString();
}
protected abstract void dumpInstructions(
@NotNull PseudocodeImpl pseudocode,
@NotNull StringBuilder out,
@NotNull BindingContext bindingContext
);
protected void dumpInstructions(
@NotNull PseudocodeImpl pseudocode,
@NotNull StringBuilder out,
@NotNull Function3<Instruction, /*next*/Instruction, /*prev*/Instruction, String> getInstructionData
) {
List<Instruction> instructions = pseudocode.getInstructionsIncludingDeadCode();
Set<Instruction> remainedAfterPostProcessInstructions = Sets.newHashSet(pseudocode.getInstructions());
List<PseudocodeLabel> labels = pseudocode.getLabels();
int instructionColumnWidth = countInstructionColumnWidth(instructions);
for (int i = 0; i < instructions.size(); i++) {
Instruction instruction = instructions.get(i);
for (PseudocodeLabel label: labels) {
if (label.getTargetInstructionIndex() == i) {
out.append(label).append(":\n");
}
}
StringBuilder line = new StringBuilder();
// Only print NEXT and PREV if the values are non-trivial
Instruction next = i == instructions.size() - 1 ? null : instructions.get(i + 1);
Instruction prev = i == 0 ? null : instructions.get(i - 1);
String prefix = getIsDeadInstructionPrefix(instruction, remainedAfterPostProcessInstructions) +
getDepthInstructionPrefix(instruction, prev);
line.append(formatInstruction(instruction, instructionColumnWidth, prefix));
line.append(getInstructionData.invoke(instruction, next, prev));
out.append(StringUtil.trimTrailing(line.toString()));
out.append("\n");
}
}
private static int countInstructionColumnWidth(List<Instruction> instructions) {
int maxWidth = 0;
for (Instruction instruction : instructions) {
String instuctionText = instruction.toString();
if (instuctionText.length() > maxWidth) {
String[] parts = instuctionText.split("\n");
if (parts.length > 1) {
for (String part : parts) {
if (part.length() > maxWidth) {
maxWidth = part.length();
}
}
}
else {
if (instuctionText.length() > maxWidth) {
maxWidth = instuctionText.length();
}
}
}
}
return maxWidth;
}
}
@@ -0,0 +1,188 @@
/*
* 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.cfg;
import com.google.common.collect.Sets;
import com.intellij.openapi.util.io.FileUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode;
import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction;
import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitor;
import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionWithNext;
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.MagicInstruction;
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.MagicKind;
import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.*;
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.LocalFunctionDeclarationInstruction;
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.SubroutineEnterInstruction;
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.SubroutineExitInstruction;
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.SubroutineSinkInstruction;
import org.jetbrains.kotlin.psi.KtElement;
import org.jetbrains.kotlin.psi.KtNamedDeclaration;
import org.jetbrains.kotlin.test.KotlinTestUtils;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintStream;
import java.util.*;
public class CFGraphToDotFilePrinter {
public static void dumpDot(File file, Collection<Pseudocode> pseudocodes) throws FileNotFoundException {
File target = KotlinTestUtils.replaceExtension(file, "dot");
PrintStream out = new PrintStream(target);
out.println("digraph " + FileUtil.getNameWithoutExtension(file) + " {");
int[] count = new int[1];
Map<Instruction, String> nodeToName = new HashMap<>();
for (Pseudocode pseudocode : pseudocodes) {
dumpNodes(pseudocode.getInstructionsIncludingDeadCode(), out, count, nodeToName, Sets
.newHashSet(pseudocode.getInstructions()));
}
int i = 0;
for (Pseudocode pseudocode : pseudocodes) {
String label;
KtElement correspondingElement = pseudocode.getCorrespondingElement();
if (correspondingElement instanceof KtNamedDeclaration) {
KtNamedDeclaration namedDeclaration = (KtNamedDeclaration) correspondingElement;
label = namedDeclaration.getName();
}
else {
label = "anonymous_" + i;
}
out.println("subgraph cluster_" + i + " {\n" +
"label=\"" + label + "\";\n" +
"color=blue;\n");
dumpEdges(pseudocode.getInstructionsIncludingDeadCode(), out, count, nodeToName);
out.println("}");
i++;
}
out.println("}");
out.close();
}
private static void dumpEdges(List<Instruction> instructions, PrintStream out, int[] count, Map<Instruction, String> nodeToName) {
for (Instruction fromInst : instructions) {
fromInst.accept(new InstructionVisitor() {
@Override
public void visitLocalFunctionDeclarationInstruction(@NotNull LocalFunctionDeclarationInstruction instruction) {
int index = count[0];
// instruction.getBody().dumpSubgraph(out, "subgraph cluster_" + index, count, "color=blue;\nlabel = \"f" + index + "\";", nodeToName);
printEdge(out, nodeToName.get(instruction), nodeToName.get(instruction.getBody().getInstructionsIncludingDeadCode().get(0)), null);
visitInstructionWithNext(instruction);
}
@Override
public void visitUnconditionalJump(@NotNull UnconditionalJumpInstruction instruction) {
printEdge(out, nodeToName.get(instruction), nodeToName.get(instruction.getResolvedTarget()), null);
}
@Override
public void visitJump(@NotNull AbstractJumpInstruction instruction) {
printEdge(out, nodeToName.get(instruction), nodeToName.get(instruction.getResolvedTarget()), null);
}
@Override
public void visitNondeterministicJump(@NotNull NondeterministicJumpInstruction instruction) {
for (Instruction nextInstruction : instruction.getNextInstructions()) {
printEdge(out, nodeToName.get(instruction), nodeToName.get(nextInstruction), null);
}
}
@Override
public void visitReturnValue(@NotNull ReturnValueInstruction instruction) {
super.visitReturnValue(instruction);
}
@Override
public void visitReturnNoValue(@NotNull ReturnNoValueInstruction instruction) {
super.visitReturnNoValue(instruction);
}
@Override
public void visitConditionalJump(@NotNull ConditionalJumpInstruction instruction) {
String from = nodeToName.get(instruction);
printEdge(out, from, nodeToName.get(instruction.getNextOnFalse()), "no");
printEdge(out, from, nodeToName.get(instruction.getNextOnTrue()), "yes");
}
@Override
public void visitInstructionWithNext(@NotNull InstructionWithNext instruction) {
printEdge(out, nodeToName.get(instruction), nodeToName.get(instruction.getNext()), null);
}
@Override
public void visitSubroutineExit(@NotNull SubroutineExitInstruction instruction) {
if (!instruction.getNextInstructions().isEmpty()) {
printEdge(out, nodeToName.get(instruction), nodeToName.get(instruction.getNextInstructions().iterator().next()), null);
}
}
@Override
public void visitSubroutineSink(@NotNull SubroutineSinkInstruction instruction) {
// Nothing
}
@Override
public void visitInstruction(@NotNull Instruction instruction) {
throw new UnsupportedOperationException(instruction.toString());
}
});
}
}
private static void dumpNodes(List<Instruction> instructions, PrintStream out, int[] count, Map<Instruction, String> nodeToName, Set<Instruction> remainedAfterPostProcessInstructions) {
for (Instruction node : instructions) {
String name = "n" + count[0]++;
nodeToName.put(node, name);
String text = node.toString();
int newline = text.indexOf("\n");
if (newline >= 0) {
text = text.substring(0, newline);
}
String shape = "box";
if (node instanceof ConditionalJumpInstruction || node instanceof UnconditionalJumpInstruction) {
shape = "diamond";
}
else if (node instanceof NondeterministicJumpInstruction) {
shape = "Mdiamond";
}
else if (node instanceof MagicInstruction && ((MagicInstruction) node).getKind() == MagicKind.UNSUPPORTED_ELEMENT) {
shape = "box, fillcolor=red, style=filled";
}
else if (node instanceof LocalFunctionDeclarationInstruction) {
shape = "Mcircle";
}
else if (node instanceof SubroutineEnterInstruction || node instanceof SubroutineExitInstruction) {
shape = "roundrect, style=rounded";
}
if (!remainedAfterPostProcessInstructions.contains(node)) {
shape += "box, fillcolor=grey, style=filled";
}
out.println(name + "[label=\"" + text + "\", shape=" + shape + "];");
}
}
private static void printEdge(PrintStream out, String from, String to, String label) {
if (label != null) {
label = "[label=\"" + label + "\"]";
}
else {
label = "";
}
out.println(from + " -> " + to + label + ";");
}
}
@@ -0,0 +1,47 @@
/*
* Copyright 2010-2017 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.checkers.javac
import org.jetbrains.kotlin.checkers.AbstractDiagnosticsTestWithStdLib
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.test.InTextDirectivesUtils
import org.jetbrains.kotlin.test.KotlinTestUtils.getHomeDirectory
import java.io.File
abstract class AbstractDiagnosticsTestWithStdLibUsingJavac : AbstractDiagnosticsTestWithStdLib() {
override fun analyzeAndCheck(testDataFile: File, files: List<TestFile>) {
val testDataFileText = testDataFile.readText()
if (InTextDirectivesUtils.isDirectiveDefined(testDataFileText, "// JAVAC_SKIP")) {
println("${testDataFile.name} test is skipped")
return
}
val groupedByModule = files.groupBy(TestFile::module)
val allKtFiles = groupedByModule.values.flatMap { getKtFiles(it, true) }
if (InTextDirectivesUtils.isDirectiveDefined(testDataFileText, "// FULL_JDK")) {
environment.registerJavac(kotlinFiles = allKtFiles)
}
else {
val mockJdk = listOf(File(getHomeDirectory(), "compiler/testData/mockJDK/jre/lib/rt.jar"))
environment.registerJavac(kotlinFiles = allKtFiles, bootClasspath = mockJdk)
}
environment.configuration.put(JVMConfigurationKeys.USE_JAVAC, true)
super.analyzeAndCheck(testDataFile, files)
}
}
@@ -0,0 +1,34 @@
/*
* Copyright 2010-2017 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.checkers.javac
import org.jetbrains.kotlin.checkers.AbstractForeignAnnotationsTest
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import java.io.File
abstract class AbstractJavacForeignAnnotationsTest : AbstractForeignAnnotationsTest() {
override fun analyzeAndCheck(testDataFile: File, files: List<TestFile>) {
val groupedByModule = files.groupBy(TestFile::module)
val allKtFiles = groupedByModule.values.flatMap { getKtFiles(it, true) }
environment.registerJavac(kotlinFiles = allKtFiles)
environment.configuration.put(JVMConfigurationKeys.USE_JAVAC, true)
super.analyzeAndCheck(testDataFile, files)
}
}
@@ -0,0 +1,37 @@
/*
* Copyright 2010-2017 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.checkers.javac
import org.jetbrains.kotlin.checkers.AbstractForeignJava8AnnotationsTest
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.test.InTextDirectivesUtils
import java.io.File
abstract class AbstractJavacForeignJava8AnnotationsTest : AbstractForeignJava8AnnotationsTest() {
override fun analyzeAndCheck(testDataFile: File, files: List<TestFile>) {
if (files.any { file -> InTextDirectivesUtils.isDirectiveDefined(file.expectedText, "// SKIP_JAVAC") }) return
val groupedByModule = files.groupBy(TestFile::module)
val allKtFiles = groupedByModule.values.flatMap { getKtFiles(it, true) }
environment.registerJavac(kotlinFiles = allKtFiles)
environment.configuration.put(JVMConfigurationKeys.USE_JAVAC, true)
super.analyzeAndCheck(testDataFile, files)
}
}
@@ -0,0 +1,109 @@
/*
* 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.checkers
import com.intellij.openapi.util.text.StringUtil
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.context.ModuleContext
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.js.analyze.TopDownAnalyzerFacadeForJS
import org.jetbrains.kotlin.js.analyzer.JsAnalysisResult
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.js.config.JsConfig
import org.jetbrains.kotlin.js.resolve.JsPlatform
import org.jetbrains.kotlin.js.resolve.MODULE_KIND
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.serialization.js.ModuleKind
import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.test.KotlinTestUtils
import java.util.*
abstract class AbstractDiagnosticsTestWithJsStdLib : AbstractDiagnosticsTest() {
private var lazyConfig: Lazy<JsConfig>? = lazy(LazyThreadSafetyMode.NONE) {
JsConfig(project, environment.configuration.copy().apply {
put(CommonConfigurationKeys.MODULE_NAME, KotlinTestUtils.TEST_MODULE_NAME)
put(JSConfigurationKeys.LIBRARIES, JsConfig.JS_STDLIB)
})
}
protected val config: JsConfig get() = lazyConfig!!.value
override fun tearDown() {
lazyConfig = null
super.tearDown()
}
override fun getEnvironmentConfigFiles(): EnvironmentConfigFiles = EnvironmentConfigFiles.JS_CONFIG_FILES
override fun analyzeModuleContents(
moduleContext: ModuleContext,
files: List<KtFile>,
moduleTrace: BindingTrace,
languageVersionSettings: LanguageVersionSettings,
separateModules: Boolean
): JsAnalysisResult {
// TODO: support LANGUAGE directive in JS diagnostic tests
moduleTrace.record<ModuleDescriptor, ModuleKind>(MODULE_KIND, moduleContext.module, getModuleKind(files))
return TopDownAnalyzerFacadeForJS.analyzeFilesWithGivenTrace(files, moduleTrace, moduleContext, config)
}
private fun getModuleKind(ktFiles: List<KtFile>): ModuleKind {
var kind = ModuleKind.PLAIN
for (file in ktFiles) {
val text = file.text
for (textLine in StringUtil.splitByLines(text)) {
var line = textLine.trim { it <= ' ' }
if (!line.startsWith("//")) continue
line = line.substring(2).trim { it <= ' ' }
val parts = StringUtil.split(line, ":")
if (parts.size != 2) continue
if (parts[0].trim { it <= ' ' } != "MODULE_KIND") continue
kind = ModuleKind.valueOf(parts[1].trim { it <= ' ' })
}
}
return kind
}
override fun getAdditionalDependencies(module: ModuleDescriptorImpl): List<ModuleDescriptorImpl> =
config.moduleDescriptors.map { it.data }
override fun shouldSkipJvmSignatureDiagnostics(groupedByModule: Map<TestModule?, List<TestFile>>): Boolean = true
override fun createModule(moduleName: String, storageManager: StorageManager): ModuleDescriptorImpl =
ModuleDescriptorImpl(Name.special("<$moduleName>"), storageManager, JsPlatform.builtIns)
override fun createSealedModule(storageManager: StorageManager): ModuleDescriptorImpl {
val module = createModule("kotlin-js-test-module", storageManager)
val dependencies = ArrayList<ModuleDescriptorImpl>()
dependencies.add(module)
dependencies.addAll(getAdditionalDependencies(module))
dependencies.add(module.builtIns.builtInsModule)
module.setDependencies(dependencies)
return module
}
}
@@ -0,0 +1,47 @@
/*
* 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.checkers
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.context.ModuleContext
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils.hasError
import org.jetbrains.kotlin.js.analyzer.JsAnalysisResult
import org.jetbrains.kotlin.js.config.JsConfig
import org.jetbrains.kotlin.js.facade.K2JSTranslator
import org.jetbrains.kotlin.js.facade.MainCallParameters
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.BindingTrace
abstract class AbstractDiagnosticsTestWithJsStdLibAndBackendCompilation : AbstractDiagnosticsTestWithJsStdLib() {
override fun analyzeModuleContents(
moduleContext: ModuleContext,
files: List<KtFile>,
moduleTrace: BindingTrace,
languageVersionSettings: LanguageVersionSettings,
separateModules: Boolean
): JsAnalysisResult {
val analysisResult = super.analyzeModuleContents(moduleContext, files, moduleTrace, languageVersionSettings, separateModules)
val diagnostics = analysisResult.bindingTrace.bindingContext.diagnostics
if (!hasError(diagnostics)) {
val translator = K2JSTranslator(config)
translator.translate(object : JsConfig.Reporter() {}, files, MainCallParameters.noCall(), analysisResult)
}
return analysisResult
}
}
@@ -0,0 +1,28 @@
/*
* 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.checkers;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.test.ConfigurationKind;
public abstract class AbstractDiagnosticsTestWithStdLib extends AbstractDiagnosticsTest {
@NotNull
@Override
protected ConfigurationKind getConfigurationKind() {
return ConfigurationKind.ALL;
}
}
@@ -0,0 +1,53 @@
/*
* Copyright 2010-2017 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.checkers;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.test.ConfigurationKind;
import org.jetbrains.kotlin.test.KotlinTestUtils;
import org.jetbrains.kotlin.test.TestJdkKind;
import java.io.File;
import java.util.List;
import java.util.Map;
public abstract class AbstractDiagnosticsWithJdk9Test extends AbstractDiagnosticsTest {
@NotNull
@Override
protected ConfigurationKind getConfigurationKind() {
return ConfigurationKind.ALL;
}
@NotNull
@Override
protected TestJdkKind getTestJdkKind(@NotNull File file) {
return TestJdkKind.FULL_JDK_9;
}
@Override
protected void doMultiFileTest(
@NotNull File file,
@NotNull Map<String, ModuleAndDependencies> modules,
@NotNull List<TestFile> testFiles
) {
if (KotlinTestUtils.getJdk9HomeIfPossible() == null) {
// Skip this test if no Java 9 is found
return;
}
super.doMultiFileTest(file, modules, testFiles);
}
}
@@ -0,0 +1,48 @@
/*
* Copyright 2010-2017 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.checkers
import org.jetbrains.kotlin.codegen.CodegenTestUtil
import org.jetbrains.kotlin.test.InTextDirectivesUtils
import org.jetbrains.kotlin.test.KotlinTestUtils
import java.io.File
abstract class AbstractForeignAnnotationsNoAnnotationInClasspathTest : AbstractForeignAnnotationsTest() {
private val compiledJavaPath = KotlinTestUtils.tmpDir("java-compiled-files")
override fun getExtraClasspath(): List<File> {
val foreignAnnotations = createJarWithForeignAnnotations()
val testAnnotations = compileTestAnnotations(foreignAnnotations)
val additionalClasspath = (foreignAnnotations + testAnnotations).map { it.path }
CodegenTestUtil.compileJava(
CodegenTestUtil.findJavaSourcesInDirectory(javaFilesDir),
additionalClasspath, emptyList(),
compiledJavaPath
)
return listOf(compiledJavaPath) + testAnnotations
}
override fun analyzeAndCheck(testDataFile: File, files: List<TestFile>) {
if (files.any { file -> InTextDirectivesUtils.isDirectiveDefined(file.expectedText, "// SOURCE_RETENTION_ANNOTATIONS") }) return
super.analyzeAndCheck(testDataFile, files)
}
override fun isJavaSourceRootNeeded() = false
override fun skipDescriptorsValidation() = true
}
@@ -0,0 +1,27 @@
/*
* Copyright 2010-2017 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.checkers
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.JVMConfigurationKeys
abstract class AbstractForeignAnnotationsNoAnnotationInClasspathWithFastClassReadingTest : AbstractForeignAnnotationsNoAnnotationInClasspathTest() {
override fun performCustomConfiguration(configuration: CompilerConfiguration) {
super.performCustomConfiguration(configuration)
configuration.put(JVMConfigurationKeys.USE_FAST_CLASS_FILES_READING, true)
}
}
@@ -0,0 +1,94 @@
/*
* Copyright 2010-2017 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.checkers
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
import org.jetbrains.kotlin.config.AnalysisFlag
import org.jetbrains.kotlin.config.ApiVersion
import org.jetbrains.kotlin.config.LanguageVersion
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.test.ConfigurationKind
import org.jetbrains.kotlin.test.InTextDirectivesUtils
import org.jetbrains.kotlin.test.MockLibraryUtil
import org.jetbrains.kotlin.test.TestJdkKind
import org.jetbrains.kotlin.utils.Jsr305State
import org.jetbrains.kotlin.utils.ReportLevel
import java.io.File
val FOREIGN_ANNOTATIONS_SOURCES_PATH = "third-party/annotations"
val TEST_ANNOTATIONS_SOURCE_PATH = "compiler/testData/foreignAnnotations/testAnnotations"
abstract class AbstractForeignAnnotationsTest : AbstractDiagnosticsTest() {
private val JSR305_GLOBAL_DIRECTIVE = "JSR305_GLOBAL_REPORT"
private val JSR305_MIGRATION_DIRECTIVE = "JSR305_MIGRATION_REPORT"
private val JSR305_SPECIAL_DIRECTIVE = "JSR305_SPECIAL_REPORT"
override fun getExtraClasspath(): List<File> {
val foreignAnnotations = createJarWithForeignAnnotations()
return foreignAnnotations + compileTestAnnotations(foreignAnnotations)
}
protected fun compileTestAnnotations(extraClassPath: List<File>): List<File> =
listOf(MockLibraryUtil.compileJavaFilesLibraryToJar(
TEST_ANNOTATIONS_SOURCE_PATH,
"test-foreign-annotations",
extraOptions = listOf("-Xallow-kotlin-package"),
extraClasspath = extraClassPath.map { it.path }
))
protected fun createJarWithForeignAnnotations(): List<File> = listOf(
MockLibraryUtil.compileJavaFilesLibraryToJar(annotationsPath, "foreign-annotations"),
ForTestCompileRuntime.jvmAnnotationsForTests()
)
override fun getConfigurationKind(): ConfigurationKind = ConfigurationKind.ALL
override fun getTestJdkKind(file: File): TestJdkKind = TestJdkKind.FULL_JDK
open protected val annotationsPath: String
get() = FOREIGN_ANNOTATIONS_SOURCES_PATH
override fun loadLanguageVersionSettings(module: List<TestFile>): LanguageVersionSettings {
val analysisFlags = loadAnalysisFlags(module)
return CompilerTestLanguageVersionSettings(
DEFAULT_DIAGNOSTIC_TESTS_FEATURES,
ApiVersion.LATEST_STABLE,
LanguageVersion.LATEST_STABLE,
analysisFlags = analysisFlags
)
}
private fun loadAnalysisFlags(module: List<TestFile>): Map<AnalysisFlag<*>, Any?> {
val globalState = module.getDirectiveValue(JSR305_GLOBAL_DIRECTIVE) ?: ReportLevel.STRICT
val migrationState = module.getDirectiveValue(JSR305_MIGRATION_DIRECTIVE)
val userAnnotationsState = module.flatMap {
InTextDirectivesUtils.findListWithPrefixes(it.expectedText, JSR305_SPECIAL_DIRECTIVE)
}.mapNotNull {
val (name, stateDescription) = it.split(":").takeIf { it.size == 2 } ?: return@mapNotNull null
val state = ReportLevel.findByDescription(stateDescription) ?: return@mapNotNull null
name to state
}.toMap()
return mapOf(AnalysisFlag.jsr305 to Jsr305State(globalState, migrationState, userAnnotationsState))
}
private fun List<TestFile>.getDirectiveValue(directive: String): ReportLevel? = mapNotNull {
InTextDirectivesUtils.findLinesWithPrefixesRemoved(it.expectedText, directive).firstOrNull()
}.firstOrNull().let { ReportLevel.findByDescription(it) }
}
@@ -0,0 +1,51 @@
/*
* 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.checkers
import org.jetbrains.kotlin.test.InTextDirectivesUtils
import java.io.File
abstract class AbstractForeignJava8AnnotationsTest : AbstractForeignAnnotationsTest() {
override val annotationsPath: String
get() = JAVA8_ANNOTATION_SOURCES_PATH
}
abstract class AbstractForeignJava8AnnotationsNoAnnotationInClasspathTest : AbstractForeignAnnotationsNoAnnotationInClasspathTest() {
override val annotationsPath: String
get() = JAVA8_ANNOTATION_SOURCES_PATH
override fun analyzeAndCheck(testDataFile: File, files: List<TestFile>) {
if (skipForCompiledVersion(files)) return
super.analyzeAndCheck(testDataFile, files)
}
}
abstract class AbstractForeignJava8AnnotationsNoAnnotationInClasspathWithFastClassReadingTest : AbstractForeignAnnotationsNoAnnotationInClasspathWithFastClassReadingTest() {
override val annotationsPath: String
get() = JAVA8_ANNOTATION_SOURCES_PATH
override fun analyzeAndCheck(testDataFile: File, files: List<TestFile>) {
if (skipForCompiledVersion(files)) return
super.analyzeAndCheck(testDataFile, files)
}
}
private fun skipForCompiledVersion(files: List<BaseDiagnosticsTest.TestFile>) =
files.any { file -> InTextDirectivesUtils.isDirectiveDefined(file.expectedText, "// SKIP_COMPILED_JAVA") }
private const val JAVA8_ANNOTATION_SOURCES_PATH = "third-party/jdk8-annotations"
@@ -0,0 +1,272 @@
/*
* Copyright 2010-2017 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.checkers;
import com.google.common.collect.Lists;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.checkers.CheckerTestUtil.ActualDiagnostic;
import org.jetbrains.kotlin.checkers.CheckerTestUtil.DiagnosedRange;
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
import org.jetbrains.kotlin.psi.KtFile;
import org.jetbrains.kotlin.resolve.BindingContext;
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 java.io.File;
import java.util.List;
public class CheckerTestUtilTest extends KotlinTestWithEnvironment {
@NotNull
private static String getTestDataPath() {
return KotlinTestUtils.getTestDataPathBase() + "/diagnostics/checkerTestUtil";
}
@Override
protected KotlinCoreEnvironment createEnvironment() {
return createEnvironmentWithMockJdk(ConfigurationKind.ALL);
}
protected void doTest(TheTest theTest) throws Exception {
String text = KotlinTestUtils.doLoadFile(getTestDataPath(), "test.kt");
theTest.test(TestCheckerUtil.createCheckAndReturnPsiFile("test.kt", text, getProject()), getEnvironment());
}
public void testEquals() throws Exception {
doTest(new TheTest() {
@Override
protected void makeTestData(List<ActualDiagnostic> diagnostics, List<DiagnosedRange> diagnosedRanges) {
}
});
}
public void testMissing() throws Exception {
DiagnosticData typeMismatch1 = diagnostics.get(1);
doTest(new TheTest(missing(typeMismatch1)) {
@Override
protected void makeTestData(List<ActualDiagnostic> diagnostics, List<DiagnosedRange> diagnosedRanges) {
diagnostics.remove(typeMismatch1.index);
}
});
}
public void testUnexpected() throws Exception {
DiagnosticData typeMismatch1 = diagnostics.get(1);
doTest(new TheTest(unexpected(typeMismatch1)) {
@Override
protected void makeTestData(List<ActualDiagnostic> diagnostics, List<DiagnosedRange> diagnosedRanges) {
diagnosedRanges.remove(typeMismatch1.index);
}
});
}
public void testBoth() throws Exception {
DiagnosticData typeMismatch1 = diagnostics.get(1);
DiagnosticData unresolvedReference = diagnostics.get(6);
doTest(new TheTest(unexpected(typeMismatch1), missing(unresolvedReference)) {
@Override
protected void makeTestData(List<ActualDiagnostic> diagnostics, List<DiagnosedRange> diagnosedRanges) {
diagnosedRanges.remove(typeMismatch1.rangeIndex);
diagnostics.remove(unresolvedReference.index);
}
});
}
public void testMissingInTheMiddle() throws Exception {
DiagnosticData noneApplicable = diagnostics.get(4);
DiagnosticData typeMismatch3 = diagnostics.get(5);
doTest(new TheTest(unexpected(noneApplicable), missing(typeMismatch3)) {
@Override
protected void makeTestData(List<ActualDiagnostic> diagnostics, List<DiagnosedRange> diagnosedRanges) {
diagnosedRanges.remove(noneApplicable.rangeIndex);
diagnostics.remove(typeMismatch3.index);
}
});
}
public void testWrongParameters() throws Exception {
DiagnosticData unused = diagnostics.get(2);
String unusedDiagnostic = asTextDiagnostic(unused, "i");
DiagnosedRange range = asDiagnosticRange(unused, unusedDiagnostic);
doTest(new TheTest(wrongParameters(unusedDiagnostic, "UNUSED_VARIABLE(a)", unused.startOffset, unused.endOffset)) {
@Override
protected void makeTestData(List<ActualDiagnostic> diagnostics, List<DiagnosedRange> diagnosedRanges) {
diagnosedRanges.set(unused.rangeIndex, range);
}
});
}
public void testWrongParameterInMultiRange() throws Exception {
DiagnosticData unresolvedReference = diagnostics.get(6);
String unusedDiagnostic = asTextDiagnostic(unresolvedReference, "i");
String toManyArguments = asTextDiagnostic(diagnostics.get(7));
DiagnosedRange range = asDiagnosticRange(unresolvedReference, unusedDiagnostic, toManyArguments);
doTest(new TheTest(wrongParameters(unusedDiagnostic, "UNRESOLVED_REFERENCE(xx)", unresolvedReference.startOffset, unresolvedReference.endOffset)) {
@Override
protected void makeTestData(List<ActualDiagnostic> diagnostics, List<DiagnosedRange> diagnosedRanges) {
diagnosedRanges.set(unresolvedReference.rangeIndex, range);
}
});
}
public void testAbstractJetDiagnosticsTest() throws Exception {
AbstractDiagnosticsTest test = new AbstractDiagnosticsTest() {
{setUp();}
};
test.doTest(getTestDataPath() + File.separatorChar + "test_with_diagnostic.kt");
}
private static abstract class TheTest {
private final String[] expected;
protected TheTest(String... expectedMessages) {
this.expected = expectedMessages;
}
public void test(@NotNull PsiFile psiFile, @NotNull KotlinCoreEnvironment environment) {
BindingContext bindingContext =
JvmResolveUtil.analyze((KtFile) psiFile, environment).getBindingContext();
String expectedText = CheckerTestUtil.addDiagnosticMarkersToText(
psiFile,
CheckerTestUtil.getDiagnosticsIncludingSyntaxErrors(bindingContext, psiFile, false, null, null, false)
).toString();
List<DiagnosedRange> diagnosedRanges = Lists.newArrayList();
CheckerTestUtil.parseDiagnosedRanges(expectedText, diagnosedRanges);
List<ActualDiagnostic> actualDiagnostics =
CheckerTestUtil.getDiagnosticsIncludingSyntaxErrors(bindingContext, psiFile, false, null, null, false);
actualDiagnostics.sort(CheckerTestUtil.DIAGNOSTIC_COMPARATOR);
makeTestData(actualDiagnostics, diagnosedRanges);
List<String> expectedMessages = Lists.newArrayList(expected);
List<String> actualMessages = Lists.newArrayList();
CheckerTestUtil.diagnosticsDiff(diagnosedRanges, actualDiagnostics, new CheckerTestUtil.DiagnosticDiffCallbacks() {
@Override
public void missingDiagnostic(CheckerTestUtil.TextDiagnostic diagnostic, int expectedStart, int expectedEnd) {
actualMessages.add(missing(diagnostic.getDescription(), expectedStart, expectedEnd));
}
@Override
public void wrongParametersDiagnostic(
CheckerTestUtil.TextDiagnostic expectedDiagnostic,
CheckerTestUtil.TextDiagnostic actualDiagnostic,
int start,
int end
) {
actualMessages.add(wrongParameters(expectedDiagnostic.asString(), actualDiagnostic.asString(), start, end));
}
@Override
public void unexpectedDiagnostic(CheckerTestUtil.TextDiagnostic diagnostic, int actualStart, int actualEnd) {
actualMessages.add(unexpected(diagnostic.getDescription(), actualStart, actualEnd));
}
@Override
public void uncheckedDiagnostic(CheckerTestUtil.TextDiagnostic diagnostic, int expectedStart, int expectedEnd) {
}
@Override
public boolean shouldUseDiagnosticsForNI() {
return false;
}
@Override
public boolean isWithNewInferenceDirective() {
return false;
}
});
assertEquals(listToString(expectedMessages), listToString(actualMessages));
}
private static String listToString(List<String> expectedMessages) {
StringBuilder stringBuilder = new StringBuilder();
for (String expectedMessage : expectedMessages) {
stringBuilder.append(expectedMessage).append("\n");
}
return stringBuilder.toString();
}
protected abstract void makeTestData(List<ActualDiagnostic> diagnostics, List<DiagnosedRange> diagnosedRanges);
}
private static String wrongParameters(String expected, String actual, int start, int end) {
return "Wrong parameters " + expected + " != " + actual +" at " + start + " to " + end;
}
private static String unexpected(String type, int actualStart, int actualEnd) {
return "Unexpected " + type + " at " + actualStart + " to " + actualEnd;
}
private static String missing(String type, int expectedStart, int expectedEnd) {
return "Missing " + type + " at " + expectedStart + " to " + expectedEnd;
}
private static String unexpected(DiagnosticData data) {
return unexpected(data.name, data.startOffset, data.endOffset);
}
private static String missing(DiagnosticData data) {
return missing(data.name, data.startOffset, data.endOffset);
}
private static String asTextDiagnostic(DiagnosticData diagnosticData, String... params) {
return diagnosticData.name + "(" + StringUtil.join(params, "; ") + ")";
}
private static DiagnosedRange asDiagnosticRange(DiagnosticData diagnosticData, String... textDiagnostics) {
DiagnosedRange range = new DiagnosedRange(diagnosticData.startOffset);
range.setEnd(diagnosticData.endOffset);
for (String textDiagnostic : textDiagnostics)
range.addDiagnostic(textDiagnostic);
return range;
}
private static class DiagnosticData {
public int index;
public int rangeIndex;
public String name;
public int startOffset;
public int endOffset;
private DiagnosticData(int index, int rangeIndex, String name, int startOffset, int endOffset) {
this.index = index;
this.rangeIndex = rangeIndex;
this.name = name;
this.startOffset = startOffset;
this.endOffset = endOffset;
}
}
private final List<DiagnosticData> diagnostics = Lists.newArrayList(
new DiagnosticData(0, 0, "UNUSED_PARAMETER", 8, 9),
new DiagnosticData(1, 1, "CONSTANT_EXPECTED_TYPE_MISMATCH", 56, 57),
new DiagnosticData(2, 2, "UNUSED_VARIABLE", 67, 68),
new DiagnosticData(3, 3, "TYPE_MISMATCH", 98, 99),
new DiagnosticData(4, 4, "NONE_APPLICABLE", 120, 121),
new DiagnosticData(5, 5, "TYPE_MISMATCH", 159, 167),
new DiagnosticData(6, 6, "UNRESOLVED_REFERENCE", 164, 166),
new DiagnosticData(7, 6, "TOO_MANY_ARGUMENTS", 164, 166)
);
}
@@ -0,0 +1,208 @@
/*
* 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.cli;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import kotlin.Pair;
import kotlin.collections.CollectionsKt;
import kotlin.io.FilesKt;
import kotlin.text.Charsets;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.checkers.AbstractForeignAnnotationsTestKt;
import org.jetbrains.kotlin.cli.common.CLITool;
import org.jetbrains.kotlin.cli.common.ExitCode;
import org.jetbrains.kotlin.cli.js.K2JSCompiler;
import org.jetbrains.kotlin.cli.js.dce.K2JSDce;
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler;
import org.jetbrains.kotlin.config.KotlinCompilerVersion;
import org.jetbrains.kotlin.load.kotlin.JvmMetadataVersion;
import org.jetbrains.kotlin.test.CompilerTestUtil;
import org.jetbrains.kotlin.test.InTextDirectivesUtils;
import org.jetbrains.kotlin.test.KotlinTestUtils;
import org.jetbrains.kotlin.test.TestCaseWithTmpdir;
import org.jetbrains.kotlin.utils.JsMetadataVersion;
import org.jetbrains.kotlin.utils.PathUtil;
import org.jetbrains.kotlin.utils.StringsKt;
import org.junit.Assert;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public abstract class AbstractCliTest extends TestCaseWithTmpdir {
private static final String TESTDATA_DIR = "$TESTDATA_DIR$";
public static Pair<String, ExitCode> executeCompilerGrabOutput(@NotNull CLITool<?> compiler, @NotNull List<String> args) {
StringBuilder output = new StringBuilder();
int index = 0;
do {
int next = args.subList(index, args.size()).indexOf("---");
if (next == -1) {
next = args.size();
}
Pair<String, ExitCode> pair = CompilerTestUtil.executeCompiler(compiler, args.subList(index, next));
output.append(pair.getFirst());
if (pair.getSecond() != ExitCode.OK) {
return new Pair<>(output.toString(), pair.getSecond());
}
index = next + 1;
}
while (index < args.size());
return new Pair<>(output.toString(), ExitCode.OK);
}
@NotNull
public static String getNormalizedCompilerOutput(@NotNull String pureOutput, @NotNull ExitCode exitCode, @NotNull String testDataDir) {
String testDataAbsoluteDir = new File(testDataDir).getAbsolutePath();
String normalizedOutputWithoutExitCode = StringUtil.convertLineSeparators(pureOutput)
.replace(testDataAbsoluteDir, TESTDATA_DIR)
.replace(FileUtil.toSystemIndependentName(testDataAbsoluteDir), TESTDATA_DIR)
.replace(PathUtil.getKotlinPathsForDistDirectory().getHomePath().getAbsolutePath(), "$PROJECT_DIR$")
.replace("expected version is " + JvmMetadataVersion.INSTANCE, "expected version is $ABI_VERSION$")
.replace("expected version is " + JsMetadataVersion.INSTANCE, "expected version is $ABI_VERSION$")
.replace("\\", "/")
.replace(KotlinCompilerVersion.VERSION, "$VERSION$");
return normalizedOutputWithoutExitCode + exitCode + "\n";
}
private void doTest(@NotNull String fileName, @NotNull CLITool<?> compiler) {
System.setProperty("java.awt.headless", "true");
Pair<String, ExitCode> outputAndExitCode = executeCompilerGrabOutput(compiler, readArgs(fileName, tmpdir.getPath()));
String actual = getNormalizedCompilerOutput(
outputAndExitCode.getFirst(), outputAndExitCode.getSecond(), new File(fileName).getParent()
);
File outFile = new File(fileName.replaceFirst("\\.args$", ".out"));
KotlinTestUtils.assertEqualsToFile(outFile, actual);
File additionalTestConfig = new File(fileName.replaceFirst("\\.args$", ".test"));
if (additionalTestConfig.exists()) {
doTestAdditionalChecks(additionalTestConfig, fileName);
}
}
private void doTestAdditionalChecks(@NotNull File testConfigFile, @NotNull String argsFilePath) {
List<String> diagnostics = new ArrayList<>(0);
String content = FilesKt.readText(testConfigFile, Charsets.UTF_8);
List<String> existsList = InTextDirectivesUtils.findListWithPrefixes(content, "// EXISTS: ");
for (String fileName : existsList) {
File file = checkedPathToFile(fileName, argsFilePath);
if (!file.exists()) {
diagnostics.add("File does not exist, but should: " + fileName);
}
else if (!file.isFile()) {
diagnostics.add("File is a directory, but should be a normal file: " + fileName);
}
}
List<String> absentList = InTextDirectivesUtils.findListWithPrefixes(content, "// ABSENT: ");
for (String fileName : absentList) {
File file = checkedPathToFile(fileName, argsFilePath);
if (file.exists() && file.isFile()) {
diagnostics.add("File exists, but shouldn't: " + fileName);
}
}
List<String> containsTextList = InTextDirectivesUtils.findLinesWithPrefixesRemoved(content, "// CONTAINS: ");
for (String containsSpec : containsTextList) {
String[] parts = containsSpec.split(",", 2);
String fileName = parts[0].trim();
String contentToSearch = parts[1].trim();
File file = checkedPathToFile(fileName, argsFilePath);
if (!file.exists()) {
diagnostics.add("File does not exist: " + fileName);
}
else if (file.isDirectory()) {
diagnostics.add("File is a directory: " + fileName);
}
else {
String text = FilesKt.readText(file, Charsets.UTF_8);
if (!text.contains(contentToSearch)) {
diagnostics.add("File " + fileName + " does not contain string: " + contentToSearch);
}
}
}
if (!diagnostics.isEmpty()) {
diagnostics.add(0, diagnostics.size() + " problem(s) found:");
Assert.fail(StringsKt.join(diagnostics, "\n"));
}
}
@NotNull
private File checkedPathToFile(@NotNull String path, @NotNull String argsFilePath) {
if (path.startsWith(TESTDATA_DIR + "/")) {
return new File(new File(argsFilePath).getParent(), path.substring(TESTDATA_DIR.length() + 1));
}
else {
return new File(tmpdir, path);
}
}
@NotNull
private static List<String> readArgs(@NotNull String argsFilePath, @NotNull String tempDir) {
List<String> lines = FilesKt.readLines(new File(argsFilePath), Charsets.UTF_8);
return CollectionsKt.mapNotNull(lines, arg -> {
if (arg.isEmpty()) {
return null;
}
// Do not replace ':' after '\' (used in compiler plugin tests)
String argsWithColonsReplaced = arg
.replace("\\:", "$COLON$")
.replace(":", File.pathSeparator)
.replace("$COLON$", ":");
return argsWithColonsReplaced
.replace("$TEMP_DIR$", tempDir)
.replace(TESTDATA_DIR, new File(argsFilePath).getParent())
.replace(
"$FOREIGN_ANNOTATIONS_DIR$",
new File(AbstractForeignAnnotationsTestKt.getFOREIGN_ANNOTATIONS_SOURCES_PATH()).getPath()
);
});
}
protected void doJvmTest(@NotNull String fileName) {
doTest(fileName, new K2JVMCompiler());
}
protected void doJsTest(@NotNull String fileName) {
doTest(fileName, new K2JSCompiler());
}
protected void doJsDceTest(@NotNull String fileName) {
doTest(fileName, new K2JSDce());
}
public static String removePerfOutput(String output) {
String[] lines = StringUtil.splitByLinesKeepSeparators(output);
StringBuilder result = new StringBuilder();
for (String line : lines) {
if (!line.contains("PERF:")) {
result.append(line);
}
}
return result.toString();
}
}
@@ -0,0 +1,40 @@
/*
* 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.config.addJvmClasspathRoot
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.test.ConfigurationKind
import java.io.File
abstract class AbstractBlackBoxAgainstJavaCodegenTest : AbstractBlackBoxCodegenTest() {
override fun doMultiFileTest(wholeFile: File, files: MutableList<TestFile>, javaFilesDir: File?) {
javaClassesOutputDirectory = javaFilesDir!!.let { directory ->
CodegenTestUtil.compileJava(CodegenTestUtil.findJavaSourcesInDirectory(directory), emptyList(), extractJavacOptions(files))
}
super.doMultiFileTest(wholeFile, files, null)
}
override fun updateConfiguration(configuration: CompilerConfiguration) {
configuration.addJvmClasspathRoot(javaClassesOutputDirectory)
}
override fun extractConfigurationKind(files: MutableList<TestFile>): ConfigurationKind {
return ConfigurationKind.ALL
}
}
@@ -0,0 +1,33 @@
/*
* 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 java.io.File
abstract class AbstractBlackBoxInlineCodegenTest : AbstractBlackBoxCodegenTest() {
override fun doMultiFileTest(wholeFile: File, files: List<TestFile>, javaFilesDir: File?) {
super.doMultiFileTest(wholeFile, files, javaFilesDir)
try {
InlineTestUtil.checkNoCallsToInline(initializedClassLoader.allGeneratedFiles.filterClassFiles(), myFiles.psiFiles)
SMAPTestUtil.checkSMAP(files, generateClassesInFile().getClassFiles())
}
catch (e: Throwable) {
println(generateToText())
throw e
}
}
}
@@ -0,0 +1,49 @@
/*
* Copyright 2010-2017 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.KotlinCoreEnvironment
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension
import org.jetbrains.kotlin.resolve.jvm.extensions.PartialAnalysisHandlerExtension
import org.jetbrains.kotlin.test.KotlinTestUtils
import java.io.File
abstract class AbstractKapt3BuilderModeBytecodeShapeTest : CodegenTestCase() {
private companion object {
var TEST_LIGHT_ANALYSIS: ClassBuilderFactory = object : ClassBuilderFactories.TestClassBuilderFactory(false) {
override fun getClassBuilderMode() = ClassBuilderMode.KAPT3
}
}
override fun doMultiFileTest(wholeFile: File, files: MutableList<TestFile>, javaFilesDir: File?) {
val txtFile = File(wholeFile.parentFile, wholeFile.nameWithoutExtension + ".txt")
compile(files, javaFilesDir)
KotlinTestUtils.assertEqualsToFile(txtFile, BytecodeListingTextCollectingVisitor.getText(classFileFactory))
}
override fun setupEnvironment(environment: KotlinCoreEnvironment) {
AnalysisHandlerExtension.registerExtension(environment.project, PartialAnalysisHandlerExtension())
}
override fun getClassBuilderFactory(): ClassBuilderFactory {
return TEST_LIGHT_ANALYSIS
}
override fun verifyWithDex(): Boolean {
return false
}
}
@@ -0,0 +1,75 @@
/*
* 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 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.CompilerTestUtil;
import org.jetbrains.kotlin.test.ConfigurationKind;
import org.jetbrains.kotlin.test.KotlinTestUtils;
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);
List<String> sourceFiles = new ArrayList<>(2);
FileUtil.processFilesRecursively(root, file -> {
if (file.getName().endsWith(".kt")) {
sourceFiles.add(relativePath(file));
return true;
}
return true;
}, file -> !LIBRARY.equals(file.getName()));
File library = new File(root, LIBRARY);
List<File> classPath =
library.exists()
? Collections.singletonList(CompilerTestUtil.compileJvmLibrary(library))
: Collections.emptyList();
assert !sourceFiles.isEmpty() : getTestName(true) + " should contain at least one .kt file";
Collections.sort(sourceFiles);
myEnvironment = KotlinCoreEnvironment.createForTests(
getTestRootDisposable(),
KotlinTestUtils.newConfiguration(
ConfigurationKind.JDK_ONLY, TestJdkKind.MOCK_JDK,
CollectionsKt.plus(classPath, KotlinTestUtils.getAnnotationsJar()), Collections.emptyList()
),
EnvironmentConfigFiles.JVM_CONFIG_FILES);
loadFiles(ArrayUtil.toStringArray(sourceFiles));
List<OccurrenceInfo> expected = readExpectedOccurrences(KotlinTestUtils.getTestDataPathBase() + "/codegen/" + sourceFiles.get(0));
String actual = generateToText();
Companion.checkGeneratedTextAgainstExpectedOccurrences(actual, expected);
}
}
@@ -0,0 +1,77 @@
/*
* Copyright 2010-2017 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.ir
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.backend.common.AbstractClosureAnnotator
import org.jetbrains.kotlin.backend.common.Closure
import java.io.File
import java.io.PrintWriter
import java.io.StringWriter
abstract class AbstractClosureAnnotatorTestCase : AbstractIrGeneratorTestCase() {
override fun doTest(wholeFile: File, testFiles: List<TestFile>) {
val dir = wholeFile.parentFile
val ignoreErrors = shouldIgnoreErrors(wholeFile)
for ((testFile, irFile) in generateIrFilesAsSingleModule(testFiles, ignoreErrors)) {
doTestIrFileAgainstExpectations(dir, testFile, irFile)
}
}
private fun doTestIrFileAgainstExpectations(dir: File, testFile: TestFile, irFile: IrFile) {
val expectedFile = File(dir, testFile.name.replace(".kt", ".closure"))
val actualClosures = renderClosures(irFile)
KotlinTestUtils.assertEqualsToFile(expectedFile, actualClosures)
}
private fun renderClosures(irFile: IrFile): String {
val actualStringWriter = StringWriter()
val actualOut = PrintWriter(actualStringWriter)
irFile.acceptChildrenVoid(object : AbstractClosureAnnotator() {
override fun recordClassClosure(classDescriptor: ClassDescriptor, closure: Closure) {
actualOut.println("Closure for class ${classDescriptor.name}:")
printClosure(closure)
actualOut.println()
}
override fun recordFunctionClosure(functionDescriptor: FunctionDescriptor, closure: Closure) {
if (functionDescriptor is ConstructorDescriptor) {
actualOut.println("Closure for constructor ${functionDescriptor.containingDeclaration.name}:")
}
else {
actualOut.println("Closure for function ${functionDescriptor.name}:")
}
printClosure(closure)
actualOut.println()
}
private fun printClosure(closure: Closure) {
closure.capturedValues.forEach {
actualOut.println(" variable ${it.name}")
}
}
})
return actualStringWriter.toString()
}
}
@@ -0,0 +1,63 @@
/*
* Copyright 2010-2017 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.ir
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.name
import org.jetbrains.kotlin.ir2cfg.generators.FunctionGenerator
import org.jetbrains.kotlin.ir2cfg.util.dump
import org.jetbrains.kotlin.test.KotlinTestUtils
import java.io.File
abstract class AbstractIrCfgTestCase : AbstractIrGeneratorTestCase() {
private val IrFunction.name: String get() = this.descriptor.name.asString()
private fun IrFile.cfgDump(): String {
val builder = StringBuilder()
for (declaration in this.declarations) {
if (declaration is IrFunction) {
builder.appendln("// FUN: ${declaration.name}")
val cfg = FunctionGenerator(declaration).generate()
builder.appendln(cfg.dump())
builder.appendln("// END FUN: ${declaration.name}")
}
}
return builder.toString()
}
private fun IrModuleFragment.cfgDump(): String {
val builder = StringBuilder()
for (file in this.files) {
builder.appendln("// FILE: ${file.name}")
builder.appendln(file.cfgDump())
builder.appendln("// END FILE: ${file.name}")
builder.appendln()
}
return builder.toString()
}
override fun doTest(wholeFile: File, testFiles: List<TestFile>) {
val irModule = generateIrModule(false)
val irModuleDump = irModule.cfgDump()
val expectedPath = wholeFile.canonicalPath.replace(".kt", ".txt")
val expectedFile = File(expectedPath)
KotlinTestUtils.assertEqualsToFile(expectedFile, irModuleDump)
}
}
@@ -0,0 +1,135 @@
/*
* Copyright 2010-2017 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.ir
import junit.framework.TestCase
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.codegen.CodegenTestCase
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi2ir.Psi2IrConfiguration
import org.jetbrains.kotlin.psi2ir.Psi2IrTranslator
import org.jetbrains.kotlin.resolve.AnalyzingUtils
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil
import org.jetbrains.kotlin.test.ConfigurationKind
import org.jetbrains.kotlin.test.InTextDirectivesUtils
import org.jetbrains.kotlin.test.KotlinTestUtils.getAnnotationsJar
import java.io.File
import java.io.FileWriter
import java.io.PrintWriter
import java.util.*
abstract class AbstractIrGeneratorTestCase : CodegenTestCase() {
override fun doMultiFileTest(wholeFile: File, files: List<TestFile>, javaFilesDir: File?) {
setupEnvironment(files, javaFilesDir)
loadMultiFiles(files)
doTest(wholeFile, files)
}
private fun setupEnvironment(files: List<TestFile>, javaFilesDir: File?) {
val jdkKind = getJdkKind(files)
val javacOptions = ArrayList<String>(0)
var addRuntime = false
var addReflect = false
for (file in 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:"))
}
val configurationKind = when {
addReflect -> ConfigurationKind.ALL
addRuntime -> ConfigurationKind.NO_KOTLIN_REFLECT
else -> ConfigurationKind.JDK_ONLY
}
val configuration = createConfiguration(
configurationKind, jdkKind,
listOf<File>(getAnnotationsJar()),
arrayOf(javaFilesDir).filterNotNull(),
files
)
myEnvironment = KotlinCoreEnvironment.createForTests(testRootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
}
protected abstract fun doTest(wholeFile: File, testFiles: List<TestFile>)
protected fun generateIrModule(ignoreErrors: Boolean = false, shouldGenerate: (KtFile) -> Boolean = { true }): IrModuleFragment {
assert(myFiles != null) { "myFiles not initialized" }
assert(myEnvironment != null) { "myEnvironment not initialized" }
return generateIrModule(myFiles.psiFiles, myEnvironment, Psi2IrTranslator(Psi2IrConfiguration(ignoreErrors)), shouldGenerate)
}
protected fun generateIrFilesAsSingleModule(testFiles: List<TestFile>, ignoreErrors: Boolean = false): Map<TestFile, IrFile> {
val irModule = generateIrModule(ignoreErrors)
val ktFiles = testFiles.filter { it.name.endsWith(".kt") }
return ktFiles.zip(irModule.files).toMap()
}
companion object {
private val IGNORE_ERRORS_PATTERN = Regex("""// !IGNORE_ERRORS""")
internal fun shouldIgnoreErrors(wholeFile: File): Boolean =
IGNORE_ERRORS_PATTERN.containsMatchIn(wholeFile.readText())
internal fun createExpectedTextFile(testFile: TestFile, dir: File, fileName: String): File {
val textFile = File(dir, fileName)
if (!textFile.exists()) {
TestCase.assertTrue("Can't create an expected text containingFile: ${textFile.absolutePath}", textFile.createNewFile())
PrintWriter(FileWriter(textFile)).use {
it.println("$fileName: new expected text containingFile for ${testFile.name}")
}
}
return textFile
}
fun generateIrModule(
ktFilesToAnalyze: List<KtFile>,
environment: KotlinCoreEnvironment,
psi2ir: Psi2IrTranslator,
shouldGenerate: (KtFile) -> Boolean
): IrModuleFragment {
val analysisResult = JvmResolveUtil.analyze(ktFilesToAnalyze, environment)
if (!psi2ir.configuration.ignoreErrors) {
analysisResult.throwIfError()
AnalyzingUtils.throwExceptionOnErrors(analysisResult.bindingContext)
}
val fileToGenerate = ktFilesToAnalyze.filter { shouldGenerate(it) }
return generateIrModule(fileToGenerate, analysisResult.moduleDescriptor, analysisResult.bindingContext, psi2ir)
}
fun generateIrModule(ktFiles: List<KtFile>, moduleDescriptor: ModuleDescriptor, bindingContext: BindingContext, ignoreErrors: Boolean = false) =
generateIrModule(ktFiles, moduleDescriptor, bindingContext, Psi2IrTranslator(Psi2IrConfiguration(ignoreErrors)))
fun generateIrModule(ktFiles: List<KtFile>, moduleDescriptor: ModuleDescriptor, bindingContext: BindingContext, psi2ir: Psi2IrTranslator) =
psi2ir.generateModule(moduleDescriptor, ktFiles, bindingContext)
}
}
@@ -0,0 +1,64 @@
/*
* Copyright 2010-2017 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.ir
import org.jetbrains.kotlin.ir.util.RenderIrElementVisitor
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.utils.Printer
import java.io.File
abstract class AbstractIrSourceRangesTestCase : AbstractIrGeneratorTestCase() {
override fun doTest(wholeFile: File, testFiles: List<TestFile>) {
val dir = wholeFile.parentFile
val testFileToIrFile = generateIrFilesAsSingleModule(testFiles)
for ((testFile, irFile) in testFileToIrFile) {
val irFileDump = irFile.dumpWithSourceLocations(irFile.fileEntry)
val expectedSourceLocations = File(dir, testFile.name.replace(".kt", ".txt"))
KotlinTestUtils.assertEqualsToFile(expectedSourceLocations, irFileDump)
}
}
private fun IrElement.dumpWithSourceLocations(fileEntry: SourceManager.FileEntry): String =
StringBuilder().also {
acceptVoid(DumpSourceLocations(it, fileEntry))
}.toString()
private class DumpSourceLocations(
out: Appendable,
val fileEntry: SourceManager.FileEntry
) : IrElementVisitorVoid {
val printer = Printer(out, " ")
val elementRenderer = RenderIrElementVisitor()
override fun visitElement(element: IrElement) {
val sourceRangeInfo = fileEntry.getSourceRangeInfo(element.startOffset, element.endOffset)
printer.println("@${sourceRangeInfo.render()} ${element.accept(elementRenderer, null)}")
printer.pushIndent()
element.acceptChildrenVoid(this)
printer.popIndent()
}
private fun SourceRangeInfo.render() =
if (startLineNumber == endLineNumber)
"$startLineNumber:$startColumnNumber..$endColumnNumber"
else
"$startLineNumber:$startColumnNumber..$endLineNumber:$endColumnNumber"
}
}
@@ -0,0 +1,275 @@
/*
* Copyright 2010-2017 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.ir
import com.intellij.openapi.util.text.StringUtil
import junit.framework.TestCase
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrDeclarationReference
import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
import org.jetbrains.kotlin.ir.expressions.IrLocalDelegatedPropertyReference
import org.jetbrains.kotlin.ir.expressions.IrPropertyReference
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.utils.rethrow
import java.io.File
import java.util.regex.Pattern
abstract class AbstractIrTextTestCase : AbstractIrGeneratorTestCase() {
override fun doTest(wholeFile: File, testFiles: List<TestFile>) {
val dir = wholeFile.parentFile
val ignoreErrors = shouldIgnoreErrors(wholeFile)
val irModule = generateIrModule(ignoreErrors)
val ktFiles = testFiles.filter { it.name.endsWith(".kt") }
for ((testFile, irFile) in ktFiles.zip(irModule.files)) {
doTestIrFileAgainstExpectations(dir, testFile, irFile)
}
if (shouldDumpDependencies(wholeFile)) {
doTestIrModuleDependencies(wholeFile, irModule)
}
}
private fun doTestIrModuleDependencies(wholeFile: File, irModule: IrModuleFragment) {
irModule.dependencyModules.forEach { irDependencyModule ->
val actual = irDependencyModule.dump()
val sanitizedModuleName = StringUtil.sanitizeJavaIdentifier(irDependencyModule.descriptor.name.asString())
val expectedFileName = wholeFile.absolutePath.replace(".kt", "__$sanitizedModuleName.txt")
KotlinTestUtils.assertEqualsToFile(File(expectedFileName), actual)
}
}
protected fun doTestIrFileAgainstExpectations(dir: File, testFile: TestFile, irFile: IrFile) {
if (testFile.isExternalFile()) return
val expectations = parseExpectations(dir, testFile)
val irFileDump = irFile.dump()
val expected = StringBuilder()
val actual = StringBuilder()
for (expectation in expectations.regexps) {
expected.append(expectation.numberOfOccurrences).append(" ").append(expectation.needle).append("\n")
val actualCount = StringUtil.findMatches(irFileDump, Pattern.compile("(" + expectation.needle + ")")).size
actual.append(actualCount).append(" ").append(expectation.needle).append("\n")
}
for (irTreeFileLabel in expectations.irTreeFileLabels) {
val actualTrees = irFile.dumpTreesFromLineNumber(irTreeFileLabel.lineNumber)
KotlinTestUtils.assertEqualsToFile(irTreeFileLabel.expectedTextFile, actualTrees)
verify(irFile)
// Check that deep copy produces an equivalent result
val irFileCopy = irFile.deepCopyWithSymbols()
val copiedTrees = irFileCopy.dumpTreesFromLineNumber(irTreeFileLabel.lineNumber)
TestCase.assertEquals("IR dump mismatch after deep copy with symbols", actualTrees, copiedTrees)
verify(irFileCopy)
val irFileCopyOld = irFile.deepCopyOld()
val copiedTreesOld = irFileCopyOld.dumpTreesFromLineNumber(irTreeFileLabel.lineNumber)
TestCase.assertEquals("IR dump mismatch after old deep copy", actualTrees, copiedTreesOld)
}
try {
TestCase.assertEquals(irFileDump, expected.toString(), actual.toString())
}
catch (e: Throwable) {
println(irFileDump)
throw rethrow(e)
}
}
private fun verify(irFile: IrFile) {
IrVerifier().verifyWithAssert(irFile)
}
private class IrVerifier : IrElementVisitorVoid {
private val errors = ArrayList<String>()
private val symbolForDeclaration = HashMap<IrElement, IrSymbol>()
val hasErrors get() = errors.isNotEmpty()
val errorsAsMessage get() = errors.joinToString(prefix = "IR verifier errors:\n", separator = "\n")
private fun error(message: String) {
errors.add(message)
}
fun verifyWithAssert(irFile: IrFile) {
irFile.acceptChildrenVoid(this)
TestCase.assertFalse(errorsAsMessage + "\n\n\n" + irFile.dump(), hasErrors)
}
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitDeclaration(declaration: IrDeclaration) {
if (declaration is IrSymbolOwner) {
declaration.symbol.checkBinding("decl", declaration)
if (declaration.symbol.owner != declaration) {
error("Symbol is not bound to declaration: ${declaration.render()}")
}
}
}
override fun visitFunction(declaration: IrFunction) {
visitDeclaration(declaration)
val functionDescriptor = declaration.descriptor
checkTypeParameters(functionDescriptor, declaration, functionDescriptor.typeParameters)
val expectedDispatchReceiver = functionDescriptor.dispatchReceiverParameter
val actualDispatchReceiver = declaration.dispatchReceiverParameter?.descriptor
if (expectedDispatchReceiver != actualDispatchReceiver) {
error("$functionDescriptor: Dispatch receiver parameter mismatch: " +
"expected $expectedDispatchReceiver, actual $actualDispatchReceiver")
}
val expectedExtensionReceiver = functionDescriptor.extensionReceiverParameter
val actualExtensionReceiver = declaration.extensionReceiverParameter?.descriptor
if (expectedExtensionReceiver != actualExtensionReceiver) {
error("$functionDescriptor: Extension receiver parameter mismatch: " +
"expected $expectedExtensionReceiver, actual $actualExtensionReceiver")
}
val declaredValueParameters = declaration.valueParameters.map { it.descriptor }
val actualValueParameters = functionDescriptor.valueParameters
if (declaredValueParameters.size != actualValueParameters.size) {
error("$functionDescriptor: Value parameters mismatch: $declaredValueParameters != $actualValueParameters")
}
else {
declaredValueParameters.zip(actualValueParameters).forEach { (declaredValueParameter, actualValueParameter) ->
if (declaredValueParameter != actualValueParameter) {
error("$functionDescriptor: Value parameters mismatch: $declaredValueParameter != $actualValueParameter")
}
}
}
}
override fun visitDeclarationReference(expression: IrDeclarationReference) {
expression.symbol.checkBinding("ref", expression)
}
override fun visitFunctionReference(expression: IrFunctionReference) {
expression.symbol.checkBinding("ref", expression)
}
override fun visitPropertyReference(expression: IrPropertyReference) {
expression.field?.checkBinding("field", expression)
expression.getter?.checkBinding("getter", expression)
expression.setter?.checkBinding("setter", expression)
}
override fun visitLocalDelegatedPropertyReference(expression: IrLocalDelegatedPropertyReference) {
expression.delegate.checkBinding("delegate", expression)
expression.getter.checkBinding("getter", expression)
expression.setter?.checkBinding("setter", expression)
}
private fun IrSymbol.checkBinding(kind: String, irElement: IrElement) {
if (!isBound) {
error("${javaClass.simpleName} $descriptor is unbound @$kind ${irElement.render()}")
}
val otherSymbol = symbolForDeclaration.getOrPut(owner) { this }
if (this != otherSymbol) {
error("Multiple symbols for $descriptor @$kind ${irElement.render()}")
}
}
override fun visitClass(declaration: IrClass) {
visitDeclaration(declaration)
checkTypeParameters(declaration.descriptor, declaration, declaration.descriptor.declaredTypeParameters)
}
private fun checkTypeParameters(descriptor: DeclarationDescriptor, declaration: IrTypeParametersContainer, expectedTypeParameters: List<TypeParameterDescriptor>) {
val declaredTypeParameters = declaration.typeParameters.map { it.descriptor }
if (declaredTypeParameters.size != expectedTypeParameters.size) {
error("$descriptor: Type parameters mismatch: $declaredTypeParameters != $expectedTypeParameters")
}
else {
declaredTypeParameters.zip(expectedTypeParameters).forEach { (declaredTypeParameter, expectedTypeParameter) ->
if (declaredTypeParameter != expectedTypeParameter) {
error("$descriptor: Type parameters mismatch: $declaredTypeParameter != $expectedTypeParameter")
}
}
}
}
}
internal class Expectations(val regexps: List<RegexpInText>, val irTreeFileLabels: List<IrTreeFileLabel>)
internal class RegexpInText(val numberOfOccurrences: Int, val needle: String) {
constructor(countStr: String, needle: String) : this(Integer.valueOf(countStr), needle)
}
internal class IrTreeFileLabel(val expectedTextFile: File, val lineNumber: Int)
companion object {
private val EXPECTED_OCCURRENCES_PATTERN = Regex("""^\s*//\s*(\d+)\s*(.*)$""")
private val IR_FILE_TXT_PATTERN = Regex("""// IR_FILE: (.*)$""")
private val DUMP_DEPENDENCIES_PATTERN = Regex("""// !DUMP_DEPENDENCIES""")
private val EXTERNAL_FILE_PATTERN = Regex("""// EXTERNAL_FILE""")
internal fun shouldDumpDependencies(wholeFile: File): Boolean =
DUMP_DEPENDENCIES_PATTERN.containsMatchIn(wholeFile.readText())
internal fun TestFile.isExternalFile() =
EXTERNAL_FILE_PATTERN.containsMatchIn(content)
internal fun KtFile.isExternalFile() =
EXTERNAL_FILE_PATTERN.containsMatchIn(text)
internal fun parseExpectations(dir: File, testFile: TestFile): Expectations {
val regexps = ArrayList<RegexpInText>()
val treeFiles = ArrayList<IrTreeFileLabel>()
for (line in testFile.content.split("\n")) {
EXPECTED_OCCURRENCES_PATTERN.matchEntire(line)?.let { matchResult ->
regexps.add(RegexpInText(matchResult.groupValues[1], matchResult.groupValues[2].trim()))
}
?: IR_FILE_TXT_PATTERN.find(line)?.let { matchResult ->
val fileName = matchResult.groupValues[1].trim()
val file = createExpectedTextFile(testFile, dir, fileName)
treeFiles.add(IrTreeFileLabel(file, 0))
}
}
if (treeFiles.isEmpty()) {
val file = createExpectedTextFile(testFile, dir, testFile.name.replace(".kt", ".txt"))
treeFiles.add(IrTreeFileLabel(file, 0))
}
return Expectations(regexps, treeFiles)
}
}
}
@@ -0,0 +1,33 @@
/*
* Copyright 2010-2017 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.ir
import org.jetbrains.kotlin.ir.util.dump
import org.jetbrains.kotlin.test.KotlinTestUtils
import java.io.File
abstract class AbstractPsi2IrBoxTestCase : AbstractIrGeneratorTestCase() {
override fun doTest(wholeFile: File, testFiles: List<TestFile>) {
val irModule = generateIrModule(false)
val irModuleDump = irModule.dump()
val expectedPath = wholeFile.canonicalPath
// .replace("/codegen/box/", "/ir/irTextBox/")
.replace(".kt", ".txt")
val expectedFile = File(expectedPath)
KotlinTestUtils.assertEqualsToFile(expectedFile, irModuleDump)
}
}
@@ -0,0 +1,46 @@
/*
* Copyright 2010-2017 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.ir
import org.jetbrains.kotlin.checkers.AbstractDiagnosticsTest
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.ir.util.dump
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.test.KotlinTestUtils
import java.io.File
abstract class AbstractPsi2IrDiagnosticsTest : AbstractDiagnosticsTest() {
override fun performAdditionalChecksAfterDiagnostics(
testDataFile: File,
testFiles: List<TestFile>,
moduleFiles: Map<TestModule?, List<TestFile>>,
moduleDescriptors: Map<TestModule?, ModuleDescriptorImpl>,
moduleBindings: Map<TestModule?, BindingContext>
) {
val actualIrDump = StringBuilder()
for (module in moduleFiles.keys) {
val ktFiles = moduleFiles[module]?.mapNotNull { it.ktFile } ?: continue
val moduleDescriptor = moduleDescriptors[module] ?: continue
val moduleBindingContext = moduleBindings[module] ?: continue
val irModule = AbstractIrGeneratorTestCase.generateIrModule(ktFiles, moduleDescriptor, moduleBindingContext, true)
actualIrDump.append(irModule.dump())
}
val expectedIrFile = File(testDataFile.canonicalPath.replace(".kt", ".ir.txt"))
KotlinTestUtils.assertEqualsToFile(expectedIrFile, actualIrDump.toString())
}
}
@@ -0,0 +1,129 @@
/*
* Copyright 2010-2017 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 org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.javac.JavacWrapper
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.renderer.*
import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil
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.junit.Assert
import java.io.File
import java.io.IOException
import java.lang.annotation.Retention
import org.jetbrains.kotlin.test.KotlinTestUtils.*
import org.jetbrains.kotlin.test.util.RecursiveDescriptorComparator.validateAndCompareDescriptorWithFile
abstract class AbstractCompileJavaAgainstKotlinTest : TestCaseWithTmpdir() {
@Throws(IOException::class)
protected fun doTestWithJavac(ktFilePath: String) {
doTest(ktFilePath, true)
}
@Throws(IOException::class)
protected fun doTestWithoutJavac(ktFilePath: String) {
doTest(ktFilePath, false)
}
@Throws(IOException::class)
protected fun doTest(ktFilePath: String, useJavac: Boolean) {
Assert.assertTrue(ktFilePath.endsWith(".kt"))
val ktFile = File(ktFilePath)
val javaFile = File(ktFilePath.replaceFirst("\\.kt$".toRegex(), ".java"))
val javaErrorFile = File(ktFilePath.replaceFirst("\\.kt$".toRegex(), ".javaerr.txt"))
val out = File(tmpdir, "out")
val compiledSuccessfully = if (useJavac) {
compileKotlinWithJava(listOf(javaFile),
listOf(ktFile),
out, testRootDisposable)
} else {
KotlinTestUtils.compileKotlinWithJava(listOf(javaFile),
listOf(ktFile),
out, testRootDisposable, javaErrorFile)
}
if (!compiledSuccessfully) return
val environment = KotlinCoreEnvironment.createForTests(
testRootDisposable,
newConfiguration(ConfigurationKind.ALL, TestJdkKind.FULL_JDK, getAnnotationsJar(), out),
EnvironmentConfigFiles.JVM_CONFIG_FILES
)
val analysisResult = JvmResolveUtil.analyze(environment)
val packageView = analysisResult.moduleDescriptor.getPackage(LoadDescriptorUtil.TEST_PACKAGE_FQNAME)
assertFalse("Nothing found in package ${LoadDescriptorUtil.TEST_PACKAGE_FQNAME}", packageView.isEmpty())
val expectedFile = File(ktFilePath.replaceFirst("\\.kt$".toRegex(), ".txt"))
validateAndCompareDescriptorWithFile(packageView, CONFIGURATION, expectedFile)
}
@Throws(IOException::class)
fun compileKotlinWithJava(
javaFiles: List<File>,
ktFiles: List<File>,
outDir: File,
disposable: Disposable
): Boolean {
val environment = createEnvironmentWithMockJdkAndIdeaAnnotations(disposable)
environment.configuration.put(JVMConfigurationKeys.USE_JAVAC, true)
environment.configuration.put(JVMConfigurationKeys.COMPILE_JAVA, true)
environment.configuration.put(JVMConfigurationKeys.OUTPUT_DIRECTORY, outDir)
environment.configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, MessageCollector.NONE)
environment.registerJavac(javaFiles = javaFiles,
kotlinFiles = listOf(KotlinTestUtils.loadJetFile(environment.project, ktFiles.first())))
if (!ktFiles.isEmpty()) {
LoadDescriptorUtil.compileKotlinToDirAndGetModule(ktFiles, outDir, environment)
}
else {
val mkdirs = outDir.mkdirs()
assert(mkdirs) { "Not created: $outDir" }
}
return JavacWrapper.getInstance(environment.project).use { it.compile() }
}
companion object {
// Do not render parameter names because there are test cases where classes inherit from JDK collections,
// and some versions of JDK have debug information in the class files (including parameter names), and some don't
private val CONFIGURATION = AbstractLoadJavaTest.COMPARATOR_CONFIGURATION.withRenderer(
DescriptorRenderer.withOptions {
withDefinedIn = false
parameterNameRenderingPolicy = ParameterNameRenderingPolicy.NONE
verbose = true
annotationArgumentsRenderingPolicy = AnnotationArgumentsRenderingPolicy.UNLESS_EMPTY
excludedAnnotationClasses = setOf(FqName(Retention::class.java.name))
modifiers = DescriptorRendererModifier.ALL
}
)
}
}
@@ -0,0 +1,111 @@
/*
* Copyright 2010-2017 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 org.jetbrains.kotlin.javac.JavacWrapper
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.renderer.*
import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil
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.junit.Assert
import java.io.File
import java.io.IOException
import java.lang.annotation.Retention
import org.jetbrains.kotlin.test.KotlinTestUtils.*
import org.jetbrains.kotlin.test.util.RecursiveDescriptorComparator.validateAndCompareDescriptorWithFile
abstract class AbstractCompileKotlinAgainstJavaTest : TestCaseWithTmpdir() {
@Throws(IOException::class)
protected fun doTest(ktFilePath: String) {
Assert.assertTrue(ktFilePath.endsWith(".kt"))
val ktFile = File(ktFilePath)
val javaFile = File(ktFilePath.replaceFirst("\\.kt$".toRegex(), ".java"))
val out = File(tmpdir, "out")
val compiledSuccessfully = compileKotlinWithJava(listOf(javaFile),
listOf(ktFile),
out, testRootDisposable)
if (!compiledSuccessfully) return
val environment = KotlinCoreEnvironment.createForTests(
testRootDisposable,
newConfiguration(ConfigurationKind.ALL, TestJdkKind.MOCK_JDK, getAnnotationsJar(), out),
EnvironmentConfigFiles.JVM_CONFIG_FILES
)
environment.configuration.put(JVMConfigurationKeys.USE_JAVAC, true)
environment.configuration.put(JVMConfigurationKeys.OUTPUT_DIRECTORY, out)
environment.registerJavac(emptyList<File>())
val analysisResult = JvmResolveUtil.analyze(environment)
val packageView = analysisResult.moduleDescriptor.getPackage(LoadDescriptorUtil.TEST_PACKAGE_FQNAME)
assertFalse("Nothing found in package ${LoadDescriptorUtil.TEST_PACKAGE_FQNAME}", packageView.isEmpty())
val expectedFile = File(ktFilePath.replaceFirst("\\.kt$".toRegex(), ".txt"))
validateAndCompareDescriptorWithFile(packageView, CONFIGURATION, expectedFile)
}
@Throws(IOException::class)
fun compileKotlinWithJava(
javaFiles: List<File>,
ktFiles: List<File>,
outDir: File,
disposable: Disposable
): Boolean {
val environment = createEnvironmentWithMockJdkAndIdeaAnnotations(disposable)
environment.configuration.put(JVMConfigurationKeys.USE_JAVAC, true)
environment.configuration.put(JVMConfigurationKeys.COMPILE_JAVA, true)
environment.configuration.put(JVMConfigurationKeys.OUTPUT_DIRECTORY, outDir)
environment.registerJavac(javaFiles = javaFiles,
kotlinFiles = listOf(KotlinTestUtils.loadJetFile(environment.project, ktFiles.first())),
arguments = arrayOf("-proc:none"))
if (!ktFiles.isEmpty()) {
LoadDescriptorUtil.compileKotlinToDirAndGetModule(ktFiles, outDir, environment)
}
else {
val mkdirs = outDir.mkdirs()
assert(mkdirs) { "Not created: $outDir" }
}
return JavacWrapper.getInstance(environment.project).use { it.compile() }
}
companion object {
// Do not render parameter names because there are test cases where classes inherit from JDK collections,
// and some versions of JDK have debug information in the class files (including parameter names), and some don't
private val CONFIGURATION = AbstractLoadJavaTest.COMPARATOR_CONFIGURATION.withRenderer(
DescriptorRenderer.withOptions {
withDefinedIn = false
parameterNameRenderingPolicy = ParameterNameRenderingPolicy.NONE
verbose = true
annotationArgumentsRenderingPolicy = AnnotationArgumentsRenderingPolicy.UNLESS_EMPTY
excludedAnnotationClasses = setOf(FqName(Retention::class.java.name))
modifiers = DescriptorRendererModifier.ALL
}
)
}
}
@@ -0,0 +1,163 @@
/*
* Copyright 2010-2017 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.io.FileUtil
import com.intellij.util.io.ZipUtil
import org.jetbrains.kotlin.cli.AbstractCliTest
import org.jetbrains.kotlin.cli.common.CLICompiler
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.test.KotlinTestUtils
import org.jetbrains.kotlin.test.TestCaseWithTmpdir
import java.io.File
import java.util.jar.JarOutputStream
import java.util.jar.Manifest
import java.util.regex.Pattern
abstract class AbstractKotlinCompilerIntegrationTest : TestCaseWithTmpdir() {
protected abstract val testDataPath: String
protected val testDataDirectory: File
get() = File(testDataPath, getTestName(true))
protected fun getTestDataFileWithExtension(extension: String): File {
return File(testDataDirectory, "${getTestName(true)}.$extension")
}
class JavaCompilationError : AssertionError("Java files are not compiled successfully")
/**
* Compiles all sources (.java and .kt) under the directory named [libraryName] to [destination].
* [destination] should be either a path to the directory under [tmpdir], or a path to the resulting .jar file (also under [tmpdir]).
* Kotlin sources are compiled first, and there should be no errors or warnings. Java sources are compiled next.
*
* @return [destination]
*/
protected fun compileLibrary(
libraryName: String,
destination: File = File(tmpdir, "$libraryName.jar"),
additionalOptions: List<String> = emptyList(),
compileJava: (sourceDir: File, javaFiles: List<File>, outputDir: File) -> Boolean = { _, javaFiles, outputDir ->
KotlinTestUtils.compileJavaFiles(javaFiles, listOf("-d", outputDir.path))
},
checkKotlinOutput: (String) -> Unit = { actual -> assertEquals(normalizeOutput("" to ExitCode.OK), actual) },
manifest: Manifest? = null,
vararg extraClassPath: File
): File {
val sourceDir = File(testDataDirectory, libraryName)
val javaFiles = FileUtil.findFilesByMask(JAVA_FILES, sourceDir)
val kotlinFiles = FileUtil.findFilesByMask(KOTLIN_FILES, sourceDir)
assert(javaFiles.isNotEmpty() || kotlinFiles.isNotEmpty()) { "There should be either .kt or .java files in the directory" }
val isJar = destination.name.endsWith(".jar")
val outputDir = if (isJar) File(tmpdir, "output-$libraryName") else destination
if (kotlinFiles.isNotEmpty()) {
val output = compileKotlin(libraryName, outputDir, extraClassPath.toList(), K2JVMCompiler(), additionalOptions, expectedFileName = null)
checkKotlinOutput(normalizeOutput(output))
}
if (javaFiles.isNotEmpty()) {
outputDir.mkdirs()
if (!compileJava(sourceDir, javaFiles, outputDir)) {
throw JavaCompilationError()
}
}
if (isJar) {
destination.delete()
val stream =
if (manifest != null) JarOutputStream(destination.outputStream(), manifest)
else JarOutputStream(destination.outputStream())
stream.use { jar ->
ZipUtil.addDirToZipRecursively(jar, destination, outputDir, "", null, null)
}
}
else assertNull("Manifest is ignored if destination is not a .jar file", manifest)
return destination
}
/**
* Compiles all .kt sources under the directory named [libraryName] to a file named "[libraryName].js" in [tmpdir]
*
* @return the path to the corresponding .meta.js file, i.e. "[libraryName].meta.js"
*/
protected fun compileJsLibrary(libraryName: String): File {
val destination = File(tmpdir, "$libraryName.js")
val output = compileKotlin(libraryName, destination, compiler = K2JSCompiler(), expectedFileName = null)
assertEquals(normalizeOutput("" to ExitCode.OK), normalizeOutput(output))
return File(tmpdir, "$libraryName.meta.js")
}
private fun normalizeOutput(output: Pair<String, ExitCode>): String {
return AbstractCliTest.getNormalizedCompilerOutput(output.first, output.second, testDataDirectory.path)
.replace(FileUtil.toSystemIndependentName(tmpdir.absolutePath), "\$TMP_DIR\$")
}
protected fun compileKotlin(
fileName: String,
output: File,
classpath: List<File> = emptyList(),
compiler: CLICompiler<*> = K2JVMCompiler(),
additionalOptions: List<String> = emptyList(),
expectedFileName: String? = "output.txt"
): Pair<String, ExitCode> {
val args = mutableListOf<String>()
val sourceFile = File(testDataDirectory, fileName)
assert(sourceFile.exists()) { "Source file does not exist: ${sourceFile.absolutePath}" }
args.add(sourceFile.path)
if (compiler is K2JSCompiler) {
if (classpath.isNotEmpty()) {
args.add("-libraries")
args.add(classpath.joinToString(File.pathSeparator))
}
args.add("-output")
args.add(output.path)
args.add("-meta-info")
}
else if (compiler is K2JVMCompiler) {
if (classpath.isNotEmpty()) {
args.add("-classpath")
args.add(classpath.joinToString(File.pathSeparator))
}
args.add("-d")
args.add(output.path)
}
else {
throw UnsupportedOperationException(compiler.toString())
}
args.addAll(additionalOptions)
val result = AbstractCliTest.executeCompilerGrabOutput(compiler, args)
if (expectedFileName != null) {
KotlinTestUtils.assertEqualsToFile(File(testDataDirectory, expectedFileName), normalizeOutput(result))
}
return result
}
companion object {
private val KOTLIN_FILES = Pattern.compile(".*\\.kt$")
@JvmStatic
protected val JAVA_FILES = Pattern.compile(".*\\.java$")!!
}
}
@@ -0,0 +1,23 @@
/*
* Copyright 2010-2017 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
abstract class AbstractLoadKotlinWithTypeTableTest : AbstractLoadJavaTest() {
protected fun doTest(fileName: String) {
doTestCompiledKotlinWithTypeTable(fileName)
}
}
@@ -0,0 +1,273 @@
/*
* Copyright 2010-2017 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.google.common.io.Closeables
import com.google.common.io.Files
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.util.io.FileUtil
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.codegen.GenerationUtils
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.sure
import org.jetbrains.org.objectweb.asm.*
import org.junit.Assert
import java.io.File
import java.io.FileInputStream
import java.nio.charset.Charset
import java.util.*
import java.util.regex.MatchResult
abstract class AbstractWriteSignatureTest : TestCaseWithTmpdir() {
private var environment: KotlinCoreEnvironment? = null
override fun setUp() {
super.setUp()
environment = KotlinTestUtils.createEnvironmentWithJdkAndNullabilityAnnotationsFromIdea(
myTestRootDisposable, ConfigurationKind.ALL, jdkKind
)
}
protected open val jdkKind: TestJdkKind
get() = TestJdkKind.MOCK_JDK
override fun tearDown() {
environment = null
super.tearDown()
}
protected fun doTest(ktFileName: String) {
val ktFile = File(ktFileName)
val text = FileUtil.loadFile(ktFile, true)
val psiFile = KotlinTestUtils.createFile(ktFile.name, text, environment!!.project)
val fileFactory = GenerationUtils.compileFileTo(psiFile, environment!!, tmpdir)
Disposer.dispose(myTestRootDisposable)
val expectations = parseExpectations(ktFile)
try {
expectations.check()
}
catch (e: Throwable) {
println(fileFactory.createText())
throw e
}
}
private class SignatureExpectation(val header: String, val name: String, val expectedJvmSignature: String?, expectedGenericSignature: String) {
private val expectedFormattedSignature = formatSignature(header, expectedJvmSignature, expectedGenericSignature)
private val jvmDescriptorToFormattedSignature = mutableMapOf<String, String>()
fun accept(name: String, actualJvmSignature: String, actualGenericSignature: String) {
if (this.name == name) {
Assert.assertFalse(jvmDescriptorToFormattedSignature.containsKey(actualJvmSignature))
jvmDescriptorToFormattedSignature[actualJvmSignature] =
formatSignature(header, expectedJvmSignature?.let { actualJvmSignature }, actualGenericSignature)
}
}
fun check() {
val formattedActualSignature =
if (expectedJvmSignature == null) {
Assert.assertTrue(
"Expected single declaration, but ${jvmDescriptorToFormattedSignature.keys} found",
jvmDescriptorToFormattedSignature.size == 1)
jvmDescriptorToFormattedSignature.values.single()
}
else {
jvmDescriptorToFormattedSignature[expectedJvmSignature].sure {
"Expected $expectedJvmSignature but only ${jvmDescriptorToFormattedSignature.keys} found for $name"
}
}
Assert.assertEquals(expectedFormattedSignature, formattedActualSignature)
}
}
private inner class PackageExpectationsSuite() {
private val classSuitesByClassName = LinkedHashMap<String, ClassExpectationsSuite>()
fun getOrCreateClassSuite(className: String): ClassExpectationsSuite =
classSuitesByClassName.getOrPut(className) { ClassExpectationsSuite(className) }
fun check() {
Assert.assertTrue(classSuitesByClassName.isNotEmpty())
classSuitesByClassName.values.forEach { it.check() }
}
}
private inner class ClassExpectationsSuite(val className: String) {
val classExpectations = ArrayList<SignatureExpectation>()
val methodExpectations = ArrayList<SignatureExpectation>()
val fieldExpectations = ArrayList<SignatureExpectation>()
fun check() {
val checker = Checker()
val classFileName = "$tmpdir/${className.replace('.', '/')}.class"
val classFile = File(classFileName)
processClassFile(checker, classFile)
if (className.endsWith("Package")) {
// This class is a package facade. We should also check package parts.
processPackageParts(checker, classFile)
}
checkCollectedSignatures()
}
private fun processPackageParts(checker: Checker, classFile: File) {
// Look for package parts in the same directory.
// Package part file names for package SomePackage look like SomePackage$<hash>.class.
val classDir = classFile.parentFile
val classLastName = classFile.name
val packageFacadePrefix = classLastName.replace(".class", "\$")
classDir.listFiles { _, lastName ->
lastName.startsWith(packageFacadePrefix) && lastName.endsWith(".class")
}.forEach { packageFacadeFile ->
processClassFile(checker, packageFacadeFile)
}
}
private fun checkCollectedSignatures() {
(classExpectations + methodExpectations + fieldExpectations).forEach(SignatureExpectation::check)
}
private fun processClassFile(checker: Checker, classFile: File) {
val classInputStream = FileInputStream(classFile)
try {
ClassReader(classInputStream).accept(checker,
ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG or ClassReader.SKIP_FRAMES)
}
finally {
Closeables.closeQuietly(classInputStream)
}
}
private inner class Checker : ClassVisitor(Opcodes.ASM5) {
override fun visit(version: Int, access: Int, name: String, signature: String?, superName: String?, interfaces: Array<out String>?) {
classExpectations.forEach { it.accept(name, name, signature ?: "null") }
super.visit(version, access, name, signature, superName, interfaces)
}
override fun visitMethod(access: Int, name: String, desc: String, signature: String?, exceptions: Array<out String>?): MethodVisitor? {
methodExpectations.forEach { it.accept(name, desc, signature ?: "null") }
return super.visitMethod(access, name, desc, signature, exceptions)
}
override fun visitField(access: Int, name: String, desc: String, signature: String?, value: Any?): FieldVisitor? {
fieldExpectations.forEach { it.accept(name, desc, signature ?: "null") }
return super.visitField(access, name, desc, signature, value)
}
}
fun addClassExpectation(name: String, jvmSignature: String?, genericSignature: String) {
classExpectations.add(SignatureExpectation("class: $name", name, jvmSignature, genericSignature))
}
fun addFieldExpectation(className: String, memberName: String, jvmSignature: String?, genericSignature: String) {
fieldExpectations.add(SignatureExpectation("field: $className::$memberName", memberName, jvmSignature, genericSignature))
}
fun addMethodExpectation(className: String, memberName: String, jvmSignature: String?, genericSignature: String) {
methodExpectations.add(SignatureExpectation("method: $className::$memberName", memberName, jvmSignature, genericSignature))
}
}
private fun parseExpectations(ktFile: File): PackageExpectationsSuite {
val expectations = PackageExpectationsSuite()
val lines = Files.readLines(ktFile, Charset.forName("utf-8"))
var lineNo = 0
while (lineNo < lines.size) {
val line = lines[lineNo]
val expectationMatch = expectationRegex.matchExact(line)
if (expectationMatch != null) {
val kind = expectationMatch.group(1)!!
val className = expectationMatch.group(2)!!
val memberName = expectationMatch.group(4)
if (kind == "class" && memberName != null) {
throw AssertionError("$ktFile:${lineNo+1}: use $className\$$memberName to denote inner class")
}
val jvmSignatureMatch = jvmSignatureRegex.matchExact(lines[lineNo + 1])
val genericSignatureMatch = genericSignatureRegex.matchExact(lines[lineNo + 1])
?: genericSignatureRegex.matchExact(lines[lineNo + 2])
if (genericSignatureMatch != null) {
val jvmSignature = jvmSignatureMatch?.group(1)
val genericSignature = genericSignatureMatch.group(1)
val classSuite = expectations.getOrCreateClassSuite(className)
when (kind) {
"class" -> classSuite.addClassExpectation(className, jvmSignature, genericSignature)
"field" -> classSuite.addFieldExpectation(className, memberName, jvmSignature, genericSignature)
"method" -> classSuite.addMethodExpectation(className, memberName, jvmSignature, genericSignature)
else -> throw AssertionError("$ktFile:${lineNo+1}: unsupported expectation kind: $kind")
}
// Expectation, skip the following 'jvm signature' and 'generic signature' lines
lineNo += 3
}
else {
throw AssertionError("$ktFile:${lineNo+1}: '$kind' should be followed by 'jvm signature' and 'generic signature'")
}
}
else {
++lineNo
}
}
return expectations
}
companion object {
fun formatSignature(header: String, jvmSignature: String?, genericSignature: String): String {
return listOf(
header,
jvmSignature?.let { "jvm signature: $it" },
"generic signature: $genericSignature"
).filterNotNull().joinToString("\n") { "// $it" }
}
val expectationRegex = Regex("^// (class|method|field): *([^:]+)(::(.+))? *(//.*)?")
val jvmSignatureRegex = Regex("^// jvm signature: *(.+) *(//.*)?")
val genericSignatureRegex = Regex("^// generic signature: *(.+) *(//.*)?")
fun Regex.matchExact(input: String): MatchResult? {
val matcher = this.toPattern().matcher(input)
if (matcher.matches()) {
return matcher.toMatchResult()
}
else {
return null
}
}
}
}
@@ -0,0 +1,89 @@
/*
* 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.serialization
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.JVMConfigurationKeys
import org.jetbrains.kotlin.container.get
import org.jetbrains.kotlin.jvm.compiler.LoadDescriptorUtil
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
import org.jetbrains.kotlin.load.kotlin.DeserializationComponentsForJava
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil
import org.jetbrains.kotlin.test.*
import org.jetbrains.kotlin.test.util.RecursiveDescriptorComparator
import java.io.File
import java.net.URLClassLoader
abstract class AbstractLocalClassProtoTest : TestCaseWithTmpdir() {
protected fun doTest(filename: String) {
val source = File(filename)
KotlinTestUtils.createEnvironmentWithMockJdkAndIdeaAnnotations(testRootDisposable).let { environment ->
LoadDescriptorUtil.compileKotlinToDirAndGetModule(listOf(source), tmpdir, environment)
}
val classNameSuffix = InTextDirectivesUtils.findStringWithPrefixes(source.readText(), "// CLASS_NAME_SUFFIX: ")
?: error("CLASS_NAME_SUFFIX directive not found in test data")
val classLoader = URLClassLoader(arrayOf(tmpdir.toURI().toURL()), ForTestCompileRuntime.runtimeAndReflectJarClassLoader())
val classFile = tmpdir.walkTopDown().singleOrNull { it.path.endsWith("$classNameSuffix.class") }
?: error("Local class with suffix `$classNameSuffix` is not found in: ${tmpdir.listFiles().toList()}")
val clazz = classLoader.loadClass(classFile.toRelativeString(tmpdir).substringBeforeLast(".class").replace('/', '.').replace('\\', '.'))
assertHasAnnotationData(clazz)
val configuration = KotlinTestUtils.newConfiguration(ConfigurationKind.ALL, TestJdkKind.MOCK_JDK, tmpdir).apply {
// This is needed because otherwise local classes, being binary classes, are not resolved via source module's container.
// They could only be resolved via the container of the _dependency_ module.
// This can be improved as soon as there's an API to get the container of the dependency module.
put(JVMConfigurationKeys.USE_SINGLE_MODULE, true)
}
val environment = KotlinCoreEnvironment.createForTests(testRootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
val components = JvmResolveUtil.createContainer(environment).get<DeserializationComponentsForJava>().components
val classDescriptor = components.classDeserializer.deserializeClass(clazz.classId)
?: error("Class is not resolved: $clazz (classId = ${clazz.classId})")
RecursiveDescriptorComparator.validateAndCompareDescriptorWithFile(
classDescriptor,
RecursiveDescriptorComparator.DONT_INCLUDE_METHODS_OF_OBJECT,
KotlinTestUtils.replaceExtension(source, "txt")
)
}
@Suppress("UNCHECKED_CAST")
private fun assertHasAnnotationData(clazz: Class<*>) {
checkNotNull(clazz.getAnnotation(
clazz.classLoader.loadClass(JvmAnnotationNames.METADATA_FQ_NAME.asString()) as Class<Annotation>
)) { "Metadata annotation is not found for class $clazz" }
}
private val Class<*>.classId: ClassId
get() = when {
enclosingMethod != null || enclosingConstructor != null || simpleName.isEmpty() -> {
val fqName = FqName(name)
ClassId(fqName.parent(), FqName.topLevel(fqName.shortName()), true)
}
else -> declaringClass?.classId?.createNestedClassId(Name.identifier(simpleName)) ?: ClassId.topLevel(FqName(name))
}
}
@@ -0,0 +1,62 @@
/*
* Copyright 2010-2017 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.serialization.builtins
import org.jetbrains.kotlin.builtins.BuiltInsPackageFragment
import org.jetbrains.kotlin.builtins.KotlinBuiltIns.*
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.renderer.AnnotationArgumentsRenderingPolicy
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.renderer.DescriptorRendererModifier
import org.jetbrains.kotlin.renderer.OverrideRenderingPolicy
import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil
import org.jetbrains.kotlin.test.ConfigurationKind
import org.jetbrains.kotlin.test.KotlinTestWithEnvironment
import org.jetbrains.kotlin.test.TestJdkKind
import org.jetbrains.kotlin.test.util.RecursiveDescriptorComparator
import java.io.File
abstract class AbstractBuiltInsWithJDKMembersTest : KotlinTestWithEnvironment() {
private val configuration = RecursiveDescriptorComparator.RECURSIVE_ALL.includeMethodsOfKotlinAny(false).withRenderer(
DescriptorRenderer.withOptions {
withDefinedIn = false
overrideRenderingPolicy = OverrideRenderingPolicy.RENDER_OPEN_OVERRIDE
verbose = true
annotationArgumentsRenderingPolicy = AnnotationArgumentsRenderingPolicy.UNLESS_EMPTY
modifiers = DescriptorRendererModifier.ALL
})
override fun createEnvironment(): KotlinCoreEnvironment =
createEnvironmentWithJdk(ConfigurationKind.JDK_ONLY, testJdkKind)
protected open val testJdkKind: TestJdkKind
get() = TestJdkKind.FULL_JDK
protected fun doTest(builtinVersionName: String) {
val module = JvmResolveUtil.analyze(environment).moduleDescriptor as ModuleDescriptorImpl
for (packageFqName in listOf(BUILT_INS_PACKAGE_FQ_NAME, COLLECTIONS_PACKAGE_FQ_NAME, RANGES_PACKAGE_FQ_NAME)) {
val loaded =
module.packageFragmentProvider.getPackageFragments(packageFqName).filterIsInstance<BuiltInsPackageFragment>().single()
RecursiveDescriptorComparator.validateAndCompareDescriptorWithFile(
loaded, configuration,
File("compiler/testData/builtin-classes/$builtinVersionName/" + packageFqName.asString().replace('.', '-') + ".txt")
)
}
}
}