test discovery: module detection

(cherry picked from commit e538af4)
This commit is contained in:
Anna Kozlova
2016-06-15 16:08:41 +03:00
parent b577a863e1
commit 9ec182d333
4 changed files with 118 additions and 22 deletions
@@ -135,6 +135,12 @@ public abstract class TestDiscoveryConfiguration extends JavaTestConfigurationBa
}
}
@Override
public void setModule(Module module) {
super.setModule(module);
myDelegate.setModule(module);
}
@Override
public void writeExternal(Element element) throws WriteExternalException {
myDelegate.writeExternal(element);
@@ -18,12 +18,15 @@ package com.intellij.execution.testDiscovery;
import com.intellij.codeInsight.TestFrameworks;
import com.intellij.execution.JavaExecutionUtil;
import com.intellij.execution.Location;
import com.intellij.execution.RunnerAndConfigurationSettings;
import com.intellij.execution.actions.ConfigurationContext;
import com.intellij.execution.configurations.ConfigurationType;
import com.intellij.execution.configurations.ModuleBasedConfiguration;
import com.intellij.execution.junit.JavaRunConfigurationProducerBase;
import com.intellij.execution.testframework.TestSearchScope;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.registry.Registry;
@@ -34,9 +37,13 @@ import com.intellij.psi.PsiMethod;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.testIntegration.TestFramework;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashSet;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Set;
public abstract class TestDiscoveryConfigurationProducer extends JavaRunConfigurationProducerBase<TestDiscoveryConfiguration> {
protected TestDiscoveryConfigurationProducer(ConfigurationType type) {
@@ -57,18 +64,46 @@ public abstract class TestDiscoveryConfigurationProducer extends JavaRunConfigur
final Pair<String, String> position = getPosition(location);
if (position != null) {
try {
final Collection<String> testsByMethodName = TestDiscoveryIndex
.getInstance(configuration.getProject()).getTestsByMethodName(position.first, position.second);
if (testsByMethodName == null || ContainerUtil.filter(testsByMethodName, s -> s.startsWith(configuration.getFrameworkPrefix())).isEmpty()) return false;
final Project project = configuration.getProject();
final TestDiscoveryIndex testDiscoveryIndex = TestDiscoveryIndex.getInstance(project);
final Collection<String> testsByMethodName = testDiscoveryIndex.getTestsByMethodName(position.first, position.second);
if (testsByMethodName == null ||
ContainerUtil.filter(testsByMethodName, s -> s.startsWith(configuration.getFrameworkPrefix())).isEmpty()) {
return false;
}
configuration.setPosition(position);
configuration.setName("Tests for " + StringUtil.getShortName(position.first) + "." + position.second);
final RunnerAndConfigurationSettings template =
configurationContext.getRunManager().getConfigurationTemplate(getConfigurationFactory());
final Module predefinedModule = ((ModuleBasedConfiguration)template.getConfiguration()).getConfigurationModule().getModule();
if (predefinedModule != null) {
configuration.setModule(predefinedModule);
}
//potentially this set won't be big, it reflects modules from where user starts his tests
final Collection<String> modules = testDiscoveryIndex.getTestModulesByMethodName(position.first, position.second);
if (modules.isEmpty()) return true;
final ModuleManager moduleManager = ModuleManager.getInstance(project);
final Set<Module> allModules = new HashSet<>(Arrays.asList(moduleManager.getModules()));
modules.stream()
.map(moduleManager::findModuleByName)
.filter(module -> module != null)
.forEach(module -> {
final List<Module> dependentModules = ModuleUtilCore.getAllDependentModules(module);
dependentModules.add(module);
allModules.retainAll(dependentModules);
});
if (!allModules.isEmpty()) {
configuration.setModule(allModules.iterator().next());
}
return true;
}
catch (IOException e) {
return false;
}
configuration.setPosition(position);
configuration.setName("Tests for " + StringUtil.getShortName(position.first) + "." + position.second);
setupPackageConfiguration(configurationContext, configuration, TestSearchScope.MODULE_WITH_DEPENDENCIES);
return true;
}
return false;
}
@@ -46,7 +46,6 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@@ -160,7 +159,7 @@ public class TestDiscoveryExtension extends RunConfigurationExtension {
if (testMethodTraces != null) {
for (File testMethodTrace : testMethodTraces) {
try {
coverageIndex.updateFromTestTrace(testMethodTrace);
coverageIndex.updateFromTestTrace(testMethodTrace, ((JavaTestConfigurationBase)configuration).getConfigurationModule().getModule());
FileUtil.delete(testMethodTrace);
}
catch (IOException e) {
@@ -187,7 +186,8 @@ public class TestDiscoveryExtension extends RunConfigurationExtension {
final File testMethodTrace = new File(tracesDirectory, className + "-" + methodName + ".tr");
if (testMethodTrace.exists()) {
try {
coverageIndex.updateFromTestTrace(testMethodTrace);
coverageIndex.updateFromTestTrace(testMethodTrace,
((JavaTestConfigurationBase)configuration).getConfigurationModule().getModule());
FileUtil.delete(testMethodTrace);
}
catch (IOException e) {
@@ -18,10 +18,12 @@ package com.intellij.execution.testDiscovery;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.ProjectComponent;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.startup.StartupManager;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.newvfs.persistent.FlushingDaemon;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.io.*;
import gnu.trove.TIntArrayList;
import gnu.trove.TIntHashSet;
@@ -80,7 +82,9 @@ public class TestDiscoveryIndex implements ProjectComponent {
final int testNameId = holder.myTestNameEnumerator.tryEnumerate(testName);
if (testNameId == 0) return;
doUpdateFromDiff(holder, testNameId, null, holder.myTestNameToUsedClassesAndMethodMap.get(testNameId));
doUpdateFromDiff(holder, testNameId, null,
holder.myTestNameToUsedClassesAndMethodMap.get(testNameId),
null);
} catch (Throwable throwable) {
thingsWentWrongLetsReinitialize(holder, throwable);
}
@@ -109,6 +113,31 @@ public class TestDiscoveryIndex implements ProjectComponent {
}
}
public Collection<String> getTestModulesByMethodName(@NotNull String classFQName, @NotNull String methodName) throws IOException {
synchronized (ourLock) {
Holder holder = null;
try {
holder = getHolder();
final TIntArrayList list = holder.myTestNameToNearestModule.get(
createKey(
holder.myClassEnumerator.enumerate(classFQName),
holder.myMethodEnumerator.enumerate(methodName)
)
);
if (list == null) return Collections.emptyList();
final ArrayList<String> result = new ArrayList<String>(list.size());
for (int moduleNameId : list.toNativeArray()) {
result.add(holder.myModuleNameEnumerator.valueOf(moduleNameId));
}
return result;
} catch (Throwable throwable) {
thingsWentWrongLetsReinitialize(holder, throwable);
return Collections.emptyList();
}
}
}
private Holder getHolder() {
Holder holder = myHolder;
@@ -154,17 +183,19 @@ public class TestDiscoveryIndex implements ProjectComponent {
public void projectClosed() {
}
private static final int VERSION = 2;
private static final int VERSION = 3;
private final class Holder {
final PersistentHashMap<Long, TIntArrayList> myMethodQNameToTestNames;
final PersistentHashMap<Integer, TIntObjectHashMap<TIntArrayList>> myTestNameToUsedClassesAndMethodMap;
final PersistentHashMap<Long, TIntArrayList> myTestNameToNearestModule;
final PersistentStringEnumerator myClassEnumerator;
final CachingEnumerator<String> myClassEnumeratorCache;
final PersistentStringEnumerator myMethodEnumerator;
final CachingEnumerator<String> myMethodEnumeratorCache;
final PersistentStringEnumerator myTestNameEnumerator;
final List<PersistentEnumeratorDelegate> myConstructedDataFiles = new ArrayList<PersistentEnumeratorDelegate>(4);
final PersistentStringEnumerator myModuleNameEnumerator;
final List<PersistentEnumeratorDelegate> myConstructedDataFiles = new ArrayList<PersistentEnumeratorDelegate>(6);
private ScheduledFuture<?> myFlushingFuture;
private boolean myDisposed;
@@ -178,6 +209,8 @@ public class TestDiscoveryIndex implements ProjectComponent {
final File classNameEnumeratorFile = new File(path + File.separator + "classNameEnumerator.data");
final File methodNameEnumeratorFile = new File(path + File.separator + "methodNameEnumerator.data");
final File testNameEnumeratorFile = new File(path + File.separator + "testNameEnumerator.data");
final File moduleNameEnumeratorFile = new File(path + File.separator + "moduleNameEnumerator.data");
final File testNameToNearestModuleFile = new File(path + File.separator + "testNameToNearestModule.data");
try {
int version = readVersion(versionFile);
@@ -190,9 +223,11 @@ public class TestDiscoveryIndex implements ProjectComponent {
PersistentHashMap<Long, TIntArrayList> methodQNameToTestNames;
PersistentHashMap<Integer, TIntObjectHashMap<TIntArrayList>> testNameToUsedClassesAndMethodMap;
PersistentHashMap<Long, TIntArrayList> testNameToNearestModule;
PersistentStringEnumerator classNameEnumerator;
PersistentStringEnumerator methodNameEnumerator;
PersistentStringEnumerator testNameEnumerator;
PersistentStringEnumerator moduleNameEnumerator;
int iterations = 0;
@@ -214,12 +249,20 @@ public class TestDiscoveryIndex implements ProjectComponent {
);
myConstructedDataFiles.add(testNameToUsedClassesAndMethodMap);
testNameToNearestModule = new PersistentHashMap<Long, TIntArrayList>(testNameToNearestModuleFile,
new MethodQNameSerializer(),
new TestNamesExternalizer());
myConstructedDataFiles.add(testNameToNearestModule);
classNameEnumerator = new PersistentStringEnumerator(classNameEnumeratorFile);
myConstructedDataFiles.add(classNameEnumerator);
methodNameEnumerator = new PersistentStringEnumerator(methodNameEnumeratorFile);
myConstructedDataFiles.add(methodNameEnumerator);
moduleNameEnumerator = new PersistentStringEnumerator(moduleNameEnumeratorFile);
myConstructedDataFiles.add(methodNameEnumerator);
testNameEnumerator = new PersistentStringEnumerator(testNameEnumeratorFile);
myConstructedDataFiles.add(testNameEnumerator);
@@ -242,9 +285,11 @@ public class TestDiscoveryIndex implements ProjectComponent {
myMethodQNameToTestNames = methodQNameToTestNames;
myTestNameToUsedClassesAndMethodMap = testNameToUsedClassesAndMethodMap;
myTestNameToNearestModule = testNameToNearestModule;
myClassEnumerator = classNameEnumerator;
myMethodEnumerator = methodNameEnumerator;
myTestNameEnumerator = testNameEnumerator;
myModuleNameEnumerator = moduleNameEnumerator;
myMethodEnumeratorCache = new CachingEnumerator<String>(methodNameEnumerator, EnumeratorStringDescriptor.INSTANCE);
myClassEnumeratorCache = new CachingEnumerator<String>(classNameEnumerator, EnumeratorStringDescriptor.INSTANCE);
@@ -416,14 +461,14 @@ public class TestDiscoveryIndex implements ProjectComponent {
return val1.equals(val2);
}
}
public void updateFromTestTrace(@NotNull File file) throws IOException {
public void updateFromTestTrace(@NotNull File file, @Nullable Module module) throws IOException {
int fileNameDotIndex = file.getName().lastIndexOf('.');
final String testName = fileNameDotIndex != -1 ? file.getName().substring(0, fileNameDotIndex) : file.getName();
doUpdateFromTestTrace(file, testName);
doUpdateFromTestTrace(file, testName, module);
}
private void doUpdateFromTestTrace(File file, final String testName) throws IOException {
private void doUpdateFromTestTrace(File file, final String testName, @Nullable Module module) throws IOException {
synchronized (ourLock) {
Holder holder = getHolder();
if (holder.myDisposed) return;
@@ -432,7 +477,7 @@ public class TestDiscoveryIndex implements ProjectComponent {
TIntObjectHashMap<TIntArrayList> classData = loadClassAndMethodsMap(file, holder);
TIntObjectHashMap<TIntArrayList> previousClassData = holder.myTestNameToUsedClassesAndMethodMap.get(testNameId);
doUpdateFromDiff(holder, testNameId, classData, previousClassData);
doUpdateFromDiff(holder, testNameId, classData, previousClassData, module != null ? holder.myModuleNameEnumerator.enumerate(module.getName()) : null);
} catch (Throwable throwable) {
thingsWentWrongLetsReinitialize(holder, throwable);
}
@@ -442,7 +487,8 @@ public class TestDiscoveryIndex implements ProjectComponent {
private void doUpdateFromDiff(Holder holder,
final int testNameId,
@Nullable TIntObjectHashMap<TIntArrayList> classData,
@Nullable TIntObjectHashMap<TIntArrayList> previousClassData) throws IOException {
@Nullable TIntObjectHashMap<TIntArrayList> previousClassData,
@Nullable Integer moduleId) throws IOException {
ValueDiff valueDiff = new ValueDiff(classData, previousClassData);
if (valueDiff.hasRemovedDelta()) {
@@ -471,6 +517,15 @@ public class TestDiscoveryIndex implements ProjectComponent {
DataInputOutputUtil.writeINT(dataOutput, testNameId);
}
});
if (moduleId != null) {
holder.myTestNameToNearestModule.appendData(createKey(classQName, methodName),
new PersistentHashMap.ValueDataAppender() {
@Override
public void append(DataOutput dataOutput) throws IOException {
DataInputOutputUtil.writeINT(dataOutput, moduleId);
}
});
}
}
}
}