blob: f9e58e9751d67fe521531c415101094d100bfe45 [file] [log] [blame]
/*
* Copyright 2000-2012 JetBrains s.r.o.
*
* 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 com.intellij.execution.testframework.export;
import com.intellij.diagnostic.LogMessageEx;
import com.intellij.execution.ExecutionBundle;
import com.intellij.execution.configurations.RunConfiguration;
import com.intellij.execution.testframework.TestFrameworkRunningModel;
import com.intellij.ide.BrowserUtil;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.diagnostic.Attachment;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.progress.PerformInBackgroundOption;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.util.ExceptionUtil;
import com.intellij.util.PathUtil;
import com.intellij.util.io.URLUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.xml.sax.SAXException;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Source;
import javax.xml.transform.TransformerException;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TransformerHandler;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.net.URL;
public class ExportTestResultsAction extends DumbAwareAction {
private static final String ID = "ExportTestResults";
private static final Logger LOG = Logger.getInstance(ExportTestResultsAction.class.getName());
private TestFrameworkRunningModel myModel;
private String myToolWindowId;
private RunConfiguration myRunConfiguration;
public static ExportTestResultsAction create(String toolWindowId, RunConfiguration runtimeConfiguration) {
ExportTestResultsAction action = new ExportTestResultsAction();
action.copyFrom(ActionManager.getInstance().getAction(ID));
action.myToolWindowId = toolWindowId;
action.myRunConfiguration = runtimeConfiguration;
return action;
}
public void setModel(TestFrameworkRunningModel model) {
myModel = model;
}
@Override
public void update(AnActionEvent e) {
e.getPresentation().setEnabled(isEnabled(e.getDataContext()));
}
private boolean isEnabled(DataContext dataContext) {
if (myModel == null) {
return false;
}
if (CommonDataKeys.PROJECT.getData(dataContext) == null) {
return false;
}
return !myModel.getRoot().isInProgress();
}
@Override
public void actionPerformed(AnActionEvent e) {
final Project project = CommonDataKeys.PROJECT.getData(e.getDataContext());
LOG.assertTrue(project != null);
final ExportTestResultsConfiguration config = ExportTestResultsConfiguration.getInstance(project);
final String name = ExecutionBundle.message("export.test.results.filename", PathUtil.suggestFileName(myRunConfiguration.getName()));
String filename = name + "." + config.getExportFormat().getDefaultExtension();
boolean showDialog = true;
while (showDialog) {
final ExportTestResultsDialog d = new ExportTestResultsDialog(project, config, filename);
d.show();
if (!d.isOK()) {
return;
}
filename = d.getFileName();
showDialog = getOutputFile(config, project, filename).exists()
&& Messages.showOkCancelDialog(
project,
ExecutionBundle.message("export.test.results.file.exists.message", filename),
ExecutionBundle.message("export.test.results.file.exists.title"),
Messages.getQuestionIcon()
) != DialogWrapper.OK_EXIT_CODE;
}
final String filename_ = filename;
ProgressManager.getInstance().run(
new Task.Backgroundable(project, ExecutionBundle.message("export.test.results.task.name"), false, new PerformInBackgroundOption() {
@Override
public boolean shouldStartInBackground() {
return true;
}
@Override
public void processSentToBackground() {
}
}) {
@Override
public void run(@NotNull ProgressIndicator indicator) {
indicator.setIndeterminate(true);
final File outputFile = getOutputFile(config, project, filename_);
final String outputText;
try {
outputText = getOutputText(config);
if (outputText == null) {
return;
}
}
catch (IOException ex) {
LOG.warn(ex);
showBalloon(project, MessageType.ERROR, ExecutionBundle.message("export.test.results.failed", ex.getMessage()), null);
return;
}
catch (TransformerException ex) {
LOG.warn(ex);
showBalloon(project, MessageType.ERROR, ExecutionBundle.message("export.test.results.failed", ex.getMessage()), null);
return;
}
catch (SAXException ex) {
LOG.warn(ex);
showBalloon(project, MessageType.ERROR, ExecutionBundle.message("export.test.results.failed", ex.getMessage()), null);
return;
}
catch (RuntimeException ex) {
ExportTestResultsConfiguration c = new ExportTestResultsConfiguration();
c.setExportFormat(ExportTestResultsConfiguration.ExportFormat.Xml);
c.setOpenResults(false);
try {
String xml = getOutputText(c);
LOG.error(LogMessageEx.createEvent("Failed to export test results", ExceptionUtil.getThrowableText(ex), null, null,
new Attachment("dump.xml", xml)));
}
catch (Throwable ignored) {
LOG.error("Failed to export test results", ex);
}
return;
}
final Ref<VirtualFile> result = new Ref<VirtualFile>();
final Ref<String> error = new Ref<String>();
ApplicationManager.getApplication().invokeAndWait(new Runnable() {
@Override
public void run() {
result.set(ApplicationManager.getApplication().runWriteAction(new Computable<VirtualFile>() {
@Override
public VirtualFile compute() {
outputFile.getParentFile().mkdirs();
final VirtualFile parent = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(outputFile.getParentFile());
if (parent == null || !parent.isValid()) {
error.set(ExecutionBundle.message("failed.to.create.output.file", outputFile.getPath()));
return null;
}
try {
VirtualFile result = parent.createChildData(this, outputFile.getName());
VfsUtil.saveText(result, outputText);
return result;
}
catch (IOException e) {
LOG.warn(e);
error.set(e.getMessage());
return null;
}
}
}));
}
}, ModalityState.defaultModalityState());
if (!result.isNull()) {
if (config.isOpenResults()) {
openEditorOrBrowser(result.get(), project, config.getExportFormat() == ExportTestResultsConfiguration.ExportFormat.Xml);
}
else {
HyperlinkListener listener = new HyperlinkListener() {
@Override
public void hyperlinkUpdate(HyperlinkEvent e) {
if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
openEditorOrBrowser(result.get(), project, config.getExportFormat() == ExportTestResultsConfiguration.ExportFormat.Xml);
}
}
};
showBalloon(project, MessageType.INFO, ExecutionBundle.message("export.test.results.succeeded", outputFile.getName()),
listener);
}
}
else {
showBalloon(project, MessageType.ERROR, ExecutionBundle.message("export.test.results.failed", error.get()), null);
}
}
});
}
@NotNull
private static File getOutputFile(
final @NotNull ExportTestResultsConfiguration config,
final @NotNull Project project,
final @NotNull String filename)
{
final File outputFolder;
final String outputFolderPath = config.getOutputFolder();
if (!StringUtil.isEmptyOrSpaces(outputFolderPath)) {
if (FileUtil.isAbsolute(outputFolderPath)) {
outputFolder = new File(outputFolderPath);
}
else {
outputFolder = new File(new File(project.getBasePath()), config.getOutputFolder());
}
}
else {
outputFolder = new File(project.getBasePath());
}
return new File(outputFolder, filename);
}
private static void openEditorOrBrowser(final VirtualFile result, final Project project, final boolean editor) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
if (editor) {
FileEditorManager.getInstance(project).openFile(result, true);
}
else {
BrowserUtil.browse(result);
}
}
});
}
@Nullable
private String getOutputText(ExportTestResultsConfiguration config) throws IOException, TransformerException, SAXException {
ExportTestResultsConfiguration.ExportFormat exportFormat = config.getExportFormat();
SAXTransformerFactory transformerFactory = (SAXTransformerFactory)SAXTransformerFactory.newInstance();
TransformerHandler handler;
if (exportFormat == ExportTestResultsConfiguration.ExportFormat.Xml) {
handler = transformerFactory.newTransformerHandler();
handler.getTransformer().setOutputProperty(OutputKeys.INDENT, "yes");
handler.getTransformer().setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
}
else {
Source xslSource;
if (config.getExportFormat() == ExportTestResultsConfiguration.ExportFormat.BundledTemplate) {
URL bundledXsltUrl = getClass().getResource("intellij-export.xsl");
xslSource = new StreamSource(URLUtil.openStream(bundledXsltUrl));
}
else {
File xslFile = new File(config.getUserTemplatePath());
if (!xslFile.isFile()) {
showBalloon(myRunConfiguration.getProject(), MessageType.ERROR,
ExecutionBundle.message("export.test.results.custom.template.not.found", xslFile.getPath()), null);
return null;
}
xslSource = new StreamSource(xslFile);
}
handler = transformerFactory.newTransformerHandler(xslSource);
handler.getTransformer().setParameter("TITLE", ExecutionBundle.message("export.test.results.filename", myRunConfiguration.getName(),
myRunConfiguration.getType().getDisplayName()));
}
StringWriter w = new StringWriter();
handler.setResult(new StreamResult(w));
TestResultsXmlFormatter.execute(myModel.getRoot(), myRunConfiguration, handler);
return w.toString();
}
private void showBalloon(final Project project, final MessageType type, final String text, @Nullable final HyperlinkListener listener) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
public void run() {
if (project.isDisposed()) return;
if (ToolWindowManager.getInstance(project).getToolWindow(myToolWindowId) != null) {
ToolWindowManager.getInstance(project).notifyByBalloon(myToolWindowId, type, text, null, listener);
}
}
});
}
}