/*******************************************************************************
* Copyright (C) 2010-2013 Dariusz Luksza <dariusz@luksza.org> and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*******************************************************************************/
package org.eclipse.egit.ui.view.synchronize;
import static org.eclipse.egit.ui.internal.UIText.GitModelWorkingTree_workingTree;
import static org.eclipse.jgit.lib.Constants.HEAD;
import static org.eclipse.jgit.lib.Constants.MASTER;
import static org.eclipse.jgit.lib.Constants.R_HEADS;
import static org.eclipse.jgit.lib.Constants.R_TAGS;
import static org.eclipse.swtbot.swt.finder.matchers.WidgetMatcherFactory.allOf;
import static org.eclipse.swtbot.swt.finder.matchers.WidgetMatcherFactory.widgetOfType;
import static org.eclipse.swtbot.swt.finder.matchers.WidgetMatcherFactory.withRegex;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.egit.ui.common.CompareEditorTester;
import org.eclipse.egit.ui.internal.CommonUtils;
import org.eclipse.egit.ui.internal.UIText;
import org.eclipse.egit.ui.internal.synchronize.GitChangeSetModelProvider;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.Status;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swtbot.swt.finder.SWTBot;
import org.eclipse.swtbot.swt.finder.widgets.SWTBotLabel;
import org.eclipse.swtbot.swt.finder.widgets.SWTBotTree;
import org.eclipse.swtbot.swt.finder.widgets.SWTBotTreeItem;
import org.hamcrest.Matcher;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
public class SynchronizeViewGitChangeSetModelTest extends
AbstractSynchronizeViewTest {
@Before
public void setUpEnabledModelProvider() {
setEnabledModelProvider(GitChangeSetModelProvider.ID);
}
@Test
public void shouldReturnNoChanges() throws Exception {
// given
changeFilesInProject();
// when
launchSynchronization(HEAD, R_HEADS + MASTER, false);
// then
SWTBot viewBot = bot.viewByTitle("Synchronize").bot();
@SuppressWarnings("unchecked")
Matcher matcher = allOf(widgetOfType(Label.class),
withRegex("No changes in .*"));
@SuppressWarnings("unchecked")
SWTBotLabel l = new SWTBotLabel((Label) viewBot.widget(matcher));
assertNotNull(l);
}
@Test
public void shouldReturnListOfChanges() throws Exception {
// given
changeFilesInProject();
// when
launchSynchronization(HEAD, HEAD, true);
// then
SWTBotTreeItem workingTreeItem = getExpandedWorkingTreeItem();
assertTrue(workingTreeItem.getText().endsWith(GitModelWorkingTree_workingTree));
}
@Test
public void shouldCompareBranchAgainstTag() throws Exception {
// given
makeChangesAndCommit(PROJ1);
// when
launchSynchronization(INITIAL_TAG, HEAD, false);
// then
SWTBotTreeItem changeSetTreeItem = getChangeSetTreeItem();
assertEquals(1, changeSetTreeItem.getItems().length);
}
@Test
public void shouldCompareTagAgainstTag() throws Exception {
// given
makeChangesAndCommit(PROJ1);
createTag("v0.1");
// when
launchSynchronization(INITIAL_TAG, R_TAGS + "v0.1", false);
// then
SWTBotTreeItem changeSetTreeItem = getChangeSetTreeItem();
assertEquals(1, changeSetTreeItem.getItems().length);
}
@Test
public void shouldOpenCompareEditor() throws Exception {
// given
changeFilesInProject();
// when
launchSynchronization(HEAD, INITIAL_TAG, true);
// then
CompareEditorTester compare = getCompareEditorForFileInGitChangeSet(
FILE1, true);
assertNotNull(compare);
}
@Test
public void shouldListFileDeletedChange() throws Exception {
// given
deleteFileAndCommit(PROJ1);
// when
launchSynchronization(HEAD, HEAD + "~1", true);
// then
SWTBotTreeItem changeSetTreeItem = getChangeSetTreeItem();
assertEquals(1, changeSetTreeItem.getItems().length);
SWTBotTreeItem commitTree = waitForNodeWithText(changeSetTreeItem,
TEST_COMMIT_MSG);
SWTBotTreeItem projectTree = waitForNodeWithText(commitTree, PROJ1);
assertEquals(1, projectTree.getItems().length);
SWTBotTreeItem folderTree = waitForNodeWithText(projectTree, FOLDER);
assertEquals(1, folderTree.getItems().length);
SWTBotTreeItem fileTree = folderTree.getItems()[0];
assertEquals("test.txt", fileTree.getText());
}
@Test
public void shouldSynchronizeInEmptyRepository() throws Exception {
// given
createEmptyRepository();
// when
launchSynchronization(EMPTY_PROJECT, "", "", true);
// then
SWTBotTreeItem workingTree = getExpandedWorkingTreeItem();
SWTBotTreeItem projectTree = waitForNodeWithText(workingTree,
EMPTY_PROJECT);
assertEquals(2, projectTree.getItems().length);
SWTBotTreeItem folderTree = waitForNodeWithText(projectTree, FOLDER);
assertEquals(2, folderTree.getItems().length);
SWTBotTreeItem fileTree = folderTree.getItems()[0];
assertEquals(FILE1, fileTree.getText());
fileTree = folderTree.getItems()[1];
assertEquals(FILE2, fileTree.getText());
}
@Test
public void shouldExchangeCompareEditorSidesBetweenIncomingAndOutgoingChanges()
throws Exception {
// given
makeChangesAndCommit(PROJ1);
// compare HEAD against tag
launchSynchronization(HEAD, INITIAL_TAG, false);
CompareEditorTester outgoingCompare = getCompareEditorForFileInGitChangeSet(
FILE1, false);
// save left value from compare editor
String outgoingLeft = outgoingCompare.getLeftEditor().getText();
// save right value from compare editor
String outgoingRight = outgoingCompare.getRightEditor().getText();
outgoingCompare.close();
assertNotSame("Text from SWTBot widgets was the same", outgoingLeft, outgoingRight);
// when
// compare tag against HEAD
launchSynchronization(INITIAL_TAG, HEAD, false);
// then
CompareEditorTester incomingComp = getCompareEditorForFileInGitChangeSet(
FILE1, false);
// right side from compare editor should be equal with left
assertThat(outgoingLeft, equalTo(incomingComp.getRightEditor().getText()));
// left side from compare editor should be equal with right
assertThat(outgoingRight, equalTo(incomingComp.getLeftEditor().getText()));
}
@Test
public void shouldNotShowIgnoredFiles()
throws Exception {
// given
String ignoredName = "to-be-ignored.txt";
IProject proj = ResourcesPlugin.getWorkspace().getRoot()
.getProject(PROJ1);
IFile ignoredFile = proj.getFile(ignoredName);
ignoredFile.create(new ByteArrayInputStream("content of ignored file"
.getBytes(proj.getDefaultCharset())), false, null);
IFile gitignore = proj.getFile(".gitignore");
gitignore.create(
new ByteArrayInputStream(ignoredName.getBytes(proj
.getDefaultCharset())), false, null);
proj.refreshLocal(IResource.DEPTH_INFINITE, null);
// when
launchSynchronization(INITIAL_TAG, HEAD, true);
// then
// asserts for Git Change Set model
SWTBotTreeItem workingTree = getExpandedWorkingTreeItem();
assertEquals(1, workingTree.getItems().length);
SWTBotTreeItem proj1Node = workingTree.getItems()[0];
assertEquals(1, proj1Node.getItems().length);
assertEquals(".gitignore", proj1Node.getItems()[0].getText());
}
@Test
public void shouldShowNonWorkspaceFileInSynchronization()
throws Exception {
// given
String name = "non-workspace.txt";
File root = new File(getTestDirectory(), REPO1);
File nonWorkspace = new File(root, name);
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(nonWorkspace), "UTF-8"));
writer.append("file content");
writer.close();
// TODO Synchronize currently shows "No changes" when the only thing that has
// changed is a non-workspace file, so add another change so that this
// does not happen. When the bug is fixed, this should be removed.
setTestFileContent("other content");
// when
launchSynchronization(INITIAL_TAG, HEAD, true);
// then
SWTBotTreeItem workingTree = getExpandedWorkingTreeItem();
assertEquals(1, workingTree.getNodes(name).size());
}
@Test
public void shouldShowCompareEditorForNonWorkspaceFileFromSynchronization()
throws Exception {
// given
String content = "file content";
String name = "non-workspace.txt";
File root = new File(getTestDirectory(), REPO1);
File nonWorkspace = new File(root, name);
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(nonWorkspace), "UTF-8"));
writer.append(content);
writer.close();
// TODO Synchronize currently shows "No changes" when the only thing that has
// changed is a non-workspace file, so add another change so that this
// does not happen. When the bug is fixed, this should be removed.
setTestFileContent("other content");
// when
launchSynchronization(INITIAL_TAG, HEAD, true);
// then
CompareEditorTester editor = getCompareEditorForNonWorkspaceFileInGitChangeSet(name);
String left = editor.getLeftEditor().getText();
String right = editor.getRightEditor().getText();
assertEquals(content, left);
assertEquals("", right);
}
@Test
public void shouldStagePartialChangeInCompareEditor() throws Exception {
// given
changeFilesInProject();
launchSynchronization(HEAD, HEAD, true);
CompareEditorTester compareEditor = getCompareEditorForFileInGitChangeSet(
FILE1, true);
// when
Display.getDefault().syncExec(new Runnable() {
public void run() {
CommonUtils.runCommand("org.eclipse.compare.copyLeftToRight",
null);
}
});
compareEditor.save();
// then file FILE1 should be in index
Repository repo = lookupRepository(repositoryFile);
Status status = new Git(repo).status().call();
assertThat(Long.valueOf(status.getChanged().size()),
is(Long.valueOf(1L)));
assertThat(status.getChanged().iterator().next(), is(PROJ1 + "/"
+ FOLDER + "/" + FILE1));
}
@Test
public void shouldUnStagePartialChangeInCompareEditor() throws Exception {
// given
changeFilesInProject();
launchSynchronization(HEAD, HEAD, true);
CompareEditorTester compareEditor = getCompareEditorForFileInGitChangeSet(
FILE1, true);
// when
Display.getDefault().syncExec(new Runnable() {
public void run() {
CommonUtils.runCommand("org.eclipse.compare.copyRightToLeft",
null);
}
});
compareEditor.save();
// then file FILE1 should be unchanged in working tree
Repository repo = lookupRepository(repositoryFile);
Status status = new Git(repo).status().call();
assertThat(Long.valueOf(status.getModified().size()),
is(Long.valueOf(1)));
assertThat(status.getModified().iterator().next(), is(PROJ1 + "/"
+ FOLDER + "/" + FILE2));
}
@Test
public void shouldRefreshSyncResultAfterWorkspaceChange() throws Exception {
// given
String newFileName = "new.txt";
launchSynchronization(INITIAL_TAG, HEAD, true);
IProject proj = ResourcesPlugin.getWorkspace().getRoot()
.getProject(PROJ1);
// when
IFile newFile = proj.getFile(newFileName);
newFile.create(
new ByteArrayInputStream("content of new file".getBytes(proj
.getDefaultCharset())), false, null);
// force refresh
proj.refreshLocal(IResource.DEPTH_INFINITE, null);
Job.getJobManager().join(ResourcesPlugin.FAMILY_AUTO_BUILD, null);
// then
SWTBotTreeItem workingTree = getExpandedWorkingTreeItem();
assertEquals(GitModelWorkingTree_workingTree, workingTree.getText());
// WidgetNotFoundException will be thrown when node named 'new.txt' not
// exists
assertNotNull(workingTree.getNode(PROJ1).expand());
assertNotNull(workingTree.getNode(PROJ1).getNode(newFileName));
}
@Test
public void shouldRefreshSyncResultAfterRepositoryChange() throws Exception {
// given
changeFilesInProject();
launchSynchronization(HEAD, HEAD, true);
// preconditions - sync result should contain two uncommitted changes
SWTBotTreeItem workingTree = getExpandedWorkingTreeItem();
assertTrue(workingTree.getText().endsWith(GitModelWorkingTree_workingTree));
assertEquals(2,
workingTree.getItems()[0].getItems()[0].getItems().length);
// when
commit(PROJ1);
// then - synchronize view should be empty
SWTBot viewBot = bot.viewByTitle("Synchronize").bot();
@SuppressWarnings("unchecked")
Matcher matcher = allOf(widgetOfType(Label.class),
withRegex("No changes in .*"));
@SuppressWarnings("unchecked")
SWTBotLabel l = new SWTBotLabel((Label) viewBot.widget(matcher));
assertNotNull(l);
}
// this test always fails with cause:
// Timeout after: 5000 ms.: Could not find context menu with text:
// Synchronize
@Ignore
@Test
public void shouldLaunchSynchronizationFromGitRepositories()
throws Exception {
// given
bot.menu("Window").menu("Show View").menu("Other...").click();
bot.shell("Show View").bot().tree().expandNode("Git").getNode(
"Git Repositories").doubleClick();
SWTBotTree repositoriesTree = bot.viewByTitle("Git Repositories").bot()
.tree();
SWTBotTreeItem egitRoot = repositoriesTree.getAllItems()[0];
egitRoot.expand();
egitRoot.collapse();
egitRoot.expand();
SWTBotTreeItem remoteBranch = egitRoot.expandNode("Branches")
.expandNode("Remote Branches");
SWTBotTreeItem branchNode = remoteBranch.getNode("origin/stable-0.7");
branchNode.select();
branchNode.contextMenu("Synchronize").click();
// when
// then
SWTBotTree syncViewTree = bot.viewByTitle("Synchronize").bot().tree();
assertEquals(8, syncViewTree.getAllItems().length);
}
protected SWTBotTreeItem getChangeSetTreeItem() {
SWTBotTree syncViewTree = bot.viewByTitle("Synchronize").bot().tree();
SWTBotTreeItem changeSetItem = waitForNodeWithText(syncViewTree,
UIText.GitChangeSetModelProviderLabel);
return changeSetItem;
}
protected CompareEditorTester getCompareEditorForFileInGitChangeSet(
String fileName,
boolean includeLocalChanges) {
SWTBotTreeItem changeSetTreeItem = getChangeSetTreeItem();
SWTBotTreeItem rootTree;
if (includeLocalChanges)
rootTree = waitForNodeWithText(changeSetTreeItem,
GitModelWorkingTree_workingTree);
else
rootTree = waitForNodeWithText(changeSetTreeItem, TEST_COMMIT_MSG);
SWTBotTreeItem projNode = waitForNodeWithText(rootTree, PROJ1);
return getCompareEditor(projNode, fileName);
}
protected CompareEditorTester getCompareEditorForNonWorkspaceFileInGitChangeSet(
final String fileName) {
SWTBotTreeItem changeSetTreeItem = getChangeSetTreeItem();
SWTBotTreeItem rootTree = waitForNodeWithText(changeSetTreeItem,
GitModelWorkingTree_workingTree);
waitForNodeWithText(rootTree, fileName).doubleClick();
return CompareEditorTester.forTitleContaining(fileName);
}
private SWTBotTreeItem getExpandedWorkingTreeItem() {
SWTBotTreeItem changeSetTreeItem = getChangeSetTreeItem();
String workingTreeNodeNameString = getWorkingTreeNodeName(changeSetTreeItem);
SWTBotTreeItem node = changeSetTreeItem.getNode(workingTreeNodeNameString);
// Full expansion
return node.doubleClick();
}
private String getWorkingTreeNodeName(SWTBotTreeItem changeSetTreeItem) {
for (SWTBotTreeItem item : changeSetTreeItem.getItems()) {
String nodeName = item.getText();
if (nodeName.contains(UIText.GitModelWorkingTree_workingTree))
return nodeName;
}
return UIText.GitModelWorkingTree_workingTree;
}
}