Fix '--task--include-statistics' option
Currently, on passing the option tasks plugin fails to add the
attributes with error [1] and output [2].
[1] error populating attribute on changes from plugin task [CONTEXT ratelimit_period="1 MINUTES" request="SSH" ]
java.lang.ClassCastException: class [Ljava.lang.Object; cannot be cast to class [Lcom.googlesource.gerrit.plugins.task.util.TopKeyMap$Entry; ([Ljava.lang.Object; is in module java.base of loader 'bootstrap'; [Lcom.googlesource.gerrit.plugins.task.util.TopKeyMap$Entry; is in unnamed module of loader java.net.FactoryURLClassLoader @58961770)
at com.googlesource.gerrit.plugins.task.util.TopKeyMap.<init>(TopKeyMap.java:66)
at com.googlesource.gerrit.plugins.task.statistics.HitHashMap.initStatistics(HitHashMap.java:144)
at com.googlesource.gerrit.plugins.task.PredicateCache.initStatistics(PredicateCache.java:59)
at com.googlesource.gerrit.plugins.task.TaskPluginDefinedInfoFactory.initStatistics(TaskPluginDefinedInfoFactory.java:405)
at com.googlesource.gerrit.plugins.task.TaskPluginDefinedInfoFactory.createPluginDefinedInfos(TaskPluginDefinedInfoFactory.java:138)
[2] "message": "Something went wrong in plugin: task"
Change-Id: Id76ea65909a93afaffd3747afef1ac41a7ae3aeb
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/util/TopKeyMap.java b/src/main/java/com/googlesource/gerrit/plugins/task/util/TopKeyMap.java
index a6627fb..8603ce0 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/task/util/TopKeyMap.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/task/util/TopKeyMap.java
@@ -55,15 +55,14 @@
}
}
- protected Entry[] entries;
+ protected Object[] entries;
public TopKeyMap() {
this(5);
}
- @SuppressWarnings("unchecked")
public TopKeyMap(int length) {
- entries = (Entry[]) new Object[length];
+ entries = new Object[length];
for (int i = 0; i < entries.length; i++) {
entries[i] = new Entry();
}
@@ -73,10 +72,11 @@
addIfTop(0, key, value);
}
+ @SuppressWarnings("unchecked")
protected void addIfTop(int i, long key, V value) {
- if (entries[entries.length - 1].key < key) {
+ if (((Entry) entries[entries.length - 1]).key < key) {
for (; i < entries.length; i++) {
- Entry e = entries[i];
+ Entry e = (Entry) entries[i];
if (e.key < key) {
long eKValue = e.key;
V eValue = e.value;
diff --git a/src/test/java/com/googlesource/gerrit/plugins/task/TaskIncludeStatisticsTest.java b/src/test/java/com/googlesource/gerrit/plugins/task/TaskIncludeStatisticsTest.java
new file mode 100644
index 0000000..8c619fe
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/task/TaskIncludeStatisticsTest.java
@@ -0,0 +1,109 @@
+// Copyright (C) 2024 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.googlesource.gerrit.plugins.task;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.GitUtil.fetch;
+import static com.googlesource.gerrit.plugins.task.TaskFileConstants.TASK_CFG;
+
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.UseSsh;
+import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.RefNames;
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import com.googlesource.gerrit.plugins.task.TaskPluginDefinedInfoFactory.TaskPluginAttribute;
+import java.io.BufferedReader;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
+import org.eclipse.jgit.junit.TestRepository;
+import org.junit.Test;
+
+@UseSsh
+public class TaskIncludeStatisticsTest extends AbstractDaemonTest {
+ private static final Gson GSON = new Gson();
+
+ @Test
+ public void testIncludeStatisticsDoesNotResultInError() throws Exception {
+ try (AutoCloseable task = installPlugin("task", Modules.Module.class)) {
+ TestRepository<InMemoryRepository> repo = cloneProject(allProjects);
+ fetch(repo, RefNames.REFS_CONFIG + ":meta-config");
+ repo.reset("meta-config");
+ createCommitAndPush(repo, RefNames.REFS_CONFIG, "Update task config", TASK_CFG, getConfig());
+
+ PushOneCommit.Result change = createChange();
+ String sshOutput =
+ adminSshSession.exec(
+ String.format(
+ "gerrit query change:%s --task--applicable --task--include-statistics --format json",
+ change.getChange().getId().get()));
+ adminSshSession.assertSuccess();
+
+ Map<Change.Id, TaskPluginAttribute> taskAttrByChange = getTaskAttributes(sshOutput);
+ TaskPluginAttribute taskAttribute = taskAttrByChange.get(change.getChange().getId());
+ assertThat(taskAttribute.message).isNull();
+ assertThat(taskAttribute.roots.size()).isEqualTo(1);
+ assertThat(taskAttribute.roots.get(0).name).isEqualTo("test root");
+ }
+ }
+
+ private String getConfig() {
+ return "[root \"test root\"]\n" + " applicable = is:open\n" + " pass = True";
+ }
+
+ private Map<Change.Id, TaskPluginAttribute> getTaskAttributes(String sshOutput) throws Exception {
+ List<Map<String, Object>> changeAttrs = getChangeAttrs(sshOutput);
+ Map<Change.Id, TaskPluginAttribute> taskAttrByChange = new HashMap<>();
+ changeAttrs.forEach(
+ change -> {
+ Double changeId = (Double) change.get("number");
+ taskAttrByChange.put(
+ Change.id(changeId.intValue()),
+ deserializeTaskAttributeFromPluginList(change.get("plugins")));
+ });
+ return taskAttrByChange;
+ }
+
+ private List<Map<String, Object>> getChangeAttrs(String sshOutput) throws Exception {
+ List<Map<String, Object>> changeAttrs = new ArrayList<>();
+ try (BufferedReader buffer = new BufferedReader(new StringReader(sshOutput))) {
+ buffer
+ .lines()
+ .forEach(
+ line -> {
+ Map<String, Object> changeAttr =
+ GSON.fromJson(line, new TypeToken<Map<String, Object>>() {}.getType());
+ if (!"stats".equals(changeAttr.get("type"))) {
+ changeAttrs.add(changeAttr);
+ }
+ });
+ }
+ return changeAttrs;
+ }
+
+ private TaskPluginAttribute deserializeTaskAttributeFromPluginList(@Nullable Object plugins) {
+ if (plugins == null) {
+ return null;
+ }
+ return GSON.fromJson(
+ GSON.toJson(((List<?>) plugins).get(0)), new TypeToken<TaskPluginAttribute>() {}.getType());
+ }
+}