From db4c73f73725bbbfe6349e0e4e555530efa2ef1f Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Wed, 18 May 2011 18:50:10 +0400 Subject: [PATCH] [Git] Rework GitChangeProviderTest +use MockDirtyScope, * add to dirty scope correctly, +testMoveNewFile +tos()-methods for more descriptive output. *add random string when creating files to avoid Git rename detection. --- .../testFramework/vcs/MockDirtyScope.java | 122 ++++++++++++++++++ .../git4idea/tests/GitChangeProviderTest.java | 115 ++++++++++++----- .../tests/git4idea/tests/GitTest.java | 32 ++++- .../tests/git4idea/tests/GitTestUtil.java | 2 +- 4 files changed, 236 insertions(+), 35 deletions(-) create mode 100644 platform/testFramework/src/com/intellij/testFramework/vcs/MockDirtyScope.java diff --git a/platform/testFramework/src/com/intellij/testFramework/vcs/MockDirtyScope.java b/platform/testFramework/src/com/intellij/testFramework/vcs/MockDirtyScope.java new file mode 100644 index 000000000000..cb37d8d44fb8 --- /dev/null +++ b/platform/testFramework/src/com/intellij/testFramework/vcs/MockDirtyScope.java @@ -0,0 +1,122 @@ +/* + * Copyright 2000-2011 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.testFramework.vcs; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vcs.AbstractVcs; +import com.intellij.openapi.vcs.FilePath; +import com.intellij.openapi.vcs.ProjectLevelVcsManager; +import com.intellij.openapi.vcs.changes.VcsModifiableDirtyScope; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.Processor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +/** + * Mock implementation of {@link com.intellij.openapi.vcs.changes.VcsDirtyScope}. + * Stores files and dirs separately without any check. + * Not all operations may be supported. + * + * @author Kirill Likhodedov + */ +public class MockDirtyScope extends VcsModifiableDirtyScope { + + private final Project myProject; + private final AbstractVcs myVcs; + private final ProjectLevelVcsManager myVcsManager; + + private final Set myDirtyFiles = new HashSet(); + private final Set myDirtyDirs = new HashSet(); + private final Set myContentRoots = new HashSet(); + + public MockDirtyScope(@NotNull Project project, @NotNull AbstractVcs vcs) { + myProject = project; + myVcs = vcs; + myVcsManager = ProjectLevelVcsManager.getInstance(myProject); + } + + @Override + public void addDirtyFile(@Nullable FilePath newcomer) { + myDirtyFiles.add(newcomer); + myContentRoots.add(myVcsManager.getVcsRootFor(newcomer.getVirtualFile())); + } + + @Override + public void addDirtyDirRecursively(@Nullable FilePath newcomer) { + myDirtyDirs.add(newcomer); + myContentRoots.add(myVcsManager.getVcsRootFor(newcomer.getVirtualFile())); + } + + @Override + @NotNull + public Collection getAffectedContentRoots() { + return myContentRoots; + } + + @Override + @NotNull + public Project getProject() { + return myProject; + } + + @Override + @NotNull + public AbstractVcs getVcs() { + return myVcs; + } + + @Override + @NotNull + public Set getDirtyFiles() { + return myDirtyFiles; + } + + @Override + @NotNull + public Set getDirtyFilesNoExpand() { + return myDirtyFiles; + } + + @Override + @NotNull + public Set getRecursivelyDirtyDirectories() { + return myDirtyDirs; + } + + @Override + public boolean isRecursivelyDirty(VirtualFile vf) { + throw new UnsupportedOperationException(); + } + + @Override + public void iterate(Processor iterator) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isEmpty() { + return myDirtyFiles.isEmpty(); + } + + @Override + public boolean belongsTo(FilePath path) { + throw new UnsupportedOperationException(); + } +} diff --git a/plugins/git4idea/tests/git4idea/tests/GitChangeProviderTest.java b/plugins/git4idea/tests/git4idea/tests/GitChangeProviderTest.java index e89f1b3ba746..6320c4fe552d 100644 --- a/plugins/git4idea/tests/git4idea/tests/GitChangeProviderTest.java +++ b/plugins/git4idea/tests/git4idea/tests/GitChangeProviderTest.java @@ -19,17 +19,20 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.progress.EmptyProgressIndicator; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vcs.*; -import com.intellij.openapi.vcs.changes.*; +import com.intellij.openapi.vcs.changes.Change; +import com.intellij.openapi.vcs.changes.ChangeListManager; +import com.intellij.openapi.vcs.changes.VcsModifiableDirtyScope; import com.intellij.openapi.vcs.changes.pending.MockChangeListManagerGate; +import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.testFramework.vcs.MockChangelistBuilder; +import com.intellij.testFramework.vcs.MockDirtyScope; import com.intellij.ui.GuiUtils; import git4idea.GitVcs; import git4idea.changes.GitChangeProvider; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; -import java.io.File; import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -41,7 +44,7 @@ import static org.testng.Assert.*; /** * Tests GitChangeProvider functionality. Scenario is the same for all tests: * 1. Modifies files on disk (creates, edits, deletes, etc.) - * 2. Manually adds them to a dirty scope (better to use VcsDirtyScopeManagerImpl, but it's too asynchronous - couldn't overcome this for now. + * 2. Manually adds them to a dirty scope. * 3. Calls ChangeProvider.getChanges() and checks that the changes are there. * @author Kirill Likhodedov */ @@ -51,41 +54,46 @@ public class GitChangeProviderTest extends GitSingleUserTest { private VcsModifiableDirtyScope myDirtyScope; private Map myFiles; private VirtualFile afile; + private VirtualFile myRootDir; @BeforeMethod @Override protected void setUp() throws Exception { super.setUp(); myChangeProvider = (GitChangeProvider) GitVcs.getInstance(myProject).getChangeProvider(); - myDirtyScope = new VcsDirtyScopeImpl(GitVcs.getInstance(myProject), myProject); myFiles = GitTestUtil.createFileStructure(myProject, myRepo, "a.txt", "b.txt", "dir/c.txt", "dir/subdir/d.txt"); + myRepo.addCommit(); + myRepo.refresh(); + afile = myFiles.get("a.txt"); // the file is commonly used, so save it in a field. - myRepo.commit(); + myRootDir = myRepo.getDir(); + + myDirtyScope = new MockDirtyScope(myProject, GitVcs.getInstance(myProject)); } @Test public void testCreateFile() throws Exception { - VirtualFile bfile = myRepo.createFile("new.txt"); - assertChanges(bfile, ADDED); + VirtualFile file = create(myRootDir, "new.txt"); + assertChanges(file, ADDED); } @Test public void testCreateFileInDir() throws Exception { - VirtualFile dir = createDirInCommand(myRepo.getDir(), "newdir"); - VirtualFile bfile = createFileInCommand(dir, "new.txt", "initial b"); + VirtualFile dir = createDir(myRootDir, "newdir"); + VirtualFile bfile = create(dir, "new.txt"); assertChanges(new VirtualFile[] {bfile, dir}, new FileStatus[] { ADDED, null} ); } @Test public void testEditFile() throws Exception { - editFileInCommand(afile, "new content"); + edit(afile, "new content"); assertChanges(afile, MODIFIED); } @Test public void testDeleteFile() throws Exception { - deleteFileInCommand(afile); + delete(afile); assertChanges(afile, DELETED); } @@ -97,7 +105,9 @@ public class GitChangeProviderTest extends GitSingleUserTest { ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { - FileUtil.delete(new File(myRepo.getDir().getPath(), "dir")); + final VirtualFile dir= myRepo.getDir().findChild("dir"); + myDirtyScope.addDirtyDirRecursively(new FilePathImpl(dir)); + FileUtil.delete(VfsUtil.virtualToIoFile(dir)); } }); } @@ -105,15 +115,29 @@ public class GitChangeProviderTest extends GitSingleUserTest { assertChanges(new VirtualFile[] { myFiles.get("dir/c.txt"), myFiles.get("dir/subdir/d.txt") }, new FileStatus[] { DELETED, DELETED }); } + @Test + public void testMoveNewFile() throws Exception { + // IDEA-59587 + // Reproducibility of the bug (in the original roots cause) depends on the order of new and old paths in the dirty scope. + // MockDirtyScope shouldn't preserve the order of items added there - a Set is returned from getDirtyFiles(). + // But the order is likely preserved if it meets the natural order of the items inserted into the dirty scope. + // That's why the test moves from .../repo/dir/new.txt to .../repo/new.txt - to make the old path appear later than the new one. + // This is not consistent though. + final VirtualFile dir= myRepo.getDir().findChild("dir"); + final VirtualFile file = create(dir, "new.txt"); + move(file, myRootDir); + assertChanges(file, ADDED); + } + @Test public void testSimultaneousOperationsOnMultipleFiles() throws Exception { VirtualFile dfile = myFiles.get("dir/subdir/d.txt"); VirtualFile cfile = myFiles.get("dir/c.txt"); - editFileInCommand(afile, "new content"); - editFileInCommand(cfile, "new content"); - deleteFileInCommand(dfile); - VirtualFile newfile = createFileInCommand("newfile.txt", "new content"); + edit(afile, "new afile content"); + edit(cfile, "new cfile content"); + delete(dfile); + VirtualFile newfile = create(myRootDir, "newfile.txt"); assertChanges(new VirtualFile[] {afile, cfile, dfile, newfile}, new FileStatus[] {MODIFIED, MODIFIED, DELETED, ADDED}); } @@ -194,19 +218,24 @@ public class GitChangeProviderTest extends GitSingleUserTest { VirtualFile file = myRepo.getDir().findChild(filename); switch (action) { case CREATE: - createFileInCommand(filename, "initial content in branch " + branchName); + final VirtualFile createdFile = createFileInCommand(filename, "initial content in branch " + branchName); + dirty(createdFile); myRepo.add(filename); break; case MODIFY: editFileInCommand(file, "new content in branch " + branchName); + dirty(file); myRepo.add(filename); break; case DELETE: + dirty(file); myRepo.rm(filename); break; case RENAME: - String name = filename + "_" + branchName.replaceAll("\\s", "_") + "_new"; - myRepo.mv(filename, name); + String newName = filename + "_" + branchName.replaceAll("\\s", "_") + "_new"; + dirty(file); + myRepo.mv(filename, newName); + dirty(myRootDir.findChild(newName)); break; default: break; @@ -223,11 +252,11 @@ public class GitChangeProviderTest extends GitSingleUserTest { FilePath fp = new FilePathImpl(virtualFiles[i]); FileStatus status = fileStatuses[i]; if (status == null) { - assertFalse(result.containsKey(fp), "File [" + fp + " shouldn't be in the change list, but it was."); + assertFalse(result.containsKey(fp), "File [" + tos(fp) + " shouldn't be in the change list, but it was."); continue; } - assertTrue(result.containsKey(fp), "File [" + fp + "] didn't change. Changes: " + result); - assertEquals(result.get(fp).getFileStatus(), status, "File statuses don't match for file [" + fp + "]"); + assertTrue(result.containsKey(fp), "File [" + tos(fp) + "] didn't change. Changes: " + tos(result)); + assertEquals(result.get(fp).getFileStatus(), status, "File statuses don't match for file [" + tos(fp) + "]"); } } @@ -237,18 +266,11 @@ public class GitChangeProviderTest extends GitSingleUserTest { /** * Marks the given files dirty in myDirtyScope, gets changes from myChangeProvider and groups the changes in the map. - * Assumes that only one change for a file happened. + * Assumes that only one change for a file has happened. */ private Map getChanges(VirtualFile... changedFiles) throws VcsException { final List changedPaths = ObjectsConvertor.vf2fp(Arrays.asList(changedFiles)); - // populate dirty scope - //for (FilePath path : changedPaths) { - // myDirtyScope.addDirtyFile(path); - //} - VcsDirtyScopeManagerImpl.getInstance(myProject).markEverythingDirty(); - myDirtyScope.addDirtyDirRecursively(new FilePathImpl(myRepo.getDir())); - // get changes MockChangelistBuilder builder = new MockChangelistBuilder(); myChangeProvider.getChanges(myDirtyScope, builder, new EmptyProgressIndicator(), new MockChangeListManagerGate(ChangeListManager.getInstance(myProject))); @@ -275,4 +297,37 @@ public class GitChangeProviderTest extends GitSingleUserTest { return result; } + private VirtualFile create(VirtualFile parent, String name) { + return create(parent, name, false); + } + + private VirtualFile createDir(VirtualFile parent, String name) { + return create(parent, name, true); + } + + private VirtualFile create(VirtualFile parent, String name, boolean dir) { + final VirtualFile file = dir ? createDirInCommand(parent, name) : createFileInCommand(parent, name, "content" + Math.random()); + dirty(file); + return file; + } + + private void edit(VirtualFile file, String content) { + editFileInCommand(file, content); + dirty(file); + } + + private void move(VirtualFile file, VirtualFile newParent) { + dirty(file); + moveFileInCommand(file, newParent); + dirty(file); + } + + private void delete(VirtualFile file) { + dirty(file); + deleteFileInCommand(file); + } + + private void dirty(VirtualFile file) { + myDirtyScope.addDirtyFile(new FilePathImpl(file)); + } } diff --git a/plugins/git4idea/tests/git4idea/tests/GitTest.java b/plugins/git4idea/tests/git4idea/tests/GitTest.java index 8c1e6f081b93..8565cecf603e 100644 --- a/plugins/git4idea/tests/git4idea/tests/GitTest.java +++ b/plugins/git4idea/tests/git4idea/tests/GitTest.java @@ -18,8 +18,12 @@ package git4idea.tests; import com.intellij.execution.process.ProcessOutput; import com.intellij.openapi.application.PluginPathManager; import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.VcsConfiguration; import com.intellij.openapi.vcs.VcsShowConfirmationOption; +import com.intellij.openapi.vcs.changes.Change; +import com.intellij.openapi.vcs.changes.ContentRevision; import com.intellij.testFramework.AbstractVcsTestCase; import com.intellij.ui.GuiUtils; import com.intellij.util.ui.UIUtil; @@ -31,6 +35,7 @@ import org.testng.annotations.BeforeMethod; import java.io.File; import java.io.IOException; +import java.util.Map; /** * The common ancestor for git test cases which need git executable. @@ -119,12 +124,31 @@ public abstract class GitTest extends AbstractVcsTestCase { setStandardConfirmation(GitVcs.NAME, op, VcsShowConfirmationOption.Value.DO_ACTION_SILENTLY); } - protected void doNothingSilently(final VcsConfiguration.StandardConfirmation op) { - setStandardConfirmation(GitVcs.NAME, op, VcsShowConfirmationOption.Value.DO_NOTHING_SILENTLY); + protected String tos(FilePath fp) { + return FileUtil.getRelativePath(new File(myMainRepo.getDir().getPath()), fp.getIOFile()); } - protected void showConfirmation(final VcsConfiguration.StandardConfirmation op) { - setStandardConfirmation(GitVcs.NAME, op, VcsShowConfirmationOption.Value.SHOW_CONFIRMATION); + protected String tos(Change change) { + switch (change.getType()) { + case NEW: return "A: " + tos(change.getAfterRevision()); + case DELETED: return "D: " + tos(change.getBeforeRevision()); + case MOVED: return "M: " + tos(change.getBeforeRevision()) + " -> " + tos(change.getAfterRevision()); + case MODIFICATION: return "M: " + tos(change.getAfterRevision()); + default: return "~: " + tos(change.getBeforeRevision()) + " -> " + tos(change.getAfterRevision()); + } + } + + protected String tos(ContentRevision revision) { + return tos(revision.getFile()); + } + + protected String tos(Map changes) { + StringBuilder stringBuilder = new StringBuilder("["); + for (Change change : changes.values()) { + stringBuilder.append(tos(change)).append(", "); + } + stringBuilder.append("]"); + return stringBuilder.toString(); } } diff --git a/plugins/git4idea/tests/git4idea/tests/GitTestUtil.java b/plugins/git4idea/tests/git4idea/tests/GitTestUtil.java index b67dbb4ab807..07e94904b5e4 100644 --- a/plugins/git4idea/tests/git4idea/tests/GitTestUtil.java +++ b/plugins/git4idea/tests/git4idea/tests/GitTestUtil.java @@ -54,7 +54,7 @@ public class GitTestUtil { } String lastElement = pathElements[pathElements.length-1]; - currentParent = lastIsDir ? createDir(project, currentParent, lastElement) : createFile(project, currentParent, lastElement, "initial content"); + currentParent = lastIsDir ? createDir(project, currentParent, lastElement) : createFile(project, currentParent, lastElement, "content" + Math.random()); result.put(path, currentParent); } return result; -- 2.32.0