/* * Copyright 2012-2014 Sergey Ignatov * * 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.intellij.erlang.rebar.runner; import com.intellij.execution.DefaultExecutionResult; import com.intellij.execution.ExecutionException; import com.intellij.execution.ExecutionResult; import com.intellij.execution.Executor; import com.intellij.execution.configurations.CommandLineState; import com.intellij.execution.configurations.GeneralCommandLine; import com.intellij.execution.process.ProcessHandler; import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.runners.ProgramRunner; import com.intellij.execution.testframework.autotest.ToggleAutoTestAction; import com.intellij.execution.testframework.sm.SMTestRunnerConnectionUtil; import com.intellij.execution.testframework.sm.runner.SMTRunnerConsoleProperties; import com.intellij.execution.testframework.sm.runner.ui.SMTRunnerConsoleView; import com.intellij.execution.testframework.ui.BaseTestsOutputConsoleView; import com.intellij.execution.ui.ConsoleView; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFileFactory; import com.intellij.psi.impl.file.PsiDirectoryFactory; import com.intellij.util.PathUtil; import com.intellij.util.Processor; import com.intellij.util.containers.ContainerUtil; import org.intellij.erlang.ErlangFileType; import org.intellij.erlang.console.ErlangConsoleUtil; import org.intellij.erlang.console.FileReferenceFilter; import org.intellij.erlang.eunit.ErlangEunitReporterModule; import org.intellij.erlang.eunit.ErlangTestConsoleProperties; import org.intellij.erlang.psi.ErlangExpression; import org.intellij.erlang.psi.ErlangFile; import org.intellij.erlang.psi.ErlangListExpression; import org.intellij.erlang.psi.ErlangTupleExpression; import org.intellij.erlang.psi.impl.ErlangElementFactory; import org.intellij.erlang.rebar.settings.RebarSettings; import org.intellij.erlang.rebar.util.ErlangTermFileUtil; import org.jetbrains.annotations.NotNull; import java.io.File; import java.io.IOException; import java.util.List; public class RebarEunitRunningState extends CommandLineState { private static final String CONFIG_FILE_NAME = "rebar.config"; private static final String EUNIT_NO_TTY_OPTION = "{no_tty, true}"; private static final String EUNIT_TEAMCITY_REPORTER = "{report,{" + ErlangEunitReporterModule.MODULE_NAME + ", []}}"; private static final String EUNIT_OPTS = "{eunit_opts,[" + EUNIT_TEAMCITY_REPORTER + "," + EUNIT_NO_TTY_OPTION + "]}."; private final RebarEunitRunConfiguration myConfiguration; public RebarEunitRunningState(@NotNull ExecutionEnvironment env, @NotNull RebarEunitRunConfiguration configuration) { super(env); myConfiguration = configuration; } @NotNull @Override public ExecutionResult execute(@NotNull Executor executor, @NotNull ProgramRunner runner) throws ExecutionException { ProcessHandler processHandler = startProcess(); setConsoleBuilder(getConsoleBuilder()); final ConsoleView consoleView = createConsoleView(executor); ErlangConsoleUtil.attachFilters(myConfiguration.getProject(), consoleView); consoleView.attachToProcess(processHandler); RebarEunitRerunFailedTestsAction rerunAction = new RebarEunitRerunFailedTestsAction(consoleView); rerunAction.init(((BaseTestsOutputConsoleView) consoleView).getProperties()); rerunAction.setModelProvider(((SMTRunnerConsoleView) consoleView)::getResultsViewer); DefaultExecutionResult executionResult = new DefaultExecutionResult(consoleView, processHandler); executionResult.setRestartActions(rerunAction, new ToggleAutoTestAction()); return executionResult; } @NotNull private ConsoleView createConsoleView(Executor executor) { SMTRunnerConsoleProperties consoleProperties = new ErlangTestConsoleProperties(myConfiguration, executor); consoleProperties.addStackTraceFilter(new FileReferenceFilter(myConfiguration.getProject(), ErlangConsoleUtil.COMPILATION_ERROR_PATH)); consoleProperties.addStackTraceFilter(new FileReferenceFilter(myConfiguration.getProject(), ErlangConsoleUtil.EUNIT_ERROR_PATH)); consoleProperties.addStackTraceFilter(new FileReferenceFilter(myConfiguration.getProject(), ErlangConsoleUtil.EUNIT_FAILURE_PATH)); return SMTestRunnerConnectionUtil.createConsole("Rebar", consoleProperties); } @NotNull @Override protected ProcessHandler startProcess() throws ExecutionException { GeneralCommandLine commandLine = RebarRunningStateUtil.getRebarCommandLine(myConfiguration); File reportsRoot = createEunitReportsEnvironment(); commandLine.getEnvironment().put("ERL_FLAGS", "-pa " + PathUtil.toSystemIndependentName(reportsRoot.getPath())); boolean rebar3 = RebarSettings.getInstance(myConfiguration.getProject()).isRebar3(); if (rebar3) { commandLine.getEnvironment().put("REBAR_CONFIG", new File(reportsRoot, CONFIG_FILE_NAME).getPath()); } else { commandLine.addParameters("-C", new File(reportsRoot, CONFIG_FILE_NAME).getPath()); } return RebarRunningStateUtil.runRebar(myConfiguration.getProject(), commandLine); } private File createEunitReportsEnvironment() throws ExecutionException { try { String workingDirectory = RebarRunningStateUtil.getWorkingDirectory(myConfiguration); File tempDirectory = FileUtil.createTempDirectory(ErlangEunitReporterModule.MODULE_NAME, null); File configFile = new File(tempDirectory, CONFIG_FILE_NAME); writeModifiedConfig(new File(workingDirectory, CONFIG_FILE_NAME), configFile); ErlangEunitReporterModule.putReporterModuleTo(tempDirectory); return tempDirectory; } catch (IOException e) { throw new ExecutionException("Failed to setup eunit reports environment", e); } } private void writeModifiedConfig(File oldConfig, final File newConfig) throws IOException { Project project = myConfiguration.getProject(); final PsiFile configPsi = createModifiedConfigPsi(oldConfig); VirtualFile outputDirectory = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(newConfig.getParentFile()); final PsiDirectory psiDirectory = outputDirectory != null ? PsiDirectoryFactory.getInstance(project).createDirectory(outputDirectory) : null; if (psiDirectory == null) { throw new IOException("Failed to save modified rebar.config"); } ApplicationManager.getApplication().runWriteAction(() -> { String name = newConfig.getName(); PsiFile prev = psiDirectory.findFile(name); if (prev != null) prev.delete(); configPsi.setName(name); psiDirectory.add(configPsi); }); } private PsiFile createModifiedConfigPsi(File oldConfig) throws IOException { Project project = myConfiguration.getProject(); String oldConfigText = oldConfig.exists() ? new String(FileUtil.loadFileText(oldConfig)) : ""; ErlangFile configPsi = (ErlangFile) PsiFileFactory.getInstance(project) .createFileFromText(CONFIG_FILE_NAME, ErlangFileType.TERMS, oldConfigText); List<ErlangTupleExpression> eunitOptsSections = ErlangTermFileUtil.getConfigSections(configPsi, "eunit_opts"); if (eunitOptsSections.isEmpty()) { configPsi.add(ErlangElementFactory.createWhitespaceFromText(project, "\n")); configPsi.add(ErlangTermFileUtil.createForm(EUNIT_OPTS)); } else { removeReportOptions(eunitOptsSections); addEunitTeamcityReportOptions(eunitOptsSections.get(0)); } return configPsi; } private static void removeReportOptions(List<ErlangTupleExpression> eunitOptsSections) { Processor<ErlangTupleExpression> deletingProcessor = erlangTupleExpression -> { ErlangTermFileUtil.deleteListExpressionItem(erlangTupleExpression); return true; }; for (ErlangTupleExpression eunitOptsSection : eunitOptsSections) { ErlangExpression eunitOptsList = eunitOptsSection.getExpressionList().get(1); ContainerUtil.process(ErlangTermFileUtil.getConfigSections(eunitOptsList, "report"), deletingProcessor); ContainerUtil.process(ErlangTermFileUtil.getConfigSections(eunitOptsList, "no_tty"), deletingProcessor); } } private void addEunitTeamcityReportOptions(ErlangTupleExpression eunitOptsTuple) throws IOException { ErlangExpression eunitOptsList = eunitOptsTuple.getExpressionList().get(1); if (!(eunitOptsList instanceof ErlangListExpression)) { throw new IOException("Invalid rebar.config file"); } ErlangTermFileUtil.addListExpressionItem((ErlangListExpression) eunitOptsList, createExpression(EUNIT_TEAMCITY_REPORTER)); ErlangTermFileUtil.addListExpressionItem((ErlangListExpression) eunitOptsList, createExpression(EUNIT_NO_TTY_OPTION)); } private ErlangExpression createExpression(String expressionText) { return ErlangElementFactory.createExpressionFromText(myConfiguration.getProject(), expressionText); } }