Merge "Add support for tracing the execution time of operations"
diff --git a/Documentation/config-labels.txt b/Documentation/config-labels.txt
index 1aa6cd7c..ff43520 100644
--- a/Documentation/config-labels.txt
+++ b/Documentation/config-labels.txt
@@ -374,6 +374,15 @@
link:access-control.html#reference[here], but must not contain `${username}` or
`${shardeduserid}`.
+[[label_ignoreSelfApproval]]
+=== `label.Label-Name.ignoreSelfApproval`
+
+If true, the label may be voted on by the uploader of the latest patch set,
+but their approval does not make a change submittable. Instead, a
+non-uploader who has the right to vote has to approve the change.
+
+Defaults to false.
+
[[label_example]]
=== Example
diff --git a/Documentation/dev-contributing.txt b/Documentation/dev-contributing.txt
index cc0fe60..bc9f782 100644
--- a/Documentation/dev-contributing.txt
+++ b/Documentation/dev-contributing.txt
@@ -368,6 +368,36 @@
that they are vetted long enough before they go into a release and we can be sure
that the update doesn't introduce a regression.
+[[deprecating-features]]
+=== Deprecating features
+
+Gerrit should be as stable as possible and we aim to add only features that last.
+However, sometimes we are required to deprecate and remove features to be able
+to move forward with the project and keep the code-base clean. The following process
+should serve as a guideline on how to deprecate functionality in Gerrit. Its purpose
+is that we have a structured process for deprecation that users, administrators and
+developers can agree and rely on.
+
+General process:
+* Make sure that the feature (e.g. a field on the API) is not needed anymore or blocks
+ further development or improvement. If in doubt, consult the mailing list.
+* If you can provide a schema migration that moves users to a comparable feature, do
+ so and stop here.
+* Mark the feature as deprecated in the documentation and release notes.
+* If possible, mark the feature deprecated in any user-visible interface. For example,
+ if you are deprecating a Git push option, add a message to the Git response if
+ the user provided the option informing them about deprecation.
+* Annotate the code with `@Deprecated` and `@RemoveAfter(x.xx)` if applicable.
+ Alternatively, use `// DEPRECATED, remove after x.xx` (where x.xx is the version
+ number that has to be branched off before removing the feature)
+* Gate the feature behind a config that is off by default (forcing admins to turn
+ the deprecated feature on explicitly).
+* After the next release was branched off, remove any code that backed the feature.
+
+You can optionally consult the mailing list to ask if there are users of the feature you
+wish to deprecate. If there are no major users, you can remove the feature without
+following this process and without the grace period of one release.
+
GERRIT
------
Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/dev-eclipse.txt b/Documentation/dev-eclipse.txt
index 6e39502..0f23db5 100644
--- a/Documentation/dev-eclipse.txt
+++ b/Documentation/dev-eclipse.txt
@@ -3,7 +3,7 @@
This document is about configuring Gerrit Code Review into an
Eclipse workspace for development and debugging with GWT.
-Java 6 or later SDK is also required to run GWT's compiler and
+Java 8 or later SDK is also required to run GWT's compiler and
runtime debugging environment.
@@ -49,6 +49,19 @@
link:dev-build-plugins.html#_bundle_custom_plugin_in_release_war[bundling in release.war]
and run `tools/eclipse/project.py`.
+[[Newer Java versions]]
+
+Java 9 and later are supported, but some adjustments must be done, because
+Java 8 is still the default:
+
+* Add JRE, e.g.: directory: /usr/lib64/jvm/java-9-openjdk, name: java-9-openjdk-9
+* Change execution environemnt for gerrit project to: JavaSE-9 (java-9-openjdk-9)
+* Check that compiler compliance level in gerrit project is set to: 9
+* Add this parameter to VM argument for gerrit_daemin launcher:
+----
+ --add-modules java.activation \
+ --add-opens=jdk.management/com.sun.management.internal=ALL-UNNAMED
+----
[[Formatting]]
== Code Formatter Settings
diff --git a/Documentation/dev-release.txt b/Documentation/dev-release.txt
index 2afac94..0abd8a1 100644
--- a/Documentation/dev-release.txt
+++ b/Documentation/dev-release.txt
@@ -365,6 +365,17 @@
must reference the new version. Upload a change to bazlets repository with
api version upgrade.
+[[clean-up-on-master]]
+=== Clean up on master
+
+Once you are done with the release, check if there are any code changes in the
+master branch that were gated on the next release. Mostly, these are
+feature-deprecations that we were holding off on to have a stable release where
+the feature is still contained, but marked as deprecated.
+
+See link:dev-contributing.html#deprecating-features[Deprecating features] for
+details.
+
GERRIT
------
Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/rest-api.txt b/Documentation/rest-api.txt
index 97cca38..8f6a47b 100644
--- a/Documentation/rest-api.txt
+++ b/Documentation/rest-api.txt
@@ -210,6 +210,15 @@
GET /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/suggest_reviewers?trace&q=J
----
+Alternatively request tracing can also be enabled by setting the
+`X-Gerrit-Trace` header:
+
+.Example Request
+----
+ GET /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/suggest_reviewers?q=J
+ X-Gerrit-Trace: issue/123
+----
+
Enabling tracing results in additional logs with debug information that
are written to the `error_log`. All logs that correspond to the traced
request are associated with the trace ID. The trace ID is returned with
diff --git a/Documentation/user-request-tracing.txt b/Documentation/user-request-tracing.txt
index ac09d3c..8430e97 100644
--- a/Documentation/user-request-tracing.txt
+++ b/Documentation/user-request-tracing.txt
@@ -11,10 +11,10 @@
request type:
* REST API: For REST calls tracing can be enabled by setting the
- `trace` request parameter, the trace ID is returned as
- `X-Gerrit-Trace` header. More information about this can be found in
- the link:rest-api.html#tracing[Request Tracing] section of the
- link:rest-api.html[REST API documentation].
+ `trace` request parameter or the `X-Gerrit-Trace` header, the trace
+ ID is returned as `X-Gerrit-Trace` header. More information about
+ this can be found in the link:rest-api.html#tracing[Request Tracing]
+ section of the link:rest-api.html[REST API documentation].
* SSH API: For SSH calls tracing can be enabled by setting the
`--trace` option. More information about this can be found in
the link:cmd-index.html#trace[Trace] section of the
diff --git a/gerrit-acceptance-tests/tests.bzl b/gerrit-acceptance-tests/tests.bzl
deleted file mode 100644
index c1e34dd..0000000
--- a/gerrit-acceptance-tests/tests.bzl
+++ /dev/null
@@ -1,21 +0,0 @@
-load("//tools/bzl:junit.bzl", "junit_tests")
-
-def acceptance_tests(
- group,
- deps = [],
- labels = [],
- vm_args = ["-Xmx256m"],
- **kwargs):
- junit_tests(
- name = group,
- deps = deps + [
- "//gerrit-acceptance-tests:lib",
- ],
- tags = labels + [
- "acceptance",
- "slow",
- ],
- size = "large",
- jvm_flags = vm_args,
- **kwargs
- )
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/ChangeInfo.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/ChangeInfo.java
index 866d74f..0f786a6 100644
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/ChangeInfo.java
+++ b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/ChangeInfo.java
@@ -14,6 +14,8 @@
package com.google.gerrit.client.info;
+import static java.util.Comparator.comparing;
+
import com.google.gerrit.client.rpc.NativeMap;
import com.google.gerrit.client.rpc.NativeString;
import com.google.gerrit.client.rpc.Natives;
@@ -30,8 +32,6 @@
import com.google.gwtjsonrpc.client.impl.ser.JavaSqlTimestamp_JsonSerializer;
import java.sql.Timestamp;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -447,18 +447,8 @@
public static void sortRevisionInfoByNumber(JsArray<RevisionInfo> list) {
final int editParent = findEditParent(list);
- Collections.sort(
- Natives.asList(list),
- new Comparator<RevisionInfo>() {
- @Override
- public int compare(RevisionInfo a, RevisionInfo b) {
- return num(a) - num(b);
- }
-
- private int num(RevisionInfo r) {
- return !r.isEdit() ? 2 * (r._number() - 1) + 1 : 2 * editParent;
- }
- });
+ Natives.asList(list)
+ .sort(comparing(r -> !r.isEdit() ? 2 * (r._number() - 1) + 1 : 2 * editParent));
}
public static int findEditParent(JsArray<RevisionInfo> list) {
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/FileInfo.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/FileInfo.java
index 345a260..fc3dbf1 100644
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/FileInfo.java
+++ b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/FileInfo.java
@@ -19,7 +19,6 @@
import com.google.gerrit.reviewdb.client.Patch;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArray;
-import java.util.Collections;
import java.util.Comparator;
public class FileInfo extends JavaScriptObject {
@@ -55,8 +54,7 @@
public final native void _row(int r) /*-{ this._row = r }-*/;
public static void sortFileInfoByPath(JsArray<FileInfo> list) {
- Collections.sort(
- Natives.asList(list), Comparator.comparing(FileInfo::path, FilenameComparator.INSTANCE));
+ Natives.asList(list).sort(Comparator.comparing(FileInfo::path, FilenameComparator.INSTANCE));
}
public static String getFileName(String path) {
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/rpc/NativeMap.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/rpc/NativeMap.java
index 4b17068..41306ff 100644
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/rpc/NativeMap.java
+++ b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/rpc/NativeMap.java
@@ -14,11 +14,12 @@
package com.google.gerrit.client.rpc;
+import static java.util.stream.Collectors.toCollection;
+
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.user.client.rpc.AsyncCallback;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.Set;
@@ -57,10 +58,7 @@
}
public final List<String> sortedKeys() {
- Set<String> keys = keySet();
- List<String> sorted = new ArrayList<>(keys);
- Collections.sort(sorted);
- return sorted;
+ return keySet().stream().sorted().collect(toCollection(ArrayList::new));
}
public final native JsArray<T> values() /*-{
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyGpgKeysScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyGpgKeysScreen.java
index 0dc1dab..1a0090a 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyGpgKeysScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyGpgKeysScreen.java
@@ -14,6 +14,8 @@
package com.google.gerrit.client.account;
+import static java.util.Comparator.comparing;
+
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.info.GpgKeyInfo;
import com.google.gerrit.client.rpc.GerritCallback;
@@ -40,8 +42,6 @@
import com.google.gwtexpui.clippy.client.CopyableLabel;
import com.google.gwtexpui.globalkey.client.NpTextArea;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.List;
public class MyGpgKeysScreen extends SettingsScreen {
@@ -118,14 +118,7 @@
List<GpgKeyInfo> list = Natives.asList(result.values());
// TODO(dborowitz): Sort on something more meaningful, like
// created date?
- Collections.sort(
- list,
- new Comparator<GpgKeyInfo>() {
- @Override
- public int compare(GpgKeyInfo a, GpgKeyInfo b) {
- return a.id().compareTo(b.id());
- }
- });
+ list.sort(comparing(GpgKeyInfo::id));
keys.clear();
keyText.setText("");
errorPanel.setVisible(false);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyIdentitiesScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyIdentitiesScreen.java
index 5c6d40f..730d98e 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyIdentitiesScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyIdentitiesScreen.java
@@ -14,6 +14,8 @@
package com.google.gerrit.client.account;
+import static java.util.Comparator.naturalOrder;
+
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.VoidResult;
import com.google.gerrit.client.rpc.GerritCallback;
@@ -29,7 +31,6 @@
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
-import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -169,7 +170,7 @@
void display(JsArray<ExternalIdInfo> results) {
List<ExternalIdInfo> idList = Natives.asList(results);
- Collections.sort(idList);
+ idList.sort(naturalOrder());
while (1 < table.getRowCount()) {
table.removeRow(table.getRowCount() - 1);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.java
index 1b946cd..7bd8b82 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.java
@@ -14,6 +14,8 @@
package com.google.gerrit.client.admin;
+import static java.util.stream.Collectors.toCollection;
+
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.LabelType;
@@ -45,7 +47,6 @@
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.ValueListBox;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
public class AccessSectionEditor extends Composite
@@ -205,9 +206,8 @@
}
private void sortPermissions(AccessSection accessSection) {
- List<Permission> permissionList = new ArrayList<>(accessSection.getPermissions());
- Collections.sort(permissionList);
- accessSection.setPermissions(permissionList);
+ accessSection.setPermissions(
+ accessSection.getPermissions().stream().sorted().collect(toCollection(ArrayList::new)));
}
void setEditing(boolean editing) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupMembersScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupMembersScreen.java
index 2614224..6eaab5d 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupMembersScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupMembersScreen.java
@@ -14,6 +14,8 @@
package com.google.gerrit.client.admin;
+import static java.util.Comparator.comparing;
+
import com.google.gerrit.client.Dispatcher;
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.VoidResult;
@@ -29,7 +31,6 @@
import com.google.gerrit.client.ui.FancyFlexTable;
import com.google.gerrit.client.ui.Hyperlink;
import com.google.gerrit.client.ui.SmallHeading;
-import com.google.gerrit.common.Nullable;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
@@ -295,26 +296,9 @@
void insert(AccountInfo info) {
Comparator<AccountInfo> c =
- new Comparator<AccountInfo>() {
- @Override
- public int compare(AccountInfo a, AccountInfo b) {
- int cmp = nullToEmpty(a.name()).compareTo(nullToEmpty(b.name()));
- if (cmp != 0) {
- return cmp;
- }
-
- cmp = nullToEmpty(a.email()).compareTo(nullToEmpty(b.email()));
- if (cmp != 0) {
- return cmp;
- }
-
- return a._accountId() - b._accountId();
- }
-
- public String nullToEmpty(String str) {
- return str == null ? "" : str;
- }
- };
+ comparing((AccountInfo a) -> nullToEmpty(a.name()))
+ .thenComparing(a -> nullToEmpty(a.email()))
+ .thenComparing(AccountInfo::_accountId);
int insertPos = getInsertRow(c, info);
if (insertPos >= 0) {
table.insertRow(insertPos);
@@ -405,20 +389,7 @@
void insert(GroupInfo info) {
Comparator<GroupInfo> c =
- new Comparator<GroupInfo>() {
- @Override
- public int compare(GroupInfo a, GroupInfo b) {
- int cmp = nullToEmpty(a.name()).compareTo(nullToEmpty(b.name()));
- if (cmp != 0) {
- return cmp;
- }
- return a.getGroupUUID().compareTo(b.getGroupUUID());
- }
-
- private String nullToEmpty(@Nullable String str) {
- return (str == null) ? "" : str;
- }
- };
+ comparing((GroupInfo g) -> nullToEmpty(g.name())).thenComparing(GroupInfo::getGroupUUID);
int insertPos = getInsertRow(c, info);
if (insertPos >= 0) {
table.insertRow(insertPos);
@@ -457,4 +428,9 @@
setRowItem(row, i);
}
}
+
+ // Like Guava's Strings#nullToEmpty, which can't be used in GWT UI code.
+ private static String nullToEmpty(String str) {
+ return str == null ? "" : str;
+ }
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupTable.java
index 259847e..be0db41 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupTable.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupTable.java
@@ -14,6 +14,8 @@
package com.google.gerrit.client.admin;
+import static java.util.Comparator.comparing;
+
import com.google.gerrit.client.Dispatcher;
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.groups.GroupList;
@@ -32,8 +34,6 @@
import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
import com.google.gwt.user.client.ui.HTMLTable.Cell;
import com.google.gwt.user.client.ui.Image;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.List;
public class GroupTable extends NavigationTable<GroupInfo> {
@@ -105,14 +105,7 @@
table.removeRow(table.getRowCount() - 1);
}
- Collections.sort(
- list,
- new Comparator<GroupInfo>() {
- @Override
- public int compare(GroupInfo a, GroupInfo b) {
- return a.name().compareTo(b.name());
- }
- });
+ list.sort(comparing(GroupInfo::name));
for (GroupInfo group : list.subList(fromIndex, toIndex)) {
final int row = table.getRowCount();
table.insertRow(row);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Labels.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Labels.java
index 1f4820f..801a927 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Labels.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Labels.java
@@ -14,6 +14,10 @@
package com.google.gerrit.client.change;
+import static java.util.stream.Collectors.collectingAndThen;
+import static java.util.stream.Collectors.toCollection;
+import static java.util.stream.Collectors.toList;
+
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.changes.ChangeApi;
import com.google.gerrit.client.changes.Util;
@@ -135,9 +139,12 @@
}
void set(ChangeInfo info) {
- List<String> names = new ArrayList<>(info.labels());
+ List<String> names =
+ info.labels()
+ .stream()
+ .sorted()
+ .collect(collectingAndThen(toList(), Collections::unmodifiableList));
Set<Integer> removable = info.removableReviewerIds();
- Collections.sort(names);
resize(names.size(), 2);
@@ -197,8 +204,7 @@
}
private static List<Integer> sort(Set<Integer> keySet, int a, int b) {
- List<Integer> r = new ArrayList<>(keySet);
- Collections.sort(r);
+ List<Integer> r = keySet.stream().sorted().collect(toCollection(ArrayList::new));
if (keySet.contains(a)) {
r.remove(Integer.valueOf(a));
r.add(0, a);
@@ -238,31 +244,32 @@
Set<Integer> removable,
String label,
Map<Integer, VotableInfo> votable) {
- List<AccountInfo> users = new ArrayList<>(in);
- Collections.sort(
- users,
- new Comparator<AccountInfo>() {
- @Override
- public int compare(AccountInfo a, AccountInfo b) {
- String as = name(a);
- String bs = name(b);
- if (as.isEmpty()) {
- return 1;
- } else if (bs.isEmpty()) {
- return -1;
- }
- return as.compareTo(bs);
- }
+ List<AccountInfo> users =
+ in.stream()
+ .sorted(
+ new Comparator<AccountInfo>() {
+ @Override
+ public int compare(AccountInfo a, AccountInfo b) {
+ String as = name(a);
+ String bs = name(b);
+ if (as.isEmpty()) {
+ return 1;
+ } else if (bs.isEmpty()) {
+ return -1;
+ }
+ return as.compareTo(bs);
+ }
- private String name(AccountInfo a) {
- if (a.name() != null) {
- return a.name();
- } else if (a.email() != null) {
- return a.email();
- }
- return "";
- }
- });
+ private String name(AccountInfo a) {
+ if (a.name() != null) {
+ return a.name();
+ } else if (a.email() != null) {
+ return a.email();
+ }
+ return "";
+ }
+ })
+ .collect(collectingAndThen(toList(), Collections::unmodifiableList));
SafeHtmlBuilder html = new SafeHtmlBuilder();
Iterator<? extends AccountInfo> itr = users.iterator();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.java
index 0bbd614..8a1a2d5 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.java
@@ -16,6 +16,8 @@
import static com.google.gwt.event.dom.client.KeyCodes.KEY_ENTER;
import static com.google.gwt.event.dom.client.KeyCodes.KEY_MAC_ENTER;
+import static java.util.stream.Collectors.collectingAndThen;
+import static java.util.stream.Collectors.toList;
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.changes.ChangeApi;
@@ -123,11 +125,15 @@
this.lc = new LocalComments(project, psId.getParentKey());
initWidget(uiBinder.createAndBindUi(this));
- List<String> names = new ArrayList<>(permitted.keySet());
+ List<String> names =
+ permitted
+ .keySet()
+ .stream()
+ .sorted()
+ .collect(collectingAndThen(toList(), Collections::unmodifiableList));
if (names.isEmpty()) {
UIObject.setVisible(labelsParent, false);
} else {
- Collections.sort(names);
renderLabels(names, all, permitted);
}
@@ -439,8 +445,11 @@
clp, project, psId, Util.C.commitMessage(), copyPath(Patch.MERGE_LIST, l)));
}
- List<String> paths = new ArrayList<>(m.keySet());
- Collections.sort(paths);
+ List<String> paths =
+ m.keySet()
+ .stream()
+ .sorted()
+ .collect(collectingAndThen(toList(), Collections::unmodifiableList));
for (String path : paths) {
if (!Patch.isMagic(path)) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AccountDashboardScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AccountDashboardScreen.java
index c6e4e2f..7ec1102 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AccountDashboardScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AccountDashboardScreen.java
@@ -14,6 +14,8 @@
package com.google.gerrit.client.changes;
+import static java.util.Comparator.comparing;
+
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.NotFoundScreen;
import com.google.gerrit.client.info.ChangeInfo;
@@ -176,7 +178,7 @@
}
}
- Collections.sort(Natives.asList(out), outComparator());
+ Natives.asList(out).sort(outComparator());
table.updateColumnsForLabels(wip, out, in, done);
workInProgress.display(wip);
@@ -187,16 +189,7 @@
}
private Comparator<ChangeInfo> outComparator() {
- return new Comparator<ChangeInfo>() {
- @Override
- public int compare(ChangeInfo a, ChangeInfo b) {
- int cmp = a.created().compareTo(b.created());
- if (cmp != 0) {
- return cmp;
- }
- return a._number() - b._number();
- }
- };
+ return comparing(ChangeInfo::created).thenComparing(ChangeInfo::_number);
}
private boolean hasChanges(JsArray<ChangeList> result) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable.java
index caea87e..4fda78b 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable.java
@@ -16,11 +16,13 @@
import static com.google.gerrit.client.FormatUtil.relativeFormat;
import static com.google.gerrit.client.FormatUtil.shortFormat;
+import static java.util.stream.Collectors.toList;
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.info.AccountInfo;
import com.google.gerrit.client.info.ChangeInfo;
import com.google.gerrit.client.info.ChangeInfo.LabelInfo;
+import com.google.gerrit.client.rpc.Natives;
import com.google.gerrit.client.ui.AccountLinkPanel;
import com.google.gerrit.client.ui.BranchLink;
import com.google.gerrit.client.ui.ChangeLink;
@@ -45,6 +47,7 @@
import com.google.gwt.user.client.ui.UIObject;
import com.google.gwt.user.client.ui.Widget;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
@@ -185,17 +188,13 @@
}
public void updateColumnsForLabels(ChangeList... lists) {
- labelNames = new ArrayList<>();
- for (ChangeList list : lists) {
- for (int i = 0; i < list.length(); i++) {
- for (String name : list.get(i).labels()) {
- if (!labelNames.contains(name)) {
- labelNames.add(name);
- }
- }
- }
- }
- Collections.sort(labelNames);
+ labelNames =
+ Arrays.stream(lists)
+ .flatMap(l -> Natives.asList(l).stream())
+ .flatMap(c -> c.labels().stream())
+ .distinct()
+ .sorted()
+ .collect(toList());
int baseColumns = BASE_COLUMNS;
if (baseColumns + labelNames.size() < columns) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/dashboards/DashboardsTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/dashboards/DashboardsTable.java
index 0e4ef4e..dcb9c01 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/dashboards/DashboardsTable.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/dashboards/DashboardsTable.java
@@ -14,6 +14,8 @@
package com.google.gerrit.client.dashboards;
+import static java.util.Comparator.comparing;
+
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.rpc.Natives;
import com.google.gerrit.client.ui.NavigationTable;
@@ -25,8 +27,6 @@
import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
import com.google.gwt.user.client.ui.Image;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -75,14 +75,7 @@
table.removeRow(table.getRowCount() - 1);
}
- Collections.sort(
- list,
- new Comparator<DashboardInfo>() {
- @Override
- public int compare(DashboardInfo a, DashboardInfo b) {
- return a.id().compareTo(b.id());
- }
- });
+ list.sort(comparing(DashboardInfo::id));
String ref = null;
for (DashboardInfo d : list) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentsCollections.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentsCollections.java
index 533b745..2698584 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentsCollections.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentsCollections.java
@@ -14,6 +14,8 @@
package com.google.gerrit.client.diff;
+import static java.util.Comparator.comparing;
+
import com.google.gerrit.client.DiffObject;
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.changes.CommentApi;
@@ -27,8 +29,6 @@
import com.google.gerrit.reviewdb.client.Project;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.user.client.rpc.AsyncCallback;
-import java.util.Collections;
-import java.util.Comparator;
/** Collection of published and draft comments loaded from the server. */
class CommentsCollections {
@@ -158,14 +158,7 @@
for (CommentInfo c : Natives.asList(in)) {
c.path(path);
}
- Collections.sort(
- Natives.asList(in),
- new Comparator<CommentInfo>() {
- @Override
- public int compare(CommentInfo a, CommentInfo b) {
- return a.updated().compareTo(b.updated());
- }
- });
+ Natives.asList(in).sort(comparing(CommentInfo::updated));
}
return in;
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedChunkManager.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedChunkManager.java
index 1a662e2..98ad023 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedChunkManager.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedChunkManager.java
@@ -14,6 +14,8 @@
package com.google.gerrit.client.diff;
+import static java.util.Comparator.comparing;
+
import com.google.gerrit.client.diff.DiffInfo.Region;
import com.google.gerrit.client.diff.DiffInfo.Span;
import com.google.gerrit.client.rpc.Natives;
@@ -227,12 +229,7 @@
/** Diff chunks are ordered by their starting lines in CodeMirror */
private Comparator<UnifiedDiffChunkInfo> getDiffChunkComparatorCmLine() {
- return new Comparator<UnifiedDiffChunkInfo>() {
- @Override
- public int compare(UnifiedDiffChunkInfo o1, UnifiedDiffChunkInfo o2) {
- return o1.getCmLine() - o2.getCmLine();
- }
- };
+ return comparing(UnifiedDiffChunkInfo::getCmLine);
}
@Override
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectsTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectsTable.java
index ac89180..3576b12 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectsTable.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectsTable.java
@@ -14,14 +14,14 @@
package com.google.gerrit.client.ui;
+import static java.util.Comparator.comparing;
+
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.projects.ProjectInfo;
import com.google.gerrit.client.projects.ProjectMap;
import com.google.gerrit.client.rpc.Natives;
import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
import com.google.gwt.user.client.ui.Image;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.List;
public class ProjectsTable extends NavigationTable<ProjectInfo> {
@@ -69,14 +69,7 @@
}
List<ProjectInfo> list = Natives.asList(projects.values());
- Collections.sort(
- list,
- new Comparator<ProjectInfo>() {
- @Override
- public int compare(ProjectInfo a, ProjectInfo b) {
- return a.name().compareTo(b.name());
- }
- });
+ list.sort(comparing(ProjectInfo::name));
for (ProjectInfo p : list.subList(fromIndex, toIndex)) {
insert(table.getRowCount(), p);
}
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/mode/ModeInfo.java b/gerrit-gwtui/src/main/java/net/codemirror/mode/ModeInfo.java
index c6f113e..9df066d 100644
--- a/gerrit-gwtui/src/main/java/net/codemirror/mode/ModeInfo.java
+++ b/gerrit-gwtui/src/main/java/net/codemirror/mode/ModeInfo.java
@@ -14,6 +14,8 @@
package net.codemirror.mode;
+import static java.util.Comparator.comparing;
+
import com.google.gerrit.client.rpc.NativeMap;
import com.google.gerrit.client.rpc.Natives;
import com.google.gwt.core.client.JavaScriptObject;
@@ -21,8 +23,6 @@
import com.google.gwt.core.client.JsArrayString;
import com.google.gwt.resources.client.DataResource;
import com.google.gwt.safehtml.shared.SafeUri;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
@@ -242,14 +242,7 @@
byMime.put(m.mode(), m);
}
}
- Collections.sort(
- Natives.asList(filtered),
- new Comparator<ModeInfo>() {
- @Override
- public int compare(ModeInfo a, ModeInfo b) {
- return a.name().toLowerCase().compareTo(b.name().toLowerCase());
- }
- });
+ Natives.asList(filtered).sort(comparing(m -> m.name().toLowerCase()));
setAll(filtered);
}
diff --git a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index 1d87880..69d603f 100644
--- a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -1487,12 +1487,7 @@
assertNotifyTo(expected.email, expected.fullName);
}
- protected void assertNotifyTo(
- com.google.gerrit.acceptance.testsuite.account.TestAccount expected) {
- assertNotifyTo(expected.preferredEmail().orElse(null), expected.fullname().orElse(null));
- }
-
- private void assertNotifyTo(String expectedEmail, String expectedFullname) {
+ protected void assertNotifyTo(String expectedEmail, String expectedFullname) {
Address expectedAddress = new Address(expectedFullname, expectedEmail);
assertThat(sender.getMessages()).hasSize(1);
Message m = sender.getMessages().get(0);
@@ -1506,11 +1501,6 @@
assertNotifyCc(expected.emailAddress);
}
- protected void assertNotifyCc(
- com.google.gerrit.acceptance.testsuite.account.TestAccount expected) {
- assertNotifyCc(expected.preferredEmail().orElse(null), expected.fullname().orElse(null));
- }
-
protected void assertNotifyCc(String expectedEmail, String expectedFullname) {
Address expectedAddress = new Address(expectedFullname, expectedEmail);
assertNotifyCc(expectedAddress);
@@ -1533,13 +1523,10 @@
assertThat(m.headers().get("Cc").isEmpty()).isTrue();
}
- protected void assertNotifyBcc(
- com.google.gerrit.acceptance.testsuite.account.TestAccount expected) {
+ protected void assertNotifyBcc(String expectedEmail, String expectedFullName) {
assertThat(sender.getMessages()).hasSize(1);
Message m = sender.getMessages().get(0);
- assertThat(m.rcpt())
- .containsExactly(
- new Address(expected.fullname().orElse(null), expected.preferredEmail().orElse(null)));
+ assertThat(m.rcpt()).containsExactly(new Address(expectedFullName, expectedEmail));
assertThat(m.headers().get("To").isEmpty()).isTrue();
assertThat(m.headers().get("Cc").isEmpty()).isTrue();
}
diff --git a/java/com/google/gerrit/acceptance/GerritServer.java b/java/com/google/gerrit/acceptance/GerritServer.java
index 6e5424c..582c7cb 100644
--- a/java/com/google/gerrit/acceptance/GerritServer.java
+++ b/java/com/google/gerrit/acceptance/GerritServer.java
@@ -24,6 +24,8 @@
import com.google.common.collect.ImmutableList;
import com.google.gerrit.acceptance.testsuite.account.AccountOperations;
import com.google.gerrit.acceptance.testsuite.account.AccountOperationsImpl;
+import com.google.gerrit.acceptance.testsuite.group.GroupOperations;
+import com.google.gerrit.acceptance.testsuite.group.GroupOperationsImpl;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.config.FactoryModule;
import com.google.gerrit.lucene.LuceneIndexModule;
@@ -441,6 +443,7 @@
bindConstant().annotatedWith(SshEnabled.class).to(daemon.getEnableSshd());
bind(AccountCreator.class);
bind(AccountOperations.class).to(AccountOperationsImpl.class);
+ bind(GroupOperations.class).to(GroupOperationsImpl.class);
factory(PushOneCommit.Factory.class);
install(InProcessProtocol.module());
install(new NoSshModule());
diff --git a/java/com/google/gerrit/acceptance/HttpResponse.java b/java/com/google/gerrit/acceptance/HttpResponse.java
index b62e932..3e98d71 100644
--- a/java/com/google/gerrit/acceptance/HttpResponse.java
+++ b/java/com/google/gerrit/acceptance/HttpResponse.java
@@ -14,11 +14,16 @@
package com.google.gerrit.acceptance;
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.ByteBuffer;
+import java.util.Arrays;
import org.apache.http.Header;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.RawParseUtils;
@@ -34,7 +39,7 @@
public Reader getReader() throws IllegalStateException, IOException {
if (reader == null && response.getEntity() != null) {
- reader = new InputStreamReader(response.getEntity().getContent());
+ reader = new InputStreamReader(response.getEntity().getContent(), UTF_8);
}
return reader;
}
@@ -59,6 +64,13 @@
return hdr != null ? hdr.getValue() : null;
}
+ public ImmutableList<String> getHeaders(String name) {
+ return Arrays.asList(response.getHeaders(name))
+ .stream()
+ .map(Header::getValue)
+ .collect(toImmutableList());
+ }
+
public boolean hasContent() {
Preconditions.checkNotNull(response, "Response is not initialized.");
return response.getEntity() != null;
diff --git a/java/com/google/gerrit/acceptance/SshSession.java b/java/com/google/gerrit/acceptance/SshSession.java
index 27dae3b..52d7f28 100644
--- a/java/com/google/gerrit/acceptance/SshSession.java
+++ b/java/com/google/gerrit/acceptance/SshSession.java
@@ -17,6 +17,7 @@
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
+import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.gerrit.acceptance.testsuite.account.TestSshKeys;
import com.jcraft.jsch.ChannelExec;
@@ -54,10 +55,10 @@
InputStream err = channel.getErrStream();
channel.connect();
- Scanner s = new Scanner(err).useDelimiter("\\A");
+ Scanner s = new Scanner(err, UTF_8.name()).useDelimiter("\\A");
error = s.hasNext() ? s.next() : null;
- s = new Scanner(in).useDelimiter("\\A");
+ s = new Scanner(in, UTF_8.name()).useDelimiter("\\A");
return s.hasNext() ? s.next() : "";
} finally {
channel.disconnect();
diff --git a/java/com/google/gerrit/acceptance/testsuite/account/AccountOperations.java b/java/com/google/gerrit/acceptance/testsuite/account/AccountOperations.java
index 58a00d0..61b7599 100644
--- a/java/com/google/gerrit/acceptance/testsuite/account/AccountOperations.java
+++ b/java/com/google/gerrit/acceptance/testsuite/account/AccountOperations.java
@@ -42,7 +42,7 @@
* <p>Example:
*
* <pre>
- * TestAccount createdAccount = accountOperations
+ * Account.Id createdAccountId = accountOperations
* .newAccount()
* .username("janedoe")
* .preferredEmail("janedoe@example.com")
diff --git a/java/com/google/gerrit/acceptance/testsuite/account/AccountOperationsImpl.java b/java/com/google/gerrit/acceptance/testsuite/account/AccountOperationsImpl.java
index 94b511b..ebbcfe4 100644
--- a/java/com/google/gerrit/acceptance/testsuite/account/AccountOperationsImpl.java
+++ b/java/com/google/gerrit/acceptance/testsuite/account/AccountOperationsImpl.java
@@ -59,12 +59,12 @@
return TestAccountCreation.builder(this::createAccount);
}
- private TestAccount createAccount(TestAccountCreation accountCreation) throws Exception {
+ private Account.Id createAccount(TestAccountCreation accountCreation) throws Exception {
AccountsUpdate.AccountUpdater accountUpdater =
(account, updateBuilder) ->
fillBuilder(updateBuilder, accountCreation, account.getAccount().getId());
AccountState createdAccount = createAccount(accountUpdater);
- return toTestAccount(createdAccount);
+ return createdAccount.getAccount().getId();
}
private AccountState createAccount(AccountsUpdate.AccountUpdater accountUpdater)
@@ -85,17 +85,6 @@
accountCreation.active().ifPresent(builder::setActive);
}
- private static TestAccount toTestAccount(AccountState accountState) {
- Account createdAccount = accountState.getAccount();
- return TestAccount.builder()
- .accountId(createdAccount.getId())
- .preferredEmail(Optional.ofNullable(createdAccount.getPreferredEmail()))
- .fullname(Optional.ofNullable(createdAccount.getFullName()))
- .username(accountState.getUserName())
- .active(accountState.getAccount().isActive())
- .build();
- }
-
private static InternalAccountUpdate.Builder setPreferredEmail(
InternalAccountUpdate.Builder builder, Account.Id accountId, String preferredEmail) {
return builder
@@ -133,6 +122,17 @@
return toTestAccount(account);
}
+ private TestAccount toTestAccount(AccountState accountState) {
+ Account account = accountState.getAccount();
+ return TestAccount.builder()
+ .accountId(account.getId())
+ .preferredEmail(Optional.ofNullable(account.getPreferredEmail()))
+ .fullname(Optional.ofNullable(account.getFullName()))
+ .username(accountState.getUserName())
+ .active(accountState.getAccount().isActive())
+ .build();
+ }
+
@Override
public TestAccountUpdate.Builder forUpdate() {
return TestAccountUpdate.builder(this::updateAccount);
diff --git a/java/com/google/gerrit/acceptance/testsuite/account/TestAccountCreation.java b/java/com/google/gerrit/acceptance/testsuite/account/TestAccountCreation.java
index a82d180..ab32409 100644
--- a/java/com/google/gerrit/acceptance/testsuite/account/TestAccountCreation.java
+++ b/java/com/google/gerrit/acceptance/testsuite/account/TestAccountCreation.java
@@ -16,6 +16,7 @@
import com.google.auto.value.AutoValue;
import com.google.gerrit.acceptance.testsuite.ThrowingFunction;
+import com.google.gerrit.reviewdb.client.Account;
import java.util.Optional;
@AutoValue
@@ -32,9 +33,9 @@
public abstract Optional<Boolean> active();
- abstract ThrowingFunction<TestAccountCreation, TestAccount> accountCreator();
+ abstract ThrowingFunction<TestAccountCreation, Account.Id> accountCreator();
- public static Builder builder(ThrowingFunction<TestAccountCreation, TestAccount> accountCreator) {
+ public static Builder builder(ThrowingFunction<TestAccountCreation, Account.Id> accountCreator) {
return new AutoValue_TestAccountCreation.Builder()
.accountCreator(accountCreator)
.httpPassword("http-pass");
@@ -83,11 +84,11 @@
}
abstract Builder accountCreator(
- ThrowingFunction<TestAccountCreation, TestAccount> accountCreator);
+ ThrowingFunction<TestAccountCreation, Account.Id> accountCreator);
abstract TestAccountCreation autoBuild();
- public TestAccount create() throws Exception {
+ public Account.Id create() throws Exception {
TestAccountCreation accountUpdate = autoBuild();
return accountUpdate.accountCreator().apply(accountUpdate);
}
diff --git a/java/com/google/gerrit/acceptance/testsuite/group/GroupOperations.java b/java/com/google/gerrit/acceptance/testsuite/group/GroupOperations.java
new file mode 100644
index 0000000..f75ca2e
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/testsuite/group/GroupOperations.java
@@ -0,0 +1,97 @@
+// Copyright (C) 2018 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.acceptance.testsuite.group;
+
+import com.google.gerrit.reviewdb.client.AccountGroup;
+
+/**
+ * An aggregation of operations on groups for test purposes.
+ *
+ * <p>To execute the operations, no Gerrit permissions are necessary.
+ *
+ * <p><strong>Note:</strong> This interface is not implemented using the REST or extension API.
+ * Hence, it cannot be used for testing those APIs.
+ */
+public interface GroupOperations {
+ /**
+ * Starts the fluent chain for querying or modifying a group. Please see the methods of {@link
+ * MoreGroupOperations} for details on possible operations.
+ *
+ * @return an aggregation of operations on a specific group
+ */
+ MoreGroupOperations group(AccountGroup.UUID groupUuid);
+
+ /**
+ * Starts the fluent chain to create a group. The returned builder can be used to specify the
+ * attributes of the new group. To create the group for real, {@link
+ * TestGroupCreation.Builder#create()} must be called.
+ *
+ * <p>Example:
+ *
+ * <pre>
+ * AccountGroup.UUID createdGroupUuid = groupOperations
+ * .newGroup()
+ * .name("verifiers")
+ * .description("All verifiers of this server")
+ * .create();
+ * </pre>
+ *
+ * <p><strong>Note:</strong> If another group with the provided name already exists, the creation
+ * of the group will fail.
+ *
+ * @return a builder to create the new group
+ */
+ TestGroupCreation.Builder newGroup();
+
+ /** An aggregation of methods on a specific group. */
+ interface MoreGroupOperations {
+
+ /**
+ * Checks whether the group exists.
+ *
+ * @return {@code true} if the group exists
+ */
+ boolean exists() throws Exception;
+
+ /**
+ * Retrieves the group.
+ *
+ * <p><strong>Note:</strong> This call will fail with an exception if the requested group
+ * doesn't exist. If you want to check for the existence of a group, use {@link #exists()}
+ * instead.
+ *
+ * @return the corresponding {@code TestGroup}
+ */
+ TestGroup get() throws Exception;
+
+ /**
+ * Starts the fluent chain to update a group. The returned builder can be used to specify how
+ * the attributes of the group should be modified. To update the group for real, {@link
+ * TestGroupUpdate.Builder#update()} must be called.
+ *
+ * <p>Example:
+ *
+ * <pre>
+ * groupOperations.forUpdate().description("Another description for this group").update();
+ * </pre>
+ *
+ * <p><strong>Note:</strong> The update will fail with an exception if the group to update
+ * doesn't exist. If you want to check for the existence of a group, use {@link #exists()}.
+ *
+ * @return a builder to update the group
+ */
+ TestGroupUpdate.Builder forUpdate();
+ }
+}
diff --git a/java/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImpl.java b/java/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImpl.java
new file mode 100644
index 0000000..f9769c5
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImpl.java
@@ -0,0 +1,159 @@
+// Copyright (C) 2018 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.acceptance.testsuite.group;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.server.GerritPersonIdent;
+import com.google.gerrit.server.Sequences;
+import com.google.gerrit.server.ServerInitiated;
+import com.google.gerrit.server.account.GroupUUID;
+import com.google.gerrit.server.group.InternalGroup;
+import com.google.gerrit.server.group.db.Groups;
+import com.google.gerrit.server.group.db.GroupsUpdate;
+import com.google.gerrit.server.group.db.InternalGroupCreation;
+import com.google.gerrit.server.group.db.InternalGroupUpdate;
+import com.google.gwtorm.server.OrmDuplicateKeyException;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import java.io.IOException;
+import java.util.Optional;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.PersonIdent;
+
+/**
+ * The implementation of {@code GroupOperations}.
+ *
+ * <p>There is only one implementation of {@code GroupOperations}. Nevertheless, we keep the
+ * separation between interface and implementation to enhance clarity.
+ */
+public class GroupOperationsImpl implements GroupOperations {
+ private final Groups groups;
+ private final GroupsUpdate groupsUpdate;
+ private final Sequences seq;
+ private final PersonIdent serverIdent;
+
+ @Inject
+ public GroupOperationsImpl(
+ Groups groups,
+ @ServerInitiated GroupsUpdate groupsUpdate,
+ Sequences seq,
+ @GerritPersonIdent PersonIdent serverIdent) {
+ this.groups = groups;
+ this.groupsUpdate = groupsUpdate;
+ this.seq = seq;
+ this.serverIdent = serverIdent;
+ }
+
+ @Override
+ public MoreGroupOperations group(AccountGroup.UUID groupUuid) {
+ return new MoreGroupOperationsImpl(groupUuid);
+ }
+
+ @Override
+ public TestGroupCreation.Builder newGroup() {
+ return TestGroupCreation.builder(this::createNewGroup);
+ }
+
+ private AccountGroup.UUID createNewGroup(TestGroupCreation groupCreation)
+ throws ConfigInvalidException, IOException, OrmException {
+ InternalGroupCreation internalGroupCreation = toInternalGroupCreation(groupCreation);
+ InternalGroupUpdate internalGroupUpdate = toInternalGroupUpdate(groupCreation);
+ InternalGroup internalGroup =
+ groupsUpdate.createGroup(internalGroupCreation, internalGroupUpdate);
+ return internalGroup.getGroupUUID();
+ }
+
+ private InternalGroupCreation toInternalGroupCreation(TestGroupCreation groupCreation)
+ throws OrmException {
+ AccountGroup.Id groupId = new AccountGroup.Id(seq.nextGroupId());
+ String groupName = groupCreation.name().orElse("group-with-id-" + groupId.get());
+ AccountGroup.UUID groupUuid = GroupUUID.make(groupName, serverIdent);
+ AccountGroup.NameKey nameKey = new AccountGroup.NameKey(groupName);
+ return InternalGroupCreation.builder()
+ .setId(groupId)
+ .setGroupUUID(groupUuid)
+ .setNameKey(nameKey)
+ .build();
+ }
+
+ private static InternalGroupUpdate toInternalGroupUpdate(TestGroupCreation groupCreation) {
+ InternalGroupUpdate.Builder builder = InternalGroupUpdate.builder();
+ groupCreation.description().ifPresent(builder::setDescription);
+ groupCreation.ownerGroupUuid().ifPresent(builder::setOwnerGroupUUID);
+ groupCreation.visibleToAll().ifPresent(builder::setVisibleToAll);
+ builder.setMemberModification(originalMembers -> groupCreation.members());
+ builder.setSubgroupModification(originalSubgroups -> groupCreation.subgroups());
+ return builder.build();
+ }
+
+ private class MoreGroupOperationsImpl implements MoreGroupOperations {
+ private final AccountGroup.UUID groupUuid;
+
+ MoreGroupOperationsImpl(AccountGroup.UUID groupUuid) {
+ this.groupUuid = groupUuid;
+ }
+
+ @Override
+ public boolean exists() throws Exception {
+ return groups.getGroup(groupUuid).isPresent();
+ }
+
+ @Override
+ public TestGroup get() throws Exception {
+ Optional<InternalGroup> group = groups.getGroup(groupUuid);
+ checkState(group.isPresent(), "Tried to get non-existing test group");
+ return toTestGroup(group.get());
+ }
+
+ private TestGroup toTestGroup(InternalGroup internalGroup) {
+ return TestGroup.builder()
+ .groupUuid(internalGroup.getGroupUUID())
+ .groupId(internalGroup.getId())
+ .nameKey(internalGroup.getNameKey())
+ .description(Optional.ofNullable(internalGroup.getDescription()))
+ .ownerGroupUuid(internalGroup.getOwnerGroupUUID())
+ .visibleToAll(internalGroup.isVisibleToAll())
+ .createdOn(internalGroup.getCreatedOn())
+ .members(internalGroup.getMembers())
+ .subgroups(internalGroup.getSubgroups())
+ .build();
+ }
+
+ @Override
+ public TestGroupUpdate.Builder forUpdate() {
+ return TestGroupUpdate.builder(this::updateGroup);
+ }
+
+ private void updateGroup(TestGroupUpdate groupUpdate)
+ throws OrmDuplicateKeyException, NoSuchGroupException, ConfigInvalidException, IOException {
+ InternalGroupUpdate internalGroupUpdate = toInternalGroupUpdate(groupUpdate);
+ groupsUpdate.updateGroup(groupUuid, internalGroupUpdate);
+ }
+
+ private InternalGroupUpdate toInternalGroupUpdate(TestGroupUpdate groupUpdate) {
+ InternalGroupUpdate.Builder builder = InternalGroupUpdate.builder();
+ groupUpdate.name().map(AccountGroup.NameKey::new).ifPresent(builder::setName);
+ groupUpdate.description().ifPresent(builder::setDescription);
+ groupUpdate.ownerGroupUuid().ifPresent(builder::setOwnerGroupUUID);
+ groupUpdate.visibleToAll().ifPresent(builder::setVisibleToAll);
+ builder.setMemberModification(groupUpdate.memberModification()::apply);
+ builder.setSubgroupModification(groupUpdate.subgroupModification()::apply);
+ return builder.build();
+ }
+ }
+}
diff --git a/java/com/google/gerrit/acceptance/testsuite/group/TestGroup.java b/java/com/google/gerrit/acceptance/testsuite/group/TestGroup.java
new file mode 100644
index 0000000..b450304
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/testsuite/group/TestGroup.java
@@ -0,0 +1,78 @@
+// Copyright (C) 2018 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.acceptance.testsuite.group;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableSet;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.AccountGroup;
+import java.sql.Timestamp;
+import java.util.Optional;
+
+@AutoValue
+public abstract class TestGroup {
+
+ public abstract AccountGroup.UUID groupUuid();
+
+ public abstract AccountGroup.Id groupId();
+
+ public String name() {
+ return nameKey().get();
+ }
+
+ public abstract AccountGroup.NameKey nameKey();
+
+ public abstract Optional<String> description();
+
+ public abstract AccountGroup.UUID ownerGroupUuid();
+
+ public abstract boolean visibleToAll();
+
+ public abstract Timestamp createdOn();
+
+ public abstract ImmutableSet<Account.Id> members();
+
+ public abstract ImmutableSet<AccountGroup.UUID> subgroups();
+
+ static Builder builder() {
+ return new AutoValue_TestGroup.Builder();
+ }
+
+ @AutoValue.Builder
+ abstract static class Builder {
+
+ public abstract Builder groupUuid(AccountGroup.UUID groupUuid);
+
+ public abstract Builder groupId(AccountGroup.Id id);
+
+ public abstract Builder nameKey(AccountGroup.NameKey name);
+
+ public abstract Builder description(String description);
+
+ public abstract Builder description(Optional<String> description);
+
+ public abstract Builder ownerGroupUuid(AccountGroup.UUID ownerGroupUuid);
+
+ public abstract Builder visibleToAll(boolean visibleToAll);
+
+ public abstract Builder createdOn(Timestamp createdOn);
+
+ public abstract Builder members(ImmutableSet<Account.Id> members);
+
+ public abstract Builder subgroups(ImmutableSet<AccountGroup.UUID> subgroups);
+
+ abstract TestGroup build();
+ }
+}
diff --git a/java/com/google/gerrit/acceptance/testsuite/group/TestGroupCreation.java b/java/com/google/gerrit/acceptance/testsuite/group/TestGroupCreation.java
new file mode 100644
index 0000000..efed720
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/testsuite/group/TestGroupCreation.java
@@ -0,0 +1,112 @@
+// Copyright (C) 2018 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.acceptance.testsuite.group;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import com.google.gerrit.acceptance.testsuite.ThrowingFunction;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.AccountGroup;
+import java.util.Optional;
+import java.util.Set;
+
+@AutoValue
+public abstract class TestGroupCreation {
+
+ public abstract Optional<String> name();
+
+ public abstract Optional<String> description();
+
+ public abstract Optional<AccountGroup.UUID> ownerGroupUuid();
+
+ public abstract Optional<Boolean> visibleToAll();
+
+ public abstract ImmutableSet<Account.Id> members();
+
+ public abstract ImmutableSet<AccountGroup.UUID> subgroups();
+
+ abstract ThrowingFunction<TestGroupCreation, AccountGroup.UUID> groupCreator();
+
+ public static Builder builder(
+ ThrowingFunction<TestGroupCreation, AccountGroup.UUID> groupCreator) {
+ return new AutoValue_TestGroupCreation.Builder().groupCreator(groupCreator);
+ }
+
+ @AutoValue.Builder
+ public abstract static class Builder {
+
+ public abstract Builder name(String name);
+
+ public abstract Builder description(String description);
+
+ public Builder clearDescription() {
+ return description("");
+ }
+
+ public abstract Builder ownerGroupUuid(AccountGroup.UUID ownerGroupUuid);
+
+ public abstract Builder visibleToAll(boolean visibleToAll);
+
+ public Builder clearMembers() {
+ return members(ImmutableSet.of());
+ }
+
+ public Builder members(Account.Id member1, Account.Id... otherMembers) {
+ return members(Sets.union(ImmutableSet.of(member1), ImmutableSet.copyOf(otherMembers)));
+ }
+
+ public abstract Builder members(Set<Account.Id> members);
+
+ abstract ImmutableSet.Builder<Account.Id> membersBuilder();
+
+ public Builder addMember(Account.Id member) {
+ membersBuilder().add(member);
+ return this;
+ }
+
+ public Builder clearSubgroups() {
+ return subgroups(ImmutableSet.of());
+ }
+
+ public Builder subgroups(AccountGroup.UUID subgroup1, AccountGroup.UUID... otherSubgroups) {
+ return subgroups(Sets.union(ImmutableSet.of(subgroup1), ImmutableSet.copyOf(otherSubgroups)));
+ }
+
+ public abstract Builder subgroups(Set<AccountGroup.UUID> subgroups);
+
+ abstract ImmutableSet.Builder<AccountGroup.UUID> subgroupsBuilder();
+
+ public Builder addSubgroup(AccountGroup.UUID subgroup) {
+ subgroupsBuilder().add(subgroup);
+ return this;
+ }
+
+ abstract Builder groupCreator(
+ ThrowingFunction<TestGroupCreation, AccountGroup.UUID> groupCreator);
+
+ abstract TestGroupCreation autoBuild();
+
+ /**
+ * Executes the group creation as specified.
+ *
+ * @return the UUID of the created group
+ */
+ public AccountGroup.UUID create() throws Exception {
+ TestGroupCreation groupCreation = autoBuild();
+ return groupCreation.groupCreator().apply(groupCreation);
+ }
+ }
+}
diff --git a/java/com/google/gerrit/acceptance/testsuite/group/TestGroupUpdate.java b/java/com/google/gerrit/acceptance/testsuite/group/TestGroupUpdate.java
new file mode 100644
index 0000000..095a270
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/testsuite/group/TestGroupUpdate.java
@@ -0,0 +1,134 @@
+// Copyright (C) 2018 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.acceptance.testsuite.group;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import com.google.gerrit.acceptance.testsuite.ThrowingConsumer;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.AccountGroup;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Function;
+
+@AutoValue
+public abstract class TestGroupUpdate {
+
+ public abstract Optional<String> name();
+
+ public abstract Optional<String> description();
+
+ public abstract Optional<AccountGroup.UUID> ownerGroupUuid();
+
+ public abstract Optional<Boolean> visibleToAll();
+
+ public abstract Function<ImmutableSet<Account.Id>, Set<Account.Id>> memberModification();
+
+ public abstract Function<ImmutableSet<AccountGroup.UUID>, Set<AccountGroup.UUID>>
+ subgroupModification();
+
+ abstract ThrowingConsumer<TestGroupUpdate> groupUpdater();
+
+ public static Builder builder(ThrowingConsumer<TestGroupUpdate> groupUpdater) {
+ return new AutoValue_TestGroupUpdate.Builder()
+ .groupUpdater(groupUpdater)
+ .memberModification(in -> in)
+ .subgroupModification(in -> in);
+ }
+
+ @AutoValue.Builder
+ public abstract static class Builder {
+
+ public abstract Builder name(String name);
+
+ public abstract Builder description(String description);
+
+ public Builder clearDescription() {
+ return description("");
+ }
+
+ public abstract Builder ownerGroupUuid(AccountGroup.UUID ownerGroupUUID);
+
+ public abstract Builder visibleToAll(boolean visibleToAll);
+
+ abstract Builder memberModification(
+ Function<ImmutableSet<Account.Id>, Set<Account.Id>> memberModification);
+
+ abstract Function<ImmutableSet<Account.Id>, Set<Account.Id>> memberModification();
+
+ public Builder clearMembers() {
+ return memberModification(originalMembers -> ImmutableSet.of());
+ }
+
+ public Builder addMember(Account.Id member) {
+ Function<ImmutableSet<Account.Id>, Set<Account.Id>> previousModification =
+ memberModification();
+ memberModification(
+ originalMembers ->
+ Sets.union(previousModification.apply(originalMembers), ImmutableSet.of(member)));
+ return this;
+ }
+
+ public Builder removeMember(Account.Id member) {
+ Function<ImmutableSet<Account.Id>, Set<Account.Id>> previousModification =
+ memberModification();
+ memberModification(
+ originalMembers ->
+ Sets.difference(
+ previousModification.apply(originalMembers), ImmutableSet.of(member)));
+ return this;
+ }
+
+ abstract Builder subgroupModification(
+ Function<ImmutableSet<AccountGroup.UUID>, Set<AccountGroup.UUID>> subgroupModification);
+
+ abstract Function<ImmutableSet<AccountGroup.UUID>, Set<AccountGroup.UUID>>
+ subgroupModification();
+
+ public Builder clearSubgroups() {
+ return subgroupModification(originalMembers -> ImmutableSet.of());
+ }
+
+ public Builder addSubgroup(AccountGroup.UUID subgroup) {
+ Function<ImmutableSet<AccountGroup.UUID>, Set<AccountGroup.UUID>> previousModification =
+ subgroupModification();
+ subgroupModification(
+ originalSubgroups ->
+ Sets.union(previousModification.apply(originalSubgroups), ImmutableSet.of(subgroup)));
+ return this;
+ }
+
+ public Builder removeSubgroup(AccountGroup.UUID subgroup) {
+ Function<ImmutableSet<AccountGroup.UUID>, Set<AccountGroup.UUID>> previousModification =
+ subgroupModification();
+ subgroupModification(
+ originalSubgroups ->
+ Sets.difference(
+ previousModification.apply(originalSubgroups), ImmutableSet.of(subgroup)));
+ return this;
+ }
+
+ abstract Builder groupUpdater(ThrowingConsumer<TestGroupUpdate> groupUpdater);
+
+ abstract TestGroupUpdate autoBuild();
+
+ /** Executes the group update as specified. */
+ public void update() throws Exception {
+ TestGroupUpdate groupUpdater = autoBuild();
+ groupUpdater.groupUpdater().accept(groupUpdater);
+ }
+ }
+}
diff --git a/java/com/google/gerrit/common/data/LabelType.java b/java/com/google/gerrit/common/data/LabelType.java
index 29fc9c7..ff7d25b 100644
--- a/java/com/google/gerrit/common/data/LabelType.java
+++ b/java/com/google/gerrit/common/data/LabelType.java
@@ -17,13 +17,13 @@
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.toList;
-import static java.util.stream.Collectors.toMap;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.reviewdb.client.LabelId;
import com.google.gerrit.reviewdb.client.PatchSetApproval;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -36,6 +36,7 @@
public static final boolean DEF_COPY_ALL_SCORES_ON_MERGE_FIRST_PARENT_UPDATE = false;
public static final boolean DEF_COPY_MAX_SCORE = false;
public static final boolean DEF_COPY_MIN_SCORE = false;
+ public static final boolean DEF_IGNORE_SELF_APPROVAL = false;
public static LabelType withDefaultValues(String name) {
checkName(name);
@@ -103,6 +104,7 @@
protected boolean copyAllScoresIfNoCodeChange;
protected boolean copyAllScoresIfNoChange;
protected boolean allowPostSubmit;
+ protected boolean ignoreSelfApproval;
protected short defaultValue;
protected List<LabelValue> values;
@@ -141,13 +143,12 @@
setCopyMaxScore(DEF_COPY_MAX_SCORE);
setCopyMinScore(DEF_COPY_MIN_SCORE);
setAllowPostSubmit(DEF_ALLOW_POST_SUBMIT);
+ setIgnoreSelfApproval(DEF_IGNORE_SELF_APPROVAL);
- byValue =
- values
- .stream()
- .collect(
- collectingAndThen(
- toMap(LabelValue::getValue, v -> v), Collections::unmodifiableMap));
+ byValue = new HashMap<>();
+ for (LabelValue v : values) {
+ byValue.put(v.getValue(), v);
+ }
}
public String getName() {
@@ -190,6 +191,14 @@
this.allowPostSubmit = allowPostSubmit;
}
+ public boolean ignoreSelfApproval() {
+ return ignoreSelfApproval;
+ }
+
+ public void setIgnoreSelfApproval(boolean ignoreSelfApproval) {
+ this.ignoreSelfApproval = ignoreSelfApproval;
+ }
+
public void setRefPatterns(List<String> refPatterns) {
if (refPatterns != null) {
this.refPatterns =
diff --git a/java/com/google/gerrit/extensions/annotations/RemoveAfter.java b/java/com/google/gerrit/extensions/annotations/RemoveAfter.java
new file mode 100644
index 0000000..aa31dd0
--- /dev/null
+++ b/java/com/google/gerrit/extensions/annotations/RemoveAfter.java
@@ -0,0 +1,37 @@
+// Copyright (C) 2018 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.extensions.annotations;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import com.google.inject.BindingAnnotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation for features that are deprecated, but still present to adhere to the one-release-grace
+ * period we promised to users.
+ */
+@Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
+@Retention(SOURCE)
+@BindingAnnotation
+public @interface RemoveAfter {
+ /**
+ * Version after which the annotated functionality can be removed. Once the referenced version was
+ * branched off, the annotated code can be removed.
+ */
+ String value();
+}
diff --git a/java/com/google/gerrit/extensions/api/changes/IncludedInInfo.java b/java/com/google/gerrit/extensions/api/changes/IncludedInInfo.java
index d876034..8fe47bd 100644
--- a/java/com/google/gerrit/extensions/api/changes/IncludedInInfo.java
+++ b/java/com/google/gerrit/extensions/api/changes/IncludedInInfo.java
@@ -13,6 +13,7 @@
// limitations under the License.
package com.google.gerrit.extensions.api.changes;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@@ -23,9 +24,11 @@
public Map<String, Collection<String>> external;
public IncludedInInfo(
- List<String> branches, List<String> tags, Map<String, Collection<String>> external) {
- this.branches = branches;
- this.tags = tags;
+ Collection<String> branches,
+ Collection<String> tags,
+ Map<String, Collection<String>> external) {
+ this.branches = new ArrayList<>(branches);
+ this.tags = new ArrayList<>(tags);
this.external = external;
}
}
diff --git a/java/com/google/gerrit/extensions/registration/DynamicSet.java b/java/com/google/gerrit/extensions/registration/DynamicSet.java
index df3560a..6b3a49b 100644
--- a/java/com/google/gerrit/extensions/registration/DynamicSet.java
+++ b/java/com/google/gerrit/extensions/registration/DynamicSet.java
@@ -168,7 +168,7 @@
@Override
public Iterator<T> iterator() {
- Iterator<Entry<T>> entryIterator = entryIterator();
+ Iterator<Entry<T>> entryIterator = entries().iterator();
return new Iterator<T>() {
@Override
public boolean hasNext() {
@@ -183,39 +183,44 @@
};
}
- public Iterator<Entry<T>> entryIterator() {
+ public Iterable<Entry<T>> entries() {
final Iterator<AtomicReference<NamedProvider<T>>> itr = items.iterator();
- return new Iterator<Entry<T>>() {
- private Entry<T> next;
-
+ return new Iterable<Entry<T>>() {
@Override
- public boolean hasNext() {
- while (next == null && itr.hasNext()) {
- NamedProvider<T> p = itr.next().get();
- if (p != null) {
- try {
- next = new Entry<>(p.pluginName, p.impl);
- } catch (RuntimeException e) {
- // TODO Log failed member of DynamicSet.
+ public Iterator<Entry<T>> iterator() {
+ return new Iterator<Entry<T>>() {
+ private Entry<T> next;
+
+ @Override
+ public boolean hasNext() {
+ while (next == null && itr.hasNext()) {
+ NamedProvider<T> p = itr.next().get();
+ if (p != null) {
+ try {
+ next = new Entry<>(p.pluginName, p.impl);
+ } catch (RuntimeException e) {
+ // TODO Log failed member of DynamicSet.
+ }
+ }
}
+ return next != null;
}
- }
- return next != null;
- }
- @Override
- public Entry<T> next() {
- if (hasNext()) {
- Entry<T> result = next;
- next = null;
- return result;
- }
- throw new NoSuchElementException();
- }
+ @Override
+ public Entry<T> next() {
+ if (hasNext()) {
+ Entry<T> result = next;
+ next = null;
+ return result;
+ }
+ throw new NoSuchElementException();
+ }
- @Override
- public void remove() {
- throw new UnsupportedOperationException();
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
}
};
}
diff --git a/java/com/google/gerrit/httpd/BUILD b/java/com/google/gerrit/httpd/BUILD
index 3e71098..fae7c6a 100644
--- a/java/com/google/gerrit/httpd/BUILD
+++ b/java/com/google/gerrit/httpd/BUILD
@@ -16,6 +16,7 @@
"//java/com/google/gerrit/server/audit",
"//java/com/google/gerrit/server/git/receive",
"//java/com/google/gerrit/server/ioutil",
+ "//java/com/google/gerrit/server/logging",
"//java/com/google/gerrit/server/restapi",
"//java/com/google/gerrit/util/cli",
"//java/com/google/gerrit/util/http",
diff --git a/java/com/google/gerrit/httpd/GerritAuthModule.java b/java/com/google/gerrit/httpd/GerritAuthModule.java
new file mode 100644
index 0000000..c0ef207
--- /dev/null
+++ b/java/com/google/gerrit/httpd/GerritAuthModule.java
@@ -0,0 +1,55 @@
+// Copyright (C) 2018 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.httpd;
+
+import static com.google.gerrit.extensions.api.lfs.LfsDefinitions.LFS_URL_WO_AUTH_REGEX;
+
+import com.google.gerrit.extensions.client.GitBasicAuthPolicy;
+import com.google.gerrit.server.config.AuthConfig;
+import com.google.inject.Inject;
+import com.google.inject.servlet.ServletModule;
+import javax.servlet.Filter;
+
+/** Configures filter for authenticating REST requests. */
+public class GerritAuthModule extends ServletModule {
+ private static final String NOT_AUTHORIZED_LFS_URL_REGEX = "^(?:(?!/a/))" + LFS_URL_WO_AUTH_REGEX;
+ private final AuthConfig authConfig;
+
+ @Inject
+ GerritAuthModule(AuthConfig authConfig) {
+ this.authConfig = authConfig;
+ }
+
+ @Override
+ protected void configureServlets() {
+ Class<? extends Filter> authFilter = retreiveAuthFilterFromConfig(authConfig);
+
+ filterRegex(NOT_AUTHORIZED_LFS_URL_REGEX).through(authFilter);
+ filter("/a/*").through(authFilter);
+ }
+
+ static Class<? extends Filter> retreiveAuthFilterFromConfig(AuthConfig authConfig) {
+ Class<? extends Filter> authFilter;
+ if (authConfig.isTrustContainerAuth()) {
+ authFilter = ContainerAuthFilter.class;
+ } else {
+ authFilter =
+ authConfig.getGitBasicAuthPolicy() == GitBasicAuthPolicy.OAUTH
+ ? ProjectOAuthFilter.class
+ : ProjectBasicAuthFilter.class;
+ }
+ return authFilter;
+ }
+}
diff --git a/java/com/google/gerrit/httpd/GitOverHttpModule.java b/java/com/google/gerrit/httpd/GitOverHttpModule.java
index 3f3737d..8400d60 100644
--- a/java/com/google/gerrit/httpd/GitOverHttpModule.java
+++ b/java/com/google/gerrit/httpd/GitOverHttpModule.java
@@ -14,20 +14,16 @@
package com.google.gerrit.httpd;
-import static com.google.gerrit.extensions.api.lfs.LfsDefinitions.LFS_URL_WO_AUTH_REGEX;
+import static com.google.gerrit.httpd.GitOverHttpServlet.URL_REGEX;
-import com.google.gerrit.extensions.client.GitBasicAuthPolicy;
import com.google.gerrit.reviewdb.client.CoreDownloadSchemes;
import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.server.config.DownloadConfig;
import com.google.inject.Inject;
import com.google.inject.servlet.ServletModule;
-import javax.servlet.Filter;
/** Configures Git access over HTTP with authentication. */
public class GitOverHttpModule extends ServletModule {
- private static final String NOT_AUTHORIZED_LFS_URL_REGEX = "^(?:(?!/a/))" + LFS_URL_WO_AUTH_REGEX;
-
private final AuthConfig authConfig;
private final DownloadConfig downloadConfig;
@@ -39,28 +35,10 @@
@Override
protected void configureServlets() {
- Class<? extends Filter> authFilter;
- if (authConfig.isTrustContainerAuth()) {
- authFilter = ContainerAuthFilter.class;
- } else {
- authFilter =
- authConfig.getGitBasicAuthPolicy() == GitBasicAuthPolicy.OAUTH
- ? ProjectOAuthFilter.class
- : ProjectBasicAuthFilter.class;
+ if (downloadConfig.getDownloadSchemes().contains(CoreDownloadSchemes.ANON_HTTP)
+ || downloadConfig.getDownloadSchemes().contains(CoreDownloadSchemes.HTTP)) {
+ filterRegex(URL_REGEX).through(GerritAuthModule.retreiveAuthFilterFromConfig(authConfig));
+ serveRegex(URL_REGEX).with(GitOverHttpServlet.class);
}
-
- if (isHttpEnabled()) {
- String git = GitOverHttpServlet.URL_REGEX;
- filterRegex(git).through(authFilter);
- serveRegex(git).with(GitOverHttpServlet.class);
- }
-
- filterRegex(NOT_AUTHORIZED_LFS_URL_REGEX).through(authFilter);
- filter("/a/*").through(authFilter);
- }
-
- private boolean isHttpEnabled() {
- return downloadConfig.getDownloadSchemes().contains(CoreDownloadSchemes.ANON_HTTP)
- || downloadConfig.getDownloadSchemes().contains(CoreDownloadSchemes.HTTP);
}
}
diff --git a/java/com/google/gerrit/httpd/init/WebAppInitializer.java b/java/com/google/gerrit/httpd/init/WebAppInitializer.java
index 8e380f5..cb476af 100644
--- a/java/com/google/gerrit/httpd/init/WebAppInitializer.java
+++ b/java/com/google/gerrit/httpd/init/WebAppInitializer.java
@@ -23,6 +23,7 @@
import com.google.gerrit.extensions.client.AuthType;
import com.google.gerrit.gpg.GpgModule;
import com.google.gerrit.httpd.AllRequestFilter;
+import com.google.gerrit.httpd.GerritAuthModule;
import com.google.gerrit.httpd.GetUserFilter;
import com.google.gerrit.httpd.GitOverHttpModule;
import com.google.gerrit.httpd.H2CacheBasedWebSession;
@@ -422,9 +423,10 @@
private Injector createWebInjector() {
final List<Module> modules = new ArrayList<>();
modules.add(RequestContextFilter.module());
- modules.add(AllRequestFilter.module());
modules.add(RequestMetricsFilter.module());
+ modules.add(sysInjector.getInstance(GerritAuthModule.class));
modules.add(sysInjector.getInstance(GitOverHttpModule.class));
+ modules.add(AllRequestFilter.module());
modules.add(sysInjector.getInstance(WebModule.class));
modules.add(sysInjector.getInstance(RequireSslFilter.Module.class));
if (sshInjector != null) {
diff --git a/java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java b/java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java
index 9a24e47..74cadd3 100644
--- a/java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java
+++ b/java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java
@@ -456,8 +456,8 @@
}
}
- Collections.sort(cmds, PluginEntry.COMPARATOR_BY_NAME);
- Collections.sort(docs, PluginEntry.COMPARATOR_BY_NAME);
+ cmds.sort(PluginEntry.COMPARATOR_BY_NAME);
+ docs.sort(PluginEntry.COMPARATOR_BY_NAME);
StringBuilder md = new StringBuilder();
md.append(String.format("# Plugin %s #\n", pluginName));
diff --git a/java/com/google/gerrit/httpd/raw/BazelBuild.java b/java/com/google/gerrit/httpd/raw/BazelBuild.java
index 940a51b..2b390a9 100644
--- a/java/com/google/gerrit/httpd/raw/BazelBuild.java
+++ b/java/com/google/gerrit/httpd/raw/BazelBuild.java
@@ -23,14 +23,15 @@
import com.google.common.html.HtmlEscapers;
import com.google.common.io.ByteStreams;
import com.google.gerrit.common.TimeUtil;
+import com.google.gerrit.launcher.GerritLauncher;
import com.google.gerrit.util.http.CacheHeaders;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.PrintWriter;
-import java.nio.file.Files;
-import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Properties;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jgit.util.RawParseUtils;
@@ -122,20 +123,19 @@
}
}
- private Properties loadBuildProperties(Path propPath) throws IOException {
- Properties properties = new Properties();
- try (InputStream in = Files.newInputStream(propPath)) {
- properties.load(in);
- } catch (NoSuchFileException e) {
- // Ignore; will be run from PATH, with a descriptive error if it fails.
- }
- return properties;
- }
-
private ProcessBuilder newBuildProcess(Label label) throws IOException {
- Properties properties = loadBuildProperties(sourceRoot.resolve(".bazel_path"));
+ Properties properties = GerritLauncher.loadBuildProperties(sourceRoot.resolve(".bazel_path"));
String bazel = firstNonNull(properties.getProperty("bazel"), "bazel");
- ProcessBuilder proc = new ProcessBuilder(bazel, "build", label.fullName());
+ List<String> cmd = new ArrayList<>();
+ cmd.add(bazel);
+ cmd.add("build");
+ if (GerritLauncher.isJdk9OrLater()) {
+ String v = GerritLauncher.getJdkVersionPostJdk8();
+ cmd.add("--host_java_toolchain=@bazel_tools//tools/jdk:toolchain_java" + v);
+ cmd.add("--java_toolchain=@bazel_tools//tools/jdk:toolchain_java" + v);
+ }
+ cmd.add(label.fullName());
+ ProcessBuilder proc = new ProcessBuilder(cmd);
if (properties.containsKey("PATH")) {
proc.environment().put("PATH", properties.getProperty("PATH"));
}
diff --git a/java/com/google/gerrit/httpd/restapi/RestApiServlet.java b/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
index 38fe0e2..10f2638 100644
--- a/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
+++ b/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
@@ -110,6 +110,7 @@
import com.google.gerrit.server.cache.PerThreadCache;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.git.LockFailureException;
+import com.google.gerrit.server.logging.RequestId;
import com.google.gerrit.server.logging.TraceContext;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
@@ -1322,11 +1323,42 @@
}
private TraceContext enableTracing(HttpServletRequest req, HttpServletResponse res) {
- String traceValue = req.getParameter(ParameterParser.TRACE_PARAMETER);
- return TraceContext.newTrace(
- traceValue != null,
- traceValue,
- (tagName, traceId) -> res.setHeader(X_GERRIT_TRACE, traceId.toString()));
+ // There are 2 ways to enable tracing for REST calls:
+ // 1. by using the 'trace' or 'trace=<trace-id>' request parameter
+ // 2. by setting the 'X-Gerrit-Trace:' or 'X-Gerrit-Trace:<trace-id>' header
+ String traceValueFromHeader = req.getHeader(X_GERRIT_TRACE);
+ String traceValueFromRequestParam = req.getParameter(ParameterParser.TRACE_PARAMETER);
+ boolean doTrace = traceValueFromHeader != null || traceValueFromRequestParam != null;
+
+ // Check whether no trace ID, one trace ID or 2 different trace IDs have been specified.
+ String traceId1;
+ String traceId2;
+ if (!Strings.isNullOrEmpty(traceValueFromHeader)) {
+ traceId1 = traceValueFromHeader;
+ if (!Strings.isNullOrEmpty(traceValueFromRequestParam)
+ && !traceValueFromHeader.equals(traceValueFromRequestParam)) {
+ traceId2 = traceValueFromRequestParam;
+ } else {
+ traceId2 = null;
+ }
+ } else {
+ traceId1 = Strings.emptyToNull(traceValueFromRequestParam);
+ traceId2 = null;
+ }
+
+ // Use the first trace ID to start tracing. If this trace ID is null, a trace ID will be
+ // generated.
+ TraceContext traceContext =
+ TraceContext.newTrace(
+ doTrace,
+ traceId1,
+ (tagName, traceId) -> res.setHeader(X_GERRIT_TRACE, traceId.toString()));
+ // If a second trace ID was specified, add a tag for it as well.
+ if (traceId2 != null) {
+ traceContext.addTag(RequestId.Type.TRACE_ID, traceId2);
+ res.addHeader(X_GERRIT_TRACE, traceId2);
+ }
+ return traceContext;
}
private boolean isDelete(HttpServletRequest req) {
diff --git a/java/com/google/gerrit/index/query/AndSource.java b/java/com/google/gerrit/index/query/AndSource.java
index e2605f4..d1e1c30 100644
--- a/java/com/google/gerrit/index/query/AndSource.java
+++ b/java/com/google/gerrit/index/query/AndSource.java
@@ -15,6 +15,7 @@
package com.google.gerrit.index.query;
import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.ImmutableList.toImmutableList;
import com.google.common.base.Throwables;
import com.google.common.collect.FluentIterable;
@@ -26,7 +27,6 @@
import com.google.gwtorm.server.ResultSet;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.Comparator;
import java.util.List;
@@ -175,10 +175,8 @@
return cardinality;
}
- private List<Predicate<T>> sort(Collection<? extends Predicate<T>> that) {
- List<Predicate<T>> r = new ArrayList<>(that);
- Collections.sort(r, this);
- return r;
+ private ImmutableList<Predicate<T>> sort(Collection<? extends Predicate<T>> that) {
+ return that.stream().sorted(this).collect(toImmutableList());
}
@Override
diff --git a/java/com/google/gerrit/launcher/GerritLauncher.java b/java/com/google/gerrit/launcher/GerritLauncher.java
index 13dad0e..0d26fe7 100644
--- a/java/com/google/gerrit/launcher/GerritLauncher.java
+++ b/java/com/google/gerrit/launcher/GerritLauncher.java
@@ -34,6 +34,7 @@
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.CodeSource;
@@ -44,6 +45,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Properties;
import java.util.Scanner;
import java.util.SortedMap;
import java.util.TreeMap;
@@ -644,6 +646,25 @@
return resolveInSourceRoot("eclipse-out");
}
+ public static boolean isJdk9OrLater() {
+ return Double.parseDouble(System.getProperty("java.class.version")) >= 53.0;
+ }
+
+ public static String getJdkVersionPostJdk8() {
+ // 9.0.4 => 9
+ return System.getProperty("java.version").substring(0, 1);
+ }
+
+ public static Properties loadBuildProperties(Path propPath) throws IOException {
+ Properties properties = new Properties();
+ try (InputStream in = Files.newInputStream(propPath)) {
+ properties.load(in);
+ } catch (NoSuchFileException e) {
+ // Ignore; will be run from PATH, with a descriptive error if it fails.
+ }
+ return properties;
+ }
+
static final String SOURCE_ROOT_RESOURCE = "/com/google/gerrit/launcher/workspace-root.txt";
/**
@@ -708,14 +729,36 @@
return ret;
}
- private static ClassLoader useDevClasspath() throws MalformedURLException, FileNotFoundException {
+ private static ClassLoader useDevClasspath() throws IOException {
Path out = getDeveloperEclipseOut();
List<URL> dirs = new ArrayList<>();
dirs.add(out.resolve("classes").toUri().toURL());
ClassLoader cl = GerritLauncher.class.getClassLoader();
- for (URL u : ((URLClassLoader) cl).getURLs()) {
- if (includeJar(u)) {
- dirs.add(u);
+
+ if (isJdk9OrLater()) {
+ Path rootPath = resolveInSourceRoot(".").normalize();
+
+ Properties properties = loadBuildProperties(rootPath.resolve(".bazel_path"));
+ Path outputBase = Paths.get(properties.getProperty("output_base"));
+
+ Path runtimeClasspath =
+ rootPath.resolve("bazel-bin/tools/eclipse/main_classpath_collect.runtime_classpath");
+ for (String f : Files.readAllLines(runtimeClasspath, UTF_8)) {
+ URL url;
+ if (f.startsWith("external")) {
+ url = outputBase.resolve(f).toUri().toURL();
+ } else {
+ url = rootPath.resolve(f).toUri().toURL();
+ }
+ if (includeJar(url)) {
+ dirs.add(url);
+ }
+ }
+ } else {
+ for (URL u : ((URLClassLoader) cl).getURLs()) {
+ if (includeJar(u)) {
+ dirs.add(u);
+ }
}
}
return URLClassLoader.newInstance(
@@ -724,7 +767,9 @@
private static boolean includeJar(URL u) {
String path = u.getPath();
- return path.endsWith(".jar") && !path.endsWith("-src.jar");
+ return path.endsWith(".jar")
+ && !path.endsWith("-src.jar")
+ && !path.contains("/com/google/gerrit");
}
private GerritLauncher() {}
diff --git a/java/com/google/gerrit/lucene/AbstractLuceneIndex.java b/java/com/google/gerrit/lucene/AbstractLuceneIndex.java
index 12f88d5..dc293cd 100644
--- a/java/com/google/gerrit/lucene/AbstractLuceneIndex.java
+++ b/java/com/google/gerrit/lucene/AbstractLuceneIndex.java
@@ -39,7 +39,8 @@
import com.google.gerrit.index.query.FieldBundle;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.index.IndexUtils;
-import com.google.gerrit.server.logging.LoggingContextAwareThreadFactory;
+import com.google.gerrit.server.logging.LoggingContextAwareExecutorService;
+import com.google.gerrit.server.logging.LoggingContextAwareScheduledExecutorService;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.ResultSet;
import java.io.IOException;
@@ -55,6 +56,7 @@
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@@ -100,7 +102,7 @@
private final ReferenceManager<IndexSearcher> searcherManager;
private final ControlledRealTimeReopenThread<IndexSearcher> reopenThread;
private final Set<NrtFuture> notDoneNrtFutures;
- private ScheduledThreadPoolExecutor autoCommitExecutor;
+ private ScheduledExecutorService autoCommitExecutor;
AbstractLuceneIndex(
Schema<V> schema,
@@ -129,13 +131,13 @@
delegateWriter = autoCommitWriter;
autoCommitExecutor =
- new ScheduledThreadPoolExecutor(
- 1,
- new ThreadFactoryBuilder()
- .setThreadFactory(new LoggingContextAwareThreadFactory())
- .setNameFormat(index + " Commit-%d")
- .setDaemon(true)
- .build());
+ new LoggingContextAwareScheduledExecutorService(
+ new ScheduledThreadPoolExecutor(
+ 1,
+ new ThreadFactoryBuilder()
+ .setNameFormat(index + " Commit-%d")
+ .setDaemon(true)
+ .build()));
@SuppressWarnings("unused") // Error handling within Runnable.
Future<?> possiblyIgnoredError =
autoCommitExecutor.scheduleAtFixedRate(
@@ -170,13 +172,13 @@
writerThread =
MoreExecutors.listeningDecorator(
- Executors.newFixedThreadPool(
- 1,
- new ThreadFactoryBuilder()
- .setThreadFactory(new LoggingContextAwareThreadFactory())
- .setNameFormat(index + " Write-%d")
- .setDaemon(true)
- .build()));
+ new LoggingContextAwareExecutorService(
+ Executors.newFixedThreadPool(
+ 1,
+ new ThreadFactoryBuilder()
+ .setNameFormat(index + " Write-%d")
+ .setDaemon(true)
+ .build())));
reopenThread =
new ControlledRealTimeReopenThread<>(
diff --git a/java/com/google/gerrit/lucene/BUILD b/java/com/google/gerrit/lucene/BUILD
index 6cb7751..9c6ba74 100644
--- a/java/com/google/gerrit/lucene/BUILD
+++ b/java/com/google/gerrit/lucene/BUILD
@@ -33,6 +33,7 @@
"//java/com/google/gerrit/lifecycle",
"//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server",
+ "//java/com/google/gerrit/server/logging",
"//lib:guava",
"//lib:gwtorm",
"//lib/flogger:api",
diff --git a/java/com/google/gerrit/pgm/Daemon.java b/java/com/google/gerrit/pgm/Daemon.java
index 517787c..1a9af55 100644
--- a/java/com/google/gerrit/pgm/Daemon.java
+++ b/java/com/google/gerrit/pgm/Daemon.java
@@ -26,6 +26,7 @@
import com.google.gerrit.extensions.client.AuthType;
import com.google.gerrit.gpg.GpgModule;
import com.google.gerrit.httpd.AllRequestFilter;
+import com.google.gerrit.httpd.GerritAuthModule;
import com.google.gerrit.httpd.GetUserFilter;
import com.google.gerrit.httpd.GitOverHttpModule;
import com.google.gerrit.httpd.H2CacheBasedWebSession;
@@ -575,10 +576,11 @@
modules.add(new ProjectQoSFilter.Module());
}
modules.add(RequestContextFilter.module());
- modules.add(AllRequestFilter.module());
modules.add(RequestMetricsFilter.module());
modules.add(H2CacheBasedWebSession.module());
+ modules.add(sysInjector.getInstance(GerritAuthModule.class));
modules.add(sysInjector.getInstance(GitOverHttpModule.class));
+ modules.add(AllRequestFilter.module());
modules.add(sysInjector.getInstance(WebModule.class));
modules.add(sysInjector.getInstance(RequireSslFilter.Module.class));
modules.add(new HttpPluginModule());
diff --git a/java/com/google/gerrit/pgm/init/InitPlugins.java b/java/com/google/gerrit/pgm/init/InitPlugins.java
index 385d20c..e43114c 100644
--- a/java/com/google/gerrit/pgm/init/InitPlugins.java
+++ b/java/com/google/gerrit/pgm/init/InitPlugins.java
@@ -14,7 +14,8 @@
package com.google.gerrit.pgm.init;
-import com.google.common.collect.FluentIterable;
+import static java.util.Comparator.comparing;
+
import com.google.gerrit.common.PluginData;
import com.google.gerrit.pgm.init.api.ConsoleUI;
import com.google.gerrit.pgm.init.api.InitFlags;
@@ -25,12 +26,10 @@
import com.google.inject.Injector;
import com.google.inject.Singleton;
import java.io.IOException;
-import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Comparator;
import java.util.List;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
@@ -58,25 +57,16 @@
throws IOException {
final List<PluginData> result = new ArrayList<>();
pluginsDistribution.foreach(
- new PluginsDistribution.Processor() {
- @Override
- public void process(String pluginName, InputStream in) throws IOException {
- Path tmpPlugin = JarPluginProvider.storeInTemp(pluginName, in, site);
- String pluginVersion = getVersion(tmpPlugin);
- if (deleteTempPluginFile) {
- Files.delete(tmpPlugin);
- }
- result.add(new PluginData(pluginName, pluginVersion, tmpPlugin));
+ (pluginName, in) -> {
+ Path tmpPlugin = JarPluginProvider.storeInTemp(pluginName, in, site);
+ String pluginVersion = getVersion(tmpPlugin);
+ if (deleteTempPluginFile) {
+ Files.delete(tmpPlugin);
}
+ result.add(new PluginData(pluginName, pluginVersion, tmpPlugin));
});
- return FluentIterable.from(result)
- .toSortedList(
- new Comparator<PluginData>() {
- @Override
- public int compare(PluginData a, PluginData b) {
- return a.name.compareTo(b.name);
- }
- });
+ result.sort(comparing(p -> p.name));
+ return result;
}
private final ConsoleUI ui;
diff --git a/java/com/google/gerrit/pgm/util/BatchProgramModule.java b/java/com/google/gerrit/pgm/util/BatchProgramModule.java
index 8d77ed8..683a205 100644
--- a/java/com/google/gerrit/pgm/util/BatchProgramModule.java
+++ b/java/com/google/gerrit/pgm/util/BatchProgramModule.java
@@ -75,6 +75,7 @@
import com.google.gerrit.server.query.change.ChangeQueryProcessor;
import com.google.gerrit.server.restapi.group.GroupModule;
import com.google.gerrit.server.rules.DefaultSubmitRule;
+import com.google.gerrit.server.rules.IgnoreSelfApprovalRule;
import com.google.gerrit.server.rules.PrologModule;
import com.google.gerrit.server.rules.SubmitRule;
import com.google.gerrit.server.update.BatchUpdate;
@@ -188,6 +189,7 @@
factory(SubmitRuleEvaluator.Factory.class);
install(new PrologModule());
install(new DefaultSubmitRule.Module());
+ install(new IgnoreSelfApprovalRule.Module());
bind(ChangeJson.Factory.class).toProvider(Providers.<ChangeJson.Factory>of(null));
bind(EventUtil.class).toProvider(Providers.<EventUtil>of(null));
diff --git a/java/com/google/gerrit/server/BUILD b/java/com/google/gerrit/server/BUILD
index 96fcd39..e5bc480 100644
--- a/java/com/google/gerrit/server/BUILD
+++ b/java/com/google/gerrit/server/BUILD
@@ -42,6 +42,7 @@
"//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server/cache/serialize",
"//java/com/google/gerrit/server/ioutil",
+ "//java/com/google/gerrit/server/logging",
"//java/com/google/gerrit/server/util/git",
"//java/com/google/gerrit/util/cli",
"//java/com/google/gerrit/util/ssl",
diff --git a/java/com/google/gerrit/server/CommentsUtil.java b/java/com/google/gerrit/server/CommentsUtil.java
index 18d9b3d..99dfbbb 100644
--- a/java/com/google/gerrit/server/CommentsUtil.java
+++ b/java/com/google/gerrit/server/CommentsUtil.java
@@ -502,7 +502,7 @@
}
private static <T extends Comment> List<T> sort(List<T> comments) {
- Collections.sort(comments, COMMENT_ORDER);
+ comments.sort(COMMENT_ORDER);
return comments;
}
diff --git a/java/com/google/gerrit/server/IdentifiedUser.java b/java/com/google/gerrit/server/IdentifiedUser.java
index 16546f9..d9a4cae 100644
--- a/java/com/google/gerrit/server/IdentifiedUser.java
+++ b/java/com/google/gerrit/server/IdentifiedUser.java
@@ -15,10 +15,12 @@
package com.google.gerrit.server;
import static com.google.common.base.MoreObjects.firstNonNull;
+import static com.google.common.flogger.LazyArgs.lazy;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
+import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.account.AccountCache;
@@ -54,6 +56,8 @@
/** An authenticated user. */
public class IdentifiedUser extends CurrentUser {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
/** Create an IdentifiedUser, ignoring any per-request state. */
@Singleton
public static class GenericFactory {
@@ -375,8 +379,13 @@
if (effectiveGroups == null) {
if (authConfig.isIdentityTrustable(state().getExternalIds())) {
effectiveGroups = groupBackend.membershipsOf(this);
+ logger.atFinest().log(
+ "Known groups of %s: %s", getLoggableName(), lazy(effectiveGroups::getKnownGroups));
} else {
effectiveGroups = registeredGroups;
+ logger.atFinest().log(
+ "%s has a non-trusted identity, falling back to %s as known groups",
+ getLoggableName(), lazy(registeredGroups::getKnownGroups));
}
}
return effectiveGroups;
diff --git a/java/com/google/gerrit/server/account/GroupBackends.java b/java/com/google/gerrit/server/account/GroupBackends.java
index 803d491..1b15512 100644
--- a/java/com/google/gerrit/server/account/GroupBackends.java
+++ b/java/com/google/gerrit/server/account/GroupBackends.java
@@ -14,6 +14,8 @@
package com.google.gerrit.server.account;
+import static java.util.Comparator.comparing;
+
import com.google.common.collect.Iterables;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.GroupReference;
@@ -25,12 +27,7 @@
public class GroupBackends {
public static final Comparator<GroupReference> GROUP_REF_NAME_COMPARATOR =
- new Comparator<GroupReference>() {
- @Override
- public int compare(GroupReference a, GroupReference b) {
- return a.getName().compareTo(b.getName());
- }
- };
+ comparing(GroupReference::getName);
/**
* Runs {@link GroupBackend#suggest(String, ProjectState)} and filters the result to return the
diff --git a/java/com/google/gerrit/server/cache/h2/BUILD b/java/com/google/gerrit/server/cache/h2/BUILD
index 2ce756b..f6418e3 100644
--- a/java/com/google/gerrit/server/cache/h2/BUILD
+++ b/java/com/google/gerrit/server/cache/h2/BUILD
@@ -9,6 +9,7 @@
"//java/com/google/gerrit/lifecycle",
"//java/com/google/gerrit/server",
"//java/com/google/gerrit/server/cache/serialize",
+ "//java/com/google/gerrit/server/logging",
"//lib:guava",
"//lib:h2",
"//lib/flogger:api",
diff --git a/java/com/google/gerrit/server/cache/h2/H2CacheFactory.java b/java/com/google/gerrit/server/cache/h2/H2CacheFactory.java
index a7824ea..af1228d 100644
--- a/java/com/google/gerrit/server/cache/h2/H2CacheFactory.java
+++ b/java/com/google/gerrit/server/cache/h2/H2CacheFactory.java
@@ -28,7 +28,8 @@
import com.google.gerrit.server.cache.h2.H2CacheImpl.ValueHolder;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePaths;
-import com.google.gerrit.server.logging.LoggingContextAwareThreadFactory;
+import com.google.gerrit.server.logging.LoggingContextAwareExecutorService;
+import com.google.gerrit.server.logging.LoggingContextAwareScheduledExecutorService;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -75,20 +76,17 @@
if (cacheDir != null) {
executor =
- Executors.newFixedThreadPool(
- 1,
- new ThreadFactoryBuilder()
- .setThreadFactory(new LoggingContextAwareThreadFactory())
- .setNameFormat("DiskCache-Store-%d")
- .build());
+ new LoggingContextAwareExecutorService(
+ Executors.newFixedThreadPool(
+ 1, new ThreadFactoryBuilder().setNameFormat("DiskCache-Store-%d").build()));
cleanup =
- Executors.newScheduledThreadPool(
- 1,
- new ThreadFactoryBuilder()
- .setThreadFactory(new LoggingContextAwareThreadFactory())
- .setNameFormat("DiskCache-Prune-%d")
- .setDaemon(true)
- .build());
+ new LoggingContextAwareScheduledExecutorService(
+ Executors.newScheduledThreadPool(
+ 1,
+ new ThreadFactoryBuilder()
+ .setNameFormat("DiskCache-Prune-%d")
+ .setDaemon(true)
+ .build()));
} else {
executor = null;
cleanup = null;
diff --git a/java/com/google/gerrit/server/change/ChangeInserter.java b/java/com/google/gerrit/server/change/ChangeInserter.java
index e8c55e8..38c97f7 100644
--- a/java/com/google/gerrit/server/change/ChangeInserter.java
+++ b/java/com/google/gerrit/server/change/ChangeInserter.java
@@ -552,8 +552,6 @@
return;
}
- PermissionBackend.ForRef perm =
- permissionBackend.user(ctx.getUser()).project(ctx.getProject()).ref(refName);
try {
try (CommitReceivedEvent event =
new CommitReceivedEvent(
@@ -565,7 +563,7 @@
ctx.getIdentifiedUser())) {
commitValidatorsFactory
.forGerritCommits(
- perm,
+ permissionBackend.user(ctx.getUser()).project(ctx.getProject()),
new Branch.NameKey(ctx.getProject(), refName),
ctx.getIdentifiedUser(),
new NoSshInfo(),
diff --git a/java/com/google/gerrit/server/change/ChangeJson.java b/java/com/google/gerrit/server/change/ChangeJson.java
index e02f666..173d1da 100644
--- a/java/com/google/gerrit/server/change/ChangeJson.java
+++ b/java/com/google/gerrit/server/change/ChangeJson.java
@@ -1405,8 +1405,7 @@
out.commitWithFooters =
mergeUtilFactory
.create(projectCache.get(project))
- .createCommitMessageOnSubmit(
- commit, mergeTip, cd.notes(), userProvider.get(), in.getId());
+ .createCommitMessageOnSubmit(commit, mergeTip, cd.notes(), in.getId());
}
}
diff --git a/java/com/google/gerrit/server/change/IncludedIn.java b/java/com/google/gerrit/server/change/IncludedIn.java
index 8f8925a..d5d54ec 100644
--- a/java/com/google/gerrit/server/change/IncludedIn.java
+++ b/java/com/google/gerrit/server/change/IncludedIn.java
@@ -63,13 +63,13 @@
ListMultimap<String, String> external = MultimapBuilder.hashKeys().arrayListValues().build();
for (ExternalIncludedIn ext : externalIncludedIn) {
ListMultimap<String, String> extIncludedIns =
- ext.getIncludedIn(project.get(), rev.name(), d.getTags(), d.getBranches());
+ ext.getIncludedIn(project.get(), rev.name(), d.tags(), d.branches());
if (extIncludedIns != null) {
external.putAll(extIncludedIns);
}
}
return new IncludedInInfo(
- d.getBranches(), d.getTags(), (!external.isEmpty() ? external.asMap() : null));
+ d.branches(), d.tags(), (!external.isEmpty() ? external.asMap() : null));
}
}
}
diff --git a/java/com/google/gerrit/server/change/IncludedInResolver.java b/java/com/google/gerrit/server/change/IncludedInResolver.java
index d1bc0a2..62e9454 100644
--- a/java/com/google/gerrit/server/change/IncludedInResolver.java
+++ b/java/com/google/gerrit/server/change/IncludedInResolver.java
@@ -14,6 +14,13 @@
package com.google.gerrit.server.change;
+import static com.google.common.collect.ImmutableSortedSet.toImmutableSortedSet;
+import static java.util.Comparator.comparing;
+import static java.util.Comparator.naturalOrder;
+import static java.util.stream.Collectors.toList;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
@@ -22,7 +29,6 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
-import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -93,11 +99,9 @@
parseCommits(allTagsAndBranches);
Set<String> allMatchingTagsAndBranches = includedIn(tipsByCommitTime, 0);
- Result detail = new Result();
- detail.setBranches(getMatchingRefNames(allMatchingTagsAndBranches, branches));
- detail.setTags(getMatchingRefNames(allMatchingTagsAndBranches, tags));
-
- return detail;
+ return new AutoValue_IncludedInResolver_Result(
+ getMatchingRefNames(allMatchingTagsAndBranches, branches),
+ getMatchingRefNames(allMatchingTagsAndBranches, tags));
}
private boolean includedInOne(Collection<Ref> refs) throws IOException {
@@ -151,15 +155,7 @@
*/
private void partition(List<RevCommit> before, List<RevCommit> after) {
int insertionPoint =
- Collections.binarySearch(
- tipsByCommitTime,
- target,
- new Comparator<RevCommit>() {
- @Override
- public int compare(RevCommit c1, RevCommit c2) {
- return c1.getCommitTime() - c2.getCommitTime();
- }
- });
+ Collections.binarySearch(tipsByCommitTime, target, comparing(RevCommit::getCommitTime));
if (insertionPoint < 0) {
insertionPoint = -(insertionPoint + 1);
}
@@ -175,15 +171,14 @@
* Returns the short names of refs which are as well in the matchingRefs list as well as in the
* allRef list.
*/
- private static List<String> getMatchingRefNames(
+ private static ImmutableSortedSet<String> getMatchingRefNames(
Set<String> matchingRefs, Collection<Ref> allRefs) {
- List<String> refNames = Lists.newArrayListWithCapacity(matchingRefs.size());
- for (Ref r : allRefs) {
- if (matchingRefs.contains(r.getName())) {
- refNames.add(Repository.shortenRefName(r.getName()));
- }
- }
- return refNames;
+ return allRefs
+ .stream()
+ .map(Ref::getName)
+ .filter(matchingRefs::contains)
+ .map(Repository::shortenRefName)
+ .collect(toImmutableSortedSet(naturalOrder()));
}
/** Parse commit of ref and store the relation between ref and commit. */
@@ -211,43 +206,14 @@
}
commitToRef.put(commit, ref.getName());
}
- tipsByCommitTime = Lists.newArrayList(commitToRef.keySet());
- sortOlderFirst(tipsByCommitTime);
+ tipsByCommitTime =
+ commitToRef.keySet().stream().sorted(comparing(RevCommit::getCommitTime)).collect(toList());
}
- private void sortOlderFirst(List<RevCommit> tips) {
- Collections.sort(
- tips,
- new Comparator<RevCommit>() {
- @Override
- public int compare(RevCommit c1, RevCommit c2) {
- return c1.getCommitTime() - c2.getCommitTime();
- }
- });
- }
+ @AutoValue
+ public abstract static class Result {
+ public abstract ImmutableSortedSet<String> branches();
- public static class Result {
- private List<String> branches;
- private List<String> tags;
-
- public Result() {}
-
- public void setBranches(List<String> b) {
- Collections.sort(b);
- branches = b;
- }
-
- public List<String> getBranches() {
- return branches;
- }
-
- public void setTags(List<String> t) {
- Collections.sort(t);
- tags = t;
- }
-
- public List<String> getTags() {
- return tags;
- }
+ public abstract ImmutableSortedSet<String> tags();
}
}
diff --git a/java/com/google/gerrit/server/change/PatchSetInserter.java b/java/com/google/gerrit/server/change/PatchSetInserter.java
index d71a93d..8bd6c17 100644
--- a/java/com/google/gerrit/server/change/PatchSetInserter.java
+++ b/java/com/google/gerrit/server/change/PatchSetInserter.java
@@ -323,9 +323,6 @@
return;
}
- PermissionBackend.ForRef perm =
- permissionBackend.user(ctx.getUser()).ref(origNotes.getChange().getDest());
-
String refName = getPatchSetId().toRefName();
try (CommitReceivedEvent event =
new CommitReceivedEvent(
@@ -340,7 +337,7 @@
ctx.getIdentifiedUser())) {
commitValidatorsFactory
.forGerritCommits(
- perm,
+ permissionBackend.user(ctx.getUser()).project(ctx.getProject()),
origNotes.getChange().getDest(),
ctx.getIdentifiedUser(),
new NoSshInfo(),
diff --git a/java/com/google/gerrit/server/change/RebaseChangeOp.java b/java/com/google/gerrit/server/change/RebaseChangeOp.java
index 909ea3a..1f216f0 100644
--- a/java/com/google/gerrit/server/change/RebaseChangeOp.java
+++ b/java/com/google/gerrit/server/change/RebaseChangeOp.java
@@ -165,8 +165,7 @@
rw.parseBody(baseCommit);
newCommitMessage =
newMergeUtil()
- .createCommitMessageOnSubmit(
- original, baseCommit, notes, changeOwner, originalPatchSet.getId());
+ .createCommitMessageOnSubmit(original, baseCommit, notes, originalPatchSet.getId());
} else {
newCommitMessage = original.getFullMessage();
}
diff --git a/java/com/google/gerrit/server/change/WalkSorter.java b/java/com/google/gerrit/server/change/WalkSorter.java
index cff1ac7..916a62b 100644
--- a/java/com/google/gerrit/server/change/WalkSorter.java
+++ b/java/com/google/gerrit/server/change/WalkSorter.java
@@ -34,7 +34,6 @@
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.Deque;
import java.util.HashSet;
import java.util.List;
@@ -110,7 +109,7 @@
for (Map.Entry<Project.NameKey, Collection<ChangeData>> e : byProject.asMap().entrySet()) {
sortedByProject.add(sortProject(e.getKey(), e.getValue()));
}
- Collections.sort(sortedByProject, PROJECT_LIST_SORTER);
+ sortedByProject.sort(PROJECT_LIST_SORTER);
return Iterables.concat(sortedByProject);
}
diff --git a/java/com/google/gerrit/server/config/GerritGlobalModule.java b/java/com/google/gerrit/server/config/GerritGlobalModule.java
index b6a257b..0761d2e 100644
--- a/java/com/google/gerrit/server/config/GerritGlobalModule.java
+++ b/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -170,6 +170,7 @@
import com.google.gerrit.server.restapi.change.SuggestReviewers;
import com.google.gerrit.server.restapi.group.GroupModule;
import com.google.gerrit.server.rules.DefaultSubmitRule;
+import com.google.gerrit.server.rules.IgnoreSelfApprovalRule;
import com.google.gerrit.server.rules.PrologModule;
import com.google.gerrit.server.rules.RulesCache;
import com.google.gerrit.server.rules.SubmitRule;
@@ -244,6 +245,7 @@
install(new NoteDbModule(cfg));
install(new PrologModule());
install(new DefaultSubmitRule.Module());
+ install(new IgnoreSelfApprovalRule.Module());
install(new ReceiveCommitsModule());
install(new SshAddressesModule());
install(ThreadLocalRequestContext.module());
diff --git a/java/com/google/gerrit/server/config/SysExecutorModule.java b/java/com/google/gerrit/server/config/SysExecutorModule.java
index 2e97a58..f552434 100644
--- a/java/com/google/gerrit/server/config/SysExecutorModule.java
+++ b/java/com/google/gerrit/server/config/SysExecutorModule.java
@@ -19,7 +19,7 @@
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.gerrit.server.FanOutExecutor;
import com.google.gerrit.server.git.WorkQueue;
-import com.google.gerrit.server.logging.LoggingContextAwareThreadFactory;
+import com.google.gerrit.server.logging.LoggingContextAwareExecutorService;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.Singleton;
@@ -83,18 +83,18 @@
return MoreExecutors.newDirectExecutorService();
}
return MoreExecutors.listeningDecorator(
- MoreExecutors.getExitingExecutorService(
- new ThreadPoolExecutor(
- 1,
- poolSize,
- 10,
- TimeUnit.MINUTES,
- new ArrayBlockingQueue<Runnable>(poolSize),
- new ThreadFactoryBuilder()
- .setThreadFactory(new LoggingContextAwareThreadFactory())
- .setNameFormat("ChangeUpdate-%d")
- .setDaemon(true)
- .build(),
- new ThreadPoolExecutor.CallerRunsPolicy())));
+ new LoggingContextAwareExecutorService(
+ MoreExecutors.getExitingExecutorService(
+ new ThreadPoolExecutor(
+ 1,
+ poolSize,
+ 10,
+ TimeUnit.MINUTES,
+ new ArrayBlockingQueue<Runnable>(poolSize),
+ new ThreadFactoryBuilder()
+ .setNameFormat("ChangeUpdate-%d")
+ .setDaemon(true)
+ .build(),
+ new ThreadPoolExecutor.CallerRunsPolicy()))));
}
}
diff --git a/java/com/google/gerrit/server/events/EventFactory.java b/java/com/google/gerrit/server/events/EventFactory.java
index 2fbc1c7..fbce4b2 100644
--- a/java/com/google/gerrit/server/events/EventFactory.java
+++ b/java/com/google/gerrit/server/events/EventFactory.java
@@ -73,7 +73,6 @@
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -329,10 +328,9 @@
}
}
// Sort by original parent order.
- Collections.sort(
- ca.dependsOn,
+ ca.dependsOn.sort(
comparing(
- (DependencyAttribute d) -> {
+ d -> {
for (int i = 0; i < parentNames.size(); i++) {
if (parentNames.get(i).equals(d.revision)) {
return i;
diff --git a/java/com/google/gerrit/server/git/MergeUtil.java b/java/com/google/gerrit/server/git/MergeUtil.java
index 0231378..c035269 100644
--- a/java/com/google/gerrit/server/git/MergeUtil.java
+++ b/java/com/google/gerrit/server/git/MergeUtil.java
@@ -41,7 +41,6 @@
import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ApprovalsUtil;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.gerrit.server.config.GerritServerConfig;
@@ -220,7 +219,7 @@
} catch (IOException e) {
throw new IntegrationException("Branch head sorting failed", e);
}
- Collections.sort(result, CodeReviewCommit.ORDER);
+ result.sort(CodeReviewCommit.ORDER);
return result;
}
@@ -315,12 +314,10 @@
*
* @param n
* @param notes
- * @param user
* @param psId
* @return new message
*/
- private String createDetailedCommitMessage(
- RevCommit n, ChangeNotes notes, CurrentUser user, PatchSet.Id psId) {
+ private String createDetailedCommitMessage(RevCommit n, ChangeNotes notes, PatchSet.Id psId) {
Change c = notes.getChange();
final List<FooterLine> footers = n.getFooterLines();
final StringBuilder msgbuf = new StringBuilder();
@@ -424,12 +421,7 @@
}
public String createCommitMessageOnSubmit(CodeReviewCommit n, RevCommit mergeTip) {
- return createCommitMessageOnSubmit(
- n,
- mergeTip,
- n.notes(),
- identifiedUserFactory.create(n.notes().getChange().getOwner()),
- n.getPatchsetId());
+ return createCommitMessageOnSubmit(n, mergeTip, n.notes(), n.getPatchsetId());
}
/**
@@ -442,14 +434,13 @@
* @param n
* @param mergeTip
* @param notes
- * @param user
* @param id
* @return new message
*/
public String createCommitMessageOnSubmit(
- RevCommit n, RevCommit mergeTip, ChangeNotes notes, CurrentUser user, Id id) {
+ RevCommit n, RevCommit mergeTip, ChangeNotes notes, Id id) {
return commitMessageGenerator.generate(
- n, mergeTip, notes.getChange().getDest(), createDetailedCommitMessage(n, notes, user, id));
+ n, mergeTip, notes.getChange().getDest(), createDetailedCommitMessage(n, notes, id));
}
private static boolean isCodeReview(LabelId id) {
diff --git a/java/com/google/gerrit/server/git/WorkQueue.java b/java/com/google/gerrit/server/git/WorkQueue.java
index a2c12df..a7336f0 100644
--- a/java/com/google/gerrit/server/git/WorkQueue.java
+++ b/java/com/google/gerrit/server/git/WorkQueue.java
@@ -14,6 +14,8 @@
package com.google.gerrit.server.git;
+import static java.util.stream.Collectors.toList;
+
import com.google.common.base.CaseFormat;
import com.google.common.base.Supplier;
import com.google.common.flogger.FluentLogger;
@@ -24,7 +26,8 @@
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.ScheduleConfig.Schedule;
-import com.google.gerrit.server.logging.LoggingContextAwareThreadFactory;
+import com.google.gerrit.server.logging.LoggingContext;
+import com.google.gerrit.server.logging.LoggingContextAwareRunnable;
import com.google.gerrit.server.util.IdGenerator;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -43,6 +46,7 @@
import java.util.concurrent.Future;
import java.util.concurrent.RunnableScheduledFuture;
import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
@@ -166,12 +170,11 @@
if (threadPriority != Thread.NORM_PRIORITY) {
ThreadFactory parent = executor.getThreadFactory();
executor.setThreadFactory(
- new LoggingContextAwareThreadFactory(
- task -> {
- Thread t = parent.newThread(task);
- t.setPriority(threadPriority);
- return t;
- }));
+ task -> {
+ Thread t = parent.newThread(task);
+ t.setPriority(threadPriority);
+ return t;
+ });
}
return executor;
@@ -253,19 +256,18 @@
Executor(int corePoolSize, final String queueName) {
super(
corePoolSize,
- new LoggingContextAwareThreadFactory(
- new ThreadFactory() {
- private final ThreadFactory parent = Executors.defaultThreadFactory();
- private final AtomicInteger tid = new AtomicInteger(1);
+ new ThreadFactory() {
+ private final ThreadFactory parent = Executors.defaultThreadFactory();
+ private final AtomicInteger tid = new AtomicInteger(1);
- @Override
- public Thread newThread(Runnable task) {
- final Thread t = parent.newThread(task);
- t.setName(queueName + "-" + tid.getAndIncrement());
- t.setUncaughtExceptionHandler(LOG_UNCAUGHT_EXCEPTION);
- return t;
- }
- }));
+ @Override
+ public Thread newThread(Runnable task) {
+ final Thread t = parent.newThread(task);
+ t.setName(queueName + "-" + tid.getAndIncrement());
+ t.setUncaughtExceptionHandler(LOG_UNCAUGHT_EXCEPTION);
+ return t;
+ }
+ });
all =
new ConcurrentHashMap<>( //
@@ -277,6 +279,75 @@
}
@Override
+ public void execute(Runnable command) {
+ super.execute(LoggingContext.copy(command));
+ }
+
+ @Override
+ public <T> Future<T> submit(Callable<T> task) {
+ return super.submit(LoggingContext.copy(task));
+ }
+
+ @Override
+ public <T> Future<T> submit(Runnable task, T result) {
+ return super.submit(LoggingContext.copy(task), result);
+ }
+
+ @Override
+ public Future<?> submit(Runnable task) {
+ return super.submit(LoggingContext.copy(task));
+ }
+
+ @Override
+ public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
+ throws InterruptedException {
+ return super.invokeAll(tasks.stream().map(LoggingContext::copy).collect(toList()));
+ }
+
+ @Override
+ public <T> List<Future<T>> invokeAll(
+ Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+ throws InterruptedException {
+ return super.invokeAll(
+ tasks.stream().map(LoggingContext::copy).collect(toList()), timeout, unit);
+ }
+
+ @Override
+ public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
+ throws InterruptedException, ExecutionException {
+ return super.invokeAny(tasks.stream().map(LoggingContext::copy).collect(toList()));
+ }
+
+ @Override
+ public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+ throws InterruptedException, ExecutionException, TimeoutException {
+ return super.invokeAny(
+ tasks.stream().map(LoggingContext::copy).collect(toList()), timeout, unit);
+ }
+
+ @Override
+ public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
+ return super.schedule(LoggingContext.copy(command), delay, unit);
+ }
+
+ @Override
+ public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
+ return super.schedule(LoggingContext.copy(callable), delay, unit);
+ }
+
+ @Override
+ public ScheduledFuture<?> scheduleAtFixedRate(
+ Runnable command, long initialDelay, long period, TimeUnit unit) {
+ return super.scheduleAtFixedRate(LoggingContext.copy(command), initialDelay, period, unit);
+ }
+
+ @Override
+ public ScheduledFuture<?> scheduleWithFixedDelay(
+ Runnable command, long initialDelay, long delay, TimeUnit unit) {
+ return super.scheduleWithFixedDelay(LoggingContext.copy(command), initialDelay, delay, unit);
+ }
+
+ @Override
protected void terminated() {
super.terminated();
queues.remove(this);
@@ -370,6 +441,10 @@
Task<V> task;
+ if (runnable instanceof LoggingContextAwareRunnable) {
+ runnable = ((LoggingContextAwareRunnable) runnable).unwrap();
+ }
+
if (runnable instanceof ProjectRunnable) {
task = new ProjectTask<>((ProjectRunnable) runnable, r, this, id);
} else {
diff --git a/java/com/google/gerrit/server/git/meta/TabFile.java b/java/com/google/gerrit/server/git/meta/TabFile.java
index ef25cd8..4c0378a 100644
--- a/java/com/google/gerrit/server/git/meta/TabFile.java
+++ b/java/com/google/gerrit/server/git/meta/TabFile.java
@@ -14,13 +14,15 @@
package com.google.gerrit.server.git.meta;
+import static com.google.common.collect.ImmutableList.toImmutableList;
+
+import com.google.common.collect.ImmutableList;
import com.google.gerrit.server.git.ValidationError;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -122,10 +124,8 @@
return buf.toString();
}
- protected static <T extends Comparable<? super T>> List<T> sort(Collection<T> m) {
- ArrayList<T> r = new ArrayList<>(m);
- Collections.sort(r);
- return r;
+ protected static <T extends Comparable<? super T>> ImmutableList<T> sort(Collection<T> m) {
+ return m.stream().sorted().collect(toImmutableList());
}
protected static String pad(int len, String src) {
diff --git a/java/com/google/gerrit/server/git/receive/BUILD b/java/com/google/gerrit/server/git/receive/BUILD
index fddb9d6..f1c604b 100644
--- a/java/com/google/gerrit/server/git/receive/BUILD
+++ b/java/com/google/gerrit/server/git/receive/BUILD
@@ -8,6 +8,7 @@
"//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server",
+ "//java/com/google/gerrit/server/logging",
"//java/com/google/gerrit/util/cli",
"//lib:args4j",
"//lib:guava",
diff --git a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
index 6107ecd..438438c 100644
--- a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
+++ b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
@@ -582,7 +582,12 @@
tracePushOption.isPresent(),
tracePushOption.orElse(null),
(tagName, traceId) -> addMessage(tagName + ": " + traceId))) {
- traceContext.addTag(RequestId.Type.RECEIVE_ID, RequestId.forProject(project.getNameKey()));
+ traceContext.addTag(RequestId.Type.RECEIVE_ID, new RequestId(project.getNameKey().get()));
+
+ // Log the push options here, rather than in parsePushOptions(), so that they are included
+ // into the trace if tracing is enabled.
+ logger.atFine().log("push options: %s", receivePack.getPushOptions());
+
try {
if (!projectState.getProject().getState().permitsWrite()) {
for (ReceiveCommand cmd : commands) {
@@ -1805,7 +1810,7 @@
return;
}
- if (validateConnected(magicBranch.dest, tip)) {
+ if (validateConnected(magicBranch.cmd, magicBranch.dest, tip)) {
this.magicBranch = magicBranch;
}
}
@@ -1814,7 +1819,7 @@
// branch. If they aren't, we want to abort. We do this check by
// looking to see if we can compute a merge base between the new
// commits and the target branch head.
- private boolean validateConnected(Branch.NameKey dest, RevCommit tip) {
+ private boolean validateConnected(ReceiveCommand cmd, Branch.NameKey dest, RevCommit tip) {
RevWalk walk = receivePack.getRevWalk();
try {
Ref targetRef = receivePack.getAdvertisedRefs().get(dest.get());
@@ -1837,7 +1842,7 @@
walk.markStart(tip);
walk.markStart(h);
if (walk.next() == null) {
- reject(magicBranch.cmd, "no common ancestry");
+ reject(cmd, "no common ancestry");
return false;
}
} finally {
@@ -1845,7 +1850,7 @@
walk.setRevFilter(oldRevFilter);
}
} catch (IOException e) {
- magicBranch.cmd.setResult(REJECTED_MISSING_OBJECT);
+ cmd.setResult(REJECTED_MISSING_OBJECT);
logger.atSevere().withCause(e).log("Invalid pack upload; one or more objects weren't sent");
return false;
}
@@ -1906,13 +1911,15 @@
}
try {
+ NoteMap rejectCommits = BanCommit.loadRejectCommitsMap(repo, receivePack.getRevWalk());
if (validCommit(
receivePack.getRevWalk().getObjectReader(),
changeEnt.getDest(),
cmd,
newCommit,
false,
- changeEnt)) {
+ changeEnt,
+ rejectCommits)) {
logger.atFine().log("Replacing change %s", changeEnt.getId());
requestReplace(cmd, true, changeEnt, newCommit);
}
@@ -1961,6 +1968,10 @@
}
}
+ private NoteMap loadRejectCommits() throws IOException {
+ return BanCommit.loadRejectCommitsMap(repo, receivePack.getRevWalk());
+ }
+
private List<CreateRequest> selectNewAndReplacedChangesFromMagicBranch(Task newProgress) {
logger.atFine().log("Finding new and replaced changes");
List<CreateRequest> newChanges = new ArrayList<>();
@@ -2074,7 +2085,8 @@
magicBranch.cmd,
c,
magicBranch.merged,
- null)) {
+ null,
+ loadRejectCommits())) {
// Not a change the user can propose? Abort as early as possible.
logger.atFine().log("Aborting early due to invalid commit");
return Collections.emptyList();
@@ -3038,6 +3050,8 @@
walk.reset();
walk.sort(RevSort.NONE);
try {
+ NoteMap rejectCommits = loadRejectCommits();
+
RevObject parsedObject = walk.parseAny(cmd.getNewId());
if (!(parsedObject instanceof RevCommit)) {
return;
@@ -3060,7 +3074,7 @@
continue;
}
- if (!validCommit(walk.getObjectReader(), branch, cmd, c, false, null)) {
+ if (!validCommit(walk.getObjectReader(), branch, cmd, c, false, null, rejectCommits)) {
break;
}
@@ -3097,9 +3111,9 @@
ReceiveCommand cmd,
RevCommit commit,
boolean isMerged,
- @Nullable Change change)
+ @Nullable Change change,
+ NoteMap rejectCommits)
throws IOException {
- PermissionBackend.ForRef perm = permissions.ref(branch.get());
ValidCommitKey key = new AutoValue_ReceiveCommits_ValidCommitKey(commit.copy(), branch);
if (validCommits.contains(key)) {
@@ -3108,18 +3122,21 @@
try (CommitReceivedEvent receiveEvent =
new CommitReceivedEvent(cmd, project, branch.get(), objectReader, commit, user)) {
- CommitValidators validators =
- isMerged
- ? commitValidatorsFactory.forMergedCommits(
- project.getNameKey(), perm, user.asIdentifiedUser())
- : commitValidatorsFactory.forReceiveCommits(
- perm,
- branch,
- user.asIdentifiedUser(),
- sshInfo,
- repo,
- receiveEvent.revWalk,
- change);
+ CommitValidators validators;
+ if (isMerged) {
+ validators =
+ commitValidatorsFactory.forMergedCommits(permissions, branch, user.asIdentifiedUser());
+ } else {
+ validators =
+ commitValidatorsFactory.forReceiveCommits(
+ permissions,
+ branch,
+ user.asIdentifiedUser(),
+ sshInfo,
+ rejectCommits,
+ receiveEvent.revWalk,
+ change);
+ }
for (CommitValidationMessage m : validators.validate(receiveEvent)) {
messages.add(
diff --git a/java/com/google/gerrit/server/git/validators/CommitValidators.java b/java/com/google/gerrit/server/git/validators/CommitValidators.java
index 3e382fb..bcb910a 100644
--- a/java/com/google/gerrit/server/git/validators/CommitValidators.java
+++ b/java/com/google/gerrit/server/git/validators/CommitValidators.java
@@ -35,7 +35,6 @@
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Branch.NameKey;
import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser;
@@ -45,11 +44,9 @@
import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.events.CommitReceivedEvent;
-import com.google.gerrit.server.git.BanCommit;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.ValidationError;
import com.google.gerrit.server.permissions.PermissionBackend;
-import com.google.gerrit.server.permissions.PermissionBackend.ForRef;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.RefPermission;
import com.google.gerrit.server.project.ProjectCache;
@@ -123,15 +120,15 @@
}
public CommitValidators forReceiveCommits(
- PermissionBackend.ForRef perm,
+ PermissionBackend.ForProject forProject,
Branch.NameKey branch,
IdentifiedUser user,
SshInfo sshInfo,
- Repository repo,
+ NoteMap rejectCommits,
RevWalk rw,
@Nullable Change change)
throws IOException {
- NoteMap rejectCommits = BanCommit.loadRejectCommitsMap(repo, rw);
+ PermissionBackend.ForRef perm = forProject.ref(branch.get());
ProjectState projectState = projectCache.checkedGet(branch.getParentKey());
return new CommitValidators(
ImmutableList.of(
@@ -157,13 +154,14 @@
}
public CommitValidators forGerritCommits(
- ForRef perm,
+ PermissionBackend.ForProject forProject,
NameKey branch,
IdentifiedUser user,
SshInfo sshInfo,
RevWalk rw,
@Nullable Change change)
throws IOException {
+ PermissionBackend.ForRef perm = forProject.ref(branch.get());
ProjectState projectState = projectCache.checkedGet(branch.getParentKey());
return new CommitValidators(
ImmutableList.of(
@@ -187,7 +185,7 @@
}
public CommitValidators forMergedCommits(
- Project.NameKey project, PermissionBackend.ForRef perm, IdentifiedUser user)
+ PermissionBackend.ForProject forProject, Branch.NameKey branch, IdentifiedUser user)
throws IOException {
// Generally only include validators that are based on permissions of the
// user creating a change for a merged commit; generally exclude
@@ -202,10 +200,11 @@
// discuss what to do about it.
// - Plugin validators may do things like require certain commit message
// formats, so we play it safe and exclude them.
+ PermissionBackend.ForRef perm = forProject.ref(branch.get());
return new CommitValidators(
ImmutableList.of(
new UploadMergesPermissionValidator(perm),
- new ProjectStateValidationListener(projectCache.checkedGet(project)),
+ new ProjectStateValidationListener(projectCache.checkedGet(branch.getParentKey())),
new AuthorUploaderValidator(user, perm, canonicalWebUrl),
new CommitterUploaderValidator(user, perm, canonicalWebUrl)));
}
diff --git a/java/com/google/gerrit/server/logging/BUILD b/java/com/google/gerrit/server/logging/BUILD
new file mode 100644
index 0000000..d3211f0
--- /dev/null
+++ b/java/com/google/gerrit/server/logging/BUILD
@@ -0,0 +1,13 @@
+java_library(
+ name = "logging",
+ srcs = glob(
+ ["**/*.java"],
+ ),
+ visibility = ["//visibility:public"],
+ deps = [
+ "//java/com/google/gerrit/common:annotations",
+ "//java/com/google/gerrit/common:server",
+ "//lib:guava",
+ "//lib/flogger:api",
+ ],
+)
diff --git a/java/com/google/gerrit/server/logging/LoggingContext.java b/java/com/google/gerrit/server/logging/LoggingContext.java
index 2ce4c93..1e81c29 100644
--- a/java/com/google/gerrit/server/logging/LoggingContext.java
+++ b/java/com/google/gerrit/server/logging/LoggingContext.java
@@ -16,6 +16,7 @@
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.flogger.backend.Tags;
+import java.util.concurrent.Callable;
import java.util.logging.Level;
/**
@@ -42,6 +43,20 @@
return INSTANCE;
}
+ public static Runnable copy(Runnable runnable) {
+ if (runnable instanceof LoggingContextAwareRunnable) {
+ return runnable;
+ }
+ return new LoggingContextAwareRunnable(runnable);
+ }
+
+ public static <T> Callable<T> copy(Callable<T> callable) {
+ if (callable instanceof LoggingContextAwareCallable) {
+ return callable;
+ }
+ return new LoggingContextAwareCallable<>(callable);
+ }
+
@Override
public boolean shouldForceLogging(String loggerName, Level level, boolean isEnabled) {
return isLoggingForced();
diff --git a/java/com/google/gerrit/server/logging/LoggingContextAwareCallable.java b/java/com/google/gerrit/server/logging/LoggingContextAwareCallable.java
new file mode 100644
index 0000000..6aff5c4
--- /dev/null
+++ b/java/com/google/gerrit/server/logging/LoggingContextAwareCallable.java
@@ -0,0 +1,66 @@
+// Copyright (C) 2018 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.logging;
+
+import com.google.common.collect.ImmutableSetMultimap;
+import java.util.concurrent.Callable;
+
+/**
+ * Wrapper for a {@link Callable} that copies the {@link LoggingContext} from the current thread to
+ * the thread that executes the callable.
+ *
+ * <p>The state of the logging context that is copied to the thread that executes the callable is
+ * fixed at the creation time of this wrapper. If the callable is submitted to an executor and is
+ * executed later this means that changes that are done to the logging context in between creating
+ * and executing the callable do not apply.
+ *
+ * <p>See {@link LoggingContextAwareRunnable} for an example.
+ *
+ * @see LoggingContextAwareRunnable
+ */
+class LoggingContextAwareCallable<T> implements Callable<T> {
+ private final Callable<T> callable;
+ private final Thread callingThread;
+ private final ImmutableSetMultimap<String, String> tags;
+ private final boolean forceLogging;
+
+ LoggingContextAwareCallable(Callable<T> callable) {
+ this.callable = callable;
+ this.callingThread = Thread.currentThread();
+ this.tags = LoggingContext.getInstance().getTagsAsMap();
+ this.forceLogging = LoggingContext.getInstance().isLoggingForced();
+ }
+
+ @Override
+ public T call() throws Exception {
+ if (callingThread.equals(Thread.currentThread())) {
+ // propagation of logging context is not needed
+ return callable.call();
+ }
+
+ // propagate logging context
+ LoggingContext loggingCtx = LoggingContext.getInstance();
+ ImmutableSetMultimap<String, String> oldTags = loggingCtx.getTagsAsMap();
+ boolean oldForceLogging = loggingCtx.isLoggingForced();
+ loggingCtx.setTags(tags);
+ loggingCtx.forceLogging(forceLogging);
+ try {
+ return callable.call();
+ } finally {
+ loggingCtx.setTags(oldTags);
+ loggingCtx.forceLogging(oldForceLogging);
+ }
+ }
+}
diff --git a/java/com/google/gerrit/server/logging/LoggingContextAwareExecutorService.java b/java/com/google/gerrit/server/logging/LoggingContextAwareExecutorService.java
new file mode 100644
index 0000000..17e152e3
--- /dev/null
+++ b/java/com/google/gerrit/server/logging/LoggingContextAwareExecutorService.java
@@ -0,0 +1,110 @@
+// Copyright (C) 2018 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.logging;
+
+import static java.util.stream.Collectors.toList;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * An {@link ExecutorService} that copies the {@link LoggingContext} on executing a {@link Runnable}
+ * to the executing thread.
+ */
+public class LoggingContextAwareExecutorService implements ExecutorService {
+ private final ExecutorService executorService;
+
+ public LoggingContextAwareExecutorService(ExecutorService executorService) {
+ this.executorService = executorService;
+ }
+
+ @Override
+ public void execute(Runnable command) {
+ executorService.execute(LoggingContext.copy(command));
+ }
+
+ @Override
+ public void shutdown() {
+ executorService.shutdown();
+ }
+
+ @Override
+ public List<Runnable> shutdownNow() {
+ return executorService.shutdownNow();
+ }
+
+ @Override
+ public boolean isShutdown() {
+ return executorService.isShutdown();
+ }
+
+ @Override
+ public boolean isTerminated() {
+ return executorService.isTerminated();
+ }
+
+ @Override
+ public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
+ return executorService.awaitTermination(timeout, unit);
+ }
+
+ @Override
+ public <T> Future<T> submit(Callable<T> task) {
+ return executorService.submit(LoggingContext.copy(task));
+ }
+
+ @Override
+ public <T> Future<T> submit(Runnable task, T result) {
+ return executorService.submit(LoggingContext.copy(task), result);
+ }
+
+ @Override
+ public Future<?> submit(Runnable task) {
+ return executorService.submit(LoggingContext.copy(task));
+ }
+
+ @Override
+ public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
+ throws InterruptedException {
+ return executorService.invokeAll(tasks.stream().map(LoggingContext::copy).collect(toList()));
+ }
+
+ @Override
+ public <T> List<Future<T>> invokeAll(
+ Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+ throws InterruptedException {
+ return executorService.invokeAll(
+ tasks.stream().map(LoggingContext::copy).collect(toList()), timeout, unit);
+ }
+
+ @Override
+ public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
+ throws InterruptedException, ExecutionException {
+ return executorService.invokeAny(tasks.stream().map(LoggingContext::copy).collect(toList()));
+ }
+
+ @Override
+ public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+ throws InterruptedException, ExecutionException, TimeoutException {
+ return executorService.invokeAny(
+ tasks.stream().map(LoggingContext::copy).collect(toList()), timeout, unit);
+ }
+}
diff --git a/java/com/google/gerrit/server/logging/LoggingContextAwareRunnable.java b/java/com/google/gerrit/server/logging/LoggingContextAwareRunnable.java
new file mode 100644
index 0000000..0bd7d00
--- /dev/null
+++ b/java/com/google/gerrit/server/logging/LoggingContextAwareRunnable.java
@@ -0,0 +1,89 @@
+// Copyright (C) 2018 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.logging;
+
+import com.google.common.collect.ImmutableSetMultimap;
+
+/**
+ * Wrapper for a {@link Runnable} that copies the {@link LoggingContext} from the current thread to
+ * the thread that executes the runnable.
+ *
+ * <p>The state of the logging context that is copied to the thread that executes the runnable is
+ * fixed at the creation time of this wrapper. If the runnable is submitted to an executor and is
+ * executed later this means that changes that are done to the logging context in between creating
+ * and executing the runnable do not apply.
+ *
+ * <p>Example:
+ *
+ * <pre>
+ * try (TraceContext traceContext = TraceContext.newTrace(true, ...)) {
+ * executor
+ * .submit(new LoggingContextAwareRunnable(
+ * () -> {
+ * // Tracing is enabled since the runnable is created within the TraceContext.
+ * // Tracing is even enabled if the executor runs the runnable only after the
+ * // TraceContext was closed.
+ *
+ * // The tag "foo=bar" is not set, since it was added to the logging context only
+ * // after this runnable was created.
+ *
+ * // do stuff
+ * }))
+ * .get();
+ * traceContext.addTag("foo", "bar");
+ * }
+ * </pre>
+ *
+ * @see LoggingContextAwareCallable
+ */
+public class LoggingContextAwareRunnable implements Runnable {
+ private final Runnable runnable;
+ private final Thread callingThread;
+ private final ImmutableSetMultimap<String, String> tags;
+ private final boolean forceLogging;
+
+ LoggingContextAwareRunnable(Runnable runnable) {
+ this.runnable = runnable;
+ this.callingThread = Thread.currentThread();
+ this.tags = LoggingContext.getInstance().getTagsAsMap();
+ this.forceLogging = LoggingContext.getInstance().isLoggingForced();
+ }
+
+ public Runnable unwrap() {
+ return runnable;
+ }
+
+ @Override
+ public void run() {
+ if (callingThread.equals(Thread.currentThread())) {
+ // propagation of logging context is not needed
+ runnable.run();
+ return;
+ }
+
+ // propagate logging context
+ LoggingContext loggingCtx = LoggingContext.getInstance();
+ ImmutableSetMultimap<String, String> oldTags = loggingCtx.getTagsAsMap();
+ boolean oldForceLogging = loggingCtx.isLoggingForced();
+ loggingCtx.setTags(tags);
+ loggingCtx.forceLogging(forceLogging);
+ try {
+ runnable.run();
+ } finally {
+ loggingCtx.setTags(oldTags);
+ loggingCtx.forceLogging(oldForceLogging);
+ }
+ }
+}
diff --git a/java/com/google/gerrit/server/logging/LoggingContextAwareScheduledExecutorService.java b/java/com/google/gerrit/server/logging/LoggingContextAwareScheduledExecutorService.java
new file mode 100644
index 0000000..e17a91e
--- /dev/null
+++ b/java/com/google/gerrit/server/logging/LoggingContextAwareScheduledExecutorService.java
@@ -0,0 +1,59 @@
+// Copyright (C) 2018 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.logging;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A {@link ScheduledExecutorService} that copies the {@link LoggingContext} on executing a {@link
+ * Runnable} to the executing thread.
+ */
+public class LoggingContextAwareScheduledExecutorService extends LoggingContextAwareExecutorService
+ implements ScheduledExecutorService {
+ private final ScheduledExecutorService scheduledExecutorService;
+
+ public LoggingContextAwareScheduledExecutorService(
+ ScheduledExecutorService scheduledExecutorService) {
+ super(scheduledExecutorService);
+ this.scheduledExecutorService = scheduledExecutorService;
+ }
+
+ @Override
+ public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
+ return scheduledExecutorService.schedule(LoggingContext.copy(command), delay, unit);
+ }
+
+ @Override
+ public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
+ return scheduledExecutorService.schedule(LoggingContext.copy(callable), delay, unit);
+ }
+
+ @Override
+ public ScheduledFuture<?> scheduleAtFixedRate(
+ Runnable command, long initialDelay, long period, TimeUnit unit) {
+ return scheduledExecutorService.scheduleAtFixedRate(
+ LoggingContext.copy(command), initialDelay, period, unit);
+ }
+
+ @Override
+ public ScheduledFuture<?> scheduleWithFixedDelay(
+ Runnable command, long initialDelay, long delay, TimeUnit unit) {
+ return scheduledExecutorService.scheduleWithFixedDelay(
+ LoggingContext.copy(command), initialDelay, delay, unit);
+ }
+}
diff --git a/java/com/google/gerrit/server/logging/LoggingContextAwareThreadFactory.java b/java/com/google/gerrit/server/logging/LoggingContextAwareThreadFactory.java
deleted file mode 100644
index 05ff0d3..0000000
--- a/java/com/google/gerrit/server/logging/LoggingContextAwareThreadFactory.java
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright (C) 2018 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.logging;
-
-import com.google.common.collect.ImmutableSetMultimap;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ThreadFactory;
-
-/**
- * ThreadFactory that copies the logging context of the current thread to any new thread that is
- * created by this ThreadFactory.
- */
-public class LoggingContextAwareThreadFactory implements ThreadFactory {
- private final ThreadFactory parentThreadFactory;
-
- public LoggingContextAwareThreadFactory() {
- this.parentThreadFactory = Executors.defaultThreadFactory();
- }
-
- public LoggingContextAwareThreadFactory(ThreadFactory parentThreadFactory) {
- this.parentThreadFactory = parentThreadFactory;
- }
-
- @Override
- public Thread newThread(Runnable r) {
- Thread callingThread = Thread.currentThread();
- ImmutableSetMultimap<String, String> tags = LoggingContext.getInstance().getTagsAsMap();
- boolean forceLogging = LoggingContext.getInstance().isLoggingForced();
- return parentThreadFactory.newThread(
- () -> {
- if (callingThread.equals(Thread.currentThread())) {
- // propagation of logging context is not needed
- r.run();
- return;
- }
-
- // propagate logging context
- LoggingContext loggingCtx = LoggingContext.getInstance();
- loggingCtx.setTags(tags);
- loggingCtx.forceLogging(forceLogging);
- try {
- r.run();
- } finally {
- loggingCtx.clearTags();
- loggingCtx.forceLogging(false);
- }
- });
- }
-}
diff --git a/java/com/google/gerrit/server/logging/RequestId.java b/java/com/google/gerrit/server/logging/RequestId.java
index 81619fb..b0a8ad9 100644
--- a/java/com/google/gerrit/server/logging/RequestId.java
+++ b/java/com/google/gerrit/server/logging/RequestId.java
@@ -19,8 +19,6 @@
import com.google.common.hash.Hashing;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.TimeUtil;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
import java.net.InetAddress;
import java.net.UnknownHostException;
@@ -52,21 +50,13 @@
return LoggingContext.getInstance().getTagsAsMap().keySet().stream().anyMatch(Type::isId);
}
- public static RequestId forChange(Change c) {
- return new RequestId(c.getId().toString());
- }
-
- public static RequestId forProject(Project.NameKey p) {
- return new RequestId(p.toString());
- }
-
private final String str;
public RequestId() {
this(null);
}
- private RequestId(@Nullable String resourceId) {
+ public RequestId(@Nullable String resourceId) {
Hasher h = Hashing.murmur3_128().newHasher();
h.putLong(Thread.currentThread().getId()).putUnencodedChars(MACHINE_ID);
str =
diff --git a/java/com/google/gerrit/server/logging/TraceContext.java b/java/com/google/gerrit/server/logging/TraceContext.java
index 3e7bab4..977baa5 100644
--- a/java/com/google/gerrit/server/logging/TraceContext.java
+++ b/java/com/google/gerrit/server/logging/TraceContext.java
@@ -47,8 +47,9 @@
* </pre>
*
* <p>The logging tags and the force logging flag are stored in the {@link LoggingContext}. {@link
- * LoggingContextAwareThreadFactory} ensures that the logging context is automatically copied to
- * background threads.
+ * LoggingContextAwareExecutorService}, {@link LoggingContextAwareScheduledExecutorService} and the
+ * executor in {@link com.google.gerrit.server.git.WorkQueue} ensure that the logging context is
+ * automatically copied to background threads.
*
* <p>On close of the trace context newly set tags are unset. Force logging is disabled on close if
* it got enabled while the trace context was open.
diff --git a/java/com/google/gerrit/server/mail/send/CommentSender.java b/java/com/google/gerrit/server/mail/send/CommentSender.java
index 54176e2..0baaa11c 100644
--- a/java/com/google/gerrit/server/mail/send/CommentSender.java
+++ b/java/com/google/gerrit/server/mail/send/CommentSender.java
@@ -239,7 +239,7 @@
}
}
- Collections.sort(groups, Comparator.comparing(g -> g.filename, FilenameComparator.INSTANCE));
+ groups.sort(Comparator.comparing(g -> g.filename, FilenameComparator.INSTANCE));
return groups;
}
diff --git a/java/com/google/gerrit/server/mime/MimeUtilFileTypeRegistry.java b/java/com/google/gerrit/server/mime/MimeUtilFileTypeRegistry.java
index eecf935..0e9a2b7 100644
--- a/java/com/google/gerrit/server/mime/MimeUtilFileTypeRegistry.java
+++ b/java/com/google/gerrit/server/mime/MimeUtilFileTypeRegistry.java
@@ -14,6 +14,8 @@
package com.google.gerrit.server.mime;
+import static java.util.Comparator.comparing;
+
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.inject.Inject;
@@ -22,12 +24,9 @@
import eu.medsea.mimeutil.MimeType;
import eu.medsea.mimeutil.MimeUtil2;
import java.io.InputStream;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
-import java.util.Comparator;
import java.util.HashSet;
-import java.util.List;
import java.util.Set;
import org.eclipse.jgit.lib.Config;
@@ -115,16 +114,7 @@
return MimeUtil2.UNKNOWN_MIME_TYPE;
}
- final List<MimeType> types = new ArrayList<>(mimeTypes);
- Collections.sort(
- types,
- new Comparator<MimeType>() {
- @Override
- public int compare(MimeType a, MimeType b) {
- return getCorrectedMimeSpecificity(b) - getCorrectedMimeSpecificity(a);
- }
- });
- return types.get(0);
+ return Collections.max(mimeTypes, comparing(this::getCorrectedMimeSpecificity));
}
@Override
diff --git a/java/com/google/gerrit/server/notedb/ChangeBundle.java b/java/com/google/gerrit/server/notedb/ChangeBundle.java
index 1d3c752..c48a2f8 100644
--- a/java/com/google/gerrit/server/notedb/ChangeBundle.java
+++ b/java/com/google/gerrit/server/notedb/ChangeBundle.java
@@ -17,11 +17,16 @@
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.common.collect.ImmutableSortedMap.toImmutableSortedMap;
import static com.google.gerrit.common.TimeUtil.truncateToSecond;
import static com.google.gerrit.reviewdb.server.ReviewDbUtil.checkColumns;
import static com.google.gerrit.reviewdb.server.ReviewDbUtil.intKeyOrdering;
import static com.google.gerrit.server.notedb.ChangeBundle.Source.NOTE_DB;
import static com.google.gerrit.server.notedb.ChangeBundle.Source.REVIEW_DB;
+import static java.util.Comparator.comparing;
+import static java.util.Comparator.naturalOrder;
+import static java.util.Comparator.nullsFirst;
import static java.util.stream.Collectors.toList;
import com.google.auto.value.AutoValue;
@@ -31,7 +36,6 @@
import com.google.common.base.Predicates;
import com.google.common.base.Strings;
import com.google.common.collect.Collections2;
-import com.google.common.collect.ComparisonChain;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -43,13 +47,14 @@
import com.google.common.collect.Maps;
import com.google.common.collect.Ordering;
import com.google.common.collect.Sets;
+import com.google.common.collect.Streams;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.Patch;
import com.google.gerrit.reviewdb.client.PatchLineComment;
import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gerrit.reviewdb.client.PatchSet.Id;
import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.CommentsUtil;
@@ -70,7 +75,6 @@
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
-import java.util.TreeMap;
/**
* A bundle of all entities rooted at a single {@link Change} entity.
@@ -105,110 +109,65 @@
Source.NOTE_DB);
}
- private static Map<ChangeMessage.Key, ChangeMessage> changeMessageMap(
- Iterable<ChangeMessage> in) {
- Map<ChangeMessage.Key, ChangeMessage> out =
- new TreeMap<>(
- new Comparator<ChangeMessage.Key>() {
- @Override
- public int compare(ChangeMessage.Key a, ChangeMessage.Key b) {
- return ComparisonChain.start()
- .compare(a.getParentKey().get(), b.getParentKey().get())
- .compare(a.get(), b.get())
- .result();
- }
- });
- for (ChangeMessage cm : in) {
- out.put(cm.getKey(), cm);
- }
- return out;
+ private static ImmutableSortedMap<ChangeMessage.Key, ChangeMessage> changeMessageMap(
+ Collection<ChangeMessage> in) {
+ return in.stream()
+ .collect(
+ toImmutableSortedMap(
+ comparing((ChangeMessage.Key k) -> k.getParentKey().get())
+ .thenComparing(k -> k.get()),
+ cm -> cm.getKey(),
+ cm -> cm));
}
// Unlike the *Map comparators, which are intended to make key lists diffable,
// this comparator sorts first on timestamp, then on every other field.
- private static final Ordering<ChangeMessage> CHANGE_MESSAGE_ORDER =
- new Ordering<ChangeMessage>() {
- final Ordering<Comparable<?>> nullsFirst = Ordering.natural().nullsFirst();
-
- @Override
- public int compare(ChangeMessage a, ChangeMessage b) {
- return ComparisonChain.start()
- .compare(a.getWrittenOn(), b.getWrittenOn())
- .compare(a.getKey().getParentKey().get(), b.getKey().getParentKey().get())
- .compare(psId(a), psId(b), nullsFirst)
- .compare(a.getAuthor(), b.getAuthor(), intKeyOrdering())
- .compare(a.getMessage(), b.getMessage(), nullsFirst)
- .result();
- }
-
- private Integer psId(ChangeMessage m) {
- return m.getPatchSetId() != null ? m.getPatchSetId().get() : null;
- }
- };
+ private static final Comparator<ChangeMessage> CHANGE_MESSAGE_COMPARATOR =
+ comparing(ChangeMessage::getWrittenOn)
+ .thenComparing(m -> m.getKey().getParentKey().get())
+ .thenComparing(
+ m -> m.getPatchSetId() != null ? m.getPatchSetId().get() : null,
+ nullsFirst(naturalOrder()))
+ .thenComparing(ChangeMessage::getAuthor, intKeyOrdering())
+ .thenComparing(ChangeMessage::getMessage, nullsFirst(naturalOrder()));
private static ImmutableList<ChangeMessage> changeMessageList(Iterable<ChangeMessage> in) {
- return CHANGE_MESSAGE_ORDER.immutableSortedCopy(in);
+ return Streams.stream(in).sorted(CHANGE_MESSAGE_COMPARATOR).collect(toImmutableList());
}
- private static TreeMap<PatchSet.Id, PatchSet> patchSetMap(Iterable<PatchSet> in) {
- TreeMap<PatchSet.Id, PatchSet> out =
- new TreeMap<>(
- new Comparator<PatchSet.Id>() {
- @Override
- public int compare(PatchSet.Id a, PatchSet.Id b) {
- return patchSetIdChain(a, b).result();
- }
- });
- for (PatchSet ps : in) {
- out.put(ps.getId(), ps);
- }
- return out;
+ private static ImmutableSortedMap<Id, PatchSet> patchSetMap(Iterable<PatchSet> in) {
+ return Streams.stream(in)
+ .collect(toImmutableSortedMap(patchSetIdComparator(), PatchSet::getId, ps -> ps));
}
- private static Map<PatchSetApproval.Key, PatchSetApproval> patchSetApprovalMap(
+ private static ImmutableSortedMap<PatchSetApproval.Key, PatchSetApproval> patchSetApprovalMap(
Iterable<PatchSetApproval> in) {
- Map<PatchSetApproval.Key, PatchSetApproval> out =
- new TreeMap<>(
- new Comparator<PatchSetApproval.Key>() {
- @Override
- public int compare(PatchSetApproval.Key a, PatchSetApproval.Key b) {
- return patchSetIdChain(a.getParentKey(), b.getParentKey())
- .compare(a.getAccountId().get(), b.getAccountId().get())
- .compare(a.getLabelId(), b.getLabelId())
- .result();
- }
- });
- for (PatchSetApproval psa : in) {
- out.put(psa.getKey(), psa);
- }
- return out;
+ return Streams.stream(in)
+ .collect(
+ toImmutableSortedMap(
+ comparing(PatchSetApproval.Key::getParentKey, patchSetIdComparator())
+ .thenComparing(PatchSetApproval.Key::getAccountId, intKeyOrdering())
+ .thenComparing(PatchSetApproval.Key::getLabelId),
+ PatchSetApproval::getKey,
+ a -> a));
}
- private static Map<PatchLineComment.Key, PatchLineComment> patchLineCommentMap(
+ private static ImmutableSortedMap<PatchLineComment.Key, PatchLineComment> patchLineCommentMap(
Iterable<PatchLineComment> in) {
- Map<PatchLineComment.Key, PatchLineComment> out =
- new TreeMap<>(
- new Comparator<PatchLineComment.Key>() {
- @Override
- public int compare(PatchLineComment.Key a, PatchLineComment.Key b) {
- Patch.Key pka = a.getParentKey();
- Patch.Key pkb = b.getParentKey();
- return patchSetIdChain(pka.getParentKey(), pkb.getParentKey())
- .compare(pka.get(), pkb.get())
- .compare(a.get(), b.get())
- .result();
- }
- });
- for (PatchLineComment plc : in) {
- out.put(plc.getKey(), plc);
- }
- return out;
+ return Streams.stream(in)
+ .collect(
+ toImmutableSortedMap(
+ comparing(
+ (PatchLineComment.Key k) -> k.getParentKey().getParentKey(),
+ patchSetIdComparator())
+ .thenComparing(PatchLineComment.Key::getParentKey)
+ .thenComparing(PatchLineComment.Key::get),
+ PatchLineComment::getKey,
+ c -> c));
}
- private static ComparisonChain patchSetIdChain(PatchSet.Id a, PatchSet.Id b) {
- return ComparisonChain.start()
- .compare(a.getParentKey().get(), b.getParentKey().get())
- .compare(a.get(), b.get());
+ private static Comparator<PatchSet.Id> patchSetIdComparator() {
+ return comparing((PatchSet.Id id) -> id.getParentKey().get()).thenComparing(id -> id.get());
}
static {
@@ -598,9 +557,10 @@
}
if (!bs.isEmpty()) {
sb.append("Only in B:");
- for (ChangeMessage cm : CHANGE_MESSAGE_ORDER.sortedCopy(bs.values())) {
- sb.append("\n ").append(cm);
- }
+ bs.values()
+ .stream()
+ .sorted(CHANGE_MESSAGE_COMPARATOR)
+ .forEach(cm -> sb.append("\n ").append(cm));
}
diffs.add(sb.toString());
}
diff --git a/java/com/google/gerrit/server/notedb/ChangeNotesParser.java b/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
index 4eeab81..cbb7020 100644
--- a/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
+++ b/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
@@ -79,7 +79,6 @@
import java.sql.Timestamp;
import java.text.ParseException;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -297,9 +296,7 @@
}
result.put(a.getPatchSetId(), a);
}
- for (Collection<PatchSetApproval> v : result.asMap().values()) {
- Collections.sort((List<PatchSetApproval>) v, ChangeNotes.PSA_BY_TIME);
- }
+ result.keySet().forEach(k -> result.get(k).sort(ChangeNotes.PSA_BY_TIME));
return result;
}
diff --git a/java/com/google/gerrit/server/notedb/LegacyChangeNoteWrite.java b/java/com/google/gerrit/server/notedb/LegacyChangeNoteWrite.java
index 7931d88..c9711b5 100644
--- a/java/com/google/gerrit/server/notedb/LegacyChangeNoteWrite.java
+++ b/java/com/google/gerrit/server/notedb/LegacyChangeNoteWrite.java
@@ -15,10 +15,12 @@
package com.google.gerrit.server.notedb;
import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.gerrit.server.CommentsUtil.COMMENT_ORDER;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ListMultimap;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Comment;
@@ -30,8 +32,6 @@
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.sql.Timestamp;
-import java.util.ArrayList;
-import java.util.Collections;
import java.util.Date;
import java.util.List;
import org.eclipse.jgit.lib.PersonIdent;
@@ -87,8 +87,7 @@
return;
}
- List<Integer> psIds = new ArrayList<>(comments.keySet());
- Collections.sort(psIds);
+ ImmutableList<Integer> psIds = comments.keySet().stream().sorted().collect(toImmutableList());
OutputStreamWriter streamWriter = new OutputStreamWriter(out, UTF_8);
try (PrintWriter writer = new PrintWriter(streamWriter)) {
diff --git a/java/com/google/gerrit/server/patch/DiffExecutorModule.java b/java/com/google/gerrit/server/patch/DiffExecutorModule.java
index f3776e0..eb6a280 100644
--- a/java/com/google/gerrit/server/patch/DiffExecutorModule.java
+++ b/java/com/google/gerrit/server/patch/DiffExecutorModule.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.patch;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
-import com.google.gerrit.server.logging.LoggingContextAwareThreadFactory;
+import com.google.gerrit.server.logging.LoggingContextAwareExecutorService;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.Singleton;
@@ -32,11 +32,8 @@
@Singleton
@DiffExecutor
public ExecutorService createDiffExecutor() {
- return Executors.newCachedThreadPool(
- new ThreadFactoryBuilder()
- .setThreadFactory(new LoggingContextAwareThreadFactory())
- .setNameFormat("Diff-%d")
- .setDaemon(true)
- .build());
+ return new LoggingContextAwareExecutorService(
+ Executors.newCachedThreadPool(
+ new ThreadFactoryBuilder().setNameFormat("Diff-%d").setDaemon(true).build()));
}
}
diff --git a/java/com/google/gerrit/server/patch/DiffSummaryLoader.java b/java/com/google/gerrit/server/patch/DiffSummaryLoader.java
index 8bca19f..9153638 100644
--- a/java/com/google/gerrit/server/patch/DiffSummaryLoader.java
+++ b/java/com/google/gerrit/server/patch/DiffSummaryLoader.java
@@ -19,7 +19,6 @@
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
@@ -66,8 +65,9 @@
break;
}
}
- Collections.sort(r);
return new DiffSummary(
- r.toArray(new String[r.size()]), patchList.getInsertions(), patchList.getDeletions());
+ r.stream().sorted().toArray(String[]::new),
+ patchList.getInsertions(),
+ patchList.getDeletions());
}
}
diff --git a/java/com/google/gerrit/server/patch/PatchList.java b/java/com/google/gerrit/server/patch/PatchList.java
index cf5df4a..dd717ba 100644
--- a/java/com/google/gerrit/server/patch/PatchList.java
+++ b/java/com/google/gerrit/server/patch/PatchList.java
@@ -47,12 +47,7 @@
private static final long serialVersionUID = PatchListKey.serialVersionUID;
private static final Comparator<PatchListEntry> PATCH_CMP =
- new Comparator<PatchListEntry>() {
- @Override
- public int compare(PatchListEntry a, PatchListEntry b) {
- return comparePaths(a.getNewName(), b.getNewName());
- }
- };
+ Comparator.comparing(PatchListEntry::getNewName, PatchList::comparePaths);
@VisibleForTesting
static int comparePaths(String a, String b) {
diff --git a/java/com/google/gerrit/server/patch/PatchScriptBuilder.java b/java/com/google/gerrit/server/patch/PatchScriptBuilder.java
index a3d9048..61f0180 100644
--- a/java/com/google/gerrit/server/patch/PatchScriptBuilder.java
+++ b/java/com/google/gerrit/server/patch/PatchScriptBuilder.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.patch;
import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.Comparator.comparing;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.common.data.CommentDetail;
@@ -34,7 +35,6 @@
import eu.medsea.mimeutil.MimeUtil2;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
@@ -55,13 +55,7 @@
static final int MAX_CONTEXT = 5000000;
static final int BIG_FILE = 9000;
- private static final Comparator<Edit> EDIT_SORT =
- new Comparator<Edit>() {
- @Override
- public int compare(Edit o1, Edit o2) {
- return o1.getBeginA() - o2.getBeginA();
- }
- };
+ private static final Comparator<Edit> EDIT_SORT = comparing(Edit::getBeginA);
private Repository db;
private Project.NameKey projectKey;
@@ -369,7 +363,7 @@
// them correctly later.
//
edits.addAll(empty);
- Collections.sort(edits, EDIT_SORT);
+ edits.sort(EDIT_SORT);
}
private void safeAdd(List<Edit> empty, Edit toAdd) {
diff --git a/java/com/google/gerrit/server/permissions/DefaultPermissionBackend.java b/java/com/google/gerrit/server/permissions/DefaultPermissionBackend.java
index 136b4ae..51a0f95 100644
--- a/java/com/google/gerrit/server/permissions/DefaultPermissionBackend.java
+++ b/java/com/google/gerrit/server/permissions/DefaultPermissionBackend.java
@@ -19,6 +19,7 @@
import static java.util.stream.Collectors.toSet;
import com.google.common.collect.Sets;
+import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.PermissionRule;
import com.google.gerrit.common.data.PermissionRule.Action;
import com.google.gerrit.extensions.api.access.GlobalOrPluginPermission;
@@ -46,6 +47,8 @@
@Singleton
public class DefaultPermissionBackend extends PermissionBackend {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
private static final CurrentUser.PropertyKey<Boolean> IS_ADMIN = CurrentUser.PropertyKey.create();
private final Provider<CurrentUser> currentUser;
@@ -186,6 +189,13 @@
private boolean isAdmin() {
if (admin == null) {
admin = computeAdmin();
+ if (admin) {
+ logger.atFinest().log(
+ "user %s is an administrator of the server", user.getLoggableName());
+ } else {
+ logger.atFinest().log(
+ "user %s is not an administrator of the server", user.getLoggableName());
+ }
}
return admin;
}
@@ -210,11 +220,32 @@
private boolean canEmailReviewers() {
List<PermissionRule> email = capabilities().emailReviewers;
- return allow(email) || notDenied(email);
+ if (allow(email)) {
+ logger.atFinest().log(
+ "user %s can email reviewers (allowed by %s)", user.getLoggableName(), email);
+ return true;
+ }
+
+ if (notDenied(email)) {
+ logger.atFinest().log(
+ "user %s can email reviewers (not denied by %s)", user.getLoggableName(), email);
+ return true;
+ }
+
+ logger.atFinest().log("user %s cannot email reviewers", user.getLoggableName());
+ return false;
}
private boolean has(String permissionName) {
- return allow(capabilities().getPermission(checkNotNull(permissionName)));
+ boolean has = allow(capabilities().getPermission(checkNotNull(permissionName)));
+ if (has) {
+ logger.atFinest().log(
+ "user %s has global capability %s", user.getLoggableName(), permissionName);
+ } else {
+ logger.atFinest().log(
+ "user %s doesn't have global capability %s", user.getLoggableName(), permissionName);
+ }
+ return has;
}
private boolean allow(Collection<PermissionRule> rules) {
diff --git a/java/com/google/gerrit/server/permissions/SectionSortCache.java b/java/com/google/gerrit/server/permissions/SectionSortCache.java
index 48c8bff..e5392b0 100644
--- a/java/com/google/gerrit/server/permissions/SectionSortCache.java
+++ b/java/com/google/gerrit/server/permissions/SectionSortCache.java
@@ -26,7 +26,6 @@
import com.google.inject.Singleton;
import com.google.inject.name.Named;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.List;
@@ -88,7 +87,7 @@
poison |= srcMap.put(sections.get(i), i) != null;
}
- Collections.sort(sections, new MostSpecificComparator(ref));
+ sections.sort(new MostSpecificComparator(ref));
int[] srcIdx;
if (isIdentityTransform(sections, srcMap)) {
diff --git a/java/com/google/gerrit/server/plugins/PluginEntry.java b/java/com/google/gerrit/server/plugins/PluginEntry.java
index f7b1e82..3a6c7b2 100644
--- a/java/com/google/gerrit/server/plugins/PluginEntry.java
+++ b/java/com/google/gerrit/server/plugins/PluginEntry.java
@@ -14,6 +14,8 @@
package com.google.gerrit.server.plugins;
+import static java.util.Comparator.comparing;
+
import java.util.Collections;
import java.util.Comparator;
import java.util.Map;
@@ -28,13 +30,7 @@
public class PluginEntry {
public static final String ATTR_CHARACTER_ENCODING = "Character-Encoding";
public static final String ATTR_CONTENT_TYPE = "Content-Type";
- public static final Comparator<PluginEntry> COMPARATOR_BY_NAME =
- new Comparator<PluginEntry>() {
- @Override
- public int compare(PluginEntry a, PluginEntry b) {
- return a.getName().compareTo(b.getName());
- }
- };
+ public static final Comparator<PluginEntry> COMPARATOR_BY_NAME = comparing(PluginEntry::getName);
private static final Map<Object, String> EMPTY_ATTRS = Collections.emptyMap();
private static final Optional<Long> NO_SIZE = Optional.empty();
diff --git a/java/com/google/gerrit/server/project/ProjectCacheClock.java b/java/com/google/gerrit/server/project/ProjectCacheClock.java
index 188ee08..eb451fd 100644
--- a/java/com/google/gerrit/server/project/ProjectCacheClock.java
+++ b/java/com/google/gerrit/server/project/ProjectCacheClock.java
@@ -18,7 +18,7 @@
import com.google.gerrit.extensions.events.LifecycleListener;
import com.google.gerrit.server.config.ConfigUtil;
import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.logging.LoggingContextAwareThreadFactory;
+import com.google.gerrit.server.logging.LoggingContextAwareScheduledExecutorService;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.util.concurrent.Executors;
@@ -54,14 +54,14 @@
// Start with generation 1 (to avoid magic 0 below).
generation.set(1);
executor =
- Executors.newScheduledThreadPool(
- 1,
- new ThreadFactoryBuilder()
- .setThreadFactory(new LoggingContextAwareThreadFactory())
- .setNameFormat("ProjectCacheClock-%d")
- .setDaemon(true)
- .setPriority(Thread.MIN_PRIORITY)
- .build());
+ new LoggingContextAwareScheduledExecutorService(
+ Executors.newScheduledThreadPool(
+ 1,
+ new ThreadFactoryBuilder()
+ .setNameFormat("ProjectCacheClock-%d")
+ .setDaemon(true)
+ .setPriority(Thread.MIN_PRIORITY)
+ .build()));
@SuppressWarnings("unused") // Runnable already handles errors
Future<?> possiblyIgnoredError =
executor.scheduleAtFixedRate(
diff --git a/java/com/google/gerrit/server/project/ProjectCacheWarmer.java b/java/com/google/gerrit/server/project/ProjectCacheWarmer.java
index adfaf62..10cf2de 100644
--- a/java/com/google/gerrit/server/project/ProjectCacheWarmer.java
+++ b/java/com/google/gerrit/server/project/ProjectCacheWarmer.java
@@ -19,11 +19,11 @@
import com.google.gerrit.extensions.events.LifecycleListener;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.logging.LoggingContextAwareThreadFactory;
+import com.google.gerrit.server.logging.LoggingContextAwareExecutorService;
import com.google.inject.Inject;
import com.google.inject.Singleton;
+import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
-import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.lib.Config;
@@ -44,13 +44,11 @@
public void start() {
int cpus = Runtime.getRuntime().availableProcessors();
if (config.getBoolean("cache", "projects", "loadOnStartup", false)) {
- ThreadPoolExecutor pool =
- new ScheduledThreadPoolExecutor(
- config.getInt("cache", "projects", "loadThreads", cpus),
- new ThreadFactoryBuilder()
- .setThreadFactory(new LoggingContextAwareThreadFactory())
- .setNameFormat("ProjectCacheLoader-%d")
- .build());
+ ExecutorService pool =
+ new LoggingContextAwareExecutorService(
+ new ScheduledThreadPoolExecutor(
+ config.getInt("cache", "projects", "loadThreads", cpus),
+ new ThreadFactoryBuilder().setNameFormat("ProjectCacheLoader-%d").build()));
Thread scheduler =
new Thread(
() -> {
diff --git a/java/com/google/gerrit/server/project/ProjectConfig.java b/java/com/google/gerrit/server/project/ProjectConfig.java
index 59fc323..bccc415 100644
--- a/java/com/google/gerrit/server/project/ProjectConfig.java
+++ b/java/com/google/gerrit/server/project/ProjectConfig.java
@@ -15,8 +15,10 @@
package com.google.gerrit.server.project;
import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.gerrit.common.data.Permission.isPermission;
import static com.google.gerrit.reviewdb.client.Project.DEFAULT_SUBMIT_TYPE;
+import static java.util.stream.Collectors.toList;
import com.google.common.base.CharMatcher;
import com.google.common.base.Joiner;
@@ -70,6 +72,7 @@
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Pattern;
@@ -89,6 +92,7 @@
public static final String KEY_DEFAULT_VALUE = "defaultValue";
public static final String KEY_COPY_MIN_SCORE = "copyMinScore";
public static final String KEY_ALLOW_POST_SUBMIT = "allowPostSubmit";
+ public static final String KEY_IGNORE_SELF_APPROVAL = "ignoreSelfApproval";
public static final String KEY_COPY_MAX_SCORE = "copyMaxScore";
public static final String KEY_COPY_ALL_SCORES_ON_MERGE_FIRST_PARENT_UPDATE =
"copyAllScoresOnMergeFirstParentUpdate";
@@ -880,6 +884,8 @@
}
label.setAllowPostSubmit(
rc.getBoolean(LABEL, name, KEY_ALLOW_POST_SUBMIT, LabelType.DEF_ALLOW_POST_SUBMIT));
+ label.setIgnoreSelfApproval(
+ rc.getBoolean(LABEL, name, KEY_IGNORE_SELF_APPROVAL, LabelType.DEF_IGNORE_SELF_APPROVAL));
label.setCopyMinScore(
rc.getBoolean(LABEL, name, KEY_COPY_MIN_SCORE, LabelType.DEF_COPY_MIN_SCORE));
label.setCopyMaxScore(
@@ -1162,21 +1168,20 @@
private void saveNotifySections(Config rc, Set<AccountGroup.UUID> keepGroups) {
for (NotifyConfig nc : sort(notifySections.values())) {
- List<String> email = new ArrayList<>();
- for (GroupReference gr : nc.getGroups()) {
- if (gr.getUUID() != null) {
- keepGroups.add(gr.getUUID());
- }
- email.add(new PermissionRule(gr).asString(false));
- }
- Collections.sort(email);
+ nc.getGroups()
+ .stream()
+ .map(gr -> gr.getUUID())
+ .filter(Objects::nonNull)
+ .forEach(keepGroups::add);
+ List<String> email =
+ nc.getGroups()
+ .stream()
+ .map(gr -> new PermissionRule(gr).asString(false))
+ .sorted()
+ .collect(toList());
- List<String> addrs = new ArrayList<>();
- for (Address addr : nc.getAddresses()) {
- addrs.add(addr.toString());
- }
- Collections.sort(addrs);
- email.addAll(addrs);
+ // Separate stream operation so that emails list contains 2 sorted sub-lists.
+ nc.getAddresses().stream().map(Address::toString).sorted().forEach(email::add);
set(rc, NOTIFY, nc.getName(), KEY_HEADER, nc.getHeader(), NotifyConfig.Header.BCC);
if (email.isEmpty()) {
@@ -1320,6 +1325,13 @@
rc,
LABEL,
name,
+ KEY_IGNORE_SELF_APPROVAL,
+ label.ignoreSelfApproval(),
+ LabelType.DEF_IGNORE_SELF_APPROVAL);
+ setBooleanConfigKey(
+ rc,
+ LABEL,
+ name,
KEY_COPY_MIN_SCORE,
label.isCopyMinScore(),
LabelType.DEF_COPY_MIN_SCORE);
@@ -1450,10 +1462,8 @@
validationErrors.add(error);
}
- private static <T extends Comparable<? super T>> List<T> sort(Collection<T> m) {
- ArrayList<T> r = new ArrayList<>(m);
- Collections.sort(r);
- return r;
+ private static <T extends Comparable<? super T>> ImmutableList<T> sort(Collection<T> m) {
+ return m.stream().sorted().collect(toImmutableList());
}
public boolean hasLegacyPermissions() {
diff --git a/java/com/google/gerrit/server/restapi/account/GetEmails.java b/java/com/google/gerrit/server/restapi/account/GetEmails.java
index 85262ee..ed3347f 100644
--- a/java/com/google/gerrit/server/restapi/account/GetEmails.java
+++ b/java/com/google/gerrit/server/restapi/account/GetEmails.java
@@ -14,6 +14,9 @@
package com.google.gerrit.server.restapi.account;
+import static java.util.Comparator.comparing;
+import static java.util.stream.Collectors.toList;
+
import com.google.gerrit.extensions.common.EmailInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.RestReadView;
@@ -25,10 +28,8 @@
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.List;
+import java.util.Objects;
@Singleton
public class GetEmails implements RestReadView<AccountResource> {
@@ -47,24 +48,19 @@
if (!self.get().hasSameAccountId(rsrc.getUser())) {
permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
}
+ return rsrc.getUser()
+ .getEmailAddresses()
+ .stream()
+ .filter(Objects::nonNull)
+ .map(e -> toEmailInfo(rsrc, e))
+ .sorted(comparing((EmailInfo e) -> e.email))
+ .collect(toList());
+ }
- List<EmailInfo> emails = new ArrayList<>();
- for (String email : rsrc.getUser().getEmailAddresses()) {
- if (email != null) {
- EmailInfo e = new EmailInfo();
- e.email = email;
- e.preferred(rsrc.getUser().getAccount().getPreferredEmail());
- emails.add(e);
- }
- }
- Collections.sort(
- emails,
- new Comparator<EmailInfo>() {
- @Override
- public int compare(EmailInfo a, EmailInfo b) {
- return a.email.compareTo(b.email);
- }
- });
- return emails;
+ private static EmailInfo toEmailInfo(AccountResource rsrc, String email) {
+ EmailInfo e = new EmailInfo();
+ e.email = email;
+ e.preferred(rsrc.getUser().getAccount().getPreferredEmail());
+ return e;
}
}
diff --git a/java/com/google/gerrit/server/restapi/account/GetWatchedProjects.java b/java/com/google/gerrit/server/restapi/account/GetWatchedProjects.java
index 112bb24..61021be 100644
--- a/java/com/google/gerrit/server/restapi/account/GetWatchedProjects.java
+++ b/java/com/google/gerrit/server/restapi/account/GetWatchedProjects.java
@@ -14,8 +14,10 @@
package com.google.gerrit.server.restapi.account;
+import static java.util.Comparator.comparing;
+import static java.util.stream.Collectors.toList;
+
import com.google.common.base.Strings;
-import com.google.common.collect.ComparisonChain;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.extensions.client.ProjectWatchInfo;
import com.google.gerrit.extensions.restapi.AuthException;
@@ -36,11 +38,7 @@
import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.List;
-import java.util.Map;
import org.eclipse.jgit.errors.ConfigInvalidException;
@Singleton
@@ -67,31 +65,28 @@
Account.Id accountId = rsrc.getUser().getAccountId();
AccountState account = accounts.get(accountId).orElseThrow(ResourceNotFoundException::new);
- List<ProjectWatchInfo> projectWatchInfos = new ArrayList<>();
- for (Map.Entry<ProjectWatchKey, ImmutableSet<NotifyType>> e :
- account.getProjectWatches().entrySet()) {
- ProjectWatchInfo pwi = new ProjectWatchInfo();
- pwi.filter = e.getKey().filter();
- pwi.project = e.getKey().project().get();
- pwi.notifyAbandonedChanges = toBoolean(e.getValue().contains(NotifyType.ABANDONED_CHANGES));
- pwi.notifyNewChanges = toBoolean(e.getValue().contains(NotifyType.NEW_CHANGES));
- pwi.notifyNewPatchSets = toBoolean(e.getValue().contains(NotifyType.NEW_PATCHSETS));
- pwi.notifySubmittedChanges = toBoolean(e.getValue().contains(NotifyType.SUBMITTED_CHANGES));
- pwi.notifyAllComments = toBoolean(e.getValue().contains(NotifyType.ALL_COMMENTS));
- projectWatchInfos.add(pwi);
- }
- Collections.sort(
- projectWatchInfos,
- new Comparator<ProjectWatchInfo>() {
- @Override
- public int compare(ProjectWatchInfo pwi1, ProjectWatchInfo pwi2) {
- return ComparisonChain.start()
- .compare(pwi1.project, pwi2.project)
- .compare(Strings.nullToEmpty(pwi1.filter), Strings.nullToEmpty(pwi2.filter))
- .result();
- }
- });
- return projectWatchInfos;
+ return account
+ .getProjectWatches()
+ .entrySet()
+ .stream()
+ .map(e -> toProjectWatchInfo(e.getKey(), e.getValue()))
+ .sorted(
+ comparing((ProjectWatchInfo pwi) -> pwi.project)
+ .thenComparing(pwi -> Strings.nullToEmpty(pwi.filter)))
+ .collect(toList());
+ }
+
+ private static ProjectWatchInfo toProjectWatchInfo(
+ ProjectWatchKey key, ImmutableSet<NotifyType> watchTypes) {
+ ProjectWatchInfo pwi = new ProjectWatchInfo();
+ pwi.filter = key.filter();
+ pwi.project = key.project().get();
+ pwi.notifyAbandonedChanges = toBoolean(watchTypes.contains(NotifyType.ABANDONED_CHANGES));
+ pwi.notifyNewChanges = toBoolean(watchTypes.contains(NotifyType.NEW_CHANGES));
+ pwi.notifyNewPatchSets = toBoolean(watchTypes.contains(NotifyType.NEW_PATCHSETS));
+ pwi.notifySubmittedChanges = toBoolean(watchTypes.contains(NotifyType.SUBMITTED_CHANGES));
+ pwi.notifyAllComments = toBoolean(watchTypes.contains(NotifyType.ALL_COMMENTS));
+ return pwi;
}
private static Boolean toBoolean(boolean value) {
diff --git a/java/com/google/gerrit/server/restapi/change/CommentJson.java b/java/com/google/gerrit/server/restapi/change/CommentJson.java
index e92abe1..a562592 100644
--- a/java/com/google/gerrit/server/restapi/change/CommentJson.java
+++ b/java/com/google/gerrit/server/restapi/change/CommentJson.java
@@ -14,11 +14,13 @@
package com.google.gerrit.server.restapi.change;
+import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.gerrit.server.CommentsUtil.COMMENT_INFO_ORDER;
import static java.util.stream.Collectors.toList;
import com.google.common.base.Strings;
-import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Streams;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.client.Comment.Range;
import com.google.gerrit.extensions.client.Side;
@@ -35,7 +37,6 @@
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.inject.Inject;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
@@ -96,9 +97,7 @@
list.add(o);
}
- for (List<T> list : out.values()) {
- Collections.sort(list, COMMENT_INFO_ORDER);
- }
+ out.values().forEach(l -> l.sort(COMMENT_INFO_ORDER));
if (loader != null) {
loader.fill();
@@ -106,13 +105,14 @@
return out;
}
- public List<T> formatAsList(Iterable<F> comments) throws PermissionBackendException {
+ public ImmutableList<T> formatAsList(Iterable<F> comments) throws PermissionBackendException {
AccountLoader loader = fillAccounts ? accountLoaderFactory.create(true) : null;
- List<T> out =
- FluentIterable.from(comments)
- .transform(c -> toInfo(c, loader))
- .toSortedList(COMMENT_INFO_ORDER);
+ ImmutableList<T> out =
+ Streams.stream(comments)
+ .map(c -> toInfo(c, loader))
+ .sorted(COMMENT_INFO_ORDER)
+ .collect(toImmutableList());
if (loader != null) {
loader.fill();
diff --git a/java/com/google/gerrit/server/restapi/change/ListRevisionDrafts.java b/java/com/google/gerrit/server/restapi/change/ListRevisionDrafts.java
index db8ef0c..dbd0ccf 100644
--- a/java/com/google/gerrit/server/restapi/change/ListRevisionDrafts.java
+++ b/java/com/google/gerrit/server/restapi/change/ListRevisionDrafts.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.restapi.change;
+import com.google.common.collect.ImmutableList;
import com.google.gerrit.extensions.common.CommentInfo;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.reviewdb.client.Comment;
@@ -61,7 +62,7 @@
.format(listComments(rsrc));
}
- public List<CommentInfo> getComments(RevisionResource rsrc)
+ public ImmutableList<CommentInfo> getComments(RevisionResource rsrc)
throws OrmException, PermissionBackendException {
return commentJson
.get()
diff --git a/java/com/google/gerrit/server/restapi/change/ListRobotComments.java b/java/com/google/gerrit/server/restapi/change/ListRobotComments.java
index 66138ab..99366aa 100644
--- a/java/com/google/gerrit/server/restapi/change/ListRobotComments.java
+++ b/java/com/google/gerrit/server/restapi/change/ListRobotComments.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.restapi.change;
+import com.google.common.collect.ImmutableList;
import com.google.gerrit.extensions.common.RobotCommentInfo;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.reviewdb.client.RobotComment;
@@ -52,7 +53,7 @@
.format(listComments(rsrc));
}
- public List<RobotCommentInfo> getComments(RevisionResource rsrc)
+ public ImmutableList<RobotCommentInfo> getComments(RevisionResource rsrc)
throws OrmException, PermissionBackendException {
return commentJson
.get()
diff --git a/java/com/google/gerrit/server/restapi/config/ListCaches.java b/java/com/google/gerrit/server/restapi/config/ListCaches.java
index c0a9d71..38664fb 100644
--- a/java/com/google/gerrit/server/restapi/config/ListCaches.java
+++ b/java/com/google/gerrit/server/restapi/config/ListCaches.java
@@ -14,14 +14,16 @@
package com.google.gerrit.server.restapi.config;
+import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.gerrit.common.data.GlobalCapability.MAINTAIN_SERVER;
import static com.google.gerrit.common.data.GlobalCapability.VIEW_CACHES;
import static com.google.gerrit.server.config.CacheResource.cacheNameOf;
import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.stream.Collectors.joining;
-import com.google.common.base.Joiner;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheStats;
+import com.google.common.collect.Streams;
import com.google.gerrit.extensions.annotations.RequiresAnyCapability;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.BinaryResult;
@@ -29,11 +31,9 @@
import com.google.gerrit.server.cache.PersistentCache;
import com.google.gerrit.server.config.ConfigResource;
import com.google.inject.Inject;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
import java.util.Map;
import java.util.TreeMap;
+import java.util.stream.Stream;
import org.kohsuke.args4j.Option;
@RequiresAnyCapability({VIEW_CACHES, MAINTAIN_SERVER})
@@ -72,19 +72,17 @@
if (format == null) {
return getCacheInfos();
}
- List<String> cacheNames = new ArrayList<>();
- for (DynamicMap.Entry<Cache<?, ?>> e : cacheMap) {
- cacheNames.add(cacheNameOf(e.getPluginName(), e.getExportName()));
- }
- Collections.sort(cacheNames);
-
+ Stream<String> cacheNames =
+ Streams.stream(cacheMap)
+ .map(e -> cacheNameOf(e.getPluginName(), e.getExportName()))
+ .sorted();
if (OutputFormat.TEXT_LIST.equals(format)) {
- return BinaryResult.create(Joiner.on('\n').join(cacheNames))
+ return BinaryResult.create(cacheNames.collect(joining("\n")))
.base64()
.setContentType("text/plain")
.setCharacterEncoding(UTF_8);
}
- return cacheNames;
+ return cacheNames.collect(toImmutableList());
}
public enum CacheType {
diff --git a/java/com/google/gerrit/server/restapi/config/ListTasks.java b/java/com/google/gerrit/server/restapi/config/ListTasks.java
index 7b69831..f77cda4 100644
--- a/java/com/google/gerrit/server/restapi/config/ListTasks.java
+++ b/java/com/google/gerrit/server/restapi/config/ListTasks.java
@@ -14,7 +14,9 @@
package com.google.gerrit.server.restapi.config;
-import com.google.common.collect.ComparisonChain;
+import static java.util.Comparator.comparing;
+import static java.util.stream.Collectors.toList;
+
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.reviewdb.client.Project;
@@ -35,8 +37,6 @@
import com.google.inject.Singleton;
import java.sql.Timestamp;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -106,20 +106,14 @@
}
private List<TaskInfo> getTasks() {
- List<TaskInfo> taskInfos = workQueue.getTaskInfos(TaskInfo::new);
- Collections.sort(
- taskInfos,
- new Comparator<TaskInfo>() {
- @Override
- public int compare(TaskInfo a, TaskInfo b) {
- return ComparisonChain.start()
- .compare(a.state.ordinal(), b.state.ordinal())
- .compare(a.delay, b.delay)
- .compare(a.command, b.command)
- .result();
- }
- });
- return taskInfos;
+ return workQueue
+ .getTaskInfos(TaskInfo::new)
+ .stream()
+ .sorted(
+ comparing((TaskInfo t) -> t.state.ordinal())
+ .thenComparing(t -> t.delay)
+ .thenComparing(t -> t.command))
+ .collect(toList());
}
public static class TaskInfo {
diff --git a/java/com/google/gerrit/server/restapi/group/GetAuditLog.java b/java/com/google/gerrit/server/restapi/group/GetAuditLog.java
index 7af4284..dcdd8a8 100644
--- a/java/com/google/gerrit/server/restapi/group/GetAuditLog.java
+++ b/java/com/google/gerrit/server/restapi/group/GetAuditLog.java
@@ -41,7 +41,6 @@
import com.google.inject.Singleton;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.Optional;
import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -138,7 +137,7 @@
accountLoader.fill();
// sort by date and then reverse so that the newest audit event comes first
- Collections.sort(auditEvents, comparing((GroupAuditEventInfo a) -> a.date).reversed());
+ auditEvents.sort(comparing((GroupAuditEventInfo a) -> a.date).reversed());
return auditEvents;
}
}
diff --git a/java/com/google/gerrit/server/restapi/group/ListSubgroups.java b/java/com/google/gerrit/server/restapi/group/ListSubgroups.java
index 97a260e..864b01b 100644
--- a/java/com/google/gerrit/server/restapi/group/ListSubgroups.java
+++ b/java/com/google/gerrit/server/restapi/group/ListSubgroups.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.group;
import static com.google.common.base.Strings.nullToEmpty;
+import static java.util.Comparator.comparing;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.GroupDescription;
@@ -29,8 +30,6 @@
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.List;
@Singleton
@@ -72,18 +71,8 @@
continue;
}
}
- Collections.sort(
- included,
- new Comparator<GroupInfo>() {
- @Override
- public int compare(GroupInfo a, GroupInfo b) {
- int cmp = nullToEmpty(a.name).compareTo(nullToEmpty(b.name));
- if (cmp != 0) {
- return cmp;
- }
- return nullToEmpty(a.id).compareTo(nullToEmpty(b.id));
- }
- });
+ included.sort(
+ comparing((GroupInfo g) -> nullToEmpty(g.name)).thenComparing(g -> nullToEmpty(g.id)));
return included;
}
}
diff --git a/java/com/google/gerrit/server/restapi/project/ListBranches.java b/java/com/google/gerrit/server/restapi/project/ListBranches.java
index bf4a547..a0d2528 100644
--- a/java/com/google/gerrit/server/restapi/project/ListBranches.java
+++ b/java/com/google/gerrit/server/restapi/project/ListBranches.java
@@ -46,7 +46,6 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
@@ -226,7 +225,7 @@
// Do nothing.
}
}
- Collections.sort(branches, new BranchComparator());
+ branches.sort(new BranchComparator());
return branches;
}
diff --git a/java/com/google/gerrit/server/restapi/project/ListTags.java b/java/com/google/gerrit/server/restapi/project/ListTags.java
index e79fdca..f59e984 100644
--- a/java/com/google/gerrit/server/restapi/project/ListTags.java
+++ b/java/com/google/gerrit/server/restapi/project/ListTags.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.project;
import static com.google.gerrit.reviewdb.client.RefNames.isConfigRef;
+import static java.util.Comparator.comparing;
import com.google.common.collect.ImmutableMap;
import com.google.gerrit.extensions.api.projects.ProjectApi.ListRefsRequest;
@@ -39,8 +40,6 @@
import java.io.IOException;
import java.sql.Timestamp;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.List;
import java.util.Map;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
@@ -135,14 +134,7 @@
}
}
- Collections.sort(
- tags,
- new Comparator<TagInfo>() {
- @Override
- public int compare(TagInfo a, TagInfo b) {
- return a.ref.compareTo(b.ref);
- }
- });
+ tags.sort(comparing(t -> t.ref));
return new RefFilter<TagInfo>(Constants.R_TAGS)
.start(start)
diff --git a/java/com/google/gerrit/server/rules/IgnoreSelfApprovalRule.java b/java/com/google/gerrit/server/rules/IgnoreSelfApprovalRule.java
new file mode 100644
index 0000000..b9ddbc6
--- /dev/null
+++ b/java/com/google/gerrit/server/rules/IgnoreSelfApprovalRule.java
@@ -0,0 +1,171 @@
+// Copyright (C) 2018 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.rules;
+
+import static com.google.common.collect.ImmutableList.toImmutableList;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableList;
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.common.data.LabelFunction;
+import com.google.gerrit.common.data.LabelType;
+import com.google.gerrit.common.data.SubmitRecord;
+import com.google.gerrit.common.data.SubmitRequirement;
+import com.google.gerrit.extensions.annotations.Exports;
+import com.google.gerrit.extensions.config.FactoryModule;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.PatchSetApproval;
+import com.google.gerrit.server.project.SubmitRuleOptions;
+import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Rule to require an approval from a user that did not upload the current patch set or block
+ * submission.
+ */
+@Singleton
+public class IgnoreSelfApprovalRule implements SubmitRule {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+ private static final String E_UNABLE_TO_FETCH_UPLOADER = "Unable to fetch uploader";
+ private static final String E_UNABLE_TO_FETCH_LABELS =
+ "Unable to fetch labels and approvals for the change";
+
+ public static class Module extends FactoryModule {
+ @Override
+ public void configure() {
+ bind(SubmitRule.class)
+ .annotatedWith(Exports.named("IgnoreSelfApprovalRule"))
+ .to(IgnoreSelfApprovalRule.class);
+ }
+ }
+
+ @Inject
+ IgnoreSelfApprovalRule() {}
+
+ @Override
+ public Collection<SubmitRecord> evaluate(ChangeData cd, SubmitRuleOptions options) {
+ List<LabelType> labelTypes;
+ List<PatchSetApproval> approvals;
+ try {
+ labelTypes = cd.getLabelTypes().getLabelTypes();
+ approvals = cd.currentApprovals();
+ } catch (OrmException e) {
+ logger.atWarning().withCause(e).log(E_UNABLE_TO_FETCH_LABELS);
+ return singletonRuleError(E_UNABLE_TO_FETCH_LABELS);
+ }
+
+ boolean shouldIgnoreSelfApproval = labelTypes.stream().anyMatch(l -> l.ignoreSelfApproval());
+ if (!shouldIgnoreSelfApproval) {
+ // Shortcut to avoid further processing if no label should ignore uploader approvals
+ return ImmutableList.of();
+ }
+
+ Account.Id uploader;
+ try {
+ uploader = cd.currentPatchSet().getUploader();
+ } catch (OrmException e) {
+ logger.atWarning().withCause(e).log(E_UNABLE_TO_FETCH_UPLOADER);
+ return singletonRuleError(E_UNABLE_TO_FETCH_UPLOADER);
+ }
+
+ SubmitRecord submitRecord = new SubmitRecord();
+ submitRecord.status = SubmitRecord.Status.OK;
+ submitRecord.labels = new ArrayList<>(labelTypes.size());
+ submitRecord.requirements = new ArrayList<>();
+
+ for (LabelType t : labelTypes) {
+ if (!t.ignoreSelfApproval()) {
+ // The default rules are enough in this case.
+ continue;
+ }
+
+ LabelFunction labelFunction = t.getFunction();
+ if (labelFunction == null) {
+ continue;
+ }
+
+ Collection<PatchSetApproval> allApprovalsForLabel = filterApprovalsByLabel(approvals, t);
+ SubmitRecord.Label allApprovalsCheckResult = labelFunction.check(t, allApprovalsForLabel);
+ SubmitRecord.Label ignoreSelfApprovalCheckResult =
+ labelFunction.check(t, filterOutPositiveApprovalsOfUser(allApprovalsForLabel, uploader));
+
+ if (labelCheckPassed(allApprovalsCheckResult)
+ && !labelCheckPassed(ignoreSelfApprovalCheckResult)) {
+ // The label has a valid approval from the uploader and no other valid approval. Set the
+ // label
+ // to NOT_READY and indicate the need for non-uploader approval as requirement.
+ submitRecord.labels.add(ignoreSelfApprovalCheckResult);
+ submitRecord.status = SubmitRecord.Status.NOT_READY;
+ // Add an additional requirement to be more descriptive on why the label counts as not
+ // approved.
+ submitRecord.requirements.add(
+ SubmitRequirement.builder()
+ .setFallbackText("Approval from non-uploader required")
+ .setType("non_uploader_approval")
+ .build());
+ }
+ }
+
+ if (submitRecord.labels.isEmpty()) {
+ return ImmutableList.of();
+ }
+
+ return ImmutableList.of(submitRecord);
+ }
+
+ private static boolean labelCheckPassed(SubmitRecord.Label label) {
+ switch (label.status) {
+ case OK:
+ case MAY:
+ return true;
+
+ case NEED:
+ case REJECT:
+ case IMPOSSIBLE:
+ return false;
+ }
+ return false;
+ }
+
+ private static Collection<SubmitRecord> singletonRuleError(String reason) {
+ SubmitRecord submitRecord = new SubmitRecord();
+ submitRecord.errorMessage = reason;
+ submitRecord.status = SubmitRecord.Status.RULE_ERROR;
+ return ImmutableList.of(submitRecord);
+ }
+
+ @VisibleForTesting
+ static Collection<PatchSetApproval> filterOutPositiveApprovalsOfUser(
+ Collection<PatchSetApproval> approvals, Account.Id user) {
+ return approvals
+ .stream()
+ .filter(input -> input.getValue() < 0 || !input.getAccountId().equals(user))
+ .collect(toImmutableList());
+ }
+
+ @VisibleForTesting
+ static Collection<PatchSetApproval> filterApprovalsByLabel(
+ Collection<PatchSetApproval> approvals, LabelType t) {
+ return approvals
+ .stream()
+ .filter(input -> input.getLabelId().get().equals(t.getLabelId().get()))
+ .collect(toImmutableList());
+ }
+}
diff --git a/java/com/google/gerrit/server/submit/MergeOp.java b/java/com/google/gerrit/server/submit/MergeOp.java
index 9a8895b..43d5f75 100644
--- a/java/com/google/gerrit/server/submit/MergeOp.java
+++ b/java/com/google/gerrit/server/submit/MergeOp.java
@@ -457,7 +457,7 @@
this.caller = caller;
this.ts = TimeUtil.nowTs();
this.db = db;
- this.submissionId = RequestId.forChange(change);
+ this.submissionId = new RequestId(change.getId().toString());
try (TraceContext traceContext =
TraceContext.open().addTag(RequestId.Type.SUBMISSION_ID, submissionId)) {
diff --git a/java/com/google/gerrit/server/submit/SubmitStrategyOp.java b/java/com/google/gerrit/server/submit/SubmitStrategyOp.java
index 290e917..51dad5b 100644
--- a/java/com/google/gerrit/server/submit/SubmitStrategyOp.java
+++ b/java/com/google/gerrit/server/submit/SubmitStrategyOp.java
@@ -53,7 +53,6 @@
import com.google.gwtorm.server.OrmException;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -184,8 +183,7 @@
continue; // Bogus ref, can't be merged into tip so we don't care.
}
}
- Collections.sort(
- commits,
+ commits.sort(
ReviewDbUtil.intKeyOrdering().reverse().onResultOf(CodeReviewCommit::getPatchsetId));
CodeReviewCommit result = MergeUtil.findAnyMergedInto(rw, commits, tip);
if (result == null) {
diff --git a/java/com/google/gerrit/server/update/RetryHelper.java b/java/com/google/gerrit/server/update/RetryHelper.java
index 132c04b..10e3455 100644
--- a/java/com/google/gerrit/server/update/RetryHelper.java
+++ b/java/com/google/gerrit/server/update/RetryHelper.java
@@ -31,6 +31,7 @@
import com.google.common.base.Predicate;
import com.google.common.base.Throwables;
import com.google.common.collect.Maps;
+import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.metrics.Counter1;
@@ -52,6 +53,8 @@
@Singleton
public class RetryHelper {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
@FunctionalInterface
public interface ChangeAction<T> {
T call(BatchUpdate.Factory batchUpdateFactory) throws Exception;
@@ -277,6 +280,9 @@
retryerBuilder.withRetryListener(listener);
return executeWithTimeoutCount(actionType, action, retryerBuilder.build());
} finally {
+ if (listener.getAttemptCount() > 1) {
+ logger.atFine().log("%s was attempted %d times", actionType, listener.getAttemptCount());
+ }
metrics.attemptCounts.record(actionType, listener.getAttemptCount());
}
}
diff --git a/java/com/google/gerrit/sshd/BUILD b/java/com/google/gerrit/sshd/BUILD
index 4743b35..47b1d89 100644
--- a/java/com/google/gerrit/sshd/BUILD
+++ b/java/com/google/gerrit/sshd/BUILD
@@ -15,6 +15,7 @@
"//java/com/google/gerrit/server/cache/h2",
"//java/com/google/gerrit/server/git/receive",
"//java/com/google/gerrit/server/ioutil",
+ "//java/com/google/gerrit/server/logging",
"//java/com/google/gerrit/server/restapi",
"//java/com/google/gerrit/server/schema",
"//java/com/google/gerrit/util/cli",
diff --git a/java/com/google/gerrit/sshd/CommandFactoryProvider.java b/java/com/google/gerrit/sshd/CommandFactoryProvider.java
index 68ea7bb..1fdf7d8 100644
--- a/java/com/google/gerrit/sshd/CommandFactoryProvider.java
+++ b/java/com/google/gerrit/sshd/CommandFactoryProvider.java
@@ -22,7 +22,7 @@
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.git.WorkQueue;
-import com.google.gerrit.server.logging.LoggingContextAwareThreadFactory;
+import com.google.gerrit.server.logging.LoggingContextAwareExecutorService;
import com.google.gerrit.sshd.SshScope.Context;
import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
@@ -78,12 +78,12 @@
int threads = cfg.getInt("sshd", "commandStartThreads", 2);
startExecutor = workQueue.createQueue(threads, "SshCommandStart", true);
destroyExecutor =
- Executors.newSingleThreadExecutor(
- new ThreadFactoryBuilder()
- .setThreadFactory(new LoggingContextAwareThreadFactory())
- .setNameFormat("SshCommandDestroy-%s")
- .setDaemon(true)
- .build());
+ new LoggingContextAwareExecutorService(
+ Executors.newSingleThreadExecutor(
+ new ThreadFactoryBuilder()
+ .setNameFormat("SshCommandDestroy-%s")
+ .setDaemon(true)
+ .build()));
}
@Override
diff --git a/java/com/google/gerrit/sshd/commands/ShowConnections.java b/java/com/google/gerrit/sshd/commands/ShowConnections.java
index 9b517c6..baadf02 100644
--- a/java/com/google/gerrit/sshd/commands/ShowConnections.java
+++ b/java/com/google/gerrit/sshd/commands/ShowConnections.java
@@ -15,8 +15,10 @@
package com.google.gerrit.sshd.commands;
import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.gerrit.sshd.CommandMetaData.Mode.MASTER_OR_SLAVE;
+import com.google.common.collect.ImmutableList;
import com.google.gerrit.common.TimeUtil;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.extensions.annotations.RequiresCapability;
@@ -33,11 +35,7 @@
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.Date;
-import java.util.List;
import java.util.Optional;
import org.apache.sshd.common.io.IoAcceptor;
import org.apache.sshd.common.io.IoSession;
@@ -92,25 +90,27 @@
throw new Failure(1, "fatal: sshd no longer running");
}
- final List<IoSession> list = new ArrayList<>(acceptor.getManagedSessions().values());
- Collections.sort(
- list,
- new Comparator<IoSession>() {
- @Override
- public int compare(IoSession arg0, IoSession arg1) {
- if (arg0 instanceof MinaSession) {
- MinaSession mArg0 = (MinaSession) arg0;
- MinaSession mArg1 = (MinaSession) arg1;
- if (mArg0.getSession().getCreationTime() < mArg1.getSession().getCreationTime()) {
- return -1;
- } else if (mArg0.getSession().getCreationTime()
- > mArg1.getSession().getCreationTime()) {
- return 1;
- }
- }
- return (int) (arg0.getId() - arg1.getId());
- }
- });
+ final ImmutableList<IoSession> list =
+ acceptor
+ .getManagedSessions()
+ .values()
+ .stream()
+ .sorted(
+ (arg0, arg1) -> {
+ if (arg0 instanceof MinaSession) {
+ MinaSession mArg0 = (MinaSession) arg0;
+ MinaSession mArg1 = (MinaSession) arg1;
+ if (mArg0.getSession().getCreationTime()
+ < mArg1.getSession().getCreationTime()) {
+ return -1;
+ } else if (mArg0.getSession().getCreationTime()
+ > mArg1.getSession().getCreationTime()) {
+ return 1;
+ }
+ }
+ return (int) (arg0.getId() - arg1.getId());
+ })
+ .collect(toImmutableList());
hostNameWidth = wide ? Integer.MAX_VALUE : columns - 9 - 9 - 10 - 32;
diff --git a/java/com/google/gerrit/testing/BUILD b/java/com/google/gerrit/testing/BUILD
index cf65908..15ceb77 100644
--- a/java/com/google/gerrit/testing/BUILD
+++ b/java/com/google/gerrit/testing/BUILD
@@ -29,6 +29,7 @@
"//java/com/google/gerrit/server/audit",
"//java/com/google/gerrit/server/cache/h2",
"//java/com/google/gerrit/server/cache/mem",
+ "//java/com/google/gerrit/server/logging",
"//java/com/google/gerrit/server/restapi",
"//java/com/google/gerrit/server/schema",
"//lib:guava",
diff --git a/java/com/google/gwtexpui/globalkey/client/KeyHelpPopup.java b/java/com/google/gwtexpui/globalkey/client/KeyHelpPopup.java
index 1318125..20d093e 100644
--- a/java/com/google/gwtexpui/globalkey/client/KeyHelpPopup.java
+++ b/java/com/google/gwtexpui/globalkey/client/KeyHelpPopup.java
@@ -14,6 +14,9 @@
package com.google.gwtexpui.globalkey.client;
+import static java.util.Comparator.comparing;
+import static java.util.stream.Collectors.toList;
+
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
@@ -31,10 +34,7 @@
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwtexpui.safehtml.client.SafeHtml;
import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
@@ -228,15 +228,6 @@
}
private List<KeyCommand> sort(KeyCommandSet set) {
- final List<KeyCommand> keys = new ArrayList<>(set.getKeys());
- Collections.sort(
- keys,
- new Comparator<KeyCommand>() {
- @Override
- public int compare(KeyCommand arg0, KeyCommand arg1) {
- return arg0.getHelpText().compareTo(arg1.getHelpText());
- }
- });
- return keys;
+ return set.getKeys().stream().sorted(comparing(KeyCommand::getHelpText)).collect(toList());
}
}
diff --git a/java/com/google/gwtexpui/safehtml/client/HighlightSuggestOracle.java b/java/com/google/gwtexpui/safehtml/client/HighlightSuggestOracle.java
index ef80cdb..758521f 100644
--- a/java/com/google/gwtexpui/safehtml/client/HighlightSuggestOracle.java
+++ b/java/com/google/gwtexpui/safehtml/client/HighlightSuggestOracle.java
@@ -14,11 +14,12 @@
package com.google.gwtexpui.safehtml.client;
+import static java.util.Comparator.comparing;
+import static java.util.stream.Collectors.toList;
+
import com.google.gwt.user.client.ui.SuggestOracle;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.List;
/**
@@ -115,15 +116,8 @@
* terms.
*/
private static List<String> splitQuery(String query) {
- List<String> queryTerms = Arrays.asList(query.split("\\s+"));
- Collections.sort(
- queryTerms,
- new Comparator<String>() {
- @Override
- public int compare(String s1, String s2) {
- return Integer.compare(s2.length(), s1.length());
- }
- });
+ List<String> queryTerms =
+ Arrays.stream(query.split("\\s+")).sorted(comparing(String::length)).collect(toList());
List<String> result = new ArrayList<>();
for (String s : queryTerms) {
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
index b362b2b..7126354 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
@@ -526,9 +526,9 @@
@Test
public void validateAccountActivation() throws Exception {
- com.google.gerrit.acceptance.testsuite.account.TestAccount activatableAccount =
+ Account.Id activatableAccountId =
accountOperations.newAccount().inactive().preferredEmail("foo@activatable.com").create();
- com.google.gerrit.acceptance.testsuite.account.TestAccount deactivatableAccount =
+ Account.Id deactivatableAccountId =
accountOperations.newAccount().preferredEmail("foo@deactivatable.com").create();
RegistrationHandle registrationHandle =
accountActivationValidationListeners.add(
@@ -554,61 +554,56 @@
/* Test account that can be activated, but not deactivated */
// Deactivate account that is already inactive
try {
- gApi.accounts().id(activatableAccount.accountId().get()).setActive(false);
+ gApi.accounts().id(activatableAccountId.get()).setActive(false);
fail("Expected exception");
} catch (ResourceConflictException e) {
assertThat(e.getMessage()).isEqualTo("account not active");
}
- assertThat(accountOperations.account(activatableAccount.accountId()).get().active())
- .isFalse();
+ assertThat(accountOperations.account(activatableAccountId).get().active()).isFalse();
// Activate account that can be activated
- gApi.accounts().id(activatableAccount.accountId().get()).setActive(true);
- assertThat(accountOperations.account(activatableAccount.accountId()).get().active()).isTrue();
+ gApi.accounts().id(activatableAccountId.get()).setActive(true);
+ assertThat(accountOperations.account(activatableAccountId).get().active()).isTrue();
// Activate account that is already active
- gApi.accounts().id(activatableAccount.accountId().get()).setActive(true);
- assertThat(accountOperations.account(activatableAccount.accountId()).get().active()).isTrue();
+ gApi.accounts().id(activatableAccountId.get()).setActive(true);
+ assertThat(accountOperations.account(activatableAccountId).get().active()).isTrue();
// Try deactivating account that cannot be deactivated
try {
- gApi.accounts().id(activatableAccount.accountId().get()).setActive(false);
+ gApi.accounts().id(activatableAccountId.get()).setActive(false);
fail("Expected exception");
} catch (ResourceConflictException e) {
assertThat(e.getMessage()).isEqualTo("not allowed to deactive account");
}
- assertThat(accountOperations.account(activatableAccount.accountId()).get().active()).isTrue();
+ assertThat(accountOperations.account(activatableAccountId).get().active()).isTrue();
/* Test account that can be deactivated, but not activated */
// Activate account that is already inactive
- gApi.accounts().id(deactivatableAccount.accountId().get()).setActive(true);
- assertThat(accountOperations.account(deactivatableAccount.accountId()).get().active())
- .isTrue();
+ gApi.accounts().id(deactivatableAccountId.get()).setActive(true);
+ assertThat(accountOperations.account(deactivatableAccountId).get().active()).isTrue();
// Deactivate account that can be deactivated
- gApi.accounts().id(deactivatableAccount.accountId().get()).setActive(false);
- assertThat(accountOperations.account(deactivatableAccount.accountId()).get().active())
- .isFalse();
+ gApi.accounts().id(deactivatableAccountId.get()).setActive(false);
+ assertThat(accountOperations.account(deactivatableAccountId).get().active()).isFalse();
// Deactivate account that is already inactive
try {
- gApi.accounts().id(deactivatableAccount.accountId().get()).setActive(false);
+ gApi.accounts().id(deactivatableAccountId.get()).setActive(false);
fail("Expected exception");
} catch (ResourceConflictException e) {
assertThat(e.getMessage()).isEqualTo("account not active");
}
- assertThat(accountOperations.account(deactivatableAccount.accountId()).get().active())
- .isFalse();
+ assertThat(accountOperations.account(deactivatableAccountId).get().active()).isFalse();
// Try activating account that cannot be activated
try {
- gApi.accounts().id(deactivatableAccount.accountId().get()).setActive(true);
+ gApi.accounts().id(deactivatableAccountId.get()).setActive(true);
fail("Expected exception");
} catch (ResourceConflictException e) {
assertThat(e.getMessage()).isEqualTo("not allowed to active account");
}
- assertThat(accountOperations.account(deactivatableAccount.accountId()).get().active())
- .isFalse();
+ assertThat(accountOperations.account(deactivatableAccountId).get().active()).isFalse();
} finally {
registrationHandle.remove();
}
diff --git a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
index fe55115..b3a5e2d 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
@@ -68,7 +68,6 @@
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.TestProjectInput;
import com.google.gerrit.acceptance.testsuite.account.AccountOperations;
-import com.google.gerrit.acceptance.testsuite.account.TestAccount;
import com.google.gerrit.common.FooterConstants;
import com.google.gerrit.common.TimeUtil;
import com.google.gerrit.common.data.LabelFunction;
@@ -1688,7 +1687,7 @@
// create a group named "ab" with one user: testUser
String email = "abcd@test.com";
String fullname = "abcd";
- TestAccount testUser =
+ Account.Id accountIdOfTestUser =
accountOperations
.newAccount()
.username("abcd")
@@ -1721,7 +1720,7 @@
Collection<AccountInfo> reviewers = c.reviewers.get(REVIEWER);
assertThat(reviewers).isNotNull();
assertThat(reviewers).hasSize(1);
- assertThat(reviewers.iterator().next()._accountId).isEqualTo(testUser.accountId().get());
+ assertThat(reviewers.iterator().next()._accountId).isEqualTo(accountIdOfTestUser.get());
// Ensure ETag and lastUpdatedOn are updated.
rsrc = parseResource(r);
@@ -1748,7 +1747,7 @@
String myGroupUserEmail = "lee@test.com";
String myGroupUserFullname = "lee";
- TestAccount myGroupUser =
+ Account.Id accountIdOfGroupUser =
accountOperations
.newAccount()
.username("lee")
@@ -1785,7 +1784,7 @@
Collection<AccountInfo> reviewers = c.reviewers.get(REVIEWER);
assertThat(reviewers).isNotNull();
assertThat(reviewers).hasSize(1);
- assertThat(reviewers.iterator().next()._accountId).isEqualTo(myGroupUser.accountId().get());
+ assertThat(reviewers.iterator().next()._accountId).isEqualTo(accountIdOfGroupUser.get());
// Ensure ETag and lastUpdatedOn are updated.
rsrc = parseResource(r);
@@ -2215,13 +2214,12 @@
// notify unrelated account as TO
String email = "user2@example.com";
- TestAccount user2 =
- accountOperations
- .newAccount()
- .username("user2")
- .preferredEmail(email)
- .fullname("User2")
- .create();
+ accountOperations
+ .newAccount()
+ .username("user2")
+ .preferredEmail(email)
+ .fullname("User2")
+ .create();
setApiUser(user);
recommend(r.getChangeId());
setApiUser(admin);
@@ -2229,7 +2227,7 @@
in.notifyDetails = new HashMap<>();
in.notifyDetails.put(RecipientType.TO, new NotifyInfo(ImmutableList.of(email)));
gApi.changes().id(r.getChangeId()).reviewer(user.getId().toString()).deleteVote(in);
- assertNotifyTo(user2);
+ assertNotifyTo(email, "User2");
// notify unrelated account as CC
setApiUser(user);
@@ -2239,7 +2237,7 @@
in.notifyDetails = new HashMap<>();
in.notifyDetails.put(RecipientType.CC, new NotifyInfo(ImmutableList.of(email)));
gApi.changes().id(r.getChangeId()).reviewer(user.getId().toString()).deleteVote(in);
- assertNotifyCc(user2);
+ assertNotifyCc(email, "User2");
// notify unrelated account as BCC
setApiUser(user);
@@ -2249,7 +2247,7 @@
in.notifyDetails = new HashMap<>();
in.notifyDetails.put(RecipientType.BCC, new NotifyInfo(ImmutableList.of(email)));
gApi.changes().id(r.getChangeId()).reviewer(user.getId().toString()).deleteVote(in);
- assertNotifyBcc(user2);
+ assertNotifyBcc(email, "User2");
}
@Test
diff --git a/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java b/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java
index 8f1bdbc..d6be960 100644
--- a/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java
@@ -183,24 +183,23 @@
@Test
public void cachedGroupsForMemberAreUpdatedOnMemberAdditionAndRemoval() throws Exception {
String username = name("user");
- com.google.gerrit.acceptance.testsuite.account.TestAccount account =
- accountOperations.newAccount().username(username).create();
+ Account.Id accountId = accountOperations.newAccount().username(username).create();
// Fill the cache for the observed account.
- groupIncludeCache.getGroupsWithMember(account.accountId());
+ groupIncludeCache.getGroupsWithMember(accountId);
String groupName = createGroup("users");
AccountGroup.UUID groupUuid = new AccountGroup.UUID(gApi.groups().id(groupName).get().id);
gApi.groups().id(groupName).addMembers(username);
Collection<AccountGroup.UUID> groupsWithMemberAfterAddition =
- groupIncludeCache.getGroupsWithMember(account.accountId());
+ groupIncludeCache.getGroupsWithMember(accountId);
assertThat(groupsWithMemberAfterAddition).contains(groupUuid);
gApi.groups().id(groupName).removeMembers(username);
Collection<AccountGroup.UUID> groupsWithMemberAfterRemoval =
- groupIncludeCache.getGroupsWithMember(account.accountId());
+ groupIncludeCache.getGroupsWithMember(accountId);
assertThat(groupsWithMemberAfterRemoval).doesNotContain(groupUuid);
}
@@ -411,19 +410,17 @@
@Test
public void cachedGroupsForMemberAreUpdatedOnGroupCreation() throws Exception {
- com.google.gerrit.acceptance.testsuite.account.TestAccount account =
- accountOperations.newAccount().create();
+ Account.Id accountId = accountOperations.newAccount().create();
// Fill the cache for the observed account.
- groupIncludeCache.getGroupsWithMember(account.accountId());
+ groupIncludeCache.getGroupsWithMember(accountId);
GroupInput groupInput = new GroupInput();
groupInput.name = name("Users");
- groupInput.members = ImmutableList.of(String.valueOf(account.accountId().get()));
+ groupInput.members = ImmutableList.of(String.valueOf(accountId.get()));
GroupInfo group = gApi.groups().create(groupInput).get();
- Collection<AccountGroup.UUID> groups =
- groupIncludeCache.getGroupsWithMember(account.accountId());
+ Collection<AccountGroup.UUID> groups = groupIncludeCache.getGroupsWithMember(accountId);
assertThat(groups).containsExactly(new AccountGroup.UUID(group.id));
}
diff --git a/javatests/com/google/gerrit/acceptance/api/project/CheckAccessIT.java b/javatests/com/google/gerrit/acceptance/api/project/CheckAccessIT.java
index 2b1416a..e4194a3 100644
--- a/javatests/com/google/gerrit/acceptance/api/project/CheckAccessIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/project/CheckAccessIT.java
@@ -20,16 +20,18 @@
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.RestResponse;
import com.google.gerrit.acceptance.TestAccount;
+import com.google.gerrit.acceptance.testsuite.group.GroupOperations;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.extensions.api.config.AccessCheckInfo;
import com.google.gerrit.extensions.api.config.AccessCheckInput;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
+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.group.InternalGroup;
import com.google.gerrit.server.group.SystemGroupBackend;
+import com.google.inject.Inject;
import java.util.List;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.RefUpdate.Result;
@@ -39,35 +41,29 @@
public class CheckAccessIT extends AbstractDaemonTest {
+ @Inject private GroupOperations groupOperations;
+
private Project.NameKey normalProject;
private Project.NameKey secretProject;
private Project.NameKey secretRefProject;
private TestAccount privilegedUser;
- private InternalGroup privilegedGroup;
@Before
public void setUp() throws Exception {
normalProject = createProject("normal");
secretProject = createProject("secret");
secretRefProject = createProject("secretRef");
- privilegedGroup = group(createGroup("privilegedGroup"));
+ AccountGroup.UUID privilegedGroupUuid =
+ groupOperations.newGroup().name(name("privilegedGroup")).create();
privilegedUser = accountCreator.create("privilegedUser", "snowden@nsa.gov", "Ed Snowden");
- gApi.groups().id(privilegedGroup.getGroupUUID().get()).addMembers(privilegedUser.username);
+ groupOperations.group(privilegedGroupUuid).forUpdate().addMember(privilegedUser.id).update();
- assertThat(gApi.groups().id(privilegedGroup.getGroupUUID().get()).members().get(0).email)
- .contains("snowden");
-
- grant(secretProject, "refs/*", Permission.READ, false, privilegedGroup.getGroupUUID());
+ grant(secretProject, "refs/*", Permission.READ, false, privilegedGroupUuid);
block(secretProject, "refs/*", Permission.READ, SystemGroupBackend.REGISTERED_USERS);
deny(secretRefProject, "refs/*", Permission.READ, SystemGroupBackend.ANONYMOUS_USERS);
- grant(
- secretRefProject,
- "refs/heads/secret/*",
- Permission.READ,
- false,
- privilegedGroup.getGroupUUID());
+ grant(secretRefProject, "refs/heads/secret/*", Permission.READ, false, privilegedGroupUuid);
block(
secretRefProject,
"refs/heads/secret/*",
@@ -81,13 +77,8 @@
SystemGroupBackend.REGISTERED_USERS);
// Ref permission
- grant(
- normalProject,
- "refs/*",
- Permission.VIEW_PRIVATE_CHANGES,
- false,
- privilegedGroup.getGroupUUID());
- grant(normalProject, "refs/*", Permission.FORGE_SERVER, false, privilegedGroup.getGroupUUID());
+ grant(normalProject, "refs/*", Permission.VIEW_PRIVATE_CHANGES, false, privilegedGroupUuid);
+ grant(normalProject, "refs/*", Permission.FORGE_SERVER, false, privilegedGroupUuid);
}
@Test
diff --git a/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java b/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
index 7ad34a6..f1e67c1 100644
--- a/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
+++ b/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
@@ -213,6 +213,24 @@
}
@Test
+ @TestProjectInput(createEmptyCommit = false)
+ public void validateConnected() throws Exception {
+ RevCommit c = testRepo.commit().message("Initial commit").insertChangeId().create();
+ testRepo.reset(c);
+
+ String r = "refs/heads/master";
+ PushResult pr = pushHead(testRepo, r, false);
+ assertPushOk(pr, r);
+
+ RevCommit amended =
+ testRepo.amend(c).message("different initial commit").insertChangeId().create();
+ testRepo.reset(amended);
+ r = "refs/for/master";
+ pr = pushHead(testRepo, r, false);
+ assertPushRejected(pr, r, "no common ancestry");
+ }
+
+ @Test
public void pushInitialCommitForRefsMetaConfigBranch() throws Exception {
// delete refs/meta/config
try (Repository repo = repoManager.openRepository(project);
diff --git a/javatests/com/google/gerrit/acceptance/rest/BUILD b/javatests/com/google/gerrit/acceptance/rest/BUILD
index b4940bc..b94a98d 100644
--- a/javatests/com/google/gerrit/acceptance/rest/BUILD
+++ b/javatests/com/google/gerrit/acceptance/rest/BUILD
@@ -4,7 +4,10 @@
srcs = glob(["*IT.java"]),
group = "rest_bindings",
labels = ["rest"],
- deps = [":util"],
+ deps = [
+ ":util",
+ "//java/com/google/gerrit/server/logging",
+ ],
)
java_library(
diff --git a/javatests/com/google/gerrit/acceptance/rest/TraceIT.java b/javatests/com/google/gerrit/acceptance/rest/TraceIT.java
index b48d47e..137dc21 100644
--- a/javatests/com/google/gerrit/acceptance/rest/TraceIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/TraceIT.java
@@ -18,7 +18,9 @@
import static org.apache.http.HttpStatus.SC_CREATED;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
+import com.google.common.truth.Expect;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.RestResponse;
@@ -27,22 +29,31 @@
import com.google.gerrit.httpd.restapi.ParameterParser;
import com.google.gerrit.httpd.restapi.RestApiServlet;
import com.google.gerrit.server.events.CommitReceivedEvent;
+import com.google.gerrit.server.git.WorkQueue;
import com.google.gerrit.server.git.validators.CommitValidationException;
import com.google.gerrit.server.git.validators.CommitValidationListener;
import com.google.gerrit.server.git.validators.CommitValidationMessage;
import com.google.gerrit.server.logging.LoggingContext;
+import com.google.gerrit.server.logging.TraceContext;
import com.google.gerrit.server.project.CreateProjectArgs;
import com.google.gerrit.server.validators.ProjectCreationValidationListener;
import com.google.gerrit.server.validators.ValidationException;
import com.google.inject.Inject;
import java.util.List;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import org.apache.http.message.BasicHeader;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
public class TraceIT extends AbstractDaemonTest {
+ @Rule public final Expect expect = Expect.create();
+
@Inject private DynamicSet<ProjectCreationValidationListener> projectCreationValidationListeners;
@Inject private DynamicSet<CommitValidationListener> commitValidationListeners;
+ @Inject private WorkQueue workQueue;
private TraceValidatingProjectCreationValidationListener projectCreationListener;
private RegistrationHandle projectCreationListenerRegistrationHandle;
@@ -71,29 +82,90 @@
assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNull();
assertThat(projectCreationListener.traceId).isNull();
- assertThat(projectCreationListener.foundTraceId).isFalse();
assertThat(projectCreationListener.isLoggingForced).isFalse();
}
@Test
- public void restCallWithTrace() throws Exception {
+ public void restCallWithTraceRequestParam() throws Exception {
RestResponse response =
adminRestSession.put("/projects/new2?" + ParameterParser.TRACE_PARAMETER);
assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNotNull();
assertThat(projectCreationListener.traceId).isNotNull();
- assertThat(projectCreationListener.foundTraceId).isTrue();
assertThat(projectCreationListener.isLoggingForced).isTrue();
}
@Test
- public void restCallWithTraceAndProvidedTraceId() throws Exception {
+ public void restCallWithTraceRequestParamAndProvidedTraceId() throws Exception {
RestResponse response =
adminRestSession.put("/projects/new3?" + ParameterParser.TRACE_PARAMETER + "=issue/123");
assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
- assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNotNull();
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isEqualTo("issue/123");
assertThat(projectCreationListener.traceId).isEqualTo("issue/123");
- assertThat(projectCreationListener.foundTraceId).isTrue();
+ assertThat(projectCreationListener.isLoggingForced).isTrue();
+ }
+
+ @Test
+ public void restCallWithTraceHeader() throws Exception {
+ RestResponse response =
+ adminRestSession.putWithHeader(
+ "/projects/new4", new BasicHeader(RestApiServlet.X_GERRIT_TRACE, null));
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNotNull();
+ assertThat(projectCreationListener.traceId).isNotNull();
+ assertThat(projectCreationListener.isLoggingForced).isTrue();
+ }
+
+ @Test
+ public void restCallWithTraceHeaderAndProvidedTraceId() throws Exception {
+ RestResponse response =
+ adminRestSession.putWithHeader(
+ "/projects/new5", new BasicHeader(RestApiServlet.X_GERRIT_TRACE, "issue/123"));
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isEqualTo("issue/123");
+ assertThat(projectCreationListener.traceId).isEqualTo("issue/123");
+ assertThat(projectCreationListener.isLoggingForced).isTrue();
+ }
+
+ @Test
+ public void restCallWithTraceRequestParamAndTraceHeader() throws Exception {
+ // trace ID only specified by trace header
+ RestResponse response =
+ adminRestSession.putWithHeader(
+ "/projects/new6?trace", new BasicHeader(RestApiServlet.X_GERRIT_TRACE, "issue/123"));
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isEqualTo("issue/123");
+ assertThat(projectCreationListener.traceId).isEqualTo("issue/123");
+ assertThat(projectCreationListener.isLoggingForced).isTrue();
+
+ // trace ID only specified by trace request parameter
+ response =
+ adminRestSession.putWithHeader(
+ "/projects/new7?trace=issue/123", new BasicHeader(RestApiServlet.X_GERRIT_TRACE, null));
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isEqualTo("issue/123");
+ assertThat(projectCreationListener.traceId).isEqualTo("issue/123");
+ assertThat(projectCreationListener.isLoggingForced).isTrue();
+
+ // same trace ID specified by trace header and trace request parameter
+ response =
+ adminRestSession.putWithHeader(
+ "/projects/new8?trace=issue/123",
+ new BasicHeader(RestApiServlet.X_GERRIT_TRACE, "issue/123"));
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isEqualTo("issue/123");
+ assertThat(projectCreationListener.traceId).isEqualTo("issue/123");
+ assertThat(projectCreationListener.isLoggingForced).isTrue();
+
+ // different trace IDs specified by trace header and trace request parameter
+ response =
+ adminRestSession.putWithHeader(
+ "/projects/new9?trace=issue/123",
+ new BasicHeader(RestApiServlet.X_GERRIT_TRACE, "issue/456"));
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeaders(RestApiServlet.X_GERRIT_TRACE))
+ .containsExactly("issue/123", "issue/456");
+ assertThat(projectCreationListener.traceIds).containsExactly("issue/123", "issue/456");
assertThat(projectCreationListener.isLoggingForced).isTrue();
}
@@ -103,7 +175,6 @@
PushOneCommit.Result r = push.to("refs/heads/master");
r.assertOkStatus();
assertThat(commitValidationListener.traceId).isNull();
- assertThat(commitValidationListener.foundTraceId).isFalse();
assertThat(commitValidationListener.isLoggingForced).isFalse();
}
@@ -114,7 +185,6 @@
PushOneCommit.Result r = push.to("refs/heads/master");
r.assertOkStatus();
assertThat(commitValidationListener.traceId).isNotNull();
- assertThat(commitValidationListener.foundTraceId).isTrue();
assertThat(commitValidationListener.isLoggingForced).isTrue();
}
@@ -125,7 +195,6 @@
PushOneCommit.Result r = push.to("refs/heads/master");
r.assertOkStatus();
assertThat(commitValidationListener.traceId).isEqualTo("issue/123");
- assertThat(commitValidationListener.foundTraceId).isTrue();
assertThat(commitValidationListener.isLoggingForced).isTrue();
}
@@ -135,7 +204,6 @@
PushOneCommit.Result r = push.to("refs/for/master");
r.assertOkStatus();
assertThat(commitValidationListener.traceId).isNull();
- assertThat(commitValidationListener.foundTraceId).isFalse();
assertThat(commitValidationListener.isLoggingForced).isFalse();
}
@@ -146,7 +214,6 @@
PushOneCommit.Result r = push.to("refs/for/master");
r.assertOkStatus();
assertThat(commitValidationListener.traceId).isNotNull();
- assertThat(commitValidationListener.foundTraceId).isTrue();
assertThat(commitValidationListener.isLoggingForced).isTrue();
}
@@ -157,28 +224,67 @@
PushOneCommit.Result r = push.to("refs/for/master");
r.assertOkStatus();
assertThat(commitValidationListener.traceId).isEqualTo("issue/123");
- assertThat(commitValidationListener.foundTraceId).isTrue();
assertThat(commitValidationListener.isLoggingForced).isTrue();
}
+ @Test
+ public void workQueueCopyLoggingContext() throws Exception {
+ assertThat(LoggingContext.getInstance().getTags().isEmpty()).isTrue();
+ assertForceLogging(false);
+ try (TraceContext traceContext = TraceContext.open().forceLogging().addTag("foo", "bar")) {
+ SortedMap<String, SortedSet<Object>> tagMap = LoggingContext.getInstance().getTags().asMap();
+ assertThat(tagMap.keySet()).containsExactly("foo");
+ assertThat(tagMap.get("foo")).containsExactly("bar");
+ assertForceLogging(true);
+
+ workQueue
+ .createQueue(1, "test-queue")
+ .submit(
+ () -> {
+ // Verify that the tags and force logging flag have been propagated to the new
+ // thread.
+ SortedMap<String, SortedSet<Object>> threadTagMap =
+ LoggingContext.getInstance().getTags().asMap();
+ expect.that(threadTagMap.keySet()).containsExactly("foo");
+ expect.that(threadTagMap.get("foo")).containsExactly("bar");
+ expect
+ .that(LoggingContext.getInstance().shouldForceLogging(null, null, false))
+ .isTrue();
+ })
+ .get();
+
+ // Verify that tags and force logging flag in the outer thread are still set.
+ tagMap = LoggingContext.getInstance().getTags().asMap();
+ assertThat(tagMap.keySet()).containsExactly("foo");
+ assertThat(tagMap.get("foo")).containsExactly("bar");
+ assertForceLogging(true);
+ }
+ assertThat(LoggingContext.getInstance().getTags().isEmpty()).isTrue();
+ assertForceLogging(false);
+ }
+
+ private void assertForceLogging(boolean expected) {
+ assertThat(LoggingContext.getInstance().shouldForceLogging(null, null, false))
+ .isEqualTo(expected);
+ }
+
private static class TraceValidatingProjectCreationValidationListener
implements ProjectCreationValidationListener {
String traceId;
- Boolean foundTraceId;
+ ImmutableSet<String> traceIds;
Boolean isLoggingForced;
@Override
public void validateNewProject(CreateProjectArgs args) throws ValidationException {
this.traceId =
Iterables.getFirst(LoggingContext.getInstance().getTagsAsMap().get("TRACE_ID"), null);
- this.foundTraceId = traceId != null;
+ this.traceIds = LoggingContext.getInstance().getTagsAsMap().get("TRACE_ID");
this.isLoggingForced = LoggingContext.getInstance().shouldForceLogging(null, null, false);
}
}
private static class TraceValidatingCommitValidationListener implements CommitValidationListener {
String traceId;
- Boolean foundTraceId;
Boolean isLoggingForced;
@Override
@@ -186,7 +292,6 @@
throws CommitValidationException {
this.traceId =
Iterables.getFirst(LoggingContext.getInstance().getTagsAsMap().get("TRACE_ID"), null);
- this.foundTraceId = traceId != null;
this.isLoggingForced = LoggingContext.getInstance().shouldForceLogging(null, null, false);
return ImmutableList.of();
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java b/javatests/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java
index a9182ef..b74a0d7 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java
@@ -60,7 +60,6 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
@@ -105,8 +104,6 @@
.fromJson(
response.getReader(), new TypeToken<List<AccountExternalIdInfo>>() {}.getType());
- Collections.sort(expectedIdInfos);
- Collections.sort(results);
assertThat(results).containsExactlyElementsIn(expectedIdInfos);
}
@@ -133,8 +130,6 @@
.fromJson(
response.getReader(), new TypeToken<List<AccountExternalIdInfo>>() {}.getType());
- Collections.sort(expectedIdInfos);
- Collections.sort(results);
assertThat(results).containsExactlyElementsIn(expectedIdInfos);
}
diff --git a/javatests/com/google/gerrit/acceptance/server/rules/IgnoreSelfApprovalRuleIT.java b/javatests/com/google/gerrit/acceptance/server/rules/IgnoreSelfApprovalRuleIT.java
new file mode 100644
index 0000000..d1b05e3
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/server/rules/IgnoreSelfApprovalRuleIT.java
@@ -0,0 +1,99 @@
+// Copyright (C) 2018 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.acceptance.server.rules;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.NoHttpd;
+import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.common.data.LabelType;
+import com.google.gerrit.common.data.SubmitRecord;
+import com.google.gerrit.common.data.SubmitRequirement;
+import com.google.gerrit.server.project.SubmitRuleOptions;
+import com.google.gerrit.server.rules.IgnoreSelfApprovalRule;
+import com.google.inject.Inject;
+import java.util.Collection;
+import java.util.Map;
+import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
+import org.eclipse.jgit.junit.TestRepository;
+import org.junit.Test;
+
+@NoHttpd
+public class IgnoreSelfApprovalRuleIT extends AbstractDaemonTest {
+ @Inject private IgnoreSelfApprovalRule rule;
+
+ @Test
+ public void blocksWhenUploaderIsOnlyApprover() throws Exception {
+ enableRule("Code-Review", true);
+
+ PushOneCommit.Result r = createChange();
+ approve(r.getChangeId());
+
+ Collection<SubmitRecord> submitRecords =
+ rule.evaluate(r.getChange(), SubmitRuleOptions.defaults());
+
+ assertThat(submitRecords).hasSize(1);
+ SubmitRecord result = submitRecords.iterator().next();
+ assertThat(result.status).isEqualTo(SubmitRecord.Status.NOT_READY);
+ assertThat(result.labels).isNotEmpty();
+ assertThat(result.requirements)
+ .containsExactly(
+ SubmitRequirement.builder()
+ .setFallbackText("Approval from non-uploader required")
+ .setType("non_uploader_approval")
+ .build());
+ }
+
+ @Test
+ public void allowsSubmissionWhenChangeHasNonUploaderApproval() throws Exception {
+ enableRule("Code-Review", true);
+
+ // Create change as user
+ TestRepository<InMemoryRepository> userTestRepo = cloneProject(project, user);
+ PushOneCommit push = pushFactory.create(db, user.getIdent(), userTestRepo);
+ PushOneCommit.Result r = push.to("refs/for/master");
+
+ // Approve as admin
+ approve(r.getChangeId());
+
+ Collection<SubmitRecord> submitRecords =
+ rule.evaluate(r.getChange(), SubmitRuleOptions.defaults());
+ assertThat(submitRecords).isEmpty();
+ }
+
+ @Test
+ public void doesNothingByDefault() throws Exception {
+ enableRule("Code-Review", false);
+
+ PushOneCommit.Result r = createChange();
+ approve(r.getChangeId());
+
+ Collection<SubmitRecord> submitRecords =
+ rule.evaluate(r.getChange(), SubmitRuleOptions.defaults());
+ assertThat(submitRecords).isEmpty();
+ }
+
+ private void enableRule(String labelName, boolean newState) throws Exception {
+ try (ProjectConfigUpdate u = updateProject(project)) {
+ Map<String, LabelType> localLabelSections = u.getConfig().getLabelSections();
+ if (localLabelSections.isEmpty()) {
+ localLabelSections.putAll(projectCache.getAllProjects().getConfig().getLabelSections());
+ }
+ localLabelSections.get(labelName).setIgnoreSelfApproval(newState);
+ u.save();
+ }
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/ssh/BUILD b/javatests/com/google/gerrit/acceptance/ssh/BUILD
index b195ecc..a01cd3e 100644
--- a/javatests/com/google/gerrit/acceptance/ssh/BUILD
+++ b/javatests/com/google/gerrit/acceptance/ssh/BUILD
@@ -17,6 +17,7 @@
vm_args = ["-Xmx512m"],
deps = [
":util",
+ "//java/com/google/gerrit/server/logging",
"//lib/commons:compress",
],
)
diff --git a/javatests/com/google/gerrit/acceptance/ssh/SshCommandsIT.java b/javatests/com/google/gerrit/acceptance/ssh/SshCommandsIT.java
index 126b0ee..e603413 100644
--- a/javatests/com/google/gerrit/acceptance/ssh/SshCommandsIT.java
+++ b/javatests/com/google/gerrit/acceptance/ssh/SshCommandsIT.java
@@ -14,12 +14,14 @@
package com.google.gerrit.acceptance.ssh;
+import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Streams;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
@@ -28,7 +30,6 @@
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.sshd.Commands;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.junit.Test;
@@ -82,16 +83,9 @@
private static final ImmutableMap<String, List<String>> MASTER_COMMANDS =
ImmutableMap.of(
Commands.ROOT,
- ImmutableList.copyOf(
- new ArrayList<String>() {
- private static final long serialVersionUID = 1L;
-
- {
- addAll(COMMON_ROOT_COMMANDS);
- addAll(MASTER_ONLY_ROOT_COMMANDS);
- Collections.sort(this);
- }
- }),
+ Streams.concat(COMMON_ROOT_COMMANDS.stream(), MASTER_ONLY_ROOT_COMMANDS.stream())
+ .sorted()
+ .collect(toImmutableList()),
"index",
ImmutableList.of(
"changes", "changes-in-project"), // "activate" and "start" are not included
diff --git a/javatests/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImplTest.java b/javatests/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImplTest.java
new file mode 100644
index 0000000..954b0e6
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImplTest.java
@@ -0,0 +1,656 @@
+// Copyright (C) 2018 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.acceptance.testsuite.group;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth8.assertThat;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.truth.Correspondence;
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.testsuite.account.AccountOperations;
+import com.google.gerrit.extensions.api.groups.GroupInput;
+import com.google.gerrit.extensions.common.AccountInfo;
+import com.google.gerrit.extensions.common.GroupInfo;
+import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.inject.Inject;
+import java.sql.Timestamp;
+import java.util.Objects;
+import java.util.Optional;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public class GroupOperationsImplTest extends AbstractDaemonTest {
+
+ @Rule public ExpectedException expectedException = ExpectedException.none();
+
+ @Inject private AccountOperations accountOperations;
+
+ @Inject private GroupOperationsImpl groupOperations;
+
+ private int uniqueGroupNameIndex;
+
+ @Test
+ public void groupCanBeCreatedWithoutSpecifyingAnyParameters() throws Exception {
+ AccountGroup.UUID groupUuid = groupOperations.newGroup().create();
+
+ GroupInfo foundGroup = getGroupFromServer(groupUuid);
+ assertThat(foundGroup.id).isEqualTo(groupUuid.get());
+ assertThat(foundGroup.name).isNotEmpty();
+ }
+
+ @Test
+ public void twoGroupsWithoutAnyParametersDoNotClash() throws Exception {
+ AccountGroup.UUID groupUuid1 = groupOperations.newGroup().create();
+ AccountGroup.UUID groupUuid2 = groupOperations.newGroup().create();
+
+ TestGroup group1 = groupOperations.group(groupUuid1).get();
+ TestGroup group2 = groupOperations.group(groupUuid2).get();
+ assertThat(group1.groupUuid()).isNotEqualTo(group2.groupUuid());
+ }
+
+ @Test
+ public void groupCreatedByTestApiCanBeRetrievedViaOfficialApi() throws Exception {
+ AccountGroup.UUID groupUuid =
+ groupOperations.newGroup().name("unique group created via test API").create();
+
+ GroupInfo foundGroup = getGroupFromServer(groupUuid);
+ assertThat(foundGroup.id).isEqualTo(groupUuid.get());
+ assertThat(foundGroup.name).isEqualTo("unique group created via test API");
+ }
+
+ @Test
+ public void specifiedNameIsRespectedForGroupCreation() throws Exception {
+ AccountGroup.UUID groupUuid =
+ groupOperations.newGroup().name("XYZ-123-this-name-must-be-unique").create();
+
+ GroupInfo group = getGroupFromServer(groupUuid);
+ assertThat(group.name).isEqualTo("XYZ-123-this-name-must-be-unique");
+ }
+
+ @Test
+ public void specifiedDescriptionIsRespectedForGroupCreation() throws Exception {
+ AccountGroup.UUID groupUuid =
+ groupOperations.newGroup().description("All authenticated users").create();
+
+ GroupInfo group = getGroupFromServer(groupUuid);
+ assertThat(group.description).isEqualTo("All authenticated users");
+ }
+
+ @Test
+ public void requestingNoDescriptionIsPossibleForGroupCreation() throws Exception {
+ AccountGroup.UUID groupUuid = groupOperations.newGroup().clearDescription().create();
+
+ GroupInfo group = getGroupFromServer(groupUuid);
+ assertThat(group.description).isNull();
+ }
+
+ @Test
+ public void specifiedOwnerIsRespectedForGroupCreation() throws Exception {
+ AccountGroup.UUID ownerGroupUuid = groupOperations.newGroup().create();
+
+ AccountGroup.UUID groupUuid =
+ groupOperations.newGroup().ownerGroupUuid(ownerGroupUuid).create();
+
+ GroupInfo foundGroup = getGroupFromServer(groupUuid);
+ assertThat(foundGroup.ownerId).isEqualTo(ownerGroupUuid.get());
+ }
+
+ @Test
+ public void specifiedVisibilityIsRespectedForGroupCreation() throws Exception {
+ AccountGroup.UUID group1Uuid = groupOperations.newGroup().visibleToAll(true).create();
+ AccountGroup.UUID group2Uuid = groupOperations.newGroup().visibleToAll(false).create();
+
+ GroupInfo foundGroup1 = getGroupFromServer(group1Uuid);
+ GroupInfo foundGroup2 = getGroupFromServer(group2Uuid);
+ assertThat(foundGroup1.options.visibleToAll).isTrue();
+ // False == null
+ assertThat(foundGroup2.options.visibleToAll).isNull();
+ }
+
+ @Test
+ public void specifiedMembersAreRespectedForGroupCreation() throws Exception {
+ Account.Id account1Id = accountOperations.newAccount().create();
+ Account.Id account2Id = accountOperations.newAccount().create();
+ Account.Id account3Id = accountOperations.newAccount().create();
+ Account.Id account4Id = accountOperations.newAccount().create();
+
+ AccountGroup.UUID groupUuid =
+ groupOperations
+ .newGroup()
+ .members(account1Id, account2Id)
+ .addMember(account3Id)
+ .addMember(account4Id)
+ .create();
+
+ GroupInfo foundGroup = getGroupFromServer(groupUuid);
+ assertThat(foundGroup.members)
+ .comparingElementsUsing(getAccountToIdCorrespondence())
+ .containsExactly(account1Id, account2Id, account3Id, account4Id);
+ }
+
+ @Test
+ public void directlyAddingMembersIsPossibleForGroupCreation() throws Exception {
+ Account.Id account1Id = accountOperations.newAccount().create();
+ Account.Id account2Id = accountOperations.newAccount().create();
+
+ AccountGroup.UUID groupUuid =
+ groupOperations.newGroup().addMember(account1Id).addMember(account2Id).create();
+
+ GroupInfo foundGroup = getGroupFromServer(groupUuid);
+ assertThat(foundGroup.members)
+ .comparingElementsUsing(getAccountToIdCorrespondence())
+ .containsExactly(account1Id, account2Id);
+ }
+
+ @Test
+ public void requestingNoMembersIsPossibleForGroupCreation() throws Exception {
+ AccountGroup.UUID groupUuid = groupOperations.newGroup().clearMembers().create();
+
+ GroupInfo foundGroup = getGroupFromServer(groupUuid);
+ assertThat(foundGroup.members).isEmpty();
+ }
+
+ @Test
+ public void specifiedSubgroupsAreRespectedForGroupCreation() throws Exception {
+ AccountGroup.UUID group1Uuid = groupOperations.newGroup().create();
+ AccountGroup.UUID group2Uuid = groupOperations.newGroup().create();
+ AccountGroup.UUID group3Uuid = groupOperations.newGroup().create();
+ AccountGroup.UUID group4Uuid = groupOperations.newGroup().create();
+
+ AccountGroup.UUID groupUuid =
+ groupOperations
+ .newGroup()
+ .subgroups(group1Uuid, group2Uuid)
+ .addSubgroup(group3Uuid)
+ .addSubgroup(group4Uuid)
+ .create();
+
+ GroupInfo foundGroup = getGroupFromServer(groupUuid);
+ assertThat(foundGroup.includes)
+ .comparingElementsUsing(getGroupToUuidCorrespondence())
+ .containsExactly(group1Uuid, group2Uuid, group3Uuid, group4Uuid);
+ }
+
+ @Test
+ public void directlyAddingSubgroupsIsPossibleForGroupCreation() throws Exception {
+ AccountGroup.UUID group1Uuid = groupOperations.newGroup().create();
+ AccountGroup.UUID group2Uuid = groupOperations.newGroup().create();
+
+ AccountGroup.UUID groupUuid =
+ groupOperations.newGroup().addSubgroup(group1Uuid).addSubgroup(group2Uuid).create();
+
+ GroupInfo foundGroup = getGroupFromServer(groupUuid);
+ assertThat(foundGroup.includes)
+ .comparingElementsUsing(getGroupToUuidCorrespondence())
+ .containsExactly(group1Uuid, group2Uuid);
+ }
+
+ @Test
+ public void requestingNoSubgroupsIsPossibleForGroupCreation() throws Exception {
+ AccountGroup.UUID groupUuid = groupOperations.newGroup().clearSubgroups().create();
+
+ GroupInfo foundGroup = getGroupFromServer(groupUuid);
+ assertThat(foundGroup.includes).isEmpty();
+ }
+
+ @Test
+ public void existingGroupCanBeCheckedForExistence() throws Exception {
+ AccountGroup.UUID groupUuid = createGroupInServer(createArbitraryGroupInput());
+
+ boolean exists = groupOperations.group(groupUuid).exists();
+
+ assertThat(exists).isTrue();
+ }
+
+ @Test
+ public void notExistingGroupCanBeCheckedForExistence() throws Exception {
+ AccountGroup.UUID notExistingGroupUuid = new AccountGroup.UUID("not-existing-group");
+
+ boolean exists = groupOperations.group(notExistingGroupUuid).exists();
+
+ assertThat(exists).isFalse();
+ }
+
+ @Test
+ public void retrievingNotExistingGroupFails() throws Exception {
+ AccountGroup.UUID notExistingGroupUuid = new AccountGroup.UUID("not-existing-group");
+
+ expectedException.expect(IllegalStateException.class);
+ groupOperations.group(notExistingGroupUuid).get();
+ }
+
+ @Test
+ public void groupNotCreatedByTestApiCanBeRetrieved() throws Exception {
+ GroupInput input = createArbitraryGroupInput();
+ input.name = "unique group not created via test API";
+ AccountGroup.UUID groupUuid = createGroupInServer(input);
+
+ TestGroup foundGroup = groupOperations.group(groupUuid).get();
+
+ assertThat(foundGroup.groupUuid()).isEqualTo(groupUuid);
+ assertThat(foundGroup.name()).isEqualTo("unique group not created via test API");
+ }
+
+ @Test
+ public void uuidOfExistingGroupCanBeRetrieved() throws Exception {
+ AccountGroup.UUID groupUuid = groupOperations.newGroup().create();
+
+ AccountGroup.UUID foundGroupUuid = groupOperations.group(groupUuid).get().groupUuid();
+
+ assertThat(foundGroupUuid).isEqualTo(groupUuid);
+ }
+
+ @Test
+ public void nameOfExistingGroupCanBeRetrieved() throws Exception {
+ AccountGroup.UUID groupUuid =
+ groupOperations.newGroup().name("ABC-789-this-name-must-be-unique").create();
+
+ String groupName = groupOperations.group(groupUuid).get().name();
+
+ assertThat(groupName).isEqualTo("ABC-789-this-name-must-be-unique");
+ }
+
+ @Test
+ public void nameKeyOfExistingGroupCanBeRetrieved() throws Exception {
+ AccountGroup.UUID groupUuid =
+ groupOperations.newGroup().name("ABC-789-this-name-must-be-unique").create();
+
+ AccountGroup.NameKey groupName = groupOperations.group(groupUuid).get().nameKey();
+
+ assertThat(groupName).isEqualTo(new AccountGroup.NameKey("ABC-789-this-name-must-be-unique"));
+ }
+
+ @Test
+ public void descriptionOfExistingGroupCanBeRetrieved() throws Exception {
+ AccountGroup.UUID groupUuid =
+ groupOperations
+ .newGroup()
+ .description("This is a very detailed description of this group.")
+ .create();
+
+ Optional<String> description = groupOperations.group(groupUuid).get().description();
+
+ assertThat(description).hasValue("This is a very detailed description of this group.");
+ }
+
+ @Test
+ public void emptyDescriptionOfExistingGroupCanBeRetrieved() throws Exception {
+ AccountGroup.UUID groupUuid = groupOperations.newGroup().clearDescription().create();
+
+ Optional<String> description = groupOperations.group(groupUuid).get().description();
+
+ assertThat(description).isEmpty();
+ }
+
+ @Test
+ public void ownerGroupUuidOfExistingGroupCanBeRetrieved() throws Exception {
+ AccountGroup.UUID originalOwnerGroupUuid = new AccountGroup.UUID("owner group");
+ AccountGroup.UUID groupUuid =
+ groupOperations.newGroup().ownerGroupUuid(originalOwnerGroupUuid).create();
+
+ AccountGroup.UUID ownerGroupUuid = groupOperations.group(groupUuid).get().ownerGroupUuid();
+
+ assertThat(ownerGroupUuid).isEqualTo(originalOwnerGroupUuid);
+ }
+
+ @Test
+ public void visibilityOfExistingGroupCanBeRetrieved() throws Exception {
+ AccountGroup.UUID visibleGroupUuid = groupOperations.newGroup().visibleToAll(true).create();
+ AccountGroup.UUID invisibleGroupUuid = groupOperations.newGroup().visibleToAll(false).create();
+
+ TestGroup visibleGroup = groupOperations.group(visibleGroupUuid).get();
+ TestGroup invisibleGroup = groupOperations.group(invisibleGroupUuid).get();
+
+ assertThat(visibleGroup.visibleToAll()).named("visibility of visible group").isTrue();
+ assertThat(invisibleGroup.visibleToAll()).named("visibility of invisible group").isFalse();
+ }
+
+ @Test
+ public void createdOnOfExistingGroupCanBeRetrieved() throws Exception {
+ GroupInfo group = gApi.groups().create(createArbitraryGroupInput()).detail();
+ AccountGroup.UUID groupUuid = new AccountGroup.UUID(group.id);
+
+ Timestamp createdOn = groupOperations.group(groupUuid).get().createdOn();
+
+ assertThat(createdOn).isEqualTo(group.createdOn);
+ }
+
+ @Test
+ public void membersOfExistingGroupCanBeRetrieved() throws Exception {
+ Account.Id memberId1 = new Account.Id(1000);
+ Account.Id memberId2 = new Account.Id(2000);
+ Account.Id memberId3 = new Account.Id(3000);
+ AccountGroup.UUID groupUuid =
+ groupOperations.newGroup().members(memberId1, memberId2, memberId3).create();
+
+ ImmutableSet<Account.Id> members = groupOperations.group(groupUuid).get().members();
+
+ assertThat(members).containsExactly(memberId1, memberId2, memberId3);
+ }
+
+ @Test
+ public void emptyMembersOfExistingGroupCanBeRetrieved() throws Exception {
+ AccountGroup.UUID groupUuid = groupOperations.newGroup().clearMembers().create();
+
+ ImmutableSet<Account.Id> members = groupOperations.group(groupUuid).get().members();
+
+ assertThat(members).isEmpty();
+ }
+
+ @Test
+ public void subgroupsOfExistingGroupCanBeRetrieved() throws Exception {
+ AccountGroup.UUID subgroupUuid1 = new AccountGroup.UUID("subgroup 1");
+ AccountGroup.UUID subgroupUuid2 = new AccountGroup.UUID("subgroup 2");
+ AccountGroup.UUID subgroupUuid3 = new AccountGroup.UUID("subgroup 3");
+ AccountGroup.UUID groupUuid =
+ groupOperations.newGroup().subgroups(subgroupUuid1, subgroupUuid2, subgroupUuid3).create();
+
+ ImmutableSet<AccountGroup.UUID> subgroups = groupOperations.group(groupUuid).get().subgroups();
+
+ assertThat(subgroups).containsExactly(subgroupUuid1, subgroupUuid2, subgroupUuid3);
+ }
+
+ @Test
+ public void emptySubgroupsOfExistingGroupCanBeRetrieved() throws Exception {
+ AccountGroup.UUID groupUuid = groupOperations.newGroup().clearSubgroups().create();
+
+ ImmutableSet<AccountGroup.UUID> subgroups = groupOperations.group(groupUuid).get().subgroups();
+
+ assertThat(subgroups).isEmpty();
+ }
+
+ @Test
+ public void updateWithoutAnyParametersIsANoop() throws Exception {
+ AccountGroup.UUID groupUuid = groupOperations.newGroup().create();
+ TestGroup originalGroup = groupOperations.group(groupUuid).get();
+
+ groupOperations.group(groupUuid).forUpdate().update();
+
+ TestGroup updatedGroup = groupOperations.group(groupUuid).get();
+ assertThat(updatedGroup).isEqualTo(originalGroup);
+ }
+
+ @Test
+ public void updateWritesToInternalGroupSystem() throws Exception {
+ AccountGroup.UUID groupUuid =
+ groupOperations.newGroup().description("original description").create();
+
+ groupOperations.group(groupUuid).forUpdate().description("updated description").update();
+
+ String currentDescription = getGroupFromServer(groupUuid).description;
+ assertThat(currentDescription).isEqualTo("updated description");
+ }
+
+ @Test
+ public void nameCanBeUpdated() throws Exception {
+ AccountGroup.UUID groupUuid = groupOperations.newGroup().name("original name").create();
+
+ groupOperations.group(groupUuid).forUpdate().name("updated name").update();
+
+ String currentName = groupOperations.group(groupUuid).get().name();
+ assertThat(currentName).isEqualTo("updated name");
+ }
+
+ @Test
+ public void descriptionCanBeUpdated() throws Exception {
+ AccountGroup.UUID groupUuid =
+ groupOperations.newGroup().description("original description").create();
+
+ groupOperations.group(groupUuid).forUpdate().description("updated description").update();
+
+ Optional<String> currentDescription = groupOperations.group(groupUuid).get().description();
+ assertThat(currentDescription).hasValue("updated description");
+ }
+
+ @Test
+ public void descriptionCanBeCleared() throws Exception {
+ AccountGroup.UUID groupUuid =
+ groupOperations.newGroup().description("original description").create();
+
+ groupOperations.group(groupUuid).forUpdate().clearDescription().update();
+
+ Optional<String> currentDescription = groupOperations.group(groupUuid).get().description();
+ assertThat(currentDescription).isEmpty();
+ }
+
+ @Test
+ public void ownerGroupUuidCanBeUpdated() throws Exception {
+ AccountGroup.UUID originalOwnerGroupUuid = new AccountGroup.UUID("original owner");
+ AccountGroup.UUID groupUuid =
+ groupOperations.newGroup().ownerGroupUuid(originalOwnerGroupUuid).create();
+
+ AccountGroup.UUID updatedOwnerGroupUuid = new AccountGroup.UUID("updated owner");
+ groupOperations.group(groupUuid).forUpdate().ownerGroupUuid(updatedOwnerGroupUuid).update();
+
+ AccountGroup.UUID currentOwnerGroupUuid =
+ groupOperations.group(groupUuid).get().ownerGroupUuid();
+ assertThat(currentOwnerGroupUuid).isEqualTo(updatedOwnerGroupUuid);
+ }
+
+ @Test
+ public void visibilityCanBeUpdated() throws Exception {
+ AccountGroup.UUID groupUuid = groupOperations.newGroup().visibleToAll(true).create();
+
+ groupOperations.group(groupUuid).forUpdate().visibleToAll(false).update();
+
+ boolean visibleToAll = groupOperations.group(groupUuid).get().visibleToAll();
+ assertThat(visibleToAll).isFalse();
+ }
+
+ @Test
+ public void membersCanBeAdded() throws Exception {
+ AccountGroup.UUID groupUuid = groupOperations.newGroup().clearMembers().create();
+
+ Account.Id memberId1 = new Account.Id(1000);
+ Account.Id memberId2 = new Account.Id(2000);
+ groupOperations.group(groupUuid).forUpdate().addMember(memberId1).addMember(memberId2).update();
+
+ ImmutableSet<Account.Id> members = groupOperations.group(groupUuid).get().members();
+ assertThat(members).containsExactly(memberId1, memberId2);
+ }
+
+ @Test
+ public void membersCanBeRemoved() throws Exception {
+ Account.Id memberId1 = new Account.Id(1000);
+ Account.Id memberId2 = new Account.Id(2000);
+ AccountGroup.UUID groupUuid = groupOperations.newGroup().members(memberId1, memberId2).create();
+
+ groupOperations.group(groupUuid).forUpdate().removeMember(memberId2).update();
+
+ ImmutableSet<Account.Id> members = groupOperations.group(groupUuid).get().members();
+ assertThat(members).containsExactly(memberId1);
+ }
+
+ @Test
+ public void memberAdditionAndRemovalCanBeMixed() throws Exception {
+ Account.Id memberId1 = new Account.Id(1000);
+ Account.Id memberId2 = new Account.Id(2000);
+ AccountGroup.UUID groupUuid = groupOperations.newGroup().members(memberId1, memberId2).create();
+
+ Account.Id memberId3 = new Account.Id(3000);
+ groupOperations
+ .group(groupUuid)
+ .forUpdate()
+ .removeMember(memberId1)
+ .addMember(memberId3)
+ .update();
+
+ ImmutableSet<Account.Id> members = groupOperations.group(groupUuid).get().members();
+ assertThat(members).containsExactly(memberId2, memberId3);
+ }
+
+ @Test
+ public void membersCanBeCleared() throws Exception {
+ Account.Id memberId1 = new Account.Id(1000);
+ Account.Id memberId2 = new Account.Id(2000);
+ AccountGroup.UUID groupUuid = groupOperations.newGroup().members(memberId1, memberId2).create();
+
+ groupOperations.group(groupUuid).forUpdate().clearMembers().update();
+
+ ImmutableSet<Account.Id> members = groupOperations.group(groupUuid).get().members();
+ assertThat(members).isEmpty();
+ }
+
+ @Test
+ public void furtherMembersCanBeAddedAfterClearingAll() throws Exception {
+ Account.Id memberId1 = new Account.Id(1000);
+ Account.Id memberId2 = new Account.Id(2000);
+ AccountGroup.UUID groupUuid = groupOperations.newGroup().members(memberId1, memberId2).create();
+
+ Account.Id memberId3 = new Account.Id(3000);
+ groupOperations.group(groupUuid).forUpdate().clearMembers().addMember(memberId3).update();
+
+ ImmutableSet<Account.Id> members = groupOperations.group(groupUuid).get().members();
+ assertThat(members).containsExactly(memberId3);
+ }
+
+ @Test
+ public void subgroupsCanBeAdded() throws Exception {
+ AccountGroup.UUID groupUuid = groupOperations.newGroup().clearSubgroups().create();
+
+ AccountGroup.UUID subgroupUuid1 = new AccountGroup.UUID("subgroup 1");
+ AccountGroup.UUID subgroupUuid2 = new AccountGroup.UUID("subgroup 2");
+ groupOperations
+ .group(groupUuid)
+ .forUpdate()
+ .addSubgroup(subgroupUuid1)
+ .addSubgroup(subgroupUuid2)
+ .update();
+
+ ImmutableSet<AccountGroup.UUID> subgroups = groupOperations.group(groupUuid).get().subgroups();
+ assertThat(subgroups).containsExactly(subgroupUuid1, subgroupUuid2);
+ }
+
+ @Test
+ public void subgroupsCanBeRemoved() throws Exception {
+ AccountGroup.UUID subgroupUuid1 = new AccountGroup.UUID("subgroup 1");
+ AccountGroup.UUID subgroupUuid2 = new AccountGroup.UUID("subgroup 2");
+ AccountGroup.UUID groupUuid =
+ groupOperations.newGroup().subgroups(subgroupUuid1, subgroupUuid2).create();
+
+ groupOperations.group(groupUuid).forUpdate().removeSubgroup(subgroupUuid2).update();
+
+ ImmutableSet<AccountGroup.UUID> subgroups = groupOperations.group(groupUuid).get().subgroups();
+ assertThat(subgroups).containsExactly(subgroupUuid1);
+ }
+
+ @Test
+ public void subgroupAdditionAndRemovalCanBeMixed() throws Exception {
+ AccountGroup.UUID subgroupUuid1 = new AccountGroup.UUID("subgroup 1");
+ AccountGroup.UUID subgroupUuid2 = new AccountGroup.UUID("subgroup 2");
+ AccountGroup.UUID groupUuid =
+ groupOperations.newGroup().subgroups(subgroupUuid1, subgroupUuid2).create();
+
+ AccountGroup.UUID subgroupUuid3 = new AccountGroup.UUID("subgroup 3");
+ groupOperations
+ .group(groupUuid)
+ .forUpdate()
+ .removeSubgroup(subgroupUuid1)
+ .addSubgroup(subgroupUuid3)
+ .update();
+
+ ImmutableSet<AccountGroup.UUID> subgroups = groupOperations.group(groupUuid).get().subgroups();
+ assertThat(subgroups).containsExactly(subgroupUuid2, subgroupUuid3);
+ }
+
+ @Test
+ public void subgroupsCanBeCleared() throws Exception {
+ AccountGroup.UUID subgroupUuid1 = new AccountGroup.UUID("subgroup 1");
+ AccountGroup.UUID subgroupUuid2 = new AccountGroup.UUID("subgroup 2");
+ AccountGroup.UUID groupUuid =
+ groupOperations.newGroup().subgroups(subgroupUuid1, subgroupUuid2).create();
+
+ groupOperations.group(groupUuid).forUpdate().clearSubgroups().update();
+
+ ImmutableSet<AccountGroup.UUID> subgroups = groupOperations.group(groupUuid).get().subgroups();
+ assertThat(subgroups).isEmpty();
+ }
+
+ @Test
+ public void furtherSubgroupsCanBeAddedAfterClearingAll() throws Exception {
+ AccountGroup.UUID subgroupUuid1 = new AccountGroup.UUID("subgroup 1");
+ AccountGroup.UUID subgroupUuid2 = new AccountGroup.UUID("subgroup 2");
+ AccountGroup.UUID groupUuid =
+ groupOperations.newGroup().subgroups(subgroupUuid1, subgroupUuid2).create();
+
+ AccountGroup.UUID subgroupUuid3 = new AccountGroup.UUID("subgroup 3");
+ groupOperations
+ .group(groupUuid)
+ .forUpdate()
+ .clearSubgroups()
+ .addSubgroup(subgroupUuid3)
+ .update();
+
+ ImmutableSet<AccountGroup.UUID> subgroups = groupOperations.group(groupUuid).get().subgroups();
+ assertThat(subgroups).containsExactly(subgroupUuid3);
+ }
+
+ private GroupInput createArbitraryGroupInput() {
+ GroupInput groupInput = new GroupInput();
+ groupInput.name = name("verifiers-" + uniqueGroupNameIndex++);
+ return groupInput;
+ }
+
+ private GroupInfo getGroupFromServer(AccountGroup.UUID groupUuid) throws RestApiException {
+ return gApi.groups().id(groupUuid.get()).detail();
+ }
+
+ private AccountGroup.UUID createGroupInServer(GroupInput input) throws RestApiException {
+ GroupInfo group = gApi.groups().create(input).detail();
+ return new AccountGroup.UUID(group.id);
+ }
+
+ private static Correspondence<AccountInfo, Account.Id> getAccountToIdCorrespondence() {
+ return new Correspondence<AccountInfo, Account.Id>() {
+ @Override
+ public boolean compare(AccountInfo actualAccount, Account.Id expectedId) {
+ Account.Id accountId =
+ Optional.ofNullable(actualAccount)
+ .map(account -> account._accountId)
+ .map(Account.Id::new)
+ .orElse(null);
+ return Objects.equals(accountId, expectedId);
+ }
+
+ @Override
+ public String toString() {
+ return "has ID";
+ }
+ };
+ }
+
+ private static Correspondence<GroupInfo, AccountGroup.UUID> getGroupToUuidCorrespondence() {
+ return new Correspondence<GroupInfo, AccountGroup.UUID>() {
+ @Override
+ public boolean compare(GroupInfo actualGroup, AccountGroup.UUID expectedUuid) {
+ AccountGroup.UUID groupUuid =
+ Optional.ofNullable(actualGroup)
+ .map(group -> group.id)
+ .map(AccountGroup.UUID::new)
+ .orElse(null);
+ return Objects.equals(groupUuid, expectedUuid);
+ }
+
+ @Override
+ public String toString() {
+ return "has UUID";
+ }
+ };
+ }
+}
diff --git a/javatests/com/google/gerrit/extensions/registration/DynamicSetTest.java b/javatests/com/google/gerrit/extensions/registration/DynamicSetTest.java
index bbcee2d..c86160f 100644
--- a/javatests/com/google/gerrit/extensions/registration/DynamicSetTest.java
+++ b/javatests/com/google/gerrit/extensions/registration/DynamicSetTest.java
@@ -123,7 +123,7 @@
ds.add("bar", 2);
ds.add("bar", 3);
- Iterator<DynamicSet.Entry<Integer>> entryIterator = ds.entryIterator();
+ Iterator<DynamicSet.Entry<Integer>> entryIterator = ds.entries().iterator();
DynamicSet.Entry<Integer> next = entryIterator.next();
assertThat(next.getPluginName()).isEqualTo("foo");
assertThat(next.getProvider().get()).isEqualTo(1);
diff --git a/javatests/com/google/gerrit/gpg/PushCertificateCheckerTest.java b/javatests/com/google/gerrit/gpg/PushCertificateCheckerTest.java
index ad8f4311..266f868 100644
--- a/javatests/com/google/gerrit/gpg/PushCertificateCheckerTest.java
+++ b/javatests/com/google/gerrit/gpg/PushCertificateCheckerTest.java
@@ -184,7 +184,7 @@
}
String cert = payload + new String(bout.toByteArray(), UTF_8);
- Reader reader = new InputStreamReader(new ByteArrayInputStream(cert.getBytes(UTF_8)));
+ Reader reader = new InputStreamReader(new ByteArrayInputStream(cert.getBytes(UTF_8)), UTF_8);
PushCertificateParser parser = new PushCertificateParser(repo, signedPushConfig);
return parser.parse(reader);
}
diff --git a/javatests/com/google/gerrit/server/BUILD b/javatests/com/google/gerrit/server/BUILD
index 29e9a0b..7be1827 100644
--- a/javatests/com/google/gerrit/server/BUILD
+++ b/javatests/com/google/gerrit/server/BUILD
@@ -49,6 +49,7 @@
"//java/com/google/gerrit/server/cache/testing",
"//java/com/google/gerrit/server/group/testing",
"//java/com/google/gerrit/server/ioutil",
+ "//java/com/google/gerrit/server/logging",
"//java/com/google/gerrit/server/project/testing:project-test-util",
"//java/com/google/gerrit/server/restapi",
"//java/com/google/gerrit/server/schema",
diff --git a/javatests/com/google/gerrit/server/change/IncludedInResolverTest.java b/javatests/com/google/gerrit/server/change/IncludedInResolverTest.java
index e91c3b4..dca2dcb 100644
--- a/javatests/com/google/gerrit/server/change/IncludedInResolverTest.java
+++ b/javatests/com/google/gerrit/server/change/IncludedInResolverTest.java
@@ -14,10 +14,9 @@
package com.google.gerrit.server.change;
+import static com.google.common.truth.Truth.assertThat;
+
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.MergeCommand.FastForwardMode;
import org.eclipse.jgit.junit.RepositoryTestCase;
@@ -27,7 +26,6 @@
import org.eclipse.jgit.revwalk.RevTag;
import org.eclipse.jgit.revwalk.RevWalk;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -55,9 +53,6 @@
private RevCommit commit_v1_3;
private RevCommit commit_v2_5;
- private List<String> expTags = new ArrayList<>();
- private List<String> expBranches = new ArrayList<>();
-
private RevWalk revWalk;
@Override
@@ -140,12 +135,8 @@
IncludedInResolver.Result detail = resolve(commit_v2_5);
// Check that only tags and branches which refer the tip are returned
- expTags.add(TAG_2_5);
- expTags.add(TAG_2_5_ANNOTATED);
- expTags.add(TAG_2_5_ANNOTATED_TWICE);
- assertEquals(expTags, detail.getTags());
- expBranches.add(BRANCH_2_5);
- assertEquals(expBranches, detail.getBranches());
+ assertThat(detail.tags()).containsExactly(TAG_2_5, TAG_2_5_ANNOTATED, TAG_2_5_ANNOTATED_TWICE);
+ assertThat(detail.branches()).containsExactly(BRANCH_2_5);
}
@Test
@@ -154,22 +145,18 @@
IncludedInResolver.Result detail = resolve(commit_initial);
// Check whether all tags and branches are returned
- expTags.add(TAG_1_0);
- expTags.add(TAG_1_0_1);
- expTags.add(TAG_1_3);
- expTags.add(TAG_2_0);
- expTags.add(TAG_2_0_1);
- expTags.add(TAG_2_5);
- expTags.add(TAG_2_5_ANNOTATED);
- expTags.add(TAG_2_5_ANNOTATED_TWICE);
- assertEquals(expTags, detail.getTags());
-
- expBranches.add(BRANCH_MASTER);
- expBranches.add(BRANCH_1_0);
- expBranches.add(BRANCH_1_3);
- expBranches.add(BRANCH_2_0);
- expBranches.add(BRANCH_2_5);
- assertEquals(expBranches, detail.getBranches());
+ assertThat(detail.tags())
+ .containsExactly(
+ TAG_1_0,
+ TAG_1_0_1,
+ TAG_1_3,
+ TAG_2_0,
+ TAG_2_0_1,
+ TAG_2_5,
+ TAG_2_5_ANNOTATED,
+ TAG_2_5_ANNOTATED_TWICE);
+ assertThat(detail.branches())
+ .containsExactly(BRANCH_MASTER, BRANCH_1_0, BRANCH_1_3, BRANCH_2_0, BRANCH_2_5);
}
@Test
@@ -178,27 +165,15 @@
IncludedInResolver.Result detail = resolve(commit_v1_3);
// Check whether all succeeding tags and branches are returned
- expTags.add(TAG_1_3);
- expTags.add(TAG_2_5);
- expTags.add(TAG_2_5_ANNOTATED);
- expTags.add(TAG_2_5_ANNOTATED_TWICE);
- assertEquals(expTags, detail.getTags());
-
- expBranches.add(BRANCH_1_3);
- expBranches.add(BRANCH_2_5);
- assertEquals(expBranches, detail.getBranches());
+ assertThat(detail.tags())
+ .containsExactly(TAG_1_3, TAG_2_5, TAG_2_5_ANNOTATED, TAG_2_5_ANNOTATED_TWICE);
+ assertThat(detail.branches()).containsExactly(BRANCH_1_3, BRANCH_2_5);
}
private IncludedInResolver.Result resolve(RevCommit commit) throws Exception {
return IncludedInResolver.resolve(db, revWalk, commit);
}
- private void assertEquals(List<String> list1, List<String> list2) {
- Collections.sort(list1);
- Collections.sort(list2);
- Assert.assertEquals(list1, list2);
- }
-
private void createAndCheckoutBranch(ObjectId objectId, String branchName) throws IOException {
String fullBranchName = "refs/heads/" + branchName;
super.createBranch(objectId, fullBranchName);
diff --git a/javatests/com/google/gerrit/server/logging/LoggingContextAwareExecutorServiceTest.java b/javatests/com/google/gerrit/server/logging/LoggingContextAwareExecutorServiceTest.java
new file mode 100644
index 0000000..5117c01
--- /dev/null
+++ b/javatests/com/google/gerrit/server/logging/LoggingContextAwareExecutorServiceTest.java
@@ -0,0 +1,71 @@
+// Copyright (C) 2018 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.logging;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.truth.Expect;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class LoggingContextAwareExecutorServiceTest {
+ @Rule public final Expect expect = Expect.create();
+
+ @Test
+ public void loggingContextPropagationToBackgroundThread() throws Exception {
+ assertThat(LoggingContext.getInstance().getTags().isEmpty()).isTrue();
+ assertForceLogging(false);
+ try (TraceContext traceContext = TraceContext.open().forceLogging().addTag("foo", "bar")) {
+ SortedMap<String, SortedSet<Object>> tagMap = LoggingContext.getInstance().getTags().asMap();
+ assertThat(tagMap.keySet()).containsExactly("foo");
+ assertThat(tagMap.get("foo")).containsExactly("bar");
+ assertForceLogging(true);
+
+ ExecutorService executor =
+ new LoggingContextAwareExecutorService(Executors.newFixedThreadPool(1));
+ executor
+ .submit(
+ () -> {
+ // Verify that the tags and force logging flag have been propagated to the new
+ // thread.
+ SortedMap<String, SortedSet<Object>> threadTagMap =
+ LoggingContext.getInstance().getTags().asMap();
+ expect.that(threadTagMap.keySet()).containsExactly("foo");
+ expect.that(threadTagMap.get("foo")).containsExactly("bar");
+ expect
+ .that(LoggingContext.getInstance().shouldForceLogging(null, null, false))
+ .isTrue();
+ })
+ .get();
+
+ // Verify that tags and force logging flag in the outer thread are still set.
+ tagMap = LoggingContext.getInstance().getTags().asMap();
+ assertThat(tagMap.keySet()).containsExactly("foo");
+ assertThat(tagMap.get("foo")).containsExactly("bar");
+ assertForceLogging(true);
+ }
+ assertThat(LoggingContext.getInstance().getTags().isEmpty()).isTrue();
+ assertForceLogging(false);
+ }
+
+ private void assertForceLogging(boolean expected) {
+ assertThat(LoggingContext.getInstance().shouldForceLogging(null, null, false))
+ .isEqualTo(expected);
+ }
+}
diff --git a/javatests/com/google/gerrit/server/logging/LoggingContextAwareThreadFactoryTest.java b/javatests/com/google/gerrit/server/logging/LoggingContextAwareThreadFactoryTest.java
deleted file mode 100644
index 1164e27..0000000
--- a/javatests/com/google/gerrit/server/logging/LoggingContextAwareThreadFactoryTest.java
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright (C) 2018 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.logging;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import com.google.common.truth.Expect;
-import java.util.SortedMap;
-import java.util.SortedSet;
-import org.junit.Rule;
-import org.junit.Test;
-
-public class LoggingContextAwareThreadFactoryTest {
- @Rule public final Expect expect = Expect.create();
-
- @Test
- public void loggingContextPropagationToNewThread() throws Exception {
- assertThat(LoggingContext.getInstance().getTags().isEmpty()).isTrue();
- assertForceLogging(false);
- try (TraceContext traceContext = TraceContext.open().forceLogging().addTag("foo", "bar")) {
- SortedMap<String, SortedSet<Object>> tagMap = LoggingContext.getInstance().getTags().asMap();
- assertThat(tagMap.keySet()).containsExactly("foo");
- assertThat(tagMap.get("foo")).containsExactly("bar");
- assertForceLogging(true);
-
- Thread thread =
- new LoggingContextAwareThreadFactory(r -> new Thread(r, "test-thread"))
- .newThread(
- () -> {
- // Verify that the tags and force logging flag have been propagated to the new
- // thread.
- SortedMap<String, SortedSet<Object>> threadTagMap =
- LoggingContext.getInstance().getTags().asMap();
- expect.that(threadTagMap.keySet()).containsExactly("foo");
- expect.that(threadTagMap.get("foo")).containsExactly("bar");
- expect
- .that(LoggingContext.getInstance().shouldForceLogging(null, null, false))
- .isTrue();
- });
-
- // Execute in background.
- thread.start();
- thread.join();
-
- // Verify that tags and force logging flag in the outer thread are still set.
- tagMap = LoggingContext.getInstance().getTags().asMap();
- assertThat(tagMap.keySet()).containsExactly("foo");
- assertThat(tagMap.get("foo")).containsExactly("bar");
- assertForceLogging(true);
- }
- assertThat(LoggingContext.getInstance().getTags().isEmpty()).isTrue();
- assertForceLogging(false);
- }
-
- @Test
- public void loggingContextPropagationToSameThread() throws Exception {
- assertThat(LoggingContext.getInstance().getTags().isEmpty()).isTrue();
- assertForceLogging(false);
- try (TraceContext traceContext = TraceContext.open().forceLogging().addTag("foo", "bar")) {
- SortedMap<String, SortedSet<Object>> tagMap = LoggingContext.getInstance().getTags().asMap();
- assertThat(tagMap.keySet()).containsExactly("foo");
- assertThat(tagMap.get("foo")).containsExactly("bar");
- assertForceLogging(true);
-
- Thread thread =
- new LoggingContextAwareThreadFactory()
- .newThread(
- () -> {
- // Verify that the tags and force logging flag have been propagated to the new
- // thread.
- SortedMap<String, SortedSet<Object>> threadTagMap =
- LoggingContext.getInstance().getTags().asMap();
- expect.that(threadTagMap.keySet()).containsExactly("foo");
- expect.that(threadTagMap.get("foo")).containsExactly("bar");
- expect
- .that(LoggingContext.getInstance().shouldForceLogging(null, null, false))
- .isTrue();
- });
-
- // Execute in the same thread.
- thread.run();
-
- // Verify that tags and force logging flag in the outer thread are still set.
- tagMap = LoggingContext.getInstance().getTags().asMap();
- assertThat(tagMap.keySet()).containsExactly("foo");
- assertThat(tagMap.get("foo")).containsExactly("bar");
- assertForceLogging(true);
- }
- assertThat(LoggingContext.getInstance().getTags().isEmpty()).isTrue();
- assertForceLogging(false);
- }
-
- private void assertForceLogging(boolean expected) {
- assertThat(LoggingContext.getInstance().shouldForceLogging(null, null, false))
- .isEqualTo(expected);
- }
-}
diff --git a/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java b/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
index c774fc5..8cd2753 100644
--- a/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
+++ b/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
@@ -426,7 +426,7 @@
@Test
public void approvalsPostSubmit() throws Exception {
Change c = newChange();
- RequestId submissionId = RequestId.forChange(c);
+ RequestId submissionId = submissionId(c);
ChangeUpdate update = newUpdate(c, changeOwner);
update.putApproval("Code-Review", (short) 1);
update.putApproval("Verified", (short) 1);
@@ -461,7 +461,7 @@
@Test
public void approvalsDuringSubmit() throws Exception {
Change c = newChange();
- RequestId submissionId = RequestId.forChange(c);
+ RequestId submissionId = submissionId(c);
ChangeUpdate update = newUpdate(c, changeOwner);
update.putApproval("Code-Review", (short) 1);
update.putApproval("Verified", (short) 1);
@@ -598,7 +598,7 @@
@Test
public void submitRecords() throws Exception {
Change c = newChange();
- RequestId submissionId = RequestId.forChange(c);
+ RequestId submissionId = submissionId(c);
ChangeUpdate update = newUpdate(c, changeOwner);
update.setSubjectForCommit("Submit patch set 1");
@@ -640,7 +640,7 @@
@Test
public void latestSubmitRecordsOnly() throws Exception {
Change c = newChange();
- RequestId submissionId = RequestId.forChange(c);
+ RequestId submissionId = submissionId(c);
ChangeUpdate update = newUpdate(c, changeOwner);
update.setSubjectForCommit("Submit patch set 1");
update.merge(
@@ -941,7 +941,7 @@
// Finish off by merging the change.
update = newUpdate(c, changeOwner);
update.merge(
- RequestId.forChange(c),
+ submissionId(c),
ImmutableList.of(
submitRecord(
"NOT_READY",
@@ -3141,4 +3141,8 @@
update.commit();
return tr.parseBody(commit);
}
+
+ private RequestId submissionId(Change c) {
+ return new RequestId(c.getId().toString());
+ }
}
diff --git a/javatests/com/google/gerrit/server/notedb/CommitMessageOutputTest.java b/javatests/com/google/gerrit/server/notedb/CommitMessageOutputTest.java
index 43e2602..8daf67f 100644
--- a/javatests/com/google/gerrit/server/notedb/CommitMessageOutputTest.java
+++ b/javatests/com/google/gerrit/server/notedb/CommitMessageOutputTest.java
@@ -151,7 +151,7 @@
ChangeUpdate update = newUpdate(c, changeOwner);
update.setSubjectForCommit("Submit patch set 1");
- RequestId submissionId = RequestId.forChange(c);
+ RequestId submissionId = submissionId(c);
update.merge(
submissionId,
ImmutableList.of(
@@ -220,7 +220,7 @@
ChangeUpdate update = newUpdate(c, changeOwner);
update.setSubjectForCommit("Submit patch set 1");
- RequestId submissionId = RequestId.forChange(c);
+ RequestId submissionId = submissionId(c);
update.merge(
submissionId, ImmutableList.of(submitRecord("RULE_ERROR", "Problem with patch set:\n1")));
update.commit();
@@ -424,4 +424,8 @@
RevCommit commit = parseCommit(commitId);
assertThat(commit.getFullMessage()).isEqualTo(expected);
}
+
+ private RequestId submissionId(Change c) {
+ return new RequestId(c.getId().toString());
+ }
}
diff --git a/javatests/com/google/gerrit/server/rules/BUILD b/javatests/com/google/gerrit/server/rules/BUILD
index 42452df..8f4c90d 100644
--- a/javatests/com/google/gerrit/server/rules/BUILD
+++ b/javatests/com/google/gerrit/server/rules/BUILD
@@ -7,9 +7,11 @@
resources = ["//prologtests:gerrit_common_test"],
deps = [
"//java/com/google/gerrit/common:server",
+ "//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server",
"//java/com/google/gerrit/server/project/testing:project-test-util",
"//java/com/google/gerrit/testing:gerrit-test-util",
+ "//lib:guava",
"//lib/guice",
"//lib/jgit/org.eclipse.jgit:jgit",
"//lib/prolog:runtime",
diff --git a/javatests/com/google/gerrit/server/rules/IgnoreSelfApprovalRuleTest.java b/javatests/com/google/gerrit/server/rules/IgnoreSelfApprovalRuleTest.java
new file mode 100644
index 0000000..27f4423
--- /dev/null
+++ b/javatests/com/google/gerrit/server/rules/IgnoreSelfApprovalRuleTest.java
@@ -0,0 +1,96 @@
+// Copyright (C) 2018 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.rules;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.common.data.LabelType;
+import com.google.gerrit.common.data.LabelValue;
+import com.google.gerrit.reviewdb.client.Account;
+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 java.time.Instant;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import org.junit.Test;
+
+public class IgnoreSelfApprovalRuleTest {
+ private static final Change.Id CHANGE_ID = new Change.Id(100);
+ private static final PatchSet.Id PS_ID = new PatchSet.Id(CHANGE_ID, 1);
+ private static final LabelType VERIFIED = makeLabel("Verified");
+ private static final Account.Id USER1 = makeAccount(100001);
+
+ @Test
+ public void filtersByLabel() {
+ LabelType codeReview = makeLabel("Code-Review");
+ PatchSetApproval approvalVerified = makeApproval(VERIFIED.getLabelId(), USER1, 2);
+ PatchSetApproval approvalCr = makeApproval(codeReview.getLabelId(), USER1, 2);
+
+ Collection<PatchSetApproval> filteredApprovals =
+ IgnoreSelfApprovalRule.filterApprovalsByLabel(
+ ImmutableList.of(approvalVerified, approvalCr), VERIFIED);
+
+ assertThat(filteredApprovals).containsExactly(approvalVerified);
+ }
+
+ @Test
+ public void filtersVotesFromUser() {
+ PatchSetApproval approvalM2 = makeApproval(VERIFIED.getLabelId(), USER1, -2);
+ PatchSetApproval approvalM1 = makeApproval(VERIFIED.getLabelId(), USER1, -1);
+
+ ImmutableList<PatchSetApproval> approvals =
+ ImmutableList.of(
+ approvalM2,
+ approvalM1,
+ makeApproval(VERIFIED.getLabelId(), USER1, 0),
+ makeApproval(VERIFIED.getLabelId(), USER1, +1),
+ makeApproval(VERIFIED.getLabelId(), USER1, +2));
+
+ Collection<PatchSetApproval> filteredApprovals =
+ IgnoreSelfApprovalRule.filterOutPositiveApprovalsOfUser(approvals, USER1);
+
+ assertThat(filteredApprovals).containsExactly(approvalM1, approvalM2);
+ }
+
+ private static LabelType makeLabel(String labelName) {
+ List<LabelValue> values = new ArrayList<>();
+ // The label text is irrelevant here, only the numerical value is used
+ values.add(new LabelValue((short) -2, "-2"));
+ values.add(new LabelValue((short) -1, "-1"));
+ values.add(new LabelValue((short) 0, "No vote."));
+ values.add(new LabelValue((short) 1, "+1"));
+ values.add(new LabelValue((short) 2, "+2"));
+ return new LabelType(labelName, values);
+ }
+
+ private static PatchSetApproval makeApproval(LabelId labelId, Account.Id accountId, int value) {
+ PatchSetApproval.Key key = makeKey(PS_ID, accountId, labelId);
+ return new PatchSetApproval(key, (short) value, Date.from(Instant.now()));
+ }
+
+ private static PatchSetApproval.Key makeKey(
+ PatchSet.Id psId, Account.Id accountId, LabelId labelId) {
+ return new PatchSetApproval.Key(psId, accountId, labelId);
+ }
+
+ private static Account.Id makeAccount(int account) {
+ return new Account.Id(account);
+ }
+}
diff --git a/plugins/BUILD b/plugins/BUILD
index b7364be..a7622b2 100644
--- a/plugins/BUILD
+++ b/plugins/BUILD
@@ -38,6 +38,8 @@
"//java/com/google/gerrit/metrics/dropwizard",
"//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server/audit",
+ "//java/com/google/gerrit/server/logging",
+ "//java/com/google/gerrit/server/schema",
"//java/com/google/gerrit/util/http",
"//lib/commons:compress",
"//lib/commons:dbcp",
diff --git a/plugins/codemirror-editor b/plugins/codemirror-editor
index 4ebf98c..22342a6 160000
--- a/plugins/codemirror-editor
+++ b/plugins/codemirror-editor
@@ -1 +1 @@
-Subproject commit 4ebf98c77086477a5fa63e339a539b47d4e8d202
+Subproject commit 22342a6da26c75b14bc629331c339d1b820b4d39
diff --git a/plugins/hooks b/plugins/hooks
index 07672f3..cc74144 160000
--- a/plugins/hooks
+++ b/plugins/hooks
@@ -1 +1 @@
-Subproject commit 07672f31880ba80300b38492df9d0acfcd6ee00a
+Subproject commit cc74144db755a18c5a63764a336b93ab3d1be1fe
diff --git a/plugins/replication b/plugins/replication
index b62f006..d557ccc 160000
--- a/plugins/replication
+++ b/plugins/replication
@@ -1 +1 @@
-Subproject commit b62f006b1350180de0af02c82fb18fb290a2548f
+Subproject commit d557ccc642c59a55750f560ce0d98870e1550d65
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
index e905e038..ebd72ad 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
@@ -122,7 +122,7 @@
_changeComments: Object,
_canStartReview: {
type: Boolean,
- computed: '_computeCanStartReview(_loggedIn, _change, _account)',
+ computed: '_computeCanStartReview(_change)',
},
_comments: Object,
/** @type {?} */
@@ -1341,9 +1341,9 @@
});
},
- _computeCanStartReview(loggedIn, change, account) {
- return !!(loggedIn && change.work_in_progress &&
- change.owner._account_id === account._account_id);
+ _computeCanStartReview(change) {
+ return !!(change.actions && change.actions.ready &&
+ change.actions.ready.enabled);
},
_computeReplyDisabled() { return false; },
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
index b5b8cd9..4e6cb6e 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
@@ -1411,18 +1411,24 @@
});
test('canStartReview computation', () => {
- const account1 = {_account_id: 1};
- const account2 = {_account_id: 2};
- const change = {
- owner: {_account_id: 1},
+ const change1 = {};
+ const change2 = {
+ actions: {
+ ready: {
+ enabled: true,
+ },
+ },
};
- assert.isFalse(element._computeCanStartReview(true, change, account1));
- change.work_in_progress = false;
- assert.isFalse(element._computeCanStartReview(true, change, account1));
- change.work_in_progress = true;
- assert.isTrue(element._computeCanStartReview(true, change, account1));
- assert.isFalse(element._computeCanStartReview(false, change, account1));
- assert.isFalse(element._computeCanStartReview(true, change, account2));
+ const change3 = {
+ actions: {
+ ready: {
+ label: 'Ready for Review',
+ },
+ },
+ };
+ assert.isFalse(element._computeCanStartReview(change1));
+ assert.isTrue(element._computeCanStartReview(change2));
+ assert.isFalse(element._computeCanStartReview(change3));
});
});
diff --git a/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list_test.html b/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list_test.html
index 8ca40e7..9996abc 100644
--- a/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list_test.html
+++ b/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list_test.html
@@ -40,7 +40,7 @@
setup(() => {
element = fixture('basic');
sandbox = sinon.sandbox.create();
- sandbox.stub(Gerrit.Nav, 'overrideCommentlinks', x => x);
+ sandbox.stub(Gerrit.Nav, 'mapCommentlinks', x => x);
});
teardown(() => { sandbox.restore(); });
diff --git a/polygerrit-ui/app/elements/change/gr-message/gr-message.html b/polygerrit-ui/app/elements/change/gr-message/gr-message.html
index 5608dec..00157ab 100644
--- a/polygerrit-ui/app/elements/change/gr-message/gr-message.html
+++ b/polygerrit-ui/app/elements/change/gr-message/gr-message.html
@@ -178,34 +178,20 @@
<gr-account-label
account="[[author]]"
hide-avatar></gr-account-label>
- <template is="dom-if" if="[[_successfulParse]]">
- <template is="dom-if" if="[[_parsedVotes.length]]">voted</template>
- <template is="dom-repeat" items="[[_parsedVotes]]" as="score">
- <span class$="score [[_computeScoreClass(score, labelExtremes)]]">
- [[score.label]] [[score.value]]
- </span>
- </template>
- [[_computeConversationalString(_parsedVotes, _parsedPatchNum, _parsedCommentCount)]]
+ <template is="dom-repeat" items="[[_getScores(message)]]" as="score">
+ <span class$="score [[_computeScoreClass(score, labelExtremes)]]">
+ [[score.label]] [[score.value]]
+ </span>
</template>
</div>
<template is="dom-if" if="[[message.message]]">
<div class="content">
- <template is="dom-if" if="[[_successfulParse]]">
- <div class="message hideOnOpen">[[_parsedChangeMessage]]</div>
- <gr-formatted-text
- no-trailing-margin
- class="message hideOnCollapsed"
- content="[[_parsedChangeMessage]]"
- config="[[_projectConfig.commentlinks]]"></gr-formatted-text>
- </template>
- <template is="dom-if" if="[[!_successfulParse]]">
- <div class="message hideOnOpen">[[message.message]]</div>
- <gr-formatted-text
- no-trailing-margin
- class="message hideOnCollapsed"
- content="[[message.message]]"
- config="[[_projectConfig.commentlinks]]"></gr-formatted-text>
- </template>
+ <div class="message hideOnOpen">[[message.message]]</div>
+ <gr-formatted-text
+ no-trailing-margin
+ class="message hideOnCollapsed"
+ content="[[message.message]]"
+ config="[[_projectConfig.commentlinks]]"></gr-formatted-text>
<div class="replyContainer" hidden$="[[!showReplyButton]]" hidden>
<gr-button link small on-tap="_handleReplyTap">Reply</gr-button>
</div>
diff --git a/polygerrit-ui/app/elements/change/gr-message/gr-message.js b/polygerrit-ui/app/elements/change/gr-message/gr-message.js
index addd660..0590c73 100644
--- a/polygerrit-ui/app/elements/change/gr-message/gr-message.js
+++ b/polygerrit-ui/app/elements/change/gr-message/gr-message.js
@@ -17,8 +17,7 @@
(function() {
'use strict';
- const PATCH_SET_PREFIX_PATTERN = /^Patch Set (\d)+:[ ]?/;
- const COMMENTS_COUNT_PATTERN = /^\((\d+)( inline)? comments?\)$/;
+ const PATCH_SET_PREFIX_PATTERN = /^Patch Set \d+: /;
const LABEL_TITLE_SCORE_PATTERN = /^([A-Za-z0-9-]+)([+-]\d+)$/;
Polymer({
@@ -102,17 +101,10 @@
type: Boolean,
value: false,
},
-
- _parsedPatchNum: String,
- _parsedCommentCount: String,
- _parsedVotes: Array,
- _parsedChangeMessage: String,
- _successfulParse: Boolean,
},
observers: [
'_updateExpandedClass(message.expanded)',
- '_consumeMessage(message.message)',
],
ready() {
@@ -192,6 +184,19 @@
return event.type === 'REVIEWER_UPDATE';
},
+ _getScores(message) {
+ if (!message.message) { return []; }
+ const line = message.message.split('\n', 1)[0];
+ const patchSetPrefix = PATCH_SET_PREFIX_PATTERN;
+ if (!line.match(patchSetPrefix)) { return []; }
+ const scoresRaw = line.split(patchSetPrefix)[1];
+ if (!scoresRaw) { return []; }
+ return scoresRaw.split(' ')
+ .map(s => s.match(LABEL_TITLE_SCORE_PATTERN))
+ .filter(ms => ms && ms.length === 3)
+ .map(ms => ({label: ms[1], value: ms[2]}));
+ },
+
_computeScoreClass(score, labelExtremes) {
const classes = [];
if (score.value > 0) {
@@ -255,73 +260,5 @@
e.stopPropagation();
this.set('message.expanded', !this.message.expanded);
},
-
- /**
- * Attempts to consume a change message to create a shorter and more legible
- * format. If the function encounters unexpected characters at any point, it
- * sets the _successfulParse flag to false and terminates, causing the UI to
- * fall back to displaying the entirety of the change message.
- *
- * A successful parse results in a one-liner that reads:
- * `${AVATAR} voted ${VOTES} and left ${NUM} comment(s) on ${PATCHSET}`
- *
- * @param {string} text
- */
- _consumeMessage(text) {
- this._parsedPatchNum = '';
- this._parsedCommentCount = '';
- this._parsedChangeMessage = '';
- this._parsedVotes = [];
- if (!text) {
- // No message body means nothing to parse.
- this._successfulParse = false;
- return;
- }
- const lines = text.split('\n');
- const messageLines = lines.shift().split(PATCH_SET_PREFIX_PATTERN);
- if (!messageLines[1]) {
- // Message is in an unexpected format.
- this._successfulParse = false;
- return;
- }
- this._parsedPatchNum = messageLines[1];
- if (messageLines[2]) {
- // Content after the colon is always vote information. If it is in the
- // most up to date schema, parse it. Otherwise, cancel the parsing
- // completely.
- let match;
- for (const score of messageLines[2].split(' ')) {
- match = score.match(LABEL_TITLE_SCORE_PATTERN);
- if (!match || match.length !== 3) {
- this._successfulParse = false;
- return;
- }
- this._parsedVotes.push({label: match[1], value: match[2]});
- }
- }
- // Remove empty line.
- lines.shift();
- if (lines.length) {
- const commentMatch = lines[0].match(COMMENTS_COUNT_PATTERN);
- if (commentMatch) {
- this._parsedCommentCount = commentMatch[1];
- // Remove comment line and the following empty line.
- lines.splice(0, 2);
- }
- this._parsedChangeMessage = lines.join('\n');
- }
- this._successfulParse = true;
- },
-
- _computeConversationalString(votes, patchNum, commentCount) {
- let clause = ' on Patch Set ' + patchNum;
- if (commentCount) {
- let commentStr = ' comment';
- if (parseInt(commentCount, 10) > 1) { commentStr += 's'; }
- clause = ' left ' + commentCount + commentStr + clause;
- if (votes.length) { clause = ' and' + clause; }
- }
- return clause;
- },
});
})();
diff --git a/polygerrit-ui/app/elements/change/gr-message/gr-message_test.html b/polygerrit-ui/app/elements/change/gr-message/gr-message_test.html
index 6bb4618..870f366 100644
--- a/polygerrit-ui/app/elements/change/gr-message/gr-message_test.html
+++ b/polygerrit-ui/app/elements/change/gr-message/gr-message_test.html
@@ -169,9 +169,7 @@
author: {},
expanded: false,
message: 'Patch Set 1: Verified+1 Code-Review-2 Trybot-Ready+1',
- _revision_number: 1,
};
- element.comments = {};
element.labelExtremes = {
'Verified': {max: 1, min: -1},
'Code-Review': {max: 2, min: -2},
@@ -201,145 +199,5 @@
const scoreChips = Polymer.dom(element.root).querySelectorAll('.score');
assert.equal(scoreChips.length, 0);
});
-
- suite('_consumeMessage', () => {
- const assertConsumeFailed = str => {
- element._consumeMessage(str);
- assert.isFalse(element._successfulParse);
- };
-
- test('no message body', () => {
- assertConsumeFailed('');
- });
-
- test('known old schema', () => {
- const str = 'Patch Set 1: Looks good to me, approved; Verified';
- assertConsumeFailed(str);
- });
-
- test('known old schema 2', () => {
- const str = [
- 'Patch Set 2: Looks good to me',
- '',
- 'Patch set 2 compiles, and runs as expected.',
- ].join('\n');
- assertConsumeFailed(str);
- });
-
- test('known old schema 3', () => {
- const str = 'Patch Set 2: (1 inline comment)';
- assertConsumeFailed(str);
- });
-
- test('known old schema 4', () => {
- const str = [
- 'Patch Set 2: Looks good to me',
- '',
- '(1 inline comment)',
- ].join('\n');
- assertConsumeFailed(str);
- });
-
- test('just change message', () => {
- const str = [
- 'Patch Set 2:',
- '',
- 'I think you should reconsider this approach.',
- 'It really makes no sense.',
- ].join('\n');
-
- element._consumeMessage(str);
-
- assert.isTrue(element._successfulParse);
- assert.equal(element._parsedPatchNum, '2');
- assert.equal(element._parsedCommentCount, '');
- assert.deepEqual(element._parsedVotes, []);
- assert.equal(element._parsedChangeMessage, [
- 'I think you should reconsider this approach.',
- 'It really makes no sense.',
- ].join('\n'));
- });
-
- test('just votes', () => {
- element._consumeMessage('Patch Set 2: Code-Review-Label+1 Verified+1');
-
- assert.isTrue(element._successfulParse);
- assert.equal(element._parsedPatchNum, '2');
- assert.equal(element._parsedCommentCount, '');
- assert.deepEqual(element._parsedVotes, [
- {label: 'Code-Review-Label', value: '+1'},
- {label: 'Verified', value: '+1'},
- ]);
- assert.equal(element._parsedChangeMessage, '');
- });
-
- test('just comments', () => {
- const str = [
- 'Patch Set 2:',
- '',
- '(8 comments)',
- ].join('\n');
-
- element._consumeMessage(str);
-
- assert.isTrue(element._successfulParse);
- assert.equal(element._parsedPatchNum, '2');
- assert.equal(element._parsedCommentCount, '8');
- assert.deepEqual(element._parsedVotes, []);
- assert.equal(element._parsedChangeMessage, '');
- });
-
- test('vote with comments and change message', () => {
- const str = [
- 'Patch Set 2: Code-Review-Label+1 Verified+1',
- '',
- '(1 comment)',
- '',
- 'LGTM',
- '',
- 'Nice work, just one nit.',
- ].join('\n');
-
- element._consumeMessage(str);
-
- assert.isTrue(element._successfulParse);
- assert.equal(element._parsedPatchNum, '2');
- assert.equal(element._parsedCommentCount, '1');
- assert.deepEqual(element._parsedVotes, [
- {label: 'Code-Review-Label', value: '+1'},
- {label: 'Verified', value: '+1'},
- ]);
- assert.equal(element._parsedChangeMessage, [
- 'LGTM',
- '',
- 'Nice work, just one nit.',
- ].join('\n'));
- });
-
- test('vote with change message', () => {
- const str = [
- 'Patch Set 2: Code-Review-Label+2 Verified+1',
- '',
- 'LGTM',
- '',
- 'Nice work.',
- ].join('\n');
-
- element._consumeMessage(str);
-
- assert.isTrue(element._successfulParse);
- assert.equal(element._parsedPatchNum, '2');
- assert.equal(element._parsedCommentCount, '');
- assert.deepEqual(element._parsedVotes, [
- {label: 'Code-Review-Label', value: '+2'},
- {label: 'Verified', value: '+1'},
- ]);
- assert.equal(element._parsedChangeMessage, [
- 'LGTM',
- '',
- 'Nice work.',
- ].join('\n'));
- });
- });
});
</script>
diff --git a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html
index aa04194..c5e32cc 100644
--- a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html
+++ b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html
@@ -138,7 +138,7 @@
_generateWeblinks: uninitialized,
/** @type {Function} */
- overrideCommentlinks: uninitialized,
+ mapCommentlinks: uninitialized,
/**
* @param {number=} patchNum
@@ -152,23 +152,38 @@
/**
* Setup router implementation.
- * @param {Function} navigate
- * @param {Function} generateUrl
- * @param {Function} generateWeblinks
- * @param {Function} overrideCommentlinks
+ * @param {function(!string)} navigate the router-abstracted equivalent of
+ * `window.location.href = ...`. Takes a string.
+ * @param {function(!Object): string} generateUrl generates a URL given
+ * navigation parameters, detailed in the file header.
+ * @param {function(!Object): string} generateWeblinks weblinks generator
+ * function takes single payload parameter with type property that
+ * determines which
+ * part of the UI is the consumer of the weblinks. type property can
+ * be one of file, change, or patchset.
+ * - For file type, payload will also contain string properties: repo,
+ * commit, file.
+ * - For patchset type, payload will also contain string properties:
+ * repo, commit.
+ * - For change type, payload will also contain string properties:
+ * repo, commit. If server provides weblinks, those will be passed
+ * as options.weblinks property on the main payload object.
+ * @param {function(!Object): Object} mapCommentlinks provides an escape
+ * hatch to modify the commentlinks object, e.g. if it contains any
+ * relative URLs.
*/
- setup(navigate, generateUrl, generateWeblinks, overrideCommentlinks) {
+ setup(navigate, generateUrl, generateWeblinks, mapCommentlinks) {
this._navigate = navigate;
this._generateUrl = generateUrl;
this._generateWeblinks = generateWeblinks;
- this.overrideCommentlinks = overrideCommentlinks;
+ this.mapCommentlinks = mapCommentlinks;
},
destroy() {
this._navigate = uninitialized;
this._generateUrl = uninitialized;
this._generateWeblinks = uninitialized;
- this.overrideCommentlinks = uninitialized;
+ this.mapCommentlinks = uninitialized;
},
/**
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
index 4f0a73b..9f1b412 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
@@ -96,7 +96,10 @@
reflectToAttribute: true,
},
noRenderOnPrefsChange: Boolean,
- comments: Object,
+ comments: {
+ type: Object,
+ value: {left: [], right: []},
+ },
lineWrapping: {
type: Boolean,
value: false,
diff --git a/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text.js b/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text.js
index 091cb75..530da02 100644
--- a/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text.js
+++ b/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text.js
@@ -60,7 +60,7 @@
* commentLink patterns
*/
_contentOrConfigChanged(content, config) {
- config = Gerrit.Nav.overrideCommentlinks(config);
+ config = Gerrit.Nav.mapCommentlinks(config);
const output = Polymer.dom(this.$.output);
output.textContent = '';
const parser = new GrLinkTextParser(config,
diff --git a/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_test.html b/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_test.html
index fc76da5..23c1442 100644
--- a/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_test.html
@@ -44,7 +44,7 @@
setup(() => {
element = fixture('basic');
sandbox = sinon.sandbox.create();
- sandbox.stub(Gerrit.Nav, 'overrideCommentlinks', x => x);
+ sandbox.stub(Gerrit.Nav, 'mapCommentlinks', x => x);
element.config = {
ph: {
match: '([Bb]ug|[Ii]ssue)\\s*#?(\\d+)',
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
index 84c1803..af10b8371 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
@@ -1498,11 +1498,28 @@
*/
getRepos(filter, reposPerPage, opt_offset) {
const defaultFilter = 'state:active OR state:read-only';
+ const namePartDelimiters = /[@.\-\s\/_]/g;
const offset = opt_offset || 0;
+ if (filter && !filter.includes(':') && filter.match(namePartDelimiters)) {
+ // The query language specifies hyphens as operators. Split the string
+ // by hyphens and 'AND' the parts together as 'inname:' queries.
+ // If the filter includes a semicolon, the user is using a more complex
+ // query so we trust them and don't do any magic under the hood.
+ const originalFilter = filter;
+ filter = '';
+ originalFilter.split(namePartDelimiters).forEach(part => {
+ if (part) {
+ filter += (filter === '' ? 'inname:' : ' AND inname:') + part;
+ }
+ });
+ }
+ // Check if filter is now empty which could be either because the user did
+ // not provide it or because the user provided only a split character.
if (!filter) {
filter = defaultFilter;
}
+
filter = filter.trim();
const encodedFilter = encodeURIComponent(filter);
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
index b7466ef..d9656e4 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
@@ -951,16 +951,46 @@
'/projects/?n=26&S=25&query=test');
});
- test('with filter', () => {
- element.getRepos('test/test/test', 25);
+ test('with blank', () => {
+ element.getRepos('test/test', 25);
assert.equal(element._fetchSharedCacheURL.lastCall.args[0].url,
- '/projects/?n=26&S=0&query=test%2Ftest%2Ftest');
+ '/projects/?n=26&S=0&query=inname%3Atest%20AND%20inname%3Atest');
});
- test('with regex filter', () => {
- element.getRepos('^test.*', 25);
+ test('with hyphen', () => {
+ element.getRepos('foo-bar', 25);
assert.equal(element._fetchSharedCacheURL.lastCall.args[0].url,
- '/projects/?n=26&S=0&query=%5Etest.*');
+ '/projects/?n=26&S=0&query=inname%3Afoo%20AND%20inname%3Abar');
+ });
+
+ test('with leading hyphen', () => {
+ element.getRepos('-bar', 25);
+ assert.equal(element._fetchSharedCacheURL.lastCall.args[0].url,
+ '/projects/?n=26&S=0&query=inname%3Abar');
+ });
+
+ test('with trailing hyphen', () => {
+ element.getRepos('foo-bar-', 25);
+ assert.equal(element._fetchSharedCacheURL.lastCall.args[0].url,
+ '/projects/?n=26&S=0&query=inname%3Afoo%20AND%20inname%3Abar');
+ });
+
+ test('with underscore', () => {
+ element.getRepos('foo_bar', 25);
+ assert.equal(element._fetchSharedCacheURL.lastCall.args[0].url,
+ '/projects/?n=26&S=0&query=inname%3Afoo%20AND%20inname%3Abar');
+ });
+
+ test('with underscore', () => {
+ element.getRepos('foo_bar', 25);
+ assert.equal(element._fetchSharedCacheURL.lastCall.args[0].url,
+ '/projects/?n=26&S=0&query=inname%3Afoo%20AND%20inname%3Abar');
+ });
+
+ test('hyphen only', () => {
+ element.getRepos('-', 25);
+ assert.equal(element._fetchSharedCacheURL.lastCall.args[0].url,
+ `/projects/?n=26&S=0&query=${defaultQuery}`);
});
});
diff --git a/tools/eclipse/project.py b/tools/eclipse/project.py
index b99c04e..64d837a 100755
--- a/tools/eclipse/project.py
+++ b/tools/eclipse/project.py
@@ -52,10 +52,12 @@
action='store', default='gerrit', dest='project_name')
opts.add_option('-b', '--batch', action='store_true',
dest='batch', help='Bazel batch option')
+opts.add_option('-j', '--java', action='store',
+ dest='java', help='Post Java 8 support (9|10|11|...)')
args, _ = opts.parse_args()
batch_option = '--batch' if args.batch else None
-
+custom_java = args.java
def _build_bazel_cmd(*args):
cmd = ['bazel']
@@ -63,6 +65,9 @@
cmd.append('--batch')
for arg in args:
cmd.append(arg)
+ if custom_java:
+ cmd.append('--host_java_toolchain=@bazel_tools//tools/jdk:toolchain_java%s' % custom_java)
+ cmd.append('--java_toolchain=@bazel_tools//tools/jdk:toolchain_java%s' % custom_java)
return cmd
@@ -70,9 +75,10 @@
return check_output(_build_bazel_cmd('info', 'output_base')).strip()
-def gen_bazel_path():
+def gen_bazel_path(ext_location):
bazel = check_output(['which', 'bazel']).strip().decode('UTF-8')
with open(path.join(ROOT, ".bazel_path"), 'w') as fd:
+ fd.write("output_base=%s\n" % ext_location)
fd.write("bazel=%s\n" % bazel)
fd.write("PATH=%s\n" % environ["PATH"])
@@ -301,7 +307,7 @@
gen_project(args.project_name)
gen_classpath(ext_location)
gen_factorypath(ext_location)
- gen_bazel_path()
+ gen_bazel_path(ext_location)
# TODO(davido): Remove this when GWT gone
gwt_working_dir = ".gwt_work_dir"