Create a label config for copying all scores if list of files is the same
This change allows creating new labels or setting to an old label a new label config: copyAllScoresIfListOfFilesDidNotChange. Labels that have this config set to true will copy all scores if the list of files did not change. Renames count as the same file (= no file change); as long as there are no explicit additions or deletions to the list of files, the scores will be copied to the next patch-set. Change-Id: I0051ea1236e6bd50a69455d799ce27081091d21a
This commit is contained in:
		@@ -283,6 +283,22 @@ forward when a new patch set is uploaded. This can be used to enable
 | 
			
		||||
sticky approvals, reducing turn-around for trivial cleanups prior to
 | 
			
		||||
submitting a change. Defaults to false.
 | 
			
		||||
 | 
			
		||||
[[label_copyAllScoresIfListOfFilesDidNotChange]]
 | 
			
		||||
=== `label.Label-Name.copyAllScoresIfListOfFilesDidNotChange`
 | 
			
		||||
 | 
			
		||||
This policy is useful if you don't want to trigger CI or human
 | 
			
		||||
verification again if the list of files didn't change.
 | 
			
		||||
 | 
			
		||||
If true, all scores for the label are copied forward when a new
 | 
			
		||||
patch-set is uploaded that has the same list of files as the previous
 | 
			
		||||
patch-set.
 | 
			
		||||
 | 
			
		||||
Renames are considered the same file when computing whether new files
 | 
			
		||||
were added or old files were deleted. Hence, if there are only renames,
 | 
			
		||||
scores will still be copied over.
 | 
			
		||||
 | 
			
		||||
Defaults to false.
 | 
			
		||||
 | 
			
		||||
[[label_copyAllScoresOnMergeFirstParentUpdate]]
 | 
			
		||||
=== `label.Label-Name.copyAllScoresOnMergeFirstParentUpdate`
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -3971,6 +3971,9 @@ copyAllScoresIfNoCodeChange] is set on the label.
 | 
			
		||||
|`copy_all_scores_on_trivial_rebase`|`false` if not set|
 | 
			
		||||
Whether link:config-labels.html#label_copyAllScoresOnTrivialRebase[
 | 
			
		||||
copyAllScoresOnTrivialRebase] is set on the label.
 | 
			
		||||
|`copy_all_scores_if_list_of_files_did_not_change`|`false` if not set|
 | 
			
		||||
Whether link:config-labels.html#label_copyAllScoresIfListOfFilesDidNotChange[
 | 
			
		||||
copyAllScoresIfListOfFilesDidNotChange] is set on the label.
 | 
			
		||||
|`copy_all_scores_on_merge_first_parent_update`|`false` if not set|
 | 
			
		||||
Whether link:config-labels.html#label_copyAllScoresOnMergeFirstParentUpdate[
 | 
			
		||||
copyAllScoresOnMergeFirstParentUpdate] is set on the label.
 | 
			
		||||
@@ -4038,6 +4041,9 @@ copyAllScoresIfNoCodeChange] is set on the label.
 | 
			
		||||
|`copy_all_scores_on_trivial_rebase`|optional|
 | 
			
		||||
Whether link:config-labels.html#label_copyAllScoresOnTrivialRebase[
 | 
			
		||||
copyAllScoresOnTrivialRebase] is set on the label.
 | 
			
		||||
|`copy_all_scores_if_list_of_files_did_not_change`|optional|
 | 
			
		||||
Whether link:config-labels.html#label_copyAllScoresIfListOfFilesDidNotChange[
 | 
			
		||||
copyAllScoresIfListOfFilesDidNotChange] is set on the label.
 | 
			
		||||
|`copy_all_scores_on_merge_first_parent_update`|optional|
 | 
			
		||||
Whether link:config-labels.html#label_copyAllScoresOnMergeFirstParentUpdate[
 | 
			
		||||
copyAllScoresOnMergeFirstParentUpdate] is set on the label.
 | 
			
		||||
 
 | 
			
		||||
@@ -29,6 +29,7 @@ import java.util.List;
 | 
			
		||||
public abstract class LabelType {
 | 
			
		||||
  public static final boolean DEF_ALLOW_POST_SUBMIT = true;
 | 
			
		||||
  public static final boolean DEF_CAN_OVERRIDE = true;
 | 
			
		||||
  public static final boolean DEF_COPY_ALL_SCORES_IF_LIST_OF_FILES_DID_NOT_CHANGE = false;
 | 
			
		||||
  public static final boolean DEF_COPY_ALL_SCORES_IF_NO_CHANGE = true;
 | 
			
		||||
  public static final boolean DEF_COPY_ALL_SCORES_IF_NO_CODE_CHANGE = false;
 | 
			
		||||
  public static final boolean DEF_COPY_ALL_SCORES_ON_TRIVIAL_REBASE = false;
 | 
			
