blob: 0d70cd6121009c97c7254800eb9f173d58a655c3 [file] [log] [blame]
/*
* Copyright (C) 2018, Google LLC.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
* under the terms of the Eclipse Distribution License v1.0 which
* accompanies this distribution, is reproduced below, and is
* available at http://www.eclipse.org/org/documents/edl-v10.php
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* - Neither the name of the Eclipse Foundation, Inc. nor the
* names of its contributors may be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.eclipse.jgit.transport;
import static org.hamcrest.Matchers.hasItems;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.eclipse.jgit.transport.ObjectIdMatcher.hasOnlyObjectIds;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import org.eclipse.jgit.errors.PackProtocolException;
import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.revwalk.RevCommit;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
public class ProtocolV2ParserTest {
@Rule
public ExpectedException thrown = ExpectedException.none();
private TestRepository<InMemoryRepository> testRepo;
@Before
public void setUp() throws Exception {
testRepo = new TestRepository<>(newRepo("protocol-v2-parser-test"));
}
private static InMemoryRepository newRepo(String name) {
return new InMemoryRepository(new DfsRepositoryDescription(name));
}
private static class ConfigBuilder {
private boolean allowRefInWant;
private boolean allowFilter;
private ConfigBuilder() {
}
static ConfigBuilder start() {
return new ConfigBuilder();
}
static TransferConfig getDefault() {
return start().done();
}
ConfigBuilder allowRefInWant() {
allowRefInWant = true;
return this;
}
ConfigBuilder allowFilter() {
allowFilter = true;
return this;
}
TransferConfig done() {
Config rc = new Config();
rc.setBoolean("uploadpack", null, "allowrefinwant", allowRefInWant);
rc.setBoolean("uploadpack", null, "allowfilter", allowFilter);
return new TransferConfig(rc);
}
}
/*
* Convert the input lines to the PacketLine that the parser reads.
*/
private static PacketLineIn formatAsPacketLine(String... inputLines)
throws IOException {
ByteArrayOutputStream send = new ByteArrayOutputStream();
PacketLineOut pckOut = new PacketLineOut(send);
for (String line : inputLines) {
if (PacketLineIn.isEnd(line)) {
pckOut.end();
} else if (PacketLineIn.isDelimiter(line)) {
pckOut.writeDelim();
} else {
pckOut.writeString(line);
}
}
return new PacketLineIn(new ByteArrayInputStream(send.toByteArray()));
}
/*
* Successful fetch with the basic core commands of the protocol.
*/
@Test
public void testFetchBasicArguments()
throws PackProtocolException, IOException {
PacketLineIn pckIn = formatAsPacketLine(
PacketLineIn.delimiter(),
"thin-pack", "no-progress", "include-tag", "ofs-delta",
"want 4624442d68ee402a94364191085b77137618633e",
"want f900c8326a43303685c46b279b9f70411bff1a4b",
"have 554f6e41067b9e3e565b6988a8294fac1cb78f4b",
"have abc760ab9ad72f08209943251b36cb886a578f87", "done",
PacketLineIn.end());
ProtocolV2Parser parser = new ProtocolV2Parser(
ConfigBuilder.getDefault());
FetchV2Request request = parser.parseFetchRequest(pckIn);
assertTrue(request.getClientCapabilities()
.contains(GitProtocolConstants.OPTION_THIN_PACK));
assertTrue(request.getClientCapabilities()
.contains(GitProtocolConstants.OPTION_NO_PROGRESS));
assertTrue(request.getClientCapabilities()
.contains(GitProtocolConstants.OPTION_INCLUDE_TAG));
assertTrue(request.getClientCapabilities()
.contains(GitProtocolConstants.CAPABILITY_OFS_DELTA));
assertThat(request.getWantIds(),
hasOnlyObjectIds("4624442d68ee402a94364191085b77137618633e",
"f900c8326a43303685c46b279b9f70411bff1a4b"));
assertThat(request.getPeerHas(),
hasOnlyObjectIds("554f6e41067b9e3e565b6988a8294fac1cb78f4b",
"abc760ab9ad72f08209943251b36cb886a578f87"));
assertTrue(request.getWantedRefs().isEmpty());
assertTrue(request.wasDoneReceived());
}
@Test
public void testFetchWithShallow_deepen() throws IOException {
PacketLineIn pckIn = formatAsPacketLine(
PacketLineIn.delimiter(),
"deepen 15",
"deepen-relative",
"shallow 28274d02c489f4c7e68153056e9061a46f62d7a0",
"shallow 145e683b229dcab9d0e2ccb01b386f9ecc17d29d",
PacketLineIn.end());
ProtocolV2Parser parser = new ProtocolV2Parser(
ConfigBuilder.getDefault());
FetchV2Request request = parser.parseFetchRequest(pckIn);
assertThat(request.getClientShallowCommits(),
hasOnlyObjectIds("28274d02c489f4c7e68153056e9061a46f62d7a0",
"145e683b229dcab9d0e2ccb01b386f9ecc17d29d"));
assertTrue(request.getDeepenNotRefs().isEmpty());
assertEquals(15, request.getDepth());
assertTrue(request.getClientCapabilities()
.contains(GitProtocolConstants.OPTION_DEEPEN_RELATIVE));
}
@Test
public void testFetchWithShallow_deepenNot() throws IOException {
PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(),
"shallow 28274d02c489f4c7e68153056e9061a46f62d7a0",
"shallow 145e683b229dcab9d0e2ccb01b386f9ecc17d29d",
"deepen-not a08595f76159b09d57553e37a5123f1091bb13e7",
PacketLineIn.end());
ProtocolV2Parser parser = new ProtocolV2Parser(
ConfigBuilder.getDefault());
FetchV2Request request = parser.parseFetchRequest(pckIn);
assertThat(request.getClientShallowCommits(),
hasOnlyObjectIds("28274d02c489f4c7e68153056e9061a46f62d7a0",
"145e683b229dcab9d0e2ccb01b386f9ecc17d29d"));
assertThat(request.getDeepenNotRefs(),
hasItems("a08595f76159b09d57553e37a5123f1091bb13e7"));
}
@Test
public void testFetchWithShallow_deepenSince() throws IOException {
PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(),
"shallow 28274d02c489f4c7e68153056e9061a46f62d7a0",
"shallow 145e683b229dcab9d0e2ccb01b386f9ecc17d29d",
"deepen-since 123123123",
PacketLineIn.end());
ProtocolV2Parser parser = new ProtocolV2Parser(
ConfigBuilder.getDefault());
FetchV2Request request = parser.parseFetchRequest(pckIn);
assertThat(request.getClientShallowCommits(),
hasOnlyObjectIds("28274d02c489f4c7e68153056e9061a46f62d7a0",
"145e683b229dcab9d0e2ccb01b386f9ecc17d29d"));
assertEquals(123123123, request.getDeepenSince());
}
@Test
public void testFetchWithNoneFilter() throws IOException {
PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(),
"filter blob:none",
PacketLineIn.end());
ProtocolV2Parser parser = new ProtocolV2Parser(
ConfigBuilder.start().allowFilter().done());
FetchV2Request request = parser.parseFetchRequest(pckIn);
assertEquals(0, request.getFilterSpec().getBlobLimit());
assertEquals(-1, request.getFilterSpec().getTreeDepthLimit());
}
@Test
public void testFetchWithBlobSizeFilter() throws IOException {
PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(),
"filter blob:limit=15",
PacketLineIn.end());
ProtocolV2Parser parser = new ProtocolV2Parser(
ConfigBuilder.start().allowFilter().done());
FetchV2Request request = parser.parseFetchRequest(pckIn);
assertEquals(15, request.getFilterSpec().getBlobLimit());
assertEquals(-1, request.getFilterSpec().getTreeDepthLimit());
}
@Test
public void testFetchWithTreeDepthFilter() throws IOException {
PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(),
"filter tree:3",
PacketLineIn.end());
ProtocolV2Parser parser = new ProtocolV2Parser(
ConfigBuilder.start().allowFilter().done());
FetchV2Request request = parser.parseFetchRequest(pckIn);
assertEquals(-1, request.getFilterSpec().getBlobLimit());
assertEquals(3, request.getFilterSpec().getTreeDepthLimit());
}
@Test
public void testFetchMustNotHaveMultipleFilters() throws IOException {
PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(),
"filter blob:none",
"filter blob:limit=12",
PacketLineIn.end());
ProtocolV2Parser parser = new ProtocolV2Parser(
ConfigBuilder.start().allowFilter().done());
thrown.expect(PackProtocolException.class);
parser.parseFetchRequest(pckIn);
}
@Test
public void testFetchFilterWithoutAllowFilter() throws IOException {
PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(),
"filter blob:limit=12", PacketLineIn.end());
ProtocolV2Parser parser = new ProtocolV2Parser(
ConfigBuilder.getDefault());
thrown.expect(PackProtocolException.class);
parser.parseFetchRequest(pckIn);
}
@Test
public void testFetchWithRefInWant() throws Exception {
RevCommit one = testRepo.commit().message("1").create();
RevCommit two = testRepo.commit().message("2").create();
testRepo.update("branchA", one);
testRepo.update("branchB", two);
PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(),
"want e4980cdc48cfa1301493ca94eb70523f6788b819",
"want-ref refs/heads/branchA",
PacketLineIn.end());
ProtocolV2Parser parser = new ProtocolV2Parser(
ConfigBuilder.start().allowRefInWant().done());
FetchV2Request request = parser.parseFetchRequest(pckIn);
assertEquals(1, request.getWantedRefs().size());
assertThat(request.getWantedRefs(),
hasItems("refs/heads/branchA"));
assertEquals(1, request.getWantIds().size());
assertThat(request.getWantIds(), hasOnlyObjectIds(
"e4980cdc48cfa1301493ca94eb70523f6788b819"));
}
@Test
public void testFetchWithRefInWantUnknownRef() throws Exception {
PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(),
"want e4980cdc48cfa1301493ca94eb70523f6788b819",
"want-ref refs/heads/branchC",
PacketLineIn.end());
ProtocolV2Parser parser = new ProtocolV2Parser(
ConfigBuilder.start().allowRefInWant().done());
RevCommit one = testRepo.commit().message("1").create();
RevCommit two = testRepo.commit().message("2").create();
testRepo.update("branchA", one);
testRepo.update("branchB", two);
FetchV2Request request = parser.parseFetchRequest(pckIn);
assertEquals(1, request.getWantedRefs().size());
assertThat(request.getWantedRefs(), hasItems("refs/heads/branchC"));
}
@Test
public void testLsRefsMinimalReq() throws IOException {
PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(),
PacketLineIn.end());
ProtocolV2Parser parser = new ProtocolV2Parser(
ConfigBuilder.getDefault());
LsRefsV2Request req = parser.parseLsRefsRequest(pckIn);
assertFalse(req.getPeel());
assertFalse(req.getSymrefs());
assertEquals(0, req.getRefPrefixes().size());
}
@Test
public void testLsRefsSymrefs() throws IOException {
PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(), "symrefs",
PacketLineIn.end());
ProtocolV2Parser parser = new ProtocolV2Parser(
ConfigBuilder.getDefault());
LsRefsV2Request req = parser.parseLsRefsRequest(pckIn);
assertFalse(req.getPeel());
assertTrue(req.getSymrefs());
assertEquals(0, req.getRefPrefixes().size());
}
@Test
public void testLsRefsPeel() throws IOException {
PacketLineIn pckIn = formatAsPacketLine(
PacketLineIn.delimiter(),
"peel",
PacketLineIn.end());
ProtocolV2Parser parser = new ProtocolV2Parser(
ConfigBuilder.getDefault());
LsRefsV2Request req = parser.parseLsRefsRequest(pckIn);
assertTrue(req.getPeel());
assertFalse(req.getSymrefs());
assertEquals(0, req.getRefPrefixes().size());
}
@Test
public void testLsRefsRefPrefixes() throws IOException {
PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.delimiter(),
"ref-prefix refs/for", "ref-prefix refs/heads",
PacketLineIn.end());
ProtocolV2Parser parser = new ProtocolV2Parser(
ConfigBuilder.getDefault());
LsRefsV2Request req = parser.parseLsRefsRequest(pckIn);
assertFalse(req.getPeel());
assertFalse(req.getSymrefs());
assertEquals(2, req.getRefPrefixes().size());
assertThat(req.getRefPrefixes(), hasItems("refs/for", "refs/heads"));
}
}