Dissolve gerrit-server top-level directory

Change-Id: I538512dfe0f1bea774c01fdd45fa410a45634011
This commit is contained in:
David Ostrovsky
2017-09-21 08:37:42 +02:00
committed by Dave Borowitz
parent 472396c797
commit 376a7bbb64
1549 changed files with 342 additions and 335 deletions

View File

@@ -0,0 +1,162 @@
// Copyright (C) 2015 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.server.git;
import static com.google.common.truth.Truth.assertThat;
import static org.easymock.EasyMock.createNiceMock;
import static org.easymock.EasyMock.replay;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Project;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import junit.framework.TestCase;
import org.junit.Test;
public class DestinationListTest extends TestCase {
public static final String R_FOO = "refs/heads/foo";
public static final String R_BAR = "refs/heads/bar";
public static final String P_MY = "myproject";
public static final String P_SLASH = "my/project/with/slashes";
public static final String P_COMPLEX = " a/project/with spaces and \ttabs ";
public static final String L_FOO = R_FOO + "\t" + P_MY + "\n";
public static final String L_BAR = R_BAR + "\t" + P_SLASH + "\n";
public static final String L_FOO_PAD_F = " " + R_FOO + "\t" + P_MY + "\n";
public static final String L_FOO_PAD_E = R_FOO + " \t" + P_MY + "\n";
public static final String L_COMPLEX = R_FOO + "\t" + P_COMPLEX + "\n";
public static final String L_BAD = R_FOO + "\n";
public static final String HEADER = "# Ref\tProject\n";
public static final String HEADER_PROPER = "# Ref \tProject\n";
public static final String C1 = "# A Simple Comment\n";
public static final String C2 = "# Comment with a tab\t and multi # # #\n";
public static final String F_SIMPLE = L_FOO + L_BAR;
public static final String F_PROPER = L_BAR + L_FOO; // alpha order
public static final String F_PAD_F = L_FOO_PAD_F + L_BAR;
public static final String F_PAD_E = L_FOO_PAD_E + L_BAR;
public static final String LABEL = "label";
public static final String LABEL2 = "another";
public static final Branch.NameKey B_FOO = dest(P_MY, R_FOO);
public static final Branch.NameKey B_BAR = dest(P_SLASH, R_BAR);
public static final Branch.NameKey B_COMPLEX = dest(P_COMPLEX, R_FOO);
public static final Set<Branch.NameKey> D_SIMPLE = new HashSet<>();
static {
D_SIMPLE.clear();
D_SIMPLE.add(B_FOO);
D_SIMPLE.add(B_BAR);
}
private static Branch.NameKey dest(String project, String ref) {
return new Branch.NameKey(new Project.NameKey(project), ref);
}
@Test
public void testParseSimple() throws Exception {
DestinationList dl = new DestinationList();
dl.parseLabel(LABEL, F_SIMPLE, null);
Set<Branch.NameKey> branches = dl.getDestinations(LABEL);
assertThat(branches).containsExactlyElementsIn(D_SIMPLE);
}
@Test
public void testParseWHeader() throws Exception {
DestinationList dl = new DestinationList();
dl.parseLabel(LABEL, HEADER + F_SIMPLE, null);
Set<Branch.NameKey> branches = dl.getDestinations(LABEL);
assertThat(branches).containsExactlyElementsIn(D_SIMPLE);
}
@Test
public void testParseWComments() throws Exception {
DestinationList dl = new DestinationList();
dl.parseLabel(LABEL, C1 + F_SIMPLE + C2, null);
Set<Branch.NameKey> branches = dl.getDestinations(LABEL);
assertThat(branches).containsExactlyElementsIn(D_SIMPLE);
}
@Test
public void testParseFooComment() throws Exception {
DestinationList dl = new DestinationList();
dl.parseLabel(LABEL, "#" + L_FOO + L_BAR, null);
Set<Branch.NameKey> branches = dl.getDestinations(LABEL);
assertThat(branches).doesNotContain(B_FOO);
assertThat(branches).contains(B_BAR);
}
@Test
public void testParsePaddedFronts() throws Exception {
DestinationList dl = new DestinationList();
dl.parseLabel(LABEL, F_PAD_F, null);
Set<Branch.NameKey> branches = dl.getDestinations(LABEL);
assertThat(branches).containsExactlyElementsIn(D_SIMPLE);
}
@Test
public void testParsePaddedEnds() throws Exception {
DestinationList dl = new DestinationList();
dl.parseLabel(LABEL, F_PAD_E, null);
Set<Branch.NameKey> branches = dl.getDestinations(LABEL);
assertThat(branches).containsExactlyElementsIn(D_SIMPLE);
}
@Test
public void testParseComplex() throws Exception {
DestinationList dl = new DestinationList();
dl.parseLabel(LABEL, L_COMPLEX, null);
Set<Branch.NameKey> branches = dl.getDestinations(LABEL);
assertThat(branches).contains(B_COMPLEX);
}
@Test(expected = IOException.class)
public void testParseBad() throws IOException {
ValidationError.Sink sink = createNiceMock(ValidationError.Sink.class);
replay(sink);
new DestinationList().parseLabel(LABEL, L_BAD, sink);
}
@Test
public void testParse2Labels() throws Exception {
DestinationList dl = new DestinationList();
dl.parseLabel(LABEL, F_SIMPLE, null);
Set<Branch.NameKey> branches = dl.getDestinations(LABEL);
assertThat(branches).containsExactlyElementsIn(D_SIMPLE);
dl.parseLabel(LABEL2, L_COMPLEX, null);
branches = dl.getDestinations(LABEL);
assertThat(branches).containsExactlyElementsIn(D_SIMPLE);
branches = dl.getDestinations(LABEL2);
assertThat(branches).contains(B_COMPLEX);
}
@Test
public void testAsText() throws Exception {
String text = HEADER_PROPER + "#\n" + F_PROPER;
DestinationList dl = new DestinationList();
dl.parseLabel(LABEL, F_SIMPLE, null);
String asText = dl.asText(LABEL);
assertThat(text).isEqualTo(asText);
dl.parseLabel(LABEL2, asText, null);
assertThat(text).isEqualTo(dl.asText(LABEL2));
}
}

View File

