View Javadoc
1   /*
2    * Copyright (C) 2018, Google LLC.
3    * and other copyright owners as documented in the project's IP log.
4    *
5    * This program and the accompanying materials are made available
6    * under the terms of the Eclipse Distribution License v1.0 which
7    * accompanies this distribution, is reproduced below, and is
8    * available at http://www.eclipse.org/org/documents/edl-v10.php
9    *
10   * All rights reserved.
11   *
12   * Redistribution and use in source and binary forms, with or
13   * without modification, are permitted provided that the following
14   * conditions are met:
15   *
16   * - Redistributions of source code must retain the above copyright
17   *   notice, this list of conditions and the following disclaimer.
18   *
19   * - Redistributions in binary form must reproduce the above
20   *   copyright notice, this list of conditions and the following
21   *   disclaimer in the documentation and/or other materials provided
22   *   with the distribution.
23   *
24   * - Neither the name of the Eclipse Foundation, Inc. nor the
25   *   names of its contributors may be used to endorse or promote
26   *   products derived from this software without specific prior
27   *   written permission.
28   *
29   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42   */
43  package org.eclipse.jgit.transport;
44  
45  import static org.hamcrest.Matchers.hasItems;
46  import static org.junit.Assert.assertEquals;
47  import static org.junit.Assert.assertFalse;
48  import static org.junit.Assert.assertThat;
49  import static org.junit.Assert.assertTrue;
50  import static org.eclipse.jgit.transport.ObjectIdMatcher.hasOnlyObjectIds;
51  
52  import java.io.ByteArrayInputStream;
53  import java.io.ByteArrayOutputStream;
54  import java.io.IOException;
55  
56  import org.eclipse.jgit.errors.PackProtocolException;
57  import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
58  import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
59  import org.eclipse.jgit.junit.TestRepository;
60  import org.eclipse.jgit.lib.Config;
61  import org.eclipse.jgit.revwalk.RevCommit;
62  import org.junit.Before;
63  import org.junit.Rule;
64  import org.junit.Test;
65  import org.junit.rules.ExpectedException;
66  
67  public class ProtocolV2ParserTest {
68  
69  	@Rule
70  	public ExpectedException thrown = ExpectedException.none();
71  
72  	private TestRepository<InMemoryRepository> testRepo;
73  
74  	@Before
75  	public void setUp() throws Exception {
76  		testRepo = new TestRepository<>(newRepo("protocol-v2-parser-test"));
77  	}
78  
79  	private static InMemoryRepository newRepo(String name) {
80  		return new InMemoryRepository(new DfsRepositoryDescription(name));
81  	}
82  
83  	private static class ConfigBuilder {
84  
85  		private boolean allowRefInWant;
86  
87  		private boolean allowFilter;
88  
89  		private ConfigBuilder() {
90  		}
91  
92  		static ConfigBuilder start() {
93  			return new ConfigBuilder();
94  		}
95  
96  		static TransferConfig getDefault() {
97  			return start().done();
98  		}
99  
100 		ConfigBuilder allowRefInWant() {
101 			allowRefInWant = true;
102 			return this;
103 		}
104 
105 		ConfigBuilder allowFilter() {
106 			allowFilter = true;
107 			return this;
108 		}
109 
110 		TransferConfig done() {
111 			Config rc = new Config();
112 			rc.setBoolean("uploadpack", null, "allowrefinwant", allowRefInWant);
113 			rc.setBoolean("uploadpack", null, "allowfilter", allowFilter);
114 			return new TransferConfig(rc);
115 		}
116 	}
117 
118 	/*
119 	 * Convert the input lines to the PacketLine that the parser reads.
120 	 */
121 	private static PacketLineIn formatAsPacketLine(String... inputLines)
122 			throws IOException {
123 		ByteArrayOutputStream send = new ByteArrayOutputStream();
124 		PacketLineOut pckOut = new PacketLineOut(send);
125 		for (String line : inputLines) {
126 			if (PacketLineIn.isEnd(line)) {
127 				pckOut.end();
128 			} else if (PacketLineIn.isDelimiter(line)) {
129 				pckOut.writeDelim();
130 			} else {
131 				pckOut.writeString(line);
132 			}
133 		}
134 
135 		return new PacketLineIn(new ByteArrayInputStream(send.toByteArray()));
136 	}
137 
138 	/*
139 	 * Successful fetch with the basic core commands of the protocol.
140 	 */
141 	@Test
142 	public void testFetchBasicArguments()
143 			throws PackProtocolException, IOException {
144 		PacketLineIn pckIn = formatAsPacketLine(
145 				PacketLineIn.delimiter(),
146 				"thin-pack", "no-progress", "include-tag", "ofs-delta",
147 				"want 4624442d68ee402a94364191085b77137618633e",
148 				"want f900c8326a43303685c46b279b9f70411bff1a4b",
149 				"have 554f6e41067b9e3e565b6988a8294fac1cb78f4b",
150 				"have abc760ab9ad72f08209943251b36cb886a578f87", "done",
151 				PacketLineIn.end());
152 		ProtocolV2Parser parser = new ProtocolV2Parser(
153 				ConfigBuilder.getDefault());
154 		FetchV2Request request = parser.parseFetchRequest(pckIn);
155 		assertTrue(request.getClientCapabilities()
156 				.contains(GitProtocolConstants.OPTION_THIN_PACK));
157 		assertTrue(request.getClientCapabilities()
158 				.contains(GitProtocolConstants.OPTION_NO_PROGRESS));
159 		assertTrue(request.getClientCapabilities()
160 				.contains(GitProtocolConstants.OPTION_INCLUDE_TAG));
161 		assertTrue(request.getClientCapabilities()
162 				.contains(GitProtocolConstants.CAPABILITY_OFS_DELTA));
163 		assertThat(request.getWantIds(),
164 				hasOnlyObjectIds("4624442d68ee402a94364191085b77137618633e",
165 						"f900c8326a43303685c46b279b9f70411bff1a4b"));
166 		assertThat(request.getPeerHas(),
167 				hasOnlyObjectIds("554f6e41067b9e3e565b6988a8294fac1cb78f4b",
168 						"abc760ab9ad72f08209943251b36cb886a578f87"));
169 		assertTrue(request.getWantedRefs().isEmpty());
170 		assertTrue(request.wasDoneReceived());
171 	}
172 
173 	@Test
174 	public void testFetchWithShallow_deepen() throws IOException {
175 		PacketLineIn pckIn = formatAsPacketLine(
176 				PacketLineIn.delimiter(),
177 				"deepen 15",
178 				"deepen-relative",
179 				"shallow 28274d02c489f4c7e68153056e9061a46f62d7a0",
180 				"shallow 145e683b229dcab9d0e2ccb01b386f9ecc17d29d",
181 				PacketLineIn.end());
182 		ProtocolV2Parser parser = new ProtocolV2Parser(
183 				ConfigBuilder.getDefault());
184 		FetchV2Request request = parser.parseFetchRequest(pckIn);
185 		assertThat(request.getClientShallowCommits(),
186 				hasOnlyObjectIds("28274d02c489f4c7e68153056e9061a46f62d7a0",
187 						"145e683b229dcab9d0e2ccb01b386f9ecc17d29d"));
188 		assertTrue(request.getDeepenNotRefs().isEmpty());
189 		assertEquals(15, request.getDepth());
190 		assertTrue(request.getClientCapabilities()
191 				.contains(GitProtocolConstants.OPTION_DEEPEN_RELATIVE));
192 	}
193 
194 	@Test
195 	public void testFetchWithShallow_deepenNot() throws IOException {
196 		PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(),
197 				"shallow 28274d02c489f4c7e68153056e9061a46f62d7a0",
198 				"shallow 145e683b229dcab9d0e2ccb01b386f9ecc17d29d",
199 				"deepen-not a08595f76159b09d57553e37a5123f1091bb13e7",
200 				PacketLineIn.end());
201 		ProtocolV2Parser parser = new ProtocolV2Parser(
202 				ConfigBuilder.getDefault());
203 		FetchV2Request request = parser.parseFetchRequest(pckIn);
204 		assertThat(request.getClientShallowCommits(),
205 				hasOnlyObjectIds("28274d02c489f4c7e68153056e9061a46f62d7a0",
206 						"145e683b229dcab9d0e2ccb01b386f9ecc17d29d"));
207 		assertThat(request.getDeepenNotRefs(),
208 				hasItems("a08595f76159b09d57553e37a5123f1091bb13e7"));
209 	}
210 
211 	@Test
212 	public void testFetchWithShallow_deepenSince() throws IOException {
213 		PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(),
214 				"shallow 28274d02c489f4c7e68153056e9061a46f62d7a0",
215 				"shallow 145e683b229dcab9d0e2ccb01b386f9ecc17d29d",
216 				"deepen-since 123123123",
217 				PacketLineIn.end());
218 		ProtocolV2Parser parser = new ProtocolV2Parser(
219 				ConfigBuilder.getDefault());
220 		FetchV2Request request = parser.parseFetchRequest(pckIn);
221 		assertThat(request.getClientShallowCommits(),
222 				hasOnlyObjectIds("28274d02c489f4c7e68153056e9061a46f62d7a0",
223 						"145e683b229dcab9d0e2ccb01b386f9ecc17d29d"));
224 		assertEquals(123123123, request.getDeepenSince());
225 	}
226 
227 	@Test
228 	public void testFetchWithNoneFilter() throws IOException {
229 		PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(),
230 				"filter blob:none",
231 				PacketLineIn.end());
232 		ProtocolV2Parser parser = new ProtocolV2Parser(
233 				ConfigBuilder.start().allowFilter().done());
234 		FetchV2Request request = parser.parseFetchRequest(pckIn);
235 		assertEquals(0, request.getFilterSpec().getBlobLimit());
236 		assertEquals(-1, request.getFilterSpec().getTreeDepthLimit());
237 	}
238 
239 	@Test
240 	public void testFetchWithBlobSizeFilter() throws IOException {
241 		PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(),
242 				"filter blob:limit=15",
243 				PacketLineIn.end());
244 		ProtocolV2Parser parser = new ProtocolV2Parser(
245 				ConfigBuilder.start().allowFilter().done());
246 		FetchV2Request request = parser.parseFetchRequest(pckIn);
247 		assertEquals(15, request.getFilterSpec().getBlobLimit());
248 		assertEquals(-1, request.getFilterSpec().getTreeDepthLimit());
249 	}
250 
251 	@Test
252 	public void testFetchWithTreeDepthFilter() throws IOException {
253 		PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(),
254 				"filter tree:3",
255 				PacketLineIn.end());
256 		ProtocolV2Parser parser = new ProtocolV2Parser(
257 				ConfigBuilder.start().allowFilter().done());
258 		FetchV2Request request = parser.parseFetchRequest(pckIn);
259 		assertEquals(-1, request.getFilterSpec().getBlobLimit());
260 		assertEquals(3, request.getFilterSpec().getTreeDepthLimit());
261 	}
262 
263 	@Test
264 	public void testFetchMustNotHaveMultipleFilters() throws IOException {
265 		PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(),
266 				"filter blob:none",
267 				"filter blob:limit=12",
268 				PacketLineIn.end());
269 		ProtocolV2Parser parser = new ProtocolV2Parser(
270 				ConfigBuilder.start().allowFilter().done());
271 
272 		thrown.expect(PackProtocolException.class);
273 		parser.parseFetchRequest(pckIn);
274 	}
275 
276 	@Test
277 	public void testFetchFilterWithoutAllowFilter() throws IOException {
278 		PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(),
279 				"filter blob:limit=12", PacketLineIn.end());
280 		ProtocolV2Parser parser = new ProtocolV2Parser(
281 				ConfigBuilder.getDefault());
282 
283 		thrown.expect(PackProtocolException.class);
284 		parser.parseFetchRequest(pckIn);
285 	}
286 
287 	@Test
288 	public void testFetchWithRefInWant() throws Exception {
289 		RevCommit one = testRepo.commit().message("1").create();
290 		RevCommit two = testRepo.commit().message("2").create();
291 		testRepo.update("branchA", one);
292 		testRepo.update("branchB", two);
293 
294 		PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(),
295 				"want e4980cdc48cfa1301493ca94eb70523f6788b819",
296 				"want-ref refs/heads/branchA",
297 				PacketLineIn.end());
298 		ProtocolV2Parser parser = new ProtocolV2Parser(
299 				ConfigBuilder.start().allowRefInWant().done());
300 
301 		FetchV2Request request = parser.parseFetchRequest(pckIn);
302 		assertEquals(1, request.getWantedRefs().size());
303 		assertThat(request.getWantedRefs(),
304 				hasItems("refs/heads/branchA"));
305 		assertEquals(1, request.getWantIds().size());
306 		assertThat(request.getWantIds(), hasOnlyObjectIds(
307 				"e4980cdc48cfa1301493ca94eb70523f6788b819"));
308 	}
309 
310 	@Test
311 	public void testFetchWithRefInWantUnknownRef() throws Exception {
312 		PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(),
313 				"want e4980cdc48cfa1301493ca94eb70523f6788b819",
314 				"want-ref refs/heads/branchC",
315 				PacketLineIn.end());
316 		ProtocolV2Parser parser = new ProtocolV2Parser(
317 				ConfigBuilder.start().allowRefInWant().done());
318 
319 		RevCommit one = testRepo.commit().message("1").create();
320 		RevCommit two = testRepo.commit().message("2").create();
321 		testRepo.update("branchA", one);
322 		testRepo.update("branchB", two);
323 
324 		FetchV2Request request = parser.parseFetchRequest(pckIn);
325 		assertEquals(1, request.getWantedRefs().size());
326 		assertThat(request.getWantedRefs(), hasItems("refs/heads/branchC"));
327 	}
328 
329 	@Test
330 	public void testLsRefsMinimalReq() throws IOException {
331 		PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(),
332 				PacketLineIn.end());
333 
334 		ProtocolV2Parser parser = new ProtocolV2Parser(
335 				ConfigBuilder.getDefault());
336 		LsRefsV2Request req = parser.parseLsRefsRequest(pckIn);
337 		assertFalse(req.getPeel());
338 		assertFalse(req.getSymrefs());
339 		assertEquals(0, req.getRefPrefixes().size());
340 	}
341 
342 	@Test
343 	public void testLsRefsSymrefs() throws IOException {
344 		PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(), "symrefs",
345 				PacketLineIn.end());
346 
347 		ProtocolV2Parser parser = new ProtocolV2Parser(
348 				ConfigBuilder.getDefault());
349 		LsRefsV2Request req = parser.parseLsRefsRequest(pckIn);
350 		assertFalse(req.getPeel());
351 		assertTrue(req.getSymrefs());
352 		assertEquals(0, req.getRefPrefixes().size());
353 
354 	}
355 
356 	@Test
357 	public void testLsRefsPeel() throws IOException {
358 		PacketLineIn pckIn = formatAsPacketLine(
359 				PacketLineIn.delimiter(),
360 				"peel",
361 				PacketLineIn.end());
362 
363 		ProtocolV2Parser parser = new ProtocolV2Parser(
364 				ConfigBuilder.getDefault());
365 		LsRefsV2Request req = parser.parseLsRefsRequest(pckIn);
366 		assertTrue(req.getPeel());
367 		assertFalse(req.getSymrefs());
368 		assertEquals(0, req.getRefPrefixes().size());
369 	}
370 
371 	@Test
372 	public void testLsRefsRefPrefixes() throws IOException {
373 		PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(),
374 				"ref-prefix refs/for", "ref-prefix refs/heads",
375 				PacketLineIn.end());
376 
377 		ProtocolV2Parser parser = new ProtocolV2Parser(
378 				ConfigBuilder.getDefault());
379 		LsRefsV2Request req = parser.parseLsRefsRequest(pckIn);
380 		assertFalse(req.getPeel());
381 		assertFalse(req.getSymrefs());
382 		assertEquals(2, req.getRefPrefixes().size());
383 		assertThat(req.getRefPrefixes(), hasItems("refs/for", "refs/heads"));
384 	}
385 }