| // Copyright (C) 2020 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.acceptance.testsuite.change; |
| |
| import com.google.auto.value.AutoValue; |
| import com.google.gerrit.acceptance.testsuite.ThrowingFunction; |
| import com.google.gerrit.acceptance.testsuite.change.TestRange.Position; |
| import com.google.gerrit.common.Nullable; |
| import com.google.gerrit.entities.Account; |
| import com.google.gerrit.entities.Comment; |
| import com.google.gerrit.entities.Patch; |
| import java.time.Instant; |
| import java.time.LocalDateTime; |
| import java.time.ZoneOffset; |
| import java.util.Optional; |
| |
| /** |
| * Attributes of the human comment. If not provided, arbitrary values will be used. This class is |
| * very similar to {@link TestRobotCommentCreation} to allow separation between robot and human |
| * comments. |
| */ |
| @AutoValue |
| public abstract class TestCommentCreation { |
| |
| public abstract Optional<String> message(); |
| |
| public abstract Optional<String> file(); |
| |
| public abstract Optional<Integer> line(); |
| |
| public abstract Optional<TestRange> range(); |
| |
| public abstract Optional<CommentSide> side(); |
| |
| public abstract Optional<Boolean> unresolved(); |
| |
| public abstract Optional<String> parentUuid(); |
| |
| public abstract Optional<String> tag(); |
| |
| public abstract Optional<Account.Id> author(); |
| |
| public abstract Optional<Instant> createdOn(); |
| |
| abstract Comment.Status status(); |
| |
| abstract ThrowingFunction<TestCommentCreation, String> commentCreator(); |
| |
| public static Builder builder( |
| ThrowingFunction<TestCommentCreation, String> commentCreator, Comment.Status commentStatus) { |
| return new AutoValue_TestCommentCreation.Builder() |
| .commentCreator(commentCreator) |
| .status(commentStatus); |
| } |
| |
| @AutoValue.Builder |
| public abstract static class Builder { |
| |
| public Builder noMessage() { |
| return message(""); |
| } |
| |
| /** Message text of the comment. */ |
| public abstract Builder message(String message); |
| |
| /** Indicates a patchset-level comment. */ |
| public Builder onPatchsetLevel() { |
| return file(Patch.PATCHSET_LEVEL); |
| } |
| |
| /** Indicates a file comment. The comment will be on the specified file. */ |
| public Builder onFileLevelOf(String filePath) { |
| return file(filePath).line(null).range(null); |
| } |
| |
| /** |
| * Starts the fluent change to create a line comment. The line comment will be at the indicated |
| * line. Lines start with 1. |
| */ |
| public FileBuilder<Builder> onLine(int line) { |
| return new FileBuilder<>(file -> file(file).line(line).range(null)); |
| } |
| |
| /** |
| * Starts the fluent chain to create a range comment. The range begins at the specified line. |
| * Lines start at 1. The start position (line, charOffset) is inclusive, the end position (line, |
| * charOffset) is exclusive. |
| */ |
| public PositionBuilder<StartAwarePositionBuilder<Builder>> fromLine(int startLine) { |
| return new PositionBuilder<>( |
| startCharOffset -> { |
| Position start = Position.builder().line(startLine).charOffset(startCharOffset).build(); |
| TestRange.Builder testRangeBuilder = TestRange.builder().setStart(start); |
| return new StartAwarePositionBuilder<>(testRangeBuilder, this::range, this::file); |
| }); |
| } |
| |
| /** File on which the comment should be added. */ |
| abstract Builder file(String filePath); |
| |
| /** Line on which the comment should be added. */ |
| abstract Builder line(@Nullable Integer line); |
| |
| /** Range on which the comment should be added. */ |
| abstract Builder range(@Nullable TestRange range); |
| |
| /** |
| * Indicates that the comment refers to a file, line, range, ... in the commit of the patchset. |
| * |
| * <p>On the UI, such comments are shown on the right side of a diff view when a diff against |
| * base is selected. See {@link #onParentCommit()} for comments shown on the left side. |
| */ |
| public Builder onPatchsetCommit() { |
| return side(CommentSide.PATCHSET_COMMIT); |
| } |
| |
| /** |
| * Indicates that the comment refers to a file, line, range, ... in the parent commit of the |
| * patchset. |
| * |
| * <p>On the UI, such comments are shown on the left side of a diff view when a diff against |
| * base is selected. See {@link #onPatchsetCommit()} for comments shown on the right side. |
| * |
| * <p>For merge commits, this indicates the first parent commit. |
| */ |
| public Builder onParentCommit() { |
| return side(CommentSide.PARENT_COMMIT); |
| } |
| |
| /** Like {@link #onParentCommit()} but for the second parent of a merge commit. */ |
| public Builder onSecondParentCommit() { |
| return side(CommentSide.SECOND_PARENT_COMMIT); |
| } |
| |
| /** |
| * Like {@link #onParentCommit()} but for the AutoMerge commit created from the parents of a |
| * merge commit. |
| */ |
| public Builder onAutoMergeCommit() { |
| return side(CommentSide.AUTO_MERGE_COMMIT); |
| } |
| |
| abstract Builder side(CommentSide side); |
| |
| /** Indicates a resolved comment. */ |
| public Builder resolved() { |
| return unresolved(false); |
| } |
| |
| /** Indicates an unresolved comment. */ |
| public Builder unresolved() { |
| return unresolved(true); |
| } |
| |
| abstract Builder unresolved(boolean unresolved); |
| |
| /** |
| * UUID of another comment to which this comment is a reply. This comment must have similar |
| * attributes (e.g. file, line, side) as the parent comment. The parent comment must be a |
| * published comment. |
| */ |
| public abstract Builder parentUuid(String parentUuid); |
| |
| /** Tag to attach to the comment. */ |
| public abstract Builder tag(String value); |
| |
| /** Author of the comment. Must be an existing user account. */ |
| public abstract Builder author(Account.Id accountId); |
| |
| /** |
| * Creation time of the comment. Like {@link #createdOn(Instant)} but with an arbitrary, fixed |
| * time zone (-> deterministic test execution). |
| */ |
| public Builder createdOn(LocalDateTime createdOn) { |
| // We don't care about the exact time zone in most tests, just that it's fixed so that tests |
| // are deterministic. |
| return createdOn(createdOn.atZone(ZoneOffset.UTC).toInstant()); |
| } |
| |
| /** |
| * Creation time of the comment. This may also lie in the past or future. Comments stored in |
| * NoteDb support only second precision. |
| */ |
| public abstract Builder createdOn(Instant createdOn); |
| |
| /** |
| * Status of the comment. Hidden in the API surface. Use {@link |
| * PerPatchsetOperations#newComment()} or {@link PerPatchsetOperations#newDraftComment()} |
| * depending on which type of comment you want to create. |
| */ |
| abstract Builder status(Comment.Status value); |
| |
| abstract Builder commentCreator(ThrowingFunction<TestCommentCreation, String> commentCreator); |
| |
| abstract TestCommentCreation autoBuild(); |
| |
| /** |
| * Creates the comment. |
| * |
| * @return the UUID of the created comment |
| */ |
| public String create() { |
| TestCommentCreation commentCreation = autoBuild(); |
| return commentCreation.commentCreator().applyAndThrowSilently(commentCreation); |
| } |
| } |
| } |