/******************************************************************************* * Copyright (C) 2010, 2012 Mathias Kinzler <mathias.kinzler@sap.com> 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.core.test.op; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.ILog; import org.eclipse.core.runtime.ILogListener; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.egit.core.Activator; import org.eclipse.egit.core.op.AddToIndexOperation; import org.eclipse.egit.core.op.BranchOperation; import org.eclipse.egit.core.op.CloneOperation; import org.eclipse.egit.core.op.CommitOperation; import org.eclipse.egit.core.op.PushOperation; import org.eclipse.egit.core.op.PushOperationResult; import org.eclipse.egit.core.op.PushOperationSpecification; import org.eclipse.egit.core.test.DualRepositoryTestCase; import org.eclipse.egit.core.test.TestRepository; import org.eclipse.egit.core.test.TestUtils; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.RefUpdate; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.transport.PushResult; import org.eclipse.jgit.transport.RemoteRefUpdate; import org.eclipse.jgit.transport.RemoteRefUpdate.Status; import org.eclipse.jgit.transport.URIish; import org.junit.After; import org.junit.Before; import org.junit.Test; public class PushOperationTest extends DualRepositoryTestCase { private static final String INVALID_URI = "invalid-uri"; File workdir; File workdir2; String projectName = "PushTest"; /** * Set up repository1 with branch "master", create some project and commit * it; then clone into repository2; finally create a branch "test" on top of * "master" in repository2 * * @throws Exception */ @Before public void setUp() throws Exception { workdir = testUtils.createTempDir("Repository1"); workdir2 = testUtils.createTempDir("Repository2"); repository1 = new TestRepository(new File(workdir, Constants.DOT_GIT)); // now we create a project in repo1 IProject project = testUtils.createProjectInLocalFileSystem(workdir, projectName); testUtils.addFileToProject(project, "folder1/file1.txt", "Hello world"); repository1.connect(project); repository1.trackAllFiles(project); repository1.commit("Initial commit"); // let's get rid of the project project.delete(false, false, null); // let's clone repository1 to repository2 URIish uri = repository1.getUri(); CloneOperation clop = new CloneOperation(uri, true, null, workdir2, "refs/heads/master", "origin", 0); clop.run(null); Repository repo2 = Activator.getDefault().getRepositoryCache().lookupRepository(new File(workdir2, Constants.DOT_GIT)); repository2 = new TestRepository(repo2); // we push to branch "test" of repository2 RefUpdate createBranch = repository2.getRepository().updateRef( "refs/heads/test"); createBranch.setNewObjectId(repository2.getRepository().resolve( "refs/heads/master")); createBranch.update(); } @After public void tearDown() throws Exception { repository1.dispose(); repository2.dispose(); repository1 = null; repository2 = null; testUtils.deleteTempDirs(); } /** * Push from repository1 "master" into "test" of repository2. * * @throws Exception */ @Test public void testPush() throws Exception { // push from repository1 to repository2 PushOperation pop = createPushOperation(); pop.run(new NullProgressMonitor()); assertEquals(Status.UP_TO_DATE, getStatus(pop.getOperationResult())); // let's add a new file to the project shared with repository1 IProject proj = importProject(repository1, projectName); ArrayList<IFile> files = new ArrayList<IFile>(); IFile newFile = testUtils.addFileToProject(proj, "folder2/file2.txt", "New file"); files.add(newFile); IFile[] fileArr = files.toArray(new IFile[files.size()]); AddToIndexOperation trop = new AddToIndexOperation(files); trop.execute(null); CommitOperation cop = new CommitOperation(fileArr, files, TestUtils.AUTHOR, TestUtils.COMMITTER, "Added file"); cop.execute(null); proj.delete(false, false, null); pop = createPushOperation(); pop.run(null); assertEquals(Status.OK, getStatus(pop.getOperationResult())); try { // assert that we cannot run this again pop.run(null); fail("Expected Exception not thrown"); } catch (IllegalStateException e) { // expected } pop = createPushOperation(); pop.run(null); assertEquals(Status.UP_TO_DATE, getStatus(pop.getOperationResult())); String newFilePath = newFile.getFullPath().toOSString(); File testFile = new File(workdir2, newFilePath); assertFalse(testFile.exists()); testFile = new File(workdir, newFilePath); assertTrue(testFile.exists()); // check out test and verify the file is there BranchOperation bop = new BranchOperation(repository2.getRepository(), "refs/heads/test"); bop.execute(null); testFile = new File(workdir2, newFilePath); assertTrue(testFile.exists()); } /** * An invalid URI should yield an operation result with an error message * and the exception should be logged * * @throws Exception */ @Test public void testInvalidUriDuringPush() throws Exception { ILog log = Activator.getDefault().getLog(); LogListener listener = new LogListener(); log.addLogListener(listener); PushOperation pop = createInvalidPushOperation(); pop.run(new NullProgressMonitor()); PushOperationResult result = pop.getOperationResult(); String errorMessage = result.getErrorMessage(new URIish(INVALID_URI)); assertNotNull(errorMessage); assertTrue(errorMessage.contains(INVALID_URI)); assertTrue(listener.loggedSomething()); assertTrue(listener.loggedException()); } private PushOperation createInvalidPushOperation() throws Exception { // set up push with invalid URI to provoke an exception PushOperationSpecification spec = new PushOperationSpecification(); // the remote is invalid URIish remote = new URIish(INVALID_URI); // update master upon master Repository local = repository1.getRepository(); RemoteRefUpdate update = new RemoteRefUpdate(local, "HEAD", "refs/heads/test", false, null, null); spec.addURIRefUpdates(remote, Collections.singletonList(update)); // now we can construct the push operation PushOperation pop = new PushOperation(local, spec, false, 0); return pop; } private static final class LogListener implements ILogListener { private boolean loggedSomething = false; private boolean loggedException = false; @Override public void logging(IStatus status, String plugin) { loggedSomething = true; loggedException = status.getException() != null; } public boolean loggedSomething() { return loggedSomething; } public boolean loggedException() { return loggedException; } } /** * We should get an {@link IllegalStateException} if we run * getOperationResult before run() * * @throws Exception */ @Test public void testIllegalStateExceptionOnGetResultWithoutRun() throws Exception { // push from repository1 to repository2 PushOperation pop = createPushOperation(); try { pop.getOperationResult(); fail("Expected Exception not thrown"); } catch (IllegalStateException e) { // expected } } /** * We should get an {@link IllegalStateException} if the spec was re-used * * @throws Exception */ @Test public void testPushWithReusedSpec() throws Exception { PushOperationSpecification spec = new PushOperationSpecification(); // the remote is repo2 URIish remote = repository2.getUri(); // update master upon master List<RemoteRefUpdate> refUpdates = new ArrayList<RemoteRefUpdate>(); RemoteRefUpdate update = new RemoteRefUpdate(repository1 .getRepository(), "HEAD", "refs/heads/test", false, null, null); refUpdates.add(update); spec.addURIRefUpdates(remote, refUpdates); PushOperation pop = new PushOperation(repository1.getRepository(), spec, false, 0); pop.run(null); pop = new PushOperation(repository1.getRepository(), spec, false, 0); try { pop.run(null); fail("Expected Exception not thrown"); } catch (IllegalStateException e) { // expected } } @Test public void testUpdateTrackingBranchIfSpecifiedInRemoteRefUpdate() throws Exception { // Commit on repository 2 IProject project = importProject(repository2, projectName); RevCommit commit = repository2.addAndCommit(project, new File(workdir2, "test.txt"), "Commit in repository 2"); project.delete(false, false, null); // We want to push from repository 2 to 1 (because repository 2 already // has tracking set up) URIish remote = repository1.getUri(); String trackingRef = "refs/remotes/origin/master"; RemoteRefUpdate update = new RemoteRefUpdate( repository2.getRepository(), "HEAD", "refs/heads/master", false, trackingRef, null); PushOperationSpecification spec = new PushOperationSpecification(); spec.addURIRefUpdates(remote, Arrays.asList(update)); PushOperation push = new PushOperation(repository2.getRepository(), spec, false, 0); push.run(null); PushOperationResult result = push.getOperationResult(); PushResult pushResult = result.getPushResult(remote); assertNotNull("Expected result to have tracking ref update", pushResult.getTrackingRefUpdate(trackingRef)); ObjectId trackingId = repository2.getRepository().resolve(trackingRef); assertEquals("Expected tracking branch to be updated", commit.getId(), trackingId); } private Status getStatus(PushOperationResult operationResult) { URIish uri = operationResult.getURIs().iterator().next(); return operationResult.getPushResult(uri).getRemoteUpdates().iterator() .next().getStatus(); } private PushOperation createPushOperation() throws Exception { // set up push from repository1 to repository2 // we cannot re-use the RemoteRefUpdate!!! PushOperationSpecification spec = new PushOperationSpecification(); // the remote is repo2 URIish remote = new URIish("file:///" + repository2.getRepository().getDirectory().toString()); // update master upon master List<RemoteRefUpdate> refUpdates = new ArrayList<RemoteRefUpdate>(); RemoteRefUpdate update = new RemoteRefUpdate(repository1 .getRepository(), "HEAD", "refs/heads/test", false, null, null); refUpdates.add(update); spec.addURIRefUpdates(remote, refUpdates); // now we can construct the push operation PushOperation pop = new PushOperation(repository1.getRepository(), spec, false, 0); return pop; } }