KOTLIN_CONFIGURATION_FLAGS directive for passing compiler configuration parameters to tests.

So far, only boolean flags are supported.
This commit is contained in:
Dmitry Petrov
2016-03-16 12:05:09 +03:00
parent cb4702c406
commit 095cbea125
7 changed files with 204 additions and 55 deletions
@@ -34,9 +34,11 @@ abstract class AbstractBlackBoxAgainstJavaCodegenTest : AbstractBlackBoxCodegenT
CodegenTestUtil.compileJava(findJavaSourcesInDirectory(directory), emptyList(), javacOptions)
}
val configuration = KotlinTestUtils.compilerConfigurationForTests(
ConfigurationKind.ALL, jdkKind, KotlinTestUtils.getAnnotationsJar(), javaOutputDir
)
val configuration = createCompilerConfigurationForTests(
ConfigurationKind.ALL, jdkKind,
/* classpath = */ listOf(KotlinTestUtils.getAnnotationsJar(), javaOutputDir),
/* javaSource = */ emptyList(),
files)
myEnvironment = KotlinCoreEnvironment.createForTests(testRootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
@@ -47,7 +47,6 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.jetbrains.kotlin.test.KotlinTestUtils.compilerConfigurationForTests;
import static org.jetbrains.kotlin.test.KotlinTestUtils.getAnnotationsJar;
public abstract class AbstractBlackBoxCodegenTest extends CodegenTestCase {
@@ -87,9 +86,11 @@ public abstract class AbstractBlackBoxCodegenTest extends CodegenTestCase {
@NotNull TestJdkKind jdkKind,
@NotNull List<String> javacOptions
) {
CompilerConfiguration configuration = compilerConfigurationForTests(
configurationKind, jdkKind, Collections.singletonList(getAnnotationsJar()),
ArraysKt.filterNotNull(new File[] {javaSourceDir})
CompilerConfiguration configuration = createCompilerConfigurationForTests(
configurationKind, jdkKind,
Collections.singletonList(getAnnotationsJar()),
ArraysKt.filterNotNull(new File[] {javaSourceDir}),
files
);
myEnvironment = KotlinCoreEnvironment.createForTests(
@@ -99,7 +100,8 @@ public abstract class AbstractBlackBoxCodegenTest extends CodegenTestCase {
loadMultiFiles(files);
classFileFactory = GenerationUtils.compileManyFilesGetGenerationStateForTest(
myEnvironment.getProject(), myFiles.getPsiFiles(), new JvmPackagePartProvider(myEnvironment)
myEnvironment.getProject(), myFiles.getPsiFiles(), new JvmPackagePartProvider(myEnvironment),
myEnvironment.getConfiguration()
).getFactory();
if (javaSourceDir != null) {
@@ -21,7 +21,7 @@ import java.io.File
abstract class AbstractBlackBoxInlineCodegenTest : AbstractBlackBoxCodegenTest() {
override fun doMultiFileTest(file: File, files: List<TestFile>, javaFilesDir: File?) {
createEnvironmentWithMockJdkAndIdeaAnnotations(ConfigurationKind.ALL)
createEnvironmentWithMockJdkAndIdeaAnnotations(ConfigurationKind.ALL, files)
loadMultiFiles(files)
blackBox()
@@ -38,7 +38,7 @@ public abstract class AbstractBytecodeTextTest extends CodegenTestCase {
@Override
protected void doMultiFileTest(@NotNull File wholeFile, @NotNull List<TestFile> files, @Nullable File javaFilesDir) throws Exception {
createEnvironmentWithMockJdkAndIdeaAnnotations(ConfigurationKind.ALL, javaFilesDir);
createEnvironmentWithMockJdkAndIdeaAnnotations(ConfigurationKind.ALL, files, javaFilesDir);
loadMultiFiles(files);
if (isMultiFileTest(files)) {
@@ -16,6 +16,8 @@
package org.jetbrains.kotlin.codegen;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.io.FileUtil;
@@ -28,15 +30,19 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.backend.common.output.OutputFile;
import org.jetbrains.kotlin.checkers.CheckerTestUtil;
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys;
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles;
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
import org.jetbrains.kotlin.cli.jvm.config.JVMConfigurationKeys;
import org.jetbrains.kotlin.cli.jvm.config.JvmContentRootsKt;
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime;
import org.jetbrains.kotlin.config.CompilerConfiguration;
import org.jetbrains.kotlin.config.CompilerConfigurationKey;
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.psi.KtFile;
import org.jetbrains.kotlin.test.ConfigurationKind;
import org.jetbrains.kotlin.test.InTextDirectivesUtils;
import org.jetbrains.kotlin.test.KotlinTestUtils;
import org.jetbrains.kotlin.test.TestJdkKind;
import org.jetbrains.kotlin.utils.ExceptionUtilsKt;
@@ -54,6 +60,7 @@ import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
@@ -61,6 +68,8 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static org.jetbrains.kotlin.codegen.CodegenTestUtil.*;
import static org.jetbrains.kotlin.test.KotlinTestUtils.compilerConfigurationForTests;
@@ -78,14 +87,25 @@ public abstract class CodegenTestCase extends UsefulTestCase {
protected final void createEnvironmentWithMockJdkAndIdeaAnnotations(
@NotNull ConfigurationKind configurationKind,
@Nullable File... javaSourceRoots
) {
createEnvironmentWithMockJdkAndIdeaAnnotations(configurationKind, Collections.<TestFile>emptyList(), javaSourceRoots);
}
protected final void createEnvironmentWithMockJdkAndIdeaAnnotations(
@NotNull ConfigurationKind configurationKind,
@NotNull List<TestFile> testFilesWithConfigurationDirectives,
@Nullable File... javaSourceRoots
) {
if (myEnvironment != null) {
throw new IllegalStateException("must not set up myEnvironment twice");
}
CompilerConfiguration configuration = compilerConfigurationForTests(
configurationKind, TestJdkKind.MOCK_JDK, Collections.singletonList(getAnnotationsJar()),
ArraysKt.filterNotNull(javaSourceRoots)
CompilerConfiguration configuration = createCompilerConfigurationForTests(
configurationKind,
TestJdkKind.MOCK_JDK,
Collections.singletonList(getAnnotationsJar()),
ArraysKt.filterNotNull(javaSourceRoots),
testFilesWithConfigurationDirectives
);
myEnvironment = KotlinCoreEnvironment.createForTests(
@@ -93,6 +113,96 @@ public abstract class CodegenTestCase extends UsefulTestCase {
);
}
@NotNull
protected static CompilerConfiguration createCompilerConfigurationForTests(
@NotNull ConfigurationKind kind,
@NotNull TestJdkKind jdkKind,
@NotNull List<File> classpath,
@NotNull List<File> javaSource,
@NotNull List<TestFile> testFilesWithConfigurationDirectives
) {
CompilerConfiguration configuration = compilerConfigurationForTests(kind, jdkKind, classpath, javaSource);
updateConfigurationByDirectivesInTestFiles(testFilesWithConfigurationDirectives, configuration);
return configuration;
}
private static void updateConfigurationByDirectivesInTestFiles(
@NotNull List<TestFile> testFilesWithConfigurationDirectives,
@NotNull CompilerConfiguration configuration
) {
List<String> kotlinConfigurationFlags = new ArrayList<String>(0);
for (TestFile testFile : testFilesWithConfigurationDirectives) {
kotlinConfigurationFlags.addAll(InTextDirectivesUtils.findListWithPrefixes(testFile.content, "// KOTLIN_CONFIGURATION_FLAGS:"));
}
updateConfigurationWithFlags(configuration, kotlinConfigurationFlags);
}
private static final Map<String, Class<?>> FLAG_NAMESPACE_TO_CLASS = ImmutableMap.of(
"CLI", CLIConfigurationKeys.class,
"JVM", JVMConfigurationKeys.class
);
private static final List<Class<?>> FLAG_CLASSES = ImmutableList.of(CLIConfigurationKeys.class, JVMConfigurationKeys.class);
private static final Pattern BOOLEAN_FLAG_PATTERN = Pattern.compile("([+-])(([a-zA-Z_0-9]*)\\.)?([a-zA-Z_0-9]*)");
private static void updateConfigurationWithFlags(@NotNull CompilerConfiguration configuration, @NotNull List<String> flags) {
for (String flag : flags) {
Matcher m = BOOLEAN_FLAG_PATTERN.matcher(flag);
if (m.matches()) {
boolean flagEnabled = !"-".equals(m.group(1));
String flagNamespace = m.group(3);
String flagName = m.group(4);
tryApplyBooleanFlag(configuration, flag, flagEnabled, flagNamespace, flagName);
}
}
}
private static void tryApplyBooleanFlag(
@NotNull CompilerConfiguration configuration,
@NotNull String flag,
boolean flagEnabled,
@Nullable String flagNamespace,
@NotNull String flagName
) {
Class<?> configurationKeysClass;
Field configurationKeyField = null;
if (flagNamespace == null) {
for (Class<?> flagClass : FLAG_CLASSES) {
try {
configurationKeyField = flagClass.getField(flagName);
break;
}
catch (Exception ignored) {
}
}
}
else {
configurationKeysClass = FLAG_NAMESPACE_TO_CLASS.get(flagNamespace);
assert configurationKeysClass != null : "Expected [+|-][namespace.]configurationKey, got: " + flag;
try {
configurationKeyField = configurationKeysClass.getField(flagName);
}
catch (Exception e) {
configurationKeyField = null;
}
}
assert configurationKeyField != null : "Expected [+|-][namespace.]configurationKey, got: " + flag;
try {
//noinspection unchecked
CompilerConfigurationKey<Boolean> configurationKey = (CompilerConfigurationKey<Boolean>) configurationKeyField.get(null);
configuration.put(configurationKey, flagEnabled);
}
catch (Exception e) {
assert false : "Expected [+|-][namespace.]configurationKey, got: " + flag;
}
}
@Override
protected void tearDown() throws Exception {
myFiles = null;
@@ -187,9 +297,8 @@ public abstract class CodegenTestCase extends UsefulTestCase {
protected GeneratedClassLoader createClassLoader() {
return new GeneratedClassLoader(
generateClassesInFile(),
configurationKind.getWithReflection()
? ForTestCompileRuntime.runtimeAndReflectJarClassLoader()
: ForTestCompileRuntime.runtimeJarClassLoader(),
configurationKind.getWithReflection() ? ForTestCompileRuntime.runtimeAndReflectJarClassLoader()
: ForTestCompileRuntime.runtimeJarClassLoader(),
getClassPathURLs()
);
}
@@ -378,36 +487,40 @@ public abstract class CodegenTestCase extends UsefulTestCase {
String expectedText = KotlinTestUtils.doLoadFile(file);
final Ref<File> javaFilesDir = Ref.create();
List<TestFile> testFiles =
KotlinTestUtils.createTestFiles(file.getName(), expectedText, new KotlinTestUtils.TestFileFactoryNoModules<TestFile>() {
@NotNull
@Override
public TestFile create(@NotNull String fileName, @NotNull String text, @NotNull Map<String, String> directives) {
if (fileName.endsWith(".java")) {
if (javaFilesDir.isNull()) {
try {
javaFilesDir.set(KotlinTestUtils.tmpDir("java-files"));
}
catch (IOException e) {
throw ExceptionUtilsKt.rethrow(e);
}
}
writeSourceFile(fileName, text, javaFilesDir.get());
}
return new TestFile(fileName, text);
}
private void writeSourceFile(@NotNull String fileName, @NotNull String content, @NotNull File targetDir) {
File file = new File(targetDir, fileName);
KotlinTestUtils.mkdirs(file.getParentFile());
FilesKt.writeText(file, content, Charsets.UTF_8);
}
});
List<TestFile> testFiles = createTestFiles(file, expectedText, javaFilesDir);
doMultiFileTest(file, testFiles, javaFilesDir.get());
}
@NotNull
private List<TestFile> createTestFiles(File file, String expectedText, final Ref<File> javaFilesDir) {
return KotlinTestUtils.createTestFiles(file.getName(), expectedText, new KotlinTestUtils.TestFileFactoryNoModules<TestFile>() {
@NotNull
@Override
public TestFile create(@NotNull String fileName, @NotNull String text, @NotNull Map<String, String> directives) {
if (fileName.endsWith(".java")) {
if (javaFilesDir.isNull()) {
try {
javaFilesDir.set(KotlinTestUtils.tmpDir("java-files"));
}
catch (IOException e) {
throw ExceptionUtilsKt.rethrow(e);
}
}
writeSourceFile(fileName, text, javaFilesDir.get());
}
return new TestFile(fileName, text);
}
private void writeSourceFile(@NotNull String fileName, @NotNull String content, @NotNull File targetDir) {
File file = new File(targetDir, fileName);
KotlinTestUtils.mkdirs(file.getParentFile());
FilesKt.writeText(file, content, Charsets.UTF_8);
}
});
}
protected void doMultiFileTest(@NotNull File wholeFile, @NotNull List<TestFile> files, @Nullable File javaFilesDir) throws Exception {
throw new UnsupportedOperationException("Multi-file test cases are not supported in this test");
}
@@ -57,14 +57,18 @@ public class CodegenTestUtil {
AnalyzingUtils.throwExceptionOnErrors(analysisResult.getBindingContext());
CompilerConfiguration configuration = environment.getConfiguration();
GenerationState state = new GenerationState(
environment.getProject(), ClassBuilderFactories.TEST,
analysisResult.getModuleDescriptor(), analysisResult.getBindingContext(), files.getPsiFiles(),
environment.getProject(),
ClassBuilderFactories.TEST,
analysisResult.getModuleDescriptor(),
analysisResult.getBindingContext(),
files.getPsiFiles(),
configuration.get(JVMConfigurationKeys.DISABLE_CALL_ASSERTIONS, false),
configuration.get(JVMConfigurationKeys.DISABLE_PARAM_ASSERTIONS, false),
GenerationState.GenerateClassFilter.GENERATE_ALL,
configuration.get(JVMConfigurationKeys.DISABLE_INLINE, false),
configuration.get(JVMConfigurationKeys.DISABLE_OPTIMIZATION, false),
/* useTypeTableInSerializer = */ false
/* useTypeTableInSerializer = */ false,
configuration.get(JVMConfigurationKeys.INHERIT_MULTIFILE_PARTS, false)
);
KotlinCodegenFacade.compileCorrectFiles(state, CompilationErrorHandler.THROW_EXCEPTION);
@@ -18,10 +18,14 @@ package org.jetbrains.kotlin.codegen;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.analyzer.AnalysisResult;
import org.jetbrains.kotlin.cli.jvm.compiler.JvmPackagePartProvider;
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
import org.jetbrains.kotlin.cli.jvm.config.JVMConfigurationKeys;
import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.config.CompilerConfiguration;
import org.jetbrains.kotlin.config.CompilerConfigurationKey;
import org.jetbrains.kotlin.descriptors.PackagePartProvider;
import org.jetbrains.kotlin.psi.KtFile;
import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil;
@@ -49,21 +53,24 @@ public class GenerationUtils {
) {
AnalysisResult analysisResult =
JvmResolveUtil.analyzeOneFileWithJavaIntegrationAndCheckForErrors(psiFile, new JvmPackagePartProvider(environment));
return compileFilesGetGenerationState(psiFile.getProject(), analysisResult, Collections.singletonList(psiFile), false);
return compileFilesGetGenerationState(psiFile.getProject(), analysisResult, Collections.singletonList(psiFile), false, null);
}
@NotNull
public static GenerationState compileManyFilesGetGenerationStateForTest(@NotNull Project project, @NotNull List<KtFile> files) {
return compileManyFilesGetGenerationStateForTest(project, files, PackagePartProvider.Companion.getEMPTY());
return compileManyFilesGetGenerationStateForTest(project, files, PackagePartProvider.Companion.getEMPTY(), null);
}
@NotNull
public static GenerationState compileManyFilesGetGenerationStateForTest(@NotNull Project project, @NotNull List<KtFile> files,
@NotNull PackagePartProvider packagePartProvider
public static GenerationState compileManyFilesGetGenerationStateForTest(
@NotNull Project project,
@NotNull List<KtFile> files,
@NotNull PackagePartProvider packagePartProvider,
@Nullable CompilerConfiguration configuration
) {
AnalysisResult analysisResult = JvmResolveUtil.analyzeFilesWithJavaIntegrationAndCheckForErrors(
project, files, packagePartProvider);
return compileFilesGetGenerationState(project, analysisResult, files, false);
return compileFilesGetGenerationState(project, analysisResult, files, false, configuration);
}
@NotNull
@@ -72,20 +79,41 @@ public class GenerationUtils {
@NotNull AnalysisResult analysisResult,
@NotNull List<KtFile> files,
boolean useTypeTableInSerializer
) {
return compileFilesGetGenerationState(project, analysisResult, files, useTypeTableInSerializer, null);
}
@NotNull
public static GenerationState compileFilesGetGenerationState(
@NotNull Project project,
@NotNull AnalysisResult analysisResult,
@NotNull List<KtFile> files,
boolean useTypeTableInSerializer,
@Nullable CompilerConfiguration configuration
) {
analysisResult.throwIfError();
GenerationState state = new GenerationState(
project, ClassBuilderFactories.TEST,
analysisResult.getModuleDescriptor(), analysisResult.getBindingContext(),
files,
/* disableCallAssertions = */ false,
/* disableParamAssertions = */ false,
getConfigurationValueOrDefault(configuration, JVMConfigurationKeys.DISABLE_CALL_ASSERTIONS, false),
getConfigurationValueOrDefault(configuration, JVMConfigurationKeys.DISABLE_PARAM_ASSERTIONS, false),
GenerationState.GenerateClassFilter.GENERATE_ALL,
/* disableInline = */ false,
/* disableOptimization = */ false,
useTypeTableInSerializer
getConfigurationValueOrDefault(configuration, JVMConfigurationKeys.DISABLE_INLINE, false),
getConfigurationValueOrDefault(configuration, JVMConfigurationKeys.DISABLE_OPTIMIZATION, false),
useTypeTableInSerializer,
getConfigurationValueOrDefault(configuration, JVMConfigurationKeys.INHERIT_MULTIFILE_PARTS, false)
);
KotlinCodegenFacade.compileCorrectFiles(state, CompilationErrorHandler.THROW_EXCEPTION);
return state;
}
private static <T> T getConfigurationValueOrDefault(
@Nullable CompilerConfiguration configuration,
@NotNull CompilerConfigurationKey<T> key,
T defaultValue
) {
if (configuration == null) return defaultValue;
return configuration.get(key, defaultValue);
}
}