blob: 4850c60953f8325c97046b53dd6f1c342927d6bd [file] [log] [blame]
/*
* 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.android.tools.idea.tests.gui.framework.fixture;
import com.android.tools.idea.gradle.GradleSyncState;
import com.android.tools.idea.gradle.IdeaAndroidProject;
import com.android.tools.idea.gradle.compiler.AndroidGradleBuildConfiguration;
import com.android.tools.idea.gradle.compiler.PostProjectBuildTasksExecutor;
import com.android.tools.idea.gradle.invoker.GradleInvocationResult;
import com.android.tools.idea.gradle.util.BuildMode;
import com.android.tools.idea.gradle.util.GradleUtil;
import com.android.tools.idea.gradle.util.ProjectBuilder;
import com.android.tools.idea.tests.gui.framework.fixture.avdmanager.AvdManagerDialogFixture;
import com.google.common.base.Charsets;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.google.common.io.Files;
import com.intellij.codeInspection.ui.InspectionTree;
import com.intellij.execution.BeforeRunTask;
import com.intellij.execution.BeforeRunTaskProvider;
import com.intellij.execution.RunnerAndConfigurationSettings;
import com.intellij.execution.configurations.ConfigurationFactory;
import com.intellij.execution.configurations.ConfigurationType;
import com.intellij.execution.configurations.RunConfiguration;
import com.intellij.execution.impl.RunManagerImpl;
import com.intellij.ide.RecentProjectsManager;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.compiler.CompilationStatusListener;
import com.intellij.openapi.compiler.CompileContext;
import com.intellij.openapi.compiler.CompilerManager;
import com.intellij.openapi.extensions.Extensions;
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.options.ex.IdeConfigurablesGroup;
import com.intellij.openapi.options.ex.ProjectConfigurablesGroup;
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.util.ThreeState;
import com.intellij.util.messages.MessageBusConnection;
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.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.model.module.JpsModuleSourceRootType;
import org.jetbrains.plugins.gradle.settings.GradleProjectSettings;
import org.jetbrains.plugins.gradle.settings.GradleSettings;
import javax.swing.*;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static com.android.SdkConstants.FD_GRADLE;
import static com.android.SdkConstants.FN_BUILD_GRADLE;
import static com.android.tools.idea.gradle.GradleSyncState.GRADLE_SYNC_TOPIC;
import static com.android.tools.idea.gradle.compiler.PostProjectBuildTasksExecutor.GRADLE_BUILD_TOPIC;
import static com.android.tools.idea.gradle.util.BuildMode.COMPILE_JAVA;
import static com.android.tools.idea.gradle.util.BuildMode.SOURCE_GEN;
import static com.android.tools.idea.gradle.util.GradleUtil.findWrapperPropertiesFile;
import static com.android.tools.idea.gradle.util.GradleUtil.updateGradleDistributionUrl;
import static com.android.tools.idea.tests.gui.framework.GuiTests.*;
import static com.android.tools.idea.tests.gui.framework.fixture.LibraryPropertiesDialogFixture.showPropertiesDialog;
import static com.google.common.io.Files.write;
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 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.jetbrains.android.AndroidPlugin.*;
import static org.jetbrains.plugins.gradle.settings.DistributionType.LOCAL;
import static org.junit.Assert.*;
public class IdeFrameFixture extends ComponentFixture<IdeFrameFixture, IdeFrameImpl> {
@NotNull private final File myProjectPath;
@NotNull private final GradleProjectEventListener myGradleProjectEventListener;
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 && 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();
MessageBusConnection connection = project.getMessageBus().connect(disposable);
connection.subscribe(GRADLE_SYNC_TOPIC, myGradleProjectEventListener);
connection.subscribe(GRADLE_BUILD_TOPIC, 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 IdeaAndroidProject getAndroidProjectForModule(@NotNull String name) {
Module module = getModule(name);
AndroidFacet facet = AndroidFacet.getInstance(module);
if (facet != null && facet.requiresAndroidModel()) {
IdeaAndroidProject androidModel = facet.getAndroidModel();
if (androidModel != null) {
return androidModel;
}
}
throw new AssertionError("Unable to find IdeaAndroidProject for module " + quote(name));
}
@NotNull
public Multimap<JpsModuleSourceRootType, String> getSourceFolderRelativePaths(@NotNull String moduleName) {
final Multimap<JpsModuleSourceRootType, String> sourceFoldersByType = ArrayListMultimap.create();
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()) {
String path = urlToPath(folder.getUrl());
String relativePath = getRelativePath(myProjectPath, new File(toSystemDependentName(path)));
sourceFoldersByType.put(folder.getRootType(), relativePath);
}
}
}
finally {
rootModel.dispose();
}
}
});
return sourceFoldersByType;
}
@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) {
for (Module module : getModuleManager().getModules()) {
if (name.equals(module.getName())) {
return module;
}
}
throw new AssertionError("Unable to find module with name " + quote(name));
}
@NotNull
private ModuleManager getModuleManager() {
return ModuleManager.getInstance(getProject());
}
@NotNull
public EditorFixture getEditor() {
if (myEditor == null) {
myEditor = new EditorFixture(robot(), this);
}
return myEditor;
}
@NotNull
public IdeaAndroidProject getAndroidModel(@NotNull String moduleName) {
Module module = getModule(moduleName);
assertNotNull("Could not find module " + moduleName, module);
AndroidFacet facet = AndroidFacet.getInstance(module);
assertNotNull("Module " + moduleName + " is not an Android module", facet);
assertTrue("Module " + moduleName + " is not a Gradle project", facet.requiresAndroidModel());
IdeaAndroidProject androidModel = facet.getAndroidModel();
assertNotNull("Module " + moduleName + " does not have a Gradle project (not synced yet or sync failed?)", androidModel);
return androidModel;
}
@NotNull
public GradleInvocationResult invokeProjectMake() {
return invokeProjectMake(null);
}
@NotNull
public GradleInvocationResult invokeProjectMake(@Nullable Runnable executeAfterInvokingMake) {
myGradleProjectEventListener.reset();
final AtomicReference<GradleInvocationResult> resultRef = new AtomicReference<GradleInvocationResult>();
ProjectBuilder.getInstance(getProject()).addAfterProjectBuildTask(new ProjectBuilder.AfterProjectBuildTask() {
@Override
public void execute(@NotNull GradleInvocationResult result) {
resultRef.set(result);
}
@Override
public boolean execute(CompileContext context) {
return false;
}
});
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;
}
@NotNull
public IdeFrameFixture invokeProjectMakeWithGradleOutput(@NotNull String output) {
ApplicationManager.getApplication().putUserData(GRADLE_BUILD_OUTPUT_IN_GUI_TEST_KEY, output);
selectProjectMakeAction();
return this;
}
@NotNull
public IdeFrameFixture waitUntilFakeGradleOutputIsApplied() {
final Application application = ApplicationManager.getApplication();
if (application.getUserData(GRADLE_BUILD_OUTPUT_IN_GUI_TEST_KEY) == null) {
fail("No fake gradle output is configured");
}
pause(new Condition("Waiting for fake gradle output to be applied") {
@Override
public boolean test() {
return application.getUserData(GRADLE_BUILD_OUTPUT_IN_GUI_TEST_KEY) == null;
}
}, SHORT_TIMEOUT);
String fakeOutput = application.getUserData(GRADLE_BUILD_OUTPUT_IN_GUI_TEST_KEY);
if (fakeOutput != null) {
fail(String.format("Fake gradle output (%s) is not applied in %d ms", fakeOutput, SHORT_TIMEOUT.duration()));
}
return this;
}
@NotNull
public CompileContext invokeProjectMakeUsingJps() {
final Project project = getProject();
AndroidGradleBuildConfiguration buildConfiguration = AndroidGradleBuildConfiguration.getInstance(project);
buildConfiguration.USE_EXPERIMENTAL_FASTER_BUILD = false;
final AtomicReference<CompileContext> contextRef = new AtomicReference<CompileContext>();
CompilerManager compilerManager = CompilerManager.getInstance(project);
Disposable disposable = new NoOpDisposable();
compilerManager.addCompilationStatusListener(new CompilationStatusListener() {
@Override
public void compilationFinished(boolean aborted, int errors, int warnings, CompileContext compileContext) {
contextRef.set(compileContext);
}
@Override
public void fileGenerated(String outputRoot, String relativePath) {
}
}, disposable);
try {
selectProjectMakeAction();
pause(new Condition("Build (" + COMPILE_JAVA + ") for project " + quote(project.getName()) + " to finish'") {
@Override
public boolean test() {
CompileContext context = contextRef.get();
return context != null;
}
}, LONG_TIMEOUT);
CompileContext context = contextRef.get();
assertNotNull(context);
return context;
}
finally {
Disposer.dispose(disposable);
}
}
/**
* 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 ChooseDeviceDialogFixture findChooseDeviceDialog() {
return ChooseDeviceDialogFixture.find(robot());
}
@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 && !ProjectBuilder.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 RuntimeException(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);
findActionButtonByActionId("Android.SyncProject").click();
return this;
}
@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);
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 AndroidToolWindowFixture getAndroidToolWindow() {
return new AndroidToolWindowFixture(getProject(), robot());
}
@NotNull
public CapturesToolWindowFixture getCapturesToolWindow() {
return new CapturesToolWindowFixture(getProject(), robot());
}
@NotNull
public BuildVariantsToolWindowFixture getBuildVariantsWindow() {
return new BuildVariantsToolWindowFixture(this);
}
@NotNull
public MessagesToolWindowFixture getMessagesToolWindow() {
return new MessagesToolWindowFixture(getProject(), robot());
}
@NotNull
public GradleToolWindowFixture getGradleToolWindow() {
return new GradleToolWindowFixture(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, new ProjectConfigurablesGroup(project), new IdeConfigurablesGroup());
}
});
return IdeSettingsDialogFixture.find(robot());
}
@NotNull
public IdeFrameFixture deleteGradleWrapper() {
deleteWrapper(getProjectPath());
return this;
}
@NotNull
public IdeFrameFixture requireGradleWrapperSet() {
File wrapperDirPath = getGradleWrapperDirPath(getProjectPath());
assertThat(wrapperDirPath).as("Gradle wrapper").isDirectory();
return this;
}
public static void deleteWrapper(@NotNull File projectDirPath) {
File wrapperDirPath = getGradleWrapperDirPath(projectDirPath);
delete(wrapperDirPath);
assertThat(wrapperDirPath).as("Gradle wrapper").doesNotExist();
}
@NotNull
private static File getGradleWrapperDirPath(@NotNull File projectDirPath) {
return new File(projectDirPath, FD_GRADLE);
}
@NotNull
public IdeFrameFixture useLocalGradleDistribution(@NotNull File gradleHomePath) {
return useLocalGradleDistribution(gradleHomePath.getPath());
}
@NotNull
public IdeFrameFixture useLocalGradleDistribution(@NotNull String gradleHome) {
GradleProjectSettings settings = getGradleSettings();
settings.setDistributionType(LOCAL);
settings.setGradleHome(gradleHome);
return this;
}
@NotNull
public GradleProjectSettings getGradleSettings() {
GradleProjectSettings settings = GradleUtil.getGradleProjectSettings(getProject());
assertNotNull(settings);
return settings;
}
@NotNull
public AvdManagerDialogFixture invokeAvdManager() {
ActionButtonFixture button = findActionButtonByActionId("Android.RunAndroidAvdManager");
button.requireVisible();
button.requireEnabled();
button.click();
return AvdManagerDialogFixture.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 instanceof WelcomeFrame && frame.isShowing()) {
return true;
}
}
return false;
}
});
}
@NotNull
public LibraryPropertiesDialogFixture showPropertiesForLibrary(@NotNull String libraryName) {
return showPropertiesDialog(robot(), libraryName, getProject());
}
@NotNull
public MessagesFixture findMessageDialog(@NotNull String title) {
return MessagesFixture.findByTitle(robot(), target(), title);
}
@NotNull
public IdeFrameFixture setGradleJvmArgs(@NotNull final String jvmArgs) {
Project project = getProject();
final GradleSettings settings = GradleSettings.getInstance(project);
settings.setGradleVmOptions(jvmArgs);
pause(new Condition("Gradle settings to be set") {
@Override
public boolean test() {
return jvmArgs.equals(settings.getGradleVmOptions());
}
}, SHORT_TIMEOUT);
return this;
}
@NotNull
public IdeFrameFixture updateGradleWrapperVersion(@NotNull String version) throws IOException {
File wrapperPropertiesFile = findWrapperPropertiesFile(getProject());
assertNotNull(wrapperPropertiesFile);
updateGradleDistributionUrl(version, wrapperPropertiesFile);
return this;
}
@NotNull
public IdeFrameFixture updateAndroidModelVersion(@NotNull String version) throws IOException {
File buildFile = new File(getProjectPath(), FN_BUILD_GRADLE);
assertThat(buildFile).isFile();
String contents = Files.toString(buildFile, Charsets.UTF_8);
Pattern pattern = Pattern.compile("classpath ['\"]com.android.tools.build:gradle:(.+)['\"]");
Matcher matcher = pattern.matcher(contents);
if (matcher.find()) {
contents = contents.substring(0, matcher.start(1)) + version + contents.substring(matcher.end(1));
write(contents, buildFile, Charsets.UTF_8);
}
else {
fail("Cannot find declaration of Android plugin");
}
return this;
}
/**
* Sets the "Before Launch" tasks in the "JUnit Run Configuration" template.
* @param taskName the name of the "Before Launch" task (e.g. "Make", "Gradle-aware Make")
*/
@NotNull
public IdeFrameFixture setJUnitDefaultBeforeRunTask(@NotNull String taskName) {
Project project = getProject();
RunManagerImpl runManager = RunManagerImpl.getInstanceImpl(project);
ConfigurationType junitConfigurationType = runManager.getConfigurationType("JUnit");
assertNotNull("Failed to find run configuration type 'JUnit'", junitConfigurationType);
for (ConfigurationFactory configurationFactory : junitConfigurationType.getConfigurationFactories()) {
RunnerAndConfigurationSettings template = runManager.getConfigurationTemplate(configurationFactory);
RunConfiguration runConfiguration = template.getConfiguration();
BeforeRunTaskProvider<BeforeRunTask>[] taskProviders = Extensions.getExtensions(BeforeRunTaskProvider.EXTENSION_POINT_NAME, project);
BeforeRunTaskProvider targetProvider = null;
for (BeforeRunTaskProvider<? extends BeforeRunTask> provider : taskProviders) {
if (taskName.equals(provider.getName())) {
targetProvider = provider;
break;
}
}
assertNotNull(String.format("Failed to find task provider '%1$s'", taskName), targetProvider);
BeforeRunTask task = targetProvider.createTask(runConfiguration);
assertNotNull(task);
task.setEnabled(true);
runManager.setBeforeRunTasks(runConfiguration, Collections.singletonList(task), false);
}
return this;
}
@NotNull
public FindDialogFixture invokeFindInPathDialog() {
invokeMenuPath("Edit", "Find", "Find in Path...");
return FindDialogFixture.find(robot());
}
@NotNull
public FindToolWindowFixture getFindToolWindow() {
return new FindToolWindowFixture(this);
}
private static class NoOpDisposable implements Disposable {
@Override
public void dispose() {
}
}
private void selectApp(@NotNull String appName) throws ClassNotFoundException {
ComboBoxActionFixture comboBoxActionFixture = new ComboBoxActionFixture(robot(), this);
comboBoxActionFixture.selectApp(appName);
}
}