Support loading subtasks from a different file This is an incremental step to completely support moving non root tasks away from task.config in All-Projects repo. Various syntaxes were introduced to import a single task from other files in same ref and are described in the documentation. Originally-Authored-By: Adithya Chakilam Change-Id: I794e6a2b6d8c93e92e633c12823c956128e2ffe6
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 dd6ab9d..5240ccc 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/task/TaskConfigFactory.java +++ b/src/main/java/com/googlesource/gerrit/plugins/task/TaskConfigFactory.java
@@ -37,9 +37,6 @@ public class TaskConfigFactory { private static final FluentLogger log = FluentLogger.forEnclosingClass(); - protected static final String EXTENSION = ".config"; - protected static final String DEFAULT = "task" + EXTENSION; - protected final GitRepositoryManager gitMgr; protected final PermissionBackend permissionBackend; @@ -62,7 +59,7 @@ } public TaskConfig getRootConfig() throws ConfigInvalidException, IOException { - return getTaskConfig(FileKey.create(getRootBranch(), DEFAULT)); + return getTaskConfig(FileKey.create(getRootBranch(), TaskFileConstants.TASK_CFG)); } public void masquerade(PatchSetArgument psa) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/TaskExpression.java b/src/main/java/com/googlesource/gerrit/plugins/task/TaskExpression.java index 5a61d29..e47e880 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/task/TaskExpression.java +++ b/src/main/java/com/googlesource/gerrit/plugins/task/TaskExpression.java
@@ -23,16 +23,30 @@ * A TaskExpression represents a config string pointing to an expression which includes zero or more * task names separated by a '|', and potentially termintated by a '|'. If the expression is not * terminated by a '|' it indicates that task resolution of at least one task is required. Task - * selection priority is from left to right. This can be expressed as: <code> - * EXPR = [ TASK_NAME '|' ] TASK_NAME [ '|' ]</code> + * selection priority is from left to right. This can be expressed as: + * + * <pre> + * TASK_REF = [ [ TASK_FILE_PATH ] '^' ] TASK_NAME + * TASK_EXPR = TASK_REF [ WHITE_SPACE * '|' [ WHITE_SPACE * TASK_EXPR ] ] + * </pre> * * <p>Example expressions to prioritized names and requirements: * * <ul> - * <li><code> "simple" -> ("simple") required</code> - * <li><code> "world | peace" -> ("world", "peace") required</code> - * <li><code> "shadenfreud |" -> ("shadenfreud") optional</code> - * <li><code> "foo | bar |" -> ("foo", "bar") optional</code> + * <li> + * <pre> "simple" -> ("simple") required</pre> + * <li> + * <pre> "world | peace" -> ("world", "peace") required</pre> + * <li> + * <pre> "shadenfreud |" -> ("shadenfreud") optional</pre> + * <li> + * <pre> "foo | bar |" -> ("foo", "bar") optional</pre> + * <li> + * <pre> "/foo^bar | baz |" -> ("task/foo^bar", "baz") optional</pre> + * <li> + * <pre> "foo^bar | baz |" -> ("cur_dir/foo^bar", "baz") optional</pre> + * <li> + * <pre> "^bar | baz |" -> ("task.config^bar", "baz") optional</pre> * </ul> */ public class TaskExpression implements Iterable<TaskKey> { @@ -72,7 +86,7 @@ throw new NoSuchElementException("No more names, yet expression was not optional"); } hasNext = null; - return TaskKey.create(key.file(), m.group(1).trim()); + return new TaskReference(key.file(), m.group(1)).getTaskKey(); } }; }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/TaskExpressionKey.java b/src/main/java/com/googlesource/gerrit/plugins/task/TaskExpressionKey.java index 0a05b2e..6fcd30d 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/task/TaskExpressionKey.java +++ b/src/main/java/com/googlesource/gerrit/plugins/task/TaskExpressionKey.java
@@ -15,6 +15,7 @@ package com.googlesource.gerrit.plugins.task; import com.google.auto.value.AutoValue; +import com.google.gerrit.entities.BranchNameKey; /** A key for TaskExpression. */ @AutoValue @@ -23,6 +24,10 @@ return new AutoValue_TaskExpressionKey(file, expression); } + public BranchNameKey branch() { + return file().branch(); + } + public abstract FileKey file(); public abstract String expression();
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/TaskFileConstants.java b/src/main/java/com/googlesource/gerrit/plugins/task/TaskFileConstants.java new file mode 100644 index 0000000..904c933 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/task/TaskFileConstants.java
@@ -0,0 +1,20 @@ +// Copyright (C) 2022 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 final class TaskFileConstants { + public static final String TASK_DIR = "task"; + public static final String TASK_CFG = "task.config"; +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/TaskReference.java b/src/main/java/com/googlesource/gerrit/plugins/task/TaskReference.java new file mode 100644 index 0000000..2463a0c --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/task/TaskReference.java
@@ -0,0 +1,67 @@ +// 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; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.NoSuchElementException; + +/** This class is used by TaskExpression to decode the task from task reference. */ +public class TaskReference { + protected FileKey currentFile; + protected String reference; + + public TaskReference(FileKey originalFile, String reference) { + currentFile = originalFile; + this.reference = reference.trim(); + if (reference.isEmpty()) { + throw new NoSuchElementException(); + } + } + + public TaskKey getTaskKey() { + String[] referenceSplit = reference.split("\\^"); + switch (referenceSplit.length) { + case 1: + return TaskKey.create(currentFile, referenceSplit[0]); + case 2: + return TaskKey.create(getFileKey(referenceSplit[0]), referenceSplit[1]); + default: + throw new NoSuchElementException(); + } + } + + protected FileKey getFileKey(String referenceFile) { + return FileKey.create(currentFile.branch(), getFile(referenceFile)); + } + + protected String getFile(String referencedFile) { + if (referencedFile.isEmpty()) { // Implies a task from root task.config + return TaskFileConstants.TASK_CFG; + } + + if (referencedFile.startsWith("/")) { // Implies absolute path to the config is provided + return Paths.get(TaskFileConstants.TASK_DIR, referencedFile).toString(); + } + + // Implies a relative path to sub-directory + Path dir = Paths.get(currentFile.file()).getParent(); + if (dir == null) { // Relative path in root task.config should refer to files under task dir + return Paths.get(TaskFileConstants.TASK_DIR, referencedFile).toString(); + } else { + return Paths.get(dir.toString(), referencedFile).toString(); + } + } +}
diff --git a/src/main/resources/Documentation/task.md b/src/main/resources/Documentation/task.md index 85fd25a..8a75c0e 100644 --- a/src/main/resources/Documentation/task.md +++ b/src/main/resources/Documentation/task.md
@@ -166,8 +166,8 @@ from the current task if they redefined in the current task. Attributes which are lists (such as subtasks) or maps (such as properties), will be preloaded by the preload-task and then extended with the attributes from the -current task. See [Optional Tasks](#optional_tasks) for how to define optional -preload-tasks. +current task. See [Task Expression](task_expression.html) for how to define +optional preload-tasks. Example: ``` @@ -178,8 +178,8 @@ : This key lists the name of a subtask of the current task. This key may be used several times in a task section to define more than one subtask for a -particular task. See [Optional Tasks](#optional_tasks) for how to define -optional subtasks. +particular task. See [Task Expression](task_expression.html) for how to define +subtasks. Example: @@ -323,27 +323,6 @@ fail = label:code-review-2 ``` -<a id="optional_tasks"/> -Optional Tasks --------------- -To define a task that may not exist and that will not cause the task referencing -it to be INVALID, follow the task name with pipe (`|`) character. This feature -is particularly useful when a property is used in the task name. - -``` - preload-task = Optional Subtask {$_name} | -``` - -To define an alternate task to load when an optional task does not exist, -list the alterante task name after the pipe (`|`) character. This feature -may be chained together as many times as needed. - -``` - subtask = Optional Subtask {$_name} | - Backup Optional Subtask {$_name} Backup | - Default Subtask # Must exist if the above two don't! -``` - <a id="tasks_factory"/> Tasks-Factory -------------
diff --git a/src/main/resources/Documentation/task_expression.md b/src/main/resources/Documentation/task_expression.md new file mode 100644 index 0000000..f9febcf --- /dev/null +++ b/src/main/resources/Documentation/task_expression.md
@@ -0,0 +1,115 @@ +<a id="task_expression"/> +Task Expression +-------------- + +The tasks in subtask and preload-task can be defined using a Task Expression. +Each task expression can contain multiple tasks (all can be optional). Tasks +from other files can be referenced using [Task Reference](#task_reference). + +``` +TASK_EXPR = TASK_REFERENCE [ WHITE_SPACE * '|' [ WHITE_SPACE * TASK_EXPR ] ] +``` + +To define a task that may not exist and that will not cause the task referencing +it to be INVALID, follow the task name with pipe (`|`) character. This feature +is particularly useful when a property is used in the task name. + +``` + preload-task = Optional task {$_name} | +``` + +To define an alternate task to load when an optional task does not exist, +list the alternate task name after the pipe (`|`) character. This feature +may be chained together as many times as needed. + +``` + subtask = Optional Subtask {$_name} | + Backup Optional Subtask {$_name} Backup | + Default Subtask # Must exist if the above two don't! +``` + +<a id="task_reference"/> +Task Reference +--------- + +Tasks reference can be a simple task name when the defined task is intended to be in +the same file, tasks from other files can also be referenced by syntax explained below. + +``` +TASK_REFERENCE = [ [ TASK_FILE_PATH ] '^' ] TASK_NAME +``` + +To reference a task from root task.config (top level task.config file of a repository) +on the current ref, prefix the task name with `^`. + +Example: + +task/.../<any>.config +``` + ... + preload-task = ^Task in root task config + ... +``` + +task.config +``` + ... + [task "Task in root task config"] + ... +``` + +To provide an absolute reference to a task under the `task` folder, provide the subpath starting +from `task` directory with a leading `/` followed by a `^` and then task name. + +Example: + +task.config +``` + ... + subtask = /foo/bar/baz.config^Absolute Example Task + ... +``` + +task/foo/bar/baz.config +``` + ... + [task "Absolute Example Task"] + ... +``` + +Similarly, to provide reference to tasks which are in a subdirectory of the file containing the +current task avoid the leading `/`. + +Example: + +task/foo/file.config +``` + ... + subtask = bar/baz.config^Relative Example Task + ... +``` + +task/foo/bar/baz.config +``` + ... + [task "Relative Example Task"] + ... +``` + +Relative tasks specified in a root task.config would look for a file path under the task directory. + +Example: + +task.config +``` + ... + subtask = foo/bar.config^Relative from Root Example Task + ... +``` + +task/foo/bar.config +``` + ... + [task "Relative from Root Example Task"] + ... +```
diff --git a/src/main/resources/Documentation/test/task-preview/non_root_with_subtask_from_root_task.md b/src/main/resources/Documentation/test/task-preview/non_root_with_subtask_from_root_task.md new file mode 100644 index 0000000..a0f11eb --- /dev/null +++ b/src/main/resources/Documentation/test/task-preview/non_root_with_subtask_from_root_task.md
@@ -0,0 +1,47 @@ +# --task-preview non-root file with subtask pointing root task + +file: `All-Projects.git:refs/meta/config:task.config` +``` +[root "Points to subFile task with rootFile task preview"] + applicable = is:open + pass = True + subtask = foo/bar/baz.config^Preview pointing to rootFile task + +[task "Task in rootFile"] + applicable = is:open + pass = True +``` + +file: `All-Projects.git:refs/meta/config:task/foo/bar/baz.config` +``` + [task "Preview pointing to rootFile task"] + applicable = is:open + pass = Fail ++ subtask = ^Task in rootFile +``` + +json: +``` +{ + "applicable" : true, + "hasPass" : true, + "name" : "Points to subFile task with rootFile task preview", + "status" : "WAITING", + "subTasks" : [ + { + "applicable" : true, + "hasPass" : true, + "name" : "Preview pointing to rootFile task", + "status" : "READY", + "subTasks" : [ + { + "applicable" : true, + "hasPass" : true, + "name" : "Task in rootFile", + "status" : "PASS" + } + ] + } + ] +} +``` \ No newline at end of file
diff --git a/src/main/resources/Documentation/test/task_states.md b/src/main/resources/Documentation/test/task_states.md index ef8285f..0b8debb 100644 --- a/src/main/resources/Documentation/test/task_states.md +++ b/src/main/resources/Documentation/test/task_states.md
@@ -2155,6 +2155,123 @@ ] } +[root "Root Import tasks using absolute syntax"] + applicable = is:open + subtask = /relative.config^Root Import task from subdir using relative syntax + subtask = /dir/common.config^Root Import task from root task.config + +{ + "applicable" : true, + "hasPass" : false, + "name" : "Root Import tasks using absolute syntax", + "status" : "PASS", + "subTasks" : [ + { + "applicable" : true, + "hasPass" : false, + "name" : "Root Import task from subdir using relative syntax", + "status" : "PASS", + "subTasks" : [ + { + "applicable" : true, + "hasPass" : true, + "name" : "Sample relative task in sub dir", + "status" : "PASS" + } + ] + }, + { + "applicable" : true, + "hasPass" : false, + "name" : "Root Import task from root task.config", + "status" : "PASS", + "subTasks" : [ + { + "applicable" : true, + "hasPass" : true, + "name" : "Subtask PASS", + "status" : "PASS" + } + ] + } + ] +} + +[root "Root Import relative tasks from root config"] + applicable = is:open + subtask = dir/common.config^Root Import task from root task.config + +{ + "applicable" : true, + "hasPass" : false, + "name" : "Root Import relative tasks from root config", + "status" : "PASS", + "subTasks" : [ + { + "applicable" : true, + "hasPass" : false, + "name" : "Root Import task from root task.config", + "status" : "PASS", + "subTasks" : [ + { + "applicable" : true, + "hasPass" : true, + "name" : "Subtask PASS", + "status" : "PASS" + } + ] + } + ] +} + +[root "Root subtasks-external user ref with Absolute and Relative syntaxes"] + subtasks-external = user absolute and relative syntaxes + +[external "user absolute and relative syntaxes"] + user = testuser + file = dir/sample.config + +{ + "applicable" : true, + "hasPass" : false, + "name" : "Root subtasks-external user ref with Absolute and Relative syntaxes", + "status" : "PASS", + "subTasks" : [ + { + "applicable" : true, + "hasPass" : true, + "name" : "Referencing single task from same user ref", + "status" : "PASS", + "subTasks" : [ + { + "applicable" : true, + "hasPass" : true, + "name" : "Relative Task", + "status" : "PASS" + }, + { + "applicable" : true, + "hasPass" : true, + "name" : "Relative Task in sub dir", + "status" : "PASS" + }, + { + "applicable" : true, + "hasPass" : true, + "name" : "task in user root config file", + "status" : "PASS" + }, + { + "applicable" : true, + "hasPass" : true, + "name" : "Absolute Task", + "status" : "PASS" + } + ] + } + ] +} + [root "Root INVALID Preload"] preload-task = missing @@ -2745,6 +2862,23 @@ fail = is:open ``` +file: `All-Projects:refs/meta/config:task/relative.config` +``` +[task "Root Import task from subdir using relative syntax"] + subtask = dir/common.config^Sample relative task in sub dir +``` + +file: `All-Projects:refs/meta/config:task/dir/common.config` +``` +[task "Sample relative task in sub dir"] + applicable = is:open + pass = is:open + +[task "Root Import task from root task.config"] + applicable = is:open + subtask = ^Subtask PASS +``` + file: `All-Projects:refs/meta/config:task/invalids.config` ``` [task "No PASS criteria"] @@ -2930,3 +3064,42 @@ applicable = is:open fail = is:open ``` + +file: `All-Users:refs/users/self:task.config` +``` +[task "task in user root config file"] + applicable = is:open + pass = is:open +``` + +file: `All-Users:refs/users/self:task/dir/sample.config` +``` +[task "Referencing single task from same user ref"] + applicable = is:open + pass = is:open + subtask = relative.config^Relative Task + subtask = sub_dir/relative.config^Relative Task in sub dir + subtask = ^task in user root config file + subtask = /foo/bar.config^Absolute Task +``` + +file: `All-Users:refs/users/self:task/dir/relative.config` +``` +[task "Relative Task"] + applicable = is:open + pass = is:open +``` + +file: `All-Users:refs/users/self:task/dir/sub_dir/relative.config` +``` +[task "Relative Task in sub dir"] + applicable = is:open + pass = is:open +``` + +file: `All-Users:refs/users/self:task/foo/bar.config` +``` +[task "Absolute Task"] + applicable = is:open + pass = is:open +```
diff --git a/src/test/java/com/googlesource/gerrit/plugins/task/TaskExpressionTest.java b/src/test/java/com/googlesource/gerrit/plugins/task/TaskExpressionTest.java index 2f27439..90b3dc3 100644 --- a/src/test/java/com/googlesource/gerrit/plugins/task/TaskExpressionTest.java +++ b/src/test/java/com/googlesource/gerrit/plugins/task/TaskExpressionTest.java
@@ -22,10 +22,13 @@ /* * <ul> - * <li><code> "simple" -> ("simple") required</code> - * <li><code> "world | peace" -> ("world", "peace") required</code> - * <li><code> "shadenfreud |" -> ("shadenfreud") optional</code> - * <li><code> "foo | bar |" -> ("foo", "bar") optional</code> + * <li><code> "simple" -> ("simple") required</code> + * <li><code> "world | peace" -> ("world", "peace") required</code> + * <li><code> "shadenfreud |" -> ("shadenfreud") optional</code> + * <li><code> "foo | bar |" -> ("foo", "bar") optional</code> + * <li><code> "/foo^bar | baz |" -> ("task/foo^bar", "baz") optional</code> + * <li><code> "foo^bar | baz |" -> ("cur_dir/foo^bar", "baz") optional</code> + * <li><code> "^bar | baz |" -> ("task.config^bar", "baz") optional</code> * </ul> */ public class TaskExpressionTest extends TestCase { @@ -38,6 +41,14 @@ public static TaskKey WORLD_TASK = TaskKey.create(file, WORLD); public static TaskKey PEACE_TASK = TaskKey.create(file, PEACE); + public static String SAMPLE = "sample"; + public static String TASK_CFG = "task.config"; + public static String SIMPLE_CFG = "task/simple.config"; + public static String PEACE_CFG = "task/peace.config"; + public static String WORLD_PEACE_CFG = "task/world/peace.config"; + public static String REL_WORLD_PEACE_CFG = "world/peace.config"; + public static String ABS_PEACE_CFG = "/peace.config"; + public void testBlank() { TaskExpression exp = getTaskExpression(""); Iterator<TaskKey> it = exp.iterator(); @@ -143,6 +154,42 @@ assertNoSuchElementException(it); } + public void testAbsoluteAndRelativeReference() { + TaskExpression exp = + getTaskExpression( + createFileKey(SIMPLE_CFG), + REL_WORLD_PEACE_CFG + "^" + SAMPLE + " | " + ABS_PEACE_CFG + "^" + SAMPLE); + Iterator<TaskKey> it = exp.iterator(); + assertTrue(it.hasNext()); + assertEquals(it.next(), TaskKey.create(createFileKey(WORLD_PEACE_CFG), SAMPLE)); + assertTrue(it.hasNext()); + assertEquals(it.next(), TaskKey.create(createFileKey(PEACE_CFG), SAMPLE)); + assertTrue(it.hasNext()); + assertNoSuchElementException(it); + } + + public void testAbsoluteAndRelativeReferenceFromRoot() { + TaskExpression exp = + getTaskExpression( + createFileKey(TASK_CFG), + REL_WORLD_PEACE_CFG + "^" + SAMPLE + " | " + ABS_PEACE_CFG + "^" + SAMPLE); + Iterator<TaskKey> it = exp.iterator(); + assertTrue(it.hasNext()); + assertEquals(it.next(), TaskKey.create(createFileKey(WORLD_PEACE_CFG), SAMPLE)); + assertTrue(it.hasNext()); + assertEquals(it.next(), TaskKey.create(createFileKey(PEACE_CFG), SAMPLE)); + assertTrue(it.hasNext()); + assertNoSuchElementException(it); + } + + public void testReferenceFromRoot() { + TaskExpression exp = getTaskExpression(createFileKey(SIMPLE_CFG), " ^" + SAMPLE + " | "); + Iterator<TaskKey> it = exp.iterator(); + assertTrue(it.hasNext()); + assertEquals(it.next(), TaskKey.create(createFileKey(TASK_CFG), SAMPLE)); + assertNoSuchElementException(it); + } + public void testDifferentKeyOnDifferentFile() { TaskExpression exp = getTaskExpression(createFileKey("foo", "bar", "baz"), SIMPLE); TaskExpression otherExp = getTaskExpression(createFileKey("foo", "bar", "other"), SIMPLE); @@ -184,6 +231,10 @@ return new TaskExpression(file, expression); } + protected static FileKey createFileKey(String file) { + return createFileKey("foo", "bar", file); + } + protected static FileKey createFileKey(String project, String branch, String file) { return FileKey.create(BranchNameKey.create(Project.NameKey.parse(project), branch), file); }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/task/TaskReferenceTest.java b/src/test/java/com/googlesource/gerrit/plugins/task/TaskReferenceTest.java new file mode 100644 index 0000000..8d079ff --- /dev/null +++ b/src/test/java/com/googlesource/gerrit/plugins/task/TaskReferenceTest.java
@@ -0,0 +1,101 @@ +// Copyright (C) 2022 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 com.google.gerrit.entities.BranchNameKey; +import com.google.gerrit.entities.Project; +import java.util.NoSuchElementException; +import junit.framework.TestCase; +import org.junit.Test; + +public class TaskReferenceTest extends TestCase { + public static String SIMPLE = "simple"; + public static String ROOT = "task.config"; + public static String COMMON = "task/common.config"; + public static String SUB_COMMON = "task/dir/common.config"; + public static FileKey ROOT_CFG = createFileKey("project", "branch", ROOT); + public static FileKey COMMON_CFG = createFileKey("project", "branch", COMMON); + public static FileKey SUB_COMMON_CFG = createFileKey("project", "branch", SUB_COMMON); + + @Test + public void testReferencingTaskFromSameFile() { + assertEquals(createTaskKey(ROOT_CFG, SIMPLE), getTaskFromReference(ROOT_CFG, SIMPLE)); + } + + @Test + public void testReferencingTaskFromRootConfig() { + String reference = "^" + SIMPLE; + assertEquals(createTaskKey(ROOT_CFG, SIMPLE), getTaskFromReference(SUB_COMMON_CFG, reference)); + } + + @Test + public void testReferencingRelativeTaskFromRootConfig() { + String reference = " dir/common.config^" + SIMPLE; + assertEquals(createTaskKey(SUB_COMMON_CFG, SIMPLE), getTaskFromReference(ROOT_CFG, reference)); + } + + @Test + public void testReferencingAbsoluteTaskFromRootConfig() { + String reference = " /common.config^" + SIMPLE; + assertEquals(createTaskKey(COMMON_CFG, SIMPLE), getTaskFromReference(ROOT_CFG, reference)); + } + + @Test + public void testReferencingRelativeTask() { + String reference = " dir/common.config^" + SIMPLE; + assertEquals( + createTaskKey(SUB_COMMON_CFG, SIMPLE), getTaskFromReference(COMMON_CFG, reference)); + } + + @Test + public void testReferencingAbsoluteTask() { + String reference = " /common.config^" + SIMPLE; + assertEquals( + createTaskKey(COMMON_CFG, SIMPLE), getTaskFromReference(SUB_COMMON_CFG, reference)); + } + + @Test + public void testMultipleUpchars() { + String reference = " ^ /common.config^" + SIMPLE; + assertNoSuchElementException(() -> getTaskFromReference(SUB_COMMON_CFG, reference)); + } + + @Test + public void testEmptyReference() { + String empty = ""; + assertNoSuchElementException(() -> getTaskFromReference(SUB_COMMON_CFG, empty)); + } + + protected static TaskKey getTaskFromReference(FileKey file, String expression) { + return new TaskReference(file, expression).getTaskKey(); + } + + protected static TaskKey createTaskKey(FileKey file, String task) { + return TaskKey.create(file, task); + } + + protected static FileKey createFileKey(String project, String branch, String file) { + return FileKey.create(BranchNameKey.create(Project.NameKey.parse(project), branch), file); + } + + protected static void assertNoSuchElementException(Runnable f) { + try { + f.run(); + assertTrue(false); + } catch (NoSuchElementException e) { + assertTrue(true); + } + } +}
diff --git a/test/check_task_visibility.sh b/test/check_task_visibility.sh index c5855e2..1be8b96 100755 --- a/test/check_task_visibility.sh +++ b/test/check_task_visibility.sh
@@ -199,7 +199,9 @@ "new_root_with_original_with_external_secret_ref.md" "non-secret_ref_with_external_secret_ref.md" "root_with_external_non-secret_ref_with_external_secret_ref.md" -"root_with_external_secret_ref.md") +"root_with_external_secret_ref.md" +"non_root_with_subtask_from_root_task.md" +) for test in "${TESTS[@]}" ; do TEST_DOC="$(replace_user_refs < "$TEST_DOC_DIR/$test" | replace_users)"