Merge "Show "Loading..." while the autocomplete query is loading."
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index 75d8847..2810d1e 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -6709,6 +6709,7 @@
|`patch` |required|
The patch to be applied. Must be compatible with `git diff` output.
For example, link:#get-patch[Get Patch] output.
+The patch must be provided as UTF-8 text, either directly or base64-encoded.
|=================================
[[applypatchpatchset-input]]
diff --git a/java/com/google/gerrit/acceptance/GerritServer.java b/java/com/google/gerrit/acceptance/GerritServer.java
index a149f29..36bc3c4 100644
--- a/java/com/google/gerrit/acceptance/GerritServer.java
+++ b/java/com/google/gerrit/acceptance/GerritServer.java
@@ -67,6 +67,7 @@
import com.google.gerrit.server.util.ReplicaUtil;
import com.google.gerrit.server.util.SocketUtil;
import com.google.gerrit.server.util.SystemLog;
+import com.google.gerrit.testing.FakeAccountPatchReviewStore.FakeAccountPatchReviewStoreModule;
import com.google.gerrit.testing.FakeEmailSender.FakeEmailSenderModule;
import com.google.gerrit.testing.InMemoryRepositoryManager;
import com.google.gerrit.testing.SshMode;
@@ -414,6 +415,7 @@
}
},
site);
+ daemon.setAccountPatchReviewStoreModuleForTesting(new FakeAccountPatchReviewStoreModule());
daemon.setEmailModuleForTesting(new FakeEmailSenderModule());
daemon.setAuditEventModuleForTesting(
MoreObjects.firstNonNull(testAuditModule, new FakeGroupAuditServiceModule()));
diff --git a/java/com/google/gerrit/acceptance/TestMetricMaker.java b/java/com/google/gerrit/acceptance/TestMetricMaker.java
index 85c5b6d..647eb9d 100644
--- a/java/com/google/gerrit/acceptance/TestMetricMaker.java
+++ b/java/com/google/gerrit/acceptance/TestMetricMaker.java
@@ -14,20 +14,26 @@
package com.google.gerrit.acceptance;
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
import com.google.gerrit.metrics.Counter0;
+import com.google.gerrit.metrics.Counter1;
+import com.google.gerrit.metrics.Counter2;
+import com.google.gerrit.metrics.Counter3;
import com.google.gerrit.metrics.Description;
import com.google.gerrit.metrics.DisabledMetricMaker;
+import com.google.gerrit.metrics.Field;
import com.google.inject.Singleton;
+import java.util.Arrays;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.lang3.mutable.MutableLong;
/**
* {@link com.google.gerrit.metrics.MetricMaker} to be bound in tests.
*
- * <p>Records how often {@link Counter0} metrics are invoked. Metrics of other types are not
- * recorded.
+ * <p>Records how often counter metrics are invoked. Metrics of other types are not recorded.
*
- * <p>Allows test to check how much a {@link Counter0} metrics is increased by an operation.
+ * <p>Allows test to check how much a counter metrics is increased by an operation.
*
* <p>Example:
*
@@ -48,18 +54,18 @@
*/
@Singleton
public class TestMetricMaker extends DisabledMetricMaker {
- private final ConcurrentHashMap<String, MutableLong> counts = new ConcurrentHashMap<>();
+ private final ConcurrentHashMap<CounterKey, MutableLong> counts = new ConcurrentHashMap<>();
- public long getCount(String counter0Name) {
- return get(counter0Name).longValue();
+ public long getCount(String counterName, Object... fieldValues) {
+ return get(CounterKey.create(counterName, fieldValues)).longValue();
}
public void reset() {
counts.clear();
}
- private MutableLong get(String counter0Name) {
- return counts.computeIfAbsent(counter0Name, name -> new MutableLong(0));
+ private MutableLong get(CounterKey counterKey) {
+ return counts.computeIfAbsent(counterKey, key -> new MutableLong(0));
}
@Override
@@ -67,11 +73,64 @@
return new Counter0() {
@Override
public void incrementBy(long value) {
- get(name).add(value);
+ get(CounterKey.create(name)).add(value);
}
@Override
public void remove() {}
};
}
+
+ @Override
+ public <F1> Counter1<F1> newCounter(String name, Description desc, Field<F1> field1) {
+ return new Counter1<>() {
+ @Override
+ public void incrementBy(F1 field1, long value) {
+ get(CounterKey.create(name, field1)).add(value);
+ }
+
+ @Override
+ public void remove() {}
+ };
+ }
+
+ @Override
+ public <F1, F2> Counter2<F1, F2> newCounter(
+ String name, Description desc, Field<F1> field1, Field<F2> field2) {
+ return new Counter2<>() {
+ @Override
+ public void incrementBy(F1 field1, F2 field2, long value) {
+ get(CounterKey.create(name, field1, field2)).add(value);
+ }
+
+ @Override
+ public void remove() {}
+ };
+ }
+
+ @Override
+ public <F1, F2, F3> Counter3<F1, F2, F3> newCounter(
+ String name, Description desc, Field<F1> field1, Field<F2> field2, Field<F3> field3) {
+ return new Counter3<>() {
+ @Override
+ public void incrementBy(F1 field1, F2 field2, F3 field3, long value) {
+ get(CounterKey.create(name, field1, field2, field3)).add(value);
+ }
+
+ @Override
+ public void remove() {}
+ };
+ }
+
+ @AutoValue
+ abstract static class CounterKey {
+ abstract String name();
+
+ abstract ImmutableList<Object> fieldValues();
+
+ static CounterKey create(String name, Object... fieldValues) {
+ return new AutoValue_TestMetricMaker_CounterKey(
+ name, ImmutableList.copyOf(Arrays.asList(fieldValues)));
+ }
+ }
}
diff --git a/java/com/google/gerrit/pgm/Daemon.java b/java/com/google/gerrit/pgm/Daemon.java
index 845cc9a..744f91b 100644
--- a/java/com/google/gerrit/pgm/Daemon.java
+++ b/java/com/google/gerrit/pgm/Daemon.java
@@ -215,6 +215,7 @@
private Path runFile;
private boolean inMemoryTest;
private AbstractModule indexModule;
+ private Module accountPatchReviewStoreModule;
private Module emailModule;
private List<Module> testSysModules = new ArrayList<>();
private List<Module> testSshModules = new ArrayList<>();
@@ -333,6 +334,11 @@
}
@VisibleForTesting
+ public void setAccountPatchReviewStoreModuleForTesting(Module module) {
+ accountPatchReviewStoreModule = module;
+ }
+
+ @VisibleForTesting
public void setEmailModuleForTesting(Module module) {
emailModule = module;
}
@@ -442,7 +448,11 @@
modules.add(new WorkQueueModule());
modules.add(new StreamEventsApiListenerModule());
modules.add(new EventBrokerModule());
- modules.add(new JdbcAccountPatchReviewStoreModule(config));
+ if (accountPatchReviewStoreModule != null) {
+ modules.add(accountPatchReviewStoreModule);
+ } else {
+ modules.add(new JdbcAccountPatchReviewStoreModule(config));
+ }
modules.add(new SysExecutorModule());
modules.add(new DiffExecutorModule());
modules.add(new MimeUtil2Module());
diff --git a/java/com/google/gerrit/server/restapi/BUILD b/java/com/google/gerrit/server/restapi/BUILD
index 62da2f2..dd0ec78d 100644
--- a/java/com/google/gerrit/server/restapi/BUILD
+++ b/java/com/google/gerrit/server/restapi/BUILD
@@ -34,6 +34,7 @@
"//lib/auto:auto-factory",
"//lib/auto:auto-value",
"//lib/auto:auto-value-annotations",
+ "//lib/commons:codec",
"//lib/commons:compress",
"//lib/commons:lang3",
"//lib/errorprone:annotations",
diff --git a/java/com/google/gerrit/server/restapi/change/ApplyPatchUtil.java b/java/com/google/gerrit/server/restapi/change/ApplyPatchUtil.java
index d4f549a..4021f77 100644
--- a/java/com/google/gerrit/server/restapi/change/ApplyPatchUtil.java
+++ b/java/com/google/gerrit/server/restapi/change/ApplyPatchUtil.java
@@ -23,6 +23,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
+import org.apache.commons.codec.binary.Base64;
import org.eclipse.jgit.api.errors.PatchApplyException;
import org.eclipse.jgit.api.errors.PatchFormatException;
import org.eclipse.jgit.lib.ObjectId;
@@ -51,8 +52,12 @@
throws IOException, RestApiException {
checkNotNull(mergeTip);
RevTree tip = mergeTip.getTree();
- InputStream patchStream =
- new ByteArrayInputStream(input.patch.getBytes(StandardCharsets.UTF_8));
+ InputStream patchStream;
+ if (Base64.isBase64(input.patch)) {
+ patchStream = new ByteArrayInputStream(org.eclipse.jgit.util.Base64.decode(input.patch));
+ } else {
+ patchStream = new ByteArrayInputStream(input.patch.getBytes(StandardCharsets.UTF_8));
+ }
try {
PatchApplier applier = new PatchApplier(repo, tip, oi);
PatchApplier.Result applyResult = applier.applyPatch(patchStream);
diff --git a/java/com/google/gerrit/testing/BUILD b/java/com/google/gerrit/testing/BUILD
index fb9e64e..81a6443 100644
--- a/java/com/google/gerrit/testing/BUILD
+++ b/java/com/google/gerrit/testing/BUILD
@@ -51,6 +51,7 @@
"//lib:junit",
"//lib/auto:auto-value",
"//lib/auto:auto-value-annotations",
+ "//lib/errorprone:annotations",
"//lib/flogger:api",
"//lib/guice",
"//lib/guice:guice-servlet",
diff --git a/java/com/google/gerrit/testing/FakeAccountPatchReviewStore.java b/java/com/google/gerrit/testing/FakeAccountPatchReviewStore.java
new file mode 100644
index 0000000..1533aeb
--- /dev/null
+++ b/java/com/google/gerrit/testing/FakeAccountPatchReviewStore.java
@@ -0,0 +1,141 @@
+// Copyright (C) 2023 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.testing;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableSet;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.extensions.events.LifecycleListener;
+import com.google.gerrit.extensions.registration.DynamicItem;
+import com.google.gerrit.lifecycle.LifecycleModule;
+import com.google.gerrit.server.change.AccountPatchReviewStore;
+import com.google.inject.Singleton;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+
+/**
+ * An implementation of the {@link AccountPatchReviewStore} that's only used in tests. This
+ * implementation stores reviewed files in memory.
+ */
+@Singleton
+public class FakeAccountPatchReviewStore implements AccountPatchReviewStore, LifecycleListener {
+
+ private final Set<Entity> store = new HashSet<>();
+
+ @Override
+ public void start() {}
+
+ @Override
+ public void stop() {}
+
+ public static class FakeAccountPatchReviewStoreModule extends LifecycleModule {
+ @Override
+ protected void configure() {
+ DynamicItem.bind(binder(), AccountPatchReviewStore.class)
+ .to(FakeAccountPatchReviewStore.class);
+ listener().to(FakeAccountPatchReviewStore.class);
+ }
+ }
+
+ @AutoValue
+ abstract static class Entity {
+ abstract PatchSet.Id psId();
+
+ abstract Account.Id accountId();
+
+ abstract String path();
+
+ static Entity create(PatchSet.Id psId, Account.Id accountId, String path) {
+ return new AutoValue_FakeAccountPatchReviewStore_Entity(psId, accountId, path);
+ }
+ }
+
+ @Override
+ @CanIgnoreReturnValue
+ public boolean markReviewed(PatchSet.Id psId, Account.Id accountId, String path) {
+ synchronized (store) {
+ Entity entity = Entity.create(psId, accountId, path);
+ return store.add(entity);
+ }
+ }
+
+ @Override
+ public void markReviewed(PatchSet.Id psId, Account.Id accountId, Collection<String> paths) {
+ paths.forEach(path -> markReviewed(psId, accountId, path));
+ }
+
+ @Override
+ public void clearReviewed(PatchSet.Id psId, Account.Id accountId, String path) {
+ synchronized (store) {
+ store.remove(Entity.create(psId, accountId, path));
+ }
+ }
+
+ @Override
+ public void clearReviewed(PatchSet.Id psId) {
+ synchronized (store) {
+ List<Entity> toRemove = new ArrayList<>();
+ for (Entity entity : store) {
+ if (entity.psId().equals(psId)) {
+ toRemove.add(entity);
+ }
+ }
+ store.removeAll(toRemove);
+ }
+ }
+
+ @Override
+ public void clearReviewed(Change.Id changeId) {
+ synchronized (store) {
+ List<Entity> toRemove = new ArrayList<>();
+ for (Entity entity : store) {
+ if (entity.psId().changeId().equals(changeId)) {
+ toRemove.add(entity);
+ }
+ }
+ store.removeAll(toRemove);
+ }
+ }
+
+ @Override
+ public Optional<PatchSetWithReviewedFiles> findReviewed(PatchSet.Id psId, Account.Id accountId) {
+ synchronized (store) {
+ int matchedPsNumber = -1;
+ Optional<PatchSetWithReviewedFiles> result = Optional.empty();
+ for (Entity entity : store) {
+ if (entity.accountId() != accountId || !entity.psId().changeId().equals(psId.changeId())) {
+ continue;
+ }
+ int entityPsNumber = Integer.parseInt(entity.psId().getId());
+ if (entityPsNumber <= psId.get() && entityPsNumber > matchedPsNumber) {
+ matchedPsNumber = entityPsNumber;
+ result =
+ Optional.of(
+ PatchSetWithReviewedFiles.create(
+ PatchSet.id(psId.changeId(), matchedPsNumber),
+ ImmutableSet.of(entity.path())));
+ }
+ }
+ return result;
+ }
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/TestMetricMakerTest.java b/javatests/com/google/gerrit/acceptance/TestMetricMakerTest.java
new file mode 100644
index 0000000..3464d21
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/TestMetricMakerTest.java
@@ -0,0 +1,202 @@
+// Copyright (C) 2023 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;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.gerrit.metrics.Counter0;
+import com.google.gerrit.metrics.Counter1;
+import com.google.gerrit.metrics.Counter2;
+import com.google.gerrit.metrics.Counter3;
+import com.google.gerrit.metrics.Description;
+import com.google.gerrit.metrics.Field;
+import org.junit.Before;
+import org.junit.Test;
+
+/** Tests for {@link TestMetricMaker}. */
+public class TestMetricMakerTest {
+ private TestMetricMaker testMetricMaker = new TestMetricMaker();
+
+ @Before
+ public void setUp() {
+ testMetricMaker.reset();
+ }
+
+ @Test
+ public void counter0() throws Exception {
+ String counterName = "test_counter";
+ Counter0 counter = testMetricMaker.newCounter(counterName, new Description("Test Counter"));
+ assertThat(testMetricMaker.getCount(counterName)).isEqualTo(0);
+
+ counter.increment();
+ assertThat(testMetricMaker.getCount(counterName)).isEqualTo(1);
+
+ counter.incrementBy(/* value= */ 3);
+ assertThat(testMetricMaker.getCount(counterName)).isEqualTo(4);
+ }
+
+ @Test
+ public void counter1_booleanField() throws Exception {
+ String counterName = "test_counter";
+ Counter1<Boolean> counter =
+ testMetricMaker.newCounter(
+ counterName,
+ new Description("Test Counter"),
+ Field.ofBoolean("boolean_field", (metadataBuilder, booleanField) -> {}).build());
+ assertThat(testMetricMaker.getCount(counterName, true)).isEqualTo(0);
+ assertThat(testMetricMaker.getCount(counterName, false)).isEqualTo(0);
+
+ counter.increment(/* field1= */ true);
+ assertThat(testMetricMaker.getCount(counterName, true)).isEqualTo(1);
+ assertThat(testMetricMaker.getCount(counterName, false)).isEqualTo(0);
+
+ counter.incrementBy(/* field1= */ true, /* value= */ 3);
+ assertThat(testMetricMaker.getCount(counterName, true)).isEqualTo(4);
+ assertThat(testMetricMaker.getCount(counterName, false)).isEqualTo(0);
+
+ counter.increment(/* field1= */ false);
+ assertThat(testMetricMaker.getCount(counterName, true)).isEqualTo(4);
+ assertThat(testMetricMaker.getCount(counterName, false)).isEqualTo(1);
+
+ counter.incrementBy(/* field1= */ false, /* value= */ 4);
+ assertThat(testMetricMaker.getCount(counterName, true)).isEqualTo(4);
+ assertThat(testMetricMaker.getCount(counterName, false)).isEqualTo(5);
+
+ assertThat(testMetricMaker.getCount(counterName)).isEqualTo(0);
+ }
+
+ @Test
+ public void counter1_stringField() throws Exception {
+ String counterName = "test_counter";
+ Counter1<String> counter =
+ testMetricMaker.newCounter(
+ counterName,
+ new Description("Test Counter"),
+ Field.ofString("string_field", (metadataBuilder, stringField) -> {}).build());
+ assertThat(testMetricMaker.getCount(counterName, "foo")).isEqualTo(0);
+ assertThat(testMetricMaker.getCount(counterName, "bar")).isEqualTo(0);
+
+ counter.increment(/* field1= */ "foo");
+ assertThat(testMetricMaker.getCount(counterName, "foo")).isEqualTo(1);
+ assertThat(testMetricMaker.getCount(counterName, "bar")).isEqualTo(0);
+
+ counter.incrementBy(/* field1= */ "foo", /* value= */ 3);
+ assertThat(testMetricMaker.getCount(counterName, "foo")).isEqualTo(4);
+ assertThat(testMetricMaker.getCount(counterName, "bar")).isEqualTo(0);
+
+ counter.increment(/* field1= */ "bar");
+ assertThat(testMetricMaker.getCount(counterName, "foo")).isEqualTo(4);
+ assertThat(testMetricMaker.getCount(counterName, "bar")).isEqualTo(1);
+
+ counter.incrementBy(/* field1= */ "bar", /* value= */ 4);
+ assertThat(testMetricMaker.getCount(counterName, "foo")).isEqualTo(4);
+ assertThat(testMetricMaker.getCount(counterName, "bar")).isEqualTo(5);
+
+ assertThat(testMetricMaker.getCount(counterName)).isEqualTo(0);
+ }
+
+ @Test
+ public void counter2() throws Exception {
+ String counterName = "test_counter";
+ Counter2<Boolean, String> counter =
+ testMetricMaker.newCounter(
+ counterName,
+ new Description("Test Counter"),
+ Field.ofBoolean("boolean_field", (metadataBuilder, booleanField) -> {}).build(),
+ Field.ofString("string_field", (metadataBuilder, stringField) -> {}).build());
+ assertThat(testMetricMaker.getCount(counterName, true, "foo")).isEqualTo(0);
+ assertThat(testMetricMaker.getCount(counterName, false, "foo")).isEqualTo(0);
+
+ counter.increment(/* field1= */ true, /* field2= */ "foo");
+ assertThat(testMetricMaker.getCount(counterName, true, "foo")).isEqualTo(1);
+ assertThat(testMetricMaker.getCount(counterName, false, "foo")).isEqualTo(0);
+
+ counter.incrementBy(/* field1= */ true, /* field2= */ "foo", /* value= */ 3);
+ assertThat(testMetricMaker.getCount(counterName, true, "foo")).isEqualTo(4);
+ assertThat(testMetricMaker.getCount(counterName, false, "foo")).isEqualTo(0);
+
+ counter.increment(/* field1= */ false, /* field2= */ "foo");
+ assertThat(testMetricMaker.getCount(counterName, true, "foo")).isEqualTo(4);
+ assertThat(testMetricMaker.getCount(counterName, false, "foo")).isEqualTo(1);
+
+ counter.incrementBy(/* field1= */ false, /* field2= */ "foo", /* value= */ 4);
+ assertThat(testMetricMaker.getCount(counterName, true, "foo")).isEqualTo(4);
+ assertThat(testMetricMaker.getCount(counterName, false, "foo")).isEqualTo(5);
+
+ counter.increment(/* field1= */ true, /* field2= */ "bar");
+ assertThat(testMetricMaker.getCount(counterName, true, "foo")).isEqualTo(4);
+ assertThat(testMetricMaker.getCount(counterName, true, "bar")).isEqualTo(1);
+
+ counter.incrementBy(/* field1= */ true, /* field2= */ "bar", /* value= */ 5);
+ assertThat(testMetricMaker.getCount(counterName, true, "foo")).isEqualTo(4);
+ assertThat(testMetricMaker.getCount(counterName, true, "bar")).isEqualTo(6);
+
+ assertThat(testMetricMaker.getCount(counterName)).isEqualTo(0);
+ assertThat(testMetricMaker.getCount(counterName, true)).isEqualTo(0);
+ assertThat(testMetricMaker.getCount(counterName, false)).isEqualTo(0);
+ }
+
+ @Test
+ public void counter3() throws Exception {
+ String counterName = "test_counter";
+ Counter3<Boolean, String, Integer> counter =
+ testMetricMaker.newCounter(
+ counterName,
+ new Description("Test Counter"),
+ Field.ofBoolean("boolean_field", (metadataBuilder, booleanField) -> {}).build(),
+ Field.ofString("string_field", (metadataBuilder, stringField) -> {}).build(),
+ Field.ofInteger("integer_field", (metadataBuilder, stringField) -> {}).build());
+ assertThat(testMetricMaker.getCount(counterName, true, "foo", 0)).isEqualTo(0);
+ assertThat(testMetricMaker.getCount(counterName, false, "foo", 0)).isEqualTo(0);
+
+ counter.increment(/* field1= */ true, /* field2= */ "foo", /* field3= */ 0);
+ assertThat(testMetricMaker.getCount(counterName, true, "foo", 0)).isEqualTo(1);
+ assertThat(testMetricMaker.getCount(counterName, false, "foo", 0)).isEqualTo(0);
+
+ counter.incrementBy(/* field1= */ true, /* field2= */ "foo", /* field3= */ 0, /* value= */ 3);
+ assertThat(testMetricMaker.getCount(counterName, true, "foo", 0)).isEqualTo(4);
+ assertThat(testMetricMaker.getCount(counterName, false, "foo", 0)).isEqualTo(0);
+
+ counter.increment(/* field1= */ false, /* field2= */ "foo", /* field3= */ 0);
+ assertThat(testMetricMaker.getCount(counterName, true, "foo", 0)).isEqualTo(4);
+ assertThat(testMetricMaker.getCount(counterName, false, "foo", 0)).isEqualTo(1);
+
+ counter.incrementBy(/* field1= */ false, /* field2= */ "foo", /* field3= */ 0, /* value= */ 4);
+ assertThat(testMetricMaker.getCount(counterName, true, "foo", 0)).isEqualTo(4);
+ assertThat(testMetricMaker.getCount(counterName, false, "foo", 0)).isEqualTo(5);
+
+ counter.increment(/* field1= */ true, /* field2= */ "bar", /* field3= */ 0);
+ assertThat(testMetricMaker.getCount(counterName, true, "foo", 0)).isEqualTo(4);
+ assertThat(testMetricMaker.getCount(counterName, true, "bar", 0)).isEqualTo(1);
+
+ counter.incrementBy(/* field1= */ true, /* field2= */ "bar", /* field3= */ 0, /* value= */ 5);
+ assertThat(testMetricMaker.getCount(counterName, true, "foo", 0)).isEqualTo(4);
+ assertThat(testMetricMaker.getCount(counterName, true, "bar", 0)).isEqualTo(6);
+
+ counter.increment(/* field1= */ false, /* field2= */ "foo", /* field3= */ 1);
+ assertThat(testMetricMaker.getCount(counterName, true, "foo", 0)).isEqualTo(4);
+ assertThat(testMetricMaker.getCount(counterName, false, "foo", 1)).isEqualTo(1);
+
+ counter.incrementBy(/* field1= */ false, /* field2= */ "foo", /* field3= */ 1, /* value= */ 6);
+ assertThat(testMetricMaker.getCount(counterName, true, "foo", 0)).isEqualTo(4);
+ assertThat(testMetricMaker.getCount(counterName, false, "foo", 1)).isEqualTo(7);
+
+ assertThat(testMetricMaker.getCount(counterName)).isEqualTo(0);
+ assertThat(testMetricMaker.getCount(counterName, true)).isEqualTo(0);
+ assertThat(testMetricMaker.getCount(counterName, false)).isEqualTo(0);
+ assertThat(testMetricMaker.getCount(counterName, true, "foo")).isEqualTo(0);
+ assertThat(testMetricMaker.getCount(counterName, false, "foo")).isEqualTo(0);
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/api/change/ApplyPatchIT.java b/javatests/com/google/gerrit/acceptance/api/change/ApplyPatchIT.java
index 898e1ff..0b55563 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/ApplyPatchIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/ApplyPatchIT.java
@@ -213,6 +213,29 @@
}
@Test
+ public void applyGerritBasedPatchUsingRestWithEncodedPatch_success() throws Exception {
+ String head = getHead(repo(), HEAD).name();
+ createBranchWithRevision(BranchNameKey.create(project, "branch"), head);
+ PushOneCommit.Result baseCommit = createChange("Add file", ADDED_FILE_NAME, ADDED_FILE_CONTENT);
+ baseCommit.assertOkStatus();
+ createBranchWithRevision(BranchNameKey.create(project, DESTINATION_BRANCH), head);
+ RestResponse patchResp =
+ userRestSession.get("/changes/" + baseCommit.getChangeId() + "/revisions/current/patch");
+ patchResp.assertOK();
+ String originalEncodedPatch = patchResp.getEntityContent();
+ String originalDecodedPatch = new String(Base64.decode(patchResp.getEntityContent()), UTF_8);
+ ApplyPatchPatchSetInput in = buildInput(originalEncodedPatch);
+ PushOneCommit.Result destChange = createChange();
+
+ RestResponse resp =
+ adminRestSession.post("/changes/" + destChange.getChangeId() + "/patch:apply", in);
+
+ resp.assertOK();
+ BinaryResult resultPatch = gApi.changes().id(destChange.getChangeId()).current().patch();
+ assertThat(removeHeader(resultPatch)).isEqualTo(removeHeader(originalDecodedPatch));
+ }
+
+ @Test
public void applyPatchWithConflict_fails() throws Exception {
initBaseWithFile(MODIFIED_FILE_NAME, "Unexpected base content");
ApplyPatchPatchSetInput in = buildInput(MODIFIED_FILE_DIFF);
@@ -404,6 +427,6 @@
}
private String removeHeader(String s) {
- return s.substring(s.indexOf("\ndiff --git"), s.length() - 1);
+ return s.substring(s.lastIndexOf("\ndiff --git"), s.length() - 1);
}
}
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts
index 721d650..bb27237 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts
@@ -1244,7 +1244,6 @@
<div class="changeStatuses">
${this.changeStatuses.map(
status => html` <gr-change-status
- .change=${this.change}
.revertedChange=${this.revertedChange}
.status=${status}
.resolveWeblinks=${resolveWeblinks}
diff --git a/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status.ts b/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status.ts
index d2b9e2d..073d9f1 100644
--- a/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status.ts
+++ b/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status.ts
@@ -7,7 +7,6 @@
import '../gr-tooltip-content/gr-tooltip-content';
import '../../../styles/shared-styles';
import {ChangeInfo} from '../../../types/common';
-import {ParsedChangeInfo} from '../../../types/types';
import {sharedStyles} from '../../../styles/shared-styles';
import {LitElement, PropertyValues, html, css} from 'lit';
import {customElement, property, state} from 'lit/decorators.js';
@@ -50,9 +49,6 @@
@property({type: Boolean, reflect: true})
flat = false;
- @property({type: Object})
- change?: ChangeInfo | ParsedChangeInfo;
-
@property({type: String})
status?: ChangeStates;
diff --git a/polygerrit-ui/app/models/accounts-model/accounts-model.ts b/polygerrit-ui/app/models/accounts-model/accounts-model.ts
index 2bf6068..1c67857 100644
--- a/polygerrit-ui/app/models/accounts-model/accounts-model.ts
+++ b/polygerrit-ui/app/models/accounts-model/accounts-model.ts
@@ -8,11 +8,14 @@
import {RestApiService} from '../../services/gr-rest-api/gr-rest-api';
import {UserId} from '../../types/common';
import {getUserId, isDetailedAccount} from '../../utils/account-util';
+import {hasOwnProperty} from '../../utils/common-util';
import {define} from '../dependency';
import {Model} from '../model';
export interface AccountsState {
- accounts: {[id: UserId]: AccountDetailInfo};
+ accounts: {
+ [id: UserId]: AccountDetailInfo | AccountInfo;
+ };
}
export const accountsModelToken = define<AccountsModel>('accounts-model');
@@ -24,33 +27,36 @@
});
}
- private updateStateAccount(id: UserId, account?: AccountDetailInfo) {
+ private updateStateAccount(
+ id: UserId,
+ account: AccountDetailInfo | AccountInfo
+ ) {
if (!account) return;
const current = {...this.getState()};
current.accounts = {...current.accounts, [id]: account};
this.setState(current);
}
- async getAccount(partialAccount: AccountInfo) {
+ async getAccount(
+ partialAccount: AccountInfo
+ ): Promise<AccountDetailInfo | AccountInfo> {
const current = this.getState();
const id = getUserId(partialAccount);
- if (current.accounts[id]) return current.accounts[id];
+ if (hasOwnProperty(current.accounts, id)) return current.accounts[id];
// It is possible to add emails to CC when they don't have a Gerrit
- // account. In this case getAccountDetails will return a 404 error hence
- // pass an empty error function to handle that.
+ // account. In this case getAccountDetails will return a 404 error then
+ // we at least use what is in partialAccount.
const account = await this.restApiService.getAccountDetails(id, () => {
- this.updateStateAccount(id, partialAccount as AccountDetailInfo);
+ this.updateStateAccount(id, partialAccount);
return;
});
if (account) this.updateStateAccount(id, account);
- return account;
+ return account ?? partialAccount;
}
async fillDetails(account: AccountInfo) {
if (!isDetailedAccount(account)) {
- if (account.email) return await this.getAccount({email: account.email});
- else if (account._account_id)
- return await this.getAccount({_account_id: account._account_id});
+ return await this.getAccount(account);
}
return account;
}
diff --git a/polygerrit-ui/app/services/gr-rest-api/gr-rest-api-impl.ts b/polygerrit-ui/app/services/gr-rest-api/gr-rest-api-impl.ts
index 0d0c88f..610d8f3 100644
--- a/polygerrit-ui/app/services/gr-rest-api/gr-rest-api-impl.ts
+++ b/polygerrit-ui/app/services/gr-rest-api/gr-rest-api-impl.ts
@@ -769,7 +769,7 @@
userId: AccountId | EmailAddress,
errFn?: ErrorCallback
): Promise<AccountDetailInfo | undefined> {
- return this._restApiHelper.fetchJSON({
+ return this._fetchSharedCacheURL({
url: `/accounts/${encodeURIComponent(userId)}/detail`,
anonymizedUrl: '/accounts/*/detail',
errFn,