Merge branch 'master' of git.labs.intellij.net:idea/community
authorKirill Likhodedov <kirill.likhodedov@jetbrains.com>
Wed, 1 Sep 2010 07:46:32 +0000 (11:46 +0400)
committerKirill Likhodedov <kirill.likhodedov@jetbrains.com>
Wed, 1 Sep 2010 07:46:32 +0000 (11:46 +0400)
plugins/hg4idea/src/org/zmlx/hg4idea/HgUtil.java
plugins/hg4idea/src/org/zmlx/hg4idea/HgVFSListener.java
plugins/hg4idea/src/org/zmlx/hg4idea/command/HgAddCommand.java
plugins/hg4idea/src/org/zmlx/hg4idea/command/HgRemoveCommand.java
plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/HgAbstractTestCase.java
plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/HgCollaborativeTestCase.java
plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/HgDeleteTestCase.java
plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/HgMockVcsHelper.java [new file with mode: 0644]
plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/HgSingleUserTestCase.java
plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/HgTestChangeListManager.java
plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/VcsHelperListener.java [new file with mode: 0644]

index 0536b9fa0bbc5fbad11e9039a063045dc680436f..669a03062a5d5f27fc245a4bfa0843e5dc95eb75 100644 (file)
@@ -20,6 +20,7 @@ import com.intellij.openapi.vcs.VcsException;
 import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.ui.GuiUtils;
+import com.intellij.util.containers.HashMap;
 import com.intellij.vcsUtil.VcsUtil;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -28,10 +29,13 @@ import org.zmlx.hg4idea.command.HgWorkingCopyRevisionsCommand;
 
 import java.io.*;
 import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
