mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
gui-tests-framework: GUI tests framework is forked from Android GUI tests framework; excision of android and gradle dependencies; addition dependencies to community-tests module and main_idea_tests
This commit is contained in:
Generated
+11
@@ -0,0 +1,11 @@
|
||||
<component name="libraryTable">
|
||||
<library name="jsr305-1.3.9">
|
||||
<CLASSES>
|
||||
<root url="jar://$PROJECT_DIR$/plugins/gradle/lib/jsr305-1.3.9.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES>
|
||||
<root url="jar://$PROJECT_DIR$/plugins/gradle/lib/jsr305-1.3.9.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</component>
|
||||
@@ -7,5 +7,47 @@
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="1.8" jdkType="JavaSDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="module-library" scope="TEST">
|
||||
<library name="JUnit4">
|
||||
<CLASSES>
|
||||
<root url="jar://$APPLICATION_HOME_DIR$/lib/junit-4.12.jar!/" />
|
||||
<root url="jar://$APPLICATION_HOME_DIR$/lib/hamcrest-core-1.3.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</orderEntry>
|
||||
<orderEntry type="module" module-name="core-api" scope="TEST" />
|
||||
<orderEntry type="library" name="fest" level="project" />
|
||||
<orderEntry type="module" module-name="platform-impl" scope="TEST" />
|
||||
<orderEntry type="module" module-name="testFramework" scope="TEST" />
|
||||
<orderEntry type="module-library">
|
||||
<library>
|
||||
<CLASSES>
|
||||
<root url="jar://$MODULE_DIR$/../plugins/gradle/lib/guava-jdk5-17.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</orderEntry>
|
||||
<orderEntry type="module" module-name="java-psi-api" scope="TEST" />
|
||||
<orderEntry type="module" module-name="bootstrap" scope="TEST" />
|
||||
<orderEntry type="module" module-name="external-system-rt" scope="TEST" />
|
||||
<orderEntry type="module" module-name="xdebugger-impl" scope="TEST" />
|
||||
<orderEntry type="module" module-name="external-system-impl" scope="TEST" />
|
||||
<orderEntry type="module" module-name="openapi" scope="TEST" />
|
||||
<orderEntry type="module" module-name="lint-api" scope="TEST" />
|
||||
<orderEntry type="module" module-name="testRunner" scope="TEST" />
|
||||
<orderEntry type="module-library">
|
||||
<library>
|
||||
<CLASSES>
|
||||
<root url="jar://$MODULE_DIR$/../plugins/gradle/lib/jsr305-1.3.9.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES>
|
||||
<root url="jar://$MODULE_DIR$/../plugins/gradle/lib/jsr305-1.3.9.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</orderEntry>
|
||||
</component>
|
||||
</module>
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright 2000-2016 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.tests.gui;
|
||||
|
||||
import com.intellij.tests.gui.fixtures.newProjectWizard.NewProjectWizardFixture;
|
||||
import com.intellij.tests.gui.framework.GuiTestCase;
|
||||
import com.intellij.tests.gui.framework.IdeGuiTest;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
/**
|
||||
* Created by karashevich on 27/05/16.
|
||||
*/
|
||||
public class NewProjectTest extends GuiTestCase {
|
||||
|
||||
@Test @IdeGuiTest
|
||||
public void testNewProject() throws IOException, InterruptedException {
|
||||
String myName = "TestProject";
|
||||
String myDomain = "com.test";
|
||||
String myPkg = "com.android.test.app";
|
||||
|
||||
|
||||
findWelcomeFrame().createNewProject();
|
||||
NewProjectWizardFixture newProjectWizard = findNewProjectWizard();
|
||||
newProjectWizard.selectProjectType("Java");
|
||||
//newProjectWizard.
|
||||
|
||||
Thread.sleep(10000);
|
||||
|
||||
//newProjectWizard.clickNext()
|
||||
//
|
||||
//newProjectWizard.getConfigureFormFactorStep().selectMinimumSdkApi(MOBILE, myMinSdk);
|
||||
//newProjectWizard.clickNext();
|
||||
|
||||
// Skip "Add Activity" step
|
||||
newProjectWizard.clickNext();
|
||||
|
||||
|
||||
//newProjectWizard.getChooseOptionsForNewFileStep().enterActivityName(myActivity);ё
|
||||
//newProjectWizard.clickFinish();
|
||||
|
||||
}
|
||||
|
||||
@NotNull
|
||||
NewProjectDescriptor newProject(@NotNull String name) {
|
||||
return new NewProjectDescriptor(name);
|
||||
}
|
||||
|
||||
private class NewProjectDescriptor {
|
||||
private String myActivity = "MainActivity";
|
||||
private String myPkg = "com.android.test.app";
|
||||
private String myMinSdk = "19";
|
||||
private String myName = "TestProject";
|
||||
private String myDomain = "com.android";
|
||||
private boolean myWaitForSync = true;
|
||||
|
||||
|
||||
private NewProjectDescriptor(@NotNull String name) {
|
||||
withName(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a custom package to use in the new project
|
||||
*/
|
||||
NewProjectDescriptor withPackageName(@NotNull String pkg) {
|
||||
myPkg = pkg;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a new project name to use for the new project
|
||||
*/
|
||||
NewProjectDescriptor withName(@NotNull String name) {
|
||||
myName = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a custom activity name to use in the new project
|
||||
*/
|
||||
NewProjectDescriptor withActivity(@NotNull String activity) {
|
||||
myActivity = activity;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a custom minimum SDK version to use in the new project
|
||||
*/
|
||||
NewProjectDescriptor withMinSdk(@NotNull String minSdk) {
|
||||
myMinSdk = minSdk;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a custom company domain to enter in the new project wizard
|
||||
*/
|
||||
NewProjectDescriptor withCompanyDomain(@NotNull String domain) {
|
||||
myDomain = domain;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Picks brief names in order to make the test execute faster (less slow typing in name text fields)
|
||||
*/
|
||||
NewProjectDescriptor withBriefNames() {
|
||||
withActivity("A").withCompanyDomain("C").withName("P").withPackageName("a.b");
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Turns off the automatic wait-for-sync that normally happens on {@link #create} */
|
||||
NewProjectDescriptor withoutSync() {
|
||||
myWaitForSync = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a project fixture for this description
|
||||
*/
|
||||
void create() {
|
||||
findWelcomeFrame().createNewProject();
|
||||
|
||||
NewProjectWizardFixture newProjectWizard = findNewProjectWizard();
|
||||
|
||||
newProjectWizard.clickNext();
|
||||
|
||||
newProjectWizard.clickNext();
|
||||
|
||||
// Skip "Add Activity" step
|
||||
newProjectWizard.clickNext();
|
||||
|
||||
newProjectWizard.getChooseOptionsForNewFileStep().enterActivityName(myActivity);
|
||||
newProjectWizard.clickFinish();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.driver;
|
||||
|
||||
import com.intellij.ui.SearchTextField;
|
||||
import org.fest.swing.annotation.RunsInEDT;
|
||||
import org.fest.swing.core.Robot;
|
||||
import org.fest.swing.driver.JComponentDriver;
|
||||
import org.fest.swing.driver.TextDisplayDriver;
|
||||
import org.fest.swing.edt.GuiQuery;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.fest.assertions.Assertions.assertThat;
|
||||
import static org.fest.swing.edt.GuiActionRunner.execute;
|
||||
|
||||
public class SearchTextFieldDriver extends JComponentDriver implements TextDisplayDriver<SearchTextField> {
|
||||
public SearchTextFieldDriver(@NotNull Robot robot) {
|
||||
super(robot);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RunsInEDT
|
||||
public void requireText(@NotNull SearchTextField component, String expected) {
|
||||
assertThat(textOf(component)).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RunsInEDT
|
||||
public void requireText(@NotNull SearchTextField component, final @NotNull Pattern pattern) {
|
||||
assertThat(textOf(component)).matches(pattern.pattern());
|
||||
}
|
||||
|
||||
@Override
|
||||
@RunsInEDT
|
||||
@Nullable
|
||||
public String textOf(final @NotNull SearchTextField component) {
|
||||
return execute(new GuiQuery<String>() {
|
||||
@Override
|
||||
protected
|
||||
@Nullable
|
||||
String executeInEDT() {
|
||||
return component.getText();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@RunsInEDT
|
||||
public void enterText(@NotNull SearchTextField textBox, @NotNull String text) {
|
||||
focusAndWaitForFocusGain(textBox);
|
||||
robot.enterText(text);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.fixtures;
|
||||
|
||||
import com.intellij.openapi.actionSystem.ActionManager;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.impl.ActionButton;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import org.fest.swing.core.GenericTypeMatcher;
|
||||
import org.fest.swing.core.Robot;
|
||||
import org.fest.swing.edt.GuiQuery;
|
||||
import org.fest.swing.exception.ComponentLookupException;
|
||||
import org.fest.swing.timing.Condition;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.Collection;
|
||||
|
||||
import static com.intellij.tests.gui.framework.GuiTests.LONG_TIMEOUT;
|
||||
import static com.intellij.tests.gui.framework.GuiTests.SHORT_TIMEOUT;
|
||||
import static com.intellij.util.containers.ContainerUtil.getFirstItem;
|
||||
import static org.fest.swing.edt.GuiActionRunner.execute;
|
||||
import static org.fest.swing.timing.Pause.pause;
|
||||
|
||||
public class ActionButtonFixture extends JComponentFixture<ActionButtonFixture, ActionButton> {
|
||||
|
||||
@NotNull
|
||||
public static ActionButtonFixture findByActionId(@NotNull final String actionId,
|
||||
@NotNull final Robot robot,
|
||||
@NotNull final Container container) {
|
||||
final Ref<ActionButton> actionButtonRef = new Ref<ActionButton>();
|
||||
pause(new Condition("Find ActionButton with ID '" + actionId + "'") {
|
||||
@Override
|
||||
public boolean test() {
|
||||
Collection<ActionButton> found = robot.finder().findAll(container, new GenericTypeMatcher<ActionButton>(ActionButton.class) {
|
||||
@Override
|
||||
protected boolean isMatching(@NotNull ActionButton button) {
|
||||
if (button.isVisible()) {
|
||||
AnAction action = button.getAction();
|
||||
if (action != null) {
|
||||
String id = ActionManager.getInstance().getId(action);
|
||||
return actionId.equals(id);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
if (found.size() == 1) {
|
||||
actionButtonRef.set(getFirstItem(found));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}, SHORT_TIMEOUT);
|
||||
|
||||
ActionButton button = actionButtonRef.get();
|
||||
if (button == null) {
|
||||
throw new ComponentLookupException("Failed to find ActionButton with ID '" + actionId + "'");
|
||||
}
|
||||
return new ActionButtonFixture(robot, button);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ActionButtonFixture waitUntilEnabledAndShowing() {
|
||||
pause(new Condition("wait for action to be enabled and showing") {
|
||||
@Override
|
||||
public boolean test() {
|
||||
//noinspection ConstantConditions
|
||||
return execute(new GuiQuery<Boolean>() {
|
||||
@Nullable
|
||||
@Override
|
||||
protected Boolean executeInEDT() throws Throwable {
|
||||
ActionButton target = target();
|
||||
if (target.getAction().getTemplatePresentation().isEnabledAndVisible()) {
|
||||
return target.isShowing() && target.isVisible() && target.isEnabled();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}, LONG_TIMEOUT);
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static ActionButtonFixture findByText(@NotNull final String text, @NotNull Robot robot, @NotNull Container container) {
|
||||
final ActionButton button = robot.finder().find(container, new GenericTypeMatcher<ActionButton>(ActionButton.class) {
|
||||
@Override
|
||||
protected boolean isMatching(@NotNull ActionButton button) {
|
||||
AnAction action = button.getAction();
|
||||
return text.equals(action.getTemplatePresentation().getText());
|
||||
}
|
||||
});
|
||||
return new ActionButtonFixture(robot, button);
|
||||
}
|
||||
|
||||
private ActionButtonFixture(@NotNull Robot robot, @NotNull ActionButton target) {
|
||||
super(ActionButtonFixture.class, robot, target);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.fixtures;
|
||||
|
||||
import org.fest.swing.core.Robot;
|
||||
import com.intellij.openapi.actionSystem.ActionManager;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.ui.components.labels.ActionLink;
|
||||
import org.fest.swing.core.GenericTypeMatcher;
|
||||
import org.fest.swing.exception.ComponentLookupException;
|
||||
import org.fest.swing.timing.Condition;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.Collection;
|
||||
|
||||
import static com.intellij.tests.gui.framework.GuiTests.SHORT_TIMEOUT;
|
||||
import static com.intellij.util.containers.ContainerUtil.getFirstItem;
|
||||
import static org.fest.swing.timing.Pause.pause;
|
||||
|
||||
public class ActionLinkFixture extends JComponentFixture<ActionLinkFixture, ActionLink> {
|
||||
@NotNull
|
||||
public static ActionLinkFixture findByActionId(@NotNull final String actionId,
|
||||
@NotNull final Robot robot,
|
||||
@NotNull final Container container) {
|
||||
final Ref<ActionLink> actionLinkRef = new Ref<ActionLink>();
|
||||
pause(new Condition("Find ActionLink with ID '" + actionId + "'") {
|
||||
@Override
|
||||
public boolean test() {
|
||||
Collection<ActionLink> found = robot.finder().findAll(container, new GenericTypeMatcher<ActionLink>(ActionLink.class) {
|
||||
@Override
|
||||
protected boolean isMatching(@NotNull ActionLink actionLink) {
|
||||
if (actionLink.isVisible()) {
|
||||
AnAction action = actionLink.getAction();
|
||||
String id = ActionManager.getInstance().getId(action);
|
||||
return actionId.equals(id);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
if (found.size() == 1) {
|
||||
actionLinkRef.set(getFirstItem(found));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}, SHORT_TIMEOUT);
|
||||
|
||||
ActionLink actionLink = actionLinkRef.get();
|
||||
if (actionLink == null) {
|
||||
throw new ComponentLookupException("Failed to find ActionLink with ID '" + actionId + "'");
|
||||
}
|
||||
return new ActionLinkFixture(robot, actionLink);
|
||||
}
|
||||
|
||||
private ActionLinkFixture(@NotNull Robot robot, @NotNull ActionLink target) {
|
||||
super(ActionLinkFixture.class, robot, target);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.fixtures;
|
||||
|
||||
import com.intellij.openapi.actionSystem.ex.ComboBoxAction;
|
||||
import com.intellij.ui.JBListWithHintProvider;
|
||||
import com.intellij.ui.popup.PopupFactoryImpl;
|
||||
import com.intellij.ui.popup.list.ListPopupModel;
|
||||
import org.fest.swing.core.GenericTypeMatcher;
|
||||
import org.fest.swing.core.Robot;
|
||||
import org.fest.swing.edt.GuiQuery;
|
||||
import org.fest.swing.edt.GuiTask;
|
||||
import org.fest.swing.fixture.JButtonFixture;
|
||||
import org.fest.swing.timing.Condition;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
import static com.intellij.tests.gui.framework.GuiTests.SHORT_TIMEOUT;
|
||||
import static org.fest.swing.edt.GuiActionRunner.execute;
|
||||
import static org.fest.swing.timing.Pause.pause;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class ComboBoxActionFixture {
|
||||
@NotNull private Robot myRobot;
|
||||
@NotNull private JButton myTarget;
|
||||
private static final Class<?> ourComboBoxButtonClass;
|
||||
static {
|
||||
Class<?> temp = null;
|
||||
try {
|
||||
temp = ComboBoxActionFixture.class.getClassLoader().loadClass(ComboBoxAction.class.getCanonicalName() + "$ComboBoxButton");
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
ourComboBoxButtonClass = temp;
|
||||
}
|
||||
|
||||
public static ComboBoxActionFixture findComboBox(@NotNull Robot robot, @NotNull Container root) {
|
||||
JButton comboBoxButton = robot.finder().find(root, new GenericTypeMatcher<JButton>(JButton.class) {
|
||||
@Override
|
||||
protected boolean isMatching(@NotNull JButton component) {
|
||||
return ourComboBoxButtonClass.isInstance(component);
|
||||
}
|
||||
});
|
||||
return new ComboBoxActionFixture(robot, comboBoxButton);
|
||||
}
|
||||
|
||||
public static ComboBoxActionFixture findComboBoxByText(@NotNull Robot robot, @NotNull Container root, @NotNull final String text) {
|
||||
JButton comboBoxButton = robot.finder().find(root, new GenericTypeMatcher<JButton>(JButton.class) {
|
||||
@Override
|
||||
protected boolean isMatching(@NotNull JButton component) {
|
||||
return ourComboBoxButtonClass.isInstance(component) && component.getText().equals(text);
|
||||
}
|
||||
});
|
||||
return new ComboBoxActionFixture(robot, comboBoxButton);
|
||||
}
|
||||
|
||||
public ComboBoxActionFixture(@NotNull Robot robot, @NotNull JButton target) {
|
||||
myRobot = robot;
|
||||
myTarget = target;
|
||||
}
|
||||
|
||||
public void selectItem(@NotNull String itemName) {
|
||||
click();
|
||||
selectItemByText(getPopupList(), itemName);
|
||||
}
|
||||
|
||||
public String getSelectedItemText() {
|
||||
return execute(new GuiQuery<String>() {
|
||||
@Nullable
|
||||
@Override
|
||||
protected String executeInEDT() throws Throwable {
|
||||
return myTarget.getText();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void click() {
|
||||
final JButtonFixture comboBoxButtonFixture = new JButtonFixture(myRobot, myTarget);
|
||||
pause(new Condition("Wait until comboBoxButton is enabled") {
|
||||
@Override
|
||||
public boolean test() {
|
||||
//noinspection ConstantConditions
|
||||
return execute(new GuiQuery<Boolean>() {
|
||||
@Override
|
||||
protected Boolean executeInEDT() throws Throwable {
|
||||
return comboBoxButtonFixture.target().isEnabled();
|
||||
}
|
||||
});
|
||||
}
|
||||
}, SHORT_TIMEOUT);
|
||||
comboBoxButtonFixture.click();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private JList getPopupList() {
|
||||
return myRobot.finder().findByType(JBListWithHintProvider.class);
|
||||
}
|
||||
|
||||
private static void selectItemByText(@NotNull final JList list, @NotNull final String text) {
|
||||
pause(new Condition("Wait until the list is populated.") {
|
||||
@Override
|
||||
public boolean test() {
|
||||
ListPopupModel popupModel = (ListPopupModel)list.getModel();
|
||||
for (int i = 0; i < popupModel.getSize(); ++i) {
|
||||
PopupFactoryImpl.ActionItem actionItem = (PopupFactoryImpl.ActionItem)popupModel.get(i);
|
||||
assertNotNull(actionItem);
|
||||
if (text.equals(actionItem.getText())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}, SHORT_TIMEOUT);
|
||||
|
||||
final Integer appIndex = execute(new GuiQuery<Integer>() {
|
||||
@Override
|
||||
protected Integer executeInEDT() throws Throwable {
|
||||
ListPopupModel popupModel = (ListPopupModel)list.getModel();
|
||||
for (int i = 0; i < popupModel.getSize(); ++i) {
|
||||
PopupFactoryImpl.ActionItem actionItem = (PopupFactoryImpl.ActionItem)popupModel.get(i);
|
||||
assertNotNull(actionItem);
|
||||
if (text.equals(actionItem.getText())) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
});
|
||||
//noinspection ConstantConditions
|
||||
assertTrue(appIndex >= 0);
|
||||
|
||||
execute(new GuiTask() {
|
||||
@Override
|
||||
protected void executeInEDT() throws Throwable {
|
||||
list.setSelectedIndex(appIndex);
|
||||
}
|
||||
});
|
||||
assertEquals(text, ((PopupFactoryImpl.ActionItem)list.getSelectedValue()).getText());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2000-2016 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.tests.gui.fixtures;
|
||||
|
||||
import org.fest.swing.core.Robot;
|
||||
import org.fest.swing.driver.ComponentDriver;
|
||||
import org.fest.swing.fixture.AbstractComponentFixture;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
|
||||
/**
|
||||
* Created by karashevich on 30/05/16.
|
||||
*/
|
||||
public class ComponentFixture<S, C extends Component> extends AbstractComponentFixture<S, C, ComponentDriver> {
|
||||
public ComponentFixture(@NotNull Class<S> selfType, @NotNull Robot robot, @NotNull Class<? extends C> type) {
|
||||
super(selfType, robot, type);
|
||||
}
|
||||
|
||||
public ComponentFixture(@NotNull Class<S> selfType, @NotNull Robot robot, @Nullable String name, @NotNull Class<? extends C> type) {
|
||||
super(selfType, robot, name, type);
|
||||
}
|
||||
|
||||
public ComponentFixture(@NotNull Class<S> selfType, @NotNull Robot robot, @NotNull C target) {
|
||||
super(selfType, robot, target);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
protected ComponentDriver createDriver(@NotNull Robot robot) {
|
||||
return new ComponentDriver(robot);
|
||||
}
|
||||
}
|
||||
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.fixtures;
|
||||
|
||||
import org.fest.swing.core.Robot;
|
||||
import org.fest.swing.core.matcher.DialogMatcher;
|
||||
import org.fest.swing.fixture.DialogFixture;
|
||||
import org.fest.swing.fixture.JTableCellFixture;
|
||||
import org.fest.swing.fixture.JTableFixture;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import static com.intellij.tests.gui.framework.GuiTests.SHORT_TIMEOUT;
|
||||
import static com.intellij.tests.gui.framework.GuiTests.findAndClickOkButton;
|
||||
import static org.fest.swing.core.matcher.DialogMatcher.withTitle;
|
||||
import static org.fest.swing.data.TableCell.row;
|
||||
import static org.fest.swing.finder.WindowFinder.findDialog;
|
||||
|
||||
public class ConfigureProjectSubsetDialogFixture {
|
||||
@NotNull private DialogFixture myDialog;
|
||||
@NotNull private final JTableFixture myModulesTable;
|
||||
|
||||
@NotNull
|
||||
public static ConfigureProjectSubsetDialogFixture find(@NotNull Robot robot) {
|
||||
DialogMatcher matcher = withTitle("Select Modules to Include in Project Subset").andShowing();
|
||||
DialogFixture dialog = findDialog(matcher).withTimeout(SHORT_TIMEOUT.duration()).using(robot);
|
||||
return new ConfigureProjectSubsetDialogFixture(dialog);
|
||||
}
|
||||
|
||||
private ConfigureProjectSubsetDialogFixture(@NotNull DialogFixture dialog) {
|
||||
myDialog = dialog;
|
||||
Robot robot = dialog.robot();
|
||||
myModulesTable = new JTableFixture(robot, robot.finder().findByType(dialog.target(), JTable.class, true));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ConfigureProjectSubsetDialogFixture selectModule(@NotNull String moduleName, boolean selected) {
|
||||
JTableCellFixture cell = myModulesTable.cell(moduleName);
|
||||
myModulesTable.enterValue(row(cell.row()).column(0), String.valueOf(selected));
|
||||
return this;
|
||||
}
|
||||
|
||||
public void clickOk() {
|
||||
findAndClickOkButton(myDialog);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.fixtures;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class DebugToolWindowFixture extends ExecutionToolWindowFixture {
|
||||
public DebugToolWindowFixture(@NotNull IdeFrameFixture frameFixture) {
|
||||
super("Debug", frameFixture);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.fixtures;
|
||||
|
||||
import com.intellij.ui.EditorNotificationPanel;
|
||||
import com.intellij.ui.HyperlinkLabel;
|
||||
import org.fest.reflect.core.Reflection;
|
||||
import org.fest.swing.core.GenericTypeMatcher;
|
||||
import org.fest.swing.core.Robot;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class EditorNotificationPanelFixture extends JComponentFixture<EditorNotificationPanelFixture, EditorNotificationPanel> {
|
||||
public EditorNotificationPanelFixture(@NotNull Robot robot, @NotNull EditorNotificationPanel target) {
|
||||
super(EditorNotificationPanelFixture.class, robot, target);
|
||||
}
|
||||
|
||||
public void performAction(@NotNull final String label) {
|
||||
HyperlinkLabel link = robot().finder().find(target(), new GenericTypeMatcher<HyperlinkLabel>(HyperlinkLabel.class) {
|
||||
@Override
|
||||
protected boolean isMatching(@NotNull HyperlinkLabel hyperlinkLabel) {
|
||||
// IntelliJ's HyperLinkLabel class does not expose the getText method (it is package private)
|
||||
return hyperlinkLabel.isShowing() &&
|
||||
label.equals(Reflection.method("getText").withReturnType(String.class).in(hyperlinkLabel).invoke());
|
||||
}
|
||||
});
|
||||
driver().click(link);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.fixtures;
|
||||
|
||||
import com.intellij.execution.impl.ConsoleViewImpl;
|
||||
import com.intellij.execution.testframework.TestTreeView;
|
||||
import com.intellij.execution.ui.layout.impl.GridImpl;
|
||||
import com.intellij.execution.ui.layout.impl.JBRunnerTabs;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.impl.ActionButton;
|
||||
import com.intellij.openapi.actionSystem.impl.ActionToolbarImpl;
|
||||
import com.intellij.openapi.ui.ThreeComponentsSplitter;
|
||||
import com.intellij.ui.content.Content;
|
||||
import com.intellij.ui.tabs.impl.JBTabsImpl;
|
||||
import com.intellij.ui.tabs.impl.TabLabel;
|
||||
import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree;
|
||||
import com.intellij.xdebugger.impl.ui.tree.nodes.XDebuggerTreeNode;
|
||||
import org.fest.swing.core.GenericTypeMatcher;
|
||||
import org.fest.swing.core.Robot;
|
||||
import org.fest.swing.edt.GuiActionRunner;
|
||||
import org.fest.swing.edt.GuiTask;
|
||||
import org.fest.swing.exception.ComponentLookupException;
|
||||
import org.fest.swing.timing.Condition;
|
||||
import org.fest.swing.timing.Pause;
|
||||
import org.fest.swing.timing.Timeout;
|
||||
import org.fest.swing.util.TextMatcher;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.tests.gui.framework.GuiTests.waitUntilFound;
|
||||
import static com.intellij.util.ui.UIUtil.findComponentOfType;
|
||||
import static com.intellij.util.ui.UIUtil.findComponentsOfType;
|
||||
import static junit.framework.Assert.assertNotNull;
|
||||
import static org.fest.reflect.core.Reflection.method;
|
||||
import static org.fest.swing.timing.Pause.pause;
|
||||
|
||||
public class ExecutionToolWindowFixture extends ToolWindowFixture {
|
||||
public static class ContentFixture {
|
||||
@NotNull private final ExecutionToolWindowFixture myParentToolWindow;
|
||||
@NotNull private final Robot myRobot;
|
||||
@NotNull private final Content myContent;
|
||||
|
||||
private ContentFixture(@NotNull ExecutionToolWindowFixture parentToolWindow, @NotNull Robot robot, @NotNull Content content) {
|
||||
myParentToolWindow = parentToolWindow;
|
||||
myRobot = robot;
|
||||
myContent = content;
|
||||
}
|
||||
|
||||
public void waitForOutput(@NotNull final TextMatcher matcher, @NotNull Timeout timeout) {
|
||||
pause(new Condition("LogCat tool window output check for package name.") {
|
||||
@Override
|
||||
public boolean test() {
|
||||
return outputMatches(matcher);
|
||||
}
|
||||
}, timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits until it grabs the console window and then returns true if its text matches that of {@code matcher}.
|
||||
* Note: The caller should wrap it in something like a org.fest.swing.timing.Pause.pause to make sure they don't hang forever if the
|
||||
* console view cannot be found for some reason.
|
||||
*/
|
||||
public boolean outputMatches(@NotNull TextMatcher matcher) {
|
||||
ConsoleViewImpl consoleView;
|
||||
while ((consoleView = findConsoleView()) == null || consoleView.getEditor() == null) {
|
||||
// If our handle has been replaced, find it again.
|
||||
JComponent consoleComponent = getTabComponent("Console");
|
||||
myRobot.click(consoleComponent);
|
||||
}
|
||||
return matcher.isMatching(consoleView.getEditor().getDocument().getText());
|
||||
}
|
||||
|
||||
// Returns the console or null if it is not found.
|
||||
@Nullable
|
||||
private ConsoleViewImpl findConsoleView() {
|
||||
try {
|
||||
return myRobot.finder().findByType(myParentToolWindow.myToolWindow.getComponent(), ConsoleViewImpl.class, false);
|
||||
} catch (ComponentLookupException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public JComponent getTabComponent(@NotNull final String tabName) {
|
||||
return getTabContent(myParentToolWindow.myToolWindow.getComponent(), JBRunnerTabs.class, GridImpl.class, tabName);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public UnitTestTreeFixture getUnitTestTree() {
|
||||
return new UnitTestTreeFixture(this, myRobot.finder().findByType(myContent.getComponent(), TestTreeView.class));
|
||||
}
|
||||
|
||||
// Returns the root of the debugger tree or null.
|
||||
@Nullable
|
||||
public XDebuggerTreeNode getDebuggerTreeRoot() {
|
||||
try {
|
||||
JComponent debuggerComponent = getTabComponent("Debugger");
|
||||
if (debuggerComponent != null) {
|
||||
myRobot.click(debuggerComponent);
|
||||
}
|
||||
ThreeComponentsSplitter threeComponentsSplitter =
|
||||
myRobot.finder().findByType(debuggerComponent, ThreeComponentsSplitter.class, false);
|
||||
JComponent innerComponent = threeComponentsSplitter.getInnerComponent();
|
||||
assertNotNull(innerComponent);
|
||||
return myRobot.finder().findByType(innerComponent, XDebuggerTree.class, false).getRoot();
|
||||
} catch (ComponentLookupException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void clickDebuggerTreeRoot() {
|
||||
try {
|
||||
JComponent debuggerComponent = getTabComponent("Debugger");
|
||||
myRobot.click(debuggerComponent);
|
||||
} catch (ComponentLookupException e) { }
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private JComponent getTabContent(@NotNull final JComponent root,
|
||||
final Class<? extends JBTabsImpl> parentComponentType,
|
||||
@NotNull final Class<? extends JComponent> tabContentType,
|
||||
@NotNull final String tabName) {
|
||||
myParentToolWindow.activate();
|
||||
myParentToolWindow.waitUntilIsVisible();
|
||||
|
||||
TabLabel tabLabel;
|
||||
if (parentComponentType == null) {
|
||||
tabLabel = waitUntilFound(myRobot, new GenericTypeMatcher<TabLabel>(TabLabel.class) {
|
||||
@Override
|
||||
protected boolean isMatching(@NotNull TabLabel component) {
|
||||
return component.toString().equals(tabName);
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
final JComponent parent = myRobot.finder().findByType(root, parentComponentType, false);
|
||||
tabLabel = waitUntilFound(myRobot, parent, new GenericTypeMatcher<TabLabel>(TabLabel.class) {
|
||||
@Override
|
||||
protected boolean isMatching(@NotNull TabLabel component) {
|
||||
return component.getParent() == parent && component.toString().equals(tabName);
|
||||
}
|
||||
});
|
||||
}
|
||||
myRobot.click(tabLabel);
|
||||
return myRobot.finder().findByType(tabContentType);
|
||||
}
|
||||
|
||||
public boolean isExecutionInProgress() {
|
||||
// Consider that execution is in progress if 'stop' toolbar button is enabled.
|
||||
for (ActionButton button : getToolbarButtons()) {
|
||||
if ("com.intellij.execution.actions.StopAction".equals(button.getAction().getClass().getCanonicalName())) {
|
||||
//noinspection ConstantConditions
|
||||
return method("isButtonEnabled").withReturnType(boolean.class).in(button).invoke();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void rerun() {
|
||||
for (ActionButton button : getToolbarButtons()) {
|
||||
if ("com.intellij.execution.runners.FakeRerunAction".equals(button.getAction().getClass().getCanonicalName())) {
|
||||
myRobot.click(button);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Could not find the Re-run button.");
|
||||
}
|
||||
|
||||
public void rerunFailed() {
|
||||
for (ActionButton button : getToolbarButtons()) {
|
||||
if ("com.intellij.execution.junit2.ui.actions.RerunFailedTestsAction".equals(button.getAction().getClass().getCanonicalName())) {
|
||||
myRobot.click(button);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Could not find the Re-run failed tests button.");
|
||||
}
|
||||
|
||||
public void waitForExecutionToFinish(@NotNull Timeout timeout) {
|
||||
Pause.pause(new Condition("Wait for execution to finish") {
|
||||
@Override
|
||||
public boolean test() {
|
||||
return !isExecutionInProgress();
|
||||
}
|
||||
}, timeout);
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
public boolean stop() {
|
||||
for (final ActionButton button : getToolbarButtons()) {
|
||||
final AnAction action = button.getAction();
|
||||
if (action != null && action.getClass().getName().equals("com.intellij.execution.actions.StopAction")) {
|
||||
//noinspection ConstantConditions
|
||||
boolean enabled = method("isButtonEnabled").withReturnType(boolean.class).in(button).invoke();
|
||||
if (enabled) {
|
||||
GuiActionRunner.execute(new GuiTask() {
|
||||
@Override
|
||||
protected void executeInEDT() throws Throwable {
|
||||
button.click();
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private List<ActionButton> getToolbarButtons() {
|
||||
ActionToolbarImpl toolbar = findComponentOfType(myContent.getComponent(), ActionToolbarImpl.class);
|
||||
assert toolbar != null;
|
||||
return findComponentsOfType(toolbar, ActionButton.class);
|
||||
}
|
||||
} // End class ContentFixture
|
||||
|
||||
protected ExecutionToolWindowFixture(@NotNull String toolWindowId, @NotNull IdeFrameFixture ideFrame) {
|
||||
super(toolWindowId, ideFrame.getProject(), ideFrame.robot());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ContentFixture findContent(@NotNull String tabName) {
|
||||
Content content = getContent(tabName);
|
||||
assertNotNull(content);
|
||||
return new ContentFixture(this, myRobot, content);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ContentFixture findContent(@NotNull TextMatcher tabNameMatcher) {
|
||||
Content content = getContent(tabNameMatcher);
|
||||
assertNotNull(content);
|
||||
return new ContentFixture(this, myRobot, content);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.fixtures;
|
||||
|
||||
import com.intellij.openapi.fileChooser.ex.FileChooserDialogImpl;
|
||||
import com.intellij.openapi.fileChooser.ex.FileSystemTreeImpl;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import org.fest.swing.core.GenericTypeMatcher;
|
||||
import org.fest.swing.core.Robot;
|
||||
import org.fest.swing.edt.GuiTask;
|
||||
import org.fest.swing.timing.Condition;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static com.intellij.tests.gui.framework.GuiTests.SHORT_TIMEOUT;
|
||||
import static com.intellij.tests.gui.framework.GuiTests.findAndClickOkButton;
|
||||
import static org.fest.reflect.core.Reflection.field;
|
||||
import static org.fest.swing.edt.GuiActionRunner.execute;
|
||||
import static org.fest.swing.timing.Pause.pause;
|
||||
import static org.fest.util.Strings.quote;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
public class FileChooserDialogFixture extends IdeaDialogFixture<FileChooserDialogImpl> {
|
||||
@NotNull
|
||||
public static FileChooserDialogFixture findOpenProjectDialog(@NotNull Robot robot) {
|
||||
return findDialog(robot, new GenericTypeMatcher<JDialog>(JDialog.class) {
|
||||
@Override
|
||||
protected boolean isMatching(@NotNull JDialog dialog) {
|
||||
return dialog.isShowing() && "Open File or Project".equals(dialog.getTitle());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static FileChooserDialogFixture findImportProjectDialog(@NotNull Robot robot) {
|
||||
return findDialog(robot, new GenericTypeMatcher<JDialog>(JDialog.class) {
|
||||
@Override
|
||||
protected boolean isMatching(@NotNull JDialog dialog) {
|
||||
String title = dialog.getTitle();
|
||||
return dialog.isShowing() && title != null && title.startsWith("Select") && title.endsWith("Project to Import");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static FileChooserDialogFixture findDialog(@NotNull Robot robot, @NotNull final GenericTypeMatcher<JDialog> matcher) {
|
||||
return new FileChooserDialogFixture(robot, find(robot, FileChooserDialogImpl.class, matcher));
|
||||
}
|
||||
|
||||
private FileChooserDialogFixture(@NotNull Robot robot, @NotNull DialogAndWrapper<FileChooserDialogImpl> dialogAndWrapper) {
|
||||
super(robot, dialogAndWrapper);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public FileChooserDialogFixture select(@NotNull final VirtualFile file) {
|
||||
sleepWithTimeBomb();
|
||||
final FileSystemTreeImpl fileSystemTree = field("myFileSystemTree").ofType(FileSystemTreeImpl.class)
|
||||
.in(getDialogWrapper())
|
||||
.get();
|
||||
assertNotNull(fileSystemTree);
|
||||
final AtomicBoolean fileSelected = new AtomicBoolean();
|
||||
execute(new GuiTask() {
|
||||
@Override
|
||||
protected void executeInEDT() throws Throwable {
|
||||
fileSystemTree.select(file, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
fileSelected.set(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
pause(new Condition("File " + quote(file.getPath()) + " is selected") {
|
||||
@Override
|
||||
public boolean test() {
|
||||
return fileSelected.get();
|
||||
}
|
||||
}, SHORT_TIMEOUT);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private void sleepWithTimeBomb() {
|
||||
//TODO: WTF?!!
|
||||
assert System.currentTimeMillis() < 1452600000000L; // 2016-01-12 12:00
|
||||
try {
|
||||
Thread.sleep(5000);
|
||||
}
|
||||
catch (InterruptedException e) {}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public FileChooserDialogFixture clickOk() {
|
||||
findAndClickOkButton(this);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.fixtures;
|
||||
|
||||
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerEx;
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
|
||||
import com.intellij.lang.annotation.HighlightSeverity;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.fileEditor.FileEditor;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiManager;
|
||||
import com.intellij.util.CommonProcessors;
|
||||
import org.fest.swing.edt.GuiQuery;
|
||||
import org.fest.swing.timing.Condition;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.io.File;
|
||||
import java.util.Collection;
|
||||
|
||||
import static com.intellij.openapi.vfs.VfsUtilCore.virtualToIoFile;
|
||||
import static com.intellij.tests.gui.framework.GuiTests.SHORT_TIMEOUT;
|
||||
import static junit.framework.Assert.assertNotNull;
|
||||
import static org.fest.assertions.Assertions.assertThat;
|
||||
import static org.fest.reflect.core.Reflection.method;
|
||||
import static org.fest.swing.edt.GuiActionRunner.execute;
|
||||
import static org.fest.swing.timing.Pause.pause;
|
||||
import static org.fest.util.Strings.quote;
|
||||
|
||||
public class FileFixture {
|
||||
@NotNull private final Project myProject;
|
||||
@NotNull private final File myPath;
|
||||
@NotNull private final VirtualFile myVirtualFile;
|
||||
|
||||
public FileFixture(@NotNull Project project, @NotNull VirtualFile file) {
|
||||
myProject = project;
|
||||
myPath = virtualToIoFile(file);
|
||||
myVirtualFile = file;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public FileFixture requireOpenAndSelected() {
|
||||
requireVirtualFile();
|
||||
pause(new Condition("File " + quote(myPath.getPath()) + " to be opened") {
|
||||
@Override
|
||||
public boolean test() {
|
||||
//noinspection ConstantConditions
|
||||
return execute(new GuiQuery<Boolean>() {
|
||||
@Override
|
||||
protected Boolean executeInEDT() throws Throwable {
|
||||
return isOpenAndSelected();
|
||||
}
|
||||
});
|
||||
}
|
||||
}, SHORT_TIMEOUT);
|
||||
return this;
|
||||
}
|
||||
|
||||
private boolean isOpenAndSelected() {
|
||||
FileEditorManager editorManager = FileEditorManager.getInstance(myProject);
|
||||
FileEditor selectedEditor = editorManager.getSelectedEditor(myVirtualFile);
|
||||
if (selectedEditor != null) {
|
||||
JComponent component = selectedEditor.getComponent();
|
||||
if (component.isVisible() && component.isShowing()) {
|
||||
Document document = FileDocumentManager.getInstance().getDocument(myVirtualFile);
|
||||
if (document != null) {
|
||||
PsiFile psiFile = PsiDocumentManager.getInstance(myProject).getPsiFile(document);
|
||||
if (psiFile != null) {
|
||||
DaemonCodeAnalyzerEx codeAnalyzer = DaemonCodeAnalyzerEx.getInstanceEx(myProject);
|
||||
//noinspection ConstantConditions
|
||||
boolean isRunning = method("isRunning").withReturnType(boolean.class).in(codeAnalyzer).invoke();
|
||||
return !isRunning;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public FileFixture waitUntilErrorAnalysisFinishes() {
|
||||
pause(new Condition("error analysis finishes") {
|
||||
@Override
|
||||
public boolean test() {
|
||||
//noinspection ConstantConditions
|
||||
return execute(new GuiQuery<Boolean>() {
|
||||
@Override
|
||||
protected Boolean executeInEDT() throws Throwable {
|
||||
return DaemonCodeAnalyzerEx.getInstanceEx(myProject).isErrorAnalyzingFinished(getPsiFile());
|
||||
}
|
||||
});
|
||||
}
|
||||
}, SHORT_TIMEOUT);
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public FileFixture requireCodeAnalysisHighlightCount(@NotNull HighlightSeverity severity, int expected) {
|
||||
Collection<HighlightInfo> highlightInfos = getHighlightInfos(severity);
|
||||
assertThat(highlightInfos).hasSize(expected);
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Collection<HighlightInfo> getHighlightInfos(@NotNull final HighlightSeverity severity) {
|
||||
waitUntilErrorAnalysisFinishes();
|
||||
|
||||
final Document document = getNotNullDocument();
|
||||
Collection<HighlightInfo> highlightInfos = execute(new GuiQuery<Collection<HighlightInfo>>() {
|
||||
@Override
|
||||
protected Collection<HighlightInfo> executeInEDT() throws Throwable {
|
||||
CommonProcessors.CollectProcessor<HighlightInfo> processor = new CommonProcessors.CollectProcessor<HighlightInfo>();
|
||||
DaemonCodeAnalyzerEx.processHighlights(document, myProject, severity, 0, document.getTextLength(), processor);
|
||||
return processor.getResults();
|
||||
}
|
||||
});
|
||||
assert highlightInfos != null;
|
||||
return highlightInfos;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private PsiFile getPsiFile() {
|
||||
final PsiFile psiFile = execute(new GuiQuery<PsiFile>() {
|
||||
@Override
|
||||
protected PsiFile executeInEDT() throws Throwable {
|
||||
return PsiManager.getInstance(myProject).findFile(myVirtualFile);
|
||||
}
|
||||
});
|
||||
assertNotNull("No Psi file found for path " + quote(myVirtualFile.getPath()), psiFile);
|
||||
return psiFile;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public FileFixture waitForCodeAnalysisHighlightCount(@NotNull final HighlightSeverity severity, final int expected) {
|
||||
final Document document = getNotNullDocument();
|
||||
pause(new Condition("Waiting for code analysis " + severity + " count to reach " + expected) {
|
||||
@Override
|
||||
public boolean test() {
|
||||
Collection<HighlightInfo> highlightInfos = execute(new GuiQuery<Collection<HighlightInfo>>() {
|
||||
@Override
|
||||
protected Collection<HighlightInfo> executeInEDT() throws Throwable {
|
||||
CommonProcessors.CollectProcessor<HighlightInfo> processor = new CommonProcessors.CollectProcessor<HighlightInfo>();
|
||||
DaemonCodeAnalyzerEx.processHighlights(document, myProject, severity, 0, document.getTextLength(), processor);
|
||||
return processor.getResults();
|
||||
}
|
||||
});
|
||||
assertNotNull(highlightInfos);
|
||||
return highlightInfos.size() == expected;
|
||||
}
|
||||
}, SHORT_TIMEOUT);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Document getNotNullDocument() {
|
||||
Document document = getDocument(myVirtualFile);
|
||||
assertNotNull("No Document found for path " + quote(myPath.getPath()), document);
|
||||
return document;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public FileFixture requireVirtualFile() {
|
||||
assertNotNull("No VirtualFile found for path " + quote(myPath.getPath()), myVirtualFile);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Document getDocument(@NotNull final VirtualFile file) {
|
||||
return execute(new GuiQuery<Document>() {
|
||||
@Override
|
||||
protected Document executeInEDT() throws Throwable {
|
||||
return FileDocumentManager.getInstance().getDocument(file);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.fixtures;
|
||||
|
||||
import com.intellij.find.impl.FindDialog;
|
||||
import com.intellij.openapi.ui.ComboBox;
|
||||
import org.fest.swing.core.Robot;
|
||||
import org.fest.swing.edt.GuiTask;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import static com.intellij.tests.gui.framework.GuiTests.findAndClickButton;
|
||||
import static org.fest.assertions.Assertions.assertThat;
|
||||
import static org.fest.swing.edt.GuiActionRunner.execute;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
public class FindDialogFixture extends IdeaDialogFixture<FindDialog> {
|
||||
@NotNull
|
||||
public static FindDialogFixture find(@NotNull Robot robot) {
|
||||
return new FindDialogFixture(robot, find(robot, FindDialog.class));
|
||||
}
|
||||
|
||||
private FindDialogFixture(@NotNull Robot robot, @NotNull DialogAndWrapper<FindDialog> dialogAndWrapper) {
|
||||
super(robot, dialogAndWrapper);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public FindDialogFixture setTextToFind(@NotNull final String text) {
|
||||
execute(new GuiTask() {
|
||||
@Override
|
||||
protected void executeInEDT() throws Throwable {
|
||||
JComponent c = getDialogWrapper().getPreferredFocusedComponent();
|
||||
assertThat(c).isInstanceOf(ComboBox.class);
|
||||
ComboBox input = (ComboBox)c;
|
||||
assertNotNull(input);
|
||||
input.setSelectedItem(text);
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public FindDialogFixture clickFind() {
|
||||
findAndClickButton(this, "Find");
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.fixtures;
|
||||
|
||||
import com.intellij.ide.OccurenceNavigatorSupport;
|
||||
import com.intellij.ui.content.Content;
|
||||
import com.intellij.ui.treeStructure.Tree;
|
||||
import com.intellij.usageView.UsageViewManager;
|
||||
import com.intellij.usages.impl.GroupNode;
|
||||
import org.fest.swing.edt.GuiQuery;
|
||||
import org.fest.util.Lists;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.List;
|
||||
|
||||
import static org.fest.reflect.core.Reflection.field;
|
||||
import static org.fest.swing.edt.GuiActionRunner.execute;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
public class FindToolWindowFixture {
|
||||
@NotNull private final IdeFrameFixture myParent;
|
||||
|
||||
public FindToolWindowFixture(@NotNull IdeFrameFixture parent) {
|
||||
myParent = parent;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ContentFixture getSelectedContext() {
|
||||
return new ContentFixture(myParent);
|
||||
}
|
||||
|
||||
public static class ContentFixture {
|
||||
@NotNull private final Content myContent;
|
||||
|
||||
ContentFixture(@NotNull IdeFrameFixture parent) {
|
||||
UsageViewManager usageViewManager = UsageViewManager.getInstance(parent.getProject());
|
||||
myContent = usageViewManager.getSelectedContent();
|
||||
assertNotNull(myContent);
|
||||
}
|
||||
|
||||
|
||||
public void findUsagesInGeneratedCodeGroup() {
|
||||
findUsageGroup("Usages in generated code");
|
||||
}
|
||||
|
||||
public void findUsageGroup(@NotNull final String groupText) {
|
||||
final Tree tree = getContentsTree();
|
||||
final List<String> groupNames = Lists.newArrayList();
|
||||
GroupNode foundGroup = execute(new GuiQuery<GroupNode>() {
|
||||
@Override
|
||||
@Nullable
|
||||
protected GroupNode executeInEDT() throws Throwable {
|
||||
GroupNode rootNode = (GroupNode)tree.getModel().getRoot();
|
||||
GroupNode found = null;
|
||||
for (GroupNode subGroup : rootNode.getSubGroups()) {
|
||||
String subGroupText = subGroup.getGroup().getText(null);
|
||||
groupNames.add(subGroupText);
|
||||
if (groupText.equals(subGroupText)) {
|
||||
found = subGroup;
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
});
|
||||
String msg = String.format("Failed to find usage group '%1$s' in %2$s", groupText, groupNames);
|
||||
assertNotNull(msg, foundGroup);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Tree getContentsTree() {
|
||||
JComponent component = myContent.getComponent();
|
||||
OccurenceNavigatorSupport navigatorSupport = field("mySupport").ofType(OccurenceNavigatorSupport.class).in(component).get();
|
||||
assertNotNull(navigatorSupport);
|
||||
JTree tree = field("myTree").ofType(JTree.class).in(navigatorSupport).get();
|
||||
assertNotNull(tree);
|
||||
return (Tree)tree;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,868 @@
|
||||
/*
|
||||
* Copyright 2000-2016 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.tests.gui.fixtures;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.intellij.codeInspection.ui.InspectionTree;
|
||||
import com.intellij.ide.RecentProjectsManager;
|
||||
import com.intellij.ide.actions.ShowSettingsUtilImpl;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.externalSystem.model.ExternalSystemException;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.module.ModuleManager;
|
||||
import com.intellij.openapi.options.ShowSettingsUtil;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ContentEntry;
|
||||
import com.intellij.openapi.roots.ModifiableRootModel;
|
||||
import com.intellij.openapi.roots.ModuleRootManager;
|
||||
import com.intellij.openapi.roots.SourceFolder;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.wm.impl.IdeFrameImpl;
|
||||
import com.intellij.openapi.wm.impl.welcomeScreen.WelcomeFrame;
|
||||
import com.intellij.ui.EditorNotificationPanel;
|
||||
import com.intellij.xdebugger.impl.ui.tree.nodes.XDebuggerTreeNode;
|
||||
import org.fest.swing.core.GenericTypeMatcher;
|
||||
import org.fest.swing.core.Robot;
|
||||
import org.fest.swing.core.matcher.JButtonMatcher;
|
||||
import org.fest.swing.core.matcher.JLabelMatcher;
|
||||
import org.fest.swing.edt.GuiQuery;
|
||||
import org.fest.swing.edt.GuiTask;
|
||||
import org.fest.swing.timing.Condition;
|
||||
import org.fest.swing.timing.Timeout;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jps.model.module.JpsModuleSourceRootType;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.tree.TreeNode;
|
||||
import java.awt.*;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.io.File;
|
||||
import java.util.Collection;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.intellij.ide.impl.ProjectUtil.closeAndDispose;
|
||||
import static com.intellij.openapi.util.io.FileUtil.*;
|
||||
import static com.intellij.openapi.util.text.StringUtil.isNotEmpty;
|
||||
import static com.intellij.openapi.vfs.VfsUtilCore.urlToPath;
|
||||
import static com.intellij.tests.gui.framework.GuiTests.*;
|
||||
import static junit.framework.Assert.assertNotNull;
|
||||
import static junit.framework.Assert.fail;
|
||||
import static org.fest.assertions.Assertions.assertThat;
|
||||
import static org.fest.swing.edt.GuiActionRunner.execute;
|
||||
import static org.fest.swing.timing.Pause.pause;
|
||||
import static org.fest.util.Strings.quote;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Created by karashevich on 30/05/16.
|
||||
*/
|
||||
public class IdeFrameFixture extends ComponentFixture<IdeFrameFixture, IdeFrameImpl> {
|
||||
@NotNull private final File myProjectPath;
|
||||
|
||||
private EditorFixture myEditor;
|
||||
|
||||
@NotNull
|
||||
public static IdeFrameFixture find(@NotNull final Robot robot, @NotNull final File projectPath, @Nullable final String projectName) {
|
||||
final GenericTypeMatcher<IdeFrameImpl> matcher = new GenericTypeMatcher<IdeFrameImpl>(IdeFrameImpl.class) {
|
||||
@Override
|
||||
protected boolean isMatching(@NotNull IdeFrameImpl frame) {
|
||||
Project project = frame.getProject();
|
||||
if (project != null && toSystemIndependentName(projectPath.getPath()).equals(project.getBasePath())) {
|
||||
return projectName == null || projectName.equals(project.getName());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
pause(new Condition("IdeFrame " + quote(projectPath.getPath()) + " to show up") {
|
||||
@Override
|
||||
public boolean test() {
|
||||
Collection<IdeFrameImpl> frames = robot.finder().findAll(matcher);
|
||||
return !frames.isEmpty();
|
||||
}
|
||||
}, LONG_TIMEOUT);
|
||||
|
||||
IdeFrameImpl ideFrame = robot.finder().find(matcher);
|
||||
return new IdeFrameFixture(robot, ideFrame, projectPath);
|
||||
}
|
||||
|
||||
public IdeFrameFixture(@NotNull Robot robot, @NotNull IdeFrameImpl target, @NotNull File projectPath) {
|
||||
super(IdeFrameFixture.class, robot, target);
|
||||
myProjectPath = projectPath;
|
||||
final Project project = getProject();
|
||||
|
||||
Disposable disposable = new NoOpDisposable();
|
||||
Disposer.register(project, disposable);
|
||||
|
||||
//myGradleProjectEventListener = new GradleProjectEventListener();
|
||||
|
||||
//GradleSyncState.subscribe(project, myGradleProjectEventListener);
|
||||
//PostProjectBuildTasksExecutor.subscribe(project, myGradleProjectEventListener);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public File getProjectPath() {
|
||||
return myProjectPath;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<String> getModuleNames() {
|
||||
List<String> names = Lists.newArrayList();
|
||||
for (Module module : getModuleManager().getModules()) {
|
||||
names.add(module.getName());
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public IdeFrameFixture requireModuleCount(int expected) {
|
||||
Module[] modules = getModuleManager().getModules();
|
||||
assertThat(modules).as("Module count in project " + quote(getProject().getName())).hasSize(expected);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
public Collection<String> getSourceFolderRelativePaths(@NotNull String moduleName, @NotNull final JpsModuleSourceRootType<?> sourceType) {
|
||||
final Set<String> paths = Sets.newHashSet();
|
||||
|
||||
Module module = getModule(moduleName);
|
||||
final ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module);
|
||||
|
||||
execute(new GuiTask() {
|
||||
@Override
|
||||
protected void executeInEDT() throws Throwable {
|
||||
ModifiableRootModel rootModel = moduleRootManager.getModifiableModel();
|
||||
try {
|
||||
for (ContentEntry contentEntry : rootModel.getContentEntries()) {
|
||||
for (SourceFolder folder : contentEntry.getSourceFolders()) {
|
||||
JpsModuleSourceRootType<?> rootType = folder.getRootType();
|
||||
if (rootType.equals(sourceType)) {
|
||||
String path = urlToPath(folder.getUrl());
|
||||
String relativePath = getRelativePath(myProjectPath, new File(toSystemDependentName(path)));
|
||||
paths.add(relativePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
rootModel.dispose();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Module getModule(@NotNull String name) {
|
||||
Module module = findModule(name);
|
||||
assertNotNull("Unable to find module with name " + quote(name), module);
|
||||
return module;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Module findModule(@NotNull String name) {
|
||||
for (Module module : getModuleManager().getModules()) {
|
||||
if (name.equals(module.getName())) {
|
||||
return module;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
private ModuleManager getModuleManager() {
|
||||
return ModuleManager.getInstance(getProject());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public EditorFixture getEditor() {
|
||||
if (myEditor == null) {
|
||||
myEditor = new EditorFixture(robot(), this);
|
||||
}
|
||||
|
||||
return myEditor;
|
||||
}
|
||||
|
||||
|
||||
//@NotNull
|
||||
//public GradleInvocationResult invokeProjectMake() {
|
||||
// return invokeProjectMake(null);
|
||||
//}
|
||||
|
||||
//@NotNull
|
||||
//public GradleInvocationResult invokeProjectMake(@Nullable Runnable executeAfterInvokingMake) {
|
||||
// myGradleProjectEventListener.reset();
|
||||
//
|
||||
// final AtomicReference<GradleInvocationResult> resultRef = new AtomicReference<GradleInvocationResult>();
|
||||
// AndroidProjectBuildNotifications.subscribe(getProject(), new AndroidProjectBuildNotifications.AndroidProjectBuildListener() {
|
||||
// @Override
|
||||
// public void buildComplete(@NotNull AndroidProjectBuildNotifications.BuildContext context) {
|
||||
// if (context instanceof GradleBuildContext) {
|
||||
// resultRef.set(((GradleBuildContext)context).getBuildResult());
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// selectProjectMakeAction();
|
||||
//
|
||||
// if (executeAfterInvokingMake != null) {
|
||||
// executeAfterInvokingMake.run();
|
||||
// }
|
||||
//
|
||||
// waitForBuildToFinish(COMPILE_JAVA);
|
||||
//
|
||||
// GradleInvocationResult result = resultRef.get();
|
||||
// assertNotNull(result);
|
||||
//
|
||||
// return result;
|
||||
//}
|
||||
|
||||
@NotNull
|
||||
public IdeFrameFixture invokeProjectMakeAndSimulateFailure(@NotNull final String failure) {
|
||||
Runnable failTask = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
throw new ExternalSystemException(failure);
|
||||
}
|
||||
};
|
||||
//ApplicationManager.getApplication().putUserData(EXECUTE_BEFORE_PROJECT_BUILD_IN_GUI_TEST_KEY, failTask);
|
||||
selectProjectMakeAction();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Finds the Run button in the IDE interface.
|
||||
*
|
||||
* @return ActionButtonFixture for the run button.
|
||||
*/
|
||||
@NotNull
|
||||
public ActionButtonFixture findRunApplicationButton() {
|
||||
return findActionButtonByActionId("Run");
|
||||
}
|
||||
|
||||
public void debugApp(@NotNull String appName) throws ClassNotFoundException {
|
||||
selectApp(appName);
|
||||
findActionButtonByActionId("Debug").click();
|
||||
}
|
||||
|
||||
public void runApp(@NotNull String appName) throws ClassNotFoundException {
|
||||
selectApp(appName);
|
||||
findActionButtonByActionId("Run").click();
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
public RunToolWindowFixture getRunToolWindow() {
|
||||
return new RunToolWindowFixture(this);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DebugToolWindowFixture getDebugToolWindow() {
|
||||
return new DebugToolWindowFixture(this);
|
||||
}
|
||||
|
||||
protected void selectProjectMakeAction() {
|
||||
invokeMenuPath("Build", "Make Project");
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes an action by menu path
|
||||
*
|
||||
* @param path the series of menu names, e.g. {@link invokeActionByMenuPath("Build", "Make Project")}
|
||||
*/
|
||||
public void invokeMenuPath(@NotNull String... path) {
|
||||
getMenuFixture().invokeMenuPath(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes an action by menu path (where each segment is a regular expression). This is particularly
|
||||
* useful when the menu items can change dynamically, such as the labels of Undo actions, Run actions,
|
||||
* etc.
|
||||
*
|
||||
* @param path the series of menu name regular expressions, e.g. {@link invokeActionByMenuPath("Build", "Make( Project)?")}
|
||||
*/
|
||||
public void invokeMenuPathRegex(@NotNull String... path) {
|
||||
getMenuFixture().invokeMenuPathRegex(path);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private MenuFixture getMenuFixture() {
|
||||
return new MenuFixture(robot(), target());
|
||||
}
|
||||
|
||||
//@NotNull
|
||||
//public IdeFrameFixture waitForBuildToFinish(@NotNull final BuildMode buildMode) {
|
||||
// final Project project = getProject();
|
||||
// if (buildMode == SOURCE_GEN && !GradleProjectBuilder.getInstance(project).isSourceGenerationEnabled()) {
|
||||
// return this;
|
||||
// }
|
||||
//
|
||||
// pause(new Condition("Build (" + buildMode + ") for project " + quote(project.getName()) + " to finish'") {
|
||||
// @Override
|
||||
// public boolean test() {
|
||||
// if (buildMode == SOURCE_GEN) {
|
||||
// PostProjectBuildTasksExecutor tasksExecutor = PostProjectBuildTasksExecutor.getInstance(project);
|
||||
// if (tasksExecutor.getLastBuildTimestamp() > -1) {
|
||||
// // This will happen when creating a new project. Source generation happens before the IDE frame is found and build listeners
|
||||
// // are created. It is fairly safe to assume that source generation happened if we have a timestamp for a "last performed build".
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
// return myGradleProjectEventListener.isBuildFinished(buildMode);
|
||||
// }
|
||||
// }, LONG_TIMEOUT);
|
||||
//
|
||||
// waitForBackgroundTasksToFinish();
|
||||
// robot().waitForIdle();
|
||||
//
|
||||
// return this;
|
||||
//}
|
||||
|
||||
@NotNull
|
||||
public FileFixture findExistingFileByRelativePath(@NotNull String relativePath) {
|
||||
VirtualFile file = findFileByRelativePath(relativePath, true);
|
||||
return new FileFixture(getProject(), file);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Contract("_, true -> !null")
|
||||
public VirtualFile findFileByRelativePath(@NotNull String relativePath, boolean requireExists) {
|
||||
//noinspection Contract
|
||||
assertFalse("Should use '/' in test relative paths, not File.separator", relativePath.contains("\\"));
|
||||
Project project = getProject();
|
||||
VirtualFile file = project.getBaseDir().findFileByRelativePath(relativePath);
|
||||
if (requireExists) {
|
||||
//noinspection Contract
|
||||
assertNotNull("Unable to find file with relative path " + quote(relativePath), file);
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
//@NotNull
|
||||
//public IdeFrameFixture requestProjectSyncAndExpectFailure() {
|
||||
// requestProjectSync();
|
||||
// return waitForGradleProjectSyncToFail();
|
||||
//}
|
||||
|
||||
@NotNull
|
||||
public IdeFrameFixture requestProjectSyncAndSimulateFailure(@NotNull final String failure) {
|
||||
Runnable failTask = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
throw new ExternalSystemException(failure);
|
||||
}
|
||||
};
|
||||
//ApplicationManager.getApplication().putUserData(EXECUTE_BEFORE_PROJECT_SYNC_TASK_IN_GUI_TEST_KEY, failTask);
|
||||
// When simulating the error, we don't have to wait for sync to happen. Sync never happens because the error is thrown before it (sync)
|
||||
// is started.
|
||||
return requestProjectSync();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public IdeFrameFixture requestProjectSync() {
|
||||
//myGradleProjectEventListener.reset();
|
||||
|
||||
// We wait until all "Run Configurations" are populated in the toolbar combo-box. Until then the "Project Sync" button is not in its
|
||||
// final position, and FEST will click the wrong button.
|
||||
pause(new Condition("Waiting for 'Run Configurations' to be populated") {
|
||||
@Override
|
||||
public boolean test() {
|
||||
RunConfigurationComboBoxFixture runConfigurationComboBox = RunConfigurationComboBoxFixture.find(IdeFrameFixture.this);
|
||||
return isNotEmpty(runConfigurationComboBox.getText());
|
||||
}
|
||||
}, SHORT_TIMEOUT);
|
||||
|
||||
waitForBackgroundTasksToFinish();
|
||||
findGradleSyncAction().waitUntilEnabledAndShowing();
|
||||
// TODO figure out why in IDEA 15 even though an action is enabled, visible and showing, clicking it (via UI testing infrastructure)
|
||||
// does not work consistently
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private ActionButtonFixture findGradleSyncAction() {
|
||||
return findActionButtonByActionId("Android.SyncProject");
|
||||
}
|
||||
|
||||
//@NotNull
|
||||
//public IdeFrameFixture waitForGradleProjectSyncToFail() {
|
||||
// try {
|
||||
// waitForGradleProjectSyncToFinish(true);
|
||||
// fail("Expecting project sync to fail");
|
||||
// }
|
||||
// catch (RuntimeException expected) {
|
||||
// // expected failure.
|
||||
// }
|
||||
// return waitForBackgroundTasksToFinish();
|
||||
//}
|
||||
|
||||
//@NotNull
|
||||
//public IdeFrameFixture waitForGradleProjectSyncToStart() {
|
||||
// Project project = getProject();
|
||||
// final GradleSyncState syncState = GradleSyncState.getInstance(project);
|
||||
// if (!syncState.isSyncInProgress()) {
|
||||
// pause(new Condition("Syncing project " + quote(project.getName()) + " to finish") {
|
||||
// @Override
|
||||
// public boolean test() {
|
||||
// return myGradleProjectEventListener.isSyncStarted();
|
||||
// }
|
||||
// }, SHORT_TIMEOUT);
|
||||
// }
|
||||
// return this;
|
||||
//}
|
||||
|
||||
//@NotNull
|
||||
//public IdeFrameFixture waitForGradleProjectSyncToFinish() {
|
||||
// waitForGradleProjectSyncToFinish(false);
|
||||
// return this;
|
||||
//}
|
||||
|
||||
//private void waitForGradleProjectSyncToFinish(final boolean expectSyncFailure) {
|
||||
// final Project project = getProject();
|
||||
//
|
||||
// // ensure GradleInvoker (in-process build) is always enabled.
|
||||
// AndroidGradleBuildConfiguration buildConfiguration = AndroidGradleBuildConfiguration.getInstance(project);
|
||||
// buildConfiguration.USE_EXPERIMENTAL_FASTER_BUILD = true;
|
||||
//
|
||||
// pause(new Condition("Syncing project " + quote(project.getName()) + " to finish") {
|
||||
// @Override
|
||||
// public boolean test() {
|
||||
// GradleSyncState syncState = GradleSyncState.getInstance(project);
|
||||
// boolean syncFinished =
|
||||
// (myGradleProjectEventListener.isSyncFinished() || syncState.isSyncNeeded() != ThreeState.YES) && !syncState.isSyncInProgress();
|
||||
// if (expectSyncFailure) {
|
||||
// syncFinished = syncFinished && myGradleProjectEventListener.hasSyncError();
|
||||
// }
|
||||
// return syncFinished;
|
||||
// }
|
||||
// }, LONG_TIMEOUT);
|
||||
//
|
||||
// findGradleSyncAction().waitUntilEnabledAndShowing();
|
||||
//
|
||||
// if (myGradleProjectEventListener.hasSyncError()) {
|
||||
// RuntimeException syncError = myGradleProjectEventListener.getSyncError();
|
||||
// myGradleProjectEventListener.reset();
|
||||
// throw syncError;
|
||||
// }
|
||||
//
|
||||
// if (!myGradleProjectEventListener.isSyncSkipped()) {
|
||||
// waitForBuildToFinish(SOURCE_GEN);
|
||||
// }
|
||||
//
|
||||
// waitForBackgroundTasksToFinish();
|
||||
//}
|
||||
|
||||
@NotNull
|
||||
public IdeFrameFixture waitForBackgroundTasksToFinish() {
|
||||
pause(new Condition("Background tasks to finish") {
|
||||
@Override
|
||||
public boolean test() {
|
||||
ProgressManager progressManager = ProgressManager.getInstance();
|
||||
return !progressManager.hasModalProgressIndicator() &&
|
||||
!progressManager.hasProgressIndicator() &&
|
||||
!progressManager.hasUnsafeProgressIndicator();
|
||||
}
|
||||
}, LONG_TIMEOUT);
|
||||
robot().waitForIdle();
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private ActionButtonFixture findActionButtonByActionId(String actionId) {
|
||||
return ActionButtonFixture.findByActionId(actionId, robot(), target());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public MessagesToolWindowFixture getMessagesToolWindow() {
|
||||
return new MessagesToolWindowFixture(getProject(), robot());
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
public EditorNotificationPanelFixture requireEditorNotification(@NotNull final String message) {
|
||||
final Ref<EditorNotificationPanel> notificationPanelRef = new Ref<EditorNotificationPanel>();
|
||||
|
||||
pause(new Condition("Notification with message '" + message + "' shows up") {
|
||||
@Override
|
||||
public boolean test() {
|
||||
EditorNotificationPanel notificationPanel = findNotificationPanel(message);
|
||||
notificationPanelRef.set(notificationPanel);
|
||||
return notificationPanel != null;
|
||||
}
|
||||
});
|
||||
|
||||
EditorNotificationPanel notificationPanel = notificationPanelRef.get();
|
||||
assertNotNull(notificationPanel);
|
||||
return new EditorNotificationPanelFixture(robot(), notificationPanel);
|
||||
}
|
||||
|
||||
public void requireNoEditorNotification() {
|
||||
assertNull(findNotificationPanel(null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates an editor notification with the given main message (unless the message is {@code null}, in which case we assert that there are
|
||||
* no visible editor notifications. Will fail if the given notification is not found.
|
||||
*/
|
||||
@Nullable
|
||||
private EditorNotificationPanel findNotificationPanel(@Nullable String message) {
|
||||
Collection<EditorNotificationPanel> panels = robot().finder().findAll(target(), new GenericTypeMatcher<EditorNotificationPanel>(
|
||||
EditorNotificationPanel.class, true) {
|
||||
@Override
|
||||
protected boolean isMatching(@NotNull EditorNotificationPanel panel) {
|
||||
return panel.isShowing();
|
||||
}
|
||||
});
|
||||
|
||||
if (message == null) {
|
||||
if (!panels.isEmpty()) {
|
||||
List<String> labels = Lists.newArrayList();
|
||||
for (EditorNotificationPanel panel : panels) {
|
||||
labels.addAll(getEditorNotificationLabels(panel));
|
||||
}
|
||||
fail("Found editor notifications when none were expected" + labels);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
List<String> labels = Lists.newArrayList();
|
||||
for (EditorNotificationPanel panel : panels) {
|
||||
List<String> found = getEditorNotificationLabels(panel);
|
||||
labels.addAll(found);
|
||||
for (String label : found) {
|
||||
if (label.contains(message)) {
|
||||
return panel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up the main label for a given editor notification panel
|
||||
*/
|
||||
private List<String> getEditorNotificationLabels(@NotNull EditorNotificationPanel panel) {
|
||||
final List<String> allText = Lists.newArrayList();
|
||||
final Collection<JLabel> labels = robot().finder().findAll(panel, JLabelMatcher.any().andShowing());
|
||||
for (final JLabel label : labels) {
|
||||
String text = execute(new GuiQuery<String>() {
|
||||
@Override
|
||||
@Nullable
|
||||
protected String executeInEDT() throws Throwable {
|
||||
return label.getText();
|
||||
}
|
||||
});
|
||||
if (isNotEmpty(text)) {
|
||||
allText.add(text);
|
||||
}
|
||||
}
|
||||
return allText;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public IdeSettingsDialogFixture openIdeSettings() {
|
||||
// Using invokeLater because we are going to show a *modal* dialog via API (instead of clicking a button, for example.) If we use
|
||||
// GuiActionRunner the test will hang until the modal dialog is closed.
|
||||
ApplicationManager.getApplication().invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Project project = getProject();
|
||||
ShowSettingsUtil.getInstance().showSettingsDialog(project, ShowSettingsUtilImpl.getConfigurableGroups(project, true));
|
||||
}
|
||||
});
|
||||
return IdeSettingsDialogFixture.find(robot());
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
public RunConfigurationsDialogFixture invokeRunConfigurationsDialog() {
|
||||
invokeMenuPath("Run", "Edit Configurations...");
|
||||
return RunConfigurationsDialogFixture.find(robot());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public InspectionsFixture inspectCode() {
|
||||
invokeMenuPath("Analyze", "Inspect Code...");
|
||||
|
||||
//final Ref<FileChooserDialogImpl> wrapperRef = new Ref<FileChooserDialogImpl>();
|
||||
JDialog dialog = robot().finder().find(new GenericTypeMatcher<JDialog>(JDialog.class) {
|
||||
@Override
|
||||
protected boolean isMatching(@NotNull JDialog dialog) {
|
||||
return "Specify Inspection Scope".equals(dialog.getTitle());
|
||||
}
|
||||
});
|
||||
JButton button = robot().finder().find(dialog, JButtonMatcher.withText("OK").andShowing());
|
||||
robot().click(button);
|
||||
|
||||
final InspectionTree tree = waitUntilFound(robot(), new GenericTypeMatcher<InspectionTree>(InspectionTree.class) {
|
||||
@Override
|
||||
protected boolean isMatching(@NotNull InspectionTree component) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
return new InspectionsFixture(robot(), getProject(), tree);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ProjectViewFixture getProjectView() {
|
||||
return new ProjectViewFixture(getProject(), robot());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Project getProject() {
|
||||
Project project = target().getProject();
|
||||
assertNotNull(project);
|
||||
return project;
|
||||
}
|
||||
|
||||
public void closeProject() {
|
||||
execute(new GuiTask() {
|
||||
@Override
|
||||
protected void executeInEDT() throws Throwable {
|
||||
closeAndDispose(getProject());
|
||||
RecentProjectsManager.getInstance().updateLastProjectPath();
|
||||
WelcomeFrame.showIfNoProjectOpened();
|
||||
}
|
||||
});
|
||||
pause(new Condition("Waiting for 'Welcome' page to show up") {
|
||||
@Override
|
||||
public boolean test() {
|
||||
for (Frame frame : Frame.getFrames()) {
|
||||
if (frame == WelcomeFrame.getInstance() && frame.isShowing()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public MessagesFixture findMessageDialog(@NotNull String title) {
|
||||
return MessagesFixture.findByTitle(robot(), target(), title);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@NotNull
|
||||
public FindDialogFixture invokeFindInPathDialog() {
|
||||
invokeMenuPath("Edit", "Find", "Find in Path...");
|
||||
return FindDialogFixture.find(robot());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public FindToolWindowFixture getFindToolWindow() {
|
||||
return new FindToolWindowFixture(this);
|
||||
}
|
||||
|
||||
//@NotNull
|
||||
//public GradleBuildModelFixture parseBuildFileForModule(@NotNull String moduleName, boolean openInEditor) {
|
||||
// Module module = getModule(moduleName);
|
||||
// return parseBuildFile(module, openInEditor);
|
||||
//}
|
||||
//
|
||||
//@NotNull
|
||||
//public GradleBuildModelFixture parseBuildFile(@NotNull Module module, boolean openInEditor) {
|
||||
// VirtualFile buildFile = getGradleBuildFile(module);
|
||||
// assertNotNull(buildFile);
|
||||
// return parseBuildFile(buildFile, openInEditor);
|
||||
//}
|
||||
//
|
||||
//@NotNull
|
||||
//public GradleBuildModelFixture parseBuildFile(@NotNull final VirtualFile buildFile, boolean openInEditor) {
|
||||
// if (openInEditor) {
|
||||
// getEditor().open(buildFile, Tab.DEFAULT).getCurrentFile();
|
||||
// }
|
||||
// final Ref<GradleBuildModel> buildModelRef = new Ref<GradleBuildModel>();
|
||||
// new ReadAction() {
|
||||
// @Override
|
||||
// protected void run(@NotNull Result result) throws Throwable {
|
||||
// buildModelRef.set(GradleBuildModel.parseBuildFile(buildFile, getProject()));
|
||||
// }
|
||||
// }.execute();
|
||||
// GradleBuildModel buildModel = buildModelRef.get();
|
||||
// assertNotNull(buildModel);
|
||||
// return new GradleBuildModelFixture(buildModel);
|
||||
//}
|
||||
|
||||
private static class NoOpDisposable implements Disposable {
|
||||
@Override
|
||||
public void dispose() {
|
||||
}
|
||||
}
|
||||
|
||||
public void selectApp(@NotNull String appName) {
|
||||
final ActionButtonFixture runButton = findRunApplicationButton();
|
||||
Container actionToolbarContainer = execute(new GuiQuery<Container>() {
|
||||
@Override
|
||||
protected Container executeInEDT() throws Throwable {
|
||||
return runButton.target().getParent();
|
||||
}
|
||||
});
|
||||
assertNotNull(actionToolbarContainer);
|
||||
|
||||
ComboBoxActionFixture comboBoxActionFixture = ComboBoxActionFixture.findComboBox(robot(), actionToolbarContainer);
|
||||
comboBoxActionFixture.selectItem(appName);
|
||||
robot().pressAndReleaseKey(KeyEvent.VK_ENTER);
|
||||
robot().waitForIdle();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
//// Methods to help control debugging under a test. ///////
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
public void resumeProgram() {
|
||||
invokeMenuPathOnRobotIdle(this, "Run", "Resume Program");
|
||||
}
|
||||
|
||||
public void stepOver() {
|
||||
invokeMenuPathOnRobotIdle(this, "Run", "Step Over");
|
||||
}
|
||||
|
||||
public void stepInto() {
|
||||
invokeMenuPathOnRobotIdle(this, "Run", "Step Into");
|
||||
}
|
||||
|
||||
public void stepOut() {
|
||||
invokeMenuPathOnRobotIdle(this, "Run", "Step Out");
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles breakpoints at the line numbers in {@code lines} of the source file with basename {@code fileBaseName}. This will work only
|
||||
* if the fileBasename is unique in the project that's open on Android Studio.
|
||||
*/
|
||||
public void toggleBreakPoints(String fileBasename, int[] lines) {
|
||||
// We open the file twice to bring the editor into focus. Idea 1.15 has this bug where opening a file doesn't automatically bring its
|
||||
// editor window into focus.
|
||||
openFile(this, fileBasename);
|
||||
openFile(this, fileBasename);
|
||||
for (int line : lines) {
|
||||
navigateToLine(this, line);
|
||||
invokeMenuPathOnRobotIdle(this, "Run", "Toggle Line Breakpoint");
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively prints out the debugger tree rooted at {@code node} into {@code builder} with an indent of
|
||||
// "{@code level} * {@code numIndentSpaces}" whitespaces. Each node is printed on a separate line. The indent level for every child node
|
||||
// is 1 more than their parent.
|
||||
private static void printNode(XDebuggerTreeNode node, StringBuilder builder, int level, int numIndentSpaces) {
|
||||
int numIndent = level;
|
||||
if (builder.length() > 0) {
|
||||
builder.append(System.getProperty("line.separator"));
|
||||
}
|
||||
for (int i = 0; i < level * numIndentSpaces; ++i) {
|
||||
builder.append(' ');
|
||||
}
|
||||
builder.append(node.getText().toString());
|
||||
Enumeration<XDebuggerTreeNode> children = node.children();
|
||||
while (children.hasMoreElements()) {
|
||||
printNode(children.nextElement(), builder, level + 1, numIndentSpaces);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints out the debugger tree rooted at {@code root}.
|
||||
*/
|
||||
@NotNull
|
||||
public static String printDebuggerTree(XDebuggerTreeNode root) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
printNode(root, builder, 0, 2);
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String[] debuggerTreeRootToChildrenTexts(XDebuggerTreeNode treeRoot) {
|
||||
List<? extends TreeNode> children = treeRoot.getChildren();
|
||||
String[] childrenTexts = new String[children.size()];
|
||||
int i = 0;
|
||||
for (TreeNode child : children) {
|
||||
childrenTexts[i] = ((XDebuggerTreeNode)child).getText().toString();
|
||||
++i;
|
||||
}
|
||||
return childrenTexts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the subset of {@code expectedPatterns} which do not match any of the children (just the first level children, not recursive) of
|
||||
* {@code treeRoot} .
|
||||
*/
|
||||
@NotNull
|
||||
public static List<String> getUnmatchedTerminalVariableValues(String[] expectedPatterns, XDebuggerTreeNode treeRoot) {
|
||||
String[] childrenTexts = debuggerTreeRootToChildrenTexts(treeRoot);
|
||||
List<String> unmatchedPatterns = Lists.newArrayList();
|
||||
for (String expectedPattern : expectedPatterns) {
|
||||
boolean matched = false;
|
||||
for (String childText : childrenTexts) {
|
||||
if (childText.matches(expectedPattern)) {
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!matched) {
|
||||
unmatchedPatterns.add(expectedPattern);
|
||||
}
|
||||
}
|
||||
return unmatchedPatterns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the appropriate pattern to look for a variable named {@code name} with the type {@code type} and value {@code value} appearing
|
||||
* in the Variables window in Android Studio.
|
||||
*/
|
||||
@NotNull
|
||||
public static String variableToSearchPattern(String name, String type, String value) {
|
||||
return String.format("%s = \\{%s\\} %s", name, type, value);
|
||||
}
|
||||
|
||||
public boolean verifyVariablesAtBreakpoint(String[] expectedVariablePatterns, String debugConfigName, long tryUntilMillis) {
|
||||
DebugToolWindowFixture debugToolWindowFixture = new DebugToolWindowFixture(this);
|
||||
final ExecutionToolWindowFixture.ContentFixture contentFixture = debugToolWindowFixture.findContent(debugConfigName);
|
||||
|
||||
contentFixture.clickDebuggerTreeRoot();
|
||||
// Wait for the debugger tree to appear.
|
||||
pause(new Condition("Looking for debugger tree.") {
|
||||
@Override
|
||||
public boolean test() {
|
||||
return contentFixture.getDebuggerTreeRoot() != null;
|
||||
}
|
||||
}, Timeout.timeout(tryUntilMillis));
|
||||
|
||||
// Get the debugger tree and print it.
|
||||
XDebuggerTreeNode debuggerTreeRoot = contentFixture.getDebuggerTreeRoot();
|
||||
if (debuggerTreeRoot == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<String> unmatchedPatterns = getUnmatchedTerminalVariableValues(expectedVariablePatterns, debuggerTreeRoot);
|
||||
return unmatchedPatterns.isEmpty();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.fixtures;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.intellij.openapi.options.Configurable;
|
||||
import com.intellij.openapi.options.ConfigurableGroup;
|
||||
import com.intellij.openapi.options.newEditor.SettingsDialog;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.ui.treeStructure.CachingSimpleNode;
|
||||
import org.fest.swing.core.GenericTypeMatcher;
|
||||
import org.fest.swing.core.Robot;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.util.List;
|
||||
|
||||
import static org.fest.assertions.Assertions.assertThat;
|
||||
import static org.fest.reflect.core.Reflection.field;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
public class IdeSettingsDialogFixture extends IdeaDialogFixture<SettingsDialog> {
|
||||
@NotNull
|
||||
public static IdeSettingsDialogFixture find(@NotNull Robot robot) {
|
||||
return new IdeSettingsDialogFixture(robot, find(robot, SettingsDialog.class, new GenericTypeMatcher<JDialog>(JDialog.class) {
|
||||
@Override
|
||||
protected boolean isMatching(@NotNull JDialog dialog) {
|
||||
String expectedTitle = SystemInfo.isMac ? "Preferences" : "Settings";
|
||||
return expectedTitle.equals(dialog.getTitle()) && dialog.isShowing();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private IdeSettingsDialogFixture(@NotNull Robot robot, @NotNull DialogAndWrapper<SettingsDialog> dialogAndWrapper) {
|
||||
super(robot, dialogAndWrapper);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<String> getProjectSettingsNames() {
|
||||
List<String> names = Lists.newArrayList();
|
||||
JPanel optionsEditor = field("myEditor").ofType(JPanel.class).in(getDialogWrapper()).get();
|
||||
assertNotNull(optionsEditor);
|
||||
|
||||
List<JComponent> trees = findComponentsOfType(optionsEditor, "com.intellij.openapi.options.newEditor.SettingsTreeView");
|
||||
assertThat(trees).hasSize(1);
|
||||
JComponent tree = trees.get(0);
|
||||
|
||||
CachingSimpleNode root = field("myRoot").ofType(CachingSimpleNode.class).in(tree).get();
|
||||
assertNotNull(root);
|
||||
|
||||
ConfigurableGroup[] groups = field("myGroups").ofType(ConfigurableGroup[].class).in(root).get();
|
||||
assertNotNull(groups);
|
||||
for (ConfigurableGroup current : groups) {
|
||||
Configurable[] configurables = current.getConfigurables();
|
||||
for (Configurable configurable : configurables) {
|
||||
names.add(configurable.getDisplayName());
|
||||
}
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<JComponent> findComponentsOfType(@NotNull JComponent parent, @NotNull String typeName) {
|
||||
List<JComponent> result = Lists.newArrayList();
|
||||
findComponentsOfType(typeName, result, parent);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void findComponentsOfType(@NotNull String typeName, @NotNull List<JComponent> result, @Nullable JComponent parent) {
|
||||
if (parent == null) {
|
||||
return;
|
||||
}
|
||||
if (parent.getClass().getName().equals(typeName)) {
|
||||
result.add(parent);
|
||||
}
|
||||
for (Component c : parent.getComponents()) {
|
||||
if (c instanceof JComponent) {
|
||||
findComponentsOfType(typeName, result, (JComponent)c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.fixtures;
|
||||
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import org.fest.reflect.exception.ReflectionError;
|
||||
import org.fest.reflect.reference.TypeRef;
|
||||
import org.fest.swing.core.GenericTypeMatcher;
|
||||
import org.fest.swing.core.Robot;
|
||||
import org.fest.swing.fixture.ContainerFixture;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import static com.intellij.tests.gui.framework.GuiTests.findAndClickCancelButton;
|
||||
import static com.intellij.tests.gui.framework.GuiTests.waitUntilFound;
|
||||
import static junit.framework.Assert.assertNotNull;
|
||||
import static org.fest.reflect.core.Reflection.field;
|
||||
|
||||
public abstract class IdeaDialogFixture<T extends DialogWrapper> extends ComponentFixture<IdeaDialogFixture, JDialog> implements ContainerFixture<JDialog> {
|
||||
@NotNull private final T myDialogWrapper;
|
||||
|
||||
@Nullable
|
||||
protected static <T extends DialogWrapper> T getDialogWrapperFrom(@NotNull JDialog dialog, Class<T> dialogWrapperType) {
|
||||
try {
|
||||
WeakReference<DialogWrapper> dialogWrapperRef = field("myDialogWrapper").ofType(new TypeRef<WeakReference<DialogWrapper>>() {})
|
||||
.in(dialog)
|
||||
.get();
|
||||
assertNotNull(dialogWrapperRef);
|
||||
DialogWrapper wrapper = dialogWrapperRef.get();
|
||||
if (dialogWrapperType.isInstance(wrapper)) {
|
||||
return dialogWrapperType.cast(wrapper);
|
||||
}
|
||||
}
|
||||
catch (ReflectionError ignored) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static class DialogAndWrapper<T extends DialogWrapper> {
|
||||
public final JDialog dialog;
|
||||
public final T wrapper;
|
||||
|
||||
public DialogAndWrapper(@NotNull JDialog dialog, @NotNull T wrapper) {
|
||||
this.dialog = dialog;
|
||||
this.wrapper = wrapper;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static <T extends DialogWrapper> DialogAndWrapper<T> find(@NotNull Robot robot, @NotNull final Class<T> clz) {
|
||||
return find(robot, clz, new GenericTypeMatcher<JDialog>(JDialog.class) {
|
||||
@Override
|
||||
protected boolean isMatching(@NotNull JDialog component) {
|
||||
return component.isShowing();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static <T extends DialogWrapper> DialogAndWrapper<T> find(@NotNull Robot robot, @NotNull final Class<T> clz,
|
||||
@NotNull final GenericTypeMatcher<JDialog> matcher) {
|
||||
final Ref<T> wrapperRef = new Ref<T>();
|
||||
JDialog dialog = waitUntilFound(robot, new GenericTypeMatcher<JDialog>(JDialog.class) {
|
||||
@Override
|
||||
protected boolean isMatching(@NotNull JDialog dialog) {
|
||||
if (matcher.matches(dialog)) {
|
||||
T wrapper = getDialogWrapperFrom(dialog, clz);
|
||||
if (wrapper != null) {
|
||||
wrapperRef.set(wrapper);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return new DialogAndWrapper<T>(dialog, wrapperRef.get());
|
||||
}
|
||||
|
||||
protected IdeaDialogFixture(@NotNull Robot robot, @NotNull JDialog target, @NotNull T dialogWrapper) {
|
||||
super(IdeaDialogFixture.class, robot, target);
|
||||
myDialogWrapper = dialogWrapper;
|
||||
}
|
||||
|
||||
protected IdeaDialogFixture(@NotNull Robot robot, @NotNull DialogAndWrapper<T> dialogAndWrapper) {
|
||||
this(robot, dialogAndWrapper.dialog, dialogAndWrapper.wrapper);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected T getDialogWrapper() {
|
||||
return myDialogWrapper;
|
||||
}
|
||||
|
||||
public void clickCancel() {
|
||||
// Grab focus in case it is not automatically done by the window manager, e.g. 9wm
|
||||
focus();
|
||||
|
||||
findAndClickCancelButton(this);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
robot().close(target());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.fixtures;
|
||||
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import org.fest.swing.core.GenericTypeMatcher;
|
||||
import org.fest.swing.core.Robot;
|
||||
import org.fest.swing.core.matcher.JTextComponentMatcher;
|
||||
import org.fest.swing.fixture.JTextComponentFixture;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.text.JTextComponent;
|
||||
|
||||
import static com.intellij.tests.gui.framework.GuiTests.findAndClickOkButton;
|
||||
import static com.intellij.tests.gui.framework.GuiTests.waitUntilFound;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
public class InputDialogFixture extends IdeaDialogFixture<DialogWrapper> {
|
||||
@NotNull
|
||||
public static InputDialogFixture findByTitle(@NotNull Robot robot, @NotNull final String title) {
|
||||
final Ref<DialogWrapper> wrapperRef = new Ref<DialogWrapper>();
|
||||
JDialog dialog = waitUntilFound(robot, new GenericTypeMatcher<JDialog>(JDialog.class) {
|
||||
@Override
|
||||
protected boolean isMatching(@NotNull JDialog dialog) {
|
||||
if (!title.equals(dialog.getTitle()) || !dialog.isShowing()) {
|
||||
return false;
|
||||
}
|
||||
DialogWrapper wrapper = getDialogWrapperFrom(dialog, DialogWrapper.class);
|
||||
if (wrapper != null) {
|
||||
String typeName = Messages.class.getName() + "$InputDialog";
|
||||
if (typeName.equals(wrapper.getClass().getName())) {
|
||||
wrapperRef.set(wrapper);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return new InputDialogFixture(robot, dialog, wrapperRef.get());
|
||||
}
|
||||
|
||||
public void enterTextAndClickOk(@NotNull String text) {
|
||||
JTextComponent input = robot().finder().find(target(), JTextComponentMatcher.any());
|
||||
assertNotNull(input);
|
||||
JTextComponentFixture inputFixture = new JTextComponentFixture(robot(), input);
|
||||
inputFixture.enterText(text);
|
||||
findAndClickOkButton(this);
|
||||
}
|
||||
|
||||
private InputDialogFixture(@NotNull Robot robot, @NotNull JDialog target, @NotNull DialogWrapper dialogWrapper) {
|
||||
super(robot, target, dialogWrapper);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.fixtures;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.intellij.codeInspection.ui.InspectionTree;
|
||||
import com.intellij.codeInspection.ui.InspectionTreeNode;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.wm.ToolWindowId;
|
||||
import org.fest.swing.core.Robot;
|
||||
import org.fest.swing.edt.GuiQuery;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import static org.fest.swing.edt.GuiActionRunner.execute;
|
||||
|
||||
/**
|
||||
* Fixture for the Inspections window in the IDE
|
||||
*/
|
||||
public class InspectionsFixture extends ToolWindowFixture {
|
||||
private final InspectionTree myTree;
|
||||
|
||||
public InspectionsFixture(@NotNull Robot robot, @NotNull Project project, InspectionTree tree) {
|
||||
super(ToolWindowId.INSPECTION, project, robot);
|
||||
myTree = tree;
|
||||
}
|
||||
|
||||
public String getResults() {
|
||||
activate();
|
||||
waitUntilIsVisible();
|
||||
|
||||
return execute(new GuiQuery<String>() {
|
||||
@Override
|
||||
@Nullable
|
||||
protected String executeInEDT() throws Throwable {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
InspectionsFixture.describe(myTree.getRoot(), sb, 0);
|
||||
return sb.toString();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void describe(@NotNull InspectionTreeNode node, @NotNull StringBuilder sb, int depth) {
|
||||
for (int i = 0; i < depth; i++) {
|
||||
sb.append(" ");
|
||||
}
|
||||
sb.append(node.toString());
|
||||
sb.append("\n");
|
||||
|
||||
// The exact order of the results sometimes varies so sort the children alphabetically
|
||||
// instead to ensure stable test output
|
||||
List<InspectionTreeNode> children = Lists.newArrayListWithExpectedSize(node.getChildCount());
|
||||
for (int i = 0, n = node.getChildCount(); i < n; i++) {
|
||||
children.add((InspectionTreeNode)node.getChildAt(i));
|
||||
}
|
||||
Collections.sort(children, new Comparator<InspectionTreeNode>() {
|
||||
@Override
|
||||
public int compare(InspectionTreeNode node1, InspectionTreeNode node2) {
|
||||
return node1.toString().compareTo(node2.toString());
|
||||
}
|
||||
});
|
||||
for (InspectionTreeNode child : children) {
|
||||
describe(child, sb, depth + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.fixtures;
|
||||
|
||||
import org.fest.swing.core.Robot;
|
||||
import org.fest.swing.driver.JComponentDriver;
|
||||
import org.fest.swing.fixture.AbstractJComponentFixture;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
public abstract class JComponentFixture<S, C extends JComponent> extends AbstractJComponentFixture<S, C, JComponentDriver> {
|
||||
public JComponentFixture(@NotNull Class<S> selfType, @NotNull Robot robot, @NotNull Class<? extends C> type) {
|
||||
super(selfType, robot, type);
|
||||
}
|
||||
|
||||
public JComponentFixture(@NotNull Class<S> selfType, @NotNull Robot robot, @Nullable String name, @NotNull Class<? extends C> type) {
|
||||
super(selfType, robot, name, type);
|
||||
}
|
||||
|
||||
public JComponentFixture(@NotNull Class<S> selfType, @NotNull Robot robot, @NotNull C target) {
|
||||
super(selfType, robot, target);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
protected JComponentDriver createDriver(@NotNull Robot robot) {
|
||||
return new JComponentDriver(robot);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.fixtures;
|
||||
|
||||
import com.intellij.openapi.roots.JavadocOrderRootType;
|
||||
import com.intellij.openapi.roots.libraries.Library;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static org.fest.assertions.Assertions.assertThat;
|
||||
|
||||
public class LibraryFixture {
|
||||
@NotNull private final Library myLibrary;
|
||||
|
||||
LibraryFixture(@NotNull Library library) {
|
||||
myLibrary = library;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public LibraryFixture requireJavadocUrls(@NotNull String... urls) {
|
||||
String[] actualUrls = myLibrary.getUrls(JavadocOrderRootType.getInstance());
|
||||
assertThat(actualUrls).as("Javadoc URLs").containsOnly(urls);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.fixtures;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.wm.impl.IdeFrameImpl;
|
||||
import org.fest.swing.core.GenericTypeMatcher;
|
||||
import org.fest.swing.core.Robot;
|
||||
import org.fest.swing.timing.Condition;
|
||||
import org.fest.swing.timing.Pause;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.fest.assertions.Assertions.assertThat;
|
||||
import static org.fest.util.Lists.newArrayList;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
class MenuFixture {
|
||||
@NotNull private final Robot myRobot;
|
||||
@NotNull private final IdeFrameImpl myContainer;
|
||||
|
||||
MenuFixture(@NotNull Robot robot, @NotNull IdeFrameImpl container) {
|
||||
myRobot = robot;
|
||||
myContainer = container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes an action by menu path
|
||||
*
|
||||
* @param path the series of menu names, e.g. {@link invokeActionByMenuPath("Build", "Make Project ")}
|
||||
*/
|
||||
void invokeMenuPath(@NotNull String... path) {
|
||||
JMenuItem menuItem = findActionMenuItem(false, path);
|
||||
myRobot.click(menuItem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes an action by menu path (where each segment is a regular expression). This is particularly
|
||||
* useful when the menu items can change dynamically, such as the labels of Undo actions, Run actions,
|
||||
* etc.
|
||||
*
|
||||
* @param path the series of menu name regular expressions, e.g. {@link invokeActionByMenuPath("Build", "Make( Project)?")}
|
||||
*/
|
||||
void invokeMenuPathRegex(@NotNull String... path) {
|
||||
JMenuItem menuItem = findActionMenuItem(true, path);
|
||||
myRobot.click(menuItem);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private JMenuItem findActionMenuItem(final boolean pathIsRegex, @NotNull String... path) {
|
||||
assertThat(path).isNotEmpty();
|
||||
int segmentCount = path.length;
|
||||
|
||||
// We keep the list of previously found pop-up menus, so we don't look for menu items in the same pop-up more than once.
|
||||
List<JPopupMenu> previouslyFoundPopups = Lists.newArrayList();
|
||||
|
||||
Container root = myContainer;
|
||||
for (int i = 0; i < segmentCount; i++) {
|
||||
final String segment = path[i];
|
||||
assertNotNull(root);
|
||||
JMenuItem found = myRobot.finder().find(root, new GenericTypeMatcher<JMenuItem>(JMenuItem.class) {
|
||||
@Override
|
||||
protected boolean isMatching(@NotNull JMenuItem menuItem) {
|
||||
return pathIsRegex ? menuItem.getText().matches(segment) : segment.equals(menuItem.getText());
|
||||
}
|
||||
});
|
||||
if (root instanceof JPopupMenu) {
|
||||
previouslyFoundPopups.add((JPopupMenu)root);
|
||||
}
|
||||
if (i < segmentCount - 1) {
|
||||
myRobot.click(found);
|
||||
List<JPopupMenu> showingPopupMenus = findShowingPopupMenus(i + 1);
|
||||
showingPopupMenus.removeAll(previouslyFoundPopups);
|
||||
assertThat(showingPopupMenus).hasSize(1);
|
||||
root = showingPopupMenus.get(0);
|
||||
continue;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
throw new AssertionError("Menu item with path " + Arrays.toString(path) + " should have been found already");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private List<JPopupMenu> findShowingPopupMenus(final int expectedCount) {
|
||||
final Ref<List<JPopupMenu>> ref = new Ref<List<JPopupMenu>>();
|
||||
Pause.pause(new Condition("waiting for " + expectedCount + " JPopupMenus to show up") {
|
||||
@Override
|
||||
public boolean test() {
|
||||
List<JPopupMenu> popupMenus = newArrayList(myRobot.finder().findAll(new GenericTypeMatcher<JPopupMenu>(JPopupMenu.class) {
|
||||
@Override
|
||||
protected boolean isMatching(@NotNull JPopupMenu popupMenu) {
|
||||
return popupMenu.isShowing();
|
||||
}
|
||||
}));
|
||||
boolean allFound = popupMenus.size() == expectedCount;
|
||||
if (allFound) {
|
||||
ref.set(popupMenus);
|
||||
}
|
||||
return allFound;
|
||||
}
|
||||
});
|
||||
List<JPopupMenu> popupMenus = ref.get();
|
||||
assertThat(popupMenus).isNotNull().hasSize(expectedCount);
|
||||
return popupMenus;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.fixtures;
|
||||
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import org.fest.swing.core.GenericTypeMatcher;
|
||||
import org.fest.swing.core.Robot;
|
||||
import org.fest.swing.edt.GuiQuery;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import static com.google.common.base.Strings.nullToEmpty;
|
||||
import static com.intellij.tests.gui.framework.GuiTests.waitUntilFound;
|
||||
import static org.fest.swing.edt.GuiActionRunner.execute;
|
||||
|
||||
class MessageDialogFixture extends IdeaDialogFixture<DialogWrapper> implements MessagesFixture.Delegate {
|
||||
@NotNull
|
||||
static MessageDialogFixture findByTitle(@NotNull Robot robot, @NotNull final String title) {
|
||||
final Ref<DialogWrapper> wrapperRef = new Ref<DialogWrapper>();
|
||||
JDialog dialog = waitUntilFound(robot, new GenericTypeMatcher<JDialog>(JDialog.class) {
|
||||
@Override
|
||||
protected boolean isMatching(@NotNull JDialog dialog) {
|
||||
if (!title.equals(dialog.getTitle()) || !dialog.isShowing()) {
|
||||
return false;
|
||||
}
|
||||
DialogWrapper wrapper = getDialogWrapperFrom(dialog, DialogWrapper.class);
|
||||
if (wrapper != null) {
|
||||
String typeName = Messages.class.getName() + "$MessageDialog";
|
||||
if (typeName.equals(wrapper.getClass().getName())) {
|
||||
wrapperRef.set(wrapper);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return new MessageDialogFixture(robot, dialog, wrapperRef.get());
|
||||
}
|
||||
|
||||
private MessageDialogFixture(@NotNull Robot robot, @NotNull JDialog target, @NotNull DialogWrapper dialogWrapper) {
|
||||
super(robot, target, dialogWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getMessage() {
|
||||
final JTextPane textPane = robot().finder().findByType(target(), JTextPane.class);
|
||||
//noinspection ConstantConditions
|
||||
return execute(new GuiQuery<String>() {
|
||||
@Override
|
||||
protected String executeInEDT() throws Throwable {
|
||||
return nullToEmpty(textPane.getText());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.fixtures;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.ui.messages.SheetController;
|
||||
import org.fest.swing.core.GenericTypeMatcher;
|
||||
import org.fest.swing.fixture.ContainerFixture;
|
||||
import org.fest.swing.fixture.JPanelFixture;
|
||||
import org.jdom.Document;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
import org.fest.swing.core.Robot;
|
||||
|
||||
import static com.google.common.base.Strings.nullToEmpty;
|
||||
import static com.intellij.openapi.util.JDOMUtil.loadDocument;
|
||||
import static com.intellij.tests.gui.framework.GuiTests.*;
|
||||
import static org.fest.assertions.Assertions.assertThat;
|
||||
import static org.fest.reflect.core.Reflection.field;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
public class MessagesFixture {
|
||||
@NotNull private final ContainerFixture<? extends Container> myDelegate;
|
||||
|
||||
@NotNull
|
||||
public static MessagesFixture findByTitle(@NotNull Robot robot, @NotNull Container root, @NotNull String title) {
|
||||
if (Messages.canShowMacSheetPanel()) {
|
||||
return new MessagesFixture(findMacSheetByTitle(robot, root, title));
|
||||
}
|
||||
MessageDialogFixture dialog = MessageDialogFixture.findByTitle(robot, title);
|
||||
return new MessagesFixture(dialog);
|
||||
}
|
||||
|
||||
private MessagesFixture(@NotNull ContainerFixture<? extends Container> delegate) {
|
||||
myDelegate = delegate;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public MessagesFixture clickOk() {
|
||||
findAndClickOkButton(myDelegate);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@NotNull
|
||||
public MessagesFixture clickYes() {
|
||||
return click("Yes");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public MessagesFixture click(@NotNull String text) {
|
||||
findAndClickButton(myDelegate, text);
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public MessagesFixture requireMessageContains(@NotNull String message) {
|
||||
String actual = ((Delegate)myDelegate).getMessage();
|
||||
assertThat(actual).contains(message);
|
||||
return this;
|
||||
}
|
||||
|
||||
public void clickCancel() {
|
||||
findAndClickCancelButton(myDelegate);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
static JPanelFixture findMacSheetByTitle(@NotNull Robot robot, @NotNull Container root, @NotNull String title) {
|
||||
JPanel sheetPanel = waitUntilFound(robot, root, new GenericTypeMatcher<JPanel>(JPanel.class) {
|
||||
@Override
|
||||
protected boolean isMatching(@NotNull JPanel panel) {
|
||||
if (panel.getClass().getName().startsWith(SheetController.class.getName()) && panel.isShowing()) {
|
||||
SheetController controller = findSheetController(panel);
|
||||
JPanel sheetPanel = field("mySheetPanel").ofType(JPanel.class).in(controller).get();
|
||||
if (sheetPanel == panel) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
String sheetTitle = getTitle(sheetPanel, robot);
|
||||
assertThat(sheetTitle).as("Sheet title").isEqualTo(title);
|
||||
|
||||
return new MacSheetPanelFixture(robot, sheetPanel);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getTitle(@NotNull JPanel sheetPanel, @NotNull Robot robot) {
|
||||
final JEditorPane messageTextPane = getMessageTextPane(sheetPanel);
|
||||
|
||||
JEditorPane titleTextPane = robot.finder().find(sheetPanel, new GenericTypeMatcher<JEditorPane>(JEditorPane.class) {
|
||||
@Override
|
||||
protected boolean isMatching(@NotNull JEditorPane editorPane) {
|
||||
return editorPane != messageTextPane;
|
||||
}
|
||||
});
|
||||
|
||||
return getHtmlBody(titleTextPane.getText());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public <T extends JComponent> T find(GenericTypeMatcher<T> matcher) {
|
||||
return myDelegate.robot().finder().find(myDelegate.target(), matcher);
|
||||
}
|
||||
|
||||
interface Delegate {
|
||||
@NotNull String getMessage();
|
||||
}
|
||||
|
||||
private static class MacSheetPanelFixture extends JPanelFixture implements Delegate {
|
||||
public MacSheetPanelFixture(@NotNull Robot robot, @NotNull JPanel target) {
|
||||
super(robot, target);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getMessage() {
|
||||
JEditorPane messageTextPane = getMessageTextPane(target());
|
||||
String text = getHtmlBody(messageTextPane.getText());
|
||||
return nullToEmpty(text);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static JEditorPane getMessageTextPane(@NotNull JPanel sheetPanel) {
|
||||
SheetController sheetController = findSheetController(sheetPanel);
|
||||
JEditorPane messageTextPane = field("messageTextPane").ofType(JEditorPane.class).in(sheetController).get();
|
||||
assertNotNull(messageTextPane);
|
||||
return messageTextPane;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static SheetController findSheetController(@NotNull JPanel sheetPanel) {
|
||||
SheetController sheetController = field("this$0").ofType(SheetController.class).in(sheetPanel).get();
|
||||
assertNotNull(sheetController);
|
||||
return sheetController;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getHtmlBody(@NotNull String html) {
|
||||
try {
|
||||
Document document = loadDocument(html);
|
||||
Element rootElement = document.getRootElement();
|
||||
String sheetTitle = rootElement.getChild("body").getText();
|
||||
return sheetTitle.replace("\n", "").trim();
|
||||
}
|
||||
catch (Throwable e) {
|
||||
Logger.getInstance(MessagesFixture.class).info("Failed to parse HTML '" + html + "'", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,375 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.fixtures;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.intellij.ide.errorTreeView.*;
|
||||
import com.intellij.openapi.externalSystem.service.notification.EditableNotificationMessageElement;
|
||||
import com.intellij.openapi.externalSystem.service.notification.NotificationMessageElement;
|
||||
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.wm.ToolWindowId;
|
||||
import com.intellij.pom.Navigatable;
|
||||
import com.intellij.ui.content.Content;
|
||||
import org.fest.swing.core.Robot;
|
||||
import org.fest.swing.edt.GuiQuery;
|
||||
import org.fest.swing.edt.GuiTask;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.HyperlinkEvent;
|
||||
import javax.swing.tree.TreeCellEditor;
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.intellij.openapi.vfs.VfsUtilCore.virtualToIoFile;
|
||||
import static javax.swing.event.HyperlinkEvent.EventType.ACTIVATED;
|
||||
import static junit.framework.Assert.assertNotNull;
|
||||
import static org.fest.assertions.Assertions.assertThat;
|
||||
import static org.fest.reflect.core.Reflection.field;
|
||||
import static org.fest.swing.awt.AWT.visibleCenterOf;
|
||||
import static org.fest.swing.edt.GuiActionRunner.execute;
|
||||
import static org.fest.util.Strings.quote;
|
||||
|
||||
public class MessagesToolWindowFixture extends ToolWindowFixture {
|
||||
MessagesToolWindowFixture(@NotNull Project project, @NotNull Robot robot) {
|
||||
super(ToolWindowId.MESSAGES_WINDOW, project, robot);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ContentFixture getGradleSyncContent() {
|
||||
Content content = getContent("Gradle Sync");
|
||||
assertNotNull(content);
|
||||
return new SyncContentFixture(content);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ContentFixture getGradleBuildContent() {
|
||||
Content content = getContent("Gradle Build");
|
||||
assertNotNull(content);
|
||||
return new BuildContentFixture(content);
|
||||
}
|
||||
|
||||
public abstract static class ContentFixture {
|
||||
@NotNull private final Content myContent;
|
||||
|
||||
private ContentFixture(@NotNull Content content) {
|
||||
myContent = content;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public MessageFixture findMessageContainingText(@NotNull ErrorTreeElementKind kind, @NotNull final String text) {
|
||||
ErrorTreeElement element = doFindMessage(kind, new MessageMatcher() {
|
||||
@Override
|
||||
protected boolean matches(@NotNull String[] lines) {
|
||||
for (String s : lines) {
|
||||
if (s.contains(text)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return createFixture(element);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public MessageFixture findMessage(@NotNull ErrorTreeElementKind kind, @NotNull MessageMatcher matcher) {
|
||||
ErrorTreeElement found = doFindMessage(kind, matcher);
|
||||
return createFixture(found);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected abstract MessageFixture createFixture(@NotNull ErrorTreeElement element);
|
||||
|
||||
@NotNull
|
||||
private ErrorTreeElement doFindMessage(@NotNull final ErrorTreeElementKind kind, @NotNull final MessageMatcher matcher) {
|
||||
ErrorTreeElement found = execute(new GuiQuery<ErrorTreeElement>() {
|
||||
@Override
|
||||
@Nullable
|
||||
protected ErrorTreeElement executeInEDT() throws Throwable {
|
||||
NewErrorTreeViewPanel component = (NewErrorTreeViewPanel)myContent.getComponent();
|
||||
ErrorViewStructure errorView = component.getErrorViewStructure();
|
||||
|
||||
Object root = errorView.getRootElement();
|
||||
return findMessage(errorView, errorView.getChildElements(root), matcher, kind);
|
||||
}
|
||||
});
|
||||
|
||||
assertNotNull(String.format("Failed to find message of type %1$s and matching text %2$s", kind, matcher.toString()), found);
|
||||
return found;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static ErrorTreeElement findMessage(@NotNull ErrorViewStructure errorView,
|
||||
@NotNull ErrorTreeElement[] children,
|
||||
@NotNull MessageMatcher matcher,
|
||||
@NotNull ErrorTreeElementKind kind) {
|
||||
for (ErrorTreeElement child : children) {
|
||||
if (child instanceof GroupingElement) {
|
||||
ErrorTreeElement found = findMessage(errorView, errorView.getChildElements(child), matcher, kind);
|
||||
if (found != null) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
if (kind == child.getKind() && matcher.matches(child.getText())) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static abstract class MessageMatcher {
|
||||
protected abstract boolean matches(@NotNull String[] text);
|
||||
|
||||
@NotNull
|
||||
public static MessageMatcher firstLineStartingWith(@NotNull final String prefix) {
|
||||
return new MessageMatcher() {
|
||||
@Override
|
||||
public boolean matches(@NotNull String[] text) {
|
||||
assertThat(text).isNotEmpty();
|
||||
return text[0].startsWith(prefix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "first line starting with " + quote(prefix);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class SyncContentFixture extends ContentFixture {
|
||||
SyncContentFixture(@NotNull Content content) {
|
||||
super(content);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
protected MessageFixture createFixture(@NotNull ErrorTreeElement element) {
|
||||
return new SyncMessageFixture(myRobot, element);
|
||||
}
|
||||
}
|
||||
|
||||
public class BuildContentFixture extends ContentFixture {
|
||||
BuildContentFixture(@NotNull Content content) {
|
||||
super(content);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
protected MessageFixture createFixture(@NotNull ErrorTreeElement element) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
public abstract static class MessageFixture {
|
||||
private static final Pattern ANCHOR_TAG_PATTERN = Pattern.compile("<a href=\"(.*?)\">([^<]+)</a>");
|
||||
|
||||
@NotNull protected final Robot myRobot;
|
||||
@NotNull protected final ErrorTreeElement myTarget;
|
||||
|
||||
protected MessageFixture(@NotNull Robot robot, @NotNull ErrorTreeElement target) {
|
||||
myRobot = robot;
|
||||
myTarget = target;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public abstract HyperlinkFixture findHyperlink(@NotNull String hyperlinkText);
|
||||
|
||||
@NotNull
|
||||
protected String extractUrl(@NotNull String wholeText, @NotNull String hyperlinkText) {
|
||||
String url = null;
|
||||
Matcher matcher = ANCHOR_TAG_PATTERN.matcher(wholeText);
|
||||
while (matcher.find()) {
|
||||
String anchorText = matcher.group(2);
|
||||
// Text may be spread across multiple lines. Put everything in one line.
|
||||
if (anchorText != null) {
|
||||
anchorText = anchorText.replaceAll("[\\s]+", " ");
|
||||
if (anchorText.equals(hyperlinkText)) {
|
||||
url = matcher.group(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
assertNotNull("Failed to find URL for hyperlink " + quote(hyperlinkText), url);
|
||||
return url;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public MessageFixture requireLocation(@NotNull File filePath, int line) {
|
||||
doRequireLocation(filePath, line);
|
||||
return this;
|
||||
}
|
||||
|
||||
protected void doRequireLocation(@NotNull File expectedFilePath, int line) {
|
||||
assertThat(myTarget).isInstanceOf(NotificationMessageElement.class);
|
||||
NotificationMessageElement element = (NotificationMessageElement)myTarget;
|
||||
|
||||
Navigatable navigatable = element.getNavigatable();
|
||||
assertThat(navigatable).isInstanceOf(OpenFileDescriptor.class);
|
||||
|
||||
OpenFileDescriptor descriptor = (OpenFileDescriptor)navigatable;
|
||||
File actualFilePath = virtualToIoFile(descriptor.getFile());
|
||||
assertThat(actualFilePath).isEqualTo(expectedFilePath);
|
||||
|
||||
assertThat((descriptor.getLine() + 1)).as("line").isEqualTo(line); // descriptor line is zero-based.
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public abstract String getText();
|
||||
}
|
||||
|
||||
public static class SyncMessageFixture extends MessageFixture {
|
||||
SyncMessageFixture(@NotNull Robot robot, @NotNull ErrorTreeElement target) {
|
||||
super(robot, target);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public HyperlinkFixture findHyperlink(@NotNull String hyperlinkText) {
|
||||
Pair<JEditorPane, String> cellEditorAndText = getCellEditorAndText();
|
||||
String url = extractUrl(cellEditorAndText.getSecond(), hyperlinkText);
|
||||
return new SyncHyperlinkFixture(myRobot, url, cellEditorAndText.getFirst());
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getText() {
|
||||
String html = getCellEditorAndText().getSecond();
|
||||
|
||||
int startBodyIndex = html.indexOf("<body>");
|
||||
assertThat(startBodyIndex).isGreaterThanOrEqualTo(0);
|
||||
|
||||
int endBodyIndex = html.indexOf("</body>");
|
||||
assertThat(endBodyIndex).isGreaterThan(startBodyIndex);
|
||||
|
||||
String body = html.substring(startBodyIndex + 6 /* 6 = length of '<body>' */, endBodyIndex);
|
||||
List<String> lines = Splitter.on('\n').omitEmptyStrings().trimResults().splitToList(body);
|
||||
body = Joiner.on(' ').join(lines);
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Pair<JEditorPane, String> getCellEditorAndText() {
|
||||
// There is no specific UI component for a hyperlink in the "Messages" window. Instead we have a JEditorPane with HTML. This method
|
||||
// finds the anchor tags, and matches the text of each of them against the given text. If a matching hyperlink is found, we fire a
|
||||
// HyperlinkEvent, simulating a click on the actual hyperlink.
|
||||
assertThat(myTarget).isInstanceOf(EditableNotificationMessageElement.class);
|
||||
|
||||
final JEditorPane editorComponent = execute(new GuiQuery<JEditorPane>() {
|
||||
@Override
|
||||
protected JEditorPane executeInEDT() throws Throwable {
|
||||
EditableNotificationMessageElement message = (EditableNotificationMessageElement)myTarget;
|
||||
TreeCellEditor cellEditor = message.getRightSelfEditor();
|
||||
return field("editorComponent").ofType(JEditorPane.class).in(cellEditor).get();
|
||||
}
|
||||
});
|
||||
assertNotNull(editorComponent);
|
||||
|
||||
String text = execute(new GuiQuery<String>() {
|
||||
@Override
|
||||
protected String executeInEDT() throws Throwable {
|
||||
return editorComponent.getText();
|
||||
}
|
||||
});
|
||||
assertNotNull(text);
|
||||
|
||||
return Pair.create(editorComponent, text);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract static class HyperlinkFixture {
|
||||
@NotNull protected final Robot myRobot;
|
||||
@NotNull protected final String myUrl;
|
||||
|
||||
protected HyperlinkFixture(@NotNull Robot robot, @NotNull String url) {
|
||||
myRobot = robot;
|
||||
myUrl = url;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public HyperlinkFixture requireUrl(@NotNull String expected) {
|
||||
assertThat(myUrl).as("URL").isEqualTo(expected);
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public HyperlinkFixture click() {
|
||||
click(true);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates a click on the hyperlink. This method returns immediately and does not wait for any UI actions triggered by the click to be
|
||||
* finished.
|
||||
*/
|
||||
public HyperlinkFixture clickAndContinue() {
|
||||
click(false);
|
||||
return this;
|
||||
}
|
||||
|
||||
private void click(boolean synchronous) {
|
||||
if (synchronous) {
|
||||
execute(new GuiTask() {
|
||||
@Override
|
||||
protected void executeInEDT() {
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
doClick();
|
||||
}
|
||||
}.run();
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
//noinspection SSBasedInspection
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
doClick();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void doClick();
|
||||
}
|
||||
|
||||
public static class SyncHyperlinkFixture extends HyperlinkFixture {
|
||||
@NotNull private final JEditorPane myTarget;
|
||||
|
||||
SyncHyperlinkFixture(@NotNull Robot robot, @NotNull String url, @NotNull JEditorPane target) {
|
||||
super(robot, url);
|
||||
myTarget = target;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doClick() {
|
||||
// at least move the mouse where the message is, so we can know that something is happening.
|
||||
myRobot.moveMouse(visibleCenterOf(myTarget));
|
||||
myTarget.fireHyperlinkUpdate(new HyperlinkEvent(this, ACTIVATED, null, myUrl));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.fixtures;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.intellij.ide.projectView.ProjectView;
|
||||
import com.intellij.ide.projectView.ProjectViewNode;
|
||||
import com.intellij.ide.projectView.impl.AbstractProjectViewPane;
|
||||
import com.intellij.ide.projectView.impl.nodes.ExternalLibrariesNode;
|
||||
import com.intellij.ide.projectView.impl.nodes.NamedLibraryElement;
|
||||
import com.intellij.ide.projectView.impl.nodes.NamedLibraryElementNode;
|
||||
import com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode;
|
||||
import com.intellij.ide.util.treeView.AbstractTreeStructure;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.projectRoots.JavaSdk;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.roots.JdkOrderEntry;
|
||||
import com.intellij.openapi.roots.LibraryOrSdkOrderEntry;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiDirectory;
|
||||
import com.intellij.util.ui.tree.TreeUtil;
|
||||
import org.fest.swing.core.Robot;
|
||||
import org.fest.swing.edt.GuiActionRunner;
|
||||
import org.fest.swing.edt.GuiQuery;
|
||||
import org.fest.swing.edt.GuiTask;
|
||||
import org.fest.swing.timing.Condition;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static com.intellij.tests.gui.framework.GuiTests.SHORT_TIMEOUT;
|
||||
import static org.fest.assertions.Assertions.assertThat;
|
||||
import static org.fest.reflect.core.Reflection.field;
|
||||
import static org.fest.swing.timing.Pause.pause;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
public class ProjectViewFixture extends ToolWindowFixture {
|
||||
ProjectViewFixture(@NotNull Project project, @NotNull Robot robot) {
|
||||
super("Project", project, robot);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public PaneFixture selectProjectPane() {
|
||||
activate();
|
||||
final ProjectView projectView = ProjectView.getInstance(myProject);
|
||||
pause(new Condition("Project view is initialized") {
|
||||
@Override
|
||||
public boolean test() {
|
||||
//noinspection ConstantConditions
|
||||
return field("isInitialized").ofType(boolean.class).in(projectView).get();
|
||||
}
|
||||
}, SHORT_TIMEOUT);
|
||||
|
||||
final String id = "ProjectPane";
|
||||
GuiActionRunner.execute(new GuiTask() {
|
||||
@Override
|
||||
protected void executeInEDT() throws Throwable {
|
||||
projectView.changeView(id);
|
||||
}
|
||||
});
|
||||
return new PaneFixture(projectView.getProjectViewPaneById(id));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public PaneFixture selectAndroidPane() {
|
||||
activate();
|
||||
final ProjectView projectView = ProjectView.getInstance(myProject);
|
||||
pause(new Condition("Project view is initialized") {
|
||||
@Override
|
||||
public boolean test() {
|
||||
//noinspection ConstantConditions
|
||||
return field("isInitialized").ofType(boolean.class).in(projectView).get();
|
||||
}
|
||||
}, SHORT_TIMEOUT);
|
||||
|
||||
final String id = "AndroidView";
|
||||
GuiActionRunner.execute(new GuiTask() {
|
||||
@Override
|
||||
protected void executeInEDT() throws Throwable {
|
||||
projectView.changeView(id);
|
||||
}
|
||||
});
|
||||
return new PaneFixture(projectView.getProjectViewPaneById(id));
|
||||
}
|
||||
|
||||
public static class PaneFixture {
|
||||
@NotNull private final AbstractProjectViewPane myPane;
|
||||
|
||||
PaneFixture(@NotNull AbstractProjectViewPane pane) {
|
||||
myPane = pane;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public PaneFixture expand() {
|
||||
GuiActionRunner.execute(new GuiTask() {
|
||||
@Override
|
||||
protected void executeInEDT() throws Throwable {
|
||||
TreeUtil.expandAll(myPane.getTree());
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private AbstractTreeStructure getTreeStructure() {
|
||||
final AtomicReference<AbstractTreeStructure> treeStructureRef = new AtomicReference<AbstractTreeStructure>();
|
||||
pause(new Condition("Tree Structure to be built") {
|
||||
@Override
|
||||
public boolean test() {
|
||||
AbstractTreeStructure treeStructure = GuiActionRunner.execute(new GuiQuery<AbstractTreeStructure>() {
|
||||
@Override
|
||||
protected AbstractTreeStructure executeInEDT() throws Throwable {
|
||||
try {
|
||||
return myPane.getTreeBuilder().getTreeStructure();
|
||||
}
|
||||
catch (NullPointerException e) {
|
||||
// expected;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
treeStructureRef.set(treeStructure);
|
||||
return treeStructure != null;
|
||||
}
|
||||
}, SHORT_TIMEOUT);
|
||||
|
||||
return treeStructureRef.get();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public NodeFixture findExternalLibrariesNode() {
|
||||
final AbstractTreeStructure treeStructure = getTreeStructure();
|
||||
|
||||
ExternalLibrariesNode node = GuiActionRunner.execute(new GuiQuery<ExternalLibrariesNode>() {
|
||||
@Nullable
|
||||
@Override
|
||||
protected ExternalLibrariesNode executeInEDT() throws Throwable {
|
||||
Object[] childElements = treeStructure.getChildElements(treeStructure.getRootElement());
|
||||
for (Object child : childElements) {
|
||||
if (child instanceof ExternalLibrariesNode) {
|
||||
return (ExternalLibrariesNode)child;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
if (node != null) {
|
||||
return new NodeFixture(node, treeStructure);
|
||||
}
|
||||
throw new AssertionError("Unable to find 'External Libraries' node");
|
||||
}
|
||||
|
||||
public void selectByPath(@NotNull final String... paths) {
|
||||
final AbstractTreeStructure treeStructure = getTreeStructure();
|
||||
|
||||
final PsiDirectoryNode node = GuiActionRunner.execute(new GuiQuery<PsiDirectoryNode>() {
|
||||
@Nullable
|
||||
@Override
|
||||
protected PsiDirectoryNode executeInEDT() throws Throwable {
|
||||
Object root = treeStructure.getRootElement();
|
||||
final List<Object> treePath = Lists.newArrayList(root);
|
||||
|
||||
for (String path : paths) {
|
||||
Object[] childElements = treeStructure.getChildElements(root);
|
||||
Object newRoot = null;
|
||||
for (Object child : childElements) {
|
||||
if (child instanceof PsiDirectoryNode) {
|
||||
PsiDirectory dir = ((PsiDirectoryNode)child).getValue();
|
||||
if (dir != null && path.equals(dir.getName())) {
|
||||
newRoot = child;
|
||||
treePath.add(newRoot);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (newRoot != null) {
|
||||
root = newRoot;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (root == treeStructure.getRootElement()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
myPane.expand(treePath.toArray(), true);
|
||||
myPane.select(root, ((PsiDirectoryNode)root).getVirtualFile(), true);
|
||||
return (PsiDirectoryNode)root;
|
||||
}
|
||||
});
|
||||
|
||||
assertNotNull(node);
|
||||
|
||||
pause(new Condition("Node to be selected") {
|
||||
@Override
|
||||
public boolean test() {
|
||||
return node.equals(GuiActionRunner.execute(new GuiQuery<Object>() {
|
||||
@Override
|
||||
protected Object executeInEDT() throws Throwable {
|
||||
DefaultMutableTreeNode selectedNode = myPane.getSelectedNode();
|
||||
if (selectedNode != null) {
|
||||
return selectedNode.getUserObject();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}));
|
||||
}
|
||||
}, SHORT_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
public static class NodeFixture {
|
||||
@NotNull private final ProjectViewNode<?> myNode;
|
||||
@NotNull private final AbstractTreeStructure myTreeStructure;
|
||||
|
||||
NodeFixture(@NotNull ProjectViewNode<?> node, @NotNull AbstractTreeStructure treeStructure) {
|
||||
myNode = node;
|
||||
myTreeStructure = treeStructure;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<NodeFixture> getChildren() {
|
||||
final List<NodeFixture> children = Lists.newArrayList();
|
||||
GuiActionRunner.execute(new GuiTask() {
|
||||
@Override
|
||||
protected void executeInEDT() throws Throwable {
|
||||
for (Object child : myTreeStructure.getChildElements(myNode)) {
|
||||
if (child instanceof ProjectViewNode) {
|
||||
children.add(new NodeFixture((ProjectViewNode<?>)child, myTreeStructure));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return children;
|
||||
}
|
||||
|
||||
public boolean isJdk() {
|
||||
if (myNode instanceof NamedLibraryElementNode) {
|
||||
NamedLibraryElement value = ((NamedLibraryElementNode)myNode).getValue();
|
||||
assertNotNull(value);
|
||||
LibraryOrSdkOrderEntry orderEntry = value.getOrderEntry();
|
||||
if (orderEntry instanceof JdkOrderEntry) {
|
||||
Sdk sdk = ((JdkOrderEntry)orderEntry).getJdk();
|
||||
return sdk.getSdkType() instanceof JavaSdk;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public NodeFixture requireDirectory(@NotNull String name) {
|
||||
assertThat(myNode).isInstanceOf(PsiDirectoryNode.class);
|
||||
VirtualFile file = myNode.getVirtualFile();
|
||||
assertNotNull(file);
|
||||
assertThat(file.getName()).isEqualTo(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Strings.nullToEmpty(myNode.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.fixtures;
|
||||
|
||||
import com.intellij.openapi.actionSystem.impl.SimpleDataContext;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.refactoring.RefactoringBundle;
|
||||
import com.intellij.refactoring.rename.RenameDialog;
|
||||
import com.intellij.refactoring.rename.RenameHandler;
|
||||
import com.intellij.ui.EditorTextField;
|
||||
import org.fest.swing.core.GenericTypeMatcher;
|
||||
import org.fest.swing.core.Robot;
|
||||
import org.fest.swing.edt.GuiQuery;
|
||||
import org.fest.swing.edt.GuiTask;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import static com.intellij.tests.gui.framework.GuiTests.waitUntilFound;
|
||||
import static org.fest.reflect.core.Reflection.field;
|
||||
import static org.fest.swing.edt.GuiActionRunner.execute;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
public class RenameDialogFixture extends IdeaDialogFixture<RenameDialog> {
|
||||
|
||||
public RenameDialogFixture(@NotNull Robot robot, @NotNull JDialog target, @NotNull RenameDialog dialogWrapper) {
|
||||
super(robot, target, dialogWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts 'rename' refactoring for the given data.
|
||||
* <p/>
|
||||
* <b>Note:</b> proper way would be to write dedicated 'project view fixture' and emulate user actions like 'expand nodes until
|
||||
* we find a target one' but that IJ component (project view) is rather complex and it's much easier to start the refactoring
|
||||
* programmatically.
|
||||
*
|
||||
* @param element target PSI element for which 'rename' refactoring should begin
|
||||
* @param handler rename refactoring handler to use
|
||||
* @param robot robot to use
|
||||
* @return a fixture for the 'rename dialog' which occurs when we start 'rename' refactoring for the given data
|
||||
*/
|
||||
@NotNull
|
||||
public static RenameDialogFixture startFor(@NotNull final PsiElement element,
|
||||
@NotNull final RenameHandler handler,
|
||||
@NotNull Robot robot)
|
||||
{
|
||||
// We use SwingUtilities instead of FEST here because RenameDialog is modal and GuiActionRunner doesn't return until
|
||||
//noinspection SSBasedInspection
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
handler.invoke(element.getProject(), new PsiElement[] { element }, SimpleDataContext.getProjectContext(element.getProject()));
|
||||
}
|
||||
});
|
||||
final Ref<RenameDialog> ref = new Ref<RenameDialog>();
|
||||
JDialog dialog = waitUntilFound(robot, new GenericTypeMatcher<JDialog>(JDialog.class) {
|
||||
@Override
|
||||
protected boolean isMatching(@NotNull JDialog dialog) {
|
||||
if (!RefactoringBundle.message("rename.title").equals(dialog.getTitle()) || !dialog.isShowing()) {
|
||||
return false;
|
||||
}
|
||||
RenameDialog renameDialog = getDialogWrapperFrom(dialog, RenameDialog.class);
|
||||
if (renameDialog == null) {
|
||||
return false;
|
||||
}
|
||||
ref.set(renameDialog);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return new RenameDialogFixture(robot, dialog, ref.get());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getNewName() {
|
||||
//noinspection ConstantConditions
|
||||
return execute(new GuiQuery<String>() {
|
||||
@Override
|
||||
protected String executeInEDT() throws Throwable {
|
||||
String text = robot().finder().findByType(target(), EditorTextField.class).getText();
|
||||
return text == null ? "" : text;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setNewName(@NotNull final String newName) {
|
||||
execute(new GuiTask() {
|
||||
@Override
|
||||
protected void executeInEDT() throws Throwable {
|
||||
robot().finder().findByType(target(), EditorTextField.class).setText(newName);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to check if a warning exists at the target 'rename dialog'
|
||||
*
|
||||
* @param warningText <code>null</code> as a wildcard to match any non-empty warning text;
|
||||
* non-null text which is evaluated to be a part of the target dialog's warning text
|
||||
* @return <code>true</code> if the target 'rename dialog' has a warning and given text matches it according to the
|
||||
* rules described above; <code>false</code> otherwise
|
||||
*/
|
||||
public boolean warningExists(@Nullable final String warningText) {
|
||||
//noinspection ConstantConditions
|
||||
return execute(new GuiQuery<Boolean>() {
|
||||
@Override
|
||||
protected Boolean executeInEDT() throws Throwable {
|
||||
JComponent errorTextPane = field("myErrorText").ofType(JComponent.class).in(getDialogWrapper()).get();
|
||||
assertNotNull(errorTextPane);
|
||||
if (!errorTextPane.isVisible()) {
|
||||
return false;
|
||||
}
|
||||
JLabel errorLabel = field("myLabel").ofType(JLabel.class).in(errorTextPane).get();
|
||||
assertNotNull(errorLabel);
|
||||
String text = errorLabel.getText();
|
||||
if (isNullOrEmpty(text)) {
|
||||
return false;
|
||||
}
|
||||
return warningText == null || text.contains(warningText);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.fixtures;
|
||||
|
||||
import com.android.tools.lint.detector.api.TextFormat;
|
||||
import com.intellij.refactoring.rename.RenameDialog;
|
||||
import com.intellij.refactoring.ui.ConflictsDialog;
|
||||
import com.intellij.ui.EditorTextField;
|
||||
import org.fest.swing.core.Robot;
|
||||
import org.fest.swing.core.matcher.JTextComponentMatcher;
|
||||
import org.fest.swing.edt.GuiActionRunner;
|
||||
import org.fest.swing.edt.GuiQuery;
|
||||
import org.fest.swing.edt.GuiTask;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.text.JTextComponent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.intellij.tests.gui.framework.GuiTests.findAndClickButton;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class RenameRefactoringDialogFixture extends IdeaDialogFixture<RenameDialog> {
|
||||
@NotNull
|
||||
public static RenameRefactoringDialogFixture find(@NotNull Robot robot) {
|
||||
return new RenameRefactoringDialogFixture(robot, find(robot, RenameDialog.class));
|
||||
}
|
||||
|
||||
private RenameRefactoringDialogFixture(@NotNull Robot robot, @NotNull DialogAndWrapper<RenameDialog> dialogAndWrapper) {
|
||||
super(robot, dialogAndWrapper);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public RenameRefactoringDialogFixture setNewName(@NotNull final String newName) {
|
||||
final EditorTextField field = robot().finder().findByType(target(), EditorTextField.class);
|
||||
GuiActionRunner.execute(new GuiTask() {
|
||||
@Override
|
||||
protected void executeInEDT() throws Throwable {
|
||||
field.requestFocus();
|
||||
}
|
||||
});
|
||||
robot().pressAndReleaseKey(KeyEvent.VK_BACK_SPACE); // to make sure we don't append to existing item on Linux
|
||||
robot().enterText(newName);
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public RenameRefactoringDialogFixture clickRefactor() {
|
||||
findAndClickButton(this, "Refactor");
|
||||
return this;
|
||||
}
|
||||
|
||||
public static class ConflictsDialogFixture extends IdeaDialogFixture<ConflictsDialog> {
|
||||
protected ConflictsDialogFixture(@NotNull Robot robot, @NotNull DialogAndWrapper<ConflictsDialog> dialogAndWrapper) {
|
||||
super(robot, dialogAndWrapper);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static ConflictsDialogFixture find(@NotNull Robot robot) {
|
||||
return new ConflictsDialogFixture(robot, find(robot, ConflictsDialog.class));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ConflictsDialogFixture clickContinue() {
|
||||
findAndClickButton(this, "Continue");
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getHtml() {
|
||||
final JTextComponent component = robot().finder().find(target(), JTextComponentMatcher.any());
|
||||
return GuiActionRunner.execute(new GuiQuery<String>() {
|
||||
@Override
|
||||
protected String executeInEDT() throws Throwable {
|
||||
return component.getText();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
String html = getHtml();
|
||||
return TextFormat.HTML.convertTo(html, TextFormat.TEXT).trim();
|
||||
}
|
||||
|
||||
public void requireMessageText(@NotNull String text) {
|
||||
assertEquals(text, getText());
|
||||
}
|
||||
|
||||
public void requireMessageTextContains(@NotNull String text) {
|
||||
assertTrue(getText() + " does not contain expected message fragment " + text, getText().contains(text));
|
||||
}
|
||||
|
||||
public void requireMessageTextMatches(@NotNull String regexp) {
|
||||
assertTrue(getText() + " does not match " + regexp, Pattern.matches(regexp, getText()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.fixtures;
|
||||
|
||||
import org.fest.swing.core.Robot;
|
||||
import org.fest.swing.core.matcher.DialogMatcher;
|
||||
import org.fest.swing.core.matcher.JLabelMatcher;
|
||||
import org.fest.swing.driver.JTextComponentDriver;
|
||||
import org.fest.swing.fixture.ContainerFixture;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.text.JTextComponent;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
import static com.intellij.tests.gui.framework.GuiTests.findAndClickOkButton;
|
||||
|
||||
public class ResourceChooserDialogFixture extends ComponentFixture<ResourceChooserDialogFixture, Dialog>
|
||||
implements ContainerFixture<Dialog> {
|
||||
|
||||
@NotNull
|
||||
public static ResourceChooserDialogFixture findDialog(@NotNull Robot robot) {
|
||||
Dialog jDialog = robot.finder().find(DialogMatcher.withTitle("Select Resource Directory").andShowing());
|
||||
|
||||
return new ResourceChooserDialogFixture(robot, jDialog);
|
||||
}
|
||||
|
||||
private ResourceChooserDialogFixture(@NotNull Robot robot, Dialog target) {
|
||||
super(ResourceChooserDialogFixture.class, robot, target);
|
||||
}
|
||||
|
||||
public void setDirectoryName(@NotNull String directory) {
|
||||
Container parent = robot().finder().find(target(), JLabelMatcher.withText("Directory name:")).getParent();
|
||||
|
||||
JTextComponent directoryField = robot().finder().findByType(parent, JTextComponent.class, true);
|
||||
JTextComponentDriver driver = new JTextComponentDriver(robot());
|
||||
driver.selectAll(directoryField);
|
||||
driver.setText(directoryField, directory);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ResourceChooserDialogFixture clickOK() {
|
||||
findAndClickOkButton(this);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.fixtures;
|
||||
|
||||
import com.intellij.openapi.actionSystem.impl.ActionToolbarImpl;
|
||||
import org.fest.swing.core.ComponentFinder;
|
||||
import org.fest.swing.core.GenericTypeMatcher;
|
||||
import org.fest.swing.core.Robot;
|
||||
import org.fest.swing.edt.GuiQuery;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import static com.intellij.openapi.actionSystem.ActionPlaces.MAIN_TOOLBAR;
|
||||
import static org.fest.reflect.core.Reflection.field;
|
||||
import static org.fest.swing.edt.GuiActionRunner.execute;
|
||||
|
||||
public class RunConfigurationComboBoxFixture extends JComponentFixture<RunConfigurationComboBoxFixture, JButton> {
|
||||
@NotNull
|
||||
static RunConfigurationComboBoxFixture find(@NotNull final IdeFrameFixture parent) {
|
||||
ComponentFinder finder = parent.robot().finder();
|
||||
ActionToolbarImpl toolbar = finder.find(parent.target(), new GenericTypeMatcher<ActionToolbarImpl>(ActionToolbarImpl.class) {
|
||||
@Override
|
||||
protected boolean isMatching(@NotNull ActionToolbarImpl toolbar) {
|
||||
String place = field("myPlace").ofType(String.class).in(toolbar).get();
|
||||
return MAIN_TOOLBAR.equals(place);
|
||||
}
|
||||
});
|
||||
|
||||
JButton button = finder.find(toolbar, new GenericTypeMatcher<JButton>(JButton.class) {
|
||||
@Override
|
||||
protected boolean isMatching(@NotNull JButton button) {
|
||||
return button.getClass().getSimpleName().equals("ComboBoxButton");
|
||||
}
|
||||
});
|
||||
return new RunConfigurationComboBoxFixture(parent.robot(), button);
|
||||
}
|
||||
|
||||
private RunConfigurationComboBoxFixture(@NotNull Robot robot, @NotNull JButton target) {
|
||||
super(RunConfigurationComboBoxFixture.class, robot, target);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getText() {
|
||||
return execute(new GuiQuery<String>() {
|
||||
@Override
|
||||
protected String executeInEDT() throws Throwable {
|
||||
return target().getText();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.fixtures;
|
||||
|
||||
import com.intellij.execution.configurations.ConfigurationType;
|
||||
import com.intellij.tests.gui.matcher.ClassNameMatcher;
|
||||
import com.intellij.ui.components.JBList;
|
||||
import com.intellij.ui.popup.PopupFactoryImpl.ActionItem;
|
||||
import com.intellij.ui.treeStructure.Tree;
|
||||
import org.fest.swing.cell.JTreeCellReader;
|
||||
import org.fest.swing.core.GenericTypeMatcher;
|
||||
import org.fest.swing.core.Robot;
|
||||
import org.fest.swing.core.matcher.DialogMatcher;
|
||||
import org.fest.swing.core.matcher.JButtonMatcher;
|
||||
import org.fest.swing.driver.BasicJListCellReader;
|
||||
import org.fest.swing.edt.GuiActionRunner;
|
||||
import org.fest.swing.edt.GuiTask;
|
||||
import org.fest.swing.fixture.JListFixture;
|
||||
import org.fest.swing.fixture.JTreeFixture;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.KeyEvent;
|
||||
|
||||
import static com.intellij.tests.gui.framework.GuiTests.waitUntilFound;
|
||||
import static org.fest.reflect.core.Reflection.method;
|
||||
|
||||
/**
|
||||
* Controls the Run Configurations dialog
|
||||
*/
|
||||
public class RunConfigurationsDialogFixture extends ComponentFixture<RunConfigurationsDialogFixture, JDialog> {
|
||||
|
||||
public RunConfigurationsDialogFixture(@NotNull Robot robot, @NotNull JDialog target) {
|
||||
super(RunConfigurationsDialogFixture.class, robot, target);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static RunConfigurationsDialogFixture find(@NotNull Robot robot) {
|
||||
JDialog frame = waitUntilFound(robot, new GenericTypeMatcher<JDialog>(JDialog.class) {
|
||||
@Override
|
||||
protected boolean isMatching(@NotNull JDialog dialog) {
|
||||
return "Run/Debug Configurations".equals(dialog.getTitle()) && dialog.isShowing();
|
||||
}
|
||||
});
|
||||
return new RunConfigurationsDialogFixture(robot, frame);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private JButton findButtonByText(@NotNull String text) {
|
||||
return robot().finder().find(target(), JButtonMatcher.withText(text).andShowing());
|
||||
}
|
||||
|
||||
public void save() {
|
||||
robot().click(findButtonByText("OK"));
|
||||
}
|
||||
|
||||
public JTreeFixture getTreeFixture() {
|
||||
Tree tree = robot().finder().findByType(target(), Tree.class, true);
|
||||
JTreeFixture fixture = new JTreeFixture(robot(), tree);
|
||||
fixture.replaceCellReader(new RunConfigurationsJTreeCellReader());
|
||||
return fixture;
|
||||
}
|
||||
|
||||
public void select(String path) {
|
||||
getTreeFixture().selectPath(path);
|
||||
}
|
||||
|
||||
public void useGradleAwareMake() {
|
||||
JBList stepsList = robot().finder().findByType(target(), JBList.class);
|
||||
JListFixture stepsFixture = new JListFixture(robot(), stepsList);
|
||||
// Find ActionToolbarImpl and ActionButtons inside.
|
||||
if (!stepsFixture.contents()[0].equals("Make")) {
|
||||
throw new IllegalStateException("There should be only only one make step, 'Make'.");
|
||||
}
|
||||
|
||||
stepsFixture.clickItem(0);
|
||||
JPanel beforeRunStepsPanel = robot().finder().find(
|
||||
target(),
|
||||
ClassNameMatcher.forClass("com.intellij.execution.impl.BeforeRunStepsPanel", JPanel.class, true));
|
||||
|
||||
ActionButtonFixture.findByText("Remove", robot(), beforeRunStepsPanel).click();
|
||||
ActionButtonFixture.findByText("Add", robot(), beforeRunStepsPanel).click();
|
||||
|
||||
JBList popupList = robot().finder().find(
|
||||
target(),
|
||||
ClassNameMatcher.forClass("com.intellij.ui.popup.list.ListPopupImpl$MyList", JBList.class, true));
|
||||
|
||||
click(popupList, "Gradle-aware Make");
|
||||
|
||||
Dialog gradleMakeDialog = robot().finder().find(DialogMatcher.withTitle("Select Gradle Task").andShowing());
|
||||
robot().click(robot().finder().find(gradleMakeDialog, JButtonMatcher.withText("OK")));
|
||||
}
|
||||
|
||||
private void click(final JBList popupList, String text) {
|
||||
JListFixture popupFixture = new JListFixture(robot(), popupList);
|
||||
popupFixture.replaceCellReader(new MakeStepsCellReader());
|
||||
final int index = popupFixture.item(text).index();
|
||||
|
||||
// For some reason calling popupFixture.click(...) doesn't work, but this does:
|
||||
GuiActionRunner.execute(new GuiTask() {
|
||||
@Override
|
||||
protected void executeInEDT() throws Throwable {
|
||||
popupList.setSelectedIndex(index);
|
||||
}
|
||||
});
|
||||
robot().pressAndReleaseKey(KeyEvent.VK_ENTER);
|
||||
}
|
||||
|
||||
/** {@link JTreeCellReader} that works with this particular tree. */
|
||||
private static class RunConfigurationsJTreeCellReader implements JTreeCellReader {
|
||||
@Nullable
|
||||
@Override
|
||||
public String valueAt(@NotNull JTree tree, Object modelValue) {
|
||||
Object userObject = method("getUserObject").withReturnType(Object.class).in(modelValue).invoke();
|
||||
if (userObject == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (userObject instanceof ConfigurationType) {
|
||||
return ((ConfigurationType)userObject).getDisplayName();
|
||||
}
|
||||
|
||||
return userObject.toString();
|
||||
}
|
||||
}
|
||||
|
||||
private static class MakeStepsCellReader extends BasicJListCellReader {
|
||||
@Nullable
|
||||
@Override
|
||||
public String valueAt(@NotNull JList list, int index) {
|
||||
Object element = list.getModel().getElementAt(index);
|
||||
if (element instanceof ActionItem) {
|
||||
return ((ActionItem)element).getText();
|
||||
}
|
||||
return super.valueAt(list, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.fixtures;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class RunToolWindowFixture extends ExecutionToolWindowFixture {
|
||||
public RunToolWindowFixture(@NotNull IdeFrameFixture frameFixture) {
|
||||
super("Run", frameFixture);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.fixtures;
|
||||
|
||||
import com.intellij.tests.gui.driver.SearchTextFieldDriver;
|
||||
import com.intellij.ui.SearchTextField;
|
||||
import org.fest.swing.core.Robot;
|
||||
import org.fest.swing.fixture.AbstractJComponentFixture;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class SearchTextFieldFixture extends AbstractJComponentFixture<SearchTextFieldFixture, SearchTextField, SearchTextFieldDriver> {
|
||||
public SearchTextFieldFixture(@NotNull Robot robot,
|
||||
@NotNull SearchTextField target) {
|
||||
super(SearchTextFieldFixture.class, robot, target);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public SearchTextFieldFixture enterText(@NotNull String text) {
|
||||
driver().enterText(target(), text);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public SearchTextFieldFixture requireText(@NotNull String text) {
|
||||
driver().requireText(target(), text);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected SearchTextFieldDriver createDriver(Robot robot) {
|
||||
return new SearchTextFieldDriver(robot);
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.fixtures;
|
||||
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import org.fest.swing.core.GenericTypeMatcher;
|
||||
import org.fest.swing.core.Robot;
|
||||
import org.fest.swing.fixture.JRadioButtonFixture;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import static com.intellij.tests.gui.framework.GuiTests.findAndClickOkButton;
|
||||
import static com.intellij.tests.gui.framework.GuiTests.waitUntilFound;
|
||||
|
||||
public class SelectRefactoringDialogFixture extends IdeaDialogFixture<DialogWrapper> {
|
||||
@NotNull
|
||||
public static SelectRefactoringDialogFixture findByTitle(@NotNull Robot robot) {
|
||||
final Ref<DialogWrapper> wrapperRef = new Ref<DialogWrapper>();
|
||||
JDialog dialog = waitUntilFound(robot, new GenericTypeMatcher<JDialog>(JDialog.class) {
|
||||
@Override
|
||||
protected boolean isMatching(@NotNull JDialog dialog) {
|
||||
if (!"Select Refactoring".equals(dialog.getTitle()) || !dialog.isShowing()) {
|
||||
return false;
|
||||
}
|
||||
DialogWrapper wrapper = getDialogWrapperFrom(dialog, DialogWrapper.class);
|
||||
if (wrapper != null) {
|
||||
wrapperRef.set(wrapper);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return new SelectRefactoringDialogFixture(robot, dialog, wrapperRef.get());
|
||||
}
|
||||
|
||||
public void selectRenameModule() {
|
||||
JRadioButton renameModuleCheckbox = robot().finder().find(target(), new GenericTypeMatcher<JRadioButton>(JRadioButton.class) {
|
||||
@Override
|
||||
protected boolean isMatching(@NotNull JRadioButton checkBox) {
|
||||
return "Rename module".equals(checkBox.getText());
|
||||
}
|
||||
});
|
||||
|
||||
JRadioButtonFixture renameModuleRadioButton = new JRadioButtonFixture(robot(), renameModuleCheckbox);
|
||||
renameModuleRadioButton.select();
|
||||
}
|
||||
|
||||
public void clickOk() {
|
||||
findAndClickOkButton(this);
|
||||
}
|
||||
|
||||
private SelectRefactoringDialogFixture(@NotNull Robot robot, @NotNull JDialog target, @NotNull DialogWrapper dialogWrapper) {
|
||||
super(robot, target, dialogWrapper);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.fixtures;
|
||||
|
||||
public class SelectSdkDialogFixture {
|
||||
//extends IdeaDialogFixture<SelectSdkDialog> {
|
||||
//@NotNull
|
||||
//public static SelectSdkDialogFixture find(@NotNull Robot robot) {
|
||||
// return new SelectSdkDialogFixture(robot, find(robot, SelectSdkDialog.class));
|
||||
//}
|
||||
//
|
||||
//private SelectSdkDialogFixture(@NotNull Robot robot, @NotNull DialogAndWrapper<SelectSdkDialog> dialogAndWrapper) {
|
||||
// super(robot, dialogAndWrapper);
|
||||
//}
|
||||
//
|
||||
//@NotNull
|
||||
//public SelectSdkDialogFixture setJdkPath(@NotNull final File path) {
|
||||
// final JLabel label = robot().finder().find(target(), JLabelMatcher.withText("Select Java JDK:").andShowing());
|
||||
// execute(new GuiTask() {
|
||||
// @Override
|
||||
// protected void executeInEDT() throws Throwable {
|
||||
// Component textField = label.getLabelFor();
|
||||
// assertThat(textField).isInstanceOf(JTextField.class);
|
||||
// ((JTextField)textField).setText(path.getPath());
|
||||
// }
|
||||
// });
|
||||
// return this;
|
||||
//}
|
||||
//
|
||||
//@NotNull
|
||||
//public SelectSdkDialogFixture clickOk() {
|
||||
// findAndClickOkButton(this);
|
||||
// return this;
|
||||
//}
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.fixtures;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.wm.ToolWindow;
|
||||
import com.intellij.openapi.wm.ToolWindowManager;
|
||||
import com.intellij.ui.content.Content;
|
||||
import org.fest.swing.core.Robot;
|
||||
import org.fest.swing.edt.GuiQuery;
|
||||
import org.fest.swing.edt.GuiTask;
|
||||
import org.fest.swing.timing.Condition;
|
||||
import org.fest.swing.timing.Timeout;
|
||||
import org.fest.swing.util.TextMatcher;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import static com.intellij.tests.gui.framework.GuiTests.SHORT_TIMEOUT;
|
||||
import static com.intellij.tests.gui.framework.GuiTests.THIRTY_SEC_TIMEOUT;
|
||||
import static org.fest.swing.edt.GuiActionRunner.execute;
|
||||
import static org.fest.swing.timing.Pause.pause;
|
||||
|
||||
public abstract class ToolWindowFixture {
|
||||
@NotNull protected final String myToolWindowId;
|
||||
@NotNull protected final Project myProject;
|
||||
@NotNull protected final Robot myRobot;
|
||||
@NotNull protected final ToolWindow myToolWindow;
|
||||
|
||||
protected ToolWindowFixture(@NotNull final String toolWindowId, @NotNull final Project project, @NotNull Robot robot) {
|
||||
myToolWindowId = toolWindowId;
|
||||
myProject = project;
|
||||
final Ref<ToolWindow> toolWindowRef = new Ref<ToolWindow>();
|
||||
pause(new Condition("Find tool window with ID '" + toolWindowId + "'") {
|
||||
@Override
|
||||
public boolean test() {
|
||||
ToolWindow toolWindow = ToolWindowManager.getInstance(project).getToolWindow(toolWindowId);
|
||||
toolWindowRef.set(toolWindow);
|
||||
return toolWindow != null;
|
||||
}
|
||||
}, SHORT_TIMEOUT);
|
||||
myRobot = robot;
|
||||
myToolWindow = toolWindowRef.get();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected Content getContent(@NotNull final String displayName) {
|
||||
activateAndWaitUntilIsVisible();
|
||||
final Ref<Content> contentRef = new Ref<Content>();
|
||||
pause(new Condition("finding content '" + displayName + "'") {
|
||||
@Override
|
||||
public boolean test() {
|
||||
Content[] contents = getContents();
|
||||
for (Content content : contents) {
|
||||
if (displayName.equals(content.getDisplayName())) {
|
||||
contentRef.set(content);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}, SHORT_TIMEOUT);
|
||||
return contentRef.get();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected Content getContent(@NotNull final String displayName, @NotNull Timeout timeout) {
|
||||
long now = System.currentTimeMillis();
|
||||
long budget = timeout.duration();
|
||||
activateAndWaitUntilIsVisible(Timeout.timeout(budget));
|
||||
long revisedNow = System.currentTimeMillis();
|
||||
budget -= (revisedNow - now);
|
||||
final Ref<Content> contentRef = new Ref<Content>();
|
||||
pause(new Condition("finding content with display name " + displayName) {
|
||||
@Override
|
||||
public boolean test() {
|
||||
Content[] contents = getContents();
|
||||
for (Content content : contents) {
|
||||
if (displayName.equals(content.getDisplayName())) {
|
||||
contentRef.set(content);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}, Timeout.timeout(budget));
|
||||
return contentRef.get();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected Content getContent(@NotNull final TextMatcher displayNameMatcher) {
|
||||
return getContent(displayNameMatcher, SHORT_TIMEOUT);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected Content getContent(@NotNull final TextMatcher displayNameMatcher, @NotNull Timeout timeout) {
|
||||
long now = System.currentTimeMillis();
|
||||
long budget = timeout.duration();
|
||||
activateAndWaitUntilIsVisible(Timeout.timeout(budget));
|
||||
long revisedNow = System.currentTimeMillis();
|
||||
budget -= (revisedNow - now);
|
||||
now = revisedNow;
|
||||
final Ref<Content> contentRef = new Ref<Content>();
|
||||
pause(new Condition("finding content matching " + displayNameMatcher.formattedValues()) {
|
||||
@Override
|
||||
public boolean test() {
|
||||
Content[] contents = getContents();
|
||||
for (Content content : contents) {
|
||||
String displayName = content.getDisplayName();
|
||||
if (displayNameMatcher.isMatching(displayName)) {
|
||||
contentRef.set(content);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}, Timeout.timeout(budget));
|
||||
return contentRef.get();
|
||||
}
|
||||
|
||||
private void activateAndWaitUntilIsVisible() {
|
||||
activateAndWaitUntilIsVisible(SHORT_TIMEOUT);
|
||||
}
|
||||
|
||||
private void activateAndWaitUntilIsVisible(@NotNull Timeout timeout) {
|
||||
long now = System.currentTimeMillis();
|
||||
long budget = timeout.duration();
|
||||
activate();
|
||||
budget -= System.currentTimeMillis() - now;
|
||||
waitUntilIsVisible(Timeout.timeout(budget));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Content[] getContents() {
|
||||
return myToolWindow.getContentManager().getContents();
|
||||
}
|
||||
|
||||
protected boolean isActive() {
|
||||
//noinspection ConstantConditions
|
||||
return execute(new GuiQuery<Boolean>() {
|
||||
@Override
|
||||
protected Boolean executeInEDT() throws Throwable {
|
||||
return myToolWindow.isActive();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void activate() {
|
||||
if (isActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Callback callback = new Callback();
|
||||
execute(new GuiTask() {
|
||||
@Override
|
||||
protected void executeInEDT() throws Throwable {
|
||||
myToolWindow.activate(callback);
|
||||
}
|
||||
});
|
||||
|
||||
pause(new Condition("Wait for ToolWindow '" + myToolWindowId + "' to be activated") {
|
||||
@Override
|
||||
public boolean test() {
|
||||
return callback.finished;
|
||||
}
|
||||
}, SHORT_TIMEOUT);
|
||||
}
|
||||
|
||||
protected void waitUntilIsVisible() {
|
||||
waitUntilIsVisible(THIRTY_SEC_TIMEOUT);
|
||||
}
|
||||
|
||||
protected void waitUntilIsVisible(@NotNull Timeout timeout) {
|
||||
pause(new Condition("Wait for ToolWindow '" + myToolWindowId + "' to be visible") {
|
||||
@Override
|
||||
public boolean test() {
|
||||
if (!isActive()) {
|
||||
activate();
|
||||
}
|
||||
return isVisible();
|
||||
}
|
||||
}, timeout);
|
||||
}
|
||||
|
||||
private boolean isVisible() {
|
||||
//noinspection ConstantConditions
|
||||
return execute(new GuiQuery<Boolean>() {
|
||||
@Override
|
||||
protected Boolean executeInEDT() throws Throwable {
|
||||
if (!myToolWindow.isVisible()) {
|
||||
return false;
|
||||
}
|
||||
JComponent component = myToolWindow.getComponent();
|
||||
return component.isVisible() && component.isShowing();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static class Callback implements Runnable {
|
||||
volatile boolean finished;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
finished = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.fixtures;
|
||||
|
||||
import com.intellij.execution.testframework.AbstractTestProxy;
|
||||
import com.intellij.execution.testframework.TestFrameworkRunningModel;
|
||||
import com.intellij.execution.testframework.TestTreeView;
|
||||
import org.fest.swing.timing.Condition;
|
||||
import org.fest.swing.timing.Pause;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Fixture for the tree widget, on the left hand side of "Run" window (when running tests).
|
||||
*/
|
||||
public class UnitTestTreeFixture {
|
||||
private ExecutionToolWindowFixture.ContentFixture myContentFixture;
|
||||
private final TestTreeView myTreeView;
|
||||
|
||||
public UnitTestTreeFixture(@NotNull ExecutionToolWindowFixture.ContentFixture contentFixture,
|
||||
@NotNull TestTreeView treeView) {
|
||||
myContentFixture = contentFixture;
|
||||
myTreeView = treeView;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public TestFrameworkRunningModel getModel() {
|
||||
Pause.pause(new Condition("Wait for the test results model.") {
|
||||
@Override
|
||||
public boolean test() {
|
||||
return myTreeView.getData(TestTreeView.MODEL_DATA_KEY.getName()) != null;
|
||||
}
|
||||
});
|
||||
|
||||
return TestTreeView.MODEL_DATA_KEY.getData(myTreeView);
|
||||
}
|
||||
|
||||
public boolean isAllTestsPassed() {
|
||||
return getFailingTestsCount() == 0;
|
||||
}
|
||||
|
||||
public int getFailingTestsCount() {
|
||||
int count = 0;
|
||||
AbstractTestProxy root = getModel().getRoot();
|
||||
for (AbstractTestProxy test : root.getAllTests()) {
|
||||
if (test.isLeaf() && test.isDefect()) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public int getAllTestsCount() {
|
||||
int count = 0;
|
||||
AbstractTestProxy root = getModel().getRoot();
|
||||
for (AbstractTestProxy test : root.getAllTests()) {
|
||||
if (test.isLeaf()) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ExecutionToolWindowFixture.ContentFixture getContent() {
|
||||
return myContentFixture;
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.fixtures;
|
||||
|
||||
import com.intellij.openapi.wm.impl.welcomeScreen.FlatWelcomeFrame;
|
||||
import org.fest.swing.core.Robot;
|
||||
import org.fest.swing.exception.ComponentLookupException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
|
||||
public class WelcomeFrameFixture extends ComponentFixture<WelcomeFrameFixture, FlatWelcomeFrame> {
|
||||
@NotNull
|
||||
public static WelcomeFrameFixture find(@NotNull Robot robot) {
|
||||
for (Frame frame : Frame.getFrames()) {
|
||||
if (frame instanceof FlatWelcomeFrame && frame.isShowing()) {
|
||||
return new WelcomeFrameFixture(robot, (FlatWelcomeFrame)frame);
|
||||
}
|
||||
}
|
||||
throw new ComponentLookupException("Unable to find 'Welcome' window");
|
||||
}
|
||||
|
||||
private WelcomeFrameFixture(@NotNull Robot robot, @NotNull FlatWelcomeFrame target) {
|
||||
super(WelcomeFrameFixture.class, robot, target);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public WelcomeFrameFixture createNewProject() {
|
||||
findActionLinkByActionId("WelcomeScreen.CreateNewProject").click();
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public WelcomeFrameFixture importProject() {
|
||||
findActionLinkByActionId("WelcomeScreen.ImportProject").click();
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private ActionLinkFixture findActionLinkByActionId(String actionId) {
|
||||
return ActionLinkFixture.findByActionId(actionId, robot(), target());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public MessagesFixture findMessageDialog(@NotNull String title) {
|
||||
return MessagesFixture.findByTitle(robot(), target(), title);
|
||||
}
|
||||
}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.fixtures.newProjectWizard;
|
||||
|
||||
import com.intellij.tests.gui.fixtures.ComponentFixture;
|
||||
import org.fest.swing.core.GenericTypeMatcher;
|
||||
import org.fest.swing.core.Robot;
|
||||
import org.fest.swing.fixture.ContainerFixture;
|
||||
import org.fest.swing.fixture.JButtonFixture;
|
||||
import org.fest.swing.fixture.JLabelFixture;
|
||||
import org.fest.swing.fixture.JTextComponentFixture;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.text.JTextComponent;
|
||||
|
||||
import static com.intellij.tests.gui.framework.GuiTests.*;
|
||||
|
||||
/**
|
||||
* Base class for fixtures which control wizards that extend {@link DynamicWizard}
|
||||
*/
|
||||
public abstract class AbstractWizardFixture<S> extends ComponentFixture<S, JDialog> implements ContainerFixture<JDialog> {
|
||||
|
||||
public AbstractWizardFixture(@NotNull Class<S> selfType, @NotNull Robot robot, @NotNull JDialog target) {
|
||||
super(selfType, robot, target);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected JRootPane findStepWithTitle(@NotNull final String title) {
|
||||
JRootPane rootPane = target().getRootPane();
|
||||
waitUntilFound(robot(), rootPane, new GenericTypeMatcher<JLabel>(JLabel.class) {
|
||||
@Override
|
||||
protected boolean isMatching(@NotNull JLabel label) {
|
||||
if (!label.isShowing()) {
|
||||
return false;
|
||||
}
|
||||
return title.equals(label.getText());
|
||||
}
|
||||
});
|
||||
return rootPane;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public S clickNext() {
|
||||
findAndClickButton(this, "Next");
|
||||
return myself();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public S clickFinish() {
|
||||
findAndClickButton(this, "Finish");
|
||||
return myself();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public S clickCancel() {
|
||||
findAndClickCancelButton(this);
|
||||
return myself();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public JTextComponentFixture findTextField(@NotNull final String labelText) {
|
||||
return new JTextComponentFixture(robot(), robot().finder().findByLabel(labelText, JTextComponent.class));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public JButtonFixture findWizardButton(@NotNull final String text) {
|
||||
JButton button = robot().finder().find(target(), new GenericTypeMatcher<JButton>(JButton.class) {
|
||||
@Override
|
||||
protected boolean isMatching(@NotNull JButton button) {
|
||||
String buttonText = button.getText();
|
||||
if (buttonText != null) {
|
||||
return buttonText.trim().equals(text) && button.isShowing();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return new JButtonFixture(robot(), button);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public JLabelFixture findLabel(@NotNull final String text) {
|
||||
JLabel label = waitUntilFound(robot(), target(), new GenericTypeMatcher<JLabel>(JLabel.class) {
|
||||
@Override
|
||||
protected boolean isMatching(@NotNull JLabel label) {
|
||||
return text.equals(label.getText().replaceAll("(?i)<.?html>", ""));
|
||||
}
|
||||
});
|
||||
|
||||
return new JLabelFixture(robot(), label);
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.fixtures.newProjectWizard;
|
||||
|
||||
import com.intellij.tests.gui.fixtures.JComponentFixture;
|
||||
import org.fest.swing.core.GenericTypeMatcher;
|
||||
import org.fest.swing.core.Robot;
|
||||
import org.fest.swing.driver.JTextComponentDriver;
|
||||
import org.fest.swing.fixture.JCheckBoxFixture;
|
||||
import org.fest.swing.fixture.JComboBoxFixture;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
public abstract class AbstractWizardStepFixture<S> extends JComponentFixture<S, JRootPane> {
|
||||
protected AbstractWizardStepFixture(@NotNull Class<S> selfType, @NotNull Robot robot, @NotNull JRootPane target) {
|
||||
super(selfType, robot, target);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected JCheckBoxFixture findCheckBoxWithLabel(@NotNull final String label) {
|
||||
JCheckBox checkBox = robot().finder().find(target(), new GenericTypeMatcher<JCheckBox>(JCheckBox.class) {
|
||||
@Override
|
||||
protected boolean isMatching(@NotNull JCheckBox component) {
|
||||
return label.equals(component.getText());
|
||||
}
|
||||
});
|
||||
return new JCheckBoxFixture(robot(), checkBox);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected JComboBoxFixture findComboBoxWithLabel(@NotNull String label) {
|
||||
JComboBox comboBox = robot().finder().findByLabel(target(), label, JComboBox.class, true);
|
||||
return new JComboBoxFixture(robot(), comboBox);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected JTextField findTextFieldWithLabel(@NotNull String label) {
|
||||
return robot().finder().findByLabel(target(), label, JTextField.class, true);
|
||||
}
|
||||
|
||||
protected void replaceText(@NotNull JTextField textField, @NotNull String text) {
|
||||
JTextComponentDriver driver = new JTextComponentDriver(robot());
|
||||
driver.selectAll(textField);
|
||||
driver.enterText(textField, text);
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.fixtures.newProjectWizard;
|
||||
|
||||
import org.fest.swing.core.Robot;
|
||||
import org.fest.swing.edt.GuiQuery;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import static org.fest.swing.edt.GuiActionRunner.execute;
|
||||
|
||||
public class ChooseOptionsForNewFileStepFixture extends AbstractWizardStepFixture<ChooseOptionsForNewFileStepFixture> {
|
||||
protected ChooseOptionsForNewFileStepFixture(@NotNull Robot robot, @NotNull JRootPane target) {
|
||||
super(ChooseOptionsForNewFileStepFixture.class, robot, target);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ChooseOptionsForNewFileStepFixture enterActivityName(@NotNull String name) {
|
||||
JTextField textField = robot().finder().findByLabel(target(), "Activity Name:", JTextField.class, true);
|
||||
replaceText(textField, name);
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getLayoutName() {
|
||||
final JTextField textField = robot().finder().findByLabel("Layout Name:", JTextField.class, true);
|
||||
//noinspection ConstantConditions
|
||||
return execute(new GuiQuery<String>() {
|
||||
@Override
|
||||
protected String executeInEDT() throws Throwable {
|
||||
return textField.getText();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.fixtures.newProjectWizard;
|
||||
|
||||
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
|
||||
import org.fest.swing.core.Robot;
|
||||
import org.fest.swing.edt.GuiQuery;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.io.File;
|
||||
|
||||
import static org.fest.assertions.Assertions.assertThat;
|
||||
import static org.fest.swing.edt.GuiActionRunner.execute;
|
||||
|
||||
public class ConfigureAndroidProjectStepFixture extends AbstractWizardStepFixture<ConfigureAndroidProjectStepFixture> {
|
||||
protected ConfigureAndroidProjectStepFixture(@NotNull Robot robot, @NotNull JRootPane target) {
|
||||
super(ConfigureAndroidProjectStepFixture.class, robot, target);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ConfigureAndroidProjectStepFixture enterApplicationName(@NotNull String text) {
|
||||
JTextField textField = findTextFieldWithLabel("Application name:");
|
||||
replaceText(textField, text);
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ConfigureAndroidProjectStepFixture enterCompanyDomain(@NotNull String text) {
|
||||
JTextField textField = findTextFieldWithLabel("Company Domain:");
|
||||
replaceText(textField, text);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
public File getLocationInFileSystem() {
|
||||
final TextFieldWithBrowseButton locationField = robot().finder().findByType(target(), TextFieldWithBrowseButton.class);
|
||||
//noinspection ConstantConditions
|
||||
return execute(new GuiQuery<File>() {
|
||||
@Override
|
||||
protected File executeInEDT() throws Throwable {
|
||||
String location = locationField.getText();
|
||||
assertThat(location).isNotNull().isNotEmpty();
|
||||
return new File(location);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.fixtures.newProjectWizard;
|
||||
|
||||
import org.fest.swing.core.Robot;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
public class ConfigureFormFactorStepFixture extends AbstractWizardStepFixture<ConfigureFormFactorStepFixture> {
|
||||
protected ConfigureFormFactorStepFixture(@NotNull Robot robot, @NotNull JRootPane target) {
|
||||
super(ConfigureFormFactorStepFixture.class, robot, target);
|
||||
}
|
||||
|
||||
//@NotNull
|
||||
//public ConfigureFormFactorStepFixture selectMinimumSdkApi(@NotNull final FormFactor formFactor, @NotNull final String api) {
|
||||
// JCheckBox checkBox = robot().finder().find(target(), new GenericTypeMatcher<JCheckBox>(JCheckBox.class) {
|
||||
// @Override
|
||||
// protected boolean isMatching(@NotNull JCheckBox checkBox) {
|
||||
// String text = checkBox.getText();
|
||||
// // "startsWith" instead of "equals" because the UI may add "(Not installed)" at the end.
|
||||
// return text != null && text.startsWith(formFactor.toString());
|
||||
// }
|
||||
// });
|
||||
// AbstractButtonDriver buttonDriver = new AbstractButtonDriver(robot());
|
||||
// buttonDriver.requireEnabled(checkBox);
|
||||
// buttonDriver.select(checkBox);
|
||||
//
|
||||
// final JComboBox comboBox = robot().finder().findByName(target(), formFactor.id + ".minSdk", JComboBox.class);
|
||||
// //noinspection ConstantConditions
|
||||
// int itemIndex = execute(new GuiQuery<Integer>() {
|
||||
// @Override
|
||||
// protected Integer executeInEDT() throws Throwable {
|
||||
// BasicJComboBoxCellReader cellReader = new BasicJComboBoxCellReader();
|
||||
// int itemCount = comboBox.getItemCount();
|
||||
// for (int i = 0; i < itemCount; i++) {
|
||||
// String value = cellReader.valueAt(comboBox, i);
|
||||
// if (value != null && value.startsWith("API " + api + ":")) {
|
||||
// return i;
|
||||
// }
|
||||
// }
|
||||
// return -1;
|
||||
// }
|
||||
// });
|
||||
// if (itemIndex < 0) {
|
||||
// throw new LocationUnavailableException("Unable to find SDK " + api + " in " + formFactor + " drop-down");
|
||||
// }
|
||||
// JComboBoxDriver comboBoxDriver = new JComboBoxDriver(robot());
|
||||
// comboBoxDriver.selectItem(comboBox, itemIndex);
|
||||
// return this;
|
||||
//}
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.fixtures.newProjectWizard;
|
||||
|
||||
import com.intellij.ide.IdeBundle;
|
||||
import com.intellij.ui.components.JBList;
|
||||
import org.fest.swing.core.GenericTypeMatcher;
|
||||
import org.fest.swing.core.Robot;
|
||||
import org.fest.swing.fixture.JListFixture;
|
||||
import org.fest.swing.fixture.JTreeFixture;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
public class NewProjectWizardFixture extends AbstractWizardFixture<NewProjectWizardFixture> {
|
||||
@NotNull
|
||||
public static NewProjectWizardFixture find(@NotNull Robot robot) {
|
||||
JDialog dialog = robot.finder().find(new GenericTypeMatcher<JDialog>(JDialog.class) {
|
||||
@Override
|
||||
protected boolean isMatching(@NotNull JDialog dialog) {
|
||||
return IdeBundle.message("title.new.project").equals(dialog.getTitle()) && dialog.isShowing();
|
||||
}
|
||||
});
|
||||
return new NewProjectWizardFixture(robot, dialog);
|
||||
}
|
||||
|
||||
private NewProjectWizardFixture(@NotNull Robot robot, @NotNull JDialog target) {
|
||||
super(NewProjectWizardFixture.class, robot, target);
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
public NewProjectWizardFixture selectProjectType(String projectTypeName){
|
||||
JListFixture projectTypeList = new JListFixture(robot(), robot().finder().findByType(JBList.class, true));
|
||||
projectTypeList.clickItem(projectTypeName);
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public NewProjectWizardFixture selectFramework(String frameworkName){
|
||||
JTreeFixture frameworkTree = new JTreeFixture(robot(), robot().finder().findByType(JTree.class, true));
|
||||
//frameworkTree.
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ConfigureAndroidProjectStepFixture getConfigureAndroidProjectStep() {
|
||||
JRootPane rootPane = findStepWithTitle("Configure your new project");
|
||||
return new ConfigureAndroidProjectStepFixture(robot(), rootPane);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ConfigureFormFactorStepFixture getConfigureFormFactorStep() {
|
||||
JRootPane rootPane = findStepWithTitle("Select the form factors your app will run on");
|
||||
return new ConfigureFormFactorStepFixture(robot(), rootPane);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ChooseOptionsForNewFileStepFixture getChooseOptionsForNewFileStep() {
|
||||
JRootPane rootPane = findStepWithTitle("Customize the Activity");
|
||||
return new ChooseOptionsForNewFileStepFixture(robot(), rootPane);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,403 @@
|
||||
/*
|
||||
* Copyright 2000-2016 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.tests.gui.framework;
|
||||
|
||||
/**
|
||||
* Created by karashevich on 30/05/16.
|
||||
*/
|
||||
|
||||
|
||||
import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectManager;
|
||||
import com.intellij.openapi.project.ex.ProjectManagerEx;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.wm.WindowManager;
|
||||
import com.intellij.openapi.wm.impl.WindowManagerImpl;
|
||||
import com.intellij.openapi.wm.impl.welcomeScreen.WelcomeFrame;
|
||||
import com.intellij.tests.gui.fixtures.IdeFrameFixture;
|
||||
import com.intellij.tests.gui.fixtures.WelcomeFrameFixture;
|
||||
import com.intellij.tests.gui.fixtures.newProjectWizard.NewProjectWizardFixture;
|
||||
import com.intellij.util.net.HttpConfigurable;
|
||||
import org.fest.swing.core.BasicRobot;
|
||||
import org.fest.swing.core.Robot;
|
||||
import org.fest.swing.edt.GuiQuery;
|
||||
import org.fest.swing.edt.GuiTask;
|
||||
import org.fest.swing.timing.Condition;
|
||||
import org.jdom.Document;
|
||||
import org.jdom.Element;
|
||||
import org.jdom.input.SAXBuilder;
|
||||
import org.jdom.xpath.XPath;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import static com.intellij.ide.impl.ProjectUtil.closeAndDispose;
|
||||
import static com.intellij.openapi.util.io.FileUtil.*;
|
||||
import static com.intellij.openapi.util.io.FileUtilRt.delete;
|
||||
import static com.intellij.openapi.vfs.VfsUtil.findFileByIoFile;
|
||||
import static com.intellij.tests.gui.framework.GuiTestRunner.canRunGuiTests;
|
||||
import static com.intellij.tests.gui.framework.GuiTests.*;
|
||||
import static junit.framework.Assert.assertNotNull;
|
||||
import static org.fest.assertions.Assertions.assertThat;
|
||||
import static org.fest.swing.edt.GuiActionRunner.execute;
|
||||
import static org.fest.swing.timing.Pause.pause;
|
||||
import static org.fest.util.Strings.quote;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
|
||||
@RunWith(GuiTestRunner.class)
|
||||
public abstract class GuiTestCase {
|
||||
protected Robot myRobot;
|
||||
|
||||
@SuppressWarnings("UnusedDeclaration") // This field is set via reflection.
|
||||
private String myTestName;
|
||||
|
||||
protected IdeFrameFixture myProjectFrame;
|
||||
|
||||
/**
|
||||
* @return the name of the test method being executed.
|
||||
*/
|
||||
protected String getTestName() {
|
||||
return myTestName;
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
if (!canRunGuiTests()) {
|
||||
// We currently do not support running UI tests in headless environments.
|
||||
return;
|
||||
}
|
||||
|
||||
Application application = ApplicationManager.getApplication();
|
||||
assertNotNull(application); // verify that we are using the IDE's ClassLoader.
|
||||
|
||||
setUpDefaultProjectCreationLocationPath();
|
||||
|
||||
myRobot = BasicRobot.robotWithCurrentAwtHierarchy();
|
||||
myRobot.settings().delayBetweenEvents(30);
|
||||
|
||||
setIdeSettings();
|
||||
setUpSdks();
|
||||
|
||||
// There is a race condition between reloading the configuration file after file deletion detected and the serialization of IDEA model
|
||||
// we just customized so that modules can't be loaded correctly.
|
||||
// This is a hack to prevent StoreAwareProjectManager from doing any reloading during test.
|
||||
ProjectManagerEx.getInstanceEx().blockReloadingProjectOnExternalChanges();
|
||||
|
||||
refreshFiles();
|
||||
}
|
||||
|
||||
private static void setIdeSettings() {
|
||||
|
||||
// Clear HTTP proxy settings, in case a test changed them.
|
||||
HttpConfigurable ideSettings = HttpConfigurable.getInstance();
|
||||
ideSettings.USE_HTTP_PROXY = false;
|
||||
ideSettings.PROXY_HOST = "";
|
||||
ideSettings.PROXY_PORT = 80;
|
||||
|
||||
// TODO: setUpDefaultGeneralSettings();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
if (myProjectFrame != null) {
|
||||
myProjectFrame.waitForBackgroundTasksToFinish();
|
||||
}
|
||||
if (myRobot != null) {
|
||||
myRobot.cleanUpWithoutDisposingWindows();
|
||||
// We close all modal dialogs left over, because they block the AWT thread and could trigger a deadlock in the next test.
|
||||
for (Window window : Window.getWindows()) {
|
||||
if (window.isShowing() && window instanceof Dialog) {
|
||||
if (((Dialog) window).getModalityType() == Dialog.ModalityType.APPLICATION_MODAL) {
|
||||
fail("Modal dialog still active: " + window);
|
||||
myRobot.close(window);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ProjectManagerEx.getInstanceEx().unblockReloadingProjectOnExternalChanges();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected WelcomeFrameFixture findWelcomeFrame() {
|
||||
return WelcomeFrameFixture.find(myRobot);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected NewProjectWizardFixture findNewProjectWizard() {
|
||||
return NewProjectWizardFixture.find(myRobot);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected IdeFrameFixture findIdeFrame(@NotNull String projectName, @NotNull File projectPath) {
|
||||
return IdeFrameFixture.find(myRobot, projectPath, projectName);
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
// Called by GuiTestRunner via reflection.
|
||||
protected void closeAllProjects() {
|
||||
pause(new Condition("Close all projects") {
|
||||
@Override
|
||||
public boolean test() {
|
||||
final Project[] openProjects = ProjectManager.getInstance().getOpenProjects();
|
||||
execute(new GuiTask() {
|
||||
@Override
|
||||
protected void executeInEDT() throws Throwable {
|
||||
for (Project project : openProjects) {
|
||||
assertTrue("Failed to close project " + quote(project.getName()), closeAndDispose(project));
|
||||
}
|
||||
}
|
||||
});
|
||||
return ProjectManager.getInstance().getOpenProjects().length == 0;
|
||||
}
|
||||
}, SHORT_TIMEOUT);
|
||||
|
||||
//noinspection ConstantConditions
|
||||
boolean welcomeFrameShown = execute(new GuiQuery<Boolean>() {
|
||||
@Override
|
||||
protected Boolean executeInEDT() throws Throwable {
|
||||
Project[] openProjects = ProjectManager.getInstance().getOpenProjects();
|
||||
if (openProjects.length == 0) {
|
||||
WelcomeFrame.showNow();
|
||||
|
||||
WindowManagerImpl windowManager = (WindowManagerImpl)WindowManager.getInstance();
|
||||
windowManager.disposeRootFrame();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
if (welcomeFrameShown) {
|
||||
pause(new Condition("'Welcome' frame to show up") {
|
||||
@Override
|
||||
public boolean test() {
|
||||
for (Frame frame : Frame.getFrames()) {
|
||||
if (frame == WelcomeFrame.getInstance() && frame.isShowing()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}, SHORT_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected IdeFrameFixture importSimpleApplication() throws IOException {
|
||||
return importProjectAndWaitForProjectSyncToFinish("SimpleApplication");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected IdeFrameFixture importMultiModule() throws IOException {
|
||||
return importProjectAndWaitForProjectSyncToFinish("MultiModule");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected IdeFrameFixture importProjectAndWaitForProjectSyncToFinish(@NotNull String projectDirName) throws IOException {
|
||||
return importProjectAndWaitForProjectSyncToFinish(projectDirName, null);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected IdeFrameFixture importProjectAndWaitForProjectSyncToFinish(@NotNull String projectDirName, @Nullable String gradleVersion)
|
||||
throws IOException {
|
||||
File projectPath = setUpProject(projectDirName, false);
|
||||
VirtualFile toSelect = findFileByIoFile(projectPath, false);
|
||||
assertNotNull(toSelect);
|
||||
|
||||
doImportProject(toSelect);
|
||||
|
||||
IdeFrameFixture projectFrame = findIdeFrame(projectPath);
|
||||
//projectFrame.waitForGradleProjectSyncToFinish();
|
||||
|
||||
return projectFrame;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected File importProject(@NotNull String projectDirName) throws IOException {
|
||||
File projectPath = setUpProject(projectDirName, false);
|
||||
VirtualFile toSelect = findFileByIoFile(projectPath, false);
|
||||
assertNotNull(toSelect);
|
||||
doImportProject(toSelect);
|
||||
return projectPath;
|
||||
}
|
||||
|
||||
private static void doImportProject(@NotNull final VirtualFile projectDir) {
|
||||
execute(new GuiTask() {
|
||||
@Override
|
||||
protected void executeInEDT() throws Throwable {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up a project before using it in a UI test:
|
||||
* <ul>
|
||||
* <li>Makes a copy of the project in testData/guiTests/newProjects (deletes any existing copy of the project first.) This copy is
|
||||
* the one the test will use.</li>
|
||||
* <li>Creates a Gradle wrapper for the test project.</li>
|
||||
* <li>Updates the version of the Android Gradle plug-in used by the project, if applicable</li>
|
||||
* <li>Creates a local.properties file pointing to the Android SDK path specified by the system property (or environment variable)
|
||||
* 'ADT_TEST_SDK_PATH'</li>
|
||||
* <li>Copies over missing files to the .idea directory (if the project will be opened, instead of imported.)</li>
|
||||
* <li>Deletes .idea directory, .iml files and build directories, if the project will be imported.</li>
|
||||
* <p/>
|
||||
* </ul>
|
||||
*
|
||||
* @param projectDirName the name of the project's root directory. Tests are located in testData/guiTests.
|
||||
* @param forOpen indicates whether the project will be opened by the IDE, or imported.
|
||||
* @param updateAndroidPluginVersion indicates if the latest supported version of the Android Gradle plug-in should be set in the
|
||||
* project.
|
||||
* @param gradleVersion the Gradle version to use in the wrapper. If {@code null} is passed, this method will use the latest supported
|
||||
* version of Gradle.
|
||||
* @return the path of project's root directory (the copy of the project, not the original one.)
|
||||
* @throws IOException if an unexpected I/O error occurs.
|
||||
*/
|
||||
@NotNull
|
||||
private File setUpProject(@NotNull String projectDirName,
|
||||
boolean forOpen) throws IOException {
|
||||
File projectPath = copyProjectBeforeOpening(projectDirName);
|
||||
|
||||
updateLocalProperties(projectPath);
|
||||
|
||||
if (forOpen) {
|
||||
File toDotIdea = new File(projectPath, Project.DIRECTORY_STORE_FOLDER);
|
||||
ensureExists(toDotIdea);
|
||||
|
||||
File fromDotIdea = new File(getTestProjectsRootDirPath(), join("commonFiles", Project.DIRECTORY_STORE_FOLDER));
|
||||
assertThat(fromDotIdea).isDirectory();
|
||||
|
||||
for (File from : notNullize(fromDotIdea.listFiles())) {
|
||||
if (from.isDirectory()) {
|
||||
File destination = new File(toDotIdea, from.getName());
|
||||
if (!destination.isDirectory()) {
|
||||
copyDirContent(from, destination);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
File to = new File(toDotIdea, from.getName());
|
||||
if (!to.isFile()) {
|
||||
copy(from, to);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
cleanUpProjectForImport(projectPath);
|
||||
}
|
||||
|
||||
return projectPath;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected File copyProjectBeforeOpening(@NotNull String projectDirName) throws IOException {
|
||||
File masterProjectPath = getMasterProjectDirPath(projectDirName);
|
||||
|
||||
File projectPath = getTestProjectDirPath(projectDirName);
|
||||
if (projectPath.isDirectory()) {
|
||||
delete(projectPath);
|
||||
System.out.println(String.format("Deleted project path '%1$s'", projectPath.getPath()));
|
||||
}
|
||||
copyDir(masterProjectPath, projectPath);
|
||||
System.out.println(String.format("Copied project '%1$s' to path '%2$s'", projectDirName, projectPath.getPath()));
|
||||
return projectPath;
|
||||
}
|
||||
|
||||
|
||||
protected void updateLocalProperties(File projectPath) throws IOException {
|
||||
//File androidHomePath = IdeSdks.getAndroidSdkPath();
|
||||
//assertNotNull(androidHomePath);
|
||||
//
|
||||
//LocalProperties localProperties = new LocalProperties(projectPath);
|
||||
//localProperties.setAndroidSdkPath(androidHomePath);
|
||||
//localProperties.save();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected File getMasterProjectDirPath(@NotNull String projectDirName) {
|
||||
return new File(getTestProjectsRootDirPath(), projectDirName);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected File getTestProjectDirPath(@NotNull String projectDirName) {
|
||||
return new File(getProjectCreationDirPath(), projectDirName);
|
||||
}
|
||||
|
||||
protected void cleanUpProjectForImport(@NotNull File projectPath) {
|
||||
File dotIdeaFolderPath = new File(projectPath, Project.DIRECTORY_STORE_FOLDER);
|
||||
if (dotIdeaFolderPath.isDirectory()) {
|
||||
File modulesXmlFilePath = new File(dotIdeaFolderPath, "modules.xml");
|
||||
if (modulesXmlFilePath.isFile()) {
|
||||
SAXBuilder saxBuilder = new SAXBuilder();
|
||||
try {
|
||||
Document document = saxBuilder.build(modulesXmlFilePath);
|
||||
XPath xpath = XPath.newInstance("//*[@fileurl]");
|
||||
//noinspection unchecked
|
||||
java.util.List<Element> modules = xpath.selectNodes(document);
|
||||
int urlPrefixSize = "file://$PROJECT_DIR$/".length();
|
||||
for (Element module : modules) {
|
||||
String fileUrl = module.getAttributeValue("fileurl");
|
||||
if (!StringUtil.isEmpty(fileUrl)) {
|
||||
String relativePath = toSystemDependentName(fileUrl.substring(urlPrefixSize));
|
||||
File imlFilePath = new File(projectPath, relativePath);
|
||||
if (imlFilePath.isFile()) {
|
||||
delete(imlFilePath);
|
||||
}
|
||||
// It is likely that each module has a "build" folder. Delete it as well.
|
||||
File buildFilePath = new File(imlFilePath.getParentFile(), "build");
|
||||
if (buildFilePath.isDirectory()) {
|
||||
delete(buildFilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Throwable ignored) {
|
||||
// if something goes wrong, just ignore. Most likely it won't affect project import in any way.
|
||||
}
|
||||
}
|
||||
delete(dotIdeaFolderPath);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected IdeFrameFixture findIdeFrame(@NotNull File projectPath) {
|
||||
return IdeFrameFixture.find(myRobot, projectPath, null);
|
||||
}
|
||||
|
||||
protected void refreshFiles() {
|
||||
execute(new GuiTask() {
|
||||
@Override
|
||||
protected void executeInEDT() throws Throwable {
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
LocalFileSystem.getInstance().refresh(false /* synchronous */);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.framework;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.intellij.openapi.projectRoots.JavaSdk;
|
||||
import com.intellij.openapi.projectRoots.JavaSdkVersion;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import org.fest.reflect.reference.TypeRef;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.fest.reflect.core.Reflection.method;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
/**
|
||||
* Collects configuration information from a UI test method's {@link IdeGuiTest} and {@link IdeGuiTestSetup} annotations and applies it
|
||||
* to the test before execution, using the the IDE's {@code ClassLoader} (which is the {@code ClassLoader} used by UI tests, to be able to
|
||||
* access IDE's services, state and components.)
|
||||
*/
|
||||
class GuiTestConfigurator {
|
||||
private static final String CLOSE_PROJECT_BEFORE_EXECUTION_KEY = "closeProjectBeforeExecution";
|
||||
private static final String RETRY_COUNT_KEY = "retryCount";
|
||||
private static final String RUN_WITH_MINIMUM_JDK_VERSION_KEY = "runWithMinimumJdkVersion";
|
||||
private static final String SKIP_SOURCE_GENERATION_ON_SYNC_KEY = "skipSourceGenerationOnSync";
|
||||
private static final String TAKE_SCREENSHOT_ON_TEST_FAILURE_KEY = "takeScreenshotOnTestFailure";
|
||||
|
||||
@NotNull private final String myTestName;
|
||||
@NotNull private final Object myTest;
|
||||
@NotNull private final ClassLoader myClassLoader;
|
||||
|
||||
@Nullable private final Object myMinimumJdkVersion;
|
||||
|
||||
private final boolean myCloseProjectBeforeExecution;
|
||||
private final int myRetryCount;
|
||||
private final boolean mySkipSourceGenerationOnSync;
|
||||
private final boolean myTakeScreenshotOnTestFailure;
|
||||
|
||||
@NotNull
|
||||
static GuiTestConfigurator createNew(@NotNull Method testMethod, @NotNull Object test) throws Throwable {
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
Class<?> target = classLoader.loadClass(GuiTestConfigurator.class.getCanonicalName());
|
||||
Map<String, Object> testConfig = method("extractTestConfiguration").withReturnType(new TypeRef<Map<String, Object>>() {})
|
||||
.withParameterTypes(Method.class)
|
||||
.in(target)
|
||||
.invoke(testMethod);
|
||||
assertNotNull(testConfig);
|
||||
return new GuiTestConfigurator(testConfig, testMethod.getName(), test, classLoader);
|
||||
}
|
||||
|
||||
// Invoked using reflection and the IDE's ClassLoader.
|
||||
@NotNull
|
||||
private static Map<String, Object> extractTestConfiguration(@NotNull Method testMethod) {
|
||||
Map<String, Object> config = Maps.newHashMap();
|
||||
IdeGuiTest guiTest = testMethod.getAnnotation(IdeGuiTest.class);
|
||||
if (guiTest != null) {
|
||||
config.put(CLOSE_PROJECT_BEFORE_EXECUTION_KEY, guiTest.closeProjectBeforeExecution());
|
||||
config.put(RUN_WITH_MINIMUM_JDK_VERSION_KEY, guiTest.runWithMinimumJdkVersion());
|
||||
config.put(RETRY_COUNT_KEY, guiTest.retryCount());
|
||||
}
|
||||
IdeGuiTestSetup guiTestSetup = testMethod.getDeclaringClass().getAnnotation(IdeGuiTestSetup.class);
|
||||
if (guiTestSetup != null) {
|
||||
config.put(SKIP_SOURCE_GENERATION_ON_SYNC_KEY, guiTestSetup.skipSourceGenerationOnSync());
|
||||
config.put(TAKE_SCREENSHOT_ON_TEST_FAILURE_KEY, guiTestSetup.takeScreenshotOnTestFailure());
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
private GuiTestConfigurator(@NotNull Map<String, Object> configuration,
|
||||
@NotNull String testName,
|
||||
@NotNull Object test,
|
||||
@NotNull ClassLoader classLoader) {
|
||||
myCloseProjectBeforeExecution = getBooleanValue(CLOSE_PROJECT_BEFORE_EXECUTION_KEY, configuration, true);
|
||||
myMinimumJdkVersion = getValue(RUN_WITH_MINIMUM_JDK_VERSION_KEY, configuration, JavaSdkVersion.class);
|
||||
myRetryCount = getIntValue(RETRY_COUNT_KEY, configuration, 0);
|
||||
mySkipSourceGenerationOnSync = getBooleanValue(SKIP_SOURCE_GENERATION_ON_SYNC_KEY, configuration, false);
|
||||
myTakeScreenshotOnTestFailure = getBooleanValue(TAKE_SCREENSHOT_ON_TEST_FAILURE_KEY, configuration, true);
|
||||
|
||||
myTestName = testName;
|
||||
myTest = test;
|
||||
myClassLoader = classLoader;
|
||||
}
|
||||
|
||||
private static boolean getBooleanValue(@NotNull String key, @NotNull Map<String, Object> configuration, boolean defaultValue) {
|
||||
Object value = configuration.get(key);
|
||||
if (value instanceof Boolean) {
|
||||
return ((Boolean)value);
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Object getValue(@NotNull String key, @NotNull Map<String, Object> configuration, @NotNull Class<?> type) {
|
||||
Object value = configuration.get(key);
|
||||
if (value != null && value.getClass().getCanonicalName().equals(type.getCanonicalName())) {
|
||||
return value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static int getIntValue(@NotNull String key, @NotNull Map<String, Object> configuration, int defaultValue) {
|
||||
Object value = configuration.get(key);
|
||||
if (value instanceof Integer) {
|
||||
return ((Integer)value);
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
void executeSetupTasks() throws Throwable {
|
||||
closeAllProjects();
|
||||
skipSourceGenerationOnSync();
|
||||
}
|
||||
|
||||
private void closeAllProjects() {
|
||||
if (myCloseProjectBeforeExecution) {
|
||||
method("closeAllProjects").in(myTest).invoke();
|
||||
}
|
||||
}
|
||||
|
||||
private void skipSourceGenerationOnSync() throws Throwable {
|
||||
if (mySkipSourceGenerationOnSync) {
|
||||
Class<?> target = loadMyClassWithTestClassLoader();
|
||||
method("doSkipSourceGenerationOnSync").in(target).invoke();
|
||||
}
|
||||
}
|
||||
|
||||
// Invoked using reflection and the IDE's ClassLoader.
|
||||
private static void doSkipSourceGenerationOnSync() {
|
||||
System.out.println("Skipping source generation on project sync.");
|
||||
}
|
||||
|
||||
boolean shouldSkipTest() throws Throwable {
|
||||
if (myMinimumJdkVersion != null) {
|
||||
Class<?> target = loadMyClassWithTestClassLoader();
|
||||
Class<?> javaSdkVersionClass = myClassLoader.loadClass(JavaSdkVersion.class.getCanonicalName());
|
||||
Boolean hasRequiredJdk = method("hasRequiredJdk").withReturnType(boolean.class)
|
||||
.withParameterTypes(javaSdkVersionClass)
|
||||
.in(target)
|
||||
.invoke(myMinimumJdkVersion);
|
||||
assertNotNull(hasRequiredJdk);
|
||||
if (!hasRequiredJdk) {
|
||||
String jdkVersion = method("getDescription").withReturnType(String.class).in(myMinimumJdkVersion).invoke();
|
||||
System.out.println(String.format("Skipping test '%1$s'. It needs JDK %2$s or newer.", myTestName, jdkVersion));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Invoked using reflection and the IDE's ClassLoader.
|
||||
private static boolean hasRequiredJdk(@NotNull JavaSdkVersion jdkVersion) {
|
||||
//Sdk jdk = IdeSdks.getJdk();
|
||||
Sdk jdk = null;
|
||||
assertNotNull("Expecting to have a JDK", jdk);
|
||||
JavaSdkVersion currentVersion = JavaSdk.getInstance().getVersion(jdk);
|
||||
return currentVersion != null && currentVersion.isAtLeast(jdkVersion);
|
||||
}
|
||||
|
||||
boolean shouldTakeScreenshotOnFailure() {
|
||||
return myTakeScreenshotOnTestFailure;
|
||||
}
|
||||
|
||||
int getRetryCount() {
|
||||
return myRetryCount;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Class<?> loadMyClassWithTestClassLoader() throws ClassNotFoundException {
|
||||
return myClassLoader.loadClass(GuiTestConfigurator.class.getCanonicalName());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.framework;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import org.fest.swing.image.ScreenshotTaker;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.internal.AssumptionViolatedException;
|
||||
import org.junit.internal.runners.model.ReflectiveCallable;
|
||||
import org.junit.internal.runners.statements.Fail;
|
||||
import org.junit.internal.runners.statements.RunAfters;
|
||||
import org.junit.internal.runners.statements.RunBefores;
|
||||
import org.junit.runner.notification.Failure;
|
||||
import org.junit.runner.notification.RunNotifier;
|
||||
import org.junit.runners.BlockJUnit4ClassRunner;
|
||||
import org.junit.runners.model.FrameworkMethod;
|
||||
import org.junit.runners.model.InitializationError;
|
||||
import org.junit.runners.model.Statement;
|
||||
import org.junit.runners.model.TestClass;
|
||||
|
||||
import java.awt.*;
|
||||
import java.lang.management.GarbageCollectorMXBean;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.MemoryMXBean;
|
||||
import java.lang.reflect.Method;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
public class GuiTestRunner extends BlockJUnit4ClassRunner {
|
||||
|
||||
private TestClass myTestClass;
|
||||
|
||||
private final List<GarbageCollectorMXBean> myGarbageCollectorMXBeans = ManagementFactory.getGarbageCollectorMXBeans();
|
||||
private final MemoryMXBean myMemoryMXBean = ManagementFactory.getMemoryMXBean();
|
||||
|
||||
@Nullable private final ScreenshotTaker myScreenshotTaker;
|
||||
|
||||
public GuiTestRunner(Class<?> testClass) throws InitializationError {
|
||||
super(testClass);
|
||||
|
||||
myScreenshotTaker = canRunGuiTests() ? new ScreenshotTaker() : null;
|
||||
|
||||
// UI_TEST_MODE is set whenever we run UI tests on top of a Studio build. In that case, we
|
||||
// assume the classpath has been properly configured. Otherwise, if we're running from the
|
||||
// IDE or an Ant build, we need to check we have access to the classpath of community-main.
|
||||
if (Strings.isNullOrEmpty(System.getenv("UI_TEST_MODE"))) {
|
||||
//try {
|
||||
// A random class which is reachable from module community-main's classpath but not
|
||||
// module android's classpath.
|
||||
//Class.forName("git4idea.repo.GitConfig", false, testClass.getClassLoader());
|
||||
|
||||
//don't check for git module here
|
||||
}
|
||||
//catch (ClassNotFoundException e) {
|
||||
// throw new InitializationError("Invalid test run configuration. Edit your test configuration and make sure that " +
|
||||
// "\"Use classpath of module\" is set to \"community-main\", *NOT* \"android\"!");
|
||||
//}
|
||||
//}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void runChild(final FrameworkMethod method, RunNotifier notifier) {
|
||||
if (!canRunGuiTests()) {
|
||||
notifier.fireTestAssumptionFailed(new Failure(describeChild(method), new AssumptionViolatedException("Headless environment")));
|
||||
System.out.println(String.format("Skipping test '%1$s'. UI tests cannot run in a headless environment.", method.getName()));
|
||||
} else if (MethodInvoker.doesIdeHaveFatalErrors()) {
|
||||
notifier.fireTestIgnored(describeChild(method)); // TODO: can we restart the IDE at this point, instead of giving up?
|
||||
System.out.println(String.format("Skipping test '%1$s': a fatal error has occurred in the IDE", method.getName()));
|
||||
notifier.pleaseStop();
|
||||
} else {
|
||||
printPerfStats();
|
||||
printTimestamp();
|
||||
super.runChild(method, notifier);
|
||||
printTimestamp();
|
||||
printPerfStats();
|
||||
}
|
||||
}
|
||||
|
||||
private void printTimestamp() {
|
||||
final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
|
||||
System.out.println(dateFormat.format(new Date()));
|
||||
}
|
||||
|
||||
private void printPerfStats() {
|
||||
long gcCount = 0, gcTime = 0;
|
||||
for (GarbageCollectorMXBean garbageCollectorMXBean : myGarbageCollectorMXBeans) {
|
||||
gcCount += garbageCollectorMXBean.getCollectionCount();
|
||||
gcTime += garbageCollectorMXBean.getCollectionTime();
|
||||
}
|
||||
System.out.printf("%d garbage collections; cumulative %d ms%n", gcCount, gcTime);
|
||||
myMemoryMXBean.gc();
|
||||
System.out.printf("heap: %s%n", myMemoryMXBean.getHeapMemoryUsage());
|
||||
System.out.printf("non-heap: %s%n", myMemoryMXBean.getNonHeapMemoryUsage());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Statement methodBlock(FrameworkMethod method) {
|
||||
FrameworkMethod newMethod;
|
||||
try {
|
||||
loadClassesWithIdeClassLoader();
|
||||
Method methodFromClassLoader = myTestClass.getJavaClass().getMethod(method.getName());
|
||||
newMethod = new FrameworkMethod(methodFromClassLoader);
|
||||
}
|
||||
catch (Exception e) {
|
||||
return new Fail(e);
|
||||
}
|
||||
Object test;
|
||||
try {
|
||||
test = new ReflectiveCallable() {
|
||||
@Override
|
||||
protected Object runReflectiveCall() throws Throwable {
|
||||
return createTest();
|
||||
}
|
||||
}.run();
|
||||
}
|
||||
catch (Throwable e) {
|
||||
return new Fail(e);
|
||||
}
|
||||
|
||||
Statement statement = methodInvoker(newMethod, test);
|
||||
|
||||
List<FrameworkMethod> beforeMethods = myTestClass.getAnnotatedMethods(Before.class);
|
||||
if (!beforeMethods.isEmpty()) {
|
||||
statement = new RunBefores(statement, beforeMethods, test);
|
||||
}
|
||||
|
||||
List<FrameworkMethod> afterMethods = myTestClass.getAnnotatedMethods(After.class);
|
||||
if (!afterMethods.isEmpty()) {
|
||||
statement = new RunAfters(statement, afterMethods, test);
|
||||
}
|
||||
|
||||
return statement;
|
||||
}
|
||||
|
||||
public static boolean canRunGuiTests() {
|
||||
return !GraphicsEnvironment.isHeadless();
|
||||
}
|
||||
|
||||
private void loadClassesWithIdeClassLoader() throws Exception {
|
||||
ClassLoader ideClassLoader = IdeTestApplication.getInstance().getIdeClassLoader();
|
||||
Thread.currentThread().setContextClassLoader(ideClassLoader);
|
||||
|
||||
Class<?> testClass = getTestClass().getJavaClass();
|
||||
myTestClass = new TestClass(ideClassLoader.loadClass(testClass.getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object createTest() throws Exception {
|
||||
return myTestClass != null ? myTestClass.getJavaClass().newInstance() : super.createTest();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Statement methodInvoker(final FrameworkMethod method, Object test) {
|
||||
try {
|
||||
assertNotNull(myScreenshotTaker);
|
||||
return new MethodInvoker(method, test, myScreenshotTaker);
|
||||
}
|
||||
catch (Throwable e) {
|
||||
return new Fail(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,562 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.framework;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import org.fest.swing.core.Robot;
|
||||
import com.intellij.diagnostic.AbstractMessage;
|
||||
import com.intellij.diagnostic.MessagePool;
|
||||
import com.intellij.ide.GeneralSettings;
|
||||
import com.intellij.ide.RecentProjectsManager;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ex.PathManagerEx;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectManager;
|
||||
import com.intellij.openapi.project.ProjectManagerAdapter;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.wm.IdeFrame;
|
||||
import com.intellij.openapi.wm.impl.IdeFrameImpl;
|
||||
import com.intellij.tests.gui.fixtures.IdeFrameFixture;
|
||||
import com.intellij.ui.components.JBList;
|
||||
import com.intellij.ui.popup.PopupFactoryImpl;
|
||||
import com.intellij.ui.popup.list.ListPopupModel;
|
||||
import org.fest.swing.core.BasicRobot;
|
||||
import org.fest.swing.core.ComponentFinder;
|
||||
import org.fest.swing.core.GenericTypeMatcher;
|
||||
import org.fest.swing.edt.GuiActionRunner;
|
||||
import org.fest.swing.edt.GuiQuery;
|
||||
import org.fest.swing.edt.GuiTask;
|
||||
import org.fest.swing.fixture.ContainerFixture;
|
||||
import org.fest.swing.fixture.JListFixture;
|
||||
import org.fest.swing.timing.Condition;
|
||||
import org.fest.swing.timing.Pause;
|
||||
import org.fest.swing.timing.Timeout;
|
||||
import org.hamcrest.BaseMatcher;
|
||||
import org.hamcrest.Description;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static com.google.common.base.Joiner.on;
|
||||
import static com.google.common.io.Files.createTempDir;
|
||||
import static com.intellij.openapi.projectRoots.JdkUtil.checkForJdk;
|
||||
import static com.intellij.openapi.util.text.StringUtil.isNotEmpty;
|
||||
import static com.intellij.util.containers.ContainerUtil.getFirstItem;
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
import static org.fest.swing.edt.GuiActionRunner.execute;
|
||||
import static org.fest.swing.finder.WindowFinder.findFrame;
|
||||
import static org.fest.swing.timing.Pause.pause;
|
||||
import static org.fest.swing.timing.Timeout.timeout;
|
||||
import static org.fest.util.Strings.isNullOrEmpty;
|
||||
import static org.fest.util.Strings.quote;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public final class GuiTests {
|
||||
public static final Timeout THIRTY_SEC_TIMEOUT = timeout(30, SECONDS);
|
||||
public static final Timeout SHORT_TIMEOUT = timeout(2, MINUTES);
|
||||
public static final Timeout LONG_TIMEOUT = timeout(5, MINUTES);
|
||||
|
||||
public static final String GUI_TESTS_RUNNING_IN_SUITE_PROPERTY = "gui.tests.running.in.suite";
|
||||
|
||||
/** Environment variable pointing to the JDK to be used for tests */
|
||||
public static final String JDK_HOME_FOR_TESTS = "JDK_HOME_FOR_TESTS";
|
||||
|
||||
private static final EventQueue SYSTEM_EVENT_QUEUE = Toolkit.getDefaultToolkit().getSystemEventQueue();
|
||||
|
||||
private static final File TMP_PROJECT_ROOT = createTempProjectCreationDir();
|
||||
|
||||
// Called by MethodInvoker via reflection
|
||||
@SuppressWarnings("unused")
|
||||
public static void failIfIdeHasFatalErrors() {
|
||||
final MessagePool messagePool = MessagePool.getInstance();
|
||||
List<AbstractMessage> fatalErrors = messagePool.getFatalErrors(true, true);
|
||||
int fatalErrorCount = fatalErrors.size();
|
||||
for (int i = 0; i < fatalErrorCount; i++) {
|
||||
System.err.println("** Fatal Error " + (i + 1) + " of " + fatalErrorCount);
|
||||
AbstractMessage error = fatalErrors.get(i);
|
||||
System.err.println("* Message: ");
|
||||
System.err.println(error.getMessage());
|
||||
|
||||
String additionalInfo = error.getAdditionalInfo();
|
||||
if (isNotEmpty(additionalInfo)) {
|
||||
System.err.println("* Additional Info: ");
|
||||
System.err.println(additionalInfo);
|
||||
}
|
||||
|
||||
String throwableText = error.getThrowableText();
|
||||
if (isNotEmpty(throwableText)) {
|
||||
System.err.println("* Throwable: ");
|
||||
System.err.println(throwableText);
|
||||
}
|
||||
System.err.println();
|
||||
}
|
||||
if (fatalErrorCount > 0) {
|
||||
throw new AssertionError(fatalErrorCount + " fatal errors found. Stopping test execution.");
|
||||
}
|
||||
}
|
||||
|
||||
// Called by MethodInvoker via reflection
|
||||
@SuppressWarnings("unused")
|
||||
public static boolean doesIdeHaveFatalErrors() {
|
||||
final MessagePool messagePool = MessagePool.getInstance();
|
||||
List<AbstractMessage> fatalErrors = messagePool.getFatalErrors(true, true);
|
||||
return !fatalErrors.isEmpty();
|
||||
}
|
||||
|
||||
// Called by IdeTestApplication via reflection.
|
||||
@SuppressWarnings("unused")
|
||||
public static void setUpDefaultGeneralSettings() {
|
||||
//setGuiTestingMode(true);
|
||||
|
||||
GeneralSettings.getInstance().setShowTipsOnStartup(false);
|
||||
setUpDefaultProjectCreationLocationPath();
|
||||
|
||||
setUpSdks();
|
||||
}
|
||||
|
||||
public static void setUpSdks() {
|
||||
|
||||
String jdkHome = getSystemPropertyOrEnvironmentVariable(JDK_HOME_FOR_TESTS);
|
||||
if (isNullOrEmpty(jdkHome) || !checkForJdk(jdkHome)) {
|
||||
fail("Please specify the path to a valid JDK using system property " + JDK_HOME_FOR_TESTS);
|
||||
}
|
||||
final File jdkPath = new File(jdkHome);
|
||||
|
||||
execute(new GuiTask() {
|
||||
@Override
|
||||
protected void executeInEDT() throws Throwable {
|
||||
//File currentAndroidSdkPath = IdeSdks.getAndroidSdkPath();
|
||||
//File currentJdkPath = IdeSdks.getJdkPath();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static File getGradleHomePath() {
|
||||
return getFilePathProperty("supported.gradle.home.path", "the path of a local Gradle 2.2.1 distribution", true);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static File getUnsupportedGradleHome() {
|
||||
return getGradleHomeFromSystemProperty("unsupported.gradle.home.path", "2.1");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static File getGradleHomeFromSystemProperty(@NotNull String propertyName, @NotNull String gradleVersion) {
|
||||
String description = "the path of a Gradle " + gradleVersion + " distribution";
|
||||
return getFilePathProperty(propertyName, description, true);
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
public static File getFilePathProperty(@NotNull String propertyName,
|
||||
@NotNull String description,
|
||||
boolean isDirectory) {
|
||||
String pathValue = System.getProperty(propertyName);
|
||||
if (!isNullOrEmpty(pathValue)) {
|
||||
File path = new File(pathValue);
|
||||
if (isDirectory && path.isDirectory() || !isDirectory && path.isFile()) {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
System.out.println("Please specify " + description + ", using system property " + quote(propertyName));
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void setUpDefaultProjectCreationLocationPath() {
|
||||
RecentProjectsManager.getInstance().setLastProjectCreationLocation(getProjectCreationDirPath().getPath());
|
||||
}
|
||||
|
||||
// Called by IdeTestApplication via reflection.
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
public static void waitForIdeToStart() {
|
||||
GuiActionRunner.executeInEDT(false);
|
||||
Robot robot = null;
|
||||
try {
|
||||
robot = BasicRobot.robotWithCurrentAwtHierarchy();
|
||||
final MyProjectManagerListener listener = new MyProjectManagerListener();
|
||||
findFrame(new GenericTypeMatcher<Frame>(Frame.class) {
|
||||
@Override
|
||||
protected boolean isMatching(@NotNull Frame frame) {
|
||||
if (frame instanceof IdeFrame) {
|
||||
if (frame instanceof IdeFrameImpl) {
|
||||
listener.myActive = true;
|
||||
ProjectManager.getInstance().addProjectManagerListener(listener);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}).withTimeout(LONG_TIMEOUT.duration()).using(robot);
|
||||
|
||||
// We know the IDE event queue was pushed in front of the AWT queue. Some JDKs will leave a dummy event in the AWT queue, which
|
||||
// we attempt to clear here. All other events, including those posted by the Robot, will go through the IDE event queue.
|
||||
try {
|
||||
if (SYSTEM_EVENT_QUEUE.peekEvent() != null) {
|
||||
SYSTEM_EVENT_QUEUE.getNextEvent();
|
||||
}
|
||||
} catch (InterruptedException ex ) {
|
||||
// Ignored.
|
||||
}
|
||||
|
||||
if (listener.myActive) {
|
||||
pause(new Condition("Project to be opened") {
|
||||
@Override
|
||||
public boolean test() {
|
||||
boolean notified = listener.myNotified;
|
||||
if (notified) {
|
||||
ProgressManager progressManager = ProgressManager.getInstance();
|
||||
boolean isIdle = !progressManager.hasModalProgressIndicator() &&
|
||||
!progressManager.hasProgressIndicator() &&
|
||||
!progressManager.hasUnsafeProgressIndicator();
|
||||
if (isIdle) {
|
||||
ProjectManager.getInstance().removeProjectManagerListener(listener);
|
||||
}
|
||||
return isIdle;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}, LONG_TIMEOUT);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
GuiActionRunner.executeInEDT(true);
|
||||
if (robot != null) {
|
||||
robot.cleanUpWithoutDisposingWindows();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static File getProjectCreationDirPath() {
|
||||
return TMP_PROJECT_ROOT;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static File createTempProjectCreationDir() {
|
||||
try {
|
||||
// The temporary location might contain symlinks, such as /var@ -> /private/var on MacOS.
|
||||
// EditorFixture seems to require a canonical path when opening the file.
|
||||
return createTempDir().getCanonicalFile();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
// For now, keep the original behavior and point inside the source tree.
|
||||
ex.printStackTrace();
|
||||
return new File(getTestProjectsRootDirPath(), "newProjects");
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static File getTestProjectsRootDirPath() {
|
||||
|
||||
final String path = PathManagerEx.getTestDataPath(GuiTestCase.class);
|
||||
return new File(path);
|
||||
|
||||
//String testDataPath = AndroidTestBase.getTestDataPath();
|
||||
//assertNotNull(testDataPath);
|
||||
//assertThat(testDataPath).isNotEmpty();
|
||||
//testDataPath = toCanonicalPath(toSystemDependentName(testDataPath));
|
||||
//return new File(testDataPath, "guiTests");
|
||||
}
|
||||
|
||||
private GuiTests() {
|
||||
}
|
||||
|
||||
public static void deleteFile(@Nullable final VirtualFile file) {
|
||||
// File deletion must happen on UI thread under write lock
|
||||
if (file != null) {
|
||||
execute(new GuiTask() {
|
||||
@Override
|
||||
protected void executeInEDT() throws Throwable {
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
file.delete(this);
|
||||
}
|
||||
catch (IOException e) {
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** Waits until an IDE popup is shown (and returns it */
|
||||
public static JBList waitForPopup(@NotNull Robot robot) {
|
||||
return waitUntilFound(robot, null, new GenericTypeMatcher<JBList>(JBList.class) {
|
||||
@Override
|
||||
protected boolean isMatching(@NotNull JBList list) {
|
||||
ListModel model = list.getModel();
|
||||
return model instanceof ListPopupModel;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Clicks an IntelliJ/Studio popup menu item with the given label prefix
|
||||
*
|
||||
* @param labelPrefix the target menu item label prefix
|
||||
* @param component a component in the same window that the popup menu is associated with
|
||||
* @param robot the robot to drive it with
|
||||
*/
|
||||
public static void clickPopupMenuItem(@NotNull String labelPrefix, @NotNull Component component, @NotNull Robot robot) {
|
||||
clickPopupMenuItemMatching(new PrefixMatcher(labelPrefix), component, robot);
|
||||
}
|
||||
|
||||
public static void clickPopupMenuItemMatching(@NotNull Matcher<String> labelMatcher, @NotNull Component component, @NotNull Robot robot) {
|
||||
// IntelliJ doesn't seem to use a normal JPopupMenu, so this won't work:
|
||||
// JPopupMenu menu = myRobot.findActivePopupMenu();
|
||||
// Instead, it uses a JList (technically a JBList), which is placed somewhere
|
||||
// under the root pane.
|
||||
|
||||
Container root = getRootContainer(component);
|
||||
|
||||
// First find the JBList which holds the popup. There could be other JBLists in the hierarchy,
|
||||
// so limit it to one that is actually used as a popup, as identified by its model being a ListPopupModel:
|
||||
assertNotNull(root);
|
||||
JBList list = robot.finder().find(root, new GenericTypeMatcher<JBList>(JBList.class) {
|
||||
@Override
|
||||
protected boolean isMatching(@NotNull JBList list) {
|
||||
ListModel model = list.getModel();
|
||||
return model instanceof ListPopupModel;
|
||||
}
|
||||
});
|
||||
|
||||
// We can't use the normal JListFixture method to click by label since the ListModel items are
|
||||
// ActionItems whose toString does not reflect the text, so search through the model items instead:
|
||||
ListPopupModel model = (ListPopupModel)list.getModel();
|
||||
List<String> items = Lists.newArrayList();
|
||||
for (int i = 0; i < model.getSize(); i++) {
|
||||
Object elementAt = model.getElementAt(i);
|
||||
if (elementAt instanceof PopupFactoryImpl.ActionItem) {
|
||||
PopupFactoryImpl.ActionItem item = (PopupFactoryImpl.ActionItem)elementAt;
|
||||
String s = item.getText();
|
||||
if (labelMatcher.matches(s)) {
|
||||
new JListFixture(robot, list).clickItem(i);
|
||||
return;
|
||||
}
|
||||
items.add(s);
|
||||
} else { // For example package private class IntentionActionWithTextCaching used in quickfix popups
|
||||
String s = elementAt.toString();
|
||||
if (labelMatcher.matches(s)) {
|
||||
new JListFixture(robot, list).clickItem(i);
|
||||
return;
|
||||
}
|
||||
items.add(s);
|
||||
}
|
||||
}
|
||||
|
||||
if (items.isEmpty()) {
|
||||
fail("Could not find any menu items in popup");
|
||||
}
|
||||
fail("Did not find menu item '" + labelMatcher + "' among " + on(", ").join(items));
|
||||
}
|
||||
|
||||
/** Returns the root container containing the given component */
|
||||
@Nullable
|
||||
public static Container getRootContainer(@NotNull final Component component) {
|
||||
return execute(new GuiQuery<Container>() {
|
||||
@Override
|
||||
@Nullable
|
||||
protected Container executeInEDT() throws Throwable {
|
||||
return (Container)SwingUtilities.getRoot(component);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void findAndClickOkButton(@NotNull ContainerFixture<? extends Container> container) {
|
||||
findAndClickButton(container, "OK");
|
||||
}
|
||||
|
||||
public static void findAndClickCancelButton(@NotNull ContainerFixture<? extends Container> container) {
|
||||
findAndClickButton(container, "Cancel");
|
||||
}
|
||||
|
||||
public static void findAndClickButton(@NotNull ContainerFixture<? extends Container> container, @NotNull final String text) {
|
||||
Robot robot = container.robot();
|
||||
JButton button = findButton(container, text, robot);
|
||||
robot.click(button);
|
||||
}
|
||||
|
||||
public static void findAndClickButtonWhenEnabled(@NotNull ContainerFixture<? extends Container> container, @NotNull final String text) {
|
||||
Robot robot = container.robot();
|
||||
final JButton button = findButton(container, text, robot);
|
||||
pause(new Condition("Wait for button " + text + " to be enabled.") {
|
||||
@Override
|
||||
public boolean test() {
|
||||
return button.isEnabled() && button.isVisible() && button.isShowing();
|
||||
}
|
||||
}, SHORT_TIMEOUT);
|
||||
robot.click(button);
|
||||
}
|
||||
|
||||
public static void invokeMenuPathOnRobotIdle(IdeFrameFixture projectFrame, String... path) {
|
||||
projectFrame.robot().waitForIdle();
|
||||
projectFrame.invokeMenuPath(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the file with basename {@code fileBasename}
|
||||
*/
|
||||
public static void openFile(IdeFrameFixture projectFrame, String fileBasename) {
|
||||
invokeMenuPathOnRobotIdle(projectFrame, "Navigate", "File...");
|
||||
projectFrame.robot().waitForIdle();
|
||||
typeText("multifunction-jni.c", projectFrame.robot(), 30);
|
||||
projectFrame.robot().pressAndReleaseKey(KeyEvent.VK_ENTER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to line number {@code lineNum} of the currently active editor window.
|
||||
*/
|
||||
public static void navigateToLine(IdeFrameFixture projectFrame, int lineNum) {
|
||||
invokeMenuPathOnRobotIdle(projectFrame, "Navigate", "Line...");
|
||||
projectFrame.robot().enterText(Integer.toString(lineNum));
|
||||
projectFrame.robot().waitForIdle();
|
||||
projectFrame.robot().pressAndReleaseKey(KeyEvent.VK_ENTER);
|
||||
}
|
||||
|
||||
private static void typeText(String text, Robot robot, long delayAfterEachCharacterMillis) {
|
||||
robot.waitForIdle();
|
||||
for (int i = 0; i < text.length(); ++i) {
|
||||
robot.type(text.charAt(i));
|
||||
Pause.pause(delayAfterEachCharacterMillis, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static JButton findButton(@NotNull ContainerFixture<? extends Container> container, @NotNull final String text, Robot robot) {
|
||||
return robot.finder().find(container.target(), new GenericTypeMatcher<JButton>(JButton.class) {
|
||||
@Override
|
||||
protected boolean isMatching(@NotNull JButton button) {
|
||||
String buttonText = button.getText();
|
||||
if (buttonText != null) {
|
||||
return buttonText.trim().equals(text) && button.isShowing();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Returns a full path to the GUI data directory in the user's AOSP source tree, if known, or null */
|
||||
//@Nullable
|
||||
//public static File getTestDataDir() {
|
||||
// File aosp = getAospSourceDir();
|
||||
// return aosp != null ? new File(aosp, RELATIVE_DATA_PATH) : null;
|
||||
//}
|
||||
|
||||
|
||||
/** Waits for a first component which passes the given matcher to become visible */
|
||||
@NotNull
|
||||
public static <T extends Component> T waitUntilFound(@NotNull final Robot robot, @NotNull final GenericTypeMatcher<T> matcher) {
|
||||
return waitUntilFound(robot, null, matcher);
|
||||
}
|
||||
|
||||
public static void skip(@NotNull String testName) {
|
||||
System.out.println("Skipping test '" + testName + "'");
|
||||
}
|
||||
|
||||
/** Waits for a first component which passes the given matcher under the given root to become visible. */
|
||||
@NotNull
|
||||
public static <T extends Component> T waitUntilFound(@NotNull final Robot robot,
|
||||
@Nullable final Container root,
|
||||
@NotNull final GenericTypeMatcher<T> matcher) {
|
||||
final AtomicReference<T> reference = new AtomicReference<T>();
|
||||
pause(new Condition("Find component using " + matcher.toString()) {
|
||||
@Override
|
||||
public boolean test() {
|
||||
ComponentFinder finder = robot.finder();
|
||||
Collection<T> allFound = root != null ? finder.findAll(root, matcher) : finder.findAll(matcher);
|
||||
boolean found = allFound.size() == 1;
|
||||
if (found) {
|
||||
reference.set(getFirstItem(allFound));
|
||||
}
|
||||
else if (allFound.size() > 1) {
|
||||
// Only allow a single component to be found, otherwise you can get some really confusing
|
||||
// test failures; the matcher should pick a specific enough instance
|
||||
fail("Found more than one " + matcher.supportedType().getSimpleName() + " which matches the criteria: " + allFound);
|
||||
}
|
||||
return found;
|
||||
}
|
||||
}, SHORT_TIMEOUT);
|
||||
|
||||
return reference.get();
|
||||
}
|
||||
|
||||
/** Waits until no components match the given criteria under the given root */
|
||||
public static <T extends Component> void waitUntilGone(@NotNull final Robot robot,
|
||||
@NotNull final Container root,
|
||||
@NotNull final GenericTypeMatcher<T> matcher) {
|
||||
pause(new Condition("Find component using " + matcher.toString()) {
|
||||
@Override
|
||||
public boolean test() {
|
||||
Collection<T> allFound = robot.finder().findAll(root, matcher);
|
||||
return allFound.isEmpty();
|
||||
}
|
||||
}, SHORT_TIMEOUT);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getSystemPropertyOrEnvironmentVariable(@NotNull String name) {
|
||||
String s = System.getProperty(name);
|
||||
return s == null ? System.getenv(name) : s;
|
||||
}
|
||||
|
||||
private static class MyProjectManagerListener extends ProjectManagerAdapter {
|
||||
boolean myActive;
|
||||
boolean myNotified;
|
||||
|
||||
@Override
|
||||
public void projectOpened(Project project) {
|
||||
myNotified = true;
|
||||
}
|
||||
}
|
||||
|
||||
private static class PrefixMatcher extends BaseMatcher<String> {
|
||||
|
||||
private final String prefix;
|
||||
|
||||
public PrefixMatcher(String prefix) {
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(Object item) {
|
||||
return item instanceof String && ((String)item).startsWith(prefix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeTo(Description description) {
|
||||
description.appendText("with prefix '" + prefix +"'");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.framework;
|
||||
|
||||
import com.intellij.openapi.projectRoots.JavaSdkVersion;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import static com.intellij.openapi.projectRoots.JavaSdkVersion.JDK_1_6;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface IdeGuiTest {
|
||||
boolean closeProjectBeforeExecution() default true;
|
||||
int retryCount() default 0;
|
||||
JavaSdkVersion runWithMinimumJdkVersion() default JDK_1_6;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.framework;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Settings to be applied when executing every test method in a class.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface IdeGuiTestSetup {
|
||||
boolean skipSourceGenerationOnSync() default false;
|
||||
boolean takeScreenshotOnTestFailure() default true;
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.framework;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.intellij.ide.BootstrapClassLoaderUtil;
|
||||
import com.intellij.ide.WindowsCommandLineProcessor;
|
||||
import com.intellij.ide.startup.StartupActionScriptManager;
|
||||
import com.intellij.idea.Main;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.PathManager;
|
||||
import com.intellij.openapi.application.ex.ApplicationEx;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.util.SystemProperties;
|
||||
import com.intellij.util.lang.UrlClassLoader;
|
||||
import com.intellij.util.text.StringTokenizer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.intellij.openapi.application.PathManager.PROPERTY_CONFIG_PATH;
|
||||
import static com.intellij.openapi.util.io.FileUtil.*;
|
||||
import static com.intellij.openapi.util.text.StringUtil.isNotEmpty;
|
||||
import static com.intellij.tests.gui.framework.GuiTests.getProjectCreationDirPath;
|
||||
import static com.intellij.util.ArrayUtil.EMPTY_STRING_ARRAY;
|
||||
import static com.intellij.util.ui.UIUtil.initDefaultLAF;
|
||||
import static org.fest.assertions.Assertions.assertThat;
|
||||
import static org.fest.reflect.core.Reflection.method;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
public class IdeTestApplication implements Disposable {
|
||||
private static final Logger LOG = Logger.getInstance(IdeTestApplication.class);
|
||||
private static final String PROPERTY_IGNORE_CLASSPATH = "ignore.classpath";
|
||||
private static final String PROPERTY_ALLOW_BOOTSTRAP_RESOURCES = "idea.allow.bootstrap.resources";
|
||||
private static final String PROPERTY_ADDITIONAL_CLASSPATH = "idea.additional.classpath";
|
||||
|
||||
private static IdeTestApplication ourInstance;
|
||||
|
||||
@NotNull private final ClassLoader myIdeClassLoader;
|
||||
|
||||
@NotNull
|
||||
public static synchronized IdeTestApplication getInstance() throws Exception {
|
||||
//System.setProperty(PLATFORM_PREFIX_KEY, "AndroidStudio");
|
||||
//System.setProperty(PLATFORM_PREFIX_KEY, "idea");
|
||||
File configDirPath = getConfigDirPath();
|
||||
System.setProperty(PROPERTY_CONFIG_PATH, configDirPath.getPath());
|
||||
|
||||
// Force Swing FileChooser on Mac (instead of native one) to be able to use FEST to drive it.
|
||||
System.setProperty("native.mac.file.chooser.enabled", "false");
|
||||
|
||||
if (!isLoaded()) {
|
||||
ourInstance = new IdeTestApplication();
|
||||
recreateDirectory(configDirPath);
|
||||
|
||||
File newProjectsRootDirPath = getProjectCreationDirPath();
|
||||
recreateDirectory(newProjectsRootDirPath);
|
||||
|
||||
ClassLoader ideClassLoader = ourInstance.getIdeClassLoader();
|
||||
Class<?> clazz = ideClassLoader.loadClass(GuiTests.class.getCanonicalName());
|
||||
method("waitForIdeToStart").in(clazz).invoke();
|
||||
method("setUpDefaultGeneralSettings").in(clazz).invoke();
|
||||
}
|
||||
|
||||
return ourInstance;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static File getConfigDirPath() throws IOException {
|
||||
File dirPath = new File(getGuiTestRootDirPath(), "config");
|
||||
ensureExists(dirPath);
|
||||
return dirPath;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static File getFailedTestScreenshotDirPath() throws IOException {
|
||||
File dirPath = new File(getGuiTestRootDirPath(), "failures");
|
||||
ensureExists(dirPath);
|
||||
return dirPath;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static File getGuiTestRootDirPath() throws IOException {
|
||||
String guiTestRootDirPathProperty = System.getProperty("gui.tests.root.dir.path");
|
||||
if (isNotEmpty(guiTestRootDirPathProperty)) {
|
||||
File rootDirPath = new File(guiTestRootDirPathProperty);
|
||||
if (rootDirPath.isDirectory()) {
|
||||
return rootDirPath;
|
||||
}
|
||||
}
|
||||
String homeDirPath = toSystemDependentName(PathManager.getHomePath());
|
||||
assertThat(homeDirPath).isNotEmpty();
|
||||
File rootDirPath = new File(homeDirPath, join("androidStudio", "gui-tests"));
|
||||
ensureExists(rootDirPath);
|
||||
return rootDirPath;
|
||||
}
|
||||
|
||||
private static void recreateDirectory(@NotNull File path) throws IOException {
|
||||
delete(path);
|
||||
ensureExists(path);
|
||||
}
|
||||
|
||||
private IdeTestApplication() throws Exception {
|
||||
String[] args = EMPTY_STRING_ARRAY;
|
||||
|
||||
LOG.assertTrue(ourInstance == null, "Only one instance allowed.");
|
||||
ourInstance = this;
|
||||
|
||||
pluginManagerStart(args);
|
||||
mainMain();
|
||||
|
||||
myIdeClassLoader = createClassLoader();
|
||||
//myIdeClassLoader = BootstrapClassLoaderUtil.initClassLoader(false);
|
||||
|
||||
|
||||
WindowsCommandLineProcessor.ourMirrorClass = Class.forName(WindowsCommandLineProcessor.class.getName(), true, myIdeClassLoader);
|
||||
|
||||
// We turn on "GUI Testing Mode" right away, even before loading the IDE.
|
||||
//Class<?> androidPluginClass = Class.forName("org.jetbrains.android.AndroidPlugin", true, myIdeClassLoader);
|
||||
//method("setGuiTestingMode").withParameterTypes(boolean.class).in(androidPluginClass).invoke(true);
|
||||
|
||||
Class<?> classUtilCoreClass = Class.forName("com.intellij.ide.ClassUtilCore", true, myIdeClassLoader);
|
||||
method("clearJarURLCache").in(classUtilCoreClass).invoke();
|
||||
|
||||
Class<?> pluginManagerClass = Class.forName("com.intellij.ide.plugins.PluginManager", true, myIdeClassLoader);
|
||||
method("start").withParameterTypes(String.class, String.class, String[].class)
|
||||
.in(pluginManagerClass)
|
||||
.invoke("com.intellij.idea.MainImpl", "start", args);
|
||||
}
|
||||
|
||||
// This method replaces BootstrapClassLoaderUtil.initClassLoader. The reason behind it is that when running UI tests the ClassLoader
|
||||
// containing the URLs for the plugin jars is loaded by a different ClassLoader and it gets ignored. The result is test failing because
|
||||
// classes like AndroidPlugin cannot be found.
|
||||
@NotNull
|
||||
private static ClassLoader createClassLoader() throws MalformedURLException, URISyntaxException {
|
||||
Collection<URL> classpath = Sets.newLinkedHashSet();
|
||||
addIdeaLibraries(classpath);
|
||||
addAdditionalClassPath(classpath);
|
||||
|
||||
UrlClassLoader.Builder builder = UrlClassLoader.build()
|
||||
.urls(filterClassPath(Lists.newArrayList(classpath)))
|
||||
.parent(IdeTestApplication.class.getClassLoader())
|
||||
.allowLock(false)
|
||||
.usePersistentClasspathIndexForLocalClassDirectories();
|
||||
if (SystemProperties.getBooleanProperty(PROPERTY_ALLOW_BOOTSTRAP_RESOURCES, true)) {
|
||||
builder.allowBootstrapResources();
|
||||
}
|
||||
|
||||
UrlClassLoader newClassLoader = builder.get();
|
||||
|
||||
// prepare plugins
|
||||
try {
|
||||
StartupActionScriptManager.executeActionScript();
|
||||
}
|
||||
catch (IOException e) {
|
||||
Main.showMessage("Plugin Installation Error", e);
|
||||
}
|
||||
|
||||
Thread.currentThread().setContextClassLoader(newClassLoader);
|
||||
return newClassLoader;
|
||||
}
|
||||
|
||||
private static void addIdeaLibraries(@NotNull Collection<URL> classpath) throws MalformedURLException {
|
||||
Class<BootstrapClassLoaderUtil> aClass = BootstrapClassLoaderUtil.class;
|
||||
String selfRoot = PathManager.getResourceRoot(aClass, "/" + aClass.getName().replace('.', '/') + ".class");
|
||||
assertNotNull(selfRoot);
|
||||
|
||||
URL selfRootUrl = new File(selfRoot).getAbsoluteFile().toURI().toURL();
|
||||
classpath.add(selfRootUrl);
|
||||
|
||||
File libFolder = new File(PathManager.getLibPath());
|
||||
addLibraries(classpath, libFolder, selfRootUrl);
|
||||
addLibraries(classpath, new File(libFolder, "ext"), selfRootUrl);
|
||||
addLibraries(classpath, new File(libFolder, "ant/lib"), selfRootUrl);
|
||||
}
|
||||
|
||||
private static void addLibraries(@NotNull Collection<URL> classPath, @NotNull File fromDir, @NotNull URL selfRootUrl)
|
||||
throws MalformedURLException {
|
||||
for (File file : notNullize(fromDir.listFiles())) {
|
||||
if (isJarOrZip(file)) {
|
||||
URL url = file.toURI().toURL();
|
||||
if (!selfRootUrl.equals(url)) {
|
||||
classPath.add(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void addAdditionalClassPath(@NotNull Collection<URL> classpath) throws MalformedURLException {
|
||||
StringTokenizer tokenizer = new StringTokenizer(System.getProperty(PROPERTY_ADDITIONAL_CLASSPATH, ""), File.pathSeparator, false);
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
String pathItem = tokenizer.nextToken();
|
||||
classpath.add(new File(pathItem).toURI().toURL());
|
||||
}
|
||||
}
|
||||
|
||||
private static List<URL> filterClassPath(@NotNull List<URL> classpath) {
|
||||
String ignoreProperty = System.getProperty(PROPERTY_IGNORE_CLASSPATH);
|
||||
if (ignoreProperty != null) {
|
||||
Pattern pattern = Pattern.compile(ignoreProperty);
|
||||
for (Iterator<URL> i = classpath.iterator(); i.hasNext(); ) {
|
||||
String url = i.next().toExternalForm();
|
||||
if (pattern.matcher(url).matches()) {
|
||||
i.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
return classpath;
|
||||
}
|
||||
|
||||
private static void pluginManagerStart(@NotNull String[] args) {
|
||||
// Duplicates what PluginManager#start does.
|
||||
Main.setFlags(args);
|
||||
initDefaultLAF();
|
||||
}
|
||||
|
||||
private static void mainMain() {
|
||||
// Duplicates what Main#main does.
|
||||
method("installPatch").in(Main.class).invoke();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ClassLoader getIdeClassLoader() {
|
||||
return myIdeClassLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
disposeInstance();
|
||||
}
|
||||
|
||||
public static synchronized void disposeInstance() {
|
||||
if (!isLoaded()) {
|
||||
return;
|
||||
}
|
||||
Application application = ApplicationManager.getApplication();
|
||||
if (application != null) {
|
||||
if (application instanceof ApplicationEx) {
|
||||
((ApplicationEx)application).exit(true, true);
|
||||
}
|
||||
else {
|
||||
application.exit();
|
||||
}
|
||||
}
|
||||
ourInstance = null;
|
||||
}
|
||||
|
||||
public static synchronized boolean isLoaded() {
|
||||
return ourInstance != null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.framework;
|
||||
|
||||
import org.fest.swing.image.ScreenshotTaker;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.runners.model.FrameworkMethod;
|
||||
import org.junit.runners.model.Statement;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Method;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
import static com.intellij.tests.gui.framework.GuiTestRunner.canRunGuiTests;
|
||||
import static com.intellij.tests.gui.framework.IdeTestApplication.getFailedTestScreenshotDirPath;
|
||||
import static org.fest.reflect.core.Reflection.field;
|
||||
import static org.fest.reflect.core.Reflection.method;
|
||||
|
||||
public class MethodInvoker extends Statement {
|
||||
@NotNull private final GuiTestConfigurator myTestConfigurator;
|
||||
@NotNull private final FrameworkMethod myTestMethod;
|
||||
@NotNull private final Object myTest;
|
||||
@NotNull private final ScreenshotTaker myScreenshotTaker;
|
||||
|
||||
MethodInvoker(@NotNull FrameworkMethod testMethod, @NotNull Object test, @NotNull ScreenshotTaker screenshotTaker) throws Throwable {
|
||||
myTestConfigurator = GuiTestConfigurator.createNew(testMethod.getMethod(), test);
|
||||
myTestMethod = testMethod;
|
||||
myTest = test;
|
||||
myScreenshotTaker = screenshotTaker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evaluate() throws Throwable {
|
||||
//if (myTestConfigurator.shouldSkipTest()) {
|
||||
// Message already printed in console.
|
||||
//return;
|
||||
//}
|
||||
String testFqn = getTestFqn();
|
||||
if (doesIdeHaveFatalErrors()) {
|
||||
// Fatal errors were caused by previous test. Skipping this test.
|
||||
System.out.println(String.format("Skipping test '%1$s': a fatal error has occurred in the IDE", testFqn));
|
||||
return;
|
||||
}
|
||||
System.out.println(String.format("Executing test '%1$s'", testFqn));
|
||||
|
||||
int retryCount = myTestConfigurator.getRetryCount();
|
||||
for (int i = 0; i <= retryCount; i++) {
|
||||
if (i > 0) {
|
||||
System.out.println(String.format("Retrying execution of test '%1$s'", testFqn));
|
||||
}
|
||||
try {
|
||||
runTest(i);
|
||||
break; // no need to retry.
|
||||
}
|
||||
catch (Throwable throwable) {
|
||||
if (retryCount == i) {
|
||||
throw throwable; // Last run, throw any exceptions caught.
|
||||
}
|
||||
else {
|
||||
throwable.printStackTrace();
|
||||
failIfIdeHasFatalErrors();
|
||||
}
|
||||
}
|
||||
}
|
||||
failIfIdeHasFatalErrors();
|
||||
}
|
||||
|
||||
public static boolean doesIdeHaveFatalErrors() {
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
try {
|
||||
Class<?> guiTestsType = Class.forName(GuiTests.class.getCanonicalName(), true, classLoader);
|
||||
//noinspection ConstantConditions
|
||||
return method("doesIdeHaveFatalErrors").withReturnType(boolean.class).in(guiTestsType).invoke();
|
||||
} catch (ClassNotFoundException ex) {
|
||||
// ignore exception
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static void failIfIdeHasFatalErrors() throws ClassNotFoundException {
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
Class<?> guiTestsType = Class.forName(GuiTests.class.getCanonicalName(), true, classLoader);
|
||||
method("failIfIdeHasFatalErrors").in(guiTestsType).invoke();
|
||||
}
|
||||
|
||||
private void runTest(int executionIndex) throws Throwable {
|
||||
myTestConfigurator.executeSetupTasks();
|
||||
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
Class<?> guiTestCaseType = Class.forName(GuiTestCase.class.getCanonicalName(), true, classLoader);
|
||||
|
||||
if (guiTestCaseType.isInstance(myTest)) {
|
||||
if (!canRunGuiTests()) {
|
||||
// We don't run tests in headless environment.
|
||||
return;
|
||||
}
|
||||
field("myTestName").ofType(String.class).in(myTest).set(myTestMethod.getName());
|
||||
}
|
||||
try {
|
||||
myTestMethod.invokeExplosively(myTest);
|
||||
}
|
||||
catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
takeScreenshot(executionIndex);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String getTestFqn() {
|
||||
return myTestMethod.getMethod().getDeclaringClass() + "#" + myTestMethod.getName();
|
||||
}
|
||||
|
||||
private void takeScreenshot(int executionIndex) {
|
||||
if (myTestConfigurator.shouldTakeScreenshotOnFailure()) {
|
||||
Method method = myTestMethod.getMethod();
|
||||
String fileNamePrefix = method.getDeclaringClass().getSimpleName() + "." + (executionIndex + 1) + "." + method.getName();
|
||||
String extension = ".png";
|
||||
|
||||
try {
|
||||
File rootDir = getFailedTestScreenshotDirPath();
|
||||
|
||||
File screenshotFilePath = new File(rootDir, fileNamePrefix + extension);
|
||||
if (screenshotFilePath.isFile()) {
|
||||
SimpleDateFormat format = new SimpleDateFormat("MM-dd-yyyy.HH:mm:ss");
|
||||
String now = format.format(new GregorianCalendar().getTime());
|
||||
screenshotFilePath = new File(rootDir, fileNamePrefix + "." + now + extension);
|
||||
}
|
||||
myScreenshotTaker.saveDesktopAsPng(screenshotFilePath.getPath());
|
||||
System.out.println("Screenshot of failed test taken and stored at " + screenshotFilePath.getPath());
|
||||
}
|
||||
catch (Throwable ignored) {
|
||||
System.out.println("Failed to take screenshot. Cause: " + ignored.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* 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.tests.gui.matcher;
|
||||
|
||||
import org.fest.swing.core.GenericTypeMatcher;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* Matcher that checks the name of the actual implementation. This is useful when matching
|
||||
* against non-public IJ classes.
|
||||
*/
|
||||
public class ClassNameMatcher<T extends Component> extends GenericTypeMatcher<T> {
|
||||
private final String myClassName;
|
||||
|
||||
private ClassNameMatcher(String className, Class<T> supportedType) {
|
||||
super(supportedType);
|
||||
myClassName = className;
|
||||
}
|
||||
|
||||
private ClassNameMatcher(String className, Class<T> supportedType, boolean requireShowing) {
|
||||
super(supportedType, requireShowing);
|
||||
myClassName = className;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isMatching(@NotNull T component) {
|
||||
return myClassName.equals(component.getClass().getName());
|
||||
}
|
||||
|
||||
public static <T extends Component> ClassNameMatcher<T> forClass(String className, Class<T> supportedType) {
|
||||
return new ClassNameMatcher<T>(className, supportedType);
|
||||
}
|
||||
|
||||
public static <T extends Component> ClassNameMatcher<T> forClass(String className, Class<T> supportedType, boolean requireShowing) {
|
||||
return new ClassNameMatcher<T>(className, supportedType, requireShowing);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user