View Javadoc
1   /*
2    * Copyright (C) 2018, Google LLC. 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  package org.eclipse.jgit.transport;
11  
12  import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
13  import static org.eclipse.jgit.lib.Constants.OBJ_COMMIT;
14  import static org.eclipse.jgit.lib.Constants.OBJ_TAG;
15  import static org.eclipse.jgit.lib.Constants.OBJ_TREE;
16  import static org.eclipse.jgit.transport.ObjectIdMatcher.hasOnlyObjectIds;
17  import static org.hamcrest.MatcherAssert.assertThat;
18  import static org.hamcrest.Matchers.hasItems;
19  import static org.junit.Assert.assertEquals;
20  import static org.junit.Assert.assertFalse;
21  import static org.junit.Assert.assertThrows;
22  import static org.junit.Assert.assertTrue;
23  
24  import java.io.ByteArrayInputStream;
25  import java.io.ByteArrayOutputStream;
26  import java.io.IOException;
27  
28  import org.eclipse.jgit.errors.PackProtocolException;
29  import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
30  import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
31  import org.eclipse.jgit.junit.TestRepository;
32  import org.eclipse.jgit.lib.Config;
33  import org.eclipse.jgit.revwalk.RevCommit;
34  import org.junit.Before;
35  import org.junit.Test;
36  
37  public class ProtocolV2ParserTest {
38  
39  	private TestRepository<InMemoryRepository> testRepo;
40  
41  	@Before
42  	public void setUp() throws Exception {
43  		testRepo = new TestRepository<>(newRepo("protocol-v2-parser-test"));
44  	}
45  
46  	private static InMemoryRepository newRepo(String name) {
47  		return new InMemoryRepository(new DfsRepositoryDescription(name));
48  	}
49  
50  	private static class ConfigBuilder {
51  
52  		private boolean allowRefInWant;
53  
54  		private boolean allowFilter;
55  
56  		private ConfigBuilder() {
57  		}
58  
59  		static ConfigBuilder start() {
60  			return new ConfigBuilder();
61  		}
62  
63  		static TransferConfig getDefault() {
64  			return start().done();
65  		}
66  
67  		ConfigBuilder allowRefInWant() {
68  			allowRefInWant = true;
69  			return this;
70  		}
71  
72  		ConfigBuilder allowFilter() {
73  			allowFilter = true;
74  			return this;
75  		}
76  
77  		TransferConfig done() {
78  			Config rc = new Config();
79  			rc.setBoolean("uploadpack", null, "allowrefinwant", allowRefInWant);
80  			rc.setBoolean("uploadpack", null, "allowfilter", allowFilter);
81  			return new TransferConfig(rc);
82  		}
83  	}
84  
85  	/*
86  	 * Convert the input lines to the PacketLine that the parser reads.
87  	 */
88  	private static PacketLineIn formatAsPacketLine(String... inputLines)
89  			throws IOException {
90  		ByteArrayOutputStream send = new ByteArrayOutputStream();
91  		PacketLineOut pckOut = new PacketLineOut(send);
92  		for (String line : inputLines) {
93  			if (PacketLineIn.isEnd(line)) {
94  				pckOut.end();
95  			} else if (PacketLineIn.isDelimiter(line)) {
96  				pckOut.writeDelim();
97  			} else {
98  				pckOut.writeString(line);
99  			}
100 		}
101 
102 		return new PacketLineIn(new ByteArrayInputStream(send.toByteArray()));
103 	}
104 
105 	/*
106 	 * Successful fetch with the basic core commands of the protocol.
107 	 */
108 	@Test
109 	public void testFetchBasicArguments()
110 			throws PackProtocolException, IOException {
111 		PacketLineIn pckIn = formatAsPacketLine(
112 				PacketLineIn.delimiter(),
113 				"thin-pack", "no-progress", "include-tag", "ofs-delta",
114 				"want 4624442d68ee402a94364191085b77137618633e",
115 				"want f900c8326a43303685c46b279b9f70411bff1a4b",
116 				"have 554f6e41067b9e3e565b6988a8294fac1cb78f4b",
117 				"have abc760ab9ad72f08209943251b36cb886a578f87", "done",
118 				PacketLineIn.end());
119 		ProtocolV2Parser parser = new ProtocolV2Parser(
120 				ConfigBuilder.getDefault());
121 		FetchV2Request request = parser.parseFetchRequest(pckIn);
122 		assertTrue(request.getClientCapabilities()
123 				.contains(GitProtocolConstants.OPTION_THIN_PACK));
124 		assertTrue(request.getClientCapabilities()
125 				.contains(GitProtocolConstants.OPTION_NO_PROGRESS));
126 		assertTrue(request.getClientCapabilities()
127 				.contains(GitProtocolConstants.OPTION_INCLUDE_TAG));
128 		assertTrue(request.getClientCapabilities()
129 				.contains(GitProtocolConstants.CAPABILITY_OFS_DELTA));
130 		assertThat(request.getWantIds(),
131 				hasOnlyObjectIds("4624442d68ee402a94364191085b77137618633e",
132 						"f900c8326a43303685c46b279b9f70411bff1a4b"));
133 		assertThat(request.getPeerHas(),
134 				hasOnlyObjectIds("554f6e41067b9e3e565b6988a8294fac1cb78f4b",
135 						"abc760ab9ad72f08209943251b36cb886a578f87"));
136 		assertTrue(request.getWantedRefs().isEmpty());
137 		assertTrue(request.wasDoneReceived());
138 	}
139 
140 	@Test
141 	public void testFetchWithShallow_deepen() throws IOException {
142 		PacketLineIn pckIn = formatAsPacketLine(
143 				PacketLineIn.delimiter(),
144 				"deepen 15",
145 				"deepen-relative",
146 				"shallow 28274d02c489f4c7e68153056e9061a46f62d7a0",
147 				"shallow 145e683b229dcab9d0e2ccb01b386f9ecc17d29d",
148 				PacketLineIn.end());
149 		ProtocolV2Parser parser = new ProtocolV2Parser(
150 				ConfigBuilder.getDefault());
151 		FetchV2Request request = parser.parseFetchRequest(pckIn);
152 		assertThat(request.getClientShallowCommits(),
153 				hasOnlyObjectIds("28274d02c489f4c7e68153056e9061a46f62d7a0",
154 						"145e683b229dcab9d0e2ccb01b386f9ecc17d29d"));
155 		assertTrue(request.getDeepenNotRefs().isEmpty());
156 		assertEquals(15, request.getDepth());
157 		assertTrue(request.getClientCapabilities()
158 				.contains(GitProtocolConstants.OPTION_DEEPEN_RELATIVE));
159 	}
160 
161 	@Test
162 	public void testFetchWithShallow_deepenNot() throws IOException {
163 		PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(),
164 				"shallow 28274d02c489f4c7e68153056e9061a46f62d7a0",
165 				"shallow 145e683b229dcab9d0e2ccb01b386f9ecc17d29d",
166 				"deepen-not a08595f76159b09d57553e37a5123f1091bb13e7",
167 				PacketLineIn.end());
168 		ProtocolV2Parser parser = new ProtocolV2Parser(
169 				ConfigBuilder.getDefault());
170 		FetchV2Request request = parser.parseFetchRequest(pckIn);
171 		assertThat(request.getClientShallowCommits(),
172 				hasOnlyObjectIds("28274d02c489f4c7e68153056e9061a46f62d7a0",
173 						"145e683b229dcab9d0e2ccb01b386f9ecc17d29d"));
174 		assertThat(request.getDeepenNotRefs(),
175 				hasItems("a08595f76159b09d57553e37a5123f1091bb13e7"));
176 	}
177 
178 	@Test
179 	public void testFetchWithShallow_deepenSince() throws IOException {
180 		PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(),
181 				"shallow 28274d02c489f4c7e68153056e9061a46f62d7a0",
182 				"shallow 145e683b229dcab9d0e2ccb01b386f9ecc17d29d",
183 				"deepen-since 123123123",
184 				PacketLineIn.end());
185 		ProtocolV2Parser parser = new ProtocolV2Parser(
186 				ConfigBuilder.getDefault());
187 		FetchV2Request request = parser.parseFetchRequest(pckIn);
188 		assertThat(request.getClientShallowCommits(),
189 				hasOnlyObjectIds("28274d02c489f4c7e68153056e9061a46f62d7a0",
190 						"145e683b229dcab9d0e2ccb01b386f9ecc17d29d"));
191 		assertEquals(123123123, request.getDeepenSince());
192 	}
193 
194 	@Test
195 	public void testFetchWithNoneFilter() throws IOException {
196 		PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(),
197 				"filter blob:none",
198 				PacketLineIn.end());
199 		ProtocolV2Parser parser = new ProtocolV2Parser(
200 				ConfigBuilder.start().allowFilter().done());
201 		FetchV2Request request = parser.parseFetchRequest(pckIn);
202 		assertFalse(request.getFilterSpec().allowsType(OBJ_BLOB));
203 		assertTrue(request.getFilterSpec().allowsType(OBJ_TREE));
204 		assertTrue(request.getFilterSpec().allowsType(OBJ_COMMIT));
205 		assertTrue(request.getFilterSpec().allowsType(OBJ_TAG));
206 		assertEquals(-1, request.getFilterSpec().getBlobLimit());
207 		assertEquals(-1, request.getFilterSpec().getTreeDepthLimit());
208 	}
209 
210 	@Test
211 	public void testFetchWithBlobSizeFilter() throws IOException {
212 		PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(),
213 				"filter blob:limit=15",
214 				PacketLineIn.end());
215 		ProtocolV2Parser parser = new ProtocolV2Parser(
216 				ConfigBuilder.start().allowFilter().done());
217 		FetchV2Request request = parser.parseFetchRequest(pckIn);
218 		assertTrue(request.getFilterSpec().allowsType(OBJ_BLOB));
219 		assertTrue(request.getFilterSpec().allowsType(OBJ_TREE));
220 		assertTrue(request.getFilterSpec().allowsType(OBJ_COMMIT));
221 		assertTrue(request.getFilterSpec().allowsType(OBJ_TAG));
222 		assertEquals(15, request.getFilterSpec().getBlobLimit());
223 		assertEquals(-1, request.getFilterSpec().getTreeDepthLimit());
224 	}
225 
226 	@Test
227 	public void testFetchWithTreeDepthFilter() throws IOException {
228 		PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(),
229 				"filter tree:3",
230 				PacketLineIn.end());
231 		ProtocolV2Parser parser = new ProtocolV2Parser(
232 				ConfigBuilder.start().allowFilter().done());
233 		FetchV2Request request = parser.parseFetchRequest(pckIn);
234 		assertTrue(request.getFilterSpec().allowsType(OBJ_BLOB));
235 		assertTrue(request.getFilterSpec().allowsType(OBJ_TREE));
236 		assertTrue(request.getFilterSpec().allowsType(OBJ_COMMIT));
237 		assertTrue(request.getFilterSpec().allowsType(OBJ_TAG));
238 		assertEquals(-1, request.getFilterSpec().getBlobLimit());
239 		assertEquals(3, request.getFilterSpec().getTreeDepthLimit());
240 	}
241 
242 	@Test
243 	public void testFetchMustNotHaveMultipleFilters() throws IOException {
244 		PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(),
245 				"filter blob:none",
246 				"filter blob:limit=12",
247 				PacketLineIn.end());
248 		ProtocolV2Parser parser = new ProtocolV2Parser(
249 				ConfigBuilder.start().allowFilter().done());
250 
251 		assertThrows(PackProtocolException.class,
252 				() -> parser.parseFetchRequest(pckIn));
253 	}
254 
255 	@Test
256 	public void testFetchFilterWithoutAllowFilter() throws IOException {
257 		PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(),
258 				"filter blob:limit=12", PacketLineIn.end());
259 		ProtocolV2Parser parser = new ProtocolV2Parser(
260 				ConfigBuilder.getDefault());
261 
262 		assertThrows(PackProtocolException.class,
263 				() -> parser.parseFetchRequest(pckIn));
264 	}
265 
266 	@Test
267 	public void testFetchWithRefInWant() throws Exception {
268 		RevCommit one = testRepo.commit().message("1").create();
269 		RevCommit two = testRepo.commit().message("2").create();
270 		testRepo.update("branchA", one);
271 		testRepo.update("branchB", two);
272 
273 		PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(),
274 				"want e4980cdc48cfa1301493ca94eb70523f6788b819",
275 				"want-ref refs/heads/branchA",
276 				PacketLineIn.end());
277 		ProtocolV2Parser parser = new ProtocolV2Parser(
278 				ConfigBuilder.start().allowRefInWant().done());
279 
280 		FetchV2Request request = parser.parseFetchRequest(pckIn);
281 		assertEquals(1, request.getWantedRefs().size());
282 		assertThat(request.getWantedRefs(),
283 				hasItems("refs/heads/branchA"));
284 		assertEquals(1, request.getWantIds().size());
285 		assertThat(request.getWantIds(), hasOnlyObjectIds(
286 				"e4980cdc48cfa1301493ca94eb70523f6788b819"));
287 	}
288 
289 	@Test
290 	public void testFetchWithRefInWantUnknownRef() throws Exception {
291 		PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(),
292 				"want e4980cdc48cfa1301493ca94eb70523f6788b819",
293 				"want-ref refs/heads/branchC",
294 				PacketLineIn.end());
295 		ProtocolV2Parser parser = new ProtocolV2Parser(
296 				ConfigBuilder.start().allowRefInWant().done());
297 
298 		RevCommit one = testRepo.commit().message("1").create();
299 		RevCommit two = testRepo.commit().message("2").create();
300 		testRepo.update("branchA", one);
301 		testRepo.update("branchB", two);
302 
303 		FetchV2Request request = parser.parseFetchRequest(pckIn);
304 		assertEquals(1, request.getWantedRefs().size());
305 		assertThat(request.getWantedRefs(), hasItems("refs/heads/branchC"));
306 	}
307 
308 	@Test
309 	public void testLsRefsMinimalReq() throws IOException {
310 		PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(),
311 				PacketLineIn.end());
312 
313 		ProtocolV2Parser parser = new ProtocolV2Parser(
314 				ConfigBuilder.getDefault());
315 		LsRefsV2Request req = parser.parseLsRefsRequest(pckIn);
316 		assertFalse(req.getPeel());
317 		assertFalse(req.getSymrefs());
318 		assertEquals(0, req.getRefPrefixes().size());
319 	}
320 
321 	@Test
322 	public void testLsRefsSymrefs() throws IOException {
323 		PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(), "symrefs",
324 				PacketLineIn.end());
325 
326 		ProtocolV2Parser parser = new ProtocolV2Parser(
327 				ConfigBuilder.getDefault());
328 		LsRefsV2Request req = parser.parseLsRefsRequest(pckIn);
329 		assertFalse(req.getPeel());
330 		assertTrue(req.getSymrefs());
331 		assertEquals(0, req.getRefPrefixes().size());
332 
333 	}
334 
335 	@Test
336 	public void testLsRefsPeel() throws IOException {
337 		PacketLineIn pckIn = formatAsPacketLine(
338 				PacketLineIn.delimiter(),
339 				"peel",
340 				PacketLineIn.end());
341 
342 		ProtocolV2Parser parser = new ProtocolV2Parser(
343 				ConfigBuilder.getDefault());
344 		LsRefsV2Request req = parser.parseLsRefsRequest(pckIn);
345 		assertTrue(req.getPeel());
346 		assertFalse(req.getSymrefs());
347 		assertEquals(0, req.getRefPrefixes().size());
348 	}
349 
350 	@Test
351 	public void testLsRefsRefPrefixes() throws IOException {
352 		PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(),
353 				"ref-prefix refs/for", "ref-prefix refs/heads",
354 				PacketLineIn.end());
355 
356 		ProtocolV2Parser parser = new ProtocolV2Parser(
357 				ConfigBuilder.getDefault());
358 		LsRefsV2Request req = parser.parseLsRefsRequest(pckIn);
359 		assertFalse(req.getPeel());
360 		assertFalse(req.getSymrefs());
361 		assertEquals(2, req.getRefPrefixes().size());
362 		assertThat(req.getRefPrefixes(), hasItems("refs/for", "refs/heads"));
363 	}
364 }