@@ -0,0 +1,322 @@
// Copyright (C) 2015 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.server.git;
import static com.google.common.truth.Truth.assertThat;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.SortedSetMultimap;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet;
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.ObjectId;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevSort;
import org.eclipse.jgit.revwalk.RevWalk;
import org.junit.Before;
import org.junit.Test;
public class GroupCollectorTest {
private TestRepository<?> tr;
@Before
public void setUp() throws Exception {
tr = new TestRepository<>(new InMemoryRepository(new DfsRepositoryDescription("repo")));
}
@Test
public void commitWhoseParentIsUninterestingGetsNewGroup() throws Exception {
RevCommit branchTip = tr.commit().create();
RevCommit a = tr.commit().parent(branchTip).create();
SortedSetMultimap<ObjectId, String> groups =
collectGroups(newWalk(a, branchTip), patchSets(), groups());
assertThat(groups).containsEntry(a, a.name());
}
@Test
public void commitWhoseParentIsNewPatchSetGetsParentsGroup() throws Exception {
RevCommit branchTip = tr.commit().create();
RevCommit a = tr.commit().parent(branchTip).create();
RevCommit b = tr.commit().parent(a).create();
SortedSetMultimap<ObjectId, String> groups =
collectGroups(newWalk(b, branchTip), patchSets(), groups());
assertThat(groups).containsEntry(a, a.name());
assertThat(groups).containsEntry(b, a.name());
}
@Test
public void commitWhoseParentIsExistingPatchSetGetsParentsGroup() throws Exception {
RevCommit branchTip = tr.commit().create();
RevCommit a = tr.commit().parent(branchTip).create();
RevCommit b = tr.commit().parent(a).create();
String group = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
SortedSetMultimap<ObjectId, String> groups =
collectGroups(
newWalk(b, branchTip), patchSets().put(a, psId(1, 1)), groups().put(psId(1, 1), group));
assertThat(groups).containsEntry(a, group);
assertThat(groups).containsEntry(b, group);
}
@Test
public void commitWhoseParentIsExistingPatchSetWithNoGroup() throws Exception {
RevCommit branchTip = tr.commit().create();
RevCommit a = tr.commit().parent(branchTip).create();
RevCommit b = tr.commit().parent(a).create();
SortedSetMultimap<ObjectId, String> groups =
collectGroups(newWalk(b, branchTip), patchSets().put(a, psId(1, 1)), groups());
assertThat(groups).containsEntry(a, a.name());
assertThat(groups).containsEntry(b, a.name());
}
@Test
public void mergeCommitAndNewParentsAllGetSameGroup() throws Exception {
RevCommit branchTip = tr.commit().create();
RevCommit a = tr.commit().parent(branchTip).create();
RevCommit b = tr.commit().parent(branchTip).create();
RevCommit m = tr.commit().parent(a).parent(b).create();
SortedSetMultimap<ObjectId, String> groups =
collectGroups(newWalk(m, branchTip), patchSets(), groups());
assertThat(groups).containsEntry(a, a.name());
assertThat(groups).containsEntry(b, a.name());
assertThat(groups).containsEntry(m, a.name());
}
@Test
public void mergeCommitWhereOneParentHasExistingGroup() throws Exception {
RevCommit branchTip = tr.commit().create();
RevCommit a = tr.commit().parent(branchTip).create();
RevCommit b = tr.commit().parent(branchTip).create();
RevCommit m = tr.commit().parent(a).parent(b).create();
String group = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
SortedSetMultimap<ObjectId, String> groups =
collectGroups(
newWalk(m, branchTip), patchSets().put(b, psId(1, 1)), groups().put(psId(1, 1), group));
// Merge commit and other parent get the existing group.
assertThat(groups).containsEntry(a, group);
assertThat(groups).containsEntry(b, group);
assertThat(groups).containsEntry(m, group);
}
@Test
public void mergeCommitWhereBothParentsHaveDifferentGroups() throws Exception {
RevCommit branchTip = tr.commit().create();
RevCommit a = tr.commit().parent(branchTip).create();
RevCommit b = tr.commit().parent(branchTip).create();
RevCommit m = tr.commit().parent(a).parent(b).create();
String group1 = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
String group2 = "1234567812345678123456781234567812345678";
SortedSetMultimap<ObjectId, String> groups =
collectGroups(
newWalk(m, branchTip),
patchSets().put(a, psId(1, 1)).put(b, psId(2, 1)),
groups().put(psId(1, 1), group1).put(psId(2, 1), group2));
assertThat(groups).containsEntry(a, group1);
assertThat(groups).containsEntry(b, group2);
// Merge commit gets joined group of parents.
assertThat(groups.asMap()).containsEntry(m, ImmutableSet.of(group1, group2));
}
@Test
public void mergeCommitMergesGroupsFromParent() throws Exception {
RevCommit branchTip = tr.commit().create();
RevCommit a = tr.commit().parent(branchTip).create();
RevCommit b = tr.commit().parent(branchTip).create();
RevCommit m = tr.commit().parent(a).parent(b).create();
String group1 = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
String group2a = "1234567812345678123456781234567812345678";
String group2b = "ef123456ef123456ef123456ef123456ef123456";
SortedSetMultimap<ObjectId, String> groups =
collectGroups(
newWalk(m, branchTip),
patchSets().put(a, psId(1, 1)).put(b, psId(2, 1)),
groups().put(psId(1, 1), group1).put(psId(2, 1), group2a).put(psId(2, 1), group2b));
assertThat(groups).containsEntry(a, group1);
assertThat(groups.asMap()).containsEntry(b, ImmutableSet.of(group2a, group2b));
// Joined parent groups are split and resorted.
assertThat(groups.asMap()).containsEntry(m, ImmutableSet.of(group1, group2a, group2b));
}
@Test
public void mergeCommitWithOneUninterestingParentAndOtherParentIsExisting() throws Exception {
RevCommit branchTip = tr.commit().create();
RevCommit a = tr.commit().parent(branchTip).create();
RevCommit m = tr.commit().parent(branchTip).parent(a).create();
String group = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
SortedSetMultimap<ObjectId, String> groups =
collectGroups(
newWalk(m, branchTip), patchSets().put(a, psId(1, 1)), groups().put(psId(1, 1), group));
assertThat(groups).containsEntry(a, group);
assertThat(groups).containsEntry(m, group);
}
@Test
public void mergeCommitWithOneUninterestingParentAndOtherParentIsNew() throws Exception {
RevCommit branchTip = tr.commit().create();
RevCommit a = tr.commit().parent(branchTip).create();
RevCommit m = tr.commit().parent(branchTip).parent(a).create();
SortedSetMultimap<ObjectId, String> groups =
collectGroups(newWalk(m, branchTip), patchSets(), groups());
assertThat(groups).containsEntry(a, a.name());
assertThat(groups).containsEntry(m, a.name());
}
@Test
public void multipleMergeCommitsInHistoryAllResolveToSameGroup() throws Exception {
RevCommit branchTip = tr.commit().create();
RevCommit a = tr.commit().parent(branchTip).create();
RevCommit b = tr.commit().parent(branchTip).create();
RevCommit c = tr.commit().parent(branchTip).create();
RevCommit m1 = tr.commit().parent(b).parent(c).create();
RevCommit m2 = tr.commit().parent(a).parent(m1).create();
SortedSetMultimap<ObjectId, String> groups =
collectGroups(newWalk(m2, branchTip), patchSets(), groups());
assertThat(groups).containsEntry(a, a.name());
assertThat(groups).containsEntry(b, a.name());
assertThat(groups).containsEntry(c, a.name());
assertThat(groups).containsEntry(m1, a.name());
assertThat(groups).containsEntry(m2, a.name());
}
@Test
public void mergeCommitWithDuplicatedParentGetsParentsGroup() throws Exception {
RevCommit branchTip = tr.commit().create();
RevCommit a = tr.commit().parent(branchTip).create();
RevCommit m = tr.commit().parent(a).parent(a).create();
tr.getRevWalk().parseBody(m);
assertThat(m.getParentCount()).isEqualTo(2);
assertThat(m.getParent(0)).isEqualTo(m.getParent(1));
SortedSetMultimap<ObjectId, String> groups =
collectGroups(newWalk(m, branchTip), patchSets(), groups());
assertThat(groups).containsEntry(a, a.name());
assertThat(groups).containsEntry(m, a.name());
}
@Test
public void mergeCommitWithOneNewParentAndTwoExistingPatchSets() throws Exception {
RevCommit branchTip = tr.commit().create();
RevCommit a = tr.commit().parent(branchTip).create();
RevCommit b = tr.commit().parent(branchTip).create();
RevCommit c = tr.commit().parent(b).create();
RevCommit m = tr.commit().parent(a).parent(c).create();
String group1 = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
String group2 = "1234567812345678123456781234567812345678";
SortedSetMultimap<ObjectId, String> groups =
collectGroups(
newWalk(m, branchTip),
patchSets().put(a, psId(1, 1)).put(b, psId(2, 1)),
groups().put(psId(1, 1), group1).put(psId(2, 1), group2));
assertThat(groups).containsEntry(a, group1);
assertThat(groups).containsEntry(b, group2);
assertThat(groups).containsEntry(c, group2);
assertThat(groups.asMap()).containsEntry(m, ImmutableSet.of(group1, group2));
}
@Test
public void collectGroupsForMultipleTipsInParallel() throws Exception {
RevCommit branchTip = tr.commit().create();
RevCommit a = tr.commit().parent(branchTip).create();
RevCommit b = tr.commit().parent(a).create();
RevCommit c = tr.commit().parent(branchTip).create();
RevCommit d = tr.commit().parent(c).create();
RevWalk rw = newWalk(b, branchTip);
rw.markStart(rw.parseCommit(d));
// Schema upgrade case: all commits are existing patch sets, but none have
// groups assigned yet.
SortedSetMultimap<ObjectId, String> groups =
collectGroups(
rw,
patchSets()
.put(branchTip, psId(1, 1))
.put(a, psId(2, 1))
.put(b, psId(3, 1))
.put(c, psId(4, 1))
.put(d, psId(5, 1)),
groups());
assertThat(groups).containsEntry(a, a.name());
assertThat(groups).containsEntry(b, a.name());
assertThat(groups).containsEntry(c, c.name());
assertThat(groups).containsEntry(d, c.name());
}
// TODO(dborowitz): Tests for octopus merges.
private static PatchSet.Id psId(int c, int p) {
return new PatchSet.Id(new Change.Id(c), p);
}
private RevWalk newWalk(ObjectId start, ObjectId branchTip) throws Exception {
// Match RevWalk conditions from ReceiveCommits.
RevWalk rw = new RevWalk(tr.getRepository());
rw.sort(RevSort.TOPO);
rw.sort(RevSort.REVERSE, true);
rw.markStart(rw.parseCommit(start));
rw.markUninteresting(rw.parseCommit(branchTip));
return rw;
}
private static SortedSetMultimap<ObjectId, String> collectGroups(
RevWalk rw,
ImmutableListMultimap.Builder<ObjectId, PatchSet.Id> patchSetsBySha,
ImmutableListMultimap.Builder<PatchSet.Id, String> groupLookup)
throws Exception {
GroupCollector gc = new GroupCollector(patchSetsBySha.build(), groupLookup.build());
RevCommit c;
while ((c = rw.next()) != null) {
gc.visit(c);
}
return gc.getGroups();
}
// Helper methods for constructing various map arguments, to avoid lots of
// type specifications.
private static ImmutableListMultimap.Builder<ObjectId, PatchSet.Id> patchSets() {
return ImmutableListMultimap.builder();
}
private static ImmutableListMultimap.Builder<PatchSet.Id, String> groups() {
return ImmutableListMultimap.builder();
}
}

