Test data from different tests bleed into custom index

已回答

I'm writing a plugin that is using a custom `FileBasedIndexExtension`. My problem is that the test data files seem to bleed in other tests so that the index contains more entries than expected.

// index
package com.example.indexconfusion;

import com.intellij.ide.highlighter.XmlFileType;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.indexing.DataIndexer;
import com.intellij.util.indexing.DefaultFileTypeSpecificInputFilter;
import com.intellij.util.indexing.FileBasedIndex;
import com.intellij.util.indexing.FileContent;
import com.intellij.util.indexing.ID;
import com.intellij.util.indexing.ScalarIndexExtension;
import com.intellij.util.io.EnumeratorStringDescriptor;
import com.intellij.util.io.KeyDescriptor;
import org.jetbrains.annotations.NotNull;

import java.util.Collections;

public class MyIndex extends ScalarIndexExtension<String> {
public static final ID<String, Void> NAME = ID.create("com.example.id");

@Override
public @NotNull ID<String, Void> getName() { return NAME; }

@Override
public @NotNull DataIndexer<String, Void, FileContent> getIndexer() {
return inputData -> {
XmlFile xmlFile = (XmlFile) inputData.getPsiFile();
XmlTag rootTag = xmlFile.getRootTag();
if (rootTag == null) return Collections.emptyMap();
String id = rootTag.getAttributeValue("id");
if (id == null) return Collections.emptyMap();
return Collections.singletonMap(id, null);
};
}

@Override
public @NotNull KeyDescriptor<String> getKeyDescriptor() {
return new EnumeratorStringDescriptor();
}

@Override
public int getVersion() { return 2; }

@Override
public FileBasedIndex.@NotNull InputFilter getInputFilter() {
return new DefaultFileTypeSpecificInputFilter(XmlFileType.INSTANCE) {
@Override
public boolean acceptInput(@NotNull VirtualFile file) {
return "xml".equals(file.getExtension()) && file.isInLocalFileSystem();
}
};
}

@Override
public boolean dependsOnFileContent() { return true; }
}

 

// Test
package com.example;

import com.example.indexconfusion.MyIndex;
import com.intellij.openapi.project.Project;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.testFramework.fixtures.BasePlatformTestCase;
import com.intellij.util.Processor;
import com.intellij.util.indexing.FileBasedIndex;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import static org.assertj.core.api.Assertions.assertThat;

public class IndexConfusion1Test extends BasePlatformTestCase {

@Override
protected String getBasePath() { return "one"; }

public void testIndex() {
myFixture.copyDirectoryToProject("", "");
Project project = myFixture.getProject();
GlobalSearchScope xmls = Objects.requireNonNull(ScopeUtil.getScope(project, "xmls"));
List<String> ids = new ArrayList<>();
Processor<String> processor = s -> {
ids.add(s);
return true;
};
FileBasedIndex.getInstance().processAllKeys(MyIndex.NAME, processor, xmls, null);
assertThat(ids).containsExactlyInAnyOrder("one_a", "one_b");
}
}

 

package com.example;

import com.intellij.ide.highlighter.XmlFileType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.GlobalSearchScopes;
import org.jetbrains.annotations.Nullable;

public class ScopeUtil {
public static @Nullable VirtualFile getFireSettingsFile(Project project) {
for (VirtualFile contentRoot : ProjectRootManager.getInstance(project).getContentRoots()) {
VirtualFile child = contentRoot.findChild("fire.settings"); // find some marker file to get the project root
if (child != null) {
return child;
}
}
return null;
}

public static @Nullable GlobalSearchScope getScope(Project project, String relativeDirectory) {
VirtualFile fireSettingsFile = getFireSettingsFile(project);
if (fireSettingsFile == null) return null;
VirtualFile targetDir = fireSettingsFile.getParent().findFileByRelativePath(relativeDirectory);
if (targetDir == null) return null;
GlobalSearchScope scope = GlobalSearchScopes.directoryScope(project, targetDir, true);
return GlobalSearchScope.getScopeRestrictedByFileTypes(scope, XmlFileType.INSTANCE);
}
}

 

Test data:

// one_a.xml
<dummy id="one_a"/>

// one_b.xml
<dummy id="one_b"/>

 

And the tests fail with the following message:

Expecting actual:
  ["one_a", "one_b", "two_b", "two_a"]
to contain exactly in any order:
  ["one_a", "one_b"]
but the following elements were unexpected:
  ["two_b", "two_a"]

 

I'm not sure how to fix this behavior.

  • Is the acquisition of `GlobalSearchScope` not correct?
  • Should I drop all indexes after each test?
  • Should I use a heavy weight test case instead to create a new project?

Here is a minimal reproducible test project. Just run `gradle check` and the tests should be failing.

Upload id: 2022_06_24_bzCuuqFCLbvAF5EUXTxjE3 (file: index-confusion.zip)

0

Hi Dominik,

Indexes are an application-level concept and if you run tests, a single instance of headless IDE is used to execute tests, so it contains all the indexed data from multiple tests.

To solve the problem, I suggest rebuilding the index used by a given test before it. To rebuild the index, use:

FileBasedIndex.getInstance().requestRebuild(NAME)
1

Hi Karol,

thanks for your quick answer! By adding this snippet before the actual test execution the tests pass:

FileBasedIndex.getInstance().requestRebuild(id);
PlatformTestUtil.dispatchAllEventsInIdeEventQueue();

Without the second line the tests might fail because the IDE is still indexing in the background.

0

请先登录再写评论。