| // 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.ssh; |
| |
| import static com.google.gerrit.acceptance.WaitUtil.waitUntil; |
| import static com.google.gerrit.entities.Patch.PATCHSET_LEVEL; |
| |
| import com.google.common.base.Splitter; |
| import com.google.gerrit.acceptance.AbstractDaemonTest; |
| import com.google.gerrit.acceptance.NoHttpd; |
| import com.google.gerrit.acceptance.Sandboxed; |
| import com.google.gerrit.acceptance.UseSsh; |
| import com.google.gerrit.acceptance.config.GerritConfig; |
| import com.google.gerrit.entities.RefNames; |
| import com.google.gerrit.extensions.api.changes.ChangeApi; |
| import com.google.gerrit.extensions.api.changes.DraftInput; |
| import com.google.gerrit.extensions.api.changes.ReviewInput; |
| import com.google.gerrit.extensions.api.changes.ReviewInput.DraftHandling; |
| import com.google.gerrit.server.query.change.ChangeData; |
| import java.io.IOException; |
| import java.io.Reader; |
| import java.time.Duration; |
| import java.util.Arrays; |
| import java.util.List; |
| import java.util.concurrent.atomic.AtomicInteger; |
| import java.util.function.Supplier; |
| import java.util.stream.Collectors; |
| import java.util.stream.Stream; |
| import java.util.stream.StreamSupport; |
| import org.eclipse.jgit.lib.ObjectId; |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| @NoHttpd |
| @UseSsh |
| @Sandboxed |
| public class StreamEventsIT extends AbstractDaemonTest { |
| private static final Duration MAX_DURATION_FOR_RECEIVING_EVENTS = Duration.ofSeconds(2); |
| private static final String TEST_REVIEW_COMMENT = "any comment"; |
| private static final String TEST_REVIEW_DRAFT_COMMENT = "any draft comment"; |
| private Reader streamEventsReader; |
| private ChangeData change; |
| |
| @Before |
| public void setup() throws Exception { |
| streamEventsReader = adminSshSession.execAndReturnReader("gerrit stream-events"); |
| } |
| |
| @After |
| public void closeStreamEvents() throws IOException { |
| streamEventsReader.close(); |
| } |
| |
| @Test |
| public void commentOnChangeShowsUpInStreamEvents() throws Exception { |
| reviewChange(new ReviewInput().message(TEST_REVIEW_COMMENT)); |
| waitForEvent(() -> pollEventsContaining("comment-added", TEST_REVIEW_COMMENT).size() == 1); |
| } |
| |
| @Test |
| public void publishedDraftPatchSetLevelCommentShowsUpInStreamEvents() throws Exception { |
| change = createChange().getChange(); |
| |
| String firstDraftComment = String.format("%s 1", TEST_REVIEW_DRAFT_COMMENT); |
| String secondDraftComment = String.format("%s 2", TEST_REVIEW_DRAFT_COMMENT); |
| |
| draftReviewChange(PATCHSET_LEVEL, firstDraftComment); |
| draftReviewChange(PATCHSET_LEVEL, secondDraftComment); |
| publishDraftReviews(); |
| |
| waitForEvent( |
| () -> |
| pollEventsContaining("comment-added", firstDraftComment, secondDraftComment).size() |
| == 1); |
| } |
| |
| @Test |
| public void batchRefsUpdatedShowSeparatelyInStreamEvents() throws Exception { |
| String refName = createChange().getChange().currentPatchSet().refName(); |
| AtomicInteger numberOfFoundEvents = new AtomicInteger(0); |
| waitForEvent( |
| () -> |
| numberOfFoundEvents.addAndGet( |
| pollEventsContaining( |
| "ref-updated", refName.substring(0, refName.lastIndexOf('/'))) |
| .size()) |
| == 2); |
| } |
| |
| @Test |
| @GerritConfig(name = "event.stream-events.enableBatchRefUpdatedEvents", value = "true") |
| public void batchRefsUpdatedShowsInStreamEvents() throws Exception { |
| ChangeData change = createChange().getChange(); |
| String patchsetRefName = change.currentPatchSet().refName(); |
| String metaRefName = RefNames.changeMetaRef(change.getId()); |
| waitForEvent( |
| () -> pollEventsContaining("batch-ref-updated", patchsetRefName, metaRefName).size() == 1); |
| } |
| |
| @Test |
| @GerritConfig(name = "event.stream-events.enableRefUpdatedEvents", value = "true") |
| @GerritConfig(name = "event.stream-events.enableBatchRefUpdatedEvents", value = "false") |
| @GerritConfig(name = "event.stream-events.enableDraftCommentEvents", value = "true") |
| public void draftCommentRefsShowInStreamEventsWithRefUpdated() throws Exception { |
| change = createChange().getChange(); |
| |
| draftReviewChange(PATCHSET_LEVEL, String.format("%s 1", TEST_REVIEW_DRAFT_COMMENT)); |
| |
| waitForEvent(() -> pollEventsContaining("ref-updated", "refs/draft-comments/").size() == 1); |
| } |
| |
| @Test(expected = InterruptedException.class) |
| @GerritConfig(name = "event.stream-events.enableRefUpdatedEvents", value = "true") |
| @GerritConfig(name = "event.stream-events.enableBatchRefUpdatedEvents", value = "false") |
| @GerritConfig(name = "event.stream-events.enableDraftCommentEvents", value = "false") |
| public void draftCommentRefsDontShowInStreamEventsWithRefUpdated() throws Exception { |
| change = createChange().getChange(); |
| |
| draftReviewChange(PATCHSET_LEVEL, String.format("%s 1", TEST_REVIEW_DRAFT_COMMENT)); |
| |
| waitForEvent(() -> pollEventsContaining("ref-updated", "refs/draft-comments/").size() == 1); |
| } |
| |
| @Test |
| @GerritConfig(name = "event.stream-events.enableBatchRefUpdatedEvents", value = "true") |
| @GerritConfig(name = "event.stream-events.enableRefUpdatedEvents", value = "false") |
| @GerritConfig(name = "event.stream-events.enableDraftCommentEvents", value = "true") |
| public void draftCommentRefsShowInStreamEventsWithBatchRefUpdated() throws Exception { |
| change = createChange().getChange(); |
| |
| draftReviewChange(PATCHSET_LEVEL, String.format("%s 1", TEST_REVIEW_DRAFT_COMMENT)); |
| |
| waitForEvent( |
| () -> pollEventsContaining("batch-ref-updated", "refs/draft-comments/").size() == 1); |
| } |
| |
| @Test(expected = InterruptedException.class) |
| @GerritConfig(name = "event.stream-events.enableBatchRefUpdatedEvents", value = "true") |
| @GerritConfig(name = "event.stream-events.enableRefUpdatedEvents", value = "false") |
| @GerritConfig(name = "event.stream-events.enableDraftCommentEvents", value = "false") |
| public void draftCommentRefsDontShowInStreamEventsWithBatchRefUpdated() throws Exception { |
| change = createChange().getChange(); |
| |
| draftReviewChange(PATCHSET_LEVEL, String.format("%s 1", TEST_REVIEW_DRAFT_COMMENT)); |
| |
| waitForEvent( |
| () -> pollEventsContaining("batch-ref-updated", "refs/draft-comments/").size() == 1); |
| } |
| |
| @Test |
| @GerritConfig(name = "event.stream-events.enableRefUpdatedEvents", value = "true") |
| @GerritConfig(name = "event.stream-events.enableBatchRefUpdatedEvents", value = "false") |
| @GerritConfig(name = "event.stream-events.enableDraftCommentEvents", value = "true") |
| public void draftCommentRefsDeletionShowInStreamEventsUponPublishing() throws Exception { |
| change = createChange().getChange(); |
| |
| draftReviewChange(PATCHSET_LEVEL, String.format("%s 1", TEST_REVIEW_DRAFT_COMMENT)); |
| publishDraftReviews(); |
| |
| waitForEvent( |
| () -> |
| pollEventsContaining( |
| "ref-updated", |
| "refs/draft-comments/", |
| "\"newRev\":\"" + ObjectId.zeroId().name() + "\"") |
| .size() |
| == 1); |
| } |
| |
| private void waitForEvent(Supplier<Boolean> waitCondition) throws InterruptedException { |
| waitUntil(() -> waitCondition.get(), MAX_DURATION_FOR_RECEIVING_EVENTS); |
| } |
| |
| private void reviewChange(ReviewInput reviewInput) throws Exception { |
| ChangeApi changeApi = gApi.changes().id(createChange().getChange().getId().get()); |
| changeApi.current().review(reviewInput); |
| } |
| |
| private void draftReviewChange(String path, String reviewMessage) throws Exception { |
| DraftInput draftInput = new DraftInput(); |
| draftInput.message = reviewMessage; |
| draftInput.path = path; |
| ChangeApi changeApi = gApi.changes().id(change.getId().get()); |
| changeApi.current().createDraft(draftInput); |
| } |
| |
| private void publishDraftReviews() throws Exception { |
| ReviewInput reviewInput = new ReviewInput(); |
| reviewInput.tag = "new_tag"; |
| reviewInput.drafts = DraftHandling.PUBLISH; |
| gApi.changes().id(change.getId().get()).current().review(reviewInput); |
| } |
| |
| private List<String> pollEventsContaining(String eventType, String... expectedContent) { |
| try { |
| char[] cbuf = new char[2048]; |
| StringBuilder eventsOutput = new StringBuilder(); |
| while (streamEventsReader.ready()) { |
| int read = streamEventsReader.read(cbuf); |
| eventsOutput.append(Arrays.copyOfRange(cbuf, 0, read)); |
| } |
| return StreamSupport.stream( |
| Splitter.on('\n').trimResults().split(eventsOutput.toString()).spliterator(), false) |
| .filter( |
| event -> |
| event.contains(String.format("\"type\":\"%s\"", eventType)) |
| && Stream.of(expectedContent).allMatch(event::contains)) |
| .collect(Collectors.toList()); |
| } catch (IOException e) { |
| throw new IllegalStateException(e); |
| } |
| } |
| } |