mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
+1
-1
@@ -77,7 +77,7 @@ public abstract class FacetBasedPackagingSourceItemsProvider<F extends Facet, E
|
||||
|
||||
protected abstract PackagingElement<?> createElement(ArtifactEditorContext context, F facet);
|
||||
|
||||
private static class FacetBasedSourceItem<F extends Facet> extends PackagingSourceItem {
|
||||
protected static class FacetBasedSourceItem<F extends Facet> extends PackagingSourceItem {
|
||||
private final FacetBasedPackagingSourceItemsProvider<F, ?> myProvider;
|
||||
private final F myFacet;
|
||||
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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.ui.mac;
|
||||
|
||||
import com.apple.eawt.AppEvent;
|
||||
import com.apple.eawt.FullScreenAdapter;
|
||||
import com.intellij.Patches;
|
||||
import com.intellij.openapi.wm.impl.IdeFrameImpl;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
public class MacFullScreenListener extends FullScreenAdapter {
|
||||
private final MacMainFrameDecorator myDecorator;
|
||||
private final IdeFrameImpl myFrame;
|
||||
|
||||
public MacFullScreenListener(final MacMainFrameDecorator decorator,
|
||||
final IdeFrameImpl frame) {
|
||||
myDecorator = decorator;
|
||||
myFrame = frame;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowEnteredFullScreen(AppEvent.FullScreenEvent event) {
|
||||
myDecorator.setInFullScreen(true);
|
||||
|
||||
JRootPane rootPane = myFrame.getRootPane();
|
||||
if (rootPane != null) rootPane.putClientProperty(MacMainFrameDecorator.FULL_SCREEN, Boolean.TRUE);
|
||||
if (Patches.APPLE_BUG_ID_10207064) {
|
||||
// fix problem with bottom empty bar
|
||||
// it seems like the title is still visible in fullscreen but the window itself shifted up for titlebar height
|
||||
// and the size of the frame is still calculated to be the height of the screen which is wrong
|
||||
// so just add these titlebar height to the frame height once again
|
||||
Timer timer = new Timer(300, new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
myFrame.setSize(myFrame.getWidth(), myFrame.getHeight() + myFrame.getInsets().top);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
timer.setRepeats(false);
|
||||
timer.start();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowExitedFullScreen(AppEvent.FullScreenEvent event) {
|
||||
myDecorator.setInFullScreen(false);
|
||||
myFrame.storeFullScreenStateIfNeeded(false);
|
||||
|
||||
JRootPane rootPane = myFrame.getRootPane();
|
||||
if (rootPane != null) rootPane.putClientProperty(MacMainFrameDecorator.FULL_SCREEN, null);
|
||||
}
|
||||
}
|
||||
@@ -15,9 +15,6 @@
|
||||
*/
|
||||
package com.intellij.ui.mac;
|
||||
|
||||
import com.apple.eawt.AppEvent;
|
||||
import com.apple.eawt.FullScreenAdapter;
|
||||
import com.apple.eawt.FullScreenUtilities;
|
||||
import com.intellij.Patches;
|
||||
import com.intellij.ide.ui.UISettings;
|
||||
import com.intellij.ide.ui.UISettingsListener;
|
||||
@@ -36,8 +33,6 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@@ -150,43 +145,10 @@ public class MacMainFrameDecorator implements UISettingsListener, Disposable {
|
||||
if (SystemInfo.isMacOSLion) {
|
||||
if (!FULL_SCREEN_AVAILABLE) return;
|
||||
|
||||
FullScreenUtilities.addFullScreenListenerTo(frame, new FullScreenAdapter() {
|
||||
@Override
|
||||
public void windowEnteredFullScreen(AppEvent.FullScreenEvent event) {
|
||||
myInFullScreen = true;
|
||||
|
||||
JRootPane rootPane = frame.getRootPane();
|
||||
if (rootPane != null) rootPane.putClientProperty(FULL_SCREEN, Boolean.TRUE);
|
||||
if (Patches.APPLE_BUG_ID_10207064) {
|
||||
// fix problem with bottom empty bar
|
||||
// it seems like the title is still visible in fullscreen but the window itself shifted up for titlebar height
|
||||
// and the size of the frame is still calculated to be the height of the screen which is wrong
|
||||
// so just add these titlebar height to the frame height once again
|
||||
Timer timer = new Timer(300, new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
frame.setSize(frame.getWidth(), frame.getHeight() + frame.getInsets().top);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
timer.setRepeats(false);
|
||||
timer.start();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowExitedFullScreen(AppEvent.FullScreenEvent event) {
|
||||
myInFullScreen = false;
|
||||
frame.storeFullScreenStateIfNeeded(false);
|
||||
|
||||
JRootPane rootPane = frame.getRootPane();
|
||||
if (rootPane != null) rootPane.putClientProperty(FULL_SCREEN, null);
|
||||
}
|
||||
});
|
||||
try {
|
||||
Class<?> clazz = Class.forName("com.apple.eawt.FullScreenUtilities");
|
||||
clazz.getMethod("addFullScreenListenerTo").invoke(null, frame, new MacFullScreenListener(this, frame));
|
||||
} catch (Exception ignored) {}
|
||||
} else {
|
||||
// toggle toolbar
|
||||
String className = "IdeaToolbar" + v;
|
||||
@@ -243,4 +205,8 @@ public class MacMainFrameDecorator implements UISettingsListener, Disposable {
|
||||
invoke(window, "toggleFullScreen:", window);
|
||||
}
|
||||
}
|
||||
|
||||
void setInFullScreen(boolean inFullScreen) {
|
||||
myInFullScreen = inFullScreen;
|
||||
}
|
||||
}
|
||||
|
||||
+27
-10
@@ -25,9 +25,7 @@ import com.intellij.execution.process.ProcessAdapter;
|
||||
import com.intellij.execution.process.ProcessEvent;
|
||||
import com.intellij.execution.process.ProcessHandler;
|
||||
import com.intellij.execution.testframework.TestConsoleProperties;
|
||||
import com.intellij.execution.testframework.sm.runner.GeneralToSMTRunnerEventsConvertor;
|
||||
import com.intellij.execution.testframework.sm.runner.OutputToGeneralTestEventsConverter;
|
||||
import com.intellij.execution.testframework.sm.runner.SMTRunnerConsoleProperties;
|
||||
import com.intellij.execution.testframework.sm.runner.*;
|
||||
import com.intellij.execution.testframework.sm.runner.ui.SMTRunnerConsoleView;
|
||||
import com.intellij.execution.testframework.sm.runner.ui.SMTRunnerNotificationsHandler;
|
||||
import com.intellij.execution.testframework.sm.runner.ui.SMTRunnerUIActionsHandler;
|
||||
@@ -87,6 +85,16 @@ public class SMTestRunnerConnectionUtil {
|
||||
final RunnerSettings runnerSettings,
|
||||
final ConfigurationPerRunnerSettings configurationSettings,
|
||||
@Nullable final TestLocationProvider locator) {
|
||||
return createConsoleWithCustomLocator(testFrameworkName, consoleProperties, runnerSettings,
|
||||
configurationSettings, locator, false);
|
||||
}
|
||||
|
||||
public static BaseTestsOutputConsoleView createConsoleWithCustomLocator(@NotNull final String testFrameworkName,
|
||||
@NotNull final TestConsoleProperties consoleProperties,
|
||||
final RunnerSettings runnerSettings,
|
||||
final ConfigurationPerRunnerSettings configurationSettings,
|
||||
@Nullable final TestLocationProvider locator,
|
||||
final boolean idBasedTreeConstruction) {
|
||||
// Console
|
||||
final String splitterPropertyName = testFrameworkName + ".Splitter.Proportion";
|
||||
final SMTRunnerConsoleView console =
|
||||
@@ -97,7 +105,7 @@ public class SMTestRunnerConnectionUtil {
|
||||
super.attachToProcess(processHandler);
|
||||
attachEventsProcessors(consoleProperties, getResultsViewer(),
|
||||
getResultsViewer().getStatisticsPane(),
|
||||
processHandler, testFrameworkName, locator);
|
||||
processHandler, testFrameworkName, locator, idBasedTreeConstruction);
|
||||
}
|
||||
};
|
||||
console.setHelpId("reference.runToolWindow.testResultsTab");
|
||||
@@ -200,15 +208,24 @@ public class SMTestRunnerConnectionUtil {
|
||||
final StatisticsPanel statisticsPane,
|
||||
final ProcessHandler processHandler,
|
||||
@NotNull final String testFrameworkName,
|
||||
@Nullable final TestLocationProvider locator) {
|
||||
@Nullable final TestLocationProvider locator,
|
||||
boolean idBasedTreeConstruction) {
|
||||
//build messages consumer
|
||||
final OutputToGeneralTestEventsConverter outputConsumer = consoleProperties instanceof SMCustomMessagesParsing
|
||||
? ((SMCustomMessagesParsing)consoleProperties).createTestEventsConverter(testFrameworkName, consoleProperties)
|
||||
: new OutputToGeneralTestEventsConverter(testFrameworkName, consoleProperties);
|
||||
final OutputToGeneralTestEventsConverter outputConsumer;
|
||||
if (consoleProperties instanceof SMCustomMessagesParsing) {
|
||||
outputConsumer = ((SMCustomMessagesParsing)consoleProperties).createTestEventsConverter(testFrameworkName, consoleProperties);
|
||||
}
|
||||
else {
|
||||
outputConsumer = new OutputToGeneralTestEventsConverter(testFrameworkName, consoleProperties);
|
||||
}
|
||||
|
||||
//events processor
|
||||
final GeneralToSMTRunnerEventsConvertor eventsProcessor = new GeneralToSMTRunnerEventsConvertor(resultsViewer.getTestsRootNode(),
|
||||
testFrameworkName);
|
||||
final GeneralTestEventsProcessor eventsProcessor;
|
||||
if (idBasedTreeConstruction) {
|
||||
eventsProcessor = new GeneralIdBasedToSMTRunnerEventsConvertor(resultsViewer.getTestsRootNode(), testFrameworkName);
|
||||
} else {
|
||||
eventsProcessor = new GeneralToSMTRunnerEventsConvertor(resultsViewer.getTestsRootNode(), testFrameworkName);
|
||||
}
|
||||
if (locator != null) {
|
||||
eventsProcessor.setLocator(locator);
|
||||
}
|
||||
|
||||
+633
@@ -0,0 +1,633 @@
|
||||
/*
|
||||
* 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.sm.runner;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.intellij.execution.process.ProcessOutputTypes;
|
||||
import com.intellij.execution.testframework.sm.SMRunnerUtil;
|
||||
import com.intellij.execution.testframework.sm.SMTestRunnerConnectionUtil;
|
||||
import com.intellij.execution.testframework.sm.runner.events.*;
|
||||
import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.testIntegration.TestLocationProvider;
|
||||
import gnu.trove.TIntObjectHashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Sergey Simonchik
|
||||
*/
|
||||
public class GeneralIdBasedToSMTRunnerEventsConvertor implements GeneralTestEventsProcessor {
|
||||
private static final Logger LOG = Logger.getInstance(GeneralIdBasedToSMTRunnerEventsConvertor.class.getName());
|
||||
|
||||
private final TIntObjectHashMap<Node> myNodeByIdMap = new TIntObjectHashMap<Node>();
|
||||
private final Set<Node> myRunningNodes = Sets.newHashSet();
|
||||
private final List<SMTRunnerEventsListener> myEventsListeners = new ArrayList<SMTRunnerEventsListener>();
|
||||
private final SMTestProxy.SMRootTestProxy myTestsRootProxy;
|
||||
private final Node myTestsRootNode;
|
||||
private final String myTestFrameworkName;
|
||||
private boolean myIsTestingFinished = false;
|
||||
private TestLocationProvider myLocator = null;
|
||||
|
||||
public GeneralIdBasedToSMTRunnerEventsConvertor(@NotNull SMTestProxy.SMRootTestProxy testsRootProxy,
|
||||
@NotNull String testFrameworkName) {
|
||||
myTestsRootProxy = testsRootProxy;
|
||||
myTestsRootNode = new Node(0, null, testsRootProxy);
|
||||
myTestFrameworkName = testFrameworkName;
|
||||
myNodeByIdMap.put(myTestsRootNode.getId(), myTestsRootNode);
|
||||
myRunningNodes.add(myTestsRootNode);
|
||||
}
|
||||
|
||||
public void setLocator(@NotNull TestLocationProvider customLocator) {
|
||||
myLocator = customLocator;
|
||||
}
|
||||
|
||||
public void addEventsListener(@NotNull SMTRunnerEventsListener listener) {
|
||||
myEventsListeners.add(listener);
|
||||
}
|
||||
|
||||
public void onStartTesting() {
|
||||
SMRunnerUtil.addToInvokeLater(new Runnable() {
|
||||
public void run() {
|
||||
myTestsRootProxy.setStarted();
|
||||
|
||||
fireOnTestingStarted();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTestsReporterAttached() {
|
||||
SMRunnerUtil.addToInvokeLater(new Runnable() {
|
||||
public void run() {
|
||||
myTestsRootProxy.setTestsReporterAttached();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void onFinishTesting() {
|
||||
SMRunnerUtil.addToInvokeLater(new Runnable() {
|
||||
public void run() {
|
||||
if (myIsTestingFinished) {
|
||||
// has been already invoked!
|
||||
return;
|
||||
}
|
||||
myIsTestingFinished = true;
|
||||
|
||||
// We don't know whether process was destroyed by user
|
||||
// or it finished after all tests have been run
|
||||
// Lets assume, if at finish all suites except root suite are passed
|
||||
// then all is ok otherwise process was terminated by user
|
||||
if (myRunningNodes.size() == 1 && myRunningNodes.contains(myTestsRootNode)) {
|
||||
myTestsRootProxy.setFinished();
|
||||
} else {
|
||||
logProblem("Unexpected running nodes: " + myRunningNodes);
|
||||
myTestsRootProxy.setTerminated();
|
||||
}
|
||||
myNodeByIdMap.clear();
|
||||
myRunningNodes.clear();
|
||||
|
||||
fireOnTestingFinished();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void onTestStarted(@NotNull final TestStartedEvent testStartedEvent) {
|
||||
SMRunnerUtil.addToInvokeLater(new Runnable() {
|
||||
public void run() {
|
||||
doStartNode(testStartedEvent, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void onSuiteStarted(@NotNull final TestSuiteStartedEvent suiteStartedEvent) {
|
||||
SMRunnerUtil.addToInvokeLater(new Runnable() {
|
||||
public void run() {
|
||||
doStartNode(suiteStartedEvent, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void doStartNode(@NotNull BaseStartedNodeEvent startedNodeEvent, boolean suite) {
|
||||
Node parentNode = findValidParentNode(startedNodeEvent);
|
||||
if (parentNode == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!validateNodeId(startedNodeEvent)) {
|
||||
return;
|
||||
}
|
||||
int nodeId = startedNodeEvent.getId();
|
||||
Node childNode = myNodeByIdMap.get(nodeId);
|
||||
if (childNode != null) {
|
||||
logProblem(startedNodeEvent + " has been already started: " + childNode + "!");
|
||||
return;
|
||||
}
|
||||
|
||||
SMTestProxy childProxy = new SMTestProxy(startedNodeEvent.getName(), suite, startedNodeEvent.getLocationUrl(), true);
|
||||
childNode = new Node(startedNodeEvent.getId(), parentNode, childProxy);
|
||||
myNodeByIdMap.put(nodeId, childNode);
|
||||
myRunningNodes.add(childNode);
|
||||
if (myLocator != null) {
|
||||
childProxy.setLocator(myLocator);
|
||||
}
|
||||
parentNode.getProxy().addChild(childProxy);
|
||||
|
||||
// progress started
|
||||
childProxy.setStarted();
|
||||
if (suite) {
|
||||
fireOnSuiteStarted(childProxy);
|
||||
} else {
|
||||
fireOnTestStarted(childProxy);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Node findValidParentNode(@NotNull BaseStartedNodeEvent startedNodeEvent) {
|
||||
int parentId = startedNodeEvent.getParentId();
|
||||
if (parentId < 0) {
|
||||
logProblem("Parent node id should be non-negative: " + startedNodeEvent + ".");
|
||||
return null;
|
||||
}
|
||||
Node parentNode = myNodeByIdMap.get(startedNodeEvent.getParentId());
|
||||
if (parentNode == null) {
|
||||
logProblem("Parent node is undefined for " + startedNodeEvent + ".");
|
||||
return null;
|
||||
}
|
||||
if (parentNode.getState() != State.RUNNING) {
|
||||
logProblem("Parent node should be running: " + parentNode + ", " + startedNodeEvent);
|
||||
return null;
|
||||
}
|
||||
return parentNode;
|
||||
}
|
||||
|
||||
public void onTestFinished(@NotNull final TestFinishedEvent testFinishedEvent) {
|
||||
SMRunnerUtil.addToInvokeLater(new Runnable() {
|
||||
public void run() {
|
||||
doFinishNode(testFinishedEvent, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void onSuiteFinished(@NotNull final TestSuiteFinishedEvent suiteFinishedEvent) {
|
||||
SMRunnerUtil.addToInvokeLater(new Runnable() {
|
||||
public void run() {
|
||||
doFinishNode(suiteFinishedEvent, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void doFinishNode(@NotNull TreeNodeEvent treeNodeEvent, boolean suite) {
|
||||
Node finishedNode = findNode(treeNodeEvent);
|
||||
if (finishedNode == null) {
|
||||
String nodeType = suite ? "Suite" : "Test";
|
||||
logProblem("Trying to finish not started " + nodeType + ": " + treeNodeEvent);
|
||||
return;
|
||||
}
|
||||
stopRunningNode(finishedNode, State.FINISHED, treeNodeEvent);
|
||||
finishedNode.getProxy().setFinished();
|
||||
if (suite) {
|
||||
fireOnSuiteFinished(finishedNode.getProxy());
|
||||
} else {
|
||||
fireOnTestFinished(finishedNode.getProxy());
|
||||
}
|
||||
}
|
||||
|
||||
public void onUncapturedOutput(@NotNull final String text, final Key outputType) {
|
||||
SMRunnerUtil.addToInvokeLater(new Runnable() {
|
||||
public void run() {
|
||||
Node activeNode = findActiveNode();
|
||||
SMTestProxy activeProxy = activeNode.getProxy();
|
||||
if (ProcessOutputTypes.STDERR.equals(outputType)) {
|
||||
activeProxy.addStdErr(text);
|
||||
} else if (ProcessOutputTypes.SYSTEM.equals(outputType)) {
|
||||
activeProxy.addSystemOutput(text);
|
||||
} else {
|
||||
activeProxy.addStdOutput(text, outputType);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void onError(@NotNull final String localizedMessage,
|
||||
@Nullable final String stackTrace,
|
||||
final boolean isCritical) {
|
||||
SMRunnerUtil.addToInvokeLater(new Runnable() {
|
||||
public void run() {
|
||||
Node activeNode = findActiveNode();
|
||||
SMTestProxy activeProxy = activeNode.getProxy();
|
||||
activeProxy.addError(localizedMessage, stackTrace, isCritical);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void onCustomProgressTestsCategory(@Nullable final String categoryName,
|
||||
final int testCount) {
|
||||
SMRunnerUtil.addToInvokeLater(new Runnable() {
|
||||
public void run() {
|
||||
fireOnCustomProgressTestsCategory(categoryName, testCount);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void onCustomProgressTestStarted() {
|
||||
SMRunnerUtil.addToInvokeLater(new Runnable() {
|
||||
public void run() {
|
||||
fireOnCustomProgressTestStarted();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void onCustomProgressTestFailed() {
|
||||
SMRunnerUtil.addToInvokeLater(new Runnable() {
|
||||
public void run() {
|
||||
fireOnCustomProgressTestFailed();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void onTestFailure(@NotNull final TestFailedEvent testFailedEvent) {
|
||||
SMRunnerUtil.addToInvokeLater(new Runnable() {
|
||||
public void run() {
|
||||
Node node = findNode(testFailedEvent);
|
||||
if (node == null) {
|
||||
logProblem("Test wasn't started! " + testFailedEvent + ".");
|
||||
return;
|
||||
}
|
||||
stopRunningNode(node, State.FAILED, testFailedEvent);
|
||||
|
||||
SMTestProxy testProxy = node.getProxy();
|
||||
|
||||
String comparisonFailureActualText = testFailedEvent.getComparisonFailureActualText();
|
||||
String comparisonFailureExpectedText = testFailedEvent.getComparisonFailureExpectedText();
|
||||
String failureMessage = testFailedEvent.getLocalizedFailureMessage();
|
||||
String stackTrace = testFailedEvent.getStacktrace();
|
||||
if (comparisonFailureActualText != null && comparisonFailureExpectedText != null) {
|
||||
testProxy.setTestComparisonFailed(failureMessage, stackTrace,
|
||||
comparisonFailureActualText, comparisonFailureExpectedText);
|
||||
} else if (comparisonFailureActualText == null && comparisonFailureExpectedText == null) {
|
||||
testProxy.setTestFailed(failureMessage, stackTrace, testFailedEvent.isTestError());
|
||||
} else {
|
||||
logProblem("Comparison failure actual and expected texts should be both null or not null.\n"
|
||||
+ "Expected:\n"
|
||||
+ comparisonFailureExpectedText + "\n"
|
||||
+ "Actual:\n"
|
||||
+ comparisonFailureActualText);
|
||||
}
|
||||
|
||||
// fire event
|
||||
fireOnTestFailed(testProxy);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void onTestIgnored(@NotNull final TestIgnoredEvent testIgnoredEvent) {
|
||||
SMRunnerUtil.addToInvokeLater(new Runnable() {
|
||||
public void run() {
|
||||
Node node = findNode(testIgnoredEvent);
|
||||
if (node == null) {
|
||||
logProblem("Test wasn't started! " + testIgnoredEvent + ".");
|
||||
return;
|
||||
}
|
||||
stopRunningNode(node, State.IGNORED, testIgnoredEvent);
|
||||
|
||||
SMTestProxy testProxy = node.getProxy();
|
||||
testProxy.setTestIgnored(testIgnoredEvent.getIgnoreComment(), testIgnoredEvent.getStacktrace());
|
||||
|
||||
// fire event
|
||||
fireOnTestIgnored(testProxy);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void onTestOutput(@NotNull final TestOutputEvent testOutputEvent) {
|
||||
SMRunnerUtil.addToInvokeLater(new Runnable() {
|
||||
public void run() {
|
||||
Node node = findNode(testOutputEvent);
|
||||
if (node == null) {
|
||||
logProblem("Test wasn't started! But " + testOutputEvent + "!");
|
||||
return;
|
||||
}
|
||||
SMTestProxy testProxy = node.getProxy();
|
||||
|
||||
if (testOutputEvent.isStdOut()) {
|
||||
testProxy.addStdOutput(testOutputEvent.getText(), ProcessOutputTypes.STDOUT);
|
||||
} else {
|
||||
testProxy.addStdErr(testOutputEvent.getText());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void onTestsCountInSuite(final int count) {
|
||||
SMRunnerUtil.addToInvokeLater(new Runnable() {
|
||||
public void run() {
|
||||
fireOnTestsCountInSuite(count);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean validateNodeId(@NotNull TreeNodeEvent treeNodeEvent) {
|
||||
int nodeId = treeNodeEvent.getId();
|
||||
if (nodeId <= 0) {
|
||||
logProblem("Node id should be positive: " + treeNodeEvent + ".");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Node findNode(@NotNull TreeNodeEvent treeNodeEvent) {
|
||||
if (!validateNodeId(treeNodeEvent)) {
|
||||
return null;
|
||||
}
|
||||
return myNodeByIdMap.get(treeNodeEvent.getId());
|
||||
}
|
||||
|
||||
private void fireOnTestingStarted() {
|
||||
for (SMTRunnerEventsListener listener : myEventsListeners) {
|
||||
listener.onTestingStarted(myTestsRootProxy);
|
||||
}
|
||||
}
|
||||
|
||||
private void fireOnTestingFinished() {
|
||||
for (SMTRunnerEventsListener listener : myEventsListeners) {
|
||||
listener.onTestingFinished(myTestsRootProxy);
|
||||
}
|
||||
}
|
||||
|
||||
private void fireOnTestsCountInSuite(final int count) {
|
||||
for (SMTRunnerEventsListener listener : myEventsListeners) {
|
||||
listener.onTestsCountInSuite(count);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void fireOnTestStarted(final SMTestProxy test) {
|
||||
for (SMTRunnerEventsListener listener : myEventsListeners) {
|
||||
listener.onTestStarted(test);
|
||||
}
|
||||
}
|
||||
|
||||
private void fireOnTestFinished(final SMTestProxy test) {
|
||||
for (SMTRunnerEventsListener listener : myEventsListeners) {
|
||||
listener.onTestFinished(test);
|
||||
}
|
||||
}
|
||||
|
||||
private void fireOnTestFailed(final SMTestProxy test) {
|
||||
for (SMTRunnerEventsListener listener : myEventsListeners) {
|
||||
listener.onTestFailed(test);
|
||||
}
|
||||
}
|
||||
|
||||
private void fireOnTestIgnored(final SMTestProxy test) {
|
||||
for (SMTRunnerEventsListener listener : myEventsListeners) {
|
||||
listener.onTestIgnored(test);
|
||||
}
|
||||
}
|
||||
|
||||
private void fireOnSuiteStarted(final SMTestProxy suite) {
|
||||
for (SMTRunnerEventsListener listener : myEventsListeners) {
|
||||
listener.onSuiteStarted(suite);
|
||||
}
|
||||
}
|
||||
|
||||
private void fireOnSuiteFinished(final SMTestProxy suite) {
|
||||
for (SMTRunnerEventsListener listener : myEventsListeners) {
|
||||
listener.onSuiteFinished(suite);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void fireOnCustomProgressTestsCategory(@Nullable final String categoryName, int testCount) {
|
||||
for (SMTRunnerEventsListener listener : myEventsListeners) {
|
||||
listener.onCustomProgressTestsCategory(categoryName, testCount);
|
||||
}
|
||||
}
|
||||
|
||||
private void fireOnCustomProgressTestStarted() {
|
||||
for (SMTRunnerEventsListener listener : myEventsListeners) {
|
||||
listener.onCustomProgressTestStarted();
|
||||
}
|
||||
}
|
||||
|
||||
private void fireOnCustomProgressTestFailed() {
|
||||
for (SMTRunnerEventsListener listener : myEventsListeners) {
|
||||
listener.onCustomProgressTestFailed();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove listeners, etc
|
||||
*/
|
||||
public void dispose() {
|
||||
SMRunnerUtil.addToInvokeLater(new Runnable() {
|
||||
public void run() {
|
||||
myEventsListeners.clear();
|
||||
|
||||
if (!myRunningNodes.isEmpty()) {
|
||||
Application application = ApplicationManager.getApplication();
|
||||
if (!application.isHeadlessEnvironment() && !application.isUnitTestMode()) {
|
||||
logProblem("Not all events were processed!");
|
||||
}
|
||||
}
|
||||
myRunningNodes.clear();
|
||||
myNodeByIdMap.clear();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void stopRunningNode(@NotNull Node node, @NotNull State stoppedState, @NotNull TreeNodeEvent event) {
|
||||
if (stoppedState == State.RUNNING) {
|
||||
throw new RuntimeException("newState shouldn't be " + State.RUNNING);
|
||||
}
|
||||
// check if has been already processed
|
||||
if (node.getState() != State.RUNNING) {
|
||||
logProblem("Can't change state of already stopped node" + node + " to " + stoppedState + ", " + event + ".");
|
||||
return;
|
||||
}
|
||||
myRunningNodes.remove(node);
|
||||
node.setState(stoppedState);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Node findActiveNode() {
|
||||
List<Node> runningLeaves = Lists.newArrayListWithExpectedSize(1);
|
||||
for (Node node : myRunningNodes) {
|
||||
if (!node.hasRunningChildren()) {
|
||||
runningLeaves.add(node);
|
||||
}
|
||||
}
|
||||
if (runningLeaves.isEmpty()) {
|
||||
throw new RuntimeException("No running leaves found, running nodes: " + myRunningNodes);
|
||||
}
|
||||
if (runningLeaves.size() == 1) {
|
||||
return runningLeaves.iterator().next();
|
||||
}
|
||||
List<Node> commonPathToRoot = null;
|
||||
for (Node leaf : runningLeaves) {
|
||||
List<Node> pathToRoot = leaf.getAncestorsFromParentToRoot();
|
||||
if (commonPathToRoot == null) {
|
||||
commonPathToRoot = pathToRoot;
|
||||
} else {
|
||||
commonPathToRoot = intersectPathsToRoot(commonPathToRoot, pathToRoot);
|
||||
}
|
||||
}
|
||||
if (commonPathToRoot == null || commonPathToRoot.isEmpty()) {
|
||||
throw new RuntimeException("Unexpected common path to root: " + commonPathToRoot + ", running leaves: " + runningLeaves);
|
||||
}
|
||||
return commonPathToRoot.get(0);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<Node> intersectPathsToRoot(@NotNull List<Node> pathToRoot1, @NotNull List<Node> pathToRoot2) {
|
||||
final int minSize = Math.min(pathToRoot1.size(), pathToRoot2.size());
|
||||
final int shift1 = pathToRoot1.size() - minSize;
|
||||
final int shift2 = pathToRoot2.size() - minSize;
|
||||
int commonSize = 0;
|
||||
for (int i = 0; i < minSize; i++) {
|
||||
Node node1 = pathToRoot1.get(i + shift1);
|
||||
Node node2 = pathToRoot2.get(i + shift2);
|
||||
if (node1 == node2) {
|
||||
commonSize = minSize - i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return pathToRoot1.subList(pathToRoot1.size() - commonSize, pathToRoot1.size());
|
||||
}
|
||||
|
||||
private static String getTestFrameworkPrefix(@NotNull String testFrameworkName) {
|
||||
return "[" + testFrameworkName + "] ";
|
||||
}
|
||||
|
||||
private void logProblem(@NotNull String msg) {
|
||||
logProblem(LOG, msg, myTestFrameworkName);
|
||||
}
|
||||
|
||||
private static void logProblem(@NotNull Logger log, @NotNull String msg, @NotNull String testFrameworkName) {
|
||||
logProblem(log, msg, SMTestRunnerConnectionUtil.isInDebugMode(), testFrameworkName);
|
||||
}
|
||||
|
||||
private static void logProblem(@NotNull Logger log, @NotNull String msg, boolean throwError, @NotNull String testFrameworkName) {
|
||||
final String text = getTestFrameworkPrefix(testFrameworkName) + msg;
|
||||
if (throwError) {
|
||||
log.error(text);
|
||||
}
|
||||
else {
|
||||
log.warn(text);
|
||||
}
|
||||
}
|
||||
|
||||
private enum State {
|
||||
RUNNING, FINISHED, FAILED, IGNORED
|
||||
}
|
||||
|
||||
private static class Node {
|
||||
private final int myId;
|
||||
private final Node myParentNode;
|
||||
private final SMTestProxy myProxy;
|
||||
private State myState = State.RUNNING;
|
||||
private int myRunningChildCount = 0;
|
||||
|
||||
Node(int id, @Nullable Node parentNode, @NotNull SMTestProxy proxy) {
|
||||
myId = id;
|
||||
myParentNode = parentNode;
|
||||
myProxy = proxy;
|
||||
if (myParentNode != null) {
|
||||
myParentNode.myRunningChildCount++;
|
||||
}
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return myId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Node getParentNode() {
|
||||
return myParentNode;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public SMTestProxy getProxy() {
|
||||
return myProxy;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public State getState() {
|
||||
return myState;
|
||||
}
|
||||
|
||||
public void setState(@NotNull State state) {
|
||||
if (myState == State.RUNNING && state != State.RUNNING) {
|
||||
if (myParentNode != null) {
|
||||
myParentNode.myRunningChildCount--;
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException("Attempt to change state from " + myState + " to " + state + ":" + toString());
|
||||
}
|
||||
myState = state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
Node node = (Node)o;
|
||||
|
||||
return myId == node.myId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return myId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "{" +
|
||||
"id=" + myId +
|
||||
", parentId=" + (myParentNode != null ? String.valueOf(myParentNode.getId()) : "<undefined>") +
|
||||
", name='" + myProxy.getName() +
|
||||
"', isSuite=" + myProxy.isSuite() +
|
||||
", state=" + myState +
|
||||
'}';
|
||||
}
|
||||
|
||||
public boolean hasRunningChildren() {
|
||||
return myRunningChildCount > 0;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<Node> getAncestorsFromParentToRoot() {
|
||||
List<Node> ancestors = Lists.newArrayList();
|
||||
Node parent = getParentNode();
|
||||
while (parent != null) {
|
||||
ancestors.add(parent);
|
||||
parent = parent.getParentNode();
|
||||
}
|
||||
return ancestors;
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
-20
@@ -15,8 +15,10 @@
|
||||
*/
|
||||
package com.intellij.execution.testframework.sm.runner;
|
||||
|
||||
import com.intellij.execution.testframework.sm.runner.events.*;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.testIntegration.TestLocationProvider;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -29,33 +31,23 @@ import org.jetbrains.annotations.Nullable;
|
||||
* and name of test method
|
||||
*/
|
||||
public interface GeneralTestEventsProcessor extends Disposable {
|
||||
void onStartTesting();
|
||||
|
||||
void onTestsCountInSuite(final int count);
|
||||
|
||||
void onTestStarted(@NotNull final String testName,
|
||||
@Nullable final String locationUrl);
|
||||
void onTestStarted(@NotNull TestStartedEvent testStartedEvent);
|
||||
|
||||
void onTestFinished(@NotNull final String testName,
|
||||
final int duration);
|
||||
void onTestFinished(@NotNull TestFinishedEvent testFinishedEvent);
|
||||
|
||||
void onTestFailure(@NotNull final String testName,
|
||||
@NotNull final String localizedMessage,
|
||||
@Nullable final String stackTrace,
|
||||
final boolean testError,
|
||||
@Nullable final String comparisionFailureActualText,
|
||||
@Nullable final String comparisionFailureExpectedText);
|
||||
void onTestFailure(@NotNull TestFailedEvent testFailedEvent);
|
||||
|
||||
void onTestIgnored(@NotNull final String testName,
|
||||
@NotNull final String ignoreComment,
|
||||
@Nullable final String stackTrace);
|
||||
void onTestIgnored(@NotNull TestIgnoredEvent testIgnoredEvent);
|
||||
|
||||
void onTestOutput(@NotNull final String testName,
|
||||
@NotNull final String text,
|
||||
final boolean stdOut);
|
||||
void onTestOutput(@NotNull TestOutputEvent testOutputEvent);
|
||||
|
||||
void onSuiteStarted(@NotNull final String suiteName,
|
||||
@Nullable final String locationUrl);
|
||||
void onSuiteStarted(@NotNull TestSuiteStartedEvent suiteStartedEvent);
|
||||
|
||||
void onSuiteFinished(@NotNull final String suiteName);
|
||||
void onSuiteFinished(@NotNull TestSuiteFinishedEvent suiteFinishedEvent);
|
||||
|
||||
void onUncapturedOutput(@NotNull final String text,
|
||||
final Key outputType);
|
||||
@@ -76,4 +68,10 @@ public interface GeneralTestEventsProcessor extends Disposable {
|
||||
void onCustomProgressTestStarted();
|
||||
void onCustomProgressTestFailed();
|
||||
void onTestsReporterAttached();
|
||||
}
|
||||
|
||||
void setLocator(@NotNull TestLocationProvider locator);
|
||||
|
||||
void addEventsListener(@NotNull SMTRunnerEventsListener viewer);
|
||||
|
||||
void onFinishTesting();
|
||||
}
|
||||
|
||||
+32
-21
@@ -19,11 +19,13 @@ import com.intellij.execution.process.ProcessOutputTypes;
|
||||
import com.intellij.execution.testframework.AbstractTestProxy;
|
||||
import com.intellij.execution.testframework.sm.SMRunnerUtil;
|
||||
import com.intellij.execution.testframework.sm.SMTestRunnerConnectionUtil;
|
||||
import com.intellij.execution.testframework.sm.runner.events.*;
|
||||
import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.testIntegration.TestLocationProvider;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
@@ -55,11 +57,11 @@ public class GeneralToSMTRunnerEventsConvertor implements GeneralTestEventsProce
|
||||
myTestFrameworkName = testFrameworkName;
|
||||
}
|
||||
|
||||
public void setLocator(TestLocationProvider customLocator) {
|
||||
public void setLocator(@NotNull TestLocationProvider customLocator) {
|
||||
myLocator = customLocator;
|
||||
}
|
||||
|
||||
public void addEventsListener(final SMTRunnerEventsListener listener) {
|
||||
public void addEventsListener(@NotNull final SMTRunnerEventsListener listener) {
|
||||
myEventsListeners.add(listener);
|
||||
}
|
||||
|
||||
@@ -111,10 +113,11 @@ public class GeneralToSMTRunnerEventsConvertor implements GeneralTestEventsProce
|
||||
});
|
||||
}
|
||||
|
||||
public void onTestStarted(@NotNull final String testName,
|
||||
@Nullable final String locationUrl) {
|
||||
public void onTestStarted(@NotNull final TestStartedEvent testStartedEvent) {
|
||||
SMRunnerUtil.addToInvokeLater(new Runnable() {
|
||||
public void run() {
|
||||
final String testName = testStartedEvent.getName();
|
||||
final String locationUrl = testStartedEvent.getLocationUrl();
|
||||
final String fullName = getFullTestName(testName);
|
||||
|
||||
if (myRunningTestsFullNameToProxy.containsKey(fullName)) {
|
||||
@@ -146,9 +149,11 @@ public class GeneralToSMTRunnerEventsConvertor implements GeneralTestEventsProce
|
||||
});
|
||||
}
|
||||
|
||||
public void onSuiteStarted(@NotNull final String suiteName, @Nullable final String locationUrl) {
|
||||
public void onSuiteStarted(@NotNull final TestSuiteStartedEvent suiteStartedEvent) {
|
||||
SMRunnerUtil.addToInvokeLater(new Runnable() {
|
||||
public void run() {
|
||||
final String suiteName = suiteStartedEvent.getName();
|
||||
final String locationUrl = suiteStartedEvent.getLocationUrl();
|
||||
final SMTestProxy parentSuite = getCurrentSuite();
|
||||
//new suite
|
||||
final SMTestProxy newSuite = new SMTestProxy(suiteName, true, locationUrl);
|
||||
@@ -168,10 +173,11 @@ public class GeneralToSMTRunnerEventsConvertor implements GeneralTestEventsProce
|
||||
});
|
||||
}
|
||||
|
||||
public void onTestFinished(@NotNull final String testName,
|
||||
final int duration) {
|
||||
public void onTestFinished(@NotNull final TestFinishedEvent testFinishedEvent) {
|
||||
SMRunnerUtil.addToInvokeLater(new Runnable() {
|
||||
public void run() {
|
||||
final String testName = testFinishedEvent.getName();
|
||||
final int duration = testFinishedEvent.getDuration();
|
||||
final String fullTestName = getFullTestName(testName);
|
||||
final SMTestProxy testProxy = getProxyByFullTestName(fullTestName);
|
||||
|
||||
@@ -191,9 +197,10 @@ public class GeneralToSMTRunnerEventsConvertor implements GeneralTestEventsProce
|
||||
});
|
||||
}
|
||||
|
||||
public void onSuiteFinished(@NotNull final String suiteName) {
|
||||
public void onSuiteFinished(@NotNull final TestSuiteFinishedEvent suiteFinishedEvent) {
|
||||
SMRunnerUtil.addToInvokeLater(new Runnable() {
|
||||
public void run() {
|
||||
final String suiteName = suiteFinishedEvent.getName();
|
||||
final SMTestProxy mySuite = mySuitesStack.popSuite(suiteName);
|
||||
if (mySuite != null) {
|
||||
mySuite.setFinished();
|
||||
@@ -257,14 +264,15 @@ public class GeneralToSMTRunnerEventsConvertor implements GeneralTestEventsProce
|
||||
});
|
||||
}
|
||||
|
||||
public void onTestFailure(@NotNull final String testName,
|
||||
@NotNull final String localizedMessage,
|
||||
@Nullable final String stackTrace,
|
||||
final boolean isTestError,
|
||||
@Nullable final String comparisionFailureActualText,
|
||||
@Nullable final String comparisionFailureExpectedText) {
|
||||
public void onTestFailure(@NotNull final TestFailedEvent testFailedEvent) {
|
||||
SMRunnerUtil.addToInvokeLater(new Runnable() {
|
||||
public void run() {
|
||||
final String testName = ObjectUtils.assertNotNull(testFailedEvent.getName());
|
||||
final String localizedMessage = testFailedEvent.getLocalizedFailureMessage();
|
||||
final String stackTrace = testFailedEvent.getStacktrace();
|
||||
final boolean isTestError = testFailedEvent.isTestError();
|
||||
final String comparisionFailureActualText = testFailedEvent.getComparisonFailureActualText();
|
||||
final String comparisionFailureExpectedText = testFailedEvent.getComparisonFailureExpectedText();
|
||||
final boolean inDebugMode = SMTestRunnerConnectionUtil.isInDebugMode();
|
||||
|
||||
final String fullTestName = getFullTestName(testName);
|
||||
@@ -281,7 +289,7 @@ public class GeneralToSMTRunnerEventsConvertor implements GeneralTestEventsProce
|
||||
if (!myFailedTestsSet.contains(testProxy)) {
|
||||
// if hasn't been already reported
|
||||
// 1. report
|
||||
onTestStarted(testName, null);
|
||||
onTestStarted(new TestStartedEvent(testName, null));
|
||||
// 2. add failure
|
||||
testProxy = getProxyByFullTestName(fullTestName);
|
||||
}
|
||||
@@ -323,11 +331,12 @@ public class GeneralToSMTRunnerEventsConvertor implements GeneralTestEventsProce
|
||||
});
|
||||
}
|
||||
|
||||
public void onTestIgnored(@NotNull final String testName,
|
||||
@NotNull final String ignoreComment,
|
||||
@Nullable final String stackTrace) {
|
||||
public void onTestIgnored(@NotNull final TestIgnoredEvent testIgnoredEvent) {
|
||||
SMRunnerUtil.addToInvokeLater(new Runnable() {
|
||||
public void run() {
|
||||
final String testName = ObjectUtils.assertNotNull(testIgnoredEvent.getName());
|
||||
final String ignoreComment = testIgnoredEvent.getIgnoreComment();
|
||||
final String stackTrace = testIgnoredEvent.getStacktrace();
|
||||
final String fullTestName = getFullTestName(testName);
|
||||
SMTestProxy testProxy = getProxyByFullTestName(fullTestName);
|
||||
if (testProxy == null) {
|
||||
@@ -341,7 +350,7 @@ public class GeneralToSMTRunnerEventsConvertor implements GeneralTestEventsProce
|
||||
} else {
|
||||
// try to fix
|
||||
// 1. report test opened
|
||||
onTestStarted(testName, null);
|
||||
onTestStarted(new TestStartedEvent(testName, null));
|
||||
|
||||
// 2. report failure
|
||||
testProxy = getProxyByFullTestName(fullTestName);
|
||||
@@ -359,10 +368,12 @@ public class GeneralToSMTRunnerEventsConvertor implements GeneralTestEventsProce
|
||||
});
|
||||
}
|
||||
|
||||
public void onTestOutput(@NotNull final String testName,
|
||||
@NotNull final String text, final boolean stdOut) {
|
||||
public void onTestOutput(@NotNull final TestOutputEvent testOutputEvent) {
|
||||
SMRunnerUtil.addToInvokeLater(new Runnable() {
|
||||
public void run() {
|
||||
final String testName = testOutputEvent.getName();
|
||||
final String text = testOutputEvent.getText();
|
||||
final boolean stdOut = testOutputEvent.isStdOut();
|
||||
final String fullTestName = getFullTestName(testName);
|
||||
final SMTestProxy testProxy = getProxyByFullTestName(fullTestName);
|
||||
if (testProxy == null) {
|
||||
|
||||
+33
-57
@@ -17,6 +17,7 @@ package com.intellij.execution.testframework.sm.runner;
|
||||
|
||||
import com.intellij.execution.process.ProcessOutputTypes;
|
||||
import com.intellij.execution.testframework.TestConsoleProperties;
|
||||
import com.intellij.execution.testframework.sm.runner.events.*;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
@@ -41,13 +42,11 @@ import static com.intellij.execution.testframework.sm.runner.GeneralToSMTRunnerE
|
||||
public class OutputToGeneralTestEventsConverter implements ProcessOutputConsumer {
|
||||
private static final Logger LOG = Logger.getInstance(OutputToGeneralTestEventsConverter.class.getName());
|
||||
|
||||
private static final String TEAMCITY_SERVICE_MESSAGE_PREFIX = "##teamcity[";
|
||||
|
||||
private GeneralTestEventsProcessor myProcessor;
|
||||
private final MyServiceMessageVisitor myServiceMessageVisitor;
|
||||
private final String myTestFrameworkName;
|
||||
|
||||
private OutputLineSplitter mySplitter;
|
||||
private final OutputLineSplitter mySplitter;
|
||||
private boolean myPendingLineBreakFlag;
|
||||
|
||||
public OutputToGeneralTestEventsConverter(@NotNull final String testFrameworkName,
|
||||
@@ -63,7 +62,7 @@ public class OutputToGeneralTestEventsConverter implements ProcessOutputConsumer
|
||||
};
|
||||
}
|
||||
|
||||
public void setProcessor(final GeneralTestEventsProcessor processor) {
|
||||
public void setProcessor(@Nullable final GeneralTestEventsProcessor processor) {
|
||||
myProcessor = processor;
|
||||
}
|
||||
|
||||
@@ -142,52 +141,38 @@ public class OutputToGeneralTestEventsConverter implements ProcessOutputConsumer
|
||||
}
|
||||
|
||||
|
||||
private void fireOnTestStarted(final String testName, @Nullable final String locationUrl) {
|
||||
assertNotNull(testName);
|
||||
|
||||
private void fireOnTestStarted(@NotNull TestStartedEvent testStartedEvent) {
|
||||
// local variable is used to prevent concurrent modification
|
||||
final GeneralTestEventsProcessor processor = myProcessor;
|
||||
if (processor != null) {
|
||||
processor.onTestStarted(testName, locationUrl);
|
||||
processor.onTestStarted(testStartedEvent);
|
||||
}
|
||||
}
|
||||
|
||||
private void fireOnTestFailure(final String testName,
|
||||
final String localizedMessage, final String stackTrace,
|
||||
final boolean isTestError,
|
||||
@Nullable final String comparisionFailureActualText,
|
||||
@Nullable final String comparisionFailureExpectedText) {
|
||||
assertNotNull(testName);
|
||||
assertNotNull(localizedMessage);
|
||||
private void fireOnTestFailure(@NotNull TestFailedEvent testFailedEvent) {
|
||||
assertNotNull(testFailedEvent.getLocalizedFailureMessage());
|
||||
|
||||
// local variable is used to prevent concurrent modification
|
||||
final GeneralTestEventsProcessor processor = myProcessor;
|
||||
if (processor != null) {
|
||||
processor.onTestFailure(testName, localizedMessage, stackTrace, isTestError,
|
||||
comparisionFailureActualText,
|
||||
comparisionFailureExpectedText);
|
||||
processor.onTestFailure(testFailedEvent);
|
||||
}
|
||||
}
|
||||
|
||||
private void fireOnTestIgnored(final String testName, final String ignoreComment,
|
||||
@Nullable final String details) {
|
||||
assertNotNull(testName);
|
||||
assertNotNull(ignoreComment);
|
||||
private void fireOnTestIgnored(@NotNull TestIgnoredEvent testIgnoredEvent) {
|
||||
|
||||
// local variable is used to prevent concurrent modification
|
||||
final GeneralTestEventsProcessor processor = myProcessor;
|
||||
if (processor != null) {
|
||||
processor.onTestIgnored(testName, ignoreComment, details);
|
||||
processor.onTestIgnored(testIgnoredEvent);
|
||||
}
|
||||
}
|
||||
|
||||
private void fireOnTestFinished(final String testName, final int duration) {
|
||||
assertNotNull(testName);
|
||||
|
||||
private void fireOnTestFinished(@NotNull TestFinishedEvent testFinishedEvent) {
|
||||
// local variable is used to prevent concurrent modification
|
||||
final GeneralTestEventsProcessor processor = myProcessor;
|
||||
if (processor != null) {
|
||||
processor.onTestFinished(testName, duration);
|
||||
processor.onTestFinished(testFinishedEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,14 +209,11 @@ public class OutputToGeneralTestEventsConverter implements ProcessOutputConsumer
|
||||
}
|
||||
}
|
||||
|
||||
private void fireOnTestOutput(final String testName, final String text, final boolean stdOut) {
|
||||
assertNotNull(testName);
|
||||
assertNotNull(text);
|
||||
|
||||
private void fireOnTestOutput(@NotNull TestOutputEvent testOutputEvent) {
|
||||
// local variable is used to prevent concurrent modification
|
||||
final GeneralTestEventsProcessor processor = myProcessor;
|
||||
if (processor != null) {
|
||||
processor.onTestOutput(testName, text, stdOut);
|
||||
processor.onTestOutput(testOutputEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,23 +239,19 @@ public class OutputToGeneralTestEventsConverter implements ProcessOutputConsumer
|
||||
}
|
||||
}
|
||||
|
||||
private void fireOnSuiteStarted(final String suiteName, @Nullable final String locationUrl) {
|
||||
assertNotNull(suiteName);
|
||||
|
||||
private void fireOnSuiteStarted(@NotNull TestSuiteStartedEvent suiteStartedEvent) {
|
||||
// local variable is used to prevent concurrent modification
|
||||
final GeneralTestEventsProcessor processor = myProcessor;
|
||||
if (processor != null) {
|
||||
processor.onSuiteStarted(suiteName, locationUrl);
|
||||
processor.onSuiteStarted(suiteStartedEvent);
|
||||
}
|
||||
}
|
||||
|
||||
private void fireOnSuiteFinished(final String suiteName) {
|
||||
assertNotNull(suiteName);
|
||||
|
||||
private void fireOnSuiteFinished(@NotNull TestSuiteFinishedEvent nodeFinishedEvent) {
|
||||
// local variable is used to prevent concurrent modification
|
||||
final GeneralTestEventsProcessor processor = myProcessor;
|
||||
if (processor != null) {
|
||||
processor.onSuiteFinished(suiteName);
|
||||
processor.onSuiteFinished(nodeFinishedEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -321,7 +299,8 @@ public class OutputToGeneralTestEventsConverter implements ProcessOutputConsumer
|
||||
|
||||
public void visitTestSuiteStarted(@NotNull final TestSuiteStarted suiteStarted) {
|
||||
final String locationUrl = fetchTestLocation(suiteStarted);
|
||||
fireOnSuiteStarted(suiteStarted.getSuiteName(), locationUrl);
|
||||
TestSuiteStartedEvent suiteStartedEvent = new TestSuiteStartedEvent(suiteStarted, locationUrl);
|
||||
fireOnSuiteStarted(suiteStartedEvent);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -343,7 +322,8 @@ public class OutputToGeneralTestEventsConverter implements ProcessOutputConsumer
|
||||
}
|
||||
|
||||
public void visitTestSuiteFinished(@NotNull final TestSuiteFinished suiteFinished) {
|
||||
fireOnSuiteFinished(suiteFinished.getSuiteName());
|
||||
TestSuiteFinishedEvent finishedEvent = new TestSuiteFinishedEvent(suiteFinished);
|
||||
fireOnSuiteFinished(finishedEvent);
|
||||
}
|
||||
|
||||
public void visitTestStarted(@NotNull final TestStarted testStarted) {
|
||||
@@ -351,7 +331,8 @@ public class OutputToGeneralTestEventsConverter implements ProcessOutputConsumer
|
||||
// final String locationUrl = testStarted.getLocationHint();
|
||||
|
||||
final String locationUrl = testStarted.getAttributes().get(ATTR_KEY_LOCATION_URL);
|
||||
fireOnTestStarted(testStarted.getTestName(), locationUrl);
|
||||
TestStartedEvent testStartedEvent = new TestStartedEvent(testStarted, locationUrl);
|
||||
fireOnTestStarted(testStartedEvent);
|
||||
}
|
||||
|
||||
public void visitTestFinished(@NotNull final TestFinished testFinished) {
|
||||
@@ -368,31 +349,27 @@ public class OutputToGeneralTestEventsConverter implements ProcessOutputConsumer
|
||||
duration = convertToInt(durationStr, testFinished);
|
||||
}
|
||||
|
||||
fireOnTestFinished(testFinished.getTestName(), duration);
|
||||
TestFinishedEvent testFinishedEvent = new TestFinishedEvent(testFinished, duration);
|
||||
fireOnTestFinished(testFinishedEvent);
|
||||
}
|
||||
|
||||
public void visitTestIgnored(@NotNull final TestIgnored testIgnored) {
|
||||
final String details = testIgnored.getAttributes().get(ATTR_KEY_STACKTRACE_DETAILS);
|
||||
fireOnTestIgnored(testIgnored.getTestName(), testIgnored.getIgnoreComment(), details);
|
||||
final String stacktrace = testIgnored.getAttributes().get(ATTR_KEY_STACKTRACE_DETAILS);
|
||||
fireOnTestIgnored(new TestIgnoredEvent(testIgnored, stacktrace));
|
||||
}
|
||||
|
||||
public void visitTestStdOut(@NotNull final TestStdOut testStdOut) {
|
||||
fireOnTestOutput(testStdOut.getTestName(), testStdOut.getStdOut(), true);
|
||||
fireOnTestOutput(new TestOutputEvent(testStdOut, testStdOut.getStdOut(), true));
|
||||
}
|
||||
|
||||
public void visitTestStdErr(@NotNull final TestStdErr testStdErr) {
|
||||
fireOnTestOutput(testStdErr.getTestName(), testStdErr.getStdErr(), false);
|
||||
fireOnTestOutput(new TestOutputEvent(testStdErr.getTestName(), testStdErr.getStdErr(), false));
|
||||
}
|
||||
|
||||
public void visitTestFailed(@NotNull final TestFailed testFailed) {
|
||||
final boolean isTestError = testFailed.getAttributes().get(ATTR_KEY_TEST_ERROR) != null;
|
||||
|
||||
fireOnTestFailure(testFailed.getTestName(),
|
||||
testFailed.getFailureMessage(),
|
||||
testFailed.getStacktrace(),
|
||||
isTestError,
|
||||
testFailed.getActual(),
|
||||
testFailed.getExpected());
|
||||
final boolean testError = testFailed.getAttributes().get(ATTR_KEY_TEST_ERROR) != null;
|
||||
TestFailedEvent testFailedEvent = new TestFailedEvent(testFailed, testError);
|
||||
fireOnTestFailure(testFailedEvent);
|
||||
}
|
||||
|
||||
public void visitPublishArtifacts(@NotNull final PublishArtifacts publishArtifacts) {
|
||||
@@ -425,7 +402,6 @@ public class OutputToGeneralTestEventsConverter implements ProcessOutputConsumer
|
||||
|
||||
@Override
|
||||
public void visitMessageWithStatus(@NotNull Message msg) {
|
||||
final String name = msg.getMessageName();
|
||||
final Map<String, String> msgAttrs = msg.getAttributes();
|
||||
|
||||
final String text = msgAttrs.get(ATTR_KEY_TEXT);
|
||||
|
||||
@@ -61,12 +61,20 @@ public class SMTestProxy extends AbstractTestProxy {
|
||||
private boolean myIsEmptyIsCached = false; // is used for separating unknown and unset values
|
||||
private boolean myIsEmpty = true;
|
||||
TestLocationProvider myCustomLocator = null;
|
||||
private final boolean myPreservePresentableName;
|
||||
|
||||
public SMTestProxy(final String testName, final boolean isSuite,
|
||||
@Nullable final String locationUrl) {
|
||||
this(testName, isSuite, locationUrl, false);
|
||||
}
|
||||
|
||||
public SMTestProxy(final String testName, final boolean isSuite,
|
||||
@Nullable final String locationUrl,
|
||||
boolean preservePresentableName) {
|
||||
myName = testName;
|
||||
myIsSuite = isSuite;
|
||||
myLocationUrl = locationUrl;
|
||||
myPreservePresentableName = preservePresentableName;
|
||||
}
|
||||
|
||||
public void setLocator(@NotNull TestLocationProvider locator) {
|
||||
@@ -470,6 +478,9 @@ public class SMTestProxy extends AbstractTestProxy {
|
||||
|
||||
@NotNull
|
||||
public String getPresentableName() {
|
||||
if (myPreservePresentableName) {
|
||||
return TestsPresentationUtil.getPresentableNameTrimmedOnly(this);
|
||||
}
|
||||
return TestsPresentationUtil.getPresentableName(this);
|
||||
}
|
||||
|
||||
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.sm.runner.events;
|
||||
|
||||
import jetbrains.buildServer.messages.serviceMessages.MessageWithAttributes;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author Sergey Simonchik
|
||||
*/
|
||||
public abstract class BaseStartedNodeEvent extends TreeNodeEvent {
|
||||
|
||||
private final int myParentId;
|
||||
private final String myLocationUrl;
|
||||
|
||||
protected BaseStartedNodeEvent(@NotNull String name,
|
||||
int id,
|
||||
int parentId,
|
||||
@Nullable final String locationUrl) {
|
||||
super(name, id);
|
||||
myParentId = parentId;
|
||||
myLocationUrl = locationUrl;
|
||||
validate();
|
||||
}
|
||||
|
||||
private void validate() {
|
||||
if (myParentId < -1) {
|
||||
fail("parentId should be greater than -2");
|
||||
}
|
||||
if (getId() == -1 ^ myParentId == -1) {
|
||||
fail("id and parentId should be -1 or non-negative");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return parent node id (non-negative integer), or -1 if undefined
|
||||
*/
|
||||
public int getParentId() {
|
||||
return myParentId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getLocationUrl() {
|
||||
return myLocationUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendToStringInfo(@NotNull StringBuilder buf) {
|
||||
append(buf, "parentId", myParentId);
|
||||
append(buf, "locationUrl", myLocationUrl);
|
||||
}
|
||||
|
||||
public static int getParentNodeId(@NotNull MessageWithAttributes message) {
|
||||
return TreeNodeEvent.getIntAttribute(message, "parentNodeId");
|
||||
}
|
||||
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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.sm.runner.events;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import jetbrains.buildServer.messages.serviceMessages.TestFailed;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author Sergey Simonchik
|
||||
*/
|
||||
public class TestFailedEvent extends TreeNodeEvent {
|
||||
|
||||
private final String myLocalizedFailureMessage;
|
||||
private final String myStacktrace;
|
||||
private final boolean myTestError;
|
||||
private final String myComparisonFailureActualText;
|
||||
private final String myComparisonFailureExpectedText;
|
||||
|
||||
public TestFailedEvent(@NotNull TestFailed testFailed, boolean testError) {
|
||||
super(testFailed.getTestName(), TreeNodeEvent.getNodeId(testFailed));
|
||||
myLocalizedFailureMessage = Preconditions.checkNotNull(testFailed.getFailureMessage());
|
||||
myStacktrace = testFailed.getStacktrace();
|
||||
myTestError = testError;
|
||||
myComparisonFailureActualText = testFailed.getActual();
|
||||
myComparisonFailureExpectedText = testFailed.getExpected();
|
||||
}
|
||||
|
||||
public TestFailedEvent(@NotNull String testName,
|
||||
@NotNull String localizedFailureMessage,
|
||||
@Nullable String stackTrace,
|
||||
boolean testError,
|
||||
@Nullable String comparisonFailureActualText,
|
||||
@Nullable String comparisonFailureExpectedText) {
|
||||
super(testName, -1);
|
||||
myLocalizedFailureMessage = Preconditions.checkNotNull(localizedFailureMessage);
|
||||
myStacktrace = stackTrace;
|
||||
myTestError = testError;
|
||||
myComparisonFailureActualText = comparisonFailureActualText;
|
||||
myComparisonFailureExpectedText = comparisonFailureExpectedText;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getLocalizedFailureMessage() {
|
||||
return myLocalizedFailureMessage;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getStacktrace() {
|
||||
return myStacktrace;
|
||||
}
|
||||
|
||||
public boolean isTestError() {
|
||||
return myTestError;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getComparisonFailureActualText() {
|
||||
return myComparisonFailureActualText;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getComparisonFailureExpectedText() {
|
||||
return myComparisonFailureExpectedText;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendToStringInfo(@NotNull StringBuilder buf) {
|
||||
append(buf, "localizedFailureMessage", myLocalizedFailureMessage);
|
||||
append(buf, "stacktrace", myStacktrace);
|
||||
append(buf, "isTestError", myTestError);
|
||||
append(buf, "comparisonFailureActualText", myComparisonFailureActualText);
|
||||
append(buf, "comparisonFailureExpectedText", myComparisonFailureExpectedText);
|
||||
}
|
||||
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.sm.runner.events;
|
||||
|
||||
import jetbrains.buildServer.messages.serviceMessages.TestFinished;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author Sergey Simonchik
|
||||
*/
|
||||
public class TestFinishedEvent extends TreeNodeEvent {
|
||||
|
||||
private final int myDuration;
|
||||
|
||||
public TestFinishedEvent(@NotNull TestFinished testFinished, int duration) {
|
||||
this(testFinished.getTestName(), TreeNodeEvent.getNodeId(testFinished), duration);
|
||||
}
|
||||
|
||||
public TestFinishedEvent(@Nullable String name, int id, int duration) {
|
||||
super(name, id);
|
||||
myDuration = duration;
|
||||
}
|
||||
|
||||
public TestFinishedEvent(@NotNull String name, int duration) {
|
||||
this(name, -1, duration);
|
||||
}
|
||||
|
||||
public int getDuration() {
|
||||
return myDuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendToStringInfo(@NotNull StringBuilder buf) {
|
||||
append(buf, "duration", myDuration);
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.sm.runner.events;
|
||||
|
||||
import jetbrains.buildServer.messages.serviceMessages.TestIgnored;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author Sergey Simonchik
|
||||
*/
|
||||
public class TestIgnoredEvent extends TreeNodeEvent {
|
||||
private final String myIgnoreComment;
|
||||
private final String myStacktrace;
|
||||
|
||||
public TestIgnoredEvent(@NotNull String testName, @NotNull String ignoreComment, @Nullable String stacktrace) {
|
||||
super(testName, -1);
|
||||
myIgnoreComment = ignoreComment;
|
||||
myStacktrace = stacktrace;
|
||||
}
|
||||
|
||||
public TestIgnoredEvent(@NotNull TestIgnored testIgnored, @Nullable String stacktrace) {
|
||||
super(testIgnored.getTestName(), TreeNodeEvent.getNodeId(testIgnored));
|
||||
myIgnoreComment = testIgnored.getIgnoreComment();
|
||||
myStacktrace = stacktrace;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getIgnoreComment() {
|
||||
return myIgnoreComment;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getStacktrace() {
|
||||
return myStacktrace;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendToStringInfo(@NotNull StringBuilder buf) {
|
||||
append(buf, "ignoreComment", myIgnoreComment);
|
||||
append(buf, "stacktrace", myStacktrace);
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.sm.runner.events;
|
||||
|
||||
import jetbrains.buildServer.messages.serviceMessages.BaseTestMessage;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author Sergey Simonchik
|
||||
*/
|
||||
public class TestOutputEvent extends TreeNodeEvent {
|
||||
|
||||
private final String myText;
|
||||
private final boolean myStdOut;
|
||||
|
||||
public TestOutputEvent(@NotNull BaseTestMessage message, @NotNull String text, boolean stdOut) {
|
||||
super(message.getTestName(), TreeNodeEvent.getNodeId(message));
|
||||
myText = text;
|
||||
myStdOut = stdOut;
|
||||
}
|
||||
|
||||
public TestOutputEvent(@NotNull String testName, @NotNull String text, boolean stdOut) {
|
||||
super(testName, -1);
|
||||
myText = text;
|
||||
myStdOut = stdOut;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getText() {
|
||||
return myText;
|
||||
}
|
||||
|
||||
public boolean isStdOut() {
|
||||
return myStdOut;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendToStringInfo(@NotNull StringBuilder buf) {
|
||||
append(buf, "text", myText);
|
||||
append(buf, "stdOut", myStdOut);
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.sm.runner.events;
|
||||
|
||||
import jetbrains.buildServer.messages.serviceMessages.TestStarted;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author Sergey Simonchik
|
||||
*/
|
||||
public class TestStartedEvent extends BaseStartedNodeEvent {
|
||||
|
||||
public TestStartedEvent(@NotNull TestStarted testStarted,
|
||||
@Nullable String locationUrl) {
|
||||
super(testStarted.getTestName(), TreeNodeEvent.getNodeId(testStarted),
|
||||
getParentNodeId(testStarted), locationUrl);
|
||||
}
|
||||
|
||||
public TestStartedEvent(@NotNull String name, @Nullable String locationUrl) {
|
||||
super(name, -1, -1, locationUrl);
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.sm.runner.events;
|
||||
|
||||
import jetbrains.buildServer.messages.serviceMessages.TestSuiteFinished;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author Sergey Simonchik
|
||||
*/
|
||||
public class TestSuiteFinishedEvent extends TreeNodeEvent {
|
||||
|
||||
public TestSuiteFinishedEvent(@NotNull TestSuiteFinished suiteFinished) {
|
||||
super(suiteFinished.getSuiteName(), TreeNodeEvent.getNodeId(suiteFinished));
|
||||
}
|
||||
|
||||
public TestSuiteFinishedEvent(@NotNull String name) {
|
||||
super(name, -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendToStringInfo(@NotNull StringBuilder buf) {
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.sm.runner.events;
|
||||
|
||||
import jetbrains.buildServer.messages.serviceMessages.TestSuiteStarted;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author Sergey Simonchik
|
||||
*/
|
||||
public class TestSuiteStartedEvent extends BaseStartedNodeEvent {
|
||||
|
||||
public TestSuiteStartedEvent(@NotNull TestSuiteStarted suiteStarted,
|
||||
@Nullable String locationUrl) {
|
||||
super(suiteStarted.getSuiteName(), TreeNodeEvent.getNodeId(suiteStarted),
|
||||
getParentNodeId(suiteStarted), locationUrl);
|
||||
}
|
||||
|
||||
public TestSuiteStartedEvent(@NotNull String name, @Nullable String locationUrl) {
|
||||
super(name, -1, -1, locationUrl);
|
||||
}
|
||||
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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.sm.runner.events;
|
||||
|
||||
import jetbrains.buildServer.messages.serviceMessages.MessageWithAttributes;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author Sergey Simonchik
|
||||
*/
|
||||
public abstract class TreeNodeEvent {
|
||||
|
||||
private final String myName;
|
||||
private final int myId;
|
||||
|
||||
public TreeNodeEvent(@Nullable String name, int id) {
|
||||
myName = name;
|
||||
myId = id;
|
||||
validate();
|
||||
}
|
||||
|
||||
private void validate() {
|
||||
if (myId < -1) {
|
||||
fail("id should be greater than -2");
|
||||
}
|
||||
if (myName != null && myName.isEmpty()) {
|
||||
fail("Tree node name is empty");
|
||||
}
|
||||
}
|
||||
|
||||
protected void fail(@NotNull String message) {
|
||||
throw new IllegalStateException(message + ", " + toString());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getName() {
|
||||
return myName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return tree node id (non-negative integer), or -1 if undefined
|
||||
*/
|
||||
public int getId() {
|
||||
return myId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String toString() {
|
||||
StringBuilder buf = new StringBuilder(getClass().getSimpleName() + "{");
|
||||
append(buf, "name", myName);
|
||||
append(buf, "id", myId);
|
||||
appendToStringInfo(buf);
|
||||
// drop last 2 chars: ', '
|
||||
buf.setLength(buf.length() - 2);
|
||||
buf.append("}");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
protected abstract void appendToStringInfo(@NotNull StringBuilder buf);
|
||||
|
||||
protected static void append(@NotNull StringBuilder buffer,
|
||||
@NotNull String key, @Nullable Object value) {
|
||||
if (value != null) {
|
||||
buffer.append(key).append("=");
|
||||
if (value instanceof String) {
|
||||
buffer.append("'").append(value).append("'");
|
||||
}
|
||||
else {
|
||||
buffer.append(String.valueOf(value));
|
||||
}
|
||||
buffer.append(", ");
|
||||
}
|
||||
}
|
||||
|
||||
public static int getNodeId(@NotNull MessageWithAttributes message) {
|
||||
return getIntAttribute(message, "nodeId");
|
||||
}
|
||||
|
||||
public static int getIntAttribute(@NotNull MessageWithAttributes message, @NotNull String key) {
|
||||
String value = message.getAttributes().get(key);
|
||||
if (value == null) {
|
||||
return -1;
|
||||
}
|
||||
return Integer.parseInt(value);
|
||||
}
|
||||
|
||||
}
|
||||
+12
@@ -226,6 +226,18 @@ public class TestsPresentationUtil {
|
||||
return presentationCandidate;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String getPresentableNameTrimmedOnly(@NotNull SMTestProxy testProxy) {
|
||||
String name = testProxy.getName();
|
||||
if (name != null) {
|
||||
name = name.trim();
|
||||
}
|
||||
if (name == null || name.isEmpty()) {
|
||||
name = NO_NAME_TEST;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Icon getIcon(final SMTestProxy testProxy,
|
||||
final TestConsoleProperties consoleProperties) {
|
||||
|
||||
+26
-25
@@ -18,6 +18,7 @@ package com.intellij.execution.testframework.sm.runner;
|
||||
import com.intellij.execution.runners.ExecutionEnvironment;
|
||||
import com.intellij.execution.testframework.TestConsoleProperties;
|
||||
import com.intellij.execution.testframework.sm.Marker;
|
||||
import com.intellij.execution.testframework.sm.runner.events.*;
|
||||
import com.intellij.execution.testframework.sm.runner.ui.SMTRunnerConsoleView;
|
||||
import com.intellij.execution.testframework.sm.runner.ui.SMTRunnerTestTreeView;
|
||||
import com.intellij.execution.testframework.sm.runner.ui.SMTestRunnerResultsForm;
|
||||
@@ -125,7 +126,7 @@ public class GeneralToSMTRunnerEventsConvertorTest extends BaseSMTRunnerTestCase
|
||||
|
||||
public void testOnTestFailure() {
|
||||
onTestStarted("some_test");
|
||||
myEventsProcessor.onTestFailure("some_test", "", "", false, null, null);
|
||||
myEventsProcessor.onTestFailure(new TestFailedEvent("some_test", "", "", false, null, null));
|
||||
|
||||
final String fullName = myEventsProcessor.getFullTestName("some_test");
|
||||
final SMTestProxy proxy = myEventsProcessor.getProxyByFullTestName(fullName);
|
||||
@@ -136,7 +137,7 @@ public class GeneralToSMTRunnerEventsConvertorTest extends BaseSMTRunnerTestCase
|
||||
|
||||
public void testOnTestComparisionFailure() {
|
||||
onTestStarted("some_test");
|
||||
myEventsProcessor.onTestFailure("some_test", "", "", false, "actual", "expected");
|
||||
myEventsProcessor.onTestFailure(new TestFailedEvent("some_test", "", "", false, "actual", "expected"));
|
||||
|
||||
final String fullName = myEventsProcessor.getFullTestName("some_test");
|
||||
final SMTestProxy proxy = myEventsProcessor.getProxyByFullTestName(fullName);
|
||||
@@ -147,8 +148,8 @@ public class GeneralToSMTRunnerEventsConvertorTest extends BaseSMTRunnerTestCase
|
||||
|
||||
public void testOnTestFailure_Twice() {
|
||||
onTestStarted("some_test");
|
||||
myEventsProcessor.onTestFailure("some_test", "", "", false, null, null);
|
||||
myEventsProcessor.onTestFailure("some_test", "", "", false, null, null);
|
||||
myEventsProcessor.onTestFailure(new TestFailedEvent("some_test", "", "", false, null, null));
|
||||
myEventsProcessor.onTestFailure(new TestFailedEvent("some_test", "", "", false, null, null));
|
||||
|
||||
assertEquals(1, myEventsProcessor.getRunningTestsQuantity());
|
||||
assertEquals(1, myEventsProcessor.getFailedTestsSet().size());
|
||||
@@ -156,7 +157,7 @@ public class GeneralToSMTRunnerEventsConvertorTest extends BaseSMTRunnerTestCase
|
||||
|
||||
public void testOnTestError() {
|
||||
onTestStarted("some_test");
|
||||
myEventsProcessor.onTestFailure("some_test", "", "", true, null, null);
|
||||
myEventsProcessor.onTestFailure(new TestFailedEvent("some_test", "", "", true, null, null));
|
||||
|
||||
final String fullName = myEventsProcessor.getFullTestName("some_test");
|
||||
final SMTestProxy proxy = myEventsProcessor.getProxyByFullTestName(fullName);
|
||||
@@ -167,7 +168,7 @@ public class GeneralToSMTRunnerEventsConvertorTest extends BaseSMTRunnerTestCase
|
||||
|
||||
public void testOnTestIgnored() {
|
||||
onTestStarted("some_test");
|
||||
myEventsProcessor.onTestIgnored("some_test", "", null);
|
||||
myEventsProcessor.onTestIgnored(new TestIgnoredEvent("some_test", "", null));
|
||||
|
||||
final String fullName = myEventsProcessor.getFullTestName("some_test");
|
||||
final SMTestProxy proxy = myEventsProcessor.getProxyByFullTestName(fullName);
|
||||
@@ -180,7 +181,7 @@ public class GeneralToSMTRunnerEventsConvertorTest extends BaseSMTRunnerTestCase
|
||||
onTestStarted("some_test");
|
||||
final String fullName = myEventsProcessor.getFullTestName("some_test");
|
||||
final SMTestProxy proxy = myEventsProcessor.getProxyByFullTestName(fullName);
|
||||
myEventsProcessor.onTestFinished("some_test", 10);
|
||||
myEventsProcessor.onTestFinished(new TestFinishedEvent("some_test", 10));
|
||||
|
||||
assertEquals(0, myEventsProcessor.getRunningTestsQuantity());
|
||||
assertEquals(0, myEventsProcessor.getFailedTestsSet().size());
|
||||
@@ -224,8 +225,8 @@ public class GeneralToSMTRunnerEventsConvertorTest extends BaseSMTRunnerTestCase
|
||||
|
||||
public void testOnFinishedTesting_WithFailure() {
|
||||
onTestStarted("test");
|
||||
myEventsProcessor.onTestFailure("test", "", "", false, null, null);
|
||||
myEventsProcessor.onTestFinished("test", 10);
|
||||
myEventsProcessor.onTestFailure(new TestFailedEvent("test", "", "", false, null, null));
|
||||
myEventsProcessor.onTestFinished(new TestFinishedEvent("test", 10));
|
||||
myEventsProcessor.onFinishTesting();
|
||||
|
||||
//Tree
|
||||
@@ -240,8 +241,8 @@ public class GeneralToSMTRunnerEventsConvertorTest extends BaseSMTRunnerTestCase
|
||||
|
||||
public void testOnFinishedTesting_WithError() {
|
||||
onTestStarted("test");
|
||||
myEventsProcessor.onTestFailure("test", "", "", true, null, null);
|
||||
myEventsProcessor.onTestFinished("test", 10);
|
||||
myEventsProcessor.onTestFailure(new TestFailedEvent("test", "", "", true, null, null));
|
||||
myEventsProcessor.onTestFinished(new TestFinishedEvent("test", 10));
|
||||
myEventsProcessor.onFinishTesting();
|
||||
|
||||
//Tree
|
||||
@@ -256,8 +257,8 @@ public class GeneralToSMTRunnerEventsConvertorTest extends BaseSMTRunnerTestCase
|
||||
|
||||
public void testOnFinishedTesting_WithIgnored() {
|
||||
onTestStarted("test");
|
||||
myEventsProcessor.onTestIgnored("test", "", null);
|
||||
myEventsProcessor.onTestFinished("test", 10);
|
||||
myEventsProcessor.onTestIgnored(new TestIgnoredEvent("test", "", null));
|
||||
myEventsProcessor.onTestFinished(new TestFinishedEvent("test", 10));
|
||||
myEventsProcessor.onFinishTesting();
|
||||
|
||||
//Tree
|
||||
@@ -302,18 +303,18 @@ public class GeneralToSMTRunnerEventsConvertorTest extends BaseSMTRunnerTestCase
|
||||
assertEquals("suite3", test2.getParent().getName());
|
||||
assertEquals("suite2", test2.getParent().getParent().getName());
|
||||
|
||||
myEventsProcessor.onTestFinished("test2", 10);
|
||||
myEventsProcessor.onTestFinished(new TestFinishedEvent("test2", 10));
|
||||
|
||||
//check that after finishing suite (suite3), current will be parent of finished suite (i.e. suite2)
|
||||
myEventsProcessor.onSuiteFinished("suite3");
|
||||
myEventsProcessor.onSuiteFinished(new TestSuiteFinishedEvent("suite3"));
|
||||
onTestStarted("test3");
|
||||
final SMTestProxy test3 =
|
||||
myEventsProcessor.getProxyByFullTestName(myEventsProcessor.getFullTestName("test3"));
|
||||
assertEquals("suite2", test3.getParent().getName());
|
||||
|
||||
//clean up
|
||||
myEventsProcessor.onSuiteFinished("suite2");
|
||||
myEventsProcessor.onSuiteFinished("suite1");
|
||||
myEventsProcessor.onSuiteFinished(new TestSuiteFinishedEvent("suite2"));
|
||||
myEventsProcessor.onSuiteFinished(new TestSuiteFinishedEvent("suite1"));
|
||||
}
|
||||
|
||||
public void testOnSuiteStarted_WithLocation() {
|
||||
@@ -336,17 +337,17 @@ public class GeneralToSMTRunnerEventsConvertorTest extends BaseSMTRunnerTestCase
|
||||
}
|
||||
|
||||
public void testConcurrentSuite_intersected() {
|
||||
myEventsProcessor.onSuiteStarted("suite1", null);
|
||||
myEventsProcessor.onTestStarted("suite2.test1", null);
|
||||
myEventsProcessor.onSuiteStarted(new TestSuiteStartedEvent("suite1", null));
|
||||
myEventsProcessor.onTestStarted(new TestStartedEvent("suite2.test1", null));
|
||||
|
||||
final SMTestProxy test1 =
|
||||
myEventsProcessor.getProxyByFullTestName(myEventsProcessor.getFullTestName("suite2.test1"));
|
||||
|
||||
myEventsProcessor.onSuiteFinished("suite1");
|
||||
myEventsProcessor.onSuiteFinished(new TestSuiteFinishedEvent("suite1"));
|
||||
|
||||
myEventsProcessor.onSuiteStarted("suite2", null);
|
||||
myEventsProcessor.onTestFinished("suite2.test1", 10);
|
||||
myEventsProcessor.onSuiteFinished("suite2");
|
||||
myEventsProcessor.onSuiteStarted(new TestSuiteStartedEvent("suite2", null));
|
||||
myEventsProcessor.onTestFinished(new TestFinishedEvent("suite2.test1", 10));
|
||||
myEventsProcessor.onSuiteFinished(new TestSuiteFinishedEvent("suite2"));
|
||||
|
||||
assertEquals("suite1", test1.getParent().getName());
|
||||
|
||||
@@ -371,7 +372,7 @@ public class GeneralToSMTRunnerEventsConvertorTest extends BaseSMTRunnerTestCase
|
||||
}
|
||||
|
||||
private void onTestStarted(final String testName, @Nullable final String locationUrl) {
|
||||
myEventsProcessor.onTestStarted(testName, locationUrl);
|
||||
myEventsProcessor.onTestStarted(new TestStartedEvent(testName, locationUrl));
|
||||
myResultsViewer.performUpdate();
|
||||
}
|
||||
|
||||
@@ -380,7 +381,7 @@ public class GeneralToSMTRunnerEventsConvertorTest extends BaseSMTRunnerTestCase
|
||||
}
|
||||
|
||||
private void onTestSuiteStarted(final String suiteName, @Nullable final String locationUrl) {
|
||||
myEventsProcessor.onSuiteStarted(suiteName, locationUrl);
|
||||
myEventsProcessor.onSuiteStarted(new TestSuiteStartedEvent(suiteName, locationUrl));
|
||||
myResultsViewer.performUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
+27
-13
@@ -15,7 +15,9 @@
|
||||
*/
|
||||
package com.intellij.execution.testframework.sm.runner;
|
||||
|
||||
import com.intellij.execution.testframework.sm.runner.events.*;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.testIntegration.TestLocationProvider;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -24,46 +26,46 @@ import org.jetbrains.annotations.Nullable;
|
||||
*/
|
||||
public class MockGeneralTestEventsProcessorAdapter implements GeneralTestEventsProcessor {
|
||||
private final StringBuilder myOutputBuffer = new StringBuilder();
|
||||
|
||||
@Override
|
||||
public void onStartTesting() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTestsCountInSuite(int count) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTestStarted(@NotNull String testName, @Nullable String locationUrl) {
|
||||
public void onTestStarted(@NotNull TestStartedEvent testStartedEvent) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTestFinished(@NotNull String testName, int duration) {
|
||||
public void onTestFinished(@NotNull TestFinishedEvent testFinishedEvent) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTestFailure(@NotNull String testName,
|
||||
@NotNull String localizedMessage,
|
||||
@Nullable String stackTrace,
|
||||
boolean testError,
|
||||
@Nullable String comparisionFailureActualText,
|
||||
@Nullable String comparisionFailureExpectedText) {
|
||||
public void onTestFailure(@NotNull TestFailedEvent testFailedEvent) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTestIgnored(@NotNull String testName, @NotNull String ignoreComment, @Nullable String stackTrace) {
|
||||
public void onTestIgnored(@NotNull TestIgnoredEvent testIgnoredEvent) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTestOutput(@NotNull String testName, @NotNull String text, boolean stdOut) {
|
||||
public void onTestOutput(@NotNull TestOutputEvent testOutputEvent) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuiteStarted(@NotNull String suiteName, @Nullable String locationUrl) {
|
||||
public void onSuiteStarted(@NotNull TestSuiteStartedEvent suiteStartedEvent) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuiteFinished(@NotNull String suiteName) {
|
||||
public void onSuiteFinished(@NotNull TestSuiteFinishedEvent suiteFinishedEvent) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUncapturedOutput(@NotNull String text, Key outputType) {
|
||||
myOutputBuffer.append("[").append(outputType.toString()).append("]"+ text);
|
||||
myOutputBuffer.append("[").append(outputType.toString()).append("]").append(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -86,6 +88,18 @@ public class MockGeneralTestEventsProcessorAdapter implements GeneralTestEventsP
|
||||
public void onTestsReporterAttached() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLocator(@NotNull TestLocationProvider locator) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addEventsListener(@NotNull SMTRunnerEventsListener viewer) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinishTesting() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
myOutputBuffer.setLength(0);
|
||||
|
||||
+54
-53
@@ -22,6 +22,7 @@ import com.intellij.execution.runners.ExecutionEnvironment;
|
||||
import com.intellij.execution.testframework.Printable;
|
||||
import com.intellij.execution.testframework.Printer;
|
||||
import com.intellij.execution.testframework.TestConsoleProperties;
|
||||
import com.intellij.execution.testframework.sm.runner.events.*;
|
||||
import com.intellij.execution.testframework.sm.runner.ui.MockPrinter;
|
||||
import com.intellij.execution.testframework.sm.runner.ui.SMTRunnerConsoleView;
|
||||
import com.intellij.execution.testframework.sm.runner.ui.SMTestRunnerResultsForm;
|
||||
@@ -174,8 +175,8 @@ public class SMTRunnerConsoleTest extends BaseSMTRunnerTestCase {
|
||||
public void testProcessor_OnTestStdOutput() {
|
||||
startTestWithPrinter("my_test");
|
||||
|
||||
myEventsProcessor.onTestOutput("my_test", "stdout1 ", true);
|
||||
myEventsProcessor.onTestOutput("my_test", "stdout2", true);
|
||||
myEventsProcessor.onTestOutput(new TestOutputEvent("my_test", "stdout1 ", true));
|
||||
myEventsProcessor.onTestOutput(new TestOutputEvent("my_test", "stdout2", true));
|
||||
|
||||
assertStdOutput(myMockResetablePrinter, "stdout1 stdout2");
|
||||
}
|
||||
@@ -183,8 +184,8 @@ public class SMTRunnerConsoleTest extends BaseSMTRunnerTestCase {
|
||||
public void testProcessor_OnTestStdErr() {
|
||||
startTestWithPrinter("my_test");
|
||||
|
||||
myEventsProcessor.onTestOutput("my_test", "stderr1 ", false);
|
||||
myEventsProcessor.onTestOutput("my_test", "stderr2", false);
|
||||
myEventsProcessor.onTestOutput(new TestOutputEvent("my_test", "stderr1 ", false));
|
||||
myEventsProcessor.onTestOutput(new TestOutputEvent("my_test", "stderr2", false));
|
||||
|
||||
assertStdErr(myMockResetablePrinter, "stderr1 stderr2");
|
||||
}
|
||||
@@ -192,10 +193,10 @@ public class SMTRunnerConsoleTest extends BaseSMTRunnerTestCase {
|
||||
public void testProcessor_OnTestMixedStd() {
|
||||
startTestWithPrinter("my_test");
|
||||
|
||||
myEventsProcessor.onTestOutput("my_test", "stdout1 ", true);
|
||||
myEventsProcessor.onTestOutput("my_test", "stderr1 ", false);
|
||||
myEventsProcessor.onTestOutput("my_test", "stdout2", true);
|
||||
myEventsProcessor.onTestOutput("my_test", "stderr2", false);
|
||||
myEventsProcessor.onTestOutput(new TestOutputEvent("my_test", "stdout1 ", true));
|
||||
myEventsProcessor.onTestOutput(new TestOutputEvent("my_test", "stderr1 ", false));
|
||||
myEventsProcessor.onTestOutput(new TestOutputEvent("my_test", "stdout2", true));
|
||||
myEventsProcessor.onTestOutput(new TestOutputEvent("my_test", "stderr2", false));
|
||||
|
||||
assertAllOutputs(myMockResetablePrinter, "stdout1 stdout2", "stderr1 stderr2", "");
|
||||
}
|
||||
@@ -203,9 +204,9 @@ public class SMTRunnerConsoleTest extends BaseSMTRunnerTestCase {
|
||||
public void testProcessor_OnFailure() {
|
||||
final SMTestProxy myTest1 = startTestWithPrinter("my_test");
|
||||
|
||||
myEventsProcessor.onTestFailure("my_test", "error msg", "method1:1\nmethod2:2", false, null, null);
|
||||
myEventsProcessor.onTestOutput("my_test", "stdout1 ", true);
|
||||
myEventsProcessor.onTestOutput("my_test", "stderr1 ", false);
|
||||
myEventsProcessor.onTestFailure(new TestFailedEvent("my_test", "error msg", "method1:1\nmethod2:2", false, null, null));
|
||||
myEventsProcessor.onTestOutput(new TestOutputEvent("my_test", "stdout1 ", true));
|
||||
myEventsProcessor.onTestOutput(new TestOutputEvent("my_test", "stderr1 ", false));
|
||||
|
||||
assertAllOutputs(myMockResetablePrinter, "stdout1 ", "\nerror msg\nmethod1:1\nmethod2:2\nstderr1 ", "");
|
||||
|
||||
@@ -215,9 +216,9 @@ public class SMTRunnerConsoleTest extends BaseSMTRunnerTestCase {
|
||||
|
||||
//other output order
|
||||
final SMTestProxy myTest2 = startTestWithPrinter("my_test2");
|
||||
myEventsProcessor.onTestOutput("my_test2", "stdout1 ", true);
|
||||
myEventsProcessor.onTestOutput("my_test2", "stderr1 ", false);
|
||||
myEventsProcessor.onTestFailure("my_test2", "error msg", "method1:1\nmethod2:2", false, null, null);
|
||||
myEventsProcessor.onTestOutput(new TestOutputEvent("my_test2", "stdout1 ", true));
|
||||
myEventsProcessor.onTestOutput(new TestOutputEvent("my_test2", "stderr1 ", false));
|
||||
myEventsProcessor.onTestFailure(new TestFailedEvent("my_test2", "error msg", "method1:1\nmethod2:2", false, null, null));
|
||||
|
||||
assertAllOutputs(myMockResetablePrinter, "stdout1 ", "stderr1 \nerror msg\nmethod1:1\nmethod2:2\n", "");
|
||||
final MockPrinter mockPrinter2 = new MockPrinter(true);
|
||||
@@ -228,9 +229,9 @@ public class SMTRunnerConsoleTest extends BaseSMTRunnerTestCase {
|
||||
public void testProcessor_OnFailure_EmptyStacktrace() {
|
||||
final SMTestProxy myTest1 = startTestWithPrinter("my_test");
|
||||
|
||||
myEventsProcessor.onTestFailure("my_test", "error msg", "\n\n", false, null, null);
|
||||
myEventsProcessor.onTestOutput("my_test", "stdout1 ", true);
|
||||
myEventsProcessor.onTestOutput("my_test", "stderr1 ", false);
|
||||
myEventsProcessor.onTestFailure(new TestFailedEvent("my_test", "error msg", "\n\n", false, null, null));
|
||||
myEventsProcessor.onTestOutput(new TestOutputEvent("my_test", "stdout1 ", true));
|
||||
myEventsProcessor.onTestOutput(new TestOutputEvent("my_test", "stderr1 ", false));
|
||||
|
||||
assertAllOutputs(myMockResetablePrinter, "stdout1 ", "\nerror msg\nstderr1 ", "");
|
||||
|
||||
@@ -242,9 +243,9 @@ public class SMTRunnerConsoleTest extends BaseSMTRunnerTestCase {
|
||||
public void testProcessor_OnFailure_Comparision_Strings() {
|
||||
final SMTestProxy myTest1 = startTestWithPrinter("my_test");
|
||||
|
||||
myEventsProcessor.onTestFailure("my_test", "error msg", "method1:1\nmethod2:2", false, "actual", "expected");
|
||||
myEventsProcessor.onTestOutput("my_test", "stdout1 ", true);
|
||||
myEventsProcessor.onTestOutput("my_test", "stderr1 ", false);
|
||||
myEventsProcessor.onTestFailure(new TestFailedEvent("my_test", "error msg", "method1:1\nmethod2:2", false, "actual", "expected"));
|
||||
myEventsProcessor.onTestOutput(new TestOutputEvent("my_test", "stdout1 ", true));
|
||||
myEventsProcessor.onTestOutput(new TestOutputEvent("my_test", "stderr1 ", false));
|
||||
|
||||
assertAllOutputs(myMockResetablePrinter,
|
||||
// std out
|
||||
@@ -281,10 +282,10 @@ public class SMTRunnerConsoleTest extends BaseSMTRunnerTestCase {
|
||||
public void testProcessor_OnFailure_Comparision_MultilineTexts() {
|
||||
final SMTestProxy myTest1 = startTestWithPrinter("my_test");
|
||||
|
||||
myEventsProcessor.onTestFailure("my_test", "error msg", "method1:1\nmethod2:2", false,
|
||||
"this is:\nactual", "this is:\nexpected");
|
||||
myEventsProcessor.onTestOutput("my_test", "stdout1 ", true);
|
||||
myEventsProcessor.onTestOutput("my_test", "stderr1 ", false);
|
||||
myEventsProcessor.onTestFailure(new TestFailedEvent("my_test", "error msg", "method1:1\nmethod2:2", false,
|
||||
"this is:\nactual", "this is:\nexpected"));
|
||||
myEventsProcessor.onTestOutput(new TestOutputEvent("my_test", "stdout1 ", true));
|
||||
myEventsProcessor.onTestOutput(new TestOutputEvent("my_test", "stderr1 ", false));
|
||||
|
||||
assertAllOutputs(myMockResetablePrinter, "stdout1 ", "\nerror msg \n" +
|
||||
"\n" +
|
||||
@@ -304,9 +305,9 @@ public class SMTRunnerConsoleTest extends BaseSMTRunnerTestCase {
|
||||
public void testProcessor_OnError() {
|
||||
final SMTestProxy myTest1 = startTestWithPrinter("my_test");
|
||||
|
||||
myEventsProcessor.onTestFailure("my_test", "error msg", "method1:1\nmethod2:2", true, null, null);
|
||||
myEventsProcessor.onTestOutput("my_test", "stdout1 ", true);
|
||||
myEventsProcessor.onTestOutput("my_test", "stderr1 ", false);
|
||||
myEventsProcessor.onTestFailure(new TestFailedEvent("my_test", "error msg", "method1:1\nmethod2:2", true, null, null));
|
||||
myEventsProcessor.onTestOutput(new TestOutputEvent("my_test", "stdout1 ", true));
|
||||
myEventsProcessor.onTestOutput(new TestOutputEvent("my_test", "stderr1 ", false));
|
||||
|
||||
assertAllOutputs(myMockResetablePrinter, "stdout1 ", "\nerror msg\nmethod1:1\nmethod2:2\nstderr1 ", "");
|
||||
|
||||
@@ -316,9 +317,9 @@ public class SMTRunnerConsoleTest extends BaseSMTRunnerTestCase {
|
||||
|
||||
//other output order
|
||||
final SMTestProxy myTest2 = startTestWithPrinter("my_test2");
|
||||
myEventsProcessor.onTestOutput("my_test2", "stdout1 ", true);
|
||||
myEventsProcessor.onTestOutput("my_test2", "stderr1 ", false);
|
||||
myEventsProcessor.onTestFailure("my_test2", "error msg", "method1:1\nmethod2:2", true, null, null);
|
||||
myEventsProcessor.onTestOutput(new TestOutputEvent("my_test2", "stdout1 ", true));
|
||||
myEventsProcessor.onTestOutput(new TestOutputEvent("my_test2", "stderr1 ", false));
|
||||
myEventsProcessor.onTestFailure(new TestFailedEvent("my_test2", "error msg", "method1:1\nmethod2:2", true, null, null));
|
||||
|
||||
assertAllOutputs(myMockResetablePrinter, "stdout1 ", "stderr1 \nerror msg\nmethod1:1\nmethod2:2\n", "");
|
||||
final MockPrinter mockPrinter2 = new MockPrinter(true);
|
||||
@@ -330,8 +331,8 @@ public class SMTRunnerConsoleTest extends BaseSMTRunnerTestCase {
|
||||
final SMTestProxy myTest1 = startTestWithPrinter("my_test");
|
||||
|
||||
myEventsProcessor.onError("error msg", "method1:1\nmethod2:2", true);
|
||||
myEventsProcessor.onTestOutput("my_test", "stdout1 ", true);
|
||||
myEventsProcessor.onTestOutput("my_test", "stderr1 ", false);
|
||||
myEventsProcessor.onTestOutput(new TestOutputEvent("my_test", "stdout1 ", true));
|
||||
myEventsProcessor.onTestOutput(new TestOutputEvent("my_test", "stderr1 ", false));
|
||||
|
||||
assertAllOutputs(myMockResetablePrinter, "stdout1 ", "\nerror msg\nmethod1:1\nmethod2:2\nstderr1 ", "");
|
||||
|
||||
@@ -342,13 +343,13 @@ public class SMTRunnerConsoleTest extends BaseSMTRunnerTestCase {
|
||||
"method1:1\n" +
|
||||
"method2:2\n" +
|
||||
"stderr1 ", "");
|
||||
myEventsProcessor.onTestFinished("my_test", 1);
|
||||
myEventsProcessor.onTestFinished(new TestFinishedEvent("my_test", 1));
|
||||
myTest1.setFinished();
|
||||
|
||||
//other output order
|
||||
final SMTestProxy myTest2 = startTestWithPrinter("my_test2");
|
||||
myEventsProcessor.onTestOutput("my_test2", "stdout1 ", true);
|
||||
myEventsProcessor.onTestOutput("my_test2", "stderr1 ", false);
|
||||
myEventsProcessor.onTestOutput(new TestOutputEvent("my_test2", "stdout1 ", true));
|
||||
myEventsProcessor.onTestOutput(new TestOutputEvent("my_test2", "stderr1 ", false));
|
||||
myEventsProcessor.onError("error msg", "method1:1\nmethod2:2", true);
|
||||
|
||||
assertAllOutputs(myMockResetablePrinter, "stdout1 ", "stderr1 \nerror msg\nmethod1:1\nmethod2:2\n", "");
|
||||
@@ -360,7 +361,7 @@ public class SMTRunnerConsoleTest extends BaseSMTRunnerTestCase {
|
||||
public void testProcessor_Suite_OnErrorMsg() {
|
||||
myEventsProcessor.onError("error msg:root", "method1:1\nmethod2:2", true);
|
||||
|
||||
myEventsProcessor.onSuiteStarted("suite", null);
|
||||
myEventsProcessor.onSuiteStarted(new TestSuiteStartedEvent("suite", null));
|
||||
final SMTestProxy suite = myEventsProcessor.getCurrentSuite();
|
||||
suite.setPrinter(myMockResetablePrinter);
|
||||
myEventsProcessor.onError("error msg:suite", "method1:1\nmethod2:2", true);
|
||||
@@ -391,9 +392,9 @@ public class SMTRunnerConsoleTest extends BaseSMTRunnerTestCase {
|
||||
public void testProcessor_OnIgnored() {
|
||||
final SMTestProxy myTest1 = startTestWithPrinter("my_test");
|
||||
|
||||
myEventsProcessor.onTestIgnored("my_test", "ignored msg", null);
|
||||
myEventsProcessor.onTestOutput("my_test", "stdout1 ", true);
|
||||
myEventsProcessor.onTestOutput("my_test", "stderr1 ", false);
|
||||
myEventsProcessor.onTestIgnored(new TestIgnoredEvent("my_test", "ignored msg", null));
|
||||
myEventsProcessor.onTestOutput(new TestOutputEvent("my_test", "stdout1 ", true));
|
||||
myEventsProcessor.onTestOutput(new TestOutputEvent("my_test", "stderr1 ", false));
|
||||
|
||||
assertAllOutputs(myMockResetablePrinter, "stdout1 ", "stderr1 ", "\nignored msg\n");
|
||||
|
||||
@@ -403,9 +404,9 @@ public class SMTRunnerConsoleTest extends BaseSMTRunnerTestCase {
|
||||
|
||||
//other output order
|
||||
final SMTestProxy myTest2 = startTestWithPrinter("my_test2");
|
||||
myEventsProcessor.onTestOutput("my_test2", "stdout1 ", true);
|
||||
myEventsProcessor.onTestOutput("my_test2", "stderr1 ", false);
|
||||
myEventsProcessor.onTestIgnored("my_test2", "ignored msg", null);
|
||||
myEventsProcessor.onTestOutput(new TestOutputEvent("my_test2", "stdout1 ", true));
|
||||
myEventsProcessor.onTestOutput(new TestOutputEvent("my_test2", "stderr1 ", false));
|
||||
myEventsProcessor.onTestIgnored(new TestIgnoredEvent("my_test2", "ignored msg", null));
|
||||
|
||||
assertAllOutputs(myMockResetablePrinter, "stdout1 ", "stderr1 ", "\nignored msg\n");
|
||||
final MockPrinter mockPrinter2 = new MockPrinter(true);
|
||||
@@ -416,9 +417,9 @@ public class SMTRunnerConsoleTest extends BaseSMTRunnerTestCase {
|
||||
public void testProcessor_OnIgnored_WithStacktrace() {
|
||||
final SMTestProxy myTest1 = startTestWithPrinter("my_test");
|
||||
|
||||
myEventsProcessor.onTestIgnored("my_test", "ignored2 msg", "method1:1\nmethod2:2");
|
||||
myEventsProcessor.onTestOutput("my_test", "stdout1 ", true);
|
||||
myEventsProcessor.onTestOutput("my_test", "stderr1 ", false);
|
||||
myEventsProcessor.onTestIgnored(new TestIgnoredEvent("my_test", "ignored2 msg", "method1:1\nmethod2:2"));
|
||||
myEventsProcessor.onTestOutput(new TestOutputEvent("my_test", "stdout1 ", true));
|
||||
myEventsProcessor.onTestOutput(new TestOutputEvent("my_test", "stderr1 ", false));
|
||||
|
||||
assertAllOutputs(myMockResetablePrinter, "stdout1 ",
|
||||
"\nmethod1:1\nmethod2:2\nstderr1 ",
|
||||
@@ -433,9 +434,9 @@ public class SMTRunnerConsoleTest extends BaseSMTRunnerTestCase {
|
||||
|
||||
//other output order
|
||||
final SMTestProxy myTest2 = startTestWithPrinter("my_test2");
|
||||
myEventsProcessor.onTestOutput("my_test2", "stdout1 ", true);
|
||||
myEventsProcessor.onTestOutput("my_test2", "stderr1 ", false);
|
||||
myEventsProcessor.onTestIgnored("my_test2", "ignored msg", "method1:1\nmethod2:2");
|
||||
myEventsProcessor.onTestOutput(new TestOutputEvent("my_test2", "stdout1 ", true));
|
||||
myEventsProcessor.onTestOutput(new TestOutputEvent("my_test2", "stderr1 ", false));
|
||||
myEventsProcessor.onTestIgnored(new TestIgnoredEvent("my_test2", "ignored msg", "method1:1\nmethod2:2"));
|
||||
|
||||
assertAllOutputs(myMockResetablePrinter,
|
||||
"stdout1 ",
|
||||
@@ -465,7 +466,7 @@ public class SMTRunnerConsoleTest extends BaseSMTRunnerTestCase {
|
||||
public void testOnUncapturedOutput_SomeSuite() {
|
||||
myEventsProcessor.onStartTesting();
|
||||
|
||||
myEventsProcessor.onSuiteStarted("my suite", null);
|
||||
myEventsProcessor.onSuiteStarted(new TestSuiteStartedEvent("my suite", null));
|
||||
final SMTestProxy mySuite = myEventsProcessor.getCurrentSuite();
|
||||
assertTrue(mySuite != myRootSuite);
|
||||
mySuite.setPrinter(myMockResetablePrinter);
|
||||
@@ -476,7 +477,7 @@ public class SMTRunnerConsoleTest extends BaseSMTRunnerTestCase {
|
||||
public void testOnUncapturedOutput_SomeTest() {
|
||||
myEventsProcessor.onStartTesting();
|
||||
|
||||
myEventsProcessor.onSuiteStarted("my suite", null);
|
||||
myEventsProcessor.onSuiteStarted(new TestSuiteStartedEvent("my suite", null));
|
||||
startTestWithPrinter("my test");
|
||||
|
||||
assertOnUncapturedOutput();
|
||||
@@ -514,9 +515,9 @@ public class SMTRunnerConsoleTest extends BaseSMTRunnerTestCase {
|
||||
myConsole.attachToProcess(null);
|
||||
|
||||
myEventsProcessor.onStartTesting();
|
||||
myEventsProcessor.onSuiteStarted("suite", null);
|
||||
myEventsProcessor.onSuiteStarted(new TestSuiteStartedEvent("suite", null));
|
||||
final SMTestProxy suite = myEventsProcessor.getCurrentSuite();
|
||||
myEventsProcessor.onSuiteFinished("suite");
|
||||
myEventsProcessor.onSuiteFinished(new TestSuiteFinishedEvent("suite"));
|
||||
myEventsProcessor.onUncapturedOutput("preved", ProcessOutputTypes.STDOUT);
|
||||
myEventsProcessor.onFinishTesting();
|
||||
|
||||
@@ -536,7 +537,7 @@ public class SMTRunnerConsoleTest extends BaseSMTRunnerTestCase {
|
||||
}
|
||||
|
||||
private SMTestProxy startTestWithPrinter(final String testName) {
|
||||
myEventsProcessor.onTestStarted(testName, null);
|
||||
myEventsProcessor.onTestStarted(new TestStartedEvent(testName, null));
|
||||
final SMTestProxy proxy =
|
||||
myEventsProcessor.getProxyByFullTestName(myEventsProcessor.getFullTestName(testName));
|
||||
proxy.setPrinter(myMockResetablePrinter);
|
||||
|
||||
+17
-16
@@ -21,6 +21,7 @@ import com.intellij.execution.testframework.sm.Marker;
|
||||
import com.intellij.execution.testframework.sm.runner.BaseSMTRunnerTestCase;
|
||||
import com.intellij.execution.testframework.sm.runner.GeneralToSMTRunnerEventsConvertor;
|
||||
import com.intellij.execution.testframework.sm.runner.SMTestProxy;
|
||||
import com.intellij.execution.testframework.sm.runner.events.*;
|
||||
import com.intellij.openapi.progress.util.ColorProgressBar;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
@@ -226,24 +227,24 @@ public class SMTestRunnerResultsFormTest extends BaseSMTRunnerTestCase {
|
||||
TestConsoleProperties.HIDE_PASSED_TESTS.set(myConsoleProperties, true);
|
||||
|
||||
myEventsProcessor.onStartTesting();
|
||||
myEventsProcessor.onSuiteStarted("suite", null);
|
||||
myEventsProcessor.onSuiteStarted(new TestSuiteStartedEvent("suite", null));
|
||||
myResultsViewer.performUpdate();
|
||||
|
||||
myEventsProcessor.onTestStarted("test_failed", null);
|
||||
myEventsProcessor.onTestStarted(new TestStartedEvent("test_failed", null));
|
||||
myResultsViewer.performUpdate();
|
||||
myEventsProcessor.onTestFailure("test_failed", "", "", false, null, null);
|
||||
myEventsProcessor.onTestFailure(new TestFailedEvent("test_failed", "", "", false, null, null));
|
||||
myResultsViewer.performUpdate();
|
||||
myEventsProcessor.onTestFinished("test_failed", 10);
|
||||
myEventsProcessor.onTestFinished(new TestFinishedEvent("test_failed", 10));
|
||||
myResultsViewer.performUpdate();
|
||||
|
||||
myEventsProcessor.onTestStarted("test", null);
|
||||
myEventsProcessor.onTestStarted(new TestStartedEvent("test", null));
|
||||
myResultsViewer.performUpdate();
|
||||
assertEquals(2, myTreeModel.getChildCount(myTreeModel.getChild(myTreeModel.getRoot(), 0)));
|
||||
|
||||
myEventsProcessor.onTestFinished("test", 10);
|
||||
myEventsProcessor.onTestFinished(new TestFinishedEvent("test", 10));
|
||||
assertEquals(2, myTreeModel.getChildCount(myTreeModel.getChild(myTreeModel.getRoot(), 0)));
|
||||
|
||||
myEventsProcessor.onSuiteFinished("suite");
|
||||
myEventsProcessor.onSuiteFinished(new TestSuiteFinishedEvent("suite"));
|
||||
myEventsProcessor.onFinishTesting();
|
||||
|
||||
assertEquals(1, myTreeModel.getChildCount(myTreeModel.getChild(myTreeModel.getRoot(), 0)));
|
||||
@@ -251,27 +252,27 @@ public class SMTestRunnerResultsFormTest extends BaseSMTRunnerTestCase {
|
||||
|
||||
public void testExpandIfOnlyOneRootChild() throws InterruptedException {
|
||||
myEventsProcessor.onStartTesting();
|
||||
myEventsProcessor.onSuiteStarted("suite1", null);
|
||||
myEventsProcessor.onSuiteStarted(new TestSuiteStartedEvent("suite1", null));
|
||||
myResultsViewer.performUpdate();
|
||||
myEventsProcessor.onSuiteStarted("suite2", null);
|
||||
myEventsProcessor.onSuiteStarted(new TestSuiteStartedEvent("suite2", null));
|
||||
myResultsViewer.performUpdate();
|
||||
|
||||
myEventsProcessor.onTestStarted("test_failed", null);
|
||||
myEventsProcessor.onTestStarted(new TestStartedEvent("test_failed", null));
|
||||
myResultsViewer.performUpdate();
|
||||
myEventsProcessor.onTestFailure("test_failed", "", "", false, null, null);
|
||||
myEventsProcessor.onTestFailure(new TestFailedEvent("test_failed", "", "", false, null, null));
|
||||
myResultsViewer.performUpdate();
|
||||
myEventsProcessor.onTestFinished("test_failed", 10);
|
||||
myEventsProcessor.onTestFinished(new TestFinishedEvent("test_failed", 10));
|
||||
myResultsViewer.performUpdate();
|
||||
|
||||
myEventsProcessor.onTestStarted("test", null);
|
||||
myEventsProcessor.onTestStarted(new TestStartedEvent("test", null));
|
||||
myResultsViewer.performUpdate();
|
||||
|
||||
myEventsProcessor.onTestFinished("test", 10);
|
||||
myEventsProcessor.onTestFinished(new TestFinishedEvent("test", 10));
|
||||
myResultsViewer.performUpdate();
|
||||
|
||||
myEventsProcessor.onSuiteFinished("suite2");
|
||||
myEventsProcessor.onSuiteFinished(new TestSuiteFinishedEvent("suite2"));
|
||||
myResultsViewer.performUpdate();
|
||||
myEventsProcessor.onSuiteFinished("suite1");
|
||||
myEventsProcessor.onSuiteFinished(new TestSuiteFinishedEvent("suite1"));
|
||||
myResultsViewer.performUpdate();
|
||||
myEventsProcessor.onFinishTesting();
|
||||
myResultsViewer.performUpdate();
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.jetbrains.android.util;
|
||||
|
||||
import com.android.jarutils.SignedJarBuilder;
|
||||
import com.android.resources.ResourceFolderType;
|
||||
import com.android.resources.ResourceType;
|
||||
import com.android.sdklib.IAndroidTarget;
|
||||
@@ -38,6 +39,9 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.*;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.*;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarOutputStream;
|
||||
@@ -90,6 +94,8 @@ public class AndroidCommonUtils {
|
||||
|
||||
@NonNls public static final String PACKAGE_MANIFEST_ATTRIBUTE = "package";
|
||||
|
||||
@NonNls public static final String ANDROID_FINAL_PACKAGE_FOR_ARTIFACT_SUFFIX = ".afp";
|
||||
|
||||
private AndroidCommonUtils() {
|
||||
}
|
||||
|
||||
@@ -529,4 +535,32 @@ public class AndroidCommonUtils {
|
||||
final String b = path.substring(dot);
|
||||
return a + suffix + b;
|
||||
}
|
||||
|
||||
public static void signApk(@NotNull File srcApk,
|
||||
@NotNull File destFile,
|
||||
@NotNull PrivateKey privateKey,
|
||||
@NotNull X509Certificate certificate)
|
||||
throws IOException, GeneralSecurityException {
|
||||
FileOutputStream fos = new FileOutputStream(destFile);
|
||||
SignedJarBuilder builder = new SafeSignedJarBuilder(fos, privateKey, certificate, destFile.getPath());
|
||||
FileInputStream fis = new FileInputStream(srcApk);
|
||||
try {
|
||||
builder.writeZip(fis, null);
|
||||
builder.close();
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
fis.close();
|
||||
}
|
||||
catch (IOException ignored) {
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
fos.close();
|
||||
}
|
||||
catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,6 +219,11 @@
|
||||
<usageTargetProvider implementation="org.jetbrains.android.AndroidUsagesTargetProvider"/>
|
||||
<renameHandler implementation="org.jetbrains.android.AndroidRenameHandler" order="first"/>
|
||||
|
||||
<packaging.elementType implementation="org.jetbrains.android.compiler.artifact.AndroidFinalPackageElementType"/>
|
||||
<packaging.sourceItemProvider implementation="org.jetbrains.android.compiler.artifact.AndroidSourceItemsProvider"/>
|
||||
<packaging.artifactPropertiesProvider implementation="org.jetbrains.android.compiler.artifact.AndroidArtifactPropertiesProvider"/>
|
||||
<packaging.artifactType implementation="org.jetbrains.android.compiler.artifact.AndroidApplicationArtifactType"/>
|
||||
|
||||
<compileServer.plugin jar-path="jps/android-jps-plugin.jar"/>
|
||||
<compileServer.plugin jar-path="android-common.jar"/>
|
||||
<compileServer.plugin jar-path="android-rt.jar"/>
|
||||
|
||||
@@ -44,6 +44,9 @@ import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vfs.*;
|
||||
import com.intellij.packaging.artifacts.Artifact;
|
||||
import com.intellij.packaging.artifacts.ArtifactProperties;
|
||||
import com.intellij.packaging.impl.compiler.ArtifactCompileScope;
|
||||
import com.intellij.psi.JavaPsiFacade;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.PsiFile;
|
||||
@@ -52,6 +55,9 @@ import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
import com.intellij.util.containers.HashSet;
|
||||
import org.jetbrains.android.compiler.artifact.AndroidApplicationArtifactProperties;
|
||||
import org.jetbrains.android.compiler.artifact.AndroidArtifactPropertiesProvider;
|
||||
import org.jetbrains.android.compiler.artifact.AndroidArtifactSigningMode;
|
||||
import org.jetbrains.android.dom.manifest.Manifest;
|
||||
import org.jetbrains.android.dom.resources.Attr;
|
||||
import org.jetbrains.android.dom.resources.DeclareStyleable;
|
||||
@@ -86,6 +92,7 @@ public class AndroidCompileUtil {
|
||||
|
||||
@NonNls public static final String PROGUARD_CFG_FILE_NAME = "proguard-project.txt";
|
||||
@NonNls public static final String OLD_PROGUARD_CFG_FILE_NAME = "proguard.cfg";
|
||||
public static final String UNSIGNED_SUFFIX = ".unsigned";
|
||||
|
||||
private AndroidCompileUtil() {
|
||||
}
|
||||
@@ -131,7 +138,7 @@ public class AndroidCompileUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
static void addMessages(final CompileContext context, final Map<CompilerMessageCategory, List<String>> messages, Module module) {
|
||||
public static void addMessages(final CompileContext context, final Map<CompilerMessageCategory, List<String>> messages, @Nullable Module module) {
|
||||
addMessages(context, messages, null, module);
|
||||
}
|
||||
|
||||
@@ -150,7 +157,7 @@ public class AndroidCompileUtil {
|
||||
static void addMessages(final CompileContext context,
|
||||
final Map<CompilerMessageCategory, List<String>> messages,
|
||||
@Nullable final Map<VirtualFile, VirtualFile> presentableFilesMap,
|
||||
final Module module) {
|
||||
@Nullable final Module module) {
|
||||
ApplicationManager.getApplication().runReadAction(new Runnable() {
|
||||
public void run() {
|
||||
if (context.getProject().isDisposed()) return;
|
||||
@@ -167,7 +174,7 @@ public class AndroidCompileUtil {
|
||||
line = Integer.parseInt(matcher.group(2));
|
||||
}
|
||||
}
|
||||
context.addMessage(category, '[' + module.getName() + "] " + message, url, line, -1);
|
||||
context.addMessage(category, (module != null ? '[' + module.getName() + "] " : "") + message, url, line, -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -619,7 +626,22 @@ public class AndroidCompileUtil {
|
||||
|
||||
public static boolean isReleaseBuild(@NotNull CompileContext context) {
|
||||
final Boolean value = context.getCompileScope().getUserData(RELEASE_BUILD_KEY);
|
||||
return value != null && value.booleanValue();
|
||||
if (value != null && value.booleanValue()) {
|
||||
return true;
|
||||
}
|
||||
final Project project = context.getProject();
|
||||
final Set<Artifact> artifacts = ArtifactCompileScope.getArtifactsToBuild(project, context.getCompileScope(), false);
|
||||
|
||||
if (artifacts != null) {
|
||||
for (Artifact artifact : artifacts) {
|
||||
final ArtifactProperties<?> properties = artifact.getProperties(AndroidArtifactPropertiesProvider.getInstance());
|
||||
if (properties instanceof AndroidApplicationArtifactProperties &&
|
||||
((AndroidApplicationArtifactProperties)properties).getSigningMode() != AndroidArtifactSigningMode.DEBUG) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void setReleaseBuild(@NotNull CompileScope compileScope) {
|
||||
@@ -668,7 +690,10 @@ public class AndroidCompileUtil {
|
||||
}
|
||||
}
|
||||
|
||||
private static void initializeGenSourceRoot(@NotNull Module module, @Nullable String sourceRootPath, boolean createIfNotExist, boolean exclude) {
|
||||
private static void initializeGenSourceRoot(@NotNull Module module,
|
||||
@Nullable String sourceRootPath,
|
||||
boolean createIfNotExist,
|
||||
boolean exclude) {
|
||||
if (sourceRootPath == null) {
|
||||
return;
|
||||
}
|
||||
@@ -716,31 +741,31 @@ public class AndroidCompileUtil {
|
||||
final Resources resources = pair.getFirst();
|
||||
waitForSmartMode(project);
|
||||
|
||||
ApplicationManager.getApplication().runReadAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!resources.isValid() || facet.getModule().isDisposed() || project.isDisposed()) {
|
||||
return;
|
||||
}
|
||||
ApplicationManager.getApplication().runReadAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!resources.isValid() || facet.getModule().isDisposed() || project.isDisposed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (final Attr attr : resources.getAttrs()) {
|
||||
final String name = attr.getName().getValue();
|
||||
for (final Attr attr : resources.getAttrs()) {
|
||||
final String name = attr.getName().getValue();
|
||||
|
||||
if (name != null) {
|
||||
resourceSet.add(new ResourceEntry(ResourceType.ATTR.getName(), name));
|
||||
}
|
||||
}
|
||||
|
||||
for (final DeclareStyleable styleable : resources.getDeclareStyleables()) {
|
||||
final String name = styleable.getName().getValue();
|
||||
|
||||
if (name != null) {
|
||||
resourceSet.add(new ResourceEntry(ResourceType.DECLARE_STYLEABLE.getName(), name));
|
||||
}
|
||||
if (name != null) {
|
||||
resourceSet.add(new ResourceEntry(ResourceType.ATTR.getName(), name));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (final DeclareStyleable styleable : resources.getDeclareStyleables()) {
|
||||
final String name = styleable.getName().getValue();
|
||||
|
||||
if (name != null) {
|
||||
resourceSet.add(new ResourceEntry(ResourceType.DECLARE_STYLEABLE.getName(), name));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
waitForSmartMode(project);
|
||||
|
||||
@@ -929,4 +954,22 @@ public class AndroidCompileUtil {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getUnsignedApkPath(@NotNull AndroidFacet facet) {
|
||||
final String apkPath = AndroidRootUtil.getApkPath(facet);
|
||||
return apkPath != null ? AndroidCommonUtils.addSuffixToFileName(apkPath, UNSIGNED_SUFFIX) : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static <T> T handleExceptionError(@NotNull CompileContext context,
|
||||
@NotNull String messagePrefix,
|
||||
@NotNull Exception e) {
|
||||
reportException(context, messagePrefix, e);
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void reportException(@NotNull CompileContext context, @NotNull String messagePrefix, @NotNull Exception e) {
|
||||
context.addMessage(CompilerMessageCategory.ERROR, messagePrefix + e.getClass().getSimpleName() + ": " + e.getMessage(), null, -1, -1);
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.jetbrains.android.compiler;
|
||||
|
||||
import com.intellij.compiler.CompilerIOUtil;
|
||||
import com.intellij.compiler.impl.CompilerUtil;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.compiler.*;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
@@ -27,6 +28,7 @@ import com.intellij.openapi.roots.ModuleOrderEntry;
|
||||
import com.intellij.openapi.roots.ModuleRootManager;
|
||||
import com.intellij.openapi.roots.OrderEntry;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vfs.JarFileSystem;
|
||||
import com.intellij.openapi.vfs.VfsUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.containers.HashSet;
|
||||
@@ -54,8 +56,6 @@ import java.util.*;
|
||||
public class AndroidPackagingCompiler implements PackagingCompiler {
|
||||
private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.compiler.AndroidPackagingCompiler");
|
||||
|
||||
public static final String UNSIGNED_SUFFIX = ".unsigned";
|
||||
|
||||
public void processOutdatedItem(CompileContext context, String url, @Nullable ValidityState state) {
|
||||
}
|
||||
|
||||
@@ -235,7 +235,7 @@ public class AndroidPackagingCompiler implements PackagingCompiler {
|
||||
: item.getResPackagePath();
|
||||
|
||||
final String finalPath = unsigned
|
||||
? AndroidCommonUtils.addSuffixToFileName(item.getFinalPath(), UNSIGNED_SUFFIX)
|
||||
? AndroidCommonUtils.addSuffixToFileName(item.getFinalPath(), AndroidCompileUtil.UNSIGNED_SUFFIX)
|
||||
: item.getFinalPath();
|
||||
|
||||
final String[] sourceRoots = AndroidCompileUtil.toOsPaths(item.getSourceRoots());
|
||||
@@ -247,6 +247,18 @@ public class AndroidPackagingCompiler implements PackagingCompiler {
|
||||
item.getAdditionalNativeLibs(), finalPath, unsigned, item.mySdkPath, item.getCustomKeystorePath(),
|
||||
new ExcludedSourcesFilter(project)));
|
||||
|
||||
if (messages.get(CompilerMessageCategory.ERROR).size() == 0) {
|
||||
if (item.myReleaseBuild == unsigned) {
|
||||
final File dst = new File(
|
||||
AndroidCommonUtils.addSuffixToFileName(item.getFinalPath(), AndroidCommonUtils.ANDROID_FINAL_PACKAGE_FOR_ARTIFACT_SUFFIX));
|
||||
FileUtil.copy(new File(finalPath), dst);
|
||||
CompilerUtil.refreshIOFile(dst);
|
||||
final VirtualFile jar = JarFileSystem.getInstance().refreshAndFindFileByPath(dst.getPath() + "!/");
|
||||
if (jar != null) {
|
||||
jar.refresh(false, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
AndroidCompileUtil.addMessages(context, messages, item.myModule);
|
||||
}
|
||||
catch (final IOException e) {
|
||||
|
||||
@@ -17,6 +17,8 @@ package org.jetbrains.android.compiler;
|
||||
|
||||
import com.intellij.compiler.CompilerConfiguration;
|
||||
import com.intellij.compiler.CompilerConfigurationImpl;
|
||||
import com.intellij.compiler.options.CompileStepBeforeRun;
|
||||
import com.intellij.facet.ProjectFacetManager;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ModalityState;
|
||||
import com.intellij.openapi.compiler.*;
|
||||
@@ -35,7 +37,13 @@ import com.intellij.openapi.roots.OrderEntry;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.packaging.artifacts.Artifact;
|
||||
import com.intellij.packaging.artifacts.ArtifactProperties;
|
||||
import com.intellij.packaging.impl.compiler.ArtifactCompileScope;
|
||||
import com.intellij.util.containers.hash.HashSet;
|
||||
import org.jetbrains.android.compiler.artifact.AndroidApplicationArtifactProperties;
|
||||
import org.jetbrains.android.compiler.artifact.AndroidArtifactPropertiesProvider;
|
||||
import org.jetbrains.android.compiler.artifact.AndroidArtifactSigningMode;
|
||||
import org.jetbrains.android.facet.AndroidFacet;
|
||||
import org.jetbrains.android.facet.AndroidRootUtil;
|
||||
import org.jetbrains.android.maven.AndroidMavenUtil;
|
||||
@@ -55,10 +63,13 @@ public class AndroidPrecompileTask implements CompileTask {
|
||||
|
||||
@Override
|
||||
public boolean execute(CompileContext context) {
|
||||
if (!checkArtifacts(context)) {
|
||||
return false;
|
||||
}
|
||||
checkAndroidDependencies(context);
|
||||
|
||||
final Project project = context.getProject();
|
||||
|
||||
|
||||
ExcludedEntriesConfiguration configuration =
|
||||
((CompilerConfigurationImpl)CompilerConfiguration.getInstance(project)).getExcludedEntriesConfiguration();
|
||||
|
||||
@@ -107,6 +118,62 @@ public class AndroidPrecompileTask implements CompileTask {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean checkArtifacts(@NotNull CompileContext context) {
|
||||
final Project project = context.getProject();
|
||||
if (!ProjectFacetManager.getInstance(project).hasFacets(AndroidFacet.ID)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final Set<Artifact> artifacts = ArtifactCompileScope.getArtifactsToBuild(project, context.getCompileScope(), false);
|
||||
if (artifacts == null) {
|
||||
return true;
|
||||
}
|
||||
final Set<Artifact> debugArtifacts = new HashSet<Artifact>();
|
||||
final Set<Artifact> releaseArtifacts = new HashSet<Artifact>();
|
||||
|
||||
for (final Artifact artifact : artifacts) {
|
||||
final ArtifactProperties<?> properties = artifact.getProperties(AndroidArtifactPropertiesProvider.getInstance());
|
||||
if (properties instanceof AndroidApplicationArtifactProperties) {
|
||||
final AndroidArtifactSigningMode mode = ((AndroidApplicationArtifactProperties)properties).getSigningMode();
|
||||
if (mode == AndroidArtifactSigningMode.DEBUG) {
|
||||
debugArtifacts.add(artifact);
|
||||
}
|
||||
else {
|
||||
releaseArtifacts.add(artifact);
|
||||
}
|
||||
}
|
||||
}
|
||||
boolean success = true;
|
||||
|
||||
if (debugArtifacts.size() > 0 && releaseArtifacts.size() > 0) {
|
||||
final String message = "Cannot build debug and release Android artifacts in the same session\n" +
|
||||
"Debug artifacts: " + toString(debugArtifacts) + "\n" +
|
||||
"Release artifacts: " + toString(releaseArtifacts);
|
||||
context.addMessage(CompilerMessageCategory.ERROR, message, null, -1, -1);
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (releaseArtifacts.size() > 0 &&
|
||||
CompileStepBeforeRun.getRunConfiguration(context) != null) {
|
||||
final String message = "Cannot build release Android artifacts in the 'make before run' session\n" +
|
||||
"Release artifacts: " + toString(releaseArtifacts);
|
||||
context.addMessage(CompilerMessageCategory.ERROR, message, null, -1, -1);
|
||||
success = false;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
private static String toString(Collection<Artifact> artifacts) {
|
||||
final StringBuilder result = new StringBuilder();
|
||||
for (Artifact artifact : artifacts) {
|
||||
if (result.length() > 0) {
|
||||
result.append(", ");
|
||||
}
|
||||
result.append(artifact.getName());
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private static void checkAndroidDependencies(@NotNull CompileContext context) {
|
||||
for (Module module : context.getCompileScope().getAffectedModules()) {
|
||||
final AndroidFacet facet = AndroidFacet.getInstance(module);
|
||||
@@ -142,7 +209,7 @@ public class AndroidPrecompileTask implements CompileTask {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void clearResCache(@NotNull AndroidFacet facet, @NotNull CompileContext context) {
|
||||
final Module module = facet.getModule();
|
||||
|
||||
|
||||
+250
@@ -0,0 +1,250 @@
|
||||
package org.jetbrains.android.compiler.artifact;
|
||||
|
||||
import com.intellij.openapi.compiler.CompileContext;
|
||||
import com.intellij.openapi.compiler.CompilerMessageCategory;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vfs.VfsUtilCore;
|
||||
import com.intellij.packaging.artifacts.Artifact;
|
||||
import com.intellij.packaging.artifacts.ArtifactProperties;
|
||||
import com.intellij.packaging.ui.ArtifactEditorContext;
|
||||
import com.intellij.packaging.ui.ArtifactPropertiesEditor;
|
||||
import com.intellij.util.xmlb.XmlSerializerUtil;
|
||||
import com.intellij.util.xmlb.annotations.Transient;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.jetbrains.android.compiler.AndroidCompileUtil;
|
||||
import org.jetbrains.android.util.AndroidBundle;
|
||||
import org.jetbrains.android.util.AndroidCommonUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.*;
|
||||
import java.security.*;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
/**
|
||||
* @author Eugene.Kudelevsky
|
||||
*/
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
public class AndroidApplicationArtifactProperties extends ArtifactProperties<AndroidApplicationArtifactProperties> {
|
||||
private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.compiler.artifact.AndroidApplicationArtifactProperties");
|
||||
|
||||
private AndroidArtifactSigningMode mySigningMode = AndroidArtifactSigningMode.RELEASE_UNSIGNED;
|
||||
private String myKeyStoreUrl = "";
|
||||
private String myKeyStorePassword = "";
|
||||
private String myKeyAlias = "";
|
||||
private String myKeyPassword = "";
|
||||
|
||||
@Override
|
||||
public void onBuildFinished(@NotNull Artifact artifact, @NotNull CompileContext context) {
|
||||
if (!(artifact.getArtifactType() instanceof AndroidApplicationArtifactType) ||
|
||||
mySigningMode != AndroidArtifactSigningMode.RELEASE_SIGNED) {
|
||||
return;
|
||||
}
|
||||
final String errorPrefix = "[Artifact '" + artifact.getName() + "'] ";
|
||||
final Pair<PrivateKey, X509Certificate> pair = getPrivateKeyAndCertificate(context, errorPrefix);
|
||||
if (pair == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String artifactFilePath = artifact.getOutputFilePath();
|
||||
final String prefix = "Cannot sign artifact " + artifact.getName() + ": ";
|
||||
|
||||
if (artifactFilePath == null) {
|
||||
context.addMessage(CompilerMessageCategory.ERROR, prefix + "output path is not specified", null, -1, -1);
|
||||
return;
|
||||
}
|
||||
|
||||
final File artifactFile = new File(artifactFilePath);
|
||||
if (!artifactFile.exists()) {
|
||||
context.addMessage(CompilerMessageCategory.ERROR, prefix + "file " + artifactFilePath + " hasn't been generated", null, -1, -1);
|
||||
return;
|
||||
}
|
||||
|
||||
File tmpDir = null;
|
||||
try {
|
||||
tmpDir = FileUtil.createTempDirectory("android_artifact", "tmp");
|
||||
final File tmpArtifact = new File(tmpDir, "tmpArtifact.apk");
|
||||
FileUtil.copy(artifactFile, tmpArtifact);
|
||||
|
||||
if (!FileUtil.delete(artifactFile)) {
|
||||
context.addMessage(CompilerMessageCategory.ERROR, "Cannot delete file " + artifactFile.getPath(), null, -1, -1);
|
||||
return;
|
||||
}
|
||||
AndroidCommonUtils.signApk(tmpArtifact, artifactFile, pair.getFirst(), pair.getSecond());
|
||||
}
|
||||
catch (IOException e) {
|
||||
context.addMessage(CompilerMessageCategory.ERROR, prefix + "I/O error: " + e.getMessage(), null, -1, -1);
|
||||
}
|
||||
catch (GeneralSecurityException e) {
|
||||
AndroidCompileUtil.reportException(context, prefix, e);
|
||||
}
|
||||
finally {
|
||||
if (tmpDir != null) {
|
||||
FileUtil.delete(tmpDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArtifactPropertiesEditor createEditor(@NotNull ArtifactEditorContext context) {
|
||||
return new AndroidArtifactPropertiesEditor(this, context.getProject());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AndroidApplicationArtifactProperties getState() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadState(AndroidApplicationArtifactProperties state) {
|
||||
XmlSerializerUtil.copyBean(state, this);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Pair<PrivateKey, X509Certificate> getPrivateKeyAndCertificate(@NotNull CompileContext context, @NotNull String errorPrefix) {
|
||||
final String keyStoreFilePath = myKeyStoreUrl != null ? VfsUtilCore.urlToPath(myKeyStoreUrl) : "";
|
||||
|
||||
if (keyStoreFilePath.length() == 0) {
|
||||
context.addMessage(CompilerMessageCategory.ERROR, errorPrefix + "Key store file is not specified", null, -1, -1);
|
||||
return null;
|
||||
}
|
||||
if (myKeyStorePassword == null || myKeyStorePassword.length() == 0) {
|
||||
context.addMessage(CompilerMessageCategory.ERROR, errorPrefix + "Key store password is not specified", null, -1, -1);
|
||||
return null;
|
||||
}
|
||||
if (myKeyAlias == null || myKeyAlias.length() == 0) {
|
||||
context.addMessage(CompilerMessageCategory.ERROR, errorPrefix + "Key alias is not specified", null, -1, -1);
|
||||
return null;
|
||||
}
|
||||
if (myKeyPassword == null || myKeyPassword.length() == 0) {
|
||||
context.addMessage(CompilerMessageCategory.ERROR, errorPrefix + "Key password is not specified", null, -1, -1);
|
||||
return null;
|
||||
}
|
||||
final File keyStoreFile = new File(keyStoreFilePath);
|
||||
final String keystorePasswordStr = getPlainKeystorePassword();
|
||||
final char[] keystorePassword = keystorePasswordStr.toCharArray();
|
||||
|
||||
final String keyPasswordStr = getPlainKeyPassword();
|
||||
final char[] keyPassword = keyPasswordStr.toCharArray();
|
||||
|
||||
final KeyStore keyStore;
|
||||
InputStream is = null;
|
||||
try {
|
||||
//noinspection IOResourceOpenedButNotSafelyClosed
|
||||
is = new FileInputStream(keyStoreFile);
|
||||
keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
keyStore.load(is, keystorePassword);
|
||||
|
||||
final KeyStore.PrivateKeyEntry entry =
|
||||
(KeyStore.PrivateKeyEntry)keyStore.getEntry(myKeyAlias, new KeyStore.PasswordProtection(keyPassword));
|
||||
if (entry == null) {
|
||||
context.addMessage(CompilerMessageCategory.ERROR,
|
||||
errorPrefix + AndroidBundle.message("android.extract.package.cannot.find.key.error", myKeyAlias), null, -1, -1);
|
||||
return null;
|
||||
}
|
||||
|
||||
final PrivateKey privateKey = entry.getPrivateKey();
|
||||
final Certificate certificate = entry.getCertificate();
|
||||
if (privateKey == null || certificate == null) {
|
||||
context.addMessage(CompilerMessageCategory.ERROR,
|
||||
errorPrefix + AndroidBundle.message("android.extract.package.cannot.find.key.error", myKeyAlias), null, -1, -1);
|
||||
return null;
|
||||
}
|
||||
return Pair.create(privateKey, (X509Certificate)certificate);
|
||||
}
|
||||
catch (FileNotFoundException e) {
|
||||
return AndroidCompileUtil.handleExceptionError(context, errorPrefix, e);
|
||||
}
|
||||
catch (KeyStoreException e) {
|
||||
return AndroidCompileUtil.handleExceptionError(context, errorPrefix, e);
|
||||
}
|
||||
catch (CertificateException e) {
|
||||
return AndroidCompileUtil.handleExceptionError(context, errorPrefix, e);
|
||||
}
|
||||
catch (NoSuchAlgorithmException e) {
|
||||
return AndroidCompileUtil.handleExceptionError(context, errorPrefix, e);
|
||||
}
|
||||
catch (IOException e) {
|
||||
return AndroidCompileUtil.handleExceptionError(context, errorPrefix, e);
|
||||
}
|
||||
catch (UnrecoverableEntryException e) {
|
||||
return AndroidCompileUtil.handleExceptionError(context, errorPrefix, e);
|
||||
}
|
||||
finally {
|
||||
if (is != null) {
|
||||
try {
|
||||
is.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.info(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public AndroidArtifactSigningMode getSigningMode() {
|
||||
return mySigningMode;
|
||||
}
|
||||
|
||||
public void setSigningMode(AndroidArtifactSigningMode signingMode) {
|
||||
mySigningMode = signingMode;
|
||||
}
|
||||
|
||||
public String getKeyStoreUrl() {
|
||||
return myKeyStoreUrl;
|
||||
}
|
||||
|
||||
public String getKeyStorePassword() {
|
||||
return myKeyStorePassword;
|
||||
}
|
||||
|
||||
public String getKeyAlias() {
|
||||
return myKeyAlias;
|
||||
}
|
||||
|
||||
public String getKeyPassword() {
|
||||
return myKeyPassword;
|
||||
}
|
||||
|
||||
public void setKeyStoreUrl(String keyStoreUrl) {
|
||||
myKeyStoreUrl = keyStoreUrl;
|
||||
}
|
||||
|
||||
public void setKeyStorePassword(String keyStorePassword) {
|
||||
myKeyStorePassword = keyStorePassword;
|
||||
}
|
||||
|
||||
public void setKeyAlias(String keyAlias) {
|
||||
myKeyAlias = keyAlias;
|
||||
}
|
||||
|
||||
public void setKeyPassword(String keyPassword) {
|
||||
myKeyPassword = keyPassword;
|
||||
}
|
||||
|
||||
@Transient
|
||||
@NotNull
|
||||
public String getPlainKeystorePassword() {
|
||||
return new String(new Base64().decode(myKeyStorePassword.getBytes()));
|
||||
}
|
||||
|
||||
@Transient
|
||||
public void setPlainKeystorePassword(@NotNull String password) {
|
||||
myKeyStorePassword = new String(new Base64().encode(password.getBytes()));
|
||||
}
|
||||
|
||||
@Transient
|
||||
@NotNull
|
||||
public String getPlainKeyPassword() {
|
||||
return new String(new Base64().decode(myKeyPassword.getBytes()));
|
||||
}
|
||||
|
||||
@Transient
|
||||
public void setPlainKeyPassword(@NotNull String password) {
|
||||
myKeyPassword = new String(new Base64().encode(password.getBytes()));
|
||||
}
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
package org.jetbrains.android.compiler.artifact;
|
||||
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.roots.ui.configuration.artifacts.sourceItems.LibrarySourceItem;
|
||||
import com.intellij.openapi.roots.ui.configuration.artifacts.sourceItems.ModuleOutputSourceItem;
|
||||
import com.intellij.packaging.artifacts.ArtifactTemplate;
|
||||
import com.intellij.packaging.artifacts.ArtifactType;
|
||||
import com.intellij.packaging.elements.CompositePackagingElement;
|
||||
import com.intellij.packaging.elements.PackagingElementFactory;
|
||||
import com.intellij.packaging.elements.PackagingElementOutputKind;
|
||||
import com.intellij.packaging.elements.PackagingElementResolvingContext;
|
||||
import com.intellij.packaging.impl.artifacts.ArtifactUtil;
|
||||
import com.intellij.packaging.impl.artifacts.PlainArtifactType;
|
||||
import com.intellij.packaging.ui.PackagingSourceItem;
|
||||
import org.jetbrains.android.facet.AndroidFacet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Eugene.Kudelevsky
|
||||
*/
|
||||
public class AndroidApplicationArtifactType extends ArtifactType {
|
||||
public AndroidApplicationArtifactType() {
|
||||
super("apk", "Android Application");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Icon getIcon() {
|
||||
return PlainArtifactType.ARTIFACT_ICON;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDefaultPathFor(@NotNull PackagingElementOutputKind kind) {
|
||||
return "/";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSuitableItem(@NotNull PackagingSourceItem item) {
|
||||
return !(item instanceof ModuleOutputSourceItem || item instanceof LibrarySourceItem);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CompositePackagingElement<?> createRootElement(@NotNull String artifactName) {
|
||||
return PackagingElementFactory.getInstance().createArchive(ArtifactUtil.suggestArtifactFileName(artifactName) + ".apk");
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<? extends ArtifactTemplate> getNewArtifactTemplates(@NotNull PackagingElementResolvingContext context) {
|
||||
return Collections.singletonList(new MyTemplate(context));
|
||||
}
|
||||
|
||||
private class MyTemplate extends ArtifactTemplate {
|
||||
protected PackagingElementResolvingContext myContext;
|
||||
|
||||
public MyTemplate(@NotNull PackagingElementResolvingContext context) {
|
||||
myContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPresentableName() {
|
||||
return "From module...";
|
||||
}
|
||||
|
||||
@Override
|
||||
public NewArtifactConfiguration createArtifact() {
|
||||
final List<Module> modules = new ArrayList<Module>();
|
||||
|
||||
for (Module module : myContext.getModulesProvider().getModules()) {
|
||||
final AndroidFacet facet = AndroidFacet.getInstance(module);
|
||||
|
||||
if (facet != null && !facet.getConfiguration().LIBRARY_PROJECT) {
|
||||
modules.add(module);
|
||||
}
|
||||
}
|
||||
|
||||
final AndroidFacet facet = AndroidArtifactUtil.chooseAndroidApplicationModule(myContext.getProject(), modules);
|
||||
if (facet == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final CompositePackagingElement<?> rootElement =
|
||||
AndroidApplicationArtifactType.this.createRootElement(facet.getModule().getName());
|
||||
rootElement.addFirstChild(new AndroidFinalPackageElement(myContext.getProject(), facet));
|
||||
return new NewArtifactConfiguration(rootElement, facet.getModule().getName(), AndroidApplicationArtifactType.this);
|
||||
}
|
||||
}
|
||||
}
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.jetbrains.android.compiler.artifact.AndroidArtifactPropertiesEditor">
|
||||
<grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="5" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<margin top="0" left="0" bottom="0" right="0"/>
|
||||
<constraints>
|
||||
<xy x="20" y="20" width="500" height="400"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<vspacer id="39e8a">
|
||||
<constraints>
|
||||
<grid row="4" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
</vspacer>
|
||||
<component id="8bfdb" class="javax.swing.JRadioButton" binding="myReleaseSignedRadio">
|
||||
<constraints>
|
||||
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<enabled value="true"/>
|
||||
<text value="Release &signed"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="791e9" class="javax.swing.JRadioButton" binding="myReleaseUnsignedRadio">
|
||||
<constraints>
|
||||
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text value="Release &unsigned"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="ce323" class="javax.swing.JRadioButton" binding="myDebugRadio">
|
||||
<constraints>
|
||||
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text value="&Debug"/>
|
||||
</properties>
|
||||
</component>
|
||||
<grid id="b6cd" binding="myReleaseKeyPanel" layout-manager="GridLayoutManager" row-count="5" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<margin top="0" left="0" bottom="0" right="0"/>
|
||||
<constraints>
|
||||
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="2" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<component id="587da" class="com.intellij.ui.components.JBLabel">
|
||||
<constraints>
|
||||
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<labelFor value="779"/>
|
||||
<text value="&Key store path: "/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="e6a18" class="com.intellij.ui.components.JBLabel">
|
||||
<constraints>
|
||||
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<labelFor value="8c694"/>
|
||||
<text value="Key store &password:"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="8c694" class="javax.swing.JPasswordField" binding="myKeyStorePasswordField">
|
||||
<constraints>
|
||||
<grid row="2" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
|
||||
<preferred-size width="150" height="-1"/>
|
||||
</grid>
|
||||
</constraints>
|
||||
<properties/>
|
||||
</component>
|
||||
<component id="5d3f2" class="com.intellij.ui.components.JBLabel">
|
||||
<constraints>
|
||||
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<labelFor value="9b706"/>
|
||||
<text value="K&ey alias: "/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="76382" class="com.intellij.ui.components.JBLabel">
|
||||
<constraints>
|
||||
<grid row="4" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<labelFor value="3398"/>
|
||||
<text value="Key pass&word:"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="3398" class="javax.swing.JPasswordField" binding="myKeyPasswordField">
|
||||
<constraints>
|
||||
<grid row="4" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
|
||||
<preferred-size width="150" height="-1"/>
|
||||
</grid>
|
||||
</constraints>
|
||||
<properties/>
|
||||
</component>
|
||||
<component id="9b706" class="com.intellij.openapi.ui.TextFieldWithBrowseButton" binding="myKeyAliasField">
|
||||
<constraints>
|
||||
<grid row="3" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
</component>
|
||||
<component id="779" class="javax.swing.JTextField" binding="myKeyStorePathField">
|
||||
<constraints>
|
||||
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
|
||||
<preferred-size width="150" height="-1"/>
|
||||
</grid>
|
||||
</constraints>
|
||||
<properties/>
|
||||
</component>
|
||||
<grid id="bdf4e" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<margin top="0" left="0" bottom="0" right="0"/>
|
||||
<constraints>
|
||||
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<component id="48c0d" class="javax.swing.JButton" binding="myCreateKeyStoreButton">
|
||||
<constraints>
|
||||
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text value="&Create new..."/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="a6531" class="javax.swing.JButton" binding="myLoadKeyStoreButton">
|
||||
<constraints>
|
||||
<grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text value="&Choose existing..."/>
|
||||
</properties>
|
||||
</component>
|
||||
<hspacer id="ed5f1">
|
||||
<constraints>
|
||||
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
</hspacer>
|
||||
</children>
|
||||
</grid>
|
||||
</children>
|
||||
</grid>
|
||||
</children>
|
||||
</grid>
|
||||
<buttonGroups>
|
||||
<group name="myGroup1">
|
||||
<member id="ce323"/>
|
||||
<member id="8bfdb"/>
|
||||
<member id="791e9"/>
|
||||
</group>
|
||||
</buttonGroups>
|
||||
</form>
|
||||
+260
@@ -0,0 +1,260 @@
|
||||
package org.jetbrains.android.compiler.artifact;
|
||||
|
||||
import com.android.annotations.NonNull;
|
||||
import com.intellij.CommonBundle;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.fileChooser.FileChooser;
|
||||
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
|
||||
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VfsUtilCore;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.packaging.ui.ArtifactPropertiesEditor;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.android.util.AndroidUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.io.*;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Eugene.Kudelevsky
|
||||
*/
|
||||
public class AndroidArtifactPropertiesEditor extends ArtifactPropertiesEditor {
|
||||
private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.compiler.artifact.AndroidApplicationArtifactProperties");
|
||||
|
||||
private final AndroidApplicationArtifactProperties myProperties;
|
||||
|
||||
private JPanel myPanel;
|
||||
private JRadioButton myDebugRadio;
|
||||
private JRadioButton myReleaseSignedRadio;
|
||||
private JRadioButton myReleaseUnsignedRadio;
|
||||
private JPanel myReleaseKeyPanel;
|
||||
private JPasswordField myKeyStorePasswordField;
|
||||
private JTextField myKeyStorePathField;
|
||||
private JPasswordField myKeyPasswordField;
|
||||
private TextFieldWithBrowseButton myKeyAliasField;
|
||||
private JButton myLoadKeyStoreButton;
|
||||
private JButton myCreateKeyStoreButton;
|
||||
|
||||
public AndroidArtifactPropertiesEditor(@NonNull AndroidApplicationArtifactProperties properties, @NotNull final Project project) {
|
||||
myProperties = properties;
|
||||
|
||||
final ActionListener listener = new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
UIUtil.setEnabled(myReleaseKeyPanel, myReleaseSignedRadio.isSelected(), true);
|
||||
}
|
||||
};
|
||||
myDebugRadio.addActionListener(listener);
|
||||
myReleaseUnsignedRadio.addActionListener(listener);
|
||||
myReleaseSignedRadio.addActionListener(listener);
|
||||
|
||||
myLoadKeyStoreButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
final String defaultPath = getKeyStorePath();
|
||||
final VirtualFile defaultFile = LocalFileSystem.getInstance().findFileByPath(defaultPath);
|
||||
final FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor();
|
||||
final VirtualFile file = FileChooser.chooseFile(descriptor, myPanel, project, defaultFile);
|
||||
if (file != null) {
|
||||
myKeyStorePathField.setText(FileUtil.toSystemDependentName(file.getPath()));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
myCreateKeyStoreButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
final NewKeyStoreDialog dialog = new NewKeyStoreDialog(project, myKeyStorePathField.getText());
|
||||
dialog.show();
|
||||
|
||||
if (dialog.getExitCode() == DialogWrapper.OK_EXIT_CODE) {
|
||||
myKeyStorePathField.setText(dialog.getKeyStorePath());
|
||||
myKeyStorePasswordField.setText(String.valueOf(dialog.getKeyStorePassword()));
|
||||
myKeyAliasField.setText(dialog.getKeyAlias());
|
||||
myKeyPasswordField.setText(String.valueOf(dialog.getKeyPassword()));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
myKeyAliasField.getButton().addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
final List<String> keys = loadExistingKeys();
|
||||
if (keys == null) {
|
||||
return;
|
||||
}
|
||||
final ChooseKeyDialog dialog =
|
||||
new ChooseKeyDialog(project, getKeyStorePath(), myKeyStorePasswordField.getPassword(), keys, getKeyAlias());
|
||||
dialog.show();
|
||||
|
||||
if (dialog.getExitCode() == DialogWrapper.OK_EXIT_CODE) {
|
||||
final String chosenKey = dialog.getChosenKey();
|
||||
if (chosenKey != null) {
|
||||
myKeyAliasField.setText(chosenKey);
|
||||
}
|
||||
|
||||
final char[] password = dialog.getChosenKeyPassword();
|
||||
if (password != null) {
|
||||
myKeyPasswordField.setText(String.valueOf(password));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private String getKeyStorePath() {
|
||||
return myKeyStorePathField.getText().trim();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private List<String> loadExistingKeys() {
|
||||
final String errorPrefix = "Cannot load key store: ";
|
||||
InputStream is = null;
|
||||
try {
|
||||
//noinspection IOResourceOpenedButNotSafelyClosed
|
||||
is = new FileInputStream(new File(getKeyStorePath()));
|
||||
final KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
keyStore.load(is, myKeyStorePasswordField.getPassword());
|
||||
return AndroidUtils.toList(keyStore.aliases());
|
||||
}
|
||||
catch (KeyStoreException e) {
|
||||
Messages.showErrorDialog(myPanel, errorPrefix + e.getMessage(), CommonBundle.getErrorTitle());
|
||||
return null;
|
||||
}
|
||||
catch (FileNotFoundException e) {
|
||||
Messages.showErrorDialog(myPanel, errorPrefix + e.getMessage(), CommonBundle.getErrorTitle());
|
||||
return null;
|
||||
}
|
||||
catch (CertificateException e) {
|
||||
Messages.showErrorDialog(myPanel, errorPrefix + e.getMessage(), CommonBundle.getErrorTitle());
|
||||
return null;
|
||||
}
|
||||
catch (NoSuchAlgorithmException e) {
|
||||
Messages.showErrorDialog(myPanel, errorPrefix + e.getMessage(), CommonBundle.getErrorTitle());
|
||||
return null;
|
||||
}
|
||||
catch (IOException e) {
|
||||
Messages.showErrorDialog(myPanel, errorPrefix + e.getMessage(), CommonBundle.getErrorTitle());
|
||||
return null;
|
||||
}
|
||||
finally {
|
||||
if (is != null) {
|
||||
try {
|
||||
is.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.info(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTabName() {
|
||||
return "Android";
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent createComponent() {
|
||||
return myPanel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isModified() {
|
||||
return getSigningMode() != myProperties.getSigningMode() ||
|
||||
!getKeyStoreFileUrl().equals(myProperties.getKeyStoreUrl()) ||
|
||||
!getKeyStorePassword().equals(myProperties.getPlainKeystorePassword()) ||
|
||||
!getKeyAlias().equals(myProperties.getKeyAlias()) ||
|
||||
!getKeyPassword().equals(myProperties.getPlainKeyPassword());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply() {
|
||||
myProperties.setSigningMode(getSigningMode());
|
||||
myProperties.setKeyStoreUrl(getKeyStoreFileUrl());
|
||||
myProperties.setPlainKeystorePassword(getKeyStorePassword());
|
||||
myProperties.setKeyAlias(getKeyAlias());
|
||||
myProperties.setPlainKeyPassword(getKeyPassword());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
switch (myProperties.getSigningMode()) {
|
||||
case RELEASE_UNSIGNED:
|
||||
myReleaseUnsignedRadio.setSelected(true);
|
||||
myDebugRadio.setSelected(false);
|
||||
myReleaseSignedRadio.setSelected(false);
|
||||
break;
|
||||
case DEBUG:
|
||||
myReleaseUnsignedRadio.setSelected(false);
|
||||
myDebugRadio.setSelected(true);
|
||||
myReleaseSignedRadio.setSelected(false);
|
||||
break;
|
||||
case RELEASE_SIGNED:
|
||||
myReleaseUnsignedRadio.setSelected(false);
|
||||
myDebugRadio.setSelected(false);
|
||||
myReleaseSignedRadio.setSelected(true);
|
||||
break;
|
||||
}
|
||||
final String keyStoreUrl = myProperties.getKeyStoreUrl();
|
||||
myKeyStorePathField.setText(keyStoreUrl != null ? VfsUtilCore.urlToPath(keyStoreUrl) : "");
|
||||
myKeyStorePasswordField.setText(myProperties.getPlainKeystorePassword());
|
||||
|
||||
final String keyAlias = myProperties.getKeyAlias();
|
||||
myKeyAliasField.setText(keyAlias != null ? keyAlias : "");
|
||||
myKeyPasswordField.setText(myProperties.getPlainKeyPassword());
|
||||
|
||||
UIUtil.setEnabled(myReleaseKeyPanel, myProperties.getSigningMode() == AndroidArtifactSigningMode.RELEASE_SIGNED, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disposeUIResources() {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private AndroidArtifactSigningMode getSigningMode() {
|
||||
if (myDebugRadio.isSelected()) {
|
||||
return AndroidArtifactSigningMode.DEBUG;
|
||||
}
|
||||
else if (myReleaseSignedRadio.isSelected()) {
|
||||
return AndroidArtifactSigningMode.RELEASE_SIGNED;
|
||||
}
|
||||
return AndroidArtifactSigningMode.RELEASE_UNSIGNED;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String getKeyStoreFileUrl() {
|
||||
final String path = getKeyStorePath();
|
||||
return VfsUtilCore.pathToUrl(path);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String getKeyStorePassword() {
|
||||
return String.valueOf(myKeyStorePasswordField.getPassword());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String getKeyAlias() {
|
||||
return myKeyAliasField.getText().trim();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String getKeyPassword() {
|
||||
return String.valueOf(myKeyPasswordField.getPassword());
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package org.jetbrains.android.compiler.artifact;
|
||||
|
||||
import com.intellij.packaging.artifacts.ArtifactProperties;
|
||||
import com.intellij.packaging.artifacts.ArtifactPropertiesProvider;
|
||||
import com.intellij.packaging.artifacts.ArtifactType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author Eugene.Kudelevsky
|
||||
*/
|
||||
public class AndroidArtifactPropertiesProvider extends ArtifactPropertiesProvider {
|
||||
protected AndroidArtifactPropertiesProvider() {
|
||||
super("android-properties");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailableFor(@NotNull ArtifactType type) {
|
||||
return type instanceof AndroidApplicationArtifactType;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ArtifactProperties<?> createProperties(@NotNull ArtifactType artifactType) {
|
||||
return new AndroidApplicationArtifactProperties();
|
||||
}
|
||||
|
||||
public static AndroidArtifactPropertiesProvider getInstance() {
|
||||
return EP_NAME.findExtension(AndroidArtifactPropertiesProvider.class);
|
||||
}
|
||||
}
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
package org.jetbrains.android.compiler.artifact;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.compiler.CompileContext;
|
||||
import com.intellij.openapi.compiler.CompileScope;
|
||||
import com.intellij.openapi.compiler.PackagingCompiler;
|
||||
import com.intellij.openapi.compiler.ValidityState;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author Eugene.Kudelevsky
|
||||
*/
|
||||
public class AndroidArtifactSigningCompiler implements PackagingCompiler {
|
||||
@Override
|
||||
public void processOutdatedItem(CompileContext context, String url, @Nullable ValidityState state) {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ProcessingItem[] getProcessingItems(CompileContext context) {
|
||||
return ApplicationManager.getApplication().runReadAction(new Computable<ProcessingItem[]>() {
|
||||
@Override
|
||||
public ProcessingItem[] compute() {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcessingItem[] process(CompileContext context, ProcessingItem[] items) {
|
||||
return new ProcessingItem[0]; //To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Android Artifact Signing Compiler";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validateConfiguration(CompileScope scope) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValidityState createValidityState(DataInput in) throws IOException {
|
||||
return new MyValidityState(in);
|
||||
}
|
||||
|
||||
private static class MyProcessingItem implements ProcessingItem {
|
||||
private final VirtualFile myApkFile;
|
||||
private final AndroidArtifactSigningMode mySigningMode;
|
||||
private final String myDebugKeyStorePath;
|
||||
private final MyValidityState myValidityState;
|
||||
|
||||
private MyProcessingItem(@NotNull VirtualFile apkFile,
|
||||
@NotNull AndroidArtifactSigningMode signingMode,
|
||||
@Nullable String debugKeyStorePath) {
|
||||
myApkFile = apkFile;
|
||||
mySigningMode = signingMode;
|
||||
myDebugKeyStorePath = debugKeyStorePath;
|
||||
|
||||
myValidityState = new MyValidityState(myApkFile.getModificationStamp(),
|
||||
mySigningMode.name(),
|
||||
myDebugKeyStorePath);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public VirtualFile getFile() {
|
||||
return myApkFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValidityState getValidityState() {
|
||||
return myValidityState;
|
||||
}
|
||||
}
|
||||
|
||||
private static class MyValidityState implements ValidityState {
|
||||
private final long myApkFileTimestamp;
|
||||
private final String mySigningMode;
|
||||
private final String myDebugKeyStorePath;
|
||||
|
||||
private MyValidityState(long apkFileTimestamp, @NotNull String signingMode, @NotNull String debugKeyStorePath) {
|
||||
myApkFileTimestamp = apkFileTimestamp;
|
||||
mySigningMode = signingMode;
|
||||
myDebugKeyStorePath = debugKeyStorePath;
|
||||
}
|
||||
|
||||
private MyValidityState(@NotNull DataInput in) throws IOException {
|
||||
myApkFileTimestamp = in.readLong();
|
||||
mySigningMode = in.readUTF();
|
||||
myDebugKeyStorePath = in.readUTF();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equalsTo(ValidityState otherState) {
|
||||
if (!(otherState instanceof MyValidityState)) {
|
||||
return false;
|
||||
}
|
||||
final MyValidityState state = (MyValidityState)otherState;
|
||||
return state.myApkFileTimestamp == myApkFileTimestamp &&
|
||||
state.mySigningMode.equals(mySigningMode) &&
|
||||
state.myDebugKeyStorePath.equals(myDebugKeyStorePath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(DataOutput out) throws IOException {
|
||||
out.writeLong(myApkFileTimestamp);
|
||||
out.writeUTF(mySigningMode);
|
||||
out.writeUTF(myDebugKeyStorePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package org.jetbrains.android.compiler.artifact;
|
||||
|
||||
/**
|
||||
* @author Eugene.Kudelevsky
|
||||
*/
|
||||
public enum AndroidArtifactSigningMode {
|
||||
DEBUG, RELEASE_UNSIGNED, RELEASE_SIGNED
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package org.jetbrains.android.compiler.artifact;
|
||||
|
||||
import com.intellij.CommonBundle;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ui.configuration.ChooseModulesDialog;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.packaging.artifacts.Artifact;
|
||||
import com.intellij.packaging.impl.artifacts.ArtifactUtil;
|
||||
import com.intellij.packaging.ui.ArtifactEditorContext;
|
||||
import com.intellij.util.Processor;
|
||||
import org.jetbrains.android.facet.AndroidFacet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Eugene.Kudelevsky
|
||||
*/
|
||||
public class AndroidArtifactUtil {
|
||||
private AndroidArtifactUtil() {
|
||||
}
|
||||
|
||||
public static boolean containsAndroidPackage(ArtifactEditorContext editorContext, Artifact artifact) {
|
||||
return !ArtifactUtil
|
||||
.processPackagingElements(artifact, AndroidFinalPackageElementType.getInstance(), new Processor<AndroidFinalPackageElement>() {
|
||||
public boolean process(AndroidFinalPackageElement e) {
|
||||
return false;
|
||||
}
|
||||
}, editorContext, true);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static AndroidFacet chooseAndroidApplicationModule(@NotNull Project project, @NotNull List<Module> modules) {
|
||||
final ChooseModulesDialog dialog = new ChooseModulesDialog(project, modules, "Select Module",
|
||||
"Selected Android application module will be included in the created artifact with all dependencies");
|
||||
dialog.setSingleSelectionMode();
|
||||
dialog.show();
|
||||
final List<Module> selected = dialog.getChosenElements();
|
||||
if (selected.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
assert selected.size() == 1;
|
||||
final Module module = selected.get(0);
|
||||
final String moduleName = module.getName();
|
||||
|
||||
final AndroidFacet facet = AndroidFacet.getInstance(module);
|
||||
if (facet == null) {
|
||||
final String message = "Cannot find Android facet for module " + moduleName;
|
||||
Messages.showErrorDialog(project, message, CommonBundle.getErrorTitle());
|
||||
return null;
|
||||
}
|
||||
return facet;
|
||||
}
|
||||
}
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
package org.jetbrains.android.compiler.artifact;
|
||||
|
||||
import com.intellij.compiler.ant.Generator;
|
||||
import com.intellij.facet.pointers.FacetPointer;
|
||||
import com.intellij.facet.pointers.FacetPointersManager;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vfs.JarFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.packaging.artifacts.ArtifactType;
|
||||
import com.intellij.packaging.elements.*;
|
||||
import com.intellij.packaging.impl.elements.FacetBasedPackagingElement;
|
||||
import com.intellij.packaging.impl.elements.ModuleOutputPackagingElement;
|
||||
import com.intellij.packaging.impl.ui.DelegatedPackagingElementPresentation;
|
||||
import com.intellij.packaging.ui.ArtifactEditorContext;
|
||||
import com.intellij.packaging.ui.PackagingElementPresentation;
|
||||
import com.intellij.util.xmlb.annotations.Attribute;
|
||||
import org.jetbrains.android.facet.AndroidFacet;
|
||||
import org.jetbrains.android.facet.AndroidRootUtil;
|
||||
import org.jetbrains.android.util.AndroidCommonUtils;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Eugene.Kudelevsky
|
||||
*/
|
||||
public class AndroidFinalPackageElement extends PackagingElement<AndroidFinalPackageElement.AndroidFinalPackageElementState>
|
||||
implements FacetBasedPackagingElement, ModuleOutputPackagingElement {
|
||||
|
||||
@NonNls static final String FACET_ATTRIBUTE = "facet";
|
||||
|
||||
private FacetPointer<AndroidFacet> myFacetPointer;
|
||||
private final Project myProject;
|
||||
|
||||
public AndroidFinalPackageElement(@NotNull Project project, @Nullable AndroidFacet facet) {
|
||||
super(AndroidFinalPackageElementType.getInstance());
|
||||
myProject = project;
|
||||
myFacetPointer = facet != null ? FacetPointersManager.getInstance(myProject).create(facet) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PackagingElementPresentation createPresentation(@NotNull ArtifactEditorContext context) {
|
||||
return new DelegatedPackagingElementPresentation(new AndroidFinalPackagePresentation(myFacetPointer));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String getApkPath() {
|
||||
if (myFacetPointer == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final AndroidFacet facet = myFacetPointer.getFacet();
|
||||
if (facet == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final String apkPath = AndroidRootUtil.getApkPath(facet);
|
||||
final String path = apkPath != null
|
||||
? AndroidCommonUtils.addSuffixToFileName(apkPath, AndroidCommonUtils.ANDROID_FINAL_PACKAGE_FOR_ARTIFACT_SUFFIX)
|
||||
: null;
|
||||
return path != null
|
||||
? FileUtil.toSystemIndependentName(path) + "!/"
|
||||
: null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends Generator> computeAntInstructions(@NotNull PackagingElementResolvingContext resolvingContext,
|
||||
@NotNull AntCopyInstructionCreator creator,
|
||||
@NotNull ArtifactAntGenerationContext generationContext,
|
||||
@NotNull ArtifactType artifactType) {
|
||||
final String apkPath = getApkPath();
|
||||
if (apkPath != null) {
|
||||
return Collections.singletonList(creator.createExtractedDirectoryInstruction(apkPath));
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void computeIncrementalCompilerInstructions(@NotNull IncrementalCompilerInstructionCreator creator,
|
||||
@NotNull PackagingElementResolvingContext resolvingContext,
|
||||
@NotNull ArtifactIncrementalCompilerContext compilerContext,
|
||||
@NotNull ArtifactType artifactType) {
|
||||
final String apkPath = getApkPath();
|
||||
if (apkPath != null) {
|
||||
final VirtualFile apk = JarFileSystem.getInstance().findFileByPath(apkPath);
|
||||
if (apk != null && apk.isValid() && apk.isDirectory()) {
|
||||
creator.addDirectoryCopyInstructions(apk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public AndroidFacet getFacet() {
|
||||
return myFacetPointer != null ? myFacetPointer.getFacet() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEqualTo(@NotNull PackagingElement<?> element) {
|
||||
if (!(element instanceof AndroidFinalPackageElement)) {
|
||||
return false;
|
||||
}
|
||||
final AndroidFinalPackageElement packageElement = (AndroidFinalPackageElement)element;
|
||||
|
||||
return myFacetPointer == null
|
||||
? packageElement.myFacetPointer == null
|
||||
: myFacetPointer.equals(packageElement.myFacetPointer);
|
||||
}
|
||||
|
||||
public AndroidFinalPackageElementState getState() {
|
||||
final AndroidFinalPackageElementState state = new AndroidFinalPackageElementState();
|
||||
state.myFacetPointer = myFacetPointer != null ? myFacetPointer.getId() : null;
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AndroidFacet findFacet(@NotNull PackagingElementResolvingContext context) {
|
||||
return myFacetPointer != null ? myFacetPointer.findFacet(context.getModulesProvider(), context.getFacetsProvider()) : null;
|
||||
}
|
||||
|
||||
public void loadState(AndroidFinalPackageElementState state) {
|
||||
myFacetPointer = state.myFacetPointer != null
|
||||
? FacetPointersManager.getInstance(myProject).<AndroidFacet>create(state.myFacetPointer)
|
||||
: null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModuleName() {
|
||||
return myFacetPointer != null ? myFacetPointer.getModuleName() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Module findModule(PackagingElementResolvingContext context) {
|
||||
final AndroidFacet facet = findFacet(context);
|
||||
return facet != null ? facet.getModule() : null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<VirtualFile> getSourceRoots(PackagingElementResolvingContext context) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public static class AndroidFinalPackageElementState {
|
||||
|
||||
@Attribute(FACET_ATTRIBUTE)
|
||||
public String myFacetPointer;
|
||||
}
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
package org.jetbrains.android.compiler.artifact;
|
||||
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.packaging.artifacts.Artifact;
|
||||
import com.intellij.packaging.elements.CompositePackagingElement;
|
||||
import com.intellij.packaging.elements.PackagingElement;
|
||||
import com.intellij.packaging.elements.PackagingElementType;
|
||||
import com.intellij.packaging.ui.ArtifactEditorContext;
|
||||
import org.jetbrains.android.facet.AndroidFacet;
|
||||
import org.jetbrains.android.util.AndroidUtils;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Eugene.Kudelevsky
|
||||
*/
|
||||
public class AndroidFinalPackageElementType extends PackagingElementType<AndroidFinalPackageElement> {
|
||||
@NonNls public static final String TYPE_ID = "android-final-package";
|
||||
|
||||
protected AndroidFinalPackageElementType() {
|
||||
super(TYPE_ID, "Android Final Package");
|
||||
}
|
||||
|
||||
public static AndroidFinalPackageElementType getInstance() {
|
||||
return getInstance(AndroidFinalPackageElementType.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Icon getCreateElementIcon() {
|
||||
return AndroidUtils.ANDROID_ICON;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canCreate(@NotNull ArtifactEditorContext context, @NotNull Artifact artifact) {
|
||||
return getAndroidApplicationFacets(context, context.getModulesProvider().getModules()).size() > 0 &&
|
||||
!AndroidArtifactUtil.containsAndroidPackage(context, artifact);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<AndroidFacet> getAndroidApplicationFacets(@NotNull ArtifactEditorContext context, @NotNull Module[] modules) {
|
||||
final List<AndroidFacet> result = new ArrayList<AndroidFacet>();
|
||||
for (Module module : modules) {
|
||||
for (AndroidFacet facet : context.getFacetsProvider().getFacetsByType(module, AndroidFacet.ID)) {
|
||||
if (!facet.getConfiguration().LIBRARY_PROJECT) {
|
||||
result.add(facet);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<Module> facetsToModules(@NotNull Collection<AndroidFacet> facets) {
|
||||
final List<Module> result = new ArrayList<Module>(facets.size());
|
||||
for (AndroidFacet facet : facets) {
|
||||
result.add(facet.getModule());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<? extends PackagingElement<?>> chooseAndCreate(@NotNull ArtifactEditorContext context,
|
||||
@NotNull Artifact artifact,
|
||||
@NotNull CompositePackagingElement<?> parent) {
|
||||
final List<AndroidFacet> facets = getAndroidApplicationFacets(context, context.getModulesProvider().getModules());
|
||||
final List<Module> modules = facetsToModules(facets);
|
||||
|
||||
final AndroidFacet facet = AndroidArtifactUtil.chooseAndroidApplicationModule(context.getProject(), modules);
|
||||
if (facet == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Collections.singletonList(new AndroidFinalPackageElement(context.getProject(), facet));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public AndroidFinalPackageElement createEmpty(@NotNull Project project) {
|
||||
return new AndroidFinalPackageElement(project, null);
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package org.jetbrains.android.compiler.artifact;
|
||||
|
||||
import com.intellij.facet.pointers.FacetPointer;
|
||||
import com.intellij.ide.projectView.PresentationData;
|
||||
import com.intellij.packaging.ui.SourceItemPresentation;
|
||||
import com.intellij.packaging.ui.SourceItemWeights;
|
||||
import com.intellij.ui.SimpleTextAttributes;
|
||||
import org.jetbrains.android.facet.AndroidFacet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author Eugene.Kudelevsky
|
||||
*/
|
||||
class AndroidFinalPackagePresentation extends SourceItemPresentation {
|
||||
private final FacetPointer<AndroidFacet> myFacetPointer;
|
||||
|
||||
public AndroidFinalPackagePresentation(@Nullable FacetPointer<AndroidFacet> facetPointer) {
|
||||
myFacetPointer = facetPointer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPresentableName() {
|
||||
final String moduleName = myFacetPointer != null ? myFacetPointer.getModuleName() : "<unknown>";
|
||||
return "'" + moduleName + "' Android final package";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(@NotNull PresentationData presentationData,
|
||||
SimpleTextAttributes mainAttributes,
|
||||
SimpleTextAttributes commentAttributes) {
|
||||
presentationData.setIcons(AndroidFacet.getFacetType().getIcon());
|
||||
presentationData.addText(getPresentableName(), mainAttributes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWeight() {
|
||||
return SourceItemWeights.LIBRARY_WEIGHT - 5;
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package org.jetbrains.android.compiler.artifact;
|
||||
|
||||
import com.intellij.facet.pointers.FacetPointersManager;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.roots.ui.configuration.artifacts.sourceItems.FacetBasedPackagingSourceItemsProvider;
|
||||
import com.intellij.openapi.roots.ui.configuration.artifacts.sourceItems.ModuleSourceItemGroup;
|
||||
import com.intellij.packaging.artifacts.Artifact;
|
||||
import com.intellij.packaging.ui.ArtifactEditorContext;
|
||||
import com.intellij.packaging.ui.PackagingSourceItem;
|
||||
import org.jetbrains.android.facet.AndroidFacet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Eugene.Kudelevsky
|
||||
*/
|
||||
public class AndroidSourceItemsProvider extends FacetBasedPackagingSourceItemsProvider<AndroidFacet, AndroidFinalPackageElement> {
|
||||
public AndroidSourceItemsProvider() {
|
||||
super(AndroidFacet.ID, AndroidFinalPackageElementType.getInstance());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AndroidFinalPackagePresentation createPresentation(AndroidFacet facet) {
|
||||
return new AndroidFinalPackagePresentation(FacetPointersManager.getInstance(facet.getModule().getProject()).create(facet));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AndroidFinalPackageElement createElement(ArtifactEditorContext context, AndroidFacet facet) {
|
||||
return new AndroidFinalPackageElement(context.getProject(), facet);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<? extends PackagingSourceItem> getSourceItems(@NotNull ArtifactEditorContext editorContext,
|
||||
@NotNull Artifact artifact,
|
||||
@Nullable PackagingSourceItem parent) {
|
||||
|
||||
if (parent instanceof ModuleSourceItemGroup && !AndroidArtifactUtil.containsAndroidPackage(editorContext, artifact)) {
|
||||
final Module module = ((ModuleSourceItemGroup)parent).getModule();
|
||||
final Set<AndroidFacet> facets =
|
||||
new HashSet<AndroidFacet>(editorContext.getFacetsProvider().getFacetsByType(module, AndroidFacet.ID));
|
||||
return Collections.singletonList(new FacetBasedSourceItem<AndroidFacet>(this, facets.iterator().next()));
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AndroidFacet getFacet(AndroidFinalPackageElement element) {
|
||||
return element.getFacet();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.jetbrains.android.compiler.artifact.ChooseKeyDialog">
|
||||
<grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="4" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<margin top="0" left="0" bottom="0" right="0"/>
|
||||
<constraints>
|
||||
<xy x="20" y="20" width="500" height="400"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<component id="6262e" class="com.intellij.ui.components.JBRadioButton" binding="myUseExistingKeyRadioButton">
|
||||
<constraints>
|
||||
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text value="&Use an existing key:"/>
|
||||
</properties>
|
||||
</component>
|
||||
<vspacer id="ba9f7">
|
||||
<constraints>
|
||||
<grid row="3" column="0" row-span="1" col-span="2" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
</vspacer>
|
||||
<component id="91b01" class="com.intellij.ui.components.JBRadioButton" binding="myCreateNewKeyRadioButton">
|
||||
<constraints>
|
||||
<grid row="1" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text value="Create a &new key"/>
|
||||
</properties>
|
||||
</component>
|
||||
<grid id="97118" binding="myNewKeyPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
|
||||
<constraints>
|
||||
<grid row="2" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="2" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children/>
|
||||
</grid>
|
||||
<component id="b9c2f" class="javax.swing.JComboBox" binding="myKeyCombo">
|
||||
<constraints>
|
||||
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
</component>
|
||||
</children>
|
||||
</grid>
|
||||
<buttonGroups>
|
||||
<group name="myGroup1">
|
||||
<member id="6262e"/>
|
||||
<member id="91b01"/>
|
||||
</group>
|
||||
</buttonGroups>
|
||||
<inspectionSuppressions>
|
||||
<suppress inspection="NoLabelFor" id="b9c2f"/>
|
||||
</inspectionSuppressions>
|
||||
</form>
|
||||
@@ -0,0 +1,129 @@
|
||||
package org.jetbrains.android.compiler.artifact;
|
||||
|
||||
import com.intellij.CommonBundle;
|
||||
import com.intellij.ide.wizard.CommitStepException;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.ui.CollectionComboBoxModel;
|
||||
import com.intellij.ui.components.JBRadioButton;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.android.exportSignedPackage.NewKeyForm;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Eugene.Kudelevsky
|
||||
*/
|
||||
public class ChooseKeyDialog extends DialogWrapper {
|
||||
private JPanel myNewKeyPanel;
|
||||
private JBRadioButton myCreateNewKeyRadioButton;
|
||||
private JBRadioButton myUseExistingKeyRadioButton;
|
||||
private JComboBox myKeyCombo;
|
||||
private JPanel myPanel;
|
||||
|
||||
private final NewKeyForm myNewKeyForm = new MyNewKeyForm();
|
||||
private final Project myProject;
|
||||
private final String myKeyStorePath;
|
||||
private final char[] myKeyStorePassword;
|
||||
private final List<String> myExistingKeys;
|
||||
|
||||
public ChooseKeyDialog(@NotNull Project project,
|
||||
@NotNull String keyStorePath,
|
||||
@NotNull char[] password,
|
||||
@NotNull List<String> existingKeys,
|
||||
@Nullable String keyToSelect) {
|
||||
super(project);
|
||||
myProject = project;
|
||||
myKeyStorePath = keyStorePath;
|
||||
myKeyStorePassword = password;
|
||||
myExistingKeys = existingKeys;
|
||||
myKeyCombo.setModel(new CollectionComboBoxModel(existingKeys, existingKeys.get(0)));
|
||||
|
||||
if (keyToSelect != null && existingKeys.contains(keyToSelect)) {
|
||||
myKeyCombo.setSelectedItem(keyToSelect);
|
||||
}
|
||||
myNewKeyPanel.add(myNewKeyForm.getContentPanel(), BorderLayout.CENTER);
|
||||
|
||||
final ActionListener listener = new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
UIUtil.setEnabled(myNewKeyPanel, myCreateNewKeyRadioButton.isSelected(), true);
|
||||
}
|
||||
};
|
||||
myCreateNewKeyRadioButton.addActionListener(listener);
|
||||
myUseExistingKeyRadioButton.addActionListener(listener);
|
||||
|
||||
final boolean useExisting = existingKeys.size() > 0;
|
||||
myUseExistingKeyRadioButton.setSelected(useExisting);
|
||||
myCreateNewKeyRadioButton.setSelected(!useExisting);
|
||||
UIUtil.setEnabled(myNewKeyPanel, !useExisting, true);
|
||||
|
||||
setTitle("Create New Key");
|
||||
init();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JComponent createCenterPanel() {
|
||||
return myPanel;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doOKAction() {
|
||||
if (myCreateNewKeyRadioButton.isSelected()) {
|
||||
try {
|
||||
myNewKeyForm.createKey();
|
||||
}
|
||||
catch (CommitStepException e) {
|
||||
Messages.showErrorDialog(myPanel, e.getMessage(), CommonBundle.getErrorTitle());
|
||||
return;
|
||||
}
|
||||
}
|
||||
super.doOKAction();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getChosenKey() {
|
||||
return myUseExistingKeyRadioButton.isSelected()
|
||||
? (String)myKeyCombo.getSelectedItem()
|
||||
: myNewKeyForm.getKeyAlias();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public char[] getChosenKeyPassword() {
|
||||
return myCreateNewKeyRadioButton.isSelected()
|
||||
? myNewKeyForm.getKeyPassword()
|
||||
: null;
|
||||
}
|
||||
|
||||
private class MyNewKeyForm extends NewKeyForm {
|
||||
@Override
|
||||
protected List<String> getExistingKeyAliasList() {
|
||||
return myExistingKeys;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected Project getProject() {
|
||||
return myProject;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected char[] getKeyStorePassword() {
|
||||
return myKeyStorePassword;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected String getKeyStoreLocation() {
|
||||
return myKeyStorePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.jetbrains.android.compiler.artifact.NewKeyStoreDialog">
|
||||
<grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="5" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<margin top="0" left="0" bottom="0" right="0"/>
|
||||
<constraints>
|
||||
<xy x="20" y="20" width="507" height="400"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<component id="61ec4" class="com.intellij.ui.components.JBLabel">
|
||||
<constraints>
|
||||
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<labelFor value="decbb"/>
|
||||
<text value="&Key store path:"/>
|
||||
</properties>
|
||||
</component>
|
||||
<vspacer id="a56a5">
|
||||
<constraints>
|
||||
<grid row="4" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
</vspacer>
|
||||
<component id="decbb" class="com.intellij.openapi.ui.TextFieldWithBrowseButton" binding="myKeyStorePathField">
|
||||
<constraints>
|
||||
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
</component>
|
||||
<grid id="1d308" binding="myNewKeyPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
|
||||
<constraints>
|
||||
<grid row="3" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<clientProperties>
|
||||
<BorderFactoryClass class="java.lang.String" value="com.intellij.ui.IdeBorderFactory$PlainSmallWithIndent"/>
|
||||
</clientProperties>
|
||||
<border type="etched" title="Key"/>
|
||||
<children/>
|
||||
</grid>
|
||||
<component id="f7c78" class="com.intellij.ui.components.JBLabel">
|
||||
<constraints>
|
||||
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<labelFor value="75dcc"/>
|
||||
<text value="&Password:"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="75dcc" class="javax.swing.JPasswordField" binding="myPasswordField">
|
||||
<constraints>
|
||||
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
|
||||
<preferred-size width="150" height="-1"/>
|
||||
</grid>
|
||||
</constraints>
|
||||
<properties/>
|
||||
</component>
|
||||
<component id="aa6ed" class="javax.swing.JLabel">
|
||||
<constraints>
|
||||
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<labelFor value="bffd5"/>
|
||||
<text value="Co&nfirm password:"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="bffd5" class="javax.swing.JPasswordField" binding="myConfirmedPassword">
|
||||
<constraints>
|
||||
<grid row="2" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
|
||||
<preferred-size width="150" height="-1"/>
|
||||
</grid>
|
||||
</constraints>
|
||||
<properties/>
|
||||
</component>
|
||||
</children>
|
||||
</grid>
|
||||
</form>
|
||||
@@ -0,0 +1,124 @@
|
||||
package org.jetbrains.android.compiler.artifact;
|
||||
|
||||
import com.intellij.CommonBundle;
|
||||
import com.intellij.ide.wizard.CommitStepException;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
|
||||
import org.jetbrains.android.exportSignedPackage.NewKeyForm;
|
||||
import org.jetbrains.android.util.AndroidBundle;
|
||||
import org.jetbrains.android.util.AndroidUtils;
|
||||
import org.jetbrains.android.util.SaveFileListener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Eugene.Kudelevsky
|
||||
*/
|
||||
public class NewKeyStoreDialog extends DialogWrapper {
|
||||
private JPanel myNewKeyPanel;
|
||||
private JPanel myPanel;
|
||||
private TextFieldWithBrowseButton myKeyStorePathField;
|
||||
private JPasswordField myPasswordField;
|
||||
private JPasswordField myConfirmedPassword;
|
||||
|
||||
private final NewKeyForm myNewKeyForm;
|
||||
private final Project myProject;
|
||||
|
||||
public NewKeyStoreDialog(@NotNull Project project, @NotNull String defaultKeyStorePath) {
|
||||
super(project);
|
||||
myProject = project;
|
||||
myKeyStorePathField.setText(defaultKeyStorePath);
|
||||
setTitle("Create New Key Store");
|
||||
myNewKeyForm = new MyNewKeyForm();
|
||||
myNewKeyPanel.add(myNewKeyForm.getContentPanel(), BorderLayout.CENTER);
|
||||
|
||||
myKeyStorePathField.addActionListener(new SaveFileListener(myPanel, myKeyStorePathField, AndroidBundle.message(
|
||||
"android.extract.package.choose.keystore.title")) {
|
||||
@Override
|
||||
protected String getDefaultLocation() {
|
||||
return getKeyStorePath();
|
||||
}
|
||||
});
|
||||
init();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JComponent createCenterPanel() {
|
||||
return myPanel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getPreferredFocusedComponent() {
|
||||
return myKeyStorePathField;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doOKAction() {
|
||||
if (getKeyStorePath().length() == 0) {
|
||||
Messages.showErrorDialog(myPanel, "Specify key store path", CommonBundle.getErrorTitle());
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
AndroidUtils.checkNewPassword(myPasswordField, myConfirmedPassword);
|
||||
myNewKeyForm.createKey();
|
||||
}
|
||||
catch (CommitStepException e) {
|
||||
Messages.showErrorDialog(myPanel, e.getMessage(), CommonBundle.getErrorTitle());
|
||||
return;
|
||||
}
|
||||
super.doOKAction();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getKeyStorePath() {
|
||||
return myKeyStorePathField.getText().trim();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public char[] getKeyStorePassword() {
|
||||
return myPasswordField.getPassword();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getKeyAlias() {
|
||||
return myNewKeyForm.getKeyAlias();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public char[] getKeyPassword() {
|
||||
return myNewKeyForm.getKeyPassword();
|
||||
}
|
||||
|
||||
private class MyNewKeyForm extends NewKeyForm {
|
||||
|
||||
@Override
|
||||
protected List<String> getExistingKeyAliasList() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected Project getProject() {
|
||||
return myProject;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected char[] getKeyStorePassword() {
|
||||
return NewKeyStoreDialog.this.getKeyStorePassword();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected String getKeyStoreLocation() {
|
||||
return getKeyStorePath();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.jetbrains.android.exportSignedPackage;
|
||||
|
||||
import com.android.jarutils.SignedJarBuilder;
|
||||
import com.android.sdklib.SdkConstants;
|
||||
import com.intellij.CommonBundle;
|
||||
import com.intellij.execution.ExecutionException;
|
||||
@@ -50,7 +49,6 @@ import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.ui.components.JBLabel;
|
||||
import org.jetbrains.android.compiler.AndroidCompileUtil;
|
||||
import org.jetbrains.android.compiler.AndroidPackagingCompiler;
|
||||
import org.jetbrains.android.compiler.AndroidProguardCompiler;
|
||||
import org.jetbrains.android.facet.AndroidFacet;
|
||||
import org.jetbrains.android.facet.AndroidFacetConfiguration;
|
||||
@@ -58,7 +56,6 @@ import org.jetbrains.android.facet.AndroidRootUtil;
|
||||
import org.jetbrains.android.sdk.AndroidPlatform;
|
||||
import org.jetbrains.android.util.AndroidBundle;
|
||||
import org.jetbrains.android.util.AndroidCommonUtils;
|
||||
import org.jetbrains.android.util.SafeSignedJarBuilder;
|
||||
import org.jetbrains.android.util.SaveFileListener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -68,12 +65,8 @@ import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
/**
|
||||
* @author Eugene.Kudelevsky
|
||||
@@ -110,7 +103,7 @@ class ApkStep extends ExportSignedPackageWizardStep {
|
||||
myWizard = wizard;
|
||||
myApkPathLabel.setLabelFor(myApkPathField);
|
||||
myProguardConfigFilePathLabel.setLabelFor(myProguardConfigFilePathField);
|
||||
|
||||
|
||||
myApkPathField.getButton().addActionListener(
|
||||
new SaveFileListener(myContentPanel, myApkPathField, AndroidBundle.message("android.extract.package.choose.dest.apk")) {
|
||||
@Override
|
||||
@@ -140,7 +133,7 @@ class ApkStep extends ExportSignedPackageWizardStep {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
myProguardCheckBox.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
@@ -193,7 +186,7 @@ class ApkStep extends ExportSignedPackageWizardStep {
|
||||
myIncludeSystemProguardFileCheckBox.setVisible(AndroidCommonUtils.isIncludingInProguardSupported(sdkToolsRevision));
|
||||
|
||||
final String proguardCfgPath = properties.getValue(PROGUARD_CFG_PATH_PROPERTY);
|
||||
if (proguardCfgPath != null &&
|
||||
if (proguardCfgPath != null &&
|
||||
LocalFileSystem.getInstance().refreshAndFindFileByPath(proguardCfgPath) != null) {
|
||||
myProguardConfigFilePathField.setText(FileUtil.toSystemDependentName(proguardCfgPath));
|
||||
final String includeSystemProguardFile = properties.getValue(INCLUDE_SYSTEM_PROGUARD_FILE_PROPERTY);
|
||||
@@ -274,35 +267,11 @@ class ApkStep extends ExportSignedPackageWizardStep {
|
||||
|
||||
@SuppressWarnings({"IOResourceOpenedButNotSafelyClosed"})
|
||||
private void createApk(File destFile) throws IOException, GeneralSecurityException {
|
||||
final String srcApkPath = AndroidRootUtil.getApkPath(myWizard.getFacet()) + AndroidPackagingCompiler.UNSIGNED_SUFFIX;
|
||||
final String srcApkPath = AndroidCompileUtil.getUnsignedApkPath(myWizard.getFacet());
|
||||
final File srcApk = new File(FileUtil.toSystemDependentName(srcApkPath));
|
||||
|
||||
if (myWizard.isSigned()) {
|
||||
FileOutputStream fos = new FileOutputStream(destFile);
|
||||
PrivateKey privateKey = myWizard.getPrivateKey();
|
||||
assert privateKey != null;
|
||||
X509Certificate certificate = myWizard.getCertificate();
|
||||
assert certificate != null;
|
||||
SignedJarBuilder builder = new SafeSignedJarBuilder(fos, privateKey, certificate, destFile.getPath());
|
||||
FileInputStream fis = new FileInputStream(srcApk);
|
||||
try {
|
||||
builder.writeZip(fis, null);
|
||||
builder.close();
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
fis.close();
|
||||
}
|
||||
catch (IOException ignored) {
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
fos.close();
|
||||
}
|
||||
catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
AndroidCommonUtils.signApk(srcApk, destFile, myWizard.getPrivateKey(), myWizard.getCertificate());
|
||||
}
|
||||
else {
|
||||
FileUtil.copy(srcApk, destFile);
|
||||
|
||||
-36
@@ -18,11 +18,9 @@ package org.jetbrains.android.exportSignedPackage;
|
||||
|
||||
import com.intellij.ide.wizard.CommitStepException;
|
||||
import com.intellij.ide.wizard.StepAdapter;
|
||||
import org.jetbrains.android.util.AndroidBundle;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* @author Eugene.Kudelevsky
|
||||
@@ -44,42 +42,8 @@ public abstract class ExportSignedPackageWizardStep extends StepAdapter {
|
||||
|
||||
public abstract String getHelpId();
|
||||
|
||||
protected static void checkNewPassword(JPasswordField passwordField, JPasswordField confirmedPasswordField) throws CommitStepException {
|
||||
char[] password = passwordField.getPassword();
|
||||
char[] confirmedPassword = confirmedPasswordField.getPassword();
|
||||
try {
|
||||
checkPassword(password);
|
||||
if (password.length < 6) {
|
||||
throw new CommitStepException(AndroidBundle.message("android.export.package.incorrect.password.length"));
|
||||
}
|
||||
if (!Arrays.equals(password, confirmedPassword)) {
|
||||
throw new CommitStepException(AndroidBundle.message("android.export.package.passwords.not.match.error"));
|
||||
}
|
||||
}
|
||||
finally {
|
||||
Arrays.fill(password, '\0');
|
||||
Arrays.fill(confirmedPassword, '\0');
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void commitForNext() throws CommitStepException;
|
||||
|
||||
protected static void checkPassword(char[] password) throws CommitStepException {
|
||||
if (password.length == 0) {
|
||||
throw new CommitStepException(AndroidBundle.message("android.export.package.specify.password.error"));
|
||||
}
|
||||
}
|
||||
|
||||
protected static void checkPassword(JPasswordField passwordField) throws CommitStepException {
|
||||
char[] password = passwordField.getPassword();
|
||||
try {
|
||||
checkPassword(password);
|
||||
}
|
||||
finally {
|
||||
Arrays.fill(password, '\0');
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Icon getIcon() {
|
||||
return null;
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.ui.CollectionComboBoxModel;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.android.util.AndroidBundle;
|
||||
import org.jetbrains.android.util.AndroidUtils;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
@@ -132,7 +133,7 @@ class InitialKeyStep extends ExportSignedPackageWizardStep {
|
||||
if (myAliasCombo.getSelectedItem() == null) {
|
||||
throw new CommitStepException(AndroidBundle.message("android.extract.package.select.key.alias.error"));
|
||||
}
|
||||
checkPassword(password);
|
||||
AndroidUtils.checkPassword(password);
|
||||
String alias = (String)myAliasCombo.getSelectedItem();
|
||||
loadKey(alias, password);
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import org.jetbrains.android.util.AndroidBundle;
|
||||
import org.jetbrains.android.util.AndroidUtils;
|
||||
import org.jetbrains.android.util.SaveFileListener;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -102,7 +103,7 @@ class KeystoreStep extends ExportSignedPackageWizardStep {
|
||||
private KeyStore checkExistingKeystoreOptionsAndCreateKeystore(File keystoreFile) throws CommitStepException {
|
||||
char[] password = myKeystorePasswordField.getPassword();
|
||||
FileInputStream fis = null;
|
||||
checkPassword(password);
|
||||
AndroidUtils.checkPassword(password);
|
||||
if (!keystoreFile.isFile()) {
|
||||
throw new CommitStepException(AndroidBundle.message("android.cannot.find.file.error", keystoreFile.getPath()));
|
||||
}
|
||||
@@ -129,7 +130,7 @@ class KeystoreStep extends ExportSignedPackageWizardStep {
|
||||
}
|
||||
|
||||
private void checkNewKeystoreOptions(File keystoreFile) throws CommitStepException {
|
||||
checkNewPassword(myKeystorePasswordField, myConfirmKeystorePasswordField);
|
||||
AndroidUtils.checkNewPassword(myKeystorePasswordField, myConfirmKeystorePasswordField);
|
||||
|
||||
if (keystoreFile.exists()) {
|
||||
throw new CommitStepException(AndroidBundle.message(
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.jetbrains.android.exportSignedPackage.NewKeyStep">
|
||||
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.jetbrains.android.exportSignedPackage.NewKeyForm">
|
||||
<grid id="27dc6" binding="myContentPanel" layout-manager="GridLayoutManager" row-count="5" column-count="4" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<margin top="0" left="0" bottom="0" right="0"/>
|
||||
<constraints>
|
||||
@@ -71,7 +71,7 @@
|
||||
<grid row="3" column="0" row-span="1" col-span="4" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="etched"/>
|
||||
<border type="etched" title="Certificate"/>
|
||||
<children>
|
||||
<component id="7b983" class="javax.swing.JLabel">
|
||||
<constraints>
|
||||
@@ -0,0 +1,270 @@
|
||||
package org.jetbrains.android.exportSignedPackage;
|
||||
|
||||
import com.android.jarutils.DebugKeyProvider;
|
||||
import com.android.jarutils.KeystoreHelper;
|
||||
import com.intellij.ide.util.PropertiesComponent;
|
||||
import com.intellij.ide.wizard.CommitStepException;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import org.jetbrains.android.util.AndroidBundle;
|
||||
import org.jetbrains.android.util.AndroidUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Eugene.Kudelevsky
|
||||
*/
|
||||
public abstract class NewKeyForm {
|
||||
private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.exportSignedPackage.NewKeyForm");
|
||||
|
||||
private JPanel myContentPanel;
|
||||
private JTextField myAliasField;
|
||||
private JPasswordField myKeyPasswordField;
|
||||
private JPasswordField myConfirmKeyPasswordField;
|
||||
private JSpinner myValiditySpinner;
|
||||
private JTextField myFirstAndLastNameField;
|
||||
private JTextField myOrganizationUnitField;
|
||||
private JTextField myCityField;
|
||||
private JTextField myStateOrProvinceField;
|
||||
private JTextField myCountryCodeField;
|
||||
private JPanel myCertificatePanel;
|
||||
private JTextField myOrganizationField;
|
||||
|
||||
private KeyStore myKeyStore;
|
||||
private PrivateKey myPrivateKey;
|
||||
private X509Certificate myCertificate;
|
||||
|
||||
public NewKeyForm() {
|
||||
myValiditySpinner.setModel(new SpinnerNumberModel(25, 1, 1000, 1));
|
||||
}
|
||||
|
||||
private int getValidity() {
|
||||
SpinnerNumberModel model = (SpinnerNumberModel)myValiditySpinner.getModel();
|
||||
return model.getNumber().intValue();
|
||||
}
|
||||
|
||||
public void init() {
|
||||
myAliasField.setText(generateAlias());
|
||||
}
|
||||
|
||||
public JPanel getContentPanel() {
|
||||
return myContentPanel;
|
||||
}
|
||||
|
||||
private boolean findNonEmptyCertificateField() {
|
||||
for (Component component : myCertificatePanel.getComponents()) {
|
||||
if (component instanceof JTextField) {
|
||||
if (((JTextField)component).getText().trim().length() > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void createKey() throws CommitStepException {
|
||||
if (getKeyAlias().length() == 0) {
|
||||
throw new CommitStepException(AndroidBundle.message("android.export.package.specify.key.alias.error"));
|
||||
}
|
||||
AndroidUtils.checkNewPassword(myKeyPasswordField, myConfirmKeyPasswordField);
|
||||
if (!findNonEmptyCertificateField()) {
|
||||
throw new CommitStepException(AndroidBundle.message("android.export.package.specify.certificate.field.error"));
|
||||
}
|
||||
doCreateKey();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String generateAlias() {
|
||||
List<String> aliasList = getExistingKeyAliasList();
|
||||
String prefix = "key";
|
||||
if (aliasList == null) {
|
||||
return prefix + '0';
|
||||
}
|
||||
Set<String> aliasSet = new HashSet<String>();
|
||||
for (String alias : aliasList) {
|
||||
aliasSet.add(alias.toLowerCase());
|
||||
}
|
||||
for (int i = 0; ; i++) {
|
||||
String alias = prefix + i;
|
||||
if (!aliasSet.contains(alias)) {
|
||||
return alias;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected abstract List<String> getExistingKeyAliasList();
|
||||
|
||||
private static void buildDName(StringBuilder builder, String prefix, JTextField textField) {
|
||||
if (textField != null) {
|
||||
String value = textField.getText().trim();
|
||||
if (value.length() > 0) {
|
||||
if (builder.length() > 0) {
|
||||
builder.append(",");
|
||||
}
|
||||
builder.append(prefix);
|
||||
builder.append('=');
|
||||
builder.append(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getDName() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
buildDName(builder, "CN", myFirstAndLastNameField);
|
||||
buildDName(builder, "OU", myOrganizationUnitField);
|
||||
buildDName(builder, "O", myOrganizationField);
|
||||
buildDName(builder, "L", myCityField);
|
||||
buildDName(builder, "ST", myStateOrProvinceField);
|
||||
buildDName(builder, "C", myCountryCodeField);
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void doCreateKey() throws CommitStepException {
|
||||
String keystoreLocation = getKeyStoreLocation();
|
||||
char[] keystorePassword = getKeyStorePassword();
|
||||
char[] keyPassword = getKeyPassword();
|
||||
String keyAlias = getKeyAlias();
|
||||
String dname = getDName();
|
||||
assert dname != null;
|
||||
boolean createdStore = false;
|
||||
final StringBuilder errorBuilder = new StringBuilder();
|
||||
final StringBuilder outBuilder = new StringBuilder();
|
||||
try {
|
||||
createdStore = KeystoreHelper
|
||||
.createNewStore(keystoreLocation, null, new String(keystorePassword), keyAlias, new String(keyPassword), dname, getValidity(),
|
||||
new DebugKeyProvider.IKeyGenOutput() {
|
||||
public void err(String message) {
|
||||
errorBuilder.append(message).append('\n');
|
||||
LOG.info("Error: " + message);
|
||||
}
|
||||
|
||||
public void out(String message) {
|
||||
outBuilder.append(message).append('\n');
|
||||
LOG.info(message);
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception e) {
|
||||
LOG.info(e);
|
||||
errorBuilder.append(e.getMessage()).append('\n');
|
||||
}
|
||||
normalizeBuilder(errorBuilder);
|
||||
normalizeBuilder(outBuilder);
|
||||
try {
|
||||
if (createdStore) {
|
||||
if (errorBuilder.length() > 0) {
|
||||
String prefix = AndroidBundle.message("android.create.new.key.error.prefix");
|
||||
Messages.showErrorDialog(myContentPanel, prefix + '\n' + errorBuilder.toString());
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (errorBuilder.length() > 0) {
|
||||
throw new CommitStepException(errorBuilder.toString());
|
||||
}
|
||||
if (outBuilder.length() > 0) {
|
||||
throw new CommitStepException(outBuilder.toString());
|
||||
}
|
||||
throw new CommitStepException(AndroidBundle.message("android.cannot.create.new.key.error"));
|
||||
}
|
||||
PropertiesComponent.getInstance(getProject()).setValue(KeystoreStep.DEFAULT_KEYSTORE_LOCATION, keystoreLocation);
|
||||
loadKeystoreAndKey(keystoreLocation, keystorePassword, keyAlias, keyPassword);
|
||||
}
|
||||
finally {
|
||||
Arrays.fill(keystorePassword, '\0');
|
||||
Arrays.fill(keyPassword, '\0');
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public char[] getKeyPassword() {
|
||||
return myKeyPasswordField.getPassword();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getKeyAlias() {
|
||||
return myAliasField.getText().trim();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected abstract Project getProject();
|
||||
|
||||
@NotNull
|
||||
protected abstract char[] getKeyStorePassword();
|
||||
|
||||
@NotNull
|
||||
protected abstract String getKeyStoreLocation();
|
||||
|
||||
private void loadKeystoreAndKey(String keystoreLocation, char[] keystorePassword, String keyAlias, char[] keyPassword)
|
||||
throws CommitStepException {
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
fis = new FileInputStream(new File(keystoreLocation));
|
||||
keyStore.load(fis, keystorePassword);
|
||||
myKeyStore = keyStore;
|
||||
KeyStore.PrivateKeyEntry entry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(keyAlias, new KeyStore.PasswordProtection(keyPassword));
|
||||
if (entry == null) {
|
||||
throw new CommitStepException(AndroidBundle.message("android.extract.package.cannot.find.key.error", keyAlias));
|
||||
}
|
||||
PrivateKey privateKey = entry.getPrivateKey();
|
||||
Certificate certificate = entry.getCertificate();
|
||||
if (privateKey == null || certificate == null) {
|
||||
throw new CommitStepException(AndroidBundle.message("android.extract.package.cannot.find.key.error", keyAlias));
|
||||
}
|
||||
PropertiesComponent.getInstance(getProject()).setValue(InitialKeyStep.DEFAULT_KEY_ALIAS, keyAlias);
|
||||
myPrivateKey = privateKey;
|
||||
myCertificate = (X509Certificate)certificate;
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new CommitStepException("Error: " + e.getMessage());
|
||||
}
|
||||
finally {
|
||||
if (fis != null) {
|
||||
try {
|
||||
fis.close();
|
||||
}
|
||||
catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void normalizeBuilder(StringBuilder builder) {
|
||||
if (builder.length() > 0) {
|
||||
builder.deleteCharAt(builder.length() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public KeyStore getKeyStore() {
|
||||
return myKeyStore;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PrivateKey getPrivateKey() {
|
||||
return myPrivateKey;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public X509Certificate getCertificate() {
|
||||
return myCertificate;
|
||||
}
|
||||
}
|
||||
@@ -16,79 +16,37 @@
|
||||
|
||||
package org.jetbrains.android.exportSignedPackage;
|
||||
|
||||
import com.android.jarutils.DebugKeyProvider;
|
||||
import com.android.jarutils.KeystoreHelper;
|
||||
import com.intellij.ide.util.PropertiesComponent;
|
||||
import com.intellij.ide.wizard.CommitStepException;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import org.jetbrains.android.util.AndroidBundle;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Eugene.Kudelevsky
|
||||
*/
|
||||
public class NewKeyStep extends ExportSignedPackageWizardStep {
|
||||
private static final Logger LOG = Logger.getInstance("#com.jetbrains.android.exportSignedPackage.NewKeyStep");
|
||||
|
||||
private JPanel myContentPanel;
|
||||
private JTextField myAliasField;
|
||||
private JPasswordField myKeyPasswordField;
|
||||
private JPasswordField myConfirmKeyPasswordField;
|
||||
private JSpinner myValiditySpinner;
|
||||
private JTextField myFirstAndLastNameField;
|
||||
private JTextField myOrganizationUnitField;
|
||||
private JTextField myCityField;
|
||||
private JTextField myStateOrProvinceField;
|
||||
private JTextField myCountryCodeField;
|
||||
private JPanel myCertificatePanel;
|
||||
private JTextField myOrganizationField;
|
||||
|
||||
private final ExportSignedPackageWizard myWizard;
|
||||
private final NewKeyForm myNewKeyForm;
|
||||
|
||||
public NewKeyStep(ExportSignedPackageWizard wizard) {
|
||||
myWizard = wizard;
|
||||
myValiditySpinner.setModel(new SpinnerNumberModel(25, 1, 1000, 1));
|
||||
}
|
||||
|
||||
private int getValidity() {
|
||||
SpinnerNumberModel model = (SpinnerNumberModel)myValiditySpinner.getModel();
|
||||
return model.getNumber().intValue();
|
||||
myNewKeyForm = new MyNewKeyForm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void _init() {
|
||||
myAliasField.setText(generateAlias());
|
||||
myNewKeyForm.init();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getComponent() {
|
||||
return myContentPanel;
|
||||
}
|
||||
|
||||
private boolean findNonEmptyCertificateField() {
|
||||
for (Component component : myCertificatePanel.getComponents()) {
|
||||
if (component instanceof JTextField) {
|
||||
if (((JTextField)component).getText().trim().length() > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return myNewKeyForm.getContentPanel();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -98,157 +56,42 @@ public class NewKeyStep extends ExportSignedPackageWizardStep {
|
||||
|
||||
@Override
|
||||
protected void commitForNext() throws CommitStepException {
|
||||
if (myAliasField.getText().trim().length() == 0) {
|
||||
throw new CommitStepException(AndroidBundle.message("android.export.package.specify.key.alias.error"));
|
||||
}
|
||||
checkNewPassword(myKeyPasswordField, myConfirmKeyPasswordField);
|
||||
if (!findNonEmptyCertificateField()) {
|
||||
throw new CommitStepException(AndroidBundle.message("android.export.package.specify.certificate.field.error"));
|
||||
}
|
||||
createKey();
|
||||
myNewKeyForm.createKey();
|
||||
final KeyStore keyStore = myNewKeyForm.getKeyStore();
|
||||
final PrivateKey privateKey = myNewKeyForm.getPrivateKey();
|
||||
final X509Certificate certificate = myNewKeyForm.getCertificate();
|
||||
assert keyStore != null && privateKey != null && certificate != null;
|
||||
myWizard.setKeystore(keyStore);
|
||||
myWizard.setPrivateKey(privateKey);
|
||||
myWizard.setCertificate(certificate);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String generateAlias() {
|
||||
List<String> aliasList = myWizard.getKeyAliasList();
|
||||
String prefix = "key";
|
||||
if (aliasList == null) {
|
||||
return prefix + '0';
|
||||
private class MyNewKeyForm extends NewKeyForm {
|
||||
@Override
|
||||
protected List<String> getExistingKeyAliasList() {
|
||||
return myWizard.getKeyAliasList();
|
||||
}
|
||||
Set<String> aliasSet = new HashSet<String>();
|
||||
for (String alias : aliasList) {
|
||||
aliasSet.add(alias.toLowerCase());
|
||||
}
|
||||
for (int i = 0; ; i++) {
|
||||
String alias = prefix + i;
|
||||
if (!aliasSet.contains(alias)) {
|
||||
return alias;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void buildDName(StringBuilder builder, String prefix, JTextField textField) {
|
||||
if (textField != null) {
|
||||
String value = textField.getText().trim();
|
||||
if (value.length() > 0) {
|
||||
if (builder.length() > 0) {
|
||||
builder.append(",");
|
||||
}
|
||||
builder.append(prefix);
|
||||
builder.append('=');
|
||||
builder.append(value);
|
||||
}
|
||||
@NotNull
|
||||
@Override
|
||||
protected String getKeyStoreLocation() {
|
||||
final String location = myWizard.getKeystoreLocation();
|
||||
assert location != null;
|
||||
return location;
|
||||
}
|
||||
}
|
||||
|
||||
private String getDName() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
buildDName(builder, "CN", myFirstAndLastNameField);
|
||||
buildDName(builder, "OU", myOrganizationUnitField);
|
||||
buildDName(builder, "O", myOrganizationField);
|
||||
buildDName(builder, "L", myCityField);
|
||||
buildDName(builder, "ST", myStateOrProvinceField);
|
||||
buildDName(builder, "C", myCountryCodeField);
|
||||
return builder.toString();
|
||||
}
|
||||
@NotNull
|
||||
@Override
|
||||
protected char[] getKeyStorePassword() {
|
||||
final char[] password = myWizard.getKeystorePassword();
|
||||
assert password != null;
|
||||
return password;
|
||||
}
|
||||
|
||||
private void createKey() throws CommitStepException {
|
||||
String keystoreLocation = myWizard.getKeystoreLocation();
|
||||
assert keystoreLocation != null;
|
||||
char[] keystorePassword = myWizard.getKeystorePassword();
|
||||
assert keystorePassword != null;
|
||||
char[] keyPassword = myKeyPasswordField.getPassword();
|
||||
String keyAlias = myAliasField.getText().trim();
|
||||
String dname = getDName();
|
||||
assert dname != null;
|
||||
boolean createdStore = false;
|
||||
final StringBuilder errorBuilder = new StringBuilder();
|
||||
final StringBuilder outBuilder = new StringBuilder();
|
||||
try {
|
||||
createdStore = KeystoreHelper
|
||||
.createNewStore(keystoreLocation, null, new String(keystorePassword), keyAlias, new String(keyPassword), dname, getValidity(),
|
||||
new DebugKeyProvider.IKeyGenOutput() {
|
||||
public void err(String message) {
|
||||
errorBuilder.append(message).append('\n');
|
||||
LOG.info("Error: " + message);
|
||||
}
|
||||
|
||||
public void out(String message) {
|
||||
outBuilder.append(message).append('\n');
|
||||
LOG.info(message);
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception e) {
|
||||
LOG.info(e);
|
||||
errorBuilder.append(e.getMessage()).append('\n');
|
||||
}
|
||||
normalizeBuilder(errorBuilder);
|
||||
normalizeBuilder(outBuilder);
|
||||
try {
|
||||
if (createdStore) {
|
||||
if (errorBuilder.length() > 0) {
|
||||
String prefix = AndroidBundle.message("android.create.new.key.error.prefix");
|
||||
Messages.showErrorDialog(myContentPanel, prefix + '\n' + errorBuilder.toString());
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (errorBuilder.length() > 0) {
|
||||
throw new CommitStepException(errorBuilder.toString());
|
||||
}
|
||||
if (outBuilder.length() > 0) {
|
||||
throw new CommitStepException(outBuilder.toString());
|
||||
}
|
||||
throw new CommitStepException(AndroidBundle.message("android.cannot.create.new.key.error"));
|
||||
}
|
||||
PropertiesComponent.getInstance(myWizard.getProject()).setValue(KeystoreStep.DEFAULT_KEYSTORE_LOCATION, keystoreLocation);
|
||||
loadKeystoreAndKey(keystoreLocation, keystorePassword, keyAlias, keyPassword);
|
||||
}
|
||||
finally {
|
||||
Arrays.fill(keystorePassword, '\0');
|
||||
Arrays.fill(keyPassword, '\0');
|
||||
}
|
||||
}
|
||||
|
||||
private void loadKeystoreAndKey(String keystoreLocation, char[] keystorePassword, String keyAlias, char[] keyPassword)
|
||||
throws CommitStepException {
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
fis = new FileInputStream(new File(keystoreLocation));
|
||||
keyStore.load(fis, keystorePassword);
|
||||
myWizard.setKeystore(keyStore);
|
||||
KeyStore.PrivateKeyEntry entry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(keyAlias, new KeyStore.PasswordProtection(keyPassword));
|
||||
if (entry == null) {
|
||||
throw new CommitStepException(AndroidBundle.message("android.extract.package.cannot.find.key.error", keyAlias));
|
||||
}
|
||||
PrivateKey privateKey = entry.getPrivateKey();
|
||||
Certificate certificate = entry.getCertificate();
|
||||
if (privateKey == null || certificate == null) {
|
||||
throw new CommitStepException(AndroidBundle.message("android.extract.package.cannot.find.key.error", keyAlias));
|
||||
}
|
||||
PropertiesComponent.getInstance(myWizard.getProject()).setValue(InitialKeyStep.DEFAULT_KEY_ALIAS, keyAlias);
|
||||
myWizard.setPrivateKey(privateKey);
|
||||
myWizard.setCertificate((X509Certificate)certificate);
|
||||
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new CommitStepException("Error: " + e.getMessage());
|
||||
}
|
||||
finally {
|
||||
if (fis != null) {
|
||||
try {
|
||||
fis.close();
|
||||
}
|
||||
catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void normalizeBuilder(StringBuilder builder) {
|
||||
if (builder.length() > 0) {
|
||||
builder.deleteCharAt(builder.length() - 1);
|
||||
@NotNull
|
||||
@Override
|
||||
protected Project getProject() {
|
||||
return myWizard.getProject();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ import com.intellij.facet.FacetManager;
|
||||
import com.intellij.facet.ModifiableFacetModel;
|
||||
import com.intellij.facet.ProjectFacetManager;
|
||||
import com.intellij.ide.util.DefaultPsiElementCellRenderer;
|
||||
import com.intellij.ide.wizard.CommitStepException;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.module.Module;
|
||||
@@ -85,10 +86,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author yole, coyote
|
||||
@@ -714,4 +712,47 @@ public class AndroidUtils {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void checkNewPassword(JPasswordField passwordField, JPasswordField confirmedPasswordField) throws CommitStepException {
|
||||
char[] password = passwordField.getPassword();
|
||||
char[] confirmedPassword = confirmedPasswordField.getPassword();
|
||||
try {
|
||||
checkPassword(password);
|
||||
if (password.length < 6) {
|
||||
throw new CommitStepException(AndroidBundle.message("android.export.package.incorrect.password.length"));
|
||||
}
|
||||
if (!Arrays.equals(password, confirmedPassword)) {
|
||||
throw new CommitStepException(AndroidBundle.message("android.export.package.passwords.not.match.error"));
|
||||
}
|
||||
}
|
||||
finally {
|
||||
Arrays.fill(password, '\0');
|
||||
Arrays.fill(confirmedPassword, '\0');
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkPassword(char[] password) throws CommitStepException {
|
||||
if (password.length == 0) {
|
||||
throw new CommitStepException(AndroidBundle.message("android.export.package.specify.password.error"));
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkPassword(JPasswordField passwordField) throws CommitStepException {
|
||||
char[] password = passwordField.getPassword();
|
||||
try {
|
||||
checkPassword(password);
|
||||
}
|
||||
finally {
|
||||
Arrays.fill(password, '\0');
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static <T> List<T> toList(@NotNull Enumeration<T> enumeration) {
|
||||
final List<T> result = new ArrayList<T>();
|
||||
while (enumeration.hasMoreElements()) {
|
||||
result.add(enumeration.nextElement());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user