Add --task--all flag to the query command

This option causes the query to append task info for all tasks visible
to the current user to the output of each change, whether the tasks are
applicable or not. When this flag is used, an additional 'applicable'
property is included in each task output to indicate whether the task
actually met its applicability criteria or not.

Change-Id: I9cfe4bbf1c46221c63d87af4f7cc309e141f64fa
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/Modules.java b/src/main/java/com/googlesource/gerrit/plugins/task/Modules.java
index 6650221..061acb7 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/task/Modules.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/task/Modules.java
@@ -47,7 +47,10 @@
   }
 
   public static class MyOptions implements DynamicBean {
-    @Option(name = "--applicable", usage = "Include applicable tasks")
-    public boolean include = false;
+    @Option(name = "--all", usage = "Include all visible tasks in the output")
+    public boolean all = false;
+
+    @Option(name = "--applicable", usage = "Include only applicable tasks in the output")
+    public boolean onlyApplicable = false;
   }
 }
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 68c1a3f..a4af8b5 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/task/TaskAttributeFactory.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/task/TaskAttributeFactory.java
@@ -45,6 +45,7 @@
 
   public enum Status {
     INVALID,
+    UNKNOWN,
     WAITING,
     READY,
     PASS,
@@ -52,6 +53,7 @@
   }
 
   public static class TaskAttribute {
+    public Boolean applicable;
     public String hint;
     public Boolean inProgress;
     public String name;
@@ -74,6 +76,8 @@
   protected final TaskConfigFactory taskFactory;
   protected final ChangeQueryBuilder cqb;
 
+  protected Modules.MyOptions options;
+
   @Inject
   public TaskAttributeFactory(
       AccountResolver accountResolver,
@@ -88,8 +92,8 @@
 
   @Override
   public PluginDefinedInfo create(ChangeData c, ChangeQueryProcessor qp, String plugin) {
-    Modules.MyOptions options = (Modules.MyOptions) qp.getDynamicBean(plugin);
-    if (options != null && options.include) {
+    options = (Modules.MyOptions) qp.getDynamicBean(plugin);
+    if (options.all || options.onlyApplicable) {
       try {
         return createWithExceptions(c);
       } catch (OrmException e) {
@@ -132,14 +136,25 @@
       List<TaskAttribute> tasks, ChangeData c, LinkedList<Task> path, Task def)
       throws OrmException {
     try {
-      if (match(c, def.applicable)) {
+      boolean applicable = match(c, def.applicable);
+      if (!applicable && !def.isVisible && !options.onlyApplicable) {
+        tasks.add(unknown());
+        return;
+      }
+
+      if (applicable || !options.onlyApplicable) {
         TaskAttribute task = new TaskAttribute(def.name);
-        if (def.inProgress != null) {
-          task.inProgress = match(c, def.inProgress);
-        }
         task.subTasks = getSubTasks(c, path, def);
         task.status = getStatus(c, def, task);
-        if (task.status != null) { // task still applies
+        boolean groupApplicable = task.status != null;
+
+        if (groupApplicable || !options.onlyApplicable) {
+          if (!options.onlyApplicable) {
+            task.applicable = applicable;
+          }
+          if (def.inProgress != null) {
+            task.inProgress = match(c, def.inProgress);
+          }
           task.hint = getHint(task.status, def);
           tasks.add(task);
         }
@@ -187,11 +202,17 @@
   protected static TaskAttribute invalid() {
     // For security reasons, do not expose the task name without knowing
     // the visibility which is derived from its applicability.
-    TaskAttribute a = new TaskAttribute("UNKNOWN");
+    TaskAttribute a = unknown();
     a.status = Status.INVALID;
     return a;
   }
 
+  protected static TaskAttribute unknown() {
+    TaskAttribute a = new TaskAttribute("UNKNOWN");
+    a.status = Status.UNKNOWN;
+    return a;
+  }
+
   protected List<Task> getRootTasks() throws ConfigInvalidException, IOException {
     return taskFactory.getRootConfig().getRootTasks();
   }
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 c2f7648..204a636 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/task/TaskConfig.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/task/TaskConfig.java
@@ -43,7 +43,10 @@
     public List<String> subTasksExternals;
     public List<String> subTasksFiles;
 
-    public Task(SubSection s) {
+    public boolean isVisible;
+
+    public Task(SubSection s, boolean isVisible) {
+      this.isVisible = isVisible;
       applicable = getString(s, KEY_APPLICABLE, null);
       fail = getString(s, KEY_FAIL, null);
       failHint = getString(s, KEY_FAIL_HINT, null);
@@ -85,8 +88,11 @@
   protected static final String KEY_SUBTASKS_FILE = "subtasks-file";
   protected static final String KEY_USER = "user";
 
-  public TaskConfig(Branch.NameKey branch, String fileName) {
+  public boolean isVisible;
+
+  public TaskConfig(Branch.NameKey branch, String fileName, boolean isVisible) {
     super(branch, fileName);
+    this.isVisible = isVisible;
   }
 
   public List<Task> getRootTasks() {
@@ -101,7 +107,7 @@
     List<Task> tasks = new ArrayList<>();
     // No need to get a task with no name (what would we call it?)
     for (String task : cfg.getSubsections(type)) {
-      tasks.add(new Task(new SubSection(type, task)));
+      tasks.add(new Task(new SubSection(type, task), isVisible));
     }
     return tasks;
   }
@@ -116,7 +122,7 @@
   }
 
   public Task getTask(String name) {
-    return new Task(new SubSection(SECTION_TASK, name));
+    return new Task(new SubSection(SECTION_TASK, name), isVisible);
   }
 
   public External getExternal(String name) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/TaskConfigFactory.java b/src/main/java/com/googlesource/gerrit/plugins/task/TaskConfigFactory.java
index f258bec..1d9be90 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/task/TaskConfigFactory.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/task/TaskConfigFactory.java
@@ -14,10 +14,15 @@
 
 package com.googlesource.gerrit.plugins.task;
 
+import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.reviewdb.client.Branch;
 import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.config.AllProjectsName;
 import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gerrit.server.permissions.RefPermission;
 import com.google.inject.Inject;
 import java.io.IOException;
 import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -32,12 +37,21 @@
   protected static final String DEFAULT = "task" + EXTENSION;
 
   protected final GitRepositoryManager gitMgr;
+  protected final PermissionBackend permissionBackend;
+
+  protected final CurrentUser user;
   protected final AllProjectsName allProjects;
 
   @Inject
-  protected TaskConfigFactory(AllProjectsName allProjects, GitRepositoryManager gitMgr) {
+  protected TaskConfigFactory(
+      AllProjectsName allProjects,
+      GitRepositoryManager gitMgr,
+      PermissionBackend permissionBackend,
+      CurrentUser user) {
     this.allProjects = allProjects;
     this.gitMgr = gitMgr;
+    this.permissionBackend = permissionBackend;
+    this.user = user;
   }
 
   public TaskConfig getRootConfig() throws ConfigInvalidException, IOException {
@@ -50,7 +64,7 @@
 
   public TaskConfig getTaskConfig(Branch.NameKey branch, String fileName)
       throws ConfigInvalidException, IOException {
-    TaskConfig cfg = new TaskConfig(branch, fileName);
+    TaskConfig cfg = new TaskConfig(branch, fileName, canRead(branch));
     Project.NameKey project = branch.getParentKey();
     try (Repository git = gitMgr.openRepository(project)) {
       cfg.load(project, git);
@@ -62,4 +76,15 @@
     }
     return cfg;
   }
+
+  public boolean canRead(Branch.NameKey branch) {
+    try {
+      PermissionBackend.ForProject permissions =
+          permissionBackend.user(user).project(branch.getParentKey());
+      permissions.ref(branch.get()).check(RefPermission.READ);
+      return true;
+    } catch (AuthException | PermissionBackendException e) {
+      return false;
+    }
+  }
 }
diff --git a/src/main/resources/Documentation/task.md b/src/main/resources/Documentation/task.md
index 96fd5b6..e4c2abf 100644
--- a/src/main/resources/Documentation/task.md
+++ b/src/main/resources/Documentation/task.md
@@ -264,8 +264,23 @@
 
 Change Query Output
 -------------------
-Changes which have tasks applicable to them will have a "task" section
-which will include applicable tasks for the change added to their output.
+It is possible to add a task section to the query output of changes using
+the task plugin switches.  The following switches are available:
+
+**\-\-@PLUGIN@\-\-applicable**
+
+This switch is meant to be used to determine the state of applicable
+tasks for each change, it outputs applicable tasks for a change.
+
+**\-\-@PLUGIN@\-\-all**
+
+This switch is meant as a debug switch, it outputs all tasks visible to the
+calling user, whether they apply to a change or not.  When this flag is used,
+an additional 'applicable' property is included in each task output to
+indicate whether the task actually met its applicability criteria or not.
+
+When tasks are appended to changes, they will have a "task" section under
+the plugins section like below:
 
 ```
   $ ssh -x -p 29418 example.com gerrit query change:123
diff --git a/src/main/resources/Documentation/task_states.md b/src/main/resources/Documentation/task_states.md
index ad58939..953693a 100644
--- a/src/main/resources/Documentation/task_states.md
+++ b/src/main/resources/Documentation/task_states.md
@@ -109,6 +109,18 @@
   applicable = is:open
   subtasks-file = invalids.config
 
+[root "Root NA Pass"]
+  applicable = -is:open
+  pass = True
+
+[root "Root NA Fail"]
+  applicable = -is:open
+  fail = True
+
+[root "NA INVALIDS"]
+  applicable = -is:open
+  subtasks-file = invalids.config
+
 [task "Subtask FAIL"]
   applicable = is:open
   fail = is:open
diff --git a/test/all b/test/all
new file mode 100644
index 0000000..5fc731c
--- /dev/null
+++ b/test/all
@@ -0,0 +1,403 @@
+   "plugins" : [
+      {
+         "name" : "task",
+         "roots" : [
+            {
+               "applicable" : false,
+               "name" : "Root N/A",
+               "status" : "INVALID"
+            },
+            {
+               "applicable" : true,
+               "name" : "Root PASS",
+               "status" : "PASS"
+            },
+            {
+               "applicable" : true,
+               "name" : "Root FAIL",
+               "status" : "FAIL"
+            },
+            {
+               "applicable" : true,
+               "name" : "Root straight PASS",
+               "status" : "PASS"
+            },
+            {
+               "applicable" : true,
+               "name" : "Root straight FAIL",
+               "status" : "FAIL"
+            },
+            {
+               "applicable" : true,
+               "name" : "Root PASS-fail",
+               "status" : "PASS"
+            },
+            {
+               "applicable" : true,
+               "name" : "Root pass-FAIL",
+               "status" : "FAIL"
+            },
+            {
+               "applicable" : true,
+               "name" : "Root grouping PASS (subtask PASS)",
+               "status" : "PASS",
+               "subTasks" : [
+                  {
+                     "applicable" : true,
+                     "name" : "Subtask PASS",
+                     "status" : "PASS"
+                  }
+               ]
+            },
+            {
+               "applicable" : true,
+               "name" : "Root grouping WAITING (subtask READY)",
+               "status" : "WAITING",
+               "subTasks" : [
+                  {
+                     "applicable" : true,
+                     "name" : "Subtask READY",
+                     "status" : "READY",
+                     "subTasks" : [
+                        {
+                           "applicable" : true,
+                           "name" : "Subtask PASS",
+                           "status" : "PASS"
+                        }
+                     ]
+                  }
+               ]
+            },
+            {
+               "applicable" : true,
+               "name" : "Root grouping WAITING (subtask FAIL)",
+               "status" : "WAITING",
+               "subTasks" : [
+                  {
+                     "applicable" : true,
+                     "name" : "Subtask FAIL",
+                     "status" : "FAIL"
+                  }
+               ]
+            },
+            {
+               "applicable" : true,
+               "name" : "Root grouping NA (subtask NA)",
+               "status" : "WAITING",
+               "subTasks" : [
+                  {
+                     "applicable" : false,
+                     "name" : "Subtask NA",
+                     "status" : "INVALID"
+                  }
+               ]
+            },
+            {
+               "applicable" : true,
+               "hint" : "You must now run the ready task",
+               "name" : "Root READY (subtask PASS)",
+               "status" : "READY",
+               "subTasks" : [
+                  {
+                     "applicable" : true,
+                     "name" : "Subtask PASS",
+                     "status" : "PASS"
+                  }
+               ]
+            },
+            {
+               "applicable" : true,
+               "name" : "Root WAITING (subtask READY)",
+               "status" : "WAITING",
+               "subTasks" : [
+                  {
+                     "applicable" : true,
+                     "name" : "Subtask READY",
+                     "status" : "READY",
+                     "subTasks" : [
+                        {
+                           "applicable" : true,
+                           "name" : "Subtask PASS",
+                           "status" : "PASS"
+                        }
+                     ]
+                  }
+               ]
+            },
+            {
+               "applicable" : true,
+               "name" : "Root WAITING (subtask FAIL)",
+               "status" : "WAITING",
+               "subTasks" : [
+                  {
+                     "applicable" : true,
+                     "name" : "Subtask FAIL",
+                     "status" : "FAIL"
+                  }
+               ]
+            },
+            {
+               "applicable" : true,
+               "inProgress" : true,
+               "name" : "Root IN PROGRESS",
+               "status" : "READY"
+            },
+            {
+               "applicable" : true,
+               "inProgress" : false,
+               "name" : "Root NOT IN PROGRESS",
+               "status" : "READY"
+            },
+            {
+               "applicable" : true,
+               "name" : "Subtasks File",
+               "status" : "WAITING",
+               "subTasks" : [
+                  {
+                     "applicable" : true,
+                     "name" : "file task/common.config PASS",
+                     "status" : "PASS"
+                  },
+                  {
+                     "applicable" : true,
+                     "name" : "file task/common.config FAIL",
+                     "status" : "FAIL"
+                  }
+               ]
+            },
+            {
+               "applicable" : true,
+               "name" : "Subtasks File (Missing)",
+               "status" : "WAITING",
+               "subTasks" : [
+                  {
+                     "applicable" : true,
+                     "name" : "file task/common.config PASS",
+                     "status" : "PASS"
+                  },
+                  {
+                     "applicable" : true,
+                     "name" : "file task/common.config FAIL",
+                     "status" : "FAIL"
+                  }
+               ]
+            },
+            {
+               "applicable" : true,
+               "name" : "Subtasks External",
+               "status" : "WAITING",
+               "subTasks" : [
+                  {
+                     "applicable" : true,
+                     "name" : "userfile task/special.config PASS",
+                     "status" : "PASS"
+                  },
+                  {
+                     "applicable" : true,
+                     "name" : "userfile task/special.config FAIL",
+                     "status" : "FAIL"
+                  }
+               ]
+            },
+            {
+               "applicable" : true,
+               "name" : "Subtasks External (Missing)",
+               "status" : "WAITING",
+               "subTasks" : [
+                  {
+                     "name" : "UNKNOWN",
+                     "status" : "INVALID"
+                  },
+                  {
+                     "applicable" : true,
+                     "name" : "userfile task/special.config PASS",
+                     "status" : "PASS"
+                  },
+                  {
+                     "applicable" : true,
+                     "name" : "userfile task/special.config FAIL",
+                     "status" : "FAIL"
+                  }
+               ]
+            },
+            {
+               "applicable" : true,
+               "name" : "Subtasks External (User Missing)",
+               "status" : "WAITING",
+               "subTasks" : [
+                  {
+                     "name" : "UNKNOWN",
+                     "status" : "INVALID"
+                  },
+                  {
+                     "applicable" : true,
+                     "name" : "userfile task/special.config PASS",
+                     "status" : "PASS"
+                  },
+                  {
+                     "applicable" : true,
+                     "name" : "userfile task/special.config FAIL",
+                     "status" : "FAIL"
+                  }
+               ]
+            },
+            {
+               "applicable" : true,
+               "name" : "Subtasks External (File Missing)",
+               "status" : "WAITING",
+               "subTasks" : [
+                  {
+                     "applicable" : true,
+                     "name" : "userfile task/special.config PASS",
+                     "status" : "PASS"
+                  },
+                  {
+                     "applicable" : true,
+                     "name" : "userfile task/special.config FAIL",
+                     "status" : "FAIL"
+                  }
+               ]
+            },
+            {
+               "applicable" : true,
+               "name" : "INVALIDS",
+               "status" : "WAITING",
+               "subTasks" : [
+                  {
+                     "applicable" : true,
+                     "name" : "No PASS criteria",
+                     "status" : "INVALID"
+                  },
+                  {
+                     "applicable" : true,
+                     "name" : "WAITING (subtask INVALID)",
+                     "status" : "WAITING",
+                     "subTasks" : [
+                        {
+                           "applicable" : true,
+                           "name" : "Subtask INVALID",
+                           "status" : "INVALID"
+                        }
+                     ]
+                  },
+                  {
+                     "applicable" : true,
+                     "name" : "WAITING (subtask missing)",
+                     "status" : "WAITING",
+                     "subTasks" : [
+                        {
+                           "applicable" : true,
+                           "name" : "MISSING",
+                           "status" : "INVALID"
+                        }
+                     ]
+                  },
+                  {
+                     "applicable" : true,
+                     "name" : "Grouping WAITING (subtask INVALID)",
+                     "status" : "WAITING",
+                     "subTasks" : [
+                        {
+                           "applicable" : true,
+                           "name" : "Subtask INVALID",
+                           "status" : "INVALID"
+                        }
+                     ]
+                  },
+                  {
+                     "applicable" : true,
+                     "name" : "Grouping WAITING (subtask missing)",
+                     "status" : "WAITING",
+                     "subTasks" : [
+                        {
+                           "applicable" : true,
+                           "name" : "MISSING",
+                           "status" : "INVALID"
+                        }
+                     ]
+                  },
+                  {
+                     "applicable" : true,
+                     "name" : "Subtask INVALID",
+                     "status" : "INVALID"
+                  }
+               ]
+            },
+            {
+               "applicable" : false,
+               "name" : "Root NA Pass",
+               "status" : "PASS"
+            },
+            {
+               "applicable" : false,
+               "name" : "Root NA Fail",
+               "status" : "FAIL"
+            },
+            {
+               "applicable" : false,
+               "name" : "NA INVALIDS",
+               "status" : "WAITING",
+               "subTasks" : [
+                  {
+                     "applicable" : true,
+                     "name" : "No PASS criteria",
+                     "status" : "INVALID"
+                  },
+                  {
+                     "applicable" : true,
+                     "name" : "WAITING (subtask INVALID)",
+                     "status" : "WAITING",
+                     "subTasks" : [
+                        {
+                           "applicable" : true,
+                           "name" : "Subtask INVALID",
+                           "status" : "INVALID"
+                        }
+                     ]
+                  },
+                  {
+                     "applicable" : true,
+                     "name" : "WAITING (subtask missing)",
+                     "status" : "WAITING",
+                     "subTasks" : [
+                        {
+                           "applicable" : true,
+                           "name" : "MISSING",
+                           "status" : "INVALID"
+                        }
+                     ]
+                  },
+                  {
+                     "applicable" : true,
+                     "name" : "Grouping WAITING (subtask INVALID)",
+                     "status" : "WAITING",
+                     "subTasks" : [
+                        {
+                           "applicable" : true,
+                           "name" : "Subtask INVALID",
+                           "status" : "INVALID"
+                        }
+                     ]
+                  },
+                  {
+                     "applicable" : true,
+                     "name" : "Grouping WAITING (subtask missing)",
+                     "status" : "WAITING",
+                     "subTasks" : [
+                        {
+                           "applicable" : true,
+                           "name" : "MISSING",
+                           "status" : "INVALID"
+                        }
+                     ]
+                  },
+                  {
+                     "applicable" : true,
+                     "name" : "Subtask INVALID",
+                     "status" : "INVALID"
+                  }
+               ]
+            }
+         ]
+      }
+   ],
diff --git a/test/check_task_statuses.sh b/test/check_task_statuses.sh
index 5d58a6e..410603e 100755
--- a/test/check_task_statuses.sh
+++ b/test/check_task_statuses.sh
@@ -4,6 +4,25 @@
 # All-Users.git - refs/users/self must already exist
 # All-Projects.git - must have 'Push' rights on refs/meta/config
 
+# ---- TEST RESULTS ----
+result() { # test [error_message]
+    local result=$?
+    if [ $result -eq 0 ] ; then
+        echo "PASSED - $1 test"
+    else
+        echo "*** FAILED *** - $1 test"
+        RESULT=$result
+        [ $# -gt 1 ] && echo "$2"
+    fi
+}
+
+# --------
+
+# Run a test setup command quietly, exit on failure
+q_setup() { # cmd [args...]
+  local out ; out=$("$@" 2>&1) || { echo "$out" ; exit ; }
+}
+
 example() { # example_num
     awk '/```/{Q++;E=(Q+1)/2};E=='"$1" < "$DOC_STATES" | grep -v '```'
 }
@@ -29,13 +48,27 @@
 }
 
 query() { # query
-    ssh -x -p "$PORT" "$SERVER" gerrit query "$1" \
-            --format json --task--applicable| head -1 | python -c "import sys, json; \
+    ssh -x -p "$PORT" "$SERVER" gerrit query "$@" \
+            --format json | head -1 | python -c "import sys, json; \
             print json.dumps(json.loads(sys.stdin.read()), indent=3, \
             separators=(',', ' : '), sort_keys=True)"
 }
 
-query_plugins() { query "$1" | awk '$0=="   \"plugins\" : [",$0=="   ],"' ; }
+query_plugins() { query "$@" | awk '$0=="   \"plugins\" : [",$0=="   ],"' ; }
+
+test_tasks() { # name expected_file task_args...
+    local name=$1 expected=$2 ; shift 2
+    local output=$STATUSES.$name
+
+    query_plugins "$@" > "$output"
+    out=$(diff "$expected" "$output")
+    result "$name" "$out"
+}
+
+test_file() { # name task_args...
+    local name=$1 ; shift
+    test_tasks "$name" "$MYDIR/$name" "$@"
+}
 
 MYDIR=$(dirname "$0")
 DOCS=$MYDIR/.././src/main/resources/Documentation
@@ -69,8 +102,8 @@
 
 
 mkdir -p "$OUT"
-setup_repo "$ALL" "$REMOTE_ALL" "$REF_ALL"
-setup_repo "$USERS" "$REMOTE_USERS" "$REF_USERS"
+q_setup setup_repo "$ALL" "$REMOTE_ALL" "$REF_ALL"
+q_setup setup_repo "$USERS" "$REMOTE_USERS" "$REF_USERS"
 
 mkdir -p "$ALL_TASKS" "$USER_TASKS"
 
@@ -79,10 +112,11 @@
 example 3 > "$INVALIDS_CFG"
 example 4 > "$USER_SPECIAL_CFG"
 
-update_repo "$ALL" "$REMOTE_ALL" "$REF_ALL"
-update_repo "$USERS" "$REMOTE_USERS" "$REF_USERS"
+q_setup update_repo "$ALL" "$REMOTE_ALL" "$REF_ALL"
+q_setup update_repo "$USERS" "$REMOTE_USERS" "$REF_USERS"
 
 example 5 |tail -n +5| awk 'NR>1{print P};{P=$0}' > "$EXPECTED"
 
-query_plugins "status:open limit:1" > "$STATUSES"
-diff "$EXPECTED" "$STATUSES"
+query="status:open limit:1"
+test_tasks statuses "$EXPECTED" --task--applicable "$query"
+test_file all --task--all "$query"