View File

@@ -0,0 +1,120 @@
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.server.git;
import static org.easymock.EasyMock.anyObject;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.createNiceMock;
import static org.easymock.EasyMock.expectLastCall;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Project;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import org.junit.Before;
import org.junit.Test;
public class GroupListTest {
private static final Project.NameKey PROJECT = new Project.NameKey("project");
private static final String TEXT =
"# UUID \tGroup Name\n"
+ "#\n"
+ "d96b998f8a66ff433af50befb975d0e2bb6e0999\tNon-Interactive Users\n"
+ "ebe31c01aec2c9ac3b3c03e87a47450829ff4310\tAdministrators\n";
private GroupList groupList;
@Before
public void setup() throws IOException {
ValidationError.Sink sink = createNiceMock(ValidationError.Sink.class);
replay(sink);
groupList = GroupList.parse(PROJECT, TEXT, sink);
}
@Test
public void byUUID() throws Exception {
AccountGroup.UUID uuid = new AccountGroup.UUID("d96b998f8a66ff433af50befb975d0e2bb6e0999");
GroupReference groupReference = groupList.byUUID(uuid);
assertEquals(uuid, groupReference.getUUID());
assertEquals("Non-Interactive Users", groupReference.getName());
}
@Test
public void put() {
AccountGroup.UUID uuid = new AccountGroup.UUID("abc");
GroupReference groupReference = new GroupReference(uuid, "Hutzliputz");
groupList.put(uuid, groupReference);
assertEquals(3, groupList.references().size());
GroupReference found = groupList.byUUID(uuid);
assertEquals(groupReference, found);
}
@Test
public void references() throws Exception {
Collection<GroupReference> result = groupList.references();
assertEquals(2, result.size());
AccountGroup.UUID uuid = new AccountGroup.UUID("ebe31c01aec2c9ac3b3c03e87a47450829ff4310");
GroupReference expected = new GroupReference(uuid, "Administrators");
assertTrue(result.contains(expected));
}
@Test
public void uUIDs() throws Exception {
Set<AccountGroup.UUID> result = groupList.uuids();
assertEquals(2, result.size());
AccountGroup.UUID expected = new AccountGroup.UUID("ebe31c01aec2c9ac3b3c03e87a47450829ff4310");
assertTrue(result.contains(expected));
}
@Test
public void validationError() throws Exception {
ValidationError.Sink sink = createMock(ValidationError.Sink.class);
sink.error(anyObject(ValidationError.class));
expectLastCall().times(2);
replay(sink);
groupList = GroupList.parse(PROJECT, TEXT.replace("\t", " "), sink);
verify(sink);
}
@Test
public void retainAll() throws Exception {
AccountGroup.UUID uuid = new AccountGroup.UUID("d96b998f8a66ff433af50befb975d0e2bb6e0999");
groupList.retainUUIDs(Collections.singleton(uuid));
assertNotNull(groupList.byUUID(uuid));
assertNull(groupList.byUUID(new AccountGroup.UUID("ebe31c01aec2c9ac3b3c03e87a47450829ff4310")));
}
@Test
public void asText() throws Exception {
assertTrue(TEXT.equals(groupList.asText()));
}
}

View File