+import java.util.Map;
 
 /**
- * <strong><font color="#FF0000">TODO JavaDoc.</font></strong>
+ * HgUtil is a collection of static utility methods for Mercurial.
  */
 public abstract class HgUtil {
   
@@ -225,14 +229,9 @@ public abstract class HgUtil {
     return vf;
   }
 
-  /**
-   * Gets the Mercurial root for the given file or throws a VcsException if non exists:
-   * the root should not only be in directory mappings, but also the .hg repository folder should exist.
-   * @see #getHgRootOrNull(com.intellij.openapi.project.Project, com.intellij.openapi.vcs.FilePath)
-   */
   @NotNull
-  public static VirtualFile getHgRootOrThrow(Project project, VirtualFile vf) throws VcsException {
-    return getHgRootOrThrow(project, VcsUtil.getFilePath(vf.getPath()));
+  public static VirtualFile getHgRootOrThrow(Project project, VirtualFile file) throws VcsException {
+    return getHgRootOrThrow(project, VcsUtil.getFilePath(file.getPath()));
   }
 
   /**
@@ -246,5 +245,26 @@ public abstract class HgUtil {
   public static boolean isMergeInProgress(@NotNull Project project, VirtualFile repository) {
     return new HgWorkingCopyRevisionsCommand(project).parents(repository).size() > 1;
   }
-
+  /**
+   * Groups the given files by their Mercurial repositories and returns the map of relative paths to files for each repository.
+   * @param hgFiles files to be grouped.
+   * @return key is repository, values is the non-empty list of relative paths to files, which belong to this repository. 
+   */
+  @NotNull
+  public static Map<VirtualFile, List<String>> getRelativePathsByRepository(Collection<HgFile> hgFiles) {
+    final Map<VirtualFile, List<String>> map = new HashMap<VirtualFile, List<String>>();
+    if (hgFiles == null) {
+      return map;
+    }
+    for(HgFile file : hgFiles) {
+      final VirtualFile repo = file.getRepo();
+      List<String> files = map.get(repo);
+      if (files == null) {
+        files = new ArrayList<String>();
+        map.put(repo, files);
+      }
+      files.add(file.getRelativePath());
+    }
+    return map;
+  }
 }
index 0c5141f6525d761c1f62903126911d4f746dcb7d..7ad4704f9206f93d630e1f752b1ddfc074218372 100644 (file)
@@ -15,6 +15,8 @@
  */
 package org.zmlx.hg4idea;
 
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.Task;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.vcs.*;
 import com.intellij.openapi.vcs.changes.ChangeListManagerImpl;
@@ -22,6 +24,7 @@ import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.util.ui.VcsBackgroundTask;
 import com.intellij.vcsUtil.VcsUtil;
+import org.jetbrains.annotations.NotNull;
 import org.zmlx.hg4idea.command.*;
 
 import java.util.*;
@@ -82,24 +85,45 @@ public class HgVFSListener extends VcsVFSListener {
   }
 
   @Override
-  protected void performAdding(Collection<VirtualFile> addedFiles, final Map<VirtualFile, VirtualFile> copyFromMap) {
-    (new VcsBackgroundTask<VirtualFile>(myProject,
-                                        HgVcsMessages.message("hg4idea.add.progress"),
-                                        VcsConfiguration.getInstance(myProject).getAddRemoveOption(),
-                                        addedFiles) {
-      protected void process(final VirtualFile file) throws VcsException {
-        if (file.isDirectory()) {
-          return;
+  protected void performAdding(final Collection<VirtualFile> addedFiles, final Map<VirtualFile, VirtualFile> copyFromMap) {
+    (new Task.ConditionalModal(myProject,
+                               HgVcsMessages.message("hg4idea.add.progress"),
+                               false,
+                               VcsConfiguration.getInstance(myProject).getAddRemoveOption() ) {
+      @Override public void run(@NotNull ProgressIndicator aProgressIndicator) {
+        final ArrayList<HgFile> adds = new ArrayList<HgFile>();
+        final HashMap<HgFile, HgFile> copies = new HashMap<HgFile, HgFile>(); // from -> to
+
+        // separate adds from copies
+        for (VirtualFile file : addedFiles) {
+          if (file.isDirectory()) {
+            continue;
+          }
+
+          final VirtualFile copyFrom = copyFromMap.get(file);
+          if (copyFrom != null) {
+            copies.put(new HgFile(myProject, copyFrom), new HgFile(myProject, file));
+          } else {
+            adds.add(new HgFile(myProject, file));
+          }
         }
-        final VirtualFile copyFrom = copyFromMap.get(file);
-        if (copyFrom != null) {
-          (new HgCopyCommand(myProject)).execute(new HgFile(myProject, copyFrom), new HgFile(myProject, file));
-        } else {
-          (new HgAddCommand(myProject)).execute(new HgFile(myProject, file));
+
+        // add for all files at once
+        if (!adds.isEmpty()) {
+          new HgAddCommand(myProject).execute(adds);
+        }
+
+        // copy needs to be run for each file separately
+        if (!copies.isEmpty()) {
+          for(Map.Entry<HgFile, HgFile> copy : copies.entrySet()) {
+            new HgCopyCommand(myProject).execute(copy.getKey(), copy.getValue());
+          }
         }
-        dirtyScopeManager.fileDirty(file);
-      }
 
+        for (VirtualFile file : addedFiles) {
+          dirtyScopeManager.fileDirty(file);
+        }
+      }
     }).queue();
   }
 
@@ -118,6 +142,29 @@ public class HgVFSListener extends VcsVFSListener {
     return HgVcsMessages.message("hg4idea.remove.single.body");
   }
 
+  @Override
+  protected VcsDeleteType needConfirmDeletion(VirtualFile file) {
+    //// newly added files (which were added to the repo but never committed) should be removed from the VCS,
+    //// but without user confirmation.
+    final FilePath filePath = VcsUtil.getFilePath(file.getPath());
+    final VirtualFile repo = HgUtil.getHgRootOrNull(myProject, filePath);
+    if (repo == null) {
+      return super.needConfirmDeletion(file);
+    }
+    final HgFile hgFile = new HgFile(repo, filePath);
+
+    final HgLogCommand logCommand = new HgLogCommand(myProject);
+    logCommand.setLogFile(true);
+    logCommand.setFollowCopies(false);
+    logCommand.setIncludeRemoved(true);
+    final List<HgFileRevision> localRevisions = logCommand.execute(hgFile, -1, true);
+    // file is newly added, if it doesn't have a history or if the last history action was deleting this file.
+    if (localRevisions == null || localRevisions.isEmpty() || localRevisions.get(0).getDeletedFiles().contains(hgFile.getRelativePath())) {
+      return VcsDeleteType.SILENT;
+    }
+    return VcsDeleteType.CONFIRM;
+  }
+
   protected void executeDelete() {
     final List<FilePath> filesToDelete = new ArrayList<FilePath>(myDeletedWithoutConfirmFiles);
     final List<FilePath> deletedFiles = new ArrayList<FilePath>(myDeletedFiles);
@@ -125,33 +172,9 @@ public class HgVFSListener extends VcsVFSListener {
     myDeletedFiles.clear();
 
     // skip unversioned files and files which are not under Mercurial
-    final List<FilePath> unversionedFilePaths = new ArrayList<FilePath>();
-    for (VirtualFile vf : ChangeListManagerImpl.getInstanceImpl(myProject).getUnversionedFiles()) {
-      unversionedFilePaths.add(VcsUtil.getFilePath(vf.getPath()));
-    }
-    skipUnversionedAndNotUnderHg(unversionedFilePaths, filesToDelete);
-    skipUnversionedAndNotUnderHg(unversionedFilePaths, deletedFiles);
-
-    // newly added files (which were added to the repo but never committed should be removed from the VCS,
-    // but without user confirmation.
-    for (Iterator<FilePath> it = deletedFiles.iterator(); it.hasNext(); ) {
-      final FilePath filePath = it.next();
-      final HgLogCommand logCommand = new HgLogCommand(myProject);
-      logCommand.setLogFile(true);
-      logCommand.setFollowCopies(false);
-      logCommand.setIncludeRemoved(true);
-      final VirtualFile repo = HgUtil.getHgRootOrNull(myProject, filePath);
-      if (repo == null) {
-        continue;
-      }
-      final HgFile hgFile = new HgFile(repo, filePath);
-      final List<HgFileRevision> localRevisions = logCommand.execute(hgFile, -1, true);
-      // file is newly added, if it doesn't have a history or if the last history action was deleting this file.
-      if (localRevisions == null || localRevisions.isEmpty() || localRevisions.get(0).getDeletedFiles().contains(hgFile.getRelativePath())) {
-        it.remove();
-        filesToDelete.add(filePath);
-      }
-    }
+    final ChangeListManagerImpl changeListManager = ChangeListManagerImpl.getInstanceImpl(myProject);
+    skipUnversionedAndNotUnderHg(changeListManager, filesToDelete);
+    skipUnversionedAndNotUnderHg(changeListManager, deletedFiles);
 
     // confirm removal from the VCS if needed
     if (myRemoveOption.getValue() != VcsShowConfirmationOption.Value.DO_NOTHING_SILENTLY) {
@@ -172,34 +195,43 @@ public class HgVFSListener extends VcsVFSListener {
   }
 
     /**
-     * Changes the given collection of files by filtering out unversioned files,
-     * files which are not under Mercurial repository, and
-     * newly added files (which were added to the repo, but never committed).
-     * @param unversionedFiles  unversioned files retrieved from the ChangeListManager.
-     *                          Passing as a parameter not to transform List<VirtualFile> to List<FilePath> twice.
+     * Changes the given collection of files by filtering out unversioned files and
+     * files which are not under Mercurial repository.
+     * @param changeListManager instance of the ChangeListManagerImpl to retrieve unversioned files from it.
      * @param filesToFilter     files to be filtered.
      */
-    private void skipUnversionedAndNotUnderHg(Collection<FilePath> unversionedFiles, Collection<FilePath> filesToFilter) {
+  private void skipUnversionedAndNotUnderHg(ChangeListManagerImpl changeListManager, Collection<FilePath> filesToFilter) {
     for (Iterator<FilePath> iter = filesToFilter.iterator(); iter.hasNext(); ) {
       final FilePath filePath = iter.next();
-      if (HgUtil.getHgRootOrNull(myProject, filePath) == null || unversionedFiles.contains(filePath)) {
+      if (HgUtil.getHgRootOrNull(myProject, filePath) == null || changeListManager.isUnversioned(filePath.getVirtualFile())) {
         iter.remove();
       }
     }
   }
 
   @Override
-  protected void performDeletion(List<FilePath> filesToDelete) {
-    (new VcsBackgroundTask<FilePath>(myProject,
+  protected void performDeletion( final List<FilePath> filesToDelete) {
+    (new Task.ConditionalModal(myProject,
                                         HgVcsMessages.message("hg4idea.remove.progress"),
-                                        VcsConfiguration.getInstance(myProject).getAddRemoveOption(),
-                                        filesToDelete) {
-      protected void process(final FilePath file) throws VcsException {
-        if (file.isDirectory()) {
-          return;
+                                        false,
+                                        VcsConfiguration.getInstance(myProject).getAddRemoveOption()) {
+      @Override public void run( @NotNull ProgressIndicator aProgressIndicator ) {
+        final ArrayList<HgFile> deletes = new ArrayList<HgFile>();
+        for (FilePath file : filesToDelete) {
+          if (file.isDirectory()) {
+            continue;
+          }
+
+          deletes.add(new HgFile(VcsUtil.getVcsRootFor(myProject, file), file));
+        }
+
+        if (!deletes.isEmpty()) {
+          new HgRemoveCommand(myProject).execute(deletes);
+        }
+
+        for (HgFile file : deletes) {
+          dirtyScopeManager.fileDirty(file.toFilePath());
         }
-        (new HgRemoveCommand(myProject)).execute(new HgFile(VcsUtil.getVcsRootFor(myProject, file), file));
-        dirtyScopeManager.fileDirty(file);
       }
 
     }).queue();
index 41566988ceb5a207d321177c0ce89216d13e669a..680c7f129c874037cb05cbb5f5f8a51fdd592172 100644 (file)
 package org.zmlx.hg4idea.command;
 
 import com.intellij.openapi.project.Project;
-import org.zmlx.hg4idea.HgFile;
+import com.intellij.openapi.vfs.VirtualFile;
 import org.jetbrains.annotations.NotNull;
+import org.zmlx.hg4idea.HgFile;
+import org.zmlx.hg4idea.HgUtil;
 
 import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
 
+/**
+ * A wrapper for 'hg add' command.
+ */
 public class HgAddCommand {
 
-  private final Project project;
+  private final Project myProject;
 
   public HgAddCommand(Project project) {
-    this.project = project;
+    myProject = project;
+  }
+
+  /**
+   * Adds given files to their Mercurial repositories.
+   * @param hgFiles files to be added.
+   */
+  public void execute(@NotNull HgFile... hgFiles) {
+    execute(Arrays.asList(hgFiles));
   }
 
-  public void execute(@NotNull HgFile hgFile) {
-    HgCommandService.getInstance(project)
-      .execute(hgFile.getRepo(), "add", Arrays.asList(hgFile.getRelativePath()));
+  /**
+   * Adds given files to their Mercurial repositories.
+   * @param hgFiles files to be added.
+   */
+  public void execute(@NotNull Collection<HgFile> hgFiles) {
+    for(Map.Entry<VirtualFile, List<String>> entry : HgUtil.getRelativePathsByRepository(hgFiles).entrySet()) {
+      HgCommandService.getInstance(myProject).execute(entry.getKey(), "add", entry.getValue());
+    }
   }
 
 }
index b89b85bcdec229f2087885b5000f7786b0f63efb..9e08ac08a3aa944ace5ddbcdce6bc6de2bc00d7c 100644 (file)
 package org.zmlx.hg4idea.command;
 
 import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
 import org.zmlx.hg4idea.HgFile;
+import org.zmlx.hg4idea.HgUtil;
 
 import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
 
+/**
+ * A wrapper for the 'hg remove' command.
+ */
 public class HgRemoveCommand {
 
-  private final Project project;
+  private final Project myProject;
 
   public HgRemoveCommand(Project project) {
-    this.project = project;
+    myProject = project;
   }
 
-  public void execute(HgFile hgFile) {
-    HgCommandService.getInstance(project)
-      .execute(hgFile.getRepo(), "remove", Arrays.asList("--after", hgFile.getRelativePath()));
+  /**
+   * Removes given files from their Mercurial repositories.
+   * @param hgFiles files to be removed.
+   */
+  public void execute(@NotNull HgFile... hgFiles) {
+    execute(Arrays.asList(hgFiles));
+  }
+
+  /**
+   * Removes given files from their Mercurial repositories.
+   * @param hgFiles files to be removed.
+   */
+  public void execute(@NotNull Collection<HgFile> hgFiles) {
+    for( Map.Entry<VirtualFile, List<String>> entry : HgUtil.getRelativePathsByRepository(hgFiles).entrySet()) {
+      List<String> filePaths = entry.getValue();
+      filePaths.add(0, "--after");
+      HgCommandService.getInstance(myProject).execute(entry.getKey(), "remove", filePaths);
+    }
   }
 
 }
index 6e92e76262d6b33062ac1f09e667f12967f0e29a..799189d51715c54d19e1c1f531a1ca16d6dcb0ab 100644 (file)
@@ -14,6 +14,7 @@ package org.zmlx.hg4idea.test;
 
 import com.intellij.execution.process.ProcessOutput;
 import com.intellij.openapi.application.PluginPathManager;
+import com.intellij.openapi.vcs.AbstractVcsHelper;
 import com.intellij.openapi.vcs.VcsConfiguration;
 import com.intellij.openapi.vcs.VcsShowConfirmationOption;
 import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager;
@@ -22,6 +23,7 @@ import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.testFramework.AbstractVcsTestCase;
 import com.intellij.vcsUtil.VcsUtil;
 import org.jetbrains.annotations.Nullable;
+import org.picocontainer.MutablePicoContainer;
 import org.testng.annotations.BeforeMethod;
 import org.zmlx.hg4idea.HgFile;
 import org.zmlx.hg4idea.HgVcs;
@@ -64,14 +66,18 @@ public abstract class HgAbstractTestCase extends AbstractVcsTestCase {
     myTraceClient = true;
   }
 
-  protected void enableSilentOperation(final VcsConfiguration.StandardConfirmation op) {
+  protected void doActionSilently(final VcsConfiguration.StandardConfirmation op) {
     setStandardConfirmation(HgVcs.VCS_NAME, op, VcsShowConfirmationOption.Value.DO_ACTION_SILENTLY);
   }
 
-  protected void disableSilentOperation(final VcsConfiguration.StandardConfirmation op) {
+  protected void doNothingSilently(final VcsConfiguration.StandardConfirmation op) {
     setStandardConfirmation(HgVcs.VCS_NAME, op, VcsShowConfirmationOption.Value.DO_NOTHING_SILENTLY);
   }
 
+  protected void showConfirmation(final VcsConfiguration.StandardConfirmation op) {
+    setStandardConfirmation(HgVcs.VCS_NAME, op, VcsShowConfirmationOption.Value.SHOW_CONFIRMATION);
+  }
+
   /**
    * Runs the hg command.
    *
@@ -111,8 +117,17 @@ public abstract class HgAbstractTestCase extends AbstractVcsTestCase {
     }
     return new HgFile(myWorkingCopyDir, fileToInclude);
   }
-                       
 
+  /**
+   * Registers HgMockVcsHelper as the AbstractVcsHelper.
+   */
+  protected HgMockVcsHelper registerMockVcsHelper() {
+    final String key = "com.intellij.openapi.vcs.AbstractVcsHelper";
+    final MutablePicoContainer picoContainer = (MutablePicoContainer) myProject.getPicoContainer();
+    picoContainer.unregisterComponent(key);
+    picoContainer.registerComponentImplementation(key, HgMockVcsHelper.class);
+    return (HgMockVcsHelper) AbstractVcsHelper.getInstance(myProject);
+  }
 
   protected VirtualFile makeFile(File file) throws IOException {
     file.createNewFile();
index 3c82f4cddf76c6e2817b4c5ca033c795d48aaa31..d157fd45b874a9fa197145a2a78038b7fded6323 100644 (file)
@@ -45,8 +45,8 @@ public class HgCollaborativeTestCase extends HgAbstractTestCase {
     activateVCS(HgVcs.VCS_NAME);
     myChangeListManager = new HgTestChangeListManager(myProject);
 
-    enableSilentOperation(VcsConfiguration.StandardConfirmation.ADD);
-    enableSilentOperation(VcsConfiguration.StandardConfirmation.REMOVE);
+    doActionSilently(VcsConfiguration.StandardConfirmation.ADD);
+    doActionSilently(VcsConfiguration.StandardConfirmation.REMOVE);
   }
 
 }
index 070cf7ea14e97c01e73402b8b6efe26aa6df3e86..f75746effe60e426db9a4d10467624b291c95e4a 100644 (file)
 // limitations under the License.
 package org.zmlx.hg4idea.test;
 
+import com.intellij.openapi.vcs.VcsConfiguration;
 import com.intellij.openapi.vfs.VirtualFile;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
 import java.io.File;
 
+import static org.testng.Assert.fail;
+
 public class HgDeleteTestCase extends HgSingleUserTestCase {
 
   @Test
@@ -62,4 +65,51 @@ public class HgDeleteTestCase extends HgSingleUserTestCase {
     verify(runHgOnProjectRepo("status"), HgTestOutputParser.removed("com", "a.txt"));
   }
 
+  /**
+   * When deleting a file which was newly added to repository, this file shouldn't be prompted for removal from the repository.
+   * 1. Create a file and add it to the repository.
+   * 2. Remove the file from disk.
+   * 3. File shouldn't be prompted for removal from repository.
+   */
+  @Test
+  public void testNewlyAddedFileShouldNotBePromptedForRemoval() throws Exception {
+    showConfirmation(VcsConfiguration.StandardConfirmation.REMOVE);
+    final VirtualFile vf = createFileInCommand("a.txt", null);
+    final HgMockVcsHelper helper = registerMockVcsHelper();
+    helper.addListener(new VcsHelperListener() {
+      @Override
+      public void dialogInvoked() {
+        fail("No dialog should be invoked, because newly added file should be silently removed from the repository");
+      }
+    });
+    deleteFileInCommand(vf);
+  }
+
+  /**
+   * A file is also considered to be newly added, if it has a history, but the last action was removal of that file.
+   * 1. Create a file, add it to the repository and commit.
+   * 2. Delete the file and commit it.
+   * 3. Create the file again and add it to the repository.
+   * 4. Delete the file.
+   * 5. File shouldn't be prompted for removal from repository.
+   */
+  @Test
+  public void testJustDeletedAndThenAddedFileShouldNotBePromptedForRemoval() throws Exception {
+    VirtualFile vf = createFileInCommand("a.txt", null);
+    myChangeListManager.commitFiles(vf);
+    deleteFileInCommand(vf);
+    myChangeListManager.commitFiles(vf);
+
+    showConfirmation(VcsConfiguration.StandardConfirmation.REMOVE);
+    vf = createFileInCommand("a.txt", null);
+    final HgMockVcsHelper helper = registerMockVcsHelper();
+    helper.addListener(new VcsHelperListener() {
+      @Override
+      public void dialogInvoked() {
+        fail("No dialog should be invoked, because newly added file should be silently removed from the repository");
+      }
+    });
+    deleteFileInCommand(myProject, vf);
+  }
+
 }
diff --git a/plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/HgMockVcsHelper.java b/plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/HgMockVcsHelper.java
new file mode 100644 (file)
index 0000000..fff4629
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2000-2010 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 org.zmlx.hg4idea.test;
+
+import com.intellij.ide.errorTreeView.HotfixData;
+import com.intellij.openapi.vcs.*;
+import com.intellij.openapi.vcs.annotate.AnnotationProvider;
+import com.intellij.openapi.vcs.annotate.FileAnnotation;
+import com.intellij.openapi.vcs.changes.Change;
+import com.intellij.openapi.vcs.history.VcsFileRevision;
+import com.intellij.openapi.vcs.history.VcsHistoryProvider;
+import com.intellij.openapi.vcs.merge.MergeProvider;
+import com.intellij.openapi.vcs.versionBrowser.ChangeBrowserSettings;
+import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList;
+import com.intellij.openapi.vfs.VirtualFile;
+import gnu.trove.THashSet;
+import org.jetbrains.annotations.Nls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.awt.*;
+import java.io.File;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Substitutes AbstractVcsHelperImpl for tests, where dialogs need to be tested.
+ * Currently it's just a stub implementation notifying listeners about action invoked (which would mean than a dialog would have been
+ * shown during normal execution).
+ * @author Kirill Likhodedov
+ */
+public class HgMockVcsHelper extends AbstractVcsHelper {
+
+  private Collection<VcsHelperListener> myListeners = new THashSet<VcsHelperListener>();
+
+  @Override
+  public void showErrors(List<VcsException> abstractVcsExceptions, @NotNull String tabDisplayName) {
+  }
+
+  @Override
+  public void showErrors(Map<HotfixData, List<VcsException>> exceptionGroups, @NotNull String tabDisplayName) {
+  }
+
+  @Override
+  public List<VcsException> runTransactionRunnable(AbstractVcs vcs, TransactionRunnable runnable, Object vcsParameters) {
+    return null;
+  }
+
+  @Override
+  public void showAnnotation(FileAnnotation annotation, VirtualFile file, AbstractVcs vcs) {
+  }
+
+  @Override
+  public void showDifferences(VcsFileRevision cvsVersionOn, VcsFileRevision cvsVersionOn1, File file) {
+  }
+
+  @Override
+  public void showChangesListBrowser(CommittedChangeList changelist, @Nls String title) {
+  }
+
+  @Override
+  public void showChangesBrowser(List<CommittedChangeList> changelists) {
+  }
+
+  @Override
+  public void showChangesBrowser(List<CommittedChangeList> changelists, @Nls String title) {
+  }
+
+  @Override
+  public void showChangesBrowser(CommittedChangesProvider provider,
+                                 RepositoryLocation location,
+                                 @Nls String title,
+                                 @Nullable Component parent) {
+  }
+
+  @Override
+  public void showWhatDiffersBrowser(@Nullable Component parent, Collection<Change> changes, @Nls String title) {
+  }
+
+  @Override
+  public <T extends CommittedChangeList, U extends ChangeBrowserSettings> T chooseCommittedChangeList(CommittedChangesProvider<T, U> provider,
+                                                                                                      RepositoryLocation location) {
+    return null;
+  }
+
+  @Override
+  public void openCommittedChangesTab(AbstractVcs vcs, VirtualFile root, ChangeBrowserSettings settings, int maxCount, String title) {
+  }
+
+  @Override
+  public void openCommittedChangesTab(CommittedChangesProvider provider,
+                                      RepositoryLocation location,
+                                      ChangeBrowserSettings settings,
+                                      int maxCount,
+                                      String title) {
+  }
+
+  @NotNull
+  @Override
+  public List<VirtualFile> showMergeDialog(List<VirtualFile> files, MergeProvider provider) {
+    return null;
+  }
+
+  @NotNull
+  @Override
+  public List<VirtualFile> showMergeDialog(List<VirtualFile> files) {
+    return null;
+  }
+
+  @Override
+  public void showFileHistory(VcsHistoryProvider vcsHistoryProvider, FilePath path, AbstractVcs vcs, String repositoryPath) {
+  }
+
+  @Override
+  public void showFileHistory(VcsHistoryProvider vcsHistoryProvider,
+                              AnnotationProvider annotationProvider,
+                              FilePath path,
+                              String repositoryPath,
+                              AbstractVcs vcs) {
+  }
+
+  @Override
+  public void showRollbackChangesDialog(List<Change> changes) {
+  }
+
+  @Nullable
+  @Override
+  public Collection<VirtualFile> selectFilesToProcess(List<VirtualFile> files,
+                                                      String title,
+                                                      @Nullable String prompt,
+                                                      String singleFileTitle,
+                                                      String singleFilePromptTemplate,
+                                                      VcsShowConfirmationOption confirmationOption) {
+    notifyListeners();
+    return null;
+  }
+  
+  @Nullable
+  @Override
+  public Collection<FilePath> selectFilePathsToProcess(List<FilePath> files,
+                                                       String title,
+                                                       @Nullable String prompt,
+                                                       String singleFileTitle,
+                                                       String singleFilePromptTemplate,
+                                                       VcsShowConfirmationOption confirmationOption) {
+    notifyListeners();
+    return null;
+  }
+
+  public void addListener(VcsHelperListener listener) {
+    myListeners.add(listener);
+  }
+
+  private void notifyListeners() {
+    for (VcsHelperListener listener : myListeners) {
+      listener.dialogInvoked();
+    }
+  }
+
+}
index f0247b2a6a91a4a312721dab9b692402883b7036..acad6f4f3ff74c204826556e543dbb49eac11052 100644 (file)
@@ -42,8 +42,8 @@ public class HgSingleUserTestCase extends HgAbstractTestCase {
     activateVCS(HgVcs.VCS_NAME);
     myChangeListManager = new HgTestChangeListManager(myProject);
 
-    enableSilentOperation(VcsConfiguration.StandardConfirmation.ADD);
-    enableSilentOperation(VcsConfiguration.StandardConfirmation.REMOVE);
+    doActionSilently(VcsConfiguration.StandardConfirmation.ADD);
+    doActionSilently(VcsConfiguration.StandardConfirmation.REMOVE);
   }
 
 }
\ No newline at end of file
index 96bbc3ff97dbdebff150a02e52f796567ef51da0..56134378c35ce7d5a781d50c2114a42bde1fdb39 100644 (file)
@@ -24,7 +24,6 @@ import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.util.containers.HashSet;
 import org.testng.Assert;
 
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -50,6 +49,7 @@ public class HgTestChangeListManager {
    */
   public void addUnversionedFilesToVcs(VirtualFile... files) {
     peer.addUnversionedFiles(peer.getDefaultChangeList(), Arrays.asList(files));
+    ensureUpToDate();
   }
 
   /**
@@ -59,7 +59,7 @@ public class HgTestChangeListManager {
    * @param files Files to be checked.
    */
   public void checkFilesAreInList(boolean only, VirtualFile... files) {
-    peer.ensureUpToDate(false);
+    ensureUpToDate();
 
     final Collection<Change> changes = peer.getDefaultChangeList().getChanges();
     if (only) {
@@ -78,6 +78,7 @@ public class HgTestChangeListManager {
    * Commits all changes of the given files.
    */
   public void commitFiles(VirtualFile... files) {
+    ensureUpToDate();
     final List<Change> changes = new ArrayList<Change>(files.length);
     for (VirtualFile f : files) {
       changes.addAll(peer.getChangesIn(f));
@@ -86,20 +87,17 @@ public class HgTestChangeListManager {
     assertNotNull(list);
     list.setComment("A comment to a commit");
     Assert.assertTrue(peer.commitChangesSynchronouslyWithResult(list, changes));
+    ensureUpToDate();
   }
 
-  public void removeFiles(final VirtualFile file) {
-     ApplicationManager.getApplication().runWriteAction(new Runnable() {
-       @Override
-       public void run() {
-         try {
-           file.delete(this);
-         }
-         catch (IOException e) {
-           e.printStackTrace();
-         }
-       }
-     });
+  /**
+   * Ensures the ChangelistManager is up to date.
+   * It is called after each operation in the HgTestChangeListManager.
+   */
+  public void ensureUpToDate() {
+    if (!ApplicationManager.getApplication().isDispatchThread()) { // for dispatch thread no need to force update.
+      peer.ensureUpToDate(false);
+    }
   }
 
 }
diff --git a/plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/VcsHelperListener.java b/plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/VcsHelperListener.java
new file mode 100644 (file)
index 0000000..1ed9ee5
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2000-2010 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 org.zmlx.hg4idea.test;
+
+/**
+ * Listens to events in HgMockVcsHelper.
+ * @author Kirill Likhodedov
+ */
+public interface VcsHelperListener {
+  void dialogInvoked();
+}