Merge "Add counts to Task headers" into stable-2.16
diff --git a/.zuul.yaml b/.zuul.yaml
new file mode 100644
index 0000000..e95b300
--- /dev/null
+++ b/.zuul.yaml
@@ -0,0 +1,10 @@
+- job:
+ name: plugins-task-build
+ parent: gerrit-plugin-build
+ pre-run:
+ tools/playbooks/install_docker.yaml
+
+- project:
+ check:
+ jobs:
+ - plugins-task-build
diff --git a/BUILD b/BUILD
index 3f724db..f5ca52c 100644
--- a/BUILD
+++ b/BUILD
@@ -6,14 +6,16 @@
load("//tools/bzl:genrule2.bzl", "genrule2")
load("//tools/bzl:js.bzl", "polygerrit_plugin")
+plugin_name = "task"
+
gerrit_plugin(
- name = "task",
+ name = plugin_name,
srcs = glob(["src/main/java/**/*.java"]),
manifest_entries = [
- "Gerrit-PluginName: task",
+ "Gerrit-PluginName: " + plugin_name,
"Gerrit-ApiVersion: 2.16",
"Implementation-Title: Task Plugin",
- "Implementation-URL: https://gerrit-review.googlesource.com/#/admin/projects/plugins/task",
+ "Implementation-URL: https://gerrit-review.googlesource.com/#/admin/projects/plugins/" + plugin_name,
"Gerrit-Module: com.googlesource.gerrit.plugins.task.Modules$Module",
"Gerrit-SshModule: com.googlesource.gerrit.plugins.task.Modules$SshModule",
"Gerrit-HttpModule: com.googlesource.gerrit.plugins.task.Modules$HttpModule",
@@ -42,3 +44,12 @@
]),
app = "plugin.html",
)
+
+sh_test(
+ name = "docker-tests",
+ size = "medium",
+ srcs = ["test/docker/run.sh"],
+ args = ["--task-plugin-jar", "$(location :task)"],
+ data = [plugin_name] + glob(["test/**"]),
+ local = True,
+)
diff --git a/pom.xml b/pom.xml
deleted file mode 100644
index dc48bcf..0000000
--- a/pom.xml
+++ /dev/null
@@ -1,90 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-Copyright (C) 2016 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.
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
- <modelVersion>4.0.0</modelVersion>
-
- <groupId>com.googlesource.gerrit.plugins.task</groupId>
- <artifactId>task</artifactId>
- <packaging>jar</packaging>
- <version>2.16</version>
- <name>task</name>
-
- <properties>
- <Gerrit-ApiType>plugin</Gerrit-ApiType>
- <Gerrit-ApiVersion>${project.version}</Gerrit-ApiVersion>
- </properties>
-
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-jar-plugin</artifactId>
- <version>2.4</version>
- <configuration>
- <archive>
- <manifestEntries>
- <Gerrit-Module>com.googlesource.gerrit.plugins.task.Modules$Module</Gerrit-Module>
- <Gerrit-HttpModule>com.googlesource.gerrit.plugins.task.Modules$HttpModule</Gerrit-HttpModule>
- <Gerrit-SshModule>com.googlesource.gerrit.plugins.task.Modules$SshModule</Gerrit-SshModule>
- <Implementation-Vendor>Gerrit Code Review</Implementation-Vendor>
- <Implementation-URL>http://code.google.com/p/gerrit/</Implementation-URL>
-
- <Implementation-Title>${Gerrit-ApiType} ${project.artifactId}</Implementation-Title>
- <Implementation-Version>${project.version}</Implementation-Version>
-
- <Gerrit-ApiType>${Gerrit-ApiType}</Gerrit-ApiType>
- <Gerrit-ApiVersion>${Gerrit-ApiVersion}</Gerrit-ApiVersion>
- </manifestEntries>
- </archive>
- </configuration>
- </plugin>
-
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- <version>2.3.2</version>
- <configuration>
- <source>1.8</source>
- <target>1.8</target>
- <encoding>UTF-8</encoding>
- <fork>true</fork>
- <compilerArgs>
- <arg>-XX:MaxPermSize=256m</arg>
- </compilerArgs>
- </configuration>
- </plugin>
- </plugins>
- </build>
-
- <dependencies>
- <dependency>
- <groupId>com.google.gerrit</groupId>
- <artifactId>gerrit-${Gerrit-ApiType}-api</artifactId>
- <version>${Gerrit-ApiVersion}</version>
- <scope>provided</scope>
- </dependency>
- </dependencies>
-
- <repositories>
- <repository>
- <id>gerrit-api-repository</id>
- <url>https://gerrit-api.commondatastorage.googleapis.com/snapshot/</url>
- </repository>
- </repositories>
-</project>
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/TaskAttributeFactory.java b/src/main/java/com/googlesource/gerrit/plugins/task/TaskAttributeFactory.java
index ee40d52..1bdf987 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/task/TaskAttributeFactory.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/task/TaskAttributeFactory.java
@@ -69,7 +69,8 @@
protected final TaskTree definitions;
protected final ChangeQueryBuilder cqb;
- protected final Map<String, Predicate<ChangeData>> predicatesByQuery = new HashMap<>();
+ protected final Map<String, ThrowingProvider<Predicate<ChangeData>, QueryParseException>>
+ predicatesByQuery = new HashMap<>();
protected Modules.MyOptions options;
@@ -86,16 +87,12 @@
for (PatchSetArgument psa : options.patchSetArguments) {
definitions.masquerade(psa);
}
- try {
return createWithExceptions(c);
- } catch (OrmException e) {
- log.atSevere().withCause(e).log("Cannot load tasks for: %s", c);
- }
}
return null;
}
- protected PluginDefinedInfo createWithExceptions(ChangeData c) throws OrmException {
+ protected PluginDefinedInfo createWithExceptions(ChangeData c) {
TaskPluginAttribute a = new TaskPluginAttribute();
try {
for (Node node : definitions.getRootNodes()) {
@@ -111,8 +108,7 @@
return a;
}
- protected void addApplicableTasks(List<TaskAttribute> atts, ChangeData c, Node node)
- throws OrmException {
+ protected void addApplicableTasks(List<TaskAttribute> atts, ChangeData c, Node node) {
try {
Task def = node.definition;
TaskAttribute att = new TaskAttribute(def.name);
@@ -155,7 +151,7 @@
}
}
}
- } catch (QueryParseException e) {
+ } catch (OrmException | QueryParseException | RuntimeException e) {
atts.add(invalid()); // bad applicability query
}
}
@@ -199,15 +195,15 @@
match(c, def.fail);
match(c, def.pass);
return true;
- } catch (OrmException | QueryParseException e) {
+ } catch (OrmException | QueryParseException | RuntimeException e) {
return false;
}
}
- protected Status getStatus(ChangeData c, Task def, TaskAttribute a) throws OrmException {
+ protected Status getStatus(ChangeData c, Task def, TaskAttribute a) {
try {
return getStatusWithExceptions(c, def, a);
- } catch (QueryParseException e) {
+ } catch (OrmException | QueryParseException | RuntimeException e) {
return Status.INVALID;
}
}
@@ -219,7 +215,8 @@
boolean hasDefinedSubtasks =
!(def.subTasks.isEmpty()
&& def.subTasksFiles.isEmpty()
- && def.subTasksExternals.isEmpty());
+ && def.subTasksExternals.isEmpty()
+ && def.subTasksFactories.isEmpty());
if (hasDefinedSubtasks) {
// Remove 'Grouping" tasks (tasks with subtasks but no PASS
// or FAIL criteria) from the output if none of their subtasks
@@ -297,30 +294,47 @@
}
protected boolean match(ChangeData c, String query) throws OrmException, QueryParseException {
- if (query == null || query.equalsIgnoreCase("true")) {
+ if (query == null) {
return true;
}
- Predicate<ChangeData> pred = predicatesByQuery.get(query);
- if (pred == null) {
- pred = cqb.parse(query);
- predicatesByQuery.put(query, pred);
- }
- return pred.asMatchable().match(c);
+ return matchWithExceptions(c, query);
}
protected Boolean matchOrNull(ChangeData c, String query) {
if (query != null) {
try {
- if (query.equalsIgnoreCase("true")) {
- return true;
- }
- return cqb.parse(query).asMatchable().match(c);
- } catch (OrmException | QueryParseException e) {
+ return matchWithExceptions(c, query);
+ } catch (OrmException | QueryParseException | RuntimeException e) {
}
}
return null;
}
+ protected boolean matchWithExceptions(ChangeData c, String query)
+ throws QueryParseException, OrmException {
+ if ("true".equalsIgnoreCase(query)) {
+ return true;
+ }
+ return getPredicate(query).asMatchable().match(c);
+ }
+
+ protected Predicate<ChangeData> getPredicate(String query) throws QueryParseException {
+ ThrowingProvider<Predicate<ChangeData>, QueryParseException> predProvider =
+ predicatesByQuery.get(query);
+ if (predProvider != null) {
+ return predProvider.get();
+ }
+ // never seen 'query' before
+ try {
+ Predicate<ChangeData> pred = cqb.parse(query);
+ predicatesByQuery.put(query, new ThrowingProvider.Entry<>(pred));
+ return pred;
+ } catch (QueryParseException e) {
+ predicatesByQuery.put(query, new ThrowingProvider.Thrown<>(e));
+ throw e;
+ }
+ }
+
protected static boolean isAllNull(Object... vals) {
for (Object val : vals) {
if (val != null) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/TaskConfig.java b/src/main/java/com/googlesource/gerrit/plugins/task/TaskConfig.java
index 5fcc8f1..ef23533 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/task/TaskConfig.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/task/TaskConfig.java
@@ -17,6 +17,7 @@
import com.google.gerrit.common.Container;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.server.git.meta.AbstractVersionedMetaData;
+import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -38,7 +39,7 @@
}
}
- public class Task extends Section {
+ public class TaskBase extends Section {
public String applicable;
public Map<String, String> exported;
public String fail;
@@ -51,12 +52,13 @@
public String readyHint;
public List<String> subTasks;
public List<String> subTasksExternals;
+ public List<String> subTasksFactories;
public List<String> subTasksFiles;
public boolean isVisible;
public boolean isTrusted;
- public Task(SubSection s, boolean isVisible, boolean isTrusted) {
+ public TaskBase(SubSection s, boolean isVisible, boolean isTrusted) {
this.isVisible = isVisible;
this.isTrusted = isTrusted;
applicable = getString(s, KEY_APPLICABLE, null);
@@ -71,8 +73,52 @@
readyHint = getString(s, KEY_READY_HINT, null);
subTasks = getStringList(s, KEY_SUBTASK);
subTasksExternals = getStringList(s, KEY_SUBTASKS_EXTERNAL);
+ subTasksFactories = getStringList(s, KEY_SUBTASKS_FACTORY);
subTasksFiles = getStringList(s, KEY_SUBTASKS_FILE);
}
+
+ protected TaskBase(TaskBase base) {
+ for (Field field : TaskBase.class.getDeclaredFields()) {
+ try {
+ field.setAccessible(true);
+ field.set(this, field.get(base));
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+
+ public class Task extends TaskBase {
+ public String name;
+
+ public Task(SubSection s, boolean isVisible, boolean isTrusted) {
+ super(s, isVisible, isTrusted);
+ name = getString(s, KEY_NAME, s.subSection);
+ }
+
+ protected Task(TaskBase base) {
+ super(base);
+ }
+ }
+
+ public class TasksFactory extends TaskBase {
+ public String namesFactory;
+
+ public TasksFactory(SubSection s, boolean isVisible, boolean isTrusted) {
+ super(s, isVisible, isTrusted);
+ namesFactory = getString(s, KEY_NAMES_FACTORY, null);
+ }
+ }
+
+ public class NamesFactory extends Section {
+ public List<String> names;
+ public String type;
+
+ public NamesFactory(SubSection s) {
+ names = getStringList(s, KEY_NAME);
+ type = getString(s, KEY_TYPE, null);
+ }
}
public class External extends Section {
@@ -91,8 +137,10 @@
Pattern.compile("([^ |]*( *[^ |])*) *\\| *");
protected static final String SECTION_EXTERNAL = "external";
+ protected static final String SECTION_NAMES_FACTORY = "names-factory";
protected static final String SECTION_ROOT = "root";
protected static final String SECTION_TASK = "task";
+ protected static final String SECTION_TASKS_FACTORY = "tasks-factory";
protected static final String KEY_APPLICABLE = "applicable";
protected static final String KEY_EXPORT_PREFIX = "export-";
protected static final String KEY_FAIL = "fail";
@@ -100,18 +148,27 @@
protected static final String KEY_FILE = "file";
protected static final String KEY_IN_PROGRESS = "in-progress";
protected static final String KEY_NAME = "name";
+ protected static final String KEY_NAMES_FACTORY = "names-factory";
protected static final String KEY_PASS = "pass";
protected static final String KEY_PRELOAD_TASK = "preload-task";
protected static final String KEY_PROPERTIES_PREFIX = "set-";
protected static final String KEY_READY_HINT = "ready-hint";
protected static final String KEY_SUBTASK = "subtask";
protected static final String KEY_SUBTASKS_EXTERNAL = "subtasks-external";
+ protected static final String KEY_SUBTASKS_FACTORY = "subtasks-factory";
protected static final String KEY_SUBTASKS_FILE = "subtasks-file";
+ protected static final String KEY_TYPE = "type";
protected static final String KEY_USER = "user";
public boolean isVisible;
public boolean isTrusted;
+ public Task createTask(TasksFactory tasks, String name) {
+ Task task = new Task(tasks);
+ task.name = name;
+ return task;
+ }
+
public TaskConfig(Branch.NameKey branch, String fileName, boolean isVisible, boolean isTrusted) {
super(branch, fileName);
this.isVisible = isVisible;
@@ -173,6 +230,14 @@
return getNames(subSection).isEmpty() ? null : new Task(subSection, isVisible, isTrusted);
}
+ public TasksFactory getTasksFactory(String name) {
+ return new TasksFactory(new SubSection(SECTION_TASKS_FACTORY, name), isVisible, isTrusted);
+ }
+
+ public NamesFactory getNamesFactory(String name) {
+ return new NamesFactory(new SubSection(SECTION_NAMES_FACTORY, name));
+ }
+
public External getExternal(String name) {
return getExternal(new SubSection(SECTION_EXTERNAL, name));
}
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 ddde9c5..99a1ba9 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/task/TaskTree.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/task/TaskTree.java
@@ -24,7 +24,9 @@
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.googlesource.gerrit.plugins.task.TaskConfig.External;
+import com.googlesource.gerrit.plugins.task.TaskConfig.NamesFactory;
import com.googlesource.gerrit.plugins.task.TaskConfig.Task;
+import com.googlesource.gerrit.plugins.task.TaskConfig.TasksFactory;
import com.googlesource.gerrit.plugins.task.cli.PatchSetArgument;
import java.io.IOException;
import java.nio.file.Path;
@@ -131,6 +133,7 @@
protected void addSubDefinitions() throws OrmException {
addSubDefinitions(getSubDefinitions());
+ addSubDefinitions(getTasksFactoryDefinitions());
addSubFileDefinitions();
addExternalDefinitions();
}
@@ -142,7 +145,7 @@
protected void addSubFileDefinitions() {
for (String file : definition.subTasksFiles) {
try {
- addSubDefinitions(getTasks(definition.config.getBranch(), file));
+ addSubDefinitions(getTaskDefinitions(definition.config.getBranch(), file));
} catch (ConfigInvalidException | IOException e) {
nodes.add(null);
}
@@ -179,12 +182,30 @@
return defs;
}
- protected List<Task> getTaskDefinitions(External external)
- throws ConfigInvalidException, IOException, OrmException {
- return getTasks(resolveUserBranch(external.user), external.file);
+ protected List<Task> getTasksFactoryDefinitions() {
+ List<Task> taskList = new ArrayList<>();
+ for (String taskFactoryName : definition.subTasksFactories) {
+ TasksFactory tasksFactory = definition.config.getTasksFactory(taskFactoryName);
+ if (tasksFactory != null) {
+ NamesFactory namesFactory = definition.config.getNamesFactory(tasksFactory.namesFactory);
+ if (namesFactory != null && "static".equals(namesFactory.type)) {
+ for (String name : namesFactory.names) {
+ taskList.add(definition.config.createTask(tasksFactory, name));
+ }
+ continue;
+ }
+ }
+ taskList.add(null);
+ }
+ return taskList;
}
- protected List<Task> getTasks(Branch.NameKey branch, String file)
+ protected List<Task> getTaskDefinitions(External external)
+ throws ConfigInvalidException, IOException, OrmException {
+ return getTaskDefinitions(resolveUserBranch(external.user), external.file);
+ }
+
+ protected List<Task> getTaskDefinitions(Branch.NameKey branch, String file)
throws ConfigInvalidException, IOException {
return taskFactory
.getTaskConfig(branch, resolveTaskFileName(file), definition.isTrusted)
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/ThrowingProvider.java b/src/main/java/com/googlesource/gerrit/plugins/task/ThrowingProvider.java
new file mode 100644
index 0000000..7644143
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/task/ThrowingProvider.java
@@ -0,0 +1,45 @@
+// Copyright (C) 2020 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;
+
+public interface ThrowingProvider<V, E extends Exception> {
+ public V get() throws E;
+
+ public static class Entry<V, E extends Exception> implements ThrowingProvider<V, E> {
+ protected V entry;
+
+ public Entry(V entry) {
+ this.entry = entry;
+ }
+
+ @Override
+ public V get() {
+ return entry;
+ }
+ }
+
+ public static class Thrown<V, E extends Exception> implements ThrowingProvider<V, E> {
+ protected E exception;
+
+ public Thrown(E exception) {
+ this.exception = exception;
+ }
+
+ @Override
+ public V get() throws E {
+ throw exception;
+ }
+ }
+}
diff --git a/src/main/resources/Documentation/task.md b/src/main/resources/Documentation/task.md
index 62297c5..9fe4e3d 100644
--- a/src/main/resources/Documentation/task.md
+++ b/src/main/resources/Documentation/task.md
@@ -181,6 +181,21 @@
...
```
+`subtasks-factory`
+
+: A subtasks-factory key specifies a task-factory, which generates zero or more
+tasks that are subtasks of the current task. This key may be used several times
+in a task section to reference tasks-factory sections.
+
+Example:
+
+```
+ subtasks-factory = "static tasks factory"
+ ...
+ [tasks-factory "static tasks factory"]
+ ...
+```
+
`subtasks-external`
: This key defines a file containing subtasks of the current task. This
@@ -274,6 +289,69 @@
Backup Optional Subtask {$_name} Backup |
Default Subtask # Must exist if the above two don't!
```
+Tasks-Factory
+-------------
+A tasks-factory section supports all the keys supported by task sections. In
+addition, this section must have a names-factory key which refers to a
+names-factory section. In conjunction with the names-factory, a tasks-factory
+section creates zero or more task definitions that look like regular tasks,
+each with a name provided by the names-factory, and all using the task definition
+set in the tasks-factory.
+
+A tasks-factory section is referenced by a subtasks-factory key in a "task"
+section. A sample task.config which defines a tasks-factory section might look
+like this:
+
+```
+[task "static task list"]
+ subtasks-factory = static tasks factory
+ ...
+
+[tasks-factory "static tasks factory"]
+ names-factory = static names factory list
+ ...
+```
+
+Names-Factory
+-------------
+A names-factory section defines a collection of name keys which are used to
+generate the names for task definitions. The section should contain a "type"
+key that specifies the type.
+
+A names-factory section is referenced by a names-factory key in a "tasks-factory"
+section. A sample task.config which defines a names-factory section might look like
+this:
+
+```
+[names-factory "static names factory list"]
+ name = my a task
+ name = my b task
+ type = static
+```
+
+The following keys may be defined in any names-factory section:
+
+`name`
+
+: This key defines the name of the tasks. This key may be used several times
+in order to define more than one task. The name key can only be used along with
+names-factory of type `static`.
+
+Example:
+```
+ name = my a task
+ name = 12345
+```
+
+`type`
+
+: This key defines the type of the names-factory section. The only
+accepted value is `static`.
+
+Example:
+```
+ type = static
+```
External Entries
----------------
diff --git a/src/main/resources/Documentation/task_states.md b/src/main/resources/Documentation/task_states.md
index 721032f..58d6ca7 100644
--- a/src/main/resources/Documentation/task_states.md
+++ b/src/main/resources/Documentation/task_states.md
@@ -103,6 +103,16 @@
subtasks-external = user special
subtasks-external = file missing
+[root "Root tasks-factory"]
+ subtasks-factory = tasks-factory static
+
+[root "Root tasks-factory static (empty name)"]
+ subtasks-factory = tasks-factory static (empty name)
+
+[root "Root tasks-factory static (empty name PASS)"]
+ pass = True
+ subtasks-factory = tasks-factory static (empty name)
+
[root "Root Properties"]
set-root-property = root-value
export-root = ${_name}
@@ -129,6 +139,14 @@
applicable = NOT is:open # Assumes test query is "is:open"
subtasks-file = invalids.config
+[tasks-factory "tasks-factory static"]
+ names-factory = names-factory static list
+ fail = True
+
+[tasks-factory "tasks-factory static (empty name)"]
+ names-factory = names-factory static (empty name list)
+ fail = True
+
[task "Subtask APPLICABLE"]
applicable = is:open
pass = True
@@ -237,6 +255,16 @@
[external "file missing"]
user = testuser
file = missing
+
+[names-factory "names-factory static list"]
+ name = my a task
+ name = my b task
+ name = my c task
+ type = static
+
+[names-factory "names-factory static (empty name list)"]
+ type = static
+
```
`task/common.config` file in project `All-Projects` on ref `refs/meta/config`.
@@ -303,6 +331,30 @@
set-A = ${B}
set-B = ${A}
fail = True
+
+[task "task (tasks-factory missing)"]
+ subtasks-factory = missing
+
+[task "task (names-factory type missing)"]
+ subtasks-factory = tasks-factory (names-factory type missing)
+
+[task "task (names-factory type INVALID)"]
+ subtasks-factory = tasks-factory (names-factory type INVALID)
+
+[tasks-factory "tasks-factory (names-factory type missing)"]
+ names-factory = names-factory (type missing)
+ fail = True
+
+[names-factory "names-factory (type missing)"]
+ name = no type test
+
+[tasks-factory "tasks-factory (names-factory type INVALID)"]
+ names-factory = name-factory (type INVALID)
+
+[names-factory "names-factory (type INVALID)"]
+ name = invalid type test
+ type = invalid
+
```
`task/special.config` file in project `All-Users` on ref `refs/users/self`.
@@ -616,6 +668,33 @@
]
},
{
+ "hasPass" : false,
+ "name" : "Root tasks-factory",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "hasPass" : true,
+ "name" : "my a task",
+ "status" : "FAIL"
+ },
+ {
+ "hasPass" : true,
+ "name" : "my b task",
+ "status" : "FAIL"
+ },
+ {
+ "hasPass" : true,
+ "name" : "my c task",
+ "status" : "FAIL"
+ }
+ ]
+ },
+ {
+ "hasPass" : true,
+ "name" : "Root tasks-factory static (empty name PASS)",
+ "status" : "PASS"
+ },
+ {
"exported" : {
"root" : "Root Properties"
},
@@ -828,6 +907,39 @@
{
"name" : "UNKNOWN",
"status" : "INVALID"
+ },
+ {
+ "hasPass" : false,
+ "name" : "task (tasks-factory missing)",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "name" : "UNKNOWN",
+ "status" : "INVALID"
+ }
+ ]
+ },
+ {
+ "hasPass" : false,
+ "name" : "task (names-factory type missing)",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "name" : "UNKNOWN",
+ "status" : "INVALID"
+ }
+ ]
+ },
+ {
+ "hasPass" : false,
+ "name" : "task (names-factory type INVALID)",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "name" : "UNKNOWN",
+ "status" : "INVALID"
+ }
+ ]
}
]
}
diff --git a/test/all b/test/all
index 82347ab..6e29127 100644
--- a/test/all
+++ b/test/all
@@ -359,6 +359,43 @@
},
{
"applicable" : true,
+ "hasPass" : false,
+ "name" : "Root tasks-factory",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "applicable" : true,
+ "hasPass" : true,
+ "name" : "my a task",
+ "status" : "FAIL"
+ },
+ {
+ "applicable" : true,
+ "hasPass" : true,
+ "name" : "my b task",
+ "status" : "FAIL"
+ },
+ {
+ "applicable" : true,
+ "hasPass" : true,
+ "name" : "my c task",
+ "status" : "FAIL"
+ }
+ ]
+ },
+ {
+ "applicable" : true,
+ "hasPass" : false,
+ "name" : "Root tasks-factory static (empty name)"
+ },
+ {
+ "applicable" : true,
+ "hasPass" : true,
+ "name" : "Root tasks-factory static (empty name PASS)",
+ "status" : "PASS"
+ },
+ {
+ "applicable" : true,
"exported" : {
"root" : "Root Properties"
},
@@ -619,6 +656,42 @@
{
"name" : "UNKNOWN",
"status" : "INVALID"
+ },
+ {
+ "applicable" : true,
+ "hasPass" : false,
+ "name" : "task (tasks-factory missing)",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "name" : "UNKNOWN",
+ "status" : "INVALID"
+ }
+ ]
+ },
+ {
+ "applicable" : true,
+ "hasPass" : false,
+ "name" : "task (names-factory type missing)",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "name" : "UNKNOWN",
+ "status" : "INVALID"
+ }
+ ]
+ },
+ {
+ "applicable" : true,
+ "hasPass" : false,
+ "name" : "task (names-factory type INVALID)",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "name" : "UNKNOWN",
+ "status" : "INVALID"
+ }
+ ]
}
]
},
@@ -767,6 +840,42 @@
{
"name" : "UNKNOWN",
"status" : "INVALID"
+ },
+ {
+ "applicable" : true,
+ "hasPass" : false,
+ "name" : "task (tasks-factory missing)",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "name" : "UNKNOWN",
+ "status" : "INVALID"
+ }
+ ]
+ },
+ {
+ "applicable" : true,
+ "hasPass" : false,
+ "name" : "task (names-factory type missing)",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "name" : "UNKNOWN",
+ "status" : "INVALID"
+ }
+ ]
+ },
+ {
+ "applicable" : true,
+ "hasPass" : false,
+ "name" : "task (names-factory type INVALID)",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "name" : "UNKNOWN",
+ "status" : "INVALID"
+ }
+ ]
}
]
}
diff --git a/test/check_task_statuses.sh b/test/check_task_statuses.sh
index 31e2d46..3b2bf0b 100755
--- a/test/check_task_statuses.sh
+++ b/test/check_task_statuses.sh
@@ -1,7 +1,6 @@
#!/bin/bash
# Usage:
-# All-Users.git - refs/users/self must already exist
# All-Projects.git - must have 'Push' rights on refs/meta/config
# ---- TEST RESULTS ----
@@ -44,14 +43,18 @@
chmod +x "$hook"
}
-setup_repo() { # repo remote ref
- local repo=$1 remote=$2 ref=$3
+setup_repo() { # repo remote ref [--initial-commit]
+ local repo=$1 remote=$2 ref=$3 init=$4
git init "$repo"
(
cd "$repo"
install_changeid_hook "$repo"
git fetch "$remote" "$ref"
- git checkout FETCH_HEAD
+ if ! git checkout FETCH_HEAD ; then
+ if [ "$init" = "--initial-commit" ] ; then
+ git commit --allow-empty -a -m "Initial Commit"
+ fi
+ fi
)
}
@@ -128,10 +131,11 @@
REF_ALL=refs/meta/config
REF_USERS=refs/users/self
+RESULT=0
mkdir -p "$OUT"
q_setup setup_repo "$ALL" "$REMOTE_ALL" "$REF_ALL"
-q_setup setup_repo "$USERS" "$REMOTE_USERS" "$REF_USERS"
+q_setup setup_repo "$USERS" "$REMOTE_USERS" "$REF_USERS" --initial-commit
mkdir -p "$ALL_TASKS" "$USER_TASKS"
@@ -156,3 +160,5 @@
test_file invalid --task--invalid "$query"
test_file invalid-applicable --task--applicable --task--invalid "$query"
+
+exit $RESULT
diff --git a/test/docker/gerrit/Dockerfile b/test/docker/gerrit/Dockerfile
index 847ce34..059f3c0 100755
--- a/test/docker/gerrit/Dockerfile
+++ b/test/docker/gerrit/Dockerfile
@@ -1,22 +1,13 @@
-FROM openjdk:8
-ARG GERRIT_WAR
-ARG TASK_PLUGIN_JAR
-ARG UID=1000
-ARG GID=1000
+FROM gerritcodereview/gerrit:2.16.27-ubuntu16
-ENV GERRIT_USER gerrit
+USER root
+
ENV GERRIT_SITE /var/gerrit
-ENV USER_HOME /home/$GERRIT_USER
-RUN mkdir -p $GERRIT_SITE/bin $GERRIT_SITE/plugins $GERRIT_SITE/etc $USER_HOME/.ssh
-COPY $GERRIT_WAR $GERRIT_SITE/bin/gerrit.war
-COPY $TASK_PLUGIN_JAR $GERRIT_SITE/plugins/task.jar
-RUN touch $GERRIT_SITE/etc/gerrit.config && \
- git config -f $GERRIT_SITE/etc/gerrit.config auth.type DEVELOPMENT_BECOME_ANY_ACCOUNT
+RUN git config -f "$GERRIT_SITE/etc/gerrit.config" auth.type \
+ DEVELOPMENT_BECOME_ANY_ACCOUNT
-EXPOSE 29418 8080
-RUN groupadd -f -g $GID users2 && \
- useradd -u $UID -g $GID $GERRIT_USER && \
- chown -R $GERRIT_USER $GERRIT_SITE $USER_HOME
+COPY artifacts /tmp/
+RUN cp /tmp/task.jar "$GERRIT_SITE/plugins/task.jar"
+RUN { [ -e /tmp/gerrit.war ] && cp /tmp/gerrit.war "$GERRIT_SITE/bin/gerrit.war" ; } || true
-USER $GERRIT_USER
-ENTRYPOINT ["/docker/gerrit_start.sh"]
+USER gerrit
diff --git a/test/docker/run.sh b/test/docker/run.sh
index 71b8a55..9c1f5d9 100755
--- a/test/docker/run.sh
+++ b/test/docker/run.sh
@@ -20,18 +20,24 @@
}
usage() { # [error_message]
- local prog=$(basename "$0")
+ local prog=$(basename -- "$0")
cat <<-EOF
Usage:
- "$prog" --gerrit-war|-g <Gerrit WAR URL or file path> \
- --task-plugin-jar|-t <task plugin URL or file path>
+ $prog [--task-plugin-jar|-t <FILE_PATH>] [--gerrit-war|-g <FILE_PATH>]
+ This tool runs the plugin functional tests in a Docker environment built
+ from the gerritcodereview/gerrit base Docker image.
+
+ The task plugin JAR and optionally a Gerrit WAR are expected to be in the
+ $ARTIFACTS dir;
+ however, the --task-plugin-jar and --gerrit-war switches may be used as
+ helpers to specify which files to copy there.
+
+ Options:
--help|-h
- --gerrit-war|-g gerrit WAR URL (or) the file path in local workspace
- eg: file:///path/to/source/file
- --task-plugin-jar|-t task plugin JAR URL (or) the file path in local workspace
- eg: file:///path/to/source/file
+ --gerrit-war|-g path to Gerrit WAR file
+ --task-plugin-jar|-t path to task plugin JAR file
EOF
@@ -44,26 +50,13 @@
docker-compose --version > /dev/null || die "docker-compose is not installed"
}
-fetch_artifact() { # source_location output_path
- curl --silent --fail --netrc "$1" --output "$2" --create-dirs || die "unable to fetch $1"
-}
-
-fetch_artifacts() {
- fetch_artifact "$GERRIT_WAR" "$ARTIFACTS/gerrit.war"
- fetch_artifact "$TASK_PLUGIN_JAR" "$ARTIFACTS/task.jar"
-}
-
build_images() {
- local build_args=(--build-arg GERRIT_WAR="/artifacts/gerrit.war" \
- --build-arg TASK_PLUGIN_JAR="/artifacts/task.jar" \
- --build-arg UID="$(id -u)" --build-arg GID="$(id -g)")
- docker-compose "${COMPOSE_ARGS[@]}" build "${build_args[@]}" --quiet
- rm -r "$ARTIFACTS"
+ docker-compose "${COMPOSE_ARGS[@]}" build --quiet
}
run_task_plugin_tests() {
docker-compose "${COMPOSE_ARGS[@]}" up --detach
- docker-compose "${COMPOSE_ARGS[@]}" exec --user=gerrit_admin run_tests \
+ docker-compose "${COMPOSE_ARGS[@]}" exec -T --user=gerrit_admin run_tests \
'/task/test/docker/run_tests/start.sh'
}
@@ -80,13 +73,18 @@
esac
shift
done
-[ -n "$GERRIT_WAR" ] || usage "'--gerrit-war' not set"
-[ -n "$TASK_PLUGIN_JAR" ] || usage "'--task-plugin-jar' not set"
PROJECT_NAME="task_$$"
COMPOSE_YAML="$MYDIR/docker-compose.yaml"
COMPOSE_ARGS=(--project-name "$PROJECT_NAME" -f "$COMPOSE_YAML")
check_prerequisite
-progress "fetching artifacts" fetch_artifacts
+mkdir -p -- "$ARTIFACTS"
+[ -n "$TASK_PLUGIN_JAR" ] && cp -f "$TASK_PLUGIN_JAR" "$ARTIFACTS/task.jar"
+if [ ! -e "$ARTIFACTS/task.jar" ] ; then
+ MISSING="Missing $ARTIFACTS/task.jar"
+ [ -n "$TASK_PLUGIN_JAR" ] && die "$MISSING, check for copy failure?"
+ usage "$MISSING, did you forget --task-plugin-jar?"
+fi
+[ -n "$GERRIT_WAR" ] && cp -f "$GERRIT_WAR" "$ARTIFACTS/gerrit.war"
progress "Building docker images" build_images
run_task_plugin_tests ; RESULT=$?
cleanup
diff --git a/test/invalid b/test/invalid
index 840c3da..adab1d1 100644
--- a/test/invalid
+++ b/test/invalid
@@ -179,6 +179,42 @@
{
"name" : "UNKNOWN",
"status" : "INVALID"
+ },
+ {
+ "applicable" : true,
+ "hasPass" : false,
+ "name" : "task (tasks-factory missing)",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "name" : "UNKNOWN",
+ "status" : "INVALID"
+ }
+ ]
+ },
+ {
+ "applicable" : true,
+ "hasPass" : false,
+ "name" : "task (names-factory type missing)",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "name" : "UNKNOWN",
+ "status" : "INVALID"
+ }
+ ]
+ },
+ {
+ "applicable" : true,
+ "hasPass" : false,
+ "name" : "task (names-factory type INVALID)",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "name" : "UNKNOWN",
+ "status" : "INVALID"
+ }
+ ]
}
]
},
@@ -315,6 +351,42 @@
{
"name" : "UNKNOWN",
"status" : "INVALID"
+ },
+ {
+ "applicable" : true,
+ "hasPass" : false,
+ "name" : "task (tasks-factory missing)",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "name" : "UNKNOWN",
+ "status" : "INVALID"
+ }
+ ]
+ },
+ {
+ "applicable" : true,
+ "hasPass" : false,
+ "name" : "task (names-factory type missing)",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "name" : "UNKNOWN",
+ "status" : "INVALID"
+ }
+ ]
+ },
+ {
+ "applicable" : true,
+ "hasPass" : false,
+ "name" : "task (names-factory type INVALID)",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "name" : "UNKNOWN",
+ "status" : "INVALID"
+ }
+ ]
}
]
}
diff --git a/test/invalid-applicable b/test/invalid-applicable
index 8e52fa0..449e595 100644
--- a/test/invalid-applicable
+++ b/test/invalid-applicable
@@ -126,6 +126,39 @@
{
"name" : "UNKNOWN",
"status" : "INVALID"
+ },
+ {
+ "hasPass" : false,
+ "name" : "task (tasks-factory missing)",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "name" : "UNKNOWN",
+ "status" : "INVALID"
+ }
+ ]
+ },
+ {
+ "hasPass" : false,
+ "name" : "task (names-factory type missing)",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "name" : "UNKNOWN",
+ "status" : "INVALID"
+ }
+ ]
+ },
+ {
+ "hasPass" : false,
+ "name" : "task (names-factory type INVALID)",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "name" : "UNKNOWN",
+ "status" : "INVALID"
+ }
+ ]
}
]
}
diff --git a/test/preview b/test/preview
index e2706ea..e74d35d 100644
--- a/test/preview
+++ b/test/preview
@@ -135,6 +135,42 @@
{
"name" : "UNKNOWN",
"status" : "INVALID"
+ },
+ {
+ "applicable" : true,
+ "hasPass" : false,
+ "name" : "task (tasks-factory missing)",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "name" : "UNKNOWN",
+ "status" : "INVALID"
+ }
+ ]
+ },
+ {
+ "applicable" : true,
+ "hasPass" : false,
+ "name" : "task (names-factory type missing)",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "name" : "UNKNOWN",
+ "status" : "INVALID"
+ }
+ ]
+ },
+ {
+ "applicable" : true,
+ "hasPass" : false,
+ "name" : "task (names-factory type INVALID)",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "name" : "UNKNOWN",
+ "status" : "INVALID"
+ }
+ ]
}
]
},
@@ -318,6 +354,42 @@
{
"name" : "UNKNOWN",
"status" : "INVALID"
+ },
+ {
+ "applicable" : true,
+ "hasPass" : false,
+ "name" : "task (tasks-factory missing)",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "name" : "UNKNOWN",
+ "status" : "INVALID"
+ }
+ ]
+ },
+ {
+ "applicable" : true,
+ "hasPass" : false,
+ "name" : "task (names-factory type missing)",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "name" : "UNKNOWN",
+ "status" : "INVALID"
+ }
+ ]
+ },
+ {
+ "applicable" : true,
+ "hasPass" : false,
+ "name" : "task (names-factory type INVALID)",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "name" : "UNKNOWN",
+ "status" : "INVALID"
+ }
+ ]
}
]
}
diff --git a/test/preview.invalid b/test/preview.invalid
index 86d12f1..056bb80 100644
--- a/test/preview.invalid
+++ b/test/preview.invalid
@@ -135,6 +135,42 @@
{
"name" : "UNKNOWN",
"status" : "INVALID"
+ },
+ {
+ "applicable" : true,
+ "hasPass" : false,
+ "name" : "task (tasks-factory missing)",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "name" : "UNKNOWN",
+ "status" : "INVALID"
+ }
+ ]
+ },
+ {
+ "applicable" : true,
+ "hasPass" : false,
+ "name" : "task (names-factory type missing)",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "name" : "UNKNOWN",
+ "status" : "INVALID"
+ }
+ ]
+ },
+ {
+ "applicable" : true,
+ "hasPass" : false,
+ "name" : "task (names-factory type INVALID)",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "name" : "UNKNOWN",
+ "status" : "INVALID"
+ }
+ ]
}
]
},
@@ -271,6 +307,42 @@
{
"name" : "UNKNOWN",
"status" : "INVALID"
+ },
+ {
+ "applicable" : true,
+ "hasPass" : false,
+ "name" : "task (tasks-factory missing)",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "name" : "UNKNOWN",
+ "status" : "INVALID"
+ }
+ ]
+ },
+ {
+ "applicable" : true,
+ "hasPass" : false,
+ "name" : "task (names-factory type missing)",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "name" : "UNKNOWN",
+ "status" : "INVALID"
+ }
+ ]
+ },
+ {
+ "applicable" : true,
+ "hasPass" : false,
+ "name" : "task (names-factory type INVALID)",
+ "status" : "WAITING",
+ "subTasks" : [
+ {
+ "name" : "UNKNOWN",
+ "status" : "INVALID"
+ }
+ ]
}
]
}
diff --git a/tools/playbooks/install_docker.yaml b/tools/playbooks/install_docker.yaml
new file mode 100644
index 0000000..89cf315
--- /dev/null
+++ b/tools/playbooks/install_docker.yaml
@@ -0,0 +1,8 @@
+- hosts: all
+ roles:
+ - name: ensure-docker
+ tasks:
+ - name: Install compose
+ shell: |
+ sudo curl -L "https://github.com/docker/compose/releases/download/1.29.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
+ sudo chmod +x /usr/local/bin/docker-compose