@@ -0,0 +1,227 @@
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.server.git;
import static com.google.gerrit.common.data.Permission.forLabel;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import static com.google.gerrit.server.project.testing.Util.allow;
import static com.google.gerrit.server.project.testing.Util.category;
import static com.google.gerrit.server.project.testing.Util.value;
import static org.junit.Assert.assertEquals;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.common.TimeUtil;
import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.lifecycle.LifecycleManager;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.LabelId;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.reviewdb.client.PatchSetInfo;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountManager;
import com.google.gerrit.server.account.AuthRequest;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.git.LabelNormalizer.Result;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.schema.SchemaCreator;
import com.google.gerrit.server.util.RequestContext;
import com.google.gerrit.server.util.ThreadLocalRequestContext;
import com.google.gerrit.testing.InMemoryDatabase;
import com.google.gerrit.testing.InMemoryModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Provider;
import com.google.inject.util.Providers;
import java.util.List;
import org.eclipse.jgit.lib.Repository;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/** Unit tests for {@link LabelNormalizer}. */
public class LabelNormalizerTest {
@Inject private AccountManager accountManager;
@Inject private AllProjectsName allProjects;
@Inject private GitRepositoryManager repoManager;
@Inject private IdentifiedUser.GenericFactory userFactory;
@Inject private InMemoryDatabase schemaFactory;
@Inject private LabelNormalizer norm;
@Inject private MetaDataUpdate.User metaDataUpdateFactory;
@Inject private ProjectCache projectCache;
@Inject private SchemaCreator schemaCreator;
@Inject protected ThreadLocalRequestContext requestContext;
@Inject private ChangeNotes.Factory changeNotesFactory;
private LifecycleManager lifecycle;
private ReviewDb db;
private Account.Id userId;
private IdentifiedUser user;
private Change change;
private ChangeNotes notes;
@Before
public void setUpInjector() throws Exception {
Injector injector = Guice.createInjector(new InMemoryModule());
injector.injectMembers(this);
lifecycle = new LifecycleManager();
lifecycle.add(injector);
lifecycle.start();
db = schemaFactory.open();
schemaCreator.create(db);
userId = accountManager.authenticate(AuthRequest.forUser("user")).getAccountId();
user = userFactory.create(userId);
requestContext.setContext(
new RequestContext() {
@Override
public CurrentUser getUser() {
return user;
}
@Override
public Provider<ReviewDb> getReviewDbProvider() {
return Providers.of(db);
}
});
configureProject();
setUpChange();
}
private void configureProject() throws Exception {
ProjectConfig pc = loadAllProjects();
for (AccessSection sec : pc.getAccessSections()) {
for (String label : pc.getLabelSections().keySet()) {
sec.removePermission(forLabel(label));
}
}
LabelType lt =
category("Verified", value(1, "Verified"), value(0, "No score"), value(-1, "Fails"));
pc.getLabelSections().put(lt.getName(), lt);
save(pc);
}
private void setUpChange() throws Exception {
change =
new Change(
new Change.Key("Iabcd1234abcd1234abcd1234abcd1234abcd1234"),
new Change.Id(1),
userId,
new Branch.NameKey(allProjects, "refs/heads/master"),
TimeUtil.nowTs());
PatchSetInfo ps = new PatchSetInfo(new PatchSet.Id(change.getId(), 1));
ps.setSubject("Test change");
change.setCurrentPatchSet(ps);
db.changes().insert(ImmutableList.of(change));
notes = changeNotesFactory.createChecked(db, change);
}
@After
public void tearDown() {
if (lifecycle != null) {
lifecycle.stop();
}
requestContext.setContext(null);
if (db != null) {
db.close();
}
InMemoryDatabase.drop(schemaFactory);
}
@Test
public void noNormalizeByPermission() throws Exception {
ProjectConfig pc = loadAllProjects();
allow(pc, forLabel("Code-Review"), -1, 1, REGISTERED_USERS, "refs/heads/*");
allow(pc, forLabel("Verified"), -1, 1, REGISTERED_USERS, "refs/heads/*");
save(pc);
PatchSetApproval cr = psa(userId, "Code-Review", 2);
PatchSetApproval v = psa(userId, "Verified", 1);
assertEquals(Result.create(list(cr, v), list(), list()), norm.normalize(notes, list(cr, v)));
}
@Test
public void normalizeByType() throws Exception {
ProjectConfig pc = loadAllProjects();
allow(pc, forLabel("Code-Review"), -5, 5, REGISTERED_USERS, "refs/heads/*");
allow(pc, forLabel("Verified"), -5, 5, REGISTERED_USERS, "refs/heads/*");
save(pc);
PatchSetApproval cr = psa(userId, "Code-Review", 5);
PatchSetApproval v = psa(userId, "Verified", 5);
assertEquals(
Result.create(list(), list(copy(cr, 2), copy(v, 1)), list()),
norm.normalize(notes, list(cr, v)));
}
@Test
public void emptyPermissionRangeKeepsResult() throws Exception {
PatchSetApproval cr = psa(userId, "Code-Review", 1);
PatchSetApproval v = psa(userId, "Verified", 1);
assertEquals(Result.create(list(cr, v), list(), list()), norm.normalize(notes, list(cr, v)));
}
@Test
public void explicitZeroVoteOnNonEmptyRangeIsPresent() throws Exception {
ProjectConfig pc = loadAllProjects();
allow(pc, forLabel("Code-Review"), -1, 1, REGISTERED_USERS, "refs/heads/*");
save(pc);
PatchSetApproval cr = psa(userId, "Code-Review", 0);
PatchSetApproval v = psa(userId, "Verified", 0);
assertEquals(Result.create(list(cr, v), list(), list()), norm.normalize(notes, list(cr, v)));
}
private ProjectConfig loadAllProjects() throws Exception {
try (Repository repo = repoManager.openRepository(allProjects)) {
ProjectConfig pc = new ProjectConfig(allProjects);
pc.load(repo);
return pc;
}
}
private void save(ProjectConfig pc) throws Exception {
try (MetaDataUpdate md = metaDataUpdateFactory.create(pc.getProject().getNameKey(), user)) {
pc.commit(md);
projectCache.evict(pc.getProject().getNameKey());
}
}
private PatchSetApproval psa(Account.Id accountId, String label, int value) {
return new PatchSetApproval(
new PatchSetApproval.Key(change.currentPatchSetId(), accountId, new LabelId(label)),
(short) value,
TimeUtil.nowTs());
}
private PatchSetApproval copy(PatchSetApproval src, int newValue) {
PatchSetApproval result = new PatchSetApproval(src.getKey().getParentKey(), src);
result.setValue((short) newValue);
return result;
}
private static List<PatchSetApproval> list(PatchSetApproval... psas) {
return ImmutableList.<PatchSetApproval>copyOf(psas);
}
}

View File

@@ -0,0 +1,251 @@
// Copyright (C) 2015 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.server.git;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.TruthJUnit.assume;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.util.HostPlatform;
import com.google.gerrit.testing.TempFileUtil;
import com.google.gwtorm.client.KeyUtil;
import com.google.gwtorm.server.StandardKeyEncoder;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import org.easymock.EasyMockSupport;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryCache;
import org.eclipse.jgit.lib.RepositoryCache.FileKey;
import org.eclipse.jgit.util.FS;
import org.junit.Before;
import org.junit.Test;
public class LocalDiskRepositoryManagerTest extends EasyMockSupport {
static {
KeyUtil.setEncoderImpl(new StandardKeyEncoder());
}
private Config cfg;
private SitePaths site;
private LocalDiskRepositoryManager repoManager;
@Before
public void setUp() throws Exception {
site = new SitePaths(TempFileUtil.createTempDirectory().toPath());
site.resolve("git").toFile().mkdir();
cfg = new Config();
cfg.setString("gerrit", null, "basePath", "git");
repoManager = new LocalDiskRepositoryManager(site, cfg);
}
@Test(expected = IllegalStateException.class)
public void testThatNullBasePathThrowsAnException() {
new LocalDiskRepositoryManager(site, new Config());
}
@Test
public void projectCreation() throws Exception {
Project.NameKey projectA = new Project.NameKey("projectA");
try (Repository repo = repoManager.createRepository(projectA)) {
assertThat(repo).isNotNull();
}
try (Repository repo = repoManager.openRepository(projectA)) {
assertThat(repo).isNotNull();
}
assertThat(repoManager.list()).containsExactly(projectA);
}
@Test(expected = RepositoryNotFoundException.class)
public void testProjectCreationWithEmptyName() throws Exception {
repoManager.createRepository(new Project.NameKey(""));
}
@Test(expected = RepositoryNotFoundException.class)
public void testProjectCreationWithTrailingSlash() throws Exception {
repoManager.createRepository(new Project.NameKey("projectA/"));
}
@Test(expected = RepositoryNotFoundException.class)
public void testProjectCreationWithBackSlash() throws Exception {
repoManager.createRepository(new Project.NameKey("a\\projectA"));
}
@Test(expected = RepositoryNotFoundException.class)
public void testProjectCreationAbsolutePath() throws Exception {
repoManager.createRepository(new Project.NameKey("/projectA"));
}
@Test(expected = RepositoryNotFoundException.class)
public void testProjectCreationStartingWithDotDot() throws Exception {
repoManager.createRepository(new Project.NameKey("../projectA"));
}
@Test(expected = RepositoryNotFoundException.class)
public void testProjectCreationContainsDotDot() throws Exception {
repoManager.createRepository(new Project.NameKey("a/../projectA"));
}
@Test(expected = RepositoryNotFoundException.class)
public void testProjectCreationDotPathSegment() throws Exception {
repoManager.createRepository(new Project.NameKey("a/./projectA"));
}
@Test(expected = RepositoryNotFoundException.class)
public void testProjectCreationWithTwoSlashes() throws Exception {
repoManager.createRepository(new Project.NameKey("a//projectA"));
}
@Test(expected = RepositoryNotFoundException.class)
public void testProjectCreationWithPathSegmentEndingByDotGit() throws Exception {
repoManager.createRepository(new Project.NameKey("a/b.git/projectA"));
}
@Test(expected = RepositoryNotFoundException.class)
public void testProjectCreationWithQuestionMark() throws Exception {
repoManager.createRepository(new Project.NameKey("project?A"));
}
@Test(expected = RepositoryNotFoundException.class)
public void testProjectCreationWithPercentageSign() throws Exception {
repoManager.createRepository(new Project.NameKey("project%A"));
}
@Test(expected = RepositoryNotFoundException.class)
public void testProjectCreationWithWidlcard() throws Exception {
repoManager.createRepository(new Project.NameKey("project*A"));
}
@Test(expected = RepositoryNotFoundException.class)
public void testProjectCreationWithColon() throws Exception {
repoManager.createRepository(new Project.NameKey("project:A"));
}
@Test(expected = RepositoryNotFoundException.class)
public void testProjectCreationWithLessThatSign() throws Exception {
repoManager.createRepository(new Project.NameKey("project<A"));
}
@Test(expected = RepositoryNotFoundException.class)
public void testProjectCreationWithGreaterThatSign() throws Exception {
repoManager.createRepository(new Project.NameKey("project>A"));
}
@Test(expected = RepositoryNotFoundException.class)
public void testProjectCreationWithPipe() throws Exception {
repoManager.createRepository(new Project.NameKey("project|A"));
}
@Test(expected = RepositoryNotFoundException.class)
public void testProjectCreationWithDollarSign() throws Exception {
repoManager.createRepository(new Project.NameKey("project$A"));
}
@Test(expected = RepositoryNotFoundException.class)
public void testProjectCreationWithCarriageReturn() throws Exception {
repoManager.createRepository(new Project.NameKey("project\\rA"));
}
@Test(expected = IllegalStateException.class)
public void testProjectRecreation() throws Exception {
repoManager.createRepository(new Project.NameKey("a"));
repoManager.createRepository(new Project.NameKey("a"));
}
@Test(expected = IllegalStateException.class)
public void testProjectRecreationAfterRestart() throws Exception {
repoManager.createRepository(new Project.NameKey("a"));
LocalDiskRepositoryManager newRepoManager = new LocalDiskRepositoryManager(site, cfg);
newRepoManager.createRepository(new Project.NameKey("a"));
}
@Test
public void openRepositoryCreatedDirectlyOnDisk() throws Exception {
Project.NameKey projectA = new Project.NameKey("projectA");
createRepository(repoManager.getBasePath(projectA), projectA.get());
try (Repository repo = repoManager.openRepository(projectA)) {
assertThat(repo).isNotNull();
}
assertThat(repoManager.list()).containsExactly(projectA);
}
@Test(expected = RepositoryCaseMismatchException.class)
public void testNameCaseMismatch() throws Exception {
assume().that(HostPlatform.isWin32() || HostPlatform.isMac()).isTrue();
repoManager.createRepository(new Project.NameKey("a"));
repoManager.createRepository(new Project.NameKey("A"));
}
@Test(expected = RepositoryCaseMismatchException.class)
public void testNameCaseMismatchWithSymlink() throws Exception {
assume().that(HostPlatform.isWin32() || HostPlatform.isMac()).isTrue();
Project.NameKey name = new Project.NameKey("a");
repoManager.createRepository(name);
createSymLink(name, "b.git");
repoManager.createRepository(new Project.NameKey("B"));
}
@Test(expected = RepositoryCaseMismatchException.class)
public void testNameCaseMismatchAfterRestart() throws Exception {
assume().that(HostPlatform.isWin32() || HostPlatform.isMac()).isTrue();
Project.NameKey name = new Project.NameKey("a");
repoManager.createRepository(name);
LocalDiskRepositoryManager newRepoManager = new LocalDiskRepositoryManager(site, cfg);
newRepoManager.createRepository(new Project.NameKey("A"));
}
private void createSymLink(Project.NameKey project, String link) throws IOException {
Path base = repoManager.getBasePath(project);
Path projectDir = base.resolve(project.get() + ".git");
Path symlink = base.resolve(link);
Files.createSymbolicLink(symlink, projectDir);
}
@Test(expected = RepositoryNotFoundException.class)
public void testOpenRepositoryInvalidName() throws Exception {
repoManager.openRepository(new Project.NameKey("project%?|<>A"));
}
@Test
public void list() throws Exception {
Project.NameKey projectA = new Project.NameKey("projectA");
createRepository(repoManager.getBasePath(projectA), projectA.get());
Project.NameKey projectB = new Project.NameKey("path/projectB");
createRepository(repoManager.getBasePath(projectB), projectB.get());
Project.NameKey projectC = new Project.NameKey("anotherPath/path/projectC");
createRepository(repoManager.getBasePath(projectC), projectC.get());
// create an invalid git repo named only .git
repoManager.getBasePath(null).resolve(".git").toFile().mkdir();
// create an invalid repo name
createRepository(repoManager.getBasePath(null), "project?A");
assertThat(repoManager.list()).containsExactly(projectA, projectB, projectC);
}
private void createRepository(Path directory, String projectName) throws IOException {
String n = projectName + Constants.DOT_GIT_EXT;
FileKey loc = FileKey.exact(directory.resolve(n).toFile(), FS.DETECTED);
try (Repository db = RepositoryCache.open(loc, false)) {
db.create(true /* bare */);
}
}
}

