Add duplicate subtask handling and tests
Change-Id: I08244cfb488ef576477ecd151a73ed4cb820eca4
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/TaskTree.java b/src/main/java/com/googlesource/gerrit/plugins/task/TaskTree.java
index 0b37f28..3dfda1f 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/task/TaskTree.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/task/TaskTree.java
@@ -30,10 +30,18 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
+import java.util.Set;
import org.eclipse.jgit.errors.ConfigInvalidException;
+/**
+ * Add structure to access the task definitions from the config as a tree.
+ *
+ * <p>This class is a "middle" representation of the task tree. The task config is represented as a
+ * lazily loaded tree, and much of the tree validity is enforced at this layer.
+ */
public class TaskTree {
protected static final String TASK_DIR = "task";
@@ -67,11 +75,20 @@
protected class NodeList {
protected LinkedList<Task> path = new LinkedList<>();
protected List<Node> nodes;
+ protected Set<String> names = new HashSet<>();
protected void addSubDefinitions(List<Task> tasks) {
for (Task task : tasks) {
- // path check detects looping definitions
- nodes.add(path.contains(task) ? null : new Node(task, path));
+ if (task != null && !path.contains(task) && names.add(task.name)) {
+ // path check above detects looping definitions
+ // names check above detects duplicate subtasks
+ try {
+ nodes.add(new Node(task, path));
+ continue;
+ } catch (Exception e) {
+ } // bad definition, handled below
+ }
+ nodes.add(null);
}
}
}
diff --git a/src/main/resources/Documentation/task_states.md b/src/main/resources/Documentation/task_states.md
index 01716cb..240d616 100644
--- a/src/main/resources/Documentation/task_states.md
+++ b/src/main/resources/Documentation/task_states.md
@@ -182,12 +182,17 @@
```
[task "No PASS criteria"]
applicable = is:open
+ fail-hint = Invalid without Pass criteria and without subtasks
[task "WAITING (subtask INVALID)"]
applicable = is:open
pass = is:open
subtask = Subtask INVALID
+[task "WAITING (subtask duplicate)"]
+ subtask = Subtask INVALID
+ subtask = Subtask INVALID
+
[task "WAITING (subtask missing)"]
applicable = is:open
pass = is:open
@@ -203,6 +208,7 @@
[task "Subtask INVALID"]
applicable = is:open
+ fail-hint = Use when an INVALID subtask is needed, not meant as a test case in itself
[task "NA Bad PASS query"]
applicable = -is:open
@@ -531,6 +537,22 @@
]
},
{
+ "hasPass" : false,
+ "name" : "WAITING (subtask duplicate)",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "hasPass" : false,
+ "name" : "Subtask INVALID",
+ "status" : "INVALID"
+ },
+ {
+ "name" : "UNKNOWN",
+ "status" : "INVALID"
+ }
+ ]
+ },
+ {
"hasPass" : true,
"name" : "WAITING (subtask missing)",
"status" : "WAITING",
diff --git a/test/all b/test/all
index 4e62515..840e11c 100644
--- a/test/all
+++ b/test/all
@@ -353,6 +353,24 @@
},
{
"applicable" : true,
+ "hasPass" : false,
+ "name" : "WAITING (subtask duplicate)",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "applicable" : true,
+ "hasPass" : false,
+ "name" : "Subtask INVALID",
+ "status" : "INVALID"
+ },
+ {
+ "name" : "UNKNOWN",
+ "status" : "INVALID"
+ }
+ ]
+ },
+ {
+ "applicable" : true,
"hasPass" : true,
"name" : "WAITING (subtask missing)",
"status" : "WAITING",
@@ -471,6 +489,24 @@
},
{
"applicable" : true,
+ "hasPass" : false,
+ "name" : "WAITING (subtask duplicate)",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "applicable" : true,
+ "hasPass" : false,
+ "name" : "Subtask INVALID",
+ "status" : "INVALID"
+ },
+ {
+ "name" : "UNKNOWN",
+ "status" : "INVALID"
+ }
+ ]
+ },
+ {
+ "applicable" : true,
"hasPass" : true,
"name" : "WAITING (subtask missing)",
"status" : "WAITING",
diff --git a/test/invalid b/test/invalid
index 528cad0..3347b60 100644
--- a/test/invalid
+++ b/test/invalid
@@ -74,6 +74,24 @@
},
{
"applicable" : true,
+ "hasPass" : false,
+ "name" : "WAITING (subtask duplicate)",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "applicable" : true,
+ "hasPass" : false,
+ "name" : "Subtask INVALID",
+ "status" : "INVALID"
+ },
+ {
+ "name" : "UNKNOWN",
+ "status" : "INVALID"
+ }
+ ]
+ },
+ {
+ "applicable" : true,
"hasPass" : true,
"name" : "WAITING (subtask missing)",
"status" : "WAITING",
@@ -180,6 +198,24 @@
},
{
"applicable" : true,
+ "hasPass" : false,
+ "name" : "WAITING (subtask duplicate)",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "applicable" : true,
+ "hasPass" : false,
+ "name" : "Subtask INVALID",
+ "status" : "INVALID"
+ },
+ {
+ "name" : "UNKNOWN",
+ "status" : "INVALID"
+ }
+ ]
+ },
+ {
+ "applicable" : true,
"hasPass" : true,
"name" : "WAITING (subtask missing)",
"status" : "WAITING",
diff --git a/test/invalid-applicable b/test/invalid-applicable
index c078c92..b1f9f0b 100644
--- a/test/invalid-applicable
+++ b/test/invalid-applicable
@@ -47,6 +47,22 @@
]
},
{
+ "hasPass" : false,
+ "name" : "WAITING (subtask duplicate)",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "hasPass" : false,
+ "name" : "Subtask INVALID",
+ "status" : "INVALID"
+ },
+ {
+ "name" : "UNKNOWN",
+ "status" : "INVALID"
+ }
+ ]
+ },
+ {
"hasPass" : true,
"name" : "WAITING (subtask missing)",
"status" : "WAITING",
diff --git a/test/preview b/test/preview
index 9394637..2989f99 100644
--- a/test/preview
+++ b/test/preview
@@ -30,6 +30,24 @@
},
{
"applicable" : true,
+ "hasPass" : false,
+ "name" : "WAITING (subtask duplicate)",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "applicable" : true,
+ "hasPass" : false,
+ "name" : "Subtask INVALID",
+ "status" : "INVALID"
+ },
+ {
+ "name" : "UNKNOWN",
+ "status" : "INVALID"
+ }
+ ]
+ },
+ {
+ "applicable" : true,
"hasPass" : true,
"name" : "WAITING (subtask missing)",
"status" : "WAITING",
@@ -183,6 +201,24 @@
},
{
"applicable" : true,
+ "hasPass" : false,
+ "name" : "WAITING (subtask duplicate)",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "applicable" : true,
+ "hasPass" : false,
+ "name" : "Subtask INVALID",
+ "status" : "INVALID"
+ },
+ {
+ "name" : "UNKNOWN",
+ "status" : "INVALID"
+ }
+ ]
+ },
+ {
+ "applicable" : true,
"hasPass" : true,
"name" : "WAITING (subtask missing)",
"status" : "WAITING",
diff --git a/test/preview.invalid b/test/preview.invalid
index 16e0426..9521061 100644
--- a/test/preview.invalid
+++ b/test/preview.invalid
@@ -30,6 +30,24 @@
},
{
"applicable" : true,
+ "hasPass" : false,
+ "name" : "WAITING (subtask duplicate)",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "applicable" : true,
+ "hasPass" : false,
+ "name" : "Subtask INVALID",
+ "status" : "INVALID"
+ },
+ {
+ "name" : "UNKNOWN",
+ "status" : "INVALID"
+ }
+ ]
+ },
+ {
+ "applicable" : true,
"hasPass" : true,
"name" : "WAITING (subtask missing)",
"status" : "WAITING",
@@ -136,6 +154,24 @@
},
{
"applicable" : true,
+ "hasPass" : false,
+ "name" : "WAITING (subtask duplicate)",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "applicable" : true,
+ "hasPass" : false,
+ "name" : "Subtask INVALID",
+ "status" : "INVALID"
+ },
+ {
+ "name" : "UNKNOWN",
+ "status" : "INVALID"
+ }
+ ]
+ },
+ {
+ "applicable" : true,
"hasPass" : true,
"name" : "WAITING (subtask missing)",
"status" : "WAITING",