blob: 39eacd86883a8be2a9c4a33c26b431c51f254b56 [file] [log] [blame]
/*
* 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 git4idea.tests;
import com.intellij.openapi.progress.EmptyProgressIndicator;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vcs.*;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vcs.changes.ChangeListManager;
import com.intellij.openapi.vcs.changes.ContentRevision;
import com.intellij.openapi.vcs.changes.VcsModifiableDirtyScope;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.testFramework.vcs.MockChangeListManagerGate;
import com.intellij.testFramework.vcs.MockChangelistBuilder;
import com.intellij.testFramework.vcs.MockDirtyScope;
import git4idea.GitVcs;
import git4idea.status.GitChangeProvider;
import git4idea.test.GitSingleRepoTest;
import git4idea.test.GitTestUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.intellij.openapi.vcs.Executor.touch;
import static com.intellij.openapi.vcs.VcsTestUtil.*;
import static git4idea.test.GitExecutor.*;
/**
* 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.
* 3. Calls ChangeProvider.getChanges() and checks that the changes are there.
*/
public abstract class GitChangeProviderTest extends GitSingleRepoTest {
protected GitChangeProvider myChangeProvider;
protected VcsModifiableDirtyScope myDirtyScope;
protected VirtualFile myRootDir;
protected VirtualFile mySubDir;
protected GitVcs myVcs;
protected VirtualFile atxt;
protected VirtualFile btxt;
protected VirtualFile dir_ctxt;
protected VirtualFile subdir_dtxt;
@Override
protected void setUp() throws Exception {
super.setUp();
try {
initTest();
}
catch (Exception e) {
super.tearDown();
throw e;
}
}
private void initTest() {
myVcs = GitVcs.getInstance(myProject);
assertNotNull(myVcs);
myChangeProvider = (GitChangeProvider) myVcs.getChangeProvider();
GitTestUtil.createFileStructure(myProjectRoot, "a.txt", "b.txt", "dir/c.txt", "dir/subdir/d.txt");
addCommit("initial");
atxt = getVirtualFile("a.txt");
btxt = getVirtualFile("b.txt");
dir_ctxt = getVirtualFile("dir/c.txt");
subdir_dtxt = getVirtualFile("dir/subdir/d.txt");
myRootDir = myProjectRoot;
mySubDir = myRootDir.findChild("dir");
myDirtyScope = new MockDirtyScope(myProject, myVcs);
cd(myProjectPath);
}
@Override
protected boolean makeInitialCommit() {
return false;
}
@Nullable
private VirtualFile getVirtualFile(@NotNull String relativePath) {
return VfsUtil.findFileByIoFile(new File(myProjectPath, relativePath), true);
}
protected void modifyFileInBranches(String filename, FileAction masterAction, FileAction featureAction) throws Exception {
git("checkout -b feature");
performActionOnFileAndRecordToIndex(filename, "feature", featureAction);
commit("commit to feature");
checkout("master");
refresh();
performActionOnFileAndRecordToIndex(filename, "master", masterAction);
commit("commit to master");
git("merge feature", true);
refresh();
}
protected enum FileAction {
CREATE, MODIFY, DELETE, RENAME
}
private void performActionOnFileAndRecordToIndex(String filename, String branchName, FileAction action) throws Exception {
VirtualFile file = myRootDir.findChild(filename);
if (action != FileAction.CREATE) { // file doesn't exist yet
assertNotNull("VirtualFile is null: " + filename, file);
}
switch (action) {
case CREATE:
File f = touch(filename, "initial content in branch " + branchName);
final VirtualFile createdFile = VfsUtil.findFileByIoFile(f, true);
dirty(createdFile);
add(filename);
break;
case MODIFY:
//noinspection ConstantConditions
overwrite(VfsUtilCore.virtualToIoFile(file), "new content in branch " + branchName);
dirty(file);
add(filename);
break;
case DELETE:
dirty(file);
git("rm " + filename);
break;
case RENAME:
String newName = filename + "_" + branchName.replaceAll("\\s", "_") + "_new";
dirty(file);
mv(filename, newName);
myRootDir.refresh(false, true);
dirty(myRootDir.findChild(newName));
break;
default:
break;
}
}
/**
* Checks that the given files have respective statuses in the change list retrieved from myChangesProvider.
* Pass null in the fileStatuses array to indicate that proper file has not changed.
*/
protected void assertChanges(VirtualFile[] virtualFiles, FileStatus[] fileStatuses) throws VcsException {
Map<FilePath, Change> result = getChanges(virtualFiles);
for (int i = 0; i < virtualFiles.length; i++) {
FilePath fp = new FilePathImpl(virtualFiles[i]);
FileStatus status = fileStatuses[i];
if (status == null) {
assertFalse("File [" + tos(fp) + " shouldn't be in the change list, but it was.", result.containsKey(fp));
continue;
}
assertTrue("File [" + tos(fp) + "] didn't change. Changes: " + tos(result), result.containsKey(fp));
assertEquals("File statuses don't match for file [" + tos(fp) + "]", result.get(fp).getFileStatus(), status);
}
}
protected void assertChanges(VirtualFile virtualFile, FileStatus fileStatus) throws VcsException {
assertChanges(new VirtualFile[] { virtualFile }, new FileStatus[] { fileStatus });
}
/**
* 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 has happened.
*/
protected Map<FilePath, Change> getChanges(VirtualFile... changedFiles) throws VcsException {
final List<FilePath> changedPaths = ObjectsConvertor.vf2fp(Arrays.asList(changedFiles));
// get changes
MockChangelistBuilder builder = new MockChangelistBuilder();
myChangeProvider.getChanges(myDirtyScope, builder, new EmptyProgressIndicator(), new MockChangeListManagerGate(ChangeListManager.getInstance(myProject)));
List<Change> changes = builder.getChanges();
// get changes for files
final Map<FilePath, Change> result = new HashMap<FilePath, Change>();
for (Change change : changes) {
VirtualFile file = change.getVirtualFile();
FilePath filePath = null;
if (file == null) { // if a file was deleted, just find the reference in the original list of files and use it.
String path = change.getBeforeRevision().getFile().getPath();
for (FilePath fp : changedPaths) {
if (FileUtil.pathsEqual(fp.getPath(), path)) {
filePath = fp;
break;
}
}
} else {
filePath = new FilePathImpl(file);
}
result.put(filePath, change);
}
return result;
}
protected VirtualFile create(VirtualFile parent, String name) {
return create(parent, name, false);
}
protected VirtualFile createDir(VirtualFile parent, String name) {
return create(parent, name, true);
}
private VirtualFile create(VirtualFile parent, String name, boolean dir) {
final VirtualFile file = dir ?
VcsTestUtil.createDir(myProject, parent, name) :
createFile(myProject, parent, name, "content" + Math.random());
dirty(file);
return file;
}
protected void edit(VirtualFile file, String content) {
editFileInCommand(myProject, file, content);
dirty(file);
}
protected void move(VirtualFile file, VirtualFile newParent) {
dirty(file);
moveFileInCommand(myProject, file, newParent);
dirty(file);
}
protected VirtualFile copy(VirtualFile file, VirtualFile newParent) {
dirty(file);
VirtualFile newFile = copyFileInCommand(myProject, file, newParent, file.getName());
dirty(newFile);
return newFile;
}
protected void delete(VirtualFile file) {
dirty(file);
deleteFileInCommand(myProject, file);
}
private void dirty(VirtualFile file) {
myDirtyScope.addDirtyFile(new FilePathImpl(file));
}
protected String tos(FilePath fp) {
return FileUtil.getRelativePath(new File(myProjectPath), fp.getIOFile());
}
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<FilePath, Change> changes) {
StringBuilder stringBuilder = new StringBuilder("[");
for (Change change : changes.values()) {
stringBuilder.append(tos(change)).append(", ");
}
stringBuilder.append("]");
return stringBuilder.toString();
}
}