		||||
@@ -101,6 +102,8 @@ public abstract class LabelType {
 | 
			
		||||
 | 
			
		||||
  public abstract boolean isCopyMaxScore();
 | 
			
		||||
 | 
			
		||||
  public abstract boolean isCopyAllScoresIfListOfFilesDidNotChange();
 | 
			
		||||
 | 
			
		||||
  public abstract boolean isCopyAllScoresOnMergeFirstParentUpdate();
 | 
			
		||||
 | 
			
		||||
  public abstract boolean isCopyAllScoresOnTrivialRebase();
 | 
			
		||||
@@ -143,6 +146,8 @@ public abstract class LabelType {
 | 
			
		||||
        .setMaxNegative(Short.MIN_VALUE)
 | 
			
		||||
        .setMaxPositive(Short.MAX_VALUE)
 | 
			
		||||
        .setCanOverride(DEF_CAN_OVERRIDE)
 | 
			
		||||
        .setCopyAllScoresIfListOfFilesDidNotChange(
 | 
			
		||||
            DEF_COPY_ALL_SCORES_IF_LIST_OF_FILES_DID_NOT_CHANGE)
 | 
			
		||||
        .setCopyAllScoresIfNoChange(DEF_COPY_ALL_SCORES_IF_NO_CHANGE)
 | 
			
		||||
        .setCopyAllScoresIfNoCodeChange(DEF_COPY_ALL_SCORES_IF_NO_CODE_CHANGE)
 | 
			
		||||
        .setCopyAllScoresOnTrivialRebase(DEF_COPY_ALL_SCORES_ON_TRIVIAL_REBASE)
 | 
			
		||||
@@ -238,6 +243,9 @@ public abstract class LabelType {
 | 
			
		||||
 | 
			
		||||
    public abstract Builder setCopyMaxScore(boolean copyMaxScore);
 | 
			
		||||
 | 
			
		||||
    public abstract Builder setCopyAllScoresIfListOfFilesDidNotChange(
 | 
			
		||||
        boolean copyAllScoresIfListOfFilesDidNotChange);
 | 
			
		||||
 | 
			
		||||
    public abstract Builder setCopyAllScoresOnMergeFirstParentUpdate(
 | 
			
		||||
        boolean copyAllScoresOnMergeFirstParentUpdate);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,7 @@ public class LabelDefinitionInfo {
 | 
			
		||||
  public Boolean copyAnyScore;
 | 
			
		||||
  public Boolean copyMinScore;
 | 
			
		||||
  public Boolean copyMaxScore;
 | 
			
		||||
  public Boolean copyAllScoresIfListOfFilesDidNotChange;
 | 
			
		||||
  public Boolean copyAllScoresIfNoChange;
 | 
			
		||||
  public Boolean copyAllScoresIfNoCodeChange;
 | 
			
		||||
  public Boolean copyAllScoresOnTrivialRebase;
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,7 @@ public class LabelDefinitionInput extends InputWithCommitMessage {
 | 
			
		||||
  public Boolean copyAnyScore;
 | 
			
		||||
  public Boolean copyMinScore;
 | 
			
		||||
  public Boolean copyMaxScore;
 | 
			
		||||
  public Boolean copyAllScoresIfListOfFilesDidNotChange;
 | 
			
		||||
  public Boolean copyAllScoresIfNoChange;
 | 
			
		||||
  public Boolean copyAllScoresIfNoCodeChange;
 | 
			
		||||
  public Boolean copyAllScoresOnTrivialRebase;
 | 
			
		||||
 
 | 
			
		||||
@@ -25,15 +25,22 @@ import com.google.common.flogger.FluentLogger;
 | 
			
		||||
import com.google.gerrit.common.Nullable;
 | 
			
		||||
import com.google.gerrit.entities.Account;
 | 
			
		||||
import com.google.gerrit.entities.LabelType;
 | 
			
		||||
import com.google.gerrit.entities.Patch.ChangeType;
 | 
			
		||||
import com.google.gerrit.entities.PatchSet;
 | 
			
		||||
import com.google.gerrit.entities.PatchSetApproval;
 | 
			
		||||
import com.google.gerrit.exceptions.StorageException;
 | 
			
		||||
import com.google.gerrit.extensions.client.ChangeKind;
 | 
			
		||||
import com.google.gerrit.extensions.client.DiffPreferencesInfo;
 | 
			
		||||
import com.google.gerrit.server.change.ChangeKindCache;
 | 
			
		||||
import com.google.gerrit.server.change.LabelNormalizer;
 | 
			
		||||
import com.google.gerrit.server.logging.Metadata;
 | 
			
		||||
import com.google.gerrit.server.logging.TraceContext;
 | 
			
		||||
import com.google.gerrit.server.logging.TraceContext.TraceTimer;
 | 
			
		||||
import com.google.gerrit.server.notedb.ChangeNotes;
 | 
			
		||||
import com.google.gerrit.server.patch.PatchList;
 | 
			
		||||
import com.google.gerrit.server.patch.PatchListCache;
 | 
			
		||||
import com.google.gerrit.server.patch.PatchListKey;
 | 
			
		||||
import com.google.gerrit.server.patch.PatchListNotAvailableException;
 | 
			
		||||
import com.google.gerrit.server.project.ProjectCache;
 | 
			
		||||
import com.google.gerrit.server.project.ProjectState;
 | 
			
		||||
import com.google.inject.Inject;
 | 
			
		||||
@@ -59,13 +66,18 @@ public class ApprovalInference {
 | 
			
		||||
  private final ProjectCache projectCache;
 | 
			
		||||
  private final ChangeKindCache changeKindCache;
 | 
			
		||||
  private final LabelNormalizer labelNormalizer;
 | 
			
		||||
  private final PatchListCache patchListCache;
 | 
			
		||||
 | 
			
		||||
  @Inject
 | 
			
		||||
  ApprovalInference(
 | 
			
		||||
      ProjectCache projectCache, ChangeKindCache changeKindCache, LabelNormalizer labelNormalizer) {
 | 
			
		||||
      ProjectCache projectCache,
 | 
			
		||||
      ChangeKindCache changeKindCache,
 | 
			
		||||
      LabelNormalizer labelNormalizer,
 | 
			
		||||
      PatchListCache patchListCache) {
 | 
			
		||||
    this.projectCache = projectCache;
 | 
			
		||||
    this.changeKindCache = changeKindCache;
 | 
			
		||||
    this.labelNormalizer = labelNormalizer;
 | 
			
		||||
    this.patchListCache = patchListCache;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
@@ -93,10 +105,15 @@ public class ApprovalInference {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private static boolean canCopy(
 | 
			
		||||
      ProjectState project, PatchSetApproval psa, PatchSet.Id psId, ChangeKind kind) {
 | 
			
		||||
      ProjectState project,
 | 
			
		||||
      PatchSetApproval psa,
 | 
			
		||||
      PatchSet.Id psId,
 | 
			
		||||
      ChangeKind kind,
 | 
			
		||||
      PatchList patchList) {
 | 
			
		||||
    int n = psa.key().patchSetId().get();
 | 
			
		||||
    checkArgument(n != psId.get());
 | 
			
		||||
    LabelType type = project.getLabelTypes().byLabel(psa.labelId());
 | 
			
		||||
 | 
			
		||||
    if (type == null) {
 | 
			
		||||
      logger.atFine().log(
 | 
			
		||||
          "approval %d on label %s of patch set %d of change %d cannot be copied"
 | 
			
		||||
@@ -153,6 +170,25 @@ public class ApprovalInference {
 | 
			
		||||
          psa.value(),
 | 
			
		||||
          project.getName());
 | 
			
		||||
      return true;
 | 
			
		||||
    } else if (type.isCopyAllScoresIfListOfFilesDidNotChange()
 | 
			
		||||
        && patchList.getPatches().stream()
 | 
			
		||||
            .noneMatch(
 | 
			
		||||
                p ->
 | 
			
		||||
                    p.getChangeType() == ChangeType.ADDED
 | 
			
		||||
                        || p.getChangeType() == ChangeType.DELETED)) {
 | 
			
		||||
      logger.atFine().log(
 | 
			
		||||
          "approval %d on label %s of patch set %d of change %d can be copied"
 | 
			
		||||
              + " to patch set %d because the label has set "
 | 
			
		||||
              + "copyAllScoresIfListOfFilesDidNotChange = true on "
 | 
			
		||||
              + "project %s and list of files did not change (maybe except a rename, which is "
 | 
			
		||||
              + "still the same file).",
 | 
			
		||||
          psa.value(),
 | 
			
		||||
          psa.label(),
 | 
			
		||||
          n,
 | 
			
		||||
          psa.key().patchSetId().changeId().get(),
 | 
			
		||||
          psId.get(),
 | 
			
		||||
          project.getName());
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    switch (kind) {
 | 
			
		||||
      case MERGE_FIRST_PARENT_UPDATE:
 | 
			
		||||
@@ -331,15 +367,38 @@ public class ApprovalInference {
 | 
			
		||||
    logger.atFine().log(
 | 
			
		||||
        "change kind for patch set %d of change %d against prior patch set %s is %s",
 | 
			
		||||
        ps.id().get(), ps.id().changeId().get(), priorPatchSet.getValue().id().changeId(), kind);
 | 
			
		||||
    PatchList patchList = getPatchList(project, ps, priorPatchSet);
 | 
			
		||||
    for (PatchSetApproval psa : priorApprovals) {
 | 
			
		||||
      if (resultByUser.contains(psa.label(), psa.accountId())) {
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
      if (!canCopy(project, psa, ps.id(), kind)) {
 | 
			
		||||
      if (!canCopy(project, psa, ps.id(), kind, patchList)) {
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
      resultByUser.put(psa.label(), psa.accountId(), psa.copyWithPatchSet(ps.id()));
 | 
			
		||||
    }
 | 
			
		||||
    return resultByUser.values();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Gets the {@link PatchList} between the two latest patch-sets. Can be used to compute difference
 | 
			
		||||
   * in files between those two patch-sets .
 | 
			
		||||
   */
 | 
			
		||||
  private PatchList getPatchList(
 | 
			
		||||
      ProjectState project, PatchSet ps, Map.Entry<PatchSet.Id, PatchSet> priorPatchSet) {
 | 
			
		||||
    PatchListKey key =
 | 
			
		||||
        PatchListKey.againstCommit(
 | 
			
		||||
            priorPatchSet.getValue().commitId(),
 | 
			
		||||
            ps.commitId(),
 | 
			
		||||
            DiffPreferencesInfo.Whitespace.IGNORE_NONE);
 | 
			
		||||
    try {
 | 
			
		||||
      return patchListCache.get(key, project.getNameKey());
 | 
			
		||||
    } catch (PatchListNotAvailableException ex) {
 | 
			
		||||
      throw new StorageException(
 | 
			
		||||
          "failed to compute difference in files, so won't copy"
 | 
			
		||||
              + " votes on labels even if list of files is the same and "
 | 
			
		||||
              + "copyAllIfListOfFilesDidNotChange",
 | 
			
		||||
          ex);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -42,6 +42,8 @@ public class LabelTypeSerializer {
 | 
			
		||||
        .setCopyAnyScore(proto.getCopyAnyScore())
 | 
			
		||||
        .setCopyMinScore(proto.getCopyMinScore())
 | 
			
		||||
        .setCopyMaxScore(proto.getCopyMaxScore())
 | 
			
		||||
        .setCopyAllScoresIfListOfFilesDidNotChange(
 | 
			
		||||
            proto.getCopyAllScoresIfListOfFilesDidNotChange())
 | 
			
		||||
        .setCopyAllScoresOnMergeFirstParentUpdate(proto.getCopyAllScoresOnMergeFirstParentUpdate())
 | 
			
		||||
        .setCopyAllScoresOnTrivialRebase(proto.getCopyAllScoresOnTrivialRebase())
 | 
			
		||||
        .setCopyAllScoresIfNoCodeChange(proto.getCopyAllScoresIfNoCodeChange())
 | 
			
		||||
@@ -68,6 +70,8 @@ public class LabelTypeSerializer {
 | 
			
		||||
        .setCopyAnyScore(autoValue.isCopyAnyScore())
 | 
			
		||||
        .setCopyMinScore(autoValue.isCopyMinScore())
 | 
			
		||||
        .setCopyMaxScore(autoValue.isCopyMaxScore())
 | 
			
		||||
        .setCopyAllScoresIfListOfFilesDidNotChange(
 | 
			
		||||
            autoValue.isCopyAllScoresIfListOfFilesDidNotChange())
 | 
			
		||||
        .setCopyAllScoresOnMergeFirstParentUpdate(
 | 
			
		||||
            autoValue.isCopyAllScoresOnMergeFirstParentUpdate())
 | 
			
		||||
        .setCopyAllScoresOnTrivialRebase(autoValue.isCopyAllScoresOnTrivialRebase())
 | 
			
		||||
 
 | 
			
		||||
@@ -35,6 +35,8 @@ public class LabelDefinitionJson {
 | 
			
		||||
    label.copyAnyScore = toBoolean(labelType.isCopyAnyScore());
 | 
			
		||||
    label.copyMinScore = toBoolean(labelType.isCopyMinScore());
 | 
			
		||||
    label.copyMaxScore = toBoolean(labelType.isCopyMaxScore());
 | 
			
		||||
    label.copyAllScoresIfListOfFilesDidNotChange =
 | 
			
		||||
        toBoolean(labelType.isCopyAllScoresIfListOfFilesDidNotChange());
 | 
			
		||||
    label.copyAllScoresIfNoChange = toBoolean(labelType.isCopyAllScoresIfNoChange());
 | 
			
		||||
    label.copyAllScoresIfNoCodeChange = toBoolean(labelType.isCopyAllScoresIfNoCodeChange());
 | 
			
		||||
    label.copyAllScoresOnTrivialRebase = toBoolean(labelType.isCopyAllScoresOnTrivialRebase());
 | 
			
		||||
 
 | 
			
		||||
@@ -111,6 +111,8 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
 | 
			
		||||
  public static final String KEY_IGNORE_SELF_APPROVAL = "ignoreSelfApproval";
 | 
			
		||||
  public static final String KEY_COPY_ANY_SCORE = "copyAnyScore";
 | 
			
		||||
  public static final String KEY_COPY_MAX_SCORE = "copyMaxScore";
 | 
			
		||||
  public static final String KEY_COPY_ALL_SCORES_IF_LIST_OF_FILES_DID_NOT_CHANGE =
 | 
			
		||||
      "copyAllScoresIfListOfFilesDidNotChange";
 | 
			
		||||
  public static final String KEY_COPY_ALL_SCORES_ON_MERGE_FIRST_PARENT_UPDATE =
 | 
			
		||||
      "copyAllScoresOnMergeFirstParentUpdate";
 | 
			
		||||
  public static final String KEY_COPY_ALL_SCORES_ON_TRIVIAL_REBASE = "copyAllScoresOnTrivialRebase";
 | 
			
		||||
@@ -1029,6 +1031,12 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
 | 
			
		||||
          rc.getBoolean(LABEL, name, KEY_COPY_MIN_SCORE, LabelType.DEF_COPY_MIN_SCORE));
 | 
			
		||||
      label.setCopyMaxScore(
 | 
			
		||||
          rc.getBoolean(LABEL, name, KEY_COPY_MAX_SCORE, LabelType.DEF_COPY_MAX_SCORE));
 | 
			
		||||
      label.setCopyAllScoresIfListOfFilesDidNotChange(
 | 
			
		||||
          rc.getBoolean(
 | 
			
		||||
              LABEL,
 | 
			
		||||
              name,
 | 
			
		||||
              KEY_COPY_ALL_SCORES_IF_LIST_OF_FILES_DID_NOT_CHANGE,
 | 
			
		||||
              LabelType.DEF_COPY_ALL_SCORES_IF_LIST_OF_FILES_DID_NOT_CHANGE));
 | 
			
		||||
      label.setCopyAllScoresOnMergeFirstParentUpdate(
 | 
			
		||||
          rc.getBoolean(
 | 
			
		||||
              LABEL,
 | 
			
		||||
@@ -1562,6 +1570,13 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
 | 
			
		||||
          KEY_COPY_ALL_SCORES_IF_NO_CHANGE,
 | 
			
		||||
          label.isCopyAllScoresIfNoChange(),
 | 
			
		||||
          LabelType.DEF_COPY_ALL_SCORES_IF_NO_CHANGE);
 | 
			
		||||
      setBooleanConfigKey(
 | 
			
		||||
          rc,
 | 
			
		||||
          LABEL,
 | 
			
		||||
          name,
 | 
			
		||||
          KEY_COPY_ALL_SCORES_IF_LIST_OF_FILES_DID_NOT_CHANGE,
 | 
			
		||||
          label.isCopyAllScoresIfListOfFilesDidNotChange(),
 | 
			
		||||
          LabelType.DEF_COPY_ALL_SCORES_IF_LIST_OF_FILES_DID_NOT_CHANGE);
 | 
			
		||||
      setBooleanConfigKey(
 | 
			
		||||
          rc,
 | 
			
		||||
          LABEL,
 | 
			
		||||
 
 | 
			
		||||
@@ -174,6 +174,11 @@ public class CreateLabel
 | 
			
		||||
      labelType.setCopyMaxScore(input.copyMaxScore);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (input.copyAllScoresIfListOfFilesDidNotChange != null) {
 | 
			
		||||
      labelType.setCopyAllScoresIfListOfFilesDidNotChange(
 | 
			
		||||
          input.copyAllScoresIfListOfFilesDidNotChange);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (input.copyAllScoresIfNoChange != null) {
 | 
			
		||||
      labelType.setCopyAllScoresIfNoChange(input.copyAllScoresIfNoChange);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -189,6 +189,12 @@ public class SetLabel implements RestModifyView<LabelResource, LabelDefinitionIn
 | 
			
		||||
      dirty = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (input.copyAllScoresIfListOfFilesDidNotChange != null) {
 | 
			
		||||
      labelTypeBuilder.setCopyAllScoresIfListOfFilesDidNotChange(
 | 
			
		||||
          input.copyAllScoresIfListOfFilesDidNotChange);
 | 
			
		||||
      dirty = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (input.copyAllScoresIfNoChange != null) {
 | 
			
		||||
      labelTypeBuilder.setCopyAllScoresIfNoChange(input.copyAllScoresIfNoChange);
 | 
			
		||||
      dirty = true;
 | 
			
		||||
 
 | 
			
		||||
@@ -37,8 +37,10 @@ import com.google.gerrit.acceptance.GitUtil;
 | 
			
		||||
import com.google.gerrit.acceptance.NoHttpd;
 | 
			
		||||
import com.google.gerrit.acceptance.PushOneCommit;
 | 
			
		||||
import com.google.gerrit.acceptance.TestAccount;
 | 
			
		||||
import com.google.gerrit.acceptance.testsuite.change.ChangeOperations;
 | 
			
		||||
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
 | 
			
		||||
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
 | 
			
		||||
import com.google.gerrit.entities.Change;
 | 
			
		||||
import com.google.gerrit.entities.LabelType;
 | 
			
		||||
import com.google.gerrit.entities.RefNames;
 | 
			
		||||
import com.google.gerrit.extensions.api.changes.CherryPickInput;
 | 
			
		||||
@@ -68,6 +70,7 @@ import org.junit.Test;
 | 
			
		||||
public class StickyApprovalsIT extends AbstractDaemonTest {
 | 
			
		||||
  @Inject private ProjectOperations projectOperations;
 | 
			
		||||
  @Inject private RequestScopeOperations requestScopeOperations;
 | 
			
		||||
  @Inject private ChangeOperations changeOperations;
 | 
			
		||||
 | 
			
		||||
  @Inject
 | 
			
		||||
  @Named("change_kind")
 | 
			
		||||
@@ -324,6 +327,96 @@ public class StickyApprovalsIT extends AbstractDaemonTest {
 | 
			
		||||
    assertVotes(c, user, 0, 0, null);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Test
 | 
			
		||||
  public void notStickyWithCopyAllScoresIfListOfFilesDidNotChangeWhenFileIsAdded()
 | 
			
		||||
      throws Exception {
 | 
			
		||||
    try (ProjectConfigUpdate u = updateProject(project)) {
 | 
			
		||||
      u.getConfig()
 | 
			
		||||
          .updateLabelType("Code-Review", b -> b.setCopyAllScoresIfListOfFilesDidNotChange(true));
 | 
			
		||||
      u.save();
 | 
			
		||||
    }
 | 
			
		||||
    Change.Id changeId =
 | 
			
		||||
        changeOperations.newChange().project(project).file("file").content("content").create();
 | 
			
		||||
    vote(admin, changeId.toString(), 2, 1);
 | 
			
		||||
    vote(user, changeId.toString(), -2, -1);
 | 
			
		||||
 | 
			
		||||
    changeOperations
 | 
			
		||||
        .change(changeId)
 | 
			
		||||
        .newPatchset()
 | 
			
		||||
        .file("new file")
 | 
			
		||||
        .content("new content")
 | 
			
		||||
        .create();
 | 
			
		||||
    ChangeInfo c = detailedChange(changeId.toString());
 | 
			
		||||
 | 
			
		||||
    // no votes are copied since the list of files changed.
 | 
			
		||||
    assertVotes(c, admin, 0, 0);
 | 
			
		||||
    assertVotes(c, user, 0, 0);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Test
 | 
			
		||||
  public void notStickyWithCopyAllScoresIfListOfFilesDidNotChangeWhenFileIsDeleted()
 | 
			
		||||
      throws Exception {
 | 
			
		||||
    try (ProjectConfigUpdate u = updateProject(project)) {
 | 
			
		||||
      u.getConfig()
 | 
			
		||||
          .updateLabelType("Code-Review", b -> b.setCopyAllScoresIfListOfFilesDidNotChange(true));
 | 
			
		||||
      u.save();
 | 
			
		||||
    }
 | 
			
		||||
    Change.Id changeId =
 | 
			
		||||
        changeOperations.newChange().project(project).file("file").content("content").create();
 | 
			
		||||
    vote(admin, changeId.toString(), 2, 1);
 | 
			
		||||
    vote(user, changeId.toString(), -2, -1);
 | 
			
		||||
 | 
			
		||||
    changeOperations.change(changeId).newPatchset().file("file").delete().create();
 | 
			
		||||
    ChangeInfo c = detailedChange(changeId.toString());
 | 
			
		||||
 | 
			
		||||
    // no votes are copied since the list of files changed.
 | 
			
		||||
    assertVotes(c, admin, 0, 0);
 | 
			
		||||
    assertVotes(c, user, 0, 0);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Test
 | 
			
		||||
  public void stickyWithCopyAllScoresIfListOfFilesDidNotChangeWhenFileIsModified()
 | 
			
		||||
      throws Exception {
 | 
			
		||||
    try (ProjectConfigUpdate u = updateProject(project)) {
 | 
			
		||||
      u.getConfig()
 | 
			
		||||
          .updateLabelType("Code-Review", b -> b.setCopyAllScoresIfListOfFilesDidNotChange(true));
 | 
			
		||||
      u.save();
 | 
			
		||||
    }
 | 
			
		||||
    Change.Id changeId =
 | 
			
		||||
        changeOperations.newChange().project(project).file("file").content("content").create();
 | 
			
		||||
    vote(admin, changeId.toString(), 2, 1);
 | 
			
		||||
    vote(user, changeId.toString(), -2, -1);
 | 
			
		||||
 | 
			
		||||
    changeOperations.change(changeId).newPatchset().file("file").content("new content").create();
 | 
			
		||||
    ChangeInfo c = detailedChange(changeId.toString());
 | 
			
		||||
 | 
			
		||||
    // only code review votes are copied since copyAllScoresIfListOfFilesDidNotChange is
 | 
			
		||||
    // configured for that label, and list of files didn't change.
 | 
			
		||||
    assertVotes(c, admin, 2, 0);
 | 
			
		||||
    assertVotes(c, user, -2, 0);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Test
 | 
			
		||||
  public void stickyWithCopyAllScoresIfListOfFilesDidNotChangeWhenFileIsRenamed() throws Exception {
 | 
			
		||||
    try (ProjectConfigUpdate u = updateProject(project)) {
 | 
			
		||||
      u.getConfig()
 | 
			
		||||
          .updateLabelType("Code-Review", b -> b.setCopyAllScoresIfListOfFilesDidNotChange(true));
 | 
			
		||||
      u.save();
 | 
			
		||||
    }
 | 
			
		||||
    Change.Id changeId =
 | 
			
		||||
        changeOperations.newChange().project(project).file("file").content("content").create();
 | 
			
		||||
    vote(admin, changeId.toString(), 2, 1);
 | 
			
		||||
    vote(user, changeId.toString(), -2, -1);
 | 
			
		||||
 | 
			
		||||
    changeOperations.change(changeId).newPatchset().file("file").renameTo("new_file").create();
 | 
			
		||||
    ChangeInfo c = detailedChange(changeId.toString());
 | 
			
		||||
 | 
			
		||||
    // only code review votes are copied since copyAllScoresIfListOfFilesDidNotChange is
 | 
			
		||||
    // configured for that label, and list of files didn't change (rename is still the same file).
 | 
			
		||||
    assertVotes(c, admin, 2, 0);
 | 
			
		||||
    assertVotes(c, user, -2, 0);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Test
 | 
			
		||||
  public void removedVotesNotSticky() throws Exception {
 | 
			
		||||
    try (ProjectConfigUpdate u = updateProject(project)) {
 | 
			
		||||
 
 | 
			
		||||
@@ -240,6 +240,7 @@ public class CreateLabelIT extends AbstractDaemonTest {
 | 
			
		||||
    assertThat(createdLabel.copyAnyScore).isNull();
 | 
			
		||||
    assertThat(createdLabel.copyMinScore).isNull();
 | 
			
		||||
    assertThat(createdLabel.copyMaxScore).isNull();
 | 
			
		||||
    assertThat(createdLabel.copyAllScoresIfListOfFilesDidNotChange).isNull();
 | 
			
		||||
    assertThat(createdLabel.copyAllScoresIfNoChange).isTrue();
 | 
			
		||||
    assertThat(createdLabel.copyAllScoresIfNoCodeChange).isNull();
 | 
			
		||||
    assertThat(createdLabel.copyAllScoresOnTrivialRebase).isNull();
 | 
			
		||||
@@ -449,6 +450,28 @@ public class CreateLabelIT extends AbstractDaemonTest {
 | 
			
		||||
    assertThat(createdLabel.copyMaxScore).isNull();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Test
 | 
			
		||||
  public void createWithCopyAllScoresIfListOfFilesDidNotChange() throws Exception {
 | 
			
		||||
    LabelDefinitionInput input = new LabelDefinitionInput();
 | 
			
		||||
    input.values = ImmutableMap.of("+1", "Looks Good", " 0", "Don't Know", "-1", "Looks Bad");
 | 
			
		||||
    input.copyAllScoresIfListOfFilesDidNotChange = true;
 | 
			
		||||
 | 
			
		||||
    LabelDefinitionInfo createdLabel =
 | 
			
		||||
        gApi.projects().name(project.get()).label("foo").create(input).get();
 | 
			
		||||
    assertThat(createdLabel.copyAllScoresIfListOfFilesDidNotChange).isTrue();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Test
 | 
			
		||||
  public void createWithoutCopyAllScoresIfListOfFilesDidNotChange() throws Exception {
 | 
			
		||||
    LabelDefinitionInput input = new LabelDefinitionInput();
 | 
			
		||||
    input.values = ImmutableMap.of("+1", "Looks Good", " 0", "Don't Know", "-1", "Looks Bad");
 | 
			
		||||
    input.copyAllScoresIfListOfFilesDidNotChange = false;
 | 
			
		||||
 | 
			
		||||
    LabelDefinitionInfo createdLabel =
 | 
			
		||||
        gApi.projects().name(project.get()).label("foo").create(input).get();
 | 
			
		||||
    assertThat(createdLabel.copyAllScoresIfListOfFilesDidNotChange).isNull();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Test
 | 
			
		||||
  public void createWithCopyAllScoresIfNoChange() throws Exception {
 | 
			
		||||
    LabelDefinitionInput input = new LabelDefinitionInput();
 | 
			
		||||
 
 | 
			
		||||
@@ -113,6 +113,7 @@ public class GetLabelIT extends AbstractDaemonTest {
 | 
			
		||||
    assertThat(fooLabel.copyAnyScore).isNull();
 | 
			
		||||
    assertThat(fooLabel.copyMinScore).isNull();
 | 
			
		||||
    assertThat(fooLabel.copyMaxScore).isNull();
 | 
			
		||||
    assertThat(fooLabel.copyAllScoresIfListOfFilesDidNotChange).isNull();
 | 
			
		||||
    assertThat(fooLabel.copyAllScoresIfNoChange).isNull();
 | 
			
		||||
    assertThat(fooLabel.copyAllScoresIfNoCodeChange).isNull();
 | 
			
		||||
    assertThat(fooLabel.copyAllScoresOnTrivialRebase).isNull();
 | 
			
		||||
@@ -135,6 +136,7 @@ public class GetLabelIT extends AbstractDaemonTest {
 | 
			
		||||
                labelType.setCopyAnyScore(true);
 | 
			
		||||
                labelType.setCopyMinScore(true);
 | 
			
		||||
                labelType.setCopyMaxScore(true);
 | 
			
		||||
                labelType.setCopyAllScoresIfListOfFilesDidNotChange(true);
 | 
			
		||||
                labelType.setCopyAllScoresIfNoCodeChange(true);
 | 
			
		||||
                labelType.setCopyAllScoresOnTrivialRebase(true);
 | 
			
		||||
                labelType.setCopyAllScoresOnMergeFirstParentUpdate(true);
 | 
			
		||||
@@ -149,6 +151,7 @@ public class GetLabelIT extends AbstractDaemonTest {
 | 
			
		||||
    assertThat(fooLabel.copyAnyScore).isTrue();
 | 
			
		||||
    assertThat(fooLabel.copyMinScore).isTrue();
 | 
			
		||||
    assertThat(fooLabel.copyMaxScore).isTrue();
 | 
			
		||||
    assertThat(fooLabel.copyAllScoresIfListOfFilesDidNotChange).isTrue();
 | 
			
		||||
    assertThat(fooLabel.copyAllScoresIfNoChange).isTrue();
 | 
			
		||||
    assertThat(fooLabel.copyAllScoresIfNoCodeChange).isTrue();
 | 
			
		||||
    assertThat(fooLabel.copyAllScoresOnTrivialRebase).isTrue();
 | 
			
		||||
 
 | 
			
		||||
@@ -43,6 +43,7 @@ public class LabelAssert {
 | 
			
		||||
    assertThat(codeReviewLabel.copyAnyScore).isNull();
 | 
			
		||||
    assertThat(codeReviewLabel.copyMinScore).isTrue();
 | 
			
		||||
    assertThat(codeReviewLabel.copyMaxScore).isNull();
 | 
			
		||||
    assertThat(codeReviewLabel.copyAllScoresIfListOfFilesDidNotChange).isNull();
 | 
			
		||||
    assertThat(codeReviewLabel.copyAllScoresIfNoChange).isTrue();
 | 
			
		||||
    assertThat(codeReviewLabel.copyAllScoresIfNoCodeChange).isNull();
 | 
			
		||||
    assertThat(codeReviewLabel.copyAllScoresOnTrivialRebase).isTrue();
 | 
			
		||||
 
 | 
			
		||||
@@ -135,6 +135,7 @@ public class ListLabelsIT extends AbstractDaemonTest {
 | 
			
		||||
    assertThat(fooLabel.copyAnyScore).isNull();
 | 
			
		||||
    assertThat(fooLabel.copyMinScore).isNull();
 | 
			
		||||
    assertThat(fooLabel.copyMaxScore).isNull();
 | 
			
		||||
    assertThat(fooLabel.copyAllScoresIfListOfFilesDidNotChange).isNull();
 | 
			
		||||
    assertThat(fooLabel.copyAllScoresIfNoChange).isNull();
 | 
			
		||||
    assertThat(fooLabel.copyAllScoresIfNoCodeChange).isNull();
 | 
			
		||||
    assertThat(fooLabel.copyAllScoresOnTrivialRebase).isNull();
 | 
			
		||||
@@ -157,6 +158,7 @@ public class ListLabelsIT extends AbstractDaemonTest {
 | 
			
		||||
                labelType.setCopyAnyScore(true);
 | 
			
		||||
                labelType.setCopyMinScore(true);
 | 
			
		||||
                labelType.setCopyMaxScore(true);
 | 
			
		||||
                labelType.setCopyAllScoresIfListOfFilesDidNotChange(true);
 | 
			
		||||
                labelType.setCopyAllScoresIfNoCodeChange(true);
 | 
			
		||||
                labelType.setCopyAllScoresOnTrivialRebase(true);
 | 
			
		||||
                labelType.setCopyAllScoresOnMergeFirstParentUpdate(true);
 | 
			
		||||
@@ -174,6 +176,7 @@ public class ListLabelsIT extends AbstractDaemonTest {
 | 
			
		||||
    assertThat(fooLabel.copyAnyScore).isTrue();
 | 
			
		||||
    assertThat(fooLabel.copyMinScore).isTrue();
 | 
			
		||||
    assertThat(fooLabel.copyMaxScore).isTrue();
 | 
			
		||||
    assertThat(fooLabel.copyAllScoresIfListOfFilesDidNotChange).isTrue();
 | 
			
		||||
    assertThat(fooLabel.copyAllScoresIfNoChange).isTrue();
 | 
			
		||||
    assertThat(fooLabel.copyAllScoresIfNoCodeChange).isTrue();
 | 
			
		||||
    assertThat(fooLabel.copyAllScoresOnTrivialRebase).isTrue();
 | 
			
		||||
 
 | 
			
		||||
@@ -581,6 +581,65 @@ public class SetLabelIT extends AbstractDaemonTest {
 | 
			
		||||
    assertThat(gApi.projects().name(project.get()).label("foo").get().copyMaxScore).isNull();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Test
 | 
			
		||||
  public void setCopyAllScoresIfListOfFilesDidNotChange() throws Exception {
 | 
			
		||||
    configLabel("foo", LabelFunction.NO_OP);
 | 
			
		||||
    assertThat(
 | 
			
		||||
            gApi.projects()
 | 
			
		||||
                .name(project.get())
 | 
			
		||||
                .label("foo")
 | 
			
		||||
                .get()
 | 
			
		||||
                .copyAllScoresIfListOfFilesDidNotChange)
 | 
			
		||||
        .isNull();
 | 
			
		||||
 | 
			
		||||
    LabelDefinitionInput input = new LabelDefinitionInput();
 | 
			
		||||
    input.copyAllScoresIfListOfFilesDidNotChange = true;
 | 
			
		||||
 | 
			
		||||
    LabelDefinitionInfo updatedLabel =
 | 
			
		||||
        gApi.projects().name(project.get()).label("foo").update(input);
 | 
			
		||||
    assertThat(updatedLabel.copyAllScoresIfListOfFilesDidNotChange).isTrue();
 | 
			
		||||
 | 
			
		||||
    assertThat(
 | 
			
		||||
            gApi.projects()
 | 
			
		||||
                .name(project.get())
 | 
			
		||||
                .label("foo")
 | 
			
		||||
                .get()
 | 
			
		||||
                .copyAllScoresIfListOfFilesDidNotChange)
 | 
			
		||||
        .isTrue();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Test
 | 
			
		||||
  public void unsetCopyAllScoresIfListOfFilesDidNotChange() throws Exception {
 | 
			
		||||
    configLabel("foo", LabelFunction.NO_OP);
 | 
			
		||||
    try (ProjectConfigUpdate u = updateProject(project)) {
 | 
			
		||||
      u.getConfig()
 | 
			
		||||
          .updateLabelType("foo", lt -> lt.setCopyAllScoresIfListOfFilesDidNotChange(true));
 | 
			
		||||
      u.save();
 | 
			
		||||
    }
 | 
			
		||||
    assertThat(
 | 
			
		||||
            gApi.projects()
 | 
			
		||||
                .name(project.get())
 | 
			
		||||
                .label("foo")
 | 
			
		||||
                .get()
 | 
			
		||||
                .copyAllScoresIfListOfFilesDidNotChange)
 | 
			
		||||
        .isTrue();
 | 
			
		||||
 | 
			
		||||
    LabelDefinitionInput input = new LabelDefinitionInput();
 | 
			
		||||
    input.copyAllScoresIfListOfFilesDidNotChange = false;
 | 
			
		||||
 | 
			
		||||
    LabelDefinitionInfo updatedLabel =
 | 
			
		||||
        gApi.projects().name(project.get()).label("foo").update(input);
 | 
			
		||||
    assertThat(updatedLabel.copyAllScoresIfListOfFilesDidNotChange).isNull();
 | 
			
		||||
 | 
			
		||||
    assertThat(
 | 
			
		||||
            gApi.projects()
 | 
			
		||||
                .name(project.get())
 | 
			
		||||
                .label("foo")
 | 
			
		||||
                .get()
 | 
			
		||||
                .copyAllScoresIfListOfFilesDidNotChange)
 | 
			
		||||
        .isNull();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Test
 | 
			
		||||
  public void setCopyAllScoresIfNoChange() throws Exception {
 | 
			
		||||
    configLabel("foo", LabelFunction.NO_OP);
 | 
			
		||||
 
 | 
			
		||||
@@ -38,6 +38,8 @@ public class LabelTypeSerializerTest {
 | 
			
		||||
          .setCopyAnyScore(!LabelType.DEF_COPY_ANY_SCORE)
 | 
			
		||||
          .setCopyMaxScore(!LabelType.DEF_COPY_MAX_SCORE)
 | 
			
		||||
          .setCopyMinScore(!LabelType.DEF_COPY_MIN_SCORE)
 | 
			
		||||
          .setCopyAllScoresIfListOfFilesDidNotChange(
 | 
			
		||||
              !LabelType.DEF_COPY_ALL_SCORES_IF_LIST_OF_FILES_DID_NOT_CHANGE)
 | 
			
		||||
          .setCopyAllScoresOnMergeFirstParentUpdate(
 | 
			
		||||
              !LabelType.DEF_COPY_ALL_SCORES_ON_MERGE_FIRST_PARENT_UPDATE)
 | 
			
		||||
          .setCopyAllScoresOnTrivialRebase(!LabelType.DEF_COPY_ALL_SCORES_ON_TRIVIAL_REBASE)
 | 
			
		||||
 
 | 
			
		||||
@@ -77,6 +77,9 @@ public class ProjectConfigTest {
 | 
			
		||||
          + "  copyMaxScore = "
 | 
			
		||||
          + !LabelType.DEF_COPY_MAX_SCORE
 | 
			
		||||
          + "\n"
 | 
			
		||||
          + "  copyAllScoresIfListOfFilesDidNotChange = "
 | 
			
		||||
          + !LabelType.DEF_COPY_ALL_SCORES_IF_LIST_OF_FILES_DID_NOT_CHANGE
 | 
			
		||||
          + "\n"
 | 
			
		||||
          + "  copyAllScoresOnMergeFirstParentUpdate = "
 | 
			
		||||
          + !LabelType.DEF_COPY_ALL_SCORES_ON_MERGE_FIRST_PARENT_UPDATE
 | 
			
		||||
          + "\n"
 | 
			
		||||
@@ -270,6 +273,8 @@ public class ProjectConfigTest {
 | 
			
		||||
    assertThat(type.isCopyAnyScore()).isNotEqualTo(LabelType.DEF_COPY_ANY_SCORE);
 | 
			
		||||
    assertThat(type.isCopyMinScore()).isNotEqualTo(LabelType.DEF_COPY_MIN_SCORE);
 | 
			
		||||
    assertThat(type.isCopyMaxScore()).isNotEqualTo(LabelType.DEF_COPY_MAX_SCORE);
 | 
			
		||||
    assertThat(type.isCopyAllScoresIfListOfFilesDidNotChange())
 | 
			
		||||
        .isNotEqualTo(LabelType.DEF_COPY_ALL_SCORES_IF_LIST_OF_FILES_DID_NOT_CHANGE);
 | 
			
		||||
    assertThat(type.isCopyAllScoresOnMergeFirstParentUpdate())
 | 
			
		||||
        .isNotEqualTo(LabelType.DEF_COPY_ALL_SCORES_ON_MERGE_FIRST_PARENT_UPDATE);
 | 
			
		||||
    assertThat(type.isCopyAllScoresOnTrivialRebase())
 | 
			
		||||
 
 | 
			
		||||
@@ -443,6 +443,7 @@ message LabelTypeProto {
 | 
			
		||||
  int32 max_positive = 16;
 | 
			
		||||
  bool can_override = 17;
 | 
			
		||||
  repeated string ref_patterns = 18;
 | 
			
		||||
  bool copy_all_scores_if_list_of_files_did_not_change = 19;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Serialized form of com.google.gerrit.server.project.ConfiguredMimeTypes.
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user