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