Merge "Remove '.js' from imports in typescript files"
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index 9c3c8b6..724436f 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -239,6 +239,12 @@
current user.
--
+[[submit-requirements]]
+--
+* `SUBMIT_REQUIREMENTS`: detailed result of the evaluated submit requirements
+ for this change.
+--
+
[[current-revision]]
--
* `CURRENT_REVISION`: describe the current revision (patch set)
@@ -6510,7 +6516,11 @@
entities.
|`requirements` |optional|
List of the link:rest-api-changes.html#requirement[requirements] to be met before this change
-can be submitted.
+can be submitted. This field is deprecated in favour of `submit_requirements`.
+|`submit_requirements` |optional|
+List of the link:#submit-requirement-result-info[SubmitRequirementResultInfo]
+containing the evaluated submit requirements for the change.
+Only set if link:#submit-requirements[`SUBMIT_REQUIREMENTS`] is requested.
|`labels` |optional|
The labels of the change as a map that maps the label names to
link:#label-info[LabelInfo] entries. +
@@ -8137,6 +8147,56 @@
the failure of the rule predicate.
|===========================
+[[submit-requirement-expression-info]]
+=== SubmitRequirementExpressionInfo
+The `SubmitRequirementExpressionInfo` describes the result of evaluating a
+single submit requirement expression, for example `label:code-review=+2`.
+
+[options="header",cols="1,6"]
+|===========================
+|Field Name |Description
+|`expression`|
+The submit requirement expression as a string, for example
+`branch:refs/heads/foo and label:verified=+1`.
+|`fulfilled`|
+True if the submit requirement is fulfilled for the change.
+|`passing_atoms`|
+A list of passing atoms as strings. For the above expression,
+`passing_atoms` can contain ["branch:refs/heads/foo"] if the branch predicate is
+fulfilled for the change.
+|`failing_atoms`|
+A list of failing atoms. This is similar to `passing_atoms` except that it
+contains the list of predicates that are not fulfilled for the change.
+|===========================
+
+[[submit-requirement-result-info]]
+=== SubmitRequirementResultInfo
+The `SubmitRequirementResultInfo` describes the result of evaluating a
+submit requirement on a change.
+
+[options="header",cols="1,^1,5"]
+|===========================
+|Field Name ||Description
+|`name`||
+The submit requirement name.
+|`description`|optional|
+Description of the submit requirement.
+|`status`||
+Status describing the result of evaluating the submit requirement. The status
+is one of (`SATISFIED`, `UNSATISFED`, `OVERRIDDEN`, `NOT_APPLICABLE`).
+|`applicability_expression_result`|optional|
+A link:#submit-requirement-expression-info[SubmitRequirementExpressionInfo]
+containing the result of evaluating the applicability expression. Not set if the
+submit requirement did not define an applicability expression.
+|`submittability_expression_result`||
+A link:#submit-requirement-expression-info[SubmitRequirementExpressionInfo]
+containing the result of evaluating the submittability expression.
+|`override_expression_result`|optional|
+A link:#submit-requirement-expression-info[SubmitRequirementExpressionInfo]
+containing the result of evaluating the override expression. Not set if the
+submit requirement did not define an override expression.
+|===========================
+
[[submitted-together-info]]
=== SubmittedTogetherInfo
The `SubmittedTogetherInfo` entity contains information about a
diff --git a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index fd78bd8..7ddf2ba 100644
--- a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -71,6 +71,7 @@
import com.google.gerrit.entities.PermissionRule.Action;
import com.google.gerrit.entities.Project;
import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.entities.SubmitRequirement;
import com.google.gerrit.extensions.api.GerritApi;
import com.google.gerrit.extensions.api.changes.ChangeApi;
import com.google.gerrit.extensions.api.changes.ReviewInput;
@@ -1595,6 +1596,14 @@
ObjectId.fromString(get(changeId, ListChangesOption.CURRENT_REVISION).currentRevision));
}
+ protected void configSubmitRequirement(
+ Project.NameKey project, SubmitRequirement submitRequirement) throws Exception {
+ try (ProjectConfigUpdate u = updateProject(project)) {
+ u.getConfig().upsertSubmitRequirement(submitRequirement);
+ u.save();
+ }
+ }
+
protected void configLabel(String label, LabelFunction func) throws Exception {
configLabel(label, func, ImmutableList.of());
}
diff --git a/java/com/google/gerrit/entities/SubmitRequirementExpressionResult.java b/java/com/google/gerrit/entities/SubmitRequirementExpressionResult.java
index 94c0e91..f7a883e 100644
--- a/java/com/google/gerrit/entities/SubmitRequirementExpressionResult.java
+++ b/java/com/google/gerrit/entities/SubmitRequirementExpressionResult.java
@@ -98,7 +98,7 @@
public abstract static class PredicateResult {
abstract ImmutableList<PredicateResult> childPredicateResults();
- abstract String predicateString();
+ public abstract String predicateString();
/** true if the predicate is passing for a given change. */
abstract boolean status();
diff --git a/java/com/google/gerrit/extensions/client/ListChangesOption.java b/java/com/google/gerrit/extensions/client/ListChangesOption.java
index 6071cc7..f1f7831 100644
--- a/java/com/google/gerrit/extensions/client/ListChangesOption.java
+++ b/java/com/google/gerrit/extensions/client/ListChangesOption.java
@@ -85,7 +85,10 @@
* Skip diffstat computation that compute the insertions field (number of lines inserted) and
* deletions field (number of lines deleted)
*/
- SKIP_DIFFSTAT(23);
+ SKIP_DIFFSTAT(23),
+
+ /** Include the evaluated submit requirements for the caller. */
+ SUBMIT_REQUIREMENTS(24);
private final int value;
diff --git a/java/com/google/gerrit/extensions/common/ChangeInfo.java b/java/com/google/gerrit/extensions/common/ChangeInfo.java
index 9e915f5..6afe8ac 100644
--- a/java/com/google/gerrit/extensions/common/ChangeInfo.java
+++ b/java/com/google/gerrit/extensions/common/ChangeInfo.java
@@ -112,6 +112,7 @@
public List<PluginDefinedInfo> plugins;
public Collection<TrackingIdInfo> trackingIds;
public Collection<LegacySubmitRequirementInfo> requirements;
+ public Collection<SubmitRequirementResultInfo> submitRequirements;
public ChangeInfo() {}
diff --git a/java/com/google/gerrit/extensions/common/SubmitRequirementExpressionInfo.java b/java/com/google/gerrit/extensions/common/SubmitRequirementExpressionInfo.java
new file mode 100644
index 0000000..4d1fce2
--- /dev/null
+++ b/java/com/google/gerrit/extensions/common/SubmitRequirementExpressionInfo.java
@@ -0,0 +1,39 @@
+// Copyright (C) 2021 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.google.gerrit.extensions.common;
+
+import java.util.List;
+
+/** Result of evaluating a single submit requirement expression. */
+public class SubmitRequirementExpressionInfo {
+
+ /** Submit requirement expression as a String. */
+ public String expression;
+
+ /** A boolean indicating if the expression is fulfilled on a change. */
+ public boolean fulfilled;
+
+ /**
+ * A list of all atoms that are passing, for example query "branch:refs/heads/foo and project:bar"
+ * has two atoms: ["branch:refs/heads/foo", "project:bar"].
+ */
+ public List<String> passingAtoms;
+
+ /**
+ * A list of all atoms that are failing, for example query "branch:refs/heads/foo and project:bar"
+ * has two atoms: ["branch:refs/heads/foo", "project:bar"].
+ */
+ public List<String> failingAtoms;
+}
diff --git a/java/com/google/gerrit/extensions/common/SubmitRequirementResultInfo.java b/java/com/google/gerrit/extensions/common/SubmitRequirementResultInfo.java
new file mode 100644
index 0000000..685e81a
--- /dev/null
+++ b/java/com/google/gerrit/extensions/common/SubmitRequirementResultInfo.java
@@ -0,0 +1,58 @@
+// Copyright (C) 2021 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.google.gerrit.extensions.common;
+
+/** Result of evaluating a submit requirement on a change. */
+public class SubmitRequirementResultInfo {
+ public enum Status {
+ /** Submit requirement is fulfilled. */
+ SATISFIED,
+
+ /**
+ * Submit requirement is not satisfied. Happens when {@code submittabilityExpressionResult} is
+ * not fulfilled.
+ */
+ UNSATISFIED,
+
+ /**
+ * Submit requirement is overridden. Happens when {@code overrideExpressionResult} is fulfilled.
+ */
+ OVERRIDDEN,
+
+ /**
+ * Submit requirement is not applicable for the change. Happens when {@code
+ * applicabilityExpressionResult} is not fulfilled.
+ */
+ NOT_APPLICABLE
+ }
+
+ /** Submit requirement name. */
+ public String name;
+
+ /** Submit requirement description. */
+ public String description;
+
+ /** Overall result (status) of evaluating this submit requirement. */
+ public Status status;
+
+ /** Result of evaluating the applicability expression. */
+ public SubmitRequirementExpressionInfo applicabilityExpressionResult;
+
+ /** Result of evaluating the submittability expression. */
+ public SubmitRequirementExpressionInfo submittabilityExpressionResult;
+
+ /** Result of evaluating the override expression. */
+ public SubmitRequirementExpressionInfo overrideExpressionResult;
+}
diff --git a/java/com/google/gerrit/pgm/util/BatchProgramModule.java b/java/com/google/gerrit/pgm/util/BatchProgramModule.java
index 30de2f5..4db657d 100644
--- a/java/com/google/gerrit/pgm/util/BatchProgramModule.java
+++ b/java/com/google/gerrit/pgm/util/BatchProgramModule.java
@@ -83,6 +83,8 @@
import com.google.gerrit.server.query.approval.ApprovalModule;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.ChangeIsVisibleToPredicate;
+import com.google.gerrit.server.query.change.ChangeQueryBuilder;
+import com.google.gerrit.server.query.change.ConflictsCacheImpl;
import com.google.gerrit.server.restapi.group.GroupModule;
import com.google.gerrit.server.rules.DefaultSubmitRule;
import com.google.gerrit.server.rules.IgnoreSelfApprovalRule;
@@ -174,6 +176,7 @@
modules.add(new NoteDbModule());
modules.add(AccountCacheImpl.module());
modules.add(ApprovalCacheImpl.module());
+ modules.add(ConflictsCacheImpl.module());
modules.add(DefaultPreferencesCacheImpl.module());
modules.add(GroupCacheImpl.module());
modules.add(GroupIncludeCacheImpl.module());
@@ -190,6 +193,10 @@
factory(ChangeIsVisibleToPredicate.Factory.class);
factory(ProjectState.Factory.class);
+ DynamicMap.mapOf(binder(), ChangeQueryBuilder.ChangeOperatorFactory.class);
+ DynamicMap.mapOf(binder(), ChangeQueryBuilder.ChangeHasOperandFactory.class);
+ DynamicMap.mapOf(binder(), ChangeQueryBuilder.ChangeIsOperandFactory.class);
+
// Submit rules
DynamicSet.setOf(binder(), SubmitRule.class);
factory(SubmitRuleEvaluator.Factory.class);
diff --git a/java/com/google/gerrit/server/change/ChangeJson.java b/java/com/google/gerrit/server/change/ChangeJson.java
index 029f231..e9c9946 100644
--- a/java/com/google/gerrit/server/change/ChangeJson.java
+++ b/java/com/google/gerrit/server/change/ChangeJson.java
@@ -31,6 +31,7 @@
import static com.google.gerrit.extensions.client.ListChangesOption.REVIEWER_UPDATES;
import static com.google.gerrit.extensions.client.ListChangesOption.SKIP_DIFFSTAT;
import static com.google.gerrit.extensions.client.ListChangesOption.SUBMITTABLE;
+import static com.google.gerrit.extensions.client.ListChangesOption.SUBMIT_REQUIREMENTS;
import static com.google.gerrit.extensions.client.ListChangesOption.TRACKING_IDS;
import static com.google.gerrit.server.ChangeMessagesUtil.createChangeMessageInfo;
import static com.google.gerrit.server.util.AttentionSetUtil.additionsOnly;
@@ -59,6 +60,11 @@
import com.google.gerrit.entities.RefNames;
import com.google.gerrit.entities.SubmitRecord;
import com.google.gerrit.entities.SubmitRecord.Status;
+import com.google.gerrit.entities.SubmitRequirement;
+import com.google.gerrit.entities.SubmitRequirementExpression;
+import com.google.gerrit.entities.SubmitRequirementExpressionResult;
+import com.google.gerrit.entities.SubmitRequirementExpressionResult.PredicateResult;
+import com.google.gerrit.entities.SubmitRequirementResult;
import com.google.gerrit.entities.SubmitTypeRecord;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.changes.FixInput;
@@ -75,6 +81,8 @@
import com.google.gerrit.extensions.common.ProblemInfo;
import com.google.gerrit.extensions.common.ReviewerUpdateInfo;
import com.google.gerrit.extensions.common.RevisionInfo;
+import com.google.gerrit.extensions.common.SubmitRequirementExpressionInfo;
+import com.google.gerrit.extensions.common.SubmitRequirementResultInfo;
import com.google.gerrit.extensions.common.TrackingIdInfo;
import com.google.gerrit.extensions.restapi.Url;
import com.google.gerrit.index.RefState;
@@ -362,11 +370,54 @@
return reqInfos;
}
+ private static Collection<SubmitRequirementResultInfo> submitRequirementsFor(ChangeData cd) {
+ Collection<SubmitRequirementResultInfo> reqInfos = new ArrayList<>();
+ Map<SubmitRequirement, SubmitRequirementResult> requirements = cd.submitRequirements();
+ for (Map.Entry<SubmitRequirement, SubmitRequirementResult> entry : requirements.entrySet()) {
+ reqInfos.add(submitRequirementToInfo(entry.getKey(), entry.getValue()));
+ }
+ return reqInfos;
+ }
+
private static LegacySubmitRequirementInfo requirementToInfo(
LegacySubmitRequirement req, Status status) {
return new LegacySubmitRequirementInfo(status.name(), req.fallbackText(), req.type());
}
+ private static SubmitRequirementResultInfo submitRequirementToInfo(
+ SubmitRequirement req, SubmitRequirementResult result) {
+ SubmitRequirementResultInfo info = new SubmitRequirementResultInfo();
+ info.name = req.name();
+ info.description = req.description().orElse(null);
+ if (req.applicabilityExpression().isPresent()) {
+ info.applicabilityExpressionResult =
+ submitRequirementExpressionToInfo(
+ req.applicabilityExpression().get(), result.applicabilityExpressionResult().get());
+ }
+ if (req.overrideExpression().isPresent()) {
+ info.overrideExpressionResult =
+ submitRequirementExpressionToInfo(
+ req.overrideExpression().get(), result.overrideExpressionResult().get());
+ }
+ info.submittabilityExpressionResult =
+ submitRequirementExpressionToInfo(
+ req.submittabilityExpression(), result.submittabilityExpressionResult());
+ info.status = SubmitRequirementResultInfo.Status.valueOf(result.status().toString());
+ return info;
+ }
+
+ private static SubmitRequirementExpressionInfo submitRequirementExpressionToInfo(
+ SubmitRequirementExpression expression, SubmitRequirementExpressionResult result) {
+ SubmitRequirementExpressionInfo info = new SubmitRequirementExpressionInfo();
+ info.expression = expression.expressionString();
+ info.fulfilled = result.status().equals(SubmitRequirementExpressionResult.Status.PASS);
+ info.passingAtoms =
+ result.getPassingAtoms().stream().map(PredicateResult::predicateString).collect(toList());
+ info.failingAtoms =
+ result.getFailingAtoms().stream().map(PredicateResult::predicateString).collect(toList());
+ return info;
+ }
+
private static void finish(ChangeInfo info) {
info.id =
Joiner.on('~')
@@ -612,6 +663,9 @@
out.labels = labelsJson.labelsFor(accountLoader, cd, has(LABELS), has(DETAILED_LABELS));
out.requirements = requirementsFor(cd);
+ if (has(SUBMIT_REQUIREMENTS)) {
+ out.submitRequirements = submitRequirementsFor(cd);
+ }
if (out.labels != null && has(DETAILED_LABELS)) {
// If limited to specific patch sets but not the current patch set, don't
diff --git a/java/com/google/gerrit/server/project/ProjectConfig.java b/java/com/google/gerrit/server/project/ProjectConfig.java
index 9f898d9..3253282 100644
--- a/java/com/google/gerrit/server/project/ProjectConfig.java
+++ b/java/com/google/gerrit/server/project/ProjectConfig.java
@@ -529,6 +529,10 @@
return submitRequirementSections;
}
+ public void upsertSubmitRequirement(SubmitRequirement requirement) {
+ submitRequirementSections.put(requirement.name(), requirement);
+ }
+
/** Adds or replaces the given {@link LabelType} in this config. */
public void upsertLabelType(LabelType labelType) {
labelSections.put(labelType.getName(), labelType);
diff --git a/java/com/google/gerrit/server/project/ProjectState.java b/java/com/google/gerrit/server/project/ProjectState.java
index 03d38b3..4569027 100644
--- a/java/com/google/gerrit/server/project/ProjectState.java
+++ b/java/com/google/gerrit/server/project/ProjectState.java
@@ -22,6 +22,7 @@
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.entities.AccessSection;
@@ -37,6 +38,7 @@
import com.google.gerrit.entities.PermissionRule;
import com.google.gerrit.entities.Project;
import com.google.gerrit.entities.StoredCommentLinkInfo;
+import com.google.gerrit.entities.SubmitRequirement;
import com.google.gerrit.entities.SubscribeSection;
import com.google.gerrit.extensions.api.projects.CommentLinkInfo;
import com.google.gerrit.extensions.client.SubmitType;
@@ -392,6 +394,21 @@
return false;
}
+ /** Get all submit requirements for a project, including those from parent projects. */
+ public Map<String, SubmitRequirement> getSubmitRequirements() {
+ Map<String, SubmitRequirement> requirements = new LinkedHashMap<>();
+ for (ProjectState s : treeInOrder()) {
+ for (SubmitRequirement requirement : s.getConfig().getSubmitRequirementSections().values()) {
+ String lowerName = requirement.name().toLowerCase();
+ SubmitRequirement old = requirements.get(lowerName);
+ if (old == null || old.allowOverrideInChildProjects()) {
+ requirements.put(lowerName, requirement);
+ }
+ }
+ }
+ return ImmutableMap.copyOf(requirements);
+ }
+
/** All available label types. */
public LabelTypes getLabelTypes() {
Map<String, LabelType> types = new LinkedHashMap<>();
diff --git a/java/com/google/gerrit/server/query/change/ChangeData.java b/java/com/google/gerrit/server/query/change/ChangeData.java
index 6f8b097..ac28342 100644
--- a/java/com/google/gerrit/server/query/change/ChangeData.java
+++ b/java/com/google/gerrit/server/query/change/ChangeData.java
@@ -53,6 +53,8 @@
import com.google.gerrit.entities.RefNames;
import com.google.gerrit.entities.RobotComment;
import com.google.gerrit.entities.SubmitRecord;
+import com.google.gerrit.entities.SubmitRequirement;
+import com.google.gerrit.entities.SubmitRequirementResult;
import com.google.gerrit.entities.SubmitTypeRecord;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.restapi.BadRequestException;
@@ -86,6 +88,7 @@
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
+import com.google.gerrit.server.project.SubmitRequirementsEvaluator;
import com.google.gerrit.server.project.SubmitRuleEvaluator;
import com.google.gerrit.server.project.SubmitRuleOptions;
import com.google.gerrit.server.util.time.TimeUtil;
@@ -265,7 +268,7 @@
ChangeData cd =
new ChangeData(
null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, project, id, null, null);
+ null, null, project, id, null, null);
cd.currentPatchSet =
PatchSet.builder()
.id(PatchSet.id(id, currentPatchSetId))
@@ -291,6 +294,7 @@
private final ProjectCache projectCache;
private final TrackingFooters trackingFooters;
private final PureRevert pureRevert;
+ private final SubmitRequirementsEvaluator submitRequirementsEvaluator;
private final SubmitRuleEvaluator.Factory submitRuleEvaluatorFactory;
// Required assisted injected fields.
@@ -302,6 +306,8 @@
private final Map<SubmitRuleOptions, List<SubmitRecord>> submitRecords =
Maps.newLinkedHashMapWithExpectedSize(1);
+ private Map<SubmitRequirement, SubmitRequirementResult> submitRequirements;
+
private StorageConstraint storageConstraint = StorageConstraint.NOTEDB_ONLY;
private Change change;
private ChangeNotes notes;
@@ -365,6 +371,7 @@
ProjectCache projectCache,
TrackingFooters trackingFooters,
PureRevert pureRevert,
+ SubmitRequirementsEvaluator submitRequirementsEvaluator,
SubmitRuleEvaluator.Factory submitRuleEvaluatorFactory,
@Assisted Project.NameKey project,
@Assisted Change.Id id,
@@ -384,6 +391,7 @@
this.starredChangesUtil = starredChangesUtil;
this.trackingFooters = trackingFooters;
this.pureRevert = pureRevert;
+ this.submitRequirementsEvaluator = submitRequirementsEvaluator;
this.submitRuleEvaluatorFactory = submitRuleEvaluatorFactory;
this.project = project;
@@ -920,6 +928,21 @@
return messages;
}
+ /** Get all submit requirements for this change, including those from parent projects. */
+ public Map<SubmitRequirement, SubmitRequirementResult> submitRequirements() {
+ if (submitRequirements == null) {
+ ProjectState state = projectCache.get(project()).orElseThrow(illegalState(project()));
+ Map<String, SubmitRequirement> requirements = state.getSubmitRequirements();
+ ImmutableMap.Builder<SubmitRequirement, SubmitRequirementResult> result =
+ ImmutableMap.builderWithExpectedSize(requirements.size());
+ for (SubmitRequirement requirement : requirements.values()) {
+ result.put(requirement, submitRequirementsEvaluator.evaluate(requirement, this));
+ }
+ submitRequirements = result.build();
+ }
+ return submitRequirements;
+ }
+
public List<SubmitRecord> submitRecords(SubmitRuleOptions options) {
// If the change is not submitted yet, 'strict' and 'lenient' both have the same result. If the
// change is submitted, SubmitRecord requested with 'strict' will contain just a single entry
diff --git a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
index 529ce73..22a727a 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
@@ -100,6 +100,8 @@
import com.google.gerrit.entities.Permission;
import com.google.gerrit.entities.Project;
import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.entities.SubmitRequirement;
+import com.google.gerrit.entities.SubmitRequirementExpression;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.annotations.Exports;
import com.google.gerrit.extensions.api.accounts.DeleteDraftCommentsInput;
@@ -142,6 +144,8 @@
import com.google.gerrit.extensions.common.GitPerson;
import com.google.gerrit.extensions.common.LabelInfo;
import com.google.gerrit.extensions.common.RevisionInfo;
+import com.google.gerrit.extensions.common.SubmitRequirementResultInfo;
+import com.google.gerrit.extensions.common.SubmitRequirementResultInfo.Status;
import com.google.gerrit.extensions.common.TrackingIdInfo;
import com.google.gerrit.extensions.events.WorkInProgressStateChangedListener;
import com.google.gerrit.extensions.restapi.AuthException;
@@ -3990,6 +3994,192 @@
}
@Test
+ public void submitRequirementIsSatisfied_whenSubmittabilityExpressionIsFulfilled()
+ throws Exception {
+ configSubmitRequirement(
+ project,
+ SubmitRequirement.builder()
+ .setName("code-review")
+ .setSubmittabilityExpression(SubmitRequirementExpression.create("label:code-review=+2"))
+ .setAllowOverrideInChildProjects(false)
+ .build());
+ configSubmitRequirement(
+ project,
+ SubmitRequirement.builder()
+ .setName("verified")
+ .setSubmittabilityExpression(SubmitRequirementExpression.create("label:verified=+1"))
+ .setAllowOverrideInChildProjects(false)
+ .build());
+
+ PushOneCommit.Result r = createChange();
+ String changeId = r.getChangeId();
+
+ ChangeInfo change = gApi.changes().id(changeId).get();
+ assertThat(change.submitRequirements).hasSize(2);
+ assertSubmitRequirementStatus(change.submitRequirements, "code-review", Status.UNSATISFIED);
+ assertSubmitRequirementStatus(change.submitRequirements, "verified", Status.UNSATISFIED);
+
+ voteLabel(changeId, "code-review", 2);
+
+ change = gApi.changes().id(changeId).get();
+ assertThat(change.submitRequirements).hasSize(2);
+ assertSubmitRequirementStatus(change.submitRequirements, "code-review", Status.SATISFIED);
+ assertSubmitRequirementStatus(change.submitRequirements, "verified", Status.UNSATISFIED);
+ }
+
+ @Test
+ public void submitRequirementIsNotApplicable_whenApplicabilityExpressionIsNotFulfilled()
+ throws Exception {
+ configSubmitRequirement(
+ project,
+ SubmitRequirement.builder()
+ .setName("code-review")
+ .setApplicabilityExpression(SubmitRequirementExpression.of("project:foo"))
+ .setSubmittabilityExpression(SubmitRequirementExpression.create("label:code-review=+2"))
+ .setAllowOverrideInChildProjects(false)
+ .build());
+
+ PushOneCommit.Result r = createChange();
+ String changeId = r.getChangeId();
+
+ ChangeInfo change = gApi.changes().id(changeId).get();
+ assertThat(change.submitRequirements).hasSize(1);
+ assertSubmitRequirementStatus(change.submitRequirements, "code-review", Status.NOT_APPLICABLE);
+ }
+
+ @Test
+ public void submitRequirementIsOverridden_whenOverrideExpressionIsFulfilled() throws Exception {
+ configLabel("build-cop-override", LabelFunction.MAX_WITH_BLOCK);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(
+ allowLabel("build-cop-override")
+ .ref("refs/heads/master")
+ .group(REGISTERED_USERS)
+ .range(-1, 1))
+ .update();
+
+ configSubmitRequirement(
+ project,
+ SubmitRequirement.builder()
+ .setName("code-review")
+ .setSubmittabilityExpression(SubmitRequirementExpression.create("label:code-review=+2"))
+ .setOverrideExpression(SubmitRequirementExpression.of("label:build-cop-override=+1"))
+ .setAllowOverrideInChildProjects(false)
+ .build());
+
+ PushOneCommit.Result r = createChange();
+ String changeId = r.getChangeId();
+ ChangeInfo change = gApi.changes().id(changeId).get();
+ assertThat(change.submitRequirements).hasSize(1);
+ assertSubmitRequirementStatus(change.submitRequirements, "code-review", Status.UNSATISFIED);
+
+ voteLabel(changeId, "build-cop-override", 1);
+
+ change = gApi.changes().id(changeId).get();
+ assertThat(change.submitRequirements).hasSize(1);
+ assertSubmitRequirementStatus(change.submitRequirements, "code-review", Status.OVERRIDDEN);
+ }
+
+ @Test
+ public void submitRequirement_overriddenInChildProject() throws Exception {
+ configSubmitRequirement(
+ allProjects,
+ SubmitRequirement.builder()
+ .setName("code-review")
+ .setSubmittabilityExpression(SubmitRequirementExpression.create("label:code-review=+1"))
+ .setOverrideExpression(SubmitRequirementExpression.of("label:build-cop-override=+1"))
+ .setAllowOverrideInChildProjects(true)
+ .build());
+
+ // Override submit requirement in child project (requires code-review=+2 instead of +1)
+ configSubmitRequirement(
+ project,
+ SubmitRequirement.builder()
+ .setName("code-review")
+ .setSubmittabilityExpression(SubmitRequirementExpression.create("label:code-review=+2"))
+ .setOverrideExpression(SubmitRequirementExpression.of("label:build-cop-override=+1"))
+ .setAllowOverrideInChildProjects(false)
+ .build());
+
+ PushOneCommit.Result r = createChange();
+ String changeId = r.getChangeId();
+ ChangeInfo change = gApi.changes().id(changeId).get();
+ assertThat(change.submitRequirements).hasSize(1);
+ assertSubmitRequirementStatus(change.submitRequirements, "code-review", Status.UNSATISFIED);
+
+ voteLabel(changeId, "code-review", 1);
+ change = gApi.changes().id(changeId).get();
+ assertThat(change.submitRequirements).hasSize(1);
+ assertSubmitRequirementStatus(change.submitRequirements, "code-review", Status.UNSATISFIED);
+
+ voteLabel(changeId, "code-review", 2);
+ change = gApi.changes().id(changeId).get();
+ assertThat(change.submitRequirements).hasSize(1);
+ assertSubmitRequirementStatus(change.submitRequirements, "code-review", Status.SATISFIED);
+ }
+
+ @Test
+ public void submitRequirement_inheritedFromParentProject() throws Exception {
+ configSubmitRequirement(
+ allProjects,
+ SubmitRequirement.builder()
+ .setName("code-review")
+ .setSubmittabilityExpression(SubmitRequirementExpression.create("label:code-review=+1"))
+ .setOverrideExpression(SubmitRequirementExpression.of("label:build-cop-override=+1"))
+ .setAllowOverrideInChildProjects(false)
+ .build());
+
+ PushOneCommit.Result r = createChange();
+ String changeId = r.getChangeId();
+ ChangeInfo change = gApi.changes().id(changeId).get();
+ assertThat(change.submitRequirements).hasSize(1);
+ assertSubmitRequirementStatus(change.submitRequirements, "code-review", Status.UNSATISFIED);
+
+ voteLabel(changeId, "code-review", 1);
+ change = gApi.changes().id(changeId).get();
+ assertThat(change.submitRequirements).hasSize(1);
+ assertSubmitRequirementStatus(change.submitRequirements, "code-review", Status.SATISFIED);
+ }
+
+ @Test
+ public void submitRequirement_ignoredInChildProject_ifParentDoesNotAllowOverride()
+ throws Exception {
+ configSubmitRequirement(
+ allProjects,
+ SubmitRequirement.builder()
+ .setName("code-review")
+ .setSubmittabilityExpression(SubmitRequirementExpression.create("label:code-review=+1"))
+ .setOverrideExpression(SubmitRequirementExpression.of("label:build-cop-override=+1"))
+ .setAllowOverrideInChildProjects(false)
+ .build());
+
+ // Override submit requirement in child project (requires code-review=+2 instead of +1).
+ // Will have no effect since parent does not allow override.
+ configSubmitRequirement(
+ project,
+ SubmitRequirement.builder()
+ .setName("code-review")
+ .setSubmittabilityExpression(SubmitRequirementExpression.create("label:code-review=+2"))
+ .setOverrideExpression(SubmitRequirementExpression.of("label:build-cop-override=+1"))
+ .setAllowOverrideInChildProjects(false)
+ .build());
+
+ PushOneCommit.Result r = createChange();
+ String changeId = r.getChangeId();
+ ChangeInfo change = gApi.changes().id(changeId).get();
+ assertThat(change.submitRequirements).hasSize(1);
+ assertSubmitRequirementStatus(change.submitRequirements, "code-review", Status.UNSATISFIED);
+
+ voteLabel(changeId, "code-review", 1);
+ change = gApi.changes().id(changeId).get();
+ assertThat(change.submitRequirements).hasSize(1);
+ // +1 was enough to fulfill the requirement: override in child project was ignored
+ assertSubmitRequirementStatus(change.submitRequirements, "code-review", Status.SATISFIED);
+ }
+
+ @Test
public void fourByteEmoji() throws Exception {
// U+1F601 GRINNING FACE WITH SMILING EYES
String smile = new String(Character.toChars(0x1f601));
@@ -4630,4 +4820,21 @@
event.getChange().workInProgress != null ? event.getChange().workInProgress : false;
}
}
+
+ private void voteLabel(String changeId, String labelName, int score) throws RestApiException {
+ gApi.changes().id(changeId).current().review(new ReviewInput().label(labelName, score));
+ }
+
+ private void assertSubmitRequirementStatus(
+ Collection<SubmitRequirementResultInfo> results,
+ String requirementName,
+ SubmitRequirementResultInfo.Status status) {
+ for (SubmitRequirementResultInfo result : results) {
+ if (result.name.equals(requirementName) && result.status == status) {
+ return;
+ }
+ }
+ throw new AssertionError(
+ "Could not find submit requirement " + requirementName + " with status " + status);
+ }
}
diff --git a/polygerrit-ui/app/utils/safe-types-util_test.js b/polygerrit-ui/app/utils/safe-types-util_test.ts
similarity index 83%
rename from polygerrit-ui/app/utils/safe-types-util_test.js
rename to polygerrit-ui/app/utils/safe-types-util_test.ts
index e3968d0..03253e0 100644
--- a/polygerrit-ui/app/utils/safe-types-util_test.js
+++ b/polygerrit-ui/app/utils/safe-types-util_test.ts
@@ -15,12 +15,12 @@
* limitations under the License.
*/
-import '../test/common-test-setup-karma.js';
-import {safeTypesBridge, _testOnly_SafeUrl} from './safe-types-util.js';
+import '../test/common-test-setup-karma';
+import {safeTypesBridge, _testOnly_SafeUrl} from './safe-types-util';
suite('safe-types-util tests', () => {
test('SafeUrl accepts valid urls', () => {
- function accepts(url) {
+ function accepts(url: string) {
const safeUrl = new _testOnly_SafeUrl(url);
assert.isOk(safeUrl);
assert.equal(url, safeUrl.toString());
@@ -35,8 +35,10 @@
});
test('SafeUrl rejects invalid urls', () => {
- function rejects(url) {
- assert.throws(() => { new _testOnly_SafeUrl(url); });
+ function rejects(url: string) {
+ assert.throws(() => {
+ new _testOnly_SafeUrl(url);
+ });
}
rejects('javascript://alert("evil");');
rejects('ftp:example.com');
@@ -44,13 +46,14 @@
});
suite('safeTypesBridge', () => {
- function acceptsString(value, type) {
- assert.equal(safeTypesBridge(value, type),
- value);
+ function acceptsString(value: string, type: string) {
+ assert.equal(safeTypesBridge(value, type), value);
}
- function rejects(value, type) {
- assert.throws(() => { safeTypesBridge(value, type); });
+ function rejects(value: unknown, type: string) {
+ assert.throws(() => {
+ safeTypesBridge(value, type);
+ });
}
test('accepts valid URL strings', () => {