Merge "Migrate all Revert tests to RevertIT"
diff --git a/Documentation/dev-bazel.txt b/Documentation/dev-bazel.txt
index ecee622..e973924 100644
--- a/Documentation/dev-bazel.txt
+++ b/Documentation/dev-bazel.txt
@@ -78,14 +78,6 @@
 Now, invoking Bazel with just `bazel build :release` would include
 all those options.
 
-Note that the follow option must be added to `container.javaOptions`
-in `$gerrit_site/etc/gerrit.config` to run Gerrit with Java 12:
-
-```
-[container]
-  javaOptions = --add-opens=jdk.management/com.sun.management.internal=ALL-UNNAMED
-```
-
 [[java-11]]
 ==== Java 11 support
 
@@ -102,14 +94,6 @@
       :release
 ```
 
-Note that the follow option must be added to `container.javaOptions`
-in `$gerrit_site/etc/gerrit.config` to run Gerrit with Java 11:
-
-```
-[container]
-  javaOptions = --add-opens=jdk.management/com.sun.management.internal=ALL-UNNAMED
-```
-
 === Node.js and npm packages
 See link:https://gerrit.googlesource.com/gerrit/+/master/polygerrit-ui/README.md#installing-node_js-and-npm-packages[Installing Node.js and npm packages].
 
diff --git a/Documentation/dev-eclipse.txt b/Documentation/dev-eclipse.txt
index 67ced54..f113a16 100644
--- a/Documentation/dev-eclipse.txt
+++ b/Documentation/dev-eclipse.txt
@@ -55,11 +55,6 @@
 * 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/java/com/google/gerrit/common/data/PatchScript.java b/java/com/google/gerrit/common/data/PatchScript.java
index 3428580..28fda17 100644
--- a/java/com/google/gerrit/common/data/PatchScript.java
+++ b/java/com/google/gerrit/common/data/PatchScript.java
@@ -56,7 +56,6 @@
   private CommentDetail comments;
   private List<Patch> history;
   private boolean hugeFile;
-  private boolean intralineDifference;
   private boolean intralineFailure;
   private boolean intralineTimeout;
   private boolean binary;
@@ -83,7 +82,6 @@
       CommentDetail cd,
       List<Patch> hist,
       boolean hf,
-      boolean id,
       boolean idf,
       boolean idt,
       boolean bin,
@@ -108,7 +106,6 @@
     comments = cd;
     history = hist;
     hugeFile = hf;
-    intralineDifference = id;
     intralineFailure = idf;
     intralineTimeout = idt;
     binary = bin;
@@ -178,10 +175,6 @@
     return diffPrefs.ignoreWhitespace != Whitespace.IGNORE_NONE;
   }
 
-  public boolean hasIntralineDifference() {
-    return intralineDifference;
-  }
-
   public boolean hasIntralineFailure() {
     return intralineFailure;
   }
diff --git a/java/com/google/gerrit/metrics/proc/OperatingSystemMXBeanFactory.java b/java/com/google/gerrit/metrics/proc/OperatingSystemMXBeanFactory.java
new file mode 100644
index 0000000..9befe16a
--- /dev/null
+++ b/java/com/google/gerrit/metrics/proc/OperatingSystemMXBeanFactory.java
@@ -0,0 +1,49 @@
+// Copyright (C) 2017 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.metrics.proc;
+
+import com.google.common.flogger.FluentLogger;
+import com.sun.management.UnixOperatingSystemMXBean;
+import java.lang.management.ManagementFactory;
+import java.lang.management.OperatingSystemMXBean;
+import java.util.Arrays;
+
+@SuppressWarnings("restriction")
+class OperatingSystemMXBeanFactory {
+  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+  static OperatingSystemMXBeanInterface create() {
+    OperatingSystemMXBean sys = ManagementFactory.getOperatingSystemMXBean();
+    if (sys instanceof UnixOperatingSystemMXBean) {
+      return new OperatingSystemMXBeanUnixNative((UnixOperatingSystemMXBean) sys);
+    }
+
+    for (String name :
+        Arrays.asList(
+            "com.sun.management.UnixOperatingSystemMXBean",
+            "com.ibm.lang.management.UnixOperatingSystemMXBean")) {
+      try {
+        Class<?> impl = Class.forName(name);
+        if (impl.isInstance(sys)) {
+          return new OperatingSystemMXBeanReflectionBased(sys);
+        }
+      } catch (ReflectiveOperationException e) {
+        logger.atFine().withCause(e).log("No implementation for %s", name);
+      }
+    }
+    logger.atWarning().log("No implementation of UnixOperatingSystemMXBean found");
+    return null;
+  }
+}
diff --git a/java/com/google/gerrit/metrics/proc/OperatingSystemMXBeanInterface.java b/java/com/google/gerrit/metrics/proc/OperatingSystemMXBeanInterface.java
new file mode 100644
index 0000000..b7d6ebf
--- /dev/null
+++ b/java/com/google/gerrit/metrics/proc/OperatingSystemMXBeanInterface.java
@@ -0,0 +1,21 @@
+// Copyright (C) 2019 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.metrics.proc;
+
+interface OperatingSystemMXBeanInterface {
+  long getProcessCpuTime();
+
+  long getOpenFileDescriptorCount();
+}
diff --git a/java/com/google/gerrit/metrics/proc/OperatingSystemMXBeanProvider.java b/java/com/google/gerrit/metrics/proc/OperatingSystemMXBeanProvider.java
deleted file mode 100644
index 35c147e..0000000
--- a/java/com/google/gerrit/metrics/proc/OperatingSystemMXBeanProvider.java
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright (C) 2017 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.metrics.proc;
-
-import com.google.common.flogger.FluentLogger;
-import java.lang.management.ManagementFactory;
-import java.lang.management.OperatingSystemMXBean;
-import java.lang.reflect.Method;
-import java.util.Arrays;
-
-class OperatingSystemMXBeanProvider {
-  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
-  private final OperatingSystemMXBean sys;
-  private final Method getProcessCpuTime;
-  private final Method getOpenFileDescriptorCount;
-
-  static class Factory {
-    static OperatingSystemMXBeanProvider create() {
-      OperatingSystemMXBean sys = ManagementFactory.getOperatingSystemMXBean();
-      for (String name :
-          Arrays.asList(
-              "com.sun.management.UnixOperatingSystemMXBean",
-              "com.ibm.lang.management.UnixOperatingSystemMXBean")) {
-        try {
-          Class<?> impl = Class.forName(name);
-          if (impl.isInstance(sys)) {
-            return new OperatingSystemMXBeanProvider(sys);
-          }
-        } catch (ReflectiveOperationException e) {
-          logger.atFine().withCause(e).log("No implementation for %s", name);
-        }
-      }
-      logger.atWarning().log("No implementation of UnixOperatingSystemMXBean found");
-      return null;
-    }
-  }
-
-  private OperatingSystemMXBeanProvider(OperatingSystemMXBean sys)
-      throws ReflectiveOperationException {
-    this.sys = sys;
-    getProcessCpuTime = sys.getClass().getMethod("getProcessCpuTime");
-    getProcessCpuTime.setAccessible(true);
-    getOpenFileDescriptorCount = sys.getClass().getMethod("getOpenFileDescriptorCount");
-    getOpenFileDescriptorCount.setAccessible(true);
-  }
-
-  public long getProcessCpuTime() {
-    try {
-      return (long) getProcessCpuTime.invoke(sys, new Object[] {});
-    } catch (ReflectiveOperationException e) {
-      return -1;
-    }
-  }
-
-  public long getOpenFileDescriptorCount() {
-    try {
-      return (long) getOpenFileDescriptorCount.invoke(sys, new Object[] {});
-    } catch (ReflectiveOperationException e) {
-      return -1;
-    }
-  }
-}
diff --git a/java/com/google/gerrit/metrics/proc/OperatingSystemMXBeanReflectionBased.java b/java/com/google/gerrit/metrics/proc/OperatingSystemMXBeanReflectionBased.java
new file mode 100644
index 0000000..8dc54ab
--- /dev/null
+++ b/java/com/google/gerrit/metrics/proc/OperatingSystemMXBeanReflectionBased.java
@@ -0,0 +1,51 @@
+// Copyright (C) 2019 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.metrics.proc;
+
+import java.lang.management.OperatingSystemMXBean;
+import java.lang.reflect.Method;
+
+class OperatingSystemMXBeanReflectionBased implements OperatingSystemMXBeanInterface {
+  private final OperatingSystemMXBean sys;
+  private final Method getProcessCpuTime;
+  private final Method getOpenFileDescriptorCount;
+
+  OperatingSystemMXBeanReflectionBased(OperatingSystemMXBean sys)
+      throws ReflectiveOperationException {
+    this.sys = sys;
+    getProcessCpuTime = sys.getClass().getMethod("getProcessCpuTime");
+    getProcessCpuTime.setAccessible(true);
+    getOpenFileDescriptorCount = sys.getClass().getMethod("getOpenFileDescriptorCount");
+    getOpenFileDescriptorCount.setAccessible(true);
+  }
+
+  @Override
+  public long getProcessCpuTime() {
+    try {
+      return (long) getProcessCpuTime.invoke(sys, new Object[] {});
+    } catch (ReflectiveOperationException e) {
+      return -1;
+    }
+  }
+
+  @Override
+  public long getOpenFileDescriptorCount() {
+    try {
+      return (long) getOpenFileDescriptorCount.invoke(sys, new Object[] {});
+    } catch (ReflectiveOperationException e) {
+      return -1;
+    }
+  }
+}
diff --git a/java/com/google/gerrit/metrics/proc/OperatingSystemMXBeanUnixNative.java b/java/com/google/gerrit/metrics/proc/OperatingSystemMXBeanUnixNative.java
new file mode 100644
index 0000000..a7f5bba
--- /dev/null
+++ b/java/com/google/gerrit/metrics/proc/OperatingSystemMXBeanUnixNative.java
@@ -0,0 +1,36 @@
+// Copyright (C) 2019 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.metrics.proc;
+
+import com.sun.management.UnixOperatingSystemMXBean;
+
+@SuppressWarnings("restriction")
+class OperatingSystemMXBeanUnixNative implements OperatingSystemMXBeanInterface {
+  private final UnixOperatingSystemMXBean sys;
+
+  OperatingSystemMXBeanUnixNative(UnixOperatingSystemMXBean sys) {
+    this.sys = sys;
+  }
+
+  @Override
+  public long getProcessCpuTime() {
+    return sys.getProcessCpuTime();
+  }
+
+  @Override
+  public long getOpenFileDescriptorCount() {
+    return sys.getOpenFileDescriptorCount();
+  }
+}
diff --git a/java/com/google/gerrit/metrics/proc/ProcMetricModule.java b/java/com/google/gerrit/metrics/proc/ProcMetricModule.java
index d9781b5..b97cc54 100644
--- a/java/com/google/gerrit/metrics/proc/ProcMetricModule.java
+++ b/java/com/google/gerrit/metrics/proc/ProcMetricModule.java
@@ -63,7 +63,7 @@
   }
 
   private void procCpuUsage(MetricMaker metrics) {
-    final OperatingSystemMXBeanProvider provider = OperatingSystemMXBeanProvider.Factory.create();
+    OperatingSystemMXBeanInterface provider = OperatingSystemMXBeanFactory.create();
 
     if (provider == null) {
       return;
diff --git a/java/com/google/gerrit/reviewdb/client/Comment.java b/java/com/google/gerrit/reviewdb/client/Comment.java
index 94e7583..eb2aa52 100644
--- a/java/com/google/gerrit/reviewdb/client/Comment.java
+++ b/java/com/google/gerrit/reviewdb/client/Comment.java
@@ -124,6 +124,24 @@
     }
   }
 
+  /**
+   * The Range class defines continuous range of character.
+   *
+   * <p>The pair (startLine, startChar) defines the first character in the range. The pair (endLine,
+   * endChar) defines the first character AFTER the range (i.e. it doesn't belong the range).
+   * (endLine, endChar) must be a valid character inside text, except EOF case.</p>
+   * <p>Special cases:</p>
+   * <ul>
+   *   <li>Zero length range: (startLine, startChar) = (endLine, endChar). Range defines insert
+   *       position right before the (startLine, startChar) character (for {@link FixReplacement)
+   *   <li>EOF case - range includes the last character in the file:
+   *       <ul>
+   *         <li>if a file ends with EOL mark, then (endLine, endChar) = (num_of_lines + 1, 0)
+   *         <li>if a file doesn't end with EOL mark, then
+   *             (endLine, endChar) = (num_of_lines, num_of_chars_in_last_line)
+   *       </ul>
+   * </ul>
+   */
   public static class Range implements Comparable<Range> {
     private static final Comparator<Range> RANGE_COMPARATOR =
         Comparator.<Range>comparingInt(range -> range.startLine)
@@ -131,10 +149,10 @@
             .thenComparingInt(range -> range.endLine)
             .thenComparingInt(range -> range.endChar);
 
-    public int startLine; // 1-based, inclusive
-    public int startChar; // 0-based, inclusive
-    public int endLine; // 1-based, exclusive
-    public int endChar; // 0-based, exclusive
+    public int startLine; // 1-based
+    public int startChar; // 0-based
+    public int endLine; // 1-based
+    public int endChar; // 0-based
 
     public Range(Range r) {
       this(r.startLine, r.startChar, r.endLine, r.endChar);
diff --git a/java/com/google/gerrit/server/account/VersionedAccountQueries.java b/java/com/google/gerrit/server/account/VersionedAccountQueries.java
index daf7100..7b5e5ce 100644
--- a/java/com/google/gerrit/server/account/VersionedAccountQueries.java
+++ b/java/com/google/gerrit/server/account/VersionedAccountQueries.java
@@ -14,11 +14,17 @@
 
 package com.google.gerrit.server.account;
 
+import static java.util.stream.Collectors.joining;
+
+import com.google.common.base.Strings;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.server.git.ValidationError;
 import com.google.gerrit.server.git.meta.VersionedMetaData;
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
 import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.lib.CommitBuilder;
 
@@ -46,6 +52,16 @@
     return queryList;
   }
 
+  public void setQueryList(String text) throws IOException, ConfigInvalidException {
+    List<ValidationError> errors = new ArrayList<>();
+    QueryList newQueryList = QueryList.parse(text, error -> errors.add(error));
+    if (!errors.isEmpty()) {
+      String messages = errors.stream().map(ValidationError::getMessage).collect(joining(", "));
+      throw new ConfigInvalidException("Invalid named queries: " + messages);
+    }
+    queryList = newQueryList;
+  }
+
   @Override
   protected void onLoad() throws IOException, ConfigInvalidException {
     queryList =
@@ -58,6 +74,10 @@
 
   @Override
   protected boolean onSave(CommitBuilder commit) throws IOException, ConfigInvalidException {
-    throw new UnsupportedOperationException("Cannot yet save named queries");
+    if (Strings.isNullOrEmpty(commit.getMessage())) {
+      commit.setMessage("Updated named queries\n");
+    }
+    saveUTF8(QueryList.FILE_NAME, queryList.asText());
+    return true;
   }
 }
diff --git a/java/com/google/gerrit/server/patch/PatchScriptBuilder.java b/java/com/google/gerrit/server/patch/PatchScriptBuilder.java
index acf88e1..b9c13b1 100644
--- a/java/com/google/gerrit/server/patch/PatchScriptBuilder.java
+++ b/java/com/google/gerrit/server/patch/PatchScriptBuilder.java
@@ -121,7 +121,6 @@
 
   private PatchScript build(PatchListEntry content, CommentDetail comments, List<Patch> history)
       throws IOException {
-    boolean intralineDifferenceIsPossible = true;
     boolean intralineFailure = false;
     boolean intralineTimeout = false;
 
@@ -134,9 +133,7 @@
     edits = new ArrayList<>(content.getEdits());
     ImmutableSet<Edit> editsDueToRebase = content.getEditsDueToRebase();
 
-    if (!isModify(content)) {
-      intralineDifferenceIsPossible = false;
-    } else if (diffPrefs.intralineDifference) {
+    if (isModify(content) && diffPrefs.intralineDifference) {
       IntraLineDiff d =
           patchListCache.getIntraLineDiff(
               IntraLineDiffKey.create(a.id, b.id, diffPrefs.ignoreWhitespace),
@@ -149,21 +146,17 @@
             break;
 
           case DISABLED:
-            intralineDifferenceIsPossible = false;
             break;
 
           case ERROR:
-            intralineDifferenceIsPossible = false;
             intralineFailure = true;
             break;
 
           case TIMEOUT:
-            intralineDifferenceIsPossible = false;
             intralineTimeout = true;
             break;
         }
       } else {
-        intralineDifferenceIsPossible = false;
         intralineFailure = true;
       }
     }
@@ -223,7 +216,6 @@
         comments,
         history,
         hugeFile,
-        intralineDifferenceIsPossible,
         intralineFailure,
         intralineTimeout,
         content.getPatchType() == Patch.PatchType.BINARY,
diff --git a/javatests/com/google/gerrit/server/fixes/FixReplacementInterpreterTest.java b/javatests/com/google/gerrit/server/fixes/FixReplacementInterpreterTest.java
index 2174927..fb49657 100644
--- a/javatests/com/google/gerrit/server/fixes/FixReplacementInterpreterTest.java
+++ b/javatests/com/google/gerrit/server/fixes/FixReplacementInterpreterTest.java
@@ -148,6 +148,22 @@
         .isEqualTo("First line\nA modification\nThird line\n");
   }
 
+  @Test()
+  public void startAfterEndOfLineMarkThrowsAnException() throws Exception {
+    FixReplacement fixReplacement =
+        new FixReplacement(filePath1, new Range(1, 11, 2, 6), "A modification");
+    mockFileContent(filePath1, "First line\nSecond line\nThird line\n");
+    assertThrows(ResourceConflictException.class, () -> toTreeModifications(fixReplacement));
+  }
+
+  @Test()
+  public void endAfterEndOfLineMarkThrowsAnException() throws Exception {
+    FixReplacement fixReplacement =
+        new FixReplacement(filePath1, new Range(2, 0, 2, 12), "A modification");
+    mockFileContent(filePath1, "First line\nSecond line\nThird line\n");
+    assertThrows(ResourceConflictException.class, () -> toTreeModifications(fixReplacement));
+  }
+
   @Test
   public void replacementsMayTouch() throws Exception {
     FixReplacement fixReplacement1 =
@@ -180,6 +196,34 @@
   }
 
   @Test
+  public void replacementsCanChangeLastLine() throws Exception {
+    FixReplacement fixReplacement =
+        new FixReplacement(filePath1, new Range(3, 0, 4, 0), "New content\n");
+    mockFileContent(filePath1, "First line\nSecond line\nThird line\n");
+
+    List<TreeModification> treeModifications = toTreeModifications(fixReplacement);
+    assertThatList(treeModifications)
+        .onlyElement()
+        .asChangeFileContentModification()
+        .newContent()
+        .isEqualTo("First line\nSecond line\nNew content\n");
+  }
+
+  @Test
+  public void replacementsCanChangeLastLineWithoutEOLMark() throws Exception {
+    FixReplacement fixReplacement =
+        new FixReplacement(filePath1, new Range(3, 0, 3, 10), "New content\n");
+    mockFileContent(filePath1, "First line\nSecond line\nThird line");
+
+    List<TreeModification> treeModifications = toTreeModifications(fixReplacement);
+    assertThatList(treeModifications)
+        .onlyElement()
+        .asChangeFileContentModification()
+        .newContent()
+        .isEqualTo("First line\nSecond line\nNew content\n");
+  }
+
+  @Test
   public void replacementsCanModifySeveralFilesInAnyOrder() throws Exception {
     FixReplacement fixReplacement1 =
         new FixReplacement(filePath1, new Range(1, 1, 3, 2), "Modified content");
diff --git a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
index 350c6d2..fb5d93d 100644
--- a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
+++ b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
@@ -91,6 +91,7 @@
 import com.google.gerrit.server.account.Accounts;
 import com.google.gerrit.server.account.AccountsUpdate;
 import com.google.gerrit.server.account.AuthRequest;
+import com.google.gerrit.server.account.VersionedAccountQueries;
 import com.google.gerrit.server.account.externalids.ExternalId;
 import com.google.gerrit.server.change.ChangeInserter;
 import com.google.gerrit.server.change.ChangeTriplet;
@@ -2953,19 +2954,19 @@
     Change change1 = insert(repo, newChange(repo));
     Change change2 = insert(repo, newChangeForBranch(repo, "stable"));
 
-    String queries =
+    String queryListText =
         "query1\tproject:repo\n"
             + "query2\tproject:repo status:open\n"
             + "query3\tproject:repo branch:stable\n"
             + "query4\tproject:repo branch:other";
 
     try (TestRepository<Repo> allUsers =
-        new TestRepository<>(repoManager.openRepository(allUsersName))) {
-      String refsUsers = RefNames.refsUsers(userId);
-      allUsers.branch(refsUsers).commit().add("queries", queries).create();
-
-      Ref userRef = allUsers.getRepository().exactRef(refsUsers);
-      assertThat(userRef).isNotNull();
+            new TestRepository<>(repoManager.openRepository(allUsersName));
+        MetaDataUpdate md = metaDataUpdateFactory.create(allUsersName)) {
+      VersionedAccountQueries queries = VersionedAccountQueries.forUser(userId);
+      queries.load(md);
+      queries.setQueryList(queryListText);
+      queries.commit(md);
     }
 
     assertThatQueryException("query:foo").hasMessageThat().isEqualTo("Unknown named query: foo");
diff --git a/plugins/delete-project b/plugins/delete-project
index 4223c71..d520c74 160000
--- a/plugins/delete-project
+++ b/plugins/delete-project
@@ -1 +1 @@
-Subproject commit 4223c71d319b04c6b42566d2d12adca20734526d
+Subproject commit d520c74076714ed6f607693b6a737615dfd829b2
diff --git a/plugins/gitiles b/plugins/gitiles
index bdbed9a..9f7b84e 160000
--- a/plugins/gitiles
+++ b/plugins/gitiles
@@ -1 +1 @@
-Subproject commit bdbed9af9bb2b77cd7fc8681da2dcee7e8f30264
+Subproject commit 9f7b84e3ad1192b10a549d7e2ea2b920f84492de
diff --git a/plugins/webhooks b/plugins/webhooks
index e267456..b8fe69d 160000
--- a/plugins/webhooks
+++ b/plugins/webhooks
@@ -1 +1 @@
-Subproject commit e26745604c1f4301cf5d130d4bcc9da8463e0b1a
+Subproject commit b8fe69d328f77af32da6b05af28c8ed10beb8bd9
diff --git a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js
index b79e5f3..44a1140 100644
--- a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js
+++ b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js
@@ -210,7 +210,7 @@
               this._showNewUserHelp = lastResultSet.length == 0;
             }
             this._results = changes.map((results, i) => ({
-              name: res.sections[i].name,
+              name: this._computeSectionName(res.sections[i].name, results),
               query: res.sections[i].query,
               results,
               isOutgoing: res.sections[i].isOutgoing,
@@ -220,6 +220,17 @@
           });
     },
 
+    _computeSectionName(name, changes) {
+      if (!changes || !changes.length || changes.length == 0) {
+        return name;
+      }
+      const more = changes[changes.length - 1]._more_changes;
+      const numChanges = changes.length;
+      const andMore = more ? ' and more' : '';
+      const changeLabel = `change${numChanges > 1 ? 's' : ''}`;
+      return `${name} (${numChanges} ${changeLabel}${andMore})`;
+    },
+
     _computeUserHeaderClass(params) {
       if (!params || !!params.project || !params.user
           || params.user === 'self') {
diff --git a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.html b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.html
index 9f3e675..7918366 100644
--- a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.html
+++ b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.html
@@ -149,6 +149,31 @@
       assert.equal(element._computeTitle('not self'), 'Dashboard for not self');
     });
 
+    suite('_computeSectionName', () => {
+      test('empty changes dont change name', () => {
+        const name = 'Work in progress';
+        assert.equal(name, element._computeSectionName(name, []));
+      });
+
+      test('1 change', () => {
+        const name = 'Work in progress';
+        assert.equal(name + ' (1 change)',
+            element._computeSectionName(name, ['1']));
+      });
+
+      test('2 changes', () => {
+        const name = 'Work in progress';
+        assert.equal(name + ' (2 changes)',
+            element._computeSectionName(name, ['1', '2']));
+      });
+
+      test('1 change and more', () => {
+        const name = 'Work in progress';
+        assert.equal(name + ' (1 change and more)',
+            element._computeSectionName(name, [{_more_changes: true}]));
+      });
+    });
+
     suite('_isViewActive', () => {
       test('nothing happens when user param is falsy', () => {
         element.params = {};
@@ -280,7 +305,7 @@
 
       return element._fetchDashboardChanges({sections}, false).then(() => {
         assert.equal(element._results.length, 1);
-        assert.equal(element._results[0].name, 'test2');
+        assert.equal(element._results[0].name, 'test2 (1 change)');
       });
     });
 
diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata-it_test.html b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata-it_test.html
index c5dba2f..86f8aaf 100644
--- a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata-it_test.html
+++ b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata-it_test.html
@@ -106,7 +106,7 @@
 
     suite('with plugin style', () => {
       setup(done => {
-        Gerrit._resetPlugins();
+        Gerrit._testOnly_resetPlugins();
         const pluginHost = fixture('plugin-host');
         pluginHost.config = {
           plugin: {
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 2267d27..f9c7798 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
@@ -345,8 +345,10 @@
         ],
       };
       setup(() => {
+        // Fake computeDraftCount as its required for ChangeComments,
+        // see gr-comment-api#reloadDrafts.
         reloadStub = sandbox.stub(element.$.commentAPI, 'reloadDrafts')
-            .returns(Promise.resolve({drafts}));
+            .returns(Promise.resolve({drafts, computeDraftCount: () => 1}));
       });
 
       test('drafts are reloaded when reload-drafts fired', done => {
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog-it_test.html b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog-it_test.html
index 5e5a89e..45f718f 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog-it_test.html
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog-it_test.html
@@ -131,7 +131,7 @@
     });
 
     test('lgtm plugin', done => {
-      Gerrit._resetPlugins();
+      Gerrit._testOnly_resetPlugins();
       const pluginHost = fixture('plugin-host');
       pluginHost.config = {
         plugin: {
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 c523b5130..9eaf603 100644
--- a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html
+++ b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html
@@ -115,6 +115,7 @@
         query: 'assignee:${user} (-is:wip OR owner:self OR assignee:self) ' +
             'is:open -is:ignored',
         hideIfEmpty: true,
+        suffixForDashboard: 'limit:25',
       },
       {
         // WIP open changes owned by viewing user. This section is omitted when
@@ -123,6 +124,7 @@
         query: 'is:open owner:${user} is:wip',
         selfOnly: true,
         hideIfEmpty: true,
+        suffixForDashboard: 'limit:25',
       },
       {
         // Non-WIP open changes owned by viewed user. Filter out changes ignored
@@ -130,6 +132,7 @@
         name: 'Outgoing reviews',
         query: 'is:open owner:${user} -is:wip -is:ignored',
         isOutgoing: true,
+        suffixForDashboard: 'limit:25',
       },
       {
         // Non-WIP open changes not owned by the viewed user, that the viewed user
@@ -138,12 +141,14 @@
         name: 'Incoming reviews',
         query: 'is:open -owner:${user} -is:wip -is:ignored ' +
             '(reviewer:${user} OR assignee:${user})',
+        suffixForDashboard: 'limit:25',
       },
       {
         // Open changes the viewed user is CCed on. Changes ignored by the viewing
         // user are filtered out.
         name: 'CCed on',
         query: 'is:open -is:ignored cc:${user}',
+        suffixForDashboard: 'limit:10',
       },
       {
         name: 'Recently closed',
diff --git a/polygerrit-ui/app/elements/gr-app-p2.html b/polygerrit-ui/app/elements/gr-app-p2.html
index dbac7fe..10011de 100644
--- a/polygerrit-ui/app/elements/gr-app-p2.html
+++ b/polygerrit-ui/app/elements/gr-app-p2.html
@@ -16,6 +16,9 @@
 -->
 <script>
   window.Gerrit = window.Gerrit || {};
+
+  // Disable extra font load from paper-styles
+  window.polymerSkipLoadingFontRoboto = true;
 </script>
 
 <link rel="import" href="/bower_components/polymer/polymer.html">
diff --git a/polygerrit-ui/app/elements/gr-app-p2.js b/polygerrit-ui/app/elements/gr-app-p2.js
index 5b6e316..2163c02 100644
--- a/polygerrit-ui/app/elements/gr-app-p2.js
+++ b/polygerrit-ui/app/elements/gr-app-p2.js
@@ -17,9 +17,6 @@
 (function() {
   'use strict';
 
-  // Disable extra font load from paper-styles
-  window.polymerSkipLoadingFontRoboto = true;
-
   Polymer({
     is: 'gr-app-p2',
   });
diff --git a/polygerrit-ui/app/elements/gr-app.html b/polygerrit-ui/app/elements/gr-app.html
index cb1d0a2..27444b3 100644
--- a/polygerrit-ui/app/elements/gr-app.html
+++ b/polygerrit-ui/app/elements/gr-app.html
@@ -29,6 +29,9 @@
     };
   }
   window.Gerrit = window.Gerrit || {};
+
+  // Disable extra font load from paper-styles
+  window.polymerSkipLoadingFontRoboto = true;
 </script>
 
 <link rel="import" href="/bower_components/polymer/polymer.html">
diff --git a/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator_test.html b/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator_test.html
index 0883707..994d666 100644
--- a/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator_test.html
+++ b/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator_test.html
@@ -58,7 +58,7 @@
       stub('gr-endpoint-decorator', {
         _import: sandbox.stub().returns(Promise.resolve()),
       });
-      Gerrit._resetPlugins();
+      Gerrit._testOnly_resetPlugins();
       container = fixture('basic');
       Gerrit.install(p => plugin = p, '0.1', 'http://some/plugin/url.html');
       // Decoration
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-api-utils.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-api-utils.js
index cf2376e..7e7e927 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-api-utils.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-api-utils.js
@@ -21,14 +21,17 @@
   const PRELOADED_PROTOCOL = 'preloaded:';
 
   let _restAPI;
-
-  const getRestAPI = () => {
+  function getRestAPI() {
     if (!_restAPI) {
       _restAPI = document.createElement('gr-rest-api-interface');
     }
     return _restAPI;
-  };
+  }
 
+  /**
+   * Retrieves the name of the plugin base on the url.
+   * @param {string|URL} url
+   */
   function getPluginNameFromUrl(url) {
     if (!(url instanceof URL)) {
       try {
@@ -51,14 +54,16 @@
           url.href, '— Unable to determine name.');
       return null;
     }
+
     // Pathname should normally look like this:
     // /plugins/PLUGINNAME/static/SCRIPTNAME.html
     // Or, for app/samples:
     // /plugins/PLUGINNAME.html
+    // TODO(taoalpha): guard with a regex
     return pathname.split('/')[2].split('.')[0];
   }
 
-  // TODO (viktard): deprecate in favor of GrPluginRestApi.
+  // TODO (taoalpha): to be deprecated.
   function send(method, url, opt_callback, opt_payload) {
     return getRestAPI().send(method, url, opt_payload).then(response => {
       if (response.status < 200 || response.status >= 300) {
@@ -80,8 +85,11 @@
     });
   }
 
-  function resetInternalState() {
-    _restAPI = null;
+
+  // TEST only methods / properties
+
+  function testOnly_resetInternalState() {
+    _restAPI = undefined;
   }
 
   window._apiUtils = {
@@ -90,6 +98,6 @@
     getRestAPI,
 
     // TEST only methods
-    resetInternalState,
+    testOnly_resetInternalState,
   };
 })(window);
\ No newline at end of file
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-actions-js-api_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-actions-js-api_test.html
index 30bd366..0131912 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-actions-js-api_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-actions-js-api_test.html
@@ -54,7 +54,7 @@
 
     suite('early init', () => {
       setup(() => {
-        Gerrit._resetPlugins();
+        Gerrit._testOnly_resetPlugins();
         Gerrit.install(p => { plugin = p; }, '0.1',
             'http://test.com/plugins/testplugin/static/test.js');
         // Mimic all plugins loaded.
@@ -76,7 +76,7 @@
 
     suite('normal init', () => {
       setup(() => {
-        Gerrit._resetPlugins();
+        Gerrit._testOnly_resetPlugins();
         element = fixture('basic');
         sinon.stub(element, '_editStatusChanged');
         element.change = {};
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-gerrit.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-gerrit.js
index 8cdbcb7..a567700 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-gerrit.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-gerrit.js
@@ -15,6 +15,11 @@
  * limitations under the License.
  */
 
+ /**
+  * This defines the Gerrit instance. All methods directly attached to Gerrit
+  * should be defined or linked here.
+  */
+
 (function(window) {
   'use strict';
 
@@ -50,11 +55,21 @@
       getPluginNameFromUrl,
       send,
       getRestAPI,
-      resetInternalState,
   } = window._apiUtils;
 
   const API_VERSION = '0.1';
 
+  /**
+   * Trigger the preinstalls for bundled plugins.
+   * This needs to happen before Gerrit as plugin bundle overrides the Gerrit.
+   */
+  function flushPreinstalls() {
+    if (window.Gerrit.flushPreinstalls) {
+      window.Gerrit.flushPreinstalls();
+    }
+  }
+  flushPreinstalls();
+
   window.Gerrit = window.Gerrit || {};
   const Gerrit = window.Gerrit;
 
@@ -67,16 +82,19 @@
   const app = document.querySelector('#app');
   if (!app) {
     // No gr-app found (running tests)
-    Gerrit._installPreloadedPlugins = installPreloadedPlugins;
-    Gerrit._flushPreinstalls = flushPreinstalls;
-    Gerrit._resetPlugins = () => {
+    const {
+      testOnly_resetInternalState,
+    } = window._apiUtils;
+    Gerrit._testOnly_installPreloadedPlugins = installPreloadedPlugins;
+    Gerrit._testOnly_flushPreinstalls = flushPreinstalls;
+    Gerrit._testOnly_resetPlugins = () => {
       _allPluginsPromise = null;
       _pluginsInstalled = [];
       _pluginsPending = {};
       _pluginsPendingCount = -1;
       _reporting = null;
       _resolveAllPluginsLoaded = null;
-      resetInternalState();
+      testOnly_resetInternalState();
       Gerrit._endpoints = new GrPluginEndpoints();
       for (const k of Object.keys(_plugins)) {
         delete _plugins[k];
@@ -257,12 +275,6 @@
     }
   };
 
-  function flushPreinstalls() {
-    if (window.Gerrit.flushPreinstalls) {
-      window.Gerrit.flushPreinstalls();
-    }
-  }
-
   function installPreloadedPlugins() {
     if (!Gerrit._preloadedPlugins) { return; }
     for (const name in Gerrit._preloadedPlugins) {
@@ -272,8 +284,6 @@
     }
   }
 
-  flushPreinstalls();
-
   // Preloaded plugins should be installed after Gerrit.install() is set,
   // since plugin preloader substitutes Gerrit.install() temporarily.
   installPreloadedPlugins();
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-gerrit_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-gerrit_test.html
index b3ec30f..9a05454 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-gerrit_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-gerrit_test.html
@@ -73,10 +73,10 @@
 
     test('flushes preinstalls if provided', () => {
       assert.doesNotThrow(() => {
-        Gerrit._flushPreinstalls();
+        Gerrit._testOnly_flushPreinstalls();
       });
       window.Gerrit.flushPreinstalls = sandbox.stub();
-      Gerrit._flushPreinstalls();
+      Gerrit._testOnly_flushPreinstalls();
       assert.isTrue(window.Gerrit.flushPreinstalls.calledOnce);
       delete window.Gerrit.flushPreinstalls;
     });
@@ -169,7 +169,7 @@
     test('preloaded plugins are installed', () => {
       const installStub = sandbox.stub();
       Gerrit._preloadedPlugins = {foo: installStub};
-      Gerrit._installPreloadedPlugins();
+      Gerrit._testOnly_installPreloadedPlugins();
       assert.isTrue(installStub.called);
       const pluginApi = installStub.lastCall.args[0];
       assert.strictEqual(pluginApi.getPluginName(), 'foo');
diff --git a/polygerrit-ui/app/test/common-test-setup.html b/polygerrit-ui/app/test/common-test-setup.html
index 696f6a5..c1d8bbd 100644
--- a/polygerrit-ui/app/test/common-test-setup.html
+++ b/polygerrit-ui/app/test/common-test-setup.html
@@ -53,8 +53,8 @@
   (function() {
     setup(() => {
       if (!window.Gerrit) { return; }
-      if (Gerrit._resetPlugins) {
-        Gerrit._resetPlugins();
+      if (Gerrit._testOnly_resetPlugins) {
+        Gerrit._testOnly_resetPlugins();
       }
     });
   })();
diff --git a/tools/bzl/junit.bzl b/tools/bzl/junit.bzl
index 987c5ca..66d7230 100644
--- a/tools/bzl/junit.bzl
+++ b/tools/bzl/junit.bzl
@@ -70,7 +70,6 @@
     # Enforce JDK 8 compatibility on Java 9, see
     # https://docs.oracle.com/javase/9/intl/internationalization-enhancements-jdk-9.htm#JSINT-GUID-AF5AECA7-07C1-4E7D-BC10-BC7E73DC6C7F
     "-Djava.locale.providers=COMPAT,CLDR,SPI",
-    "--add-opens=jdk.management/com.sun.management.internal=ALL-UNNAMED",
 ]
 
 def junit_tests(name, srcs, **kwargs):