View File

@@ -0,0 +1,164 @@
// Copyright (C) 2015 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.server.git;
import static com.google.common.truth.Truth.assertThat;
import static org.easymock.EasyMock.createNiceMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.reset;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.config.RepositoryConfig;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.testing.GerritBaseTests;
import com.google.gerrit.testing.TempFileUtil;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.SortedSet;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryCache;
import org.eclipse.jgit.lib.RepositoryCache.FileKey;
import org.eclipse.jgit.util.FS;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class MultiBaseLocalDiskRepositoryManagerTest extends GerritBaseTests {
private Config cfg;
private SitePaths site;
private MultiBaseLocalDiskRepositoryManager repoManager;
private RepositoryConfig configMock;
@Before
public void setUp() throws IOException {
site = new SitePaths(TempFileUtil.createTempDirectory().toPath());
site.resolve("git").toFile().mkdir();
cfg = new Config();
cfg.setString("gerrit", null, "basePath", "git");
configMock = createNiceMock(RepositoryConfig.class);
expect(configMock.getAllBasePaths()).andReturn(new ArrayList<Path>()).anyTimes();
replay(configMock);
repoManager = new MultiBaseLocalDiskRepositoryManager(site, cfg, configMock);
}
@After
public void tearDown() throws IOException {
TempFileUtil.cleanup();
}
@Test
public void defaultRepositoryLocation()
throws RepositoryCaseMismatchException, RepositoryNotFoundException, IOException {
Project.NameKey someProjectKey = new Project.NameKey("someProject");
Repository repo = repoManager.createRepository(someProjectKey);
assertThat(repo.getDirectory()).isNotNull();
assertThat(repo.getDirectory().exists()).isTrue();
assertThat(repo.getDirectory().getParent())
.isEqualTo(repoManager.getBasePath(someProjectKey).toAbsolutePath().toString());
repo = repoManager.openRepository(someProjectKey);
assertThat(repo.getDirectory()).isNotNull();
assertThat(repo.getDirectory().exists()).isTrue();
assertThat(repo.getDirectory().getParent())
.isEqualTo(repoManager.getBasePath(someProjectKey).toAbsolutePath().toString());
assertThat(repoManager.getBasePath(someProjectKey).toAbsolutePath().toString())
.isEqualTo(repoManager.getBasePath(someProjectKey).toAbsolutePath().toString());
SortedSet<Project.NameKey> repoList = repoManager.list();
assertThat(repoList.size()).isEqualTo(1);
assertThat(repoList.toArray(new Project.NameKey[repoList.size()]))
.isEqualTo(new Project.NameKey[] {someProjectKey});
}
@Test
public void alternateRepositoryLocation() throws IOException {
Path alternateBasePath = TempFileUtil.createTempDirectory().toPath();
Project.NameKey someProjectKey = new Project.NameKey("someProject");
reset(configMock);
expect(configMock.getBasePath(someProjectKey)).andReturn(alternateBasePath).anyTimes();
expect(configMock.getAllBasePaths()).andReturn(Arrays.asList(alternateBasePath)).anyTimes();
replay(configMock);
Repository repo = repoManager.createRepository(someProjectKey);
assertThat(repo.getDirectory()).isNotNull();
assertThat(repo.getDirectory().exists()).isTrue();
assertThat(repo.getDirectory().getParent()).isEqualTo(alternateBasePath.toString());
repo = repoManager.openRepository(someProjectKey);
assertThat(repo.getDirectory()).isNotNull();
assertThat(repo.getDirectory().exists()).isTrue();
assertThat(repo.getDirectory().getParent()).isEqualTo(alternateBasePath.toString());
assertThat(repoManager.getBasePath(someProjectKey).toAbsolutePath().toString())
.isEqualTo(alternateBasePath.toString());
SortedSet<Project.NameKey> repoList = repoManager.list();
assertThat(repoList.size()).isEqualTo(1);
assertThat(repoList.toArray(new Project.NameKey[repoList.size()]))
.isEqualTo(new Project.NameKey[] {someProjectKey});
}
@Test
public void listReturnRepoFromProperLocation() throws IOException {
Project.NameKey basePathProject = new Project.NameKey("basePathProject");
Project.NameKey altPathProject = new Project.NameKey("altPathProject");
Project.NameKey misplacedProject1 = new Project.NameKey("misplacedProject1");
Project.NameKey misplacedProject2 = new Project.NameKey("misplacedProject2");
Path alternateBasePath = TempFileUtil.createTempDirectory().toPath();
reset(configMock);
expect(configMock.getBasePath(altPathProject)).andReturn(alternateBasePath).anyTimes();
expect(configMock.getBasePath(misplacedProject2)).andReturn(alternateBasePath).anyTimes();
expect(configMock.getAllBasePaths()).andReturn(Arrays.asList(alternateBasePath)).anyTimes();
replay(configMock);
repoManager.createRepository(basePathProject);
repoManager.createRepository(altPathProject);
// create the misplaced ones without the repomanager otherwise they would
// end up at the proper place.
createRepository(repoManager.getBasePath(basePathProject), misplacedProject2);
createRepository(alternateBasePath, misplacedProject1);
SortedSet<Project.NameKey> repoList = repoManager.list();
assertThat(repoList.size()).isEqualTo(2);
assertThat(repoList.toArray(new Project.NameKey[repoList.size()]))
.isEqualTo(new Project.NameKey[] {altPathProject, basePathProject});
}
private void createRepository(Path directory, Project.NameKey projectName) throws IOException {
String n = projectName.get() + Constants.DOT_GIT_EXT;
FileKey loc = FileKey.exact(directory.resolve(n).toFile(), FS.DETECTED);
try (Repository db = RepositoryCache.open(loc, false)) {
db.create(true /* bare */);
}
}
@Test(expected = IllegalStateException.class)
public void testRelativeAlternateLocation() {
configMock = createNiceMock(RepositoryConfig.class);
expect(configMock.getAllBasePaths()).andReturn(Arrays.asList(Paths.get("repos"))).anyTimes();
replay(configMock);
repoManager = new MultiBaseLocalDiskRepositoryManager(site, cfg, configMock);
}
}

