package com.google.jstestdriver.idea.assertFramework.jstd; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.intellij.javascript.testFramework.AbstractTestFileStructure; import com.intellij.javascript.testFramework.JstdRunElement; import com.intellij.javascript.testFramework.util.JsPsiUtils; import com.intellij.lang.javascript.psi.JSDefinitionExpression; import com.intellij.lang.javascript.psi.JSExpression; import com.intellij.lang.javascript.psi.JSFile; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; import net.jcip.annotations.NotThreadSafe; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; @NotThreadSafe public class JstdTestFileStructure extends AbstractTestFileStructure { private final List<JstdTestCaseStructure> myTestCaseStructures; private final Map<String, JstdTestCaseStructure> myTestCaseStructureByNameMap; private Map<PsiElement, String> myNameByPsiElementMap; private Map<PsiElement, Void> myPrototypeBasedTestElements; public JstdTestFileStructure(@NotNull JSFile jsFile) { super(jsFile); myTestCaseStructures = Lists.newArrayList(); myTestCaseStructureByNameMap = Maps.newHashMap(); } @Override public boolean isEmpty() { for (JstdTestCaseStructure testCaseStructure : myTestCaseStructures) { if (testCaseStructure.getTestCount() > 0) { return false; } } return true; } public List<JstdTestCaseStructure> getTestCaseStructures() { return myTestCaseStructures; } public JstdTestCaseStructure getTestCaseStructureByName(String testCaseName) { return myTestCaseStructureByNameMap.get(testCaseName); } public void addTestCaseStructure(JstdTestCaseStructure testCaseStructure) { myTestCaseStructures.add(testCaseStructure); myTestCaseStructureByNameMap.put(testCaseStructure.getName(), testCaseStructure); } public int getTestCaseCount() { return myTestCaseStructures.size(); } @Nullable public String getNameByPsiElement(@NotNull PsiElement element) { return myNameByPsiElementMap.get(element); } public boolean isPrototypeTestElement(@NotNull PsiElement element) { return myPrototypeBasedTestElements.containsKey(element); } @Override @Nullable public JstdRunElement findJstdRunElement(@NotNull TextRange textRange) { for (JstdTestCaseStructure testCaseStructure : myTestCaseStructures) { JstdRunElement jstdRunElement = testCaseStructure.findJstdRunElement(textRange); if (jstdRunElement != null) { return jstdRunElement; } } return null; } @Override public PsiElement findPsiElement(@NotNull String testCaseName, @Nullable String testMethodName) { JstdTestCaseStructure testCaseStructure = myTestCaseStructureByNameMap.get(testCaseName); if (testCaseStructure == null) { return null; } if (testMethodName == null) { return testCaseStructure.getEnclosingCallExpression(); } JstdTestStructure testStructure = testCaseStructure.getTestStructureByName(testMethodName); if (testStructure != null) { return testStructure.getTestMethodNameDeclaration(); } return null; } @Nullable public JstdTestCaseStructure findEnclosingTestCaseByOffset(int documentOffset) { for (JstdTestCaseStructure testCaseStructure : myTestCaseStructures) { TextRange testCaseCallExpressionTextRange = testCaseStructure.getEnclosingCallExpression().getTextRange(); if (JsPsiUtils.containsOffsetStrictly(testCaseCallExpressionTextRange, documentOffset)) { return testCaseStructure; } } return null; } @NotNull @Override public List<String> getTopLevelElements() { if (myTestCaseStructures.isEmpty()) { return Collections.emptyList(); } List<String> out = new ArrayList<>(myTestCaseStructures.size()); for (JstdTestCaseStructure structure : myTestCaseStructures) { out.add(structure.getName()); } return out; } @NotNull @Override public List<String> getChildrenOf(@NotNull String topLevelElementName) { JstdTestCaseStructure testCaseStructure = myTestCaseStructureByNameMap.get(topLevelElementName); if (testCaseStructure == null) { return Collections.emptyList(); } List<String> out = new ArrayList<>(testCaseStructure.getTestCount()); for (JstdTestStructure testStructure : testCaseStructure.getTestStructures()) { out.add(testStructure.getName()); } return out; } @Override public boolean contains(@NotNull String testCaseName, @Nullable String testMethodName) { return findPsiElement(testCaseName, testMethodName) != null; } void postProcess() { myNameByPsiElementMap = Collections.emptyMap(); myPrototypeBasedTestElements = Collections.emptyMap(); if (myTestCaseStructures.isEmpty()) { return; } int totalCount = 0; int prototypeBasedTestCount = 0; for (JstdTestCaseStructure testCaseStructure : myTestCaseStructures) { totalCount += testCaseStructure.getTestCount() + 1; for (JstdTestStructure testStructure : testCaseStructure.getTestStructures()) { if (testStructure.getWholeLeftDefExpr() != null) { prototypeBasedTestCount++; } } } myNameByPsiElementMap = new IdentityHashMap<>(totalCount); if (prototypeBasedTestCount > 0) { myPrototypeBasedTestElements = new IdentityHashMap<>(prototypeBasedTestCount); } for (JstdTestCaseStructure testCaseStructure : myTestCaseStructures) { JSExpression testCaseMethodExpr = testCaseStructure.getEnclosingCallExpression().getMethodExpression(); if (testCaseMethodExpr != null) { myNameByPsiElementMap.put(testCaseMethodExpr, testCaseStructure.getName()); } for (JstdTestStructure testStructure : testCaseStructure.getTestStructures()) { PsiElement anchor = testStructure.getTestMethodNameDeclaration(); myNameByPsiElementMap.put(anchor, testStructure.getName()); JSDefinitionExpression wholeLeftDefExpr = testStructure.getWholeLeftDefExpr(); if (wholeLeftDefExpr != null) { myPrototypeBasedTestElements.put(wholeLeftDefExpr, null); } } } } }