Rename jet -> kotlin in compiler-tests (except resolve)
Move test-related code to org.jetbrains.kotlin.test, also move some tests to packages with better names
This commit is contained in:
@@ -0,0 +1,869 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test;
|
||||
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.ShutDownTracker;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.CharsetToolkit;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiFileFactory;
|
||||
import com.intellij.psi.impl.PsiFileFactoryImpl;
|
||||
import com.intellij.rt.execution.junit.FileComparisonFailure;
|
||||
import com.intellij.testFramework.LightVirtualFile;
|
||||
import com.intellij.testFramework.TestDataFile;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import junit.framework.TestCase;
|
||||
import kotlin.Function1;
|
||||
import kotlin.KotlinPackage;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
import org.jetbrains.kotlin.analyzer.AnalysisResult;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.JetCoreEnvironment;
|
||||
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime;
|
||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys;
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration;
|
||||
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl;
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic;
|
||||
import org.jetbrains.kotlin.diagnostics.Errors;
|
||||
import org.jetbrains.kotlin.diagnostics.Severity;
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages;
|
||||
import org.jetbrains.kotlin.lexer.JetTokens;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.platform.PlatformToKotlinClassMap;
|
||||
import org.jetbrains.kotlin.plugin.JetLanguage;
|
||||
import org.jetbrains.kotlin.psi.JetFile;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace;
|
||||
import org.jetbrains.kotlin.resolve.ImportPath;
|
||||
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics;
|
||||
import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil;
|
||||
import org.jetbrains.kotlin.resolve.lazy.LazyResolveTestUtil;
|
||||
import org.jetbrains.kotlin.test.util.UtilPackage;
|
||||
import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice;
|
||||
import org.jetbrains.kotlin.util.slicedMap.SlicedMap;
|
||||
import org.jetbrains.kotlin.util.slicedMap.WritableSlice;
|
||||
import org.jetbrains.kotlin.utils.PathUtil;
|
||||
import org.jetbrains.kotlin.utils.UtilsPackage;
|
||||
import org.junit.Assert;
|
||||
|
||||
import javax.tools.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.jetbrains.kotlin.cli.jvm.JVMConfigurationKeys.ANNOTATIONS_PATH_KEY;
|
||||
import static org.jetbrains.kotlin.cli.jvm.JVMConfigurationKeys.CLASSPATH_KEY;
|
||||
import static org.jetbrains.kotlin.jvm.compiler.LoadDescriptorUtil.compileKotlinToDirAndGetAnalysisResult;
|
||||
import static org.jetbrains.kotlin.psi.PsiPackage.JetPsiFactory;
|
||||
import static org.jetbrains.kotlin.test.ConfigurationKind.ALL;
|
||||
import static org.jetbrains.kotlin.test.ConfigurationKind.JDK_AND_ANNOTATIONS;
|
||||
|
||||
public class JetTestUtils {
|
||||
public static final String TEST_GENERATOR_NAME = "org.jetbrains.kotlin.generators.tests.TestsPackage";
|
||||
public static final String PLEASE_REGENERATE_TESTS = "Please regenerate tests (GenerateTests.kt)";
|
||||
|
||||
private static final List<File> filesToDelete = new ArrayList<File>();
|
||||
|
||||
/**
|
||||
* Syntax:
|
||||
*
|
||||
* // MODULE: name(dependency1, dependency2, ...)
|
||||
*
|
||||
* // FILE: name
|
||||
*
|
||||
* Several files may follow one module
|
||||
*/
|
||||
public static final Pattern FILE_OR_MODULE_PATTERN = Pattern.compile("(?://\\s*MODULE:\\s*(\\w+)(\\(\\w+(?:, \\w+)*\\))?\\s*)?" +
|
||||
"//\\s*FILE:\\s*(.*)$", Pattern.MULTILINE);
|
||||
public static final Pattern DIRECTIVE_PATTERN = Pattern.compile("^//\\s*!(\\w+)(:\\s*(.*)$)?", Pattern.MULTILINE);
|
||||
|
||||
public static final BindingTrace DUMMY_TRACE = new BindingTrace() {
|
||||
@NotNull
|
||||
@Override
|
||||
public BindingContext getBindingContext() {
|
||||
return new BindingContext() {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Diagnostics getDiagnostics() {
|
||||
return Diagnostics.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <K, V> V get(ReadOnlySlice<K, V> slice, K key) {
|
||||
return DUMMY_TRACE.get(slice, key);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public <K, V> Collection<K> getKeys(WritableSlice<K, V> slice) {
|
||||
return DUMMY_TRACE.getKeys(slice);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@TestOnly
|
||||
@Override
|
||||
public <K, V> ImmutableMap<K, V> getSliceContents(@NotNull ReadOnlySlice<K, V> slice) {
|
||||
return ImmutableMap.of();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public <K, V> void record(WritableSlice<K, V> slice, K key, V value) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <K> void record(WritableSlice<K, Boolean> slice, K key) {
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <K, V> V get(ReadOnlySlice<K, V> slice, K key) {
|
||||
if (slice == BindingContext.PROCESSED) return (V) Boolean.FALSE;
|
||||
return SlicedMap.DO_NOTHING.get(slice, key);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public <K, V> Collection<K> getKeys(WritableSlice<K, V> slice) {
|
||||
assert slice.isCollective();
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void report(@NotNull Diagnostic diagnostic) {
|
||||
if (Errors.UNRESOLVED_REFERENCE_DIAGNOSTICS.contains(diagnostic.getFactory())) {
|
||||
throw new IllegalStateException("Unresolved: " + diagnostic.getPsiElement().getText());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public static BindingTrace DUMMY_EXCEPTION_ON_ERROR_TRACE = new BindingTrace() {
|
||||
@NotNull
|
||||
@Override
|
||||
public BindingContext getBindingContext() {
|
||||
return new BindingContext() {
|
||||
@NotNull
|
||||
@Override
|
||||
public Diagnostics getDiagnostics() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <K, V> V get(ReadOnlySlice<K, V> slice, K key) {
|
||||
return DUMMY_EXCEPTION_ON_ERROR_TRACE.get(slice, key);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public <K, V> Collection<K> getKeys(WritableSlice<K, V> slice) {
|
||||
return DUMMY_EXCEPTION_ON_ERROR_TRACE.getKeys(slice);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@TestOnly
|
||||
@Override
|
||||
public <K, V> ImmutableMap<K, V> getSliceContents(@NotNull ReadOnlySlice<K, V> slice) {
|
||||
return ImmutableMap.of();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public <K, V> void record(WritableSlice<K, V> slice, K key, V value) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <K> void record(WritableSlice<K, Boolean> slice, K key) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <K, V> V get(ReadOnlySlice<K, V> slice, K key) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public <K, V> Collection<K> getKeys(WritableSlice<K, V> slice) {
|
||||
assert slice.isCollective();
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void report(@NotNull Diagnostic diagnostic) {
|
||||
if (diagnostic.getSeverity() == Severity.ERROR) {
|
||||
throw new IllegalStateException(DefaultErrorMessages.render(diagnostic));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static final Class<? extends TestCase>[] NO_INNER_CLASSES = ArrayUtil.EMPTY_CLASS_ARRAY;
|
||||
|
||||
private JetTestUtils() {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static AnalysisResult analyzeFile(@NotNull JetFile file) {
|
||||
return JvmResolveUtil.analyzeOneFileWithJavaIntegration(file);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static AnalysisResult analyzeFileWithoutBody(@NotNull JetFile file) {
|
||||
return JvmResolveUtil.analyzeFilesWithJavaIntegration(file.getProject(),
|
||||
Collections.singleton(file),
|
||||
Predicates.<PsiFile>alwaysFalse());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static JetCoreEnvironment createEnvironmentWithFullJdk(Disposable disposable) {
|
||||
return createEnvironmentWithJdkAndNullabilityAnnotationsFromIdea(disposable,
|
||||
ConfigurationKind.ALL, TestJdkKind.FULL_JDK);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static JetCoreEnvironment createEnvironmentWithMockJdkAndIdeaAnnotations(Disposable disposable) {
|
||||
return createEnvironmentWithMockJdkAndIdeaAnnotations(disposable, ConfigurationKind.ALL);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static JetCoreEnvironment createEnvironmentWithMockJdkAndIdeaAnnotations(Disposable disposable, @NotNull ConfigurationKind configurationKind) {
|
||||
return createEnvironmentWithJdkAndNullabilityAnnotationsFromIdea(disposable, configurationKind, TestJdkKind.MOCK_JDK);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static JetCoreEnvironment createEnvironmentWithJdkAndNullabilityAnnotationsFromIdea(
|
||||
@NotNull Disposable disposable,
|
||||
@NotNull ConfigurationKind configurationKind,
|
||||
@NotNull TestJdkKind jdkKind
|
||||
) {
|
||||
return JetCoreEnvironment.createForTests(
|
||||
disposable,
|
||||
compilerConfigurationForTests(configurationKind, jdkKind, getAnnotationsJar()),
|
||||
EnvironmentConfigFiles.JVM_CONFIG_FILES);
|
||||
}
|
||||
|
||||
public static File findMockJdkRtJar() {
|
||||
return new File(JetTestCaseBuilder.getHomeDirectory(), "compiler/testData/mockJDK/jre/lib/rt.jar");
|
||||
}
|
||||
|
||||
public static File findAndroidApiJar() {
|
||||
return new File(JetTestCaseBuilder.getHomeDirectory(), "dependencies/android.jar");
|
||||
}
|
||||
|
||||
public static File getAnnotationsJar() {
|
||||
return new File(JetTestCaseBuilder.getHomeDirectory(), "compiler/testData/mockJDK/jre/lib/annotations.jar");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static File getJdkAnnotationsJar() {
|
||||
File jdkAnnotations = new File(JetTestCaseBuilder.getHomeDirectory(), "dependencies/annotations/kotlin-jdk-annotations.jar");
|
||||
if (!jdkAnnotations.exists()) {
|
||||
throw new RuntimeException("Kotlin JDK annotations jar not found; please run 'ant dist' to build it");
|
||||
}
|
||||
return jdkAnnotations;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static File getAndroidSdkAnnotationsJar() {
|
||||
File androidSdkAnnotations = new File(JetTestCaseBuilder.getHomeDirectory(), "dependencies/annotations/kotlin-android-sdk-annotations.jar");
|
||||
if (!androidSdkAnnotations.exists()) {
|
||||
throw new RuntimeException("Kotlin Android SDK annotations jar not found; please run 'ant dist' to build it");
|
||||
}
|
||||
return androidSdkAnnotations;
|
||||
}
|
||||
|
||||
public static void mkdirs(File file) throws IOException {
|
||||
if (file.isDirectory()) {
|
||||
return;
|
||||
}
|
||||
if (!file.mkdirs()) {
|
||||
if (file.exists()) {
|
||||
throw new IOException("failed to create " + file + " file exists and not a directory");
|
||||
}
|
||||
throw new IOException();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static File tmpDirForTest(TestCase test) throws IOException {
|
||||
File answer = FileUtil.createTempDirectory(test.getClass().getSimpleName(), test.getName());
|
||||
deleteOnShutdown(answer);
|
||||
return answer;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static File tmpDir(String name) throws IOException {
|
||||
// we should use this form. otherwise directory will be deleted on each test
|
||||
File answer = FileUtil.createTempDirectory(new File(System.getProperty("java.io.tmpdir")), name, "");
|
||||
deleteOnShutdown(answer);
|
||||
return answer;
|
||||
}
|
||||
|
||||
public static void deleteOnShutdown(File file) {
|
||||
if (filesToDelete.isEmpty()) {
|
||||
ShutDownTracker.getInstance().registerShutdownTask(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ShutDownTracker.invokeAndWait(true, true, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (File victim : filesToDelete) {
|
||||
FileUtil.delete(victim);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
filesToDelete.add(file);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static JetFile createFile(@NotNull @NonNls String name, @NotNull String text, @NotNull Project project) {
|
||||
LightVirtualFile virtualFile = new LightVirtualFile(name, JetLanguage.INSTANCE, text);
|
||||
virtualFile.setCharset(CharsetToolkit.UTF8_CHARSET);
|
||||
PsiFileFactoryImpl factory = (PsiFileFactoryImpl) PsiFileFactory.getInstance(project);
|
||||
//noinspection ConstantConditions
|
||||
return (JetFile) factory.trySetupPsiForFile(virtualFile, JetLanguage.INSTANCE, true, false);
|
||||
}
|
||||
|
||||
public static String doLoadFile(String myFullDataPath, String name) throws IOException {
|
||||
String fullName = myFullDataPath + File.separatorChar + name;
|
||||
return doLoadFile(new File(fullName));
|
||||
}
|
||||
|
||||
public static String doLoadFile(@NotNull File file) throws IOException {
|
||||
return FileUtil.loadFile(file, CharsetToolkit.UTF8, true).trim();
|
||||
}
|
||||
|
||||
public static String getFilePath(File file) {
|
||||
return FileUtil.toSystemIndependentName(file.getPath());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static CompilerConfiguration compilerConfigurationForTests(@NotNull ConfigurationKind configurationKind,
|
||||
@NotNull TestJdkKind jdkKind, File... extraClasspath) {
|
||||
return compilerConfigurationForTests(configurationKind, jdkKind, Arrays.asList(extraClasspath), Collections.<File>emptyList());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static CompilerConfiguration compilerConfigurationForTests(@NotNull ConfigurationKind configurationKind,
|
||||
@NotNull TestJdkKind jdkKind, @NotNull Collection<File> extraClasspath, @NotNull Collection<File> priorityClasspath) {
|
||||
CompilerConfiguration configuration = new CompilerConfiguration();
|
||||
configuration.addAll(CLASSPATH_KEY, priorityClasspath);
|
||||
if (jdkKind == TestJdkKind.MOCK_JDK) {
|
||||
configuration.add(CLASSPATH_KEY, findMockJdkRtJar());
|
||||
}
|
||||
else if (jdkKind == TestJdkKind.ANDROID_API) {
|
||||
configuration.add(CLASSPATH_KEY, findAndroidApiJar());
|
||||
}
|
||||
else {
|
||||
configuration.addAll(CLASSPATH_KEY, PathUtil.getJdkClassesRoots());
|
||||
}
|
||||
if (configurationKind == ALL) {
|
||||
configuration.add(CLASSPATH_KEY, ForTestCompileRuntime.runtimeJarForTests());
|
||||
}
|
||||
configuration.addAll(CLASSPATH_KEY, extraClasspath);
|
||||
|
||||
if (configurationKind == ALL || configurationKind == JDK_AND_ANNOTATIONS) {
|
||||
if (jdkKind == TestJdkKind.ANDROID_API) {
|
||||
configuration.add(ANNOTATIONS_PATH_KEY, getAndroidSdkAnnotationsJar());
|
||||
} else {
|
||||
configuration.add(ANNOTATIONS_PATH_KEY, getJdkAnnotationsJar());
|
||||
}
|
||||
}
|
||||
|
||||
return configuration;
|
||||
}
|
||||
|
||||
public static void resolveAllKotlinFiles(JetCoreEnvironment environment) throws IOException {
|
||||
List<String> paths = environment.getConfiguration().get(CommonConfigurationKeys.SOURCE_ROOTS_KEY);
|
||||
if (paths == null) return;
|
||||
List<JetFile> jetFiles = Lists.newArrayList();
|
||||
for (String path : paths) {
|
||||
File file = new File(path);
|
||||
if (file.isFile()) {
|
||||
jetFiles.add(loadJetFile(environment.getProject(), file));
|
||||
}
|
||||
else {
|
||||
//noinspection ConstantConditions
|
||||
for (File childFile : file.listFiles()) {
|
||||
if (childFile.getName().endsWith(".kt")) {
|
||||
jetFiles.add(loadJetFile(environment.getProject(), childFile));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
LazyResolveTestUtil.resolve(jetFiles, environment);
|
||||
}
|
||||
|
||||
public static void assertEqualsToFile(@NotNull File expectedFile, @NotNull String actual) {
|
||||
assertEqualsToFile(expectedFile, actual, new Function1<String, String>() {
|
||||
@Override
|
||||
public String invoke(String s) {
|
||||
return s;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void assertEqualsToFile(@NotNull File expectedFile, @NotNull String actual, @NotNull Function1<String, String> sanitizer) {
|
||||
try {
|
||||
String actualText = UtilPackage.trimTrailingWhitespacesAndAddNewlineAtEOF(StringUtil.convertLineSeparators(actual.trim()));
|
||||
|
||||
if (!expectedFile.exists()) {
|
||||
FileUtil.writeToFile(expectedFile, actualText);
|
||||
Assert.fail("Expected data file did not exist. Generating: " + expectedFile);
|
||||
}
|
||||
String expected = FileUtil.loadFile(expectedFile, CharsetToolkit.UTF8, true);
|
||||
|
||||
String expectedText = UtilPackage.trimTrailingWhitespacesAndAddNewlineAtEOF(StringUtil.convertLineSeparators(expected.trim()));
|
||||
|
||||
if (!Comparing.equal(sanitizer.invoke(expectedText), sanitizer.invoke(actualText))) {
|
||||
throw new FileComparisonFailure("Actual data differs from file content: " + expectedFile.getName(),
|
||||
expected, actual, expectedFile.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw UtilsPackage.rethrow(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void compileKotlinWithJava(
|
||||
@NotNull List<File> javaFiles,
|
||||
@NotNull List<File> ktFiles,
|
||||
@NotNull File outDir,
|
||||
@NotNull Disposable disposable
|
||||
) throws IOException {
|
||||
if (!ktFiles.isEmpty()) {
|
||||
compileKotlinToDirAndGetAnalysisResult(ktFiles, outDir, disposable, ALL);
|
||||
}
|
||||
else {
|
||||
boolean mkdirs = outDir.mkdirs();
|
||||
assert mkdirs : "Not created: " + outDir;
|
||||
}
|
||||
if (!javaFiles.isEmpty()) {
|
||||
compileJavaFiles(javaFiles, Arrays.asList(
|
||||
"-classpath", outDir.getPath() + File.pathSeparator + ForTestCompileRuntime.runtimeJarForTests(),
|
||||
"-d", outDir.getPath()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
public interface TestFileFactory<M, F> {
|
||||
F createFile(@Nullable M module, @NotNull String fileName, @NotNull String text, @NotNull Map<String, String> directives);
|
||||
M createModule(@NotNull String name, @NotNull List<String> dependencies);
|
||||
}
|
||||
|
||||
public static abstract class TestFileFactoryNoModules<F> implements TestFileFactory<Void, F> {
|
||||
@Override
|
||||
public final F createFile(
|
||||
@Nullable Void module,
|
||||
@NotNull String fileName,
|
||||
@NotNull String text,
|
||||
@NotNull Map<String, String> directives
|
||||
) {
|
||||
return create(fileName, text, directives);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public abstract F create(@NotNull String fileName, @NotNull String text, @NotNull Map<String, String> directives);
|
||||
|
||||
@Override
|
||||
public Void createModule(@NotNull String name, @NotNull List<String> dependencies) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static <M, F> List<F> createTestFiles(String testFileName, String expectedText, TestFileFactory<M, F> factory) {
|
||||
Map<String, String> directives = parseDirectives(expectedText);
|
||||
|
||||
List<F> testFiles = Lists.newArrayList();
|
||||
Matcher matcher = FILE_OR_MODULE_PATTERN.matcher(expectedText);
|
||||
if (!matcher.find()) {
|
||||
// One file
|
||||
testFiles.add(factory.createFile(null, testFileName, expectedText, directives));
|
||||
}
|
||||
else {
|
||||
int processedChars = 0;
|
||||
M module = null;
|
||||
// Many files
|
||||
while (true) {
|
||||
String moduleName = matcher.group(1);
|
||||
String moduleDependencies = matcher.group(2);
|
||||
if (moduleName != null) {
|
||||
module = factory.createModule(moduleName, parseDependencies(moduleDependencies));
|
||||
}
|
||||
|
||||
String fileName = matcher.group(3);
|
||||
int start = processedChars;
|
||||
|
||||
boolean nextFileExists = matcher.find();
|
||||
int end;
|
||||
if (nextFileExists) {
|
||||
end = matcher.start();
|
||||
}
|
||||
else {
|
||||
end = expectedText.length();
|
||||
}
|
||||
String fileText = expectedText.substring(start, end);
|
||||
processedChars = end;
|
||||
|
||||
testFiles.add(factory.createFile(module, fileName, fileText, directives));
|
||||
|
||||
if (!nextFileExists) break;
|
||||
}
|
||||
assert processedChars == expectedText.length() : "Characters skipped from " +
|
||||
processedChars +
|
||||
" to " +
|
||||
(expectedText.length() - 1);
|
||||
}
|
||||
return testFiles;
|
||||
}
|
||||
|
||||
private static List<String> parseDependencies(@Nullable String dependencies) {
|
||||
if (dependencies == null) return Collections.emptyList();
|
||||
|
||||
Matcher matcher = Pattern.compile("\\w+").matcher(dependencies);
|
||||
List<String> result = new ArrayList<String>();
|
||||
while (matcher.find()) {
|
||||
result.add(matcher.group());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Map<String, String> parseDirectives(String expectedText) {
|
||||
Map<String, String> directives = Maps.newHashMap();
|
||||
Matcher directiveMatcher = DIRECTIVE_PATTERN.matcher(expectedText);
|
||||
int start = 0;
|
||||
while (directiveMatcher.find()) {
|
||||
if (directiveMatcher.start() != start) {
|
||||
Assert.fail("Directives should only occur at the beginning of a file: " + directiveMatcher.group());
|
||||
}
|
||||
String name = directiveMatcher.group(1);
|
||||
String value = directiveMatcher.group(3);
|
||||
String oldValue = directives.put(name, value);
|
||||
Assert.assertNull("Directive overwritten: " + name + " old value: " + oldValue + " new value: " + value, oldValue);
|
||||
start = directiveMatcher.end() + 1;
|
||||
}
|
||||
return directives;
|
||||
}
|
||||
|
||||
public static List<String> loadBeforeAfterText(String filePath) {
|
||||
String content;
|
||||
|
||||
try {
|
||||
content = FileUtil.loadFile(new File(filePath), true);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
List<String> files = createTestFiles("", content, new TestFileFactoryNoModules<String>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public String create(@NotNull String fileName, @NotNull String text, @NotNull Map<String, String> directives) {
|
||||
int firstLineEnd = text.indexOf('\n');
|
||||
return StringUtil.trimTrailing(text.substring(firstLineEnd + 1));
|
||||
}
|
||||
});
|
||||
|
||||
Assert.assertTrue("Exactly two files expected: ", files.size() == 2);
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
public static String getLastCommentedLines(@NotNull Document document) {
|
||||
List<CharSequence> resultLines = new ArrayList<CharSequence>();
|
||||
for (int i = document.getLineCount() - 1; i >= 0; i--) {
|
||||
int lineStart = document.getLineStartOffset(i);
|
||||
int lineEnd = document.getLineEndOffset(i);
|
||||
if (document.getCharsSequence().subSequence(lineStart, lineEnd).toString().trim().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ("//".equals(document.getCharsSequence().subSequence(lineStart, lineStart + 2).toString())) {
|
||||
resultLines.add(document.getCharsSequence().subSequence(lineStart + 2, lineEnd));
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Collections.reverse(resultLines);
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (CharSequence line : resultLines) {
|
||||
result.append(line).append("\n");
|
||||
}
|
||||
result.delete(result.length() - 1, result.length());
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
public enum CommentType {
|
||||
ALL,
|
||||
LINE_COMMENT,
|
||||
BLOCK_COMMENT
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String getLastCommentInFile(@NotNull JetFile file) {
|
||||
return KotlinPackage.first(getLastCommentsInFile(file, CommentType.ALL, true));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<String> getLastCommentsInFile(@NotNull JetFile file, CommentType commentType, boolean assertMustExist) {
|
||||
PsiElement lastChild = file.getLastChild();
|
||||
if (lastChild != null && lastChild.getNode().getElementType().equals(JetTokens.WHITE_SPACE)) {
|
||||
lastChild = lastChild.getPrevSibling();
|
||||
}
|
||||
assert lastChild != null;
|
||||
|
||||
List<String> comments = ContainerUtil.newArrayList();
|
||||
|
||||
while (true) {
|
||||
if (lastChild.getNode().getElementType().equals(JetTokens.BLOCK_COMMENT)) {
|
||||
if (commentType == CommentType.ALL || commentType == CommentType.BLOCK_COMMENT) {
|
||||
String lastChildText = lastChild.getText();
|
||||
comments.add(lastChildText.substring(2, lastChildText.length() - 2).trim());
|
||||
}
|
||||
}
|
||||
else if (lastChild.getNode().getElementType().equals(JetTokens.EOL_COMMENT)) {
|
||||
if (commentType == CommentType.ALL || commentType == CommentType.LINE_COMMENT) {
|
||||
comments.add(lastChild.getText().substring(2).trim());
|
||||
}
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
|
||||
lastChild = lastChild.getPrevSibling();
|
||||
}
|
||||
|
||||
if (comments.isEmpty() && assertMustExist) {
|
||||
throw new AssertionError(String.format(
|
||||
"Test file '%s' should end in a comment of type %s; last node was: %s", file.getName(), commentType, lastChild));
|
||||
}
|
||||
|
||||
return comments;
|
||||
}
|
||||
|
||||
public static void compileJavaFiles(@NotNull Collection<File> files, List<String> options) throws IOException {
|
||||
JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
|
||||
DiagnosticCollector<JavaFileObject> diagnosticCollector = new DiagnosticCollector<JavaFileObject>();
|
||||
StandardJavaFileManager fileManager = javaCompiler.getStandardFileManager(diagnosticCollector, Locale.ENGLISH, Charset.forName("utf-8"));
|
||||
try {
|
||||
Iterable<? extends JavaFileObject> javaFileObjectsFromFiles = fileManager.getJavaFileObjectsFromFiles(files);
|
||||
|
||||
JavaCompiler.CompilationTask task = javaCompiler.getTask(
|
||||
new StringWriter(), // do not write to System.err
|
||||
fileManager,
|
||||
diagnosticCollector,
|
||||
options,
|
||||
null,
|
||||
javaFileObjectsFromFiles);
|
||||
|
||||
Boolean success = task.call(); // do NOT inline this variable, call() should complete before errorsToString()
|
||||
Assert.assertTrue(errorsToString(diagnosticCollector), success);
|
||||
} finally {
|
||||
fileManager.close();
|
||||
}
|
||||
}
|
||||
|
||||
private static String errorsToString(DiagnosticCollector<JavaFileObject> diagnosticCollector) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (javax.tools.Diagnostic<? extends JavaFileObject> diagnostic : diagnosticCollector.getDiagnostics()) {
|
||||
if (diagnostic.getKind() == javax.tools.Diagnostic.Kind.ERROR) {
|
||||
builder.append(diagnostic).append("\n");
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public static String navigationMetadata(@TestDataFile String testFile) {
|
||||
return testFile;
|
||||
}
|
||||
|
||||
public static void assertAllTestsPresentByMetadata(
|
||||
@NotNull Class<?> testCaseClass,
|
||||
@NotNull File testDataDir,
|
||||
@NotNull Pattern filenamePattern,
|
||||
boolean recursive
|
||||
) {
|
||||
TestMetadata testClassMetadata = testCaseClass.getAnnotation(TestMetadata.class);
|
||||
Assert.assertNotNull("No metadata for class: " + testCaseClass, testClassMetadata);
|
||||
String rootPath = testClassMetadata.value();
|
||||
File rootFile = new File(rootPath);
|
||||
|
||||
Set<String> filePaths = collectPathsMetadata(testCaseClass);
|
||||
|
||||
File[] files = testDataDir.listFiles();
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
if (file.isDirectory()) {
|
||||
if (recursive && containsTestData(file, filenamePattern)) {
|
||||
assertTestClassPresentByMetadata(testCaseClass, file);
|
||||
}
|
||||
}
|
||||
else if (filenamePattern.matcher(file.getName()).matches()) {
|
||||
assertFilePathPresent(file, rootFile, filePaths);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertAllTestsPresentInSingleGeneratedClass(
|
||||
@NotNull Class<?> testCaseClass,
|
||||
@NotNull File testDataDir,
|
||||
@NotNull final Pattern filenamePattern
|
||||
) {
|
||||
TestMetadata testClassMetadata = testCaseClass.getAnnotation(TestMetadata.class);
|
||||
Assert.assertNotNull("No metadata for class: " + testCaseClass, testClassMetadata);
|
||||
final File rootFile = new File(testClassMetadata.value());
|
||||
|
||||
final Set<String> filePaths = collectPathsMetadata(testCaseClass);
|
||||
|
||||
FileUtil.processFilesRecursively(testDataDir, new Processor<File>() {
|
||||
@Override
|
||||
public boolean process(File file) {
|
||||
if (file.isFile() && filenamePattern.matcher(file.getName()).matches()) {
|
||||
assertFilePathPresent(file, rootFile, filePaths);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void assertFilePathPresent(File file, File rootFile, Set<String> filePaths) {
|
||||
String path = FileUtil.getRelativePath(rootFile, file);
|
||||
if (path != null) {
|
||||
String relativePath = FileUtil.nameToCompare(path);
|
||||
if (!filePaths.contains(relativePath)) {
|
||||
Assert.fail("Test data file missing from the generated test class: " + file + "\n" + PLEASE_REGENERATE_TESTS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Set<String> collectPathsMetadata(Class<?> testCaseClass) {
|
||||
return ContainerUtil.newHashSet(
|
||||
ContainerUtil.map(collectMethodsMetadata(testCaseClass), new Function<String, String>() {
|
||||
@Override
|
||||
public String fun(String pathData) {
|
||||
return FileUtil.nameToCompare(pathData);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private static Set<String> collectMethodsMetadata(Class<?> testCaseClass) {
|
||||
Set<String> filePaths = Sets.newHashSet();
|
||||
for (Method method : testCaseClass.getDeclaredMethods()) {
|
||||
TestMetadata testMetadata = method.getAnnotation(TestMetadata.class);
|
||||
if (testMetadata != null) {
|
||||
filePaths.add(testMetadata.value());
|
||||
}
|
||||
}
|
||||
return filePaths;
|
||||
}
|
||||
|
||||
private static boolean containsTestData(File dir, Pattern filenamePattern) {
|
||||
File[] files = dir.listFiles();
|
||||
assert files != null;
|
||||
for (File file : files) {
|
||||
if (file.isDirectory()) {
|
||||
if (containsTestData(file, filenamePattern)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (filenamePattern.matcher(file.getName()).matches()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void assertTestClassPresentByMetadata(
|
||||
@NotNull Class<?> outerClass,
|
||||
@NotNull File testDataDir
|
||||
) {
|
||||
InnerTestClasses innerClassesAnnotation = outerClass.getAnnotation(InnerTestClasses.class);
|
||||
Class<? extends TestCase>[] innerClasses = innerClassesAnnotation == null ? NO_INNER_CLASSES : innerClassesAnnotation.value();
|
||||
for (Class<?> innerClass : innerClasses) {
|
||||
TestMetadata testMetadata = innerClass.getAnnotation(TestMetadata.class);
|
||||
if (testMetadata != null && testMetadata.value().equals(getFilePath(testDataDir))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
Assert.fail("Test data directory missing from the generated test class: " + testDataDir + "\n" + PLEASE_REGENERATE_TESTS);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static JetFile loadJetFile(@NotNull Project project, @NotNull File ioFile) throws IOException {
|
||||
String text = FileUtil.loadFile(ioFile, true);
|
||||
return JetPsiFactory(project).createPhysicalFile(ioFile.getName(), text);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<JetFile> loadToJetFiles(@NotNull JetCoreEnvironment environment, @NotNull List<File> files) throws IOException {
|
||||
List<JetFile> jetFiles = Lists.newArrayList();
|
||||
for (File file : files) {
|
||||
jetFiles.add(loadJetFile(environment.getProject(), file));
|
||||
}
|
||||
return jetFiles;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static ModuleDescriptorImpl createEmptyModule() {
|
||||
return createEmptyModule("<empty-for-test>");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static ModuleDescriptorImpl createEmptyModule(@NotNull String name) {
|
||||
return new ModuleDescriptorImpl(Name.special(name), Collections.<ImportPath>emptyList(), PlatformToKotlinClassMap.EMPTY);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static File replaceExtension(@NotNull File file, @Nullable String newExtension) {
|
||||
return new File(file.getParentFile(), FileUtil.getNameWithoutExtension(file) + (newExtension == null ? "" : "." + newExtension));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user