View File

@@ -0,0 +1,526 @@
// Copyright (C) 2011 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.server.git;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import com.google.common.collect.Iterables;
import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.ContributorAgreement;
import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.common.data.PermissionRule;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.config.PluginConfig;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gwtorm.client.KeyUtil;
import com.google.gwtorm.server.StandardKeyEncoder;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.util.RawParseUtils;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
public class ProjectConfigTest extends LocalDiskRepositoryTestCase {
private static final String LABEL_SCORES_CONFIG =
" copyMinScore = "
+ !LabelType.DEF_COPY_MIN_SCORE
+ "\n" //
+ " copyMaxScore = "
+ !LabelType.DEF_COPY_MAX_SCORE
+ "\n" //
+ " copyAllScoresOnMergeFirstParentUpdate = "
+ !LabelType.DEF_COPY_ALL_SCORES_ON_MERGE_FIRST_PARENT_UPDATE
+ "\n" //
+ " copyAllScoresOnTrivialRebase = "
+ !LabelType.DEF_COPY_ALL_SCORES_ON_TRIVIAL_REBASE
+ "\n" //
+ " copyAllScoresIfNoCodeChange = "
+ !LabelType.DEF_COPY_ALL_SCORES_IF_NO_CODE_CHANGE
+ "\n" //
+ " copyAllScoresIfNoChange = "
+ !LabelType.DEF_COPY_ALL_SCORES_IF_NO_CHANGE
+ "\n";
private final GroupReference developers =
new GroupReference(new AccountGroup.UUID("X"), "Developers");
private final GroupReference staff = new GroupReference(new AccountGroup.UUID("Y"), "Staff");
private Repository db;
private TestRepository<Repository> util;
@BeforeClass
public static void setUpOnce() {
KeyUtil.setEncoderImpl(new StandardKeyEncoder());
}
@Override
@Before
public void setUp() throws Exception {
super.setUp();
db = createBareRepository();
util = new TestRepository<>(db);
}
@Test
public void readConfig() throws Exception {
RevCommit rev =
util.commit(
util.tree( //
util.file("groups", util.blob(group(developers))), //
util.file(
"project.config",
util.blob(
"" //
+ "[access \"refs/heads/*\"]\n" //
+ " exclusiveGroupPermissions = read submit create\n" //
+ " submit = group Developers\n" //
+ " push = group Developers\n" //
+ " read = group Developers\n" //
+ "[accounts]\n" //
+ " sameGroupVisibility = deny group Developers\n" //
+ " sameGroupVisibility = block group Staff\n" //
+ "[contributor-agreement \"Individual\"]\n" //
+ " description = A simple description\n" //
+ " accepted = group Developers\n" //
+ " accepted = group Staff\n" //
+ " autoVerify = group Developers\n" //
+ " agreementUrl = http://www.example.com/agree\n")) //
));
ProjectConfig cfg = read(rev);
assertThat(cfg.getAccountsSection().getSameGroupVisibility()).hasSize(2);
ContributorAgreement ca = cfg.getContributorAgreement("Individual");
assertThat(ca.getName()).isEqualTo("Individual");
assertThat(ca.getDescription()).isEqualTo("A simple description");
assertThat(ca.getAgreementUrl()).isEqualTo("http://www.example.com/agree");
assertThat(ca.getAccepted()).hasSize(2);
assertThat(ca.getAccepted().get(0).getGroup()).isEqualTo(developers);
assertThat(ca.getAccepted().get(1).getGroup().getName()).isEqualTo("Staff");
assertThat(ca.getAutoVerify().getName()).isEqualTo("Developers");
AccessSection section = cfg.getAccessSection("refs/heads/*");
assertThat(section).isNotNull();
assertThat(cfg.getAccessSection("refs/*")).isNull();
Permission create = section.getPermission(Permission.CREATE);
Permission submit = section.getPermission(Permission.SUBMIT);
Permission read = section.getPermission(Permission.READ);
Permission push = section.getPermission(Permission.PUSH);
assertThat(create.getExclusiveGroup()).isTrue();
assertThat(submit.getExclusiveGroup()).isTrue();
assertThat(read.getExclusiveGroup()).isTrue();
assertThat(push.getExclusiveGroup()).isFalse();
}
@Test
public void readConfigLabelDefaultValue() throws Exception {
RevCommit rev =
util.commit(
util.tree( //
util.file("groups", util.blob(group(developers))), //
util.file(
"project.config",
util.blob(
"" //
+ "[label \"CustomLabel\"]\n" //
+ " value = -1 Negative\n" //
+ " value = 0 No Score\n" //
+ " value = 1 Positive\n")) //
));
ProjectConfig cfg = read(rev);
Map<String, LabelType> labels = cfg.getLabelSections();
Short dv = labels.entrySet().iterator().next().getValue().getDefaultValue();
assertThat((int) dv).isEqualTo(0);
}
@Test
public void readConfigLabelDefaultValueInRange() throws Exception {
RevCommit rev =
util.commit(
util.tree( //
util.file("groups", util.blob(group(developers))), //
util.file(
"project.config",
util.blob(
"" //
+ "[label \"CustomLabel\"]\n" //
+ " value = -1 Negative\n" //
+ " value = 0 No Score\n" //
+ " value = 1 Positive\n" //
+ " defaultValue = -1\n")) //
));
ProjectConfig cfg = read(rev);
Map<String, LabelType> labels = cfg.getLabelSections();
Short dv = labels.entrySet().iterator().next().getValue().getDefaultValue();
assertThat((int) dv).isEqualTo(-1);
}
@Test
public void readConfigLabelDefaultValueNotInRange() throws Exception {
RevCommit rev =
util.commit(
util.tree( //
util.file("groups", util.blob(group(developers))), //
util.file(
"project.config",
util.blob(
"" //
+ "[label \"CustomLabel\"]\n" //
+ " value = -1 Negative\n" //
+ " value = 0 No Score\n" //
+ " value = 1 Positive\n" //
+ " defaultValue = -2\n")) //
));
ProjectConfig cfg = read(rev);
assertThat(cfg.getValidationErrors()).hasSize(1);
assertThat(Iterables.getOnlyElement(cfg.getValidationErrors()).getMessage())
.isEqualTo("project.config: Invalid defaultValue \"-2\" for label \"CustomLabel\"");
}
@Test
public void readConfigLabelScores() throws Exception {
RevCommit rev =
util.commit(
util.tree( //
util.file("groups", util.blob(group(developers))), //
util.file(
"project.config",
util.blob(
"" //
+ "[label \"CustomLabel\"]\n" //
+ LABEL_SCORES_CONFIG)) //
));
ProjectConfig cfg = read(rev);
Map<String, LabelType> labels = cfg.getLabelSections();
LabelType type = labels.entrySet().iterator().next().getValue();
assertThat(type.isCopyMinScore()).isNotEqualTo(LabelType.DEF_COPY_MIN_SCORE);
assertThat(type.isCopyMaxScore()).isNotEqualTo(LabelType.DEF_COPY_MAX_SCORE);
assertThat(type.isCopyAllScoresOnMergeFirstParentUpdate())
.isNotEqualTo(LabelType.DEF_COPY_ALL_SCORES_ON_MERGE_FIRST_PARENT_UPDATE);
assertThat(type.isCopyAllScoresOnTrivialRebase())
.isNotEqualTo(LabelType.DEF_COPY_ALL_SCORES_ON_TRIVIAL_REBASE);
assertThat(type.isCopyAllScoresIfNoCodeChange())
.isNotEqualTo(LabelType.DEF_COPY_ALL_SCORES_IF_NO_CODE_CHANGE);
assertThat(type.isCopyAllScoresIfNoChange())
.isNotEqualTo(LabelType.DEF_COPY_ALL_SCORES_IF_NO_CHANGE);
}
@Test
public void editConfig() throws Exception {
RevCommit rev =
util.commit(
util.tree( //
util.file("groups", util.blob(group(developers))), //
util.file(
"project.config",
util.blob(
"" //
+ "[access \"refs/heads/*\"]\n" //
+ " exclusiveGroupPermissions = read submit\n" //
+ " submit = group Developers\n" //
+ " upload = group Developers\n" //
+ " read = group Developers\n" //
+ "[accounts]\n" //
+ " sameGroupVisibility = deny group Developers\n" //
+ " sameGroupVisibility = block group Staff\n" //
+ "[contributor-agreement \"Individual\"]\n" //
+ " description = A simple description\n" //
+ " accepted = group Developers\n" //
+ " autoVerify = group Developers\n" //
+ " agreementUrl = http://www.example.com/agree\n" //
+ "[label \"CustomLabel\"]\n" //
+ LABEL_SCORES_CONFIG)) //
));
update(rev);
ProjectConfig cfg = read(rev);
AccessSection section = cfg.getAccessSection("refs/heads/*");
cfg.getAccountsSection()
.setSameGroupVisibility(Collections.singletonList(new PermissionRule(cfg.resolve(staff))));
Permission submit = section.getPermission(Permission.SUBMIT);
submit.add(new PermissionRule(cfg.resolve(staff)));
ContributorAgreement ca = cfg.getContributorAgreement("Individual");
ca.setAccepted(Collections.singletonList(new PermissionRule(cfg.resolve(staff))));
ca.setAutoVerify(null);
ca.setDescription("A new description");
rev = commit(cfg);
assertThat(text(rev, "project.config"))
.isEqualTo(
"" //
+ "[access \"refs/heads/*\"]\n" //
+ " exclusiveGroupPermissions = read submit\n" //
+ " submit = group Developers\n" //
+ "\tsubmit = group Staff\n" //
+ " upload = group Developers\n" //
+ " read = group Developers\n" //
+ "[accounts]\n" //
+ " sameGroupVisibility = group Staff\n" //
+ "[contributor-agreement \"Individual\"]\n" //
+ " description = A new description\n" //
+ " accepted = group Staff\n" //
+ " agreementUrl = http://www.example.com/agree\n"
+ "[label \"CustomLabel\"]\n" //
+ LABEL_SCORES_CONFIG
+ "\tfunction = MaxWithBlock\n" // label gets this function when it is created
+ "\tdefaultValue = 0\n"); // label gets this value when it is created
}
@Test
public void editConfigMissingGroupTableEntry() throws Exception {
RevCommit rev =
util.commit(
util.tree( //
util.file("groups", util.blob(group(developers))), //
util.file(
"project.config",
util.blob(
"" //
+ "[access \"refs/heads/*\"]\n" //
+ " exclusiveGroupPermissions = read submit\n" //
+ " submit = group People Who Can Submit\n" //
+ " upload = group Developers\n" //
+ " read = group Developers\n")) //
));
update(rev);
ProjectConfig cfg = read(rev);
AccessSection section = cfg.getAccessSection("refs/heads/*");
Permission submit = section.getPermission(Permission.SUBMIT);
submit.add(new PermissionRule(cfg.resolve(staff)));
rev = commit(cfg);
assertThat(text(rev, "project.config"))
.isEqualTo(
"" //
+ "[access \"refs/heads/*\"]\n" //
+ " exclusiveGroupPermissions = read submit\n" //
+ " submit = group People Who Can Submit\n" //
+ "\tsubmit = group Staff\n" //
+ " upload = group Developers\n" //
+ " read = group Developers\n");
}
@Test
public void readExistingPluginConfig() throws Exception {
RevCommit rev =
util.commit(
util.tree( //
util.file(
"project.config",
util.blob(
"" //
+ "[plugin \"somePlugin\"]\n" //
+ " key1 = value1\n" //
+ " key2 = value2a\n"
+ " key2 = value2b\n")) //
));
update(rev);
ProjectConfig cfg = read(rev);
PluginConfig pluginCfg = cfg.getPluginConfig("somePlugin");
assertThat(pluginCfg.getNames().size()).isEqualTo(2);
assertThat(pluginCfg.getString("key1")).isEqualTo("value1");
assertThat(pluginCfg.getStringList(("key2"))).isEqualTo(new String[] {"value2a", "value2b"});
}
@Test
public void readUnexistingPluginConfig() throws Exception {
ProjectConfig cfg = new ProjectConfig(new Project.NameKey("test"));
cfg.load(db);
PluginConfig pluginCfg = cfg.getPluginConfig("somePlugin");
assertThat(pluginCfg.getNames()).isEmpty();
}
@Test
public void editPluginConfig() throws Exception {
RevCommit rev =
util.commit(
util.tree( //
util.file(
"project.config",
util.blob(
"" //
+ "[plugin \"somePlugin\"]\n" //
+ " key1 = value1\n" //
+ " key2 = value2a\n" //
+ " key2 = value2b\n")) //
));
update(rev);
ProjectConfig cfg = read(rev);
PluginConfig pluginCfg = cfg.getPluginConfig("somePlugin");
pluginCfg.setString("key1", "updatedValue1");
pluginCfg.setStringList("key2", Arrays.asList("updatedValue2a", "updatedValue2b"));
rev = commit(cfg);
assertThat(text(rev, "project.config"))
.isEqualTo(
"" //
+ "[plugin \"somePlugin\"]\n" //
+ "\tkey1 = updatedValue1\n" //
+ "\tkey2 = updatedValue2a\n" //
+ "\tkey2 = updatedValue2b\n");
}
@Test
public void readPluginConfigGroupReference() throws Exception {
RevCommit rev =
util.commit(
util.tree( //
util.file("groups", util.blob(group(developers))), //
util.file(
"project.config",
util.blob(
"" //
+ "[plugin \"somePlugin\"]\n" //
+ "key1 = "
+ developers.toConfigValue()
+ "\n")) //
));
update(rev);
ProjectConfig cfg = read(rev);
PluginConfig pluginCfg = cfg.getPluginConfig("somePlugin");
assertThat(pluginCfg.getNames().size()).isEqualTo(1);
assertThat(pluginCfg.getGroupReference("key1")).isEqualTo(developers);
}
@Test
public void readPluginConfigGroupReferenceNotInGroupsFile() throws Exception {
RevCommit rev =
util.commit(
util.tree( //
util.file("groups", util.blob(group(developers))), //
util.file(
"project.config",
util.blob(
"" //
+ "[plugin \"somePlugin\"]\n" //
+ "key1 = "
+ staff.toConfigValue()
+ "\n")) //
));
update(rev);
ProjectConfig cfg = read(rev);
assertThat(cfg.getValidationErrors()).hasSize(1);
assertThat(Iterables.getOnlyElement(cfg.getValidationErrors()).getMessage())
.isEqualTo(
"project.config: group \"" + staff.getName() + "\" not in " + GroupList.FILE_NAME);
}
@Test
public void editPluginConfigGroupReference() throws Exception {
RevCommit rev =
util.commit(
util.tree( //
util.file("groups", util.blob(group(developers))), //
util.file(
"project.config",
util.blob(
"" //
+ "[plugin \"somePlugin\"]\n" //
+ "key1 = "
+ developers.toConfigValue()
+ "\n")) //
));
update(rev);
ProjectConfig cfg = read(rev);
PluginConfig pluginCfg = cfg.getPluginConfig("somePlugin");
assertThat(pluginCfg.getNames().size()).isEqualTo(1);
assertThat(pluginCfg.getGroupReference("key1")).isEqualTo(developers);
pluginCfg.setGroupReference("key1", staff);
rev = commit(cfg);
assertThat(text(rev, "project.config"))
.isEqualTo(
"" //
+ "[plugin \"somePlugin\"]\n" //
+ "\tkey1 = "
+ staff.toConfigValue()
+ "\n");
assertThat(text(rev, "groups"))
.isEqualTo(
"# UUID\tGroup Name\n" //
+ "#\n" //
+ staff.getUUID().get()
+ " \t"
+ staff.getName()
+ "\n");
}
private ProjectConfig read(RevCommit rev) throws IOException, ConfigInvalidException {
ProjectConfig cfg = new ProjectConfig(new Project.NameKey("test"));
cfg.load(db, rev);
return cfg;
}
private RevCommit commit(ProjectConfig cfg)
throws IOException, MissingObjectException, IncorrectObjectTypeException {
try (MetaDataUpdate md =
new MetaDataUpdate(GitReferenceUpdated.DISABLED, cfg.getProject().getNameKey(), db)) {
util.tick(5);
util.setAuthorAndCommitter(md.getCommitBuilder());
md.setMessage("Edit\n");
cfg.commit(md);
Ref ref = db.exactRef(RefNames.REFS_CONFIG);
return util.getRevWalk().parseCommit(ref.getObjectId());
}
}
private void update(RevCommit rev) throws Exception {
RefUpdate u = db.updateRef(RefNames.REFS_CONFIG);
u.disableRefLog();
u.setNewObjectId(rev);
Result result = u.forceUpdate();
assertWithMessage("Cannot update ref for test: " + result)
.that(result)
.isAnyOf(Result.FAST_FORWARD, Result.FORCED, Result.NEW, Result.NO_CHANGE);
}
private String text(RevCommit rev, String path) throws Exception {
RevObject blob = util.get(rev.getTree(), path);
byte[] data = db.open(blob).getCachedBytes(Integer.MAX_VALUE);
return RawParseUtils.decode(data);
}
private static String group(GroupReference g) {
return g.getUUID().get() + "\t" + g.getName() + "\n";
}
}

