View Javadoc
1   /*
2    * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com> and others
3    *
4    * This program and the accompanying materials are made available under the
5    * terms of the Eclipse Distribution License v. 1.0 which is available at
6    * https://www.eclipse.org/org/documents/edl-v10.php.
7    *
8    * SPDX-License-Identifier: BSD-3-Clause
9    */
10  
11  package org.eclipse.jgit.transport;
12  
13  import static org.junit.Assert.assertEquals;
14  import static org.junit.Assert.assertNotNull;
15  import static org.junit.Assert.assertTrue;
16  
17  import java.io.IOException;
18  import java.io.OutputStream;
19  import java.util.HashMap;
20  import java.util.HashSet;
21  import java.util.Map;
22  
23  import org.eclipse.jgit.errors.NotSupportedException;
24  import org.eclipse.jgit.errors.TransportException;
25  import org.eclipse.jgit.lib.ObjectId;
26  import org.eclipse.jgit.lib.ObjectIdRef;
27  import org.eclipse.jgit.lib.ProgressMonitor;
28  import org.eclipse.jgit.lib.Ref;
29  import org.eclipse.jgit.lib.RefUpdate.Result;
30  import org.eclipse.jgit.lib.Repository;
31  import org.eclipse.jgit.lib.TextProgressMonitor;
32  import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
33  import org.eclipse.jgit.transport.RemoteRefUpdate.Status;
34  import org.junit.Before;
35  import org.junit.Test;
36  
37  public class PushProcessTest extends SampleDataRepositoryTestCase {
38  	private PushProcess process;
39  
40  	private MockTransport transport;
41  
42  	private HashSet<RemoteRefUpdate> refUpdates;
43  
44  	private HashSet<Ref> advertisedRefs;
45  
46  	private Status connectionUpdateStatus;
47  
48  	@Override
49  	@Before
50  	public void setUp() throws Exception {
51  		super.setUp();
52  		transport = new MockTransport(db, new URIish());
53  		refUpdates = new HashSet<>();
54  		advertisedRefs = new HashSet<>();
55  		connectionUpdateStatus = Status.OK;
56  	}
57  
58  	/**
59  	 * Test for fast-forward remote update.
60  	 *
61  	 * @throws IOException
62  	 */
63  	@Test
64  	public void testUpdateFastForward() throws IOException {
65  		final RemoteRefUpdate rru = new RemoteRefUpdate(db,
66  				"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
67  				"refs/heads/master", false, null, null);
68  		final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
69  				ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
70  		testOneUpdateStatus(rru, ref, Status.OK, Boolean.TRUE);
71  	}
72  
73  	/**
74  	 * Test for non fast-forward remote update, when remote object is not known
75  	 * to local repository.
76  	 *
77  	 * @throws IOException
78  	 */
79  	@Test
80  	public void testUpdateNonFastForwardUnknownObject() throws IOException {
81  		final RemoteRefUpdate rru = new RemoteRefUpdate(db,
82  				"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
83  				"refs/heads/master", false, null, null);
84  		final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
85  				ObjectId.fromString("0000000000000000000000000000000000000001"));
86  		testOneUpdateStatus(rru, ref, Status.REJECTED_NONFASTFORWARD, null);
87  	}
88  
89  	/**
90  	 * Test for non fast-forward remote update, when remote object is known to
91  	 * local repository, but it is not an ancestor of new object.
92  	 *
93  	 * @throws IOException
94  	 */
95  	@Test
96  	public void testUpdateNonFastForward() throws IOException {
97  		final RemoteRefUpdate rru = new RemoteRefUpdate(db,
98  				"ac7e7e44c1885efb472ad54a78327d66bfc4ecef",
99  				"refs/heads/master", false, null, null);
100 		final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
101 				ObjectId.fromString("2c349335b7f797072cf729c4f3bb0914ecb6dec9"));
102 		testOneUpdateStatus(rru, ref, Status.REJECTED_NONFASTFORWARD, null);
103 	}
104 
105 	/**
106 	 * Test for non fast-forward remote update, when force update flag is set.
107 	 *
108 	 * @throws IOException
109 	 */
110 	@Test
111 	public void testUpdateNonFastForwardForced() throws IOException {
112 		final RemoteRefUpdate rru = new RemoteRefUpdate(db,
113 				"ac7e7e44c1885efb472ad54a78327d66bfc4ecef",
114 				"refs/heads/master", true, null, null);
115 		final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
116 				ObjectId.fromString("2c349335b7f797072cf729c4f3bb0914ecb6dec9"));
117 		testOneUpdateStatus(rru, ref, Status.OK, Boolean.FALSE);
118 	}
119 
120 	/**
121 	 * Test for remote ref creation.
122 	 *
123 	 * @throws IOException
124 	 */
125 	@Test
126 	public void testUpdateCreateRef() throws IOException {
127 		final RemoteRefUpdate rru = new RemoteRefUpdate(db,
128 				"ac7e7e44c1885efb472ad54a78327d66bfc4ecef",
129 				"refs/heads/master", false, null, null);
130 		testOneUpdateStatus(rru, null, Status.OK, Boolean.TRUE);
131 	}
132 
133 	/**
134 	 * Test for remote ref deletion.
135 	 *
136 	 * @throws IOException
137 	 */
138 	@Test
139 	public void testUpdateDelete() throws IOException {
140 		final RemoteRefUpdate rru = new RemoteRefUpdate(db, (String) null,
141 				"refs/heads/master", false, null, null);
142 		final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
143 				ObjectId.fromString("2c349335b7f797072cf729c4f3bb0914ecb6dec9"));
144 		testOneUpdateStatus(rru, ref, Status.OK, Boolean.TRUE);
145 	}
146 
147 	/**
148 	 * Test for remote ref deletion (try), when that ref doesn't exist on remote
149 	 * repo.
150 	 *
151 	 * @throws IOException
152 	 */
153 	@Test
154 	public void testUpdateDeleteNonExisting() throws IOException {
155 		final RemoteRefUpdate rru = new RemoteRefUpdate(db, (String) null,
156 				"refs/heads/master", false, null, null);
157 		testOneUpdateStatus(rru, null, Status.NON_EXISTING, null);
158 	}
159 
160 	/**
161 	 * Test for remote ref update, when it is already up to date.
162 	 *
163 	 * @throws IOException
164 	 */
165 	@Test
166 	public void testUpdateUpToDate() throws IOException {
167 		final RemoteRefUpdate rru = new RemoteRefUpdate(db,
168 				"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
169 				"refs/heads/master", false, null, null);
170 		final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
171 				ObjectId.fromString("2c349335b7f797072cf729c4f3bb0914ecb6dec9"));
172 		testOneUpdateStatus(rru, ref, Status.UP_TO_DATE, null);
173 	}
174 
175 	/**
176 	 * Test for remote ref update with expected remote object.
177 	 *
178 	 * @throws IOException
179 	 */
180 	@Test
181 	public void testUpdateExpectedRemote() throws IOException {
182 		final RemoteRefUpdate rru = new RemoteRefUpdate(db,
183 				"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
184 				"refs/heads/master", false, null, ObjectId
185 						.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
186 		final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
187 				ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
188 		testOneUpdateStatus(rru, ref, Status.OK, Boolean.TRUE);
189 	}
190 
191 	/**
192 	 * Test for remote ref update with expected old object set, when old object
193 	 * is not that expected one.
194 	 *
195 	 * @throws IOException
196 	 */
197 	@Test
198 	public void testUpdateUnexpectedRemote() throws IOException {
199 		final RemoteRefUpdate rru = new RemoteRefUpdate(db,
200 				"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
201 				"refs/heads/master", false, null, ObjectId
202 						.fromString("0000000000000000000000000000000000000001"));
203 		final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
204 				ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
205 		testOneUpdateStatus(rru, ref, Status.REJECTED_REMOTE_CHANGED, null);
206 	}
207 
208 	/**
209 	 * Test for remote ref update with expected old object set, when old object
210 	 * is not that expected one and force update flag is set (which should have
211 	 * lower priority) - shouldn't change behavior.
212 	 *
213 	 * @throws IOException
214 	 */
215 	@Test
216 	public void testUpdateUnexpectedRemoteVsForce() throws IOException {
217 		final RemoteRefUpdate rru = new RemoteRefUpdate(db,
218 				"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
219 				"refs/heads/master", true, null, ObjectId
220 						.fromString("0000000000000000000000000000000000000001"));
221 		final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
222 				ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
223 		testOneUpdateStatus(rru, ref, Status.REJECTED_REMOTE_CHANGED, null);
224 	}
225 
226 	/**
227 	 * Test for remote ref update, when connection rejects update.
228 	 *
229 	 * @throws IOException
230 	 */
231 	@Test
232 	public void testUpdateRejectedByConnection() throws IOException {
233 		connectionUpdateStatus = Status.REJECTED_OTHER_REASON;
234 		final RemoteRefUpdate rru = new RemoteRefUpdate(db,
235 				"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
236 				"refs/heads/master", false, null, null);
237 		final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
238 				ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
239 		testOneUpdateStatus(rru, ref, Status.REJECTED_OTHER_REASON, null);
240 	}
241 
242 	/**
243 	 * Test for remote refs updates with mixed cases that shouldn't depend on
244 	 * each other.
245 	 *
246 	 * @throws IOException
247 	 */
248 	@Test
249 	public void testUpdateMixedCases() throws IOException {
250 		final RemoteRefUpdate rruOk = new RemoteRefUpdate(db, (String) null,
251 				"refs/heads/master", false, null, null);
252 		final Ref refToChange = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
253 				ObjectId.fromString("2c349335b7f797072cf729c4f3bb0914ecb6dec9"));
254 		final RemoteRefUpdate rruReject = new RemoteRefUpdate(db,
255 				(String) null, "refs/heads/nonexisting", false, null, null);
256 		refUpdates.add(rruOk);
257 		refUpdates.add(rruReject);
258 		advertisedRefs.add(refToChange);
259 		executePush();
260 		assertEquals(Status.OK, rruOk.getStatus());
261 		assertTrue(rruOk.isFastForward());
262 		assertEquals(Status.NON_EXISTING, rruReject.getStatus());
263 	}
264 
265 	/**
266 	 * Test for local tracking ref update.
267 	 *
268 	 * @throws IOException
269 	 */
270 	@Test
271 	public void testTrackingRefUpdateEnabled() throws IOException {
272 		final RemoteRefUpdate rru = new RemoteRefUpdate(db,
273 				"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
274 				"refs/heads/master", false, "refs/remotes/test/master", null);
275 		final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
276 				ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
277 		refUpdates.add(rru);
278 		advertisedRefs.add(ref);
279 		final PushResult result = executePush();
280 		final TrackingRefUpdate tru = result
281 				.getTrackingRefUpdate("refs/remotes/test/master");
282 		assertNotNull(tru);
283 		assertEquals("refs/remotes/test/master", tru.getLocalName());
284 		assertEquals(Result.NEW, tru.getResult());
285 	}
286 
287 	/**
288 	 * Test for local tracking ref update disabled.
289 	 *
290 	 * @throws IOException
291 	 */
292 	@Test
293 	public void testTrackingRefUpdateDisabled() throws IOException {
294 		final RemoteRefUpdate rru = new RemoteRefUpdate(db,
295 				"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
296 				"refs/heads/master", false, null, null);
297 		final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
298 				ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
299 		refUpdates.add(rru);
300 		advertisedRefs.add(ref);
301 		final PushResult result = executePush();
302 		assertTrue(result.getTrackingRefUpdates().isEmpty());
303 	}
304 
305 	/**
306 	 * Test for local tracking ref update when remote update has failed.
307 	 *
308 	 * @throws IOException
309 	 */
310 	@Test
311 	public void testTrackingRefUpdateOnReject() throws IOException {
312 		final RemoteRefUpdate rru = new RemoteRefUpdate(db,
313 				"ac7e7e44c1885efb472ad54a78327d66bfc4ecef",
314 				"refs/heads/master", false, null, null);
315 		final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
316 				ObjectId.fromString("2c349335b7f797072cf729c4f3bb0914ecb6dec9"));
317 		final PushResult result = testOneUpdateStatus(rru, ref,
318 				Status.REJECTED_NONFASTFORWARD, null);
319 		assertTrue(result.getTrackingRefUpdates().isEmpty());
320 	}
321 
322 	/**
323 	 * Test for push operation result - that contains expected elements.
324 	 *
325 	 * @throws IOException
326 	 */
327 	@Test
328 	public void testPushResult() throws IOException {
329 		final RemoteRefUpdate rru = new RemoteRefUpdate(db,
330 				"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
331 				"refs/heads/master", false, "refs/remotes/test/master", null);
332 		final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
333 				ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
334 		refUpdates.add(rru);
335 		advertisedRefs.add(ref);
336 		final PushResult result = executePush();
337 		assertEquals(1, result.getTrackingRefUpdates().size());
338 		assertEquals(1, result.getAdvertisedRefs().size());
339 		assertEquals(1, result.getRemoteUpdates().size());
340 		assertNotNull(result.getTrackingRefUpdate("refs/remotes/test/master"));
341 		assertNotNull(result.getAdvertisedRef("refs/heads/master"));
342 		assertNotNull(result.getRemoteUpdate("refs/heads/master"));
343 	}
344 
345 	private PushResult testOneUpdateStatus(final RemoteRefUpdate rru,
346 			final Ref advertisedRef, final Status expectedStatus,
347 			Boolean fastForward) throws NotSupportedException,
348 			TransportException {
349 		refUpdates.add(rru);
350 		if (advertisedRef != null)
351 			advertisedRefs.add(advertisedRef);
352 		final PushResult result = executePush();
353 		assertEquals(expectedStatus, rru.getStatus());
354 		if (fastForward != null)
355 			assertEquals(fastForward, Boolean.valueOf(rru.isFastForward()));
356 		return result;
357 	}
358 
359 	private PushResult executePush() throws NotSupportedException,
360 			TransportException {
361 		process = new PushProcess(transport, refUpdates);
362 		return process.execute(new TextProgressMonitor());
363 	}
364 
365 	private class MockTransport extends Transport {
366 		MockTransport(Repository local, URIish uri) {
367 			super(local, uri);
368 		}
369 
370 		@Override
371 		public FetchConnection openFetch() throws NotSupportedException,
372 				TransportException {
373 			throw new NotSupportedException("mock");
374 		}
375 
376 		@Override
377 		public PushConnection openPush() throws NotSupportedException,
378 				TransportException {
379 			return new MockPushConnection();
380 		}
381 
382 		@Override
383 		public void close() {
384 			// nothing here
385 		}
386 	}
387 
388 	private class MockPushConnection extends BaseConnection implements
389 			PushConnection {
390 		MockPushConnection() {
391 			final Map<String, Ref> refsMap = new HashMap<>();
392 			for (Ref r : advertisedRefs)
393 				refsMap.put(r.getName(), r);
394 			available(refsMap);
395 		}
396 
397 		@Override
398 		public void close() {
399 			// nothing here
400 		}
401 
402 		@Override
403 		public void push(ProgressMonitor monitor,
404 				Map<String, RemoteRefUpdate> refsToUpdate, OutputStream out)
405 				throws TransportException {
406 			push(monitor, refsToUpdate);
407 		}
408 
409 		@Override
410 		public void push(ProgressMonitor monitor,
411 				Map<String, RemoteRefUpdate> refsToUpdate)
412 				throws TransportException {
413 			for (RemoteRefUpdate rru : refsToUpdate.values()) {
414 				assertEquals(Status.NOT_ATTEMPTED, rru.getStatus());
415 				rru.setStatus(connectionUpdateStatus);
416 			}
417 		}
418 	}
419 }