Merge branch 'stable-2.15' into stable-2.16
* stable-2.15:
Upgrade bazlets to latest stable-2.15 to build with 2.15.9 API
Upgrade bazlets to latest stable-2.14 to build with 2.14.18 API
Do not fetch commit message for ref deletions
Revert "Fix ref updated event on change deletion"
Bazel: Include eclipse-out directory in .bazelignore
Add explanatory comment to empty BUILD file(s)
Upgrade bazlets to latest stable-2.15 to build with 2.15.7 API
Upgrade bazlets to latest stable-2.14 to build with 2.14.17 API
Upgrade bazlets to latest stable-2.15 to build with 2.15.6 API
WORKSPACE: Make commented out local_path line spaces indent consistent
Upgrade bazlets to latest stable-2.14 to build with 2.14.16 API
Migrate `tools/bazel.rc` to `.bazelrc`
Update build documentation to link to dev-bazel instead of dev-buck
Align Eclipse compiler settings with core Gerrit's
Upgrade bazlets to latest stable-2.15 to build with 2.15.5 API
bazlets: Replace native.git_repository with skylark rule
Upgrade bazlets to latest stable-2.14 to build with 2.14.15 API
Add eclipse-out to .gitignore
Format all build files with buildifier 0.15.0
Update bazlets to latest revision on stable-2.15
Remove commented-out code
Format BUILD files with buildifier 0.12.0
Format Java files with google-java-format 1.6
Update bazlets to latest revision on stable-2.14
Change-Id: I49f57257660bcd01bc65a06a0af802980a4476fe
diff --git a/.gitignore b/.gitignore
index 83277ad..cabf34f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,4 +3,5 @@
.project
/.settings
/bazel-*
+.DS_Store
/eclipse-out
diff --git a/WORKSPACE b/WORKSPACE
index 7610ede..60d9408 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -3,8 +3,8 @@
load("//:bazlets.bzl", "load_bazlets")
load_bazlets(
- commit = "ca34f0cf89b2e041ea7f4aa4b9696efc2d76746f",
- #local_path = "/home/<user>/projects/bazlets",
+ commit = "c2227415d5044f8439bd47edffb0f052f8da2ac5",
+ # local_path = "/home/<user>/projects/bazlets",
)
# Snapshot Plugin API
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/GlobalRulesFileName.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/GlobalRulesFileName.java
new file mode 100644
index 0000000..4d33168
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/GlobalRulesFileName.java
@@ -0,0 +1,24 @@
+// Copyright (C) 2018 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.its.base;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.google.inject.BindingAnnotation;
+import java.lang.annotation.Retention;
+
+@Retention(RUNTIME)
+@BindingAnnotation
+public @interface GlobalRulesFileName {}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/ItsHookModule.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/ItsHookModule.java
index 91b26a0..dc6f4a5 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/ItsHookModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/ItsHookModule.java
@@ -14,30 +14,43 @@
package com.googlesource.gerrit.plugins.its.base;
-import com.google.gerrit.common.EventListener;
import com.google.gerrit.extensions.annotations.Exports;
import com.google.gerrit.extensions.annotations.PluginName;
import com.google.gerrit.extensions.config.FactoryModule;
+import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.server.config.PluginConfigFactory;
import com.google.gerrit.server.config.ProjectConfigEntry;
+import com.google.gerrit.server.config.SitePaths;
+import com.google.gerrit.server.events.EventListener;
import com.google.gerrit.server.git.validators.CommitValidationListener;
+import com.google.inject.Inject;
+import com.google.inject.Provides;
import com.googlesource.gerrit.plugins.its.base.its.ItsConfig;
import com.googlesource.gerrit.plugins.its.base.its.ItsHookEnabledConfigEntry;
import com.googlesource.gerrit.plugins.its.base.validation.ItsValidateComment;
import com.googlesource.gerrit.plugins.its.base.workflow.ActionController;
import com.googlesource.gerrit.plugins.its.base.workflow.ActionRequest;
+import com.googlesource.gerrit.plugins.its.base.workflow.AddComment;
+import com.googlesource.gerrit.plugins.its.base.workflow.AddPropertyToField;
+import com.googlesource.gerrit.plugins.its.base.workflow.AddSoyComment;
+import com.googlesource.gerrit.plugins.its.base.workflow.AddStandardComment;
import com.googlesource.gerrit.plugins.its.base.workflow.Condition;
-import com.googlesource.gerrit.plugins.its.base.workflow.Property;
+import com.googlesource.gerrit.plugins.its.base.workflow.CreateVersionFromProperty;
+import com.googlesource.gerrit.plugins.its.base.workflow.CustomAction;
+import com.googlesource.gerrit.plugins.its.base.workflow.ItsRulesProjectCacheImpl;
+import com.googlesource.gerrit.plugins.its.base.workflow.LogEvent;
import com.googlesource.gerrit.plugins.its.base.workflow.Rule;
-import com.googlesource.gerrit.plugins.its.base.workflow.action.AddComment;
-import com.googlesource.gerrit.plugins.its.base.workflow.action.AddSoyComment;
-import com.googlesource.gerrit.plugins.its.base.workflow.action.AddStandardComment;
-import com.googlesource.gerrit.plugins.its.base.workflow.action.AddVelocityComment;
-import com.googlesource.gerrit.plugins.its.base.workflow.action.LogEvent;
+import java.nio.file.Path;
public class ItsHookModule extends FactoryModule {
+ /** Rules configuration filename pattern */
+ private static final String CONFIG_FILE_NAME = "actions%s.config";
+
+ /** Folder where rules configuration files are located */
+ private static final String ITS_FOLDER = "its";
+
private final String pluginName;
private final PluginConfigFactory pluginCfgFactory;
@@ -55,13 +68,34 @@
DynamicSet.bind(binder(), CommitValidationListener.class).to(ItsValidateComment.class);
DynamicSet.bind(binder(), EventListener.class).to(ActionController.class);
factory(ActionRequest.Factory.class);
- factory(Property.Factory.class);
factory(Condition.Factory.class);
factory(Rule.Factory.class);
factory(AddComment.Factory.class);
factory(AddSoyComment.Factory.class);
factory(AddStandardComment.Factory.class);
- factory(AddVelocityComment.Factory.class);
+ factory(CreateVersionFromProperty.Factory.class);
factory(LogEvent.Factory.class);
+ factory(AddPropertyToField.Factory.class);
+ DynamicMap.mapOf(binder(), CustomAction.class);
+ install(ItsRulesProjectCacheImpl.module());
+ }
+
+ @Provides
+ @ItsPath
+ @Inject
+ Path itsPath(SitePaths sitePaths) {
+ return sitePaths.etc_dir.normalize().resolve(ITS_FOLDER);
+ }
+
+ @Provides
+ @GlobalRulesFileName
+ String globalRulesFileName() {
+ return String.format(CONFIG_FILE_NAME, "");
+ }
+
+ @Provides
+ @PluginRulesFileName
+ String pluginRulesFileName() {
+ return String.format(CONFIG_FILE_NAME, "-" + pluginName);
}
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/ItsPath.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/ItsPath.java
new file mode 100644
index 0000000..dfd5119
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/ItsPath.java
@@ -0,0 +1,24 @@
+// Copyright (C) 2018 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.its.base;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.google.inject.BindingAnnotation;
+import java.lang.annotation.Retention;
+
+@Retention(RUNTIME)
+@BindingAnnotation
+public @interface ItsPath {}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/PluginRulesFileName.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/PluginRulesFileName.java
new file mode 100644
index 0000000..e53b44b
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/PluginRulesFileName.java
@@ -0,0 +1,24 @@
+// Copyright (C) 2018 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.its.base;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.google.inject.BindingAnnotation;
+import java.lang.annotation.Retention;
+
+@Retention(RUNTIME)
+@BindingAnnotation
+public @interface PluginRulesFileName {}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/its/InitIts.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/its/InitIts.java
index 1cd2b74..9fb2b3d 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/its/InitIts.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/its/InitIts.java
@@ -21,7 +21,10 @@
import com.google.gerrit.pgm.init.api.ConsoleUI;
import com.google.gerrit.pgm.init.api.InitStep;
import com.google.gerrit.pgm.init.api.Section;
+import com.google.gerrit.server.config.SitePaths;
+import com.google.inject.Inject;
import java.io.IOException;
+import java.nio.file.Path;
import java.util.EnumSet;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.Config;
@@ -41,6 +44,8 @@
ENFORCED
}
+ @Inject private SitePaths sitePaths;
+
private final String pluginName;
private final String itsDisplayName;
protected final ConsoleUI ui;
@@ -61,7 +66,18 @@
}
@Override
- public void run() throws IOException, ConfigInvalidException {}
+ public void run() throws IOException, ConfigInvalidException {
+ Path deprecatedRules = sitePaths.etc_dir.normalize().resolve("its").resolve("action.config");
+ if (deprecatedRules.toFile().exists()) {
+ ui.error(
+ "Deprecated rules file '%s' (No trailing 's' in 'action') will not be read. "
+ + "Please migrate to 'etc/its/actions.config' (Trailing 's' in 'actions') and retry "
+ + "the init step.\n",
+ deprecatedRules);
+ throw new ConfigInvalidException(
+ "Deprecated configuration file found: " + deprecatedRules.toRealPath());
+ }
+ }
@Override
public void postRun() throws IOException, ConfigInvalidException {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/its/InvalidTransitionException.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/its/InvalidTransitionException.java
index 78c6ec5..949031b 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/its/InvalidTransitionException.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/its/InvalidTransitionException.java
@@ -14,9 +14,7 @@
package com.googlesource.gerrit.plugins.its.base.its;
-import java.io.IOException;
-
-public class InvalidTransitionException extends IOException {
+public class InvalidTransitionException extends Exception {
private static final long serialVersionUID = 1L;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/its/ItsConfig.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/its/ItsConfig.java
index 99fe80c..552065e 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/its/ItsConfig.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/its/ItsConfig.java
@@ -28,9 +28,11 @@
import com.google.gerrit.server.events.ChangeMergedEvent;
import com.google.gerrit.server.events.ChangeRestoredEvent;
import com.google.gerrit.server.events.CommentAddedEvent;
-import com.google.gerrit.server.events.Event;
import com.google.gerrit.server.events.PatchSetCreatedEvent;
+import com.google.gerrit.server.events.PrivateStateChangedEvent;
+import com.google.gerrit.server.events.RefEvent;
import com.google.gerrit.server.events.RefUpdatedEvent;
+import com.google.gerrit.server.events.WorkInProgressStateChangedEvent;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
@@ -76,29 +78,19 @@
// Plugin enablement --------------------------------------------------------
- public boolean isEnabled(Event event) {
- if (event instanceof PatchSetCreatedEvent) {
- PatchSetCreatedEvent e = (PatchSetCreatedEvent) event;
- return isEnabled(e.getProjectNameKey(), e.getRefName());
- } else if (event instanceof CommentAddedEvent) {
- CommentAddedEvent e = (CommentAddedEvent) event;
- return isEnabled(e.getProjectNameKey(), e.getRefName());
- } else if (event instanceof ChangeMergedEvent) {
- ChangeMergedEvent e = (ChangeMergedEvent) event;
- return isEnabled(e.getProjectNameKey(), e.getRefName());
- } else if (event instanceof ChangeAbandonedEvent) {
- ChangeAbandonedEvent e = (ChangeAbandonedEvent) event;
- return isEnabled(e.getProjectNameKey(), e.getRefName());
- } else if (event instanceof ChangeRestoredEvent) {
- ChangeRestoredEvent e = (ChangeRestoredEvent) event;
- return isEnabled(e.getProjectNameKey(), e.getRefName());
- } else if (event instanceof RefUpdatedEvent) {
- RefUpdatedEvent e = (RefUpdatedEvent) event;
- return isEnabled(e.getProjectNameKey(), e.getRefName());
- } else {
- log.debug("Event {} not recognised and ignored", event);
- return false;
+ public boolean isEnabled(RefEvent event) {
+ if (event instanceof PatchSetCreatedEvent
+ || event instanceof CommentAddedEvent
+ || event instanceof ChangeMergedEvent
+ || event instanceof ChangeAbandonedEvent
+ || event instanceof ChangeRestoredEvent
+ || event instanceof PrivateStateChangedEvent
+ || event instanceof WorkInProgressStateChangedEvent
+ || event instanceof RefUpdatedEvent) {
+ return isEnabled(event.getProjectNameKey(), event.getRefName());
}
+ log.debug("Event {} not recognised and ignored", event);
+ return false;
}
public boolean isEnabled(Project.NameKey projectNK, String refName) {
@@ -110,17 +102,8 @@
projectNK.get());
return false;
}
-
- if (isEnforcedByAnyParentProject(refName, projectState)) {
- return true;
- }
-
- return !"false"
- .equals(
- pluginCfgFactory
- .getFromProjectConfigWithInheritance(projectState, pluginName)
- .getString("enabled", "false"))
- && isEnabledForBranch(projectState, refName);
+ return isEnforcedByAnyParentProject(refName, projectState)
+ || (isEnabledForProject(projectState) && isEnabledForBranch(projectState, refName));
}
private boolean isEnforcedByAnyParentProject(String refName, ProjectState projectState) {
@@ -134,6 +117,14 @@
return false;
}
+ private boolean isEnabledForProject(ProjectState projectState) {
+ return !"false"
+ .equals(
+ pluginCfgFactory
+ .getFromProjectConfigWithInheritance(projectState, pluginName)
+ .getString("enabled", "false"));
+ }
+
private boolean isEnabledForBranch(ProjectState project, String refName) {
String[] refPatterns =
pluginCfgFactory
@@ -154,6 +145,16 @@
return RefPatternMatcher.getMatcher(refPattern).match(refName, null);
}
+ // Project association
+ public Optional<String> getItsProjectName(Project.NameKey projectNK) {
+ ProjectState projectState = projectCache.get(projectNK);
+ if (projectState == null) {
+ return Optional.empty();
+ }
+ return Optional.ofNullable(
+ pluginCfgFactory.getFromProjectConfig(projectState, pluginName).getString("its-project"));
+ }
+
// Issue association --------------------------------------------------------
/**
@@ -215,6 +216,16 @@
}
/**
+ * Pattern to skip the mandatory check for an issue. Can be used to explicitly bypass the
+ * mandatory issue pattern check for some commits.
+ *
+ * <p>When no pattern is specified, it will return a pattern which never matches.
+ */
+ public Optional<Pattern> getDummyIssuePattern() {
+ return Optional.ofNullable(getPluginConfigString("dummyIssuePattern")).map(Pattern::compile);
+ }
+
+ /**
* Gets how necessary it is to associate commits with issues
*
* @return policy on how necessary association with issues is
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/its/ItsFacade.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/its/ItsFacade.java
index 9d7b3fa..db2857b 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/its/ItsFacade.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/its/ItsFacade.java
@@ -31,8 +31,17 @@
public void addComment(String issueId, String comment) throws IOException;
+ default void addValueToField(String issueId, String value, String fieldId) throws IOException {
+ throw new UnsupportedOperationException(
+ "add-value-to-field is not currently implemented by " + getClass());
+ }
+
public void performAction(String issueId, String actionName) throws IOException;
+ default void createVersion(String itsProject, String version) throws IOException {
+ throw new UnsupportedOperationException("create-version is not implemented by " + getClass());
+ }
+
public boolean exists(final String issueId) throws IOException;
public String createLinkForWebui(String url, String text);
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/its/ItsFacadeFactory.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/its/ItsFacadeFactory.java
new file mode 100644
index 0000000..4e2f035
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/its/ItsFacadeFactory.java
@@ -0,0 +1,25 @@
+// Copyright (C) 2018 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.its.base.its;
+
+import com.google.gerrit.reviewdb.client.Project;
+
+/* An interface to get server information from child its-plugin embedded in the ItsFacade implementation */
+public interface ItsFacadeFactory {
+
+ /* Returns the object of type ItsFacade containing server info extracted from project.config if configured
+ * or the default server configured in gerrit.config if project name is empty or null*/
+ ItsFacade getFacade(Project.NameKey project);
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/its/NoopItsFacade.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/its/NoopItsFacade.java
index de4acd9..ecf3a6f 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/its/NoopItsFacade.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/its/NoopItsFacade.java
@@ -32,6 +32,13 @@
}
@Override
+ public void addValueToField(String issueId, String value, String fieldId) throws IOException {
+ if (log.isDebugEnabled()) {
+ log.debug("addValueToField({},{},{})", issueId, fieldId, value);
+ }
+ }
+
+ @Override
public void addRelatedLink(String issueId, URL relatedUrl, String description)
throws IOException {
if (log.isDebugEnabled()) {
@@ -55,6 +62,13 @@
}
@Override
+ public void createVersion(String itsProject, String version) {
+ if (log.isDebugEnabled()) {
+ log.debug("createVersion({},{})", itsProject, version);
+ }
+ }
+
+ @Override
public String healthCheck(Check check) throws IOException {
if (log.isDebugEnabled()) {
log.debug("healthCheck()");
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/its/SingleItsServer.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/its/SingleItsServer.java
new file mode 100644
index 0000000..08bfe62
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/its/SingleItsServer.java
@@ -0,0 +1,35 @@
+// Copyright (C) 2018 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.its.base.its;
+
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.inject.Inject;
+
+/* An ItsServer implementation that should be bound
+ * for backward compatibility */
+public class SingleItsServer implements ItsFacadeFactory {
+
+ private final ItsFacade its;
+
+ @Inject
+ public SingleItsServer(ItsFacade its) {
+ this.its = its;
+ }
+
+ @Override
+ public ItsFacade getFacade(Project.NameKey project) {
+ return its;
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/util/IssueExtractor.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/util/IssueExtractor.java
index 17f83c8..cb577c5 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/util/IssueExtractor.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/util/IssueExtractor.java
@@ -1,5 +1,7 @@
package com.googlesource.gerrit.plugins.its.base.util;
+import static java.util.Arrays.copyOfRange;
+
import com.google.common.base.Strings;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
@@ -17,7 +19,6 @@
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -125,7 +126,26 @@
public Map<String, Set<String>> getIssueIds(String projectName, String commitId) {
Map<String, Set<String>> ret = Maps.newHashMap();
String commitMessage = commitMessageFetcher.fetchGuarded(projectName, commitId);
+ addIssueIdsFromCommitMessage(ret, commitMessage);
+ return ret;
+ }
+ /**
+ * Gets issues from a commit message.
+ *
+ * @param commitMessage The commit message string.
+ * @return A mapping, whose keys are issue ids and whose values is a set of places where the issue
+ * occurs. Each issue occurs at least in "somewhere". Issues from the first line get tagged
+ * with an occurrence "subject". Issues in the last block get tagged with "footer". Issues
+ * occurring between "subject" and "footer" get tagged with "body".
+ */
+ public Map<String, Set<String>> getIssueIdsFromCommitMessage(String commitMessage) {
+ Map<String, Set<String>> ret = Maps.newHashMap();
+ addIssueIdsFromCommitMessage(ret, commitMessage);
+ return ret;
+ }
+
+ private void addIssueIdsFromCommitMessage(Map<String, Set<String>> ret, String commitMessage) {
addIssuesOccurrence(commitMessage, "somewhere", ret);
String[] lines = commitMessage.split("\n");
@@ -157,10 +177,10 @@
// No footer could be found. So all lines after the first one (that's
// the subject) is the body.
if (lines.length > 0) {
- body = StringUtils.join(lines, "\n", 1, lines.length);
+ body = String.join("\n", copyOfRange(lines, 1, lines.length));
}
} else {
- body = StringUtils.join(lines, "\n", 1, footerStart - 1);
+ body = String.join("\n", copyOfRange(lines, 1, footerStart - 1));
StringBuilder footerBuilder = new StringBuilder();
for (int lineIdx = footerStart; lineIdx < footerEnd; lineIdx++) {
@@ -180,7 +200,7 @@
}
footerBuilder.append(line);
}
- footer = StringUtils.join(lines, "\n", footerStart, footerEnd);
+ footer = String.join("\n", copyOfRange(lines, footerStart, footerEnd));
}
if (body != null) {
addIssuesOccurrence(body, "body", ret);
@@ -189,7 +209,6 @@
addIssuesOccurrence(footer, "footer", ret);
}
}
- return ret;
}
/**
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/util/ItsProjectExtractor.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/util/ItsProjectExtractor.java
new file mode 100644
index 0000000..e872f8d
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/util/ItsProjectExtractor.java
@@ -0,0 +1,34 @@
+// Copyright (C) 2018 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.its.base.util;
+
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.inject.Inject;
+import com.googlesource.gerrit.plugins.its.base.its.ItsConfig;
+import java.util.Optional;
+
+public class ItsProjectExtractor {
+
+ private final ItsConfig itsConfig;
+
+ @Inject
+ ItsProjectExtractor(ItsConfig itsConfig) {
+ this.itsConfig = itsConfig;
+ }
+
+ public Optional<String> getItsProject(String gerritProjectName) {
+ return itsConfig.getItsProjectName(new Project.NameKey(gerritProjectName));
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/util/PropertyAttributeExtractor.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/util/PropertyAttributeExtractor.java
index 3b95144..f87f7b5 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/util/PropertyAttributeExtractor.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/util/PropertyAttributeExtractor.java
@@ -14,7 +14,7 @@
package com.googlesource.gerrit.plugins.its.base.util;
-import com.google.common.collect.Sets;
+import com.google.common.collect.ImmutableMap;
import com.google.gerrit.server.data.AccountAttribute;
import com.google.gerrit.server.data.ApprovalAttribute;
import com.google.gerrit.server.data.ChangeAttribute;
@@ -22,126 +22,80 @@
import com.google.gerrit.server.data.RefUpdateAttribute;
import com.google.inject.Inject;
import com.googlesource.gerrit.plugins.its.base.its.ItsFacade;
-import com.googlesource.gerrit.plugins.its.base.workflow.Property;
-import java.util.Set;
+import java.util.HashMap;
+import java.util.Map;
import org.apache.commons.lang.StringEscapeUtils;
-/** Extractor to translate the various {@code *Attribute}s to {@link Property Properties}. */
-public class PropertyAttributeExtractor {
- private Property.Factory propertyFactory;
+/** Extractor to translate the various {@code *Attribute}s to properties. */
+class PropertyAttributeExtractor {
private ItsFacade its;
@Inject
- PropertyAttributeExtractor(ItsFacade its, Property.Factory propertyFactory) {
+ PropertyAttributeExtractor(ItsFacade its) {
this.its = its;
- this.propertyFactory = propertyFactory;
}
- public Set<Property> extractFrom(AccountAttribute accountAttribute, String prefix) {
- Set<Property> properties = Sets.newHashSet();
+ Map<String, String> extractFrom(AccountAttribute accountAttribute, String prefix) {
+ Map<String, String> properties = new HashMap<>();
if (accountAttribute != null) {
- // deprecated, to be removed soon. migrate to ones without dash.
- properties.add(propertyFactory.create(prefix + "-email", accountAttribute.email));
- properties.add(propertyFactory.create(prefix + "-username", accountAttribute.username));
- properties.add(propertyFactory.create(prefix + "-name", accountAttribute.name));
-
- // New style configs for vm and soy
- properties.add(propertyFactory.create(prefix + "Email", accountAttribute.email));
- properties.add(propertyFactory.create(prefix + "Username", accountAttribute.username));
- properties.add(propertyFactory.create(prefix + "Name", accountAttribute.name));
+ if (accountAttribute.email != null) {
+ properties.put(prefix + "Email", accountAttribute.email);
+ }
+ if (accountAttribute.username != null) {
+ properties.put(prefix + "Username", accountAttribute.username);
+ }
+ if (accountAttribute.name != null) {
+ properties.put(prefix + "Name", accountAttribute.name);
+ }
}
return properties;
}
- public Set<Property> extractFrom(ChangeAttribute changeAttribute) {
- Set<Property> properties = Sets.newHashSet();
- properties.add(propertyFactory.create("project", changeAttribute.project));
- properties.add(propertyFactory.create("branch", changeAttribute.branch));
- properties.add(propertyFactory.create("topic", changeAttribute.topic));
- properties.add(propertyFactory.create("subject", changeAttribute.subject));
- properties.add(
- propertyFactory.create(
- "escapedSubject", StringEscapeUtils.escapeJava(changeAttribute.subject)));
-
- // deprecated, to be removed soon. migrate to ones without dash.
- properties.add(propertyFactory.create("commit-message", changeAttribute.commitMessage));
- properties.add(propertyFactory.create("change-id", changeAttribute.id));
- properties.add(propertyFactory.create("change-number", String.valueOf(changeAttribute.number)));
- properties.add(propertyFactory.create("change-url", changeAttribute.url));
-
- // New style configs for vm and soy
- properties.add(propertyFactory.create("commitMessage", changeAttribute.commitMessage));
- properties.add(propertyFactory.create("changeId", changeAttribute.id));
- properties.add(propertyFactory.create("changeNumber", String.valueOf(changeAttribute.number)));
- properties.add(propertyFactory.create("changeUrl", changeAttribute.url));
-
- // Soy specfic config though will work with Velocity too
- properties.add(
- propertyFactory.create(
- "formatChangeUrl", its.createLinkForWebui(changeAttribute.url, changeAttribute.url)));
-
- String status = null;
- if (changeAttribute.status != null) {
- status = changeAttribute.status.toString();
- }
- properties.add(propertyFactory.create("status", status));
- properties.addAll(extractFrom(changeAttribute.owner, "owner"));
- return properties;
+ Map<String, String> extractFrom(ChangeAttribute changeAttribute) {
+ return ImmutableMap.<String, String>builder()
+ .put("branch", changeAttribute.branch)
+ .put("topic", changeAttribute.topic != null ? changeAttribute.topic : "")
+ .put("subject", changeAttribute.subject)
+ .put("escapedSubject", StringEscapeUtils.escapeJava(changeAttribute.subject))
+ .put("commitMessage", changeAttribute.commitMessage)
+ .put("changeId", changeAttribute.id)
+ .put("changeNumber", String.valueOf(changeAttribute.number))
+ .put("changeUrl", changeAttribute.url)
+ .put("formatChangeUrl", its.createLinkForWebui(changeAttribute.url, changeAttribute.url))
+ .put("status", changeAttribute.status != null ? changeAttribute.status.toString() : "")
+ .put(
+ "private",
+ changeAttribute.isPrivate != null ? changeAttribute.isPrivate.toString() : "false")
+ .put("wip", changeAttribute.wip != null ? changeAttribute.wip.toString() : "false")
+ .putAll(extractFrom(changeAttribute.owner, "owner"))
+ .build();
}
- public Set<Property> extractFrom(PatchSetAttribute patchSetAttribute) {
- Set<Property> properties = Sets.newHashSet();
- properties.add(propertyFactory.create("revision", patchSetAttribute.revision));
- // deprecated, to be removed soon. migrate to ones without dash.
- properties.add(
- propertyFactory.create("patch-set-number", String.valueOf(patchSetAttribute.number)));
-
- // New style configs for vm and soy
- properties.add(
- propertyFactory.create("patchSetNumber", String.valueOf(patchSetAttribute.number)));
-
- properties.add(propertyFactory.create("ref", patchSetAttribute.ref));
-
- // deprecated, to be removed soon. migrate to ones without dash.
- properties.add(propertyFactory.create("created-on", patchSetAttribute.createdOn.toString()));
-
- // New style configs for vm and soy
- properties.add(propertyFactory.create("createdOn", patchSetAttribute.createdOn.toString()));
-
- properties.add(propertyFactory.create("parents", patchSetAttribute.parents.toString()));
- properties.add(
- propertyFactory.create("deletions", Integer.toString(patchSetAttribute.sizeDeletions)));
- properties.add(
- propertyFactory.create("insertions", Integer.toString(patchSetAttribute.sizeInsertions)));
- properties.addAll(extractFrom(patchSetAttribute.uploader, "uploader"));
- properties.addAll(extractFrom(patchSetAttribute.author, "author"));
- return properties;
+ Map<String, String> extractFrom(PatchSetAttribute patchSetAttribute) {
+ return ImmutableMap.<String, String>builder()
+ .put("revision", patchSetAttribute.revision)
+ .put("patchSetNumber", String.valueOf(patchSetAttribute.number))
+ .put("ref", patchSetAttribute.ref)
+ .put("createdOn", patchSetAttribute.createdOn.toString())
+ .put("parents", patchSetAttribute.parents.toString())
+ .put("deletions", Integer.toString(patchSetAttribute.sizeDeletions))
+ .put("insertions", Integer.toString(patchSetAttribute.sizeInsertions))
+ .putAll(extractFrom(patchSetAttribute.uploader, "uploader"))
+ .putAll(extractFrom(patchSetAttribute.author, "author"))
+ .build();
}
- public Set<Property> extractFrom(RefUpdateAttribute refUpdateAttribute) {
- Set<Property> properties = Sets.newHashSet();
- properties.add(propertyFactory.create("revision", refUpdateAttribute.newRev));
-
- // deprecated, to be removed soon. migrate to ones without dash.
- properties.add(propertyFactory.create("revision-old", refUpdateAttribute.oldRev));
-
- // New style configs for vm and soy
- properties.add(propertyFactory.create("revisionOld", refUpdateAttribute.oldRev));
- properties.add(propertyFactory.create("project", refUpdateAttribute.project));
- properties.add(propertyFactory.create("ref", refUpdateAttribute.refName));
- return properties;
+ Map<String, String> extractFrom(RefUpdateAttribute refUpdateAttribute) {
+ return ImmutableMap.<String, String>builder()
+ .put("revision", refUpdateAttribute.newRev)
+ .put("revisionOld", refUpdateAttribute.oldRev)
+ .put("ref", refUpdateAttribute.refName)
+ .build();
}
- public Set<Property> extractFrom(ApprovalAttribute approvalAttribute) {
- Set<Property> properties = Sets.newHashSet();
- // deprecated, to be removed soon. migrate to ones without dash.
- properties.add(
- propertyFactory.create("approval-" + approvalAttribute.type, approvalAttribute.value));
-
- // New style configs for vm and soy
- properties.add(
- propertyFactory.create(
- "approval" + approvalAttribute.type.replace("-", ""), approvalAttribute.value));
- return properties;
+ public Map<String, String> extractFrom(ApprovalAttribute approvalAttribute) {
+ return ImmutableMap.<String, String>builder()
+ .put("approval" + approvalAttribute.type.replace("-", ""), approvalAttribute.value)
+ .build();
}
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/util/PropertyExtractor.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/util/PropertyExtractor.java
index f24eec4..0a1bfd1 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/util/PropertyExtractor.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/util/PropertyExtractor.java
@@ -14,7 +14,6 @@
package com.googlesource.gerrit.plugins.its.base.util;
-import com.google.common.collect.Sets;
import com.google.gerrit.extensions.annotations.PluginName;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet;
@@ -27,31 +26,36 @@
import com.google.gerrit.server.events.ChangeMergedEvent;
import com.google.gerrit.server.events.ChangeRestoredEvent;
import com.google.gerrit.server.events.CommentAddedEvent;
-import com.google.gerrit.server.events.Event;
import com.google.gerrit.server.events.PatchSetCreatedEvent;
import com.google.gerrit.server.events.PatchSetEvent;
+import com.google.gerrit.server.events.PrivateStateChangedEvent;
+import com.google.gerrit.server.events.RefEvent;
import com.google.gerrit.server.events.RefUpdatedEvent;
+import com.google.gerrit.server.events.WorkInProgressStateChangedEvent;
import com.google.inject.Inject;
-import com.googlesource.gerrit.plugins.its.base.workflow.Property;
+import com.googlesource.gerrit.plugins.its.base.workflow.RefEventProperties;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import org.eclipse.jgit.lib.ObjectId;
-/** Extractor to translate an {@link ChangeEvent} to {@link Property Properties}. */
+/** Extractor to translate an {@link ChangeEvent} to a map of properties}. */
public class PropertyExtractor {
- private IssueExtractor issueExtractor;
- private Property.Factory propertyFactory;
- private PropertyAttributeExtractor propertyAttributeExtractor;
+ private final ItsProjectExtractor itsProjectExtractor;
+ private final IssueExtractor issueExtractor;
+ private final PropertyAttributeExtractor propertyAttributeExtractor;
private final String pluginName;
@Inject
PropertyExtractor(
IssueExtractor issueExtractor,
- Property.Factory propertyFactory,
+ ItsProjectExtractor itsProjectExtractor,
PropertyAttributeExtractor propertyAttributeExtractor,
@PluginName String pluginName) {
this.issueExtractor = issueExtractor;
- this.propertyFactory = propertyFactory;
+ this.itsProjectExtractor = itsProjectExtractor;
this.propertyAttributeExtractor = propertyAttributeExtractor;
this.pluginName = pluginName;
}
@@ -72,40 +76,41 @@
}
}
- private Map<String, Set<String>> extractFrom(PatchSetEvent event, Set<Property> common) {
- common.add(propertyFactory.create("event-type", event.type));
+ private Map<String, Set<String>> extractMapFrom(PatchSetEvent event, Map<String, String> common) {
ChangeAttribute change = event.change.get();
PatchSetAttribute patchSet = event.patchSet.get();
- common.addAll(propertyAttributeExtractor.extractFrom(change));
- common.addAll(propertyAttributeExtractor.extractFrom(patchSet));
+ common.putAll(propertyAttributeExtractor.extractFrom(change));
+ common.putAll(propertyAttributeExtractor.extractFrom(patchSet));
PatchSet.Id patchSetId =
newPatchSetId(Integer.toString(change.number), Integer.toString(patchSet.number));
return issueExtractor.getIssueIds(change.project, patchSet.revision, patchSetId);
}
- private Map<String, Set<String>> extractFrom(ChangeAbandonedEvent event, Set<Property> common) {
- common.addAll(propertyAttributeExtractor.extractFrom(event.abandoner.get(), "abandoner"));
- common.add(propertyFactory.create("reason", event.reason));
- return extractFrom((PatchSetEvent) event, common);
+ private Map<String, Set<String>> extractFrom(
+ ChangeAbandonedEvent event, Map<String, String> common) {
+ common.putAll(propertyAttributeExtractor.extractFrom(event.abandoner.get(), "abandoner"));
+ common.put("reason", event.reason);
+ return extractMapFrom(event, common);
}
- private Map<String, Set<String>> extractFrom(ChangeMergedEvent event, Set<Property> common) {
- common.addAll(propertyAttributeExtractor.extractFrom(event.submitter.get(), "submitter"));
- return extractFrom((PatchSetEvent) event, common);
+ private Map<String, Set<String>> extractFrom(
+ ChangeMergedEvent event, Map<String, String> common) {
+ common.putAll(propertyAttributeExtractor.extractFrom(event.submitter.get(), "submitter"));
+ return extractMapFrom(event, common);
}
- private Map<String, Set<String>> extractFrom(ChangeRestoredEvent event, Set<Property> common) {
- common.addAll(propertyAttributeExtractor.extractFrom(event.restorer.get(), "restorer"));
- common.add(propertyFactory.create("reason", event.reason));
- return extractFrom((PatchSetEvent) event, common);
+ private Map<String, Set<String>> extractFrom(
+ ChangeRestoredEvent event, Map<String, String> common) {
+ common.putAll(propertyAttributeExtractor.extractFrom(event.restorer.get(), "restorer"));
+ common.put("reason", event.reason);
+ return extractMapFrom(event, common);
}
- private Map<String, Set<String>> extractFrom(RefUpdatedEvent event, Set<Property> common) {
- common.add(propertyFactory.create("event-type", event.type));
+ private Map<String, Set<String>> extractFrom(RefUpdatedEvent event, Map<String, String> common) {
if (event.submitter != null) {
- common.addAll(propertyAttributeExtractor.extractFrom(event.submitter.get(), "submitter"));
+ common.putAll(propertyAttributeExtractor.extractFrom(event.submitter.get(), "submitter"));
}
- common.addAll(propertyAttributeExtractor.extractFrom(event.refUpdate.get()));
+ common.putAll(propertyAttributeExtractor.extractFrom(event.refUpdate.get()));
RefUpdateAttribute refUpdateEvent = event.refUpdate.get();
String commitId =
(refUpdateEvent.newRev.equals(ObjectId.zeroId().name())
@@ -114,29 +119,53 @@
return issueExtractor.getIssueIds(event.getProjectNameKey().get(), commitId);
}
- private Map<String, Set<String>> extractFrom(PatchSetCreatedEvent event, Set<Property> common) {
- common.addAll(propertyAttributeExtractor.extractFrom(event.uploader.get(), "uploader"));
- return extractFrom((PatchSetEvent) event, common);
+ private Map<String, Set<String>> extractFrom(
+ PatchSetCreatedEvent event, Map<String, String> common) {
+ common.putAll(propertyAttributeExtractor.extractFrom(event.uploader.get(), "uploader"));
+ return extractMapFrom(event, common);
}
- private Map<String, Set<String>> extractFrom(CommentAddedEvent event, Set<Property> common) {
- common.addAll(propertyAttributeExtractor.extractFrom(event.author.get(), "commenter"));
+ private Map<String, Set<String>> extractFrom(
+ CommentAddedEvent event, Map<String, String> common) {
+ common.putAll(propertyAttributeExtractor.extractFrom(event.author.get(), "commenter"));
+ common.put("comment", event.comment);
if (event.approvals != null) {
for (ApprovalAttribute approvalAttribute : event.approvals.get()) {
- common.addAll(propertyAttributeExtractor.extractFrom(approvalAttribute));
+ common.putAll(propertyAttributeExtractor.extractFrom(approvalAttribute));
}
}
- common.add(propertyFactory.create("comment", event.comment));
- return extractFrom((PatchSetEvent) event, common);
+ return extractMapFrom(event, common);
+ }
+
+ private Map<String, Set<String>> extractFrom(
+ WorkInProgressStateChangedEvent event, Map<String, String> common) {
+ common.putAll(propertyAttributeExtractor.extractFrom(event.changer.get(), "changer"));
+ return extractFrom((ChangeEvent) event, common);
+ }
+
+ private Map<String, Set<String>> extractFrom(
+ PrivateStateChangedEvent event, Map<String, String> common) {
+ common.putAll(propertyAttributeExtractor.extractFrom(event.changer.get(), "changer"));
+ return extractFrom((ChangeEvent) event, common);
+ }
+
+ private Map<String, Set<String>> extractFrom(ChangeEvent event, Map<String, String> common) {
+ common.put("event-type", event.type);
+ ChangeAttribute change = event.change.get();
+ common.putAll(propertyAttributeExtractor.extractFrom(change));
+ common.put("refName", event.refName);
+
+ // Got no patch set information, extract from commit message.
+ return issueExtractor.getIssueIdsFromCommitMessage(change.commitMessage);
}
/**
- * A set of property sets extracted from an event.
+ * A set of properties extracted from an event.
*
- * <p>As events may relate to more that a single issue, and properties sets are should be tied to
- * a single issue, returning {@code Set<Property>} is not sufficient, and we need to return {@code
- * Set<Set<Property>>}. Using this approach, a PatchSetCreatedEvent for a patch set with commit
- * message:
+ * <p>As events may relate to more that a single issue and a group of properties should be tied to
+ * a single issue, we need to return {@code Set<Map>} of properties. As properties we understand a
+ * map of event attributes. Using this approach, a PatchSetCreatedEvent for a patch set with
+ * commit message:
*
* <pre>
* (bug 4711) Fix treatment of special characters in title
@@ -166,15 +195,22 @@
* same event. So in the above example, a comment "mentioned in change 123" may be added for issue
* 42, and a comment "fixed by change 123” may be added for issue 4711.
*
- * @param event The event to extract property sets from.
- * @return sets of property sets extracted from the event.
+ * @param event The event to extract property maps from.
+ * @return set of property maps extracted from the event.
*/
- public Set<Set<Property>> extractFrom(Event event) {
+ public RefEventProperties extractFrom(RefEvent event) {
Map<String, Set<String>> associations = null;
- Set<Set<Property>> ret = Sets.newHashSet();
+ Map<String, String> common = new HashMap<>();
+ common.put("event", event.getClass().getName());
+ String project = event.getProjectNameKey().get();
+ common.put("event-type", event.type);
+ common.put("project", project);
- Set<Property> common = Sets.newHashSet();
- common.add(propertyFactory.create("event", event.getClass().getName()));
+ itsProjectExtractor
+ .getItsProject(project)
+ .ifPresent(itsProject -> common.put("its-project", itsProject));
+ common.put("ref", event.getRefName());
+ common.put("itsName", pluginName);
if (event instanceof ChangeAbandonedEvent) {
associations = extractFrom((ChangeAbandonedEvent) event, common);
@@ -188,23 +224,22 @@
associations = extractFrom((PatchSetCreatedEvent) event, common);
} else if (event instanceof RefUpdatedEvent) {
associations = extractFrom((RefUpdatedEvent) event, common);
+ } else if (event instanceof PrivateStateChangedEvent) {
+ associations = extractFrom((PrivateStateChangedEvent) event, common);
+ } else if (event instanceof WorkInProgressStateChangedEvent) {
+ associations = extractFrom((WorkInProgressStateChangedEvent) event, common);
}
+ Set<Map<String, String>> ret = new HashSet<>();
if (associations != null) {
- for (String issue : associations.keySet()) {
- Set<Property> properties = Sets.newHashSet();
- Property property = propertyFactory.create("issue", issue);
- properties.add(property);
- property = propertyFactory.create("its-name", pluginName);
- properties.add(property);
- for (String occurrence : associations.get(issue)) {
- property = propertyFactory.create("association", occurrence);
- properties.add(property);
- }
- properties.addAll(common);
+ for (Entry<String, Set<String>> assoc : associations.entrySet()) {
+ Map<String, String> properties = new HashMap<>();
+ properties.put("issue", assoc.getKey());
+ properties.put("association", String.join(" ", assoc.getValue()));
+ properties.putAll(common);
ret.add(properties);
}
}
- return ret;
+ return new RefEventProperties(common, ret);
}
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/validation/ItsValidateComment.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/validation/ItsValidateComment.java
index 969eeb0..f02050b 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/validation/ItsValidateComment.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/validation/ItsValidateComment.java
@@ -16,6 +16,7 @@
import com.google.common.collect.Lists;
import com.google.gerrit.extensions.annotations.PluginName;
+import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.events.CommitReceivedEvent;
import com.google.gerrit.server.git.validators.CommitValidationException;
import com.google.gerrit.server.git.validators.CommitValidationListener;
@@ -23,6 +24,7 @@
import com.google.inject.Inject;
import com.googlesource.gerrit.plugins.its.base.its.ItsConfig;
import com.googlesource.gerrit.plugins.its.base.its.ItsFacade;
+import com.googlesource.gerrit.plugins.its.base.its.ItsFacadeFactory;
import com.googlesource.gerrit.plugins.its.base.util.IssueExtractor;
import java.io.IOException;
import java.util.Collections;
@@ -41,9 +43,11 @@
@Inject private ItsConfig itsConfig;
+ @Inject private ItsFacadeFactory itsFacadeFactory;
+
@Inject private IssueExtractor issueExtractor;
- private List<CommitValidationMessage> validCommit(RevCommit commit)
+ private List<CommitValidationMessage> validCommit(Project.NameKey project, RevCommit commit)
throws CommitValidationException {
List<CommitValidationMessage> ret = Lists.newArrayList();
ItsAssociationPolicy associationPolicy = itsConfig.getItsAssociationPolicy();
@@ -57,6 +61,7 @@
String details = null;
if (issueIds.length > 0) {
List<String> nonExistingIssueIds = Lists.newArrayList();
+ client = itsFacadeFactory.getFacade(project);
for (String issueId : issueIds) {
boolean exists = false;
try {
@@ -92,7 +97,10 @@
ret.add(commitValidationFailure(synopsis, details));
}
- } else {
+ } else if (!itsConfig
+ .getDummyIssuePattern()
+ .map(p -> p.matcher(commitMessage).find())
+ .orElse(false)) {
synopsis = "Missing issue-id in commit message";
StringBuilder sb = new StringBuilder();
@@ -132,10 +140,11 @@
@Override
public List<CommitValidationMessage> onCommitReceived(CommitReceivedEvent receiveEvent)
throws CommitValidationException {
- ItsConfig.setCurrentProjectName(receiveEvent.getProjectNameKey());
+ Project.NameKey projectName = receiveEvent.getProjectNameKey();
+ ItsConfig.setCurrentProjectName(projectName);
- if (itsConfig.isEnabled(receiveEvent.getProjectNameKey(), receiveEvent.getRefName())) {
- return validCommit(receiveEvent.commit);
+ if (itsConfig.isEnabled(projectName, receiveEvent.getRefName())) {
+ return validCommit(projectName, receiveEvent.commit);
}
return Collections.emptyList();
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/action/Action.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/Action.java
similarity index 66%
rename from src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/action/Action.java
rename to src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/Action.java
index b8b2102..4198271 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/action/Action.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/Action.java
@@ -12,23 +12,26 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.googlesource.gerrit.plugins.its.base.workflow.action;
+package com.googlesource.gerrit.plugins.its.base.workflow;
-import com.googlesource.gerrit.plugins.its.base.workflow.ActionRequest;
-import com.googlesource.gerrit.plugins.its.base.workflow.Property;
+import com.googlesource.gerrit.plugins.its.base.its.ItsFacade;
import java.io.IOException;
-import java.util.Set;
+import java.util.Map;
/** Interface for actions on an issue tracking system */
public interface Action {
+ /** @return The type of this action */
+ ActionType getType();
/**
* Execute this action.
*
- * @param issue The issue to execute on.
+ * @param its The facade interface to execute actions.
+ * @param target The target to execute on. Its kind will depend on the action type.
* @param actionRequest The request to execute.
* @param properties The properties for the execution.
*/
- public void execute(String issue, ActionRequest actionRequest, Set<Property> properties)
+ void execute(
+ ItsFacade its, String target, ActionRequest actionRequest, Map<String, String> properties)
throws IOException;
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionController.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionController.java
index b1c0982..4af8f1b 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionController.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionController.java
@@ -14,13 +14,17 @@
package com.googlesource.gerrit.plugins.its.base.workflow;
-import com.google.gerrit.common.EventListener;
import com.google.gerrit.server.events.Event;
+import com.google.gerrit.server.events.EventListener;
+import com.google.gerrit.server.events.RefEvent;
import com.google.inject.Inject;
import com.googlesource.gerrit.plugins.its.base.its.ItsConfig;
import com.googlesource.gerrit.plugins.its.base.util.PropertyExtractor;
import java.util.Collection;
+import java.util.Map;
import java.util.Set;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* Controller that takes actions according to {@code ChangeEvents@}.
@@ -29,6 +33,9 @@
* issue's status).
*/
public class ActionController implements EventListener {
+
+ private static final Logger log = LoggerFactory.getLogger(ActionController.class);
+
private final PropertyExtractor propertyExtractor;
private final RuleBase ruleBase;
private final ActionExecutor actionExecutor;
@@ -48,21 +55,48 @@
@Override
public void onEvent(Event event) {
- if (!itsConfig.isEnabled(event)) {
+ if (event instanceof RefEvent) {
+ RefEvent refEvent = (RefEvent) event;
+ if (itsConfig.isEnabled(refEvent)) {
+ handleEvent(refEvent);
+ }
+ }
+ }
+
+ private void handleEvent(RefEvent refEvent) {
+ RefEventProperties refEventProperties = propertyExtractor.extractFrom(refEvent);
+
+ handleIssuesEvent(refEventProperties.getIssuesProperties());
+ handleProjectEvent(refEventProperties.getProjectProperties());
+ }
+
+ private void handleIssuesEvent(Set<Map<String, String>> issuesProperties) {
+ for (Map<String, String> issueProperties : issuesProperties) {
+ Collection<ActionRequest> actions = ruleBase.actionRequestsFor(issueProperties);
+ if (!actions.isEmpty()) {
+ actionExecutor.executeOnIssue(actions, issueProperties);
+ }
+ }
+ }
+
+ private void handleProjectEvent(Map<String, String> projectProperties) {
+ if (projectProperties.isEmpty()) {
return;
}
- Set<Set<Property>> propertiesCollections = propertyExtractor.extractFrom(event);
- for (Set<Property> properties : propertiesCollections) {
- Collection<ActionRequest> actions = ruleBase.actionRequestsFor(properties);
- if (!actions.isEmpty()) {
- for (Property property : properties) {
- if ("issue".equals(property.getKey())) {
- String issue = property.getValue();
- actionExecutor.execute(issue, actions, properties);
- }
- }
- }
+ Collection<ActionRequest> projectActions = ruleBase.actionRequestsFor(projectProperties);
+ if (projectActions.isEmpty()) {
+ return;
}
+ if (!projectProperties.containsKey("its-project")) {
+ String project = projectProperties.get("project");
+ log.error(
+ "Could not process project event. No its-project associated with project {}. "
+ + "Did you forget to configure the ITS project association in project.config?",
+ project);
+ return;
+ }
+
+ actionExecutor.executeOnProject(projectActions, projectProperties);
}
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionExecutor.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionExecutor.java
index 0e31b93..355d1f3 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionExecutor.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionExecutor.java
@@ -14,16 +14,14 @@
package com.googlesource.gerrit.plugins.its.base.workflow;
+import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.registration.PluginName;
+import com.google.gerrit.reviewdb.client.Project;
import com.google.inject.Inject;
import com.googlesource.gerrit.plugins.its.base.its.ItsFacade;
-import com.googlesource.gerrit.plugins.its.base.workflow.action.Action;
-import com.googlesource.gerrit.plugins.its.base.workflow.action.AddComment;
-import com.googlesource.gerrit.plugins.its.base.workflow.action.AddSoyComment;
-import com.googlesource.gerrit.plugins.its.base.workflow.action.AddStandardComment;
-import com.googlesource.gerrit.plugins.its.base.workflow.action.AddVelocityComment;
-import com.googlesource.gerrit.plugins.its.base.workflow.action.LogEvent;
+import com.googlesource.gerrit.plugins.its.base.its.ItsFacadeFactory;
import java.io.IOException;
-import java.util.Set;
+import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -31,58 +29,103 @@
public class ActionExecutor {
private static final Logger log = LoggerFactory.getLogger(ActionExecutor.class);
- private final ItsFacade its;
+ private final ItsFacadeFactory itsFactory;
private final AddComment.Factory addCommentFactory;
private final AddStandardComment.Factory addStandardCommentFactory;
- private final AddVelocityComment.Factory addVelocityCommentFactory;
private final AddSoyComment.Factory addSoyCommentFactory;
private final LogEvent.Factory logEventFactory;
+ private final AddPropertyToField.Factory addPropertyToFieldFactory;
+ private final CreateVersionFromProperty.Factory createVersionFromPropertyFactory;
+ private final DynamicMap<CustomAction> customActions;
@Inject
public ActionExecutor(
- ItsFacade its,
+ ItsFacadeFactory itsFactory,
AddComment.Factory addCommentFactory,
AddStandardComment.Factory addStandardCommentFactory,
- AddVelocityComment.Factory addVelocityCommentFactory,
AddSoyComment.Factory addSoyCommentFactory,
- LogEvent.Factory logEventFactory) {
- this.its = its;
+ LogEvent.Factory logEventFactory,
+ AddPropertyToField.Factory addPropertyToFieldFactory,
+ CreateVersionFromProperty.Factory createVersionFromPropertyFactory,
+ DynamicMap<CustomAction> customActions) {
+ this.itsFactory = itsFactory;
this.addCommentFactory = addCommentFactory;
this.addStandardCommentFactory = addStandardCommentFactory;
- this.addVelocityCommentFactory = addVelocityCommentFactory;
this.addSoyCommentFactory = addSoyCommentFactory;
this.logEventFactory = logEventFactory;
+ this.addPropertyToFieldFactory = addPropertyToFieldFactory;
+ this.createVersionFromPropertyFactory = createVersionFromPropertyFactory;
+ this.customActions = customActions;
}
- public void execute(String issue, ActionRequest actionRequest, Set<Property> properties) {
- try {
- String name = actionRequest.getName();
- Action action = null;
- if ("add-comment".equals(name)) {
- action = addCommentFactory.create();
- } else if ("add-standard-comment".equals(name)) {
- action = addStandardCommentFactory.create();
- } else if ("add-velocity-comment".equals(name)) {
- action = addVelocityCommentFactory.create();
- } else if ("add-soy-comment".equals(name)) {
- action = addSoyCommentFactory.create();
- } else if ("log-event".equals(name)) {
- action = logEventFactory.create();
- }
+ private Action getAction(String actionName) {
+ switch (actionName) {
+ case "add-comment":
+ return addCommentFactory.create();
+ case "add-standard-comment":
+ return addStandardCommentFactory.create();
+ case "add-soy-comment":
+ return addSoyCommentFactory.create();
+ case "log-event":
+ return logEventFactory.create();
+ case "add-property-to-field":
+ return addPropertyToFieldFactory.create();
+ case "create-version-from-property":
+ return createVersionFromPropertyFactory.create();
+ default:
+ return customActions.get(PluginName.GERRIT, actionName);
+ }
+ }
+ private void execute(
+ Action action, String target, ActionRequest actionRequest, Map<String, String> properties)
+ throws IOException {
+ ItsFacade its = itsFactory.getFacade(new Project.NameKey(properties.get("project")));
+ action.execute(its, target, actionRequest, properties);
+ }
+
+ private void executeOnIssue(
+ String issue, ActionRequest actionRequest, Map<String, String> properties) {
+ try {
+ Action action = getAction(actionRequest.getName());
if (action == null) {
+ ItsFacade its = itsFactory.getFacade(new Project.NameKey(properties.get("project")));
its.performAction(issue, actionRequest.getUnparsed());
- } else {
- action.execute(issue, actionRequest, properties);
+ } else if (action.getType() == ActionType.ISSUE) {
+ execute(action, issue, actionRequest, properties);
}
} catch (IOException e) {
log.error("Error while executing action " + actionRequest, e);
}
}
- public void execute(String issue, Iterable<ActionRequest> actions, Set<Property> properties) {
+ public void executeOnIssue(Iterable<ActionRequest> actions, Map<String, String> properties) {
for (ActionRequest actionRequest : actions) {
- execute(issue, actionRequest, properties);
+ executeOnIssue(properties.get("issue"), actionRequest, properties);
+ }
+ }
+
+ private void executeOnProject(
+ String itsProject, ActionRequest actionRequest, Map<String, String> properties) {
+ try {
+ String actionName = actionRequest.getName();
+ Action action = getAction(actionName);
+ if (action == null) {
+ log.debug("No action found for name {}", actionName);
+ return;
+ }
+ if (action.getType() != ActionType.PROJECT) {
+ return;
+ }
+ execute(action, itsProject, actionRequest, properties);
+ } catch (IOException e) {
+ log.error("Error while executing action " + actionRequest, e);
+ }
+ }
+
+ public void executeOnProject(Iterable<ActionRequest> actions, Map<String, String> properties) {
+ for (ActionRequest actionRequest : actions) {
+ executeOnProject(properties.get("its-project"), actionRequest, properties);
}
}
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionRequest.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionRequest.java
index dec671e..964c01d 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionRequest.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionRequest.java
@@ -49,11 +49,7 @@
* @return The name of the requested action, if a name has been given. "" otherwise.
*/
public String getName() {
- String ret = "";
- if (chopped.length > 0) {
- ret = chopped[0];
- }
- return ret;
+ return getParameter(0);
}
/**
@@ -63,11 +59,7 @@
* @return The name of the requested parameter, if the requested parameter exists. "" otherwise.
*/
public String getParameter(int i) {
- String ret = "";
- if (chopped.length > i) {
- ret = chopped[i];
- }
- return ret;
+ return chopped.length > i ? chopped[i] : "";
}
/**
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionType.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionType.java
new file mode 100644
index 0000000..8d18cce
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionType.java
@@ -0,0 +1,22 @@
+// Copyright (C) 2018 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.its.base.workflow;
+
+public enum ActionType {
+ /** Actions that will be executed on ITS issues */
+ ISSUE,
+ /** Actions that will be executed on ITS projects */
+ PROJECT
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/action/AddComment.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddComment.java
similarity index 61%
rename from src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/action/AddComment.java
rename to src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddComment.java
index 16855d3..2624613 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/action/AddComment.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddComment.java
@@ -12,39 +12,28 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.googlesource.gerrit.plugins.its.base.workflow.action;
+package com.googlesource.gerrit.plugins.its.base.workflow;
import com.google.common.base.Strings;
-import com.google.inject.Inject;
import com.googlesource.gerrit.plugins.its.base.its.ItsFacade;
-import com.googlesource.gerrit.plugins.its.base.workflow.ActionRequest;
-import com.googlesource.gerrit.plugins.its.base.workflow.Property;
import java.io.IOException;
-import java.util.Set;
-import org.apache.commons.lang.StringUtils;
+import java.util.Map;
/**
* Adds a fixed comment to an issue.
*
* <p>The action requests parameters get concatenated and get added to the issue.
*/
-public class AddComment implements Action {
+public class AddComment extends IssueAction {
public interface Factory {
AddComment create();
}
- private final ItsFacade its;
-
- @Inject
- public AddComment(ItsFacade its) {
- this.its = its;
- }
-
@Override
- public void execute(String issue, ActionRequest actionRequest, Set<Property> properties)
+ public void execute(
+ ItsFacade its, String issue, ActionRequest actionRequest, Map<String, String> properties)
throws IOException {
- String[] parameters = actionRequest.getParameters();
- String comment = StringUtils.join(parameters, " ");
+ String comment = String.join(" ", actionRequest.getParameters());
if (!Strings.isNullOrEmpty(comment)) {
its.addComment(issue, comment);
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddPropertyToField.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddPropertyToField.java
new file mode 100644
index 0000000..c19aa73
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddPropertyToField.java
@@ -0,0 +1,47 @@
+// Copyright (C) 2018 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.its.base.workflow;
+
+import com.google.inject.Inject;
+import com.googlesource.gerrit.plugins.its.base.its.ItsFacade;
+import java.io.IOException;
+import java.util.Map;
+import java.util.Optional;
+
+public class AddPropertyToField extends IssueAction {
+
+ public interface Factory {
+ AddPropertyToField create();
+ }
+
+ private final AddPropertyToFieldParametersExtractor parametersExtractor;
+
+ @Inject
+ public AddPropertyToField(AddPropertyToFieldParametersExtractor parametersExtractor) {
+ this.parametersExtractor = parametersExtractor;
+ }
+
+ @Override
+ public void execute(
+ ItsFacade its, String issue, ActionRequest actionRequest, Map<String, String> properties)
+ throws IOException {
+ Optional<AddPropertyToFieldParameters> parameters =
+ parametersExtractor.extract(actionRequest, properties);
+ if (!parameters.isPresent()) {
+ return;
+ }
+ its.addValueToField(issue, parameters.get().getPropertyValue(), parameters.get().getFieldId());
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddPropertyToFieldParameters.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddPropertyToFieldParameters.java
new file mode 100644
index 0000000..55b029d
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddPropertyToFieldParameters.java
@@ -0,0 +1,37 @@
+// Copyright (C) 2018 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.its.base.workflow;
+
+/** Parameters needed by {@link AddPropertyToField} action */
+public class AddPropertyToFieldParameters {
+
+ private final String propertyValue;
+ private final String fieldId;
+
+ public AddPropertyToFieldParameters(String propertyValue, String fieldId) {
+ this.propertyValue = propertyValue;
+ this.fieldId = fieldId;
+ }
+
+ /** @return The event property's value to add to the ITS field */
+ public String getPropertyValue() {
+ return propertyValue;
+ }
+
+ /** @return The id of the ITS field to which the property value is added */
+ public String getFieldId() {
+ return fieldId;
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddPropertyToFieldParametersExtractor.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddPropertyToFieldParametersExtractor.java
new file mode 100644
index 0000000..7a259f1
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddPropertyToFieldParametersExtractor.java
@@ -0,0 +1,67 @@
+// Copyright (C) 2018 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.its.base.workflow;
+
+import com.google.common.base.Strings;
+import com.google.inject.Inject;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Optional;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AddPropertyToFieldParametersExtractor {
+
+ private static final Logger log =
+ LoggerFactory.getLogger(AddPropertyToFieldParametersExtractor.class);
+
+ @Inject
+ public AddPropertyToFieldParametersExtractor() {}
+
+ /**
+ * @return The parameters needed to perform an AddPropertyToField action. Empty if the parameters
+ * could not be extracted.
+ */
+ public Optional<AddPropertyToFieldParameters> extract(
+ ActionRequest actionRequest, Map<String, String> properties) {
+ String[] parameters = actionRequest.getParameters();
+ if (parameters.length != 2) {
+ log.error(
+ "Wrong number of received parameters. Received parameters are {}. Exactly two parameters are expected. The first one is the ITS field id, the second one is the event property id",
+ Arrays.toString(parameters));
+ return Optional.empty();
+ }
+
+ String propertyId = parameters[0];
+ if (Strings.isNullOrEmpty(propertyId)) {
+ log.error("Received property id is blank");
+ return Optional.empty();
+ }
+
+ String fieldId = parameters[1];
+ if (Strings.isNullOrEmpty(fieldId)) {
+ log.error("Received field id is blank");
+ return Optional.empty();
+ }
+
+ if (!properties.containsKey(propertyId)) {
+ log.error("No event property found for id {}", propertyId);
+ return Optional.empty();
+ }
+
+ String propertyValue = properties.get(propertyId);
+ return Optional.of(new AddPropertyToFieldParameters(propertyValue, fieldId));
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddSoyComment.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddSoyComment.java
new file mode 100644
index 0000000..c29d622
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddSoyComment.java
@@ -0,0 +1,105 @@
+// Copyright (C) 2017 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.its.base.workflow;
+
+import com.google.common.base.Strings;
+import com.google.common.io.CharStreams;
+import com.google.inject.Inject;
+import com.google.inject.ProvisionException;
+import com.google.template.soy.SoyFileSet;
+import com.google.template.soy.SoyFileSet.Builder;
+import com.google.template.soy.data.SanitizedContent;
+import com.google.template.soy.tofu.SoyTofu;
+import com.googlesource.gerrit.plugins.its.base.ItsPath;
+import com.googlesource.gerrit.plugins.its.base.its.ItsFacade;
+import java.io.IOException;
+import java.io.Reader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.Map;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Adds a short predefined comments to an issue.
+ *
+ * <p>Comments are added for merging, abandoning, restoring of changes and adding of patch sets.
+ */
+public class AddSoyComment extends IssueAction {
+ private static final Logger log = LoggerFactory.getLogger(AddSoyComment.class);
+
+ public interface Factory {
+ AddSoyComment create();
+ }
+
+ private final Path templateDir;
+ protected HashMap<String, Object> soyContext;
+
+ @Inject
+ public AddSoyComment(@ItsPath Path itsPath) {
+ this.templateDir = itsPath.resolve("templates");
+ }
+
+ private String soyTemplate(
+ SoyFileSet.Builder builder,
+ String template,
+ SanitizedContent.ContentKind kind,
+ Map<String, String> properties) {
+ Path templatePath = templateDir.resolve(template + ".soy");
+ String content;
+
+ try (Reader r = Files.newBufferedReader(templatePath, StandardCharsets.UTF_8)) {
+ content = CharStreams.toString(r);
+ } catch (IOException err) {
+ throw new ProvisionException(
+ "Failed to read template file " + templatePath.toAbsolutePath().toString(), err);
+ }
+
+ builder.add(content, templatePath.toAbsolutePath().toString());
+ SoyTofu.Renderer renderer =
+ builder
+ .build()
+ .compileToTofu()
+ .newRenderer("etc.its.templates." + template)
+ .setContentKind(kind)
+ .setData(properties);
+ return renderer.render();
+ }
+
+ private String soyTextTemplate(Builder builder, String template, Map<String, String> properties) {
+ return soyTemplate(builder, template, SanitizedContent.ContentKind.TEXT, properties);
+ }
+
+ @Override
+ public void execute(
+ ItsFacade its, String issue, ActionRequest actionRequest, Map<String, String> properties)
+ throws IOException {
+ String comment = buildComment(actionRequest, properties);
+ if (!Strings.isNullOrEmpty(comment)) {
+ its.addComment(issue, comment);
+ }
+ }
+
+ private String buildComment(ActionRequest actionRequest, Map<String, String> properties) {
+ String template = actionRequest.getParameter(1);
+ if (!template.isEmpty()) {
+ return soyTextTemplate(SoyFileSet.builder(), template, properties);
+ }
+ log.error("No template name given in {}", actionRequest);
+ return "";
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddStandardComment.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddStandardComment.java
new file mode 100644
index 0000000..d289024
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddStandardComment.java
@@ -0,0 +1,92 @@
+// Copyright (C) 2017 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.its.base.workflow;
+
+import com.google.common.base.Strings;
+import com.googlesource.gerrit.plugins.its.base.its.ItsFacade;
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * Adds a short predefined comments to an issue.
+ *
+ * <p>Comments are added for merging, abandoning, restoring of changes and adding of patch sets.
+ */
+public class AddStandardComment extends IssueAction {
+ public interface Factory {
+ AddStandardComment create();
+ }
+
+ private String getCommentChangeEvent(String action, String prefix, Map<String, String> map) {
+ String ret = "";
+ String changeNumber = Strings.nullToEmpty(map.get("changeNumber"));
+ if (!changeNumber.isEmpty()) {
+ changeNumber += " ";
+ }
+ ret += "Change " + changeNumber + action;
+ String submitter = getValueFromMap(map, prefix, "Name", "Username");
+ if (!submitter.isEmpty()) {
+ ret += " by " + submitter;
+ }
+ String subject = Strings.nullToEmpty(map.get("subject"));
+ if (!subject.isEmpty()) {
+ ret += ":\n" + subject;
+ }
+ String reason = Strings.nullToEmpty(map.get("reason"));
+ if (!reason.isEmpty()) {
+ ret += "\n\nReason:\n" + reason;
+ }
+ String url = Strings.nullToEmpty(map.get("formatChangeUrl"));
+ if (!url.isEmpty()) {
+ ret += "\n\n" + url;
+ }
+ return ret;
+ }
+
+ private String getValueFromMap(Map<String, String> map, String keyPrefix, String... keyOptions) {
+ for (String key : keyOptions) {
+ String ret = Strings.nullToEmpty(map.get(keyPrefix + key));
+ if (!ret.isEmpty()) {
+ return ret;
+ }
+ }
+ return "";
+ }
+
+ @Override
+ public void execute(
+ ItsFacade its, String issue, ActionRequest actionRequest, Map<String, String> properties)
+ throws IOException {
+ String comment = buildComment(properties);
+ if (!Strings.isNullOrEmpty(comment)) {
+ its.addComment(issue, comment);
+ }
+ }
+
+ private String buildComment(Map<String, String> properties) {
+ switch (properties.get("event-type")) {
+ case "change-abandoned":
+ return getCommentChangeEvent("abandoned", "abandoner", properties);
+ case "change-merged":
+ return getCommentChangeEvent("merged", "submitter", properties);
+ case "change-restored":
+ return getCommentChangeEvent("restored", "restorer", properties);
+ case "patchset-created":
+ return getCommentChangeEvent("had a related patch set uploaded", "uploader", properties);
+ default:
+ return "";
+ }
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/Condition.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/Condition.java
index 6f8df84..e527652 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/Condition.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/Condition.java
@@ -20,9 +20,9 @@
import com.google.gerrit.common.Nullable;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
-import com.googlesource.gerrit.plugins.its.base.workflow.action.Action;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Set;
/**
@@ -87,13 +87,12 @@
* conditions, true iff properties does not contain any property that matches the rules key
* and whose value matches at least one of the rule's value.
*/
- public boolean isMetBy(Iterable<Property> properties) {
- for (Property property : properties) {
- String propertyKey = property.getKey();
- if ((key == null && propertyKey == null) || (key != null && key.equals(propertyKey))) {
- if (values.contains(property.getValue())) {
- return !negated;
- }
+ public boolean isMetBy(Map<String, String> properties) {
+ String property = properties.get(key);
+ String[] propertyValues = property != null ? property.split(" ") : new String[] {};
+ for (String p : propertyValues) {
+ if (values.contains(p.trim())) {
+ return !negated;
}
}
return negated;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/CreateVersionFromProperty.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/CreateVersionFromProperty.java
new file mode 100644
index 0000000..91cd34d
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/CreateVersionFromProperty.java
@@ -0,0 +1,50 @@
+// Copyright (C) 2018 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.its.base.workflow;
+
+import com.google.inject.Inject;
+import com.googlesource.gerrit.plugins.its.base.its.ItsFacade;
+import java.io.IOException;
+import java.util.Map;
+import java.util.Optional;
+
+/** Creates a version in the ITS. The value of the version is extracted from an event property. */
+public class CreateVersionFromProperty extends ProjectAction {
+
+ public interface Factory {
+ CreateVersionFromProperty create();
+ }
+
+ private final CreateVersionFromPropertyParametersExtractor parametersExtractor;
+
+ @Inject
+ public CreateVersionFromProperty(
+ CreateVersionFromPropertyParametersExtractor parametersExtractor) {
+ this.parametersExtractor = parametersExtractor;
+ }
+
+ @Override
+ public void execute(
+ ItsFacade its, String itsProject, ActionRequest actionRequest, Map<String, String> properties)
+ throws IOException {
+ Optional<CreateVersionFromPropertyParameters> parameters =
+ parametersExtractor.extract(actionRequest, properties);
+ if (!parameters.isPresent()) {
+ return;
+ }
+
+ its.createVersion(itsProject, parameters.get().getPropertyValue());
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/CreateVersionFromPropertyParameters.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/CreateVersionFromPropertyParameters.java
new file mode 100644
index 0000000..2ddc245
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/CreateVersionFromPropertyParameters.java
@@ -0,0 +1,30 @@
+// Copyright (C) 2018 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.its.base.workflow;
+
+/** Parameters needed by {@link CreateVersionFromProperty} action */
+public class CreateVersionFromPropertyParameters {
+
+ private final String propertyValue;
+
+ public CreateVersionFromPropertyParameters(String propertyValue) {
+ this.propertyValue = propertyValue;
+ }
+
+ /** @return The extracted property value that will be used as the version value */
+ public String getPropertyValue() {
+ return propertyValue;
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/CreateVersionFromPropertyParametersExtractor.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/CreateVersionFromPropertyParametersExtractor.java
new file mode 100644
index 0000000..1eb01ce
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/CreateVersionFromPropertyParametersExtractor.java
@@ -0,0 +1,57 @@
+// Copyright (C) 2018 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.its.base.workflow;
+
+import com.google.common.base.Strings;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Optional;
+import javax.inject.Inject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class CreateVersionFromPropertyParametersExtractor {
+
+ private static final Logger log =
+ LoggerFactory.getLogger(CreateVersionFromPropertyParametersExtractor.class);
+
+ @Inject
+ public CreateVersionFromPropertyParametersExtractor() {}
+
+ public Optional<CreateVersionFromPropertyParameters> extract(
+ ActionRequest actionRequest, Map<String, String> properties) {
+ String[] parameters = actionRequest.getParameters();
+ if (parameters.length != 1) {
+ log.error(
+ "Wrong number of received parameters. Received parameters are {}. Only one parameter is expected, the property id.",
+ Arrays.toString(parameters));
+ return Optional.empty();
+ }
+
+ String propertyId = parameters[0];
+ if (Strings.isNullOrEmpty(propertyId)) {
+ log.error("Received property id is blank");
+ return Optional.empty();
+ }
+
+ if (!properties.containsKey(propertyId)) {
+ log.error("No event property found for id {}", propertyId);
+ return Optional.empty();
+ }
+
+ String propertyValue = properties.get(propertyId);
+ return Optional.of(new CreateVersionFromPropertyParameters(propertyValue));
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/CustomAction.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/CustomAction.java
new file mode 100644
index 0000000..5a79650
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/CustomAction.java
@@ -0,0 +1,21 @@
+// Copyright (C) 2018 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.its.base.workflow;
+
+import com.google.gerrit.extensions.annotations.ExtensionPoint;
+
+/** Interface for actions specific to its-* plugins * */
+@ExtensionPoint
+public interface CustomAction extends Action {}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/IssueAction.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/IssueAction.java
new file mode 100644
index 0000000..37707ad
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/IssueAction.java
@@ -0,0 +1,24 @@
+// Copyright (C) 2018 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.its.base.workflow;
+
+/** Abstraction for actions on ITS issues */
+public abstract class IssueAction implements StandardAction {
+
+ @Override
+ public final ActionType getType() {
+ return ActionType.ISSUE;
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ItsRulesProjectCache.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ItsRulesProjectCache.java
new file mode 100644
index 0000000..265032b
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ItsRulesProjectCache.java
@@ -0,0 +1,22 @@
+package com.googlesource.gerrit.plugins.its.base.workflow;
+
+import java.util.List;
+
+/** Cache of project defined ITS rules */
+public interface ItsRulesProjectCache {
+
+ /**
+ * Get the cached ITS rules for a project
+ *
+ * @param projectName name of the project.
+ * @return the cached rules; an empty list if no such project exists or projectName is null.
+ */
+ List<Rule> get(String projectName);
+
+ /**
+ * Invalidate the cached rules for the given project.
+ *
+ * @param projectName project for which the rules are being evicted
+ */
+ void evict(String projectName);
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ItsRulesProjectCacheImpl.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ItsRulesProjectCacheImpl.java
new file mode 100644
index 0000000..7e37397
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ItsRulesProjectCacheImpl.java
@@ -0,0 +1,123 @@
+// Copyright (C) 2018 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.its.base.workflow;
+
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
+import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.cache.CacheModule;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectState;
+import com.google.inject.Inject;
+import com.google.inject.Module;
+import com.google.inject.Singleton;
+import com.google.inject.TypeLiteral;
+import com.google.inject.name.Named;
+import com.googlesource.gerrit.plugins.its.base.GlobalRulesFileName;
+import com.googlesource.gerrit.plugins.its.base.PluginRulesFileName;
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import org.eclipse.jgit.lib.Config;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Singleton
+public class ItsRulesProjectCacheImpl implements ItsRulesProjectCache {
+ private static final Logger log = LoggerFactory.getLogger(ItsRulesProjectCacheImpl.class);
+ private static final String CACHE_NAME = "its_rules_project";
+
+ private final LoadingCache<String, List<Rule>> cache;
+
+ @Inject
+ ItsRulesProjectCacheImpl(@Named(CACHE_NAME) LoadingCache<String, List<Rule>> cache) {
+ this.cache = cache;
+ }
+
+ @Override
+ public List<Rule> get(String projectName) {
+ try {
+ return cache.get(projectName);
+ } catch (ExecutionException e) {
+ log.warn("Cannot get project specific rules for project {}", projectName, e);
+ return ImmutableList.of();
+ }
+ }
+
+ @Override
+ public void evict(String projectName) {
+ cache.invalidate(projectName);
+ }
+
+ public static Module module() {
+ return new CacheModule() {
+ @Override
+ protected void configure() {
+ cache(CACHE_NAME, String.class, new TypeLiteral<List<Rule>>() {}).loader(Loader.class);
+
+ bind(ItsRulesProjectCacheImpl.class);
+ bind(ItsRulesProjectCache.class).to(ItsRulesProjectCacheImpl.class);
+ DynamicSet.bind(binder(), GitReferenceUpdatedListener.class)
+ .to(ItsRulesProjectCacheRefresher.class);
+ }
+ };
+ }
+
+ static class Loader extends CacheLoader<String, List<Rule>> {
+ private final String globalRulesFileName;
+ private final String pluginRulesFileName;
+ private final ProjectCache projectCache;
+ private final RulesConfigReader rulesConfigReader;
+
+ @Inject
+ Loader(
+ @GlobalRulesFileName String globalRulesFileName,
+ @PluginRulesFileName String pluginRulesFileName,
+ ProjectCache projectCache,
+ RulesConfigReader rulesConfigReader) {
+ this.globalRulesFileName = globalRulesFileName;
+ this.pluginRulesFileName = pluginRulesFileName;
+ this.projectCache = projectCache;
+ this.rulesConfigReader = rulesConfigReader;
+ }
+
+ @Override
+ public List<Rule> load(String projectName) throws IOException {
+ ProjectState project = projectCache.checkedGet(new Project.NameKey(projectName));
+ List<Rule> projectRules = readRulesFrom(project);
+ if (projectRules.isEmpty()) {
+ for (ProjectState parent : project.parents()) {
+ projectRules = readRulesFrom(parent);
+ if (!projectRules.isEmpty()) {
+ break;
+ }
+ }
+ }
+ return projectRules;
+ }
+
+ private List<Rule> readRulesFrom(ProjectState project) {
+ Config general = project.getConfig(globalRulesFileName).get();
+ Config pluginSpecific = project.getConfig(pluginRulesFileName).get();
+ return new ImmutableList.Builder<Rule>()
+ .addAll(rulesConfigReader.getRulesFromConfig(general))
+ .addAll(rulesConfigReader.getRulesFromConfig(pluginSpecific))
+ .build();
+ }
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ItsRulesProjectCacheRefresher.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ItsRulesProjectCacheRefresher.java
new file mode 100644
index 0000000..4cbd3d2
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ItsRulesProjectCacheRefresher.java
@@ -0,0 +1,39 @@
+package com.googlesource.gerrit.plugins.its.base.workflow;
+
+import com.google.gerrit.extensions.api.GerritApi;
+import com.google.gerrit.extensions.common.ProjectInfo;
+import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
+import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.inject.Inject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ItsRulesProjectCacheRefresher implements GitReferenceUpdatedListener {
+ private static final Logger log = LoggerFactory.getLogger(ItsRulesProjectCacheRefresher.class);
+
+ private final GerritApi gApi;
+ private final ItsRulesProjectCache itsRuleProjectCache;
+
+ @Inject
+ ItsRulesProjectCacheRefresher(GerritApi gApi, ItsRulesProjectCache itsRuleProjectCache) {
+ this.gApi = gApi;
+ this.itsRuleProjectCache = itsRuleProjectCache;
+ }
+
+ @Override
+ public void onGitReferenceUpdated(Event event) {
+ if (!event.getRefName().equals(RefNames.REFS_CONFIG)) {
+ return;
+ }
+ String projectName = event.getProjectName();
+ itsRuleProjectCache.evict(projectName);
+ try {
+ for (ProjectInfo childProject : gApi.projects().name(projectName).children()) {
+ itsRuleProjectCache.evict(childProject.name);
+ }
+ } catch (RestApiException e) {
+ log.warn("Unable to evict ITS rules cache", e);
+ }
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/action/LogEvent.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/LogEvent.java
similarity index 60%
rename from src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/action/LogEvent.java
rename to src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/LogEvent.java
index 3a46202..6eec9a4 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/action/LogEvent.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/LogEvent.java
@@ -12,13 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.googlesource.gerrit.plugins.its.base.workflow.action;
+package com.googlesource.gerrit.plugins.its.base.workflow;
import com.google.inject.Inject;
-import com.googlesource.gerrit.plugins.its.base.workflow.ActionRequest;
-import com.googlesource.gerrit.plugins.its.base.workflow.Property;
+import com.googlesource.gerrit.plugins.its.base.its.ItsFacade;
import java.io.IOException;
-import java.util.Set;
+import java.util.Map;
+import java.util.Map.Entry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -27,14 +27,25 @@
*
* <p>This event helps when developing rules as available properties become visible.
*/
-public class LogEvent implements Action {
+public class LogEvent extends IssueAction {
private static final Logger log = LoggerFactory.getLogger(LogEvent.class);
private enum Level {
ERROR,
WARN,
INFO,
- DEBUG
+ DEBUG;
+
+ static Level fromString(String s) {
+ if (s != null) {
+ for (Level level : Level.values()) {
+ if (s.toUpperCase().equals(level.toString())) {
+ return level;
+ }
+ }
+ }
+ return INFO;
+ }
}
public interface Factory {
@@ -44,8 +55,8 @@
@Inject
public LogEvent() {}
- private void logProperty(Level level, Property property) {
- String message = property.toString();
+ private void logProperty(Level level, Entry<String, String> property) {
+ String message = String.format("[%s = %s]", property.getKey(), property.getValue());
switch (level) {
case ERROR:
log.error(message);
@@ -65,24 +76,11 @@
}
@Override
- public void execute(String issue, ActionRequest actionRequest, Set<Property> properties)
+ public void execute(
+ ItsFacade its, String issue, ActionRequest actionRequest, Map<String, String> properties)
throws IOException {
- String levelParameter = actionRequest.getParameter(1);
- if (levelParameter != null) {
- levelParameter = levelParameter.toLowerCase();
- }
- Level level = Level.INFO;
- if ("error".equals(levelParameter)) {
- level = Level.ERROR;
- } else if ("warn".equals(levelParameter)) {
- level = Level.WARN;
- } else if ("info".equals(levelParameter)) {
- level = Level.INFO;
- } else if ("debug".equals(levelParameter)) {
- level = Level.DEBUG;
- }
-
- for (Property property : properties) {
+ Level level = Level.fromString(actionRequest.getParameter(1));
+ for (Entry<String, String> property : properties.entrySet()) {
logProperty(level, property);
}
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ProjectAction.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ProjectAction.java
new file mode 100644
index 0000000..5867377
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ProjectAction.java
@@ -0,0 +1,24 @@
+// Copyright (C) 2018 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.its.base.workflow;
+
+/** Abstraction for actions on ITS projects */
+public abstract class ProjectAction implements StandardAction {
+
+ @Override
+ public final ActionType getType() {
+ return ActionType.PROJECT;
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/Property.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/Property.java
deleted file mode 100644
index eb1f087..0000000
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/Property.java
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright (C) 2013 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.its.base.workflow;
-
-import com.google.gerrit.common.Nullable;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-
-/**
- * A property to match against {@code Condition}s.
- *
- * <p>A property is a simple key value pair.
- */
-public class Property {
- public interface Factory {
- Property create(@Assisted("key") String key, @Assisted("value") String value);
- }
-
- private final String key;
- private final String value;
-
- @Inject
- public Property(@Assisted("key") String key, @Nullable @Assisted("value") String value) {
- this.key = key;
- this.value = value;
- }
-
- public String getKey() {
- return key;
- }
-
- public String getValue() {
- return value;
- }
-
- @Override
- public boolean equals(Object other) {
- boolean ret = false;
- if (other != null && other instanceof Property) {
- Property otherProperty = (Property) other;
- ret = true;
-
- if (key == null) {
- ret &= otherProperty.getKey() == null;
- } else {
- ret &= key.equals(otherProperty.getKey());
- }
-
- if (value == null) {
- ret &= otherProperty.getValue() == null;
- } else {
- ret &= value.equals(otherProperty.getValue());
- }
- }
- return ret;
- }
-
- @Override
- public int hashCode() {
- return (key == null ? 0 : key.hashCode()) * 31 + (value == null ? 0 : value.hashCode());
- }
-
- @Override
- public String toString() {
- return "[" + key + " = " + value + "]";
- }
-}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/RefEventProperties.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/RefEventProperties.java
new file mode 100644
index 0000000..30940bb
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/RefEventProperties.java
@@ -0,0 +1,50 @@
+// Copyright (C) 2018 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.its.base.workflow;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+/** The properties extracted from a {@link com.google.gerrit.server.events.RefEvent} */
+public class RefEventProperties {
+
+ private final Map<String, String> projectProperties;
+ private final Set<Map<String, String>> issuesProperties;
+
+ /**
+ * @param projectProperties Properties of the ref event
+ * @param issuesProperties Properties of the ref event added of the properties specific to the
+ * issues. There will be as many set of properties as number of issues
+ */
+ public RefEventProperties(
+ Map<String, String> projectProperties, Set<Map<String, String>> issuesProperties) {
+ this.projectProperties = Collections.unmodifiableMap(projectProperties);
+ this.issuesProperties = Collections.unmodifiableSet(issuesProperties);
+ }
+
+ /** @return Properties of the ref event */
+ public Map<String, String> getProjectProperties() {
+ return projectProperties;
+ }
+
+ /**
+ * @return Properties of the ref event added of the properties specific to the issues. There will
+ * be as many set of properties as number of issues
+ */
+ public Set<Map<String, String>> getIssuesProperties() {
+ return issuesProperties;
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/Rule.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/Rule.java
index 3efa269..b83b969 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/Rule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/Rule.java
@@ -21,6 +21,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Set;
/** A single rule that associates {@code Action}s to {@code Condition}s. */
@@ -71,7 +72,7 @@
* @param properties The properties to check against the rule's conditions.
* @return The actions that should get fired.
*/
- public Collection<ActionRequest> actionRequestsFor(Iterable<Property> properties) {
+ public Collection<ActionRequest> actionRequestsFor(Map<String, String> properties) {
for (Condition condition : conditions) {
if (!condition.isMetBy(properties)) {
return Collections.emptyList();
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/RuleBase.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/RuleBase.java
index 41dc9fa..b6e4ad6 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/RuleBase.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/RuleBase.java
@@ -14,42 +14,31 @@
package com.googlesource.gerrit.plugins.its.base.workflow;
-import com.google.common.collect.Lists;
-import com.google.gerrit.extensions.annotations.PluginName;
-import com.google.gerrit.server.config.SitePath;
+import com.google.common.collect.ImmutableList;
import com.google.inject.Inject;
+import com.googlesource.gerrit.plugins.its.base.GlobalRulesFileName;
+import com.googlesource.gerrit.plugins.its.base.ItsPath;
+import com.googlesource.gerrit.plugins.its.base.PluginRulesFileName;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
+import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.eclipse.jgit.util.FS;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-/** Collection and matcher agains {@link Rule}s. */
+/** Collection and matcher against {@link Rule}s. */
public class RuleBase {
private static final Logger log = LoggerFactory.getLogger(RuleBase.class);
- /** File beginning (relative to site) to load rules from */
- private static final String ITS_CONFIG_FILE_START =
- "etc" + File.separatorChar + "its" + File.separator + "actions";
-
- /** File end to load rules from */
- private static final String ITS_CONFIG_FILE_END = ".config";
-
- /** The section for rules within rulebases */
- private static final String RULE_SECTION = "rule";
-
- /** The key for actions within rulebases */
- private static final String ACTION_KEY = "action";
-
- private final Path sitePath;
- private final Rule.Factory ruleFactory;
- private final Condition.Factory conditionFactory;
- private final ActionRequest.Factory actionRequestFactory;
- private final String pluginName;
+ private final File globalRuleFile;
+ private final File itsSpecificRuleFile;
+ private final ItsRulesProjectCache rulesProjectCache;
private Collection<Rule> rules;
@@ -59,107 +48,43 @@
@Inject
public RuleBase(
- @SitePath Path sitePath,
- Rule.Factory ruleFactory,
- Condition.Factory conditionFactory,
- ActionRequest.Factory actionRequestFactory,
- @PluginName String pluginName) {
- this.sitePath = sitePath;
- this.ruleFactory = ruleFactory;
- this.conditionFactory = conditionFactory;
- this.actionRequestFactory = actionRequestFactory;
- this.pluginName = pluginName;
- reloadRules();
+ @ItsPath Path itsPath,
+ @GlobalRulesFileName String globalRulesFileName,
+ @PluginRulesFileName String pluginRulesFileName,
+ ItsRulesProjectCache rulesProjectCache,
+ RulesConfigReader rulesConfigReader) {
+ this.globalRuleFile = itsPath.resolve(globalRulesFileName).toFile();
+ this.itsSpecificRuleFile = itsPath.resolve(pluginRulesFileName).toFile();
+ this.rulesProjectCache = rulesProjectCache;
+ this.rules =
+ new ImmutableList.Builder<Rule>()
+ .addAll(getRulesFromFile(rulesConfigReader, globalRuleFile))
+ .addAll(getRulesFromFile(rulesConfigReader, itsSpecificRuleFile))
+ .build();
}
/**
- * Adds rules from a file to the the RuleBase.
+ * Gets rules from a file.
*
* <p>If the given file does not exist, it is silently ignored
*
+ * @param rulesConfigReader The rules reader
* @param ruleFile File from which to read the rules
+ * @return A collection of rules or an empty collection if the file does not exist or contains an
+ * invalid configuration
*/
- private void addRulesFromFile(File ruleFile) {
+ private static Collection<Rule> getRulesFromFile(
+ RulesConfigReader rulesConfigReader, File ruleFile) {
if (ruleFile.exists()) {
FileBasedConfig cfg = new FileBasedConfig(ruleFile, FS.DETECTED);
try {
cfg.load();
+ return rulesConfigReader.getRulesFromConfig(cfg);
} catch (IOException | ConfigInvalidException e) {
log.error("Invalid ITS action configuration", e);
- return;
- }
-
- Collection<String> subsections = cfg.getSubsections(RULE_SECTION);
- for (String subsection : subsections) {
- Rule rule = ruleFactory.create(subsection);
- Collection<String> keys = cfg.getNames(RULE_SECTION, subsection);
- for (String key : keys) {
- String[] values = cfg.getStringList(RULE_SECTION, subsection, key);
- if (ACTION_KEY.equals(key)) {
- for (String value : values) {
- ActionRequest actionRequest = actionRequestFactory.create(value);
- rule.addActionRequest(actionRequest);
- }
- } else {
- for (String value : values) {
- Condition condition = conditionFactory.create(key, value);
- rule.addCondition(condition);
- }
- }
- }
- rules.add(rule);
}
}
- }
-
- /** Loads the rules for the RuleBase. */
- private void reloadRules() {
- rules = Lists.newArrayList();
-
- // Add rules from file with typo in filename
- //
- // While the documentation called for "actions.config" (Trailing "s" in
- // "actions"), the code previously only loaded "action.config" (No
- // trailing "s" in "action"). To give users time to gracefully migrate to
- // "actions.config" (with trailing "s", we (for now) load files from both
- // locations, but consider "actions.config" (with trailing "s" the
- // canonical place.
- File faultyNameRuleFile =
- new File(
- sitePath.toFile(),
- "etc" + File.separatorChar + "its" + File.separator + "action.config");
- if (faultyNameRuleFile.exists()) {
- log.warn(
- "Loading rules from deprecated 'etc/its/action.config' (No "
- + "trailing 's' in 'action'). Please migrate to "
- + "'etc/its/actions.config' (Trailing 's' in 'actions').");
- addRulesFromFile(faultyNameRuleFile);
- }
-
- // Add global rules
- File globalRuleFile = new File(sitePath.toFile(), ITS_CONFIG_FILE_START + ITS_CONFIG_FILE_END);
- addRulesFromFile(globalRuleFile);
-
- // Add its-specific rules
- File itsSpecificRuleFile =
- new File(sitePath.toFile(), ITS_CONFIG_FILE_START + "-" + pluginName + ITS_CONFIG_FILE_END);
- addRulesFromFile(itsSpecificRuleFile);
-
- if (!globalRuleFile.exists() && !itsSpecificRuleFile.exists()) {
- try {
- log.warn(
- "Neither global rule file "
- + globalRuleFile.getCanonicalPath()
- + " nor Its specific rule file"
- + itsSpecificRuleFile.getCanonicalPath()
- + " exist. Please configure "
- + "rules.");
- } catch (IOException e) {
- log.warn(
- "Neither global rule file nor Its specific rule files exist. "
- + "Please configure rules.");
- }
- }
+ return Collections.emptyList();
}
/**
@@ -168,11 +93,23 @@
* @param properties The properties to search actions for.
* @return Requests for the actions that should be fired.
*/
- public Collection<ActionRequest> actionRequestsFor(Iterable<Property> properties) {
- Collection<ActionRequest> ret = Lists.newLinkedList();
- for (Rule rule : rules) {
- ret.addAll(rule.actionRequestsFor(properties));
+ public Collection<ActionRequest> actionRequestsFor(Map<String, String> properties) {
+ String projectName = properties.get("project");
+ Collection<Rule> fromProjectConfig = rulesProjectCache.get(projectName);
+ Collection<Rule> rulesToAdd = !fromProjectConfig.isEmpty() ? fromProjectConfig : rules;
+ if (rulesToAdd.isEmpty() && !globalRuleFile.exists() && !itsSpecificRuleFile.exists()) {
+ log.warn(
+ "Neither global rule file {} nor Its specific rule file {} exist and no rules are "
+ + "configured for project {}. Please configure rules.",
+ globalRuleFile,
+ itsSpecificRuleFile,
+ projectName);
+ return Collections.emptyList();
}
- return ret;
+ Collection<ActionRequest> actions = new ArrayList<>();
+ for (Rule rule : rulesToAdd) {
+ actions.addAll(rule.actionRequestsFor(properties));
+ }
+ return actions;
}
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/RulesConfigReader.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/RulesConfigReader.java
new file mode 100644
index 0000000..e4c0f0c
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/RulesConfigReader.java
@@ -0,0 +1,72 @@
+// Copyright (C) 2018 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.its.base.workflow;
+
+import com.google.inject.Inject;
+import java.util.ArrayList;
+import java.util.Collection;
+import org.eclipse.jgit.lib.Config;
+
+public class RulesConfigReader {
+
+ /** The section for rules within rulebases */
+ static final String RULE_SECTION = "rule";
+
+ /** The key for actions within rulebases */
+ static final String ACTION_KEY = "action";
+
+ private final Rule.Factory ruleFactory;
+ private final Condition.Factory conditionFactory;
+ private final ActionRequest.Factory actionRequestFactory;
+
+ @Inject
+ RulesConfigReader(
+ Rule.Factory ruleFactory,
+ Condition.Factory conditionFactory,
+ ActionRequest.Factory actionRequestFactory) {
+ this.ruleFactory = ruleFactory;
+ this.conditionFactory = conditionFactory;
+ this.actionRequestFactory = actionRequestFactory;
+ }
+
+ Collection<Rule> getRulesFromConfig(Config cfg) {
+ Collection<Rule> rules = new ArrayList<>();
+ for (String subsection : cfg.getSubsections(RULE_SECTION)) {
+ Rule rule = ruleFactory.create(subsection);
+ for (String key : cfg.getNames(RULE_SECTION, subsection)) {
+ String[] values = cfg.getStringList(RULE_SECTION, subsection, key);
+ if (ACTION_KEY.equals(key)) {
+ addActions(rule, values);
+ } else {
+ addConditions(rule, key, values);
+ }
+ }
+ rules.add(rule);
+ }
+ return rules;
+ }
+
+ private void addActions(Rule rule, String[] values) {
+ for (String value : values) {
+ rule.addActionRequest(actionRequestFactory.create(value));
+ }
+ }
+
+ private void addConditions(Rule rule, String key, String[] values) {
+ for (String value : values) {
+ rule.addCondition(conditionFactory.create(key, value));
+ }
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/StandardAction.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/StandardAction.java
new file mode 100644
index 0000000..eb23693
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/StandardAction.java
@@ -0,0 +1,18 @@
+// Copyright (C) 2013 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.its.base.workflow;
+
+/** Interface for actions defined by base module */
+interface StandardAction extends Action {}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/action/AddSoyComment.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/action/AddSoyComment.java
deleted file mode 100644
index bbad6e9..0000000
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/action/AddSoyComment.java
+++ /dev/null
@@ -1,131 +0,0 @@
-// Copyright (C) 2017 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.its.base.workflow.action;
-
-import com.google.common.base.Strings;
-import com.google.common.io.CharStreams;
-import com.google.gerrit.server.config.SitePath;
-import com.google.inject.Inject;
-import com.google.inject.ProvisionException;
-import com.google.template.soy.SoyFileSet;
-import com.google.template.soy.data.SanitizedContent;
-import com.google.template.soy.tofu.SoyTofu;
-import com.googlesource.gerrit.plugins.its.base.its.ItsFacade;
-import com.googlesource.gerrit.plugins.its.base.workflow.ActionRequest;
-import com.googlesource.gerrit.plugins.its.base.workflow.Property;
-import java.io.File;
-import java.io.IOException;
-import java.io.Reader;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.HashMap;
-import java.util.Set;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Adds a short predefined comments to an issue.
- *
- * <p>Comments are added for merging, abandoning, restoring of changes and adding of patch sets.
- */
-public class AddSoyComment implements Action {
- private static final Logger log = LoggerFactory.getLogger(AddSoyComment.class);
-
- public interface Factory {
- AddSoyComment create();
- }
-
- /** Directory (relative to site) to search templates in */
- private static final String ITS_TEMPLATE_DIR =
- "etc" + File.separator + "its" + File.separator + "templates";
-
- private final ItsFacade its;
- private final Path sitePath;
- protected HashMap<String, Object> soyContext;
-
- @Inject
- public AddSoyComment(@SitePath Path sitePath, ItsFacade its) {
- this.sitePath = sitePath;
- this.its = its;
- }
-
- private HashMap<String, Object> getSoyContext(Set<Property> properties) {
- HashMap<String, Object> soyContext = new HashMap<>();
- for (Property property : properties) {
- String key = property.getKey();
- if (!Strings.isNullOrEmpty(key)) {
- String value = property.getValue();
- if (!Strings.isNullOrEmpty(value)) {
- soyContext.put(key, value);
- }
- }
- }
-
- return soyContext;
- }
-
- private String soyTemplate(
- SoyFileSet.Builder builder,
- String template,
- SanitizedContent.ContentKind kind,
- Set<Property> properties) {
- Path templateDir = sitePath.resolve(ITS_TEMPLATE_DIR);
- Path templatePath = templateDir.resolve(template + ".soy");
- String content;
-
- try (Reader r = Files.newBufferedReader(templatePath, StandardCharsets.UTF_8)) {
- content = CharStreams.toString(r);
- } catch (IOException err) {
- throw new ProvisionException(
- "Failed to read template file " + templatePath.toAbsolutePath().toString(), err);
- }
-
- builder.add(content, templatePath.toAbsolutePath().toString());
-
- HashMap<String, Object> context = getSoyContext(properties);
-
- SoyTofu.Renderer renderer =
- builder
- .build()
- .compileToTofu()
- .newRenderer("etc.its.templates." + template)
- .setContentKind(kind)
- .setData(context);
- return renderer.render();
- }
-
- protected String soyTextTemplate(
- SoyFileSet.Builder builder, String template, Set<Property> properties) {
- return soyTemplate(builder, template, SanitizedContent.ContentKind.TEXT, properties);
- }
-
- @Override
- public void execute(String issue, ActionRequest actionRequest, Set<Property> properties)
- throws IOException {
- SoyFileSet.Builder builder = SoyFileSet.builder();
- String template = null;
- String templateName = actionRequest.getParameter(1);
- if (templateName.isEmpty()) {
- log.error("No template name given in {}", actionRequest);
- } else {
- template = templateName;
- }
- if (!Strings.isNullOrEmpty(template)) {
- String comment = soyTextTemplate(builder, template, properties);
- its.addComment(issue, comment);
- }
- }
-}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/action/AddStandardComment.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/action/AddStandardComment.java
deleted file mode 100644
index eb8c4a6..0000000
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/action/AddStandardComment.java
+++ /dev/null
@@ -1,136 +0,0 @@
-// Copyright (C) 2017 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.its.base.workflow.action;
-
-import com.google.common.base.Strings;
-import com.google.common.collect.Maps;
-import com.google.inject.Inject;
-import com.googlesource.gerrit.plugins.its.base.its.ItsFacade;
-import com.googlesource.gerrit.plugins.its.base.workflow.ActionRequest;
-import com.googlesource.gerrit.plugins.its.base.workflow.Property;
-import java.io.IOException;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Adds a short predefined comments to an issue.
- *
- * <p>Comments are added for merging, abandoning, restoring of changes and adding of patch sets.
- */
-public class AddStandardComment implements Action {
- public interface Factory {
- AddStandardComment create();
- }
-
- private final ItsFacade its;
-
- @Inject
- public AddStandardComment(ItsFacade its) {
- this.its = its;
- }
-
- private String formatPerson(String prefix, Map<String, String> map) {
- String ret = Strings.nullToEmpty(map.get(prefix + "-name"));
- ret = Strings.nullToEmpty(map.get(prefix + "Name"));
- if (ret.isEmpty()) {
- ret = Strings.nullToEmpty(map.get(prefix + "-username"));
- ret = Strings.nullToEmpty(map.get(prefix + "Username"));
- }
- return ret;
- }
-
- private String getCommentChangeEvent(String action, String prefix, Map<String, String> map) {
- String ret = "";
- String changeNumber = getValueFromMap(map, "", "change-number", "changeNumber");
- if (!changeNumber.isEmpty()) {
- changeNumber += " ";
- }
- ret += "Change " + changeNumber + action;
- String submitter = getValueFromMap(map, prefix, "-name", "Name", "-username", "Username");
- if (!submitter.isEmpty()) {
- ret += " by " + submitter;
- }
- String subject = Strings.nullToEmpty(map.get("subject"));
- if (!subject.isEmpty()) {
- ret += ":\n" + subject;
- }
- String reason = Strings.nullToEmpty(map.get("reason"));
- if (!reason.isEmpty()) {
- ret += "\n\nReason:\n" + reason;
- }
- String url = getValueFromMap(map, "", "change-url", "changeUrl");
- if (!url.isEmpty()) {
- ret += "\n\n" + its.createLinkForWebui(url, url);
- }
- return ret;
- }
-
- private String getValueFromMap(Map<String, String> map, String keyPrefix, String... keyOptions) {
- for (String key : keyOptions) {
- String ret = Strings.nullToEmpty(map.get(keyPrefix + key));
- if (!ret.isEmpty()) {
- return ret;
- }
- }
- return "";
- }
-
- private String getCommentChangeAbandoned(Map<String, String> map) {
- return getCommentChangeEvent("abandoned", "abandoner", map);
- }
-
- private String getCommentChangeMerged(Map<String, String> map) {
- return getCommentChangeEvent("merged", "submitter", map);
- }
-
- private String getCommentChangeRestored(Map<String, String> map) {
- return getCommentChangeEvent("restored", "restorer", map);
- }
-
- private String getCommentPatchSetCreated(Map<String, String> map) {
- return getCommentChangeEvent("had a related patch set uploaded", "uploader", map);
- }
-
- @Override
- public void execute(String issue, ActionRequest actionRequest, Set<Property> properties)
- throws IOException {
- String comment = "";
- Map<String, String> map = Maps.newHashMap();
- for (Property property : properties) {
- String current = property.getValue();
- if (!Strings.isNullOrEmpty(current)) {
- String key = property.getKey();
- String old = Strings.nullToEmpty(map.get(key));
- if (!old.isEmpty()) {
- old += ", ";
- }
- map.put(key, old + current);
- }
- }
- String eventType = map.get("event-type");
- if ("change-abandoned".equals(eventType)) {
- comment = getCommentChangeAbandoned(map);
- } else if ("change-merged".equals(eventType)) {
- comment = getCommentChangeMerged(map);
- } else if ("change-restored".equals(eventType)) {
- comment = getCommentChangeRestored(map);
- } else if ("patchset-created".equals(eventType)) {
- comment = getCommentPatchSetCreated(map);
- }
- if (!Strings.isNullOrEmpty(comment)) {
- its.addComment(issue, comment);
- }
- }
-}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/action/AddVelocityComment.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/action/AddVelocityComment.java
deleted file mode 100644
index 13a6655..0000000
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/action/AddVelocityComment.java
+++ /dev/null
@@ -1,150 +0,0 @@
-// Copyright (C) 2013 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.its.base.workflow.action;
-
-import com.google.common.base.Strings;
-import com.google.gerrit.server.config.SitePath;
-import com.google.inject.Inject;
-import com.googlesource.gerrit.plugins.its.base.its.ItsFacade;
-import com.googlesource.gerrit.plugins.its.base.workflow.ActionRequest;
-import com.googlesource.gerrit.plugins.its.base.workflow.Property;
-import java.io.File;
-import java.io.IOException;
-import java.io.StringWriter;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Arrays;
-import java.util.Set;
-import org.apache.commons.lang.StringUtils;
-import org.apache.velocity.VelocityContext;
-import org.apache.velocity.runtime.RuntimeInstance;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Adds a short predefined comments to an issue.
- *
- * <p>Comments are added for merging, abandoning, restoring of changes and adding of patch sets.
- */
-public class AddVelocityComment implements Action {
- private static final Logger log = LoggerFactory.getLogger(AddVelocityComment.class);
-
- public interface Factory {
- AddVelocityComment create();
- }
-
- /** Directory (relative to site) to search templates in */
- private static final String ITS_TEMPLATE_DIR =
- "etc" + File.separator + "its" + File.separator + "templates";
-
- private final ItsFacade its;
- private final Path sitePath;
- private final RuntimeInstance velocityRuntime;
-
- @Inject
- public AddVelocityComment(
- RuntimeInstance velocityRuntime, @SitePath Path sitePath, ItsFacade its) {
- this.velocityRuntime = velocityRuntime;
- this.sitePath = sitePath;
- this.its = its;
- }
-
- private VelocityContext getVelocityContext(Set<Property> properties) {
- VelocityContext velocityContext = new VelocityContext();
- for (Property property : properties) {
- String key = property.getKey();
- if (!Strings.isNullOrEmpty(key)) {
- String value = property.getValue();
- if (!Strings.isNullOrEmpty(value)) {
- velocityContext.put(key, value);
- }
- }
- }
-
- velocityContext.put("its", new VelocityAdapterItsFacade(its));
-
- return velocityContext;
- }
-
- private String velocify(String template, Set<Property> properties) {
- VelocityContext context = getVelocityContext(properties);
- StringWriter w = new StringWriter();
- velocityRuntime.evaluate(context, w, "ItsComment", template);
- return w.toString();
- }
-
- @Override
- public void execute(String issue, ActionRequest actionRequest, Set<Property> properties)
- throws IOException {
- String template = null;
- String templateName = actionRequest.getParameter(1);
- if ("inline".equals(templateName)) {
- String[] allParameters = actionRequest.getParameters();
- String[] templateParameters = Arrays.copyOfRange(allParameters, 1, allParameters.length);
- template = StringUtils.join(templateParameters, " ");
- } else {
- if (templateName.isEmpty()) {
- log.error("No template name given in {}", actionRequest);
- } else {
- Path templateDir = sitePath.resolve(ITS_TEMPLATE_DIR);
- Path templatePath = templateDir.resolve(templateName + ".vm");
- if (Files.isReadable(templatePath)) {
- template = new String(Files.readAllBytes(templatePath));
- } else {
- log.error("Cannot read template {}", templatePath);
- }
- }
- }
- if (!Strings.isNullOrEmpty(template)) {
- String comment = velocify(template, properties);
- its.addComment(issue, comment);
- }
- }
-
- /** Adapter for ItsFacade to be used through Velocity */
- // Although we'd prefer to keep this class private, Velocity will only pick
- // it up, if it is public.
- public class VelocityAdapterItsFacade {
-
- private final ItsFacade facade;
-
- private VelocityAdapterItsFacade(ItsFacade facade) {
- this.facade = facade;
- }
-
- /**
- * Format a link to a URL in the used Its' syntax.
- *
- * @param url URL to link to
- * @param caption Text used to represent the link
- * @return Link to the given URL in the used Its' syntax.
- */
- public String formatLink(String url, String caption) {
- return facade.createLinkForWebui(url, caption);
- }
-
- /**
- * Format a link to an URL.
- *
- * <p>The provided URL is used as caption for the formatted link.
- *
- * @param url URL to link to
- * @return Link to the given URL in the used Its' syntax.
- */
- public String formatLink(String url) {
- return facade.createLinkForWebui(url, url);
- }
- }
-}
diff --git a/src/main/resources/Documentation/config-common.md b/src/main/resources/Documentation/config-common.md
index d53b0c6..666481f 100644
--- a/src/main/resources/Documentation/config-common.md
+++ b/src/main/resources/Documentation/config-common.md
@@ -1,17 +1,17 @@
-Common configuration for `its-base`-based plugins
-=================================================
+# Common configuration for `its-base`-based plugins
-#### Table of Contents
-* [Identifying ITS ids][identifying-its-ids]
-* [Enabling ITS integration][enabling-its-integration]
-* [Configuring rules of when to take which actions in the ITS][configure-rules]
-* [Further common configuration details][config-common-detail]
+[TOC]: # "Table of Contents"
+
+### Table of Contents
+- [Identifying ITS ids](#identifying-its-ids)
+- [Enabling ITS integration](#enabling-its-integration)
+- [Associating a Gerrit project with its ITS project counterpart](#associating-a-gerrit-project-with-its-its-project-counterpart)
+- [Configuring rules of when to take which actions in the ITS](#configuring-rules-of-when-to-take-which-actions-in-the-its)
+- [Multiple Its](#multiple-its)
+- [Further common configuration details](#further-common-configuration-details)
-
-[identifying-its-ids]: #identifying-its-ids
-<a name="identifying-its-ids">Identifying ITS ids</a>
------------------------------------------------------
+## Identifying ITS ids
In order to extract ITS ids from commit messages, @PLUGIN@ uses
[commentlink][upstream-comment-link-doc]s of
@@ -35,15 +35,15 @@
Sample commit message relating to bug 4711, and bug 167.
```
-[upstream-comment-link-doc]: ../../../Documentation/config-gerrit.html#commentlink
+[upstream-comment-link-doc](../../../Documentation/config-gerrit.html#commentlink)
By setting a `commentlink`'s `association` on the plugin's @PLUGIN@ configuration, it
is possible to require commits to carry ITS references; the following
values are supported (default is `OPTIONAL`):
MANDATORY
-: One or more issue-ids are required in the git commit message, otherwise
- the git push will be rejected.
+: One or more issue-ids are required in the git commit message. The git push will
+ be rejected otherwise.
SUGGESTED
: Whenever the git commit message does not contain one or more issue-ids,
@@ -72,9 +72,7 @@
using the same syntax used in the gerrit.config. Project's hierarchy will be respected
when evaluating the links configuration and association policy.
-[enabling-its-integration]: #enabling-its-integration
-<a name="enabling-its-integration">Enabling ITS integration</a>
----------------------------------------------------------------
+## Enabling ITS integration
It can be configured per project whether the issue tracker
integration is enabled or not. To enable the issue tracker integration
@@ -122,11 +120,22 @@
branch = ^refs/heads/stable-.*
```
+## Associating a Gerrit project with its ITS project counterpart
+To be able to make use of actions acting at the ITS project level, you must
+associate a Gerrit project to its ITS project counterpart.
-[configure-rules]: #configure-rules
-<a name="configure-rules">Configuring rules of when to take which actions in the ITS</a>
-----------------------------------------------------------------------------------------
+It must be configured per project and per plugin. To configure the association
+for a project mapping to an ITS project named `manhattan-project`, the project
+must have the following entry in its `project.config` file in the
+`refs/meta/config` branch:
+
+```
+ [plugin "@PLUGIN@"]
+ its-project = manhattan-project
+```
+
+## Configuring rules of when to take which actions in the ITS
Setting up which event in Gerrit (E.g.: “Change Merged”, or “User
‘John Doe’ voted ‘+2’ for ‘Code-Review’ on a change”) should trigger
@@ -138,9 +147,7 @@
-[multiple-its]: #multiple-its
-<a name="mutiple-its">Multiple ITS</a>
---------------------------------------
+## Multiple ITS
Although not a common setup the @PLUGIN@ plugin supports connecting
Gerrit to multiple issue tracking systems.
@@ -160,15 +167,12 @@
just need to use the appropriate name to configure each plugin.
+## Further common configuration details
-[config-common-detail]: #config-common-detail
-<a name="config-common-detail">Further common configuration details</a>
------------------------------------------------------------------------
+[common-config-commentlink](#common-config-commentlink)
+[common-config-commentlinkGroupIndex](#common-config-commentlinkGroupIndex)
-[common-config-commentlink]: #common-config-commentlink
-[common-config-commentlinkGroupIndex]: #common-config-commentlinkGroupIndex
-
-<a name="common-config-commentlink">`@PLUGIN@.commentlink`
+<a name="common-config-commentlink">`@PLUGIN@.commentlink`</a>
: The name of the comment link to use to extract issue ids.
This setting is useful to reuse the same comment link from different Its
@@ -178,13 +182,19 @@
Default is `@PLUGIN@`
-<a name="common-config-commentlinkGroupIndex">`@PLUGIN@.commentlinkGroupIndex`
+<a name="common-config-commentlinkGroupIndex">`@PLUGIN@.commentlinkGroupIndex`</a>
: The group index within `@PLUGIN@.commentlink` that holds the issue id.
Default is `1`, if there are are groups within the regular expression for
the `@PLUGIN@.commentlink` comment link, and the default is `0`, if there
are no such groups.
+<a name="common-config-dummyIssuePattern">`@PLUGIN@.dummyIssuePattern`</a>
+: Pattern which can be specified to match a dummy issue.
+
+ This setting is useful to bypass the MANDATORY check for commits matching
+ a specific pattern.
+
[Back to @PLUGIN@ documentation index][index]
[index]: index.html
diff --git a/src/main/resources/Documentation/config-rulebase-common.md b/src/main/resources/Documentation/config-rulebase-common.md
index 16f2de5..17baf77 100644
--- a/src/main/resources/Documentation/config-rulebase-common.md
+++ b/src/main/resources/Documentation/config-rulebase-common.md
@@ -1,16 +1,17 @@
-Rule base configuration
-=======================
+# Rule base configuration
-#### Table of Contents
-* [Overview][overview]
-* [Rules][rules]
-* [Conditions][conditions]
-* [Event Properties][event-properties]
-* [Actions][actions]
+[TOC]: # "Table of Contents"
-[overview]: #overview
-<a name="overview">Overview</a>
--------------------------------
+### Table of Contents
+- [Overview](#overview)
+- [Rule Bases Scope](#rule-bases-scope)
+- [Rules](#rules)
+- [Conditions](#conditions)
+- [Event Properties](#event-properties)
+- [Actions](#actions)
+
+
+## Overview
In this part we describe how to specify which events in Gerrit (E.g.:
“Change Merged”, or “User ‘John Doe’ voted ‘+2’ for ‘Code-Review’ on a
@@ -18,16 +19,12 @@
‘Resolved’”) on the ITS.
Actions on the ITS and conditions for the action to take place are
-configured through the rule bases in `etc/its/actions.config` (for global rules
-to be picked up by all configured ITS plugins) and
-`etc/its/actions-@PLUGIN@.config` (for rules to be picked up only by @PLUGIN@)
-in the site directory. A rule base is a git config file, and may contain an
-arbitrary number of rules. Each rule can have an arbitrary number of conditions
-and actions. A rule fires all associated actions, once all of its conditions are
-met.
+configured through rule bases. A rule base is a git config file and
+may contain an arbitrary number of rules. Each rule can have an
+arbitrary number of conditions and actions. A rule fires all associated
+actions, once all of its conditions are met.
-A simple `etc/its/actions.config` (or
-`etc/its/actions-@PLUGIN@.config`) may look like
+A simple rule bases file may look like
```
[rule "rule1"]
@@ -47,13 +44,92 @@
Goodness! Someone gave a negative code review in Gerrit on an
associated change.” to each such issue.
-The order of rules in `etc/its/actions.config` need not be
-respected. So in the above example, do not rely on `rule1` being
-evaluated before `rule2`.
+The order of rules in a rule base file need not be respected. So in the
+above example, do not rely on `rule1` being evaluated before `rule2`.
-[rules]: #rules
-<a name="rules">Rules</a>
--------------------------
+## Rule bases scope
+
+Rule bases can be defined in two scopes:
+
+* Global
+* Plugin specific
+
+Global rules are picked up by all configured ITS plugins and they are
+defined in a rule base file named `actions.config`.
+
+Plugin specific rules are picked up only by @PLUGIN@ plugin and they
+are defined in a rule base file named `actions-@PLUGIN@.config`.
+
+A second aspect of rules bases scope is what projects they apply to;
+in this case the rules can be:
+
+* Generic
+* Project specific
+
+The generic scope refers to rules that apply to all projects that
+enable ITS integration on the gerrit site; they are defined on rule
+base files located inside the `gerrit_site/etc/its/` folder.
+
+Project-specific rules are defined on rule base files located on the
+`refs/meta/config` branch of a project and they apply exclusively to
+the project that defines them (or that inherits them) and, possibly,
+to child projects (see further explanations about rules inheritance
+below). Project specific rules take precedence over generic rules,
+i.e, when they are defined, generic rules do not apply to the project.
+
+Thus, to define global generic rules, i.e., rules that are picked up
+by all the ITS plugins and that apply to all the projects that enable
+any ITS integration, they should be defined in the
+`gerrit_site/etc/its/actions.config` rule base file.
+
+Rules defined in the `gerrit_site/etc/its/actions-@PLUGIN@.config` rule
+base file have generic but plugin specific scope, i.e., they apply to
+all projects on the gerrit site that enable integration with @PLUGIN@.
+
+On the other hand, if the rule base file `actions.config` is created
+on the `refs/meta/config` branch of project 'P', the rules defined
+on this file will have global but project specific scope, i.e, they
+apply to all the ITS integrations defined for this project. Thus, if
+project 'P' integrates with ITS system 'x' and with ITS system 'y',
+the rules are applied to these two ITS integrations.
+
+Contrarily, rules defined on the rule base file `actions-@PLUGIN@.config`
+created on the `refs/meta/config` branch of project 'P' have project
+and plugin specific scope, i.e., they apply only to the @PLUGIN@
+integration defined for project 'P'.
+
+Finally, is important to notice that if global and plugin specific rules
+are defined, the final set of rules applied is the merge of them and this
+is true either if they are defined in generic or project specific scope.
+
+[rules-inheritance]: #rules-inheritance
+### <a name="rules-inheritance">Rules inheritance</a>
+
+For project specific rules, i.e., those defined on the `refs/meta/config`
+branch of a project, inheritance is honored, similar to what is done in
+other cases of project configurations.
+
+Thus, if project 'P' defines project specific rules, these are applied
+to children projects of project 'P' that enable an ITS integration.
+
+This inheritance, however, is capped at the closest level, which means
+that if a project defines at least one of the rule bases files,
+(`actions.config` or `actions-@PLUGIN@.config`), the presence of these
+files is not evaluated for any of the project's parents.
+
+Thence, and continuing the example, if project 'Q' is a child of
+project 'P' and project 'Q' enables ITS integration, rules defined in
+project 'P' apply to project 'Q'. If however, project 'Q' defines its
+own set of rules, either on file `actions.config` or
+`actions-@PLUGIN@.config` (or both), the rules defined by project 'P'
+no longer apply to project 'Q' , i.e., rules defined in project 'Q'
+override and replace any rules defined on any parent project.
+
+The same applies to the rules defined at the site level: if any project
+defines their own rule base files, the global ones defined in the
+`gerrit_site/etc/its/` folder do not apply to this project.
+
+## Rules
Each rule consists of three items: A name, a set of conditions, and a
set of actions.
@@ -72,9 +148,7 @@
There is no upper limit on the number of elements in a rules set of
conditions, and set of actions. Each of those sets may be empty.
-[conditions]: #conditions
-<a name="conditions">Conditions</a>
------------------------------------
+## Conditions
The conditions are lines of the form
@@ -101,9 +175,7 @@
status = !,DRAFT
```
-[event-properties]: #event-properties
-<a name="event-properties">Event Properties</a>
------------------------------------------------
+## Event Properties
The properties exposed by events depend on the kind of event.
@@ -135,7 +207,7 @@
make a rule in the rulebase match only for certain ITS plugins, if more
than one is installed.
- For example
+For example
```
[rule "someRuleForBugzillaOnly"]
@@ -148,25 +220,27 @@
action = add-comment Dear JIRA users, the change had a -2 Code-Review approval.
```
- would report the “Heya Bugzilla...” text only through its-bugzilla for
- changes that had a -2 Code-Review and have an association through
- its-bugzilla. And for changes that had a -2 Code-Review and have an
- association through its-jira, its-jira would report “Dear Jira users, ...”.
+would report the “Heya Bugzilla...” text only through its-bugzilla for
+changes that had a -2 Code-Review and have an association through
+its-bugzilla. And for changes that had a -2 Code-Review and have an
+association through its-jira, its-jira would report “Dear Jira users, ...”.
The further properties are listed in the event's
corresponding subsection below:
-* [ChangeAbandonedEvent][event-properties-ChangeAbandonedEvent]
-* [ChangeMergedEvent][event-properties-ChangeMergedEvent]
-* [ChangeRestoredEvent][event-properties-ChangeRestoredEvent]
-* [CommentAddedEvent][event-properties-CommentAddedEvent]
-* [PatchSetCreatedEvent][event-properties-PatchSetCreatedEvent]
-* [RefUpdatedEvent][event-properties-RefUpdatedEvent]
-* [Common properties for events on a change][event-properties-change]
-* [Common properties for events on a patch set][event-properties-patch-set]
+* [Property: `association`](#property-association)
+* [ChangeAbandonedEvent](#changeabandonedevent)
+* [ChangeMergedEvent](#changemergedevent)
+* [ChangeRestoredEvent](#changerestoredevent)
+* [CommentAddedEvent](#commentaddedevent)
+* [PatchSetCreatedEvent](#patchSetcreatedevent)
+* [RefUpdatedEvent](#refupdatedevent)
+* [WorkInProgressStateChangedEvent](#workinprogressstatechangedevent)
+* [PrivateStateChangedEvent](#privatestatechangedevent)
+* [Common properties for events on a change](#common-properties-for-events-on-a-change)
+* [Common properties for events on a patch set](#common-properties-for-events-on-a-patch-set)
-[property-association]: #property-association
-### <a name="property-association">Property: `association`</a>
+### Property: `association`
The property `association` describes how the `issue` got associated to
this event.
@@ -175,53 +249,52 @@
values are:
`somewhere`
-: issue id occurs somewhere in the commit message of the change/the
- most recent patch set.
+: issue id occurs somewhere in the commit message of the change/the
+ most recent patch set.
`subject`
-: issue id occurs in the first line of the commit message of the
- change/the most recent patch set.
+: issue id occurs in the first line of the commit message of the
+ change/the most recent patch set.
`body`
-: issue id occurs after the subject but before the footer of the
- commit message of the change/the most recent patch set.
+: issue id occurs after the subject but before the footer of the
+ commit message of the change/the most recent patch set.
`footer`
-: issue id occurs in the last paragraph after the subject of the
- commit message of the change/the most recent patch set
+: issue id occurs in the last paragraph after the subject of the
+ commit message of the change/the most recent patch set
`footer-<Key>`
-: issue id occurs in the footer of the commit message of the
- change/the most recent patch set, and is in a line with a key
- (part before the colon).
+: issue id occurs in the footer of the commit message of the
+ change/the most recent patch set, and is in a line with a key
+ (part before the colon).
- So for example, if the footer would contain a line
+So for example, if the footer would contain a line
- ```
+```
Fixes-Issue: issue 4711
```
- then a property `association` with value `footer-Fixes-Issue`
- would get added to the event for issue “4711”.
+then a property `association` with value `footer-Fixes-Issue`
+would get added to the event for issue “4711”.
`added@<Association-Value>`
-: (only for events that allow to determine the patch set number.
- So for example, this `association` property is not set for
- RevUpdatedEvents)
+: (only for events that allow to determine the patch set number.
+ So for example, this `association` property is not set for
+ RevUpdatedEvents)
- issue id occurs at `<Association-Value>` in the most recent
- patch set of the change, and either the event is for patch set
- 1 or the issue id does not occur at `<Association-Value>` in
- the previous patch set.
+issue id occurs at `<Association-Value>` in the most recent
+patch set of the change, and either the event is for patch set
+1 or the issue id does not occur at `<Association-Value>` in
+the previous patch set.
- So for example if issue “4711” occurs in the subject of patch
- set 3 (the most recent patch set) of a change, but not in
- patch set 2. When adding a comment to this change, the event
- for issue “4711” would get a property 'association' with value
- `added@subject`.
+So for example if issue “4711” occurs in the subject of patch
+set 3 (the most recent patch set) of a change, but not in
+patch set 2. When adding a comment to this change, the event
+for issue “4711” would get a property 'association' with value
+`added@subject`.
-[event-properties-ChangeAbandonedEvent]: #event-properties-ChangeAbandonedEvent
-### <a name="event-properties-ChangeAbandonedEvent">ChangeAbandonedEvent</a>
+### ChangeAbandonedEvent
`abandonerEmail`
: email address of the user abandoning the change.
@@ -242,11 +315,10 @@
: reason why the change has been abandoned.
In addition to the above properties, the event also provides
-properties for the abandoned [Change][event-properties-change], and
-its most recent [Patch Set][event-properties-patch-set].
+properties for the abandoned [Change][common-properties-for-events-on-a-change], and
+its most recent [Patch Set][common-properties-for-events-on-a-patch-set].
-[event-properties-ChangeMergedEvent]: #event-properties-ChangeMergedEvent
-### <a name="event-properties-ChangeMergedEvent">ChangeMergedEvent</a>
+### ChangeMergedEvent
`event`
: `com.google.gerrit.server.events.ChangeMergedEvent`
@@ -264,11 +336,10 @@
: username of the user causing the merge of the change.
In addition to the above properties, the event also provides
-properties for the merged [Change][event-properties-change], and its
-most recent [Patch Set][event-properties-patch-set].
+properties for the merged [Change][common-properties-for-events-on-a-change], and its
+most recent [Patch Set][common-properties-for-events-on-a-patch-set].
-[event-properties-ChangeRestoredEvent]: #event-properties-ChangeRestoredEvent
-### <a name="event-properties-ChangeRestoredEvent">ChangeRestoredEvent</a>
+### ChangeRestoredEvent
`event`
: `com.google.gerrit.server.events.ChangeRestoredEvent`
@@ -289,11 +360,10 @@
: username of the user restoring the change.
In addition to the above properties, the event also provides
-properties for the restored [Change][event-properties-change], and it's
-most recent [Patch Set][event-properties-patch-set].
+properties for the restored [Change][common-properties-for-events-on-a-change], and it's
+most recent [Patch Set][common-properties-for-events-on-a-patch-set].
-[event-properties-CommentAddedEvent]: #event-properties-CommentAddedEvent
-### <a name="event-properties-CommentAddedEvent">CommentAddedEvent</a>
+### CommentAddedEvent
NOTE: For consistency with the other events, the `author-...`
properties of the CommentAddedEvent do not refer to the author of the
@@ -314,7 +384,7 @@
: added comment itself.
`event`
-: `com.google.gerrit.server.events.CommentAddedEvent+
+: `com.google.gerrit.server.events.CommentAddedEvent+`
`event-type`
: `comment-added`
@@ -328,11 +398,10 @@
: `-2`
In addition to the above properties, the event also provides
-properties for the [Change][event-properties-change] the comment was
-added for, and it's most recent [Patch Set][event-properties-patch-set].
+properties for the [Change][common-properties-for-events-on-a-change] the comment was
+added for, and it's most recent [Patch Set][common-properties-for-events-on-a-patch-set].
-[event-properties-PatchSetCreatedEvent]: #event-properties-PatchSetCreatedEvent
-### <a name="event-properties-PatchSetCreatedEvent">PatchSetCreatedEvent</a>
+### PatchSetCreatedEvent
`event`
: `com.google.gerrit.server.events.PatchSetCreatedEvent`
@@ -341,11 +410,10 @@
: `patchset-created`
In addition to the above properties, the event also provides
-properties for the uploaded [Patch Set][event-properties-patch-set],
-and the [Change][event-properties-change] it belongs to.
+properties for the uploaded [Patch Set][common-properties-for-events-on-a-patch-set],
+and the [Change][common-properties-for-events-on-a-change] it belongs to.
-[event-properties-RefUpdatedEvent]: #event-properties-RefUpdatedEvent
-### <a name="event-properties-RefUpdatedEvent">RefUpdatedEvent</a>
+### RefUpdatedEvent
`event`
: `com.google.gerrit.server.events.RefUpdatedEvent`
@@ -375,8 +443,41 @@
`submitterUsername`
: username of the user that updated the ref.
-[event-properties-change]: #event-properties-change
-### <a name="event-properties-change">Common properties for events on a change</a>
+### WorkInProgressStateChangedEvent
+
+`event`
+: `com.google.gerrit.server.events.WorkInProgressStateChangedEvent`
+
+`event-type`
+: `wip-state-changed`
+
+`changerEmail`
+: email address of the user that changed the WIP state
+
+`submitterName`
+: name of the user that changed the WIP state
+
+`changerUsername`
+: username of the user that changed the WIP state
+
+### PrivateStateChangedEvent
+
+`event`
+: `com.google.gerrit.server.events.PrivateStateChangedEvent`
+
+`event-type`
+: `private-state-changed`
+
+`changerEmail`
+: email address of the user that changed the private state
+
+`submitterName`
+: name of the user that changed the private state
+
+`changerUsername`
+: username of the user that changed the private state
+
+### Common properties for events on a change
`branch`
: name of the branch the change belongs to.
@@ -418,8 +519,13 @@
`topic`
: name of the topic the change belongs to.
-[event-properties-patch-set]: #event-properties-patch-set
-### <a name="event-properties-patch-set">Common properties for events on a patch set</a>
+`private`
+: whether the change is marked private
+
+`wip`
+: whether the change is marked work in progress (WIP)
+
+### Common properties for events on a patch set
`authorEmail`
: email address of this patch set's author.
@@ -461,9 +567,7 @@
`uploaderUsername`
: username of the user that uploaded this patch set.
-[actions]: #actions
-<a name="actions">Actions</a>
------------------------------
+## Actions
Lines of the form
@@ -482,12 +586,12 @@
[`add-standard-comment`][action-add-standard-comment]
: adds a predefined standard comment for certain events
-[`add-velocity-comment`][action-add-velocity-comment]
-: adds a rendered Velocity template as issue comment
-
-[`add-soy-comment`][action-add-velocity-comment]
+[`add-soy-comment`][action-add-soy-comment]
: adds a rendered Closure Template (soy) template as issue comment
+[`create-version-from-property`][action-create-version-from-property]
+: creates a version based on an event's property value
+
[`log-event`][action-log-event]
: appends the event's properties to Gerrit's log
@@ -495,8 +599,7 @@
[further-actions]: config-rulebase-plugin-actions.md
-[action-add-comment]: #action-add-comment
-### <a name="action-add-comment">Action: add-comment</a>
+### Action: add-comment
The `add-comment` action adds the given parameters as comment to any
associated rule.
@@ -511,8 +614,7 @@
If no parameters are given, no comment gets added.
-[action-add-standard-comment]: #action-add-standard-comment
-### <a name="action-add-standard-comment">Action: add-standard-comment</a>
+### Action: add-standard-comment
The `add-standard-comment` action adds predefined comments to
associated issues for change abandoned, merged, restored, and patch
@@ -523,69 +625,7 @@
(abandoner, merger, ...), the change's subject, a reason (if one has
been given), and a link to the change.
-[action-add-velocity-comment]: #action-add-velocity-comment
-### <a name="action-add-velocity-comment">Action: add-velocity-comment</a>
-
-The `add-velocity-comment` action renders a Velocity template for the
-event and adds the output as comment to any associated issue.
-
-So for example
-
-```
- action = add-velocity-comment TemplateName
-```
-
-would render the template `etc/its/templates/TemplateName.vm` add the
-output as comment to associated issues.
-
-If 'TemplateName' is `inline`, the Velocity template to render is not
-loaded from a file, but the template is built by joining the remaining
-parameters. So for example
-
-```
- action = add-velocity-comment inline Sample template using $subject property.
-```
-
-would render “Sample template using $subject property.” as Velocity
-template.
-
-If 'TemplateName' is not `inline`, further parameters get ignored.
-
-Any [property][event-properties] of the event may be used from
-templates. So for example `$subject` in the above example refers to
-the event's subject property, and `$changeNumber` would refer to the
-change's number.
-
-Additionally, the context's `its` property provides an object that
-allows to format links using the its' syntax:
-
-`formatLink( url )`
-: Formats a link to a url.
-
- So for example upon adding a comment to a change, the
- following rule formats a link to the change:
-
- ```
-[rule "formatLinkSampleRule"]
- event-type = comment-added
- action = add-velocity-comment inline Comment for change $change-number added. See ${its.formatLink($changeUrl)}
-```
-
-`formatLink( url, caption )`
-: Formats a link to a url using 'caption' to represent the url.
-
- So for example upon adding a comment to a change, the following rule
- formats a link to the change using the change number as link
- capition:
-
- ```
-[rule "formatLinkSampleRule"]
- event-type = comment-added
- action = add-velocity-comment inline Comment for change ${its.formatLink($changeUrl, $changeNumber)} added.
-```
-
-[action-add-soy-comment]: #action-add-soy-comment
-### <a name="action-add-soy-comment">Action: add-soy-comment</a>
+### Action: add-soy-comment
The `add-soy-comment` action renders a Closure template (soy) for the
event and adds the output as comment to any associated issue.
@@ -619,8 +659,34 @@
the event's subject property, and `$changeNumber` would refer to the
change's number.
-[action-log-event]: #action-log-event
-### <a name="action-log-event">Action: log-event</a>
+### Action: add-property-to-field
+
+The `add-property-to-field` action adds an event property value to an ITS designated field.
+
+The field is expected to be able to hold multiple values.
+The ITS field value deduplication depends on the its implementation.
+
+Example with the event property `branch` and a field identified as `labels`:
+
+```
+ action = add-property-to-field branch labels
+```
+
+### Action: create-version-from-property
+
+The `create-version-from-property` action creates a version in the ITS project
+by using an event property value as the version value.
+
+This is useful when you want to create a new version in the ITS when a tag is
+created in the Gerrit project.
+
+Example with the event property `ref`:
+
+```
+ action = create-version-from-property ref
+```
+
+### Action: log-event
The `log-event` action appends the event's properties to Gerrit's log.
diff --git a/src/main/templates/Documentation/about.md b/src/main/templates/Documentation/about.md
index 343ec34..6c8b47d 100644
--- a/src/main/templates/Documentation/about.md
+++ b/src/main/templates/Documentation/about.md
@@ -8,11 +8,13 @@
* [its-bugzilla][its-bugzilla]
* [its-jira][its-jira]
+* [its-phabricator][its-phabricator]
* [its-rtc][its-rtc]
-[its-bugzilla]: https://gerrit-review.googlesource.com/#/admin/projects/plugins/its-bugzilla
-[its-jira]: https://gerrit-review.googlesource.com/#/admin/projects/plugins/its-jira
-[its-rtc]: https://gerrit-review.googlesource.com/#/admin/projects/plugins/its-rtc
+[its-bugzilla]: https://gerrit-review.googlesource.com/admin/repos/plugins/its-bugzilla
+[its-jira]: https://gerrit-review.googlesource.com/admin/repos/plugins/its-jira
+[its-phabricator]: https://gerrit-review.googlesource.com/admin/repos/plugins/its-phabricator
+[its-rtc]: https://gerrit-review.googlesource.com/admin/repos/plugins/its-rtc
`its-base` provides means to:
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/base/its/ItsConfigTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/base/its/ItsConfigTest.java
index 6561d00..1a147c7 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/its/base/its/ItsConfigTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/base/its/ItsConfigTest.java
@@ -14,6 +14,7 @@
package com.googlesource.gerrit.plugins.its.base.its;
+import static org.easymock.EasyMock.eq;
import static org.easymock.EasyMock.expect;
import com.google.common.base.Suppliers;
@@ -28,8 +29,8 @@
import com.google.gerrit.server.events.ChangeMergedEvent;
import com.google.gerrit.server.events.ChangeRestoredEvent;
import com.google.gerrit.server.events.CommentAddedEvent;
-import com.google.gerrit.server.events.Event;
import com.google.gerrit.server.events.PatchSetCreatedEvent;
+import com.google.gerrit.server.events.RefEvent;
import com.google.gerrit.server.events.RefUpdatedEvent;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
@@ -38,6 +39,7 @@
import com.googlesource.gerrit.plugins.its.base.testutil.LoggingMockingTestCase;
import com.googlesource.gerrit.plugins.its.base.validation.ItsAssociationPolicy;
import java.util.Arrays;
+import java.util.Optional;
import org.eclipse.jgit.lib.Config;
public class ItsConfigTest extends LoggingMockingTestCase {
@@ -47,7 +49,8 @@
private PluginConfigFactory pluginConfigFactory;
private Config serverConfig;
- public void setupIsEnabled(String enabled, String parentEnabled, String[] branches) {
+ public void setupIsEnabled(
+ String enabled, String itsProject, String parentEnabled, String[] branches) {
ProjectState projectState = createMock(ProjectState.class);
expect(projectCache.get(new Project.NameKey("testProject"))).andReturn(projectState).anyTimes();
@@ -81,7 +84,7 @@
parents = Arrays.asList(parentProjectState, projectState);
}
- expect(projectState.treeInOrder()).andReturn(parents);
+ expect(projectState.treeInOrder()).andReturn(parents).anyTimes();
PluginConfig pluginConfig = createMock(PluginConfig.class);
@@ -90,6 +93,7 @@
.anyTimes();
expect(pluginConfig.getString("enabled", "false")).andReturn(enabled).anyTimes();
+ expect(pluginConfig.getString(eq("its-project"))).andReturn(itsProject).anyTimes();
PluginConfig pluginConfigWI = createMock(PluginConfig.class);
@@ -104,7 +108,7 @@
public void testIsEnabledRefNoParentNoBranchEnabled() {
String[] branches = {};
- setupIsEnabled("true", null, branches);
+ setupIsEnabled("true", null, null, branches);
ItsConfig itsConfig = createItsConfig();
@@ -116,7 +120,7 @@
public void BROKEN_testIsEnabledRefNoParentNoBranchDisabled() {
String[] branches = {};
- setupIsEnabled("false", null, branches);
+ setupIsEnabled("false", null, null, branches);
ItsConfig itsConfig = createItsConfig();
@@ -128,7 +132,7 @@
public void testIsEnabledRefNoParentNoBranchEnforced() {
String[] branches = {};
- setupIsEnabled("enforced", null, branches);
+ setupIsEnabled("enforced", null, null, branches);
ItsConfig itsConfig = createItsConfig();
@@ -140,7 +144,7 @@
public void testIsEnabledRefNoParentMatchingBranchEnabled() {
String[] branches = {"^refs/heads/test.*"};
- setupIsEnabled("true", null, branches);
+ setupIsEnabled("true", null, null, branches);
ItsConfig itsConfig = createItsConfig();
@@ -152,7 +156,7 @@
public void BROKEN_testIsEnabledRefNoParentMatchingBranchDisabled() {
String[] branches = {"^refs/heads/test.*"};
- setupIsEnabled("false", null, branches);
+ setupIsEnabled("false", null, null, branches);
ItsConfig itsConfig = createItsConfig();
@@ -164,7 +168,7 @@
public void testIsEnabledRefNoParentMatchingBranchEnforced() {
String[] branches = {"^refs/heads/test.*"};
- setupIsEnabled("enforced", null, branches);
+ setupIsEnabled("enforced", null, null, branches);
ItsConfig itsConfig = createItsConfig();
@@ -176,7 +180,7 @@
public void BROKEN_testIsEnabledRefNoParentNonMatchingBranchEnabled() {
String[] branches = {"^refs/heads/foo.*"};
- setupIsEnabled("true", null, branches);
+ setupIsEnabled("true", null, null, branches);
ItsConfig itsConfig = createItsConfig();
@@ -188,7 +192,7 @@
public void BROKEN_testIsEnabledRefNoParentNonMatchingBranchDisabled() {
String[] branches = {"^refs/heads/foo.*"};
- setupIsEnabled("false", null, branches);
+ setupIsEnabled("false", null, null, branches);
ItsConfig itsConfig = createItsConfig();
@@ -200,7 +204,7 @@
public void BROKEN_testIsEnabledRefNoParentNonMatchingBranchEnforced() {
String[] branches = {"^refs/heads/foo.*"};
- setupIsEnabled("enforced", null, branches);
+ setupIsEnabled("enforced", null, null, branches);
ItsConfig itsConfig = createItsConfig();
@@ -212,7 +216,7 @@
public void testIsEnabledRefNoParentMatchingBranchMiddleEnabled() {
String[] branches = {"^refs/heads/foo.*", "^refs/heads/test.*", "^refs/heads/baz.*"};
- setupIsEnabled("true", null, branches);
+ setupIsEnabled("true", null, null, branches);
ItsConfig itsConfig = createItsConfig();
@@ -224,7 +228,7 @@
public void BROKEN_testIsEnabledRefNoParentMatchingBranchMiddleDisabled() {
String[] branches = {"^refs/heads/foo.*", "^refs/heads/test.*", "^refs/heads/baz.*"};
- setupIsEnabled("false", null, branches);
+ setupIsEnabled("false", null, null, branches);
ItsConfig itsConfig = createItsConfig();
@@ -236,7 +240,7 @@
public void testIsEnabledRefNoParentMatchingBranchMiddleEnforced() {
String[] branches = {"^refs/heads/foo.*", "^refs/heads/test.*", "^refs/heads/baz.*"};
- setupIsEnabled("enforced", null, branches);
+ setupIsEnabled("enforced", null, null, branches);
ItsConfig itsConfig = createItsConfig();
@@ -248,7 +252,7 @@
public void BROKEN_testIsEnabledRefParentNoBranchEnabled() {
String[] branches = {};
- setupIsEnabled("false", "true", branches);
+ setupIsEnabled("false", null, "true", branches);
ItsConfig itsConfig = createItsConfig();
@@ -260,7 +264,7 @@
public void BROKEN_testIsEnabledRefParentNoBranchDisabled() {
String[] branches = {};
- setupIsEnabled("false", "false", branches);
+ setupIsEnabled("false", null, "false", branches);
ItsConfig itsConfig = createItsConfig();
@@ -272,7 +276,7 @@
public void testIsEnabledRefParentNoBranchEnforced() {
String[] branches = {};
- setupIsEnabled("false", "enforced", branches);
+ setupIsEnabled("false", null, "enforced", branches);
ItsConfig itsConfig = createItsConfig();
@@ -284,7 +288,7 @@
public void testIsEnabledEventNoBranches() {
String[] branches = {};
- setupIsEnabled("true", null, branches);
+ setupIsEnabled("true", null, null, branches);
PatchSetCreatedEvent event = new PatchSetCreatedEvent(testChange("testProject", "testBranch"));
@@ -297,7 +301,7 @@
public void testIsEnabledEventSingleBranchExact() {
String[] branches = {"refs/heads/testBranch"};
- setupIsEnabled("true", null, branches);
+ setupIsEnabled("true", null, null, branches);
PatchSetCreatedEvent event = new PatchSetCreatedEvent(testChange("testProject", "testBranch"));
@@ -310,7 +314,7 @@
public void testIsEnabledEventSingleBranchRegExp() {
String[] branches = {"^refs/heads/test.*"};
- setupIsEnabled("true", null, branches);
+ setupIsEnabled("true", null, null, branches);
PatchSetCreatedEvent event = new PatchSetCreatedEvent(testChange("testProject", "testBranch"));
@@ -323,7 +327,7 @@
public void BROKEN_testIsEnabledEventSingleBranchNonMatchingRegExp() {
String[] branches = {"^refs/heads/foo.*"};
- setupIsEnabled("true", null, branches);
+ setupIsEnabled("true", null, null, branches);
PatchSetCreatedEvent event = new PatchSetCreatedEvent(testChange("testProject", "testBranch"));
@@ -336,7 +340,7 @@
public void testIsEnabledEventMultiBranchExact() {
String[] branches = {"refs/heads/foo", "refs/heads/testBranch"};
- setupIsEnabled("true", null, branches);
+ setupIsEnabled("true", null, null, branches);
PatchSetCreatedEvent event = new PatchSetCreatedEvent(testChange("testProject", "testBranch"));
@@ -349,7 +353,7 @@
public void testIsEnabledEventMultiBranchRegExp() {
String[] branches = {"^refs/heads/foo.*", "^refs/heads/test.*"};
- setupIsEnabled("true", null, branches);
+ setupIsEnabled("true", null, null, branches);
PatchSetCreatedEvent event = new PatchSetCreatedEvent(testChange("testProject", "testBranch"));
@@ -362,7 +366,7 @@
public void testIsEnabledEventMultiBranchMixedMatchExact() {
String[] branches = {"refs/heads/testBranch", "refs/heads/foo.*"};
- setupIsEnabled("true", null, branches);
+ setupIsEnabled("true", null, null, branches);
PatchSetCreatedEvent event = new PatchSetCreatedEvent(testChange("testProject", "testBranch"));
@@ -375,7 +379,7 @@
public void testIsEnabledEventMultiBranchMixedMatchRegExp() {
String[] branches = {"refs/heads/foo", "^refs/heads/test.*"};
- setupIsEnabled("true", null, branches);
+ setupIsEnabled("true", null, null, branches);
PatchSetCreatedEvent event = new PatchSetCreatedEvent(testChange("testProject", "testBranch"));
@@ -388,7 +392,7 @@
public void BROKEN_testIsEnabledEventDisabled() {
String[] branches = {"^refs/heads/testBranch"};
- setupIsEnabled("false", null, branches);
+ setupIsEnabled("false", null, null, branches);
PatchSetCreatedEvent event = new PatchSetCreatedEvent(testChange("testProject", "testBranch"));
@@ -401,7 +405,7 @@
public void testIsEnabledCommentAddedEvent() {
String[] branches = {};
- setupIsEnabled("true", null, branches);
+ setupIsEnabled("true", null, null, branches);
CommentAddedEvent event = new CommentAddedEvent(testChange("testProject", "testBranch"));
@@ -414,7 +418,7 @@
public void testIsEnabledChangeMergedEvent() {
String[] branches = {};
- setupIsEnabled("true", null, branches);
+ setupIsEnabled("true", null, null, branches);
ChangeMergedEvent event = new ChangeMergedEvent(testChange("testProject", "testBranch"));
@@ -427,7 +431,7 @@
public void testIsEnabledChangeAbandonedEvent() {
String[] branches = {};
- setupIsEnabled("true", null, branches);
+ setupIsEnabled("true", null, null, branches);
ChangeAbandonedEvent event = new ChangeAbandonedEvent(testChange("testProject", "testBranch"));
@@ -440,7 +444,7 @@
public void testIsEnabledChangeRestoredEvent() {
String[] branches = {};
- setupIsEnabled("true", null, branches);
+ setupIsEnabled("true", null, null, branches);
ChangeRestoredEvent event = new ChangeRestoredEvent(testChange("testProject", "testBranch"));
@@ -453,7 +457,7 @@
public void testIsEnabledRefUpdatedEvent() {
String[] branches = {};
- setupIsEnabled("true", null, branches);
+ setupIsEnabled("true", null, null, branches);
RefUpdatedEvent event = new RefUpdatedEvent();
RefUpdateAttribute refUpdateAttribute = new RefUpdateAttribute();
@@ -469,7 +473,7 @@
}
public void BROKEN_testIsEnabledUnknownEvent() {
- Event event = new Event("foo") {};
+ RefEvent event = createMock(RefEvent.class);
ItsConfig itsConfig = createItsConfig();
@@ -479,6 +483,31 @@
assertLogMessageContains("not recognised and ignored");
}
+ public void testGetItsProjectNull() {
+ String[] branches = {};
+ setupIsEnabled("true", null, null, branches);
+
+ ItsConfig itsConfig = createItsConfig();
+
+ replayMocks();
+
+ assertFalse(itsConfig.getItsProjectName(new Project.NameKey("testProject")).isPresent());
+ }
+
+ public void testGetItsProjectConfigured() {
+ String[] branches = {};
+ setupIsEnabled("true", "itsProject", null, branches);
+
+ ItsConfig itsConfig = createItsConfig();
+
+ replayMocks();
+
+ Optional<String> itsProjectName =
+ itsConfig.getItsProjectName(new Project.NameKey("testProject"));
+ assertTrue(itsProjectName.isPresent());
+ assertEquals("itsProject", itsProjectName.get());
+ }
+
public void testGetIssuePatternNullMatch() {
ItsConfig itsConfig = createItsConfig();
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/base/util/ItsProjectExtractorTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/base/util/ItsProjectExtractorTest.java
new file mode 100644
index 0000000..0aecd05
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/base/util/ItsProjectExtractorTest.java
@@ -0,0 +1,62 @@
+// Copyright (C) 2018 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.its.base.util;
+
+import static org.easymock.EasyMock.expect;
+
+import com.google.gerrit.extensions.config.FactoryModule;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.googlesource.gerrit.plugins.its.base.its.ItsConfig;
+import com.googlesource.gerrit.plugins.its.base.testutil.MockingTestCase;
+import java.util.Optional;
+
+public class ItsProjectExtractorTest extends MockingTestCase {
+
+ private static final String PROJECT = "project";
+ private static final String ITS_PROJECT = "itsProject";
+
+ private Injector injector;
+ private ItsConfig itsConfig;
+
+ public void test() {
+ ItsProjectExtractor projectExtractor = injector.getInstance(ItsProjectExtractor.class);
+
+ expect(itsConfig.getItsProjectName(new Project.NameKey(PROJECT)))
+ .andReturn(Optional.of(ITS_PROJECT))
+ .once();
+
+ replayMocks();
+
+ String ret = projectExtractor.getItsProject(PROJECT).orElse(null);
+ assertEquals(ret, ITS_PROJECT);
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ injector = Guice.createInjector(new TestModule());
+ }
+
+ private class TestModule extends FactoryModule {
+ @Override
+ protected void configure() {
+ itsConfig = createMock(ItsConfig.class);
+ bind(ItsConfig.class).toInstance(itsConfig);
+ }
+ }
+}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/base/util/PropertyAttributeExtractorTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/base/util/PropertyAttributeExtractorTest.java
index 69d8196..3e61bc1 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/its/base/util/PropertyAttributeExtractorTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/base/util/PropertyAttributeExtractorTest.java
@@ -15,9 +15,10 @@
import static org.easymock.EasyMock.expect;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
import com.google.gerrit.extensions.config.FactoryModule;
+import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Change.Status;
import com.google.gerrit.server.data.AccountAttribute;
import com.google.gerrit.server.data.ApprovalAttribute;
@@ -28,23 +29,22 @@
import com.google.inject.Injector;
import com.googlesource.gerrit.plugins.its.base.its.ItsFacade;
import com.googlesource.gerrit.plugins.its.base.testutil.LoggingMockingTestCase;
-import com.googlesource.gerrit.plugins.its.base.workflow.Property;
-import java.util.Set;
+import java.util.HashMap;
+import java.util.Map;
public class PropertyAttributeExtractorTest extends LoggingMockingTestCase {
private Injector injector;
private ItsFacade facade;
- private Property.Factory propertyFactory;
public void testAccountAttributeNull() {
replayMocks();
PropertyAttributeExtractor extractor = injector.getInstance(PropertyAttributeExtractor.class);
- Set<Property> actual = extractor.extractFrom(null, "prefix");
+ Map<String, String> actual = extractor.extractFrom(null, "prefix");
- Set<Property> expected = Sets.newHashSet();
+ Map<String, String> expected = new HashMap<>();
assertEquals("Properties do not match", expected, actual);
}
@@ -55,39 +55,18 @@
accountAttribute.name = "testName";
accountAttribute.username = "testUsername";
- // deprecated, to be removed soon. migrate to ones without dash.
- Property propertyEmail2 = createMock(Property.class);
- expect(propertyFactory.create("prefix-email", "testEmail")).andReturn(propertyEmail2);
-
- Property propertyName2 = createMock(Property.class);
- expect(propertyFactory.create("prefix-name", "testName")).andReturn(propertyName2);
-
- Property propertyUsername2 = createMock(Property.class);
- expect(propertyFactory.create("prefix-username", "testUsername")).andReturn(propertyUsername2);
-
- // New style configs for vm and soy
- Property propertyEmail = createMock(Property.class);
- expect(propertyFactory.create("prefixEmail", "testEmail")).andReturn(propertyEmail);
-
- Property propertyName = createMock(Property.class);
- expect(propertyFactory.create("prefixName", "testName")).andReturn(propertyName);
-
- Property propertyUsername = createMock(Property.class);
- expect(propertyFactory.create("prefixUsername", "testUsername")).andReturn(propertyUsername);
-
replayMocks();
PropertyAttributeExtractor extractor = injector.getInstance(PropertyAttributeExtractor.class);
- Set<Property> actual = extractor.extractFrom(accountAttribute, "prefix");
+ Map<String, String> actual = extractor.extractFrom(accountAttribute, "prefix");
- Set<Property> expected = Sets.newHashSet();
- expected.add(propertyEmail);
- expected.add(propertyName);
- expected.add(propertyUsername);
- expected.add(propertyEmail2);
- expected.add(propertyName2);
- expected.add(propertyUsername2);
+ ImmutableMap<String, String> expected =
+ new ImmutableMap.Builder<String, String>()
+ .put("prefixEmail", "testEmail")
+ .put("prefixName", "testName")
+ .put("prefixUsername", "testUsername")
+ .build();
assertEquals("Properties do not match", expected, actual);
}
@@ -98,7 +77,6 @@
owner.username = "testUsername";
ChangeAttribute changeAttribute = new ChangeAttribute();
- changeAttribute.project = "testProject";
changeAttribute.branch = "testBranch";
changeAttribute.topic = "testTopic";
changeAttribute.subject = "testSubject";
@@ -107,75 +85,7 @@
changeAttribute.url = "http://www.example.org/test";
changeAttribute.owner = owner;
changeAttribute.commitMessage = "Commit Message";
-
- Property propertyProject = createMock(Property.class);
- expect(propertyFactory.create("project", "testProject")).andReturn(propertyProject);
-
- Property propertyBranch = createMock(Property.class);
- expect(propertyFactory.create("branch", "testBranch")).andReturn(propertyBranch);
-
- Property propertyTopic = createMock(Property.class);
- expect(propertyFactory.create("topic", "testTopic")).andReturn(propertyTopic);
-
- Property propertySubject = createMock(Property.class);
- expect(propertyFactory.create("subject", "testSubject")).andReturn(propertySubject);
-
- Property propertyEscapedSubject = createMock(Property.class);
- expect(propertyFactory.create("escapedSubject", "testSubject"))
- .andReturn(propertyEscapedSubject);
-
- Property propertyId2 = createMock(Property.class);
- expect(propertyFactory.create("change-id", "testId")).andReturn(propertyId2);
-
- Property propertyId = createMock(Property.class);
- expect(propertyFactory.create("changeId", "testId")).andReturn(propertyId);
-
- Property propertyNumber2 = createMock(Property.class);
- expect(propertyFactory.create("change-number", "4711")).andReturn(propertyNumber2);
-
- Property propertyNumber = createMock(Property.class);
- expect(propertyFactory.create("changeNumber", "4711")).andReturn(propertyNumber);
-
- Property propertyUrl2 = createMock(Property.class);
- expect(propertyFactory.create("change-url", "http://www.example.org/test"))
- .andReturn(propertyUrl2);
-
- Property propertyUrl = createMock(Property.class);
- expect(propertyFactory.create("changeUrl", "http://www.example.org/test"))
- .andReturn(propertyUrl);
-
- Property propertyStatus = createMock(Property.class);
- expect(propertyFactory.create("status", null)).andReturn(propertyStatus);
-
- Property propertyEmail = createMock(Property.class);
- expect(propertyFactory.create("ownerEmail", "testEmail")).andReturn(propertyEmail);
-
- Property propertyName = createMock(Property.class);
- expect(propertyFactory.create("ownerName", "testName")).andReturn(propertyName);
-
- Property propertyUsername = createMock(Property.class);
- expect(propertyFactory.create("ownerUsername", "testUsername")).andReturn(propertyUsername);
-
- Property propertyCommitMessage = createMock(Property.class);
- expect(propertyFactory.create("commitMessage", "Commit Message"))
- .andReturn(propertyCommitMessage);
-
- Property propertyEmail2 = createMock(Property.class);
- expect(propertyFactory.create("owner-email", "testEmail")).andReturn(propertyEmail2);
-
- Property propertyName2 = createMock(Property.class);
- expect(propertyFactory.create("owner-name", "testName")).andReturn(propertyName2);
-
- Property propertyUsername2 = createMock(Property.class);
- expect(propertyFactory.create("owner-username", "testUsername")).andReturn(propertyUsername2);
-
- Property propertyCommitMessage2 = createMock(Property.class);
- expect(propertyFactory.create("commit-message", "Commit Message"))
- .andReturn(propertyCommitMessage2);
-
- Property propertyFormatChangeUrl = createMock(Property.class);
- expect(propertyFactory.create("formatChangeUrl", "http://www.example.org/test"))
- .andReturn(propertyFormatChangeUrl);
+ changeAttribute.status = Change.Status.NEW;
expect(facade.createLinkForWebui("http://www.example.org/test", "http://www.example.org/test"))
.andReturn("http://www.example.org/test");
@@ -184,30 +94,71 @@
PropertyAttributeExtractor extractor = injector.getInstance(PropertyAttributeExtractor.class);
- Set<Property> actual = extractor.extractFrom(changeAttribute);
+ Map<String, String> actual = extractor.extractFrom(changeAttribute);
- Set<Property> expected = Sets.newHashSet();
- expected.add(propertyProject);
- expected.add(propertyBranch);
- expected.add(propertyTopic);
- expected.add(propertySubject);
- expected.add(propertyEscapedSubject);
- expected.add(propertyId);
- expected.add(propertyId2);
- expected.add(propertyNumber);
- expected.add(propertyNumber2);
- expected.add(propertyUrl);
- expected.add(propertyUrl2);
- expected.add(propertyStatus);
- expected.add(propertyEmail);
- expected.add(propertyName);
- expected.add(propertyUsername);
- expected.add(propertyCommitMessage);
- expected.add(propertyEmail2);
- expected.add(propertyName2);
- expected.add(propertyUsername2);
- expected.add(propertyCommitMessage2);
- expected.add(propertyFormatChangeUrl);
+ ImmutableMap<String, String> expected =
+ new ImmutableMap.Builder<String, String>()
+ .put("branch", "testBranch")
+ .put("topic", "testTopic")
+ .put("subject", "testSubject")
+ .put("escapedSubject", "testSubject")
+ .put("changeId", "testId")
+ .put("changeNumber", "4711")
+ .put("changeUrl", "http://www.example.org/test")
+ .put("status", Change.Status.NEW.name())
+ .put("ownerEmail", "testEmail")
+ .put("ownerName", "testName")
+ .put("ownerUsername", "testUsername")
+ .put("commitMessage", "Commit Message")
+ .put("formatChangeUrl", "http://www.example.org/test")
+ .put("private", "false")
+ .put("wip", "false")
+ .build();
+ assertEquals("Properties do not match", expected, actual);
+ }
+
+ public void testChangeAttributeNoOwnerEmail() {
+ AccountAttribute owner = new AccountAttribute();
+ owner.name = "testName";
+ owner.username = "testUsername";
+
+ ChangeAttribute changeAttribute = new ChangeAttribute();
+ changeAttribute.branch = "testBranch";
+ changeAttribute.topic = "testTopic";
+ changeAttribute.subject = "testSubject";
+ changeAttribute.id = "testId";
+ changeAttribute.number = 4711;
+ changeAttribute.url = "http://www.example.org/test";
+ changeAttribute.owner = owner;
+ changeAttribute.commitMessage = "Commit Message";
+ changeAttribute.status = Change.Status.NEW;
+
+ expect(facade.createLinkForWebui("http://www.example.org/test", "http://www.example.org/test"))
+ .andReturn("http://www.example.org/test");
+
+ replayMocks();
+
+ PropertyAttributeExtractor extractor = injector.getInstance(PropertyAttributeExtractor.class);
+
+ Map<String, String> actual = extractor.extractFrom(changeAttribute);
+
+ ImmutableMap<String, String> expected =
+ new ImmutableMap.Builder<String, String>()
+ .put("branch", "testBranch")
+ .put("topic", "testTopic")
+ .put("subject", "testSubject")
+ .put("escapedSubject", "testSubject")
+ .put("changeId", "testId")
+ .put("changeNumber", "4711")
+ .put("changeUrl", "http://www.example.org/test")
+ .put("status", Change.Status.NEW.name())
+ .put("ownerName", "testName")
+ .put("ownerUsername", "testUsername")
+ .put("commitMessage", "Commit Message")
+ .put("formatChangeUrl", "http://www.example.org/test")
+ .put("private", "false")
+ .put("wip", "false")
+ .build();
assertEquals("Properties do not match", expected, actual);
}
@@ -218,7 +169,6 @@
owner.username = "testUsername";
ChangeAttribute changeAttribute = new ChangeAttribute();
- changeAttribute.project = "testProject";
changeAttribute.branch = "testBranch";
changeAttribute.topic = "testTopic";
changeAttribute.subject = "testSubject";
@@ -229,75 +179,6 @@
changeAttribute.owner = owner;
changeAttribute.commitMessage = "Commit Message";
- Property propertyProject = createMock(Property.class);
- expect(propertyFactory.create("project", "testProject")).andReturn(propertyProject);
-
- Property propertyBranch = createMock(Property.class);
- expect(propertyFactory.create("branch", "testBranch")).andReturn(propertyBranch);
-
- Property propertyTopic = createMock(Property.class);
- expect(propertyFactory.create("topic", "testTopic")).andReturn(propertyTopic);
-
- Property propertySubject = createMock(Property.class);
- expect(propertyFactory.create("subject", "testSubject")).andReturn(propertySubject);
-
- Property propertyEscapedSubject = createMock(Property.class);
- expect(propertyFactory.create("escapedSubject", "testSubject"))
- .andReturn(propertyEscapedSubject);
-
- Property propertyId = createMock(Property.class);
- expect(propertyFactory.create("changeId", "testId")).andReturn(propertyId);
-
- Property propertyNumber = createMock(Property.class);
- expect(propertyFactory.create("changeNumber", "4711")).andReturn(propertyNumber);
-
- Property propertyUrl = createMock(Property.class);
- expect(propertyFactory.create("changeUrl", "http://www.example.org/test"))
- .andReturn(propertyUrl);
-
- Property propertyId2 = createMock(Property.class);
- expect(propertyFactory.create("change-id", "testId")).andReturn(propertyId2);
-
- Property propertyNumber2 = createMock(Property.class);
- expect(propertyFactory.create("change-number", "4711")).andReturn(propertyNumber2);
-
- Property propertyUrl2 = createMock(Property.class);
- expect(propertyFactory.create("change-url", "http://www.example.org/test"))
- .andReturn(propertyUrl2);
-
- Property propertyStatus = createMock(Property.class);
- expect(propertyFactory.create("status", "ABANDONED")).andReturn(propertyStatus);
-
- Property propertyEmail = createMock(Property.class);
- expect(propertyFactory.create("ownerEmail", "testEmail")).andReturn(propertyEmail);
-
- Property propertyName = createMock(Property.class);
- expect(propertyFactory.create("ownerName", "testName")).andReturn(propertyName);
-
- Property propertyUsername = createMock(Property.class);
- expect(propertyFactory.create("ownerUsername", "testUsername")).andReturn(propertyUsername);
-
- Property propertyCommitMessage = createMock(Property.class);
- expect(propertyFactory.create("commitMessage", "Commit Message"))
- .andReturn(propertyCommitMessage);
-
- Property propertyEmail2 = createMock(Property.class);
- expect(propertyFactory.create("owner-email", "testEmail")).andReturn(propertyEmail2);
-
- Property propertyName2 = createMock(Property.class);
- expect(propertyFactory.create("owner-name", "testName")).andReturn(propertyName2);
-
- Property propertyUsername2 = createMock(Property.class);
- expect(propertyFactory.create("owner-username", "testUsername")).andReturn(propertyUsername2);
-
- Property propertyCommitMessage2 = createMock(Property.class);
- expect(propertyFactory.create("commit-message", "Commit Message"))
- .andReturn(propertyCommitMessage2);
-
- Property propertyFormatChangeUrl = createMock(Property.class);
- expect(propertyFactory.create("formatChangeUrl", "http://www.example.org/test"))
- .andReturn(propertyFormatChangeUrl);
-
expect(facade.createLinkForWebui("http://www.example.org/test", "http://www.example.org/test"))
.andReturn("http://www.example.org/test");
@@ -305,30 +186,26 @@
PropertyAttributeExtractor extractor = injector.getInstance(PropertyAttributeExtractor.class);
- Set<Property> actual = extractor.extractFrom(changeAttribute);
+ Map<String, String> actual = extractor.extractFrom(changeAttribute);
- Set<Property> expected = Sets.newHashSet();
- expected.add(propertyProject);
- expected.add(propertyBranch);
- expected.add(propertyTopic);
- expected.add(propertySubject);
- expected.add(propertyEscapedSubject);
- expected.add(propertyId);
- expected.add(propertyNumber);
- expected.add(propertyUrl);
- expected.add(propertyId2);
- expected.add(propertyNumber2);
- expected.add(propertyUrl2);
- expected.add(propertyStatus);
- expected.add(propertyEmail);
- expected.add(propertyName);
- expected.add(propertyUsername);
- expected.add(propertyCommitMessage);
- expected.add(propertyEmail2);
- expected.add(propertyName2);
- expected.add(propertyUsername2);
- expected.add(propertyCommitMessage2);
- expected.add(propertyFormatChangeUrl);
+ ImmutableMap<String, String> expected =
+ new ImmutableMap.Builder<String, String>()
+ .put("branch", "testBranch")
+ .put("topic", "testTopic")
+ .put("subject", "testSubject")
+ .put("escapedSubject", "testSubject")
+ .put("changeId", "testId")
+ .put("changeNumber", "4711")
+ .put("changeUrl", "http://www.example.org/test")
+ .put("status", Change.Status.ABANDONED.name())
+ .put("ownerEmail", "testEmail")
+ .put("ownerName", "testName")
+ .put("ownerUsername", "testUsername")
+ .put("commitMessage", "Commit Message")
+ .put("formatChangeUrl", "http://www.example.org/test")
+ .put("private", "false")
+ .put("wip", "false")
+ .build();
assertEquals("Properties do not match", expected, actual);
}
@@ -354,103 +231,28 @@
patchSetAttribute.uploader = uploader;
patchSetAttribute.author = author;
- Property propertyRevision = createMock(Property.class);
- expect(propertyFactory.create("revision", "1234567891123456789212345678931234567894"))
- .andReturn(propertyRevision);
-
- Property propertyNumber = createMock(Property.class);
- expect(propertyFactory.create("patchSetNumber", "42")).andReturn(propertyNumber);
-
- Property propertyNumber2 = createMock(Property.class);
- expect(propertyFactory.create("patch-set-number", "42")).andReturn(propertyNumber2);
-
- Property propertyRef = createMock(Property.class);
- expect(propertyFactory.create("ref", "testRef")).andReturn(propertyRef);
-
- Property propertyCreatedOn = createMock(Property.class);
- expect(propertyFactory.create("createdOn", "1234567890")).andReturn(propertyCreatedOn);
-
- Property propertyCreatedOn2 = createMock(Property.class);
- expect(propertyFactory.create("created-on", "1234567890")).andReturn(propertyCreatedOn2);
-
- Property propertyParents = createMock(Property.class);
- expect(propertyFactory.create("parents", "[parent1, parent2]")).andReturn(propertyParents);
-
- Property propertyDeletions = createMock(Property.class);
- expect(propertyFactory.create("deletions", "7")).andReturn(propertyDeletions);
-
- Property propertyInsertions = createMock(Property.class);
- expect(propertyFactory.create("insertions", "12")).andReturn(propertyInsertions);
-
- Property propertyUploaderEmail = createMock(Property.class);
- expect(propertyFactory.create("uploaderEmail", "testEmail1")).andReturn(propertyUploaderEmail);
-
- Property propertyUploaderName = createMock(Property.class);
- expect(propertyFactory.create("uploaderName", "testName1")).andReturn(propertyUploaderName);
-
- Property propertyUploaderUsername = createMock(Property.class);
- expect(propertyFactory.create("uploaderUsername", "testUsername1"))
- .andReturn(propertyUploaderUsername);
-
- Property propertyUploaderEmail2 = createMock(Property.class);
- expect(propertyFactory.create("uploader-email", "testEmail1"))
- .andReturn(propertyUploaderEmail2);
-
- Property propertyUploaderName2 = createMock(Property.class);
- expect(propertyFactory.create("uploader-name", "testName1")).andReturn(propertyUploaderName2);
-
- Property propertyUploaderUsername2 = createMock(Property.class);
- expect(propertyFactory.create("uploader-username", "testUsername1"))
- .andReturn(propertyUploaderUsername2);
-
- Property propertyAuthorEmail = createMock(Property.class);
- expect(propertyFactory.create("authorEmail", "testEmail2")).andReturn(propertyAuthorEmail);
-
- Property propertyAuthorName = createMock(Property.class);
- expect(propertyFactory.create("authorName", "testName2")).andReturn(propertyAuthorName);
-
- Property propertyAuthorUsername = createMock(Property.class);
- expect(propertyFactory.create("authorUsername", "testUsername2"))
- .andReturn(propertyAuthorUsername);
-
- Property propertyAuthorEmail2 = createMock(Property.class);
- expect(propertyFactory.create("author-email", "testEmail2")).andReturn(propertyAuthorEmail2);
-
- Property propertyAuthorName2 = createMock(Property.class);
- expect(propertyFactory.create("author-name", "testName2")).andReturn(propertyAuthorName2);
-
- Property propertyAuthorUsername2 = createMock(Property.class);
- expect(propertyFactory.create("author-username", "testUsername2"))
- .andReturn(propertyAuthorUsername2);
-
replayMocks();
PropertyAttributeExtractor extractor = injector.getInstance(PropertyAttributeExtractor.class);
- Set<Property> actual = extractor.extractFrom(patchSetAttribute);
+ Map<String, String> actual = extractor.extractFrom(patchSetAttribute);
- Set<Property> expected = Sets.newHashSet();
- expected.add(propertyRevision);
- expected.add(propertyNumber);
- expected.add(propertyNumber2);
- expected.add(propertyRef);
- expected.add(propertyCreatedOn);
- expected.add(propertyCreatedOn2);
- expected.add(propertyParents);
- expected.add(propertyDeletions);
- expected.add(propertyInsertions);
- expected.add(propertyUploaderEmail);
- expected.add(propertyUploaderName);
- expected.add(propertyUploaderUsername);
- expected.add(propertyUploaderEmail2);
- expected.add(propertyUploaderName2);
- expected.add(propertyUploaderUsername2);
- expected.add(propertyAuthorEmail);
- expected.add(propertyAuthorName);
- expected.add(propertyAuthorUsername);
- expected.add(propertyAuthorEmail2);
- expected.add(propertyAuthorName2);
- expected.add(propertyAuthorUsername2);
+ ImmutableMap<String, String> expected =
+ new ImmutableMap.Builder<String, String>()
+ .put("revision", "1234567891123456789212345678931234567894")
+ .put("patchSetNumber", "42")
+ .put("ref", "testRef")
+ .put("createdOn", "1234567890")
+ .put("parents", "[parent1, parent2]")
+ .put("deletions", "7")
+ .put("insertions", "12")
+ .put("uploaderEmail", "testEmail1")
+ .put("uploaderName", "testName1")
+ .put("uploaderUsername", "testUsername1")
+ .put("authorEmail", "testEmail2")
+ .put("authorName", "testName2")
+ .put("authorUsername", "testUsername2")
+ .build();
assertEquals("Properties do not match", expected, actual);
}
@@ -458,39 +260,20 @@
RefUpdateAttribute refUpdateAttribute = new RefUpdateAttribute();
refUpdateAttribute.newRev = "1234567891123456789212345678931234567894";
refUpdateAttribute.oldRev = "9876543211987654321298765432139876543214";
- refUpdateAttribute.project = "testProject";
refUpdateAttribute.refName = "testRef";
- Property propertyRevision = createMock(Property.class);
- expect(propertyFactory.create("revision", "1234567891123456789212345678931234567894"))
- .andReturn(propertyRevision);
-
- Property propertyRevisionOld = createMock(Property.class);
- expect(propertyFactory.create("revisionOld", "9876543211987654321298765432139876543214"))
- .andReturn(propertyRevisionOld);
-
- Property propertyRevisionOld2 = createMock(Property.class);
- expect(propertyFactory.create("revision-old", "9876543211987654321298765432139876543214"))
- .andReturn(propertyRevisionOld2);
-
- Property propertyProject = createMock(Property.class);
- expect(propertyFactory.create("project", "testProject")).andReturn(propertyProject);
-
- Property propertyRef = createMock(Property.class);
- expect(propertyFactory.create("ref", "testRef")).andReturn(propertyRef);
-
replayMocks();
PropertyAttributeExtractor extractor = injector.getInstance(PropertyAttributeExtractor.class);
- Set<Property> actual = extractor.extractFrom(refUpdateAttribute);
+ Map<String, String> actual = extractor.extractFrom(refUpdateAttribute);
- Set<Property> expected = Sets.newHashSet();
- expected.add(propertyRevision);
- expected.add(propertyRevisionOld);
- expected.add(propertyRevisionOld2);
- expected.add(propertyProject);
- expected.add(propertyRef);
+ ImmutableMap<String, String> expected =
+ new ImmutableMap.Builder<String, String>()
+ .put("revision", "1234567891123456789212345678931234567894")
+ .put("revisionOld", "9876543211987654321298765432139876543214")
+ .put("ref", "testRef")
+ .build();
assertEquals("Properties do not match", expected, actual);
}
@@ -499,21 +282,13 @@
approvalAttribute.type = "TestType";
approvalAttribute.value = "TestValue";
- Property propertyApproval = createMock(Property.class);
- expect(propertyFactory.create("approvalTestType", "TestValue")).andReturn(propertyApproval);
-
- Property propertyApproval2 = createMock(Property.class);
- expect(propertyFactory.create("approval-TestType", "TestValue")).andReturn(propertyApproval2);
-
replayMocks();
PropertyAttributeExtractor extractor = injector.getInstance(PropertyAttributeExtractor.class);
- Set<Property> actual = extractor.extractFrom(approvalAttribute);
+ Map<String, String> actual = extractor.extractFrom(approvalAttribute);
- Set<Property> expected = Sets.newHashSet();
- expected.add(propertyApproval);
- expected.add(propertyApproval2);
+ Map<String, String> expected = ImmutableMap.of("approvalTestType", "TestValue");
assertEquals("Properties do not match", expected, actual);
}
@@ -522,21 +297,13 @@
approvalAttribute.type = "Test-Type";
approvalAttribute.value = "TestValue";
- Property propertyApproval = createMock(Property.class);
- expect(propertyFactory.create("approvalTestType", "TestValue")).andReturn(propertyApproval);
-
- Property propertyApproval2 = createMock(Property.class);
- expect(propertyFactory.create("approval-Test-Type", "TestValue")).andReturn(propertyApproval2);
-
replayMocks();
PropertyAttributeExtractor extractor = injector.getInstance(PropertyAttributeExtractor.class);
- Set<Property> actual = extractor.extractFrom(approvalAttribute);
+ Map<String, String> actual = extractor.extractFrom(approvalAttribute);
- Set<Property> expected = Sets.newHashSet();
- expected.add(propertyApproval);
- expected.add(propertyApproval2);
+ Map<String, String> expected = ImmutableMap.of("approvalTestType", "TestValue");
assertEquals("Properties do not match", expected, actual);
}
@@ -551,8 +318,6 @@
protected void configure() {
facade = createMock(ItsFacade.class);
bind(ItsFacade.class).toInstance(facade);
- propertyFactory = createMock(Property.Factory.class);
- bind(Property.Factory.class).toInstance(propertyFactory);
}
}
}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/base/util/PropertyExtractorTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/base/util/PropertyExtractorTest.java
index 0b900c0..33e8aa6 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/its/base/util/PropertyExtractorTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/base/util/PropertyExtractorTest.java
@@ -16,12 +16,15 @@
import static org.easymock.EasyMock.expect;
import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.gerrit.extensions.annotations.PluginName;
import com.google.gerrit.extensions.config.FactoryModule;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.reviewdb.client.Project.NameKey;
import com.google.gerrit.server.data.AccountAttribute;
import com.google.gerrit.server.data.ApprovalAttribute;
import com.google.gerrit.server.data.ChangeAttribute;
@@ -31,77 +34,76 @@
import com.google.gerrit.server.events.ChangeMergedEvent;
import com.google.gerrit.server.events.ChangeRestoredEvent;
import com.google.gerrit.server.events.CommentAddedEvent;
-import com.google.gerrit.server.events.Event;
import com.google.gerrit.server.events.PatchSetCreatedEvent;
+import com.google.gerrit.server.events.RefEvent;
import com.google.gerrit.server.events.RefUpdatedEvent;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.googlesource.gerrit.plugins.its.base.testutil.LoggingMockingTestCase;
-import com.googlesource.gerrit.plugins.its.base.workflow.Property;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Optional;
import java.util.Set;
public class PropertyExtractorTest extends LoggingMockingTestCase {
private Injector injector;
+ private ItsProjectExtractor itsProjectExtractor;
private IssueExtractor issueExtractor;
- private Property.Factory propertyFactory;
private PropertyAttributeExtractor propertyAttributeExtractor;
public void testDummyChangeEvent() {
PropertyExtractor propertyExtractor = injector.getInstance(PropertyExtractor.class);
- Property property1 = createMock(Property.class);
- expect(
- propertyFactory.create(
- "event",
- "com.googlesource.gerrit.plugins."
- + "its.base.util.PropertyExtractorTest$DummyEvent"))
- .andReturn(property1);
+ expect(itsProjectExtractor.getItsProject("testProject")).andReturn(Optional.empty());
replayMocks();
- Set<Set<Property>> actual = propertyExtractor.extractFrom(new DummyEvent());
+ Set<Map<String, String>> actual =
+ propertyExtractor.extractFrom(new DummyEvent()).getIssuesProperties();
- Set<Set<Property>> expected = Sets.newHashSet();
+ Set<Map<String, String>> expected = new HashSet<>();
assertEquals("Properties do not match", expected, actual);
}
public void testChangeAbandonedEvent() {
ChangeAbandonedEvent event = new ChangeAbandonedEvent(testChange("testProject", "testBranch"));
+ expect(itsProjectExtractor.getItsProject("testProject")).andReturn(Optional.empty());
+
ChangeAttribute changeAttribute = createMock(ChangeAttribute.class);
event.change = Suppliers.ofInstance(changeAttribute);
- Property propertyChange = createMock(Property.class);
- expect(propertyAttributeExtractor.extractFrom(changeAttribute))
- .andReturn(Sets.newHashSet(propertyChange));
+ Map<String, String> changeProperties =
+ ImmutableMap.of("project", "testProject", "changeNumber", "176");
+ expect(propertyAttributeExtractor.extractFrom(changeAttribute)).andReturn(changeProperties);
AccountAttribute accountAttribute = createMock(AccountAttribute.class);
event.abandoner = Suppliers.ofInstance(accountAttribute);
- Property propertySubmitter = createMock(Property.class);
+ Map<String, String> accountProperties = ImmutableMap.of("abandonerName", "testName");
expect(propertyAttributeExtractor.extractFrom(accountAttribute, "abandoner"))
- .andReturn(Sets.newHashSet(propertySubmitter));
+ .andReturn(accountProperties);
PatchSetAttribute patchSetAttribute = createMock(PatchSetAttribute.class);
event.patchSet = Suppliers.ofInstance(patchSetAttribute);
- Property propertyPatchSet = createMock(Property.class);
- expect(propertyAttributeExtractor.extractFrom(patchSetAttribute))
- .andReturn(Sets.newHashSet(propertyPatchSet));
+ Map<String, String> patchSetProperties =
+ ImmutableMap.of("revision", "testRevision", "patchSetNumber", "3");
+ expect(propertyAttributeExtractor.extractFrom(patchSetAttribute)).andReturn(patchSetProperties);
event.reason = "testReason";
- Property propertyReason = createMock(Property.class);
- expect(propertyFactory.create("reason", "testReason")).andReturn(propertyReason);
-
changeAttribute.project = "testProject";
changeAttribute.number = 176;
patchSetAttribute.revision = "testRevision";
patchSetAttribute.number = 3;
- Set<Property> common = Sets.newHashSet();
- common.add(propertyChange);
- common.add(propertySubmitter);
- common.add(propertyPatchSet);
- common.add(propertyReason);
+ Map<String, String> common =
+ ImmutableMap.<String, String>builder()
+ .putAll(changeProperties)
+ .putAll(accountProperties)
+ .putAll(patchSetProperties)
+ .put("reason", "testReason")
+ .put("ref", "refs/heads/testBranch")
+ .build();
eventHelper(event, "ChangeAbandonedEvent", "change-abandoned", common, true);
}
@@ -109,33 +111,38 @@
public void testChangeMergedEvent() {
ChangeMergedEvent event = new ChangeMergedEvent(testChange("testProject", "testBranch"));
+ expect(itsProjectExtractor.getItsProject("testProject")).andReturn(Optional.empty());
+
ChangeAttribute changeAttribute = createMock(ChangeAttribute.class);
event.change = Suppliers.ofInstance(changeAttribute);
- Property propertyChange = createMock(Property.class);
- expect(propertyAttributeExtractor.extractFrom(changeAttribute))
- .andReturn(Sets.newHashSet(propertyChange));
+ Map<String, String> changeProperties =
+ ImmutableMap.of("project", "testProject", "changeNumber", "176");
+ expect(propertyAttributeExtractor.extractFrom(changeAttribute)).andReturn(changeProperties);
AccountAttribute accountAttribute = createMock(AccountAttribute.class);
event.submitter = Suppliers.ofInstance(accountAttribute);
- Property propertySubmitter = createMock(Property.class);
+ Map<String, String> accountProperties = ImmutableMap.of("submitterName", "testName");
expect(propertyAttributeExtractor.extractFrom(accountAttribute, "submitter"))
- .andReturn(Sets.newHashSet(propertySubmitter));
+ .andReturn(accountProperties);
PatchSetAttribute patchSetAttribute = createMock(PatchSetAttribute.class);
event.patchSet = Suppliers.ofInstance(patchSetAttribute);
- Property propertyPatchSet = createMock(Property.class);
- expect(propertyAttributeExtractor.extractFrom(patchSetAttribute))
- .andReturn(Sets.newHashSet(propertyPatchSet));
+ Map<String, String> patchSetProperties =
+ ImmutableMap.of("revision", "testRevision", "patchSetNumber", "3");
+ expect(propertyAttributeExtractor.extractFrom(patchSetAttribute)).andReturn(patchSetProperties);
changeAttribute.project = "testProject";
changeAttribute.number = 176;
patchSetAttribute.revision = "testRevision";
patchSetAttribute.number = 3;
- Set<Property> common = Sets.newHashSet();
- common.add(propertyChange);
- common.add(propertySubmitter);
- common.add(propertyPatchSet);
+ Map<String, String> common =
+ ImmutableMap.<String, String>builder()
+ .putAll(changeProperties)
+ .putAll(accountProperties)
+ .putAll(patchSetProperties)
+ .put("ref", "refs/heads/testBranch")
+ .build();
eventHelper(event, "ChangeMergedEvent", "change-merged", common, true);
}
@@ -143,38 +150,40 @@
public void testChangeRestoredEvent() {
ChangeRestoredEvent event = new ChangeRestoredEvent(testChange("testProject", "testBranch"));
+ expect(itsProjectExtractor.getItsProject("testProject")).andReturn(Optional.empty());
+
ChangeAttribute changeAttribute = createMock(ChangeAttribute.class);
event.change = Suppliers.ofInstance(changeAttribute);
- Property propertyChange = createMock(Property.class);
- expect(propertyAttributeExtractor.extractFrom(changeAttribute))
- .andReturn(Sets.newHashSet(propertyChange));
+ Map<String, String> changeProperties =
+ ImmutableMap.of("project", "testProject", "changeNumber", "176");
+ expect(propertyAttributeExtractor.extractFrom(changeAttribute)).andReturn(changeProperties);
AccountAttribute accountAttribute = createMock(AccountAttribute.class);
event.restorer = Suppliers.ofInstance(accountAttribute);
- Property propertySubmitter = createMock(Property.class);
+ Map<String, String> accountProperties = ImmutableMap.of("restorerName", "testName");
expect(propertyAttributeExtractor.extractFrom(accountAttribute, "restorer"))
- .andReturn(Sets.newHashSet(propertySubmitter));
+ .andReturn(accountProperties);
PatchSetAttribute patchSetAttribute = createMock(PatchSetAttribute.class);
event.patchSet = Suppliers.ofInstance(patchSetAttribute);
- Property propertyPatchSet = createMock(Property.class);
- expect(propertyAttributeExtractor.extractFrom(patchSetAttribute))
- .andReturn(Sets.newHashSet(propertyPatchSet));
+ Map<String, String> patchSetProperties =
+ ImmutableMap.of("revision", "testRevision", "patchSetNumber", "3");
+ expect(propertyAttributeExtractor.extractFrom(patchSetAttribute)).andReturn(patchSetProperties);
event.reason = "testReason";
- Property propertyReason = createMock(Property.class);
- expect(propertyFactory.create("reason", "testReason")).andReturn(propertyReason);
-
changeAttribute.project = "testProject";
changeAttribute.number = 176;
patchSetAttribute.revision = "testRevision";
patchSetAttribute.number = 3;
- Set<Property> common = Sets.newHashSet();
- common.add(propertyChange);
- common.add(propertySubmitter);
- common.add(propertyPatchSet);
- common.add(propertyReason);
+ Map<String, String> common =
+ ImmutableMap.<String, String>builder()
+ .putAll(changeProperties)
+ .putAll(accountProperties)
+ .putAll(patchSetProperties)
+ .put("reason", "testReason")
+ .put("ref", "refs/heads/testBranch")
+ .build();
eventHelper(event, "ChangeRestoredEvent", "change-restored", common, true);
}
@@ -182,38 +191,40 @@
public void testCommentAddedEventWOApprovals() {
CommentAddedEvent event = new CommentAddedEvent(testChange("testProject", "testBranch"));
+ expect(itsProjectExtractor.getItsProject("testProject")).andReturn(Optional.empty());
+
ChangeAttribute changeAttribute = createMock(ChangeAttribute.class);
event.change = Suppliers.ofInstance(changeAttribute);
- Property propertyChange = createMock(Property.class);
- expect(propertyAttributeExtractor.extractFrom(changeAttribute))
- .andReturn(Sets.newHashSet(propertyChange));
+ Map<String, String> changeProperties =
+ ImmutableMap.of("project", "testProject", "changeNumber", "176");
+ expect(propertyAttributeExtractor.extractFrom(changeAttribute)).andReturn(changeProperties);
AccountAttribute accountAttribute = createMock(AccountAttribute.class);
event.author = Suppliers.ofInstance(accountAttribute);
- Property propertySubmitter = createMock(Property.class);
+ Map<String, String> accountProperties = ImmutableMap.of("commenterName", "testName");
expect(propertyAttributeExtractor.extractFrom(accountAttribute, "commenter"))
- .andReturn(Sets.newHashSet(propertySubmitter));
+ .andReturn(accountProperties);
PatchSetAttribute patchSetAttribute = createMock(PatchSetAttribute.class);
event.patchSet = Suppliers.ofInstance(patchSetAttribute);
- Property propertyPatchSet = createMock(Property.class);
- expect(propertyAttributeExtractor.extractFrom(patchSetAttribute))
- .andReturn(Sets.newHashSet(propertyPatchSet));
+ Map<String, String> patchSetProperties =
+ ImmutableMap.of("revision", "testRevision", "patchSetNumber", "3");
+ expect(propertyAttributeExtractor.extractFrom(patchSetAttribute)).andReturn(patchSetProperties);
event.comment = "testComment";
- Property propertyComment = createMock(Property.class);
- expect(propertyFactory.create("comment", "testComment")).andReturn(propertyComment);
-
changeAttribute.project = "testProject";
changeAttribute.number = 176;
patchSetAttribute.revision = "testRevision";
patchSetAttribute.number = 3;
- Set<Property> common = Sets.newHashSet();
- common.add(propertyChange);
- common.add(propertySubmitter);
- common.add(propertyPatchSet);
- common.add(propertyComment);
+ Map<String, String> common =
+ ImmutableMap.<String, String>builder()
+ .putAll(changeProperties)
+ .putAll(accountProperties)
+ .putAll(patchSetProperties)
+ .put("ref", "refs/heads/testBranch")
+ .put("comment", "testComment")
+ .build();
eventHelper(event, "CommentAddedEvent", "comment-added", common, true);
}
@@ -221,51 +232,53 @@
public void testCommentAddedEventWApprovals() {
CommentAddedEvent event = new CommentAddedEvent(testChange("testProject", "testBranch"));
+ expect(itsProjectExtractor.getItsProject("testProject")).andReturn(Optional.empty());
+
ChangeAttribute changeAttribute = createMock(ChangeAttribute.class);
event.change = Suppliers.ofInstance(changeAttribute);
- Property propertyChange = createMock(Property.class);
- expect(propertyAttributeExtractor.extractFrom(changeAttribute))
- .andReturn(Sets.newHashSet(propertyChange));
+ Map<String, String> changeProperties =
+ ImmutableMap.of("project", "testProject", "changeNumber", "176");
+ expect(propertyAttributeExtractor.extractFrom(changeAttribute)).andReturn(changeProperties);
AccountAttribute accountAttribute = createMock(AccountAttribute.class);
event.author = Suppliers.ofInstance(accountAttribute);
- Property propertySubmitter = createMock(Property.class);
+ Map<String, String> accountProperties = ImmutableMap.of("commenterName", "testName");
expect(propertyAttributeExtractor.extractFrom(accountAttribute, "commenter"))
- .andReturn(Sets.newHashSet(propertySubmitter));
+ .andReturn(accountProperties);
PatchSetAttribute patchSetAttribute = createMock(PatchSetAttribute.class);
event.patchSet = Suppliers.ofInstance(patchSetAttribute);
- Property propertyPatchSet = createMock(Property.class);
- expect(propertyAttributeExtractor.extractFrom(patchSetAttribute))
- .andReturn(Sets.newHashSet(propertyPatchSet));
+ Map<String, String> patchSetProperties =
+ ImmutableMap.of("revision", "testRevision", "patchSetNumber", "3");
+ expect(propertyAttributeExtractor.extractFrom(patchSetAttribute)).andReturn(patchSetProperties);
ApprovalAttribute approvalAttribute1 = createMock(ApprovalAttribute.class);
- Property propertyApproval1 = createMock(Property.class);
+ Map<String, String> approuvalProperties1 = ImmutableMap.of("approvalCodeReview", "0");
expect(propertyAttributeExtractor.extractFrom(approvalAttribute1))
- .andReturn(Sets.newHashSet(propertyApproval1));
+ .andReturn(approuvalProperties1);
ApprovalAttribute approvalAttribute2 = createMock(ApprovalAttribute.class);
- Property propertyApproval2 = createMock(Property.class);
+ Map<String, String> approuvalProperties2 = ImmutableMap.of("approvalVerified", "0");
expect(propertyAttributeExtractor.extractFrom(approvalAttribute2))
- .andReturn(Sets.newHashSet(propertyApproval2));
+ .andReturn(approuvalProperties2);
ApprovalAttribute[] approvalAttributes = {approvalAttribute1, approvalAttribute2};
event.approvals = Suppliers.ofInstance(approvalAttributes);
event.comment = "testComment";
- Property propertyComment = createMock(Property.class);
- expect(propertyFactory.create("comment", "testComment")).andReturn(propertyComment);
-
changeAttribute.project = "testProject";
changeAttribute.number = 176;
patchSetAttribute.revision = "testRevision";
patchSetAttribute.number = 3;
- Set<Property> common = Sets.newHashSet();
- common.add(propertyChange);
- common.add(propertySubmitter);
- common.add(propertyPatchSet);
- common.add(propertyComment);
- common.add(propertyApproval1);
- common.add(propertyApproval2);
+ Map<String, String> common =
+ ImmutableMap.<String, String>builder()
+ .putAll(changeProperties)
+ .putAll(accountProperties)
+ .putAll(patchSetProperties)
+ .putAll(approuvalProperties1)
+ .putAll(approuvalProperties2)
+ .put("ref", "refs/heads/testBranch")
+ .put("comment", "testComment")
+ .build();
eventHelper(event, "CommentAddedEvent", "comment-added", common, true);
}
@@ -273,33 +286,38 @@
public void testPatchSetCreatedEvent() {
PatchSetCreatedEvent event = new PatchSetCreatedEvent(testChange("testProject", "testBranch"));
+ expect(itsProjectExtractor.getItsProject("testProject")).andReturn(Optional.empty());
+
ChangeAttribute changeAttribute = createMock(ChangeAttribute.class);
event.change = Suppliers.ofInstance(changeAttribute);
- Property propertyChange = createMock(Property.class);
- expect(propertyAttributeExtractor.extractFrom(changeAttribute))
- .andReturn(Sets.newHashSet(propertyChange));
+ Map<String, String> changeProperties =
+ ImmutableMap.of("project", "testProject", "changeNumber", "176");
+ expect(propertyAttributeExtractor.extractFrom(changeAttribute)).andReturn(changeProperties);
AccountAttribute accountAttribute = createMock(AccountAttribute.class);
event.uploader = Suppliers.ofInstance(accountAttribute);
- Property propertySubmitter = createMock(Property.class);
+ Map<String, String> accountProperties = ImmutableMap.of("uploaderName", "testName");
expect(propertyAttributeExtractor.extractFrom(accountAttribute, "uploader"))
- .andReturn(Sets.newHashSet(propertySubmitter));
+ .andReturn(accountProperties);
PatchSetAttribute patchSetAttribute = createMock(PatchSetAttribute.class);
event.patchSet = Suppliers.ofInstance(patchSetAttribute);
- Property propertyPatchSet = createMock(Property.class);
- expect(propertyAttributeExtractor.extractFrom(patchSetAttribute))
- .andReturn(Sets.newHashSet(propertyPatchSet));
+ Map<String, String> patchSetProperties =
+ ImmutableMap.of("revision", "testRevision", "patchSetNumber", "3");
+ expect(propertyAttributeExtractor.extractFrom(patchSetAttribute)).andReturn(patchSetProperties);
changeAttribute.project = "testProject";
changeAttribute.number = 176;
patchSetAttribute.revision = "testRevision";
patchSetAttribute.number = 3;
- Set<Property> common = Sets.newHashSet();
- common.add(propertyChange);
- common.add(propertySubmitter);
- common.add(propertyPatchSet);
+ Map<String, String> common =
+ ImmutableMap.<String, String>builder()
+ .putAll(changeProperties)
+ .putAll(accountProperties)
+ .putAll(patchSetProperties)
+ .put("ref", "refs/heads/testBranch")
+ .build();
eventHelper(event, "PatchSetCreatedEvent", "patchset-created", common, true);
}
@@ -309,57 +327,43 @@
AccountAttribute accountAttribute = createMock(AccountAttribute.class);
event.submitter = Suppliers.ofInstance(accountAttribute);
- Property propertySubmitter = createMock(Property.class);
+ Map<String, String> accountProperties = ImmutableMap.of("submitterName", "testName");
expect(propertyAttributeExtractor.extractFrom(accountAttribute, "submitter"))
- .andReturn(Sets.newHashSet(propertySubmitter));
+ .andReturn(accountProperties);
RefUpdateAttribute refUpdateAttribute = createMock(RefUpdateAttribute.class);
event.refUpdate = Suppliers.ofInstance(refUpdateAttribute);
- Property propertyRefUpdated = createMock(Property.class);
+ Map<String, String> refUpdatedProperties =
+ ImmutableMap.of("revision", "testRevision", "revisionOld", "oldRevision");
expect(propertyAttributeExtractor.extractFrom(refUpdateAttribute))
- .andReturn(Sets.newHashSet(propertyRefUpdated));
+ .andReturn(refUpdatedProperties);
refUpdateAttribute.project = "testProject";
refUpdateAttribute.newRev = "testRevision";
+ refUpdateAttribute.oldRev = "oldRevision";
+ refUpdateAttribute.refName = "testBranch";
- Set<Property> common = Sets.newHashSet();
- common.add(propertySubmitter);
- common.add(propertyRefUpdated);
+ expect(itsProjectExtractor.getItsProject("testProject")).andReturn(Optional.empty());
+
+ Map<String, String> common =
+ ImmutableMap.<String, String>builder()
+ .putAll(accountProperties)
+ .putAll(refUpdatedProperties)
+ .put("ref", "testBranch")
+ .put("project", "testProject")
+ .build();
eventHelper(event, "RefUpdatedEvent", "ref-updated", common, false);
}
private void eventHelper(
- Event event, String className, String type, Set<Property> common, boolean withRevision) {
+ RefEvent event,
+ String className,
+ String type,
+ Map<String, String> common,
+ boolean withRevision) {
PropertyExtractor propertyExtractor = injector.getInstance(PropertyExtractor.class);
- Property propertyItsName = createMock(Property.class);
- expect(propertyFactory.create("its-name", "ItsTestName")).andReturn(propertyItsName).anyTimes();
-
- Property propertyEvent = createMock(Property.class);
- expect(propertyFactory.create("event", "com.google.gerrit.server.events." + className))
- .andReturn(propertyEvent);
-
- Property propertyEventType = createMock(Property.class);
- expect(propertyFactory.create("event-type", type)).andReturn(propertyEventType);
-
- Property propertyAssociationFooter = createMock(Property.class);
- expect(propertyFactory.create("association", "footer")).andReturn(propertyAssociationFooter);
-
- Property propertyAssociationAnywhere = createMock(Property.class);
- expect(propertyFactory.create("association", "anywhere"))
- .andReturn(propertyAssociationAnywhere)
- .times(2);
-
- Property propertyAssociationBody = createMock(Property.class);
- expect(propertyFactory.create("association", "body")).andReturn(propertyAssociationBody);
-
- Property propertyIssue42 = createMock(Property.class);
- expect(propertyFactory.create("issue", "42")).andReturn(propertyIssue42);
-
- Property propertyIssue4711 = createMock(Property.class);
- expect(propertyFactory.create("issue", "4711")).andReturn(propertyIssue4711);
-
HashMap<String, Set<String>> issueMap = Maps.newHashMap();
issueMap.put("4711", Sets.newHashSet("body", "anywhere"));
issueMap.put("42", Sets.newHashSet("footer", "anywhere"));
@@ -373,28 +377,30 @@
replayMocks();
- Set<Set<Property>> actual = propertyExtractor.extractFrom(event);
+ Set<Map<String, String>> actual = propertyExtractor.extractFrom(event).getIssuesProperties();
- Set<Set<Property>> expected = Sets.newHashSet();
- Set<Property> properties = Sets.newHashSet();
- properties.add(propertyItsName);
- properties.add(propertyEvent);
- properties.add(propertyEventType);
- properties.add(propertyAssociationAnywhere);
- properties.add(propertyAssociationFooter);
- properties.add(propertyIssue42);
- properties.addAll(common);
- expected.add(properties);
+ Map<String, String> propertiesIssue4711 =
+ ImmutableMap.<String, String>builder()
+ .put("itsName", "ItsTestName")
+ .put("event", "com.google.gerrit.server.events." + className)
+ .put("event-type", type)
+ .put("association", "body anywhere")
+ .put("issue", "4711")
+ .putAll(common)
+ .build();
+ Map<String, String> propertiesIssue42 =
+ ImmutableMap.<String, String>builder()
+ .put("itsName", "ItsTestName")
+ .put("event", "com.google.gerrit.server.events." + className)
+ .put("event-type", type)
+ .put("association", "anywhere footer")
+ .put("issue", "42")
+ .putAll(common)
+ .build();
+ Set<Map<String, String>> expected = new HashSet<>();
+ expected.add(propertiesIssue4711);
+ expected.add(propertiesIssue42);
- properties = Sets.newHashSet();
- properties.add(propertyItsName);
- properties.add(propertyEvent);
- properties.add(propertyEventType);
- properties.add(propertyAssociationAnywhere);
- properties.add(propertyAssociationBody);
- properties.add(propertyIssue4711);
- properties.addAll(common);
- expected.add(properties);
assertEquals("Properties do not match", expected, actual);
}
@@ -409,20 +415,30 @@
protected void configure() {
bind(String.class).annotatedWith(PluginName.class).toInstance("ItsTestName");
+ itsProjectExtractor = createMock(ItsProjectExtractor.class);
+ bind(ItsProjectExtractor.class).toInstance(itsProjectExtractor);
+
issueExtractor = createMock(IssueExtractor.class);
bind(IssueExtractor.class).toInstance(issueExtractor);
propertyAttributeExtractor = createMock(PropertyAttributeExtractor.class);
bind(PropertyAttributeExtractor.class).toInstance(propertyAttributeExtractor);
-
- propertyFactory = createMock(Property.Factory.class);
- bind(Property.Factory.class).toInstance(propertyFactory);
}
}
- private class DummyEvent extends Event {
+ private class DummyEvent extends RefEvent {
public DummyEvent() {
super(null);
}
+
+ @Override
+ public String getRefName() {
+ return null;
+ }
+
+ @Override
+ public NameKey getProjectNameKey() {
+ return new Project.NameKey("testProject");
+ }
}
}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/base/validation/ItsValidateCommentTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/base/validation/ItsValidateCommentTest.java
index 5573878..d247a7c 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/its/base/validation/ItsValidateCommentTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/base/validation/ItsValidateCommentTest.java
@@ -26,10 +26,12 @@
import com.google.inject.Injector;
import com.googlesource.gerrit.plugins.its.base.its.ItsConfig;
import com.googlesource.gerrit.plugins.its.base.its.ItsFacade;
+import com.googlesource.gerrit.plugins.its.base.its.ItsFacadeFactory;
import com.googlesource.gerrit.plugins.its.base.testutil.LoggingMockingTestCase;
import com.googlesource.gerrit.plugins.its.base.util.IssueExtractor;
import java.io.IOException;
import java.util.List;
+import java.util.Optional;
import java.util.regex.Pattern;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.transport.ReceiveCommand;
@@ -44,6 +46,7 @@
private IssueExtractor issueExtractor;
private ItsFacade itsFacade;
private ItsConfig itsConfig;
+ private ItsFacadeFactory itsFacadeFactory;
private Project project = new Project(new Project.NameKey("myProject"));
@@ -75,6 +78,7 @@
expect(itsConfig.getItsAssociationPolicy())
.andReturn(ItsAssociationPolicy.SUGGESTED)
.atLeastOnce();
+ expect(itsConfig.getDummyIssuePattern()).andReturn(Optional.empty()).atLeastOnce();
expect(commit.getFullMessage()).andReturn("TestMessage").atLeastOnce();
expect(commit.getId()).andReturn(commit).anyTimes();
expect(commit.getName()).andReturn("TestCommit").anyTimes();
@@ -99,6 +103,7 @@
expect(itsConfig.getItsAssociationPolicy())
.andReturn(ItsAssociationPolicy.MANDATORY)
.atLeastOnce();
+ expect(itsConfig.getDummyIssuePattern()).andReturn(Optional.empty()).atLeastOnce();
expect(commit.getFullMessage()).andReturn("TestMessage").atLeastOnce();
expect(commit.getId()).andReturn(commit).anyTimes();
expect(commit.getName()).andReturn("TestCommit").anyTimes();
@@ -116,6 +121,31 @@
}
}
+ public void testOnlySkipMatching() throws CommitValidationException {
+ List<CommitValidationMessage> ret;
+ ItsValidateComment ivc = injector.getInstance(ItsValidateComment.class);
+ ReceiveCommand command = createMock(ReceiveCommand.class);
+ RevCommit commit = createMock(RevCommit.class);
+ CommitReceivedEvent event = newCommitReceivedEvent(command, project, null, commit, null);
+
+ expect(itsConfig.getItsAssociationPolicy())
+ .andReturn(ItsAssociationPolicy.MANDATORY)
+ .atLeastOnce();
+ expect(itsConfig.getDummyIssuePattern())
+ .andReturn(Optional.of(Pattern.compile("SKIP")))
+ .atLeastOnce();
+ expect(commit.getFullMessage()).andReturn("TestMessage SKIP").atLeastOnce();
+ expect(commit.getId()).andReturn(commit).anyTimes();
+ expect(commit.getName()).andReturn("TestCommit").anyTimes();
+ expect(issueExtractor.getIssueIds("TestMessage SKIP")).andReturn(new String[] {}).atLeastOnce();
+
+ replayMocks();
+
+ ret = ivc.onCommitReceived(event);
+
+ assertEmptyList(ret);
+ }
+
public void testSuggestedMatchingSingleExisting() throws CommitValidationException, IOException {
List<CommitValidationMessage> ret;
ItsValidateComment ivc = injector.getInstance(ItsValidateComment.class);
@@ -129,6 +159,7 @@
expect(commit.getId()).andReturn(commit).anyTimes();
expect(commit.getName()).andReturn("TestCommit").anyTimes();
expect(issueExtractor.getIssueIds("bug#4711")).andReturn(new String[] {"4711"}).atLeastOnce();
+ expect(itsFacadeFactory.getFacade(project.getNameKey())).andReturn(itsFacade);
expect(itsFacade.exists("4711")).andReturn(true).atLeastOnce();
replayMocks();
@@ -152,6 +183,7 @@
expect(commit.getId()).andReturn(commit).anyTimes();
expect(commit.getName()).andReturn("TestCommit").anyTimes();
expect(issueExtractor.getIssueIds("bug#4711")).andReturn(new String[] {"4711"}).atLeastOnce();
+ expect(itsFacadeFactory.getFacade(project.getNameKey())).andReturn(itsFacade);
expect(itsFacade.exists("4711")).andReturn(true).atLeastOnce();
replayMocks();
@@ -176,6 +208,7 @@
expect(commit.getId()).andReturn(commit).anyTimes();
expect(commit.getName()).andReturn("TestCommit").anyTimes();
expect(issueExtractor.getIssueIds("bug#4711")).andReturn(new String[] {"4711"}).atLeastOnce();
+ expect(itsFacadeFactory.getFacade(project.getNameKey())).andReturn(itsFacade);
expect(itsFacade.exists("4711")).andReturn(false).atLeastOnce();
replayMocks();
@@ -204,6 +237,7 @@
expect(commit.getId()).andReturn(commit).anyTimes();
expect(commit.getName()).andReturn("TestCommit").anyTimes();
expect(issueExtractor.getIssueIds("bug#4711")).andReturn(new String[] {"4711"}).atLeastOnce();
+ expect(itsFacadeFactory.getFacade(project.getNameKey())).andReturn(itsFacade);
expect(itsFacade.exists("4711")).andReturn(false).atLeastOnce();
replayMocks();
@@ -234,6 +268,7 @@
expect(issueExtractor.getIssueIds("bug#4711, bug#42"))
.andReturn(new String[] {"4711", "42"})
.atLeastOnce();
+ expect(itsFacadeFactory.getFacade(project.getNameKey())).andReturn(itsFacade).anyTimes();
expect(itsFacade.exists("4711")).andReturn(true).atLeastOnce();
expect(itsFacade.exists("42")).andReturn(true).atLeastOnce();
@@ -260,6 +295,7 @@
expect(issueExtractor.getIssueIds("bug#4711, bug#42"))
.andReturn(new String[] {"4711", "42"})
.atLeastOnce();
+ expect(itsFacadeFactory.getFacade(project.getNameKey())).andReturn(itsFacade).anyTimes();
expect(itsFacade.exists("4711")).andReturn(true).atLeastOnce();
expect(itsFacade.exists("42")).andReturn(true).atLeastOnce();
@@ -287,9 +323,9 @@
expect(issueExtractor.getIssueIds("bug#4711, bug#42"))
.andReturn(new String[] {"4711", "42"})
.atLeastOnce();
+ expect(itsFacadeFactory.getFacade(project.getNameKey())).andReturn(itsFacade).anyTimes();
expect(itsFacade.exists("4711")).andReturn(false).atLeastOnce();
expect(itsFacade.exists("42")).andReturn(true).atLeastOnce();
-
replayMocks();
ret = ivc.onCommitReceived(event);
@@ -321,6 +357,7 @@
expect(issueExtractor.getIssueIds("bug#4711, bug#42"))
.andReturn(new String[] {"4711", "42"})
.atLeastOnce();
+ expect(itsFacadeFactory.getFacade(project.getNameKey())).andReturn(itsFacade).anyTimes();
expect(itsFacade.exists("4711")).andReturn(false).atLeastOnce();
expect(itsFacade.exists("42")).andReturn(true).atLeastOnce();
@@ -353,6 +390,7 @@
expect(issueExtractor.getIssueIds("bug#4711, bug#42"))
.andReturn(new String[] {"4711", "42"})
.atLeastOnce();
+ expect(itsFacadeFactory.getFacade(project.getNameKey())).andReturn(itsFacade).anyTimes();
expect(itsFacade.exists("4711")).andReturn(false).atLeastOnce();
expect(itsFacade.exists("42")).andReturn(false).atLeastOnce();
@@ -387,8 +425,9 @@
expect(issueExtractor.getIssueIds("bug#4711, bug#42"))
.andReturn(new String[] {"4711", "42"})
.atLeastOnce();
+ expect(itsFacadeFactory.getFacade(project.getNameKey())).andReturn(itsFacade).anyTimes();
expect(itsFacade.exists("4711")).andReturn(false).atLeastOnce();
- expect(itsFacade.exists("42")).andReturn(false).atLeastOnce();
+ expect(itsFacade.exists("42")).andReturn(true).atLeastOnce();
replayMocks();
@@ -419,6 +458,7 @@
expect(issueExtractor.getIssueIds("bug#4711, bug#42"))
.andReturn(new String[] {"4711", "42"})
.atLeastOnce();
+ expect(itsFacadeFactory.getFacade(project.getNameKey())).andReturn(itsFacade).anyTimes();
expect(itsFacade.exists("4711")).andThrow(new IOException("InjectedEx1")).atLeastOnce();
expect(itsFacade.exists("42")).andReturn(false).atLeastOnce();
@@ -506,6 +546,9 @@
itsConfig = createMock(ItsConfig.class);
bind(ItsConfig.class).toInstance(itsConfig);
+
+ itsFacadeFactory = createMock(ItsFacadeFactory.class);
+ bind(ItsFacadeFactory.class).toInstance(itsFacadeFactory);
}
}
}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionControllerTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionControllerTest.java
index d2926f9..bd32089 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionControllerTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionControllerTest.java
@@ -16,11 +16,12 @@
import static org.easymock.EasyMock.anyObject;
import static org.easymock.EasyMock.expect;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
import com.google.gerrit.extensions.config.FactoryModule;
import com.google.gerrit.server.events.ChangeEvent;
-import com.google.gerrit.server.events.Event;
+import com.google.gerrit.server.events.RefEvent;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.googlesource.gerrit.plugins.its.base.its.ItsConfig;
@@ -28,6 +29,8 @@
import com.googlesource.gerrit.plugins.its.base.util.PropertyExtractor;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
import java.util.Set;
public class ActionControllerTest extends LoggingMockingTestCase {
@@ -43,125 +46,100 @@
ChangeEvent event = createMock(ChangeEvent.class);
- Set<Set<Property>> propertySets = Collections.emptySet();
- expect(propertyExtractor.extractFrom(event)).andReturn(propertySets).anyTimes();
+ Set<Map<String, String>> propertySets = new HashSet<>();
+ expect(propertyExtractor.extractFrom(event))
+ .andReturn(new RefEventProperties(Collections.emptyMap(), propertySets))
+ .anyTimes();
replayMocks();
actionController.onEvent(event);
}
- public void testNoActions() {
+ public void testNoActionsOrNoIssues() {
ActionController actionController = createActionController();
ChangeEvent event = createMock(ChangeEvent.class);
- Set<Set<Property>> propertySets = Sets.newHashSet();
- Set<Property> propertySet = Collections.emptySet();
- propertySets.add(propertySet);
+ Set<Map<String, String>> propertySets = new HashSet<>();
+ Map<String, String> properties = ImmutableMap.of("fake", "property");
+ propertySets.add(properties);
- expect(propertyExtractor.extractFrom(event)).andReturn(propertySets).anyTimes();
+ expect(propertyExtractor.extractFrom(event))
+ .andReturn(new RefEventProperties(properties, propertySets))
+ .anyTimes();
+ // When no issues are found in the commit message, the list of actions is empty
+ // as there are no matchs with an empty map of properties.
Collection<ActionRequest> actions = Collections.emptySet();
- expect(ruleBase.actionRequestsFor(propertySet)).andReturn(actions).once();
+ expect(ruleBase.actionRequestsFor(properties)).andReturn(actions).times(2);
replayMocks();
actionController.onEvent(event);
}
- public void testNoIssues() {
+ public void testSinglePropertyMapSingleIssueActionSingleProjectAction() {
ActionController actionController = createActionController();
ChangeEvent event = createMock(ChangeEvent.class);
- Set<Set<Property>> propertySets = Sets.newHashSet();
- Set<Property> propertySet = Collections.emptySet();
- propertySets.add(propertySet);
+ Map<String, String> projectProperties = ImmutableMap.of("its-project", "itsProject");
- expect(propertyExtractor.extractFrom(event)).andReturn(propertySets).anyTimes();
+ Map<String, String> issueProperties =
+ ImmutableMap.<String, String>builder()
+ .putAll(projectProperties)
+ .put("issue", "testIssue")
+ .build();
- Collection<ActionRequest> actions = Lists.newArrayListWithCapacity(1);
- ActionRequest action1 = createMock(ActionRequest.class);
- actions.add(action1);
- expect(ruleBase.actionRequestsFor(propertySet)).andReturn(actions).once();
+ Set<Map<String, String>> propertySets = ImmutableSet.of(issueProperties);
+
+ expect(propertyExtractor.extractFrom(event))
+ .andReturn(new RefEventProperties(projectProperties, propertySets))
+ .anyTimes();
+
+ ActionRequest issueActionRequest1 = createMock(ActionRequest.class);
+ Collection<ActionRequest> issueActionRequests = ImmutableList.of(issueActionRequest1);
+ expect(ruleBase.actionRequestsFor(issueProperties)).andReturn(issueActionRequests).once();
+
+ ActionRequest projectActionRequest1 = createMock(ActionRequest.class);
+ Collection<ActionRequest> projectActionRequests = ImmutableList.of(projectActionRequest1);
+ expect(ruleBase.actionRequestsFor(projectProperties)).andReturn(projectActionRequests).once();
+
+ actionExecutor.executeOnIssue(issueActionRequests, issueProperties);
+ actionExecutor.executeOnProject(projectActionRequests, projectProperties);
replayMocks();
actionController.onEvent(event);
}
- public void testSinglePropertySetSingleActionSingleIssue() {
+ public void testMultiplePropertyMapsMultipleActionMultipleIssue() {
ActionController actionController = createActionController();
ChangeEvent event = createMock(ChangeEvent.class);
- Property propertyIssue1 = createMock(Property.class);
- expect(propertyIssue1.getKey()).andReturn("issue").anyTimes();
- expect(propertyIssue1.getValue()).andReturn("testIssue").anyTimes();
+ Map<String, String> properties1 = ImmutableMap.of("issue", "testIssue");
+ Map<String, String> properties2 = ImmutableMap.of("issue", "testIssue2");
- Set<Property> propertySet = Sets.newHashSet();
- propertySet.add(propertyIssue1);
+ Set<Map<String, String>> propertySets = ImmutableSet.of(properties1, properties2);
- Set<Set<Property>> propertySets = Sets.newHashSet();
- propertySets.add(propertySet);
+ expect(propertyExtractor.extractFrom(event))
+ .andReturn(new RefEventProperties(Collections.emptyMap(), propertySets))
+ .anyTimes();
- expect(propertyExtractor.extractFrom(event)).andReturn(propertySets).anyTimes();
-
- Collection<ActionRequest> actionRequests = Lists.newArrayListWithCapacity(1);
ActionRequest actionRequest1 = createMock(ActionRequest.class);
- actionRequests.add(actionRequest1);
- expect(ruleBase.actionRequestsFor(propertySet)).andReturn(actionRequests).once();
+ Collection<ActionRequest> actionRequests1 = ImmutableList.of(actionRequest1);
- actionExecutor.execute("testIssue", actionRequests, propertySet);
-
- replayMocks();
-
- actionController.onEvent(event);
- }
-
- public void testMultiplePropertySetsMultipleActionMultipleIssue() {
- ActionController actionController = createActionController();
-
- ChangeEvent event = createMock(ChangeEvent.class);
-
- Property propertyIssue1 = createMock(Property.class);
- expect(propertyIssue1.getKey()).andReturn("issue").anyTimes();
- expect(propertyIssue1.getValue()).andReturn("testIssue").anyTimes();
-
- Property propertyIssue2 = createMock(Property.class);
- expect(propertyIssue2.getKey()).andReturn("issue").anyTimes();
- expect(propertyIssue2.getValue()).andReturn("testIssue2").anyTimes();
-
- Set<Property> propertySet1 = Sets.newHashSet();
- propertySet1.add(propertyIssue1);
-
- Set<Property> propertySet2 = Sets.newHashSet();
- propertySet2.add(propertyIssue1);
- propertySet2.add(propertyIssue2);
-
- Set<Set<Property>> propertySets = Sets.newHashSet();
- propertySets.add(propertySet1);
- propertySets.add(propertySet2);
-
- expect(propertyExtractor.extractFrom(event)).andReturn(propertySets).anyTimes();
-
- Collection<ActionRequest> actionRequests1 = Lists.newArrayListWithCapacity(1);
- ActionRequest actionRequest1 = createMock(ActionRequest.class);
- actionRequests1.add(actionRequest1);
-
- Collection<ActionRequest> actionRequests2 = Lists.newArrayListWithCapacity(2);
ActionRequest actionRequest2 = createMock(ActionRequest.class);
- actionRequests2.add(actionRequest2);
ActionRequest actionRequest3 = createMock(ActionRequest.class);
- actionRequests2.add(actionRequest3);
+ Collection<ActionRequest> actionRequests2 = ImmutableList.of(actionRequest2, actionRequest3);
- expect(ruleBase.actionRequestsFor(propertySet1)).andReturn(actionRequests1).once();
- expect(ruleBase.actionRequestsFor(propertySet2)).andReturn(actionRequests2).once();
+ expect(ruleBase.actionRequestsFor(properties1)).andReturn(actionRequests1).once();
+ expect(ruleBase.actionRequestsFor(properties2)).andReturn(actionRequests2).once();
- actionExecutor.execute("testIssue", actionRequests1, propertySet1);
- actionExecutor.execute("testIssue", actionRequests2, propertySet2);
- actionExecutor.execute("testIssue2", actionRequests2, propertySet2);
+ actionExecutor.executeOnIssue(actionRequests1, properties1);
+ actionExecutor.executeOnIssue(actionRequests2, properties2);
replayMocks();
@@ -173,7 +151,7 @@
}
private void setupCommonMocks() {
- expect(itsConfig.isEnabled(anyObject(Event.class))).andReturn(true).anyTimes();
+ expect(itsConfig.isEnabled(anyObject(RefEvent.class))).andReturn(true).anyTimes();
}
@Override
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionExecutorTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionExecutorTest.java
index f2b6a55..e82ffd1 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionExecutorTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionExecutorTest.java
@@ -16,52 +16,71 @@
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.expectLastCall;
-import com.google.common.collect.Sets;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.gerrit.extensions.annotations.Exports;
import com.google.gerrit.extensions.config.FactoryModule;
+import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.reviewdb.client.Project;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.googlesource.gerrit.plugins.its.base.its.ItsFacade;
+import com.googlesource.gerrit.plugins.its.base.its.ItsFacadeFactory;
import com.googlesource.gerrit.plugins.its.base.testutil.LoggingMockingTestCase;
-import com.googlesource.gerrit.plugins.its.base.workflow.action.AddComment;
-import com.googlesource.gerrit.plugins.its.base.workflow.action.AddSoyComment;
-import com.googlesource.gerrit.plugins.its.base.workflow.action.AddStandardComment;
-import com.googlesource.gerrit.plugins.its.base.workflow.action.AddVelocityComment;
-import com.googlesource.gerrit.plugins.its.base.workflow.action.LogEvent;
import java.io.IOException;
import java.util.Collections;
+import java.util.Map;
import java.util.Set;
public class ActionExecutorTest extends LoggingMockingTestCase {
+
+ private static final String CUSTOM_ACTION_NAME = "custom-action-name";
+
private Injector injector;
private ItsFacade its;
+ private ItsFacadeFactory itsFacadeFactory;
private AddComment.Factory addCommentFactory;
private AddStandardComment.Factory addStandardCommentFactory;
- private AddVelocityComment.Factory addVelocityCommentFactory;
private AddSoyComment.Factory addSoyCommentFactory;
private LogEvent.Factory logEventFactory;
+ private AddPropertyToField.Factory addPropertyToFieldFactory;
+ private CreateVersionFromProperty.Factory createVersionFromPropertyFactory;
+ private CustomAction customAction;
+
+ private Map<String, String> properties =
+ ImmutableMap.of("issue", "4711", "project", "testProject");
+ private Map<String, String> projectProperties =
+ ImmutableMap.<String, String>builder()
+ .putAll(properties)
+ .put("its-project", "itsTestProject")
+ .build();
public void testExecuteItem() throws IOException {
ActionRequest actionRequest = createMock(ActionRequest.class);
expect(actionRequest.getName()).andReturn("unparsed");
expect(actionRequest.getUnparsed()).andReturn("unparsed action 1");
+ expect(itsFacadeFactory.getFacade(new Project.NameKey(properties.get("project"))))
+ .andReturn(its);
- Set<Property> properties = Collections.emptySet();
+ Set<ActionRequest> actionRequests = ImmutableSet.of(actionRequest);
its.performAction("4711", "unparsed action 1");
replayMocks();
ActionExecutor actionExecutor = createActionExecutor();
- actionExecutor.execute("4711", actionRequest, properties);
+ actionExecutor.executeOnIssue(actionRequests, properties);
}
public void testExecuteItemException() throws IOException {
ActionRequest actionRequest = createMock(ActionRequest.class);
expect(actionRequest.getName()).andReturn("unparsed");
expect(actionRequest.getUnparsed()).andReturn("unparsed action 1");
+ expect(itsFacadeFactory.getFacade(new Project.NameKey(properties.get("project"))))
+ .andReturn(its);
- Set<Property> properties = Collections.emptySet();
+ Set<ActionRequest> actionRequests = ImmutableSet.of(actionRequest);
its.performAction("4711", "unparsed action 1");
expectLastCall().andThrow(new IOException("injected exception 1"));
@@ -69,7 +88,7 @@
replayMocks();
ActionExecutor actionExecutor = createActionExecutor();
- actionExecutor.execute("4711", actionRequest, properties);
+ actionExecutor.executeOnIssue(actionRequests, properties);
assertLogThrowableMessageContains("injected exception 1");
}
@@ -82,8 +101,11 @@
ActionRequest actionRequest2 = createMock(ActionRequest.class);
expect(actionRequest2.getName()).andReturn("unparsed");
expect(actionRequest2.getUnparsed()).andReturn("unparsed action 2");
+ expect(itsFacadeFactory.getFacade(new Project.NameKey(properties.get("project"))))
+ .andReturn(its)
+ .anyTimes();
- Set<Property> properties = Collections.emptySet();
+ Set<ActionRequest> actionRequests = ImmutableSet.of(actionRequest1, actionRequest2);
its.performAction("4711", "unparsed action 1");
its.performAction("4711", "unparsed action 2");
@@ -91,7 +113,7 @@
replayMocks();
ActionExecutor actionExecutor = createActionExecutor();
- actionExecutor.execute("4711", Sets.newHashSet(actionRequest1, actionRequest2), properties);
+ actionExecutor.executeOnIssue(actionRequests, properties);
}
public void testExecuteIterableExceptions() throws IOException {
@@ -106,8 +128,12 @@
ActionRequest actionRequest3 = createMock(ActionRequest.class);
expect(actionRequest3.getName()).andReturn("unparsed");
expect(actionRequest3.getUnparsed()).andReturn("unparsed action 3");
+ expect(itsFacadeFactory.getFacade(new Project.NameKey(properties.get("project"))))
+ .andReturn(its)
+ .anyTimes();
- Set<Property> properties = Collections.emptySet();
+ Set<ActionRequest> actionRequests =
+ ImmutableSet.of(actionRequest1, actionRequest2, actionRequest3);
its.performAction("4711", "unparsed action 1");
expectLastCall().andThrow(new IOException("injected exception 1"));
@@ -118,8 +144,7 @@
replayMocks();
ActionExecutor actionExecutor = createActionExecutor();
- actionExecutor.execute(
- "4711", Sets.newHashSet(actionRequest1, actionRequest2, actionRequest3), properties);
+ actionExecutor.executeOnIssue(actionRequests, properties);
assertLogThrowableMessageContains("injected exception 1");
assertLogThrowableMessageContains("injected exception 3");
@@ -129,85 +154,149 @@
ActionRequest actionRequest = createMock(ActionRequest.class);
expect(actionRequest.getName()).andReturn("add-comment");
- Set<Property> properties = Collections.emptySet();
+ Set<ActionRequest> actionRequests = ImmutableSet.of(actionRequest);
AddComment addComment = createMock(AddComment.class);
expect(addCommentFactory.create()).andReturn(addComment);
+ expect(itsFacadeFactory.getFacade(new Project.NameKey(properties.get("project"))))
+ .andReturn(its);
- addComment.execute("4711", actionRequest, properties);
+ addComment.execute(its, "4711", actionRequest, properties);
replayMocks();
ActionExecutor actionExecutor = createActionExecutor();
- actionExecutor.execute("4711", actionRequest, properties);
+ actionExecutor.executeOnIssue(actionRequests, properties);
}
public void testAddSoyCommentDelegation() throws IOException {
ActionRequest actionRequest = createMock(ActionRequest.class);
expect(actionRequest.getName()).andReturn("add-soy-comment");
- Set<Property> properties = Collections.emptySet();
+ Set<ActionRequest> actionRequests = ImmutableSet.of(actionRequest);
AddSoyComment addSoyComment = createMock(AddSoyComment.class);
expect(addSoyCommentFactory.create()).andReturn(addSoyComment);
+ expect(itsFacadeFactory.getFacade(new Project.NameKey(properties.get("project"))))
+ .andReturn(its);
- addSoyComment.execute("4711", actionRequest, properties);
+ addSoyComment.execute(its, "4711", actionRequest, properties);
replayMocks();
ActionExecutor actionExecutor = createActionExecutor();
- actionExecutor.execute("4711", actionRequest, properties);
+ actionExecutor.executeOnIssue(actionRequests, properties);
}
public void testAddStandardCommentDelegation() throws IOException {
ActionRequest actionRequest = createMock(ActionRequest.class);
expect(actionRequest.getName()).andReturn("add-standard-comment");
- Set<Property> properties = Collections.emptySet();
+ Set<ActionRequest> actionRequests = ImmutableSet.of(actionRequest);
AddStandardComment addStandardComment = createMock(AddStandardComment.class);
expect(addStandardCommentFactory.create()).andReturn(addStandardComment);
+ expect(itsFacadeFactory.getFacade(new Project.NameKey(properties.get("project"))))
+ .andReturn(its);
- addStandardComment.execute("4711", actionRequest, properties);
+ addStandardComment.execute(its, "4711", actionRequest, properties);
replayMocks();
ActionExecutor actionExecutor = createActionExecutor();
- actionExecutor.execute("4711", actionRequest, properties);
- }
-
- public void testAddVelocityCommentDelegation() throws IOException {
- ActionRequest actionRequest = createMock(ActionRequest.class);
- expect(actionRequest.getName()).andReturn("add-velocity-comment");
-
- Set<Property> properties = Collections.emptySet();
-
- AddVelocityComment addVelocityComment = createMock(AddVelocityComment.class);
- expect(addVelocityCommentFactory.create()).andReturn(addVelocityComment);
-
- addVelocityComment.execute("4711", actionRequest, properties);
-
- replayMocks();
-
- ActionExecutor actionExecutor = createActionExecutor();
- actionExecutor.execute("4711", actionRequest, properties);
+ actionExecutor.executeOnIssue(actionRequests, properties);
}
public void testLogEventDelegation() throws IOException {
ActionRequest actionRequest = createMock(ActionRequest.class);
expect(actionRequest.getName()).andReturn("log-event");
- Set<Property> properties = Collections.emptySet();
+ Set<ActionRequest> actionRequests = ImmutableSet.of(actionRequest);
LogEvent logEvent = createMock(LogEvent.class);
expect(logEventFactory.create()).andReturn(logEvent);
+ expect(itsFacadeFactory.getFacade(new Project.NameKey(properties.get("project"))))
+ .andReturn(its);
- logEvent.execute("4711", actionRequest, properties);
+ logEvent.execute(its, "4711", actionRequest, properties);
replayMocks();
ActionExecutor actionExecutor = createActionExecutor();
- actionExecutor.execute("4711", actionRequest, properties);
+ actionExecutor.executeOnIssue(actionRequests, properties);
+ }
+
+ public void testCreateVersionFromPropertyDelegation() throws IOException {
+ ActionRequest actionRequest = createMock(ActionRequest.class);
+ expect(actionRequest.getName()).andReturn("create-version-from-property");
+
+ CreateVersionFromProperty createVersionFromProperty =
+ createMock(CreateVersionFromProperty.class);
+ expect(createVersionFromPropertyFactory.create()).andReturn(createVersionFromProperty);
+ expect(itsFacadeFactory.getFacade(new Project.NameKey(properties.get("project"))))
+ .andReturn(its);
+
+ createVersionFromProperty.execute(its, "itsTestProject", actionRequest, projectProperties);
+
+ replayMocks();
+
+ ActionExecutor actionExecutor = createActionExecutor();
+ actionExecutor.executeOnProject(Collections.singleton(actionRequest), projectProperties);
+ }
+
+ public void testAddPropertyToFieldDelegation() throws IOException {
+ ActionRequest actionRequest = createMock(ActionRequest.class);
+ expect(actionRequest.getName()).andReturn("add-property-to-field");
+
+ Set<ActionRequest> actionRequests = ImmutableSet.of(actionRequest);
+
+ AddPropertyToField addPropertyToField = createMock(AddPropertyToField.class);
+ expect(addPropertyToFieldFactory.create()).andReturn(addPropertyToField);
+ expect(itsFacadeFactory.getFacade(new Project.NameKey(properties.get("project"))))
+ .andReturn(its);
+
+ addPropertyToField.execute(its, "4711", actionRequest, properties);
+
+ replayMocks();
+
+ ActionExecutor actionExecutor = createActionExecutor();
+ actionExecutor.executeOnIssue(actionRequests, properties);
+ }
+
+ public void testExecuteIssueCustomAction() throws IOException {
+ expect(customAction.getType()).andReturn(ActionType.ISSUE);
+
+ ActionRequest actionRequest = createMock(ActionRequest.class);
+ expect(actionRequest.getName()).andReturn(CUSTOM_ACTION_NAME);
+ expect(itsFacadeFactory.getFacade(new Project.NameKey(properties.get("project"))))
+ .andReturn(its);
+
+ Set<ActionRequest> actionRequests = ImmutableSet.of(actionRequest);
+
+ customAction.execute(its, "4711", actionRequest, properties);
+
+ replayMocks();
+
+ ActionExecutor actionExecutor = createActionExecutor();
+ actionExecutor.executeOnIssue(actionRequests, properties);
+ }
+
+ public void testExecuteProjectCustomAction() throws IOException {
+ expect(customAction.getType()).andReturn(ActionType.PROJECT);
+
+ ActionRequest actionRequest = createMock(ActionRequest.class);
+ expect(actionRequest.getName()).andReturn(CUSTOM_ACTION_NAME);
+ expect(itsFacadeFactory.getFacade(new Project.NameKey(properties.get("project"))))
+ .andReturn(its);
+
+ Set<ActionRequest> actionRequests = ImmutableSet.of(actionRequest);
+
+ customAction.execute(its, "itsTestProject", actionRequest, projectProperties);
+
+ replayMocks();
+
+ ActionExecutor actionExecutor = createActionExecutor();
+ actionExecutor.executeOnProject(actionRequests, projectProperties);
}
private ActionExecutor createActionExecutor() {
@@ -235,11 +324,24 @@
addStandardCommentFactory = createMock(AddStandardComment.Factory.class);
bind(AddStandardComment.Factory.class).toInstance(addStandardCommentFactory);
- addVelocityCommentFactory = createMock(AddVelocityComment.Factory.class);
- bind(AddVelocityComment.Factory.class).toInstance(addVelocityCommentFactory);
-
logEventFactory = createMock(LogEvent.Factory.class);
bind(LogEvent.Factory.class).toInstance(logEventFactory);
+
+ itsFacadeFactory = createMock(ItsFacadeFactory.class);
+ bind(ItsFacadeFactory.class).toInstance(itsFacadeFactory);
+
+ addPropertyToFieldFactory = createMock(AddPropertyToField.Factory.class);
+ bind(AddPropertyToField.Factory.class).toInstance(addPropertyToFieldFactory);
+
+ createVersionFromPropertyFactory = createMock(CreateVersionFromProperty.Factory.class);
+ bind(CreateVersionFromProperty.Factory.class).toInstance(createVersionFromPropertyFactory);
+
+ DynamicMap.mapOf(binder(), CustomAction.class);
+ customAction = createMock(CustomAction.class);
+
+ bind(CustomAction.class)
+ .annotatedWith(Exports.named(CUSTOM_ACTION_NAME))
+ .toInstance(customAction);
}
}
}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/action/AddCommentTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/AddCommentTest.java
similarity index 87%
rename from src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/action/AddCommentTest.java
rename to src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/AddCommentTest.java
index d16bab2..149dfca 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/action/AddCommentTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/AddCommentTest.java
@@ -11,18 +11,17 @@
// 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.its.base.workflow.action;
+package com.googlesource.gerrit.plugins.its.base.workflow;
import static org.easymock.EasyMock.expect;
+import com.google.common.collect.ImmutableMap;
import com.google.gerrit.extensions.config.FactoryModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.googlesource.gerrit.plugins.its.base.its.ItsFacade;
import com.googlesource.gerrit.plugins.its.base.testutil.LoggingMockingTestCase;
-import com.googlesource.gerrit.plugins.its.base.workflow.ActionRequest;
import java.io.IOException;
-import java.util.HashSet;
public class AddCommentTest extends LoggingMockingTestCase {
private Injector injector;
@@ -36,7 +35,7 @@
replayMocks();
AddComment addComment = createAddComment();
- addComment.execute("4711", actionRequest, new HashSet<>());
+ addComment.execute(null, "4711", actionRequest, ImmutableMap.of());
}
public void testPlain() throws IOException {
@@ -48,7 +47,7 @@
replayMocks();
AddComment addComment = createAddComment();
- addComment.execute("4711", actionRequest, new HashSet<>());
+ addComment.execute(its, "4711", actionRequest, ImmutableMap.of());
}
private AddComment createAddComment() {
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/AddPropertyToFieldParametersExtractorTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/AddPropertyToFieldParametersExtractorTest.java
new file mode 100644
index 0000000..fb047f7
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/AddPropertyToFieldParametersExtractorTest.java
@@ -0,0 +1,105 @@
+// Copyright (C) 2018 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.its.base.workflow;
+
+import static org.easymock.EasyMock.expect;
+
+import com.google.gerrit.extensions.config.FactoryModule;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.googlesource.gerrit.plugins.its.base.testutil.MockingTestCase;
+import java.util.Collections;
+import java.util.Optional;
+
+public class AddPropertyToFieldParametersExtractorTest extends MockingTestCase {
+
+ private static final String FIELD_ID = "fieldId";
+ private static final String PROPERTY_ID = "propertyId";
+ private static final String PROPERTY_VALUE = "propertyValue";
+
+ private AddPropertyToFieldParametersExtractor extractor;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ Injector injector = Guice.createInjector(new TestModule());
+ extractor = injector.getInstance(AddPropertyToFieldParametersExtractor.class);
+ }
+
+ private class TestModule extends FactoryModule {}
+
+ public void testNoParameter() {
+ testWrongNumberOfReceivedParameters(new String[] {});
+ }
+
+ public void testOneParameter() {
+ testWrongNumberOfReceivedParameters(new String[] {PROPERTY_ID});
+ }
+
+ public void testThreeParameters() {
+ testWrongNumberOfReceivedParameters(new String[] {PROPERTY_ID, PROPERTY_ID, PROPERTY_ID});
+ }
+
+ private void testWrongNumberOfReceivedParameters(String[] parameters) {
+ ActionRequest actionRequest = createMock(ActionRequest.class);
+ expect(actionRequest.getParameters()).andReturn(parameters);
+
+ replayMocks();
+
+ assertFalse(extractor.extract(actionRequest, Collections.emptyMap()).isPresent());
+ }
+
+ public void testBlankFieldId() {
+ ActionRequest actionRequest = createMock(ActionRequest.class);
+ expect(actionRequest.getParameters()).andReturn(new String[] {PROPERTY_ID, ""});
+
+ replayMocks();
+
+ assertFalse(extractor.extract(actionRequest, Collections.emptyMap()).isPresent());
+ }
+
+ public void testBlankPropertyId() {
+ ActionRequest actionRequest = createMock(ActionRequest.class);
+ expect(actionRequest.getParameters()).andReturn(new String[] {"", FIELD_ID});
+
+ replayMocks();
+
+ assertFalse(extractor.extract(actionRequest, Collections.emptyMap()).isPresent());
+ }
+
+ public void testUnknownPropertyId() {
+ ActionRequest actionRequest = createMock(ActionRequest.class);
+ expect(actionRequest.getParameters()).andReturn(new String[] {FIELD_ID, PROPERTY_ID});
+
+ replayMocks();
+
+ assertFalse(extractor.extract(actionRequest, Collections.emptyMap()).isPresent());
+ }
+
+ public void testHappyPath() {
+ ActionRequest actionRequest = createMock(ActionRequest.class);
+ expect(actionRequest.getParameters()).andReturn(new String[] {PROPERTY_ID, FIELD_ID});
+
+ replayMocks();
+
+ Optional<AddPropertyToFieldParameters> extractedParameters =
+ extractor.extract(actionRequest, Collections.singletonMap(PROPERTY_ID, PROPERTY_VALUE));
+ if (!extractedParameters.isPresent()) {
+ fail();
+ }
+ assertEquals(PROPERTY_VALUE, extractedParameters.get().getPropertyValue());
+ assertEquals(FIELD_ID, extractedParameters.get().getFieldId());
+ }
+}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/AddPropertyToFieldTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/AddPropertyToFieldTest.java
new file mode 100644
index 0000000..a601b04
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/AddPropertyToFieldTest.java
@@ -0,0 +1,77 @@
+// Copyright (C) 2018 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.its.base.workflow;
+
+import static org.easymock.EasyMock.expect;
+
+import com.google.gerrit.extensions.config.FactoryModule;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.googlesource.gerrit.plugins.its.base.its.ItsFacade;
+import com.googlesource.gerrit.plugins.its.base.testutil.MockingTestCase;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Optional;
+import org.easymock.EasyMock;
+
+public class AddPropertyToFieldTest extends MockingTestCase {
+
+ private static final String ISSUE_ID = "4711";
+ private static final String FIELD_ID = "fieldId";
+ private static final String PROPERTY_ID = "propertyId";
+ private static final String PROPERTY_VALUE = "propertyValue";
+
+ private Injector injector;
+ private AddPropertyToFieldParametersExtractor parametersExtractor;
+ private ItsFacade its;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ injector = Guice.createInjector(new TestModule());
+ }
+
+ private class TestModule extends FactoryModule {
+ @Override
+ protected void configure() {
+ parametersExtractor = createMock(AddPropertyToFieldParametersExtractor.class);
+ bind(AddPropertyToFieldParametersExtractor.class).toInstance(parametersExtractor);
+
+ its = createMock(ItsFacade.class);
+ bind(ItsFacade.class).toInstance(its);
+ }
+ }
+
+ public void testHappyPath() throws IOException {
+ ActionRequest actionRequest = createMock(ActionRequest.class);
+
+ Map<String, String> properties = Collections.singletonMap(PROPERTY_ID, PROPERTY_VALUE);
+ expect(parametersExtractor.extract(actionRequest, properties))
+ .andReturn(Optional.of(new AddPropertyToFieldParameters(PROPERTY_VALUE, FIELD_ID)));
+
+ its.addValueToField(ISSUE_ID, PROPERTY_VALUE, FIELD_ID);
+ EasyMock.expectLastCall().once();
+
+ replayMocks();
+
+ AddPropertyToField addPropertyToField = createAddPropertyToField();
+ addPropertyToField.execute(its, ISSUE_ID, actionRequest, properties);
+ }
+
+ private AddPropertyToField createAddPropertyToField() {
+ return injector.getInstance(AddPropertyToField.class);
+ }
+}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/AddStandardCommentTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/AddStandardCommentTest.java
new file mode 100644
index 0000000..d695bd1
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/AddStandardCommentTest.java
@@ -0,0 +1,197 @@
+// Copyright (C) 2017 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.its.base.workflow;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.gerrit.extensions.config.FactoryModule;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.googlesource.gerrit.plugins.its.base.its.ItsFacade;
+import com.googlesource.gerrit.plugins.its.base.testutil.LoggingMockingTestCase;
+import java.io.IOException;
+import java.util.Map;
+
+public class AddStandardCommentTest extends LoggingMockingTestCase {
+ private Injector injector;
+
+ private ItsFacade its;
+
+ public void testChangeMergedPlain() throws IOException {
+ ActionRequest actionRequest = createMock(ActionRequest.class);
+
+ Map<String, String> properties = ImmutableMap.of("event-type", "change-merged");
+
+ its.addComment("42", "Change merged");
+ replayMocks();
+
+ StandardAction action = injector.getInstance(AddStandardComment.class);
+ action.execute(its, "42", actionRequest, properties);
+ }
+
+ public void testChangeMergedFull() throws IOException {
+ ActionRequest actionRequest = createMock(ActionRequest.class);
+
+ Map<String, String> properties =
+ ImmutableMap.<String, String>builder()
+ .put("event-type", "change-merged")
+ .put("subject", "Test-Change-Subject")
+ .put("changeNumber", "4711")
+ .put("submitterName", "John Doe")
+ .put("formatChangeUrl", "HtTp://ExAmPlE.OrG/ChAnGe")
+ .build();
+
+ its.addComment(
+ "176",
+ "Change 4711 merged by John Doe:\n"
+ + "Test-Change-Subject\n"
+ + "\n"
+ + "HtTp://ExAmPlE.OrG/ChAnGe");
+ replayMocks();
+
+ StandardAction action = injector.getInstance(AddStandardComment.class);
+ action.execute(its, "176", actionRequest, properties);
+ }
+
+ public void testChangeAbandonedPlain() throws IOException {
+ ActionRequest actionRequest = createMock(ActionRequest.class);
+
+ Map<String, String> properties = ImmutableMap.of("event-type", "change-abandoned");
+
+ its.addComment("42", "Change abandoned");
+ replayMocks();
+
+ StandardAction action = injector.getInstance(AddStandardComment.class);
+ action.execute(its, "42", actionRequest, properties);
+ }
+
+ public void testChangeAbandonedFull() throws IOException {
+ ActionRequest actionRequest = createMock(ActionRequest.class);
+
+ Map<String, String> properties =
+ ImmutableMap.<String, String>builder()
+ .put("event-type", "change-abandoned")
+ .put("reason", "Test-Reason")
+ .put("subject", "Test-Change-Subject")
+ .put("changeNumber", "4711")
+ .put("abandonerName", "John Doe")
+ .put("formatChangeUrl", "HtTp://ExAmPlE.OrG/ChAnGe")
+ .build();
+
+ its.addComment(
+ "176",
+ "Change 4711 abandoned by John Doe:\n"
+ + "Test-Change-Subject\n"
+ + "\n"
+ + "Reason:\n"
+ + "Test-Reason\n"
+ + "\n"
+ + "HtTp://ExAmPlE.OrG/ChAnGe");
+ replayMocks();
+
+ StandardAction action = injector.getInstance(AddStandardComment.class);
+ action.execute(its, "176", actionRequest, properties);
+ }
+
+ public void testChangeRestoredPlain() throws IOException {
+ ActionRequest actionRequest = createMock(ActionRequest.class);
+
+ Map<String, String> properties = ImmutableMap.of("event-type", "change-restored");
+
+ its.addComment("42", "Change restored");
+ replayMocks();
+
+ StandardAction action = injector.getInstance(AddStandardComment.class);
+ action.execute(its, "42", actionRequest, properties);
+ }
+
+ public void testChangeRestoredFull() throws IOException {
+ ActionRequest actionRequest = createMock(ActionRequest.class);
+
+ Map<String, String> properties =
+ ImmutableMap.<String, String>builder()
+ .put("event-type", "change-restored")
+ .put("reason", "Test-Reason")
+ .put("subject", "Test-Change-Subject")
+ .put("changeNumber", "4711")
+ .put("restorerName", "John Doe")
+ .put("formatChangeUrl", "HtTp://ExAmPlE.OrG/ChAnGe")
+ .build();
+
+ its.addComment(
+ "176",
+ "Change 4711 restored by John Doe:\n"
+ + "Test-Change-Subject\n"
+ + "\n"
+ + "Reason:\n"
+ + "Test-Reason\n"
+ + "\n"
+ + "HtTp://ExAmPlE.OrG/ChAnGe");
+ replayMocks();
+
+ StandardAction action = injector.getInstance(AddStandardComment.class);
+ action.execute(its, "176", actionRequest, properties);
+ }
+
+ public void testPatchSetCreatedPlain() throws IOException {
+ ActionRequest actionRequest = createMock(ActionRequest.class);
+
+ Map<String, String> properties = ImmutableMap.of("event-type", "patchset-created");
+
+ its.addComment("42", "Change had a related patch set uploaded");
+ replayMocks();
+
+ StandardAction action = injector.getInstance(AddStandardComment.class);
+ action.execute(its, "42", actionRequest, properties);
+ }
+
+ public void testPatchSetCreatedFull() throws IOException {
+ ActionRequest actionRequest = createMock(ActionRequest.class);
+
+ Map<String, String> properties =
+ ImmutableMap.<String, String>builder()
+ .put("event-type", "patchset-created")
+ .put("subject", "Test-Change-Subject")
+ .put("changeNumber", "4711")
+ .put("uploaderName", "John Doe")
+ .put("formatChangeUrl", "HtTp://ExAmPlE.OrG/ChAnGe")
+ .build();
+
+ its.addComment(
+ "176",
+ "Change 4711 had a related patch set uploaded by "
+ + "John Doe:\n"
+ + "Test-Change-Subject\n"
+ + "\n"
+ + "HtTp://ExAmPlE.OrG/ChAnGe");
+ replayMocks();
+
+ StandardAction action = injector.getInstance(AddStandardComment.class);
+ action.execute(its, "176", actionRequest, properties);
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ injector = Guice.createInjector(new TestModule());
+ }
+
+ private class TestModule extends FactoryModule {
+ @Override
+ protected void configure() {
+ its = createMock(ItsFacade.class);
+ bind(ItsFacade.class).toInstance(its);
+ }
+ }
+}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/ConditionTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/ConditionTest.java
index c1a6a8d..3df38cc 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/ConditionTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/ConditionTest.java
@@ -13,15 +13,12 @@
// limitations under the License.
package com.googlesource.gerrit.plugins.its.base.workflow;
-import static org.easymock.EasyMock.expect;
-
-import com.google.common.collect.Lists;
+import com.google.common.collect.ImmutableMap;
import com.google.gerrit.extensions.config.FactoryModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.googlesource.gerrit.plugins.its.base.testutil.LoggingMockingTestCase;
-import java.util.Collection;
-import java.util.Collections;
+import java.util.Map;
public class ConditionTest extends LoggingMockingTestCase {
private Injector injector;
@@ -34,12 +31,7 @@
public void testIsMetBySimple() {
Condition condition = createCondition("testKey", "testValue");
- Property property1 = createMock(Property.class);
- expect(property1.getKey()).andReturn("testKey").anyTimes();
- expect(property1.getValue()).andReturn("testValue").anyTimes();
-
- Collection<Property> properties = Lists.newArrayListWithCapacity(1);
- properties.add(property1);
+ Map<String, String> properties = ImmutableMap.of("testKey", "testValue");
replayMocks();
@@ -49,7 +41,7 @@
public void testIsMetBySimpleEmpty() {
Condition condition = createCondition("testKey", "testValue");
- Collection<Property> properties = Collections.emptySet();
+ Map<String, String> properties = ImmutableMap.of();
replayMocks();
@@ -59,12 +51,7 @@
public void testIsMetByMismatchedKey() {
Condition condition = createCondition("testKey", "testValue");
- Property property1 = createMock(Property.class);
- expect(property1.getKey()).andReturn("otherKey").anyTimes();
- expect(property1.getValue()).andReturn("testValue").anyTimes();
-
- Collection<Property> properties = Lists.newArrayListWithCapacity(1);
- properties.add(property1);
+ Map<String, String> properties = ImmutableMap.of("otherKey", "testValue");
replayMocks();
@@ -74,12 +61,7 @@
public void testIsMetByMismatchedValue() {
Condition condition = createCondition("testKey", "testValue");
- Property property1 = createMock(Property.class);
- expect(property1.getKey()).andReturn("testKey").anyTimes();
- expect(property1.getValue()).andReturn("otherValue").anyTimes();
-
- Collection<Property> properties = Lists.newArrayListWithCapacity(1);
- properties.add(property1);
+ Map<String, String> properties = ImmutableMap.of("testKey", "otherValue");
replayMocks();
@@ -89,12 +71,7 @@
public void testIsMetByOredSingle() {
Condition condition = createCondition("testKey", "value1,value2,value3");
- Property property1 = createMock(Property.class);
- expect(property1.getKey()).andReturn("testKey").anyTimes();
- expect(property1.getValue()).andReturn("value2").anyTimes();
-
- Collection<Property> properties = Lists.newArrayListWithCapacity(1);
- properties.add(property1);
+ Map<String, String> properties = ImmutableMap.of("testKey", "value2");
replayMocks();
@@ -104,17 +81,7 @@
public void testIsMetByOredMultiple() {
Condition condition = createCondition("testKey", "value1,value2,value3");
- Property property1 = createMock(Property.class);
- expect(property1.getKey()).andReturn("testKey").anyTimes();
- expect(property1.getValue()).andReturn("value1").anyTimes();
-
- Property property2 = createMock(Property.class);
- expect(property2.getKey()).andReturn("testKey").anyTimes();
- expect(property2.getValue()).andReturn("value3").anyTimes();
-
- Collection<Property> properties = Lists.newArrayListWithCapacity(2);
- properties.add(property1);
- properties.add(property2);
+ Map<String, String> properties = ImmutableMap.of("testKey", "value1 value3");
replayMocks();
@@ -122,19 +89,9 @@
}
public void testIsMetByOredMultipleWithSpaces() {
- Condition condition = createCondition("testKey", "value1, value2, value3");
+ Condition condition = createCondition("testKey", "value1, value2 value3");
- Property property1 = createMock(Property.class);
- expect(property1.getKey()).andReturn("testKey").anyTimes();
- expect(property1.getValue()).andReturn("value1").anyTimes();
-
- Property property2 = createMock(Property.class);
- expect(property2.getKey()).andReturn("testKey").anyTimes();
- expect(property2.getValue()).andReturn("value3").anyTimes();
-
- Collection<Property> properties = Lists.newArrayListWithCapacity(2);
- properties.add(property1);
- properties.add(property2);
+ Map<String, String> properties = ImmutableMap.of("testKey", "value1 value3");
replayMocks();
@@ -144,22 +101,7 @@
public void testIsMetByOredAll() {
Condition condition = createCondition("testKey", "value1,value2,value3");
- Property property1 = createMock(Property.class);
- expect(property1.getKey()).andReturn("testKey").anyTimes();
- expect(property1.getValue()).andReturn("value1").anyTimes();
-
- Property property2 = createMock(Property.class);
- expect(property2.getKey()).andReturn("testKey").anyTimes();
- expect(property2.getValue()).andReturn("value2").anyTimes();
-
- Property property3 = createMock(Property.class);
- expect(property3.getKey()).andReturn("testKey").anyTimes();
- expect(property3.getValue()).andReturn("value3").anyTimes();
-
- Collection<Property> properties = Lists.newArrayListWithCapacity(1);
- properties.add(property1);
- properties.add(property2);
- properties.add(property3);
+ Map<String, String> properties = ImmutableMap.of("testKey", "value1 value2 value3");
replayMocks();
@@ -169,22 +111,7 @@
public void testIsMetByOredOvershoot() {
Condition condition = createCondition("testKey", "value1,value2,value3");
- Property property1 = createMock(Property.class);
- expect(property1.getKey()).andReturn("testKey").anyTimes();
- expect(property1.getValue()).andReturn("otherValue1").anyTimes();
-
- Property property2 = createMock(Property.class);
- expect(property2.getKey()).andReturn("testKey").anyTimes();
- expect(property2.getValue()).andReturn("value2").anyTimes();
-
- Property property3 = createMock(Property.class);
- expect(property3.getKey()).andReturn("testKey").anyTimes();
- expect(property3.getValue()).andReturn("otherValue3").anyTimes();
-
- Collection<Property> properties = Lists.newArrayListWithCapacity(3);
- properties.add(property1);
- properties.add(property2);
- properties.add(property3);
+ Map<String, String> properties = ImmutableMap.of("testKey", "otherValue1 value2 otherValue3");
replayMocks();
@@ -194,7 +121,7 @@
public void testNegatedIsMetByEmpty() {
Condition condition = createCondition("testKey", "!,testValue");
- Collection<Property> properties = Collections.emptySet();
+ Map<String, String> properties = ImmutableMap.of();
replayMocks();
@@ -204,12 +131,7 @@
public void testNegatedIsMetByMismatchedKey() {
Condition condition = createCondition("testKey", "!,testValue");
- Property property1 = createMock(Property.class);
- expect(property1.getKey()).andReturn("otherKey").anyTimes();
- expect(property1.getValue()).andReturn("testValue").anyTimes();
-
- Collection<Property> properties = Lists.newArrayListWithCapacity(1);
- properties.add(property1);
+ Map<String, String> properties = ImmutableMap.of("otherKey", "testValue");
replayMocks();
@@ -219,12 +141,7 @@
public void testNegatedIsMetByMaMismatchedValue() {
Condition condition = createCondition("testKey", "!,testValue");
- Property property1 = createMock(Property.class);
- expect(property1.getKey()).andReturn("testKey").anyTimes();
- expect(property1.getValue()).andReturn("otherValue").anyTimes();
-
- Collection<Property> properties = Lists.newArrayListWithCapacity(1);
- properties.add(property1);
+ Map<String, String> properties = ImmutableMap.of("testKey", "otherValue");
replayMocks();
@@ -234,12 +151,7 @@
public void testNegatedIsMetByOredNoMatch() {
Condition condition = createCondition("testKey", "!,value1,value2,value3");
- Property property1 = createMock(Property.class);
- expect(property1.getKey()).andReturn("testKey").anyTimes();
- expect(property1.getValue()).andReturn("otherValue").anyTimes();
-
- Collection<Property> properties = Lists.newArrayListWithCapacity(1);
- properties.add(property1);
+ Map<String, String> properties = ImmutableMap.of("testKey", "otherValue");
replayMocks();
@@ -249,12 +161,7 @@
public void testNegatedIsMetByOredSingleMatch() {
Condition condition = createCondition("testKey", "!,value1,value2,value3");
- Property property1 = createMock(Property.class);
- expect(property1.getKey()).andReturn("testKey").anyTimes();
- expect(property1.getValue()).andReturn("value1").anyTimes();
-
- Collection<Property> properties = Lists.newArrayListWithCapacity(1);
- properties.add(property1);
+ Map<String, String> properties = ImmutableMap.of("testKey", "value1");
replayMocks();
@@ -264,17 +171,7 @@
public void testNegatedIsMetByOredMultiple() {
Condition condition = createCondition("testKey", "!,value1,value2,value3");
- Property property1 = createMock(Property.class);
- expect(property1.getKey()).andReturn("testKey").anyTimes();
- expect(property1.getValue()).andReturn("value1").anyTimes();
-
- Property property2 = createMock(Property.class);
- expect(property2.getKey()).andReturn("testKey").anyTimes();
- expect(property2.getValue()).andReturn("value3").anyTimes();
-
- Collection<Property> properties = Lists.newArrayListWithCapacity(2);
- properties.add(property1);
- properties.add(property2);
+ Map<String, String> properties = ImmutableMap.of("testKey", "value1 value3");
replayMocks();
@@ -284,22 +181,7 @@
public void testNegatedIsMetByOredAll() {
Condition condition = createCondition("testKey", "!,value1,value2,value3");
- Property property1 = createMock(Property.class);
- expect(property1.getKey()).andReturn("testKey").anyTimes();
- expect(property1.getValue()).andReturn("value1").anyTimes();
-
- Property property2 = createMock(Property.class);
- expect(property2.getKey()).andReturn("testKey").anyTimes();
- expect(property2.getValue()).andReturn("value2").anyTimes();
-
- Property property3 = createMock(Property.class);
- expect(property3.getKey()).andReturn("testKey").anyTimes();
- expect(property3.getValue()).andReturn("value3").anyTimes();
-
- Collection<Property> properties = Lists.newArrayListWithCapacity(1);
- properties.add(property1);
- properties.add(property2);
- properties.add(property3);
+ Map<String, String> properties = ImmutableMap.of("testKey", "value1 value2 value3");
replayMocks();
@@ -309,23 +191,7 @@
public void testNegatedIsMetByOredOvershoot() {
Condition condition = createCondition("testKey", "!,value1,value2,value3");
- Property property1 = createMock(Property.class);
- expect(property1.getKey()).andReturn("testKey").anyTimes();
- expect(property1.getValue()).andReturn("otherValue1").anyTimes();
-
- Property property2 = createMock(Property.class);
- expect(property2.getKey()).andReturn("testKey").anyTimes();
- expect(property2.getValue()).andReturn("value2").anyTimes();
-
- Property property3 = createMock(Property.class);
- expect(property3.getKey()).andReturn("testKey").anyTimes();
- expect(property3.getValue()).andReturn("otherValue3").anyTimes();
-
- Collection<Property> properties = Lists.newArrayListWithCapacity(3);
- properties.add(property1);
- properties.add(property2);
- properties.add(property3);
-
+ Map<String, String> properties = ImmutableMap.of("testKey", "otherValue1 value2 otherValue3");
replayMocks();
assertFalse("isMetBy gave true", condition.isMetBy(properties));
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/CreateVersionFromPropertyParametersExtractorTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/CreateVersionFromPropertyParametersExtractorTest.java
new file mode 100644
index 0000000..82b33e7
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/CreateVersionFromPropertyParametersExtractorTest.java
@@ -0,0 +1,83 @@
+package com.googlesource.gerrit.plugins.its.base.workflow;
+
+import static org.easymock.EasyMock.expect;
+
+import com.google.gerrit.extensions.config.FactoryModule;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.googlesource.gerrit.plugins.its.base.testutil.MockingTestCase;
+import java.util.Collections;
+import java.util.Optional;
+
+public class CreateVersionFromPropertyParametersExtractorTest extends MockingTestCase {
+
+ private static final String ITS_PROJECT = "test-project";
+ private static final String PROPERTY_ID = "propertyId";
+ private static final String PROPERTY_VALUE = "propertyValue";
+
+ private CreateVersionFromPropertyParametersExtractor extractor;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ Injector injector = Guice.createInjector(new TestModule());
+ extractor = injector.getInstance(CreateVersionFromPropertyParametersExtractor.class);
+ }
+
+ private class TestModule extends FactoryModule {}
+
+ public void testNoParameter() {
+ testWrongNumberOfReceivedParameters(new String[] {});
+ }
+
+ public void testTwoParameters() {
+ testWrongNumberOfReceivedParameters(new String[] {PROPERTY_ID, PROPERTY_ID});
+ }
+
+ private void testWrongNumberOfReceivedParameters(String[] parameters) {
+ ActionRequest actionRequest = createMock(ActionRequest.class);
+ expect(actionRequest.getParameters()).andReturn(parameters);
+
+ replayMocks();
+
+ Optional<CreateVersionFromPropertyParameters> extractedParameters =
+ extractor.extract(actionRequest, Collections.emptyMap());
+ assertFalse(extractedParameters.isPresent());
+ }
+
+ public void testBlankPropertyId() {
+ ActionRequest actionRequest = createMock(ActionRequest.class);
+ expect(actionRequest.getParameters()).andReturn(new String[] {""});
+
+ replayMocks();
+
+ Optional<CreateVersionFromPropertyParameters> extractedParameters =
+ extractor.extract(actionRequest, Collections.emptyMap());
+ assertFalse(extractedParameters.isPresent());
+ }
+
+ public void testUnknownPropertyId() {
+ ActionRequest actionRequest = createMock(ActionRequest.class);
+ expect(actionRequest.getParameters()).andReturn(new String[] {PROPERTY_ID});
+
+ replayMocks();
+
+ Optional<CreateVersionFromPropertyParameters> extractedParameters =
+ extractor.extract(actionRequest, Collections.emptyMap());
+ assertFalse(extractedParameters.isPresent());
+ }
+
+ public void testHappyPath() {
+ ActionRequest actionRequest = createMock(ActionRequest.class);
+ expect(actionRequest.getParameters()).andReturn(new String[] {PROPERTY_ID});
+
+ replayMocks();
+
+ Optional<CreateVersionFromPropertyParameters> extractedParameters =
+ extractor.extract(actionRequest, Collections.singletonMap(PROPERTY_ID, PROPERTY_VALUE));
+ if (!extractedParameters.isPresent()) {
+ fail();
+ }
+ assertEquals(PROPERTY_VALUE, extractedParameters.get().getPropertyValue());
+ }
+}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/CreateVersionFromPropertyTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/CreateVersionFromPropertyTest.java
new file mode 100644
index 0000000..a89509c
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/CreateVersionFromPropertyTest.java
@@ -0,0 +1,76 @@
+// Copyright (C) 2018 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.its.base.workflow;
+
+import static org.easymock.EasyMock.expect;
+
+import com.google.gerrit.extensions.config.FactoryModule;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.googlesource.gerrit.plugins.its.base.its.ItsFacade;
+import com.googlesource.gerrit.plugins.its.base.testutil.MockingTestCase;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Optional;
+import org.easymock.EasyMock;
+
+public class CreateVersionFromPropertyTest extends MockingTestCase {
+
+ private static final String ITS_PROJECT = "test-project";
+ private static final String PROPERTY_ID = "propertyId";
+ private static final String PROPERTY_VALUE = "propertyValue";
+
+ private Injector injector;
+ private ItsFacade its;
+ private CreateVersionFromPropertyParametersExtractor parametersExtractor;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ injector = Guice.createInjector(new TestModule());
+ }
+
+ private class TestModule extends FactoryModule {
+ @Override
+ protected void configure() {
+ its = createMock(ItsFacade.class);
+ bind(ItsFacade.class).toInstance(its);
+
+ parametersExtractor = createMock(CreateVersionFromPropertyParametersExtractor.class);
+ bind(CreateVersionFromPropertyParametersExtractor.class).toInstance(parametersExtractor);
+ }
+ }
+
+ public void testHappyPath() throws IOException {
+ ActionRequest actionRequest = createMock(ActionRequest.class);
+
+ Map<String, String> properties = Collections.emptyMap();
+ expect(parametersExtractor.extract(actionRequest, properties))
+ .andReturn(Optional.of(new CreateVersionFromPropertyParameters(PROPERTY_VALUE)));
+
+ its.createVersion(ITS_PROJECT, PROPERTY_VALUE);
+ EasyMock.expectLastCall().once();
+
+ replayMocks();
+
+ CreateVersionFromProperty createVersionFromProperty = createCreateVersionFromProperty();
+ createVersionFromProperty.execute(its, ITS_PROJECT, actionRequest, properties);
+ }
+
+ private CreateVersionFromProperty createCreateVersionFromProperty() {
+ return injector.getInstance(CreateVersionFromProperty.class);
+ }
+}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/ItsRulesProjectCacheTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/ItsRulesProjectCacheTest.java
new file mode 100644
index 0000000..3b33b41
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/ItsRulesProjectCacheTest.java
@@ -0,0 +1,155 @@
+// Copyright (C) 2018 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.its.base.workflow;
+
+import static com.googlesource.gerrit.plugins.its.base.workflow.RulesConfigReader.ACTION_KEY;
+import static com.googlesource.gerrit.plugins.its.base.workflow.RulesConfigReader.RULE_SECTION;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.isA;
+
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.extensions.config.FactoryModule;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectLevelConfig;
+import com.google.gerrit.server.project.ProjectState;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.googlesource.gerrit.plugins.its.base.GlobalRulesFileName;
+import com.googlesource.gerrit.plugins.its.base.PluginRulesFileName;
+import com.googlesource.gerrit.plugins.its.base.testutil.LoggingMockingTestCase;
+import com.googlesource.gerrit.plugins.its.base.workflow.RuleBaseTest.RuleBaseKind;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+import org.eclipse.jgit.lib.Config;
+
+public class ItsRulesProjectCacheTest extends LoggingMockingTestCase {
+ private class TestModule extends FactoryModule {
+ @Override
+ protected void configure() {
+ rulesConfigReader = createMock(RulesConfigReader.class);
+ bind(RulesConfigReader.class).toInstance(rulesConfigReader);
+
+ projectCache = createMock(ProjectCache.class);
+ bind(ProjectCache.class).toInstance(projectCache);
+
+ bind(String.class)
+ .annotatedWith(GlobalRulesFileName.class)
+ .toInstance(RuleBaseKind.GLOBAL.fileName);
+ bind(String.class)
+ .annotatedWith(PluginRulesFileName.class)
+ .toInstance(RuleBaseKind.ITS.fileName);
+ }
+ }
+
+ private static final String ACTION_1 = "action1";
+ private static final String CONDITION_KEY = "condition";
+ private static final String RULE_1 = "rule1";
+ private static final String TEST_PROJECT = "testProject";
+ private static final String VALUE_1 = "value1";
+
+ private Injector injector;
+ private ProjectCache projectCache;
+ private RulesConfigReader rulesConfigReader;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ injector = Guice.createInjector(new TestModule());
+ }
+
+ public void testProjectConfigIsLoaded() throws IOException {
+ Rule rule1 = new Rule(RULE_1);
+ ActionRequest action1 = new ActionRequest(ACTION_1);
+ Condition condition1 = new Condition(CONDITION_KEY, VALUE_1);
+ rule1.addActionRequest(action1);
+ rule1.addCondition(condition1);
+
+ ProjectState projectState = createMock(ProjectState.class);
+ ProjectLevelConfig projectLevelConfigGlobal = createMock(ProjectLevelConfig.class);
+ Config projectGlobalCfg = new Config();
+ projectGlobalCfg.setString(RULE_SECTION, RULE_1, CONDITION_KEY, VALUE_1);
+ projectGlobalCfg.setString(RULE_SECTION, RULE_1, ACTION_KEY, ACTION_1);
+ expect(projectLevelConfigGlobal.get()).andReturn(projectGlobalCfg);
+ expect(projectState.getConfig(RuleBaseKind.GLOBAL.fileName))
+ .andReturn(projectLevelConfigGlobal);
+ ProjectLevelConfig projectLevelConfigPlugin = createMock(ProjectLevelConfig.class);
+ expect(projectLevelConfigPlugin.get()).andReturn(new Config());
+ expect(projectState.getConfig(RuleBaseKind.ITS.fileName)).andReturn(projectLevelConfigPlugin);
+ expect(projectCache.checkedGet(new Project.NameKey(TEST_PROJECT))).andReturn(projectState);
+ expect(rulesConfigReader.getRulesFromConfig(isA(Config.class)))
+ .andReturn(ImmutableList.of(rule1))
+ .andReturn(ImmutableList.of());
+ replayMocks();
+
+ ItsRulesProjectCacheImpl.Loader loader =
+ injector.getInstance(ItsRulesProjectCacheImpl.Loader.class);
+ Collection<Rule> actual = loader.load(TEST_PROJECT);
+ List<Rule> expected = ImmutableList.of(rule1);
+
+ assertEquals("Rules do not match", expected, actual);
+ assertTrue(actual.contains(rule1));
+ }
+
+ public void testParentProjectConfigIsLoaded() throws IOException {
+ Rule rule1 = new Rule(RULE_1);
+ ActionRequest action1 = new ActionRequest(ACTION_1);
+ Condition condition1 = new Condition(CONDITION_KEY, VALUE_1);
+ rule1.addActionRequest(action1);
+ rule1.addCondition(condition1);
+
+ ProjectState projectState = createMock(ProjectState.class);
+ ProjectLevelConfig projectLevelConfigGlobal = createMock(ProjectLevelConfig.class);
+ expect(projectLevelConfigGlobal.get()).andReturn(new Config());
+ expect(projectState.getConfig(RuleBaseKind.GLOBAL.fileName))
+ .andReturn(projectLevelConfigGlobal);
+ ProjectLevelConfig projectLevelConfigPlugin = createMock(ProjectLevelConfig.class);
+ expect(projectLevelConfigPlugin.get()).andReturn(new Config());
+ expect(projectState.getConfig(RuleBaseKind.ITS.fileName)).andReturn(projectLevelConfigPlugin);
+
+ ProjectState parentProjectState = createMock(ProjectState.class);
+ ProjectLevelConfig parentProjectConfigGlobal = createMock(ProjectLevelConfig.class);
+ Config parentGlobalCfg = new Config();
+ parentGlobalCfg.setString(RULE_SECTION, RULE_1, CONDITION_KEY, VALUE_1);
+ parentGlobalCfg.setString(RULE_SECTION, RULE_1, ACTION_KEY, ACTION_1);
+ expect(parentProjectConfigGlobal.get()).andReturn(parentGlobalCfg);
+ expect(parentProjectState.getConfig(RuleBaseKind.GLOBAL.fileName))
+ .andReturn(parentProjectConfigGlobal);
+ ProjectLevelConfig parentProjectConfigPlugin = createMock(ProjectLevelConfig.class);
+ expect(parentProjectConfigPlugin.get()).andReturn(new Config());
+ expect(parentProjectState.getConfig(RuleBaseKind.ITS.fileName))
+ .andReturn(parentProjectConfigPlugin);
+ expect(projectState.parents()).andReturn(FluentIterable.of(parentProjectState));
+ expect(projectCache.checkedGet(new Project.NameKey(TEST_PROJECT))).andReturn(projectState);
+
+ expect(rulesConfigReader.getRulesFromConfig(isA(Config.class)))
+ .andReturn(ImmutableList.of())
+ .andReturn(ImmutableList.of())
+ .andReturn(ImmutableList.of(rule1))
+ .andReturn(ImmutableList.of());
+
+ replayMocks();
+
+ ItsRulesProjectCacheImpl.Loader loader =
+ injector.getInstance(ItsRulesProjectCacheImpl.Loader.class);
+ Collection<Rule> actual = loader.load(TEST_PROJECT);
+ List<Rule> expected = ImmutableList.of(rule1);
+
+ assertEquals("Rules do not match", expected, actual);
+ assertTrue(actual.contains(rule1));
+ }
+}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/LogEventTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/LogEventTest.java
new file mode 100644
index 0000000..0a26db8
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/LogEventTest.java
@@ -0,0 +1,159 @@
+// Copyright (C) 2013 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.its.base.workflow;
+
+import static org.easymock.EasyMock.expect;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.gerrit.extensions.config.FactoryModule;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.googlesource.gerrit.plugins.its.base.its.ItsFacade;
+import com.googlesource.gerrit.plugins.its.base.testutil.LoggingMockingTestCase;
+import java.io.IOException;
+import java.util.Map;
+import org.apache.log4j.Level;
+
+public class LogEventTest extends LoggingMockingTestCase {
+ private Injector injector;
+ private ItsFacade its;
+
+ public void testNull() throws IOException {
+ ActionRequest actionRequest = createMock(ActionRequest.class);
+ expect(actionRequest.getParameter(1)).andReturn(null);
+
+ replayMocks();
+
+ LogEvent logEvent = createLogEvent();
+ logEvent.execute(null, "4711", actionRequest, ImmutableMap.of());
+ }
+
+ public void testEmpty() throws IOException {
+ ActionRequest actionRequest = createMock(ActionRequest.class);
+ expect(actionRequest.getParameter(1)).andReturn("");
+
+ replayMocks();
+
+ LogEvent logEvent = createLogEvent();
+ logEvent.execute(null, "4711", actionRequest, ImmutableMap.of());
+ }
+
+ public void testLevelDefault() throws IOException {
+ ActionRequest actionRequest = createMock(ActionRequest.class);
+ expect(actionRequest.getParameter(1)).andReturn("");
+
+ Map<String, String> properties = ImmutableMap.of("KeyA", "ValueA");
+
+ replayMocks();
+
+ LogEvent logEvent = createLogEvent();
+ logEvent.execute(its, "4711", actionRequest, properties);
+
+ assertLogMessageContains("KeyA = ValueA", Level.INFO);
+ }
+
+ public void testLevelError() throws IOException {
+ ActionRequest actionRequest = createMock(ActionRequest.class);
+ expect(actionRequest.getParameter(1)).andReturn("error");
+
+ Map<String, String> properties = ImmutableMap.of("KeyA", "ValueA");
+
+ replayMocks();
+
+ LogEvent logEvent = createLogEvent();
+ logEvent.execute(its, "4711", actionRequest, properties);
+
+ assertLogMessageContains("KeyA = ValueA", Level.ERROR);
+ }
+
+ public void testLevelWarn() throws IOException {
+ ActionRequest actionRequest = createMock(ActionRequest.class);
+ expect(actionRequest.getParameter(1)).andReturn("warn");
+
+ Map<String, String> properties = ImmutableMap.of("KeyA", "ValueA");
+
+ replayMocks();
+
+ LogEvent logEvent = createLogEvent();
+ logEvent.execute(its, "4711", actionRequest, properties);
+
+ assertLogMessageContains("KeyA = ValueA", Level.WARN);
+ }
+
+ public void testLevelInfo() throws IOException {
+ ActionRequest actionRequest = createMock(ActionRequest.class);
+ expect(actionRequest.getParameter(1)).andReturn("info");
+
+ Map<String, String> properties = ImmutableMap.of("KeyA", "ValueA");
+
+ replayMocks();
+
+ LogEvent logEvent = createLogEvent();
+ logEvent.execute(its, "4711", actionRequest, properties);
+
+ assertLogMessageContains("KeyA = ValueA", Level.INFO);
+ }
+
+ public void testLevelDebug() throws IOException {
+ ActionRequest actionRequest = createMock(ActionRequest.class);
+ expect(actionRequest.getParameter(1)).andReturn("debug");
+
+ Map<String, String> properties = ImmutableMap.of("KeyA", "ValueA");
+
+ replayMocks();
+
+ LogEvent logEvent = createLogEvent();
+ logEvent.execute(its, "4711", actionRequest, properties);
+
+ assertLogMessageContains("KeyA = ValueA", Level.DEBUG);
+ }
+
+ public void testMultipleProperties() throws IOException {
+ ActionRequest actionRequest = createMock(ActionRequest.class);
+ expect(actionRequest.getParameter(1)).andReturn("info");
+
+ Map<String, String> properties =
+ ImmutableMap.<String, String>builder()
+ .put("KeyA", "ValueA")
+ .put("KeyB", "ValueB")
+ .put("KeyC", "ValueC")
+ .build();
+
+ replayMocks();
+
+ LogEvent logEvent = createLogEvent();
+ logEvent.execute(its, "4711", actionRequest, properties);
+
+ assertLogMessageContains("KeyA = ValueA", Level.INFO);
+ assertLogMessageContains("KeyB = ValueB", Level.INFO);
+ assertLogMessageContains("KeyC = ValueC", Level.INFO);
+ }
+
+ private LogEvent createLogEvent() {
+ return injector.getInstance(LogEvent.class);
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ injector = Guice.createInjector(new TestModule());
+ }
+
+ private class TestModule extends FactoryModule {
+ @Override
+ protected void configure() {
+ its = createMock(ItsFacade.class);
+ }
+ }
+}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/PropertyTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/PropertyTest.java
deleted file mode 100644
index 06ac41a..0000000
--- a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/PropertyTest.java
+++ /dev/null
@@ -1,118 +0,0 @@
-// Copyright (C) 2013 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.its.base.workflow;
-
-import com.google.gerrit.extensions.config.FactoryModule;
-import com.google.inject.Guice;
-import com.google.inject.Injector;
-import com.googlesource.gerrit.plugins.its.base.testutil.LoggingMockingTestCase;
-
-public class PropertyTest extends LoggingMockingTestCase {
- private Injector injector;
-
- public void testGetKeyNull() {
- Property property = new Property(null, "testValue");
- assertNull("Key is not null", property.getKey());
- }
-
- public void testGetKeyNonNull() {
- Property property = createProperty("testKey", "testValue");
- assertEquals("Key does not match", "testKey", property.getKey());
- }
-
- public void testGetValueNull() {
- Property property = createProperty("testKey", null);
- assertNull("Value is not null", property.getValue());
- }
-
- public void testGetValueNonNull() {
- Property property = createProperty("testKey", "testValue");
- assertEquals("Value does not match", "testValue", property.getValue());
- }
-
- public void testEqualsSimilar() {
- Property propertyA = createProperty("testKey", "testValue");
- Property propertyB = createProperty("testKey", "testValue");
- assertTrue("Property is equal to similar", propertyA.equals(propertyB));
- }
-
- public void testEqualsNull() {
- Property property = createProperty("testKey", "testValue");
- assertFalse("Property is equal to null", property.equals(null));
- }
-
- public void testEqualsNull2() {
- Property property = new Property(null, null);
- assertFalse("Property is equal to null", property.equals(null));
- }
-
- public void testEqualsNulledKey() {
- Property propertyA = new Property(null, "testValue");
- Property propertyB = createProperty("testKey", "testValue");
- assertFalse("Single nulled key does match", propertyA.equals(propertyB));
- }
-
- public void testEqualsNulledKey2() {
- Property propertyA = createProperty("testKey", "testValue");
- Property propertyB = new Property(null, "testValue");
- assertFalse("Single nulled key does match", propertyA.equals(propertyB));
- }
-
- public void testEqualsNulledValue() {
- Property propertyA = createProperty("testKey", "testValue");
- Property propertyB = createProperty("testKey", null);
- assertFalse("Single nulled value does match", propertyA.equals(propertyB));
- }
-
- public void testEqualsNulledValue2() {
- Property propertyA = createProperty("testKey", null);
- Property propertyB = createProperty("testKey", "testValue");
- assertFalse("Single nulled value does match", propertyA.equals(propertyB));
- }
-
- public void testHashCodeEquals() {
- Property propertyA = createProperty("testKey", "testValue");
- Property propertyB = createProperty("testKey", "testValue");
- assertEquals("Hash codes do not match", propertyA.hashCode(), propertyB.hashCode());
- }
-
- public void testHashCodeNullKey() {
- Property property = new Property(null, "testValue");
- property.hashCode();
- }
-
- public void testHashCodeNullValue() {
- Property property = createProperty("testKey", null);
- property.hashCode();
- }
-
- private Property createProperty(String key, String value) {
- Property.Factory factory = injector.getInstance(Property.Factory.class);
- return factory.create(key, value);
- }
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
-
- injector = Guice.createInjector(new TestModule());
- }
-
- private class TestModule extends FactoryModule {
- @Override
- protected void configure() {
- factory(Property.Factory.class);
- }
- }
-}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/RuleBaseTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/RuleBaseTest.java
index 80e12fb..305e8d1 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/RuleBaseTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/RuleBaseTest.java
@@ -11,152 +11,76 @@
// 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.its.base.workflow;
import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.isA;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.gerrit.extensions.annotations.PluginName;
import com.google.gerrit.extensions.config.FactoryModule;
-import com.google.gerrit.server.config.SitePath;
import com.google.inject.Guice;
import com.google.inject.Injector;
+import com.googlesource.gerrit.plugins.its.base.GlobalRulesFileName;
+import com.googlesource.gerrit.plugins.its.base.ItsPath;
+import com.googlesource.gerrit.plugins.its.base.PluginRulesFileName;
import com.googlesource.gerrit.plugins.its.base.testutil.LoggingMockingTestCase;
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection;
-import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.UUID;
+import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.util.FileUtils;
public class RuleBaseTest extends LoggingMockingTestCase {
+ private static final String PROJECT_KEY = "project";
+ private static final String TEST_PROJECT = "testProject";
+
private Injector injector;
- private Path sitePath;
- private Rule.Factory ruleFactory;
- private Condition.Factory conditionFactory;
- private ActionRequest.Factory actionRequestFactory;
+ private Path itsPath;
+ private RulesConfigReader rulesConfigReader;
+ private ItsRulesProjectCache rulesProjectCache;
private boolean cleanupSitePath;
- private enum RuleBaseKind {
- GLOBAL,
- ITS,
- FAULTY
- }
+ public enum RuleBaseKind {
+ GLOBAL("actions"),
+ ITS("actions-ItsTestName");
- public void testWarnNonExistingRuleBase() {
- replayMocks();
+ String fileName;
- createRuleBase();
-
- assertLogMessageContains("Neither global");
- }
-
- public void testEmptyRuleBase() throws IOException {
- injectRuleBase("");
-
- replayMocks();
-
- createRuleBase();
- }
-
- public void testSimpleRuleBase() throws IOException {
- injectRuleBase("[rule \"rule1\"]\n" + "\tconditionA = value1\n" + "\taction = action1");
-
- Rule rule1 = createMock(Rule.class);
- expect(ruleFactory.create("rule1")).andReturn(rule1);
-
- Condition condition1 = createMock(Condition.class);
- expect(conditionFactory.create("conditionA", "value1")).andReturn(condition1);
- rule1.addCondition(condition1);
-
- ActionRequest actionRequest1 = createMock(ActionRequest.class);
- expect(actionRequestFactory.create("action1")).andReturn(actionRequest1);
- rule1.addActionRequest(actionRequest1);
-
- replayMocks();
-
- createRuleBase();
- }
-
- public void testBasicRuleBase() throws IOException {
- injectRuleBase(
- "[rule \"rule1\"]\n"
- + "\tconditionA = value1,value2\n"
- + "\tconditionA = value3,value of 4\n"
- + "\tconditionB = value5\n"
- + "\taction = action1\n"
- + "\taction = action2 param\n"
- + "\n"
- + "[ruleXZ \"nonrule\"]\n"
- + "\tconditionA = value1\n"
- + "\taction = action2\n"
- + "[rule \"rule2\"]\n"
- + "\tconditionC = value6\n"
- + "\taction = action3");
-
- Rule rule1 = createMock(Rule.class);
- expect(ruleFactory.create("rule1")).andReturn(rule1);
-
- Condition condition1 = createMock(Condition.class);
- expect(conditionFactory.create("conditionA", "value1,value2")).andReturn(condition1);
- rule1.addCondition(condition1);
-
- Condition condition2 = createMock(Condition.class);
- expect(conditionFactory.create("conditionA", "value3,value of 4")).andReturn(condition2);
- rule1.addCondition(condition2);
-
- Condition condition3 = createMock(Condition.class);
- expect(conditionFactory.create("conditionB", "value5")).andReturn(condition3);
- rule1.addCondition(condition3);
-
- ActionRequest actionRequest1 = createMock(ActionRequest.class);
- expect(actionRequestFactory.create("action1")).andReturn(actionRequest1);
- rule1.addActionRequest(actionRequest1);
-
- ActionRequest actionRequest2 = createMock(ActionRequest.class);
- expect(actionRequestFactory.create("action2 param")).andReturn(actionRequest2);
- rule1.addActionRequest(actionRequest2);
-
- Rule rule2 = createMock(Rule.class);
- expect(ruleFactory.create("rule2")).andReturn(rule2);
-
- Condition condition4 = createMock(Condition.class);
- expect(conditionFactory.create("conditionC", "value6")).andReturn(condition4);
- rule2.addCondition(condition4);
-
- ActionRequest actionRequest3 = createMock(ActionRequest.class);
- expect(actionRequestFactory.create("action3")).andReturn(actionRequest3);
- rule2.addActionRequest(actionRequest3);
-
- replayMocks();
-
- createRuleBase();
+ RuleBaseKind(String fileName) {
+ this.fileName = fileName + ".config";
+ }
}
public void testActionRequestsForSimple() throws IOException {
- injectRuleBase("[rule \"rule1\"]\n" + "\taction = action1");
+ String rules = "[rule \"rule1\"]\n\taction = action1\n";
+ injectRuleBase(rules);
Rule rule1 = createMock(Rule.class);
- expect(ruleFactory.create("rule1")).andReturn(rule1);
-
ActionRequest actionRequest1 = createMock(ActionRequest.class);
- expect(actionRequestFactory.create("action1")).andReturn(actionRequest1);
- rule1.addActionRequest(actionRequest1);
- Collection<Property> properties = Collections.emptySet();
+ Map<String, String> properties = ImmutableMap.of(PROJECT_KEY, TEST_PROJECT);
List<ActionRequest> rule1Match = Lists.newArrayListWithCapacity(1);
rule1Match.add(actionRequest1);
expect(rule1.actionRequestsFor(properties)).andReturn(rule1Match);
+ expect(rulesConfigReader.getRulesFromConfig(isA(Config.class)))
+ .andReturn(ImmutableList.of(rule1))
+ .once();
+
+ expect(rulesProjectCache.get(TEST_PROJECT)).andReturn(ImmutableList.of());
+
replayMocks();
RuleBase ruleBase = createRuleBase();
@@ -175,163 +99,96 @@
+ "\taction = action2\n"
+ "\n"
+ "[rule \"rule2\"]\n"
- + "\taction = action3");
+ + "\taction = action3\n");
Rule rule1 = createMock(Rule.class);
- expect(ruleFactory.create("rule1")).andReturn(rule1);
-
ActionRequest actionRequest1 = createMock(ActionRequest.class);
- expect(actionRequestFactory.create("action1")).andReturn(actionRequest1);
- rule1.addActionRequest(actionRequest1);
-
ActionRequest actionRequest2 = createMock(ActionRequest.class);
- expect(actionRequestFactory.create("action2")).andReturn(actionRequest2);
- rule1.addActionRequest(actionRequest2);
Rule rule2 = createMock(Rule.class);
- expect(ruleFactory.create("rule2")).andReturn(rule2);
-
ActionRequest actionRequest3 = createMock(ActionRequest.class);
- expect(actionRequestFactory.create("action3")).andReturn(actionRequest3);
- rule2.addActionRequest(actionRequest3);
- Collection<Property> properties = Lists.newArrayListWithCapacity(1);
- Property property1 = createMock(Property.class);
- properties.add(property1);
+ Map<String, String> properties = ImmutableMap.of(PROJECT_KEY, TEST_PROJECT);
- List<ActionRequest> rule1Match = Lists.newArrayListWithCapacity(2);
- rule1Match.add(actionRequest1);
- rule1Match.add(actionRequest2);
- expect(rule1.actionRequestsFor(properties)).andReturn(rule1Match);
+ List<ActionRequest> rule1Match = ImmutableList.of(actionRequest1, actionRequest2);
+ expect(rule1.actionRequestsFor(properties)).andReturn(rule1Match).anyTimes();
- List<ActionRequest> rule2Match = Lists.newArrayListWithCapacity(1);
- rule2Match.add(actionRequest3);
- expect(rule2.actionRequestsFor(properties)).andReturn(rule2Match);
+ List<ActionRequest> rule2Match = ImmutableList.of(actionRequest3);
+ expect(rule2.actionRequestsFor(properties)).andReturn(rule2Match).anyTimes();
+
+ expect(rulesProjectCache.get(TEST_PROJECT)).andReturn(ImmutableList.of());
+
+ expect(rulesConfigReader.getRulesFromConfig(isA(Config.class)))
+ .andReturn(ImmutableList.of(rule1, rule2))
+ .andReturn(ImmutableList.of())
+ .anyTimes();
replayMocks();
RuleBase ruleBase = createRuleBase();
Collection<ActionRequest> actual = ruleBase.actionRequestsFor(properties);
- List<ActionRequest> expected = Lists.newArrayListWithCapacity(3);
- expected.add(actionRequest1);
- expected.add(actionRequest2);
- expected.add(actionRequest3);
+ List<ActionRequest> expected = ImmutableList.of(actionRequest1, actionRequest2, actionRequest3);
assertEquals("Matched actionRequests do not match", expected, actual);
}
- public void testWarnExistingFaultyNameRuleBaseFile() throws IOException {
- injectRuleBase("", RuleBaseKind.FAULTY);
-
- replayMocks();
-
- createRuleBase();
-
- assertLogMessageContains("Please migrate"); // Migration warning for old name
- assertLogMessageContains("Neither global"); // For rule file at at usual places
- }
-
- public void testSimpleFaultyNameRuleBase() throws IOException {
- injectRuleBase(
- "[rule \"rule1\"]\n" + "\tconditionA = value1\n" + "\taction = action1",
- RuleBaseKind.FAULTY);
-
- Rule rule1 = createMock(Rule.class);
- expect(ruleFactory.create("rule1")).andReturn(rule1);
-
- Condition condition1 = createMock(Condition.class);
- expect(conditionFactory.create("conditionA", "value1")).andReturn(condition1);
- rule1.addCondition(condition1);
-
- ActionRequest actionRequest1 = createMock(ActionRequest.class);
- expect(actionRequestFactory.create("action1")).andReturn(actionRequest1);
- rule1.addActionRequest(actionRequest1);
-
- replayMocks();
-
- createRuleBase();
-
- assertLogMessageContains("Please migrate"); // Migration warning for old name
- assertLogMessageContains("Neither global"); // For rule file at at usual places
- }
-
- public void testSimpleItsRuleBase() throws IOException {
- injectRuleBase(
- "[rule \"rule1\"]\n" + "\tconditionA = value1\n" + "\taction = action1", RuleBaseKind.ITS);
-
- Rule rule1 = createMock(Rule.class);
- expect(ruleFactory.create("rule1")).andReturn(rule1);
-
- Condition condition1 = createMock(Condition.class);
- expect(conditionFactory.create("conditionA", "value1")).andReturn(condition1);
- rule1.addCondition(condition1);
-
- ActionRequest actionRequest1 = createMock(ActionRequest.class);
- expect(actionRequestFactory.create("action1")).andReturn(actionRequest1);
- rule1.addActionRequest(actionRequest1);
-
- replayMocks();
-
- createRuleBase();
- }
-
public void testAllRuleBaseFilesAreLoaded() throws IOException {
- injectRuleBase("[rule \"rule1\"]\n" + "\taction = action1", RuleBaseKind.FAULTY);
+ injectRuleBase("[rule \"rule2\"]\n\taction = action2", RuleBaseKind.GLOBAL);
- injectRuleBase("[rule \"rule2\"]\n" + "\taction = action2", RuleBaseKind.GLOBAL);
+ injectRuleBase("[rule \"rule3\"]\n\taction = action3", RuleBaseKind.ITS);
- injectRuleBase("[rule \"rule3\"]\n" + "\taction = action3", RuleBaseKind.ITS);
-
- Collection<Property> properties = Collections.emptySet();
-
- Rule rule1 = createMock(Rule.class);
- expect(ruleFactory.create("rule1")).andReturn(rule1);
-
- ActionRequest actionRequest1 = createMock(ActionRequest.class);
- expect(actionRequestFactory.create("action1")).andReturn(actionRequest1);
- rule1.addActionRequest(actionRequest1);
-
- List<ActionRequest> rule1Match = Lists.newArrayListWithCapacity(1);
- rule1Match.add(actionRequest1);
- expect(rule1.actionRequestsFor(properties)).andReturn(rule1Match);
+ Map<String, String> properties = ImmutableMap.of(PROJECT_KEY, TEST_PROJECT);
Rule rule2 = createMock(Rule.class);
- expect(ruleFactory.create("rule2")).andReturn(rule2);
-
ActionRequest actionRequest2 = createMock(ActionRequest.class);
- expect(actionRequestFactory.create("action2")).andReturn(actionRequest2);
- rule2.addActionRequest(actionRequest2);
- List<ActionRequest> rule2Match = Lists.newArrayListWithCapacity(1);
- rule2Match.add(actionRequest2);
+ List<ActionRequest> rule2Match = ImmutableList.of(actionRequest2);
expect(rule2.actionRequestsFor(properties)).andReturn(rule2Match);
Rule rule3 = createMock(Rule.class);
- expect(ruleFactory.create("rule3")).andReturn(rule3);
-
ActionRequest actionRequest3 = createMock(ActionRequest.class);
- expect(actionRequestFactory.create("action3")).andReturn(actionRequest3);
- rule3.addActionRequest(actionRequest3);
- List<ActionRequest> rule3Match = Lists.newArrayListWithCapacity(1);
- rule3Match.add(actionRequest3);
+ List<ActionRequest> rule3Match = ImmutableList.of(actionRequest3);
expect(rule3.actionRequestsFor(properties)).andReturn(rule3Match);
+ expect(rulesProjectCache.get(TEST_PROJECT)).andReturn(ImmutableList.of());
+
+ expect(rulesConfigReader.getRulesFromConfig(isA(Config.class)))
+ .andReturn(ImmutableList.of(rule2, rule3))
+ .andReturn(ImmutableList.of())
+ .anyTimes();
+
replayMocks();
RuleBase ruleBase = createRuleBase();
Collection<ActionRequest> actual = ruleBase.actionRequestsFor(properties);
- List<ActionRequest> expected = Lists.newArrayListWithCapacity(3);
- expected.add(actionRequest1);
- expected.add(actionRequest2);
- expected.add(actionRequest3);
+ List<ActionRequest> expected = ImmutableList.of(actionRequest2, actionRequest3);
assertEquals("Matched actionRequests do not match", expected, actual);
+ }
- assertLogMessageContains("Please migrate"); // Migration warning for old name
+ public void testProjectConfigIsLoaded() {
+ Rule rule1 = createMock(Rule.class);
+ ActionRequest actionRequest1 = createMock(ActionRequest.class);
+
+ Map<String, String> properties = ImmutableMap.of(PROJECT_KEY, TEST_PROJECT);
+
+ List<ActionRequest> rule1Match = ImmutableList.of(actionRequest1);
+ expect(rule1.actionRequestsFor(properties)).andReturn(rule1Match);
+
+ expect(rulesProjectCache.get(TEST_PROJECT)).andReturn(ImmutableList.of(rule1));
+
+ replayMocks();
+
+ RuleBase ruleBase = createRuleBase();
+ Collection<ActionRequest> actual = ruleBase.actionRequestsFor(properties);
+
+ List<ActionRequest> expected = ImmutableList.of(actionRequest1);
+
+ assertEquals("Matched actionRequests do not match", expected, actual);
}
private RuleBase createRuleBase() {
@@ -343,35 +200,9 @@
}
private void injectRuleBase(String rules, RuleBaseKind ruleBaseKind) throws IOException {
- String baseName = "";
- switch (ruleBaseKind) {
- case GLOBAL:
- baseName = "actions";
- break;
- case ITS:
- baseName = "actions-ItsTestName";
- break;
- case FAULTY:
- baseName = "action";
- break;
- default:
- fail("Unknown ruleBaseKind");
- }
- File ruleBaseFile =
- new File(
- sitePath.toFile(),
- "etc" + File.separatorChar + "its" + File.separator + baseName + ".config");
-
- File ruleBaseParentFile = ruleBaseFile.getParentFile();
- if (!ruleBaseParentFile.exists()) {
- assertTrue(
- "Failed to create parent (" + ruleBaseParentFile + ") for " + "rule base",
- ruleBaseParentFile.mkdirs());
- }
- try (FileWriter unbufferedWriter = new FileWriter(ruleBaseFile);
- BufferedWriter writer = new BufferedWriter(unbufferedWriter)) {
- writer.write(rules);
- }
+ Path ruleBaseFile = itsPath.resolve(ruleBaseKind.fileName);
+ Files.createDirectories(ruleBaseFile.getParent());
+ Files.write(ruleBaseFile, rules.getBytes());
}
@Override
@@ -384,8 +215,8 @@
@Override
public void tearDown() throws Exception {
if (cleanupSitePath) {
- if (Files.exists(sitePath)) {
- FileUtils.delete(sitePath.toFile(), FileUtils.RECURSIVE);
+ if (Files.exists(itsPath)) {
+ FileUtils.delete(itsPath.toFile(), FileUtils.RECURSIVE);
}
}
super.tearDown();
@@ -401,20 +232,25 @@
bind(String.class).annotatedWith(PluginName.class).toInstance("ItsTestName");
- sitePath = randomTargetPath();
- assertFalse("sitePath already (" + sitePath + ") already exists", Files.exists(sitePath));
+ itsPath = randomTargetPath().resolve("etc").resolve("its");
+ assertFalse("itsPath (" + itsPath + ") already exists", Files.exists(itsPath));
cleanupSitePath = true;
- bind(Path.class).annotatedWith(SitePath.class).toInstance(sitePath);
+ bind(Path.class).annotatedWith(ItsPath.class).toInstance(itsPath);
- ruleFactory = createMock(Rule.Factory.class);
- bind(Rule.Factory.class).toInstance(ruleFactory);
+ rulesConfigReader = createMock(RulesConfigReader.class);
+ bind(RulesConfigReader.class).toInstance(rulesConfigReader);
- conditionFactory = createMock(Condition.Factory.class);
- bind(Condition.Factory.class).toInstance(conditionFactory);
+ rulesProjectCache = createMock(ItsRulesProjectCache.class);
+ bind(ItsRulesProjectCache.class).toInstance(rulesProjectCache);
- actionRequestFactory = createMock(ActionRequest.Factory.class);
- bind(ActionRequest.Factory.class).toInstance(actionRequestFactory);
+ bind(String.class)
+ .annotatedWith(GlobalRulesFileName.class)
+ .toInstance(RuleBaseKind.GLOBAL.fileName);
+
+ bind(String.class)
+ .annotatedWith(PluginRulesFileName.class)
+ .toInstance(RuleBaseKind.ITS.fileName);
}
}
}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/RuleTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/RuleTest.java
index 6d4d234..c106a81 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/RuleTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/RuleTest.java
@@ -15,6 +15,7 @@
import static org.easymock.EasyMock.expect;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.gerrit.extensions.config.FactoryModule;
import com.google.inject.Guice;
@@ -23,6 +24,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
public class RuleTest extends LoggingMockingTestCase {
private Injector injector;
@@ -33,7 +35,7 @@
}
public void testActionsForUnconditionalRule() {
- Collection<Property> properties = Collections.emptySet();
+ Map<String, String> properties = ImmutableMap.of();
Rule rule = createRule("testRule");
@@ -50,7 +52,7 @@
}
public void testActionRequestsForConditionalRuleEmptyProperties() {
- Collection<Property> properties = Collections.emptySet();
+ Map<String, String> properties = ImmutableMap.of();
Rule rule = createRule("testRule");
@@ -70,7 +72,7 @@
}
public void testActionRequestsForConditionalRules() {
- Collection<Property> properties = Collections.emptySet();
+ Map<String, String> properties = ImmutableMap.of();
Rule rule = createRule("testRule");
@@ -94,7 +96,7 @@
}
public void testActionRequestsForMultipleActionRequests() {
- Collection<Property> properties = Collections.emptySet();
+ Map<String, String> properties = ImmutableMap.of();
Rule rule = createRule("testRule");
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/RulesConfigReaderTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/RulesConfigReaderTest.java
new file mode 100644
index 0000000..96fb869
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/RulesConfigReaderTest.java
@@ -0,0 +1,84 @@
+// Copyright (C) 2018 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.its.base.workflow;
+
+import static com.googlesource.gerrit.plugins.its.base.workflow.RulesConfigReader.ACTION_KEY;
+import static com.googlesource.gerrit.plugins.its.base.workflow.RulesConfigReader.RULE_SECTION;
+import static org.easymock.EasyMock.expect;
+
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.extensions.config.FactoryModule;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.googlesource.gerrit.plugins.its.base.testutil.LoggingMockingTestCase;
+import java.util.Collection;
+import org.eclipse.jgit.lib.Config;
+
+public class RulesConfigReaderTest extends LoggingMockingTestCase {
+
+ private class TestModule extends FactoryModule {
+ @Override
+ protected void configure() {
+ ruleFactory = createMock(Rule.Factory.class);
+ bind(Rule.Factory.class).toInstance(ruleFactory);
+
+ conditionFactory = createMock(Condition.Factory.class);
+ bind(Condition.Factory.class).toInstance(conditionFactory);
+
+ actionRequestFactory = createMock(ActionRequest.Factory.class);
+ bind(ActionRequest.Factory.class).toInstance(actionRequestFactory);
+ }
+ }
+
+ private static final String ACTION_1 = "action1";
+ private static final String CONDITION_KEY = "condition";
+ private static final String RULE_1 = "rule1";
+ private static final String VALUE_1 = "value1";
+
+ private ActionRequest.Factory actionRequestFactory;
+ private Condition.Factory conditionFactory;
+ private Rule.Factory ruleFactory;
+ private Injector injector;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ injector = Guice.createInjector(new TestModule());
+ }
+
+ public void testGetRulesFromConfig() {
+ Config cfg = new Config();
+ cfg.setString(RULE_SECTION, RULE_1, CONDITION_KEY, VALUE_1);
+ cfg.setString(RULE_SECTION, RULE_1, ACTION_KEY, ACTION_1);
+
+ Rule rule1 = createMock(Rule.class);
+ expect(ruleFactory.create(RULE_1)).andReturn(rule1);
+
+ ActionRequest actionRequest1 = createMock(ActionRequest.class);
+ expect(actionRequestFactory.create(ACTION_1)).andReturn(actionRequest1);
+ rule1.addActionRequest(actionRequest1);
+
+ Condition condition1 = createMock(Condition.class);
+ expect(conditionFactory.create(CONDITION_KEY, VALUE_1)).andReturn(condition1);
+ rule1.addCondition(condition1);
+
+ replayMocks();
+
+ Collection<Rule> expected = ImmutableList.of(rule1);
+
+ RulesConfigReader rulesConfigReader = injector.getInstance(RulesConfigReader.class);
+ assertEquals("Rules do not match", expected, rulesConfigReader.getRulesFromConfig(cfg));
+ }
+}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/action/AddStandardCommentTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/action/AddStandardCommentTest.java
deleted file mode 100644
index cfcda01..0000000
--- a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/action/AddStandardCommentTest.java
+++ /dev/null
@@ -1,313 +0,0 @@
-// Copyright (C) 2017 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.its.base.workflow.action;
-
-import static org.easymock.EasyMock.expect;
-
-import com.google.common.collect.Sets;
-import com.google.gerrit.extensions.config.FactoryModule;
-import com.google.inject.Guice;
-import com.google.inject.Injector;
-import com.googlesource.gerrit.plugins.its.base.its.ItsFacade;
-import com.googlesource.gerrit.plugins.its.base.testutil.LoggingMockingTestCase;
-import com.googlesource.gerrit.plugins.its.base.workflow.ActionRequest;
-import com.googlesource.gerrit.plugins.its.base.workflow.Property;
-import java.io.IOException;
-import java.util.Set;
-
-public class AddStandardCommentTest extends LoggingMockingTestCase {
- private Injector injector;
-
- private ItsFacade its;
-
- public void testChangeMergedPlain() throws IOException {
- ActionRequest actionRequest = createMock(ActionRequest.class);
-
- Set<Property> properties = Sets.newHashSet();
-
- Property propertyEventType = createMock(Property.class);
- expect(propertyEventType.getKey()).andReturn("event-type").anyTimes();
- expect(propertyEventType.getValue()).andReturn("change-merged").anyTimes();
- properties.add(propertyEventType);
-
- its.addComment("42", "Change merged");
- replayMocks();
-
- Action action = injector.getInstance(AddStandardComment.class);
- action.execute("42", actionRequest, properties);
- }
-
- public void testChangeMergedFull() throws IOException {
- ActionRequest actionRequest = createMock(ActionRequest.class);
-
- Set<Property> properties = Sets.newHashSet();
-
- Property propertyEventType = createMock(Property.class);
- expect(propertyEventType.getKey()).andReturn("event-type").anyTimes();
- expect(propertyEventType.getValue()).andReturn("change-merged").anyTimes();
- properties.add(propertyEventType);
-
- Property propertySubject = createMock(Property.class);
- expect(propertySubject.getKey()).andReturn("subject").anyTimes();
- expect(propertySubject.getValue()).andReturn("Test-Change-Subject").anyTimes();
- properties.add(propertySubject);
-
- Property propertyChangeNumber = createMock(Property.class);
- expect(propertyChangeNumber.getKey()).andReturn("changeNumber").anyTimes();
- expect(propertyChangeNumber.getValue()).andReturn("4711").anyTimes();
- properties.add(propertyChangeNumber);
-
- Property propertySubmitterName = createMock(Property.class);
- expect(propertySubmitterName.getKey()).andReturn("submitterName").anyTimes();
- expect(propertySubmitterName.getValue()).andReturn("John Doe").anyTimes();
- properties.add(propertySubmitterName);
-
- Property propertyChangeUrl = createMock(Property.class);
- expect(propertyChangeUrl.getKey()).andReturn("changeUrl").anyTimes();
- expect(propertyChangeUrl.getValue()).andReturn("http://example.org/change").anyTimes();
- properties.add(propertyChangeUrl);
-
- expect(its.createLinkForWebui("http://example.org/change", "http://example.org/change"))
- .andReturn("HtTp://ExAmPlE.OrG/ChAnGe");
-
- its.addComment(
- "176",
- "Change 4711 merged by John Doe:\n"
- + "Test-Change-Subject\n"
- + "\n"
- + "HtTp://ExAmPlE.OrG/ChAnGe");
- replayMocks();
-
- Action action = injector.getInstance(AddStandardComment.class);
- action.execute("176", actionRequest, properties);
- }
-
- public void testChangeAbandonedPlain() throws IOException {
- ActionRequest actionRequest = createMock(ActionRequest.class);
-
- Set<Property> properties = Sets.newHashSet();
-
- Property propertyEventType = createMock(Property.class);
- expect(propertyEventType.getKey()).andReturn("event-type").anyTimes();
- expect(propertyEventType.getValue()).andReturn("change-abandoned").anyTimes();
- properties.add(propertyEventType);
-
- its.addComment("42", "Change abandoned");
- replayMocks();
-
- Action action = injector.getInstance(AddStandardComment.class);
- action.execute("42", actionRequest, properties);
- }
-
- public void testChangeAbandonedFull() throws IOException {
- ActionRequest actionRequest = createMock(ActionRequest.class);
-
- Set<Property> properties = Sets.newHashSet();
-
- Property propertyEventType = createMock(Property.class);
- expect(propertyEventType.getKey()).andReturn("event-type").anyTimes();
- expect(propertyEventType.getValue()).andReturn("change-abandoned").anyTimes();
- properties.add(propertyEventType);
-
- Property propertyReason = createMock(Property.class);
- expect(propertyReason.getKey()).andReturn("reason").anyTimes();
- expect(propertyReason.getValue()).andReturn("Test-Reason").anyTimes();
- properties.add(propertyReason);
-
- Property propertySubject = createMock(Property.class);
- expect(propertySubject.getKey()).andReturn("subject").anyTimes();
- expect(propertySubject.getValue()).andReturn("Test-Change-Subject").anyTimes();
- properties.add(propertySubject);
-
- Property propertyChangeNumber = createMock(Property.class);
- expect(propertyChangeNumber.getKey()).andReturn("changeNumber").anyTimes();
- expect(propertyChangeNumber.getValue()).andReturn("4711").anyTimes();
- properties.add(propertyChangeNumber);
-
- Property propertySubmitterName = createMock(Property.class);
- expect(propertySubmitterName.getKey()).andReturn("abandonerName").anyTimes();
- expect(propertySubmitterName.getValue()).andReturn("John Doe").anyTimes();
- properties.add(propertySubmitterName);
-
- Property propertyChangeUrl = createMock(Property.class);
- expect(propertyChangeUrl.getKey()).andReturn("changeUrl").anyTimes();
- expect(propertyChangeUrl.getValue()).andReturn("http://example.org/change").anyTimes();
- properties.add(propertyChangeUrl);
-
- expect(its.createLinkForWebui("http://example.org/change", "http://example.org/change"))
- .andReturn("HtTp://ExAmPlE.OrG/ChAnGe");
-
- its.addComment(
- "176",
- "Change 4711 abandoned by John Doe:\n"
- + "Test-Change-Subject\n"
- + "\n"
- + "Reason:\n"
- + "Test-Reason\n"
- + "\n"
- + "HtTp://ExAmPlE.OrG/ChAnGe");
- replayMocks();
-
- Action action = injector.getInstance(AddStandardComment.class);
- action.execute("176", actionRequest, properties);
- }
-
- public void testChangeRestoredPlain() throws IOException {
- ActionRequest actionRequest = createMock(ActionRequest.class);
-
- Set<Property> properties = Sets.newHashSet();
-
- Property propertyEventType = createMock(Property.class);
- expect(propertyEventType.getKey()).andReturn("event-type").anyTimes();
- expect(propertyEventType.getValue()).andReturn("change-restored").anyTimes();
- properties.add(propertyEventType);
-
- its.addComment("42", "Change restored");
- replayMocks();
-
- Action action = injector.getInstance(AddStandardComment.class);
- action.execute("42", actionRequest, properties);
- }
-
- public void testChangeRestoredFull() throws IOException {
- ActionRequest actionRequest = createMock(ActionRequest.class);
-
- Set<Property> properties = Sets.newHashSet();
-
- Property propertyEventType = createMock(Property.class);
- expect(propertyEventType.getKey()).andReturn("event-type").anyTimes();
- expect(propertyEventType.getValue()).andReturn("change-restored").anyTimes();
- properties.add(propertyEventType);
-
- Property propertyReason = createMock(Property.class);
- expect(propertyReason.getKey()).andReturn("reason").anyTimes();
- expect(propertyReason.getValue()).andReturn("Test-Reason").anyTimes();
- properties.add(propertyReason);
-
- Property propertySubject = createMock(Property.class);
- expect(propertySubject.getKey()).andReturn("subject").anyTimes();
- expect(propertySubject.getValue()).andReturn("Test-Change-Subject").anyTimes();
- properties.add(propertySubject);
-
- Property propertyChangeNumber = createMock(Property.class);
- expect(propertyChangeNumber.getKey()).andReturn("changeNumber").anyTimes();
- expect(propertyChangeNumber.getValue()).andReturn("4711").anyTimes();
- properties.add(propertyChangeNumber);
-
- Property propertySubmitterName = createMock(Property.class);
- expect(propertySubmitterName.getKey()).andReturn("restorerName").anyTimes();
- expect(propertySubmitterName.getValue()).andReturn("John Doe").anyTimes();
- properties.add(propertySubmitterName);
-
- Property propertyChangeUrl = createMock(Property.class);
- expect(propertyChangeUrl.getKey()).andReturn("changeUrl").anyTimes();
- expect(propertyChangeUrl.getValue()).andReturn("http://example.org/change").anyTimes();
- properties.add(propertyChangeUrl);
-
- expect(its.createLinkForWebui("http://example.org/change", "http://example.org/change"))
- .andReturn("HtTp://ExAmPlE.OrG/ChAnGe");
-
- its.addComment(
- "176",
- "Change 4711 restored by John Doe:\n"
- + "Test-Change-Subject\n"
- + "\n"
- + "Reason:\n"
- + "Test-Reason\n"
- + "\n"
- + "HtTp://ExAmPlE.OrG/ChAnGe");
- replayMocks();
-
- Action action = injector.getInstance(AddStandardComment.class);
- action.execute("176", actionRequest, properties);
- }
-
- public void testPatchSetCreatedPlain() throws IOException {
- ActionRequest actionRequest = createMock(ActionRequest.class);
-
- Set<Property> properties = Sets.newHashSet();
-
- Property propertyEventType = createMock(Property.class);
- expect(propertyEventType.getKey()).andReturn("event-type").anyTimes();
- expect(propertyEventType.getValue()).andReturn("patchset-created").anyTimes();
- properties.add(propertyEventType);
-
- its.addComment("42", "Change had a related patch set uploaded");
- replayMocks();
-
- Action action = injector.getInstance(AddStandardComment.class);
- action.execute("42", actionRequest, properties);
- }
-
- public void testPatchSetCreatedFull() throws IOException {
- ActionRequest actionRequest = createMock(ActionRequest.class);
-
- Set<Property> properties = Sets.newHashSet();
-
- Property propertyEventType = createMock(Property.class);
- expect(propertyEventType.getKey()).andReturn("event-type").anyTimes();
- expect(propertyEventType.getValue()).andReturn("patchset-created").anyTimes();
- properties.add(propertyEventType);
-
- Property propertySubject = createMock(Property.class);
- expect(propertySubject.getKey()).andReturn("subject").anyTimes();
- expect(propertySubject.getValue()).andReturn("Test-Change-Subject").anyTimes();
- properties.add(propertySubject);
-
- Property propertyChangeNumber = createMock(Property.class);
- expect(propertyChangeNumber.getKey()).andReturn("changeNumber").anyTimes();
- expect(propertyChangeNumber.getValue()).andReturn("4711").anyTimes();
- properties.add(propertyChangeNumber);
-
- Property propertySubmitterName = createMock(Property.class);
- expect(propertySubmitterName.getKey()).andReturn("uploaderName").anyTimes();
- expect(propertySubmitterName.getValue()).andReturn("John Doe").anyTimes();
- properties.add(propertySubmitterName);
-
- Property propertyChangeUrl = createMock(Property.class);
- expect(propertyChangeUrl.getKey()).andReturn("changeUrl").anyTimes();
- expect(propertyChangeUrl.getValue()).andReturn("http://example.org/change").anyTimes();
- properties.add(propertyChangeUrl);
-
- expect(its.createLinkForWebui("http://example.org/change", "http://example.org/change"))
- .andReturn("HtTp://ExAmPlE.OrG/ChAnGe");
-
- its.addComment(
- "176",
- "Change 4711 had a related patch set uploaded by "
- + "John Doe:\n"
- + "Test-Change-Subject\n"
- + "\n"
- + "HtTp://ExAmPlE.OrG/ChAnGe");
- replayMocks();
-
- Action action = injector.getInstance(AddStandardComment.class);
- action.execute("176", actionRequest, properties);
- }
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
-
- injector = Guice.createInjector(new TestModule());
- }
-
- private class TestModule extends FactoryModule {
- @Override
- protected void configure() {
- its = createMock(ItsFacade.class);
- bind(ItsFacade.class).toInstance(its);
- }
- }
-}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/action/AddVelocityCommentTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/action/AddVelocityCommentTest.java
deleted file mode 100644
index 61042a4..0000000
--- a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/action/AddVelocityCommentTest.java
+++ /dev/null
@@ -1,450 +0,0 @@
-// Copyright (C) 2013 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.its.base.workflow.action;
-
-import static org.easymock.EasyMock.anyObject;
-import static org.easymock.EasyMock.capture;
-import static org.easymock.EasyMock.eq;
-import static org.easymock.EasyMock.expect;
-
-import com.google.common.collect.Sets;
-import com.google.gerrit.extensions.config.FactoryModule;
-import com.google.gerrit.server.config.SitePath;
-import com.google.inject.Guice;
-import com.google.inject.Injector;
-import com.googlesource.gerrit.plugins.its.base.its.ItsFacade;
-import com.googlesource.gerrit.plugins.its.base.testutil.LoggingMockingTestCase;
-import com.googlesource.gerrit.plugins.its.base.workflow.ActionRequest;
-import com.googlesource.gerrit.plugins.its.base.workflow.Property;
-import com.googlesource.gerrit.plugins.its.base.workflow.action.AddVelocityComment.VelocityAdapterItsFacade;
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.Writer;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.UUID;
-import org.apache.velocity.VelocityContext;
-import org.apache.velocity.runtime.RuntimeInstance;
-import org.easymock.Capture;
-import org.easymock.EasyMock;
-import org.easymock.IAnswer;
-import org.eclipse.jgit.util.FileUtils;
-
-public class AddVelocityCommentTest extends LoggingMockingTestCase {
- private Injector injector;
-
- private Path sitePath;
- private ItsFacade its;
- private RuntimeInstance velocityRuntime;
-
- private boolean cleanupSitePath;
-
- public void testWarnNoTemplateNameGiven() throws IOException {
- ActionRequest actionRequest = createMock(ActionRequest.class);
- expect(actionRequest.getParameter(1)).andReturn("");
- replayMocks();
-
- AddVelocityComment addVelocityComment = createAddVelocityComment();
- addVelocityComment.execute("4711", actionRequest, new HashSet<>());
-
- assertLogMessageContains("No template name");
- }
-
- public void testInlinePlain() throws IOException {
- ActionRequest actionRequest = createMock(ActionRequest.class);
- expect(actionRequest.getParameter(1)).andReturn("inline");
- expect(actionRequest.getParameters()).andReturn(new String[] {"inline", "Simple-text"});
-
- IAnswer<Boolean> answer = new VelocityWriterFiller("Simple-text");
- expect(
- velocityRuntime.evaluate(
- (VelocityContext) anyObject(),
- (Writer) anyObject(),
- (String) anyObject(),
- eq("Simple-text")))
- .andAnswer(answer);
-
- its.addComment("4711", "Simple-text");
-
- replayMocks();
-
- AddVelocityComment addVelocityComment = createAddVelocityComment();
- addVelocityComment.execute("4711", actionRequest, new HashSet<>());
- }
-
- public void testInlineWithMultipleParameters() throws IOException {
- ActionRequest actionRequest = createMock(ActionRequest.class);
- expect(actionRequest.getParameter(1)).andReturn("inline");
- expect(actionRequest.getParameters()).andReturn(new String[] {"inline", "Param2", "Param3"});
-
- Set<Property> properties = Sets.newHashSet();
-
- IAnswer<Boolean> answer = new VelocityWriterFiller("Param2 Param3");
- expect(
- velocityRuntime.evaluate(
- (VelocityContext) anyObject(),
- (Writer) anyObject(),
- (String) anyObject(),
- eq("Param2 Param3")))
- .andAnswer(answer);
-
- its.addComment("4711", "Param2 Param3");
-
- replayMocks();
-
- AddVelocityComment addVelocityComment = createAddVelocityComment();
- addVelocityComment.execute("4711", actionRequest, properties);
- }
-
- public void testInlineWithSingleProperty() throws IOException {
- ActionRequest actionRequest = createMock(ActionRequest.class);
- expect(actionRequest.getParameter(1)).andReturn("inline");
- expect(actionRequest.getParameters()).andReturn(new String[] {"inline", "${subject}"});
-
- Set<Property> properties = Sets.newHashSet();
-
- Property propertySubject = createMock(Property.class);
- expect(propertySubject.getKey()).andReturn("subject").anyTimes();
- expect(propertySubject.getValue()).andReturn("Rosebud").anyTimes();
- properties.add(propertySubject);
-
- IAnswer<Boolean> answer = new VelocityWriterFiller("Rosebud");
- Capture<VelocityContext> contextCapture = createCapture();
- expect(
- velocityRuntime.evaluate(
- capture(contextCapture),
- (Writer) anyObject(),
- (String) anyObject(),
- eq("${subject}")))
- .andAnswer(answer);
-
- its.addComment("4711", "Rosebud");
-
- replayMocks();
-
- AddVelocityComment addVelocityComment = createAddVelocityComment();
- addVelocityComment.execute("4711", actionRequest, properties);
-
- VelocityContext context = contextCapture.getValue();
- assertEquals("Subject property of context did not match", "Rosebud", context.get("subject"));
- }
-
- public void testInlineWithUnusedProperty() throws IOException {
- ActionRequest actionRequest = createMock(ActionRequest.class);
- expect(actionRequest.getParameter(1)).andReturn("inline");
- expect(actionRequest.getParameters()).andReturn(new String[] {"inline", "Test"});
-
- Set<Property> properties = Sets.newHashSet();
-
- Property propertySubject = createMock(Property.class);
- expect(propertySubject.getKey()).andReturn("subject").anyTimes();
- expect(propertySubject.getValue()).andReturn("Rosebud").anyTimes();
- properties.add(propertySubject);
-
- IAnswer<Boolean> answer = new VelocityWriterFiller("Test");
- expect(
- velocityRuntime.evaluate(
- (VelocityContext) anyObject(),
- (Writer) anyObject(),
- (String) anyObject(),
- eq("Test")))
- .andAnswer(answer);
-
- its.addComment("4711", "Test");
-
- replayMocks();
-
- AddVelocityComment addVelocityComment = createAddVelocityComment();
- addVelocityComment.execute("4711", actionRequest, properties);
- }
-
- public void testInlineWithMultipleProperties() throws IOException {
- ActionRequest actionRequest = createMock(ActionRequest.class);
- expect(actionRequest.getParameter(1)).andReturn("inline");
- expect(actionRequest.getParameters())
- .andReturn(new String[] {"inline", "${subject}", "${reason}", "${subject}"});
-
- Set<Property> properties = Sets.newHashSet();
-
- Property propertySubject = createMock(Property.class);
- expect(propertySubject.getKey()).andReturn("subject").anyTimes();
- expect(propertySubject.getValue()).andReturn("Rosebud").anyTimes();
- properties.add(propertySubject);
-
- Property propertyReason = createMock(Property.class);
- expect(propertyReason.getKey()).andReturn("reason").anyTimes();
- expect(propertyReason.getValue()).andReturn("Life").anyTimes();
- properties.add(propertyReason);
-
- IAnswer<Boolean> answer = new VelocityWriterFiller("Rosebud Life Rosebud");
- Capture<VelocityContext> contextCapture = createCapture();
- expect(
- velocityRuntime.evaluate(
- capture(contextCapture),
- (Writer) anyObject(),
- (String) anyObject(),
- eq("${subject} ${reason} ${subject}")))
- .andAnswer(answer);
-
- its.addComment("4711", "Rosebud Life Rosebud");
-
- replayMocks();
-
- AddVelocityComment addVelocityComment = createAddVelocityComment();
- addVelocityComment.execute("4711", actionRequest, properties);
-
- VelocityContext context = contextCapture.getValue();
- assertEquals("Subject property of context did not match", "Rosebud", context.get("subject"));
- assertEquals("Reason property of context did not match", "Life", context.get("reason"));
- }
-
- public void testItsWrapperFormatLink1Parameter()
- throws IOException, SecurityException, IllegalArgumentException {
- ActionRequest actionRequest = createMock(ActionRequest.class);
- expect(actionRequest.getParameter(1)).andReturn("inline");
- expect(actionRequest.getParameters()).andReturn(new String[] {"inline", "Simple-Text"});
-
- IAnswer<Boolean> answer = new VelocityWriterFiller("Simple-Text");
- Capture<VelocityContext> contextCapture = createCapture();
- expect(
- velocityRuntime.evaluate(
- capture(contextCapture),
- (Writer) anyObject(),
- (String) anyObject(),
- eq("Simple-Text")))
- .andAnswer(answer);
-
- its.addComment("4711", "Simple-Text");
-
- expect(its.createLinkForWebui("http://www.example.org/", "http://www.example.org/"))
- .andReturn("Formatted Link");
-
- replayMocks();
-
- AddVelocityComment addVelocityComment = createAddVelocityComment();
- addVelocityComment.execute("4711", actionRequest, new HashSet<>());
-
- VelocityContext context = contextCapture.getValue();
- Object itsAdapterObj = context.get("its");
- assertNotNull("its property is null", itsAdapterObj);
- assertTrue(
- "Its is not a VelocityAdapterItsFacade instance",
- itsAdapterObj instanceof VelocityAdapterItsFacade);
- VelocityAdapterItsFacade itsAdapter = (VelocityAdapterItsFacade) itsAdapterObj;
- String formattedLink = itsAdapter.formatLink("http://www.example.org/");
- assertEquals("Result of formatLink does not match", "Formatted Link", formattedLink);
- }
-
- public void testItsWrapperFormatLink2Parameters()
- throws IOException, SecurityException, IllegalArgumentException {
- ActionRequest actionRequest = createMock(ActionRequest.class);
- expect(actionRequest.getParameter(1)).andReturn("inline");
- expect(actionRequest.getParameters()).andReturn(new String[] {"inline", "Simple-Text"});
-
- IAnswer<Boolean> answer = new VelocityWriterFiller("Simple-Text");
- Capture<VelocityContext> contextCapture = createCapture();
- expect(
- velocityRuntime.evaluate(
- capture(contextCapture),
- (Writer) anyObject(),
- (String) anyObject(),
- eq("Simple-Text")))
- .andAnswer(answer);
-
- its.addComment("4711", "Simple-Text");
-
- expect(its.createLinkForWebui("http://www.example.org/", "Caption"))
- .andReturn("Formatted Link");
-
- replayMocks();
-
- AddVelocityComment addVelocityComment = createAddVelocityComment();
- addVelocityComment.execute("4711", actionRequest, new HashSet<>());
-
- VelocityContext context = contextCapture.getValue();
- Object itsAdapterObj = context.get("its");
- assertNotNull("its property is null", itsAdapterObj);
- assertTrue(
- "Its is not a VelocityAdapterItsFacade instance",
- itsAdapterObj instanceof VelocityAdapterItsFacade);
- VelocityAdapterItsFacade itsAdapter = (VelocityAdapterItsFacade) itsAdapterObj;
- String formattedLink = itsAdapter.formatLink("http://www.example.org/", "Caption");
- assertEquals("Result of formatLink does not match", "Formatted Link", formattedLink);
- }
-
- public void testWarnTemplateNotFound() throws IOException {
- ActionRequest actionRequest = createMock(ActionRequest.class);
- expect(actionRequest.getParameter(1)).andReturn("non-existing-template");
-
- replayMocks();
-
- AddVelocityComment addVelocityComment = createAddVelocityComment();
- addVelocityComment.execute("4711", actionRequest, new HashSet<>());
-
- assertLogMessageContains("non-existing-template");
- }
-
- public void testTemplateSimple() throws IOException {
- ActionRequest actionRequest = createMock(ActionRequest.class);
- expect(actionRequest.getParameter(1)).andReturn("test-template");
-
- injectTestTemplate("Simple Test Template");
-
- IAnswer<Boolean> answer = new VelocityWriterFiller("Simple Test Template");
- expect(
- velocityRuntime.evaluate(
- (VelocityContext) anyObject(),
- (Writer) anyObject(),
- (String) anyObject(),
- eq("Simple Test Template")))
- .andAnswer(answer);
-
- its.addComment("4711", "Simple Test Template");
-
- replayMocks();
-
- AddVelocityComment addVelocityComment = createAddVelocityComment();
- addVelocityComment.execute("4711", actionRequest, new HashSet<>());
- }
-
- public void testTemplateMultipleParametersAndProperties() throws IOException {
- ActionRequest actionRequest = createMock(ActionRequest.class);
- expect(actionRequest.getParameter(1)).andReturn("test-template");
-
- Set<Property> properties = Sets.newHashSet();
-
- Property propertySubject = createMock(Property.class);
- expect(propertySubject.getKey()).andReturn("subject").anyTimes();
- expect(propertySubject.getValue()).andReturn("Rosebud").anyTimes();
- properties.add(propertySubject);
-
- Property propertyReason = createMock(Property.class);
- expect(propertyReason.getKey()).andReturn("reason").anyTimes();
- expect(propertyReason.getValue()).andReturn("Life").anyTimes();
- properties.add(propertyReason);
-
- injectTestTemplate(
- "Test Template with subject: ${subject}.\n" + "${reason} is the reason for ${subject}.");
-
- IAnswer<Boolean> answer =
- new VelocityWriterFiller(
- "Test Template with subject: Rosebud.\n" + "Life is the reason for Rosebud.");
- Capture<VelocityContext> contextCapture = createCapture();
- expect(
- velocityRuntime.evaluate(
- capture(contextCapture),
- (Writer) anyObject(),
- (String) anyObject(),
- eq(
- "Test Template with subject: ${subject}.\n"
- + "${reason} is the reason for ${subject}.")))
- .andAnswer(answer);
-
- its.addComment(
- "4711", "Test Template with subject: Rosebud.\n" + "Life is the reason for Rosebud.");
-
- replayMocks();
-
- AddVelocityComment addVelocityComment = createAddVelocityComment();
- addVelocityComment.execute("4711", actionRequest, properties);
-
- VelocityContext context = contextCapture.getValue();
- assertEquals("Subject property of context did not match", "Rosebud", context.get("subject"));
- assertEquals("Reason property of context did not match", "Life", context.get("reason"));
- }
-
- private AddVelocityComment createAddVelocityComment() {
- return injector.getInstance(AddVelocityComment.class);
- }
-
- private void injectTestTemplate(String template) throws IOException {
- File templateParentFile =
- new File(
- sitePath.toFile(), "etc" + File.separatorChar + "its" + File.separator + "templates");
- assertTrue(
- "Failed to create parent (" + templateParentFile + ") for " + "rule base",
- templateParentFile.mkdirs());
- File templateFile = new File(templateParentFile, "test-template.vm");
-
- try (FileWriter unbufferedWriter = new FileWriter(templateFile);
- BufferedWriter writer = new BufferedWriter(unbufferedWriter)) {
- writer.write(template);
- }
- }
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- cleanupSitePath = false;
- injector = Guice.createInjector(new TestModule());
- }
-
- @Override
- public void tearDown() throws Exception {
- if (cleanupSitePath) {
- if (Files.exists(sitePath)) {
- FileUtils.delete(sitePath.toFile(), FileUtils.RECURSIVE);
- }
- }
- super.tearDown();
- }
-
- private Path randomTargetPath() {
- return Paths.get("target", "random-name-" + UUID.randomUUID().toString());
- }
-
- private class TestModule extends FactoryModule {
- @Override
- protected void configure() {
- sitePath = randomTargetPath();
- assertFalse("sitePath already (" + sitePath + ") already exists", Files.exists(sitePath));
- cleanupSitePath = true;
-
- bind(Path.class).annotatedWith(SitePath.class).toInstance(sitePath);
-
- its = createMock(ItsFacade.class);
- bind(ItsFacade.class).toInstance(its);
-
- velocityRuntime = createMock(RuntimeInstance.class);
- bind(RuntimeInstance.class).toInstance(velocityRuntime);
- }
- }
-
- private class VelocityWriterFiller implements IAnswer<Boolean> {
- private final String fill;
- private final boolean returnValue;
-
- private VelocityWriterFiller(String fill, boolean returnValue) {
- this.fill = fill;
- this.returnValue = returnValue;
- }
-
- private VelocityWriterFiller(String fill) {
- this(fill, true);
- }
-
- @Override
- public Boolean answer() throws Throwable {
- Object[] arguments = EasyMock.getCurrentArguments();
- Writer writer = (Writer) arguments[1];
- writer.write(fill);
- return returnValue;
- }
- }
-}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/action/LogEventTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/action/LogEventTest.java
deleted file mode 100644
index 99fa2c3..0000000
--- a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/action/LogEventTest.java
+++ /dev/null
@@ -1,159 +0,0 @@
-// Copyright (C) 2013 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.its.base.workflow.action;
-
-import static org.easymock.EasyMock.expect;
-
-import com.google.common.collect.Sets;
-import com.google.gerrit.extensions.config.FactoryModule;
-import com.google.inject.Guice;
-import com.google.inject.Injector;
-import com.googlesource.gerrit.plugins.its.base.testutil.LoggingMockingTestCase;
-import com.googlesource.gerrit.plugins.its.base.workflow.ActionRequest;
-import com.googlesource.gerrit.plugins.its.base.workflow.Property;
-import java.io.IOException;
-import java.util.HashSet;
-import java.util.Set;
-import org.apache.log4j.Level;
-
-public class LogEventTest extends LoggingMockingTestCase {
- private Injector injector;
-
- public void testEmpty() throws IOException {
- ActionRequest actionRequest = createMock(ActionRequest.class);
- expect(actionRequest.getParameter(1)).andReturn("");
-
- replayMocks();
-
- LogEvent logEvent = createLogEvent();
- logEvent.execute("4711", actionRequest, new HashSet<>());
- }
-
- public void testLevelDefault() throws IOException {
- ActionRequest actionRequest = createMock(ActionRequest.class);
- expect(actionRequest.getParameter(1)).andReturn("");
-
- Set<Property> properties = Sets.newHashSet();
- properties.add(new PropertyMock("KeyA", "ValueA", "PropertyA"));
- replayMocks();
-
- LogEvent logEvent = createLogEvent();
- logEvent.execute("4711", actionRequest, properties);
-
- assertLogMessageContains("PropertyA", Level.INFO);
- }
-
- public void testLevelError() throws IOException {
- ActionRequest actionRequest = createMock(ActionRequest.class);
- expect(actionRequest.getParameter(1)).andReturn("error");
-
- Set<Property> properties = Sets.newHashSet();
- properties.add(new PropertyMock("KeyA", "ValueA", "PropertyA"));
- replayMocks();
-
- LogEvent logEvent = createLogEvent();
- logEvent.execute("4711", actionRequest, properties);
-
- assertLogMessageContains("PropertyA", Level.ERROR);
- }
-
- public void testLevelWarn() throws IOException {
- ActionRequest actionRequest = createMock(ActionRequest.class);
- expect(actionRequest.getParameter(1)).andReturn("warn");
-
- Set<Property> properties = Sets.newHashSet();
- properties.add(new PropertyMock("KeyA", "ValueA", "PropertyA"));
- replayMocks();
-
- LogEvent logEvent = createLogEvent();
- logEvent.execute("4711", actionRequest, properties);
-
- assertLogMessageContains("PropertyA", Level.WARN);
- }
-
- public void testLevelInfo() throws IOException {
- ActionRequest actionRequest = createMock(ActionRequest.class);
- expect(actionRequest.getParameter(1)).andReturn("info");
-
- Set<Property> properties = Sets.newHashSet();
- properties.add(new PropertyMock("KeyA", "ValueA", "PropertyA"));
- replayMocks();
-
- LogEvent logEvent = createLogEvent();
- logEvent.execute("4711", actionRequest, properties);
-
- assertLogMessageContains("PropertyA", Level.INFO);
- }
-
- public void testLevelDebug() throws IOException {
- ActionRequest actionRequest = createMock(ActionRequest.class);
- expect(actionRequest.getParameter(1)).andReturn("debug");
-
- Set<Property> properties = Sets.newHashSet();
- properties.add(new PropertyMock("KeyA", "ValueA", "PropertyA"));
- replayMocks();
-
- LogEvent logEvent = createLogEvent();
- logEvent.execute("4711", actionRequest, properties);
-
- assertLogMessageContains("PropertyA", Level.DEBUG);
- }
-
- public void testMultipleProperties() throws IOException {
- ActionRequest actionRequest = createMock(ActionRequest.class);
- expect(actionRequest.getParameter(1)).andReturn("info");
-
- Set<Property> properties = Sets.newHashSet();
- properties.add(new PropertyMock("KeyA", "ValueA", "PropertyA"));
- properties.add(new PropertyMock("KeyB", "ValueB", "PropertyB"));
- properties.add(new PropertyMock("KeyC", "ValueC", "PropertyC"));
- replayMocks();
-
- LogEvent logEvent = createLogEvent();
- logEvent.execute("4711", actionRequest, properties);
-
- assertLogMessageContains("PropertyA", Level.INFO);
- assertLogMessageContains("PropertyB", Level.INFO);
- assertLogMessageContains("PropertyC", Level.INFO);
- }
-
- private LogEvent createLogEvent() {
- return injector.getInstance(LogEvent.class);
- }
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- injector = Guice.createInjector(new TestModule());
- }
-
- private class TestModule extends FactoryModule {
- @Override
- protected void configure() {}
- }
-
- private class PropertyMock extends Property {
- private final String toString;
-
- public PropertyMock(String key, String value, String toString) {
- super(key, value);
- this.toString = toString;
- }
-
- @Override
- public String toString() {
- return toString;
- }
- }
-}