Merge "Add named destinations support"
This commit is contained in:
32
Documentation/user-named-destinations.txt
Normal file
32
Documentation/user-named-destinations.txt
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
= Gerrit Code Review - Named Destinations
|
||||||
|
|
||||||
|
[[user-named-destinations]]
|
||||||
|
== User Named Destinations
|
||||||
|
It is possible to define named destination sets on a user level.
|
||||||
|
To do this, define the named destination sets in files named after
|
||||||
|
each destination set in the `destinations` directory of the user's
|
||||||
|
account ref in the `All-Users` project. The user's account ref is
|
||||||
|
based on the user's account id which is an integer. The account
|
||||||
|
refs are sharded by the last two digits (`+nn+`) in the refname,
|
||||||
|
leading to refs of the format `+refs/users/nn/accountid+`. The
|
||||||
|
user's destination files are a 2 column tab delimited file. Each
|
||||||
|
row in a destination file represents a single destination in the
|
||||||
|
named set. The left column represents the ref of the destination,
|
||||||
|
and the right column represents the project of the destination.
|
||||||
|
|
||||||
|
Example destination file named `destinations/myreviews`:
|
||||||
|
|
||||||
|
----
|
||||||
|
# Ref Project
|
||||||
|
#
|
||||||
|
refs/heads/master gerrit
|
||||||
|
refs/heads/stable-2.11 gerrit
|
||||||
|
refs/heads/master plugins/cookbook-plugin
|
||||||
|
----
|
||||||
|
|
||||||
|
GERRIT
|
||||||
|
------
|
||||||
|
Part of link:index.html[Gerrit Code Review]
|
||||||
|
|
||||||
|
SEARCHBOX
|
||||||
|
---------
|
||||||
@@ -88,6 +88,12 @@ Changes that conflict with change 'ID'. Change 'ID' can be specified
|
|||||||
as a legacy numerical 'ID' such as 15183, or a newer style Change-Id
|
as a legacy numerical 'ID' such as 15183, or a newer style Change-Id
|
||||||
that was scraped out of the commit message.
|
that was scraped out of the commit message.
|
||||||
|
|
||||||
|
[[destination]]
|
||||||
|
destination:'NAME'::
|
||||||
|
+
|
||||||
|
Changes which match the current user's destination named 'NAME'.
|
||||||
|
(see link:user-named-destinations.html[Named Destinations]).
|
||||||
|
|
||||||
[[owner]]
|
[[owner]]
|
||||||
owner:'USER', o:'USER'::
|
owner:'USER', o:'USER'::
|
||||||
+
|
+
|
||||||
|
|||||||
@@ -0,0 +1,80 @@
|
|||||||
|
// 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.account;
|
||||||
|
|
||||||
|
import com.google.gerrit.reviewdb.client.Account;
|
||||||
|
import com.google.gerrit.reviewdb.client.RefNames;
|
||||||
|
import com.google.gerrit.server.git.DestinationList;
|
||||||
|
import com.google.gerrit.server.git.ValidationError;
|
||||||
|
import com.google.gerrit.server.git.VersionedMetaData;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
|
import org.eclipse.jgit.lib.CommitBuilder;
|
||||||
|
import org.eclipse.jgit.lib.FileMode;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/** Preferences for user accounts. */
|
||||||
|
public class VersionedAccountDestinations extends VersionedMetaData {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(VersionedAccountDestinations.class);
|
||||||
|
|
||||||
|
public static VersionedAccountDestinations forUser(Account.Id id) {
|
||||||
|
return new VersionedAccountDestinations(RefNames.refsUsers(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String ref;
|
||||||
|
private final DestinationList destinations = new DestinationList();
|
||||||
|
|
||||||
|
private VersionedAccountDestinations(String ref) {
|
||||||
|
this.ref = ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getRefName() {
|
||||||
|
return ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DestinationList getDestinationList() {
|
||||||
|
return destinations;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onLoad() throws IOException, ConfigInvalidException {
|
||||||
|
String prefix = DestinationList.DIR_NAME + "/";
|
||||||
|
for (PathInfo p : getPathInfos(true)) {
|
||||||
|
if (p.fileMode == FileMode.REGULAR_FILE) {
|
||||||
|
String path = p.path;
|
||||||
|
if (path.startsWith(prefix)) {
|
||||||
|
String label = path.substring(prefix.length());
|
||||||
|
ValidationError.Sink errors = destinations.createLoggerSink(path, log);
|
||||||
|
destinations.parseLabel(label, readUTF8(path), errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValidationError.Sink createSink(String file) {
|
||||||
|
return ValidationError.createLoggerSink(file, log);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean onSave(CommitBuilder commit) throws IOException,
|
||||||
|
ConfigInvalidException {
|
||||||
|
throw new UnsupportedOperationException("Cannot yet save destinations");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
// 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 com.google.common.collect.HashMultimap;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.SetMultimap;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
import com.google.gerrit.reviewdb.client.Branch;
|
||||||
|
import com.google.gerrit.reviewdb.client.Project;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class DestinationList extends TabFile {
|
||||||
|
public static final String DIR_NAME = "destinations";
|
||||||
|
private SetMultimap<String, Branch.NameKey> destinations = HashMultimap.create();
|
||||||
|
|
||||||
|
public Set<Branch.NameKey> getDestinations(String label) {
|
||||||
|
return destinations.get(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void parseLabel(String label, String text,
|
||||||
|
ValidationError.Sink errors) throws IOException {
|
||||||
|
destinations.replaceValues(label,
|
||||||
|
toSet(parse(text, DIR_NAME + label, TRIM, null, errors)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String asText(String label) {
|
||||||
|
Set<Branch.NameKey> dests = destinations.get(label);
|
||||||
|
if (dests == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
List<Row> rows = Lists.newArrayListWithCapacity(dests.size());
|
||||||
|
for (Branch.NameKey dest : sort(dests)) {
|
||||||
|
rows.add(new Row(dest.get(), dest.getParentKey().get()));
|
||||||
|
}
|
||||||
|
return asText("Ref", "Project", rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static Set<Branch.NameKey> toSet(List<Row> destRows) {
|
||||||
|
Set<Branch.NameKey> dests = Sets.newHashSetWithExpectedSize(destRows.size());
|
||||||
|
for(Row row : destRows) {
|
||||||
|
dests.add(new Branch.NameKey(new Project.NameKey(row.right), row.left));
|
||||||
|
}
|
||||||
|
return dests;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -36,7 +36,7 @@ public class GroupList extends TabFile {
|
|||||||
|
|
||||||
public static GroupList parse(String text, ValidationError.Sink errors)
|
public static GroupList parse(String text, ValidationError.Sink errors)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
List<Row> rows = parse(text, FILE_NAME, errors);
|
List<Row> rows = parse(text, FILE_NAME, TRIM, TRIM, errors);
|
||||||
Map<AccountGroup.UUID, GroupReference> groupsByUUID =
|
Map<AccountGroup.UUID, GroupReference> groupsByUUID =
|
||||||
new HashMap<>(rows.size());
|
new HashMap<>(rows.size());
|
||||||
for(Row row : rows) {
|
for(Row row : rows) {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ public class QueryList extends TabFile {
|
|||||||
|
|
||||||
public static QueryList parse(String text, ValidationError.Sink errors)
|
public static QueryList parse(String text, ValidationError.Sink errors)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
return new QueryList(parse(text, FILE_NAME, errors));
|
return new QueryList(parse(text, FILE_NAME, TRIM, TRIM, errors));
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getQuery(String name) {
|
public String getQuery(String name) {
|
||||||
|
|||||||
@@ -27,6 +27,17 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class TabFile {
|
public class TabFile {
|
||||||
|
public interface Parser {
|
||||||
|
public String parse(String str);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Parser TRIM = new Parser() {
|
||||||
|
public String parse(String str) {
|
||||||
|
return str.trim();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
protected static class Row {
|
protected static class Row {
|
||||||
public String left;
|
public String left;
|
||||||
public String right;
|
public String right;
|
||||||
@@ -37,9 +48,9 @@ public class TabFile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static List<Row> parse(String text, String filename,
|
protected static List<Row> parse(String text, String filename, Parser left,
|
||||||
ValidationError.Sink errors) throws IOException {
|
Parser right, ValidationError.Sink errors) throws IOException {
|
||||||
List<Row> rows = new ArrayList<>();
|
List<Row> rows = new ArrayList<Row>();
|
||||||
BufferedReader br = new BufferedReader(new StringReader(text));
|
BufferedReader br = new BufferedReader(new StringReader(text));
|
||||||
String s;
|
String s;
|
||||||
for (int lineNumber = 1; (s = br.readLine()) != null; lineNumber++) {
|
for (int lineNumber = 1; (s = br.readLine()) != null; lineNumber++) {
|
||||||
@@ -54,8 +65,15 @@ public class TabFile {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
rows.add(new Row(s.substring(0, tab).trim(),
|
Row row = new Row(s.substring(0, tab), s.substring(tab + 1));
|
||||||
s.substring(tab + 1).trim()));
|
rows.add(row);
|
||||||
|
|
||||||
|
if (left != null) {
|
||||||
|
row.left = left.parse(row.left);
|
||||||
|
}
|
||||||
|
if (right != null) {
|
||||||
|
row.right = right.parse(row.right);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return rows;
|
return rows;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
package com.google.gerrit.server.git;
|
package com.google.gerrit.server.git;
|
||||||
|
|
||||||
import com.google.common.base.MoreObjects;
|
import com.google.common.base.MoreObjects;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
import org.eclipse.jgit.dircache.DirCache;
|
import org.eclipse.jgit.dircache.DirCache;
|
||||||
import org.eclipse.jgit.dircache.DirCacheBuilder;
|
import org.eclipse.jgit.dircache.DirCacheBuilder;
|
||||||
@@ -50,6 +51,7 @@ import java.io.BufferedReader;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Support for metadata stored within a version controlled branch.
|
* Support for metadata stored within a version controlled branch.
|
||||||
@@ -59,6 +61,23 @@ import java.util.Objects;
|
|||||||
* later be written back to the repository.
|
* later be written back to the repository.
|
||||||
*/
|
*/
|
||||||
public abstract class VersionedMetaData {
|
public abstract class VersionedMetaData {
|
||||||
|
/**
|
||||||
|
* Path information that does not hold references to any repository
|
||||||
|
* data structures, allowing the application to retain this object
|
||||||
|
* for long periods of time.
|
||||||
|
*/
|
||||||
|
public static class PathInfo {
|
||||||
|
public final FileMode fileMode;
|
||||||
|
public final String path;
|
||||||
|
public final ObjectId objectId;
|
||||||
|
|
||||||
|
protected PathInfo(TreeWalk tw) {
|
||||||
|
fileMode = tw.getFileMode(0);
|
||||||
|
path = tw.getPathString();
|
||||||
|
objectId = tw.getObjectId(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private RevCommit revision;
|
private RevCommit revision;
|
||||||
protected ObjectReader reader;
|
protected ObjectReader reader;
|
||||||
protected ObjectInserter inserter;
|
protected ObjectInserter inserter;
|
||||||
@@ -439,6 +458,17 @@ public abstract class VersionedMetaData {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<PathInfo> getPathInfos(boolean recursive) throws IOException {
|
||||||
|
TreeWalk tw = new TreeWalk(reader);
|
||||||
|
tw.addTree(revision.getTree());
|
||||||
|
tw.setRecursive(recursive);
|
||||||
|
List<PathInfo> paths = Lists.newArrayList();
|
||||||
|
while (tw.next()) {
|
||||||
|
paths.add(new PathInfo(tw));
|
||||||
|
}
|
||||||
|
return paths;
|
||||||
|
}
|
||||||
|
|
||||||
protected static void set(Config rc, String section, String subsection,
|
protected static void set(Config rc, String section, String subsection,
|
||||||
String name, String value) {
|
String name, String value) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import com.google.gerrit.common.data.GroupReference;
|
|||||||
import com.google.gerrit.common.errors.NotSignedInException;
|
import com.google.gerrit.common.errors.NotSignedInException;
|
||||||
import com.google.gerrit.reviewdb.client.Account;
|
import com.google.gerrit.reviewdb.client.Account;
|
||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||||
|
import com.google.gerrit.reviewdb.client.Branch;
|
||||||
import com.google.gerrit.reviewdb.client.Change;
|
import com.google.gerrit.reviewdb.client.Change;
|
||||||
import com.google.gerrit.reviewdb.client.RefNames;
|
import com.google.gerrit.reviewdb.client.RefNames;
|
||||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||||
@@ -34,6 +35,7 @@ import com.google.gerrit.server.account.CapabilityControl;
|
|||||||
import com.google.gerrit.server.account.GroupBackend;
|
import com.google.gerrit.server.account.GroupBackend;
|
||||||
import com.google.gerrit.server.account.GroupBackends;
|
import com.google.gerrit.server.account.GroupBackends;
|
||||||
import com.google.gerrit.server.account.VersionedAccountQueries;
|
import com.google.gerrit.server.account.VersionedAccountQueries;
|
||||||
|
import com.google.gerrit.server.account.VersionedAccountDestinations;
|
||||||
import com.google.gerrit.server.change.ChangeTriplet;
|
import com.google.gerrit.server.change.ChangeTriplet;
|
||||||
import com.google.gerrit.server.config.AllProjectsName;
|
import com.google.gerrit.server.config.AllProjectsName;
|
||||||
import com.google.gerrit.server.config.AllUsersName;
|
import com.google.gerrit.server.config.AllUsersName;
|
||||||
@@ -98,6 +100,7 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
|
|||||||
public static final String FIELD_CONFLICTS = "conflicts";
|
public static final String FIELD_CONFLICTS = "conflicts";
|
||||||
public static final String FIELD_DELETED = "deleted";
|
public static final String FIELD_DELETED = "deleted";
|
||||||
public static final String FIELD_DELTA = "delta";
|
public static final String FIELD_DELTA = "delta";
|
||||||
|
public static final String FIELD_DESTINATION = "destination";
|
||||||
public static final String FIELD_DRAFTBY = "draftby";
|
public static final String FIELD_DRAFTBY = "draftby";
|
||||||
public static final String FIELD_EDITBY = "editby";
|
public static final String FIELD_EDITBY = "editby";
|
||||||
public static final String FIELD_FILE = "file";
|
public static final String FIELD_FILE = "file";
|
||||||
@@ -818,6 +821,28 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
|
|||||||
return IsReviewedPredicate.create(args.getSchema(), parseAccount(who));
|
return IsReviewedPredicate.create(args.getSchema(), parseAccount(who));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Operator
|
||||||
|
public Predicate<ChangeData> destination(String name)
|
||||||
|
throws QueryParseException {
|
||||||
|
AllUsersName allUsers = args.allUsersName.get();
|
||||||
|
try (Repository git = args.repoManager.openRepository(allUsers)) {
|
||||||
|
VersionedAccountDestinations d =
|
||||||
|
VersionedAccountDestinations.forUser(self());
|
||||||
|
d.load(git);
|
||||||
|
Set<Branch.NameKey> destinations =
|
||||||
|
d.getDestinationList().getDestinations(name);
|
||||||
|
if (destinations != null) {
|
||||||
|
return new DestinationPredicate(destinations, name);
|
||||||
|
}
|
||||||
|
} catch (RepositoryNotFoundException e) {
|
||||||
|
throw new QueryParseException("Unknown named destination (no " +
|
||||||
|
allUsers.get() +" repo): " + name, e);
|
||||||
|
} catch (IOException | ConfigInvalidException e) {
|
||||||
|
throw new QueryParseException("Error parsing named destination: " + name, e);
|
||||||
|
}
|
||||||
|
throw new QueryParseException("Unknown named destination: " + name);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Predicate<ChangeData> defaultField(String query) throws QueryParseException {
|
protected Predicate<ChangeData> defaultField(String query) throws QueryParseException {
|
||||||
if (query.startsWith("refs/")) {
|
if (query.startsWith("refs/")) {
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
// 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.query.change;
|
||||||
|
|
||||||
|
import com.google.gerrit.reviewdb.client.Branch;
|
||||||
|
import com.google.gerrit.reviewdb.client.Change;
|
||||||
|
import com.google.gerrit.server.query.OperatorPredicate;
|
||||||
|
import com.google.gwtorm.server.OrmException;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
class DestinationPredicate extends OperatorPredicate<ChangeData> {
|
||||||
|
Set<Branch.NameKey> destinations;
|
||||||
|
|
||||||
|
DestinationPredicate(Set<Branch.NameKey> destinations, String value) {
|
||||||
|
super(ChangeQueryBuilder.FIELD_DESTINATION, value);
|
||||||
|
this.destinations = destinations;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean match(final ChangeData object) throws OrmException {
|
||||||
|
Change change = object.change();
|
||||||
|
if (change == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return destinations.contains(change.getDest());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCost() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.replay;
|
||||||
|
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
import com.google.gerrit.reviewdb.client.Branch;
|
||||||
|
import com.google.gerrit.reviewdb.client.Project;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
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 = Sets.newHashSet();
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user