View File

@@ -0,0 +1,119 @@
// Copyright (C) 2015 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.server.git;
import static com.google.common.truth.Truth.assertThat;
import static org.easymock.EasyMock.createNiceMock;
import static org.easymock.EasyMock.replay;
import java.io.IOException;
import junit.framework.TestCase;
import org.junit.Test;
public class QueryListTest extends TestCase {
public static final String Q_P = "project:foo";
public static final String Q_B = "branch:bar";
public static final String Q_COMPLEX = "branch:bar AND peers:'is:open\t'";
public static final String N_FOO = "foo";
public static final String N_BAR = "bar";
public static final String L_FOO = N_FOO + "\t" + Q_P + "\n";
public static final String L_BAR = N_BAR + "\t" + Q_B + "\n";
public static final String L_FOO_PROP = N_FOO + " \t" + Q_P + "\n";
public static final String L_BAR_PROP = N_BAR + " \t" + Q_B + "\n";
public static final String L_FOO_PAD_F = " " + N_FOO + "\t" + Q_P + "\n";
public static final String L_FOO_PAD_E = N_FOO + " \t" + Q_P + "\n";
public static final String L_BAR_PAD_F = N_BAR + "\t " + Q_B + "\n";
public static final String L_BAR_PAD_E = N_BAR + "\t" + Q_B + " \n";
public static final String L_COMPLEX = N_FOO + "\t" + Q_COMPLEX + "\t \n";
public static final String L_BAD = N_FOO + "\n";
public static final String HEADER = "# Name\tQuery\n";
public static final String C1 = "# A Simple Comment\n";
public static final String C2 = "# Comment with a tab\t and multi # # #\n";
public static final String F_SIMPLE = L_FOO + L_BAR;
public static final String F_PROPER = L_BAR_PROP + L_FOO_PROP; // alpha order
public static final String F_PAD_F = L_FOO_PAD_F + L_BAR_PAD_F;
public static final String F_PAD_E = L_FOO_PAD_E + L_BAR_PAD_E;
@Test
public void testParseSimple() throws Exception {
QueryList ql = QueryList.parse(F_SIMPLE, null);
assertThat(ql.getQuery(N_FOO)).isEqualTo(Q_P);
assertThat(ql.getQuery(N_BAR)).isEqualTo(Q_B);
}
@Test
public void testParseWHeader() throws Exception {
QueryList ql = QueryList.parse(HEADER + F_SIMPLE, null);
assertThat(ql.getQuery(N_FOO)).isEqualTo(Q_P);
assertThat(ql.getQuery(N_BAR)).isEqualTo(Q_B);
}
@Test
public void testParseWComments() throws Exception {
QueryList ql = QueryList.parse(C1 + F_SIMPLE + C2, null);
assertThat(ql.getQuery(N_FOO)).isEqualTo(Q_P);
assertThat(ql.getQuery(N_BAR)).isEqualTo(Q_B);
}
@Test
public void testParseFooComment() throws Exception {
QueryList ql = QueryList.parse("#" + L_FOO + L_BAR, null);
assertThat(ql.getQuery(N_FOO)).isNull();
assertThat(ql.getQuery(N_BAR)).isEqualTo(Q_B);
}
@Test
public void testParsePaddedFronts() throws Exception {
QueryList ql = QueryList.parse(F_PAD_F, null);
assertThat(ql.getQuery(N_FOO)).isEqualTo(Q_P);
assertThat(ql.getQuery(N_BAR)).isEqualTo(Q_B);
}
@Test
public void testParsePaddedEnds() throws Exception {
QueryList ql = QueryList.parse(F_PAD_E, null);
assertThat(ql.getQuery(N_FOO)).isEqualTo(Q_P);
assertThat(ql.getQuery(N_BAR)).isEqualTo(Q_B);
}
@Test
public void testParseComplex() throws Exception {
QueryList ql = QueryList.parse(L_COMPLEX, null);
assertThat(ql.getQuery(N_FOO)).isEqualTo(Q_COMPLEX);
}
@Test(expected = IOException.class)
public void testParseBad() throws Exception {
ValidationError.Sink sink = createNiceMock(ValidationError.Sink.class);
replay(sink);
QueryList.parse(L_BAD, sink);
}
@Test
public void testAsText() throws Exception {
String expectedText = HEADER + "#\n" + F_PROPER;
QueryList ql = QueryList.parse(F_SIMPLE, null);
String asText = ql.asText();
assertThat(asText).isEqualTo(expectedText);
ql = QueryList.parse(asText, null);
asText = ql.asText();
assertThat(asText).isEqualTo(expectedText);
}
}