Support importing tasks from user refs This change adds support to import a single task from any user ref (All-Users.git:/refs/users/..) by specifying their username in task config file. See documentation for usage details. Change-Id: If40f6dbaba2a0ed6c9fe33e15a4fe2c61d79bb2e
diff --git a/src/main/antlr4/com/googlesource/gerrit/plugins/task/TaskReference.g4 b/src/main/antlr4/com/googlesource/gerrit/plugins/task/TaskReference.g4 index 5631e07..b360040 100644 --- a/src/main/antlr4/com/googlesource/gerrit/plugins/task/TaskReference.g4 +++ b/src/main/antlr4/com/googlesource/gerrit/plugins/task/TaskReference.g4
@@ -16,7 +16,10 @@ * * This file defines the grammar used for Task Reference * - * TASK_REF = [ [ TASK_FILE_PATH ] '^' ] TASK_NAME + * TASK_REFERENCE = [ + * [ @USERNAME [ TASK_FILE_PATH ] ] | + * [ TASK_FILE_PATH ] + * ] '^' TASK_NAME * * Examples: * @@ -40,6 +43,16 @@ * Implied task: * file: All-Projects:refs/meta/config:task.config task: sample * + * file: Any projects, ref, file + * reference: @jim^sample + * Implied task: + * file: All-Users:refs/users/<jim>:task.config task: sample + * + * file: Any projects, ref, file + * reference: @jim/foo^simple + * Implied task: + * file: All-Users:refs/users/<jim>:task/foo^simple task: sample + * */ grammar TaskReference; @@ -53,7 +66,12 @@ ; file_path - : (absolute| relative)? TASK_DELIMETER + : user absolute? TASK_DELIMETER + | (absolute| relative)? TASK_DELIMETER + ; + +user + : '@' NAME ; absolute @@ -73,11 +91,16 @@ ; NAME - : URL_ALLOWED_CHARS_EXCEPT_FWD_SLASH+ + : URL_ALLOWED_CHARS_EXCEPT_FWD_SLASH_AND_AT URL_ALLOWED_CHARS_EXCEPT_FWD_SLASH* ; fragment URL_ALLOWED_CHARS_EXCEPT_FWD_SLASH - : ':' | '?' | '#' | '[' | ']' | '@' + : URL_ALLOWED_CHARS_EXCEPT_FWD_SLASH_AND_AT + | '@' + ; + +fragment URL_ALLOWED_CHARS_EXCEPT_FWD_SLASH_AND_AT + : ':' | '?' | '#' | '[' | ']' |'!' | '$' | '&' | '\'' | '(' | ')' | '*' | '+' | ',' | ';' | '=' | '%' | 'A'..'Z' | 'a'..'z' | '0'..'9' @@ -86,4 +109,4 @@ TASK_DELIMETER : '^' - ; \ No newline at end of file + ;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/FileKey.java b/src/main/java/com/googlesource/gerrit/plugins/task/FileKey.java index 2d44757..9856075 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/task/FileKey.java +++ b/src/main/java/com/googlesource/gerrit/plugins/task/FileKey.java
@@ -16,10 +16,15 @@ import com.google.auto.value.AutoValue; import com.google.gerrit.entities.BranchNameKey; +import com.google.gerrit.entities.Project; /** An immutable reference to a fully qualified file in gerrit repo. */ @AutoValue public abstract class FileKey { + public static FileKey create(Project.NameKey project, String branch, String file) { + return new AutoValue_FileKey(BranchNameKey.create(project, branch), file); + } + public static FileKey create(BranchNameKey branch, String file) { return new AutoValue_FileKey(branch, file); }
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 44564ed..d28e529 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/task/Modules.java +++ b/src/main/java/com/googlesource/gerrit/plugins/task/Modules.java
@@ -39,6 +39,8 @@ .annotatedWith(Exports.named(ViewPathsCapability.VIEW_PATHS)) .to(ViewPathsCapability.class); factory(TaskPath.Factory.class); + factory(TaskReference.Factory.class); + factory(TaskExpression.Factory.class); bind(ChangePluginDefinedInfoFactory.class) .annotatedWith(Exports.named("task"))
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/Preloader.java b/src/main/java/com/googlesource/gerrit/plugins/task/Preloader.java index 3361464..e996193 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/task/Preloader.java +++ b/src/main/java/com/googlesource/gerrit/plugins/task/Preloader.java
@@ -39,14 +39,17 @@ } protected final TaskConfigFactory taskConfigFactory; + protected final TaskExpression.Factory taskExpressionFactory; protected final StatisticsMap<TaskExpressionKey, Optional<Task>> optionalTaskByExpression = new HitHashMap<>(); protected Statistics statistics; @Inject - public Preloader(TaskConfigFactory taskConfigFactory) { + public Preloader( + TaskConfigFactory taskConfigFactory, TaskExpression.Factory taskExpressionFactory) { this.taskConfigFactory = taskConfigFactory; + this.taskExpressionFactory = taskExpressionFactory; } public List<Task> getRootTasks() throws IOException, ConfigInvalidException { @@ -109,7 +112,7 @@ statistics.preloaded++; } Optional<Task> preloadFrom = - getOptionalTask(new TaskExpression(definition.file(), expression)); + getOptionalTask(taskExpressionFactory.create(definition.file(), expression)); if (preloadFrom.isPresent()) { return preloadFrom(definition, preloadFrom.get()); }
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 759caba..5b9bef0 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/task/TaskExpression.java +++ b/src/main/java/com/googlesource/gerrit/plugins/task/TaskExpression.java
@@ -14,6 +14,8 @@ package com.googlesource.gerrit.plugins.task; +import com.google.inject.Inject; +import com.google.inject.assistedinject.Assisted; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.regex.Matcher; @@ -47,11 +49,21 @@ * </ul> */ public class TaskExpression implements Iterable<TaskKey> { + public interface Factory { + TaskExpression create(FileKey key, String expression); + } + protected static final Pattern EXPRESSION_PATTERN = Pattern.compile("([^ |]+[^|]*)(\\|)?"); protected final TaskExpressionKey key; + protected final TaskReference.Factory taskReferenceFactory; - public TaskExpression(FileKey key, String expression) { + @Inject + public TaskExpression( + TaskReference.Factory taskReferenceFactory, + @Assisted FileKey key, + @Assisted String expression) { this.key = TaskExpressionKey.create(key, expression); + this.taskReferenceFactory = taskReferenceFactory; } @Override @@ -84,7 +96,7 @@ } hasNext = null; try { - return new TaskReference(key.file(), m.group(1)).getTaskKey(); + return taskReferenceFactory.create(key.file(), m.group(1)).getTaskKey(); } catch (ConfigInvalidException e) { throw new RuntimeConfigInvalidException(e); }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/TaskKey.java b/src/main/java/com/googlesource/gerrit/plugins/task/TaskKey.java index 6acab3e..8cddac0 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/task/TaskKey.java +++ b/src/main/java/com/googlesource/gerrit/plugins/task/TaskKey.java
@@ -17,6 +17,9 @@ import com.google.auto.value.AutoValue; import com.google.common.base.Preconditions; import com.google.gerrit.entities.BranchNameKey; +import com.google.gerrit.entities.RefNames; +import com.google.gerrit.server.account.AccountCache; +import com.google.gerrit.server.config.AllUsersName; import java.nio.file.Path; import java.nio.file.Paths; import org.eclipse.jgit.errors.ConfigInvalidException; @@ -55,15 +58,32 @@ } public static class Builder { + protected final AccountCache accountCache; + protected final AllUsersName allUsersName; protected final FileKey relativeTo; + protected BranchNameKey branch; protected String file; protected String task; - Builder(FileKey relativeTo) { + Builder(FileKey relativeTo, AllUsersName allUsersName, AccountCache accountCache) { this.relativeTo = relativeTo; + this.allUsersName = allUsersName; + this.accountCache = accountCache; } public TaskKey buildTaskKey() { + return isReferencingAnotherRef() ? getAnotherRefTask() : getSameRefTask(); + } + + protected TaskKey getAnotherRefTask() { + return TaskKey.create( + isReferencingRootFile() + ? FileKey.create(branch, TaskFileConstants.TASK_CFG) + : FileKey.create(branch, file), + task); + } + + protected TaskKey getSameRefTask() { return TaskKey.create( isRelativePath() ? relativeTo : FileKey.create(relativeTo.branch(), file), task); } @@ -94,6 +114,19 @@ this.task = task; } + public void setUsername(String username) throws ConfigInvalidException { + branch = + BranchNameKey.create( + allUsersName, + RefNames.refsUsers( + accountCache + .getByUsername(username) + .orElseThrow( + () -> new ConfigInvalidException("Cannot resolve username: " + username)) + .account() + .id())); + } + protected void throwIfInvalidPath() throws ConfigInvalidException { Path path = Paths.get(file); if (!path.startsWith(TaskFileConstants.TASK_DIR) @@ -115,5 +148,13 @@ protected boolean isFileAlreadySet() { return file != null; } + + protected boolean isReferencingRootFile() { + return file == null; + } + + protected boolean isReferencingAnotherRef() { + return branch != null; + } } }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/TaskReference.java b/src/main/java/com/googlesource/gerrit/plugins/task/TaskReference.java index 139b27d..308bae8 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/task/TaskReference.java +++ b/src/main/java/com/googlesource/gerrit/plugins/task/TaskReference.java
@@ -14,6 +14,11 @@ package com.googlesource.gerrit.plugins.task; +import com.google.common.annotations.VisibleForTesting; +import com.google.gerrit.server.account.AccountCache; +import com.google.gerrit.server.config.AllUsersNameProvider; +import com.google.inject.Inject; +import com.google.inject.assistedinject.Assisted; import java.nio.file.Paths; import java.util.NoSuchElementException; import org.antlr.v4.runtime.BaseErrorListener; @@ -28,11 +33,25 @@ /** This class is used by TaskExpression to decode the task from task reference. */ public class TaskReference { - protected FileKey currentFile; - protected String reference; + protected final String reference; + protected final TaskKey.Builder taskKeyBuilder; - public TaskReference(FileKey originalFile, String reference) { - currentFile = originalFile; + interface Factory { + TaskReference create(FileKey relativeTo, String reference); + } + + @Inject + public TaskReference( + AllUsersNameProvider allUsersNameProvider, + AccountCache accountCache, + @Assisted FileKey relativeTo, + @Assisted String reference) { + this(new TaskKey.Builder(relativeTo, allUsersNameProvider.get(), accountCache), reference); + } + + @VisibleForTesting + public TaskReference(TaskKey.Builder taskKeyBuilder, String reference) { + this.taskKeyBuilder = taskKeyBuilder; this.reference = reference.trim(); if (reference.isEmpty()) { throw new NoSuchElementException(); @@ -40,14 +59,13 @@ } public TaskKey getTaskKey() throws ConfigInvalidException { - TaskKey.Builder builder = new TaskKey.Builder(currentFile); ParseTreeWalker walker = new ParseTreeWalker(); try { - walker.walk(new TaskReferenceListener(builder), parse()); + walker.walk(new TaskReferenceListener(taskKeyBuilder), parse()); } catch (RuntimeConfigInvalidException e) { throw e.checkedException; } - return builder.buildTaskKey(); + return taskKeyBuilder.buildTaskKey(); } protected ParseTree parse() { @@ -112,5 +130,14 @@ } } } + + @Override + public void enterUser(TaskReferenceParser.UserContext ctx) { + try { + builder.setUsername(ctx.NAME().getText()); + } catch (ConfigInvalidException e) { + throw new RuntimeConfigInvalidException(e); + } + } } }
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 f2e651e..44fcadc 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/task/TaskTree.java +++ b/src/main/java/com/googlesource/gerrit/plugins/task/TaskTree.java
@@ -90,6 +90,7 @@ protected final PredicateCache predicateCache; protected final MatchCache matchCache; protected final Preloader preloader; + protected final TaskExpression.Factory taskExpressionFactory; protected final NodeList root = new NodeList(); protected final Provider<ChangeQueryBuilder> changeQueryBuilderProvider; protected final Provider<ChangeQueryProcessor> changeQueryProcessorProvider; @@ -112,6 +113,7 @@ Provider<ChangeQueryBuilder> changeQueryBuilderProvider, Provider<ChangeQueryProcessor> changeQueryProcessorProvider, PredicateCache predicateCache, + TaskExpression.Factory taskExpressionFactory, Preloader preloader) { this.accountResolver = accountResolver; this.allUsers = allUsers; @@ -120,6 +122,7 @@ this.changeQueryBuilderProvider = changeQueryBuilderProvider; this.predicateCache = predicateCache; this.matchCache = new MatchCache(predicateCache); + this.taskExpressionFactory = taskExpressionFactory; this.preloader = preloader; } @@ -365,7 +368,7 @@ for (String expression : task.subTasks) { try { Optional<Task> def = - preloader.getOptionalTask(new TaskExpression(task.file(), expression)); + preloader.getOptionalTask(taskExpressionFactory.create(task.file(), expression)); if (def.isPresent()) { addPreloaded(def.get()); }
diff --git a/src/main/resources/Documentation/task_expression.md b/src/main/resources/Documentation/task_expression.md index f9febcf..498d2b4 100644 --- a/src/main/resources/Documentation/task_expression.md +++ b/src/main/resources/Documentation/task_expression.md
@@ -4,7 +4,7 @@ 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). +from other files and refs can be referenced using [Task Reference](#task_reference). ``` TASK_EXPR = TASK_REFERENCE [ WHITE_SPACE * '|' [ WHITE_SPACE * TASK_EXPR ] ] @@ -33,10 +33,14 @@ --------- 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. +the same file, tasks from other files and refs can also be referenced by syntax explained +below. ``` -TASK_REFERENCE = [ [ TASK_FILE_PATH ] '^' ] TASK_NAME + TASK_REFERENCE = [ + [ @USERNAME [ TASK_FILE_PATH ] ] | + [ TASK_FILE_PATH ] + ] '^' TASK_NAME ``` To reference a task from root task.config (top level task.config file of a repository) @@ -113,3 +117,36 @@ [task "Relative from Root Example Task"] ... ``` + +To reference a task from a specific user ref (All-Users.git:refs/users/<user>), specify the +username with `@`. + +when referencing from user refs, to get task from top level task.config on a user ref use +`@<username>^<task_name>` and to get any task under the task directory use the relative +path, like: `@<username>/<relative path from task dir>^<task_name>`. It doesn't matter which +project, ref and file one is referencing from while using this syntax. + +Example: +Assumption: Account id of user_a is 1000000 + +All-Users:refs/users/00/1000000:task.config +``` + ... + [task "top level task"] + ... +``` + +All-Users:refs/users/00/1000000:/task/dir/common.config +``` + ... + [task "common task"] + ... +``` + +All-Projects:refs/meta/config:/task.config +``` + ... + preload-task = @user_a_username^top level task + preload-task = @user_a_username/dir/common.config^common task + ... +```
diff --git a/src/main/resources/Documentation/test/task-preview/subtask_using_user_syntax/root_with_subtask_non-secret_ref_with_subtask_secret_ref.md b/src/main/resources/Documentation/test/task-preview/subtask_using_user_syntax/root_with_subtask_non-secret_ref_with_subtask_secret_ref.md new file mode 100644 index 0000000..fad2f1d --- /dev/null +++ b/src/main/resources/Documentation/test/task-preview/subtask_using_user_syntax/root_with_subtask_non-secret_ref_with_subtask_secret_ref.md
@@ -0,0 +1,52 @@ +# --task-preview root file with subtask pointing to a non-secret user ref with subtask pointing to a secret user ref. + +file: `All-Projects.git:refs/meta/config:task.config` +``` + [root "Root Preview NON-SECRET subtask with SECRET subtask"] + applicable = "is:open" + pass = True ++ subtask = @{non_secret_user}/secret_external.config^NON-SECRET with SECRET subtask +``` + +file: `All-Users.git:{non_secret_user_ref}:task/secret_external.config` +``` +[task "NON-SECRET with SECRET subtask"] + applicable = is:open + pass = True + subtask = @{secret_user}/secret.config^SECRET task +``` + +file: `All-Users:{secret_user_ref}:task/secret.config` +``` +[task "SECRET task"] + applicable = is:open + pass = Fail +``` + +json: +``` +{ + "applicable" : true, + "hasPass" : true, + "name" : "Root Preview NON-SECRET subtask with SECRET subtask", + "status" : "WAITING", + "subTasks" : [ + { + "applicable" : true, + "hasPass" : true, + "name" : "NON-SECRET with SECRET subtask", + "status" : "WAITING", + "subTasks" : [ + { + "name" : "UNKNOWN", # Only Test Suite: non-secret + "status" : "UNKNOWN" # Only Test Suite: non-secret + "applicable" : true, # Only Test Suite: secret + "hasPass" : true, # Only Test Suite: secret + "name" : "SECRET task", # Only Test Suite: secret + "status" : "READY" # Only Test Suite: secret + } + ] + } + ] +} +``` \ No newline at end of file
diff --git a/src/main/resources/Documentation/test/task-preview/subtask_using_user_syntax/root_with_subtask_secret_ref.md b/src/main/resources/Documentation/test/task-preview/subtask_using_user_syntax/root_with_subtask_secret_ref.md new file mode 100644 index 0000000..d2b3349 --- /dev/null +++ b/src/main/resources/Documentation/test/task-preview/subtask_using_user_syntax/root_with_subtask_secret_ref.md
@@ -0,0 +1,36 @@ +# --task-preview root file with subtask pointing to secret user ref + +file: `All-Projects.git:refs/meta/config:task.config` +``` + [root "Root Preview SECRET external"] + applicable = is:open + pass = True ++ subtask = @{secret_user}/secret.config^SECRET Task +``` + +file: `All-Users.git:{secret_user_ref}:task/secret.config` +``` +[task "SECRET Task"] + applicable = is:open + pass = Fail +``` + +json: +``` +{ + "applicable" : true, + "hasPass" : true, + "name" : "Root Preview SECRET external", + "status" : "WAITING", + "subTasks" : [ + { + "name" : "UNKNOWN", # Only Test Suite: non-secret + "status" : "UNKNOWN" # Only Test Suite: non-secret + "applicable" : true, # Only Test Suite: secret + "hasPass" : true, # Only Test Suite: secret + "name" : "SECRET Task", # Only Test Suite: secret + "status" : "READY" # Only Test Suite: secret + } + ] +} +``` \ 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 0b8debb..e325a9a 100644 --- a/src/main/resources/Documentation/test/task_states.md +++ b/src/main/resources/Documentation/test/task_states.md
@@ -2272,6 +2272,32 @@ ] } +[root "Root Import user tasks"] + applicable = is:open + subtask = @testuser/foo/bar.config^Absolute Task + subtask = @testuser^task in user root config file + +{ + "applicable" : true, + "hasPass" : false, + "name" : "Root Import user tasks", + "status" : "PASS", + "subTasks" : [ + { + "applicable" : true, + "hasPass" : true, + "name" : "Absolute Task", + "status" : "PASS" + }, + { + "applicable" : true, + "hasPass" : true, + "name" : "task in user root config file", + "status" : "PASS" + } + ] +} + [root "Root INVALID Preload"] preload-task = missing
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 90b3dc3..fe01ee2 100644 --- a/src/test/java/com/googlesource/gerrit/plugins/task/TaskExpressionTest.java +++ b/src/test/java/com/googlesource/gerrit/plugins/task/TaskExpressionTest.java
@@ -16,9 +16,12 @@ import com.google.gerrit.entities.BranchNameKey; import com.google.gerrit.entities.Project; +import com.google.gerrit.server.account.AccountCache; +import com.google.gerrit.server.config.AllUsersName; import java.util.Iterator; import java.util.NoSuchElementException; import junit.framework.TestCase; +import org.mockito.Mockito; /* * <ul> @@ -228,7 +231,18 @@ } protected TaskExpression getTaskExpression(FileKey file, String expression) { - return new TaskExpression(file, expression); + AccountCache accountCache = Mockito.mock(AccountCache.class); + TaskReference.Factory factory = Mockito.mock(TaskReference.Factory.class); + Mockito.when(factory.create(Mockito.any(), Mockito.any())) + .thenAnswer( + invocation -> + new TaskReference( + new TaskKey.Builder( + (FileKey) invocation.getArguments()[0], + new AllUsersName("All-Users"), + accountCache), + (String) invocation.getArguments()[1])); + return new TaskExpression(factory, file, expression); } protected static FileKey createFileKey(String 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 index 5f309d0..2036f16 100644 --- a/src/test/java/com/googlesource/gerrit/plugins/task/TaskReferenceTest.java +++ b/src/test/java/com/googlesource/gerrit/plugins/task/TaskReferenceTest.java
@@ -14,14 +14,23 @@ package com.googlesource.gerrit.plugins.task; +import com.google.gerrit.entities.Account; import com.google.gerrit.entities.BranchNameKey; import com.google.gerrit.entities.Project; +import com.google.gerrit.server.account.AccountCache; +import com.google.gerrit.server.account.AccountState; +import com.google.gerrit.server.config.AllUsersName; +import java.sql.Timestamp; import java.util.NoSuchElementException; +import java.util.Optional; import junit.framework.TestCase; import org.eclipse.jgit.errors.ConfigInvalidException; import org.junit.Test; +import org.mockito.Mockito; public class TaskReferenceTest extends TestCase { + private static final String ALL_USERS = "All-Users"; + public static String SIMPLE = "simple"; public static String ROOT = "task.config"; public static String COMMON = "task/common.config"; @@ -30,64 +39,94 @@ public static FileKey COMMON_CFG = createFileKey("project", "branch", COMMON); public static FileKey SUB_COMMON_CFG = createFileKey("project", "branch", SUB_COMMON); + public static final String TEST_USER = "testuser"; + public static final int TEST_USER_ID = 100000; + public static final Account TEST_USER_ACCOUNT = + Account.builder(Account.id(TEST_USER_ID), new Timestamp(0L)).build(); + public static final String TEST_USER_REF = + "refs/users/" + String.format("%02d", TEST_USER_ID % 100) + "/" + TEST_USER_ID; + public static final FileKey TEST_USER_ROOT_CFG = createFileKey(ALL_USERS, TEST_USER_REF, ROOT); + public static final FileKey TEST_USER_COMMON_CFG = + createFileKey(ALL_USERS, TEST_USER_REF, COMMON); + @Test - public void testReferencingTaskFromSameFile() { + public void testReferencingTaskFromSameFile() throws Exception { assertEquals(createTaskKey(ROOT_CFG, SIMPLE), getTaskFromReference(ROOT_CFG, SIMPLE)); } @Test - public void testReferencingTaskFromRootConfig() { + public void testReferencingTaskFromRootConfig() throws Exception { String reference = "^" + SIMPLE; assertEquals(createTaskKey(ROOT_CFG, SIMPLE), getTaskFromReference(SUB_COMMON_CFG, reference)); } @Test - public void testReferencingRelativeTaskFromRootConfig() { + public void testReferencingRelativeTaskFromRootConfig() throws Exception { String reference = " dir/common.config^" + SIMPLE; assertEquals(createTaskKey(SUB_COMMON_CFG, SIMPLE), getTaskFromReference(ROOT_CFG, reference)); } @Test - public void testReferencingAbsoluteTaskFromRootConfig() { + public void testReferencingAbsoluteTaskFromRootConfig() throws Exception { String reference = " /common.config^" + SIMPLE; assertEquals(createTaskKey(COMMON_CFG, SIMPLE), getTaskFromReference(ROOT_CFG, reference)); } @Test - public void testReferencingRelativeDirTask() { + public void testReferencingRelativeDirTask() throws Exception { String reference = " dir/common.config^" + SIMPLE; assertEquals( createTaskKey(SUB_COMMON_CFG, SIMPLE), getTaskFromReference(COMMON_CFG, reference)); } @Test - public void testReferencingRelativeFileTask() { + public void testReferencingRelativeFileTask() throws Exception { String reference = "common.config^" + SIMPLE; assertEquals(createTaskKey(COMMON_CFG, SIMPLE), getTaskFromReference(COMMON_CFG, reference)); } @Test - public void testReferencingAbsoluteTask() { + public void testReferencingAbsoluteTask() throws Exception { String reference = " /common.config^" + SIMPLE; assertEquals( createTaskKey(COMMON_CFG, SIMPLE), getTaskFromReference(SUB_COMMON_CFG, reference)); } @Test - public void testMultipleUpchars() { + public void testReferencingRootUserTask() throws Exception { + String reference = "@" + TEST_USER + "^" + SIMPLE; + assertEquals( + createTaskKey(TEST_USER_ROOT_CFG, SIMPLE), getTaskFromReference(SUB_COMMON_CFG, reference)); + } + + @Test + public void testReferencingUserTaskDir() throws Exception { + String reference = "@" + TEST_USER + "/common.config^" + SIMPLE; + assertEquals( + createTaskKey(TEST_USER_COMMON_CFG, SIMPLE), + getTaskFromReference(SUB_COMMON_CFG, reference)); + } + + @Test + public void testMultipleUpchars() throws Exception { String reference = " ^ /common.config^" + SIMPLE; assertNoSuchElementException(() -> getTaskFromReference(SUB_COMMON_CFG, reference)); } @Test - public void testEmptyReference() { + public void testEmptyReference() throws Exception { String empty = ""; assertNoSuchElementException(() -> getTaskFromReference(SUB_COMMON_CFG, empty)); } protected static TaskKey getTaskFromReference(FileKey file, String expression) { + AccountCache accountCache = Mockito.mock(AccountCache.class); + Mockito.when(accountCache.getByUsername(TEST_USER)) + .thenReturn(Optional.of(AccountState.forAccount(TEST_USER_ACCOUNT))); try { - return new TaskReference(file, expression).getTaskKey(); + return new TaskReference( + new TaskKey.Builder(file, new AllUsersName(ALL_USERS), accountCache), expression) + .getTaskKey(); } catch (ConfigInvalidException e) { throw new NoSuchElementException(); } @@ -101,7 +140,7 @@ return FileKey.create(BranchNameKey.create(Project.NameKey.parse(project), branch), file); } - protected static void assertNoSuchElementException(Runnable f) { + protected static void assertNoSuchElementException(Executable f) throws Exception { try { f.run(); assertTrue(false); @@ -109,4 +148,9 @@ assertTrue(true); } } + + @FunctionalInterface + interface Executable { + void run() throws Exception; + } }
diff --git a/test/check_task_visibility.sh b/test/check_task_visibility.sh index 1be8b96..aa9f25c 100755 --- a/test/check_task_visibility.sh +++ b/test/check_task_visibility.sh
@@ -201,6 +201,8 @@ "root_with_external_non-secret_ref_with_external_secret_ref.md" "root_with_external_secret_ref.md" "non_root_with_subtask_from_root_task.md" +"subtask_using_user_syntax/root_with_subtask_secret_ref.md" +"subtask_using_user_syntax/root_with_subtask_non-secret_ref_with_subtask_secret_ref.md" ) for test in "${TESTS[@]}" ; do