package com.google.jstestdriver.idea.execution; import com.google.jstestdriver.idea.assertFramework.JstdRunElement; import com.google.jstestdriver.idea.assertFramework.TestFileStructureManager; import com.google.jstestdriver.idea.assertFramework.TestFileStructurePack; import com.google.jstestdriver.idea.config.JstdConfigFileUtils; import com.google.jstestdriver.idea.execution.settings.JstdConfigType; import com.google.jstestdriver.idea.execution.settings.JstdRunSettings; import com.google.jstestdriver.idea.execution.settings.ServerType; import com.google.jstestdriver.idea.execution.settings.TestType; import com.google.jstestdriver.idea.util.CastUtils; import com.intellij.execution.Location; import com.intellij.execution.PsiLocation; import com.intellij.execution.RunnerAndConfigurationSettings; import com.intellij.execution.actions.ConfigurationContext; import com.intellij.execution.junit.RuntimeConfigurationProducer; import com.intellij.lang.javascript.JavaScriptSupportLoader; import com.intellij.lang.javascript.psi.JSFile; import com.intellij.openapi.fileTypes.FileTypeManager; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; import java.util.List; import java.util.StringTokenizer; public class JstdRuntimeConfigurationProducer extends RuntimeConfigurationProducer { private static final JstdRunSettingsProvider[] RUN_SETTINGS_PROVIDERS = { new JstdDirectoryRunSettingsProvider(), new JstdConfigFileRunSettingsProvider(), new TestElementRunSettingsProvider(), new JsFileRunSettingsProvider() }; private PsiElement mySourceElement; public JstdRuntimeConfigurationProducer() { super(JstdConfigurationType.getInstance()); } @Override public PsiElement getSourceElement() { return mySourceElement; } @Override protected RunnerAndConfigurationSettings createConfigurationByElement(Location location, ConfigurationContext context) { @SuppressWarnings({"unchecked"}) RunSettingsContext runSettingsContext = buildRunSettingsContext(location); if (runSettingsContext == null) { return null; } final RunnerAndConfigurationSettings runnerSettings = cloneTemplateConfiguration(location.getProject(), context); if (!(runnerSettings.getConfiguration() instanceof JstdRunConfiguration)) { return null; } JstdRunConfiguration runConfiguration = (JstdRunConfiguration) runnerSettings.getConfiguration(); runConfiguration.setRunSettings(runSettingsContext.runSettings); mySourceElement = runSettingsContext.psiElement; runnerSettings.setName(runConfiguration.suggestedName()); return runnerSettings; } @SuppressWarnings({"RawUseOfParameterizedType"}) @Override protected RunnerAndConfigurationSettings findExistingByElement(final Location location, @NotNull final RunnerAndConfigurationSettings[] existingConfigurations, ConfigurationContext context) { RunSettingsContext runSettingsContext = buildRunSettingsContext(location); if (runSettingsContext == null) { return null; } final JstdRunSettings runSettingsPattern = runSettingsContext.runSettings; RunnerAndConfigurationSettings bestRaCSettings = null; for (RunnerAndConfigurationSettings candidateRaCSettings : existingConfigurations) { JstdRunConfiguration runConfiguration = CastUtils.tryCast(candidateRaCSettings.getConfiguration(), JstdRunConfiguration.class); if (runConfiguration == null) { continue; } JstdRunSettings runSettingsCandidate = runConfiguration.getRunSettings(); TestType patternTestType = runSettingsPattern.getTestType(); if (patternTestType == runSettingsCandidate.getTestType()) { if (runSettingsPattern.getTestType() == TestType.ALL_CONFIGS_IN_DIRECTORY) { File dir1 = new File(runSettingsPattern.getDirectory()); File dir2 = new File(runSettingsCandidate.getDirectory()); if (dir1.isDirectory() && dir2.isDirectory() && dir1.equals(dir2)) { bestRaCSettings = candidateRaCSettings; break; } } else if (patternTestType == TestType.CONFIG_FILE) { File configFilePattern = new File(runSettingsPattern.getConfigFile()); File configFileCandidate = new File(runSettingsCandidate.getConfigFile()); if (configFilePattern.isFile() && configFileCandidate.isFile() && configFilePattern.equals(configFileCandidate)) { bestRaCSettings = candidateRaCSettings; break; } } else if (patternTestType == TestType.JS_FILE || patternTestType == TestType.TEST_CASE || patternTestType == TestType.TEST_METHOD) { File jsFilePattern = new File(runSettingsPattern.getJsFilePath()); File jsFileCandidate = new File(runSettingsCandidate.getJsFilePath()); boolean eq = jsFileCandidate.isFile() && jsFilePattern.equals(jsFileCandidate); if (patternTestType == TestType.TEST_CASE) { eq = eq && runSettingsPattern.getTestCaseName().equals(runSettingsCandidate.getTestCaseName()); } if (patternTestType == TestType.TEST_METHOD) { eq = eq && runSettingsPattern.getTestMethodName().equals(runSettingsCandidate.getTestMethodName()); } if (eq) { if (bestRaCSettings == null) { bestRaCSettings = candidateRaCSettings; } else { JstdRunConfiguration bestRunConfiguration = CastUtils.tryCast(bestRaCSettings.getConfiguration(), JstdRunConfiguration.class); if (bestRunConfiguration != null) { JstdRunSettings bestRunSettings = bestRunConfiguration.getRunSettings(); if (bestRunSettings.getConfigType() == JstdConfigType.GENERATED) { bestRaCSettings = candidateRaCSettings; } else if (!new File(bestRunSettings.getConfigFile()).isFile()) { bestRaCSettings = candidateRaCSettings; } } } } } } } return bestRaCSettings; } @Nullable private static RunSettingsContext buildRunSettingsContext(@Nullable Location<PsiElement> location) { if (location instanceof PsiLocation) { PsiElement element = location.getPsiElement(); JstdRunSettings runSettings = findJstdRunSettings(element); if (runSettings != null) { return new RunSettingsContext(runSettings, element); } } return null; } @Nullable private static JstdRunSettings findJstdRunSettings(@NotNull PsiElement element) { for (JstdRunSettingsProvider jstdRunSettingsProvider : RUN_SETTINGS_PROVIDERS) { JstdRunSettings runSettings = jstdRunSettingsProvider.provideSettings(element); if (runSettings != null) { return runSettings; } } return null; } private static class RunSettingsContext { final JstdRunSettings runSettings; final PsiElement psiElement; private RunSettingsContext(JstdRunSettings runSettings, PsiElement psiElement) { this.runSettings = runSettings; this.psiElement = psiElement; } } private interface JstdRunSettingsProvider { @Nullable JstdRunSettings provideSettings(@NotNull PsiElement psiElement); } private static class JsFileRunSettingsProvider implements JstdRunSettingsProvider { private static boolean matchTestSource(String s) { StringTokenizer st = new StringTokenizer(s, "-_"); while (st.hasMoreTokens()) { String token = st.nextToken().toLowerCase(); if ("test".equals(token)) { return true; } } return false; } @Override public JstdRunSettings provideSettings(@NotNull PsiElement psiElement) { PsiFile psiFile = psiElement.getContainingFile(); if (psiFile == null) { return null; } final VirtualFile virtualFile = psiFile.getVirtualFile(); if (virtualFile == null || FileTypeManager.getInstance().getFileTypeByFile(virtualFile) != JavaScriptSupportLoader.JAVASCRIPT) { return null; } VirtualFile entity = virtualFile; VirtualFile projectDir = psiElement.getProject().getBaseDir(); boolean testFound = false; while (entity != null) { if (matchTestSource(entity.getName())) { testFound = true; } if (entity.equals(projectDir)) { break; } entity = entity.getParent(); } if (entity == projectDir && testFound) { JstdRunSettings.Builder builder = new JstdRunSettings.Builder(); builder.setTestType(TestType.JS_FILE); builder.setConfigType(JstdConfigType.GENERATED); builder.setJSFilePath(virtualFile.getPath()); builder.setServerType(ServerType.INTERNAL); return builder.build(); } return null; } } private static class JstdConfigFileRunSettingsProvider implements JstdRunSettingsProvider { @Override public JstdRunSettings provideSettings(@NotNull PsiElement psiElement) { PsiFile psiFile = psiElement.getContainingFile(); if (psiFile != null) { VirtualFile virtualFile = psiFile.getVirtualFile(); if (virtualFile != null && JstdConfigFileUtils.isJstdConfigFile(virtualFile)) { JstdRunSettings.Builder builder = new JstdRunSettings.Builder(); builder.setConfigFile(virtualFile.getPath()) .setServerType(ServerType.INTERNAL); return builder.build(); } } return null; } } private static class JstdDirectoryRunSettingsProvider implements JstdRunSettingsProvider { @Override public JstdRunSettings provideSettings(@NotNull PsiElement psiElement) { if (!(psiElement instanceof PsiDirectory)) { return null; } PsiDirectory psiDirectory = (PsiDirectory) psiElement; VirtualFile directory = psiDirectory.getVirtualFile(); List<VirtualFile> jstdConfigs = JstdClientCommandLineBuilder.INSTANCE.collectJstdConfigFilesInDirectory(directory); if (jstdConfigs.isEmpty()) { return null; } JstdRunSettings.Builder builder = new JstdRunSettings.Builder(); builder.setTestType(TestType.ALL_CONFIGS_IN_DIRECTORY) .setDirectory(directory.getPath()) .setServerType(ServerType.INTERNAL); return builder.build(); } } private static class TestElementRunSettingsProvider implements JstdRunSettingsProvider { @Override public JstdRunSettings provideSettings(@NotNull PsiElement psiElement) { JSFile jsFile = CastUtils.tryCast(psiElement.getContainingFile(), JSFile.class); VirtualFile virtualFile = jsFile != null ? jsFile.getVirtualFile() : null; if (virtualFile == null) { return null; } TestFileStructurePack pack = TestFileStructureManager.fetchTestFileStructurePackByJsFile(jsFile); if (pack != null) { JstdRunElement jstdRunElement = pack.getJstdRunElement(psiElement); if (jstdRunElement != null) { String testMethodName = jstdRunElement.getTestMethodName(); if (testMethodName != null) { return new JstdRunSettings.Builder() .setTestType(TestType.TEST_METHOD) .setJSFilePath(virtualFile.getPath()) .setTestCaseName(jstdRunElement.getTestCaseName()) .setTestMethodName(testMethodName) .build(); } else { return new JstdRunSettings.Builder() .setTestType(TestType.TEST_CASE) .setJSFilePath(virtualFile.getPath()) .setTestCaseName(jstdRunElement.getTestCaseName()) .build(); } } } return null; } } @Override public int compareTo(Object o) { return PREFERED; } }