Merge "Introduce deep-equal and migrate code to use it."
diff --git a/Documentation/user-search.txt b/Documentation/user-search.txt
index 4499f97..694e5d8 100644
--- a/Documentation/user-search.txt
+++ b/Documentation/user-search.txt
@@ -49,10 +49,6 @@
returned results. Search can also be performed by typing only a
text with no operator, which will match against a variety of fields.
-Operators can escape characters, for example double quotes, by enclosing the
-value with double quotes and escaping double quotes inside the value with
-a backslash.
-
[[age]]
age:'AGE'::
+
diff --git a/antlr3/com/google/gerrit/index/query/Query.g b/antlr3/com/google/gerrit/index/query/Query.g
index 75c75c7..1bf20aa 100644
--- a/antlr3/com/google/gerrit/index/query/Query.g
+++ b/antlr3/com/google/gerrit/index/query/Query.g
@@ -157,12 +157,13 @@
;
EXACT_PHRASE
-@init { final StringBuilder buf = new StringBuilder(); }
- : '"' ( ESCAPE[buf] | i = ~('\\'|'"') { buf.appendCodePoint(i); } )* '"' {
- setText(buf.toString());
+ : '"' ( ~('"') )* '"' {
+ String s = $text;
+ setText(s.substring(1, s.length() - 1));
}
- | '{' ( ESCAPE[buf] | i = ~('\\'|'{'|'}') { buf.appendCodePoint(i); } )* '}' {
- setText(buf.toString());
+ | '{' ( ~('{'|'}') )* '}' {
+ String s = $text;
+ setText(s.substring(1, s.length() - 1));
}
;
@@ -196,11 +197,3 @@
// | '~' permit
)
;
-
-fragment ESCAPE[StringBuilder buf] :
- '\\'
- ( 't' { buf.append('\t'); }
- | 'n' { buf.append('\n'); }
- | 'r' { buf.append('\r'); }
- | i = (~('t'|'n'|'r')) { buf.appendCodePoint(i); }
- );
diff --git a/java/com/google/gerrit/pgm/util/BatchProgramModule.java b/java/com/google/gerrit/pgm/util/BatchProgramModule.java
index ee9fd77..b24fdad 100644
--- a/java/com/google/gerrit/pgm/util/BatchProgramModule.java
+++ b/java/com/google/gerrit/pgm/util/BatchProgramModule.java
@@ -81,7 +81,6 @@
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.project.SubmitRequirementsEvaluatorImpl;
import com.google.gerrit.server.project.SubmitRuleEvaluator;
-import com.google.gerrit.server.query.FileEditsPredicate;
import com.google.gerrit.server.query.approval.ApprovalModule;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.ChangeIsVisibleToPredicate;
@@ -207,8 +206,6 @@
modules.add(new DefaultSubmitRuleModule());
modules.add(new IgnoreSelfApprovalRuleModule());
- factory(FileEditsPredicate.Factory.class);
-
bind(ChangeJson.Factory.class).toProvider(Providers.of(null));
bind(EventUtil.class).toProvider(Providers.of(null));
bind(GitReferenceUpdated.class).toInstance(GitReferenceUpdated.DISABLED);
diff --git a/java/com/google/gerrit/server/config/GerritGlobalModule.java b/java/com/google/gerrit/server/config/GerritGlobalModule.java
index 1647c5c..24882cb 100644
--- a/java/com/google/gerrit/server/config/GerritGlobalModule.java
+++ b/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -190,7 +190,6 @@
import com.google.gerrit.server.project.SubmitRequirementExpressionsValidator;
import com.google.gerrit.server.project.SubmitRequirementsEvaluatorImpl;
import com.google.gerrit.server.project.SubmitRuleEvaluator;
-import com.google.gerrit.server.query.FileEditsPredicate;
import com.google.gerrit.server.query.approval.ApprovalModule;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.ChangeIsVisibleToPredicate;
@@ -490,7 +489,6 @@
factory(GitModules.Factory.class);
factory(VersionedAuthorizedKeys.Factory.class);
factory(StoreSubmitRequirementsOp.Factory.class);
- factory(FileEditsPredicate.Factory.class);
bind(AccountManager.class);
bind(SubscriptionGraph.Factory.class).to(ConfiguredSubscriptionGraphFactory.class);
diff --git a/java/com/google/gerrit/server/query/FileEditsPredicate.java b/java/com/google/gerrit/server/query/FileEditsPredicate.java
deleted file mode 100644
index e874929..0000000
--- a/java/com/google/gerrit/server/query/FileEditsPredicate.java
+++ /dev/null
@@ -1,197 +0,0 @@
-// 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.server.query;
-
-import com.google.auto.value.AutoValue;
-import com.google.common.collect.Iterables;
-import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.entities.Patch;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.patch.DiffNotAvailableException;
-import com.google.gerrit.server.patch.DiffOperations;
-import com.google.gerrit.server.patch.DiffOptions;
-import com.google.gerrit.server.patch.FilePathAdapter;
-import com.google.gerrit.server.patch.Text;
-import com.google.gerrit.server.patch.filediff.FileDiffOutput;
-import com.google.gerrit.server.patch.filediff.TaggedEdit;
-import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gerrit.server.query.change.SubmitRequirementPredicate;
-import com.google.inject.assistedinject.Assisted;
-import com.google.inject.assistedinject.AssistedInject;
-import java.io.IOException;
-import java.util.List;
-import java.util.Map;
-import java.util.regex.Pattern;
-import java.util.stream.Collectors;
-import org.eclipse.jgit.diff.Edit;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectReader;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevTree;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.treewalk.TreeWalk;
-
-/**
- * A submit-requirement predicate that can be used in submit requirements expressions. This
- * predicate is fulfilled if the diff between the latest patchset of the change and the base commit
- * includes a specific file path pattern with some specific content modification. The modification
- * could be an added, deleted or replaced content.
- */
-public class FileEditsPredicate extends SubmitRequirementPredicate {
- private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
- private DiffOperations diffOperations;
- private GitRepositoryManager repoManager;
- private final FileEditsArgs fileEditsArgs;
-
- public interface Factory {
- FileEditsPredicate create(FileEditsArgs fileEditsArgs);
- }
-
- @AutoValue
- public abstract static class FileEditsArgs {
- abstract String filePattern();
-
- abstract String editPattern();
-
- public static FileEditsArgs create(String filePattern, String contentPattern) {
- return new AutoValue_FileEditsPredicate_FileEditsArgs(filePattern, contentPattern);
- }
- }
-
- @AssistedInject
- public FileEditsPredicate(
- DiffOperations diffOperations,
- GitRepositoryManager repoManager,
- @Assisted FileEditsPredicate.FileEditsArgs fileEditsArgs) {
- super("fileEdits", fileEditsArgs.filePattern() + "," + fileEditsArgs.editPattern());
- this.diffOperations = diffOperations;
- this.repoManager = repoManager;
- this.fileEditsArgs = fileEditsArgs;
- }
-
- @Override
- public boolean match(ChangeData cd) {
- try {
- Map<String, FileDiffOutput> modifiedFiles =
- diffOperations.listModifiedFilesAgainstParent(
- cd.project(),
- cd.currentPatchSet().commitId(),
- /* parentNum= */ 0,
- DiffOptions.DEFAULTS);
- FileDiffOutput firstDiff =
- Iterables.getFirst(modifiedFiles.values(), /* defaultValue= */ null);
- if (firstDiff == null) {
- // No available diffs. We cannot identify old and new commit IDs.
- // engine.fail();
- return false;
- }
-
- Pattern filePattern = null;
- Pattern editPattern = null;
- if (fileEditsArgs.filePattern().startsWith("^")) {
- // We validated the pattern before creating this predicate. No need to revalidate.
- String pattern = fileEditsArgs.filePattern();
- filePattern = Pattern.compile(pattern);
- }
- if (fileEditsArgs.editPattern().startsWith("^")) {
- // We validated the pattern before creating this predicate. No need to revalidate.
- String pattern = fileEditsArgs.editPattern();
- editPattern = Pattern.compile(pattern);
- }
- try (Repository repo = repoManager.openRepository(cd.project());
- ObjectReader reader = repo.newObjectReader();
- RevWalk rw = new RevWalk(reader)) {
- RevTree aTree =
- firstDiff.oldCommitId().equals(ObjectId.zeroId())
- ? null
- : rw.parseTree(firstDiff.oldCommitId());
- RevTree bTree = rw.parseCommit(firstDiff.newCommitId()).getTree();
-
- for (FileDiffOutput entry : modifiedFiles.values()) {
- String newName =
- FilePathAdapter.getNewPath(entry.oldPath(), entry.newPath(), entry.changeType());
- String oldName = FilePathAdapter.getOldPath(entry.oldPath(), entry.changeType());
-
- if (Patch.isMagic(newName)) {
- continue;
- }
-
- if (match(newName, fileEditsArgs.filePattern(), filePattern)
- || (oldName != null && match(oldName, fileEditsArgs.filePattern(), filePattern))) {
- List<Edit> edits =
- entry.edits().stream().map(TaggedEdit::jgitEdit).collect(Collectors.toList());
- if (edits.isEmpty()) {
- continue;
- }
- Text tA;
- if (oldName != null) {
- tA = load(aTree, oldName, reader);
- } else {
- tA = load(aTree, newName, reader);
- }
- Text tB = load(bTree, newName, reader);
- for (Edit edit : edits) {
- if (tA != Text.EMPTY) {
- String aDiff = tA.getString(edit.getBeginA(), edit.getEndA(), true);
- if (match(aDiff, fileEditsArgs.editPattern(), editPattern)) {
- return true;
- }
- }
- if (tB != Text.EMPTY) {
- String bDiff = tB.getString(edit.getBeginB(), edit.getEndB(), true);
- if (match(bDiff, fileEditsArgs.editPattern(), editPattern)) {
- return true;
- }
- }
- }
- }
- }
- } catch (IOException e) {
- logger.atSevere().log("Error while evaluating commit edits.", e);
- return false;
- }
- } catch (DiffNotAvailableException e) {
- logger.atSevere().log("Diff error while evaluating commit edits.", e);
- return false;
- }
- return false;
- }
-
- @Override
- public int getCost() {
- return 10;
- }
-
- private Text load(@Nullable ObjectId tree, String path, ObjectReader reader) throws IOException {
- if (tree == null || path == null) {
- return Text.EMPTY;
- }
- final TreeWalk tw = TreeWalk.forPath(reader, path, tree);
- if (tw == null) {
- return Text.EMPTY;
- }
- if (tw.getFileMode(0).getObjectType() != Constants.OBJ_BLOB) {
- return Text.EMPTY;
- }
- return new Text(reader.open(tw.getObjectId(0), Constants.OBJ_BLOB));
- }
-
- private boolean match(String text, String search, @Nullable Pattern searchPattern) {
- return searchPattern == null ? text.contains(search) : searchPattern.matcher(text).find();
- }
-}
diff --git a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
index 759b707..65751b6 100644
--- a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
+++ b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -890,12 +890,12 @@
}
@Operator
- public Predicate<ChangeData> f(String file) throws QueryParseException {
+ public Predicate<ChangeData> f(String file) {
return file(file);
}
@Operator
- public Predicate<ChangeData> file(String file) throws QueryParseException {
+ public Predicate<ChangeData> file(String file) {
if (file.startsWith("^")) {
return new RegexPathPredicate(file);
}
diff --git a/java/com/google/gerrit/server/query/change/SubmitRequirementChangeQueryBuilder.java b/java/com/google/gerrit/server/query/change/SubmitRequirementChangeQueryBuilder.java
index a403d55..cc24dd04 100644
--- a/java/com/google/gerrit/server/query/change/SubmitRequirementChangeQueryBuilder.java
+++ b/java/com/google/gerrit/server/query/change/SubmitRequirementChangeQueryBuilder.java
@@ -18,12 +18,7 @@
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.QueryBuilder;
import com.google.gerrit.index.query.QueryParseException;
-import com.google.gerrit.server.query.FileEditsPredicate;
-import com.google.gerrit.server.query.FileEditsPredicate.FileEditsArgs;
import com.google.inject.Inject;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.regex.PatternSyntaxException;
/**
* A query builder for submit requirement expressions that includes all {@link ChangeQueryBuilder}
@@ -36,23 +31,9 @@
private static final QueryBuilder.Definition<ChangeData, ChangeQueryBuilder> def =
new QueryBuilder.Definition<>(SubmitRequirementChangeQueryBuilder.class);
- /**
- * Regular expression for the {@link #file(String)} operator. Field value is of the form:
- *
- * <p>'$fileRegex',withDiffContaining='$contentRegex'
- *
- * <p>Both $fileRegex and $contentRegex may contain escaped single or double quotes.
- */
- private static final Pattern FILE_EDITS_PATTERN =
- Pattern.compile("'((?:(?:\\\\')|(?:[^']))*)',withDiffContaining='((?:(?:\\\\')|(?:[^']))*)'");
-
- private final FileEditsPredicate.Factory fileEditsPredicateFactory;
-
@Inject
- SubmitRequirementChangeQueryBuilder(
- Arguments args, FileEditsPredicate.Factory fileEditsPredicateFactory) {
+ SubmitRequirementChangeQueryBuilder(Arguments args) {
super(def, args);
- this.fileEditsPredicateFactory = fileEditsPredicateFactory;
}
@Override
@@ -68,44 +49,4 @@
}
return super.is(value);
}
-
- /**
- * A SR operator that can match with file path and content pattern. The value should be of the
- * form:
- *
- * <p>file:"'$filePattern',withDiffContaining='$contentPattern'"
- *
- * <p>The operator matches with changes that have their latest PS vs. base diff containing a file
- * path matching the {@code filePattern} with an edit (added, deleted, modified) matching the
- * {@code contentPattern}. {@code filePattern} and {@code contentPattern} can start with "^" to
- * use regular expression matching.
- *
- * <p>If the specified value does not match this form, we fall back to the operator's
- * implementation in {@link ChangeQueryBuilder}.
- */
- @Override
- public Predicate<ChangeData> file(String value) throws QueryParseException {
- Matcher matcher = FILE_EDITS_PATTERN.matcher(value);
- if (!matcher.find()) {
- return super.file(value);
- }
- String filePattern = matcher.group(1);
- String contentPattern = matcher.group(2);
- if (filePattern.startsWith("^")) {
- validateRegularExpression(filePattern, "Invalid file pattern.");
- }
- if (contentPattern.startsWith("^")) {
- validateRegularExpression(contentPattern, "Invalid content pattern.");
- }
- return fileEditsPredicateFactory.create(FileEditsArgs.create(filePattern, contentPattern));
- }
-
- private static void validateRegularExpression(String pattern, String errorMessage)
- throws QueryParseException {
- try {
- Pattern.compile(pattern);
- } catch (PatternSyntaxException e) {
- throw new QueryParseException(errorMessage, e);
- }
- }
}
diff --git a/javatests/com/google/gerrit/acceptance/server/project/SubmitRequirementsEvaluatorIT.java b/javatests/com/google/gerrit/acceptance/server/project/SubmitRequirementsEvaluatorIT.java
index ce11202..a23fb7b 100644
--- a/javatests/com/google/gerrit/acceptance/server/project/SubmitRequirementsEvaluatorIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/project/SubmitRequirementsEvaluatorIT.java
@@ -23,7 +23,6 @@
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.PushOneCommit;
-import com.google.gerrit.acceptance.testsuite.change.ChangeOperations;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.entities.Change;
@@ -50,18 +49,14 @@
@Inject SubmitRequirementsEvaluator evaluator;
@Inject private ProjectOperations projectOperations;
@Inject private Provider<InternalChangeQuery> changeQueryProvider;
- @Inject private ChangeOperations changeOperations;
private ChangeData changeData;
private String changeId;
- private static final String FILE_NAME = "file.txt";
- private static final String CONTENT = "line 1\nline 2\nline 3\n";
-
@Before
public void setUp() throws Exception {
PushOneCommit.Result pushResult =
- createChange(testRepo, "refs/heads/master", "Fix a bug", FILE_NAME, CONTENT, "topic");
+ createChange(testRepo, "refs/heads/master", "Fix a bug", "file.txt", "content", "topic");
changeData = pushResult.getChange();
changeId = pushResult.getChangeId();
}
@@ -273,361 +268,6 @@
assertThat(result.status()).isEqualTo(SubmitRequirementResult.Status.SATISFIED);
}
- @Test
- public void byFileEdits_deletedContent_matching() throws Exception {
- Change.Id parent = changeOperations.newChange().file(FILE_NAME).content(CONTENT).create();
- Change.Id childId =
- changeOperations
- .newChange()
- .file(FILE_NAME)
- .content(CONTENT.replace("line 2\n", ""))
- .childOf()
- .change(parent)
- .create();
-
- SubmitRequirementExpression exp =
- SubmitRequirementExpression.create("file:\"'^.*\\.txt',withDiffContaining='line 2'\"");
-
- ChangeData childChangeData = changeQueryProvider.get().byLegacyChangeId(childId).get(0);
- SubmitRequirementExpressionResult srResult = evaluator.evaluateExpression(exp, childChangeData);
- assertThat(srResult.status()).isEqualTo(SubmitRequirementExpressionResult.Status.PASS);
- }
-
- @Test
- public void byFileEdits_deletedContent_nonMatching() throws Exception {
- Change.Id parent = changeOperations.newChange().file(FILE_NAME).content(CONTENT).create();
- Change.Id childId =
- changeOperations
- .newChange()
- .file(FILE_NAME)
- .content(CONTENT.replace("line 1\n", ""))
- .childOf()
- .change(parent)
- .create();
-
- SubmitRequirementExpression exp =
- SubmitRequirementExpression.create("file:\"'^.*\\.txt',withDiffContaining='line 2'\"");
-
- ChangeData childChangeData = changeQueryProvider.get().byLegacyChangeId(childId).get(0);
- SubmitRequirementExpressionResult srResult = evaluator.evaluateExpression(exp, childChangeData);
- assertThat(srResult.status()).isEqualTo(SubmitRequirementExpressionResult.Status.FAIL);
- }
-
- @Test
- public void byFileEdits_addedContent_matching() throws Exception {
- Change.Id parent = changeOperations.newChange().file(FILE_NAME).content(CONTENT).create();
- Change.Id childId =
- changeOperations
- .newChange()
- .file(FILE_NAME)
- .content(CONTENT + "line 4\n")
- .childOf()
- .change(parent)
- .create();
-
- SubmitRequirementExpression exp =
- SubmitRequirementExpression.create("file:\"'^.*\\.txt',withDiffContaining='line 4'\"");
-
- ChangeData childChangeData = changeQueryProvider.get().byLegacyChangeId(childId).get(0);
- SubmitRequirementExpressionResult srResult = evaluator.evaluateExpression(exp, childChangeData);
- assertThat(srResult.status()).isEqualTo(SubmitRequirementExpressionResult.Status.PASS);
- }
-
- @Test
- public void byFileEdits_addedContent_nonMatching() throws Exception {
- Change.Id parent = changeOperations.newChange().file(FILE_NAME).content(CONTENT).create();
- Change.Id childId =
- changeOperations
- .newChange()
- .file(FILE_NAME)
- .content(CONTENT + "line 4\n")
- .childOf()
- .change(parent)
- .create();
-
- SubmitRequirementExpression exp =
- SubmitRequirementExpression.create("file:\"'^.*\\.txt',withDiffContaining='line 5'\"");
-
- ChangeData childChangeData = changeQueryProvider.get().byLegacyChangeId(childId).get(0);
- SubmitRequirementExpressionResult srResult = evaluator.evaluateExpression(exp, childChangeData);
- assertThat(srResult.status()).isEqualTo(SubmitRequirementExpressionResult.Status.FAIL);
- }
-
- @Test
- public void byFileEdits_addedFile_matching() throws Exception {
- Change.Id parent = changeOperations.newChange().file(FILE_NAME).content(CONTENT).create();
- Change.Id childId =
- changeOperations
- .newChange()
- .file("new_file.txt")
- .content("content of the new file")
- .childOf()
- .change(parent)
- .create();
-
- SubmitRequirementExpression exp =
- SubmitRequirementExpression.create(
- "file:\"'^new.*\\\\.txt',withDiffContaining='of the new'\"");
-
- ChangeData childChangeData = changeQueryProvider.get().byLegacyChangeId(childId).get(0);
- SubmitRequirementExpressionResult srResult = evaluator.evaluateExpression(exp, childChangeData);
- assertThat(srResult.status()).isEqualTo(SubmitRequirementExpressionResult.Status.PASS);
- }
-
- @Test
- public void byFileEdits_addedFile_nonMatching() throws Exception {
- Change.Id parent = changeOperations.newChange().file(FILE_NAME).content(CONTENT).create();
- Change.Id childId =
- changeOperations
- .newChange()
- .file("new_file.txt")
- .content("content of the new file")
- .childOf()
- .change(parent)
- .create();
-
- SubmitRequirementExpression exp =
- SubmitRequirementExpression.create(
- "file:\"'^new.*\\.txt',withDiffContaining='not_exist'\"");
-
- ChangeData childChangeData = changeQueryProvider.get().byLegacyChangeId(childId).get(0);
- SubmitRequirementExpressionResult srResult = evaluator.evaluateExpression(exp, childChangeData);
- assertThat(srResult.status()).isEqualTo(SubmitRequirementExpressionResult.Status.FAIL);
- }
-
- @Test
- public void byFileEdits_modifiedContent_matching() throws Exception {
- Change.Id parent = changeOperations.newChange().file(FILE_NAME).content(CONTENT).create();
- Change.Id childId =
- changeOperations
- .newChange()
- .file(FILE_NAME)
- .content(CONTENT.replace("line 3\n", "line three\n"))
- .childOf()
- .change(parent)
- .create();
-
- SubmitRequirementExpression exp =
- SubmitRequirementExpression.create("file:\"'^.*\\.txt',withDiffContaining='three'\"");
-
- ChangeData childChangeData = changeQueryProvider.get().byLegacyChangeId(childId).get(0);
- SubmitRequirementExpressionResult srResult = evaluator.evaluateExpression(exp, childChangeData);
- assertThat(srResult.status()).isEqualTo(SubmitRequirementExpressionResult.Status.PASS);
- }
-
- @Test
- public void byFileEdits_modifiedContent_nonMatching() throws Exception {
- Change.Id parent = changeOperations.newChange().file(FILE_NAME).content(CONTENT).create();
- Change.Id childId =
- changeOperations
- .newChange()
- .file(FILE_NAME)
- .content(CONTENT.replace("line 3\n", "line three\n"))
- .childOf()
- .change(parent)
- .create();
-
- SubmitRequirementExpression exp =
- SubmitRequirementExpression.create("file:\"'^.*\\.txt',withDiffContaining='ten'\"");
-
- ChangeData childChangeData = changeQueryProvider.get().byLegacyChangeId(childId).get(0);
- SubmitRequirementExpressionResult srResult = evaluator.evaluateExpression(exp, childChangeData);
- assertThat(srResult.status()).isEqualTo(SubmitRequirementExpressionResult.Status.FAIL);
- }
-
- @Test
- public void byFileEdits_modifiedContentPattern_matching() throws Exception {
- Change.Id parent = changeOperations.newChange().file(FILE_NAME).content(CONTENT).create();
- Change.Id childId =
- changeOperations
- .newChange()
- .file(FILE_NAME)
- .content(CONTENT.replace("line 3\n", "line three\n"))
- .childOf()
- .change(parent)
- .create();
-
- SubmitRequirementExpression exp =
- SubmitRequirementExpression.create(
- "file:\"'^.*\\.txt',withDiffContaining='^.*th[rR]ee$'\"");
-
- ChangeData childChangeData = changeQueryProvider.get().byLegacyChangeId(childId).get(0);
- SubmitRequirementExpressionResult srResult = evaluator.evaluateExpression(exp, childChangeData);
- assertThat(srResult.status()).isEqualTo(SubmitRequirementExpressionResult.Status.PASS);
- }
-
- @Test
- public void byFileEdits_exactMatchingWithFilePath_matching() throws Exception {
- Change.Id parent = changeOperations.newChange().file(FILE_NAME).content(CONTENT).create();
- Change.Id childId =
- changeOperations
- .newChange()
- .file(FILE_NAME)
- .content(CONTENT.replace("line 3\n", "line three\n"))
- .childOf()
- .change(parent)
- .create();
-
- SubmitRequirementExpression exp =
- SubmitRequirementExpression.create(
- String.format("file:\"'%s',withDiffContaining='three'\"", FILE_NAME));
-
- ChangeData childChangeData = changeQueryProvider.get().byLegacyChangeId(childId).get(0);
- SubmitRequirementExpressionResult srResult = evaluator.evaluateExpression(exp, childChangeData);
- assertThat(srResult.status()).isEqualTo(SubmitRequirementExpressionResult.Status.PASS);
- }
-
- @Test
- public void byFileEdits_exactMatchingWithFilePath_nonMatching() throws Exception {
- Change.Id parent = changeOperations.newChange().file(FILE_NAME).content(CONTENT).create();
- Change.Id childId =
- changeOperations
- .newChange()
- .file(FILE_NAME)
- .content(CONTENT.replace("line 3\n", "line three\n"))
- .childOf()
- .change(parent)
- .create();
-
- SubmitRequirementExpression exp =
- SubmitRequirementExpression.create(
- "file:\"'non_existent.txt',withDiffContaining='three'\"");
-
- ChangeData childChangeData = changeQueryProvider.get().byLegacyChangeId(childId).get(0);
- SubmitRequirementExpressionResult srResult = evaluator.evaluateExpression(exp, childChangeData);
- assertThat(srResult.status()).isEqualTo(SubmitRequirementExpressionResult.Status.FAIL);
- }
-
- @Test
- public void byFileEdits_notMatchingWithFilePath() throws Exception {
- Change.Id parent = changeOperations.newChange().file(FILE_NAME).content(CONTENT).create();
- Change.Id childId =
- changeOperations
- .newChange()
- .file(FILE_NAME)
- .content(CONTENT.replace("line 3\n", "line three\n"))
- .childOf()
- .change(parent)
- .create();
-
- // commit edit only matches with files ending with ".java". Since our modified file name ends
- // with ".txt", the applicability expression will not match.
- SubmitRequirementExpression exp =
- SubmitRequirementExpression.create("file:\"'^.*\\.java',withDiffContaining='three'\"");
-
- ChangeData childChangeData = changeQueryProvider.get().byLegacyChangeId(childId).get(0);
- SubmitRequirementExpressionResult srResult = evaluator.evaluateExpression(exp, childChangeData);
- assertThat(srResult.status()).isEqualTo(SubmitRequirementExpressionResult.Status.FAIL);
- }
-
- @Test
- public void byFileEdits_escapeSingleQuotes() throws Exception {
- Change.Id parent = changeOperations.newChange().file(FILE_NAME).content(CONTENT).create();
- Change.Id childId =
- changeOperations
- .newChange()
- .file(FILE_NAME)
- .content(CONTENT.replace("line 3\n", "line 'three' is modified\n"))
- .childOf()
- .change(parent)
- .create();
-
- SubmitRequirementExpression exp =
- SubmitRequirementExpression.create(
- "file:\"'^.*\\.txt',withDiffContaining='line \\'three\\' is'\"");
-
- ChangeData childChangeData = changeQueryProvider.get().byLegacyChangeId(childId).get(0);
- SubmitRequirementExpressionResult srResult = evaluator.evaluateExpression(exp, childChangeData);
- assertThat(srResult.status()).isEqualTo(SubmitRequirementExpressionResult.Status.PASS);
- }
-
- @Test
- public void byFileEdits_doubleEscapeSingleQuote() throws Exception {
- Change.Id parent = changeOperations.newChange().file(FILE_NAME).content(CONTENT).create();
- Change.Id childId =
- changeOperations
- .newChange()
- .file(FILE_NAME)
- // This will be written to the file as: line \'three\' is modified.
- .content(CONTENT.replace("line 3\n", "line \\'three\\' is modified\n"))
- .childOf()
- .change(parent)
- .create();
-
- // Users can still provide back-slashes in regexes by escaping them.
- SubmitRequirementExpression exp =
- SubmitRequirementExpression.create(
- "file:\"'^.*\\\\.txt',withDiffContaining='line \\\\'three\\\\' is'\"");
-
- ChangeData childChangeData = changeQueryProvider.get().byLegacyChangeId(childId).get(0);
- SubmitRequirementExpressionResult srResult = evaluator.evaluateExpression(exp, childChangeData);
- assertThat(srResult.status()).isEqualTo(SubmitRequirementExpressionResult.Status.PASS);
- }
-
- @Test
- public void byFileEdits_escapeDoubleQuotes() throws Exception {
- Change.Id parent = changeOperations.newChange().file(FILE_NAME).content(CONTENT).create();
- Change.Id childId =
- changeOperations
- .newChange()
- .file(FILE_NAME)
- .content(CONTENT.replace("line 3\n", "line \"three\" is modified\n"))
- .childOf()
- .change(parent)
- .create();
-
- SubmitRequirementExpression exp =
- SubmitRequirementExpression.create(
- "file:\"'^.*\\.txt',withDiffContaining='line \\\"three\\\" is'\"");
-
- ChangeData childChangeData = changeQueryProvider.get().byLegacyChangeId(childId).get(0);
- SubmitRequirementExpressionResult srResult = evaluator.evaluateExpression(exp, childChangeData);
- assertThat(srResult.status()).isEqualTo(SubmitRequirementExpressionResult.Status.PASS);
- }
-
- @Test
- public void byFileEdits_invalidSyntax() throws Exception {
- Change.Id parent = changeOperations.newChange().file(FILE_NAME).content(CONTENT).create();
- Change.Id childId =
- changeOperations
- .newChange()
- .file(FILE_NAME)
- .content(CONTENT.replace("line 3\n", "line three\n"))
- .childOf()
- .change(parent)
- .create();
-
- SubmitRequirementExpression exp =
- SubmitRequirementExpression.create(
- "file:\"'^.*\\.txt',withDiffContaining=forgot single quotes\"");
-
- ChangeData childChangeData = changeQueryProvider.get().byLegacyChangeId(childId).get(0);
- SubmitRequirementExpressionResult srResult = evaluator.evaluateExpression(exp, childChangeData);
- // If the format is invalid, the operator falls back to the default operator of
- // ChangeQueryBuilder which does not match the change, i.e. returns false.
- assertThat(srResult.status()).isEqualTo(SubmitRequirementExpressionResult.Status.FAIL);
- }
-
- @Test
- public void byFileEdits_invalidFilePattern() throws Exception {
- SubmitRequirementExpression exp =
- SubmitRequirementExpression.create("file:\"'^**',withDiffContaining='content'\"");
-
- SubmitRequirementExpressionResult srResult = evaluator.evaluateExpression(exp, changeData);
- assertThat(srResult.status()).isEqualTo(SubmitRequirementExpressionResult.Status.ERROR);
- assertThat(srResult.errorMessage().get()).isEqualTo("Invalid file pattern.");
- }
-
- @Test
- public void byFileEdits_invalidContentPattern() throws Exception {
- SubmitRequirementExpression exp =
- SubmitRequirementExpression.create("file:\"'fileName\\.txt',withDiffContaining='^**'\"");
-
- SubmitRequirementExpressionResult srResult = evaluator.evaluateExpression(exp, changeData);
- assertThat(srResult.status()).isEqualTo(SubmitRequirementExpressionResult.Status.ERROR);
- assertThat(srResult.errorMessage().get()).isEqualTo("Invalid content pattern.");
- }
-
private void voteLabel(String changeId, String labelName, int score) throws RestApiException {
gApi.changes().id(changeId).current().review(new ReviewInput().label(labelName, score));
}
diff --git a/javatests/com/google/gerrit/index/query/QueryParserTest.java b/javatests/com/google/gerrit/index/query/QueryParserTest.java
index f478803..776a2c4 100644
--- a/javatests/com/google/gerrit/index/query/QueryParserTest.java
+++ b/javatests/com/google/gerrit/index/query/QueryParserTest.java
@@ -17,7 +17,6 @@
import static com.google.gerrit.index.query.QueryParser.AND;
import static com.google.gerrit.index.query.QueryParser.COLON;
import static com.google.gerrit.index.query.QueryParser.DEFAULT_FIELD;
-import static com.google.gerrit.index.query.QueryParser.EXACT_PHRASE;
import static com.google.gerrit.index.query.QueryParser.FIELD_NAME;
import static com.google.gerrit.index.query.QueryParser.SINGLE_WORD;
import static com.google.gerrit.index.query.QueryParser.parse;
@@ -205,43 +204,6 @@
assertThat(r).child(2).hasNoChildren();
}
- @Test
- public void fieldNameWithEscapedDoubleQuotesInValue() throws Exception {
- // Actual String: A \"special\" word
- String search = "message:\"A \\\"special\\\" word\"";
- Tree r = parse(search);
- assertThat(r).hasType(FIELD_NAME);
- assertThat(r).hasChildCount(1);
- assertThat(r).hasText("message");
- assertThat(r).child(0).hasType(EXACT_PHRASE);
- // Antlr escaped the double quotes in the phrase.
- assertThat(r).child(0).hasText("A \"special\" word");
- }
-
- @Test
- public void fieldNameWithEscapedTabCharacterIsPreserved() throws Exception {
- String[] searches = {"message:\"A \\t word\"", "message:{A \\t word}"};
- for (String search : searches) {
- Tree r = parse(search);
- assertThat(r).hasType(FIELD_NAME);
- assertThat(r).hasChildCount(1);
- assertThat(r).hasText("message");
- assertThat(r).child(0).hasType(EXACT_PHRASE);
- assertThat(r).child(0).hasText("A \t word");
- }
- }
-
- @Test
- public void fieldNameWithEscapedBackslashIsIncludedInOutput() throws Exception {
- String search = "message:\"A backslash \\\\ in phrase\"";
- Tree r = parse(search);
- assertThat(r).hasType(FIELD_NAME);
- assertThat(r).hasChildCount(1);
- assertThat(r).hasText("message");
- assertThat(r).child(0).hasType(EXACT_PHRASE);
- assertThat(r).child(0).hasText("A backslash \\ in phrase");
- }
-
private static void assertParseFails(String query) {
assertThrows(QueryParseException.class, () -> parse(query));
}
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.ts b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.ts
index 2495fe4..5ae5271 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.ts
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.ts
@@ -243,6 +243,7 @@
* Is the column disabled by a server config or experiment?
*/
_isColumnEnabled(column: string, config: ServerInfo, experiments: string[]) {
+ if (!columnNames.includes(column)) return false;
if (!config || !config.change) return true;
if (column === 'Comments') return experiments.includes('comments-column');
if (column === 'Requirements')
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_test.js b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_test.js
index ff8a0f0..841a781 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_test.js
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_test.js
@@ -337,6 +337,11 @@
});
});
+ test('obsolete column in preferences not visible', () => {
+ assert.isTrue(element._isColumnEnabled('Subject'));
+ assert.isFalse(element._isColumnEnabled('Assignee'));
+ });
+
suite('random column does not exist', () => {
let element;
diff --git a/polygerrit-ui/app/elements/shared/gr-vote-chip/gr-vote-chip.ts b/polygerrit-ui/app/elements/shared/gr-vote-chip/gr-vote-chip.ts
index a18b772..5327dff 100644
--- a/polygerrit-ui/app/elements/shared/gr-vote-chip/gr-vote-chip.ts
+++ b/polygerrit-ui/app/elements/shared/gr-vote-chip/gr-vote-chip.ts
@@ -140,9 +140,11 @@
}
} else if (isQuickLabelInfo(this.label)) {
if (this.label.approved) {
- return '👍️';
+ return '👍';
} else if (this.label.rejected) {
- return '👎️';
+ return '👎';
+ } else if (this.label.disliked || this.label.recommended) {
+ return valueString(this.label.value);
}
}
return '';
diff --git a/polygerrit-ui/app/elements/shared/gr-vote-chip/gr-vote-chip_test.ts b/polygerrit-ui/app/elements/shared/gr-vote-chip/gr-vote-chip_test.ts
index 1958087..a3f23df 100644
--- a/polygerrit-ui/app/elements/shared/gr-vote-chip/gr-vote-chip_test.ts
+++ b/polygerrit-ui/app/elements/shared/gr-vote-chip/gr-vote-chip_test.ts
@@ -49,9 +49,7 @@
test('renders', () => {
expect(element).shadowDom.to.equal(`<span class="container">
- <div class="positive vote-chip">
- 👍️
- </div>
+ <div class="max vote-chip">👍</div>
</span>`);
});
});
diff --git a/polygerrit-ui/app/utils/label-util.ts b/polygerrit-ui/app/utils/label-util.ts
index 1a48a7b..0aa4a51 100644
--- a/polygerrit-ui/app/utils/label-util.ts
+++ b/polygerrit-ui/app/utils/label-util.ts
@@ -90,8 +90,10 @@
: LabelStatus.RECOMMENDED;
}
} else if (isQuickLabelInfo(label)) {
- if (label.approved) return LabelStatus.RECOMMENDED;
- if (label.rejected) return LabelStatus.DISLIKED;
+ if (label.approved) return LabelStatus.APPROVED;
+ if (label.rejected) return LabelStatus.REJECTED;
+ if (label.disliked) return LabelStatus.DISLIKED;
+ if (label.recommended) return LabelStatus.RECOMMENDED;
}
return LabelStatus.NEUTRAL;
}
@@ -178,7 +180,12 @@
);
}
if (isQuickLabelInfo(labelInfo)) {
- return !!labelInfo.rejected || !!labelInfo.approved;
+ return (
+ !!labelInfo.rejected ||
+ !!labelInfo.approved ||
+ !!labelInfo.recommended ||
+ !!labelInfo.disliked
+ );
}
return false;
}
diff --git a/polygerrit-ui/app/utils/label-util_test.ts b/polygerrit-ui/app/utils/label-util_test.ts
index 142c607..6cb04af 100644
--- a/polygerrit-ui/app/utils/label-util_test.ts
+++ b/polygerrit-ui/app/utils/label-util_test.ts
@@ -187,8 +187,12 @@
let labelInfo: QuickLabelInfo = {};
assert.equal(getLabelStatus(labelInfo), LabelStatus.NEUTRAL);
labelInfo = {approved: createAccountWithEmail()};
- assert.equal(getLabelStatus(labelInfo), LabelStatus.RECOMMENDED);
+ assert.equal(getLabelStatus(labelInfo), LabelStatus.APPROVED);
labelInfo = {rejected: createAccountWithEmail()};
+ assert.equal(getLabelStatus(labelInfo), LabelStatus.REJECTED);
+ labelInfo = {recommended: createAccountWithEmail()};
+ assert.equal(getLabelStatus(labelInfo), LabelStatus.RECOMMENDED);
+ labelInfo = {disliked: createAccountWithEmail()};
assert.equal(getLabelStatus(labelInfo), LabelStatus.DISLIKED);
});